Files
AILang/docs/plans/0021-architect-iron-law-extension.md
T
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
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.
2026-05-28 13:31:31 +02:00

352 lines
15 KiB
Markdown

# 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. ✓