Files
AILang/docs/specs/0011-audit-orchestrator-agent.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

22 KiB
Raw Permalink Blame History

ailang-audit-orchestrator — Design Spec

Date: 2026-05-11 Status: Draft — awaiting user spec review Authors: Brummel (orchestrator) + Claude

Goal

Extend the Boss-context-offload pattern established in or.1 / corrected in or.2 to the post-implement validation cycle. The Boss should only carry the milestone-close context that involves a decision: classify a drift item, ratify a baseline shift, dispatch a tidy iter. Everything that's pure execution — running architect, running the bench scripts, running docwriter, running fieldtester — moves into a dedicated subagent context (ailang-audit-orchestrator) and reaches the Boss only as a structured end-report.

The two existing skills (audit, fieldtest) and their four worker agents (ailang-architect, ailang-bencher, ailang-docwriter, ailang-fieldtester) remain unchanged in body. What changes is their caller: at milestone-close, the Boss dispatches the new orchestrator agent, which inhales the audit + fieldtest discipline as sequential role-switches in its own context — the same shape as the or.2-corrected ailang-implement-orchestrator.

Background — why now

Three observations from 2026-05-11 motivate this spec:

  1. or.1 proved the offload pattern. Per-/implement Boss-context cost dropped from ~100k to ~700 tokens by relocating the per-task loop into the implement-orchestrator subagent.

  2. or.2 defined the constraint. Claude Code categorically forbids nested subagent dispatch. The corrected architecture uses inline role-switches inside the subagent's own context. Phase-reference files (ailang-implementer.md, etc.) define the discipline; the orchestrator inhales them per phase.

  3. Milestone close was left out. Audit + fieldtest currently run as direct Boss-dispatched calls: architect report → Boss-side bench bash → docwriter dispatch → fieldtest dispatch, with each report landing in the Boss-context. Roughly 515k tokens per milestone-close. Not the 100×-hebel of /implement, but a real cost and — more importantly — a structural inconsistency: the per-iter execution path is offloaded, the post-iter validation path is not.

The user-stated principle that governs this spec:

Boss-context is decision-context. Everything else moves out.

Concretely:

  • Audit + fieldtest clean → Boss gets a compact end-report ("milestone closes, nothing to decide"), writes the milestone-close JOURNAL entry, advances.
  • Audit + fieldtest find anything actionable → Boss gets a structured findings report and decides per item: fix / ratify / carry-on. The decision and the downstream dispatch (planner + implement for fix, --update-baseline for ratify) remain Boss-level work, as today.

Architecture

Boss-Orchestrator
    │
    │ Agent("ailang-audit-orchestrator", carrier)
    ▼
ailang-audit-orchestrator        ← runs in its own context
    │
    │   Phase 1 — architect role-switch
    │       reads skills/audit/agents/ailang-architect.md
    │       performs the drift review inline
    │       collects: prioritised drift list
    │
    │   Phase 2 — bencher role-switch
    │       runs bench/check.py && bench/compile_check.py && bench/cross_lang.py
    │       collects: exit code + raw numbers
    │       HALT on exit 2 (infra-broken — subsequent phases unreliable)
    │
    │   Phase 3 — docwriter role-switch (conditional)
    │       cargo doc --no-deps 2>&1 → warnings?
    │       if yes: read skills/audit/agents/ailang-docwriter.md, run inline,
    │               commit rustdoc fixes on main
    │       collects: warning count, list of fixed warnings (or 0)
    │
    │   Phase 4 — fieldtester role-switch (conditional, carrier flag)
    │       if run_fieldtest && Phase 1 architect did not flag critical drift:
    │           reads skills/fieldtest/agents/ailang-fieldtester.md
    │           writes 24 .ailx examples in examples/, runs ail check/build/run,
    │           writes docs/specs/<date>-fieldtest-<milestone>.md,
    │           commits as `fieldtest: <milestone> — <N> examples, <K> findings`
    │       collects: spec_path, examples_added, findings, status
    │
    │   Phase 5 — end-report assembly
    │       structured findings → compact text → ≤500 tokens
    │       JOURNAL draft (Boss may accept/edit) → /tmp/ail-audit/<milestone>/draft.md
    ▼
end-report → Boss

Boss-Orchestrator (after reading end-report):
    │
    │   SUCCESS (status: clean):
    │       read /tmp/ail-audit/.../draft.md (optional)
    │       write docs/journals/<date>-milestone-<X>-close.md
    │       append docs/journals/INDEX.md
    │       (no further dispatches — milestone closes)
    │
    │   FAILURE (status: findings_*):
    │       classify each drift_item / bench finding / fieldtest finding
    │           → fix    → dispatch planner + implement-orchestrator (tidy iter)
    │           → ratify → run bench/<x>.py --update-baseline + JOURNAL ratify entry
    │           → carry-on → record in JOURNAL, no commit
    │       on bug from fieldtest: dispatch debug skill

The Boss-Orchestrator sees: one dispatch out, one compressed report back. Per-phase work — drift list assembly, bench bash output, rustdoc fix loops, fieldtest implementation cycles — all happen inside the orchestrator-agent's context and never reach the Boss.

Same structural pattern as or.2: the four phase-reference files (ailang-architect.md, ailang-bencher.md, ailang-docwriter.md, ailang-fieldtester.md) stay in their current locations. The orchestrator reads each one verbatim at its role-switch and applies the discipline inline. No nested subagent dispatch.

Components

ailang-audit-orchestrator (new agent)

Location: skills/audit/agents/ailang-audit-orchestrator.md.

Tools: Read, Edit, Write, Bash, Glob, Grep. No Agent tool — the or.2 Iron Law applies (Claude Code does not permit nested subagent dispatch).

Standing reading list:

  1. CLAUDE.md — orchestrator framing.
  2. docs/DESIGN.md — invariants the audit must hold against.
  3. docs/journals/INDEX.md plus the last 13 referenced per-iter files — recent state to inform the architect-phase scope.
  4. skills/audit/SKILL.md — canonical audit discipline.
  5. skills/fieldtest/SKILL.md — canonical fieldtest discipline, conditionally relevant.
  6. Phase-reference files (read per-phase, not all upfront):
    • skills/audit/agents/ailang-architect.md
    • skills/audit/agents/ailang-bencher.md
    • skills/audit/agents/ailang-docwriter.md
    • skills/fieldtest/agents/ailang-fieldtester.md

Model: Opus 4.7.

Iron Law (verbatim, derived from or.2):

PER-PHASE WORK RUNS SEQUENTIALLY IN YOUR OWN CONTEXT — architect role-switch,
then bencher, then docwriter, then fieldtester. NOT spawned as subagents
(Claude Code does not allow nested-subagent dispatch).

BENCH EXIT CODE 2 HALTS THE RUN. Subsequent phases would run on a broken
infrastructure and produce unreliable findings. Report `infra_blocked` and stop.

EVERY PHASE COMPLETES (or halts on the bench-2 condition). FINDINGS AGGREGATE.
Boss decides per item; the orchestrator does NOT pre-resolve.

skills/audit/SKILL.md (small additive change)

Today's body describes the five-step process for Boss-direct invocation. After this spec:

  • Gain a short "Standard Mode" subsection at the top, saying:

    "At milestone close: dispatch ailang-audit-orchestrator. The orchestrator runs Steps 13 + the conditional fieldtest phase inline, returns a structured end-report. The Boss applies Steps 4 (Classify) and 5 (Resolve) on the report — these remain Boss work."

  • The existing Steps 15 are retained verbatim. They now serve two audiences:

    • The orchestrator-agent reads them as phase-reference (alongside the per-agent files).
    • The Boss reads them when invoking audit in Manual Mode (drift suspected outside milestone close, debugging an isolated phase, etc.). Manual Mode dispatch shape unchanged.
  • The Cross-references section gains:

    • Standard-mode dispatcher: skills/audit/agents/ailang-audit-orchestrator.md
    • "Why inline phases, not nested subagents" pointer to the or.2 journal.

skills/fieldtest/SKILL.md (cross-reference update only)

