# Hathor Domain — Technical Specifications

> Worldbuilding, Narrative Design, and World Simulation Platform

This document specifies what is implemented in `libs/hathor/*` and
`apps/hathor/*`. Every model, field, enum, endpoint, event, and library entry
below is taken directly from the source: primarily
`database/prisma/schema.prisma`, the `world-api`, `narrative-api`, and
`simulation-worker` route files, `event-publisher/src/*`,
`event-handlers/src/index.ts`, `contracts/src/events/hathor.ts`, the
`svc-veilborn-*` app files, `lore-compiler/src/*`, `validation/src/index.ts`,
and the `package.json`/`project.json` of each project.

The Hathor domain spans **17 libraries** and **7 applications**. Section 10 is
the only forward-looking section; everything in it is labelled `(planned)` and
sourced from the V2 contract docs under `V2/`.

---

## 1. Data Models (`@hathor/database`)

`@hathor/database` is the Prisma schema and client for Hathor. The schema is in
`libs/hathor/database/prisma/schema.prisma`. The Prisma generator emits the
client into `src/generated/client`;
`previewFeatures = ["fullTextSearchPostgres"]` is enabled. The datasource is
PostgreSQL via the `HATHOR_DATABASE_URL` environment variable. The schema does
**not** declare a multi-schema namespace — all tables live in the connection's
default schema. Every model id is a `cuid()` stored as `VarChar(25)`.

The schema defines 20 models in total: World, WorldVersion, Branch,
MergeRequest, Entity, EntityRelation, StoryGraph, StoryNode, StoryArc,
DialogueTree, DialogueNode, DialogueSession, Quest, QuestObjective, QuestReward,
SimulationRun, SimulationEvent, SimulationSnapshot, ValidationResult, and
AuditLog.

### 1.1 World

The `World` model is the top-level container for a simulation universe. Every
other model belongs to a world, directly or indirectly.

**Table:** `world` | **Unique:** `[userId, name]`

| Field             | Type            | Default    | Description          |
| ----------------- | --------------- | ---------- | -------------------- |
| `id`              | String (cuid)   | auto       | Primary key          |
| `name`            | String (≤200)   | —          | World name           |
| `description`     | String? (Text)  | —          | Description          |
| `status`          | WorldStatus     | `DRAFT`    | Lifecycle status     |
| `genre`           | WorldGenre      | `FANTASY`  | Genre classification |
| `scope`           | WorldScope      | `MEDIUM`   | World size/scope     |
| `technologyLevel` | TechnologyLevel | `MEDIEVAL` | Technology era       |
| `magicLevel`      | MagicLevel      | `MEDIUM`   | Magic prevalence     |
| `userId`          | String (≤50)    | —          | Owner user ID        |
| `organizationId`  | String? (≤50)   | —          | Organization         |
| `settings`        | Json            | `{}`       | World settings       |
| `stats`           | Json            | `{}`       | Aggregate statistics |
| `tags`            | String[]        | `[]`       | Tags                 |
| `createdAt`       | DateTime        | `now()`    | Creation timestamp   |
| `updatedAt`       | DateTime        | auto       | Update timestamp     |
| `publishedAt`     | DateTime?       | —          | Publish timestamp    |
| `deletedAt`       | DateTime?       | —          | Soft-delete marker   |

**Relations:** `versions`, `branches`, `entities`, `quests`, `storyGraphs`,
`dialogueTrees`, `simulations`. **Indexes:** `status`, `genre`, `userId`,
`organizationId`, `createdAt`, `deletedAt`.

---

### 1.2 WorldVersion

`WorldVersion` represents an immutable snapshot of world state. Versions form a
parent-child chain analogous to git commits: each version has a
`parentVersionId` pointing to the previous one, and `isCurrent` marks the HEAD
of each branch.

**Table:** `world_version` | **Unique:** `[worldId, version]`,
`[worldId, branchId, version]`

| Field             | Type           | Default | Description                       |
| ----------------- | -------------- | ------- | --------------------------------- |
| `id`              | String         | auto    | Primary key                       |
| `version`         | Int            | `1`     | Version number                    |
| `name`            | String? (≤200) | —       | Version name                      |
| `description`     | String? (Text) | —       | Version description               |
| `worldId`         | String         | —       | FK to World (cascade delete)      |
| `branchId`        | String?        | —       | FK to Branch                      |
| `parentVersionId` | String?        | —       | Self-FK to parent WorldVersion    |
| `changes`         | Json           | `[]`    | List of changes (delta)           |
| `snapshot`        | Json           | `{}`    | Full state snapshot               |
| `createdBy`       | String? (≤50)  | —       | Creator user ID                   |
| `createdAt`       | DateTime       | `now()` | Creation timestamp                |
| `isCurrent`       | Boolean        | `false` | Whether this is the branch's HEAD |

Self-relation `VersionParent` provides `childVersions`. Also has merge-request
back-relations `mergeRequestsSource` and `mergeRequestsTarget`.

---

### 1.3 Branch

A `Branch` represents an alternate timeline or development track within a world.
Each branch diverges from a specific `branchPointVersionId`, accumulates its own
`WorldVersion` records, and can be merged back via a `MergeRequest`.

**Table:** `branch` | **Unique:** `[worldId, name]`

| Field                  | Type           | Default | Description                  |
| ---------------------- | -------------- | ------- | ---------------------------- |
| `id`                   | String         | auto    | Primary key                  |
| `name`                 | String (≤200)  | —       | Branch name                  |
| `description`          | String? (Text) | —       | Branch description           |
| `worldId`              | String         | —       | FK to World (cascade delete) |
| `branchPointVersionId` | String?        | —       | Version where branch split   |
| `isMain`               | Boolean        | `false` | Whether main branch          |
| `createdAt`            | DateTime       | `now()` | Creation timestamp           |
| `updatedAt`            | DateTime       | auto    | Update timestamp             |

**Relations:** `versions`.

---

### 1.4 MergeRequest

A `MergeRequest` tracks a proposal to merge changes from one version into
another. The `conflicts` JSON field records any entities changed on both sides;
each conflict must be resolved (source, target, or custom) before the merge can
execute.

**Table:** `merge_request`

| Field             | Type               | Default | Description               |
| ----------------- | ------------------ | ------- | ------------------------- |
| `id`              | String             | auto    | Primary key               |
| `title`           | String (≤200)      | —       | Merge request title       |
| `description`     | String? (Text)     | —       | Description               |
| `sourceVersionId` | String             | —       | FK to source WorldVersion |
| `targetVersionId` | String             | —       | FK to target WorldVersion |
| `status`          | MergeRequestStatus | `OPEN`  | Status                    |
| `conflicts`       | Json               | `[]`    | Conflict details          |
| `resolution`      | Json?              | —       | Resolution details        |
| `createdBy`       | String? (≤50)      | —       | Creator user ID           |
| `reviewedBy`      | String? (≤50)      | —       | Reviewer user ID          |
| `createdAt`       | DateTime           | `now()` | Creation timestamp        |
| `updatedAt`       | DateTime           | auto    | Update timestamp          |
| `mergedAt`        | DateTime?          | —       | Merge timestamp           |

---

### 1.5 Entity

`Entity` is the universal container for all objects in a world. Rather than
having separate tables for characters, factions, locations, etc., all 10 entity
types share this one table and store type-specific properties in the
`properties` JSON field. This design allows new entity types to be added without
schema migrations.

The 10 supported entity types are: CHARACTER, FACTION, LOCATION, EVENT,
RELATIONSHIP, ERA, REGION, ITEM, CULTURE, RELIGION.

**Table:** `entity` | **Unique:** `[worldId, type, name]`

| Field          | Type                    | Default | Description                  |
| -------------- | ----------------------- | ------- | ---------------------------- |
| `id`           | String                  | auto    | Primary key                  |
| `name`         | String (≤200)           | —       | Entity name                  |
| `description`  | String? (Text)          | —       | Description                  |
| `type`         | EntityType              | —       | Entity type (10 values)      |
| `worldId`      | String                  | —       | FK to World (cascade delete) |
| `versionId`    | String?                 | —       | Version reference            |
| `properties`   | Json                    | `{}`    | Type-specific properties     |
| `tags`         | String[]                | `[]`    | Tags                         |
| `aliases`      | String[]                | `[]`    | Alternate names for search   |
| `createdAt`    | DateTime                | `now()` | Creation timestamp           |
| `updatedAt`    | DateTime                | auto    | Update timestamp             |
| `deletedAt`    | DateTime?               | —       | Soft-delete marker           |
| `searchVector` | Unsupported(`tsvector`) | —       | Full-text search vector      |

**Relations:** `outgoingRelations`, `incomingRelations` (both
`EntityRelation[]`).

---

### 1.6 EntityRelation

`EntityRelation` models a directed relationship between two entities — for
example, "character A is allied with faction B" or "location C is inside region
D". The `type` field is free-text so any relationship semantics can be expressed
without schema changes. The `bidirectional` flag indicates whether the
relationship applies equally in both directions.

**Table:** `entity_relation` | **Unique:** `[sourceId, targetId, type]`

| Field           | Type          | Default | Description                    |
| --------------- | ------------- | ------- | ------------------------------ |
| `id`            | String        | auto    | Primary key                    |
| `sourceId`      | String        | —       | FK to source Entity (cascade)  |
| `targetId`      | String        | —       | FK to target Entity (cascade)  |
| `type`          | String (≤100) | —       | Relationship type (free-text)  |
| `properties`    | Json          | `{}`    | Additional properties          |
| `strength`      | Float         | `1.0`   | Relationship strength / weight |
| `bidirectional` | Boolean       | `false` | Whether bidirectional          |
| `createdAt`     | DateTime      | `now()` | Creation timestamp             |
| `updatedAt`     | DateTime      | auto    | Update timestamp               |

---

### 1.7 StoryGraph, StoryNode, StoryArc

The three story models together represent a non-linear branching narrative. A
`StoryGraph` is the top-level container; `StoryNode` records represent
individual scenes, choices, or conditions; `StoryArc` records are the directed
edges connecting nodes.

**StoryGraph** — Branching narrative graph. **Table:** `story_graph` |
**Unique:** `[worldId, name, version]`. Fields: `id`, `name`, `description?`,
`worldId` (FK, cascade), `version` (Int, default `1`), `isActive` (Boolean,
default `true`), `tags`, `createdAt`, `updatedAt`, `publishedAt?`. Relations:
`nodes` (StoryNode[]), `arcs` (StoryArc[]).

**StoryNode** — A node in a story graph. **Table:** `story_node`. Fields: `id`,
`storyGraphId` (FK, cascade), `type` (StoryNodeType, default `SCENE`), `name`,
`content?`, `positionX`/`positionY` (Float, default `0`, for the visual editor),
`conditions` (Json `[]`), `actions` (Json `[]`), `metadata` (Json `{}`),
`createdAt`, `updatedAt`. Relations: `outgoingArcs`, `incomingArcs`.

**StoryArc** — Directed edge between two StoryNodes. **Table:** `story_arc` |
**Unique:** `[storyGraphId, sourceId, targetId]`. Fields: `id`, `storyGraphId`
(FK, cascade), `sourceId`/`targetId` (FK to StoryNode, cascade), `label?` (≤200,
choice text), `conditions` (Json `[]`, traversal conditions), `priority` (Int,
default `0`, for auto-selection), `createdAt`.

---

### 1.8 DialogueTree, DialogueNode, DialogueSession

The three dialogue models support both authoring (the tree structure) and
runtime playback (active sessions). A `DialogueTree` holds the authored
structure; `DialogueNode` records hold individual lines and choices;
`DialogueSession` tracks a live playback instance, recording which node the
participant is currently at and what choices have been made.

**DialogueTree** — Tree structure for character conversations. **Table:**
`dialogue_tree` | **Unique:** `[worldId, name]`. Fields: `id`, `name`,
`description?`, `worldId` (FK, cascade), `speakers` (Json `[]`, array of entity
IDs), `locationId?` (location context), `tags`, `createdAt`, `updatedAt`.
Relations: `nodes`, `sessions`.

**DialogueNode** — A single dialogue step. **Table:** `dialogue_node`. Fields:
`id`, `dialogueTreeId` (FK, cascade), `type` (DialogueNodeType, default
`STATEMENT`), `speakerId?` (entity ID), `content?`, `emotion?` (≤50,
emotion/tone tag), `conditions` (Json `[]`), `actions` (Json `[]`), `isStart`
(Boolean, default `false`), `isEnd` (Boolean, default `false`), `choices` (Json
`[]`), `nextNodes` (Json `[]`, branching node IDs), `createdAt`, `updatedAt`.

**DialogueSession** — A runtime instance of a dialogue tree being played.
**Table:** `dialogue_session`. Fields: `id`, `dialogueTreeId` (FK, cascade),
`currentNodeId?`, `history` (Json `[]`, visited nodes), `context` (Json `{}`,
session state), `participantId?` (≤50), `isComplete` (Boolean, default `false`),
`startedAt`, `completedAt?`.

---

### 1.9 Quest, QuestObjective, QuestReward

Quests are stored across three related tables. `Quest` holds the top-level
definition; `QuestObjective` holds the individual tasks a player must complete;
`QuestReward` holds the items, currency, or reputation changes granted on
completion.

**Quest** — A quest definition. **Table:** `quest` | **Unique:**
`[worldId, name]`. Fields: `id`, `name`, `description?`, `worldId` (FK,
cascade), `category` (QuestCategory, default `SIDE`), `priority` (QuestPriority,
default `NORMAL`), `status` (QuestStatus, default `DRAFT`), `giverId?`
(quest-giver entity), `locationId?`, `minLevel?`/`maxLevel?` (Int),
`prerequisites` (Json `[]`, quest IDs), `tags`, `createdAt`, `updatedAt`,
`publishedAt?`. Relations: `objectives`, `rewards`.

**QuestObjective** — An objective within a quest. **Table:** `quest_objective`.
Fields: `id`, `questId` (FK, cascade), `type` (ObjectiveType, default `CUSTOM`),
`description` (Text, required), `targetCount` (Int, default `1`),
`currentProgress` (Int, default `0`), `isCompleted` (Boolean, default `false`),
`isOptional` (Boolean, default `false`), `orderIndex` (Int, default `0`),
`targetEntityId?`, `metadata` (Json `{}`), `createdAt`, `updatedAt`.

**QuestReward** — A reward granted on quest completion. **Table:**
`quest_reward`. Fields: `id`, `questId` (FK, cascade), `type` (String, ≤50),
`amount?` (Int, for currency/XP), `itemId?` (entity ID for item rewards),
`description?` (≤500), `isChoice` (Boolean, default `false`), `createdAt`.

---

### 1.10 SimulationRun, SimulationEvent, SimulationSnapshot

