From c41e0e5a9e48fd0099eda8c286f10ec7a30e8e8a Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 16 May 2026 13:18:20 +0200 Subject: [PATCH] tidy setup: retire effect-op-arg-modes bundle; split out effect-doc-honesty + AILang-code spec discipline The effect-op-arg-modes brainstorm spec bundled a real, self-contained DESIGN.md honesty fix with speculative build-ahead infra (an EffectOpSig arg-mode field whose only consumer was a synthetic test op). Recon had already destroyed the "effect-handler infrastructure" premise: the effect raise/declare/subset/propagation machinery is fully generic over any effect string; the only real prerequisite was one struct field, not a milestone. The user rejected the bundle as incoherent and the build-ahead half as the iteration-discipline trap repeated. - Retire the uncommitted effect-op-arg-modes spec (deleted from the working tree; never committed). - roadmap: drop the false "sub-ms-2 = effect-handler infrastructure" framing; mark Stateful-islands sub-milestone sequencing as UNDER RE-THINK with the user (no !Mut planning until that conversation); add the split-out effect-doc-honesty as an in-flight P2 [~] todo. - plan docs/plans/2026-05-16-effect-doc-honesty.md: placeholder-free tidy plan (the three DESIGN.md fictions + form_a.md + main.rs + a new doc-presence pin) over recon-mapped exact byte sites. - brainstorm SKILL: specs and design talk LEAD with the AILang (.ail) program that should work (= the feature-acceptance clause-1 evidence); the Rust implementation shape is secondary, never a substitute; a no-surface infra milestone still shows concrete .ail (often a must-fail fixture). Codifies user feedback from this session. --- docs/plans/2026-05-16-effect-doc-honesty.md | 356 ++++++++++++++++++++ docs/roadmap.md | 63 +++- skills/brainstorm/SKILL.md | 59 ++++ 3 files changed, 471 insertions(+), 7 deletions(-) create mode 100644 docs/plans/2026-05-16-effect-doc-honesty.md diff --git a/docs/plans/2026-05-16-effect-doc-honesty.md b/docs/plans/2026-05-16-effect-doc-honesty.md new file mode 100644 index 0000000..f033f67 --- /dev/null +++ b/docs/plans/2026-05-16-effect-doc-honesty.md @@ -0,0 +1,356 @@ +# Effect-prose documentation-honesty tidy — Implementation Plan + +> **Parent:** no spec — this is a **tidy iteration** (documentation +> drift correction). Scope authority is the `docs/roadmap.md` P2 +> `[~] [todo]` "DESIGN.md effect-prose is fiction" (split out +> 2026-05-16 from the retired effect-op-arg-modes bundle). Facts +> triple-verified by three prior `ailang-grounding-check` PASSes; +> exact byte sites mapped by `ailang-plan-recon` 2026-05-16. +> +> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` +> to run this plan. Steps use `- [ ]` checkboxes. + +**Goal:** Make three false effect-system claims in the project's +documentation true, in lockstep with their two satellite mentions, +guarded by one new doc-presence pin. No language, checker, or +codegen change. + +**Architecture:** Pure documentation edits across four files +(`docs/DESIGN.md`, `crates/ailang-core/src/ast.rs` doc-comment, +`crates/ailang-core/specs/form_a.md`, `crates/ail/src/main.rs` +internal CONTRACT string) plus one new regression pin +(`crates/ailang-core/tests/effect_doc_honesty_pin.rs`). Recon +confirmed no existing drift/coverage test scans the effect-prose +region, so the only RED risk is the new pin, by construction; it is +authored first (TDD: RED before the corrections, GREEN after). + +**Tech Stack:** `ailang-core` (doc-comment + form_a.md + the new +integration test), `ail` (main.rs CONTRACT string), `docs/DESIGN.md`. + +--- + +## Files this plan creates or modifies + +- Create: `crates/ailang-core/tests/effect_doc_honesty_pin.rs` — one + test asserting corrected strings present + false strings absent + across the four edited files. +- Modify: `docs/DESIGN.md:171-173` — Decision 3 effect-row + Diverge + claims. +- Modify: `docs/DESIGN.md:2722` — "What is not (yet) supported" + Diverge fragment. +- Modify: `crates/ailang-core/src/ast.rs:438-439` — `Term::Do` + doc-comment. +- Modify: `crates/ailang-core/specs/form_a.md:226` and `:356-358` — + the two Diverge mentions. +- Modify: `crates/ail/src/main.rs:288-290` — merge-prose CONTRACT + effect-annotation example. + +--- + +## Task 1: The doc-presence pin (RED first) + +**Files:** +- Create: `crates/ailang-core/tests/effect_doc_honesty_pin.rs` + +- [ ] **Step 1: Write the pin** + +Robust-substring strategy (single-line fragments only — no +cross-line-wrap matches; the iter-revert journal documented that a +wrap-spanning grep fragment silently mismatches). Each of the four +files is read at runtime via `CARGO_MANIFEST_DIR`-relative paths +(the pattern the F1/F4 sibling `design_schema_drift.rs:434-449` +uses). + +```rust +//! Regression pin: the effect-system documentation must stay true. +//! +//! Three load-bearing effect claims were fiction until the +//! 2026-05-16 effect-doc-honesty tidy: a non-existent +//! row-polymorphic effect row, `Diverge` advertised as a wired-up +//! built-in op (zero code in any crate), and a `Term::Do` +//! doc-comment naming a non-existent link-time effect-handler +//! table. This pin fails if any of the fiction strings reappears +//! or any corrected anchor is dropped. + +use std::fs; + +fn read(rel: &str) -> String { + let p = concat!(env!("CARGO_MANIFEST_DIR"), "/../../"); + fs::read_to_string(format!("{p}{rel}")) + .unwrap_or_else(|e| panic!("read {rel}: {e}")) +} + +#[test] +fn design_md_effect_prose_is_true() { + let d = read("docs/DESIGN.md"); + // fiction absent + assert!(!d.contains("row-polymorphic"), + "DESIGN.md: the effect row is NOT row-polymorphic (no EffectRow / row var exists)"); + assert!(!d.contains("`IO` and `Diverge` (for infinite"), + "DESIGN.md: `Diverge` is not a wired-up MVP effect"); + assert!(!d.contains("the built-in IO and Diverge ops"), + "DESIGN.md §What-is-not-supported: there is no built-in Diverge op"); + // corrected anchors present + assert!(d.contains("flat, unordered, closed set of effect names"), + "DESIGN.md Decision 3 must describe the real (flat set-equality) effect model"); + assert!(d.contains("`Diverge` (for non-termination) is reserved"), + "DESIGN.md Decision 3 must state Diverge is reserved/unimplemented"); +} + +#[test] +fn term_do_doc_comment_is_true() { + let a = read("crates/ailang-core/src/ast.rs"); + assert!(!a.contains("resolved against the effect-handler table at link time"), + "ast.rs Term::Do: there is no effect-handler table and no link-time resolution"); + assert!(a.contains("resolved at typecheck against `Env::effect_ops`"), + "ast.rs Term::Do doc must describe the real resolution mechanism"); +} + +#[test] +fn form_a_spec_effect_names_are_true() { + let f = read("crates/ailang-core/specs/form_a.md"); + assert!(!f.contains("`Diverge` for `diverge/*`"), + "form_a.md: there is no `diverge/*` op namespace"); + assert!(!f.contains("currently `IO` and `Diverge`"), + "form_a.md: `Diverge` is reserved, not a currently-usable effect"); + assert!(f.contains("The only built-in effect op is `io/print_str`"), + "form_a.md rule 3 must name the real single built-in effect op"); +} + +#[test] +fn merge_prose_contract_example_is_true() { + let m = read("crates/ail/src/main.rs"); + assert!(!m.contains("`(effects Diverge)`"), + "main.rs CONTRACT: do not use a non-existent effect as the example"); +} +``` + +- [ ] **Step 2: Run — expect RED** + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin` +Expected: FAIL — `design_md_effect_prose_is_true` (and the others) +panic on the still-present fiction strings / still-absent corrected +anchors. + +--- + +## Task 2: `docs/DESIGN.md` corrections + +**Files:** +- Modify: `docs/DESIGN.md:170-173`, `docs/DESIGN.md:2722` + +- [ ] **Step 1: Decision 3 (lines 170-173)** + +Replace exactly: + +``` +The default is total, pure functions. Effects are declared as a set in the +function type: `(Int) -> Int ![IO]`. The effect set is row-polymorphic +(`![IO | r]`). In the MVP only the effects `IO` and `Diverge` (for infinite +loops) are wired up. +``` + +with: + +``` +The default is total, pure functions. Effects are declared as a set in the +function type: `(Int) -> Int ![IO]`. The effect set is a flat, unordered, +closed set of effect names, unified by set-equality — there is no effect +row variable; a signature lists exactly the effects its body may perform. +In the MVP only the effect `IO` is wired up (its sole op is `io/print_str`). +`Diverge` (for non-termination) is reserved as an effect name but is +unimplemented — no op, no codegen, no checker injection — in the same +sense Decision 4 reserves refinements. +``` + +(Lines 168-169 header/blank and 175-176 stay untouched.) + +- [ ] **Step 2: "What is not (yet) supported" (line 2722)** + +Replace exactly: + +``` +- No effect handlers — only the built-in IO and Diverge ops. +``` + +with: + +``` +- No effect handlers — only the built-in `IO` op (`io/print_str`). + `Diverge` is a reserved effect name with no op and no codegen. +``` + +- [ ] **Step 3: Run the pin — `design_md_effect_prose_is_true` GREEN** + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin design_md_effect_prose_is_true` +Expected: PASS. + +--- + +## Task 3: `crates/ailang-core/src/ast.rs` `Term::Do` doc-comment + +**Files:** +- Modify: `crates/ailang-core/src/ast.rs:438-439` + +- [ ] **Step 1: Replace the two doc lines** + +Replace exactly: + +``` + /// Effect operation invocation (e.g. `do print "hi"`). The `op` is + /// resolved against the effect-handler table at link time. +``` + +with: + +``` + /// Effect operation invocation (e.g. `do io/print_str "hi"`). The + /// `op` is resolved at typecheck against `Env::effect_ops` (an + /// `IndexMap`) and lowered by a literal + /// `match` in codegen (`lower_effect_op`); there is no + /// effect-handler table and no link-time resolution. +``` + +(Lines 440-441 — blank `///` and the Iter 14e cross-ref — and the +`Do { ... }` variant body 442-447 stay untouched.) + +- [ ] **Step 2: Run the pin — `term_do_doc_comment_is_true` GREEN** + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin term_do_doc_comment_is_true` +Expected: PASS. + +--- + +## Task 4: `crates/ailang-core/specs/form_a.md` Diverge mentions + +**Files:** +- Modify: `crates/ailang-core/specs/form_a.md:226`, `:356-358` + +- [ ] **Step 1: Line 226 (valid-effect-name enumeration)** + +Replace exactly: + +``` +`EFFECT-NAME` is a bare identifier — currently `IO` and `Diverge`. +``` + +with: + +``` +`EFFECT-NAME` is a bare identifier. The only effect with a built-in +op today is `IO` (op `io/print_str`); `Diverge` is a reserved name +(no op, unimplemented). +``` + +(Line 227 "Effects are a set; order is irrelevant." stays — and is +now corroborated by the DESIGN.md set-equality wording.) + +- [ ] **Step 2: Lines 356-358 (rule 3 op→effect mapping)** + +Replace exactly: + +``` +3. **Effects on side-effecting fns.** A function whose body uses `(do ...)` + MUST list every effect operation's effect in its `(effects ...)` + clause. `IO` for `io/print_*`; `Diverge` for `diverge/*`. +``` + +with: + +``` +3. **Effects on side-effecting fns.** A function whose body uses `(do ...)` + MUST list every effect operation's effect in its `(effects ...)` + clause. The only built-in effect op is `io/print_str`, whose + effect is `IO`. +``` + +- [ ] **Step 3: Run the pin — `form_a_spec_effect_names_are_true` GREEN** + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin form_a_spec_effect_names_are_true` +Expected: PASS. + +--- + +## Task 5: `crates/ail/src/main.rs` merge-prose CONTRACT example + +**Files:** +- Modify: `crates/ail/src/main.rs:288-290` + +- [ ] **Step 1: Replace the effect-annotation bullet** + +Replace exactly: + +``` + - Effect annotations on return types (`(effects IO)`, + `(effects Diverge)`). Prose shows these as `with IO` etc.; if + uncertain, keep what the original had. +``` + +with: + +``` + - Effect annotations on return types (e.g. `(effects IO)`). + Prose shows these as `with IO` etc.; if uncertain, keep what + the original had. +``` + +- [ ] **Step 2: Run the pin — `merge_prose_contract_example_is_true` GREEN** + +Run: `cargo test --workspace -p ail --test effect_doc_honesty_pin merge_prose_contract_example_is_true` + +(Note: the pin is an `ailang-core` integration test; run the whole +pin here.) + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin` +Expected: PASS (all four tests). + +--- + +## Task 6: Full verification + +- [ ] **Step 1: Whole pin GREEN** + +Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin` +Expected: PASS — 4/4. + +- [ ] **Step 2: No collateral drift** + +Run: `cargo test --workspace` +Expected: PASS — the prior green count + 4 new pin tests; in +particular `design_schema_drift.rs`, `spec_drift.rs`, +`schema_coverage.rs` stay green (recon confirmed none scans the +effect-prose region; this step proves it empirically). + +- [ ] **Step 3: Build sanity (no behavioural change)** + +Run: `cargo build --workspace` +Expected: clean. No codegen / checker code was touched — `ail check` +/ `ail run` behaviour is byte-unchanged by construction (doc-comment ++ markdown + an internal guidance string only). + +--- + +## Self-review (planner Step 5, inline) + +1. **Scope coverage:** every roadmap-`[todo]` clause (a)/(b)/(c) + + form_a.md ×2 + main.rs + guard test has a task. ✓ +2. **Placeholder scan:** no "TBD"/"TODO"/"similar to"/"appropriate"; + every code-changing step carries exact before→after text. ✓ +3. **Type/string consistency:** the pin's asserted corrected + substrings (`"flat, unordered, closed set of effect names"`, + `` "`Diverge` (for non-termination) is reserved" ``, + `"resolved at typecheck against `Env::effect_ops`"`, + `"The only built-in effect op is `io/print_str`"`) appear + verbatim in the Task 2/3/4 replacement bodies; the asserted + absent substrings (`"row-polymorphic"`, + `"the built-in IO and Diverge ops"`, + `"resolved against the effect-handler table at link time"`, + `` "`Diverge` for `diverge/*`" ``, `"currently `IO` and `Diverge`"`, + `` "`(effects Diverge)`" ``) are exactly the strings removed. + Cross-checked task-by-task. ✓ +4. **Step granularity:** every step is one edit or one command, + 2-5 min. ✓ +5. **No commit steps.** ✓ (Boss commits the whole tidy.) +6. **Line-wrap robustness:** every pin substring is single-line + (no `\n`-spanning fragment) — the documented iter-revert + grep-wrap failure mode is avoided. ✓ diff --git a/docs/roadmap.md b/docs/roadmap.md index 6ae8a15..e442319 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -221,6 +221,33 @@ clean. Pick the next milestone from P2.)_ - [ ] **\[todo\]** 21'h iteration — final 21' carry-over (latency methodology pass). Numbering kept for continuity with the 21' arc. +- [~] **\[todo\]** DESIGN.md effect-prose is fiction — standalone + documentation-honesty tidy (split out 2026-05-16 from the rejected + effect-op-arg-modes bundle; in flight via planner→implement). Three + load-bearing effect-system claims in DESIGN.md are presently false + and mislead every future brainstorm + the architect, who read + DESIGN.md as truth: (a) DESIGN.md:172/2722 say `IO` **and** + `Diverge` are wired-up built-in ops — `Diverge` has zero code in + any crate (not registered, never injected, no string literal); + reconcile to "only `IO` (`io/print_str`) is wired up; `Diverge` + reserved/unimplemented", modelled on Decision 4's + reserved-but-opaque refinements precedent. (b) DESIGN.md:171-172 + "the effect set is row-polymorphic (`![IO | r]`)" — no row variable + / `EffectRow` exists anywhere; effect sets are flat string + collections unified by set-equality; remove the claim (a flat + closed set is *more* locally legible — Decision 3 pillar — so this + is honesty, not a concession; pillar-doc edit, flag at review). + (c) `crates/ailang-core/src/ast.rs:438-439` `Term::Do` doc-comment + "resolved against the effect-handler table at link time" — there is + no handler table; it is an `IndexMap` lookup at typecheck + a + literal codegen `match`; correct it. Lockstep: `form_a.md:226,358` + + CLI-help `main.rs:289` Diverge mentions reconciled. Guard: a new + doc-presence test (false strings absent, corrected text present). + No language/codegen change; pure documentation truth. Grounding + triple-verified 2026-05-16 (three `ailang-grounding-check` PASSes). + - context: surfaced by the effect-subsystem recon during the + retired effect-op-arg-modes brainstorm; see the Stateful-islands + "UNDER RE-THINK" note for why the bundle was rejected. - [ ] **\[todo\]** `io/print_float` always-emit-`.0` — surface printer always emits `.` or `e/E` so re-lex routes to Float; the runtime printer (`printf("%g\n", v)`) doesn't, so `2.0` @@ -330,13 +357,35 @@ clean. Pick the next milestone from P2.)_ fn signatures stay pure while LLM authors can write imperative accumulators directly. Spec `docs/specs/2026-05-15-mut-local.md`; iters mut.1/mut.2/mut.3/mut.4-tidy (commits 7b92719, b24718a, - 03fb633, 20add51). Remaining sub-milestones (to be brainstormed - separately, in order): (2) effect-handler infrastructure as a - prerequisite for any non-IO/non-Diverge effect; (3) `!Mut` effect - + `ref a` first-class type that can escape mut blocks under - uniqueness-tracking; (4) mutable-array primitive `MutArray a`; - (5) `Stateful a b` sealed callable type + `pipe` combinator with - zero-allocation composition + `runST`-equivalent escape discharge. + 03fb633, 20add51). + + **Sub-milestone sequencing is UNDER RE-THINK with the user + (2026-05-16) — no further sub-milestone is planned or brainstormed + until that conversation happens.** Why: the 2026-05-15 decomposition + named "(2) effect-handler infrastructure as a prerequisite for any + non-IO/non-Diverge effect" as the next step. A read-only recon of + the live effect subsystem (2026-05-16) showed that premise is false: + the effect raise / declared-set / `UndeclaredEffect`-subset / call- + propagation machinery is already fully generic over an arbitrary + effect string; `!Mut` needs **no** effect-handler machinery. The + only real code prerequisite the recon found is one hard-coded + assumption (`linearity.rs` walks every `Term::Do` arg as `Borrow`; + `EffectOpSig` has no per-arg mode field) — a single struct field, + not a milestone. A brainstorm spec that tried to make sub-ms-2 a + standalone deliverable bundled that speculative one-field capability + with an unrelated DESIGN.md honesty fix; the user correctly rejected + the bundle as incoherent and the build-ahead-of-consumer half as the + iteration-discipline trap repeated. **Resolution:** (a) the DESIGN.md + effect-honesty correction is split out and runs now as a standalone + documentation tidy (see the P2 `[todo]` "DESIGN.md effect-prose is + fiction" below); (b) the `arg_modes` finding is recorded as recon + context for the future `!Mut` design, NOT built ahead — it is + first-iteration material of whatever the `!Mut` milestone turns out + to be, validated there against a real consuming op, never a + synthetic test op; (c) the old "(2)…(5)" sequence (effect-handler + infra → `!Mut`+`ref a` → `MutArray a` → `Stateful a b`+`pipe`) is + no longer treated as settled — the whole ordering and granularity + is what the pending user conversation re-decides. **Motivation.** AILang's signature-as-contract thesis is *better* served by an explicit `Stateful a b` + `!Mut` annotation than by diff --git a/skills/brainstorm/SKILL.md b/skills/brainstorm/SKILL.md index 03de1c5..d505485 100644 --- a/skills/brainstorm/SKILL.md +++ b/skills/brainstorm/SKILL.md @@ -79,6 +79,14 @@ message — no batching. Focus on: Lead with your recommended option and explain why. Each approach gets: - core idea (1-2 sentences) +- **the AILang program first, code not prose.** The *primary* + artifact is the `.ail` (Form-A) program that should work under + this approach — the code the language is *for*. Show it. The + implementation shape (changed struct/signature/match-arm, + before → after) is *secondary supporting* detail — "also useful, + not the point" — include it but never let it stand in for the + `.ail`. "The approach extends the struct with a field" is not an + approach; the `.ail` it makes work (or correctly reject) is. - substantive trade-offs (semantic fit, schema impact, performance, composability) - effort observation (NOT effort as rationale — see CLAUDE.md @@ -108,6 +116,27 @@ example — see the criterion text) lives in `docs/DESIGN.md` ("Feature-acceptance criterion"); this skill is the gate that applies it during spec writing. +**Clause 1 is unjudgeable without the worked author code.** For any +milestone with authoring surface, the spec MUST contain the actual +Form-A (`.ail`) example an LLM author would write — and that example +*is* the clause-1 evidence. A prose assertion ("an LLM naturally +reaches for X") with no shown code is exactly the unfalsifiable +hand-wave the criterion exists to prevent: write the code the LLM +would write, then judge whether it is in fact what the criterion +demands. For an infrastructure milestone with **no** author surface +there is still concrete AILang code: show the actual `.ail` the +milestone *delivers* — frequently a test fixture **whose correct +behaviour is rejection** (wrong code must fail to typecheck — the +clause-3 discriminator) — plus a *minimal, honest* slice of the +north-star program the infrastructure exists to serve (provisional +surface explicitly flagged, never a vague non-normative hand-wave). +"This milestone has no surface so there's no AILang code to show" is +the rationalisation to refuse: an infra milestone that cannot be +tied to concrete AILang code is speculative infra. (AILang's own +thesis is structured concrete form over prose description; a +spec that describes code in prose is incoherent with the language it +specifies.) + ### Step 5 — Present design in sections Scale each section to its complexity (a few sentences if @@ -118,6 +147,18 @@ straightforward, up to 200-300 words if nuanced). Cover: - Error handling - Testing strategy +**The AILang program is the headline; the implementation shape is +supporting.** The `## Concrete code shapes` section leads with the +`.ail` (Form-A) program — the worked author example for a surface +milestone (= the Step-4 clause-1 evidence), or the delivered +fixture-and-north-star for an infra milestone (per Step 4). The +before → after implementation shape (struct / signature / match-arm) +follows as a clearly secondary subsection — "also useful, not the +point" — never first, never a substitute for the `.ail`. Prose may +*explain* code, never *replace* it. Exact bytes/line-numbers remain +the planner's job (planner Iron Law) — the spec owns the *shape*, +shown, with the AILang program foremost. + Ask after each section whether it looks right. Be ready to revisit earlier sections if a later one surfaces a contradiction. @@ -136,6 +177,7 @@ Structure: ## Goal ## Architecture +## Concrete code shapes ## Components ## Data flow ## Error handling @@ -143,6 +185,14 @@ Structure: ## Acceptance criteria ``` +`## Concrete code shapes` is mandatory and **leads with the AILang +program**: the worked `.ail` author example for an authoring-surface +milestone (= the Step-4 clause-1 evidence), or — for a no-surface +infra milestone — the `.ail` the milestone *delivers* (often a +must-fail fixture) plus a minimal honest north-star slice (Step 4). +The before → after implementation shape for each load-bearing change +follows as an explicitly secondary subsection, never first. + ### Step 7 — Self-review Inline checklist (not a subagent dispatch): @@ -155,6 +205,11 @@ Inline checklist (not a subagent dispatch): sub-milestone decomposition? 4. **Ambiguity check:** could any requirement be interpreted two ways? If so, pick one and make it explicit. +5. **Concrete-code check:** does `## Concrete code shapes` exist and + carry before → after code for every load-bearing change (plus the + worked `.ail` example for an authoring-surface milestone)? A + load-bearing change described only in prose is a self-review + failure to fix, exactly like a placeholder. Fix issues inline. No need to re-review — fix and commit. @@ -263,6 +318,7 @@ Hand off carries: | "Spec exists from previous milestone, append to it" | New milestone = new spec file. The architect agent reads spec files per milestone; mixing scopes makes drift review unreadable. | | "Approaches A, B, C are all bad — proceed with A" | This is exactly the moment to surface "the problem is mis-framed" rather than ratify a known-bad shape into DESIGN.md. JOURNAL the impasse, escalate to user. | | "Just polishing a wording after PASS, no need to re-dispatch" | The grounding-check report attests to specific bytes. A polish edit changes the bytes; the previous attestation no longer covers them. Re-dispatch is cheap; the alternative is a commit with an attestation that doesn't match the file. | +| "The shape is clear from the prose, I don't need to paste the code" | If it's clear, pasting it is free; if pasting it is hard, it wasn't clear. Clause 1 is unjudgeable without the worked `.ail`; "an LLM reaches for it" with no shown code is the exact hand-wave the criterion exists to kill. Prose about code is incoherent with a language whose thesis is structured-form-over-prose. | ## Red Flags — STOP @@ -271,6 +327,9 @@ Hand off carries: - "User said it's clear, who am I to push back" - "The 2-3 approaches all suck, just pick one" - Skipping Step 7 self-review because "I wrote it carefully" +- A load-bearing change described in prose with no before → after + code block; an authoring-surface milestone with no worked `.ail` + example - Editing the spec file after a Step 7.5 PASS without re-dispatching - Jumping straight from spec to `implement` (must go via `planner`) - Inventing a design rationale that's actually about effort, not