Today's body is unchanged. Fieldtest's standard trigger expands from "orchestrator dispatches ailang-fieldtester after audit closes" to "either the audit-orchestrator runs phase 4 inline, OR the Boss dispatches ailang-fieldtester directly in Manual Mode". The agent file (ailang-fieldtester.md) is unchanged.

Manual Mode for fieldtest covers the genuine edge case: audit found drift, a tidy-iter landed, re-running fieldtest in isolation makes sense, no point re-running the whole audit-orchestrator.

Worker agents — unchanged in body, expanded role

ailang-architect.md, ailang-bencher.md, ailang-docwriter.md, ailang-fieldtester.md keep their current frontmatter, tools, standing reading lists, status protocols, output formats.

What changes is their role-in-the-system: they serve as phase references when invoked by the audit-orchestrator (Standard Mode), AND as directly-dispatchable subagents when invoked by the Boss (Manual Mode). The dual duty mirrors how or.2 left ailang-implementer.md and siblings in the implement skill.

Same caveat applies as in or.2 known-debt: these files frame themselves as "dispatched subagents receiving a carrier from a dispatcher". In Standard Mode the orchestrator IS the dispatcher and the worker. If the first real audit-orchestrator run shows confusion during phase reads, a follow-up cleanup iter rewrites the framing. Not pre-emptive.

skills/README.md (skill table + roster + pipeline)

  • Skill table row for audit: gains a one-line note about Standard-Mode dispatcher.
  • Agent roster: gains row for ailang-audit-orchestrator (dispatched by audit, Standard Mode). Existing rows for architect / bencher / docwriter / fieldtester gain "and serves as phase reference for ailang-audit-orchestrator" suffix, analogous to or.2.
  • Pipeline diagram: the audit -> fieldtest chain at milestone-close is replaced by a single audit (via ailang-audit-orchestrator) node; the conditional fieldtest is now phase 4 of that single dispatch.

Data flow

Carrier — Boss → ailang-audit-orchestrator

Field Content
milestone_id e.g. "ct.2" or "22" (string, free-form, used in commit messages and filenames)
commit_range e.g. "<prev-milestone-close-sha>..HEAD" — the architect's scope
run_fieldtest bool — set by Boss based on milestone spec. true if milestone touched user-visible surface (Decision 6, schema, effects, types, modes, diagnostics); false for purely internal milestones
fieldtest_axis_hints (only if run_fieldtest = true) bullet list, one per milestone axis the fieldtest should probe — copied verbatim from the milestone spec
milestone_scope (only if run_fieldtest = true) 13 sentences naming what shipped, for the fieldtester's carrier

End-report — ailang-audit-orchestrator → Boss

Plain-text report, ≤ 500 tokens, fixed structure:

Status:           clean | findings_drift | findings_bench | findings_rustdoc
                 | findings_fieldtest | findings_multiple | infra_blocked
Milestone:        <milestone_id>

Phase 1 (architect):
  Drift items:    <N>
                  - [<priority>] <path>: <1-line justification>
                  - ...
                  (or: none)
  Recommendation: <architect's single next-iter recommendation>
                  (verbatim from architect output)

Phase 2 (bench):
  Exit code:      0 | 1 | 2
  Numbers:        <raw figures from the three scripts, verbatim>

Phase 3 (rustdoc):
  Warnings:       <count> (or 0)
                  - <warning-snippet 1>
                  - ...
                  Auto-fixed: yes | no | partial (orchestrator may commit fixes)

Phase 4 (fieldtest):
  Ran:            yes | no (reason: run_fieldtest=false | architect critical-drift)
  Status:         clean | friction_found | bugs_found | (n/a)
  Spec:           docs/specs/<date>-fieldtest-<milestone>.md (committed)
  Findings:       <N> (one line each, with class)

Suggested classification (Boss decides):
  - <item ref>: fix | ratify | carry-on | needs-debug
  - ...
  (Orchestrator's heuristic suggestion. Boss applies audit-Step-4
  semantics and may override per item.)

Journal draft:    /tmp/ail-audit/<milestone>/draft.md
Commits made:     <SHA range or "none">