Simulation state is tracked across three models. `SimulationRun` is the job
record, tracking status and progress from submission through completion.
`SimulationEvent` records individual events emitted during the run (e.g., a
price spike, an alliance formed). `SimulationSnapshot` captures the full
simulation state at a specific tick so it can be compared or restored later.

**SimulationRun** — A simulation job. **Table:** `simulation_run`.

| Field            | Type               | Default   | Description             |
| ---------------- | ------------------ | --------- | ----------------------- |
| `id`             | String             | auto      | Primary key             |
| `worldId`        | String             | —         | FK to World (cascade)   |
| `type`           | SimulationType     | `FULL`    | Simulation type         |
| `status`         | SimulationStatus   | `PENDING` | Job status              |
| `priority`       | SimulationPriority | `NORMAL`  | Priority                |
| `input`          | Json               | `{}`      | Input parameters        |
| `output`         | Json?              | —         | Output results          |
| `progress`       | Float              | `0`       | Progress (0–1)          |
| `currentTick`    | Int                | `0`       | Current simulation tick |
| `totalTicks`     | Int?               | —         | Total ticks to run      |
| `errorMessage`   | String? (Text)     | —         | Error message           |
| `errorDetails`   | Json?              | —         | Structured error detail |
| `workerId`       | String? (≤50)      | —         | Worker assignment       |
| `startVersionId` | String?            | —         | Version at sim start    |
| `endVersionId`   | String?            | —         | Version after sim       |
| `createdAt`      | DateTime           | `now()`   | Creation timestamp      |
| `updatedAt`      | DateTime           | auto      | Update timestamp        |
| `startedAt`      | DateTime?          | —         | Start timestamp         |
| `completedAt`    | DateTime?          | —         | Completion timestamp    |

**Relations:** `events`, `snapshots`.

**SimulationEvent** — An event generated during simulation at a specific tick.
**Table:** `simulation_event`. Fields: `id`, `simulationId` (FK, cascade),
`type` (String, ≤100), `tick` (Int), `data` (Json `{}`), `affectedEntities`
(Json `[]`, entity IDs), `createdAt`.

**SimulationSnapshot** — Full state snapshot at a tick. **Table:**
`simulation_snapshot` | **Unique:** `[simulationId, tick]`. Fields: `id`,
`simulationId` (FK, cascade), `tick` (Int), `state` (Json `{}`), `createdAt`.

---

### 1.11 ValidationResult

`ValidationResult` stores the output of lore consistency validation checks so
results can be reviewed later and compared across runs.

**Table:** `validation_result`

| Field            | Type          | Description             |
| ---------------- | ------------- | ----------------------- |
| `id`             | String        | Primary key             |
| `worldId`        | String        | World reference         |
| `entityId`       | String?       | Entity reference        |
| `validationType` | String (≤100) | Validation type         |
| `isValid`        | Boolean       | Whether valid           |
| `issues`         | Json (`[]`)   | Issues found            |
| `suggestions`    | Json (`[]`)   | Improvement suggestions |
| `createdAt`      | DateTime      | Creation timestamp      |

---

### 1.12 AuditLog

`AuditLog` records a tamper-evident trail of tracked changes, capturing the
previous state alongside the new changes so that the history of any entity can
be reconstructed.

**Table:** `audit_log`. Fields: `id`, `entityType` (String, ≤50), `entityId`
(String, ≤25), `action` (String, ≤50), `userId?` (≤50), `changes` (Json `{}`),
`previousState` (Json?), `createdAt`. **Indexes:** `[entityType, entityId]`,
`userId`, `action`, `createdAt`.

---

## 2. Database Enumerations

All enums below are defined in `schema.prisma` and re-exported from
`@hathor/database`. They form the canonical persistence contract — see the note
at the end of this section for differences between these Prisma enums and the
per-service string-union types used at the API wire layer.

### WorldStatus (4)

`DRAFT`, `ACTIVE`, `ARCHIVED`, `DELETED`

### WorldGenre (8)

`FANTASY`, `SCIENCE_FICTION`, `HISTORICAL`, `CONTEMPORARY`, `HORROR`, `MYSTERY`,
`MYTHOLOGY`, `CUSTOM`

### WorldScope (5)

`SMALL` (village/city), `MEDIUM` (region/country), `LARGE` (continent), `EPIC`
(world), `COSMIC` (multi-world/universe)

### TechnologyLevel (9)

`PRIMITIVE`, `ANCIENT`, `MEDIEVAL`, `RENAISSANCE`, `INDUSTRIAL`, `MODERN`,
`FUTURISTIC`, `ADVANCED`, `MIXED`

### MagicLevel (5)

`NONE`, `LOW`, `MEDIUM`, `HIGH`, `EPIC`

### EntityType (10)

`CHARACTER`, `FACTION`, `LOCATION`, `EVENT`, `RELATIONSHIP`, `ERA`, `REGION`,
`ITEM`, `CULTURE`, `RELIGION`

### CharacterStatus (5)

`ALIVE`, `DECEASED`, `UNKNOWN`, `MISSING`, `LEGENDARY`

### FactionType (10)

`GOVERNMENT`, `MILITARY`, `RELIGIOUS`, `CRIMINAL`, `MERCHANT`, `GUILD`,
`SECRET_SOCIETY`, `FAMILY`, `TRIBAL`, `OTHER`

### LocationType (11)

`CITY`, `TOWN`, `VILLAGE`, `FORTRESS`, `TEMPLE`, `DUNGEON`, `WILDERNESS`,
`LANDMARK`, `REGION`, `REALM`, `PLANE`

### QuestStatus (6)

`DRAFT`, `AVAILABLE`, `ACTIVE`, `COMPLETED`, `FAILED`, `CANCELLED`

### QuestCategory (6)

`MAIN`, `SIDE`, `EXPLORATION`, `FACTION`, `DAILY`, `WORLD_EVENT`

### QuestPriority (4)

`LOW`, `NORMAL`, `HIGH`, `CRITICAL`

### ObjectiveType (8)

`COLLECT`, `KILL`, `TALK`, `EXPLORE`, `ESCORT`, `DELIVER`, `CRAFT`, `CUSTOM`

### StoryNodeType (6)

`START`, `SCENE`, `CHOICE`, `CONDITION`, `ACTION`, `END`

### DialogueNodeType (6)

`GREETING`, `STATEMENT`, `QUESTION`, `RESPONSE`, `CHOICE`, `END`

### SimulationType (5)

`ECONOMY`, `POLITICS`, `CULTURE`, `SCENARIO`, `FULL`

### SimulationStatus (6)

`PENDING`, `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`

### SimulationPriority (4)

`LOW`, `NORMAL`, `HIGH`, `CRITICAL`

### MergeRequestStatus (5)

`OPEN`, `APPROVED`, `MERGED`, `REJECTED`, `CANCELLED`

### ValidationSeverity (4)

`INFO`, `WARNING`, `ERROR`, `CRITICAL`

> **Note on string-union types in services.** The API apps and the SDK declare
> their own lowercase string-union types that are not always identical to the
> Prisma enums above. For example `apps/hathor/world-api/src/types.ts` declares
> `WorldStatus = 'draft' | 'in_progress' | 'review' | 'published' | 'archived'`
> and `MergeRequestStatus = 'open' | 'merged' | 'closed' | 'conflict'`, and
> `@hathor/validation` (`src/types.ts`) declares
> `ValidationSeverity = 'error' | 'warning' | 'info'` (three values, no
> `CRITICAL`). Treat the Prisma enums above as the persistence contract and the
> per-service unions as the wire/runtime contract.

