Files
AILang/docs/plans/0021-architect-iron-law-extension.md
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

15 KiB

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:

#!/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
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):

- **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:

2. `git log --oneline -30` and `git diff <prev-milestone-close>..HEAD` for
   the factual diff.

Replace it with:

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

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:

## 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:

- [ ] **\[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
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. ✓