Reports and signals
How a card returns a result, how the orchestrator notices it finished, and what "parked" means.
A card reports a structured result that outlives it: the report lives in the table, not in the scrollback. That is how an orchestrator reads what happened without parsing ANSI.
The report
Each report carries:
- the payload, with
ok: truefor success andok: falsefor a declared failure; - a
verdict(aprovado/reprovado/ null), when it is a review; - the
roleof the reporter at the moment of the report — that is how you tell a review from an implementer approving itself; - the
channelit came in through, stamped by the server.
Reports are numbered; reading with the last number seen returns the next one, without re-reading the same and without losing old rounds.
Knowing a card finished
The app pushes a text pointer to whoever should know: the card that spawned it, or the last directive sent, or the board’s orchestrator mark. It is not a system notification — nothing blinks outside the window, and that was asked for, not forgotten. There are four notices:
| Notice | When it fires |
|---|---|
| report available | the card called report |
exited without calling report |
the process died owing a report |
idle without calling report |
it stayed quiet beyond the floor, with a live task |
| came up and produced no byte at all | 30s with no single output — the process is alive and silent, and nothing was terminated |
The third has three different sentences, and the difference is the mechanism, not style: with no agent reading the line, the card could not report and the sentence does not accuse; with the turn declared finished by the card itself, the accusation is backed by a fact it emitted; with no turn fact, it is inferred silence — the sentence says how long the card has been quiet and asks for a check, instead of asserting abandonment. An alarm that is wrong teaches the orchestrator to ignore it, and that is how the true signal dies.
The last is the only one that is not about silence after working, but about never having spoken: it is the honest name of a PTY that produced not a single byte. It warns, never kills — a process that just took a while to paint cannot be destroyed by a clock.
The default remains querying the status and then reading the report.
card_status answers:
| State | What it means |
|---|---|
running |
the declared turn ended and output arrived after — a new turn began |
idle |
turn closed, or stopped waiting for you |
at-prompt |
free shell at the prompt (a shell has no turn, so it is never idle) |
waiting |
blocked on a consent decision |
exited |
the process died |
no-output |
came up and emitted no byte; the first byte erases the state on its own |
unknown |
the output does not distinguish work from repaint |
unknown is not an app failure: a stopped TUI repaints the screen forever,
and a TUI can be running inside a bash card. Read it as check the screen
before deciding a dispatch — never as free.
Talking to a live card
Sending text to a card is queuing, and it returns right away. The text can end in a few different states, and the difference matters:
| State | What happened |
|---|---|
queued |
it is in the card’s queue |
delivered |
confirmed on screen — the agent has the text |
parked |
the provider’s queue held it; the agent has not seen it yet |
unconfirmed |
it wrote, with no evidence on screen |
failed |
visibly stuck and cleared — resend |
cancelled |
the author died before the delivery went out |
parked is not delivered. That distinction was born from real damage:
a “stop now” notice sat in the queue and arrived after the damage. A
correction channel that only delivers at the end of the turn is not a
correction channel.
Lineage
Who created whom is recorded with reason, provider and depth, and survives a restart. Every agent spawn declares a reason — it is the only field the app cannot derive. There is a depth cap on the spawn chain.