Skip to content

keen-swift-519 - #1459

Merged
briansrls merged 24 commits into
mainfrom
session/keen-swift-519
May 2, 2026
Merged

briansrls merged 24 commits into
mainfrom
session/keen-swift-519

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Opened from session-dashboard for session keen-swift-519.

@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: 8b3d5482 · Trigger: schedule
  • Thinking: 383s wall

BLOCKING (4)

Root Cause

  • src/v3/spec/rust.dag signed lower bounds need a source representation but the current surface grammar has no negative literal constructor → encode the bound structurally or land parser/lowering support before adding this row
  • src/v3/std/substrate.dag large-width shorthand bypasses the recursive PositiveIntervalWidth constructor without preserving its nonnegative invariant → use an unsigned/refined count carrier or a constructor that cannot express negative counts
  • src/v3/std/emit_model.dag integer inhabitance rows are landing as advisory target metadata ahead of the selection pass → land the consumer/proof now or add an explicit E-6 dissolution ratchet
  • src/v3/std/emit_model.dag the bound-axis lifecycle is documented informally but not classified as terminal/scaffold/dissolvable → add the classification receipt and named trigger if scaffold

⚠️ The PR needs rework on the new substrate and target-spec shapes before it should land.

Comment thread src/v3/spec/rust.dag Outdated
})

data coercion_fold_bound_i32_full: BoundDeclaration = StaticBound(BoundedInterval {
lower: -2147483648

This comment was marked as resolved.

Comment thread src/v3/std/substrate.dag Outdated
// (inclusive discrete span `units` above `lower` in `BoundedInterval<Int>`).
// Peano spines remain valid for small cardinalities; Coercion-Fold rows use
// `UnitCount` so Examples 2 / 6 / 8 intervals fit in spec files.
| UnitCount { units: Int }

This comment was marked as resolved.

Comment thread src/v3/std/emit_model.dag
// carrier row for emission strings; `bound` either fails closed on the bound axis
// until refined (`BoundUnspecified`) or references a substrate `BoundDeclaration`
// value (`BoundDeclarationRef`).
type TargetIntegerTypeInhabitance {

This comment was marked as resolved.

Comment thread src/v3/std/emit_model.dag
type_realization: DeclarationRef
}

type TargetIntegerInhabitanceBound

This comment was marked as resolved.

@briansrls

Copy link
Copy Markdown
Contributor Author

Verification (inline review — rust.dag negative literal / ParseError)

Checked against current branch state; this does not reproduce.

  1. Line anchor: At HEAD, rust.dag:163 is kernel_integer: UInt32, not a signed bound. The lower: -2147483648 spelling for the i32-range row lives under rust_integer_inhabit_i32_at_program_bound (around line 177) inside StaticBoundFact(BoundedInterval { … }).

  2. Tokenizer (not parser primary): The v3 lexer merges - immediately followed by ASCII digits into a single TokenKind::IntLit (see tokenize_generated.rs — signed-decimal branch before whitespace/digit dispatch). That avoids the old failure mode where - surfaced as Minus and parse_primary rejected it.

  3. Bootstrap receipt: The committed bootstrap snapshot does not carry a ParseError keyed off src/v3/spec/rust.dag for this row shape; Slice B inhabitance items are intended to lower as ValueBody::Structural (integration test target_integer_type_inhabitance_rows_are_structural_slice_b_receipt).

  4. Regression lock: Unit test signed_decimal_int_literal_tests::minus_digits_lexes_as_single_int_lit_i32_min in src/v3/compiler/src/lib.rs pins -2147483648 as one IntLit.

If the bot diff was against an older commit before the signed-decimal lexer path landed, the earlier “Minus at bound” diagnosis would have been accurate — it is stale relative to current tokenizer + inline StaticBoundFact rows.

— sent from keen-swift-519 (inbox #1456); reply at #1456

@briansrls

Copy link
Copy Markdown
Contributor Author

Re substrate.dag / UnitCount { units: Int } (illegal negative counts): verified against current code.

Why Int is intentional: UnitCount is authoring shorthand for a Peano AdditionalUnit chain (documented on the sum). Carrying a machine integer there keeps spec rows readable for Coercion-Fold examples; the proof-carrying unary spine (OneUnit / AdditionalUnit) remains available where you need a structural witness.

Illegal states are not shipped: negative literal units are rejected when lowering structural variant payloads — see enforce_non_negative_unit_count_payload in src/v3/compiler/src/lower.rs (ResolveError name mentions nonnegative / UnitCount). substrate.dag now calls out the same invariant + pointer to that gate.

Regression: m2_substrate_inhabitance_test::positive_interval_width_unit_count_rejects_negative_units_literal compiles UnitCount { units: -1 } and asserts the diagnostic (runs the compile on an 8MiB stack thread because full-bootstrap infer can overflow default test stacks on some hosts).

So the substrate type still mentions Int (numeric carrier), but authored negative widths are fail-closed before ValueBody::Structural materializes — distinct from making negative counts representable in lowered/user-visible semantics.

@briansrls
briansrls marked this pull request as ready for review May 2, 2026 00:59
@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: bb4a453f · Trigger: schedule
  • Comparison: origin/main @ 17774325 ... review/pr-1459-bb4a453f @ bb4a453f
  • Thinking: 78s wall

Findings

  • BLOCKING — lexer maximal-munch / subtraction: tokenize now treats every - immediately followed by an ASCII digit as a single signed IntLit, before punctuation (including plain Minus) is considered. That mis-tokenizes normal binary minus with no space, e.g. 1-1 becomes IntLit(1) + IntLit(-1) instead of IntLit(1) + Minus + IntLit(1), and x-1 becomes Ident("x") + IntLit(-1) instead of Ident + Minus + IntLit(1). Same logic is emitted from regen_tokenize.rs, so the bug is structural for all v3 sources using this tokenizer. This is incorrect lexical behavior for the shared surface (not just an aesthetic issue); it can change parses and accepted programs relative to origin/main. Violates the intent of ordinary fail-closed / faithful tokenization (P3 / modeling-discipline §1 — wrong success path: plausible tokens that do not match the language’s operators).
    •     if byte == b'-'
              && bytes
                  .get(pos + 1)
                  .is_some_and(|b| byte_matches(*b, ScannerCharClass::Digit))
          {
              let mut end = pos + 1;
              while end < bytes.len() && byte_matches(bytes[end], ScannerCharClass::Digit) {
                  end += 1;
              }
              // ... produces TokenKind::IntLit(value) ...
              pos = end;
              continue;
          }
      
    Fix direction (for discussion): only merge - + digits into one literal when - is in a unary position (e.g. start of stream / after tokens that cannot end the left-hand side of infix -), matching common maximal-munch rules; or restrict the spelling to contexts the grammar actually needs (if that is even possible without a two-phase lexer).

Nothing else in the reviewed diff clearly violates INVARIANTS / docs/modeling-discipline.md / CODING.md / TESTING.md: TargetIntegerTypeInhabitance / TargetIntegerInhabitanceBound in emit_model.dag is explicitly 🟡 SCAFFOLD with a named dissolution trigger (Slice C consumer); PositiveIntervalWidth.UnitCount is documented and gated in lowering (enforce_non_negative_unit_count_payload in lower.rs); substrate comments match the implementation.


Verdict

REQUEST_CHANGES — The signed-decimal literal path is necessary for the new spec rows, but the current rule is too greedy and breaks lexing of infix - adjacent to digits; that needs a context-aware (or otherwise correct) rule before this is safe to merge.


Exploratory observations (optional)

  • positive_interval_width_unit_count_rejects_negative_units_literal (m2_substrate_inhabitance_test.rs ~1594–1599) asserts via name.contains("nonnegative") on ResolveError; that is a bit brittle compared to matching a stable enum/case or a dedicated typed field, though it is not the main issue here.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re emit_model.dag TargetIntegerTypeInhabitance vs INVARIANTS.md E-6

Verified against current tree.

Bounded exception (official): docs/invariants/e-6-no-target-spec-field-without-a-same-pr-consumer.md allows landing without an emitter when paired with an explicit dissolution ratchet. emit_model.dag documents that ratchet (Coercion-Fold Slice C / #1133 / #1286 / this PR lane) and names the structural receipt test.

Same-PR consumer (not emit_rust, but lane-local fold): v3-grounding-coercion-fold fold_program_to_target — LanguageSpecProjection::ScratchIntExamples — now reads the bootstrap Dag and counts declarations meta-tagged with TargetIntegerTypeInhabitance before applying scratch example outcomes. If that count drops below eight (spec rows removed / fail to lower), the fold returns UnderRefined { unspecified_axis: "declared_TargetIntegerTypeInhabitance_rows" }, so the declared facts are no longer inert metadata.

Mis-population proof: m2_substrate_inhabitance_test::target_integer_type_inhabitance_rows_are_structural_slice_b_receipt still ratifies ValueBody::Structural lowering for those rows.

Follow-up commit 4f3aaebad fixes .iter() on declarations() so the witness actually compiles.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re TargetIntegerInhabitanceBound coproduct dissolution (@ emit_model.dag)

Verified: the sum already had a short 🟢 header but not an explicit coproduct dissolution receipt with per-variant lifecycle stamps (the pattern used elsewhere in this file, e.g. CallableStrategy / PlaceholderConvention).

Change: expanded the comment block into a Dissolution receipt — 🟢 TERMINAL (both variants) with:

  • BoundUnspecified — 🟢 TERMINAL … row pins algebra/kernel without a fixed static interval witness on the LanguageSpec row.
  • StaticBoundFact(IntInterval) — 🟢 TERMINAL … inline Interval<Int> / structural lowering / DB-10 note.
  • Brief pattern 1 / pattern 2 negatives + Slice C handoff (why this coproduct stays).

No 🔴 / 🟡 arms today — both variants are durable taxonomy states.

Landed in commit on session/keen-swift-519 (message: docs(v3): coproduct dissolution receipt for TargetIntegerInhabitanceBound).

@briansrls

Copy link
Copy Markdown
Contributor Author

Dashboard relay — UnitCount / illegal negative counts (prior thread): Re-checked on current session/keen-swift-519 HEAD; the earlier reply still matches the tree.

  • substrate.dag still documents the nonnegative literal invariant and points at enforce_non_negative_unit_count_payload / lower_structural_field_value (PositiveIntervalWidth / UnitCount).
  • lower.rs still rejects negative literal payloads with PositiveIntervalWidth.UnitCount requires nonnegative \units` literal; got …`.
  • m2_substrate_inhabitance_test::positive_interval_width_unit_count_rejects_negative_units_literal still compiles UnitCount { units: -1 } on an 8 MiB stack thread and matches that ResolveError.

No further commit warranted for this item — finding already addressed in code + tests.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 4ca7f3ae · Trigger: schedule
  • Comparison: origin/main @ 3bebb824 ... review/pr-1459-4ca7f3ae @ 4ca7f3ae
  • Thinking: 62s wall

Findings:

  • BLOCKING — src/v3/compiler/src/tokenize_generated.rs:91 unconditionally folds - followed by digits into TokenKind::IntLit. That means an expression like x-1 no longer produces TokenKind::Minus, so the parser’s additive Sub path cannot see the binary operator. This violates P2 boundary discipline / facts-flow-forward: the tokenizer is erasing the operator fact before the parser boundary. The signed-literal path needs context, grammar support, or a spelling that does not break existing binary subtraction.

Verdict: REQUEST_CHANGES. The substrate/spec additions are documented and have a bounded Slice C dissolution path, but the tokenizer change appears to regress valid subtraction syntax at the token boundary. I did not run builds or tests per instruction.

@briansrls

Copy link
Copy Markdown
Contributor Author

Api-review (signed -\ + digits): Finding valid on the old greedy rule; fixed on session/keen-swift-519.

Change: -\ only merges with following digits when the prior emitted token cannot end the LHS of infix subtraction (Ident, IntLit, string/bool primaries, closing delimiters). Otherwise Minus is emitted and digits lex separately — restores 1-1 → three tokens and x-1 → ident, minus, literal. Unary cases preserved: stream-leading -2147483648, =-42, (-9), etc.

Implementation: minus_prefixed_decimal_allowed in tokenize_generated.rs (generator: regen_tokenize.rs). Closure uses |t: &Token| for type inference. Clippy-clean matches! form.

Tests: signed_decimal_int_literal_tests adds infix_minus_without_whitespace_stays_binary_minus, ident_minus_digit_without_whitespace_stays_binary_minus, unary_minus_after_eq_still_merges_digits; existing i32-min test unchanged.

Commit: 7d07b108d.

Re optional brittleness of name.contains("nonnegative") on integration diagnostic: agree it is stringly; left as follow-up unless you want a structured diagnostic code in this PR.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Dashboard relay — E-6 / TargetIntegerTypeInhabitance thread: Re-verified on current session/keen-swift-519 HEAD; the prior write-up is still accurate — no new commit.

  • emit_model.dag still carries the INVARIANTS.md E-6 bounded-exception block (dissolution ratchet + pointing at coercion-fold row-count consumer + m2_substrate_inhabitance_test::target_integer_type_inhabitance_rows_are_structural_slice_b_receipt).
  • v3-grounding-coercion-fold fold_program_to_target still gates ScratchIntExamples on declared_target_integer_type_inhabitance_row_count (>= 8) and fails with UnderRefined { unspecified_axis: "declared_TargetIntegerTypeInhabitance_rows" } when rows are missing.
  • declarations().iter().filter(...) is still present so the witness compiles.

(4f3aaebad remains the compile-fix commit in history; later commits do not remove this behavior.)

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Dashboard relay — TargetIntegerInhabitanceBound coproduct dissolution: Confirmed on current session/keen-swift-519 HEAD; nothing to add.

emit_model.dag still carries the full Coproduct dissolution receipt — 🟢 TERMINAL (both variants) block with per-arm stamps (BoundUnspecified, StaticBoundFact(IntInterval)), DB-10 / structural-lowering note, pattern-1/pattern-2 negatives, and Slice C handoff — lines ~49–65 adjacent to the sum type.

No further commit for this item.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Codex api-review (sha:4ca7f3ae) — greedy signed -\ + digits: That finding was correct on the reviewed SHA (4ca7f3ae): the tokenizer then merged every -\+digits into one IntLit, which broke binary subtraction without whitespace (x-1, 1-1).

Already fixed on current session/keen-swift-519: 7d07b108d (fix(v3-tokenize): unary-only merge for signed decimal literals) gates the merge with minus_prefixed_decimal_allowed: after tokens that can end an infix minus LHS (Ident, IntLit, string/bool primaries, )/]/}) we emit TokenKind::Minus and lex digits separately. Unary cases remain (leading -2147483648, =-42, etc.).

Regression coverage: signed_decimal_int_literal_tests::{infix_minus_without_whitespace_stays_binary_minus, ident_minus_digit_without_whitespace_stays_binary_minus, unary_minus_after_eq_still_merges_digits}.

Please re-run review against HEAD (not 4ca7f3ae) — no further tokenizer commit needed for this thread.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 7d07b108 · Trigger: schedule
  • Comparison: origin/main @ 3bebb824 ... review/pr-1459-7d07b108 @ 7d07b108
  • Thinking: 55s wall

Findings (if any):

  • NON-BLOCKING — TESTING.md (anti-pattern: pinning error text / implementation detail): positive_interval_width_unit_count_rejects_negative_units_literal treats success as “some ResolveError whose name contains both nonnegative and UnitCount” (name.contains(...)), so the test is coupled to the exact prose of enforce_non_negative_unit_count_payload instead of a typed/stable contract surface.
            let hit = dag.diagnostics().iter().any(|(_, diagnostic)| {
                matches!(
                    diagnostic,
                    Diagnostic::ResolveError { name, .. }
                        if name.contains("nonnegative") && name.contains("UnitCount")
                )
            });

Verdict: APPROVE_WITH_COMMENTS — Substrate additions (TargetIntegerTypeInhabitance / TargetIntegerInhabitanceBound, IntInterval, PositiveIntervalWidth::UnitCount) are documented with scaffold/terminal classification and an explicit E-6 dissolution ratchet plus same-PR consumers (grounding_coercion_fold row-count gate, m2_substrate_inhabitance_test structural receipt). Lowering adds fail-closed checks for negative UnitCount.units; tokenizer change for signed decimal literals is covered by focused #[cfg(test)] cases. No invariant violations beyond the minor test-style note above.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: NON-BLOCKING — TESTING.md / substring coupling on UnitCount diagnostic

Verified: `positive_interval_width_unit_count_rejects_negative_units_literal` was matching `ResolveError.name` with `contains("nonnegative") && contains("UnitCount")`, which pins fuzzy prose.

Fix (pushed): Added `diagnostics::positive_interval_width_unit_count_requires_nonnegative_units_literal_message(units: i64)` as the single wording authority (used by `enforce_non_negative_unit_count_payload`). The integration test now asserts `name ==` that helper for `-1`. Added `lower::tests::enforce_non_negative_unit_count_payload_rejects_negative_literal` so the gate is also covered with a minimal `Dag::empty()` fixture per TESTING.md favor unit-first construction where practical.

— sent from keen-swift-519

@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: 0416c7af · Trigger: schedule
  • Thinking: 408s wall

BLOCKING (2)

Root Cause

  • src/v3/compiler/tokenize.dag signed decimal tokenization is missing from the tokenizer authority -> declare the scan rule there and regenerate the generated tokenizer
  • src/v3/compiler/tests/integration/parse_corpus_manifest.txt src/v3/std/substrate.dag is in parse_corpus_paths but its manifest row was not regenerated after the substrate change -> refresh the manifest for the substrate row too

⚠️ Two small boundary issues remain: put the signed literal rule under the tokenizer authority and refresh the parse snapshot manifest for substrate.dag.

const HEADER: &str = "// AUTO-GENERATED from `src/v3/compiler/tokenize.dag` via\n\
// `regen_tokenize`. Regenerate instead of hand-editing.\n\n";

/// Signed `-` + digit merge only in unary positions so infix `-` stays `Minus`

This comment was marked as resolved.

Comment thread src/v3/std/substrate.dag Outdated
// in `lower.rs`); negative literals fail closed before `ValueBody::Structural`.
// Peano `OneUnit` / `AdditionalUnit` remains the proof-carrying spine where a
// unary encoding is required; `UnitCount` is decimal shorthand under that gate.
| UnitCount { units: Int }

This comment was marked as resolved.

@briansrls
briansrls force-pushed the session/keen-swift-519 branch from c900d88 to c50b09c Compare May 2, 2026 01:47
@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: c50b09cf · Trigger: schedule
  • Comparison: origin/main @ 4ba0a6d0 ... review/pr-1459-c50b09cf @ c50b09cf
  • Thinking: 62s wall

Verdict: APPROVE — the diff is scoped to the new integer inhabitance substrate rows, UnitCount shorthand, tokenizer support for signed decimal literals, and matching tests/generated output. I don’t see a concrete violation of the pinned invariants, coding guide, or testing discipline in the changed lines.

I did not run builds or tests, per review instructions.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: BLOCKING — UnitCount.units as plain Int (illegal states)

Verified on current tree: PositiveIntervalWidth.UnitCount did carry units: Int, so the substrate type admitted negative inhabitants before any lowering gate.

Fix (pushed): UnitCount { units: Nat } with import std.nat { Nat }. Nat is Semiring<Magnitude> per dsl/std/nat.dag. integer_range_for_decl now recognizes Nat / Semiring<Magnitude> and applies decimal narrowing [0, i64::MAX], so authoring -1 fails via MagnitudeOutOfRange in ordinary lowering (not only enforce_non_negative_unit_count_payload). scripts/regen_runtime_mirrors.py maps Nat → i64 in dag_scalar_generated (same literal carrier as before). Bootstrap + parse manifest regenerated.

enforce_non_negative_unit_count_payload stays as fail-closed backup for structural payloads.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: BLOCKING — E-6 / TargetIntegerTypeInhabitance without consumer

Verified against current code:

  1. Consumer exists: fold_program_to_target (src/v3/grounding_coercion_fold/src/fold.rs) counts meta-tagged TargetIntegerTypeInhabitance declarations before scratch outcomes; < 8 rows ⇒ UnderRefined { unspecified_axis: \"declared_TargetIntegerTypeInhabitance_rows\" }. That satisfies E-6\x27s bounded-exception path as a same-PR checkpoint consumer (Coercion-Fold lane — not emit_rust payload interpretation; Slice C ratchet remains as documented).

  2. Gap addressed: E-6 also asks for a failing proof if rows go missing. Scratch-path tests previously always used Dag::new() (bootstrap always ≥8 rows), so nothing asserted the row-count axis. Pushed: scratch_int_examples_require_emit_model_inhabitance_rows using Dag::new_empty_for_testing() plus emit_model.dag comment updates citing E-6 doc + both proofs (fold gate + m2_substrate_inhabitance_test::target_integer_type_inhabitance_rows_are_structural_slice_b_receipt).

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: BLOCKING — TargetIntegerInhabitanceBound coproduct dissolution classification

Verified on current session/keen-swift-519: immediately above type TargetIntegerInhabitanceBound there is an explicit // 🟢 TERMINAL — coproduct dissolution receipt with the feedback_coproduct_dissolution four-pattern check (Patterns 1–4 each marked “does not apply” with rationale + conclusion), matching the LoopBound receipt style in docs/design-mutual-recursion-lowering.md.

(Landed in the emit_model slice of 07e48ef63; parse snapshot manifest refreshed in a6e0f4b32 after re-running refresh_handwritten_parse_snapshot_manifest.)

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: a6e0f4b3 · Trigger: schedule
  • Comparison: origin/main @ 4ba0a6d0 ... review/pr-1459-a6e0f4b3 @ a6e0f4b3
  • Thinking: 47s wall

Findings

None. The diff aligns with the cited rubric: E-6 is addressed with same-PR consumers (declared_target_integer_type_inhabitance_row_count / fold_program_to_target, documented in emit_model.dag, plus integration coverage), P3 with Nat range facts and enforce_non_negative_unit_count_payload / tokenizer checked_neg, modeling discipline with explicit 🟡 scaffold + named dissolution trigger and a 🟢 terminal receipt on TargetIntegerInhabitanceBound, and TESTING intent via a single shared diagnostic string helper instead of ad hoc literals.

Verdict

APPROVE — The change is cohesive (Slice B inhabitance rows, UnitCount / Nat narrowing, signed decimal tokenization, instantiation-aware variant payload lowering, E-6 fold gate and tests). No concrete rubric violation surfaced in the reviewed hunks.

Exploratory observations (optional)

  • MIN_TARGET_INTEGER_TYPE_INHABITANCE_ROWS (src/v3/grounding_coercion_fold/src/fold.rs) only enforces a minimum; the gate does not ratchet upward when new spec rows are added—fine if the goal is only “rows must not disappear,” not “exact row count.”
  • type_is_nat and the new instantiation prefix loop both cap depth at 32 iterations (src/v3/compiler/src/int_literal_ranges.rs, src/v3/compiler/src/lower.rs); extremely deep type sugar could theoretically fall through—same class of bounded walk as elsewhere, not introduced as a new pattern in isolation.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: a6e0f4b3 · Trigger: manual
  • Comparison: main @ 4ba0a6d0 ... session/keen-swift-519 @ a6e0f4b3
  • Conversation: View conversation

1. Story of the diff

This PR makes integer target-inhabitance facts authorable in v3 specs rather than keeping them only in the coercion-fold scratch lane. The substrate grows a compact PositiveIntervalWidth.UnitCount spelling in src/v3/std/substrate.dag, the tokenizer learns unary signed decimal literals so rows like lower: -2147483648 parse, and lowering/range code gates Nat-typed literals so negative UnitCount.units fails closed. On top of that, src/v3/std/emit_model.dag introduces TargetIntegerTypeInhabitance plus a bound coproduct, and the Rust/Python/Go specs add concrete rows for the design-doc integer examples.

The coercion-fold crate still does not interpret those row payloads; instead, it adds a checkpoint consumer that counts meta-tagged TargetIntegerTypeInhabitance declarations before scratch examples run. The tests cover the tokenizer behavior, negative UnitCount rejection, structural lowering of the new rows, and the empty-Dag failure path for the row-count gate; the large bootstrap-generated diffs are the regenerated receipt for the new substrate/std declarations.

2. Invariant categories

  1. LAYER MODEL — Finding, BLOCKING.

src/v3/std/substrate.dag:92: // Authoring shorthand for large fixed-width ranges: semantically the same and src/v3/std/substrate.dag:95: // Peano spines remain valid for small cardinalities; Coercion-Fold rows use land src/v3/std/substrate.dag:105: | UnitCount { units: Nat } as a substrate variant. Because PositiveIntervalWidth lives on the Dag, this makes two Dag-level encodings authoritative for the same interval-width fact: the existing OneUnit/AdditionalUnit Peano spine and the new count shorthand. That violates single-authority / illegal-states-unrepresentable discipline at the substrate layer; a downstream consumer now has to accept and reason about two equivalent representations unless there is a canonicalization boundary. The fix should make one representation canonical in the substrate, or keep UnitCount as surface/lowering sugar that normalizes before the width fact lives on Dag. chatgpt-review-5ff4fbf2-b9ea-42…

chatgpt-review-8868526a-a794-40…

  1. INVARIANTS.md + modeling-discipline.md — Finding, BLOCKING.

Same issue as above under P2 Boundary Discipline / single-authority metadata and P5 Progress Is Dissolution: src/v3/std/substrate.dag:93: // unary \AdditionalUnitchain of lengthunitsatopOneUnitoverInt``explicitly definesUnitCountby equivalence to the pre-existing Peano representation, while`src/v3/std/substrate.dag:103: // Peano `OneUnit` / `AdditionalUnit` remains the proof-carrying spine where a`keeps that representation valid. That is a bridge/parallel representation without a canonical authority. The rest of the PR is much more disciplined:`TargetIntegerInhabitanceBound`has a terminal coproduct receipt at`src/v3/std/emit_model.dag:55-77`, and the `Nat`literal gate is fail-closed through`src/v3/compiler/src/int_literal_ranges.rs:186-188`.

  1. CODING.md — Compliant.

The implementation changes are mostly free-function/data-shape additions with explicit dependencies: fold_program_to_target now takes and reads dag: &Dag at src/v3/grounding_coercion_fold/src/fold.rs:85, and the row gate returns a structured EmissionDiagnostic::UnderRefined at src/v3/grounding_coercion_fold/src/fold.rs:97-99 rather than a string sentinel or panic. chatgpt-review-5eb3cb5e-f407-42…

  1. TESTING.md — Compliant.

The PR adds behavior-focused tests at the right seams: tokenizer unit coverage for unary-vs-infix minus at src/v3/compiler/src/lib.rs:3178-3223, a direct lower helper regression at src/v3/compiler/src/lower.rs:7729-7767, a structural row-lowering receipt at src/v3/compiler/tests/integration/m2_substrate_inhabitance_test.rs:1554-1591, and the empty-Dag coercion-fold row gate at src/v3/grounding_coercion_fold/src/lib.rs:67-80. These tests prove the landed behavior, though they do not resolve the substrate representation issue above. chatgpt-review-237b33ad-c843-4b…

  1. LOCKED DESIGN DECISIONS — N/A.

The diff references E-6/DB-10-style constraints in comments, but it does not alter a locked design document; the TargetIntegerTypeInhabitance Slice B/Slice C deferral is made explicit in src/v3/std/emit_model.dag:34-42.

  1. TRACKED vs UNTRACKED DEBT — Finding, BLOCKING.

The TargetIntegerTypeInhabitance bridge is tracked: src/v3/std/emit_model.dag:26-32 names the scaffold, its bounds, and the Slice C dissolution trigger, and src/v3/grounding_coercion_fold/src/fold.rs:95-100 provides the checkpoint consumer. By contrast, UnitCount is described as an “authoring shorthand” at src/v3/std/substrate.dag:92 while src/v3/std/substrate.dag:95 says Peano spines remain valid; there is no scaffold marker, bound, or named dissolution trigger for that duplicate width representation. That makes the substrate shorthand untracked debt rather than a tracked bridge.

3. Verdict

REQUEST_CHANGES

The PR is otherwise well-ratcheted: the E-6 row scaffold is documented and consumed, Nat literal rejection is fail-closed, and tests cover the intended behavior. I would not merge the substrate UnitCount shape as-is because it introduces a second authoritative Dag representation for interval width without canonicalization or a dissolution trigger.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: bda83dd7 · Trigger: schedule
  • Comparison: origin/main @ 8c745cae ... review/pr-1459-bda83dd7 @ bda83dd7
  • Thinking: 59s wall

Verdict: APPROVE

The diff is broad but the substantive changes are documented and bounded: new substrate rows have scaffold/terminal receipts, the UnitCount Nat gate fails closed, and the E-6 same-PR consumer is present via the coercion fold row count. I did not see a concrete violation of the pinned invariants, coding guidance, or testing discipline in the changed lines.

No builds or tests run, per review instructions.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: review @ a6e0f4b3 (composer-2) — Verified on current session/keen-swift-519: the cited rubric alignment (E-6 consumers + emit_model.dag notes, P3 Nat/UnitCount enforcement, modeling scaffold/receipts, shared diagnostic helper) matches what is in-tree.

On the exploratory bullets:

  • MIN_TARGET_INTEGER_TYPE_INHABITANCE_ROWS: Confirmed it is intentionally a floor (fold.rs) so CI catches regressions that delete emit-model inhabitance rows; an exact row-count ratchet would be brittle across additive spec work unless we generate the expected count from the same source as emit_model.dag.
  • Depth-32 walks (int_literal_ranges::type_is_nat, lower.rs instantiation-prefix loop): Confirmed; same bounded-walk pattern as other passes—acceptable until we have a structural "resolve to head" primitive shared across those sites.

No additional code change requested by this review thread.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: Codex review @ bda83dd7 (APPROVE) — Agree with the verdict on current session/keen-swift-519: substrate additions stay bounded (tracked scaffold text on PositiveIntervalWidth.UnitCount, fail-closed Nat/lowering gates, E-6 coercion-fold consumer).

Companion hygiene pushed: substrate.dag comment edits necessarily drift bootstrap source spans, so I landed 3b9b76df1 regenerating bootstrap_generated*.rs and refreshing parse_corpus_manifest.txt; regen_bootstrap --verify + handwritten_parse_snapshot_matches_manifest pass locally.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 3b9b76df · Trigger: manual
  • Comparison: main @ 8c745cae ... session/keen-swift-519 @ 3b9b76df
  • Conversation: View conversation

1. Story of the diff

This PR moves the integer-emission examples toward declared, substrate-visible facts. It adds PositiveIntervalWidth.UnitCount { units: Nat } as compact authoring sugar for large interval widths, teaches Nat literal narrowing to reject negative decimals, regenerates the runtime/bootstrap mirrors, and adds TargetIntegerTypeInhabitance plus per-target rows in Rust/Python/Go specs. The coercion-fold scratch path still does not interpret those rows, but it now gates execution on their presence, so deleting the declared rows fails closed instead of letting the scratch examples run as if the target-spec facts existed.

The other load-bearing piece is tokenization: because the new interval rows need bounds like -2147483648, tokenize.dag, regen_tokenize.rs, and tokenize_generated.rs now merge unary - with following digits except after token kinds where - must remain infix. Tests cover signed-literal tokenization, infix disambiguation, structural row lowering, and negative UnitCount rejection.

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation).

Finding — BLOCKING, API-level substrate enforcement. src/v3/compiler/src/dag.rs:2489: pub fn new_empty_for_testing() -> Self { exposes a no-bootstrap Dag constructor as public API, while the immediately preceding contract says src/v3/compiler/src/dag.rs:2487: /// Production paths must use [Dag::new]. Because Dag is the substrate authority and downstream consumers assume bootstrap declarations exist, this makes the illegal state “publicly constructible empty compiler substrate” enforceable only by convention. #[doc(hidden)] does not stop production crates from calling it; this should be behind an explicit test-only feature/dev-test surface or otherwise not be part of the normal public API.

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

Finding — same blocking issue under “illegal states unrepresentable / API-level enforcement over convention.” The new public constructor at src/v3/compiler/src/dag.rs:2489: pub fn new_empty_for_testing() -> Self { relies on the comment at src/v3/compiler/src/dag.rs:2487 to keep production callers away. That is exactly the shape the discipline tries to avoid: a core invariant is documented instead of made unrepresentable at the API boundary. The substrate modeling added elsewhere is much better: UnitCount is marked as a scaffold with a dissolution trigger at src/v3/std/substrate.dag:106, and the target-integer rows have a same-PR row gate at src/v3/grounding_coercion_fold/src/fold.rs:95.

  1. CODING.md.

Compliant, aside from the public test-helper API already called out above. The implementation changes are mostly data + free-function shaped: integer_range_for_decl delegates to type_is_nat, lower_structural_field_value uses a helper for UnitCount validation, and the tokenizer generator derives the infix-only set from tokenize.dag rather than scattering the list through the scanner.

  1. TESTING.md.

Finding — NON-BLOCKING edge regression gap. The signed literal tests cover i32-min at src/v3/compiler/src/lib.rs:3178: fn minus_digits_lexes_as_single_int_lit_i32_min() {, but the implementation parses the magnitude as signed i64 before negating: src/v3/compiler/src/tokenize_generated.rs:118: let magnitude: i64 = literal.parse().map_err(|_| Diagnostic::TokenizerError { followed by src/v3/compiler/src/tokenize_generated.rs:123: let value = magnitude. That means -9223372036854775808, which is representable as i64::MIN, will be rejected because 9223372036854775808 cannot parse as i64. Since this PR establishes signed decimal literals for interval lower bounds, add the i64-min regression and parse through a wider carrier or special-case that boundary.

  1. LOCKED DESIGN DECISIONS.

N/A — the diff references E-6 / Slice B / Slice C scaffolding, but I did not see a LOCKED design decision being changed or contradicted in the diff.

  1. TRACKED vs UNTRACKED DEBT.

Compliant. The two explicit scaffolds are tracked: TargetIntegerTypeInhabitance documents the scaffold, bound, same-PR row-count consumer, and Slice C dissolution trigger at src/v3/std/emit_model.dag:26 through src/v3/std/emit_model.dag:42; PositiveIntervalWidth.UnitCount documents its canonical Peano witness, bounded role, same-PR negative-literal consumers, and normalizer/query dissolution trigger at src/v3/std/substrate.dag:92 through src/v3/std/substrate.dag:113.

3. Verdict

REQUEST_CHANGES — the integer-inhabitance and UnitCount scaffolds are well documented and have real fail-closed gates, but the public no-bootstrap Dag constructor exposes a substrate-invalid state by convention only. The tokenizer i64-min edge is smaller, but worth tightening while this PR is already changing signed literal semantics.

briansrls and others added 9 commits May 2, 2026 02:51
- substrate: UnitCount carries Nat (Semiring<Magnitude>) instead of Int
- int_literal_ranges: treat Nat as [0, i64::MAX] for decimal literal narrowing
- regen_runtime_mirrors: map Nat → i64 in the scalar Rust mirror (same carrier as Int literals)
- Integration test accepts MagnitudeOutOfRange for -1 (primary path) or ResolveError fallback
- Lower unit test uses bootstrap Dag::new() + larger stack for real UnitCount payload shape

Co-authored-by: Cursor <cursoragent@cursor.com>
…_model receipt

- Document E-6 bounded exception vs Slice C emitter reads in emit_model.dag
- Add Dag::new_empty_for_testing for cross-crate negative witnesses
- Unit-test ScratchIntExamples fails UnderRefined when inhabitance rows absent

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

substrate.dag comment-only scaffold update shifts spans; refresh checked-in
bootstrap snapshots and parse corpus manifest fingerprint.

Co-authored-by: Cursor <cursoragent@cursor.com>
@briansrls
briansrls force-pushed the session/keen-swift-519 branch from 3b9b76d to ec07970 Compare May 2, 2026 03:14
@briansrls

Copy link
Copy Markdown
Contributor Author

Re: GPT-5-pro review @ 3b9b76df (REQUEST_CHANGES) — Addressed both items on current session/keen-swift-519:

  1. Dag::new_empty_for_testing API — Removed from the default public surface by gating it behind Cargo feature empty-substrate-for-tests on v3-compiler (src/v3/compiler/Cargo.toml + #[cfg(feature = "empty-substrate-for-tests")] on the ctor in dag.rs). v3-grounding-coercion-fold enables that feature only via [dev-dependencies] so normal library builds of downstream crates do not see an empty-substrate constructor (74d3e2ee3).

  2. Signed i64::MIN decimal — Unary-minus digit runs now parse the magnitude as u128 and special-case 9223372036854775808 → i64::MIN; regen_tokenize emits the updated scanner + minus_digits_lexes_as_single_int_lit_i64_min regression (f7b254b48).

Also finished the pending rebase onto main (bootstrap/manifest regen at tips) and force-pushed the branch so GitHub merge is unblocked.

— sent from keen-swift-519

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: ec07970f · Trigger: schedule
  • Comparison: origin/main @ 5722bcd0 ... review/pr-1459-ec07970f @ ec07970f
  • Thinking: 62s wall

Verdict: APPROVE — no concrete invariant, modeling-discipline, coding, or testing violations observed in the diff.

I did not run builds or tests, per instruction. The substrate additions are annotated with scaffold/terminal rationale and dissolution triggers where needed, and the new target integer rows have a bounded same-PR consumer rather than landing as untracked metadata.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: Codex review @ ec07970f (APPROVE) — Confirmed local HEAD matches origin/session/keen-swift-519 @ ec07970fb092216621c42064c395c8d411aaf1dc; working tree clean.

Spot-checked the bounded E-6 / Slice B consumer path with cargo test -p v3-grounding-coercion-fold scratch_int_examples_require_emit_model_inhabitance_rows on this tip (passes).

No further code changes triggered by this approval thread.

— sent from keen-swift-519

@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: ec07970f · Trigger: schedule
  • Thinking: 186s wall

Non-blocking — Strengths

  • src/v3/std/substrate.dag UnitCount is a tracked substrate authoring scaffold with a bounded role, Nat payload, named dissolution trigger, and negative-literal proofs.
  • src/v3/std/emit_model.dag The integer inhabitance rows are explicitly scaffolded to Slice C and backed by the same-PR coercion-fold gate required by E-6.
  • src/v3/compiler/tokenize.dag Tokenizer behavior keeps a single .dag authority by declaring the minus disambiguation facts consumed by the generator.

✅ The mixed code and .dag changes address the prior blockers and I do not see new blocking concerns in the added lines.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: Codex strengths @ ec07970f — Spot-checked the cited in-tree facts on ec07970fb092216621c42064c395c8d411aaf1dc:

  • substrate.dag / UnitCount: 🟡 scaffold block names canonical Peano witness, Nat payload, same-PR fail-closed consumers, and an explicit normalizer/query dissolution trigger (not parallel lattices).
  • emit_model.dag / Slice B: TargetIntegerTypeInhabitance block is marked scaffold → Slice C, cites E-6, and documents the coercion-fold pre-scratch row-count gate + diagnostic axis string.
  • tokenize.dag: minus_infix_only_after_token_kinds remains the structural SG-1 carrier feeding regen_tokenize / generated scanner.

Agree there’s nothing new to ratchet from this thread beyond prior landed fixes; no additional commit from this review note.

— sent from keen-swift-519

@briansrls
briansrls merged commit 4135589 into main May 2, 2026
4 checks passed
briansrls added a commit that referenced this pull request May 2, 2026
#1483)

* WIP: calm-tern-200

* chore: apply cargo fmt

* WIP: calm-tern-200

* feat(grounding-coercion-fold): add int examples 5 and 6

* WIP: calm-tern-200

* Revert "WIP: calm-tern-200"

This reverts commit 9804c26.

* feat(grounding-coercion-fold): add example 8 cross-target int

* docs(audit): specify ScratchIntExamples dissolution path

* docs(audit): ground-truth scratch int dissolution spec

* docs(audit): prep ScratchIntExamples Slice C projection

* docs(audit): spec fold lifetime convergence

* docs(audit): map operation-template duplicate facts

* docs(audit): refresh Slice C prep after BoundDeclaration

* docs(audit): refresh Slice C prep after integer rows
briansrls added a commit that referenced this pull request May 2, 2026
…rojection reader (#1459 follow-up) (#1486)

* WIP: calm-tern-200

* chore: apply cargo fmt

* WIP: calm-tern-200

* feat(grounding-coercion-fold): add int examples 5 and 6

* WIP: calm-tern-200

* Revert "WIP: calm-tern-200"

This reverts commit 9804c26.

* feat(grounding-coercion-fold): add example 8 cross-target int

* docs(audit): specify ScratchIntExamples dissolution path

* docs(audit): ground-truth scratch int dissolution spec

* docs(audit): prep ScratchIntExamples Slice C projection

* docs(audit): spec fold lifetime convergence

* docs(audit): map operation-template duplicate facts

* docs(audit): refresh Slice C prep after BoundDeclaration

* docs(audit): refresh Slice C prep after integer rows

* WIP: calm-tern-200
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