web/src/features/dashboard/README.md
Dashboards: the project Home page (a read-only dashboard viewer), the self-serve dashboard detail page, and the legacy curated Home cards that render as "preset" placements.
web/src/pages/project/[projectId]/index.tsx — Home. Controller resolves the
v3/v4 read path before anything mounts, then renders HomeDashboard.web/src/pages/project/[projectId]/dashboards/[dashboardId]/index.tsx —
dashboard detail. Same controller split (DashboardDetailView).DashboardQuerySchedulerProvider.components/ — the legacy curated Home cards (TracesBarListChart,
ModelCostTable, …) rendered by home-preset-registry.tsx as preset
placements, plus dashboard-scoped dialogs/selectors. Cards receive a
required metricsVersion from the preset context — they never read the
session themselves.hooks/ — useDashboardQueryScheduler.tsx: the scheduler controller hook,
provider/context, executeQuery cache policy, and
useScheduledDashboardExecuteQuery (the one entry point widgets use to run
a dashboard query under the concurrency budget, over tRPC or SSE).stores/ — dashboardQuerySchedulerStore.ts: per-mount vanilla Zustand
store owning the widget query queue (register/unregister/markDone/
resetQueue/setMaxConcurrent). Created by the page, provided via context,
destroyed on unmount.server/ — dashboard-router.ts (tRPC). executeQuery requires an
explicit version and validates v2 against the session's read path.lib/, utils/ — pure helpers (table href building, import/export).Related but outside this folder: web/src/features/widgets/ owns
DashboardGrid / DashboardWidget / WidgetContent and the chart library;
web/src/hooks/useSSEDashboardQuery.ts owns the SSE transport (kept there
because parseSSEBuffer has non-dashboard consumers).
["dashboard.executeQuery", input, retry]
key, so identical widgets share one query/stream and a transport flip reuses
cached rows.readPath prop — never mirrored into a store.items[queryId]?.status === "running" only.useState version counter that
re-rendered the whole grid on every queue transition).useDashboardQueryScheduler.clienttest.ts pins this).cancel_http_readonly_queries_on_client_close, so
when the browser disconnects (the handler breaks and the ClickHouse stream
socket closes) the server kills the query instead of running the abandoned
aggregation to completion; max_execution_time bounds any straggler.dashboard.chart
query names. Next slice: extract the detail page's paste/import workflows
into actions/*.ts.Read .agents/skills/frontend-large-feature-architecture/SKILL.md (and its
references/local-feature-state.md) before adding state or effects here. For
chart/formatting work, web/src/features/widgets/chart-library/ARCHITECTURE.md
owns the data → preparer → visualiser contract.