# 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 <prev-close>..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 <prev-milestone-close>..HEAD` for
   the factual diff.
```

Replace it with:

```markdown
2. `git log --oneline -30` and `git diff <prev-milestone-close>..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:

- `<hash 1>` iter architect-iron-law.1: bench/architect_sweeps.sh runs the four design-md-consolidation sweeps against DESIGN.md
- `<hash 2>` 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 `<hash 1>` and `<hash 2>` 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:** `<hash 1>` / `<hash 2>` 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. ✓
