plan: design-md-rolesplit.1 — the whole milestone, build-atomic by task ordering

9 tasks: (1) RED-first design_index_pin.rs 4-clause guard; (2)
design/INDEX.md + 14 prose contracts; (3) 5 model whitepapers; (4)
decision-record journal + INDEX pointer; (5) retarget the 3
doc-reading tests GREEN vs design/; (6) 2 diagnostics + 2 E2Es
lockstep; (7) architect_sweeps.sh spine repoint; (8) ~12 agent lists
+ 5 SKILL bodies + CLAUDE.md + README + bucket-(e) comment xrefs +
OQ7 cite deletion; (9) clean cut (git rm DESIGN.md) + whole-tree gate
+ acceptance grep. Build-atomicity by construction: include_str!
retarget (Task 5) precedes DESIGN.md deletion (Task 9), data-model.md
exists from Task 2 — every intermediate cargo build is green, no
monster task. Self-review 8/8 (contiguity + compile-gate-ordering +
filter-resolution scrubbed).
This commit is contained in:
2026-05-19 12:16:21 +02:00
parent 314e5e4920
commit deeffb1872
+795
View File
@@ -0,0 +1,795 @@
# DESIGN.md → `design/` role-split — Implementation Plan (iter 1, the whole milestone)
> **Parent spec:** `docs/specs/2026-05-19-design-md-rolesplit.md`
> (incl. the authoritative **Appendix — Boss-adjudicated relocation
> map**; grounding-check PASS ×2).
>
> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement`
> to run this plan. Steps use `- [ ]` checkboxes for tracking.
**Goal:** Replace the 3020-line `docs/DESIGN.md` with the `design/`
ledger (`INDEX.md` + 15 `contracts/` + 5 `models/`), move
decision-records to a journal, retarget every live reference, and
ship the RED-first `design_index_pin.rs` anti-regrowth guard — in a
build-atomic shape.
**Architecture:** The spec Appendix table is the per-`##`/`###`
relocation map (line ranges → destination); this plan executes it.
Build-atomicity is achieved *by task ordering*: `design/contracts/data-model.md`
is created (Task 2) and `design_schema_drift.rs`'s `include_str!`
retargeted to it (Task 5) **before** `docs/DESIGN.md` is deleted
(Task 9), so the workspace compiles at every task boundary. The
single milestone commit is the Boss's; tasks are review units, not
commits.
**Tech Stack:** Markdown (`design/`), Rust tests
(`crates/ailang-core/tests/`, `crates/ail/tests/`), `crates/ailang-check/src/lib.rs`
(2 diagnostic strings — the only logic edit), `bench/architect_sweeps.sh`,
skill/agent Markdown.
**Authoritative map:** every "move DESIGN.md:AB" below is the spec
Appendix row for that range. The Appendix is placeholder-free; this
plan does not restate the 70-row table — it cites it. Re-levelling
rule (all moved prose): the source `##`/`###` heading becomes the
file's `#` H1 (or `##` for a subsection under a file's single H1);
no content edits except (a) heading level, (b) `honesty-rule.md` is
*rewritten* per spec §Architecture-4, (c) the OQ7 cite deletion.
---
## Task 1: RED-first `design_index_pin.rs` (the anti-regrowth spine)
**Files:**
- Create: `crates/ailang-core/tests/design_index_pin.rs`
- [ ] **Step 1: Write the 4-clause guard test**
```rust
//! Structural pin for the design/ ledger. Sibling of
//! docs_honesty_pin.rs. Fails RED the instant the design/ split
//! re-conflates contract + rationale + narrative, an INDEX row
//! dangles, a contract loses its ratifying test, or docs/DESIGN.md
//! is resurrected. Spec: docs/specs/2026-05-19-design-md-rolesplit.md.
use std::fs;
use std::path::{Path, PathBuf};
fn root() -> PathBuf {
PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../../"))
}
fn read(rel: &str) -> String {
fs::read_to_string(root().join(rel))
.unwrap_or_else(|e| panic!("read {rel}: {e}"))
}
fn norm(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
/// Parse the two pipe-tables out of design/INDEX.md.
/// Returns (contracts, models) as rows of trimmed cells.
fn index_tables() -> (Vec<Vec<String>>, Vec<Vec<String>>) {
let idx = read("design/INDEX.md");
let mut contracts = Vec::new();
let mut models = Vec::new();
let mut section = "";
for line in idx.lines() {
let t = line.trim();
if t.starts_with("## Contracts") {
section = "c";
continue;
}
if t.starts_with("## Models") {
section = "m";
continue;
}
if !t.starts_with('|') {
continue;
}
let cells: Vec<String> = t
.trim_matches('|')
.split('|')
.map(|c| c.trim().to_string())
.collect();
// skip header + separator rows
if cells.iter().any(|c| c.starts_with("---")) {
continue;
}
if cells.first().map(|c| c.as_str()) == Some("id") {
continue;
}
match section {
"c" => contracts.push(cells),
"m" => models.push(cells),
_ => {}
}
}
(contracts, models)
}
/// A link cell may be a design/ path, a source path, a dual-link
/// "A + B", or carry a trailing "(in-source ...)" / "§..." note.
/// Resolve to the first concrete path token and check it exists.
fn link_target_exists(cell: &str) -> bool {
let first = cell.split(" + ").next().unwrap_or(cell).trim();
// strip a trailing parenthetical or §-note
let path = first
.split(" (")
.next()
.unwrap_or(first)
.split(" §")
.next()
.unwrap_or(first)
.trim()
.trim_end_matches("//!")
.trim();
!path.is_empty() && root().join(path).exists()
}
#[test]
fn design_md_is_gone() {
// clause 4 — clean-cut pin
assert!(
!root().join("docs/DESIGN.md").exists(),
"docs/DESIGN.md was resurrected; the split is clean-cut"
);
}
#[test]
fn every_index_link_resolves() {
// clause 1
let (contracts, models) = index_tables();
assert!(contracts.len() >= 15, "expected >=15 contract rows, got {}", contracts.len());
assert!(models.len() >= 5, "expected >=5 model rows, got {}", models.len());
for row in contracts.iter().chain(models.iter()) {
let link = row.last().expect("row has a link cell");
assert!(
link_target_exists(link),
"INDEX link does not resolve: {:?} (row {:?})",
link,
row
);
}
}
#[test]
fn every_contract_names_a_resolvable_ratifying_test() {
// clause 2 — ratifying-test token resolves to a real file under
// crates/**/tests, crates/**/src (in-source #[cfg(test)] mod
// tests are first-class ratifiers — spec OQ1/OQ2), bench/, or
// skills/**/SKILL.md.
let (contracts, _) = index_tables();
for row in &contracts {
// columns: id | consumer/lifetime | ratifying-test | link
let rt = &row[2];
// take the path token (before any " (" note)
let path = rt.split(" (").next().unwrap_or(rt).trim();
assert!(
root().join(path).exists(),
"ratifying-test does not resolve to a real file: {:?} (contract {:?})",
path,
row[0]
);
}
}
#[test]
fn contracts_carry_no_decision_record_prose() {
// clause 3 — the conflation tripwire.
let dir = root().join("design/contracts");
let markers = [
"we rejected",
"an earlier draft",
"Why not other",
"was retired in iter",
"rollback plan",
"previously all",
];
for entry in fs::read_dir(&dir).expect("design/contracts/ exists") {
let p = entry.unwrap().path();
if p.extension().and_then(|e| e.to_str()) != Some("md") {
continue;
}
let body = norm(&fs::read_to_string(&p).unwrap());
for m in &markers {
assert!(
!body.contains(m),
"decision-record marker {:?} found in contract {:?} — re-conflation",
m,
p.file_name().unwrap()
);
}
}
}
```
- [ ] **Step 2: Run to verify it is RED**
Run: `cargo test -p ailang-core --test design_index_pin 2>&1 | tail -20`
Expected: FAIL — `design_md_is_gone` fails ("was resurrected"; DESIGN.md
still present) and `every_index_link_resolves` / the others panic on
`read design/INDEX.md: ... No such file`. The test **file compiles**
(build stays green); only the assertions are RED. This is the
required RED-first state.
---
## Task 2: `design/INDEX.md` + the 15 `design/contracts/*.md`
**Files:**
- Create: `design/INDEX.md`
- Create: `design/contracts/{feature-acceptance,authoring-surface,roundtrip-invariant,memory-model,data-model,float-semantics,typeclasses,tail-calls,frozen-value-layout,honesty-rule,embedding-abi,str-abi,scope-boundaries,verification}.md`
(14 prose files; `mangling`, `env-construction`, `qualified-xref`
are source-link-only — **no file**, INDEX rows only)
- [ ] **Step 1: Write `design/INDEX.md`**
Use the spec §"Concrete code shapes" item 1 INDEX block **verbatim**
(it already carries the corrected 15-row Contracts table incl.
`qualified-xref`, `str-abi`, `scope-boundaries`, and the 5-row
Models table). Header preamble = the spec's INDEX preamble text
PLUS, per spec Appendix OQ6, a short "## Project framing"
sub-section absorbing DESIGN.md:618 (`## Goal`), :1955
(`## Project ecosystem` intro), :8392 (`## Project language:
English`) — moved whole, re-levelled to `###` under the preamble.
- [ ] **Step 2: Run the INDEX-shape assertion**
Run: `cargo test -p ailang-core --test design_index_pin every_index_link_resolves 2>&1 | tail -5`
Expected: still FAIL, but now past the parse (panics on a contract
file not yet existing, not on "read design/INDEX.md"). Confirms
INDEX.md parses and has ≥15 contract / ≥5 model rows.
- [ ] **Step 3: Create the 14 prose contract files (one per Appendix contract row)**
For each file below, concatenate the cited DESIGN.md line ranges
**from the spec Appendix table**, re-level the top heading to `#`,
and route every `### …` the Appendix tags `D` to Task 4 (NOT into
this file). Files and their Appendix-sourced ranges:
- `feature-acceptance.md` ← DESIGN.md:93164
- `authoring-surface.md` ← :230240 + :268297 + :298331 (the three
contract `###`s of Decision 6 per Appendix; :241243 and all
`### Why…`/`### …does not do`/`### Implementation outline`/`###
Form refinements`/`### Empirical addendum` go to Task 4)
- `roundtrip-invariant.md` ← :21132208 (whole block, 3 `###`s kept)
- `memory-model.md` ← :11231150 + :11511312 + :13131351 + :13941412
+ :14131509 (the five binding `###`s per Appendix)
- `data-model.md` ← :23772615 (whole `## Data model` + 4 `###`s)
- `float-semantics.md` ← :27002838 **except** the inline
`**Str ABI.**` paragraph at :2802 (that one para → `str-abi.md`,
the single sanctioned sentence-level move per Appendix OQ4)
- `typeclasses.md` ← :15781639 + :17531823 + :18241855 + :18561917
+ :19181964 + :20012042 (the six contract `###`s of Decision 11
per Appendix)
- `tail-calls.md` ← :731787 (whole `## Decision 8`)
- `frozen-value-layout.md` ← :23342376
- `honesty-rule.md`**rewritten** (next step — do NOT raw-move :5682)
- `embedding-abi.md` ← :22662333
- `str-abi.md` ← :20432088 (`### Heap-Str primitives`) + the
`**Str ABI.**` paragraph lifted from :2802
- `scope-boundaries.md` ← :28393020 (`## What is not (yet) supported`)
- `verification.md` ← :26872699
Each file starts `# <Title>` and ends with a line
`Ratified by: <the INDEX ratifying-test path for this contract>.`
- [ ] **Step 4: Write the rewritten `design/contracts/honesty-rule.md`**
Do NOT raw-move DESIGN.md:5682. Rewrite so the rule names the new
home. The two `docs_honesty_pin.rs:70,72` anchors MUST appear as
**contiguous substrings** (planner item-6 contiguity — no soft-wrap
splitting them):
```markdown
# The honesty rule this ledger holds itself to
`design/` describes what AILang **is now**: schema, semantics,
invariants, runtime contracts. It is present-tense by construction.
Two things never belong in a contract or model file:
- **Forward intent** ("planned / will back / on the path to") — that
lives in `docs/roadmap.md`.
- **History and rationale** ("an earlier draft said / previously /
why X was chosen / why Y was rejected / retired in iter Z") — that
lives in `docs/journals/`. Decision-records are journal content,
not ledger content; this is the honesty rule it holds itself to.
The single legitimate exception is a present-tense reserved or
deliberately-excluded claim that is explicitly and correctly
labelled. The discriminator is not whether a sentence mentions past
or future, but whether the document asserts something exists, works, or changed that does not.
Ratified by: `crates/ailang-core/tests/docs_honesty_pin.rs`.
```
(The two pinned phrases — `the honesty rule it holds itself to` and
`whether the document asserts something exists, works, or changed
that does not` — each sit on a single physical line above; `norm()`
in `docs_honesty_pin.rs` is whitespace-tolerant but contiguity is
kept anyway to satisfy the raw-`grep` advisory sweep.)
- [ ] **Step 5: Verify clause-3 (no decision-record prose leaked into contracts)**
Run: `cargo test -p ailang-core --test design_index_pin contracts_carry_no_decision_record_prose 2>&1 | tail -5`
Expected: PASS (every `###` the Appendix tags `D` was routed to
Task 4, not into these files). If a marker fires, a `### Why…`
subsection was wrongly included — move it to the Task 4 journal.
---
## Task 3: the 5 `design/models/*.md` whitepapers
**Files:**
- Create: `design/models/{rc-uniqueness,typeclasses,effects,authoring-surface,pipeline}.md`
- [ ] **Step 1: Create the 5 model files (Appendix model-tagged ranges)**
- `rc-uniqueness.md` ← :788852 + :853913 + :914977 + :10151122
+ :13781393 (Decision 9/10 narrative `###`s per Appendix)
- `typeclasses.md` ← :15361577 + :16401752 (Decision 11 narrative)
- `effects.md` ← :190204 (Decision 3 — the
`effect_doc_honesty_pin.rs` present-anchors live here; that pin's
read retargets to this file in Task 5)
- `authoring-surface.md` ← :332471 + :590674 (`### Candidate
notations` + `### Form (B)` — the `docs_honesty_pin.rs` Form-B
placeholder present-anchor retargets here in Task 5)
- `pipeline.md` ← :26162662 (`## Pipeline`) + :26632686 (`## CLI`)
Each starts `# <Title>`. Models carry no `Ratified by:` line (no
test pins a model — clause 2 is contracts-only).
- [ ] **Step 2: Verify models resolve in INDEX**
Run: `cargo test -p ailang-core --test design_index_pin every_index_link_resolves 2>&1 | tail -5`
Expected: closer to PASS — model links now resolve; remaining
failures are only source-link contract rows whose targets already
exist (`runtime/str.c`, `crates/ailang-codegen/src/lib.rs`, …) so
this should now PASS for all 20 link rows. If it still fails, the
failing row's link path is mistyped in INDEX.md — fix the INDEX
cell.
---
## Task 4: decision-record journal + journals INDEX pointer
**Files:**
- Create: `docs/journals/2026-05-19-design-decision-records.md`
- Modify: `docs/journals/INDEX.md` (append one pointer line)
- [ ] **Step 1: Create the relitigation-guard archive**
Concatenate, in DESIGN.md order, every range the spec Appendix tags
`D`: :165176, :177189, :205213, :214229, :241243, :245267,
:472486, :487519, :520536, :537589, :675730, :9781014,
:13521377, :15101519, :15201535, :19652000, :20892112, plus
any genuine "why" sentence extracted from the source-link `##`s
(`## Mangling scheme` :22092218, `## Convention: qualified
cross-module references` :22192236, `## Env construction`
:22372265 — per Appendix, behaviour sentences are *dropped* as
code-is-SoT; only rationale sentences move here).
Header:
```markdown
# Design decision-records — relitigation guard (migrated 2026-05-19)
Why-X-chosen / why-Y-rejected / deliberately-does-not-do / rollback
/ empirical-addendum prose, migrated out of the former
`docs/DESIGN.md` by the design-md-rolesplit milestone. A future
brainstorm reads this so it does not re-propose a settled-and-
rejected idea. Order preserved from the source. This file is
append-only history; it is NOT a contract surface.
```
- [ ] **Step 2: Append the journals INDEX pointer**
Append to `docs/journals/INDEX.md` (newest-last, one line):
```
- 2026-05-19 — design-decision-records (migration): relitigation-guard archive — every why/rejected/does-not-do/rollback/empirical ### moved out of the former docs/DESIGN.md by the design-md-rolesplit milestone. Companion to spec 2026-05-19-design-md-rolesplit.
```
- [ ] **Step 3: Verify acceptance criterion 2 (nothing dropped)**
Run: `python3 - <<'EOF'
import re
src=open("docs/DESIGN.md").read().splitlines()
heads=[(i+1,l) for i,l in enumerate(src) if re.match(r'^#{2,3} ',l)]
print(len(heads),"headings in DESIGN.md;",
"expect every line range covered by spec Appendix")
EOF`
Expected: prints the heading count; cross-check by eye that every
`##`/`###` appears in exactly one of Task 2/3/4 (the Appendix is the
checklist). This is a human-verifiable completeness gate, not an
automated assertion (the automated guarantee is clause-3 +
acceptance grep in Task 9).
---
## Task 5: retarget the three doc-reading tests (GREEN against `design/`)
**Files:**
- Modify: `crates/ailang-core/tests/design_schema_drift.rs:22` (+ remove `data_model_section` 2837 and `data_model_section_is_bounded` 411430; repoint 7 anchor tests)
- Modify: `crates/ailang-core/tests/docs_honesty_pin.rs` (the `read("docs/DESIGN.md")` calls + the :70/:72 anchors)
- Modify: `crates/ailang-core/tests/effect_doc_honesty_pin.rs:21`
- [ ] **Step 1: Retarget `design_schema_drift.rs`**
Line 22, exact before → after:
```rust
- const DESIGN_MD: &str = include_str!("../../../docs/DESIGN.md");
+ const DATA_MODEL: &str = include_str!("../../../design/contracts/data-model.md");
```
Delete `fn data_model_section()` (lines 2837) and
`fn data_model_section_is_bounded()` (lines 411430). In the 7
anchor tests (`design_md_anchors_every_term_variant` :43,
`…pattern…` :185, `…type…` :214, `…literal…` :248, `…def_kind`
:277, `…parammode…` :348, `…nested_struct_keys` :373) replace every
`data_model_section()` call with the const `DATA_MODEL`. Update the
module rustdoc (:1,3,11) "DESIGN.md §Data model" → "design/contracts/data-model.md".
- [ ] **Step 2: Retarget `docs_honesty_pin.rs` per-sentence home**
The `read()` helper (1519) and `norm()` (2426) are unchanged.
Replace each `read("docs/DESIGN.md")` with a read of the design/
file where that test's pinned strings now live (per spec Appendix):
- `design_md_has_no_wunschdenken` (:30) and
`design_md_has_no_doc_archaeology` (:51): absent-pins span
Boehm/float/typeclasses/effects/Form-B — read the concatenation
of `design/models/rc-uniqueness.md`, `design/contracts/float-semantics.md`,
`design/contracts/typeclasses.md`, `design/models/effects.md`,
`design/models/authoring-surface.md` (absent-pins must be absent
from all → join the reads).
- `design_md_present_tense_anchors_present` (:68): split per anchor
to its home file; **:70** `"the honesty rule it holds itself to"`
and **:72** `"whether the document asserts something exists,
works, or changed that does not"` → read `design/contracts/honesty-rule.md`;
the Form-B placeholder anchor (:90 area) → `design/models/authoring-surface.md`;
the remaining present-anchors → their Appendix home file.
- `form_a_scalar_param_carveout_present_and_old_rule_absent`
(:104137): the `FORM_A_SPEC` read is unchanged; the
`read("docs/DESIGN.md")` at :112 → `design/contracts/embedding-abi.md`
(the `:135` pin `Export parameters are written **bare**` lives
in `## Embedding ABI`, Appendix → embedding-abi.md).
- `prose_roundtrip_md_has_no_wunschdenken` (:94101): reads
`docs/PROSE_ROUNDTRIP.md` — **unchanged**.
- [ ] **Step 3: Retarget `effect_doc_honesty_pin.rs:21`**
`design_md_effect_prose_is_true` (:1934), line 21:
```rust
- let d = read("docs/DESIGN.md");
+ let d = norm(&[read("design/models/effects.md"),
+ read("design/contracts/scope-boundaries.md")].join("\n"));
```
(Decision 3 effect prose → `models/effects.md`; the
`"the built-in IO and Diverge ops"` absent-pin's home
`## What is not (yet) supported` → `contracts/scope-boundaries.md`.
Adjust the existing `norm()` usage in the test so the join is
normalised once; keep every pinned string assertion unchanged.)
- [ ] **Step 4: Verify the three tests GREEN (DESIGN.md still present)**
Run: `cargo test -p ailang-core --test design_schema_drift --test docs_honesty_pin --test effect_doc_honesty_pin 2>&1 | tail -15`
Expected: PASS for all three (they now read `design/` files which
exist from Tasks 23; `docs/DESIGN.md` still on disk but no longer
read by these tests). `cargo build --workspace` still green
(`include_str!` now points at the existing `design/contracts/data-model.md`).
---
## Task 6: retarget the 2 diagnostics + 2 E2Es (lockstep)
**Files:**
- Modify: `crates/ailang-check/src/lib.rs` (Float branch ~872876, Show branch ~885893)
- Modify: `crates/ail/tests/eq_float_noinstance.rs:41-42`
- Modify: `crates/ail/tests/show_no_instance_e2e.rs:47-48`
- [ ] **Step 1: Retarget the Float-branch diagnostic**
`crates/ailang-check/src/lib.rs` Float branch, exact:
```rust
- orderability per IEEE-754); see DESIGN.md §\"Float semantics\".",
+ orderability per IEEE-754); see design/contracts/float-semantics.md.",
```
- [ ] **Step 2: Retarget the Show-branch diagnostic (contiguity-safe)**
`crates/ailang-check/src/lib.rs` Show branch. The path
`design/contracts/typeclasses.md` MUST contain no interior literal
whitespace and must not be split by a `\`-continuation in a way
that injects a space (Rust `\`-continuation strips the newline +
leading indent, so keep the full path token on one physical source
line). Exact:
```rust
- the prelude; see DESIGN.md §\"Prelude (built-in) \
- classes\". User types declare their own \
+ the prelude; see design/contracts/typeclasses.md. \
+ User types declare their own \
```
(Runtime string becomes `…the prelude; see design/contracts/typeclasses.md. User types declare their own …` — the path is one contiguous run; the E2E `contains("design/contracts/typeclasses.md")` holds.)
- [ ] **Step 3: Retarget the two E2E assertions (lockstep)**
`crates/ail/tests/eq_float_noinstance.rs` lines 4142:
```rust
- no_inst.message.contains("Float semantics") || no_inst.message.contains("DESIGN"),
- "expected NoInstance message to cross-reference DESIGN.md §Float semantics, got: {:?}",
+ no_inst.message.contains("design/contracts/float-semantics.md"),
+ "expected NoInstance message to cross-reference the float-semantics contract, got: {:?}",
```
`crates/ail/tests/show_no_instance_e2e.rs` lines 4748:
```rust
- no_inst.message.contains("Prelude (built-in) classes"),
- "expected DESIGN.md §Prelude (built-in) classes cross-reference, got message: {:?}",
+ no_inst.message.contains("design/contracts/typeclasses.md"),
+ "expected design/contracts/typeclasses.md cross-reference, got message: {:?}",
```
Also update the module-rustdoc / comment lines in both E2E files
(`eq_float_noinstance.rs:5,38`, `show_no_instance_e2e.rs:5,43-45`)
that name "DESIGN.md §…" → the new contract path.
- [ ] **Step 4: Verify the lockstep E2Es GREEN**
Run: `cargo test -p ail --test eq_float_noinstance --test show_no_instance_e2e 2>&1 | tail -10`
Expected: PASS — each asserts the new contiguous pointer substring.
(Both filters resolve: `eq_float_noinstance` has
`eq_at_float_fires_float_aware_noinstance`; `show_no_instance_e2e`
has its Show-noinstance test — verified present in the tree.)
---
## Task 7: retarget `bench/architect_sweeps.sh`
**Files:**
- Modify: `bench/architect_sweeps.sh:22,25-28,34` (+ the 5 `run_sweep` scan target)
- [ ] **Step 1: Repoint the spine + guard**
```sh
- DESIGN="docs/DESIGN.md"
+ INDEX="design/INDEX.md"
+ DESIGN_GLOB="design/contracts design/models"
```
Guard (2528):
```sh
- if [[ ! -f "$DESIGN" ]]; then
- echo "architect_sweeps: $DESIGN not found (run from repo root)" >&2
+ if [[ ! -f "$INDEX" ]]; then
+ echo "architect_sweeps: $INDEX not found (run from repo root)" >&2
exit 2
fi
```
- [ ] **Step 2: Repoint the grep target (multi-file scan)**
Line 34, before → after (recursive over the design/ prose set;
sweep regexes themselves **unchanged** per spec §3):
```sh
- matches=$(grep -nE "$pattern" "$DESIGN" || true)
+ matches=$(grep -rnE "$pattern" $DESIGN_GLOB || true)
```
Exit-code contract preserved: 0 = all sweeps clean, 1 = an anchor
matched, 2 = `design/INDEX.md` not found.
- [ ] **Step 3: Verify exit-0 clean and exit-2 on missing spine**
Run: `bash bench/architect_sweeps.sh; echo "exit=$?"`
Expected: prints the per-sweep lines and `exit=0` (the migrated
`design/` prose is honest — no wunschdenken/post-mortem; if Sweep 5
fires, a moved file kept a stripped-prose marker → fix the file,
not the sweep).
Run: `mv design/INDEX.md /tmp/IX.bak; bash bench/architect_sweeps.sh; echo "exit=$?"; mv /tmp/IX.bak design/INDEX.md`
Expected: `architect_sweeps: design/INDEX.md not found` and `exit=2`.
---
## Task 8: retarget buckets (b)(c)(d)(e) — agent lists, SKILL bodies, CLAUDE.md, README, comment xrefs
**Files (from plan-recon's exhaustive 5-bucket set):**
- Modify (b) ~12 agent files: `skills/audit/agents/ailang-architect.md`, `skills/audit/agents/ailang-bencher.md`, `skills/brainstorm/agents/ailang-grounding-check.md`, `skills/debug/agents/ailang-debugger.md`, `skills/docwriter/agents/ailang-docwriter.md`, `skills/fieldtest/agents/ailang-fieldtester.md`, `skills/implement/agents/ailang-implementer.md`, `skills/implement/agents/ailang-implement-orchestrator.md`, `skills/implement/agents/ailang-quality-reviewer.md`, `skills/implement/agents/ailang-spec-reviewer.md`, `skills/implement/agents/ailang-tester.md`, `skills/planner/agents/ailang-plan-recon.md`
- Modify (c) 5 SKILL bodies: `skills/audit/SKILL.md`, `skills/brainstorm/SKILL.md`, `skills/docwriter/SKILL.md`, `skills/fieldtest/SKILL.md`, `skills/boss/SKILL.md`
- Modify (d) `CLAUDE.md`, `skills/README.md`
- Modify (e) code/C/.ail comment xrefs (full list below)
- [ ] **Step 1: Rewrite the architect reading-list bullet (the representative)**
`skills/audit/agents/ailang-architect.md:30`, exact:
```
- 2. `docs/DESIGN.md` — the canonical specification. Drift is measured against
+ 2. `design/INDEX.md` — the typed contract ledger and sole spine.
+ Walk the Contracts table; drift is measured against each row's
+ `link` target (a `design/contracts/` file or the source `//!`
+ it names). `design/models/` is context, not a drift surface.
```
Also `ailang-architect.md:73,81,83,118,123` (the `architect_sweeps.sh`
exit-2 / Sweep-5 references): replace "DESIGN.md" with
"design/INDEX.md" and keep the exit-2 / sweep semantics text
(Task 7 preserved them).
- [ ] **Step 2: Rewrite the remaining (b) agent reading lists to role**
Per spec §4: `grounding-check` (`:34`) and architect read
`design/contracts/`; `fieldtester` (`:41`, no-edit limit `:255`
preserved) reads `design/INDEX.md` + `design/models/`; `bencher`
(`:38`) reads `design/models/rc-uniqueness.md` (Decision-9/Boehm
narrative); `debugger` (`:29`), `tester` (`:26`), `implementer`
(`:30`), `implement-orchestrator` (`:35`), `spec-reviewer` (`:34`),
`quality-reviewer` (`:32`), `plan-recon` (`:34,72,147`),
`docwriter` (`:35` + the 8 other refs) → name `design/INDEX.md` as
the spine. Replace every literal `docs/DESIGN.md` token in these
files; preserve each bullet's role descriptor.
- [ ] **Step 3: Rewrite (c) SKILL bodies + (d) CLAUDE.md / README**
Replace every live `docs/DESIGN.md` reference in
`skills/{audit,brainstorm,docwriter,fieldtest,boss}/SKILL.md` and
`skills/README.md` with `design/INDEX.md` (or the specific
`design/contracts/<x>.md` where the prose names a section, e.g.
`boss/SKILL.md:284` "Canonical feature-acceptance criterion" →
`design/contracts/feature-acceptance.md`). `CLAUDE.md`: `:45`
code-layout table row, the `## Roles of docs/DESIGN.md …` heading
and its paragraph (:199212) → rewrite to describe the `design/`
ledger (DESIGN = current-state mirror discipline preserved, just
re-homed); `:78,178` inline refs → `design/INDEX.md`.
- [ ] **Step 4: Retarget (e) code/C/.ail comment xrefs + OQ7 deletion**
Replace `DESIGN.md §"X"` → the Appendix destination
(`design/contracts/<x>.md` or `design/models/<x>.md`) in: `ail-embed/src/lib.rs:4,35,91`
(drop the DESIGN.md line-number cites — `→ design/contracts/frozen-value-layout.md`);
`crates/ailang-check/src/lib.rs:449,861,882,2647,6336`;
`crates/ailang-check/tests/duplicate_ctor_pin.rs:4` (→ env-construction
source-link note); `crates/ailang-codegen/src/drop.rs:88,114`,
`lib.rs:178,685`, `match_lower.rs:107`;
`crates/ailang-codegen/tests/embed_record_layout_pin.rs:1`,
`embed_staticlib_lowering.rs:47`; `crates/ailang-core/src/ast.rs:3`
(→ `design/contracts/data-model.md`), `:211`;
`crates/ailang-surface/src/lib.rs:7`, `parse.rs:78,81`,
`print.rs:128`; `crates/ailang-surface/tests/round_trip.rs:10,72`
(→ `design/contracts/roundtrip-invariant.md`);
`crates/ail/src/main.rs:1393` (→ qualified-xref source-link),
`:1572`; `crates/ail/tests/codegen_import_map_fallback_pin.rs:2`,
`polyfn_dot_qualified_branch_pin.rs:2` (→
`design/contracts/typeclasses.md`), `e2e.rs:2843` (→
`design/contracts/str-abi.md`), `eq_ord_e2e.rs:89,110` (→ mangling
source-link), `embed/record_roundtrip.c:2`, `embed/tick_roundtrip.c:6`
(→ `design/contracts/frozen-value-layout.md`);
`examples/fieldtest/floats_3_safe_division.ail:19`,
`floats_4_float_to_str_reach.ail:6` (→
`design/contracts/float-semantics.md`); `runtime/rc.c:41` (→
`design/contracts/frozen-value-layout.md`), `runtime/str.c:172,198`
(→ `design/contracts/str-abi.md`);
`crates/ailang-core/specs/form_a.md:89`.
**OQ7 — delete, not retarget:** `crates/ailang-codegen/src/lib.rs:103`
"Iter 13b notes in `DESIGN.md`" — rewrite the comment to state the
behaviour directly and **drop the cite entirely** (no `design/` or
journal pointer; the referenced notes exist nowhere — a pointer
would be fiction, itself an honesty-rule violation).
- [ ] **Step 5: Verify build still green (doc/comment-only task)**
Run: `cargo build --workspace 2>&1 | tail -3`
Expected: `Finished` (this task edits only Markdown + comments +
docstrings; no logic). `docs/DESIGN.md` still present here.
---
## Task 9: the clean cut — delete `docs/DESIGN.md` + whole-tree gate
**Files:**
- Delete: `docs/DESIGN.md`
- [ ] **Step 1: Delete the file**
Run: `git rm -q docs/DESIGN.md && echo deleted`
Expected: `deleted`. (`git rm` stages the deletion; the Boss owns
the final commit shape — this only removes it from the working
tree + index, no commit.)
- [ ] **Step 2: Workspace build gate (build-atomicity proof)**
Run: `cargo build --workspace 2>&1 | tail -3`
Expected: `Finished …` with **0 errors**. This proves
build-atomicity: the only compile-time consumer
(`design_schema_drift.rs` `include_str!`) was retargeted in Task 5
to the now-existing `design/contracts/data-model.md`, so deleting
`docs/DESIGN.md` does not break the build.
- [ ] **Step 3: Whole-suite GREEN incl. the design_index_pin spine**
Run: `cargo test --workspace 2>&1 | tail -25`
Expected: all green. Specifically `design_index_pin`'s four tests
now ALL pass: `design_md_is_gone` (DESIGN.md deleted),
`every_index_link_resolves`, `every_contract_names_a_resolvable_ratifying_test`,
`contracts_carry_no_decision_record_prose`. `design_schema_drift`,
`docs_honesty_pin`, `effect_doc_honesty_pin`, `eq_float_noinstance`,
`show_no_instance_e2e` green (Tasks 56).
- [ ] **Step 4: Acceptance-gate grep (recon-undercount countermeasure)**
Run: `grep -rIn 'DESIGN\.md' . --exclude-dir=.git --exclude-dir=target | grep -vE '^\./docs/(journals/|specs/|plans/|roadmap\.md|WhatsNew\.md|journal-archive\.md)|^\./bench/orchestrator-stats/' | grep -v 'design-md-rolesplit' || echo "CLEAN: zero live DESIGN.md references"`
Expected: `CLEAN: zero live DESIGN.md references`. Any line printed
is a missed live reference (code/script/agent/comment) — retarget
it (Task 8 bucket) before the milestone is done. The exclude list
is exactly the spec acceptance-criterion-8 append-only history set;
the `design-md-rolesplit` filter drops this milestone's own
spec/plan/journal self-references.
- [ ] **Step 5: architect_sweeps clean on the final tree**
Run: `bash bench/architect_sweeps.sh; echo "exit=$?"`
Expected: `exit=0` (all five sweeps clean against the migrated
`design/` prose — the honesty discipline survived the move).
---
## Self-review (planner Step 5)
1. **Spec coverage:** Task 1 = §"Concrete code shapes" pin; Tasks
24 = the Appendix relocation map (every `##`/`###`); Task 5 =
§3 test retargets; Task 6 = §2 diagnostics; Task 7 = §3
architect_sweeps; Task 8 = §4 agent/contract rewrites + bucket
(e); Task 9 = §"Clean cut" + acceptance criteria 112. Every
spec section has a task. ✓
2. **Placeholder scan:** no TBD/TODO/"similar to"/"add appropriate".
"Concatenate the Appendix-cited ranges" is an exact executable
instruction (the Appendix is the placeholder-free byte map). ✓
3. **Type/path consistency:** `DATA_MODEL` const name consistent
(Task 5 def ↔ Task 5 anchor-test use); `design/contracts/<x>.md`
paths consistent across Tasks 2/5/6/8 and INDEX.md; ratifier
paths match the grounding-PASS'd INDEX. ✓
4. **Step granularity:** each step is one file-group action /
one Run; the largest (Task 2 Step 3 = 14 files) is one mechanical
move-per-file pass, each file 25 min. ✓
5. **No commit steps:** none. Task 9 uses `git rm` (stages a
deletion in the working tree; the Boss commits the iter). ✓
6. **Pin/replacement contiguity:** Task 2 Step 4 keeps the two
`docs_honesty_pin.rs:70,72` phrases each on one physical line;
Task 6 Step 2 keeps `design/contracts/typeclasses.md` a
whitespace-free contiguous token across the `\`-continuation
(the recurring grep/line-wrap family — scrubbed). ✓
7. **Compile-gate vs deferred-caller:** the only compile-time
coupling is `design_schema_drift.rs` `include_str!`
`docs/DESIGN.md` deletion. Task 5 retargets the `include_str!`
to the already-created `design/contracts/data-model.md` (Task 2)
**before** Task 9 deletes the file. No task's build/test gate
depends on a step a later task performs: Tasks 18 keep
`docs/DESIGN.md` on disk, so every intermediate `cargo build` is
green; Task 9's gate is the first (and only) that requires the
deletion, and by then its sole consumer was moved. The plan is
build-atomic by construction, not by a monster task. ✓
8. **Verification filter strings resolve:** Task 1/2/3/5 use
`--test design_index_pin` / `--test design_schema_drift` /
`--test docs_honesty_pin` / `--test effect_doc_honesty_pin`
(files created/existing — resolve). Task 6 `--test
eq_float_noinstance --test show_no_instance_e2e` resolve
(existing). Task 9 Step 3 uses the **unfiltered** `cargo test
--workspace` (no filter to mis-resolve) + a named-test
expectation. Task 9 Step 4 grep is unfiltered (`-rIn`, no
`--include`) per the recon-undercount countermeasure with an
explicit `CLEAN:` sentinel so "nothing matched" cannot
masquerade. ✓
Plan is placeholder-free and build-atomic. Hand off to `implement`.