---
path: /scene/[id]/report
surface: customer
domain: scene
auth: anon or signed-in
source: apps/oshun/web/src/app/scene/[id]/report/page.tsx
status: walked
last_walked: >
  2026-06-28 focused automated addendum — default form state, reason options,
  optional detail/maxLength, absence of contact/cancel fields, back-link href,
  submit-enabled in-flight behavior, recoverable 503 failure, browser-offline
  failure, anonymous success, signed-in receipts, and operator inbox/decision
  read-back covered in public-scene-abuse-report.spec.ts. 2026-06-26 signed-in
  reporter addendum — PublicSceneReportForm now includes the member access token
  when AuthProvider is authenticated; BFF stamps reporterUserId from the
  validated bearer and /profile/safety displays the receipt. Evidence:
  public-scene-abuse-report.spec.ts. 2026-05-29 automated runtime walk
  (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected
  content, screenshot verified; live screen-reader, touch, offline, and
  telemetry-delivery checks pending a manual AT pass. Evidence:
  WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified
  2026-06-03 against current source.
---

# Scene · Report (abuse / safety form)

## Purpose

Anonymous-or-signed-in abuse / safety-report form for a public living offering.
Renders the `PublicSceneReportForm` client component and a "Back to offering"
link. Reports route to Lilith review with the share code, reason, page context,
and the validated reporter subject when the viewer is signed in.

`/scene` is in `apps/oshun/web/src/proxy.ts` `PUBLIC_PATHS` — anyone with the
share code can submit a report without signing in.

## Entry points

- "Report" action on the full scene viewer (`/scene/<id>` accent-coloured
  `<Link>`)
- Direct URL — anyone with the path can open the form
- Email / external link to "Report this scene"
- Direct URL / bookmark — yes (anon)

## Layout regions

`page.tsx` renders a single `<main>` with a centred 80svh content grid:

- **Eyebrow**: `Living Offering <params.id>` (mono caps, teal)
- **Title**: `Report this offering` (5xl serif)
- **Body paragraph**: "Reports route to Lilith review with the share code,
  reason, and page context."
- **`<PublicSceneReportForm shortCode={params.id} />`**: the actual form
  composition (lives in
  `apps/oshun/web/src/app/scene/[id]/report/PublicSceneReportForm.tsx`)
- **"Back to offering"** (Link) → `/scene/<id>`

## States

- [x] **Default** — form renders with the Reason select defaulting to `privacy`
      and an empty Detail textarea
- [x] **Submitting** — `onSubmit` is async with no in-flight disable/spinner;
      the button stays enabled while `submitPublicLivingSceneReport` awaits
- [x] **Submitted successfully** — no redirect; the `aria-live="polite"`
      `role="status"` line updates inline to "Report sent to review."
- [x] **Submit failed (recoverable)** — same inline status line shows "Report
      could not be sent."; the form stays on-page for retry
- [x] **Validation** — none beyond `maxLength=2000` on Detail; Reason always has
      a value (defaulted), Detail may be empty
- [x] **Offline** — `fetch` rejects → caught → "Report could not be sent."
      status
- [ ] **Standalone PWA** — verify form renders cleanly

## Interactions

Page-level: one navigation link.

- [x] **"Back to offering"** (Link) → `/scene/<id>`
  - Keyboard: Tab focusable; Enter activates

Form-level (inside `PublicSceneReportForm`):

- [x] **Reason** — a `<select>` (not radios/chips) with six options: `privacy`
      (default), `self-harm`, `harassment`, `copyright`, `misinformation`,
      `other`
- [x] **Detail** — a `<textarea maxLength={2000}>` for additional context
- [x] **"Send report"** — primary submit; calls
      `submitPublicLivingSceneReport({ shortCode, reporterUserId: null, reason, detail, accessToken })`;
      `accessToken` is present only when AuthProvider is authenticated
- [x] **Inline status** — `aria-live="polite"` `role="status"` line below the
      button that surfaces the success / failure message
- No reporter-contact (email) field and no cancel/clear button exist; the only
  way back is the page-level "Back to offering" link above the form

## Data & contracts

- **Reads**: none (page is static aside from the `params.id` interpolation)
- **Writes**: `submitPublicLivingSceneReport()` (`@/lib/living-scenes-public`
  ~188) → `POST /v1/living-scenes/public/<shortCode>/report` (singular `report`,
  ~196) with a JSON body `{ reporterUserId, reason, detail }` and an
  Authorization bearer when signed in. The BFF stamps `reporterUserId` from the
  validated bearer when present and uses the nullable body field only for
  anonymous/back-compat submissions.
- **Realtime**: none
- **Caching**: SSR static
- **Auth/role check**: anon allowed (`/scene` is in `src/proxy.ts`
  `PUBLIC_PATHS`); signed-in sessions attach the member bearer for receipt
  tracking.
- **Metadata**: `title: 'Report Living Offering'` (bare; the `| OSHUN` suffix
  comes from the root layout's `title.template = '%s | OSHUN'`),
  `robots: { index: false, follow: false }`

## Automation Evidence

- `apps/oshun/web/e2e/public-scene-abuse-report.spec.ts`
  - `renders the public form defaults, constraints, back link, in-flight state, and failure status`
  - `an anonymous visitor reports a shared offering and it routes to review`
  - `a report with a reason but no detail is still accepted`
  - `a self-harm report appears as an S1 (critical) auto INC with the scene deep link`
  - operator resolve/dismiss API read-back and signed-in reporter receipt tests

## Cross-references

- Sibling scene routes:
  - [`scene-id.md`](./scene-id.md)
  - [`scene-id-embed.md`](./scene-id-embed.md)
- Lilith review (operator surface): out-of-scope here; reports route into the
  moderation queue
- Component sources:
  - `apps/oshun/web/src/app/scene/[id]/report/page.tsx`
  - `apps/oshun/web/src/app/scene/[id]/report/PublicSceneReportForm.tsx`
  - `apps/oshun/web/src/lib/living-scenes-public.ts`
    (`submitPublicLivingSceneReport`)

## Open questions / known gaps

- [x] Resolved: fields enumerated — Reason `<select>` (privacy/self-harm/
      harassment/copyright/misinformation/other, default `privacy`) + Detail
      `<textarea maxLength=2000>`; submits to
      `POST /v1/living-scenes/public/<shortCode>/report`
- [ ] Confirm where reports are stored and how Lilith review is notified
      (operator side)
- [x] Resolved: anonymous reporters cannot include a contact email — the form
      sends `reporterUserId: null` and exposes no contact field; signed-in
      identity is derived from the validated bearer rather than a visible field
- [x] Resolved: post-submit UX — no redirect/reset; an inline `aria-live` status
      line shows "Report sent to review." or "Report could not be sent."
- [ ] Confirm rate limiting and abuse protection on the report endpoint
      (anonymous submissions are abuse-prone)
- [ ] No CAPTCHA visible at the page level — verify whether one is added inside
      the form
