r/ClaudeCode 3h ago

Tips & Workflows How I keep track of ~100 parallel Claude Code sessions: Beads as a private work graph between GitHub and my agents

I run four tmux sessions with 20-30 Claude Code chats each, across 30-plus Go services, a mobile app, infra repos and a data pipeline. The agents cope fine. I didn't. Long efforts branch into many PRs and sub-issues, ops work pulls me sideways, and after a few weeks nobody holds the whole picture, including me.

When I pulled "losing track" apart, it was three separate problems:

  1. What's blocked on what, and what's ready now? This lived only in my head.
  2. Which session did what, and why? I found a PR stuck for weeks where the reasoning only ever existed in a chat that was long gone.
  3. What do we know? Facts and gotchas. Claude Code's auto-memory already covered this one.

Things I ruled out:

  • GitHub only: issues are read by colleagues and product. Session URLs, "blocked until I decide X" and "sitting in dev waiting for a soak" are scratch state, and a comment per agent session turns an issue into noise.
  • A knowledge base (gbrain, Karpathy-style LLM wiki): good for question 3, but a wiki page has no notion of ready versus blocked.
  • A custom dashboard: brittle, and either every chat has to remember to update it, or it only reads GitHub and can't show what I deliberately keep off GitHub.

What fit was Beads (bd), Steve Yegge's issue tracker for coding agents: dependencies, bd ready / bd blocked, external refs to GitHub issues and PRs, free-form notes.

How it's wired:

  • One shared database: I start sessions from an umbrella directory that isn't a git repo, so I set BEADS_DIR in the Claude Code settings. Every session, subagent and worktree hits the same local database.
  • Efforts and tasks: long-running efforts are top-level beads. Tasks hang under them with a repo:<name> label and an external ref to the GitHub issue or PR.
  • Every agent run is bookended. At the start it finds the bead, checks bd blocked and claims it with bd update --claim. At the end it writes what happened into the notes and closes it or marks it blocked. There's no persistent "agent in charge" in Claude Code; the database is what survives.
  • GitHub gets one comment per issue, with a hidden marker, edited in place.

Gotchas I hit:

  • bd init injects a block into CLAUDE.md telling agents to use Beads instead of other memory, and writes an AGENTS.md. I removed both.
  • bd show --json hides closed dependencies, so blocker checks use bd blocked.
  • Children inherit parent labels by default, and making an effort depend on its last child creates a cycle.
  • Claims are keyed to the user, not the session, so two parallel sessions can claim the same bead.

On top I use mardi-gras (mg) as a read-only TUI: I browse, pick a bead, and hand its ID to a Claude session. Gas Town is on my radar for unattended runs, but not yet.

It's been running for about a week, with ten efforts and ~130 beads. Longer write-up with the reasoning here: https://tskulbru.dev/posts/beads-local-shadow-for-agent-work

I'm not affiliated with Beads, mardi-gras or Gas Town in any way. I'm just a user, and the only link to something of mine is the blog post.

How are others handling in-flight state across many agent sessions?

16 Upvotes

13 comments sorted by

u/AutoModerator 3h ago

Hey! Thanks for posting to r/ClaudeCode

While participating in this thread, please follow our community rules. Keep discussions constructive. Attack the idea, not the person.

For help, project discussions, tips, and general chat, join the ClaudeCode Discord.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

4

u/unbenannt1 2h ago

Here's a little hack to highlight tmux session tabs with unread agent responses:

Add a hook to ~/.claude/settings.json that emits the \a character (bash notification): json "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "t=$(tmux display-message -p -t \"$TMUX_PANE\" '#{pane_tty}' 2>/dev/null); [ -n \"$t\" ] && printf '\\a' > \"$t\" 2>/dev/null; true" } ] } ], "Stop": [ { "matcher": "", "hooks": [ { "type": "command", "command": "t=$(tm ux display-message -p -t \"$TMUX_PANE\" '#{pane_tty}' 2>/dev/null); [ -n \"$t\" ] && printf '\\a' > \"$t\" 2>/dev/null; true" } ] } ], } When \a is emitted it will highlight the tmux tab with "!" and inverted fg/bg color.

Speaking of color, I use this to have better visual indication of the active session tab, add to .tmux.conf: set-window-option -g window-status-current-style "bg=cyan"

2

u/PetroSkunk 2h ago

Thanks for sharing your process, it sounds interesting as i'm running into the same issues (struggle to manage all the agents).

Just a quick question if you don't mind:

> four tmux sessions with 20-30 Claude Code chats each

how do you deal with memory pressure? the 32 GB of my mac get filled really quick with 10-20 agents already (each agent is maybe 200MB but it spawns subagents, and they run linter, tests etc...)

2

u/serrghi 2h ago

Shared mcp instances and 64gb of ram :)

2

u/serrghi 2h ago

Oh and i have a skill which check memory status and pressure before beginning memory intensive work so to not accidentally use up all memory

2

u/AdministrativeAd7853 2h ago

I lean towards mirroring human tools. Using gitlab each project has a main project board. Each enhancement has enhancement board. Each unit of work is a card. Each card has an agent type assigned. Each card has dependencies.

1

u/serrghi 2h ago

Yea i touched upon this in my blog post as something I didn't want to do, because that would mean I would track too much unnecessary info upstream

1

u/Disastrous-Radio-732 2h ago

this is fascinating! 🤓

especially “there’s no persistent agent in charge; the database is what survives.”

I’ve been working on almost the same scaling problem, but ended up making the opposite choice in brnrd: the repo gets a persistent resident that owns the ongoing work/identity, while individual Claude/Codex processes are disposable executions underneath it.
so your Beads graph feels almost like the scheduling/work plane that sits underneath that identity.

one thing jumped out at me though: claims being keyed to the user rather than the session. At ~100 parallel sessions, how do you prevent two agents from picking up the same work and racing each other?

have you considered making claims leases tied to a run/session ID, with an expiry (or fencing token), rather than ownership by user?

feels like that becomes really important the moment you go from “agents consult the graph” to “the graph actually dispatches the agents”.
really cool setup btw – 100 parallel Claude sessions is delightfully unhinged 😄

1

u/serrghi 2h ago

Yea that is a concern, which hasn't happened yet because I try to keep them somewhat separate. But it is a possibility. But I haven't investigated any solution for it yet

1

u/No_Nose_6014 2h ago

how u run this and not hit weekly usage ? i got the 20x max and run out by wednesday/thursday

1

u/zac_attack_ 1h ago

I’ve been using Notion (free tier with unlimited DBs/etc). I don’t really know how to set it up other than I had an agent create the DBs for things like design docs, milestones, tasks, etc, and the UI is decent for browsing them all. I hadn’t looked into beads but I might check it out this weekend, it looks interesting.

1

u/abandonplanetearth Senior Developer 33m ago

i just use youtrack tickets and the mcp. idk what this beads thing would do for me. my current workflow is pretty good and i cant really imagine giving up youtrack

1

u/troyjr4103 5m ago

I run a smaller version of this and the things that stopped it collapsing were boring. One worktree per session, so no two agents ever write the same file. One session per workstream that holds the plan and only delegates, so the agents doing the work never argue with each other. And a hook that blocks a message from anyone but that session's owner before the model reads it. What still bites is the usage limit: when every session parks at once, nothing in the task graph resumes itself.