Repository navigation
Conversation
`[floor-bare-name-ambiguity]` reported four counts and not one name. `names_total` is summed across scopes, so a name ambiguous in three hundred scopes is counted three hundred times, and no line in the receipt said which names those were. That is enough to size a campaign and not enough to run one: layer 2 of this class is a REFUSAL landed together with the rename or qualification of every site it would refuse, and that site list is exactly what the producer was discarding. `claim_scope_for` already knew it — it built a `HashSet<String>` of the out-of-region colliding names and returned `.len()`. It now returns the rows: each name with every module outside the authored region that claims it and the KIND it claims it as, both sides of the collision rather than only the newcomer, since a census naming only the loser cannot say what it collided with. The authored-region rule is untouched: a name the author's own imports rank is ordinary shadowing and is still not counted. The floor unions those rows across the fold and prints one `[floor-bare-name-ambiguity-name]` line per DISTINCT name with its claimants, its kind signature, and the number of scopes it is ambiguous in; `names_distinct=` is appended to the existing summary line, and a `[floor-bare-name-ambiguity-kinds]` histogram folds the signatures — `data+fn` is the shape that crosses the evaluator's kind dispatch, `fn` alone the common shape that merely picks a body. The per-name list is deliberately not truncated to a top-N: a census whose tail is elided is an allow-list with extra steps, and the refusal this feeds admits none. Report-only. No refusal lands here, no budget moves, and the resolution the evaluator performs is byte-for-byte the one it performed before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…the reporter `AmbiguousBareName::kind_signature` landed unconsumed while the floor's reporter recomputed exactly that fold inline -- a dangling `pub` declaration (DESIGN section 3c) and a second copy of its logic (section 2) in one diff. The reporter now unions the per-scope claimants back into the same `AmbiguousBareName` carrier the scope produced and reads the signature off it, so one type answers both about a scope's rows and about the census, and the fold exists once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…rations The declaration census says which names two transitively-reached modules both spell. It does not say anything READS one, and that gap is the whole difference between a refusal that dissolves a defect and one that refuses a convention: the corpus deliberately carries per-module rows that every extdeps module declares -- `extdeps_external_authority_anchor` is claimed by 315 modules -- and a wall keyed on the declaration population would refuse all of them. So the scope now also carries its READS: every bare-name reference site in the scope whose referring module declares the name nowhere itself and has nothing in its own authored import closure declaring it either, so the reference falls through to the shared slot and the slot holds declarations nothing the author ranked. That is the interpreter's own three-step resolution used as the filter, in its order, with the authored-region rule unchanged. COUNTED STATICALLY, over every reference site in each scope's closure, never over the lookups the fold happens to execute. An execution-keyed census omits a reference on a path no witness runs -- exactly the site a later refusal would surprise, and the site a runtime-placed wall would never refuse at all. The sites are read off `reference_closure_index`, which already classified every ExprVar occurrence into bindings and free references and publishes the free ones per module, so this is a projection of an index the scope already consulted and not a second walk. The floor prints one `[floor-bare-name-ambiguity-read]` line per (name, referring module) site with the declarations the slot is choosing between, and a `[floor-bare-name-ambiguity-reads]` line carrying read_sites, read_names_distinct and declared_names_distinct side by side -- the two populations in one line, which is what the rename campaign's size is read from. Still report-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
`read_sites` counted distinct (name, referring module) pairs and called them reference sites. `refs_by_module` publishes a module's free references DEDUPED, so three references to one name from one module are one row -- the pair grain is the right one, because a fix is written per referring module and repairs every occurrence in it, but the receipt has to say so. The key is now `read_name_module_pairs`, and both the carrier's doc and the reporter's note state the grain and why it is not an occurrence count. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…a wall can key on
The first read census keyed on `refs_by_module`, and that set is a deliberate
UNION over several question kinds because its only consumer asks "which modules
must this scope reach": a type annotation's `String`, a record literal's type
name, a variant pattern's constructor and an expression's callee all widen a
closure equally, and over-approximating there is harmless.
It is not harmless as a refusal's population, and the census showed exactly how.
Of 8 names read ambiguously, 5 were type-position -- String, Int, List, Bool,
WireContract, each declared by both `std.*` and its `v2.std.*` self-host copy --
and 3 were not reads at all: `observation` came from the pattern
`BmcFirmwareEvidenceObserved { observation: o }`, `population` from a call's
argument label, `observe` from a function's own fn-typed parameter. A wall keyed
on that set would refuse a record field the day a second declaration of its
spelling appeared.
So the collector now publishes the VALUE-POSITION projection beside the union,
built at the two arms the interpreter's own three-step resolution serves -- a free
`ExprVar` and a call's target -- rather than filtered back out of the union
afterwards, which could only approximate a walk that already knows the answer. The
census and its wall key on that projection; the closure walk keeps the union it
needs.
And the adjacent class is NAMED rather than silently filtered: the receipt carries
a `channel=value_position_only not_covered=type_position_name_collisions
owner=v2_self_host_replacement_migration` line, so a zero in this census can never
be cited as "no ambiguity in the corpus". The std/v2.std double is a real
ambiguity in the type channel, owned by the migration that ends it by ending the
double, and is not a rename this wall could ask for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…n index The filter re-derived "the author's imports rank it" from `func_env.parents`, and that is a second resolution rule beside the first. The interpreter's second tier is `file_import_bindings` -- the module a FILE explicitly imported a name from -- while `parents` is the flattened transitive closure, whose own carrier note says it "does not separate a direct import from a transitively reachable module". So any transitively reached module that merely spelled the name suppressed a row while the interpreter still fell through to the shared slot, and the census under-reported exactly the residue it exists to enumerate: a name reached through a wildcard import, which binds nothing and ranks nothing. The question is now asked where it is answered. `PreparedScopeIndexes::falls_through_to_shared_slot` runs `lookup_fn_from`'s first two tiers, in its order and with its conditions, on the same index `lookup_fn_from` reads -- own module's qualified declaration, then the file's explicit import binding -- and the census consults it after the scope's indexes are built. One rule, one home, and the wall this feeds will consume the same method rather than a copy of it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…tiers The previous head said "one rule, one home" and left the interpreter its own copy: `falls_through_to_shared_slot` restated `lookup_fn_from`'s first two tiers while `lookup_fn_from` kept restating them itself. The copies had already disagreed -- the interpreter gates the tiers on `ambiguous_bare_function_names` and the restatement did not, despite claiming to ask them "with its conditions". A census that can drift from the live lookup does not name the population execution resolves, which is the whole point of keying a refusal on it. There is now one implementation: `PreparedScopeIndexes::site_resolved_fn` answers the site-relative tiers and returns the node or `None`. `lookup_fn_from` IS that method `.or_else(shared slot)`, and `falls_through_to_shared_slot` is that method `.is_none()`. Nothing to drift from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
The value-position projection recorded every `ExprCall`'s target name without consulting the binder stack, which the `ExprVar` arm one case above has always done. So a function calling its OWN fn-typed parameter or its own `let` was reported as a bare read of the shared slot: `std.goal_assessment`'s `inspect_goal` takes `observe: fn(Subject, ObservationRequest) -> ...` and matches on `observe(subject, request)`, and `gunbc.artifact_acquisition` writes `let observe = handler.observe` and calls it. Both were counted; neither reaches the slot, and a wall keyed on that projection would have refused a local. The binder stack is already threaded through the walk and already answers this question for free variables, so the callee arm now asks it too. The closure union is untouched -- over-approximating THERE is correct, since a module whose callee happens to be local still has to be reachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
…see it
The census's negative -- a (name, referring module) pair leaving the ambiguous-read
list -- proves only that the list moved. So the receipt now carries the positive
half: for a name that IS ambiguous in the shared slot and is NOT read through it at
some site, `[floor-bare-name-ambiguity-bound]` names the module that answered,
derived from the resolved declaration's own source file through the same
`file_module_paths` the first tier reads. A qualification that the resolver
silently dropped would leave the pair in the ambiguous list; a qualification that
bound to the WRONG authority would show up here as the wrong module.
And the proof site itself: `extdeps.tools.hostname` adds `Unit` to the
`import std.types { ... }` line it already carries for `String`, `List`, `Bool`,
`Int` and `NonEmptyStr`. That is the whole edit. `Unit` and `String` appear
side by side in its exit block, one explicitly imported and one falling through to
the shared slot where `std.types` and `v2.std.cardinality` both spell it and
precedence picks arbitrarily. The module's own import line is the evidence of which
authority it means, so the qualification decides the read the way the author
already decided its siblings.
This is one site deliberately: the campaign is about 106 of them, and there is a
standing finding that a selective import can be dropped at the resolver, so the
hundred waits on this one landing green with its pair bound to std.types and
`test.claim.hostname_set_invocation_lowering_witness_test` still passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 610b50e970
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Already won inside the authored region: the author's imports rank it and | ||
| // precedence has settled it. Ordinary shadowing, not ambiguity. | ||
| Some((_, true)) => {} | ||
| Some((_, true, _)) => {} |
There was a problem hiding this comment.
Include authored winners in the wildcard collision census
When the winning declaration entered the authored region through a wildcard import, this arm suppresses every later claimant, even though wildcard imports intentionally create no file_import_bindings entry. Consequently, a bare read of that name still falls through site_resolved_fn to the ambiguous shared slot, but the name is absent from ambiguous, so the new ambiguous_bare_reads scan never examines or reports it. Record the competing claimants here and decide ambiguity per read site instead of treating every authored-region winner as explicitly bound.
Useful? React with 👍 / 👎.
|
Closing as a duplicate: this is the same nine-commit series that landed as #11078 (merged 2026-09-12T03:15:29Z), and the census content is already byte-identical on main. The branch tip is now ~490 files behind main, so merging it would only re-open a stale tip inside the same Rust files other repairs have since touched. Confirmed against origin/main by direct comparison, not by a three-dot diff, which misleads after a squash merge. The campaign continues under node adhoc-3fc0db35-69c (PRs #11137, #11138 and the wall). Auto-opened by the session dashboard after its authoring session was archived; no work is lost. — sent from bright-eagle-728 |
Auto-opened by session-dashboard for session
snappy-pike-265.Pushing to
session/snappy-pike-265advances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan