# Calliope — Technical Specifications

> Autonomous AI Artist Creation and Management Platform

This specification documents the contracts that exist in code today. The
canonical source of every shared type, schema, enum, event, and persistence
artifact is `libs/calliope/core` (`@calliope/core`). Per-domain entity schemas
(song composition, video production, physical media, etc.) live in the
respective `libs/calliope/<package>/src/types/` directories and are referenced
where relevant.

Calliope is implemented as 28 publishable TypeScript packages under
`libs/calliope/`. There is no `apps/calliope/` or `services/calliope/` directory
in the repository; the API surface is defined as an OpenAPI contract generated
from `@calliope/core` Zod schemas. This document covers the core type system,
persistence schema, event model, HTTP API surface, and configuration contract
that all 28 packages build on.

---

## Branded Identifier Types

Calliope uses TypeScript branded types for identifiers and names. Branded types
prevent accidental use of a raw `string` where a validated `ArtistId` is
expected — the type system enforces that every `ArtistId` has been parsed
through the Zod validator and passed its regex check. All three types are
defined in `libs/calliope/core/src/types/artist.ts`.

| Type         | Backing          | Validation                                                                                                |
| ------------ | ---------------- | --------------------------------------------------------------------------------------------------------- |
| `ArtistId`   | branded `string` | `ArtistIdSchema` — must match `UUID_V7_REGEX`; lowercased on parse                                        |
| `ArtistName` | branded `string` | `ArtistNameSchema` — `ARTIST_NAME_REGEX`, 2–50 chars, letters/digits/spaces/hyphens, no leading separator |
| `StageName`  | branded `string` | `StageNameSchema` — `STAGE_NAME_REGEX`, same rule as `ArtistName`                                         |

`UUID_V7_REGEX` enforces a version-7 UUID (`...-7xxx-[89ab]xxx-...`). Each type
exposes `parse*` (throwing) and `safeParse*` (`ZodSafeParseResult`) helpers.

---

## Domain Object: Artist

The `Artist` is the root aggregate of the entire domain. Every piece of output —
a song, a social post, a concert — is owned by an `Artist`. `ArtistSchema` is
defined in `libs/calliope/core/src/schema-index.ts`; the persisted shape is the
`calliope_artists` table in `libs/calliope/core/src/db-schema.ts`.

| Field         | Type                       | Notes                                                  |
| ------------- | -------------------------- | ------------------------------------------------------ |
| `id`          | `ArtistId`                 | UUID v7 primary key                                    |
| `name`        | `ArtistName`               | Legal/registered name; unique index on the table       |
| `stageName`   | `StageName`                | Public performing name                                 |
| `status`      | `ArtistStatus`             | Lifecycle enum (below)                                 |
| `personaSeed` | `PersonaSeed`              | Frozen identity seed; stored as JSONB with a GIN index |
| `currentEra`  | `string \| null`           | Free-text current-era label, 1–120 chars               |
| `careerPhase` | `CareerPhase`              | Career-stage enum (below)                              |
| `createdAt`   | ISO 8601 datetime (offset) | `timestamptz`, defaults to now                         |
| `updatedAt`   | ISO 8601 datetime (offset) | `timestamptz`, defaults to now                         |

`createArtist()` in `schema-index.ts` builds a valid `Artist` with a generated
UUID v7 and a default `PersonaSeed`/era for testing and prototyping.

### `ArtistStatus`

The `ArtistStatus` enum tracks the lifecycle state of an artist entity, from
initial conception through active career to archive. `ARTIST_STATUS_VALUES` /
`artistStatusEnum` (`calliope_artist_status`):

```
conception | active | hiatus | retired | archived
```

### `CareerPhase`

The `CareerPhase` enum captures where the artist is in her long-term career arc.
Each phase has a distinct strategic posture, content strategy, and set of
milestones — documented in `CAREER_PHASE_CONFIGS`. `CAREER_PHASE_VALUES` /
`careerPhaseEnum` (`calliope_career_phase`), defined in
`libs/calliope/core/src/types/career-phase.ts`:

```
underground | breakthrough | ascension | peak | experimentation | reinvention | legacy
```

`CAREER_PHASE_CONFIGS` ships a `CareerPhaseConfig` for every phase, each with a
`typicalDurationRange` (`minMonths`/`maxMonths`), `keyMilestones`,
`transitionTriggers`, and `contentStrategyAdjustments`. For example,
`underground` is 6–18 months with milestones `Identity lock-in`,
`First core fan nucleus`, `Signature style consistency`.

---

## Domain Object: PersonaSeed

The `PersonaSeed` is the immutable identity core of an artist — the set of
psychological, cultural, and creative parameters that all subsequent output must
be consistent with. Once parsed, the object is deep-frozen, so no code can
accidentally mutate it; any intentional evolution must go through the explicit
version-control flow in `@calliope/genesis`.

`PersonaSeedSchema` is defined in
`libs/calliope/core/src/types/persona-seed.ts`.

| Field                 | Type                   | Constraints                                                     |
| --------------------- | ---------------------- | --------------------------------------------------------------- |
| `coreValues`          | `string[]`             | 3–12 identity labels (1–120 chars), case-insensitively unique   |
| `emotionalPalette`    | `EmotionWeight[]`      | 3–12 entries; `weight`s must sum to 1 (±0.001); emotions unique |
| `artisticStatement`   | `string`               | Narrative text, 24–2000 chars                                   |
| `bigFiveTraits`       | `BigFiveTraits`        | Record keyed by `BigFiveTrait`, each value 0–1                  |
| `creativeMotivations` | `CreativeMotivation[]` | 1–9 entries, no duplicates                                      |
| `genreIdentity`       | `GenreSpecification`   | See below                                                       |
| `culturalBackground`  | `CulturalProfile`      | See below                                                       |
| `taboos`              | `string[]`             | 0–40 labels, unique; may not overlap `coreValues`               |

`EmotionWeight` = `{ emotion: string (1–120 chars), weight: number 0–1 }`.

### `BigFiveTrait`

The Big Five model is used to parameterize the artist's psychological profile.
Each trait maps to specific artistic behaviors: openness drives experimental
risk-taking, conscientiousness determines polish vs. rawness, and so on.

`BIG_FIVE_TRAIT_VALUES`:
`openness | conscientiousness | extraversion | agreeableness | neuroticism`.
`BigFiveTraits` is `Record<BigFiveTrait, number>` with values in the unit
interval.

### `CreativeMotivation`

`CreativeMotivation` encodes what drives the artist to create. Different
motivations produce distinctly different artistic postures — an artist driven by
`activism` behaves very differently from one driven by `legacy_building`.

```
identity_expression | emotional_transmutation | cultural_preservation |
sonic_innovation | narrative_worldbuilding | social_connection |
spiritual_exploration | activism | legacy_building
```

