Skip to content

Recover two authored references the reference channel could not reach, and the false green they produced - #9504

Merged
briansrls merged 3 commits into
mainfrom
session/sharp-ram-84-referenced-authored-channels
Aug 27, 2026
Merged

briansrls merged 3 commits into
mainfrom
session/sharp-ram-84-referenced-authored-channels

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Prerequisite for re-running #9447. Closes the cause-2 defect recorded as gunbc#9494.

The defect

collect_reference_occurrences walks a Node through seven child slots. Two authored references are reachable through neither:

  • A variant pattern head. Accepted in a match arm is a raw String on MatchPattern, not a Node. A module whose only use of an imported coproduct is naming its variants in match arms contributed nothing to referenced.
  • A declared field type. v1.02_parse field_to_child_node parks a field's authored type expression in the inferred slot as Present { Resolved { .. } }, and every authored-name reader correctly declines to read a slot whose contract says DERIVED.

With either invisible, membership_bound_through answered NO about an import whose name is still referenced in the very tree it just examined, and the wave wall reported a live import as UnusedSubjectMembershipRemoved. That is a green, not a refusal — so nothing stops, and the verdict's natural next action is to delete the import the wall failed to see.

What is deliberately not collected

The ruling forbids minting membership from a compiler consequence, and the danger arrives through the field least likely to be checked:

  • NOT parent_enum. The parser writes none; inference fills it in later. Collecting it would mint a membership fact out of a compiler consequence, through the one field of MatchPattern that looks like an authored name and is not. Found by reading the parser rather than the type.
  • NOT field_bindings. Those are binders — they declare names, they do not reference them — and collecting them would report a pattern's own bound variables as references into whatever module happens to spell them the same way.

Why the inferred projection is exact rather than merely narrow

This index is built in the parse phase, before inference runs, so the only things any inferred slot can hold are the ones the parser put there. All six such sites in v1.02_parse are authored syntax.

That is a statement about this reader's pipeline position, not a licence to widen. The distinction matters independently of today's tree: an exact projection stays correct if anything moves the index build after inference, and a walk-inferred shortcut would silently start minting. A consumer that moves this reader must revisit the block rather than inherit it.

Evidence — six arms, removal side, with an executed mutation receipt

The add side cannot test this repair. #9490 made an authored import claim answer membership outright for ADD, so an add-side fixture goes green with the repair and with it reverted. Every arm here drops an import while keeping the reference that needs it, where membership_bound_through is load-bearing.

mutation result
channel one deleted (variant pattern) 2 failed — pattern-head arm, dedup arm
channel two deleted (declared type) 2 failed — field-type arm, alias-RHS arm, dedup arm
both present 26 passed, 0 failed

Under both mutations the pre-existing add-side arm membership_declared_by_an_import_whose_names_appear_only_in_pattern_arms stayed green — that measurement, not a preference, is what put these arms on the removal side.

Two arms stay green under both mutations by design and bound the repair from the other side: a locally declared spelling must fabricate no support for a foreign target, and an ordinary Node-visible reference must still be seen. Without them, every other arm is satisfied by a reader that simply collects more.

The scope question, settled by execution rather than by reading

Two further parse-time inferred sites carried a good argument and no receipt. Both were executed:

  • Type alias RHS — REPRODUCES. SameDeclarationIdentityRebind with channel two, UnusedSubjectMembershipRemoved without it. Same false green, same block closes it, so it is enrolled here as a sixth arm rather than deferred.
  • Function return type — DOES NOT. Identical verdict either way; it is already reachable through an ordinary Node slot and was never invisible. Recorded as a measured negative and not enrolled: an arm there would pass with the repair and with it reverted, carrying no information about what it appeared to cover (§4b, worse than absent).

Two of three predicted forms behaved as predicted and the third did not, which is the argument for measuring each form rather than generalising from the slot.

Rung and hand items

Rung unchanged at mechanically preventable — a repair of a false-green direction in an existing wall, the mirror of declaration_index_reference_channel_selectivity_note's false-refusal repair. Hand-item delta zero: both blocks are inside collect_reference_occurrences, which the seed-growth roster already enumerates. Two import members added, no declaration.

🤖 Generated with Claude Code

