When I first started working with autonomous coding agents, I tried doing what most people do: a single assistant that received the entire prompt in the hope it would design the architecture, write the backend, wire up the frontend, build tests, and make a clean commit. It quickly became obvious that this is a dead end. Within fifteen minutes, such a model loses the thread, forgets safety guidelines, and in extreme cases wipes Git history trying to “resolve a conflict” on its own terms.
To take control over this, I built a custom orchestration template based on OpenCode and split the work across nine strictly separated entities: one coordinator and eight specialized engineers.
Iron Rule: The Orchestrator Does Not Write Code
In my configuration, the primary agent, the Orchestrator, acts strictly as an analyst and project manager. It is categorically forbidden from touching source code files. Its sole responsibility is to take the user’s request, decompose it into discrete tasks, and delegate the work to the appropriate subagents.
There is no room for rogue actions. If a task involves a new API endpoint, the Orchestrator does not attempt to create it. It assigns the task to the backend engineer, waits for the result, and only then decides on the next step.
Eight Roles for Specific Tasks
Each of the eight subagents has a clearly defined role, its own set of system instructions, and dedicated tooling:
- Frontend Engineer – builds user interfaces in React or Next.js, manages styling and application state.
- Backend Engineer – creates API endpoints, database schemas, authentication, and server-side business logic.
- Research Engineer – browses the web, reads library documentation, and extracts data using scraping tools.
- QA Engineer – writes and executes unit and integration tests, verifying test coverage.
- Write Engineer – focuses exclusively on technical documentation, README files, and developer guides.
- Blogger – crafts content for the blog and social media platforms, ensuring the featured image is never duplicated in post bodies.
- Reviewer – runs security audits for OWASP vulnerabilities, secret leaks, and general code quality.
- DevOps – manages CI/CD pipelines, Docker containerization, versioning, and release publishing.
Strict Security and Least-Privilege Access
Security remains the biggest risk when running AI agents. A model with unrestricted shell access will eventually make a costly mistake. That is why I enforced the principle of least privilege across all agent definitions.
Software engineering agents (Frontend and Backend) can edit source files and run local dev servers, but Git repository write operations are completely blocked. Documentation agents (Write and Blogger) are restricted to Markdown files, with their shell access cut down to the bare minimum.
The only agent authorized to commit revisions, tag releases, and push changes to the remote server is DevOps. Before code reaches DevOps, however, it must pass a security audit from the Reviewer and automated test suites run by the QA Engineer. This chain prevents unverified or broken code from landing on the main branch.
State File Instead of Context Bloat
Passing the entire conversation history between so many subagents would instantly blow through context windows and drive up token costs. Instead, I use a dedicated state tracking file: WORKFLOW_STATE.md.
The Orchestrator records the current workflow state, task breakdown, and execution progress in this file. Every subagent reads it at the start of its turn, executes its assigned slice of work, updates the state, and yields execution. Chat history is not the single source of truth, keeping each model’s context clean and focused exclusively on the task at hand.
Routing via OmniRoute and the OpenAI Exception
Each role runs on the model best suited to its workload. This is handled by a local OmniRoute gateway instance in my homelab, where OpenCode queries dedicated profiles (such as omniroute/agent-backend or omniroute/agent-reviewer). Under the hood, the router selects model combos, applies token compression, and manages automatic failovers if an upstream provider goes down.
I made a deliberate exception for OpenAI. API keys for OpenAI are configured directly inside OpenCode, bypassing the local proxy. This complies with OpenAI Terms of Service (ToS) – direct connections from an official client eliminate the risk of traffic being flagged as suspicious or accounts getting restricted. It serves primarily as an independent fallback model in fallback plugin rules.
Local Forgejo via the MCP Protocol
Most out-of-the-box agent frameworks assume GitHub as the default. In my homelab, source control is hosted on a private Forgejo instance (repo.noop.re). The agents communicate with it locally over the Model Context Protocol (MCP). Built container images are pushed directly to a private registry, and CI workflows run inside local Forgejo Actions. The entire development lifecycle stays self-hosted inside my own network.
A Reusable Template
I packaged this entire architecture into the oc-agents-orchestrator template. An initialization script spins up a new project with all agent definitions, permission policies, and CI workflows in seconds, while a synchronization script lets me update configurations across all existing projects as the agent rules evolve.
