Skip to content
Merged

SG-6 #560

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ Cleared (prior PR #521): `DerivedOpEffect { method, path_template, shape }` coll
- `src/v3/std/dimensions.dag` — DB-3 terminal types: `Dimension<Carrier>` (incl. `break_diagnostic`), `Witness<Carrier>`, `DimensionReport<Carrier>`, `OptionalDiagnostic`. `src/v3/std/workflows.dag` names the witness spine (`behavior_spine` = `Dag.nodes` order). Runtime analyze + symbolic-cost migration: `v3_compiler::analyze_symbolic_cost_dimension` (no Dag-side `Dimension<Carrier>` value registry until a compilation pass consumes it).
- `src/v3/lenses/cost.dag` — per-Behavior lowering: Value → Constant(0); Transform → sequential(1, Σ inputs); Branch → sequential(1, condition, max_path(arms)); Loop → iterate(LinearCost(source), body); Bind → passthrough. Forward-fold accumulator pattern mirrors `lenses/complexity.dag`; `MissingCost` short-circuits every composition wrapper so malformed references never silently substitute a zero leaf.
- Rust mirror in `src/v3/compiler/src/dag.rs`: `SymbolicCost` / `SizeVariable` carriers and `sequential` / `iterate` / `max_path` / `normalize` / `dominates` functions — needed because `emit_rust_module`'s `is_bootstrap_file` filter excludes `src/v3/std/` declarations from Rust emission; same pattern `Behavior` / `LoopBound` use.
- `src/v3/compiler/src/lens_cost_symbolic_generated.rs` via `regen_lens_cost_symbolic` binary. Exposed as `v3_compiler::lens_cost_symbolic::{symbolic_cost_of, SymbolicCostEntry, SymbolicCostLookup}`.
- `src/v3/compiler/src/lens_cost_symbolic_generated.rs` via the unified `regen_lens` binary (`cargo run -p v3-compiler --bin regen_lens -- --lens cost_symbolic`; registry in `src/v3/compiler/regen.dag`). Exposed as `v3_compiler::lens_cost_symbolic::{symbolic_cost_of, SymbolicCostEntry, SymbolicCostLookup}`.
- Acceptance fixture: `src/v3/compiler/tests/lane2_stage_2d_symbolic_cost_test.rs` (20 tests). Covers Value/Transform/Branch/Loop lowering; recursive-fn body-cost fact-flow (PR #537 briansrls/codex BLOCKING); zero-drop normalization; `Linear(v) * Linear(v) → Polynomial(v, 2)` nested-fold fingerprint; cross-variable product stays `Product`; composite-dominance child-walk (PR #537 codex P2); `max_path` three-way step preserving incomparable branches (PR #537 briansrls BLOCKING) with an order-independence pin; dominance partial order (Unknown / Linear / Log / Polynomial degree ordering); `max_path` dominant-selection; checked-in generated-module snapshot guard.

**Loop cost for `LoopBound::Descent` clusters uses `LoopNode.source` as the size-variable carrier.** The cluster's `members: NonSingletonList<MemberDescent>` and `intra_cluster_calls` carry the descent witnesses needed for the termination proof (#519), but the loop's own `source` port is the runtime value being descended upon — which is the honest recursion-depth bound for both `Cardinality` and `Descent` bounds. Richer per-member analysis (distinguishing list-descent from bounded-integer descent so `Descent` clusters report `ConstantCost` for bounded-int descent instead of `LinearCost`) is a Stage 2d follow-up, flagged in DB-7 §"Recursion depth bounds".
Expand Down Expand Up @@ -675,6 +675,34 @@ Each sub-PR must reduce handwritten-Rust line count in `src/v3/compiler/src/emit

**Cross-references:** `docs/emit-bridges.md` (bridge inventory), `docs/phase1-lane3-consolidation-build-plan.md` §"Wrapper exception receipt," `src/v3/SELF_HOSTING.md` §3 (Stage 1 emit.dag design note).

### Self-Gen Program — SG-6 (regen/testgen/bootstrap/harness cutover)

**SG program context.** The Self-Gen (SG) program is the "zero hand-authored Rust in `src/v3`" track dispatched out of the parent `gunbc Self Gen` session. Each SG lane owns a narrow surface and the same hard rule: every SG PR reduces the hand-authored Rust census in `src/v3`, ratchet only down. SG-6 specifically owns `build.rs`, `bootstrap.rs`, `pipeline_authority.rs`, `lens_testgen.rs`, and `src/bin/regen_*.rs`.

**Landed (PR #560, SG-6 regen-driver cutover / partial lane).**
- Four per-lens regen bins (`regen_lens_cost.rs`, `regen_lens_cost_symbolic.rs`, `regen_lens_structural_resolution.rs`, `regen_lens_unused_parameters.rs`) deleted.
- SG-4 prep's `regen_infer_helpers.rs` absorbed into the same registry per its own "SG-6 owns folding all of these" header — `infer_helpers` entry added to `regen.dag`, bin deleted, SG-4 freshness test's regen-command message repointed at `regen_lens -- --lens infer_helpers`.
- Single `src/v3/compiler/src/bin/regen_lens.rs` shim reads `src/v3/compiler/regen.dag`'s `LensRegistryEntry` records via `Dag::new()` and regenerates each declared `*_generated.rs`. `name` composes from `std.types::NonEmptyStr`; `lens_file` / `generated_file` from `std.types::FilePath`. `read_registry` fails closed on duplicate `name` / duplicate `generated_file`.
- `sg6_hand_authored_census_test.rs` pins: (a) the `src/bin/` census — both flat-file and directory-form Cargo autobin shapes; (b) the full `(name, lens_file, generated_file)` registry triple across all 6 lenses; (c) uniqueness of `name` / `generated_file`; (d) singleton resolution of `--lens <name>`; (e) **positive-path CLI smoke** via `sg6_regen_lens_cli_smoke_regenerates_named_entry_without_drift`, which spawns the bin through `CARGO_BIN_EXE_regen_lens`, asserts exit success, stdout target-path format, and byte-identity of the regenerated snapshot.

**Deferral: SG-6 registry consumer consolidation (S, follow-up).** `regen.dag` is now the primary authority for `(name, lens_file, generated_file)` triples, but two classes of downstream consumer still mirror those paths instead of resolving through the registry:
1. Every `src/v3/compiler/tests/integration/m2_lens_*_migration_test.rs` has a local `lens_path()` and a `checked_in_generated_module()` backed by `include_str!`.
2. `src/v3/compiler/src/lib.rs` (plus `lens_unused_parameters.rs`) embeds each generated file via `include_str!`.

The triple-ratchet in `sg6_hand_authored_census_test.rs` keeps them from diverging silently, but the duplication still exists.

Dissolution trigger: add a shared `tests/common` helper that walks `regen.dag` and returns a lens source path keyed by registry `name`; re-point every `m2_lens_*_migration_test.rs::lens_path()` at that helper. Once the source-side path is sourced exclusively from the registry, the `lens_file` column of the triple ratchet collapses into a dependency on the same helper (the SG-6 census then pins only `name` + `generated_file`, with `lens_file` re-derived at read time). `include_str!` on the generated output remains compile-time and stays hardcoded — that bridge dissolves only when/if the lib adopts a runtime-loaded generated-file surface.

**Deferral: SG-6 `regen_lens` CLI error-path smoke (XS, follow-up).** PR #560 landed the positive-path CLI smoke (see Landed, item e). The remaining gap is behavioral coverage of the error paths — `parse_args` on unknown flags, bootstrap-diagnostic exit codes, and the unknown-selector stderr path — none of which are exercised by any test today. The cheapest shape is a negative-path smoke that spawns `regen_lens -- --lens <unknown>` through `CARGO_BIN_EXE_regen_lens`, asserts exit != 0, and greps stderr for the registry-name enumeration the driver already prints. Landing this closes the last CLI-surface gap; nothing structural in the driver needs to change for it.

**Deferral: SG-6 remaining lane surfaces (XL aggregate, follow-up PRs).**
- `src/v3/compiler/src/pipeline_authority.rs` (276 lines): the text-scanning body parser for `pipeline.dag`'s `fn compile { ... }` body should dissolve into a declared `data pipeline_compile_order: List<DeclarationRef>` record consumed structurally (same pattern as `LensRegistryEntry`).
- `src/v3/compiler/src/lens_testgen.rs` (792 lines): port to a `.dag` lens under `src/v3/lenses/testgen.dag`, emitted into `src/v3/compiler/src/lens_testgen_generated.rs`; register with `regen.dag` (or its widened successor). This is the largest single chunk and satisfies SG-6's "adding a generated test no longer requires hand-editing Rust drivers" acceptance axis.
- `src/v3/compiler/src/bootstrap.rs` (468 lines): narrow to a host shim; `materialize_pipeline_realizations` becomes `.dag`-authored where possible.
- `src/v3/compiler/build.rs` (181 lines): already narrow but should gain an explicit generated-style header declaring its shim status.

**Rule (records design intent from the meta-review on PR #560).** If a second generated-artifact class appears (e.g., generated-test registry, generated-emitter driver), widen `LensRegistryEntry` / `regen_lens` into a generic `GeneratedArtifactEntry` registry BEFORE landing a new driver. Adding a second per-class registry or driver would re-introduce the per-lens-bin duplication SG-6 just dissolved.

### Cross-cutting — performance

**Deferral: self-compile perf ratchet investigation (M, not on any critical path but compounding).** Self-compile time drifted from ~60s to ~70s in recent cycles (~16% growth). The ratchet keeps getting bumped without a root-cause investigation; each bump normalizes the regression. Scope: (1) profile a single `cargo test -p v3-compiler-tests` run, identify the top hot paths; (2) measure where the 10s came from across recent PRs (bisect across #479, #489, #490 if signal is unclear); (3) either fix the regression or document it as an accepted cost with a new ratchet ceiling. **Yellow-flag threshold: 90s.** If self-compile exceeds that before this deferral is scheduled, it preempts other work. No design doc needed; profiling is a data-gathering exercise.
Expand Down
8 changes: 8 additions & 0 deletions src/v3/compiler/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ fn main() {
// match exactly what the regen drivers write to disk as
// committed Rust.
const REGEN_OUTPUTS: &[&str] = &[
// SG-5 substrate / runtime-mirror projections, generated from
// `src/v3/std/substrate.dag` + `src/v3/compiler/runtime_mirrors.dag`
// and consumed by hand-authored Rust via `include!(...)`.
// Registering them here keeps SG-0's census honest: they carry
// `// AUTO-GENERATED` headers AND are listed as producer-owned
// outputs, so the content header alone never masquerades as
// generated (SG-0's `sg0_generated_partition_is_producer_owned`
// invariant).
"src/v3/compiler/src/dag_branch_generated.rs",
"src/v3/compiler/src/dag_cluster_generated.rs",
"src/v3/compiler/src/dag_cost_generated.rs",
Expand Down
78 changes: 78 additions & 0 deletions src/v3/compiler/regen.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
module v3.compiler.regen

import std.types { FilePath, NonEmptyStr }

// Lens regen registry. Each `data <name>_entry: LensRegistryEntry`
// record below is picked up structurally by the `regen_lens` binary
// (`dag.declarations().filter(meta_tag == LensRegistryEntry)`), which
// then reads `lens_file`, emits the Rust projection via
// `emit_rust_module`, and writes to `generated_file`. Mirrors the
// `PipelineStageBinding` pattern in `src/v3/compiler/pipeline.dag`.
//
// Adding a new generated lens is a pure `.dag` edit: drop the lens
// source in `src/v3/lenses/`, add a `data ..._entry: LensRegistryEntry`
// record here with the three paths filled in, and
// `cargo run -p v3-compiler --bin regen_lens` picks it up. No
// per-lens Rust driver is added or edited.
//
// `name` composes from `std.types::NonEmptyStr` (= `String where
// non_empty`) so the `--lens <name>` selector key cannot encode an
// empty string. `lens_file` and `generated_file` compose from
// `std.types::FilePath` (also `String where non_empty`) per
// MODELING M9 — every field attaches to the existing path/identifier
// vocabulary rather than reintroduce a raw `String`. Refinement
// means empty-string entries cannot be lowered into the registry,
// closing the "illegal states representable" hole that a raw
// `String` would leave open across all three fields.

This comment was marked as resolved.

This comment was marked as resolved.

type LensRegistryEntry {
name: NonEmptyStr
lens_file: FilePath
generated_file: FilePath
}

data lens_cost_entry: LensRegistryEntry = {
name: "cost"
lens_file: "src/v3/lenses/complexity.dag"
generated_file: "src/v3/compiler/src/lens_cost_generated.rs"
}

data lens_cost_symbolic_entry: LensRegistryEntry = {
name: "cost_symbolic"
lens_file: "src/v3/lenses/cost.dag"
generated_file: "src/v3/compiler/src/lens_cost_symbolic_generated.rs"
}

data lens_provenance_entry: LensRegistryEntry = {
name: "provenance"
lens_file: "src/v3/lenses/provenance.dag"
generated_file: "src/v3/compiler/src/lens_provenance_generated.rs"
}

data lens_structural_resolution_entry: LensRegistryEntry = {
name: "structural_resolution"
lens_file: "src/v3/lenses/structural_resolution.dag"
generated_file: "src/v3/compiler/src/lens_structural_resolution_generated.rs"
}

data lens_unused_parameters_entry: LensRegistryEntry = {
name: "unused_parameters"
lens_file: "src/v3/lenses/unused_parameters.dag"
generated_file: "src/v3/compiler/src/lens_unused_parameters_generated.rs"
}

// Absorbed from SG-4 prep (PR #562): `regen_infer_helpers.rs` was
// landed as a per-file regen driver mirroring the deleted
// `regen_lens_*` bins, explicitly flagged in its own header as
// "SG-6 owns folding all of these into a single generic regen
// target". This is that absorption. `infer_helpers.dag` is a lens
// in the broad sense the registry uses (reads substrate Dag, emits
// a Rust module via `emit_rust_module`), so it fits the existing
// `LensRegistryEntry` shape without a rename. A future `.dag`
// artifact that is genuinely non-lens would trigger the widening
// to a `GeneratedArtifactEntry` carrier.
data lens_infer_helpers_entry: LensRegistryEntry = {
name: "infer_helpers"
lens_file: "src/v3/lenses/infer_helpers.dag"
generated_file: "src/v3/compiler/src/infer_helpers_generated.rs"
}
56 changes: 0 additions & 56 deletions src/v3/compiler/src/bin/regen_infer_helpers.rs

This file was deleted.

Loading
Loading