#286: request() now throws C2ApiError — message is FastAPI's `detail` or a
plain sentence for the status (401/403/404/429/5xx), with toString() so the
many String(e) call sites read cleanly. status/body stay on the error; the
Discord parsers rebuild the legacy "C2 API error N: body" text via
legacyErrorText(), so their invite/code parsing is unchanged.
#284: Resolve, Regenerate summary, alert-rule toggle/delete/load,
Acknowledge, trip deletes, system delete and Restart OP25 now surface
failures (ErrorBanner where the page has one, alert() where the page
already uses it). Rules tab no longer says "No alert rules configured"
when the load failed. Confirms added for Resolve (no reopen path exists),
rule delete and trip delete. Reopen deliberately not built — manual
resolve doesn't set resolved_at, so reopening is a lifecycle change.
Frontend unverified: no Node on the authoring box (#207).
Closes#286
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UI_REDESIGN chunk 4 was never finished: 39 app/component files still used
raw Tailwind greys, kept readable in light mode only by the !important
override sheet. In dark mode text-gray-600 is ~2.3:1 on surface, and the
light override maps it to #94a3b8 (~2.6:1 on white) — unreadable in both.
Mechanical mapping (886 classes): gray-100/200 -> ink, 300/400 -> ink-2,
500/600/700 -> ink-muted, bg 950/900/800 -> page/surface/raised,
border 800 -> line, 700/600 -> line-strong, plus hover/divide/placeholder.
Alpha suffixes kept (tokens are color-mix, so /60 works). bg-gray-700,
text-white and marketing pages left alone. text-[9px] -> text-[10px].
Frontend unverified: no Node on the authoring box (#207).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From a live review of drb.cusano.net.
MapView.tsx / globals.css:
- The Leaflet map painted above the sticky Nav (z-40) and modal overlays, so
on Live the account dropdown opened *behind* the map. Pin .leaflet-container
to its own stacking context (position:relative; z-index:0) — keeps Leaflet's
internal pane order, drops the whole map below app chrome. The map's own
overlay UI (legend, rail, clock, fit-all) is outside .leaflet-container and
unaffected. Chosen over raising Nav's z-index, which would float the sticky
header over modal backdrops on ~7 pages.
- Basemap: the "Dark" tile URL is already CARTO's keyless dark raster (so a
prod "API KEY REQUIRED" watermark is a stale build or CARTO rate-limiting
the origin, not this code). Add NEXT_PUBLIC_MAP_TILE_URL as a build-time
override so a keyed style drops in without a code change; add the OSM
attribution the keyless CARTO tiles require.
incidents/page.tsx, alerts/page.tsx:
- Both dumped raw Firestore "requires an index / PERMISSION_DENIED" strings
(with a console.firebase URL) straight into the UI when the composite
indexes aren't deployed (server-26 #13/#51). Collapse those known infra
failures to a plain sentence; any other error passes through verbatim so a
real bug still shows. alerts also now surfaces the events-query error at
all — it was swallowed, showing a false "No alerts triggered yet." on a
public-safety screen.
onboarding/page.tsx: stale comment (/dashboard -> "/").
Untypechecked (no node/npm locally); presentational only — one string
helper, one added error branch, a CSS rule, two tile-URL constants, a
comment. next build in deploy.yml gates it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>