All 176 files in the four accumulating directories now use a zero-padded 4-digit counter prefix that reflects creation order (`NNNN-slug.md`). The counter is assigned per directory in strict git-log creation order; ties broken alphabetically by original name. The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is dropped — the date is recoverable from git log and the counter carries the ordering. A file's counter is stable for the life of the file: never reassigned, never reused, never compacted. Deleted files retire their counter; subsequent files do not fill the gap. This is the property that lets cross-references stay literal — refs use the full filename including the counter (`design/contracts/0007-honesty-rule.md`) so they grep cleanly and resolve directly without a glob step. 313 cross-references updated across .md/.rs/.toml/.c/.json files (test pins, include_str! paths, design-INDEX entries, baseline notes, runtime C comments, inter-contract markdown links incl. bare basename and `../models/foo.md` forms). CLAUDE.md gets a new "File-naming convention" section spelling out the rule and rationale. skills/brainstorm/SKILL.md and skills/planner/SKILL.md updated so new spec/plan creation produces counter-prefixed names from the start. The full test suite (cargo test --workspace) passes.
34 KiB
DESIGN.md → design/ role-split — Implementation Plan (iter 1, the whole milestone)
Parent spec:
docs/specs/0045-design-md-rolesplit.md(incl. the authoritative Appendix — Boss-adjudicated relocation map; grounding-check PASS ×2).For agentic workers: REQUIRED SUB-SKILL: use
skills/implementto 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/0002-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:A–B" 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
//! 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/0045-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-xrefare 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:6–18 (## Goal), :19–55
(## Project ecosystem intro), :83–92 (## 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:93–164authoring-surface.md← :230–240 + :268–297 + :298–331 (the three contract###s of Decision 6 per Appendix; :241–243 and all### Why…/### …does not do/### Implementation outline/### Form refinements/### Empirical addendumgo to Task 4)roundtrip-invariant.md← :2113–2208 (whole block, 3###s kept)memory-model.md← :1123–1150 + :1151–1312 + :1313–1351 + :1394–1412- :1413–1509 (the five binding
###s per Appendix)
- :1413–1509 (the five binding
data-model.md← :2377–2615 (whole## Data model+ 4###s)float-semantics.md← :2700–2838 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← :1578–1639 + :1753–1823 + :1824–1855 + :1856–1917- :1918–1964 + :2001–2042 (the six contract
###s of Decision 11 per Appendix)
- :1918–1964 + :2001–2042 (the six contract
tail-calls.md← :731–787 (whole## Decision 8)frozen-value-layout.md← :2334–2376honesty-rule.md← rewritten (next step — do NOT raw-move :56–82)embedding-abi.md← :2266–2333str-abi.md← :2043–2088 (### Heap-Str primitives) + the**Str ABI.**paragraph lifted from :2802scope-boundaries.md← :2839–3020 (## What is not (yet) supported)verification.md← :2687–2699
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/0007-honesty-rule.md
Do NOT raw-move DESIGN.md:56–82. 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):
# 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← :788–852 + :853–913 + :914–977 + :1015–1122- :1378–1393 (Decision 9/10 narrative
###s per Appendix)
- :1378–1393 (Decision 9/10 narrative
-
typeclasses.md← :1536–1577 + :1640–1752 (Decision 11 narrative) -
effects.md← :190–204 (Decision 3 — theeffect_doc_honesty_pin.rspresent-anchors live here; that pin's read retargets to this file in Task 5) -
authoring-surface.md← :332–471 + :590–674 (### Candidate notations+### Form (B)— thedocs_honesty_pin.rsForm-B placeholder present-anchor retargets here in Task 5) -
pipeline.md← :2616–2662 (## Pipeline) + :2663–2686 (## 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: :165–176, :177–189, :205–213, :214–229, :241–243, :245–267,
:472–486, :487–519, :520–536, :537–589, :675–730, :978–1014,
:1352–1377, :1510–1519, :1520–1535, :1965–2000, :2089–2112, plus
any genuine "why" sentence extracted from the source-link ##s
(## Mangling scheme :2209–2218, ## Convention: qualified cross-module references :2219–2236, ## Env construction
:2237–2265 — per Appendix, behaviour sentences are dropped as
code-is-SoT; only rationale sentences move here).
Header:
# 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(+ removedata_model_section28–37 anddata_model_section_is_bounded411–430; repoint 7 anchor tests) -
Modify:
crates/ailang-core/tests/docs_honesty_pin.rs(theread("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:
- const DESIGN_MD: &str = include_str!("../../../docs/DESIGN.md");
+ const DATA_MODEL: &str = include_str!("../../../design/contracts/0002-data-model.md");
Delete fn data_model_section() (lines 28–37) and
fn data_model_section_is_bounded() (lines 411–430). 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/0002-data-model.md".
- Step 2: Retarget
docs_honesty_pin.rsper-sentence home
The read() helper (15–19) and norm() (24–26) 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) anddesign_md_has_no_doc_archaeology(:51): absent-pins span Boehm/float/typeclasses/effects/Form-B — read the concatenation ofdesign/models/0004-rc-uniqueness.md,design/contracts/0005-float-semantics.md,design/contracts/0013-typeclasses.md,design/models/0002-effects.md,design/models/0001-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"→ readdesign/contracts/0007-honesty-rule.md; the Form-B placeholder anchor (:90 area) →design/models/0001-authoring-surface.md; the remaining present-anchors → their Appendix home file. -
form_a_scalar_param_carveout_present_and_old_rule_absent(:104–137): theFORM_A_SPECread is unchanged; theread("docs/DESIGN.md")at :112 →design/contracts/0003-embedding-abi.md(the:135pinExport parameters are written **bare**…lives in## Embedding ABI, Appendix → embedding-abi.md). -
prose_roundtrip_md_has_no_wunschdenken(:94–101): readsdocs/PROSE_ROUNDTRIP.md— unchanged. -
Step 3: Retarget
effect_doc_honesty_pin.rs:21
design_md_effect_prose_is_true (:19–34), line 21:
- let d = read("docs/DESIGN.md");
+ let d = norm(&[read("design/models/0002-effects.md"),
+ read("design/contracts/0010-scope-boundaries.md")].join("\n"));
(Decision 3 effect prose → models/0002-effects.md; the
"the built-in IO and Diverge ops" absent-pin's home
## What is not (yet) supported → contracts/0010-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 2–3; 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/0002-data-model.md).
Task 6: retarget the 2 diagnostics + 2 E2Es (lockstep)
Files:
-
Modify:
crates/ailang-check/src/lib.rs(Float branch ~872–876, Show branch ~885–893) -
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:
- orderability per IEEE-754); see DESIGN.md §\"Float semantics\".",
+ orderability per IEEE-754); see design/contracts/0005-float-semantics.md.",
- Step 2: Retarget the Show-branch diagnostic (contiguity-safe)
crates/ailang-check/src/lib.rs Show branch. The path
design/contracts/0013-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:
- the prelude; see DESIGN.md §\"Prelude (built-in) \
- classes\". User types declare their own \
+ the prelude; see design/contracts/0013-typeclasses.md. \
+ User types declare their own \
(Runtime string becomes …the prelude; see design/contracts/0013-typeclasses.md. User types declare their own … — the path is one contiguous run; the E2E contains("design/contracts/0013-typeclasses.md") holds.)
- Step 3: Retarget the two E2E assertions (lockstep)
crates/ail/tests/eq_float_noinstance.rs lines 41–42:
- 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/0005-float-semantics.md"),
+ "expected NoInstance message to cross-reference the float-semantics contract, got: {:?}",
crates/ail/tests/show_no_instance_e2e.rs lines 47–48:
- 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/0013-typeclasses.md"),
+ "expected design/contracts/0013-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 5run_sweepscan target) -
Step 1: Repoint the spine + guard
- DESIGN="docs/DESIGN.md"
+ INDEX="design/INDEX.md"
+ DESIGN_GLOB="design/contracts design/models"
Guard (25–28):
- 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):
- 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/0004-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/0004-feature-acceptance.md). CLAUDE.md: :45
code-layout table row, the ## Roles of docs/DESIGN.md … heading
and its paragraph (:199–212) → 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/0006-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/0002-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/0009-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/0013-typeclasses.md), e2e.rs:2843 (→
design/contracts/0011-str-abi.md), eq_ord_e2e.rs:89,110 (→ mangling
source-link), embed/record_roundtrip.c:2, embed/tick_roundtrip.c:6
(→ design/contracts/0006-frozen-value-layout.md);
examples/fieldtest/floats_3_safe_division.ail:19,
floats_4_float_to_str_reach.ail:6 (→
design/contracts/0005-float-semantics.md); runtime/rc.c:41 (→
design/contracts/0006-frozen-value-layout.md), runtime/str.c:172,198
(→ design/contracts/0011-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/0002-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 5–6).
- 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)
- Spec coverage: Task 1 = §"Concrete code shapes" pin; Tasks
2–4 = 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 1–12. Every spec section has a task. ✓ - 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). ✓
- Type/path consistency:
DATA_MODELconst name consistent (Task 5 def ↔ Task 5 anchor-test use);design/contracts/<x>.mdpaths consistent across Tasks 2/5/6/8 and INDEX.md; ratifier paths match the grounding-PASS'd INDEX. ✓ - 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 2–5 min. ✓
- No commit steps: none. Task 9 uses
git rm(stages a deletion in the working tree; the Boss commits the iter). ✓ - Pin/replacement contiguity: Task 2 Step 4 keeps the two
docs_honesty_pin.rs:70,72phrases each on one physical line; Task 6 Step 2 keepsdesign/contracts/0013-typeclasses.mda whitespace-free contiguous token across the\-continuation (the recurring grep/line-wrap family — scrubbed). ✓ - Compile-gate vs deferred-caller: the only compile-time
coupling is
design_schema_drift.rsinclude_str!↔docs/DESIGN.mddeletion. Task 5 retargets theinclude_str!to the already-createddesign/contracts/0002-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 1–8 keepdocs/DESIGN.mdon disk, so every intermediatecargo buildis 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. ✓ - 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_e2eresolve (existing). Task 9 Step 3 uses the unfilteredcargo 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 explicitCLEAN:sentinel so "nothing matched" cannot masquerade. ✓
Plan is placeholder-free and build-atomic. Hand off to implement.