Skip to content

test(release): drive the inconclusive publish guard with registry census cells - #1604

Merged
davidfarah2003 merged 19 commits into
mainfrom
fix/1584-publish-guard-cells
Sep 15, 2026
Merged

davidfarah2003 merged 19 commits into
mainfrom
fix/1584-publish-guard-cells

Conversation

@davidfarah2003

@davidfarah2003 davidfarah2003 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

What this changes

The publish preflight refuses a release when the registry census cannot be read, but no fixture ever drove that state. The guard was unverified rather than dead, and deleting it whole left the suite green. This adds the cells that drive it.

Refs #1584.

Direction was established before any code was written

The issue asks whether the guards should be deleted. The A/B differential was built first: A is the base commit verbatim, B is A with exactly one guard block removed and nothing else. Per cell rows, not a pass count.

leg suite result cells differing from A
A, base verbatim 57 passed, 0 failed n/a
B, inconclusive block deleted 57 passed, 0 failed 0 of 57
B, incomplete block deleted 57 passed, 0 failed 0 of 57

The differential agreed everywhere, which reproduces the report exactly. That is a result, not a failure to find one, and it does not by itself say the guards are dead. The comparator was checked against a copy of the A rows with a single verdict flipped, and reported that difference, so the zeros above are a finding rather than a broken reader.

Reachability was then argued from the code path and measured against the shipped function, and the two rungs turned out to be different cases.

The inconclusive rung is live, and the fix is fixtures

readExactVersion returns unknown:<status> for any response that is neither 200 nor 404, and unknown:<message> when the fetch throws. Driving the shipped preflightNpmPublish over every composition of three statuses across three packages, 27 in total, the inconclusive rung is reached 19 times.

composition of exact version responses verdict reached
404/404/404 all-absent
200/200/200 all-present
200/200/404 mixed
503/404/404 inconclusive
404/404/503 inconclusive
any composition containing one unreadable response inconclusive, 19 of 27

A first version of that enumeration was wrong and is worth naming: it matched packages by substring against the request URL, and the bare name cotal-ai is a substring of the encoded scoped names, so every request matched the same package and the tally depended only on the last status. It was caught by printing the real URLs. The table above dispatches by call order and asserts, for all 27 rows, that the statuses delivered are the ones intended, with 0 mismatches.

So the guard is load bearing and the remedy is the one the issue suggests. Added cells:

  • a 503 on one exact-version read refuses the release as inconclusive
  • the inconclusive census names the unreadable package and carries its registry status
  • an inconclusive census refuses before any OIDC exchange, trust GET, or publish call
  • an inconclusive census prints the complete package and version census before exiting
  • a 302 on every exact-version read refuses the release rather than reading as absent
  • a thrown fetch on the exact-version read refuses the release as inconclusive
  • the inconclusive census carries the thrown transport message, not a bare status
  • a thrown exact-version read stops after the census and never reaches OIDC or trust work
  • accept control: an all-present census still reports the no-op and is not read as inconclusive

With these in place the same deletion that was invisible now reds by name:

leg suite result red cells
with fix, inconclusive block deleted 89 passed, 4 failed a 503 on one exact-version read refuses the release as inconclusive; an inconclusive census refuses before any OIDC exchange, trust GET, or publish call; a 302 on every exact-version read refuses the release rather than reading as absent; a thrown fetch on the exact-version read refuses the release as inconclusive
with fix, unmodified 93 passed, 0 failed none

The incomplete rung is not deleted, and not covered either

The issue proposes the same remedy for the incomplete block. That one is a different case, and deleting it would be wrong for a different reason than the report assumes.

With no unknown rows, present and absent partition the census, so zero present rows forces absent equal to rows, which the earlier all-absent rung already claims. Enumerating every present, absent and unknown partition gives 0 that reach incomplete. The enumerator can emit that label: removing the all-absent rung makes it reachable in 3 partitions, which is the control for that zero.

So it is an unreachable backstop, not a tested guard and not a confirmed dead one. Deleting it would remove a refusal that becomes live the moment the status domain grows a fourth outcome, and no test would notice. Instead this pins the premise, so the reasoning cannot rot silently:

  • the census values observed across the 200, 404 and 503 answers are present, absent and unknown only
  • the 4 return statements the compiler finds in readExactVersion each yield present, absent or an unknown: value, so no fourth census outcome reaches the verdict ladder unbucketed
  • under the shipped bucket predicates every production census row lands in exactly one bucket, so no row is double-counted by the verdict ladder and none is invisible to it

The second and third bullets are quoted from the live suite rather than paraphrased, which they were until this revision: the paraphrases matched no cell in the file, so a reader checking them found nothing and could not tell a renamed cell from a deleted one. The second cell's name is a template literal carrying the live return count, so the leading 4 moves with the source.

A review falsified the first version of this claim, and the correction is the second cell

An earlier revision of this PR carried one cell here and claimed it reds when a fourth readExactVersion outcome appears. That claim was FALSE, and a reviewer disproved it with a parse-checked, byte-different mutant adding a branch that returns a new value on HTTP 418: the suite completed with 69 of 69 passed, 0 failed, and the named cell red 0 of 1.

The mechanism is worth stating because it is not specific to this file. That cell drives three registry answers and records what the census carried, so it is an OBSERVATION over the statuses it happens to send. A branch guarded by a status it never sends is invisible to it by construction. A sampling test cannot support a universal claim, and the claim was the thing that was wrong, not the cell.

The second cell closes it structurally: it reads the shipped source, extracts readExactVersion and enumerates EVERY return, requiring each to be "present", "absent" or an unknown: template. A new branch is a new return whatever status guards it.

Attacking the new cell rather than reasoning about it then found a real escape inside it. Finding the body by slicing to the first closing-brace line is defeated by a template literal that contains one, which ends the slice early and drops every return below it. Walking brace depth while skipping string, template and comment context fixes it. Measured against eight sources, each parse-checked at exit 0 and each asserted byte-different from the real file before scoring:

source extractor verdict
real source 4 returns, all in domain GREEN
the reviewer's 418 branch 5 returns, 1 out of domain RED
return spelled across lines 5 returns, 1 out of domain RED
value returned via a helper 5 returns, 1 out of domain RED
ternary inside the return 5 returns, 1 out of domain RED
value bound to a variable first 5 returns, 1 out of domain RED
template literal truncating the slice 5 returns, 1 out of domain RED
the function renamed extraction fails RED

The last row is deliberate. An extractor that stops finding its target reds rather than passing, because an extractor reporting nothing is indistinguishable from a source with nothing wrong.

A second reviewer then supplied three more shapes and all three red as well: a finally block overriding the returned value, a helper arrow declared INSIDE the function so its literal is not outside the extracted body, and a near-miss string such as "absent " with a trailing space. Eight attacks, eight reds, one green on the real source.

What the escape was actually worth, which is less than the first correction implied

The same reviewer also showed the hazard is narrower than "a fourth outcome appears and nothing notices", and independent enumeration here reproduces it exactly. readExactVersion ends in a catch-all returning an unknown: template, so a genuinely new HTTP status is absorbed and stays in domain: 418 reads as unknown:418. Producing an out-of-domain value requires ADDING A RETURN, not meeting a new status.

And if one were added, the ladder still refuses. Enumerating every three-row partition over present, absent, unknown and an escaped value, with the ladder transcribed from the shipped lines:

measure rows
total partitions 64
partitions containing an escaped value 37
escaped value reaches a refusal 37 of 37
escaped value reaches a publish 0 of 37
CONTROL, incomplete rung deleted 7 of 37 now fall through to a publish

