Contributing
Applies to: Dispatch v1.0.0, last verified 2026-04-05
Dispatch is a fair-code project maintained by The Pixel Labs. Contributions are welcome — bug reports, documentation improvements, and pull requests.
Before you start
Section titled “Before you start”- Read the How It Works and Architecture pages to understand the system before making changes.
- Search open issues and open PRs before filing a new one.
- For significant changes, open an issue first to discuss the approach. This avoids wasted effort on PRs that won’t be merged.
Where to discuss
Section titled “Where to discuss”- Bug reports and feature requests: GitHub Issues
- Questions and design discussions: open an issue with the
questionlabel, or use the Discussions tab if available on the repository - Security vulnerabilities: do not open a public issue. See
SECURITY.mdfor responsible disclosure instructions.
Reporting bugs
Section titled “Reporting bugs”Open an issue on GitHub with:
- Dispatch version (
npm list | grep dispatchor the version inpackage.json) - Node.js version (
node --version) - OS and shell
- Steps to reproduce (exact commands)
- What you expected vs. what happened
- Relevant log output (run the server with
LOG_LEVEL=debug npm startto increase verbosity)
Never include credentials, API keys, or .env contents in an issue. Scrub any paths that reveal personal information.
Branch naming
Section titled “Branch naming”Use the following prefixes:
feat/short-descriptionfix/what-is-brokendocs/what-changedrefactor/what-changedThis is not just a convention — Dispatch uses branch names to link git sessions to epics. When an agent’s branch name matches an epic slug (e.g. feat/user-auth links to the user-auth epic), the Sessions panel shows that relationship automatically. Consistent branch naming keeps the dashboard useful.
Development workflow
Section titled “Development workflow”1. Fork and clone
Section titled “1. Fork and clone”git clone https://github.com/thepixelabs/dispatch.gitcd dispatchnpm install2. Start in development mode
Section titled “2. Start in development mode”npm run devThis starts the Express server and the Vite dev server concurrently. The dashboard is at http://localhost:5173 with hot reload. Server restarts on TypeScript changes via tsx watch.
3. Make your changes
Section titled “3. Make your changes”The codebase has three main areas:
| Area | Path | Language |
|---|---|---|
| Backend server | server/index.ts | TypeScript |
| Frontend dashboard | src/ | TypeScript + React |
| CLI | cli.js | Node.js (plain JS) |
| Docs site | docs-site/src/ | Astro + MDX |
4. Run the tests
Section titled “4. Run the tests”npm test # full suite (vitest)npm run test:unit # parser + state-builder + activity-feed + reducer onlyAll tests must pass before you open a PR. If you changed server parsing logic, run the full suite. If your change is frontend-only, test:unit is usually enough locally — CI runs everything.
5. Type-check and build
Section titled “5. Type-check and build”npm run buildThis runs tsc on the server and vite build on the frontend. Fix any TypeScript errors before opening a PR.
6. Test your change manually
Section titled “6. Test your change manually”- Register a project with
node cli.js add /some/path --name "Test" - Confirm the dashboard shows it at
http://localhost:4242(production) orhttp://localhost:5173(dev)
7. Open a pull request
Section titled “7. Open a pull request”- Keep PRs focused — one logical change per PR
- Write a clear description: what changed, why, and how to verify it
- Reference the GitHub issue number if one exists (
Fixes #123) - All CI checks must pass before merge
Changing documentation
Section titled “Changing documentation”The docs site lives in docs-site/. It uses Astro Starlight.
cd docs-sitenpm installnpm run devThe docs dev server is at http://localhost:4321. Pages are in docs-site/src/content/docs/. Edit .md or .mdx files there.
When adding a new page, also add it to docs-site/astro.config.mjs under the sidebar array so it appears in the navigation.
Writing style:
- State the concrete behavior, not the intent. “The server polls every 2 seconds” not “the server tries to poll frequently.”
- Use specific values. “30 seconds” not “a short time.”
- Lead with what the user needs to know. Put implementation detail at the end or in a collapsible.
- Do not use passive voice when you can name the subject.
Changing agent definitions
Section titled “Changing agent definitions”Agent definitions live in agents/. Each file is a Markdown document with a YAML frontmatter name: field. The name must be unique across all agents and must match [a-zA-Z0-9_-]+.
If you add an agent, add it to the table in docs-site/src/content/docs/agents/catalog.md.
The .tasks/ blackboard protocol
Section titled “The .tasks/ blackboard protocol”Dispatch watches the .tasks/ directory for coordination data written by agents. If you are contributing changes to how Dispatch reads or renders this data, or if you are writing agent personas that participate in multi-agent coordination, you need to understand the protocol.
The full specification is in .tasks/ Protocol Reference. The short version:
- Each epic lives in
.tasks/<epic-name>/with aplan.md(YAML frontmatter for machine state, prose body for context) and an append-onlyexecution-log.md. - Phases in
plan.mdhaveid,title,persona, andstatus(TODO, IN_PROGRESS, DONE, BLOCKED). - Agents claim phases by editing the frontmatter. Only one agent should claim a phase at a time.
- Completed epics are moved to
.tasks/.archive/to keep the active view clean.
When testing changes to the server’s file watcher or state builder, register the sample-project/ directory included in the repo for a realistic .tasks/ directory without needing live agents.
Adding a new provider adapter
Section titled “Adding a new provider adapter”Dispatch uses an adapter pattern for CLI agent execution. Each provider (Claude, Codex, Gemini, Copilot) has an adapter in server/adapters/ that implements the AgentAdapter interface from server/adapters/types.ts:
interface AgentAdapter { readonly tool: AgentTool readonly supportsSpawn: boolean execute(ctx: SpawnContext): Promise<SpawnResult> stream(ctx: SpawnContext, onChunk: (c: StreamChunk) => void): Promise<SpawnResult> checkEnvironment(): Promise<{ ok: boolean; error?: string }>}To add a new provider:
- Add the tool type to the
AgentToolunion inserver/types.ts. - Add the binary to
ALLOWED_CLI_BINARIESinserver/adapters/constants.ts. This is a compile-time constant and the only source of binary names for spawn calls. - Create the adapter at
server/adapters/<provider>.ts. Implementexecute()andstream()to spawn the CLI binary and parse its stdout. SetsupportsSpawn = falseif the provider has no CLI spawn interface. - Register the adapter in
server/adapters/index.tsby adding it to theADAPTERSrecord. - Add a session scanner if the provider stores session data in a custom format (see
server/copilot-session-scanner.tsfor an example). Register the scanner inserver/multi-session-poller.ts. - Write tests in
server/__tests__/adapters/<provider>.test.ts. Mockchild_process.spawn— never shell out to real CLI binaries in tests.
Key rules:
- The adapter does not sanitize prompts.
agent-spawner.tsappliessandboxedContent()before calling adapters (ADR-010). - Binary names come from
ALLOWED_CLI_BINARIESonly — never from user input or config (ADR-009). DISPATCH_API_URLin the spawned env is alwayshttp://127.0.0.1:<PORT>— never from user config.
Code style
Section titled “Code style”- TypeScript: the project uses strict mode. No
anyunless it is unavoidable and documented. - React: functional components and hooks only. No class components.
- Formatting: Prettier is not yet enforced but follow the existing style.
- bash: use
[[ ]]not[ ]. Quote all variables. Error early withset -eor explicit exit checks.
Commit messages
Section titled “Commit messages”Follow Conventional Commits:
feat(server): add DISPATCH_PORT env varfix(cli): default port not applied when PORT is unsetdocs(installation): add pm2 and systemd persistence examplesTypes: feat, fix, docs, refactor, test, chore.
Licensing
Section titled “Licensing”By submitting a pull request you agree that your contribution is licensed under the PolyForm Shield 1.0.0 License that covers this project. at covers this project. covers this project. at covers this project. e 2.0 that covers this project. at covers this project. covers this project. at covers this project. project. at covers this project. covers this project. at covers this project.