profile-safety.mdscene-id-report.mdscene-id.mdoperator-admin.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
This record follows the shipped report path for a public Living Offering. The
public form, 202 receipt, operator-list projection, reporter read-back, and
report-specific resolve/dismiss APIs are real. The join stops at two important
boundaries: reports live in a process-local singleton with no snapshot sink, and
the operator console's visible controls write to generic incident stores rather
than the report-specific resolution endpoints.
Personas#
- Anonymous viewer — reports a public or revoked
/scene/<shortCode>link without needing an account. - Signed-in reporter — submits the same form and can later read their
receipt on
/profile/safety. - Trust-and-safety operator — sees the report projected into
/operator/adminand opens the affected offering. - API operator — calls the report-specific resolve or dismiss endpoint; this is the only shipped way to mutate the report's own status.
Pre-conditions#
- A Living Offering share exists. The report endpoint looks up the share by short code but does not require it to remain active.
- The public form at
/scene/<shortCode>/reportoffers the fixed reasonsprivacy,self-harm,harassment,copyright,misinformation, andother. Detail is optional and capped at 2,000 characters. - Public abuse protection permits at most 120 requests in 60 seconds for this route group.
- A bearer token is optional. When one is valid, the BFF derives the reporter identity from it; the request-body id is not authoritative for that case.
Steps#
1. Open and submit the public form#
PublicSceneReportForm preserves the offering back link, requires one reason,
allows an empty detail, and posts to
POST /v1/living-scenes/public/:shortCode/report. The page exposes an explicit
failure status when the request is rejected and a success message only after an
OK response.
2. Record and route the report#
The BFF validates the short code, normalizes the reason/detail, records the
report, appends living_scene.public_report_created to the admin audit stream,
and returns 202 with the report receipt. Every reason uses the same routed
queue, lilith-living-scene-public-reports; self-harm becomes severity S1,
harassment/copyright/misinformation S2, and privacy/other S3 in the admin-list
projection. Severity is not a separate crisis-review workflow.
3. Inspect the operator projection#
GET /v1/admin/abuse-reports maps reports to INC-* rows with category auto,
the calculated severity, report status, and a /scene/<shortCode> deep link.
/operator/admin prepends those rows to AdminInboxConsole, where the operator
can inspect the summary and open the offering.
4. Resolve through the report API#
POST /v1/admin/abuse-reports/:id/resolve and /dismiss update the report
record and append living_scene.public_report_resolved or
living_scene.public_report_dismissed audit events. A signed-in reporter reads
the resulting status through GET /v1/living-scenes/public-reports/mine.
The visible AdminInboxDecisionPanel and AdminInboxIncidentActions do not
call those endpoints. They update the generic operator-decision and incident
lifecycle stores, so using them does not resolve or dismiss the abuse report.
5. Apply a separate takedown when warranted#
The Living Offering deletion/takedown API can tombstone public URLs and revoke download grants, and browser coverage proves that cascade. It is a separate operator/API action; resolving an abuse report does not automatically take the offering down, warn a sharer, or suspend an account.
Post-conditions#
- The submitter receives a
202report receipt and, when authenticated, can read its current status on/profile/safety. - The operator list contains an incident-shaped projection with the correct severity and offering deep link.
- Resolve/dismiss status and actor/note are readable for as long as the BFF process remains alive; corresponding admin audit events are appended.
- No claim is made that the report itself, generic operator decision, incident action, and Living Offering takedown are one atomic lifecycle.
Failure modes#
- Process restart —
livingSceneAbuseReportStoreis a module singleton over an in-memory map. It has no durable snapshot wiring, so report-list and reporter-receipt state disappear on restart even when admin audit persistence is configured. - False operator closure — generic verdict/incident buttons can succeed
while the abuse report remains
open; only the report-specific API changes that record. - Revoked share — a known revoked share still accepts a report and returns
202; this is intentional coverage, not proof that the share is playable. - Missing detail — valid and accepted. A reason is the minimum useful payload.
- Unknown report id — resolve/dismiss returns
404and creates no terminal record. - Rate limit — the 121st request in the covered window returns
429. - Unavailable BFF — the public form keeps the viewer on the page and shows “Report could not be sent”; there is no offline report queue.
E2E coverage#
apps/oshun/web/e2e/public-scene-abuse-report.spec.tsdrives the form constraints and failure state, anonymous202, optional detail, S1 projection, API resolve/dismiss read-back, unknown-id rejection, and the signed-in/profile/safetyreceipt.apps/oshun/web/e2e/scene-password-and-grants.spec.tscovers report creation on active and revoked shares, routed-queue output, rate limiting, and the separate deletion/tombstone/download-grant cascade.- Coverage depth: partial. The API lifecycle is deep; the missing browser join is a report-specific resolve/dismiss/takedown control in the operator UI.
Per-view files touched#
customer/12-scene/scene-id-report.md— public form and submit states.customer/12-scene/scene-id.md— affected offering and tombstone result.customer/09-account/profile-safety.md— authenticated reporter receipts.operator/operator-admin.md— operator projection and the generic-control boundary.
Cross-references#
scene-public-viewer-anonymous.md— the public surface from which the report link opens.scene-keep-and-share.md— share and revocation lifecycle.incident-triage.md— generic operator incident lifecycle, which is adjacent but not report-specific.- Sources:
apps/oshun/web/src/app/scene/[id]/report/PublicSceneReportForm.tsx,apps/oshun/bff/src/living-scenes/abuse-report-store.ts, andapps/oshun/bff/src/routes/living-scenes.ts.
Open questions#
- Which operator control should call report-specific resolve/dismiss, and should it optionally compose with a Living Offering takedown?
- Which durable store should own abuse reports and reporter receipts across restart?
- Should S1 self-harm reports route to a distinct safety queue rather than only receiving a higher severity label in the shared queue?
- What explicit account/sharer enforcement actions, if any, belong in this workflow?