V1 Web PWA · Surface walkthrough

Profile · Safety Record

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
9sections9 minread

On this page

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#

  • /profile sub-section deep link (highlightPath = safety)
  • Profile settings panel "Safety" row → /profile/safety
  • /messages report-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, and medical-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 from GET /v1/user-reports/mine. Each row has data-profile-safety-user-report-row=<reportId>, data-profile-safety-user-report-status=open|triaging|actioned|dismissed, and data-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 has data-profile-safety-report-row=<reportId> and data-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.
  • 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 carries data-profile-safety-timeline-row, data-profile-safety-timeline-surface=<surface>, and an expanded data-profile-safety-timeline-detail block 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 with data-profile-safety-surface=<surface> plus data-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 including us-988tel:988 and audit-platform#profile-safety-record, plus the operator queue lane. Links expose data-profile-safety-resource-link=<resourceId> and data-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-empty and does not show the removed fabricated Renata/acute-grief sample.
  • Populated (single active frame) — live operator activation projects a durable frame and /profile/safety renders 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-unavailable and 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-reports row renders from GET /v1/user-reports/mine with 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/mine with 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/safety is 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, marks data-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 the contemplative-strict tone band.
  • Decline re-entry — routes to data-profile-safety-reentry-status="idle" while keeping the audit card and us-988 resource visible.
  • 24h check-in deliver/dismiss — due row exposes "Deliver now", then "Dismiss", and the row state updates to delivered then dismissed.
  • 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_opened with resourceId, resourceKind, destination, and frame ID.
  • Export — no export control exists on this surface.

Data & contracts#

  • Reads: GET /v1/safety/crisis-frames/mine from apps/oshun/bff/src/safety/my-crisis-frames-route.ts. The route requires the signed-in member bearer, sets cache-control: no-store, and returns { generatedAt, frames[] }, where each frame includes frameId, surface, kind, region, activatedAtIso, haltSynthesis, and suspendMemoryWrites.
  • Submitted report receipt reads: GET /v1/user-reports/mine from apps/oshun/bff/src/routes/user-reports.ts. The route requires the signed-in member bearer and filters live customer-submitted reports by reporterId === authContext.userId. Response rows are AdminUserReportRecord receipts with reportId, reporter/target metadata, category, rationale, status, outcome, reviewer fields, optional relatedContentItemId, submittedAt, and lastUpdatedAt.
  • Public scene report receipt reads: GET /v1/living-scenes/public-reports/mine from apps/oshun/bff/src/routes/my-abuse-reports.ts. The route requires the signed-in member bearer and filters livingSceneAbuseReportStore by reporterUserId === authContext.userId. Response rows include reportId, shareId, shortCode, reason, detail, createdAtUnixSeconds, deepLinkPath, status, and terminal actionedAtUnixSeconds / resolutionNote when present; operator identity is intentionally omitted.
  • Write source for receipts: POST /v1/living-scenes/public/:shortCode/report from apps/oshun/bff/src/routes/living-scenes.ts. Anonymous reports keep reporterUserId: null; signed-in reports include an Authorization bearer and the BFF stamps the validated subject as reporterUserId.
  • Write source for submitted reports: POST /v1/user-reports from apps/oshun/bff/src/routes/user-reports.ts; moderator decisions are written by POST /v1/admin/user-reports/:reportId/decision.
  • Write source for frames: operator-scoped POST /v1/admin/safety/crisis-frame/activate publishes the crisis-frame event; the real worker projects durable markers per contemplative surface.
  • Local recovery state: the page builds a StillnessWindow, ReentryFlow, CheckInSchedule, and IncidentRecord from the active frame using @oshun/persona-policy-lilith. Member choices are persisted under oshun-profile-safety-v1:<frameId>. The page also writes oshun-profile-safety-v1:last-frame after 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_opened with surface: 'profile_safety', sourcePath: '/profile/safety', resourceId, resourceKind, destination, and the active frameId. The customer taxonomy records this under canonical oshun.safety.resource_opened.
  • Auth: AuthProvider supplies 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-988 link target plus lilith_safety_resource_opened telemetry 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
  • 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/safety shows the report receipt row
    • operator resolve persists terminal status and note
    • /profile/safety readback shows resolved and the operator note
  • 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_removed decision through the real admin BFF
    • moderator decision emits a real in-app customer-message notification and the /messages deep-link follow returns to /profile/safety?reportId=<id>
    • direct GET /v1/user-reports/mine read-back proves reporter isolation and terminal status
    • /profile/safety shows the submitted-report receipt row as actioned with content_removed

Cross-references#

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/safety can replay the cached document and oshun-profile-safety-v1:last-frame snapshot while the browser is offline. A first-ever offline visit with no cached document remains outside the offline contract and cannot show member data.