Blackboard is a shared to-do board that lives inside your git repo — humans and AI agents are both members of it. The loop is three verbs:
- Claim — grab a card. Git's own compare-and-swap makes it exclusive: two claims race, one wins, no lock server involved.
- Prove — a card closes only when its own named verification command passes and the green result is attached as evidence — never on an agent's say-so.
- Approve — anything irreversible needs a human's deliberate hold, never a tap. Agents can never approve (Law 3).
Concretely: koji claims fix flaky auth test, runs
pytest tests/auth -q, and attaches the passing result — the card closes
on that green check, not on koji's word. rotate the shared deploy key is
risk: irreversible, so closing it also needs sailesh's hold-to-approve
on his phone; no amount of agent confidence substitutes for that.
It's all just git underneath: append-only, per-principal JSONL event logs,
committed to the repo; board state is a pure fold over those logs. No
server holds the truth — if every daemon on earth dies, cat and
git pull still work. That's the actual point, not a nice-to-have: it's
what lets two or more principals who share no trust domain — your
agent fleet and mine, different vendors, no shared server — work one board
without either side being the referee. (See "Why not Symphony," below,
for the single-owner tools this isn't.)
Chalk (apps/chalk) is the first client: a live room where you and
your friends bring your own agents — Codex, Claude Code, anything that
speaks MCP — deal cards, watch the crew claim them, and approve the
irreversible from a phone with a hold, not a mash.
An issue is prose closed on faith — nothing stops two agents grabbing the same ticket, and "done" is whatever the agent says. Blackboard keeps git as the transport and adds physics: claims that structurally exclude, a done only a passing check can write, and a history you can replay.
A chat can carry the conversation about work — it can't hold a lease, verify a done, or referee two fleets. A chat believes whoever spoke last. (Any chat app can render a Blackboard room, though — views are projections; the protocol is the product.)
openai/symphony proved the category:
managing agent work beats supervising agents. But Symphony is one
principal's tool — one board owner, one trust domain, its own CI as the
judge. Blackboard is the layer for the case Symphony doesn't claim: two
or more principals who don't share a trust domain — your fleet and my
fleet, different vendors, no shared server — where claims must exclude
structurally, "done" must be a check either side can re-derive
(bb rederive), and the referee is a fold over git, not anyone's CI.
(A Symphony-style board could render a Blackboard room tomorrow — views
are projections. That's the roadmap, not the rivalry.)
Needs only Bun. Install once from a Blackboard checkout with
bun install && bun link --cwd packages/cli. Then, from any git repo, one
command gives you a seeded board, a running serve, and a copy-paste connection
card (init → join → optional GitHub-issue import → serve → the Claude/Codex
agent-mount block for this repo):
cd your-repo
bb up # --room NAME · --as NAME · --no-import · --pair · --port NIt's idempotent — re-running while the board is live reuses the existing serve
and re-prints the connection card, and a teammate running it on your board is
onboarded with their own principal. Full walkthrough:
docs/QUICKSTART.md.
Or watch the protocol reject an unlawful write, no server, in ~10 seconds:
git clone https://github.com/ss251/blackboard.git
cd blackboard
bun install
./scripts/demo-board.sh /tmp/bounce && ./scripts/demo-race.sh /tmp/bouncedemo-board.sh does a real git init and walks one card through a full
lawful close (claim → evidence → green verdict → a human decision citing
the verdict's own sha256). demo-race.sh then fires two real, concurrent
bb claim processes at the board's still-open card — one wins, and when
the loser tries to post anyway, the fold, not a server, not a lock,
structurally rejects the write. Verified output:
[CLAIMED] fix flaky auth test
card_01KY1FVSY6F4SMW9WZD5TCZ13V
lease: koji until 2026-07-21T04:55:15.824Z
evidence: 0 post(s), 0 artifact(s)
REJECTED post evt_01KY1FW4J5FNBMR5DTQWK03ZCE from rex: unheld card: lease belongs to koji
That's the whole protocol in two scripts (./scripts/demo-approve.sh /tmp/bounce continues the same room into the interrupt →
hold-to-approve beat — full filmed sequence:
docs/DEMO-RUNBOOK.md). The full gate, if you
want it:
bunx tsc --noEmit # strict, zero errors
bun test # 604 pass · 0 fail · 5809 expect() calls · 46 files · ~85sThree ways to see real board state without building anything, in order of how much you want running:
npx blackboard demo— the fastest live room. Scaffolds a throwaway git sandbox (20 events, 14 commits, all 7 card lifecycles, the crown-jewel irreversible-payout decision), exports a replay bundle, and serves it atws://localhost:4207/board— connect Chalk or any client, Ctrl+C to stop. Needs Bun onPATHandbun installrun once in this repo; add--no-serveto just scaffold + export without a server,--dir PATHto pick the sandbox location,--port Nto pick the port.bb replay --export json— no server: folds this repo's real history (or--range/--board/--roomany other) into the same portal-ready bundlebb demowrites, for offline inspection or a future hosted scrubber../scripts/demo-board.sh /tmp/bounce && ./scripts/demo-race.sh /tmp/bounce— THE BOUNCE in ~10 seconds flat, no server, no client: the claim race + fold rejection from "60 seconds, no build" above, printed straight to your terminal.
One command builds a fully-populated demo room (three principals, a lawfully-closed card, an open race target, an undecided interrupt):
./scripts/demo-board.sh /tmp/demo-roomscripts/demo-race.sh and scripts/demo-approve.sh then drive the race
and the phone-approval moments against it —
docs/DEMO-RUNBOOK.md is the full filmed
sequence. Or build a board by hand and watch the fold from a terminal —
no Swift toolchain required:
bun run bb init
bun run bb join sailesh
CARD=$(bun run bb open \
--title "fix flaky auth test" \
--goal "auth suite passes deterministically" \
--context "test_login flakes ~30% since #412; suspect fixture race" \
--scope "src/auth/**" \
--acceptance "pytest tests/auth -q passes 5/5 consecutive runs" \
--verify-cmd "pytest tests/auth -q" \
--verify-runs 5 \
--handoff "PR against main; note any fixture API changes")
bun run bb claim "$CARD"
bun run bb board # terminal render of the fold — no build required- Intake (
bb import github <owner/repo>) uses GPT-5.6 to draft the six card fields from issue prose — always human-confirmed before a card is opened (never auto-opened). Offline, it falls back to a template draft.
...or watch the same state in Chalk. Start the read-only projection:
bun run packages/serve/src/bin.ts --repo .
# blackboard-serve: ws://localhost:4207/board · board=board_local room=Chalk repo=.then, in another terminal, build Chalk (Swift 6; needs Xcode 26 + an iOS 26.5 simulator, so macOS only):
cd apps/chalk && swift test # 152 pass · 25 suites
xcodegen generate --spec apps/chalk/project.yml # writes Chalk.xcodeproj (gitignored)
xcodebuild -project apps/chalk/Chalk.xcodeproj -scheme ChalkApp \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' buildTo run Chalk on a physical iPhone with personal-team signing, follow
docs/INSTALL-IPHONE.md.
In a connected local room, the corner gear opens connection settings. A long-press on the crew pill is the power-user shortcut; VoiceOver exposes the same Connection settings action. Hosted cloud rooms use the board picker instead, so neither local-only shortcut appears there.
Codex or Claude Code can mount the same board as a sixth kind of writer —
packages/mcp/README.md has copy-paste config.toml / .mcp.json
blocks and owns the current tool list, including the scoped read projections.
The door deliberately exposes no decision tool — Law 3.
Binding — a PR that violates one is rejected, no discussion.
- The network never writes. Servers/sockets never append events. Writes go through local, principal-owned paths (CLI / MCP on the writer's machine).
- Done is a check, not a claim. Cards close only via a green verdict artifact matching the card's named verification command.
- No decision surface for agents. Approve/deny is human-only
(
bb decide, Chalk).packages/mcpnever grows a decision tool. - Peer content is data, never prompt. Other principals' patches and transcripts are artifacts to verify, never text injected into a prompt.
- Append-only. Nobody hand-edits
.blackboard/logs or rewrites their history.
Full protocol: SPEC.md.
flowchart LR
subgraph W["writers — local, principal-owned (Law 1)"]
CLI["bb CLI<br/>init · join · open · claim · post · artifact · decide"]
MCP["blackboard-mcp<br/>no decision tool (Law 3)"]
end
LOG[(".blackboard/<principal>.jsonl<br/>append-only, git-committed")]
FOLD["packages/core<br/>fold(events) → BoardState<br/>pure · deterministic · replayable"]
SERVE["packages/serve<br/>project() → snapshot / delta<br/>ws://localhost:4207/board — reads only"]
CHALK["apps/chalk<br/>ChalkData → ChalkKit → Room / ApprovalCard"]
CLI -->|git commit| LOG
MCP -->|git commit| LOG
LOG -->|git-ordered read| FOLD
FOLD --> SERVE
SERVE -->|snapshot / delta| CHALK
CHALK -.->|"hold-to-approve → POST /decide<br/>(serve shells bb decide locally)"| CLI
fold is pure: no I/O, no daemon, no database. Same logs in, same
BoardState out, on every machine, independently — bb replay [commit]
re-derives it at any point in git history. serve only ever reads the
fold; the one place it looks like it writes — POST /decide, wired to
Chalk's hold-to-approve — actually resolves the principal server-side
(never trusting a client-supplied identity) and shells the real
bb decide, which does the actual append on the machine serve runs on.
The network still only ever carries an intent — Law 1 holds.
Full guided tour — the 30-second map, the life of a card with real event ids, and every law's enforcement site: docs/ARCHITECTURE.md.
The full art-directed set — shippable assets vs. north-star concepts, and
what each one is for — is in design/PLATES.md; the
binding design contract is
design/CHALK-DESIGN-SPEC.md. The
social-preview card is
design/plates/og-card-v.png (see
docs/SUBMISSION.md for the one manual step that
image still needs). Demo video shot list:
docs/VIDEO-SCRIPT.md.
Day 1 (handoffs/DAY1-CORE.md's work order — target: one Codex session,
≤3h wall clock): packages/core — the zod schemas, the pure fold, git
I/O, replay — and the original 8-verb bb CLI, plus
scripts/race-demo.sh, before anything else in this repo existed.
The human-only decision producer — bb decide, the CLI half of the crown
jewel — is Pragadeesh's Codex, merged
as PR #37: 2,507 lines, a
9-task TDD sequence with a quality pass per task plus an independent
whole-branch review, closing both #28 and #12. The agent door
(packages/mcp, no decision tool by construction) was specified
in issue #7 for exactly
this reason — so the same kind of tool that helped build this repo is
also a documented way to use it (packages/mcp/README.md's Codex
config.toml block).
The rest of the loop — Chalk, serve, the protocol fixes — ran across
parallel Codex and Claude Code sessions, two principals (sailesh,
Pragadeesh122) on disjoint leases, coordinated the way
docs/CONTRIBUTING-AGENTS.md describes:
claim the issue, touch only its file list, open the PR with the gate
output pasted in, merge on green. This repo was built by the pattern it
implements. Full receipts in docs/SUBMISSION.md.
Build Week 2026. Protocol + CLI + MCP door + serve projection + Chalk
(Room, ApprovalCard/HoldToApprove, replay) are shipped and green on
main, plus bb sync --watch / claim --watch heartbeats (#38),
bb verify / bb rederive (#40), GitHub-issue intake (#41), and the full
cloud spine (#52–#57, closed): GitHub App auth, hosted boards, the
per-principal hosted writer, Chalk's GitHub sign-in + board picker, and
the browser client (apps/chalk-web, served at /app) — live today on
Railway. Gaps that are real, not hidden: Chalk is iOS-only, no macOS
build yet (the design spec is macOS-primary — apps/chalk-web is the
second-screen stand-in until then). Tour:
docs/ARCHITECTURE.md.
Sailesh (@ss251) and Pragadeesh (@Pragadeesh122) — cousins, coordinating through the board they built.
MIT (see SPEC.md). Full writeup:
docs/SUBMISSION.md.


