Files
AILang/docs/plans/0094-design-md-rolesplit.tidy.md
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
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.
2026-05-28 13:31:31 +02:00

540 lines
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# design-md-rolesplit.tidy — Implementation Plan (audit drift fix)
> **Parent spec:** `docs/specs/0045-design-md-rolesplit.md`
> **Scope authority:** `docs/journals/2026-05-19-audit-design-md-rolesplit.md`
> "Resolution" (committed 2ba5e16) — the architect `[medium]`+`[low]`
> drift fix, 5 orchestrator-decided points.
>
> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement`
> to run this plan. Steps use `- [ ]` checkboxes for tracking.
**Goal:** Close the spirit-vs-letter gap: extract the
faithfully-migrated history/decision-record prose out of the 3
affected contract files into the decision-record journal, fix the
one stale cross-ref, re-scope the honesty sweeps to
`design/contracts/` only, and widen `design_index_pin.rs` clause-3
so the in-code hard gate provably subsumes `architect_sweeps.sh`
Sweep-1 over the contracts scope.
**Architecture:** TDD by gate-first ordering — Task 1 widens
clause-3 and verifies it goes RED against the *current* (un-stripped)
contract files (proving the widened gate catches what the 6 literal
markers missed); Tasks 25 strip the history prose (sentence-level
per recon's contract-residue map) → clause-3 returns GREEN; Task 6
re-scopes the sweep + its lockstep doc; Task 7 is the whole-tree
gate. Build stays green throughout (only one test file's body +
Markdown change; the test compiles, fails at runtime until the strip
lands). The widened clause-3 is a hand-rolled provable superset of
Sweep-1 (no `regex` dep).
**Tech Stack:** Markdown (`design/contracts/*.md`,
`docs/journals/2026-05-19-design-decision-records.md`), one Rust
test (`crates/ailang-core/tests/design_index_pin.rs`),
`bench/architect_sweeps.sh`, `skills/audit/agents/ailang-architect.md`.
**Pinned-byte hazard (recon Cross-references — honor in every strip
task):** each history strip runs adjacent to a `docs_honesty_pin.rs`
verbatim substring pin. `norm()` collapses whitespace but the pinned
run must stay a contiguous token-run:
- `str-abi.md:19` `type-installed; codegen is reserved and not yet shipped``docs_honesty_pin.rs:121-122`
- `typeclasses.md:317` `` `io/print_str` is the only built-in direct-output effect-op `` ← `docs_honesty_pin.rs:125-126`
- `scope-boundaries.md:9` `` `Diverge` is a reserved effect name with no op and no codegen `` ← `docs_honesty_pin.rs:112-113` (over-strip guard — NOT in any edit span; do not touch)
---
## Task 1: widen `design_index_pin.rs` clause-3 (gate-first, RED)
**Files:**
- Modify: `crates/ailang-core/tests/design_index_pin.rs:130-157` (`fn contracts_carry_no_decision_record_prose`)
- [ ] **Step 1: Replace the clause-3 body with the widened, provable-superset matcher**
Replace `fn contracts_carry_no_decision_record_prose` (current
:130-157, the 6-literal-marker `markers` array + `body.contains(m)`
loop) with this. The other 3 clauses + helpers (`root` :10-12,
`read` :14-17, `norm` :19-21, `index_tables` :23-62,
`link_target_exists` :67-81, `design_md_is_gone` :83-90,
`every_index_link_resolves` :92-107,
`every_contract_names_a_resolvable_ratifying_test` :109-128) are
**untouched**.
```rust
#[test]
fn contracts_carry_no_decision_record_prose() {
// clause 3 — the conflation tripwire, widened (design-md-rolesplit.tidy,
// audit Resolution-4) to a FAITHFUL SUPERSET of
// bench/architect_sweeps.sh Sweep-1's history-anchor regex over the
// design/contracts/ scope, PLUS the audit-named decision-record
// phrases (the deliberate widening that closes the case-variance
// dodge the 6 literal markers left open).
//
// Invariant: clause-3 GREEN ⟹ Sweep-1 finds nothing in design/contracts/.
//
// Sweep-1 = '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'
// Faithfulness, per Sweep-1 alternative:
// - Iter / Family / pre- / <n> sketch / 21.g / **Status:
// → sweep1_line_anchor() reproduces each verbatim,
// case-SENSITIVE (grep -E, no -i) — exactly Sweep-1 here,
// never narrower.
// - ^[^/]*2026-NN-NN → date_anchor() reproduces Sweep-1's
// ^[^/]* PATH-EXCLUSION (only the line prefix before the
// first '/' is scanned) and generalises 2026→20NN
// (⊇ Sweep-1, never narrower). A `docs/specs/2026-..`
// citation has '/' before the date ⇒ NOT flagged — faithful
// to Sweep-1, no over-fire on legit present-tense spec
// cross-refs (the iter-.1 plan defect this repairs).
// - PHRASES: literal decision-record idioms (case-insensitive) —
// the deliberate widening that closes the capital-variance
// dodge ("An earlier draft" vs "an earlier draft").
//
// A blanket case-insensitive iter/milestone detector was evaluated
// and REJECTED (audit Resolution-4 corrected): it conflates the
// memory-model rule-names "Iter A"/"Iter B", and ordinary words
// "pre-existing"/"pre-set"/"pre-Boehm"/"pre-tail-call", with
// provenance stamps — unworkable. faithful-Sweep-1 (the capital-I,
// digit-anchored form) already excludes those by construction and
// is confirmed ZERO across every contract file; lowercase
// `(iter <code>)` provenance is removed by the strip tasks, not by
// a fragile hard-gate regex. The load-bearing invariant
// (clause-3 GREEN ⟹ Sweep-1 clean in contracts/) holds with
// sweep1_line_anchor + date_anchor + PHRASES alone.
let dir = root().join("design/contracts");
const PHRASES: &[&str] = &[
"we rejected", "an earlier draft", "earlier draft committed",
"why not other", "was retired", "were retired", "rollback plan",
"previously all", "deliberately deferred", "new-baseline decision",
"amends the above", "out of scope per",
"the journal records when", "unchanged from the original draft",
];
// Sweep-1's non-date alternatives, hand-rolled verbatim, case-SENSITIVE
// (grep -E without -i), on the raw physical line.
fn sweep1_line_anchor(line: &str) -> Option<&'static str> {
let kw_digit = |kw: &str| -> bool {
let mut from = 0;
while let Some(i) = line[from..].find(kw) {
let p = from + i + kw.len();
if line[p..].chars().next().map_or(false, |c| c.is_ascii_digit()) {
return true;
}
from = p;
}
false
};
if kw_digit("Iter ") { return Some("Iter <n>"); }
if kw_digit("Family ") { return Some("Family <n>"); }
if kw_digit("pre-") { return Some("pre-<n>"); }
if line.contains("**Status: ") { return Some("**Status:"); }
if line.contains("21.g") { return Some("21.g"); }
if let Some(s) = line.find(" sketch") {
let pre = line[..s].as_bytes();
if pre.last().map_or(false, u8::is_ascii_alphanumeric)
&& pre.iter().rev()
.take_while(|c| c.is_ascii_alphanumeric())
.any(u8::is_ascii_digit)
{
return Some("<n> sketch");
}
}
None
}
// Sweep-1's `^[^/]*2026-NN-NN`, generalised to 20NN-NN-NN,
// PATH-EXCLUDED: scan only the line prefix BEFORE the first '/'.
fn date_anchor(line: &str) -> Option<String> {
let prefix = line.split('/').next().unwrap_or(line).as_bytes();
for w in prefix.windows(10) {
if w[0] == b'2' && w[1] == b'0'
&& w[2].is_ascii_digit() && w[3].is_ascii_digit()
&& w[4] == b'-' && w[5].is_ascii_digit() && w[6].is_ascii_digit()
&& w[7] == b'-' && w[8].is_ascii_digit() && w[9].is_ascii_digit()
{
return Some(String::from_utf8_lossy(w).into_owned());
}
}
None
}
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 raw = fs::read_to_string(&p).unwrap();
let fname = p.file_name().unwrap().to_string_lossy().into_owned();
let low = norm(&raw).to_lowercase();
for ph in PHRASES {
assert!(
!low.contains(ph),
"clause-3: history phrase {ph:?} in contract {fname:?}"
);
}
for line in raw.lines() {
if let Some(a) = sweep1_line_anchor(line) {
panic!("clause-3: Sweep-1 anchor {a:?} in contract {fname:?}: {line:?}");
}
if let Some(d) = date_anchor(line) {
panic!("clause-3: history date {d:?} in contract {fname:?}: {line:?}");
}
}
}
}
```
- [ ] **Step 2: Verify clause-3 is now RED against the un-stripped tree**
Run: `cargo test -p ailang-core --test design_index_pin contracts_carry_no_decision_record_prose 2>&1 | tail -8`
Expected: **FAIL** — fires on still-present history prose: a PHRASES
hit (e.g. `clause-3: history phrase "deliberately deferred" in
contract "str-abi.md"`) and/or the path-excluded date
(`clause-3: history date "2026-05-13" in contract "str-abi.md"`).
This is the required RED: the widened gate catches what the 6
literal case-sensitive markers missed (the capital-variance dodge).
- [ ] **Step 3: Verify the other 3 clauses + build still GREEN**
Run: `cargo test -p ailang-core --test design_index_pin 2>&1 | grep -E '^test (design_md_is_gone|every_index_link_resolves|every_contract_names) '`
Expected: those 3 still `ok` (only clause-3 RED). `cargo build -p ailang-core --tests 2>&1 | tail -1``Finished` (the new matcher compiles; no new dependency).
---
## Task 2: strip history prose from `design/contracts/0013-typeclasses.md`
**Files:**
- Modify: `design/contracts/0013-typeclasses.md` (:77-78, :182-186, :197-200, :279-289, :291-300, :302-317 — recon's complete scan)
- Modify: `docs/journals/2026-05-19-design-decision-records.md` (append at EOF, after :431)
- [ ] **Step 1: Append the migrated typeclasses history to the decision-record journal**
Append at EOF of `docs/journals/2026-05-19-design-decision-records.md`:
```markdown
## Migrated from design/contracts/ at design-md-rolesplit.tidy (2026-05-19)
History/decision-record prose extracted from contract files by the
post-audit tidy (the `###`-whole relocation in iter .1 dragged these
into contracts; they are relitigation-guard content, not contract).
### typeclasses.md — Prelude-classes evolution
An earlier draft committed to a fixed Prelude (Show/Eq/Ord on the
primitives), but the implementation work to wire `int_to_str` as a
heap-allocated-string runtime primitive proved substantively
separable from the typeclass machinery itself, and the LLM-utility
case for primitive `Show` is weak (LLM-natural form is `int_to_str
x`, not `show x`). Milestone 23 added `Ordering`/`Eq`/`Ord` +
primitive instances + the five helpers; operator routing through
`Eq`/`Ord` and `print`-rewire were held out for bench-rebaseline /
post-mq-dispatcher reasons; `Show` shipped in milestone 24 (iters
24.2/24.3). `MethodNameCollision` was retired at iter mq.3
(cross-class method sharing became structurally legal). The
dedicated `KindMismatch` diagnostic was retired at iter ctt.3
(`BareCrossModuleTypeRef` subsumes it).
```
- [ ] **Step 2: Rewrite the 6 typeclasses.md spans present-tense (recon's contract-residue map)**
Apply each, keeping every present-tense contract fact, dropping the
history clause:
- `:77-78` `Three invariants make this work, all installed in milestone 24's iter 24.3 and worth keeping load-bearing:``Three invariants make this work:`
- `:182-186` drop the parenthetical `(`MethodNameCollision` was retired at iter mq.3. …)` opening; keep the present-tense replacement sentence: `Cross-class method sharing is structurally legal; ambiguity surfaces at the call site via `AmbiguousMethodResolution` or, for class-fn name overlap, via the `class-method-shadowed-by-fn` warning. See §"Method dispatch" below.`
- `:197-200` keep `… is rejected earlier by the canonical-form validator as `BareCrossModuleTypeRef` — `f` is a bare, non-primitive name not declared as a `TypeDef` in the owning module.`; drop `so the dedicated `KindMismatch` diagnostic was retired at iter ctt.3.`
- `:279-289` (the "Milestone 22 ships no built-in Prelude classes / An earlier draft committed to…" paragraph) — delete wholesale (pure decision-record; the present-tense state is the m24 prelude below).
- `:291-300` replace the `Milestone 23 amends the above:` paragraph with present-tense: `The prelude ships the `Ordering` ADT, the `Eq` and `Ord` classes, primitive `Eq Int/Bool/Str` and `Ord Int/Bool/Str` instances, and the five polymorphic free-fn helpers `ne`/`lt`/`le`/`gt`/`ge`. Float has neither `Eq` nor `Ord` instance per §"Float semantics"; a polymorphic helper invoked at Float fires `NoInstance` at typecheck with a Float-aware diagnostic cross-referencing this section.` (drop "remain out of scope per their original substantive reasons (bench rebaseline, milestone-24 dependency…); Show itself ships in milestone 24" — that relitigation is now in the journal.)
- `:302-317` replace the `Milestone 24 amends the above further:` opening with present-tense: `The prelude ships `class Show a where show : (a borrow) -> Str` and primitive `Show Int`, `Show Bool`, `Show Str`, `Show Float` instances. Float **is** included in Show (unlike Eq/Ord) …`**keep verbatim and byte-contiguous** the tail through `:317` `` `io/print_str` is the only built-in direct-output effect-op `` (pinned by `docs_honesty_pin.rs:125-126`) and the `:315` `print_mono_body_shape.rs` cross-reference; drop only `Milestone 24 amends the above further`, `(iter 24.2)`, `shipped in iter 24.3` (→ replace `shipped in iter 24.3 with body` with `with body`).
- [ ] **Step 3: Verify typeclasses.md pin + clause-3 progress**
Run: `cargo test -p ailang-core --test docs_honesty_pin 2>&1 | grep -E 'test result:'`
Expected: PASS (the `io/print_str…` pinned run intact). Run
`grep -n 'io/print_str` is the only built-in direct-output effect-op' design/contracts/0013-typeclasses.md`
Expected: exactly one contiguous match (pin survived).
---
## Task 3: strip history prose from `design/contracts/0011-str-abi.md`
**Files:**
- Modify: `design/contracts/0011-str-abi.md` (:14, :16, :18, :20, :22-23, :74, :38-43, :45-47)
- Modify: `docs/journals/2026-05-19-design-decision-records.md` (append under the tidy section)
- [ ] **Step 1: Append str-abi history to the journal**
Append under the `## Migrated …` section:
```markdown
### str-abi.md — heap-Str builtin provenance + deferred-operator rationale
Builtin iter provenance: `int_to_str`/`bool_to_str`/`float_to_str`/
`str_clone` shipped iter 24.1; `str_concat` shipped iter str-concat
(2026-05-13); the non-escape static-Str lowering pass landed iter
18b, move-tracking partial-drop iter 18d.3. Operator routing through
classes is deliberately deferred — it would migrate every fixture
and re-baseline the bench corpus (a new-baseline decision, not an
iter detail). `Num` is not in milestone 22: class-based numeric
overloading would invoke literal-defaulting which axis-7 excluded.
```
- [ ] **Step 2: Strip the `(iter …)` parentheticals + deferred rationale**
- `:14` `- `int_to_str : (borrow Int) -> Str` (iter 24.1) — decimal rendering` → `- `int_to_str : (borrow Int) -> Str` — decimal rendering`
- `:16` `- `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`.` → drop ` (iter 24.1)`
- `:18` `- `float_to_str : (borrow Float) -> Str` (iter 24.1) —` → drop ` (iter 24.1)`. **Hazard:** `:19` `type-installed; codegen is reserved and not yet shipped.` is pinned (`docs_honesty_pin.rs:121-122`) — keep `:19` byte-identical and contiguous with `:18`'s ``.
- `:20` `- `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh` → drop ` (iter 24.1)`
- `:22-23` `- `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat,\n 2026-05-13) — combines …` → `- `str_concat : (borrow Str, borrow Str) -> Str` — combines two `Str` values into a single owned `Str`.` (collapse the soft-wrap; drop the whole `(iter str-concat, 2026-05-13)` — this is the Sweep-1 exit-1 trip)
- `:74` drop ` (iter 18b)` and ` (iter 18d.3)`; keep the present-tense mechanism sentence.
- `:38-43` keep `` `==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators. Class methods are accessed by name (`eq x y`, `lt x y`, …), not via these operators. `` ; drop `(unchanged from the original draft)` and the whole `Routing operators through classes is deliberately deferred — … new-baseline decision rather than an iter detail.`
- `:45-47` keep `Arithmetic operators (`+`, `-`, `*`, `/`) stay primitive and per-type.`; drop `` `Num` is NOT in milestone 22. `` and `Class-based numeric overloading would invoke literal-defaulting which axis-7 already excluded.`
- [ ] **Step 3: Verify str-abi pin intact**
Run: `grep -n 'type-installed; codegen is reserved and not yet shipped' design/contracts/0011-str-abi.md`
Expected: exactly one contiguous match. Run
`cargo test -p ailang-core --test docs_honesty_pin 2>&1 | grep 'test result:'` → PASS.
---
## Task 4: strip history prose from `design/contracts/0010-scope-boundaries.md`
**Files:**
- Modify: `design/contracts/0010-scope-boundaries.md` (:5-6, :14-17, :36-38)
- Modify: `docs/journals/2026-05-19-design-decision-records.md` (append under the tidy section)
- [ ] **Step 1: Append scope-boundaries history to the journal**
```markdown
### scope-boundaries.md — retired ops + deferral rationale
Polymorphic-fn-as-value is deferred because it would need one
closure-pair global per instantiation. The per-type print ops
`io/print_int`/`io/print_bool`/`io/print_float` were retired in iter
rpe.1 (the polymorphic `print` is the canonical non-Str output path).
```
- [ ] **Step 2: Rewrite the 3 scope-boundaries spans**
- `:5-6` keep `Snapshot of the current boundary.`; delete `Items move out of this list as iterations land; the JOURNAL records when.` (process-meta, not a scope contract; not decision-record either — plain delete, not journal-moved).
- `:14-17` keep `Polymorphic fns must be **directly called** at the use site. Passing a polymorphic fn as a value (`let f = id in f(42)`) is not yet supported.`; drop ` — it would need one closure-pair global per instantiation, deferred.` (rationale → journal, Step 1).
- `:36-38` keep `Effects on function signatures, with `do op(args)` for direct effect ops (`io/print_str`). The polymorphic `print` (§"Polymorphic print") is the canonical output path for non-Str values.`; drop `The per-type print ops `io/print_int`, `io/print_bool`, `io/print_float` were retired in iter rpe.1;` (→ journal).
- [ ] **Step 3: Over-strip guard — the Diverge pin must be untouched**
Run: `grep -n 'Diverge` is a reserved effect name with no op and no codegen' design/contracts/0010-scope-boundaries.md`
Expected: exactly one match at the original location (`:9`,
unedited — it is NOT in any edit span; this guards against
collateral deletion). `cargo test -p ailang-core --test effect_doc_honesty_pin 2>&1 | grep 'test result:'` → PASS.
---
## Task 5: fix the stale cross-ref in `design/contracts/0005-float-semantics.md`
**Files:**
- Modify: `design/contracts/0005-float-semantics.md:100`
- [ ] **Step 1: Repoint the stale "below" direction**
`:99-100` verbatim `Both allocate a fresh heap-Str slab at the call
site (see "Str ABI" below for the dual realisation) and carry
`ret_mode: Own`…`. Replace `(see "Str ABI" below for the dual
realisation)``(see design/contracts/0011-str-abi.md for the dual
realisation)`. (`design/contracts/0011-str-abi.md` exists — confirmed
recon; INDEX row `design/INDEX.md:93`.)
- [ ] **Step 2: Verify float-semantics pin intact + no stale "below"**
Run: `grep -n '"Str ABI" below' design/contracts/0005-float-semantics.md || echo "CLEAN: stale below gone"`
Expected: `CLEAN: stale below gone`. Run
`cargo test -p ail --test eq_float_noinstance 2>&1 | grep 'test result:'` → PASS.
---
## Task 5b: residual lowercase provenance stamps (exhaustive-scan additions)
> The audit's `[medium]` finding was a 3-file spot-check. The
> planning-time exhaustive scan found two more contract files
> carrying lowercase `iter <code>` provenance the audit missed
> (audit Resolution corrected in the same iter). These are spirit
> cleanup — faithful-Sweep-1/clause-3 do NOT trip on them (lowercase,
> no digit-anchored capital `Iter`), so they are not gate-blocking,
> but a contract carrying "iter form-a.1" is exactly the residue this
> milestone exists to remove. Strip + journal-note for completeness.
**Files:**
- Modify: `design/contracts/0009-roundtrip-invariant.md:73`
- Modify: `design/contracts/0002-data-model.md:149,161`
- Modify: `docs/journals/2026-05-19-design-decision-records.md` (append under the tidy section)
- [ ] **Step 1: Append the provenance to the journal**
```markdown
### roundtrip-invariant.md / data-model.md — corpus + loop-recur provenance
The Form-A round-trip corpus was flipped from `.ail.json` to `.ail`
at iter form-a.1. The `loop`/`recur` schema entries were added at
loop-recur iter 1 (strictly additive; pre-existing fixtures hash
bit-identically). Provenance only — the present-tense schema/contract
is the entry shapes themselves.
```
- [ ] **Step 2: Strip the stamps, keep the present-tense contract**
- `roundtrip-invariant.md:73` `… in at least one `.ail` fixture (corpus flipped from `.ail.json` to `.ail` at iter form-a.1). New AST` → drop the parenthetical: `… in at least one `.ail` fixture. New AST` (the invariant is "every variant appears in ≥1 `.ail` fixture"; the flip provenance is journal-class).
- `data-model.md:149` `// loop-recur iter 1: strict iteration block. `binders` declares``// loop: strict iteration block. `binders` declares` (drop `-recur iter 1:` → the schema-comment names the construct, not the iter).
- `data-model.md:161` `// loop-recur iter 1: re-enter the lexically innermost enclosing``// recur: re-enter the lexically innermost enclosing` (this entry is the `recur` schema — name it `recur`, drop the iter stamp).
- Leave `data-model.md:154` `pre-existing fixtures hash bit-identically` untouched — "pre-existing" is an ordinary present-tense word, NOT provenance (faithful-Sweep-1's `pre-[0-9]` does not match it; confirmed).
- [ ] **Step 3: Verify no lowercase `iter <code>` provenance remains in contracts**
Run: `grep -rnoE 'iter [a-z0-9][-a-z0-9.]*[0-9]' design/contracts/ | grep -ivE 'iteration|iterat' || echo "CLEAN: no iter-code provenance"`
Expected: `CLEAN: no iter-code provenance` (the `(iter 24.1)` /
`iter str-concat` / `iter form-a.1` / `loop-recur iter 1` stamps all
removed by Tasks 3 + 5b; `iteration`-word usages excluded). Run
`cargo test -p ailang-core --test design_schema_drift 2>&1 | grep 'test result:'`
then `cargo test -p ailang-surface --test round_trip 2>&1 | grep 'test result:'`
Expected: both PASS — `design_schema_drift` anchors on the JSON
`"t": "loop"` / `"t": "recur"` schema tokens (untouched; only the
prose comment changed), and the round-trip corpus is
content-unchanged.
---
## Task 6: re-scope `architect_sweeps.sh` honesty sweeps + lockstep doc
**Files:**
- Modify: `bench/architect_sweeps.sh:25` (+ header `:5-7`)
- Modify: `skills/audit/agents/ailang-architect.md:77-84`
- [ ] **Step 1: Narrow `DESIGN_GLOB` to `design/contracts`**
`bench/architect_sweeps.sh:25` verbatim
`DESIGN_GLOB="design/contracts design/models"`
`DESIGN_GLOB="design/contracts"`. (Substantive reason, encoded as a
comment on the same line: `# contracts only — design/models is the
narrative tier; "as of milestone N" context is legitimate there`.)
Header `:5-7` prose `design/ prose set (design/contracts +
design/models)``design/contracts (the honesty-bound contract
surface; design/models is the narrative tier, out of honesty-sweep
scope)`. The 5 `run_sweep` regexes (`:46-59`) are **unchanged**; the
exit-code block (`:61-64`) is **unchanged**.
- [ ] **Step 2: Lockstep `ailang-architect.md`**
In `skills/audit/agents/ailang-architect.md` the `:77-84`
history-anchor-regrowth bullet: append one sentence after the
exit-semantics lines: `Sweeps 14 scan `design/contracts/` only —
`design/models/` is the narrative tier (§ reading-list: "context,
not a drift surface"), so a milestone-context phrase there is not a
regrowth.` (`:34` already states models/ is not a drift surface;
this makes the sweep scope explicit and consistent.) No other
edit; Sweep count stays 5 so `docs_honesty_pin.rs:10` `//!` needs no
change.
- [ ] **Step 3: Verify the sweep now scans contracts only**
Run: `bash bench/architect_sweeps.sh; echo "exit=$?"`
Expected: at this point Tasks 25b have stripped the contract
history, so **`exit=0`** and `All five sweeps clean.` (If exit 1
with a `design/contracts/...` match, a history span was missed —
fix it; if the match is `design/models/...` the glob narrowing
didn't apply — re-check `:25`.)
---
## Task 7: whole-tree GREEN gate (milestone-close proof)
**Files:** none (verification only).
- [ ] **Step 1: clause-3 now GREEN, design_index_pin 4/4**
Run: `cargo test -p ailang-core --test design_index_pin 2>&1 | grep -E '^test |test result:'`
Expected: all 4 `ok``contracts_carry_no_decision_record_prose`
flipped RED→GREEN (history stripped), the other 3 unchanged-GREEN.
- [ ] **Step 2: the retargeted pins + E2Es still GREEN**
Run: `cargo test -p ailang-core --test design_schema_drift --test docs_honesty_pin --test effect_doc_honesty_pin 2>&1 | grep 'test result:'`
Expected: 3× PASS. Run
`cargo test -p ail --test eq_float_noinstance --test show_no_instance_e2e 2>&1 | grep 'test result:'`
Expected: 2× PASS.
- [ ] **Step 3: whole workspace GREEN + sweep exit 0 + acceptance grep**
Run: `cargo test --workspace 2>&1 | grep -oE '[0-9]+ passed; [0-9]+ failed' | awk -F'[ ;]' '{p+=$1; f+=$4} END {print "TOTAL passed="p" failed="f}'`
Expected: `failed=0` and `passed` ≥ 646 (the iter .1 baseline; the
widened clause-3 adds no new test count, it replaces a body).
Run: `bash bench/architect_sweeps.sh; echo "exit=$?"`
Expected: `exit=0`.
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' | grep -v 'design_index_pin.rs' || echo "CLEAN"`
Expected: `CLEAN` (no live-ref regression from the tidy edits).
---
## Self-review (planner Step 5)
1. **Spec/Resolution coverage:** Task 1 = Resolution-4 (clause-3 =
faithful-Sweep-1 + path-excluded-date + PHRASES; the
case-insensitive-iter-detector mechanism was found unworkable and
is corrected in the audit Resolution + this plan — it conflated
the "Iter A/B" rule-names and "pre-existing"/"pre-set" with
provenance); Tasks 24 = Resolution-1 (history → journal, the 3
audit-named files); Task 5 = Resolution-2 (stale "below"); Task
5b = Resolution-1 scope correction (the planning-time exhaustive
scan found 2 more contract files with lowercase provenance the
audit's 3-file spot-check missed); Task 6 = Resolution-3 (sweep
re-scope + lockstep); Task 7 = Resolution-5 exit state. All
points covered, scope corrected on evidence. ✓
2. **Placeholder scan:** no TBD/TODO/"similar to". Every strip names
the exact recon line + verbatim before→after text. ✓
3. **Type/path consistency:** `design/contracts/0011-str-abi.md`,
`docs/journals/2026-05-19-design-decision-records.md`,
`contracts_carry_no_decision_record_prose` used identically
across tasks; the journal append section header
`## Migrated … (2026-05-19)` consistent Tasks 2/3/4. ✓
4. **Step granularity:** each step is one file's edit-set or one
Run; the largest (Task 2 Step 2, 6 spans) is one
mechanical-per-span pass, 25 min. ✓
5. **No commit steps:** none. ✓
6. **Pin/replacement contiguity:** the three `docs_honesty_pin.rs`
pinned runs (`str-abi.md:19`, `typeclasses.md:317`,
`scope-boundaries.md:9`) are called out as hazards in the header
AND verified by an explicit `grep -n` contiguity check in Tasks
2/3/4 Step 3 + the over-strip guard in Task 4 Step 3. The
widened clause-3 matcher (Task 1) is a single self-contained
function body — no cross-task substring split. ✓
7. **Compile-gate vs deferred-caller:** the only Rust edit is one
self-contained test fn body (Task 1); it compiles standalone
(no signature change, no caller, no new dependency — hand-rolled
matcher). Every Run gate is satisfiable at its task: Task 1 Step
2 expects RED (pre-strip), Task 7 expects GREEN (post-strip);
no gate depends on a later task's code. Build green throughout
(test compiles; only its runtime assertion is RED until the
strip lands). ✓
8. **Verification filter strings resolve:** `-p ailang-core --test
design_index_pin` / `design_schema_drift` / `docs_honesty_pin` /
`effect_doc_honesty_pin`, `-p ail --test eq_float_noinstance` /
`show_no_instance_e2e`, and `-p ailang-surface --test round_trip`
(Task 5b — corrected to its real crate `ailang-surface`, not
`ail`) all exist (iter .1 created/retargeted them). The clause-3
filter `contracts_carry_no_decision_record_prose` is the exact fn
name. Task 7 Step 3 uses the **unfiltered** `cargo test
--workspace` with a numeric `failed=0` + `passed≥646` assertion
(cannot masquerade as "nothing ran"). The `grep`/`bash
architect_sweeps` gates have explicit `CLEAN`/`exit=0` sentinels;
Task 5b Step 3's `grep … || echo "CLEAN…"` likewise. ✓
Plan is placeholder-free; clause-3 is a hand-rolled **faithful**
Sweep-1 superset — faithful-Sweep-1 line anchors (case-sensitive,
digit-anchored — confirmed ZERO across all contracts) + Sweep-1's
path-excluded date + the audit-named decision-record phrases; **no
`regex` dep, no blanket iter detector** (the rejected mechanism).
The audit Resolution-4 is corrected in lockstep. Hand off to
`implement`.