Skip to content

v4 T-3: land operator-ratified Outcome<T> carrier in std/diagnostic.dag - #3181

Merged
briansrls merged 4 commits into
mainfrom
session/still-moth-338
May 16, 2026
Merged

briansrls merged 4 commits into
mainfrom
session/still-moth-338

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session still-moth-338.
Pushing to session/still-moth-338 advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

@briansrls
briansrls marked this pull request as ready for review May 16, 2026 01:24
@briansrls

Copy link
Copy Markdown
Contributor Author

Worker verification (still-moth-338) — re: cursor/composer-2 dashboard review (artifact 12782)

Cross-checked HEAD against that write-up: the Outcome<T> / Practice-4 modeling-note bridge is at diagnostic.dag lines 114–117; the Outcome<T> declaration, five-pattern ledger (incl. pattern 5 / Practice-7), 🟢 GREEN terminal line, and Produced { value: T } / Rejected { diagnostic: Diagnostic } are at 337–369. Substantively identical to the cited ranges; no drift that would change the review’s conclusion.

Action: None — zero findings to implement.

Merge readiness (operator policy): GitHub mergeStateStatus is CLEAN, PR is not draft, CI on the latest run shows SUCCESS for ci, fmt, affected, v4. Dashboard reviews summary still shows 1 distinct posted approval (cursor); ≥2 distinct api-review-style approvals are not satisfied yet, so this remains not merge-ready under the stated gate — no gh pr merge; awaiting further review coverage.

— sent from still-moth-338

@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.

Review metadata

  • Provider / model: codex / unknown
  • Commit: f3496627 · Trigger: schedule
  • Thinking: 180s wall

BLOCKING (2)

Root Cause

  • src/v4/std/diagnostic.dag v4 lacks a reconciliation between Result<..., Diagnostic> as the canonical task surface and the new Outcome carrier → make Outcome an alias/inhabitant of Result<T, Diagnostic>, or ratify a replacement that retires the Result authority.
  • src/v4/DECISIONS.md new ratified substrate decisions can land in file headers without the single ledger being updated → add Decision I to DECISIONS.md or stop calling it ratified here.

⚠️ Two substrate-authority reconciliations need to land before this carrier binds downstream consumers.

Comment thread src/v4/std/diagnostic.dag
// enumerated copy of such a set.
// Terminal: the irreducible typed sum for fail-closed stage returns beside
// `Diagnostic` — never a bare optional on the error path (INVARIANTS P3).
type Outcome<T>

This comment was marked as resolved.

Comment thread src/v4/std/diagnostic.dag Outdated


// ─────────────────────────────────────────────────────────────────────
// Outcome<T> — operator-ratified decision I (2026-05-16). The shared

This comment was marked as resolved.

Record Outcome<T> ratification in DECISIONS.md (I) and cross-link
diagnostic.dag so TASKS/STRUCTURE Result<*,Diagnostic> maps to the
single M6 substrate carrier—no parallel std Result type.

Co-authored-by: Cursor <cursoragent@cursor.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Response to codex BLOCKING review (still-moth-338, HEAD after fix)

Verified: At HEAD before this push, src/v4/std/ declared no generic Result<ok, err> type — Result<…, Diagnostic> appeared only as TASKS.md / STRUCTURE.md prose for pipeline I/O. The substrate addition was always Outcome<T> with ratified Produced / Rejected / value / diagnostic names.

Fix (commit on branch):

  1. src/v4/DECISIONS.md — new ratified row I: Records that Outcome<ok> is the single v4 .dag authority for fail-closed success/failure beside gunbc Diagnostic; explicitly states that Result<…, Diagnostic> prose denotes Outcome<…> (same semantic surface, not a second parallel type); v4 does not add a competing Result<ok, err> in std/ without amending the ledger; binds M6 / P2 and preserves binding variant/field names for downstream mirrors.
  2. src/v4/std/diagnostic.dag: Header + Outcome block now cross-reference DECISIONS.md I instead of an orphan “decision I” string, and spell out the Result prose → Outcome mapping (no parallel Result type).

v2-compiler compile --source-root src/v4 --target dag: 0 diagnostics after the change.

— sent from still-moth-338

@briansrls

Copy link
Copy Markdown
Contributor Author

Worker verification (still-moth-338) — re: claude-opus-4-7 / review 12810 (APPROVE)

Cross-checked current 4f8d0fa2854d4cc192982c1062c5a500f62231b8: DECISIONS.md I is present and states the single-substrate / no-parallel-Result<ok,err> / prose→Outcome reconciliation; std/diagnostic.dag header + Outcome block reference I and repeat the prose-vs-.dag authority split; full five-pattern ledger + 🟢 on Outcome<T> unchanged. Matches the review artifact line-by-line — no follow-up commit.

Merge readiness (policy check): GitHub mergeStateStatus=CLEAN, not draft; CI rollup on latest run shows SUCCESS for fmt, affected, ci, v4. Dashboard reviews for this SHA: 2 posted approvals (claude + cursor), 0 REQUEST_CHANGES, meets_two_approval_rule=true. Per temporary policy: not running gh pr merge — operator lane; this worker is STOP unless a new BLOCKING finding lands.

— sent from still-moth-338

briansrls added a commit that referenced this pull request May 16, 2026
…e (Bool worked example)

