Skip to main content
OpenCode Tutorial: The Open-Source Terminal Agent

A Worked Example: A Full OpenCode Task

Watch an OpenCode task from start to finish, seeing how it plans, acts, tests, and iterates to transform a real feature request into working code.

Intermediate12 minBy ToolDix Editorial

Learning objectives

  • Trace one complete OpenCode run from task to working code
  • Recognize the perceive-plan-act-observe loop in action
  • See how OpenCode handles iteration when tests fail

ToolDix original visual

OpenCode Tutorial practice loop
1

Frame

Name the outcome and constraints.

2

Build

Try one bounded workflow.

3

Review

Keep evidence, revise, and share.

The task

You have a Node.js + Express REST API for a small task-management app. Users can create, read, update, and delete tasks. Right now, there's no way to assign a task to another user. You want to add that feature:

"Add task assignment. Users should be able to assign a task to another user.
Add an 'assignee' field to the task model. Add a PATCH endpoint
/tasks/:id/assign that takes a user ID and updates the task's assignee.
Only the task creator can assign a task. Run the tests to confirm nothing breaks."

You run OpenCode in plan mode first, to see the strategy before it modifies anything.

Stage 1: Plan (OpenCode reasons about the task)

OpenCode analyzes your task, reads your codebase structure, and outputs:

I've read your codebase. Here's my plan:

CHANGES NEEDED:

1. Database schema
   - File: migrations/add_assignee.js
   - Add a migration that adds an 'assignee_id' column to the tasks table
   - Set as nullable (tasks might not have an assignee)

2. Task model
   - File: src/models/Task.js
   - Add 'assignee_id' field to the Task schema
   - Add a method to validate that only the creator can assign

3. API endpoint
   - File: src/routes/tasks.js
   - Add PATCH /tasks/:id/assign
   - Validate that req.user is the task creator
   - Update task.assignee_id and save
   - Return the updated task

4. Tests
   - File: tests/tasks.test.js
   - Add test: creator can assign a task
   - Add test: non-creator gets 403 Forbidden
   - Add test: assigning to a non-existent user is handled gracefully

SUMMARY:
- 4 files will be modified or created
- All changes are isolated to task assignment logic
- Tests will validate the permission boundary

Ready to proceed? (yes/no)

You read this and think: good strategy. The permission check (only creator can assign) is correct per the requirements. You approve.

Stage 2: Act (OpenCode modifies files)

OpenCode starts executing:

Creating migration file...
✓ src/migrations/add_assignee.js created

Updating Task model...
✓ src/models/Task.js modified (added assignee_id field and validation)

Adding PATCH endpoint...
✓ src/routes/tasks.js modified (added /tasks/:id/assign route)

Running tests...
✗ npm test

Tests failed. Reading error output...

Error: TypeError: Task.findById is not defined

A test failed. The error says Task.findById is not defined. The agent reads the error and reasons: "The test is trying to use Task.findById, but I didn't export it from the model file. Let me fix that."

OpenCode updates the Task model to export the method:

✓ src/models/Task.js modified (exported findById method)

Running tests again...

Stage 3: Observe and iterate (tests pass)

✓ npm test completed

All tests passing:
  ✓ task creation
  ✓ task read
  ✓ creator can assign a task
  ✓ non-creator gets 403 Forbidden
  ✓ assigning to non-existent user returns error
  ✓ all other tests still passing

✓ Task complete

SUMMARY:
- 4 files modified
- All tests passing
- Assignee feature ready to merge

The agent reports success. You can now review the diffs, merge them into your branch, and ship the feature.

What happened at each stage

Let's trace the agent loop through this task:

Turn 1 — Perceive + Plan:

  • Input: the task description and the codebase
  • Agent reads your git repo, understands the current structure (Express + Mongoose, for example)
  • Decides: need a migration, model update, endpoint, tests
  • Outputs: the plan (for your approval)

Turn 2 — Act:

  • Creates the migration file
  • Updates the model
  • Adds the endpoint
  • Runs tests

Turn 3 — Observe (tests failed) + Plan again:

  • Reads the test error
  • Reasons: the test can't find Task.findById, so I didn't export it correctly
  • Decides: update the model to export the method

Turn 4 — Act again:

  • Updates the model file
  • Runs tests again

Turn 5 — Observe (tests passed) + Stop:

  • Reads the test output
  • All tests pass
  • Checks: does the task description say "run the tests to confirm"? Yes, done.
  • Produces final answer: task complete

The loop didn't run for a fixed number of iterations (you didn't hard-code "5 turns"). It ran until the goal was met (all tests passing, feature complete). If something had broken differently and required more iterations, the loop would have continued (up to the max-turn limit you set in config).

Why each stage matters

Plan stage: You got to see the strategy before any files changed. This is why plan mode is valuable for non-trivial tasks. If the agent had misunderstood (e.g., tried to add the assignee field to all columns instead of as a new field, or didn't include the permission check), you could have stopped it and given feedback.

Act stage: The agent didn't just follow a fixed script. It created the files, ran tests, and validated work—actually checking that the code worked, not just writing code.

Observe stage: When tests failed, the agent didn't give up or ask you to debug. It read the error, diagnosed the problem (missing export), and fixed it. This is what makes it an agent, not just a code generator.

Real-world variations

This example went smoothly: one iteration to fix a mistake, then success. Real tasks are often messier:

  • Tests might fail for multiple reasons, requiring multiple iterations
  • The agent might misread your intent and create something you need to reject
  • The agent might get stuck in a loop (trying the same fix repeatedly) and hit the max-turn limit
  • The agent might create code that passes tests but violates a constraint you didn't explicitly mention (e.g., it assumes a specific database setup that your test environment doesn't have)

In those cases, you'd review the work, understand what went wrong, refine the task description, and try again. The agent is a tool for finishing tasks, not a magic bullet that eliminates all need for judgment.

What you'd do next (in a real workflow)

After the agent reports success:

  1. Review the diffs. Check the actual code changes. Is the logic correct? Does it match your intent? Are there any edge cases missing?

  2. Run tests locally. The agent ran tests in the CI environment, but you might want to run them on your machine to be sure.

  3. Test manually. Create a task, assign it, verify the UI works if there is one.

  4. Check the schema migration. Does the migration look right? Will it work on your production database?

  5. Merge and ship. If everything looks good, merge to main and deploy.

The agent didn't replace the human reviewer. It just did the grunt work of writing the code and validating it. You still had the final say.

Why this example is realistic (and where it simplifies)

Realistic parts:

  • The error (missing export) is a real error that would happen
  • The iteration to fix it is typical of real OpenCode runs
  • The permission check (creator-only assignment) is exactly the kind of business logic an agent needs to get right
  • The test feedback loop (run, fail, adapt, retry) is how real code gets written

Simplified parts:

  • No complex edge cases (what if the assignee is already assigned? What if a user gets deleted?)
  • No infrastructure surprises (the migration runs cleanly)
  • No ambiguous requirements (the task description was clear)
  • Only one iteration needed to fix a mistake

In real projects, you'd often have 3-5 iterations, more complex error messages, and cases where the agent needs you to clarify what "correct" means. But the shape—perceive, plan, act, observe, iterate—is the same.

Common mistake

Expecting the agent to be right the first time. It won't be. Real OpenCode workflows involve at least one iteration (usually to fix a test failure or a misunderstanding). Plan your tasks so failures are safe (run tests, validate in a staging environment), and build in time for iteration. Don't expect "run OpenCode once, deploy immediately" to be your workflow—expect "run OpenCode, review the diffs, iterate if needed, then deploy" to be the realistic flow.

Sources and license context

These references informed the lesson. ToolDix adds its own explanation, workflow, and practice rather than reproducing source material. Every link below leaves ToolDix and opens the publisher's own site in a new tab.

Keep going

Read these next on ToolDix.

Original lessons that build on what you just read.