The control is what makes the zero mean anything, and it also settles the issue's original question. The incomplete rung earns its place because it REFUSES: delete it and seven escaped-value states reach a live recursive publish. Its justification is its own measured refusal behaviour, not the tripwire's promise. That makes the false sentence a real defect in a secondary cell, now fixed, rather than the load-bearing argument for keeping the guard.

Both escapes were then driven through the real suite rather than through the probe alone. The reviewer's 418 mutant and the template-literal truncation each produce exit 1 with the completion marker present and exactly one red, named, printing the offending return. The restore was verified by blob sha against the commit in both cases, not by a clean git status. The reviewer's mutant is also pinned in the mutation config, so the escape stays closed rather than having been closed once.

The backstop claim was a prediction, so it was measured

The paragraph above says the incomplete rung becomes reachable if a later change lets a row carry a value outside the domain. That was an argument, published beside measurements, so it was tested rather than left as reasoning.

Making readExactVersion map 404 to an out-of-domain value, using a constant that already exists in the same file rather than an invented one, drives the shipped function straight into the backstop:

drift applied to readExactVersion outcome
404 maps to an out of domain value throws, the packages that would publish are not the complete fixed group
census rows printed 3 of 3, each carrying the out of domain value

So the rung is a live backstop rather than dead code, and the reason for keeping it is now measured.

Testing that also exposed a real weakness in the suite, pre-existing and worth fixing here because it defeats the cell this PR adds. The manual token census called the preflight without a guard, so a refusal there aborted the file and every cell below it reported nothing at all. Under the drift the suite stopped after 7 reds with no completion marker, and the domain cell, which sits below that call, never ran. Read as a pass count that looks like the cell failing to fire, and green by not running is indistinguishable from green by passing.

29 of the 68 cells at the base commit already sat below that call. The call maps 404 to absent and reaches ready, so it refuses only if the 404 mapping changes, which is exactly the drift the domain cell exists to detect: the cell meant to catch that class was among the cells that class silenced.

The call now captures its own refusal and names it, so the cells below it still run:

  • the manual token census completes rather than aborting the remaining cells
out of domain drift completion marker red cells
before the guard absent, file aborted 7, domain cell never ran
after the guard present 23, including both domain cells by name

The cell predicate was also checked directly, after asserting the predicate text is a true substring of the suite so the probe grades the shipped code and not a transcription of it: it reds on a value set containing an out of domain value and stays green on an in domain one, so it discriminates rather than being always red.

One mutation was dropped rather than chased

An added mutation that reorders the inconclusive rung below all-absent survived the proof. It looked like a coverage gap and is not one: because a row in absent is never in unknown, absent === rows already implies unknown === 0, so the two orderings cannot disagree. Two independent checks agree, one enumerating all 35 partitions of the bucket counts and one applying the reorder to the module itself and comparing all 27 measured compositions, byte identical.

Writing a cell to kill it would have produced a test that passes for the wrong reason, so the mutation is dropped and the disjointness it relies on is asserted instead. Both comparators were first shown able to report a disagreement against a planted ordering that genuinely differs; the first planted ordering was itself equivalent and the control correctly refused to license the result until it was replaced.

Evidence a reviewer can re-run

pnpm smoke:npm-publish-preflight
node scripts/mutation-proof.mjs --config bin/smoke/mutations/npm-publish-preflight.json
check result
suite, unmodified 93 passed, 0 failed, exit 0, completion marker present
mutation proof 38 of 38 killed, 0 survived, 0 wrong-red, exit 0
suite at base, before this change 57 passed, 0 failed

