Help us shape the future of Go! Take the 2026 Go Developer Survey

buildermesh

package module
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 1 Imported by: 0

README

Latest release Go version NATS JetStream, built in Linux, macOS and Windows MIT license

Many boxes  ·  Proof before main  ·  Drive the work  ·  Quickstart  ·  Commands  ·  Manual


buildermesh (bmesh) is one Go binary that runs Claude Code sessions on your own machines (boxes) and turns ideas into changes on main. You decide what gets built.

Agents write half-page specs; you accept or reject them in plain words. A lead session builds an accepted spec in its own git worktree, with executor sessions on branches under it. Routines run the repo's checks and record each result against a commit's exact files, and bmesh land refuses to land a tree those checks haven't passed.

One mesh, many boxes

Three boxes, one workflow

One box, same workflow

  • One hub. bmesh hub runs NATS, a message server, inside the binary on an always-on box, over a tailnet such as Tailscale. Boxes share one token; nothing else is installed. A single box needs no hub: bmesh serve embeds one.
  • bmesh on every box. Only bmesh talks to the hub: bmesh serve, the daemon, always on, which wakes quiet leads, lands what its leads finished, runs routines, sends phone notifications and draws the map; and the bmesh commands sessions run. A box set to update itself installs each bmesh release; on others, each session opens with a line to run bmesh update.
  • Signed jobs. Boxes joined with bmesh init --hosts share a signed roster, and every job between them is signed and checked. The signing and approval keys sit where no agent can read them (on macOS, behind the Keychain password dialog). A secure box holds an update of its plugins, or of the repo bmesh updates itself from, until you approve it there.

Each picture below plays one of the demo's steps on its own, on a loop.  Grey lights a command,  teal a message.

Every box builds. Each box runs its own leads and executors, each in its own git worktree, all at once.

The leads on build-box, hub-box and the laptop start one after another and keep running

A coordinator keeps leads in step. The session that started the builds starts a lead on each box in turn (over ssh on the other boxes), then follows their events through the hub. On your word it holds a lead (bmesh hold; with --gate now it stops at its next inbox read, as task-1 does here) until another has landed, then releases it (bmesh unhold), and the held lead's bmesh serve wakes it.

A check goes to the box that can run it. A check that runs apart from the lead, like the review, is queued once for every box that runs it, so a free box takes it. One that needs macOS, like task-1's xcode here, waits on the hub until a Mac's bmesh serve takes it; the result comes back to the lead.

Push notifications. When a lead asks, is blocked or is done, its event goes through the hub to bmesh serve with an ntfy topic set (here, hub-box's), which pushes it to your phone, once.

Three leads' events, done, asks and blocked, go through the hub to hub-box's bmesh serve, which pushes each to the phone

Every box levels. A lead's land moves its own box's checkout. bmesh sync levels this box's checkout with origin and sends a level job to every other box that has the repo (the box that landed is already level, so the picture leaves its job out); each fast-forwards its own unless it's dirty or diverged, and a secure box holds a plugin change for your approval. A laptop that wakes takes the jobs it missed, in order; a job waits on the hub for a day.

All of this works on one box too. With no other boxes, bmesh serve embeds the hub, and the same commands drive the same flow. [runner] max_leads caps how many leads run at once (bmesh build refuses past it), so here the coordinator starts a third lead once one of the first two has landed, and holds it until the lead it depends on has landed too.

Proof before main

bmesh land runs verify first, queues the Opus review the spec's size calls for when the branch has none and waits for it, then takes the land lease, runs verify on the rebased tree and carries the review across the rebase when the diff is unchanged (a changed diff gets a new review, which land queues and waits for), then writes the results into the spec as one line:

verified: review ok t:3f2a9c1e m:claude-opus-5-5 c:81d0b7a4; verify ok t:3f2a9c1e

t: is the landed tree, c: the tree the review ran on before the rebase. bmesh done refuses until the spec on origin/main reads done with a merged commit and a recorded result for every entry.

How you drive the work

  1. Propose. bmesh ideate app starts an engineer and a UI/UX engineer; each lands three to five ranked specs on main, and with an ntfy topic set the list reaches your phone.
  2. Choose. Reply in plain words: bmesh choose-ideas app "1 and 3 yes, 2 smaller, 4 no, 5 later". The verdicts land on main; a word that isn't a verdict, like smaller, marks the spec adjusting, with your words kept for the rework.
  3. Build. bmesh build app 012 checks that main passes its verify commands, marks the spec building and starts a lead.
  4. Verify. Routines run verify (the repo's tests) and, for larger changes, review (Opus, in a session that never saw the change).
  5. Land. bmesh land verifies, queues the review the spec's size calls for and waits for it, then rebases onto origin/main (resolving the conflicts a rule can), requires a result for the tree it lands, writes the verified line, marks the spec done and fast-forwards main. It waits out what clears by itself (a review running, main moving, someone's unpushed main, another lead landing) and stops only for what needs the lead. A lead usually hands this to its box's daemon instead: it runs bmesh ready --text "<done line>" and ends its turn, its state reads landing, and the daemon runs land and done for it. The daemon wakes the lead only when land refuses (a conflict, a failing check or review), and once at the end for its closing line. A hold waits for the release without waking it, and a message withdraws the request and wakes it.

A lead reports problems outside its spec as feedback. When a batch started from a chat is done, the plugin's build skill starts builds for the quick fixes without asking you, writes the rest up as proposed specs for your review and tags the next version with bmesh release. In a chat, say "proposals for app", "1 and 3 yes", "build what's accepted in app" or "what's building".

Quickstart (single box)

You need Go 1.26 or later, git, Claude Code (claude on your PATH, signed in) and a repo with a main branch.

go install github.com/rickdiculousli/buildermesh/cmd/bmesh@latest
claude plugin marketplace add rickdiculousli/buildermesh
claude plugin install buildermesh@buildermesh
cd ~/repos/your-repo
bmesh init      # press Enter at every prompt
bmesh serve     # keep it running

Add the repo's verify commands to the row bmesh init wrote in repos.toml (it prints the path):

[repos.your-repo]
# the keys init wrote stay as they are
verify = ["go vet ./...", "go test ./..."]
bmesh scaffold your-repo    # commit and push what it writes
bmesh ideate your-repo      # your first proposal round

scaffold writes AGENTS.md's shared sections and the core engineering rules; in a chat, "scaffold your-repo" asks which rule sets the repo gets first.

Quickstart (mesh)

One always-on box runs the hub; the other boxes reach it over a tailnet such as Tailscale. Install bmesh and the plugin on every box as above, then:

  1. On the hub box:

    bmesh init --role hub --service    # Enter at every prompt; enable the two services it prints
    
  2. Copy the hub's token file (nats.token in its config dir) into each other box's config dir.

  3. On each other box:

    bmesh init --hub nats://<hub tailnet ip>:4222 --token-file <config dir>/nats.token --service    # enable the service it prints
    # a laptop that sleeps: bmesh init --role leaf --service, with nats://<hub tailnet ip>:7422 at the prompt; enable the two services it prints
    
  4. Optional, so boxes can act on each other: join them to a roster. On the box you type at, run bmesh init --hosts --admin. For each other box, run bmesh hosts invite <its host name> on the admin box, then bmesh init --hosts on that box and paste the code. Restart bmesh serve on each box after its init --hosts.

Monitor the mesh

bmesh serve draws the mesh live at http://127.0.0.1:3774/: leads with their executors, rounds with their engineers, each check with its queue, and at the top what needs you: a session that is blocked, asking or silent. A lead its daemon is landing, or one held at a gate, is drawn calm and never in that band, and a quiet working lead is the only kind called silent.

All commands and flags

Honestly, you can mostly skip this. With the plugin installed, just ask your agent to run the mesh for you: "proposals for app", "build what's accepted in app", "what's building", "sync my repos". The skills know these commands so you don't have to. This list is here for when you're curious or something needs a closer look.

--config FILE, --node PATH and --json go before or after any command (Synopsis). Each command links to its section of the manual.

Setup and updates

Command Flags
bmesh init [--service] [--force] [--role hub|leaf|client] [--hub URL] [--token-file F] [--runner K] [--no-repo]
bmesh init --hosts [--admin]
bmesh serve [--listen ADDR] [--restart [--detach]]
bmesh hub
bmesh config check
bmesh t3 login [--server URL] [--callback-port 52299] [--timeout D] · --status
bmesh version
bmesh help
bmesh update [--repo R] [--latest] [--answer KEY=VALUE]... [--defaults] [--apply [--yes]]
bmesh update --plan [--json]
bmesh update --walk --from REV [--to REV] [--dir D] [--before-swap]
bmesh update --notice

Proposals and builds

Command Flags
bmesh ideate <repo> [--idea] [--model M] [--effort E] [--context <text|->] [--force]
bmesh choose-ideas <repo> "<reply>"
bmesh spec list [--repo R] [--status S] [--json]
bmesh spec next-id [--repo R] [--count N] [--peek]
bmesh spec set --id NNN --status S [--repo R] [--from A,B] [--field K=V]...
bmesh spec template [--repo R]
bmesh spec steps [<NNN>] [--repo R] [--tick N,N [--note W] | --check [--wait D]]
bmesh spec verified [--spec NNN] [--commit C] [--write]
bmesh build <repo> <NNN> [--model M] [--effort E] [--path P] [--family F] [--hold land|now] [--force]
bmesh build --accepted <repo> [--model M] [--effort E] [--family F] [--hold land|now] [--force]
bmesh lead start --repo R --spec NNN --brief FILE [--model M] [--effort E] [--title T] [--path P]
bmesh hold <path>|--family F [--gate land|now] --text T
bmesh unhold <path>|--family F [--text T]
bmesh held
bmesh feedback "<text>" [--repo R] [--where <file|command>] [--fixed SHA]
bmesh feedback list <family>|--repo R [--json]
bmesh feedback drain <family> [--through SEQ]
bmesh feedback drain --seq SEQ[,SEQ...]
bmesh fix [--model M] [--effort E]
bmesh release <repo> [--minor|--patch] [--dry-run]
bmesh scaffold <repo> [--check]

Nodes

Command Flags
bmesh whoami
bmesh started|progress|blocked|ask [--text T]
bmesh done --commit SHA [--text T]
bmesh send <path> <text|->
bmesh inbox [--peek] [--json]
bmesh thread-status [<path>] [--json]
bmesh mesh-status [--all] [--json]
bmesh watch --nodes a,b [--since T] [--timeout D]
bmesh executor new --name N [--base REF] [--model M] [--json]
bmesh executor list [--json]
bmesh executor remove <name> [--force]
bmesh node forget <path> [--worktree | --wipe [--dry-run]] [--wait 30s]
bmesh node stop <path> [--text T] [--wait 30s]

Checks and landing

Command Flags
bmesh routine list [--json]
bmesh routine status [<kind>] [--json]
bmesh routine queue <kind> [--commit C] [--model M] [--effort E] [--go] [--rerun] [--again] [--text T] [-- args...]
bmesh routine go <kind>
bmesh routine wait <job id> [--timeout D]
bmesh routine run <kind> [--commit HEAD] [--model M] [--effort E] [--here] [--rerun] [--text T] [-- args...]
bmesh result find <kind> [--commit C] [--model M] [--json] [-- args...]
bmesh result record <kind> --ok|--fail [--commit C] [--model M [--base B]] [--output FILE|-] [-- args...]
bmesh lock-lease acquire <slot> [--ttl D]
bmesh lock-lease renew <key> [--ttl D]
bmesh lock-lease release <key>
bmesh lock-lease run <slot> [--ttl D] -- <cmd...>
bmesh setup [--check] [--force]
bmesh land [--spec NNN] [--size XS|S|M|L] [--ttl D] [--wait D]
bmesh ready --text "<done line>" [--size XS|S|M|L] | --cancel

Boxes

Command Flags
bmesh hosts [--json]
bmesh hosts --here
bmesh hosts --notes [--json]
bmesh hosts invite <name> [--secure] [--accept a,b] [--replace]
bmesh hosts set <name> [--secure=true|false] [--accept a,b]
bmesh hosts revoke <name>
bmesh hosts ping [<host>] [--wait D]
bmesh hosts replies [--json]
bmesh hosts held [--json]
bmesh hosts approve <id>
bmesh hosts deny <id>
bmesh sync [--repo R | --all] [--wait 30s]
bmesh prune [--repo R | --all] [--apply] [--wait 30s]
bmesh repos [--json]
bmesh repos show <name> [--json]
bmesh repos publish
bmesh repos propose [--wait 30s]
bmesh repos remove <name> [--checkouts [--ignored-ok]] [--apply] [--wait 1m]

Add-ons (off unless config.toml's [addons] enable lists them; bmesh serve runs them)

Add-on Config
t3-done: pushes when a T3 thread you started here is done, failed or asking [addons] enable = ["t3-done"] · [addons.t3-done] ntfy = URL [every = 5s] [min_busy = 5m]

What's inside

  • bmesh: the CLI, daemon, map server and NATS hub in one binary.
  • Runners: t3 (a T3 Code thread), claude (headless claude -p turns) or tmux runs each session.
  • The plugin: twelve Claude Code skills, seven for your chat and five that the sessions bmesh starts follow.
  • Specs: docs/proposals/NNN-slug.md in each repo, with status, builder, merged commit and verified line in the frontmatter.

The manual covers every command, flag, config key, output and refusal, and the concepts behind them. Internals covers NATS subjects, buckets, record shapes and signing.

Build and test

go build ./cmd/bmesh
TMPDIR=$HOME/.cache/bmesh-scratch/tmp go test -count=1 ./...

Tests refuse /tmp, so TMPDIR points elsewhere. The seeded mesh (scripts/seed.sh), the map's browser check (node scripts/check.mjs) and the UI-change check are in the manual's Testing section.

License

MIT.


Documentation

Overview

Package buildermesh embeds the files of this repo that the bmesh binary hands to other repos: the paragraph a repo's AGENTS.md gets, the README of a proposals directory and the example routine kinds. They stay in the repo for reading and are embedded from there, so the two never differ.

Index

Constants

This section is empty.

Variables

View Source
var AgentsSnippet string

AgentsSnippet is docs/agents-snippet.md: the buildermesh paragraph for a repo's AGENTS.md, with its bracketed verify placeholder.

View Source
var Examples embed.FS

Examples holds examples/routines/<kind>.toml, the kinds a repo is seeded with.

View Source
var ProposalsReadme string

ProposalsReadme is docs/proposals/README.md: what a proposals directory holds and the lifecycle of a spec.

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis
Package addons holds the optional parts compiled into bmesh, one package each under addons/.
Package addons holds the optional parts compiled into bmesh, one package each under addons/.
t3done
Package t3done is the t3-done add-on: it posts to ntfy when a T3 thread a person started on this box is done, failed or asking, read from T3's own database since T3's API doesn't say whether a turn left a background task pending.
Package t3done is the t3-done add-on: it posts to ntfy when a T3 thread a person started on this box is done, failed or asking, read from T3's own database since T3's API doesn't say whether a turn left a background task pending.
cmd
bmesh command
Command bmesh is the buildermesh CLI: nodes report events, message each other and watch the mesh through it.
Command bmesh is the buildermesh CLI: nodes report events, message each other and watch the mesh through it.
internal
brief
Package brief renders the briefs the mesh hands its threads: the engineer and UI/UX engineer briefs of a proposal round, the build brief of a lead and the half-pager a spec is written as.
Package brief renders the briefs the mesh hands its threads: the engineer and UI/UX engineer briefs of a proposal round, the build brief of a lead and the half-pager a spec is written as.
checkout
Package checkout makes and moves the clean detached worktrees that routine instances run in.
Package checkout makes and moves the clean detached worktrees that routine instances run in.
config
Package config reads bmesh's config.toml: schema, discovery and defaults.
Package config reads bmesh's config.toml: schema, discovery and defaults.
daemon
Package daemon holds the bmesh daemon's loops: it keeps each lead's runner sample current, wakes idle leads with unread mail, marks quiet nodes silent, and runs the routine kinds of the repos in [repos].
Package daemon holds the bmesh daemon's loops: it keeps each lead's runner sample current, wakes idle leads with unread mail, marks quiet nodes silent, and runs the routine kinds of the repos in [repos].
hosts
Package hosts is the mesh's membership and proof: the admin's signed roster, signed jobs and replies between boxes, presence, the one-time invite and the keys behind them.
Package hosts is the mesh's membership and proof: the admin's signed roster, signed jobs and replies between boxes, presence, the one-time invite and the keys behind them.
instance
Package instance runs routine jobs: a shell command, or a claude -p session that persists across jobs.
Package instance runs routine jobs: a shell command, or a claude -p session that persists across jobs.
kind
Package kind reads routine kind files: the TOML a repo keeps under .bmesh/routines/ to say how a routine runs.
Package kind reads routine kind files: the TOML a repo keeps under .bmesh/routines/ to say how a routine runs.
level
Package level brings one checkout level with its origin (020): it fetches, judges the checkout against origin/<branch> (or a pinned tag), fast-forwards a box that is behind, pushes one that is ahead when the push gate allows, and refreshes the plugins sessions load from it.
Package level brings one checkout level with its origin (020): it fetches, judges the checkout against origin/<branch> (or a pinned tag), fast-forwards a box that is behind, pushes one that is ahead when the push gate allows, and refreshes the plugins sessions load from it.
merge
Package merge resolves the conflict regions of a three-way text merge by rule, and leaves a region no rule resolves a conflict.
Package merge resolves the conflict regions of a three-way text merge by rule, and leaves a region no rule resolves a conflict.
mesh
Package mesh holds the NATS names, paths and clients of the bmesh mesh.
Package mesh holds the NATS names, paths and clients of the bmesh mesh.
meshtest
Package meshtest starts an embedded NATS server for tests.
Package meshtest starts an embedded NATS server for tests.
nix
Package nix removes a repo's checkout from a box (023): the admin's signed nix job names a repo and the origin its registry row has, the box takes the path from its own registry row and refuses, touching nothing, anything whose loss couldn't be undone from a remote.
Package nix removes a repo's checkout from a box (023): the admin's signed nix job names a repo and the origin its registry row has, the box takes the path from its own registry row and refuses, touching nothing, anything whose loss couldn't be undone from a remote.
notify
Package notify posts a notification: to an ntfy URL over HTTP, or through a command (the test hook config.toml's notify command names).
Package notify posts a notification: to an ntfy URL over HTTP, or through a command (the test hook config.toml's notify command names).
proc
Package proc puts a command in its own process group and kills the group, so no descendant outlives a run: Setpgid and kill(-pid) on Unix, a new process group and taskkill /T on Windows.
Package proc puts a command in its own process group and kills the group, so no descendant outlives a run: Setpgid and kill(-pid) on Unix, a new process group and taskkill /T on Windows.
prune
Package prune removes the worktrees and branches a finished node leaves behind on this box: one node's (`node forget --worktree`, the forget-worktree op) or a whole checkout's (the prune op).
Package prune removes the worktrees and branches a finished node leaves behind on this box: one node's (`node forget --worktree`, the forget-worktree op) or a whole checkout's (the prune op).
runner
Package runner starts and observes the agent sessions behind nodes: a T3 Code thread (T3), claude in a tmux window (Tmux) or headless claude -p turns (Claude).
Package runner starts and observes the agent sessions behind nodes: a T3 Code thread (T3), claude in a tmux window (Tmux) or headless claude -p turns (Claude).
runner/t3mcp
Package t3mcp speaks to T3 Code's MCP endpoint (/mcp: streamable HTTP, JSON-RPC, the mcp-session-id header) and holds the sign-in that endpoint needs: the OAuth login that mints a token and the file it is kept in.
Package t3mcp speaks to T3 Code's MCP endpoint (/mcp: streamable HTTP, JSON-RPC, the mcp-session-id header) and holds the sign-in that endpoint needs: the OAuth login that mints a token and the file it is kept in.
runner/t3mcp/t3fake
Package t3fake is an httptest T3 Code server for tests: the environment document, the MCP OAuth endpoints and the MCP tools the t3 runner calls, answering with the shapes recorded from a T3 0.1.0 server (kept in testdata/t3v2).
Package t3fake is an httptest T3 Code server for tests: the environment document, the MCP OAuth endpoints and the MCP tools the t3 runner calls, answering with the shapes recorded from a T3 0.1.0 server (kept in testdata/t3v2).
scaffold
Package scaffold gives a ship repo what a buildermesh build expects of it: the "Definition of done", "Proposals" and "Buildermesh" sections of its AGENTS.md between markers, and its "Engineering rules" block (rules.go), docs/proposals/README.md and assets/.gitkeep, the routine kinds its registry row names, seeded from the embedded examples, and the row's setup as .bmesh/config.toml's [setup].
Package scaffold gives a ship repo what a buildermesh build expects of it: the "Definition of done", "Proposals" and "Buildermesh" sections of its AGENTS.md between markers, and its "Engineering rules" block (rules.go), docs/proposals/README.md and assets/.gitkeep, the routine kinds its registry row names, seeded from the embedded examples, and the row's setup as .bmesh/config.toml's [setup].
selfupdate
Package selfupdate builds bmesh from a checkout of its repo's main and swaps the result in for a binary, keeping the one it replaces beside it.
Package selfupdate builds bmesh from a checkout of its repo's main and swaps the result in for a binary, keeping the one it replaces beside it.
setup
Package setup runs a repo's setup command (its .bmesh/config.toml setup) in a worktree: what installs the dependencies the repo gitignores, which a fresh `git worktree add` lacks.
Package setup runs a repo's setup command (its .bmesh/config.toml setup) in a worktree: what installs the dependencies the repo gitignores, which a fresh `git worktree add` lacks.
shell
Package shell runs a command line through the platform's shell: sh on Unix, cmd on Windows.
Package shell runs a command line through the platform's shell: sh on Unix, cmd on Windows.
spec
Package spec finds a spec on the project's main branch and reads its frontmatter, with the same rules as tools/proposals.py.
Package spec finds a spec on the project's main branch and reads its frontmatter, with the same rules as tools/proposals.py.
upgrade
Package upgrade reads a release's upgrade notes, docs/upgrading/<tag>.toml in bmesh's own repo: the steps an install from the release before needs, distilled by the coordinator from the Upgrading sections of the specs the release carries.
Package upgrade reads a release's upgrade notes, docs/upgrading/<tag>.toml in bmesh's own repo: the steps an install from the release before needs, distilled by the coordinator from the Upgrading sections of the specs the release carries.
web
Package web serves the map: a page, the JSON it reads (the node tree, a lead's page, the routines and the strip) and the mesh's events as server-sent events.
Package web serves the map: a page, the JSON it reads (the node tree, a lead's page, the routines and the strip) and the mesh's events as server-sent events.
scripts
seed command
Command seed fills a scratch mesh (scripts/seed.sh runs it against a throwaway bmesh serve) with every state the map draws: two live leads and their executors (working, asking, done and one idle), a held lead, a landing lead (ready, its box's daemon landing it), a done lead for the shelf, a silent lead, an orphan, a round and its two engineers (one blocked), a pooled routine kind with instances in two repos and three spent fresh runs, a queued job, a few events and messages, two leases, a result and a daemon digest, and writes the quota fixture the seeded config's quota command reads (-quota).
Command seed fills a scratch mesh (scripts/seed.sh runs it against a throwaway bmesh serve) with every state the map draws: two live leads and their executors (working, asking, done and one idle), a held lead, a landing lead (ready, its box's daemon landing it), a done lead for the shelf, a silent lead, an orphan, a round and its two engineers (one blocked), a pooled routine kind with instances in two repos and three spent fresh runs, a queued job, a few events and messages, two leases, a result and a daemon digest, and writes the quota fixture the seeded config's quota command reads (-quota).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL