diff --git a/docs/journals/2026-05-19-audit-design-md-rolesplit.md b/docs/journals/2026-05-19-audit-design-md-rolesplit.md index dcbf9ec..e6392ba 100644 --- a/docs/journals/2026-05-19-audit-design-md-rolesplit.md +++ b/docs/journals/2026-05-19-audit-design-md-rolesplit.md @@ -83,13 +83,21 @@ milestone. iteration** `design-md-rolesplit.tidy`. Orchestrator-decided scope (the architect named the gap; the clause-3-widening decision is mine): - 1. Move the decision-record/history prose out of - `design/contracts/{typeclasses,str-abi,scope-boundaries}.md` - into `docs/journals/2026-05-19-design-decision-records.md` - (append — same milestone's relitigation archive). Each removed - history sentence is replaced by its present-tense contract - equivalent (e.g. the builtin *signature* is the contract; the - `(iter …)` stamp is journal metadata). + 1. Move the decision-record/history prose out of the affected + contract files into + `docs/journals/2026-05-19-design-decision-records.md` (append — + same milestone's relitigation archive). Each removed history + sentence is replaced by its present-tense contract equivalent + (the builtin *signature* is the contract; the `(iter …)` stamp + is journal metadata). **Scope corrected on planning-time + evidence (see amendment below):** the architect's `[medium]` + was a 3-file spot-check + (`typeclasses`/`str-abi`/`scope-boundaries`); the exhaustive + plan-recon + Boss verification scan found two more contract + files carrying lowercase `iter ` provenance the + spot-check missed — `roundtrip-invariant.md:73` ("at iter + form-a.1") and `data-model.md:149,161` ("loop-recur iter 1:"). + The true strip set is **5 contract files**. 2. Fix `float-semantics.md` stale-direction cross-ref → `(see design/contracts/str-abi.md)`. 3. **Re-scope the `architect_sweeps.sh` honesty sweeps from @@ -102,19 +110,60 @@ milestone. `ailang-architect.md` + any sweep-scope doc in lockstep. 4. **Widen `design_index_pin.rs` clause-3** so, over the `design/contracts/` scope, it *subsumes* `architect_sweeps.sh` - Sweep-1's history-anchor regex (case-insensitive; iter-code - and ISO-date regexes; "earlier draft", "amends the above", - "was retired", "deliberately deferred", "new-baseline - decision", "milestone NN"). Invariant established: + Sweep-1's history-anchor regex. Invariant established: **clause-3 GREEN ⟹ Sweep-1 finds nothing in `contracts/`** — - the in-code hard gate enforces the spirit; the advisory can - then only legitimately fire on `models/` (now out of its - scope). This permanently closes the spirit-vs-letter gap the - literal 6-marker list left open. - 5. Exit state: `architect_sweeps.sh` exit 0; widened - `design_index_pin.rs` clause-3 GREEN; whole `cargo test - --workspace` GREEN; the decision-record journal grows; no new - contract carries history residue. + the in-code hard gate enforces the spirit; the advisory then + only legitimately fires on `models/` (out of its scope after + point 3). This permanently closes the spirit-vs-letter gap the + literal 6-marker list left open. **Mechanism corrected on + planning-time evidence (see amendment below):** the original + "case-insensitive iter-code regex" sketch is *unworkable* and + is rejected — a blanket case-insensitive `iter`/`milestone` + detector conflates the memory-model rule-names "Iter A"/"Iter + B" and the ordinary words "pre-existing"/"pre-set"/"pre-Boehm" + with provenance, over-firing on legitimate present-tense + contract prose. The workable design: clause-3 = a **faithful + reproduction** of Sweep-1's actual alternatives + (case-*sensitive*, digit-anchored — confirmed ZERO across all + contracts) + Sweep-1's `^[^/]*` **path-excluded** date + (so `docs/specs/2026-…` citations are not flagged) + the + audit-named decision-record **phrases** (case-insensitive — the + deliberate widening that closes the capital-variance dodge). + The lowercase `(iter )` provenance is removed by the + strip tasks (point 1), not by a fragile hard-gate regex; the + invariant holds without the rejected detector. + 5. Exit state: `architect_sweeps.sh` exit 0; the widened + (faithful-superset) `design_index_pin.rs` clause-3 GREEN; whole + `cargo test --workspace` GREEN; the decision-record journal + grows; no contract carries history residue. + +### Resolution amendment (2026-05-19, planning-time evidence) + +The tidy plan's gate-first Task 1 (RED-verification) + Boss +independent verification surfaced two defects in this Resolution as +first written — caught exactly where the planner→Boss loop is +designed to catch them, before any contract byte moved: + +- **Mechanism (point 4):** "case-insensitive iter-code regex" is + unworkable (conflates rule-names + ordinary words with + provenance). Replaced by the faithful-Sweep-1 + path-excluded-date + + phrases design above. The load-bearing invariant (clause-3 ⟹ + Sweep-1 clean) is *preserved* — it never required the blanket + detector; faithful-Sweep-1 + phrases suffices and faithful-Sweep-1 + is confirmed already ZERO across every contract. +- **Scope (point 1):** the `[medium]` was a 3-file spot-check; the + exhaustive scan found `roundtrip-invariant.md` + `data-model.md` + also carry lowercase provenance. The strip set is 5 files (the + plan's Task 5b covers the 2 additions; they are spirit-cleanup, + not gate-blocking — neither Sweep-1 nor the corrected clause-3 + trips on lowercase `iter form-a.1`, so the invariant/gate close + regardless; stripping them is the audit's *spirit* fully honored). + +This amendment is the upstream-artifact correction the discipline +mandates over patching the plan a third time (the "two+ defects in +one iteration ⇒ the upstream artifact is wrong" rule). The plan +`docs/plans/design-md-rolesplit.tidy.md` is re-derived consistently +with it. Milestone closes after the tidy iteration lands Boss-verified. No fieldtest (zero authoring-surface change — the only author-facing diff --git a/docs/plans/design-md-rolesplit.tidy.md b/docs/plans/design-md-rolesplit.tidy.md index 105355b..61dde2e 100644 --- a/docs/plans/design-md-rolesplit.tidy.md +++ b/docs/plans/design-md-rolesplit.tidy.md @@ -61,60 +61,92 @@ loop) with this. The other 3 clauses + helpers (`root` :10-12, ```rust #[test] fn contracts_carry_no_decision_record_prose() { - // clause 3 — the conflation tripwire, widened (design-md-rolesplit.tidy) - // to a PROVABLE SUPERSET of bench/architect_sweeps.sh Sweep-1's - // history-anchor regex over the design/contracts/ scope. + // 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 established by this milestone: clause-3 GREEN ⟹ - // Sweep-1 finds nothing in design/contracts/. + // Invariant: clause-3 GREEN ⟹ Sweep-1 finds nothing in design/contracts/. // - // Subsumption proof (each Sweep-1 alternative ⊆ a check below): - // 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' - // - "Iter " / "Family " / "21.g" / " sketch" - // ⊆ iter_code_token() (any "iter"/"family"/"milestone" - // token followed by a code, case-insensitive) - // - "2026-NN-NN" ⊆ iso_date() (any 20NN-NN-NN) - // - "**Status: " / "pre-" ⊆ PHRASES (literal, ci) - // Broader than Sweep-1 by construction; never narrower. + // 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- / 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 )` 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"); - // case-insensitive literal decision-record / history phrases 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", "**status:", "sketch", + "amends the above", "out of scope per", "the journal records when", "unchanged from the original draft", ]; - // a token that looks like an iter / milestone / family provenance code: - // a keyword followed (within 2 words) by an alnum/dot code or a digit. - fn iter_code_token(low: &str) -> Option { - for kw in ["iter ", "milestone ", "family ", "axis-", "pre-"] { + // 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) = low[from..].find(kw) { + while let Some(i) = line[from..].find(kw) { let p = from + i + kw.len(); - let tail: String = low[p..].chars().take(12).collect(); - // "iter 24.1", "iter str-concat,", "milestone 22", - // "family 18", "axis-7", "pre-22a" - if tail.chars().next().map_or(false, |c| { - c.is_ascii_alphanumeric() - }) && tail.chars().take(3).any(|c| { - c.is_ascii_digit() || c == '-' || c == '.' - }) { - return Some(format!("{kw}{tail}")); + 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 "); } + if kw_digit("Family ") { return Some("Family "); } + if kw_digit("pre-") { return Some("pre-"); } + 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(" sketch"); + } } None } - // an ISO-8601-ish date: 20NN-NN-NN - fn iso_date(b: &[u8]) -> Option { - for w in b.windows(10) { + // 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 { + 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() @@ -132,24 +164,22 @@ fn contracts_carry_no_decision_record_prose() { 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/decision-record phrase {:?} in contract {:?}", - ph, p.file_name().unwrap() + "clause-3: history phrase {ph:?} in contract {fname:?}" ); } - assert!( - iter_code_token(&low).is_none(), - "clause-3: iter/milestone provenance code {:?} in contract {:?}", - iter_code_token(&low).unwrap(), p.file_name().unwrap() - ); - assert!( - iso_date(raw.as_bytes()).is_none(), - "clause-3: ISO date {:?} in contract {:?} (history anchor)", - iso_date(raw.as_bytes()).unwrap(), p.file_name().unwrap() - ); + 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:?}"); + } + } } } ``` @@ -157,11 +187,12 @@ fn contracts_carry_no_decision_record_prose() { - [ ] **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** — the assertion fires on the still-present history -prose (e.g. `clause-3: ISO date "2026-05-13" in contract "str-abi.md"` -or `iter/milestone provenance code "iter 24.1…"`). This is the -required RED: the widened gate correctly catches what the 6 literal -markers missed. +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** @@ -324,6 +355,57 @@ Expected: `CLEAN: stale below gone`. Run --- +## 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 ` 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/roundtrip-invariant.md:73` +- Modify: `design/contracts/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 ` 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:** @@ -358,7 +440,7 @@ change. - [ ] **Step 3: Verify the sweep now scans contracts only** Run: `bash bench/architect_sweeps.sh; echo "exit=$?"` -Expected: at this point Tasks 2–5 have stripped the contract +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 @@ -397,11 +479,18 @@ 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 - widen); Tasks 2–4 = Resolution-1 (history → journal, 3 files); - Task 5 = Resolution-2 (stale "below"); Task 6 = Resolution-3 - (sweep re-scope + lockstep); Task 7 = Resolution-5 exit state. - All 5 points covered. ✓ +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/str-abi.md`, @@ -428,16 +517,23 @@ Expected: `CLEAN` (no live-ref regression from the tidy edits). 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:** `--test +8. **Verification filter strings resolve:** `-p ailang-core --test design_index_pin` / `design_schema_drift` / `docs_honesty_pin` / - `effect_doc_honesty_pin` / `eq_float_noinstance` / - `show_no_instance_e2e` 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. ✓ + `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; the widened clause-3 is a hand-rolled -provable Sweep-1 superset (no `regex` dep). Hand off to `implement`. +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`.