# Uzume Domain — Technical Specifications

> Named after Ame-no-Uzume-no-Mikoto — the Shinto goddess who performed the
> first show in recorded mythology — Uzume is the Oshun platform for everything
> that happens between "the doors open" and "the last cable is struck."

This document specifies the implemented Uzume domain as it exists in
`libs/uzume/`. Uzume is a library-only domain: there are no `apps/uzume/` or
`services/uzume/` directories. It consists of 21 TypeScript libraries plus one
Rust/WASM workspace (`uzume-protocol-engines`).

This is the authoritative low-level reference for engineers working on Uzume.
For a conceptual introduction to what each subsystem does and why it exists, see
`features.md`. For the layered architecture and design patterns, see
`architecture.md`. Every type, enum, schema, table, event, constant, and
protocol described below is traceable to source files under `libs/uzume/`.

---

## 1. Library Inventory

All 22 packages exist under `libs/uzume/`. The table below is the definitive
package manifest — use it to find the path and npm name for any library. Note
that `@uzume/protocol-engines` is the npm wrapper for the Rust workspace; the
native module published separately as `@uzume/protocol-native`.

| Package                   | Path                          | Type       | `package.json` description                                            |
| ------------------------- | ----------------------------- | ---------- | --------------------------------------------------------------------- |
| `@uzume/core`             | `libs/uzume/core`             | TypeScript | Core domain primitives, configuration contracts, and shared utilities |
| `@uzume/nexus`            | `libs/uzume/nexus`            | TypeScript | Show control protocol bridging and subsystem integration              |
| `@uzume/protocol-engines` | `libs/uzume/protocol-engines` | Rust/WASM  | Rust protocol engine workspace with WASM control-surface targets      |
| `@uzume/lumina`           | `libs/uzume/lumina`           | TypeScript | Lighting design and show control orchestration                        |
| `@uzume/sonos`            | `libs/uzume/sonos`            | TypeScript | Audio engineering                                                     |
| `@uzume/prism`            | `libs/uzume/prism`            | TypeScript | Video, projection, and LED content systems                            |
| `@uzume/kinesis`          | `libs/uzume/kinesis`          | TypeScript | Rigging, flying, automation, robotics, motion control                 |
| `@uzume/pyra`             | `libs/uzume/pyra`             | TypeScript | Special effects and atmospherics                                      |
| `@uzume/scena`            | `libs/uzume/scena`            | TypeScript | Scenic design, set construction, props                                |
| `@uzume/koru`             | `libs/uzume/koru`             | TypeScript | Stage management and show calling                                     |
| `@uzume/atlas`            | `libs/uzume/atlas`            | TypeScript | Production and tour management                                        |
| `@uzume/aegis`            | `libs/uzume/aegis`            | TypeScript | Safety and compliance                                                 |
| `@uzume/aurora`           | `libs/uzume/aurora`           | TypeScript | Extended reality and virtual production                               |
| `@uzume/echo`             | `libs/uzume/echo`             | TypeScript | Audience engagement and accessibility                                 |
| `@uzume/vestis`           | `libs/uzume/vestis`           | TypeScript | Costume and wardrobe management                                       |
| `@uzume/previz`           | `libs/uzume/previz`           | TypeScript | Pre-visualization                                                     |
| `@uzume/chronos`          | `libs/uzume/chronos`          | TypeScript | Scheduling                                                            |
| `@uzume/tesla`            | `libs/uzume/tesla`            | TypeScript | Power distribution and electrical management                          |
| `@uzume/hermes`           | `libs/uzume/hermes`           | TypeScript | Crew communication                                                    |
| `@uzume/forge`            | `libs/uzume/forge`            | TypeScript | Network infrastructure                                                |
| `@uzume/muse`             | `libs/uzume/muse`             | TypeScript | AI assistance                                                         |
| `@uzume/broadcast`        | `libs/uzume/broadcast`        | TypeScript | Broadcast switching, streaming, replay, and simulcast control         |

The Rust workspace npm wrapper is published as `@uzume/protocol-engines`; its
native module crate (`uzume-node-bridge`) builds the npm package
`@uzume/protocol-native`.

Each subsystem library exposes a `UzumeLibraryMetadata` constant (e.g.
`UZUME_CORE_METADATA`, `UZUME_LUMINA_METADATA`, `UZUME_BROADCAST_METADATA`) and
a corresponding `getUzume<Subsystem>Metadata()` accessor returning
`{ id, subsystem, description }`.

### 1.1 Subsystem Engine Counts

Each subsystem library is a flat collection of independent `*-engine.ts` modules
re-exported from `src/index.ts`. Each engine is a self-contained domain-specific
implementation — not a generic service. The counts below are source modules,
excluding `index.ts` and test files. They provide a useful signal for code
review: a library where the count does not match likely has an engine missing
from `src/index.ts`.

| Library            | Engine modules |
| ------------------ | -------------- |
| `@uzume/broadcast` | 10             |
| `@uzume/prism`     | 15             |
| `@uzume/aurora`    | 25             |
| `@uzume/previz`    | 15             |
| `@uzume/sonos`     | 25             |
| `@uzume/echo`      | 20             |
| `@uzume/koru`      | 15             |
| `@uzume/scena`     | 10             |
| `@uzume/vestis`    | 10             |
| `@uzume/lumina`    | 20             |
| `@uzume/kinesis`   | 30             |
| `@uzume/pyra`      | 18             |
| `@uzume/tesla`     | 10             |
| `@uzume/forge`     | 10             |
| `@uzume/hermes`    | 10             |
| `@uzume/atlas`     | 24             |
| `@uzume/chronos`   | 10             |
| `@uzume/aegis`     | 30             |
| `@uzume/muse`      | 15             |

`@uzume/core` and `@uzume/nexus` follow a different shape (core: typed modules;
nexus: a metadata-only package — see §11).

---

## 2. Technology Stack

The table below records every technology choice that affects how the domain is
built, tested, and deployed. The key design decision is the TypeScript/Rust
split: TypeScript for domain logic and coordination, Rust for anything that
requires deterministic timing or high-throughput packet processing. The Rust
workspace release profile (`lto = true`, `codegen-units = 1`, `opt-level = "s"`)
is tuned for binary size and performance on embedded and server targets, not
compilation speed.

| Layer                 | Technology                                          |
| --------------------- | --------------------------------------------------- |
| Primary language      | TypeScript (ESM, `"type": "module"`)                |
| Protocol language     | Rust (edition 2021, workspace `version` 0.1.0)      |
| TypeScript build      | `tsx` / TypeScript compiler (no Nx executor pinned) |
| Rust build            | Cargo workspace; `wasm-pack` for the WASM crate     |
| Database              | PostgreSQL via Drizzle ORM (`drizzle-orm` catalog)  |
| Event bus             | NATS (`nats` npm package)                           |
| Validation            | Zod                                                 |
| Testing (TypeScript)  | Vitest                                              |
| Testing (Rust)        | `cargo test`                                        |
| Node.js native bridge | napi-rs (`napi` 2.16, `napi8` feature)              |
| Browser bridge        | wasm-bindgen 0.2 + `serde-wasm-bindgen`             |

`@uzume/core` depends on the shared packages `@oshun/database`, `@oshun/errors`,
`@oshun/logging`, plus `nats`, `drizzle-orm`, and `zod`. Subsystem libraries
declare `@uzume/core` as a `peerDependency` (`workspace:*`).

The Rust workspace release profile uses `lto = true`, `codegen-units = 1`,
`opt-level = "s"`. Shared crate dependencies: `serde`, `serde_json`,
`thiserror`, `wasm-bindgen`, `js-sys`, `serde-wasm-bindgen`, `tokio` (full
features), `bytes`, `napi`/`napi-derive`/`napi-build`, `midir`.

---

## 3. Core Domain Types (`@uzume/core/types`)

The core domain types define the shared vocabulary for the entire Uzume surface:
what a show is, what a cue is, what a venue is, and how production departments
are represented. These types are the contracts that subsystem libraries build on
top of, and that consuming applications receive back.

Domain types are exported through the `@uzume/core/types` subpath
(`src/types/index.ts`). They are **not** part of the package root export — the
root `src/index.ts` exports config, database, errors, event-bus, logging, and
utils only. This separation means a subsystem that only needs types does not
pull in the NATS client and database ORM.

`src/types/index.ts` re-exports: `department`, `cue`, `protocol-audio`,
`protocol-automation-safety`, `protocol-control`, `protocol-dmx`,
`protocol-equipment`, `protocol-gdtf`, `protocol-maintenance`, `protocol-mvr`,
`protocol-patched-fixture`, `show`, `timeline`, `venue`.

Every interface below has a matching Zod schema and a `parse*` helper. The
`parse*` helpers throw with a descriptive message if validation fails — they are
the recommended entry points for untrusted external data.

### 3.1 Department (`department.ts`)

The `Department` enum is the organizational backbone of Uzume: it classifies who
owns a given cue, resource, budget line, or alert. Every cue list belongs to a
department, every budget allocation belongs to a department, and the event bus
routes alerts to department dashboards. The `DepartmentReadinessState` enum
tracks the real-time operational state of a department during a show — used by
the stage manager's cue display to show which departments are ready to receive a
"Go."

`Department` enum — 18 values:

`lighting`, `audio`, `video`, `automation`, `effects`, `scenic`,
`stage_management`, `production`, `safety`, `xr`, `audience`, `wardrobe`,
`previz`, `scheduling`, `electrical`, `communication`, `network`, `ai`.

`DepartmentReadinessState` enum — 7 values: `not_ready`, `prepping`, `standby`,
`ready`, `active`, `blocked`, `offline`.

`DepartmentAlertSeverity` union: `info | warning | critical | safety-critical`.

| Interface                       | Fields                                                                                                   |
| ------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `DepartmentContact`             | `fullName`, `email?`, `phone?`                                                                           |
| `DepartmentBudgetAllocation`    | `currency` (3-char), `allocatedAmount`, `committedAmount`, `actualAmount`, `contingencyPercent?` (0–100) |
| `DepartmentEquipmentAllocation` | `equipmentCategory`, `allocatedCount`, `reservedCount?`, `notes?`                                        |
| `DepartmentConfig`              | `department`, `headOfDepartment`, `crewCount`, `budgetAllocation`, `equipmentAllocation[]`               |
| `DepartmentCueReference`        | `cueId`, `cueNumber`, `cueLabel?`                                                                        |
| `DepartmentAlert`               | `id`, `message`, `severity`, `createdAt` (ISO datetime)                                                  |
| `DepartmentStatus`              | `department`, `currentCue?`, `nextCue?`, `readinessState`, `activeAlerts[]`, `updatedAt`                 |

