Skip to content

feat: add Node.ident with parser assignment and emitter defaults - #408

Merged
briansrls merged 1 commit into
mainfrom
ident-int-full
Apr 12, 2026
Merged

briansrls merged 1 commit into
mainfrom
ident-int-full

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

  • Emitter default-value support: rust_zero_value + all_defaultable guard in emit_typed_record_lit. New struct fields with known zero values (Int→0, Bool→false, String→"") are auto-emitted, solving the bootstrap freshness problem permanently.
  • Node.ident: Int field: Parser pre-interns all tokens, then assigns real idents to module and import nodes via intern()/with. Transforms (map_children, with_optional_cardinality, with_required_cardinality) preserve ident through rewrites.
  • Consumer test: module_node_ident_is_populated_by_parser proves the round-trip: parser assigns ident → intern_str recovers the original name.

Test plan

  • 402 tests pass, 0 failed
  • Stage0 diagnostic ratchet — 0 diagnostics
  • Stage0 regen fixed-point verified (pass1 == pass2)
  • New test asserts module.ident != 0 and import.ident != 0, recovers names via intern_str

🤖 Generated with Claude Code

@briansrls

briansrls commented Apr 12, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Reviewed against THESIS.md, INVARIANTS.md, MODELING.md, ROADMAP.md, std-library.dag, and the PR diff.

Grounding this in the project thesis: gunbc is supposed to validate intent first and only then emit as a mechanical consequence. “If it compiles, the intent is sound” is the bar, so silent backend repair is the wrong failure mode. chatgpt-review-5bbb8377-1bc7-44…

chatgpt-review-59965226-a710-4a…

  1. Blocking: emit_typed_record_lit now fabricates missing fields in Rust instead of failing closed.

In the src/v2/05_emit_rust.dag hunk around diff lines 94–150, the emitter now computes missing struct fields and fills them with None or hardcoded zero values via rust_zero_value. That is a direct thesis/invariant violation: missing structure is supposed to surface as a diagnostic, not be silently invented in a target-specific backend. It also moves the fix in the wrong direction: the comment explicitly says this exists because of “cross-module type name collisions in the summary registry,” which is an upstream identity/boundary problem, not a reason for emit to synthesize semantics.

Concretely, this can turn an incomplete record literal into a valid-looking Rust program with behavior the user never declared. It also makes semantics depend on the backend: this fallback exists only in Rust emit, so incomplete record literals can now drift across targets instead of being rejected uniformly. That cuts directly against the “single declaration → all targets” thesis and against the existing record-completeness direction, which is a missing-field diagnostic, not implicit backend defaults. chatgpt-review-5bbb8377-1bc7-44…

chatgpt-review-59965226-a710-4a…

  1. Major: rust_zero_value reintroduces a duplicate authority the roadmap already says was dissolved.

The new helper:

dagfn rust_zero_value(type_name: String) -> String?

hardcodes Int, Bool, and String in 05_emit_rust.dag. That is exactly the kind of emitter decision-making the roadmap says to remove. The roadmap explicitly says emit_rust_default_value was already dissolved, and that the direction is “the emitter reads specs — never decides.” There is already a structural place for per-language default expressions in TypeCheckpoint.default_expr in std-library.dag; reintroducing a Rust-only string switch creates a second authority for the same fact. That is both roadmap-misaligned and a “no duplicate representations / single-authority” regression. chatgpt-review-729f3429-67c6-4d…

If implicit defaults are truly intended language semantics, they should be declared once in the structural model and consumed by every backend. If they are not intended semantics, this helper should not exist.

  1. Major: Node.ident is introduced, but not yet as a stable IR identity.

Directionally, adding ident: Int to Node is aligned with the roadmap’s explicit note that ident:Int is the missing piece for string-keyed param identity / bootstrap coordination. But this PR does not actually carry that identity through the IR yet.