Boss-side reading workflow (on clean)

  1. Read the end-report's Status: clean confirmation.
  2. Optionally read /tmp/ail-audit/<milestone>/draft.md.
  3. Write docs/journals/<date>-milestone-<X>-close.md, accepting the orchestrator's draft as a starting point or rewriting the summary.
  4. Append a one-line entry to docs/journals/INDEX.md.
  5. (If trigger is done-state: WhatsNew + notify per CLAUDE.md.)
  6. Milestone closes; pick up the next item from docs/roadmap.md.

Boss-side workflow (on findings)

  1. Read the end-report's structured findings.
  2. Per item, classify: fix / ratify / carry-on / needs-debug.
  3. Dispatch downstream as today:
    • fixplanner + implement for a tidy iter
    • ratifybench/<x>.py --update-baseline + JOURNAL ratify entry
    • carry-on → record in JOURNAL, no commit
    • needs-debugdebug skill (RED-first, GREEN via implement mini-mode)
  4. Write docs/journals/<date>-milestone-<X>-close.md summarising findings + classification + downstream-actions-taken.
  5. If findings_fieldtest includes friction or spec_gap worth a follow-up: brainstorm for next milestone OR add to roadmap.

Error handling

Phase-level outcomes

Phase Possible outcomes Orchestrator action
Phase 1 (architect) drift_found / clean Always continue to phase 2. Drift severity is metadata in the report, NOT a halt condition.
Phase 2 (bench) Exit 0 / 1 / 2 0 or 1: continue. 2: HALT — report infra_blocked, skip phases 3+4, exit with Status: infra_blocked.
Phase 3 (docwriter) Warnings present / absent / fix-failed Continue regardless. If warnings present, attempt fix and commit; record outcome. If fix fails, record Auto-fixed: no and pass through.
Phase 4 (fieldtest) clean / friction / bugs / infra_blocked Skipped entirely if run_fieldtest=false or if architect flagged a priority: highest drift item. Skip is recorded as Ran: no (reason: architect-highest-priority-drift). Always reported, never halts.

Why aggregate, not abort-at-first-failure

The four phases test independent dimensions:

  • Architect: code vs. DESIGN.md (semantic drift).
  • Bench: code vs. baseline (performance drift).
  • Docwriter: code vs. rustdoc (documentation drift).
  • Fieldtester: code vs. real-world LLM-author usage (usability drift).

A drift in one is not predictive of a drift in another. Aborting at phase 1 on architect-drift would force the Boss into a fix-then-re-run loop: fix architect drift, re-run, discover bench regression, fix that, re-run, discover fieldtest bug. Aggregating gives Boss the whole picture in one pass.

The ONE exception is bench exit 2: the bench infrastructure itself is broken, which means later phases run on an unreliable system. Halting is correct there.

Critical-drift skip on phase 4

The architect-phase output classifies drift items by priority (highest / high / medium — see skills/audit/agents/ailang-architect.md output format). If any item is priority: highest (i.e. drift against docs/DESIGN.md — an invariant violation, a fundamental schema break), phase 4 is skipped. Rationale: fieldtest writes new examples that exercise the surface; on a codebase that violates DESIGN.md, those examples generate noise findings that the Boss must triage instead of acting on the actual highest-priority drift.

priority: highest is the architect's call, not the orchestrator's. The orchestrator just reads priority from the architect-phase output and gates phase 4 on it. priority: high items (drift against the milestone spec, not DESIGN.md) do NOT trigger the skip — the language's invariants still hold; fieldtest can still produce useful signal.

NEEDS_CONTEXT from orchestrator

Returned if milestone_id or commit_range is ambiguous, or if run_fieldtest=true but fieldtest_axis_hints / milestone_scope is missing. Boss provides the missing carrier fields and re-dispatches.

BLOCKED from orchestrator

Returned on unrecoverable internal error (e.g., a phase-reference file missing, a bash binary not found, a git operation failing non-trivially). Halts immediately. Boss diagnoses and either fixes infrastructure + re-dispatches, or escalates.

Testing strategy

