Skip to content

Commit 0b428bc

Browse files
Rewrite README as a tighter technical pitch
Co-authored-by: Andrew <andrewxhill@gmail.com>
1 parent c4b0e38 commit 0b428bc

1 file changed

Lines changed: 124 additions & 127 deletions

File tree

README.md

Lines changed: 124 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,96 @@
11
# mainline
22

3-
Five agents share one machine.
3+
Git is very good at branching.
44

5-
They all have worktrees. They all fork from the same local `main`. They all run tests, rebase, fix conflicts, and eventually want to land. And because this is real life, `main` is also supposed to stay clean enough to push upstream all day.
5+
Git is not a coordinator.
66

7-
Git gives you the primitives for that world. It does not give you a coordinator.
7+
That distinction does not matter when one person is working in one checkout.
8+
It matters a lot when one machine is running five agents, ten worktrees, a
9+
factory daemon, and a protected local `main` that still needs to stay clean and
10+
pushable all day.
811

9-
That coordinator is `mainline`.
12+
That is what `mainline` is for.
1013

11-
`mainline` is a local control plane for parallel worktree development. It keeps topic work in topic worktrees, serializes integrations onto protected `main`, coalesces publishes so only the newest protected tip matters, and stores queue state durably so the machine can still explain itself after a crash.
14+
`mainline` is a repo-local control plane for parallel worktree development. It
15+
keeps feature work in feature worktrees, serializes integrations onto protected
16+
`main`, coalesces publishes so only the newest protected tip matters, and keeps
17+
the whole queue durable so the machine can still explain itself after a crash.
1218

13-
This is not a new VCS. It is the missing piece between "Git has branches" and "ten humans and agents are all landing work on the same box."
19+
This is not a new VCS. It is the missing piece between “Git has branches” and
20+
“many humans and agents are all trying to land code on the same box.”
1421

1522
---
1623

17-
## The Bug Is Not In Git
24+
## The Bug Is Local Coordination
1825

19-
The failure mode here is subtle.
26+
The default workflow looks reasonable:
2027

21-
Nothing is obviously broken. Everyone is doing normal Git things:
28+
1. branch off `main`
29+
2. work in parallel
30+
3. rebase occasionally
31+
4. merge or push when done
2232

23-
- branch off `main`
24-
- work in parallel
25-
- rebase occasionally
26-
- merge or push when done
33+
That works for one person.
2734

28-
That workflow is fine for one person.
35+
It degrades fast under local parallelism:
2936

30-
It gets sloppy fast under local parallelism:
37+
- one worktree rebases while another keeps building on stale `main`
38+
- conflicts get discovered on the protected branch instead of in the source worktree
39+
- two publish attempts race even though only one protected tip actually matters
40+
- an agent gets interrupted and nobody knows what was queued, blocked, retried, or cancelled
3141

32-
- one worktree rebases while another is still building on stale `main`
33-
- conflicts are discovered late, on the protected branch, in the wrong checkout
34-
- two publish attempts race even though only the newest protected tip matters
35-
- an agent gets interrupted and nobody can tell what was queued, what was blocked, or what is safe to retry
42+
The failure mode is not dramatic. It is just expensive. `main` stops being
43+
boring. Queue state lives in scrollback. Humans and agents start doing careful
44+
Git surgery by hand.
3645

37-
The result is not some dramatic distributed systems incident. It is worse: low-grade local chaos. `main` stops being boring. People start treating the protected branch as a scratchpad. Queue state lives in scrollback and memory.
46+
`mainline` makes that coordination problem explicit and mechanical.
3847

39-
`mainline` takes that coordination problem and makes it explicit.
40-
41-
## The Core Idea
48+
## The Model
4249

4350
There is one rule that matters:
4451

4552
**`main` is not where feature work happens.**
4653

47-
Once you enforce that, the rest becomes mechanical:
54+
Once that line is real, the rest follows:
4855

49-
- feature work happens in feature worktrees
56+
- feature work happens in topic worktrees
5057
- submissions are recorded durably
51-
- integrations happen one at a time
52-
- conflicts are resolved back in the source worktree
58+
- integration is serialized
59+
- conflicts are resolved in the source worktree
5360
- publish is a separate queue
54-
- operators can see the queue, retry items, cancel items, and watch the system progress
61+
- operators can see, retry, cancel, and stream what is happening
5562

