Skip to content

M5-early: coercion data infrastructure — checkpoint + inhabitant registries - #286

Merged
briansrls merged 5 commits into
mainfrom
cursor/m5-early-coercion-1883
Apr 1, 2026
Merged

briansrls merged 5 commits into
mainfrom
cursor/m5-early-coercion-1883

Conversation

@briansrls

@briansrls briansrls commented Apr 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Establishes the coercion data infrastructure for M5-early, replacing hardcoded per-language type maps with structured TypeCheckpoint and InhabitantDecl registries that mirror the std/coercion.dag schema and per-language data from dsl/extdeps/languages/{rust,python,go}/types.dag.

This is the foundational layer that build_type_rendering will read from directly once the old emit_node_type_rc path is fully removed (E0c completion). The design doc is at docs/coercion-design.md.

Changes

  • New v2_coercion.rs module with TypeCheckpoint, InhabitantDecl, CallableRepr, and CoercionRegistry structs.
  • Per-language checkpoint + inhabitant data populated from .dag declaration data (Rust: 8 checkpoints + 5 inhabitants, Python: 8+5, Go: 8+5).
  • target_primitive_type now reads from CoercionRegistry.lookup_checkpoint instead of per-language *_TYPE_MAP hash maps.
  • Container identity (is_known_keyed_container_name, is_known_element_container_name) derived from coercion inhabitant arity instead of hardcoded const arrays.
  • New target_container_template_bare for algebra-based bare templates (no Rc wrapping baked in) — ready for the TypeRendering path.
  • 7 coercion registry tests validating cross-language checkpoint resolution, inhabitant templates, is_copy data, and template application.
  • ROADMAP updated with 6 completed M5-early items.

Review feedback addressed

Remaining review items (tracked, not blocking per queue discipline)

  • Add SVG viz, test helpers, and makegen scaffold #1 Fail-open fabrication: coerce_primitive_type falls through to dag_name on miss. Transitional — M5-full will make this fail-closed.
  • Codex/graph viz test helpers #2 Duplicate authority: Rust tables are hand-maintained mirrors of .dag data. Dissolves when the compiler reads .dag data at runtime (M4/Lane 1 Tier 3).
  • . #3 Name-based dispatch: dag_container_to_algebra uses a string ladder. Dissolves when algebra identity flows from declaration edges (M4/Lane 1).

Authority chain

std/coercion.dag           → shared schema (TypeCheckpoint, InhabitantDecl, CallableRepr)
extdeps/languages/*/types.dag → per-language data instances
v2_coercion.rs             → stage0 Rust mirror (consumed by target_primitive_type, TypeRendering)

Testing

  • ✅ cargo clippy --all-targets -- -D warnings
  • ✅ cargo test --workspace --exclude v2-compiler-tests (19 passed, 0 failed)
  • ✅ cargo test -p v2-compiler-tests (282 passed, 0 failed)
  • ✅ cargo test -p v2-compiler-tests full_dsl_compiles -- --ignored
  • ✅ cargo test -p v2-compiler-tests strict_compile_diagnostic_count -- --ignored
  • ✅ scripts/l1-ratchet.sh --check (L1 21 ≤ 22)
  • ⚠️ bootstrap_fixed_point — pre-existing failure (fails on main too)
Open in Web Open in Cursor 

cursoragent and others added 3 commits April 1, 2026 21:31
…tion

Introduce v2_coercion module mirroring std/coercion.dag schema:
- TypeCheckpoint, InhabitantDecl, CallableRepr structs
- CoercionRegistry with checkpoint/inhabitant lookup
- Per-language data matching dsl/extdeps/languages/{rust,python,go}/types.dag

Wire target_primitive_type through CoercionRegistry.lookup_checkpoint,
replacing direct reads from per-language RUST_TYPE_MAP/PYTHON_TYPE_MAP/GO_TYPE_MAP
for primitive type resolution.

Add target_container_template_bare for algebra-based bare container
templates (without sharing wrapping), for use by the TypeRendering path.

Replace hardcoded KEYED_CONTAINER_NAMES/ELEMENT_CONTAINER_NAMES with
coercion-derived lists from COERCION_KEYED_CONTAINER_NAMES/COERCION_ELEMENT_CONTAINER_NAMES.

Authority chain: std/coercion.dag → extdeps/languages/*/types.dag → v2_coercion.rs → build_type_rendering

Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
…t data

8 new tests proving:
- Rust/Python/Go primitive type checkpoints produce correct target types
- Container algebra inhabitants resolve to correct templates
- Cross-language inhabitant templates (Vec vs list vs [])
- is_copy checkpoint data correct for Rust (Copy vs !Copy)
- Template application with positional parameters
- Parity between coercion registry and legacy type maps

Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
Mark 6 items complete in M5-early section:
- Coercion data structures mirroring std/coercion.dag
- Per-language checkpoint + inhabitant data
- Primitive type resolution via CoercionRegistry
- Container identity derived from inhabitant arity
- Bare container templates for TypeRendering path
- 8 coercion registry tests

Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review (5 violations, 0 good, 0 improve)

Invariant violations (5)

The checkpoint introduces useful coercion scaffolding, but in its current form it still violates core invariants by duplicating authority, failing open on unknown types, reintroducing name-based dispatch, adding per-call rebuild overhead, and enshrining a self-comparison test.

