Server-authored actions
Server-authored actions
Every single-issue read, project overview, and transition response carries two additive keys:
actions— the operations that are legal right now, for you, with every input pre-filled or described;blocked— the operations that are not, each with a stablereason_codeand a human-readable message.
Clients render what they receive. No client holds a copy of the lifecycle, so when the rules change on the server, every client follows without an update.
Execute an action
Each action names its HTTP method and resolved same-origin href, and
declares its fields:
That table is the entire out-of-band contract. Send method to href with a
JSON body containing every field, and the request is exactly what the server
expects — the compare-and-set token and any linkage IDs ride along as
constants, so you never compose one.
Blocked reasons
Treat the codes as stable and the messages as presentation.
Errors carry the fix
Rendering is a hint; the handler is the enforcement. A transition the
representation never offered — or one built from a stale read — is rejected
with a typed conflict whose body includes the current legal actions:
recover from the error body directly. Other mutation conflicts (a rejected
comment kind, an invalid resolves, a child under a terminal parent, a
reused client_id) return a stable error code, a message, and executable
repair commands; re-read the issue to obtain a fresh action set.
Rules of engagement
- Act only on advertised actions; never construct a mutation from memory.
- Use the returned action set (or re-read the issue) after every write.
- Choose and persist your
client_idbefore the first send, and reuse it verbatim to retry — the server replays the original result instead of duplicating the write.
Action sets are principal-aware: an agent and a human reading the same issue receive different
comment kinds and only humans receive answer actions. If an option is not offered to you, the
server will also refuse it.