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; weights 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 transforms 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(albumTypeEnumcalliope_album_type):studio | ep | mixtape | live | compilation | deluxe.AlbumStatus(albumStatusEnumcalliope_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 transforms 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,limit1–100 default 20),PaginationMeta(page,limit,total,totalPages). - Artist:
ArtistResponse,CreateArtistRequest,UpdateArtistRequest,ArtistListResponse. - Song:
SongResponse,CreateSongRequest,UpdateSongRequest,SongListResponse. - Era:
EraResponse(includesthematicPillars),CreateEraRequest(includesalbumType/albumTitle),UpdateEraRequest,EraListResponse. - Social post:
SocialPostResponse,ScheduleSocialPostRequest,SocialPostListResponse. The API platform enum here isinstagram | tiktok | youtube | x | threadsand the status enum isscheduled | published | cancelled. - Concert:
ConcertResponse(venueName,city, ISOcountryCode,capacity,ticketed, statusplanned | 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) isinstagram | tiktok | twitter | youtube | threads | bluesky, andcalliope_social_posts.status(socialPostStatusEnum) isdrafted | 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, andvalidateBackwardCompatibility. - 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_CONFIGmaps every event type to a topic, partitioned byartist_id. Topics fall into two retention classes:operational_7d(7-day retention) andaudit_indefinite(no expiry).CALLIOPE_KAFKA_CONSUMER_GROUPSdefines 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, anInMemoryCalliopeEventStore, 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
*SchemaZod validator;parse*throws andsafeParse*returns aZodSafeParseResult. Cross-field invariants (era key↔color coverage, emotional-palette weight sum, persona core-value/taboo disjointness, etc.) are enforced bysuperRefineand covered by*.spec.tsalongside each type. ArtistId,ArtistName, andStageNamereject inputs that violate their regexes;ArtistIdrequires a UUID v7.CALLIOPE_EVENT_TYPE_COUNTequals the number ofCALLIOPE_EVENT_TYPESentries (69);parseCalliopeDomainEventvalidates 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 --checkfails if the committed spec drifts from the schemas. - The Drizzle schema migrates cleanly;
calliope_artists.nameis unique, thepersona_seedGIN index andcalliope_artist_embeddingsHNSW index exist, and FK cascade/set-null rules matchdb-schema.ts. CalliopeConfigService.fromEnvironment()resolves a fully typedCalliopeConfigfor each of the four environments, applyingCALLIOPE_*overrides where present.