This is a skill/agent refactor, not a language feature. No cargo test coverage applies directly. Verification ladder:

  1. Agent file parsesAgent("ailang-audit-orchestrator", ...) resolves to a runnable spec. Confirm via a no-op dispatch on a trivial milestone fixture (e.g., commit-range with no real changes).
  2. All four phases produce structured output — dispatch on a real recent milestone, verify the end-report has all five sections populated (or n/a with reason).
  3. Bench exit 2 halts — temporarily break a bench fixture, verify the orchestrator stops after Phase 2 with infra_blocked and does NOT run phases 3+4.
  4. Critical-drift skip on phase 4 — temporarily inject a fake severity=high architect finding, verify phase 4 is skipped with the documented reason.
  5. run_fieldtest=false skip — dispatch on a purely-internal milestone, verify phase 4 is skipped with the documented reason.
  6. Aggregate, not abort — dispatch on a milestone with known architect-drift, verify phases 2, 3, 4 still run despite the architect finding.
  7. Boss-side workflow on clean — manually integrate a clean run: read draft journal, write milestone-close journal, append INDEX. Verify linear history and INDEX coherence.
  8. Manual-Mode preserved — invoke audit skill directly (without the orchestrator) — verify the current Boss-direct dispatch path still works for ad-hoc invocations.

Verification steps 3, 4, 6 require synthetic fixtures and are one-shot reproducers — they confirm the discipline once. Future audit runs answer "is the design holding up" empirically.

Acceptance criteria

The refactor is accepted when:

  1. skills/audit/agents/ailang-audit-orchestrator.md exists, following the standard agent template (frontmatter, spirit-letter lead-in, standing reading list, carrier contract, Iron Law, process, status protocol, output format, common rationalisations, red flags).
  2. skills/audit/SKILL.md gains a Standard-Mode subsection naming the orchestrator-agent as the dispatcher at milestone close. Steps 15 retained verbatim as phase references + Manual-Mode path. Cross- references updated.
  3. skills/fieldtest/SKILL.md cross-references updated to reflect that Phase 4 of the audit-orchestrator is now the standard trigger; Manual Mode path retained.
  4. skills/README.md skill table, agent roster, and pipeline diagram updated.
  5. The four worker agent files (ailang-architect.md, ailang-bencher.md, ailang-docwriter.md, ailang-fieldtester.md) are unchanged in body. (The known-debt re: subagent framing carries over from or.2.)
  6. The eight verification steps in Testing strategy pass.
  7. The first real milestone close after the refactor shows a Boss-context delta consistent with the predicted offload (target: ≤ 1k tokens per audit-orchestrator dispatch + end-report, plus the Boss-side decision context for whatever findings emerge).

Migration

Single trivial-mechanical addition. No code touched, no language behaviour change. Two ways to land:

Option (a) — single direct-on-main commit. The new agent file, plus the additive edits to audit/SKILL.md, fieldtest/SKILL.md, README.md. Analogous to or.2's landing.

Option (b) — iter au.1 via the planner + implement loop. Forces the new orchestrator-agent through its own discipline cycle (plan → TDD → review). Adds about a day of latency for a refactor that has no executable surface to test against.

Recommendation: (a). Same reasoning as or.2: doc-only revision, no behaviour to test, the first real audit at the next milestone close IS the verification step. Option (b) is process-theatre for this case.

Out of scope (deferred to other specs)

  • Brainstorm offload — the /brainstorm usage statistic was 2% pre-or.1; not cost-effective to encapsulate yet.
  • Planner offload — the pr.1 recon-subagent already addresses the read-heavy phase of planning; further offload is premature.
  • Audit-result auto-resolution — the Boss's fix / ratify / carry-on decision is semantic, not mechanical. Pushing this into the orchestrator would violate the user-stated principle that Boss-context is decision-context. Out of scope on principle, not on effort.
  • Per-milestone-close branch isolation — implement-orchestrator uses iter-branches; audit-orchestrator commits direct-on-main because per-phase changes (docwriter rustdoc fixes, fieldtest fixtures) are individually safe and don't benefit from iter-isolation. May be reconsidered if a future audit produces larger-scale changes that warrant branching.
  • Worker model downgrade — all roles stay at Opus 4.7, as in or.1.