Skip to content

attention.pending v1

A card has come to rest on a person for a reason that is neither a question nor a review (charter: waiting cards and broker approvals reach chat §1-2). Pushed when the card parks, and read back by the carrier’s sweep from GET /v1/boards/:id/attention/pending, from one builder. Mirrors SuperPipeline’s AttentionPendingBody (apps/api/src/board/views.ts). It has no subject: nothing is frozen, so nothing is bound. Readers ignore properties they do not know.

Every event below carries dataschema https://raw.githubusercontent.com/SuperJackfruitLabs/treaty/main/schemas/superpipeline/attention.pending.v1.json.

Body event CloudEvents type Subject
attention.pending com.superjackfruit.superpipeline.attention.pending.v1 evt.<workspace>.superpipeline.attention.pending

The subject is the NATS subject the relay files it under; <workspace> is the board’s workspace. See the envelope and subjects.

  • Produced by SuperPipeline, through the relay.
  • Consumed by:
    • SuperExchange: Posts it to the board’s room, for the boards it carries. Receives it through the relay.
    • AgentPod: The hub’s board chat posts it, for the boards it still carries.
    • SuperDispatch: Takes it as a nudge to look at the board soon, on a board opted into scheduling. Reads nothing from its data.

Readers ignore properties they do not know, so a field may be added without a new version.

Field Type Required Description Constraints
event "attention.pending" yes
boardId string or null yes Null only on a board not yet initialised; a carrier needs it to route the event.
boardName string yes Empty string when the board has no name, never absent.
cardId string yes
cardTitle string yes
stageKey string yes
stageName string yes The stage’s name, or its key when the board no longer has that stage.
waitId string yes Names this wait. A card that parks again is a new wait; an answer naming an ended one is refused.
reason one of "repeated-failure", "blocked", "not-authorised" yes
detail string or null yes The run’s own words; null when the park gave none.
summary string or null yes
failureCount integer or null yes How many attempts failed, when the breaker tripped; null otherwise. at least 0
earlierStages array of object yes The stages before this one, in order.
earlierStages[].key string yes
earlierStages[].name string yes
options array of object yes resume_here (approve), one resume_at:<stageKey> (choose) per earlier stage, archive (reject).
options[].id string yes
options[].label string yes
options[].meaning one of "approve", "reject", "revise", "choose", "defer" yes What choosing the option does (gate.v2 §2).
options[].needsComment false yes
options[].input object no What choosing the option asks the person to type.
options[].input.kind one of "text", "url" yes
options[].input.label string yes
options[].input.required boolean yes
options[].input.maxLength integer yes at least 1
deadline object no When the decision is reminded about and escalated. Absent when its stage sets no timeout.
deadline.remindAt string no format date-time
deadline.escalateAt string no format date-time
ts string yes When the card came to rest. format date-time

From fixtures/superpipeline-events/examples.json, case “blocked”:

{
"event": "attention.pending",
"boardId": "brd_134a0ddb6d434478",
"boardName": "Attention board",
"cardId": "crd_0f3c9a2b7d4e4a11",
"cardTitle": "Add login",
"stageKey": "build",
"stageName": "Build",
"waitId": "wait_3c4d5e6f7a8b9c0d",
"reason": "blocked",
"detail": "the staging database is down",
"summary": "Staging DB down",
"failureCount": null,
"earlierStages": [
{
"key": "plan",
"name": "Plan"
}
],
"options": [
{
"id": "resume_here",
"label": "Resume here",
"meaning": "approve",
"needsComment": false,
"input": {
"kind": "text",
"label": "What should the next attempt do?",
"required": true,
"maxLength": 2730
}
},
{
"id": "resume_at:plan",
"label": "Resume at Plan",
"meaning": "choose",
"needsComment": false,
"input": {
"kind": "text",
"label": "What should the next attempt do?",
"required": true,
"maxLength": 2730
}
},
{
"id": "archive",
"label": "Archive",
"meaning": "reject",
"needsComment": false
}
],
"ts": "2026-11-10T09:30:00.000Z"
}

The corpus has 2 valid cases in all:

breaker-tripped
{
"event": "attention.pending",
"boardId": "brd_134a0ddb6d434478",
"boardName": "Attention board",
"cardId": "crd_0f3c9a2b7d4e4a11",
"cardTitle": "Add login",
"stageKey": "build",
"stageName": "Build",
"waitId": "wait_3c4d5e6f7a8b9c0d",
"reason": "repeated-failure",
"detail": "flaky",
"summary": null,
"failureCount": 3,
"earlierStages": [],
"options": [
{
"id": "resume_here",
"label": "Resume here",
"meaning": "approve",
"needsComment": false,
"input": {
"kind": "text",
"label": "What should the next attempt do?",
"required": true,
"maxLength": 2730
}
},
{
"id": "archive",
"label": "Archive",
"meaning": "reject",
"needsComment": false
}
],
"ts": "2026-11-10T09:30:00.000Z",
"deadline": {
"escalateAt": "2026-11-11T09:30:00.000Z"
}
}

Each of these must fail. The corpus says why:

  • the reason is one of three
  • detail is present, null when there is none
  • waitId is required
  • an attention option never asks for a comment