Skip to content

Add Go multi-target validation - #483

Closed
briansrls wants to merge 11 commits into
mainfrom
sharp-jay-373
Closed

briansrls wants to merge 11 commits into
mainfrom
sharp-jay-373

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

  • add staged Go target specs and a Go emitter for v3 DAG programs
  • introduce shared staged emit schema and target execution model facts for Rust and Go
  • validate that GC targets skip ownership/rendering loading and add Rust/Go parity coverage for the same program

Testing

  • cargo check -p v3-compiler
  • cargo test -p v3-compiler emit_go -- --nocapture
  • cargo test -p v3-compiler --test m1_3_emit_go_test -- --nocapture

Notes

  • the Rust/Go behavioral parity test skips the Go runtime roundtrip when the go toolchain is unavailable in the environment

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 00422f56bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/v3/compiler/src/emit_go.rs Outdated
locals,
)?;
Ok(format!(
"func() {ret} {{ {acc_name} := {init}; for _, {item_name} := range {list} {{ {acc_name} = {body} }}; _ = ({item_type})({item_name}); return {acc_name} }}()"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove post-loop use of fold range variable

The ListFold template references {item_name} after the for _, {item_name} := range ... loop, but Go scopes range variables to the loop body, so emitted code always fails to compile with undefined: __foldItem whenever fold is used. This is a hard runtime-blocking regression for any program that lowers through the fold callable strategy.

Useful? React with 👍 / 👎.

Comment thread src/v3/compiler/src/emit_go.rs Outdated
locals,
)?;
Ok(format!(
"func() {ret} {{ __out := make({ret}, 0, len({list})); for _, {item_name} := range {list} {{ __out = append(__out, {body}) }}; return __out }}()"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid named range vars when map body may ignore them

This emits for _, {item_name} := range {list} for map, which fails to compile when the mapped function ignores its input (the loop variable is then unused). Since unused parameters are valid in this language, a constant mapper can trigger Go's declared and not used error here (and the same pattern appears in ListFilter).

Useful? React with 👍 / 👎.

Comment thread src/v3/compiler/src/emit_go.rs Outdated
if children.len() == 1 {
Ok(children[0].label.clone())
} else {
Ok("value".to_string())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bind variant payloads without assuming a value field

For variants with more than one payload field, this fallback returns "value", so branch emission generates binding := v.value. But emitted variant structs use their real field labels, not a synthetic value member, so any match arm that binds a multi-field variant payload will produce invalid Go code.

Useful? React with 👍 / 👎.

@briansrls

This comment has been minimized.

@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 · b6cee326

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

BLOCKING (2)

Root Cause

  • src/v3/compiler/src/emit_rust.rs The new execution-model bridge was introduced as a memory-only binding in both emitters; carry scope through the binding and consume it, or remove the field until a target actually needs it.
  • src/v3/std/emit_model.dag The refactor extracted the strategy coproducts out of rust.dag but not their documented scope and dissolution trigger; restore that receipt at the new shared authority.

Non-blocking — Strengths

  • src/v3/compiler/src/emit_go.rs The execution-model gate is a clean single-authority way to keep GC targets from loading ownership policy, and the dedicated test makes that intent explicit.

⚠️ The shared target-model split is directionally right, but scope currently dies at the emit boundary and the moved strategy scaffolds lost their tracking receipts.


type TargetExecutionModel {
memory: MemoryModel
scope: ScopeModel

This comment was marked as resolved.

Comment thread src/v3/std/emit_model.dag
cost: Int
}

type CallableStrategy

This comment was marked as resolved.

@briansrls

This comment has been minimized.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review — Track 2 / go.dag multi-target

Architecture is right — shared `TargetExecutionModel` factored out, ownership-skip verification for GC targets present, `go` toolchain presence is gated (graceful on systems without Go). The lane's design goal is realized.

Blocker — substrate leak in Go output (substantive)

The parity test `emit_go_and_rust_programs_are_behaviorally_equivalent_when_go_is_available` fails because emitted Go contains:

```
./main.go:15:42: undefined: DeclarationRef
```

`DeclarationRef` is a rust.dag type — it exists as an alias the Rust emitter uses internally. It should never appear in Go output.

Diagnosis

This is the multi-target stress test paying off early — it revealed an emitter gap the single-target Rust case couldn't surface. Either:

  1. go.dag is missing a realization for the concept that `DeclarationRef` covers. The shared emit machinery defaults to the Rust realization's `carrier` string when the target's spec doesn't declare one, and nothing in the architecture forces go.dag to cover every concept rust.dag does.

  2. The emitter has a default-to-Rust fallback somewhere that fires when target.realization lookup misses — same mechanism different layer. The symptom is identical from the Go output's perspective.

Both readings point at the same underlying question: does the emitter correctly consult the target's realization for every concept, and fail-closed when the target doesn't declare one?

Proposed action

  1. Find where `DeclarationRef` is read during emit (probably emit_rust.rs or a shared emitter helper). Follow the code path for the Go target.
  2. If it hits a Rust-specific const/string that isn't gated on target, that's the leak. The fix is to (a) add a corresponding realization to go.dag, OR (b) make the emit path consult the target's spec and fail-closed (or skip the declaration) when absent.
  3. Preference is (a) — declare what Go needs explicitly. (b) is a fallback disguised as a fix.

If this turns out to be bigger than a narrow go.dag addition (i.e. the emitter has multiple Rust-specific assumptions), flag it and we'll scope a follow-up. Lane 9 (english/yaml/spice/verilog) will amplify this issue by 4×, so getting the factoring right here pays forward.

Positive signals

  • `TargetExecutionModel` in target specs matches docs: unified dependency + rendering design — open edges for review #477 §2 exactly
  • Ownership-skip verification is present (proof, not just no-op)
  • Parity test gracefully skips when Go isn't installed — good CI hygiene
  • The first-pass `emit_go_program_compiles` test passes (basic mechanics work) — the parity test is specifically the stronger requirement that broke

Non-blocking observations

  • Worth a comment in go.dag naming which concepts are expected to be declared per target and which are target-universal (handled by shared emit infra). Right now the boundary is implicit.
  • When the fix lands, add a comment explaining the substrate → target contract: every emit concept consulted across multiple targets gets its own per-target realization; the shared emit machinery only handles structural walking, never concept-to-string mapping.

Recommendation: diagnose + fix the `DeclarationRef` leak before merge. This is the exact class of issue multi-target lanes are supposed to catch. Good signal that the infrastructure is working as intended.

@briansrls

This comment has been minimized.

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

Director Review — Go multi-target validation (#483)

This is the empirical test of "add a new emission target = one spec-file edit." Result: validated, with one observation.

What landed (3396 lines):

  • go.dag (694 lines) — full realization spec including go_execution_model declaring GarbageCollected, syntax bundles, type/operator/callable/pattern realizations
  • emit_go.rs (2123 lines) — table-driven emitter that filters realizations by go_* prefix
  • Shared infrastructure (emit_model.dag, computation_model.dag) — extracted from rust-specific code, now reusable
  • Cross-target equivalence test — same DAG → Rust + Go → executes both, asserts equal output

The good:

  • go_execution_model.memory == GarbageCollected correctly gates ownership rendering off — Go skips the entire ownership stage
  • No hardcoded language names in dispatch logic (only name.starts_with("go_") filtering)
  • The TargetExecutionModelBinding extraction made Rust-specific code language-agnostic — that's a real architectural win
  • Cross-target equivalence test executes both compilers (rustc + go run) on generated code

The observation:
emit_go.rs is 2123 lines. That's substantial for "language-specific boilerplate." For comparison, emit_python.rs (#484) is 1356 lines for the same job. Some of the size difference is probably Go's verbosity (explicit error returns, package boilerplate), but it's worth a structural look — is there a shared emitter walker that both Go and Python could share, with target-specific syntax declared in the spec? Not blocking this PR — but worth tracking as the third target lands and patterns emerge.

Thesis status:

  • ✅ One spec file per target (go.dag is the authority)
  • ✅ Memory model gates ownership decisions
  • ✅ No core compiler changes (just architectural refactor that benefits all targets)
  • ✅ Cross-target equivalence tested empirically

LGTM. Ship.

@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 · 3158b519

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

BLOCKING (2)

Root Cause

  • src/v3/std/emit_model.dag The shared emit schema has no structural “belongs to target” fact, so both emitters compensate with rust_/go_ name filtering -> add a typed target authority or grouping and key the indexes from that instead of declaration names.
  • src/v3/compiler/src/emit_go.rs Go rehydrates the shared pattern record into a narrower local binding and then renders from that smaller shape -> carry the full shared record forward, or explicitly shrink the shared substrate with a documented reason, before more targets depend on it.

Non-blocking — Strengths

  • src/v3/compiler/tests/m1_3_emit_go_test.rs The Rust/Go parity roundtrip is useful coverage because it exercises the same source through both target specs and will catch cross-target drift early.

⚠️ The prior scope/receipt issues are fixed and the multi-target direction looks right, but the new shared emit substrate still depends on name-based target routing and silently drops part of PatternRealization.

} else {
continue;
};
if !decl

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 shared realization tables are partitioned by rust_ name-prefix checks, so target selection now depends on internal declaration names instead of typed substrate facts (layer opacity / single authority).

VectorList,
}

#[derive(Debug, Clone)]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

BLOCKING: PatternRealizationBinding keeps only strategy/scrutinee/head_expr/tail_expr, so empty_pattern and cons_pattern from the shared PatternRealization substrate are silently dropped at the spec-to-emitter boundary (facts flow forward).

@briansrls

This comment has been minimized.

@briansrls

briansrls commented Apr 16, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

According to a document from 2026-04-16, this loop made real initial progress, but it has now crossed into diminishing-return repetition.

Loop summary.

3 completed review rounds are present in the attached history: 2 completed browser reviews and 1 Codex review, plus a 4th browser pass that had started but had not landed when the bundle was captured (ALL_REVIEWS.txt, lines 1–73). I can confirm 2 visible reviewed revisions—the initial migration revision and a later follow-up explicitly called out as d78551f; the exact PR commit count is not recoverable from the attached artifacts. The captured review window is about 57 minutes, from 20:40Z to 21:37Z on 2026-04-16 (ALL_REVIEWS.txt, lines 1, 29, 57, 70).

Forward progress evidence.

The loop did accomplish something real. This PR deletes the handwritten cost logic from lens_cost.rs and replaces it with a thin wrapper over a compiled .dag lens, which is exactly the direction the project’s lens invariant calls forward progress (pr-485.diff, lines 34–99; INVARIANTS.md, lines 131–147). It also adds three genuine consumers/ratchets: complexity.dag must compile cleanly, the checked-in generated module must match a fresh emit, and the compiled lens must match a handwritten oracle on core fixtures (pr-485.diff, lines 511–565). That means the loop is not just growing substrate; it enabled a real consumer path, which is the strongest positive signal in this whole review cycle. The roadmap explicitly says existing modeling only counts when it has structural consumers and structural tests, not just declarations (ROADMAP.md, lines 1174–1191). On that standard, this loop did pay down real debt.

Debt accumulation evidence.

The same blocker was then re-raised across every completed round: the compiled lens seeds itself with a synthetic bound from fold(d.ports, ...), spends that bound on helper-list recursion as well as the actual graph walk, and silently returns 0 on fuel exhaustion or failed lookup (pr-485.diff, lines 591–756; ALL_REVIEWS.txt, lines 1–24 and 29–68). That is not “new bug discovery”; that is one unresolved root cause getting restated. And it is a thesis-level root cause, not a local cleanup: gunbc says every computation must terminate with a proven bound, and emitted behavior should be a consequence of validated intent, not a best-effort fallback (THESIS.md, lines 15–23). The invariants say consumers must not reconstruct missing facts locally, and repeated local compensation means the fix belongs upstream, not in the consumer (INVARIANTS.md, lines 149–205). This loop has correctly identified that pattern, but it has not yet graduated it into a structural rule or substrate decision. None of the changed files are INVARIANTS.md, ROADMAP.md, or the discipline doc; the diff only touches the lens wrapper, generated module, test file, and complexity.dag itself (pr-485.diff, lines 1–99, 102–299, 300–566, 567–761). So the loop is still working the instance, not the class.

Cheating signal.

Mild, but real. I do not see deceptive accounting; the blocker stayed visible, and no one pretended it was gone. But I also do not see explicit scaffold accounting for the silent-0 fallback—no named trigger, no bounded bridge receipt, no tracked temporary compromise. And by the second browser round, the visible follow-up had narrowed to a naming/clippy pass (d78551f) while the only blocking issue remained untouched (ALL_REVIEWS.txt, lines 29–52). That is the classic finite-budget move: improve the ratchet and clean the surface without changing the structural contract. Rational, but it means another ordinary review round is likely to produce the same conversation again.

Meta-verdict

🔁 PAUSE_AND_REGROUP

The direction is right, and the first round added real value. But ordinary patch/review iteration has already extracted the real question—what is the authoritative decreasing measure and lookup authority for compiled lenses? Until that is answered, another normal round will mostly restate the same blocker. Keep the migration and the new consumer tests, but stop treating the remaining issue like a small fix. It is a substrate/design decision now.


View conversation

@briansrls

briansrls commented Apr 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Meta-Review (Loop Health)

Loop summary

I can verify 5 logged review events, of which 4 are substantive: browser reviews at 20:22 and 20:57 UTC, codex reviews at 20:25 and 21:36 UTC, plus another browser run that had only started by 21:40 UTC. That gives K = 2 codex reviews, L = 3 browser invocations (2 completed, 1 in progress), and about 78 minutes elapsed from first logged review to latest logged activity. The exact Git commit count is not recoverable from the supplied artifacts; the strongest defensible summary is at least 4 reviewed revisions / states. 195

Forward progress evidence

This loop is not pure churn. It did create a real consumer: the Go emitter stopped being paper-only and gained dedicated tests, including Rust↔Go behavioral coverage, which both browser and codex explicitly called out as meaningful progress. It also closed some earlier leaf findings: by the second browser review, the earlier scope drop was fixed; by the last codex review, the remaining blockers had narrowed to two, and codex explicitly said the prior scope/receipt issues were fixed. That means the loop is capable of closing concrete bugs and not just renaming them. 195

195

195

There is also some honest scaffold accounting. The moved shared strategy coproducts were rehomed with explicit yellow receipts and dissolution triggers, which is better than letting them drift undocumented. 195

Debt accumulation evidence

The loop’s core failure is that the same root class survives every round. At 20:22, the blocker was “facts produced upstream, silently dropped” — scope erased, LanguageSpec slots declared but not consumed, go_collection_ops declared but reimplemented locally, and go_rendering present but optional. At 20:57, scope was fixed, but the deeper problem remained: target ownership was still reconstructed by go_/rust_ prefixes and malformed go_rendering was still allowed to be skipped. At 21:36, codex was still flagging the same class: no structural “belongs to target” fact, plus Go silently narrowing PatternRealization. That is not findings graduating to invariants; it is the same debt resurfacing in new surfaces. 195

195

195

That repeated class is especially bad because the project’s own foundations already say what should happen instead. The thesis says a target spec should be the implementation, and adding a target should cost a spec file, not new compiler-side routing. The invariants say post-lowering consumers must use structural identity, not names; boundary facts must land end-to-end with an actual consumer; and new semantic boundaries that are only partially consumed are speculative metadata, not acceptable closure. This PR is still on the wrong side of those rules. chatgpt-review-8e1e31e2-3ecf-4f…

chatgpt-review-8e1e31e2-3ecf-4f…

chatgpt-review-8e1e31e2-3ecf-4f…

chatgpt-review-c4753f02-ed87-44…

chatgpt-review-c4753f02-ed87-44…

Cheating signal

The implementer is mixed. The good news is that the scaffolded strategy types are now documented instead of hidden. The bad news is that the highest-cost compromises are still being handled as quiet, local blast-radius minimizations instead of explicit structural closure: name-prefix routing for target ownership, a test that blesses skipping malformed go_rendering, and a narrowed local PatternRealization binding. That is exactly the “smaller local fix under finite budget” pattern you warned about: rational, but still debt-shifting. 195

195

Meta-verdict: 🔁 PAUSE_AND_REGROUP

The bluff to call is this: the loop is closing leaf bugs, but it is not converting the recurring root cause into structure. The Go target is now real enough to matter, which means continuing to patch per-surface symptoms has gone from useful to dangerous. Do not spend another round finding the next local instance.

The next move should be one structural step: define a typed target authority that owns realizations / patterns / collection ops / rendering / execution model as one bundle, and force emitters to read that bundle instead of filtering global declarations by rust_/go_ names. Until that lands, the next round is likely to produce one more genuine fix and one more hardened workaround. That is negative marginal value.

This is not REVERT_AND_RETHINK because the direction is right: shared emit_model.dag plus a live Go consumer is real progress. It is not SHIP_WITH_DEBT because the remaining debt sits in the exact authority boundary this PR is trying to establish. It is not KEEP_ITERATING because the project’s own prior meta-review lesson was: when the same root question survives multiple rounds, stop patching instances and graduate the class into an invariant. The current loop is at that point. chatgpt-review-c4753f02-ed87-44…

chatgpt-review-c4753f02-ed87-44…


View conversation

@briansrls

briansrls commented Apr 16, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Principle audit.

Fail-closed. Mostly satisfied. The new target-data loaders are generally strict: missing or malformed target facts become EmitError::MissingTargetSyntax / EmitError::MalformedRealization, which is the right shape for this layer. I did not see a new user-path panic I’d stop on in the diff.

Illegal states unrepresentable. Mostly satisfied at the substrate level. Moving shared realization/meta-types into src/v3/std/emit_model.dag and target execution facts into src/v3/std/computation_model.dag is a good step toward typed target facts instead of ad-hoc per-emitter conventions. The weak spot is implementation-side: src/v3/compiler/src/emit_go.rs:87-90 narrows TypeDefinitionSyntax to only two fields, so the emitter can silently forget enum-lowering facts that the spec did declare.

Facts flow forward. BLOCKING. This is the real issue I’d stop on. src/v3/std/emit_model.dag:134-140 defines TypeDefinitionSyntax with enum_def, enum_unit_variant, and enum_data_variant, and src/v3/spec/go.dag:637-643 supplies those facts for Go. But src/v3/compiler/src/emit_go.rs:1547-1555 reads only struct_def and struct_field, then render_type_declaration hardcodes sum lowering with bare variant.label names (emit_go.rs:1242-1277), and the constructor/match paths do the same (emit_go.rs:1079-1121, emit_go.rs:733-776). That means a module with two disjs that both use Ok/Err will emit duplicate top-level Go types in one package. The upstream fact exists; the consumer drops it and compensates locally. That is exactly the THESIS/INVARIANTS failure mode this project is trying to eliminate.

Coproduct dissolution. Satisfied. I did not see a new substrate Rust enum here that needs a terminal/scaffold/dissolve-now classification. The new Rust enums in the emitter are implementation-local binding enums, so the full substrate dissolution rule does not bite. The new .dag strategy coproducts in emit_model.dag at least carry scaffold receipts, which is the right discipline for this milestone.

Single-authority metadata. Mixed. This PR improves the authority story by pulling realization meta-types out of rust.dag and into shared v3.std.emit_model, which is real progress. The blocking concern above is also a single-authority problem: Go immediately creates a second authority for sum-type lowering instead of consuming TypeDefinitionSyntax as the one canonical source.

API-level enforcement. Not there yet for the new shared syntax surface. Nothing in the current API forces an emitter to consume the full shared syntax record, so emit_go.rs can narrow it and proceed. That is why the enum-definition facts can be lost without any compiler pressure. I would be more comfortable once the binding layer either carries the full shared record forward or the shared record is split so omission is impossible by construction.

Design question.

Is a target supposed to be one structural authority that owns syntax + execution model + realizations, or are emitters still allowed to reconstruct “what belongs to Go” from partial records plus local convention?

What is at stake is larger than this one bug. Right now the PR is clearly moving toward the first answer — shared emit_model.dag, shared computation_model.dag, typed target bundles in dag.rs — but the Go emitter still behaves like the second answer in a few key places. That is how you get both the already-known target-routing issues and this new sum-type collision path: the shared target bundle exists, but the emitter is still free to re-decide parts of it locally. If the project wants THESIS-style “targets are declarations, not compiler features,” the emitter needs to consume the target bundle as authority, not as a hint.

Verdict.

REQUEST_CHANGES

This is real forward progress: sharing the emit-model substrate and adding a Rust/Go behavioral parity test are both strong moves. But I would stop on the dropped TypeDefinitionSyntax facts in emit_go.rs, because that is a real Go correctness hole now, and it will get harder to unwind once more sum-type programs and more target-specific logic accumulate around the current lowering.

LOOP HEALTH: converging — this round adds a real consumer (Go emission plus parity testing) and narrows the remaining debt to one target-authority class, but that class is still substrate-shaped enough that I’d fix it before layering more Go cases on top.


View conversation

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · 796174fe

✅ Review (blocking: 0, non-blocking: 0+/0-)

⚠️ No new concerns beyond the two already-open blockers; five earlier comments are fixed, but those remaining substrate issues still prevent LGTM.

@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 · 796174fe

✅ Review (blocking: 0, non-blocking: 0+/0-)

⚠️ No new concerns beyond the two already-open blockers; five earlier comments are fixed, but those remaining substrate issues still prevent LGTM.

@briansrls

Copy link
Copy Markdown
Contributor Author

Closing in favor of a unified reconciliation approach.

The merge-train experiment on ci-fixed-point-stability surfaced that the PRs have overlapping structural concerns that don't reconcile cleanly at merge time (schema drift on TargetExecutionModel, CallableRealization, ParameterDisposition; spec-to-emit boundary leaks; missing dissolution receipts; fail-open paths where fail-closed is required; duplicated/conflicting target dispatch keyed on name prefix rather than typed facts).

Rather than push each branch through another round of rebase-fix-review that would repeatedly rediscover cross-branch conflicts, all six PRs (#482, #483, #484, #485, #486, #487) are being reconciled into a single reconciliation-l1.5 branch that lands the deliverables coherently:

All review comments (codex, chatgpt-browser, director) harvested into .claude/pr-reconciliation.md so nothing is lost.

Class of fixes addressed uniformly in reconciliation:

  • C1: 🟢/🟡/🔴 receipts on every new coproduct (modeling-discipline §4)
  • C2: every spec field consumed by its emitter, fail-closed on unsupported
  • C3: one canonical schema for TargetExecutionModel / CallableRealization / ParameterDisposition across std/ and specs
  • C4: target selection keyed on typed substrate facts, not name prefix
  • C5: all analysis paths return Result — no silent defaults on invariant breaks
  • C6: last-use tracks borrowed + consumed uses (FF-1)
  • C7: four-fixture suite gets non-Copy variants + signature-shape assertions
  • C8: L2 lens migrations follow a single established pattern (structural measure, not fuel; private generated wrapper)
  • C9: per-emitter bugs fixed (Go ListFold scope, Python variant naming, etc.)
  • C10: stage0 regen --output-dir path runs fmt too

Thanks for the work — all of it ships through the reconciliation.

@briansrls briansrls closed this Apr 16, 2026
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