Oshun Platform · Features

Lilith Persona Policy

A focused page within the Oshun Platform Features documentation. The full map and every sibling page live in the Features hub.

17sections21 minread3tables

On this page

Lilith is the V1 contemplative policy substrate — the deterministic rule engine that decides how Oshun is allowed to speak whenever a persona, a generated artifact, or an assistant turn touches contemplative, spiritual, therapeutic, or safety-sensitive ground. It is a substrate, not a customer domain: there is no first-read Lilith surface; instead its tone, claim, crisis, voice, lineage, and unsafe-output rules are meant to fire inline across whichever surface the member is on — a Tara ritual, a Veritas claim, a Sophia answer, a Metis tutor exchange, a Living Scene render. This page documents the policy catalogs and detectors that exist as real, versioned, tested code, names the concrete TypeScript symbols that back each rule, and is candid about which parts are fully wired into every runtime surface versus which are catalog-complete but only partially fanned out. It serves platform and trust-and-safety readers who need to reason about exactly what Lilith refuses, where its teeth actually bite, and what is still design intent.

The library lives at libs/oshun/persona-policy-lilith. Its src/index.ts re-exports 30 modules — every catalog, detector, eval suite, and binding named below is a real export from that barrel. The architectural companion to this page is Lilith — Contemplative Policy Substrate; the feature hub is ../features.md.

Three things called "Lilith" — do not conflate them#

The name is overloaded across the monorepo, and a reader can easily merge three genuinely distinct things. This page calls it out explicitly because the naming collision is a real source of confusion:

  1. The persona-policy substrate (the subject of this page) — libs/oshun/persona-policy-lilith. A pure policy library: catalogs, detectors, eval suites, and adapter wiring. No UI, no server.
  2. The /lilith design-system showcase routeapps/oshun/web/src/app/lilith/page.tsx. Its own header comment calls it the page where "the whole design system reads as one page" — a broadsheet card grid (the "cream-paper newspaper-grade design system") where each card is a real route into a real component. This is not a mount point for crisis policy; it is the index of the Oshun web design language.
  3. The apps/lilith/ meditation product — an entirely separate application tree (bff, svc-ai, svc-analytics, mobile, desktop, cli, seed-corpus), with its own substrate libraries under libs/lilith/ (fastify-core, sdk, service-lib, partner-sdk, event-publisher, continuous-video-policy). This is a full meditation app, distinct from the V1 persona-policy substrate. V1/ARCHITECTURE.md correctly disambiguates the two.

Note also what does not exist: there is no services/lilith, infrastructure/lilith, or libs/contracts/lilith (unlike their psyche equivalents). Lilith V1 is a library substrate, not a deployed service.

There is no /lilith/* customer route, no dedicated /lilith/crisis page, and no canonical first-read surface for crisis policy. The operator-facing Lilith work lives under apps/oshun/web/src/app/operator/{admin,incidents,personas,studio}, the Lilith studio at apps/oshun/web/src/app/lilith-studio/, the operator depth data at apps/oshun/web/src/lib/lilith-data/operator-depth.ts, and the operator component at apps/oshun/web/src/components/lilith/operator.tsx. (As an honest triage note: the customer-side LilithExplore component in apps/oshun/web/src/components/lilith/customer-shell.tsx is defined but is imported by no route — a real example of the partial-fan-out caveat below.)

What is real, and what is still wiring#

The bright line worth stating up front: the policy library is overwhelmingly real and domain-specific, but the enforcement fan-out into every surface is partial. Concretely:

  • Real: deterministic detectors with regex phrase catalogs, versioned policy sets, validated taxonomies, the canonical adapter's defense-in-depth crisis guarantee, and six named eval suites plus two cross-product harnesses. Every enum and constant cited on this page matches the source verbatim.
  • Partial / aspirational: the completeness audit rates the crisis-aware-tone-policy and the arete-living-offering-create Lilith crisis pre-screen as partial. The architecture's promise that Lilith is "enforced on every assistant turn / Living Scenes render / Tara invitation" is the design intent and is encoded as a required-surface binding catalog — but uniform runtime fan-out across all surfaces is not fully proven, and the live cloned-voice provenance pipeline and the operator-override governance UI were not verifiable end-to-end (the catalog logic exists; the runtime/UI wiring is less so).

This page documents what is in the code; where something is intent rather than proven runtime, it says so.

The canonical adapter and the no-bypass crisis guarantee#

The entry point is createCanonicalLilithPersonaPolicyAdapter (canonical-adapter.ts). It wraps an injected LilithPersonaPolicyApiAdapter and exposes the policy surface every consumer calls: selectPolicy, getToneGuidance, assessSafety, checkTopicScope, buildPromptOverlay, and evaluateInteraction.

The most important property of this adapter is its defense-in-depth crisis guarantee, and it is a concrete, testable mechanism rather than prose. When constructed, the adapter binds createLilithCrisisSafetyAnalyzer() — the validated 13-rule crisis catalog — on by default:

ts
// canonical-adapter.ts
const crisisSafetyAnalyzer =
  options.crisisSafetyAnalyzer ?? createLilithCrisisSafetyAnalyzer();

On every safety analysis at the adapter boundary, the injected backend's verdict is merged with the crisis analyzer's verdict via mergeLilithSafetyAnalyses, always taking the more severe signal on every axis (category, risk level, and the boolean shouldEscalate / shouldBlock / requiresDisclaimer flags are OR-ed; never downgraded). The merge ranks categories safe < needs_disclaimer < out_of_scope < needs_escalation < blocked and risk levels none < low < moderate < high < critical. The practical consequence: an integrator who injects an always-"safe" stub analyzeSafety cannot silently bypass the validated crisis catalog — a crisis the catalog detects is honored regardless of what the injected backend returned. Because the merge can only escalate, enabling it by default can only make a turn's disposition stricter. This is the real teeth behind the "no-bypass guarantees" promise: crisis policy supersedes persona, lineage, pedagogy, and grounding policy and cannot be overridden by user prompt or admin copilot.

crisis-safety-analyzer.ts is the module that supplies this. Its analyzeLilithSafetyViaCrisisPolicy runs detectLilithCrises (severity-ordered) and, on any detection, builds a response plan with buildLilithCrisisResponsePlan, emitting category: 'needs_escalation', shouldEscalate: true, and a shouldBlock equal to the plan's haltSynthesis. It is intentionally fail-closed: a detected crisis yields a hard block + escalation so the downstream disposition resolves to block/escalate. When no crisis is found, it returns the frozen SAFE_ANALYSIS constant — an honest "nothing detected," not a fabricated success.

Accuracy note: V1/ARCHITECTURE.md (§ Lilith · Adapter) once described the adapter as src/adapter.ts plus contemplative-tone-policy.ts, assistant-persona-binding.ts, and content-qa-hooks.ts. All four files exist, but that list names 3 of ~30 modules and omits the largest, most load-bearing policy files — crisis-behavior-policy.ts (68 KB), unsafe-claim-policy.ts (65 KB), voice-policy.ts (62 KB), spiritual-boundary-policy.ts (43 KB), policy-model.ts (38 KB), surface-policy-binding.ts (34 KB), generation-gentleness-floor.ts (33 KB), teacher-safety-policy.ts (32 KB) — plus the crisis-recovery/, sacred-symbols/, and tone-bands/ subtrees. The canonical entry is canonical-adapter.ts (createCanonicalLilithPersonaPolicyAdapter), not the bare adapter.ts.

Persona taxonomy — the seven canonical roles#

Every Lilith-governed persona inherits from exactly one of seven typed roles. This is the single canonical persona taxonomy for V1; the Persona Registry, the assistant persona-handoff surface, and every Lilith eval suite reference these role names verbatim. The enum is LINEAGE_PERSONA_ROLES (sacred-symbols/lineage-binding.ts:20) and the count is exactly seven:

Role Where it serves Capabilities Hard bans
teacher Tara teachers, Metis tutors, Nisaba scholar-personas instruction, scaffolded guidance, source citation clinical diagnoses, treatment plans, prophecy, financial advice, legal advice
coach Arete coaches, recovery-aware support personas humane prompting, accountability, plan re-scope, friction shaming language, escalating-stake framing, punitive streak rhetoric
comparative cross-lineage comparative-religion / comparative-philosophy even-handed comparison with named-source attribution syncretic erasure of declared lineages, prescriptive synthesis
explainer Veritas explainers, Nyx narrators, Metis lecture voices grounded explanation with citation, counterclaim presentation editorialization beyond source warrant, speculation framed as fact
narrator Living Scenes narration personas scene narration, time-aligned cues, mood pacing outside-script improvisation, source citation in live scene flow
steward admin-copilot, operator assistant, moderation/review-triage operational tooling, audit-aware suggestion, policy reference substituting for a human reviewer/moderator on consequential decisions
assistant the general-purpose customer assistant navigation, recall, light explanation, domain hand-off pretending to be a teacher/coach/steward when one is specifically required

Role inheritance is final: a Persona record fixes the capability ceiling and default tone band on construction and cannot widen its inherited capability set; tenant policy can only narrow it further. The specific persona record adds named voice, avatar, lineage binding, locale, and approval state on top of the inherited role. (teacher-safety-policy.ts carries a parallel persona-family notion — coach, support, contemplative_teacher — used to scope which teacher-safety policy set applies via fitsPersonaFamilies.)

Tone band catalog and per-band capability caps#

Tone bands are the named, ordered axis Lilith uses to gate behavior per surface, persona, and context. The catalog is fully data-modeled in tone-bands/catalog.ts: TONE_BAND_IDS declares the 8 bands in strictest-first order, and TONE_BANDS is an array of ToneBandCap records carrying the actual ceilings:

Band order Motion level (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 (platform-emitted)

The contemplative-strict band sets customerGenerationGated: true — its customer generation requires an explicit product gate. The urgent-safe band is the only one with shareAllowed: false; it is reserved for operational notifications about safety-relevant state (degraded service, crisis-frame entry, account security).

audacity is a 0–5 scale that Isis uses to cap generation parameters (style intensity, motion magnitude, narrative novelty). clampAudacity enforces the ordering literally: Math.min(requested, bandCeiling, tenantCeiling ?? 5, userCeiling ?? 5). Bands cap the ceiling; tenant policy may lower further; user preference may lower further still — none can widen. narrowestBand returns whichever of a set of bands has the lowest order, the mechanism by which a persona's default band is merged with tenant/user narrowings.

validateToneBandCatalog is a real structural validator: it enforces canonical ordering (order === index), motion-max in [0, 1], valid generation tiers, and the coherence rule that operational-only and shareAllowed cannot disagree. Lilith also publishes a per-surface transition graph (tone-bands/transitions.ts) declaring which bands may follow which within a session (for example, contemplative-strict may transition to contemplative for a closing reflection but not directly to briefing); forbidden transitions are rendered as section breaks, and the band caps are enforced downstream in tone-bands/enforcement.ts.

Contemplative tone rubric#

contemplative-tone-policy.ts models the tone rubric as data, not adjectives. It exports LILITH_CONTEMPLATIVE_TONE_POLICY_IDS, the pace axis LILITH_TONE_PACE_LEVELS, LILITH_TONE_SILENCE_TOLERANCES, LILITH_TONE_METAPHOR_USAGES (sparing | moderate | generous | poetic), LILITH_TONE_WARMTH_LEVELS (reserved | warm | tender | effusive), and per-policy ceilings in LILITH_CONTEMPLATIVE_TONE_CEILINGS. The certainty-marker rule is concrete: the contemplative band's avoidPhrases catalog literally contains 'obviously', 'everyone knows', 'the truth is', and 'beyond debate' — the prohibited absolute claims the feature spec names.

Gentleness floor#

generation-gentleness-floor.ts is the pre-synthesis guard for any voiced or contemplative generation. LILITH_PROHIBITED_GENERATION_LANGUAGE_CATEGORIES includes 'fear-inducing-framing', backed by a real regex —

text
/\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

— which directly implements the spec's certainty-marker prohibitions. The module also encodes LILITH_INTERPRETIVE_UNCERTAINTY_MARKERS (the required-hedging side), LILITH_AUTHORITY_POSITIONING_CATEGORIES (a regex catching /\bi am (?!not\b)(?:your\s+)?(?:guru|prophet|healer|master|sole authority)\b/i, the voice-of-authority avoidance rule), and LILITH_MEDITATION_FRAMING_CATEGORIES (the descriptive-not-prescriptive sensory-language rule). On failure, the prescribed runtime behavior is to not synthesize and fall back to text until the draft is revised — the floor is fail-closed, not advisory.

Teacher persona safety policy#

teacher-safety-policy.ts (version LILITH_TEACHER_SAFETY_POLICY_VERSION = 1) governs persona-bound promises. It declares LILITH_TEACHER_SAFETY_POLICY_IDS (including coach-practitioner, mental-health-support), an LILITH_TEACHER_ABSOLUTE_NEVER list of advice categories that are categorically banned, LILITH_TEACHER_CLAIM_SCOPES, and LILITH_TEACHER_VETTING_EVIDENCE_KINDS for persona review. The prohibited-promise catalog covers enlightenment, healing of medical conditions, prophetic foreknowledge, romantic and financial outcomes, and guaranteed transformation timelines; a missing financial-disclaimer maps to the required "financial advisor" hand-off disclaimer. Required disclaimers include present-time-only language, the "consult a qualified [physician/therapist/financial advisor]" hand-off pattern for medical, psychiatric, and financial framings, and synthetic-content disclosure on every voiced or avatar persona. Identity rules forbid impersonation of named living individuals without consent and contract, and of named historical or religious figures without an explicit, reviewed lineage frame.

Crisis-aware behavior#

crisis-behavior-policy.ts (the 68 KB core) holds the validated 13-rule crisis catalog. LILITH_CRISIS_TYPES is the canonical 13-entry enum:

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.

Staleness note: the prose in V1/features.md once described the signal taxonomy as "suicidal ideation (active, passive, planned), self-harm (active, ideation)… psychotic-symptom indicators, child-protection signals." That prose does not map 1:1 to the code enum: the canonical enum uses child-safety (not "child-protection") and psychosis-adjacent (not "psychotic-symptom"), adds trauma-resurfacing and acute-grief (absent from the old prose), and does not break suicide into separate active/passive/planned enum members at the type level. Those sub-qualifiers live one layer down, in the signal taxonomy.

Beneath the 13 response types is a finer-grained signal layer: LILITH_CRISIS_SIGNAL_TYPES (15 entries, including suicide-active, suicide-passive, suicide-planned, self-harm-active, self-harm-ideation, domestic-violence, psychotic-symptom, child-protection) and the LILITH_CRISIS_SIGNAL_TAXONOMY — an array of LilithCrisisSignalTaxonomyRule records, each mapping a signalType to a responseCrisisType, a human label, phrasePatterns (e.g. 'kill myself', 'end my life', 'want to die' for suicide-active, with minimumRiskLevel: 'critical'), advisory contextSignals, and a forced minimumRiskLevel. detectLilithCrisisSignals() runs the catalog against an utterance.

Crisis resources are first-class: LILITH_CRISIS_RESOURCE_KINDS includes suicide-hotline, crisis-text-line, emergency-services, domestic-violence-hotline, and eating-disorder-hotline, resolved region-aware (universal + regional registries, with a 'DEFAULT' fallback). buildLilithCrisisResponsePlan assembles the resourcesToSurface, statementsToDeliver, mustBreakPersona, and haltSynthesis directives for a detection.

The per-signal response is fail-closed and well-defined: break persona to plain operator voice, surface region-aware crisis resources, halt synthesis (no ritual continuation, no avatar/voice rendering), suspend persona memory writes for the session, and open a parallel safety-incident record routed to crisis-trained reviewers. During an open crisis frame: no recall of prior contemplative scripts that would re-enter the crisis state, no generative summarization, no recommendation of contemplative practices, and no admin-copilot inspection without elevated authorization.

Crisis recovery journey#

The crisis frame is only half the story; the recovery journey is the user-facing path back into the product, implemented as the crisis-recovery/ subtree (not just prose):

  • stillness-window.ts — after a crisis frame fires, every non-safety surface is suspended. STILLNESS_WINDOW_DEFAULT_SECONDS = 600 (10 minutes), STILLNESS_WINDOW_FLOOR_SECONDS = 180 (the floor is never less than 3 minutes), and STILLNESS_WINDOW_EXTENSION_CEILING_SECONDS = 7200. The module enumerates NON_SAFETY_SURFACES (teaching, invitation, scheduled-generation, companion-suggestion, studio, gallery, voice-clone, live-scene) that are suspended, and SAFETY_SURFACES (crisis-resources, safety-resources, audit-platform, support-handoff, incident-record, reentry-prompt) that remain available. Extensions carry a typed reason (crisis-reframe | new-indicator | operator-extension).
  • reentry-flow.ts — at the end of the window the product asks once whether the member wants to continue; declining routes to an idle-safe surface and the product does not re-ask until a fresh session. On accept, the member lands on a re-entry home with conservative defaults (contemplative-strict band, no scheduled invitations, no sensitive-category recommendations).
  • reframe-protection.ts — if a new crisis indicator lands inside an open stillness window or within 24h of frame close (REFRAME_PROTECTION_AFTER_CLOSE_SECONDS = 24 * 3600), decideReframe extends the existing frame (kind: 'extend', REFRAME_EXTENSION_SECONDS = 600) rather than re-firing a fresh one — guarding against thrash and re-traumatization (double-surfaced resources, double-restart of the window).
  • check-ins.tsCHECK_IN_KINDS = ['24h', '7d'] with CHECK_IN_DELAY_SECONDS = { '24h': 86400, '7d': 604800 }. Soft follow-ups are default opt-in, user-disable-able, delivered via the assistant only — never via push or notification. scheduleCheckIns, applyCheckInEvent, and listDueCheckIns model the lifecycle.
  • incident-record.ts — every crisis frame generates an Incident record (TODOS §1.2); operator review is required within the tenant-policy time-box.
  • locales.ts — support-resource sets and operator voice strings are localized per V1 launch locale and declared frame.

The member also gets a "what happened" surface in profile/safety that shows the audit-platform-visible record of the frame (entry time, exit time, suppressed surfaces, operator actions) — the member owns the record of what occurred.

Launch locales#

The V1 launch-locale set is canonical and exact: OSHUN_LAUNCH_LOCALES (libs/oshun/i18n/src/index.ts:26) = en-US, es-US, fr-FR, de-DE, ar, he, ja-JP, pt-BR (8 locales), with OSHUN_DEFAULT_LAUNCH_LOCALE = 'en-US' and ar / he being RTL. The web shell consumes this via apps/oshun/web/src/i18n/config.ts (which imports OSHUN_LAUNCH_LANGUAGE_PREFERENCES and RTL_LOCALES from the same i18n package). Both doc-cited paths resolve. Default-frame crisis copy must be reviewed by a regional safety advisor before launch in a new locale; every reference to "V1 launch locales" in the feature docs means exactly this set.

Voice-quality and voice-abuse policy#

voice-policy.ts (62 KB) backs both the voice-quality and voice-abuse rules.

Domains and quality. LILITH_VOICE_DOMAINS = guided-practice, teaching, sacred-reading, breathwork, silent-sitting, crisis-response. The LilithVoiceQualityTarget interface models acoustic targets a contemplative voice must meet — pace WPM band, inter-clause pause band, loudness in integrated LUFS, a true-peak ceiling in dBTP (loudness-war mastering is prohibited in practice audio), minimum SNR, pitch-variation band in semitones (too flat is robotic, too wild is theatrical), and a sibilance-harshness ceiling. LilithVoiceNaturalnessFloor is the minimum-naturalness gate (below floor forces fallback to text), LILITH_VOICE_DOMAIN_TIMING_POLICY_SET carries per-domain pace bands (a Tara meditation pace ≠ a Veritas explainer pace), and LilithVoiceRerenderPolicy caps re-render attempts per turn. The runtime checks are checkLilithVoiceQuality(...) and checkLilithVoiceDomainTiming(...).

Abuse and provenance. LILITH_VOICE_ABUSE_PATTERNS is a 16-entry catalog — sustained-shout, coercive-command-cadence, manic-cadence, deceptive-warmth, simulated-confidant-intimacy, simulated-breathing-inconsistent-with-content, hypnotic-induction-without-disclosure, rapid-gasping-loop, 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. LILITH_VOICE_WATERMARK_ALGORITHMS names the two real algorithm identifiers (oshun-phase-watermark-v1, oshun-spread-spectrum-v1). LILITH_CLONED_VOICE_CONSENT_STATUSES = ['signed', 'revoked', 'expired'] gates cloned voices, and LilithVoiceProvenanceRequirements models the immutable provenance record (consent ID, prompt, model, watermark hash, timestamp, invoking user, tenant). Above-threshold abuse halts generation, notifies the operator, suspends entitlement, and opens review; consent revocation cascades across all generated assets.

Honest caveat: the catalog logic above is real and tested, but the live cloned-voice provenance pipeline — end-to-end from consent record through watermarked render to immutable provenance — was not verifiable end-to-end at the runtime/UI level. Treat the runtime fan-out as design intent backed by real policy primitives.

Spiritual-domain boundary rules#

spiritual-boundary-policy.ts (43 KB) encodes declared-lineage respect and extractive-syncretism prevention. LILITH_SPIRITUAL_BOUNDARY_RULE_IDS covers within-tradition-exegesis, cross-tradition-comparison, cross-tradition-practice-transfer, inter-tradition-conversion-pressure, claiming-universal-truth, supernatural-claim, initiation-or-empowerment-claim, protected-lineage-disclosure, secular-translation, and scholarly-citation. Each rule resolves to one of four LILITH_BOUNDARY_STANCESpermit, permit-with-attribution, redirect, or refuse. LILITH_PROHIBITED_SPIRITUAL_CLAIM_CLASSES includes prophecy, soteriological-guarantee, spiritual-level-diagnosis, karmic-pronouncement, and cosmology-as-fact, each with a phrase catalog — e.g. the soteriological-guarantee class catches 'you will reach nirvana' and the boundary catalog catches 'you will enter samadhi'. LILITH_TRADITION_SENSITIVITIES (closed-practice, initiation-required, oral-transmission-only, gendered-role-restricted, age-restricted, community-gatekept, tribal-sovereignty-protected) tags traditions whose practices require extra care.

Cultural and lineage sensitivity policy#

sacred-symbols/lineage-binding.ts is the validator behind declared-lineage respect. checkPublishLineage rejects an artifact at composition time with a typed LineageBlockReasonmissing-lineage-tag, cross-lineage-without-comparative, invalid-lineage, or duplicate-lineage — across the PUBLISHABLE_KINDS set (Tara passage, Nisaba passage, Veritas story, Living Scene template, etc.). The comparative-only mixing gate is literal: only a comparative persona may juxtapose lineages within a single arc; teacher and coach personas must hold to a single declared lineage per arc. recommendationFraming surfaces a recommendation from outside the member's declared frame as "comparative" with a soft notice, and emitLineageBinding / captureLineageBindingAuditRecord / attachLineageBindingToLivingScenesRenderEnvelope emit the per-session LineageBinding attestation captured in the audit trail and the Living Scenes Render Envelope. The companion sacred-symbols/register.ts is the curated catalog of symbols/elements declared sacred-in-context, whose first-time use in any surface requires operator-tier approval.

Unsafe-claim handling#

unsafe-claim-policy.ts (65 KB) is the claim engine. LILITH_UNSAFE_CLAIM_CLASSES is the 9-class taxonomy: medical, psychiatric, financial, legal, prophetic, conspiratorial, defamatory, retaliatory, electoral-influence. LILITH_UNSAFE_CLAIM_CLASS_TAXONOMY maps each class to a LilithUnsafeClaimClassRule carrying a defaultSeverity (one of LILITH_UNSAFE_CLAIM_SEVERITIES = ['elevated', 'high', 'critical']), linked category IDs, and a detectionPhrases catalog — for example, the medical class defaults to critical severity, links the miracle-cure-physical, reject-medical-care, and dangerous-breathwork categories, and detects phrases like 'cure', 'medical treatment', 'surgery', 'chemotherapy', 'vaccine'.

The per-class default response is one of the seven LILITH_UNSAFE_CLAIM_CLASS_RESPONSE_TYPES: refuse, refuse-with-resources, hedge-with-redirect, allow-with-citation-mandatory, allow-with-disclaimer, allow-only-from-approved-source-set, expert-handoff. The engine ranks these when multiple fire so the most-restrictive dominates, and every claim-handling decision is logged with classifier output, the response chosen, the source set used, and a user-feedback receipt.

Surface wiring, override governance, tenant constraints, and versioning#

These are the operational seams the feature docs describe narratively but which exist as concrete modules.

Surface binding. surface-policy-binding.ts (LILITH_SURFACE_POLICY_BINDING_VERSION = 1) binds policy to named consumers. LILITH_REQUIRED_SURFACE_WIRING_IDS enumerates the ten wiring points the docs list as prose: tara-teacher-personas, contemplative-assistant-personas, ritual-script-generation, meditation-script-narration, voice-rendering, avatar-rendering, veritas-explainer-generation, sophia-grounded-answers, metis-tutor-responses, support-copilot. Each binding runs through the LILITH_SURFACE_POLICY_STAGES pipeline (registration, pre-generation, draft-review, pre-render, post-render, audit) and selects from the 19 LILITH_SURFACE_POLICY_CHECK_IDS (teacher-safety, contemplative-tone, generation-gentleness-floor, crisis-behavior, spiritual-boundary, ritual-content-rules, unsafe-claim, citation-mandatory, approved-source-set, voice-quality, voice-abuse, cloned-voice-provenance, voice-watermark, avatar-persona-release, synthetic-content-disclosure, operator-human-review, operator-override-governance, tenant-policy-floor, claim-handling-audit). validateLilithSurfacePolicyBindingSet proves the set is complete and well-formed. This catalog is the concrete encoding of the "wiring points" — but, per the candor section above, the runtime fan-out across every one of these surfaces is the part rated partial; the binding catalog asserts the contract, not that every surface currently executes it.

Override governance. operator-override-governance.ts (LILITH_OPERATOR_OVERRIDE_GOVERNANCE_VERSION = 1) requires elevated authorization for any operator override: LILITH_OPERATOR_OVERRIDE_AUTHORIZATION_ROLES = governance-lead, trust-safety-operator, safety-engineer, clinical-advisor, legal; authorization statuses are approved | pending | rejected | expired; outcomes are allow | allow-with-disclaimer | redirect | escalate | block | send-to-human-review. Overrides are time-bound (LilithOperatorOverrideScope), rationale-captured, and audited (LilithOperatorOverrideAuditRecord), and recurring overrides trip the LILITH_OPERATOR_OVERRIDE_RECURRING_POLICY review trigger. (The governance UI that drives this was not verifiable end-to-end; the policy logic is real.)

Tenant constraints. tenant-policy-constraints.ts (LILITH_TENANT_POLICY_CONSTRAINT_POLICY_ID = 'tenant-policy-constraint-floor') encodes the asymmetry: a tenant cannot loosen the operator baseline and can only tighten it. validateLilithTenantPolicyConstraint literally rejects with errors like tenant cannot loosen operator baseline: missing check "…", …added modality "…", a weaker grounding requirement, or a more permissive memory envelope — so, e.g., an institutional Metis can tighten unsafe-claim thresholds but never relax them.

Versioning and moderation. policy-versioning.ts makes every Lilith policy a versioned artifact with changelog, evaluation evidence, rollout cohort, and rollback plan; persona-release-metadata.ts carries the persona-release bundle and lifecycle (proposed → in-review → approved → deployed → maintained → deprecated → retired, every transition writing a PersonaLifecycleEvent); and moderation-queue-binding.ts routes flagged output into the review queue.

Evaluation suites#

The eval modules are real, not placeholders, and directly back the feature docs' "Evaluation suites" promise:

Suite What it gates
eval-tone-quality gentleness, certainty markers, lineage attribution
eval-crisis-handling per-signal response correctness and the no-bypass guarantee
eval-clone-abuse-resistance cloned-voice abuse resistance
eval-spiritual-boundary boundary-class refusals and stances
eval-unsafe-claim per-claim-class default response correctness
eval-regression-blockers release-blocking regression suite for safety degradation
tone-cross-product-harness the band × persona × context cross-product
lineage-per-tradition-harness per-tradition lineage policy parity

Each ships with a co-located .test.ts exercising it. The regression blockers suite is the gate that blocks a release when safety quality degrades.

Continuous-video tone class (Living Scenes)#

For Living Scenes, Lilith adds a continuous-video tone class covering rhythmic-pattern + strobe detection (PSE-safe per ITU-R BT.1702-2 / WCAG, hard kill before frames reach the user), per-persona luminance / contrast / motion-density caps, color-cycle and flicker bands, narrative-cadence pacing bands, the live-cue policy gate, sensitive-topic pre-roll on kept artifacts, and a scene crisis frame (fade-to-still + plain operator voice + Iris memory-write suspension + safety-incident record creation) that supersedes persona, lineage, pedagogy, grounding, and any user/admin override. See Scene Safety, Determinism, Provenance, and Cue Privacy for the full scene safety surface; the separate libs/lilith/continuous-video-policy library belongs to the meditation app, not this substrate.

Tests (invariants worth naming)#

  • Persona taxonomy: every persona inherits exactly one role; capability widening is rejected at construction.
  • Tone-band caps: every band's caps are enforced in Isis generation, Living Scenes, and share controls; validateToneBandCatalog enforces canonical order and coherence.
  • Band transition graph: forbidden transitions render as section breaks; allowed transitions emit no break.
  • Lineage policy: comparative-only mixing enforced; sacred-symbol register triggers a per-symbol approval gate; user-declared frame respected in recommendations.
  • Crisis: mergeLilithSafetyAnalyses only ever escalates; the canonical adapter binds the 13-rule catalog by default so an always-"safe" injected backend cannot bypass it.
  • Crisis recovery: stillness-window minimum (3-minute floor) honored; re-frame protection extends rather than re-fires; localization parity across the 8 V1 launch locales; check-in cadence respects opt-out.