fix(research): restore subagent fan-out, record that a skill body and its allowed-tools must agree
research instructed "spawn one subagent per URL" while its allowed-tools granted no spawn tool, so it silently degraded to serial fetches. Three other skills spawn subagents without trouble because they declare no allowed-tools. The defect was the mismatch, not the spawning. ADR-0027 records the agreement rule. research drops allowed-tools and gets its steps 4-5 fan-out and the orchestrator-writes gotcha back (1.0.1 -> 1.1.0). Closes #116 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EGHFJextYtVQseaHPDDhxB
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# A skill's body and its `allowed-tools` must agree — a spawning step needs a spawn tool or no list
|
||||
|
||||
**Status:** Accepted (2026-09-21)
|
||||
|
||||
`plugins/bin/.apm/skills/research/SKILL.md` once told the agent to "spawn one subagent per URL"
|
||||
while its `allowed-tools` granted nothing that spawns. `WebFetch` was granted, so it never
|
||||
hard-failed: it degraded to serial fetches in the orchestrator's own context, and the "in
|
||||
parallel" wording, the page cap and the "subagents summarise, orchestrator writes" gotcha all
|
||||
quietly stopped meaning anything. The #99 retrofit rewrote steps 4 and 5 as honest serial reads
|
||||
with a real page cap and per-page reduction to notes (#116).
|
||||
|
||||
The defect was a **mismatch between what the body instructs and what `allowed-tools` permits**. It
|
||||
was not that a skill spawned subagents. Three skills spawn today and work: `write-docs` (Reader
|
||||
Testing sub-agent), `improve-codebase-architecture` (`Explore`, and 3+ parallel sub-agents in
|
||||
`references/interface-design.md`) and `forge` (fork and clean-context subagents). None declares
|
||||
`allowed-tools`, so all inherit every tool, spawning included. `research` was the only one that
|
||||
both restricted the list and instructed spawning.
|
||||
|
||||
**Decision: a skill may instruct spawning subagents, provided its `allowed-tools` agrees with its
|
||||
body.** Either:
|
||||
|
||||
- omit `allowed-tools`, so the skill inherits every tool on every target; or
|
||||
- list the spawn tool — only once its per-target name is known, since `allowed-tools` is a flat list
|
||||
and `claude`, `copilot` and `codex` name it differently.
|
||||
|
||||
A step that needs a tool the list does not grant must be rewritten as a step that does not need it.
|
||||
That is what the #99 retrofit did to `research`, and it stays correct until one of the two options
|
||||
above is taken.
|
||||
|
||||
Fan-out is not confined to agents. `CONTEXT.md` says a plugin-scope agent *delegates to skills*
|
||||
because it cannot disclose to itself; it does not say skills may not delegate.
|
||||
|
||||
## Consequence for `research`
|
||||
|
||||
Its fan-out is restored, and `allowed-tools` is dropped to do it (version 1.0.1 → 1.1.0). That is
|
||||
the first of the two options above; the second is unavailable until the per-target spawn tool names
|
||||
are known.
|
||||
|
||||
The cost is stated rather than hidden: `research` fetches arbitrary web pages, and inheriting every
|
||||
tool widens what an injected page could reach for. Two things bound it. The subagents only read and
|
||||
summarise, and the orchestrator alone writes files, so the write surface is unchanged in intent. And
|
||||
the least-privilege list was never enforceable across targets anyway, since it could not name a
|
||||
spawn tool. If a per-target form of `allowed-tools` appears, restore a list that includes the spawn
|
||||
tool.
|
||||
|
||||
Rejected: banning spawning in skills (contradicted by three working skills, and unsupported by
|
||||
`CONTEXT.md`), and guessing a per-target spawn tool name in `allowed-tools` (no per-target form
|
||||
exists, and a wrong guess reproduces the defect silently).
|
||||
Reference in New Issue
Block a user