atlas-iac/docs/hux/ADR-0001-hux-v1-contract-freeze.md
jenkins 6964a9d8c8 hermes(hux): close Wave A review findings in autonomy and the HTTP pipeline
F1 policy writes and allow grants are human-surface only; F2 worker trust is
confined to the hook allowlist and unexpected exceptions become audited 500
error records; F4 external side effects release only for the same run and
argument hash; F6 the gate honours budget exhaustion; F8 only the gateway
can vouch for an empty process registry and failed receipts can be
superseded; F11/F12 receipt revision and unshipped card routes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNPhwu2bsaRNg3DETSAZoM
2026-08-24 00:48:20 -03:00

92 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-0001: freeze the hux.v1 contract at 1.0.0
Status: accepted, 2026-08-24. Owners: Claude (backend), Codex (integration).
## Decision
`services/hermes/contracts/hux/` is frozen as contract version `1.0.0`
(`VERSION`, `flags.json.contract_version`). It is the only shape any HUX
surface (Chat, Worker, Telegram, voice) codes against. The Python reference
implementation lives in `dockerfiles/hermes-hux-foundation/hux/`:
`contracts.py` validates records, `rules.py` holds the governance rules the
schemas cannot express.
Records carry their own version in `schema` (`hux.event.v1`). Wire fixtures
under `services/hermes/contracts/hux/examples/` are validated by
`testing/tests/test_hermes_hux_contract_schemas.py` and are the exchange
format with Codex: a change to a fixture is a change to the contract.
## What 1.0.0 covers
| Requirement | Where |
|---|---|
| Tenant and surface identity | `common.identity` (tenant_slot, hashed subject, surface, trust: router/relay/worker) — carried on every stored record that a caller can create |
| Capability negotiation, default-off flags | `identity.capabilities` (`GET /hux/v1/capabilities`), `flags.json`; a card is enabled only when its dependency chain is enabled (`rules.flag_enabled`) |
| Redacted activity events | `event`: `seq` (monotonic per conversation), `turn`, `correlation_id`, `idempotency_key`, replay by `after_seq`, kinds for decisions, delegation, failure/completion, cancellation, budget and side-effect gates; `detail` is allowlisted per kind and scrubbed |
| Memory | `memory`: provenance, `source`, `reason`, `sensitivity`, `topic`, `ttl`, `approval_mode` incl. `no_store`, `status` incl. `no_store`, edit via `supersedes`, delete via `forgotten`, retrieval removal via `retrievable=false`, revisioned |
| Projects, lineage, artifacts, versions, diffs, authorization | `project`, `conversation.branch`, `artifact.versions[]` immutable with `content_ref` hash, `diff_from`, `lineage`, `promotion`, `access`, `revision` |
| Autonomy | `permission.policy` (levels, grants, budgets: tokens/tool calls/wall clock/spend/subagents/delegations/scope), `approval` with `request.external`, `cancellation_receipt`, `budget_state` |
| Research | `citation.source/passage/citation/notebook` with classification, support verdict, notes, assumptions, unresolved questions, `dedupe_key` |
| Additive migrations, rollback readers | `identity.manifest` (`data_layout_version`, `min_reader_contract_version`); rules in "Compatibility" below |
## Wire conventions
- Identity is asserted by the trusted hop, never by the client body: the
chat router sets `X-Hermes-Tenant-Identity: slot-N`, `X-Hux-Subject:
usr_<hash>`, `X-Hux-Surface: chat`; the Telegram relay adds the relay key
and `trust: relay`; the Worker uses `trust: worker`. Requests without a
complete, well-formed identity are `401 unauthorized`. The service must
only be reachable from those hops (NetworkPolicy is Codex's to enforce).
- Mutations of revisioned records send `If-Match: <revision>`; a mismatch is
`409 conflict` with `hux.error.v1`. Creates accept `Idempotency-Key`; a
repeat returns the original record with `200` instead of a duplicate.
- Lists return `{"items": [...], "next": <cursor|null>}`. Event pages use
`after_seq`; the SSE stream sets `id:` to `seq` so `Last-Event-ID`
resumes exactly.
- Errors are always `hux.error.v1`. A disabled card answers `404 flag_off`
so an old client and a flag-off server look identical.
- Every read and mutation writes a `common.audit_outcome`.
## Compatibility rules
1. Within `1.x`: only add optional fields, enum values or new record kinds.
Never rename, remove, change a type, or make an optional field required.
2. Readers ignore unknown fields and unknown enum values they do not need;
a reader that needs a new enum value must check `contract_version`.
3. On-disk data is append-only per family; `data_layout_version` bumps only
for additive layout changes, and `min_reader_contract_version` says the
oldest service that can still read it. Rolling the service back to any
version ≥ `min_reader_contract_version` leaves data readable.
4. Anything outside 1–3 is `hux.v2`, served beside v1 until every surface
has moved.
## Revision 1.1.0 (2026-08-24, additive)
Raised after the three Wave A lanes reported. Adds `receipt` to
`evidence_ref.kind`, `unprocessable` (422) to error codes, optional
`revision` on source/passage/citation, optional `audit_stale` on the privacy
policy, and per-route body caps (artifact uploads up to 25 MiB content). The
argument hash a `once` approval is released against travels as
`request.evidence[] = {kind: "tool_call", id, hash}`; the agent hook must
send it or the gate can never release. Memory rules skip content-free
statuses (`no_store`, `forgotten`, `rejected`).
Review follow-up (same revision, still additive): `cancellation_receipt`
gains optional `revision`. Stored documents in every revisioned family carry
the store's `revision`, and a `failed_to_cancel` receipt is the one
non-terminal outcome: a later stop that the gateway (worker trust) confirms
supersedes it with a revision bump; an identical repeat replays. Only a
human actor (router/relay trust on chat, telegram or voice) may write a
policy or hold an `allow` grant; `trust: worker` reaches only the routes in
`hux.flags.WORKER_ROUTES` (SO-08). External side effects release only
against an approval for the same run and argument hash. Cards HUX-06,
HUX-09 and HUX-12 declare no routes until they ship.
## Consequences
- Codex codes UI against the fixtures, not against the running service.
- Claude's implementation cards (HUX-01/02/03/04/05/08/10/11) may add
optional fields but must ship the fixture change in the same commit.
- If Codex needs a shared-interface change, it lands as a `1.(x+1).0`
revision of the schemas and fixtures here, never as an informal edit.