Skip to content

Finish #11138: delete the retained grammar_coverage_string_eq clone by making the module's three implicit dependencies explicit - #11382

Closed
briansrls wants to merge 30 commits into
mainfrom
ssb-11138
Closed

briansrls wants to merge 30 commits into
mainfrom
ssb-11138

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session stern-seal-895.
Pushing to ssb-11138 advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

Brian Searls and others added 30 commits September 12, 2026 03:26
`fn string_eq(a: String, b: String) -> Bool { a == b }` was declared nine
times, byte-identical, across `v2.lens`: `reference_deps`,
`fact_cardinality`, `complexity_linearity_audit`, `effect_reach`,
`module_graph`, `manufactured_dependency_census`,
`production_qualification_origin_probe`, `enforcement.vocab` and
`live_read_classification`. That is §2 duplication in its plainest form:
one concept, nine homes, nine things to maintain and nine places a future
change to string equality could diverge.

The single home is `v2.std.text`, which declares `String` itself and
already carries the exact peer this function belongs beside --
`fn char_eq(a: Char, b: Char) -> Bool { a == b }`, used as the `eq`
argument to `list_starts_with` the same way `string_eq` is used as the
`eq` argument to `contains`. The new declaration sits directly after it.

Five of the nine modules already imported `v2.std.text { String }` and
simply name `string_eq` alongside it. Four had no `v2.std.text` import
and gain one. One of those, `v2.lens.fact_cardinality`, had no imports at
all -- it read `String`, `Int`, `FreeMonoid` and `Empty` entirely through
the shared name slot -- so this is its first import line.

This is independent of the bare-name-ambiguity qualification batches:
`string_eq` was never in that census's read population, because each
module's own copy won its own scope's registry inside the authored
region, which is ordinary shadowing rather than ambiguity. It is a
duplication finding the campaign surfaced, not a resolution defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The import was inserted INSIDE the `import v2.lens.module_graph {` block
rather than after it, which the parser refused:
`expected name, found LBrace`. CI caught it as
`module index refused: 1 unparseable .dag source(s)` naming the file and
position, which is the fail-closed behaviour working -- an unparseable
source refused the module index rather than being skipped.

Cause: the insertion anchor was the last line STARTING with `import`,
which for a multi-line import block is the block's opening line. The
other four files that gained an import in this change were checked and
are all at top level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The required floor on this branch refused with `FAILED PHASE
namespace-wave-admission (37 unadjudicated delta(s))`. The deltas are
real and they are the honest consequence of the change: moving one
function out of nine modules moves the binding at every site that calls
it.

All 37 are one shape, measured rather than inferred --

  TargetChanged binding <consumer>::<declaration> `string_eq`
  base {<the consuming module itself>} -> head {v2.std.text}

-- and the floor enumerated no delta of any other shape. The rows added
here are that list one-for-one, sharing a single label const so 37 rows
cannot drift apart in spelling.

WHAT MAKES IT SAFE TO ADMIT. The nine deleted bodies were BYTE-IDENTICAL
-- `a == b`, all with signature `(a: String, b: String) -> Bool` -- so
every call site denotes exactly the function it denoted at the base. A
body differing anywhere would have made this a semantic change wearing a
relocation's name, which is precisely what this adjudication exists to
rule out, so all nine were compared before the collapse rather than
assumed equal from the shared spelling.

ALSO PAID HERE: the three `gunbc#11071 LinuxKernelRelease rehome` rows,
which reported CONSUMED, are deleted along with their now-stale
description. A consumed row's deletion comes due on this roster's OWN
next touch, and this change is such a touch.

ON THE FLOOR RESULT ITSELF: the same run reported
`floor_class=infra signature=MemoryStallRefusedPageThrash`, which is
explicitly "not a verdict about the diff" -- the floor did not complete,
so this branch still has no clean floor verdict. The namespace phase ran
and its 37 deltas are a real finding independent of that; the floor
itself needs a re-run before this branch can claim a clean result.

ROSTER CONTENTION, stated so it is not a surprise: gunbc#11137 also
touches this roster and also pays the same three consumed-row deletions.
Whichever lands second will conflict here and should keep BOTH cohorts --
they admit unrelated deltas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The conflict is the one this branch's own commit predicted: #11137 and
this change both touch `NAMESPACE_TRANSITION_ADMISSIONS`, and both were
authored believing they owed the three `gunbc#11071` consumed-row
deletions.

