Skip to content

A qualified path's first segment is a namespace root: reference-derived edges no longer bind it to a same-named declaration - #12190

Merged
gunbai-bot[bot] merged 1 commit into
mainfrom
session/stern-lynx-90-namespace-root
Sep 24, 2026
Merged

gunbai-bot[bot] merged 1 commit into
mainfrom
session/stern-lynx-90-namespace-root

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

A qualified path's first segment is a namespace root, never a bare reference

Defect. test.claim.secret_rotation_witness declares fn v2(). Every import-free production module reading a v2.std.* path (among them src/v2/std/artifact, src/v2/std/grounding and src/v2/std/cross_tree/resolution) got a reference-derived dependency edge into that test module. The edge came from dependency_resolution_facts_live, via the host reference_resolution_facts. Found by the Pkg7e plan (v2.workflow.emitted_crate_workspace, on its own branch): its two-closure join refused with ClosuresDisagree. The live DAG of v2.compiler.compile had 214 modules against the seed emission's 190, and the extra 24 included that test claim.

Earliest unjustified link (DESIGN §6b).

  • v1_compiler.cli_run ExprVarClassification::classify counted a dotted chain's head as a namespace segment only when no declaration anywhere shared its spelling.
  • reference_resolution_facts runs with no declaration index (decl_index: None), so that arm could never fire. Every chain head therefore fell to the free-reference default, went into bare, and resolved UniqueBare to the homonym.
  • The dotted scan had already recorded the chain whole and resolved it Qualified by longest_declared_module_prefix. The head's role was a fact the producer held and did not consult.

Repair, at that link.

  • ExprVarClassification gains the declared module set (module_names). Both production callers already hold it; the binder fixtures pass None and keep their behaviour.
  • When a chain whose prefix names a declared module is recorded, its head node is marked. A marked head that no binder holds is classified QualifiedChainHead, the member it always belonged to, so no bare edge is emitted.
  • A binder still outranks it. A dotted read whose head names no module, such as a value row's .field, keeps its edge.
  • This is a classification fix, not a filter over edges afterwards.

Evidence, by execution.

  • test.claim.namespace_root_reference_edge_witness runs the real producer over fixtures/namespace_root_homonym. That pool is outside the corpus roots, so the homonym never enters the live pool. In it, nsroot_decl.homonym declares fn nsroot() beside the nsroot.pkg.* modules.
    • RED a_qualified_path_head_never_resolves_to_a_same_named_declaration: FAIL on the unrepaired binary, PASS after.
    • Control the_qualified_target_and_a_value_chain_keep_their_edges: PASS on both. The qualified edge and a value-chain edge survive.
  • Real closure. emitted_compiler_closure_module_dag now derives 190 modules (it was 214) and no longer contains test.claim.secret_rotation_witness; the seed emits 190. That is count equality only. The identity-grain join is the Pkg7e plan's ClosuresDisagree check, which runs when 7e is un-held on top of this.
  • Neighbouring witnesses, all PASS: module_graph_edge_source_witness (5/5), emitted_closure_crate_partition_test (4/4), bare_name_ambiguity_wall_witness_test (8/8).
  • Failure mode rostered: gunbc.recurring_failure_mode a_namespace_root_segment_resolves_as_a_bare_reference, with its ceiling and the namespace-cut terminal step as the next-rung trigger.

Consistency with the namespace-cut plan. docs/plans/namespace-cut-replacement-plan.md repoints dependency_resolution_facts_live to the reference-derived producer and makes parse-then-derive the terminal mechanism. This keeps that producer honest in the meantime, and it adds no second edge producer. No open PR touches entry_resolve.rs or v2.lens.module_graph.

v1 admission. This changes hand host Rust (cli_run.rs, cli_run/entry_resolve.rs). It is admitted as v2 self-host work under gunbc.v1_maintenance_standing v1_seed_standing: the dependency facts decide the emitted compiler's crate partition. Receipt:

  • Hand-item delta: +1 fn (ref_field_chain_head) and +2 fields on an existing struct.
  • classify and collect_node_refs_inner are modified, not added.
  • No emitted mirror changes, and no pub mod is added.

🤖 Generated with Claude Code

…erived producer no longer resolves it to a same-named declaration

ExprVarClassification classify treated a dotted chain's head as a namespace segment only when no
declaration anywhere shared its spelling, and reference_resolution_facts runs with no declaration
index, so the head of every v2.std.* path fell to the free-reference default and resolved
UniqueBare to test.claim.secret_rotation_witness fn v2(). A head whose chain names a declared
module is now QualifiedChainHead; binders still outrank it and value chains keep their edges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit c82f6c5 Sep 24, 2026
5 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/stern-lynx-90-namespace-root branch September 24, 2026 05:38
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.

0 participants