832375f2ac
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.
540 lines
30 KiB
Markdown
540 lines
30 KiB
Markdown
# 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 2–5 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 1–4 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 2–5b 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 2–4 = 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, 2–5 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`.
|