How work flows
The board is the queue and git holds the state. A merged pull request is the only thing that changes status. This page covers plans, features, issues, lanes and specs, and explains why no check can stop a merge.
Updated
Learn how work flows from plan to merge
Reads this page, explains the three words, the five derived steps and the advisory gates back to you, then tests you on real scenarios. Writes nothing.
PROMPT 1 of 2 — learn what this module teaches. You write nothing anywhere in this prompt.
ASK EVERY QUESTION WITH the `AskUserQuestion` tool, with a RECOMMENDED option first. Not prose.
Fetch and read the whole page:
curl -s https://docs.utopiamodels.ai/docs/operating/work.md
If the fetch fails, say what came back and stop.
1 — EXPLAIN IT BACK, UNDER THESE HEADINGS
THE THREE WORDS PLAN, Feature, issue — what each is, the layer it lives on, and why "task" was retired.
Why a PLAN is not a Feature.
THE FIVE STEPS BRIEF · LOOP · FILE · DISPATCHED · DONE — and why every step is derived, never reported.
What the only thing that advances a step is.
LANES Why an issue with no lane is structurally undispatchable however idle the fleet is, and why
"plenty open, zero dispatchable" is a routing defect, not a capacity problem.
THICK TASKS Where spec.md and plan.md live, why the plan.md checkboxes are the live work state, and the
two rules that keep a brief from rotting.
GATES Why nothing gates main, which one gate is the hard stop, which failures are real, and the
two gate designs that are forbidden outright.
FINISHING What "finished" means, and why an armed auto-merge is not it.
THE UNCOMFORTABLE The four parallel trackers the page admits to, and why it publishes that.
Quote the page where you lean on it.
2 — WALK FIVE SCENARIOS
For each, say what the page says happens, and quote the line:
a) A run exits with its pull request open and auto-merge armed.
b) A dispatched issue's body describes the work but names no spec path.
c) A lint on a pull-request title is red.
d) The board shows 40 open issues and the scheduler dispatches none.
e) A master needs another master's input on an issue.
3 — QUIZ ME
Three questions with the `AskUserQuestion` tool, one at a time, four options each, correct answer not always first. Draw from: which layer a Feature sits on, what advances step 5, what a superseded decision record must carry and in which commit, and how presence is established. After each answer, quote the line that settles it.
If `AskUserQuestion` is unavailable in this session, ask the same questions as plain numbered text in one message instead.
Then say exactly this:
"Module learned. Paste prompt 2 and I will map your own work loop onto it."
Then stop.Map your work loop onto plan → issue → merged PR
Surveys your tracker and repo for thin tasks, typed status and second trackers, then proposes spec/plan templates and a merged-PR finish line. Writes only after you confirm.
PROMPT 2 of 2 — integrate this module into my own setup. Survey read-only first; propose; write only what I confirm.
HOW LONG THIS TAKES: a multi-step audit (6 steps, about 4 questions, writes only what you approve) — expect roughly 10–25 minutes across several turns. Running headless with a timeout? Budget for that, or start with a shorter prompt first. Cut off partway? Paste this prompt again — it resumes from its checklist rather than starting over.
ASK EVERY QUESTION WITH the `AskUserQuestion` tool, each with a RECOMMENDED option first and one line of why. If I accept every default without reading, I must still end up with something that works.
Reference, read it first:
curl -s https://docs.utopiamodels.ai/docs/operating/work.md
FIRST — WRITE YOUR CHECKLIST TO A FILE
RESUMING? Before you write anything, check whether ~/WORK-INTEGRATE-CHECKLIST.md already exists. If it does, this prompt was interrupted: read it, tell me which steps are already ticked, and continue from the first unchecked step. Do not overwrite it, do not redo a ticked step, and do not re-ask a question whose answer it already records — the scope answer under ## Rules included, if it's there. If ## Rules has no Scope field, this checklist predates the scope question: say so plainly in your first output line and ask it now, before anything else. Only if the file does not exist, write it fresh as below.
Write ~/WORK-INTEGRATE-CHECKLIST.md and work from it:
# Work module — integrate
- [ ] 1. Find the tracker and how work reaches an agent
- [ ] 2. Measure how thick my tasks are
- [ ] 3. Measure how status actually moves
- [ ] 4. Measure my gates
- [ ] 5. Propose the adoption
- [ ] 6. Write only what I confirmed
## Rules
- Read only until step 6. No tracker writes at any point.
- Every finding carries the command that produced it.
SCOPE — ASK ME BEFORE YOU SURVEY ANYTHING OF MINE
Before step 1, ask me this with the `AskUserQuestion` tool, with both options exactly as written:
"Should I treat any path as off-limits, or do you want me to have full access to survey and propose changes anywhere in this environment?"
A — Exclude .env*, credentials and CI/deploy config (.github/workflows/, deploy keys) from anything I read or propose touching. [RECOMMENDED for most readers]
B — No exclusions. Full access to survey and propose anywhere, including secrets and CI files.
This is my preference, not a policy: some people want their agent to reach everything, some do not, and neither answer is the correct one. Never assume it and never pick for me. My answer governs every read and every proposal for the rest of this prompt. Write my answer under ## Rules in the checklist as "Scope: A" or "Scope: B", so it survives compaction. It governs the SURVEY and PROPOSE steps only — nothing gets written until step 6, and there only what I confirm.
Before you ask, decide whether an answer can reach me at all. It cannot in a one-shot non-interactive run — `claude -p`, `codex exec`, `gemini -p`, a scripted or CI invocation — and you can tell without guessing: the `AskUserQuestion` tool is missing from your tool list there, while an interactive session has it. It also cannot if the question comes back immediately with no real answer. In either case do not ask, do not wait and do not re-ask: a question asked as plain text in a one-shot run ends the run with nothing done. This is the one case where you pick for me: treat A as my answer, record and hold it exactly as above, and make the first line of your output exactly: "No interactive scope answer available — defaulted to A (excluded .env*, credentials and CI/deploy config). To choose B, re-run this prompt in an interactive session and answer the scope question."
Only if the `AskUserQuestion` tool is in your tool list but fails when you call it, ask the same question as plain numbered text in one message instead.
1 — FIND THE LOOP
`gh auth status`, `git remote -v`. Then find how work reaches an agent here: an issue assigned to a bot, a TODO file, a chat message, a label. Say which is the real one. If there is more than one tracker, count them — the reference page admits to four, so do not pretend there is one if there is not.
2 — HOW THICK ARE MY TASKS?
Pull the last 30 closed issues (`gh issue list --state closed --limit 30 --json number,title,body`). For each: does the body name a spec file by path? Does that file exist (`git ls-files | grep <path>`)? Does it carry acceptance criteria? Report the fraction that are THIN — a sentence with no brief an agent could re-ground from.
3 — HOW DOES STATUS MOVE?
For the same 30: was each closed by a merged pull request whose body carries a closing keyword (`gh pr list --state merged --search "closes #<n>"` or the issue's timeline), or closed by hand? Report the fraction. Every hand close is a status a human typed.
4 — WHAT ACTUALLY GATES MAIN?
`gh api repos/<owner>/<repo>/branches/<default>/protection` (a 403 or 404 is a finding, not an error). List the workflows that run on pull requests. For each, classify it the way the page does: hard stop (secret scan), real (build, typecheck, deploy), or not real (a title or filename lint). Flag any gate that blocks on content keywords.
5 — PROPOSE
Terminal output, three parts:
THIN → THICK a spec.md template (objective · acceptance · scope · grounding) and a plan.md template
(phases → checkbox items), and the path convention they live at beside the work
THE FINISH "done = a merged PR whose body closes the issue", and what in my current flow contradicts it
THE GATES which checks to keep as real, which to demote to information, which to delete
Ask me with the `AskUserQuestion` tool which to adopt first. RECOMMEND the templates plus a line in my agent instructions file saying the issue body must name the spec path — the page calls that the highest-leverage line in the pipeline and the easiest to omit.
6 — WRITE ONLY WHAT I CONFIRMED
Create only accepted files. Never modify an existing file you did not create without asking; if the instructions-file line is accepted, show me the exact diff and ask again before applying it. If something does not work, say so and stop.
When the writes are done, tell me in one line, filled in with the real paths:
"To undo, delete `~/WORK-INTEGRATE-CHECKLIST.md` and <the spec.md and plan.md templates at the path convention I accepted> — nothing else on your system was touched."
If I approved an edit to a file you did not create, name that file in the same line with the command that reverts it (`git checkout -- <path>` if uncommitted) instead of claiming nothing else was touched.
FINISH
Every file created with its path, and the three fractions you measured (thin tasks, hand closes, real gates) so I can re-measure them in a month. The runbook companion to this page is https://docs.utopiamodels.ai/docs/operating/operations.md.One principle sits under all of it. We keep no stored copies of state and run no resident processes, and the documents are the config. We delete anything that can drift instead of monitoring it. Anything that runs unattended reports on the board, which is where the founder already looks, so nobody can mistake silence for health.
The three words
There are three layers and one word for each. People confuse these words more often than any others, so the fleet retired a fourth word, "task", which used to mean two of them at once.
| Layer | Word | What it is | Example id |
|---|---|---|---|
| Above the board | PLAN | One thinking session: a document, its research, and the open questions it settled | PLAN-178 |
| On the board | Feature | A body of shippable work with ordered child issues | #5878 |
| On the board | issue | One unit of work you can dispatch, run until its pull request merges | #5882 |
A PLAN is not a Feature. A PLAN references Features but is not one. One PLAN can produce several Features, and some of what a PLAN decides never turns into work at all.
The five steps
1 BRIEF · 2 LOOP ⟳ ‖ 3 FILE · 4 DISPATCHED · 5 DONE| # | Step | What it means |
|---|---|---|
| 1 | BRIEF | The founder's rough note, restated. No research has happened yet |
| 2 | LOOP ⟳ | Research and questions alternate until the plan is complete |
| 3 | FILE | At least one issue exists for this plan |
| 4 | DISPATCHED | A master is doing the work |
| 5 | DONE | Every referenced issue is closed |
Nobody reports a step. The system works each one out. It reads steps 3 to 5 off the board every time it renders, and it reads steps 1 and 2 from events on the plan's own entry. Nothing anyone writes moves a step forward. A merged pull request does, because merging closes an issue. There is no status field for a human to set, so there is no status field for a human to forget.
Step 4 has a mode, and neither mode is "blocked". The two modes are DISPATCHED (unattended) and EXECUTING (interactive), where a human takes part but does not hold the work up. Being stuck is not a state. If you
are stuck, you write a comment on the issue that says exactly what blocks you, and then you exit cleanly.
The one outcome we do not accept is stopping without saying anything.
The loop, end to end
PLAN (a thinking session)
└─ Feature issue + ordered sub-issues + a committed brief
└─ the Architect DISPATCHES (starting the run IS the assignment)
└─ a master executes to merged + live
└─ git moves the board
└─ the founder glances at truthThe board
We use exactly one tracker, a GitHub organization project board. Every open issue in the knowledge repository is on it. When we measured on 2026-08-16, no work existed off the board.
Work that becomes a board issue lives on the board and nowhere else. In particular, it does not also go into an agent harness's private to-do list, because two trackers cannot both be correct.
Each row on the board has the issue, its Lane (the master that serves it), its Priority, its Portfolio, and a Status. The system works the Status out. Nobody types it.
Lanes, and the failure mode nobody expects
A lane sends an issue to a master. What matters most is what happens without one. If an issue has no lane, or sits on a lane that no master serves, no master can ever pick it up. This holds permanently, it happens without any warning, and it does not matter how idle the fleet is.
People misread this signal more than any other in the system. A board with plenty of open work and nothing dispatchable looks exactly like a capacity problem. People have gone looking for a broken scheduler while the scheduler printed the real cause every five minutes. On 2026-08-16 we found thirteen open issues that no master could pick up. Eleven had no lane at all, and two were on a lane that no sweeper reaches. That is a routing defect, and more compute does not fix it.
One command shows the cause:
# on novaserver, from the knowledge clone
bash _infra/scripts/sweep-dispatch.sh --explainBoard hygiene, honestly
We measured two weaknesses, and we record them here as they are:
- Closed rows from other repositories never get cleaned up. The reconciler archives rows only for the repository it runs in, so closed items from sibling repos pile up. On 2026-08-16 they made up most of the rows on the board.
- A row can have no lane and no status at all. Nothing rejects a row like that when someone creates it, and routing cannot see it.
Thick tasks: never work a thin one
A dispatched issue is more than one sentence. Its brief lives in the repository, next to the work:
<portfolio>/specs/<issue#>-<slug>/spec.md objective · acceptance · scope · grounding
<portfolio>/specs/<issue#>-<slug>/plan.md phases → checkbox itemsThe checkboxes in plan.md are the live state of the work. Tick each one through [ ] → [~] → [x] and
commit as you go. This is not a report you write afterwards. After a context reset, a peer taking over or
the founder looking in, everyone reads the same file. This has worked in practice. One master's run hit
its cap, and a different master finished the work from that file alone.
Two rules keep the brief from going stale:
- The issue body must contain the literal spec path. A runner reads a spec only if the issue names it. If the issue does not name the spec, the master doing the work never sees it. This line matters more than any other in the pipeline, and it is the easiest one to leave out.
- If the spec refers to prerequisites that no longer exist, or there is no spec, stop cleanly and explain why on the issue. Do not keep working from a stale brief. A spec can cite machinery that no longer exists without anyone meaning harm, so whoever acts on it checks every reference again in that session.
A non-trivial architecture choice gets a decision record next to the work, in
<portfolio>/specs/<issue#>-<slug>/decisions/. When you supersede one, edit the old record's Superseded-by
header in the same commit. A decision that becomes invalid without anyone recording it is the failure
this fleet repeats most.
Communication
One master reaches another by @mentioning a GitHub account in a comment on the relevant issue. There is no other mechanism. At the start of a master's next dispatched run, the system injects its unread mentions. Each mention arrives at least once, the read state persists, and the conversation happens where the founder already looks.
We run no chat system, no message bus and no inbox service. We had a NATS bus and deleted it. See Decisions.
We work out presence from observable work, such as a last commit or a last comment. Agents never report their own presence. A heartbeat that an agent writes about itself is a claim, not evidence.
Gates: all of them are advisory
Nothing gates main. On this GitHub plan tier, branch protection and rulesets both return 403 in every
repository. We are not exploiting a loophole. That is the real configuration, and we built the fleet's
process to state it openly.
So when a check is red, missing or never started, treat it as information and keep going.
The rule is to fix forward. When a check fails, find the real failure. A broken build or a genuine security finding is real, so fix it. Infrastructure being down is not, so use the documented fallback and carry on. Never wait for a human to unblock a merge.
| Gate | If it fails |
|---|---|
| Secret scan | This is the one hard stop. If the workflow cannot run, run the same pinned scanner locally (see Operations), and merge only after that |
| A real build, typecheck or deploy failure | Real. Fix the code |
| Shell syntax check | Real, and cheap to fix. A script that the shell cannot parse cannot run. Read backwards from the reported line, because the shell reports an unbalanced quote at the next paren, not where the mistake is |
| A lint on a title, a filename, or a doc convention | Not real. It never blocks a merge |
A merge never waits on a human. If a merge does not go through, read the actual error and act on it.
Rebase a branch that conflicts, or use gh pr merge <n> --squash --admin. Escalating a merge with lines
like "needs eyes" or "should I force it?" is banned. You can reverse a merge in git, and a pull request
left open is what actually costs us.
We forbid two gate designs outright, because we tried both:
- A gate that blocks on content keywords or on a generated map. Gates like that blocked documentation by mistake and reverted their own cleanup.
- Resident processes, heartbeat polls and scheduled "health checks". The board is the queue, and the failure we guard against is silence.
Finishing
Work is finished when a pull request whose body says Closes #N merges. The merge closes the issue and
moves the board. Nobody types a status, so the board cannot disagree with what git already recorded.
Turning on auto-merge does not finish the work. If the pull request is still open when the run exits, the work is not done, and the run's close-out comment must say so. Once, fifteen pull requests piled up unnoticed because runs treated an armed auto-merge as finished.
What the loop actually produced
We measured on 2026-08-16. Over the previous seven days the fleet closed 745 issues. Over the previous
twenty-four hours it made roughly 157 dispatch runs, and two of them failed. Both failures filed their
own board issue automatically through systemd's OnFailure path.
That last detail is the useful one. We did not design the system to avoid failure. We designed it so a failure reports itself within seconds, in a place a human already reads.
The uncomfortable part
Our doctrine says "the board is the ONLY tracker" and also "the plan.md checkboxes ARE the work state".
We depend on both statements, and they contradict each other. If we count honestly, one body of work has
four parallel trackers. They are the board, a plan registry, the plan documents themselves, and the
checkboxes in each spec's plan.md. Some plan documents have no registry entry. The command center reads
only entries, so it cannot see those documents.
We publish this instead of hiding it. A wiki that claims one tracker while the fleet runs four is exactly the kind of document this site exists to replace.
Operations
Runbooks for the Utopia fleet. Every command runs exactly as written and names the box it runs on, because a command that works from one machine and fails from another is not a runbook.
The board
One GitHub project board is the only tracker. An agent writes two fields, a job on a runner computes every other field, and status changes only when a merged pull request closes an issue.