### `CulturalProfile`

The `CulturalProfile` grounds the artist in specific cultural traditions,
languages, and regional influences. It is the basis for authenticity validation
in `@calliope/genesis` — ensuring that an artist's expressed cultural identity
is internally consistent.

| Field                 | Type       | Constraints                             |
| --------------------- | ---------- | --------------------------------------- |
| `primaryCulture`      | `string`   | 1–120 chars                             |
| `culturalLineage`     | `string[]` | 1–12 entries, unique                    |
| `languages`           | `string[]` | 1–12 entries, unique                    |
| `regionalInfluences`  | `string[]` | 1–12 entries, unique                    |
| `valueAnchors`        | `string[]` | 1–12 entries, unique                    |
| `traditionReferences` | `string[]` | 0–20 entries, unique, defaults `[]`     |
| `sensitivityNotes`    | `string[]` | 0–20 notes (1–240 chars), defaults `[]` |

---

## Domain Object: GenreSpecification

`GenreSpecification` defines an artist's musical identity in terms of genre —
not just what genre she is, but what genres she avoids, what fusions she
embraces, and how her genre is expected to evolve over her career. The
`antiGenres` field is as important as `primaryGenre`: it documents the creative
boundaries that make the artist distinctive.

`GenreSpecificationSchema`
(`libs/calliope/core/src/types/genre-specification.ts`).

| Field                | Type                | Constraints                                       |
| -------------------- | ------------------- | ------------------------------------------------- |
| `primaryGenre`       | genre name          | 2–80 chars; may not appear in `antiGenres`        |
| `subgenres`          | genre name `[]`     | ≤10, unique; none may appear in `antiGenres`      |
| `fusionRules`        | `GenreFusionRule[]` | ≤12; no rule genre may conflict with `antiGenres` |
| `sceneAffiliation`   | `string[]`          | ≤20 (1–120 chars), unique                         |
| `antiGenres`         | genre name `[]`     | ≤20, unique — genres the artist explicitly avoids |
| `genreEvolutionPath` | `GenreShift[]`      | ≤16; no shift may reference an `antiGenres` entry |

- `GenreFusionRule` =
  `{ sourceGenres: 2–4 genre names, resultingGenre: genre name, rationale: 12–400 chars }`.
- `GenreShift` =
  `{ fromGenre, toGenre, trigger: 8–280 chars, confidence: 0–1 }`.

---

## Domain Object: SonicProfile

`SonicProfile` captures the musical parameters of an artist's sound: the tempos
she gravitates toward, her preferred keys, her instrumentation palette, her
production aesthetic, and her philosophy around dynamic range and spatial audio.
This is the document `@calliope/muse` reads when building a creative brief for
Euterpe.

`SonicProfileSchema` (`libs/calliope/core/src/types/sonic-profile.ts`).

| Field                    | Type                     | Constraints                               |
| ------------------------ | ------------------------ | ----------------------------------------- |
| `bpmRange`               | `BpmRange`               | `{ min, max }` positive ints, `max ≥ min` |
| `keyPreferences`         | `KeySignature[]`         | ≥1, no duplicates                         |
| `instrumentationPalette` | `Instrument[]`           | ≥1, no duplicates                         |
| `productionAesthetic`    | `ProductionAesthetic`    | enum (below)                              |
| `dynamicRange`           | `DynamicRange`           | `compressed \| natural \| extreme`        |
| `spatialAudioPhilosophy` | `SpatialAudioPhilosophy` | enum (below)                              |
| `referenceTrackIds`      | `string[]`               | defaults `[]`                             |
| `antiReferenceTrackIds`  | `string[]`               | defaults `[]`; no overlap with references |

The enumerations used by `SonicProfile` are defined alongside the schema:

- `KeySignature` — all 30 major/minor key signatures (`C major` … `Ab minor`).
- `Instrument` — 38 values (`piano`, `acoustic-guitar`, `electric-guitar`,
  `bass`, `drums`, strings, woodwinds, brass, synth family, world instruments,
  ensemble values).
- `ProductionAesthetic` —
  `raw | polished | lo_fi | experimental | maximalist | minimal`.
- `SpatialAudioPhilosophy` —
  `intimate_mono | center_stage | surround_immersion | dynamic_movement`.

---

## Domain Object: VisualProfile

`VisualProfile` defines the visual language of an artist's identity — the color
palette, typography, imagery style, fashion, and iconic elements that recur
across all her output. This is what `@calliope/visage` reads when generating
album art, promotional visuals, and fashion looks. The `characterLoraModelId`
field is the critical link to the trained LoRA model that enforces facial
consistency across all generated images.

`VisualProfileSchema` (`libs/calliope/core/src/types/visual-profile.ts`).

| Field                  | Type                  | Constraints                                                         |
| ---------------------- | --------------------- | ------------------------------------------------------------------- |
| `colorPalette`         | `ColorPaletteEntry[]` | ≥1; unique colors and unique semantic labels                        |
| `typographyStyle`      | `string`              | 1–160 chars                                                         |
| `imageryStyle`         | `ImageryStyle`        | `photorealistic \| illustrated \| anime \| abstract \| mixed_media` |
| `fashionAesthetic`     | `FashionProfile`      | silhouette/material/styling keyword arrays                          |
| `iconicElements`       | `IconicElement[]`     | ≥1; names unique (case-insensitive)                                 |
| `characterLoraModelId` | `string`              | non-empty — reference to the consistency LoRA                       |
| `photographyStyle`     | `PhotographyStyle`    | lighting/lens/framing descriptors                                   |

- `ColorPaletteEntry` =
  `{ semanticLabel: 1–80 chars, color: hex (#rgb or #rrggbb) }`.
- `FashionProfile` =
  `{ silhouetteKeywords[], materialPalette[], stylingDescriptors[] }`, each
  non-empty.
- `IconicElement` = `{ name: 1–120 chars, description: 1–500 chars }`.
- `PhotographyStyle` = `{ lightingApproach, lensLanguage, framingStyle }`, each
  1–160 chars.

---

## Domain Object: MovementProfile

`MovementProfile` captures the physical movement language of an artist — how she
moves, how she dances, and what role she plays in group formations. This is the
contract `@calliope/kinesis` builds on when generating choreography.

`MovementProfileSchema` (`libs/calliope/core/src/types/movement-profile.ts`).

