On this page

adas-subagent — coding-agent-era ADAS port to AgentCanvas

Framing note. Upstream ADAS (Hu et al. 2024) lives in the reasoning module = stateless LLM call paradigm — the only option in 2024. adas-subagent proposes reasoning module = tool-augmented Claude conversation with context management as the coding-agent-era replacement. This is the primary contribution; "faithful 1:1 reproduction" is not the goal. Several paradigm-independent elements (3-call Reflexion structure, Reflexion_prompt_1 / 2 text, bootstrap_CI algorithm, archive contract) are preserved verbatim — the "verbatim" labels in tables below are accurate at that per-element level. Phrases like "preserves upstream's X" should be read as "structurally accurate translation of paradigm-independent element X," not as a 1:1 fidelity claim about the overall pipeline.

Port of Hu et al. 2024, "Automated Design of Agentic Systems", to the AgentCanvas / coding-agent setting. The meta-LLM iteratively proposes graph + nodeset edits against a target graph; each generation's design intent, structural snapshot, and bootstrap-CI fitness are appended to an archive that drives the next generation's prompt.

Structural delta vs upstream: each of upstream search.py's get_json_response_from_gpt_reflect calls (the 3 Reflexion rounds in proposer and the retry-on-smoke-fail call in implementer) becomes one Agent({subagent_type: "general-purpose", ...}) spawn — a fully tool-augmented independent Claude conversation. This preserves ADAS's per-call sampling diversity (3 independent samples per propose, not one autoregressive trace) while gaining the "Claude is the meta-LLM" convenience (no external API key, full tool access on every sample). See § 1 ("One iter = one Claude conversation + N independent sub-agent samples") for the contract, and the "What's intentionally adapted" table for the two adaptations sub-agent invocation forces (no pinned stateless-API params; role: assistant history rendered as text in sub-agent prompts).

Cross-variant contract: .claude/commands/architect/_common/files-contract.md defines the shared iteration files (run-dir layout, resolve protocol, {graph}.yaml schema, edit whitelist, backend API). adas-subagent reuses it for evidence storage; this README only documents the deltas specific to adas-subagent.

This variant's concrete file set is declared in config.yaml § manifest — every file it writes, classified into a global file-type, with purpose / schema / access (per _common/files-contract.md § 4). That manifest is the single source of truth for file identity; this README narrates only rationale.

Three structural contracts (deltas vs adas v1)

1. One iter = one Claude conversation + N independent sub-agent samples

The loop skill owns the main Claude conversation for an entire iter; that main conversation invokes proposer, implementer, and evaluator as same-conversation phases (no nested skill conversations). The shared msg_list survives across all three phases.

Inside proposer and implementer, each of upstream search.py's get_json_response_from_gpt_reflect calls becomes one Agent({subagent_type: "general-purpose", ...}) invocation — a fully tool-augmented Claude sub-agent, not a stateless API call and not a Claude thinking-pass. Specifically:

Maximum sub-agent invocations per iter = 3 propose + 3 debug retry = 6. Minimum = 3 (propose with Smoke passing first try).

Why sub-agents and not Claude's own thinking-pass? A single Claude pass is one autoregressive sample no matter how many "rounds" it narrates internally; KV cache is shared across the rounds, so "diversity from independent sampling" is lost. Upstream ADAS's 3-call Reflexion gets its quality-diversity behavior from 3 independent samples at temperature 0.8 — Agent(...) spawns restore this property because each spawn is a fresh Claude. Tool access on each sub-agent is the second half of the contract: this upgrades "stateless gpt-4o call" to "tool-augmented Claude conversation" (the modernization framing), which only pays off if sub-agents actually use their tools (Read, Grep, Bash) to ground proposals in log.jsonl / archive / source.

This matches upstream search.py's single-msg_list semantics (search.py:183–223) at the textual-content level; the role: assistant tagging upstream gets is approximated by rendering prior outputs as text inside the sub-agent's user prompt (Agent tool takes only a single prompt string).

Adas v1 split proposer/implementer/evaluator across separate Claude conversations AND collapsed each Reflexion round into a single thinking-pass — losing both the reflection chain and the sampling diversity. Adasv3 fixes both: one conversation envelope (for msg_list continuity) AND independent sub-agent spawns (for diversity).

2. archive.jsonl is independent of iter dirs

Located at outputs/design_runs/adas-subagent/{graph}/v{N}/archive.jsonl. Not derived from iter dirs. Each entry is self-contained (graph_summary is rendered structurally at append time). The iter dir holds evidence (graph snapshot, metrics, exports) for reproducibility; archive.jsonl is the meta-LLM's curated working memory.

Entry schema (see HTML "Archive contract" §):

{
  "generation": int | "initial" | "reference",
  "iter_id": "iter_N" | null,
  "name": "<meta-LLM-given>",
  "thought": "<verbatim from proposer's final LLM output>",
  "graph_summary": { "nodes": [...], "wires": [...], "loop": {...} },
  "diff_narrative": "<plain-text What-changed>",
  "fitness": "95% Bootstrap CI: (lo%, hi%), Median: x%" | null
}

