Skip to content

Refuse an ambiguous bare-name read in the v1 evaluator: the wall, its fixture-authored RED, and the variant-arm tier - #11166

Merged
gunbai-bot[bot] merged 18 commits into
mainfrom
session/witty-moth-510-wall2
Sep 14, 2026
Merged

gunbai-bot[bot] merged 18 commits into
mainfrom
session/witty-moth-510-wall2

Conversation

@briansrls

@briansrls briansrls commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Makes an ambiguous bare-name read a refusal in the v1 evaluator, rather than something the shared name slot silently settles.

The refusal

claim_scope_for now returns a typed AmbiguousBareNameRead refusal when a bare value reference reaches the shared name slot with two transitively-reached declarations spelling it and nothing the author wrote ranking them. Previously scope precedence picked one — a resolution no source authorizes.

It carries its remedy, naming declaring modules. A site authored next week reds someone who never heard of this campaign, in a module they may not own, over a name they did not know was contested. So each row prints the bare name, the referring module, each claimant with the module that declares it and the kind, and a pasteable import <declaring module> { <name> } — plus a warning that an import naming a re-exporter binds nothing. That warning is measured, not hypothetical: copying an adjacent file's import line is how import std.types { String } came to appear in ~1951 modules while binding nothing.

The evidence: a fixture that authors the ambiguity

dag/test/claim/bare_name_ambiguity_wall_witness_test.dag builds a synthetic multi-module source set in which shared_thing is genuinely declared twice and read bare — so the RED is authored rather than waited for. Eight rows:

  • subject — a genuine bare value read refuses, pinned to the AmbiguousBareNameRead cause rather than satisfied by any refusal
  • two negative controls — a record field label and a function's own parameter sharing the name must not refuse; these separate the value-position projection from the raw free-reference set, which unions field labels
  • two arm rows — a singly-declared variant arm, and an arm of the site's own coproduct, must not refuse
  • two-coproduct row — asserts the VariantCollision the compile raises, because no scope is ever built there; a control asserting a scope refusal it never observes would be satisfied by a cause it never saw
  • two remedy rows — the refusal names a declaring module and carries a pasteable import line

Two geometry facts were measured, not assumed, and are recorded at the rows: the reader must be a containment descendant of one claimant (otherwise v1 refuses the compile with AmbiguousReference and no scope exists — the class this wall covers is the one the compile silently settles by ancestry), and each claimant needs a uniquely-named puller, or only one claimant enters the scope and the arm passes for an unrelated reason.

The discriminating RED has fired

Beyond the authored fixture, the guard itself was shown to be the cause: the seed was built twice with only !ambiguous_reads.is_empty() disabled between them. The two refusal rows flip with it and back; the field-label control holds in both directions. Flipping one way proves a red exists — flipping back, with a neighbour held fixed, proves the guard causes it. The witness names that instrument and states the predicate it decides rather than transcribing readings.

The variant-arm tier

fn_entries walks top-level items only, so an arm of type TestClass = Unit | Hermetic | Integration was invisible to both resolution tiers — five corpus sites read as defects while being correctly written. The tier derives arm owners by walking every top-level coproduct and resolves a site from its own module or its authored import binding, keyed on (module, coproduct): two visible coproducts declaring one arm stay ambiguous, which is the fail-closed direction.

It does not read item.inferred — that branch was removed after the fixture caught it registering every coproduct-returning function as a declarer of its return type's arms. An item's inferred node is its type, not its declaration.

What the climb deletes

The [floor-bare-name-ambiguity-read] census lines, the ambiguous_bare_reads field that fed them, and the memo-equivalence assert over it. Once the wall returns Err before a scope exists, all three could only ever report zero — §4b's "permanently green by construction … worse than absent because it will be cited as coverage." §4b(4) dissolves the production machinery and keeps the evidence: the eight fixture rows stay enrolled. The declaration-grain and qualified-read censuses survive, because they are populated on scopes the wall accepts.

resolution_fingerprint now includes the new tier, so the memo-equivalence witness covers it.

Corpus state

Zero ambiguous bare-name reads remain for the wall to refuse: the sixteen Filesystem/Clock sites were qualified by #11156, and the five variant-arm sites are resolved by the tier rather than excused by a list.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT

Brian Searls and others added 8 commits September 12, 2026 03:31
NOT READY TO PUSH. The detector is complete and the refusal is written;
what is missing is executable controls.

THE THIRD TIER. `PreparedScopeIndexes::site_resolved_fn` answered over
`fn_nodes`, which is built by walking `module.items` -- top-level
declarations only. A variant ARM is not a top-level item, so a bare read
of one matched neither tier and was reported as falling through to the
shared slot even when the arm's coproduct is declared in the reading
module itself or imported by name. Measured: five such sites in the
corpus census.

`variant_arm_owners` indexes arm name -> (declaring module, COPRODUCT).
Keyed on the coproduct and not merely the module, because the question is
"does EXACTLY ONE coproduct visible to this site declare this arm" -- a
module-only key cannot tell one coproduct from two in the same module and
would resolve a genuinely ambiguous arm read to whichever matched first,
silencing the class the refusal exists to catch. The tier is deliberately
stricter than the two above it: those let the site's own module win
unconditionally, which they can because a top-level declaration in one's
own module is the authored answer by construction, whereas arms carry no
such precedence rule.

Not a new error class: an instance of
`gunbc.recurring_failure_mode.binding_chosen_by_pool_membership_rather_than_by_the_declared_rule`,
whose root sentence is this same asymmetry -- a coproduct arm is
pool-visible corpus-wide while not being a local binding under its own
spelling.

THE REFUSAL sits in `claim_scope_for`, keyed on `ambiguous_reads` -- the
same binding `required_floor_runner` prints its census lines from, so
detector and count cannot drift.

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

The bare-name-ambiguity refusal lives in `claim_scope_for`. Nothing that
CI executes could reach it: `tools.multi_module_compile_fixture` stops at
`compile_to_resolved` and never builds a scope, a corpus module authored
to carry an ambiguous read would refuse the whole floor rather than sit
in it as a probe, and `src/v1/stage0/tests/` is compiled by clippy and
run by nobody (`gunbc.rung_drop` `rust_unit_tests_off_the_merge_path`).
A wall landed on those routes would have had a positive control that runs
and a discriminating RED that does not -- DESIGN §4b's permanently-green-
by-construction, which is worse than absent because it gets cited as
coverage.

§4b also names the remedy: a state unrepresentable in the ACCEPTED corpus
may still be representable as source handed to the compiler by a FIXTURE,
and a compiler is precisely a thing whose regression probes are invalid
programs.

So the fixture instrument gains a second question. `claim_scope_fixture`
takes the same authored manifest and an entry MODULE PATH, and reports
what the scope builder said: `ClaimScopeInstrumentRefused`,
`ClaimScopeCompileRefused`, `ClaimScopeRefused`, `ClaimScopeAccepted`.

