Lesson 4 of 6
Dispatch workers and verify handoffs
Run each worker in its own worktree, keep shared context small, bound its steps, and treat every “done” as an unverified claim until you re-run the checks.
You are the orchestrator — there is no agent message bus
OpenCode gives you pieces to run one worker well: subagents, the Task tool, child sessions, permissions, and steps. It does not schedule a graph, pass messages between runs, or decide when a task is accepted. There is no documented cross-agent message bus. Shared context moves through the repo and written files.
Keep context small. Project-wide rules go in AGENTS.md (commit it). The frozen interface lives in source plus the task brief. One task’s detail lives in TASK.md in that worktree. The full orchestration plan stays with you. Pasting the whole plan into every worker wastes budget and hides the contract. Assuming a worker remembers another worker’s decision is context starvation — contexts are separate. /init can create or update AGENTS.md; instructions in opencode.json can add extra files.
Dispatch one session per worker inside its worktree
Because subagents share the project directory they were started in, dispatch from inside each worktree, in separate terminals:
cd ~/agent-lab/tt-due && opencode --agent Agent-DUE
cd ~/agent-lab/tt-tags && opencode --agent Agent-TAGS
cd ~/agent-lab/tt-search && opencode --agent Agent-SEARCH
Non-interactive:
opencode run --dir ~/agent-lab/tt-due --agent Agent-DUE \
"Read TASK.md and implement it. Run the listed checks. Write HANDOFF.md. Do not edit files outside the contract."
--agent selects the agent. permission.task globs control which subagents a primary may launch; deny removes that subagent from the Task tool description. You can still @mention a subagent. Child sessions use Leader+Down / Right / Left / Up. Background subagent tasks (OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS) are experimental — do not depend on them. Watching three terminals is manual on purpose.
Run opencode agent list inside each worktree first; the supplied direct-session roles must show mode: all. Inspect TASK.md and the resolved config before dispatch. After a handoff, the learner runs the checks, approves the diff, and commits the changed feature/test/registration. Record that new commit SHA; never merge an uncommitted worktree.
Status has five honest states — “done” alone is banned
Use only: planned (not started); in progress (started, no result claimed); blocked (needs a decision — say what); done, unverified (worker stopped and handed off — not accepted); verified (you ran the acceptance checks and they passed). A status board that never says blocked is not being maintained honestly.
The handoff is a document, not a vibe. It must include: files changed and the commit id; contract deltas (or none); the exact commands and output, not a summary; a mandatory what I did not verify line; blockers and the requested next action. An empty “not verified” line is usually a lie by omission. If the worker truly covered every criterion, map each AC to a named check.
Bound every worker, then verify the diff yourself
Set steps on each agent (start at 20). When the cap hits, the agent must summarise and stop. Cap tools with permission. Cap which subagents it may launch with permission.task. Do not pass --auto on a write-capable worker: --auto approves anything not explicitly denied. Keep ask on writes you have not narrowly allowed. doom_loop (default ask) fires after three identical tool calls.
A claim is not a result. Only a check is a result. Before marking verified: read the handoff, especially “not verified”; inspect git -C ~/agent-lab/tt-due diff <baseline>..feat/due-dates and git show --stat; re-run the acceptance checks on that branch; confirm the write set and that no secrets landed. If you skip this, Lesson 5 inherits the worker’s mistakes and you will blame “the merge.”