Repository navigation
. - #3
Merged
Merged
.#3
Conversation
briansrls
pushed a commit
that referenced
this pull request
Feb 3, 2026
…fragility TODO_hacks.md: - Hack #5: cardinality Empty tests use concrete empty values (false/0/"") not actual absence. BoundaryMocks has no way to represent "absent." Blocks meaningful B.3 boundary testing for scalar types. - Hack #4 amendment: Value::List filter_map silently drops non-string elements (separate from catch-all issue). - Hack #3 note: runtime check() already handles lists correctly; codegen to_check_code() just needs to mirror it. - Notes: typed matchers as finite logic language; Hack #5 priority. consolidation.md: - §5: type_id == "List" dual encoding across 4+ locations. Canonical model is element type + cardinality. Migration strategy documented. - §6: Codebase fragility — builder functions as strings (rename-unsafe), buck-out/gen in 16 locations (no single constant), CODEGEN_SOURCES hardcoded directory list (staleness gap). - Tasks updated for all new items. https://claude.ai/code/session_01FcPT2VEZdE1W7QdL7SjJgC
briansrls
pushed a commit
that referenced
this pull request
Feb 13, 2026
Addresses feedback from 2026-02-12 PR review across 6 key areas: 1. Fix probe→observer recursion (#1): Intermediate observers are now promoted to probes unconditionally (not gated on Exact matchers). Tests are seeded from baseline DryRun, so concrete values aren't needed. This enables compositional segment testing A→B + B→C. 2. Fix extract_observers() seen/merge bug (#2): NodeExamples with only input-dependent matchers (Exact/Contains) no longer suppress valid chain-safe matchers from live_expected_outputs for the same node. Switched to merge-by-node approach using BTreeMap union. 3. Make gaps fail CI (#3): Coverage gaps now generate a failing test_observability_invariant_no_gaps test instead of just a header comment. Aligns behavior with the stated invariant. 4. Make lowering failures loud (#4): DAG lowering errors now generate a failing test_probe_observer_lowering_failed test instead of silently returning None and skipping all chain tests. 5. Seed policy fail-closed (#5/#8): Inverted seed_policy_for_type to whitelist known-safe primitive types (String, Bool, Int, etc.) and default unknown types to ExplicitSeedRequired. New types and aliases no longer silently fall into placeholder generation. 6. Additional hardening: - Add input_mocks as a probe source (#6) for DAGs seeded via entry input ports - Track weak observers (Any/IsRequest/IsResponse) in coverage reports (#5) so teams can identify low-value assertions - Promoted probes now appear in analysis results - ParamType::from(&str) panics on unknown types instead of silently defaulting to Str (#9) - Int parsing returns ParseError::InvalidInt instead of unwrap_or(0) https://claude.ai/code/session_014cTfu4arnDzFZCELaR26P4
briansrls
added a commit
that referenced
this pull request
Mar 1, 2026
Hack #1 - PipeMethod stringly-typed spread: Add PipeMethod::as_str() and Display impl as single source of truth. Delete three duplicate pipe_method_name() functions from lib.rs, expr.rs, fn_codegen.rs. All callsites now use method.as_str(). Hack #2 - Silent fallbacks in resource_defs.rs: Replace silent return-fallback with .expect() on compile_from_context, data_values.get, and serde_json::from_value. Delete ResourceDslData::fallback() and three FALLBACK_* constants. DSL syntax errors now fail the build immediately. Same treatment applied to gitignore.rs: silent fallback → expect(). Hack #3 - #[path] crate boundary (documented): core/resolve uses #[path] to compile resolve_service.rs from gunbc-dag. Physical move requires extracting gunbc-dag dependencies from the module first (use super::* reverse dependency). Documented as Lane A task. Hack #4 - compile-on-demand spread (documented): 7 files use compile_from_context at runtime. Now all fail-closed (no silent fallbacks). The include_str!/StdLibHost migration is a Lane A Phase A3 task. Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
briansrls
added a commit
that referenced
this pull request
Mar 1, 2026
#2 (pragma_lint silent fallback): Delete default_pragma_lint_policy() and hardcoded fallback. Policy file must exist — panic with actionable message. #3 (param_source_ string heuristic): Add NodeKind::ParamSource variant. Lowerer tags param_source nodes. Executor checks node.kind instead of node_id.starts_with("param_source_"). #4 (Value::Skipped coercion): map/join over Skipped produce empty list/string (temporary — required until C10-full wires all return expressions). Tagged as RT-N workaround. LowerError::InvalidFileOp from previous commit now fail-closed. Note: pragma-lint-policy.txt needs regeneration (gunbc-pragma blocked by C10). Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
briansrls
added a commit
that referenced
this pull request
Mar 1, 2026
PR comment #1 (param field access wiring): Already fixed in previous commit — ExprComputeOp.execute() destructures Value::Map inputs into field-level env entries (line__is_blank etc). Param source nodes feed the full record; the compute node unpacks it. PR comment #2 (passthrough Skipped fallback): Restore partial fail-closed enforcement. Required outputs now return ExecError when other passthroughs arrived but this one is missing (indicating a genuine lowerer wiring gap). When NO passthroughs arrived (entire callable was skipped/guarded), fall back to Skipped. PR comment #3 (derive_file_spec panic): Change return type from Option to Result<FileOperationSpec, LowerError>. Invalid file operations now return a typed LowerError instead of panicking. Caller maps Err to None (skip operation with diagnostic). Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
briansrls
added a commit
that referenced
this pull request
Mar 3, 2026
…tions - Reorder candidates in default_value_for_slot to try REST before Shell/File (mirrors probe_best_response fix from earlier commit) - Mark 3 observations resolved in tasks.md: classify_module doc (#3), from path standardization (#8), probe_best_response ordering (#10) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Mar 13, 2026
…nt Map Addresses review invariant #3 (No fallbacks that fabricate). extract_projection previously fabricated a Value::Map from the output when the expected field wasn't found. Now returns Result<Value, String> so callers handle the error explicitly. PopResult gains an Error variant; run_machine propagates it. Also confirms all 5 review findings against the code: 1. Three parallel stmt evaluators (eval_body, eval_block_as_body, eval_pure_block_stmts) — No parallel implementations violation 2. ANF verifier permits Calls in Blocks — Boundary contract loophole 3. Projection fabrication — Fixed in this commit 4. Duplicate PC (heap vs native stack) — Architecture issue 5. O(N) Rc clone — Performance issue The correct fix for #1, #2, #4 is slice-based continuations (stack bubbling), documented in DESIGN-eval-redesign.md as the next step. Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
briansrls
added a commit
that referenced
this pull request
Mar 23, 2026
…map feedback (#193) * Remove aspirational tests that describe target state not yet implemented Delete 4 failing tests and their 5 now-dead helper functions: - phase6_fold_lambda_uses_reconciled_accumulator_type (R3: fold type refinement) - phase6_anonymous_record_literal_fails_closed_without_named_type (R2) - phase6_anonymous_record_literal_does_not_rank_shape_candidates (R2) - phase6_go_runtime_bridge_methods_keep_method_style_receivers (P1.10) These tests were written to describe Phase 1 target behavior. They will be re-added when the corresponding roadmap items (R2, R3, P1.10) land. Co-authored-by: briansrls <briansrls@gmail.com> * Fix lingering v2.compiler.pipeline references to v2.compiler.compile M1 naming cleanup renamed 06_pipeline.dag to compile.dag (module v2.compiler.compile), but emit_main_rs, emit_main_mod_uses, and emit_compile_match_arm still referenced the old module name. Co-authored-by: briansrls <briansrls@gmail.com> * Align L1 ratchet script categories with ROADMAP.md Break the connective count into '.connective direct access' and 'Conj/Disj references' (previously double-counted). Add classify_type_structure as a separate tracked category. Fix set -euo pipefail + grep exit code interaction via || true. Script and roadmap table now measure the same 7 categories. Ratchet set to 374 (current actual total). Co-authored-by: briansrls <briansrls@gmail.com> * Clarify milestone status labels: tree-green vs prior-branch vs structural Feedback #2: readers could not tell which milestones are verified on the current tree versus achieved on an earlier green branch. Added a status column and a note explaining that prior-branch milestones re-verify once stage0 self-compile is green. Updated P3.1 and M1 accordingly. Co-authored-by: briansrls <briansrls@gmail.com> * Add InferredNode migration boundary subsection (P1.9) Feedback #3: the representation change was conceptually clear but the mechanical migration plan was implicit. Added a table listing every type, API, and layer that changes when P1.9 lands, plus the ordering constraint that it must be an atomic commit. Co-authored-by: briansrls <briansrls@gmail.com> * Split normalization scope: Phase 1 (hardcoded arity) vs Phase 3 (declarations) Feedback #4: the roadmap described normalization as populating structural properties from .dag declarations, but P1.14 defers declaration-driven population to Phase 3. Made the two scopes explicit so readers see that Phase 1 normalization uses the hardcoded arity bridge, and Phase 3 normalization replaces it with generic slot substitution. Co-authored-by: briansrls <briansrls@gmail.com> * Narrow Phase 1 fabrication gate to Rust bootstrap-critical path Feedback #5: 'no emit fabrication sites' in the Phase 1 checklist was overstated — the document defers Go interface{}, Python _unimplemented(), and Go unhandled-expr to Phase 4. Narrowed the Phase 1 state and exit criteria to specify 'no silent/fail-open fabrication on the bootstrap- critical Rust emit path' and explicitly list the Phase 4 deferrals. Co-authored-by: briansrls <briansrls@gmail.com> * Sharpen v1 retirement gate and scrambled-name test definition Feedback #6: - Phase 3 gate now includes a concrete feature-off proof (build + test without v1-bootstrap) rather than just saying 'can be removed.' - Scrambled-name test explicitly defined as comparing inferred structure (typed graph shapes), not emitted artifacts. Emit is excluded because it legitimately reads names for target-language identifiers. Co-authored-by: briansrls <briansrls@gmail.com> * Add LanguageSpec checklist, DAG artifact schema, and TypeVar name-opacity note Feedback #7: Phase 4 contracts were named but not specified. Added: - P4.1 Contract: compact checklist of what belongs in LanguageSpec, grouped by purpose, with completeness test and existing values. - P4.4 Contract: DAG artifact schema (version + modules + diagnostics), versioning mechanism, and note that it reuses the existing Value serialization format. - TypeVar name-opacity explanation in generics design: slot names are structural placeholders consumed by normalization pre-inference, not type identities that inference branches on. Co-authored-by: briansrls <briansrls@gmail.com> * R2: Anonymous record tuple index emits compile_error!() for index >= 4 Stopgap: the hardcoded 0-3 index mapping now emits compile_error!() instead of silently falling back to "0" for higher indices and for field-not-found. The real fix (proper field access for any arity) remains a backlog item. Co-authored-by: briansrls <briansrls@gmail.com> * R4: map_insert reads key type from actual argument instead of hardcoding String The ExprCall bridge path for map_insert on a bare Map receiver now reads the key type from the first argument (remaining |> first) rather than fabricating leaf_node(name: "String"). The leaf_node fallback remains only for the unreachable None branch (count >= 2 guard). Co-authored-by: briansrls <briansrls@gmail.com> * R3: Extract shared refine_collection_result_type for map/flat_map/fold Both ExprCall (bridge path) and ExprMethodCall computed map/flat_map/fold result types through independent inline blocks (~20 lines each). Extracted into a single refine_collection_result_type helper that both paths call. The ExprCall path still owns map_insert/map_merge refinement (those are Call-bridge-specific, not duplicated in MethodCall). Co-authored-by: briansrls <briansrls@gmail.com> * P1.10: Delete dead runtime_bridge_method_name from core The function had zero callers — each emitter owns its own per-target bridge method name rendering (rust_bridge_fn_name, go_bridge_method_name, py_bridge_method_name). These per-target maps are legitimate rendering decisions (Go=PascalCase, Python=with_update for BridgeWith) and remain as-is. The 4-parallel-map problem is now 3 per-target maps with no dead shared intermediary. Co-authored-by: briansrls <briansrls@gmail.com> * P1.19: Delete duplicate mock extraction; import has_mock_prefix from shared emit Deleted starts_with_prefix (duplicated has_mock_prefix from 05_emit.dag). extract_mock_props now uses the imported has_mock_prefix. The Rust-only copy of mock prefix detection is eliminated. Co-authored-by: briansrls <briansrls@gmail.com> * P1.20: Replace testgen fabrication sites with compile_error!() - emit_simple_expr wildcard: todo!() -> compile_error!() - emit_data_value_json wildcard: "null" -> {"__error__": ...} - Default::default() dry-run fallbacks -> compile_error!() All three silent fabrication sites now fail loudly instead of producing valid-looking but wrong test/mock code. Co-authored-by: briansrls <briansrls@gmail.com> * P1.21: Add testgen verification gate + fix emit_typed_data_value_json fabrication New test v2_testgen_emits_valid_rust verifies: - emit_simple_expr uses compile_error!() not todo!() - dry-run fallbacks use compile_error!() not Default::default() - mock extraction uses shared has_mock_prefix, not Rust-only duplicate - shared emit defines TestProjection and extract_test_projections - emit_data_value_json does not silently fabricate "null" Also fixes emit_typed_data_value_json wildcard (second copy of the same fabrication pattern, line 438 in 05_emit.dag). Co-authored-by: briansrls <briansrls@gmail.com> * R1: Delete 30-line RC3 emit safety net for Optional field access field_summary_for_type in inference already correctly produces OptionalUnwrap for .value on Optional bases. The emit-side compensation (checking return_type and base_summary for Optional) was dead code — no test exercises a path where StoredField is produced for .value on an Optional base. All 116 tests pass. Co-authored-by: briansrls <briansrls@gmail.com> * Tighten L1 ratchet 374 -> 372 after R1 emit safety net deletion Co-authored-by: briansrls <briansrls@gmail.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
briansrls
added a commit
that referenced
this pull request
Mar 27, 2026
Review fixes: - Fix variable shadowing: let_value/let_body renamed to val_expr/body_expr in 04_infer.dag ExprLet handling (reviewer comment #1) - Delete 4 unused accessor functions: call_arg_nodes, record_field_nodes, list_elements, string_interp_parts (reviewer comment #2) - Comments #3 (collection kind match) and #4 (variant connective) were already fixed in earlier commits Documentation: - Updated Phase 5 Exit Criteria with full breakdown of remaining 147: - 8 type-name comparisons: 5 access (blocked on inline test std loading), 1 is_kernel_type, 1 string element, 1 Tuple - 139 type constructors: factory functions (bridge debt) - Documented the specific blocker: inline tests don't load std modules, so types resolve as bare kernel seed leaves, not algebra compositions. The algebra infrastructure exists (FreeMonoid.index, OrderedRing, structural method lookup B4) but can't be verified end-to-end in inline tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Mar 28, 2026
Three decidability improvements to the complexity engine: 1. RecursionPattern classification: get_or_compute_summary now detects self-recursion via the placeholder mechanism, classifies as LinearRecursion (1 self-call) or DivideAndConquer (>1 self-calls), and produces bounded CostSum instead of CostUnknown. 2. Body budget removal: the 500-node estimate_expr_size guard produced false CostUnknown violations on infer_expr and cost_of_expr (the two largest and most important functions). The caching/placeholder mechanism already prevents exponential analysis, and the decidability invariant guarantees termination. Ratchet: 2 → 0. 3. ROADMAP updates: iteration primitive audit complete (all bounded), trace_pop_frame resolved as dead code (Gap #3: 4-layer analysis of dead code detection, reachability-gated analysis, persistent data structures). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 of 4 tasks
This was referenced Apr 6, 2026
briansrls
added a commit
that referenced
this pull request
Apr 7, 2026
Address 6 of 7 review comments on PR #336: 1. branching_proof_safe: validate all dimensions of branching fallback proof (not just first). proof_safe_for_branching correctly checks first dimension only (structural first bounds total recursive entries). 2. CallGraph M4 debt: strengthened comment noting string keys are module-qualified from typed IR, blocked on Map<K,V> non-String keys. 3. complexity_diagnostics: now reads violations from ComplexityReport instead of returning hardcoded []. Violations are non-blocking (included in diagnostics for visibility, don't block emission). 4+5. size_bound_param: Forever and ExplicitCount now return None (fail-closed) instead of fabricating synthetic identifiers. bounded_recursive_cost uses "__unresolved" binder + records violation via check_bound_violation. 7. ComplexityReport preserves intern_table (no longer emptied). violations: List<String> field surfaces unresolvable bounds. 6. Added cx_unresolvable_bound_produces_violation and cx_non_recursive_has_no_violation ratchet tests. Self-compile: 51 files emitted, 251 complexity violations (all "unresolvable recursion bound" — analyzer limitations, not program errors). Violations ratchet down as recursion detection improves. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 8, 2026
…List" Addresses review comment #3: classify_field_recursion was comparing field_type_name == "List" directly, violating structural dispatch. Now uses is_container_type() and container_expected_arity() from std/types.dag to dispatch on container kind structurally: - Arity-1 containers (List, Set, NonEmptyList, NonEmptySet): element type at position 0 → ListRecursion (or SetRecursion for sets, but currently all arity-1 map to ListRecursion since Set<self> recursion is the same structural pattern) - Arity-2 containers (Map): value type at position 1 → MapValueRecursion Adding a new container type to std/types.dag now automatically extends recursive field detection with zero changes to the classify function. Review comments #1 (no downstream consumer) and #2 (string proxies) are acknowledged as known constraints — #1 resolves when CX-L2 lands, #2 resolves with M4 (Node.name deletion). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 9, 2026
…MAP (review feedback) #2 ExprLet scope: annotate_descent gave the value initializer access to its own binding (inner_ctx applied to all children). Fixed: value sees outer ctx, only body sees inner_ctx. Ratchet 528→485 (43 false descent evidence entries eliminated). Regression test added. #3 ROADMAP sync: KF-8 marked DEFERRED, heap-space marked TODO, binary search stack corrected to O(1) via TCO (not O(log n)). #4 Rename: time_bound → recurrence_bound. This is a structural recurrence bound (work_exponent=0), not full runtime. Full time = recurrence * per_step_cost. #5 TCO guarantee: documented that stack O(1) for tail-recursive functions assumes the target language guarantees tail-call elimination. 365 tests pass, clippy clean, stage0 fixed point verified (485 diagnostics). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 10, 2026
…ations Three review issues fixed: 1. Remove parallel Provenance type. Option B now threads the existing SubValueRelation through bindings — single authority, no dual representation. The sound foundation already exists; the fix is placement (on TypeBinding), not a new type algebra. 2. Remove source_param: String. Source tracking follows from the scope chain (binding structure), not a new string-keyed field. CallbackContract uses param index (Int), not name (String). Aligns with M4 structural identity direction. 3. Rewrite open question #3 from annotation syntax (@returns) to structural .dag contracts (like AlgebraMethodSemantics). Aligns with INVARIANTS.md: annotations are not facts. Also syncs ROADMAP.md to match (removes source_param: String, clarifies single-authority framing). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8 tasks done
briansrls
added a commit
that referenced
this pull request
Apr 11, 2026
Addresses invariant violation feedback on PR #384: 1. Wire contract: no longer collapses to module-wide string. Passes wire_contract_item (Node?) per-enum for per-type resolution. 2. Response format (text vs json): reverted is_string_like heuristic. Needs WireFormat fact on response declarations in std/. 3. Shell multi-field return: reverted name-based channel fabrication. Needs channel→field mapping in transport definition. Both #2 and #3 now emit compile_error! on the unmodeled cases, making the gap explicit rather than silently fabricating. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 11, 2026
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
…rate shortcuts Five-item pass from the latest ChatGPT review. The headline is #5 — replacing `TypeShape::Primitive(Prim)` with a newtype around `DeclarationId`. That was an M0-inheritance bug that propagated through every subsequent substrate refactor unnoticed because Port machinery was never in scope for the type substrate rework. After this commit the declaration table is the SINGLE source of type identity across the v3 compiler; no name-keyed bridge remains. #1 — Delete `lower_fn_item_pending`; unoverload `ArrowBody::Pending`: - Block-bodied `fn` items in std/ files no longer create Arrow declarations at all. They're skipped in `collect_symbols` (matched on `body: None`), so the declaration table never allocates a slot for them. `ArrowBody::Pending` reverts to its thesis meaning (primitive realization lag only) — no overloaded "user body not lowered yet" interpretation. - Call sites that target block-bodied fns fail-closed at inference because no declaration exists with their name. #2 — Remove `resolve_pending_identifiers` tolerance: - Split the sweep into a tolerant bootstrap variant and a strict user-code variant. Bootstrap tolerates dangling stubs (the canonical `dsl/std/*.dag` files reference types like `Tuple` that live in std/ modules outside the M1(2.6) load set). User-code `lower()` captures a `user_start` snapshot and runs the strict variant over stubs allocated during user lowering — every Identifier stub at `id >= user_start` must resolve or it's a fail-closed `ResolveError`. - `Atom(Identifier { resolved: None })` now means exactly one thing at the user-code boundary: "not yet resolved, pre-sweep". #3 — Move `allocate_type_params` into `collect_symbols`: - Pass 1 of lowering (`collect_symbols_phase`) now allocates top-level declarations AND their TypeParam children in one pass, populating `Declaration.type_params` before any body-lowering runs. Pass 2 (`lower_bodies_phase`) reads `local_scope_from_parent` instead of re-allocating type params. - Bootstrap is restructured into two phases: all files Phase-1 first (so every cross-file template has its type_params slot populated), then all files Phase-2 with a shared symbols map rebuilt from the post-Phase-1 declaration table. `build_template_arguments` no longer hits forward-reference gaps that would have required half-valid placeholder parameters for real declarations. - `fixup_instantiation_template_params` is deleted. The half-valid state it repaired no longer exists for real declarations. Stub templates (bootstrap dangling refs) still use self-reference placeholders, but the stub itself is caught by the sweep and the Instantiation stays dead in bootstrap bodies — no repair pass. #4 — Move `inject_realization_stub` into `#[cfg(test)]`: - Production `bootstrap()` no longer injects realization declarations. The §6.5 smoke test moves into a `#[cfg(test)]` module inside `bootstrap.rs` where it builds its own synthetic realization chain (TestRealization meta-type, anonymous instance, anonymous realization Arrow) without polluting `Dag::new()`. - `Dag::realization_smoke_arrow` / `set_realization_smoke_arrow` deleted — production Dag has no such slot. - `assert_realization_shape` also moved to test-only. #5 — Replace `TypeShape::Primitive(Prim)` with newtype over `DeclarationId`: - `types.rs`: `TypeShape` is now `{ declaration: DeclarationId }`, `Copy + Eq + Hash`. `Prim` enum deleted. - `infer.rs`: `primitive_shape(dag, name)` helper replaces all `TypeShape::Primitive(Prim::X)` constructions. Literal node dispatch looks up Int/Bool/String by name. `declaration_to_type_shape` + `type_shape_to_declaration` deleted — bridging is now a trivial `TypeShape::new(decl_id)`. `walk_to_type_shape` stops at the first named top-level declaration and returns it as the TypeShape; no name-keyed primitive matching. - `lower.rs`: `lower_type_for_port(ty, dag)` looks up primitive names via `declaration_by_name`. `sentinel_type_shape` helper for the mark-unresolved fallback. - `m0_acceptance.rs`: ~25 assertions updated via `primitive_shape(&dag, "X")`. Test status: 1 unit test (bootstrap realization smoke) + 41 M0 + 3 M1 substrate + 4 real-stdlib parse smoke = **49/49 green**. Clippy clean. Name-bridge audit: `grep -rn` in `src/v3/compiler/src/` turns up: - `OPERATOR_FIELD_MAP` (10 entries, documented dissolution trigger to M2 when surface grammar exposes algebra field access directly) — one localized constant, not a pervasive pattern. No other name-keyed bridges remain. Review items now closed: - FAIL-CLOSED: declaration-graph failures go through phantom ports (tolerant in bootstrap, strict in user code) - ILLEGAL STATES: Pending unoverloaded, ExternalRealization typed edge checked at both construction and dispatch, TemplateArgument half-valid state eliminated for real templates - FACTS FLOW FORWARD: declaration identity flows end-to-end from bootstrap source through inference to port types; no name-keyed collapse at the port boundary - COPROD DISSOLUTION: ArrowBody back to terminal-2 + scaffold-1 (Pending only for realization lag); AtomPayload Identifier phase coproduct tracked in ROADMAP as M2 substrate refactor - SINGLE AUTHORITY: `inject_primitive_operators` and `inject_realization_stub` both out of production bootstrap; the declaration table is the only source of type identity - API-LEVEL ENFORCEMENT: `lower()` runs the strict sweep before returning, so no caller discipline is required Still deferred (tracked in ROADMAP): - AtomPayload `Identifier { resolved: Option }` → split variants (M2 substrate refactor) - Flat namespace via `declaration_by_name` (M2 module system) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
Four structural dissolutions from the round 6 ledger audit, plus the corresponding ledger rewrites. After this commit the only 🟡 or 🔴 items left are those that are structurally deferred to later milestones (M2 module system, M3 Pending ratchet). #1 — VariantPayload::Unit dissolved - Unit variant collapsed into `Positional(vec![])`. Unit and zero-arity positional were structurally indistinguishable at the substrate level; the dedicated Unit arm was cosmetic. - VariantPayload is now a 2-variant enum (Positional, Record), reclassified 🟢 terminal. - `parse_variant` emits `Positional(Vec::new())` for bare variants; `lower_type_sum` drops the Unit match arm. #2 — SurfaceItem::Module/Import/DataDecl dissolved - Parser absorbs module / import / data declarations at parse time instead of emitting them as SurfaceItem variants. Their M1(2.6) semantic effect was "no-op at lowering," so carrying them through as SurfaceItem scaffolds was pure scaffolding debt. - New parser helpers `absorb_module_item` / `absorb_import_item` / `absorb_data_item` consume the token stream without emitting a SurfaceItem. - `parse_item` returns `Result<Option<SurfaceItem>, Diagnostic>`: None means "absorbed, no item to emit." - SurfaceItem drops from 9 variants to 6 (Let, Fn, TypeAtom, TypeRecord, TypeSum, TypeAlias), reclassified 🟢 terminal modulo the M2 Type* collapse question. - `lower_item` / `collect_symbols_phase` / `item_span` all drop the Module/Import/DataDecl arms. #3 — SurfaceExpr literal trio dissolved - The three surface-expression literal variants (IntLit/BoolLit/StringLit) collapse into a single `Literal { value: SurfaceLiteral, span }` variant. The new `SurfaceLiteral` enum is parse-local (Int/Bool/String), preserving the G3 guardrail that parse.rs doesn't mention Dag types — `SurfaceLiteral` mirrors `LiteralBits` but lives in the parse layer. - SurfaceExpr drops from 6 variants to 4 (Literal, Var, Call, If), reclassified 🟢 terminal. - `lower_expr` collapses its three literal arms into one match with an inner `match value { SurfaceLiteral::* }`. - `is_recursive`, `descent_provable`, `collect_calls`, `expr_span`, `is_strictly_smaller` all updated. #4 — AtomPayload::Identifier phase coproduct split - `Atom(Identifier { name: String, resolved: Option<DeclarationId> })` replaced by two structural variants: - `Atom(UnresolvedIdentifier(String))` — pre-sweep, name reference pending resolution. - `Atom(ResolvedIdentifier(DeclarationId))` — post-sweep, typed edge to referent declaration. - The pre/post-sweep phase is now a coproduct variant, not a field value. Pattern matches for the two cases live on different arms instead of on `Some`/`None`. - `resolve_pending_identifiers` rewrites the connective on resolution instead of mutating an `Option` in place. The sweep is a structural phase transition, not a field update. - All pattern-match sites updated: `placeholder_connective`, `alloc_identifier_stub`, `build_template_arguments`'s stub check, `run_identifier_sweep`, `unresolved_operator_name`, `resolve_arrow_walk`, `walk_to_type_shape`, `target_display_name`, `assert_target_name` in tests. - AtomPayload is now 4 variants (Literal, UnresolvedIdentifier, ResolvedIdentifier, TypeParam), reclassified 🟢 terminal. The former compression debt the ledger flagged (`Option<_>` hiding a phase coproduct) is eliminated. Test status: 1 unit test (bootstrap realization smoke) + 41 M0 + 3 M1 substrate + 7 real-stdlib parse smoke = 52/52 green. Clippy clean. Net -71 lines (parse.rs net -94, lower.rs net -58 via simpler match arms, dag.rs +28 for the split AtomPayload). Ledger audit after this commit: - 🟢 terminal at M1(2.6): TypeConnective, AtomPayload, CardinalityBound, LiteralBits, ArrowBody (terminal-2 modulo the Pending scaffold), SurfaceItem, SurfaceType, SurfaceExpr, SurfaceLiteral, VariantPayload. - 🟡 scaffold with thesis-defined dissolution trigger: ArrowBody::Pending (§8.11 ratchet to zero by M3). - 🔴 deferred to M2+: flat namespace via declaration_by_name (needs module-scoped declaration table). Tracked in ROADMAP. No remaining compression debt in the parse or declaration layers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
Codex blockers on 636947f: FnExternalBody and Data items compile cleanly in ordinary user code, leaving ArrowBody::Unparsed and ValueBody::Unparsed scaffolds in user-range declarations with no later rejection. `fn foo(x: Int) -> Int { junk }` and `data foo: Int = { junk }` both round-tripped through compile_to_dag without a diagnostic, violating THESIS.md static grounding and modeling-discipline.md principle 1 FAIL-CLOSED. Context: these scaffolds exist so the std/bootstrap files with match/record/pipe/lambda bodies can parse cleanly (their bodies stay as preserved source spans, dissolved when the parser grows to cover the remaining grammar — DOWNSTREAM_REQUIREMENTS.md class-5 gaps #3 and #5). Bootstrap-range declarations still need them. Ordinary user code never should. Fix: add `reject_user_unparsed_scaffolds(dag, strict_from)` sweep in lower.rs that walks declarations at id `>= strict_from` (the user-lowered range after bootstrap) and emits fail-closed diagnostics for: - `TypeConnective::Arrow { body: ArrowBody::Unparsed(span), .. }` - `Declaration.value_body == Some(ValueBody::Unparsed(span))` Called from `lower()` alongside `resolve_pending_identifiers_strict`. Bootstrap-range declarations (id < strict_from) are still tolerated — those scaffolds exist by design until the parser catches up. Regression tests: - `m18_r14_user_block_bodied_fn_is_rejected`: user-code `fn foo(x: Int) -> Int { junk }` returns Err from compile_to_dag. - `m18_r14_user_data_with_opaque_body_is_rejected`: user-code `data foo: Int = { junk }` returns Err. Coverage: 41 M0 + 29 M1 substrate (27 existing + 2 new R14) + 7 real-stdlib smoke + 1 realization smoke = 78 green. Clippy clean (collapsible-match warning fixed in the same pass). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
Ship the substrate + lens foundation needed for PR-B's Rust emitter.
The 100% structural scope from the plan: data items with record-
literal bodies lower to a new ValueBody::Structural variant carrying
the field labels + literal bits; `dsl/extdeps/languages/rust.dag`
loads as a bootstrap fixture declaring Realization and 18 Rust-side
realization facts; a cost lens lands as the third observational lens
in the lens_depth/lens_provenance template.
Phase 1 — Parser (SurfaceExpr::Record + data body integration):
- New SurfaceExpr::Record { fields, span } variant.
- parse_data_item uses a 3-token lookahead (`{` → Ident → `:`) to
distinguish record-literal bodies from unparseable bodies.
Fallback path (brace-skip) preserved for non-record bodies.
- New parse_record_literal helper.
- Walk helpers (is_recursive/descent_provable/collect_calls) gain
Record arms.
Phase 2 — Substrate (ValueBody::Structural variant):
- ValueBody grows Structural { fields: Vec<(String, LiteralBits)> }.
Dissolution ledger updated with 4-pattern receipt; the new variant
is terminal at PR-B scope (class-5 gap #3 tracks the upgrade to
nested records / port-carried field values).
- reject_user_unparsed_scaffolds narrowed to match only Unparsed;
Structural passes through unchecked as the structurally-grounded
form, not a scaffold.
Phase 3 — Lowering (data body inhabitance checking):
- walk_to_conj_decl helper mirrors walk_to_disj_decl from R15;
walks Instantiation/ResolvedIdentifier edges to a Conj.
- lower_record_to_structural enforces: type walks to Conj, no extra
fields, no missing fields, scalar-literal field values only, and
each literal's type matches the declared field type. Fails
fail-closed with diagnostics anchored to the offending span.
- Type check uses declaration_by_name("Int"/"Bool"/"String")
directly — the primitive cache isn't populated until the end of
bootstrap, AFTER lower_bodies_phase runs for rust.dag.
- lower_data_item populates meta_tag from the type annotation even
on the Unparsed fallback path; structural bodies take precedence.
- SurfaceExpr::Record in user-code expression position emits a
fail-closed diagnostic naming class-5 gap #3 as the follow-up;
M1(3) only supports record literals in data-body position.
Phase 4 — rust.dag + bootstrap fixture:
- New dsl/extdeps/languages/rust.dag with the Realization meta-type
(target_name/op_name/carrier/cost — all scalar fields matching
ValueBody::Structural's literal-only constraint) and 18 data
items: 3 primitive mappings, 10 operator realizations, 3
structural templates (Bind/Branch/Main).
- bootstrap.rs appends rust.dag after string_type.dag so String is
resolved by the time Realization.target_name lowers.
- rust_main_wrap's carrier uses `%Q` as a literal-double-quote
placeholder — the v3 tokenizer has no escape sequences, so `\"`
inside a string is impossible.
Phase 5 — lens_cost.rs (third observational lens):
- Pure-reader CostLens following the lens_depth/lens_provenance
template. Transform costs 1 + sum(inputs); Branch costs 1 + cond
+ max(paths) (runtime fires exactly one path); Loop costs 1 +
source + init; Value / parameters are leaves; Bind passes through.
- 6 acceptance tests: literals, single transform, chained transform,
branch max-of-paths, branch asymmetric paths, Bind pass-through.
Tests — m1_substrate_test.rs:
- m1_3_prb_data_item_record_body_lowers_structurally uses local
names (LocalMeta/test_local_item) to avoid colliding with
rust.dag's bootstrap names; find_named would otherwise return
the bootstrap declaration first.
- m1_3_prb_data_item_with_extra_field_is_rejected and
m1_3_prb_data_item_with_wrong_field_type_is_rejected use
LocalMeta/LocalMeta2 for the same reason.
- m1_3_prb_rust_dag_bootstrap_loads_structurally asserts
rust_int_add lowers with Structural value_body pointing at the
Realization Conj via meta_tag, and that all four field slots
carry the expected literal bits.
- child_declarations_are_anonymous inverts its Realization
assertion: pre-PR-B bootstrap loaded only dsl/std/*, so the
realization meta-type was absent. M1(3) PR-B introduces rust.dag
as the first extdeps fixture, so Realization IS present now —
this is the thesis-aligned end-state.
- m17_r9_data_item_has_unparsed_value_body_scaffold test adds a
Structural panic arm for exhaustiveness.
90/90 tests green, clippy clean.
Phases 6 (emit_rust.rs) and 7 (docs close-out) land next.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
…rate shortcuts Five-item pass from the latest ChatGPT review. The headline is #5 — replacing `TypeShape::Primitive(Prim)` with a newtype around `DeclarationId`. That was an M0-inheritance bug that propagated through every subsequent substrate refactor unnoticed because Port machinery was never in scope for the type substrate rework. After this commit the declaration table is the SINGLE source of type identity across the v3 compiler; no name-keyed bridge remains. #1 — Delete `lower_fn_item_pending`; unoverload `ArrowBody::Pending`: - Block-bodied `fn` items in std/ files no longer create Arrow declarations at all. They're skipped in `collect_symbols` (matched on `body: None`), so the declaration table never allocates a slot for them. `ArrowBody::Pending` reverts to its thesis meaning (primitive realization lag only) — no overloaded "user body not lowered yet" interpretation. - Call sites that target block-bodied fns fail-closed at inference because no declaration exists with their name. #2 — Remove `resolve_pending_identifiers` tolerance: - Split the sweep into a tolerant bootstrap variant and a strict user-code variant. Bootstrap tolerates dangling stubs (the canonical `dsl/std/*.dag` files reference types like `Tuple` that live in std/ modules outside the M1(2.6) load set). User-code `lower()` captures a `user_start` snapshot and runs the strict variant over stubs allocated during user lowering — every Identifier stub at `id >= user_start` must resolve or it's a fail-closed `ResolveError`. - `Atom(Identifier { resolved: None })` now means exactly one thing at the user-code boundary: "not yet resolved, pre-sweep". #3 — Move `allocate_type_params` into `collect_symbols`: - Pass 1 of lowering (`collect_symbols_phase`) now allocates top-level declarations AND their TypeParam children in one pass, populating `Declaration.type_params` before any body-lowering runs. Pass 2 (`lower_bodies_phase`) reads `local_scope_from_parent` instead of re-allocating type params. - Bootstrap is restructured into two phases: all files Phase-1 first (so every cross-file template has its type_params slot populated), then all files Phase-2 with a shared symbols map rebuilt from the post-Phase-1 declaration table. `build_template_arguments` no longer hits forward-reference gaps that would have required half-valid placeholder parameters for real declarations. - `fixup_instantiation_template_params` is deleted. The half-valid state it repaired no longer exists for real declarations. Stub templates (bootstrap dangling refs) still use self-reference placeholders, but the stub itself is caught by the sweep and the Instantiation stays dead in bootstrap bodies — no repair pass. #4 — Move `inject_realization_stub` into `#[cfg(test)]`: - Production `bootstrap()` no longer injects realization declarations. The §6.5 smoke test moves into a `#[cfg(test)]` module inside `bootstrap.rs` where it builds its own synthetic realization chain (TestRealization meta-type, anonymous instance, anonymous realization Arrow) without polluting `Dag::new()`. - `Dag::realization_smoke_arrow` / `set_realization_smoke_arrow` deleted — production Dag has no such slot. - `assert_realization_shape` also moved to test-only. #5 — Replace `TypeShape::Primitive(Prim)` with newtype over `DeclarationId`: - `types.rs`: `TypeShape` is now `{ declaration: DeclarationId }`, `Copy + Eq + Hash`. `Prim` enum deleted. - `infer.rs`: `primitive_shape(dag, name)` helper replaces all `TypeShape::Primitive(Prim::X)` constructions. Literal node dispatch looks up Int/Bool/String by name. `declaration_to_type_shape` + `type_shape_to_declaration` deleted — bridging is now a trivial `TypeShape::new(decl_id)`. `walk_to_type_shape` stops at the first named top-level declaration and returns it as the TypeShape; no name-keyed primitive matching. - `lower.rs`: `lower_type_for_port(ty, dag)` looks up primitive names via `declaration_by_name`. `sentinel_type_shape` helper for the mark-unresolved fallback. - `m0_acceptance.rs`: ~25 assertions updated via `primitive_shape(&dag, "X")`. Test status: 1 unit test (bootstrap realization smoke) + 41 M0 + 3 M1 substrate + 4 real-stdlib parse smoke = **49/49 green**. Clippy clean. Name-bridge audit: `grep -rn` in `src/v3/compiler/src/` turns up: - `OPERATOR_FIELD_MAP` (10 entries, documented dissolution trigger to M2 when surface grammar exposes algebra field access directly) — one localized constant, not a pervasive pattern. No other name-keyed bridges remain. Review items now closed: - FAIL-CLOSED: declaration-graph failures go through phantom ports (tolerant in bootstrap, strict in user code) - ILLEGAL STATES: Pending unoverloaded, ExternalRealization typed edge checked at both construction and dispatch, TemplateArgument half-valid state eliminated for real templates - FACTS FLOW FORWARD: declaration identity flows end-to-end from bootstrap source through inference to port types; no name-keyed collapse at the port boundary - COPROD DISSOLUTION: ArrowBody back to terminal-2 + scaffold-1 (Pending only for realization lag); AtomPayload Identifier phase coproduct tracked in ROADMAP as M2 substrate refactor - SINGLE AUTHORITY: `inject_primitive_operators` and `inject_realization_stub` both out of production bootstrap; the declaration table is the only source of type identity - API-LEVEL ENFORCEMENT: `lower()` runs the strict sweep before returning, so no caller discipline is required Still deferred (tracked in ROADMAP): - AtomPayload `Identifier { resolved: Option }` → split variants (M2 substrate refactor) - Flat namespace via `declaration_by_name` (M2 module system) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Apr 15, 2026
Four structural dissolutions from the round 6 ledger audit, plus the corresponding ledger rewrites. After this commit the only 🟡 or 🔴 items left are those that are structurally deferred to later milestones (M2 module system, M3 Pending ratchet). #1 — VariantPayload::Unit dissolved - Unit variant collapsed into `Positional(vec![])`. Unit and zero-arity positional were structurally indistinguishable at the substrate level; the dedicated Unit arm was cosmetic. - VariantPayload is now a 2-variant enum (Positional, Record), reclassified 🟢 terminal. - `parse_variant` emits `Positional(Vec::new())` for bare variants; `lower_type_sum` drops the Unit match arm. #2 — SurfaceItem::Module/Import/DataDecl dissolved - Parser absorbs module / import / data declarations at parse time instead of emitting them as SurfaceItem variants. Their M1(2.6) semantic effect was "no-op at lowering," so carrying them through as SurfaceItem scaffolds was pure scaffolding debt. - New parser helpers `absorb_module_item` / `absorb_import_item` / `absorb_data_item` consume the token stream without emitting a SurfaceItem. - `parse_item` returns `Result<Option<SurfaceItem>, Diagnostic>`: None means "absorbed, no item to emit." - SurfaceItem drops from 9 variants to 6 (Let, Fn, TypeAtom, TypeRecord, TypeSum, TypeAlias), reclassified 🟢 terminal modulo the M2 Type* collapse question. - `lower_item` / `collect_symbols_phase` / `item_span` all drop the Module/Import/DataDecl arms. #3 — SurfaceExpr literal trio dissolved - The three surface-expression literal variants (IntLit/BoolLit/StringLit) collapse into a single `Literal { value: SurfaceLiteral, span }` variant. The new `SurfaceLiteral` enum is parse-local (Int/Bool/String), preserving the G3 guardrail that parse.rs doesn't mention Dag types — `SurfaceLiteral` mirrors `LiteralBits` but lives in the parse layer. - SurfaceExpr drops from 6 variants to 4 (Literal, Var, Call, If), reclassified 🟢 terminal. - `lower_expr` collapses its three literal arms into one match with an inner `match value { SurfaceLiteral::* }`. - `is_recursive`, `descent_provable`, `collect_calls`, `expr_span`, `is_strictly_smaller` all updated. #4 — AtomPayload::Identifier phase coproduct split - `Atom(Identifier { name: String, resolved: Option<DeclarationId> })` replaced by two structural variants: - `Atom(UnresolvedIdentifier(String))` — pre-sweep, name reference pending resolution. - `Atom(ResolvedIdentifier(DeclarationId))` — post-sweep, typed edge to referent declaration. - The pre/post-sweep phase is now a coproduct variant, not a field value. Pattern matches for the two cases live on different arms instead of on `Some`/`None`. - `resolve_pending_identifiers` rewrites the connective on resolution instead of mutating an `Option` in place. The sweep is a structural phase transition, not a field update. - All pattern-match sites updated: `placeholder_connective`, `alloc_identifier_stub`, `build_template_arguments`'s stub check, `run_identifier_sweep`, `unresolved_operator_name`, `resolve_arrow_walk`, `walk_to_type_shape`, `target_display_name`, `assert_target_name` in tests. - AtomPayload is now 4 variants (Literal, UnresolvedIdentifier, ResolvedIdentifier, TypeParam), reclassified 🟢 terminal. The former compression debt the ledger flagged (`Option<_>` hiding a phase coproduct) is eliminated. Test status: 1 unit test (bootstrap realization smoke) + 41 M0 + 3 M1 substrate + 7 real-stdlib parse smoke = 52/52 green. Clippy clean. Net -71 lines (parse.rs net -94, lower.rs net -58 via simpler match arms, dag.rs +28 for the split AtomPayload). Ledger audit after this commit: - 🟢 terminal at M1(2.6): TypeConnective, AtomPayload, CardinalityBound, LiteralBits, ArrowBody (terminal-2 modulo the Pending scaffold), SurfaceItem, SurfaceType, SurfaceExpr, SurfaceLiteral, VariantPayload. - 🟡 scaffold with thesis-defined dissolution trigger: ArrowBody::Pending (§8.11 ratchet to zero by M3). - 🔴 deferred to M2+: flat namespace via declaration_by_name (needs module-scoped declaration table). Tracked in ROADMAP. No remaining compression debt in the parse or declaration layers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Jun 22, 2026
This was referenced Jul 3, 2026
This was referenced Jul 11, 2026
This was referenced Jul 23, 2026
5 tasks done
This was referenced Aug 30, 2026
This was referenced Sep 6, 2026
Merged
This was referenced Sep 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.