gate.pending v1
A gate is waiting on a person (charter: a gate closes over chat; gate.v2). Pushed by SuperPipeline, and read back by the carrier’s sweep from GET /v1/boards/:id/gates/pending, from one builder. Mirrors SuperPipeline’s GatePendingBody (apps/api/src/board/views.ts). Readers ignore properties they do not know.
CloudEvents
Section titled “CloudEvents”Every event below carries dataschema https://raw.githubusercontent.com/SuperJackfruitLabs/treaty/main/schemas/superpipeline/gate.pending.v1.json.
Body event |
CloudEvents type |
Subject |
|---|---|---|
gate.pending |
com.superjackfruit.superpipeline.gate.pending.v1 |
evt.<workspace>.superpipeline.gate.pending |
The subject is the NATS subject the relay files it under; <workspace> is the board’s workspace. See the envelope and subjects.
Who sends it, who reads it
Section titled “Who sends it, who reads it”- 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.
Fields
Section titled “Fields”Readers ignore properties they do not know, so a field may be added without a new version.
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
event |
"gate.pending" |
yes | ||
boardId |
string or null | yes | ||
boardName |
string | yes | What the board is called; empty string when it has no name, never absent. | |
cardId |
string | yes | ||
gateId |
string | yes | ||
stageKey |
string | yes | ||
returnStageKey |
string | yes | ||
cardTitle |
string | yes | ||
producedBy |
string | yes | ||
handoffSummary |
string or null | yes | What the reviewer is asked to approve; null for a gate with no handoff. | |
approvalSubject |
object (subject) or object (variantSubjects) | no | ||
approvalSubject.id |
string | yes | in the subject shape |
|
approvalSubject.digest |
string | yes | in the subject shape |
|
approvalSubject.schema |
string | yes | in the subject shape |
|
approvalSubject.revision |
integer | yes | in the subject shape; at least 0 |
|
approvalSubject.expiresAt |
string or null | yes | in the subject shape |
|
approvalSubject.preview |
array of object (type = “text”) or object (type = “field”) or object (type = “link”) | yes | in the subject shape |
|
approvalSubject.preview[].type |
"text" |
yes | in the type = "text" shape |
|
approvalSubject.preview[].label |
string | no | in the type = "text" shape |
|
approvalSubject.preview[].text |
string | yes | in the type = "text" shape |
|
approvalSubject.preview[].type |
"field" |
yes | in the type = "field" shape |
|
approvalSubject.preview[].label |
string | yes | in the type = "field" shape |
|
approvalSubject.preview[].value |
string | yes | in the type = "field" shape |
|
approvalSubject.preview[].type |
"link" |
yes | in the type = "link" shape |
|
approvalSubject.preview[].label |
string | yes | in the type = "link" shape |
|
approvalSubject.preview[].url |
string | yes | in the type = "link" shape |
|
approvalSubject.previewComplete |
boolean | yes | False when the preview was cut to its bounds; no approving option may then be offered. | in the subject shape |
approvalSubject.variants |
array of object | yes | in the variantSubjects shape; at least 1 item |
|
approvalSubject.variants[].id |
string | yes | in the variantSubjects shape |
|
approvalSubject.variants[].label |
string | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject |
object | yes | A subject as a decision envelope carries it (gate.v2 §3): identity, expiry and the kind’s bounded preview. The canonical payload stays behind the authenticated route. | in the variantSubjects shape |
approvalSubject.variants[].subject.id |
string | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject.digest |
string | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject.schema |
string | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject.revision |
integer | yes | in the variantSubjects shape; at least 0 |
|
approvalSubject.variants[].subject.expiresAt |
string or null | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject.preview |
array of object (type = “text”) or object (type = “field”) or object (type = “link”) | yes | in the variantSubjects shape |
|
approvalSubject.variants[].subject.preview[].type |
"text" |
yes | in the type = "text" shape |
|
approvalSubject.variants[].subject.preview[].label |
string | no | in the type = "text" shape |
|
approvalSubject.variants[].subject.preview[].text |
string | yes | in the type = "text" shape |
|
approvalSubject.variants[].subject.preview[].type |
"field" |
yes | in the type = "field" shape |
|
approvalSubject.variants[].subject.preview[].label |
string | yes | in the type = "field" shape |
|
approvalSubject.variants[].subject.preview[].value |
string | yes | in the type = "field" shape |
|
approvalSubject.variants[].subject.preview[].type |
"link" |
yes | in the type = "link" shape |
|
approvalSubject.variants[].subject.preview[].label |
string | yes | in the type = "link" shape |
|
approvalSubject.variants[].subject.preview[].url |
string | yes | in the type = "link" shape |
|
approvalSubject.variants[].subject.previewComplete |
boolean | yes | False when the preview was cut to its bounds; no approving option may then be offered. | in the variantSubjects shape |
reviewUrl |
string | no | Authenticated web route for reviewing the authoritative subject. | |
options |
array of object | yes | ||
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 |
boolean | yes | ||
options[].delivery |
one of "automatic", "manual" |
no | Only on an option that approves a subject. | |
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 |
|
quorum |
object | no | A quorum gate’s progress; absent on a gate one approval passes. | |
quorum.required |
integer | yes | at least 1 | |
quorum.from |
one of "deciders", "admins" |
yes | ||
quorum.approvals |
array of object | yes | ||
quorum.approvals[].principalId |
string | yes | ||
quorum.approvals[].at |
string | yes | format date-time |
|
quorum.approvals[].variantId |
string | no | ||
release |
object | no | What holds an approval before it acts; absent when nothing does. | |
release.holdForMinutes |
integer | no | at least 0 | |
release.notBefore |
string | no | format date-time |
|
checks |
array of object | no | The checks that ran before the gate opened; absent when the stage has none. | |
checks[].name |
string | yes | ||
checks[].provider |
one of "ci", "verdict" |
yes | ||
checks[].status |
one of "pending", "passed", "failed", "overridden" |
yes | ||
checks[].url |
string | no | ||
ts |
string | yes | When the gate opened, on the gate’s own clock, so a re-read is byte-identical. | format date-time |
Example
Section titled “Example”From fixtures/superpipeline-events/examples.json, case “plain”:
{ "event": "gate.pending", "boardId": "brd_134a0ddb6d434478", "boardName": "Developer Experience", "cardId": "crd_0f3c9a2b7d4e4a11", "gateId": "gte_5a1b2c3d4e5f6071", "stageKey": "review", "returnStageKey": "research", "cardTitle": "Research this", "producedBy": "agt_9e8d7c6b5a493827", "handoffSummary": "Two sources.", "options": [ { "id": "approve", "label": "Approve", "meaning": "approve", "needsComment": false }, { "id": "request_changes", "label": "Request changes", "meaning": "revise", "needsComment": true }, { "id": "reject", "label": "Reject", "meaning": "reject", "needsComment": false } ], "ts": "2026-11-10T09:30:00.000Z"}The corpus has 5 valid cases in all:
subject-bound
{ "event": "gate.pending", "boardId": "brd_134a0ddb6d434478", "boardName": "Developer Experience", "cardId": "crd_0f3c9a2b7d4e4a11", "gateId": "gte_5a1b2c3d4e5f6071", "stageKey": "review", "returnStageKey": "research", "cardTitle": "Research this", "producedBy": "agt_9e8d7c6b5a493827", "handoffSummary": "Two sources.", "options": [ { "id": "approve_automatic", "label": "Approve and post", "meaning": "approve", "needsComment": false, "delivery": "automatic" }, { "id": "approve_manual", "label": "Approve, I'll post it", "meaning": "approve", "needsComment": false, "delivery": "manual" }, { "id": "request_changes", "label": "Request changes", "meaning": "revise", "needsComment": true }, { "id": "reject", "label": "Reject", "meaning": "reject", "needsComment": false } ], "ts": "2026-11-10T09:30:00.000Z", "approvalSubject": { "id": "sub_7d6c5b4a39281706", "digest": "sha256:4f2a0c1e9b8d7f6a5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7", "schema": "social-publish/v1", "revision": 1, "expiresAt": "2026-11-11T09:30:00.000Z", "preview": [ { "type": "text", "label": "Post", "text": "We shipped the decision envelope." }, { "type": "field", "label": "Account", "value": "@example" }, { "type": "link", "label": "Draft", "url": "https://example.com/draft" } ], "previewComplete": true }, "reviewUrl": "https://app.superpipeline.dev/b/brd_134a0ddb6d434478/c/crd_0f3c9a2b7d4e4a11/review"}variants
{ "event": "gate.pending", "boardId": "brd_134a0ddb6d434478", "boardName": "Developer Experience", "cardId": "crd_0f3c9a2b7d4e4a11", "gateId": "gte_5a1b2c3d4e5f6071", "stageKey": "review", "returnStageKey": "research", "cardTitle": "Research this", "producedBy": "agt_9e8d7c6b5a493827", "handoffSummary": "Two sources.", "options": [ { "id": "choose", "label": "Choose a variant", "meaning": "choose", "needsComment": false }, { "id": "reject", "label": "Reject", "meaning": "reject", "needsComment": false } ], "ts": "2026-11-10T09:30:00.000Z", "approvalSubject": { "variants": [ { "id": "short", "label": "Short", "subject": { "id": "sub_7d6c5b4a39281706", "digest": "sha256:4f2a0c1e9b8d7f6a5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7", "schema": "social-publish/v1", "revision": 1, "expiresAt": "2026-11-11T09:30:00.000Z", "preview": [ { "type": "text", "label": "Post", "text": "We shipped the decision envelope." }, { "type": "field", "label": "Account", "value": "@example" }, { "type": "link", "label": "Draft", "url": "https://example.com/draft" } ], "previewComplete": true } }, { "id": "long", "label": "Long", "subject": { "id": "sub_8e7d6c5b4a392817", "digest": "sha256:4f2a0c1e9b8d7f6a5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7", "schema": "social-publish/v1", "revision": 1, "expiresAt": "2026-11-11T09:30:00.000Z", "preview": [ { "type": "text", "label": "Post", "text": "We shipped the decision envelope." }, { "type": "field", "label": "Account", "value": "@example" }, { "type": "link", "label": "Draft", "url": "https://example.com/draft" } ], "previewComplete": true } } ] }}deadline-quorum-release-checks
{ "event": "gate.pending", "boardId": "brd_134a0ddb6d434478", "boardName": "Developer Experience", "cardId": "crd_0f3c9a2b7d4e4a11", "gateId": "gte_5a1b2c3d4e5f6071", "stageKey": "review", "returnStageKey": "research", "cardTitle": "Research this", "producedBy": "agt_9e8d7c6b5a493827", "handoffSummary": "Two sources.", "options": [ { "id": "approve", "label": "Approve", "meaning": "approve", "needsComment": false }, { "id": "request_changes", "label": "Request changes", "meaning": "revise", "needsComment": true }, { "id": "reject", "label": "Reject", "meaning": "reject", "needsComment": false } ], "ts": "2026-11-10T09:30:00.000Z", "deadline": { "remindAt": "2026-11-10T13:30:00.000Z", "escalateAt": "2026-11-11T09:30:00.000Z" }, "quorum": { "required": 2, "from": "deciders", "approvals": [ { "principalId": "prn_0123456789abcdef0123", "at": "2026-11-10T10:00:00.000Z" } ] }, "release": { "holdForMinutes": 30, "notBefore": "2026-11-10T12:00:00.000Z" }, "checks": [ { "name": "ci / test", "provider": "ci", "status": "passed", "url": "https://forge.example.com/runs/1" }, { "name": "tone", "provider": "verdict", "status": "overridden" } ]}no-board-name-no-handoff
{ "event": "gate.pending", "boardId": null, "boardName": "", "cardId": "crd_0f3c9a2b7d4e4a11", "gateId": "gte_5a1b2c3d4e5f6071", "stageKey": "review", "returnStageKey": "research", "cardTitle": "Research this", "producedBy": "agt_9e8d7c6b5a493827", "handoffSummary": null, "options": [ { "id": "approve", "label": "Approve", "meaning": "approve", "needsComment": false }, { "id": "request_changes", "label": "Request changes", "meaning": "revise", "needsComment": true }, { "id": "reject", "label": "Reject", "meaning": "reject", "needsComment": false } ], "ts": "2026-11-10T09:30:00.000Z"}Refused
Section titled “Refused”Each of these must fail. The corpus says why:
- boardName is never absent
- an option’s meaning is one of five
- a variant gate carries no top-level id
- ts is a date-time
- a subject says whether its preview is complete
- the event names itself
Source
Section titled “Source”- Schema:
schemas/superpipeline/gate.pending.v1.json - In the package:
import schema from "@superjackfruitlabs/treaty-event-schemas/superpipeline/gate.pending.v1.json" with { type: "json" };