RESOLUTION: KEEP BOTH COHORTS. They adjudicate unrelated deltas -- one
`TargetChanged` on `extdeps.tools.sha256sum`'s
`extdeps_external_authority_anchor` from #11137, and 37 on `string_eq`
from this change. Dropping either would leave its delta unadjudicated and
refuse the phase.

WHAT THE MERGE CORRECTED RATHER THAN CARRIED. This branch claimed the
THIRTY-FIFTH dissolution and the deletion of the three consumed rows.
#11137 reached the roster first and paid that debt, so by the time this
merges there is no debt left to pay and the claim would be false. The
note is renumbered THIRTY-SIXTH and says plainly that it dissolves
nothing: a ledger recording one deletion twice is worse than one
recording it once, and this file's whole value is that its history can be
read back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The floor on this head is CLEAN -- `verdict=FloorClean`,
`claims_failed=0` -- and the namespace phase reports `0 unadjudicated
delta(s)`, so all 37 `string_eq` rows matched their deltas one-for-one.
What blocked it was the other column: `1 stale admission(s)`.

The stale row is `gunbc#11137 extdeps.tools.sha256sum names Filesystem
instead of reaching it`. #11137 merged, so the narrowed import is at the
base, the delta it admitted stopped being producible, and a row matching
no delta blocks the phase. Its own recorded trigger was "this row goes
when #11137 merges", and this change is the roster touch on which that
came due -- so it is deleted here rather than left to refuse the next
roster-touching PR.

That is the mechanism working exactly as designed, and it is worth
noticing that the row predicted its own retirement in the commit that
introduced it. The ledger's value is that this is checkable rather than
remembered.

ALSO RETRACTED: this change was authored believing it owed the three
`gunbc#11071` consumed-row deletions. #11137 reached the roster first and
paid them, so there was no debt left. The original text claimed the
deletion; the claim is retracted rather than carried, because a ledger
recording one deletion twice is worse than one recording it once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Main landed #11165, which replaced `AdmissionSubject::Binding`'s
`target: &str` with `expected_candidates: &'static [&'static str]` -- the
EXACT candidate set after the admitted transition, checked at the head
before admission and at the base to derive consumption.

The 37 `string_eq` rows are rewritten to it. Each names
`&["v2.std.text"]`, which is the whole post-transition set for these
sites, not merely one member of it: after the collapse exactly one module
declares `string_eq`, so the singleton IS the set and the stricter check
is satisfiable rather than merely tolerated.

That schema change is a strictly better instrument for what these rows
claim. The old `target` field was documentation -- `admission_subject_matches`
ignored it, matching on module, declaration and spelling alone -- so a row
could name any target and still match. The new field is checked, which
means a row whose transition does not land exactly where it says now
fails instead of passing quietly. I would not have caught a wrong
`target` in the old shape; I would now.

ALSO IN THIS MERGE: main has already retired the `gunbc#11137` row and
recorded the retirement, so nothing is re-deleted here. That deletion was
owed once and four branches reached it independently; main is where it
landed, and this branch simply adopts that history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Review 64521, three findings, all verified.

THE LEDGER OVERSTATED THE COLLAPSE. It said "the single declaration now
lives in `v2.std.text`". Nine homes survive this change, and the sentence
read as if none did. Measured rather than recalled, with the command in
the text so it can be re-derived: six exact `fn string_eq` declarations
plus three RENAMED byte-identical variants -- `floor_join_string_eq`,
`string_eq_native_routing`, `fn_index_string_eq`.

The renamed three matter more than their count. They are §3's NICKNAME --
a second name for one concept -- and they are the form `grep string_eq`
does not find, which is why they are named in the ledger rather than left
to the next reader's search. I had corrected this count in a PR comment
earlier; a PR comment is not readable from the code, which is the whole
reason the correction belongs here.

The residual is now a DECLARED FRONTIER (§3c) with a stated reason for
the scope and a trigger to close it, rather than silence: each further
consumer produces its own `TargetChanged` delta needing a row, and the
`dag/` files would be the first `dag/` modules importing `v2.std.text`
for this name -- a different reach question that deserves its own
evidence.

TWO PIECES OF MERGE RESIDUE, both mine:

  A stale `TRIGGER: this row goes when #11137 merges` was sitting inside
  the `gunbc#11138` cohort header, two lines after the text saying #11137
  had already landed. §4b(3) makes a row's trigger the whole check -- a
  cohort header carrying an already-satisfied trigger is how the wrong
  rows get retired on the next roster touch. Deleted.

  The `gunbc#11071` retraction paragraph appeared twice, near-verbatim,
  joined by a mid-sentence seam from the conflict resolution. One
  statement survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Review 64530 is right, and the way it is wrong is the point: my
enumeration was short by six, and four of the missing ones are in
`v2.lens` -- the scope this change claims to have collapsed.

WHAT I DID WRONG, mechanically. I filed a list under a command that does
not produce it. The list was assembled with `^fn string_eq(` -- exact
spelling only -- and then filed under
`^fn [a-z_]*string_eq[a-z_]*(a: String, b: String)`, which is broader and
returns 16 declarations, not the 9 I wrote. The header asserted the list
was "measured with" a command I had not run to build it.

WHAT THE CORRECT COMMAND SHOWS. Four byte-identical `a == b` copies
survive INSIDE `v2.lens`, under nicknamed spellings:
`grammar_coverage_string_eq`, `lens_module_gate_string_eq`,
`agreement_string_eq`, `impact_string_eq`. So this change collapsed the
copies spelled exactly `string_eq` and left the ones spelled otherwise.
That is §3's NICKNAME surviving precisely because a name-shaped search
does not find it -- and the header had claimed it went out of its way to
name nicknames "rather than left to the next reader's search".

THE FIX IS TO STOP ENUMERATING. §6 says name the instrument, never
transcribe its output, and this is why: a list in this file is a
transcription that rots, and the first one was wrong on the day it was
written. The header now names the command and states the rule for reading
its hits, and the frontier trigger is "the command returns exactly ONE
declaration" -- which adjudicates itself against the tree rather than
against a list this file keeps.

That also repairs a §4b(1) inflation the review names: a trigger
adjudicated against a short list is SATISFIABLE WHILE THE CONCEPT IS
STILL FORKED. The four `v2.lens` survivors would have been invisible to
it.

The scope sentence is corrected too. "Scoped to `v2.lens`" was false --
this change does not clear `v2.lens`, and now says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Review 64537, three findings, all merge residue from patching this ledger
incrementally instead of cutting it once.

WHAT WAS WRONG. The `gunbc#11138` header carried #11137's adjudication --
the sha256sum candidate narrowing and the `String` -> `std.string_type`
zero-delta story -- attributing another change's `TargetChanged`
rationale to this cohort. §3 names that: a meaning fork gives one name
two materially different meanings, and a roster header is exactly where
that is load-bearing. The preamble was also present twice, which is §2
redundancy in a file whose value is that its history reads back.

AND IT CONTRADICTED ITSELF ABOUT THE CENSUS. Twenty lines after the
corrected frontier -- survivors remain, closes when the named `grep`
returns one declaration -- the header still asserted "THE POPULATION IS
COMPLETE AND MEASURED" via `grep -c '^fn string_eq' goes 9 -> 0`, and
that after merge the base authors `string_eq` "only in `v2.std.text`".
That re-inflated the narrow census the previous commit had just corrected
and would have been read as the stronger claim, because it is stated
later and more confidently. §4b(1): do not cite the strongest path while
another stays silent.

The block is now cut once rather than patched again: main's own #11137
retirement record is left untouched above, and this cohort's header
carries only what this change does -- the relocation, its classification,
the byte-identical adjudication, the instrument-named survivor frontier
with the four `v2.lens` nicknames it does not reach, and one trigger.

CHECKED THAT THE CUT ONLY ADDS: 439 insertions, 1 deletion, and ZERO of
main's doc lines removed. That check exists because the last conflict
resolution on this campaign silently deleted a real adjudication receipt
while adding a false one; verifying the subtraction side is now part of
touching this file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…ng a region

Same resolution shape as the sibling batch, for the same reason: take
main's file whole and add this change's block to it, so nothing of main's
is re-derived and nothing of main's can be lost.

WHY NOT PATCH THE CONFLICT REGION. Twice on this campaign a
region-replacing resolution silently deleted `THE gunbc#10671 ROWS
DISSOLVED HERE` -- an adjudication carrying the three-direction join that
ESTABLISHED consumption for four rows rather than asserting it -- because
region replacement swallows text neither side was in conflict about. Git
reports no conflict for that text, so nothing flags the loss.

VERIFIED THE SUBTRACTION SIDE, which is the check those two losses
produced: `git diff origin/main` on this file deletes ZERO of main's doc
lines, and the 37 `STRING_EQ_COLLAPSE_LABEL` rows are all present.

TWO EXTRACTION BUGS WORTH NAMING, both from re-deriving structure instead
of reading it. Slicing the old roster at `s.index('&[') + 3` cut the `T`
off `TransitionAdmission`, because rustfmt had collapsed the const onto
one line -- the same collapse that broke an earlier resolution on this
campaign. And a doc-block anchor that did not match returned an EMPTY
capture that a line count would have caught and a success check did not.
Both were found by counting what came out, not by reading what went in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…ng a region

The only conflict is `namespace_wave_admission.rs`, the fleet's admission
roster, which every lane with a transition to admit edits. Resolved by
the method that cannot lose text: take main's file whole, re-add only
this branch's own block (the `STRING_EQ_COLLAPSE_LABEL` doc, the const,
and its 37 rows), then verify `git diff origin/main` deletes ZERO of
main's lines. It deletes none -- checked, not assumed, because the two
earlier resolutions on this file that replaced the conflict REGION
instead of taking a SIDE silently dropped the `gunbc#10671` adjudication
receipt, outside the markers where git reports nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…s branch's rows

#11156 landed, so main's roster is now 2 rows rather than 182. This
branch carried the pre-deletion roster plus its own 37, which is the
conflict.

Resolved by the rule: take main's file whole, re-add only this branch's
own block (the `STRING_EQ_COLLAPSE_LABEL` doc, the const, and its 37
rows), then verify. `git diff origin/main` on this file deletes ZERO of
main's lines, and the array is now 39 rows -- main's two survivors plus
these 37.

WHAT THIS SHOULD DEMONSTRATE, and it is worth reading the wave phase line
rather than only the verdict: this branch's own content did not change at
all in this commit, so if `namespace-wave-admission` goes green here, the
181 consumed rows were the WHOLE of its red -- which is the cleanest
confirmation available that the debt was the roster's and not this
change's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…ed not swept

The floor refused `namespace-wave-admission` with 0 unadjudicated deltas,
0 stale admissions and 2 CONSUMED admissions due. Both are the rows
gunbc#11156 authored, consumed by its own merge.

ADJUDICATED AGAINST THEIR OWN TRIGGER, which is the distinction the side
chat's correction insisted on: the rows are not retained because a count
says 2, and not deleted because a wall is red. Their block authored
`TRIGGER: these rows go when #11156 merges. The base then carries the
named imports, the deltas stop being producible, and CONSUMED comes due on
the roster's next touch.` #11156 merged as `d7b7ab96c1f`, checked by
identity before this was written, and the floor independently reported
exactly those two as `already satisfied at the base`. Trigger, merge and
floor report agree.

This lane pays because they are THIS author's rows. The alternative --
another lane deleting admissions it did not author -- is how an unexamined
deletion gets made on someone else's judgement.

Their describing paragraphs go with them, per precedent. Audited: `git
diff origin/main` on this file deletes 24 doc lines and all 24 are that
description.

A RECEIPT THIS RUN ALSO PROVIDES, recorded in the entry because it answers
a question rather than restating one: this branch's own content did not
change between the run that reported 181 consumed rows and the run that
reported these 2. Only the base moved. So the 181 were the whole of this
branch's earlier red -- measured, not assumed.

Recorded as the THIRTY-EIGHTH DISSOLUTION.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…e the fourth

Review 65155 is right and the finding is inside this PR's own stated
scope: four byte-identical `a == b` clones survived in `v2.lens` under
module-prefixed nicknames, and the prefix is exactly what hides them from
a `fn string_eq` grep while this PR claims to have collapsed that scope.
DESIGN §3 names that as nicknaming, and §3's attractor argument is the
reason it matters: while a clone stands, nearby questions get answered in
its vocabulary.

THREE ARE FOLDED IN, mechanically identical to the nine already done:
`lens_module_gate_string_eq`, `agreement_string_eq` and
`impact_string_eq` are deleted, their call sites read `string_eq`, and
each file imports it from `v2.std.text`. All three compile at 0 blocking
errors.

THE FOURTH IS MEASURED, NOT DEFENDED, and the reason is recorded beside
the declaration rather than in this message.
`v2.lens.enforcement.grammar_coverage` DECLARES NO IMPORTS AT ALL -- every
name it uses reaches its declaration through the shared name slot. Adding
the one import line the collapse needs turns on the listed-import
requirement for the whole file and those names stop resolving: 6 blocking
errors before, 10 after, the four new ones being `dedupe_snoc`,
`tokenize` and `parse_module` unresolved plus a downstream effect-summary
refusal. So the honest collapse of that clone is the qualification of the
module's whole surface, which is a different change with a different blast
radius and belongs to the bare-name qualification campaign. Doing it here
would hide a resolution-regime change inside a DRY cleanup.

