Domain · Specifications

Calliope — Technical Specifications

Calliope uses TypeScript branded types for identifiers and names.

23sections21 minread

On this page

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 ArtistNameSchemaARTIST_NAME_REGEX, 2–50 chars, letters/digits/spaces/hyphens, no leading separator
StageName branded string StageNameSchemaSTAGE_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):

text
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:

text
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.

text
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 majorAb minor).
  • Instrument — 38 values (piano, acoustic-guitar, electric-guitar, bass, drums, strings, woodwinds, brass, synth family, world instruments, ensemble values).
  • ProductionAestheticraw | polished | lo_fi | experimental | maximalist | minimal.
  • SpatialAudioPhilosophyintimate_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:

  • MovementDnafluid | sharp | grounded | aerial | expansive | contained | explosive | languid.
  • FormationRolecenter | 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:

  • FrequencyRangesub_bass | bass | low_mid | mid | high_mid | presence | brilliance.
  • TimbreDescriptorairy | warm | bright | dark | breathy | gritty | velvet.
  • TextureDescriptorsilk | glass | velour | sand | metal | mist | grain.
  • VisualCueTypecolor | 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 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):

text
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:

text
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#

text
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#

text
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#

text
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#

text
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#

text
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#

text
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 lifecycleArtistConceived, ArtistBorn, ArtistProfileUpdated, ArtistActivated, ArtistHiatusStarted, ArtistHiatusEnded, ArtistRetired, ArtistLegacyProgramStarted, ArtistArchivePublished.
  • EraArtistEraPlanned, ArtistEraStarted, ArtistEraEnded.
  • VoiceVoiceModelCreated, VoiceModelEvolved, VoiceModelDeprecated.
  • SongSongConceived, SongGenerated, SongMixed, SongMastered, SongLyricsFinalized, SongQualityReviewed, SongReleased, SongArchived.
  • AlbumAlbumConceived, AlbumRecorded, AlbumTrackAdded, AlbumTrackRemoved, AlbumArtworkUpdated, AlbumReleased, AlbumArchived.
  • Music videoMusicVideoConceived, MusicVideoStoryboardApproved, MusicVideoGenerated, MusicVideoPostProduced, MusicVideoReleased.
  • ChoreographyChoreographyGenerated, ChoreographyRevised.
  • SocialSocialPostScheduled, SocialPostPublished, SocialPostDeleted.
  • FanFanMilestoneReached, FanSegmentUpdated, FanCampaignLaunched.
  • ConcertConcertAnnounced, ConcertScheduled, ConcertPerformed, ConcertCancelled.
  • LoreLoreEntryCreated, LoreRevealed, LoreCanonTierUpdated, LoreRetconned.
  • CollectibleCollectibleDropped, CollectibleSoldOut, CollectibleTransferred.
  • CollaborationCollaborationNegotiated, CollaborationStarted, CollaborationReleased, CollaborationCancelled.
  • Career phaseCareerPhaseForecasted, CareerPhaseTransitioned.
  • ReinventionReinventionTriggered, ReinventionCompleted.
  • Quality gateQualityGatePassed, QualityGateFailed, QualityGateOverrideApproved.
  • Brand partnershipBrandPartnershipFormed, 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.