An issue is a note
We have already written about Issue types and Turn. One explains how a workflow becomes a note; the other explains why the board groups issues by whose move it is. Both circle around the same thing without ever looking at it directly: the issue itself.
This post fills that gap.
An issue is a note
In Bonsai, a project’s issues live inside the project folder:
1_Projects/<project>/Issues/12.md
The file name is the number. Numbers are unique within the project and never change; rename the issue and the file stays put. Every card on the board is one of these notes.
There is no second database. The board, the issue page and an AI agent calling list_issues over MCP all read the same files. You can open an issue in the note editor, search it, and link to it from any other note with [[…]]. When the project is archived, its issues are archived with it.
We built it this way because what goes into an issue is knowledge: why the work matters, what counts as done, which calls were made along the way, what was learned. Put that in a system separate from your project notes and the next agent has to look in two places.
The top half: facts
An issue’s front matter records only the facts about this piece of work. A feature halfway through looks roughly like this:
---
type: Feature
state: Reviewing
priority: high
owner: Guoliang
goal: 9
blocked_by: 10, 11
repo: livewithbonsai/Bonsai
branch: issue/12-cache-hint
pr: livewithbonsai/Bonsai#89
---
Field by field:
| Field | Meaning |
|---|---|
type | What kind of work this is. Points at one of your type notes: Feature, Bug, Task, Goal |
state | Which step of the workflow it is on. The allowed values come from the type note |
priority | urgent, high, medium, low, or nothing |
owner | The person responsible. Always a human, and it does not change |
goal | The goal this belongs to: the number of a Goal issue in the same project |
blocked_by | Which issues must finish first, as a list of numbers |
repo | The code repository. Leave it out and the project note’s repository is used |
branch, pr | The branch and pull request, written back by the agent once they exist |
The UI calls it “Status”, but the front matter key is state. status is reserved for the server in Bonsai’s note format.
What is not here matters more.
No model, no machine. Which model writes the code and which computer runs it are not facts about this issue; they are rules about how this kind of work gets done. They live in type notes and role notes. Swap the reviewing model in one role note and every issue follows.
No assignee. The “whose turn” shown on a card is computed. In the agent’s turn it is the role the type note dispatches for that step; in your turn it is the owner. On a team of people, assignee is a field you keep updating by hand. With agents it changes every hour, so Bonsai does not store it.
No run history, no CI status. Whether a run is going right now is the present, not a fact, and Bonsai keeps it elsewhere. Whether the PR merged or CI passed is GitHub’s fact; the issue only holds a pointer.
So an issue is light. We often compare it to starting a conversation with Claude: you do not fill in a form, you say what you want.
The bottom half: for you and for the agent
Below the front matter is the body. A new issue starts from its type’s template, and the built-in template has four sections:
Description. What to do, why now, testable acceptance criteria, and links to the notes an agent must read before it starts. The acceptance criteria are your spec for the agent. The reviewing agent checks every line, but no agent may change them.
Decisions. Calls that need you. One heading per decision, a few options below it, the agent’s pick marked “recommended”, and always an “Other”. Click an option on the issue page and the decision is made; the issue moves on.
You should know. After every run the agent rewrites this section with the few things you most need to know. Anything worth keeping beyond this issue gets marked “worth remembering”.
Log. Append only. One entry per run: date, role, round, what happened. Every review round is recorded here too.
The order follows what you need when you open an issue: what this is, what you have to decide now, what you should know, and only then the full history.
The type decides how it moves
An issue does not know what to do next. Its type does.
The type note lists the states, which of the four Turns each belongs to, which role to dispatch in each agent state, where that role may move the issue, and how many rounds it gets. The built-in Feature runs like this:
Ready → Developing → Reviewing → Review PR → Deploying to Staging → Verify Staging → Deploying to Production → Done
Bold steps are the agent’s turn; italic steps are yours. The issue bounces between the two, and the board groups it by Turn with Your turn on the far left.
This split keeps issues clean: the issue records this piece of work; the type records this kind of work. You never re-plan the workflow for each issue.
The engine: dispatch when it is time, stop when it should
When an issue enters an agent state, Bonsai now dispatches the role from that state’s row in the type note to your computer. You do not click anything. This is the Automation engine.
Its rules are few, and deliberately dumb:
- It reads the front matter’s
typeandstateand the issue’s runs. It does not read the body, judge the code, or pick a model. - One issue has at most one run going at a time.
- If an agent finishes without handing the issue on, hands it to a state the row does not allow, or the step has hit its round
limit, the issue moves to Needs Decision with a line in the Log explaining why, and waits for you. - In your turn it does nothing. A state with no row is where the work stops for a person.
- It never moves an issue to Done, Canceled or any other closing state. Closing is yours.
We want you to be able to predict what it will do. A scheduler that guesses your intent sounds nice until it guesses wrong and you have no idea where to look.
Goals: one issue that carries others
A Goal is not a separate kind of object. It is an issue whose type is Goal. Its body adds a Plan table, and child issues point back to it with goal: <number>.
A goal goes from idea to done like this:
- You create a Goal with one line on what done looks like and why. It sits in Draft.
- You open a chat beside the Goal. That chat is the Planner. It reads the project notes and the code (read-only), splits the goal into child issues with their
blocked_bydependencies, fills in the Plan table, and stops. It does not change any state or dispatch any work. - You review the split and click Approve on the Goal page. That is the only approval the whole goal needs.
- The engine takes over. Children whose dependencies are all closed move into their first agent state and start, two at a time by default (adjustable per project). When one finishes, whatever it was blocking can start.
- When every child is closed, the Goal closes itself.
Along the way, each child still stops at Review PR, Verify Staging and Needs Decision for you. Approving a goal once does not sign away every merge that follows.
What stays with you
Put together, these are the parts of an issue that are always yours:
- Writing acceptance criteria, and approving a Goal’s split.
- Deciding: every choice in Decisions.
- Reviewing the PR and checking staging: the states in your turn.
- Closing: Done, Canceled and Duplicate are yours to set.
- Changing the rules: type notes and role notes live in folders starting with
_, which no agent permission can write.
When an issue is done, the distill role reads its You should know section and writes the lines marked “worth remembering” back into the project notes. The next agent to work on the project starts from what this one learned.
Where things stand
All of this is live: the fields, the four-section body, type notes, the Turn board, the Automation engine, Goals and the Planner. The top of the issue page lists every field, and status, type, priority, owner, goal, dependencies, repository and PR can all be edited there.
The full field reference is in the docs: Issues. For writing a workflow see Issue types, for the engine’s rules see Automation, and for splitting a goal see Planner.