Skip to content

docs: DB-9 R2.1 — Track 9 substrate integrity primitives graduation - #516

Merged
briansrls merged 4 commits into
mainfrom
db9-r2-1-integrity-primitives
Apr 17, 2026
Merged

briansrls merged 4 commits into
mainfrom
db9-r2-1-integrity-primitives

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

Follow-up to PR #511 (merged at R2 post-review f22d5ecf1). #511's
four review blockers (chatgpt-auto-review REQUEST_CHANGES, codex-review
BLOCKING, three inline BLOCKING comments) all converged on one class:
R2's substrate records admitted illegal states and relied on "enforced
by construction" rather than type-shape enforcement. This PR graduates
four substrate integrity primitives into src/v3/std/substrate.dag
and rewrites the DB-9 R2 design records on top of them.

Before (from merged #511):

type Cluster {
  members: List<MemberDescent>                    // admits [] and singletons
  intra_cluster_calls: List<IntraClusterCall>     // admits []
}
type MemberDescent {
  member: NodeId
  position: Int                                   // admits negative / out-of-arity
}
type IntraClusterCall {
  transform: NodeId                               // admits non-Transform
}

After (this PR):

type Cluster {
  members: NonSingletonList<MemberDescent>              // ≥2 by type shape
  intra_cluster_calls: NonEmptyList<IntraClusterCall>   // ≥1 by type shape
}
type MemberDescent {
  member: NodeId
  position: ArityIndex                                  // typed newtype over NonNegativeInt
}
type IntraClusterCall {
  transform: TransformRef                               // typed newtype witnessing Behavior::Transform
}

What lands

  1. Four substrate integrity primitives in src/v3/std/substrate.dag:

    • NonEmptyList<T> — cardinality ≥ 1 by type shape
    • NonSingletonList<T> — cardinality ≥ 2 by type shape
    • ArityIndex — typed newtype over NonNegativeInt; paired with arity-bearing carrier
    • TransformRef — typed newtype over NodeId that statically witnesses Behavior::Transform
  2. DB-9 R2.1 doc rewrite (docs/design-mutual-recursion-lowering.md):

    • Substrate record declarations use the new primitives
    • Dissolution-receipt paragraph rewritten — no more "enforced by construction" language
    • Revision history + Changelog entries for R2.1
    • Rejected-alternatives row explaining why construction-only invariants were rejected
    • Associations section updated
  3. ROADMAP Track 9 entry (ROADMAP.md) — substrate integrity primitives graduation, primitive ledger, planned second consumer (IndexedElement<T>.index in src/v3/std/list.dag), and explicit deferral of the deeper Dag.nodes segregation refactor.

Why a follow-up PR

#511 merged at R2 while the R2.1 design discussion was in progress. The
graduation work is independently valuable (gates any future consumer,
including IndexedElement cleanup) so it lands as a clean follow-up
rather than an amendment to the merged history.

Test plan

  • cargo test -p v3-compiler --test m2_substrate_inhabitance_test (3/3 pass — reflection surface, coproduct/runtime carrier parity, Rust/reflected parity)
  • cargo test -p v3-compiler --test real_stdlib_parse_smoke (9/9 pass)
  • cargo test -p v3-compiler --test l1_5_fixed_point_test pipeline_dag_parses (1/1 pass)
  • cargo fmt --all --check (pre-push hook)
  • CI green end-to-end
  • Review: confirm the four original blockers (from docs: DB-9 R2 — mutual recursion lowers to substrate Loop #511 comments 3101980249, 3101980252, 3102304401, and the chatgpt-auto-review/codex-review on f22d5ecf1) are now answered at the type shape rather than at the producer

🤖 Generated with Claude Code

…rimitives

Closes the four blockers on PR #511 post-review (chatgpt-auto-review
REQUEST_CHANGES, codex-review BLOCKING, three inline BLOCKING review
comments). All four converged on one class: R2's substrate records
(Cluster, MemberDescent, IntraClusterCall) admitted illegal states
(empty/singleton members, negative position, non-Transform handle)
and relied on "enforced by construction" rather than type-shape
enforcement.

R2.1 graduates four primitives into src/v3/std/substrate.dag and
consumes them from the R2 records:

  - NonEmptyList<T>          — Cluster.intra_cluster_calls
  - NonSingletonList<T>      — Cluster.members
  - ArityIndex               — MemberDescent.position
  - TransformRef             — IntraClusterCall.transform

Illegal states are now unrepresentable by the type shape; the
"enforced by construction" language is gone from the dissolution
receipt and replaced with type-level answers.

ROADMAP Track 9 entry added with primitive ledger and the planned
second consumer (IndexedElement<T>.index in src/v3/std/list.dag).
The deeper substrate refactor (segregating Dag.nodes by kind) is
explicitly deferred as an independent track.

Verified: m2_substrate_inhabitance_test (3/3), real_stdlib_parse_smoke
(9/9), l1_5_fixed_point_test::pipeline_dag_parses — all pass.

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

briansrls commented Apr 17, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Principle audit.

1. FAIL-CLOSED. This looks okay in the landed diff. I do not see a new silent-success path or a “keep going with placeholder state” move; the round is mostly deleting “enforced by construction” prose and trying to surface invariants structurally. The only thing I would watch later is the sketched as_transform(dag, id) -> TransformRef? constructor: when that lands, a bad cast should have a crisp failure story, not a silent None that callers can blur into “not applicable.”

2. ILLEGAL STATES UNREPRESENTABLE. Partially satisfied, with one BLOCKING concern. NonEmptyList<T> and NonSingletonList<T> are real wins: empty and singleton cases move onto the shape itself. But MemberDescent.position is overclaimed. The doc now says negative and out-of-arity positions are “unrepresentable by the type shape” (docs/design-mutual-recursion-lowering.md, diff lines 60, 85-88, 111-116), while the new ArityIndex block explicitly says bound-relative validity is still checked “at the construction site that pairs an ArityIndex with its carrier” (src/v3/std/substrate.dag, diff lines 188-197). That means the type makes non-negativity structural, but the member-relative bound is still behavioral/API-level, not in the data model.

3. FACTS FLOW FORWARD. Satisfied. This round is materially better than R2 on fact preservation: IntraClusterCall stays anchored on the authoritative Transform rather than compressing to caller/callee, and the Track 9 ledger makes the new carriers explicit instead of leaving them as unwritten convention. I do not see a new silent fact-drop in the changed material.

4. COPRODUCT DISSOLUTION. Satisfied. No new substrate enum is introduced here, so there is no new N≥2 Rust enum that needed a fresh terminal/scaffold/dissolve-now receipt. The existing LoopBound receipt remains in-doc, which is the right shape for a substrate stop-signal.

5. SINGLE AUTHORITY. Satisfied. The shift from raw call-edge facts to TransformRef is the right direction: the authoritative callsite stays the Transform node, not a parallel edge payload. I do not see a second source of truth being introduced by NonEmptyList/NonSingletonList; the only issue is that ArityIndex still leaves one important relation outside the shape, not that it duplicates the fact.

6. API-LEVEL ENFORCEMENT. Same BLOCKING issue as #2. NonEmptyList and NonSingletonList are genuinely API-level. ArityIndex is not fully there yet: the new type stops negative integers, but the crucial invariant for this consumer is “this index is valid for this member,” and the diff still places that in constructor prose rather than in the type. So the current check is mixed: structural for cardinality and sign, behavioral for member-relative arity.

Design question. Does MemberDescent really want an ArityIndex, or does it want a first-class reference to an actual formal parameter of member? That is the crux, because if the real invariant is “this points to one of this member’s parameters,” then a bare index still makes downstream code trust a relation that is not carried by the shape and will be tempting to re-check or re-derive later.

Verdict. REQUEST_CHANGES. This is directionally strong, and I think the NonEmptyList/NonSingletonList part is a clean improvement. But I would not bank the “four blockers closed” / “out-of-arity is now unrepresentable by the type shape” claim until either the carrier is strengthened or the docs are softened to say only non-negativity moved into the type.

LOOP HEALTH: converging — this round clearly dissolves real construction-only debt and turns a repeated review theme into an explicit Track 9 pattern, but the ArityIndex half still stops short of the fully structural guarantee the doc now claims.


View conversation

@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 · 1ef38dbd

⚠️ Review (blocking: 1, non-blocking: 1+/0-)

BLOCKING (1)

Root Cause

  • src/v3/std/substrate.dag primitive declarations landed without the matching consumer-side substrate migration -> either land the actual Cluster/MemberDescent/IntraClusterCall hookup in this PR or rewrite the new comments/docs/roadmap as planned future consumers

Non-blocking — Strengths

  • docs/design-mutual-recursion-lowering.md The R2.1 rewrite cleanly retires the old raw-carrier shape at the design level and keeps the five-Behavior constraint intact.

ROADMAP — Verified

  • Lane 3 Stage 3a.1 direction: The docs stay aligned on lowering mutual recursion through Loop instead of drifting back to the rejected lens-level SCC design.

ROADMAP — Incomplete

  • Track 9 substrate integrity primitives graduation: The ledger is ahead of the substrate authority: the primitives are present, but their advertised first consumers are not yet reflected in src/v3/std/substrate.dag.

⚠️ The modeling direction is right, but this PR currently records Track 9 as graduated before the substrate authority actually uses the new carriers.

Comment thread src/v3/std/substrate.dag Outdated
// (empty lists, negative indices, mis-kinded handles). These four
// primitives make the invariants structural so the offending states
// are unrepresentable by the type, not merely avoided by the
// producer. Consumers so far: `Cluster` / `MemberDescent` /

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.

BLOCKING: The new header says these primitives already serve Cluster/MemberDescent/IntraClusterCall, but the checked-in substrate still defines none of those consumers, so the authority file overstates Track 9 graduation and violates thesis-verification/single-authority discipline.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 1ef38dbd

⚠️ Review (blocking: 1, non-blocking: 1+/0-)

BLOCKING (1)

Root Cause

  • src/v3/std/substrate.dag primitive declarations landed without the matching consumer-side substrate migration -> either land the actual Cluster/MemberDescent/IntraClusterCall hookup in this PR or rewrite the new comments/docs/roadmap as planned future consumers

Non-blocking — Strengths

  • docs/design-mutual-recursion-lowering.md The R2.1 rewrite cleanly retires the old raw-carrier shape at the design level and keeps the five-Behavior constraint intact.

ROADMAP — Verified

  • Lane 3 Stage 3a.1 direction: The docs stay aligned on lowering mutual recursion through Loop instead of drifting back to the rejected lens-level SCC design.

ROADMAP — Incomplete

  • Track 9 substrate integrity primitives graduation: The ledger is ahead of the substrate authority: the primitives are present, but their advertised first consumers are not yet reflected in src/v3/std/substrate.dag.

⚠️ The modeling direction is right, but this PR currently records Track 9 as graduated before the substrate authority actually uses the new carriers.

…h the consumers

CI exposed a pre-existing v3 compiler limitation triggered by the
new primitives in substrate.dag: declaring generic Conj records with
nested `List<element>` fields (`NonEmptyList<T>`, `NonSingletonList<T>`)
makes the testgen lens emit a "NonEmptyList<Int> compiles" claim with
source `let witness: NonEmptyList<Int> = { head: 1, tail: empty() }`,
which hits a ResolveError on implicit template binding when v3 tries
to unify the nested `List<element>` parameter.

That's a real compiler gap but it's out of scope for a doc PR. The
simpler correct answer: this is pure design work. The primitive
declarations should land with their first consumers (DB-9 Lane 3
Stage 3a.1 implementation PR) — primitives without consumers would
be decorative and the Rust realization has to land at the same time
for the invariants to actually hold.

Changes:
  - Remove the four primitive declarations from src/v3/std/substrate.dag
    (NonEmptyList<T>, NonSingletonList<T>, ArityIndex, TransformRef)
  - Update R2.1 design doc to note primitives land with the
    implementation PR
  - Update ROADMAP Track 9 entry to status 🟡 Design with the same note
  - Keep the R2.1 substrate record rewrites in the doc (Cluster,
    MemberDescent, IntraClusterCall use the new primitive names)
  - Keep the ROADMAP Track 9 primitive ledger + planned second consumer
    (IndexedElement<T>.index)

Verified: m1_5_testgen_test (3/3), m2_substrate_inhabitance_test (3/3),
real_stdlib_parse_smoke (9/9), l1_5_fixed_point_test pipeline parse.

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

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

This is the substrate integrity primitives graduation we discussed at the cross-PR level — the structural answer to the "lossy compression at substrate" class that surfaced in #511, #513, and #515 review loops. Doc PR (ROADMAP + design-mutual-recursion-lowering.md), CI green, mergeable.

Anchored in INVARIANTS.md L:992 ("every fact in one place") and L:2022 ("make illegal states unrepresentable").

Structural assessment — right move

Picks "tighten with reusable primitives" over "dissolve specific records" (the alternative I'd floated on #511). The primitive route is arguably cleaner because the carriers are reusable: TransformRef is the next typed-NodeId story, NonSingletonList is the next "binary-relation carrier" story, ArityIndex is the next index-into-arity-bearing-thing. Not bespoke for DB-9.

Strengths worth banking:

  • Replaces "enforced by construction" dissolution receipts with type-level ones — exactly what the prior round's review found missing.
  • Names a planned second consumer (IndexedElement<T>.index in std/list.dag) — proves the primitives are general, not single-use.
  • Rejected Dag.nodes: List<Behavior> → per-kind lists explicitly with reasoning ("Rust-side consumer migration is independent of this doc-level graduation; file separately when a consumer benefits"). That's graduation discipline, not scope creep.
  • Closes the cross-PR class as a Track 9 ledger entry instead of a feedback memo — the right home for an invariant that lives in the substrate vocabulary.

Codex blocker — labeling, not structural

Codex says "primitive declarations landed without the matching consumer-side substrate migration → either land the actual hookup or rewrite as planned future consumers."

But the doc DOES already mark this as design-only:

  • ROADMAP entry status: 🟡 Design
  • ROADMAP entry text: "Declarations land in src/v3/std/substrate.dag with the DB-9 Lane 3 Stage 3a.1 implementation PR (same commit as the first consumers — the primitives without consumers would be decorative)."

So codex's prescription "land OR rewrite as planned" is already satisfied by the second branch. The misread is probably driven by:

  1. Section header "substrate integrity primitives graduation" (reads past-tense)
  2. Changelog line "R2.1 approved" (reads landed)
  3. Status badge "🟡 Design" without the explicit "no code yet" qualifier

Recommended tightening (5-10 line doc edit)

  1. Track 9 section header — add scope qualifier:

    **Surfaced by PR #511 review loop (2026-04-17) — substrate integrity
    primitives graduation (design only; declarations land with DB-9 impl PR):**
    
  2. One leading sentence under the table to set expectation before any reader scans into details:

    This entry is a design ledger; the primitive declarations have not been
    written to `src/v3/std/substrate.dag` yet — they land alongside their
    first consumers in the DB-9 Lane 3 Stage 3a.1 implementation PR.
    
  3. Status line — bolder:

    **Status:** 🟡 Design only — no `substrate.dag` changes in this PR.
    Declarations land alongside first consumers in the DB-9 implementation PR.
    
  4. Changelog R2.1 entry — one word change:

    - **2026-04-17 R2.1 (design)** — graduated substrate records onto Track 9...
    

No structural change. Just makes the design-vs-landed boundary impossible to misread.

Verdict

APPROVE after the labeling tightening. The codex blocker is mechanical; structurally this is exactly the right close for the cross-PR pattern. Once landed, this also unblocks the DB-9 Lane 3 Stage 3a.1 impl PR with a much stronger contract — the impl PR can simply land the primitives + consumers as one structurally-honest substrate addition, rather than re-litigating shape decisions.

…Index

Addresses the ChatGPT REQUEST_CHANGES review on PR #516 sha 1ef38db.

The review correctly flagged that `MemberDescent.position: ArityIndex`
only moved non-negativity into the type shape — the critical invariant
("position is a valid index into THIS member's parameter list") still
sat outside both field types, so the R2.1 dissolution-receipt claim
that "out-of-arity is unrepresentable by the type shape" overclaimed
the guarantee. The member-relative bound was still constructor-time
prose on MemberDescent itself, i.e. the exact "enforced by construction"
pattern Track 9 exists to dissolve.

Fix:
  - MemberDescent { member: NodeId, position: ArityIndex }
  → MemberDescent { param: ParamRef }

  `ParamRef` is an opaque typed handle whose sole producer is a
  fallible `param_of(member: NodeId, slot: Int) -> ParamRef?`. The
  constructor returns None for any non-Bind `member` or any slot
  outside the member's parameter arity, so a MemberDescent value
  statically witnesses BOTH "this is a valid member node" AND "this
  is a valid parameter of that member." The relation lives on the
  handle, not on MemberDescent's constructor.

  `member` and `slot` are recoverable via ParamRef accessors rather
  than duplicated as MemberDescent fields — single authority for
  "which parameter of which member" lives on the handle.

Track 9 updates:
  - ArityIndex removed as a standalone primitive (rejected: graduates
    only non-negativity; leaves the bound-relation anti-pattern intact).
    May exist as an INPUT to param_of, not a stored substrate field.
  - ParamRef added to the ledger with the "typed reference into
    authoritative indexable carrier" framing.
  - Planned second consumer updated: IndexedElement<T>.index would
    graduate into an analogous ElementRef<T> handle, not ArityIndex.
  - Explicit note that ElementRef is not pre-declared — handles land
    with consumers, not speculatively.

Doc updates throughout:
  - Lowering shape illustrated: MemberDescent uses param_of(..).unwrap()
  - Lowering consumer steps: added ParamRef construction step
  - Termination lens consumer description: reads member + slot via
    ParamRef accessors rather than separate fields
  - Error reporting: diagnostic names failing TransformRef + caller's
    ParamRef (slot recoverable via ParamRef.slot_of)
  - Acceptance criteria: NonSingletonList/NonEmptyList + ParamRef/
    TransformRef called out explicitly
  - Rejected-alternatives: new row documenting the ArityIndex +
    separate member approach and why it was rejected
  - Open questions: signature-alignment note updated to reflect
    ParamRef captures the general case cleanly

Verified: m1_5_testgen_test (3/3), m2_substrate_inhabitance_test (3/3),
real_stdlib_parse_smoke (9/9), l1_5_fixed_point_test pipeline parse.

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

briansrls commented Apr 17, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Principle audit.

1. FAIL-CLOSED. Satisfied. For a docs-only round, this is in good shape: the new story is “opaque typed handle with a fail-closed constructor,” not “raw Int/NodeId plus trust the producer.” I do not see a new silent-success path being normalized here.

2. ILLEGAL STATES UNREPRESENTABLE. Satisfied, and this is the big win in the round. The earlier ArityIndex problem is genuinely gone: NonEmptyList<T>, NonSingletonList<T>, ParamRef, and TransformRef move the key invalid states onto the shape instead of leaving them in constructor prose. That matches both the thesis’s decompression direction and the discipline’s “illegal states unrepresentable” bar.

3. FACTS FLOW FORWARD. BLOCKING. The storage shape is improved, but one consumer paragraph now seems semantically inconsistent with the fact it just preserved. The doc explicitly says mixed-position SCCs are real and that MemberDescent.param is per-member to capture them (docs/design-mutual-recursion-lowering.md, diff lines 138-139). But the termination-lens paragraph then says the lens should verify “the argument at the caller-member’s descent slot” against “the caller’s parameter at the same slot” (docs/design-mutual-recursion-lowering.md, diff lines 178-179). That drops the callee-side slot fact the new ParamRef carrier was introduced to preserve. If caller and callee descend on different positions, the consumer as written checks the wrong argument slot. I think the lens needs to say it resolves both members’ ParamRefs and compares the argument feeding the callee’s descent param against the caller’s descent param.

4. COPRODUCT DISSOLUTION. Satisfied. No new substrate enum is being added here, and the round actually improves the decomposition story by explicitly demoting ArityIndex from “primitive” to “at most an input to a constructor.” That is the right use of the Track 9 lens.

5. SINGLE AUTHORITY. Satisfied. Transform remains the authoritative callsite; ParamRef becomes the authoritative member/slot relation instead of re-storing (member, position) as parallel facts. I do not see a new second source of truth introduced in the diff.

6. API-LEVEL ENFORCEMENT. Mostly satisfied. The invalid-record problem is much better now: the type shape blocks the old empty/singleton/out-of-arity/mis-kinded states. My only reservation is the same one as #3: the API now gives you the right handles, but the consumer prose still needs to use both handles correctly or it semantically collapses back toward a single-slot rule.

Design question. Is the descent check keyed by the caller’s chosen parameter, the callee’s chosen parameter, or the ordered pair of both? That is the crux, because ParamRef only earns its keep if the lens compares the argument passed into the callee’s designated descent param against the caller’s designated descent param; otherwise heterogeneous per-member measures are still being flattened in consumer logic.

Verdict. REQUEST_CHANGES. This is materially better than the earlier draft, and I think the ParamRef replacement is the right fix for the prior blocker. But I would correct the termination-lens paragraph before merging, because right now the doc says mixed-position SCCs are supported while the consumer prose appears to validate only caller-aligned positions.

LOOP HEALTH: converging — this round cleanly closes the earlier ArityIndex overclaim and makes honest forward progress on Track 9, but one last consumer-side wording fix is needed so the new carrier is consumed with the generality the doc claims.


View conversation

@briansrls

Copy link
Copy Markdown
Contributor Author

Meta-review in progress... (view conversation)

Loop-health check: is this review cycle making forward progress, or shifting debt? Posts in ~5-15 minutes.

… ParamRef at each edge

ChatGPT REQUEST_CHANGES on PR #516 sha 3910d44 correctly caught that
the termination-lens paragraph still described the descent check with
a single-slot formulation ("argument at caller's descent slot vs
caller's param at same slot"). That silently collapses mixed-position
SCCs (e.g. process/help where a's descent is slot 1 and b's is slot 0)
back to single-position validation — the new per-member ParamRef
carrier would do no work, and the lens would mis-check the wrong
argument on heterogeneous SCCs.

Fix: rewrite the termination-lens paragraph to walk BOTH members'
ParamRef handles at each IntraClusterCall edge:

  caller_slot = caller's MemberDescent.param.slot_of()
  callee_slot = callee's MemberDescent.param.slot_of()

  check: argument at the Transform's callee_slot position is
         structurally smaller than the caller's formal parameter
         at caller_slot

The check crosses the caller/callee boundary that ParamRef was
introduced to preserve — callee_slot selects WHICH ARGUMENT is being
fed into the callee's descent param, caller_slot selects WHICH FORMAL
is the caller's decreasing measure.

Also updated:
  - Error reporting example now shows both slot labels in the
    diagnostic body (useful in mixed-position SCCs where they differ)
  - Acceptance criteria: negative-fixture diagnostic must name BOTH
    ParamRef handles; added a mixed-position positive-fixture
    acceptance to regression-guard the callee-side slot usage
  - Changelog entry for this second ChatGPT-review follow-up

Verdict on original review: ChatGPT's principles 1, 2, 4, 5 satisfied
already. Principles 3 + 6 (the single BLOCKING class) flip green with
this commit — the API's dual handles are now consumed with the
generality the doc claims.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls
briansrls merged commit 8fdb19c into main Apr 17, 2026
3 checks passed
@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

briansrls added a commit that referenced this pull request Apr 18, 2026
Reviewer (commit 697c3a5) flagged two BLOCKING single-authority slips
where this snapshot drifted from ROADMAP.md (the root doc, not the
src/v3 sub-tracker I initially referenced):

1. #516 row listed Track 9 primitives as NonEmptyList/NonSingletonList/
   ArityIndex/TransformRef. Authoritative list at ROADMAP.md:765-768 is
   NonEmptyList<T>/NonSingletonList<T>/ParamRef/TransformRef. ArityIndex
   was explicitly REJECTED as a standalone primitive at ROADMAP.md:772.
   Row now restates from ROADMAP with line reference, not from memory.

2. §3 XS brief and §5b both instructed a child chat to "add ROADMAP
   entry" for ParamRef/TransformRef constructor-validation asymmetry.
   ROADMAP.md:777-786 already carries that exact debt with the Lane 3c
   graduation trigger. §3 brief now scopes to a substrate.dag
   cross-reference comment only; §5b ParamRef row removed; §5a gains
   the row pointing at the existing ROADMAP entry.

Non-blocking finding also addressed:

3. §6 Q3 flagged compiler.dag's v2-path carryover as "no ROADMAP row
   yet" — a real planning fact stranded mid-doc. Moved to §5b as a
   migration candidate (pending director pre-clearance on whether it's
   a real 3c blocker or a v2→v3 bridging detail).

Root cause (reviewer's deepest point): doc was behaving partly like a
second tracker, partly like a thin read-model. Added explicit
Snapshot-discipline rule: "This doc is a thin read-model over ROADMAP.
When ROADMAP and this doc conflict, ROADMAP is right and this doc must
be fixed — delete the restated fact, replace with a line reference."
Plus fixed the parent link from ../src/v3/ROADMAP.md (the sub-tracker)
to ../ROADMAP.md (the authoritative root).

Also updated §5a to include Track 9 second consumer
(IndexedElement.index → ElementRef) as already-in-ROADMAP
(ROADMAP.md:770 — "deliberately not pre-declared"), removing it
from §5b where I'd incorrectly framed it as needing migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Apr 18, 2026
* Phase plan 2026-04-18 — post-merge-wave coordination snapshot

Lands docs/phase-plan-2026-04-18.md as the director-chat coordination snapshot
for the post-merge-wave phase. Derived from the 2026-04-18 draft with director
review baked in.

What this doc is:
- Snapshot of where the four lanes stand after tonight's 10 merges.
- Paste-ready dispatch briefs for the next batch (§3).
- Pre-clearance tracker for medium-horizon items (§4).
- Stage 3c readiness exploration (§6) — the "strategic horizon" work.
- Operating model for the director + child-chat fan-out pattern (§7).

Director review findings incorporated:
- Staleness: #523 was shown "in flight" but already merged as 977224a —
  doc now reflects merged state with explicit SHA.
- DB-numbering audit: draft reserved DB-18/19/20/21/22 speculatively;
  dropped because DB-7 (symbolic cost), DB-8 (fixed-point ratchet), and
  DB-15 (test-infra R2) already cover the work. Also noted DB-17 is
  allocated to reference-resolution-provenance, so draft's use of DB-17
  for WorkflowEffect would have collided.
- Stage 3c exploration (§6) filled in: compiler.dag is a 310-line cycle
  meta-model (not compiler internals); Lane 1 Stage 1e is a hard blocker
  per lane3-self-hosting-cycle.md; 4 genuinely-open questions named.
- STOP-AND-ESCALATE rule added to each §3 dispatch brief + §7 — child
  chats halt when pre-clearance turns out to have been wrong, rather than
  patching forward and losing the structural signal.
- §5 split: items already in ROADMAP (point to authority) vs migration
  candidates (new from this session); explicit dispatch decision on
  which items go to which chat.

What's next:
- Current §2 dispatches close; §3 briefs go out in order (Python pilot
  gated on PR 2.5).
- §5b migration happens via combined XS brief (§3) + dedicated
  ROADMAP-hygiene chat (NOT folded into the XS brief).
- Next director session commits the first 60 min to §6's open questions
  regardless of tactical surprises.

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

* Address chatgpt-review on #525: fix Track 9 drift + ROADMAP-duplication

Reviewer (commit 697c3a5) flagged two BLOCKING single-authority slips
where this snapshot drifted from ROADMAP.md (the root doc, not the
src/v3 sub-tracker I initially referenced):

1. #516 row listed Track 9 primitives as NonEmptyList/NonSingletonList/
   ArityIndex/TransformRef. Authoritative list at ROADMAP.md:765-768 is
   NonEmptyList<T>/NonSingletonList<T>/ParamRef/TransformRef. ArityIndex
   was explicitly REJECTED as a standalone primitive at ROADMAP.md:772.
   Row now restates from ROADMAP with line reference, not from memory.

2. §3 XS brief and §5b both instructed a child chat to "add ROADMAP
   entry" for ParamRef/TransformRef constructor-validation asymmetry.
   ROADMAP.md:777-786 already carries that exact debt with the Lane 3c
   graduation trigger. §3 brief now scopes to a substrate.dag
   cross-reference comment only; §5b ParamRef row removed; §5a gains
   the row pointing at the existing ROADMAP entry.

Non-blocking finding also addressed:

3. §6 Q3 flagged compiler.dag's v2-path carryover as "no ROADMAP row
   yet" — a real planning fact stranded mid-doc. Moved to §5b as a
   migration candidate (pending director pre-clearance on whether it's
   a real 3c blocker or a v2→v3 bridging detail).

Root cause (reviewer's deepest point): doc was behaving partly like a
second tracker, partly like a thin read-model. Added explicit
Snapshot-discipline rule: "This doc is a thin read-model over ROADMAP.
When ROADMAP and this doc conflict, ROADMAP is right and this doc must
be fixed — delete the restated fact, replace with a line reference."
Plus fixed the parent link from ../src/v3/ROADMAP.md (the sub-tracker)
to ../ROADMAP.md (the authoritative root).

Also updated §5a to include Track 9 second consumer
(IndexedElement.index → ElementRef) as already-in-ROADMAP
(ROADMAP.md:770 — "deliberately not pre-declared"), removing it
from §5b where I'd incorrectly framed it as needing migration.

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

* Address chatgpt-review on b7b4dba: authority attribution + framing

Non-blocking finding from the re-review: DB-17 attribution said
"per ROADMAP §Scheduled deletions" without distinguishing which
ROADMAP or citing the allocating design doc. Reviewer couldn't verify
the attribution in the uploaded ROADMAP file.

Fix: name the allocating authority explicitly
(docs/design-reference-resolution-provenance.md) with the ROADMAP
reference as a secondary pointer, not the primary one.

Design-question finding: doc claimed to be "thin read-model over
ROADMAP" but in practice reads from ROADMAP + DB design docs +
post-l15-phase-plan. Reviewer rightly flagged the framing as
understating what the doc actually depends on.

Fix: broaden the snapshot-discipline framing to "thin read-model
over ROADMAP and the design docs ROADMAP references." Added
explicit authority order on conflict: (1) specific DB design doc
for what it locks, (2) ROADMAP for active-deferrals / structural
debt, (3) lane/phase docs for sequencing. Future readers who hit
conflicting information now know where to resolve from.

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

* WIP: gunbc design advancement

* Address chatgpt-review on 07f3c95: strip citation-count bookkeeping

Non-blocking finding: §1 "structural rule banked" had a Q4/Q3 drift
(§1 said Q4 cited in ComposedEffect; §2 correctly said Q3), and more
fundamentally the citation-count bookkeeping was this snapshot acting
as a second authority for a rule not yet in INVARIANTS.md.

Fix (reviewer's option b — strip bookkeeping until the memo graduates
to a canonical authority):
- §1 banked rule: removed the specific "Q5 in PR 2.5, Q4 in
  ComposedEffect" citation list and the "graduate after Python pilot
  dispatch" counter. Replaced with a brief statement that the memo
  is currently cited from dispatch briefs (§2, §3) and graduation is
  a separate follow-up not tracked from this snapshot.
- §5b: removed the "graduation" migration row for the same reason.
- §5b dispatch decision: recalibrated row count (4 not 5) and
  clarified that 2 rows go to §3 briefs, 1 to a dedicated hygiene
  chat, 1 to director pre-clearance first.

Q-number citations in §2 acceptance and §3 briefs remain — those are
legitimate dispatch acceptance criteria ("cite Q5" / "cite Q3"), not
aggregate bookkeeping. Only the aggregate graduation-tracking from
the snapshot is stripped.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls briansrls mentioned this pull request Apr 18, 2026
@briansrls
briansrls deleted the db9-r2-1-integrity-primitives 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