When running as a CLI subagent (opencode headless mode), follow this protocol for shared state coordination. In headless mode your stdout is captured in the spawner log, but the orchestrator reads only the result artifact below. If you do not write it, the orchestrator reports your run as crashed even on success.
Use file-based I/O for coordination. Coordination/state files (task-board, progress,
result hand-offs) MUST be written to the project-root memory store .serena/memories/
— that is the only location the orchestrator (oma agent:status), oma verify, and the
memory/retro tooling read. Writing them anywhere else (e.g. .agents/results/) leaves them
orphaned and your run is reported as crashed. Human-facing deliverables (plans, bug
reports, design docs) belong under .agents/results/ instead.
If Serena MCP is available, use read_memory/write_memory/edit_memory (its default
base path is .serena/memories); otherwise write the same files there directly with
opencode's file tools.
OpenCode Desktop runs one long-lived sidecar server; new sessions reuse its MCP clients, so a stuck Serena MCP stays stuck until the Desktop app is fully relaunched (the TUI is rarely affected). When a Serena MCP call times out or the MCP queue is clearly stuck, do not keep retrying MCP — fall back narrowly:
- Memory ops — read/write the same files directly under
.serena/memories/(as above). If a recent Serena CLI is installed (≥ 1.5, checkserena --version),serena memories read|write|listis an equivalent alternative; older versions (e.g. 1.3.x) do not have thememoriescommand. - Code analysis — fall back to native search/read tools. The Serena CLI cannot
execute analysis tools (
serena toolsonly lists/describes them). - Diagnostics —
serena project health-checkandserena project indexwork without MCP.
Keep the fallback scoped to the blocked call: this is a recovery path, not a license to abandon Serena-first. A full Desktop relaunch is what actually resets the stale MCP client.
All result, progress, and state files MUST be written to the project root .serena/memories/ directory, never to a subdirectory's .serena/memories/.
- Project root = the git repository root (where
.gitexists) - Session-scoped naming: when running under an orchestration session, append session ID as suffix:
result-{agent-id}-{sessionId}.md(e.g.,result-frontend-session-20260405-100835.md)progress-{agent-id}-{sessionId}.md
- Manual (non-orchestrated) runs: no suffix,
result-{agent-id}.md
- Read
.serena/memories/task-board.md(orread_memory("task-board.md")) to confirm your assigned task when it exists. - Create
.serena/memories/progress-{agent-id}[-{sessionId}].mdwith initial status.
- Periodically update
progress-{agent-id}[-{sessionId}].mdwith current state. - Include: action taken, current status, files created/modified.
- Create
.serena/memories/result-{agent-id}[-{sessionId}].mdwith final result including:- A status line — see Status line format below (REQUIRED)
- Summary of work done
- Files created/modified
- Acceptance criteria checklist
- Still create
result-{agent-id}[-{sessionId}].mdwith the status line set tofailed. - Include detailed error description and what remains incomplete.
The orchestrator parses the status with the regex ^## Status:\s*(\S+). The result file
MUST contain a single line in exactly this shape — heading marker, colon on the same line,
plain word, no backticks, no quotes:
## Status: completed
Use ## Status: failed on failure. Do NOT split it across lines or render it as a
sub-bullet (e.g. - Status: completed) — that fails to parse and a failed run would be
silently misreported as completed.