Repository navigation
S2.6.5 — The anti-vacuity assertion set, asserted by the runner #322
Description
Parent: #293
Build target 6. Anchors: CLAIM-293.3, NEVER-293.4. Cases: 3, 11, 5, 6, 7, 21, 22.
Blocked by: #321
The single most important rule in this Story
Every
== 0assertion must be paired with a> 0assertion on the same counter, measured one run earlier.
"Run 2 made zero mutations" is meaningless alone. "Run 1 made more than zero and run 2 made zero" is the property. This generalises the house denominator rule from corpus size to effect count.
The scanning denominators stop the harness reporting success over zero skills. Only run1.mutations > 0 stops it reporting success over zero work. A harness with the first and without the second is skill-lint-green-over-zero-files with extra steps: it scans four skills, executes nothing meaningful, and reports a perfect idempotency result.
The assertion set
Run-level:
scenarios_executed > 0skills_covered == |skills/*/SKILL.md|, both sides read at run time — mutation J2: delete a verb directory and every "zero violations over the real corpus" loop stays greenskills_covered == PINNED_SKILL_COUNT, a hardcoded literal citing the plan — J2 and this fail for different reasons and neither substitutes; a disk-read on both sides passes a corpus of three verbs and a deleted onescenarios == SCENARIO_ROSTER.length- every skill has ≥ 1 scenario, asserted per-skill — an aggregate
N > 0is satisfied by four scenarios all on one verb
Per-scenario, and the ordering matters:
run1.mutations > 0evaluated beforerun2.mutations == 0is consideredrun2.reads >=that skill's## Re-entryrow count, read from itsSKILL.mdat run time — this distinguishes "decided to do nothing" from "did nothing". A run 2 with zero mutations and zero reads did not run. The least obvious assertion here and the one most likely to be omittedrun2.mutating_argv == []reported as the list, not the count
Verdict mechanics — non-negotiable
The verdict is the exit code plus a JSON artifact. The runner never greps its own stdout, and nothing greps the runner's.
Finding 37: a mutation harness decided on
"0 fail" not in stdout, and the corpus under test contained the literal "63 pass, 0 fail" — it reported SURVIVED for a mutation that killed two tests. Its recorded owner is "any future harness". This is it.docs/evidence/is full of the phrases this runner will print.
The runner exits non-zero on an empty roster, and a test invokes it against one. skill-lint exits 0 over zero files today and its non-vacuity lives only in a test file — do not repeat that.
Done when
- All the above, asserted by the runner's exit code, not only by a test file (cases 3, 11)
- Three mutations killed: empty roster, deleted verb directory, run 1 mutating nothing — each naming the scenario (cases 5, 6, 7)
- A fixture repository seeded with the runner's own success phrases still yields the correct verdict (cases 21, 22)
Refs #293
Activity
dev-pmallapp commentedon Sep 10, 2026
iai-evidence
Verdict: PASS. Artifact:
docs/evidence/322-20260910T042657Z.md, pinned at95c0af46749e5b9d912c7419beaa64c695309e8d. PR #333.Build target 6 of
docs/design/stories/293.md. Cases 3, 5, 6, 7, 11, 21, 22 PASS.CLAIM-293.3andNEVER-293.4are met for the harness's transcription of each skill, which is a different population from the skill.bun run skill-harnessexists for the first time. The command named in seven test-plan cases was in nopackage.jsonin this tree until now.Chain
1038 tests / 1024 pass / 0 fail / 14 skip / 3777
expect()· lint 0 across 121 files · typecheck clean · skill-lint 4 + 12, 0 errors · claim-lint 120 files, 0 errors, 1 warning (the expectedstories/9.mdone) ·bun run skill-harnessexit 0 · both verifier scripts PASS, required contexts still six. CI on PR #333 is 7/7.The measurement
goal-create/absent-milestonestory-create/two-unstoried-rowsstory-design/design-and-sentinelstory-test-plan/plan-and-sentinelstory-design/sentinel-onlystory-test-plan/file-onlyEvery threshold in the right-hand column is read off
skills/*/SKILL.mdat run time.run2.reads >= rowsis what separates "decided to do nothing" from "did nothing" — a run 2 with zero mutations and zero reads did not run.The last two scenarios are the surface-separation pair, which is why the mutation counter is the union of both surfaces: a forge-only count scores
story-test-plan/file-onlyas a vacuous run 1, and mutation M8 measures exactly that.Mutation testing — 16 injected, 13 killed, 3 survivors
goal-create's create step disabledrun-1-made-no-mutation; artifact showsrun2: null> 0checkcountReadsbecomes the subtractiongoal-create, not the skill predictedmutationscounts the forge onlyrenderArtifactforcesverdict: "pass"process.exit(0)## Re-entryrows reorderedThree survived their first run, and each was a real gap now closed and re-killed:
scenario-threwfailure, one failure per cause.renderReport(...).includes("verdict: pass")returns the identical answer for every fixture in the corpus. The kill had to be static:decideVerdict's own source may not containrenderReport,renderArtifact,SUCCESS_PHRASES,.includes("orstdout, with the extracted body asserted non-empty first. This is finding 37, whose recorded owner was "any future harness".renderArtifactcould emitverdict: "pass"on a failing run with every test green, because the only agreement assertion compared exit codes and the real roster never fails. S2.6.6 — The seam rung and its CI job #323's CI job may read that artifact — the disagreement would have surfaced as a green job over a red run.M4, M5 and M7 leave
bun run skill-harnessexit 0 and unchanged, and die only to synthetic scenarios built to trip them:M9 survived everywhere and is kept anyway — the tree-hash check is entailed by run 2's mutation evidence being empty over this roster, retained because the issue mandates it and M10 is its one independent killer. Recorded as redundancy rather than hidden. S1 survives by design and must not be "fixed": reordering a
## Re-entryrow changes not one assertion, which isNEVER-293.5working.Findings raised
writeFileanywhere in a module;runner.tsexposes a context method that only delegates torecorder.writeFileand imports no fs at all.packages/exec/test/port.test.tshad already rewritten its launcher detector from a call shape to an import for exactly this reason, and the standing rule is that for an assertion — unlike a ban — a false positive must be fixed. Knowing the class did not prevent reproducing it one task later. The detector now reads the capability off the import list, with a regression test asserting a delegating module is a non-writer.isMutatingGhArgvreturns false for everygitargv by construction, socalls.length - attemptedMutations.lengthscoresgit addandgit commitas reads and a run 2 that committed four times would satisfyreads >= 4. M5 is the measurement: the subtraction passes the real roster and dies only to a deliberately mixed one.PATCHnor--milestoneongh issue edit. Two## Re-entrybranches are dead in this roster for a correct reason. They were left implemented and unexercised rather than forced: a scenario built to reach them would be built around the fake's gaps. Owner: S2.6.6 — The seam rung and its CI job #323's audit and S2.6.7 — The live rung, never required #324's live rung.What this does NOT establish
CLAIM-293.11and case 18 are S2.6.6 — The seam rung and its CI job #323's. This task authored them and deliberately did not rule on their faithfulness — that would be self-attestation.SKILL.md. The runner prints threeNOT VERIFIEDlines saying so on every run. Gate ruling G2 forbids reading this green as licence to upgradeCLAIM-41.8orCLAIM-47.4.CLAIM-293.6half (ii) is still NOT MET —--injectis deliberately absent; case 9 closes at S2.6.6 — The seam rung and its CI job #323.ghargv this repository builds has ever been executed, and no build target owns paying that bill #330) is still unpaid — all six scenarios answered everyghcall from S2.6.3 — The stateful fake forge: able to lie, and non-enforcing #320's fake.shouldRetryResponseanswersretry: falsefor the transient connection error the reference tells you to retry #331 untouched.dev-pmallapp commentedon Sep 10, 2026
Closed by the merge of PR #333 into
story/293-s2-6-skill-execution-harnessat6e05328.A task PR targets the story branch, so GitHub's closing keywords never fire (
CONTRIBUTING.md, Pull requests); this closure is explicit and cites the merge commit.Verified PASS before merge — evidence
docs/evidence/322-20260910T042657Z.md,## iai-evidencesentinel posted, CI 7/7 on the PR head95c0af4.Carried forward to #323, deliberately not closed here:
CLAIM-293.11/ case 18 — the four transcriptions are unaudited; ruling on them here would have been self-attestation.CLAIM-293.6half (ii) —--injectis deliberately absent from the CLI; case 9 needs the truncated-list lie armed per scenario.