Its annotation carries the measurement, the reason, and a trigger naming
the capability that retires it (the module naming its own imports), with
the same grep instrument the rest of the cohort was measured with.

Admission rows for the three new transitions are NOT authored here: the
floor enumerates the deltas, and rows are written against what it reports
rather than against what I predict it will report.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…tore a doc block I had dropped

#11240 landed, so main's roster is empty and the two consumed gunbc#11156
rows are gone from the base. As agreed with bright-boar-435 and
eager-raven-113, exactly one receipt may exist for that consumption
event: #11240 keeps it, this PR sheds it. Both this branch's copy of the
deletion and its THIRTY-EIGHTH entry are gone; the array now carries only
this change's own 37 string_eq rows.

TWO THINGS FOUND WHILE DOING IT, both mine.

FIRST, I HAD SILENTLY DELETED MY OWN DOC BLOCK. The 62-line description
of the string_eq cohort -- what the 37 rows admit, why the rest is not in
this change, the trigger -- was dropped by commit 619465f, the one that
paid the consumed-admission debt: its slice ran from the gunbc#11156 doc
to the array and swallowed the string_eq block sitting between them. It is
restored here from 21150a8.

WHY MY AUDIT DID NOT CATCH IT, which is the part worth keeping. That
commit's check was `git diff origin/main` deletes zero of main's doc
lines, and it passed honestly -- the string_eq doc is THIS BRANCH's
addition, so it was never in main and a diff against main is structurally
incapable of reporting it as lost. The instrument was blind to exactly
the content it was most likely to lose: my own. A deletion audit has to
compare against the tree the deletion was made from, not against the tree
it will land on.

SECOND, A DUPLICATE ORDINAL, also mine. Main carries TWO entries numbered
THIRTY-SEVENTH: #11156's (the 181 rows) and #11240's (the two rows). I
numbered #11240's off a base that predated #11156's landing. Renumbered
#11240's to THIRTY-EIGHTH -- the number this branch's shed entry vacated
-- because an ordinal that repeats defeats the only thing an ordinal is
for, and a later citation of "the thirty-seventh" would be ambiguous.

`git diff origin/main` on this file now deletes exactly two lines: that
ordinal, and the empty array line reopened to hold the 37 rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
refs/pull/11138/head has served 5dfe70e for ~17 hours while
refs/heads/session/witty-moth-510-string-eq has been 819e4f0 since
08:51Z. Every reviewer fetches the pull ref, gets the stale sha, and
correctly refuses: "worktree freshness check failed ... refusing to
review a stale/wrong checkout". Four failed reviews across three
providers and both initiation paths, and #11138 is the only stale pull
ref among 106 open PRs -- bright-boar-435's control on #11310 shows an
equivalent PR's pull ref tracking its head exactly, so the reviewer
machinery is sound and the ref is what is wrong.

This commit is empty on purpose: it changes no content and exists only to
give GitHub a ref update that may unstick the pull ref. Safe here
specifically because this PR carries ZERO approvals, so moving the head
invalidates no review state; it would not be safe on a PR carrying one.

Close/reopen is the other common remedy and is deliberately NOT used:
it can trigger fleet automation nobody has verified on a PR that is
blocked rather than broken.

If the pull ref does not follow, that is the finding, and it escalates as
a GitHub-side stuck ref rather than anything this branch can fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
… restore the ordinal fix

Same conflict and same method as the previous three merges of this file:
take main's version whole, re-add only this branch's own block (the
`STRING_EQ_COLLAPSE_LABEL` doc, the const, and its 37 rows). Main now
carries 7 rows of its own, so the array is 44.

AND THE DUAL AUDIT CAUGHT SOMETHING THE MAIN-ONLY AUDIT COULD NOT. Diffing
against main showed ZERO deletions, which is the check I have been running
all day. Diffing against MY OWN HEAD showed three doc lines gone -- and
that is the audit whose absence cost me the `string_eq` doc block on this
same branch this morning. Two of the three were a real loss:

  main still carries TWO entries numbered THIRTY-SEVENTH (2026-09-12 and
  2026-09-13), the duplicate ordinal I created by numbering gunbc#11240's
  entry off a base that predated gunbc#11156's landing. This branch had
  renumbered the second to THIRTY-EIGHTH; taking main's file whole
  reverted that. Re-applied.

The third difference is NOT a loss and is worth saying so rather than
"restoring" it: main's version of the 181-row sentence is a past-tense
edit of mine -- "the array WAS empty of inherited rows after THAT
deletion" -- which is correct on main, where the deletion is history and
the array is no longer this change's own two. My branch's present-tense
phrasing was correct only while #11138 carried that deletion, and it no
longer does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
… the roll call

Review 65866 found the cohort header enumerating four v2.lens survivors and
stating "declared NINE times" / "the nine copies this change deletes". Both
were stale at this head, which folds three of the four and deletes twelve --
and the header diagnoses that exact failure mode two paragraphs earlier.

DESIGN.md section 6: name the instrument, never transcribe its output. The
enumeration and the counts are both transcriptions of a grep this file already
names. They are replaced by the grep itself: the survivor paragraph states the
shape of what the command shows and points at each survivor's own retention
annotation, and the deletion delta is read from the diff rather than counted
here. grammar_coverage's retention drops "the twelve others" for the same
reason; its own trigger and instrument are unchanged.

No admission row, disposition, or expected_candidates entry is touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…ibed counts

Review 65887: the retention annotation for the surviving clone still said
"measured at 6 blocking errors before and 10 after" with no producer named
that re-derives them -- the same section 6 defect the previous commit removed
from the roster header, two files away and locally unapplied.

The counts are replaced by the shape that actually carries the argument: the
import turns on the listed-import requirement and the compile gains exactly
dedupe_snoc, tokenize and parse_module unresolved, plus the downstream
effect-summary refusal each causes. The names are the reason the collapse is
not a one-line delete; the totals never were. Re-derivation is the same act the
trigger closes on, so the annotation points at that rather than at a number.

Annotation only. No declaration, trigger or admission row is touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The required floor on this branch failed namespace-wave-admission with seven
consumed admissions due for deletion -- the gunbc#11182 inventory evidence
relocation rows, each reported as "already satisfied at the base, consumed by
its own merge".

The thirty-eighth dissolution explicitly RETAINED these seven, on the identity
check "product.inventory carries no InventoryLotEvidence on main". That check
now answers the other way: origin/main declares type InventoryLotEvidence and
fn admit_ledger_evidence in product.inventory. The relocation is at base, so
there is no delta left for these rows to admit.

The rows go and their block comment goes with them; the retention paragraph is
deleted rather than corrected in place, because prose explaining why deleted
rows were kept is the stale citation section 3 forbids. The thirty-eighth's
forward-looking sentence about what remains below is re-tensed to what it left
behind, since it is now a historical statement rather than a description of the
array.

These are not this branch's rows. The deletion is owed on landing or on the
roster's own next touch, this branch is that touch and is already blocked by
them, so paying here is what stops the same wall standing in front of the next
unrelated lane.

No executed verdict changes: with the transition present at the base there is
no delta for these to admit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Review 65902 found the retention rationale contradicted by another file in
this same diff: v2.lens.fact_cardinality had zero imports at base, gained
`import v2.std.text { string_eq }` as its only import, and kept resolving its
bare cross-module names -- exactly what the annotation said could not happen.
Both statements could not be true.

Measured rather than argued. A seed built from this tree compiles this module
at 6 blocking errors; applying the collapse (import added, clone deleted, both
call sites rewritten) gives 10, and the four new refusals are dedupe_snoc,
tokenize and parse_module as "has no established callee identity", plus the
downstream join. The retention is justified; the STATED MECHANISM was not.

The real rule is the import closure, not the file. An import moves resolution
into the closure that import opens; names outside it stop resolving. v2.std.text
imports v2.std.algebra, so fact_cardinality's contains and list_snoc_item are
inside the closure its one import opened. Nothing on the path from v2.std.text
reaches v2.lens.coverage or v2.compiler, so these three are not. Same act,
opposite outcome, and the discriminator is where each name is declared relative
to the closure.

fact_cardinality is now cited in the annotation as the in-diff control for that
distinction, which is what the earlier phrasing lacked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
…0-string-eq

# Conflicts:
#	src/v1/stage0/src/namespace_wave_admission.rs
…0-string-eq

# Conflicts:
#	src/v1/stage0/src/namespace_wave_admission.rs
The floor on this branch failed namespace-wave-admission with one consumed
admission: gunbc#11193 artifact_store_fs anchor leaf. gunbc#11347 landed the
source change and the admission row together, so once it reached main the
transition was present at the base -- origin/main's
dag/extdeps/realization/artifact_store_fs.dag line 3 carries
`import extdeps.filesystem.filesystem_io { Filesystem }`, checked by identity.

The row and its block go; the dissolution entry records the deletion.

This branch preserved that row through the previous merge and deletes it at
this one, and both were right: at the first merge it was live at that base and
the conflict rule requires carrying another lane's row forward; at this one it
is consumed and the debt falls on the roster's next touch.

No executed verdict changes: with the import at the base there is no delta left
for the row to admit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The collapse is scoped to v2.lens. Seven declarations survive outside it
and one survives inside it under a different name, and none of them was
declared anywhere -- so a reader met "nine copies collapse to one
authority" with no way to learn what remained.

The annotation names the census as a COMMAND rather than a list or a
count, because either would be stale on the next merge and would then be
cited as coverage (DESIGN section 6: name the instrument, never
transcribe its output). It also states the exception the command cannot
find: v2.lens.enforcement.grammar_coverage retains a byte-identical
clone under the name grammar_coverage_string_eq, declared with its own
reason at its own site, which no grep for '^fn string_eq' will match.

The trigger is per fork rather than for the set -- a fork collapses when
a change touches its module, or earlier if that module's import closure
already reaches v2.std.text -- because no single event retires the whole
residue. The owner is the DESIGN section 3 single-authority reviewer, a
standing criterion rather than a person.

Annotation only; no semantic change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtQbXXBvrT2onJjxKswVYf
Review 66106, both findings, and both were mine.

ONE INSTRUMENT. The v2.std.text annotation named the narrow pattern
'^fn string_eq'; the roster header named the broader
'^fn [a-z_]*string_eq[a-z_]*('. Two spellings of one census is the
second authority DESIGN section 3 forbids, and the narrow one
UNDER-REPORTS -- it misses every renamed fork, which is the whole thing
this residue is about. Both sites now name the broader command, and the
annotation says it is the same one the roster names so neither drifts.

NOT EVERY SURVIVOR IS DECLARED. The roster header claimed each survivor
states its own retention at its declaration, and the annotation called
grammar_coverage_string_eq THE renamed exception. Both false: the
broader command finds several renamed forks, and grammar_coverage is the
only one carrying a stated retention. Every other hit is undeclared
residue -- no reason, no retention, nothing at its declaration. Both
sites now say that plainly.

The annotation also corrects its own earlier wording in place rather
than silently replacing it, because a reader who met the narrow command
should learn it under-reported rather than assume the census changed.

Annotation and doc-comment only; no semantic change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtQbXXBvrT2onJjxKswVYf
…rows

Conflict was the whole NAMESPACE_TRANSITION_ADMISSIONS block. Main
emptied the roster in #11356; this branch fills it with the 37 string_eq
rows. Neither side could be taken whole, so both were audited.

TAKEN FROM MAIN: the file entire, including its #11356 prose and its
correction that the #11182 relocation rows are gone and the claim they
survived was already stale. This branch's older paragraph asserting
those seven "still admit a real delta" is dropped -- main's identity
check answers the other way and gunbc#11274 retired them.

NOT TAKEN FROM MAIN: the closing sentence "THE ROSTER IS EMPTY AGAIN,
which is its resting state". True on main, FALSE here, because this
branch refills the array. Rewritten to keep main's correction while
saying plainly that what follows is a different population with its
reason stated at its own site.

RESTORED FROM THIS BRANCH after the audit found it dropped: the
THIRTY-NINTH DISSOLUTION entry naming the gunbc#11193 anchor row and the
identity check that admitted its deletion. Main retired the same row in
#11356 but its prose says only that two rows went, not which. The entry
is the only place in the file that names it, so it is kept with a note
that main landed the same retirement -- one retirement, recorded once,
by the side that wrote down what it was.

RE-AUDITED BY IDENTITY against the new base: none of the nine v2.lens
modules carries the transition yet, so all 37 rows still admit a real
delta and none is consumed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtQbXXBvrT2onJjxKswVYf
Review 66122. The command was spelled with -E in v2.std.text and
without it in the roster header -- directly below the sentence saying
two spellings would be a second authority. Both are now byte-identical.

Checked before aligning rather than assuming it was cosmetic: the two
spellings return the SAME 13 declarations here, because `(` is literal
in BRE and escaped in ERE. So this was a second spelling rather than a
second census. Standardised on the -E form because it does not depend
on that BRE/ERE distinction holding for the next person who edits the
pattern.

Doc-comment only; no semantic change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NtQbXXBvrT2onJjxKswVYf
…ng_eq clone

`grammar_coverage_string_eq` was retained in this PR as a frozen-X exception on a
measured obstacle: `v2.lens.enforcement.grammar_coverage` declared no imports, so
adding the one import the collapse needs moved it into its own import closure and
stranded three previously implicit dependencies -- `dedupe_snoc`, `tokenize` and
`parse_module`.

