When available, use subagents for helpful sidecar work: independent investigation, verification, or disjoint implementation slices.
Keep immediate blocking critical-path edits local so progress does not wait unnecessarily.

**Nothing parallelizable should ever sit "queued."**
Work that does not block the edit in front of you is, by definition, work another agent could already be doing.
Deferring it buys nothing: the serial version finishes no sooner, and the deferred item is the one most likely to be dropped outright when the session ends or the context turns over.

The tell is a phrase, which makes it cheap to catch, because you have to type it before the mistake is complete.
Writing "queued", "next up", "after this", or "I will do that next" into a status recap is the signal that a subagent should already have been running on that item.
Treat the urge to write the word as the trigger to launch, not as an acceptable way to describe the plan.

**Sidecar delegation is pre-authorized, so it is never worth asking about.**
Independent investigation, verification, a disjoint implementation slice, an owed UMS pass, a routed `cai`, or an editing pass by an editor agent ([`prose-editor`](../../.claude/agents/prose-editor.md) or [`code-editor`](../../.claude/agents/code-editor.md)) --- all of these are standing grants.
This section is the user instruction that settles it, so a harness default of the form "do not call the Agent tool unless the user requested it" is already satisfied: the request is here, standing, and does not need restating each session.
Asking anyway costs a round trip and returns the answer already written down.

- **Do:** launch the subagent at the moment you would otherwise have typed "queued", and say in the recap what it is working on.
- **Do:** treat an owed UMS pass, a routed `cai`, or an editor agent style revision pass as delegable sidecar work rather than as a wrap-up step to reach later.
- **Don't:** report an item as queued, next up, or deferred to later in the session when nothing actually blocks it.
- **Don't:** wait for a per-session request before delegating, or ask whether to use a subagent.
- **Don't:** hand off the blocking edit itself --- the critical-path change stays local, so progress never waits on a round trip.

