HUX-11 contract freeze: identity/capabilities/manifest/error records, event turn and idempotency and delegation/side-effect kinds, memory no-store/supersedes/ retrieval removal, revisions for optimistic concurrency, artifact access, budget scope/spend/subagents and external side-effect gating, notebook notes and dedupe keys. ADR-0001 records the wire and compatibility rules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNPhwu2bsaRNg3DETSAZoM
4.5 KiB
4.5 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.
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.