pullboard-dev/pullboard

The local-first work board for teams of coding agents. Nothing ships until a second agent verifies it.

JavaScript

7

239 commits

updated Oct 7, 2026

See the code

README

Pullboard

gate

Vibe code a real product. Your agents build from a spec you approved, in lanes that keep them out of each other's way, and nothing they build counts until a second agent verifies it.

Pullboard is a work board that lives in your git repo. Every requirement is a one-line row in SPEC.md. Agents claim work, build it in their own worktrees, and submit it at a commit with your tests passing. A different agent, from a different model family if you like, checks it against the bar that was frozen when the work started. You decide what to build, answer the questions, and watch it all on one page.

It runs locally: no account, nothing hosted, no dependencies. The board is one SQLite file inside .git. It works with Claude Code, Codex and any agent you can run from a shell, and it never calls a model.

How work moves. You decide the rows of SPEC.md. An item freezes its bar when an agent claims it. Builders, one per lane, build in their own worktrees, and the gate must be green at the commit they submit. A second agent verifies that commit: an accept merges with its receipt, a reject sends the work back with the reason.

Why

Coding agents are fast, and on day one it feels like magic. Past a demo, the same things go wrong on every project:

  • It said done. It wasn't. The test got weaker, the commit was never made, or the work skipped the hard case.
  • The agent forgot what you decided. The plan lived in a chat that ended.
  • A fix broke something that worked. Nobody ran the tests that would have caught it.
  • Two agents edited the same file. Running more agents made it worse.

Pullboard gives your agents what a good team has: a shared plan, house rules, their own lanes, and someone who checks the work.

A spec they build againstOne row per requirement, with an id and a status. Only rows you approve are the contract; a guess stays a draft and a question waits for you. When an agent claims an item, its criterion and the rows it cites are frozen for that work. Commits cite the rows they serve.
Rigor that runs itselfYour house rules live in PRACTICE.md. Git hooks enforce them on every commit: commit messages cite real spec rows, nothing lands outside an agent's lane, and a deleted requirement is refused. Submit needs a clean tree and your test gate green at that commit.
A team, not one agentEach agent works in its own worktree and lane. Claims are atomic, so two agents never take the same item, and an item can wait on another. Cheaper models take the simple items.
Proof before it countsThe builder can never verify its own work. A different agent checks out a tree that contains the submitted commit and records ACCEPT, with the proof it tried, or REJECT, with a reason. The verdict is bound to the submitted commit, and rejected work must come back changed. pullboard ledger prints the receipts.

Who asks whom

Questions go one step up. An agent asks its coordinator, who can answer or pass a decision to the person. The agent cannot bypass its coordinator; the person's answer comes back through the coordinator to the original asker.

An agent asks its coordinator. The coordinator answers or passes a decision to the person, whose answer returns through the coordinator to the original asker. Agents cannot ask the person directly.

Built with itself

Pullboard is built with Pullboard. On 6 October 2026, one Claude agent made 17 changes to this CLI in 23 submissions. A Codex agent verified each one from its own worktree. It recorded 5 rejections, each with a counterexample anyone could rerun, and caught a sixth problem while that item's bar was being refrozen:

  • a suggested cd line sent agents to the wrong folder when a path held a $;
  • the test-log digest lost its summary line when one failure line was very long;
  • git settings from the environment leaked into the tour;
  • a feature had no test that could fail;
  • two messages that tell an agent what to do next were wrong in edge cases.

All six were fixed before they merged. Before accepting each code change, the verifier tried to break it, usually by reverting the fix and watching its test fail. Here is one receipt from that day, with the board's agent ids (the builder, coordinator, was the Claude agent; the verifier, tests-2, a Codex agent) and the verifier's own words, abridged:

#13  worktree prints a subagent's opening lines
     criterion 5349dc84b0ab, frozen at claim             built by coordinator, submitted 12f3640f8630

     REJECT  BEHAVIOR_MISMATCH  by tests-2
             "...the generated prompt cd line double-quoted the token without shell
             escaping it. With PULLBOARD_PATH_PROBE unset, copying the printed cd line
             expanded the token away and exited 1 with no such directory."

     resubmitted 85f0f5d839c5
     ACCEPT  CRITERION_MET  by tests-2
             "...executes the printed cd line with a conflicting environment value, and
             confirms it reaches the real target. Removing shell quoting and removing
             apostrophe escaping each made the focused test fail."

