V6 — Egbe, the agentic-companion universe — runs on a wager that an agent's life is worth reading. The Moirai kernel keeps thousands of Ori thinking off-screen, the Ori service records every consequential thing each of them does as a durable event, and then a player logs off for a week. When they come back, the world has moved without them: a bond formed, a feud sharpened, an agent reached a Crossroads, one transcended. Someone has to turn that pile of machine-recorded life-events into something a human wants to read — a recap, a biography, a gentle nudge toward a story still forming — and to do it without ever lying about what happened. That someone is Clio, named for the muse of history. Clio is the story engine: it ranks the significance of life-events, assembles the returning-player Chronicle as voiced narrative beats inside a few-second budget, surfaces emergent arcs before they conclude, writes the long-form Book of the Ori that becomes a memorial at death or transcendence, and reconciles unorderable concurrency conflicts into a coherent life. Its one inviolable rule is inherited from the Ori service it reads: Clio never invents events — every word of narration is a read over the authoritative log. This page is the deep companion to the "The Clio Story Engine" section of ../V6_ARCHITECTURE.md.
What ships, honestly#
The story engine is real, deterministic, and tested as a TypeScript library; the model that polishes prose is an injected, fail-loud seam; and the Rust "service" is currently a thin capability shell, not the orchestrator the monolith sketches. Three honest layers:
- Real and tested — the engine is the TS library. Every algorithm lives in
libs/v6/clio-story/src/index.ts(1,701 lines, zero external dependencies): significance ranking (rankChronicleEventsBySignificance,:445), the Chronicle (createReturningPlayerChronicle,:487), emergent-arc surfacing (surfaceEmergentArcPrompts,:683), the Book of the Ori (generateBookOfTheOri,:722), narrative reconciliation (reconcileOriConflictNarrative,:764), and Tier-2 density backfill (createClioDensityBackfillSummary,:819). These are pure functions over typed event inputs with domain-specific scoring formulas and stable ordering, exercised by 12 Vitest cases (index.spec.ts, 9;clio-narration.spec.ts, 3) that assert computed outcomes — rank order, a ≤ 3,500 ms budget, newest-significant-first streaming, chapter titles, keepsake render refs — not mere shape. - The generation seam is injected and fail-loud, never faked. The base
Chronicle produces deterministic, grounded template prose. The
model-narrated path (
createReturningPlayerChronicleNarrated,:636) takes an injectedClioNarrativeWriter(:605) — the V6 mount supplies Iris — and replaces each beat's prose with the model's. It then re-checks the model against the log: a writer that cites an event outside its beat's Ori-log slice throwsClioNarrationFabricationError(:614), and an empty narrative throws too (:665). The LLM may speak the life; it cannot invent one, and it cannot fake success. With no writer injected, the deterministic prose stands. - The Rust service is a thin shell — honestly less than the monolith
implies. The monolith bills
apps/v6/egbe-clio-service/as a "Rust orchestrator + batched LLM summarization." What is committed is a 68-line HTTP health endpoint (src/lib.rs) that binds port46106, answersGET /healthwith a capability list, and 404s everything else; itsCargo.tomldepends only onegbe-protocol, so it does not — and cannot — call the TypeScript engine. The orchestration and summarization logic genuinely lives in@oshun/clio-story; the Rust binary is today a service-discovery/health surface advertising the Clio capabilities, not the compute path.
One engine, two faces#
Clio is split the way most V6 subsystems are: a Rust process boundary for the fleet to discover and health-check, and a TypeScript library where the real work happens. The split is honest about where intelligence lives.
The Rust shell (apps/v6/egbe-clio-service/src/lib.rs:6) is a
ServiceDescriptor — name: "egbe-clio-service", port: 46106, and a
capabilities array (chronicle-generation, story-beat-ranking,
batched-narrative-beat-generation, book-of-the-ori,
narrative-reconciliation, …). health_document() renders it through the
shared health_json formatter (libs/v6/egbe-protocol/rust/src/lib.rs:1241),
exactly as every Egbe service does, so a fleet supervisor sees Clio as
{"service": "egbe-clio-service","status":"ok","port":46106,...}. That is the
entire Rust surface, and its one unit test only asserts the health document
names the service.
The library carries a V6PackageDescriptor (index.ts:22) — authority
'story' — enumerating 20 capabilities from significance-ranking through
tier2-density-summary-backfill, each backed by an exported function and a
test. clioStoryHasCapability (:441) is the typed predicate the verifiers and
the package's own descriptor test exercise. Everything below is in that one
file.
Significance ranking: the rubric that decides what matters#
Nothing in Clio is interesting until it can answer "of everything that happened,
what is worth telling first?" That is rankChronicleEventsBySignificance
(index.ts:445). It scores each ClioSignificanceCandidate — a projection of
one Ori event — against a curated, versioned rubric
(v6.clio.significance-rubric.1) whose five weights are real constants
(:449):
| Factor | Weight |
|---|---|
| Event type | 0.30 |
| Emotional weight | 0.22 |
| Arc relevance | 0.20 |
| Relationship impact | 0.16 |
| Steward relevance | 0.12 |
Event type is not a free input: eventTypeSignificance (:1581) maps the
17-value life-event taxonomy to a fixed scale, and the ranking is exactly the V6
ending hierarchy made numeric — Died 100, Transcended 96, Departed 92,
Crossroads 88, RelationshipChanged 82, … down to Discovered 44 and
Born 40. The other four factors are clamped 0–100, the weighted sum is rounded
to a totalScore, and compareRankedChronicleEvents (:917) breaks ties first
by recency, then by eventRef for total determinism. The top N (default 3)
become chronicleLeadEventRefs. This single function is the engine's spine: the
Chronicle leads with it, the stream prioritizes by it, and emergent-arc
surfacing borrows its clamping discipline. The test at index.spec.ts:41 pins
the produced order and the lead/non-lead split against known inputs, so a
regression to random or constant scoring would fail.
How narrative emerges from agent lives#
The returning-player Chronicle (createReturningPlayerChronicle,
index.ts:487) is where ranked events become a reading. The pipeline is
mechanical and auditable:
Grouping by life-thread. groupRankedEventsForChronicle (:938) keys
events by (agentRef, primary arcThreadRef) so a beat is always one agent
carrying one thread — never a jumble across agents — and chunks each group at
CHRONICLE_MAX_EVENTS_PER_BATCH = 6 (:56). Each ClioChronicleBatch records
a promptContract: 'read-over-ori-log-no-invention' (:1017): the contract is
in the data, not just the prose.
The few-second budget. Clio's summarization is the one latency-sensitive
thing it does, so it models its own cost. Each batch's estimatedGenerationMs
is
CHRONICLE_MODEL_BATCH_BASE_MS (420) + events × CHRONICLE_EVENT_SYNTHESIS_MS (24)
(:1014), and because batches generate in parallel, estimateReadyMs (:1137)
is CHRONICLE_BATCH_OVERHEAD_MS (220) + max(batch ms), compared against
CHRONICLE_READY_BUDGET_MS = 3_500 (:50). isLongChronicleAbsence (:1118)
flips the Chronicle from ready to streaming when the absence exceeds 7
game-days, the event count exceeds 18, or the estimate blows the budget. The
budget test (index.spec.ts:154) asserts estimatedReadyMs ≤ 3500; the
streaming test (:294) asserts long absences come back status: 'streaming'
with the stream ordered newest-significant-first and the first chunk readable
within budget. This is the same cost discipline Moirai applies fleet-wide, made
local to one read.
Beats in the agent's voice. createChronicleBeat (:1021) emits a
ClioChronicleBeat whose narrative weaves the grouped event summaries with
the agent's display name and arc-thread title, marks the lead beats, and carries
a voiceContext derived from the agent's voice summary — so the recap reads as
this agent's story, not a log dump.
The no-invention covenant#
The base beat prose is deterministic template assembly — safe, but flat. The
real prose pass is createReturningPlayerChronicleNarrated (:636). It keeps
ranking, budget, batching, and streaming exactly as produced, and replaces only
each beat's narrative with output from the injected ClioNarrativeWriter
(:605), handing the writer only that beat's Ori-log slice (logEvents,
built from sourceEventRefs, :647). Then it audits the model: every
citedEventRef the writer returns must be in the allowed set, or
ClioNarrationFabricationError (:614) is thrown naming the fabricated refs
(:662); an all-whitespace narrative throws as well (:665). The evidence tags
swap from read-over-ori-log-no-invention to
model-narrated-grounded-over-ori-log + fabrication-checked-against-ori-log
(:675). clio-narration.spec.ts proves all three branches: a faithful writer
is woven in, a writer that plants event:planted:not-in-log is rejected
(:89), and an empty writer fails loud (:108). This is V6's local copy of the
fair-play covenant V8's writers' room enforces against its mystery proof — the
model speaks, it does not author.
Citation membership is only the first check. The required
ClioNarrativeEntailmentVerifier extracts every factual claim, binds its
receipt to the SHA-256 of the exact trimmed narrative, anchors every extracted
claim as a verbatim span of that narrative, and assesses it against bounded
Ori-log passages. Missing or stale hashes, incomplete extraction, unanchored
claims, unknown passages, or any non-entailing claim throw
ClioNarrationEntailmentError; released beats carry
semantic-claim-entailment-checked-over-ori-log and their complete semantic
proof bundle.
Emergent-arc surfacing: stories worth a glance#
The Chronicle is retrospective; surfaceEmergentArcPrompts (index.ts:683) is
prospective. It reads the live event stream for ClioEmergentArcSignals of four
kinds — escalating-feud, forming-romance, struggling-agent,
fitting-wild-agent — and raises the worthwhile ones as gentle prompts
before they conclude. scoreEmergentArcSignal (:1465) is genuinely
kind-specific, not one formula renamed: a feud weights relationship tension 0.32
and conclusion-risk 0.20; a romance weights affinity 0.36 and inverts tension
((100 - tension) × 0.10); a struggling agent weights welfare risk 0.42; a
wild-agent fit weights household fit 0.40. Signals below minScore (default 65)
or already concluded are dropped (:1433); the rest are sorted, capped
(default 3), and rendered. Every prompt is typed tone: 'gentle',
requiresImmediateAction: false, beforeConclusion: true (:271), and the
copy is non-demanding by construction (gentleEmergentArcPromptText, :1521 —
"A quiet check-in could help if you choose"). Suppressed signals are returned by
ref so the surfacing is auditable. The test (index.spec.ts:632) asserts the
kinds that surface, the priority order, and that every prompt is gentle —
surfacing a demanding or post-conclusion prompt would fail.
The Book of the Ori and the legacy edition#
generateBookOfTheOri (index.ts:722) is the long-form biography — a
continuous read over an agent's whole event log. Events are sorted ascending,
chunked into chapters (default 4 events each), and titled by
bookOfOriChapterTitle (:1211): a chapter containing a Died/Transcended
event is "Legacy", an opening chapter with Born/Discovered is
"Beginnings", otherwise the dominant arc-thread names it. Coverage is
explicit — coverage.complete is true only when every covered ref equals every
input ref (:744) — so the biography provably omits nothing. The test (:363)
asserts the chapter sequence "Beginnings" → … → "Legacy", that the prose
contains specific events, and that coverage is total.
When a life ends, createBookOfOriKeepsake (:1266) detects the terminal event
and emits a ClioBookOfOriYemayaKeepsake — service: 'Yemaya',
editionFormat: 'book-of-ori-keepsake-edition', trigger of 'transcendence'
or 'death', an idempotency key, and status: 'requested'. Note the honest
seam: Clio does not render the keepsake — it emits a render request to
Yemaya (clio --> yemaya in the monolith topology). The library produces the
significance-ranked biography and the request; the media pipeline fulfills it.
The test (:509) pins the death-trigger keepsake's shape and render ref.
Narrative reconciliation: two truths, one coherent life#
V6's event store accepts commutative concurrent writes, but some pairs are
genuinely unorderable — two accounts of the same moment that both land. The Ori
service hands those to Clio, and reconcileOriConflictNarrative
(index.ts:764) resolves them narratively rather than by deleting either. It
writes a small connective beat (createNarrativeReconciliationBeat,
:1295) — itself an event of type 'Reflected', with
promptContract: 'small-connective-beat-no-invention' — that bridges the
conflicting accounts ("connects concurrent accounts … so the biography keeps
both events readable without replacing either", :1332). It folds that beat
plus both conflicting events into a merged Book of the Ori, records an
auditable ClioNarrativeReconciliationLogEntry (:1392,
auditable: true), and computes a coherence struct (:1409) that is
coherent only if the connective beat is included, both conflicting events are
covered, and the merged biography's coverage is complete. The reconciliation is
the one place Clio writes a new event — and it logs that it did. The test
(:517) asserts the connective beat's timestamp falls just after the conflict,
both events survive in the merged biography, and the bridge is logged.
Tier-2 density backfill: keeping culled agents in the story#
When a constrained device hits its embodiment cap, the world server stops
rendering distant agents — but they must not vanish from the narrative.
createClioDensityBackfillSummary (index.ts:819) takes the culled set and
emits a tier2-density-summary-backfill: it orders agents by cognition tier
(tierWeight, :1631 — Clotho 3 > Lachesis 2 > Atropos 1) then distance, names
the three representatives, and writes a headline plus beats that preserve goal
and relationship counts "behind the cap" so a later rehydration is believable.
It is a real summarization with a stable ordering and an empty-set branch, not a
placeholder string.
Verification and the readiness gate#
Clio carries five dedicated CI verifiers — verify:v6 clio-significance,
clio-chronicle, clio-emergent-arcs, clio-book-of-ori, and
clio-narrative-reconciliation (scripts/v6/verify-v6-clio-*.mjs). Read
honestly, these are source-presence and substring gates: e.g.
verify-v6-clio-chronicle.mjs asserts the engine source contains
createReturningPlayerChronicle, CHRONICLE_READY_BUDGET_MS = 3_500, the
streaming evidence tags, and the named test cases — they guard against the
symbols and contracts being deleted or renamed, while the behavioral
correctness is owned by the Vitest specs they require to exist. The five roll up
into the launch-blocking aggregate verify:v6 fate-legacy-clio-readiness,
backed by V6/release/fate-legacy-clio-readiness.v6release.json
(status: green, requiredBeforeLaunch: true, covering TODO sections 24–26).
Treat the verifiers as drift gates and the specs as the proof of behavior.
Where this connects#
- Upstream — the memory Clio reads. Clio narrates over the Ori service's
event-sourced biographies (
clio -->|reads event log| ori); theClioSignificanceEventTypetaxonomy and the unorderable-conflict handoff both originate there. See Ori: the biography service. - Sibling — how the agents speak. The Chronicle's voiced beats and the gentle arc prompts reach the steward through the conversation surface; the intent/voice pipeline is Vac: the communication pipeline.
- Sibling — where the lives end. Death and transcendence trigger Clio's Book of the Ori and the Yemaya keepsake; the ending lifecycle and the governed foundry that begins lives are The Foundry: endings and legacy.
- Cost — what the summarization is allowed to spend. Clio's batched beat generation runs as lower-tier model work under the same cost discipline its own 3.5 s budget mirrors; the fleet-wide scheduler is The Moirai kernel and cost tiering.