| Field                     | Type                    | Constraints                                                 |
| ------------------------- | ----------------------- | ----------------------------------------------------------- |
| `movementDna`             | `MovementDna`           | enum (below)                                                |
| `idlePatterns`            | `IdleMovement[]`        | ≥1; `{ name, durationMs }`; names unique                    |
| `gesturalVocabulary`      | `Gesture[]`             | ≥1; `{ name, intensity 0–1 }`; names unique                 |
| `walkStyle`               | `WalkStyle`             | `{ posture, cadenceSpm, strideLengthCm }`                   |
| `culturalDanceTraditions` | `DanceTradition[]`      | `{ tradition, influenceWeight 0–1 }`; unique; defaults `[]` |
| `signatureMove`           | `SignatureMove \| null` | `{ name, description }` or `null`                           |
| `groupFormationRole`      | `FormationRole \| null` | enum or `null`                                              |

The enumerations used by `MovementProfile`:

- `MovementDna` —
  `fluid | sharp | grounded | aerial | expansive | contained | explosive | languid`.
- `FormationRole` — `center | lead | support | anchor | swing`.

---

## Domain Object: BeautyProfile

`BeautyProfile` captures the artist's hair and beauty identity — the
era-specific hair colors that signal artistic shifts, the signature makeup
elements that fans recognize, and the degree to which the artist embraces
transformative looks.

`BeautyProfileSchema` (`libs/calliope/core/src/types/beauty-profile.ts`).

| Field                       | Type                        | Constraints                                                              |
| --------------------------- | --------------------------- | ------------------------------------------------------------------------ |
| `signatureHairStyle`        | `HairStyle`                 | `{ styleName, texture }`                                                 |
| `hairColorTimeline`         | `HairColorEra[]`            | ≥1; `{ eraNumber, color: hex }`; unique era numbers, strictly increasing |
| `makeupIdentity`            | `MakeupProfile`             | `{ signatureLook, accentElements[] }`; accents unique                    |
| `signatureBeautyElement`    | `string`                    | 1–180 chars                                                              |
| `transformationWillingness` | `TransformationWillingness` | `never \| subtle \| moderate \| extreme`                                 |
| `nailArtStyle`              | `NailArtStyle \| null`      | `{ motifs[], colorPalette: hex[] }` or `null`; colors unique             |

---

## Domain Object: SynestheticProfile

`SynestheticProfile` is one of the most complex schemas in the domain: it
encodes the cross-modal rules that connect the artist's sound, color, movement,
and emotion into a single coherent system. `@calliope/synesthesia` reads this
profile when scoring every generated asset for cross-modal consistency.

`SynestheticProfileSchema`
(`libs/calliope/core/src/types/synesthetic-profile.ts`). The schema validates an
authored base and then `transform`s it, automatically deriving three fields when
they are not explicitly supplied.

Authored fields (must be provided explicitly):

| Field                   | Type                                       | Constraints                                |
| ----------------------- | ------------------------------------------ | ------------------------------------------ |
| `soundColorMap`         | `Map<FrequencyRange, hex>`                 | must cover all 7 `FrequencyRange` values   |
| `timbreTextureMap`      | `Map<TimbreDescriptor, TextureDescriptor>` | must cover all 7 `TimbreDescriptor` values |
| `chordMoodColorTriads`  | `ChordMoodColor[]`                         | ≥1; chord+mood pairs must be unique        |
| `keyColorAssignments`   | `Map<KeySignature, hex>`                   | —                                          |
| `bpmKineticEnergyMap`   | `Map<number, number 0–1>`                  | ≥1 entry                                   |
| `rhythmBodyPartMap`     | `RhythmBodyMapping[]`                      | ≥1                                         |
| `emotionCrossModalSpec` | `EmotionCrossModal[]`                      | ≥1                                         |

Derived-or-supplied fields (computed by `deriveMovementSoundPreferences`,
`deriveVisualEmotionalConnections`, `deriveDeviationAllowances` when not
provided):

| Field                        | Type                            | Constraints                                  |
| ---------------------------- | ------------------------------- | -------------------------------------------- |
| `movementSoundPreferences`   | `MovementSoundPreference[]`     | 2–5 entries                                  |
| `visualEmotionalConnections` | `VisualEmotionalConnection[]`   | 3–6 entries                                  |
| `deviationAllowances`        | `SynestheticDeviationAllowance` | five drift-tolerance values plus a rationale |

The enumerations used by `SynestheticProfile`:

- `FrequencyRange` —
  `sub_bass | bass | low_mid | mid | high_mid | presence | brilliance`.
- `TimbreDescriptor` —
  `airy | warm | bright | dark | breathy | gritty | velvet`.
- `TextureDescriptor` — `silk | glass | velour | sand | metal | mist | grain`.
- `VisualCueType` — `color | texture | silhouette | lighting | icon`.

`EmotionCrossModal` =
`{ emotion, color (hex), movementQuality, timbreDescriptor }`.
`SynestheticDeviationAllowance` carries `colorDriftTolerance`,
`keyColorDriftTolerance`, `movementEnergyTolerance`, `timbreTextureTolerance`,
`emotionalReframingTolerance` (each 0–1) and a `rationale`.

---

## Domain Object: EmotionalPalette

`EmotionalPalette` defines the emotional register an artist operates in: the
specific emotions she can express, how intensely she expresses them, where she
is vulnerable, and what triggers emotional shifts. This object is used by
`@calliope/pathos` when engineering the emotional dimension of a piece.

`EmotionalPaletteSchema` (`libs/calliope/core/src/types/emotional-palette.ts`).

| Field                    | Type                           | Constraints                                 |
| ------------------------ | ------------------------------ | ------------------------------------------- |
| `primaryEmotions`        | `Emotion[]`                    | 3–5 entries, no duplicates                  |
| `emotionIntensityRanges` | `Map<Emotion, IntensityRange>` | must cover every primary emotion            |
| `vulnerabilityMatrix`    | `VulnerabilityEntry[]`         | one entry per emotion; emotions ⊆ primaries |
| `emotionalTriggers`      | `EmotionalTrigger[]`           | each must link ≥1 primary emotion           |

The `Emotion` enum covers the full range of emotional states an artist might
express. It has 16 values:
`joy, trust, fear, surprise, sadness, disgust, anger, anticipation, awe, longing, defiance, serenity, melancholy, triumph, tenderness, restlessness`.

Supporting types:

- `IntensityRange` = `{ min, max }` in 0–1, `max ≥ min`.
- `VulnerabilityEntry` =
  `{ emotion, vulnerabilityLevel 0–1, disclosureThreshold 0–1, boundaryNotes? }`.
- `EmotionalTrigger` =
  `{ trigger, linkedEmotions[], intensityShift -1..1, copingDirective? }`.

---

## Domain Object: ArtistEra

An `ArtistEra` represents a distinct phase of an artist's creative life — a
period with its own sonic identity, visual language, movement style, and beauty
aesthetic. The era is the primary container that holds the full parameterization
of what the artist sounds like, looks like, and moves like at a given point in
her career. Multiple cross-field constraints are enforced to ensure internal
consistency across the era's profiles.