Pre-append cleanup: strip reflection field (verbatim upstream search.py:232–235; debug_thought is no longer in the schema — implementer's retry sub-agents edit the overlay natively and return {edit_summary, extra_targets}).

3. Two-tier evaluation

Smoke and Performance each need their own {graph}.yaml profile entry: - smoke_<graph>episode_count=5, worker_count=1, fixed episode_indices (deterministic for debug reproducibility) - perf_<graph>episode_count=100, worker_count=4–8

5 skills

Skill Role Invoked by
/architect:adas-subagent:understand P0 — load context (files-contract + concept/contract docs + graph state). Read-only. manual (or loop P0)
/architect:adas-subagent:loop Orchestrator. Owns the per-iter Claude conversation. Drives the 3 worker skills, Atomic Writer, archive append, termination. user (entry point)
/architect:adas-subagent:proposer 3-call Reflexion (analyze helper → propose → R1 → R2). Outputs staging proposal.md. loop (each iter)
/architect:adas-subagent:implementer native edit (sub-agent realizes the # Change spec) → Smoke → classify → debug retry ≤3. SKIP on exhaust. loop (each iter, after proposer)
/architect:adas-subagent:evaluator Method-free eval runner — experiment:run perf eval → neutral staging metrics.json (acc_list + primary_metric_value + secondary_metrics). bootstrap_CI / fitness_str are added later by loop's Atomic Writer (via lib/helpers.py:bootstrap_confidence_interval). loop (each iter, after implementer; also iter_0 baseline)

Library + data

Load-bearing Python helpers and seed data live alongside the skill markdown — skills import these instead of inlining the logic.

Path Purpose
lib/helpers.py bootstrap_confidence_interval (fitness_str), render_graph_summary (archive entries), build_acc_list_from_export, write_evaluator_staging, atomic_commit (Atomic Writer 3-step transaction), append_reference_seed, update_trace_md, append_lineage_md, render_backend_llm_cheat_sheet (proposer step 3.5)
data/reference_seeds.json The 7 ADAS reference patterns (COT, COT_SC, Self-Refine, LLM Debate, Step-back, Quality-Diversity, Dynamic Role Assignment) injected into a fresh archive.jsonl at pre-seed time.

Overlay seeding + the §7 edit whitelist are handled by the shared _common/lib/overlay.py — the typed graph_edits patch applier was retired 2026-05-20 (the implementer sub-agent edits the overlay natively; serialising intent into an op enum for a deterministic replayer was an ADAS-era vestige).

Skills import these by adding lib/ to sys.path (no package init — these are scripts, not a Python package):

import sys
sys.path.insert(0, ".claude/commands/architect/adas-subagent/lib")
from helpers import bootstrap_confidence_interval, atomic_commit

These files are tracked in git so a fresh Claude session resuming a compacted conversation can locate them directly from README.md and the per-skill steps.

Run-dir layout (delta vs files-contract)

Standard iter dir per files-contract, plus these adas-subagent-specific files:

outputs/design_runs/adas-subagent/{graph}/v{N}/
├── archive.jsonl                # NEW — per-vN, append-only
├── trace.md                     # standard
├── lineage.md                   # standard
├── iter_0/                      # baseline (no proposer/implementer)
│   ├── graph.json
│   ├── active_workspace/        # complete mutation set vs frozen (empty/absent for iter_0)
│   ├── metrics.json             # standard + new "fitness_str" top-level
│   └── (no proposal.md, no debug_log.md — iter_0 has no design)
├── iter_1/                      # first evolved gen
│   ├── parent.txt
│   ├── proposal.md              # NEW — replaces adas v1's analysis.md + revision.md
│   ├── graph.json               # convenience copy of active_workspace/graphs/{graph}.json
│   ├── active_workspace/        # bootstrapped from parent's active_workspace, layered with this iter's edits
│   ├── debug_log.md             # NEW name (was adas v1's debug_log.md too)
│   ├── metrics.json             # with fitness_str
│   ├── summary.csv, export.json
│   └── (no analysis.md, no revision.md, no report.md — folded into proposer/loop)
├── .staging/                    # NEW — transient pre-commit dir
│   └── iter_n/                  # populated by proposer/implementer/evaluator; mv to iter_n/ on success, rm -rf on SKIP
└── .loop_state/                 # standard

Termination

What this variant does NOT do

Paradigm-independent structural anchors preserved verbatim

adas-subagent element Upstream anchor Verbatim?
3-call Reflexion (propose / R1 / R2) search.py:188, 194, 198 Yes (call sequence) — implemented as 3 independent Agent(...) sub-agent spawns
Reflexion_prompt_1, Reflexion_prompt_2 mmlu_prompt.py:497, 525 Verbatim text (only "code""patch" field rename)
Failed iter not in archive search.py:202, 225 (n -= 1; continue) Verbatim semantics
bootstrap_confidence_interval(acc_list, 100000, 0.95) utils.py:31 Verbatim algorithm
fitness_str = "95% Bootstrap CI: (lo%, hi%), Median: x%" utils.py:76 Verbatim format
del next_solution['reflection'] before archive append search.py:232–235 Verbatim semantics for the reflection field; debug_thought is no longer in the schema
get_prompt(archive) injects archive verbatim into [ARCHIVE] mmlu_prompt.py:535–541 Same shape (graph_summary in place of code)
get_reflexion_prompt(archive[-1]) separately echoes most recent mmlu_prompt.py:544–547 Verbatim semantics
Sampling diversity (3 independent samples per propose; up to 3 more per debug retry) search.py call sites at temperature=0.8 Equivalent semantics via Agent(...) spawns (each spawn is one independent Claude sample). Upstream's specific temperature=0.8 / response_format=json_object / max_tokens=4096 / model=gpt-4o-2024-05-13 no longer applies — those are stateless-API knobs; sub-agents are not stateless API calls.

What's intentionally adapted (not verbatim)

Quick reference