Governed actions
Approvals, idempotency, receipts, and change feeds for writes.
Governed actions are writes that carry their own rulebook: who approved, what was required, what happened, and the receipt proving it. They back the destination and ontology writes operators approve every day.
Action contracts
| Field | Meaning |
|---|---|
| Name + description | What the action does, for approvers |
| Input / output schema | Typed shapes in and out — no surprises |
| Required permissions | Data write, delete, or maintain — at least one |
| Approval policy | None, or required with roles, minimum approvals, self-approval rules |
| Preconditions | Input paths that must exist before running (up to 100) |
| Target | Which endpoint receives the write |
{
"contractVersion": 1,
"name": "warehouse-forecast-append",
"requiredPermissions": ["data:write"],
"approvalPolicy": { "mode": "required", "roles": ["owner", "admin"], "minimumApprovals": 1, "selfApproval": false },
"target": { "kind": "source_write", "dataEndpointId": "…" }
}Request and approval
| Field | Meaning |
|---|---|
| Input | The write payload itself |
| Idempotency key | 8–200 characters, caller-chosen — retries with the same key never double-apply |
| Decision | Approved or rejected, with a reason up to 2000 characters |
Example: approve with reason "Q3 forecast refresh, reviewed evidence." Example 2: reject with reason "wrong horizon — resubmit for Q4."
Safety semantics
| Guarantee | Meaning |
|---|---|
| Idempotency required, caller-action scope | Same key + same action = one effect; conflicts rejected, never merged |
| Concurrency reject | Simultaneous same-key attempts rejected on up to 20 key paths |
| Uncertain outcome | Reconcile before retry — never blindly rerun an unknown |
| No exactly-once claim | The contract is honest: receipts, not magic |
Receipts
Every execution leaves a receipt — the proof of what happened:
| Field | Meaning |
|---|---|
| Status | Succeeded, failed, or uncertain |
| Ids | Request, action, version, and operation identifiers |
| Request hash | Tamper-evident fingerprint of what was asked |
| Result | The write outcome, or null when there is none |
| Recovery | Whether recovery is required, with instructions |
{
"contractVersion": 1,
"status": "succeeded",
"operationId": "…",
"requestHash": "…64 hex chars…",
"result": { "rowsWritten": 20 },
"recovery": { "required": false, "instruction": "" }
}An uncertain receipt is a pause, not a failure: reconcile the target state first, then decide — the receipt tells you how.
Image: receipt detail with status, hashes, result, and recovery instruction.
Change feed
Downstream consumers follow action outcomes without polling blindly:
| Operation | Meaning |
|---|---|
| Pull | Named consumer, page size up to 100, optional sequence cursor and topic filter (assertion revisions, identity corrections, completed queries, committed actions) |
| Acknowledge | Mark event ids consumed, up to 100 at a time |
Example: a review dashboard pulling the last 25 committed actions. Example 2: a sync job filtering identity corrections since its cursor.
Choosing well
- Require approvals with named roles and no self-approval where effects leave the platform.
- Generate idempotency keys from the business operation, not random — retries then mean something.
- Treat uncertain receipts as investigation starts, and reconcile before anything reruns.