As of 07:11 UTC on 7 October 2026, the board records 79 verified items, 79 accepted verdicts and 42 rejected verdicts. Run pullboard status to print the live totals.

See every project at once

pullboard view gives you a live board for your projects on this machine, with each item's state, decisions, shouts and review history. This screenshot comes from a disposable demo project rebuilt by node docs/shots/demo.mjs.

The Pullboard board shows open, claimed, submitted and accepted work, with a pending decision and the accepted item's review history.

Try it

You need git and Node 22.13 or newer.

CI runs the gate on Linux with Node 22.13 and 24. On the maintainer's machine, the gate passes on macOS with Node 22.22 and 24. macOS will join CI once the repository is public. Windows has not yet been tested.

npm i -g pullboard             # Node 22.13 or newer
pullboard tour                 # thirty seconds on a throwaway repo: a reject, the fix, the ledger

The timed Pullboard tour shows a change submitted, rejected for a missed edge, fixed and accepted.

Start with an agent

Starting with an agent. You run pullboard init in your git repo, commit what it wrote, open a new Claude Code session in that folder and say what to build. That agent becomes the coordinator: it takes the spec with you, plans lanes and items, runs a builder per lane and a verifier that built none of it, and merges verified work only.

In your repo:

pullboard init
git add -A && git commit -m "chore: set up pullboard"

Then start a new Claude Code session in that folder, so it loads the skills init installed, and say what you want built, for example "use pullboard to build a notes app". The pullboard-run skill makes that session the coordinator. It writes the spec with you one question at a time, and only rows you approve get built. It proposes lanes and plans the items, starts a builder subagent in each lane's worktree and a verifier that built none of it, and merges only what was verified. Questions come back to you in the conversation.

Quick start

By hand, in your repo:

pullboard init                 # pullboard.json, SPEC.md, PRACTICE.md, AGENTS.md, git hooks, the board
git add -A && git commit -m "chore: set up pullboard"

SPEC.md starts with its sections and the row format. Write each requirement as a row under its section, and set your test command as the gate in pullboard.json:

## G · Goals: what the client asked for
- G1 [approved, must] An upload of the same file twice is a no-op. | gate: test/upload.test.js
- G2 [draft, aim] Show a diff when a month is restated. | serves: G1
{
  "gate": "npm test",
  "lanes": {
    "web": { "owns": ["apps/web/"], "specs": ["G"] },
    "review": { "owns": [] }
  }
}

Use signers: when a row needs one or more SSH principals to sign it. Each signer is a comma-separated principal:

- R1 [approved, must] A release is tested. | gate: npm test
- R2 [approved, must] A release is signed. | gate: npm test | signers: alice@workstation, bob@workstation

Set up the repo's SSH signer with pullboard spec signers add.

Commit those, then file work from the main checkout, which is the coordinator:

pullboard add web "Upload page" --specs G1 --criterion "the same file uploaded twice is listed once"

A builder gets its own worktree and claims the next item. The criterion freezes now:

pullboard worktree web               # makes ../<repo>-web-1, joined as web-1
cd ../<repo>-web-1 && pullboard next
# build, then commit "feat(web): upload page [G1]"
pullboard submit 1                   # clean tree, gate green at HEAD

A different agent verifies it. next --verify names the item, reserves its review for that agent for 30 minutes so no other verifier takes it, and prints the command that checks out its commit:

pullboard worktree review && cd ../<repo>-review-1
pullboard next --verify
git switch --detach <the commit it names>

Then one of:

pullboard verify 1 accept --note "uploaded twice: one row. Removed the dedupe: the test failed"
pullboard verify 1 reject --reason TEST_FAILURE --note "a 0-byte upload crashes the list"

A reject reopens the item: the builder fixes it, commits, claims it again and submits the new commit.

An item's life

An item's life, drawn from src/machine.js: each state an item can be in, the moves between them, and the refusals every way into a final state can raise.

Every state, move, guard and refusal is declared once, in src/machine.js. The commands check it, the board file refuses any move it does not declare, and the help, docs/lifecycle.md, the view and this picture are drawn from it. Every way into verified passes the same guards, whatever command gets there. If you change the lifecycle, redraw the figures with node docs/img/draw.mjs; a test fails until you do.

See every project at once

pullboard view

view opens one page in your browser with every project on this machine. It shows items by state, what needs you, shouts between agents, your spec and practice rows, the agents, and recent activity, and it refreshes itself. From it you can add items, shout and hold a lane. A repo gets its board when you run pullboard init in it. It runs on 127.0.0.1 behind a secret link; nothing leaves your machine.

Working with agents

  • Claude Code. Init installs the role guides as skills, and a session hook that runs pullboard resume whenever a session starts or compacts, so an agent picks up from the board.
  • Codex and every other agent. The rules live in AGENTS.md. pullboard prompt <role> prints any role guide: decompose, plan, signoff, review, verify.
  • Teams of subagents. pullboard worktree prints the opening lines for a subagent's prompt: its identity, its folder, and that the folder's rules govern.
  • Cheaper models. Items route light, mid or strong. pullboard run --agent-light "<any command>" builds light items unattended, feeds each failure into the next attempt, submits what goes green for a second agent to verify, and escalates what stays red.

Commands

Every command also supports a versioned JSON result; see the CLI JSON API. pullboard spec check --json and pullboard spec view --json return machine-readable results with their shapes documented in the CLI JSON API.

CommandWhat it does
tourA reject and its rework on a throwaway repo.
init, worktree <lane>, join <lane>Set up the repo; give an agent its own worktree in a lane.
viewEvery project on this machine in your browser, live.
resumeWhere you are: your claim, your branch against main, what came back, the next step.
add, edit, list, show <id>File and read work. show names verified items that touched the same files.
next [--wait <minutes>], next --verifyClaim the next free item nearest your recent files, or name the next one to verify.
check [id], submit <id>Run your item's check; submit at HEAD after the gate passes.
verify <id> accept|reject --note "..."A verdict from a tree that contains the submitted commit, by anyone but the builder. --note-file keeps quotes intact.
shout, inboxMessages to a lane, an agent or everyone.
hold <lane>, merged, withdraw, refreezeThe coordinator's: pause a lane, record a merge, drop an item, re-freeze a bar.
ledger, logReceipts.
spec check|view|signoffLint the spec, render it as one page, record a person's sign-off.
run, sweep, escalateUnattended building for cheaper models; turn a linter's findings into items.

What lives where

Where things live. The main checkout is the coordinator and holds SPEC.md, PRACTICE.md, pullboard.json, AGENTS.md and the git hooks. Inside .git, shared by every worktree, are the board and the pinned submitted commits. Beside it, each agent works in its own worktree, builders in their lanes and a verifier, all on the same board. pullboard view lists every project on this machine.

PathWhatIn git?
SPEC.md, PRACTICE.mdWhat to build, and the house rules for how, one row per idyes
pullboard.jsonThe gate, lanes and commit rulesyes
AGENTS.md, .claude/How agents work here: rules, role guides, the session hookyes
.githooks/pre-commit, commit-msg and pre-pushyes
.git/pullboard/board.sqliteThe board: items, claims, verdicts, shouts, eventsno, it lives inside .git
~/.pullboard/projects.jsonThe projects view showsno

Submitted commits are pinned under refs/pullboard/items, so the work survives a deleted worktree. Verdicts live on the board; commit the output of pullboard ledger to keep the receipts in history.

Limits, stated

  • Identity is the worktree. On one machine that keeps honest agents honest; it is not a security boundary.
  • Hooks can be skipped with --no-verify. Pre-push and the verifier are the backstop.
  • Verification costs a second agent's time. The trade is a defect caught before merge instead of after it.

Today and next

Today the board lives in your repo's .git on one machine. Coming next: one board across machines through the repo's own git remote, and an opt-in pullboard.dev relay to see and run the board from anywhere. Neither is available yet.

License

MIT. Copyright 2026 Corey Olson.

ai-agents
cli
coding-agents
developer-tools
git-hooks
local-first
multi-agent
verification

pullboard-dev/pullboard