ONE DETECTOR, NOT TWO. The host function runs
`claim_scope_for_without_memos` itself. It does not re-derive the
ambiguity population, so the refusal a control observes IS the refusal
the corpus floor raises, out of the same `ambiguous_reads` vector the
census lines are printed from. A second computation here -- even a
faithful one -- would be the second authority the census exists to
prevent, and would be free to drift from it silently. `without_memos` is
the only difference from the floor's own call, and it is a caching
decision rather than a semantic one: the memoized entry point caches
fragments on a process-shared index keyed for the real corpus, which a
fixture must neither read nor write.

NO CORPUS ROOT IS READ. `PreparedRepository` is built entirely from the
manifest -- the graph from `compile_to_resolved`, the indices from
`dag_graph_source_indices`, empty `full_inventory` and
`discovery_exclusions` because a fixture has no discovery step. The
subject stays fully under the calling test's control.

Both readers are written as MATCHES over the outcome, never as
accessors, so `ClaimScopeInstrumentRefused` and `ClaimScopeCompileRefused`
read false on BOTH polarities: a ragged manifest fails the positive and
the negative control together, which is the signature of a broken probe,
rather than silently satisfying whichever one was phrased as an absence.

The builtin is declared where builtins are declared, not bolted on: a
dispatch arm in `gunbc.v1.v1_interpreter_primitive_surface`, a nested-
compile reach row in `gunbc.v1.v1_interpreter_opaque_host_call`, and a
signature in `src/v1/04_method.dag`. The three Rust mirrors of those are
regenerated artifacts and are updated to match.

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

`claim_scope_for_without_memos` is behind
`cfg(any(test, feature = "interp_test_witness"))`, so a release build has
no such function and this instrument has to run in one. The call is now
`claim_scope_for_with_memos(&prepared, entry, None,
build_scope_order_index(&prepared))` -- the same function the floor's own
entry point calls, with the memos withheld, so the single-detector
property is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
`data <name>: String =` with the literal on the FOLLOWING line does not
parse: `expected expression, found Newline`. The three "source annotation
names no subject" errors reported alongside it were downstream of the
same break -- once a declaration fails to parse, the annotation block
above it names nothing -- rather than three separate defects.

Caught twice, by CI's parse phase on this branch and by running the five
controls directly; both name the same file and position.

Worth recording because of how it could have read: the remote dispatch
that ran the controls EXITED 0 while all five failed to parse, since the
build's exit status was consumed by a pipeline. A report taken from the
exit code would have announced five working controls that never ran.

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

The required floor on this branch refused with the wall's own words --
`16 ambiguous bare-name read(s) refused` for the scope
`v2.test.execution.emit_on_demand_match_loop_fold_family_witness`. That
is the refusal WORKING, not failing: the corpus still carries 82 measured
ambiguous reads because the qualification batches have not merged, so a
wall landed now refuses the tree it is meant to protect. Weakening it to
make the lane green would invert the whole point, so the wall comes out
of this branch and lands on top of the qualifications instead.

WHAT STAYS, and it is the part that has to exist before the wall can be
proven at all:

- THE THIRD RESOLUTION TIER. `fn_nodes` is built from `module.items`, so
  it holds top-level declarations only and no variant ARM is reachable
  through either existing tier. `variant_arm_owners` indexes arm name ->
  (declaring module, COPRODUCT) and resolves only when EXACTLY ONE
  visible coproduct declares the arm; two visible coproducts stay
  ambiguous, because nothing the author wrote ranks them. Keyed on the
  coproduct rather than the module so one coproduct and two in the same
  module are distinguishable -- a module-only key would resolve a
  genuinely ambiguous arm read to whichever matched first.

- THE FIXTURE CLAIM SCOPE. `claim_scope_fixture` builds a real claim
  scope over a caller-authored manifest by calling
  `claim_scope_for_with_memos` itself, so a refusal a control observes IS
  the refusal the corpus floor raises rather than a second copy of the
  logic. `PreparedRepository` is built entirely from the manifest and no
  corpus root is read.

THE TWO CONTROLS ARE RUN, NOT DESCRIBED, and they are about THIS
instrument rather than about any wall -- a control for a wall belongs
with the wall.

  a_well_formed_manifest_builds_a_claim_scope                  -> true
  an_entry_outside_the_subject_reaches_the_caller_as_a_scope_refusal -> true

The two manifests are identical and differ by exactly one string, the
entry MODULE PATH, so the only variable is the scope builder's verdict.

AND THE RED IS PINNED TO ITS CAUSE. `claim_scope_was_refused` says only
that SOMETHING refused, which would go green the day an unrelated refusal
started firing first and would then be cited as coverage for a cause it
never observed. `claim_scope_refusal_names` matches the arm AND requires
the cause to name `EntryModuleOutsidePreparedSubject`. Falsified rather
than assumed: pointing the marker at a string the refusal does not
contain turns that row `false`, so the predicate is reading the cause and
not merely the arm.

`ClaimScopeInstrumentRefused` and `ClaimScopeCompileRefused` read false on
both polarities, so a ragged manifest fails the positive and the negative
row together instead of satisfying whichever was phrased as an absence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The first revision of this instrument mapped EVERY `Err` from
`claim_scope_for` into `ClaimScopeRefused`. That made a refusal about the
HOST PROCESS indistinguishable from a refusal about the SUBJECT -- the
not-applicable-versus-malformed conflation this outcome type exists to
prevent, built into the type's own producer.

It is not a theoretical gap. `claim_scope_for_with_memos` reaches
`reference_closure_index`, which is memoized in a `thread_local!` keyed by
`subject_digest` and bounded at `FLOOR_PREPARED_SUBJECTS_PER_PROCESS`,
where a subject beyond that population is refused. The required floor
already holds both slots: the corpus subject, and the `policy_prepared`
subject built for `REQUIRED_FLOOR_POLICY_MODULE`. A fixture is therefore
the third subject and is refused -- for a reason that says nothing
whatever about the manifest.

MEASURED on gunbc#11143's floor, and the shape of the evidence is the
point: the POSITIVE control returned false while the NEGATIVE one PASSED,
the inverse of both passing locally. The memo is thread-local and the
floor evaluates claims across several workers, so the verdict depended on
which worker picked the claim up. And because the floor prints the
claim's Bool rather than the cause, the instrument could not report its
own failure mode -- the real reason sat in a `cause` field nothing reads.
That is what the conflation costs: not a wrong label, an undiagnosable
one.

TWO FIXES, both wrong on their own terms before this:

- The bounded resource is now ACQUIRED FIRST, and its refusal returns
  `ClaimScopeInstrumentRefused` naming the host process's caches. The
  readers already treat that arm as false on BOTH polarities, so a
  process that cannot host the fixture fails the positive and the
  negative control together rather than reporting a scope refusal that
  never happened.

- `subject_digest` was `String::new()`. In a digest-keyed memo an empty
  key is not merely uninformative: every fixture collides with every
  other fixture and with anything else leaving the field blank, and a
  scope can consume an index built from a different graph. It is now
  derived from the manifest through the same
  `multi_module_fixture_source_digest` the compile instrument uses.

WHAT THIS DOES NOT FIX, stated so it is not mistaken for the whole
repair: the fixture still CONSUMES one of the bounded per-subject slots,
so it still competes with the corpus and policy subjects. Removing the
competition needs a non-registering construction path and lands
separately; raising the cap is explicitly not the answer, because that
wall is a stated production cost limit and widening it to fit a test
instrument is the test dictating production limits.

EVIDENCE. Both controls still return true against a binary built with the
real digest, so the memo still resolves the scope it is keyed for:

  a_well_formed_manifest_builds_a_claim_scope                        -> true
  an_entry_outside_the_subject_reaches_the_caller_as_a_scope_refusal -> true

The `ClaimScopeInstrumentRefused` arm's own red is NOT authorable locally:
it needs a process already holding two distinct subjects, which a single
`gunbc run` does not. Its discriminating evidence comes from the floor,
and is not claimed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
The instrument still COMPETED with the floor for a bounded resource after
the previous commit classified that competition honestly. This removes it.

`reference_closure_index` is a `thread_local!` memo keyed by
`subject_digest` and bounded at `FLOOR_PREPARED_SUBJECTS_PER_PROCESS`,
where a subject beyond that population is refused. The required floor
already holds both slots -- the corpus subject and the `policy_prepared`
subject built for `REQUIRED_FLOOR_POLICY_MODULE` -- so a fixture going
through the cache is the third subject and is refused, for a reason that
says nothing about the manifest.

THE SPLIT. `build_reference_closure_index` is the body
`reference_closure_index` always ran, lifted out of the cache around it.
`reference_closure_index` is now that build plus the bounded memo, and
its behaviour is unchanged. `claim_scope_for_with_memos` gains one
parameter, `reference_index: Option<Rc<ReferenceClosureIndex>>`, where
`None` is exactly today's path. BOTH PRODUCTION CALL SITES PASS `None`,
so the corpus subject and the policy subject observe nothing new.

`Some(index)` is for a caller that has built the index for its own
subject and must not occupy a slot. The fixture is one module and is
discarded immediately; registering it would spend one of a bounded
population on a throwaway and refuse the next real subject.

RAISING THE BOUND IS NOT THE REMEDY. It is a stated production cost wall,
and widening it so a test instrument fits is the instrument dictating
production limits -- the absorbing fallback in a convenience costume.

A DISTINCTION THE SPLIT MADE VISIBLE. The extracted build keeps its OWN
refusal, `ExprVarReconciliationMismatch`, where a traversed occurrence
landed in no member. That one IS about the supplied graph, so the fixture
reports it as a scope refusal while the budget refusal is an instrument
refusal. Before the split the two were indistinguishable -- and both
carry the same `CLAIM-SCOPE REFUSAL cause=` prefix, so no text
discriminator would have separated them either.

THE NEW CONTROL, and what it is and is not evidence of.
`several_distinct_fixture_subjects_all_resolve` builds FIVE distinct
fixture subjects. Five rather than three deliberately: the row must fail
if the fixture occupies even ONE slot, because two would still fit a
process that happened to have room, and a probe that passes on a lucky
cache is not evidence.

Locally all three controls return true, and the log now shows a real
digest (`subject=dfafa6d2df784a25`) rather than the empty string the
first revision passed. But THE LOCAL PASS IS NOT THE PROOF: a single
`gunbc run` holds only the corpus subject, so there is room either way
and the OLD code would likely pass this row too. The discriminating
environment is the floor, where both slots are already taken and the old
code demonstrably failed -- positive control false, negative control
passed, inverted by which worker picked the claim up.

The second half of the non-competition claim -- that the corpus and
policy subjects still resolve -- is carried by the floor run CONTAINING
this row rather than by a separate assertion: every other claim is
evaluated against the corpus subject, so if these five had exhausted that
cache the floor would refuse rather than report a verdict.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
`ClaimScopeCompileRefused` carried only `blocking_count: Int`. The
diagnostic rows were in the host and dropped, so a `.dag` control could
see THAT a manifest never reached scope construction and never WHY.

That cost a wrong diagnosis, which is why this is a commit rather than a
tidy-up. The wall's subject control failed; the outcome was neither
Accepted nor ScopeRefused; and with no cause readable from the
instrument I supplied one by inference -- reading the
`undeclared_bare_type_reference_class_witness` prose and concluding that
two declarers of one name must be refused by the compile's global
uniqueness rule.

THAT INFERENCE WAS WRONG, and the corpus refutes it in one line: `Unit` is
declared by both `std.types` and `v2.std.cardinality`, `Filesystem` by
both `extdeps.filesystem.filesystem_io` (a service) and
`v2.extdeps.file_system` (a type), and main compiles every run. Two
declarers plainly do not refuse a compile. The real cause, read by
reproducing the compile by hand:

  resolved 1 sources (reference-derived closure entry=amb/user.dag),
  2 indexed modules in the name census only
  error[amb/user.dag:4:3]: undefined variable 'shared_thing'

`undefined variable`. The declaring modules were never in the compiled
closure -- indexed in the name census only -- so the read had nothing to
resolve to. Nothing to do with there being two of them.

I read an ARM and supplied a CAUSE from prose. That is precisely the
conflation this outcome type was split to prevent two commits ago, made
in my own reasoning instead of in the code, and the instrument's silence
is what let it stand.

So the arm now carries `diagnostics: List<CompileDiagnosticCensusRow>`
beside the count, and `claim_scope_compile_refusal_has_class` lets a
control name the class it got instead of reporting a bare
non-acceptance. The next person to hit this reads the cause instead of
guessing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
@gunbai-bot gunbai-bot Bot changed the title Bare-name ambiguity, stages 2-3: qualify the ~105 value-position reads by declaring module, then land the wall The bare-name ambiguity wall — NOT READY: its discriminating red has never fired Sep 12, 2026
@gunbai-bot

gunbai-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Merge hazard on this branch, recorded before it bites.

This branch (…-wall2) was cut from …-wall (#11143) and therefore inherited the third resolution tier. #11143 has since removed the tier, in response to review 64472 — correctly, because it was landed production behaviour with no discriminating control there, and its controls (the singly-declared-arm row and the two-visible-coproducts row) live here.

So the two branches now disagree about the tier on purpose:

…-wall   (#11143)  tier absent   — instrument only, controls proven
…-wall2  (#11166)  tier present  — tier + wall, controls not yet firing

Merging …-wall into …-wall2 will propagate the removal and silently delete the tier from this PR. Git will do it cleanly — there is no textual conflict, because one side deleted lines the other never touched — so nothing will flag it. The wall would then ship without the tier that stops it refusing five correctly-written variant-arm sites, which is the precise failure the tier exists to prevent.

If this branch needs anything from …-wall (the compile-diagnostics repair is already here), take it as a targeted cherry-pick rather than a branch merge, and verify afterwards with:

git show <this-branch>:src/v1/stage0/src/v1_interpreter.rs | grep -c 'variant_arm_owners\|site_resolved_variant_arm_module'

That should be non-zero. Zero means the tier was merged away.

This is a semantic conflict with no textual signature, which is why it is written down rather than left to be noticed.

— sent from witty-moth-510

…ure finds a tier defect

THE WALL'S CONTROLS COULD NOT RUN. Every arm of the witness returned the
same non-verdict, and the cause was read out of the instrument rather
than inferred once `ClaimScopeCompileRefused` started carrying its
diagnostic rows: `AmbiguousReference: shared_thing`. The manifest never
compiled, so no scope was ever built and no control observed anything.

WHY, MEASURED. v1 ALREADY refuses a bare read whose claimants it cannot
rank: `global_bare_policy_candidate` picks the nearest containment
ancestor, and where no candidate is an ancestor the compile emits a
blocking `AmbiguousReference`. The old fixtures put both claimants and
the reader in unrelated module trees, which is exactly the shape the
compile refuses. The class THIS wall covers is the other one -- the read
the compile SETTLES SILENTLY by ancestry, ranking two declarations by
where their modules sit rather than by anything the author wrote.

So the geometry now reproduces that settlement: the reader `amb.user`
sits under claimant `amb`, with `elsewhere.y` as the unranked second
claimant. A second measured requirement came with it -- each claimant
needs a uniquely-named puller (`only_amb`, `only_elsewhere`). A manifest
whose user reads only the contested name pulls in ONE claimant, so
nothing collides and the arm goes accepted for a reason unrelated to the
wall.

WHAT THE FIXTURE THEN CAUGHT, which is the part worth the whole exercise.
Both variant-arm rows were RED, and not because of the fixture:
`fragment_coproduct_disj_node` read `item.inferred`, which is an item's
TYPE and not its declaration. For `fn probe() -> Pick` that is the `Disj`
of `Pick`, so EVERY function returning a coproduct registered itself as a
second declarer of that coproduct's arms; the tier saw two visible owners
where the source has one and refused a read the author had named. The
branch is deleted. Reading a declaration off an inferred type is the same
category error in miniature that this wall exists to refuse.

ROW 5 NOW ASSERTS WHAT ACTUALLY HAPPENS. Two visible coproducts is
refused at COMPILE grain as `VariantCollision`, so no scope is built and
the tier's strict arm is unreachable through any manifest the compile
accepts. Keeping a row that claimed a scope refusal there would be a
control satisfied by a cause it never observed, so it asserts the compile
class instead, and the strict arm keeps its honest standing: the
fail-closed direction, with the compile wall below it as its evidence
rather than a control of its own.

Eight rows, all green by execution against a locally built binary:
subject refuses; field label, local parameter, singly-declared arm and
own-coproduct arm all accepted; two-coproduct manifest refused as
VariantCollision; refusal names a declaring module and carries a
pasteable import line.

Also: `claim_scope_refusal_names` uses `string_contains` (std.methods)
rather than the hand-rolled `(split |> count) > 1`, matching the repair
already made on gunbc#11143.

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

gunbai-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

The wall's controlled RED is now authorable, and enrolling it found a defect in the tier.

Pushed 013f1ba5c4. Eight rows, all green by execution against a locally built binary.

Why every arm was a non-verdict before. Once ClaimScopeCompileRefused started carrying its diagnostic rows, the cause was readable instead of inferable: AmbiguousReference: shared_thing. The manifest never compiled, so no scope was built and no control observed anything. v1 already refuses a bare read whose claimants it cannot rank — global_bare_policy_candidate picks the nearest containment ancestor, and where no candidate is an ancestor the compile emits a blocking AmbiguousReference. The old fixtures put claimants and reader in unrelated trees, which is precisely the shape the compile refuses. The class this wall covers is the other one: the read the compile settles silently by ancestry. The geometry now reproduces that settlement (reader amb.user under claimant amb, elsewhere.y unranked), plus a second measured requirement — each claimant needs a uniquely-named puller, because a user that reads only the contested name pulls in one claimant and the arm goes accepted for a reason unrelated to the wall.

What the fixture caught. Both variant-arm rows were red, and not because of the fixture. fragment_coproduct_disj_node read item.inferred, which is an item's TYPE, not its declaration — for fn probe() -> Pick that is the Disj of Pick, so every function returning a coproduct registered as a second declarer of that coproduct's arms. The tier saw two visible owners where the source has one and refused a read the author had named. Branch deleted; both arm rows green in the site's own module and through an explicit import. Nothing in the corpus could have shown this: the phantom owner needs a coproduct-returning function and a contested arm name in one scope.

Row 5 now asserts what actually happens. Two visible coproducts is refused at compile grain as VariantCollision, so the tier's strict arm is unreachable through any manifest the compile accepts. A row claiming a scope refusal there would be a control satisfied by a cause it never observed, so it asserts the compile class instead. The strict arm keeps its honest standing — fail-closed direction, evidence named as the compile wall below it, not a control of its own.

This PR stays in draft and is inside the fleet landing freeze; it also still depends on #11156 / #11138 / #11143 landing before the corpus census reaches zero on the population the wall governs.

— sent from witty-moth-510

@gunbai-bot

gunbai-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Both failures are one cause, and it is this wall doing its job against a corpus whose repair is held by the freeze.

required-witnesses-floor and witnesses both fail on the same refusal:

CLAIM-SCOPE REFUSAL cause=AmbiguousBareNameRead
scope=v2.test.execution.emit_on_demand_match_loop_fold_family_witness sites=16

The 16 sites are 14 Filesystem reads and 2 Clock reads. I checked the population against the fix rather than assuming it: the referring modules named in the refusal are exactly the set of dag/ files #11156 edits — gunbc.clock_read, gunbc.fabric_event_log_host, extdeps.provisioning.ubuntu_seeded_install_media_remaster, extdeps.realization.materialization_store_local, gunbc.auth.netrc_binding, gunbc.devboot.produce, gunbc.fabric_event_log, the three gunbc.host_effect_*, gunbc.package_delivery, the two gunbc.roadmap*, gunbc.srv3_boot_once_cd, gunbc.tools.bmc_onboard and tools.self_host_curated_seed_linked_harness. #11156 is green and CLEAN; it is held only by the fleet landing freeze on dag/, src/v1/, src/v2/.

So this red is not a defect in this diff — it is the ordering the campaign was designed around: the wall goes green when the qualifications land, and until then it correctly refuses the residue. The refusal text is also the remedy #11156 applies, site by site.

I am not stacking #11156 onto this branch to go green. A merge of it conflicts on src/v1/stage0/src/v1_interpreter_dispatch_generated.rs with GeneratedArtifactConcurrentDivergence — a generated projection both sides changed, whose declared repair route is a full regenerate-and-fixed-point cycle, not a hand resolution. Spending that on a draft PR to manufacture a green that the merge order produces for free is the wrong trade, and hand-resolving those bytes is exactly what the driver refuses.

This PR stays in draft until the freeze lifts and #11156 / #11138 / #11143 land. Its own eight controls are green by execution (013f1ba5c4).

— sent from witty-moth-510

#11156 landed, so the sixteen Filesystem/Clock sites this wall refused
are bound to their declaring services on main. That was the whole of this
PR's floor red -- the wall was firing correctly on a corpus whose repair
was still in flight.

One conflict, `v1_interpreter_dispatch_generated.rs`, the generated
projection both sides changed: main's file taken whole, this branch's
three generated rows re-added (3 insertions, 0 deletions against
origin/main). VERIFIED BY THE GATE rather than by that reasoning --
`claim_executor --required-regen` reports `first_generation_equal=true`,
155/155 planned/executed/adjudicated -- so the bytes are what the merged
authorities project. The conflict disappears entirely once #11143 lands,
since main then already carries these rows.

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

gunbai-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Merged main (d7b7ab96c) — the population this wall refused is now qualified on main. Head b6cb886a51ce.

#11156 landed, so the sixteen Filesystem/Clock sites this wall was refusing are bound to their declaring services. That refusal was the whole of this PR's floor red: the wall was firing correctly on a corpus whose repair was still in flight.

One conflict, v1_interpreter_dispatch_generated.rs — the generated projection both sides changed (GeneratedArtifactConcurrentDivergence). Main's file taken whole, this branch's three generated rows re-added: 3 insertions, 0 deletions against origin/main. Verified by the gate rather than by that reasoning — claim_executor --required-regen reports first_generation_equal=true, 155/155 planned/executed/adjudicated. That conflict disappears once #11143 lands, since main then already carries these rows.

Staying in draft for one more step, deliberately. I attempted to merge #11143's branch in directly so this PR would carry the two things already adjudicated there — the narrowed slot-competition commentary and the seed-growth receipt — and hit exactly the hazard recorded on this PR earlier: that merge strips the variant-arm tier (variant_arm_owners 5 → 0, site_resolved_variant_arm_module 5 → 0), because #11143's branch is where the tier was removed and this branch is where it lives. Git conflicted rather than silently taking it this time, but the resolved tree had the deletion in it.

So I aborted it. Those two repairs arrive cleanly via main once #11143 lands: main will carry no tier, this branch adds it, and there is nothing to collide. Hand-resolving a deletion into this wall's own detector to save one merge is not a trade worth making.

Verified after the abort: all four tier symbols intact.

— sent from witty-moth-510

#11143 landed, which is why the #11143-into-#11166 branch merge was
aborted earlier: taking the tier-removal through a branch merge stripped
the variant-arm tier (`variant_arm_owners` 5 -> 0), while taking the same
content through main does not, because main carries no tier and this
branch adds one.

VERIFIED IN THE MERGED TREE RATHER THAN ASSUMED, since "safer" is not
"checked": all four tier symbols present at their full counts, the
`AmbiguousBareNameRead` refusal present, the seed-growth receipt arrived,
and the narrowed slot-competition commentary arrived.

FOUR RESOLUTIONS, none of them a blind side-take:

1. `claim_scope_fixture_instrument_witness_test.dag` -- main's, whole. Its
   commentary is the version the side-chat review adjudicated; this
   branch's copy is the superseded draft.

2. `multi_module_compile_fixture.dag` -- main's file, plus only this
   branch's own addition: the `diagnostics` field on
   `ClaimScopeCompileRefused` and the reader over it. Main's `NonEmptyStr`
   causes are kept rather than reverted to bare `String`.

3. TWO DOC BLOCKS IN `cli_run.rs` WHERE MAIN'S TEXT IS TRUE ON MAIN AND
   FALSE HERE, which is why neither side could simply be taken. Main's
   §3c frontier says this type's consumer "is not in this diff" and names
   the trigger that would bring it -- both are satisfied HERE, so the
   paragraph is replaced with the discharge rather than left describing a
   state that no longer holds. And main's `ScopeAccepted` doc says
   acceptance is SILENT about ambiguity because the builder collects
   without refusing; this change adds the refusal, so acceptance now DOES
   exclude the census population, and the doc says so with the scope
   stated exactly (value-position only; type-position collisions remain
   out, per the wall's own annotation).

4. TWO DUPLICATE DEFINITIONS GIT CREATED SILENTLY. `emit_host.rs` and
   `v1_interpreter.rs` auto-merged with NO conflict and produced two
   copies each of `claim_scope_dag_multi_module_fixture` and
   `claim_scope_fixture_value` -- both sides added the same function, so
   git kept both, and only `rustc` E0428 surfaced it. The stale copies are
   deleted: they were this branch's older drafts carrying two defects
   review already repaired on main (a transcribed measurement, and a
   "carries an empty subject digest" claim that stopped being true once
   the digest was made real).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
Brian Searls and others added 3 commits September 13, 2026 06:26
…er exists

The floor refused `declarations` with one finding:

  IMPORT-MEMBER-ABSENT dag/test/claim/bare_name_ambiguity_wall_witness_test.dag:11:3:
  imports `claim_scope_was_refused` from `tools.multi_module_compile_fixture`,
  which declares no such name

This witness was written against an earlier shape of the fixture
instrument; gunbc#11143 deleted `claim_scope_was_refused` as a dangling
declaration before landing. Each side is correct alone and the
combination does not resolve.

NOT REPAIRED BY RESTORING THE READER, which would re-add a weaker
predicate than the one that survived. The subject control now uses
`claim_scope_refusal_names` pinned to `AmbiguousBareNameRead`, so it
requires the refusal to NAME its cause. That is strictly stronger and it
is the shape #11143's own review blessed on the negative control, which
demands `EntryModuleOutsidePreparedSubject` rather than accepting any
refusal: a probe satisfied by ANY refusal goes green the day an unrelated
one fires first, and is then cited as coverage for a cause it never
observed.

AND THE FINDING WAS NOT ASSUMED TO BE THE ONLY ONE. "One finding" may be
the first rather than all, so every member imported from that instrument
was audited across all 21 importing files against both trees: exactly one
name differs, `claim_scope_compile_refusal_has_class`, which is this
branch's own addition and resolves here. No second defect is hiding
behind the first.

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

My own row errored on the floor, and it is a defect in this branch rather
than an infra stall:

  adjudication BLOCKING phase=floor
  identity=test.claim.bare_name_ambiguity_wall_witness
           .an_arm_declared_by_two_visible_coproducts_is_refused_by_the_compile
  errored: type error: filter expects a list, got Null

CAUSE, AND IT IS MINE. Resolving the main merge, I took main's Rust
projection of `ClaimScopeFixtureOutcome` and this branch's `.dag` type.
Main's projection sets only `blocking_count`; this branch's type declares
`diagnostics: List<CompileDiagnosticCensusRow>` as well. So the field was
declared and never set, arrived as Null, and the first reader to fold over
it failed. A field a type declares and a projection never sets is not a
smaller answer -- it is an unwritable one.

The projection now carries the rows, which is also what lets the
instrument say WHY a manifest failed to compile rather than only THAT it
did; that is the whole reason this branch widened the arm.

WHY THIS IS WORTH A SENTENCE BEYOND THE FIX: the merge that produced it
conflicted on the `.dag` type and NOT on the Rust projection, so the
halves were adjudicated separately and agreed with neither. Two silent
defects from that same merge were already found by a build (duplicate
definitions, E0428); this third one a build cannot see, because the
mismatch is between a `.dag` declaration and a Rust value, and only
EXECUTION reaches it. The controls are what found it.

Re-run locally on a rebuilt binary: the failing row and two neighbours
all return true.

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

Review 65430, REQUEST_CHANGES, and it is right on both halves. Verified in
the code rather than accepted:

  cli_run.rs          the wall returns Err when `ambiguous_reads` is
                      non-empty, BEFORE `PreparedClaimScope` exists
  required_floor_runner.rs:6590   `let built = claim_scope_for(..)?`

So a non-empty population propagates the `?` and never reaches the census.
`ambiguous_read_sites` could only ever be populated from a scope that was
built, which the wall now makes impossible -- the
`[floor-bare-name-ambiguity-read]` per-site lines and the
`[floor-bare-name-ambiguity-reads]` summary could print zero and nothing
else. DESIGN §4b: "permanently green by construction, carrying no
information, and worse than absent because it will be cited as coverage."

DELETED PER §4b(4), which is the rule that distinguishes this from
weakening the floor: a climb deletes the redundant lower-rung PRODUCTION
machinery it obsoletes, while the class's discriminating RED and positive
control REMAIN ENROLLED. The evidence is separate and stays --
`bare_name_ambiguity_wall_witness_test.dag` keeps all eight rows,
including the subject that must refuse and the two controls that must not.
Removed: the per-site loop, the summary line, the `ambiguous_read_sites`
map and its population. Clippy clean, so nothing else read them.

AND THE ANNOTATION FALSIFIED ITSELF. It said "the census lines in
`required_floor_runner` read this same vector, so the refusal and the
count cannot drift apart". After this climb they cannot AGREE either --
the count is structurally silent. Withdrawn and replaced with what is
true, including why the two adjacent censuses are NOT in scope:
`ambiguous_bare_names` and `qualified_bare_reads` are populated on scopes
the wall ACCEPTS, so they keep carrying information. Only the read half
dies, because only it reads the population the wall refuses on.

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

gunbai-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review 65430's REQUEST_CHANGES is correct on both halves, and I verified each in the code rather than accepting them. Pushed a9285c4f69dc.

1. The read census was permanently zero by construction. The wall returns Err when ambiguous_reads is non-empty, before PreparedClaimScope exists; required_floor_runner reaches built only through claim_scope_for(..)?. So a non-empty population propagates the ? and never arrives at the census — [floor-bare-name-ambiguity-read] and [floor-bare-name-ambiguity-reads] could print zero and nothing else. That is DESIGN §4b's decoration: "permanently green by construction, carrying no information, and worse than absent because it will be cited as coverage."

Deleted under §4b(4), which is what distinguishes this from weakening the floor — a climb deletes the redundant lower-rung production machinery it obsoletes, while the discriminating RED and positive control remain enrolled. The evidence is separate and stays: bare_name_ambiguity_wall_witness_test.dag keeps all eight rows, including the subject that must refuse and the field-label and local-parameter controls that must not. Removed the per-site loop, the summary line, the ambiguous_read_sites map and its population; clippy is clean, so nothing else read them.

2. The annotation falsified itself, and that is the sharper half. It claimed "the census lines in required_floor_runner read this same vector, so the refusal and the count cannot drift apart." After this climb they cannot agree either — the count is structurally silent. Withdrawn and replaced with what is true, including the scope the review drew and I had not: ambiguous_bare_names and qualified_bare_reads are populated on scopes the wall accepts, so they keep carrying information. Only the read half dies, because only it reads the population the wall refuses on.

The review's framing is the part worth keeping past this PR: a climb that leaves its own census standing produces a line that can only ever agree with itself, and the fact that it was my annotation asserting the opposite is what made it findable.

— sent from witty-moth-510

… census took with it

Review 65520, REQUEST_CHANGES, three findings, all verified and all mine.

1. A MERGE-BLOCKING CLIPPY RED I INTRODUCED. `ambiguous_claimants_len`
   was bound for the `[floor-bare-name-ambiguity-reads]` line that the
   census dissolution deleted, leaving a dead binding. `unused_variables`
   is warn-by-default and CLAUDE.md states `cargo clippy --all-targets --
   -D warnings` runs in `required-witnesses-build`, so a warning there
   blocks a merge. Deleted; `cargo clippy --all-targets -p v1-compiler --
   -D warnings` is now clean.

   MY EARLIER CLIPPY RUN DID NOT COVER IT: I ran `--release -p v1-compiler
   --lib`, which does not build the integration-test and example targets
   and reports a different set. CLAUDE.md names `--all-targets` as "the
   only command that compiles the integration-test and example targets, so
   a red there is invisible to every other step", and I used the invisible
   one.

2 & 3. TWO DOC BLOCKS DELETED AS COLLATERAL, NOT AS DISSOLUTION. The
   annotations on `compile_dag_multi_module_fixture` (27 lines) and
   `multi_module_compile_fixture_value` (5 lines) are gone from the tree.
   §4b(4) authorizes deleting obsoleted PRODUCTION machinery on a climb;
   these are the scope statement and next-rung-trigger bookkeeping of an
   instrument that still runs, and §4b(2) requires a class below its
   ceiling to keep naming its trigger. Restored verbatim from origin/main;
   `git diff origin/main` on both files now deletes ZERO doc lines.

   HOW THEY WENT: the duplicate-definition dedup two heads ago walked
   upward from the function it was deleting while lines started with
   `///`, and swallowed the doc block belonging to the NEXT function. That
   is the same slice-swallow that took the string_eq doc on gunbc#11138
   earlier today -- second instance, same mechanism, and the check that
   would have caught both is the one I ran on that PR and not on this one:
   after any deletion, diff against the tree the deletion was made from and
   count the doc lines it removes.

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

gunbai-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review 65520's three findings are all verified and all mine. Pushed 572da555e82c.

1. A merge-blocking clippy red I introduced. ambiguous_claimants_len was bound solely for the [floor-bare-name-ambiguity-reads] line the census dissolution deleted. Removed; cargo clippy --all-targets -p v1-compiler -- -D warnings is clean.

Worth stating why I missed it: I ran --release -p v1-compiler --lib. CLAUDE.md names --all-targets as "the only command that compiles the integration-test and example targets, so a red there is invisible to every other step" — I used the invisible one and reported it as clippy-clean.

2 & 3. Two doc blocks deleted as collateral, not as dissolution. The annotations on compile_dag_multi_module_fixture (27 lines) and multi_module_compile_fixture_value (5 lines) were gone from the tree. §4b(4) authorizes deleting obsoleted production machinery on a climb; these are the scope statement and next-rung-trigger bookkeeping of an instrument that still runs, and §4b(2) requires a class below its ceiling to keep naming its trigger. Restored verbatim from origin/main — git diff origin/main on both files now deletes zero doc lines.

How they went, because the mechanism matters more than the restoration: the duplicate-definition dedup two heads ago walked upward from the function it was deleting while lines started with ///, and swallowed the doc block belonging to the next function. That is the same slice-swallow that took the string_eq doc on #11138 earlier today — second instance, same mechanism.

And the check that would have caught both is one I ran on that PR and not on this one: after any deletion, diff against the tree the deletion was made from and count the doc lines it removes. On #11138 I ran it and it found nothing, because the lost block was my own branch's addition; here it would have worked, because these were main's lines. Two failures, one instrument, and I applied it to the case it couldn't see and skipped the case it could.

— sent from witty-moth-510

@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 13, 2026 23:20
…sert, and its last citation

Review 65757, REQUEST_CHANGES, three findings, all correct and all the
same class -- the climb deleted the census's CONSUMER and left everything
that fed it.

1. THE FIELD WAS RETAINED WRITE-ONLY. The wall returns `Err` whenever
   `ambiguous_reads` is non-empty and BOTH entry points reach it
   (`claim_scope_for_without_memos` delegates to
   `claim_scope_for_with_memos`), so on every `Ok` path
   `PreparedClaimScope.ambiguous_bare_reads` was the empty vector by
   construction. §4b(4) says a climb deletes the lower-rung PRODUCTION
   machinery it obsoletes, and the collection into the returned struct is
   exactly that. Field and push both deleted.

2. THE MEMO-EQUIVALENCE ASSERT WAS `vec![] == vec![]`. A non-empty
   population propagates `Err` through the `.expect("memoized scope")`
   above it, so the assertion could not fail. That is the same §4b
   sentence this diff already quotes to justify deleting the census
   lines -- "permanently green by construction, carrying no information,
   and worse than absent because it will be cited as coverage" -- and I
   applied it to the printed lines while leaving an assert in the same
   state two thousand lines away. Deleted. The sibling asserts on
   `ambiguous_bare_names` and `qualified_bare_reads` stay: those
   populations are non-empty on accepted scopes and the comparison can
   still fail.

3. AND THE LAST CITATION OF THE DELETED CENSUS. The fixture doc still
   said the wall reads "the same `ambiguous_reads` vector the census
   prints from" while this diff deletes that census -- a named instrument
   that no longer exists, which is §6's rot in the direction the rule
   warns about.

I also retired two dangling symbol citations the review did not raise: two
historical sentences still named `ambiguous_bare_reads` after the field
was gone, so a reader following them greps for a symbol that does not
exist. Both now say what was deleted without naming a dead symbol. Zero
mentions remain in `src`.

`cargo clippy --all-targets -p v1-compiler -- -D warnings` clean.

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

gunbai-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review 65757's three findings are all correct and all the same class: the climb deleted the census's consumer and left everything that fed it. Pushed 32e7fb76929d.

1. The field was retained write-only. The wall returns Err whenever ambiguous_reads is non-empty and both entry points reach it, so on every Ok path PreparedClaimScope.ambiguous_bare_reads was the empty vector by construction. §4b(4) names the collection into the returned struct as exactly the production machinery a climb dissolves. Field and push deleted.

2. The memo-equivalence assert was vec![] == vec![]. A non-empty population propagates Err through the .expect("memoized scope") above it, so the assertion could not fail. That is the same §4b sentence this diff already quotes to justify deleting the census lines — and I applied it to the printed lines while leaving an assert in the identical state two thousand lines away. Deleted.

The sibling asserts on ambiguous_bare_names and qualified_bare_reads stay: those populations are non-empty on accepted scopes, so those comparisons can still fail.

3. The last citation of the deleted census. The fixture doc still said the wall reads "the same ambiguous_reads vector the census prints from" while this diff deletes that census — §6 rot in the direction the rule warns about, a named instrument that no longer exists.

I also retired two dangling symbol citations the review did not raise. Two historical sentences still named ambiguous_bare_reads after the field was gone, so a reader following them would grep for a symbol that does not exist. Both now say what was deleted without naming a dead symbol; zero mentions remain in src.

The generalizable part, since this is the second time today one of my climbs was half-finished: deleting a consumer is not dissolving a class. The consumer is the visible end; the producer, the field it fills, the asserts that compare it and the prose that cites it all outlive it silently, and each one reads as coverage afterward. The check is to grep the symbol and account for every hit before calling the climb done — which is what I did here only after being told.

cargo clippy --all-targets -p v1-compiler -- -D warnings clean.

— sent from witty-moth-510

… contradicting annotation

Review 65775, REQUEST_CHANGES, three findings, all correct.

1. THE WALL'S DISCRIMINATING RED HAD NEVER FIRED -- the PR title said so
   and I had been treating "the rows are green" as the evidence. §4b(1)
   wants the rung established by EXECUTED evidence: a discriminating RED
   refused on the real acceptance path plus an accepted positive control.
   Executed now, by building the seed twice against this same manifest
   with only the wall's own guard disabled between them:

     WALL REMOVED   bare_value_read_of_an_ambiguous_name_refuses   false
                    the_refusal_names_a_declaring_module           false
                    a_field_label_sharing_...does_not_refuse       TRUE
     WALL RESTORED  all three                                      true

   The two rows that assert the refusal flip with the wall and back; the
   negative control holds in both directions. That last part is the
   load-bearing half -- it shows the rows moved because the REFUSAL moved
   and not because anything about the manifest or fixture changed. The
   receipt is recorded at the rows, with the re-derivation route, and the
   disabling edit is not in this branch.

2. THE NEW TIER WAS INVISIBLE TO THE MEMO-EQUIVALENCE FINGERPRINT.
   `resolution_fingerprint` claims to cover "EVERY RESOLUTION THIS INDEX
   SET CAN ANSWER" and did not enumerate `variant_arm_owners`, which this
   PR adds and which `site_resolved_variant_arm_module` answers from. So
   the new memoized fragment field travelled the memo path with zero
   equivalence coverage, and a divergence there flows straight into
   whether the wall fires -- the fail-open direction being §5's absorbing
   fallback. One loop added. Deleting the old `ambiguous_bare_reads`
   assert was right; leaving the new tier uncovered was the regression,
   and both were in the same diff.

3. AND THE FIXTURE'S ACCEPTANCE ANNOTATION SAID THE OPPOSITE OF ITS RUST
   TWIN. The `.dag` block still read "ACCEPTANCE IS SILENT ABOUT
   AMBIGUITY... must not be cited as coverage for a refusal that is not in
   this change", while this diff makes all three clauses false and the
   five `claim_scope_was_accepted` control rows are precisely rows cited
   as that coverage. Its Rust twin was rewritten and this one was not --
   one fact, two annotations, opposite obligations (§3 meaning fork).
   Worse, my paragraph was inserted BELOW the stale block, so the orphan
   became the leading `//` block of `claim_scope_compile_refusal_has_class`
   -- §4c binds an annotation to the declaration it leads, and that is the
   third time this slice/dedup mechanism has moved prose onto the wrong
   declaration in this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5dKcSuYW3nvAjtVcDxqhT
@gunbai-bot gunbai-bot Bot changed the title The bare-name ambiguity wall — NOT READY: its discriminating red has never fired Refuse an ambiguous bare-name read in the v1 evaluator: the wall, its fixture-authored RED, and the variant-arm tier Sep 14, 2026
@gunbai-bot

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Review 65775's three findings are all correct. Pushed 8798f05be126, and the PR is retitled because the condition its old title named is now satisfied.

1. The discriminating RED has fired. The review is right that I had been treating "the rows are green" as the evidence, and §4b(1) asks for the rung established by executed evidence — a discriminating RED refused on the real acceptance path plus an accepted positive control. Executed by building the seed twice against the same manifest, with only the wall's own if !ambiguous_reads.is_empty() guard disabled between them:

WALL REMOVED    bare_value_read_of_an_ambiguous_name_refuses              false
                the_refusal_names_a_declaring_module                      false
                a_field_label_sharing_an_ambiguous_name_does_not_refuse   TRUE
WALL RESTORED   all three                                                 true

The two rows asserting the refusal flip with the wall and back. The negative control holding in both directions is the load-bearing half: it shows those rows moved because the refusal moved, not because anything about the manifest or the fixture changed. Receipt recorded at the rows with its re-derivation route; the disabling edit is not in this branch and nothing re-derives it.

2. The new tier was invisible to the memo-equivalence fingerprint. resolution_fingerprint claims to cover "every resolution this index set can answer" and did not enumerate variant_arm_owners — so the new memoized fragment field travelled the memo path with zero equivalence coverage, and a divergence there flows straight into whether the wall fires. The fail-open direction is §5's absorbing fallback, which is the one direction a silent divergence must not take. One loop added.

The pairing is the part worth naming: deleting the obsolete ambiguous_bare_reads assert was right, and leaving the new tier uncovered was the regression — both in the same diff. I removed equivalence coverage and added an uncovered field in one change and checked neither against the other.

3. The fixture's acceptance annotation said the opposite of its Rust twin. All three clauses of the old block are false after this change, and the five claim_scope_was_accepted rows are precisely the "rows cited as coverage" it forbids. One fact, two annotations, opposite obligations. Compounding it, my replacement was inserted below the stale block, so the orphan became the leading // block of a function it does not describe — the third time in this branch that the same slice/dedup mechanism has moved prose onto the wrong declaration.

— sent from witty-moth-510

…strument instead of quoting it

Review 65808, REQUEST_CHANGES, both findings correct.

1. THE ROW PROJECTION WAS HAND-COPIED THREE TIMES -- in
   `compile_diagnostic_census_value`, in `claim_scope_fixture_value`, and
   in `multi_module_compile_fixture_value` forty lines apart in one file.
   §2's test is that "net concepts must not grow by re-invention", and the
   cost here is not style: the day the `.dag` row gains a field, two of
   the three keep emitting the old shape and the new field arrives NULL at
   its first reader. THIS INSTRUMENT HAS ALREADY PAID THAT PRICE ONCE --
   the `diagnostics` field this branch added was declared and unset, and a
   control died with `type error: filter expects a list, got Null`. I fixed
   that instance and then left three producers standing to do it again.

   One `compile_diagnostic_census_row_value`, three callers.

2. AND MY §4b(1) RECEIPT WAS A TRANSCRIBED TABLE IN A COMMENT. It quoted
   the two-binary readings and said, in its own words, that "nothing
   re-derives it" -- which is §6 ("a transcribed number is unreachable
   from the thing that owns it, so it rots") and §4c ("an annotation is
   never evidence that a machine claim holds, because no Accepted program
   can read one"). A rung resting on prose is what this wall exists to
   refuse, one level up, and I wrote one into the wall's own witness while
   correcting other people's transcriptions all day.

   Replaced with the two things that are legitimately annotation content:
   THE INSTRUMENT, named so anyone can re-derive it (negate the wall's own
   `!ambiguous_reads.is_empty()` guard, rebuild, run these three rows
   against both binaries), and THE PREDICATE the run decides -- the two
   refusal rows must FLIP with the wall and the field-label control must
   HOLD in both directions, because a run where all three move together
   would mean the rows are keyed on the manifest rather than on the wall.
   Stated as what the run must show rather than as what a run showed.

   The next rung is named rather than claimed: the mutation is a hand edit
   of the seed, not a mode of it, and making it a flag is what would turn
   this receipt into an instrument.

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

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Review 65808's two findings are correct. Pushed 0b8761cba055.

1. The row projection was hand-copied three times — in compile_diagnostic_census_value, in claim_scope_fixture_value, and in multi_module_compile_fixture_value forty lines apart in one file. §2's test is that "net concepts must not grow by re-invention", and the cost here isn't style: the day CompileDiagnosticCensusRow gains a field, two of the three keep emitting the old shape and the new field arrives NULL at its first reader.

This instrument has already paid that exact price once. The diagnostics field this branch added was declared and never set, and a control died with type error: filter expects a list, got Null. I fixed that instance and then left three producers standing to do it again. One compile_diagnostic_census_row_value, three callers.

2. My §4b(1) receipt was a transcribed table in a comment — and it said in its own words that "nothing re-derives it", which is the confession. §6: "a transcribed number is unreachable from the thing that owns it, so it rots." §4c: "an annotation is never evidence that a machine claim holds, because no Accepted program can read one." A rung resting on prose is what this wall exists to refuse, one level up, and I wrote one into the wall's own witness while correcting other people's transcriptions all day.

Replaced with the two things that are legitimately annotation content:

  • The instrument, named so anyone can re-derive it: negate the wall's own !ambiguous_reads.is_empty() guard, rebuild, run these three rows against both binaries.
  • The predicate the run decides, stated as what it must show rather than what a run showed: the two refusal rows must flip with the wall, and the field-label control must hold in both directions — because a run where all three move together would mean the rows are keyed on the manifest rather than on the wall, and the pair would establish nothing.

And the next rung is named rather than claimed: the mutation is a hand edit of the seed, not a mode of it, so making it a flag is what would turn this receipt into an instrument.

— sent from witty-moth-510

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit ed853cf Sep 14, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/witty-moth-510-wall2 branch September 14, 2026 03:20
gunbai-bot Bot pushed a commit that referenced this pull request Sep 14, 2026
…es on the floor's path); cite the real symbols; typed WitnessDeleted rows for the nine deleted lens identities (review 66247)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aMigU7Gz7kFLQageRhCqx
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