Files
callstack__agent-device/scripts/__tests__/help-conformance-topic-coverage.test.ts
T
Michał Pierzchała 2d1d70613f feat(bench): renderer-pinned samples, topic-coverage gate, error-recovery quizzes; trim skillgym to agentic checks (#1411)
* 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.
2026-07-27 14:24:36 +02:00

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`,
);
}
});