2.9 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.
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.
Telegram coalescing contract
Telegram or lock-screen adapters MUST coalesce pending changes per authenticated account and 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.