Skip to content

feat(r3): Gap 4 build-step producer — bounded MethodTemplateContract Map adapter - #1598

Merged
briansrls merged 16 commits into
mainfrom
session/silent-boar-29-pb-gap4-build-step
May 4, 2026
Merged

briansrls merged 16 commits into
mainfrom
session/silent-boar-29-pb-gap4-build-step

Conversation

@briansrls

@briansrls briansrls commented May 3, 2026 •

Copy link
Copy Markdown
Contributor

Scope (accepted disposition)

Gap 4 adapter slice / build-step handoff for legacy Single-template leaf migration. Director / PB acceptance: #issuecomment-4367336134. Per dispatch, the smallest legal v2-consumable build-step hook is the ephemeral Map<String, String> adapter — the alternatives (re-declared carrier, v2→v3.std.* import bridge, Gap 5 substrate rewrite) all hit explicit STOP boundaries.

This PR does NOT claim:

  • Full structural row-85 closure.
  • Full MethodTemplateContract preservation.
  • Higher-order row migration.
  • Gap 5 completion.

Explicitly parked for Gap 5 / substrate design:

  • runtime_template
  • emit_template sum identity (SingleTemplate / HigherOrderTemplates)
  • wraps_result
  • placeholder_convention
  • MethodRef.decl typed identity (only the resolved method-name string lands)
  • Higher-order rows

Boundary discipline (held):

  • ✅ No committed generated .dag. Producer writes into a caller-supplied directory (out_dir) which must be ephemeral (OUT_DIR / temp dir). v2 consumes via --source-root <ephemeral> per PR test(v2): ratchet ephemeral source-root imports #1575.
  • ✅ No v2 → v3.std.* import bridge. The generated module declares its own name (generated.method_template_projection) and uses only kernel shapes (Map<String, String>).
  • ✅ No hand-authored second Map<String, String> authority. The producer's only template-text source is the typed MethodTemplateContract rows in src/v3/std/{rust,python,go}_method_template_contracts.dag.

What lands

Producer (v3-compiler): pb_method_template_projection_dag_emit::write_method_template_projection_dag(dag, out_dir) walks rows via #1568's projection and writes <out_dir>/generated/method_template_projection.dag:

module generated.method_template_projection
data rust_method_template_emit:   Map<String, String> = { "count": "(\{recv\}.len() as i64)", ... }
data python_method_template_emit: Map<String, String> = { ... }
data go_method_template_emit:     Map<String, String> = { ... }

{ / } escaped to \{ / \} for v2's grammar. BTreeMap-backed iteration → deterministic bytes for build-pipeline reproducibility. Fail-closed on legacy map-key collision when two distinct MethodDeclaration bindings project to the same name string within a target (MethodTemplateProjectionDagEmitError::LegacyMapKeyCollision { target, name, first_dag_method, duplicate_dag_method }).

Tests

Split test chain (accepted by Director):

  • v3-side (8 producer tests, 7 integration + 1 in-module unit): path, header authority chain, module + per-target map declarations, Rust/Python/Go count row spot-checks, higher-order rows skipped, deterministic bytes, legacy map-key collision fails closed.
  • v2-side (1 consumability ratchet): uses compile_dag_named_with_source_roots (PR test(v2): ratchet ephemeral source-root imports #1575) with a temp ephemeral source-root carrying the producer's exact shape; asserts entry imports generated.method_template_projection { rust_method_template_emit } cleanly and the generated module is loaded only from the temp root (not src/ / dsl/). Closes the producer ↔ consumer loop within the v2-tests crate (no v3-compiler dep).

PR #1575's stage0_compile_imports_ephemeral_generated_source_root (#[ignore]d, slow stage0 subprocess) covers the slower stage0 case for arbitrary .dag.

Authority

Test plan

  • cargo test -p v3-compiler --test integration pb_method_template_projection_dag_emit — 7/7 pass.
  • cargo test -p v3-compiler --lib pb_method_template_projection_dag_emit — 1/1 pass (collision case).
  • cargo test -p v2-compiler-tests pb_method_template_projection_consumability — 1/1 pass.
  • cargo test -p v3-compiler --test integration sg0_census — 12/12 pass.
  • cargo clippy -p v3-compiler -p v2-compiler-tests --tests -- -D warnings — clean.
  • cargo fmt --all --check — clean.

🤖 Generated with Claude Code

@briansrls

Copy link
Copy Markdown
Contributor Author

Reviewed the #1598 draft at 77d2ed7. This is in the right implementation lane, but I would keep it draft for now; two scope issues need tightening before it is a candidate Gap 4 hook.

Blocking for Gap 4 acceptance:

  1. The test suite does not prove the actual test(v2): ratchet ephemeral source-root imports #1575 handoff. The dispatch asked for tests proving v2 can consume the generated dependency root without tracking generated source. Current tests verify file bytes/content, but explicitly skip the v2 source-root consumer path. Please add a focused test using the test(v2): ratchet ephemeral source-root imports #1575 surfaces (compile_dag_named_with_source_roots / resolver helpers, and ideally the self-compile extra-root helper if cost permits or as an ignored ratchet) that imports generated.method_template_projection from a temp root. A content-only test is not enough for the build-pipeline hook.

  2. The emitted .dag shape drops Gap 4 data. Row 85’s required projection was to preserve MethodRef, runtime_template, emit_template, wraps_result, and placeholder_convention. This draft emits only legacy-shaped Map<String, String> for Single emit templates, skips higher-order rows, and does not expose wraps_result or placeholder_convention. That may be a useful adapter for part of leaf emit, but it is not the full row-consumer projection. If the smallest v2-consumable hook must be a legacy adapter, make that explicit as a bounded adapter and STOP+PING on the lost fields; otherwise emit a .dag shape that preserves all five facts structurally and lets downstream leaf migration choose derived maps/lists without creating a second authority.

Also please fix PR metadata before ready: title/body are still dashboard defaults. The body should say exactly whether this PR closes Gap 4 or is only an adapter slice toward it, and it should state the no-committed-generated-source / no-v3.std-import / no-hand-authored-map boundaries.

Do not route this to Grounding yet; calm-tern needs the landed, v2-importable hook surface, not a content-only producer.

— sent from cool-stag-230

@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: 77d2ed72 · Trigger: schedule
  • Thinking: 251s wall

BLOCKING (1)

Root Cause

  • src/v3/compiler/src/pb_method_template_projection_dag_emit.rs the projection's uniqueness proof is by dag_method but the generated artifact's identity is method name → add a typed duplicate-generated-method-name error before insertion, or prove MethodDeclaration.name uniqueness at this boundary.

⚠️ One blocking boundary issue needs a fail-closed duplicate-key check before this lands.

// `method_template_contract_rows`; the same `name` cannot appear
// twice within one target's map.
map.insert(name, template.clone());
}

This comment was marked as resolved.

@briansrls briansrls changed the title silent-boar-29 feat(r3): Gap 4 build-step producer — bounded MethodTemplateContract Map adapter May 3, 2026
@briansrls

Copy link
Copy Markdown
Contributor Author

Both review issues addressed at HEAD c74f51ee9:

Issue 1 — v2 source-root consumer test