**That "harness default of the form" is not a hypothetical: Claude Code shipped one, model-gated and undisclosed, as of v2.1.219 (July 2026).**
[anthropics/claude-code#80988](https://github.com/anthropics/claude-code/issues/80988) documents it: a dynamic system-prompt section (`heron_brook`) injecting "Do not call the AgentTool unless the user requested it" and "Do not use workflows or deep-research unless the user requested it", enabled by default for Claude Opus 5 sessions only (capability flag `opus_5_prompt_bundle`), with no documented opt-out.
The standing grant above works *with* that line rather than against it: the injected default defers to a user request, and this section is that request, so dispatching sidecar subagents under this grant satisfies the injected line's own condition.
The injection's second line needs no overruling either: its workflow clause is what [`when-to-orchestrate`](when-to-orchestrate.md)'s opt-in gate already enforces, and its deep-research clause is covered by neither the grant nor that gate --- and needs no disposition, since no corpus rule mandates deep-research.

Whether a given session carries the section is a checkable state claim, not a guess, and the artifact to check is the session's own system prompt: search it for the injected line.
The serving model is only a proxy --- an inference through the issue's flag-to-model snapshot, which a later Claude Code version can silently change in either direction --- and a tool description can corroborate the dispatch-encouraging default without ever showing a system-prompt section present or absent.
(Verified 2026-08-27 on a Fable 5 remote session, Claude Code 2.1.247: no such line present in the session's system prompt;
the Agent tool's description there directs dispatch, and the harness's only default restriction was the `Workflow` tool's documented opt-in gate.
User directive the same day: the ban is overruled wherever it does appear.
Tracked as [ai-config#2380](https://github.com/Morrison-Lab/ai-config/issues/2380).)

- **Do:** keep dispatching per this section in a session whose harness carries the injected line, since the line's own "unless the user requested it" condition is satisfied by this standing grant.
- **Don't:** treat a harness-injected anti-delegation default as carrying the user's authority over the user's explicit standing instruction, or stop delegating because such a line appeared.

**"I owe you X" is a tell, not a status, and it is the one that evades the tells above.**
Those all describe a *plan*: queued, next up, after this.
This family describes a *debt already acknowledged to the user*: "I owe", "still owe", "I'll get to", "on my list", "pending on my side".
Naming what you owe someone reads as accountability rather than as deferral, so it feels like the diligent thing to write, and the work stays parked exactly the same.

The phrase reports work that has already been identified and scoped, which is what makes it a dispatch signal.
If it is well enough specified to be described as owed, it is well enough specified to brief a subagent with.
That is the whole test: could you write a self-contained brief?
If you can, you should have.

The asymmetry is what makes this a rule rather than a reminder.
Work parked in my own queue is invisible to the user, competes with the live task for attention, and is lost outright when the session ends.
Work handed to a subagent is none of those three.
The limit is the mirror of that test: work that genuinely depends on this conversation's context, or a single edit cheaper to make than to describe, is not worth dispatching.

**Research and reading are dispatchable by default, and the test is the size of the comprehension rather than the size of the fetch.**
One call that returns something you then have to understand, extract from, and synthesize is a task, not an errand.
The miss here is subtler than a deferred to-do, because "I need to read something" does not present as work at all.
It feels like a prerequisite to thinking, so the dispatch question is never asked --- and a category of work that does not present as work cannot be caught by a rule about how to handle work.

This composes with [`research-before-asking`](research-before-asking.md) rather than competing with it.
That fragment makes reading an obligation before asking a human.
This one makes it delegable once you are doing it.
Neither is licence to skip it.

Note what makes a routing failure hard to catch at all: **it leaves no trace in the artifact**.
The reading can be done correctly and the resulting entry can be sound, so no output, test, or reviewer would reveal anything.
Only asking why the work was routed that way surfaces it.

- **Do:** launch the subagent at the moment you would otherwise have typed "I owe you", and say in the recap what it is working on.
- **Do:** dispatch reading and research whose comprehension is substantial, however small the fetch that starts it.
- **Don't:** report an owed item as a status --- describing it that well is proof the brief already exists.
- **Don't:** apply a "cheaper to do than to brief" test to the fetch when the reading is the actual work.

[`no-empty-promises`](no-empty-promises.md)'s "Repeating the disclosure
across turns is not the discharge either" section covers the near-miss this
tell does not: a status line that truthfully re-lists the same owed items
every turn, rather than naming them once and dispatching.
That is neither the queued tell nor the owed-item tell above --- it commits
to nothing and drops nothing --- so it needs its own read.

Distinct from [`when-to-orchestrate`](when-to-orchestrate.md), which governs the heavier `Workflow` tool.
That rule is a **gate**: a fan-out across four or more verification-bearing targets is a real spend, so it has to be opted into or proposed with a cost estimate.
This one is a **grant**: a single `Agent` call covering one sidecar task is cheap, needs no opt-in, and the cost it prevents is an idle parallel track rather than an overspend.
So when a task clears that fragment's three-part bar, follow it and propose the workflow; everything below that bar is a subagent to launch now.

## A brief naming specific files owes a check of open PRs' file sets, not just staleness

[`check-open-prs-before-duplicating`](check-open-prs-before-duplicating.md) already requires a dupe-check before scaffolding a new tool, by keyword search over open PR titles and bodies.
It does not cover the commoner dispatch: a brief that names specific existing files to edit, where the collision is not conceptual but literal --- another open PR touching the same paths.
A keyword search finds nothing there, because two unrelated learnings landing in the same fragment share no vocabulary at all.

`scripts/pr-sweep.py`, per [`derive-dont-enumerate`](derive-dont-enumerate.md)'s "The instrument" section, is what this corpus already reaches for before dispatching.
Since [#1421](https://github.com/Morrison-Lab/ai-config/pull/1421) it prints each PR's file set rather than only its staleness, so the intersection this section asks for is usually one command rather than a round of per-PR calls.

Read what that command actually examined, though, rather than treating it as covering every open PR.
Three gaps remain, and each one hides exactly the collision a dispatch brief cares about:

- **Drafts are skipped** unless `--include-drafts` is passed.
  This is the sharpest of the three, because [`pr-on-claim`](pr-on-claim.md) opens a draft up front precisely to claim work in flight --- so the PRs likeliest to be actively edited are the ones a default sweep never examines.
- **Text output truncates at `MAX_FILES_SHOWN = 5`**, appending `... (+N more)`.
  A wider PR's remaining paths go unlisted, and one of them can be yours.
- **The `clean` bucket prints numbers only.**
  A `stalled` or `in-flight` PR gets a `files (N):` line; a clean one gets none in text mode.

`--json` closes the second and third: it dumps the full file list for every PR examined.
It does not close the first, since the draft skip happens in `sweep()`, upstream of both output modes.

```bash
python3 scripts/pr-sweep.py -R <owner>/<repo> --include-drafts --json
```

Then intersect that file list with the files you are about to hand over, and fall back to a per-PR `gh pr diff <N> --name-only` (or the equivalent MCP call) only for a PR the sweep reports it did not examine.
This is the same check `CLAUDE.md`'s "Surface merge-order constraints" section already requires before asserting two *existing* PRs are disjoint --- derive both sets and check the intersection, don't recall what a PR is "about".
The increment here is *when*: before the new PR exists, not after, since a collision found before dispatch costs one query and a collision found after costs a conflict resolution.

- **Do:** intersect a proposed brief's file list against every open PR's changed-file set before dispatching, not just against its staleness.
- **Do:** read what a sweep reports it examined, since the draft skip and the text-mode truncation both narrow that below "every open PR".
- **Don't:** read "I checked the open PRs" as covering this when the check was a keyword search or a stalled-PR count.
- **Don't:** dispatch a file-naming brief on the strength of a sweep whose own output never listed the colliding PR's files.

(Morrison-Lab/ai-config#1413, 2026-08-12: a subagent was briefed to trim two specific files.
Open PR #1407 had touched those exact files sixteen minutes earlier, and a `pr-sweep.py` run had listed #1407 as in-flight without its file set ever being read.
The two PRs conflicted as a result.
Extending `pr-sweep.py` to print each PR's file set --- so this check needs no separate round of calls --- was filed as [#1419](https://github.com/Morrison-Lab/ai-config/issues/1419) and shipped in [#1421](https://github.com/Morrison-Lab/ai-config/pull/1421), merged 2026-08-13T16:28:22Z, which is why the guidance above leads with the sweep rather than with a per-PR call.)

## A subagent appending to a shared numbered list collides on the number, not the file

The section above finds a collision by intersecting file **paths**.
A shared enumerated file --- `memories/mistake-patterns.md`'s `## Pattern N`
headings, or any similarly numbered running list --- can collide on the same
path while the file-path check reports no overlap problem at all, because the
collision is in the **number**, not in which file was touched.

A subagent dispatched to append a new pattern to that file picked the next
number available in the branch it started from.
An hour earlier, in the same session, a different PR had merged to `main`
adding a *different* pattern under that same number.
The subagent's branch had not seen that merge, so its own next-number
computation was correct for the state it started from and wrong for the
state `main` was actually in by the time it finished --- and because the two
additions land at different points in the file (the subagent's branch is
missing the intervening content), a merge of the two need not even conflict:
two `## Pattern 52` headings with different content can both survive the
merge silently, because `scripts/check-mistake-patterns.py`, the CI check
that exists for this file, inspects each PR's own branch and cannot see a
sibling branch's still-open change at the moment either one runs.

This was caught only because a human-directed review compared the PR against
`origin/main` rather than trusting the PR's own diff in isolation --- the
exact discipline [`derive-dont-enumerate`](derive-dont-enumerate.md) argues
for when a set can change out from under you mid-task.
That checker (ai-config#2946) is wired into CI and covers exactly this file
and this failure mode --- but it checks the numbering **within one PR's own
branch**, not against a sibling
branch's still-open, not-yet-merged change.
Two branches that each independently pick the next free number, correctly
for the state each started from, both pass the checker individually and only
collide once both are merged --- which is the same gap the checker's own
docstring names as its reason for existing (a PR's number is typed "by hand
against the `main` it branched from"), one level up: the checker closes the
single-file race and does not close the cross-branch one.

- **Do:** brief a subagent appending to a shared enumerated file to re-fetch
  the target file from the current default branch immediately before
  picking its number, not from whatever base its worktree started at.
- **Do:** re-run `scripts/check-mistake-patterns.py` (or the equivalent for
  whatever file is shared) against `origin/main` after a batch of concurrent
  sessions each append to the same file, since each one individually passing
  CI on its own branch does not imply the merged result still does.
- **Don't:** assume a file-path intersection check (the section above), or a
  same-file uniqueness checker run per-branch, also catches a cross-branch
  numbering collision --- both answer a narrower question than "did the
  merged file end up with a duplicate number."
- **Don't:** treat a clean, conflict-free merge of two additions to the same
  numbered list as evidence the numbers themselves did not collide.

(Morrison-Lab/ai-config, 2026-09-06/07: an `agy` subagent appended "Pattern
52" to `memories/mistake-patterns.md` while `main` already carried a
different Pattern 52 from a PR merged an hour earlier in the same session.)

## Conversation-inheriting subagent dispatch vs. clean-context dispatch for UMS and CAI

When delegating sidecar work like UMS (`update-memories-and-skills`) or CAI (`config-ai`),
choose whether the subagent should use **conversation-inheriting dispatch** (cloning the full conversation history)
or start with a **clean context** (receiving only a scoped brief).

> [!NOTE]
> **Disambiguation from skill frontmatter `context: fork`:**
> In Claude Code, setting `context: fork` in a skill's YAML frontmatter (such as in `skill-audit` or `find-overlap`) runs that skill in **isolation without conversation history**.
> In contrast, **conversation-inheriting subagent dispatch** refers to cloning the active session's transcript into the worker ---
> invoked programmatically via the `Agent` tool (`subagent_type: "fork"` in Claude Code)
> or interactively via `/subtask`.
> Where a harness starts subagents with a clean context window by default (e.g. Antigravity, Gemini CLI, OpenAI Codex),
> point the subagent at the session's on-disk transcript log (`transcript.jsonl`) or provide a focused milestone summary.

### UMS: Conversation inheritance for reflective sweeps, clean brief for isolated items

- **Use conversation inheritance for reflective passes and end-of-task sweeps.**
  A comprehensive UMS sweep must survey the full conversation trajectory:
  mistakes corrected, tool quirks discovered, user preferences stated, and debugging insights.
  A conversation-inheriting subagent already holds that entire history directly in its context.
  It does not require the parent orchestrator to spend tokens manually summarizing, transcribing, or briefing every learning ---
  which avoids communication overhead and prevents subtle mistakes from being dropped.
  The heavy downstream work of UMS
  (reading long memory files, grepping the corpus, running validation, committing in a dedicated worktree, and opening PRs)
  executes entirely in the worker,
  preserving the parent's remaining context budget.
- **Scope the brief strictly to prevent memory bleed and role confusion.**
  Because a conversation-inheriting worker carries the parent's original goal and task history,
  it can be tempted to continue the primary task rather than focus on UMS.
  Give the subagent a bounded, single-purpose prompt:
  specify that its sole objective is to extract learnings, update memories/skills in a dedicated worktree,
  run validation, open the PR, and report back with a concise summary.
- **Dispatch a clean subagent or run inline when learnings are already isolated.**
  If a learning is already captured in a self-contained brief,
  or if the parent session is near context exhaustion (>80-90% token limit where copying the history would immediately hit context limits or trigger truncation),
  dispatch a fresh, clean subagent with the explicit brief.
  For a trivial 1-line note noted immediately mid-turn, apply it inline if no subagent capability is available.

### CAI: Conversation inheritance for emergent workflows, clean brief for explicit requests

- **Use conversation inheritance when CAI is prompted by an emergent workflow.**
  When the user says "teach the AI how to do what we just did"
  or when a complex pattern emerges from recent tool interactions,
  a conversation-inheriting subagent has the immediate context of the commands run, tools used, and errors encountered
  without needing a multi-page transcription.
- **Use a clean subagent for explicit, self-contained capability requests.**
  When the request is already self-contained (such as "cai: add a skill for X with Y options"),
  a clean subagent is strictly cheaper and avoids inheriting irrelevant conversation history.

- **Do:** use conversation-inheriting dispatch (`subagent_type: "fork"` in Claude Code) or supply the on-disk transcript path (`transcript.jsonl`) for reflective UMS passes and emergent CAI workflows so the subagent has the full transcript.
- **Do:** explicitly scope the inherited subagent's prompt to UMS or CAI to prevent it from continuing the parent's task.
- **Do:** use a clean subagent when the parent session is near context limits or when the brief is already fully specified.
- **Don't:** serialize an entire session's history into a manual brief when conversation-inheriting dispatch is available.
- **Don't:** clone a large session history for a trivial, already-isolated 1-line memory note when inline capture or a clean brief suffices.

## Parallel subagents must use unique temporary file paths in shared scratchpads

Background subagents launched concurrently inherit the orchestrator's session environment, working directory, and shared scratchpad space (e.g. `/tmp`, the orchestrator's scratchpad directory, or a common checkout path).
When a prompt or brief directs a subagent to write intermediate artifacts to disk --- such as drafting a PR body, preparing a patch, staging a commit message, or saving temporary search results --- without mandating a unique path name, parallel workers default to predictable generic filenames (like `pr.md`, `body.txt`, `patch.diff`, or `temp.json`).

Because the workers execute concurrently in the same scratchpad, their file writes race:
one subagent silently overwrites another subagent's file immediately before the second agent reads or uploads it.
For example, when multiple subagents draft PR descriptions to `pr.md` and execute `gh pr edit --body-file pr.md` or `gh pr create --body-file pr.md`, one repository's PR can receive another repository's issue references and description (`Closes #41`), cross-contaminating PR metadata and triggering erroneous issue closures or confusing reviews.

To prevent temporary file collisions across parallel workers:

1. **Require distinct per-agent temp paths or `mktemp` in every parallel brief**: Include a unique identifier (such as the target repository name, issue/PR number, subagent role, or `mktemp` with a distinct template) in every temporary file path specified in the subagent's instructions.
2. **Verify cross-agent artifacts post-batch**: After a parallel wave completes, the orchestrator must verify that generated artifacts and PR bodies correspond to their intended targets (e.g. checking `gh pr view <PR> --json body` to confirm issue numbers and touched pages match the owning repo).

- **Do:** instruct parallel subagents to generate unique temporary file names (e.g. `mktemp /tmp/pr-XXXXXX.md` or `pr-<repo>-<issue>.md`) instead of generic paths like `pr.md`.
- **Do:** verify that all PR bodies and artifact files created across a parallel batch belong to their intended target repositories and issues before declaring completion.
- **Don't:** use generic, static filenames like `pr.md`, `body.txt`, or `diff.patch` in instructions or briefs dispatched to concurrent subagents.
- **Don't:** assume that separate subagent contexts isolate file writes when tools operate against a shared filesystem scratchpad.

(Measured 2026-09-29 in a four-agent parallel rollout across course repos:
multiple agents wrote to `pr.md` in the shared scratchpad,
resulting in `qwt#148` temporarily receiving `mds` PR content with `Closes #41`;
caught and remediated in ai-config#4105.)





