Skip to content

test(bench): falsification fixtures for oracles + typed runner outcomes - #1893

Merged
thymikee merged 2 commits into
mainfrom
claude/agent-device-1481-5cb991
Aug 20, 2026
Merged

test(bench): falsification fixtures for oracles + typed runner outcomes#1893
thymikee merged 2 commits into
mainfrom
claude/agent-device-1481-5cb991

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Implements #1481 — the two surviving deterministic PR-time quality items carved out of #1406 for the help-conformance bench (the repo's single non-gating small-model planning oracle):

1. Falsification fixtures for benchmark oracles

  • Every named expectation in EXPECTATION_SCORERS (scripts/help-conformance-case-checks.mjs, exported as KNOWN_EXPECTATIONS) now has a fixture entry in the new scripts/__tests__/help-conformance-expectation-fixtures.ts: a minimal passing witness, at least one known-bad counterexample, and a metamorphic variant where useful (a second passing plan built from different domain nouns, proving the scorer keys on plan shape rather than memorizing content).
  • scripts/__tests__/help-conformance-expectation-falsification.test.ts is the completeness gate (same "what enumerates N" shape as the existing topic-coverage/error-recovery-coverage gates): a new named expectation with no fixture fails it, and a stale fixture for a removed expectation also fails.
  • Fixtures run through the real validatePlanCommands/scoreExpectations production pipeline, not a reimplementation, so they can't drift from what the bench actually does with a model's plan.
  • Regressions included per the issue: swallowed lifecycle command prefixes (bare open/press instead of agent-device open/agent-device press), unsupported flags (wait --selector) and selector combinations (is role=... label=...), pseudo refs (@<search-ref>), shell operators (| tee), and invalid positional ordering (get @e5 text instead of get text @e5).

2. Typed runner outcomes

  • scripts/help-conformance-runner-output.mjs now exports classifyRunnerOutput, returning a discriminated RunnerOutcome (declared in the new companion scripts/help-conformance-runner-output.d.mts, following the same .mjs+.d.mts pairing already used for help-conformance-sample-outputs.mjs):
    type RunnerOutcome =
      | { kind: 'success'; raw: string; commands: string[] }
      | { kind: 'runner-error'; raw: string; message: string; reason: RunnerErrorReason };
    instead of the old raw-string detectRunnerError(): string | undefined inference.
  • runCase in help-conformance-bench.mjs now only calls validatePlanCommands/scoreExpectations for a 'success' outcome; a 'runner-error' result carries runnerError/runnerErrorReason and nothing else — a result can no longer carry both runnerError and model-validation checks.
  • summarizeResults (help-conformance-summary.mjs) now reports passRate: null for a group with zero evaluated trials (every trial was a runner error), and the CLI's aggregate-stability print renders that as N/A instead of the misleading 0/0 (0%).
  • Single-trial mode is unchanged (the Aggregate stability summary already only prints when --repeat > 1), so it still isn't mislabeled as stability evidence.

No paid model calls in either gate — everything runs through the local command-validator subprocess deterministically.

Test plan

  • pnpm typecheck — clean
  • pnpm lint (oxlint --deny-warnings) — clean
  • pnpm check:fallow (fallow audit) — clean (0 issues in changed files)
  • pnpm format:check — clean
  • npx vitest run scripts/__tests__/help-conformance-* — 100/100 passing (bench, sample-outputs, topic-coverage, error-recovery-coverage, and the new expectation-falsification suite)
  • Full pnpm test:unit (unit-core + subprocess-stub, ~7200 tests) — all green; two pre-existing &&-chain plan-validator tests hit the 5s timeout only under full-suite CPU contention and pass cleanly in isolation (unrelated to this change)
  • node scripts/help-conformance-bench.mjs --dry-run smoke-tested end to end with an override doc

Two deterministic PR-time quality gates for the help-conformance bench
(the repo's single non-gating small-model planning oracle):

- Every EXPECTATION_SCORERS entry in help-conformance-case-checks.mjs
  now has a falsification fixture (a minimal passing witness plus at
  least one known-bad counterexample, and a metamorphic variant where
  useful) in the new help-conformance-expectation-fixtures.ts, run
  through the real validatePlanCommands/scoreExpectations pipeline.
  help-conformance-expectation-falsification.test.ts is the "what
  enumerates N" completeness gate: a new named expectation with no
  fixture fails it. Counterexamples cover swallowed lifecycle command
  prefixes, unsupported flags/selectors, pseudo refs, shell operators,
  and invalid positional ordering.

- help-conformance-runner-output.mjs now returns a discriminated
  RunnerOutcome ({kind:'success',commands}|{kind:'runner-error',
  message,reason}) instead of a raw-string success inference. Only a
  'success' outcome ever reaches validatePlanCommands/scoreExpectations
  in runCase, so a runner-error result can no longer also carry
  model-validation checks, and an all-runner-error aggregate now
  reports passRate: null (rendered as "N/A") instead of "0/0 (0%)".

Fixes #1481
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.33 MB 2.33 MB 0 B
JS gzip 765.3 kB 765.3 kB 0 B
npm tarball 890.0 kB 890.0 kB 0 B
npm unpacked 3.10 MB 3.10 MB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 23.8 ms 22.2 ms -1.6 ms
CLI --help 54.5 ms 55.1 ms +0.7 ms

Top changed chunks: no changes in the largest emitted chunks.

…rowing

Thermo-nuclear pass over 4b2df0a's diff:

- help-conformance-bench.mjs's runOutcome() catch block was hand-building
  the exact {kind:'runner-error', raw, message, reason} shape that
  runner-output.mjs's private runnerError() helper already constructs for
  its own two error paths. Export it as runnerErrorOutcome so the
  discriminated union has exactly one constructor for its error variant,
  reused by both error sources instead of duplicated.
- runCase's two return branches repeated the same
  {runner, caseId, trial, outputPath} fields; pulled into a shared `base`
  object.
- Reverted bench.test.ts's rateLimitedOutcome block: it had an explicit
  `: RunnerOutcome` annotation and an if/throw narrowing guard, added only
  to give fallow's dead-code checker a "real consumer" of the type before
  the actual fix (adding the .d.mts to .fallowrc.json's ignorePatterns,
  matching the existing sample-outputs.d.mts precedent) was found. That
  workaround is now unnecessary scaffolding — replaced with the same
  flat assert.deepEqual style the surrounding assertions already use.
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head 4e4fe2517d64901b2124b98e69f697c52d1a9396: code-clean. The falsification fixtures are non-vacuous and exercise the real validator/scorer pipeline; the registry-derived completeness check catches missing and stale fixtures, and the typed outcome branch keeps infrastructure errors out of model scoring. Exact-head CI is green and size is unchanged.

Before merge, please retitle the body section to ## Validation, note the 10-file/no-expansion scope, and add Closes #1481 so the PR metadata matches the repository template.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 20, 2026
@thymikee
thymikee merged commit 250e30a into main Aug 20, 2026
28 checks passed
@thymikee
thymikee deleted the claude/agent-device-1481-5cb991 branch August 20, 2026 10:52
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-20 10:52 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant