Skip to content

Public evidence brief fallback page · closes #4#9

Open
Frex22 wants to merge 1 commit into
frex22/stripe-compliance-packfrom
frex22/evidence-brief-page
Open

Public evidence brief fallback page · closes #4#9
Frex22 wants to merge 1 commit into
frex22/stripe-compliance-packfrom
frex22/evidence-brief-page

Conversation

@Frex22

@Frex22 Frex22 commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Locally hosted Senso fallback for the evidence brief. When Senso is unavailable, clicking "View evidence" routes to /evidence/:id on our own deploy (handoff/API §07 F4). The page is public, unauthenticated, print-friendly, and has no app chrome.

Server

  • GET /v1/evidence/:id — public, no bearer. Returns a denormalised EvidenceBriefResponse (changeReport + vendor + policyFired + policyAlsoMatched + actionSummary). /v1/evidence/ added to PUBLIC_PATHS in auth.ts.
  • Path naming nuance: the user-facing URL is /evidence/:id (SPA route, served by Vite's history fallback so React mounts). The JSON endpoint lives under /v1/evidence/:id so the dev proxy can route it without colliding with the SPA's index.html fallback.
  • apps/api/src/db/change-reports.ts — in-memory ChangeReportStore loaded from seed/change-reports.json. Track A's real runner will write to ClickHouse AND this cache when its ChangeReports land; the brief stays available even if Senso publish fails.
  • apps/api/src/db/policy-store.ts — in-memory policy lookups so the brief resolves policyFiredId to display names.

Seed data

  • seed/change-reports.json — the locked Notion demo story (retention 90→30 days + price $16→$19 / +18.75%) and a Stripe sub-processor fleet entry with country: "IN". Both match the handoff Demo Seed §07.
  • seed/policies.json — the 3 seeded policies from handoff §05 (data retention PII shrink, price hike near renewal, sub-processor added restricted) so the brief can resolve policy names.
  • seed/vendors.json — populated with vnd_notion and vnd_stripe so vendor lookups resolve. (This is technically scope beyond strict [Sprint 1 | Frontend | Frex22] Public evidence brief fallback page #4 but is the minimum the brief needs to render; the empty array from [Sprint 1 | Frontend/API | Frex22] Add Vendor flow with first-scan subscription #2 has been replaced.)

Web

  • apps/web/src/screens/SensoBrief.tsx — public, no app chrome. Renders vendor, policy fired (+ "also matched"), severity badge, recommendation, every Change with before/after diff and dollar impact, every Citation with verbatim quote + URL + section + fetched timestamp + country, and the routed actions list with an empty state.
  • apps/web/src/styles/brief.css — readable print-targeted layout with @media print rules: page-break-inside: avoid for changes and citations, link URL annotation on print.
  • apps/web/src/App.tsx — tiny pathname check parses /evidence/:id and renders SensoBrief directly (no app chrome). No router dependency — one regex.
  • apps/web/src/lib/api.tsgetEvidence(id) with the new skipAuth: true option since the brief is public.

Shared

  • packages/shared/src/types.ts — adds Citation, Change, ChangeReport, Recommendation, EvidenceBriefResponse, plus the enums used by them (ChangeCategory, Materiality, ChangeState, Resolution, RecommendationAction).
  • packages/shared/src/schemas.ts — matching zod schemas including the handoff-mandated ≥1 citation per change refinement (citations: z.array(CitationSchema).min(1)).

Caveats reviewers should know

  • The endpoint is intentionally unauthenticated. Senso brings its own anonymous browse model and the runbook says the fallback is "the same Senso-shaped template hosted locally". Don't add bearer enforcement here without coordinating with product.
  • actionSummary is empty for seeded reports. Actions are persisted to the ClickHouse actions table by Track A's agent runner. Until those land, the brief shows a friendly empty state. The shape is in place so the UI lights up automatically when actions exist.
  • Seed expansion. seed/vendors.json previously had an empty array (from [Sprint 1 | Frontend/API | Frex22] Add Vendor flow with first-scan subscription #2). It now has 2 vendors. seed/policies.json is new. Both are required for the brief to render names. Track A will likely want to round these out to the full 8 vendors + 3 policies; structure matches the handoff so it's purely additive.
  • No React Router added. One pathname regex in App.tsx. If you want a real router later, swap it in — but for one extra public route the regex is the right tradeoff.

Smoke verified end-to-end

  • GET /v1/evidence/chg_seed_notion (no bearer) → 200 with full denormalised brief (vendor=Notion, policy=Data retention shrinks for PII vendors, P1, 2 changes, citation quote matches the source)
  • GET /v1/evidence/chg_seed_stripe_subprocessor200 with vendor=Stripe, citation country=IN
  • GET /v1/evidence/chg_does_not_exist404 with standard error envelope (code: not-found)
  • Vite dev proxy: /evidence/:id serves index.html (SPA route, Content-Type text/html); /v1/evidence/:id proxies through to the API and returns JSON

Test plan

  • pnpm test — 39 specs across 8 suites. All passing.
    • tests/api/evidence.test.ts — auth-less 200, 404 envelope, denormalisation (vendor + policy lookup), Stripe fleet entry
    • tests/web/evidence-brief.test.tsx — render with citation+quote+timestamp, not-found state, empty actions state, no app chrome, brief class applied for print CSS
  • pnpm typecheck clean across all three workspaces.
  • Live end-to-end smoke against the API (no Senso involvement, no ClickHouse dependency for the brief itself).
  • Browser smoke: pnpm --filter @redline/web dev → visit http://localhost:5173/evidence/chg_seed_notion → see the full brief without app chrome → Cmd-P preview to verify print CSS. (Easy to repro; left for reviewer.)

Stacking note

This PR is stacked on #3 (base: frex22/stripe-compliance-pack), which is itself stacked on #2 (frex22/add-vendor-first-scan). When #7 + #8 merge to main, retarget this PR to main and rebase.

🤖 Generated with Claude Code

Locally hosted Senso fallback. When Senso is unavailable, the brief is
served from `/evidence/:id` on our own deploy (handoff/API §07 F4).

- GET /v1/evidence/:id — public, unauthenticated. Returns a denormalised
  EvidenceBriefResponse (changeReport + vendor + policyFired + actionSummary).
  /v1/evidence/ added to PUBLIC_PATHS in auth.ts.
- /v1/evidence (JSON) vs /evidence/:id (SPA): the user-facing URL is
  /evidence/:id (served by Vite's history fallback so React can mount),
  the JSON endpoint lives under /v1/evidence/:id so the dev proxy can
  route it cleanly without colliding with the SPA route.
- apps/api/src/db/change-reports.ts — in-memory ChangeReportStore loaded
  from seed/change-reports.json. Track A's real runner will write to
  ClickHouse AND this cache when its ChangeReports land.
- apps/api/src/db/policy-store.ts — in-memory policy lookups so the brief
  can resolve policyFiredId to display names.
- Seed data: seed/change-reports.json with the locked Notion demo story
  (retention 90→30 days + price $16→$19 +18.75%) and a Stripe sub-processor
  fleet entry. seed/policies.json with the 3 seeded policies from
  handoff §05. seed/vendors.json populated with vnd_notion and vnd_stripe
  so vendor lookups resolve.
- apps/web/src/screens/SensoBrief.tsx — public, no app chrome.
  Renders vendor, policy fired, severity, recommendation, every Change
  with before/after diff, every Citation with verbatim quote + URL +
  section + fetched timestamp + country, and the routed actions list
  (empty state when no actions are recorded).
- apps/web/src/styles/brief.css — readable print-targeted layout with
  @media print rules: page-break-inside avoid for changes and citations,
  link URL annotation on print.
- apps/web/src/App.tsx — pathname check parses /evidence/:id and renders
  SensoBrief without the Onboard/Upgrade chrome. No router dependency.

Smoke verified end-to-end:
- GET /v1/evidence/chg_seed_notion (no bearer) → 200 with full brief
- GET /v1/evidence/chg_does_not_exist → 404 with standard error envelope
- Vite proxy serves index.html for /evidence/:id (SPA) and forwards
  /v1/evidence/:id to the API (data)

Tests: 39/39 passing across 8 suites. 9 new specs cover the evidence
endpoint (auth-less 200, 404, denormalisation, fleet entry) and the
SensoBrief component (render, not-found, citation display, empty
actions, no app chrome, print class).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant