Context. surface customer · domain account · route /profile/safety · auth signed-in · source apps/oshun/web/src/app/profile/safety/page.tsx
Last walked. 2026-06-26 submitted user-report receipts — signed-in reporter creates a live /v1/user-reports record, moderator decisions it through /v1/admin/user-reports/:reportId/decision, and /profile/safety renders the actioned receipt, including the /messages decision-notification deep-link return to /profile/safety?reportId=<id>, in user-flag-to-review.spec.ts. 2026-06-26 public scene report receipts — signed-in reporter submits /scene/<shortCode>/report with the real bearer, reads GET /v1/living-scenes/public-reports/mine, sees the open receipt on /profile/safety, operator resolves it, and the page shows resolved status plus note in public-scene-abuse-report.spec.ts. 2026-06-25 real-dev-infra route journey — live operator activation POST /v1/admin/safety/crisis-frame/activate, Redis-worker durable projection, member readback GET /v1/safety/crisis-frames/mine, empty state, anonymous redirect, stillness, re-entry accept/decline, check-in deliver/dismiss/opt-out, and axe verified by profile-safety-crisis-recovery.spec.ts. 2026-06-29 offline replay addendum — /profile/safety is an exact service-worker shell navigation; a successful live frame load writes a last-frame snapshot, and Chromium E2E proves offline cached-document replay restores the audit-visible frame plus the member's locally accepted re-entry choice without the generic fallback. 2026-06-30 timeline addendum — the full cross-surface /v1/safety/crisis-frames/mine frame array now renders as an expandable crisis-frame timeline and is real-BFF covered by profile-safety-crisis-recovery.spec.ts. 2026-06-30 resource-link addendum — the surfaced us-988 and audit-platform resources are actionable links, and the real-BFF profile-safety flow asserts lilith_safety_resource_opened telemetry.
Purpose#
"Audit-platform-visible record of recent crisis frames, recovery steps,
resources surfaced, submitted Trust & Safety reports, and public-scene report
receipts." The server route mounts the ProfileSafetyJourney client
composition, which reads the signed-in member's active crisis-frame projection,
submitted user-report receipts, and public-scene report receipts from the BFF,
then derives the local recovery controls from the Lilith crisis recovery
library.
The page must never show an example crisis event. With no durable frame, it renders an honest empty record.
Entry points#
/profilesub-section deep link (highlightPath = safety)- Profile settings panel "Safety" row →
/profile/safety /messagesreport-status decision notification deep link →/profile/safety?reportId=<id>- Crisis-frame follow-up links (post-incident push or in-product notification) — product entry point still to verify
- Direct URL / bookmark — yes (auth required; anonymous users redirect before the safety page renders)
Layout regions#
page.tsx is <ProfileSafetyJourney /> with no route chrome. The component
renders a constrained work surface with:
- Header — back link to
/profile, H1 "Safety record", and a one-sentence explanation that the view mirrors the audit platform's crisis-frame record. - What happened (
data-profile-safety-what-happened) — frame type, opened time, closed time, operator review SLA, and a redacted excerpt. The page maps the BFF crisis taxonomy to Lilith recovery taxonomy before building this view:suicidal-ideation → suicide-ideation,self-harm-imminent → active-self-harm,violence-to-others-credible → violence-toward-others,child-protection-signal → child-safety,domestic-violence-signal → interpersonal-violence, andmedical-emergency → acute-panic. - Report receipts (
data-profile-safety-report-receipts) — two receipt lanes:- Submitted reports (
data-profile-safety-user-report-receipts) reads signed-in Trust & Safety reports fromGET /v1/user-reports/mine. Each row hasdata-profile-safety-user-report-row=<reportId>,data-profile-safety-user-report-status=open|triaging|actioned|dismissed, anddata-profile-safety-user-report-outcome=<AdminReportOutcome>, then shows target label, category, submitted/updated timestamps, rationale, and a Trust & Safety reviewed marker for actioned reports. - Public scene reports (
data-profile-safety-public-report-receipts) reads signed-in public-scene abuse reports filed by this member, newest first. Each row hasdata-profile-safety-report-row=<reportId>anddata-profile-safety-report-status=open|resolved|dismissed, links back to/scene/<shortCode>, shows reason/detail, and shows the operator note when the report is terminal.
- Submitted reports (
- Crisis-frame timeline (
data-profile-safety-timeline) — an expandable chronological list of every active BFF frame marker projected into the contemplative surfaces (psyche,lilith-video,tara,iris,assistant). Each row carriesdata-profile-safety-timeline-row,data-profile-safety-timeline-surface=<surface>, and an expandeddata-profile-safety-timeline-detailblock with frame ID, region, synthesis halt, and memory-write suspension. - Stillness window (
data-profile-safety-stillness) — explains the minimum stillness period and lists non-safety surfaces withdata-profile-safety-surface=<surface>plusdata-suspended=true|false. - Re-entry (
data-profile-safety-reentry) — shows a disabled "Show re-entry prompt" button before the window closes, then lets the member accept into the conservative re-entry home or decline into idle-safe routing. - Soft check-ins (
data-profile-safety-checkins) — 24h and 7d rows with scheduled/delivered/dismissed/opt-out state and an opt-out control. - Resources surfaced (
data-profile-safety-resources) — actionable resource links currently includingus-988→tel:988andaudit-platform→#profile-safety-record, plus the operator queue lane. Links exposedata-profile-safety-resource-link=<resourceId>anddata-profile-safety-resource-destination=<href>.
States#
- Loading — component renders "Loading your record..." while auth/BFF read is pending.
- Empty — real BFF read returns no frames; page renders
data-profile-safety-emptyand does not show the removed fabricated Renata/acute-grief sample. - Populated (single active frame) — live operator activation projects a
durable frame and
/profile/safetyrenders the interactive record. - Recent event — current surface shows the first active frame returned
by
/v1/safety/crisis-frames/mine; all surfaces share the same frame ID for a single activation. - Error / unavailable — non-2xx fetch or missing auth token renders
data-profile-safety-unavailableand refuses to show an example record. - Submitted report receipts empty — real BFF read returns no signed-in
user reports; page renders
data-profile-safety-user-report-empty. - Submitted report receipts populated — signed-in
POST /v1/user-reportsrow renders fromGET /v1/user-reports/minewith open or terminal status and decision outcome. - Submitted report receipts unavailable — non-2xx user-report receipt
fetch renders
data-profile-safety-user-report-unavailable; the page does not fabricate report history. - Public scene report receipts empty — real BFF read returns no
signed-in public-scene reports; page renders
data-profile-safety-report-empty. - Public scene report receipts populated — signed-in public-scene report
row renders from
GET /v1/living-scenes/public-reports/minewith open or terminal status and the operator note. - Public scene report receipts unavailable — non-2xx receipt fetch
renders
data-profile-safety-report-unavailable; the page does not fabricate report history. - Anonymous user — route redirects to
/welcome; the safety page does not render. - Populated timeline — the page renders the full active BFF frame array
as
data-profile-safety-timeline; the first row is current and every projected surface marker can be expanded for frame metadata. - Offline — after a successful authenticated live frame load, the route
stores a compact last-frame snapshot beside the frame-keyed recovery
state;
/profile/safetyis an exact service-worker shell navigation, so a later offline navigation replays the cached document, restores the real audit-visible frame and local re-entry choice, marksdata-profile-safety-offline-snapshot="true", and does not show the generic offline fallback.
Interactions#
- Re-entry prompt unlock — before stillness closes, "Show re-entry prompt" is disabled; after clock advance, it becomes actionable.
- Accept re-entry — routes to
data-profile-safety-reentry-status="home"and names thecontemplative-stricttone band. - Decline re-entry — routes to
data-profile-safety-reentry-status="idle"while keeping the audit card andus-988resource visible. - 24h check-in deliver/dismiss — due row exposes "Deliver now", then
"Dismiss", and the row state updates to
deliveredthendismissed. - 7d check-in opt-out — "Opt out of soft check-ins" changes remaining
scheduled rows to
data-state="opt-out"and disables itself. - Timeline entry expansion — each timeline marker button toggles an expanded frame metadata block with frame ID, region, synthesis halt, and memory-write suspension.
- Resource link — surfaced resource rows are anchors and emit
lilith_safety_resource_openedwithresourceId,resourceKind, destination, and frame ID. - Export — no export control exists on this surface.
Data & contracts#
- Reads:
GET /v1/safety/crisis-frames/minefromapps/oshun/bff/src/safety/my-crisis-frames-route.ts. The route requires the signed-in member bearer, setscache-control: no-store, and returns{ generatedAt, frames[] }, where each frame includesframeId,surface,kind,region,activatedAtIso,haltSynthesis, andsuspendMemoryWrites. - Submitted report receipt reads:
GET /v1/user-reports/minefromapps/oshun/bff/src/routes/user-reports.ts. The route requires the signed-in member bearer and filters live customer-submitted reports byreporterId === authContext.userId. Response rows areAdminUserReportRecordreceipts withreportId, reporter/target metadata,category,rationale,status,outcome, reviewer fields, optionalrelatedContentItemId,submittedAt, andlastUpdatedAt. - Public scene report receipt reads:
GET /v1/living-scenes/public-reports/minefromapps/oshun/bff/src/routes/my-abuse-reports.ts. The route requires the signed-in member bearer and filterslivingSceneAbuseReportStorebyreporterUserId === authContext.userId. Response rows includereportId,shareId,shortCode,reason,detail,createdAtUnixSeconds,deepLinkPath,status, and terminalactionedAtUnixSeconds/resolutionNotewhen present; operator identity is intentionally omitted. - Write source for receipts:
POST /v1/living-scenes/public/:shortCode/reportfromapps/oshun/bff/src/routes/living-scenes.ts. Anonymous reports keepreporterUserId: null; signed-in reports include an Authorization bearer and the BFF stamps the validated subject asreporterUserId. - Write source for submitted reports:
POST /v1/user-reportsfromapps/oshun/bff/src/routes/user-reports.ts; moderator decisions are written byPOST /v1/admin/user-reports/:reportId/decision. - Write source for frames: operator-scoped
POST /v1/admin/safety/crisis-frame/activatepublishes the crisis-frame event; the real worker projects durable markers per contemplative surface. - Local recovery state: the page builds a
StillnessWindow,ReentryFlow,CheckInSchedule, andIncidentRecordfrom the active frame using@oshun/persona-policy-lilith. Member choices are persisted underoshun-profile-safety-v1:<frameId>. The page also writesoshun-profile-safety-v1:last-frameafter a live frame read so browser-offline route replay can recover the frame identity needed to read the frame-keyed local recovery choices. - Server writes from this page: none. Re-entry and check-in changes are local recovery-state choices; they are not currently posted back to the BFF.
- Telemetry: resource opens dispatch
lilith_safety_resource_openedwithsurface: 'profile_safety',sourcePath: '/profile/safety',resourceId,resourceKind,destination, and the activeframeId. The customer taxonomy records this under canonicaloshun.safety.resource_opened. - Auth:
AuthProvidersupplies the access token; the page waits for authenticated status before issuing the BFF read. - Metadata:
title: 'Safety record',description: 'Audit-platform-visible record of recent crisis frames, recovery steps, resources surfaced, and report receipts.',robots: { index: false, follow: false },alternates.canonical: '/profile/safety'.
E2E coverage#
apps/oshun/web/e2e/profile-safety-crisis-recovery.spec.ts- live no-frame empty state from
/v1/safety/crisis-frames/mine - live operator activation → durable projection → member page readback
- full cross-surface timeline from the real BFF
frames[]projection, including expandable assistant-frame metadata - stillness suspension/lift across all rendered non-safety surfaces
- re-entry accept and decline paths
- surfaced
us-988link target pluslilith_safety_resource_openedtelemetry from the live safety record - 24h deliver/dismiss and 7d opt-out
- real service-worker offline cached-document replay of the last live frame and locally accepted re-entry choice without generic fallback markers
- anonymous redirect before render
- shared axe gate via
./fixtures
- live no-frame empty state from
apps/oshun/web/e2e/public-scene-abuse-report.spec.ts- signed-in reporter submits
/scene/<shortCode>/report - direct BFF receipt read returns the report as
open /profile/safetyshows the report receipt row- operator resolve persists terminal status and note
/profile/safetyreadback showsresolvedand the operator note
- signed-in reporter submits
apps/oshun/web/e2e/user-flag-to-review.spec.ts- signed-in reporter creates a live Trust & Safety report through the real BFF
- moderator records a
content_removeddecision through the real admin BFF - moderator decision emits a real in-app customer-message notification and the
/messagesdeep-link follow returns to/profile/safety?reportId=<id> - direct
GET /v1/user-reports/mineread-back proves reporter isolation and terminal status /profile/safetyshows the submitted-report receipt row asactionedwithcontent_removed
Cross-references#
- Parent:
profile.md - Sibling:
profile-memory.md - Journey:
../../journeys/crisis-aware-tone-policy.md - Journey:
../../journeys/public-scene-abuse-report.md - Journey:
../../journeys/user-flag-to-review.md - Lilith safety policy:
V1/features.md#lilith-persona-policyandV1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey - Component source:
apps/oshun/web/src/app/profile/safety/ProfileSafetyJourney.tsx
Open questions / known gaps#
- Resource links and resource-open telemetry are covered from the real profile-safety flow.
- Recovery choices are local-only; decide whether accept/decline, check-in-dismiss, and opt-out should be audit-visible BFF writes.
- Confirm what the "audit-platform-visible" framing means for operator parity: whether the operator audit team sees the same derived recovery state or only the durable frame marker.
- Visited offline replay is proven for the safety record: after a live frame
load,
/profile/safetycan replay the cached document andoshun-profile-safety-v1:last-framesnapshot while the browser is offline. A first-ever offline visit with no cached document remains outside the offline contract and cannot show member data.