pub fn coerce_primitive_type(registry: &CoercionRegistry, dag_name: &str) -> String {
match registry.lookup_checkpoint(dag_name) {
Some(cp) => cp.target_type.clone(),
None => dag_name.to_string(),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Invariant violation: Returning the original dag_name on a missing checkpoint is a fail-open fabrication path, violating Modeling Faithfulness and No fallbacks that fabricate.

// Per-language data (mirrors dsl/extdeps/languages/*/types.dag)
// =========================================================================

pub fn rust_checkpoints() -> Vec<TypeCheckpoint> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Invariant violation: This new hand-maintained per-target coercion table block creates a second authority in Rust instead of deriving from the .dag source, violating No duplicate representations and No parallel implementations.

///
/// The .dag language uses `List`, `Set`, `Map` etc. as surface names.
/// The coercion engine resolves these through their algebra identities.
fn dag_container_to_algebra(name: &str) -> Option<&'static str> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Invariant violation: dag_container_to_algebra hardcodes a string-matched name ladder for container identity, violating No case enumeration for open sets and Heuristics indicate lost structure.

},
RenderTarget::Dag => name.clone(),
}
let registry = crate::v2_coercion::registry_for_target(&target);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Invariant violation: target_primitive_type now rebuilds a fresh coercion registry on every lookup instead of reusing one indexed authority, violating the Performance Invariant's one-time indexing rule.

Comment thread src/v2/stage0/src/compiler_tests.rs Outdated
use crate::v2_compiler_languages::target_primitive_type;
// Verify coercion produces the same results as legacy type maps
for name in &["Int", "Float", "Bool", "Unit", "String", "Bytes", "Secret", "Json"] {
let legacy = target_primitive_type(RenderTarget::Rust, name.to_string());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Invariant violation: This "legacy parity" test is tautological because target_primitive_type now delegates to coerce_primitive_type, violating the No tautological tests invariant.

@briansrls

Copy link
Copy Markdown
Contributor Author

Invariant review (39eea4c1)

Violations (5):

  • src/v2/stage0/src/v2_coercion.rs:259 Returning the original dag_name on a missing checkpoint is a fail-open fabrication path, violating Modeling Faithfulness and No fallbacks that fabricate.
  • src/v2/stage0/src/v2_coercion.rs:120 This new hand-maintained per-target coercion table block creates a second authority in Rust instead of deriving from the .dag source, violating No duplicate representations and No parallel implementations.
  • src/v2/stage0/src/v2_coercion.rs:278 dag_container_to_algebra hardcodes a string-matched name ladder for container identity, violating No case enumeration for open sets and Heuristics indicate lost structure.
  • src/v2/stage0/src/v2_compiler_languages.rs:276 target_primitive_type now rebuilds a fresh coercion registry on every lookup instead of reusing one indexed authority, violating the Performance Invariant's one-time indexing rule.
  • src/v2/stage0/src/compiler_tests.rs:711 This "legacy parity" test is tautological because target_primitive_type now delegates to coerce_primitive_type, violating the No tautological tests invariant.

The checkpoint introduces useful coercion scaffolding, but in its current form it still violates core invariants by duplicating authority, failing open on unknown types, reintroducing name-based dispatch, adding per-call rebuild overhead, and enshrining a self-comparison test.

@briansrls
briansrls marked this pull request as ready for review April 1, 2026 21:46

@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: 39eea4c181

ℹ️ 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 on lines +276 to +277
let registry = crate::v2_coercion::registry_for_target(&target);
crate::v2_coercion::coerce_primitive_type(&registry, &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.

P2 Badge Cache coercion registries instead of rebuilding per lookup

target_primitive_type now constructs a fresh CoercionRegistry on every call, which allocates and populates multiple Vec/HashMap structures before doing a single lookup. This function sits on the type-emission hot path (emit_primitive_type), so large compiles will pay this cost repeatedly and regress compile performance compared with the previous static-map lookup behavior. Reusing a per-target static/lazy registry would preserve the new data model without adding per-lookup allocation overhead.

Useful? React with 👍 / 👎.

Comment thread src/v2/stage0/src/compiler_tests.rs Outdated
Comment on lines +711 to +713
let legacy = target_primitive_type(RenderTarget::Rust, name.to_string());
let reg = registry_for_target(&RenderTarget::Rust);
let coerced = coerce_primitive_type(&reg, 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.

P2 Badge Validate parity test against legacy maps directly

This test no longer checks parity with the legacy type maps because legacy is computed via target_primitive_type, which was changed in the same commit to call the coercion registry path. As a result, the assertion compares two values produced by the same implementation and will still pass if checkpoint data drifts from RUST_TYPE_MAP/PYTHON_TYPE_MAP/GO_TYPE_MAP, so the intended regression guard is effectively disabled.

Useful? React with 👍 / 👎.

cursoragent and others added 2 commits April 1, 2026 21:50
…zy_static

Addresses review violation #4 (per-call rebuild overhead):
- Replace registry_for_target() value return with &'static references
  to lazy_static singletons (RUST_REGISTRY, PYTHON_REGISTRY, etc.)
- Registries built once at first access, O(1) thereafter

Also addresses violation #5 (tautological test):
- Delete coercion_parity_with_legacy_type_maps test — target_primitive_type
  now delegates to coerce_primitive_type, making it a self-comparison

Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
…t count

Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
@briansrls
briansrls merged commit 784543e into main Apr 1, 2026
1 check passed
@briansrls
briansrls deleted the cursor/m5-early-coercion-1883 branch June 1, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants