Repository navigation
v4.1.0 - A light theme, and every source wears its mark
Dispatch has been a dark room. Fine at night, wrong at a sunny desk, and worse next to the white windows you keep it beside. The sources it reads wore text badges and borrowed glyphs: a branch icon for GitHub, a speech bubble for Slack. You could tell them apart after a week, not on day one.
This release gives Dispatch a light theme that follows your Mac, and gives every source its own mark. Nothing you rely on changes. The board, sessions, playbooks, Vault and board.db carry over with no migration. Underneath, the groundwork for the next releases is in place: a frontend architecture standard, a shadcn and Tailwind toolchain with 35 primitives, typed HTTP errors at every route, and a repository layer in front of the board store. None of it changes what you see today. Every byte of it was proven identical before merge.
Light, dark, or whatever the Mac says
Open Settings, Appearance, Theme. Three buttons: System, Light, Dark. A click applies at once, with no save and no reload. Under System, Dispatch follows the macOS appearance. An evening switch to dark in System Settings flips the board too. Every open tab follows. A reload never flashes the wrong theme, because a script reads your choice before the first frame. A running terminal stays live through a switch. Dispatch never remounts the terminal frame.
The default is System. If your Mac is in light mode, Dispatch is light after this upgrade, with no action from you. Choose Dark to keep what you had.
The dark theme changed a little on the way. Corners are 8 and 12 pixels instead of 6 and 10. Filled buttons darken on hover instead of lightening. The state chip and the column labels mix their colour with the text colour, so they read as text and not as paint. We measured every text and surface pair in both themes: 185 pairs per theme, none below the threshold.
Each source wears its mark
GitHub, Linear, Sentry and Slack show their own marks, taken from simple-icons. Granola and Calendar got marks drawn for Dispatch. They appear on board cards, in the Inbox and on Today, on the Connections tab and in the setup wizard, on the six Sources rows in the sidebar, and on the nodes of the Flow page. The source badge is now an 18 pixel tile with the mark and no text. The name is on the tooltip and on the accessibility label. Agent, local and group items keep their icons.
Git worktrees next to the main checkout
A git worktree next to its main checkout showed up as a second repo on a card, and the card start failed with branch-conflict. Dispatch now identifies a repo by its git common directory. The main checkout wins, Dispatch skips the duplicate, and the start records one warning that names the skipped folder. Cards saved before this release still list the duplicate. The same guard is what lets them start now.
What changed underneath
docs/standards/frontend-architecture.mddefines seven layers (route, view, container, component, hook, domain utility, query), an import matrix and three global bans. ESLint boundaries and dependency-cruiser enforce them. Three service-direction rules that were warnings are errors now.- Tailwind v4 and the shadcn toolchain are installed, with 35 primitives in
src/web/components/ui/themed from the tokens. No page uses them yet. The pages move in the next releases. A dev-only gallery shows them. - Every HTTP route parses its params, query and body with zod and throws a typed error (
ValidationError400,NotFoundError404,ConflictError409,UpstreamError502,InternalError500). One handler, mounted last, writes{ "error": "<code>" }. Hand-writtenres.statuscalls fell from 272 to 1. We proved every status and body byte-identical against the previous release: 62 contract rows and 113 live requests. - Services reach the board store through a
BoardRepositoryinterface of 102 members, behind a proxy that only the bootstrap retargets. Tests get a fake repository. - The repo tracks its agent hooks: a pre-edit rule that refuses inline styles, hex colours, Radix outside
components/uiand server data outside query files, a formatter per edit, and a static check before a session stops. Every hook fails open. The hooks need Claude Code CLI 2.1.9 or later.
Known limits
We swept the light theme on the main surfaces only. Menus, modals, toasts, the command palette, hover and press states and the terminal panel were not measured in light. Disabled controls read near 2.2:1 and off nodes on the Flow page below 2:1 in light. A white Linear state colour reads between 3.96:1 and 4.39:1 on inset and pressed surfaces. Overlays from the new primitive set open without motion. The theme hook has no automated test because the repo has no DOM test runner. Graded browser runs prove it instead. A future script-src content security policy would block the pre-paint script.
Upgrade notes
No migration and no configuration step. Restart Dispatch and reload. The theme follows your Mac unless you pick Light or Dark in Settings, Appearance. For contributors: npm run check now runs dependency-cruiser, knip in production mode and the hook checks. A git pull in a checkout with a local CLAUDE.md or .claude/settings.json overwrites them, because both are tracked now.
See it
dispatch-v4.1.0-light-theme.mp4
Higher-quality video with narration: dispatch-v4.1.0-light-theme.mp4
Full changelog: v4.0.0...v4.1.0
Requires Node ≥ 22.22, tmux, ttyd, git, and the Claude Code CLI. Setup is in the README.