This branch is now MERGED onto current main 794e3d2ec, at merge commit e2e244c3b, with merge-base 81dc5b7144272d68234b54401d9ae716e1cabe6e. Main moved again after the previous revision of this body was measured, so every figure below is re-pinned rather than carried over. Two commits touched the same suite and the same fixture: dfa3d0273 (#1618, ten mutation entries pinning each census state refusal branch) and 47fb01548 (#1611, a comment correction). Exactly one textual hunk conflicted, in the fixture, and it is resolved by keeping BOTH sides. No entry from either side was dropped.

Old figure with the sha that produced it, beside the new figure with the sha that produces it now. Nothing is restated under a sha that did not measure it:

figure before, at 5a5c888eb after, at e2e244c3b
mutation fixture entries 28 38
mutation proof 28 of 28 killed 38 of 38 killed, 0 survived, 0 wrong-red
suite, unmodified 93 passed, 0 failed 93 passed, 0 failed, unchanged
inconclusive block deleted 89 passed, 4 failed 89 passed, 4 failed, unchanged, the same four cells by name

The suite total does not move, and that is a measurement rather than an omission. dfa3d0273 is fixture-only: 80 added lines, ten mutation entries, no new cell. Each of its entries pins the cell the enumerated branch set has one entry per refusal in the grader, so the list cannot rot, which already existed at the merge base. 47fb01548 changes comments only. The merged suite file differs from this branch's by 11 lines, all of them comment lines and 0 non-comment lines.

Both sides survive, measured as a set rather than asserted:

check on the merged tree at e2e244c3b result
merge commit parents 2, 5a5c888eb and 794e3d2ec, so main is an ancestor rather than a fast forward
conflicted hunks resolved 1 of 1, in the fixture; the suite file auto-merged
fixture entries 38 = 23 at the merge base + 10 from #1618 + 5 from this branch
base entries missing from the merged fixture 0 of 23
#1618 entries missing 0 of 10
this branch's entries missing 0 of 5
entries in the merged fixture present in NO source 0
duplicate entries 0
entries identical between the two sides 0, so nothing was deduplicated and nothing was dropped as redundant
find anchors matching exactly once in the merged tree 38 of 38
distinct expectRed cells still present in the merged suite 26 of 26
combined suite 93 passed, 0 failed, exit 0
full pinned proof 38 of 38 killed, 0 survived, 0 wrong-red, exit 0
denominator source the fixture file, 38 entries, equal to the 38 graded rows counted from the run
both mutated files after the run restored, blob sha identical to the commit

The proof instrument was positive-controlled rather than trusted, because a reader never shown to return anything but KILLED is an instrument and not a measurement. Three entries in one run produced three different verdicts:

positive control verdict what it establishes
find and replace identical ERROR, "mutation produced an identical file, it did not apply" the no-op guard is live, so a scored kill is not an accusation about nothing
a header comment no cell reads is reworded SURVIVED the survived axis can be non-zero, so "0 survived" is a result and not a constant
a real fixture entry that must kill KILLED, red on its named cell, 89 marks against baseline 93 the kill axis discriminates in the same run

The mutation proof prints its own scope limit: it proves the suite depends on the mutated code, not that a real entry point reaches it. The cells call the exported function directly, so that caveat applies to them. Proved separately by running the actual release command, node scripts/preflight-npm-publish.mjs, the same one ci:publish runs, against a fake registry:

registry behaviour child exit refusal census rows carrying the unreadable status write shaped calls
503 on the exact version read 1 registry census was inconclusive for 22/22 packages 22 0 of 22 requests
404 on the exact version read, accept control 0 none 0 0 of 22 requests

The 404 control is what makes the 503 row mean something: the same harness and the same command produce no refusal when the registry answers normally.

Scope

Four files touched, and this is no longer test-only. scripts/preflight-npm-publish.mjs gains three
named, exported bucket predicates and a CENSUS_BUCKETS table, and its verdict ladder calls them
instead of inlining the comparisons; scripts/preflight-npm-publish.d.mts is regenerated from the
module to match. The other two are the suite and its mutation fixture, both under bin/smoke.
The production change is behaviour-preserving, but it is a production change and the earlier
"test only, two files" wording was carried over from a revision where it was true.


Head note, superseded. This paragraph described the merge commit e2e244c3b1c73a5e7e1d60a50173f6f3a952ecb4 as the push, with a fixture of 38 entries, a suite at 93 passed and a proof of all 38 killed. That commit is real and is still an ancestor of the current head, but every figure in the sentence is two revisions out of date: the fixture now carries 49 entries and the suite 119 cells. The current state is recorded in the head note at the end of this description, which is the one to read.

v2: Pin the census return domain with the compiler, and the buckets with the shipped predicates

Two cells in this PR claimed more than they proved. A three-family panel at e2e244c3b returned
one APPROVE and two REQUEST_CHANGES, and the two REQUEST_CHANGES were reached independently and
landed on the same two cells. Both were right. The feature work is unchanged; the instruments
behind those two cells are replaced.

Head: f16074385f9297b287186eb2c786a3bb2a5854b1 (branched from e2e244c3b).


1. The return extractor could not see the returns it claimed to enumerate

The cell asserted a universal: every return in readExactVersion yields present, absent or an
unknown value, so no fourth census outcome can reach the verdict ladder unbucketed. The instrument
behind that universal was:

[...body.matchAll(/\breturn\s+([^;]+);/g)]

over a brace-walked body. That regex requires a terminating semicolon and a non-empty expression,
so it structurally cannot represent a bare return;, and a semicolon-free return is swallowed into
the next capture. The shipped function has four in-domain returns, so the cell passed for the
right reason and would have kept passing after a change that broke it.

What it is now. Returns are enumerated by the TypeScript compiler: readExactVersion is located
as exactly one top-level FunctionDeclaration taken from the parsed file's own statement list,
and every ReturnStatement inside it is collected, with the walk stopping at any function
boundary, constructors and class static blocks included.

That sentence is this specific in the third revision because the second revision's version of it
was wrong in a way the prose hid. Location was previously a recursive descent that kept the LAST
declaration of the name, so a nested same-name declaration replaced the real target and the cell
read a clean decoy while the shipped function returned an out-of-domain value. Zero matches and two
or more are now both refusals, and the refusal carries the count. A return is in domain only when the syntax pins its
value: the string literals present or absent, or a template whose head begins unknown:.
Anything else, including a bare return, an identifier, or a call, is out of domain and reds.

What the cell claims now. Not "every return" as an unqualified universal, but the N return
statements the compiler finds
, with N printed in the cell's own name. At this head N is 4 and the
transcript says so.

The printed count is a real signal only because location is now pinned, and the second revision
claimed it unconditionally when it was not yet true. Under the shadow that defeated the old lookup,
a fifth return in the shipped function did NOT change the printed count: the enumerator was reading
the four-return decoy, so the rendered name stayed "the 4 return statements ...", byte-identical to
a clean run, and the transcript offered a reader no signal at all. With the top-level pin the count
moves as the sentence always said it would, which is measured here rather than asserted: the
shadowing mutation renders the cell at 5 returns.

The tell

Introducing a bare return; into the shipped source must make this cell red. It now does:

✗ FAIL: the 5 return statements the compiler finds in readExactVersion ...
  [ 'return; -- bare return: yields undefined, which no census bucket claims' ]
SUITE COMPLETE: 104 passed, 1 failed

node --check exits 0 on that source, so it is a change a commit could really land. Under the
retired regex the same edit left the cell green.

The eight escapes, as evidence rather than as proof

Eight shapes the retired regex passed clean are now asserted to be refused, each parse-checked so
it is a real change rather than a syntax error refused for the wrong reason. This table is
evidence about the instrument, not a proof of closure: it says these eight shapes are caught,
not that no ninth exists.

shape what the retired regex did
bare return; invisible: the capture requires a non-empty expression
semicolon-free return, ASI swallowed into the next return's captured text
two semicolon-free template returns laundered: one blob began `unknown: and ended `
out-of-domain string containing ; truncated at the semicolon inside the literal
out-of-domain template containing ; truncated at the semicolon inside the template
regex literal /}}/ above an escape desynchronised the brace walk
return of an identifier accepted as text: an expression is not a value
return of a call accepted as text: same failure

Two controls keep this from being a parser that refuses everything: a positive control asserting the
unmodified skeleton yields exactly four in-domain returns, and a false-positive control asserting a
nested function's return is not attributed to readExactVersion.

2. The disjointness cell restated the predicates it should have checked

The cell re-implemented bucket membership inline and asked whether each row satisfied exactly one.
A reviewer checked all three predicate pairs and found no string can satisfy two, so the
cell's documented purpose, catching a row in two buckets, was unreachable by construction. It
was an unkillable cell, which is the exact defect the commit it replaced claimed to remove.

What it is now. The three predicates are exported from the script as isUnknownRegistry,
isPresentRegistry and isAbsentRegistry, and the verdict ladder itself calls them, so they
are the shipped membership rule rather than a second copy. The cell applies them to production rows
and is renamed to what it proves: under the shipped bucket predicates every production census row
lands in exactly one bucket
. Both failure directions are live, since a row matching zero buckets
and a row matching two are both counted.

It can now fail. Widening the shipped absent bucket to also claim unknown: rows reds it
(104 passed, 3 failed). Under the previous cell that same production change left the copy green.

A second cell extends the check from the three rows the probe happens to produce to the whole value
domain the enumerator proved readExactVersion can return.

A gap found by attacking the fix

Rewriting one CENSUS_BUCKETS entry to an equivalent inline arrow left all 105 cells green. The
exported table agreed with the ladder today but was free to drift, which would have restored the
transcription defect one layer up: the suite would grade a copy again, just a copy with export on
it. Two cells close it, one requiring the ladder to call the predicates by name (read with the
same parser), one requiring each table entry to be the exported function by identity. Both
carry mutations and both were confirmed red before being committed.

3. Production change

Minimal and behaviour-preserving: three predicates named and exported, the ladder's three filters
calling them instead of inlining the comparisons, and a CENSUS_BUCKETS table. The declaration is
regenerated from the module, as the drift cell requires.

Grading

49 mutations, all killed, each red and naming its own cell (All 49 mutation(s) killed. The suite discriminates.). Every mutation was verified to bite under real conditions: its find
matches exactly once at this head, applying it genuinely changes the file, and applied alone
the suite goes red with the cell it names red, not merely some cell. No report-all switch was
used; a bite proved only under one is not a bite proof, because report-all continues past the first
casualty. Every find is code-only, so no anchor can be disarmed by a commit that reflows a
comment.

The earlier revision of this section said "all 45 killed" and "independently verified to bite", and
review found that only the mechanical half of that was witnessed, correctly. It is now witnessed in
full, and the full run also found something the previous summary had not: one inherited mutation
graded WRONG-RED
. Its expectRed quoted a cell name retired when the enumerator was rewritten to
use the compiler, so a genuine, correctly-aimed red was being graded as a miss. It is re-tagged on
the invariant substring of the cell name, since that name is a template literal carrying the live
count. That defect predates this revision: at the previous head the tag matches the suite file zero
times.

Twelve mutations are new or retargeted, which is also the row count of the table below: eleven new
and one retargeted, re-derived from the fixture against the 38-entry state this revision started
from. Earlier revisions of this sentence said seven and then eight, both of which disagreed with the
twelve-row table printed directly beneath them.