Validation: `DepartmentConfigSchema` rejects
`committedAmount > allocatedAmount`. Helpers: `parseDepartmentConfig`,
`parseDepartmentStatus`, `isDepartment`, `isDepartmentReady`,
`hasBlockingDepartmentAlerts`.

### 3.2 Cue (`cue.ts`)

The `Cue` is the fundamental unit of show execution — an instruction to a
department to change something at a specific time or trigger. Cues have a
trigger mechanism (`CueTriggerType`), a lifecycle state (`CueState`), and a map
of parameter changes to apply to target devices. The `CueList` groups cues into
a sequence, tracks which cue is active, and enforces consistency between
`cueCount` and the actual list length.

`CueTriggerType` enum — 6 values: `manual`, `follow`, `auto_follow`, `timecode`,
`midi`, `osc`.

`CueState` enum — 6 values: `standby`, `go`, `running`, `complete`, `skipped`,
`disabled`.

`CueParameterValue` is a recursive union:
`string | number | boolean | null | CueParameterMap | CueParameterValue[]`;
`CueParameterMap` is `Record<string, CueParameterValue>`.

| `Cue` field        | Type                | Notes                                                          |
| ------------------ | ------------------- | -------------------------------------------------------------- |
| `id`               | `string` (UUID)     |                                                                |
| `number`           | `string`            | regex `^([A-Za-z]{1,6}\s+)?\d+(\.\d+)?$` — e.g. `Q 5`, `Q 5.5` |
| `label`            | `string`            | non-empty                                                      |
| `department`       | `Department`        |                                                                |
| `triggerType`      | `CueTriggerType`    |                                                                |
| `waitTimeMs`       | `number`            | non-negative integer                                           |
| `durationMs`       | `number?`           | non-negative integer                                           |
| `fadeTimeMs`       | `number?`           | non-negative integer                                           |
| `targetDeviceIds`  | `readonly string[]` | min length 1                                                   |
| `parameterChanges` | `CueParameterMap`   |                                                                |
| `state`            | `CueState`          |                                                                |
| `isEnabled`        | `boolean`           |                                                                |

`CueList` fields: `id` (UUID), `department`, `sequenceNumber`, `label`,
`cueCount`, `activeCueIndex` (≥ −1), `cues[]`.

Validation rules:

- `Cue`: `isEnabled` must be `false` when `state === disabled`.
- `CueList`: `cueCount` must equal `cues.length`; `activeCueIndex` must be `-1`
  for an empty list and otherwise point to an existing cue.

Helpers: `parseCue`, `parseCueList`, `cueNumberToSortableValue`,
`sortCuesByNumber`, `isCueReadyToExecute`, `getNextCue`.

### 3.3 Show (`show.ts`)

The `Show` type is the root aggregate for a production in Uzume: it carries the
show's identity, creative team, production dates, venue assignments, and
configuration (which subsystems are enabled, how cues are numbered, what
timecode format is used). The `ShowStatus` lifecycle mirrors the real phases of
a production from planning through archiving.

`ShowType` enum — 8 values: `theatre`, `concert`, `festival`, `corporate`,
`ceremony`, `broadcast`, `immersive`, `touring`.

`ShowStatus` enum — 8 values: `planning`, `pre-production`, `tech`, `dress`,
`preview`, `running`, `closed`, `archived`.

`CueNumberingMode` enum — 4 values: `numeric`, `decimal`, `departmental`,
`custom`.

`SmpteFrameRate` (show.ts): literal union `24 | 25 | 29.97 | 30`.

| Interface                | Key fields                                                                                                                                                      |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ShowPersonContact`      | `fullName`, `email?`, `phone?`, `organization?`                                                                                                                 |
| `ShowDesignerAssignment` | `person`, `assistantDesigners?`, `notes?`                                                                                                                       |
| `ShowProductionDates`    | `preProductionStart` (required); `techStart?`, `dressStart?`, `previewStart?`, `openingNight?`, `runningStart?`, `closingNight?`, `loadInStart?`, `loadOutEnd?` |
| `ShowVenueAssignment`    | `venueId`, `venueName`, `assignmentRole` (`primary` \| `rehearsal` \| `tour-stop` \| `backup`), `startDate`, `endDate?`                                         |
| `ShowMetadata`           | `title`, `producer`, `director`, `designers` (map by department), `dates`, `venueAssignments[]` (min 1)                                                         |
| `CueNumberingScheme`     | `mode`, `prefix?`, `separator` (`'' \| ' ' \| '-' \| '.'`), `startAt`, `increment` (>0), `decimalPlaces` (0–3), `departmentPrefixes?`, `customPattern?`         |
| `ShowTimecodeFormat`     | `frameRate` (24/25/29.97/30), `dropFrame`, `displayFormat` (`hh:mm:ss:ff` \| `hh:mm:ss;ff`), `startTimecode`                                                    |
| `ShowConfiguration`      | `enabledSubsystems[]`, `departmentList[]`, `cueNumberingScheme`, `timecodeFormat`                                                                               |
| `Show`                   | `id` (UUID), `code`, `type`, `status`, `metadata`, `configuration`, `createdAt`, `updatedAt`                                                                    |

Validation: every `departmentList` entry must appear in `enabledSubsystems`;
`dropFrame` is valid only with `frameRate` 29.97 or 30.

Helpers: `parseShow`, `isShowType`, `normalizeShowConfiguration` (de-dupes
department lists), `isShowActive` (true for `tech`/`dress`/`preview`/`running`).

### 3.4 Timeline (`timeline.ts`)

The `Timeline` type maps show events to SMPTE timecode frame positions. This is
what allows lighting cues, video triggers, and automation moves to be locked to
an audio or video timecode source — everything that happens at a given timecode
position is grouped into a `TimelineEvent`. Markers and regions provide
editorial annotations that do not trigger execution. The timecode arithmetic
helpers implement the SMPTE drop-frame correction that affects 29.97 fps
timecode, which is the standard for NTSC broadcast.

`TimelineFrameRate`: `24 | 25 | 29.97 | 30`.

`TimelineEventType` union:
`cue_trigger | cue_complete | manual_note | automation_checkpoint | safety_checkpoint`.

| Interface              | Fields                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------- |
| `SmpteTimecode`        | `hours`, `minutes` (0–59), `seconds` (0–59), `frames`, `frameRate`, `dropFrame`         |
| `TimelineCueReference` | `cueListId`, `cueId`, `cueNumber`, `cueLabel?`                                          |
| `TimelineEvent`        | `id`, `timestampFrames`, `department`, `cueReference`, `eventType`, `note?`             |
| `TimelineMarker`       | `id`, `timestampFrames`, `label`, `color` (hex `#RRGGBB`)                               |
| `TimelineRegion`       | `id`, `startFrame`, `endFrame`, `label`, `department?`, `description?`                  |
| `Timeline`             | `id`, `showId`, `label`, `frameRate`, `dropFrame`, `events[]`, `markers[]`, `regions[]` |

Validation: `frames` must be `≤ round(frameRate) − 1`; `dropFrame` valid only
for 29.97/30; `TimelineRegion.endFrame ≥ startFrame`.

Timecode arithmetic helpers (all SMPTE-aware, drop-frame-correct for 30 fps):
`parseSmpteTimecode`, `formatSmpteTimecode`, `timecodeToFrames`,
`framesToTimecode`, `convertFramesBetweenRates`, `convertTimecodeRate`,
`addTimecodes`, `subtractTimecodes`, `parseTimeline`, `normalizeTimeline`.

### 3.5 Venue (`venue.ts`)

The `Venue` type stores the physical and technical specification of a
performance space. The technical sub-types (`HouseLightingSystem`,
`HouseAudioSystem`, `VenueNetworkInfrastructure`) record what is permanently
installed at the venue so that a touring production knows what it needs to bring
versus what it can use in-house. The access sub-types (`DockAccessHours`,
`ElevatorCapacity`, `HallwayDimensions`) capture the physical logistics of
getting equipment in and out.

`VenueType` enum — 8 values: `proscenium`, `thrust`, `arena`, `traverse`,
`black_box`, `outdoor`, `stadium`, `custom`.

| Interface                    | Fields                                                                                                                                              |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VenueDimensions`            | `widthMeters`, `depthMeters`, `heightMeters`, `stageWidthMeters?`, `stageDepthMeters?`, `stageHeightMeters?`                                        |
| `VenueRiggingPoint`          | `id`, `label`, `xMeters`, `yMeters`, `zMeters`, `safeWorkingLoadKg`                                                                                 |
| `VenuePowerCapacity`         | `location`, `phases` (`1 \| 3`), `voltage`, `amperage`, `availableKw`                                                                               |
| `LoadingDockDimensions`      | `bayCount`, `dockHeightMeters`, `widthMeters`, `depthMeters`, `rollUpDoorWidthMeters`, `rollUpDoorHeightMeters`                                     |
| `FlyTowerSpecs`              | `heightMeters`, `lineSetCount`, `counterweightSystem`                                                                                               |
| `VenueSpecs`                 | `dimensions`, `seatingCapacity`, `riggingGridHeightMeters`, `riggingPoints[]`, `powerCapacityByLocation[]`, `loadingDockDimensions`, `flyTower`     |
| `HouseLightingSystem`        | `consoleModel`, `dmxUniverses`, `dimmerCount`, `protocolSupport[]`                                                                                  |
| `HouseAudioSystem`           | `consoleModel`, `inputChannelCount`, `outputZoneCount`, `paSystemModel`, `networkProtocols[]`                                                       |
| `InstalledVideoSystem`       | `switcherModel`, `projectionSurfaceCount`, `ledProcessorModel?`, `ledResolution?`, `ndiEnabled`                                                     |
| `PatchPanelLocation`         | `id`, `name`, `locationDescription`, `availableProtocols[]`                                                                                         |
| `VenueNetworkInfrastructure` | `coreSwitchModel`, `vlanCapable`, `ptpGrandmasterAvailable`, `fiberBackbone`, `wirelessCoverage` (`none\|partial\|full`), `managedSubnets[]` (CIDR) |
| `VenueTechnical`             | `houseLightingSystem`, `houseAudioSystem`, `installedVideo`, `patchPanelLocations[]`, `networkInfrastructure`                                       |
| `DockAccessHours`            | `days[]` (weekday names), `opensAt`/`closesAt` (HH:MM 24-hour)                                                                                      |
| `ElevatorCapacity`           | `available`, `maxWeightKg`, `cabWidthMeters`, `cabDepthMeters`, `cabHeightMeters`                                                                   |
| `HallwayDimensions`          | `name`, `widthMeters`, `heightMeters`, `turnRadiusMeters`                                                                                           |
| `FloorLoadLimit`             | `areaName`, `maxLoadKgPerSquareMeter`, `notes?`                                                                                                     |
| `VenueAccess`                | `dockAccessHours`, `elevatorCapacity`, `scenicMovementHallwayDimensions[]`, `floorLoadLimitsByArea[]`                                               |
| `Venue`                      | `id` (UUID), `code`, `name`, `type`, `specs`, `technical`, `access`, `timezone`, `createdAt`, `updatedAt`                                           |

Helpers: `parseVenue`, `isVenueType`, `supportsHeavyRigging` (default min SWL
1000 kg), `getMaxFloorLoadKgPerSquareMeter`, `hasProtocolAtPatchPanel`.

---

## 4. Protocol Type Specifications (`@uzume/core/types`)

The protocol types define the low-level wire format structures that Uzume uses
to communicate with physical hardware. These are distinct from the domain types
in §3 — they represent the byte-level concepts (universes, channels, packets,
device UIDs) rather than the production-management concepts (shows, cues,
venues). Each protocol module defines constants, interfaces, validation rules,
and helpers for one or two related protocols.

### 4.1 DMX512 (`protocol-dmx.ts`)

DMX512 is the ANSI E1.11 standard for lighting control: a serial protocol that
delivers 512 channel values (each 0–255) per universe at up to 44 Hz. Art-Net
and sACN are IP transports for DMX universes — they carry the same 512-channel
payload over UDP, enabling much larger channel counts and network distances than
physical DMX cabling. RDM (Remote Device Management, ANSI E1.20) is a
bidirectional extension of DMX that allows console-to-fixture parameter queries
and commands.

Constants: `DMX_CHANNEL_COUNT = 512`, `SACN_UNIVERSE_MIN = 1`,
`SACN_UNIVERSE_MAX = 63999`.

| Interface       | Fields                                                                                               |
| --------------- | ---------------------------------------------------------------------------------------------------- |
| `DMXUniverse`   | `universe`, `sourcePriority` (0–200), `channels` (`Uint8Array`, exactly 512), `updatedAt`            |
| `DMXAddress`    | `universe` (0–63999), `channel` (1–512)                                                              |
| `DMXPatch`      | `fixtureId`, `startAddress`, `footprintSize` (1–512), `label?`                                       |
| `ArtNetConfig`  | `ipAddress` (IPv4), `subnet` (0–15), `universe`, `mode` (`broadcast \| unicast`), `targetIpAddress?` |
| `SACNConfig`    | `universe` (1–63999), `multicastGroup` (IPv4), `priority` (0–200), `cid` (UUID)                      |
| `RdmSensorData` | `sensorId`, `label`, `value`, `unit`, `state` (`normal \| warning \| alarm`)                         |
| `RDMDevice`     | `uid` (12-char hex), `manufacturer`, `model`, `dmxAddress`, `sensorData[]`, `statusMessages[]`       |

Validation: `DMXPatch` rejects footprints overrunning channel 512;
`ArtNetConfig` requires `targetIpAddress` when `mode === unicast`; `SACNConfig`
requires `multicastGroup` to equal the computed group for its universe.

Helpers: `createDMXUniverse`, `setDmxChannel` (value 0–255), `getDmxChannel`,
`computeSacnMulticastGroup` (`239.255.<hi>.<lo>`), `parseDMXPatch`,
`parseArtNetConfig`, `parseSACNConfig`, `parseRDMDevice`.

### 4.2 Control Protocols (`protocol-control.ts`)

OSC (Open Sound Control), MIDI, and SMPTE timecode are the three most common
show control protocols beyond DMX. OSC is an address-pattern-based message
protocol (similar to HTTP URLs) used widely by audio consoles, video servers,
and custom control systems. MIDI Show Control (MSC) is a MIDI extension
specifically designed for theatrical cue triggering. Timecode (LTC and MTC)
carries the master clock signal that all timecode-locked cues chase.

`OSCArgument` discriminated union — 8 variants: `int32`, `float32`, `string`,
`blob` (`Uint8Array`), `true`, `false`, `nil`, `timetag`.

`MIDIMessageType` union — 6 values: `note_on`, `note_off`, `cc`,
`program_change`, `sysex`, `msc`.

`MSCCommandFormat` union — 8 values: `lighting`, `sound`, `machinery`, `video`,
`projection`, `process_control`, `pyro`, `all`.

`MSCCommandType` union — 7 values: `go`, `stop`, `resume`, `timed_go`, `set`,
`fire`, `all_off`.

`TimecodeFrameRate` union — 5 values: `24`, `25`, `29.97df`, `29.97ndf`, `30`
(string literals). `TimecodeType` union: `ltc | mtc`.

| Interface     | Fields                                                                     |
| ------------- | -------------------------------------------------------------------------- |
| `OSCMessage`  | `addressPattern` (must start with `/`), `arguments[]`                      |
| `MSCCommand`  | `commandFormat`, `commandType`, `cueNumber?`, `cueList?`, `cuePath?`       |
| `MIDIMessage` | `type`, `channel` (1–16), `dataBytes[]`, `mscCommand?`                     |
| `Timecode`    | `hours`, `minutes` (0–59), `seconds` (0–59), `frames`, `frameRate`, `type` |

Validation: MSC non-`all_off` commands require `cueNumber`;
`note_on`/`note_off`/ `cc` require exactly 2 data bytes; `program_change`
exactly 1; `sysex` must be `0xF0 … 0xF7`; `msc` type requires an `mscCommand`.
`Timecode` `frames` capped per frame rate (23/24/29/29/29).

Helpers: `parseOSCMessage`, `parseMIDIMessage`, `parseMSCCommand`,
`parseTimecode`, `parseTimecodeString`, `formatTimecode`, `midiStatusByte`.

### 4.3 Network Audio (`protocol-audio.ts`)

Dante is Audinate's proprietary network audio protocol — the most widely
deployed standard in professional live sound — while AES67 is the
interoperability layer that allows Dante devices to talk to competing systems
(Q-SYS, RAVENNA, Livewire). Both route audio over standard Ethernet but require
PTP (Precision Time Protocol) synchronization to within a few microseconds to
avoid audible glitches. `WirelessMicrophone` represents the RF and battery
telemetry that wireless systems report back to the mixing position.

Constants: `DANTE_MAX_CHANNELS = 1024`, `AES67_PTP_DOMAIN_MIN = 0`,
`AES67_PTP_DOMAIN_MAX = 127`.

`AudioSampleRate`: `44100 | 48000 | 88200 | 96000 | 176400 | 192000`.
`AudioBitDepth`: `16 | 24 | 32`. `DanteLatencySettingMs`:
`0.25 | 0.5 | 1 | 2 | 5 | 10`.

| Interface            | Fields                                                                                                                      |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `DanteRoute`         | `sourceDevice`, `sourceChannel`, `destinationDevice`, `destinationChannel`, `isMulticast`                                   |
| `DanteDevice`        | `name`, `rxChannelCount`, `txChannelCount`, `sampleRate`, `latencySettingMs`, `subscriptions[]`                             |
| `AES67Stream`        | `multicastAddress`, `sampleRate`, `bitDepth`, `channelCount` (1–128), `ptpDomain` (0–127)                                   |
| `WirelessMicrophone` | `frequencyMHz` (30–3000), `powerDbm`, `batteryPercentage`, `rfSignalStrength`, `audioLevel`, `deviceModel`, `transmitterId` |
| `SpeakerPosition`    | `coordinates`, `orientation` (yaw/pitch/roll degrees), `model`, `arrayMembership`                                           |

Validation: a Dante route cannot loop a channel to itself; subscription
`destinationDevice` must match the owning `DanteDevice.name`; `AES67Stream`
`multicastAddress` must be in 224.0.0.0/4.

Helpers: `parseDanteRoute`, `parseDanteDevice`, `parseAES67Stream`,
`parseWirelessMicrophone`, `parseSpeakerPosition`,
`validateDanteRouteForDevices`, `computeWirelessMicBatteryClass` (`critical`
<15%, `low` <35%, else `ok`).

### 4.4 Video (`protocol-video.ts`)

The video protocol types cover both the signal format layer (SDI frame rates and
formats, NDI streams) and the application layer (LED panel mapping, projection
surface geometry, media asset metadata). SDI is the traditional uncompressed
digital video standard for broadcast; NDI is NewTek's IP-based alternative that
routes video over standard Ethernet. `LEDProcessor` represents the Brompton,
Megapixel, or other LED management hardware that sits between the media server
and the LED panels.

`VideoFrameRate`: `23.976 | 24 | 25 | 29.97 | 30 | 50 | 59.94 | 60 | 120`.
`VideoColorSpace`: `bt709 | bt2020 | srgb | dci-p3 | display-p3 | acescg`.
`SDIFormat`: `HD | 3G | 6G | 12G`. `MediaCodec`:
`prores | dnxhr | h264 | h265 | av1 | vp9 | jpeg2000 | notchlc | hap`.

| Interface           | Fields                                                                                                          |
| ------------------- | --------------------------------------------------------------------------------------------------------------- |
| `VideoResolution`   | `width`, `height` (positive integers)                                                                           |
| `NDISource`         | `name`, `ipAddress`, `resolution`, `frameRate`, `colorSpace`, `bandwidthMbps`                                   |
| `SDISignal`         | `format`, `resolution`, `frameRate`, `embeddedAudioChannels` (0–64)                                             |
| `LEDPanelMapping`   | `panelId`, `outputPort`, `x`, `y`, `width`, `height`                                                            |
| `LEDProcessor`      | `model`, `inputCount`, `outputCount`, `panelMapping[]`, `brightness` (0–100), `colorTemperature` (1000–20000 K) |
| `ProjectionSurface` | `meshVertices[]` (≥3), `uvMapping[]` (≥3), `resolution`, `blendingZones[]`                                      |
| `BlendingZone`      | `edge` (`left\|right\|top\|bottom`), `start`, `end`, `feather` (all 0–1)                                        |
| `MediaAsset`        | `filePath`, `codec`, `resolution`, `durationSeconds`, `frameRate`, `colorSpace`, `thumbnail`                    |

Validation: SDI embedded-audio channel limit per format (HD/3G 16, 6G 32, 12G
64); `LEDProcessor` panel ports must be ≤ `outputCount` and `panelId` unique;
`ProjectionSurface` `uvMapping` length must equal `meshVertices` length.

Helpers: `parseNDISource`, `parseSDISignal`, `parseLEDProcessor`,
`parseProjectionSurface`, `parseMediaAsset`, `estimateVideoBandwidthMbps`,
`mediaAssetFrameCount`, `mediaAssetAspectRatio`.

### 4.5 Automation and Safety (`protocol-automation-safety.ts`)

Automation safety types represent the physical safety infrastructure that
governs moving equipment. A `MotionAxis` is an individual motorized axis (one
chain hoist, one flying rail, one turntable) with position and velocity ranges
enforced by the control system. A `LoadCell` is a weighing transducer attached
to a rigging point that reports the actual load in real time — critical for
detecting overloads before a structural failure. The `SafetyPLC` (Programmable
Logic Controller) is the hardware safety layer that enforces emergency stop
logic independently of the show control software.

`MotionAxisType`: `hoist | fly | turntable | lift | track | custom`.
`MotionAxisStatus`: `idle | moving | fault | e_stopped`. `LoadCellStatus`:
`normal | warning | alarm | fault`. `SafetyPlcState`: `run | stopped | fault`.
`GPIODirection`: `input | output`.

| Interface    | Fields                                                                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `MotionAxis` | `axisId`, `type`, `positionRange`, `velocityRange`, `accelerationRange`, `currentPosition`, `currentVelocity`, `status`, `customTypeLabel?` |
| `LoadCell`   | `id`, `location`, `currentLoadKg`, `ratedCapacityKg`, `alarmThresholdKg`, `status`                                                          |
| `SafetyPLC`  | `manufacturer`, `model`, `ipAddress`, `safetyState`, `inputStates`, `outputStates`, `emergencyStopActive`                                   |
| `GPIOPin`    | `pinNumber`, `direction`, `state`, `debounceMs` (0–10000)                                                                                   |

Validation: axis `currentPosition` within `positionRange`; idle/`e_stopped` axes
must have near-zero velocity; `custom` type requires `customTypeLabel`;
`LoadCell.alarmThresholdKg ≤ ratedCapacityKg` with status/load coherence checks;
`SafetyPLC` cannot be `run` while `emergencyStopActive`.

Helpers: `parseMotionAxis`, `parseLoadCell`, `parseSafetyPLC`, `parseGPIOPin`,
`motionAxisTravelPercent`, `loadCellUtilizationPercent`,
`evaluateLoadCellStatus`, `canEnableSafetyOutputs`.

### 4.6 Equipment (`protocol-equipment.ts`)

The equipment types form the inventory backbone of the domain. The `Equipment`
type is a discriminated union keyed on `EquipmentCategory` — each category
selects a distinct `specification` interface with category-appropriate fields
(e.g. `LightingFixtureSpec` has DMX footprint and fixture type;
`RiggingHoistSpec` has rated capacity and chain speed). The
`isSafetyCriticalEquipment` helper identifies equipment that requires additional
inspection rigor.

`EquipmentCategory` enum — 24 values: `lighting_fixture`, `lighting_accessory`,
`audio_speaker`, `audio_console`, `audio_microphone`, `audio_wireless`,
`audio_processing`, `video_display`, `video_projector`, `video_processor`,
`video_camera`, `rigging_hoist`, `rigging_truss`, `rigging_hardware`,
`effects_pyro`, `effects_atmospheric`, `effects_laser`, `scenic_element`,
`electrical_distro`, `electrical_cable`, `communication_intercom`,
`communication_radio`, `network_switch`, `network_cable`.

`Equipment<C>` extends `EquipmentBase` (`id`, `name`, `manufacturer`, `model`,
`serialNumber`, `status` ∈ `available|in_use|maintenance|retired`, `location?`,
`tags[]`) and adds `category` plus a `specification` whose shape is selected by
category via `EquipmentSpecificationByCategory`. Each of the 24 categories has a
distinct typed specification (e.g. `LightingFixtureSpec`, `RiggingHoistSpec`,
`EffectsLaserSpec`, `NetworkSwitchSpec`).

`EquipmentRecord` is the discriminated union over all 24 typed equipment shapes.

Helpers: `parseEquipment`, `parseEquipmentList`, `isEquipmentCategory`,
`estimateEquipmentPowerDrawWatts`, `isSafetyCriticalEquipment` (true for rigging
hoists/hardware, pyro, electrical distro).

### 4.7 Maintenance (`protocol-maintenance.ts`)

Maintenance records in live entertainment are not optional paperwork — they are
the legal basis for operating equipment above audiences. A chain hoist that has
not had a load test in the past year may not be legally rigged in many
jurisdictions. The `MaintenanceComplianceSummary` aggregate is what a production
manager or insurance auditor reviews to confirm a production's equipment is in
compliance.

`MaintenanceInspectionType`:
`visual | functional | load_test | electrical_test | calibration`.

`MaintenanceResult` discriminated union: `{ pass }` |
`{ conditional_pass, notes }` | `{ fail, notes? }`.

| Interface                      | Fields                                                                                                                                                                                        |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CorrectiveAction`             | `id`, `description`, `status` (`open\|in_progress\|resolved\|waived`), `assigneeId?`, `dueAt?`, `resolvedAt?`                                                                                 |
| `MaintenanceRecord`            | `id`, `equipmentId`, `inspectionType`, `result`, `inspectorId`, `inspectorCertification`, `inspectedAt`, `nextDueAt?`, `photos[]`, `correctiveActions[]`                                      |
| `MaintenanceComplianceSummary` | aggregate counts: compliant/non-compliant/overdue/dueSoon records, `complianceRate`, `openCorrectiveActions`, `failedRecordCount`, `conditionalPassRecordCount`, `byInspectionType` breakdown |

