diff --git a/dsl/gunbc/roadmap_authority.dag b/dsl/gunbc/roadmap_authority.dag new file mode 100644 index 00000000000..fc3787c4c3a --- /dev/null +++ b/dsl/gunbc/roadmap_authority.dag @@ -0,0 +1,372 @@ +// gunbc/roadmap_authority.dag — the REAL ROADMAP, authored as `.dag` data (the transcription). +// +// SCAFFOLD (DESIGN §7 / plan invert-hand-maintained §6 step 2-3): this is the markdown analogue of +// ci.yml's authored `data ci_workflow` — the single AUTHORITY the ROADMAP.md projection is derived from. +// `project_roadmap_to_markdown(roadmap_authority(), merged)` emits the document; the eventual effectful +// `RoadmapGate` byte-compares the committed ROADMAP.md against that emission (the `ci_yaml_gate` shape). +// dissolution-trigger: the authored `data` is INTERMEDIATE (mirrors ci.yml's authored workflow); the +// TERMINAL is bridging the structure from `ctrl.process_algebra.ProcessGraph` once v2 self-host gives +// in-substrate graph traversal, at which point this authored authority dissolves into a walk over the +// work graph (plan §6 / DESIGN §7 recursion). +// +// FRAME IS THE WIN (warm-lark-306 ruling): model the FRAME faithfully — checkbox lines + their PR +// bindings, sub-group labels, plan-doc pointers, the section/forest structure — and carry PURE PROSE +// (section intros, the `◆ Milestones` spine) as OPAQUE authored Strings emitted VERBATIM (the §5 +// DecodeFidelity fence). Prose is NOT modelled into medium constructs; whole-file byte-exact prose is +// not the goal, the frame-gate is the honest stop. +// +// COMPLETES-IFF, not mention (DESIGN §5, plan §5): a line is a `DerivableLine` (box DERIVED from `prs`, +// `(#ref)` emitted from the binding) ONLY where the bound PR(s) are the line's completion. A line that +// merely MENTIONS a PR as partial evidence, or whose box is hand-set, is an `AuthoredLine` (verbatim) — +// a mention carries no `prs`, so it cannot false-derive a box (the soundness boundary is structural). +// +// STATUS = MERGED REALITY (warm-lark-306, the 6 corrections): rows are authored to what has actually +// merged on main, NOT transcribed from the stale committed ROADMAP. The corrections are flagged inline +// where they apply (C1 measure rows, C2 round-trip law, C3 inversion sub-rows, C4 self-host no-overclaim, +// C5 edge-(b) scoped-not-greenlit). + +module gunbc.roadmap_authority + +import gunbc.roadmap_document { RoadmapDocument, RoadmapSection } +import gunbc.roadmap_model { + RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc, + SectionElement, section_prose, section_group, +} +import gunbc.roadmap_status { RoadmapItem, DerivableLine, AuthoredLine } +import ctrl.process_algebra { ProcessNodeId } +import std.markdown { MarkdownBlock, HeadingBlock, ParagraphBlock, TextInline, H1 } + +// === Node constructors (terse line authoring) =============================== + +// ProcessNodeId is a branded NonEmptyStr; mint one from a kebab id literal. (Identity + deps are the +// ProcessGraph's authority — these ids stand in until the graph traversal bridge lands, plan §6.) +fn nid(s: String) -> ProcessNodeId { s } + +// An AUTHORED checkbox line: hand-set box, `content` emitted verbatim (the §5 fence; bold/refs/links +// live IN the String). The honest default for mentions + hand-checked lines. +fn authored(id: String, done: Bool, content: String) -> RoadmapNode { + RoadmapNode { node: nid(s: id), line: AuthoredLine { done: done, content: content }, carrier: NoCarrier } +} +// An authored line carrying a plan-doc pointer (rendered as ` [plan](path)`; a dangling path is the +// emit error — the construction wall, plan §2). +fn authored_doc(id: String, done: Bool, content: String, path: String) -> RoadmapNode { + RoadmapNode { node: nid(s: id), line: AuthoredLine { done: done, content: content }, carrier: PlanDoc { path: path } } +} +// A DERIVABLE checkbox line: box derived from `prs` (done iff all merged), `(#ref)` emitted from the +// binding, `title` bolded by emit, `description` the fenced refs-free prose tail. The inversion. +fn derivable(id: String, prs: List, title: String, description: String) -> RoadmapNode { + RoadmapNode { node: nid(s: id), line: DerivableLine { prs: prs, title: title, description: description }, carrier: NoCarrier } +} +fn derivable_doc(id: String, prs: List, title: String, description: String, path: String) -> RoadmapNode { + RoadmapNode { node: nid(s: id), line: DerivableLine { prs: prs, title: title, description: description }, carrier: PlanDoc { path: path } } +} + +// === The document preamble =================================================== + +fn preamble() -> List { + [ + HeadingBlock { level: H1, inlines: [TextInline { text: "gunbc — Roadmap" }] }, + ParagraphBlock { inlines: [TextInline { text: "`DESIGN.md` is the authority for *why*. This is the **shape of the work** — a scannable, dependency-ordered checklist. **Checkboxes are authoritative for progress**; detail lives in the linked plan docs. A task's real state is its branch/PR." }] }, + ParagraphBlock { inlines: [TextInline { text: "Legend: `[x]` done · `[ ]` todo · **indentation = depends on the item it sits under**." }] }, + ParagraphBlock { inlines: [TextInline { text: "**Priority order, top = now.** Bands: **stability / correctness** (§0–§4) → **expansion** (§5–§7) → **shelved** (§8)." }] }, + ] +} + +// === §0. Fail-closed lock-down =============================================== + +fn section_0() -> RoadmapSection { + RoadmapSection { + title: "0. Fail-closed lock-down LANE — BLOCKS expansion into products", + elements: [ + section_prose(content: "Cache flakes, un-wired lenses, complexity violations = one problem: modeled, not made *impossible to write*. Fix = correctness by construction, not validation ([DESIGN §5](DESIGN.md))."), + section_prose(content: "This window = a few days of STABILITY — shrink the fail-open surface, don't \"lock\" it. The deepest root (`Value::Null` overload, ~131 sites) stays OPEN until its own runway ([fork plan](docs/plans/model-realization-fork.md) §3)."), + section_prose(content: "**◆ Milestones:** fail-open audited ✓ · numeric tower grounded ✓ (#5428) → **▸ NOW: cache warm==cold oracle · every lens wired-or-deleted · stage0 census under budget** → `Value::Null` split *(deep root)*"), + section_group( + label: "Audits (done)", + nodes: [ + authored(id: "0-audit-wiring", done: true, content: "**lens/gate wiring** — most analytical lenses inert (authored, no discovered gate)"), + authored(id: "0-audit-failopen", done: true, content: "**fail-open code** — cache lossy-digest · under-keyed memos · `unwrap_or_default` infer"), + authored_doc(id: "0-audit-fork", done: true, content: "**model↔realization fork — ROOT CONFIRMED** — one seam (~13 bridges); sub-roots = numeric tower + `Value::Null`", path: "docs/plans/model-realization-fork.md"), + authored_doc(id: "0-audit-remaining", done: false, content: "remaining: coercion/equality straddles · inference fail-open · cache-purity · CI-coverage-completeness", path: "docs/plans/fail-closed-lockdown.md"), + ], + edges: [], + ), + section_group( + label: "In-scope this window", + nodes: [ + // C: numeric-tower grounding completes iff #5428 merged — DERIVABLE (the inversion). + derivable_doc(id: "0-numeric-tower", prs: [5428], title: "numeric-tower grounding", description: "— `Int=GroupCompletion`; `==` straddle guard now dead-in-corpus", path: "docs/plans/model-realization-fork.md"), + authored(id: "0-cache-trustworthy", done: false, content: "**cache trustworthy** — authoritative home is §2 F2/F3/P1; ship the warm==cold oracle as a detective now"), + authored_doc(id: "0-rust-gate", done: false, content: "**rust-gate coverage** (shared §1) — opt-level=3 restores Pop-A to per-PR (#5456); run-all-unless-`#[ignore]`d (#5427)", path: "docs/plans/ci-selection-vs-scheduling.md"), + authored(id: "0-promote-lenses", done: false, content: "**promote-or-delete inert lenses · de-vacuum gates** — EmitHostGate de-vacuumed ✓ (#5477); 4 advisory lenses widened+bounded, whole-corpus deferred to `.dag` structural-reflection *(silent-wren-739)*"), + // realization-vocabulary containment guard completes iff #5445/#5453 — DERIVABLE. + derivable_doc(id: "0-realization-vocab", prs: [5445, 5453], title: "realization-vocabulary containment guard", description: "— target-AST importable only at the realization edge (fail-closed, shrinking-roster)", path: "docs/plans/emission-ingestion-inverse.md"), + authored(id: "0-stage0-census", done: false, content: "**stage0 clone-census inert + seed regressed** to 21540 (~1138 over) — resolve by clone-reduction / substrate-migration, NEVER a cap-bump; #5427 `#[ignore]` is the interim *(fierce-hawk-540 via quick-ant-298)*"), + ], + edges: [], + ), + section_group( + label: "Fenced OUT (after stability)", + nodes: [ + authored(id: "0-value-null", done: false, content: "**split `Value::Null`** (None/Absent/miss/Violates → own carriers) — ~131-site substrate change, the deeper root; own runway"), + authored(id: "0-selfhost-purity", done: false, content: "**self-host purity gate** — a §5 deliverable, not §0 (avoids the §0↔§5 cycle)"), + // cross-tree import activation LANDED iff #5473 — DERIVABLE (the §0↔§5 escalate item now closed). + derivable(id: "0-cross-tree", prs: [5473], title: "cross-tree import activation (§5)", description: "— LANDED; the §0↔§5 escalate item is now closed"), + authored_doc(id: "0-disposition", done: false, content: "**`Disposition` carrier** — a new concept; parked", path: "docs/plans/disposition-carrier.md"), + authored(id: "0-complexity-residue", done: false, content: "complexity-budget whole-codebase (§3) · cache-redundancy completeness (§2 P3) — residue, after construction"), + ], + edges: [], + ), + section_group( + label: "Meta — lock down the reasoning (§7 recursion)", + nodes: [ + // inert-lens hygiene backstop completes iff #5433 — DERIVABLE. + derivable(id: "0-inert-hygiene", prs: [5433], title: "inert-lens hygiene backstop", description: "— every `lens/*.dag` wired or deleted; runs over the corpus"), + authored_doc(id: "0-reachability", done: false, content: "**reachability-completeness lens** — every declared node (code carrier · doc · lens) reachable from a run-root, rostered, or deleted; generalizes #5433 to carriers + docs", path: "docs/plans/inert-layer-lens.md"), + authored_doc(id: "0-gate-hygiene", done: false, content: "**gate-hygiene: a floor-enrolled gate must be green-on-main at merge** — roster-completeness assertion promoted to should-land *(quick-ant-298)*", path: "docs/plans/emission-ingestion-inverse.md"), + // construction-justification rule completes iff #5476 — DERIVABLE. + derivable_doc(id: "0-construction-rule", prs: [5476], title: "construction-justification rule", description: "(authoring-time) — justify why a class can't be construction before adding a lens *(silent-wren-739)*", path: "docs/plans/construction-justification-rule.md"), + authored_doc(id: "0-expressibility", done: false, content: "**expressibility frontier** — partition each modeling discipline into wall / lens-residue / undecidable-review *before* gating", path: "docs/plans/expressibility-frontier.md"), + authored_doc(id: "0-skipped-modeling", done: false, content: "**confront the skipped modeling decisions** — the `🟡` comment backlog", path: "docs/plans/disposition-carrier.md"), + authored(id: "0-axiom-lens", done: false, content: "**axiom + syllogism lens** (DESIGN open thread #1) — every claim chains back to an axiom, no orphan/cycle; stays `[ ]` until it runs executably over this doc"), + ], + edges: [], + ), + ], + } +} + +// === §1. CI under control ==================================================== + +fn section_1() -> RoadmapSection { + RoadmapSection { + title: "1. CI under control (the correctness floor)", + elements: [ + section_prose(content: "A flaky or green-but-broken floor means no gate protects anything — so CI is upstream of every §0 claim. (Compute fabric lives here: the substrate CI runs on; selling it as infra is downstream.)"), + section_prose(content: "**◆ Milestones:** opt-3 per-PR ✓ (#5456) → **▸ NOW — floor never OOMs (memory-aware width)** → floor runs the affected-set → every host knob from one measured `ResourceEnvelope`"), + section_group( + label: "", + nodes: [ + authored(id: "1-privacy", done: true, content: "privacy (compute fabric)"), + // C1 — MEASURE ROWS (bright-stag-194): MISSING from the committed ROADMAP; #5470/#5478 merged. + // The std.measure FLOOR + demand-CEIL families completing the ceil-not-equal-floor money-pair. + derivable_doc(id: "1-measure-foundation", prs: [5470, 5478], title: "Section 1 spawn-width foundation — std.measure expressibility", description: "— the FLOOR family (measure_scale_fraction_floor + measure_fit_count_floor) and the demand-CEIL family completing the ceil-not-equal-floor money-pair landed, dissolving the measure unwrap→raw-arith→rewrap §3 fork. Authority: the expressibility-frontier spec #5467", path: "docs/plans/expressibility-frontier.md"), + authored_doc(id: "1-floor-right-things", done: false, content: "**floor runs the right things** — cadence = two axes: SELECTION (by *what changed*) vs SCHEDULING (by cost); cost never drives selection", path: "docs/plans/ci-selection-vs-scheduling.md"), + // Sub-items under "floor runs the right things" (nested via the edges below — same group so the + // forest resolves; the dependency edge IS the indentation, plan §2). + derivable(id: "1-opt3", prs: [5456], title: "opt-level=3 restores Pop-A to per-PR", description: "— merged"), + authored(id: "1-per-pr-shrink", done: false, content: "per-PR = #5427 run-all sound baseline, shrunk to the affected set (#5427)"), + authored(id: "1-nightly", done: false, content: "nightly = full-corpus selector-backstop + non-hermetic residue (#5447 stood down; ⚠ CI-gen load-bearing) *(quick-ant-298)*"), + authored(id: "1-floor-reliably", done: false, content: "**floor runs reliably & affordably** — memory-aware scheduling (spawn_width is memory-blind → OOM as the corpus grows) + kill sccache false-greens"), + authored_doc(id: "1-builtin-registry", done: false, content: "**tree-scoped builtin registry** (fail-closed) — global seed registry leaks intrinsics into the substrate compile; instance fix #5452, class fix (partition) open *(quick-ant-298)*", path: "docs/plans/compile-clean-forcecheck.md"), + authored(id: "1-repo-model", done: false, content: "repo model (internal repo) on compute fabric"), + authored_doc(id: "1-ci-compute-fabric", done: false, content: "**CI on compute fabric** — derive every host knob from one measured `ResourceEnvelope`; ends the crash-or-idle swing", path: "docs/plans/compute-envelope-model.md"), + authored(id: "1-sellable", done: false, content: "*(downstream)* compute fabric as a sellable infra piece"), + ], + edges: [ + RoadmapEdge { child: nid(s: "1-opt3"), parent: nid(s: "1-floor-right-things") }, + RoadmapEdge { child: nid(s: "1-per-pr-shrink"), parent: nid(s: "1-floor-right-things") }, + RoadmapEdge { child: nid(s: "1-nightly"), parent: nid(s: "1-floor-right-things") }, + ], + ), + ], + } +} + +// === §2. Minimal work — caching by realization =============================== + +fn section_2() -> RoadmapSection { + RoadmapSection { + title: "2. Minimal work — caching by realization (fail-closed)", + elements: [ + section_prose(content: "Gate: uncached non-redundant work is an ERROR, not \"slow\". The cache-key-from-inputs construction is the §0 \"cache trustworthy\" item. → [plan](docs/plans/realization-measurement-loop.md)"), + section_prose(content: "**◆ Milestones:** key-by-construction ✓ (#5425) → **▸ warm==cold purity proven (616/616, #5429)** → resolve-cache ON — ~18% floor cut *(core ask)* → native `content(T)` *(gated B2)*"), + section_group( + label: "", + nodes: [ + derivable(id: "2-f1", prs: [5421], title: "F1 scheduler gives heavy nodes budgeted width", description: ""), + derivable(id: "2-f2f3", prs: [5425], title: "F2/F3 `resolved_graph` key derived from declared `inputs_considered`", description: "— construction, not a lens"), + authored(id: "2-p4", done: false, content: "P4 economic tier (measured cost → `Materialization`) — instrument done (#5431); remaining = the consumer feedback + width-fold"), + authored(id: "2-p5", done: false, content: "P5 native `content(T) = content_hash(subgraph)` — gated on B2"), + authored(id: "2-blockers", done: false, content: "blockers: B1 #5295 generic-instantiation (gates cross-shard `Share`) · B2 cross-tree content-hash (gates P5)"), + ], + edges: [], + ), + ], + } +} + +// === §3. Complexity budget gate ============================================== + +fn section_3() -> RoadmapSection { + RoadmapSection { + title: "3. Complexity budget gate (stability — validation)", + elements: [ + section_prose(content: "Operator decision (2026-06-21): budget-gate validation is the in-window tool; the algorithmic-cost *rewrite construction* is expansion, relocated to §5. Detection is total by construction (cost.dag U2); the gate's reach is a subject-production limit (fn-body reflection), not a detection one."), + section_prose(content: "**◆ Milestones:** budget-gate non-toothless ✓ (#5437) → complexity gates the whole codebase *(gated on §5 fn-body reflection)*"), + section_group( + label: "", + nodes: [ + authored(id: "3-complexity-lens", done: true, content: "complexity lens total over the kernel (cost.dag U2); the gate runs a curated subject roster"), + // cost-lens zero-absorption fix / budgets non-toothless completes iff #5437 — DERIVABLE. + derivable(id: "3-cost-lens", prs: [5437], title: "cost-lens zero-absorption fix — budgets non-toothless", description: ""), + authored(id: "3-synthesis-advisory", done: false, content: "synthesis stays advisory (by Rice, optimality is a ratchet not a wall — DESIGN §5)"), + ], + edges: [], + ), + section_prose(content: "→ rewrite-catalog construction design preserved + relocated to §5 ([plan](docs/plans/algebraic-rewrite-optimization.md))"), + ], + } +} + +// === §4. Testgen as the bug-class oracle ===================================== + +fn section_4() -> RoadmapSection { + RoadmapSection { + title: "4. Testgen as the bug-class oracle (coverage by construction)", + elements: [ + section_prose(content: "Prevent the next class, not the last instance: generate witnesses from declared structure. → [audit + method](docs/plans/testgen-oracle.md)"), + section_prose(content: "**◆ Milestones:** output gated ✓ (#5434) · coproduct-exhaustiveness structural ✓ (#5441) · cross-rep-equality ✓ (#5449) · oracle-method mapped ✓ (#5471) → anemia lens? *(parked, likely advisory)*"), + section_group( + label: "", + nodes: [ + derivable(id: "4-gate-output", prs: [5434], title: "gate the generated output", description: "— floor-discover `generated/` (or regen==committed drift gate)"), + derivable(id: "4-coproduct-exhaust", prs: [5441], title: "CoproductExhaustiveness made structural", description: "— over every declared coproduct, not a hand-roster"), + derivable(id: "4-cross-rep", prs: [5449], title: "cross-representation-equality category", description: "— straddle witness per coproduct × native realization"), + derivable_doc(id: "4-oracle-method", prs: [5471], title: "the oracle method (retro)", description: "— bug-class→mechanism map (generator/lens/wall); testgen owns A + B-routing only, rest are lenses/walls", path: "docs/plans/testgen-oracle.md"), + derivable(id: "4-affected-set", prs: [5430], title: "affected-set = the completeness half", description: "— model the full repo-process universe"), + ], + edges: [], + ), + ], + } +} + +// === §5. Self-host v2 → delete src/v1 ======================================== + +fn section_5() -> RoadmapSection { + RoadmapSection { + title: "5. Self-host v2 → delete `src/v1` (expansion)", + elements: [ + section_prose(content: "Anchor (do not flip-flop): `.dag` = truth; purely self-hosting (v2 emits its own seed, no stage0 hand-edits); emit Rust + TypeScript; then shrink the seed to zero. → [plan](docs/plans/v2-self-hosting.md) · [de-fork audit](docs/plans/dsl-v2-defork-audit.md)"), + // C4 — SELF-HOST PATH-A no-overclaim (bright-stag): #5481 landed the class-3 corpus-coherence + // consolidation + cargo-green seed; §7 regen-fixpoint is DEFERRED (#5514), src/v1 NOT yet deletable. + // The milestone spine reads NOW=cargo-builds-green, KEYSTONE/TERMINAL ahead — NOT "self-host done". + section_prose(content: "**◆ Milestones (critical path):** front-end ✓ · emit-rust well-typed ✓ · de-fork Step 1 ✓ (#5473) · class-3 corpus-coherence + cargo-green seed ✓ (#5481) → **▸ NOW: emitted crate cargo-builds green** → std forks collapsed → real fixed point → **KEYSTONE: `regen --verify` in CI** → seed-honesty → **TERMINAL: `src/v1` deleted** *(§7 regen-fixpoint deferred, #5514; src/v1 NOT yet deletable)*"), + section_group( + label: "", + nodes: [ + authored(id: "5-rewrite", done: false, content: "rewrite suboptimal patterns to the cheaper equivalent (`O(n²)→O(n)` …) — published finite catalog, bulletproof where it fires"), + authored(id: "5-unknown-dissolve", done: false, content: "`Unknown` dissolved over time as an anemic atom (reuse the `Disposition` carrier)"), + authored(id: "5-on-logn", done: false, content: "`O(n^x)→O(n log n)` substitution as per-idiom rules (open: cleaner shared framing?)"), + authored(id: "5-frontend", done: true, content: "front-end (parse / resolve / infer) over the whole tree"), + authored(id: "5-emit-rust", done: true, content: "emit whole tree `--target rust` (well-typed under CI gate)"), + authored(id: "5-defork", done: false, content: "de-fork dsl ↔ v2 (one std authority, no historical forks)"), + // de-fork Step 1 LANDED iff #5473 — DERIVABLE; nested under "de-fork dsl ↔ v2". + derivable(id: "5-defork-step1", prs: [5473], title: "turn on cross-tree import — Step 1 LANDED", description: "— PR-B (collapse forks) next *(nimble-koi-625)*"), + authored(id: "5-cargo-green", done: false, content: "emitted crate `cargo build`s green (Route-A last mile)"), + authored(id: "5-cargo-fixpoint", done: false, content: "real fixed point: `content_hash` stage1==stage2 (dissolve placeholder hashes) → wire `regen_stage0 --verify` lockstep gate into CI (keystone) → collapse `src/v1` → pinned v2-emitted seed (terminal, not a big-bang `rm`)"), + ], + edges: [ + RoadmapEdge { child: nid(s: "5-defork-step1"), parent: nid(s: "5-defork") }, + RoadmapEdge { child: nid(s: "5-cargo-fixpoint"), parent: nid(s: "5-cargo-green") }, + ], + ), + ], + } +} + +// === §6. idea → idea compiler ================================================ + +fn section_6() -> RoadmapSection { + RoadmapSection { + title: "6. idea → idea compiler (expansion — stop anchoring on code)", + elements: [ + section_prose(content: "A program is a canonical `Node` (the *idea*); ingest / emit / eval across many media via one grammar read both directions (§2 N+M). → [plan](docs/plans/idea-machine.md)"), + section_prose(content: "**◆ Milestones:** medium axis ✓ → language axis: English ingest round-trip → cross-media first-class (JSON/react/diagram) → invert hand-maintained: ROADMAP emitted + drift-gated"), + section_group( + label: "", + nodes: [ + authored(id: "6-medium-axis", done: true, content: "**medium axis** — `Medium` + `DecodeFidelity`; `LanguageModel` unified (13 forks dissolved); `compile(Eval) → EvalResult\{value: Medium\}`"), + // C2 — ROUND-TRIP LAW (bright-stag + quick-seal-137): established across TWO structurally- + // different media — markdown (#5525) AND GHA-expr (#5527) — generalize-threshold met. Distinct + // from the EMIT medium checkmark (no overclaim): v2-TargetModel convergence is the deferred + // single-authority destination; per-medium round-trips are v1-seed interim. Completes iff both. + derivable_doc(id: "6-roundtrip-law", prs: [5525, 5527], title: "round-trip law (ingest∘emit = id, DecodeFidelity-bounded)", description: "— established across two structurally-different media: markdown (block-document) and GHA-expr (recursive-expression). v2-TargetModel convergence is the deferred single-authority destination; per-medium round-trips are v1-seed interim. Authority for the law: the round-trip oracle #5513 §5.2", path: "docs/plans/emission-ingestion-inverse.md"), + authored(id: "6-language-axis", done: false, content: "**language axis** — 15+ targets wave-1; English emit proven"), + authored(id: "6-cross-media", done: false, content: "**cross-media targets beyond syntax** — JSON / react / diagram as first-class media (not stringified)"), + authored_doc(id: "6-emit-ingest-extended", done: false, content: "**emission = ingestion⁻¹ extended past syntax** — diagnostic + orchestration intent emit per-target by rows", path: "docs/plans/emission-ingestion-inverse.md"), + authored(id: "6-fidelity-composeup", done: false, content: "`FidelityDisposition` compose-up → medium-level `DecodeFidelity`"), + authored(id: "6-eval-generalize", done: false, content: "eval runtime generalization (wave-1 literal pins → `wave1_model_core` primitives)"), + authored_doc(id: "6-invert", done: false, content: "**invert hand-maintained artifacts** — emit each (ROADMAP flagship · doc indexes) from its `.dag` authority + drift-gate it (the ci.yml pattern)", path: "docs/plans/invert-hand-maintained.md"), + // C3 — INVERSION SUB-ROWS (warm-lark-306): slice-1 (#5491 status derivation) + slice-2 (#5508 + // section-emit) + step-3a (#5520 projection layer) landed; step-3b (RoadmapItem fold) incoming. + // #5513 medium-as-Node is now operative as quick-seal's acceptance test. Completes iff the + // landed slices merged (step-3b not yet in this set — authored to current merged reality). + // Nested under "invert hand-maintained artifacts". + derivable_doc(id: "6-invert-status", prs: [5491, 5508, 5520], title: "PR→checkbox status + section-emit + projection layer", description: "— box derived from an explicit *completes-iff-PR* binding (not mere mention), drift-gated; slice-1 status derivation + slice-2 section-emit + step-3a whole-document projection landed; #5513 medium-as-Node operative as the acceptance test", path: "docs/plans/invert-hand-maintained.md"), + // English sub-items nested under "language axis". + authored(id: "6-english-closure", done: false, content: "English vocabulary closure → fail-closed English ingest (today's catch-all is fail-open; also §0)"), + authored(id: "6-english-roundtrip", done: false, content: "English ingest round-trip (only emit proven today)"), + ], + edges: [ + RoadmapEdge { child: nid(s: "6-english-closure"), parent: nid(s: "6-language-axis") }, + RoadmapEdge { child: nid(s: "6-english-roundtrip"), parent: nid(s: "6-language-axis") }, + RoadmapEdge { child: nid(s: "6-invert-status"), parent: nid(s: "6-invert") }, + ], + ), + ], + } +} + +// === §7. HTML / React rendering ============================================== + +fn section_7() -> RoadmapSection { + RoadmapSection { + title: "7. HTML / React rendering (expansion — the \"website\" sellable piece)", + elements: [ + section_prose(content: "Depends on §6 — react/html is a first-class medium (idea-machine.md §3/§4), downstream of §6's cross-media item."), + section_prose(content: "**◆ Milestones:** react/html page stands up (real page) → in the demo beside the TS emit"), + section_group( + label: "", + nodes: [ + authored(id: "7-react-standup", done: false, content: "react/html rendering stands up (real page, not fixture)"), + authored(id: "7-demo", done: false, content: "add to the demo alongside the TypeScript emit (website + language, dogfoodable)"), + ], + edges: [], + ), + ], + } +} + +// === §8. Session dashboard on .dag (SHELVED) ================================= + +fn section_8() -> RoadmapSection { + RoadmapSection { + title: "8. Session dashboard on `.dag` (SHELVED)", + elements: [ + section_prose(content: "Product/infra tooling — shelved during the stability window (no `.dag`-correctness leverage right now)."), + section_group( + label: "", + nodes: [ + authored(id: "8-pipeline", done: false, content: "idea → PR pipeline *(deferred)*"), + ], + edges: [], + ), + ], + } +} + +// === The whole authority ===================================================== + +// The REAL ROADMAP as the single `.dag` authority — preamble + the eight sections in priority order. +// `project_roadmap_to_markdown(roadmap_authority(), merged)` is the emitted ROADMAP.md. +fn roadmap_authority() -> RoadmapDocument { + RoadmapDocument { + preamble: preamble(), + sections: [ + section_0(), section_1(), section_2(), section_3(), + section_4(), section_5(), section_6(), section_7(), section_8(), + ], + } +} diff --git a/dsl/gunbc/roadmap_document.dag b/dsl/gunbc/roadmap_document.dag index d2e3f6e4a3b..e126a57db5f 100644 --- a/dsl/gunbc/roadmap_document.dag +++ b/dsl/gunbc/roadmap_document.dag @@ -20,16 +20,17 @@ module gunbc.roadmap_document import std.markdown { MarkdownDocument, MarkdownBlock } -import gunbc.roadmap_model { RoadmapNode, RoadmapEdge } +import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, SectionElement } import gunbc.roadmap_emit { project_roadmap_section } -// One ROADMAP section: an editorial title (the `##` heading) over a work-node forest. The forest is the -// EXISTING line model (nodes + their parent/child edges) — reused whole, not re-coined. This is the -// grouping axis the work graph itself does not carry. +// One ROADMAP section: an editorial title (the `##` heading) over an ORDERED element sequence +// (`SectionElement` = authored intro prose | a labelled checkbox sub-group). This subsumes the +// slice-2 single-list section (`nodes`+`edges`) — that flat list is now one `Group` element with an +// empty label, no fork (§3) — and carries the real ROADMAP's frame faithfully: intro prose + the +// `◆ Milestones` spine fenced as `Prose`, the `**…:**` sub-groups as `Group`s, all in document order. type RoadmapSection { title: String - nodes: List - edges: List + elements: List } // The whole ROADMAP as authority. `preamble` is the document-level authored frame above the first @@ -43,7 +44,7 @@ type RoadmapDocument { // The medium blocks for one section: delegate to the per-section projection (step-2), take its blocks. fn section_blocks(s: RoadmapSection, merged: List) -> List { - project_roadmap_section(title: s.title, nodes: s.nodes, edges: s.edges, merged: merged).blocks + project_roadmap_section(title: s.title, elements: s.elements, merged: merged).blocks } // THE whole-document projection (the ci.yml `project_workflow_to_yaml` analogue, markdown medium): diff --git a/dsl/gunbc/roadmap_emit.dag b/dsl/gunbc/roadmap_emit.dag index 870dc19be93..cb7aeb8af38 100644 --- a/dsl/gunbc/roadmap_emit.dag +++ b/dsl/gunbc/roadmap_emit.dag @@ -24,6 +24,7 @@ import std.markdown { import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc, RoadmapParent, TopLevel, UnderParent, roadmap_parent, is_top_level, + SectionElement, Prose, Group, SectionProse, SectionGroup, } // Completion is a Bool here (§2: roadmap_status carries no 2-variant enum restating it). The ONLY // checkbox-state type in the project is std.markdown's GFM glyph `CheckboxState = Checked | Unchecked`; @@ -60,23 +61,34 @@ fn carrier_inlines(carrier: PlanDocCarrier) -> List { } } +// The `(#ref)` annotation, EMITTED FROM THE BINDING (plan TASK-2 emit-refs-from-binding, §3 single +// authority): a DerivableLine's `prs` is the one source of both the derived box AND its rendered refs, +// so the `(#5445/#5453)` annotation is generated here, never hand-kept in prose. An empty binding emits +// no annotation. Mentions keep their refs in AuthoredLine prose (no binding to emit from). +fn refs_text(prs: List) -> String { + fold(prs, init: "", f: (acc, p) => + if acc == "" { concat("#", to_string(p)) } else { concat(acc, concat("/#", to_string(p))) }) +} +fn refs_inlines(prs: List) -> List { + if count(prs) == 0 { [] } else { [TextInline { text: concat(" (", concat(refs_text(prs: prs), ")")) }] } +} +// The fenced prose tail of a DerivableLine (verbatim, refs-free — the refs emit from `prs` above). A +// leading space separates it from the title/refs; empty renders nothing. +fn desc_inlines(description: String) -> List { + if description == "" { [] } else { [TextInline { text: concat(" ", description) }] } +} + // Render the line body by arm (the fold's payoff): a DerivableLine is a BOLD title (emit applies the -// markup) plus an optional fenced description tail; an AuthoredLine is its `content` String emitted -// VERBATIM as a single text inline (the §5 DecodeFidelity fence — any bold/refs/links live in the -// String, so opaque prose round-trips byte-exact). The `(#ref)` annotation from `prs` is deferred to -// the emit-refs increment; this increment renders the structural title + carrier only. +// markup) + its `(#ref)` annotation derived from `prs` + an optional fenced description tail; an +// AuthoredLine is its `content` String emitted VERBATIM as a single text inline (the §5 DecodeFidelity +// fence — any bold/refs/links live in the String, so opaque prose round-trips byte-exact). fn line_inlines(line: RoadmapItem) -> List { match line { - DerivableLine { prs: _, title, description } => - if description == "" { - [StrongInline { inlines: [TextInline { text: title }] }] - } else { - [ - StrongInline { inlines: [TextInline { text: title }] }, - TextInline { text: " " }, - TextInline { text: description }, - ] - } + DerivableLine { prs, title, description } => + concat( + concat([StrongInline { inlines: [TextInline { text: title }] }], refs_inlines(prs: prs)), + desc_inlines(description: description), + ) AuthoredLine { done: _, content } => [TextInline { text: content }] } } @@ -127,14 +139,39 @@ fn task_item_for(rn: RoadmapNode, nodes: List, edges: List, edges: List, merged: List) -> MarkdownDocument { - let tops = nodes |> filter(n => is_top_level(edges: edges, node: n.node)) - MarkdownDocument { - blocks: [ - HeadingBlock { level: H2, inlines: [TextInline { text: title }] }, - TaskListBlock { items: tops |> map(n => task_item_for(rn: n, nodes: nodes, edges: edges, merged: merged, fuel: roadmap_max_depth())) }, +// One checkbox sub-group's blocks: an optional `**{label}:**` heading-paragraph (the UNIFORM frame the +// projection owns — §3 single authority, see roadmap_model.SectionGroup) followed by a task list of its +// top-level nodes (each nesting its children as the indentation = dependency forest). An empty label +// emits the bare list with no preceding label paragraph. +fn group_blocks(g: SectionGroup, merged: List) -> List { + let tops = g.nodes |> filter(n => is_top_level(edges: g.edges, node: n.node)) + let list = TaskListBlock { items: tops |> map(n => task_item_for(rn: n, nodes: g.nodes, edges: g.edges, merged: merged, fuel: roadmap_max_depth())) } + if g.label == "" { + [list] + } else { + [ + ParagraphBlock { inlines: [StrongInline { inlines: [TextInline { text: concat(g.label, ":") }] }] }, + list, ] } } + +// One section element's blocks: authored prose emitted VERBATIM as a single-TextInline paragraph (the +// §5 fence — markup inside the String passes through untouched), or a checkbox sub-group. +fn element_blocks(e: SectionElement, merged: List) -> List { + match e { + Prose { prose } => [ParagraphBlock { inlines: [TextInline { text: prose.content }] }] + Group { group } => group_blocks(g: group, merged: merged) + } +} + +// One ROADMAP section: an H2 heading + its ordered element sequence (intro prose, the `◆ Milestones` +// spine, then one-or-more labelled checkbox sub-groups — interleaved IN DOCUMENT ORDER). A pure fold +// over `elements` preserving order; the whole-document projection maps this over sections. +fn project_roadmap_section(title: String, elements: List, merged: List) -> MarkdownDocument { + MarkdownDocument { + blocks: fold(elements, + init: [HeadingBlock { level: H2, inlines: [TextInline { text: title }] }], + f: fn(acc, e) { concat(acc, element_blocks(e: e, merged: merged)) }) + } +} diff --git a/dsl/gunbc/roadmap_model.dag b/dsl/gunbc/roadmap_model.dag index 401287927dc..269778f6166 100644 --- a/dsl/gunbc/roadmap_model.dag +++ b/dsl/gunbc/roadmap_model.dag @@ -60,6 +60,38 @@ type RoadmapEdge { parent: ProcessNodeId } +// === Section content: the frame-is-the-win element sequence ================== +// +// A real ROADMAP `##` section is NOT a single flat checkbox list (what the slice-2 RoadmapSection +// assumed): it interleaves authored INTRO prose (the section rationale, the `◆ Milestones` spine, a +// `→ [plan](…)` pointer) with one-or-MORE labelled checkbox SUB-GROUPS (`**Audits (done):**`, +// `**In-scope this window:**`). `SectionElement` is that ordered sequence — the frame-is-the-win +// faithful model of the section: model the FRAME (sub-group boxes + their bindings), FENCE the prose. +// +// - `SectionProse` carries authored prose VERBATIM (the §5 DecodeFidelity fence): an opaque String +// emitted as a paragraph, ALL markup (bold/links/refs/code-spans) living inside it. It is NOT modelled +// into medium constructs — fake-modelling prose is the forbidden over-reach (plan §5). +// - `SectionGroup` is one checkbox list: an authored `label` + its node/edge forest (the EXISTING line +// model, reused whole). The label is SEMANTIC TEXT, not literal markup: every real sub-group renders as +// the UNIFORM `**{label}:**` frame, so the bold+colon is a rendering convention the projection owns +// (single authority, §3) — not the `**…:**` repeated in every String. An empty `label` is the bare +// unlabelled `[ ]`-list that opens a section (no label paragraph emitted). +type SectionProse { content: String } +type SectionGroup { label: String, nodes: List, edges: List } + +// The ordered content of a section: authored prose, or a checkbox sub-group. A closed sum so the +// emitter dispatches the two renderings (a verbatim paragraph vs a task list) — no Optional straddle. +type SectionElement + = Prose { prose: SectionProse } + | Group { group: SectionGroup } + +// Typed constructors so an element literal in list position carries its declared SectionElement type +// (a bare inline payload arm is read as a Product, cf. roadmap_parent above). +fn section_prose(content: String) -> SectionElement { Prose { prose: SectionProse { content: content } } } +fn section_group(label: String, nodes: List, edges: List) -> SectionElement { + Group { group: SectionGroup { label: label, nodes: nodes, edges: edges } } +} + // === Dependency projection: the indentation edge ============================ // Where a roadmap line sits in the forest. A closed domain sum, NOT `ProcessNodeId?` — the Optional diff --git a/dsl/test/claim/roadmap_authority_test.dag b/dsl/test/claim/roadmap_authority_test.dag new file mode 100644 index 00000000000..c36bd5858bb --- /dev/null +++ b/dsl/test/claim/roadmap_authority_test.dag @@ -0,0 +1,154 @@ +// dsl/test/claim/roadmap_authority_test.dag — §5 by-execution proof for gunbc.roadmap_authority. +// +// Proves the REAL-ROADMAP transcription projects faithfully: the whole document emits, the FRAME is +// modelled (sections + nested checkbox forests + sub-group labels + plan pointers), the 6 STATUS +// CORRECTIONS are authored to MERGED REALITY, refs emit FROM THE BINDING, and warm-lark-306's two +// invariants hold — (a) authored prose emits BYTE-VERBATIM including its internal markup (the §5 fence), +// (b) a section is an ORDERED element sequence (intro prose → ◆ Milestones → sub-groups, in order). +// Discriminating: a perturbed merged set flips a DERIVED box (the binding has teeth), prose markup is +// asserted byte-exact (a mangle goes red), and a reversed-order adjacency is asserted ABSENT. +// +// The effectful drift gate (read the committed ROADMAP.md, byte-compare) is the SEPARATE RoadmapGate +// variant, deferred until the file is fully emittable (warm-lark-306) — Filesystem.Read is a host effect +// and cannot live in a pure witness. + +module test.claim.roadmap_authority + +import extdeps.languages.markdown { serialize_markdown_source } +import gunbc.roadmap_document { project_roadmap_to_markdown } +import gunbc.roadmap_authority { roadmap_authority } + +// The merged-PR set = current MAIN reality (the PRs that have actually landed; step-3b #5529 is NOT in +// it — the inversion sub-rows are authored to what merged, not to the incoming PR). +fn merged() -> List { + [5428, 5445, 5453, 5433, 5476, 5473, 5456, 5470, 5478, 5421, 5425, 5236, + 5437, 5434, 5441, 5449, 5471, 5430, 5481, 5491, 5508, 5520, 5525, 5527] +} + +fn emit_with(m: List) -> String { + serialize_markdown_source(doc: project_roadmap_to_markdown(doc: roadmap_authority(), merged: m)) +} +fn out() -> String { emit_with(m: merged()) } + +// === Frame: all sections + the preamble project ============================= + +fn witness_frame_all_sections() -> Bool { + let o = out() + string_contains(s: o, pattern: "# gunbc — Roadmap") + && string_contains(s: o, pattern: "## 0. Fail-closed lock-down") + && string_contains(s: o, pattern: "## 1. CI under control") + && string_contains(s: o, pattern: "## 2. Minimal work") + && string_contains(s: o, pattern: "## 3. Complexity budget gate") + && string_contains(s: o, pattern: "## 4. Testgen as the bug-class oracle") + && string_contains(s: o, pattern: "## 5. Self-host v2") + && string_contains(s: o, pattern: "## 6. idea → idea compiler") + && string_contains(s: o, pattern: "## 7. HTML / React rendering") + && string_contains(s: o, pattern: "## 8. Session dashboard on `.dag` (SHELVED)") +} + +// Sub-group labels render as the uniform `**{label}:**` frame (projection-owned, single authority). +fn witness_subgroup_labels() -> Bool { + let o = out() + string_contains(s: o, pattern: "**Audits (done):**") + && string_contains(s: o, pattern: "**In-scope this window:**") + && string_contains(s: o, pattern: "**Fenced OUT (after stability):**") + && string_contains(s: o, pattern: "**Meta — lock down the reasoning (§7 recursion):**") +} + +// === emit-refs-from-binding (plan TASK-2, §3 single authority) =============== + +// A DerivableLine's `(#ref)` is GENERATED from `prs` — single (#N) and multi (#N/#M). The numeric-tower +// row binds [5428]; the realization-vocab row binds [5445, 5453] → `(#5445/#5453)`. +fn witness_emit_refs_from_binding() -> Bool { + let o = out() + string_contains(s: o, pattern: "**numeric-tower grounding** (#5428)") + && string_contains(s: o, pattern: "**realization-vocabulary containment guard** (#5445/#5453)") +} + +// === The 6 status corrections (authored to MERGED REALITY) =================== + +// C1 — MEASURE ROWS (bright-stag-194): missing from the committed ROADMAP; #5470/#5478 merged → DERIVED +// `[x]`, the FLOOR + demand-CEIL families, refs from the binding. +fn witness_c1_measure_rows() -> Bool { + string_contains(s: out(), + pattern: "- [x] **Section 1 spawn-width foundation — std.measure expressibility** (#5470/#5478)") +} +// C2 — ROUND-TRIP LAW (bright-stag + quick-seal-137): its OWN `[x]` row bound to #5525/#5527 (two media), +// DISTINCT from the medium-axis checkmark — no overclaim that the medium checkmark = round-trip-complete. +fn witness_c2_roundtrip_law() -> Bool { + let o = out() + string_contains(s: o, pattern: "- [x] **round-trip law (ingest∘emit = id, DecodeFidelity-bounded)** (#5525/#5527)") + && string_contains(s: o, pattern: "- [x] **medium axis** —") + && string_contains(s: o, pattern: "per-medium round-trips are v1-seed interim") +} +// C3 — INVERSION SUB-ROWS (warm-lark-306): slice-1/slice-2/step-3a landed → DERIVED `[x]` bound to +// #5491/#5508/#5520 (step-3b #5529 intentionally NOT in the set — authored to merged reality). +fn witness_c3_inversion_subrows() -> Bool { + string_contains(s: out(), + pattern: "- [x] **PR→checkbox status + section-emit + projection layer** (#5491/#5508/#5520)") +} +// C4 — SELF-HOST PATH-A no-overclaim (bright-stag): #5481 landed (in the spine) but §7 regen-fixpoint is +// DEFERRED and src/v1 is NOT yet deletable — the milestone must NOT read as "self-host done". +fn witness_c4_selfhost_no_overclaim() -> Bool { + let o = out() + string_contains(s: o, pattern: "class-3 corpus-coherence + cargo-green seed ✓ (#5481)") + && string_contains(s: o, pattern: "§7 regen-fixpoint deferred, #5514; src/v1 NOT yet deletable") + && string_contains(s: o, pattern: "**TERMINAL: `src/v1` deleted**") +} +// C5 — edge-(b) / .dag→rust coverage wall (bright-stag): SCOPED / pending operator greenlight, NOT +// greenlit — no status change, the rust-gate coverage row stays `[ ]`. +fn witness_c5_rust_gate_not_greenlit() -> Bool { + string_contains(s: out(), pattern: "- [ ] **rust-gate coverage** (shared §1)") +} + +// === Invariant (a): authored prose emits BYTE-VERBATIM including its markup (the §5 fence) === + +// The §2 ◆ Milestones spine is authored prose; its internal markup (bold, the ✓ glyph, and a `(#5425)` +// that is PROSE — not a binding) passes through UNTOUCHED. A mangled fence (stripped bold/glyph, or a +// (#5425) "helpfully" re-emitted) makes this go red. +fn witness_a_prose_verbatim() -> Bool { + string_contains(s: out(), + pattern: "**◆ Milestones:** key-by-construction ✓ (#5425) → **▸ warm==cold purity proven (616/616, #5429)**") +} + +// === Invariant (b): the section is an ORDERED element sequence =============== + +// §0 in document order: intro prose → window note → ◆ Milestones spine → Audits sub-group. The exact +// `tail\n\nhead` adjacencies hold ONLY if order is preserved (a reorder breaks every one of them). +fn witness_b_ordered_interleaving() -> Bool { + let o = out() + string_contains(s: o, pattern: "([DESIGN §5](DESIGN.md)).\n\nThis window = a few days of STABILITY") + && string_contains(s: o, pattern: "[fork plan](docs/plans/model-realization-fork.md) §3).\n\n**◆ Milestones:** fail-open audited") + && string_contains(s: o, pattern: "`Value::Null` split *(deep root)*\n\n**Audits (done):**") +} +// Discriminating negative: if the element order were lost (sub-group hoisted before the prose), the +// Audits label would sit immediately before the window prose — assert that adjacency is ABSENT. +fn witness_b_order_discriminator() -> Bool { + !string_contains(s: out(), pattern: "**Audits (done):**\n\nThis window = a few days of STABILITY") +} + +// === Discriminating: the DERIVED box is a function of the merged set ========= + +// numeric-tower (#5428) and measure (#5470/#5478) are DERIVED `[x]` when merged; drop those PRs and the +// SAME lines derive `[ ]` (the box is computed from the binding, not baked) — the inversion has teeth. +fn witness_derivation_has_teeth() -> Bool { + let dropped = emit_with(m: [5445, 5453, 5433, 5476, 5473, 5456, 5421, 5425]) + string_contains(s: dropped, pattern: "- [ ] **numeric-tower grounding** (#5428)") + && string_contains(s: dropped, pattern: "- [ ] **Section 1 spawn-width foundation — std.measure expressibility** (#5470/#5478)") + && !string_contains(s: dropped, pattern: "- [x] **numeric-tower grounding** (#5428)") +} + +test fn roadmap_authority_witnesses() -> Bool { + witness_frame_all_sections() + && witness_subgroup_labels() + && witness_emit_refs_from_binding() + && witness_c1_measure_rows() + && witness_c2_roundtrip_law() + && witness_c3_inversion_subrows() + && witness_c4_selfhost_no_overclaim() + && witness_c5_rust_gate_not_greenlit() + && witness_a_prose_verbatim() + && witness_b_ordered_interleaving() + && witness_b_order_discriminator() + && witness_derivation_has_teeth() +} diff --git a/dsl/test/claim/roadmap_document_test.dag b/dsl/test/claim/roadmap_document_test.dag index e6cc9eca562..4a68873a1f4 100644 --- a/dsl/test/claim/roadmap_document_test.dag +++ b/dsl/test/claim/roadmap_document_test.dag @@ -11,7 +11,7 @@ module test.claim.roadmap_document import extdeps.languages.markdown { serialize_markdown_source } import gunbc.roadmap_document { RoadmapDocument, RoadmapSection, project_roadmap_to_markdown } -import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc } +import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc, SectionElement, section_group } import gunbc.roadmap_status { RoadmapItem, DerivableLine, AuthoredLine } import std.markdown { MarkdownBlock, HeadingBlock, ParagraphBlock, TextInline, H1 } import ctrl.process_algebra { ProcessNodeId } @@ -45,13 +45,19 @@ fn child_node() -> RoadmapNode { } fn lockdown_section() -> RoadmapSection { - RoadmapSection { title: "0. Fail-closed lock-down", nodes: [audit_node()], edges: [] } + RoadmapSection { + title: "0. Fail-closed lock-down", + elements: [section_group(label: "", nodes: [audit_node()], edges: [])], + } } fn expansion_section() -> RoadmapSection { RoadmapSection { title: "6. Expansion", - nodes: [status_node(), child_node()], - edges: [RoadmapEdge { child: child_id, parent: status_id }], + elements: [section_group( + label: "", + nodes: [status_node(), child_node()], + edges: [RoadmapEdge { child: child_id, parent: status_id }], + )], } } @@ -114,7 +120,7 @@ fn expected_roadmap() -> String { "\n", "## 6. Expansion\n", "\n", - "- [x] **PR-checkbox status**\n", + "- [x] **PR-checkbox status** (#5491)\n", " - [ ] **host-fed status bridge**\n" ], "") } diff --git a/dsl/test/claim/roadmap_emit_test.dag b/dsl/test/claim/roadmap_emit_test.dag index 38d2ff4942d..8f58278dd53 100644 --- a/dsl/test/claim/roadmap_emit_test.dag +++ b/dsl/test/claim/roadmap_emit_test.dag @@ -10,7 +10,7 @@ module test.claim.roadmap_emit import extdeps.languages.markdown { serialize_markdown_source } import gunbc.roadmap_emit { project_roadmap_section } -import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc } +import gunbc.roadmap_model { RoadmapNode, RoadmapEdge, PlanDocCarrier, NoCarrier, PlanDoc, SectionElement, section_group } import gunbc.roadmap_status { RoadmapItem, DerivableLine, AuthoredLine } import ctrl.process_algebra { ProcessNodeId } @@ -38,10 +38,12 @@ fn child_node() -> RoadmapNode { fn nodes() -> List { [parent_node(), child_node()] } fn edges() -> List { [RoadmapEdge { child: child_id, parent: parent_id }] } +// One unlabelled sub-group (the slice-2 single-list section is now a Group with an empty label). +fn elements() -> List { [section_group(label: "", nodes: nodes(), edges: edges())] } fn emit(merged: List) -> String { serialize_markdown_source(doc: project_roadmap_section( - title: "6. Expansion", nodes: nodes(), edges: edges(), merged: merged, + title: "6. Expansion", elements: elements(), merged: merged, )) }