Repository navigation
Releases: blackswanalpha/bundlebox
Release list
v0.9.0
v0.9.0 — 2026-09-27
The release where bb scan takes half the time on a large tree, and
bb sentinel runs everything free before it spends on an agent.
Added
bb sentinel(#82, #84). Runs every check that needs no model first,
then hands only what is left to agent lanes inside the daily budgets. #84
adds the Python policy, kernel ops, and links into lathe and arc.bb pipeline tailand a console gears panel (#79).tailstreams gear
runs and stages fromgear_runs.jsonlandepisodes.jsonluntil Ctrl-C
(--gear,--verbose,--json). The console shows runs per gear over 24h,
7d and all time, with failures, average seconds and the latest 15 runs.- Bench page changes and new tests for doctor, drivers, facts, packs, ratify,
swebench and six expert modules (#81). - One-page pitch, deck and PDF under
docs/pitch/(#70).
Changed
- Scan and pinpoint performance (#71 to #76). On a shallow clone of django
(7,091 files)bb scanwent from 22 s to about 11 s. Each file is hashed and
counted once, five detectors stop rescanning the corpus per candidate, the
kernel'sdupeskeys windows by interned line ids,resolve()memoises by
spec, and a per-file cache keyed by content sha1 carries single-file
derivations between scans.importGraphanddead-depsread import specs
through that cache (#77, #78). - Findings noise (#80). Open findings on this tree went from 826 to 192. A
window where half or more rows are a bare string literal is no longer a
duplicate block, in JS and in the kernel.oversight:god-fileand
oversight:duplicationno longer reach the findings board.bb findings
defaults to 20 rows, most-committed files first within a severity. - The cut-file prompt suggests a pasteable
bb pinpointcommand (#83, #86).
Fixed
orientskipsarc buildwhen the arc binary is absent. An npm install
ships no binary, and this was 104 oforient's 119 recorded failures (#79).bbwaits for stdout and stderr to flush on exit.bb findings --json | …
stopped at 65,536 bytes (#81).
Known issues
test/doctor.test.jsfails on Windows: therepositoryrow reportswarn
for a workspace whose git lives in a subdirectory. The release gate runs on
Ubuntu.- A kernel installed before this release reports help tables as duplicate
blocks.bb kernel installfetches the new one.
v0.8.0
v0.8.0 — 2026-09-22
The release where a probability model watches the agent, and Jev's word reaches
the price of a lane.
Until now Jev was asked one kind of question, whether a contested shape is
deliberate, and only the ask queue read the answer. This release gives the
answer two more readers. Triage prices a finding with it, and a new verb,
bb foreman, asks Jev about the agent's run itself: is it stuck, off track,
finished, verified. Every Jev call now survives a slow reply.
Added
bb foreman — a responsibility policy over the coding agent (#64).
The design is thruwire/foreman's, ported
into the Python expert (expert/bundlebox_expert/foreman.py, stdlib only).
Six responsibilities own twelve yes/no checks: human escalation, repository
instructions, documentation (routed, asked only when the job calls for docs),
worker health, completion and verification. Jev answers every check in one
call. A deterministic policy turns the probabilities into one of continue,
steer, stop, verify, resume, finish or escalate. Thresholds and priorities are
thruwire's.
bb foreman [--job "..."] [--since <rev>] [--active] score the run, pick one action
bb foreman verify [--cmd "npm test"] run verification against the current tree
bb foreman replay [--set <check>=<bar>] re-decide the timeline under other bars
bb foreman label <#> right|wrong mark a recorded action for replay
bb foreman log | checks
- Without a key the checks come from what the box already records:
grapple's drift window for stuck and off track, git's changes for work done,
and the lastbb foreman verifyrun. Evidence cannot tell whether a job's
requirements are met, so on evidence alonefinishneeds a passing
verification. - A run's commits are its work (#65). A run is measured from a base
commit:--since, else the base its first assessment recorded, else HEAD at
the session's first tool call. Before this, a run that committed as it went
looked like a run that had done nothing. - A verification is tied to the tree's content hash, built with
git write-treein a throwaway index. Committing a verified tree keeps it
verified. Editing it makes the verification stale. - Every assessment is a row of
.bundlebox/var/foreman-timeline.jsonl,
with the scores and state it was decided on.replayre-runs any threshold
change and counts candidate fixes and regressions against the labels.
thruwire lists this calibration tooling as missing. - The PostToolUse hook steers a live session. Every
foreman.hook_every
tool calls (10) it assesses the session with the agent marked active, so
only steer and stop can fire. It ships asforeman.hook: "observe": the row
is recorded and nothing is injected."steer"puts the steer or stop into
the agent's window. Jev stays out of the hook unlessforeman.hook_jevis
set, and then gets one attempt, because the hook is killed at 15s.
Measured on this repository with Jev, one session's work, --since 2c5c471:
with the job stated as row 0's loose text the policy said resume
(implementation_complete 0.49). With a --job naming what shipped, the same
commits, tree and verification gave finish (0.92). Jev scores the run
against the job you state.
Changed
Jev's probability reaches the price of a lane (#63). Triage priced every
heuristic finding off one method constant, PRECISION.heuristic = 0.6. Jev's
p now enters expectedValue as a prior on that constant, weighted by the
code windows Jev read (at most 3, shrunk at n/(n+4)). p=1.0 moves a heuristic
from 0.60 to 0.34, not to 0. On a medium finding at 150k tokens, ev goes 0.8
to 0.51, under the 0.6 floor, so the lane does not open. A person's answer
retires the opinion for its shape.
The Jev call retries a timeout, a 429 and a 5xx (#67), up to twice after
the first attempt: 500ms then 1s, or the server's Retry-After up to 5s. A
refused connection or a 4xx returns at once. One bb foreman request timed
out at 8s and the same request answered in 2.1s moments later; before this,
that one slow reply dropped Jev for the whole assessment.
Fixed
- The read guard denies only lines its quote can hand back (#68). A
region's quote is capped at 6000 characters when recorded and 2600 when
served. A region spanningsrc/wire/hooks.js:292-825arrived as ~30 lines,
and reads of lines 414-443 were denied with a quote that did not contain
them. A ranged read is now denied only when the quote carries every line, a
whole read only when every region arrives whole. - The foreman hook records a base for a session entry that has none (#66).
Entries written before bases existed never got one, and the run fell back to
HEAD: 0 commits, and a clean tree read as no work. - The MCP registry job waits ten minutes for npm, not one (054d5e5).
v0.7.1's record went red on a 404 while npm was still processing the
tarball.
v0.7.1
v0.7.1 — 2026-09-22
The release that draws the factory's own numbers and then puts the server where
something can find it.
v0.7.0 aimed the locate. This one answers the question that follows: is the
aimed locate worth anything, and can anyone install it. Seven figures now come
off the runs already on disk, including the one arm bundlebox loses. And bb mcp — nine tools that until now nothing outside this repository could
discover — has a registry record, a plugin manifest and a CI job that publishes
both.
Measured
SWE-bench Verified, 100 instances, both arms counting tokens over text on disk,
neither calling a model:
| bare | packed | |
|---|---|---|
| context per task | 8.7k | 2.8k (−67.5%, 3.1x) |
| gold files in scope | 24.6% (31/126) | 64.3% (81/126) |
| instances with every gold file | 26/100 | 61/100 |
| files put in front of the model | 10 read whole | 6.8 in scope |
| assembling the window | 246ms | 8.9s |
The last row is the arm bundlebox loses, and it is drawn rather than dropped.
6.6s of the 8.9 is the first-visit symbol space, which a workspace pays once
rather than per task; on a tree already scanned the same row reads 38ms against
1.6s.
Derived from those counts at vendor list prices, no request sent: $43.59
against $14.17 per 1,000 tasks on Opus 5.
Added
docs/benchmark/index.html — the runs as a page.
scripts/benchmark-page.mjs draws it from what is already on disk: SWE-bench
Verified at n=100, this repository's own open findings, the 142-run store and
the price table. A number it derives rather than reads says DERIVED where it
appears, and the prefill rate behind the only time estimate is a control the
reader sets. The trend shows savings falling from ~98% to ~67% on 2026-09-19
with the cause named — 1cdb60a re-modelled the bare arm to open one 80-line
range per hit instead of reading matched files whole, so the step is the
baseline getting stricter, not the tool getting worse. Served at
https://blackswanalpha.github.io/bundlebox/docs/benchmark/
A clock per arm. bb bench arm and runTask stamped one seconds across
both arms together, so the question a session waits on had no answer per arm.
bare_ms and packed_ms are carried through the SWE-bench rows and summed
into the totals.
The server is discoverable. server.json is the official MCP registry
record and package.json carries the matching mcpName, which is how the
registry verifies the package and the record have one publisher.
.claude-plugin/ makes the repository installable with
/plugin marketplace add blackswanalpha/bundlebox, carrying the MCP entry and
both skills. smithery.yaml declares the stdio start command, listed local
because bundlebox reads the repository it is started in. The README gains a
copyable mcpServers block for agents bb wire does not manage.
release.yml publishes the registry record. A new mcp-registry job, after
npm because the registry reads the published tarball to check mcpName. It
holds no secret: login github-oidc exchanges the runner's own token, and
GitHub grants io.github.<owner>/* to workflows in that owner's repositories.
The rule the file already had is unchanged — the job sits below release, so
its failure ends there.
Fixed
The locate baseline counted rows its own guard would refuse. It read
measured, recall 0 over 20 exception rows. Fifteen of those twenty were rows
nothing acts on: 8 files that did not exist when the brief was written, 5 test
files, 1 path outside the workspace, 1 artefact under .bundlebox/.
writeVerdict exempts the last three outright, so the denominator and the
guard the figure is read to decide disagreed about what a row is. excluded()
now applies the guard's own four rules, markCreated stamps each edit with
whether git says the file arrived after its brief, and the exclusions are
tallied and named so the smaller sample is auditable. The baseline reads
unknown: 5 rows over 2 briefs, under its own 12/4 floor.
An import edge can name a file. Two of the five surviving rows were never
named for "close the loop on a verified scenario" — neither word is in the
statement, so nothing lexical could reach them, while src/genesis/practice.js
was in the scope the ranker chose and imports both directly. neighbours
spreads a share of the leader's score along out edges and they return at
candidate 3 and 7. Named, never budgeted: rankDetailed reports them
separately, so the opening scope is identical to before at about fifteen tokens
each. A 24-instance SWE-bench A/B is identical in every cell, because those
instances are Python and this graph carries 4 Python import edges — it proves
no regression and nothing more.
The bench scenario cost one run per open finding. bench init derives its
suite from the open findings, so the corpus paid whatever this repository
happened to owe that week. At 12 findings a run took 37.7s against the corpus's
20s cap and the step that ran it twice took 75s — red on every main build since
v0.7.0. The suite is pinned at two tasks, 5.0s, and the step that re-ran the
whole thing for determinism is gone: ci.yml already asserts it on the full
suite across both arms with no timeout, and asserts more than the step did.
The corpus reads 58 passed, 0 red.
Checks
npm run lint 290 files, 0 errors
npm test 0 fail
npm run expert:test 71 tests, OK
cookbook run 58 passed, 0 red, 0 blocked, 0 empty
ablation gate bare 218652, packed 28187, 87.1% over 12 task(s)
v0.7.0
v0.7.0 — 2026-09-22
The release where the locate is aimed, the work is labelled, and the factory is
put under its own microscope.
v0.6.0 measured the loop against itself. This one acts on what the measurement
said. The content search was returning an arbitrary twelve files and now
returns the best twelve, scored. Four fitted heads went in and every one of
them abstains until it beats its own holdout. bb intent replaced the fix
that every prompt was being labelled with in silence. And a fifth mainboard
category reads this box's own output, because bb mainboard gaps reported a
healthy queue while fifty-nine of its sixty-two units were the same finding.
Measured
grepHits, 100 SWE-bench Verified instances (78 django, 22 astropy), 126 gold
files, same instances each run, no model called:
| arm | in scope | named | packed | ratio |
|---|---|---|---|---|
| before | 73/126 57.9% | 75.4% | 333.7k | 2.6x |
| scored grep, slice 6 | 82/126 65.1% | 83.3% | 336.3k | 2.6x |
| scored grep, slice 12 | 81/126 64.3% | 83.3% | 283.3k | 3.1x |
MAX_FILES 6 -> 12 is a cost change and not a recall one: recall moves by one
gold file of 126 and the prompt is 16% smaller. Instances where a plain grep
reached a gold file the brief never mentioned: 2 before, 0 after. bb pinpoint
stays at 0 model tokens, 0.54s against a 0.78s baseline and a 1s gate — the
first version lowercased every file to search it and doubled the locate to
1.5s.
What each fitted head has to stand on, on this tree:
task-head n=12, 10 of 12 fires led to an edit — no win over the regex yet
confidence prior 18 acted_on of 156 — the constant stays
symbol space 2,945 symbols x terms, stdlib SVD, 1.6s, 95 KB
locate aim unknown, n=3 over 1 brief, against a floor of 12 over 4
Every one abstains until it beats its holdout, which today means none of them
steers. That is the result, not a step before it.
bb genesis --finding swept over all 945 open findings: the 28 that name a
route or a command each derived a capability, the rest derived the row saying
they could not, and every one derived a rule.
Round trips, from the shapes the sessions actually recorded: 1.04 tool calls
per turn, npm run -> npm test -> npm run the most common three-command run
here, and git status -> git diff recurring 17 times across the 18 sessions
that run either.
Added
bb intent — which kind of unit a prompt is. fix, verify, investigate,
build or write, decided from a table fitted offline rather than from a model at
prompt time. The kind picks the output reserve reserveFor reads, and the
shipped table reserves 12k for a fix against 40k for an investigation, so the
label moves ~28k of window between room-to-write and code-to-read. Until this
existed autoPinpoint passed fix for every prompt — a wrong answer on every
question that is not one, billed every turn. Five one-vs-rest heads rather than
one multiclass fit, so a kind the join never labelled falls back on its own
instead of taking the other four down, each carrying its own useful. Same
prompt plus same table gives the same kind every time, offline, and the
derivation comes back with the decision. Fitted at session end, the one budget
allowed to call Jev.
bb case — everything the board knows about one subject. Six subsystems
can each be right and leave nobody able to say what happened: a declared
service down, a corpus that cannot run, eleven spun sessions, tokens-per-turn
at 2.3x its ceiling, one detector owning 89% of the queue. Nothing here
collects anything — every row was already in the store — and a subject is
equality on a value a row already names: a file, or a service, stage,
session, detector, corpus, base, url, view or area in its
evidence. Deliberately not a connected component: the first build used one and
produced a single case of 622 rows, because a duplication finding bridges the
two files it names and enough bridges merge the tree. occurred.js dates a row
from git:last-touch or from the evidence itself and reports how many rows it
could place, rather than copying first_seen and turning every timeline into a
record of when the scans ran.
The proofhouse — FACTORY, a fifth mainboard category whose subject is this
box. bb mainboard gaps reported "Agent: 62 unit(s) ready of 66" while
fifty-nine of those were one fileless finding scoped to the whole repository,
6.9M projected tokens, every part verdict FITS, and nothing in the tree could
say so. Four checks, each arithmetic over an artefact already on disk, each
naming what it read in its evidence. A check that cannot look reports blind
instead of nothing. It runs last in the tick, because a queue is worth checking
once it is full, and it is wired into the factory gear as an optional step.
bb genesis --finding <id> and --ticket <file>. Two inlets beside the
document: the row's path is the surface, its detail is the rule, the routes and
commands it names are capabilities, and everything it cannot settle is an
unknown row.
bb genesis practice. The round loop that sends packs, verifies what came
back, keeps or rejects each scenario and records the lessons. It resolves the
persona's base against services.json and starts what declares it — services
declared and none at this base is a mismatch it refuses to run at, no services
at all is a base a person keeps up. A round that keeps a red-under-a-rule
scenario compiles and routes what it merged, so it ends holding a budgeted unit
and a lane. Two stops: a ceiling re-read at the top of every round, and
coverage holding across two rounds.
bb gates run and bb situation. At 1.04 tool calls per turn every turn
is a full replay, so the cheapest saving is a verb that answers a whole
situation in one call. gates run runs each distinct gate once and stops at
the first failure; situation returns the tree, the gates, the stale
artefacts, the packed work and the last echos run.
bb grapple, asking Jev. A calibrated opinion on each contested shape
before the queue is ranked, behind TYPESAFE_API_KEY, off with BB_JEV=off,
and null on any failure. jev.probabilities() exposes the same call to a
caller that composes its own state, so the prompt path gets the same treatment
as opinions().
spends: true on a pipeline stage, refused by the runner unless
bridge.enabled, bridge.daily_budget_usd and lanes.daily_budget_usd are
all set, with the missing one named. bb cron install --spend writes the line
for it as a separate list, so the default install has no branch to get wrong,
and the three keys are read at the tick — unsetting one stops the loop without
touching the crontab.
over_by in bb tokens budget: the fold gap reported, not closed.
ui steps in the corpus: four verbs, a driver declared in persona.json,
no model between the step and the action. page reads a local HTML file,
playwright and adb are imported at run time and report their absence rather
than throwing.
Changed
grepHits scores before it slices. It scored nothing and stopped at the
first twelve files matching any of the four longest terms, in directory order.
It now scores every file by how many distinct long terms it carries, returns
the best twelve, runs on every locate rather than only when the symbol tables
went quiet, and weights its hits by that count instead of a flat 1.
ambiguity() can now be unresolved. Its eight signals were all presence
tests, so a locate that matched three irrelevant symbols reported nothing
wrong. PRECISION gains lexical and for_rule() becomes a ninth signal that
fires only on a measured locate under its own prior. locate-replay scores
only the EXCEPTION rows — an obedient agent edits inside the scope by
construction, so an in-scope edit measures obedience and not aim — and drops a
file changed and changed back inside one window first.
per_detector reads the open count, not the run. Four rows a run over a
hundred runs never tripped a per-run limit: 285 duplicate-blocks and 200
oversight:duplication rows sat on an 875-row board while every run stayed
inside its budget. A row whose actuator closes it is exempt; a plan- actuator
is not, because it writes the plan behind a decision and leaves the finding
open. 485 rows now report as counts and every suppressed count is printed
rather than dropped.
aggregate() splits by intent kind. Re-reading while investigating is
reading; re-reading while fixing is the waste the rule was written for, and
averaging the two hides both. buckmaster joins sessions to intent rows for
the label and prints the split only when more than one kind is present.
reserveSamples reads intent rows too, so the reserve table is checkable
against located prompts and not only against the sessions a lane ran.
bb lathe apply reads grapple's proposals. Every row past the bar is
reported as proposed with the step still missing. Nothing from grapple becomes
a file and the ledger is untouched, so reach and tombstone are unaffected. An
absent file reports present: false rather than that grapple earned nothing.
One similarity in janitor/heap.js over terms, files, prefix and title,
with an optional fitted table and the Jaccard as the documented fallback;
bench/gate.js delegates to it. converge.rs is kept on purpose: the arc is
the independent second opinion.
Fixed
The repository root is not a scope. A finding with no files whose only
location is . is about the workspace, not about code in it. Handing . to
context.evaluate expanded it to every file in the tree, reported SPLIT, and
returned one window-sized slice of the repository per part — twelve such
findings compiled to fifty-nine units holding 6.8M projected tokens. A
directory below the root still counts, because ui-generic files one row per
directory and those files are the finding. An empty scope is a state the brief
already na...
v0.6.0
v0.6.0 — 2026-09-19
The release where the loop is measured against itself, and the box learns to
ask.
v0.5.0 gave every subsystem something to drive. This release asks whether the
driving is aimed: the promotion rule is replayed against the closures git can
prove, the scenario selector is scored against boards it already ran, the
locator's aim is measured per brief, and the packed arm of bb bench is
compared with 36 real agent runs rather than with a strawman. Where the
measurement contradicted a claim, the claim moved.
The other half is the handoff layer. A session that hits something the box
cannot settle used to guess. bb grapple records the question, asks it once,
stores the answer, and in its first phase changes nothing about a session
except what it writes down.
Measured
bundlebox-projects/bench, 2026-09-18: 36 Claude Code runs on
claude-sonnet-5, six seeded defects, three repeats, both arms on one 39-file
tree.
claimed by bb bench |
measured | |
|---|---|---|
| tokens, packed against bare | 13.3x fewer | 1.36x fewer |
| time to first token | — | 47% slower packed |
| cost | — | 5.3% more packed |
Three defects follow from the raw runs and each is a change below: the bare
arm read whole files no agent reads, two briefs shared 1,594 of 3,350
characters after the varying part so nothing cached, and the packed arm
acknowledged its own instructions at 34% more output per turn.
Closure labels, bb triage backfill over this repository:
15 acted on a commit in the window touched the path
69 vanished the path is gone
30 unchanged the window has commits and none touched the path
87 unknown the window has no commits; git cannot see a working tree
The rule refuses more than it answers on purpose. 128 of 201 closure windows
here contain no commit at all, and labelling those unchanged would fit a
policy that learns the findings people fix are the ones nobody touches.
Round trips, bb echos over 23 sessions: 4,391 calls across 4,269 turns,
1.03 per turn. One session made 452 calls across 452 turns without issuing two
at once.
Added
bb grapple — the handoff layer. What the box cannot settle, asked once
and stored. Six sub-verbs over an event log under .bundlebox/: observe
runs every detector and records what it would have done; ask is the queue,
capped at two per session because past that a queue is an interruption;
ratify batches yes/no rows into one decision moment; harvest reports the
labels nobody was asked for, or unknown below ten of them; promote turns
what the log has earned into lathe proposals; status prints phase, queue,
collisions, drift and labels. The scoring is expert/bundlebox_expert/grapple.py
with the base rates in priors.json. grapple.phase decides what leaves the
module: observe, the default, injects nothing; enforce adds one blocking
check and lets the queue reach the window; the phase moves when the bench arms
say the guard earned it.
The pre-write hook. PreToolUse on Write|Edit|MultiEdit|NotebookEdit,
bb hook pre-write: a write outside the scope list the standing brief
declared, at the one moment the agent has committed nothing. In observe it
records the verdict and emits no bytes, so the recorded verdicts are the base
rate enforce is gated on. Wired by bb wire --apply.
The narrative, put back after a compaction. src/wire/narrative.js
writes what the session was doing from the record the guards and hooks left
on disk, never from the summary: the task, the scope, the files edited, the
last commands, the gates still open, the questions nobody answered.
PreCompact freezes it beside the marker so the summary and the record describe
the same moment. SessionStart with source: "compact" puts it back first,
then the rules verbatim, each under its own cap, and the marker keeps the next
prompt from doing it twice. A harness with no PreCompact still gets the page
as it stands.
bb triage — the labels, and the rule fitted to them. backfill labels
the closures git can still prove, four ways, and refuses where git is blind.
calibrate hill-climbs the promotion rule over the labelled ones with the
cost of a token as an input, scored against the whole corpus's tokens so
"promote everything" cannot win. The shipped policy is the starting point and
ties leave it in place. --apply keeps the fitted rule in calibration.json
with its sample size, where bb doctor can show it. On this tree:
--cost 0.5 promote_at low 114 promoted, 15/15 caught, waste 0.80
--cost 2 promote_at high 20 promoted, 6/15 caught, waste 0.02
bb cookbook calibrate. A stored board records the realised state of
every scenario, so an alternative selector is scoreable against boards it
already ran, leave-one-forward, no request and no process. Four rules keep the
number honest, one test each: a selection is scored as a set, a green board is
excluded rather than scored zero, the shipped vector is candidate zero, and
board i is scored only by a policy that saw boards before it. On this
workspace it refuses: 8 of 9 boards are green, so 1 can be scored against a
floor of 4.
bb cookbook check refuses an undefined token. An unresolved {{token}}
was a red step about nothing that cost a session to diagnose. The check builds
the scope once from the persona, base, the setup's saves and the earlier
steps' saves, and answers from the code that already resolves tokens rather
than a second copy of the grammar.
Two echos. stray asks the question converge cannot: of the files a
session edited after being handed a scope, how many were in it, one window per
brief. batching counts calls per turn, every call and not only the ones that
produce an event, pooled and reported once.
Closure labels on findings. witness() hashes size and mtime per open
finding on every scan, a stat and never a read. When a finding closes it says
acted_on, vanished, unchanged or unknown, because a policy trained on
resolved alone learns that deleting the file is the most reliable fix.
pinpoint carries the edit. statedEdits reads backticked pairs out of the
problem statement and proposals emits a unified diff, only when the old text
occurs once, on one line, in one located region. Otherwise the section is
absent: a wrong diff costs the undo on top of the fix.
One log row per brief. activate() appends to var/brief/log.jsonl, one
line per brief, never overwritten, so a session that locates four tasks keeps
all four and the scope that had to be located again is the one worth reading.
The second cron line. bb cron install writes factory every 30 minutes
under the recom gate and full every 6 hours, ungated, for the gears that
declared on: cron while no installed line reached them. bb doctor reports
a gear declared but unreached as its own row.
The docs. Both pages carry a sidebar with named, iconed sections, and
docs/visuals/index.html lists all sixty verbs from the command table with
the help line, the usage line, the source file and the effect column, filtered
by text and chapter. docs/demo/ holds the four-beat demo and the script that
regenerates it.
Changed
One preamble opens every brief. pinpoint and a genesis pack share it, so
the first byte that differs between two briefs is the title and the shared
prefix caches. The "Do not" list is folded in as imperatives and the section
headers lost their trailing instruction clauses.
The bare arm of bb bench is an agent. It greps the tree, counts the
search output and opens one 80-line range around the first hit per file. The
measured agent read 16 tokens of fresh input across 18 runs; the old arm read
the top N files whole.
The intake gear runs bb fix before compile, with no flags, so a tick
reports what the actuators could close for free and edits nothing. Closing
them is bb fix --apply, by hand.
cookbook run gates on base_up, one TCP connect when the corpus
declares a base. A corpus run against a dead port read as twenty failing
scenarios rather than one absent service.
The command centre is the console, in the module, the verb, the alias,
the artefact path, the config key, the service name and the committed corpora
that execute it. Earlier changelogs keep the old name because they record what
shipped under it.
Fixed
The duplicate-search denial keyed on the words alone, so grep "^export"
over a second file was refused as already in the window. The key carries the
target now. Found on this guard's author's session, twice in one afternoon.
A bb on PATH older than its hooks was silent. Every event it did not
know was logged as unknown event and did nothing, and the log was the only
place it showed. bb wire status reads that log and says which events, and
that the fix is a reinstall.
bin/bb is a POSIX shell wrapper that locates a usable node, replacing
#!/usr/bin/env node, which exited 127 under the minimal PATH a hook, cron
job or systemd unit inherits. Four sessions of accounting were lost while
every command reported success; messages/crash.md is the reproduction.
bb session rm and bb session backfill recover the records from
transcripts already on disk.
The janitor's status verb wrote. emit writes only when applying; the
bare run was replacing 1.1MB of out/janitor/ while documenting itself
read-only.
An edit arrived as an absolute path and a scope is repo-relative, so
without rel() every edit read as strayed and oscillate saw one file under
two spellings.
bridge nested a brief assuming its H1 was line 0. It drops the title
wherever it sits and demotes the rest.
Verified on this box before tagging:
npm run kernel && npm run arc bbk 0.6.0, arc 0.6.0
npm run lint 0 errors
npm test 596 pass, 0 fail
npm run expert:test 55 ok
v0.5.0
v0.5.0 — 2026-09-17
The release where what this box measures drives something.
v0.4.0 moved the locating to the side that does it for nothing: bb pinpoint
in front of a search, the reference tables in front of the tree, and a gate
that says whether returned work is finished. Every subsystem underneath it
already had its data and its report, and nothing in between. bb lathe
proposed scripts nobody applied. bb bench printed the tasks where packing
loses and they stayed routable, so the next run packed them again and lost
again. bb uptake measured instruction lines nothing removed. Seventeen
detectors filed a finding and left it for a session, including the ones whose
fix is a delete.
So this release is the actuator layer, plus the two surfaces that were missing
from the loop it closes: a session that opens with no environment gets one, and
the ports this workspace is served by are a page rather than a guess.
Measured
Actuator coverage, bb findings --json over this repository today:
792 findings open
484 name an actuator that runs locally, for zero model tokens
14 of the 22 actuators are named by at least one finding
| before | after | |
|---|---|---|
| findings naming a local actuator | 2 | 484 of 792 |
bb lathe proposals, and the habits under them |
114 proposed | 9 real |
| echo findings against this workspace | 16, twelve of them the echo's fault | 4 |
| CI jobs that ran the arc/JS agreement test | 0 of 9 | every one |
The lathe row is a subtraction, not a filter. commandShapes shaped heredoc
bodies as commands, so } catch, const n and Math.random() are on disk in
shapes.jsonl; it split pipelines on |, which made grep ; head the
seventh-strongest habit at 70 occurrences; and it counted wrappers, which made
timeout the most supported shape on the box. With those three closed, 114
proposals become 9 habits, and each one is a shape somebody ran by hand more
than twice.
544 node tests, 36 python tests, 0 failures, 260 files lint clean. 437 node
tests shipped in v0.4.0.
Added
bb fix runs 22 actuators. Each closes one mechanical case of one finding
and declines the rest by name, because a guessed edit is a wrong edit nobody
checks again. Six are the mechanical deletes: drop-dead-export,
remove-dead-dep, strip-debug-line, relock-npm,
resolve-identical-conflict, drop-dead-knob. Eleven more cover the detectors
that named none — four edit, seven write the plan behind a decision and leave
the finding open for the person who makes it.
Every actuator re-derives its detector's answer before it acts. A scan is a
photograph, and drop-dead-export acting on a stale one unexports a symbol
somebody started importing this morning; the test for that writes the caller
after the scan and asserts the decline. Each imports the detector's own pattern
rather than copying it — exportDeclRe, isWholeLineDebug and
conflictBlocks are exported now, so the rule that files a finding and the
rule that fixes it cannot drift apart.
drop-dead-knob is the first entry in DESTRUCTIVE, which was declared empty
and waiting for one. bb fix asks for --force before it runs. The edit is
reversible; what is not is that a knob somebody already set stops being ignored
and starts being unknown.
The rest of the actuator layer.
bb bench gate a task the bench measured as packed-loses-bare is marked
`pack: false` by `bb route` and refused by
`bb monitor guard --for`
bb recom repeatable fact-records for the four surfaces that re-drove on a
schedule: the cron line, three pipeline stages, cookbook
boards, genesis coverage
bb wire trim the instructions block is rows with ids, so a line
`bb uptake` measured nobody reaching for can be removed
bb lathe apply writes the habits into scripts/ with a `bb recom` record
behind each; `reach` measures whether the habit recurred,
`sweep` tombstones what displaced nothing
bb proxy pack, serve and sieve at the doorway, for an agent whose
hook system this box cannot write into
bb echos spin, oscillate, drift, diminishing and converge over the
sessions already on disk
tokens calibrate churn_factor, anchor_widen and reserve_by_kind fitted per
repo, on the `measure` gear's tick
bb wire trim refuses to trim all nine MCP tools: 0 of 19 across the whole set
is the server not being reached, and bb unwire is the verb for that. A
calibration factor with too few samples keeps its shipped value and says so.
A session that opens with no environment gets one. Found by running
bb env in a workspace somebody inited once and never wired: 2 of 9 artefacts,
bb wire status 0/11 files. Every surface this box installs assumes
.bundlebox is there, none of them says so when it is not, and the session
does what a session with no environment has always done — it searches the tree.
SessionStart runs bb init inline, because detection over the tree finishes
inside the 30s hook budget, and detaches the artefact build, which is minutes
of scanning. Measured on a fresh fixture: 2.5s inline, artefacts on disk within
6s.
The guard is asymmetric on purpose. Declining a real project costs one line of
advice a session can act on; initing a home directory, a mount point or /tmp
scatters a state directory nobody asked for and starts a background build over
a tree that is not a codebase. The obvious non-projects are refused by name and
the rest need positive evidence: a git worktree, or a manifest at the root or
one level down. wire.auto_init turns it off, and it does not switch off with
inject_context — a workspace that was just created still says so.
bb viewport serve — the ports this workspace is served by, as a page.
bb runbook status answers for the declared system. A workspace is served by
whatever is listening: a dev server somebody started by hand holds the port the
declared one wants, and runbook reports that service down without being able to
say why. The viewport reads the socket table and joins it to the declaration,
which produces three sets — declared and listening, declared and not, and a
listener nobody declared whose process works inside this tree. The third is the
one no other verb reports and it is usually the reason for the second.
Three socket readers, because the one that exists differs by box: ss, lsof,
netstat. how names the reader that answered, because a table with no rows
means "nothing is listening" under one reader and "no reader was available"
under none.
Two mainboard views. bugbash is the bar a screen has to clear, checked
against the rendered screen rather than the source: six mechanizable rows from
spinwish's board, and the fourteen that need a person reading screenshots are
not here. It pairs with designlabs, which measures the declaration and says
so itself (affordance.signified unknown — a parse cannot see a pixel), so the
two produce the comparison neither can make alone: the declaration passed and
the screen does something else. turntables replays the boards already under
.bundlebox/var/boards/ and files the two facts no single run holds — a
scenario that changed its mind between runs nobody touched the corpus between,
and a scenario that went green to red and stayed. It runs nothing; the corpus
was already paid for.
bb dotty keeps what a run showed, the other half of what bb recom keeps.
The PNG is for the person; the accessibility summary is for the session,
because an image costs vision tokens and cannot be grepped, diffed or asserted
on.
Two detectors. swallowed-errors — a catch that discards the error with no
comment saying why (79 here, and quiet on the ones this tree already explains).
dead-config — a shipped key nothing reads (9).
sieve.enabled defaults to true. The allowlist reaches log, probe and test
output, Read/Edit/Write are absent from it by construction, and an elided middle
spills to disk before the marker is written.
Changed
Three echo rules were measuring the harness rather than the work. Found by
working through what the echos filed against this workspace: sixteen hits, and
twelve of them were the echo's fault.
drift reported five sessions as having changed nothing. Every one had changed
plenty, through the shell — one session ran 217 bash calls holding 59
redirects, 42 heredocs, 11 git writes and 4 sed -i. bb uptake settled the
same argument for reading and states it plainly: a file opened with sed -n
counts as much as one opened with Read, because which tool a session uses is a
harness setting. writesFiles says the equivalent about writing. A shell edit
carries no hash and no path, so oscillate ignores it, which is honest: this
box cannot tell whether a sed -i put a file back to a value it already had.
Five hits become one.
diminishing fired on 15 of 15 eligible sessions here, at ratios between 1.62
and 3.25 around a median of 2.24. The docstring at the top of the file already
said why that is not a finding — every long session costs more per change
because the window is re-sent on every turn, and that much is arithmetic — and
the threshold was then set at 1.6, below the arithmetic. A rule that fires on
the middle of a distribution is describing the distribution.
diminishing_ratio is a multiple of this workspace's median now, which is the
move bb oversight already makes when it calls a file "2.6x this tree's median
of 135". Under five eligible sessions there is no median worth having and the
answer is unknown, never ok. Fifteen hits become one.
spin claims the same question cannot return a different answer. That fails
for a command watching something this box does not control — gh pr in four
sessions, curl in a fifth, git show in a sixth — ...
v0.4.0
v0.4.0 — 2026-09-16
The release that stops recommending the box and starts running it.
bb uptake shipped in v0.3.0 to answer one question: of everything bb wire
installs in front of an agent, what does a session reach for? Sixteen
sessions later the answer was in. The MCP tools fired in 0. bb pinpoint
fired in 3 of the 13 sessions that opened five or more distinct files — and
those thirteen opened between 30 and 598 files each. The reference tables were
read in 5 of 16 sessions that ran a search.
Every surface was advisory, and an advisory surface loses to the model's own
habit about three times in four. So the work in this release moves the locating
to the side that does it for nothing, refuses to hand over what it already
found, and then makes it possible to say whether what came back is finished.
The completion half is not ours: the gate checker is
unlazy 2.1.0 (MIT, commit 1667149),
vendored and renamed rather than rewritten. src/finish/vendor/PROVENANCE.md
records every rename and why nothing else moved.
Measured
bb bench run — 12 tasks derived from this repository's own open findings. Both
arms are token counts over text on disk; neither calls a model.
bare 783.9k search the tree, open the top 10 files whole
packed 32.7k one pinpoint prompt per task
saved 751.2k 95.8% of the bare arm, 24x less context
bb bench swebench run --n 12 — SWE-bench Verified, 12 astropy instances, run
against a worktree at the commit before the ranker change and again after it.
Identical instance sets, verified by hash.
| before | after | |
|---|---|---|
| gold files in scope (budgeted to be read) | 2/16 — 12.5% | 8/16 — 50% |
| gold files named (in scope, or pointed at) | 6/16 — 37.5% | 11/16 — 68.8% |
| bare arm (top 10 files read whole) | 5/16 — 31.3% | 5/16 — 31.3% |
| instances with every gold file in scope | 2 | 6 |
| packed context | 35.8k — 91x less | 50.5k — 64.5x less |
Before the ranker change the packed arm localised worse than reading ten files
at random: 12.5% against the bare arm's 31.3%. The brief was cheap and pointed
at the wrong files. The trade is stated rather than buried — packed context rose
41%, so the context saving fell from 91x to 64.5x, in exchange for four times the
localisation.
Not measured here: resolve rate, patch correctness, latency, tool calls, billed
cost. Those need the official harness, a model, and an A/B of real sessions.
Added
-
The enforcement axis (
wire.auto_pinpoint,wire.serve_from_brief,
wire.guard_searches).UserPromptSubmitnow runsbb pinpointon a
task-shaped prompt instead of suggesting it — 0.47s measured against a 15s hook
budget, because every input pinpoint reads is a stored artefact and it computes
none of them. What enters the window is the map, about 420 tokens; the quoted
regions stay on disk until a read asks for one, because paying for every
located region on every prompt spends the saving on regions the session never
opens.PreToolUsethen answers from that brief. A read whose region is already
quoted is DENIED and the quote comes back in the denial reason: measured on
src/wire/brief.js, 392 tokens of region in place of 11,185 tokens of file. An
exact duplicate read is denied. A single-identifier search the declaration
index answers is denied with the rows. A file cut from the scope for budget is
asked about rather than refused, because the brief's own instruction is to
name it and why.A file in SCOPE is never fully blocked. Claude Code requires one successful
read of a file before it will edit it, so blocking the scope blocks the change;
the denial says to read the range, that read is allowed, and it costs the quote
instead of the file.Grepis the declared tool andBashis where the
measured transcripts read and search, so both are guarded. -
bb pinpoint gaps/bb pinpoint next.bb scan,bb oversight scanand
bb auditor gateeach produce a statement about the tree and then stop one
step short of the window, leaving a session to spend its first turns finding
out where the statement lives. Every open finding, oversight measurement and
declared standard that failed or has no evidence now becomes a located,
quoted, budgeted brief before a model sees it;nextmakes one the session's
active brief, which is the record the guards answer from.unprovennever collapses intofailed. A standard nobody has looked at is a
gap in the EVIDENCE and the brief says so, because a worklist that quietly
promotes "nobody looked" to "this is broken" is the failure the auditor exists
to remove. -
bb finish— the acceptance ledger. The one axis this factory measured and
never closed:bb auditordeclares the bar before the first edit and
bb pinpointcarries the gate into the brief, and then a session reported
"done" with nothing here able to contradict it.The vendored checker already holds the parts that are easy to get subtly wrong
— an approval binding the command, the expectation, the resolved cwd, the
shell, the timeout and the whole inheritedPATH; a definition digest, so
editing aCHECK:invalidates the evidence thatCHECK:produced; an
abandonment that exits non-zero and reports a handoff instead of dropping the
gate. What bundlebox adds is the join:bb finish initderivesGATES.mdfrom
the active brief's scope, the detected gates and the open findings in that
scope, because a ledger written from a blank page is a ledger nobody writes.bb hook stopis a fourth verification layer that executes nothing: it reports
a declared gate still unmet when a session is about to say it is done, and is
silent where there is noGATES.md. -
arc/— a Rust index compiler. The search guard asks one question per tool
call: is this name declared, and where. It answered by reading 20,000 lines of
symbols-*.mdand running a regex per line.arccompiles those tables into
one 86KB binary index — two sorted id arrays, one by lowercased name and one by
the REVERSED name, so exact, prefix and suffix are each a single binary search,
a suffix being a prefix of the reversal. 2,153 declarations, 20ms to compile.
Dependency-free, like the kernel and for the same reason: it must build on a box
with rustc, cargo and no network.per call markdown scan 1.80 ms arc index, cold 0.14 ms 13x — what a hook process pays arc index, warm 0.073 ms 25x — a CLI or MCP process spawning arc2.28 ms 1x — the process is the cost So the win is the FORMAT, not the language.
arccompiles;src/arc/read.js
reads in-process, where the 13x lands; and a test holds both readers to one
contract on every query shape, which is the only thing that keeps two
implementations equal. -
bb lathe— LATHE-1, the automation engine. Not a language model, and
nothing in its path calls one: four count-based models over artefacts this
workspace already produced. Closed, aperiodic, contiguous sequences over what
sessions ran (PrefixSpan with a projected database and BIDE's
closedness test, inbundlebox_expert/sequences.py); the expert's existing
logistic outcome model, read rather than retrained; the janitor's decayed
memory; and prefix entropy over the arc index.It emits what the workspace did by hand more than twice: tagged scripts a habit
could become (@turns,@needs, and@safe false, because the model knows
what ran and not whether running it unattended is safe), the regions the tree
already repeats, the prologue a new file in each group starts with, and the
prefixes that complete to exactly one name. Every row names the support behind
it. -
bb env— is the environment set up. That question had no answer.
bb initwrote a config file and stopped, so a fresh workspace had no tables,
no index, no findings and no page, and nothing said so — the first session
searched the tree, which is the cost this box exists to remove. Nine
artefacts, each with the verb and the declared gear that produces it, whether
it is on disk and how old. Measured on a fresh workspace: 1 of 9 after
bb init --apply, 8 of 9 after onebb env up --apply, commandcenter page
included.One row is deliberately not a gear's to build. The janitor writes the memory
files a PERSON wrote, sobb envreports where it comes from — the SessionEnd
hook, or a hand — instead of promising to run it on a tick. -
Two skills, installed by
bb wireand removed bybb unwire.bb-finish
carries the ledger discipline;antislopcarries the fifteen prose rules. A
skill is the only surface this box installs that costs nothing until it
triggers — the instructions block arrives in every system prompt and is billed
whether the session was about it or not. -
Anti-slop on the axis it never covered.
bb slophas always run over every
brief, commit message and PR body this factory emits. What it never saw was the
markdown the MODEL writes into the tree, which the next session re-reads and is
billed for again, every time, until somebody deletes it — a document is the only
artefact here that charges rent. ThePostToolUsehook now lints a prose file
the agent just wrote and names the rule, the line and the token cost. Advisory
by design: a hedge is sometimes the honest word, and a hook that rewrites
somebody's sentence unasked is worse than the sentence.
Changed
- The ranker scores specificity and path, not just match quality. Enforcing
a brief raises the cost of a bad locate, and the locator had two holes. "wire
the pre-read guard so it denies a read the pinpoint brief already quotes"
rankedslop/index.js,tokens/ledger.jsandauditor/charter.jsfirst —
each declares a symbol called exact...
v0.3.0
v0.3.0 — 2026-09-15
The release that looks at the input axis, asks whether any of the wiring was
ever used, and stops publishing a green workflow over a red job.
Two verbs came from a second read of caliper
and Chisle — the first pass took an
ablation and a losses column from them, and walked past the half of each that
mattered more. docs/prior-art.md records both rounds, including what was
deliberately not taken.
Added
-
bb sieve— the input axis. Every other verb in this box shrinks what a
session is asked to read. This one shrinks what it reads because it ran a
command. A tool result is billed when it arrives and again on every later turn,
because the whole window is re-sent, so shrinking it once is paid back every
turn that follows. Three tiers:scrub(lossless — ANSI, blank runs, repeated
lines),dedup(byte-identical to this tool's previous output in this
session),elide(head + tail, with error-looking lines carried out of the
middle).Read,EditandWriteare absent from the allowlist by
construction — their output is the text a later exact-match edit is written
against — and the elided middle spills to disk so recovery is a grep rather
than a re-run of a command that may not be idempotent. Installed by
bb wire --applyas aPostToolUsehook.Off by default, and the measurement is why.
bb sieve replaypushes real
transcripts through the same pure transform for nothing. Across four
workspaces on one box the saving ran 1.8%, 15.9%, 18.7% and 34.1% of all tool
output — the largest sample, 29 sessions, is the 1.8% — and in all four it was
almost entirely the lossyelidetier;scrubanddeduptogether never
reached 3% of the win. So there is no lossless default worth shipping, and the
lossy one buys a benefit that depends entirely on what a workspace's sessions
run. The replay reports the split by tier; the default does not guess. -
bb uptake— installed is not used.bb wire statusreports that the
instructions block, the MCP entry and the hooks are installed. The block is
billed in every window of every session whether the agent obeys it or not, so
a surface nothing reaches for is the most expensive kind of dead code this
factory can write — and nothing could see it. This reads the transcripts for
evidence that each surface was reached for, against the moment it was for:
pinpointonly against sessions that opened five or more distinct files,
tablesonly against sessions that ran a search. A file opened withsed -n
counts exactly as much as one opened withRead, because which of the two a
session uses is a harness setting and not a fact about the work. Heredoc
bodies are stripped before a command is read — without that, every document
this box writes about itself reads as a session that ran half the CLI.The three surfaces that arrive in the system prompt — the block, the
SessionStartcontext, the read guard — are reported as not observable,
with the reason, and never as 0%. A transcript cannot answer for them, and a
number meaning "nothing was checked" is the one thing this box does not print. -
docs/agents.md. Integrating bundlebox into all ten agents it knows,
wiring one it does not, and driving agents as lanes. The surfaces, the six
hooks, the nine MCP tools, thecustom_commandtokens, and what bounds a
lane.
Fixed
-
Windows CI was red and could not fail a build.
the kernel and the JS engine agree about a run step with body keysfailed on all three
windows-latestjobs for as long as the log history goes back, while
continue-on-errorkept the workflow green over it. Two defects, and the test
was catching the first correctly:- The two engines picked different shells.
src/cookbook/engine.js
hard-codedbash -lc; the kernel had already decided in
kernel/src/gate.rs::shellthat Windows gets cmd.exe, because hard-coding
bash there turned every command into a spawn error rather than a verdict.
Every GitHub Windows runner ships git-bash, so the JS engine found one and
passed while the kernel used cmd.exe and failed — one corpus, two shells. - The test was a probe of shell quoting. It built
node -p "JSON.stringify({\"screen\":…})", and bash and cmd.exe do not agree about
\". The payload now goes to a script file and the command is
node emit.js.
The shell a command runs under is one fact and now has one implementation,
exec.shellCmd, mirroring the Rust line for line. It replaced four
hard-codedbash -lcsites and one inline duplicate. Consequence, stated
rather than discovered later: a piped command on Windows now loses
set -o pipefailin the JS engine as it already did in the kernel — the
documented gap, in both engines instead of in one. - The two engines picked different shells.
-
src/bridge/index.jsrecorded a gate that never ran as a gate that
failed. Its acceptance command hard-coded bash, so on a box without one
spawnSyncreturned a null status, which read asaccepted: 0— the exact
misreadingkernel/src/gate.rswarns about in its own comment. -
bb runbook upcould not spawn on a non-Linux box. The no-cage fallback
hard-coded bash. Its otherbash -lc, the one handed tosystemd-run, is
deliberately unchanged and now says so: that branch is gated on
useSystemd(), and a box reaching it has bash by construction. -
bb failsafe run <op> --applyhad the same hard-coded shell. -
Tool results carry their tool. Every transcript reader — claude, codex,
gemini, opencode — now records the producing tool's name and id on each
result. A result was an anonymous blob before, which an allowlist cannot work
against.
Changed
-
Windows gates.
continue-on-erroris gone from the matrix. It was
best-effort while the shells disagreed; with that fixed, a Windows break is a
failed build again rather than a green workflow over a red job. Every step
afternpm test— the scan, the doc-drift check, the corpus validation — ran
on Windows for the first time in the process, because the job had never got
past the tests to reach them. -
A guard against the next hard-coded shell. The defect recurred four times
because each caller reached forbash -lcon its own and nothing was
watching. A test now walkssrc/for the literal argv form and fails on any
hit not allowlisted with a stated reason. It found thebridgesite the
moment it was written. The allowlist is checked for rot in the same test: an
entry that no longer matches is an entry permitting nothing.
Corrected
- One workspace is not a calibration. On bundlebox's own transcripts the
ESTIMATEandMEASUREDtoken figures for the sieve agreed to 0.2%, which
read as a result about the estimator. Across three other workspaces they
diverge by up to 58%.docs/prior-art.mdrecords the correction beside the
claim.
v0.2.0
v0.2.0 — 2026-09-15
The release that measures itself. A new verb reports what packing a task is
worth with both arms measured, the corpus over bundlebox actually exercises
bundlebox, and five files that had grown past the point where a session reads a
region instead of a file are split.
Added
bb bench— the ablation. The same task twice: BARE searches the tree for
its terms and opens the top--capfiles whole, PACKED is the one
bb pinpointprompt for it. Both arms are token counts over text on disk, so
neither calls a model and two runs on one tree return the same number —
asserted in CI and inbb selftest. A task that costs MORE packed than bare
is printed in the table, not dropped.bb bench initderives a suite from the
open findings that name a file;bench initandbench runare stages of the
measuregear, so the number refreshes on the same tick that folds the ledger.- The command centre reports it. Four stat cards, each tagged MEASURED or
ESTIMATE so the two cannot be read as one kind of number, and two inline-SVG
charts: bare against packed per task, and the share saved across every run. No
CDN — a chart that needs a network is a blank rectangle on the box this page
is designed for. New routesGET /healthandGET /api/bench. - An ambiguity ledger on every brief.
bb pinpointnow states what the
brief does NOT settle — no gate detected, nothing in the symbol tables
matched, no region located, no evidence on file, a scope cut to fit, a
statement too thin to locate anything — scored, with the reasons kept separate
from the score. Stated, never filled in by guessing. anti-slopdetector. Eight JS/TS patterns that discard evidence or pay a
quadratic cost: adjacent eagerfilter/map, a reducer copying its
accumulator per element, chained assertions,anyin a contract, open
dictionaries, mocked modules,Reflectescapes. One rule firing once is
infoand can never be promoted into work; three rules in one file is
medium.- A corpus over bundlebox itself. Twenty scenarios, fifty-five steps — the
CLI's dry runs, the MCP handshake, the command centre's four routes, the
bench's own determinism — committed and run in CI against a live command
centre. Coverage 96.6%. A corpus persona can now declareexcluded: a
capability it must not run, with a reason, leaves the denominator instead of
being an open gap forever. bb helpis a table. Verb, effect, what it does, grouped by the question
each group answers. Theeffectcolumn readsspends/writes --apply/
records/reads, so which two verbs can cost money is read rather than
remembered.docs/prior-art.md: seven projects read, what came back and what did not.
Fixed
bb cron installwrote a line that ran nothing. No--apply, so the
unattended worker reportedwould-runevery thirty minutes and produced
nothing indefinitely while looking installed. It carries the flag now, and
refuses to install if the gear reaches a verb that can spend — checked against
the gear spec rather than assumed. A selftest holds both halves.- The coverage matcher compared the first token, so one step running
bb scanreported all twenty-threebb *capabilities as covered and a
one-step corpus read as 100%. It matches the whole command, understands
node bin/bb.js scanas the same capability as the documentedbb scan,
treats a declared--applyas significant, and matches each segment of a
shell line. bb genesisre-seeding a corpus rewrotepersona.jsonfrom a fixed field
list and silently dropped every key not on it.bb simulatemerged findings only when a run produced some, so findings
from a run against a service that was down could never be closed by a clean
run. A clean run is the evidence that closes them.- The
simulationsframe read every run ever stored, so one bad run poisoned
simulation-error-pctpermanently. It reads the latest run per profile; the
history stays on disk forbb simulate show. - A deliberate 404 probe was reported as a phantom call against an undeclared
route. A step asserting absence is not evidence of a missing route. - World derivation extracted a formula (
projected = overhead + ...), acd
and prose lines from fenced blocks as command capabilities, each of which
stayed permanently uncovered. .gitignoreexcluded directory contents rather than directories, because git
cannot re-include a path inside an excluded directory — written the old way,
the corpus would never have shipped.
Changed
- Five files past a third of one session's payload are split along the seams
they already declared, with each entry point kept as a barrel so no caller
outside the directory changed:kernel/src/scenario.rs→
scenario/{expect,steps}.rs;src/snapgen/tables.js→
{walk,symbols,views,sources}.js;src/git/index.js→
{repo,commit,forge}.js;src/pipeline/runner.js→{facts,report}.jswith
runGearsplit intodecide()andexecute();src/genesis/index.js→
{world,packs}.js.src/commandcenter/page.jsand
src/designlabs/scaffold.jscrossed the same bar and are split too. - The workspace's runbook service is the command centre, which answers
/health;bb runbook upstarts it and the smoke profile runs against it. - CI runs the corpus, the simulation, the bench determinism check and the evals.
Measured
225 node tests, 23 expert tests, 25 selftest checks, 0 lint errors, 0 open
high-severity findings, 10 of 10 pipeline stages holding.
On this tree, twelve bench tasks: 700.3k bare against 34.6k packed. That
percentage is a property of the tree as much as of the tool, which is why
bundlebox-projects/tokenlab sweeps it across three tree sizes and three read
budgets and reports 78%–97%. What holds across all nine of those rows is the
middle column: the packed arm is constant. A pinpoint prompt costs what the task
costs; the bare arm pays for the repository.