`ArtistEraSchema` (`libs/calliope/core/src/types/artist-era.ts`); persisted as
`calliope_artist_eras`.

| Field                | Type                    | Notes                          |
| -------------------- | ----------------------- | ------------------------------ |
| `eraId`              | UUID                    | —                              |
| `eraName`            | `string`                | 1–120 chars                    |
| `eraNumber`          | positive int            | —                              |
| `albumId`            | UUID, nullable/optional | —                              |
| `sonicParameters`    | `SonicProfile`          | —                              |
| `visualParameters`   | `VisualProfile`         | —                              |
| `synestheticProfile` | `SynestheticProfile`    | —                              |
| `movementProfile`    | `MovementProfile`       | —                              |
| `beautyProfile`      | `BeautyProfile`         | —                              |
| `startedAt`          | date (coerced)          | —                              |
| `endedAt`            | date, nullable/optional | must be later than `startedAt` |

Cross-field validation (`superRefine`) enforces internal consistency: every
`sonicParameters.keyPreferences` key must have a
`synestheticProfile.keyColorAssignments` entry; `walkStyle` cadence must fall
within `[bpmRange.min × 0.5, bpmRange.max × 2]`; and
`beautyProfile.hairColorTimeline` must contain an entry for this `eraNumber`.

---

## Domain Object: Song

A `Song` is a record of a conceived or produced piece of music, tracking its
production lifecycle from conception through mastering to release and stream
count accumulation. It is always owned by an `Artist` and optionally belongs to
an `Album`.

`SongSchema` (`libs/calliope/core/src/schema-index.ts`); persisted as
`calliope_songs`.

| Field             | Type                     | Notes                                         |
| ----------------- | ------------------------ | --------------------------------------------- |
| `id`              | UUID                     | —                                             |
| `artistId`        | `ArtistId`               | FK to `calliope_artists`, cascade delete      |
| `title`           | `string`                 | 1–200 chars                                   |
| `albumId`         | UUID, nullable           | FK to `calliope_albums`, set-null on delete   |
| `trackNumber`     | positive int, nullable   | —                                             |
| `durationMs`      | positive int, nullable   | —                                             |
| `genreTags`       | `string[]`               | defaults `[]`                                 |
| `bpm`             | int 1–300, nullable      | —                                             |
| `keySignature`    | `KeySignature`, nullable | —                                             |
| `moodTags`        | `string[]`               | defaults `[]`                                 |
| `lyrics`          | `string`, nullable       | —                                             |
| `creativeBriefId` | UUID, nullable           | —                                             |
| `audioAssetId`    | UUID, nullable           | (the DB column is free-text `audio_asset_id`) |
| `status`          | `SongStatus`             | defaults `conceived`                          |
| `releasedAt`      | datetime, nullable       | —                                             |
| `streamCount`     | nonnegative int          | defaults `0` (`bigint` column)                |

`SongStatus` tracks the production lifecycle of a song. `songStatusEnum`
(`calliope_song_status`): `conceived | generated | mixed | mastered | released`.

The `calliope_songs` table also carries a GIN full-text index over
`title + lyrics` to support catalog search.

---

## Domain Object: Album

An `Album` is a conceptual container for a collection of songs, tracking the
production lifecycle from conception through to release. The `AlbumType` enum
makes EPs and mixtapes first-class citizens alongside studio albums.

`AlbumSchema` (`libs/calliope/core/src/schema-index.ts`); persisted as
`calliope_albums`.

| Field                | Type               | Notes                        |
| -------------------- | ------------------ | ---------------------------- |
| `id`                 | UUID               | —                            |
| `artistId`           | `ArtistId`         | FK, cascade delete           |
| `title`              | `string`           | 1–200 chars                  |
| `albumType`          | `AlbumType`        | enum (below)                 |
| `eraId`              | UUID, nullable     | FK to `calliope_artist_eras` |
| `trackCount`         | nonnegative int    | defaults `0`                 |
| `conceptDescription` | `string`, nullable | 1–4000 chars                 |
| `coverArtAssetId`    | UUID, nullable     | —                            |
| `status`             | `AlbumStatus`      | defaults `conception`        |
| `releasedAt`         | datetime, nullable | —                            |

The `AlbumType` and `AlbumStatus` enums cover all standard release formats and
production stages:

- `AlbumType` (`albumTypeEnum` `calliope_album_type`):
  `studio | ep | mixtape | live | compilation | deluxe`.
- `AlbumStatus` (`albumStatusEnum` `calliope_album_status`):
  `conception | recording | mixing | mastering | pre_release | released`.

---

## Domain Object: CreativeBrief

The `CreativeBrief` is the structured request Calliope hands to a generation
engine (Euterpe for music, Isis for visuals, Aja for motion). It is how
Calliope's artistic intent is translated into actionable instructions for the
generation layer. A brief must always specify which type of output it is
targeting so that the correct quality thresholds are applied.

`CreativeBriefSchema` (`libs/calliope/core/src/types/creative-brief.ts`). The
schema `transform`s by defaulting `qualityThresholds` from
`QUALITY_THRESHOLDS_BY_OUTPUT[targetOutput]` when omitted.

| Field                    | Type                           | Notes                                         |
| ------------------------ | ------------------------------ | --------------------------------------------- |
| `artistId`               | `ArtistId`                     | —                                             |
| `era`                    | `ArtistEra`                    | —                                             |
| `targetOutput`           | `TargetOutput`                 | enum (below)                                  |
| `sonicDirection`         | partial sonic block, optional  | optional subset of `SonicProfile` fields      |
| `visualDirection`        | partial visual block, optional | optional subset of `VisualProfile` fields     |
| `emotionalDirection`     | emotional block, optional      | optional subset of emotional-palette fields   |
| `synestheticConstraints` | synesthetic block, optional    | optional subset of synesthetic-profile fields |
| `qualityThresholds`      | `QualityThresholds`, optional  | defaults per `targetOutput`                   |
| `constraints`            | `string[]`                     | defaults `[]`                                 |
| `antiConstraints`        | `string[]`                     | defaults `[]`; no overlap with `constraints`  |

Validation: a brief must declare at least one direction block (sonic, visual,
emotional, or synesthetic). Without at least one direction, the brief has no
artistic content.

### `TargetOutput`

`TargetOutput` identifies what type of creative output a brief is requesting.
This value is used to look up the appropriate quality thresholds in
`QUALITY_THRESHOLDS_BY_OUTPUT`. `TargetOutputSchema`
(`libs/calliope/core/src/types/quality-thresholds.ts`):

```
song | album | visual | video | social_post | concert | merch | lyric_video | teaser
```

---

## Quality Thresholds

