Spec (a settled design → the contract)
Turn the agreed design into a document that can be graded. That is the whole difference between a
spec and a conversation: /plan, every reviewer and /build all measure their work against this file,
and against a conversation they measure against nothing.
Runs after grill reaches an empty frontier.
This repo describes itself in .agents/, at the repository root. Read the files that bind your step,
and treat them as authority over anything you would otherwise assume.
| File | Answers |
|---|---|
.agents/lifecycle.md |
which phases run, which review lenses are in use, how many rounds each loop gets, how wide a wave may be |
.agents/gates.md |
the commands that must pass, in order, and what "green" means here |
.agents/tracker.md |
where specs and tickets live, and what may be written to the board |
.agents/forge.md |
the git host, and how a branch becomes a reviewed change |
.agents/docs.md |
where architecture truth lives, the glossary, and what "docs are part of done" costs |
.agents/naming.md |
branch, commit, directory and test-name grammar |
.agents/working-agreement.md |
working hours, what may run unattended, how to report, when to stop and ask |
Three rules:
- A missing file is a real answer: that thing is not configured here. Say so in one line and take
the safest reading — do not invent the repo's conventions, and do not fall back on another project's.
If the whole directory is absent, stop and tell the user to run
/onboardinstead of guessing. .agents/beats your own judgement, and loses to the user. It was written deliberately, so a rule you disagree with is reported once, not routed around.- Read only what your step needs. Every line costs on every turn of a long run.
A spec states WHAT. A plan states HOW. The moment a spec names a class, a method, a table or a control-flow step it has become a plan, written before anybody read the code — and it gets none of the checking a plan gets.
The test: could two competent developers satisfy this with different designs? If not, you have specified a design.
The numbers are permanent. Requirements and acceptance criteria are each one numbered list, and the number is the handle every later step uses — a review finding, a review comment, a commit, a plan's coverage table.
- Adding a requirement appends. It never inserts.
- Removing one leaves the number retired:
~~withdrawn~~with the reason and the date. Never reused. - Renumbering is the one edit that silently breaks every consumer. A revision preserves numbers by definition; if it cannot, it stops and says why.
Every requirement and criterion names where it came from — the ticket, a comment, an answered
question, a recorded assumption. A line with no trace is one somebody invented, and inventing
requirements is the easiest failure to commit here: the ticket is terse, the gap is obvious, and filling
it feels like diligence. Fill nothing. A gap goes under ## Open.
A spec with anything under ## Open is a draft, not a contract. Say so in the header and in the
summary, and name the open questions.
## Out of scope is not filler. A reviewer who assumes a nearby behaviour was in scope reports its
absence as a missing requirement, and the round trip is spent explaining that nobody asked for it.
When NOT to use
- The frontier is not empty → back to
grill. A spec with holes is a contract nobody can sign. - A spec already exists for this work → read it and use
--revise. Never write a second one; two specs for one change is a contradiction nothing detects. - A change too small to have requirements. A one-line fix does not need a contract, and writing one costs more than the change.
- Turning it into tasks →
plan.
Step 1 — Where it goes
The path is in .agents/tracker.md — typically <specs dir>/<KEY>-<short-desc>/spec.md, with the
same <short-desc> as the branch per .agents/naming.md. Without a ticket key the directory is just
<short-desc>.
It is committed. Unlike every comparable system, the spec lives in the repo, moves through review and has a history — which is what makes "was this requirement weakened to fit the code?" answerable at all. Do not put it anywhere else.
Step 2 — Write it
**Date:** YYYY-MM-DD
**Status:** Specced
**Ticket:** <KEY> | none
**Scope:** One paragraph — what this covers and what it deliberately doesn't.
## Problem
Two or three sentences: what is wrong or missing today, for whom, and why now. No design.
## Requirements
1. <what the system must do, observably> — *source: ticket description*
2. <...> — *source: Q3 answered B*
3. ~~withdrawn~~ — <why, and when>
## Acceptance criteria
1. <an observable outcome a test or a tester can settle> — *covers requirement 1*
2. <...> — *covers requirements 2, 3*
## Decisions
- **<decision>** — <the reason, one clause>. Rejected: <the live alternative and why not>.
## Constraints
- <an edge case settled without asking> — assumed because <convention / codebase / low-risk default>.
## Out of scope
- <what this deliberately does not cover, especially anything a reader would expect it to>
## Open
- <a question still unanswered> — blocks criterion <n>. *(Delete this section once empty.)*
The header is fixed, not free-form. It is the one place any skill looks to know where the work stands, and no skill re-derives state from a branch name or a directory listing.
**Status:** |
Means | Set by |
|---|---|---|
Specced |
designed and agreed, nothing built | /spec, when it commits |
Building |
in flight; **Wave:** n of m says how far |
/build, on the go-ahead and after each wave |
Built |
the branch is green; **Branch:** carries the name |
/build, at the end |
Superseded by <path> |
a later spec replaced this one | whoever wrote the replacement |
A stale header is worse than no header, because the next run trusts it. Superseded by is the one
that gets forgotten: a spec replaced by a later one and left at Specced is a spec /advance will
happily pick up and plan.
Anything beyond this table is not a status. Do not invent one.
## Decisions carries the rejected alternatives, or the design gets relitigated in three months.
A decision with no live alternative is not a decision; leave it out.
## Decisions is not ## Implementation. Record what was chosen — a library, a boundary, a data
shape the user agreed to. The moment you name a file path, a class or a task order, it belongs in
plan, where it gets scouted against the real code and reviewed.
A criterion nobody can settle is worse than a missing one. It survives every review, because each reviewer grades how completely the wording was satisfied rather than whether it could be.
- State an observable outcome. Never name a function, a control-flow step, or a validity notion the change is expected to introduce. Naming one that does not exist yet hands the implementer a design to invent in order to satisfy the wording, and from then on every review grades how completely the invention was built.
- No proxy evidence. A criterion the repository cannot produce evidence for is not a criterion. The only thing that could satisfy "the code is clear" is a test asserting some prose is still present.
- Verify every count, path, symbol and file the criterion asserts, with a command you actually ran —
a
git ls-files, a grep whose output you read. A number you have not confirmed belongs in the criterion as "every site", a form an off-by-one cannot falsify, not as "the five sites". - Read the criteria together as a set and confirm one implementation can satisfy all of them at once. Two criteria that are individually reasonable and jointly impossible are visible only side by side.
The same bar governs an assumption recorded instead of a question: an assumption you cannot check is a question somebody should have asked.
How much a call is allowed to cost is configured, not improvised. .agents/lifecycle.md § Effort
and budget sets, per role: the model tier, the reasoning effort, and a soft output ceiling.
Read it before dispatching, and pass the tier and effort you find there.
Four levers, in the order they actually move cost:
- How many calls — the lens count. Six lenses cost three times two lenses, and this is the largest lever by a distance.
- Which tier — a cheap tier on a call whose shape is already written down.
- How much effort — reasoning effort per call, independent of tier.
- The output ceiling — how long a report may run.
The ceiling is an instruction to the callee, not an enforced limit. So it is written as a consequence, and the consequence is never "skim":
Stay under about
<n>output tokens. If the work does not fit, returnreviewer-uncertaintyand say what you did not reach — never a shallow pass inside the budget.
A truncated report that reads as complete is the one outcome worse than an expensive one, because the caller cannot tell the difference and ships on it.
Two rules that keep a budget honest:
- Report what a phase cost when the run is over: calls made, tier each ran on, and anything that hit its ceiling. A budget nobody measures is a preference.
- A budget is a default, not a cap on judgement. Where a call genuinely needs more — a diff far past what one lens can hold, a security lens over a permission change — take the higher tier and say in one line that you did and why. Silently overspending and silently underspending are the same defect.
A budget is tight because of the subscription, not the repo, so .agents/lifecycle.md records which
plan is paying and which profile was taken. When it says a lens was dropped for cost rather than
relevance, do not helpfully add it back.
If .agents/lifecycle.md sets no budget, say so once and use the defaults in this document's own model
table.
Step 3 — Check it before anyone builds on it
Everyone who has read this so far was in the room while it was designed, so the question that decides its quality — does this document stand on its own? — is still unasked.
Spawn spec-reviewer once per lens, all of them in one message.
Lenses, and the bodies to paste, are in spec-lenses.md.
Say one line first, so a fresh round of questions does not arrive unexplained:
Four reviewers are reading this cold — none of them saw our conversation. Anything they cannot work out from the document alone is something an implementer will not be able to either.
Route by severity. That is yours, not the user's:
fix— apply it yourself and report a count, not a list.question— back togrill. Each is a live frontier item: ask them as one round, each with your recommendation, then wait. A gap settled here in a sentence is one an implementer would otherwise settle alone at 200k context.note— drop it unless it changes something.
Fold every answer back in, decisions and rejected alternatives included, so the document carries the reasoning rather than the conversation.
The round cap is in .agents/lifecycle.md — typically two, the second only if the first changed
the design. A spec that gained three decisions is worth re-reading; one that gained wording is not. If
a further round still returns question findings, stop spawning reviewers, carry on with the user
directly, and record what is still open under ## Open.
Step 4 — Commit
One commit: docs(specs): design <feature>, per .agents/naming.md. Leave it uncommitted until step 3
is done — what comes back usually changes it.
Then put it in front of the user. They are about to be asked to agree with a document they have so far
only seen as chat messages, so hand it to operator-view with the path
— or, when the requirements are worth reading one at a time and annotating,
show builds it as a page whose numbered lines they can comment on.
Step 5 — Tell the board, if that is a standing write
Read the issue tracker freely. Write to it only as .agents/tracker.md allows — at the repository
root. That file names the tracker, the project key, and the writes that are standing permission here
— typically none, or a small closed set such as claiming a ticket when work starts and commenting the
agreed design and the shipped link.
Limits that hold for every write, whatever the tracker:
- Only the writes that file lists. Anything wider — creating issues, a bulk edit, another field — needs a manifest shown to the user and an explicit go-ahead.
- English, whatever language the session ran in. A board is read outside the team that fills it.
- Only ever forwards. Never drag an item backwards, and never set a status that reports an outcome — those stay human calls.
- If it is assigned to somebody else, leave it and say so. Taking a colleague's ticket silently costs more than it saves.
- Never blocking. Missing credentials, a 404 or an outage each cost one line, and the run carries on. The committed spec is the authority; the board is a convenience.
Say in one line what you wrote and where. If .agents/tracker.md is missing, write nothing and
carry on with the local files.
Revising
--revise rewrites an existing spec in place, preserving every number. New requirements append;
removals become ~~withdrawn~~. If it cannot preserve the numbering, it stops and says why.
A revision during implementation is a retrofit risk. Weakening a requirement so the code passes turns a failed implementation into a passing one without anybody deciding to. So a revision says in the header what changed and why — and because the spec is committed, the diff backs that up.
An old-shape spec is left alone until it is revised. Specs written before this numbering existed
are read as they are; --revise is what gives them numbers.
Chat summary
The path, the status, the counts (N requirements, M criteria, K constraints), every open question by
name if the status is draft, and anything the grounding pass found — a requirement the codebase already
satisfies, one that looks impossible, an attachment nobody could read. Then: /plan is next.
Language
The spec is English. Chat may stay in the user's language.
Dispatching the agents
These agents ship inside this plugin, so their subagent_type carries the plugin prefix:
lifecycle:code-scout, lifecycle:spec-reviewer, lifecycle:change-reviewer,
lifecycle:ticket-implementer, lifecycle:finding-fixer, lifecycle:operator-view.
A bare name may resolve, and it may also pick up a different agent the repo happens to define. Pass the prefixed form. Where this document links an agent by file, the prefixed name is what goes in the dispatch.