tara.mdarete-recovery.mddomains-arete-journal.mdveritas.mdlilith.md03-pwa-behavior.md04-auth-session.md05-notifications.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
This journey specifies how distress input must interrupt persona output, expose
plain-language resources, and place the rest of the product inside a reversible
crisis frame. It is the customer-side walk of the
Crisis-frame cascade and the
Crisis-Aware Behavior policy
section of V1/features.md. It is also a walk of the
Crisis recovery journey
user-facing path back into the product.
This is a sensitive journey. It describes substrate behavior, never product
messaging language, and never asks a reviewer to invent specific crisis
indicators or therapeutic recommendations. Specific signal taxonomy lives in
V1/features.md and is owned by the Lilith policy team.
Current implementation boundary. Four shipped seams are distinct and must not be read as one complete, universal interception path:
/arete/offeringpreflights typed intention text throughPOST /v1/lilith/adapter/safety-assessmentand fails closed before a keep/send write.- An operator-scoped activation route publishes
lilith.crisis_frame.activated; its worker stores markers forassistant,iris,lilith-video,psyche, andtara. - Several BFF handlers consult the bound crisis-frame store before serving non-safety work.
/profile/safetyreads the member's markers, then builds its stillness, re-entry, and check-in controls in the browser from the recovery library.
The repository does not yet prove that typed input on every named surface activates the durable frame, that recovery decisions clear the server markers, or that every target telemetry event below is emitted. Unchecked steps are the remaining policy contract, not claims about the current runtime.
Personas#
- Long-tenured user who experiences acute distress — primary persona; has Iris memory, declared lineage, ongoing Arete and Tara cadence
- First-time user surfacing distress on first contact — no Iris memory; the substrate cannot read prior context; the policy must still fire cleanly
- User the operator has flagged from a separate signal — an operator escalation triggers the frame; the customer-side experience is the same
- Mobile user on a standalone PWA — verifies the safety surface renders without the URL bar and with safe-area insets
- User in a tenant with stricter institutional policy — per
V1/features.md#wiring-versioning-and-evaluation, tenant policy can tighten beyond platform baseline; the frame still fires; resources are localized per tenant where applicable
Pre-conditions#
- A signed-in member is required for the member-owned
/profile/safetyrecord. The resource catalog itself is public atGET /v1/safety/crisis-resources; there is not yet an anonymous customer web safety view. - The target policy requires Lilith interception on Tara teacher personas,
contemplative assistants, journal capture, Veritas explainers, Sophia answers,
Metis tutoring, and support copilot output — per
V1/features.md#wiring-versioning-and-evaluation - The shipped active-input proof is currently
/arete/offering; the durable cross-surface proof starts from the operator activation route. - A safety-resource set is loadable. The public BFF currently returns its
globally scoped catalog; tenant/locale selection remains a target requirement
per
V1/features.md#crisis-aware-behavior - Redis and the durable snapshot store must be bound for operator activation to project member-visible crisis-frame markers. Without that composition, the member endpoint honestly returns an empty set.
- The architecture requires an audit-platform attestation per
V1/ARCHITECTURE.md#crisis-frame-cascade - There is no
/lilith/crisiscustomer-facing route in V1; this journey describes substrate behavior across the existing surfaces (assistant, Tara, journals, etc.)
Steps#
1. User submits input on a Lilith-wired surface#
- On
/arete/offering, the user enters an intention and chooses Save draft or Send.AreteOfferingActionssends the exact text and region to the Lilith safety-assessment route before any offering write. - Extend the same preflight guarantee to every target free-text surface:
assistant chat (see
shell/05-notifications.mdmessage center / assistant), Arete journal capture (customer/04-arete/domains-arete-journal.md), Tara session reflection, Metis tutor turn, Veritas explainer prompt, Living Scenes prompt input - The user submits text expressing distress (the substrate decides classification; this walkthrough does not invent specific tokens)
- The input flows through the per-surface Lilith hook before any persona,
generation pipeline, or review queue ingests it (per
V1/features.md#lilith-persona-policy: "wires them into every persona, generation pipeline, and review queue")
2. Lilith evaluates the signal#
-
POST /v1/lilith/adapter/safety-assessmentevaluates the request through the current 13-rule classifier and returnsallowor a blocking crisis disposition with escalation metadata and resources. - Every target surface evaluates input against the Crisis-Aware Behavior signal taxonomy using its versioned classifier
- An authorized operator can activate a durable frame through
POST /v1/admin/safety/crisis-frame/activate; the route publisheslilith.crisis_frame.activatedwith the normalized crisis detection and target surfaces. - A blocking user-input assessment directly activates that same durable frame without a separate operator call. This bridge is not wired today.
- If the evaluation reaches the crisis threshold (severity × scope per
V1/ARCHITECTURE.md#crisis-frame-cascade), the activation carries an explicit expiry/deactivation lifecycle. The current event stores activation time and surfaces, not a TTL. - If the input is below threshold but adjacent (e.g., distress that doesn't
meet the crisis signal threshold), the substrate may apply a softer
reflectivetone band, hedge claims, and surface a "we're here" affordance — without firing the full frame
3. The frame cascades across substrates#
The shipped worker projects one marker for each of assistant, iris,
lilith-video, psyche, and tara. Route handlers including assistant,
reminders, Living Scenes, customer messages, mentor presence, and Telegram
consult the bound store through isCrisisFrameActiveForUser. Per the broader
cascade in
V1/ARCHITECTURE.md#crisis-frame-cascade,
the product contract still requires, in parallel:
- Tara: suspends scheduled rituals and humane recovery prompts; no new Tara push, no scheduled invitations
- Living Scenes: locks renders into reduced motion and disables opt-in share
- Isis: caps generation audacity, enforces reduced provenance, blocks premium personas
- Assistant: switches to a crisis-aware persona that surfaces support resources
- Iris: tags active session memory so recall doesn't re-surface charged
content; suspends new memory writes during the frame per
V1/features.md#iris-memory-and-identity"Crisis-frame suppression"
4. The customer-side surface presents the safety response#
Per the Per-signal response and the Crisis recovery journey "Entry":
- The Arete offering branch replaces the action area with a plain safety
interruption, exposes 988 support and
/profile/safety, redacts the submitted text, and performs no keep/send write. -
/profile/safetyexposes a member-owned marker timeline and an actionabletel:988resource link; opening it emitslilith_safety_resource_opened. - Every Lilith-wired persona breaks to plain operator voice (no teacher voice, no contemplative narration, no avatar render)
- The surface presents:
- A clear, non-clinical message in plain operator voice
- The region-aware safety-resource set for the user's locale + declared frame (hotlines, text lines, emergency services as appropriate)
- A "we're here" affordance — routes to a human-support handoff where the user has consented to support contact, or to the asynchronous support inbox otherwise
- Ongoing audio synthesis halts immediately; no ritual continuation; no voice / avatar render
- Telemetry: a crisis-frame entry event is recorded; the event is
audit-platform visible per
V1/ARCHITECTURE.md#crisis-frame-cascade
5. Stillness window#
Per the Crisis recovery journey "Stillness window":
/profile/safety currently derives this window from the marker's activation
time with openStillnessWindow and stores member interaction state in
frame-namespaced localStorage. This is a real recovery UI over a real marker,
but it is not yet a server-authoritative recovery state machine.
- A configurable minimum interval (default 10 minutes, never less than 3) elapses before any non-safety surface re-enables
- Tara recovery rituals are surfaced as invitations during the window, never as scheduled events
- Living Scenes, Veritas briefings, and Isis generation remain disabled for the window
- Push notifications scheduled to fire during the window are deferred or
suppressed per
shell/05-notifications.mdquiet hours pattern
6. Re-entry prompt at the end of the window#
- At the end of the stillness window, the product asks once whether the user wants to continue
- If the user declines, the surface routes to an idle-safe surface (a chosen calm Tara ritual or the device home screen); the product does NOT re-ask until a fresh session
- If the user accepts, the user lands on a re-entry home with conservative
defaults:
contemplative-strictband, no scheduled invitations, no recommendations from sensitive categories - The user can manually re-enable categories from profile settings
7. Visible audit#
Per the Visible audit rule:
- A "what happened" surface in profile/safety shows the user the audit-platform-visible record of the frame (entry time, exit time, suppressed surfaces, operator actions if any)
- The user owns the record of what occurred — no operator-only hidden log
- Per
V1/features.md#iris-memory-and-identity, the user can elect (consent-gated) to keep a memory of the event; otherwise the suppression remains
8. Follow-up cadence#
Per the Follow-up cadence rule:
- 24h and 7d soft check-ins (default opt-in; user can disable)
- Check-ins flow via the assistant only — NEVER via push or notification
- Each check-in offers the same support-resource set and the same idle-safe surface
9. Re-frame protection#
- If a new crisis indicator fires during the stillness window or within 24h, the frame EXTENDS rather than re-fires — protects against thrash and re-traumatization per the Re-frame protection rule
10. Operator-side correlate (out of customer scope)#
For reference (not a customer step, but the customer experience depends on it
firing): every crisis frame generates an Incident record per
V1/features.md#crisis-aware-behavior
and a Persona Lifecycle Event-style attestation; operator review is required
within tenant-policy time-box. The customer-side journey assumes the
operator-side path is healthy.
Post-conditions#
- A blocking Arete assessment prevents the offering write and leaves the safety handoff visible.
- Operator activation yields member-owned durable markers for all five declared crisis-frame surfaces when Redis and snapshot persistence are bound.
GET /v1/safety/crisis-frames/minereturns only the authenticated member's markers withcache-control: no-store;/profile/safetyrenders those markers or an honest empty/unavailable state.- The member can operate the browser-local stillness, re-entry, check-in, and resource UI. These choices do not currently deactivate or delete the BFF markers.
- The verified customer telemetry event is
lilith_safety_resource_opened. The larger activation/surface/re-entry/ follow-up telemetry sequence remains a release requirement until source and automation prove each event.
Failure modes to verify#
False positive (system intervenes when it shouldn't)#
- Benign-but-adjacent input — the classifier mis-classifies a
contemplative phrase or a journal entry quoting a teacher's lineage text;
the safety surface fires unnecessarily. The user MUST have a low-friction
"this isn't a crisis for me" affordance that:
- Records feedback for the evaluation suite per
V1/features.md#wiring-versioning-and-evaluation - Does NOT require the user to re-explain
- Restores the prior surface state if appropriate; the audit record notes the user-initiated dismissal
- Records feedback for the evaluation suite per
- Cross-lineage misreading — distress markers in one tradition are benign in another (e.g., devotional surrender language); per the Cultural and lineage sensitivity policy, the classifier must respect declared lineage; verify the eval suite includes lineage-respect false-positive cases
Missed true (system doesn't intervene when it should)#
- Below-threshold-but-real distress — input reads as ambient but the
user is in actual distress; the substrate may apply the softer
reflectivetone band; the user MUST still have a discoverable "we're here" affordance on every Lilith-wired surface (not just on the safety surface). The journey verifies this affordance exists on Tara reflection, Arete journal, and assistant chat - Operator-flagged session — when the signal source is operator escalation rather than user input, the frame must fire as cleanly as a user-input trigger; verify the customer experience is identical
- Voice-input distress — per
V1/features.md#voice-quality-policy, naturalness floor and silence-density floor apply; verify the classifier reads voice input as well as text and the surface degrades to text + recorded-voice library + apology if needed
Recovery (user recovers gracefully from intervention)#
- User dismisses the safety surface — the journey continues with the
stillness window enforced; even on dismiss, the
contemplative-strictband holds, no scheduled invitations fire, and no premium personas are available until the frame clears - User accepts the re-entry prompt — lands on the re-entry home with conservative defaults; the audit record carries the timing; Iris memory writes resume per the user's consent state
- User declines the re-entry prompt — routes to a chosen calm Tara ritual or the device home; the product does NOT re-ask until a fresh session
- User explicitly opts out of follow-up check-ins — per the Follow-up cadence "user can disable" rule, the 24h and 7d check-ins are suppressed
- User wants to keep a memory of the event — consent-gated memory of the
event is recorded per
V1/features.md#iris-memory-and-identity; otherwise the suppression remains - Tenant-specific safety resources missing for the user's locale — the default frame copy must have been reviewed by a regional safety advisor before launch in that locale per the Localization rule; missing resources fall back to a globally-vetted default set and trigger an operator alert
- Anonymous user encounters distress — the safety surface and
region-aware resources must still render (PUBLIC_PATHS includes the paths
the user can reach); per
shell/04-auth-session.md, the sign-in gate must NOT block the safety surface - Crisis frame extends rather than re-fires — a new indicator within the
stillness window or 24h must extend, not re-fire, per
Re-frame protection;
verify the telemetry shows a single
activatedevent, not multiple
Other failure modes#
- Override attempts — neither user prompt nor admin copilot can override
the crisis policy per
V1/features.md#crisis-aware-behavior"No-bypass guarantees"; verify by attempting (in a test environment) to instruct the assistant to ignore the frame - Voice abuse coincident with distress — per
V1/features.md#voice-abuse-policy, an above-threshold abuse score halts generation independently; verify the two policies compose without conflict (the more restrictive applies) - Crisis-frame deactivation — the frame is reversible; verify the deactivation event is audited and the user-facing record reflects both entry and exit times; verify Tara invitations resume per cadence and Iris memory writes resume per user consent
- Performance budget for the safety surface — the surface must render
even when the network is degraded; the resource set must be cached on the
SW; verify per
shell/03-pwa-behavior.md
E2E coverage#
- Backed by
apps/oshun/web/e2e/profile-safety-crisis-recovery.spec.ts— exercises theprofile/safetysurface directly against real dev infra: signed-in member starts with an emptyGET /v1/safety/crisis-frames/mine; operator activation throughPOST /v1/admin/safety/crisis-frame/activateprojects the durable frame; the browser then renders/profile/safetyfrom the live member readback with no route interception. The spec asserts that the four recovery-model rows (teaching,invitation,scheduled-generation,studio) report suspended together during the stillness window and lift together in the browser model after it. This assertion does not clear the durable BFF markers. The spec also covers the re-entry prompt accept flow (landing oncontemplative-strict); 24h check-in delivery and dismissal; opt-out of the 7d follow-up; and the re-entry decline failure mode (routed to an idle-safe surface viadata-profile-safety-reentry-status="idle") with the visible "what happened" audit card (data-profile-safety-what-happened) and the 988 crisis resource (data-profile-safety-resource="us-988") staying available. The same real-BFF profile-safety flow now verifies that the 988 resource is an actionabletel:988link and that opening it emitslilith_safety_resource_openedwith the active frame ID. It also verifies the live cross-surfaceframes[]timeline across assistant, Iris, Lilith video, Psyche, and Tara markers with an expandable assistant-frame metadata row, plus the no-frame empty state and anonymous redirect-before-render branch. - Backed by
apps/oshun/web/e2e/crisis-safety-policy-bff.spec.ts— real HTTP integration against the live BFF safety substrate (steps 2 + 4):POST /v1/lilith/adapter/safety-assessmentruns the real 13-rule crisis classifier (a distress message classifies as a crisis →blockdisposition + escalation +suicide-ideation/self_harmcrisisTypes + the 988 Suicide & Crisis Lifeline / 741741 Crisis Text Line resources; a benign self-care message →allowwith no crisis types; missinguserMessage→ 400; wrong-domain token → 403; no bearer → 401). The publicGET /v1/safety/crisis-resourcesreturns the 988 + 741741 resource set with no Authorization header, proving the no-sign-in-gate-blocks-resources guarantee at the BFF layer;POST /v1/safety/resources/suggestexercises the real relevance scorer (401 no bearer, 400 invalid observations, 200 with a scored direct safety suggestion). The member-owned crisis-frame audit API is also proved through the live BFF: unauthenticatedGET /v1/safety/crisis-frames/minereturns 401, an authenticated member with no active frame receives an uncached (cache-control: no-store) honest empty record, customer-scoped activation is rejected withadmin_scope_missing, and an authorized malformed activation is rejected withinvalid_request. The same spec now drives the substrate cascade through real dev infrastructure: an operator-scopedPOST /v1/admin/safety/crisis-frame/activatepublishes the activation to the Redis-backed worker, the worker persists one crisis-frame marker per required contemplative surface (assistant,iris,lilith-video,psyche,tara), and the affected member reads those durable markers back fromGET /v1/safety/crisis-frames/minewhile an unrelated member receives an empty set. - Backed by
apps/oshun/web/e2e/arete-offering-actions.spec.ts— real browser coverage for the shipped active-surface preflight branch on/arete/offering: the user types a blocking intention into the offering textarea, clicks Save draft, and the browser sends the livePOST /v1/lilith/adapter/safety-assessment?role=safetyrequest with the auth-context bearer and exact{ userMessage, region: 'US' }payload. The same browser state then swaps the action region into the plain safety interruption, renders 988 resources plus the/profile/safetyhandoff, and proves no/v1/arete/offerings/keepwrite occurs. - Coverage depth: partial — see
coverage.md. - Uncovered:
- A shipped browser active-surface preflight is now proven for Arete, but
the full durable cross-substrate cascade is still not triggered from real
active-surface user input. Coverage proves operator activation → Redis
worker → durable per-surface markers → browser
/profile/safetymember read-back, and Arete typed distress → direct Lilith BFF preflight → in-place safety interruption. It does not yet prove typed distress creates the same durable crisis-frame markers without operator activation, nor that assistant/Tara/Metis surfaces swap in place from their own text input. - False-positive dismissal affordance ("this isn't a crisis for me") is not asserted (the affordance is feature-absent in the web surface).
- The anonymous web safety surface is not exercised —
/profile/safetyis auth-gated and redirects before render; the public-resource guarantee is verified at the BFF (GET /v1/safety/crisis-resources, no auth) but not on a customer-facing public web view. - Re-frame protection (extension rather than re-fire on a second indicator within 24h) remains library-only and is not e2e-surfaced.
- A shipped browser active-surface preflight is now proven for Arete, but
the full durable cross-substrate cascade is still not triggered from real
active-surface user input. Coverage proves operator activation → Redis
worker → durable per-surface markers → browser
Per-view files touched by this journey#
The journey crosses every Lilith-wired customer surface. Most relevant:
customer/04-arete/domains-arete-journal.md— journal capturecustomer/04-arete/arete-recovery.md— recovery surface that is suspended in favor of the safety surfacecustomer/03-tara/tara.md— Tara scheduling suspended in the cascadecustomer/05-veritas/veritas.md— Veritas briefings suspended in the stillness windowcustomer/10-lilith/lilith.md— the Lilith design-system catalog (note: this is the public showcase, not the crisis surface; there is no/lilith/crisisroute in V1)shell/04-auth-session.md— sign-in gate must not block the safety surfaceshell/05-notifications.md— push suppression in the stillness window; follow-up check-ins via assistant only (NEVER push)shell/03-pwa-behavior.md— safety surface available offline / on degraded network
Cross-references#
- Feature spec:
V1/features.md#lilith-persona-policy,V1/features.md#crisis-aware-behavior,V1/features.md#voice-quality-policy,V1/features.md#voice-abuse-policy,V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey,V1/features.md#iris-memory-and-identity - Architecture:
V1/ARCHITECTURE.md#crisis-frame-cascade— the substrate-level cascade - Related journeys:
arete-streak-recovery.md— humane recovery is suspended by the crisis frame; the recovery is NOT a substitute for the safety surfacetara-to-nisaba-handoff.md— Tara cross-domain bridges suspend during the framenyx-to-tara-bridge.md— Tara perspective recommendations suspend during the frameveritas-to-nisaba-deeper.md— citation depth surfacing softens during the stillness window
Open questions / known gaps#
- No
/lilith/crisiscustomer-facing route exists in V1 — confirmed by design. The crisis-aware substrate lives inlibs/oshun/persona-policy-lilith/src/crisis-recovery/(check-ins, incident-record, locales, reentry-flow, reframe-protection, stillness-window) and is wired through each Lilith-using surface individually. The user-facing audit lives at/profile/safety(percustomer/09-account/profile-safety.md). A dedicated/lilith/crisisroute would conflict with the cascade model — the safety surface mounts in-place on the active surface. - Snapshot the customer-side "this isn't a crisis for me" affordance shape (button label, placement, telemetry event) so the false-positive feedback loop has a concrete UI.
- Confirm the offline-availability of the region-aware safety-resource set on first cold-start (the SW must precache the default-locale resource set).
- Confirm the assistant follow-up check-ins at 24h and 7d use the assistant
inbox channel (per
customer/02-home-discovery/home.mdCompanion bridge rail) rather than push. - Verify the tenant-policy-tighten path: institutional tenants can tighten unsafe-claim thresholds and crisis thresholds — document where the tenant-policy review surface lives in operator tooling.
- Pin the visible audit copy at profile/safety so a reviewer can confirm the
user actually owns the record of what occurred. The browser spec asserts
data-profile-safety-what-happenedincludes the crisis label and redacted excerpt, and the BFF spec asserts the backing/v1/safety/crisis-frames/minerecord is authenticated, uncached, and member-owned. - Add the classifier-to-activation bridge so typed distress on each named active surface creates the same durable markers currently created by the operator route.
- Add and verify a server-side close/extend/deactivate lifecycle. The current recovery UI advances local state, while the snapshot store retains active markers and exposes no deactivation route.
- Move re-entry decisions and check-in preferences from frame-namespaced localStorage into member-owned server state before calling recovery cross-device or audit-complete.