feat/second-sdr-adsb-ais
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b7222230bd |
frontend: label machine-generated output and unbuilt entitlements (Gate A)
Gate A (BUSINESS_MODEL.md, board minutes #42, dated to today by minutes #79 decision 14) blocks putting a price or an unbuilt entitlement claim on a surface a reader can see, and requires that unverified machine assertions be labelled as such on the same screen as the assertion. The pricing leg was already met — /pricing and both homepage CTAs stopped quoting the invented catalog. Condition A2 was not: a search of the whole frontend for a "machine-generated" or "unverified" qualifier returned zero hits. Every transcript, summary, title, location, unit list and vehicle list is pipeline output that no human reviews, and entity-name accuracy in those transcripts has never been measured (server-26#48) — yet all of it was rendered to the reader as plain fact. Unqualified machine assertions about real incidents and real people is the exposure Gate A exists to stop. A2 — one reusable element, components/ui/MachineOutputNotice.tsx, rendered on the same screen as the output (a footnote elsewhere does not satisfy A1's "same screen" standard). Three variants for three shapes of surface, all saying the same thing; the "popup" variant uses fixed grays because a Leaflet popup is stock-white in both themes. Covered: - incident detail: under the summary (covers summary, title, location, units on scene/cleared, vehicles, tags) and above the call spine - incident list: above the timeline groups - Archive (/calls): above the transcript rows - node detail: above the Recent Calls table - Watch//alerts: above the events table, whose Snippet column is transcript text and whose keyword match was made against it - Live map: the desktop incident rail, pinned above the scroll area so it cannot be scrolled off the screen it qualifies; the mobile drawer; the incident marker popup; the incident-path stop popup - /systems: the source-call transcript preview - /features: the two marketing sections that describe the AI pipeline A1 — components/ui/UnbuiltMarker.tsx marks a claim unbuilt inline: - /faq: the retention answer promised 7/90/365-day windows. There is no TTL and no deletion sweep anywhere in the product (server-26#44), so the answer now states plainly that nothing is deleted automatically and marks per-plan retention as not yet available. - /settings/billing: the plan cards' claims — custom retention, SSO/SAML, uptime SLA, data residency — are marked not-yet-available next to the plan that makes them. Labelling only. No retention, SSO, SLA or residency was built; no billing, Stripe or checkout code was touched (Gate B still bars charging anyone); no price was added anywhere; no Python was touched. Both themes verified against the light-mode !important overrides in globals.css, which are untouched. tsc --noEmit clean. Refs: server-26#46, server-26#44, server-26#48 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
53965e1a19 |
Rebuild the frontend as a product rather than an internal tool
The UI worked but read as an operator console: no public face, no way to describe or sell the thing, and no account surface beyond the node list. This adds the missing halves and reorganises what was already there around the incident, which is the unit of value the rest of the pipeline is built to produce. A shared design system replaces per-page styling: components/ui (Button, Card, Badge, EmptyState, Skeleton, PageHeader), a type scale and shadow set in the Tailwind config, and light-mode tokens in globals.css. The existing html:not(.dark) remap mechanism is extended rather than replaced -- a parallel theming system would have been two sources of truth for the same colours. Public marketing pages (/, /features, /pricing, /faq) load without a session. middleware.ts gained a PUBLIC_PATHS allowlist to permit that; it remains a UX redirect and is still NOT an authorisation boundary, which the comment there says explicitly. Real enforcement is unchanged and still lives server-side in c2-core's auth.py. Chrome switching is done by pathname in ChromeSwitcher instead of by route group, because a route group would have collided on / and forced most of app/ to move for no behavioural gain. Billing and API keys ship as typed stubs, not integrations. lib/billing.ts and lib/apiKeys.ts define the data model and the screens consume it, but every mutating call throws with a message naming the backend route that has to exist first, and the sample data is labelled as sample. Nothing here can charge anyone or mint a real credential -- picking a payment processor and holding its keys is a decision for a human, and a half-wired checkout is worse than an obviously absent one. The severity work from the c2-core change lands here too. severity is now a filter and sort dimension on the incident list rather than decoration, since a busy dispatch channel is only readable if you can collapse it to moderate and above. routine gets a muted treatment because it is the majority of traffic, legacy "unknown" still renders nothing, and TypeBadge handles the new "other" incident type. Severity rendering moved into lib/severity.tsx so the incident list, incident detail and call rows cannot drift apart. Deliberately not touched: calls, map, alerts, nodes, systems, tokens, trips and admin. They already share the palette and stay coherent, and rewriting them would have buried the parts that actually needed to change. No colour tokens were renamed, so nothing regressed there. Verified with tsc --noEmit (npm run typecheck), clean. No runtime verification was possible and none was done. No new environment variables. |