---

## 3. Database Schema Highlights

This section summarizes the structural design decisions in the schema that are
most important to understand before working with Hathor's database layer.

- **ORM:** Prisma 6 | **DB:** PostgreSQL.
- **Generated client:** emitted into `database/src/generated/client`.
- **20 models:** World, WorldVersion, Branch, MergeRequest, Entity,
  EntityRelation, StoryGraph, StoryNode, StoryArc, DialogueTree, DialogueNode,
  DialogueSession, Quest, QuestObjective, QuestReward, SimulationRun,
  SimulationEvent, SimulationSnapshot, ValidationResult, AuditLog.
- **Version-control model:** Git-like versioning with branches, parent-child
  version chains (`WorldVersion.parentVersionId`), and merge requests.
  `isCurrent` on `WorldVersion` marks the HEAD.
- **Entity design:** a single `Entity` table with `properties: Json` for
  type-specific fields supports all 10 entity types without per-type tables.
- **Full-text search:** an `Unsupported("tsvector")` column on `entity`
  (`fullTextSearchPostgres` preview feature enabled).
- **Client lifecycle:** `@hathor/database` exposes `getHathorClient`,
  `createHathorClient`, and `disconnectHathorClient` (`database/src/client.ts`),
  plus seed scripts (`prisma/seed.ts`, `prisma/seed-test.ts`).

---

## 4. API Surface

The Hathor domain ships three HTTP worldbuilding services (World API, Narrative
API, and Simulation Worker) plus two Veilborn game services. The worldbuilding
services store data through in-memory service stores backed by repositories; the
simulation worker uses an in-memory job queue.

### 4.1 World API (`@hathor/world-api`, Port 3001)

The World API is a Hono server (`apps/hathor/world-api/src/app.ts`). Global
middleware includes `secureHeaders`, `cors`, request `logger`, `prettyJSON`, and
a `rateLimit` middleware applied to `/api/*`. Routes are mounted under
`/api/v1`. Validation uses `@hono/zod-validator`.

Health: `GET /health`, `GET /ready` (readiness probes the world and version
services).

#### World CRUD (`routes/worlds.ts`)

| Method | Path                          | Description               |
| ------ | ----------------------------- | ------------------------- |
| GET    | `/api/v1/worlds`              | List worlds (filterable)  |
| POST   | `/api/v1/worlds`              | Create a world            |
| GET    | `/api/v1/worlds/:id`          | Get world by ID           |
| PUT    | `/api/v1/worlds/:id`          | Update world properties   |
| DELETE | `/api/v1/worlds/:id`          | Delete a world            |
| POST   | `/api/v1/worlds/:id/clone`    | Clone a world             |
| GET    | `/api/v1/worlds/:id/export`   | Export world as JSON file |
| PUT    | `/api/v1/worlds/:id/status`   | Update world status       |
| PATCH  | `/api/v1/worlds/:id/settings` | Update world settings     |
| PATCH  | `/api/v1/worlds/:id/stats`    | Update world stats        |

#### Collaborators (`routes/worlds.ts`)

| Method | Path                                       | Description           |
| ------ | ------------------------------------------ | --------------------- |
| POST   | `/api/v1/worlds/:id/collaborators`         | Add a collaborator    |
| DELETE | `/api/v1/worlds/:id/collaborators/:userId` | Remove a collaborator |

> Collaborators are tracked by the world service; there is no `Collaborator`
> Prisma table — collaborator state is part of the in-memory world record.

#### Branches (`routes/worlds.ts`)

| Method | Path                                    | Description        |
| ------ | --------------------------------------- | ------------------ |
| GET    | `/api/v1/worlds/:id/branches`           | List branches      |
| POST   | `/api/v1/worlds/:id/branches`           | Create a branch    |
| GET    | `/api/v1/worlds/:id/branches/:branchId` | Get branch details |
| DELETE | `/api/v1/worlds/:id/branches/:branchId` | Delete a branch    |

#### Versioning (`routes/versions.ts`)

The versioning routes implement the full git-like version lifecycle: committing
changes, viewing history, reconstructing state at any point, and marking
official releases.

| Method | Path                                          | Description                     |
| ------ | --------------------------------------------- | ------------------------------- |
| GET    | `/api/v1/worlds/:worldId/versions`            | List versions                   |
| POST   | `/api/v1/worlds/:worldId/versions`            | Create a version (commit)       |
| GET    | `/api/v1/worlds/:worldId/versions/:versionId` | Get version by ID               |
| GET    | `…/versions/:versionId/history`               | Get ancestor chain              |
| GET    | `…/versions/:versionId/state`                 | Reconstruct state at a version  |
| GET    | `…/versions/:versionId/changes`               | List changes in a version       |
| GET    | `/api/v1/worlds/:worldId/releases`            | List releases                   |
| POST   | `…/versions/:versionId/release`               | Mark version as a release       |
| POST   | `…/versions/:versionId/snapshot`              | Create a snapshot for a version |
| GET    | `…/versions/:versionId/snapshot`              | Get the snapshot for a version  |
| POST   | `…/versions/:versionId/revert`                | Revert world to a version       |
| GET    | `…/versions/:fromId/compare/:toId`            | Diff between two versions       |
| GET    | `/api/v1/worlds/:worldId/current`             | Get the current (HEAD) version  |
| GET    | `/api/v1/worlds/:worldId/state`               | Get current reconstructed state |

#### Merge System (`routes/merge.ts`)

The merge routes cover the full merge workflow: diffing branches, creating a
merge request, resolving individual conflicts, and executing the merge.
Cherry-pick and rebase are also exposed here.

| Method | Path                                           | Description                     |
| ------ | ---------------------------------------------- | ------------------------------- |
| GET    | `…/diff/:fromId/:toId`                         | Diff two versions               |
| GET    | `…/branches/:sourceId/diff/:targetId`          | Diff two branches               |
| GET    | `…/merge-requests`                             | List merge requests             |
| POST   | `…/merge-requests`                             | Create a merge request          |
| GET    | `…/merge-requests/:mrId`                       | Get merge request               |
| POST   | `…/merge-requests/:mrId/close`                 | Close a merge request           |
| GET    | `…/merge-requests/:mrId/conflicts`             | List conflicts                  |
| POST   | `…/merge-requests/:mrId/conflicts/resolve`     | Resolve a single conflict       |
| POST   | `…/merge-requests/:mrId/conflicts/resolve-all` | Resolve all (`source`/`target`) |
| POST   | `…/merge-requests/:mrId/merge`                 | Execute the merge               |
| POST   | `…/cherry-pick`                                | Cherry-pick a version's changes |
| POST   | `…/rebase`                                     | Rebase a branch onto another    |

> Merge-request creation in `routes/merge.ts` takes a `sourceBranchId` /
> `targetBranchId` body and the merge service resolves their HEAD versions.

#### Query Engine (`routes/query.ts`)

The query engine exposes general-purpose search and graph-traversal operations
over world entities. `POST …/relationships` performs an actual BFS over an
entity's ID-reference fields up to a requested depth; `inferTargetType()` maps
field names (e.g. `factionId`, `allies`, `prerequisites`) to entity types.

