Svelte 5 (runes) + TS + Vite SPA in dashboard/. Built into
src/serena/resources/dashboard/ and served by Flask in src/serena/dashboard.py
at /dashboard/. dashboard/CLAUDE.md is the authoritative ruleset — read it
before editing anything under dashboard/; this memory only records the
hard invariants.
- Backend is a frozen contract.
dashboard.pyendpoint names, request/response shapes, ports, and the host-header check must not change from the frontend. Canonical route list =API_ROUTESindashboard/vite.config.ts. - Build output is committed & CI-enforced. After any
dashboard/srcchange:npm run build(writes hashed assets tosrc/serena/resources/dashboard/) and commit the regeneratedindex.html+assets/. Stale output fails CI (.github/workflows/dashboard.yml; poe taskpoe build-dashboard). - Before committing dashboard work:
npm run formatand stage all changes — a partial stage leaves files prettier-dirty and CI'sprettier --checkfails. prebuild(scripts/clean-assets.mjs) clearsassets/becauseemptyOutDir: false(the dir also holds icon/logo PNGs).
npm run dev (Vite :5273, proxies API to a Serena server on :24282) ·
npm run build · npm run check (svelte-check) · npm test (Vitest) ·
npm run lint / npm run format. Dev needs a running Serena MCP server with the
dashboard enabled; logos/icons 404 under dev (backend-served).
- Layered:
lib/api/(types.ts→endpoints.ts→client.ts, the onlyfetch) →lib/stores/*.svelte.ts(runes singletons, getter-only) → components. Neverfetchfrom a component. - Two backend failure channels (non-2xx
ApiErrorAND HTTP-200{status:'error'}); normalize every mutation throughrunMutation(). $derivedfor computed values;$effectonly for true side effects (never to sync state). Reactive collections useSvelteSet/SvelteMap. Colors viavar(--token)fromstyles/tokens.css, never hardcoded. Charts only throughChartPanel.svelte. Snippets, not<slot>. No jQuery.- Each store/lib gets a unit test; each component a render+interaction test.
Vitest + jsdom; chart-mounting tests must mock
chart.js/auto.