Helpers: `parseMaintenanceRecord`, `parseMaintenanceRecordList`,
`isMaintenanceRecordOverdue`, `isMaintenanceRecordDueSoon`,
`listOverdueMaintenanceRecords`, `generateMaintenanceComplianceSummary`
(deduplicates to the latest record per equipment/inspection-type pair).

### 4.8 GDTF (`protocol-gdtf.ts`)

GDTF (General Device Type Format) is the open standard — developed jointly by MA
Lighting, Robe, and the GDTF group — for describing the complete capabilities of
a lighting fixture in a machine-readable file. A GDTF file (`.gdtf`, a ZIP
archive) contains the fixture's DMX modes, physical dimensions, wheel contents,
and geometry hierarchy. The `parseGDTFFixtureArchive` function reads these
archives directly, which is what enables Lumina to ingest any manufacturer's
GDTF file without custom code.

GDTF (General Device Type Format) fixture profiles. `GDTFWheelType`:
`color | gobo`. `GDTFGeometryType`:
`base | yoke | head | beam | pixel_group | other`.

| Interface                | Fields                                                                                                                           |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `GDTFChannelFunction`    | `name`, `attribute`, `dmxFrom`/`dmxTo` (0–255), `physicalFrom?`, `physicalTo?`, `description?`                                   |
| `GDTFDMXChannel`         | `name`, `offset` (1–2048), `defaultValue`, `highlightValue?`, `physicalDescription?`, `functions[]`                              |
| `GDTFDMXMode`            | `name`, `channelCount`, `channelLayout[]`                                                                                        |
| `GDTFPhysicalProperties` | `weightKg`, `dimensions`, `powerDrawWatts`, `beamAngleRangeDegrees`, `colorTemperatureRangeKelvin`, `cri` (0–100), `lumenOutput` |
| `GDTFWheel`              | `name`, `type`, `slots[]`                                                                                                        |
| `GDTFGeometryNode`       | `name`, `type`, `children[]` (recursive)                                                                                         |
| `GDTFFixture`            | `manufacturer`, `model`, `dmxModes[]`, `physical`, `wheels[]`, `geometryHierarchy[]`, `sourceArchive?`                           |

Parsers (real implementations, not stubs): `parseGDTFFixtureXml` parses GDTF
`description.xml` via an in-module XML tree parser; `parseGDTFFixtureArchive`
extracts a `.gdtf` ZIP archive (handles stored and DEFLATE-compressed entries
via `node:zlib`); `parseGDTFFixtureFile` reads from disk.

### 4.9 MVR (`protocol-mvr.ts`)

MVR (My Virtual Rig) is the show-file exchange format that GDTF-based software
uses to transfer a complete lighting design between tools — from the CAD design
application (Vectorworks Spotlight) to the lighting console (grandMA3) to Uzume.
An MVR file is a ZIP archive containing a GDTF file per fixture and a scene XML.
Uzume implements full bidirectional MVR exchange: it can both import designs
from other tools and export its own for use in external software.

MVR (My Virtual Rig) scene exchange. `MVRLayerType`:
`fixtures | trusses | scenic | focus_points`. `MVRSourceApplication`:
`vectorworks_spotlight | grandma3 | capture | uzume | other`.

| Interface             | Fields                                                                                                                |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `MVRVector3`          | `x`, `y`, `z`                                                                                                         |
| `MVRTransform`        | `position`, `rotation`, `scale`                                                                                       |
| `MVRLayer`            | `id`, `name`, `type`, `visible`, `locked`, `order`                                                                    |
| `MVRFixturePlacement` | `id`, `fixtureId`, `fixtureType`, `layerId`, `transform`, `focusPointId?`, `groupIds[]`                               |
| `MVRTrussDefinition`  | `id`, `name`, `layerId`, `trussType`, `lengthMeters`, `transform`, `attachments[]`                                    |
| `MVRScenicElement`    | `id`, `name`, `layerId`, `category`, `transform`                                                                      |
| `MVRFocusPoint`       | `id`, `name`, `layerId`, `position`                                                                                   |
| `MVRGroupDefinition`  | `id`, `name`, `memberIds[]`                                                                                           |
| `MVRSceneMetadata`    | `title`, `author?`, `createdAt`, `updatedAt`, `sourceApplication`, `units` (`m\|ft`), `notes?`                        |
| `MVRScene`            | `metadata`, `layers[]`, `fixturePlacements[]`, `trusses[]`, `scenic[]`, `focusPoints[]`, `groups[]`, `sourceArchive?` |