One further correction, recorded because a reader would otherwise re-derive it. The two fixture
entries that inline the absent comparison are adjacent and nearly identically worded, and they are
not interchangeable: one rewrites the module-level CENSUS_BUCKETS table, the other rewrites the
ladder's own filter. Only the second is visible to the ladder enumerator, because the table sits
outside preflightNpmPublish. Measured: applying the bucket entry leaves that enumerator's callee
list byte-identical, so it could not defeat the ladder cell even in principle. The suite now reads
both entries out of the fixture by name and asserts which is which, and a cell records that the
bucket entry is graded instead by a runtime identity comparison over imported function objects,
which no textual decoy can launder. The by-name lookups must also resolve: a missed lookup is
otherwise silent, and while building this a renamed entry deleted three cells from the run while
reporting zero failures.

mutation cell it reds
bare return added the N return statements ...
semicolon-free return, ASI the N return statements ...
fourth status-specific outcome (retargeted) the N return statements ...
shipped absent widened to overlap unknown ... every production census row lands in exactly one bucket
enumerator stops refusing a bare return the return enumerator refuses a bare return
enumerator descends into nested functions a return inside a nested function is not attributed
CENSUS_BUCKETS entry drifts off the export each CENSUS_BUCKETS entry is the exported predicate object
ladder inlines instead of calling the export the verdict ladder buckets rows by calling the exported predicates
census lookup reverts to last-match-wins a nested same-name readExactVersion decoy cannot replace the shipped declaration
boundary reverts to the hand-rolled union a return inside a constructor is not attributed
shadowing nested readExactVersion appended the N return statements ...
shadowing nested preflightNpmPublish appended the verdict ladder buckets rows by calling the exported predicates

The last eight grade the instrument itself, so it cannot be quietly relaxed back into the defect it
replaced. The final two are the shadowing appends: one per enumerator, applied to the shipped source
rather than a synthetic one.

Suite: 93 → 119 cells, 0 failed (twelve added at this revision, none removed). Fixture: 38 → 49 entries, every existing entry preserved BY NAME, 0 dropped, and 43 of the 45 inherited objects byte-identical. The two that are not: one expectRed/cell re-tag (entry 27, which keeps its mutation and moves to the renamed cell) and one find/replace re-anchor. An earlier revision of this sentence claimed every inherited entry was byte-identical, which was false in exactly those two places; preservation is by name, not by bytes. One existing
anchor was re-anchored rather than edited in meaning: renaming the boundary predicate rotted it to
zero matches, and the one-line repair would have been ambiguous, because that line now occurs twice
once the ladder walk gained the same guard. It is anchored on a two-line code-only window. The shared 401
find remains legitimate double-grading (entry 21 deletes the branch, entry 26 corrupts it to
createPackage), and both still red for distinct reasons.

Corrections to figures quoted in the review packet

  • The merge at e2e244c3b combined 28 + 33 → 38 unique, 0 dropped, 0 invented (23 shared). The
    brief said 27 + 32 → 37. The property is exactly as described and still holds; the counts were
    each low by one. Measured with a reader proven against the mutations key and against a bad ref.
  • merge-tree --write-tree against live main 6cc504beb returns 0, but a real rebase conflicts,
    because e2e244c3b is itself a merge commit and a linear rebase replays a pre-merge commit. A
    real merge of live main is clean, and the suite is 107/0 and all 45 anchors intact on the
    merged tree. Reported because a zero from an unproven reader is a claim about the instrument.

Head note: this revision is 7d9f125, fourteen commits rebased onto live main 08cff35 (which carries #1626, #1628 and #1605). The rebase of the previous head conflicted twice in the preflight mutation fixture and was resolved as a union, not by taking this branch's side: an earlier analysis of this PR against an older main concluded main's entries were a strict subset of this branch's, which is false against current main because ten census-state refusal entries landed in between. Final fixture: 49 entries, 49 unique names, 0 dropped from main, 4 new here.

Measured at this head on a second machine, with the real instruments: the preflight suite 119 passed, 0 failed; bin/smoke/mutation-fixtures.smoke.ts 437 files, 2,548 anchors, 0 dead, 0 ambiguous, 0 spanning prose; suite-ambient-env PASS (this suite strips COTAL_ keys before spreading the environment into its child); ci-fragments and docs voice clean; shard-stability STABLE against main. The full mutation proof over the 49-entry fixture is recorded on the checks tab through CI's reproof workflow and was run on the second machine before the push.

No catalogue, spec or docs file is touched here, so the docs-bundle gate is not triggered by this branch; run anyway on the second machine, the chain's regeneration is a no-op.

Head note, v4 revision. This revision is e7ec3e6, three commits on top of 7d9f125. Everything above this note that is not explicitly marked superseded describes 7d9f125 and its figures are that sha's; the figures below are this sha's and are not interchangeable with them.

A review found that ladderFilterCallees asks whether the ladder's TEXT contains a call to each exported predicate, which a call inside if (false) satisfies while the live ladder has drifted. Measured at 7d9f125, each bucket drifted alone with a dead call retained: unknown 119/0 NOT CAUGHT, present 119/0 NOT CAUGHT, absent 116/3 where those three reds are the existing positive control and the two decoy cells failing because their find anchors on the exact absent line the drift rewrote. That is anchor rot, not detection, so all three buckets were exploitable.

ladderConsumedBuckets pins the ladder by data flow instead: the sole top-level preflightNpmPublish, the sole registryVerdict declaration in its own statement list, the identifiers that initialiser reads, each resolved to a declaration in that same list. For each of those consumed bindings the reader requires a direct identifier call whose callee resolves, by the TypeScript checker's symbol identity, to the module's exported predicate of the same name with a single top-level declaration (v6, below). A call that is not the initialiser of a consumed binding is never read, so a dead call, a nested call and a call in an unread branch all fail to satisfy it. Buckets are partitioned by initialiser SHAPE, not by binding NAME, so a ladder that keeps a correct absent binding and reads an inline absentRows in the verdict is still caught.

Four mutations, one per bucket plus that rename variant. Measured at this sha, each applied alone: every one reds the rung cell that grades its own bucket's data flow. The absent pair additionally reds the three older text-anchored controls, which is the pre-existing anchor rot described above and not detection; it is called out here because a reader counting reds would otherwise credit it as such.

Two defects in the first version of this work, both caught by the instruments rather than by review, both disclosed because the shape generalises. First, the controls built their inputs by substituting a literal shipped line, so the mutants rotted the controls and a genuine detection arrived as 2 reds with only 1 real. Second, the replacement lifted the shipped registryVerdict statement out of the module BY AST, which still reads the file the mutants edit: the rename mutant rewrites that statement, the lifted text moved under it, and mutation-proof graded a real detection WRONG-RED. AST-lifting is not rot-proof, it is rot with more steps. The verdict is now synthetic, and the drift risk that creates is paid for by a cell that reads the same three shipped bucket lines through the skeleton and through the shipped module as resolved top-level export bindings and requires their sorted resolved-callee sets to agree, so a missing, inline or locally shadowed shipped callee diverges from the clean skeleton. The cell compares which exported declarations feed the verdict, not rung order, arguments or predicate semantics. That cell reds under a shipped-module drift, which is correct and is why two cells red on the acceptance patches rather than one.

Measured at e7ec3e6: the preflight suite 140 passed, 0 failed; the full mutation proof over the 53-entry fixture ALL 53 KILLED; mutation-fixtures.smoke.ts 437 files, 2,552 anchors, 0 dead, 0 ambiguous, 0 spanning prose; suite-ambient-env PASS. The acceptance patches against this tree, in a clean clone: the dead-call laundering that was green at 7d9f125 is 138 passed, 2 failed, and the drift without the dead call is 137 passed, 3 failed. Only the two smoke files change, so the docs-bundle gate is not triggered by this revision either.

Verified on a second machine at e7ec3e6 before the push: the preflight suite 140 passed, 0 failed; the 53-entry mutation proof ALL 53 KILLED; mutation-fixtures 437 files, 2,552 anchors, 0 dead, 0 ambiguous; suite-ambient-env PASS; ci-fragments and docs voice clean; shard-stability STABLE on the merged tree against live main 08cff35, which is an ancestor of this head. Not verified here: CI at this sha, which has not run yet; the docs-bundle gate is not triggered by this diff (two smoke files only).

Head note, v5 revision. This revision is 3bd71b6, one commit on top of e7ec3e6. The v4 figures above are that sha's.

A review of v4 found that the data-flow pin still classified each rung's callee by identifier text: a local predicate declared inside the ladder with the same name as the export satisfies "initialised by a call of isPresentRegistry" while the shipped rung calls the local copy. Measured at e7ec3e6, each of the three buckets shadowed alone stays 140 passed, 0 failed. A widened local isAbsentRegistry that also admits a value the export refuses stays green too, and is not an equivalent mutant: the behavioural cells never feed that value, so they cannot see it. That is why the repair is structural rather than another behavioural cell.

ladderConsumedBuckets resolved each consumed rung callee through the call site's lexical scope chain at this revision (superseded by v6, below: the scope walk is gone and resolution is by the checker's symbol identity); a same-name local recorded a null callee and the bucket's structural cell reds. The synthetic skeleton declares the same three exported predicates before its function so the agreement cell has a positive control on real exports. Three mutants are added in the same-name-local shape, one per bucket, the absent one widened; their bucket lines are byte-identical so no control anchor moves. The two sentences above that overclaimed the v4 reader are rewritten in place.

Measured at 3bd71b6: the suite 143 passed, 0 failed; the 56-entry proof ALL 56 KILLED, each new mutant on its own bucket's structural cell (141 marks against 143); the three acceptance patches (present shadow, dead-call drift, widened absent shadow) each 141 passed, 2 failed, the bucket's structural cell plus the agreement cell, with no behavioural cell added for the widened value; suite-ambient-env PASS. Verified on a second machine at 3bd71b6 before the push: suite 143/0; proof 56 KILLED, 0 survived, 0 wrong-red; the three acceptance patches 141/2 each on the same two cells; mutation-fixtures, suite-ambient-env, ci-fragments and docs voice clean; shard-stability STABLE on the merged tree against live main 08cff35. Not verified here: CI at this sha. The reader does not pin the callback argument (a rung reading row.direct instead of row.registry is caught by behavioural cells, not by this pin), and that is stated rather than claimed.

Head note, v6 revision. This revision is 1ae54cf, one commit on 3bd71b6. The v5 panel found two shapes the scope walker did not enumerate (a destructured parameter, and a var hoisted out of a sibling block), each leaving the suite green at 143/0 while the shipped export and the shadow diverged on a real input. v6 stops enumerating scope kinds. ladderConsumedBuckets builds a TypeScript program over the module, takes getSymbolAtLocation on each consumed rung call's expression, follows aliases with getAliasedSymbol, and accepts the callee only when that symbol is the module's exported symbol of the same name, whose sole declaration is the top-level one. Refusal is by symbol identity, so binding patterns, var hoisting, parameters and catch clauses are covered by the binder rather than by a list maintained here. What this proves is that the call binds to the export. What it does not prove: nothing about the argument the callback passes, nothing about the order of the rungs, and nothing about what the predicate means. Five reader controls are added and no behavioural cell: two hoisted-var placements (unknown and absent), the destructured-parameter placement, a positive control that the destructured-parameter skeleton really rebinds, and an export-clause module that must still resolve all three rungs. Measured at 1ae54cf: the suite 148 passed, 0 failed; the 59-entry proof ALL 59 KILLED, one new mutant per new placement, each red on its bucket's structural cell; all six acceptance patches (present shadow, dead-call drift, widened absent shadow, sibling-block var, destructured parameter, hoisted var on absent) each 146 passed, 2 failed, the bucket's structural cell plus the agreement cell, with the dts-freshness cell green in every run; the three new patches applied to the v5 reader are invisible to it (143/0 each); mutation-fixtures every anchor present and unique; suite-ambient-env PASS. Verified on a second machine at 1ae54cf: suite 148/0, the same six patches red on the same cells, proof ALL 59 KILLED; mutation-fixtures OK; suite-ambient-env PASS; ci-fragments 8 passed.

davidfarah2003 and others added 14 commits September 15, 2026 09:37
…sus cells

The publish preflight refuses when the exact-version census cannot be read, but
no fixture drove that state, so the guard was unverified and deleting it whole
left the suite green.

readExactVersion returns `unknown:<status>` for any response that is neither 200
nor 404, and `unknown:<message>` when the fetch throws. Both feed the inconclusive
rung of the verdict ladder. Every existing fixture answered 200 or 404, so neither
branch had ever executed under test.

This adds the fixtures that drive it: a registry that answers 503 for one package,
a registry that answers 302 for every package, and a transport that throws instead
of answering. Each reaches the inconclusive refusal, and the cells assert the census
names the unreadable package and carries its status or thrown message, and that the
refusal happens before any OIDC exchange, trust read, or write-shaped call.

Five mutations carry expectRed so the guard cannot be removed silently again:
deleting the block whole, treating an unreadable status as absent, swallowing a
thrown read as absent, reordering the rung below all-absent, and dropping the
census print that names the offending package.

The incomplete rung is a different case and is deliberately not deleted. With no
unknown rows, zero present rows forces absent === rows, which the earlier all-absent
rung already claims, so no composition of the current status domain reaches it. A
cell now pins that domain to present, absent and unknown, so if a later change adds
a fourth outcome the backstop becomes reachable and the cell reds rather than
leaving a deletion to look safe.

Refs #1584
…lable reorder

The mutation proof reported one survivor: moving the inconclusive rung below
all-absent left every cell green. That looked like a coverage gap and is not one.

A row counted in absent is never counted in unknown, so absent === rows already
implies unknown === 0 and the two orderings cannot disagree. Enumerating all 35
present, absent and unknown partitions gives 0 disagreements, and applying the
reorder to the module itself gives byte-identical verdicts across all 27 status
compositions. Both comparators were proved able to report a disagreement first,
against a planted ordering that genuinely differs.

So the mutation is equivalent, and no fixture can kill it. Writing a cell to chase
it would have produced a test that passes for the wrong reason. The mutation is
dropped and the premise it depended on is pinned instead: a cell now asserts each
census row lands in exactly one bucket, so if a later change lets a row count twice
the ordering stops being safe and a named cell reds.

Refs #1584
While testing whether the new domain cell actually catches the drift it claims to
guard, the drift run produced no verdict from that cell at all. The cell had not
passed, it had never executed: the manual token census calls the preflight without
a guard, so a refusal there aborts the file and every cell below it reports nothing.

Green by not running and green by passing are indistinguishable to a reader counting
failures, which is what made the first reading of that run wrong.

The exposure is pre-existing, and it is the same class the suite exists to catch. The
unguarded call maps 404 to absent and reaches ready, so it refuses only if the 404
mapping changes, which is precisely the drift the domain cell is there to detect. The
cell meant to catch that drift was among the cells the drift silenced.

The call now captures its own refusal and names it, so the cells below it still run.
Measured: under the out-of-domain drift the suite previously aborted after 7 reds with
no completion marker, and now completes with 23 reds including both domain cells by
name. On the committed behaviour the suite is 69 passed, 0 failed.

