ADR-007 — Verified component loop: sequential, ≤2 agents, frozen-file handoffs
Date: 2026-06-22
Amended: 2026-10-09
Status: accepted
Staked out ahead of Phase 9 implementation as
proposed, with promotion gated on the loop running once on a real component (the Phase 9 exit condition). Promoted 2026-07-23 — see the amendment below.
Context
The system is pivoting to introduce ad-hoc agentic loops — starting with component generation — as a learning ground for agent orchestration, while staying inside the lite-agentic charter. The constraints that shape the decision:
- Runtime is Claude Pro, not the API. The scarce resource is a rolling usage window with weekly caps, not per-token dollars. Anything that runs N agents at once drains the window N× simultaneously and trips rate limits.
- Figma is non-Enterprise. The Variables REST API is Enterprise-org-only (same wall as Code Connect, ADR-002 amendment). Variables are readable only via the Plugin API (MCP), interactively.
- The charter forbids continuous/always-on agents. Automation that recurs is a script or a GitHub Action; agents are bounded, developer-triggered moments.
A blueprint was evaluated (“In-Demand, API-Driven Agentic Loop Architecture”) proposing: a hub-and-spoke router spawning 2–3 parallel worker agents, an asymmetric Sonnet-router / Haiku-worker model split billed per token, Figma variables pulled over REST into a frozen STATUS_QUO.md, and an adversarial verifier gating the output.
Three of its load-bearing assumptions are false for this setup:
- Parallel worker swarm — written for API billing where parallel runs cost “pennies.” On Pro it is the opposite of cheap: it burns the shared usage window fastest and is the quickest path to rate limits.
- Figma variables over REST — impossible on this plan; the endpoint is Enterprise-gated.
- Per-token cost tiering (
--model=haikuworker sessions via API) — assumes API credits the developer does not have; it sits on top of, not inside, the Pro subscription.
Its three good ideas are plan-independent and worth keeping: frozen state-file handoffs, an adversarial verification stage, and on-demand command-triggered execution.
Options considered for how to run the loop:
- Parallel multi-agent (blueprint as written) — 2–3 concurrent workers + verifier, API-billed.
- Sequential ≤2-agent loop in Claude Code — orchestrate in the main session, spawn exactly one fresh subagent for adversarial review; deterministic work stays
npmscripts; context handed off via frozen files. - No loop — keep the single-shot
/component-scaffoldprompt; add no verification stage.
Decision
Option 2 — a sequential, at-most-two-agent verified loop, run on Claude Code subagents (no API billing), with frozen-file handoffs.
/component-loop <Name> runs bounded stages:
- Sense (script, no AI) —
npm run sense:component <Name>writes.claude/handoff/runs/<Name>.snapshot.jsonfrom the committed frozen-memory files (airtable-governance.json,token-usage.json,figma-variables.json). No live API call. - Scaffold (main session) — reuses the
/component-scaffoldmoment, fed only the snapshot + schema + a template component. - Gate (script) —
npm run metadata:validate && npm run typecheck && npm run build; fail-fast bounces back to stage 2 with the error. - Adversarial review (one spawned subagent) — fresh, independent context running
/code-review+ lint + a11y; findings written to.claude/handoff/runs/<Name>.review.json. (This stage is what became theadversarialreview path, as distinct from the subagent-freein-sessionpath — ADR-010 amendment 2026-07-12.) - Fix + PR (main session) — apply findings, re-run the gate, open the PR. No agent-written code reaches
mainunreviewed.
Binding rules:
- Sequential, ≤2 agents. Main session + at most one reviewer subagent. The reviewer is spawned because a fresh context is what makes the review independent — that is the only justification for a second agent. No parallel workers.
- Frozen-file handoffs only. Stages communicate through committed/cached snapshots, never streamed raw data or per-stage live API calls.
- Deterministic work stays a script. Sensing, validation, typecheck, build are
npmscripts; agents do only what a script cannot. - Figma frozen memory is an MCP snapshot, not a REST pull (forced by the Enterprise gate); code remains the source of truth and the snapshot is a drift mirror.
Option 1 was rejected because its cost model inverts on Pro. Option 3 was rejected because an unverified single-shot scaffold pushes half-broken code to human review — the adversarial stage is the point of the pivot.
Consequences
- Predictable, bounded agent spend. A run touches one usage window sequentially, not a fan-out. The loop is interruptible and re-runnable per stage.
- The frozen-memory layer (Phase 8) is a prerequisite, not optional — without committed snapshots the stages would make live calls and the isolation guarantee collapses. ADR scope therefore couples to the
sensescripts. - Lint and a11y debt lands inside stage 4 as CLI checks, rather than as separate manual passes — the pivot pays down that debt as a side effect.
- Lite-agentic charter preserved. Loop ≠ continuous loop: it runs once on a trigger and stops. The “no always-on watcher” rule (CLAUDE.md, Agentic moments) still holds.
- Trade-off accepted: sequential is slower in wall-clock time than a parallel swarm. On Pro that is the correct trade — wall-clock is cheap, usage-window headroom is not.
- Trade-off accepted: Figma drift detection depends on a human remembering to refresh
figma-variables.jsonvia the MCP. There is no scheduled pull to lean on; this is inherent to the non-Enterprise plan, not a gap to fix later. - Revisit if the plan changes (Figma Enterprise would unlock REST sensing) or if a single reviewer subagent proves insufficient for stateful components like
Accordion— at which point a second sequential review pass is preferred over going parallel.
Amendment (2026-06-22) — Behavioral a11y tier in the gate and the review
ADR-008 adds a Tier-2 behavioral a11y check (Vitest + Testing Library + vitest-axe, jsdom), gated to
interactive components. It folds into this loop at two points:
- Stage 2 (gate) gains
npm run a11y:coverage && npm run a11y:testafter the build.a11y:coveragefails fast if an interactive component lacks its<Name>.a11y.test.tsx;a11y:testruns the behavioral assertions. Non-interactive components (e.g. Badge) are a no-op — the gate is complexity-scoped, so the Badge pilot path is unchanged. - Stage 3 (adversarial review) now judges, for interactive components, whether the a11y test
covers the contract — every metadata
keyboardInteraction, the toggling state attribute, focus movement, APG semantics. The deterministic gate proves the test passes; the reviewer judges whether it tests the right things. This makes “lint and a11y debt lands inside the loop” (a consequence above) concrete and gives the second agent a job the script can’t do.
The Phase 9 exit condition is unchanged but now has teeth: Accordion ships through the loop only when
its behavioral a11y test asserts the full Accordion/Disclosure contract and passes.
Amendment (2026-07-23) — Promoted to accepted: exit condition satisfied
The promotion condition was met and then some: Accordion shipped through the loop on 2026-07-09
with its behavioral a11y contract asserted, and the committed run ledger
(.claude/handoff/run-ledger.json) now records 12 completed runs across the component set.
Downstream docs (docs/06-agentic-moments.md, docs/11-self-improving-loops.md) have treated the
loop as settled fact since; this amendment closes the loop on the ADR’s own status so the citation
trail no longer dead-ends on proposed. No change to the decision or its binding rules.
Amendment (2026-10-09) — The reviewer is risk-triggered, not always-on
The harness ablation (scripts/harness-ablation/results.md, 27 runs) measured the loop against context alone. The full loop never beat context alone on the product-quality headline and cost 69% more per clean component. All 12 gate retries were metadata:validate compliance churn. The reviewer’s real catches (forced-colors handling, APG keyboard test gaps, type narrowing) were concentrated in interactive components; on display-only Badge its main fix repaired a contrast miss the loop had created itself. ADR-029 records the full evidence and its limits (N = 3, one discriminating task).
Stage 4 changes:
- Interactive components keep the adversarial subagent, unchanged. “Interactive” uses the derivation
scripts/a11y-coverage.jsalready applies for the Tier 2 a11y gate (ADR-008):component.type ∈ {interactive, input}, an interactive ARIA role, or a widget keyboard contract. One shared function backs both, exposed asnpm run component:risk -- <Name>. - Every other component takes the in-session path (ADR-010 amendment 2026-07-12):
/code-reviewon the diff, then the gate, then the PR, recorded asreviewPath: in-sessionso the run ledger keeps the two paths comparable. - Overrides:
--reviewforces the subagent,--no-reviewskips it.--evalkeeps the reviewer always-on so the ablation’s Arm 2 stays re-runnable.
The binding rules stand. Sequential, ≤2 agents still holds; the second agent is now conditional. The Accordion revisit condition above becomes one of ADR-029’s revisit triggers: the pre-registered Accordion stretch task tests whether the reviewer earns its cost where it is still spent. Shipped 2026-10-09: /add-component Stage 3 runs npm run component:risk -- <Name>, and the display path records path: "standard". run-ledger.json entries now carry path and risk, so reviewer and no-reviewer runs can be compared per tier.