fix(opencode): teach orchestrator about subagents and enforce on-disk artifacts

Two related orchestration failures from recent runs:

1. An orchestrator missed the multi-agent concept entirely and produced
   reviews / implementations itself instead of dispatching @check / @make.
   The workflow described phases as "Dispatch @<name>" everywhere but
   never explained who the cast was, what "dispatch" meant, or that the
   orchestrator (agent: build) is distinct from the subagents.
2. Another orchestrator dispatched @test pointing at a $RUN_DIR/task-N.md
   that it never wrote — the file-write instruction in Phase 5 was a
   single bolded sentence inside a paragraph, easy to skim past, and
   nothing checked artifact existence before dispatching.

Adds a top-level "Roles & Dispatch" section between the parse line and
Run Artifacts. It establishes the multi-agent model, lists the cast
(@check / @simplify / @test / @make / @pm) with one-line role and
permission notes, defines "Dispatch" as a tool call (not a role-play
instruction), and lists three anti-patterns the orchestrator must
avoid (acting as a subagent, skipping a dispatch, paraphrasing
artifacts instead of letting subagents read them from disk).

Restructures Phase 5 as five explicit numbered steps. Step 4 mandates
writing each task to $RUN_DIR/task-<N>.md and verifying with test -f;
step 5 requires dropping inline copies once the file is the source of
truth. The phase is "not done" until every task file exists on disk.

Adds a row to Dispatch Hygiene's Pre-Dispatch Validation table that
requires test -f verification of any artifact path the dispatch
references; missing files route back to the producing phase.
This commit is contained in:
Harald Hoyer 2026-05-07 19:04:08 +02:00
parent 25f4c6f179
commit 236b4d2470

View file

@ -18,6 +18,29 @@ Parse `$ARGUMENTS`: the first whitespace-separated token is the issue ID, an opt
---
## Roles & Dispatch
This is a **multi-agent** workflow. There is one orchestrator (you, running in `agent: build` mode per this file's frontmatter) and a cast of specialised subagents that the orchestrator dispatches at each phase. **The orchestrator coordinates; subagents do the work.** The orchestrator does not write production code, write tests, or play any subagent's role — it plans, dispatches, merges findings, edits its own artifacts under `.workflow/`, and commits.
**The cast** (each defined as a separate agent file under `config/opencode/agents/<name>.md`):
| Subagent | Role | Notable constraints |
|---|---|---|
| `@check` | Reviews plans and code for risks, correctness, testability. Returns `ACCEPTABLE` / `NEEDS WORK` / `BLOCK`. | Read-only — no write / edit / bash. |
| `@simplify` | Reviews for unnecessary complexity. Advisory recommendations. | Read-only. |
| `@test` | Writes failing tests for a task spec, verifies RED, hands off to `@make`. | May only modify test files / `#[cfg(test)] mod` blocks. Bash sandboxed to test runners. |
| `@make` | Implements a single task spec. Verifies acceptance criteria. | May only modify files listed in the task spec. Bash sandboxed to language toolchains; no `git`, network, `cd`. |
| `@pm` | Reads/updates `TODO/` issue files. | May only modify `TODO/` contents. Bash sandboxed to `git show` / `git ls-tree` / `ls`. |
**What "Dispatch" means here.** Every "dispatch `@<name>`" in the phase descriptions is a call to opencode's subagent / task invocation tool with that agent name. Each dispatch starts a **fresh context**: the subagent has no memory of prior phases, no view of this orchestration, and no access beyond what its own file declares. The subagent receives only what the dispatch prompt provides — typically an absolute path to a file in `$RUN_DIR` plus a small per-dispatch context block.
**Anti-patterns to avoid:**
- Performing a subagent's work in the orchestrator's session ("I'll think like `@check` for a moment and produce the review myself"). Every `@<name>` reference is a tool call, not a role-play.
- Skipping a dispatch because the orchestrator "could just do it." The agents enforce permission boundaries the orchestrator (in `agent: build` mode) does not have.
- Paraphrasing a subagent's output into the next dispatch's prompt instead of letting the next subagent read the on-disk artifact directly.
---
## Run Artifacts
The orchestrator writes plan and task-spec artifacts to a per-run directory in the worktree. Subagents read these by absolute path rather than from inline prompt text. This keeps dispatch prompts small, eliminates paraphrase drift between dispatches (`@check` and `@simplify` see the same plan byte-for-byte), and gives Dispatch Hygiene's Finalized-Text Rule a physical anchor — the file *is* the final version.
@ -159,6 +182,7 @@ Scan the artifact and reject (revise, retry) if any of the following are present
| Code blocks longer than ~5 lines that draft the answer | Violates No-Implementation-in-Plan-or-Spec. Trim to structure (signature + "current state" only). |
| Two versions of the same code, "actually let me correct…", or open questions | Violates the Finalized-Text Rule. Redo the artifact. |
| Test bodies inside `@make` specs when tests are coming from `@test` | Duplicates the TDD handoff. |
| Artifact path referenced in the dispatch (e.g. `$RUN_DIR/plan.md`, `$RUN_DIR/task-<N>.md`) but the file isn't on disk | The subagent will fail to read it and either error or fabricate context. **Verify with `test -f "<path>"` before every dispatch.** If missing, go back to the phase that produces it (Phase 3 for `plan.md`, Phase 5 for `task-<N>.md`) and write the file before retrying. |
If any check trips, **do not dispatch.** Fix and re-validate. Repeated trips on a single task signal a Phase 5 split problem — go back and split.
@ -189,7 +213,15 @@ Reviewers should evaluate testability:
## Phase 5: Split into Tasks
Break the approved plan into discrete tasks. **Write each task to `$RUN_DIR/task-<N>.md`** (1-indexed: `task-1.md`, `task-2.md`, …). Phase 6 (`@test`) and Phase 7 (`@make`) read these files by absolute path.
**The output of this phase is one file per task at `$RUN_DIR/task-<N>.md`** (1-indexed: `task-1.md`, `task-2.md`, …). These files are the source-of-truth that Phase 6 (`@test`) and Phase 7 (`@make`) read by absolute path. **No file written = no dispatch in later phases.** If you skip the file-write step, every downstream dispatch will reference a non-existent path and fail.
Steps:
1. Break the approved plan into discrete tasks (see Split Heuristic and task-size guidance below).
2. For each task, draft the task spec covering the fields in the table below.
3. Apply **Dispatch Hygiene** (above) to each draft.
4. **Write each finalized spec to `$RUN_DIR/task-<N>.md`.** After writing, verify with `test -f "$RUN_DIR/task-<N>.md"` for every N. Phase 5 is not done until every task file exists on disk.
5. Drop your inline copies of the task drafts. From this phase onward, the file is the only source of truth — if you need a task spec later, read it back from disk.
Each task file must contain: