Skip to content

The bare-name ambiguity census names its population instead of sizing it - #11078

Merged
briansrls merged 9 commits into
mainfrom
session/snappy-pike-265
Sep 12, 2026
Merged

briansrls merged 9 commits into
mainfrom
session/snappy-pike-265

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

[floor-bare-name-ambiguity] reported four counts and not one name. names_total is summed across scopes, so a name ambiguous in 300 scopes is counted 300 times, and nothing said which names those were — enough to size a campaign, not enough to run one.

Layer 2 of this class (v1#11065 is layer 1) is a REFUSAL landed together with the rename or qualification of every site it would refuse. That site list is exactly what claim_scope_for was discarding: it built a set of the out-of-region colliding names and returned .len(). It now returns the rows — each name, every module outside the authored region that claims it, and the kind it claims it as, both sides of the collision. The authored-region rule is untouched.

The floor unions those rows across the fold and prints one [floor-bare-name-ambiguity-name] line per DISTINCT name (claimants, kind signature, scopes-affected), appends names_distinct= to the summary line, and folds a [floor-bare-name-ambiguity-kinds] histogram. Not truncated to a top-N: an elided tail is an allow-list with extra steps.

Report-only — no refusal, no budget move, resolution unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RMZWo8pBkPif7V9ZNNVTye

v1 PURPOSE admission (DESIGN §3, gunbc.v1_maintenance_standing v1_seed_standing)

This change is admitted because it serves the v2 self-host program, not v1's growth for its own sake: it is the census feeding the layer-2 refusal on bare-name ambiguity in the seed's shared slot (layer 1 is #11065), and bare-name resolution is a rule v2 has to reproduce exactly. Deletion-is-the-census requires that refusal to land together with the fix for every site it would refuse, so enumerating those sites is the prerequisite, not an optional diagnostic.

No new v1 capability, no budget raised, resolution byte-identical — except where a correction was required: lookup_fn_from and the census now consume one implementation of the site-relative tiers (PreparedScopeIndexes::site_resolved_fn), which is behavior-identical to the body it replaces.

Nothing is deleted here because the deletion IS the wall, which lands in the follow-up PR together with whatever renames the census names.

Brian Searls and others added 2 commits September 11, 2026 18:21
`[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
@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in the pushed head.

  • kind_signature unconsumed + logic duplicated — correct, and it was the worse of the two shapes: the method's own doc-comment asserted a consumption that did not exist. The reporter now unions each scope's claimants back into the same AmbiguousBareName carrier the scope produced and reads row.kind_signature() off it, so the fold exists once and the carrier answers about both grains. git grep kind_signature is now definition + one call site.
  • v1 PURPOSE test, stated explicitly — this change is admitted under gunbc.v1_maintenance_standing v1_seed_standing because it serves the v2 self-host program rather than v1's own growth: it is the census input to the layer-2 bare-name refusal for the namespace cut (layer 1 is Read a name's kind from the node it resolves to, not a bare-name registry projection #11065). The refusal is a replacement migration of the name-resolution rule, and deletion-is-the-census requires the refusal to land together with the rename or qualification of every site it would refuse — that site list is precisely what this diff stops discarding. No new v1 capability, no budget move, resolution byte-identical.

— sent from snappy-pike-265

Brian Searls and others added 2 commits September 11, 2026 19:50
…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
@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Grain finding accepted — it was a real fidelity defect, and it mattered because that number is what sizes the rename campaign. refs_by_module publishes a module's free references deduped, so the population is (name, referring module) pairs, not occurrences. The pair grain is the right one to report (a fix is written per referring module and repairs every occurrence inside it), so the key is renamed rather than the count changed: read_name_module_pairs, with the carrier's doc and the reporter's note both stating the grain and why it is not an occurrence count.

That commit is held locally for a few minutes rather than pushed: the floor run currently in flight on this PR is the census run this whole change exists to produce, and re-pushing for a label-and-doc change would spend another floor runner — the fleet's binding constraint — for numbers that do not move. It goes up as soon as that run's census is read.

On the hand-rust receipt: admitted under gunbc.v1_maintenance_standing v1_seed_standing because it serves the v2 self-host program — it is the census input to the layer-2 bare-name refusal for the namespace cut (layer 1 is #11065), and deletion-is-the-census requires the refusal to land with the fix for every site it would refuse. No new v1 capability, no budget move, resolution byte-identical. Nothing is deleted here because the deletion is the wall, which lands in the follow-up PR with the renames.

— sent from snappy-pike-265

Brian Searls and others added 2 commits September 11, 2026 21:25
…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
@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Finding confirmed and fixed in the pushed head — it was the right call, and the carrier's own documentation agrees with you: file_import_bindings' note says ResolvedFuncEnv.parents "does not separate a direct import from a transitively reachable module — a first-hit fold over it is the same silent pick one level out." That is precisely what my filter was doing, and it under-reported the wildcard-import residue the census exists to enumerate.

Fix as suggested: the question is now asked where it is answered. PreparedScopeIndexes::falls_through_to_shared_slot runs lookup_fn_from's first two tiers — the site file's own module's qualified declaration, then the module that file explicitly imported the name FROM — in its order, with its conditions, on the same index lookup_fn_from reads. The census consults it after the scope's indexes are built, and the wall this feeds will consume the same method rather than a copy. No second resolver.

Two related things in the same head, for context: the read population is now the VALUE-POSITION projection of the reference collector (a free ExprVar and a call's target) rather than the closure union, since the union folds in type annotations and record-literal type names — a wall keyed on it would refuse a type collision at a site where the shared value slot is never consulted. And the type-position double (String/Int/List/Bool/WireContract across std.* and v2.std.*) is now named in the receipt as the adjacent class this census deliberately does not cover, so a zero here can never be read as "no ambiguity in the corpus".

The run on the superseded head was cancelled rather than left to finish, since the fleet's runner pool is the binding constraint.

— sent from snappy-pike-265

Brian Searls and others added 3 commits September 11, 2026 22:26
…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
@briansrls
briansrls merged commit f4b63e3 into main Sep 12, 2026
4 checks passed
@briansrls
briansrls deleted the session/snappy-pike-265 branch September 12, 2026 03:15
@briansrls
briansrls restored the session/snappy-pike-265 branch September 12, 2026 03:16
briansrls pushed a commit that referenced this pull request Sep 12, 2026
…11137)

* Qualify the extdeps.tools bare-name reads by their declaring module

23 of the 105 value-position reads the bare-name-ambiguity census names
(`[floor-bare-name-ambiguity-read]`, landed #11078) sit under
`extdeps.tools`. Each was resolved by whichever claimant scope precedence
happened to rank first; this binds each to the authority its own module
means, through the file's own import, so the read no longer falls through
the shared name slot.

Three names, three declaring authorities, each named rather than inherited:

- `String` is declared by `std.string_type`, NOT by `std.types`. Ten of
  these files carried `import std.types { String }`, which answers
  nothing -- `std.types` neither declares `String` nor imports it, so the
  name was travelling through the shared slot while the import line
  implied otherwise. The bogus name is dropped from the `std.types` list
  and `import std.string_type { String }` added, so the citation now
  names the declaration (§3: cite the symbol, not a re-exporter).
- `Unit` IS declared by `std.types` (`type Unit`), so it joins the
  existing list. This is the form the proof site landed in #11078 already
  demonstrates: `extdeps.tools.hostname` reads
  `[floor-bare-name-ambiguity-bound] name=Unit resolves_to=std.types`.
- `Filesystem` at `extdeps.tools.sha256sum` is the one judgment site in
  this batch and it is not close: the call is `Filesystem.Write(path:,
  content:)`, matching `service Filesystem { operation Write }` in
  `extdeps.filesystem.filesystem_io`. The other claimant, `std.resources`,
  declares a `resource Filesystem` whose capability is lowercase `write`
  and which has no `Write` operation at all. The file already imported
  `extdeps.filesystem.filesystem_io` bare; it now names `{ Filesystem }`,
  which is the form 82 other files in the corpus already use.

No renames, and neither declaring side is touched -- this is the
qualification mechanism 305 sites corpus-wide already resolve through,
applied to the residue that never got it.

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

* Admit the one namespace delta the sha256sum qualification produces

The required floor on this branch is CLEAN -- `verdict=FloorClean
unexpected_failures=0` over planned=3757 executed=3757 claims_failed=0 --
and the census moved exactly as the change intended:
`read_name_module_pairs` 105 -> 82, `qualified_name_module_pairs`
305 -> 334, with all 23 `[floor-bare-name-ambiguity-bound]` rows naming
the authority this PR chose (10 `String` -> std.string_type, 12 `Unit` ->
std.types, 1 `Filesystem` -> extdeps.filesystem.filesystem_io).

What blocked the lane was a different phase: `FAILED PHASE
namespace-wave-admission (1 unadjudicated delta)`.

THE DELTA, AND IT IS A REAL ONE -- a side effect on a name this PR never
set out to touch:

  TargetChanged binding
  extdeps.tools.sha256sum::extdeps_external_authority_anchor
  base {extdeps.filesystem.filesystem_io, extdeps.shell,
        extdeps.tools.sha256sum}
  head {extdeps.shell, extdeps.tools.sha256sum}

`extdeps.tools.sha256sum` imported `extdeps.filesystem.filesystem_io`
BARE. A bare module import drags the whole module into the candidate set
for every name it declares, so filesystem_io's copy of
`extdeps_external_authority_anchor` -- the per-module convention row some
315 modules each author -- was a candidate at this site. Naming
`{ Filesystem }` binds the one declaration the author meant and drops the
rest, which narrows the candidate set for that unrelated spelling too.

NO RESOLUTION CHANGES: sha256sum authors its own
`extdeps_external_authority_anchor`, and a module's own declaration wins
inside the authored region, so the site resolved to sha256sum's row
before and resolves to sha256sum's row after. The removed candidate could
not have won either way. What moved is the SET, three modules to two --
the resolution stopped depending on a module the author never named,
which is the qualification's whole purpose. That is admitted, not
silenced: the row states it out loud, which is what this wall exists for.

The ten `extdeps.tools.* -> std.string_type` membership additions needed
no row: the phase classified each `ExplicitlyEvaluatedZeroDelta ...
reached by a name this module authors`.

ALSO PAID HERE: the three `gunbc#11071 LinuxKernelRelease rehome` rows are
deleted. #11071 merged, so the base already binds those spellings to
`extdeps.linux.kernel` and the floor reported all three CONSUMED. A
consumed row's deletion comes due on this roster's OWN next touch; this
change is that touch, so the debt is paid rather than inherited by an
unrelated lane.

NOTE FOR THE LATER BATCHES: the Filesystem/Clock batch converts fourteen
more bare imports to named ones, so it should be expected to produce this
same class of delta per module rather than treated as a surprise.

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

* Address review 64326: consistent String citation, and name the instrument

Three of the four findings were right and are fixed here.

TRANSCRIBED INSTRUMENT OUTPUT (valid). The roster doc carried
`verdict=FloorClean unexpected_failures=0` and the planned/executed/
claims_failed counts as prose. DESIGN §6 is explicit that a measurement
is cited by naming the producer that re-derives it, never by copying its
numbers, because a transcribed number rots without anyone touching either
end. The counts are gone; the claim now names the `claim_executor`
invocation and the verdict line to read it off.

TWO FILES LEFT ON THE OTHER SPELLING (valid). `extdeps.tools.jq` and
`extdeps.tools.sha512sum` were edited on exactly the line that carries
`String` and kept it on `std.types`, which left them inconsistent with
their nine siblings after a change whose stated purpose is to qualify
reads by their declaring module. They were excluded because the census
does not list them -- their reads are settled inside the authored region
-- but "not in the measured population" is a reason to not CHASE a site,
not a reason to leave a wrong citation on a line already being edited.
Both now name `std.string_type`. All thirteen touched files are
consistent: none carries `String` on a `std.types` import.

THE ROSTER DID NOT SPEAK TO THE `String` REQUALIFICATION (valid as a gap
in the writing). The doc said "NO RESOLUTION CHANGES" about the sha256sum
anchor and a reader could take it as covering the whole diff. It now
states why `String` needs no row, and states it as the wave phase's
measurement rather than as this author's assertion.

THE FOURTH FINDING IS NOT ACCURATE and nothing is changed for it. It
reads the `String` requalification as retargeting the spelling to "a
second authority" and forking it across nine modules. `std.types`
declares no `String` -- the finding says so itself -- so it was never an
authority for that name, and `import std.types { String }` bound nothing.
The two real claimants are `std.string_type` and `v2.std.text`, which the
census names as the self-host migration double and explicitly scopes out.
Naming one of the two existing declarations creates no third.

That it is measured rather than argued is the part that matters: the
wave phase compares the binding table on both sides, and on this head it
reported exactly ONE `TargetChanged binding` delta over 814,621 binding
rows compared -- the sha256sum anchor -- and NONE for `String`. The ten
`String` sites are reported as `ExplicitlyEvaluatedZeroDelta membership
... reached by a name this module authors`: evaluated, zero. So the read
resolved to `std.string_type` before and after; what changed is its
authorization, from an accident of scope precedence to something the
author wrote.

The finding's live observation -- that ~1951 modules carry
`import std.types { String }` -- is real and is a corpus-wide defect this
campaign does not fix: those lines name a module that does not declare
the name they list. They are harmless only while those modules' reads are
not ambiguous. Correcting them is its own change with its own
adjudication, not a drive-by in a PR scoped to 23 measured reads.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Sep 12, 2026
…le (#11145)

Second batch of the bare-name-ambiguity campaign (census instrument:
#11078, first batch: #11137). 57 of the 105 value-position reads the
census names, across 29 extdeps modules outside `extdeps.tools`.

Same two names and the same two authorities as the first batch:

- `String` is declared by `std.string_type`. Twenty-six of these files
  carried `import std.types { String }`, which binds nothing: `std.types`
  neither declares `String` nor imports it, so the read was travelling
  through the shared slot while the import line implied it had been
  settled. The name is dropped from the `std.types` list and
  `import std.string_type { String }` added.
  Two files -- `extdeps.clock` and `extdeps.entropy` -- read `String`
  bare without importing it under any spelling at all, and simply gain
  the `std.string_type` line.
- `Unit` is declared by `std.types` (`type Unit`), so it joins the
  existing list, which is the form the #11078 proof site already shows
  binding to `resolves_to=std.types`.

`extdeps.github.pulls` reads only `String` and so takes only that half.

No renames, and neither declaring side is touched. Both this batch and
the first are the mechanism 305 sites corpus-wide already resolve
through, applied to the residue that never got it.


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

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Sep 12, 2026
…at are not (#11146)

Fourth and final qualification batch of the bare-name-ambiguity campaign
(census: #11078; batches one to three: #11137 and its successors). It is
two lines, because auditing the last seven sites the census names found
that only two of them are defects.

THE TWO THAT ARE. `gunbc.cli_services` reads `Unit` and `String` in the
shell-exit fold (`0 => Unit` / `nonzero => String "..."`), the same shape
the extdeps tool modules carry. `Unit` joins the `std.types` import that
declares it; `String` moves to `std.string_type`, which is the module
that actually declares it.

THE FIVE THAT ARE NOT, and why they must not be swept. Each is a bare
read of a VARIANT ARM, not of either claimant type:

  std.fidelity                 Unit  -> arm of its OWN `type TestClass
                                       = Unit | Hermetic | Integration`
  gunbc.live_deploy.unit_standing
                               Unit  -> arm of `SystemdUnitProperty` in
                                       `extdeps.systemd`, ALREADY
                                       imported by name; the site is
                                       `property: Unit`, the systemd
                                       `Unit=` property
  v2.test.lens_cost.valuation  Unit  -> arm of `TestgenLayer` in
  v2.test.manual.infer_ground_add     `v2.std.verification`, ALREADY
                                       imported by name; the site is
                                       `layer: Unit`
  v2.extdeps.languages.cpp     Int   -> arm of its OWN `type
                                       CppSignedIntegerSurfaceSpelling`;
                                       the site is `spelling: Int`

None of these means `std.types.Unit`, `v2.std.cardinality.Unit`, or
`v2.std.integer.Int`. Adding an import naming one of those authorities
would not qualify a read -- it would point a correctly-written site at a
declaration whose meaning is different, and it would do so while the
census reported the corpus getting cleaner.

WHY THE CENSUS COUNTS THEM. Its resolver is
`PreparedScopeIndexes::site_resolved_fn`, two tiers over `fn_nodes`:
the site's own module (`{module_path}.{name}`), then the file's import
binding (`{source_module}.{name}`). `fn_nodes` is populated from
`fragment.fn_entries`, and `fn_entries` is built by walking
`module.items` -- TOP-LEVEL items only. A variant arm is not a top-level
item, so no arm is ever reachable under either tier, and a bare read of
one is reported as falling through to the shared slot even when it is
locally declared or explicitly imported.

This is a bounded over-approximation in the instrument, five sites wide
at this revision, and it is load-bearing for the refusal this campaign is
building toward: that wall is specified to key on the SAME value-position
population this census reports, and these five are sites that must NOT
refuse. The instrument needs a third resolution tier -- variant arms of a
type the site's module declares or explicitly imports -- before a
refusal over this population is safe to land. That tier is not in this
PR: it is a change to the census's own resolver, it wants its own
discriminating controls, and it belongs with the wall rather than with a
batch of import lines.

With this batch the qualification stage is complete: 100 of the 105
reported reads are bound to the authority their own module means, and
the residual five are not qualification targets but the instrument's own
next correction.


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

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <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