MonoFlow

Every change has a blast radius.

Your agent sees the file it edited. MonoFlow sees every package that change reaches, tests them, and pushes only green.

Blast radius of one edit core/core.go
PackageWhy it is inResult
coreeditedpass
apiimports corefail api_test.go:7
workerimports corepass
webimports apipass
docsunrelatedskipped
cliunrelatedskipped

4 of 6 packages tested, 2 skippedapi caught before push

Agents rarely fail at the code. They fail at the repo around it.

In a plain monorepo, an agentWith MonoFlow, it
greps the tree and reads pages of matchesasks find and graph query: occurrences grouped code / tests / docs, every importer
tests one package and misses the dependent it brokeruns verify: its packages' failures in seconds, every dependent checked behind them
reads pages of colored logs to find one failing linegets each break as file:line, with the line shown
pushes, then learns CI is redships a commit that passed CI locally, on that exact commit
polls gh run watch, paying tokens to waitends its turn; the result is delivered to it

Three commands. Edit to PR.

  1. Start monoflow start fix-rounding

    Its own worktree and branch, off the latest main. No two agents share a checkout.

  2. Verify monoflow verify

    Your packages' failures in seconds; every dependent's tests in the background behind them. Re-runs only the test files your edits touched. Local CI checks everything before the push.

  3. Ship monoflow ship -m "fix: rounding"

    Returns at once. CI runs locally on the commit; only green is pushed and opened as a PR.

real output, demo repo
$ monoflow start fix-rounding
status: created
path: /repo/.agents/worktrees/fix-rounding
branch: agent/fix-rounding
next: cd /repo/.agents/worktrees/fix-rounding and work only there; edit, then monoflow verify

Output is short plain text, because an agent reads it rather than parsing it. Add --json to any command for scripts.

What your agents get

CI for this change, in one command

The changed packages answer first, in seconds; their dependents are checked in the background and the next verify includes them. Cached per check and per test file, so a re-verify runs only what your edits touched. Failures main already has don't block.

scope: 27 of 29 packages: the changed ones and all their
dependents (what CI runs for this change); the other 2
cannot be affected
not affected: internal/tools, schema

From an agent's run on opentelemetry-go

Nothing red reaches the remote

ship runs CI on the exact commit, in a clean checkout, and pushes only on green. Pushes to main are refused.

A map, not a grep

find "<text>" groups occurrences into code, tests and docs, without changelogs or generated files: 5 KB where grep prints 23 KB. graph query <dir> lists every package importing it.

No polling

Hooks for Claude Code and opencode put CI results in the agent's context, and wake it when pending work finishes.

One worktree per agent

Sparse worktrees with their own dependencies, checkpoints outside Git history, and AST-level merges of Go files between agents.

Guardrails

Commits are refused when assertions disappear or secrets leak. Risk scores and proof receipts tell reviewers where to look.

Live code or a pinned release.

Every in-repo dependency is one of two kinds of edge, and each consumer picks. monoflow version edges lists them all.

Source

The consumer points at the package's directory: workspace:, Go replace or go.work, a Cargo or uv path. The default.

A change to the package reaches the consumer at once. verify re-tests it, and an API break is fixed in the same change.

Pinned

The consumer points at a release snapshot under .agents/releases/<pkg>@<version>/.

Later changes do not reach it, and verify does not re-test it for them. It upgrades when it chooses, and its own checks run on the upgrade.

  1. version suggest moneyThe bump the exported-API diff requires: removed or changed symbols mean major.
  2. version bump money minorWrites the version and reports each consumer's impact. It rewrites none of them.
  3. version release moneyTags money@1.3.0 and tells each pinning consumer whether it is compatible or breaking for it.
  4. version pin ledger moneyCommits the snapshot, so any clone, IDE or CI builds it without MonoFlow.
  5. version upgrade ledger moneyMoves the pin to the newest compatible release and runs ledger's checks.

Releasing a version twice, or a package with uncommitted changes, is refused. Snapshots nobody pins are removed, and version check flags one that no longer matches its tag. With many agents, each records the bump its change needs (task changeset) and version apply applies the highest per package.

How it works

MonoFlow doesn't replace your compilers, test runners or CI. It decides what to run, where to run it, and how the agent hears about the result. Everything it knows is a plain file under .agents/.

  1. Graph

    Packages and their edges are read from the manifests already in the repo: go.mod, pnpm/npm/yarn workspaces, Cargo, pyproject.toml. A changed file belongs to the package with the longest matching path. The affected set is those packages plus every transitive dependent. For Go, go list runs in every module to find real importers.

    .agents/monoflow.config.json

  2. Worktree

    start fetches main and runs git worktree add -b agent/<id> from origin/main (--from-local for your local main). Caches and events are symlinked to the root. Dependencies are installed offline (pnpm) or linked. --packages narrows it to a sparse cone.

    .agents/worktrees/<id>

  3. Verify

    Changed packages run first, in parallel, while the dependents' upstream builds run; the agent's verify answers with their failures at once, or PENDING when they pass, and the dependents' tests run in the background (their typecheck and lint in local CI, before the push). Results are keyed by a SHA-256 of files, command and upstream keys, per check and per vitest test file, so unchanged inputs aren't re-run, and a reformatted file keeps its keys. A failure is re-checked against main: if main fails the same way, it's reported as pre-existing and doesn't block. Output is stripped to file:line with source context.

    .agents/cache/verify/

  4. Background

    A hook fires after each agent tool call. When edits have been quiet for 1.5s, verify starts in the background, keyed by HEAD plus the working-tree diff. Runs are never cancelled by newer edits: what they checked stays cached. When the agent asks about the same files, the result is often already there.

    .agents/verify/<key>

  5. Ship

    ship commits after the test-integrity and secret checks, then returns. A detached process checks the commit out cleanly and runs local CI. It pushes and opens the PR only on green, and only if the commit merges cleanly into main. On red or on a conflict, it records why and pushes nothing. After the push, a watcher reports a conflict if main later moves into one.

    .agents/outer_loop/ · refs/monoflow/proofs/<sha>

  6. Events

    A detached watcher polls gh so the agent doesn't have to. Results land as JSON files. Claude Code and opencode hooks add them to the agent's context, and the Stop hook waits on pending work and wakes the agent when something needs action.

    .agents/events/

Remote CI imports the proofs pushed by ship. Packages already verified locally on identical inputs count as cache hits, so CI runs only the rest.

Measured on real monorepos.

The same agent (Claude Haiku) does the same task with MonoFlow and with plain git plus the repo's own tooling, from a fresh copy, one run at a time. Every number is in e2e/bench.

TanStack Querypnpm and Nx, ~100 projects; a default changed across every framework package
Plain git + Nx
472s
MonoFlow
407s

14% sooner, with 24% fewer tool calls (39 against 52) and 8% fewer tokens (109k against 118k). Latest build, 1 run; plain git + Nx, mean of 2.

opentelemetry-go29 Go modules; a break only another module's tests catch
Plain git
130s
MonoFlow
101s

Median time. MonoFlow runs stayed between 90 and 112 s; plain runs spread from 109 to 338 s. Tokens about equal (60k against 55k).

Every change in both arms passed the repo's tests and type checks on the pushed branch; only MonoFlow's pushes were checked by CI first. Not ahead yet: trpc (Turborepo, 11 packages), where plain agents running one vitest command finished first (205 s against 318 s).

Install

  1. Build

    Needs Go 1.22 or later. The result is one static binary.

    git clone https://github.com/M0xoo/monoflow.git
    cd monoflow && make build
  2. Install into your repo

    Puts the binary at .agents/bin/monoflow, links it as ./bin/monoflow, and runs monoflow init.

    cd /path/to/your-repo
    /path/to/monoflow/install.sh
  3. Affected-only GitHub Actions

    Optional. Remote CI runs only affected packages and skips work a local proof already covers.

    ./bin/monoflow init --setup-gh-actions

Commands

CommandWhat it does
Agent path
start <task-id> [--packages=…]Worktree and branch for one task, off the latest main
find "<text>" [--regex]Every occurrence, grouped code / tests / docs / examples; changelogs and generated files left out
verify [--package=<p>|--all|-u|--strict]The changed packages' failures first, then every dependent (PENDING while they run); -u rewrites snapshots
ship -m "…" [--title=…]Commit, run local CI on that commit, push and open the PR when green
Setup & understanding
init [--setup-gh-actions]Discover packages, write config, the agent guide and hooks; optionally add CI workflows
clone <url> [dest] --packages=…Partial clone of the listed packages and their dependencies
graphFull dependency graph
graph query <pkg|dir>Dependencies, dependents and (Go) every package importing it
graph impact <file>Packages affected by changing a file
subset set|add|remove|update|deps|list|check|resetNarrow the working copy to the packages a task needs
Inner loop
checkpoint save "msg" | revert [id] | listSnapshot and restore the working tree without commits
cache status|clean|serveContent-addressed package cache, with an optional HTTP server
quarantine list|addKeep known-flaky tests out of agent feedback
Isolation
worktree create|list|focus|add|remove|update|isolatePer-agent sparse worktrees and the packages they contain
worktree sync --to=B --from=AAST-level merge between agents' worktrees
worktree finish <id> | gcRemove a finished worktree, or clean up abandoned ones
Guardrails
task checkMake sure only allowed paths were changed
task prove <id>Run definition-of-done checks and write a proof receipt
verify --proof=<id>Verify a proof receipt
task sliceSplit a diff into stacked review tiers
guard checkTest integrity guard and secret scan (also run on every commit)
review risk|audit|throttleRisk score, PR-description audit, and limits on PR volume
Git & shipping
commit -m "…"Guarded commit; starts local CI on it
branch [name|switch], syncBranch and pull while keeping the sparse checkout intact
pushPush once local CI is green; direct pushes to main are refused
events unread|list|wait|ack|clearInbox of CI results and other events, delivered to agents by hooks
pr create|list|view|test|local|review|mergePRs with a risk tier, proof receipt and affected packages
pr status [id]Local proof receipt and the GitHub PR in one view
queue spec, ci checkSpeculative local merge, and a receipt that lets CI take its fast path
ci health [--check=…] [--logs]When each check started failing, the breaking commits and flaky checks
outer status|waitLocal CI jobs on commits
Build & release
deliver [--target=…]Build a self-contained deliverable into .agents/dist
app testAssemble the full app from changed packages plus cached builds
version edges|suggest|bump|releaseSource and pinned edges; the bump the API diff requires; tag a release and notify its consumers
version pin|unpin|upgrades|upgradeConsume a release, go back to live code, see and apply newer releases
version list|apply|checkVersions and ranges; apply changesets; check ranges and snapshots
scenario run [--json]End-to-end multi-agent simulation on a temporary repo

monoflow help <command> shows one command's flags; monoflow help --all lists everything.