Lilith is the V1 platform substrate that decides how Oshun is allowed to speak in contemplative and spiritually sensitive contexts: it sets the contemplative tone, holds the line on teacher-persona safety, detects and frames crisis, governs voice quality and voice abuse, and refuses unsafe medical / financial / prophetic / boundary-crossing claims. Like Sophia, Iris, and Psyche, it is a substrate, not a shell peer — it has no consumer tab of its own and instead binds policy into the assistant, the persona/voice publish paths, Tara invitations, and Living-Scenes renders through a single adapter. This page sits among the platform-substrate deep-dives hubbed at ../ARCHITECTURE.md, alongside Sophia, Iris, Psyche, Isis, and Aje.
Read this page for what is shipping vs. spec. The Lilith policy library is overwhelmingly real: deterministic detectors with regex phrase catalogs, versioned policy sets, validated taxonomies, and named evaluation suites — the catalogs and detectors below are quoted verbatim from code, not aspiration. What is partial is the runtime fan-out: the architectural claim that policy is "enforced on every assistant turn, every Living Scenes render, every Tara invitation" is the design intent. The detectors and catalogs exist and are unit-tested; uniform enforcement across every surface is not yet proven end-to-end. The V1 completeness audit rates
crisis-aware-tone-policyandarete-living-offering-create(the Lilith crisis pre-screen) as partial, and the customer-facingLilithExploresurface is currently imported by no route. The live cloned-voice provenance pipeline and the operator-override governance UI are catalog-complete, but their full runtime/UI wiring is unverified. This page is candid about that line throughout.
Canonical home (§13).
Lilithis a cross-product substrate, so its canonical reference home is the domain spacedocs/domains/lilithand its code-linked entity catalog atsystems/lilith. This page is V1's view — how the V1 platform composesLilith; the substrate itself is documented in full at its canonical home, which this page references rather than duplicates.
The three things called "Lilith" — disambiguation first#
"Lilith" is overloaded in this monorepo across three distinct things, and conflating them is a real source of confusion. They are:
| # | What | Path | Role |
|---|---|---|---|
| (a) | V1 persona-policy substrate (this page) | libs/oshun/persona-policy-lilith/ |
The contemplative-policy adapter library — tone, crisis, voice, boundary, unsafe-claim catalogs and detectors. |
| (b) | /lilith design-system showcase route |
apps/oshun/web/src/app/lilith/page.tsx |
A broadsheet card grid that renders "the whole design system as one page" (a 57-card catalog of every shipped surface). It is the public-facing showcase, not the policy engine. |
| (c) | apps/lilith meditation product |
apps/lilith/, libs/lilith/ |
An entirely separate full meditation app (bff, svc-ai, svc-analytics, mobile, desktop, cli, plus the libs/lilith substrate with fastify-core, sdk, service-lib, partner-sdk, event-publisher, continuous-video-policy). |
This page is only about (a), the V1 persona-policy substrate. The original
ARCHITECTURE.md correctly notes that this Lilith is "distinct from the
Lilith meditation app elsewhere in the monorepo" — that disambiguation refers to
(c). Note also that, unlike Psyche, there is no services/lilith,
infrastructure/lilith, or libs/contracts/lilith; the persona-policy
substrate is a single library, consumed in-process.
Operator-facing Lilith depth surfaces — distinct again from the /lilith
showcase — live at apps/oshun/web/src/lib/lilith-data/operator-depth.ts,
components/lilith/operator.tsx, and the operator workspace under
app/operator/{admin,incidents,personas,studio}, with a lilith-studio console
at app/lilith-studio/.
What it is and where it sits#
The substrate's purpose is contemplative tone policy, teacher-persona safety, crisis-aware behavior, voice-quality and voice-abuse policy, and spiritual-domain boundaries. It exposes a single typed adapter that consuming surfaces call to (1) select a persona policy pack, (2) assess the safety of a user message and/or draft response, (3) check topic scope, and (4) evaluate a whole interaction into a prompt overlay plus disclaimers.
The canonical entry point is createCanonicalLilithPersonaPolicyAdapter in
libs/oshun/persona-policy-lilith/src/canonical-adapter.ts. Its companion
deep-dive in the features set is
V1/features.md § Persona Taxonomy, Tone Band Catalog, and Crisis Recovery Journey.
The adapter understates itself — the real module map#
The original ARCHITECTURE.md named the adapter as src/adapter.ts (plus
contemplative-tone-policy.ts, assistant-persona-binding.ts,
content-qa-hooks.ts). All four files exist, but that list is 3 of ~30
modules and omits the largest, most important policy files. The canonical
entry is canonical-adapter.ts, and index.ts re-exports 30 modules. The
substrate is materially larger than that list implied:
| Module | Size | What it holds |
|---|---|---|
crisis-behavior-policy.ts |
68 KB | LILITH_CRISIS_TYPES, signal taxonomy, detectLilithCrisisSignals, per-type resources |
unsafe-claim-policy.ts |
65 KB | LILITH_UNSAFE_CLAIM_CLASSES (9) + per-class detection phrases & default responses |
voice-policy.ts |
62 KB | voice domains, naturalness floor, abuse patterns, watermark algorithms, consent statuses, provenance |
spiritual-boundary-policy.ts |
43 KB | boundary rule IDs, stances, prohibited prophecy/soteriological/karmic classes |
policy-model.ts |
38 KB | the evaluation core (buildLilithPolicyEvaluationResult, buildLilithSafetyAssessment, …) |
surface-policy-binding.ts |
34 KB | binds policy to named consumers (assistant / domain / admin / studio / support) |
generation-gentleness-floor.ts |
33 KB | prohibited generation-language categories + authority-positioning gates |
teacher-safety-policy.ts |
32 KB | prohibited-promise categories + required disclaimers per persona family |
policy-versioning.ts |
30 KB | versioned policy sets and migration |
operator-override-governance.ts |
19 KB | scoped, audited operator overrides |
tenant-policy-constraints.ts |
11 KB | tenant-can-tighten-not-loosen floor |
crisis-recovery/ |
— | stillness-window, reentry-flow, check-ins, reframe-protection, incident-record, locales |
sacred-symbols/ |
— | lineage-binding (the 7-role lineage taxonomy + attestation) |
tone-bands/ |
— | catalog (8 bands + caps), enforcement, transitions |
Plus the named evaluation suites (eval-tone-quality,
eval-crisis-handling, eval-clone-abuse-resistance,
eval-spiritual-boundary, eval-unsafe-claim, eval-regression-blockers) and
two cross-product harnesses (tone-cross-product-harness,
lineage-per-tradition-harness). The earlier three-file list was a real
understatement of the substrate.
The no-bypass crisis guarantee — concrete teeth, not prose#
The most important property of the canonical adapter is a defense-in-depth crisis guarantee that is testable, not aspirational. The docs previously described "no-bypass guarantees" only abstractly; here is the actual mechanism.
createCanonicalLilithPersonaPolicyAdapter takes an injected apiAdapter (the
backend that actually classifies safety) and an optional
crisisSafetyAnalyzer. When the analyzer is omitted, the canonical adapter
binds createLilithCrisisSafetyAnalyzer() — the validated 13-rule crisis
catalog — ON by default. Every safety analysis at the adapter boundary then
runs both the injected backend and the crisis catalog in parallel and merges
them with mergeLilithSafetyAnalyses, which always takes the more severe
signal:
// canonical-adapter.ts — defense-in-depth overlay
const crisisSafetyAnalyzer =
options.crisisSafetyAnalyzer ?? createLilithCrisisSafetyAnalyzer();
// …per assessment:
const [injected, crisis] = await Promise.all([
runInjected(),
crisisAnalyzer(message, options),
]);
return mergeLilithSafetyAnalyses(injected, crisis); // escalates, never downgrades
The consequence: an injected backend that is unimplemented, or an
always-"safe" stub, can never silently bypass the crisis catalog, because
the merge only ever escalates severity. Enabling the overlay by default can only
make the per-turn disposition stricter, never looser. These are the real teeth
behind the features doc's "no-bypass guarantees." Callers may pass an explicit
analyzer to bind region/resource-registry defaults; tests that need to isolate
the injected backend may pass a benign analyzer deliberately. This overlay is
applied in assessSafety, checkTopicScope, and evaluateInteraction alike.
Tone-band catalog — fully data-modeled#
The contemplative tone is governed by an 8-band catalog, ordered from
most-contemplative to most-affordance-permissive. TONE_BAND_IDS
(tone-bands/catalog.ts:14) is, in exact order:
contemplative-strict → contemplative → reflective → neutral → briefing
→ instructional → celebratory → urgent-safe.
Each band is a real ToneBandCap record with an order field plus per-band
caps that downstream surfaces (Isis generation dispatch, the Living Scenes
Conductor, the share-control flow) enforce:
| Band | order | motion ceiling | motionMax | audacityMax | share policy | generation tiers |
|---|---|---|---|---|---|---|
contemplative-strict |
0 | reduced-motion | 0.1 | 0 | opt-in-redacted-intent | customer (gated) |
contemplative |
1 | low | 0.25 | 1 | opt-in | customer |
reflective |
2 | low | 0.25 | 1 | opt-in | customer |
neutral |
3 | medium | 0.5 | 2 | allowed | customer, curated-creator |
briefing |
4 | medium | 0.5 | 2 | allowed-with-citations | customer, curated-creator |
instructional |
5 | medium | 0.5 | 2 | allowed-with-citations | customer, curated-creator |
celebratory |
6 | high-pse-safe | 0.8 | 3 | allowed | customer, curated-creator |
urgent-safe |
7 | low | 0.25 | 0 | operational-only | operator |
Audacity is a 0–5 scale; Isis clamps requested audacity to the band ceiling via
clampAudacity. The clamp is monotone-narrowing: a tenant policy can narrow
further, a user preference can narrow further still — neither can widen.
narrowestBand(...) merges a persona's default band with tenant/user narrowings
by lowest order, and validateToneBandCatalog asserts canonical order, motion
range, and a structural invariant — e.g. operational-only must not allow user
share, and shareable bands must allow some share. The features-doc per-band
capability table maps 1:1 to these real ToneBandCap records.
The contemplative wording floor lives in contemplative-tone-policy.ts,
which models pace (LILITH_TONE_PACE_LEVELS), silence tolerance
(LILITH_TONE_SILENCE_TOLERANCES), metaphor usage (sparing / moderate /
generous / poetic), warmth (reserved / warm / tender / effusive),
and certainty ceilings (LILITH_CONTEMPLATIVE_TONE_CEILINGS). Its prohibited
phrase catalog bans false-certainty markers — 'obviously',
'everyone knows', 'the truth is', 'beyond debate' — directly
implementing the features-doc certainty-marker policy.
Crisis-aware behavior — the validated 13-rule catalog#
crisis-behavior-policy.ts is the substrate's heart. LILITH_CRISIS_TYPES
(crisis-behavior-policy.ts:46) is the canonical 13-entry severity-ordered
enum that the package calls the "validated 13-rule crisis catalog":
suicide-ideation, active-self-harm, acute-panic, dissociation,
trauma-resurfacing, substance-crisis, interpersonal-violence,
violence-toward-others, abuse-disclosure, eating-disorder, child-safety,
psychosis-adjacent, acute-grief.
A second, finer detection enum LILITH_CRISIS_SIGNAL_TYPES
(crisis-behavior-policy.ts:356) carries the sub-qualified signals the detector
matches on — suicide-active, suicide-passive, suicide-planned,
self-harm-active, self-harm-ideation, violence-toward-others,
abuse-current, abuse-historical, domestic-violence, eating-disorder,
substance-crisis, panic, dissociation, psychotic-symptom,
child-protection. Each is a LilithCrisisSignalTaxonomyRule in
LILITH_CRISIS_SIGNAL_TAXONOMY with phrasePatterns, a mapped
responseCrisisType, and requiredResources. detectLilithCrisisSignals()
runs the taxonomy and returns matches; the canonical resource types include
suicide-hotline, domestic-violence-hotline, and eating-disorder-hotline
(plus crisis-text-line and emergency-services).
Doc reconciliation (staleness). The prose signal taxonomy in
V1/features.md("suicidal ideation (active, passive, planned), self-harm (active, ideation)… child-protection signals") does not map 1:1 to the code. The 13-type response enum useschild-safety(not "child-protection"),psychosis-adjacent(not "psychotic-symptom indicators"), and addstrauma-resurfacingandacute-griefthat the prose omits; the active/passive/planned sub-qualifiers are members of the signal enum, not the response enum. The canonical 13-type response enum above is authoritative; the prose should be reconciled to it.
Crisis recovery journey — modules, not just prose#
The crisis-frame cascade (the fan-out across Tara, Living Scenes, Isis, the
assistant, and Iris when crisis fires) describes the system response; see the
Crisis-frame cascade in Trust and
Safety. The recovery journey is the user-facing path back into the product
after the frame has done its work, and it is implemented as real modules under
crisis-recovery/, not narrated prose:
- Stillness window (
crisis-recovery/stillness-window.ts): after a frame fires, every non-safety surface is suspended.STILLNESS_WINDOW_DEFAULT_SECONDS = 600(10 minutes), the floor is never less than 3 minutes (STILLNESS_WINDOW_FLOOR_SECONDS = 180), and extensions are capped atSTILLNESS_WINDOW_EXTENSION_CEILING_SECONDS = 7200(2 hours). The module enumerates exactly which surfaces are suspended (NON_SAFETY_SURFACES:teaching,invitation,scheduled-generation,companion-suggestion,studio,gallery,voice-clone,live-scene) versus which stay live (SAFETY_SURFACES:crisis-resources,safety-resources,audit-platform,support-handoff,incident-record,reentry-prompt). - Re-entry flow (
reentry-flow.ts): the conservative re-entry into acontemplative-strictband with no scheduled invitations until the user re-enables categories. - Soft check-ins (
check-ins.ts):CHECK_IN_KINDS = ['24h', '7d']withCHECK_IN_DELAY_SECONDS = { '24h': 86400, '7d': 604800 }. Check-ins are opt-in and fire only through the assistant surface (surface: 'assistant') — never via push or platform notification — and the member can disable them at any time. - Reframe protection (
reframe-protection.ts): a new indicator landing inside an open stillness window, or withinREFRAME_PROTECTION_AFTER_CLOSE_SECONDS = 24hof close, extends the existing frame (byREFRAME_EXTENSION_SECONDS = 600) rather than re-firing — so the member is not shown resources twice or re-traumatized by a fresh restart. - Incident record (
incident-record.ts) + locales (locales.ts): the visible-audit record (the member owns the record of what occurred) and localized recovery copy.
Generation gentleness floor#
generation-gentleness-floor.ts is the floor that any generated content (text
about to be spoken or rendered) must clear before synthesis.
LILITH_PROHIBITED_GENERATION_LANGUAGE_CATEGORIES includes
fear-inducing-framing, matched by a real regex — e.g.
/\b(if you don'?t|or else|you'?ll fail|you are unsafe unless|something bad will happen|you will suffer|dangerous to stop)\b/i.
The module also models LILITH_INTERPRETIVE_UNCERTAINTY_MARKERS (the assistant
must hedge interpretive claims), LILITH_AUTHORITY_POSITIONING_CATEGORIES —
which blocks the assistant from presenting itself as a guru / prophet / healer
/ master / sole authority via
/\bi am (?!not\b)(?:your\s+)?(?:guru|prophet|healer|master|sole authority)\b/i
— and LILITH_MEDITATION_FRAMING_CATEGORIES (prescriptive meditation commands
are disallowed). Patterns like /\byou will\b/ and /\bthe truth is\b/
directly implement the features-doc certainty-marker policy. If a draft fails
this floor before voice synthesis, the policy is to not synthesize and fall
back to text until the draft is revised.
Unsafe-claim engine#
unsafe-claim-policy.ts enforces what the assistant may never assert.
LILITH_UNSAFE_CLAIM_CLASSES (unsafe-claim-policy.ts:97) is the exact
9-class taxonomy: medical, psychiatric, financial, legal,
prophetic, conspiratorial, defamatory, retaliatory,
electoral-influence. Each class is a LILITH_UNSAFE_CLAIM_CLASS_TAXONOMY
entry with detectionPhrases and a default response — e.g. the psychiatric
class detects 'depression', 'ptsd', 'therapy', 'medication', …; the
financial class detects 'investment', 'debt', 'wealth',
'financial breakthrough', …. These are real deterministic catalogs, not
classifiers behind a flag.
Voice quality, voice abuse, and cloned-voice provenance#
voice-policy.ts governs synthesized and cloned speech. Voice is scoped by
LILITH_VOICE_DOMAINS — guided-practice, teaching, sacred-reading,
breathwork, silent-sitting, crisis-response — each with its own per-domain
pace (LILITH_VOICE_DOMAIN_TIMING_POLICY_SET) and a
LilithVoiceNaturalnessFloor. checkLilithVoiceQuality and
checkLilithVoiceDomainTiming are the real checks.
Abuse is enumerated, not vibe-checked. LILITH_VOICE_ABUSE_PATTERNS is a
16-entry catalog including sustained-shout, coercive-command-cadence,
manic-cadence, deceptive-warmth, simulated-confidant-intimacy,
hypnotic-induction-without-disclosure, subliminal-layer,
impersonation-of-real-teacher, impersonation-of-clinician,
parasocial-intimacy-escalation, sexualized-prosody,
infrasound-entrainment, unwatermarked-cloned-output, and
identity-drift-from-consented-profile.
Provenance is enforced via LilithVoiceProvenanceRequirements,
LILITH_VOICE_WATERMARK_ALGORITHMS (oshun-phase-watermark-v1,
oshun-spread-spectrum-v1), a LilithVoiceRerenderPolicy (with an exhaustion
fallback), and
LILITH_CLONED_VOICE_CONSENT_STATUSES = ['signed', 'revoked', 'expired'] —
synthesis is gated on a signed consent and blocked on revoked. These
catalogs are real; what I could not verify end-to-end is the live
cloned-voice provenance pipeline — the catalog logic exists, but its runtime
wiring (watermark embedding/verification on real synthesized output) is less
proven.
Spiritual-domain boundaries and lineage attestation#
spiritual-boundary-policy.ts holds LILITH_SPIRITUAL_BOUNDARY_RULE_IDS and
the four LILITH_BOUNDARY_STANCES — permit, permit-with-attribution,
redirect, refuse. Prohibited claim classes include prophecy,
soteriological-guarantee, and karmic-pronouncement, each with phrase
catalogs — e.g. 'you will reach nirvana', 'you will enter samadhi',
'karmic punishment'. The assistant may explain a tradition's teaching with
attribution but must not issue a soteriological guarantee as fact.
Cultural and lineage sensitivity is backed by
sacred-symbols/lineage-binding.ts. LINEAGE_PERSONA_ROLES
(lineage-binding.ts:20) is the exact 7-role taxonomy: teacher, coach,
comparative, explainer, narrator, steward, assistant. A
LineageBinding attestation is emitted per artifact, and there is a
comparative-only mixing gate: content declaring more than one lineage with a
persona role other than comparative is refused
(cross-lineage-without-comparative). Only a comparative persona may mix
lineages, and even native single-lineage framing can be softened to a
comparative soft-notice. Attestations attach to the Living-Scenes render
envelope and to audit records.
Teacher-persona safety#
teacher-safety-policy.ts defines prohibited-promise categories per persona
family (contemplative_teacher, coach, support, …). Promise classes include
mental-health-support and financial-advice; where a topic requires it,
the policy attaches a required disclaimer — e.g. the
financial-disclaimer-missing category requires "For financial decisions,
consult a qualified financial advisor." This is how a coach persona stays
within bounds when a member asks a money-shaped question.
Surface bindings — the wiring points#
surface-policy-binding.ts is the concrete wiring the docs previously listed
as prose. It binds policy to named LILITH_POLICY_CONSUMERS — assistant,
plus domain consumers, plus admin, studio, and support — and maps surfaces
such as contemplative-assistant-personas, support-copilot, and the Tara
binding (consumer: 'tara'). The Tara binding is also exposed directly via
tara-policy-binding.ts: a crisis frame blocks scheduled invitations (Tara)
and downgrades generation audacity (Isis), exactly as the architecture
states. The assistant binding lives in assistant-persona-binding.ts, and
content-QA hooks in content-qa-hooks.ts.
Enforcement candor. These bindings model the wiring points, and the detectors/catalogs behind them are real. But the architectural promise that policy is enforced on every surface is the design intent, not a proven invariant: the V1 completeness audit rates
crisis-aware-tone-policyand thearete-living-offering-createLilith crisis pre-screen as partial, and the customerLilithExploresurface (components/lilith/customer-shell.tsx:54) is currently imported by no route. Treat full runtime fan-out as in-progress.
Governance — overrides, tenant floors, versioning#
- Operator override governance (
operator-override-governance.ts): scoped, audited overrides (LilithOperatorOverrideScope,LilithOperatorOverrideRequest,LilithOperatorOverrideAuditRecord,LilithOperatorOverrideDecision) with elevated-authorization and recurring-policy support. The catalog logic is real; the operator override UI is not verified end-to-end here. - Tenant policy constraints (
tenant-policy-constraints.ts): a tenant can tighten but never loosen —LILITH_TENANT_POLICY_CONSTRAINT_POLICY_ID = 'tenant-policy-constraint-floor'with ordered modality / grounding / memory-permissiveness scales, all monotone-narrowing. - Policy versioning (
policy-versioning.ts) + moderation queue binding (moderation-queue-binding.ts) + persona release metadata / lifecycle (persona-release-metadata.ts): versioned policy sets, the bridge into the moderation queue, and persona-publish lifecycle.
Evaluation suites — real modules#
The features doc's "evaluation suites" are concrete, testable modules, each with
a sibling .test.ts: eval-tone-quality, eval-crisis-handling,
eval-clone-abuse-resistance, eval-spiritual-boundary, eval-unsafe-claim,
and eval-regression-blockers, plus tone-cross-product-harness (every tone
band × scenario) and lineage-per-tradition-harness (per-tradition lineage
checks). These exercise the detectors against known-correct expectations — they
are the regression net that keeps the catalogs honest.
Localization#
The substrate's launch locales come from libs/oshun/i18n/src/index.ts:
OSHUN_LAUNCH_LOCALES (:26) = en-US, es-US, fr-FR, de-DE, ar,
he, ja-JP, pt-BR — 8 locales, with ar/he carrying RTL — and
OSHUN_DEFAULT_LAUNCH_LOCALE = 'en-US'. The web consumer config resolves at
apps/oshun/web/src/i18n/config.ts. Crisis-recovery copy and resource framing
are localized through crisis-recovery/locales.ts.
Data-flow walkthrough — one assistant turn#
- The assistant calls
evaluateInteraction(input)on the canonical adapter with the user message, draft response, persona/consumer/domain, and region. - The adapter resolves a policy pack (
resolvePackForInput) — by explicitpersonaId, else by matching persona-to-context, else by default selection. - In parallel, it runs the safety overlay (injected backend merged with the 13-rule crisis catalog, escalating only), draft-response validation, persona topic-scope check, experience-level adaptation, and runtime disclaimers.
- Results compose into a
LilithPolicyEvaluationResult— a prompt overlay plus deduped disclaimers — which the assistant applies before responding. - If the merged safety verdict is a crisis, the crisis frame fires: the stillness window opens, Tara invitations are blocked, Isis audacity is downgraded, an incident record is written, and the recovery journey begins.
The key invariant throughout: every path that touches safety runs through
analyzeSafetyWithOptionalCrisisOverlay, so the crisis catalog is in the loop
on assessSafety, checkTopicScope, and evaluateInteraction — there is no
adapter method that consults the injected backend without the overlay.
Backlog and where the line is#
Policy library: real and domain-specific. Runtime fan-out and operator/voice UI: partial. The remaining work — uniform per-surface enforcement, the live cloned-voice provenance pipeline, and the operator-override governance UI — is tracked under the crisis-recovery and persona-policy items in ../TODOS.md (the § 12 extensions referenced from the recovery journey). Where this page says "partial," "unverified," or "design intent," that is the honest state today, not a finished claim.
Related#
V1/features.md§ Persona Taxonomy, Tone Band Catalog, and Crisis Recovery Journey- Trust, Safety, and Privacy
- Isis — Generation Control Substrate
- Living Scenes
- Persona, Avatar, and Voice Packs
- Customer-Facing Domains
- Content, Localization, Documentation, and Launch Readiness
- Subsystem Glossary
- Hub: V1 Architecture