Skip to content

Encode the sixteen prose mutations as patches, and judge them - #7

Merged
systemslibrarian merged 1 commit into
mainfrom
mutation/encode-patches
Oct 1, 2026
Merged

systemslibrarian merged 1 commit into
mainfrom
mutation/encode-patches

Conversation

@systemslibrarian

Copy link
Copy Markdown
Owner

Every record in e2e/verdict-mutations.json described its mutation as prose: "src/dpf/gen.ts — flip every direction bit: X becomes Y". A person can follow that; a script cannot, so nothing replayed it and the recorded kills were the author's side of the claim.

Each record now carries a patch beside the prose — file, an anchor that must occur exactly once, and its replacement. The prose stays, because some of it holds something a patch cannot: key-part-root records that the deeper serializer mutation (ROOT_MATERIAL_BYTES = SECURITY_BYTES) was tried first and rejected as evidence, since it stops the key round-tripping and takes the whole suite red, which is not a readable kill on that marker.

All sixteen encodings were right first time

Not what the adoption note predicted — two of privacy-pass's twenty were wrong. The reason is that this ledger's prose already named the file and gave an exact before/after pair. Two needed a decision, both resolvable by reading rather than by judging what a claim meant:

  • server-view-0 — if (share[index] === 0) continue; occurs twice in src/pir/server.ts, at lines 19 and 55. The record says "in serverAnswerProgressive", which begins at line 33, so the anchor carries the following foldedRecords += 1;, a line only the progressive version has.
  • record-match — verdict.className = \verdict ${...}`occurs twice; the record names thematches` ternary, which is the second.

The runner

scripts/mutation.mjs, named after sleeve-check and privacy-pass, loop after pqxdh-wire, enforcing all four rules:

  1. the owning test passed unmutated in the same run, checked per record from assertedBy rather than from the suite's exit code;
  2. the patch changed the file, and the file returns to its original md5 — both checked for every selected patch before any is applied, and the md5 re-checked after each one, aborting the run if a restore does not land;
  3. the bundle hash moved, and the failure does not match one of four shapes meaning the code never ran (build error, server never started, nothing on the port, port already held). CI=1, --strictPort on 4698;
  4. a patch that does not compile is DOES NOT BUILD, never a kill.

It archives HEAD into a temporary tree and mutates that, so a session dying mid-run cannot strand an inverted condition in a file that also holds real work, and it refuses a dirty working copy.

The gate is --project=verdict-ledger, which the config gives dependencies: ['claims'], so claims, flows and verdict-coverage run first and verdict-ledger.spec.ts then asserts every record's triple was executed. A -g run cannot exit 0 here. When a mutation kills, the claims project goes red and verdict-ledger is skipped — which is why the verdict is read from the owning test's own result in the JSON report.

Result

16 record(s) make the round trip
baseline bundle 0165188c715d · baseline green, 21 specs

 1/16  KILLED  tree-point          0165188c715d -> cd330e8b42c5 -> 0165188c715d
 ...
16/16  KILLED  prg-node-width      0165188c715d -> 9abe88a93344 -> 0165188c715d

16/16 killed, 0 survived, 0 neither

Every line carries the hashes it moved between and back. Results are not written into the ledger: verdict-ledger.spec.ts already fails the run when a recorded kill did not execute, so an archived observed string would duplicate an enforced answer, and §4.1c calls writing back optional and enforcement the requirement.

Two files change: the ledger gains patch on each of 16 records, and the runner is new. No source, test or workflow is touched.

🤖 Generated with Claude Code

Every record in e2e/verdict-mutations.json described its mutation as PROSE:
"src/dpf/gen.ts — flip every direction bit: X becomes Y". A person can follow
that; a script cannot, so nothing replayed it and the recorded kills were the
author's side of the claim.

Each record now carries a `patch` beside the prose: the file, an anchor that
must occur exactly once, and its replacement. The prose stays, because some of
it is worth keeping that a patch cannot hold -- key-part-root records that the
deeper serializer mutation (ROOT_MATERIAL_BYTES = SECURITY_BYTES) was tried
first and rejected as evidence, because it stops the key round-tripping and
takes the whole suite red, which is not a readable kill on that marker. The
prose says why; the patch is what runs.

ALL SIXTEEN ENCODINGS WERE RIGHT FIRST TIME, which is not what the adoption
note predicted (two of privacy-pass's twenty were wrong), and the reason is
that this ledger's prose already named the file and gave an exact before/after
pair. Two needed a decision, both resolvable by reading rather than by judging
what a claim meant:

  server-view-0   `if (share[index] === 0) continue;` occurs TWICE in
                  src/pir/server.ts, at lines 19 and 55. The record says "in
                  serverAnswerProgressive", which begins at line 33, so the
                  anchor carries the following `foldedRecords += 1;` -- a line
                  only the progressive version has.
  record-match    `verdict.className = `verdict ${...}`` occurs twice; the
                  record names the `matches` ternary, which is the second.

THE RUNNER. scripts/mutation.mjs, following sleeve-check and privacy-pass for
its name and pqxdh-wire for its loop, enforcing all four rules:

  1. the owning test passed unmutated in the same run, checked per record from
     `assertedBy` rather than from the suite's exit code;
  2. the patch changed the file, and the file returns to its original md5 --
     both checked for every selected patch BEFORE any is applied, and the md5
     re-checked after each one, aborting the run if a restore does not land;
  3. the bundle hash moved, AND the failure does not match one of four shapes
     that mean the code never ran (build error, server never started, nothing
     on the port, port already held). CI=1 and --strictPort on 4698;
  4. a patch that does not compile is DOES NOT BUILD, never a kill.

It archives HEAD into a temporary tree and mutates that, so a session dying
mid-run cannot strand an inverted condition in a file that also holds real
work, and it refuses a dirty working copy rather than reporting results about
a tree nobody else has.

The gate is `--project=verdict-ledger`, which playwright.config.ts gives
`dependencies: ['claims']`, so claims, flows and verdict-coverage run first and
e2e/verdict-ledger.spec.ts then asserts every record's triple was executed. A
`-g` run cannot exit 0 here: the named test passes and the ledger assertion
fails on the other fifteen. When a mutation does kill, the claims project goes
red and verdict-ledger is SKIPPED, which is why the verdict is read from the
owning test's own result in the JSON report.

  16 record(s) make the round trip
  baseline bundle 0165188c715d, baseline green, 21 specs
  16/16 killed, 0 survived, 0 neither

Every line carries the hashes it moved between and back. Results are not
written into the ledger: verdict-ledger.spec.ts already fails the run when a
recorded kill did not execute, so an archived `observed` string would be a
second copy of an enforced answer. §4.1c calls writing back optional and
enforcement the requirement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@systemslibrarian
systemslibrarian merged commit fcd81f3 into main Oct 1, 2026
4 checks passed
@systemslibrarian
systemslibrarian deleted the mutation/encode-patches branch October 3, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant