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
5.8 KiB
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 andtrust: relay; the Worker usestrust: worker. Requests without a complete, well-formed identity are401 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 is409 conflictwithhux.error.v1. Creates acceptIdempotency-Key; a repeat returns the original record with200instead of a duplicate. - Lists return
{"items": [...], "next": <cursor|null>}. Event pages useafter_seq; the SSE stream setsid:toseqsoLast-Event-IDresumes exactly. - Errors are always
hux.error.v1. A disabled card answers404 flag_offso an old client and a flag-off server look identical. - Every read and mutation writes a
common.audit_outcome.
Compatibility rules
- 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. - Readers ignore unknown fields and unknown enum values they do not need;
a reader that needs a new enum value must check
contract_version. - On-disk data is append-only per family;
data_layout_versionbumps only for additive layout changes, andmin_reader_contract_versionsays the oldest service that can still read it. Rolling the service back to any version ≥min_reader_contract_versionleaves data readable. - 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).0revision of the schemas and fixtures here, never as an informal edit.