Validation: every entity's `layerId` must reference a layer of the matching
type; `focusPointId` references must exist; group `memberIds` must reference
existing scene entities.

MVR exchange (full bidirectional implementation): `parseMVRSceneXml`,
`parseMVRSceneArchive`, `parseMVRSceneFile`, `serializeMVRSceneXml`,
`serializeMVRSceneArchive` (writes a stored-mode ZIP), `writeMVRSceneFile`,
`parseMVRScene`.

### 4.10 Patched Fixture (`protocol-patched-fixture.ts`)

The patched fixture is the runtime representation of a real lighting fixture
that has been assigned a physical DMX address in the patch. It bridges the
static GDTF fixture profile (what the fixture is capable of) with the live
production context (where it is, which DMX address it uses, what its current
parameters are). The `revision` counter on `PatchedFixture` enables optimistic
concurrency: two operators cannot silently overwrite each other's parameter
changes.

Bridges GDTF profiles into a placed, patched, live fixture.

| Interface                         | Fields                                                                                                                                                                                                    |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FixtureColorParameters`          | `red`/`green`/`blue` (0–255), `white?`, `amber?`, `uv?`                                                                                                                                                   |
| `FixturePositionParameters`       | `panDegrees`, `tiltDegrees`                                                                                                                                                                               |
| `FixtureBeamParameters`           | `zoomDegrees`, `focusPercent` (0–100), `irisPercent?`, `frostPercent?`                                                                                                                                    |
| `PatchedFixtureCurrentParameters` | `intensityPercent` (0–100), `color`, `position`, `beam`                                                                                                                                                   |
| `PatchedFixture`                  | `fixtureId`, `label`, `gdtfFixture`, `modeName`, `universe` (0–63999), `dmxAddress` (1–512), `placement`, `focusTarget?`, `colorGel?`, `groupMemberships[]`, `currentParameters`, `updatedAt`, `revision` |
| `PatchedFixtureRealtimeUpdate`    | partial `intensityPercent`/`color`/`position`/`beam` patch                                                                                                                                                |

Validation: `modeName` must exist in `gdtfFixture.dmxModes`; the patched mode
must not overrun channel 512.

Helpers: `parsePatchedFixture`, `createPatchedFixture` (de-dupes groups, sets
`revision` 0), `applyPatchedFixtureRealtimeUpdate` (merges and increments
`revision`), `patchedFixtureChannelFootprint`, `patchedFixturePatchEndAddress`.

---

## 5. Event Bus (`@uzume/core/event-bus`)

The event bus is the coordination mechanism that allows subsystem libraries to
react to each other's state changes without importing one another. When the
stage manager calls a cue in `@uzume/koru`, it publishes a `cue.trigger` event;
`@uzume/lumina`, `@uzume/sonos`, and any other subscribed subsystem each receive
that event and take action independently. This is what makes subsystem isolation
possible at the architecture level.

The event bus is a NATS-backed publish/subscribe layer. Exported from
`src/event-bus/index.ts`: `types`, `transport`, `in-memory-transport`,
`uzume-nats-event-bus`.

### 5.1 Event Types and Subjects

There are 6 event types. Each type has a constant name, a string `type` value
embedded in the envelope, and a NATS subject used for routing. The table below
maps all three for easy lookup when writing publishers or subscribers.

| Event type constant        | `type` value               | NATS subject              |
| -------------------------- | -------------------------- | ------------------------- |
| `CUE_TRIGGER`              | `cue.trigger`              | `uzume.cue.trigger`       |
| `DEVICE_STATUS_CHANGED`    | `device.status.changed`    | `uzume.device.status`     |
| `SAFETY_ALERT`             | `safety.alert`             | `uzume.safety.alert`      |
| `SCHEDULE_UPDATED`         | `schedule.updated`         | `uzume.schedule.update`   |
| `EQUIPMENT_STATUS_CHANGED` | `equipment.status.changed` | `uzume.equipment.status`  |
| `CREW_NOTIFICATION`        | `crew.notification`        | `uzume.crew.notification` |

### 5.2 Event Payload Schemas

Each event type has a Zod schema in `UZUME_EVENT_PAYLOAD_SCHEMAS` that validates
the payload at publish and subscribe boundaries. The schemas are the contract
between publishers and subscribers — if a field is missing or of the wrong type,
NATS delivery still succeeds but Zod validation will throw before the subscriber
processes the event. The table below lists each schema and its key payload
fields.

| Schema                               | Payload fields                                                                                                                                             |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UzumeCueTriggerPayloadSchema`       | `showId`, `cueListId`, `cueId`, `triggerSource` (`manual\|timeline\|timecode\|api`), `plannedTimeMs`, `actualTimeMs`, `driftMs`                            |
| `UzumeDeviceStatusPayloadSchema`     | `deviceId`, `subsystem`, `status` (`online\|degraded\|offline\|maintenance`), `changedAt`, `details?`                                                      |
| `UzumeSafetyAlertPayloadSchema`      | `alertId`, `zone`, `severity` (`warning\|critical\|safety-critical`), `message`, `requiresEvacuation`, `acknowledgedBy?`                                   |
| `UzumeScheduleUpdatedPayloadSchema`  | `scheduleId`, `showId`, `updateType` (`call_time\|rehearsal\|load_in\|load_out\|milestone`), `effectiveAt`, `impactedDepartments[]`, `summary`             |
| `UzumeEquipmentStatusPayloadSchema`  | `equipmentId`, `category`, `status` (`available\|allocated\|maintenance\|failed`), `location`, `assignedShowId?`                                           |
| `UzumeCrewNotificationPayloadSchema` | `notificationId`, `crewId`, `department`, `priority` (`info\|warning\|urgent`), `channel` (`app\|sms\|radio\|email`), `message`, `requiresAcknowledgement` |

### 5.3 Envelope, Priority, Delivery

The delivery guarantee is selected automatically from the event priority: a
`safety-critical` event uses guaranteed delivery (request/ack with retry) while
`informational` events use best-effort. This means safety alerts cannot be
silently dropped by a NATS connection issue without triggering retries, while
routine status updates do not add ack latency.

`UZUME_EVENT_PRIORITIES`: `informational | operational | safety-critical`.
`UZUME_DELIVERY_GUARANTEES`: `best-effort | guaranteed`.

`UzumeEventEnvelope<T>` fields: `eventId`, `type`, `subject`, `occurredAt`,
`correlationId?`, `sourceSubsystem?`, `priority`, `deliveryGuarantee`,
`payload`.

`UzumePublishOptions`: `correlationId?`, `sourceSubsystem?`, `priority?`,
`guaranteedRetryLimit?`, `retryDelayMs?`, `ackTimeoutMs?`.
`UzumeSubscriptionOptions`: `queueGroup?`.

### 5.4 Transports and Bus

Two transport implementations share the same `UzumeNatsTransport` interface: the
production transport wraps a real NATS connection, while the in-memory transport
runs entirely in-process for unit tests and embedded scenarios. Switching
between them requires only changing the transport constructor call — the bus and
all subscriber code remain identical.

- `UzumeNatsTransport` interface — `publish`, `request`, `subscribe`, `close`.
- `NatsJsUzumeTransport` — production transport wrapping a real `nats`
  connection; `createNatsJsUzumeTransport(config)` connects with
  `UzumeNatsConnectionConfig` (`servers`, `name?`, `token?`, `user?`,
  `password?`, `timeoutMs?`, default timeout 5000 ms).
- `InMemoryUzumeNatsTransport` — in-process transport implementing the same
  interface for tests and embedded use.
- `UzumeNatsEventBus` — typed bus over a transport. `publish` selects
  `best-effort` vs `guaranteed` delivery from event priority; guaranteed events
  use request/ack with retry (default `guaranteedRetryLimit` 3,
  `guaranteedRetryDelayMs` 100, `guaranteedAckTimeoutMs` 1000). Public methods:
  `publish`, `subscribe`, `close`.

---

## 6. Persistence — Drizzle ORM Schema (`@uzume/core/database`)

All persistence for the Uzume domain lives in `@uzume/core`. Concentrating the
database schema in one library avoids fragmentation across 22 separate schemas
and prevents subsystem libraries from creating implicit cross-schema
dependencies. Subsystem libraries are stateless computation libraries; they
return values to the consuming application, which decides what to persist.

The PostgreSQL schema namespace is `uzume` (`pgSchema('uzume')`). Exported from
`src/database/index.ts`: `schema`, migration `definitions`/`index`/`service`,
and `seeds`.

### 6.1 PostgreSQL Enums (19)

The following 19 enums are created in the `uzume` schema. They are listed here
with their value count so that a schema diff can confirm completeness.

`show_status` (8), `department_type` (7: `lighting`, `audio`, `video`,
`automation`, `effects`, `scenic`, `stage_management`), `cue_target_type` (6),
`cue_trigger_type` (`manual`, `timecode`, `follow`, `auto`),
`equipment_condition` (5), `ownership_status` (4), `maintenance_result` (`pass`,
`fail`, `needs_attention`), `assignment_type` (`show`, `truck`, `warehouse`),
`certification_status` (4), `purchase_order_status` (6), `contract_status` (5),
`settlement_status` (4), `inspection_result` (3), `incident_severity` (4),
`incident_status` (4), `certification_log_status` (4),
`safety_document_approval_status` (4), `safety_audit_event_type` (`insert`,
`update`, `delete`, `state_change`).

### 6.2 Tables (24)

`UZUME_CORE_TABLES` enumerates all 24 tables. Every table carries a UUID primary
key (`defaultRandom()`), JSONB `metadata` where applicable, and timestamp
columns. Drizzle `relations()` are defined for every table, and
`InferSelectModel`/`InferInsertModel` types are exported per table (e.g.
`Venue`/`NewVenue`, `Show`/`NewShow`, `Cue`/`NewCue`).

| Table                   | Purpose                                                                               |
| ----------------------- | ------------------------------------------------------------------------------------- |
| `venues`                | Venue records: dimensions, rigging points, power capacity (JSONB), capacity           |
| `shows`                 | Show records: code, title, venue FK, status, dates, planned/actual budget             |
| `departments`           | Per-show departments (unique per show + `department_type`)                            |
| `equipment_categories`  | Hierarchical equipment categories (self-referencing `parent_category_id`)             |
| `equipment_items`       | Equipment inventory: serial, manufacturer, model, condition, ownership, RFID, barcode |
| `maintenance_logs`      | Equipment maintenance/inspection logs with `next_due_at`                              |
| `equipment_assignments` | Equipment-to-show/truck/warehouse assignments                                         |
| `crew`                  | Crew roster: name, contact, skills, union affiliation, day rate, availability         |
| `certifications`        | Crew certifications with issuer, jurisdiction, expiry, status                         |
| `labor_rules`           | Union labor rules: overtime threshold, meal penalty, turnaround, rest break           |
| `crew_assignments`      | Crew-to-show assignments with department, labor rule, call/wrap times                 |
| `vendors`               | Vendor directory with pricing terms, lead time, rating                                |
| `budgets`               | Per-show/department budget lines: allocated/actual/variance, currency, FX rate        |
| `purchase_orders`       | Purchase orders: line items, approval chain, totals, status                           |
| `contracts`             | Contracts: parties, terms, milestones, payment schedule, value                        |
| `settlements`           | Financial settlements: gross/expense/net amounts, reconciliation status               |
| `risk_assessments`      | Risk assessments: likelihood, severity, mitigations, residual risk                    |
| `inspections`           | Safety inspections: checklist type, result, photos, `next_due_at`                     |
| `incidents`             | Incidents: type, severity, status, root cause, corrective actions                     |
| `certifications_log`    | Certification status-change log                                                       |
| `safety_documents`      | Safety document versions with approval status and file URI                            |
| `safety_audit_log`      | Append-only safety audit trail (table/record/event-type/actor)                        |
| `cue_lists`             | Cue lists per show + department, with cue prefix and numbering start                  |
| `cues`                  | Cues: number, label, trigger type/time, pre/post wait, duration, target, parameters   |

### 6.3 Migrations

There is a single bootstrap migration that creates the entire schema from
scratch. This design means the schema has one authoritative starting state —
incremental migrations are added only when the schema evolves after initial
deployment. The migration tracking table and advisory lock ensure that
concurrent application startup does not run the migration twice.

`definitions.ts` defines the single bootstrap migration
`20260210120000_uzume_core_schema` ("uzume core schema bootstrap"), with full
`CREATE`/`DROP` SQL for the schema, the 19 enums, all 24 tables, and indexes.
`UZUME_CORE_MIGRATIONS` lists it; `getUzumeCoreMigrations`,
`registerUzumeCoreMigrations` integrate with the `@oshun/database`
`MigrationRunner`.

`service.ts` provides `UzumeCoreMigrationService` and
`createUzumeCoreMigrationService`, `migrateUzumeCoreUp`, `migrateUzumeCoreDown`,
`resolveUzumePostgresConfigFromEnv`. The migration tracking table is
`public.uzume_schema_migrations`; the advisory lock id is `620001`.

### 6.4 Seeds

`seeds/generator.ts` exports `generateUzumeSeedDataset` with
`UzumeSeedGeneratorOptions`, `UzumeSeedSummary`, and `UzumeSeedDataset`,
producing reference data for the schema.

---

## 7. Errors (`@uzume/core/errors`)

The error system serves two purposes that go beyond a simple exception
hierarchy. First, it routes errors to the correct department dashboard so that a
lighting error surfaces to the lighting operator's display, not the sound board.
Second, it escalates safety-critical errors through dedicated channels (audible
and visual alerts) to ensure they are not silently swallowed. These requirements
are why the error system is more complex than a simple `throw new Error()`.

Exported from `src/errors/index.ts`: `error-codes`, `error-framework`,
`error-routing`, `safety-escalation`, `uzume-error`.

### 7.1 Error Codes

`UZUME_ERROR_SUBSYSTEMS` — 19 subsystems: `LUMINA`, `SONOS`, `PRISM`, `KINESIS`,
`PYRA`, `SCENA`, `KORU`, `NEXUS`, `ATLAS`, `AEGIS`, `AURORA`, `ECHO`, `VESTIS`,
`PREVIZ`, `CHRONOS`, `TESLA`, `HERMES`, `FORGE`, `MUSE`.

`UzumeErrorCode` is the template-literal type `${Subsystem}-${ThreeDigitCode}`
(e.g. `LUMINA-042`, sequence 001–999). Helpers: `isUzumeErrorCode`,
`createUzumeErrorCode`, `parseUzumeErrorCode`.

### 7.2 UzumeError

`UZUME_ERROR_SEVERITIES`: `info | warning | critical | safety-critical`.

`UzumeError` extends `@oshun/errors`' `OshunError` and adds `subsystem`,
`uzumeCode`, `severity`, `errorId` (UUID), `escalationRequired`, plus
`isSafetyCritical()`. `UzumeOperationalContext` carries `showId?`, `venueId?`,
`cueListId?`, `cueId?`, `deviceId?`, `operatorId?`. Default code is
`<subsystem>-900`; `safety-critical` severity defaults `escalationRequired` to
true. Helpers: `isUzumeError`, `toUzumeError`.

### 7.3 Error Routing and Escalation

`error-routing.ts`: `UzumeErrorRouter` routes errors to department dashboards.
`UzumeDepartmentDashboard` has 17 values; `SUBSYSTEM_DASHBOARD_MAP` maps each of
the 19 subsystems to a primary dashboard. `buildUzumeDashboardRoutes` adds a
`show_control` route for critical errors and `safety` + `stage_management`
routes for safety-critical errors. Topics follow
`uzume.dashboard.<dashboard>.errors`.

`safety-escalation.ts`: `UzumeSafetyEscalationService` escalates safety-critical
errors across `UzumeSafetyEscalationChannel`s. `UZUME_SAFETY_ALERT_MODES`:
`audible | visual`. The result reports delivered/failed channels and
covered/missing alert modes.

`error-framework.ts`: `UzumeErrorFramework` (and `createUzumeErrorFramework`)
ties routing, escalation, and logging together with `UzumeErrorFrameworkConfig`.

---

## 8. Configuration (`@uzume/core/config`)

The configuration schema is designed for fail-fast startup: all required
environment variables are validated and typed at boot time so that a missing
`UZUME_NATS_SERVERS` is discovered before the first cue fires, not during the
show. Feature flags allow a consuming application to enable only the subsystems
it needs, which also drives which NATS subscriptions are established at startup.

`config/env.ts` defines `UzumeConfigSchema` (a Zod transform of an environment
schema). Environment variables:

| Variable                              | Required | Purpose                                              |
| ------------------------------------- | -------- | ---------------------------------------------------- |
| `UZUME_DATABASE_URL`                  | yes      | Primary PostgreSQL URL                               |
| `UZUME_TIMESCALE_URL`                 | no       | TimescaleDB URL (falls back to `UZUME_DATABASE_URL`) |
| `UZUME_CLICKHOUSE_URL`                | no       | ClickHouse URL                                       |
| `UZUME_REDIS_URL`                     | yes      | Redis URL                                            |
| `UZUME_NATS_SERVERS`                  | yes      | Comma-separated NATS server list                     |
| `UZUME_NATS_STREAM_NAME`              | no       | NATS stream name (default `uzume.events`)            |
| `UZUME_S3_ENDPOINT`                   | yes      | S3-compatible object storage endpoint                |
| `UZUME_S3_BUCKET`                     | yes      | Object storage bucket                                |
| `UZUME_S3_REGION`                     | no       | Object storage region (default `us-east-1`)          |
| `UZUME_S3_ACCESS_KEY`                 | yes      | Object storage access key                            |
| `UZUME_S3_SECRET_KEY`                 | yes      | Object storage secret key                            |
| `UZUME_S3_FORCE_PATH_STYLE`           | no       | Path-style addressing toggle (default `true`)        |
| `UZUME_FEATURE_LIGHTING`              | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_AUDIO`                 | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_VIDEO`                 | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_AUTOMATION`            | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_EFFECTS`               | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_SCENIC`                | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_STAGE_MANAGEMENT`      | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_SHOW_CONTROL`          | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_PRODUCTION_MANAGEMENT` | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_SAFETY`                | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_AUDIENCE_EXPERIENCE`   | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_PREVIZ`                | no       | Feature flag (default `true`)                        |
| `UZUME_FEATURE_AI`                    | no       | Feature flag (default `true`)                        |

The transform shapes config into `database`, `redis`, `nats`, `objectStorage`,
and `features` sections. `loadUzumeConfig` parses and caches; on failure it
throws an aggregated message. Helpers: `resetUzumeConfigCache`,
`isUzumeFeatureEnabled`, types `UzumeConfig`, `UzumeFeatureKey`.

---

## 9. Logging (`@uzume/core/logging`)

Structured logging with subsystem context allows log aggregation systems to
filter events by subsystem (`LUMINA`, `AEGIS`, etc.) and correlate related
events across subsystems using the `UzumeCorrelationContext`. This is essential
for diagnosing show-time incidents where events from multiple subsystems are
interleaved in the log stream.

`logging/uzume-logger.ts` provides structured logging with subsystem context.
`UzumeSubsystem` is a union of subsystem names; `UzumeCorrelationContext` and
`UzumeLogOutputTarget` describe correlation propagation and output targets.
`UzumeSubsystemLogger` is the per-subsystem logger; `UzumeLoggingConfig` and
`UzumeLoggingInfrastructure` configure it; `createUzumeLoggingInfrastructure`
builds the logging stack.

---

## 10. Utilities (`@uzume/core/utils`)

The utilities module is a collection of pure computation functions that multiple
subsystem libraries need. They are kept in `@uzume/core` rather than in the
subsystem libraries to avoid duplication and to ensure that, for example, every
subsystem's SMPTE timecode arithmetic uses the same drop-frame-correct
implementation.

Exported from `src/utils/index.ts`: `dmx`, `protocol-address`,
`safety-calculations`, `smpte-timecode`, `unit-conversion`.

### 10.1 `smpte-timecode.ts`

SMPTE timecode arithmetic is subtle: 29.97 drop-frame timecode skips frame
numbers 0 and 1 at the start of each minute (except every 10th minute) to
compensate for the 0.1% difference between 30 and 29.97 fps. Getting this wrong
causes timecode-locked cues to drift by 3.6 seconds per hour. The functions
below handle drop-frame correction transparently.

`SmpteFrameRate` (`24|25|29.97|30`), `TimecodeRoundingMode`
(`round|floor|ceil|trunc`), `SmpteTimecode`, `TimecodeConversionOptions`.
Functions: `parseSmpteTimecode`, `formatSmpteTimecode`, `timecodeToTotalFrames`,
`totalFramesToTimecode`, `convertSmpteTimecodeFrameRate`, `addSmpteTimecodes`,
`subtractSmpteTimecodes`, `multiplySmpteTimecode`, `compareSmpteTimecodes`,
`createSmpteTimecodeRange`.

### 10.2 `dmx.ts`

DMX merge (HTP = highest-takes-priority, LTP = latest-takes-priority) is the
mechanism for combining DMX output from multiple sources — the operator desk, a
timecode playback system, and an effects engine may all be writing to the same
universe simultaneously. `mergeDmxUniverseSources` produces the merged output;
`captureDmxSnapshot` records the state for later comparison or restore.

Constants `DMX_UNIVERSE_MIN = 1`, `DMX_UNIVERSE_MAX = 65535`. `DmxMergeMode`
(`htp|ltp`), `DmxFadeCurve`. Interfaces for patch entries, conflicts, source
state, merged state, snapshots, and fades. Functions include
`detectDmxPatchConflicts`, `mergeDmxUniverseSources`, `captureDmxSnapshot`,
`calculateDmxFade`, `calculateDmxUniverseFadeSnapshot`, plus validators.

### 10.3 `safety-calculations.ts`

These functions implement the structural and electrical load calculations that
appear in rigging permit documentation and power distribution design. They are
based on standard engineering formulas (ASCE 7 for wind load, standard bridle
geometry for point loads) and return a `SafetyStatus` so callers can distinguish
between confirmed safe, confirmed over-capacity, and capacity-unknown cases.

`SafetyStatus` (`within_limit|over_capacity|capacity_not_provided`). Structural,
electrical, and environmental load math: `calculatePointLoad`,
`calculateDistributedLoad`, `calculateBridleLegTension`,
`calculateSinglePhaseElectricalLoad`, `calculateThreePhaseElectricalLoad`,
`calculateVerticalCableWeight`, `calculateWindLoadAsce7Simplified`.

### 10.4 `protocol-address.ts`

Protocol address strings follow different conventions in different tools and
console displays (e.g., `1.001` vs `1/1` vs `U1C1` for DMX universe 1 channel
1). These helpers parse human-readable address strings into typed structs and
format typed structs back to canonical strings, insulating the rest of the
codebase from the fragmentation.

DMX/Art-Net/sACN/Dante/NDI/IP-subnet/MIDI address parsing and formatting (e.g.
`parseDmxAddressString`, `artNetPortAddress`, `parseSacnUniverse`,
`parseDanteChannelString`, `parseIpSubnet`, `midiNoteNumberToName`,
`parseMidiCcString`).

### 10.5 `unit-conversion.ts`

Distance/weight/angle conversions, fractional-inch formatting, DMX↔degrees and
DMX↔Kelvin conversions, single/three-phase power math (watts/VA/amps), and
audio-level conversions (dBu/dBV/dBFS/dBSPL, Pa).

---

## 11. `@uzume/nexus`

`@uzume/nexus` is the intended TypeScript entry point for the show control
protocol bridge. The current TypeScript implementation is a metadata-only
package: `src/index.ts` exports `UZUME_NEXUS_METADATA` and
`getUzumeNexusMetadata()` and nothing else. The protocol-bridging behavior its
description refers to is fully implemented in the Rust `uzume-protocol-engines`
workspace and consumed by TypeScript applications via the napi-rs native module
and the WASM control surface. The split exists because the protocol engines
require sub-microsecond precision that Node.js cannot provide — moving them to
Rust was a deliberate performance decision, not an implementation gap.

---

## 12. Rust Protocol Engines (`@uzume/protocol-engines`)

The Rust workspace is the performance foundation of Uzume. It is a Cargo
workspace at `libs/uzume/protocol-engines/` (`resolver = "2"`, edition 2021)
with six crates organized into three layers: the shared type definitions
(`uzume-protocol-types`), the timing and orchestration logic
(`uzume-timing-core`), the network I/O engines (`uzume-network-io`), and two
platform bridges that expose the workspace to TypeScript and browser
environments. The integration test crate validates cross-crate behavior.

| Crate                        | Role                                                              |
| ---------------------------- | ----------------------------------------------------------------- |
| `uzume-protocol-types`       | Core protocol types: DMX, RDM, MIDI, OSC, SMPTE timecode          |
| `uzume-timing-core`          | High-precision timing, timecode sync, master timeline, show state |
| `uzume-network-io`           | UDP/TCP I/O and Art-Net / sACN / OSC / MIDI / MSC engines         |
| `uzume-node-bridge`          | napi-rs Node.js native module (`@uzume/protocol-native`)          |
| `uzume-control-surface-wasm` | wasm-bindgen browser control surface                              |
| `uzume-integration-tests`    | Cross-crate integration test suite                                |

### 12.1 `uzume-protocol-types`

This crate defines the shared types used across all other crates in the
workspace. Keeping them in a separate crate avoids circular dependencies between
`uzume-timing-core` and `uzume-network-io`, both of which need DMX, MIDI, OSC,
and timecode types.

Constants: `DMX_UNIVERSE_CHANNEL_COUNT = 512`, `DMX_MAX_UNIVERSES = 65535`,
`DMX_MIN_REFRESH_HZ = 44.0`, `DMX_MAX_REFRESH_HZ = 100.0`,
`RDM_MAX_UID = 0xFFFFFFFFFFFF`,
`RDM_DEFAULT_REDISCOVERY_INTERVAL_MICROS = 30_000_000`,
`RDM_MAX_LABEL_LENGTH = 32`, `DMX_RECORDING_MAGIC = b"UZDMXREC"`,
`DMX_RECORDING_FORMAT_VERSION = 1`.

`ProtocolTypeError` enumerates the crate's error variants (invalid DMX
length/channel/universe/refresh-rate/source-metadata, RDM UID/PID/parameter
errors, recording I/O and data errors, MIDI channel, OSC address, timecode).

DMX: `DMXUniverse` (fixed 512-channel buffer); `DmxMergeMode`
(`Htp`/`Ltp`/`Priority`); `DmxUniverseManager` merges per-channel writes from
multiple tracked sources (`DmxChannelSource`, `DmxChannelWrite`) and exposes
`DmxUniverseSnapshot`s. DMX recording: `DmxRecording`, `DmxRecordingSession`,
`DmxPlaybackEngine` with a binary on-disk format, delta frames, timecode-locked
seek, and live-vs-recorded A/B comparison (`DmxAbComparisonReport`).

RDM: `RdmCommandClass`, `RdmStandardPid` (DISC_UNIQUE_BRANCH, DISC_MUTE,
DEVICE_INFO, DMX_START_ADDRESS, IDENTIFY_DEVICE, DEVICE_LABEL,
MANUFACTURER_LABEL, SOFTWARE_VERSION_LABEL, SENSOR_VALUE), `RdmRequest`/
`RdmResponse`, `RdmDiscoveryRange` (binary-search splitting),
`RdmDiscoveredDevice`, and `RdmEngine` (discovery cycles, device tracking,
re-discovery interval).

MIDI: `MIDIMessage` (`status`/`data1`/`data2?`/`timestamp_micros`) with
`note_on` and `control_change` constructors (channel 1–16 validated).

OSC: `OSCArgument` (`Int`/`Float`/`String`/`Bool`/`Blob`), `OSCMessage`
(`address` must start with `/`, optional `timetag`).

Timecode: `TimecodeRate` (`Fps24`/`Fps25`/`Fps2997`/`Fps30`) with
`nominal_fps()`; `TimecodeFrame` with `total_frames()`, `from_total_frames`,
`add_frames`, and frame-range validation.

### 12.2 `uzume-timing-core`

This is the show's master clock and sequencer. `HighPrecisionClock` provides the
time source; `TimerWheel` fires scheduled events at the right frame;
`SmpteTimecodeCounter` tracks the running timecode from one of three sources
(system clock, incoming LTC audio, incoming MTC MIDI); `MasterTimelineEngine`
coordinates which cues fire at which timecode positions; and the
`CrossSubsystemCueSequencer` fans out a single cue-go event to multiple
subsystems simultaneously.

Constants include `DMX_MIN_REFRESH_HZ`, `LTC_BITS_PER_FRAME = 80`,
`MTC_QUARTER_FRAME_COUNT = 8`, `MIN/MAX_TIMELINE_PLAYBACK_SPEED` (0.1–10.0), and
discovery/health/failover defaults.

Major components: `HighPrecisionClock`/`HighPrecisionTimestamp`, `TimerWheel`
(`ScheduledEvent`/`ScheduledEventKind`), `SmpteTimecodeCounter` and
`TimecodeSyncEngine` (`TimecodeSource` ∈ system clock / external LTC / external
MTC; `MtcQuarterFrameDecoder`; LTC config), the `MasterTimelineEngine`
(`TimelineRole`, `TimelineTransportState`, `TimelineSubsystem`, loop regions,
state recall, dispatch events), the `CrossSubsystemCueSequencer` (`CueGoSource`,
cross-subsystem cue plans/dispatch), the `ShowStateSnapshotEngine`, the
`ProtocolTranslationEngine` (rule-based protocol translation with visual mapping
graphs and reverse mappings), and show-file commit/merge structures
(`ShowStateManifest`, `ShowFileCommit`, `MergeConflictStrategy`,
`ShowMergeResult`).

### 12.3 `uzume-network-io`

This crate owns the UDP/TCP sockets and protocol packet parsers. Art-Net, sACN,
and OSC all run over UDP; MIDI Show Control uses a TCP control channel. The
output engines format outgoing packets and manage discovery handshakes
(Art-Net's ArtPoll/ArtPollReply sequence, sACN's universe discovery mechanism).
The MIDI engine integrates with the `midir` crate for actual MIDI port I/O.

Constants: `ARTNET_PORT = 6454`, `ARTNET_PROTOCOL_VERSION = 14`, Art-Net opcodes
(`ArtDmx 0x5000`, `ArtPoll 0x2000`, `ArtPollReply 0x2100`, `ArtSync 0x5200`),
`ARTNET_MAX_DMX_PAYLOAD = 512`, `SACN_PORT = 5568`, `SACN_PRIORITY_MIN/MAX`
(0/200), `SACN_MAX_DMX_PAYLOAD = 512`, `OSC_DEFAULT_PORT = 8000`,
`MIDI_CLOCK_PPQ = 24`.

Components: `UdpEndpoint`/`UdpDatagram` (`NetworkIoConfig`, `NetworkIoError`),
`TcpControlServer`/`TcpControlConnection`, Art-Net/sACN/OSC packet parsers
(`ArtNetPacket`, `SacnPacket`, `OscPacket`/`OscMessagePacket`/
`OscBundlePacket`), the `ArtNetOutputEngine` and `SacnOutputEngine` (with
discovery: `ArtNetDiscoveredDevice`, `SacnUniverseDiscovery`), the `OscEngine`,
the `MidiEngine` (`MidiMessage`, `MidiClockState`, `MidiInboundEvent`,
`MidiPortInfo`), and the `MidiShowControlEngine` (`MscPacket`, `MscCommandType`,
`MscTarget`, `MscTimedGoTime`).

### 12.4 `uzume-control-surface-wasm`

This crate is the browser bridge. It exposes a small WASM API surface so that a
browser-based control surface (a lighting console running in a tablet browser,
for example) can construct and send protocol messages directly — without a
server round-trip for each button press. The surface is intentionally minimal to
keep the WASM binary small and load-time short.

A `wasm-bindgen` surface exposing `WasmDMXUniverse` (`setChannel`, `getChannel`,
`toUint8Array`) and the free functions `createMidiNoteOn`, `createOscMessage`,
and `createTimecodeFrame`. Built with
`wasm-pack build crates/uzume-control-surface-wasm --target web --release`.

### 12.5 `uzume-node-bridge`

This crate is the server-side Node.js bridge. It compiles to a native `.node`
module via napi-rs and is published as `@uzume/protocol-native`. TypeScript
server code can call `UzumeProtocolBridge` methods synchronously (they execute
in the Node.js thread) for operations that need Rust performance without
launching a subprocess. The message history limit (2048 entries) prevents
unbounded memory growth in long-running show-control servers.

A napi-rs native module (`@uzume/protocol-native`). Exposes the
`UzumeProtocolBridge` class with methods for DMX channel I/O
(`set_universe_channel`, `get_universe_channel`, `universe_output`), device
discovery (`discover_devices`), timecode sync (`sync_timecode_to_system_clock`,
`sync_timecode_external`, `set_timecode_chase_offset_frames`,
`current_timecode`), and OSC/MIDI message send/receive with bounded history
(`MESSAGE_HISTORY_LIMIT = 2048`). napi object types: `DiscoveredDevice`,
`TimecodeFrameBinding`, `OscMessageBinding`, `MidiMessageBinding`.

### 12.6 Build Commands

Run these from `libs/uzume/protocol-engines/` unless noted. The `pnpm` aliases
(`cargo:test`, `cargo:check`, etc.) wrap these commands for use within the Nx
monorepo build system.

```bash
# All Rust tests
cargo test --workspace          # (npm: cargo:test)
# Integration tests
cargo test -p uzume-integration-tests -- --nocapture   # (npm: cargo:test:integration)
# Workspace check
cargo check --workspace         # (npm: cargo:check)
# WASM control surface
pnpm --filter @uzume/protocol-engines build:wasm
# Node native module
napi build --platform --dts index.d.ts     # (in uzume-node-bridge, npm: build)
```

---

## 13. Subsystem Library Surface

Each of the 19 subsystem libraries (and `@uzume/lumina`, `@uzume/broadcast`,
etc.) is a flat collection of independent `*-engine.ts` modules re-exported from
`src/index.ts` alongside the library metadata constant. Subsystem libraries do
not import from one another; cross-subsystem coordination flows through the
`@uzume/core` event bus. This section documents two libraries in detail as
canonical examples of the module-per-engine pattern. The remaining 17 follow the
same structure; per-file inventories live alongside each library's
`src/index.ts`.

The following engine module lists are verified against `src/index.ts` in each
library:

- **`@uzume/lumina`** (lighting): `fixture-patch-engine`,
  `fixture-library-manager`, `gdtf-parser`, `mvr-exchange-engine`,
  `fixture-parameter-engine`, `color-engine`, `gel-library`,
  `color-picker-engine`, `color-consistency-engine`,
  `subtractive-mixing-engine`, `tracking-cue-stack-engine`, `fade-engine`,
  `cue-trigger-engine`, `multi-cue-list-playback-engine`, `cue-macro-engine`,
  `parametric-effects-engine`, `pixel-mapping-engine`,
  `generative-content-engine`, `console-integration-bridge`,
  `atmospheric-effects-engine`.
- **`@uzume/broadcast`**: `broadcast-switcher-integration-engine`,
  `broadcast-multiviewer-management-engine`, `replay-system-integration-engine`,
  `broadcast-graphics-engine`, `broadcast-audio-split-management-engine`,
  `broadcast-multi-platform-streaming-engine`,
  `stream-health-monitoring-dashboard-engine`,
  `srt-contribution-feed-management-engine`,
  `multi-venue-simulcast-distribution-engine`,
  `iso-recording-management-engine`.

Subsystem engines are domain-specific implementations, not generic CRUD. For
example: `@uzume/aegis`'s `risk-assessment-engine.ts` defines
`RiskLikelihoodScore`/`RiskSeverityScore` 1–5 scales, `RiskLevel`, and a
probability×severity scoring matrix; `@uzume/lumina`'s `fixture-patch-engine.ts`
consumes `@uzume/core/types` GDTF/MVR helpers and detects DMX channel patch
overlaps.

---

## 14. V2 Esports Integration (consumer-side)

This section documents the V2 esports tooling's consumption of Uzume libraries.
It is included in the Uzume specifications because it is the primary external
consumer of `@uzume/broadcast`, `@uzume/lumina`, and `@uzume/prism`, and because
the integration policies (primary package selection, NDI delegation, rollback
isolation) are set at the Uzume boundary.

The V2 esports tooling package `@v2/esports-tools` (`apps/v2/esports-tools/`)
consumes Uzume libraries — Uzume itself does not depend on V2. Two named
contracts live in that package.

#### V2 Esports Broadcast Pipeline

`esports-broadcast-pipeline.ts` imports from `@uzume/broadcast` and binds it as
V2's primary esports broadcast pipeline through
`buildV2EsportsBroadcastPipeline` (binding id
`v2.esports.uzume-broadcast-pipeline`, primary package `@uzume/broadcast`). The
pipeline composes the real broadcast subsystem engines —
`BroadcastMultiPlatformStreamingEngine`, `BroadcastSwitcherIntegrationEngine`,
`BroadcastGraphicsEngine`, `ReplaySystemIntegrationEngine`,
`SrtContributionFeedManagementEngine`, and
`StreamHealthMonitoringDashboardEngine`.

The requested-primary type is `'@uzume/broadcast' | 'obs' | 'ndi' | 'vmix'`, but
the contract pins `standaloneObsAsPrimary: false`,
`standaloneNdiAsPrimary: false`, and `standaloneVmixAsPrimary: false` —
`@uzume/broadcast` is always the program authority and NDI routing is delegated
to `@uzume/prism`. The one piece of OBS that is retained
(`obsWebSocketRetained: true`) is scoped purely to
`streamer-mode notification suppression`: the OBS WebSocket exists only to mute
desktop/overlay notifications for on-camera talent, never to drive the program
feed. That purpose is fixed by `V2_ESPORTS_BROADCAST_OBS_WEBSOCKET_PURPOSE`
(`streamer-mode-notification-suppression-only`) and emits the
`v2.streamer-mode.notifications.suppressed` event. The rollback policy is
`off-rollback-broadcast-operations` with `mayInfluenceRollback: false`.

#### V2 In-Arena LED + Projection Integration

`arena-led-projection-integration.ts` composes `@uzume/lumina` and
`@uzume/prism` through `buildV2ArenaLedProjectionIntegration` (binding id
`v2.esports.uzume-arena-led-projection`) for live-tournament in-arena LED walls
and projection mapping. Lighting and cue triggering come from `@uzume/lumina`
(`PixelMappingEngine`, `CueTriggerEngine`); LED-wall layout, projection mapping,
auto-alignment, and video routing come from `@uzume/prism`
(`LedWallLayoutDesignerEngine`, `ProjectionMappingEngine`,
`ProjectionAutoAlignmentEngine`, `VideoRoutingMatrixEngine`), with per-pixel
correction generated by `generatePerPixelCorrectionMap` and aligned panels
emitting `v2.esports.arena.projection.aligned`. Its rollback policy is
`off-rollback-arena-production-operations`.

Both bridges expose `mayInfluenceRollback: false` — they are
presentation/show-control state only and cannot affect deterministic match
simulation.

This integration is documented under `V2/docs/integration/` and is verified by
`V2/ue/Tools/check-v2-uzume-broadcast-pipeline.py` and
`check-v2-uzume-arena-led-projection.py`.

---

## 15. Acceptance Criteria

These criteria define what "done" means for a change to the Uzume domain. They
are ordered from most mechanical (type checking) to most structural (schema
consistency). All criteria must pass before a change is merged, not just the
ones affected by the specific change — the criteria are a holistic gate.

A change to the Uzume domain is acceptable when:

1. `npx tsc --noEmit` passes for each modified TypeScript library.
2. `npx vitest run` passes for each modified library's test files (`*.spec.ts` /
   `*.test.ts`).
3. `cargo test --workspace` and `cargo check --workspace` pass for changes to
   `uzume-protocol-engines`.
4. New domain objects, enums, or protocol types in `@uzume/core/types` ship with
   a matching Zod schema and `parse*` helper.
5. New event types are registered in `UZUME_EVENT_TYPES`,
   `UZUME_EVENT_SUBJECTS`, `UZUME_EVENT_PAYLOAD_SCHEMAS`, and
   `UzumeEventPayloadMap` together.
6. Database schema changes update `schema.ts`, the bootstrap migration SQL in
   `definitions.ts`, and `UZUME_CORE_TABLES`.
7. Subsystem engine modules added to a library are re-exported from that
   library's `src/index.ts`.