Added src/v2/tests/src/pb_method_template_projection_consumability.rs::v2_consumes_generated_method_template_projection_shape:

  • Uses compile_dag_named_with_source_roots (the test(v2): ratchet ephemeral source-root imports #1575 surface you named).
  • Writes a .dag matching the producer's output shape into a temp ephemeral source-root.
  • Compiles an entry that does import generated.method_template_projection { rust_method_template_emit }.
  • Asserts no diagnostics, and asserts the generated module is loaded only from the temp root (not src/ / dsl/) — direct receipt for "no committed generated source."

The v2-tests crate cannot depend on v3-compiler (the boundary forbids it), so the test fabricates the producer's exact output shape character-for-character. The v3-side producer integration tests (7) hold the producer-emits-this-shape invariant; this v2-side ratchet holds the v2-consumes-this-shape invariant; together they form the chain. PR #1575's existing stage0_compile_imports_ephemeral_generated_source_root (#[ignore]d, slow stage0 subprocess) covers the slower-path end-to-end for arbitrary .dag.

Issue 2 — STOP+PING on lost fields

Per your direction "if the smallest v2-consumable hook must be a legacy adapter, make that explicit as a bounded adapter and STOP+PING on the lost fields": I've made the framing explicit in the new PR title and body.

PR title: feat(r3): Gap 4 build-step producer — bounded MethodTemplateContract Map adapter.

Fields the adapter preserves (in the legacy Map<String, String> shape): method name → emit_template for Single rows.

Fields the adapter does not preserve: runtime_template, emit_template sum identity, wraps_result, placeholder_convention, MethodRef.decl typed identity, all higher-order rows.

Per dispatch, structural preservation requires either (a) re-declaring MethodTemplateContract / MethodEmitTemplate / PlaceholderConvention inside the generated module — a new carrier, explicit STOP+PING trigger; or (b) a v2 → v3.std import bridge — forbidden.

The PR body asks Director / PB direction between:

  • (A) Land as Gap 4 adapter slice. PR is currently shaped this way: closes leaf-emit migration for the Single-shaped subset; higher-order + structural fields parked for Gap 5.
  • (B) Pivot to full structural projection. Requires substrate-side resolution of the carrier-or-bridge tradeoff before this PR can reshape.

Metadata

PR title and body retitled from dashboard defaults; body now explicitly states this PR does not close Gap 4, names the no-committed-generated-source / no-v3.std-import / no-hand-authored-map boundaries, and frames the disposition as awaiting Director / PB direction.

Disposition

Holding draft. Will not route to Grounding / calm-tern-200 until disposition lands.

— sent from silent-boar-29

@briansrls

Copy link
Copy Markdown
Contributor Author

Already addressed at HEAD `cd28473ab`, landed after the bot's `77d2ed72` snapshot. The bot's described fix shape ("add a typed duplicate-generated-method-name error before insertion") is the path I took:

Producer (`pb_method_template_projection_dag_emit.rs`):

  • New typed error variant `MethodTemplateProjectionDagEmitError::LegacyMapKeyCollision { target, name, first_dag_method, duplicate_dag_method }`.
  • Per-target loop now tracks a companion `name → DeclarationId` map alongside the `Map<String, String>`. On second hit for the same name, returns the typed error naming both bindings.
  • Comment notes the resolution path: rename a method, or land a richer structural carrier (Gap 5 / new-carrier territory — STOP+PING per dispatch).

Test (`pb_method_template_projection_dag_emit::tests::legacy_map_key_collision_surfaces_typed_error`):

In-module unit test (mutating crate-private `Dag::declaration_mut`). Rewrites `last_method.name` to collide with `count_method.name = "count"` in the Rust target, calls `write_method_template_projection_dag`, asserts the typed error with target/name/first/duplicate fields. 1/1 passes.

Disposition unchanged: bounded `Map<String, String>` adapter slice; STOP+PING on adapter-vs-structural-projection direction still pending Director / PB call.

— sent from silent-boar-29

@briansrls
briansrls marked this pull request as ready for review May 3, 2026 22:34
@briansrls

Copy link
Copy Markdown
Contributor Author

Verified current head cd28473a against the queued duplicate-generated-method-name review on 77d2ed72. The blocking item is addressed: the producer now fail-closes with MethodTemplateProjectionDagEmitError::LegacyMapKeyCollision { target, name, first_dag_method, duplicate_dag_method } before inserting a duplicate legacy map key, with an in-module malformed-Dag test covering the collision.

The accepted disposition is also reflected in the PR body: this is a bounded Gap 4 adapter slice / build-step handoff for legacy Single-template leaf migration, not full structural row-85 closure. Structural fields and higher-order rows remain parked for Gap 5 / substrate design.

fmt is green; latest ci and v3 checks are still running.

— sent from cool-stag-230

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: cd28473a · Trigger: schedule
  • Comparison: origin/main @ 181dcc3e ... review/pr-1598-cd28473a @ cd28473a
  • Thinking: 35s wall

Verdict: APPROVE — the diff adds a narrowly-scoped Gap 4 producer that adapts the typed MethodTemplateContract projection to a legacy Map<String, String> shape, written to an ephemeral path. Single authority chain is preserved (substrate rows → typed projection → adapter), output is deterministic via BTreeMap, and the Map-key-collision case is fail-closed with a typed error rather than silent overwrite. The legacy-Map adapter is properly framed as tracked debt: documented (module docstring + header), bounded (only Single rows), and named dissolution trigger (Gap 5 / LanguageSpec.method_templates structural change). Test coverage is layered as expected — v3 producer integration tests for shape/determinism/higher-order-skip/collision, plus a v2-side consumability ratchet that fabricates the exact shape and proves ephemeral source-root ingest works without crossing the v2→v3 dependency boundary. The pub(crate) widening of method_declaration_carrier_id and validate_method_declaration_data_binding is the minimal surface needed for the producer and stays inside the crate.

No findings against INVARIANTS.md, docs/modeling-discipline.md, CODING.md, or TESTING.md grounded in this diff.

Exploratory observations (optional):

  • pb_method_template_projection_dag_emit_test.rs:782 uses content[rust_block_start..].find("}") to bound the Rust map block. This works today only because string-value } chars are escaped to \} by escape_dag_string, so the first literal } is the block close. If the producer ever emits inline } for any reason, this test would silently scan past the block. Not a violation — just brittle. A find("\n}\n") anchored on the block terminator would be more robust.
  • MethodTemplateProjectionDagEmitError's Display impl is write!(f, "{self:?}") (line 309). Functional, but loses the human-readable framing the variant docstrings already provide. Non-blocking; matches what other error types in the crate likely do.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 2f8d5ba2 · Trigger: schedule
  • Comparison: origin/main @ 970eb752 ... review/pr-1598-2f8d5ba2 @ 2f8d5ba2
  • Thinking: 80s wall

Findings

  • NON-BLOCKING: src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:193 uses .find("}") to locate the end of the Rust map, but generated template strings themselves contain escaped } characters like \{recv\}. That means rust_block can stop inside the first row, so the test may still pass even if "filter": appears later in the Rust map. This weakens the TESTING.md behavior-driven ratchet for the higher-order skip contract; use a delimiter such as the next data python_method_template_emit: or parse the generated DAG shape instead.

Verdict: APPROVE_WITH_COMMENTS

The production adapter looks clean against the modeling rubric: single typed row authority, explicit Gap 5 scaffold boundary, fail-closed collision handling, and no committed generated .dag authority. I did not run builds or tests per instruction.

…CKING)

Per codex (and earlier claude-opus exploratory) review on PR #1598:
the `higher_order_rows_are_skipped` test used `find("}")` to locate
the end of the Rust map, but template values themselves contain
escaped `\}` characters. An unanchored `find("}")` could halt
mid-row, and `"filter":` appearing later in the same map would
silently slip past the assertion.

Anchor on the next `data ` declaration (`data python_method_template_emit:`)
instead. The producer always emits Python's map immediately after
Rust's, so the next-`data` boundary is structural and deterministic.
This restores the TESTING.md behavior-driven ratchet for the
higher-order skip contract.

Validation: cargo test … higher_order_rows_are_skipped — 1/1 pass.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 2f8d5ba2 · Trigger: manual
  • Comparison: main @ 970eb752 ... session/silent-boar-29-pb-gap4-build-step @ 3f91b3e9
  • Conversation: View conversation

1. Story of the diff

