Walkthrough
From a Markdown spec to a merged plan: decompose, compile, validate, run, read the report.
This guide takes one spec through the whole agb lifecycle. The example target is a small CLI called do-better; substitute your own repository and spec file. Every behavior described here is linked to the v1.0.0 source.
The example spec
The spec asks for a CLI foundation and one scan command. A sensible decomposition is two tickets:
T1(foundation): the command dispatcherbin/dobetter.mjsandlib/dirs.mjs, which creates the.dobetter/directories. Everything else builds on it, so it lands first.T2(scan):lib/scan.mjs, which inventories the target codebase and writes.dobetter/comprehension/codemap.md. It depends onT1.
Compile the plan from the spec
The primary path is to let agb compile the plan. agb plan takes an Antigravity brain id or a local spec.md, plus the target repo, and writes plan.json (plan usage: C27):
agb plan spec.md /abs/path/to/do-better
# options: --out plan.json --force --no-coldstart --no-parallax --no-premortemThe compiler runs the plan-boundary gates (see Plan boundary). If any finding is blocking, nothing is written and the command exits 2; the fix surface is the spec, not the JSON (blocking path). Premortem risks are printed as advisory. An existing plan.json is only overwritten with --force (overwrite guard).
Read (or hand-write) plan.json
The compiled plan for the example looks like this. You can also write it by hand; see Writing plans.
{
"repo": "/abs/path/to/do-better",
"base": "main",
"gate": { "build": "npm run build-check", "test": "npm test" },
"tickets": [
{
"id": "T1",
"title": "CLI entry point and directory layout foundation",
"body": "Create bin/dobetter.mjs dispatching scan, charter, audit. Create lib/dirs.mjs that idempotently creates .dobetter/comprehension/, .dobetter/findings/, .dobetter/backlog/. Add test/dirs.test.mjs covering directory creation.",
"scope": ["bin/dobetter.mjs", "lib/dirs.mjs", "test/dirs.test.mjs"],
"rails": ["package.json"],
"edges": [{ "to": "T2" }],
"tier": "mid",
"pool_hint": "auto"
},
{
"id": "T2",
"title": "Implement the scan command",
"body": "Implement lib/scan.mjs: inspect file sizes and types, ask the model for a summary, write .dobetter/comprehension/codemap.md using lib/dirs.mjs. Add test/scan.test.mjs.",
"scope": ["lib/scan.mjs", "test/scan.test.mjs"],
"rails": ["bin/dobetter.mjs", "lib/dirs.mjs", "package.json"],
"tier": "cheap",
"pool_hint": "gemini"
}
]
}- Disjoint scopes.
T2listsT1's files as rails (read-only), not scope, so it cannot rewrite the foundation. - Edges point forward.
T1's edge{ "to": "T2" }makesT1a predecessor ofT2(edge semantics). - Gate commands must match
npm testornpm run <script>(gate regex).
Validate and preflight
agb validate plan.json # schema, ids, edges, tier/pool routability; exit 2 if invalid
agb preflight plan.json # scope overlap + per-ticket coldstart; refreshes quota telemetryvalidate runs no models (validate). preflight refreshes quota telemetry and runs coldstart unless you pass --no-coldstart, which skips both (preflight).
Run it
agb run plan.jsonBefore touching anything, run requires the repo to be on the plan's base branch and to have a clean tree (base check, clean-tree guard). It then takes the run lock and writes .booster/run.json for status and the sidecar dashboard.
What happens to T1
- Worktree.
agbcreates.worktrees/agb-t1on branchagb/t1: both names use the lowercased ticket id (worktree and branch names: C5). - Build. A builder model is picked from the tier's candidate list (for
mid:gemini-3.8-flash-high,gemini-4-argon-low,claude-sonnet-5-5-medium), filtered bypool_hintand routed to the least-loaded pool (tier candidates: C27). The builder ends withTICKET-DONEorTICKET-BLOCKED: <reason>(charter). - Gates.
npm run build-checkandnpm testrun inside the worktree, sandboxed (see Gates and sandboxing). - Prosecution. The diff goes to a prosecutor from a different model family: a Gemini builder is prosecuted by
gpt-oss-120b-medium(falling back toclaude-sonnet-5-5-medium) (prosecutor choice). By default one clean pass is required;prosecution.dryPassesraises that (dry passes). - Strikes. A failed gate, a blocking prosecution or a blocked agent consumes a strike. Each ticket gets two; after the second the ticket fails (two-strike rule).
- Merge. Merges are sequential and rebase-first (lifecycle: C27). The ticket branch is rebased onto the current base; a conflict fails the ticket and quarantines the candidate under
refs/quarantine/(rebase). The rebased commit is checked out in a throwaway integration worktree.worktrees/agb-integration-<token8>(integration worktree: C5), the post-merge gates run there, and only then does the base ref advance with a compare-and-swapupdate-ref(CAS advance). Your checkout is never used as the merge workspace.
T2 and beyond
Dispatch is event-driven: T2 becomes ready the moment all its predecessors have merged, with no wave barriers (ready check). It gets .worktrees/agb-t2 on agb/t2, branched from the updated base that already contains T1. With tier: cheap and pool_hint: gemini its builder is one of gemini-3.8-flash-low, gemini-3.8-flash-medium. If T1 had failed, T2 would be failed through as blocked by failed predecessor (blocked).
The report
When the run ends, agb run prints the report as JSON and writes it to .booster/report.json (report shape, report.json):
{
"runId": "run-mfx3k2a1",
"merged": ["T1", "T2"],
"failed": {},
"requests": { "gemini-flash": 4, "gemini-pro": 0, "claude": 0, "gpt-oss": 2 },
"enforcementAvailable": true,
"enforcementReason": null,
"compromised": null
}| Key | Meaning |
|---|---|
runId | run-<base36 timestamp>; also the log directory under .booster/logs/ (runId) |
merged | Ticket ids that reached the base branch |
failed | Map of ticket id to failure reason |
requests | Request counts per pool (gemini-flash, gemini-pro, claude, gpt-oss) (pools) |
enforcementAvailable / enforcementReason | Whether live in-session rail enforcement was active, and why not |
compromised | null, or the reason the run's integrity check failed |
The exit code is 0 when failed is empty and compromised is null, and 2 otherwise; an invalid plan exits 1 (exit: C27). See Exit and error codes.