Why markdown plans break when you run multiple AI agents A developer built workstream-mcp, an MIT-licensed MCP server that replaces markdown checklist files with a Postgres-backed task store for coordinating multiple AI coding agents. Claims use SELECT ... FOR UPDATE SKIP LOCKED inside a single transaction so concurrent agents each receive a different task without blocking, and claims carry a TTL that returns abandoned tasks to the pool. The server also enforces dependency-aware scheduling, a JSON-defined state machine that returns illegal_transition with allowed_next, reviewer approval flows, an append-only event log guarded by database triggers, and an outbox for GitHub and Slack notifications. Most of us coordinate AI coding agents the same way: a markdown file in the repo with a checklist. The agent reads it, picks an item, does the work, ticks the box and commits. For one agent in one session, it works well. Then you start a second session. And a third. Two agents pick the same item. Nothing in a markdown file stops two readers from choosing the same line at the same moment. You find out when both open a PR for the same fix. An agent dies mid-task. The session times out, the laptop sleeps, the context runs out. The item stays marked "in progress" and no other agent will touch it. Edits race each other. Two agents update the plan at once, and one change is lost or turns into a merge conflict an agent then "resolves" badly. History lives in git diffs. Who claimed what, when, and why it was rejected is buried in commit history, if it was recorded at all. Nobody can see it. You can't tell at a glance which agent is on what, or what has been stuck for an hour. None of these are agent problems. They're the classic problems of shared mutable state, and a file is the wrong place for it. I built workstream-mcp https://github.com/ahmad-codex/workstream-mcp to cover that list. It's an MCP server, so agents use it through ordinary tool calls. Claims run in one Postgres transaction with SELECT ... FOR UPDATE SKIP LOCKED . Concurrent claimers skip rows that are already locked instead of waiting on them, so 32 agents claiming at once each get a different task, and none of them block. A test suite runs exactly that. Each claim has a TTL. If an agent stops refreshing it, the task returns to the pool. A plan can also cap how long a claim lives and refuse refreshes when the holder hasn't recorded any progress, so an agent stuck in a loop that keeps its heartbeat going still lets go. Dependencies decide what can run in parallel. When the orchestrator breaks work into tasks, it declares which ones depend on which, typically because they touch the same module. claim next task skips a task until its dependencies are done, so two agents never work the same code at once, and tasks that don't overlap run side by side. The state machine is data. Each plan type stores its statuses, transitions, allowed roles and retry caps in a JSON graph. If an agent tries an illegal move, the tool returns illegal transition along with allowed next . In practice the model reads that and corrects itself. Reviews are part of the flow. A developer agent submits an attempt; a reviewer approves it or sends it back to the pool for another pass. After the retry cap, the task goes to a human. Every change writes an event. Database triggers block updates and deletes on the event log, so the history can't be rewritten after the fact. GitHub and Slack go through an outbox. The change and its notification commit in the same transaction, and a background worker delivers it with retries. A GitHub outage never fails an agent's tool call. The dashboard shows each plan as a board: who holds each task, how long their claim has left, what is waiting on what, and what is stuck. It reads through the same tools the agents call. docker run --rm -p 127.0.0.1:8080:8080 -e WORKSTREAM ADMIN TOKEN=local-admin \ ghcr.io/ahmad-codex/workstream-mcp:demo Open http://localhost:8080/demo-insecure-token/ui/ http://localhost:8080/demo-insecure-token/ui/ . From a clone of the repo, WORKSTREAM ADMIN TOKEN=local-admin node scripts/simulate-agents.mjs runs two developer agents and a reviewer against a ten-task plan, so you can watch claims move across the board. To connect your own agent: claude mcp add --transport http workstream-demo http://localhost:8080/demo-insecure-token/mcp , then ask it to claim the next task. For a real setup, clone the repo, open Claude Code in it and paste the setup prompt from the README. It's MIT licensed. The roadmap https://github.com/ahmad-codex/workstream-mcp/blob/master/ROADMAP.md is public, with good first issues for webhooks, connectors and SDKs. Several of these details came straight from the first Reddit thread: the outbox transaction and the refresh limits both shipped within a day of someone pointing at them. I'd especially like to hear how you coordinate multiple agents today, and what breaks.