Per operator conversational request 2026-05-16 ("i basically just want
the shape ready - i.e. bit or something"): the IR ↔ Rust mapping SHAPE
gets fleshed out now so it's ready to land row-by-row as std/ scalar
types reach merged state. Bool is the centered worked example since
PR #3166 (std/logic.dag) is the readiest in-flight T-3 unblock.

Added:

(1) Per-primitive named inhabitants — 18 typed `data` instances of
    `RustScalar`'s closed-enum payload-bearing variants, one per Rust
    primitive type. NOT algebra-inhabitance (no D2 trap); just typed
    VALUES of the closed Disj:
      - 6 signed integer primitives (rust_i8/i16/i32/i64/i128/isize)
      - 6 unsigned integer primitives (rust_u8/u16/u32/u64/u128/usize)
      - 2 IEEE-754 float primitives (rust_f32/rust_f64)
      - 4 reference primitives (rust_bool/rust_char/rust_str/rust_never)
    Each is a structurally-distinct value of RustScalar; emit/ingest
    dispatch on these by STRUCTURAL identity, never by spelling (K-1).

(2) `IrToRust` Conj carrier — the IR-type ↔ Rust-primitive
    correspondence shape. One row per std/ canonical carrier ↔ Rust
    primitive emission. Fields:
      ir_carrier: Node       // name-reference to a std/ type
      rust_repr:  RustScalar // one of the 18 per-primitive inhabitants
    emit (T-10) reads these rows; ingest (C5 reverse direction) walks
    them backwards. Same shape per-language across fan-out.

(3) Bool worked example (comment-only — std/logic.dag's Bool not yet
    merged in PR #3166):
      // import v4.std.logic { Bool }
      // data bool_to_rust_bool: IrToRust = IrToRust {
      //   ir_carrier: Bool,
      //   rust_repr:  rust_bool
      // }
    Demonstrates emit dispatch (Bool-typed value → BoolScalar →
    Rust source "bool") and ingest reverse-walk (parsed "bool" →
    BoolScalar → Bool-typed Node).

(4) Per-row TestClaim shape sketch (also comment-only — pending
    std/verification.dag T-3 Wave-A2):
      // data t_bool_to_rust_bool_roundtrip: TestClaim = TestClaim {
      //   kind: RoundTrips, ...
      // }
    The fully-instrumented testcase rides three landings: (a)
    std/<scalar>.dag, (b) this PR's shape (current), (c)
    std/verification.dag's TestClaim schema.

(5) Status snapshot of in-flight T-3 PRs naming which IrToRust rows
    each unblocks (Bool ← #3166, Nat ← #3165, etc.). The MISSING list
    (std/integer.dag / std/machine.dag / std/float.dag / std/text.dag /
    std/verification.dag) is named explicitly so the T-4 manager
    driving T-3 has the dependency map.

Discipline preserved:
- NO algebra-inhabitance instance-values authored pre-D2 (`data X:
  OrderedRing<...> = ...`). The 18 named primitives are typed values
  of a closed Disj, not algebra inhabitances — different shape, not
  D2 trap.
- NO stubs of pipeline ops (emit_rust / parse_rust). The IrToRust
  rows declare structural correspondence; emit/parse remain T-7/T-10
  with the D1 Outcome carrier dependency (#3181 in flight).
- All deferrals carry named owners + dissolution triggers per the
  manager reinforcement msg_8b477982 discipline.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- 18 per-primitive named inhabitants resolve against the RustScalar
  closed Disj.
- IrToRust carrier shape compiles (Conj record over Node + RustScalar).
- Worked example + TestClaim sketches are comments only (no
  data-instance rows authored pre-T-3).
- Shape is ready for incremental row landing — one row per std/
  scalar PR merge (Bool/Nat/Int/Float/Char/Str/Never).

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

Copy link
Copy Markdown
Contributor Author

Worker verification (still-moth-338) — re: cursor/composer-2 review 12834 (APPROVE, no findings)

Spot-checked src/v4/std/diagnostic.dag on PR HEAD: Outcome<T> sits after Diagnostic; header block references DECISIONS.md I and the TASKS/STRUCTURE Result<…, Diagnostic> prose vs Outcome<…> .dag authority split; Practice 4 block runs 347–368 (five patterns + 🟢 GREEN + terminal line), type Outcome<T> / variants 369–371. Matches the artifact’s description (artifact used 347–371 inclusive — same span in practice). DECISIONS.md row I still encodes no parallel Result<ok, err> in std/. No src/v3/ paths in this PR → P5 N/A. No commit — nothing to change.

Merge readiness: GitHub CLEAN, not draft; CI SUCCESS (fmt, affected, ci, v4). Dashboard: ≥2 distinct approval providers (claude + cursor), 0 REQUEST_CHANGES, mergeable=MERGEABLE, checks_state=passing, ready=true. Per temporary policy: not running gh pr merge — operator owns merge; worker STOP.

— sent from still-moth-338

@briansrls
briansrls merged commit 54d12e6 into main May 16, 2026
7 checks 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.

Review metadata

  • Provider / model: codex / unknown
  • Commit: 9c09dca1 · Trigger: schedule
  • Thinking: 153s wall

Non-blocking — Strengths

  • src/v4/std/diagnostic.dag Outcome<T> is shaped as a typed success/rejection sum beside Diagnostic, with no optional-value/error illegal states and a Practice-4 terminal ledger.

✅ The prior authority gaps are closed and I found no blocking concerns in the changed lines.

briansrls added a commit that referenced this pull request May 16, 2026
Per openai-pro REQUEST_CHANGES sha:dfb9464e (2026-05-16T20:45:01Z):

> "The stale D2 seam at src/v4/extdeps/languages/rust.dag:733-738
>  reintroduces a second textual authority for where inhabitance
>  should attach... A future worker faithfully following that later
>  seam could implement the wrong primitive-grounding path even
>  though the surrounding file says the opposite."

Verified: the RustScalar block had a "SEAM (where D2 inhabitance
plugs in)" section that still framed D2 as operator-pending
(msg_640d429c) AND said "the per-variant inhabitance instance-values
land in this file with these typed variants as their carriers —
IntScalar { kind: Signed, width: Bits32 } is the carrier for Rust's
i32 inhabitance into OrderedRing" — which is the superseded
parallel-substrate per-language `data rust_<x>: OrderedRing<Rust<X>>`
shape the ratified D2 row (DECISIONS.md, commit 44a37ad) explicitly
forbids per INVARIANTS P1:42 `numeric_aliases_align_to_refinements`.

Fixes (comment-only, no structural change):
1. RustScalar seam reframed: section now titled "ROLE under the
   RATIFIED D2 resolver" — clarifies RustScalar is the parse-stage
   source-syntax classifier, NOT the inhabitance carrier; primitive
   grounding+inhabitance lands via D2a three-thin-facts
   (alias-identity to std/ carrier + GroundingMap + per-language
   operation-semantics). The earlier framing is called out as
   SUPERSEDED with reference to the D2 row's D2.2 anti-pattern.
2. Header STRUCTURAL FINDING block: D1 status updated from
   "RATIFIED ... NOT YET LANDED" to "LANDED via #3181 commit
   54d12e6" (Outcome<T> = Produced | Rejected); D2 status updated
   from "operator-pending SEMANTIC decision, msg_640d429c" to
   "RATIFIED via #3195 commit 44a37ad, encoded as DECISIONS.md
   D2 row's THREE-THIN-FACTS resolver shape".
3. Owned ELSEWHERE LanguageModel + Pipeline emission/ingest
   bullets: D1 NOT-YET-LANDED references updated to "LANDED via
   #3181"; STOP-TRIGGERs preserved (the carrier landing doesn't
   make this slice the right home for emission/parse ops — they
   belong in compiler/05_emit.dag / compiler/02_parse.dag per
   the ptx #3170 precedent).
4. Consumes block: "std/algebra.dag returns when D2 ratifies"
   prose reconciled — algebra inhabitance for Rust primitives now
   flows transitively through the D2a(1) alias-identity targets
   (e.g. `type RustI32 = Int32`), never re-declared in this file
   per the machine-readable-inhabitance form (`List<T> =
   FreeMonoid<T>` precedent).
5. Substrate-fit modeling note (LITERAL / VALUE bullet): "RIDES
   D2 — pre-D2 the Value's TYPED interpretation is unspecified"
   prose updated to reflect D2-ratified state — the literal's
   typed interpretation rides D2a(1) alias-identity to the std/
   carrier where the algebra inhabitance lives.
6. Owns RustCost bullet: "no instance-values pre-T-3 / pre-D2"
   prose updated — pre-D2 is no longer accurate; the deferral
   is on T-12 lens/cost.dag for concrete cost rows.

Two remaining "operator-pending" references in the file are
PLACEMENT-related (GroundingMap-home pending operator decision per
T-4 mgr msg_148854d5) — correct, not stale.

v2-compiler: 0 diagnostics through full lower-and-emit (68 files).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request May 16, 2026
…y + classifiers (no STOP) (#3174)

* WIP: T-4 extdeps/languages

* v4 T-4 rust-slice: model extdeps/languages/rust.dag — type-system vocabulary + classifiers (no STOP)

Authors the canonical Rust declarative model end-to-end per the T-4 manager
directive (msg_4f7e1280, operator-critical-path item 3; reinforced
msg_8b477982): grammar productions + type-system structure as Node-shaped
declaration-class data — closed-Disj classifiers + Conj records. Same shape
as ptx.dag #3170.

Structural finding: NO STOP from the type-system surface. Rust's scalar
primitive vocabulary, reference kinds, visibility scopes, and ownership /
lifetime model fit the substrate's existing classifier shape without
modification. No 7th connective, no 6th behavior surfaced.

DEFERRED with named owner + dissolution trigger (NOT improvised, NOT stubbed —
per manager reinforcement msg_8b477982 "stub-to-keep-moving is the FORBIDDEN
deferral; defer-with-named-trigger is the legitimate form"):
- Per-language primitive inhabitance instance-values (i32→OrderedRing etc.)
  rides the D2 SEMANTIC decision (operator-pending, msg_640d429c) — §0
  ratifies v2-syntax legality only, NOT the inhabitance shape — AND
  std/integer.dag / std/float.dag / std/logic.dag / std/text.dag (T-3 wave
  A3) by P2 single-authority. The RustScalar Disj here is the SEAM through
  which D2 inhabitance plugs in.
- Pipeline emission/ingest operations (.dag Node → Rust source; Rust source
  → Node ingest) rides the D1 carrier — RATIFIED as
  Outcome<T> = Produced { value: T } | Rejected { diagnostic: Diagnostic }
  but NOT YET LANDED in std/diagnostic.dag. Owners: compiler/05_emit.dag
  (T-10), compiler/02_parse.dag (T-7).
- Bidirectional Rust LanguageModel substrate (grammar productions,
  C5-fidelity disposition, ingest∘emit roundtrip) — bundled T-4 work, rides
  D1.
- Rust ownership / lifetime semantic enforcement — lens/ownership.dag (T-13)
  consumes RustReference as the structural fact it reads (IN-B per
  THESIS:401 — effects intrinsic to the type signature).
- Per-instruction cost INSTANCE-VALUES — rides T-12 lens/cost.dag + U2.

Closed-Disj classifiers with full 5-pattern Practice-4 ledger (per
modeling-discipline.md §4, BINDING gate (b) — "five patterns attempted"
lead-in present on every coproduct):
- RustIntKind (2) — Signed / Unsigned
- RustIntWidth (6) — Bits8/16/32/64/128/Pointer
- RustFloatWidth (2) — Bits32/Bits64 (the stable Rust IEEE-754 set;
  f16/f128 are unstable feature-gates per the pinned Reference)
- RustScalarKind (5) — Int/Float/Bool/Char/Unit
- RustReferenceKind (2) — Shared/Exclusive
- RustVisibility (4) — Private/Crate/Super/Pub
- RustScalar (5-variant Disj with named-field variant payloads) — the
  kind × width admissibility is STRUCTURALLY ENFORCED: Bool/Char/Unit
  carry no width payload, so "Bool with width=Bits32" is unrepresentable
  (P2 illegal-states-unrepresentable).

Conj records (no ledger — only coproducts dissolve):
- RustReference { kind, lifetime: Symbol } — the IN-B effect-typed
  parameter the ownership lens reads
- RustCost { instruction_cost: Int, allocation_cost: Int } — 🟡 scaffold
  mirroring std/diagnostic.dag's Extent.ByteRange + ptx.dag's Dim3 /
  PtxCost (dissolves when std/cardinality.dag refinement substrate lands)

Header reconciliations (per ptx #3170 discipline, scaffold-Consumes
synthesis msg_1fddb75c):
- Owns line REVISED to reflect the delivered slice; scaffold's goals
  preserved in Owned ELSEWHERE with owners + dissolution triggers.
- Consumes line REVISED to `std/node.dag (Symbol only)`; scaffold's
  anticipatory `std/node.dag, std/algebra.dag` line predated the D2
  inhabitance deferral.

Test plan:
- v2-compiler compile --source-root src/v4 --target dag → 0 diagnostics
  (63 modules indexed, 1 file emitted). VERIFIED 2026-05-16.
- No new files; closed file tree invariant honored.
- No hand-Rust (never existed in this file).
- No stub of `emit_rust : Node -> Outcome<RustSource>` or any pipeline op
  (the FORBIDDEN deferral per manager reinforcement).
- No `data <name>: Algebra<Carrier> = …` instance-values introduced pre-D2.
- No `Result<T, Diagnostic>` / `Outcome<T>` invented locally (per the
  ratified-but-unlanded D1 carrier).

cpp.dag + typescript.dag stay HELD per the manager directive (their
scaffold Consumes lists T-3 scalar files that are scaffold-only). python.dag
+ go.dag fan out from this canonical shape once D1 lands AND the operator
ratifies the rust shape + seams (one-canonical-then-fan-out discipline).

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

* WIP: T-4 extdeps/languages

* v4 T-4 rust: fix BLOCKING #3174 — Reference primitive-types partition (drop Unit, add Str + Never)

Operator BLOCKING inline review at rust.dag:609 (2026-05-16T01:06:34Z):
"RustScalarKind claims a closed Reference scalar partition with Unit and
without str/!, but the Rust Reference lists char/str/never as primitive
types and unit as the zero-field TupleType, so this substrate partition
is not externally faithful under INVARIANTS P1/M3."

Verified against the cited Reference URLs:
- §3.5 textual-types lists BOTH `char` AND `str` as primitive textual types.
- §3.6 never-type — `!` IS in the Reference's primitive-types section
  (specific in-position stability gates are a Reference detail, not grounds
  for omitting the kind under L-2 "model the spec").
- §3.10 tuple-types — `()` is the zero-field TupleType, NOT a primitive
  scalar.

The prior `RustScalarKind = Int | Float | Bool | Char | Unit` partition
was therefore unfaithful to the Rust Reference's primitive-type partition
(P1 violation: substrate partition does not ground in spec partition).
Same defect propagated to RustScalar's variant set and both 5-pattern
ledgers.

Fix:
- RustScalarKind: drop `Unit`, add `Str` and `Never`. Now the 6-way
  partition matches Reference §3.3 Bool / §3.4 Numeric (Int+Float) /
  §3.5 Textual (Char+Str) / §3.6 Never.
- RustScalar: drop `UnitScalar`, add `StrScalar` and `NeverScalar`. Six
  variants; kind×width admissibility still STRUCTURALLY ENFORCED
  (Bool/Char/Str/Never carry no width payload).
- Both 5-pattern Practice-4 ledgers updated to reflect the new variant
  set (Algebraic-form line now lists Str → FreeMonoid<Char> via
  std/text.dag and Never → empty cardinality via std/cardinality.dag as
  the deferred inhabitance targets; Terminal lines updated five→six;
  Parameterized-family argument updated for the new heterogeneous
  algebraic groundings).
- Modeling notes' LITERAL/VALUE mapping: `()` removed from the literal
  examples and cross-referenced to SCOPE NEGATIVE Tuple types.
- SCOPE NEGATIVE: explicit new entry for "Tuple types (including the
  zero-tuple `()` / 'unit')" — they are their own type category per
  §3.10, modeled via a future tuple-type carrier composing over
  std/cardinality.dag. Names the prior mis-classification and the
  correction rationale.
- Header Owns: Int/Float/Bool/Char/Unit partition → Int/Float/Bool/Char/
  Str/Never partition, with explicit note that `()` is TupleType and
  out-of-scope.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics (63 modules indexed, 1 file emitted).
  VERIFIED post-fix.
- INVARIANTS P1 faithfulness restored: RustScalarKind grounds in
  Reference §3.3-§3.6's primitive-type partition; tuple types
  (including `()`) are SCOPE NEGATIVE.
- Six-pattern coproduct + Conj structure unchanged (still 7 closed-Disj
  coproducts with full 5-pattern ledgers + 2 Conj records).
- No D1/D2 trap re-introduced: still ZERO inhabitance instance-values,
  ZERO pipeline ops, ZERO Result/Outcome locally invented. The deferral
  notes' Str → FreeMonoid<Char> and Never → empty-cardinality
  groundings are DECLARED targets, not authored instances.

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

* v4 T-4 rust: fix BLOCKING #3174 visibility + add per-carrier Spec doc-anchors

TWO operator-directed changes in one cycle for PR #3174:

(1) BLOCKING inline review at rust.dag:847 (briansrls 2026-05-16T01:06:34Z):
"RustVisibility collapses Rust Reference pub(in SimplePath) into Super,
dropping the required path fact and making cases like pub(in crate::outer_mod)
unrepresentable, violating extdeps fidelity and P2 boundary discipline."

Verified against the Rust Reference §visibility-and-privacy: the Reference
gives `pub(in SimplePath)` as the FUNDAMENTAL path-restricted form, with
`pub(crate)`, `pub(self)`, `pub(super)` as syntactic CONVENIENCE FORMS
("`pub(crate)` is the same as `pub(in crate)`. `pub(super)` is the same as
`pub(in super)`. `pub(self)` is the same as `pub(in self)`"). The prior
4-way partition (Private | Crate | Super | Pub) dropped the path fact —
the Reference's fundamental axis — and made `pub(in crate::outer_mod)`
unrepresentable. P1 modeling-faithfulness AND P2 boundary-discipline
violation.

Fix: refactor RustVisibility to the Reference's fundamental three-way form:
  type RustVisibility
    = Private
    | Pub
    | PubIn { path: List<Symbol> }

PubIn carries the SimplePath as a List<Symbol> of K-1-opaque segments
(each segment a Symbol per the same K-1 discipline node.dag:70-86 used
for RustReference's lifetime). The four syntactic shorthand forms each
have ONE canonical PubIn representation per the Reference's stated
equivalences (no parallel PubCrate/PubSelf/PubSuper variants — that would
be the duplicate-authority P2 violation). Multi-segment paths like
`pub(in crate::outer_mod)` represent as `PubIn { path: [crate-symbol,
outer_mod-symbol] }` — the path fact preserved structurally, not dropped.

The 5-pattern Practice-4 ledger updated to reflect:
- Variant-is-data argument now cites the "kind=Pub with path=[...]"
  illegal state the Disj-with-payloads forbids.
- Algebraic-form argument generalizes the partial-order to PubIn paths
  ordered by module-containment, with the BoundedLattice<Visibility>
  parallel-rep smell still rejected.
- Parameterized-family argument cites the heterogeneous shapes (Private/Pub
  nullary, PubIn carrying List<Symbol>) as the irreducible Disj-with-
  payloads form — collapsing PubIn into Private/Pub would lose the path
  fact, exactly the regression this fix undid.

Header Owns line updated: 4-way → 3-way; path-restricted visibility
CARRIES its SimplePath structurally; `pub(in crate::outer_mod)`
representable.

(2) NEW STANDING REQUIREMENT (T-4 mgr directive msg_448b8188, operator-
directed): per-carrier/per-section `// Spec: <URL>` doc-anchor comment
lines pointing to the exact upstream Reference page each carrier models.
Required before/as the python/go fan-out mirrors the convention. Plain
documentation comments — explicitly NOT the cut doc_anchor.dag substrate
concept (no data, no TTL, no parsing); pin discipline holds (a spec
change at the URL is a visible C1 edit to the carrier).

Added Spec lines on each carrier:
- RustIntKind:       https://doc.rust-lang.org/reference/types/numeric.html
- RustIntWidth:      https://doc.rust-lang.org/reference/types/numeric.html
- RustFloatWidth:    https://doc.rust-lang.org/reference/types/numeric.html
- RustScalarKind:    https://doc.rust-lang.org/reference/types.html (+ per-
                     variant URLs for Bool/Int/Float/Char/Str/Never)
- RustScalar:        https://doc.rust-lang.org/reference/types.html
- RustReferenceKind: https://doc.rust-lang.org/reference/types/pointer.html
- RustVisibility:    https://doc.rust-lang.org/reference/visibility-and-privacy.html
- RustReference:     https://doc.rust-lang.org/reference/types/pointer.html
- RustCost:          gunbc-internal (no upstream URL applies; the U1
                     one-homomorphism-with-cost discipline pins it)

Comment-only, additive. The header `Anchor:` root line preserved verbatim.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED post both edits.
- P1 modeling-faithfulness restored on visibility: the partition grounds
  in Reference §visibility-and-privacy's fundamental form.
- P2 boundary-discipline restored: path fact preserved structurally
  (`pub(in crate::outer_mod)` representable).
- Doc-anchor convention established for python/go/cpp/typescript fan-out
  to mirror; ready for that the moment the operator ratifies the rust
  shape + the rest of the queued findings are addressed.

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

* v4 T-4 rust: fix BLOCKING #3174 — RustReference adds `referent: Node` (TypeNoBounds preserved)

Operator BLOCKING inline review (briansrls 2026-05-16T01:06:34Z):
"RustReference is declared as a typed reference but only carries kind and
lifetime, so the ReferenceType referent TypeNoBounds is lost and &'a i32 /
&'a str collapse to one fact, violating extdeps fidelity and P2 boundary
discipline; see https://doc.rust-lang.org/reference/types/pointer.html."

Verified against Reference §types-pointer: the ReferenceType grammar is
`&'lifetime mut? TypeNoBounds` — THREE structural coordinates (lifetime ×
reference-kind × referent type). The prior `RustReference { kind,
lifetime }` shape dropped the referent fact; `&'a i32` and `&'a str`
both produced the same RustReference value, collapsing distinct types.
INVARIANTS P1 modeling-faithfulness + P2 boundary-discipline violations.

Fix: add `referent: Node` field. Node is the bounded kernel's single
recursive type per node.dag A1 — same shape as `Edge.target: Node` and
`Diagnostic.correction Suggested(Node)`. Any type expression IS a Node
(per feedback_nodes_are_nodes — there is no separate "TypeNoBounds
alias"). Whether the referent is in fact a kind=TypeNode Node is a
structural well-formedness invariant the LanguageModel grammar enforces,
not a type-level constraint here (the same discipline node.dag's
`node_well_formed` applies to connective child shapes).

  type RustReference {
    kind: RustReferenceKind
    lifetime: Symbol
    referent: Node
  }

Now `&'a i32` ↔ `RustReference { kind: Shared, lifetime: 'a, referent:
<i32 Node> }` is distinct from `&'a str` ↔ `RustReference { kind: Shared,
lifetime: 'a, referent: <str Node> }`. Three structural coordinates per
the Reference grammar; the IN-B "intrinsic to the type signature" claim
now spans all three.

Import expanded: `import v4.std.node { Node, Symbol }` (was `{ Symbol }`
only — Node added for the referent field).

Header updates:
- Owns line: RustReference's three coordinates now spelled out — kind +
  lifetime Symbol + referent Node — with explicit "&'a i32 / &'a str
  distinct facts (P1/P2 fidelity)" annotation.
- Consumes line: revised to `Node` + `Symbol` from std/node.dag (was
  `Symbol` only). Same scaffold-Consumes reconciliation discipline as
  prior commits. ptx.dag had zero imports; this file now has two,
  driven by the RustReference Conj record's lifetime + referent fields.
- RustReference's modeling-notes block:
  - Opening prose re-cast around the Reference grammar's three-coordinate
    structure
  - New `referent` field documented with the "Node is the bounded
    kernel's single recursive type" rationale + the well-formedness
    boundary note
  - IN-B / ownership lens prose extended: the lens now reads
    (kind, lifetime, referent) coordinates; the referent's own type-
    Node structure is recursively walked as needed for the full type-
    expression grammar (array types, generic instantiations, function
    pointer types, nested references) without this file declaring each
    shape separately

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED post-fix.
- P1 modeling-faithfulness restored on references: the Conj carrier
  grounds in the Reference's `&'lifetime mut? TypeNoBounds` rule's three
  coordinates.
- P2 boundary-discipline restored: referent fact preserved (`&'a i32` ≠
  `&'a str`).
- The `referent: Node` field is the SEAM through which the bundled T-4
  LanguageModel grammar's type-expression productions plug in when they
  land (any Rust type expression IS a Node — the grammar productions
  yield Node trees the referent slot consumes).
- No D1/D2 trap reintroduced: still ZERO inhabitance instance-values,
  ZERO pipeline ops, ZERO Result/Outcome locally invented.

One more BLOCKING queued ("+1 more" per dashboard relay) — addressing
next.

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

* v4 T-4 rust: fix BLOCKING #3174 — pub(self) ≡ Private (P2 canonicalization)

Operator BLOCKING inline review at rust.dag:962 (briansrls 2026-05-16T02:26:00Z):
"RustVisibility treats Private as distinct from PubIn { path: [self] }, but
the Rust Reference says pub(self) is equivalent to not using pub at all
(https://doc.rust-lang.org/reference/visibility-and-privacy.html), so the
carrier has two authorities for one visibility scope under INVARIANTS P2."

Verified — the Reference §visibility-and-privacy states:
  "pub(self) is the same as pub(in self) AND is the same as not using
   pub at all."

So `pub(self)` / `pub(in self)` / no-modifier are THREE syntactic forms
for ONE semantic visibility scope. My prior PubIn fold admitted ALL
`pub(in path)` forms — including `pub(in self)` as `PubIn { path:
[self-symbol] }`. That created two STRUCTURAL representations of the
same visibility: `Private` AND `PubIn { path: [self-symbol] }`. P2
single-authority violation: two authorities for the no-modifier scope.

Fix:
- Doc-comment update: `Private` is now explicitly the canonical
  representation of `pub(self)` / `pub(in self)` / no-modifier (per the
  Reference's three-way equivalence). PubIn's doc states only
  `pub(crate)` / `pub(in crate)` / `pub(super)` / `pub(in super)` /
  arbitrary `pub(in path)` fold into PubIn — pub(self) family
  canonicalizes to Private.
- New `rust_visibility_well_formed(v: RustVisibility) -> Bool`
  well-formedness fn declared. PubIn { path: [self-symbol] } is
  documented as ill-formed; producers MUST canonicalize to Private.
- 🟡 SCAFFOLD invariant: same producer-obligation shape as Dim3 /
  RustCost. The actual check `path == [self-symbol]` requires
  comparing against a canonical `self` Symbol, which K-1 forbids
  minting in data literals. Until std/ exposes a canonical
  `self_symbol` surface OR std/cardinality.dag lands a refined-path
  carrier admitting only non-[self] paths, the fn returns `true`
  unconditionally and the invariant is producer-obligated and
  documented. Dissolution trigger named.
- 5-pattern Practice-4 ledger Pattern-3 (Algebraic form) updated:
  the partial-order chain previously listed `Private ⊑ PubIn { path:
  [self] } ⊑ ...` which was contradictory under the fix (those are
  the same scope). Now: Private ⊑ PubIn { path: [super] } ⊑ PubIn
  { path: [crate] } ⊑ Pub; pub(self) ≡ Private is not a separate
  level.
- Terminal note: lists the three-way canonical mapping explicitly
  (`pub(self)` → Private, NOT PubIn).
- BLOCKING-correction history extended: this is the second visibility
  BLOCKING — first dropped the path fact (commit d90431ee0), second
  admitted duplicate authority for pub(self). Both now corrected.

Test plan:
- v2-compiler compile --source-root src/v4 --target dag → 0 diagnostics.
  VERIFIED post-fix.
- P2 single-authority restored: each Reference visibility scope has
  exactly ONE canonical representation in the partition.
- Producer-obligation invariant declared via well-formedness fn (same
  shape as Dim3/RustCost 🟡 bridge — producer-side until refined-path
  substrate lands).
- Dissolution trigger named: std/ canonical-self-symbol surface OR
  std/cardinality.dag refined-path carrier.

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

* v4 T-4 rust: add per-primitive named registry + IrToRust mapping shape (Bool worked example)

Per operator conversational request 2026-05-16 ("i basically just want
the shape ready - i.e. bit or something"): the IR ↔ Rust mapping SHAPE
gets fleshed out now so it's ready to land row-by-row as std/ scalar
types reach merged state. Bool is the centered worked example since
PR #3166 (std/logic.dag) is the readiest in-flight T-3 unblock.

Added:

(1) Per-primitive named inhabitants — 18 typed `data` instances of
    `RustScalar`'s closed-enum payload-bearing variants, one per Rust
    primitive type. NOT algebra-inhabitance (no D2 trap); just typed
    VALUES of the closed Disj:
      - 6 signed integer primitives (rust_i8/i16/i32/i64/i128/isize)
      - 6 unsigned integer primitives (rust_u8/u16/u32/u64/u128/usize)
      - 2 IEEE-754 float primitives (rust_f32/rust_f64)
      - 4 reference primitives (rust_bool/rust_char/rust_str/rust_never)
    Each is a structurally-distinct value of RustScalar; emit/ingest
    dispatch on these by STRUCTURAL identity, never by spelling (K-1).

(2) `IrToRust` Conj carrier — the IR-type ↔ Rust-primitive
    correspondence shape. One row per std/ canonical carrier ↔ Rust
    primitive emission. Fields:
      ir_carrier: Node       // name-reference to a std/ type
      rust_repr:  RustScalar // one of the 18 per-primitive inhabitants
    emit (T-10) reads these rows; ingest (C5 reverse direction) walks
    them backwards. Same shape per-language across fan-out.

(3) Bool worked example (comment-only — std/logic.dag's Bool not yet
    merged in PR #3166):
      // import v4.std.logic { Bool }
      // data bool_to_rust_bool: IrToRust = IrToRust {
      //   ir_carrier: Bool,
      //   rust_repr:  rust_bool
      // }
    Demonstrates emit dispatch (Bool-typed value → BoolScalar →
    Rust source "bool") and ingest reverse-walk (parsed "bool" →
    BoolScalar → Bool-typed Node).

(4) Per-row TestClaim shape sketch (also comment-only — pending
    std/verification.dag T-3 Wave-A2):
      // data t_bool_to_rust_bool_roundtrip: TestClaim = TestClaim {
      //   kind: RoundTrips, ...
      // }
    The fully-instrumented testcase rides three landings: (a)
    std/<scalar>.dag, (b) this PR's shape (current), (c)
    std/verification.dag's TestClaim schema.

(5) Status snapshot of in-flight T-3 PRs naming which IrToRust rows
    each unblocks (Bool ← #3166, Nat ← #3165, etc.). The MISSING list
    (std/integer.dag / std/machine.dag / std/float.dag / std/text.dag /
    std/verification.dag) is named explicitly so the T-4 manager
    driving T-3 has the dependency map.

Discipline preserved:
- NO algebra-inhabitance instance-values authored pre-D2 (`data X:
  OrderedRing<...> = ...`). The 18 named primitives are typed values
  of a closed Disj, not algebra inhabitances — different shape, not
  D2 trap.
- NO stubs of pipeline ops (emit_rust / parse_rust). The IrToRust
  rows declare structural correspondence; emit/parse remain T-7/T-10
  with the D1 Outcome carrier dependency (#3181 in flight).
- All deferrals carry named owners + dissolution triggers per the
  manager reinforcement msg_8b477982 discipline.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- 18 per-primitive named inhabitants resolve against the RustScalar
  closed Disj.
- IrToRust carrier shape compiles (Conj record over Node + RustScalar).
- Worked example + TestClaim sketches are comments only (no
  data-instance rows authored pre-T-3).
- Shape is ready for incremental row landing — one row per std/
  scalar PR merge (Bool/Nat/Int/Float/Char/Str/Never).

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

* v4 T-4 rust: remove rust_visibility_well_formed stub (FORBIDDEN-deferral fix)

Per claude-opus-4-7 review observation 2026-05-16T02:35:05Z (PR #3174):

> "`rust_visibility_well_formed` (line ~1080) returns `true` for all
> variants with a "PRE-T-3 LIMITATION" note saying the real check
> can't be written because K-1 forbids minting `Symbol` literals. The
> function is documented as a scaffold with named dissolution trigger,
> so this is the legitimate defer-with-trigger form rather than a stub
> — but it is worth noting that the function currently asserts an
> invariant it doesn't enforce. A downstream consumer naïvely calling
> it would get a false sense of validation. Consider whether the
> producer-obligation phrasing in the header is sufficient, or whether
> the fn should not exist at all until it can do real work."

This is exactly the stub-to-keep-moving anti-pattern the manager
reinforcement msg_8b477982 named as the FORBIDDEN deferral. A fn
named `*_well_formed` returning `true` unconditionally is a
false-validation surface — a naive consumer reading "is this value
well-formed? Yes, the substrate said so" would have no idea the
check is a noop. The fact that I added it (commit e878926ce) under
the 🟡-scaffold framing was the smell; the review correctly named
the anti-pattern.

Fix: REMOVE the fn entirely. The producer-obligation invariant lives
as DOCUMENTED-ONLY text in the RustVisibility doc-comment, alongside
explicit named conditions under which a real fn would land:
  (a) std/ exposes a canonical self_symbol via a typed boundary
      surface, enabling a working check, OR
  (b) std/cardinality.dag lands a refined-path carrier admitting only
      non-[self] paths (illegal states type-unrepresentable, no fn
      needed).

The doc-comment now explicitly states:
- NO fn is declared (the prior `rust_visibility_well_formed` was a
  false-validation stub).
- The invariant lives as producer obligation until the substrate can
  actually enforce.
- Downstream consumers reading PubIn MUST NOT assume any
  well-formedness check has validated the path.

This is the same discipline as the Dim3 / RustCost 🟡 scaffolds —
applied at the DOCUMENTATION layer, NOT as a noop fn. The proper
end-state has the fn appear only when it can do real work.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED post-removal.
- No false-validation surface introduced.
- Producer obligation explicit + dissolution trigger named.

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

* WIP: T-4 extdeps/languages

* v4 T-4 rust: structural P2 enforcement — introduce RustPathSegment (no Self variant)

Codex REQUEST_CHANGES (codex-default 2026-05-16T02:51:57Z):

> "RustVisibility = Private | Pub | PubIn { path: List<Symbol> } still
> admits the illegal duplicate-authority state PubIn { path:
> [self-symbol] }, and the diff explicitly leaves that invariant as a
> producer-side doc comment only. ... the single-authority rule for
> pub(self)/pub(in self) vs Private is not structurally enforced by
> the model being introduced here. ... On substrate-facing modeling
> work, that boundary should be encoded or blocked structurally rather
> than documented."

Codex is correct. My prior remediation (commit 03a43b720 — remove the
noop well-formedness fn, leave the invariant as a doc-comment) moved
the smell from "noop validator" to "documented-only," but the actual
structural state `PubIn { path: [self-symbol] }` remained CONSTRUCTIBLE.
modeling-discipline.md §Practices 2 & 6 require illegal states
unrepresentable; INVARIANTS P2 require single-authority structurally.

Fix: introduce `RustPathSegment` as a typed closed-Disj classifier
between RustReferenceKind and RustVisibility, with NO `Self` variant:

  type RustPathSegment
    = Crate
    | Super
    | Named { id: Symbol }

`RustVisibility.PubIn` updated to `{ path: List<RustPathSegment> }`.
Now `PubIn { path: [Self] }` is STRUCTURALLY UNREPRESENTABLE — Self
isn't a constructible segment kind. The duplicate-authority for the
no-modifier visibility scope is impossible at the TYPE level, not just
forbidden by convention.

Canonicalization rules (now type-enforced):
- `pub(crate)` / `pub(in crate)` → `PubIn { path: [Crate] }`
- `pub(super)` / `pub(in super)` → `PubIn { path: [Super] }`
- `pub(in crate::outer_mod)` → `PubIn { path: [Crate, Named { id: outer_mod }] }`
- `pub(self)` / `pub(in self)` / no-modifier → `Private` (the only
  representation; PubIn with Self segment is unrepresentable)
- `pub(in self::outer_mod)` → `PubIn { path: [Named { id: outer_mod }] }`
  (parse boundary drops the leading self per Reference §paths —
  `self::X` IS `X` from current module's POV)

Scope-negative (named C1 edit):
- `$crate` macro-context segment — not modeled in this slice; lands
  as a `MacroCrate` variant in RustPathSegment when hygienic-macro
  support lands.

Documentation cleanup:
- Removed the documented-only "producer-obligation invariant" block
  (the false-validation surface the codex / claude-opus reviews
  correctly flagged).
- Updated RustVisibility's BLOCKING-correction history: now THREE
  BLOCKING fixes — (1) path fact dropped, (2) PubIn admitted Self
  case, (3) doc-only enforcement isn't structural; final fix is
  type-level via RustPathSegment.
- 5-pattern Practice-4 ledger on RustVisibility's Pattern-3 partial-
  order updated (paths use RustPathSegment variants, not Symbol).
- Full 5-pattern ledger on the new RustPathSegment carrier.
- Header Owns line updated to mention RustPathSegment.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- `PubIn { path: [Self] }` structurally unrepresentable (Self not a
  RustPathSegment variant — try to write it; v2-compiler diagnoses).
- `PubIn { path: [Crate] }`, `PubIn { path: [Super] }`, `PubIn { path:
  [Crate, Named { id: ... }] }` all constructible and well-typed.
- The 18 per-primitive named inhabitants + IrToRust shape unaffected.
- Bool worked-example unaffected.

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

* v4 T-4 rust: land first concrete IR↔Rust row — Bool → rust_bool (post-T-3-merge)

Per operator request 2026-05-16 ("FYI everything under T-3 has merged - can
you please find an example to implement in extdeps to implement now?"). With
std/logic.dag merged (PR #3166), Bool now exists as a typed Node identity
and the first concrete IR↔Rust correspondence row can land structurally.

Refactor: IrToRust shape parameterized by IR type.

Prior: `type IrToRust { ir_carrier: Node, rust_repr: RustScalar }` — used
a value-field Node reference for the IR side. Value-level type-identity is
clunky (requires reifying type names as Node values) and inconsistent with
the U1 Homomorphism precedent (`type Homomorphism<C, Source, Target>` uses
type-parameter positions for the algebraic carriers).

Now:
  type IrToRust<IRCarrier> {
    rust_repr: RustScalar
  }

The IR side is a type-parameter, matching U1's `C<Source>` / `C<Target>`
shape. Type-level keying; emit dispatches by type-identity match on
IRCarrier at compile time. Same per-language pattern across fan-out.

First concrete row:
  import v4.std.logic { Bool }

  data bool_to_rust_bool: IrToRust<Bool> = IrToRust {
    rust_repr: rust_bool
  }

This is the LIVE end-to-end IR↔Rust correspondence for the Bool ↔
`bool` mapping. emit (T-10) will read this row to project a Bool-typed
.dag value to Rust source `bool`; ingest (C5) walks it backwards.

Bool is the natural first row:
- std/logic.dag declares `Bool = True | False` (closed Disj) + the
  `bool_boolean_algebra: BooleanAlgebra<Bool>` inhabitance (PR #3166
  merged 2026-05-16).
- Rust's `bool` per Reference §types-boolean is the same two-valued
  type inhabiting BooleanAlgebra — 1:1 correspondence.
- Bool ↔ rust_bool is the simplest non-trivial IR↔Rust row; rust_i*
  / rust_u* / rust_f* still wait on std/integer.dag + std/float.dag
  + std/machine.dag (still unfilled per merged-T-3 scope: logic, nat,
  cardinality, collection, witness, diagnostic-Outcome landed; integer,
  float, machine, text, verification, report still scaffold-only).

Doc-comment updates:
- IrToRust's prose describes the type-parameterized shape and the
  type-identity dispatch (compile-time, K-1-compatible).
- The Bool worked-example block becomes the LIVE row (not commented).
- Operator's "can we write testcases now?" question answered concretely:
  the SHAPE is now end-to-end live for Bool. The TestClaim itself
  awaits std/verification.dag's schema (also T-3 Wave-A2, pending).

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- The Bool import resolves to std/logic.dag's `type Bool = True | False`.
- The IrToRust<Bool> type-parameter instantiation is well-typed.
- The data row `bool_to_rust_bool` correctly references rust_bool (the
  BoolScalar named inhabitant declared earlier in the file).
- Same shape ready for python/go fan-out once those slices land.
- Next rows (Nat → ?) await std/integer.dag for the abstract-int ↔
  rust_u32 / rust_u64 mapping; std/text.dag for char/str; etc.

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

* v4 T-4 rust: fix post-refactor comment drift (List<Symbol> → List<RustPathSegment>)

Cursor APPROVE_WITH_COMMENTS 2026-05-16T03:25:09Z, two non-blocking
post-refactor comment-drift findings:

- Line 189: Consumes header still said "List<Symbol> in RustVisibility
  .PubIn's path" after the RustPathSegment refactor changed the field
  to List<RustPathSegment>.

- Line 1122: RustVisibility Practice-4 ledger Pattern-5 still said
  "PubIn carries a List<Symbol> path" — same drift.

Both fixed by updating the descriptions to reflect the current shape:
- Consumes header now reads "`List<RustPathSegment>` in `RustVisibility
  .PubIn`'s path — the segments themselves are local typed-Disj values,
  and `Named` segments carry an opaque `Symbol` identifier per K-1"
  and adds the `Bool` import from std/logic (for the live `IrToRust<Bool>`
  row).
- Pattern-5 ledger now reads "PubIn carries a List<RustPathSegment>
  path".

The remaining `List<Symbol>` mentions in the file (lines 1057 / 1099 /
1156) are intentional historical / hypothetical references:
- 1057: BLOCKING-correction history block describing prior shape #2
  (the version that admitted PubIn{path:[self-symbol]}).
- 1099: Pattern-2 ledger describing the COLLAPSED-TO-UNIFORM-RECORD
  anti-pattern as `{ kind: Symbol, path: List<Symbol>? }` — hypothetical.
- 1156: well-formedness evolution note describing why the doc-only
  invariant approach failed.

None of those describe the CURRENT shape; cursor's review explicitly
flagged 189 + 1122 only, which are now corrected.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- No functional change — comment-only.

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

* v4 T-4 rust: add Layer-2 structural-assertion fn + 3-layer testing pattern doc

Per operator request 2026-05-16 ("yes lets add the expectation up front for
other languages before the fan out/my review"): establish the Layer-2
testing pattern + first concrete assertion fn for the live Bool → rust_bool
row, BEFORE python/go/cpp/typescript fan-out, so they all mirror the same
shape.

Added:

(1) 3-layer testing pattern doc-comment block — explicitly names the
    three layers and their gating dependencies:

    - Layer 1 (LIVE NOW): 0-diag structural typing. Every `data X: T = ...`
      is a type-check assertion. Drift breaks the build. Not a "test" in
      the TestClaim sense, but real structural verification.

    - Layer 2 (AUTHORABLE NOW, pattern established here): structural-
      assertion fns. Exhaustive match on closed-Disj variants, returning
      Bool. v2-compiler's exhaustiveness check verifies all variants
      covered; the fn body encodes the expected variant. Forward-anchor
      for Equals TestClaim once verification.dag lands.

    - Layer 3 (GATED): TestClaim data. Requires PR #3183 (std/verification
      .dag, in flight) for declarative TestClaim data. Executable
      RoundTrips claims additionally need T-7 (parse) + T-10 (emit).

(2) First concrete Layer-2 fn:

    fn assert_bool_to_rust_bool_consistency() -> Bool {
      match bool_to_rust_bool.rust_repr {
        BoolScalar                       => true
        IntScalar { kind: _, width: _ }  => false
        FloatScalar { width: _ }         => false
        CharScalar                       => false
        StrScalar                        => false
        NeverScalar                      => false
      }
    }

    Asserts via exhaustive match that the live `bool_to_rust_bool` row's
    `rust_repr` IS the `BoolScalar` variant. If a future edit changes
    the row to point at a different per-primitive inhabitant (e.g.
    `rust_i32`), the fn still compiles but returns `false` — a structural
    change with a behavioral signal that lands as TestClaim verification
    once #3183 merges.

(3) Forward-anchor TestClaim sketch (comment-only — std/verification.dag
    not yet merged) showing how the fn becomes the body of an `Equals`
    TestClaim once #3183 lands:

      data t_bool_to_rust_bool_kind: TestClaim = TestClaim {
        kind:     Equals,
        input:    assert_bool_to_rust_bool_consistency(),
        expected: true
      }

(4) Fan-out template note — python.dag / go.dag / cpp.dag / typescript.dag
    will each mirror this Layer-2 pattern: their per-primitive Bool row
    gets an analogous `assert_bool_to_<lang>_bool_consistency` fn.
    Establishing the pattern HERE removes one decision-point from each
    fan-out worker's slice.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- Exhaustive match on RustScalar's 6 variants type-checks.
- The assertion fn's structural property (Bool row maps to BoolScalar)
  encoded; future drift produces a `false` return at the behavioral
  level when an interpreter (T-22) runs it or a TestClaim asserts it.
- No D1/D2 trap: no inhabitance instance-values, no pipeline ops, no
  Result/Outcome locally invented. The fn is a structural-property
  check, not a stub of a pipeline operation.

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

* v4 T-4 rust: refresh comment-only authoring guide post-refactor + Bool merge

Claude APPROVE_WITH_COMMENTS 2026-05-16T03:42:39Z — two non-blocking
doc-drift findings:

(1) Worked-example block (prior lines 1576-1590) used the OLD field-based
    IrToRust shape (`IrToRust { ir_carrier: Bool, rust_repr: rust_bool }`),
    but the carrier was refactored to the type-parameterized form
    `IrToRust<IRCarrier> { rust_repr: RustScalar }` in commit ed1d47f33.
    Block also said "since std/logic.dag's Bool isn't merged yet" —
    contradicts the live `bool_to_rust_bool` row + the import statement.

(2) "Status of in-flight std/ PRs" snapshot still listed Bool's std/logic
    as a PENDING row, even though it's now LANDED in commit ed1d47f33.

Fix: rewrite the entire authoring-guide block to reflect current state:

- Status snapshot updated:
  - std/logic.dag (Bool) ✅ LANDED + bool_to_rust_bool row LANDED above
  - std/nat.dag (Nat) ✅ LANDED (still needs std/integer.dag for u* mapping)
  - std/integer.dag / std/machine.dag / std/float.dag / std/text.dag
    remain ❌ MISSING

- Authoring template rewritten to the current type-parameter form:
    data <ir>_to_<rust>: IrToRust<<IRCarrier>> = IrToRust {
      rust_repr: <rust_primitive>
    }

- Layer-2 assertion-fn template added (mirrors the live
  `assert_bool_to_rust_bool_consistency` pattern) — fan-out workers
  for python/go/cpp/typescript copy this verbatim with language-
  appropriate variants.

- TestClaim sketch updated to current state: PR #3183 named (was
  generic "T-3 Wave-A2"); template uses the type-parameter form.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- Comment-only changes; no code/declaration drift.
- Authoring template now matches the live row's shape — fan-out
  workers won't mis-copy the obsolete value-field form.

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

* WIP: T-4 extdeps/languages

* WIP: T-4 extdeps/languages

* WIP: T-4 extdeps/languages

* v4 T-4 rust: fix BLOCKING #3174 — head-rooted PubInPath replaces unconstrained List<RustPathSegment>

Operator BLOCKING inline review 2026-05-16T05:06:51Z at line 1166:

> "PubIn stores an unconstrained List<RustPathSegment>, so impossible or
> edition-dependent visibility paths like [], [Crate, Super], [Named] in
> Rust 2018, or Super after a named segment are constructible despite Rust
> Reference path-qualifier rules, violating extdeps fidelity and INVARIANTS
> P2."

The prior `List<RustPathSegment>` shape admitted arbitrary segment
sequences. The Reference's path-qualifier rules forbid several orderings,
but the structural carrier couldn't enforce them — same shape of
duplicate-authority / illegal-states-constructible defect the prior
fix-rounds addressed for the Self-segment case, here for the
position-ordering case.

Fix: replace the flat list with a HEAD-ROOTED structure:

  type NamedSegment {
    id: Symbol
  }

  type SuperHops
    = OneSuper                            // base — exactly one super
    | MoreSuper { outer: SuperHops }      // recursive — super::super::...

  type PubInRoot
    = AtCrate                             // pub(in crate)
    | AtSuperChain { hops: SuperHops }    // pub(in super) / super::super / ...

  type PubInPath {
    root:   PubInRoot                     // required ancestor anchor
    suffix: List<NamedSegment>            // 0+ named segments after root
  }

  type RustVisibility
    = Private
    | Pub
    | PubIn { path: PubInPath }

Illegal Reference path-qualifier orderings all become STRUCTURALLY
UNREPRESENTABLE:

  - `[]` (empty path)              → root coordinate required by
                                     PubInPath shape; unrepresentable.
  - `[Crate, Super]` (super after  → SuperHops only at root; suffix
     crate root)                     admits only NamedSegments;
                                     unrepresentable.
  - `[Named]` alone (bare named    → root required; bare named without
     without root)                   prefix unrepresentable.
  - `Super after a Named segment`  → suffix admits only NamedSegments;
     (e.g. `crate::foo::super`)      unrepresentable.
  - `Crate after Named` (e.g.      → only one root coordinate (head);
     `super::crate`)                 Crate cannot appear in suffix;
                                     unrepresentable.
  - Zero super hops                → SuperHops's base case is OneSuper
     (`pub(in super)` with 0 hops)   (no Zero variant); a zero-hop
                                     super-chain — which would be
                                     ≡ `self` ≡ Private — is
                                     unrepresentable.
  - Self-rooted any                → PubInRoot has no Self anchor (prior
     (`pub(in self)`, etc.)          fix); unrepresentable.

Both INVARIANTS P2 (single authority) and modeling-discipline.md
Practices 2/6 (illegal states unrepresentable) are enforced
STRUCTURALLY by the type shape, not by documentation or producer
obligation.

Documentation updates:
- RustPathSegment carrier DELETED (its three variants are now
  position-distinguished by the head-rooted shape).
- New NamedSegment / SuperHops / PubInRoot / PubInPath carriers
  declared with full 5-pattern Practice-4 ledgers on the two new
  closed-Disj coproducts (SuperHops, PubInRoot).
- RustVisibility's doc block updated: PubIn variant now carries
  PubInPath; the BLOCKING-correction history extended to include
  this round-4 head-rooted-refactor; the canonical-mapping table
  in the Terminal line updated.
- Header Owns line updated to reflect the new carrier set.
- Consumes-reconciliation note updated: List<RustPathSegment>
  references replaced with List<NamedSegment> (suffix usage).
- The well-formedness paragraph after RustVisibility no longer
  references RustPathSegment; type-enforcement framing updated.

SuperHops's inductive shape (OneSuper | MoreSuper { outer: SuperHops })
is the Peano-like representation of "Nat ≥ 1", structurally encoding
the non-empty-super-chain constraint. The Practice-4 ledger Pattern-2
explicitly notes that a `{ hops: Nat }` proxy would reintroduce the
0-hop case, repeating the same duplicate-authority defect this fix
addresses.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- The 6 illegal Reference path-qualifier orderings (listed above) are
  all unrepresentable via the type shape.
- INVARIANTS P2 type-enforced (single canonical representation per
  Reference visibility scope).
- modeling-discipline.md Practices 2/6 type-enforced (illegal states
  unrepresentable).
- This fix is OUTSIDE the D2 inhabitance HOLD (RustVisibility carrier
  is independent of the 4 D2-pending per-primitive registry / IrToRust
  items); manager directive permits visibility refactors.

One more BLOCKING queued per the dashboard relay ("+1 more"); standing
by to verify/address.

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

* WIP: T-4 extdeps/languages

* v4 T-4 rust: fix BLOCKING #3174 — SuperHops 🟡 not 🟢 (M9/P5 — parent exists in v3 termination.dag scaffold)

Operator BLOCKING inline review 2026-05-16T06:26:19Z at line 942:

> "SuperHops is classified 🟢 terminal even though it duplicates the
> same Peano positive-count shape that dsl/std/termination.dag already
> treats as a scaffold pending shared numeric refinements, violating
> M9/P5."

Verified — internal contradiction in the file: I added a 🟡 SCAFFOLD
note ABOVE the SuperHops declaration in the prior commit (acknowledging
the parallel-to-Nat encoding + naming the dissolution trigger when
shared positive-count substrate lands) BUT left the 5-pattern Practice-4
classification as `🟢 GREEN (terminal)` UNCHANGED. The two are
contradictory:
- 🟢-terminal: claims no upstream parent, irreducible substrate
- 🟡-scaffold: acknowledges parent (shared positive-count) exists
  in upstream-scaffold form, this is a bridge

Per M9 (DFS the concept DAG): the genuine parent (positive-count
substrate) exists in v3-scaffold form at dsl/std/termination.dag and is
a dissolution target. Per P5 (Progress Is Dissolution): scaffolds need
explicit dissolution paths. Classifying SuperHops 🟢-terminal forecloses
the dissolution path — wrong classification.

Fix: rewrite the Practice-4 ledger to be 🟡 SCAFFOLD-consistent:
- Header line: `🟡 SCAFFOLD (parallel positive-count encoding pending
  shared substrate authority)` with explicit reference to operator
  BLOCKING + codex BLOCKING that surfaced it.
- Pattern-1 (Fact placement): updated to note the consumer surface is
  invariant under future dissolution.
- Pattern-3 (Algebraic form): CORRECTED from "N/A by construction"
  (which was the false claim a 🟢-terminal would justify) to "PARTIAL
  — the genuine parent exists in v3-scaffold form at
  dsl/std/termination.dag, this is the bridge to the shared
  authority." Per M9, the parent IS the shared positive-count
  substrate; the local encoding is the bridge.
- Pattern-5 (Parameterized family): updated to note the shape WOULD
  project from a richer `F<X>` (refinement over shared positive-count
  carrier) when that lands — that's the dissolution path.
- Terminal line: replaced "Terminal:" with "🟡 SCAFFOLD:" with the
  three bridge properties (scaffold doc / producer bounds /
  dissolution trigger).

Also removed the now-redundant 🟡 SCAFFOLD note that lived as a
separate block ABOVE the type declaration (added in the prior commit).
The integrated 🟡-ledger replaces it; double-documentation drift
forbidden.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED post-fix.
- Comment-only change; no structural carrier changes.
- M9 honored: SuperHops's genuine parent (shared positive-count
  substrate) is named explicitly; the bridge classification is
  consistent through the ledger.
- P5 honored: dissolution trigger named, three bridge properties
  documented.

"+1 more queued" per the dashboard relay — standing by.

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

* v4 T-4 rust: fix BLOCKING #3174 — explicit self-prefixed canonicalization rules at PubInPath

Operator BLOCKING inline review 2026-05-16T06:26:19Z at line 1110:

> "The model treats every pub(in self::...) path as unrepresentable,
> but Rust allows self as the first SimplePath segment and super after
> it, so valid aliases like pub(in self::super) have no required
> canonicalization path under extdeps fidelity/P2."

Verified — the prior `root` field doc at line 1108 said "The Self
anchor is structurally excluded (PubInRoot has no Self variant)"
without immediately naming that VALID self-prefixed source paths
(`pub(in self::super)`, `pub(in self::super::super::M)`, etc.) DO have
a required canonical PubInPath / Private destination — the parser /
resolver is OBLIGATED to apply that canonicalization. A reader of the
line-1108 statement could infer "all self-prefixed paths are
unrepresentable visibilities," which is FALSE: `self::super` resolves
to `super` per Reference §paths, so `pub(in self::super)` IS a valid
form with canonical PubInPath { root: AtSuperChain { hops: OneSuper },
suffix: [] }.

The canonicalization rules table I added in the prior commit (post-
PubInPath declaration) DID include `pub(in self::super) →
AtSuperChain { OneSuper }`, but the line-1108 doc didn't reference it
and the table didn't enumerate deeper self-prefix forms explicitly.

Fix in two parts:

(1) Extended the PubInPath.root field doc at line 1108 to explicitly
    state: this is NOT a claim that all self-prefixed Rust source
    paths are unrepresentable; Rust does admit `self` as first
    SimplePath segment; the canonical destination for VALID
    self-prefixed forms is enumerated in the canonicalization rules
    table below. The PARSER/RESOLVER is obligated under extdeps-
    fidelity / P2 to apply the canonicalizations before constructing
    the carrier; the carrier itself represents the post-resolution
    canonical scope, not the source SimplePath syntax.

(2) Extended the canonicalization rules table to cover:
    - `pub(in self::super::super)` → AtSuperChain { MoreSuper { outer:
      OneSuper } } (n-hop super chain after a leading self)
    - `pub(in self::super::M::…)` → AtSuperChain { chain } + suffix
      [M, …] (super-chain followed by named tail)
    Plus clarified the descendant-rejection case (`pub(in self::M)`
    where M resolves as a child) by naming WHY it's a descendant.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED post-fix.
- Comment-only change; structural carrier (PubInPath / PubInRoot /
  SuperHops / NamedSegment) unchanged.
- Each VALID self-prefixed Rust source SimplePath form has a NAMED
  canonical destination in the rules table; the parser/resolver
  obligation is explicit (extdeps fidelity / P2 contract).
- Reference fidelity restored: `self::super` resolves to `super` per
  §paths; canonical PubInPath represents the resolution-side form.

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

* v4 T-4 rust: fix BLOCKING #3174 — two more self-prefix overclaims in RustVisibility doc

Operator BLOCKING inline review 2026-05-16T07:30:47Z (verbatim same finding
as 06:26:19Z — line 1110 fixed in efab8f99d, now re-flagged at line 1261
because the same overclaim shape appeared in two other locations I missed).

Verified — two occurrences of the same defect:

(1) Lines 1262-1264 in RustVisibility's opening doc block:
    "but `pub(in self)` / `pub(in self::...)` is STRUCTURALLY
    UNREPRESENTABLE (PubInRoot has no Self anchor, and SuperHops has no
    zero-hop case — type-level enforcement of pub(self) ≡ Private, NOT
    a documentation-only invariant)."
    Wrong: `pub(in self::super)` IS valid Rust and DOES have a canonical
    destination (`AtSuperChain { OneSuper }`).

(2) Lines 1322-1328 in PubIn variant doc:
    "Multi-segment paths starting with `self` (e.g.
    `pub(in self::outer_mod)`) are likewise out-of-scope for this
    carrier — PubInRoot has no Self anchor, so they cannot be
    constructed at all; whatever upstream parser/resolver does with
    such source is its concern, not modeled here."
    Wrong: same overclaim — conflates the descendant case
    (parse-rejected) with valid `pub(in self::super)` (canonicalizes).

Fix at both locations:

- Replace the blanket "ALL self::... is unrepresentable" claim with the
  three-case enumeration: `pub(in self)` ≡ Private; `pub(in self::super)`
  ≡ `pub(in super)` (canonicalizes via the rules table); deeper forms
  follow the table; `pub(in self::descendant)` is parse-rejected per
  ancestor rule.
- State explicitly that the parser/resolver applies canonicalization
  BEFORE constructing PubInPath (extdeps-fidelity / P2 obligation;
  canonicalizations are REQUIRED, not optional).
- Cross-reference the canonicalization rules table after the PubInPath
  declaration so readers find the per-source-form destinations.
- Cite both BLOCKING review timestamps (06:26:19Z + 07:30:47Z) in the
  history note.

The earlier fix at line 1110 (commit efab8f99d) addressed the SAME defect
in the PubInPath.root field doc; this commit catches the two other
locations that carried the same overclaim. Should have been a single
sweep; lesson is to grep for the overclaim shape, not just fix the
flagged line.

Test plan:
- v2-compiler compile --source-root src/v4 --target dag → 0 diagnostics.
  VERIFIED post-fix.
- Comment-only change; structural carrier unchanged.
- All three doc-comment occurrences of the "Self anchor exclusion"
  framing now consistently name the canonicalization path for VALID
  self-prefixed source forms (extdeps-fidelity / P2 honored).

"+2 more queued" per the dashboard relay — standing by.

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

* v4 T-4 rust: fix BLOCKING #3174 — context-dependent canonicalization for pub(in path) ≡ current-module ≡ Private

Operator BLOCKING inline review 2026-05-16T07:30:48Z at line 1174:

> "The table maps crate-rooted paths with suffix directly to PubIn, so
> a current-module scope like pub(in crate::a::b) inside crate::a::b
> remains representable separately from Private, violating INVARIANTS
> P2 single authority and Rust Reference visibility semantics."

Verified — the canonicalization rules table at line 1174 maps
`pub(in crate::M::N::…)` → `PubInPath { root: AtCrate, suffix: [M, N, …] }`
unconditionally. But per Reference visibility semantics, if the
declaring item lives INSIDE `crate::M::N::…` (i.e., that path IS the
current module of the item, reflexively an ancestor but not a strict
ancestor), then `pub(in crate::M::N::…)` is semantically equivalent to
`pub(self)` ≡ no-modifier ≡ Private.

Two structural representations of the same scope:
- `Private` (no-modifier scope)
- `PubInPath { root: AtCrate, suffix: [M, N, …] }` for item in `crate::M::N::…`

This is the P2 duplicate-authority defect — same axis as the
`pub(self)` ≡ Private case I already canonicalized, but
CONTEXT-DEPENDENT (requires knowing the item's containing module).

The canonicalization is structurally hard to enforce because the
carrier doesn't carry module context (that's runtime/parse-time
information). Same shape as how `pub(self)` canonicalization is a
producer obligation, not a type-level fact — except this one is
parameterized by declaration context.

Fix:

(1) Updated the `pub(in crate::M::N::…)` row in the canonicalization
    rules table to name the strict-ancestor-vs-current-module
    distinction. If the resolved path is the EXACT current module
    (item declared INSIDE `crate::M::N::…`), canonicalize to Private;
    if a strict super-module, keep as PubIn with crate-rooted path.

(2) Updated the `pub(in foo::bar)` (arbitrary-resolution) row in the
    same way — strict ancestor → PubIn; current module → Private.

(3) Added an explicit CONTEXT-DEPENDENT CANONICALIZATION INVARIANT
    block right after the table, naming this as the carrier's P2
    discipline: the no-modifier scope has EXACTLY ONE structural
    representation (Private); any pub(in path) source form whose
    resolved path is the item's current module MUST canonicalize to
    Private regardless of syntactic form. The structural carrier
    doesn't know "current module" — that's resolver context — so
    the canonicalization is a producer obligation.

This is a comment-only update; the carrier's structural shape is
correct, but the parser/resolver canonicalization contract needed
the context-dependent case spelled out explicitly so producers know
to apply it.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir /tmp/v4-stage1-rust
  --target dag → 0 diagnostics. VERIFIED.
- The canonicalization-rules table now correctly handles the
  reflexive-ancestor case (path === current module → Private) for
  both crate-rooted and arbitrary-rooted forms.
- P2 single-authority restored on the no-modifier visibility scope:
  any source form resolving to the current module canonicalizes to
  Private as the unique structural representation.

"+1 more queued" per the dashboard relay — standing by.

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

* v4 T-4 rust: Bool row spec pointer — single authority (std/logic.dag) not duplicate Wikipedia anchor

Cursor APPROVE_WITH_COMMENTS 2026-05-16T07:38:25Z, exploratory observation:

> "The IR-side spec pointer for the Bool row cites Wikipedia for Boolean
> algebra while the real authority is std/logic / the pinned logic anchor;
> harmless as extra color, but future edits could drop the Wikipedia line
> to keep a single external spec story per row."

Verified. The Bool row's "Spec (IR side)" line cited both
`https://en.wikipedia.org/wiki/Boolean_algebra` AND `std/logic.dag header`.
std/logic.dag itself carries that Wikipedia URL as its own `Anchor:` line
(the L-2 spec authority); pointing at the same URL from the Bool row in
rust.dag duplicates the anchor — the kind of two-authority drift this
file's modeling discipline forbids elsewhere.

Fix: replace the duplicate-anchor pair with a single `std/logic.dag` line
that explicitly delegates to its `Anchor:` for the upstream reference.
One external-spec authority per row.

Test plan:
- v2-compiler compile --source-root src/v4 --target dag → 0 diagnostics.
  VERIFIED.
- Comment-only change.
- Single-authority discipline applied to the Bool row's IR-side spec
  pointer.

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

* WIP: T-4 extdeps/languages

* v4 T-4 rust: fix BLOCKING #3174 — collapse PubInPath to absolute-path-from-crate-root (openai-pro REQUEST_CHANGES)

openai-pro/gpt-5.5-pro REQUEST_CHANGES 2026-05-16T08:36:04Z (carries
higher weight than codex per policy):

> "PubInPath is introduced as the post-resolution canonical visibility
> carrier, but the shape still permits two structural values for the
> same resolved visibility scope. ... For an item inside
> crate::outer_mod::inner_mod, `pub(in crate::outer_mod)` and
> `pub(super)` denote the same ancestor scope, but this carrier can
> encode them differently. ... The canonical carrier should key the
> resolved module once, not preserve absolute-vs-relative source
> spelling in the canonical value."

Verified — head-rooted `PubInPath { root: PubInRoot, suffix:
List<NamedSegment> }` admitted source-spelling-distinct but
resolved-scope-equivalent values:
- `pub(super)` for item in `crate::a::b` → `PubInPath { root:
  AtSuperChain { hops: OneSuper }, suffix: [] }`
- `pub(in crate::a)` for same item → `PubInPath { root: AtCrate,
  suffix: [a] }`

Both denote the parent-module scope. SAME resolved scope, TWO
structural authorities = P2 violation. The carrier's own invariant
("same resolved scope must canonicalize to the same RustVisibility
value") explicitly forbids this.

The fix is structural: collapse PubInPath to a single absolute-path-
from-crate-root form. Every valid `pub(in <SimplePath>)` resolves to
a unique absolute path under crate; that's the unique canonical form.

REFACTOR:

- `PubInPath { root: PubInRoot, suffix: List<NamedSegment> }`
  → `PubInPath { resolved_path: List<NamedSegment> }`
  (absolute from crate root; empty = crate root ≡ `pub(crate)`).

- `PubInRoot { AtCrate | AtSuperChain { hops: SuperHops } }` DELETED
  (the absolute-vs-relative source-spelling distinction is not
  canonical).

- `SuperHops { OneSuper | MoreSuper { outer: SuperHops } }` DELETED
  (no super-chains in canonical form; the resolver computes the
  absolute path by walking the module tree).

- `NamedSegment { id: Symbol }` UNCHANGED (segments inside the
  absolute path).

- `RustVisibility.PubIn { path: PubInPath }` UNCHANGED (the carrier
  reference; only PubInPath's shape changed).

CANONICALIZATION RULES TABLE rewritten for the absolute form:

  pub(crate) / pub(in crate)  → PubIn { path: { resolved_path: [] } }
  pub(in <SimplePath>)        → resolver computes absolute-from-crate-
                                root path; if it's a strict ancestor,
                                PubIn { path: { resolved_path:
                                <absolute> } }; if it IS the item's
                                current module, Private (reflexive
                                case); if non-ancestor, parse-
                                rejected.

  For item in `crate::a::b::c`:
    pub(super)                 → resolved_path: [a, b]
    pub(super::super)          → resolved_path: [a]
    pub(in crate::a)           → resolved_path: [a]   ← SAME canonical
                                                         value as
                                                         pub(super::super)
    pub(in crate::a::b::c)     → Private (reflexive)
    pub(in self::super)        → SAME as pub(super)
    pub(in crate::other)       → REJECTED (non-ancestor)

Single-authority via the absolute form: source-spelling-distinct but
resolved-scope-equivalent forms ALL canonicalize to the SAME
PubInPath value. P2 type-enforced; no "two structural authorities for
same scope" possible.

DOC CHANGES:

- Header Owns line: updated to describe the new shape (absolute-path-
  from-crate-root + named segments; no PubInRoot / SuperHops).

- PubInPath block: complete rewrite. Names the canonical-shape
  rationale + cites the openai-pro REQUEST_CHANGES + names the
  deleted carriers as non-canonical source-spelling artifacts +
  notes future-substrate option for a pre-resolution syntactic
  carrier if IDE / diagnostic-span use cases need it.

- RustVisibility block: doc-comment updated throughout to describe
  absolute-path form. BLOCKING-correction history extended with
  round-5 note explaining the absolute-path refactor as the final
  fix. Pattern-5 ledger updated. Terminal line updated. Well-
  formedness paragraph rewritten to describe absolute-form
  enforcement.

- Canonicalization rules table rewritten end-to-end.

The 5-pattern Practice-4 ledger COUNT changes:
- DELETED: SuperHops's ledger (one fewer Disj with ledger)
- DELETED: PubInRoot's ledger (one fewer Disj with ledger)
- KEPT: RustVisibility's ledger (Pattern-5 updated)

Historical references to the deleted carriers (in the BLOCKING-
correction history blocks) are intentional audit trail — they
describe past shapes, not current state.

Test plan:
- v2-compiler compile --source-root src/v4 --output-dir
  /tmp/v4-stage1-rust --target dag → 0 diagnostics. VERIFIED.
- Same resolved scope = same canonical PubInPath value (P2 single-
  authority TYPE-ENFORCED via the unique-absolute-path shape).
- No duplicate-authority defect possible: there is no AtSuperChain
  variant to admit a parallel source-spelling representation.
- The reflexive-current-module case (`pub(in <path-equals-current>)`)
  canonicalizes to Private upstream; the carrier never sees it.
- File line count decreased substantially (~226 lines deleted from
  the SuperHops + PubInRoot blocks; canonicalization rules table
  also shrank as it no longer enumerates per-super-chain variants).

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

* WIP: T-4 extdeps/languages

* v4 T-4 rust: remove redundant Bool import + reconcile kernel-ambient vs imported framing

Claude APPROVE_WITH_COMMENTS 2026-05-16T09:00:49Z, exploratory observation:

> "The header documents Int, Bool, List as 'kernel-ambient' yet imports
> Bool explicitly from v4.std.logic. The Consumes commentary acknowledges
> this tension but the two framings don't fully reconcile. A future pass
> might pick one model (kernel-ambient vs imported) for the primitives
> the file actually uses."

Verified empirically — Bool IS resolvable without an `import` statement
in t…
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