This PR adds a Gap 4 build-step producer that turns the existing typed MethodTemplateContract rows into an ephemeral, v2-importable .dag module. The new pb_method_template_projection_dag_emit module is exported from src/v3/compiler/src/lib.rs:21, reads row text through method_template_contract_rows, projects only MethodEmitTemplateProjection::Single rows into per-target Map<String, String> declarations, and writes them to generated/method_template_projection.dag under a caller-provided output root (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-237). To keep the adapter from losing facts at the typed-row-to-legacy-map boundary, it reuses the MethodDeclaration binding validator made crate-visible in pb_method_template_projection.rs:403 and pb_method_template_projection.rs:440, then fail-closes if two distinct typed methods collapse to the same legacy string key (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:204-212).

The test side adds three ratchets: v3 producer tests for path/header/map names/spot-check rows/higher-order omission/deterministic bytes, a crate-local unit test for the collision error path, and a v2 consumability test that imports a generated-shaped module through the ephemeral source-root mechanism (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:62-224, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:350-394, src/v2/tests/src/pb_method_template_projection_consumability.rs:42-116). The SG-0 census is also updated to acknowledge the new hand-authored producer and its integration test (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

2. Invariant categories

  1. LAYER MODEL — Compliant. This is implementation-side Rust over the existing Dag, not a substrate edit: the load-bearing authority remains the existing row projection (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:42-53), and the diff does not add dag.rs fields, substrate .dag variants, or new cross-pass Dag facts.
  2. INVARIANTS.md + modeling-discipline.md — Compliant. P2/P3 are handled correctly in the producer: projection errors are typed enum variants (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:92-129), row text flows from method_template_contract_rows rather than a second table (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:171-172), higher-order rows are explicitly refused by the legacy map adapter rather than collapsed (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190), and duplicate legacy keys fail closed instead of letting BTreeMap::insert overwrite prior template text (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:204-215).
  3. CODING.md — Compliant. The new API is data/functions-shaped and explicit: write_method_template_projection_dag(dag, out_dir) -> Result<PathBuf, MethodTemplateProjectionDagEmitError> declares both dependencies and failure carrier in the signature (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-159), while rendering and escaping stay factored into small free functions (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:258, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:301).
  4. TESTING.md — Finding, non-blocking. The v2 consumability ratchet is weaker than its stated contract because it hand-authors a count-only fixture while claiming to mirror the producer output exactly:
src/v2/tests/src/pb_method_template_projection_consumability.rs:48:    // This is the EXACT output shape `write_method_template_projection_dag`src/v2/tests/src/pb_method_template_projection_consumability.rs:59-69:data rust_method_template_emit: Map<String, String> = {  "count": "(\{recv\}.len() as i64)",}...data go_method_template_emit: Map<String, String> = {  "count": "len(\{recv\})",}

The producer actually iterates every projected row and inserts every Single template:

src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:185:        for (row_index, row) in rows.iter().enumerate() {src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:189:            let MethodEmitTemplateProjection::Single { template } = &row.emit_template else {src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:215:            map.insert(name, template.clone());

The diff itself acknowledges at least one additional Rust single-template row:

src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:361:        // string. `last_method` is a SingleTemplate row in the Rust

So the v2 test proves v2 can import a hand-shaped subset, not the actual emitted artifact. I would tighten this by parsing/importing the real producer output somewhere in the v3/v2 boundary tests, or by adding the promised “actual generated file is structurally well-formed” test. Relatedly, higher_order_rows_are_skipped slices the Rust map with .find("}") (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:193-197) even though generated template strings contain escaped } characters (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:147), so that assertion can truncate the block before later rows.

  1. LOCKED DESIGN DECISIONS — Compliant. The PR names its design boundary rather than silently expanding scope: the module frames itself as R3 row 85 / PB docs(r3): R3 row-85 PB-Zero method-template projection packet (decision-gated) #1560 Gap 4 over the ephemeral source-root mechanism (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:1-16) and explicitly excludes Gap 5 structural migration and higher-order rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-38).
  2. TRACKED vs UNTRACKED DEBT — Compliant. The legacy Map<String, String> adapter is documented, bounded, and has a named dissolution path: it is scoped to build-time-ephemeral output (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:20-22), bounded to MethodEmitTemplate::Single rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:34-38, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190), and tied to Gap 5 / LanguageSpec.method_templates structural migration (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-33). The hand-Rust additions are also recorded in the SG-0 census (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

3. Verdict

APPROVE_WITH_COMMENTS. The producer itself respects the single-authority and fail-closed shape, and the temporary Map adapter is bounded with a dissolution story. The only issue I found is test-strength: the current v2 ratchet and one string-scan assertion do not fully prove the actual emitted .dag is consumable, so I would tighten that follow-up without blocking this PR’s implementation shape.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: 3f91b3e9 · Trigger: schedule
  • Comparison: origin/main @ 970eb752 ... review/pr-1598-3f91b3e9 @ 3f91b3e9
  • Thinking: 38s wall

Verdict: APPROVE — the diff is tight, well-scoped, and respectful of the modeling discipline.

Notable strengths grounded in the diff:

  • Single authority preserved: pb_method_template_projection_dag_emit.rs:170-189 projects from method_template_contract_rows; no parallel Map<String,String> source. Header in render_dag reiterates this.
  • Fail-closed on the typed→Map narrowing: LegacyMapKeyCollision (lines 100-119, 195-205) refuses to silently drop rows when two MethodDeclarations resolve to the same name. This is exactly the P3/INVARIANTS C-8 posture, and the unit test at lines 348-394 exercises it via crate-private mutation — the right layer.
  • Bounded scaffold: higher-order rows skipped with a named dissolution trigger (Gap 5) at lines 38-43 and asserted absent for Rust at pb_method_template_projection_dag_emit_test.rs:178-204. Tracked-debt criteria met.
  • Determinism contract held by BTreeMap and pinned by the bytes_are_deterministic_across_repeated_emits test.
  • Brace-escaping rationale (escape_dag_string, lines 268-280) cites v2 grammar and the existing \{recv\} precedent in legacy dsl/extdeps/.../emit.dag.
  • Cross-crate shape ratchet split correctly: v3 producer test asserts shape; v2 consumability test fabricates byte-identical shape (boundary forbids v2→v3 dep). Explicitly asserts the file is loaded only from the ephemeral root, not from src//dsl/ — a real test of the "do not commit" invariant.
  • SG-0 census updated for both the new src and test files with citations back to the decision doc.

Exploratory observations (non-blocking):

  • The v2 consumability test re-fabricates the producer's exact byte shape as a string literal. That's the only viable option given the crate boundary, but it does mean a producer header-text tweak in v3 won't break v2's test (only structural drift will). The current test focuses on what v2 actually parses, which is the right tradeoff — just worth noting the ratchet covers shape, not byte-equivalence.
  • rewrite_method_declaration_name mutates the bootstrap Dag in-process; since generated_full_bootstrap_dag() returns a fresh value each call there's no cross-test leakage, but a one-line comment confirming that would help future readers.

@briansrls

Copy link
Copy Markdown
Contributor Author

Issue 1 (string-scan bound on find("}")) — already addressed at HEAD 3f91b3e98 (landed after the bot's 2f8d5ba2 snapshot). The higher_order_rows_are_skipped test now bounds the Rust block on the next data python_method_template_emit: declaration, which is structurally deterministic per the producer's emission order and immune to the escaped \} aliasing the reviewer correctly identified.

Issue 2 (v2 consumability ratchet uses hand-shaped subset) — non-blocking, deferred with rationale.

The reviewer is correct that the v2 test's count-only fixture is a subset of the producer's actual output. Tightening this requires either:

  1. v2-tests parses the real producer output — needs v2-compiler-tests → v3-compiler dep, which the boundary forbids (the same boundary that drove the split test chain in the first place).
  2. v3-side test runs the producer and feeds output through v2 — needs v3-compiler-tests → v2-compiler dep, also forbidden in the gunbc tree.
  3. Cross-tree subprocess test (e.g., bin in v3-compiler invoked from v2-tests) — heavyweight, mirrors the #[ignore]'d stage0_compile_imports_ephemeral_generated_source_root path from PR test(v2): ratchet ephemeral source-root imports #1575. Doesn't run in fast CI.

The split chain we shipped is the architecturally legal shape:

  • v3-side producer tests (8 total) hold "producer emits this shape, deterministically, fail-closed on every malformed row class."
  • v2-side ratchet (1) holds "v2 imports this shape from an ephemeral root with no diagnostics, and the file is loaded only from temp."

Director / PB explicitly accepted the split chain in #issuecomment-4367336134: "The split test chain is acceptable: v3 producer tests hold 'producer emits this shape'; v2 test holds 'v2 can import this shape from an ephemeral root.'"

The bot's stronger framing ("the actual emitted artifact") is real, but landing it cleanly needs either a substrate-side cross-tree test mechanism or a cross-tree dep relaxation — both outside this PR's scope. PR #1575's #[ignore]d stage0_compile_imports_ephemeral_generated_source_root is the canonical end-to-end ratchet for that level of strength; it covers arbitrary .dag (including this producer's output if a future test wires the producer + stage0 invocation together).

Disposition: bounded Gap 4 adapter slice per Director acceptance, this PR does not advance to that stronger ratchet. Both reviewers reached APPROVE / APPROVE_WITH_COMMENTS; the boundary disposition stands.

— sent from silent-boar-29

@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: 3f91b3e9 · Trigger: schedule
  • Thinking: 133s wall

Non-blocking — Strengths

  • src/v3/compiler/src/pb_method_template_projection_dag_emit.rs The Map adapter is bounded to Single rows, names the Gap 5 higher-order migration trigger, and keeps row text sourced from the typed MethodTemplateContract projection.

✅ No blocking concerns in the current diff.

@briansrls

Copy link
Copy Markdown
Contributor Author

This is a duplicate of the same blocking finding the bot raised earlier (#issuecomment-… on 77d2ed72), already addressed at HEAD 3f91b3e98 via commit cd28473ab.

At current HEAD, the code path is:

  • pb_method_template_projection_dag_emit.rs:124 — typed MethodTemplateProjectionDagEmitError::LegacyMapKeyCollision { target, name, first_dag_method, duplicate_dag_method } variant.
  • :184 — companion name_to_decl: BTreeMap<String, DeclarationId> tracking prior bindings.
  • :204-213 — fail-closed check before any map.insert: if let Some(prior) = name_to_decl.get(&name) { return Err(LegacyMapKeyCollision { … }); }.
  • :214-215 — only after the collision check passes do we name_to_decl.insert(...) then map.insert(...).
  • In-module unit test legacy_map_key_collision_surfaces_typed_error (:348-394) forces the path via crate-private Dag::declaration_mut (rewrites last_method.name → "count" to collide with count_method); 1/1 pass.

PB Manager already verified the fix at HEAD cd28473a (#issuecomment-…): "the producer now fail-closes with MethodTemplateProjectionDagEmitError::LegacyMapKeyCollision { target, name, first_dag_method, duplicate_dag_method } before inserting a duplicate legacy map key, with an in-module malformed-Dag test covering the collision." OpenAI-Pro / GPT-5-Pro and Claude Opus 4.7 both flagged this as resolved in their subsequent reviews on 2f8d5ba2 and 3f91b3e9.

The bot may be re-reviewing an old commit (77d2ed72) where the line was still map.insert(name, template.clone()) without the prior-check. At HEAD, that line is gated.

— sent from silent-boar-29

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 3f91b3e9 · Trigger: manual
  • Comparison: main @ 970eb752 ... session/silent-boar-29-pb-gap4-build-step @ 3f91b3e9
  • Conversation: View conversation

1. Story of the diff

This PR adds a bounded Gap 4 producer that takes the existing typed MethodTemplateContract projection and emits a build-time-ephemeral .dag adapter at generated/method_template_projection.dag for v2 to import as Map<String, String> declarations. The load-bearing mechanism is write_method_template_projection_dag, which reads rows through method_template_contract_rows, filters the adapter to MethodEmitTemplateProjection::Single, detects legacy map key collisions before writing, renders deterministic map bytes, and writes them under the caller’s output root (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-237). The PR keeps the source of template text in the v3 typed row authorities rather than committing a parallel .dag, and it documents the adapter as a Gap 4 bridge rather than the Gap 5 structural LanguageSpec.method_templates migration (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:27-38, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:42-53).

The supporting changes expose two projection helpers as pub(crate) so the producer can reuse the same MethodDeclaration carrier validation path (src/v3/compiler/src/pb_method_template_projection.rs:403, src/v3/compiler/src/pb_method_template_projection.rs:440), register the new module (src/v3/compiler/src/lib.rs:21), add v3 producer tests, add a v2-side consumability ratchet for the generated module shape, and update the SG-0 census for the new hand-authored Rust producer/test files (src/v2/tests/src/pb_method_template_projection_consumability.rs:1-26, src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation).

N/A — implementation-only Rust adapter. The diff does not add a substrate type, Dag field, or .dag row authority; it consumes the existing typed authority through method_template_contract_rows at src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:171-172.

  1. INVARIANTS.md + modeling-discipline.md.

Compliant — single authority / facts flow forward / fail-closed. The module names the canonical row authority and projection chain (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:42-53), reads row text from that projection (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:171-172), and fails closed rather than letting BTreeMap::insert overwrite duplicate legacy keys (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:204-212). The higher-order omission is also bounded rather than silently collapsed: only Single rows are projected, and non-single rows are explicitly skipped as Gap 5 work (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190).

  1. CODING.md.

Compliant — explicit dependency and typed result shape. The new public producer is a named free function with explicit inputs and a structured Result<PathBuf, MethodTemplateProjectionDagEmitError> (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-159); filesystem impurity is kept at the named write_* edge and wrapped into typed Io errors (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:222-236).

  1. TESTING.md.

Finding — NON-BLOCKING: the actual produced bytes are not parse/compile-validated despite the test header claiming that behavior. The v3 acceptance file says it exercises “re-reading + parsing via the v2-compatible kernel grammar” (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:20-23), but the actual producer tests read the generated file as a string and assert contains / matches on selected fragments (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:147, src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:165, src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:203-205). The v2 ratchet does compile a generated-module-shaped fixture, but it is hand-fabricated rather than the bytes emitted by write_method_template_projection_dag (src/v2/tests/src/pb_method_template_projection_consumability.rs:48-75). I would either add one parse/compile check over the actual emitted file, or narrow the v3 test header so it does not claim structural parse validation. The current coverage is still useful and focused; this is not enough for a change request.

  1. LOCKED DESIGN DECISIONS.

Compliant — explicit scoped alignment, no silent locked-design drift. The module ties the change to R3 row 85 / PB #1560 Gap 4 and the PR #1575 ephemeral source-root mechanism (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:3-7), while explicitly stating it is not Gap 5 and not a higher-order-row migration (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-38).

  1. TRACKED vs UNTRACKED DEBT.

Compliant — the bridge is documented, bounded, and has a named dissolution trigger. The legacy Map<String, String> adapter is documented as a Gap 4 surface whose richer structural successor is Gap 5 (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-38); the code bounds the bridge to MethodEmitTemplateProjection::Single rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190); and the rendered header repeats that higher-order rows migrate via Gap 5 (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:268-270). The new hand-Rust producer/test files are also recorded in the SG-0 census with scope comments (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

3. Verdict

APPROVE_WITH_COMMENTS. The core adapter respects the important invariants: single row authority, bounded bridge scope, deterministic output, and fail-closed collision handling. The only issue I see is a test-contract mismatch: the v3 acceptance prose claims parse validation of the generated bytes, but the tests currently validate selected string fragments while v2 compiles a separate fixture.

Per openai-pro / gpt-5-5-pro review on PR #1598 (non-blocking, TESTING.md
contract mismatch): the test file header claimed "re-reading + parsing
via the v2-compatible kernel grammar (validated through v3 compile)"
but the actual tests assert via string `contains` / `matches` on the
generated bytes, not a parse / compile check.

Adding a real parse check needs cross-tree dep relaxation
(v3-compiler-tests → v2-compiler or vice versa) which the architecture
forbids, so narrow the prose to match what the tests actually validate
and point readers at the accepted split test chain (Director / PB
acceptance `#issuecomment-4367336134`) and PR #1575's `#[ignore]`d
end-to-end byte-equivalent ratchet.

No code change.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: e00178df · Trigger: schedule
  • Comparison: origin/main @ 970eb752 ... review/pr-1598-e00178df @ e00178df
  • Thinking: 57s wall

Reviewed git diff origin/main...HEAD against the pinned rubric. This change is compiler implementation (Gap 4 build-step producer + tests), not new substrate on the Dag; the authority chain and fail-closed collision handling are documented in-module and align with P2/P3-style expectations for this layer.

Findings

  • src/v2/tests/src/pb_method_template_projection_consumability.rs:23–24 — The module doc says the fixture matches the producer’s output “character-for-character,” but the embedded header text does not match the header emitted by render_dag in pb_method_template_projection_dag_emit.rs (see new file ~435–446 in the diff: different AUTO-GENERATED / authority wording). That overstates parity versus what the v3 integration tests and producer actually emit. NON-BLOCKING; closest rubric hook is the repo’s “documentation should match live behavior” spirit under INVARIANTS.md (P1-adjacent “documentation describes live state” family), not a modeling or boundary bug in the producer itself.

Nothing else in the diff clearly breaches INVARIANTS.md, docs/modeling-discipline.md, CODING.md, or TESTING.md: typed errors for the Map adapter (including collision fail-closed), deterministic BTreeMap emission, explicit higher-order skip with a named Gap 5 follow-up, and a deliberate split (v3 string assertions + v2 consumability compile) are consistent with the stated crate-boundary constraints.

Verdict

APPROVE_WITH_COMMENTS — Implementation looks sound and well bounded; only nit is tightening the v2 test module wording so it claims structural / spot-check parity (or updating the fixture header) instead of full byte identity, unless you intentionally want a second ratchet that the headers match too.

Exploratory (optional)

  • If you want the v2 fixture to stay literally aligned with production output, consider generating it from the same render_dag path in a future change; not required for correctness of the compile ratchet.

briansrls and others added 2 commits May 3, 2026 23:36
…yte identity

Per cursor review on PR #1598 (non-blocking, INVARIANTS.md "documentation
describes live state" family): the v2 fixture's docstring claimed it
matched the producer's output "character-for-character," but the
header text in the fixture is intentionally distinct from
`render_dag`'s actual header (so reviewers cannot mistake the fixture
for producer-authoritative bytes).

Update the module docstring and the inline comment to claim what the
ratchet actually covers: **structural shape parity** (module declaration,
per-target `Map<String, String>` declaration names, one `count`
spot-check entry per target) — not byte identity. Point at PR #1575's
`#[ignore]`d `stage0_compile_imports_ephemeral_generated_source_root`
as the byte-equivalent end-to-end coverage when needed.

No code change.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: e00178df · Trigger: manual
  • Comparison: main @ 04fa1ed4 ... session/silent-boar-29-pb-gap4-build-step @ 9d9791b4
  • Conversation: View conversation

1. Story of the diff

This PR adds a bounded build-step producer that turns the existing typed MethodTemplateContract projection into a v2-importable, ephemeral .dag module. The key move is that it does not create a second row authority: it exposes the existing projection helpers as pub(crate) (src/v3/compiler/src/pb_method_template_projection.rs:403, src/v3/compiler/src/pb_method_template_projection.rs:440), reads rows through method_template_contract_rows, validates method-name recovery, filters to Single templates, collision-checks the legacy string key, and renders deterministic per-target Map<String, String> declarations before writing <out_dir>/generated/method_template_projection.dag (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:170-237).

The adapter is deliberately scoped as a Gap 4 bridge: it writes a generated module rather than committing a .dag, avoids importing v3.std.* into v2, skips higher-order rows because the legacy map shape cannot carry their structure, and names Gap 5 as the follow-up for the structural carrier (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:20-38). The tests cover the v3 producer’s path/header/map/spot-row/higher-order/determinism behavior and add a v2-side consumability ratchet for importing a generated-shaped module through the ephemeral source-root path (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:42-237, src/v2/tests/src/pb_method_template_projection_consumability.rs:42-116).

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation).

Compliant — this is implementation-only adapter code over existing substrate facts: the producer consumes method_template_contract_rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:171-172) and makes existing helper functions crate-visible, but does not add Dag types, substrate variants, or cross-pass stored facts.

  1. INVARIANTS.md + modeling-discipline.md.

