If you were started in a git worktree (check: does your working directory contain .dispatch/worktrees/?), every file path you read, write, edit, or delete MUST use the worktree path. Never cd to or reference the parent repo at the main checkout path directly — that is the main working tree and other agents or the user may be working there. Edits to the wrong tree silently land in the wrong place, cause merge conflicts, and get lost.
- Your working directory is your worktree root. Use relative paths or the full worktree-prefixed absolute path.
- Run
gitcommands from your worktree — they automatically operate on the correct branch. - Run
pnpm,vitest, and other tools from the worktree root so they pick up your changes. - If you need to verify which tree you are in:
git rev-parse --show-toplevel.
- You MUST call the
dispatch_eventMCP tool throughout every task turn. These events drive the agent status indicator in the Dispatch UI — the more frequently and accurately you report, the more useful the dashboard becomes. - Event types and when to use them:
working— You are actively making progress: reading files, writing code, running commands, researching. Use a short message describing the current activity (e.g., "Reading agent-sidebar.tsx", "Running E2E tests", "Refactoring auth middleware").blocked— You hit an error, a failing test, a missing dependency, or any obstacle you are actively trying to resolve. Message should describe the blocker (e.g., "Type error in agent-type-icon.tsx", "E2E test 'sidebar toggle' failing").waiting_user— You need a decision, clarification, or approval before continuing. Message should describe what you need (e.g., "Should I delete the legacy endpoint?", "Need confirmation on color palette").done— The task is complete and all checks pass. Message should summarize what was accomplished.idle— No meaningful action was taken this turn (e.g., an informational question was answered).
- Required checkpoints (minimum):
- Start of turn:
workingwith what you are about to do. - Phase transitions: Call
workingagain with an updated message whenever your activity shifts to a distinct phase (e.g., moving from research → implementation → testing → validation). This keeps the UI status current. - On error or obstacle: Switch to
blockedimmediately. When you unblock yourself, switch back toworking. - Before final response: Emit a terminal event —
done,idle,waiting_user, orblocked.
- Start of turn:
- Hard requirements:
- Do not send a final response unless
done,waiting_user,blocked, oridlehas been emitted in the same turn. - If
dispatch_eventfails, report that failure explicitly in the response. - Keep messages short (under ~80 chars) — they are displayed in a narrow sidebar.
- Do not send a final response unless
- For any UI/layout/style interaction change, validate behavior in Playwright before marking the task complete.
- Include at least one Playwright interaction that covers the changed UI path (for example: open/close panes, modal flow, or action button state changes).
- For pages with SSE/WebSocket activity, do not use Playwright
waitUntil: "networkidle"for readiness checks. - Use
waitUntil: "domcontentloaded"(or"load") and wait for concrete UI-ready signals (visible control/text/state) instead.
- Prefer shadcn/ui components over hand-rolled UI when an equivalent shadcn option exists.
- Only hand-roll when there is no suitable shadcn primitive or composition path.
- Default to colocating state with the smallest component that fully owns the UI and behavior.
- Do not lift state to a route, layout, or app root unless multiple siblings must coordinate through a shared owner.
- Treat route/layout-level state as a fallback, not a default. Those layers should not become new dumping grounds.
- Treat global client state (including Jotai atoms) as rare and justified only for truly cross-cutting concerns.
- If a piece of state only drives one feature subtree, keep it in that subtree even if persistence is needed. Prefer a small local persistence helper over promoting the whole state domain to global state.
- Prefer URL state over in-memory UI state for shareable/navigation state such as selected entity, active detail pane, or tab when that state should survive reloads, deep links, or back/forward navigation.
- Prefer React Query for server state, request lifecycle state, caching, invalidation, optimistic updates, and background refetching. Do not mirror API state into local UI state unless there is a specific UI-only derivation that cannot be computed from query data.
- Before adding a new atom, first ask:
- Is this server state? Use React Query.
- Is this only used by one component or feature cluster? Keep it local.
- Is this shareable navigation state? Put it in the URL.
- Is this truly app-wide and cross-cutting? Only then consider Jotai.
App.tsxis a composition root, not a feature implementation file. Do not add feature-specific dialog state, selection state, or view toggles there when they can live lower in the tree.
- For UI state that needs to survive reloads, prefer jotai's
atomWithLocalStoragehelper inapps/web/src/lib/store.tsover a hand-rolleduseState+useEffectpattern. The helper handles SSR-safety, JSON serialization, read-on-mount, and write-on-change in one place — and it sidesteps the read/write effect-ordering races that come up when persistence keys change at runtime. - When the persisted value needs to be keyed by another value (cwd, agent ID, route segment, etc.), use jotai's
atomFamilyto produce one atom per key rather than building a custom prefix scheme on top of plain atoms. Example:atomFamily((cwd: string) => atomWithLocalStorage(\dispatch:foo:${cwd}`, default))`. - This is the one place where Jotai is preferred over local state even for feature-specific UI — the persistence machinery belongs in a shared spot, and the atom's identity stays stable across mounts so the value reads back correctly when the dialog/component remounts.
Before marking any task as done, run the following checks and fix any failures:
- Type checking:
pnpm run check(runstsc --noEmitfor backend + web). - Web finalization: If any files under
apps/web/changed, runpnpm run finalize:web(type check + production build). - E2E tests:
pnpm run test:e2e(Playwright). Always spins up its own isolated DB and server — safe to run alongside other agents. - Unit tests:
pnpm run test(Vitest) if backend logic changed.
- Do not consider a task complete until all applicable checks pass.
- If a pre-existing test is flaky (fails before your changes too), note it in your response but do not skip the rest of the suite.
- If any files under
apps/web/changed, runpnpm run finalize:webbefore marking the task complete. - After running
pnpm run finalize:web, verify the served app via an explicitly started local dev stack when the task affects UI/theme/rendering behavior. - After UI/theme/rendering validation on an isolated dev stack, leave that stack running for the user to inspect unless they explicitly ask you to tear it down.
- In the final response, include the exact local URLs/ports for the running validation stack and the cleanup command(s) needed to stop it later.
- Never write temporary files (screenshots, test scripts, scratch files) to the repo root.
- Use
/tmp/or$DISPATCH_MEDIA_DIRfor ephemeral files. - Playwright screenshots should be published via the
dispatch_shareMCP tool, not saved locally.
- NEVER run
pnpm run devdirectly in your terminal — it will block your session and killing it can kill your agent process. - NEVER use
pkill,killall, orlsof | xargs killto manage dev servers — these can kill your own agent process. - Use the repo MCP tools to manage dev environments:
repo_dev_up,repo_dev_restart,repo_dev_down,repo_dev_status, andrepo_dev_logs. They spin up an isolated DB, API server, and Vite frontend on auto-selected free ports. - If you start a validation stack for user review, do not tear it down automatically at the end of the turn unless the user explicitly asks.
repo_dev_upauto-selects free ports and prints the URLs — just use the printed URLs.
- Treat
127.0.0.1:6767as production by default; do not stop or kill the existing production server for ad-hoc testing. - When backend changes need local validation, use
repo_dev_upand point validation tooling to the printed URL. - Only operate on production (
:6767) when explicitly requested by the user.
- Production uses the
dispatchdatabase. Never connect to it from dev servers. repo_dev_upcreates an isolated Postgres container with its own port — no manualDATABASE_URLsetup needed.- Migrations run automatically on API server start.
-
When the user indicates that a release should require or recommend assisted update handling, create or update
release-notes/next-assisted-update.json. -
Follow
release-notes/AUTHORING.mdfor the schema, merge rules, and authoring guidance. Do not invent a parallel format. -
If
release-notes/next-assisted-update.jsonalready exists, merge your change into that file instead of creating a second metadata file. -
Validate the file before finishing with:
pnpm tsx bin/embed-assisted-update.ts --check-only --metadata release-notes/next-assisted-update.json