3.5 KiB
Human Gates producer and notification contract
Human Gates is an account-bound release-candidate inbox. The canonical mobile route is #/my-work/human-gates. Reads may use the last account-scoped browser cache, but Release/Hold decisions require a live authenticated identity and an online server round trip.
A cold open of the canonical route confirms the account and opens Human Gates from the core browser runtime while optional Today and Planning bundles continue hydrating or recovering. The full workspace adopts that controller and fixed review snapshot without a second queue request or duplicate decision handlers.
Producer intake
Authenticated producers submit POST /api/v1/human-gates/intake with a unique Idempotency-Key header and JSON such as:
{
"source": "release-bot",
"project": "stackchain/dashboard",
"candidate_hash": "abc123",
"title": "Dashboard candidate",
"priority": 7,
"artifacts": [{"name": "manifest", "url": "https://forge.example/artifacts/manifest.json"}],
"links": [{"label": "change", "url": "https://forge.example/pulls/1415"}],
"checks": [{"name": "browser", "state": "success", "required": true}],
"score": {"value": 92, "provenance": "release-evaluator/v2"},
"provenance": {"producer": "release-bot", "run_id": "run-9"}
}
The immutable identity is authenticated account + source + project + candidate_hash. Retrying the same key and body returns the same gate. Reusing a key for different facts, or redefining an existing candidate hash, returns 409. A newer hash from the same source/project atomically marks older pending candidates superseded; detail history remains available for audit. Configure durable storage with STACKCHAIN_HUMAN_GATE_DB (default: $STACKCHAIN_STATE_DIR/human-gates.sqlite3).
Consumers list GET /api/v1/human-gates, inspect GET /api/v1/human-gates/{id}, and submit POST /api/v1/human-gates/{id}/decision with a new Idempotency-Key, expected_revision, and either release or hold. Hold requires a reason. Release requires all three checklist confirmations; if any required check is not successful it also requires an explicit override reason. Durable receipts are available at GET /api/v1/human-gate-receipts/{receipt_id}. All endpoints are authenticated, account-bound, and Cache-Control: no-store.
Privacy-safe notification contract
Web Push is opt-in per authenticated device under My Work → Settings → Notify me when release decisions are waiting. The preference is stored with that device's push subscription; revoked sessions are removed before delivery. The poller honors the device's routine-alert quiet hours, coalesces unchanged pending counts, and routes a notification tap to #/my-work/human-gates.
Web Push, Telegram, and other lock-screen adapters MUST expose count and route only:
{
"pending_count": 3,
"route": "#/my-work/human-gates"
}
The notification text may say “3 Human Gates pending” and provide the route. It MUST NOT include project names, candidate hash values, titles, artifact/link URLs, checks, scores, provenance, decision history, superseded candidate facts, reasons, or receipts. Multiple intake/supersession events before delivery replace the pending notification with the latest count rather than emitting one message per candidate. A transition to zero clears the outstanding notification; it does not send candidate detail. Producers and adapters must fetch current state after authenticated route open rather than treating notification delivery as an action authorization.