Claude Code roles
When a single agent plans a change, writes it and then checks it, it is checking its own work. If it misunderstood the task at the start, it will still misunderstand it at the end, because nothing in between looked at the task again.
This feature splits the work across three roles. The planner turns your task into a plan file and writes no code. The implementer follows that plan and is not allowed to change it. The reviewer compares the result against the plan without having seen the implementation happen. The plan file is what they all work from, and you can read it.
Because the three are separate subagents, each can run on its own model. Planning and reviewing need a model that reasons well. Implementing is mostly writing code against a plan that already names the files and functions, which a smaller model can do.
Catalog name claude-code-roles. Requires the claude
feature in the same workbench.
Add it
Section titled “Add it”monoceros add-feature acme claude-code-rolesThe roles come without a model of their own, so each one runs on whatever model your Claude Code session is on. That already works: you get the plan file and the separate review even when all three run on the same model.
You get more out of the split by giving each role its own model. Here is what that looks like in the config, and the command that writes it:
features: - ref: ghcr.io/getmonoceros/monoceros-features/claude-code-roles:1 options: plannerModel: 'opus' implementModel: 'sonnet' reviewModel: 'opus'monoceros add-feature acme claude-code-roles -- \ plannerModel=opus \ implementModel=sonnet \ reviewModel=opusThe yml is the source of truth, so both tabs describe the same workbench: the command writes exactly that block. To change a model later, edit it there and apply again.
Give the planner and the reviewer your best model. The planner has to understand your task and your code before it can write a usable plan, and the reviewer has to spot the step that is missing. The implementer follows a plan that already names the files, the functions and the command that decides pass or fail, so a smaller model is usually enough.
Apply once and the subagents and the skills are in the container:
monoceros apply acmeThen start Claude Code in the app you want to work on, so the skills find the plans filed for it.
For an app that does not exist yet, name its directory anyway. run asks
whether to create it, which answers the question of where to start when the
project is the thing you are about to build.
monoceros run acme --in=projects/<app> -- claudeUse it
Section titled “Use it”Three skills. Start at the top for a task that needs thinking through, and further down when the earlier work is already done.
/monoceros-plan <your task, an issue number, or a backlog entry>Claude loads the task, looks at the code, and asks before it assumes. One question at a time, each with the answer it will work from if you say nothing, so “yes, yes, no” is a complete conversation. It asks nothing when your task already says what should be true afterwards and how to check it.
The questions are about what you want, not about how it gets built. Not which database, but whether the data should still be there after a restart. Not whether you need authentication, but whether anyone else uses this. One answer often opens the next question, and that is the point. Turning those answers into technical decisions is the planner’s job, and it writes them into the plan where you can read them back.
Then it hands your answers to the planner, which writes the plan file. You get the goal, the acceptance command, the assumptions and the host steps, and then it stops. Nothing is implemented until you say so.
Saying yes is enough. Claude carries on to the next step from there, so you only type the command below when you stopped after the plan and want to pick it up later, or when you are implementing a plan from a previous session.
/monoceros-ship <app>/<slug>The implementer reads the whole plan, works the steps in order, runs the acceptance command the plan names, and reports what changed, the real output of that command, its deviations, and the steps only you can do on the host. Then the review runs, gated on that command having come back green. Findings are repaired for at most two rounds, and if the same finding survives a repair, the chain stops and tells you.
The slug on its own is enough when you are working inside the app. <app>/<slug>
resolves from anywhere, which is the form the previous step hands you.
/monoceros-review <app>/<slug>The reviewer runs the acceptance command itself instead of trusting the report,
and reads the diff. Then it answers PASS or CHANGES_REQUIRED with a numbered
list, each item pinned to file:line.
It checks what the tests cannot: a step that was skipped, something in the diff
no step asked for, an acceptance criterion met in the letter but not in
substance, code that does not fit how the rest of the project is built. It
reports security problems as defects, which block a PASS. It leaves style
alone unless it can say what breaks and when.
When the app serves a page a browser loads, all three roles follow the page’s
references rather than its status code. A dev server answers / with the page
shell whether the app works or not, so a check there can never fail: the plan’s
acceptance command fetches the served HTML and then every script and stylesheet
it names. That is the check that catches a white page.
Options
Section titled “Options”| Option | Default | Description |
|---|---|---|
plannerModel | (empty) | Model for the planning role. |
implementModel | (empty) | Model for the implementing role. |
reviewModel | (empty) | Model for the reviewing role. |
plannerEffort | (empty) | Effort level for the planning role. |
implementEffort | (empty) | Effort level for the implementing role. |
reviewEffort | (empty) | Effort level for the reviewing role. |
Each takes opus, sonnet, haiku, fable, or a full model id such as
claude-opus-5. An empty option means the role runs on whatever model your
session is on. There are no built-in defaults, because a model id written into a
release goes out of date faster than the release does.
All four aliases are Anthropic models, so the roles need no gateway and no second API key: your existing login covers all three. The trade is that you cannot put a planner and a reviewer from different families against each other. OpenCode roles can, through OpenRouter or Zen.
How hard each role thinks
Section titled “How hard each role thinks”Effort is the second dial next to the model: how long a role reasons before it
acts. It takes low, medium, high, xhigh or max, and an empty value
means the role runs on whatever effort your session is on.
options: plannerEffort: 'xhigh' implementEffort: 'medium' reviewEffort: 'high'The split follows the same logic as the models. Planning and reviewing are thinking work: the planner has to understand your code before it writes a plan, and the reviewer has to notice the step that is missing. The implementer works from a plan that already names the files, the functions and the command that decides pass or fail, so it deliberates less and is the cheapest place to turn the dial down.
Worth measuring rather than guessing. In one run all three roles sat on high
by inheritance, which nobody had chosen, and the reviewer alone accounted for
almost 40 percent of the cost.
Where the plans live
Section titled “Where the plans live”Plans are stored in ~/.claude/plans/, not in your project. That directory
survives monoceros apply, and it also survives deleting everything under
projects/, which is what you tend to do between runs.
One folder per app, so /monoceros-ship dark-mode-toggle finds the right plan
from inside the app, and todo-app/dark-mode-toggle finds it from anywhere.
Committing and pushing
Section titled “Committing and pushing”The implementer commits when the project is a git repository: one commit per
round, so the reviewer reads a real diff instead of guessing which untracked
files belong to the change. It does not push. git push, gh pr create and the
package publishers are denied for the implementer and the reviewer.
When the plan scaffolds a new project, the first step is git init plus a
.gitignore, because version control belongs to creating a project. A directory
that was already there is left alone: whether it becomes a repository is your
decision, and the planner only mentions it in its report.
If the container has no git identity, the commit fails with “Please tell me who you are”. The implementer reports that line as it stands and finishes its report. It does not invent a name to get past it, because that name would end up in the history of your project.
You decide what leaves the machine. Everything the roles produce stays in your container: the plan file, the commits and the test output, all of which you can check yourself.
Which language you get answers in
Section titled “Which language you get answers in”You talk to the session in whatever language you use. The planner records that language in the plan, in a field called Reply to the user in, and the implementer and the reviewer read it from there. They never see your messages, so without that field they would answer a German question in English.
The plan file itself stays English, along with code, comments and commit messages. It outlives the conversation and the two roles that execute it work from English prompts.
Customising a role
Section titled “Customising a role”monoceros apply rewrites all six files, so any change you make to them is lost
on the next apply. To keep a change, put your own version in the project:
.claude/agents/monoceros-planner.md takes precedence over the global one and
is never overwritten. The same works for a skill, at
.claude/skills/monoceros-plan/SKILL.md.
The monoceros- prefix on every subagent and skill has a reason. A role called
/plan or implement is exactly what you would write for yourself, and the
names share one space per container.
Remove it
Section titled “Remove it”monoceros remove-feature acme claude-code-rolesThis is also how you change the options from the command line. Adding the feature a second time with different options is an error on purpose, so the yml can never disagree with what you asked for.
The next apply takes the subagents, the skills and their permission guard out
of the container. Whatever other features put in the same directories stays, and
so do the plans you already wrote.