Public evidence brief fallback page · closes #4#9
Open
Frex22 wants to merge 1 commit into
Open
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Locally hosted Senso fallback for the evidence brief. When Senso is unavailable, clicking "View evidence" routes to
/evidence/:idon 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 denormalisedEvidenceBriefResponse(changeReport + vendor + policyFired + policyAlsoMatched + actionSummary)./v1/evidence/added toPUBLIC_PATHSinauth.ts./evidence/:id(SPA route, served by Vite's history fallback so React mounts). The JSON endpoint lives under/v1/evidence/:idso the dev proxy can route it without colliding with the SPA's index.html fallback.apps/api/src/db/change-reports.ts— in-memoryChangeReportStoreloaded fromseed/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 resolvespolicyFiredIdto 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 withcountry: "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 withvnd_notionandvnd_stripeso 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 printrules:page-break-inside: avoidfor changes and citations, link URL annotation on print.apps/web/src/App.tsx— tiny pathname check parses/evidence/:idand rendersSensoBriefdirectly (no app chrome). No router dependency — one regex.apps/web/src/lib/api.ts—getEvidence(id)with the newskipAuth: trueoption since the brief is public.Shared
packages/shared/src/types.ts— addsCitation,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 changerefinement (citations: z.array(CitationSchema).min(1)).Caveats reviewers should know
actionSummaryis empty for seeded reports. Actions are persisted to the ClickHouseactionstable 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/vendors.jsonpreviously 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.jsonis 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.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) →200with 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_subprocessor→200with vendor=Stripe, citation country=INGET /v1/evidence/chg_does_not_exist→404with standard error envelope (code: not-found)/evidence/:idservesindex.html(SPA route, Content-Type text/html);/v1/evidence/:idproxies through to the API and returns JSONTest 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 entrytests/web/evidence-brief.test.tsx— render with citation+quote+timestamp, not-found state, empty actions state, no app chrome, brief class applied for print CSSpnpm typecheckclean across all three workspaces.pnpm --filter @redline/web dev→ visithttp://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 tomain, retarget this PR tomainand rebase.🤖 Generated with Claude Code