# 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:

```json
{
  "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**:

```json
{
  "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.
