or.2: orchestrator-agent design correction — no nested subagent dispatch
Claude Code categorically forbids subagents from spawning other subagents (code.claude.com/docs/en/sub-agents and the Agent SDK subagents page). The `or.1` architecture (and `pr.1` that ran on top of it) presumed a named-exception for `ailang-implement- orchestrator` to dispatch implementer / spec-reviewer / quality- reviewer / tester per task. That exception never existed at the platform level; the `Agent` tool was silently dropped from the orchestrator-agent's tool set at dispatch time. Architecture revised, doc-only: - Per-task phases (implementer → spec-compliance check → quality check) now run as sequential role-switches in the orchestrator- agent's own context, not as nested subagents. - The four role-files (implementer / spec-reviewer / quality- reviewer / tester) become phase reference files the orchestrator-agent consults at role-switch boundaries. - Boss-context offload preserved; fresh-per-phase context given up (the property that drove or.1's nested-dispatch design is not buildable in Claude Code). Files touched: - skills/implement/agents/ailang-implement-orchestrator.md (frontmatter, Iron Law, Phase 2/3 rewrite, rationalisations, red flags) - skills/implement/SKILL.md (frontmatter, Iron Law, sub-status vocabulary note, cross-references) - skills/README.md (Conventions: no more named exception; agent roster: role-files recast as phase references) - docs/journals/INDEX.md (or.2 entry appended) - docs/journals/2026-05-11-iter-or.2.md (new — full rationale) - docs/roadmap.md (P1 tool-wiring item removed; resolved as categorically-not-fixable) - docs/WhatsNew.md (user-facing correction entry appended) No code changes; no bench impact; CLAUDE.md untouched (no stale references to fix there).
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# iter or.2 — orchestrator-agent design correction
|
||||
|
||||
**Date:** 2026-05-11
|
||||
**Branch:** none (trivial-mechanical carve-out — doc-only revision, single commit on main)
|
||||
**Status:** DONE
|
||||
**Tasks completed:** N/A (doc revision, not a plan-driven iter)
|
||||
|
||||
## Summary
|
||||
|
||||
This entry corrects a factual error in the `or.1` architecture that
|
||||
surfaced during the first real dispatch (pr.1, same day). The
|
||||
revision lands as one direct-on-main commit because the change is
|
||||
doc-only: no code, no tests, no behaviour shift in any compiler
|
||||
crate. The Iron-Law mini-fix from earlier today (`origin/main` →
|
||||
`main`) is preserved unchanged — that fix remains correct.
|
||||
|
||||
### What was wrong
|
||||
|
||||
`or.1` and `pr.1` shipped the `ailang-implement-orchestrator` agent
|
||||
with frontmatter `tools: Read, Edit, Write, Bash, Glob, Grep, Agent`
|
||||
and an architecture that depended on the orchestrator-agent
|
||||
spawning further subagents per task (implementer → spec-reviewer →
|
||||
quality-reviewer → tester, each in fresh context). The architecture
|
||||
was framed in `skills/README.md` as "agents do not call other
|
||||
agents — one named exception" (the orchestrator-agent being the
|
||||
exception).
|
||||
|
||||
The exception never existed in Claude Code. The platform forbids
|
||||
**all** nested-subagent dispatch, categorically:
|
||||
|
||||
> "Subagents cannot spawn other subagents, so `Agent(agent_type)`
|
||||
> has no effect in subagent definitions."
|
||||
> — https://code.claude.com/docs/en/sub-agents
|
||||
|
||||
> "Subagents cannot spawn their own subagents. Don't include
|
||||
> `Agent` in a subagent's `tools` array."
|
||||
> — https://code.claude.com/docs/en/agent-sdk/subagents
|
||||
|
||||
The `Agent` tool was silently dropped from the orchestrator-agent's
|
||||
tool set at dispatch time (no error, no warning). The pr.1 run
|
||||
caught this empirically; the diagnosis closed the question today.
|
||||
|
||||
### Why the failure took the form it did
|
||||
|
||||
The Boss-Orchestrator (me) designed `or.1` on the false premise
|
||||
that the project's house rule "agents do not call other agents"
|
||||
was a local convention with a documented exception. In reality the
|
||||
house rule was a *restatement of an external constraint* — and the
|
||||
"named exception" was incompatible with the constraint itself.
|
||||
This is a verification-of-capability failure: the architecture was
|
||||
locked in before checking the platform docs for what subagents can
|
||||
do. The right order is the other way around. (Memorised as a
|
||||
feedback memory for future architecture decisions.)
|
||||
|
||||
### The corrected architecture
|
||||
|
||||
Same skill body, same skill triggers, same branch-and-merge
|
||||
discipline, same Boss-side end-report contract. What changes is
|
||||
*inside* the orchestrator-agent's run:
|
||||
|
||||
- Per-task phases (implementer → spec-compliance check → quality
|
||||
check) run sequentially as **role-switches in the orchestrator-
|
||||
agent's own context**, not as nested subagents.
|
||||
- The `ailang-implementer.md` / `ailang-spec-reviewer.md` /
|
||||
`ailang-quality-reviewer.md` / `ailang-tester.md` files now serve
|
||||
as **phase reference files** — the orchestrator-agent consults
|
||||
them at each role-switch to inhale the discipline of that role,
|
||||
but does not dispatch them as separate subagents.
|
||||
- The Boss-context offload property is preserved (the Boss still
|
||||
sees only the ≤500-token end-report; per-task chatter stays
|
||||
inside the orchestrator-agent's context).
|
||||
- The fresh-per-phase context property — which was the design goal
|
||||
that drove `or.1` to attempt nested dispatch — is **given up**.
|
||||
Pure role-switching inside one context is a weaker discipline
|
||||
than fresh-context-per-phase. The trade-off is named openly: the
|
||||
cost is the loss of fresh-eyes review per phase; the benefit is
|
||||
that the architecture is buildable in Claude Code at all.
|
||||
- The pr.1 "degraded mode" was not actually degraded — it was the
|
||||
only mode this agent can run in. The pr.1 journal's degraded-
|
||||
mode caveat is preserved as-is (historical record of how the
|
||||
constraint was discovered).
|
||||
|
||||
### Why option (A), not (B) or (C)
|
||||
|
||||
Three options were on the table:
|
||||
|
||||
- **(A)** Single-context inline phases (what landed) — preserves
|
||||
Boss-context offload, gives up fresh-per-phase context.
|
||||
- **(B)** Roll back to pre-or.1: Boss dispatches each per-task
|
||||
subagent itself. Preserves fresh-per-phase context, gives up
|
||||
Boss-context offload.
|
||||
- **(C)** Hybrid: orchestrator-agent for setup / branch / journal,
|
||||
Boss for per-task dispatches in between. Preserves both
|
||||
theoretically but multiplies moving parts.
|
||||
|
||||
(A) was chosen because the Boss-context offload is the scarce
|
||||
resource (user chat budget). Empirically, the pr.1 run with the
|
||||
plan template's verbatim per-task content blocks made fresh-per-
|
||||
phase context contribute little additional signal beyond a careful
|
||||
single-context phase loop. The decision was made by the user
|
||||
2026-05-11 after the diagnosis was presented; the rationale belongs
|
||||
in the user's hands, not the agent's.
|
||||
|
||||
### Files changed
|
||||
|
||||
- `skills/implement/agents/ailang-implement-orchestrator.md` —
|
||||
frontmatter (`Agent` removed from tools, description rewritten),
|
||||
Iron Law (sequential role-switches instead of fresh subagents),
|
||||
Phase 2.1 / 2.2 / 2.3 rewritten as inline phases, Phase 3 likewise,
|
||||
common rationalisations and red flags updated.
|
||||
- `skills/implement/SKILL.md` — frontmatter description, Iron Law,
|
||||
per-task sub-status mechanics note (vocabulary is preserved but
|
||||
now describes inline role-phase status), one new
|
||||
Common-Rationalisation row, Cross-references rewritten (role-files
|
||||
recast as phase references, with a "Why inline phases" subsection
|
||||
pointing here).
|
||||
- `skills/README.md` — Conventions: "Agents do not call other
|
||||
agents — one named exception" → "Agents do not call other agents"
|
||||
(the exception is gone, with an explanation of why). Agent roster
|
||||
rows for implementer / spec-reviewer / quality-reviewer / tester
|
||||
recast as phase references for the orchestrator-agent.
|
||||
- `docs/roadmap.md` — P1 entry "`Agent` tool wiring for the
|
||||
dispatched orchestrator-agent" removed (resolved as
|
||||
categorically-not-fixable).
|
||||
- `docs/journals/INDEX.md` — this entry appended.
|
||||
- `docs/WhatsNew.md` — user-facing correction entry appended.
|
||||
|
||||
### Files NOT changed (and why)
|
||||
|
||||
- `CLAUDE.md` — no stale references; the file does not encode the
|
||||
named-exception story.
|
||||
- `skills/implement/agents/ailang-implementer.md`,
|
||||
`ailang-spec-reviewer.md`, `ailang-quality-reviewer.md`,
|
||||
`ailang-tester.md` — these remain unchanged. Their content
|
||||
(discipline, reading list, output format) is correct in the new
|
||||
architecture; their *role* in the system has shifted from
|
||||
"dispatched subagent" to "phase reference for the orchestrator-
|
||||
agent". The shift is documented in the README roster row and the
|
||||
SKILL.md Cross-references; no in-file edits are required for
|
||||
correctness. A future cleanup iter may rewrite their frontmatter
|
||||
to drop the "subagent" framing if it begins to mislead — for now
|
||||
the cost of touching four files outweighs the marginal clarity
|
||||
gain.
|
||||
- `docs/specs/2026-05-11-implement-orchestrator-agent.md` and
|
||||
`docs/specs/2026-05-11-plan-recon-subagent.md` — specs are
|
||||
historical records of the design as conceived; they remain
|
||||
accurate to that moment. Drift between spec and the corrected
|
||||
architecture is acknowledged here, not by editing the specs.
|
||||
|
||||
## Concerns
|
||||
|
||||
None. The revision is deliberate and self-contained.
|
||||
|
||||
## Known debt
|
||||
|
||||
- **Phase reference files frame themselves as dispatched subagents.**
|
||||
`ailang-implementer.md` and its three siblings still read like
|
||||
files describing a subagent receiving a carrier from a dispatcher.
|
||||
In the corrected architecture, the orchestrator-agent IS the
|
||||
dispatcher and the worker for those phases. If the next
|
||||
`/implement` run shows the orchestrator-agent confused by this
|
||||
framing during phase reads, a follow-up cleanup iter rewrites
|
||||
the four files. Not pre-emptive — wait for an empirical signal.
|
||||
- **No verification ladder for the corrected design.** `or.1`'s
|
||||
verification ladder (items 1–5 in
|
||||
`docs/specs/2026-05-11-implement-orchestrator-agent.md`) was
|
||||
written for the nested-dispatch architecture. Items 1–3
|
||||
(Boss-side dispatch, branch creation, journal write) carry over
|
||||
unchanged. Items 4–5 (intentional BLOCKED, intentional re-loop)
|
||||
are weaker in the inline-phase world — re-loop is a same-context
|
||||
retry, not a fresh-context re-dispatch — and a new ladder entry
|
||||
for the inline phase-switch should be added the next time
|
||||
`/implement` runs against a non-trivial plan. Tracked
|
||||
informally; no ticket.
|
||||
|
||||
## Blocked detail
|
||||
|
||||
N/A — DONE.
|
||||
|
||||
## Commits
|
||||
|
||||
This entry's revision lands as a single trivial-mechanical commit
|
||||
on `main` (no iter branch, no plan template). The commit subject is
|
||||
`or.2: orchestrator-agent design correction — no nested subagent dispatch`.
|
||||
|
||||
## Stats
|
||||
|
||||
N/A — no orchestrator-agent dispatch (this is a doc-only revision
|
||||
made directly by the Boss-Orchestrator).
|
||||
@@ -8,3 +8,4 @@
|
||||
- 2026-05-11 — iter or.1: orchestrator-refactor → 2026-05-11-iter-or.1.md
|
||||
- 2026-05-11 — fieldtest canonical-type-names → 2026-05-11-fieldtest-canonical-type-names.md
|
||||
- 2026-05-11 — iter pr.1: plan-recon subagent → 2026-05-11-iter-pr.1.md
|
||||
- 2026-05-11 — iter or.2: orchestrator-agent design correction (no nested subagent dispatch) → 2026-05-11-iter-or.2.md
|
||||
|
||||
Reference in New Issue
Block a user