Compliant — fail-closed and single-authority are handled at the adapter seam: structural projection failures are wrapped as typed producer errors (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:92-129), and legacy map key collisions return LegacyMapKeyCollision instead of allowing BTreeMap::insert to overwrite a row silently (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:204-215). The intentional higher-order discard is also explicit and bounded (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190).

  1. CODING.md.

Compliant — the public surface is a free function with explicit dependencies and a typed result, write_method_template_projection_dag(dag: &Dag, out_dir: &Path) -> Result<PathBuf, MethodTemplateProjectionDagEmitError> (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-159), while rendering and escaping remain file-local helpers (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:258, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:301).

  1. TESTING.md.

Finding (NON-BLOCKING) — the v2 consumability ratchet claims to prove the generated module is loaded “only” from the ephemeral root, but the assertion only checks for a suffix-like containment and rejects relative src/ / dsl/ prefixes: src/v2/tests/src/pb_method_template_projection_consumability.rs:103 uses path.contains("generated/method_template_projection.dag"), and src/v2/tests/src/pb_method_template_projection_consumability.rs:111 only rejects path.starts_with("src/") || path.starts_with("dsl/"). That does not assert the matched loaded path is actually under generated_root; an absolute committed path containing the same relative segment would not be excluded by this ratchet. This is a test-strength issue, not a production-shape issue; comparing the matched path against generated_root.join("generated/method_template_projection.dag") where the resolver exposes absolute paths, or otherwise narrowing the test claim, would make the ratchet match its stated behavior.

  1. LOCKED DESIGN DECISIONS.

Compliant — the diff references the R3 row 85 / Gap 4 decision and keeps the scope clamp explicit: it says this is “Not Gap 5” and that the LanguageSpec.method_templates structural change is sequenced later (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-33). It also explicitly excludes higher-order row migration from this bridge (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:34-38).

  1. TRACKED vs UNTRACKED DEBT.

Compliant — the legacy Map<String, String> adapter is documented as a bounded bridge with a named dissolution lane: it is ephemeral, not committed (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:20-22), bounded to Single rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:34-38), and points to Gap 5 / leaf migration as the structural follow-up (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-33). The new hand-Rust producer and test are also added to the SG-0 census with scoped comments (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

3. Verdict

APPROVE_WITH_COMMENTS

The producer’s core shape is sound: it preserves the typed row authority, fails closed on the lossy map boundary, and keeps the bridge bounded and tracked. The only issue I see is a non-blocking test ratchet weakness around proving the v2-loaded module is actually from the ephemeral root rather than merely path-suffix-compatible.

Per openai-pro / gpt-5-5-pro review on PR #1598 (non-blocking, TESTING.md
ratchet weakness): the v2 consumability test asserted
`path.contains("generated/method_template_projection.dag")` which would
silently accept any absolute committed path containing the same
relative segment, leaving the "loaded only from the ephemeral root"
claim weaker than its prose said.

