{"slug": "why-markdown-plans-break-when-you-run-multiple-ai-agents", "title": "Why markdown plans break when you run multiple AI agents", "summary": "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.", "body_md": "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.\n\nThen you start a second session. And a third.\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n**Nobody can see it.** You can't tell at a glance which agent is on what, or what has been stuck for an hour.\n\nNone of these are agent problems. They're the classic problems of shared mutable state, and a file is the wrong place for it.\n\nI 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.\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n```\ndocker run --rm -p 127.0.0.1:8080:8080 -e WORKSTREAM_ADMIN_TOKEN=local-admin \\\n  ghcr.io/ahmad-codex/workstream-mcp:demo\n```\n\nOpen [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.\n\nTo 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.\n\nFor a real setup, clone the repo, open Claude Code in it and paste the setup prompt from the README.\n\nIt'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.", "url": "https://wpnews.pro/news/why-markdown-plans-break-when-you-run-multiple-ai-agents", "canonical_source": "https://dev.to/ahmad_shawar_de8d35d546cf/why-markdown-plans-break-when-you-run-multiple-ai-agents-h11", "published_at": "2026-10-27 21:55:01+00:00", "updated_at": "2026-10-27 22:12:21.406827+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "agent-protocols", "mlops"], "entities": ["workstream-mcp", "GitHub", "Slack", "Postgres", "Claude Code", "ahmad-codex"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/why-markdown-plans-break-when-you-run-multiple-ai-agents", "markdown": "https://wpnews.pro/news/why-markdown-plans-break-when-you-run-multiple-ai-agents.md", "text": "https://wpnews.pro/news/why-markdown-plans-break-when-you-run-multiple-ai-agents.txt", "jsonld": "https://wpnews.pro/news/why-markdown-plans-break-when-you-run-multiple-ai-agents.jsonld"}}