Dashboard
A Next.js app, built with the design system, that shows how contexts compare, every run, and the page an agent wrote, from the Postgres history.
What
apps/web is a dashboard on top of the database. It needs Postgres (bun run db:up) and
runs on port 3000:
bun run web| Page | Shows |
|---|---|
/ Overview | One sentence and three stats on what the benchmark found (lib/findings.ts); a heatmap of contexts × models with a mean column, one metric at a time (?metric= design system, quality, done, cost), one domain (?domain=, banking by default; guide 24) and a batch (?label=); models with fewer than five runs hidden unless ?all=1; cost per run against violations on a log-log chart |
/runs | Every real run, 50 to a page (?page=), filtered with five Selects (?domain=, ?model=, ?context=, ?task=, ?label=), sorted by a column head (?sort= ds, quality, steps, cost or started, ?dir=asc); every choice is in the address. ?batch= and ?rule= narrow it from other pages |
/batches | One row per benchmark call: batch label, model, context, design-system hash, mean violations per run, cost. A "new version" badge marks the first batch after the design-system hash changed |
/runs/<id> | One run: cost, tokens, batch and design-system hash with copy buttons; the screenshot next to the code, its lines numbered and those with a violation marked; violations in an accordion by rule, each linked to its line; every step; a "Compare with…" menu of the task's other runs |
/runs/compare?a=&b= | Two runs side by side: their numbers, their screenshots, and the rules each broke, the biggest difference first |
/tasks | Which tasks are hardest (share done, quality and design-system violations per run, read on the series that ran every task: difficulty.ts), then the ten tasks: title, summary, runs, no help against the best context, the components a good answer uses |
/tasks/<id> | One task: the brief as the agent reads it, the texts the page must show, the best and worst runs, a heatmap of the task alone, the reference answer |
/models/<provider>/<model> | One model: runs, violations and cost per run, a row per context, the rules it breaks most; every model name in the app links here |
/rules | The rules broken most, by group (?group=), domain (?domain=) and model (?model=), with one message as an example |
/methodology | How it works: the five contexts, the three groups of rules, the tasks, cost, how the overview counts, what it does not say |
/runs/<id>/screenshot/<theme> | The PNG stored with the run |
/chat | Ask in plain language; answers come from tools over these same queries and from the docs (guide 17) |
The queries are in packages/bench/src/dashboard.ts (overview, listBatches, listRuns,
filterOptions, getRun, getScreenshot). They count like bench:history: violations marked ignored or
covered are not counted (anecdote 08).
The app imports @vektos/bench/dashboard, a subpath, so it does not bundle the runner, the
AI SDK or Playwright.
Why
- Built with the design system. Every component on the page is from
@vektos/ds; the rest iscss()with tokens. A test (dogfood.test.ts) gradesapps/web/srcwith the benchmark's own design-system graders and fails on a single violation. A dashboard that breaks the rules it measures would not be credible, and writing it showed what the rules feel like from the agent's side (anecdote 09). - The chart answers the thesis. "Which context gives compliant code at the lowest cost?" is the bottom-left of one picture. Color is the context, shape is the model, and a table with the same numbers sits below it.
- Server components, no client state. Pages read the database on the server. Filters are links. The only client code is the dark-mode switch and the header's current-page marker.
- Dark mode from the start. The choice is remembered, and a small script in
<head>restores it before the first paint so a dark page does not flash white. - Screenshots from the database. The
byteacolumn is served by a route handler, so the dashboard does not needresults/on disk. - A broken database is a page, not a crash.
error.tsxsays to start Postgres. - Every name leads somewhere. A model, a context, a task, a batch label, a batch and a rule are
links wherever they appear: the tables, the run's header and batch card, the chat's tool results.
They all go to the run list with that filter, through one helper (
runsHrefinapps/web/src/lib/links.ts), so a name always leads to the same place. A count in the Overview's table opens the runs behind it./runstakes?batch=and?rule=(a11y/color-contrast) as well, shown as a sentence with a way to remove them. - A link looks like one. Text links are the design system's
Link, underlined inaccent.text; the filter pills, the header and the Overview's badge links keepnext/linkwith a class of their own.Router(components/router.tsx) gives the design system Next's router throughRouterProvider, soLink,BreadcrumbandPaginationmove without a page load and prefetch on hover. (Until chapter 38 a global rule styled every<a>without a class.) - A run's page starts with a
Breadcrumb(Runs / task), its error is anAlert, and its screenshots areTabs, light first. The run list pages withPagination.
Try it
bun run db:up && bun run db:migrate
bun run web # http://localhost:3000
bun run build # a production build; pages read the database at request time
bun test packages/bench/src/dogfood # the dashboard follows the design system
bun run check:db # the queries, against a real database
bun run check:app # with the app running: every page at 1280 and 390, light and dark, axeClick a model's name anywhere, then a rule in one of its runs: /runs?rule=a11y%2Fcolor-contrast
lists every run that broke it. Open /runs?model=moonshotai/kimi-k3.1-pro&context=none, pick a run, and compare its
screenshot with the same task at ds-mcp-check-full. Open /batches to see which design-system
version each batch used, and /?label=smoke to see only the smoke-test batches.
Extend it
- A new page. A file in
apps/web/src/app/, a server component,await connection()first so Next does not run the query at build time, and a query indashboard.ts. - A token the page needs. Add it in
packages/ds/panda.config.tsand runbun run --filter @vektos/ds prepare. Adding one changes the design-system hash of later benchmark batches, because the agent reads the same table. - Responsive layout. Use the breakpoints (
{ base: "4", md: "8" }, mobile first). The totals row is a grid of two columns that becomes four frommd; page padding grows the same way. Cards in a row useflexWrapwithflexBasis: "60"andminW: "0", so a long line of code scrolls inside its block instead of widening the page. - Tasks are read from the repository at run time (
lib/tasks.ts, Node'sfs), not from the database.next.config.tsshipstasks/with the server (outputFileTracingIncludes); a deploy that leaves the repository out needs them in the database instead. - Syntax colours in the code. The code view numbers and marks lines; colouring tokens would need a highlighter and colour tokens for it.
Known gaps
-
check:appruns in CI on demo runs (guide 20): fifteen addresses, a run's page and a model's page included, at two widths and two themes. Lighthouse is not run. -
Task difficulty depends on one panel. Only the series that ran every task count; a new task run by one series leaves no panel until a series has run all of them. The design-system column mixes contexts, so it mostly reflects the runs without context.
-
The share preview is one picture for every page.
opengraph-image.tsxdraws the name and the question with@vektos/ds/raster; a run or a task does not get its own. -
No auth, no write path. It reads the database; anyone who can reach port 3000 sees every run, including the code agents wrote. Fine on a laptop, not on the internet.
-
The chart is read-only. No hover table, no zoom; the points have a
<title>and the table has everything. The chart is hidden from screen readers on purpose, because the table carries the same numbers. -
No trend chart.
/batcheslists batches in time order and/regressionscompares versions (guide 23), but nothing draws a line per model and context across batches. -
The finding compares means of means. Each model counts once, whatever its number of runs; the cost is the median of each model's ratio, so one expensive model does not decide it. Both are said on the page, not argued.
-
The metric and batch controls need JavaScript. They change the address with the router; without scripts they do nothing (the links to "show all" and to the runs still work).
-
Filters do not know each other. On
/runs?rule=…the selects list every model and context, including ones with no run that broke the rule, so a choice can lead to an empty list. -
No sticky table head. The runs table scrolls with the page; its head goes with it. A sticky head needs the table's scroll box to scroll vertically, and
TableScrollscrolls sideways. -
Names in the chat's sentences are not links. Only the tool results are.
-
Tables scroll sideways on a phone, inside their cards; the chart is small there.