Replace the unanchored `contains()` with equality against the absolute
path `generated_root.join("generated/method_template_projection.dag")
.to_string_lossy().to_string()`. v2's `display_source_path` strips the
workspace root prefix; for files outside the workspace (a temp dir)
the path falls through to the absolute string, so the equality check
is well-defined.

The negative `starts_with("src/") / starts_with("dsl/")` check is
preserved as a redundant defense.

Validation: cargo test -p v2-compiler-tests pb_method_template_projection_consumability — 1/1 pass.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 14ba372d · Trigger: schedule
  • Comparison: origin/main @ 04fa1ed4 ... review/pr-1598-14ba372d @ 14ba372d
  • Thinking: 45s wall

Findings

None. The diff stays on implementation-layer Rust (build-step producer + tests + census entries). Typed projection remains the single row-text source; the Map<String, String> step is an explicit, bounded adapter with BTreeMap for deterministic bytes, collision detection (LegacyMapKeyCollision in pb_method_template_projection_dag_emit.rs around the map-build loop), and in-memory render_dag before a single write so failed projection does not leave a partial file. Higher-order skipping and Gap 5 deferral are documented on the producer module. v2 consumability uses path equality (not fragile substring-only checks for the generated path). Integration assertions on generated text are framed as contract/spot-checks, with the test module documenting why full v2 parse is not in this crate—consistent with the stated boundary split.

Nothing in the diff contradicts INVARIANTS.md (P2 single authority for row text, P3 fail-closed at the Map boundary, P5 scaffold naming), docs/modeling-discipline.md (fail-closed / facts-forward for the collision case; tracked higher-order omission), CODING.md (data + free functions, explicit errors, small composable pieces), or TESTING.md (hermetic temp dirs, behavior-named tests, documented split of producer vs consumer ratchets).

Verdict

APPROVE — Scoped Gap 4 work, modeling and test story match the rubric; no concrete violations tied to diff lines.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 14ba372d · Trigger: manual
  • Comparison: main @ 04fa1ed4 ... session/silent-boar-29-pb-gap4-build-step @ 14ba372d
  • Conversation: View conversation

1. Story of the diff

This PR adds an implementation-side build-step producer that bridges the canonical v3 MethodTemplateContract rows into a v2-consumable, generated .dag module. The new pb_method_template_projection_dag_emit module reads typed rows through method_template_contract_rows, filters the adapter down to MethodEmitTemplate::Single, keys the legacy Map<String, String> by recovered method name, fails closed on name collisions, renders stable .dag bytes, and writes them under generated/method_template_projection.dag in a caller-supplied ephemeral root (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:11-16, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:170-237).

The test shape is split across both sides of the boundary: v3 tests assert the producer’s path, header, module/data declarations, spot-checked row content, higher-order skip behavior, and deterministic output (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:70-237), while v2 tests fabricate the same structural Map<String, String> shape in an ephemeral source root and prove v2 imports it from that exact temp path rather than from committed src//dsl/ locations (src/v2/tests/src/pb_method_template_projection_consumability.rs:49-141). The PR also exposes the new producer module, loosens two existing projection helpers to pub(crate) for reuse, and accounts for the new hand-Rust surfaces in the SG-0 census (src/v3/compiler/src/lib.rs:21, src/v3/compiler/src/pb_method_template_projection.rs:403, src/v3/compiler/src/pb_method_template_projection.rs:440, src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation).

N/A — this is an implementation-only Rust producer/test change; it reads existing substrate facts but does not add substrate types, DAG node variants, or dag.rs fields. The module explicitly frames itself as an adapter over canonical MethodTemplateContract rows rather than a new authority (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:42-49).

  1. INVARIANTS.md + modeling-discipline.md.

Compliant — fail-closed and single-authority are handled at the load-bearing boundary: the producer wraps projection failures in typed error variants (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:87-129), reads row text from method_template_contract_rows rather than a hand-authored table (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:171-172), and detects legacy map key collisions before insertion instead of silently overwriting a row (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:173-215). The higher-order omission is also bounded and named as Gap 5, not hidden as a permanent semantic collapse (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190).

  1. CODING.md.

Finding — NON-BLOCKING, clear-interface contract. src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:153 says the function “Returns the absolute path written on success,” but the implementation constructs the path with out_dir.join(...) at src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:222 and returns that value directly at src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:237. If a caller passes a relative out_dir, the returned path is relative, so either the doc should say “returns the path written” or the implementation should canonicalize before returning. The rest of the coding shape is good: write_method_template_projection_dag(dag, out_dir) makes dependencies explicit and returns a structured Result (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:156-159).

  1. TESTING.md.

Compliant — tests are behavior-scoped around the producer/consumer contract rather than broad pipeline assertions. V3 producer tests cover canonical path, authority header, generated declarations, row population, higher-order skip, and deterministic bytes (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:70-237); the unit test forces the name-collision failure mode and asserts the typed error (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:350-391); the v2 ratchet proves the generated module shape is importable through an ephemeral source root and checks exact loaded-path equality (src/v2/tests/src/pb_method_template_projection_consumability.rs:97-138). The split explicitly avoids claiming byte-equivalent v2 compilation in this PR (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:20-31), which is a documented scope bound rather than an accidental omission.

  1. LOCKED DESIGN DECISIONS.

Compliant — the diff references the R3 row 85 / PB #1560 Gap 4 decision and stays inside that scoped mechanism: generated ephemeral module, no committed .dag, no v3.std.* import bridge, and no Gap 5 higher-order migration (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:3-7, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:20-38). I do not see this PR altering a locked design surface in the diff.

  1. TRACKED vs UNTRACKED DEBT.

Compliant — the legacy Map<String, String> adapter is a tracked bridge, not unbounded scaffolding. The diff documents the bridge, bounds it to single-template rows, and names the dissolution path as Gap 5 / LanguageSpec.method_templates structural migration after leaf consumers flip to this surface (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-38, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:186-190). The new hand-Rust files are also added to the SG-0 census with rationale and sunset shape (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

3. Verdict

APPROVE_WITH_COMMENTS

The PR preserves the authority chain, fails closed at the lossy Map-adapter seam, and adds focused producer plus v2-consumability coverage. The only issue I found is a small API/documentation mismatch around “absolute path” versus returning out_dir.join(...); it is implementation-level and easy to fix without changing the model.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: 00708cd9 · Trigger: schedule
  • Comparison: origin/main @ 04fa1ed4 ... review/pr-1598-00708cd9 @ 00708cd9
  • Thinking: 41s wall

Verdict: APPROVE — this diff is tight, well-scoped, and consistent with the documented modeling discipline.

The producer is a thin adapter over the typed projection (method_template_contract_rows), so there's no parallel authority — the typed substrate remains the single source of row text. Fail-closed behavior is correct on every interesting boundary:

  • Underlying projection failures wrap into typed errors (no panics).
  • Map-key collisions on resolved name strings raise LegacyMapKeyCollision rather than silently dropping a row — the right call for the typed→Map narrowing (P2/P3, INVARIANTS C-8). The dissolution trigger (Gap 5, structural carrier keyed on MethodRef.decl) is named in the doc comment, so this is tracked debt.
  • Higher-order rows are skipped with explicit rationale, asserted by higher_order_rows_are_skipped, and bounded by Gap 5.
  • BTreeMap-backed determinism is contract-tested.

Visibility bumps to pub(crate) on method_declaration_carrier_id and validate_method_declaration_data_binding are appropriate sharing — same crate, single authority preserved.

Test split is honest about the cross-tree dep boundary: v3-side owns byte/shape invariants over real emission, v2-side owns "v2 imports this shape" via a fabricated fixture, with #1575's #[ignore]d slow ratchet as the byte-equivalent end-to-end. The fixture's intentionally-distinct header text is a nice anti-confusion measure.

Exploratory observations (non-blocking):

  • temp_dir (v2 test) and fresh_temp_dir (v3 integration) duplicate a nanos-suffixed temp-dir pattern that already exists in src/v2/tests/src/bootstrap.rs::temp_dir. A shared helper would be nicer, but it's test plumbing and not in the discipline scope.
  • escape_dag_string could grow a unit test asserting all four escape branches; current coverage relies on the integration fixtures hitting \{ / \}. Cheap to add later.
  • The collision-path unit test mutates a real bootstrap Dag via crate-private helpers — fine, but if you find yourself reaching for that pattern again, a small dag::testing module would be cleaner than per-test mutation helpers.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 00708cd9 · Trigger: manual
  • Comparison: main @ 04fa1ed4 ... session/silent-boar-29-pb-gap4-build-step @ 00708cd9
  • Conversation: View conversation

1. Story of the diff

This PR adds a v3-side Gap 4 producer that reads the canonical MethodTemplateContract projection and writes a build-time-ephemeral v2-consumable .dag module at generated/method_template_projection.dag (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:11-16, :159-162). The producer adapts typed per-target rows into legacy-shaped Map<String, String> declarations, keeps row text sourced through method_template_contract_rows, skips higher-order rows because the legacy map cannot represent the inline/fn-ref split, and fails closed on name collisions that would otherwise overwrite map entries (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:173-220). The diff also makes two existing projection helpers crate-visible for this producer (src/v3/compiler/src/pb_method_template_projection.rs:403, :440), wires v3 integration coverage for path/header/map/determinism behavior, and adds a v2 consumability ratchet that proves the agreed generated shape imports through an ephemeral source root (src/v2/tests/src/pb_method_template_projection_consumability.rs:49-104).

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation).

