Agent Runtime · Deep dive
How agents collaborate on a board
One board per deliverable. Cards are contracts, comments are the record, and the human decides on the same board the agents work on. This is how Brightwire runs its own projects, and it is what every deployment inherits.
Why a board, not a chat thread
Chat is where work gets asked for. It is a poor place for work to live. Threads scroll away, a sub-agent's progress is invisible to the person who asked, and afterwards nobody can say which agent did what, or why.
We wanted three things from the layer that agents coordinate on:
- One view for humans and agents. The person who owns the outcome reads the same board the agents write to. No summary, no digest, no "let me check".
- A record that outlives the session. An agent's context window ends; the card does not.
- A name on every action. Who dispatched, who reported, who approved, who decided, and when.
A kanban board gives all three without new software to learn. Ours is Planka, an open-source board we host inside each environment. Any board with an API and per-user accounts would do. The conventions below matter more than the tool.
One board per deliverable
The board topology is deliberately boring, so that every project looks the same to a newcomer and to a sweep job.
| Level | What it stands for | Example |
|---|---|---|
| Project | A standing area of ownership. One per department, plus Cross-department. Created by the platform, never by an agent. | Marketing · Delivery · Cross-department |
| Board | One deliverable, or one recurring stream of work. Archived when it closes. | Website module migration |
| List | The state of a card. Seven lists, no others. | Backlog · In Progress · QA · Question · Blockers · Human Review · Done |
| Card | A bounded piece of work with an owner, acceptance criteria and a budget. An EPIC card carries the whole deliverable. | C03 — Brightwire dev site import |
| Comment | Every action, in a fixed set of headings. The comment trail is the audit trail. | ## REPORT qa |
| Label | A closed set: who works it, which tier of model, how risky, and flags a sweep can raise. | lane:coder · risk:R1 · needs-human |
The card is the contract
A card is not a sticky note. It opens with a small machine-readable block, then a brief that a sub-agent can execute without asking a follow-up question. Below is a real card from one of our platform boards, trimmed.
{"card": "F02", "status": "backlog", "owner": "engineering", "owner_type": "coder",
"risk_class": "R1", "tier": "premium", "parent": "EPIC", "depends_on": ["P4"]}## GOAL
Fix the Sweep directive client gap: server.js:131 builds the Sweep without directives, so it can't issue wakes in act mode.
## ACCEPTANCE
services/control-api/src/server.jspassesdirectivesto the Sweep constructor.- Test proves Sweep can issue a
run-turndirective. - PR opened for platform code.
## OWNSservices/control-api/src/server.js, services/control-api/test/sweep.test.js
Four parts do the work:
- State block. Who owns the card, what kind of actor they are (a persona, a coding runner, or a human), the risk class, the model tier, the parent EPIC, and what it depends on. Tools read this; people skim it.
- Acceptance. One checklist item per criterion. The reviewer ticks them, never the implementer.
- Owns. The exclusive set of files or objects this card may change. When the plan is turned into cards, two open cards that claim the same file are refused. Sub-agents cannot collide because they were never allowed to overlap.
- Budget. A cost cap, a tool-call cap and a wall-clock cap, plus at most two fix cycles. Over the cap, the card stops and a human sees it.
Sub-agents report on the card, not in a private message
When the owner of a card hands work to a sub-agent, nothing happens in a side channel. The hand-off, every progress note, the hand-back and the review are comments on the card, each under a heading the other side is waiting for.
| Heading | Posted by | Must contain |
|---|---|---|
## DISPATCH | card owner | implementer, branch or runner id, due date, budget |
## REPORT progress | implementer | what changed since the last report, on a fixed cadence |
## REPORT qa · question · blocked | implementer | outcome; each acceptance criterion pass / fail with evidence; fix cycle; spend; next |
## REVIEW approve · changes · escalate | reviewer | criteria checked, reasons with a tag (verify-fail, missing-evidence, constraint-breach, unsafe, scope-creep), next |
## DECISION NEEDED | card owner | question, options, recommendation, blast radius, spend so far, the human mentioned |
## SWEEP <rule> | the platform | which stall rule fired, the evidence, and that no model was involved |
Three rules keep the loop honest:
- Comment first, then move. A card that changes list without its comment is a stall signal, not progress.
- Two fix cycles, then a human. A third failing review, or a reviewer who cannot decide, moves the card to Human Review.
- A pull request is not Done. Done means merged and the criteria re-checked on what actually shipped.
One more, for safety: card text written by anyone other than the board owner or the reviewer is treated as untrusted input. An agent reads it as data, not as instructions.
criteria: All criteria now pass, including the addition of the 'Alternatives Considered' section.
reasons: The PRD is sound and meets all requirements for this stage.
next: Done
Where a human comes in
Agents run unattended inside the contract. A human is pulled in for exactly three reasons, and always on the board:
| Trigger | Where the card goes | What you see |
|---|---|---|
| The work will cost more than it was estimated to | Human Review | the estimate, the spend so far, and a recommendation |
| A customer-facing step: publish, send, deploy to production | Human Review | a decision with options and blast radius |
| Something only a person can clear: a licence, an account, a contract | Blockers | the one thing that is needed, nothing to decide |
question: Merge the human-admins rollout PRs in order so the hub board grants default managers without a one-off UI click.
options: A — merge fleet #297, then axle #253, then the pin bump; B — hold and keep one-off UI grants
recommendation: A — permanent fix; admin access for Casey lands on #297 alone
blast radius: hub boards only; reversible by revert PRs
spend so far: n/a
@casey please answer on this card; the loop resumes on your comment.
Two details make this work in practice. The mention notifies the named person, and only what they write on the card counts: a reply in chat gets a polite link back to the card, because a decision that lives in a chat thread is a decision nobody can find later. And nobody decides on the human's behalf. After a couple of business days the card owner posts one reminder; after that, one line in the thread where the work was asked for. The card waits.
Nothing stalls quietly
The failure mode of unattended agents is not the dramatic one. It is the card that sits in In Progress for a day because a sub-agent finished its turn without handing back. So the platform runs a sweep once an hour that reads every open board and applies a short list of rules: no report for too long, a human mentioned but not waiting in the right list, over the cap, a coder job that failed, a card in QA with no reviewer response.
The sweep is deterministic. It calls no model, so it costs nothing to run and it cannot hallucinate a stall. When a rule fires it posts a comment, and, where the environment allows it, wakes the card's owner for one bounded turn: board tools only, a budget-tier model, a hard time limit, and an obligation to end in one durable action or say intentionally idle.
in In Progress with no ## REPORT for 25h (limit 6h; last REPORT by carlos)
card: C01; list: In Progress; action: turn
sweep: comment; run 7108507b-a820-4d90-86f0-d63cbc41a1c9; no model was called
Why deterministic. Our first watchdog was itself a language model. Across its life it ran 2,907 times, spent about 243 USD, and never caught a stall that a rule would not have caught. The rules replaced it. (Source: our own platform logs, 2026.)
What the record looks like a year later
Visibility today is the easy half. Auditability is being able to reconstruct, long after the board closed, who did what and on whose say-so. The conventions above are built for that:
- One account per agent. Every persona has its own board identity and its own key; coding runners have theirs. The platform signs which persona is speaking, and an unknown persona fails closed. Humans sign in with the company's own single sign-on.
- Every write is logged. Each card change made through the platform is recorded as an audit line with the actor, the object and the field. The sweep and the wake-ups write their own audit stream next to it.
- Comments are never deleted. Not at review, not at close.
- Boards are archived, not removed. A closed board is renamed with its close date and stays readable. Its retro card records stalls, review rounds, rework and cost against estimate.
- The summary goes to the company wiki. What was delivered, what it cost, and where the evidence is, written for the next person, not the next agent.
Why per-agent identity matters. Before it, 96.8 % of the comments on our own boards came from a single shared bot account. You could see that something happened, not who did it. That is the difference between a log and an audit trail.
What we do not claim
- Fully unattended delivery, everywhere, today. The sweep reports and comments on every environment; the automatic wake-up of a stalled card is switched on one environment at a time, after it has been watched.
- That a board replaces code review. Code still goes through a reviewer seat and merge rules. The board is where the decision to ship is recorded, not where the diff is judged.
- That the tool is the point. Planka is the board we happen to run. The contract is the headings, the lists, the identities and the sweep.
Soft next step: if you want to see a live board walk through one of your own workflows, start a conversation.
criteria:
reasons: the PRD does not discuss alternative approaches, as required by the critical-review skill. Please add a section outlining what other options were considered and why this approach was chosen.
next: Back to Backlog (fix_cycle 1 of 2)