THE OBSTACLE WAS REAL AND THE CARVE-OUT WAS STILL WRONG. DESIGN section 3 reserves
frozen-X for where NO Y can hold the boundary, and says "atomic" describes the
AUTHORITY TRANSITION rather than the amount of implementation work. The trigger was
the tell: "this clone goes when grammar_coverage names its own imports" names source
work this change can express today -- it waits on no compiler capability, no cycle
being removed, no other authority landing. A trigger naming work you could do now is
a task, not a dissolution condition, and the surviving X is an attractor while it
stands.

So the module now names what it already used. There are no cycles to break, and each
direction was confirmed rather than assumed: `v2.lens.coverage` (dedupe_snoc) imports
only `v2.std.algebra`; `v2.compiler.tokenize` and `v2.compiler.parse` carry ordinary
explicit compiler/std import surfaces; and nothing in the corpus imports
`v2.lens.enforcement.grammar_coverage` at all, so no module can depend back on it.
With the surface named, `string_eq` comes from `v2.std.text` like every other consumer
in the cohort and the clone is deleted.

TWO ANNOTATIONS BECAME FALSE ON DELETION AND ARE CORRECTED, not left to rot. Both
`v2.std.text`'s authority note and the `namespace_wave_admission` seed header stated
that exactly one survivor carried a stated retention and named this one. The census
command they both cite now returns eleven forks besides the authority and NONE of
them is declared, which is what each note now says.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4cA9129DjUpri3sZGKGkX
…o false claims

THE STRANDED SET IS FOUR, NOT THREE, AND THE FOURTH IS NOT A NAME. The closure
analysis that measured this module's implicit dependencies named `dedupe_snoc`,
`tokenize` and `parse_module` -- three value names read off a resolution failure.
With exactly those three imported, four of six witnesses still FAILED with
`filesystem_read requires Filesystem.Read in the import closure`.

THE INSTRUMENT COULD NOT SEE IT, which is the part worth keeping. A name-resolution
census reports the names that failed to resolve; an EFFECT CAPABILITY rides the same
import closure and is not a name, so that census would report three no matter how many
capabilities were missing. This is not a miscount to be corrected by counting more
carefully -- it is the wrong instrument for the question, and anyone repeating that
census on another zero-import module will get the same wrong answer in the same way.
The right instrument is execution: a typecheck passes all six witnesses, and only
running them distinguishes the two states. DESIGN section 5 -- a typecheck is not a
consumer.

So `extdeps.filesystem.filesystem_io { Filesystem }` is imported here for the same
reason `v2.lens.vacuity` and `v2.lens.identity_captured_navigation.roster_gate` import
it, and the witnesses that read the live tree pass by execution rather than by
typecheck.

TWO FALSE CLAIMS ARE REPAIRED, both of them prose this branch already carried.

FIRST, `v2.std.text` opened "The nine byte-identical copies that lived across v2.lens
collapse here". False on this branch, which folds more than the original nine. The
repair is COUNT-FREE rather than a corrected integer: a few lines below, the same
annotation states that a number written there would rot and that none appears, so
substituting a new integer would make the annotation contradict itself while staying
true for about a week. DESIGN section 6 -- name the instrument, never transcribe its
output -- and the census command in the roster header IS the instrument.

SECOND, the thirty-ninth dissolution's rationale said main's prose "states that two
rows went and does not say which", which conflated two different retirements. Checked
against origin/main rather than restated: main's surviving two-rows prose is the
THIRTY-SEVENTH dissolution, the `gunbc#11156` pair discharged by #11156 merging, and
`11193` has ZERO occurrences in main's copy of this file because gunbc#11356 removed
the single #11193 row AND the block describing it. So main did not fail to say which
row went; it recorded that retirement nowhere at all. The entry is kept for the reason
it was always kept -- it preserves the identity-grounded receipt main intentionally
dropped with the row -- and only the rationale changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4cA9129DjUpri3sZGKGkX
@briansrls
briansrls marked this pull request as ready for review September 14, 2026 16:24
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-14T16:42:05.857733Z 6110c63 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@gunbai-bot

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Duplicate: this is the same head (6110c63) as #11138, opened automatically at 16:24 when stern-seal-895's worktree was flushed during closeout. #11138 is the one under sign-off and bright-eagle-728 enqueues it; #11364 was the same duplication earlier and is already closed. Closing so only one of the three lands. The APPROVE on this PR (review 66284) reads on identical bytes and carries over to #11138. — sent from bright-boar-435

@gunbai-bot gunbai-bot Bot closed this Sep 14, 2026
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