Refs #1584
The domain cell added earlier drives HTTP 200, 404 and 503 and records what
the census carried. That is an observation over the statuses it sends, so a
fourth outcome guarded by a status it never sends escapes it: a branch
returning a new value on 418 leaves the suite fully green. A review of this
branch demonstrated exactly that with a parse-checked mutant, which falsifies
the claim that the cell reds when a fourth readExactVersion outcome appears.

Add a second cell that reads the shipped source and enumerates every return in
readExactVersion, requiring each to be present, absent or an unknown value. A
new branch is a new return whatever status guards it, so this half cannot miss
an unenumerated outcome. The extractor returns null and reds when it stops
finding the function or its body, because an extractor that reports nothing
looks identical to a source with nothing wrong.

Rename the behavioural cell to say what it actually measures, and pin the
escape with a mutation so it stays closed rather than being closed once.

Refs #1584
…e line

Attacking the new structural cell rather than reasoning about it found a real
escape in it. Slicing the function body to the first occurrence of a newline,
closing brace, newline sequence is defeated by a template literal that contains
one: the slice ends early, and every return below that point, including an
out-of-domain one, falls outside the extracted body and is never examined. The
cell stayed green on a source that returns a fourth census value.

Walk brace depth from the opening brace instead, skipping string, template and
comment context so a brace inside any of them cannot close the body. Measured
against eight sources, each parse-checked at exit zero and each asserted
byte-different from the real file before scoring: the real source greens, and
all seven attacks red, including the template-literal truncation the previous
spelling missed and a renamed function, which reds by refusing to extract
rather than by passing.

Refs #1584
…pplies

The verdict ladder filtered rows with three inline expressions, so a test that
wanted to check bucket disjointness had no shipped definition to depend on and
had to transcribe them. A transcribed predicate grades the transcript: widening
the shipped absent bucket leaves the copy green, which is what made the
disjointness cell unkillable.

Name the three predicates, export them, and have the ladder call them, so the
membership rule a test imports is the one the release path actually applies.
… a regex

The domain cell claimed every return in readExactVersion lands in the census
domain, and rested that universal on /\\breturn\\s+([^;]+);/g over a brace-walked
body. A panel killed the instrument, not the claim: a bare `return;` carries no
expression, so the regex matched nothing and the cell stayed green while the
function gained an undefined return; and a semicolon-free return was swallowed
into the next capture, laundering an out-of-domain value into a domain-looking
blob. Both parse. Both change behaviour. Neither could red the cell.

Enumerate ReturnStatement nodes with the TypeScript compiler instead, excluding
nested functions, and accept a return only when the syntax pins its value: the
string literals present and absent, or a template whose head is a static
unknown: prefix. A bare return is out of domain by construction.

The cell now reports the count it found in its own name, so a changed return
count is visible in the transcript rather than implied. Grade the instrument
too: eight escape shapes the retired regex passed clean are asserted to be
refused, each parse-checked so it is a change a commit could really land, with
a positive control and a nested-function false-positive case so a parser that
refused everything could not pose as rigour.

Derive bucket membership from the shipped predicates. The old cell retyped them
inline and asked for exactly-one membership, which no string can violate, so the
overlap it documented was unreachable and widening the shipped absent bucket
left it green. It now applies CENSUS_BUCKETS, the predicates the ladder calls,
to production rows and to the enumerated return domain, and is renamed to what
it proves.
…shipped bucket predicates

Five mutations, each proved to bite: the find matches exactly once, applying it
changes the file, and applied alone the suite reds with the NAMED cell red.

Two are the escapes that killed the retired regex, now landed on the real
source: a bare return, and a semicolon-free return closed by ASI. Both parse
(node --check exits 0) and both previously left the cell green.

One widens the shipped absent bucket to overlap unknown. That is the overlap the
previous disjointness cell documented as its purpose and could not detect,
because it retyped the predicates instead of importing them.

Two grade the instrument itself, so it cannot be relaxed back into the defect it
replaced: dropping the bare-return refusal, and dropping the nested-function
boundary.

Entry 27 keeps its mutation and moves to the renamed domain cell. Anchors for
the two injected-return mutations span two lines so they stay distinct from
entries 0 and 23, which anchor on those same single lines.
…that calls them

Found by attacking the new bucket cells rather than by reasoning about them:
rewriting one CENSUS_BUCKETS entry to an equivalent inline arrow left all 105
cells green. The exported table agreed with the ladder today but was free to
diverge, which would have reintroduced the transcription defect one layer up:
the suite would grade a copy again, just a copy with an export keyword on it.

Two cells close it. One parses preflightNpmPublish and requires each bucket
filter to CALL an exported predicate by name, so exports the ladder stops using
cannot stay as decoration. The other requires each CENSUS_BUCKETS entry to be
the exported function by IDENTITY, not merely a predicate that agrees.

Both carry a mutation, and both were confirmed to red before being committed.
…claration

Both enumerators located their target with a recursive forEachChild that
assigned on every match, so the LAST declaration of that name anywhere in the
file won. A nested same-name declaration therefore replaced the real target
silently, and the printed return count did not move, so the transcript offered
a reader no signal.

Measured, not assumed. With the retired lookup restored:

  - a nested readExactVersion decoy with four in-domain returns hid a real
    function returning "outside-domain"
  - a nested preflightNpmPublish decoy calling all three predicates by name
    returned the ladder cell to green while the shipped ladder had already
    drifted off the exported absent predicate

Both now locate their target as exactly one top-level FunctionDeclaration taken
from parsed.statements. Nothing nested can be selected because nothing nested is
looked at. Zero and two or more are both refusals carrying the COUNT, because
"found 0" and "found 2" are different repairs.

The return-walk boundary was a hand-rolled six-way union that omitted
ConstructorDeclaration and ClassStaticBlockDeclaration, so a local class
constructor's return was attributed to the enclosing function and reddened a
clean source with five enumerated returns. It is now
ts.isFunctionLike(node) || ts.isClassStaticBlockDeclaration(node), pinned in one
place. ts.isFunctionLike covers constructors; a class static block is not
function-like to the compiler, so it is named separately.

Five cells, each verified to red against the exact retired implementation:
114 passed 0 failed at this head, 110/4 with the old lookup restored and
112/2 with the old boundary restored.

Disclosed: a return inside a class static block parses under TypeScript but node
rejects it as an illegal return statement, so that cell grades the boundary
predicate rather than a change a commit could land. It is kept for that reason
and says so where it stands.
…ary too

Found by attacking the pin I had just added, which is the only way to learn that
a fix is half a fix. Pinning the target to one top-level declaration stops a
SIBLING decoy from being SELECTED. It does nothing about a decoy declared INSIDE
the real function, whose `filter` calls the walk was still collecting into the
same list.

Measured at that intermediate state, with the top-level pin already in place:
the shipped ladder with the absent comparison inlined, plus a nested decoy
`preflightNpmPublish` calling isAbsentRegistry, produced callees
[isUnknownRegistry, isPresentRegistry, isAbsentRegistry] and the cell stayed
GREEN over a ladder that had already drifted.

Location and attribution are two claims and the pin makes only one of them. The
ladder walk now stops at the same boundary the return walk uses, and the matched
`filter` call does not recurse into its own arrow, because the callee name has
already been read and nothing below it is the ladder's code.

115 passed, 0 failed. The real ladder cell stays green, which is the control
that says the boundary did not simply stop finding everything.
… shadowing appends

Four mutations, preserving all 45 existing entries (49 total, 49 unique names).