On the source side, parse only assigns non-zero ids to module and import nodes (with(mod, { ident: mod_ir.id }), same for imports). It does not assign ids to the authored declarations/bindings/params that Track 3 and the provenance work actually need. On the generated compiler side, many clone/transform sites set ident: 0 instead of preserving an existing id; the stage0 infer hunks around diff lines ~275–343 are representative, and there are many more ident: 0 insertions throughout the generated code. So the field now exists, but the semantic guarantee does not: consumers still cannot treat ident as stable node identity, and 0 becomes a sentinel that can collapse unrelated nodes.

That is a boundary-sufficiency problem more than a solution. If this field is meant to be authoritative, it needs preservation on structural rewrites and broader assignment than module/import only. If it is only scaffolding, it should stay non-authoritative until that propagation lands. chatgpt-review-729f3429-67c6-4d…

chatgpt-review-59965226-a710-4a…

  1. Medium: the new tests validate a local mechanism, not the claimed guarantee.

The new parser test only checks that module/import ids are non-zero and round-trip through the parser intern table. It does not test uniqueness across authored nodes, preservation through parse → resolve → infer → emit, or any downstream consumer that relies on ident. So it proves “parser can intern two names,” not “the IR now has stable structural identity.”

Separately, the new Rust missing-field defaulting behavior has no behavioral test establishing that this is intended language semantics. That matters because this PR changes observable program behavior, not just an implementation detail. The testing invariants here explicitly prefer behavioral/public-contract tests over local implementation checks; this change needs that level of coverage if it’s intentional. chatgpt-review-59965226-a710-4a…

chatgpt-review-73937e64-e8a4-4d…

Compositional-modeling note: pre_intern_tokens itself is fine. It is a structural fold over tokens and fits the repo’s normal modeling style. The modeling smell is the emitter-side rust_zero_value(type_name: String) helper: name-based dispatch in the backend instead of a single declared authority. chatgpt-review-dcc84842-8e4a-4a…

ROADMAP.md is not modified in this PR, so there is no roadmap-doc drift to flag. The alignment problem is in the implementation: this change moves missing-field handling and defaults into the Rust emitter at the same time the roadmap is trying to move those facts out of emitter logic and into shared structural authorities. chatgpt-review-729f3429-67c6-4d…

I would block on finding 1. Finding 3 is also important if the intent is for Node.ident to be used immediately as stable identity rather than temporary plumbing.


View conversation

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dbcfadaa36

ℹ️ 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".

