# design-md-consolidation iter 2 — sweep 2: REVERTED + migration plans + correction history + future speculations > **Parent spec:** `docs/specs/0004-design-md-consolidation.md` > §"Sweep 2 — REVERTED + migration plans out". > > **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` > to run this plan. Steps use `- [ ]` checkboxes for tracking. **Goal:** Remove the REVERTED-decision body, the completed migration plan, the correction-history paragraph, and the non-binding future speculations from `docs/DESIGN.md`; condense binding-prohibition forward-looking statements into assertive form. **Architecture:** Documentation-only refactor. Each task targets one self-contained section or one decision class. "RED" is a pre-task grep / line-range read showing the target sites; "GREEN" is the post-task grep returning empty (or only inside the binding- prohibition contexts the spec wants preserved). No code changes. **Tech Stack:** Edit (`docs/DESIGN.md`), grep, `cargo test --workspace`, `bench/check.py`, `bench/compile_check.py`. **Lesson from Sweep 1.** The acceptance grep must cover the spec's full intent, not one specific anchor form. This plan's composite acceptance grep is therefore the union of all variant phrasings the spec section names — encoded once at Step 1 of Task 6 — so the sweep cannot ship while any variant survives. **Files this plan modifies:** - Modify: `docs/DESIGN.md` — five edit tasks (sections / paragraphs). - Append: `docs/JOURNAL.md` — one iter entry. - Modify: `docs/roadmap.md` — flip Sweep 2 entry from `[ ]` → `[x]`. --- ## Task 1: Delete Decision 7 (`Term::If` removal — REVERTED) **Files:** Modify: `docs/DESIGN.md` (delete lines 593-624 inclusive at iter start; line numbers may shift slightly). The entire Decision 7 block is REVERTED-and-restored audit-trail text. `Term::If` exists in the language; the witness is the Term schema in the Data-model section. The decision body has no state-only content to preserve — every claim it makes describes a removal that was rolled back. - [ ] **Step 1: Pre-test — verify decision boundaries** ```bash grep -n '^## Decision 7\|^## Decision 8' docs/DESIGN.md ``` Expected: two adjacent matches; the first is line ~593 (`## Decision 7: redundancy removal — \`Term::If\` is not a primitive`), the second is line ~625 (`## Decision 8: explicit, verified tail calls`). The block to delete spans the first up to (but not including) the second. - [ ] **Step 2: Read the block to confirm scope** ```bash sed -n '593,624p' docs/DESIGN.md ``` Expected output: 32 lines starting with `## Decision 7: …` and ending with `… the new hashes become the new identity.` followed by a blank line. The next line is `## Decision 8: …`. - [ ] **Step 3: Delete the Decision 7 block** Use the Edit tool. The `old_string` is the full 32-line block from line 593 (`## Decision 7: redundancy removal — \`Term::If\` is not a primitive`) through line 623 (`max3\`) is intentional; the new hashes become the new identity.`) plus the trailing blank line at 624 — i.e. delete the entire block AND the blank line that follows it, so line 625's `## Decision 8: …` becomes contiguous with whatever previously preceded line 593 plus a single blank-line separator. The exact `old_string` to pass to Edit (32 lines): ``` ## Decision 7: redundancy removal — `Term::If` is not a primitive This decision was made on shaky grounds — applying CLAUDE.md's "no redundancies" rule to a case that turned out to be primitive control flow, not redundancy. The post-removal match-on-Bool form (3× the tokens, asymmetric `pat-wild` for the false case) was worse for token economy and worse for the natural shape of the language. `Term::If` is restored. The text below is preserved for the audit trail. `Term::If { cond, then, else_ }` is semantically a subset of `Term::Match` on `Bool`. Per CLAUDE.md the language must contain no redundancies; two AST nodes for the same operation produces an authoring decision with no semantic content and an extra codegen path. `Term::If` was removed. Migration shape on the JSON side: {"t":"if","cond":C,"then":A,"else":B} → {"t":"match","scrutinee":C, "arms":[ {"pat":{"p":"lit","lit":{"kind":"bool","value":true}},"body":A}, {"pat":{"p":"wild"},"body":B}]} The wildcard arm satisfies the typechecker's `primitive-needs-wildcard` rule. A future iter may upgrade the exhaustiveness check to recognise the `true`+`false` arm pair as covering Bool without a wildcard; until then, wildcard is the canonical migration target. No schema version bump (no third-party consumes `ailang/v0`). Hash invalidation for the three migrated fixtures (`sum`, `sort`, `max3`) is intentional; the new hashes become the new identity. ``` `new_string` is the empty string (the entire block is deleted, including the trailing blank line; section boundary collapses to whatever blank-line separator already existed before line 593). - [ ] **Step 4: Post-test** ```bash grep -n '^## Decision' docs/DESIGN.md | head -15 ``` Expected: Decision 7 absent; Decision 6 followed directly by Decision 8. Decisions 1, 2, 3, 4, 5, 6, 8, 9, 10, 11 — note that Decision numbering is NOT renumbered (Decision 8 stays Decision 8; the gap is intentional and matches existing JOURNAL references). ```bash grep -n 'REVERTED\|preserved for the audit trail\|primitive-needs-wildcard.*future iter' docs/DESIGN.md ``` Expected: empty. - [ ] **Step 5: Commit** ```bash git add docs/DESIGN.md git commit -m "design-md-consolidation 2.1: delete Decision 7 (Term::If REVERTED block)" ``` --- ## Task 2: Condense Decision 9 narrative-of-changes opener **Files:** Modify: `docs/DESIGN.md` (lines ~682-689 at iter start; shifted by -32 after Task 1; re-locate at Step 1). Decision 9's current opener is a three-frame narrative ("Originally framed as X; then re-framed as Y; the revision flips Z"). This recounts how the framing changed across three points in time. State- only form: one sentence stating what Decision 9 currently is, with the "Boehm as parity oracle" rationale preserved (it is the substantive content; only the narrative-of-changes wrapping is cut). - [ ] **Step 1: Pre-test — locate the paragraph** ```bash grep -n '^## Decision 9' docs/DESIGN.md ``` Note the line number; then: ```bash sed -n ',p' docs/DESIGN.md ``` Expected: the opener reads "Originally framed as 'transitional Boehm until the RC pipeline lands and the default flips'; then re-framed as a symmetric dual-allocator policy with Boehm as the CLI default. The revision flips the asymmetry to match Decision 10:" followed by the three RC/Boehm/bump bullets. - [ ] **Step 2: Replace the narrative-of-changes opener** Edit: - old (the paragraph between the `## Decision 9` heading and the three bullet points): ``` Originally framed as "transitional Boehm until the RC pipeline lands and the default flips"; then re-framed as a symmetric dual-allocator policy with Boehm as the CLI default. The revision flips the asymmetry to match Decision 10: ``` - new: ``` AILang ships two allocator backends with an asymmetric role: ``` The substantive content (RC canonical, Boehm parity oracle, bump leak-only) follows immediately as the existing three bullets, so the new opener leads directly into them. - [ ] **Step 3: Verify the surrounding sentences scan** After the edit, the paragraph reads: ``` ## Decision 9: dual allocator — RC canonical, Boehm parity oracle AILang ships two allocator backends with an asymmetric role: - **RC is canonical.** `--alloc=rc` is the CLI default … - **Boehm stays as a parity oracle.** … - **`--alloc=bump`** is unchanged: a leak-only bench instrument, … ``` - [ ] **Step 4: Also condense the second narrative paragraph** The current text after the bullets contains a second narrative paragraph (around line 717 originally; shifted): "Originally, every ADT box, lambda env, and closure pair was allocated with bare `malloc` and never freed…" — this is also pre-Decision-9 history. Read its full span: ```bash grep -n 'Originally, every ADT box' docs/DESIGN.md ``` If present, the paragraph and the surrounding two paragraphs (the "`--alloc=bump` mode introduced for the bench is a measurement tool, not a production target." sentence and the ADT-box history) together describe how the GC arrived in AILang. The state-only content is in the bullets above; this is purely history. Edit: delete from the line "The `--alloc=bump` mode introduced for the bench is a measurement tool, not a production target." through the line "both make a collector necessary." (the end of the "Originally, every ADT box…" paragraph). The next paragraph beginning **Choice: Boehm-Demers-Weiser conservative GC.** is the state-only content and stays. Verify the precise text by reading the region first: ```bash sed -n ',p' docs/DESIGN.md ``` The block to delete is approximately: ``` The `--alloc=bump` mode introduced for the bench is a measurement tool, not a production target. Originally, every ADT box, lambda env, and closure pair was allocated with bare `malloc` and never freed. That worked for the 17 test fixtures (all small, all short-lived) but is incompatible with any real workload — a stdlib `fold` over a million-element list would leak a million boxes. The "Goal" section's "no GC for the MVP" framing predates the parameterised-ADT pipeline and the explicit-recursion expectation; both make a collector necessary. ``` The next non-blank line (`**Choice: Boehm-Demers-Weiser conservative GC.**`) is the start of the state-only Boehm description and stays. - [ ] **Step 5: Post-test** ```bash grep -nE '[Oo]riginally framed|Originally, every ADT box|--alloc=bump.*measurement tool' docs/DESIGN.md ``` Expected: empty. ```bash grep -n '## Decision 9' docs/DESIGN.md ``` Read 30 lines forward; verify the section reads as state ("AILang ships two allocator backends … RC is canonical … Boehm parity oracle … bump leak-only … Choice: Boehm-Demers-Weiser conservative GC …"). - [ ] **Step 6: Commit** ```bash git add docs/DESIGN.md git commit -m "design-md-consolidation 2.2: condense Decision 9 narrative-of-changes opener + drop pre-Decision-9 history paragraphs" ``` --- ## Task 3: Delete Decision 10 §"Migration plan" **Files:** Modify: `docs/DESIGN.md` (lines ~1384-1414 at iter start in the original file; shifted further by Tasks 1-2; re-locate at Step 1). Decision 10's §"Migration plan" is a 7-point list of past iters (annotation feature → RC runtime → uniqueness inference → reuse hints → drop-iterative → validation bench → advisory lint). Migration is complete; the end state is what the rest of Decision 10 already describes. The list is pure history. - [ ] **Step 1: Pre-test — locate the section** ```bash grep -n '^### Migration plan\|^### Adjacent extensions' docs/DESIGN.md ``` Expected: two matches. The first (Migration plan) is the section to delete; the second (Adjacent extensions) is what comes after. - [ ] **Step 2: Read the block to confirm scope** ```bash grep -n '^### Migration plan' docs/DESIGN.md # gives grep -n '^### Adjacent extensions' docs/DESIGN.md # gives sed -n ',p' docs/DESIGN.md ``` Expected: 30+ lines starting with `### Migration plan` and ending just before the `### Adjacent extensions for mutability …` header. The 7 numbered items follow the section header. - [ ] **Step 3: Delete the entire `### Migration plan` block** Use the Edit tool with `old_string` set to the entire Migration plan section (header + 7 items + the trailing blank line that separates it from the next subsection). The `new_string` is the empty string. After the edit, `### Codegen contract` (or whatever the prior subsection is) is followed directly by `### Adjacent extensions for mutability …` separated by one blank line. - [ ] **Step 4: Post-test** ```bash grep -n '^### Migration plan' docs/DESIGN.md ``` Expected: empty. ```bash grep -n '^### Adjacent extensions for mutability' docs/DESIGN.md ``` Expected: still present (this was NOT deleted; it follows directly after whatever subsection precedes it). - [ ] **Step 5: Commit** ```bash git add docs/DESIGN.md git commit -m "design-md-consolidation 2.3: delete Decision 10 §Migration plan (7-point completed-iter list)" ``` --- ## Task 4: Condense Decision 11 §"Why mono, not virtual dispatch" **Files:** Modify: `docs/DESIGN.md` (lines ~1603-1618 at iter start; shifted by Tasks 1-3; re-locate at Step 1). The current §"Why mono, not virtual dispatch (the empirically- grounded version)" paragraph is structured as a correction history: "The original rationale implicitly argued X. A micro-benchmark refutes that specific claim. The correct rationale is one level up: Y." State-only form skips the correction step: only Y stays. - [ ] **Step 1: Pre-test — locate the paragraph** ```bash grep -n 'empirically-grounded version' docs/DESIGN.md ``` Expected: 1 line, around `**Why mono, not virtual dispatch (the empirically-grounded version).**`. ```bash sed -n ',p' docs/DESIGN.md ``` Expected: the correction-history paragraph as currently formed (starts with `**Why mono, not virtual dispatch (the empirically- grounded version).** The original rationale implicitly argued …`, ends with `… while the older "saves an indirect call" framing does not.`). - [ ] **Step 2: Replace with the state-only form** Edit: - old (the full correction-history paragraph): ``` **Why mono, not virtual dispatch (the empirically-grounded version).** The original rationale implicitly argued that mono saves a per-call indirect-jump cost. A mono-vs-vdisp micro-benchmark (`bench/mono_dispatch.py`, JOURNAL bench-notes entry) refutes that specific claim: on a saturating branch predictor with a monomorphic indirect target, indirect dispatch is free relative to the same-shape non-inlined direct call. The correct rationale is one level up: **mono makes the call target visible to the optimiser, unlocking inlining and downstream loop transformations that virtual dispatch prevents in principle.** The measured end-to-end win on a tight LCG hot loop is substantial vs non-inlinable direct call and larger still vs polymorphic vdisp (4 distinct targets cycling). On larger callee bodies or cold call sites the inlining win shrinks toward zero, but the architectural claim — "mono enables optimisations vdisp forbids" — holds across the spectrum, while the older "saves an indirect call" framing does not. ``` - new: ``` **Why mono, not virtual dispatch.** Monomorphisation makes the call target visible to the optimiser, unlocking inlining and downstream loop transformations that virtual dispatch prevents in principle. On a saturating branch predictor with a monomorphic indirect target, the indirect call itself is comparable in cost to a non-inlined direct call — the win is in what the optimiser can do with the visible target, not in the call instruction. The end-to-end gain shrinks toward zero on larger callee bodies and cold call sites, but the architectural claim — "mono enables optimisations vdisp forbids" — holds across the spectrum (`bench/mono_dispatch.py` and the corresponding JOURNAL bench-notes entry record the measured ratios). ``` This drops "(the empirically-grounded version)" from the header, removes "the original rationale … refutes" framing, and folds the indirect-call-cost observation into a state-only form (it is true, in the present tense, that the indirect call is comparable in cost). The bench reference becomes a parenthetical pointer to JOURNAL, not a claim-and-refutation narrative. - [ ] **Step 3: Post-test** ```bash grep -nE 'empirically-grounded version|original rationale|refutes that specific claim' docs/DESIGN.md ``` Expected: empty. ```bash grep -n 'mono makes the call target\|optimiser, unlocking inlining' docs/DESIGN.md ``` Expected: at least one match — the substantive rationale survived. - [ ] **Step 4: Commit** ```bash git add docs/DESIGN.md git commit -m "design-md-consolidation 2.4: condense Decision 11 mono-vs-vdisp correction history to state-only rationale" ``` --- ## Task 5: Future-iter speculations — per-site decisions **Files:** Modify: `docs/DESIGN.md` (5 sites at iter start: lines 616, 762, 1063, 1134, 1424, 1750. Site 616 is moot — it lives inside Decision 7's body, deleted by Task 1. Verify at Step 1.) Per-site decisions: | Site (iter-start line) | Text fragment | Decision | |------------------------|---------------|----------| | 616 | `A future iter may upgrade the exhaustiveness check to recognise the \`true\`+\`false\` arm pair…` | MOOT — inside Decision 7 deleted by Task 1; verify the line is gone. | | 762 | `A future iter may layer a per-fn-arena optimisation on top: when a fn's return type contains no boxed ADT…` | CONDENSE — the per-fn arena is already shipped (Decision 9 §"Per-fn arena via stack `alloca`"). Drop the "future iter may layer" framing; the layering exists. | | 1063 | `A future iter that proposes any of laziness, recursive value bindings, shared mutable state, or any feature that creates cycles must either prove the cycle is collectible by an extension (e.g. linear ownership) or be rejected at design time.` | KEEP, tighten — this is a binding language constraint; restructure as an assertive forward-looking rule (drop "A future iter that proposes" wrapper; lead with the prohibition). | | 1134 | `a later iter (deferred) makes the explicit annotation mandatory and rejects \`Implicit\` for boxed parameter types.` | REMOVE — non-binding aspiration; if it ever ships, the language at that point describes itself. | | 1424 | `Concrete design deferred until the need materialises with a real workload.` | KEEP, condense — the binding part (no tracing GC backstop, ownership/linear extension) precedes; the "deferred" sentence is the punchline. Condense to remove the "until the need materialises" framing. | | 1750 | `A future milestone may add a Prelude when concrete LLM-author code surfaces a case that benefits from \`Show a\` / \`Eq a\` / \`Ord a\` over per-type primitive functions. Until then, primitive output goes through \`io/print_int\` / \`io/print_bool\` / \`io/print_str\` directly.` | REMOVE the first sentence; KEEP the second (the second describes current state). | - [ ] **Step 1: Pre-test — locate surviving sites** ```bash grep -nE 'A future (iter|milestone|Prelude) may|A future iter that|a later iter \(deferred\)|Concrete design deferred' docs/DESIGN.md ``` Expected: 5 sites (line 616 from the original is gone; the remaining 5 sites have shifted by -32-30-? lines after Tasks 1-3). If 6 sites match, line 616's content survived Task 1 — investigate before continuing. - [ ] **Step 2: Site 762 (per-fn arena)** Read the surrounding paragraph (typically a 10-line block opening with `A future iter may layer a per-fn-arena optimisation on top`). The next subsection (`### Per-fn arena via stack \`alloca\``) already describes the implemented arena. The "future iter may layer" paragraph is announcement; the next subsection is the spec. Delete the whole paragraph (the announcement); the implementation description stands alone. Edit: - old (the announcement paragraph): ``` A future iter may layer a per-fn-arena optimisation on top: when a fn's return type contains no boxed ADT, ADT boxes allocated inside that fn cannot escape, so an arena freed at fn return is sound by construction. That requires escape analysis, the corresponding AST/IR plumbing, and is its own design pass. Boehm-everything is the floor; arena is an optimisation above it. ``` - new: (empty — delete entire paragraph; the next subsection header `### Per-fn arena via stack \`alloca\`` follows directly.) Note: the paragraph may already differ slightly from the example above due to Sweep 1's history-anchor strips. Verify by reading the actual current text first. - [ ] **Step 3: Site 1063 (binding prohibition — KEEP, tighten)** Edit: - old (current form): ``` A future iter that proposes any of laziness, recursive value bindings, shared mutable state, or any feature that creates cycles must either prove the cycle is collectible by an extension (e.g. linear ownership) or be rejected at design time. ``` - new: ``` Laziness, recursive value bindings, shared mutable state, or any feature that creates cycles is rejected at design time unless the proposal proves the cycle is collectible by an extension (e.g. linear ownership). ``` This restructures from "a future iter that proposes X must Y" to "X is rejected unless Y" — the same constraint, in assertive present-tense form. - [ ] **Step 4: Site 1134 (REMOVE)** Read the surrounding paragraph; the sentence to remove is in the list under "Schema additions" (the Type::Fn parameter modes paragraph). The current text reads: ``` The legacy `(con T)` form is treated as `(own T)` semantically throughout existing fixtures; a later iter (deferred) makes the explicit annotation mandatory and rejects `Implicit` for boxed parameter types. ``` Edit: - old: ` a later iter (deferred) makes the explicit annotation mandatory and rejects \`Implicit\` for boxed parameter types.` - new: `` (empty) The remaining sentence reads "The legacy `(con T)` form is treated as `(own T)` semantically throughout existing fixtures." — this is state-only and stands alone. Adjust capitalisation/period: the ", a later iter" was preceded by a semicolon; after removal, the sentence ends at "fixtures." and the semicolon becomes a period. Verify the final form scans: ``` The legacy `(con T)` form is treated as `(own T)` semantically throughout existing fixtures. ``` - [ ] **Step 5: Site 1424 (KEEP, condense)** Read the surrounding paragraph (section "Adjacent extensions for mutability"). The current text reads: ``` If future workloads need mutable arrays, hash tables, or other inherently mutable primitives, the answer is **not** a tracing GC backstop. The answer is a separate ownership/linear extension that gates mutability behind static single-owner discipline. RC + uniqueness is the universal floor; ownership extends it for specific high-performance primitives without rebreaking the acyclicity invariant. Concrete design deferred until the need materialises with a real workload. ``` Edit: - old: `Concrete design deferred until the need materialises with a real workload.` - new: (empty — drop the entire sentence; the prior content carries the binding constraint, the "deferred" sentence adds nothing.) The paragraph now ends at "without rebreaking the acyclicity invariant." which is a clean state-only conclusion. - [ ] **Step 6: Site 1750 (REMOVE first sentence, KEEP second)** Edit: - old: ``` A future milestone may add a Prelude when concrete LLM-author code surfaces a case that benefits from `Show a` / `Eq a` / `Ord a` over per-type primitive functions. Until then, primitive output goes through `io/print_int` / `io/print_bool` / `io/print_str` directly. ``` - new: ``` Primitive output goes through `io/print_int` / `io/print_bool` / `io/print_str` directly. ``` The second sentence describes current state (how primitive output works today); the first is pure aspiration for an unbuilt feature. - [ ] **Step 7: Post-test** ```bash grep -nE 'A future (iter|milestone|Prelude) may|A future iter that|a later iter \(deferred\)|Concrete design deferred' docs/DESIGN.md ``` Expected: empty. ```bash grep -nE 'Laziness, recursive value bindings, shared mutable state' docs/DESIGN.md ``` Expected: 1 line — the binding prohibition (Site 1063, in restructured form) survived. - [ ] **Step 8: Commit** ```bash git add docs/DESIGN.md git commit -m "design-md-consolidation 2.5: future-iter speculations — remove aspirations, tighten binding prohibitions" ``` --- ## Task 6: Final validation + JOURNAL entry + roadmap update **Files:** Read: `docs/DESIGN.md`. Append: `docs/JOURNAL.md`. Modify: `docs/roadmap.md`. - [ ] **Step 1: Composite acceptance grep** ```bash grep -nE 'REVERTED|preserved for the audit trail|Migration plan|[Oo]riginally framed|original rationale|empirically-grounded version|A future (iter|milestone|Prelude) may|A future iter that|a later iter \(deferred\)|Concrete design deferred' docs/DESIGN.md ``` Expected: empty. ```bash grep -n 'Term::If' docs/DESIGN.md ``` Expected: at least one match in the Term-schema section (line ~1909 region, `{ "t": "if", "cond": Term, "then": Term, "else": Term }`) — `Term::If` exists in the language; the schema shows it. The Decision 7 *block* is gone, but `Term::If` itself is still described in the schema. - [ ] **Step 2: cargo test sweep** ```bash cargo test --workspace 2>&1 | grep -E "test result:|FAILED" | head -25 ``` Expected: every line ends `0 failed`. - [ ] **Step 3: Bench gates** ```bash python3 bench/check.py 2>&1 | tail -5 python3 bench/compile_check.py 2>&1 | tail -5 ``` Expected: each summary line shows `0 regressed`. - [ ] **Step 4: Sweep-1 acceptance grep stays clean** ```bash grep -nE 'Iter [0-9]+[a-z]?(\.[0-9]+)?|Family [0-9]+|^[^/]*2026-[0-9]{2}-[0-9]{2}|\*\*Status: |pre-[0-9]+[a-z]?|[0-9]+[a-z]? sketch|21.g' docs/DESIGN.md ``` Expected: empty (no regression of Sweep-1's invariant). - [ ] **Step 5: Append JOURNAL entry** Append to `docs/JOURNAL.md`. Template: ```markdown ## 2026-05-10 — Iteration design-md-consolidation 2: REVERTED + migration + correction history + future speculations Second iteration of the milestone defined in `docs/specs/0004-design-md-consolidation.md`. Sweep 2 closes the "REVERTED + migration plans + correction history + future speculations" item: the audit-trail preservation of Decision 7, the 7-point Decision 10 Migration plan, the Decision 9 narrative-of-changes opener, the Decision 11 mono-vs-vdisp correction history, and the non-binding future-iter speculations are removed or condensed. DESIGN.md now describes only the current state plus the timeless rationale; the discarded narratives live in JOURNAL (this entry plus the original iter entries that recorded each change at the time). Sections deleted entirely: - Decision 7 (Term::If REVERTED block) — 32 lines. `Term::If` exists in the language; the witness is the Term-schema section. Decision numbering preserves the gap (Decision 6 → Decision 8) so JOURNAL cross-references remain stable. - Decision 10 §"Migration plan" — 7-point list of past iters (annotation feature → RC runtime → uniqueness inference → reuse hints → drop-iterative → validation bench → advisory lint). The end state is described in the rest of Decision 10. Paragraphs condensed: - Decision 9 narrative-of-changes opener ("Originally framed as X; then re-framed as Y; the revision flips Z") → one-line state opener leading into the existing three RC/Boehm/bump bullets. - Decision 9 pre-Decision-9 history paragraphs ("Originally, every ADT box, lambda env, and closure pair was allocated with bare malloc and never freed…") removed. - Decision 11 §"Why mono, not virtual dispatch (the empirically-grounded version)" → state-only form. The substantive rationale ("mono enables optimisations vdisp forbids") survives; the correction history (`The original rationale implicitly argued … the 2026-05-10 micro-benchmark refutes that specific claim`) is dropped. Future-iter speculations — per-site decisions: - Site `A future iter may layer a per-fn-arena optimisation` REMOVED — the per-fn arena is shipped; the announcement is history. - Site `A future iter that proposes any of laziness…` KEPT, TIGHTENED — restructured from "future iter that proposes X must Y" to "X is rejected unless Y" (assertive present-tense form of the same binding constraint). - Site `a later iter (deferred) makes the explicit annotation mandatory` REMOVED — non-binding aspiration. - Site `Concrete design deferred until the need materialises` REMOVED — the binding content (no tracing GC backstop, ownership/linear extension) precedes; the "deferred" sentence was the unneeded punchline. - Site `A future milestone may add a Prelude` REMOVED first sentence (aspiration), KEPT second sentence (current-state description of how primitive output works today). Acceptance: - composite grep `grep -nE 'REVERTED|preserved for the audit trail|Migration plan|[Oo]riginally framed|original rationale|empirically-grounded version|A future (iter|milestone|Prelude) may|A future iter that|a later iter \(deferred\)|Concrete design deferred' docs/DESIGN.md` returns empty. - Sweep-1 invariant grep stays empty (no regression). - `cargo test --workspace` 0 FAILED. - `bench/check.py` 0 regressed; `bench/compile_check.py` 0 regressed. - `Term::If` in the Term schema (Data-model section) still present; only the Decision 7 *block* is gone, not the AST node. - DESIGN.md size: lines (down from 2253 — record the actual number). Tasks (commit subjects): - design-md-consolidation 2.1: delete Decision 7 (Term::If REVERTED block) - design-md-consolidation 2.2: condense Decision 9 narrative-of-changes opener + drop pre-Decision-9 history paragraphs - design-md-consolidation 2.3: delete Decision 10 §Migration plan (7-point completed-iter list) - design-md-consolidation 2.4: condense Decision 11 mono-vs-vdisp correction history to state-only rationale - design-md-consolidation 2.5: future-iter speculations — remove aspirations, tighten binding prohibitions Carried into sweep 3: schema SoT inversion (DESIGN.md §Data-model becomes the canonical schema; ast.rs gains a doc-comment pointing to it; new drift test `design_schema_drift.rs` enforces structural agreement). Carried into sweep 4: workflow / cross-reference cleanup (Project ecosystem `agents/` path correction; Verification section workflow detail; "What is not (yet) supported" §Recently lifted gates removal). Process note: this iter's plan-time composite grep matched the spec's full intent on the first attempt — no fixup commits for missed anchor variants. The Sweep 1 lesson held. ``` Adjust the template's specifics to match what was actually edited; record the actual final DESIGN.md line count. - [ ] **Step 6: Update roadmap.md** Edit `docs/roadmap.md`: - old: ``` - [x] Sweep 1 — remove history anchors - [ ] Sweep 2 — REVERTED + migration plans out - [ ] Sweep 3 — schema SoT inversion + data-model hardening ``` - new: ``` - [x] Sweep 1 — remove history anchors - [x] Sweep 2 — REVERTED + migration plans out - [ ] Sweep 3 — schema SoT inversion + data-model hardening ``` - [ ] **Step 7: Commit** ```bash git add docs/JOURNAL.md docs/roadmap.md git commit -m "design-md-consolidation 2: journal entry + roadmap sweep-2 closed" ```