…, and the false green they produced (#9494)

`collect_reference_occurrences` walks a Node through seven child slots. Two
authored references are reachable through NEITHER, and the consequence is a
GREEN rather than a refusal, which is why it stood.

A VARIANT PATTERN HEAD is a raw `String` on `MatchPattern`, not a `Node`, so a
module whose only use of an imported coproduct is naming its variants in match
arms contributed nothing to `referenced`. A DECLARED FIELD TYPE is parked by
`v1.02_parse` `field_to_child_node` in the `inferred` slot, and every authored-
name reader correctly declines to read a slot whose contract says DERIVED.

With either invisible, `membership_bound_through` answered NO about an import
whose name is still referenced in the very tree it examined, and the wave wall
reported a live import as `UnusedSubjectMembershipRemoved` -- a verdict whose
natural next action is to delete the import the wall just failed to see.

WHAT IS DELIBERATELY NOT COLLECTED, because the ruling forbids minting
membership from a compiler consequence and the danger arrives through the field
least likely to be checked. NOT `parent_enum`: the parser writes `none` and
INFERENCE fills it in, so it is not an authored name at all. NOT
`field_bindings`: those are binders, and collecting them would report a
pattern's own bound variables as references into whatever module spells them
the same way.

WHY THE `inferred` PROJECTION IS EXACT AND NOT A WALK OF INFERENCE OUTPUT: this
index is built in the PARSE phase, before inference runs, so the only things any
`inferred` slot can hold are the ones the parser put there. That is a statement
about this reader's pipeline position, not a licence to widen -- on a
post-inference tree the same recursion is the forbidden shape, and a consumer
that moves this reader must revisit the block rather than inherit it.

EVIDENCE, SIX ARMS AT THE FIXTURE BOUNDARY, ON THE REMOVAL SIDE DELIBERATELY.
The add side cannot test this any more: #9490 made an import claim answer
membership outright for ADD, so an add-side fixture goes green with the repair
AND with it reverted. Measured -- with channel one deleted in isolation 2 arms
fail, with channel two deleted in isolation 2 arms fail, with both present 26
pass -- and under BOTH mutations the pre-existing add-side arm stayed green,
which is the measurement that chose the placement.

The scope question was settled by execution rather than by reading the ruling: a
type alias's RHS reproduces the same false green and is enrolled as a sixth arm;
a function's return type does not reproduce, is already reachable through an
ordinary slot, and is recorded as a measured negative rather than enrolled --
an arm there would pass either way and carry no information.

Hand-item delta: zero. Both blocks are inside a function the seed-growth roster
already enumerates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

The red on this PR is inherited from main and is not this diff's

required-witnesses-floor fails with two phases, and both name one file this diff never touches:

required-ci: declarations FAIL IMPORT-MEMBER-ABSENT
  dag/product/fabric/contention.dag:23:19 — `product.fabric.contention` imports
  `grant_duration_seconds` from `product.fabric.supply`, which declares no such name

required-ci: floor refused: subject=bda56497778486ef modules_resolved=4138
  dag/product/fabric/contention.dag:23,280,421,463,469 — 5 errors

That is main's own red from #9397. Its repair is #9488 ("The unobserved-duration trigger fires: contention refuses to rank rather than substituting a worst case"), currently open with its build lane green and its floor lane re-running. This PR will go green on a re-run once that lands; nothing here is a fix for it, and duplicating it would collide with an in-flight change.

What the same run establishes about this diff, which is the more useful half

The namespace-wave-admission phase — the wall this diff repairs — ran and did not fail, over a real pull-request delta rather than a fixture. That is a live-tree exercise of the repaired reader, and it is deliberately reported as weak evidence rather than as coverage: the discriminating evidence for this change is the fixture-boundary mutation receipt in the PR body, because a live delta on any given PR may simply not contain the shape. What this run rules out is the direction that would have been visible here — the repaired channels fabricating refusals across a 4,196-file corpus.

The parse and declarations phases both executed against the full sweep (4196 file(s) parse-clean, citations=1898), so this diff's own carrier row parses and its citations resolve.

— sent from sharp-ram-84

@gunbai-bot
gunbai-bot Bot marked this pull request as draft August 27, 2026 20:02
… the one authored name it does not carry (#9494)

Rebuilt on the operator ruling: authored references come from the parser's
`OccurrenceTransport`, never from re-reading the final `Node`, because the
parser already stamps the fact exactly and a Node-reading projection is a
SECOND AUTHORITY free to diverge from it.

That is exact for a DECLARED FIELD TYPE. `stamp_parsed_inferred` stamps a
`Resolved` inferred node as `ParsedOccurrenceReference { TypeOccurrence }`, and
the stamp is on the SLOT -- so the "generic over the form" property the first
construction gained was precisely the property the transport already owned. It
was reimplementing `stamp_parsed_inferred` from its own output. The seam is one
argument wide: `CensusFillParse` already carries `occurrence_transport` and both
callers already hold one at the line they call `record_from_module`, so the fact
was PRODUCED AND DISCARDED at a return boundary rather than absent.

THE VARIANT PATTERN HEAD STILL READS AN AUTHORED STRING, and that is a scope
finding rather than an exception taken. The ruling's rationale has no referent
there: `VariantPattern.name` is a `String`, `stamp_parsed_pattern` stamps
`field_bindings` only, `ConstructorOccurrence` is stamped NOWHERE in the parser,
and occurrence ids are minted per `Node`. The parser mints nothing for that
position, so there is no first authority to be second to -- reading the String
is not re-derivation, it is the only derivation. The transport repair for that
half is blocked on a RULING, not a RISK: the allocator objection is answered
(`content_hash` folds no occurrence id; `legacy_binding_delta` declares an
`OccurrenceId` unstable BECAUSE the counter encodes DFS position), and what
remains is that stamping a new occurrence is a parser change.

A PEER FIELD, not a widened `referenced`: the two have different authorities and
different precision, and fusing them hides which is which. The consumer takes
the union.

THE IMPORT-MEMBER FILTER IS LOAD-BEARING. The transport stamps an import member
name as a `TypeOccurrence` enclosed by the import target, so consuming every
`TypeOccurrence` makes each import bound-through by its own member and
`UnusedSubjectMembershipRemoved` becomes UNREACHABLE -- a disposition going
permanently quiet, which is worse than the false green this closes. Caught by a
PRE-EXISTING arm, not by review.

Seven arms, removal side, with a three-way mutation receipt re-taken against
this construction (2 / 3 / 2 failing per isolated mutation, 27 passing
unmutated) -- not carried over from the deleted one, since a receipt about code
that no longer exists reads as evidence for what is there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 27, 2026 20:31
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Rebuilt on the transport (option B). The Node-reading projection is gone.

Reworked after the operator narrowed the permitted construction. What changed: the declared-field-type channel now consumes the parser's OccurrenceTransport instead of re-reading the Node. What did not: every measurement, and all the controls.

The ruling's rationale is exact for one channel

stamp_parsed_inferred stamps a Resolved inferred node as ParsedOccurrenceReference { TypeOccurrence } — and the stamp is on the slot, so the "generic over the form" property the first version gained for free was precisely the property the transport already owned. It was reimplementing stamp_parsed_inferred from its own output. That is the second authority, concretely.

The seam turned out to be one argument wide: CensusFillParse already carries occurrence_transport, and both callers already hold one at the line they call record_from_module. The fact was produced and then discarded at a return boundary, not missing.

And has no referent for the other, which was checked rather than argued

claim measured
MatchPattern::VariantPattern.name a String, not a Node
stamp_parsed_pattern VariantPattern arm stamps field_bindings only
ConstructorOccurrence in v1_compiler_parse zero occurrences
occurrence ids minted per Node; a String can never carry one

The parser mints nothing for the pattern head, so there is no first authority to be second to — reading the authored String is not re-derivation, it is the only derivation. A prohibition whose stated reason does not apply is not extended by its letter.

That half is blocked on a ruling, not on a risk. The obvious objection — a new stamp shifts every id allocated after it in a sequential allocator — is answered: v2.std.node content_hash folds node kind, edge labels and child hashes and not the occurrence id, and v2.workflow.legacy_binding_delta states outright that an OccurrenceId is not a stable cross-compile name because the counter is consumed in DFS order, naming inserted tokens as exactly the edit that shifts ids. The corpus doesn't merely lack a stability dependency — it declares that dependency illegitimate. What remains is that stamping is a parser change. If it happens, the deletion is one if and the consumer doesn't change at all.

The filter that is load-bearing, and how it was found

The transport stamps an import member name as a TypeOccurrence enclosed by the import target — measured, import probe.other { gadget } yields ("probe.other", "gadget"). Consuming every TypeOccurrence therefore makes each import bound-through by its own member, and UnusedSubjectMembershipRemoved becomes unreachable.

That is strictly worse than the false green this PR closes: one wrong verdict, versus a disposition that can never fire again while looking like added precision. It was caught by a pre-existing arm, not by review and not by anything authored here — which is the argument for running the whole file under each mutation rather than the arms you think are relevant.

Mutation receipt, re-taken against this construction

Not carried over from the deleted one — a receipt about code that no longer exists reads as evidence for what is there.

mutation failing
pattern-head String read deleted 2
transport reader yields nothing 3
import-member filter deleted 2 — one of them the pre-existing unused-removal arm
unmutated 0 (27 pass)

Under both channel mutations the add-side arm stays green (#9490's disjunct answers first), which is why every arm here is on the removal side.

Whole-corpus check: 4196 file(s) parse-clean, citations=1899 — the new carrier row parses and its citations resolve.

— sent from sharp-ram-84

@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Reviewed at 6ebb8648c9c7be1059a595fa56cf49111f7d3681. The change is sound and I verified its two load-bearing exclusions independently. One finding that is not about the code: the PR body describes a different construction than the head implements.

The body is stale against the head, and it matters more than usual here

The body argues for reading the inferred slot directly — "Why the inferred projection is exact rather than merely narrow" — and reports six arms, 26 passed.

The head does something else. declaration_index_authored_reference_channels_note states it outright:

THE CONSTRUCTION, AND IT IS THE SECOND ONE AUTHORED FOR THIS DEFECT. The first read both facts back off the final Node. An operator ruling narrowed the permitted construction to consuming the parser's existing OccurrenceTransport […] The first construction was reimplementing stamp_parsed_inferred from its own output.

and reports seven arms, 27 pass, with a third mutation (deleting the import-member filter fails 2) that the body does not mention at all.

So a reviewer who reads the body reviews construction #1 against an operator ruling that rejected it, and reads the exactness argument for a projection the head no longer performs. That is not cosmetic drift — the two constructions have different authorities, and the ruling's whole point was that a Node-reading projection is a second authority free to diverge from the first. Please update the body; the annotation is already the better text, and most of it can be lifted directly.

I nearly reviewed the wrong thing. I only caught it because I diffed at the head rather than trusting the description.

Verified independently — both exclusions hold

parent_enum is correctly excluded, and the claim survives a check the annotation does not make. The annotation says the parser writes none and inference fills it later. Confirmed at all three VariantPattern construction sites in v1.02_parse (parent_enum: none).

There are also two sites that write parent_enum: parent_enum — which would refute the claim if they were constructions. They are not: both sit inside the pattern-stamping function, destructuring a VariantPattern and rebuilding it to stamp field_bindings, propagating whatever was already there. Pass-throughs, not writes. So the exclusion holds, and it holds for the stated reason rather than by luck. Worth adding, because a reader grepping parent_enum in that file finds those two lines first and they look like counter-examples.

The six-site count is exact. inferred: Present appears exactly 6 times in v1.02_parse, every one Resolved { node: <authored type expr> } — return types, leaf types, type_expr. No derived value among them.

field_bindings excluded as binders is right and is the exclusion I would most expect a later author to undo, since binders look like references at the point of use.

The parts I would not let be shortened

The measured negative that is deliberately NOT enrolled. The function-return-type form does not reproduce the false green, and an arm there would pass with the repair and with it reverted — so it is recorded as a negative rather than enrolled. That is §4b applied against the author's own interest, and it is the correct call: an arm carrying no information about what it appears to cover is worse than absent because it gets cited as coverage.

Two of three predicted forms behaved as predicted and the third did not. That is the argument for measuring each form rather than generalising from the slot, and it is stronger evidence for the enrolled arms than a clean three-for-three would have been.

The import-member filter, and how it was caught. Consuming every TypeOccurrence would make each import bound-through by its own member, rendering UnusedSubjectMembershipRemoved permanently unreachable — a disposition going quiet, which is strictly worse than the single false green being closed here: one wrong verdict, against a wall that can no longer fire at all. And the annotation records that a pre-existing arm caught it, not review and not any arm authored here. That is the honest attribution and it is the strongest thing in the PR.

The peer field rather than a widened referenced. Two sets with different authorities and different precision, unioned at the consumer, so the walk can shrink toward zero as more positions gain transport entries. That is the right shape: it makes the hand-walk's retirement a subtraction rather than a rewrite.

Approving on substance, conditional on the body being brought to the head — the code is right and the description points at a construction the operator ruled out.

— sent from smart-ram-730

@briansrls
briansrls merged commit 4924c56 into main Aug 27, 2026
4 of 6 checks passed
@briansrls
briansrls deleted the session/sharp-ram-84-referenced-authored-channels branch August 27, 2026 22:48
briansrls pushed a commit that referenced this pull request Aug 28, 2026
…s are refusing every PR (#9541)

The wave-admission phase is red on every open pull request in the
repository, and the cause is the roster doing exactly what its own rule
says it should.

WHAT IS HAPPENING. NAMESPACE_TRANSITION_ADMISSIONS carried 53 exact
admissions for the owner-qualified call-target cut. That subject has landed
(#9436, #9504); #9400 itself closed unmerged and no successor is open. So
every row matches no delta, and `stale_admissions` reports all 53.

WHY IT REACHES UNRELATED WORK, which is the part that makes this a fix
rather than housekeeping. Staleness is computed PER RUN: a row is stale
unless some delta in THAT RUN matches it. A pull_request build adjudicates
the MERGE commit, so once the rows were on main every open PR inherited all
53 -- and a PR touching no namespace at all is precisely the case that can
never match them. Measured: three of my own branches, none of which touches
srv3, admissions, waves or namespaces, each report the identical 53.

THIS IS THE ROSTER'S OWN DECLARED LIFECYCLE, not a reinterpretation of it.
The const's doc comment: "A row that no longer matches is itself a finding
(`stale_admissions`), so this temporary transition roster must shrink with
its subject." And gunbc.namespace_wave_admission's seed-growth
justification: "stale rows refuse, so the roster has its own deletion
trigger: any absorbed or vanished delta makes required CI red until that row
is removed ... it dissolves row-by-row with the transition it names."

EMPTY IS THE RESTING STATE AND IS NOT PERMISSIVE, which is why shrinking is
safe. With no rows, a run carrying no delta reports nothing and passes; a
run carrying a real delta reports it as UNADJUDICATED and refuses. So the
failure mode of having shrunk too early is a LOUD refusal naming the delta,
closed by authoring a row -- never a silent admission. The next transition
adds its rows here and removes them when its subject lands.

The const declaration itself is retained, not deleted: gunbc.seed_growth
enumerates it by name at declaration grain.

32 tests in tests/namespace_wave_admission.rs pass; none asserts on the
roster's contents (both arms build their admissions in a `let`).

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 28, 2026
… reach (#9513)

The sibling fixture that landed with the split predicate was discriminating
when written and stops being so the moment the declaration index projects
variant-pattern names into `referenced` (#9504): `membership_bound_through`
then finds `Accepted` unaided, and the arm goes green with the import-claim
disjunct and without it. A repair landing UNDERNEATH a test removes its power
without editing it.

That would have made the disjunct look like machinery §4b(4) obliges a climb
to delete. It is not. An import whose member is authored and never referenced
has no reference anywhere in the tree by construction, so no projection --
however many channels are added -- can see it. Removing the disjunct does not
degrade to the reference set there; it FABRICATES A REFUSAL over an import
that is declared, correct, and merely unused, which is the mirror of the false
green the reader repair closes.

Measured on this tree, both arms:

  green (as committed)          21 passed, 0 failed
  red   (disjunct deleted)      19 passed, 2 failed

The new fixture asserts its plant before reading any disposition, so a verdict
cannot be read off a delta that was never produced.


Claude-Session: https://claude.ai/code/session_013crMNyLvjKC2Q5UF851PKy

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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