# Iris Domain — Technical Specifications

> Technical specification for the Iris AI Assistant platform. Documents the
> shared type system, the runnable API surface, the conversation/memory/agent
> library clusters, persistence, events, configuration, and integration
> contracts as they exist in `libs/iris/**` and `apps/iris/**`.
>
> **Grounding rule:** every entity, field, enum value, endpoint, and
> configuration key below is traceable to source. Items not yet implemented are
> labelled `(planned)`. Where the prior draft asserted detail that does not
> exist in code, it has been removed.

This document is the authoritative source for what is actually implemented in
the Iris codebase, as opposed to what is planned. It was written directly
against the source files in `libs/iris/types/src/`,
`libs/iris/conversation-core/src/`, `libs/iris/conversation-orchestration/src/`,
`libs/iris/agents/src/`, `libs/iris/core/src/config/`,
`libs/iris/database/prisma/schema.prisma`, `apps/iris/api/src/`,
`libs/shared/event-bus/src/`, and the `libs/iris/integrations/` packages.
Package counts come from `find libs/iris -name package.json` (258) and
`find apps/iris -name package.json` (11).

Several things asserted in earlier drafts were removed because they could not be
traced to code: Kafka topics, the `iris_*` PostgreSQL tables, Qdrant
collections, Redis cache-key tables, the WebSocket event catalog,
Free/Pro/Enterprise plan-limit tiers, the model-routing decision matrix, and
several environment variables. Every section that describes target-state
infrastructure that is not yet wired is explicitly labelled as planned.

---

## Table of Contents

1. [Scope and Implementation Status](#1-scope-and-implementation-status)
2. [Shared Type System (`@iris/types`)](#2-shared-type-system-iristypes)
3. [Conversation Domain Objects](#3-conversation-domain-objects)
4. [Memory Domain Objects](#4-memory-domain-objects)
5. [Agent, Task, and Action Domain Objects](#5-agent-task-and-action-domain-objects)
6. [Model and Provider Domain Objects](#6-model-and-provider-domain-objects)
7. [Tool Domain Objects](#7-tool-domain-objects)
8. [User, Session, and Preference Objects](#8-user-session-and-preference-objects)
9. [Event Type System](#9-event-type-system)
10. [Conversation Engine (`@iris/conversation-core`)](#10-conversation-engine-irisconversation-core)
11. [HTTP / GraphQL API Surface (`@iris/api`)](#11-http--graphql-api-surface-irisapi)
12. [Persistence](#12-persistence)
13. [Configuration (`@iris/config`, `@iris/core`)](#13-configuration-irisconfig-iriscore)
14. [Library Clusters](#14-library-clusters)
15. [Cross-Domain Integration Libraries](#15-cross-domain-integration-libraries)
16. [Rollback-Safe In-Match Commentary Contract (V2 Consumer)](#16-rollback-safe-in-match-commentary-contract-v2-consumer)
17. [V2 Accessibility Bridge Contract (V2 Consumer)](#17-v2-accessibility-bridge-contract-v2-consumer)
18. [V2 Mobile Companion App (V2 Consumer)](#18-v2-mobile-companion-app-v2-consumer)
19. [Acceptance Criteria](#19-acceptance-criteria)

---

## 1. Scope and Implementation Status

Iris is **partially implemented**. The codebase is a large package tree:

- `libs/iris/**` — **258 `package.json` files** (Nx projects), organised into
  functional clusters (conversation, memory, knowledge, agents, code, privacy,
  platform, multimodal, emotional, accessibility, analytics, integrations, and
  foundation libraries).
- `apps/iris/**` — **11 application `package.json` files**: `@iris/api`,
  `@iris/dashboard`, `@iris/desktop`, `@iris/developer-portal`,
  `@iris/marketplace`, `@iris/mobile`, `@iris/wearable-shared`,
  `@iris/browser-extension`, `@iris/pwa`, `@iris/widget`, `@iris/xr-shared`.
- There is **no `services/iris/`** directory.

The most fully built-out, verifiable pieces — and the focus of the concrete
sections of this document — are:

- `@iris/types` — the shared domain type system (8 source modules with tests).
- `@iris/api` — a runnable Hono REST + GraphQL service with conversation,
  memory, agent, and knowledge routes.
- `@iris/conversation-core` — the dialogue engine: conversation manager, turn
  manager, context window, dialogue state machine.
- `@iris/conversation-orchestration` — multi-model orchestration plus the V2
  match-commentary contract.
- `@iris/agents` — a mobile-safe built-in agent catalog.
- `@iris/core` / `@iris/config` — foundation utilities and configuration.
- `libs/iris/database` — a Prisma schema (3 models).

Many cluster leaf packages exist as Nx projects with their own `src/`; this
document describes the ones whose contracts are load-bearing and verifiable. It
does **not** claim production deployment, Kubernetes topology, or external
infrastructure that is not present in the repository.

---

## 2. Shared Type System (`@iris/types`)

`libs/iris/types/src/` is the canonical domain vocabulary. All other Iris
packages import their core types from here rather than defining them locally,
which ensures that a `ConversationId` in the conversation engine is the same
type as a `ConversationId` in the memory system. There are eight source modules,
each with a co-located `*.test.ts`:

| Module            | Defines                                                  |
| ----------------- | -------------------------------------------------------- |
| `conversation.ts` | Conversations, messages, turns, threads, content blocks  |
| `memory.ts`       | The four-tier memory model                               |
| `agent.ts`        | Agents, tasks, actions, executions, plans, multi-agent   |
| `model.ts`        | Providers, models, requests, responses, streaming, costs |
| `tool.ts`         | Tool definitions, calls, executions, registry interfaces |
| `user.ts`         | Users, profiles, preferences, sessions, auth, quotas     |
| `events.ts`       | Domain events, event bus / event store interfaces        |
| `index.ts`        | Re-exports                                               |

Two conventions are used throughout the type system:

- **Branded ID types** — every entity has its own opaque ID type (e.g.
  `ConversationId`, `MessageId`, `AgentId`) defined as
  `string & { readonly [Brand]: ... }`. This prevents accidentally passing a
  `UserId` where a `ConversationId` is expected. A `create*Id()` factory
  produces IDs in the form `${prefix}_${base36-timestamp}-${random}` (e.g.
  `conv_…`, `msg_…`, `agt_…`). The full list: `ConversationId`, `MessageId`,
  `ThreadId`, `TurnId`, `MemoryId`, `MemoryBlockId`, `MemoryChunkId`, `AgentId`,
  `TaskId`, `ActionId`, `ExecutionId`, `ToolId`, `ToolCallId`,
  `ToolExecutionId`, `UserId`, `SessionId`, `ProfileId`, `EventId`,
  `CorrelationId`.
- **Zod schemas** — most string-union types ship a paired `…Schema`
  (`z.enum([...])`) for runtime validation, so the same enum definition works
  for both TypeScript type checking and API request validation.

---

## 3. Conversation Domain Objects

The conversation module defines the full lifecycle of a dialogue: from the
top-level `Conversation` container through `Thread` (a branch of the
conversation) down to individual `Message` objects and the `ContentBlock[]`
array that carries the actual content. Every field is `readonly` on the core
types, enforcing immutable records at the type level.

### 3.1 Conversation

`Conversation` (all fields `readonly`):

| Field            | Type                 | Meaning                                  |
| ---------------- | -------------------- | ---------------------------------------- |
| `id`             | `ConversationId`     | Branded conversation ID                  |
| `title`          | `string?`            | Optional title                           |
| `description`    | `string?`            | Optional description                     |
| `ownerId`        | `string`             | Owning user                              |
| `status`         | `ConversationStatus` | Lifecycle status                         |
| `config`         | `ConversationConfig` | Model, mode, sampling, tooling config    |
| `participants`   | `ParticipantState[]` | Participants with per-participant counts |
| `threads`        | `Thread[]`           | Branch threads                           |
| `activeThreadId` | `ThreadId?`          | Currently active thread                  |
| `stats`          | `ConversationStats`  | Aggregate counters                       |
| `createdAt`      | `Date`               | Creation time                            |
| `updatedAt`      | `Date`               | Last update                              |
| `lastMessageAt`  | `Date?`              | Time of last message                     |
| `metadata`       | `Record<string,…>?`  | Arbitrary metadata                       |

Related inputs: `ConversationInput` (create), `ConversationUpdate` (patch).

**`ConversationConfig`** — `mode` (`ConversationMode`), `model` (string),
`provider?`, `systemPrompt?`, `temperature?`, `maxTokens?`, `topP?`, `topK?`,
`stopSequences?`, `tools?` (string IDs), `contextWindow?`,
`summarizationEnabled?`, `streamingEnabled?`.

**`ConversationStats`** — `messageCount`, `turnCount`, `threadCount`,
`participantCount`, `totalTokens`, `inputTokens`, `outputTokens`,
`averageResponseMs?`, `topics?`.

**`ConversationContext`** — assembled context for a model call: `systemPrompt?`,
`messages`, `tools?`, `summary?`, `contextWindow`, `totalTokens`, `truncated`.

### 3.2 Message

`Message`: `id` (`MessageId`), `conversationId`, `threadId?`,
`parentMessageId?`, `role` (`ParticipantRole`), `participantId`, `content`
(`ContentBlock[]`), `status` (`MessageStatus`), `createdAt`, `updatedAt?`,
`metadata?` (`MessageMetadata`).

`MessageMetadata`: `model?`, `provider?`, `stopReason?` (`FinishReason`),
`tokenUsage?` (`TokenUsage`), `latencyMs?`, `cached?`, plus open keys.

`TokenUsage` (conversation module): `inputTokens`, `outputTokens`,
`totalTokens`, `cacheReadTokens?`, `cacheWriteTokens?`.

`MessageDelta` carries streaming updates with `type` of `content_block_start`,
`content_block_delta`, `content_block_stop`, or `message_delta`.

### 3.3 Content Blocks

`ContentType` union: `text`, `image`, `audio`, `video`, `file`, `code`,
`tool_use`, `tool_result`. `ContentBlock` is the discriminated union of
`TextContent`, `ImageContent`, `AudioContent`, `VideoContent`, `FileContent`,
`CodeContent`, `ToolUseContent`, `ToolResultContent`. `TextContent` carries
optional `TextAnnotation[]` (`bold | italic | code | link | mention | highlight`
with `startIndex`/`endIndex`). Type guards: `isTextContent`, `isImageContent`,
`isToolUseContent`, `isToolResultContent`; helpers `extractText`, `textContent`,
`normalizeContent`.

### 3.4 Turn and Thread

`Turn`: `id` (`TurnId`), `conversationId`, `index`, `userMessage`,
`assistantMessage?`, `toolUses?` (`ToolExchange[]`), `startedAt`,
`completedAt?`, `durationMs?`, `tokenUsage?`, `metadata?`. `ToolExchange` pairs
a `ToolUseContent` with a `ToolResultContent`. `TurnSummary` is a compressed
turn record (`userIntent`, `assistantAction`, `outcome?`, `topics?`,
`tokenCount`).

`Thread`: `id` (`ThreadId`), `conversationId`, `parentThreadId?`,
`branchFromMessageId?`, `name?`, `description?`, `status` (`ThreadStatus`),
`messageCount`, `createdAt`, `updatedAt`, `resolvedAt?`, `metadata?`.

### 3.5 Conversation Enums

The following enums define the status vocabulary for conversation, message, and
thread lifecycle management. They are the values that appear in status fields
throughout the REST API, GraphQL schema, and event payloads.

| Enum                 | Values                                                                       |
| -------------------- | ---------------------------------------------------------------------------- |
| `ParticipantRole`    | `user`, `assistant`, `system`, `tool`, `function`                            |
| `ContentType`        | `text`, `image`, `audio`, `video`, `file`, `code`, `tool_use`, `tool_result` |
| `MessageStatus`      | `pending`, `streaming`, `completed`, `failed`, `cancelled`, `filtered`       |
| `FinishReason`       | `stop`, `length`, `tool_use`, `content_filter`, `error`, `cancelled`         |
| `ThreadStatus`       | `active`, `resolved`, `archived`, `deleted`                                  |
| `ConversationStatus` | `active`, `paused`, `completed`, `archived`, `deleted`                       |
| `ConversationMode`   | `chat`, `completion`, `assistant`, `agent`                                   |

### 3.6 Conversation Operations and Events

Operation request/response types: `SendMessageRequest`/`SendMessageResponse`,
`RegenerateMessageRequest`, `BranchConversationRequest`.

`ConversationEventType` (the type-module union): `conversation.created`,
`conversation.updated`, `conversation.deleted`, `message.created`,
`message.updated`, `message.deleted`, `message.streaming`, `turn.started`,
`turn.completed`, `thread.created`, `thread.resolved`, `participant.joined`,
`participant.left`. Event objects: `ConversationEvent`, `MessageEvent`,
`TurnEvent`, `ThreadEvent`, union `AnyConversationEvent`.

---

## 4. Memory Domain Objects

`memory.ts` documents itself as a **MemGPT-inspired tiered memory model**. The
type module is the authoritative definition of the four-tier hierarchy; it is
referenced by all 22 packages in `libs/iris/memory/` and by the `/api/v1/memory`
routes in `@iris/api`.

### 4.1 Memory Tiers and Classification

`MemoryTier` defines **four tiers**: `core`, `working`, `archival`, `episodic`.
Each tier has a distinct timescale and entry type, described in the subsections
below.

> The four-tier model is authoritative. A `semantic` tier and a `long_term` tier
> (named in an earlier draft) are **not** in the enum. "Semantic" retrieval is a
> property of archival memory (vector embeddings), not a separate tier; episodic
> memory covers long-term event history.

`MemoryType` — 13 values: `persona`, `goal`, `preference`, `fact`, `event`,
`conversation`, `entity`, `relationship`, `skill`, `instruction`, `context`,
`summary`, `reflection`.

`MemoryImportance` — `critical`, `high`, `medium`, `low`, `trivial`, with
`MEMORY_IMPORTANCE_VALUES` mapping them to `100 / 75 / 50 / 25 / 10`.

`CoreMemorySection` — `persona`, `user`, `system`, `goals`.

### 4.2 Tier Entry Types

**Core memory** — `CoreMemoryBlock` (`id`, `section`, `content`, `maxTokens`,
`currentTokens`, timestamps); `CoreMemoryConfig` groups the persona/user/system/
goals blocks under a `totalTokenBudget`; `CoreMemoryUpdate` operations are
`replace | append | prepend | insert`.

**Working memory** — `WorkingMemoryEntry` (`id`, `type`, `content`, `relevance`,
`recency`, `accessCount`, timestamps, `expiresAt?`). `WorkingMemoryConfig`
(`maxEntries`, `maxTokens`, `decayRate`, `relevanceThreshold`, `ttlMs?`).
`WorkingMemoryState` bundles entries + config.

**Archival memory** — `ArchivalMemoryEntry` (`id`, `type`, `content`,
`summary?`, `importance`, `embedding?` (`number[]`), `embeddingModel?`,
`topics?`, `entities?` (`EntityReference[]`), `source?` (`MemorySource`),
timestamps, `accessCount`). `ArchivalSearchQuery` / `ArchivalSearchResult`
(`matchType`: `semantic | keyword | hybrid`). `ArchivalMemoryConfig`:
`embeddingModel`, `embeddingDimensions`, `indexType` (`flat | hnsw | ivf`),
`similarityMetric` (`cosine | euclidean | dot_product`), `autoSummarize`,
`extractEntities`.

**Episodic memory** — `EpisodicMemoryEntry` (`id`, `eventType`, `timestamp`,
`content`, `participants?`, `location?`, `emotion?` (`EmotionState`),
`outcome?`, `relatedMemories?`, `conversationId?`, `turnId?`).
`EpisodicEventType` — 12 values: `conversation_start`, `conversation_end`,
`user_message`, `assistant_response`, `tool_use`, `error`, `milestone`,
`decision`, `learning`, `preference_change`, `goal_update`, `context_switch`.
`EmotionState`: `valence` (-1..1), `arousal` (0..1), `dominance?`, `label?`.

### 4.3 Generic Memory, Chunks, Operations

`MemoryBlock` is the tier-agnostic record (`tier`, `type`, `content`,
`importance`, `tokenCount`, `embedding?`, `references?`, access counters).
`MemoryChunk` and `MemoryContext` assemble tier content into an LLM context
window under a token budget. `MemoryContextConfig` carries `coreBudgetRatio` /
`workingBudgetRatio` / `archivalBudgetRatio` / `episodicBudgetRatio`.

`MemoryOperation` — `create`, `read`, `update`, `delete`, `search`, `summarize`,
`merge`, `split`, `archive`, `restore`. `MemorySummary` records a summarization
with a `compressionRatio`.

### 4.4 Default Configurations

These constants represent the out-of-box behavior when no explicit configuration
is provided. The `DEFAULT_MEMORY_CONTEXT_CONFIG` budget ratios (core 15%,
working 40%, archival 30%, episodic 15%) determine how the LLM context window is
partitioned between tiers on each turn.

| Constant                         | Notable values                                                                                                         |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `DEFAULT_WORKING_MEMORY_CONFIG`  | `maxEntries 100`, `maxTokens 8000`, `decayRate 0.95`, `relevanceThreshold 0.3`, `ttlMs 3600000`                        |
| `DEFAULT_ARCHIVAL_MEMORY_CONFIG` | `embeddingModel 'text-embedding-3-small'`, `embeddingDimensions 1536`, `indexType 'hnsw'`, `similarityMetric 'cosine'` |
| `DEFAULT_MEMORY_CONTEXT_CONFIG`  | `tokenBudget 32000`; budget ratios `core 0.15 / working 0.4 / archival 0.3 / episodic 0.15`                            |

---

## 5. Agent, Task, and Action Domain Objects

The agent module defines the runtime objects for autonomous task execution. An
`Agent` holds configuration (what capabilities it has, which model it uses, what
tools it can invoke) and a live `AgentState` (what it is currently doing, how
many turns it has taken, any errors). A `Task` represents a unit of work
assigned to an agent, and `Action` represents a single step within that task.
The nesting is: one `Agent` → many `Task`s → each `Task` has many `Action`s.

### 5.1 Agent

`AgentConfig`: `id` (`AgentId`), `name`, `description?`, `role` (`AgentRole`),
`model`, `provider?`, `systemPrompt`, `capabilities` (`AgentCapability[]`),
`tools?`, `maxTurns?`, `maxTokensPerTurn?`, `temperature?`, `topP?`,
`stopSequences?`, `metadata?`. `AgentState` adds runtime status, current task/
action, counters, `errors` (`AgentError[]`), `metrics` (`AgentMetrics`). `Agent`
= `config` + `state` + timestamps.

`DEFAULT_AGENT_CONFIG` — `role: 'assistant'`,
`capabilities: ['conversation','tool_use']`, `maxTurns: 20`, `temperature: 0.7`.

### 5.2 Task and Action

`Task`: `id` (`TaskId`), `name`, `agentId`, `parentTaskId?`, `status`,
`priority`, `input` (`TaskInput`), `output?` (`TaskOutput`), `actions`
(`ActionRecord[]`), `dependencies?`, `blockedBy?`, timestamps, `timeoutMs?`,
`retryCount`, `maxRetries`. `TaskInput` type is
`text | structured | multimodal`; `TaskConstraints` caps tokens/actions/duration
and allows tool allow/forbid lists. `TaskOutput` may carry `TaskArtifact[]`.

`Action`: `id` (`ActionId`), `taskId`, `agentId`, `type` (`ActionType`), `name`,
`input`, `status`, `result?` (`ActionResult`), timing, `retryCount`.
`ActionResult` may include `ActionSideEffect[]` (`file_created`,
`file_modified`, `file_deleted`, `memory_updated`, `state_changed`,
`external_call`, each with a `reversible` flag).

`DEFAULT_TASK_CONSTRAINTS` — `maxTokens: 100000`, `maxActions: 50`,
`maxDurationMs: 300000`.

### 5.3 Execution, Delegation, Planning, Multi-Agent

- **Execution** — `ExecutionContext` (token budget, deadline, variables, tools,
  capabilities), `ExecutionResult`, `ExecutionTrace` / `ExecutionTraceEntry`
  (entry `type`: `thought | action | observation | error | decision`).
- **Delegation** — `DelegationRequest` / `DelegationResult` for agent-to-agent
  task handoff.
- **Planning** — `ExecutionPlan` (`steps`, `dependencies`, `confidence`,
  `alternatives?`), `PlanStep`, `PlanDependency`
  (`requires | enables | blocks`).
- **Multi-agent** — `AgentTeam` (`coordinatorId`, `routingStrategy`:
  `round_robin | capability_based | load_balanced | custom`), `AgentMessage`
  (`request | response | notification | broadcast`).

### 5.4 Agent Enums

The following enums define the vocabulary for agent roles, lifecycle states,
declared capabilities, and task/action status. Note that the runnable
`@iris/api` agent route validates a _different, route-local_ set of these values
(see §11.3) — the API layer is not yet fully aligned to the types defined here.

| Enum              | Values                                                                                                                                         |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `AgentRole`       | `assistant`, `specialist`, `coordinator`, `executor`, `analyst`, `reviewer`, `custom`                                                          |
| `AgentStatus`     | `initializing`, `ready`, `busy`, `waiting`, `paused`, `error`, `terminated`                                                                    |
| `AgentCapability` | `conversation`, `tool_use`, `code_execution`, `file_access`, `web_access`, `memory_access`, `delegation`, `planning`, `reflection`, `learning` |
| `TaskStatus`      | `pending`, `queued`, `running`, `paused`, `completed`, `failed`, `cancelled`, `timeout`                                                        |
| `TaskPriority`    | `critical`, `high`, `normal`, `low`, `background` (`TASK_PRIORITY_VALUES` 100/75/50/25/10)                                                     |
| `ActionType`      | `tool_call`, `message`, `delegate`, `memory_operation`, `code_execution`, `file_operation`, `web_request`, `wait`, `decision`, `custom`        |
| `ActionStatus`    | `pending`, `executing`, `completed`, `failed`, `cancelled`, `skipped`                                                                          |

> Note: the runnable `@iris/api` agent route validates a _different,
> route-local_ set of capability/status enums (see §11.3) — the API layer is not
> yet aligned to `@iris/types`.

### 5.5 Built-In Agent Catalog (`@iris/agents`)

`libs/iris/agents/src/index.ts` exports a static catalog used by V2 companion
surfaces. `IRIS_BUILT_IN_AGENT_IDS` — 9 IDs: `general`, `codeassist`,
`researcher`, `writer`, `analyst`, `support`, `creative`, `operations`,
`supervisor`. Each `IrisBuiltInAgentDescriptor` carries `label`, `specialty`,
`summary`, `surfaces` (`web | mobile | desktop | service | game-companion`),
`capabilities` (`IrisAgentCapability`), `defaultToolCategories`
(`IrisAgentToolCategory`), `maxAutonomousSteps`, `requiresHumanReview`, and
`implementationPackages`. Exported functions: `listIrisBuiltInAgents`,
`getIrisBuiltInAgent`, `selectIrisAgentForIntent` (intent-ordered selection over
`IrisAgentIntent`), `resolveIrisAgentsLaunchReadiness`.

The descriptors delegate to implementation packages — `@iris/agents-core`,
`@iris/archetypes`, `@iris/multi-agent`, `@iris/workflows`, and (for
`supervisor`) `@iris/agent-marketplace`.

---

## 6. Model and Provider Domain Objects

The model module defines the typed layer between Iris and AI providers. It gives
every provider a uniform `ModelDefinition` shape so the model router
(`@iris/model-routing`) can compare providers on price, context window, and
capability without hardcoding provider-specific logic into the routing decision.
The `WELL_KNOWN_MODELS` registry is the authoritative list of supported models
with real pricing and context metadata.

### 6.1 Providers and Models

`ModelProvider` — `anthropic`, `openai`, `google`, `cohere`, `mistral`, `groq`,
`together`, `aws-bedrock`, `azure-openai`, `local`, `custom`. `ProviderStatus` —
`available`, `degraded`, `unavailable`, `unknown`. `ProviderConfig` and
`ProviderHealth` (`latencyMs?`, `quotaRemaining?`).

`ModelDefinition`: `id`, `provider`, `name`, `displayName?`, `tier`
(`ModelTier`), `capabilities` (`ModelCapability[]`), `contextWindow`,
`maxOutputTokens`, `inputPricePerMToken`, `outputPricePerMToken`,
`cachePricePerMToken?`, `embeddingDimensions?`, dates, `isDefault?`.

`ModelTier` — `flagship`, `standard`, `fast`, `economy`, `embedding`.
`ModelCapability` — `chat`, `completion`, `embedding`, `vision`, `audio`,
`video`, `function_calling`, `tool_use`, `streaming`, `json_mode`,
`system_prompt`, `multi_turn`, `caching`.

### 6.2 Model Registry (`WELL_KNOWN_MODELS`)

The registry ships eight models with real pricing and context-window metadata.
`claude-sonnet-4` is the default (`isDefault: true`). `MODEL_ALIASES` maps short
names like `claude`, `gpt4`, and `gemini` to their full model IDs so callers can
use convenient names. The helpers `resolveModelAlias`, `getModelDefinition`,
`modelHasCapability`, `estimateCost`, `findModels`, and `getDefaultModel` are
the primary API for querying this registry.

Eight registered models with real pricing/context metadata:

| Key                      | Provider  | Tier      | Context | Default |
| ------------------------ | --------- | --------- | ------- | ------- |
| `claude-opus-4-5`        | anthropic | flagship  | 200K    | no      |
| `claude-sonnet-4`        | anthropic | standard  | 200K    | **yes** |
| `claude-haiku-3-5`       | anthropic | fast      | 200K    | no      |
| `gpt-4o`                 | openai    | flagship  | 128K    | no      |
| `gpt-4o-mini`            | openai    | fast      | 128K    | no      |
| `gemini-2-0-flash`       | google    | fast      | 1M      | no      |
| `text-embedding-3-small` | openai    | embedding | 8191    | no      |
| `text-embedding-3-large` | openai    | embedding | 8191    | no      |

`MODEL_ALIASES` maps short names (`claude`, `claude-opus`, `gpt4`, `gemini`,
`embed-small`, …) to model IDs. Helpers: `resolveModelAlias`,
`getModelDefinition`, `modelHasCapability`, `estimateCost`, `findModels`,
`getDefaultModel`.

### 6.3 Requests, Responses, Streaming, Cost

`ChatCompletionRequest` (`model`, `messages`, `tools?`, `toolChoice?`,
`responseFormat?`, sampling params, `stream?`), `EmbeddingRequest`,
`ChatCompletionResponse`, `EmbeddingResponse`. `StopReason` — `end_turn`,
`stop_sequence`, `max_tokens`, `tool_use`, `content_filter`, `error`.
`StreamEventType` — `message_start`, `content_block_start`,
`content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`,
`error`. Cost types: `CostEstimate`, `UsageTracking` (per-model and per-provider
breakdowns). Caching: `CacheControl` (`ephemeral | persistent`),
`CachedContentBlock`.

---

## 7. Tool Domain Objects

The tool module defines the contract for everything an agent can invoke. A
`ToolDefinition` declares what a tool does, what inputs it accepts
(JSON-Schema-shaped), what risk level it carries, and what rate limits apply. A
`ToolSpecification` is the trimmed version passed directly to an LLM (`name`,
`description`, `input_schema`), containing only what the model needs to decide
whether to call the tool. The registry and executor interfaces (`IToolRegistry`,
`IToolExecutor`, `IToolValidator`) are the extension points for registering new
tools.

`ToolDefinition`: `id` (`ToolId`), `name`, `displayName?`, `description`,
`category` (`ToolCategory`), `version`, `status` (`ToolStatus`), `riskLevel`
(`ToolRiskLevel`), `inputSchema` (`ToolInputSchema`, JSON-Schema-shaped),
`outputSchema?`, `examples?`, `permissions?`, `rateLimit?` (`ToolRateLimit`),
`timeout?`, `retryConfig?` (`ToolRetryConfig`), `tags?`. `ToolSpecification` is
the trimmed shape passed to an LLM (`name`, `description`, `input_schema`).

| Enum                  | Values                                                                                                            |
| --------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `ToolCategory`        | `code`, `file`, `web`, `data`, `communication`, `search`, `analysis`, `generation`, `memory`, `utility`, `custom` |
| `ToolRiskLevel`       | `safe`, `low`, `medium`, `high`, `critical`                                                                       |
| `ToolStatus`          | `enabled`, `disabled`, `deprecated`, `experimental`                                                               |
| `ToolExecutionStatus` | `pending`, `validating`, `executing`, `completed`, `failed`, `timeout`, `cancelled`, `rate_limited`               |
| `FileOperation`       | `read`, `write`, `append`, `delete`, `list`, `copy`, `move`                                                       |
| `WebOperation`        | `fetch`, `search`, `scrape`, `screenshot`                                                                         |
| `CodeOperation`       | `execute`, `analyze`, `format`, `lint`, `test`                                                                    |

Execution types: `ToolExecutionContext`, `ToolExecutionResult`,
`ToolExecutionError` (`type`:
`validation | execution | timeout | permission | rate_limit | unknown`),
`ToolResult`. Registry/executor interfaces: `IToolRegistry`, `IToolExecutor`,
`IToolValidator`. Defaults: `DEFAULT_TOOL_RETRY_CONFIG` (`maxRetries 3`,
`initialDelayMs 1000`, `maxDelayMs 30000`, `backoffMultiplier 2`),
`DEFAULT_TOOL_RATE_LIMIT` (`maxCallsPerMinute 60`, `maxCallsPerHour 1000`,
`maxConcurrent 10`), `DEFAULT_TOOL_TIMEOUT` = `30000`.

---

## 8. User, Session, and Preference Objects

The user module defines the core identity, authentication, and preference
objects. `User` is the minimal identity record; `UserProfile` adds rich profile
data. `UserPreferences` groups five preference namespaces (`GeneralPreferences`,
`AssistantPreferences`, `NotificationPreferences`, `PrivacyPreferences`,
`AccessibilityPreferences`) under a single object, each with a `DEFAULT_*`
constant and a `createDefaultPreferences(userId)` factory. `IrisJwtClaims`
defines the token payload for JWT-authenticated sessions.

`User`: `id` (`UserId`), `email`, `username?`, `displayName`, name fields,
`avatarUrl?`, `status` (`UserStatus`), `role` (`UserRole`), `accountType`
(`AccountType`), `organizationId?`, `teamIds?`, `permissions?`, timestamps.
`UserProfile` adds bio/location/social/expertise plus `language`, `timezone`,
`locale`.

| Enum              | Values                                                  |
| ----------------- | ------------------------------------------------------- |
| `UserStatus`      | `active`, `inactive`, `pending`, `suspended`, `deleted` |
| `UserRole`        | `user`, `premium`, `admin`, `system`                    |
| `AccountType`     | `individual`, `team`, `enterprise`                      |
| `ThemePreference` | `light`, `dark`, `system`                               |
| `VoiceStyle`      | `natural`, `professional`, `friendly`, `concise`        |
| `ResponseLength`  | `brief`, `moderate`, `detailed`, `comprehensive`        |
| `SessionStatus`   | `active`, `idle`, `expired`, `revoked`                  |
| `DeviceType`      | `desktop`, `mobile`, `tablet`, `unknown`                |
| `AuthMethod`      | `password`, `oauth`, `sso`, `api_key`, `magic_link`     |
| `OAuthProvider`   | `google`, `github`, `microsoft`, `apple`, `facebook`    |
| `UsagePeriod`     | `hour`, `day`, `week`, `month`                          |

`UserPreferences` groups `GeneralPreferences`, `AssistantPreferences`,
`NotificationPreferences`, `PrivacyPreferences`, `AccessibilityPreferences`.
`AssistantPreferences` includes `defaultModel?`, `voiceStyle`, `responseLength`,
`showThinking`, `streamResponses`, `rememberContext`, `contextRetentionDays`,
`preferredTools?`, `disabledTools?`. Each preference group ships a `DEFAULT_*`
constant and `createDefaultPreferences(userId)` assembles them.

Authentication: `AuthContext`, `IrisJwtClaims` (`sub`, `userId`, `sessionId`,
`email`, `roles`, `permissions?`, `iat`/`exp`/`iss`/`aud`), `LoginRequest`,
`LoginResponse`. Quotas: `UserQuota` (conversation/message/token/apiCall/storage
limits + used counters), `UsageStats`. Guard `isQuotaExceeded` compares used vs.
limit across all five quota dimensions.

> Quota _limits_ are fields on `UserQuota` records; they are not hard-coded plan
> tiers anywhere in `libs/iris`. There is no per-plan limits table in code; an
> earlier draft's "Free/Pro/Enterprise" table has been dropped.

---

## 9. Event Type System

The event module defines the type-level contracts for how Iris components notify
each other about state changes. It uses a CloudEvents-inspired envelope that
carries routing metadata (category, aggregate, correlation ID, trace IDs)
separately from the event payload. This lets consumers filter and route events
based on the envelope alone, without deserializing the payload.

Note that this module defines **TypeScript interfaces**, not a running message
broker. The shared runtime transport is the `@oshun/event-bus` (see §12.3), and
no `libs/iris` package currently publishes events onto it. The types here are
the agreed contract for when publishing is implemented.

`events.ts` defines a CloudEvents-style domain-event model. `DomainEvent<T>`:
`id` (`EventId`), `type` (string), `category` (`EventCategory`), `aggregateId`,
`aggregateType`, `payload`, `metadata` (`EventMetadata`), `occurredAt`,
`sequenceNumber?`. `EventMetadata` carries `correlationId`, `causationId?`,
`source`, `version`, `timestamp`, optional `userId`/`sessionId`/`agentId`/
`traceId`/`spanId`/`tags`/`context`.

`EventCategory` — `conversation`, `message`, `agent`, `task`, `memory`, `user`,
`session`, `system`, `tool`, `model`, `error`. `EventSeverity` — `debug`,
`info`, `warning`, `error`, `critical`.

The `IrisEventType` union is composed from per-category unions. Representative
members (string literals defined in code):

| Category     | Event types (literal `type` strings)                                                                               |
| ------------ | ------------------------------------------------------------------------------------------------------------------ |
| Conversation | `conversation.created/updated/deleted/archived/restored`                                                           |
| Message      | `message.created/updated/deleted`, `message.streaming.started/delta/completed/error`                               |
| Agent        | `agent.created/updated/deleted/started/stopped/paused/resumed/error`                                               |
| Task         | `task.created/started/completed/failed/cancelled/paused/resumed/progress`                                          |
| Memory       | `memory.created/updated/deleted/accessed/summarized/compacted/promoted/demoted`                                    |
| Tool         | `tool.registered/unregistered/called/completed/failed/timeout`                                                     |
| Model        | `model.request.started/completed/failed/rate_limited`, `model.stream.started/chunk/completed`                      |
| User         | `user.created/updated/deleted/activated/deactivated`, `user.preferences.updated`                                   |
| Session      | `session.created/refreshed/expired/revoked`                                                                        |
| System       | `system.started/stopped`, `system.health.changed`, `system.config.updated`, `system.maintenance.started/completed` |
| Error        | `error.occurred/recovered/escalated`                                                                               |

Typed payloads exist for the principal events (`ConversationCreatedPayload`,
`MessageCreatedPayload`, `TaskCompletedPayload`, `MemoryCreatedPayload`,
`ToolCalledPayload`, `ModelRequestCompletedPayload`, …).

Interfaces `IEventBus` (`publish`, `publishBatch`, `subscribe`, `unsubscribe`,
`getSubscriptionCount`) and `IEventStore` (`append`, `getEvents`,
`getEventsByType`, `getLatestEvent`) define the bus/store contracts, plus
event-replay types. Factories: `createEventMetadata`, `createDomainEvent`,
`createEventId`, `createCorrelationId`.

> These are **type-level contracts in `@iris/types`**. They are not the same as
> the shared `@oshun/event-bus` runtime (see §12.3); no `libs/iris` package
> currently publishes domain events onto a broker, and there are no Kafka topics
> — the prior draft's Kafka topic table was unfounded.

---

## 10. Conversation Engine (`@iris/conversation-core`)

`@iris/conversation-core` is one of the most fully built-out packages in the
iris codebase. It implements the core dialogue runtime: a state machine that
tracks every conversation from `idle` through `generating` to `completed`, a
context window manager that assembles and truncates the message history to fit
within token budgets, and a conversation manager that handles persistence,
branching, incognito mode, search indexing, snapshots, and export.

`libs/iris/conversation-core/src/` is the dialogue engine. Public exports
(`index.ts`) include `createConversationManager`, `createContextWindow`,
`createDialogueStateMachine`, plus turn and history managers.

### 10.1 Dialogue State Machine

The dialogue state machine is the heart of the conversation engine. It enforces
that a conversation can only move between states through defined transitions —
for example, you cannot jump from `idle` directly to `streaming` without going
through `processing` and `generating`. This makes it impossible for a bug to
leave a conversation stuck in an intermediate state silently.

`DialogueStateType` defines 11 states: `idle`, `awaiting_input`, `processing`,
`generating`, `streaming`, `tool_executing`, `awaiting_tool_result`,
`completed`, `error`, `paused`, `terminated`.

`DialogueEvent` — discriminated union driving transitions: `USER_MESSAGE`,
`SYSTEM_MESSAGE`, `START_GENERATION`, `START_STREAMING`, `STREAM_CHUNK`,
`STREAM_COMPLETE`, `GENERATION_COMPLETE`, `TOOL_CALL`, `TOOL_RESULT`, `ERROR`,
`PAUSE`, `RESUME`, `TERMINATE`, `RESET`.

A static `STATE_TRANSITIONS` table (`dialogue-state.ts`) maps each state to its
allowed `(event → next state)` pairs. Representative edges:
`idle --USER_MESSAGE--> processing`;
`processing --START_GENERATION--> generating`;
`generating --TOOL_CALL--> tool_executing`;
`streaming --STREAM_COMPLETE--> completed`; any non-terminal state
`--ERROR--> error` and `--TERMINATE--> terminated`. `StateTransitionResult`
records `success`, `fromState`, `toState`, `event`, and an error string on a
rejected transition. `DialogueStateData` tracks `previousState`,
`transitionCount`, `pendingToolCalls`, and `streamingState`.

### 10.2 Context Window

`ContextWindowConfig` — `maxTokens`, `systemPromptReserved`, `responseReserved`,
`enableSummarization`, `summarizationThreshold` (0–1 ratio),
`maxMessagesBeforeSummarize`, `minMessagesToKeep`, `enableTruncation`,
`truncationStrategy` (`oldest_first | least_important | smart`),
`charsPerToken`. `ContextWindowState` and `ContextAddResult` report token
accounting and whether summarization/truncation occurred.
`ContextOptimizationOptions` / `OptimizedContextResult` describe budget-aware
message selection with per-role weights and optional focus terms.

### 10.3 Conversation Manager and Sessions

`ConversationManagerConfig` controls persistence, auto-save interval, in-memory
cap, branching, search, export, and a default incognito policy.
`ConversationSession` bundles a `MutableConversation`, `DialogueStateData`,
`ContextWindowState`, the active/all turns (`TurnState`), and session metadata.
`CreateConversationOptions` accepts
`incognito?: boolean | ConversationIncognitoEnableOptions`.

**Incognito mode** — `ConversationIncognitoPolicy` toggles `disablePersistence`,
`disableSearchIndexing`, `disableExport`, `disableSnapshots`,
`disableBookmarks`, `disableTagging`, `scrubMessageMetadata`.
`ConversationIncognitoMode` records the active mode (`enabledAt`, `reason?`,
`expiresAt?`, `policy`).

`TurnManagerConfig` caps concurrent tool calls, tool-execution timeout, retries,
and toggles turn summaries. `TurnState` tracks `activeToolCalls` /
`completedToolCalls` maps and a token accumulator.

### 10.4 History, Snapshots, Export

`ConversationSnapshot` captures dialogue/context/stats state for rollback, with
`reason` of `auto | manual | before_tool | before_branch | error_recovery`.
`RollbackResult` reports removed messages/turns. `ConversationStorage` is the
storage-adapter interface (save/load conversations, messages, turns, snapshots).
`ConversationExportPayload` / `ConversationImportResult` define portable export
bundles (`format`: `object | json`).

### 10.5 Conversation Engine Events

`ConversationEngineEvent` is the engine-internal event union (distinct from the
`@iris/types` `events.ts` model). Members include
`conversation.created/ updated/deleted`,
`conversation.incognito_enabled/disabled`,
`message.created/updated/streaming/completed`, `turn.started/completed/failed`,
`tool.called/completed`, `state.changed`, `context.summarized/truncated`,
`snapshot.created`, `rollback.completed`, `thread.created/switched`, `error`.

Other `conversation-core` source files: `context-window.ts`,
`conversation-manager.ts`, `conversation-history.ts`, `turn-manager.ts`,
`streaming-response-generator.ts`, `conversation-search-index.ts`,
`conversation-bookmarks.ts`, `conversation-tags.ts`,
`conversation-highlights.ts`, `conversation-sharing.ts`,
`conversation-analytics-tracker.ts`, `in-memory-conversation-storage.ts`.

---

## 11. HTTP / GraphQL API Surface (`@iris/api`)

`@iris/api` is the only runnable service in the iris codebase today. It is a
single Hono process that hosts all route groups — conversation, memory, agent,
knowledge, GraphQL, health, and docs — under one process on port 3100 (default).
Authentication is not yet implemented; every route resolves the user via a
`getUserId()` placeholder returning `'user-001'`. Rate limiting is applied
per-route using preset tiers (`readonly`, `standard`, `heavy`, `streaming`,
`search`).

`apps/iris/api/` is a runnable Hono service. `src/index.ts` wires global
middleware (request-id, distributed tracing, `secureHeaders`, CORS, request
logging, response timing), mounts the route groups, and serves on `PORT`
(default `3100`). Logging and tracing are initialised from env via
`@iris/core/logging` and `@iris/core/tracing`.

Mount points:

| Base path               | Router                                |
| ----------------------- | ------------------------------------- |
| `/health`               | `healthRoutes`                        |
| `/api/v1/conversations` | `conversationRoutes`                  |
| `/api/v1/memory`        | `memoryRoutes`                        |
| `/api/v1/agents`        | `agentRoutes`                         |
| `/api/v1/knowledge`     | `knowledgeRoutes`                     |
| `/graphql`              | GraphQL handler (playground enabled)  |
| `/docs`                 | `docsRoutes` (OpenAPI + code samples) |
| `/`                     | Service info JSON                     |

All routes apply a per-route `rateLimiter` preset (`readonly`, `standard`,
`heavy`, `streaming`, `search`). Request bodies are validated with
`@hono/zod-validator`. Authentication is **not yet implemented** — every route
resolves the user via a `getUserId()` placeholder returning `'user-001'`.

### 11.1 Health Endpoints

| Method | Path               | Description                                                                                                       |
| ------ | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| GET    | `/health`          | Basic liveness JSON (`status`, `service`, `version`)                                                              |
| GET    | `/health/live`     | Liveness probe (`{ status: 'alive' }`)                                                                            |
| GET    | `/health/ready`    | Readiness probe — real Postgres + Redis health round-trips via `@oshun/database`; 503 if a subsystem is unhealthy |
| GET    | `/health/detailed` | Runtime + process memory metrics                                                                                  |

### 11.2 Conversation Endpoints (`/api/v1/conversations`)

| Method | Path                     | Description                                                              |
| ------ | ------------------------ | ------------------------------------------------------------------------ |
| GET    | `/`                      | List (paginated; `status`, `search`, `sortBy`, `sortOrder` query params) |
| POST   | `/`                      | Create (`CreateConversationSchema`)                                      |
| GET    | `/:id`                   | Get by ID                                                                |
| PATCH  | `/:id`                   | Update title / systemPrompt / settings / metadata                        |
| DELETE | `/:id`                   | Delete                                                                   |
| POST   | `/:id/archive`           | Archive                                                                  |
| POST   | `/:id/branch/:messageId` | Branch from a message                                                    |
| GET    | `/stats/summary`         | Per-user conversation stats                                              |
| POST   | `/:id/messages`          | Send a message, returns user + assistant message                         |
| GET    | `/:id/messages`          | List messages (`limit`, `before`, `after`)                               |
| POST   | `/:id/regenerate`        | Regenerate last assistant response                                       |
| POST   | `/:id/stream`            | Stream a response via Server-Sent Events                                 |

`CreateConversationSchema`: `title?`, `systemPrompt?`, `model` (default
`claude-3-5-sonnet-20241022`), `settings?` (`temperature` default 0.7 [0–2],
`maxTokens` default 4096 [1–100000], `topP?`, `presencePenalty?`,
`frequencyPenalty?`), `metadata?`. `SendMessageSchema`: `content` (1–100000
chars), `role` (`user | system`, default `user`), optional `contentBlocks`
(`type ∈ text|image|audio|code|tool_use|tool_result`), `metadata?`,
`parentMessageId?`.

### 11.3 Agent Endpoints (`/api/v1/agents`)

Agent CRUD plus enable/disable/stats, per-agent tool management, and tasks:

| Method               | Path                                       | Description                    |
| -------------------- | ------------------------------------------ | ------------------------------ |
| GET / POST           | `/`                                        | List / create agents           |
| GET / PATCH / DELETE | `/:id`                                     | Get / update / delete an agent |
| POST                 | `/:id/enable`, `/:id/disable`              | Enable / disable an agent      |
| GET                  | `/:id/stats`                               | Agent statistics               |
| GET / POST           | `/:id/tools`                               | List / add agent tools         |
| DELETE / PATCH       | `/:id/tools/:toolName`                     | Remove / enable-disable a tool |
| GET / POST           | `/tasks`                                   | List / create tasks            |
| GET                  | `/tasks/:taskId`                           | Get a task                     |
| POST                 | `/tasks/:taskId/{run,pause,resume,cancel}` | Task lifecycle                 |
| GET                  | `/:id/tasks`                               | List tasks for one agent       |

Route-local validation enums (note: **not** the `@iris/types` enums): agent
`status` ∈ `idle, running, paused, error, completed, disabled`; `capabilities` ∈
`conversation, code_generation, code_execution, web_search, file_operations, tool_use, memory_access, delegation, planning, reasoning`;
task `priority` ∈ `critical, high, medium, low`; task `status` ∈
`pending, queued, running, paused, completed, failed, cancelled`.

### 11.4 Memory Endpoints (`/api/v1/memory`)

Tier-segmented routes plus generic memory CRUD:

| Method               | Path                    | Description                      |
| -------------------- | ----------------------- | -------------------------------- |
| GET                  | `/core`                 | All core memory blocks           |
| GET / PUT            | `/core/:section`        | Get / replace a core block       |
| POST                 | `/core/:section/append` | Append to a core block           |
| GET                  | `/working/:sessionId`   | Working memory for a session     |
| POST                 | `/working`              | Add a working-memory entry       |
| DELETE               | `/working/:sessionId`   | Clear a session's working memory |
| GET                  | `/archival/search`      | Search archival memory           |
| GET / POST           | `/archival`             | List / add archival entries      |
| GET / POST           | `/episodic`             | List / record episodic events    |
| POST                 | `/`                     | Create a memory of any tier      |
| GET / PATCH / DELETE | `/:id`                  | Get / update / delete a memory   |
| POST                 | `/search`               | Cross-tier semantic search       |
| GET                  | `/stats/summary`        | Memory statistics                |
| POST                 | `/cleanup`              | Delete expired memories          |

`CreateMemorySchema` validates `tier` ∈ `core, working, archival, episodic`;
`type` against the 13-value `MemoryType` enum; `importance` against
`critical, high, medium, low, trivial`. Note the route's core-section validation
accepts `persona, human, goals, instructions, custom` — the runnable API uses
`human` where `@iris/types` `CoreMemorySection` uses `user`; the API layer is
not yet fully aligned to the type module.

### 11.5 Knowledge Endpoints (`/api/v1/knowledge`)

Knowledge bases, documents, chunks, and search/RAG:

| Method               | Path                                | Description                    |
| -------------------- | ----------------------------------- | ------------------------------ |
| GET / POST           | `/`                                 | List / create knowledge bases  |
| GET / PATCH / DELETE | `/:id`                              | KB get / update / delete       |
| GET / POST           | `/:id/documents`                    | List / add documents           |
| GET / PATCH / DELETE | `/:id/documents/:documentId`        | Document get / update / delete |
| GET                  | `/:id/documents/:documentId/chunks` | List a document's chunks       |
| POST                 | `/search`                           | Search across knowledge bases  |
| GET                  | `/search`                           | Quick GET search (`q`, `kb`)   |
| POST                 | `/rag`                              | Build RAG context for a query  |
| GET                  | `/:id/stats`                        | KB statistics                  |

Document `type` ∈ `text, markdown, pdf, html, code, json, csv, spreadsheet`;
document `status` ∈ `pending, processing, indexed, failed, archived`; document
`source.type` ∈ `upload, url, api, integration`. KB `settings` allow `chunkSize`
(100–2000), `chunkOverlap` (0–200), `embeddingModel`, `maxDocuments`,
`maxChunksPerDocument`, `autoReindex`. RAG: `maxChunks` (1–20), `maxTokens`
(100–16000), `minScore` (0–1).

### 11.6 GraphQL API (`/graphql`)

`src/graphql/schema.ts` defines an SDL schema served with a playground.

- **Enums** include `ConversationStatus`, `MessageRole`, `AttachmentType`
  (`FILE, IMAGE, AUDIO, VIDEO, CODE`), `ToolCallStatus`, `TaskStatus`,
  `TaskStepType` (`THINKING, TOOL_CALL, TOOL_RESULT, RESPONSE`),
  `TaskStepStatus`, `MemoryTier` (`CORE, WORKING, ARCHIVAL, EPISODIC`),
  `MemoryType`, and others.
- **Query** fields — `conversation`/`conversations`, `message`/`messages`,
  `agent`/`agents`, `task`/`tasks`, `coreMemory`/`coreMemoryBlock`, and the
  knowledge queries; list queries use Relay-style connections.
- **Mutation** fields — conversation, message, agent, task, memory, and
  knowledge mutations (e.g. `createConversation`, `sendMessage`,
  `regenerateMessage`, `createAgent`, `runTask`, `updateCoreMemory`,
  `addToArchival`, `recordEpisodicEvent`, `createKnowledgeBase`, `addDocument`,
  `reindexKnowledgeBase`).
- **Subscription** fields — `messageStream`, `conversationUpdated`,
  `agentUpdated`, `taskUpdated`.

Supporting GraphQL files: `resolvers.ts`, `data-loaders.ts`,
`service-data-sources.ts`, `types.ts`.

---

## 12. Persistence

Understanding how Iris stores data is critical for anyone working on the
service. There are two distinct storage mechanisms that must not be confused:
(1) the Prisma schema in `libs/iris/database`, which defines the PostgreSQL
tables for governance data (consent, continuity, memory scope); and (2) the
`DurableJsonStateStore` used by `@iris/api` services, which stores conversation
and agent state in in-memory Maps backed by a local JSON file. The in-memory
store is not a database — it is a development-stage durability mechanism that
will be replaced with PostgreSQL persistence as the service matures.

### 12.1 Prisma Schema (`libs/iris/database`)

`libs/iris/database/prisma/schema.prisma` targets PostgreSQL via the
`IRIS_DATABASE_URL` env var and generates a Prisma client to
`../src/generated/client`. It defines **exactly three models**:

**`ConsentRecord`** (`consent_records`) — a consent ledger entry. Fields: `id`
(UUID), `slug` (unique), `title`, `summary`, `description?`, `primaryDomain`,
`domains[]`, `subject` (JSON), `category`, `status`, `target` (JSON), `version`,
`legalBasis`/`collection`/`verification` (JSON), `permissions`/`evidence` (JSON
arrays), the request/decision/grant/deny/ withdraw/revoke timestamps,
`expiresAt?`, `renewalRequired`, `renewalRequestedAt?`, `supersededById?`,
`history` (JSON), `metadata` (JSON), `createdAt`/`updatedAt`. Indexed on
`(primaryDomain,status)`, `(status,requestedAt)`, `expiresAt`, `supersededById`.

**`ContinuityState`** (`continuity_states`) — per-user cross-domain continuity
snapshot. Fields: `id`, `userId` (UUID), `summary`, `primaryDomain`,
`domains[]`, `recentDomains[]`, `status`, `momentum`, `focusTheme?` (JSON),
`currentIntent?`, `streakDays`, `activeJourneyCount`, `reminderCount`,
`lastActivityAt?`, `lastComputedAt`, `journeys`/`routeSnapshots`/`reminders`/
`checkpoints` (JSON arrays), `assistantState?` (JSON), timestamps. Indexed on
`userId`, `(primaryDomain,status)`, `(status,lastComputedAt)`, `lastActivityAt`.

**`MemoryScope`** (`memory_scopes`) — a named, governed memory boundary. Fields:
`id`, `slug` (unique), `title`, `summary`, `description?`, `primaryDomain`,
`domains[]`, `status`, `kind`, `mode`, `owner` (JSON), `priority`, `isDefault`,
`boundary`/`retention`/`consent`/`sharing`/ `userControls`/`governance` (JSON),
`runtimeMappings` (JSON array), `linkedPersonaIds[]`, `tags[]`, `metadata`
(JSON), timestamps. Indexed on `slug`, `(primaryDomain,status)`,
`(status,priority)`, `isDefault`.

> The Prisma schema does **not** define `iris_conversations`, `iris_messages`,
> `iris_memories`, `iris_knowledge_documents`, or `iris_knowledge_chunks`. Those
> tables, and the Qdrant/Redis cache-key tables described in an earlier draft,
> do not exist in code and have been removed.

### 12.2 API Service Storage

The `@iris/api` services (`conversation.service.ts`, `memory.service.ts`,
`agent.service.ts`, `knowledge.service.ts`) hold all state in **in-memory
`Map`s** keyed by branded IDs (e.g.
`conversations: Map<ConversationId, StoredConversation>`, with
`userConversations`, `agentTasks`, `documentChunks`, etc. as secondary indexes).

Durability is provided by `DurableJsonStateStore<T>`
(`src/services/durable-json-state.ts`): an atomic JSON-file snapshot store that
writes to a `${pid}.${timestamp}.tmp` file and `renameSync`s it into place,
loading on startup. It is **file-backed JSON state**, not a database. The
`/health/ready` probe is the only place the API touches PostgreSQL/Redis, and
only to check connectivity through `@oshun/database`.

### 12.3 Event Transport

The shared `@oshun/event-bus` library (`libs/shared/event-bus`) is
**Redis-backed**, not Kafka. `event-bus.ts` opens two `ioredis` connections
(publisher + subscriber): `publish()` serialises an envelope into a TTL-bounded
Redis key and fans it out over Redis pub/sub; a Redis `HASH` tracks acked
`(eventId, subscriptionId | group)` pairs; `nack(delay)` and delayed publish use
a durable Redis sorted set; dead letters live in a Redis list + hash. Consumer
groups race via `SET NX` claim keys.

No `libs/iris` package currently publishes onto this bus, and there is no Kafka
broker anywhere in the iris tree.

---

## 13. Configuration (`@iris/config`, `@iris/core`)

All Iris service configuration is managed through a Zod-validated schema tree
defined in `libs/iris/core/src/config/schemas.ts`. This means configuration
errors are caught at startup rather than at runtime, and every config key has an
explicit type. The loader (`loader.ts`) reads from environment variables,
populating the schema with default values where variables are absent.

### 13.1 Configuration Schema

`libs/iris/core/src/config/schemas.ts` defines the Zod-validated configuration
tree (`irisConfigSchema → IrisConfig`):

- `service` — `name` (default `iris`), `version`, `environment`
  (`development | staging | production | test`), `instanceId?`.
- `logging` — `level` (`trace | debug | info | warn | error | fatal | silent`),
  `pretty?`, `redactPaths?`, `includeTimestamp`, `enableTracing`.
- `context` — `defaultTimeoutMs` (30000), `maxContextDepth` (10),
  `propagateHeaders`.
- `providers` — per-provider config objects for `anthropic`, `openai`, `google`,
  `cohere`, `mistral`, `local`; `defaultProvider` (default `anthropic`).
- `agents` — array of `agentConfigSchema` entries.
- `memory` — `enabled`, `provider` (`in-memory | redis | postgres | vector-db`,
  default `in-memory`), `maxSize` (1000), `ttlMs?`, optional `embeddings` block.
- `conversation` — `maxTurns` (100), `maxTokensPerMessage?`, `contextWindow`
  (128000), `persistMessages` (default `false`), optional `summarization` block.
- `tools` — array of `toolConfigSchema` entries.

Provider defaults: Anthropic `claude-3-5-sonnet-20241022`, OpenAI `gpt-4-turbo`,
Google `gemini-1.5-pro` / `us-central1`, Cohere `command-r-plus`, Mistral
`mistral-large-latest`; all HTTP providers default to a 120000 ms timeout and 3
retries.

### 13.2 Environment Variables

`libs/iris/core/src/config/loader.ts` reads configuration from the environment
(it accepts a custom `env` object, defaulting to `process.env`). The table below
lists every variable that the loader actually reads — as verified against
source. Variables listed in earlier documentation drafts that are not read in
code (e.g. `IRIS_QDRANT_URL`, `IRIS_KAFKA_BROKERS`, `IRIS_JWT_SECRET`) have been
removed from this table.

Keys actually read:

| Variable                                                                                                                                                      | Purpose                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `NODE_ENV`                                                                                                                                                    | Environment selection            |
| `SERVICE_NAME`, `SERVICE_VERSION`, `INSTANCE_ID`                                                                                                              | Service identity                 |
| `LOG_LEVEL`, `LOG_PRETTY`, `LOG_TIMESTAMP`, `LOG_REDACT_PATHS`                                                                                                | Logging                          |
| `IRIS_ENABLE_TRACING`                                                                                                                                         | Enable tracing in logging config |
| `IRIS_DEFAULT_TIMEOUT_MS`, `IRIS_MAX_CONTEXT_DEPTH`, `IRIS_PROPAGATE_HEADERS`                                                                                 | Context                          |
| `IRIS_DEFAULT_PROVIDER`                                                                                                                                       | Default model provider           |
| `ANTHROPIC_API_KEY`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL`, `ANTHROPIC_MAX_TOKENS`, `ANTHROPIC_TEMPERATURE`, `ANTHROPIC_TIMEOUT`, `ANTHROPIC_MAX_RETRIES`   | Anthropic                        |
| `OPENAI_API_KEY`, `OPENAI_ORGANIZATION`, `OPENAI_BASE_URL`, `OPENAI_MODEL`, `OPENAI_MAX_TOKENS`, `OPENAI_TEMPERATURE`, `OPENAI_TIMEOUT`, `OPENAI_MAX_RETRIES` | OpenAI                           |
| `GOOGLE_AI_API_KEY`, `GOOGLE_AI_PROJECT`, `GOOGLE_AI_LOCATION`, `GOOGLE_AI_MODEL`, `GOOGLE_AI_MAX_TOKENS`, `GOOGLE_AI_TEMPERATURE`, `GOOGLE_AI_TIMEOUT`       | Google AI                        |
| `IRIS_MEMORY_ENABLED`, `IRIS_MEMORY_PROVIDER`, `IRIS_MEMORY_MAX_SIZE`, `IRIS_MEMORY_TTL_MS`                                                                   | Memory                           |
| `IRIS_EMBEDDINGS_ENABLED`, `IRIS_EMBEDDINGS_PROVIDER`, `IRIS_EMBEDDINGS_MODEL`, `IRIS_EMBEDDINGS_DIMENSIONS`                                                  | Embeddings                       |
| `IRIS_CONVERSATION_MAX_TURNS`, `IRIS_CONVERSATION_MAX_TOKENS_PER_MESSAGE`, `IRIS_CONVERSATION_CONTEXT_WINDOW`, `IRIS_CONVERSATION_PERSIST`                    | Conversation                     |
| `IRIS_SUMMARIZATION_ENABLED`, `IRIS_SUMMARIZATION_THRESHOLD`, `IRIS_SUMMARIZATION_MODEL`                                                                      | Summarization                    |

The `@iris/api` service additionally reads `PORT` (default `3100`),
`CORS_ORIGINS`, `OTEL_SERVICE_NAME` / `IRIS_SERVICE_NAME`, `NODE_ENV`, and the
`POSTGRES_*` / `REDIS_*` / `DB_*` variables consumed by `@oshun/database` for
its health probes.

> Cohere and Mistral have config schemas but no dedicated `*_API_KEY` loader
> branch in `loader.ts`. There is no `IRIS_QDRANT_URL`, `IRIS_MINIO_*`,
> `IRIS_KAFKA_BROKERS`, `IRIS_ENCRYPTION_KEY`, or `IRIS_JWT_SECRET` consumed in
> code — the prior draft's environment table listed variables that are not read
> anywhere.

### 13.3 Error Codes

`libs/iris/core/src/errors/codes.ts` defines a numeric error-code taxonomy (~92
codes) grouped by range: general (1000–1099), validation (1100–1199), auth
(1200–1299), resource (1300–1399), context (1400–1499), conversation
(1500–1599), agent (1600–1699), model/provider (1700–1799), memory (1800–1899),
configuration (1900–1999), external service (2000–2099). It also maps codes to
HTTP statuses. `errors/base.ts` defines the base error classes used across
`@iris/core`.

`@iris/core` also provides request `context`, structured `logging` (Pino-based),
and OpenTelemetry-style `tracing` (`correlation`, `decorators`, `profiler`,
`tracer`).

---

## 14. Library Clusters

`libs/iris/**` is organised into clusters, each covering a distinct functional
area. The package counts below are derived from `package.json` files actually on
disk — not from directory listings — so they are authoritative. Some package
names do not match their directory path; where this is true, the `package.json`
`name` field is the correct identifier, not the path.

### 14.1 Conversation Cluster

There are **31 `@iris/conversation*`-named packages**. They fall into two
groups:

- **Top-level `conversation-*` packages** (15): `@iris/conversation-core`,
  `conversation-context`, `conversation-intent`, `conversation-state`,
  `conversation-response`, `conversation-rag`, `conversation-style`,
  `conversation-format`, `conversation-citations`, `conversation-uncertainty`,
  `conversation-orchestration`, and the four provider adapters
  (`conversation-providers-anthropic`, `-google`, `-local`, `-openai`).
- **`libs/iris/conversation/` subdirectory packages** (16): the umbrella
  `@iris/conversation` plus `conversation-benchmarking`,
  `conversation-branching`, `conversation-costs`, `conversation-export`,
  `conversation-finetuning`, `conversation-prompts`, `conversation-search`,
  `conversation-summarization`, `conversation-templates`, `conversation-tokens`,
  and the six reasoning packages under `conversation/reasoning/`
  (`@iris/code-reasoning`, `@iris/consistency`, `@iris/math-reasoning`,
  `@iris/metacognition`, `@iris/scientific-reasoning`,
  `@iris/structured-reasoning`).

> Earlier docs described "10 conversation-_ libs" — the real count is far
> higher, and the two-group layout (sibling
> `conversation-_`directories vs. children of`conversation/`) is itself a
> notable structural fact.

### 14.2 Memory Cluster

`libs/iris/memory/` contains **22 `package.json` files**: `@iris/memory-core`,
`memory-short-term`, `memory-long-term`, `memory-episodic`, `memory-semantic`,
`memory-working`-equivalent state lives under `memory-transitions` /
`memory-consolidation`, plus `memory-retrieval`, `memory-persistence`,
`memory-analytics`, `memory-debugging`, `memory-migration`, `memory-sharing`,
`memory-tools`, `memory-visualization`, `memory-writing`, and a
`memory/personalization/` sub-tree (`@iris/personalization`,
`personalization-feedback`, `-inference`, `-segments`, `-testing`) and
`@iris/privacy` (`memory/privacy`).

> The earlier "7 memory-\* libs" figure is wrong; the directory holds 22
> packages. Note also `@iris/memory-working` does **not** exist as a package —
> working-memory behaviour is split across `memory-transitions`,
> `memory-consolidation`, and the tier types in `@iris/types`.

### 14.3 Other Clusters

| Cluster                        | Representative packages                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `knowledge/` (~23)             | `@iris/knowledge` (core), `knowledge-chunking`, `knowledge-embeddings`, `knowledge-retrieval`, `knowledge-rag` (+ `rag/` sub-tree: `agentic-rag`, `rag-multimodal`, `rag-evaluation`, `rag-debugging`, `rag-adaptive-chunking`), `knowledge-graph`, `knowledge-graphrag`, `knowledge-factcheck`, `knowledge-grounding`, `knowledge-realtime`, `knowledge-curation`, `knowledge-enterprise`, `knowledge-personal`, `knowledge-versioning`, `knowledge-export`, `knowledge-query`, `knowledge-freshness`, `knowledge-types`                                                                                                                                                                                                                                                                                                                                                        |
| `agents/`                      | `@iris/agents` (catalog), `agents-core`, `@iris/archetypes`, `@iris/multi-agent` (+ `multi-agent-memory`), `@iris/agent-personalities`, `@iris/agent-marketplace`, `@iris/agent-specialization`, `@iris/workflows`, the `agents/proactive/` packages (`ambient`, `anticipation`, `automation`, `reminders`), the `agents/tools/` registry (`api-tools`, `code-tools`, `database-tools`, `filesystem-tools`, `web-tools`, `function-calling`, `tool-auth`, `tool-composition`, `tool-marketplace`, `tool-monitoring`, `tools-registry`, `tool-versioning`, `learning-tools`), and the `agents/computer-use/` packages (`@iris/computer-use`, `@iris/browser-automation`, `@iris/desktop-automation`, `@iris/sandbox`, `@iris/action-safety`, `@iris/screenshot-vision`, `@oshun/iris-computer-use-native`, `computer-use-recording`, `-recovery`, `-templates`, `-accessibility`) |
| `code/`                        | `@iris/code-generation`, `code-review`, `code-explanation`, `code-understanding`, `code-architecture`, `code-codebase`, `code-dependencies`, `code-semantic`, `code-search`, `code-quality`, `code-metrics`, `code-consistency`, `code-languages`, `code-git`, `code-agentic`, `code-cli`, `code-turbo-mode`, `code-memory-persistence`, `code-repository-intelligence`, and the `code/ide/` packages (`code-ide`, `-actions`, `-completions`, `-hover`, `-inline`)                                                                                                                                                                                                                                                                                                                                                                                                              |
| `multimodal/`                  | `@iris/voice` (`multimodal/voice/recognition`) and the `voice/` family (`voice-synthesis`, `voice-conversation`, `voice-commands`, `voice-auth`, `voice-effects`, `voice-journaling`, `voice-pronunciation`), `@iris/multimodal/vision` and the `vision/` family (`vision-understanding`, `vision-documents`, `vision-diagrams`, `vision-generation`, `vision-screenshare`, `vision-search`, `vision-video`, `vision-memory`), `@iris/iot`, `@iris/spatial`, `@iris/bci`                                                                                                                                                                                                                                                                                                                                                                                                         |
| `voice/` (top-level)           | `@iris/voice-providers`, `@iris/voice-empathic-synthesis`, `@iris/voice-ultra-low-latency`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `emotional/`                   | `emotional-recognition`, `emotional-response`, `emotional-rapport`, `emotional-social`, `emotional-ethics`, `emotional-tracking`, `emotional-wellbeing`, `emotional-voice-analysis`, `emotional-multimodal-fusion`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `privacy/`                     | `privacy` (umbrella, at `memory/privacy`) plus the `privacy/` tree: `privacy-encryption`, `privacy-local`, `privacy-anonymization`, `privacy-minimization`, `privacy-audit`, `privacy-access`, `privacy-secrets`, `privacy-sovereignty`, `privacy-communication`, `privacy-dashboard`, `privacy-private-cloud`, `privacy-third-party-consent`, `privacy-tiered-compute`, and the `privacy/safety/` and `privacy/security/` sub-trees                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `platform/`                    | `platform-admin`, `platform-billing`, `platform-gateway`, `platform-ratelimit`, `platform-reporting`, `platform-codegen`, `platform-sla` / `platform-sla-management`, `platform-webhooks`, `@iris/streaming`, `@iris/plugins`, the `platform/customization/` packages (`@iris/agent-builder`, `@iris/whitelabel`), and the `platform/sdk-*` packages                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `accessibility/`               | `accessibility` (umbrella), `accessibility-visual` (+ `-display`, `-alternatives`), `accessibility-motor`, `accessibility-hearing`, `accessibility-cognitive`, `accessibility-braille`, `accessibility-i18n`, `accessibility-voice-ui`, `accessibility-adaptive`, `accessibility-testing`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `analytics/`                   | `analytics` (umbrella), `analytics-realtime`, `analytics-cohort`, `analytics-funnel`, `analytics-ab`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `personalization/` (top-level) | `personalization-benchmarking`, `personalization-state-aware`, `personalization-user-model-persistence`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `bci/` (top-level)             | `bci-apple-hid`, `bci-intent-prediction-api`, `bci-privacy-framework`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `testing/`                     | `testing` (umbrella), `testing-chaos`, `testing-load`, `testing-synthetic`, `testing-visual`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| Foundation                     | `@iris/core`, `@iris/types`, `@iris/config`, `@iris/embeddings`, `@iris/model-routing`, `@iris/ensemble`, `@iris/failover`, `@iris/mcp`, `@iris/a2a`, `@iris/reasoning-thinking`, `@iris/concordia-assistant`, `@iris/sdk` (`sdk/typescript`), `libs/iris/database` (Prisma schema), `presence` (`presence-sync`, `presence-integration`)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |

> Some package names do not carry an `@iris/` prefix or do not match their
> directory path (e.g. `@iris/knowledge` lives at `knowledge/core`,
> `@iris/voice` lives at `multimodal/voice/recognition`,
> `@oshun/iris-computer-use-native` lives under `agents/computer-use/native/ts`,
> `@iris/privacy` lives at `memory/privacy`). Treat the `package.json` `name`
> field as authoritative.

---

## 15. Cross-Domain Integration Libraries

`libs/iris/integrations/` contains **six packages, one per sister Oshun domain**
— they are **cross-Oshun-domain bridges**, not third-party SaaS connectors. Each
package encapsulates the typed API surface that Iris exposes to a specific
consuming domain, so that domain can depend on a stable contract without
importing from `@iris/api` internals directly. The `@iris/integrations-psyche`
package is the most developed example: it exports `./avatar`, `./emotion`,
`./conferencing`, and `./agents` sub-paths, with only `eventemitter3` and `zod`
as runtime dependencies.

| Package                     | Bridges Iris to | Stated purpose                                                      |
| --------------------------- | --------------- | ------------------------------------------------------------------- |
| `@iris/integrations-hathor` | Hathor          | Worldbuilding integration                                           |
| `@iris/integrations-maya`   | Maya            | Metaverse-engine integration                                        |
| `@iris/integrations-nyx`    | Nyx             | Astronomy integration                                               |
| `@iris/integrations-psyche` | Psyche          | AI avatar representation, emotion expression, conferencing presence |
| `@iris/integrations-sophia` | Sophia          | Research & knowledge integration                                    |
| `@iris/integrations-yemaya` | Yemaya          | Creative Studio integration                                         |

There is **no top-level `@iris/integrations` package**, and no Slack / Microsoft
Teams / Notion / Jira / GitHub / Confluence connector exists anywhere in
`libs/iris`. The `@iris/integrations-psyche` agent classes exported from
`./agents` include `avatar-presenter-agent`, `conference-participant-agent`,
`emotion-controller-agent`, and `persona-manager-agent`.

---

## 16. Rollback-Safe In-Match Commentary Contract (V2 Consumer)

The V2 fighting-game project (`V2/`) consumes Iris for AI play-by-play
commentary during live matches. This section documents a specialized Iris API
contract that is entirely distinct from the standard conversation API.

The fundamental engineering challenge is that V2's combat simulation uses
rollback networking for lag compensation: when a desync is detected, the engine
rolls back game state and re-simulates from the divergence point. Any LLM call
made from inside the rollback bubble would be non-deterministic on the re-sim,
corrupting replay files and breaking reconciliation between peers. Iris
therefore exposes an **in-match commentary contract** with hard guarantees that
LLM calls originate only from outside the rollback boundary and that commentary
outputs never feed game state.

**Implementation:** `createMatchCommentaryStream` is implemented in
`libs/iris/conversation-orchestration/src/match-commentary.ts` and re-exported
from that package's `index.ts` (verified on disk, with a co-located
`match-commentary.spec.ts`). It is consumed by V2 through
`@v2/iris-commentary-orchestration` plus the `V2AICommentary` Unreal plugin.

The example below shows the complete call signature. Key parameters:
`determinismMode: 'off-rollback-only'` is the hard contract flag;
`latencyBudgetMs` sets the end-to-end deadline;
`fallbackMode: 'pre-recorded-bank'` activates the pre-recorded fallback when the
budget is exceeded; `rightsContext` carries the NIL/likeness metadata for rights
gates.

```typescript
import { createMatchCommentaryStream } from '@iris/conversation-orchestration';

const stream = createMatchCommentaryStream({
  matchId: 'match_abc123',
  commentators: [
    'calliope-v2-aria-voss-play-by-play',
    'calliope-v2-marcus-knox-color',
  ], // @calliope/persona-live ids
  ruleset: 'wwe', // cross-ref @shakti/fighting-ruleset-bridge
  determinismMode: 'off-rollback-only', // hard contract: never feeds simulation
  latencyBudgetMs: 800, // end-to-end cap from KO trigger to playable audio
  fallbackMode: 'pre-recorded-bank', // when latency exceeded
  rightsContext: { fighters: ['ryu', 'ken'], venue: 'metro-city-arena' },
});

stream.on('cue', (cue) => {
  // cue.kind: 'pre-match' | 'mid-match' | 'ko' | 'finisher' | 'reversal' | 'comeback' | 'post-match'
});
```

### 16.1 Determinism Guarantees

The **fundamental safety property** is that commentary outputs are
gameplay-inert: they are audio + subtitle only and never feed simulation state.
Every emitted cue is an `audio/subtitle` artifact (an `audio` voice handle plus
a `subtitle` string) — there is no field a cue can write that the rollback
engine reads back. Because outputs don't influence the deterministic combat sim,
the call itself can be asynchronous and non-deterministic without breaking
rollback.

1. **No call from inside the rollback bubble.** Commentary calls originate from
   V2's `V2/services/` adapter layer, never from `V2Combat`, `V2Gameplay`,
   `V2Input`, or `V2Netcode`. Iris refuses calls tagged
   `from-rollback-frame=true`.
2. **No output mutates simulation state.** Commentary tokens are audio /
   subtitle outputs only; they never feed game state, AI Director hints, balance
   counters, or any input the rollback engine consumes.
3. **Replay reproducibility via record-and-replay cache.** The LLM call is
   non-deterministic, so a seed alone cannot regenerate the same line. The cache
   stores the **specific output of the live call**, keyed by
   `(matchId, cueId, cueTriggerFrame, branchId)` — a record-and-replay pattern.
   Each fresh replay-takeover from a mid-match save state mints a new
   `branchId`; re-playing the same takeover branch hits the cache.
4. **Per-peer cue distribution in 1v1 rollback matches.** Each peer's
   `V2/services/` adapter independently calls Iris; commentary is local-audio
   output only, so divergence between peers is acceptable (Invariant #2).
   Tournament broadcasts and spectator surfaces receive one canonical stream
   from the broadcaster's adapter (or the host peer's, absent a broadcaster).
5. **Latency budget with deterministic fallback.** KO-trigger commentary has an
   800 ms end-to-end budget
   (`IRIS_MATCH_COMMENTARY_DEFAULT_LATENCY_BUDGET_MS = 800`): ~420 ms Iris LLM
   round trip, ~240 ms persona TTS render, ~100 ms Psyche lipsync/expression
   render, ~40 ms safety margin. On budget overrun the contract emits a
   `latency-budget-exceeded` cue from the pre-recorded bank. Unlike the live
   record-and-replay cache (Invariant #3), the fallback line is **fully
   deterministic** so every peer and every replay selects the identical line: it
   is chosen by `selectFallbackLine(buildIrisMatchCommentaryFallbackKey( …))`
   from a key whose shape is
   `IRIS_MATCH_COMMENTARY_FALLBACK_KEY_SHAPE = '(cueKind,ruleset,commentatorId,seed)'`.
   The `IrisMatchCommentaryFallbackKey` carries exactly those four fields —
   `cueKind`, `ruleset`, `commentatorId`, `seed` — and
   `stringifyIrisMatchCommentaryFallbackKey` produces the stable
   `(cueKind,ruleset,commentatorId,seed)` digest used to index the bank, so the
   pre-recorded fallback never desyncs peers or replays.

### 16.2 Cue Taxonomy

Each cue type has its own latency budget that reflects how time-sensitive the
associated game event is. Pre-match cues have generous 5-second budgets because
they can be pre-baked during the loading screen. KO and reversal cues have tight
600–800 ms budgets because they must play while the game moment is still fresh.
The `finisher` cue gets 1,200 ms because fatality/critical-art sequences have
their own animation time that absorbs the extra latency.

| Cue                    | Trigger                                                      | Latency budget         |
| ---------------------- | ------------------------------------------------------------ | ---------------------- |
| `pre-match.intro`      | Match-load fade-in                                           | 5,000 ms (pre-bake OK) |
| `pre-match.tale`       | Mid-load tale about the rivalry / venue                      | 5,000 ms (pre-bake OK) |
| `round.start`          | Round begin                                                  | 800 ms                 |
| `combo.notable`        | Player lands a notable combo                                 | 800 ms                 |
| `reversal`             | Counter-hit / parry / Drive Impact / Soul Charge / Reversal  | 600 ms                 |
| `near-ko`              | Defender HP < 10%                                            | 800 ms                 |
| `ko`                   | KO event                                                     | 600 ms                 |
| `finisher`             | Fatality / Critical Art / Rage Art / Critical Edge / Blazin' | 1,200 ms               |
| `comeback`             | Player overcomes a 50%+ HP deficit                           | 800 ms                 |
| `pre-match.tournament` | Tournament context line                                      | 5,000 ms (pre-bake OK) |
| `post-match.recap`     | Match-end recap pre-replay                                   | 1,500 ms               |

### 16.3 Per-Commentator Persona Binding and Gates

Each commentator is not a generic TTS voice — it is a named AI artist with a
distinct persona, vocal style, and rights profile. This section documents the
rendering pipeline from LLM output to final audio, and the safety gates that run
before any cue is emitted.

Each commentator binds to a `@calliope/persona-live` AI artist. The pipeline is:
V2 cue trigger → `@iris/conversation-orchestration` (LLM call, off-rollback) →
`@calliope/persona-live` persona transform → `@iris/voice` TTS →
`@psyche/avatar-lipsync` + `@psyche/avatar-expressions` → `@euterpe/master` mix
ducking → V2 `V2Audio` MetaSounds graph.

Each cue passes rights + safety gates before emission — NIL/likeness clearance,
originality checking, harm checking, regional tone filtering, and EU AI Act
conformity. Several gate packages are **planned**, not yet on disk; specific
package assignments are tracked in the V2 dependency backlog. Failed gates fall
through to the pre-recorded bank with a logged reason code.

> Cross-references to `@themis/*`, `@kuanyin/*`, `@nous/safety`,
> `@calliope/persona-live`, `@v2/*`, etc. describe **planned cross-domain
> contracts** owned by other domains and the V2 sister-monorepo; their existence
> and exact names are governed by those domains' own docs and backlogs, not this
> file.

### 16.4 V2 Bridge Surfaces (planned / cross-domain)

The following V2-facing surfaces are described in the V2 architecture and
dependency docs and are owned jointly with the V2 sister monorepo:

- `@iris/concordia-assistant` (`libs/iris/concordia-assistant`, on disk) — Iris
  source of truth for appellant / arbiter dialogue scaffolding in
  Concordia-style dispute flows; consumed by V2 through
  `@v2/concordia-substrate`.
- `@iris/accessibility` accessibility-bridge profile — consumed by V2 through
  `@v2/iris-accessibility`.
- `@iris/voice` real-time translation routing — consumed by V2 through
  `@v2/iris-realtime-translation` for spectator chat, commentary localization,
  broadcast overlays, and companion second-screen feeds; presentation-only and
  off rollback.

These bridge contracts depend on packages owned by other domains; treat their
naming and status as governed by the V2 and partner-domain docs.

---

## 17. V2 Accessibility Bridge Contract (V2 Consumer)

The V2 fighting-game project consumes Iris for player accessibility across its
Unreal main client and its companion surfaces. This **V2 Accessibility Bridge
Contract** is owned by `@iris/accessibility`
(`libs/iris/accessibility/src/index.ts`) and adapted into V2 by
`@v2/iris-accessibility`
(`apps/v2/iris-accessibility/src/iris-accessibility.ts`). Like the commentary
contract in §16, every output is presentation / certification only: the surface
sets `mayInfluenceRollback: false` and cannot change deterministic
match-simulation state.

`@iris/accessibility` exports `buildIrisV2AccessibilityBridge`, the bridge id
`IRIS_V2_ACCESSIBILITY_BRIDGE_ID`, and the capability vocabulary
(`caption-streaming`, `dyslexia-typography`, `cognitive-load-reduction`,
`screen-reader-bridge`, `native-ue-accessibility-hooks`). From a
`IrisV2AccessibilityProfileInput` it derives:

- **Caption streaming.** Iris does not yet own a live caption engine end to end,
  so the bridge names `@psyche/caption-streaming` as its **real-time caption
  provider** (the `captionProviderPackageName` field). V2 reuses the same Psyche
  engine that backs `@v2/psyche-caption-streaming` (see Psyche §19.2), keeping a
  single caption pipeline behind both the spectator/HUD path and the
  accessibility path.
- **Dyslexia-friendly typography.** `IrisV2DyslexiaTypographyTokens` ships a
  dyslexia-safe font stack with `letterSpacingEm: 0`, generous word spacing and
  line height, a minimum body font size, and a measure cap — and asserts
  `preservesButtonLabelFit` so the typography change never clips fight-UI
  labels.
- **Cognitive assistive UI.** `IrisV2CognitiveLoadUiPolicy` drives a
  reduced-cognitive-load mode (simplified language, persistent tutorial hints,
  step-by-step prompts, reduced-distraction chrome, reduced motion) while
  guaranteeing `hidesCriticalActions: false` — the **cognitive assistive UI**
  never removes a control the player needs to fight.
- **Screen-reader bridge.** `IrisV2ScreenReaderBridgePolicy` defines the
  **screen-reader bridge**: a live-region policy of
  `polite-status-assertive-critical`, mandatory descriptions for every focusable
  widget, and suppression of dramatic color-only text so platform screen readers
  receive clean semantics.

`@v2/iris-accessibility` wraps that bridge through
`buildV2IrisAccessibilitySurface` and surfaces explicit V2 control flags
(`controlsCaptionStreaming`, `controlsDyslexiaCognitiveAssistiveUi`,
`controlsScreenReaderBridge`, `companionAndMainClientBridge`,
`nativeUeAccessibilityHooks`). It composes the Psyche caption engine via
`createAccessibilityCaptionEngine`, emits the
`v2.accessibility.caption-stream.ready` topic, and gates release on
accessibility certification (`certGateBlocksRelease: true`). The rollback policy
string is `off-rollback-accessibility-ui-only`, and the surface carries
`mayInfluenceRollback: false`. The contract is validated by
`V2/ue/Tools/check-v2-iris-accessibility.py`.

---

## 18. V2 Mobile Companion App (V2 Consumer)

V2's second-screen companion is implemented as the React Native surface
`apps/oshun/mobile/v2/` (`companionAppModel.ts`, `V2CompanionApp.tsx`,
`V2CompanionLaunchTile.tsx`, route `app/v2.tsx`). Its agent layer is the
mobile-safe built-in catalog `@iris/agents` (see §5.5), which the companion
imports to resolve frame-data and appeal-help agents.

`buildV2CompanionModel` assembles the companion from the shared Oshun client
packages (`@oshun/shell-core`, `@oshun/auth-client`,
`@oshun/concordia-integration`, `@oshun/trust-safety`) plus `@sophia/client` for
local research and `@iris/agents` for the agent catalog. It calls
`resolveIrisAgentsLaunchReadiness` to confirm the agent package is wired and
`selectIrisAgentForIntent` to pick the `frame-data-query` agent for the "Open
frame-data codex" action. The companion is purely a client surface; it never
touches the rollback simulation. The binding is validated by
`V2/ue/Tools/check-v2-mobile-companion.py`, whose Iris-facing checks require
that `apps/oshun/mobile/v2/` and `@iris/agents` are both named in this
specification.

---

## 19. Acceptance Criteria

These criteria define the minimum bar for a change to be considered complete and
correct for the Iris domain. They are written to catch the most common failure
modes: type misalignment, hardcoded config, storage claims that outrun
implementation, and documentation that drifts from code.

A change to the Iris domain satisfies this specification when:

1. **Type fidelity** — new or changed domain objects are defined in
   `@iris/types` with branded IDs and (for string unions) a paired Zod
   `…Schema`; co-located `*.test.ts` files cover the new types.
2. **Enum discipline** — enum values added in code are reflected here; the
   four-tier `MemoryTier` model is preserved unless the enum itself changes.
3. **API contract** — new `@iris/api` routes apply a `rateLimiter` preset,
   validate input with `@hono/zod-validator`, and surface errors through the
   `errorHandler`; the GraphQL schema stays consistent with the REST routes.
4. **State-machine soundness** — changes to `conversation-core` keep the
   `DialogueStateType` / `DialogueEvent` transition table total (every
   non-terminal state handles `ERROR` and `TERMINATE`).
5. **Persistence honesty** — claims about storage match reality: the Prisma
   schema has three models; the API services use in-memory `Map`s with
   `DurableJsonStateStore` JSON-file durability.
6. **Event transport honesty** — event-transport descriptions remain
   Redis-backed (`@oshun/event-bus`); no Kafka claims.
7. **Configuration accuracy** — new configuration keys are added to
   `core/config/schemas.ts` and read in `loader.ts`, and documented in §13.2
   with their real variable names.
8. **No fabrication** — every entity, endpoint, event, and count in the docs is
   traceable to source; planned-but-absent items are labelled `(planned)`.