One per rule:
  - the census top-level pin reverted to the retired last-match-wins lookup
  - the return-walk boundary reverted to the hand-rolled six-way union

One per enumerator that APPENDS a shadowing nested declaration, applied to the
shipped source rather than a synthetic one:
  - a nested readExactVersion decoy beside a poisoned real function
  - a nested preflightNpmPublish decoy beside a drifted unknown comparison

Bite evidence, measured per mutation, applied ALONE, with no report-all switch
in the environment (that variable does not exist in this repo at all; grep is
empty with a passing positive control, so "unset" is the only state available):

  [45] anchor 1x, changes file, suite rc=1, 110/5, names its cell
  [46] anchor 1x, changes file, suite rc=1, 113/2, names its cell
  [47] anchor 1x, changes file, suite rc=1, 114/1, names its cell
  [48] anchor 1x, changes file, suite rc=1, 114/1, names its cell

Every `find` is code-only; the fixture suite's prose gate reports 0 anchors
spanning prose over 2529 anchors.

Also re-anchored the existing nested-function mutation at index 42. Renaming the
boundary predicate rotted its anchor to 0 matches, and the obvious repair was
ambiguous: the single line `if (isReturnScopeBoundary(node)) return;` now occurs
TWICE, because the ladder walk was given the same guard. It is anchored on a
two-line code-only window that pins the census walk specifically. The nearest
comment would have been unique for free, which is the trap the fixture suite
exists to catch.
… real red as WRONG-RED

Found by running the full proof rather than trusting the previous run's summary.
Mutation 37 reddened exactly the cell it aims at and mutation-proof still graded
it WRONG-RED, because its expectRed quoted a cell name that no longer exists.

Not caused by this branch's changes. Measured at the base commit 23fc9d2a: the
expectRed string occurs 0 times in the suite file there, with a positive control
confirming the grep works. An earlier commit rewrote the enumerator to use the
compiler, reworded the cell, and left the tag on the retired wording.

The cell name is a template literal carrying the live return count, so the tag
now pins the invariant substring rather than the whole rendered line. A count
change cannot rot it again, which is what happened to the anchor at index 42 in
the previous commit for the analogous reason.
…ch one it is

Self-review caught a real error in the previous commit. The cell that pairs the
ladder decoy with a fixture drift described itself as applying "fixture entry
44", and entry 44 by name is the CENSUS_BUCKETS drift, not the ladder-inline
drift. It applies the ladder one. The comment named the wrong entry.

The distinction is not cosmetic, and the two are easy to confuse because they
inline the same absent comparison and sit adjacent in the fixture. They touch
different code. Measured: the bucket entry rewrites the module-level
CENSUS_BUCKETS table, which is OUTSIDE preflightNpmPublish, so the ladder
enumerator cannot see it at all. Applying it leaves that enumerator's callee list
byte-identical, so it could never have defeated the cell in question.

A unique anchor is not necessarily the right anchor, and a named entry is not
necessarily the right entry.

Four changes, each with a control run:

  - both drift entries are now read out of the fixture BY NAME and asserted to
    match the strings this file uses, so the cell grades the mutation the fixture
    actually ships. Pointing it at the bucket entry reds 1 cell.
  - the bucket entry is separately asserted to change the source while leaving
    the ladder enumerator's reading identical, which is the measurement that
    proves the two are not interchangeable.
  - the bucket drift IS graded, by a runtime identity comparison over imported
    function objects, which no textual decoy can launder. Stated where it stands
    rather than left implicit.
  - the by-name lookups must RESOLVE. Found by control: renaming one fixture
    entry silently DELETED three cells from the run and reported zero failures,
    because a missed lookup returns empty and the cells below pass on nothing. A
    rename is a legitimate later commit, so it now reds 2 cells instead of
    quietly shrinking the suite.

Also repaired a positive control that asserted its anchor occurs exactly once in
the live file. A decoy legitimately makes it occur twice, so the control failed
for the wrong reason under precisely the input it exists to grade.

119 passed, 0 failed. Fixtures 437 files, 2529 anchors, all zero, rc=0.
@davidfarah2003
davidfarah2003 force-pushed the fix/1584-publish-guard-cells branch from f160743 to 7d9f125 Compare September 15, 2026 07:42
v4 writer and others added 5 commits September 15, 2026 11:29
… by text presence

ladderFilterCallees asks whether the ladder's TEXT contains a call to each
exported predicate. A call inside `if (false)` satisfies that and launders a
live drift. Measured at 7d9f125, each bucket drifted alone with a dead call
retained:

  unknown  drifted + dead call  ->  119 passed, 0 failed   NOT CAUGHT
  present  drifted + dead call  ->  119 passed, 0 failed   NOT CAUGHT
  absent   drifted + dead call  ->  116 passed, 3 failed   NOT CAUGHT EITHER

The absent row is a trap: its three reds are the existing positive control and
the two decoy cells, all of which anchor on the exact absent line the drift
rewrote. They are anchor rot, not detection.

ladderConsumedBuckets reads which value registryVerdict actually consumes: the
sole top-level ladder, the sole registryVerdict declaration in its own statement
list, the identifiers that initialiser reads, resolved to declarations in that
same list, each required to be initialised by a call of the exported predicate.
A call that is not the initialiser of a consumed binding is never looked at, so
there is no guard to relax.

Four mutants, one per bucket plus the rename variant where the dead call is made
live and the verdict reads an inline copy beside a still-correct binding.

The controls are founded on a skeleton assembled from the fixture's own strings
and an AST-lifted verdict statement, never on a shipped line a mutant rewrites.
The first version anchored on shipped text and the present mutant produced 2
reds, only 1 of them detection.
…not AST-lifted

The rename mutant graded WRONG-RED: 5 reds in the transcript, only 1 of them
detection. The skeleton lifted the shipped registryVerdict statement out of the
module by AST, and that mutant rewrites exactly that statement, so the lifted
text changed under the mutant and the clean control stopped being clean.

AST-lifting is not rot-proof, it is rot with more steps: it still reads the file
the mutants edit. The verdict is now written out synthetically, which nothing
edits. The risk that a synthetic stand-in drifts from the shipped ladder is paid
for by a new cell that runs the same three shipped bucket lines through the
skeleton AND through the shipped module and requires both readings to agree.
…g, not the synthetic skeleton

mutation-proof graded this entry WRONG-RED: the suite went red, the detection
was real, and the expectRed named the wrong cell. It was tagged on the skeleton
cell that describes this same laundering shape, and that cell is synthetic by
design, so it does not move when the shipped module is mutated. A mutation of
the shipped file must be graded on a cell that reads the shipped file.

Re-tagged on the shipped absent rung cell, which is where it actually reds.
The rung pin asked which declaration each consumed bucket callee binds to, and
answered it with a scope walk written in the suite: enclosing source file, block,
function parameters, catch clause, first visible declaration wins. Every revision
of that walk was a better enumeration of the ways a name can be rebound, and each
one was defeated by a shape it did not enumerate. Two more were found at the
previous head, on a tree where all 143 cells were green:

  a same-name predicate in the ladder's destructured parameter list  143 passed, 0 failed
  `{ var isUnknownRegistry = ... }` in a sibling block, hoisted      143 passed, 0 failed

The first escaped because the walk filtered parameters on `ts.isIdentifier(name)`
and the ladder's only parameter is an object binding pattern, so every binding
inside it was invisible. The second escaped because `var` hoists to the function
scope while the walk read only the statements of the block it was handed. Both are
live: driving the real preflight over a 404/404/410 census under the second moves
the refusal from inconclusive to incomplete with every cell green.

So stop enumerating scope kinds and ask the binder. `ladderConsumedBuckets` now
builds a Program over the module, takes `getSymbolAtLocation` on the call's
expression, follows aliases with `getAliasedSymbol`, and accepts the callee only
when that symbol is the module's exported symbol of the same name, which must have
exactly one declaration and that declaration must be the top-level one. Refusal is
by symbol identity. Binding patterns, `var` hoisting, parameters and catch clauses
are already modelled, because it is the same binder the language uses.

Five controls are added on the skeleton: the two hoisted-var placements, the
destructured-parameter placement with its positive control, and an export-clause
module that must still resolve, which is what makes following aliases the right
comparison rather than a stricter one. Three mutants are added, one per new
placement, each aimed at its bucket's structural cell. The three rung cells are
renamed to state the symbol-identity claim they now make. No behavioural cell is
added for any of the widened values: a cell per input kills one mutant and leaves
every unenumerated sibling alive.
@davidfarah2003

Copy link
Copy Markdown
Contributor Author

Panel complete: three independent reviews of 1ae54cf2a001d0dcc61492adc07a489880f9aad1.

Relayed by the orchestrator. The measurements below belong to the reviewers.

All three reviews approve, so this is recorded as green. Each review reproduced the suite baseline at the head, red-ran all six sealed acceptance patches on the structural rung cell for their own bucket, and tried to defeat the symbol-identity resolution with placements of its own; none found an escape that rebinds the callee. Each also confirmed the three body corrections landed on the live body at this head.


Review 1: APPROVE

Blocking (0):
none

Body defects (0):
none

Non-blocking (0):
none

Not verified (5):

  • CI was not rerun and check state was not used because GitHub writes were forbidden and the provider cancellations are not evidence about this head.
  • I did not run a live npm publish or any registry write-shaped operation.
  • Repository-wide pnpm build, full typecheck, smoke:ci, and every unrelated suite were not run. I ran the focused 148-cell suite, all 59 configured mutations, all six acceptance patches, mutation-fixtures, suite-ambient-env, a strict focused TypeScript check, control mutations, and real-function harnesses.
  • The .internal submodule was not checked out in this lane, so mutation-fixtures reported its production-walk submodule observation as UNOBSERVED rather than passed. The target npm-publish-preflight fixture itself was observed with all 59 anchors present and unique.
  • I did not independently reproduce the writer's claimed second-machine run.

On defeating the resolution: I could not defeat the checker resolution with a legal same-name binding that kept the suite green.


Review 2: APPROVE

Blocking (0):
none

Body defects (1):

  • All three defects the writer drafted corrections for are ALREADY FIXED in the live body. Fetched read-only at 2026-09-15T14:09:19Z, at which time updated_at was 2026-09-15T13:35:31Z and head sha was 1ae54cf (matching the graded head). Line 425 now says the callee resolves 'by the TypeScript checker's symbol identity, to the module's exported predicate of the same name with a single [...]

Non-blocking (2):

  • Symbol identity pins WHICH function the rung calls, not WHAT that function computes. Correctly scoped by the body, and I could not turn it into a live escape. Evidence: Two shapes of mine keep the suite at 148/0 because they do not rebind the callee. (a) export let isAbsentRegistry plus reassign-and-restore around the bucketing line; (b) an indirect implementation, `export const isAbsentRegistry = (registry) => [...]
  • Three defensive branches in the new resolver survive mutation; none is a correctness hole on this tree. Evidence: Measured serially, each reverted immediately, each with a clean tree confirmed after revert: (1) dropping the alias-follow on the CALLEE side (const resolved = raw ?? null) is 148/0 green; (2) deleting the top-level-declaration requirement (declaration.parent.parent.parent !== parsed to false) is [...]

Not verified (8):

  • CI at this head. Every Linux job since 11:44Z was cancelled by the provider; a re-run is a GitHub write and is forbidden. Nothing in this verdict rests on check state.
  • pnpm smoke:npm-publish-preflight as a wrapper was not exercised directly. I ran the underlying command from package.json (./node_modules/.bin/tsx bin/smoke/npm-publish-preflight.smoke.ts). pnpm 11.1.2 IS present; I did not try the wrapper because it would touch node_modules and I would then have to re-verify lane resolution.
  • Repository-wide checks outside this suite were not run: no pnpm typecheck, no pnpm build, no smoke:ci, no docs-voice. Out of budget scope; I spent the budget on attacking the resolver as instructed.
  • No live npm registry was contacted. All real-module runs used an injected fetchImpl over an asserted census.
  • I did not reproduce the body's 'Verified on a second machine' claim. I have one lane and cannot verify another host's figures; I neither rely on nor dispute that sentence.
  • The reader still does not pin the filter callback's ARGUMENT (row.registry vs row.direct). Unchanged from v5, stated in the body, covered by behavioural cells rather than by this pin.
  • The skeleton/shipped agreement cell cannot backstop a resolver blind spot, since both sides are read by the same resolver. It is the second red on all six patches and is a consequence, not independent evidence. I confirmed this rather than counting it as a second detection.
  • Mutation proof shows the suite DEPENDS on the mutated code, not that a production entry point reaches it. I covered the reachability half separately by driving the real module.

On defeating the resolution: I could not defeat it. Every shape I built that actually rebinds the consumed callee was refused on the correct cell, including four shapes that are NOT in the packet. The shapes that stay green do not rebind the callee, and I could not make any of them change what the real module returns.


Review 3: APPROVE

Blocking (0):
none

Body defects (1):

  • Editorial / stylistic: In line 443 of the PR body (v6 head note), the sentence 'What it does not prove: nothing about the argument the callback passes, nothing about the order of the rungs, and nothing about what the predicate means' uses an informal double negative ('does not prove: nothing about...') that colloquially means 'proves nothing about...'. It does not misinform the reader about the scope of proof, but [...]

Non-blocking (2):

  • Compilation overhead: checkedModule constructs a ts.Program on each invocation of ladderConsumedBuckets. While called only ~10-15 times during the smoke suite, this adds ~50-100ms per run (~7.7s total suite duration). This overhead is negligible and entirely justified given the exactness of compiler-grade symbol resolution.
  • Agreement cell sensitivity: The agreement cell checks set equality of resolved callees between the synthetic skeleton and the shipped module. While it successfully catches drifts when the resolver operates, it compares resolved symbols rather than independently verifying the checker's resolution. The structural rung cells and symbol-identity controls directly inspect callee === null and calleeWhy, so the suite [...]

Not verified (4):

  • No GitHub writes: GitHub writes are strictly forbidden, so no GitHub comments, review submissions, labels, or remote CI re-runs were performed.
  • CI checks: CI Linux checks were cancelled by the provider since 11:44Z; check run state was not inspected or relied upon as a verdict on this head.
  • Live npm publish: The real publish workflow against the public npm registry was not run end-to-end.
  • Second machine verification: The PR body's claim of verification on a second machine at 1ae54cf was not independently corroborated (only one host/clone available).

On defeating the resolution: Extensive attempts to defeat the checker-based resolution were conducted against ladderConsumedBuckets. Every mechanism capable of shadowing isAbsentRegistry at runtime was tested, including: (1) destructured array parameters ([isAbsentRegistry]), (2) nested object destructuring parameters ({ extra: { isAbsentRegistry } }), (3) sibling-block var hoisting ({ var isAbsentRegistry = ... }), (4) backward var hoisting placed after the verdict ladder inside preflightNpmPublish, (5) var in switch [...]

@davidfarah2003
davidfarah2003 merged commit 531581b into main Sep 15, 2026
45 of 53 checks passed
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