test(bench): falsification fixtures for oracles + typed runner outcomes - #1893
Merged
Conversation
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
Size Report
Startup median (7 runs, lower is better):
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.
Member
Author
|
Reviewed exact head Before merge, please retitle the body section to |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
EXPECTATION_SCORERS(scripts/help-conformance-case-checks.mjs, exported asKNOWN_EXPECTATIONS) now has a fixture entry in the newscripts/__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.tsis 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.validatePlanCommands/scoreExpectationsproduction pipeline, not a reimplementation, so they can't drift from what the bench actually does with a model's plan.open/pressinstead ofagent-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 textinstead ofget text @e5).2. Typed runner outcomes
scripts/help-conformance-runner-output.mjsnow exportsclassifyRunnerOutput, returning a discriminatedRunnerOutcome(declared in the new companionscripts/help-conformance-runner-output.d.mts, following the same.mjs+.d.mtspairing already used forhelp-conformance-sample-outputs.mjs):detectRunnerError(): string | undefinedinference.runCaseinhelp-conformance-bench.mjsnow only callsvalidatePlanCommands/scoreExpectationsfor a'success'outcome; a'runner-error'result carriesrunnerError/runnerErrorReasonand nothing else — a result can no longer carry bothrunnerErrorand model-validation checks.summarizeResults(help-conformance-summary.mjs) now reportspassRate: nullfor a group with zero evaluated trials (every trial was a runner error), and the CLI's aggregate-stability print renders that asN/Ainstead of the misleading0/0 (0%).Aggregate stabilitysummary 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— cleanpnpm lint(oxlint --deny-warnings) — cleanpnpm check:fallow(fallow audit) — clean (0 issues in changed files)pnpm format:check— cleannpx vitest run scripts/__tests__/help-conformance-*— 100/100 passing (bench, sample-outputs, topic-coverage, error-recovery-coverage, and the new expectation-falsification suite)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-runsmoke-tested end to end with an override doc