V1 Web PWA · Surface walkthrough

Scene · Report (abuse / safety form)

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

walked
9sections4 minread

On this page

Context. surface customer · domain scene · route /scene/[id]/report · auth anon or signed-in · source apps/oshun/web/src/app/scene/[id]/report/page.tsx

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.

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#

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

Interactions#

Page-level: one navigation link.

  • "Back to offering" (Link) → /scene/<id>
    • Keyboard: Tab focusable; Enter activates

Form-level (inside PublicSceneReportForm):

  • Reason — a <select> (not radios/chips) with six options: privacy (default), self-harm, harassment, copyright, misinformation, other
  • Detail — a <textarea maxLength={2000}> for additional context
  • "Send report" — primary submit; calls submitPublicLivingSceneReport({ shortCode, reporterUserId: null, reason, detail, accessToken }); accessToken is present only when AuthProvider is authenticated
  • Inline statusaria-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:
  • 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#

  • 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)
  • 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
  • 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