OpenCode-specific companion to 12-agents-subagents.md, 13-agent-sessions.md, and 06-context.md.
An agentic workflow is you + a primary agent (Build or Plan) + subagents (General, Explore, Scout) in OpenCode.
Practice orchestration on ../templates/sample-project/ — not one chat doing everything alone.
| Concept | Description |
|---|---|
| Agent Model | Primary vs subagent vs agentic workflow |
| Workflow Execution | Execution internals, parallel agents, context |
| Commands, Skills, and AGENTS.md | /commands, SKILL.md, AGENTS.md |
| Token Efficiency | Doom loops, steps limit |
| Orchestration Principles | Phase gates, verify gates, work-splitting |
Optional: skim tools/opencode-reference.md (cheat sheet) and tools/opencode-ecosystem.md (Java skills install).
| Section | Topic |
|---|---|
| Parallel Triage | Red build — parallel explore + bash + diagnose |
| Refactor Pipeline | Plan → implement → verify |
| PR-Ready Workflow | CI log → diagnose → fix → verify |
| Full Orchestration | End-to-end session |
| Anti-Patterns | Failure patterns and recovery |
| Type | Files |
|---|---|
| Templates | triage-session · refactor-pipeline · pr-review · hotfix |
| Playbooks | red-build-triage · safe-refactor · pre-pr-review · incident-hotfix |
| Tools | opencode-reference · opencode-ecosystem · opencode-agents.example.json |
Copy templates into .opencode/commands/ in your repo; use playbooks as orchestration prompts.
cd ../templates/sample-project
mvn test -Dtest=PersonServiceImplTest # 1 failure until fixed../templates/sample-project/README.md — project overview and setup.
You already write Java. This section is how to run a team of agents, not how to code.
See Core Concepts, Workflow sections, and Resources at the top.
A subagent is not the primary. Each subagent runs in a child session with isolated context.
Full agent model: Agent Model.
| Pattern | Agents | Java use case |
|---|---|---|
| Parallel triage | @explore + bash + @general (diagnose) |
Red CI / local mvn test failure |
| Plan → implement | @explore → @general plan → Build + @general implement |
Safe OrderService-style refactor |
| Verify gate | Build bash after any General edit | mvn test / mvn verify — block merge without output |
| Parallel research | 2× @explore (different packages) |
Map legacy before API change |
| PR-ready | Plan + log + @general minimal fix + bash |
CI log → fix → green |
flowchart LR
triage[Parallel triage]
plan[Plan gate you]
impl[Implement]
verify[bash verify]
triage --> plan
plan --> impl
impl --> verify
Full worked example with orchestration prompts, plan approval guidance, and review checklist: Refactor Pipeline.
Before you accept agent work as PR-ready:
- Scope: Only expected paths (e.g.
com.example.demo, notpom.xmlsurprises) - Tests: Shell output shows
Failures: 0(not "should pass") - Public API: No signature changes unless ticket allows
- Diff size: Hotfix = minimal; refactor = focused module
- Root cause: Diagnosis tied log line to code line
Parallel writers on one file; implement before plan approval; skipping verify after General. Full list: Anti-Patterns.
Tool: OpenCode — terminal-based AI coding assistant (TUI)
OpenCode is a terminal UI (TUI) — not an IDE plugin. You run it from a shell in your project directory.
┌─────────────────────────────────────────────────────────────┐
│ OpenCode │ Plan ▾ │ sample-project │
├─────────────────────────────────────────────────────────────┤
│ Session tree (left) │ Chat pane (main) │
│ ├─ Primary (Plan) │ You: Orchestrate parallel triage…│
│ ├─ ↳ Explore (child) │ Plan: Architecture (3 bullets)… │
│ └─ ↳ General (child) │ [Type message here] │
├─────────────────────────────────────────────────────────────┤
│ Status: step 4/8 │ model: claude-sonnet-4 │
└─────────────────────────────────────────────────────────────┘
- Chat pane — where you type prompts and read agent replies.
- Agent picker — Tab cycles primary agents (Plan ↔ Build). Tab switches which primary agent receives your next message — it does not create a new session or reset context. Plan and Build share the same conversation thread and accumulated tool outputs. To get a clean context for implement, start a new session explicitly (or paste
WORKFLOW_STATE.md/ the approved plan into a fresh session). - Session tree — child sessions appear when subagents run; use Ctrl+Down to inspect them.
- Status bar — current step count and model.
Install OpenCode once on your machine:
# npm (Node.js 18+)
npm install -g opencode-ai
# macOS (Homebrew)
brew install opencodeVerify: opencode --version
Official install options and updates: OpenCode docs.
cd your-java-project # e.g. sample-project
opencode # opens TUI in current directory
/connect # set LLM provider API key (once)
/init # scan repo; generate AGENTS.mdOpenCode loads project rules from the nearest AGENTS.md and config from opencode.json (see below).
| Term | What it is | You |
|---|---|---|
| Primary agent | Main assistant in the chat thread. Switch with Tab. Built-ins: Build, Plan. | Set intent, approve gates, reject bad scope |
| Subagent | Specialist worker the primary invokes (or you @mention). Built-ins: General, Explore, Scout. | Receive summaries; never assume they saw full chat |
| Agentic workflow | End-to-end session: you + primary + one or more subagents + verify + your approvals. | Design who runs when; merge outputs for standup/PR |
Subagents are one part of an agentic workflow — not the whole system. The primary drives the conversation; subagents do bounded tasks in child sessions and return a final text summary (some docs call this a "digest" — same thing).
flowchart TB
you[You]
primary[Primary Build or Plan]
subExplore[Subagent Explore]
subGeneral[Subagent General]
subScout[Subagent Scout]
you -->|Tab| primary
primary -->|delegate or at-mention| subExplore
primary --> subGeneral
primary --> subScout
subExplore -->|summary| primary
subGeneral -->|summary| primary
subScout -->|summary| primary
primary --> you
| Primary | Role | Java use |
|---|---|---|
| Plan | Analysis and design; restricted writes | Triage report, refactor plan, "do not edit yet" |
| Build | Implementation and execution | Fix bug, refactor after approval, mvn test |
| Primary | edit |
bash |
Notes |
|---|---|---|---|
| Plan | ask |
ask |
Prompts you before edits or shell commands |
| Build | allow |
allow |
Can edit files and run mvn test without asking |
These are built-in defaults. Override in project opencode.json or .opencode/agents/*.md. If Plan edits files without asking, check your config — you may have loosened permissions.
When to Tab: Stay on Plan until triage/plan is approved → Tab to Build for implementation.
| Subagent | Typical behavior | Source scope | Demo project |
|---|---|---|---|
| Explore | Read-only; fast search | Internal — your codebase only | Map com.example.demo, list tests |
| General | Multi-step; can edit when allowed | Internal + bash | Fix PersonServiceImpl, refactor PersonServiceImpl |
| Scout | Read-only; external / dependency research | External — library source, upstream docs, changelogs | Check Spring Boot version constraints, look up JUnit 5 API |
Scout data source: Scout reaches external information through the web_search tool — it queries the web for library docs, changelogs, release notes, and upstream source references. Network access is required. Built-in OpenCode providers include web search with no extra config; if search is disabled in your environment, register an MCP server that provides search or docs lookup in opencode.json.
Explore vs Scout rule: Use Explore when you need to map or search your own code. Use Scout when you need to understand a third-party library or look up something outside the repo.
For the general rationale (focus, parallelism, context control, safety), see 12-agents-subagents.md.
In OpenCode specifically: Explore is read-only, Scout reaches external sources, General can edit. Combining them in one orchestration message lets you run exploration + tests + diagnosis concurrently while keeping the primary context lean.
Prefer a subagent when:
- The task is broad ("understand this module", "find all usages of
OrderService") - You need parallel independent work (tests + exploration + refactor plan)
- The work is self-contained and can be described in one detailed prompt
- You want a specialist (General + CI log for one red check)
Prefer direct primary tools when:
- The task is one step (read one file, run one command, fix one obvious line)
- You already know exactly where to edit
- Latency matters and delegation overhead isn't worth it
- Primary delegates — You ask the primary to use Explore / General / run tests.
- @mention —
@explore,@general,@scoutfor a direct specialist turn. - Parallel — Primary launches several workers; you merge (Section 1).
Subagents do not see your full chat unless the primary passes context in the task prompt.
Red build triage on sample-project:
| Step | Role | Action |
|---|---|---|
| 1 | You + Plan primary | "Triage only — no edits" |
| 2 | Explore (parallel) | Map package + tests |
| 3 | Verify (parallel, bash) | Run mvn test, capture failures — a bash call in the primary context, not a fourth subagent |
| 4 | Diagnose (parallel) | Root cause for top failure (log or code) |
| 5 | You | Approve single next action |
| 6 | Tab → Build primary | — |
| 7 | General | Minimal fix PersonServiceImpl.java |
| 8 | Verify (bash) | mvn test all green |
Detailed prompts: Parallel Triage.
Internals: Workflow Execution
| Mechanism | Use |
|---|---|
| Tab | Switch Plan ↔ Build |
| @mention | Direct subagent turn |
task tool |
Primary spawns child session — OpenCode built-in that creates an isolated subagent run and injects its final text summary back into the parent context (you orchestrate via message) |
Custom /command |
Repeatable workflows — Commands, Skills, and AGENTS.md |
| Skills | java-junit, java-code-review, etc. |
| AGENTS.md | Project rules; run /init once |
Config file location: opencode.json at project root (same level as pom.xml). Global defaults: ~/.config/opencode/config.json. Project config overrides global. Loaded at session start, before AGENTS.md.
Permissions per agent in opencode.json or .opencode/agents/*.md (edit, bash, task, skill, doom_loop).
Custom agents: mode: "primary" | "subagent" — tools/opencode-agents.example.json.
mode |
Meaning | Can spawn subagents? | Reachable by Tab? |
|---|---|---|---|
primary |
Owns the conversation thread; can delegate to subagents via task; you switch to it with Tab |
Yes | Yes |
subagent |
Runs in an isolated child session; cannot spawn further child sessions; final text summary returns to parent | No | No |
A custom agent with mode: subagent cannot issue task calls — it cannot delegate further. Why: subagent mode runs in a child session where the task tool is not available; delegation is a primary-only capability. A subagent that tried to spawn another worker would have no mechanism to do so. It also cannot be reached by pressing Tab; it is only invoked by a primary via @mention or task.
CI triage: Plan/Build + ci log + @general.
Session navigation: Ctrl+Down → first child session; Up → parent — inspect subagent work directly. Run /help keybinds to see current mappings.
Reference: tools/opencode-reference.md · tools/opencode-ecosystem.md
| Term | Meaning |
|---|---|
| Primary | Build or Plan — main conversation agent |
| Subagent | General, Explore, Scout — delegated specialist |
| Child session | Isolated context for one subagent run |
| Agentic workflow | Full orchestrated session with your gates |
| Delegate | Primary assigns work to a subagent |
| Phase gate | A checkpoint where you must review output and explicitly approve before the next phase runs. Nothing proceeds until you say so. |
| Verify gate | A mandatory mvn test (or equivalent) bash run after any implementer edit. Its output — Tests run: N, Failures: 0 where N ≥ 1 — must appear verbatim in the summary before you accept the work. "Should pass" is not a passing verify gate. |
| Tab | Switch primary (Plan ↔ Build) |
See also: Agent Model
| Layer | Who | Responsibility |
|---|---|---|
| You | Human | Intent, gates, approve/reject, merge for standup/PR |
| Primary | Build or Plan | Conversation owner; delegates to subagents; merges summaries |
| Subagent | Explore, General, Scout | Bounded task in a child session; returns summary only |
| System agents | compaction, title, summary | Automatic; you do not invoke them |
You design who runs when. OpenCode handles sessions, tools, and context boundaries.
sequenceDiagram
participant You
participant Primary as Plan_or_Build
participant Tools as read_grep_bash_edit
participant Task as task_tool
participant Child as Subagent_session
You->>Primary: First message
Primary->>Tools: Iterate tools as needed
Tools-->>Primary: File contents, grep hits, command output
alt Delegation needed
Primary->>Task: Spawn subagent with prompt
Task->>Child: New child session starts
Child->>Tools: Own agentic loop
Tools-->>Child: Results
Child-->>Task: Text summary at end
Task-->>Primary: Return value injected into parent context
end
Primary-->>You: Merged reply
- Your message is appended to the primary session (Build or Plan).
- OpenCode loads project rules from the nearest
AGENTS.md(and global~/.config/opencode/AGENTS.mdif present). - Available skills appear in the
skilltool description; the agent may load one when relevant. - The primary agent plans its next action: read files, run bash, delegate, or reply.
The primary iterates until it has enough to answer or delegate:
| Tool | Typical use (Java) |
|---|---|
read / grep / glob |
Find PersonServiceImpl, failing test, CI log path |
bash |
mvn test, git diff, gh run view --log-failed |
edit / write |
Apply fix (Build only; Plan should not) |
task |
Spawn Explore / General / Scout in a child session |
skill |
Load java-junit, java-code-review, etc. |
Each iteration re-sends accumulated context — see Token Efficiency for cost control.
When the primary delegates:
- OpenCode creates a child session with a fresh context.
- The child receives the system prompt (with tool descriptions) + AGENTS.md + what the parent puts in the task prompt — not your full chat history.
- The child runs its own loop (read → analyze → bash → …).
- When the child finishes, its final text summary is the task return value.
- The parent reads that summary and continues — or replies to you.
Critical rule: Put paths, constraints, acceptance criteria, and return format in every delegation prompt.
How the parent includes context: The primary does not forward your full chat or prior tool outputs automatically. When it calls task, it copies relevant excerpts — file paths, error lines, acceptance criteria, return format — directly into the text of the delegation prompt. That is why the critical rule applies to your orchestration message: you write paths and constraints there so the primary can relay them into each child task prompt.
For session lifecycle, context growth mechanics, practical token cost reference, subagent isolation diagram, model window sizes, and session-splitting triggers, see 13-agent-sessions.md.
OpenCode summary: Each step re-sends the full accumulated context. At 20 steps with large tool outputs, a typical Java triage session consumes 30 000–80 000 tokens. This is why phase resets and subagent delegation matter.
Cost control and guardrails: Token Efficiency.
The primary synthesizes:
- Its own tool results
- Subagent summaries
- Your original question
You judge quality using the review checklist in §0 of this guide.
| Pattern | Who initiates | Child session? | Context in child | Return value |
|---|---|---|---|---|
@mention in your message |
You directly | Yes | System prompt + AGENTS.md + what you write after @mention | Summary injected into primary |
Primary delegates via task |
Primary agent | Yes | System prompt + AGENTS.md + what primary puts in task prompt | Summary injected into primary |
| Primary uses bash/read directly | Primary agent | No (inline) | Full primary context | Inline in primary loop |
When does the primary use task vs inline tools? The LLM decides based on your prompt. If you say "orchestrate parallel triage: @explore + mvn test + diagnose", the primary typically issues task calls for subagents and bash inline. If you say "read PersonServiceImpl.java and fix it", Build may use read + edit directly without spawning a child session.
Rule of thumb: Use @mention or task when the work is bounded and produces a summary. Use inline tools when it is one file, one command, and you want full primary context.
| Signal in your prompt | Typical primary behavior |
|---|---|
| "orchestrate", "in parallel" | Issues task calls for each named worker |
@explore, @general, @scout explicit mention |
Always delegates to that subagent |
| "plan only" or "read-only" | Delegates to Explore or a plan-only General |
| "fix this line in X.java" | Stays inline — read + edit directly |
One command ("run mvn test") |
Stays inline — bash directly |
When in doubt, name the subagent explicitly (@explore) — the primary will always delegate when you do.
Both create an isolated child session, but they differ in what seeds the child's context:
| Mechanism | Who initiates | Context the child receives |
|---|---|---|
@mention in your message |
You | System prompt + AGENTS.md + text you wrote after @mention |
Primary delegates via task |
Primary agent | System prompt + AGENTS.md + what the primary puts in the delegation prompt — may include excerpts from the conversation it considers relevant |
Both return a text summary that is injected into the primary context. From the child's perspective, the session is identical — it cannot tell whether it was spawned by the user or the primary.
sequenceDiagram
participant You
participant Plan as Plan_primary
participant E as Explore_child
participant V as Verify_bash
participant G as General_plan_child
You->>Plan: Parallel triage message
par Step A
Plan->>E: task Explore map package
E-->>Plan: Architecture digest
and Step B
Plan->>V: bash mvn test
V-->>Plan: Failure list
and Step C
Plan->>G: task General diagnose log
G-->>Plan: Root cause proposal
end
Plan-->>You: Merged triage report
- The primary issues multiple
taskcalls (or@explore+ bash +@generalin one orchestration message). - Each subagent runs in an independent child session.
- Independent tasks can run concurrently (subject to provider rate limits).
- Semantics resemble
CompletableFuture.allOfwithexceptionallyon each — one child failing does not cancel the others; the parent receives each child's result (or error summary) and merges them all. - The primary merges all summaries before replying.
When the primary issues multiple task calls in one reasoning step, OpenCode dispatches them as concurrent API calls (subject to provider rate limits). Bash commands run in the primary's own loop — sequentially, inline.
An orchestration message that lists @explore + bash + @general causes the primary to handle all three in one agentic step:
| Worker | Execution | Runs in |
|---|---|---|
@explore |
Concurrent (child session) | Isolated context |
bash mvn test |
Sequential (inline) | Primary context |
@general diagnose |
Concurrent (child session) | Isolated context |
You send one message. The primary returns after all workers complete (or fail). You do not send three separate messages.
| Setting | Recommendation |
|---|---|
| Parallel read-only (Explore) | Safe at 2–3 |
| Parallel writers | Never on the same file |
| Mixed triage (Explore + bash + diagnose) | Start with 2 concurrent tasks |
| Provider throttling | Reduce concurrency if timeouts or flaky completions |
| One worker fails | Primary includes the error in the merged reply; you decide whether to retry that worker or proceed — it does not abort the other workers |
Increase concurrency only after clean runs at a lower setting.
When provider rate limits are hit, OpenCode queues excess task calls and retries with backoff. You will see increased latency in the merged reply, not an explicit error. If a child session times out after retries, it returns an error summary; the primary merges it as a failed worker. If you consistently see slow or missing worker results, reduce concurrency to 2 and retry.
When subagents create child sessions:
| Keybind (default) | Action |
|---|---|
Ctrl+Down (session_child_first) |
Enter first child session |
| Right / Left | Cycle between sibling child sessions |
Up (session_parent) |
Return to parent session |
Default keybinds can be remapped in ~/.config/opencode/keybinds.json. Run /help keybinds to see current mappings.
Use this to inspect what Explore actually read, or what General proposed, without guessing from the parent summary.
For what subagents don't see, what flows back (Parent→Child / Child→Parent / Parent→You),
and the WORKFLOW_STATE.md cross-session handoff pattern, see
13-agent-sessions.md and
12-agents-subagents.md — Pass Context Explicitly.
OpenCode note: WORKFLOW_STATE.md is a human convention — you create the file
(or ask Build to create it) and paste its path into the task prompt.
OpenCode does not load it automatically.
Reference: Multi-agent workflow article.
| Agent | When | Your action |
|---|---|---|
| compaction | Context window grows large | None — summarizes history automatically |
| title | New session | None — generates short title |
| summary | Session end / export | None — generates session summary |
Trigger: When accumulated context (messages + tool outputs) approaches the model's context window — typically around 80–90% of the window (provider-dependent). Not configurable per session by the user.
Are system agents visible in the session tree? Compaction is not shown as a separate session in the tree — you see its effect when replies become shorter or seem to forget earlier details. The title and summary agents create visible session metadata (the session title bar and export summary), not child sessions you can navigate into.
What compaction preserves:
- The last ~5–10 turns verbatim (exact count is provider-dependent and not user-configurable)
- A model-generated summary paragraph of earlier work (key facts, decisions, file paths)
What compaction discards:
- Verbatim tool outputs from early turns (full file reads, long
mvn testlogs, grep dumps)
Recovery when replies "forget" early details:
- Re-inject critical constraints in your next message (paths, acceptance criteria)
- Point agents at
WORKFLOW_STATE.mdif you maintain one - Start a fresh session phase with the approved plan pasted — do not rely on chat history alone
If replies suddenly "forget" early details, compaction may have run. Re-inject critical constraints in your next message or use WORKFLOW_STATE.md.
| Pattern | When | Example |
|---|---|---|
| Primary only | One known file, one command | Build: "fix PersonServiceImpl.findById, run mvn test" |
| @mention | Direct specialist turn | @explore map com.example.demo tests |
| Primary delegates | Multi-step orchestration | Plan: "orchestrate parallel triage…" |
| Custom command | Repeatable workflow | /triage → expands to full prompt |
| Skill | Domain workflow | "Use java-code-review skill on PaymentService" |
Details: Commands, Skills, and AGENTS.md.
Message 1 — Plan primary, parallel triage:
Plan primary — orchestrate parallel triage on sample-project at <project_root>/templates/sample-project:
1) @explore read-only: map com.example.demo — classes, tests, intentional bugs.
2) Run mvn test -Dtest=PersonServiceImplTest (bash); report failures only with assertion text.
3) Read ci-logs/unit-tests-failure.log; diagnose PersonServiceImplTest — root cause and minimal fix (no edits).
Merge: Architecture (3 bullets), Test status, one recommended next action.
You: Approve "minimal fix in PersonServiceImpl.java".
Message 2 — Tab to Build:
Build primary — apply approved PersonServiceImpl fix only. Run mvn test -Dtest=PersonServiceImplTest. Paste Tests run / Failures line.
Verify yourself:
cd <project_root>/templates/sample-project && mvn test -Dtest=PersonServiceImplTest- Each subagent prompt had absolute paths and return format
- Plan phase produced no edits (or you explicitly allowed them)
- Parallel tasks were independent (no two writers on one file)
- Build phase attached mvn test output, not "should pass"
- You inspected child sessions (Ctrl+Down) when summary was vague
See also: Workflow Execution
Reference: tools/opencode-reference.md, tools/opencode-ecosystem.md
| Command | What it does | Java use |
|---|---|---|
/init |
Scan repo; create or update AGENTS.md |
When onboarding a new service repo |
/undo |
Revert last agent edits; restore your prompt. Reverts file edits written to disk in the last agent turn. Does not undo bash side effects (compiled .class files, installed packages, git commit). Use git diff to inspect what was reverted. |
Bad refactor — retry with tighter scope |
/redo |
Re-apply after undo | — |
/share |
Copy conversation link | Hand off triage to teammate |
/help |
List commands | — |
/connect |
Configure LLM provider API key | First-time setup |
Tab |
Cycle primary agents (Build ↔ Plan) | Gate: Plan until approved → Build |
Docs: OpenCode intro, Commands.
Store markdown files in:
- Project:
.opencode/commands/<name>.md - Global:
~/.config/opencode/commands/<name>.md
Invoke with /name in the TUI.
| Field | Purpose |
|---|---|
description |
Shown in command picker |
agent |
Which agent runs the command (plan, build, @explore, …) |
model |
Optional model override |
subtask |
true = run in isolated subagent context, identical to a primary task tool call — the command's full prompt becomes the child's context; the primary receives only the final summary; keeps the primary context clean when a command does heavy searching or reading. false = run inline in the primary context. |
| Syntax | Meaning |
|---|---|
$ARGUMENTS |
All arguments after command name |
$1, $2, … |
Positional arguments |
!`bash cmd` |
Inject live shell output into prompt |
@path/to/File.java |
Inject file contents |
The !`bash cmd` expression runs at invocation time — when you type /triage in the TUI — so the output is always fresh. It is not evaluated when the command file is saved.
Create .opencode/commands/triage.md:
---
description: Parallel red-build triage (map + test + diagnose)
agent: plan
subtask: false
---
Plan primary — orchestrate parallel triage on sample-project at <project_root>/templates/sample-project:
1) @explore read-only: map com.example.demo — classes, tests per class.
2) !`cd <project_root>/templates/sample-project && mvn -q test -Dtest=PersonServiceImplTest 2>&1 | tail -30`
3) Read ci-logs/unit-tests-failure.log; diagnose top failure — root cause, minimal fix (no edits).
Merge: Architecture (3 bullets), Test status, one recommended next action for my approval.Run: /triage
---
description: Run Maven tests for a module
agent: build
---
Run tests in module $1:
!`cd <project_root>/templates/sample-project && mvn test -Dtest=PersonServiceImplTest 2>&1 | tail -25`
Report: Tests run, Failures, Errors. Do not fix code unless I ask.Run: /verify .
---
description: Diagnose single CI failure from log file
agent: plan
---
Plan primary — incident triage only (no edits):
Repo: /work/ai/subagents/[CUSTOMIZE:service-path]
Log: @$1
1. Parse failing test and assertion from log
2. Root cause with evidence (log line + source line)
3. Minimal fix proposal — do NOT implement
Stop for my approval.Note:
[CUSTOMIZE:service-path]is a human placeholder — replace it with your actual service path before saving (e.g./work/services/payment-service). It is not OpenCode placeholder syntax. Valid OpenCode placeholders are$ARGUMENTS,$1,!`cmd`, and@path/to/file.
Run: /hotfix <project_root>/templates/sample-project/ci-logs/unit-tests-failure.log
More templates: templates/.
OpenCode loads the nearest AGENTS.md when you work in a directory tree.
cd your-java-service
opencode
/init## Build and test
- Build: `mvn -q verify -DskipTests` then `mvn test`
- Focused: `mvn test -Dtest=PersonServiceImplTest`
- Java 21, JUnit 5, AssertJ
## Layout
- Main: `src/main/java/com/example/...`
- Tests mirror main package
- No edits under `target/`
## Conventions
- Public API changes require ticket approval
- Prefer immutable value types; no public fields
- Run tests before claiming success
## MCP
- Use context7 for Spring Boot / JUnit docs when API is unclearcontext7 is an MCP server (Model Context Protocol) that provides up-to-date library documentation. Install and register it once:
npx -y @upstash/context7-mcpAdd to project opencode.json under mcpServers (see context7 docs). After registration, agents can query current Spring Boot / JUnit API docs when your prompt or AGENTS.md references context7.
- Large code blocks (link to files with
@instead) - Auto-generated junk from
/inityou never reviewed - Secrets or credentials
Place nested AGENTS.md per module. Closest file wins for the directory you are editing.
Standard: agents.md
Skills bundle reusable instructions the agent loads on demand via the skill tool.
| Location | Scope |
|---|---|
.opencode/skills/<name>/SKILL.md |
Project |
~/.config/opencode/skills/<name>/SKILL.md |
Global |
.claude/skills/<name>/SKILL.md |
Project (Claude-compatible) |
~/.claude/skills/<name>/SKILL.md |
Global (Claude-compatible) |
Docs: OpenCode skills.
---
name: java-junit
description: JUnit 5 best practices — parameterized tests, AssertJ, naming conventions
license: MIT
compatibility: opencode
---
## What I do
- Review or generate JUnit 5 tests
- Prefer @ParameterizedTest for boundary cases
- Method names: methodName_stateUnderTest_expectedBehavior
## When to use me
Use when adding or fixing unit tests in Java modules.Required: name (matches folder name), description (1–1024 chars, used for discovery).
Use the java-junit skill. Add parameterized tests for PersonServiceImpl edge cases.
{
"permission": {
"skill": {
"*": "allow",
"experimental-*": "ask",
"internal-*": "deny"
}
}
}Pattern matching uses the skill's name: frontmatter field. Most-specific pattern wins — internal-audit beats internal-* beats *. deny prevents the agent from loading the skill even when you explicitly ask for it; use ask instead if you want a confirmation prompt before the skill loads.
If no pattern matches a skill name, access defaults to deny. Always include a "*": "allow" catch-all unless you want unknown skills blocked by default.
# JUnit + Spring Boot testing
git clone https://github.com/vekzz-dev/opencode-skills.git /tmp/opencode-skills
cp -r /tmp/opencode-skills/java-junit \
/tmp/opencode-skills/java-springboot \
/tmp/opencode-skills/spring-boot-testing \
~/.config/opencode/skills/
# Full Java skill pack (review, security, deps, concurrency)
git clone https://github.com/shcherbi/open-code-ai-java.git /tmp/open-code-ai-java
# Follow repo README — link-skills.sh or copy .opencode/skill/* to ~/.config/opencode/skills/| Skill (examples) | Trigger |
|---|---|
java-junit |
"add tests", "fix failing test" |
spring-boot-testing |
@WebMvcTest, Testcontainers |
java-code-review |
"review this PR", "check thread safety" |
maven-dependency-audit |
"audit dependencies", CVE check |
security-audit |
OWASP-style review |
Full ecosystem list: tools/opencode-ecosystem.md.
Define in opencode.json or .opencode/agents/<name>.md:
---
description: Read-only scope auditor before PR
mode: subagent
permission:
edit: deny
bash:
"*": deny
"git diff*": allow
"git log*": allow
---
Audit changed files under the ticket scope only. Flag surprises (pom.xml, generated code).Create interactively: opencode agent create
Example config: tools/opencode-agents.example.json.
| Need | Use |
|---|---|
| Repeatable triage every Monday | Custom /triage command |
| Consistent test style | java-junit skill |
| Plan without accidental edits | Plan primary + edit: deny on custom planner |
| Fast codebase map | @explore or Explore subagent |
| Implement after approval | Build + @general |
| Put it in... | When |
|---|---|
AGENTS.md |
Always-on project rules (build commands, layout, conventions) every session must know |
| Custom command | Repeatable multi-step workflow you invoke manually (/triage, /hotfix) |
SKILL.md |
Domain expertise the agent loads on demand when the task matches (test style, security review) |
opencode.json |
Agent permissions, step limits, model routing — not instructional content |
Decision flow:
- Does every session need this rule? →
AGENTS.md - Is it a workflow you run on demand? → custom command
- Is it specialized knowledge for certain tasks? → skill
- Is it about what agents are allowed to do? →
opencode.json
See also: Workflow Execution
Related: Anti-Patterns
A context window is the maximum number of tokens a single LLM call can accept as input. In an agentic loop, every step re-sends the full accumulated conversation plus all prior tool outputs — not just the latest message.
| Context type | What it contains |
|---|---|
| Primary context | All messages (yours + agent's) + tool-call requests and outputs + system prompt + AGENTS.md + loaded skills (compaction may summarize early content) |
| Subagent context | System prompt (with tool descriptions) + AGENTS.md + task prompt + its own reads/searches — isolated from primary chat |
One-line rule: Primary context grows with every tool call; subagent context stays bounded and only the final summary returns to primary.
Full mechanics (filling rate, token cost table, model window sizes, session-splitting triggers): 13-agent-sessions.md. OpenCode-specific guardrails below.
Each agent step re-sends the full accumulated context (your messages + tool outputs + prior reasoning). Over 15–20 steps, cost grows quadratically, not linearly — tokens at step N ≈ sum of all tokens from steps 1 through N-1 plus the new tool output.
| Cause | Symptom | Cost impact |
|---|---|---|
| Vague prompt | 3 rounds of clarification | ~3× messages |
| Full repo in every subagent | Huge grep/read dumps in parent | Context bloat |
| No step limit | Same mvn test fails 8× |
Runaway iterations |
| High concurrency + throttling | Timeouts → retries | Wasted parallel spend |
| One overloaded agent | Plan + implement + test in one session | 20+ steps in one window |
Rule: Design narrow workers, explicit gates, and hard bounds.
In opencode.json or agent frontmatter:
{
"agent": {
"plan": {
"steps": 8
},
"build": {
"steps": 20
},
"explore": {
"steps": 5
}
}
}When the limit is reached, the agent receives a system prompt to summarize work and list remaining tasks — then stops iterating.
What you see when steps is reached: The agent appends a final message in the normal conversation pane (no special UI indicator). Typical pattern:
Reached step limit.
Completed: mapped PersonServiceImpl, proposed fetchOrThrow extraction
Remaining: implement refactor, run mvn test
Blocked on: your approval of the plan
The session does not close. Send a new message with a tighter prompt or paste WORKFLOW_STATE.md / the approved plan to continue in a fresh phase.
Important — steps limit does NOT reset the context window. The session's full accumulated history is still present after the limit is reached. The benefit is the forced summary: it becomes a compact re-entry point you can copy and paste into a fresh session rather than continuing from the large context. Starting a new session after a
steps-limit summary is almost always cheaper than continuing in the same session.
| Agent | Suggested steps |
|---|---|
| Plan (triage/design) | 5–8 |
| Explore (read-only) | 5 |
| General (implement) | 15–20 |
| Custom diagnose-only | 8 |
You decide whether to start a fresh session with a tighter prompt.
OpenCode exposes a doom_loop permission key — recovery behavior when an agent appears stuck (repeating failed actions).
OpenCode treats an agent as stuck when it makes 3+ consecutive tool calls that are structurally identical — same tool, same arguments, or the same error response — without changing state (e.g. three identical mvn test failures with no file edits between attempts).
{
"agent": {
"plan": {
"permission": {
"doom_loop": "deny"
}
},
"build": {
"permission": {
"doom_loop": "ask"
}
}
}
}| Value | Behavior |
|---|---|
allow |
OpenCode injects a system-level prompt: "You appear to be in a loop. Summarize what you tried and propose an alternative." Agent continues automatically. |
ask |
Same injection is shown to you for approval before being sent to the agent. |
deny |
No injection; agent exhausts its steps budget and stops. You redirect manually. |
What the injected prompt looks like:
You appear stuck. Summarize what you have tried, what failed (exact error),
and what information you need to proceed. Do not issue further tool calls
until you have asked.
This message appears as a system turn in the conversation pane — you can see it. With ask, it is shown to you first and you approve before it is sent to the agent.
For triage-only Plan agents, prefer deny or ask so you redirect instead of watching endless retries.
Bad (expensive):
Fix the project.
Good (cheap, actionable):
Repo: <project_root>/templates/sample-project
Failing: PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsException
Log: ci-logs/unit-tests-failure.log (lines 1–18)
Constraint: edit PersonServiceImpl.java only; run mvn test -Dtest=PersonServiceImplTest
Return: root cause, diff summary, test output
After step 10 in a single session, context growth accelerates.
| Pattern | When |
|---|---|
| End General plan session → new General implement session | Refactor pipeline |
WORKFLOW_STATE.md handoff |
Multi-phase workflows |
| New message with approved plan pasted | Plan → implement (no "resume") |
Do not carry 40 messages of failed attempts into the implement phase.
One page of structured state beats re-explaining the ticket in every message. See Workflow Execution.
| Rule | Reason |
|---|---|
| Start with 2 parallel tasks | Avoid provider throttling |
| Never parallel writers on same file | Race conditions + rework = 2× tokens |
| Explore + bash + diagnose in parallel | OK — read-only + independent |
Two General agents editing PersonServiceImpl |
Forbidden |
Increase concurrency only after clean runs at the lower setting.
Signals:
- Same bash command fails 3× with identical output
- Agent proposes the same fix you already rejected
- "Let me try again" without new information
mvn testred but agent edits unrelated files
Break the loop:
Ctrl-Cto interrupt (if supported in your TUI).- Paste:
Stop. Do not run more commands.
Summarize:
1. What you tried (commands + files)
2. What failed (exact error lines)
3. What you need from me to proceed (one question max)
- Fix locally or tighten prompt; start a new session phase.
Never add || true, @Disabled, or delete assertions to "green" tests without root-cause approval.
| Task | Model tier | Agent | Quality / cost tradeoff |
|---|---|---|---|
| Triage / plan | Faster / cheaper (e.g. Haiku) | Plan | Reading and reasoning only — cheaper models handle this well |
| Multi-file implement | Capable (e.g. Sonnet) | Build | Multi-step edits need fewer logic errors; wrong diffs cost more to fix than token savings |
| Codebase map | Fast | Explore | Short read-only loops; speed matters more than depth |
| Single-file hotfix | Capable but bounded steps |
Build + General | One bounded edit — capable model with tight scope |
Practical rule: Cheaper models (Haiku-class) are fine for Plan and Explore because those phases produce summaries and plans, not production diffs. Capable models (Sonnet-class) are worth the cost for Build implement phases — Haiku on Build often produces subtly wrong logic that triggers doom loops and wastes more tokens than you saved.
Configure per agent in opencode.json:
{
"agent": {
"plan": {
"model": "anthropic/claude-haiku-4-20250514"
},
"build": {
"model": "anthropic/claude-sonnet-4-20250514"
}
}
}Run opencode models for available IDs.
Scenario: PersonServiceImpl.findById fix attempted 4×; tests still red because agent edits wrong method.
| Step | Waste | Better approach |
|---|---|---|
| 1–2 | Explore entire repo | Prompt: read only PersonServiceImpl.java + PersonServiceImplTest.java |
| 3–4 | Full mvn verify each loop |
mvn test -Dtest=PersonServiceImplTest |
| 5+ | General tries refactor PersonServiceImpl | Stop — hotfix scope only |
Recovery message:
Build primary — PersonServiceImpl hotfix only.
Read PersonServiceImpl.java lines 1–60 and PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsException test.
Root cause must reference exact line. One file edit max.
Run: mvn test -Dtest=PersonServiceImplTest
Paste full failure output if still red.
steps: treat as final attempt before escalation to me.
-
stepsset on Plan and Build agents -
doom_loopisaskordenyon Plan - Subagent prompts have paths + constraints + return format
- Parallel tasks are independent (no dual writers)
-
WORKFLOW_STATE.mdor template used for multi-phase work - Verify command is focused (
-Dtest=...) before full suite
- Token usage optimization (TrueFoundry)
- Context constraints in agent loops (Augment Code)
- Anti-Patterns
See also: Agent Model, Workflow Execution, Token Efficiency; tools/opencode-reference.md (skim)
General concepts: phase gates, verify gates — 12-agents-subagents.md
| Role | Who | Responsibility |
|---|---|---|
| Orchestrator | Primary agent (Build or Plan) + you | Split work, choose subagents, merge summaries, enforce gates |
| Worker | Subagent (Explore, General, Scout) | One bounded task in child session; returns summary |
| You | Human | Approve plans, reject bad diffs, define acceptance criteria |
Subagents do not see your full chat. Every worker prompt must repeat paths, constraints, and return format.
OpenCode delegation: @mention, primary orchestration messages, custom /commands, and task tool child sessions — see Workflow Execution.
flowchart TB
you[You]
primary[Primary Build or Plan]
w1[Explore]
w2[General]
w3[Scout optional]
you -->|orchestration message| primary
primary --> w1
primary --> w2
primary --> w3
w1 -->|summary| primary
w2 -->|summary| primary
w3 -->|summary| primary
primary --> you
- Long searches stay in Explore — you get a map, not 40 files in primary context.
- Never two writers on the same file in parallel.
- After any General / implement edit, a verify gate (
mvn test) is non-optional.
| Situation | You |
|---|---|
| Plan-only finished | Approve or edit plan before Build implements |
| Diff touches public API | Review before merge |
| Diagnosis proposes "rewrite module" | Reject; demand minimal fix |
| Tests not shown in summary | Block — request mvn test output |
| Production incident | Hotfix playbook; tight scope |
| Agent loops 3× on same failure | Interrupt — Token Efficiency |
Use this before designing a session to decide how to structure the work.
| Situation | Structure | Rule |
|---|---|---|
| Tasks are independent, different tool types (read + bash + diagnose) | Parallel — one orchestration message | No shared files between workers |
| Output of step A is input to step B (plan → implement) | Sequential — separate messages, gate between | Never overlap; paste plan into next message |
| One file, one command, under 3 steps | Inline — primary handles directly | No delegation overhead needed |
| Same file edited by two workers | Forbidden | Race condition; one edit is lost |
| Long session (10–12+ steps) moving to a new phase | New session with WORKFLOW_STATE.md or pasted plan |
Avoids carrying heavy context forward |
A phase gate is a point where you must review and explicitly approve before the next phase runs.
Before passing any phase gate, confirm:
- The worker returned concrete artifacts (class names, file paths, log lines, test output) — not assertions like "should work"
- The scope is within bounds — no surprise files changed, no unrelated refactors
- If it is a plan phase: method names and signatures are explicit, not prose
- If it is an implement phase: verify gate passed (see below)
A verify gate is both a tool invocation and a human check:
- Tool invocation — Build primary runs
mvn test(or a focused-Dtest=...variant) - Human check — you read the output; the summary must include the literal line
Tests run: N, Failures: 0where N ≥ 1
The gate passes only when that output appears verbatim. A result of Tests run: 0, Failures: 0 is a build misconfiguration (wrong module, empty -Dtest=, or tests not discovered) — not a passing gate. Block and ask the agent to check the -Dtest= argument or module path.
"Should pass", "tests look fine", or "I believe it works" are not passing verify gates. Block and request the actual output.
| Workflow | Structure |
|---|---|
| Triage | Parallel Explore + bash verify + diagnose (Plan primary) |
| Refactor | Explore → plan (Plan) → gate → implement + verify (Build) |
| PR / CI fix | Plan reads log → diagnose → gate → Build applies fix → verify |
Templates: templates/
See also: Agent Model, Workflow Execution
cd <project_root>/templates/sample-project && mvn test -Dtest=PersonServiceImplTest
# Expect exactly: Tests run: 3, Failures: 1Intentional issues in sample-project:
| Class | Issue | Used in |
|---|---|---|
PersonServiceImpl.findById |
Uses .orElse(null) instead of .orElseThrow(...) |
Parallel Triage, PR-Ready Workflow |
PersonServiceImpl |
Duplicated fetch/map/save pattern in create and update (refactor target, not a bug) |
Refactor Pipeline |
If state is wrong:
| Symptom | Action |
|---|---|
Failures: 0 |
PersonServiceImpl fix already applied. Reset: git -C <project_root> checkout templates/sample-project/ |
Failures > 1 |
Unrelated local changes. git stash and retry |
See sample-project/README.md for the full workflow map.
Before running triage, know what you are triaging:
| Class | Tests | Known issue |
|---|---|---|
PersonServiceImpl |
3 (create, findById-exists, findById-missing) | findById returns null instead of throwing — 1 test will fail |
PersonController |
3 (create-valid, create-invalid, findById-missing) | Integration tests (require DB) — run separately |
Expected baseline: Tests run: 3, Failures: 1 — the single failing test is PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsException.
You are not fixing code first. The Plan primary orchestrates three workers; you merge and pick one next action.
Approve when: Each worker returned concrete artifacts (paths, test names, log lines).
Reject when: Any worker says "should pass" without command output.
flowchart LR
you[You]
primary[Plan primary]
e[Explore child]
v["mvn test bash inline"]
d[Diagnose child]
you --> primary
primary --> e
primary --> v
primary --> d
e --> primary
v --> primary
d --> primary
primary --> you
| Step | Subagent / worker | Output you need |
|---|---|---|
| A | Explore | Map com.example.demo + service/tests |
| B | Verify (mvn test) |
mvn test exit code + failing test list |
| C | Diagnose | Root cause for top failure (ci log) |
Steps A–B–C can run in parallel. After approval → Tab to Build for the fix.
If the CI log contains multiple unrelated failures, triage one root cause per session — see PR-Ready Workflow — "When the log has multiple failures".
You send one orchestration message — not three separate messages. The Plan primary reads your prompt and, in one agentic step, dispatches:
| Worker | How it runs | Context |
|---|---|---|
@explore |
task → child session |
Isolated from primary chat — gets system prompt + AGENTS.md + explore instructions |
mvn test |
bash inline |
Primary context — not a spawned child session |
| Diagnose (log + code) | task → General child session |
Isolated from primary chat — gets system prompt + AGENTS.md + diagnose instructions |
Verify is inline, not a subagent: Step B (mvn test) runs as a bash call in the primary context. The flowchart shows it as a parallel step because it starts at the same time as the subagents, not because it is a fourth child session.
The primary waits for all workers to complete (or fail), merges summaries, then replies once. Details: Workflow Execution — "Parallelism: concurrent or sequential?".
Primary: Plan (triage only — no edits)
Subagents: Explore + verify + diagnose (parallel)
Switch to Plan primary. Orchestrate parallel triage on sample-project at <project_root>/templates/sample-project:
1) @explore — read-only map of com.example.demo: classes, tests per class, intentional bugs.
2) Run mvn test -Dtest=PersonServiceImplTest (bash); report failures only with assertion text.
3) Read ci-logs/unit-tests-failure.log; diagnose PersonServiceImplTest — root cause and minimal fix proposal (no edits).
Merge: Architecture (3 bullets), Test status, one recommended next action for my approval.
Plan primary — parallel triage on /work/services/payment-service:
1) @explore — map com.acme.payment: controllers, services, integration tests.
2) bash: mvn test -pl payment-core 2>&1 | tail -40
3) Read ci-logs/unit-tests-failure.log (or pasted GH Actions excerpt); diagnose top failure — root cause, minimal fix (no edits).
Merge for standup. Triage only.
- Explore:
PersonServiceImpl,PersonController, test class list - Verify:
PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsExceptionfailed - Diagnose:
findByIduses.orElse(null)instead of.orElseThrow(...) - Merged: "approve minimal fix in PersonServiceImpl.java" → then Tab to Build
Good Explore output is a concise bullet list — not prose, not "I looked at the project":
Classes: PersonServiceImpl, PersonController, PersonService, PersonMapper, Person
Tests: PersonServiceImplTest (3), PersonControllerTest (3 — integration)
Bug: PersonServiceImpl.findById uses .orElse(null) (should throw RuntimeException)
Duplication: PersonServiceImpl.create and update share fetch/map/save pattern
Interface: PersonService implemented by PersonServiceImpl
No other unit test coverage found
Bad Explore output (reject this):
I analyzed the project and found several classes related to calculations and
order processing. The tests seem to be passing mostly.
What is missing: no class names, no test counts, no specific file paths, no identified issue.
Re-prompt with: "Return: class names, test class names with test count, and any logic issues found. Bullet list only."
Reject if Explore returns vague summaries without class names, test counts, or file paths.
Explore often returns incomplete data — some class names but no test counts, or a partial file list with no identified issues. Do not accept partial output and continue to Diagnose.
| What you got | Action |
|---|---|
| Class names, no test counts | Re-prompt: require test class names with counts per class |
| Tests listed, no bugs or duplication noted | Re-prompt: require logic issues and structural notes for known packages |
Missing expected classes (e.g. no PersonServiceImpl) |
Re-prompt with narrower scope: map src/main/java/com/example/demo only |
Re-prompt template:
@explore — incomplete. Return bullet list only:
- All class names under com.example.demo
- Test class names with test count per class
- Any logic bugs or duplication found
Do not proceed until all three sections are present.
## Build triage — sample-project
- **Scope:** com.example.demo (N classes, M tests)
- **Red:** PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsException — returns null instead of throwing
- **Next:** Minimal fix PersonServiceImpl.findById; then mvn test -Dtest=PersonServiceImplTest (Build primary)
- **Not doing:** PersonServiceImpl refactor in same PRCopy templates/triage-session.md to .opencode/commands/triage.md, then run /triage.
See also: Parallel Triage; mvn test -Dtest=PersonServiceImplTest green (or fix PersonServiceImpl first)
Gate each phase. No implementation until you approve the plan.
| Phase | Worker | You approve |
|---|---|---|
| Map | Explore | Duplication identified |
| Plan | General (plan-only) | Helpers + discount rules preserved |
| Implement | General (new message with pasted plan) | Diff size reasonable |
| Verify | bash | mvn test output attached |
Primary: Plan for phases 1–2 → Tab to Build for implement + verify.
sequenceDiagram
participant You
participant PlanPrimary as Plan primary
participant Explore
participant GeneralPlan as General plan
participant BuildPrimary as Build primary
participant GeneralImpl as General implement
participant Verify
You->>PlanPrimary: Refactor pipeline start
PlanPrimary->>Explore: Read-only map PersonServiceImpl
Explore-->>PlanPrimary: Duplication report
PlanPrimary->>GeneralPlan: Plan only
GeneralPlan-->>PlanPrimary: Extraction plan
PlanPrimary->>You: Plan gate
You->>BuildPrimary: Tab + approved
BuildPrimary->>GeneralImpl: Implement pasted plan
GeneralImpl-->>BuildPrimary: Diff
BuildPrimary->>Verify: mvn test
Verify-->>BuildPrimary: All green
BuildPrimary->>You: Summary
Primary: Plan
Subagents: Explore, then General (plan only)
Plan primary — refactor pipeline for PersonServiceImpl in <project_root>/templates/sample-project, phases 1–2 only:
1) @explore read-only: Document duplication in PersonServiceImpl.java; both create and update fetch-map-save in near-identical steps. List candidate private helpers.
2) @general plan only — no edits: Propose refactor with method names/signatures. Confirm PersonServiceImplTest expectations unchanged.
Stop and return plan for my approval.
Plan-only is prompt-based, not permission-enforced.
@general plan only — no editsrelies on the agent obeying your instruction. General can still edit if permissions allow. For a hard guarantee during the plan phase, setedit: denyin.opencode/agents/general.md(or inopencode.jsonfor the General agent), then restoreedit: allowbefore the implement phase.
Optional: Use spring-boot-testing skill when refactoring a @Service with injected dependencies in your work repo.
Primary: Build
Subagents: General (implement), then verify
Build primary — implement the approved PersonServiceImpl plan below:
<paste approved plan>
Extract shared private helpers; keep public API signatures unchanged.
Run mvn test -Dtest=PersonServiceImplTest then mvn test -Dtest=PersonServiceImplTest.
Return: files changed, test output, 3-line before/after summary.
Paste the complete plan output including bullets and headers from General's reply — do not strip it to one line. The implementer General needs the full structural context. Summarizing down to one sentence is a common cause of incorrect or incomplete implementations. Include:
- Private method names and signatures (e.g.
fetchOrThrow(UUID id)) - Which public methods call them
- Existing behaviour preserved
What if General's plan is prose? If General returns a narrative description without method signatures, reject it and re-prompt.
Bad plan (reject):
We should extract a helper that does the fetch and another for saving.
The create and update methods can call these helpers to reduce
duplication while keeping the same behavior.
Good plan (approve):
Private methods to add:
- private Person fetchOrThrow(UUID id)
Call sites:
- findById: entity = fetchOrThrow(id); return personMapper.toResponse(entity)
- update: entity = fetchOrThrow(id); update fields; save; return personMapper.toResponse(entity)
Public API: unchanged (findById, create, update signatures unchanged)
Tests: PersonServiceImplTest all 3 tests green
Re-prompt if you get prose:
Return: private method names with exact Java signatures
(e.g. private Person fetchOrThrow(UUID id))
and the call-site line from findById. No prose, structured list only.
A vague plan produces a vague implementation. Do not proceed to Build until the plan includes explicit signatures.
Do not paste the full chat history. If General's plan is vague (no signatures), reject and re-prompt before pasting into the implement message.
Refactor note: Extract fetchOrThrow to eliminate the duplicated findById + orElseThrow block that appears in both findById (after fix) and update.
- Only
PersonServiceImpl.java(+ imports) changed - Public method signatures unchanged
-
PersonServiceImplTest: all 3 tests green - Full unit suite green
Save phase 1 as .opencode/commands/refactor-plan.md — see templates/refactor-pipeline.md.
See also: safe-refactor playbook.
See also: ci-logs fixture
Simulate a real PR: CI evidence → diagnose → you approve → minimal fix → verify gate.
| Gate | Criterion |
|---|---|
| CI input | Log path or pasted excerpt |
| Fix scope | One root cause, one primary file |
| Verify | mvn test output in summary |
Primary: Plan (diagnose) → Build (fix + verify)
flowchart TD
ciLog[CI log file]
planPrimary[Plan primary]
buildPrimary[Build primary]
ciLog --> planPrimary
planPrimary -->|approve| buildPrimary
buildPrimary --> verify[mvn test]
Message 1 (Plan) produces a diagnosis — root cause, evidence, and a minimal fix proposal. You must read and approve that diagnosis before Build acts on it. This is the approval gate: you verify the agent identified the right bug before any file is edited.
If you paste both messages at once, Build executes the fix before you have reviewed the root cause. The gate is eliminated and wrong fixes can be applied automatically.
Send these as two separate messages. Message 2 must only be sent after you have reviewed and approved Message 1's output. Do not paste both together.
Primary: Plan first, then Build after approval
Subagents: Diagnose + General (fix) + verify
Plan primary — PR-ready workflow for <project_root>/templates/sample-project:
Read ci-logs/unit-tests-failure.log; root cause for PersonServiceImplTest; minimal fix proposal (no edits). Stop for approval.
Build primary — apply approved fix to PersonServiceImpl.java only; run mvn test -Dtest=PersonServiceImplTest; paste Tests run / Failures line.
Plan primary — PR #142 unit-tests failure:
Log excerpt:
<paste gh run view --log-failed output>
Repo: /work/services/order-api
Diagnose root cause; minimal fix proposal; no edits. Stop for approval.
Find the run ID, then fetch the log:
gh pr checks <PR-number> --repo org/repo # lists checks with run IDs
gh run list --repo org/repo --branch <branch> --limit 5 # recent runs on branch
gh run view <run-id> --log-failed # download failed job log| Situation | Your action |
|---|---|
| Multiple unrelated test failures | Triage one root cause per session. Pick the failure most likely to unblock the build. |
| Failures in different modules | Run one Plan session per failure; implement fixes sequentially. |
| Compile error (not test failure) | Fix compile first — tests cannot run until build succeeds. Paste [ERROR] lines from Maven output. |
| Build failure (non-test) | Same diagnose → approve → fix pattern. Paste Maven [ERROR] block, not Surefire output. |
Rule: One root cause, one primary file per fix session. Do not batch unrelated fixes in one General task.
- Diagnose:
PersonServiceImpl.findByIdreturns null instead of throwing - Verify:
Tests run: 3, Failures: 0 - Files:
PersonServiceImpl.javaonly
templates/hotfix.md or /hotfix <log-path> — see Commands, Skills, and AGENTS.md.
See also: Sections 1–3
Full agentic workflow: triage → fix → plan refactor → approve → implement → verify → scope check. You merge; primary orchestrates.
| # | Worker | Task | Your gate |
|---|---|---|---|
| 1 | Explore | Map com.example.demo |
— |
| 2 | Verify (bash) | mvn test |
— |
| 3 | Diagnose / fix plan | PersonServiceImpl if red | Approve fix |
| 4 | General | Plan-only PersonServiceImpl | Approve plan |
| 5 | General | Implement (pasted plan) | Review diff |
| 6 | Verify (bash) | mvn test |
Block if red |
| 7 | Explore | Scope check | Reject creep |
Steps 1–2 parallel under Plan. Steps 3–7 sequential; Tab to Build before edits.
[STOP — wait for my approval] in the prompt below is not a magic keyword that pauses OpenCode mid-execution. It marks where you must split the work into separate messages.
In practice:
- Send Message 1 — everything up to the first
[STOP](parallel triage + diagnose proposal). - Wait for Plan's reply. Review. Approve the PersonServiceImpl fix.
- Tab to Build. Send Message 2 — PersonServiceImpl fix only.
- Wait. Review. Approve the PersonServiceImpl refactor plan.
- Send Message 3 — implement + verify + scope check.
The full prompt block below is a reference script showing the complete session plan — not a single paste-and-run message.
Primary: Plan (triage + plans) → Build (implement + verify)
Subagents: Explore, General, verify bash
Send these as three separate messages. Wait for the agent to reply fully before sending the next one.
Plan primary — full orchestration on <project_root>/templates/sample-project:
Parallel:
- @explore: map com.example.demo (PersonServiceImpl, PersonController) and tests.
- bash: mvn test -Dtest=PersonServiceImplTest, failures only.
Sequential (no edits):
- Diagnose PersonServiceImpl from ci-logs/unit-tests-failure.log; propose minimal fix (no edits).
Stop and return: architecture map, test status, diagnose proposal for my approval.
Your gate: Review. If PersonServiceImpl is red, approve the fix. Then Tab to Build and send Message 2.
Send only after you approve Message 1's diagnose proposal.
Why combine fix + plan in one message? This is an exception to the single-task rule. The two tasks are sequential and non-conflicting — the PersonServiceImpl fix runs first, then the refactor plan is drafted with tests green. Neither task touches separate concerns. Do not combine tasks that could interact (e.g. two implementers on related classes).
Build primary — apply approved PersonServiceImpl fix only.
Run mvn test -Dtest=PersonServiceImplTest — paste Tests run / Failures line.
Then @general plan only: PersonServiceImpl dedup; extract fetchOrThrow helper used by findById and update.
No edits to PersonServiceImpl yet. Return plan for my approval.
Your gate: Confirm PersonServiceImpl tests are green. Review refactor plan — check method signatures are explicit. Approve, then send Message 3.
Send only after you approve Message 2's refactor plan.
Build primary — implement the approved PersonServiceImpl plan:
<paste approved plan>
Run mvn test -Dtest=PersonServiceImplTest then mvn test -Dtest=PersonServiceImplTest.
@explore read-only: files changed under src/; flag surprises.
Final: test output, files changed, PR-ready yes/no.
Full session reference: The block below shows the complete session at a glance — it is not a single paste-and-run message. Use the three messages above when working in OpenCode.
Plan: parallel triage + diagnose → [gate] → Build: PersonServiceImpl fix + refactor plan → [gate] → Build: PersonServiceImpl implement + verify + scope check
- All tests green
PersonServiceImpldeduplicated and fixed- Scope limited to
com.example.demo
Install /triage first; full orchestration reuses patterns from templates/.
Goal: Recognize bad delegation early and take the keyboard back.
For the general failure patterns (parallel writers, doom loop, scope creep, verify skip, stop conditions, and recovery prompts), see 12-agents-subagents.md — Failure Patterns.
This section covers OpenCode-specific anti-patterns and recovery actions.
Reference: Token Efficiency
| Symptom | Your action |
|---|---|
| "Tests should pass" with no log | Reject; rerun mvn test on Build primary |
| 12 files changed for one bug | Revert; single-file + -Dtest= constraint |
| Two agents edited same file | /undo or git checkout -- <file>; run sequentially |
| Refactor + feature in one General task | Split explore / plan / implement |
| Explore edited files | Plan primary + @explore read-only; check permissions |
| Plan primary implemented code | Plan-only prompt; Tab to Build only after approval |
| Custom subagent writes during Plan | edit: deny for plan-phase agents in opencode.json |
| @mention General while Plan runs parallel Explore | One orchestration message via primary |
Same mvn test fails 3× |
Interrupt; set doom_loop: deny — Token Efficiency |
Agent edits pom.xml silently |
Reject; @explore read-only scope audit on git diff |
Symptom: You asked for triage or plan only; Plan primary edited source files or committed a fix without your approval.
Mechanism: Plan defaults to edit: ask but can still edit if you approve prompts or config is loosened. "Plan only" in natural language is not a hard lock.
Scenario:
You: Plan primary — diagnose PersonServiceImplTest failure; no edits.
Agent: Root cause is null return instead of throw. I've applied the fix in PersonServiceImpl.java.
You never Tabbed to Build or approved the diff.
Your action: /undo or revert. Re-send with explicit gate: no edits; stop for my approval. For hard enforcement, set edit: deny on Plan in opencode.json. Tab to Build only after you approve the proposed fix.
For general failure patterns, see 12-agents-subagents.md — Failure Patterns. OpenCode-specific recovery actions:
| Symptom | OpenCode action |
|---|---|
| Explore edited files | Plan primary + @explore read-only; set edit: deny on Explore agent in opencode.json |
| Custom subagent writes during Plan | edit: deny for plan-phase agents in opencode.json |
| Conflicting @mentions | One orchestration message via primary; do not @mention while primary already orchestrates |
| Agent loops on same test | Set doom_loop: ask in opencode.json; interrupt with Ctrl-C |
For general stop conditions, see 12-agents-subagents.md — Stop conditions.
OpenCode-specific: use /undo to revert agent edits; set edit: deny in opencode.json to enforce read-only mode.
Before approving an implement phase, check whether public surface changed:
git diff HEAD -- '**/src/main/**/*.java' | grep '^[+-].*public 'Signals to block on:
| Signal | Example |
|---|---|
| Changed method signature | - public double calculateTotal(...) / + public BigDecimal calculateTotal(...) |
| Removed public method | - public void legacyDiscount() with no replacement |
| New checked exception on existing method | + throws IOException on a method callers do not expect |
Also review the agent summary for "renamed", "changed return type", or "removed deprecated API". If any public declaration changed without ticket approval, reject and demand minimal fix or explicit API review.
See 12-agents-subagents.md — Failure Patterns for the general recovery prompt template (stop / summarize / redirect).
OpenCode-specific recovery:
Build primary — run mvn test; paste last 40 lines of output.
Then @general with failure excerpt + ci log path; minimal diff; one file only.
Interrupt if looping (Ctrl-C in the TUI, then):
Stop. Summarize what you tried, what failed, and what you need from me.
Do not run more commands until I reply.
- Used correct primary (Plan vs Build) per phase?
- Verify after every implementer?
- Approved plan before Build edited?
stepsanddoom_loopconfigured for long sessions?
Playbooks + templates — replace demo path with your service.
Reference: Agent Model, Java developer quick start.
All examples assume this repo layout:
ai-tools/
├── opencode-agent-patterns/ ← this folder
│ └── README.md ← this file
└── templates/
└── sample-project/ ← demo project (shared with ai-assistant-course)
├── src/main/java/com/example/demo/
│ ├── service/impl/PersonServiceImpl.java ← intentional bug + refactor target
│ ├── controller/PersonController.java
│ └── domain/Person.java
└── src/test/java/com/example/demo/
├── PersonServiceImplTest.java ← one test fails
└── PersonControllerTest.java
Run tests locally:
cd <project_root>/templates/sample-project && mvn test -Dtest=PersonServiceImplTestExpected today: 1 failure in PersonServiceImplTest.findById_whenPersonDoesNotExist_throwsException.
Context:
- Repo: <project_root>/templates/sample-project
- Goal: <one sentence>
Tasks:
1. <step>
2. <step>
Constraints:
- Do not change files outside <project_root>/templates/sample-project unless asked
- Run mvn test before claiming success
Return:
- Files touched
- Test output summary
- Remaining risks
Critical rule: Subagents do not see your chat. Put every path, convention, and acceptance criterion in the prompt.
Primary: Plan or Build
You say: "How is order pricing structured in this project?"
@explore
In <project_root>/templates/sample-project, find all classes and methods
related to person creation, retrieval, and mapping.
Thoroughness: medium.
Return: class names, method signatures, and a 5-line summary of the flow.
Good return:
- Lists
PersonServiceImpl,PersonController,PersonMapper - Notes duplicated fetch-save pattern in
createandupdatemethods - No file changes (read-only)
Primary: Build
You say: "Run tests and show me only failures."
Build primary:
cd <project_root>/templates/sample-project
mvn -q test -Dtest=PersonServiceImplTest
If failures exist, print failing test class/method names and assertion messages.
Do not fix code unless I ask.
Good return:
- Exit code 1
CalculatorTest.isEven_whenNumberIsEven_returnsTrue— expected true, was false
Primary: Plan (diagnose) → Build (fix)
You say: "Why is mvn test failing?"
Plan primary — diagnose only, no edits:
Project: <project_root>/templates/sample-project
Log: <project_root>/templates/sample-project/ci-logs/unit-tests-failure.log
Failing check: unit-tests
Tasks:
1. Read PersonServiceImpl.java and PersonServiceImplTest.java
2. Identify root cause (not just symptom)
3. Propose minimal fix
Return: root cause in one paragraph, suggested patch, files to touch.
After approval:
Build primary — apply approved PersonServiceImpl fix only. Run mvn test -Dtest=PersonServiceImplTest. Paste test summary.
Root cause: PersonServiceImpl.findById uses .orElse(null) instead of .orElseThrow(...) when the person is not found.
Section reference: Parallel Triage
Primary: Plan (plan) → Build (implement)
You say: "Refactor PersonServiceImpl to remove duplication but keep behavior and tests green."
Plan phase:
Plan primary:
@explore read-only — document PersonServiceImpl duplication in create and update methods.
@general plan only — propose fetchOrThrow helper extraction; no edits.
Stop for my approval.
Build phase (paste approved plan):
Build primary — implement approved PersonServiceImpl plan.
Run mvn test -Dtest=PersonServiceImplTest then mvn test -Dtest=PersonServiceImplTest.
Return: before/after summary and test output.
Section reference: Refactor Pipeline
Primary: Build
You say: "Add input validation to create and add tests."
Use java-junit skill.
In <project_root>/templates/sample-project:
1. Update PersonServiceImpl.create to validate duplicate email → throw IllegalArgumentException
2. Add PersonServiceImplTest cases for duplicate email (BDD naming)
3. Run mvn test -Dtest=PersonServiceImplTest
Return list of changed files and test summary.
Primary: Plan (triage)
You say: "Explore pricing, run tests, and draft a refactor plan — in parallel."
Plan primary — orchestrate in parallel on <project_root>/templates/sample-project:
A) @explore read-only: PersonServiceImpl architecture and duplication
B) bash: mvn test -Dtest=PersonServiceImplTest — failures only
C) @general plan only: PersonServiceImpl refactor steps — no edits
Merge into one report: architecture, test status, refactor plan outline.
sequenceDiagram
participant User
participant Plan as Plan_primary
participant E as Explore
participant B as bash
participant G as General
User->>Plan: Parallel analysis request
Plan->>E: Map PersonServiceImpl
Plan->>B: mvn test
Plan->>G: Plan only
E-->>Plan: Code map
B-->>Plan: Test failures
G-->>Plan: Refactor steps
Plan->>User: Combined report
Rule: Parallel tasks must be independent. Don't spawn two agents that edit the same file at once.
Section reference: Parallel Triage
Save as .opencode/commands/triage.md (see templates/triage-session.md).
Run in TUI:
/triageExpands to full parallel triage prompt on Plan primary — one keystroke for Monday standup triage.
- A text summary (findings, commands run, files changed)
- Sometimes artifacts (diffs, log excerpts, file paths)
- Child session inspectable via
Ctrl+Down
| Signal | Good | Investigate further |
|---|---|---|
| Tests | mvn test green (if they ran tests) |
"Should pass" with no command output |
| Scope | Only files you expected | Surprise deletes or broad refactors |
| Cause | Root cause named | Symptom-only ("test failed") |
| Repro | Exact class/method | Vague "fixed the bug" |
Continue (new message in same phase):
- Small follow-up on last output
- Implement step after approved plan (paste plan in)
Start fresh (new General session):
- Task type changes (explore → implement)
- Doom loop after 3 identical failures
- Context would be huge — use
WORKFLOW_STATE.mdinstead
- One clear outcome per subagent ("fix isEven", not "fix everything in the repo").
- Absolute paths to the repo and key files.
- Explicit constraints (read-only, plan-only, which tests to run).
- Return format ("bullet list of files + test output").
- Acceptance criteria ("all tests pass", "no public API change").
See Commands + skills + agents together in Commands, Skills, and AGENTS.md for a task-oriented lookup table.
- Set
stepson Plan and Build agents - Use
doom_loop: askon Plan - Phase resets: plan session → implement session with pasted plan
- Focused tests before full suite:
mvn test -Dtest=CalculatorTest
Full guide: Token Efficiency.
- Spawning a subagent to read one known file — use Read directly.
- Vague prompts ("make it better") — subagent lacks your implicit context.
Full list with worked examples and recovery steps: Anti-Patterns.
Section index: see Workflow sections at the top.
Explore
@explore— list all test classes and what each verifies in <project_root>/templates/sample-project.
Verify
Build primary — run
mvn test -Dtest=PersonServiceImplTestin <project_root>/templates/sample-project and report failures only.
CI / test fix
Plan primary — read ci-logs/unit-tests-failure.log; diagnose PersonServiceImplTest; minimal fix proposal. Then Build — apply fix and run mvn test -Dtest=PersonServiceImplTest.
Refactor
Plan —
@explore+@generalplan only for PersonServiceImpl. After approval, Build — implement plan and run PersonServiceImplTest.
Parallel
Plan primary — in parallel: (1)
@explorePersonServiceImpl, (2) mvn test failures, (3)@generalrefactor plan only.
Custom command
/triage(after installing from templates/triage-session.md)
See Resources at the top for templates, playbooks, and tools.