# Architect Iron-Law Extension — Implementation Plan > **Parent:** `docs/roadmap.md` P2 todo "Architect-iron-law standing-grep > extension" (no per-iteration spec — this is a tooling-discipline > extension, not a milestone). > > **Context:** JOURNAL 2026-05-10 "Milestone close: design-md-consolidation" > (the four sweep regexes); JOURNAL 2026-05-10 "Fieldtest close: > Floats milestone" (the lockstep failure-mode that B1 surfaced). > > **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` > to run this plan. Steps use `- [ ]` checkboxes for tracking. **Goal:** Extend `ailang-architect`'s drift review with two standing checks so DESIGN.md cannot silently regrow history anchors, and so new typecheck-reject / `lower_app` arms cannot ship without their cross-file companion update. **Architecture:** A new helper script `bench/architect_sweeps.sh` runs the four sweep regexes from the design-md-consolidation milestone against `docs/DESIGN.md` and prints any matched lines. The architect agent's standing reading list and "What you check" section are extended with two new bullets: one that invokes the sweep script, one that walks the two known cross-file lockstep pairings (`Pattern::Lit::*` rejects ↔ `pre_desugar_validation.rs`, and `lower_app` arms ↔ `is_static_callee`). No production code changes; this is process-tooling only. **Tech Stack:** bash, markdown. **Files this plan creates or modifies:** - Create: `bench/architect_sweeps.sh` — runs the four design-md- consolidation sweep regexes against `docs/DESIGN.md`. Exit 0 if all sweeps are clean, 1 if any anchor is found. Output is the matched lines, prefixed by sweep name. - Modify: `skills/audit/agents/ailang-architect.md` — adds two new bullets to "What you check" (DESIGN.md history-anchor regrowth + Lockstep invariants across files), and inserts a Step 2.5 in "The Process" that runs the sweep script. - Modify: `docs/JOURNAL.md` — append a closing entry under today's date. - Modify: `docs/roadmap.md` — strike the "Architect-iron-law standing-grep extension" P2 todo (entry done). --- ## Task 1: Helper script `bench/architect_sweeps.sh` **Files:** - Create: `bench/architect_sweeps.sh` - Test: manual invocation against current `docs/DESIGN.md` - [ ] **Step 1: Write the script** Create `bench/architect_sweeps.sh` with the following exact contents: ```bash #!/usr/bin/env bash # bench/architect_sweeps.sh # # Run the four design-md-consolidation sweep-invariant greps against # docs/DESIGN.md. Output: matched lines, prefixed by sweep name. Exit 0 # if all four sweeps are clean, 1 if any anchor was found. # # Companion to skills/audit/agents/ailang-architect.md "What you check # / DESIGN.md history-anchor regrowth". See JOURNAL 2026-05-10 # ("Milestone close: design-md-consolidation") for the rationale of # each sweep regex; the regexes here are copied verbatim from JOURNAL # entries 12487 (Sweep 1), 12619 (Sweep 2), 12718-12720 (Sweep 3), # and 12840 (Sweep 4). # # A non-zero exit is advisory, not authoritative: the architect agent # reviews each match and decides whether it is a legitimate quote # (e.g. a JOURNAL citation inside a block-quote) or fresh drift. set -u DESIGN="docs/DESIGN.md" HITS=0 if [[ ! -f "$DESIGN" ]]; then echo "architect_sweeps: $DESIGN not found (run from repo root)" >&2 exit 2 fi run_sweep() { local name="$1" local pattern="$2" local matches matches=$(grep -nE "$pattern" "$DESIGN" || true) if [[ -n "$matches" ]]; then echo "=== Sweep: $name ===" echo "$matches" echo HITS=1 fi } run_sweep "1 (history anchors)" \ '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' run_sweep "2 (REVERTED + migration)" \ 'REVERTED|preserved for the audit trail|Migration plan|[Oo]riginally framed|original rationale|empirically-grounded version|A future (iter|iteration|milestone|Prelude) may|A future iter that|a (later|future) iter(ation)? \(deferred\)|Concrete design deferred' run_sweep "3 (schema SoT)" \ '^\s*(struct |enum |pub (struct|enum|fn))|whenever the two disagree|ast\.rs is the source of truth' run_sweep "4 (workflow + stale cross-references)" \ 'agents/README\.md|ailang-docwriter|Recently lifted|see the JOURNAL queue|later the same day|Sharpened later|closure conversion in JOURNAL' if [[ $HITS -eq 0 ]]; then echo "All four sweeps clean." fi exit $HITS ``` - [ ] **Step 2: Make it executable** Run: `chmod +x bench/architect_sweeps.sh` Expected: no output, exit 0. - [ ] **Step 3: Run against current DESIGN.md, expect clean** Run: `bash bench/architect_sweeps.sh` Expected: stdout = `All four sweeps clean.`, exit code 0. If any sweep matches, the script implementation is wrong (the design-md-consolidation milestone closed all four sweeps as clean on 2026-05-10; today's DESIGN.md is the post-close baseline). Diff the regex against the JOURNAL-recorded one character-by-character. - [ ] **Step 4: Commit** ```bash git add bench/architect_sweeps.sh git commit -m "iter architect-iron-law.1: bench/architect_sweeps.sh runs the four design-md-consolidation sweeps against DESIGN.md" ``` --- ## Task 2: Extend `ailang-architect` agent prose **Files:** - Modify: `skills/audit/agents/ailang-architect.md` - [ ] **Step 1: Add "DESIGN.md history-anchor regrowth" bullet to "What you check"** In `skills/audit/agents/ailang-architect.md`, locate the "What you check" section (currently has bullets: Drift against DESIGN.md, Drift against the milestone spec, Growing debt, Consistency across crates, Test coverage, Scaling break points, JOURNAL truthfulness). Insert a new bullet immediately AFTER the "JOURNAL truthfulness" bullet (i.e. as the last bullet in the list): ```markdown - **DESIGN.md history-anchor regrowth.** Run `bash bench/architect_sweeps.sh` from the repo root. Exit 0 = clean. Exit 1 = at least one of the four sweeps from the design-md-consolidation milestone (2026-05-10) matched. Review each match: a legitimate quote (e.g. a JOURNAL citation inside a block- quote) is fine; a fresh history anchor / REVERTED narrative / workflow detail / stale cross-reference is drift to flag. - **Lockstep invariants across files.** Two known cross-file pairings must move together; a new arm in one without the matching update in the other ships silently broken (B1 in the Floats fieldtest is the canonical example). On every milestone-close, walk these pairs: | Pair | Failure mode | |---|---| | `Pattern::Lit::*` typecheck rejects in `crates/ailang-check/src/lib.rs` ↔ pre-desugar walkers in `crates/ailang-check/src/pre_desugar_validation.rs` | A reject arm placed AFTER `desugar_module` in the call chain is unreachable through the public `check` API if the desugar pass rewrites that pattern shape first (e.g. `desugar::build_eq` rewrites `Pattern::Lit::Float` into `(== scrut lit)` before typecheck runs). New rejects on a `Pattern::Lit::*` shape MUST live in `pre_desugar_validation.rs`, or be paired with a written guarantee that no desugar pass eats the shape. | | `lower_app` arms in `crates/ailang-codegen/src/lib.rs::lower_app` (line ~1749) ↔ name recognition in `crates/ailang-codegen/src/lib.rs::is_static_callee` (line ~2189) | A name lowered by a direct `lower_app` arm but unrecognised by `is_static_callee` falls through to the indirect-call path with `UnknownVar` (`UnknownVar` panic in worst case). Every new builtin lowering arm in `lower_app` must have a matching `is_static_callee` entry. | Walk procedure: for each milestone-scope commit-range arm landed in these files (use `git diff ..HEAD --` on each file in the pair), open both files in the pair and confirm the matching update is present. Flag any unpaired arm as drift. ``` - [ ] **Step 2: Add Step 2.5 to "The Process"** In `skills/audit/agents/ailang-architect.md`, locate "The Process" section. Steps are currently numbered 1 through 6. Replace Step 2 + the current Step 3 with Step 2 + Step 2.5 + Step 3 (renumbering nothing else). The current Step 2 reads: ```markdown 2. `git log --oneline -30` and `git diff ..HEAD` for the factual diff. ``` Replace it with: ```markdown 2. `git log --oneline -30` and `git diff ..HEAD` for the factual diff. 2.5. Run `bash bench/architect_sweeps.sh` from the repo root. If exit code is 1, treat each matched line as a drift-suspicion to verify. If exit code is 2, the script could not find DESIGN.md — fix the working directory and re-run before continuing. ``` - [ ] **Step 3: Self-check by re-reading the modified agent file** Read `skills/audit/agents/ailang-architect.md` end-to-end. Verify: - The two new bullets appear at the end of "What you check". - Step 2.5 appears between Step 2 and Step 3 in "The Process". - File path references resolve: `crates/ailang-check/src/lib.rs`, `crates/ailang-check/src/pre_desugar_validation.rs`, `crates/ailang-codegen/src/lib.rs`, `bench/architect_sweeps.sh` all exist. - [ ] **Step 4: Commit** ```bash git add skills/audit/agents/ailang-architect.md git commit -m "iter architect-iron-law.2: extend ailang-architect with sweep-script invocation + lockstep-checklist for two known cross-file pairings" ``` --- ## Task 3: JOURNAL entry + roadmap close-out **Files:** - Modify: `docs/JOURNAL.md` - Modify: `docs/roadmap.md` - [ ] **Step 1: Append JOURNAL entry** Append the following block at the end of `docs/JOURNAL.md`: ```markdown ## 2026-05-10 — Architect iron-law extension: sweep script + lockstep-checklist `ailang-architect`'s drift-review iron law extended with two standing checks, both motivated by failure modes that shipped in the closing milestones of the typeclass-and-floats arc: 1. **DESIGN.md history-anchor regrowth.** The four sweep regexes from the design-md-consolidation milestone (history anchors, REVERTED + migration, schema SoT, workflow + stale cross- references) are now packaged as `bench/architect_sweeps.sh` and run as Step 2.5 of the architect's process. Today's DESIGN.md is clean against all four; the script's job is to keep it that way against future commits. Non-zero exit is advisory: the architect reads each match and decides legitimate-quote vs. fresh drift. 2. **Lockstep invariants across files.** Two cross-file pairings are pinned in the agent's "What you check" section as standing walks: `Pattern::Lit::*` typecheck-rejects ↔ `pre_desugar_validation.rs` walkers (the B1 lockstep — the typecheck reject is unreachable through `check_module` if a desugar pass rewrites the shape first), and `lower_app` arms ↔ `is_static_callee` recognition (the iter-4.2 / 4.4 lockstep — a direct lowering arm without `is_static_callee` recognition falls through to indirect-call with `UnknownVar`). For each milestone-scope commit-range arm landed in these files, the architect now opens both files in the pair. Both pairings are real, both shipped silently broken at least once, and both were caught only by post-hoc means (B1 by fieldtest, the iter-4 ones by mid-iter implementer review). The architect was the right role to catch them prospectively; the standing checklist is how that role catches them in future. Per-task commits: - `` iter architect-iron-law.1: bench/architect_sweeps.sh runs the four design-md-consolidation sweeps against DESIGN.md - `` iter architect-iron-law.2: extend ailang-architect with sweep-script invocation + lockstep-checklist for two known cross-file pairings The roadmap entry "Architect-iron-law standing-grep extension" under P2 is closed by this iteration. Future lockstep pairings, as they surface in JOURNAL entries, can extend the table inline without further iteration ceremony. ``` (The hashes will be filled in when the actual commits are made; the commit-creating step in this task patches them in.) - [ ] **Step 2: Resolve commit-hash placeholders** After Tasks 1 and 2 have been committed, get the two short hashes: Run: `git log --oneline -5 | head -3` Expected: top three lines start with the two iter-1/iter-2 commits plus the previous (Floats) commit. Edit `docs/JOURNAL.md` — replace `` and `` with the short SHA from the corresponding commits. The Task 1 commit is "iter architect-iron-law.1:..." and Task 2 is "...iron-law.2:...". - [ ] **Step 3: Strike the roadmap entry** In `docs/roadmap.md`, locate the bullet: ```markdown - [ ] **\[todo\]** Architect-iron-law standing-grep extension — the architect agent's drift review should run the four sweep invariant greps on every milestone-close so DESIGN.md cannot silently regrow history anchors / REVERTED narratives / workflow detail / stale cross-references. Path-anchored exceptions (`docs/specs/2026-05-09-*`) need an allowlist. Same extension should add the **lockstep-checklist** the Floats fieldtest exposed: for every new typecheck reject arm on a pattern shape, verify the corresponding desugar pass does not eat that shape first; for every new lowering arm in `lower_app`, verify `is_static_callee` recognises it. Both shipped silently-broken in the Floats milestone (B1 and the iter-4.2/4.4 pull-forwards) and the architect missed them. - context: JOURNAL 2026-05-10 ("Milestone close: design-md-consolidation"); 2026-05-10 ("Fieldtest close: Floats milestone") — B1 + architect-checklist follow-up. ``` Delete it entirely. (Done entries are deleted from the roadmap once they are no longer interesting context; the JOURNAL entry above preserves the rationale.) - [ ] **Step 4: Verify the script still runs clean after agent-prose changes** Run: `bash bench/architect_sweeps.sh` Expected: `All four sweeps clean.`, exit 0. (Sanity — Tasks 2 and 3 should not have touched DESIGN.md.) - [ ] **Step 5: Commit** ```bash git add docs/JOURNAL.md docs/roadmap.md git commit -m "iter architect-iron-law.3: JOURNAL entry + close roadmap todo" ``` --- ## Self-review checklist 1. **Spec coverage:** the roadmap entry has two halves (standing greps + lockstep checklist); Task 1 covers the script that embodies the greps, Task 2 covers the agent-prose for both halves, Task 3 covers the JOURNAL + roadmap closure. All covered. ✓ 2. **Placeholder scan:** `` / `` are placeholders resolved in Task 3 Step 2 by the implementer reading actual git output. No "TBD", no "TODO", no "implement later", no "similar to Task N". ✓ 3. **Type / path consistency:** `bench/architect_sweeps.sh` is the same path in script, agent file, and JOURNAL. `lower_app` / `is_static_callee` line numbers match the verified `grep` output from this conversation (1749 / 2189). `pre_desugar_validation.rs` exists. ✓ 4. **Step granularity:** each step is a single file-edit-or-command, 2-5 minutes. ✓