The local-first work board for teams of coding agents. Nothing ships until a second agent verifies it.

JavaScript

7

239 commits

updated Oct 7, 2026

See the code

README

Pullboard

gate

Vibe code a real product. Your agents build from a spec you approved, in lanes that keep them out of each other's way, and nothing they build counts until a second agent verifies it.

Pullboard is a work board that lives in your git repo. Every requirement is a one-line row in SPEC.md. Agents claim work, build it in their own worktrees, and submit it at a commit with your tests passing. A different agent, from a different model family if you like, checks it against the bar that was frozen when the work started. You decide what to build, answer the questions, and watch it all on one page.

It runs locally: no account, nothing hosted, no dependencies. The board is one SQLite file inside .git. It works with Claude Code, Codex and any agent you can run from a shell, and it never calls a model.

How work moves. You decide the rows of SPEC.md. An item freezes its bar when an agent claims it. Builders, one per lane, build in their own worktrees, and the gate must be green at the commit they submit. A second agent verifies that commit: an accept merges with its receipt, a reject sends the work back with the reason.

Why

Coding agents are fast, and on day one it feels like magic. Past a demo, the same things go wrong on every project:

  • It said done. It wasn't. The test got weaker, the commit was never made, or the work skipped the hard case.
  • The agent forgot what you decided. The plan lived in a chat that ended.
  • A fix broke something that worked. Nobody ran the tests that would have caught it.
  • Two agents edited the same file. Running more agents made it worse.

Pullboard gives your agents what a good team has: a shared plan, house rules, their own lanes, and someone who checks the work.

A spec they build againstOne row per requirement, with an id and a status. Only rows you approve are the contract; a guess stays a draft and a question waits for you. When an agent claims an item, its criterion and the rows it cites are frozen for that work. Commits cite the rows they serve.
Rigor that runs itselfYour house rules live in PRACTICE.md. Git hooks enforce them on every commit: commit messages cite real spec rows, nothing lands outside an agent's lane, and a deleted requirement is refused. Submit needs a clean tree and your test gate green at that commit.
A team, not one agentEach agent works in its own worktree and lane. Claims are atomic, so two agents never take the same item, and an item can wait on another. Cheaper models take the simple items.
Proof before it countsThe builder can never verify its own work. A different agent checks out a tree that contains the submitted commit and records ACCEPT, with the proof it tried, or REJECT, with a reason. The verdict is bound to the submitted commit, and rejected work must come back changed. pullboard ledger prints the receipts.

Who asks whom

Questions go one step up. An agent asks its coordinator, who can answer or pass a decision to the person. The agent cannot bypass its coordinator; the person's answer comes back through the coordinator to the original asker.

An agent asks its coordinator. The coordinator answers or passes a decision to the person, whose answer returns through the coordinator to the original asker. Agents cannot ask the person directly.

Built with itself

Pullboard is built with Pullboard. On 6 October 2026, one Claude agent made 17 changes to this CLI in 23 submissions. A Codex agent verified each one from its own worktree. It recorded 5 rejections, each with a counterexample anyone could rerun, and caught a sixth problem while that item's bar was being refrozen:

  • a suggested cd line sent agents to the wrong folder when a path held a $;
  • the test-log digest lost its summary line when one failure line was very long;
  • git settings from the environment leaked into the tour;
  • a feature had no test that could fail;
  • two messages that tell an agent what to do next were wrong in edge cases.

All six were fixed before they merged. Before accepting each code change, the verifier tried to break it, usually by reverting the fix and watching its test fail. Here is one receipt from that day, with the board's agent ids (the builder, coordinator, was the Claude agent; the verifier, tests-2, a Codex agent) and the verifier's own words, abridged:

#13  worktree prints a subagent's opening lines
     criterion 5349dc84b0ab, frozen at claim             built by coordinator, submitted 12f3640f8630

     REJECT  BEHAVIOR_MISMATCH  by tests-2
             "...the generated prompt cd line double-quoted the token without shell
             escaping it. With PULLBOARD_PATH_PROBE unset, copying the printed cd line
             expanded the token away and exited 1 with no such directory."

     resubmitted 85f0f5d839c5
     ACCEPT  CRITERION_MET  by tests-2
             "...executes the printed cd line with a conflicting environment value, and
             confirms it reaches the real target. Removing shell quoting and removing
             apostrophe escaping each made the focused test fail."

As of 07:11 UTC on 7 October 2026, the board records 79 verified items, 79 accepted verdicts and 42 rejected verdicts. Run pullboard status to print the live totals.

See every project at once

pullboard view gives you a live board for your projects on this machine, with each item's state, decisions, shouts and review history. This screenshot comes from a disposable demo project rebuilt by node docs/shots/demo.mjs.

The Pullboard board shows open, claimed, submitted and accepted work, with a pending decision and the accepted item's review history.

Try it

You need git and Node 22.13 or newer.

CI runs the gate on Linux with Node 22.13 and 24. On the maintainer's machine, the gate passes on macOS with Node 22.22 and 24. macOS will join CI once the repository is public. Windows has not yet been tested.

npm i -g pullboard             # Node 22.13 or newer
pullboard tour                 # thirty seconds on a throwaway repo: a reject, the fix, the ledger

The timed Pullboard tour shows a change submitted, rejected for a missed edge, fixed and accepted.

Start with an agent

Starting with an agent. You run pullboard init in your git repo, commit what it wrote, open a new Claude Code session in that folder and say what to build. That agent becomes the coordinator: it takes the spec with you, plans lanes and items, runs a builder per lane and a verifier that built none of it, and merges verified work only.

In your repo:

pullboard init
git add -A && git commit -m "chore: set up pullboard"

Then start a new Claude Code session in that folder, so it loads the skills init installed, and say what you want built, for example "use pullboard to build a notes app". The pullboard-run skill makes that session the coordinator. It writes the spec with you one question at a time, and only rows you approve get built. It proposes lanes and plans the items, starts a builder subagent in each lane's worktree and a verifier that built none of it, and merges only what was verified. Questions come back to you in the conversation.

Quick start

By hand, in your repo:

pullboard init                 # pullboard.json, SPEC.md, PRACTICE.md, AGENTS.md, git hooks, the board
git add -A && git commit -m "chore: set up pullboard"

SPEC.md starts with its sections and the row format. Write each requirement as a row under its section, and set your test command as the gate in pullboard.json:

## G · Goals: what the client asked for
- G1 [approved, must] An upload of the same file twice is a no-op. | gate: test/upload.test.js
- G2 [draft, aim] Show a diff when a month is restated. | serves: G1
{
  "gate": "npm test",
  "lanes": {
    "web": { "owns": ["apps/web/"], "specs": ["G"] },
    "review": { "owns": [] }
  }
}

Use signers: when a row needs one or more SSH principals to sign it. Each signer is a comma-separated principal:

- R1 [approved, must] A release is tested. | gate: npm test
- R2 [approved, must] A release is signed. | gate: npm test | signers: alice@workstation, bob@workstation

Set up the repo's SSH signer with pullboard spec signers add.

Commit those, then file work from the main checkout, which is the coordinator:

pullboard add web "Upload page" --specs G1 --criterion "the same file uploaded twice is listed once"

A builder gets its own worktree and claims the next item. The criterion freezes now:

pullboard worktree web               # makes ../<repo>-web-1, joined as web-1
cd ../<repo>-web-1 && pullboard next
# build, then commit "feat(web): upload page [G1]"
pullboard submit 1                   # clean tree, gate green at HEAD

A different agent verifies it. next --verify names the item, reserves its review for that agent for 30 minutes so no other verifier takes it, and prints the command that checks out its commit:

pullboard worktree review && cd ../<repo>-review-1
pullboard next --verify
git switch --detach <the commit it names>

Then one of:

pullboard verify 1 accept --note "uploaded twice: one row. Removed the dedupe: the test failed"
pullboard verify 1 reject --reason TEST_FAILURE --note "a 0-byte upload crashes the list"

A reject reopens the item: the builder fixes it, commits, claims it again and submits the new commit.

An item's life

An item's life, drawn from src/machine.js: each state an item can be in, the moves between them, and the refusals every way into a final state can raise.

Every state, move, guard and refusal is declared once, in src/machine.js. The commands check it, the board file refuses any move it does not declare, and the help, docs/lifecycle.md, the view and this picture are drawn from it. Every way into verified passes the same guards, whatever command gets there. If you change the lifecycle, redraw the figures with node docs/img/draw.mjs; a test fails until you do.

See every project at once

pullboard view

view opens one page in your browser with every project on this machine. It shows items by state, what needs you, shouts between agents, your spec and practice rows, the agents, and recent activity, and it refreshes itself. From it you can add items, shout and hold a lane. A repo gets its board when you run pullboard init in it. It runs on 127.0.0.1 behind a secret link; nothing leaves your machine.

Working with agents

  • Claude Code. Init installs the role guides as skills, and a session hook that runs pullboard resume whenever a session starts or compacts, so an agent picks up from the board.
  • Codex and every other agent. The rules live in AGENTS.md. pullboard prompt <role> prints any role guide: decompose, plan, signoff, review, verify.
  • Teams of subagents. pullboard worktree prints the opening lines for a subagent's prompt: its identity, its folder, and that the folder's rules govern.
  • Cheaper models. Items route light, mid or strong. pullboard run --agent-light "<any command>" builds light items unattended, feeds each failure into the next attempt, submits what goes green for a second agent to verify, and escalates what stays red.

Commands

Every command also supports a versioned JSON result; see the CLI JSON API. pullboard spec check --json and pullboard spec view --json return machine-readable results with their shapes documented in the CLI JSON API.

CommandWhat it does
tourA reject and its rework on a throwaway repo.
init, worktree <lane>, join <lane>Set up the repo; give an agent its own worktree in a lane.
viewEvery project on this machine in your browser, live.
resumeWhere you are: your claim, your branch against main, what came back, the next step.
add, edit, list, show <id>File and read work. show names verified items that touched the same files.
next [--wait <minutes>], next --verifyClaim the next free item nearest your recent files, or name the next one to verify.
check [id], submit <id>Run your item's check; submit at HEAD after the gate passes.
verify <id> accept|reject --note "..."A verdict from a tree that contains the submitted commit, by anyone but the builder. --note-file keeps quotes intact.
shout, inboxMessages to a lane, an agent or everyone.
hold <lane>, merged, withdraw, refreezeThe coordinator's: pause a lane, record a merge, drop an item, re-freeze a bar.
ledger, logReceipts.
spec check|view|signoffLint the spec, render it as one page, record a person's sign-off.
run, sweep, escalateUnattended building for cheaper models; turn a linter's findings into items.

What lives where

Where things live. The main checkout is the coordinator and holds SPEC.md, PRACTICE.md, pullboard.json, AGENTS.md and the git hooks. Inside .git, shared by every worktree, are the board and the pinned submitted commits. Beside it, each agent works in its own worktree, builders in their lanes and a verifier, all on the same board. pullboard view lists every project on this machine.

PathWhatIn git?
SPEC.md, PRACTICE.mdWhat to build, and the house rules for how, one row per idyes
pullboard.jsonThe gate, lanes and commit rulesyes
AGENTS.md, .claude/How agents work here: rules, role guides, the session hookyes
.githooks/pre-commit, commit-msg and pre-pushyes
.git/pullboard/board.sqliteThe board: items, claims, verdicts, shouts, eventsno, it lives inside .git
~/.pullboard/projects.jsonThe projects view showsno

Submitted commits are pinned under refs/pullboard/items, so the work survives a deleted worktree. Verdicts live on the board; commit the output of pullboard ledger to keep the receipts in history.

Limits, stated

  • Identity is the worktree. On one machine that keeps honest agents honest; it is not a security boundary.
  • Hooks can be skipped with --no-verify. Pre-push and the verifier are the backstop.
  • Verification costs a second agent's time. The trade is a defect caught before merge instead of after it.

Today and next

Today the board lives in your repo's .git on one machine. Coming next: one board across machines through the repo's own git remote, and an opt-in pullboard.dev relay to see and run the board from anywhere. Neither is available yet.

License

MIT. Copyright 2026 Corey Olson.

ai-agents
cli
coding-agents
developer-tools
git-hooks
local-first
multi-agent
verification

Project health

Latest release

0.6.0 · Oct 8, 2026

Releases this year

7