Every generated output is scored by `@calliope/muse-gate` against a set of six
minimum quality thresholds. The thresholds are calibrated per `TargetOutput`
type — a concert has tighter identity-consistency requirements than a social
post, and a song has higher emotional-impact requirements than merchandise.

`QualityThresholdsSchema`
(`libs/calliope/core/src/types/quality-thresholds.ts`). A `QualityThresholds`
object is six minimum scores, each a finite number in 0–1:

```
minimumTechnicalScore
minimumEmotionalImpactScore
minimumNoveltyScore
minimumIdentityConsistencyScore
minimumReplayValueScore
minimumOverallAestheticScore
```

`QUALITY_THRESHOLDS_BY_OUTPUT` ships a calibrated threshold set for each of the
nine `TargetOutput` values. The table below shows representative values across
the most commonly used output types:

| Output      | technical | emotional | novelty | identity | replay | overall |
| ----------- | --------- | --------- | ------- | -------- | ------ | ------- |
| song        | 0.78      | 0.82      | 0.70    | 0.85     | 0.80   | 0.80    |
| album       | 0.80      | 0.84      | 0.74    | 0.88     | 0.81   | 0.83    |
| concert     | 0.84      | 0.90      | 0.77    | 0.90     | 0.87   | 0.88    |
| social_post | 0.68      | 0.72      | 0.66    | 0.80     | 0.70   | 0.74    |
| merch       | 0.72      | 0.70      | 0.69    | 0.82     | 0.68   | 0.76    |

`visual`, `video`, `lyric_video`, and `teaser` each carry their own calibrated
sets (see the source table). `concert` is the strictest profile because a live
performance is irreversible; `social_post` is the most permissive because
content velocity matters there.

`QualityScores` (`libs/calliope/core/src/types/asset-reference.ts`) is the
parallel measured-score object — the actual scores a gate evaluation produces,
with six 0–1 fields without the `minimum` prefix: `technicalScore`,
`emotionalImpactScore`, `noveltyScore`, `identityConsistencyScore`,
`replayValueScore`, `overallAestheticScore`.

---

## Domain Object: AssetReference

`AssetReference` describes a generated artifact stored in S3 — the metadata
record that tracks where a generated file lives, how large it is, which model
produced it, and what quality scores it received. Every approved output that
enters the release pipeline has a corresponding `AssetReference`.

`AssetReferenceSchema` (`libs/calliope/core/src/types/asset-reference.ts`).

| Field                | Type                       | Constraints                                                       |
| -------------------- | -------------------------- | ----------------------------------------------------------------- |
| `assetId`            | UUID                       | —                                                                 |
| `assetType`          | `string`                   | 1–80 chars, `[a-z0-9_-]+`                                         |
| `s3Bucket`           | `string`                   | 3–63 chars, valid S3 bucket name                                  |
| `s3Key`              | `string`                   | 1–1024 chars                                                      |
| `mimeType`           | `string`                   | valid MIME type                                                   |
| `fileSizeBytes`      | positive int               | —                                                                 |
| `width` / `height`   | positive int, nullable     | both set or both `null`                                           |
| `durationMs`         | nonnegative int, nullable  | —                                                                 |
| `generationMetadata` | `GenerationMetadata`       | `{ modelUsed, prompt, seed (nullable), parameters }`, passthrough |
| `qualityScores`      | `QualityScores`            | six measured 0–1 scores                                           |
| `createdAt`          | ISO 8601 datetime (offset) | —                                                                 |

---

## Schema Index

`CalliopeSchemaIndex` (`libs/calliope/core/src/schema-index.ts`) is a single
registry that makes it easy to access any schema and its parse helpers without
importing from deep paths. Each `SchemaToolkit` exposes `schema`, a `partial`
variant, `parse`, and `safeParse`. Registered keys are: `artist`, `album`,
`song`, `era`, `personaSeed`, `genreSpecification`, `sonicProfile`,
`visualProfile`, `synestheticProfile`, `movementProfile`, `beautyProfile`,
`emotionalPalette`, `qualityThresholds`, `creativeBrief`, `assetReference`. The
module also exports `createArtist`, `createSong`, `createAlbum`, and `createEra`
factory helpers.

---

## HTTP API Surface

There is no running `calliope-api` service in the repository. The REST contract
is generated from `@calliope/core` Zod schemas by
`libs/openapi/scripts/generate-calliope-spec.ts` (OpenAPI 3.1.0, served base
`https://api.oshun.io/calliope`). The generated paths are organized by resource
and nested under the artist they belong to.

### Artists

```
GET    /v1/artists                          — List artists (paginated)
POST   /v1/artists                          — Create artist
GET    /v1/artists/{artistId}               — Get artist
PATCH  /v1/artists/{artistId}               — Update artist
DELETE /v1/artists/{artistId}               — Delete artist
```

### Songs

```
GET    /v1/artists/{artistId}/songs              — List songs
POST   /v1/artists/{artistId}/songs              — Create song
GET    /v1/artists/{artistId}/songs/{songId}     — Get song
PATCH  /v1/artists/{artistId}/songs/{songId}     — Update song
DELETE /v1/artists/{artistId}/songs/{songId}     — Delete song
```

### Eras

```
GET    /v1/artists/{artistId}/eras               — List eras
POST   /v1/artists/{artistId}/eras               — Create era (with founding album)
GET    /v1/artists/{artistId}/eras/{eraId}       — Get era
PATCH  /v1/artists/{artistId}/eras/{eraId}       — Update era
DELETE /v1/artists/{artistId}/eras/{eraId}       — Delete era
```

### Social Posts

```
GET    /v1/artists/{artistId}/social-posts             — List social posts
POST   /v1/artists/{artistId}/social-posts             — Schedule social post
GET    /v1/artists/{artistId}/social-posts/{postId}    — Get social post
DELETE /v1/artists/{artistId}/social-posts/{postId}    — Delete social post
```

### Concerts

```
GET    /v1/artists/{artistId}/concerts                 — List concerts
POST   /v1/artists/{artistId}/concerts                 — Plan concert
GET    /v1/artists/{artistId}/concerts/{concertId}     — Get concert
PATCH  /v1/artists/{artistId}/concerts/{concertId}     — Update concert
DELETE /v1/artists/{artistId}/concerts/{concertId}     — Delete concert
```

### Analytics

```
GET    /v1/artists/{artistId}/analytics/overview       — Aggregate KPI overview
GET    /v1/artists/{artistId}/analytics/streaming      — Per-platform streaming analytics
GET    /v1/artists/{artistId}/analytics/engagement     — Social + fan-segment engagement
```

### API Schemas

`CalliopeOpenApiComponentSchemas`
(`libs/calliope/core/src/api/openapi-schemas.ts`) defines the request/response
component schemas used to build the OpenAPI contract. Each resource has a
dedicated set of request, response, and list response schemas:

- **Paging:** `PageQuery` (`page` ≥1 default 1, `limit` 1–100 default 20),
  `PaginationMeta` (`page`, `limit`, `total`, `totalPages`).
- **Artist:** `ArtistResponse`, `CreateArtistRequest`, `UpdateArtistRequest`,
  `ArtistListResponse`.
- **Song:** `SongResponse`, `CreateSongRequest`, `UpdateSongRequest`,
  `SongListResponse`.
- **Era:** `EraResponse` (includes `thematicPillars`), `CreateEraRequest`
  (includes `albumType`/`albumTitle`), `UpdateEraRequest`, `EraListResponse`.
- **Social post:** `SocialPostResponse`, `ScheduleSocialPostRequest`,
  `SocialPostListResponse`. The API platform enum here is
  `instagram | tiktok | youtube | x | threads` and the status enum is
  `scheduled | published | cancelled`.
- **Concert:** `ConcertResponse` (`venueName`, `city`, ISO `countryCode`,
  `capacity`, `ticketed`, status `planned | announced | performed | cancelled`),
  `PlanConcertRequest`, `UpdateConcertRequest`, `ConcertListResponse`.
- **Analytics:** `AnalyticsOverviewResponse`, `StreamingAnalyticsResponse`
  (per-platform: `spotify | apple_music | youtube_music | deezer`),
  `EngagementAnalyticsResponse`.
- **Errors:** `ErrorResponse` = `{ error: { code, message, details? } }`.

> Note: the API-layer social-post and concert schemas differ from the
> persistence-layer enums. The database `calliope_social_posts.platform`
> (`socialPlatformEnum`) is
> `instagram | tiktok | twitter | youtube | threads | bluesky`, and
> `calliope_social_posts.status` (`socialPostStatusEnum`) is
> `drafted | scheduled | published | deleted`. The API contract is the narrower
> surface.

---

## Domain Events

Calliope's event system records every significant state change as an immutable,
append-only event. Events are hash-chained (each event carries the SHA-256 hash
of its predecessor) to provide tamper evidence. They are published to Kafka and
serialized using Avro for schema-versioned, backward-compatible consumption by
downstream services.

All event infrastructure is defined in `libs/calliope/core/src/events/`.

### Event Types

`CALLIOPE_EVENT_TYPES` (`events/types.ts`) defines **69** event types
(`CALLIOPE_EVENT_TYPE_COUNT`). Events are PascalCase string constants grouped by
the aggregate they belong to:

- **Artist lifecycle** — `ArtistConceived`, `ArtistBorn`,
  `ArtistProfileUpdated`, `ArtistActivated`, `ArtistHiatusStarted`,
  `ArtistHiatusEnded`, `ArtistRetired`, `ArtistLegacyProgramStarted`,
  `ArtistArchivePublished`.
- **Era** — `ArtistEraPlanned`, `ArtistEraStarted`, `ArtistEraEnded`.
- **Voice** — `VoiceModelCreated`, `VoiceModelEvolved`, `VoiceModelDeprecated`.
- **Song** — `SongConceived`, `SongGenerated`, `SongMixed`, `SongMastered`,
  `SongLyricsFinalized`, `SongQualityReviewed`, `SongReleased`, `SongArchived`.
- **Album** — `AlbumConceived`, `AlbumRecorded`, `AlbumTrackAdded`,
  `AlbumTrackRemoved`, `AlbumArtworkUpdated`, `AlbumReleased`, `AlbumArchived`.
- **Music video** — `MusicVideoConceived`, `MusicVideoStoryboardApproved`,
  `MusicVideoGenerated`, `MusicVideoPostProduced`, `MusicVideoReleased`.
- **Choreography** — `ChoreographyGenerated`, `ChoreographyRevised`.
- **Social** — `SocialPostScheduled`, `SocialPostPublished`,
  `SocialPostDeleted`.
- **Fan** — `FanMilestoneReached`, `FanSegmentUpdated`, `FanCampaignLaunched`.
- **Concert** — `ConcertAnnounced`, `ConcertScheduled`, `ConcertPerformed`,
  `ConcertCancelled`.
- **Lore** — `LoreEntryCreated`, `LoreRevealed`, `LoreCanonTierUpdated`,
  `LoreRetconned`.
- **Collectible** — `CollectibleDropped`, `CollectibleSoldOut`,
  `CollectibleTransferred`.
- **Collaboration** — `CollaborationNegotiated`, `CollaborationStarted`,
  `CollaborationReleased`, `CollaborationCancelled`.
- **Career phase** — `CareerPhaseForecasted`, `CareerPhaseTransitioned`.
- **Reinvention** — `ReinventionTriggered`, `ReinventionCompleted`.
- **Quality gate** — `QualityGatePassed`, `QualityGateFailed`,
  `QualityGateOverrideApproved`.
- **Brand partnership** — `BrandPartnershipFormed`, `BrandPartnershipRenewed`,
  `BrandPartnershipEnded`.

### Event Envelope

Every Calliope event is wrapped in the same envelope, regardless of type. The
envelope carries routing metadata (artist ID as partition key), integrity
metadata (hash chain), and correlation metadata for distributed tracing.

`CalliopeDomainEventSchema` (`events/types.ts`) — every event carries:

| Field           | Type                       | Notes                                                     |
| --------------- | -------------------------- | --------------------------------------------------------- |
| `id`            | UUID                       | —                                                         |
| `type`          | `CalliopeEventType`        | one of the 69 event types                                 |
| `artistId`      | UUID                       | partition key                                             |
| `aggregateType` | `CalliopeAggregateType`    | one of 15 aggregates                                      |
| `aggregateId`   | UUID                       | —                                                         |
| `payload`       | type-specific (see below)  | —                                                         |
| `metadata`      | `CalliopeEventMetadata`?   | `correlationId`, `causationId`, `schemaVersion`, `labels` |
| `occurredAt`    | ISO 8601 datetime (offset) | —                                                         |
| `recordedAt`    | ISO 8601 datetime (offset) | —                                                         |
| `sourceSystem`  | `string`                   | 1–80 chars                                                |
| `sequence`      | positive int               | —                                                         |
| `previousHash`  | 64-hex SHA-256 or `null`   | hash-chain link                                           |
| `hash`          | 64-hex SHA-256             | this event's hash                                         |

`CalliopeAggregateType` enumerates all 15 aggregates:
`artist | era | voice_model | song | album | video | choreography | social_post | fan | concert | lore | collectible | collaboration | quality_gate | brand_partnership`.

### Event Payloads

The `CalliopeEventPayloads` map associates each event type with one of 16
payload schemas. All payloads are artist-scoped. The most commonly referenced
payload schemas are:

- `ArtistEventPayload` — `{ artistId, stageName?, reason?, metadata? }`.
- `SongEventPayload` —
  `{ artistId, songId, albumId?, assetId?, durationMs?, qualityScores?, metadata? }`.
- `QualityGateEventPayload` —
  `{ artistId, gateId, targetEntityType (song | album | video | post | concert | asset), targetEntityId, scoreSummary (`QualityScores`), thresholdSummary?, metadata? }`.
- `CareerPhaseEventPayload` —
  `{ artistId, fromPhase?, toPhase?, confidence?, metadata? }`.
- `CollectibleEventPayload` —
  `{ artistId, collectibleId, collectionId?, chain?, tokenId?, metadata? }`.

`parseCalliopeDomainEvent` / `safeParseCalliopeDomainEvent` validate the
envelope and then the payload against `getCalliopeEventPayloadSchema(type)`.

### Event Infrastructure

Four modules handle the mechanics of event storage, serialization, and delivery:

- **Hash chain** (`events/hash-chain.ts`) — `computeCalliopeEventHash`,
  `computeCalliopePayloadHash`, `sha256Hex`, `toCanonicalJson`, and a genesis
  hash constant; events form a tamper-evident chain.
- **Avro** (`events/avro.ts`) — `CALLIOPE_AVRO_EVENT_SCHEMAS`, Avro serializer
  and deserializer, an Avro Kafka publisher, an in-memory schema registry, an
  in-memory dead-letter queue, and `validateBackwardCompatibility`.
- **Kafka** (`events/kafka.ts`) — `CalliopeKafkaPublisherService`,
  `buildCalliopeKafkaHeaders`, `DEFAULT_CALLIOPE_KAFKA_TOPIC`, and an in-memory
  publisher for tests.
- **Topic config** (`events/topic-config.ts`) — `CALLIOPE_KAFKA_TOPIC_CONFIG`
  maps every event type to a topic, partitioned by `artist_id`. Topics fall into
  two retention classes: `operational_7d` (7-day retention) and
  `audit_indefinite` (no expiry). `CALLIOPE_KAFKA_CONSUMER_GROUPS` defines six
  consumer groups: `calliope.lifecycle.projections.v1`,
  `calliope.catalog.production.v1`, `calliope.fandom.engagement.v1`,
  `calliope.worldbuilding.lore.v1`, `calliope.partnerships.v1`,
  `calliope.audit.ledger.v1`.
- **Event service** (`events/service.ts`) — `CalliopeEventService`, an
  `InMemoryCalliopeEventStore`, and chain-verification reporting types.

---

## Persistence

Database: `calliope` (PostgreSQL; default DSN
`postgresql://oshun:oshun_dev@localhost:5432/calliope`). The schema is defined
with **Drizzle ORM** in `libs/calliope/core/src/db-schema.ts`; migrations live
in `libs/calliope/core/drizzle/` (`0000_cooing_cloak.sql`,
`0001_thin_puck.sql`). `libs/calliope/core/src/database.ts` provides a
primary/replica topology with separate OLTP and OLAP connection-pool profiles.

### Tables

The database has twelve tables. Each is listed below with its primary purpose:

| Table                        | Purpose                                                                          |
| ---------------------------- | -------------------------------------------------------------------------------- |
| `calliope_artists`           | Artist entities; `personaSeed` JSONB with a GIN index; unique name index         |
| `calliope_artist_eras`       | Per-artist era records; sonic/visual/synesthetic JSONB                           |
| `calliope_visual_assets`     | Visual assets (typed by `visualAssetTypeEnum`); S3 references, optional LoRA id  |
| `calliope_voice_models`      | Voice models (typed by `voiceModelTypeEnum`); provider + range; versioned        |
| `calliope_fan_segments`      | Anonymous fan segments (typed by `fanSegmentTypeEnum`); engagement score         |
| `calliope_social_posts`      | Scheduled/published social posts                                                 |
| `calliope_lore_entries`      | Transmedia lore entries (typed by `loreEntryTypeEnum`, `canonTierEnum`)          |
| `calliope_concerts`          | Concert records (typed by `concertTypeEnum`, `concertStatusEnum`)                |
| `calliope_artist_embeddings` | Identity embeddings, `vector(1536)` with an HNSW cosine index                    |
| `calliope_albums`            | Album concepts and metadata                                                      |
| `calliope_songs`             | Song records; GIN full-text index over title + lyrics                            |
| `calliope_collaborations`    | Collaboration records (typed by `collaboratorTypeEnum`, `collaborationTypeEnum`) |

### Persistence-Layer Enums

The database defines a richer set of enums than the application-layer schemas
expose — covering typed columns that appear in multiple tables. Beyond the
shared status enums already documented above, `db-schema.ts` defines:

- `visualAssetTypeEnum` (14 values): `character_sheet`, `album_cover`,
  `press_photo`, `social_post`, `music_video_frame`, `merch_design`,
  `fashion_look`, `hair_look`, `makeup_look`, `nail_art`, `photocard`,
  `vinyl_design`, `stage_design`, `concert_visual`.
- `voiceModelTypeEnum`: `singing | speaking | rap`.
- `voiceModelProviderEnum`: `elevenlabs | custom`.
- `fanSegmentTypeEnum`:
  `casual_listener | engaged_fan | superfan | advocate | dormant`.
- `socialPlatformEnum`:
  `instagram | tiktok | twitter | youtube | threads | bluesky`.
- `socialPostTypeEnum`: `text | image | video | story | reel | carousel`.
- `socialPostStatusEnum`: `drafted | scheduled | published | deleted`.
- `loreEntryTypeEnum`:
  `event | character | location | artifact | rule | prophecy | symbol`.
- `canonTierEnum`: `confirmed | implied | ambiguous`.
- `concertTypeEnum`:
  `virtual | livestream | hologram | ar | gaming_platform | hybrid`.
- `concertStatusEnum`: `planning | rehearsal | live | completed | archived`.
- `collaboratorTypeEnum`: `calliope_artist | human_artist | human_producer`.
- `collaborationTypeEnum`: `featured | co_write | remix | duet | produced_by`.
- `embeddingTypeEnum`:
  `sonic_identity | visual_identity | lyrical_style | personality | movement_style`.

### Vector Storage

`calliope_artist_embeddings` is a specialized table for artist identity vectors.
It uses the pgvector `vector` type at 1536 dimensions with an HNSW
`vector_cosine_ops` index. The `embeddingType` column selects one of the five
semantic dimensions (`sonic_identity`, `visual_identity`, `lyrical_style`,
`personality`, `movement_style`), enabling drift detection and identity
similarity search independently per dimension. `@calliope/core` also depends on
`@qdrant/js-client-rest`, making a Qdrant-backed vector path available; the
Drizzle schema itself models pgvector.

---

## Configuration

