the bar, declared before the work: scope, standards, governance, assurance — then checked after it
bb auditor [areas|standards [area]|charter <area>|brief <area>|gate <area>|plan|pack|send <area> <kind>|record <area> <kind> <file>|drift] [--json]
What happens between the moment you press enter and the moment the session is measured: sixty verbs, nine hooks, five chained gears, eleven readiness stages, and exactly one arrow that spends.
the whole loop, and where the money is
Everything except one box is a parse, a count, a path check or a set difference. The loop closes on itself: what a merge changes is what the next scan reads.
bb run and
bb bridge are refused inside a gear, checked when the crontab line is written rather than
trusted. Pressing enter yourself enters at you and skips straight to a session.sixty, read off src/cli.js in the order a factory uses them
Each card is one entry in the command table: the name, what it answers, the usage line,
and what it does to the world. Two can spend money and only with the flag named on the card. Eleven can
change a file a person wrote and only with --apply. The rest read, or write under
.bundlebox/.
.bundlebox/ only
writes --apply can change a file you wrote, dry run until the flag
spends can call a paid model, with the flag named
the bar, declared before the work: scope, standards, governance, assurance — then checked after it
bb auditor [areas|standards [area]|charter <area>|brief <area>|gate <area>|plan|pack|send <area> <kind>|record <area> <kind> <file>|drift] [--json]
detect languages, agents and gates; write .bundlebox/config.json
bb init [--force] [--json]
what this box can run, and which runtime serves each op
bb doctor [--json]
what a complete .bundlebox holds, and whether this one does (no tokens)
bb env [--json] | bb env up [--apply]
run the local detectors (no tokens)
bb scan [--only a,b] [--json]
list findings
bb findings [--detector x] [--severity s] [--status open|resolved|fixed|wontfix|all] [--limit n] [--json]
one finding: evidence and the triage derivation
bb explain <id> [--json]
label what closed findings cost, and fit the promotion rule to them (0 model tokens)
bb triage [backfill|calibrate] [--apply] [--json]
measure the tree against its own medians; findings, guidelines, briefs (no tokens)
bb oversight scan [--trees a,b] [--write] [--json] | report | brief <files...> | guidelines [--build] | thresholds
the design studio: doctrine, UI research plans, a declared system and the gate that holds it
bb designlabs [status|init|check|tables|plan|collect|intake|sources|principles] [dir] [--apply] [--json]
turn open findings into packed work units (no tokens)
bb compile [--write] [--max-units N] [--json]
will these files fit one session? parts table and split
bb context <paths...> [--kind fix|verify|investigate|build|write] [--brief file] [--json]
what proves a change here (detected, merged with kernel.gates)
bb gates [--list] [--json]
pack compiled units into lanes and waves (no tokens)
bb route [--write] [--max-parallel N] [--agent name] [--run-id id] [--json]
reference tables a session reads instead of searching (no tokens)
bb snapgen build [--only a,b] [--force] | stale | list | show <table> | index | skeleton <file> | blast [--since ref] | callers <symbol> [--json]
compile the derived symbol tables into one binary index the guards read in microseconds
bb arc [stat] | bb arc build | bb arc lookup <name...> [--under dir] [--shapes exact,prefix,suffix]
one problem, one focused prompt: where, regions, scope, evidence, gate (no tokens)
bb pinpoint "<problem>" [--files a,b] [--max-files N] [--kind fix|verify|investigate|build|write] [--print] [--json]
estimate, ledger, calibrate, profile, prices, budget
bb tokens estimate <paths> | ledger | calibrate [--apply] [--tokens-only] | profile [--probe] | prices | budget [--json]
the input axis: shrink a tool result before it enters the window, and measure it on transcripts that already exist (0 tokens)
bb sieve [status] | bb sieve replay [--limit N] [--json] | bb sieve check <file|-> [--text] | bb sieve spill
compile the agent's memory: check every anchor, sweep what rotted, place what survives
bb janitor [status|compile|heap|explain <id>|prune|warehouse] [--budget N] [--age-factor N] [--stores a,b] [--verbose] [--json]
anti-slop for prose: what a summary says that costs tokens and carries nothing (0 model tokens)
bb slop <file|-> | bb slop fix <file|-> [--apply] | bb slop rules [--json]
the ablation benchmark: what a task costs with the factory and without it (no tokens)
bb bench [run [suite]] | init [--limit N] | suites | show | gate [<task>] | swebench run [--n 12] [--repos a,b] [--json] [--cap 10]
a document or a prompt becomes a world model, a corpus and the briefs that fill it (0 tokens until send)
bb genesis <doc.md|-|--prompt "..."> [--base url] [--name id] | show | plan | pack [--batch 4] | send [surface] [--run --spend] | seed | list
run a scenario corpus against the running system; red steps become findings (0 model tokens)
bb cookbook [list|check|select|calibrate|run|board|init] [--persona id] [--base url] [--rpm n] [--only surface] [--budget steps] [--engine kernel|js] [--plan] [--apply] [--json]
run a situation profile at rising concurrency and report what the system does (0 model tokens)
bb simulate run [profile] --base <url> [--levels 1,8,32] [--seconds 3] | profiles | show | init <id>
the running system: is it up, is it answering, what broke since the last call, what is it costing (0 model tokens)
bb runbook [status | up <group> | down <group> | wait <group> | logs [id] | perf | services | groups | init] [--apply] [--wait[=s]] [--force] [--level E] [--grep <rx>] [--sample] [--all] [--json]
the ports this workspace is served by, as a page: what is listening, for which directory, and whether it answers (0 tokens)
bb viewport [status] [--json] | bb viewport build | bb viewport serve [--port 65432] | bb viewport open
what has already been driven, and whether that answer still holds (0 model tokens)
bb recom [list|check <id>|gate <id> -- <cmd>|repeatable|replay <id>|record --from <file>|refresh <id>|forget <id>|mobile|probes|template] [--apply] [--json]
what the screen showed: a frame for a person, an accessibility summary for the session (0 model tokens)
bb dotty [targets | shot [label] --url <u> | during [label] [--reload] -- <cmd>] [--port 9222] [--json]
one board over every view, one ledger under it; and which pipeline stage does not hold
bb mainboard [board|run|gaps|views|check] [--base url] [--only view,view] [--persona id] [--target local|prod] [--json]
a dataframe over the factory's own data, and the evals that turn a measurement into a finding
bb frames [sources|show|check|eval] [--source x] [--where col:op:value] [--group col] [--agg name:fn:col] [--sort -col] [--cols a,b] [--limit n] [--describe] [--markdown] [--json]
what is failing now, why, and the operation that closes it (0 model tokens)
bb failsafe [status|why|ops|run <op> [--apply]|rerun|check] [--json]
execute the routed plan as agent lanes (dry-run by default)
bb run [--plan <run_id>|latest] [--apply] [--pr] [--agent <name>] [--max-parallel N] [--json]
draft a packed call to an agent; send only with --run (and --spend for a paid agent)
bb bridge draft "<problem>" [--reason exhausted|assist|complete|asked] [--files a,b] | send <id> [--run] [--spend] [--allow-near] [--agent name] | list | report [--json]
what this workspace is spending in the current 5-hour block, and how many tokens the box saved it
bb monitor [status|savings|blocks|sessions|guard] [--days n] [--plan pro|max5|max20|custom] [--limit n] [--compact] [--json] [--no-color] [--watch --interval 10] [--write-state]
what a session used and saved (MEASURED vs ESTIMATE)
bb session [id] [--write] | end --session <id> --transcript <path> | list | rm <id> | backfill [--since <date>] [--transcripts N] [--dry-run] [--json]
the wire: local compression proxy
bb headroom start --apply | status | stop | savings | doctor [--json]
which agent CLIs are installed, and the lean flag stack for each
bb agents [--json]
the acceptance ledger: derive it from the brief, parse it, run it, re-verify returned work
bb finish init [--apply] [--force] derive GATES.md from the active brief and the detected gates
commit, push, draft PR, ready, merge and review, with the workspace rules as code
bb git commit [--lane L01 | --scope a,b] [--apply] | push [--apply] | pr [--lane L01] [--apply] | ready <n> [--apply] | merge <n> [--override-gate] [--apply] | review <n> [--write] | status [--cwd <dir>]
run local actuators over findings that name one (dry run without --apply)
bb fix [--apply] [--detector x] [--id id] [--force] [--json]
run declared gears (pipelines of local verbs) with gates and freshness
bb pipeline run <gear> [--apply] [--verbose] | list | runs [--limit N] | suggest [--json]
index tagged scripts (@tag/@title/@needs/...) and run them with their needs checked
bb scripts scan | list | run <tag> [--apply] | table [--write] [--json]
the unattended worker: the crontab lines that run the free pipeline
bb cron install [--every 30] [--no-gate] [--apply] | status | remove [--apply]
signals, rules, graph, model and memory over transcripts and episodes (python3)
bb buckmaster episodes | signals [--limit N] | rules | recommend | graph | model [--train] [--weights] | memory | recall --about "<text>" [--tier procedural,semantic,episodic] [--useful] | outcomes [--all] | backlog [--json]
the automation engine: what this workspace did by hand more than twice, as scripts, snippets, boilerplate and completions (LATHE-1, no tokens)
bb lathe what the model holds and what it emitted
one page for this workspace: the pipeline, the window, every session and what it saved
bb console [serve] [--port 7788] | build [--file out.html] | state [--json]
install the instructions block, MCP entry and hooks into each agent
bb wire [--agents claude,codex,...|auto] [--apply] [--global] [--json] | bb wire status | bb wire trim [--apply]
remove only bundlebox's blocks, MCP entries and hook rows from each agent
bb unwire [--agents a,b|auto] [--apply] [--global]
of everything bb wired in front of the agents, what did the sessions actually reach for (0 tokens)
bb uptake [--since <iso>] [--quiet] [--json] | bb uptake sessions
a Claude Code hook handler (stdin JSON in, JSON out, always exit 0)
bb hook session-start|prompt|pre-read|post-tool|pre-compact|session-end
serve the zero-token verbs as an MCP server over stdio
bb mcp
the Rust kernel: status, build from source, or install a release binary
bb kernel [status|ops|build|install [--version v]]
the silent-failure checks; each prints its measurement
bb selftest [--json]
is there a newer bundlebox, and install it
bb update [--apply]
print the version
bb version
this list
bb help [verb]
the five agents that watch the work rather than the tree: spin, oscillate, drift, diminishing, converge (0 tokens)
bb echos [run] [--only spin,drift] [--limit N] [--json] | bb echos show | bb echos list
the handoff layer: what the box cannot settle, asked once and stored (0 tokens)
bb grapple [observe|status|ask|ratify|harvest|promote|backfill <rev>|priors] [--json]
the doorway: pack, serve and sieve for an agent that has no hook system (0 model tokens of its own)
bb proxy [--agent codex] [--prompt-file f] [--raw] -- <agent command...>
Aliases the table also answers to: gc and memory for
janitor, blackice and audit for auditor, scenarios and
corpus for cookbook, board and mb for mainboard,
dataframes for frames, cc for console, usage for monitor,
sg and switchgear for pipeline, learn and bm for
buckmaster, bridgeswap for bridge, scripttag and st for scripts,
ctx for context.
Every verb takes --json. A module that fails to load is reported by
bb help as broken with the first line of its error, and the rest of the table still
answers: one bad import must not take the CLI down with it.
nine hooks, in the order they fire
This is what bb wire --apply installs. Every row runs local code against
artefacts already on disk, and the only thing it can spend is the few hundred tokens it chooses to
put in the window.
In a workspace that was never inited, this runs bb init inline — detection
over the tree, well inside the budget — and detaches the artefact build, which is minutes of
scanning and indexing and is not a hook's to spend. The returned line says it is still building
rather than implying the environment is ready.
Then the rot notice: which lines in the memory just loaded no longer resolve. The claim is already in the window, stated with the confidence it earned the day it was true, so the correction arrives beside it.
Fired again with source: compact, right after a compaction, it is the first
observable moment the window can be corrected. The narrative goes back first — the task,
the scope, the files already edited, the last commands, the gate still open — read off the record
the guards wrote, never off the summary. Then the rules, verbatim. Two caps, because truncating
one joined band would cut whichever came second, and neither is the right one to lose.
→ window · ≤600 tok what this box holds, and what no longer exists → after a compaction · ≤1200 + ≤700 tok the record, then the rules
If the round before was a compaction and no SessionStart answered it, the same two bands go back here. It fires once per compaction, not once per prompt: the marker records which compaction has already been answered, and on which hook.
Otherwise, when the prompt is a task, the hook runs bb pinpoint rather than
suggesting it: measured on this tree at 0.47s against a 15s budget, because every input it reads
is a stored artefact. A suggestion was measured too, and it loses to the model's own habit about
three times in four. A brief already standing for the same prompt is reused, never rebuilt.
What enters is the map, about 300 tokens: the files, the symbols, the verdict. The quoted regions stay on disk until a read asks for one.
→ window · ≤1000 tok the located, budgeted brief
If pinpoint already located a region in this file, the read is denied and the reason carries the range to read instead. The model needs a read of a file before it will edit it, so blocking the scope blocks the change — and the denial names the read that is allowed.
Otherwise the file is priced against what the window has left, and an oversized open comes back with an offset and a limit.
✕ denied · ≤900 tok read this range, not the file
A declaration search is the case where the answer is not merely cheaper to look up, it is
already written down: symbols-*.md is a name / file / line index rebuilt by
fingerprint. The denial hands back the matching rows and the search never runs.
What is not denied matters as much. A string literal, a call site, a comment, a pattern no declaration index can answer: the tables have no rows for it and the search goes through untouched. And the same words over a different file are a different search: the duplicate key carries the target, because keyed on the words alone it refused a second file as already answered, twice in one afternoon.
✕ denied · ≤500 tok the rows, instead of the walk
The one blocking check grapple adds, at the one moment the agent has committed nothing: a write outside the scope list the standing brief declared.
In observe phase it records the verdict it would have returned and emits
nothing — no decision, no bytes — so the session behaves exactly as it did before grapple
existed, and those recorded verdicts are the base rate the enforce phase is gated on.
A guard nobody measured is a guard nobody can defend turning on.
✕ denied · ≤900 tok out of scope — in enforce only
bb sieve rewrites an oversized tool result before the window is billed for it twice, keeping the head, the tail and every error-like line, and leaving the full output on disk to grep.
The command shape is appended for bb lathe — git commit, never
the message. Mining that order out of the transcripts took four minutes of wall clock for 4.8
seconds of CPU; this hook is already running on every tool call, so forty bytes here is the
cheapest place in the system to learn the same fact. A shape log also cannot carry a secret
somebody passed on a command line.
Markdown the session writes into the tree is slop-checked, advisory only: a document is the
only artefact here that charges rent, and a hook that rewrites your sentence unasked is worse
than the sentence. And sixty bytes go to grapple's counters, no decision attached: everything
bb grapple scores as drift is read back from this row.
→ window · ≤300 tok a shrunk result, and the rule it broke
Emits nothing, deliberately. Whether this hook's stdout reaches the window after the summary is written cannot be observed from here, and a mitigation built on a guess silently does nothing.
What it does instead is freeze the record: the narrative is written now, so what goes
back after the summary describes the same moment the summary does and not a later one. Then the
marker, and the next observable event — SessionStart with source: compact, or the
next prompt — does the work.
· silent a frozen page and a marker on disk
The fourth verification layer, and the only one that is not the work reviewing itself. It executes nothing. What it can say is whether the ledger this work declared is still sitting there with gates unmet — the one question a session about to report "done" cannot answer about itself. Silent where no ledger was declared, because a workspace that never set a bar has not failed to meet one.
✕ not done the gates still open
The transcript is folded into the ledger and the session is priced: tokens billed and turns taken, measured, against what the local path displaced. Saved is reported as two different things — cache reads and wire trims are measured, displaced turns are counted per episode and multiplied out, and the line says which is which.
Then sleep-time compute. None of it reaches a window — the session is over — and the session that paid for the turns should not pay for learning from them.
scripts/ stays behind its own flag: a hook that adds files to somebody's repository unasked is a hook they turn offbb findings→ you tokens used · tokens saved · turns · peak window
The caps are per emission, not per session. Every band declared in
src/wire/hooks.js added together is 6,100 tokens, and the two largest fire once per
compaction — against reads and searches it routinely denies for more than that on their own.
bb wire status reads the hook log for events the bb on PATH did not
recognise, because an installed guard that an older binary silently ignores looks exactly like one
that is working.
every answer is already on disk
No hook computes anything expensive. Each one serves a file that a gear built earlier, which is why a 15-second budget is never close to spent.
| Artefact | Built by | Served to |
|---|---|---|
.bundlebox/out/snapgen/ | bb snapgen build | pre-search — the declaration rows that replace a walk |
.bundlebox/out/arc/ | bb arc build | pinpoint — every declaration as one binary index |
.bundlebox/out/pinpoint/ | bb pinpoint | prompt and pre-read — the map, then the quoted regions |
.bundlebox/var/brief.json | the prompt hook | the standing brief, so a repeated prompt is not re-located; pre-write, for the scope list |
.bundlebox/var/narrative/ | the pre-compact hook | session-start and prompt — the record of the work, put back after a compaction |
.bundlebox/var/findings.json | bb scan | prompt and compile — what is open, with evidence |
.bundlebox/var/episodes.jsonl | bb buckmaster | session-end — turns displaced per verb |
.bundlebox/var/sessions/ | bb tokens ledger | session-end and bb monitor — the bill, and the window left |
.bundlebox/var/calls.jsonl | the post-tool hook | bb lathe — the shapes this box repeated |
.bundlebox/grapple/ | the post-tool and pre-write hooks | bb grapple — the verdicts it would have returned, and the drift it scores |
.bundlebox/out/janitor/ | bb janitor, at session end | session-start and prompt — the rot notice, and the rules restated in full |
.bundlebox/auditor/<area>/ | bb auditor charter | the bar an area declared, before the work, by hand |
janitor · auditor · snapgen
Three things here are not stages in the tick. Each has its own trigger, writes its own artefact, and is read back somewhere else entirely — which is the part that is easy to lose.
janitor — the memory compiler · triggered at SessionEndA compiler, not a cleanup script, for one reason that shows up in every verb: a cleanup script answers "is this tidy", and a compiler answers "does this build", which is a question with a failing answer. A rule whose file no longer exists is an ERROR. Two live rules that contradict each other is an ERROR, and neither is silently dropped.
| Touch point | Reads | Does |
|---|---|---|
SessionStart | diagnostics.json | the rot notice: which lines in the memory just loaded no longer resolve |
SessionStart (compact) | the narrative, RULES.md | the record of the work goes back, then the rules in full |
UserPromptSubmit | the marker | the same two bands, when no SessionStart answered the compaction |
PreCompact | the record | freezes the narrative and writes the marker, so the restate fires once |
SessionEnd | the four stores | recompiles the heap for the next session, on the sleep-time budget |
Every read is age-capped at janitor.max_age_hours, 168 by default. The
stale guard matters more here than anywhere: a hook that quoted a week-old diagnostics file as
though it described the tree right now would be making exactly the mistake the resolve pass
exists to catch. Exit codes: 0 clean · 10 warnings ·
11 errors · 30 the compiler itself failed.
auditor — the bar, declared first · triggered by handEvery other verb here answers "what is wrong with what was written". This one answers the question that comes before it — what is in scope, what bar it has to meet, who decides when it does not, and what counts as proof — and it answers while changing the code is still cheap.
The audit gear declares on: ["hand"] — it is the one gear
no cron line reaches, because a governance bar nobody chose is not a bar. The split everything
turns on: a charter is living and is re-derived when the area moves, a review is
dated and is never edited. Mixing those two destroys the only thing a review is for.
snapgen — the tables · triggered by fingerprintEach table is a set difference, a parse or a count over the workspace, rebuilt only
when its inputs change. In the orient gear it carries skip_if_fresh
against a walk of the source, so a tick over an untouched tree rebuilds nothing.
This is the one artefact a session is told to read instead of searching, and the only one the guards will deny a tool call on behalf of. A declaration search is the case where the answer is not merely cheaper to look up — it is already written down.
a declared pipeline, as data
A gear is an ordered list of verbs with a gate in front of each stage. Every built-in
gear is free and local: run and bridge are refused inside one, checked when
the crontab line is written rather than assumed. factory chains five of them, every 30
minutes, and stops at lanes.
intake — what is wrongEnds holding lanes: the last point before anything spends.
--applyorient — what a session gets handed instead of searchingEverything the nine hooks above serve from.
measure — what it all costThe half of the loop that decides whether any of the rest was worth running.
buckmaster — train on all of itThe one thing every gear writes is the episode table, which is how the model trains
by the factory being used. The one gear here with a runtime dependency: bb
buckmaster is the CLI half of the Python expert system in expert/, reached
through src/core/expert.js. Without python3 ≥ 3.9 every sub-verb warns and returns 2,
and the tick carries on — the zero-token path never touches this module. What is lost is the
learning, not the factory.
watch — fold what was spentLast in the chain, and it used to be missing: the tick folded the ledger and rebuilt nothing, so the one page this workspace has showed whenever somebody last ran it by hand.
full runs on the slower line, every 6 hours, ungated:
situation → ops → intake → orient → scenarios → watch → buckmaster. It is the one that
needs a service up, so the recom gate — which answers from the tree, the config and the head commit —
would not see it coming.
five triggers · three of them wired
Sixty verbs. Nobody types most of them. Five things can fire a verb, and two of the five are declared in the source and read by nothing.
| Verb | Hook | Condition |
|---|---|---|
init | SessionStart | no .bundlebox in a dir that looks like a project — detection inline, build detached |
janitor read | SessionStart | diagnostics.json under 168h → the rot notice |
the narrative, janitor read | SessionStart (compact), UserPromptSubmit | the round after a compaction → the record of the work, then RULES.md in full |
pinpoint | UserPromptSubmit | the prompt is a task and wire.auto_pinpoint |
snapgen, arc read | PreToolUse · Read, Grep|Bash | the tables have rows for what the call is asking |
grapple | PreToolUse · Write|Edit | a write outside the standing brief's scope |
sieve | PostToolUse | result over the threshold, sieve.enabled |
slop | PostToolUse | a markdown write into the tree |
finish | Stop | a ledger exists with gates unmet |
tokens, session | SessionEnd | wire.measure_sessions |
grapple, lathe, echos, janitor build | SessionEnd | each behind its own config flag |
| Line | Gate | What it reaches |
|---|---|---|
*/30 * * * * | bb recom gate cron/factory | scan, oversight, fix, compile, route, snapgen, arc, pinpoint, buckmaster, tokens, session, bench, triage, echos, lathe, monitor, console |
0 */6 * * * | ungated | the same, plus runbook, viewport, failsafe, doctor, cookbook, mainboard, frames |
Both under one flock, so they never overlap. Neither can reach
run or bridge: unsafeVerbs() walks the chain and refuses to
write the crontab line, rather than trusting that a gear stayed free. The gate runs 48 times a day so
that the pipeline does not.
Nine MCP tools, and the model decides when: bb_pinpoint, bb_context,
bb_snapgen, bb_findings, bb_scan,
bb_oversight_brief, bb_explain, bb_tokens_estimate,
bb_session.
Twenty-six verbs wait to be typed. Most should: wire, unwire,
kernel, update, cron and init change the box, and
a box that reconfigures itself on a timer is worse than one that does not. The ones worth noticing
are the free ones nobody is reminded of — env, context, gates,
uptake, simulate, dotty, designlabs,
scripts, proxy, headroom, agents,
explain, findings.
src/cron.js:100 filters gears for on: "cron". It is the only code in the tree that reads gear.on at all.intake carries it. No git hook is installed, so a commit is scanned by the next half-hour tick and not before.orient carries it. The hook detaches bb init's build instead, and never runs a gear.measure and watch carry it. The hook runs its own list, so monitor status and console build wait for cron.A trigger name that nothing dispatches on is a schedule somebody believes in. The
factory already learned this once — situation, ops and
scenarios declared cron for months while no chain reached them, which is
why the reachability check at cron.js:100 exists. It checks one of the five names.
bb pipeline · eleven stages, each with an exit criterion
A stage is not done because it ran once. It is done when its output exists, is fresh against its inputs, and asserts something. Every stage before agent is free.
Stage 10 is the only one that asks about the work rather than the artefacts. Every stage above it checks that something was derived and is still fresh; none of them checks whether the sessions are converging.