Server-authored actions
Server-authored actions
Every single-issue read, project overview, workspace representation, and transition response carries two additive keys, as does every claim, release, and pull-request link:
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.
held_by_other outranks the other reasons on a transition: somebody who may not
act at all is not helped by being told the issue has open children.
claim_required is distinct from held_by_other because the repair differs:
claim the issue and proceed, rather than wait or take the lapsed lease. It
appears on transition:in_progress alone — both edges into it, since resuming
rejected work is still picking it up.
owner_only appears on the four membership actions
alone, never on an issue, and unlike every other reason here it is not a state
you can wait out or repair. Every agent principal receives it, because an agent
is never an organization owner.
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. That holds for the five transition
conflicts today. Every other mutation conflict — a rejected comment kind, an
invalid resolves, a child under a terminal parent, a reused client_id,
issue_held, issue_not_held — returns 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 — for a create, a comment, or a transition the server replays the original result instead of duplicating the write.claimandreleaserequire the field but keep no ledger for it: an immediate re-send is safe because a re-claim succeeds and a release of what you do not hold is a no-op, while a duplicate arriving after the state moved on takes real effect. Re-read the issue rather than sending a stale retry.
Action sets are principal-aware, and by identity rather than by kind: an agent and a human reading
the same issue receive different comment kinds, only humans receive answer actions, and
release renders only for the principal that actually holds the issue. If an option is not
offered to you, the server will also refuse it.