`CalliopeConfigSchema` and `CalliopeConfigService`
(`libs/calliope/core/src/services/calliope-config.ts`) load typed configuration
per environment (`development | staging | production | test`). Configuration is
not a flat list of environment variables — it is a structured object with
per-environment defaults, overridable through `CALLIOPE_*` environment
variables.

| Section                    | Contents                                                                                                                                                  |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `environment`              | resolved from `CALLIOPE_ENVIRONMENT` / `NODE_ENV`                                                                                                         |
| `aiModelEndpoints`         | `voiceSynthesis`, `imageGeneration`, `videoGeneration`, `textGeneration` URLs                                                                             |
| `s3Buckets`                | `audio`, `visual`, `video` bucket names                                                                                                                   |
| `cacheTtlsSeconds`         | `artistProfile`, `currentEra`, `socialPostQueue`, `fanEngagementCounters`, `concertState`                                                                 |
| `qualityThresholdDefaults` | `QualityThresholds` for `song`, `album`, `visual`, `video`, `social_post`, `concert`                                                                      |
| `rateLimits`               | `elevenLabs` (RPM + chars/month), `euterpeSongGeneration` (concurrency), `imageGeneration` (RPM/model), `videoGeneration` (concurrent jobs + queue depth) |
| `featureFlags`             | five `experimental*` flags (see below)                                                                                                                    |

### Notable Environment Variables

All AI model endpoint URLs are overridable: `CALLIOPE_VOICE_SYNTHESIS_ENDPOINT`,
`CALLIOPE_IMAGE_GENERATION_ENDPOINT`, `CALLIOPE_VIDEO_GENERATION_ENDPOINT`,
`CALLIOPE_TEXT_GENERATION_ENDPOINT`. S3 buckets: `CALLIOPE_S3_AUDIO_BUCKET`,
`CALLIOPE_S3_VISUAL_BUCKET`, `CALLIOPE_S3_VIDEO_BUCKET`. Cache TTLs and rate
limits each have a dedicated `CALLIOPE_CACHE_TTL_*` / `CALLIOPE_RATE_LIMIT_*`
variable. Quality thresholds can be overridden wholesale
(`CALLIOPE_QUALITY_THRESHOLD_OVERRIDES`, JSON) or per output
(`CALLIOPE_QUALITY_THRESHOLD_SONG`, `..._ALBUM`, `..._VISUAL`, `..._VIDEO`,
`..._SOCIAL_POST`, `..._CONCERT`). `CALLIOPE_DATABASE_URL` selects the database
DSN.

### Feature Flags

`CalliopeFeatureFlags` has five experimental flags, all of which are on by
default in development and test environments and all off in production except
the drift guard: `experimentalVoiceStyleTransfer`,
`experimentalImagePromptDistillation`, `experimentalVideoAutopilot`,
`experimentalCrossDomainCoCreation`, `experimentalAdaptivePersonaDriftGuard`. In
production all are off except `experimentalAdaptivePersonaDriftGuard`; in
development and test all five are on.

---

## Cross-Domain Integration

`@calliope/bridge` isolates Calliope from external Oshun domains. Rather than
importing external domain APIs directly (which would couple Calliope to their
interfaces and break whenever they change), every cross-domain call goes through
a typed adapter class in bridge that translates Calliope-native requests into
the external domain's expected format. Contracts are defined in
`libs/calliope/bridge/src/contracts/` and adapter classes in
`libs/calliope/bridge/src/services/`.

### Bridge Adapters

The table below maps each adapter to the external domain it wraps and what
Calliope uses it for:

| Adapter            | External domain role (as used by Calliope)                   |
| ------------------ | ------------------------------------------------------------ |
| `EuterpeAdapter`   | Music generation, audio engineering, mastering               |
| `IsisAdapter`      | Visual asset generation; character-consistency LoRA training |
| `AjaAdapter`       | Motion/choreography generation                               |
| `MayaAdapter`      | Virtual environments and venues                              |
| `AphroditeAdapter` | Live streaming / real-time performance delivery              |
| `HathorAdapter`    | Worldbuilding and lore knowledge                             |
| `SophiaAdapter`    | Cultural research and trend knowledge                        |
| `ThemisAdapter`    | Legal/compliance and rights management                       |
| `PsycheAdapter`    | Parasocial ethics and fan-wellbeing review                   |
| `AjeAdapter`       | Web3 / token economy infrastructure                          |
| `NyxAdapter`       | Cross-domain integration adapter                             |

Supporting bridge services: `CrossDomainEventBus`, `AudioToolsBridge`,
`KnowledgePipelineManager`, `VisualPipelineOrchestrator`,
`VirtuosoMigrationBridge`. The bridge also ships a `uzume` contract module.

### Infrastructure Dependencies

`@calliope/core` depends on the shared Oshun platform libraries `@oshun/cache`,
`@oshun/config`, `@oshun/database`, `@oshun/logging`, `@oshun/metrics`,
`@oshun/rate-limit`, `@oshun/storage`, and `@oshun/testing`, plus `drizzle-orm`,
`ioredis` (Redis), `kafkajs` (Kafka), `pg` (PostgreSQL),
`@qdrant/js-client-rest`, and `zod`.

---

## Acceptance Criteria

The following criteria define correctness for the Calliope core contracts. They
are designed to catch regressions in schema validation, event integrity, API
contract drift, and database migration correctness:

- Every shared schema parses through its `*Schema` Zod validator; `parse*`
  throws and `safeParse*` returns a `ZodSafeParseResult`. Cross-field invariants
  (era key↔color coverage, emotional-palette weight sum, persona
  core-value/taboo disjointness, etc.) are enforced by `superRefine` and covered
  by `*.spec.ts` alongside each type.
- `ArtistId`, `ArtistName`, and `StageName` reject inputs that violate their
  regexes; `ArtistId` requires a UUID v7.
- `CALLIOPE_EVENT_TYPE_COUNT` equals the number of `CALLIOPE_EVENT_TYPES`
  entries (69); `parseCalliopeDomainEvent` validates both the envelope and the
  type-specific payload.
- The hash chain links each event to its predecessor; chain verification detects
  tampering. Avro serialization round-trips and passes backward- compatibility
  validation.
- The OpenAPI contract is generated deterministically from
  `CalliopeOpenApiComponentSchemas`; `generate-calliope-spec.ts --check` fails
  if the committed spec drifts from the schemas.
- The Drizzle schema migrates cleanly; `calliope_artists.name` is unique, the
  `persona_seed` GIN index and `calliope_artist_embeddings` HNSW index exist,
  and FK cascade/set-null rules match `db-schema.ts`.
- `CalliopeConfigService.fromEnvironment()` resolves a fully typed
  `CalliopeConfig` for each of the four environments, applying `CALLIOPE_*`
  overrides where present.
