Development
New here? Start with the Quickstart for a guided first run, then use the deployment overview to choose how the dashboard connects to bunqueue. Control-agent environment variables are documented under Control agent configuration. This page is the day-to-day workflow: run, gate, and how to add a page additively.
Run
bun install
bun start # agent + dashboard together (Ctrl-C stops both)bun start (scripts/dev.ts) is the one-command path. Prefer separate processes? The granular commands still work:
bun run agent # control agent (start/stop/restart) → 127.0.0.1:6800
bun dev # dashboard → http://localhost:5273Point it at a server via Settings (or VITE_BUNQUEUE_URL). In dev, /api/* is proxied to http://localhost:6790.
Quality gate
bun run qualityThis is the exact blocking gate run by the CI workflow on every push and pull request. Release, Pages, and Docker run the same command before publishing. It executes, in order:
bun run check: Oxlint plus an Oxfmt formatting check (bun run check:fixapplies safe lint fixes, then formats files).bun run build: strict typechecks forsrc/,agent/, andscripts/, then the production Vite build.bun run size: initial-load and total JavaScript bundle budgets.bun run docs:build: the VitePress production build, including dead-link checks.bun run test:coverage: the complete Bun test suite plus aggregate coverage floors.bun run audit:high: a blocking dependency audit for HIGH and CRITICAL advisories.
CI also runs bun run test:e2e:browser as a separate blocking matrix on Chromium, Firefox, and WebKit. The suite uses the production bundle, a non-root BASE_PATH, an authenticated disposable Bunqueue 2.8.59 process, and a temporary database. It verifies the token gate, full sidebar navigation, SSE reconnection after an actual upstream restart, confirmed Cron mutations, and automated WCAG A/AA rules. For a local first run:
bun run test:e2e:browser:install
bun run test:e2e:browserThe audit has one ID-specific exception: GHSA-qwww-vcr4-c8h2 affects React Router's RSC mode. This project is a client-only BrowserRouter SPA and has no RSC request handler or server actions, so that advisory is not applicable. The exception does not suppress any other advisory; a new HIGH or CRITICAL finding fails the gate. Remove it if the app adopts RSC, or when a compatible patched React Router release becomes available.
Notes:
.oxlintrc.jsonand.oxfmtrc.jsonare the committed root configurations for Oxlint and Oxfmt. Oxlint runs the JavaScript, TypeScript, Oxc, Unicorn, React, and JSX accessibility plugins with the project's curated severities;oxlint-tsgolintprovides the type-aware rules. The lint script also retains Biome's implicit-anydeclaration check, which Oxlint does not yet implement. Oxfmt keeps the established two-space, 100-column, single-quote style and deterministic imports.src/index.css(Tailwind v4 at-rules),agent/, andscripts/are excluded from formatting and linting. They are not skipped by typechecking:tsconfig.jsoncoverssrc/, whiletsconfig.agent.jsoncovers both Bun runtime directories (except the generatedscripts/embedded.gen.ts).bunfig.tomlpreloadstest/setup.ts(alocalStorageshim) so store imports work underbun test.
Adding a page (additive)
- Create
src/pages/control/MyPage.tsx(a new file). Usebqfor data, theui/*kit for layout (see components.md),usePolledDatafor polling. - Wire it in
src/App.tsx(a new<Route>). - Add a nav item in
src/components/layout/Sidebar.tsx'sNAVarray (reuse an existing icon or add one toui/icons). - If the page shows a job/queue state, drive its state-dependent buttons off
lib/jobActions.ts::actionGatesrather than re-deriving which actions are dashboard-authorized, see api-mapping.md. Upstream endpoint acceptance alone is not authorization: the shared gates intentionally keep DLQ retry and completed-job requeue false.
Both steps 2 and 3 are required, a route with no nav entry (or vice versa) is a dead end. src/pages/Alerts.tsx is exactly this: fully built, routed nowhere, findable only by reading the source (see pages.md). Don't leave a new page in that state.
Do not rewrite existing pages or the api.ts client. Corrected behaviour goes in a new page using bq. If you find a live bug while working nearby, check known-issues.md first, it may already be tracked, and add it there if not, rather than silently patching something out of scope.
Conventions
- Data:
usePolledData(() => bq.x())returns{ data, error, loading, refetching, refetch }. RenderLoadingStateon first load,ErrorStateon failure with data absent, otherwise the content (keep last data while refreshing). - Actions: call
bq.*thenrefetch(); guard destructive ops withwindow.confirm; surface failures inline. - Formatting: use
lib/format(formatNumberuses.thousands; times are relative; durations fromstartedAt/completedAt). - Styling: Tailwind tokens (
bg-surface,text-muted,border-line,text-accent),.tnumfor numbers, mono for IDs. - Keep files focused; prefer new small components over growing a page past ~300 lines.
Tests
Use bun test for a fast local iteration and bun run test:coverage for the same suite with the CI coverage floor. Tests live under test/ and cover pure logic, stores and clients, component regressions, SSE parsing, and control-agent behaviour. Add focused regression coverage there for every bug fix or new testable behaviour.