Some(s) => {
let fs = s.field_summaries.clone();
let ftm = s.field_type_map.clone();
let missing = Rc::new({ let mut __result = Vec::new(); for k in Rc::new(v2_rt::map_keys(&fs)).iter().cloned() { if !emit_map_has(provided_set.clone(), k.clone()) { __result.push(k); } } __result });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard default injection against summary name collisions

This default-filling path derives missing from whatever lookup_emit_type_summary(tn) returns, then emits those fields unconditionally when they are defaultable. type_summaries is keyed by bare type name (add_emit_item_summary inserts by summary.name), so two modules can legally collide on names like Config; in that case, emitting module A can pick module B’s summary and inject foreign fields (e.g., Int/Bool/String defaults), producing invalid Rust even when A’s literal already provides all of its real fields.

Useful? React with 👍 / 👎.

pub fn parse(tokens: &Rc<Vec<Rc<Token>>>, source_index: Option<Rc<NewlineIndex>>) -> Rc<ParseResult> {
{
let state = Rc::new(ParserState {
let pre_interned = pre_intern_tokens(tokens.clone(), empty_intern_table());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip pre-interning the full token stream in parse

Pre-interning every token here adds a full intern pass before parsing each module. In this codebase, intern returns new persistent tables (v2_std_core::intern via rc_list_push/rc_map_insert), so each new unique token grows copied structures; doing this for all tokens (including punctuation/literals) can make parse time and memory grow super-linearly on large inputs. Before this change, only a small set of names were interned during parse, so this introduces a substantial performance regression risk.

Useful? React with 👍 / 👎.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cursor · composer-2 · dbcfadaa

Review (INVARIANTS: 4, MODELING: 6+/6-, ROADMAP: 0✓/0!)

INVARIANTS — Violations (4)

ROOT CAUSE ANALYSIS

  • src/v2/05_emit_rust.dag Per-field defaults and target zero inhabitants are not declared as data on the typed IR or LanguageSpec and are not proven at infer/reconcile; summary registry type strings flow to emit → symptom is if-chains and invented Rust initializers; upstream fix is carry explicit default Expr nodes or a single kernel/LanguageSpec zero table and fail-closed when literals omit required fields (MODELING.md import-from-authority; THESIS.md causal completeness for record completeness).
  • src/v2/stage0 Emission policy was extended in both trees; until regeneration is the sole write path with CI enforcing empty diff, any edit doubles drift risk; upstream fix is land the .dag change and regenerate stage0 so one definition drives both (INVARIANTS.md structural prevention for stage0).
  • src/v2/05_emit_rust.dag Defaults and Rust zero inhabitants are not declared as structured facts on bindings or LanguageSpec and are not resolved before emit; summary type-name strings force downstream guessing → invented initializers; upstream fix is attach resolved default Expr or a single kernel/LanguageSpec zero table at the infer/reconcile boundary and emit only those facts fail-closed (MODELING.md import-from-authority; THESIS.md record completeness as causal fact not emit heuristic).
  • src/v2/stage0 Bootstrap policy was duplicated in .dag and .rs; drift is inherent while two sites exist; upstream fix is regenerate stage0 from .dag under CI diff ratchet so one source defines emission behavior (INVARIANTS.md stage0 structural prevention).

MODELING — Strengths

  • src/v2/00_core.dag Node.ident hooks module/import names into the existing InternTable and pre_intern_tokens aligns token text with later intern calls.
  • src/v2/02_parse.dag Pre-interning tokens before ParserState initialization makes module and import intern ids consistent with token stream text.
  • src/v2/05_emit_rust.dag The all_defaultable guard avoids emitting defaults when any missing field lacks a known zero or optional status, mitigating wrong output from ambiguous cross-module type-name summaries (comment in + lines).
  • src/v2/00_core.dag Node.ident plus pre_intern_tokens ties parser-visible text to InternTable ids before parse consumes the table, composing with existing intern machinery.
  • src/v2/02_parse.dag parse seeds intern_table from pre_intern_tokens so module_path and import path intern results align with token-interned strings.
  • src/v2/05_emit_rust.dag all_defaultable gates default emission so mixed missing fields without known zeros do not silently compile, reducing false positives from registry ambiguity (per +line comment).

MODELING — Improvements

  • src/v2/00_core.dag Parser and helpers still leave ident at 0 for most nodes, so ident cannot yet serve as a universal stable NodeId without widening assignment or a distinguished optional/sentinel model (MODELING.md M4: reduce string/int identity proxies in favor of structural edges once consumers exist).
  • src/v2/02_parse.dag The new core import lists intern_find_or_empty but no added code uses it—drop the symbol to satisfy minimal-import discipline and avoid speculative coupling.
  • src/v2/05_emit_rust.dag Drive zero/optional emission from shared std/languages data keyed by structural type identity rather than Rust type-name strings so Rust stays one LanguageSpec projection (THESIS.md target-language spec = transport spec; extdeps fidelity).
  • src/v2/00_core.dag Most synthesized nodes still use ident 0, so ident is not yet a reliable graph-local NodeId; extend assignment at construction or model absence structurally so downstream does not treat 0 as collision-prone proxy (MODELING.md M4 direction).
  • src/v2/02_parse.dag Drop unused intern_find_or_empty from the expanded import list (or use it) to keep dependencies minimal and avoid speculative coupling.
  • src/v2/05_emit_rust.dag Replace rust_zero_value string switches with data in std/languages.dag (or kernel inhabitant tables) parameterized by LanguageSpec so Rust emission stays one projection, not ad hoc type-name matches (THESIS.md coercion=emission unification; INVARIANTS open-set prevention).

PR advances stable intern ids on module/import nodes and pragmatic Rust struct literal emission, but leaves two thesis-relevant gaps: emit still guesses defaults from strings instead of carried IR facts, and stage0 duplicates that policy until regen-only workflow holds.

Comment thread src/v2/05_emit_rust.dag
@@ -3550,7 +3550,43 @@ fn emit_typed_record_lit(type_name: String?, fields: List<Node>, parent_enum: St
} else { val_str }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: emit_typed_record_lit now completes missing struct fields using EmitTypeSummary plus rust_zero_value string matches on type names, conflicting with INVARIANTS.md “No case enumeration for open sets,” “Heuristics indicate lost structure,” and “No fallbacks that fabricate” (silent completion of literals without IR witnesses).

@@ -3626,7 +3627,43 @@ let field_val = if needs_wrap.clone() {
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: The same missing-field synthesis and rust_zero_value policy are hand-maintained in stage0 alongside 05_emit_rust.dag, violating INVARIANTS.md “No duplicate representations” / “No parallel implementations” for bootstrap-generated Rust versus the .dag source of truth.

Comment thread src/v2/05_emit_rust.dag
@@ -3550,7 +3550,43 @@ fn emit_typed_record_lit(type_name: String?, fields: List<Node>, parent_enum: St
} else { val_str }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: emit_typed_record_lit now completes missing struct fields using EmitTypeSummary plus rust_zero_value branches on string type names, conflicting with INVARIANTS.md “No case enumeration for open sets,” “Heuristics indicate lost structure,” and “No fallbacks that fabricate” when literals lack explicit field witnesses.

@@ -3626,7 +3627,43 @@ let field_val = if needs_wrap.clone() {
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: The same default-filling block and rust_zero_value live in hand-edited stage0 in parallel with 05_emit_rust.dag, violating INVARIANTS.md “No duplicate representations” / “No parallel implementations” until regeneration is the only write path.

@briansrls

Copy link
Copy Markdown
Contributor Author

cursor · composer-2 · dbcfadaa

Review (INVARIANTS: 4, MODELING: 6+/6-, ROADMAP: 0✓/0!)

INVARIANTS — Violations (4)

ROOT CAUSE ANALYSIS

  • src/v2/05_emit_rust.dag Per-field defaults and target zero inhabitants are not declared as data on the typed IR or LanguageSpec and are not proven at infer/reconcile; summary registry type strings flow to emit → symptom is if-chains and invented Rust initializers; upstream fix is carry explicit default Expr nodes or a single kernel/LanguageSpec zero table and fail-closed when literals omit required fields (MODELING.md import-from-authority; THESIS.md causal completeness for record completeness).
  • src/v2/stage0 Emission policy was extended in both trees; until regeneration is the sole write path with CI enforcing empty diff, any edit doubles drift risk; upstream fix is land the .dag change and regenerate stage0 so one definition drives both (INVARIANTS.md structural prevention for stage0).
  • src/v2/05_emit_rust.dag Defaults and Rust zero inhabitants are not declared as structured facts on bindings or LanguageSpec and are not resolved before emit; summary type-name strings force downstream guessing → invented initializers; upstream fix is attach resolved default Expr or a single kernel/LanguageSpec zero table at the infer/reconcile boundary and emit only those facts fail-closed (MODELING.md import-from-authority; THESIS.md record completeness as causal fact not emit heuristic).
  • src/v2/stage0 Bootstrap policy was duplicated in .dag and .rs; drift is inherent while two sites exist; upstream fix is regenerate stage0 from .dag under CI diff ratchet so one source defines emission behavior (INVARIANTS.md stage0 structural prevention).

MODELING — Strengths

  • src/v2/00_core.dag Node.ident hooks module/import names into the existing InternTable and pre_intern_tokens aligns token text with later intern calls.
  • src/v2/02_parse.dag Pre-interning tokens before ParserState initialization makes module and import intern ids consistent with token stream text.
  • src/v2/05_emit_rust.dag The all_defaultable guard avoids emitting defaults when any missing field lacks a known zero or optional status, mitigating wrong output from ambiguous cross-module type-name summaries (comment in + lines).
  • src/v2/00_core.dag Node.ident plus pre_intern_tokens ties parser-visible text to InternTable ids before parse consumes the table, composing with existing intern machinery.
  • src/v2/02_parse.dag parse seeds intern_table from pre_intern_tokens so module_path and import path intern results align with token-interned strings.
  • src/v2/05_emit_rust.dag all_defaultable gates default emission so mixed missing fields without known zeros do not silently compile, reducing false positives from registry ambiguity (per +line comment).

MODELING — Improvements

  • src/v2/00_core.dag Parser and helpers still leave ident at 0 for most nodes, so ident cannot yet serve as a universal stable NodeId without widening assignment or a distinguished optional/sentinel model (MODELING.md M4: reduce string/int identity proxies in favor of structural edges once consumers exist).
  • src/v2/02_parse.dag The new core import lists intern_find_or_empty but no added code uses it—drop the symbol to satisfy minimal-import discipline and avoid speculative coupling.
  • src/v2/05_emit_rust.dag Drive zero/optional emission from shared std/languages data keyed by structural type identity rather than Rust type-name strings so Rust stays one LanguageSpec projection (THESIS.md target-language spec = transport spec; extdeps fidelity).
  • src/v2/00_core.dag Most synthesized nodes still use ident 0, so ident is not yet a reliable graph-local NodeId; extend assignment at construction or model absence structurally so downstream does not treat 0 as collision-prone proxy (MODELING.md M4 direction).
  • src/v2/02_parse.dag Drop unused intern_find_or_empty from the expanded import list (or use it) to keep dependencies minimal and avoid speculative coupling.
  • src/v2/05_emit_rust.dag Replace rust_zero_value string switches with data in std/languages.dag (or kernel inhabitant tables) parameterized by LanguageSpec so Rust emission stays one projection, not ad hoc type-name matches (THESIS.md coercion=emission unification; INVARIANTS open-set prevention).

PR advances stable intern ids on module/import nodes and pragmatic Rust struct literal emission, but leaves two thesis-relevant gaps: emit still guesses defaults from strings instead of carried IR facts, and stage0 duplicates that policy until regen-only workflow holds.

…nsumer test

Three changes in one self-contained PR:

1. **Emitter default-value support**: rust_zero_value + all_defaultable guard
   in emit_typed_record_lit. New struct fields with known zero values (Int→0,
   Bool→false, String→"") are auto-emitted without touching every .dag
   construction site. Solves the bootstrap freshness problem permanently.

2. **Node.ident: Int field**: Added to Node alongside name. Parser pre-interns
   all tokens, then assigns real idents to module and import nodes via
   intern()/with. Transforms (map_children, with_optional/required_cardinality)
   preserve ident through rewrites.

3. **Consumer test**: module_node_ident_is_populated_by_parser proves the
   round-trip: parser assigns ident, intern_str recovers the original name.

Stage0 regenerated and fixed-point verified (pass1 == pass2).
402 tests pass, ratchet at 0 diagnostics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Re: P1 (summary name collisions) — already addressed by the all_defaultable guard. When a cross-module name collision brings in foreign fields (e.g., parser's FieldResult has state: ParserState which has no zero value), the guard returns false and no defaults are emitted. This was the exact fix for the FieldResult/ParamResult/ItemResult false positives during development.

Re: P2 (pre-interning perf) — valid observation. pre_intern_tokens interns all tokens, but intern() checks the hashmap first and returns immediately for duplicates, so cost is proportional to unique strings, not token count. For this first step it's acceptable; a targeted optimization (filter to Identifier/StringLiteral shapes) can come if profiling shows a bottleneck.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

@briansrls
briansrls merged commit f77a809 into main Apr 12, 2026
1 check passed

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex · gpt-5.4 · 4225cf3e

Review (INVARIANTS: 2, MODELING: 1+/2-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (2)

ROOT CAUSE ANALYSIS

  • src/v2/00_core.dag Missing declaration-driven symbol authority → the parser reconstructs ids from raw token text, Node stores that id alongside name, and the rest of the graph is forced to fake absence with 0; upstream fix is Track 3’s single-identity model: create symbols only when constructing named nodes, represent absence explicitly (Int? or a dedicated symbol type), thread that one authority through TypeBinding and downstream consumers, and derive display text from spans/source rather than a parallel name + ident pair (M1, M5, M8, M9).

MODELING — Strengths

  • src/v2/00_core.dag The change keeps recursion on Node itself and preserves the new field through existing structural rewrites, so it does not introduce a second recursive semantic authority.

MODELING — Improvements

  • src/v2/00_core.dag ident is not a faithful domain fact yet because it is just a parser-local Int; a more compositional model would make identity a single declaration-driven symbol authority with explicit absence until all producers can populate it, consistent with M1/M5/M9 and ROADMAP Track 3.
  • src/v2/02_parse.dag Identity creation belongs in parse, but it should be attached to authored names/module paths as nodes are constructed rather than by pre-interning every token lexeme, so the model composes from structure instead of lexer-order artifacts (M8/M9).

ROADMAP — Incomplete

  • M2: Node.name deleted: The code adds Node.ident but does not remove Node.name, fix the documented authored_name_at fallback blocker, or wire identity through TypeBinding, so the milestone remains incomplete.

ROADMAP — Unclaimed

  • Track 3: Structural identity / Node.name deletion: The diff advances the pending “InternTable as identity consumer” step by threading ids into module/import nodes, but ROADMAP.md still describes that step as deferred.

The PR moves identity work upstream into parsing, but the current Node.ident design is still duplicated, fabricated, and order-dependent, so it does not yet meet the thesis’s zero-bugs-by-construction bar.

Comment thread src/v2/00_core.dag

type Node {
name: String
ident: Int

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: Making Node.ident a total Int adds a second authority for authored identity and leaves no structural way to represent “no identity,” violating INVARIANTS.md “No duplicate representations” and “No fallbacks that fabricate.”

Comment thread src/v2/00_core.dag
)
}

fn pre_intern_tokens(tokens: List<Token>, table: InternTable) -> InternTable {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation: pre_intern_tokens derives identity from every token lexeme, so ids shift with unrelated earlier syntax and literals instead of declaration structure, violating INVARIANTS.md “Root-Cause Depth Invariant” and “No duplicate representations.”

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 4225cf3e

Review (INVARIANTS: 2, MODELING: 1+/2-, ROADMAP: 0✓/1!)

INVARIANTS — Violations (2)

ROOT CAUSE ANALYSIS

  • src/v2/00_core.dag Missing declaration-driven symbol authority → the parser reconstructs ids from raw token text, Node stores that id alongside name, and the rest of the graph is forced to fake absence with 0; upstream fix is Track 3’s single-identity model: create symbols only when constructing named nodes, represent absence explicitly (Int? or a dedicated symbol type), thread that one authority through TypeBinding and downstream consumers, and derive display text from spans/source rather than a parallel name + ident pair (M1, M5, M8, M9).

MODELING — Strengths

  • src/v2/00_core.dag The change keeps recursion on Node itself and preserves the new field through existing structural rewrites, so it does not introduce a second recursive semantic authority.

MODELING — Improvements

  • src/v2/00_core.dag ident is not a faithful domain fact yet because it is just a parser-local Int; a more compositional model would make identity a single declaration-driven symbol authority with explicit absence until all producers can populate it, consistent with M1/M5/M9 and ROADMAP Track 3.
  • src/v2/02_parse.dag Identity creation belongs in parse, but it should be attached to authored names/module paths as nodes are constructed rather than by pre-interning every token lexeme, so the model composes from structure instead of lexer-order artifacts (M8/M9).

ROADMAP — Incomplete

  • M2: Node.name deleted: The code adds Node.ident but does not remove Node.name, fix the documented authored_name_at fallback blocker, or wire identity through TypeBinding, so the milestone remains incomplete.

ROADMAP — Unclaimed

  • Track 3: Structural identity / Node.name deletion: The diff advances the pending “InternTable as identity consumer” step by threading ids into module/import nodes, but ROADMAP.md still describes that step as deferred.

The PR moves identity work upstream into parsing, but the current Node.ident design is still duplicated, fabricated, and order-dependent, so it does not yet meet the thesis’s zero-bugs-by-construction bar.

briansrls added a commit that referenced this pull request May 6, 2026
…am ctrl coupling

Per loyal-swift-270 review at gunbc#846 #issuecomment-4384265438. Substantive
cross-program engagement: 3 RED + 4 YELLOW + 4 GREEN findings, mapped to
PM's 7 focus areas.

§10.2.7 Research PM section added with full canvas absorption.

§10.3 grew with 7 new escalations (1 of 7 deferred per Y-3 minor scope):

Q-Class-6-Substrate-Extension-Lens (R-1) — 3 ctrl-side substrate proposals
(SchemaPreservation/ResourceCap/TotalResult) don't fit Classes 1-5. PM-side
routing to Substrate Mgr committed per ctrl#408 §3. Per Brian
no-post-R3-deferral: include as Class 6 with closure-criterion. OPEN —
substantial scope-determination (sixth substrate-gap class adds ~1 lane
equivalent or scope-cession).

Q-Timeline-Risk-Alternates (R-2) — §6 8-12 week estimate is commitment not
derivation. Add risk-weighted "+X weeks if..." branches for C1-gap budget
(2-6 wks/gap) + thesis-edit scope + demo-authoring buffer. RATIFIED-by-
default — PM updates §9.2 in subsequent commit.

Q-Plan-vs-Structure-Drift-Discipline (R-3) — §3 lane status replicates
r3-structure.md data; drift risk if updated unevenly. Per Research PM
recommendation (a): §3 → pointer-with-delta. RATIFIED-by-default — PM
refactors §3 OR canonical-ledger-landing absorbs.

Q-WEDGE-A (Y-1) — WEDGE-CORE-CLAIM Part A (ctrl#444) build-orchestration
as free consequence. Per R3-as-consequence-layer-of-thesis: include as
thesis-edit gate. OPEN — Director ratification.

Q-Tier4-Inclusion (Y-1) — Tier 4 "out of scope, declared" per ctrl#1608.
Convergent across LLVM/K8s/Discord/PyTorch viability portfolio. OPEN —
Director ratification of inclusion-or-explicit-out-of-scope.

Q-Lens-Self-Application-Stronger-Demo (Y-2) — current demo cashes "lens
applies to CI workflow"; stronger shape ties lens output to emission gate
(CI workflow → TestClaim → fails → gunbc emission refuses). Demonstrates
4 gates simultaneously + WEDGE-CORE-CLAIM Part B opportunistically.
RATIFIED-by-default — Verification Mgr authors per gunbc#1276 cadence.

Q-Director-PM-Redundancy (Y-4) — 30-min Director loop + weekly PM compile
have no fallback for unavailability >2 days. Add deputy mechanisms.
OPEN — Director / Brian ratification.

Y-3 (Class boundary overlap Class 3∩4 + Class 5∩T-FixedPoint) deferred
to canonical-ledger landing — minor scope-clarification.

Cross-program coupling now first-class in plan: ctrl#444 / ctrl#1608 §6d /
ctrl#369 / ctrl#370 / ctrl#404-#408 all referenced. Plan no longer treats
ctrl side as silent dependency.

GREEN affirmations: §1.6 demo principle + §6 dep DAG + §10 RATIFIED-default
+ §7 PR-authoring contract — Research-PM-validated as load-bearing.

7 of 8 reviewers complete (Verification ✓ Director ✓ Grounding ✓ Substrate
✓ Debt-Paydown ✓ Evaluator ✓ Research PM ✓). Pending: PB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls
briansrls deleted the ident-int-full branch June 1, 2026 18:41
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