mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
2d1d70613f
* feat(bench): renderer-pinned samples, topic-coverage gate, error quizzes; trim skillgym to agentic checks The help conformance bench's quoted CLI output is now sourced from scripts/help-conformance-sample-outputs.mjs, and every sample is rebuilt through the real production renderers (settle output formatters, printHumanError, formatSnapshotText, refMutationAdmissionResponse) by scripts/__tests__/help-conformance-sample-outputs.test.ts — a rendering or message change fails deterministically instead of leaving the bench grading against output the CLI no longer prints. This retires the fabricated recoverable-failure envelope (production never throws a textual settle timeout; that case is replaced by a real DEVICE_IN_USE recovery quiz). Bench cases move to scripts/help-conformance-cases.mjs and are enumerated against the help-topic registry: helpTopicIds() is exported from cli-help, and scripts/__tests__/help-conformance-topic-coverage.test.ts fails when a help topic has neither a bench case nor an explicit waiver. New case families: error-envelope recovery quizzes (device-in-use, stale pinned ref, ambiguous find match, app-not-installed) pinned to real error text, topic coverage for tv/web/react-native/debugging/workflow, and a metamorphic twin of the settled-diff quiz. The skillgym smoke suite shrinks from 119 cases to the 5 that measure what only an agentic runner can show: skill routing plus output interpretation with a proven local CLI help probe (local-cli-help-policy). Its embedded samples now import the same pinned constants, replacing hand-transcribed output that had already drifted from the renderer. Knowledge checks belong to the bench; live fixture behavior belongs to the iOS simulator e2e suite. * review: drive error samples through the real producers; enforce local-help on the routing smoke The DEVICE_IN_USE, AMBIGUOUS_MATCH, and APP_NOT_INSTALLED parity tests no longer hand-author the producer message before rendering: each drives the actual producer — buildDeviceInUseBySessionError (extracted in session-open.ts and called by the handler), buildAmbiguousMatchError (now exported from find.ts), and buildAppNotInstalledError (extracted in app-resolution.ts and thrown by the resolver). Because each factory is exported from its producer file and called by the production path, dropping the production call would make it test-only and fail check:production-exports — the wiring is gate-enforced, not conventional. open-and-snapshot now sets requireLocalCliHelp and allowOnlyLocalCliHelpCommands, so the 'skill plus local help' claim is observed rather than assumed; without them the case can pass on model prior alone.
68 lines
2.8 KiB
TypeScript
68 lines
2.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'vitest';
|
|
import { CASES } from '../help-conformance-cases.mjs';
|
|
import { helpTopicIds } from '../../src/cli/parser/cli-help.ts';
|
|
|
|
// "What enumerates N": benchmark cases are keyed to help topics, and this gate
|
|
// keys the case list to the topic registry itself. A new help topic must gain
|
|
// a benchmark case (docs: [..., '<topic>']) or an explicit waiver here — the
|
|
// waiver names why the topic's guidance is not yet benchmarked, so uncovered
|
|
// topics are a visible decision instead of silent drift.
|
|
const WAIVED_TOPICS: Record<string, string> = {
|
|
cdp: 'JS-heap forensics niche; add cases when heap-guidance regressions show up in practice.',
|
|
macos: 'macOS surface guidance is thin and stable; no observed planning regressions yet.',
|
|
maestro: 'Compatibility reference, not a planning loop; conformance is oracle-tested instead.',
|
|
'physical-device': 'Needs device-specific setup guidance; no portable planning task defined yet.',
|
|
'react-devtools':
|
|
'Profiling-window guidance; add cases when render-diagnosis planning regresses.',
|
|
remote: 'Remote/cloud lease setup; niche until remote workflows are benchmarked end to end.',
|
|
};
|
|
|
|
const FIRST_SCREEN_DOC = '--help:first30';
|
|
|
|
test('every case doc id is the first screen or a real help topic', () => {
|
|
const topics = new Set(helpTopicIds());
|
|
for (const testCase of CASES) {
|
|
for (const doc of testCase.docs) {
|
|
assert.ok(
|
|
doc === FIRST_SCREEN_DOC || topics.has(doc),
|
|
`case "${testCase.id}" references unknown help doc "${doc}"`,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('every help topic has a benchmark case or an explicit waiver', () => {
|
|
const covered = new Set(CASES.flatMap((testCase) => testCase.docs));
|
|
const uncovered = helpTopicIds().filter(
|
|
(topic) => !covered.has(topic) && !(topic in WAIVED_TOPICS),
|
|
);
|
|
assert.deepEqual(
|
|
uncovered,
|
|
[],
|
|
'new help topics need a benchmark case in scripts/help-conformance-cases.mjs or a waiver above',
|
|
);
|
|
});
|
|
|
|
test('waivers only name real, uncovered topics', () => {
|
|
const topics = new Set(helpTopicIds());
|
|
const covered = new Set(CASES.flatMap((testCase) => testCase.docs));
|
|
for (const [topic, reason] of Object.entries(WAIVED_TOPICS)) {
|
|
assert.ok(topics.has(topic), `waived topic "${topic}" no longer exists — remove the waiver`);
|
|
assert.ok(
|
|
!covered.has(topic),
|
|
`waived topic "${topic}" is now covered by a case — remove the waiver`,
|
|
);
|
|
assert.ok(reason.trim().length > 0, `waiver for "${topic}" needs a reason`);
|
|
}
|
|
});
|
|
|
|
test('the first help screen is exercised by every case family', () => {
|
|
for (const testCase of CASES) {
|
|
assert.ok(
|
|
testCase.docs.includes(FIRST_SCREEN_DOC),
|
|
`case "${testCase.id}" must include the first-screen doc — it is the only text every runner sees before choosing a topic`,
|
|
);
|
|
}
|
|
});
|