Web dashboard¶
Vite + React dashboard for the spectrafit benchmark. It fetches /api/report
from the FastAPI service in python/oracles/ and renders it as two
destinations — Standing (verdict) and Evidence (data and verification; #audit
redirects here) — each a declarative PanelRecord in
src/panels/registry.tsx. A built
deployment of this dashboard is published at web/.
Run locally¶
The dashboard proxies /api to a live API on :8000, so start that first,
from the repo root:
Then, from web/:
Tests¶
tsc --noEmit (via npm run build / npm run typecheck) is the type-check
gate; there is also a Playwright e2e suite driven from the repo root via
uv run poe web_e2e (needs both the API and Vite dev server up).
Two TypeScript packages are installed on purpose. The type-check runs the
TypeScript 7 native compiler, installed under the alias @typescript/native
and called by path from the typecheck script. The plain typescript package
stays on 5.x because openapi-typescript builds src/openapi.gen.ts with the
TypeScript JavaScript API, which TypeScript 7 no longer ships, and declares a
typescript@^5.x peer.
Regenerating the OpenAPI contract¶
The TypeScript types in src/openapi.gen.ts are generated from the live
OpenAPI schema published by the FastAPI app — there is no hand-kept schema.
With the API running (uv run poe serve):
src/contract/index.ts re-exports the named view types from the generated file, so
downstream view code never needs to change. After any contract-affecting
change, prefer uv run poe contract_regen from the repo root — it
regenerates this file plus the two other checked-in schema mirrors
(web/openapi.snapshot.json and the Python golden) in one shot.
Adding a dashboard panel¶
The dashboard panel registry (src/panels/registry.tsx) is the single source of truth — every panel (plot, table, card) is a declarative PanelRecord entry that specifies its title, destination, and rendering function.
Panel record structure¶
Each PanelRecord carries:
id: unique panel identifier (e.g.,"accuracy-parity")dest: destination ("standing"/"evidence"/ removed"audit")scope: visibility scope ("static"for Standing,"overview"or"case"for Evidence)section: grouping within the destination (e.g.,"sec-finding","sec-compare")title: panel heading (string or function of the report)caption: descriptive text shown below the title (optional; string or function)make(r, ctx): render function that returnsSVGSVGElement(for plots) orReactNode(for composite panels)
Adding a new panel¶
-
Write a body function in the appropriate module under
src/panels/bodies/:standing.tsxfordest: "standing"evidenceOverview.tsxfordest: "evidence", scope: "overview"evidenceCase.tsxfordest: "evidence", scope: "case"
The function receives the
report(the fullBenchReport) andctx(context metadata), and returns either an SVG element or React JSX. Plot panels usePlotMountfor responsive SVG rendering; table panels use React components. -
Import the body function at the top of
src/panels/registry.tsx -
Add a record to
PANELS[]following the pattern of existing entries: -
Run the tests — the vitest suite includes a render-audit that checks every panel title is stable and no hardcoded backend IDs appear:
The registry renders all panels via renderPanels(dest, report, ctx) in src/shell/renderPanels.tsx, which filters by destination and scope — no conditional logic needed in your body function.
Running & previewing the dashboard¶
Beyond the basic npm run dev loop in Run locally above, the
full dev-server workflow also covers:
- Both servers together. The dashboard needs the FastAPI backend
(
uv run poe serve, port 8000) and the Vite dev server (npm run dev, port 5173) running at once; Vite proxies/apito:8000. - Port conflicts. If
:8000is already bound by a staleserveprocess, free it withlsof -tiTCP:8000 -sTCP:LISTEN | xargs kill(the same one-linercontract_regenuses internally) before starting a fresh one. - Full pre-push smoke check.
uv run poe web_smokerunscd web && npm ci && npm run typecheck && npm run smoke && npm run build— typecheck, vitest (smokeis an alias forvitest run), and a production build (which itself re-runstsc --noEmit) in one shot. - End-to-end (Playwright).
uv run poe web_e2erunscd web && npx playwright testagainst a live API + Vite dev server; install the browser once withnpx playwright install chromium. - Offline
report.htmlbundling.uv run poe report_htmlruns the benchmark and bundles the latest run into a self-contained, data-inlinedreport.html(equivalent tonpm run build:htmlwithBENCH_JSONset to the run's results);uv run poe report_e2ethen runs a Playwright pass against that bundled file (REPORT_HTML_PATHoverrides the auto-detected path). - Contract regen. After any FastAPI schema change, prefer
uv run poe contract_regen(repo root) over the manualnpm run contractstep above — it drives one live API instance and regenerates all three checked-in schema mirrors (web/src/openapi.gen.ts,web/openapi.snapshot.json, and the Python golden) together.
Next steps¶
- Look up the endpoint this dashboard fetches — Python: benchmark
API covers the
/api/reportFastAPI app, its/api/v1vs. legacy dual mount, and the CORS constraint tied to the report's git provenance. - See the full tooling configuration —
pyproject.toml, explained covers why thepoetasks referenced above (report_html,contract_regen,web_smoke) are configured the way they are.