| Method | Path                                  | Description                                              |
| ------ | ------------------------------------- | -------------------------------------------------------- |
| POST   | `…/query`                             | General entity query                                     |
| GET    | `…/search?q=…`                        | Full-text search                                         |
| GET    | `…/factions`                          | Query factions                                           |
| GET    | `…/characters`                        | Query characters                                         |
| GET    | `…/locations`                         | Query locations                                          |
| GET    | `…/events`                            | Query timeline events                                    |
| GET    | `…/entities/:type/:id`                | Get one entity                                           |
| POST   | `…/entities/:type/batch`              | Get many entities by ID                                  |
| GET    | `…/entities/:type/:id/related/:field` | Get entities related by field                            |
| POST   | `…/relationships`                     | BFS graph traversal                                      |
| POST   | `…/paths`                             | Find paths between entities                              |
| POST   | `…/aggregate`                         | Aggregation (`count`/`sum`/`avg`/`min`/`max`/`group_by`) |

#### Auth and Rate Limiting

- Auth: `X-User-ID` request header (forwarded by the gateway), read by mutating
  routes.
- Rate limit: `rateLimit({ limit: 100, windowMs: 60_000 })` on `/api/*`. Exposes
  `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.

---

### 4.2 Narrative API (`@hathor/narrative-api`, Port 3002)

The Narrative API is a Hono server (`apps/hathor/narrative-api/src/app.ts`) with
middleware: `cors`, `logger`, `timing`, `requestId`, `errorHandler`. Validation
uses `@hono/zod-validator`. Routes are mounted under `/api`.

Health: `GET /health`; service info at `GET /`.

#### Story Graphs (`routes/story-graphs.ts`)

CRUD at `/api/story-graphs` plus: arc add/update/delete (`/:id/arcs`,
`/:id/arcs/:arcId`); node add/update/delete (`/:id/nodes`,
`/:id/nodes/:nodeId`); connect nodes (`POST /:id/nodes/:nodeId/connect`); state
update (`PUT /:id/state`); variable and flag setters
(`PUT /:id/variables/:name`, `PUT /:id/flags/:name`); clone (`POST /:id/clone`);
export (`GET /:id/export`).

#### Quests (`routes/quests.ts`)

CRUD at `/api/quests` plus lifecycle transitions
(`POST /:id/start|complete|fail|abandon`); objective progress
(`PUT /:id/objectives/:objId/progress`, `POST /:id/objectives/:objId/complete`);
clone (`POST /:id/clone`); export (`GET /:id/export`).

#### Dialogues (`routes/dialogues.ts`)

CRUD at `/api/dialogues` plus: node CRUD (`/:id/nodes`, `/:id/nodes/:nodeId`);
choice CRUD (`/:id/nodes/:nodeId/choices/...`); speaker CRUD
(`/:id/speakers/...`); runtime sessions (`POST /:id/sessions`,
`GET /sessions/:sessionId`, `GET /sessions/:sessionId/current`,
`POST /sessions/:sessionId/advance`, `DELETE /sessions/:sessionId`); clone
(`POST /:id/clone`); export (`GET /:id/export`).

#### Validation (`routes/validation.ts`)

The narrative validation routes allow validating individual resources (a story
graph, quest, or dialogue) or batching multiple resources in a single request.

| Method | Path                                | Description                      |
| ------ | ----------------------------------- | -------------------------------- |
| POST   | `/api/validation/story-graphs`      | Validate a story graph           |
| POST   | `/api/validation/quests`            | Validate a quest                 |
| POST   | `/api/validation/dialogues`         | Validate a dialogue tree         |
| POST   | `/api/validation/batch`             | Batch-validate multiple entities |
| POST   | `/api/validation/constraints/check` | Check a single constraint        |

---

### 4.3 Simulation Worker (`@hathor/simulation-worker`, Port 3004)

The Simulation Worker is a Hono server
(`apps/hathor/simulation-worker/src/app.ts`) with middleware: `cors`, `logger`.
Routes are mounted under `/api`. The default worker-pool configuration has one
worker each for economy, politics, culture, and scenario job types.

| Method | Path                      | Description                       |
| ------ | ------------------------- | --------------------------------- |
| POST   | `/api/jobs`               | Submit a simulation job           |
| GET    | `/api/jobs`               | List jobs (filter by status/type) |
| GET    | `/api/jobs/:jobId`        | Get job status, position, ETA     |
| POST   | `/api/jobs/:jobId/cancel` | Cancel a running or queued job    |
| POST   | `/api/jobs/:jobId/retry`  | Retry a failed job                |
| GET    | `/api/workers`            | Get all worker health statuses    |
| GET    | `/api/workers/:workerId`  | Get one worker's health           |
| GET    | `/api/stats`              | Queue and worker statistics       |
| POST   | `/api/cleanup`            | Trigger cleanup of completed jobs |

Job submission validates the simulation `type` against
`economy | politics | culture | scenario | integrated`, validates the
`startDate`/`endDate`, and rejects when the queue is full (HTTP 503).

Note that the simulation worker uses its own string-union types that differ
slightly from the Prisma enums in § 2. `SimulationType` in the worker is
`'economy' | 'politics' | 'culture' | 'scenario' | 'integrated'`.
`SimulationStatus` adds `paused` to the Prisma set; `SimulationPriority` is
`'urgent' | 'high' | 'normal' | 'low' | 'background'`
(`apps/hathor/simulation-worker/src/types/index.ts`). The five simulation input
shapes are `EconomySimulationInput`, `PoliticsSimulationInput`,
`CultureSimulationInput`, `ScenarioSimulationInput`, `IntegratedSimulationInput`
(all extend `BaseSimulationInput`), with matching `*SimulationOutput` types.

---

### 4.4 Veilborn Game Services

Two Fastify services under `apps/hathor/` implement **Veilborn Chronicles**, a
tabletop-RPG / wargame game built on the Hathor domain. Both use
`@lilith/fastify-core` and `@lilith/service-lib` and run on port `8080` by
default (`PORT` env var).

#### `@hathor/veilborn-core` (`apps/hathor/svc-veilborn-core`)

The core game engine for the tabletop RPG and wargame. The HTTP surface
(`src/app.ts`) exposes `/`, `/health`, `/ready`, `/live`, and a `/metrics`
endpoint (JSON or Prometheus). The game systems are implemented as ~30
domain-logic modules under `src/` (~42k lines total), each fully unit-tested
under `src/__tests__/integration/`:

- `combat.ts` — hex-grid tactical combat ("Confluence"): axial/cube hex
  coordinates, Tide Initiative turn order, an Impulse/Flow/Surge/Torrent action
  economy, line-of-sight and cover.
- `character-creation.ts`, `character-sheet.ts`, `abilities.ts`, `awakening.ts`,
  `paths.ts`, `resonance.ts` — character build, progression, and abilities.
- `cards.ts` — card system (the largest module).
- `dice.ts` — dice resolution.
- `world-gen.ts` — seeded procedural generation of realms, regions, locations,
  items, creatures, and factions (`SeededRandom`).
- `wargame.ts`, `territory-control.ts`, `combat-ai.ts`, `veylmaster.ts` —
  wargame and AI systems.
- `weave.ts`, `threads.ts`, `social.ts`, `multiplayer.ts`, `game-state.ts`,
  `tutorial.ts`, `launch-content.ts`, `mobile-ui.ts`, `ui-system.ts`,
  `beta-program.ts`, `asset-gen.ts`, `data-access.ts`, `database-schema.ts` —
  social, persistence, content, and UI systems.

#### `@hathor/veilborn-strategy` (`apps/hathor/svc-veilborn-strategy`)

Strategy game engine for the "The Veil War" mode. Fastify service with optional
`@fastify/websocket` real-time sync and `@lilith/service-lib` JWT
authentication. REST surface (`src/app.ts`) under `/api/v1`:

| Method | Path                                               | Description                   |
| ------ | -------------------------------------------------- | ----------------------------- |
| GET    | `/api/v1/games`                                    | List games                    |
| POST   | `/api/v1/games`                                    | Create a game                 |
| GET    | `/api/v1/games/:gameId`                            | Get full game state           |
| POST   | `/api/v1/games/:gameId/join`                       | Join a game                   |
| GET    | `/api/v1/games/:gameId/state`                      | Player view (fog-of-war)      |
| POST   | `/api/v1/games/:gameId/actions`                    | Submit an action              |
| GET    | `/api/v1/games/:gameId/actions`                    | Action history                |
| GET    | `/api/v1/games/:gameId/players`                    | List players                  |
| POST   | `/api/v1/games/:gameId/snapshots`                  | Save game state               |
| GET    | `/api/v1/games/:gameId/snapshots`                  | List snapshots                |
| POST   | `/api/v1/games/:gameId/snapshots/:snapshotId/load` | Restore a snapshot            |
| DELETE | `/api/v1/games/:gameId/snapshots/:snapshotId`      | Delete a snapshot             |
| GET    | `/api/v1/ws/game`                                  | WebSocket sync (when enabled) |
| GET    | `/api/v1/realtime/stats`                           | Real-time stats               |

Five game modes are wired at game-creation time, each with its own engine and
route module: **Echoes of Fate** (`echoes-of-fate/timeline-engine.ts`,
`routes/temporal-routes.ts`), **Resonance Wars**
(`resonance-wars/combat-engine.ts`, `routes/resonance-wars-routes.ts`), **Weave
Conspiracy** social deduction (`weave-conspiracy/conspiracy-engine.ts`,
`routes/weave-conspiracy-routes.ts`), **Eternal Game** area control
(`eternal-game/influence-engine.ts`, `routes/eternal-game-routes.ts`), and
**Grand Campaign** multi-session meta-game
(`grand-campaign/campaign-manager.ts`, `routes/grand-campaign-routes.ts`). The
service also includes per-mode AI opponents (`src/ai/*`) and a balance pipeline
(`src/balance/*` — skill rating, pattern tracking, tournament runner).

### 4.5 Studio Web (`@hathor/studio-web`)

`apps/hathor/studio-web` is a small package that defines the **V2 fighting-game
narrative content map** (`src/v2-narrative-content.ts`). It declares five
narrative surfaces (`story`, `side_story`, `krypt`, `chronicles`, `dj_story`),
the content packs and beats per surface, and the compiled-artifact plans
(dialogue banks, sequencer outlines, quest graphs, timeline data, rivalry
matrices, codex entries, localization keys) emitted to
`V2/ue/Content/Generated/...`. Its only dependency is `@hathor/lore-compiler`.
It is not an HTTP server.

---

## 5. Events

All events are delivered via `@oshun/event-bus` (Redis-backed in
`@hathor/event-publisher`). The event contracts are defined in
`@oshun/contracts` (`libs/contracts/src/events/hathor.ts`).

### 5.1 Events Published (`@hathor/event-publisher`)

`HathorEventPublisher`
(`libs/hathor/event-publisher/src/hathor-event-publisher.ts`) publishes seven
event types. It uses a singleton accessor pattern (`getHathorEventPublisher` /
factory `createHathorEventPublisher`) and a default Redis key prefix of
`oshun:events`. Publish failures are swallowed (logged, not thrown).

| Event                         | Publisher method             | Default targets     |
| ----------------------------- | ---------------------------- | ------------------- |
| `hathor.world.created`        | `publishWorldCreated`        | `isis`, `bellona`   |
| `hathor.world.published`      | `publishWorldPublished`      | `bellona`, `yemaya` |
| `hathor.element.added`        | `publishElementAdded`        | (none)              |
| `hathor.narrative.generated`  | `publishNarrativeGenerated`  | `sophia`            |
| `hathor.simulation.started`   | `publishSimulationStarted`   | (none)              |
| `hathor.simulation.completed` | `publishSimulationCompleted` | `bellona`           |
| `hathor.world.validated`      | `publishWorldValidated`      | (none)              |

The Zod payload schemas (from `@oshun/contracts`) carry richer information than
a simple ID list. Key fields per event type:

- **`HathorWorldCreatedPayload`** — `worldId`, `projectId`, `userId`, `name`,
  `type` (`WorldType`:
  `narrative | game | simulation | virtual_production | educational | experimental`),
  `description?`, and `settings` (`scale`, `timeScale`, `physics`).
- **`HathorWorldPublishedPayload`** — `worldId`, `projectId`, `userId`,
  `version`, `previousVersion?`, `statistics` (location/character/item/rule
  counts, total assets and bytes), `exportFormats`, `validationPassed`,
  `publishedAt`.
- **`HathorElementAddedPayload`** — `elementId`, `worldId`, `projectId`,
  `elementType` (`location | character | item | event | rule | relationship`),
  `name`, `parentElementId?`, `attributes?`.
- **`HathorNarrativeGeneratedPayload`** — `narrativeId`, `worldId`, `projectId`,
  `userId`, `type` (`backstory | plot | dialogue | description | lore`),
  `title`, `characterCount`, `wordCount`, `referencedElements`,
  `coherenceScore?`, `generationTimeMs`.
- **`HathorSimulationStartedPayload`** / **`HathorSimulationCompletedPayload`**
  — `simulationId`, `worldId`, `projectId`, `type`
  (`physics | social | economic | ecological | narrative`), simulation
  parameters/results, artifacts, duration, success.
- **`HathorWorldValidatedPayload`** — `worldId`, `projectId`, `validationId`,
  `passed`, `checks[]` (each with `name`, `category`, `passed`, `severity`,
  `message`, `affectedElements?`), `score` (0–100), `durationMs`.

### 5.2 Events Consumed (`@hathor/event-handlers`)

`setupHathorEventHandlers` (`libs/hathor/event-handlers/src/index.ts`)
subscribes to four upstream events via `@oshun/event-bus`. Subscriptions use
consumer-group support, graceful shutdown, and a `getHandlerRegistrations()`
helper that assigns per-event concurrency so a burst from one source does not
block the others.

| Event                      | Handler file                           | Concurrency |
| -------------------------- | -------------------------------------- | ----------- |
| `sophia.document.ingested` | `handlers/sophia-document-ingested.ts` | 5           |
| `isis.asset.generated`     | `handlers/isis-asset-generated.ts`     | 10          |
| `yemaya.project.created`   | `handlers/yemaya-project-created.ts`   | 3           |
| `yemaya.character.created` | `handlers/yemaya-character-created.ts` | 10          |

Handlers receive a `HathorHandlerContext` with repositories for worlds,
suggestions, document references, asset links, character visual attributes,
character summaries, relationship suggestions, character arcs, and a job-queue
client (`event-handlers/src/types.ts`).

---

## 6. Configuration and Environment Variables

The following environment variables must be set for a full Hathor deployment.
Port defaults are hard-coded in each service and overridden by `PORT` (Veilborn
services) or `DEFAULT_APP_CONFIG` (simulation worker).

```bash
# Database (Prisma)
HATHOR_DATABASE_URL=postgresql://...

# Event bus (Redis)
REDIS_URL=redis://localhost:6379
HATHOR_EVENTS_ENABLED=true     # set to "false" to disable publishing

# Service ports (defaults)
# world-api          3001
# narrative-api      3002
# simulation-worker  3004
# veilborn-core      8080 (PORT)
# veilborn-strategy  8080 (PORT)
```

`@hathor/event-publisher` reads `REDIS_URL` and `HATHOR_EVENTS_ENABLED`. The
simulation worker's default port (3004) is hard-coded in `DEFAULT_APP_CONFIG`.

---

## 7. Integration Points

### 7.1 Sophia Integration (`@hathor/sophia-integration`)

Sophia is the only domain that Hathor calls synchronously (via direct API call
rather than events). This is because citation lookup and lore fact-checking are
interactive operations that require an immediate response. The three services in
this library each address a different aspect of the Sophia integration:

| Service                    | File                    | Description                                             |
| -------------------------- | ----------------------- | ------------------------------------------------------- |
| `CitationService`          | `citation-service.ts`   | Link world lore to Sophia research sources              |
| `ResearchGroundingService` | `research-grounding.ts` | Generate world content grounded in researched knowledge |
| `LoreValidationService`    | `lore-validation.ts`    | Fact-check world lore against Sophia's research corpus  |

### 7.2 Bellona Consumption of Hathor Artifacts

The Bellona-facing compiler and consumer are implemented in the **Bellona**
domain, not in Hathor. This boundary exists because Bellona owns knowledge of
engine project formats — Hathor produces engine-neutral compiled artifacts and
Bellona converts them to engine-native form. The relevant Bellona files are
`libs/bellona/integration/src/hathor/lore-compiler.ts` and
`libs/bellona/integration/src/hathor/artifact-consumer.ts`. When
`hathor.world.published` is emitted, Bellona consumes it to produce
engine-native artifacts. Hathor's own `@hathor/lore-compiler` produces the
upstream compiled artifacts (see § 8).

### 7.3 Shared Oshun Libraries Used

| Library            | Usage                                       |
| ------------------ | ------------------------------------------- |
| `@oshun/event-bus` | Event publishing and subscription           |
| `@oshun/contracts` | Hathor event schemas and `HathorEventTypes` |
| `@oshun/logging`   | Structured logging                          |

---

## 8. Library Catalog

This section documents the implementation details of each Hathor library — the
files that exist, the entry points, and the key types and functions exported.

### 8.1 `@hathor/lore-compiler`

Compiles narrative content into engine-ready and film-ready artifacts
(`libs/hathor/lore-compiler/src`). Three compilers are exposed via factory
functions:

- **Quest Compiler** (`engine/quest-compiler.ts`, `createQuestCompiler`) —
  compiles quests for target engines. `TargetEngine` is
  `unreal | unity | godot | blender | custom`; `EngineArtifactFormat` is
  `json | yaml | xml | binary | sqlite`.
- **Screenplay Compiler** (`film/screenplay-compiler.ts`,
  `createScreenplayCompiler`) — compiles a story into a `CompiledScreenplay` and
  exports it. `FilmArtifactFormat` is
  `fountain | fdx | pdf | html | json | avid_log`. Beats use the 15-beat
  `FilmBeatType` set (Save-the-Cat structure).
- **Bellona Package Builder** (`bellona/package-builder.ts`,
  `BellonaPackageBuilder`) — assembles a `BellonaPackage` (manifest, world,
  quests, characters, locations, items, dialogues, optional screenplay, assets,
  localization).

Key compiled types: `CompiledQuest`, `CompiledObjective`, `QuestPrerequisite`,
`QuestReward`, `DialogueTree`/`DialogueNode`, `CompiledScreenplay`,
`CompiledBeat`, `CompiledScene`, `CompilationResult<T>`.

### 8.2 `@hathor/validation`

Four lore validators plus a combined runner (`libs/hathor/validation/src`):
`createTimelineValidator`, `createCausalityValidator`,
`createTaxonomyValidator`, `createContradictionDetector`, and
`createLoreValidator()` which runs all four. `LoreValidator.validate()` accepts
events, eras, timeline, characters, and locations, respects `failFast` and
`maxIssuesPerCategory`, and returns a `LoreValidationResult` with per-validator
results, `allIssues`, and a `summary` (`errorCount`, `warningCount`,
`infoCount`, `validatorsRun`, `duration`). The validation lib's
`ValidationSeverity` has three values: `error | warning | info`.

### 8.3 `@hathor/narrative`

Engine-agnostic narrative systems (`libs/hathor/narrative/src`): `quest/` (quest
engine), `dialogue/` (dialogue engine), `story-graph/` (typed-node graph with
traversal), `journal/` (journal/codex system), `branching/` (branching
utilities), and `export/` — exporters for Ink (`ink-exporter.ts`), Yarn Spinner
(`yarn-exporter.ts`), and JSON (`json-exporter.ts`) selected by
`exporter-factory.ts`.

### 8.4 `@hathor/simulation`

The largest domain library (`libs/hathor/simulation/src`): four simulation
managers — `economy/manager.ts`, `politics/manager.ts`, `culture/manager.ts`,
`scenario/manager.ts` — each with a `providers.ts` and typed inputs; an NPC
behavior-tree system (`npc/behavior-tree.ts`); state persistence
(`state/state-store.ts`, `state/postgres-storage.ts`, `state/redis-cache.ts`);
and a physics engine (`physics/engine.ts`, `physics/math.ts`). There is **no**
`ecology` module.

### 8.5 `@hathor/llm-npc`

LLM-powered NPC system (`libs/hathor/llm-npc/src`): `brain/` — personality
(`personality-manager.ts`), emotional state, memory, world awareness;
`dialogue/` — `dialogue-generator.ts`, `safety-filter.ts`; `behavior/` —
`behavior-controller.ts`, `npc-conversation.ts`; `platforms/` — `llm-client.ts`,
`nvidia-ace-client.ts`, `inworld-client.ts`, `convai-client.ts`; a
`provider-chain.ts` with circuit-breaker failover; and an `advanced/npc-sota.ts`
module.

### 8.6 `@hathor/theory`

Three theory subsystems (`libs/hathor/theory/src`): `mda/` (MDA framework),
`narrative/` (narrative theory — fabula/syuzhet, beat sheets, structure
templates), `cinematography/` (shot/composition/lighting/color planning). Each
has a `manager.ts`, `types.ts`, and `constants.ts`.

### 8.7 `@hathor/pre-production`

Pre-production tooling (`libs/hathor/pre-production/src`):

- `scriptwriting/` — chronicle document parser (`parser.ts`), templates, and an
  exporter.
- `storyboard/` — `storyboard-manager.ts` with frames, sequences, layers, camera
  specs, transitions, character placement, and an exporter.
- `planning/` — a `project-manager.ts` for worldbuilding project management
  (phases, milestones, tasks, dependencies, `checklists.ts`), **plus** a very
  large library of virtual-production / on-set systems (~240 `*-system.ts` and
  `drone-*` modules covering drone choreography and safety, virtual sets,
  LED-wall ICVFX, relighting, motion capture, switching, color, and continuity).
  Each module has a matching `.test.ts`.

### 8.8 `@hathor/domain-models`

Engine-agnostic worldbuilding domain models (`libs/hathor/domain-models/src`).
Nine subdomains, each with `types.ts`, `constants.ts`, `factories.ts`,
`utils.ts`, `validators.ts`: `common/`, `faction/`, `economy/`, `law/`,
`culture/`, `geography/`, `timeline/`, `character/`, `location/`. This library
holds the pure world type system. The **Git-like versioning code (versions,
branches, merge requests) lives in `@hathor/world-api`'s services**, not here.

### 8.9 `@hathor/client`

TypeScript SDK for the Hathor HTTP APIs (`libs/hathor/client/src`).
`HathorClient` / `createClient` / `createClientFromEnv` expose three resources:
`WorldResource` (`api/world.ts`), `NarrativeResource` (`api/narrative.ts`),
`SimulationResource` (`api/simulation.ts`). Branded ID types: `WorldId`,
`VersionId`, `BranchId`, `EntityId`, `StoryGraphId`, `QuestId`, `DialogueId`,
`JobId`. Helpers (`helpers/`): world-state (`getWorldStateSnapshot`,
`buildRelationshipMap`, `findConnectedEntities`, `getTimelineRange`,
`getEntityTimeline`, `analyzeFaction`, `compareVersions`, `searchWorld`,
`findEntities`); quest generation (`generateQuestFromTemplate`,
`generateQuestChain`, `analyzeQuest`, `getQuestRecommendations`,
`QUEST_TEMPLATES`); lore validation (`validateWorldLore`, `validateEntity`,
`checkLoreImpact`, `getValidationRules`).

### 8.10 Façade Libraries

Four thin façade libraries adapt Hathor domain types for cross-domain (CGI /
production) consumption. The façade pattern keeps the CGI pipeline's interface
stable — consumers see a minimal, stable packet type rather than the full
internal domain model. Each façade exports metadata, a packet builder, a
validator, and a serializer:

- **`@hathor/characters`** — CGI character-definition import facade. Maps
  `Character` records into a `HathorCharactersCgiPacket` (appearance, costume
  designs, visual references); `createHathorCharactersCgiPacket`,
  `validateHathorCharactersCgiPacket`. Depends on `@hathor/domain-models` and
  `@hathor/pre-production`.
- **`@hathor/world`** — CGI scene-definition import facade. Depends on
  `@hathor/domain-models`.
- **`@hathor/timeline`** — narrative scene-order facade (story-order vs.
  shoot-order reconciliation, causal-dependency checking, world-state
  continuity). Depends on `@hathor/domain-models`.
- **`@hathor/quests`** — scene-dependency / story-arc capture facade. Depends on
  `@hathor/narrative`.

---

## 9. Common Error Format

All three Hono-based Hathor APIs return errors in a consistent JSON envelope.
Understanding this shape is important when building integrations that need to
distinguish error types programmatically.

```json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "World not found: wld_xyz"
  }
}
```

Common codes seen in the route handlers: `NOT_FOUND`, `VALIDATION_ERROR`,
`CREATE_FAILED`, `CLONE_FAILED`, `QUERY_FAILED`, `MERGE_FAILED`,
`CONFLICTS_EXIST`, `INVALID_TYPE`, `QUEUE_FULL`, `INTERNAL_ERROR`. Zod
validation failures surface via the global `onError` handler with a `details`
array. The Veilborn Fastify services use a different envelope: a top-level
`{ success, data?, error?, timestamp }` shape with `error.code` and
`error.message`.

### SDK Error Hierarchy (`@hathor/client`)

The SDK maps HTTP error responses to a typed exception hierarchy so callers can
catch specific error conditions without inspecting response codes manually.
`HathorError` is the base, with subclasses `NetworkError`, `TimeoutError`,
`AbortError`, `NotFoundError`, `ValidationError`, `ConfigError`,
`UnauthorizedError`, `ForbiddenError`, `ConflictError`, `RateLimitError`.
`createErrorFromResponse` maps HTTP responses to the right subclass.

---

## 10. V2 Fighting-Game Narrative Contract (planned)

The V2 fighting-game project (`V2/`) is a named Hathor consumer for Story Mode,
per-fighter Side Stories, the Krypt, Chronicles of the Sword, Def Jam Story, and
the rivalry matrix. The integration contract is documented in
`V2/V2_ARCHITECTURE.md`, `V2/V2_DEPENDENCIES.md`, and
`V2/docs/integration/hathor-narrative-contract.md`.

**Implemented today:** `apps/hathor/studio-web/src/v2-narrative-content.ts`
defines the V2 narrative content map (five surfaces, content packs, beats, and
compiled-artifact plans) and a coverage assertion. It targets
`@hathor/lore-compiler` and the `unreal` engine, JSON output,
compiled-artifact-only consumption.

**Planned (not yet in code).** The following are described in the V2 contract
docs as reciprocal Hathor backlog and are **not** implemented in `libs/hathor/*`
today. They are listed here because they are an explicit design contract, not
invented detail:

1. Extend `@hathor/domain-models` / `@hathor/narrative` with fighting-game
   entity shapes: `FighterTimeline`, `RivalryMatrix`, `LadderEndingGraph`,
   `SignatureStageContext` (and related `FactionMatrix`,
   `SideStorySequencerOutline`). The `LadderEndingGraph` is the per-fighter
   arcade/ladder ending branch tree that the lore compiler emits to
   `V2/ue/Content/Generated/Quests`; per the contract, every fighter has at
   least one ladder ending graph and each `arcadeEndingIds` entry must resolve
   to one.
2. Extend `@hathor/llm-npc` with combat-bark and fighting-game personality
   presets, plus open-world NPC hooks (`npc.encounter.start`,
   `npc.dialogue.request`, `npc.behavior.tick`, `npc.combat.barks`); NPC outputs
   cached off-rollback at `V2/ue/Content/Generated/NPCDialogue/<hash>`.
3. Extend `@hathor/lore-compiler` with the V2 emission contract (dialogue banks,
   Sequencer outlines, quest graphs, fighter-timeline and rivalry-matrix UE data
   assets, codex entries, localization keys).
4. Extend `@hathor/validation` with rivalry-consistency and Themis NIL / venue
   rights gates.
5. Add a fighting-game scenario-generation preset to `@hathor/simulation`.

Planned V2-related events on the bus (per the contract doc):
`hathor.dialogue.ready`, `hathor.rivalry.updated`, `hathor.timeline.branched`.
These are **not** in `@oshun/contracts` today — only the seven events in § 5.1
are implemented.

---

## 11. Acceptance Criteria

A Hathor deployment is correct when all of the following hold:

1. `@hathor/database` migrates cleanly and exposes the 20 models / 19 enums in §
   1–2; `getHathorClient` connects and disconnects.
2. `world-api` (3001) serves the world, branch, version, merge, and query routes
   in § 4.1 behind the 100-req/min rate limit and the `X-User-ID` mutation
   check; `/ready` reports both the world and version services.
3. `narrative-api` (3002) serves the story-graph, quest, dialogue (including
   runtime sessions), and validation routes in § 4.2.
4. `simulation-worker` (3004) accepts, runs, lists, cancels, and retries
   simulation jobs of all five types and reports worker health and queue stats.
5. `@hathor/event-publisher` publishes the seven `hathor.*` events with the
   contract payloads in § 5.1; `@hathor/event-handlers` subscribes to the four
   upstream events in § 5.2.
6. `@hathor/lore-compiler` produces quest, screenplay, and Bellona-package
   artifacts; `@hathor/validation`'s `createLoreValidator()` runs all four
   validators.
7. The Veilborn services build and serve their game and strategy APIs (§ 4.4)
   with passing integration tests.
8. `@hathor/client` round-trips against all three HTTP APIs with its branded ID
   types and error hierarchy.