56-
That is the whole shape of the tool.
63+
Git still owns refs, rebases, fast-forwards, pushes, and worktrees. SQLite owns
64+
queue state, locks, and event history. `mainline` coordinates the two.
5765

58-
Git stays in charge of refs, rebases, fast-forwards, pushes, and worktrees. SQLite stores the queue, locks, and event history. `mainline` coordinates the two.
66+
## The Daily Shape
5967

60-
## What Using It Feels Like
68+
The short CLI is `mq`, because that is what it is: the main queue for one
69+
machine.
6170

62-
You do your normal work in a feature worktree. You commit there. Then you hand that branch to the local queue.
71+
The agent path should feel like this:
6372

6473
```bash
65-
mq repo init --repo .
66-
git add mainline.toml && git commit -m "Initialize mainline repo policy"
67-
./scripts/install-hooks.sh
6874
cd /path/to/topic-worktree
6975
mq submit --check-only --json
7076
mq submit --wait --timeout 15m --json
71-
mq watch --repo /path/to/main
7277
```
7378

74-
The short name is `mq` because that is what it is: the main queue for one machine.
79+
The controller path should feel like this:
80+
81+
```bash
82+
mq land --json --timeout 30m
83+
```
84+
85+
The daemon path should feel like this:
86+
87+
```bash
88+
mainlined --repo /path/to/protected-main --interval 2s --json
89+
mq events --repo /path/to/protected-main --follow --json --lifecycle
90+
```
91+
92+
That is the product: one machine, one protected branch, many worktrees, one
93+
queue.
7594

7695
### Land a branch
7796

@@ -85,42 +104,47 @@ The short name is `mq` because that is what it is: the main queue for one machin
85104

86105
![Watch mainline operator state](docs/demos/gifs/watch.gif)
87106

88-
The demos are generated from source-controlled VHS tapes in [`docs/demos/tapes/`](docs/demos/tapes/) and can be re-rendered with:
107+
The demos are generated from source-controlled VHS tapes in
108+
[`docs/demos/tapes/`](docs/demos/tapes/) and can be re-rendered with:
89109

90110
```bash
91111
./docs/demos/scripts/render.sh
92112
```
93113

94-
## Why The Model Holds Up
114+
## Why It Holds Up
95115

96-
The model is intentionally conservative.
116+
Because the model is conservative in the right places.
97117

98118
- Git is still the source of truth
99-
- `go-git` handles ordinary repository inspection and config work
100-
- native `git` is used where exact write-path behavior matters
101-
- SQLite gives durable local state instead of shell folklore
102-
- the protected branch stays clean because it is treated as infrastructure, not a workspace
119+
- the protected branch is treated as infrastructure, not as a workspace
120+
- the queue survives crashes and restarts
121+
- conflicts get pushed back to the worktree that owns them
122+
- publish is coalesced instead of “whatever finished pushing last”
103123

104-
That matters if your machine is running Codex, Claude, factory daemons, humans, or all of them at once. `mainline` does not ask those actors to become perfectly disciplined. It gives them a coordinator that turns the safe path into the normal path.
124+
This is exactly what you want when the machine is running Codex, Claude,
125+
factory daemons, humans, or all of them at once. `mainline` does not ask those
126+
actors to become perfectly disciplined. It turns the safe path into the normal
127+
path.
105128

106129
## What Ships
107130

108131
`mainline` is already a real toolchain:
109132

110-
- repo discovery for standard repos and bare-clone-plus-worktree layouts
133+
- standard repo and bare-clone-plus-worktree discovery
111134
- durable SQLite queue state in shared Git storage
112135
- serialized integration and publish workers
113-
- `land` for one-command submit-plus-integrate-plus-publish
114-
- `submit`, `run-once`, `publish`, `retry`, and `cancel`
115-
- `status`, `watch`, `logs`, `events`, and `confidence`
136+
- branch submission from topic worktrees and detached SHAs
137+
- `submit --check-only`, `submit --wait`, and one-shot `land`
138+
- `status`, `watch`, `logs`, `events`, `doctor`, and `confidence`
116139
- daemon mode through `mainlined`
140+
- retry and cancel as real operator controls
117141
- policy checks, hook coordination, and repo-managed hooks
118-
- release archives, Homebrew assets, Nix packaging, and machine-readable release manifests
119-
- versioned GitHub release package assets for package-manager automation
142+
- Homebrew, Nix, GitHub release archives, checksums, and release manifests
120143

121-
This repo also dogfoods the workflow. The committed worktree instructions live in [SKILL.md](/Users/devrel/Projects/recallnet/mainline/.agents/skills/worktree/SKILL.md), and the repo-specific guardrails live in [AGENTS.md](/Users/devrel/Projects/recallnet/mainline/AGENTS.md).
122-
123-
It also ships evidence, not just claims: stress runs, soak runs, certification runs against real repo layouts, and a `mq confidence` gate that tells you whether the current build has enough proof behind it.
144+
This repo dogfoods that workflow. The repo-local worktree instructions live in
145+
[.agents/skills/worktree/SKILL.md](/Users/devrel/Projects/recallnet/mainline/.agents/skills/worktree/SKILL.md),
146+
and the repo-specific guardrails live in
147+
[AGENTS.md](/Users/devrel/Projects/recallnet/mainline/AGENTS.md).
124148

125149
## Install
126150

@@ -132,7 +156,6 @@ From source:
132156
git clone git@github.com:recallnet/mainline.git
133157
cd mainline
134158
make build
135-
make install-hooks
136159
```
137160

138161
With `go install`:
@@ -143,112 +166,86 @@ go install github.com/recallnet/mainline/cmd/mq@latest
143166
go install github.com/recallnet/mainline/cmd/mainlined@latest
144167
```
145168

146-
Homebrew and Nix details are in [install.md](/Users/devrel/Projects/recallnet/mainline/docs/install.md).
147-
148-
## The Operator Surface
169+
Homebrew and Nix details are in
170+
[install.md](/Users/devrel/Projects/recallnet/mainline/docs/install.md).
149171

150-
Setup and health:
172+
Recommended first-time repo setup after install:
151173

152174
```bash
153-
mq repo init --repo /path/to/main --main-worktree /path/to/main
154-
mq config edit --repo /path/to/main
155-
mq doctor --repo /path/to/main
156-
mq repo show --repo /path/to/main --json
175+
cd /path/to/protected-main
176+
mq repo init --repo . --main-worktree .
177+
git add mainline.toml
178+
git commit -m "Initialize mainline repo policy"
179+
./scripts/install-hooks.sh
157180
```
158181

159-
Queue work:
182+
That init commit matters. It turns the repo’s queue policy into versioned,
183+
reviewable state instead of one more local convention that agents have to infer.
160184

161-
```bash
162-
cd /path/to/topic-worktree
163-
mq submit --check --json
164-
mq submit --check-only --json
165-
mq submit --json
166-
mq submit --wait --timeout 10m
167-
mq land --json --timeout 30m
168-
mq run-once --repo /path/to/main
169-
mq publish --repo /path/to/main
170-
```
185+
## The Core Commands
171186

172-
For factory or daemon callers, the intended handoff is:
187+
Setup:
173188

174-
- `mq submit --check --json` to fast-fail deterministic problems before queue mutation
175-
- `mq submit --check-only --json` for the stricter dry-run path: clean worktree, current protected tip included, and no duplicate active submission for the same branch SHA
176-
- `[integration].MaxQueueDepth` to stop dead queues from silently accumulating unbounded queued branches
177-
- `mq submit --json` to record the branch and get a stable `submission_id`
178-
- `mq submit --wait --timeout 10m` when an agent needs a blocking landed-or-blocked answer without implementing its own poll loop
179-
- `mq events --follow --json --lifecycle` to subscribe once instead of polling
180-
- `mainlined` or `mq land` to carry the branch the rest of the way to integrated and published state
189+
```bash
190+
mq repo init --repo /path/to/protected-main --main-worktree /path/to/protected-main
191+
mq config edit --repo /path/to/protected-main
192+
mq doctor --repo /path/to/protected-main --fix --json
193+
```
181194

182-
The best default for coding agents in this repo is:
195+
Submit and land:
183196

184197
```bash
185198
cd /path/to/topic-worktree
186199
mq submit --check-only --json
187200
mq submit --wait --timeout 15m --json
201+
mq land --json --timeout 30m
188202
```
189203

190-
The best default for controller agents and factory daemons is:
204+
Operate and observe:
191205

192206
```bash
193-
mq land --json --timeout 30m
207+
mq status --repo /path/to/protected-main --json
208+
mq watch --repo /path/to/protected-main
209+
mq events --repo /path/to/protected-main --follow --json --lifecycle
210+
mq retry --repo /path/to/protected-main --submission 17
211+
mq cancel --repo /path/to/protected-main --publish 4
194212
```
195213

196-
If `origin/main` advances before your branch reaches the front of the queue, that is normal queue work, not a manual repair job. `mainline` syncs protected `main` from upstream before integration when policy allows it, records that as a durable event, and only blocks if the branch now has a real rebase conflict.
214+
## Repository Layouts
197215

198-
`mq submit --wait` is integration-scoped, not publish-scoped. It exits `0` when the branch is integrated, `1` for blocked/failed/cancelled outcomes, and `2` on timeout.
216+
`mainline` supports both:
199217

200-
If a repo sets `[integration].MaxQueueDepth`, `mq submit` will reject new queued work once that many submissions are already waiting. Use `mq submit --check-only --json` when an agent wants a cheap preflight without consuming queue capacity.
218+
- normal repos with `.git/` inside the checked-out worktree
219+
- bare-clone storage with linked worktrees, such as
220+
`~/Projects/.bare/owner/repo.git` with `~/Projects/owner/repo`
201221

202-
`[checks].CommandTimeout` is per-repo and now defaults to `5m`, which is a better fit for real pre-integrate gates. `mainline` still enforces a hard ceiling of `15m`, and a hung pre-integrate check blocks the submission with `blocked_reason = "check_timeout"` instead of letting one branch freeze the queue forever.
222+
For bare-clone layouts, queue state and locks live with shared Git storage so
223+
every worktree sees the same queue truth.
203224

204-
Observe and control:
225+
## Architecture
205226

206-
```bash
207-
mq status --repo /path/to/main --json
208-
mq watch --repo /path/to/main
209-
mq logs --repo /path/to/main --follow
210-
mq retry --repo /path/to/main --submission 17
211-
mq cancel --repo /path/to/main --publish 4
212-
mq confidence --repo /path/to/main --json
213-
mq version
214-
```
227+
The design is intentionally small.
215228

216-
Daemon mode:
229+
- `go-git` handles ordinary inspection and config work
230+
- native `git` is used where exact write-path behavior matters
231+
- Git answers topology and branch semantics
232+
- SQLite answers ordering, durability, coordination, and audit history
217233

218-
```bash
219-
mainlined --repo /path/to/main --interval 2s --json
220-
```
234+
More detail is in
235+
[ARCHITECTURE.md](/Users/devrel/Projects/recallnet/mainline/docs/ARCHITECTURE.md).
221236

222-
## Where It Fits
237+
## When To Use It
223238

224239
Use `mainline` when:
225240

226241
- many worktrees share one machine
227-
- `main` needs to stay pushable all day
228-
- humans and coding agents are landing in parallel
242+
- `main` must stay clean and pushable
243+
- humans and agents are landing in parallel
244+
- you want deterministic branch landing instead of ad hoc Git rituals
229245
- you want queue state to survive crashes and restarts
230-
- you want a local operator view instead of reading raw `.git` state
231-
232-
Do not use it if your workflow is one person, one branch, one push, no parallelism. Plain Git is already excellent there.
233-
234-
## Repository Layouts
235-
236-
`mainline` supports both:
237-
238-
- normal repos with `.git/` inside the checked-out worktree
239-
- bare-clone storage with linked worktrees, such as `~/Projects/.bare/owner/repo.git` with `~/Projects/owner/repo`
240-
241-
For bare-clone layouts, state and locks live with shared Git storage so every worktree sees the same queue truth.
242-
243-
## Architecture
244-
245-
The system is intentionally small.
246-
247-
- Git answers branch and worktree semantics
248-
- SQLite answers ordering, durability, recovery, and audit history
249-
- `mainline` coordinates the landing path between them
250246

251-
More detail is in [ARCHITECTURE.md](/Users/devrel/Projects/recallnet/mainline/docs/ARCHITECTURE.md).
247+
Do not use it if your workflow is one person, one branch, one push, and no
248+
parallelism. Plain Git is already excellent there.
252249

253250
## Development
254251

0 commit comments

Comments
 (0)