atlas-iac/docs/hermes_hux_contracts.md
jenkins b4145861d2 hermes(hux): freeze hux.v1 contract 1.0.0 and move reference modules into the foundation package
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
2026-08-24 00:48:20 -03:00

9.7 KiB

Hermes chat UX program (HUX): shared contracts

This is the contract-only foundation for the twelve-card program that makes chat.bstein.dev a first-class assistant surface. Nothing here changes runtime behaviour. It fixes the shapes, state machines and ownership that every later slice codes against, so the UI side and the backend side can work in parallel without inventing informal shapes.

Schemas live in services/hermes/contracts/hux/ as JSON Schema 2020-12 with one worked example per record under examples/. The rules a schema cannot express (state machines, capability matrix, mode catalog, privacy defaults, suggestion gating, flag dependencies) live in dockerfiles/hermes-hux-foundation/hux/rules.py; hux/contracts.py validates records without external packages. testing/tests/test_hermes_hux_contract_schemas.py keeps schemas, examples, rules and the live Switchyard catalog in agreement. The contract is frozen at 1.0.0 (see docs/hux/ADR-0001-hux-v1-contract-freeze.md for identity headers, If-Match, idempotency and compatibility rules).

Where the backend lives

The chat product has exactly one repo-owned HTTP surface on the request path: the Go tenant router (services/hermes/router/). It already owns the Keycloak identity to slot mapping, the admin-surface deny list, durable state on its PVC, and the JS/CSS bridge it injects into every WebUI page. New HUX APIs are therefore router routes under /hux/v1/, storing per-tenant records on the tenant PVC beside the existing Telegram media store. The upstream WebUI is a pinned image patched at build time; UI work goes through the injected bridge and, where unavoidable, the fail-closed patch scripts in dockerfiles/.

/hux/v1 returns 404 until hux.foundation is enabled. Every other flag depends on it (see flags.json), and the router refuses to serve a flag whose dependencies are off.

Identity and provenance rules

  • Users are usr_<hash>: the hashed Keycloak subject the router already uses. Raw subjects, emails and Telegram ids never appear in a HUX record.
  • Every record carries provenance (surface, actor, time, session/run, route, build). route.requested is a friendly mode or a route id; a vendor model name never appears in a contract. build pins the commit and image digest so the timeline can say which release produced a decision.
  • Evidence is referenced, never inlined (evidence_ref). The UI expands a reference through the API that owns it, which is how redaction stays in one place.
  • sensitivity and redaction are mandatory on events and memory. Records leaving a tenant are redacted according to redaction.level first.

API surface (contract only)

All routes are tenant-scoped through the router; the caller never names the tenant. Responses are the records below, wrapped as {"items": [...], "next": cursor} for lists.

Area Routes Record
Foundation (HUX-11) GET /hux/v1/capabilities, GET /hux/v1/manifest hux.capabilities.v1, hux.manifest.v1, errors as hux.error.v1
Events (HUX-01) GET /hux/v1/conversations/{id}/events?after_seq=N (JSON), GET .../events/stream (SSE, id: = seq) hux.event.v1
Memory (HUX-02) GET/POST /hux/v1/memory, POST /hux/v1/memory/{id}/{approve,reject,forget}, GET /hux/v1/memory/export hux.memory.v1
Projects (HUX-03) GET/POST/PATCH /hux/v1/projects, GET/PATCH /hux/v1/conversations, POST /hux/v1/conversations/{id}/branch, GET /hux/v1/search?q= hux.project.v1, hux.conversation.v1
Artifacts (HUX-04) GET/POST /hux/v1/artifacts, POST /hux/v1/artifacts/{id}/versions, GET .../versions/{n}/diff?from=, POST .../promote hux.artifact.v1
Autonomy (HUX-05) GET/PUT /hux/v1/policy?scope=, GET /hux/v1/approvals, POST /hux/v1/approvals/{id} (`once session
Modes (HUX-06) GET /hux/v1/modes, PUT /hux/v1/conversations/{id}/mode hux.mode.v1
Research (HUX-08) GET /hux/v1/messages/{id}/citations, GET /hux/v1/sources/{id}, GET/PATCH /hux/v1/notebooks/{id} hux.source.v1, hux.passage.v1, hux.citation.v1, hux.research_notebook.v1
Onboarding (HUX-09) GET /hux/v1/suggestions?context=, POST /hux/v1/suggestions/{id}/{dismiss,never,acted} hux.suggestion.v1, hux.suggestion_state.v1
Privacy (HUX-10) GET /hux/v1/privacy/policy, POST /hux/v1/conversations/{id}/forget hux.privacy_policy.v1, hux.privacy_notice.v1, hux.retention_audit.v1
Release (HUX-12) GET /hux/v1/releases (operator surface, worker only) hux.release.v1

Multimodal (HUX-07) reuses artifacts for images/audio (type image/audio, lineage for variants) and events for transcript corrections (kind: artifact.version on the transcript artifact). It has no schema of its own by design.

State machines and rules that are fixed here

  • Memory is a ledger: proposed -> active|rejected, active -> expired|forgotten, expired -> forgotten. Nothing edits content in place; a correction is a new entry whose source points at the old one. Sensitive entries need approval_mode: ask and must expire or decay; restricted content and the credentials/minors/location/biometric topics are never written. Forgotten entries keep their audit trail and drop their content.
  • Autonomy has three levels. Read-only capabilities are always allowed, mutating ones ask unless the level is autonomous, network is denied in safe, and deploy always asks whatever the level or grant says. Explicit grants override the matrix; an unexpired deny beats everything. See default_capability_matrix() and effective_decision().
  • Approvals move once from pending and are then terminal. Choices are the gateway's existing once|session|always|deny.
  • Cancellation produces a receipt with an outcome and the side effects observed, each marked reverted or not. "Stop" is not done until the receipt exists.
  • Modes are intents mapped to Switchyard constraints, never to a vendor. Fast, Thoughtful, Research and Create all allow both hosted providers; Private is local-only, memory off, web off, ephemeral, and cannot be overridden to a hosted route. The advanced drawer may pin an exact atlas/manual/... route id; the test suite checks each default route exists in switchyard-configmap.yaml.
  • Privacy scopes sensitive topics to the conversation, never shares them across surfaces, decays them within 30 days, and shows a just-in-time notice with real controls (forget_this_conversation, switch_to_private, disable_memory_here, dismiss). A daily retention audit record proves the rules ran.
  • Suggestions need a trigger context, are always dismissable, honour max_shows (at most 5) and a cooldown of at least an hour, and stop for good on never_again, dismissal or action.
  • Release is linear: reviewed -> merged -> built -> verified -> deployed -> converged -> live_verified, with rolled_back reachable from any state that has an image. Each step names its evidence: merge commit, CI build plus image digest, Harbor digest equal to the image digest, Flux revision, running pod digest equal to the image digest, passing health probe. release_transition_problems() is the single source of truth the Worker's task state must call before it claims anything is deployed.

Cards, owners, waves, flags

flags.json is the registry. All twelve flags default off and are read from the HUX_FLAGS comma list; a flag counts only when its dependency chain is on.

Card Flag Wave Backend (Claude) Frontend (Codex)
HUX-11 Shared foundation hux.foundation A /hux/v1 router scaffold, provenance, redaction Bridge integration in Chat, Worker, Telegram, voice
HUX-01 Activity timeline hux.activity_timeline A Event store and stream Timeline UI, decision cards, evidence expansion
HUX-02 Memory control hux.memory_control A Ledger, provenance, TTL, approval, migration Memory page, per-message controls, export
HUX-05 Autonomy controls hux.autonomy A Policy engine, grants, budgets, approvals, receipts Ask First/Safe/Autonomous, permission matrix, stop UX
HUX-10 Privacy behaviour hux.privacy A Threat model, topic policy, scoping, audit Notices, controls, regression tests
HUX-12 Deployment follow-through hux.release_followthrough A Release state machine in the Worker Exact-SHA CI, Flux convergence, live evidence, rollback UI
HUX-03 Projects/chats hux.projects B Data model, indexes, branch lineage Navigation, search, branch graph, migration UI
HUX-04 Artifact workspace hux.artifacts B Typed versions, diffs, lineage, promotion Preview/edit/diff/share workspace
HUX-06 Friendly modes hux.friendly_modes B Mode catalog and Switchyard mapping Chooser and advanced route drawer
HUX-08 Research/citations hux.research B Sources, passages, citations, notebook Citation strip, source drawer, integrity E2E
HUX-07 Multimodal hux.multimodal C Attachment lineage, transcript correction, permissions Drag/drop, previews, annotation, consent
HUX-09 Onboarding hux.onboarding C Suggestion policy and suppression Dismissible UI, restraint tests

Versioning

Record schemas carry their version in the schema field (hux.event.v1). Adding optional fields is a compatible change; anything else is a v2 record served beside v1 until every surface has moved. Consumers must ignore unknown optional fields and must not reuse record ids across versions.