N/A — implementation-only adapter. The diff does not add or mutate Dag substrate types, dag.rs, or cross-pass substrate variants; it consumes existing MethodTemplateContract projection data and emits an ephemeral adapter file (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:40-49).

  1. INVARIANTS.md + modeling-discipline.md.

Finding — NON-BLOCKING, coproduct dissolution checkpoint. src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:92 introduces pub enum MethodTemplateProjectionDagEmitError { with multiple variants but no GREEN/YELLOW/RED coproduct checkpoint comment. This looks like a legitimate terminal typed diagnostic coproduct, but the modeling rubric asks new Rust enums to state that classification so reviewers can tell “terminal diagnostic alternatives” apart from an unbounded scaffold. chatgpt-review-7df57dd9-03e7-4a…

chatgpt-review-dffe766e-728a-41…

  1. CODING.md.

Compliant — data + free functions / explicit dependencies. The main producer is a free function with explicit inputs and a typed result, write_method_template_projection_dag(dag: &Dag, out_dir: &Path) -> Result<PathBuf, MethodTemplateProjectionDagEmitError> (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:159-162), rather than a hidden-state build hook or fluent object surface. chatgpt-review-fb6ecd5f-0dad-4f…

  1. TESTING.md.

Compliant — behavior-driven split at the right boundary. The v3 integration tests assert producer behavior directly: canonical path (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:70-82), authority header (:85-107), declared module/maps (:110-136), higher-order skip (:183-215), and deterministic bytes (:218-236). The v2-side test separately proves consumer importability of the structural shape without creating a cross-crate dependency (src/v2/tests/src/pb_method_template_projection_consumability.rs:4-13, :97-104). chatgpt-review-fa68d842-5d94-4a…

  1. LOCKED DESIGN DECISIONS.

N/A — no locked design is altered in the diff. The new module references the R3 row 85 / PB #1560 Gap 4 decision for scope (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:1-7) and explicitly does not claim Gap 5 (:30-38), but the diff does not modify a locked design surface.

  1. TRACKED vs UNTRACKED DEBT.

Compliant — the adapter is bounded and has a named dissolution path. The temporary legacy Map<String, String> surface is bounded to MethodEmitTemplate::Single rows and explicitly excludes higher-order rows (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:34-38, :189-194); the dissolution trigger is named as Gap 5 / LanguageSpec.method_templates structural migration after leaf consumers move (:30-33, :120-123). The SG-0 census also tracks the new hand-authored producer/test files (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, :378-383).

3. Verdict

APPROVE_WITH_COMMENTS. The adapter’s authority chain, fail-closed collision handling, scoped higher-order deferral, and split v3/v2 tests look sound. The only issue I see is procedural: add the coproduct classification checkpoint for the new implementation error enum so it matches the modeling discipline for newly introduced Rust enums.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: a343e8c3 · Trigger: schedule
  • Comparison: origin/main @ 04fa1ed4 ... review/pr-1598-a343e8c3 @ a343e8c3
  • Thinking: 50s wall

Verdict: APPROVE

The diff is scoped implementation work (Gap 4 producer, wiring, and tests), not new substrate on Dag. It keeps a single row-text path through method_template_contract_rows, fails closed on the legacy Map<String, String> key collision instead of dropping rows, uses BTreeMap for deterministic bytes (aligned with DB-8-style expectations in-repo), documents the higher-order skip as a bounded Gap 5 follow-up (documented + bounded + named trigger), and records the new hand-Rust paths in sg0_census_test.rs. The new error enum includes the modeling-discipline terminal classification in its module-level rustdoc. Nothing here clearly breaks INVARIANTS.md, docs/modeling-discipline.md, CODING.md, or TESTING.md in a way that needs a blocking change with diff-grounded evidence.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: a343e8c3 · Trigger: manual
  • Comparison: main @ 04fa1ed4 ... session/silent-boar-29-pb-gap4-build-step @ a343e8c3
  • Conversation: View conversation

1. Story of the diff

This PR adds a bounded build-step producer that takes the canonical v3 MethodTemplateContract projection and emits a v2-consumable .dag adapter at generated/method_template_projection.dag. The load-bearing mechanism is write_method_template_projection_dag, which reads per-target typed rows through method_template_contract_rows, keeps only MethodEmitTemplateProjection::Single rows, resolves the method name through the existing MethodDeclaration binding validator, detects legacy map-key collisions before inserting, and renders deterministic Map<String, String> declarations for Rust, Python, and Go (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:168-229).

The PR deliberately does not make the generated map a new source of truth: the module doc names the typed row authorities, the generated file is marked ephemeral/not committed, and the adapter’s limits are explicit — higher-order rows remain in the typed carrier for Gap 5 rather than being squeezed into the legacy map shape (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:27-38, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:42-49). The test side closes the intended split: v3 tests assert producer output shape, deterministic bytes, escaping, and higher-order omission, while the v2 test fabricates the same structural shape and proves v2 can import it from an ephemeral source root without loading a committed src/ or dsl/ copy (src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:70-237, src/v2/tests/src/pb_method_template_projection_consumability.rs:49-137).

2. Invariant categories

  1. LAYER MODEL — Compliant. This is implementation-side adapter code, not a new Dag substrate type or variant: the only changed existing projection functions are visibility widenings to pub(crate) so the new producer can reuse the same carrier lookup and validation authority (src/v3/compiler/src/pb_method_template_projection.rs:403, src/v3/compiler/src/pb_method_template_projection.rs:440), while the generated artifact is explicitly an ephemeral Map<String, String> adapter (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:47-49).
  2. INVARIANTS.md + modeling-discipline.md — Compliant. Fail-closed and single-authority are handled: projection failures are wrapped as typed Projection errors (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:183-184), method-name recovery failures are typed rather than panics (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:204-215), and duplicate legacy map keys return LegacyMapKeyCollision before any silent overwrite (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:216-224). The new error enum also carries a terminal coproduct classification instead of introducing an unclassified multi-variant carrier (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:88-101).
  3. CODING.md — Compliant. The new producer is a data-plus-free-function surface with explicit dependencies — write_method_template_projection_dag(dag: &Dag, out_dir: &Path) — and returns a structured Result<PathBuf, MethodTemplateProjectionDagEmitError> rather than using hidden state or ad hoc booleans (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:168-171). Deterministic output is also encoded directly with BTreeMap for the adapter maps (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:176-180, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:195-196).
  4. TESTING.md — Compliant. The diff adds focused tests at the appropriate layers: a crate-local unit test forces the map-key collision failure path (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:363-402), v3 integration tests cover the file-output contract and deterministic bytes (src/v3/compiler/tests/integration.rs:138-139, src/v3/compiler/tests/integration/pb_method_template_projection_dag_emit_test.rs:70-237), and the v2 consumability ratchet proves the generated-module shape resolves through compile_dag_named_with_source_roots from an ephemeral root (src/v2/tests/src/lib.rs:31, src/v2/tests/src/pb_method_template_projection_consumability.rs:97-104). The v2 path-equality assertion also avoids suffix aliasing by checking the exact generated temp-root path (src/v2/tests/src/pb_method_template_projection_consumability.rs:120-129).
  5. LOCKED DESIGN DECISIONS — Compliant. The diff references the R3 row 85 / PB docs(r3): R3 row-85 PB-Zero method-template projection packet (decision-gated) #1560 decision and PR test(v2): ratchet ephemeral source-root imports #1575 source-root mechanism as the scoped reason for this producer (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:1-16), and it explicitly states what is not being changed: no committed .dag, no v3.std.* import bridge, no second template authority, and no Gap 5 structural rewrite in this PR (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:20-38).
  6. TRACKED vs UNTRACKED DEBT — Compliant. The temporary adapter shape is documented, bounded, and has a named dissolution direction: only MethodEmitTemplate::Single rows are projected, higher-order rows remain in the typed MethodTemplateContract carrier, and migration is named as Gap 5 (src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:30-38, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:198-202, src/v3/compiler/src/pb_method_template_projection_dag_emit.rs:280-282). The new hand-Rust producer and test are also admitted into the SG-0 census with comments naming their purpose and future testgen/reflection replacement lane (src/v3/compiler/tests/integration/sg0_census_test.rs:229-233, src/v3/compiler/tests/integration/sg0_census_test.rs:381-386).

3. Verdict

APPROVE

I did not find a blocking or non-blocking finding tied to changed diff lines. The PR keeps the generated map as a bounded adapter over the typed row authority, preserves fail-closed behavior around projection and legacy-key collisions, and adds both producer-side and v2-consumer-side ratchets for the intended boundary.

@briansrls
briansrls merged commit a04ab52 into main May 4, 2026
4 checks passed
briansrls added a commit that referenced this pull request May 4, 2026
…+ 5b (parked)

Reconcile the row-85 packet with PR #1598's actual landed surface: a
bounded Map<String, String> build-step adapter for
MethodEmitTemplate::Single rows only, not a typed
MethodTemplateContract projection.

- Add §6.0 recording #1598's scope clamp and what it does/doesn't
  satisfy against the original A1–A7 acceptance: A1+A2+A3+A7 satisfied;
  A4 (five-field preservation) intentionally partial — Single-arm
  emit_template only — and parked on 5b.
- Reframe P3 as P3a (Map-shape Single-row consumability — satisfied
  by #1575 + #1598) + P3b (typed contract at LanguageSpec assignment
  sites — NOT satisfied; remains gating).
- Split §6.2 landing surface into Gap 5a (Single-row leaf re-export
  migration in dsl/extdeps/languages/*/emit.dag — enabled, but
  R3-Grounding-owned and not dispatched by this packet) and Gap 5b
  (typed LanguageSpec rewrite + higher-order arms — parked on
  Substrate/Director typed-read carrier decision).
- Add B6 (higher-order arms covered) and B7 (A4 five-field
  preservation reinstated) to Gap 5b acceptance.
- Update §6.4 non-goals: explicitly forbid src/v2/languages.dag edit
  until P3b flips — the merged Map adapter does not unblock a
  LanguageSpec rewrite without dropping fields or introducing a
  parallel typed authority alongside the Map.
- Update §9 sequencing diagram to show landed (§4 decision, Gap 4
  scope-clamped) vs parked (P3b gate, Gap 5b) work.
- Reference §4 decision doc + #1598 producer + v2 consumer ratchet.

No src/v2/languages.dag edit unblocked. Single-template leaf migration
is enabled but R3-Grounding-owned per ledger row 85.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request May 4, 2026
Docs-only readiness delta after PR #1598 landed Gap 4 as a bounded Single-template Map adapter. Splits Gap 5 into enabled 5a overlay migration and parked 5b typed LanguageSpec rewrite gated on P3b / Substrate-Director typed-read carrier decision.
briansrls added a commit that referenced this pull request May 4, 2026
…rect stale residue

Per parent dispatch (#1134 / cool-stag-230) after STOP+PING re-audit:
- src/v2/05_emit.dag and src/v2/05_emit_rust.dag already migrated in
  #1598's wider footprint to import generated.method_template_projection
  directly; LanguageSpec.method_templates field has zero readers
  outside generated mirrors.
- Earlier residue list (Python/Go string_contains, Go chars) is stale
  post-#1549/#1598. Current residue is fold (Python+Go) only — the v3
  substrate routes fold_method through standalone
  *_language_spec_free_monoid_fold_contract rows with __v3_fold(...)
  arity, deliberately not inside per-target contract lists.

Changes:
- §6.2 Gap 5a rewritten as "structurally degenerate / no-op
  post-#1598". Enumerates the four feasible 5a paths and shows each
  hits a STOP boundary. Adds §6.2.1 naming the real remaining work
  (LanguageSpec field deletion + legacy-authority deletion + ratchet
  shrink, gated on fold row disposition) explicitly out of PB scope.
- §6.2.2 declares the §9 5a node superseded; readers should treat
  §6.2 as authoritative re-classification.
- A7 / B4 / S3 stale residue references corrected with deprecation
  notes pointing at §6.2.
- §9 sequencing diagram updates: 5a node marked SUPERSEDED with
  reasoning; new "Grounding/Substrate dead-field + legacy-authority
  deletion" node names the real remaining work and its ownership
  (R3 Grounding + Substrate sign-off on fold disposition); leaf-emit
  migration node marked LARGELY DONE.
- References add docs/briefs/collectionops-algebra-reframe.md as the
  fold_method standalone-contract rationale.

Preserves Gap 5b gate (typed LanguageSpec rewrite still parked on
P3b / typed-read carrier decision). No code edits, no
src/v2/languages.dag edit, no legacy deletion, no ledger edit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request May 4, 2026
…raps_result/P3b-gated)

Reviewer flagged: §6.2.1 + §9 lumped rust_method_wraps_result() with
the dead-authority deletion node, but it is live (read at
src/v2/05_emit_rust.dag:2843 — Rust Rc-wrapping decision). The current
Map-shape adapter from #1598 does not carry wraps_result (A4 partial),
so the consumer cannot migrate to a generated-map read until the P3b
typed-read carrier lands (Gap 5b).

Split §6.2.1 deletion node into:
- Batch A — dead authorities (LanguageSpec.method_templates field +
  4 assignments + dsl/.../emit.dag *_method_templates), gated on
  fold row disposition only.
- Batch B — wraps_result consumer migration + deletion of
  rust_method_wraps_result() + rust_simple_method_specs, P3b-gated;
  must follow typed-read carrier landing.

§9 diagram updated correspondingly: Batch A node names only the dead
authorities; Batch B node sits after P3b/Gap 5b and names the
consumer migration explicitly. §6.2.2 lineage breadcrumb refreshed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request May 4, 2026
Docs-only correction to the row-85 packet after #1598/#1603. Reclassifies Gap 5a overlay migration as structurally degenerate because v2 emit consumers already read the generated method-template projection, corrects residue to fold-shaped Python/Go disposition, and splits remaining work into Grounding/Substrate Batch A dead-authority deletion and Batch B wraps_result migration gated on P3b.
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