/strictlybetter:run — ONE CYCLE
This is the loop body. Follow the numbered procedure exactly once; the Stop hook re-invokes it while the campaign is running with budget left. No improvisation: every decision below is the engine's, and every number you print is quoted from its output. You are the HYPOTHESIZE agent; every other role is a spawned agent or the engine.
Three rules that the guard hook and the ledger enforce, restated so they survive compaction: never edit files yourself during a campaign (the guard denies it), never compute a statistic (no deltas, no percentages, no "roughly"), never paraphrase the engine's numbers (quote the line).
# Resolve the engine. RUN THIS BLOCK VERBATIM — do not substitute a path you guessed.
# Order: ZCode's plugin root (ZCode also exports the legacy CLAUDE_PLUGIN_ROOT, so its own
# var is checked first), Claude Code, Codex, an explicit checkout (SB_ROOT), the Claude Code
# plugin cache (newest version), and LAST the working tree's git toplevel (a contributor
# running inside the strictlybetter checkout). First one that holds scripts/sb.py wins.
SB_PY=""
SB_CACHE="$(find "$HOME/.claude/plugins/cache" -maxdepth 5 -path '*/strictlybetter/*/scripts/sb.py' 2>/dev/null | sort | tail -n 1)"
for d in "${ZCODE_PLUGIN_ROOT:-}" "${CLAUDE_PLUGIN_ROOT:-}" "${CODEX_PLUGIN_ROOT:-}" "${SB_ROOT:-}" \
"${SB_CACHE%/scripts/sb.py}" "$(git rev-parse --show-toplevel 2>/dev/null)"; do
[ -n "$d" ] && [ -f "$d/scripts/sb.py" ] && SB_PY="$d/scripts/sb.py" && break
done
if [ -z "$SB_PY" ]; then
echo "strictlybetter: engine not found — set SB_ROOT to your strictlybetter checkout" >&2
return 2 2>/dev/null || exit 2 # FAIL CLOSED: proceeding would run `python3 ""`
fi
SB_ROOT="${SB_PY%/scripts/sb.py}"
SB_REPO="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
sb() { python3 "$SB_PY" --repo "$SB_REPO" "$@"; } # a FUNCTION, not a string: zsh does not
SB=sb # word-split "$SB"; both shells run `sb`
$SB --version | grep -q '^sb 1\.' || { echo "strictlybetter: engine did not answer (expected sb 1.x)" >&2; return 2 2>/dev/null || exit 2; } # major version only; exact patch level is not asserted
0 · Re-anchor (never trust conversational memory)
$SB status --json # the truth on disk
$SB status --json | grep -q '"campaign": null' || $SB next # the cold-start brief, when a campaign exists
(a) The brief
$SB next --json > "$SB_REPO/.strictlybetter/inbox/brief.json"; $SB next
$SB campaign show | python3 -c 'import json,sys; c=json.load(sys.stdin); print("max_parallel", c["max_parallel"], "distill_every", c["distill_every"])'
From the brief take: status, batch_size, operator_mix (a list of [operator, count]),
allowed_diff_sizes, frontier, goals, guardrails, recent_dead_ends, accepted_so_far,
archive_hints, inheritance (path or null), frozen_paths, protected_paths,
open_experiments, stop_requested, composition, and in a frontier campaign frontier_members,
parent_member, preferred_member. Note max_parallel and distill_every from campaign show.
statusis notrunning→ print the brief'shalt_reasonand stop.stop_requestedtrue orbatch_size0 → skip to (f).open_experimentsnon-empty → those are a previous cycle's unfinished ids. For each, resume at the step its$SB ledger view <id>record shows (nocommit: its worktree may hold uncommitted work, run (d) fromsubmit; hasjudge_statbut noconfirm: continue from the judge; hasconfirm: accept or discard). Finish them before pre-registering new ones.- Read the inheritance body if present (
Readthe path) and the last few dead ends. compositionisfrontier→frontier_memberslists the active members (id, commit, attempts, confirm medians per goal),parent_memberis the member the nextpreregwill branch from, andpreferred_memberis the onesb/<campaign>points at. Pass nothing extra anywhere:preregpicks the parent. Quote all three in (g).
(b) Hypothesize: write batch_size files
You write these yourself, one JSON per hypothesis, with the Write tool, at
$SB_REPO/.strictlybetter/inbox/hyp-<n>.json:
{"operator": "algorithmic", "target": "src/parse/lexer.rs",
"hypothesis": "The lexer re-scans whitespace on every token; a single skip loop should raise throughput.",
"predicted": {"bench_throughput": "+5..15%"},
"mechanism": "removes an O(n) rescan per token in the hot loop",
"expected_diff_size": "small"}
Rules: operator comes from operator_mix (spend the counts); target is a real path or
symbol; predicted names only campaign metrics, goals first; expected_diff_size is one of
allowed_diff_sizes; mechanism is one sentence a judge could check; do not repeat a
recent_dead_ends entry (same operator + target) without a new mechanism; prefer the
frontier's diagnostics and archive_hints over guesswork. A good hypothesis is small,
falsifiable, and names where the time or bytes actually go.
(c) Pre-register and spawn experimenters
For each hypothesis file, in order:
H="$SB_REPO/.strictlybetter/inbox/hyp-1.json"
$SB prereg --file "$H" # → {"id": "e0007", "worktree": "...", "base_commit": "...", "prereg_hash": "..."}
prereg writes the ledger line before any diff exists, creates the worktree from the
campaign head, and charges the budget. In a frontier campaign the worktree is rooted at the
parent member the engine picked and the output carries parent_member; never pass --parent,
that flag is the human's override. If it errors (STOP file, budget exhausted, unknown
operator, a predicted metric not in the campaign) print the message; fix the hypothesis file
only for the last two causes and retry once; otherwise skip to (f).
Then write $SB_REPO/.strictlybetter/inbox/task-<id>.json (Write tool) exactly as
skills/_shared/subagents.md shows: id, worktree, hypothesis (the hyp file's absolute
path), frozen_paths, protected_paths, commands (from profile.json), inheritance,
archive_hints. Pick the tier by operator: config|docs|test-add → sb-experimenter-low;
algorithmic|allocation|caching|bugfix|numerics|data → sb-experimenter-medium;
concurrency|model|refactor-enabling|dependency → sb-experimenter-high. Note the wall-clock
(date +%s) and spawn with exactly this task text:
Implement the strictlybetter experiment in
<abs path to task-<id>.json>. Edit only inside the worktree it names; do not run the benchmark yourself more than twice; returnDONE <id>orBLOCKED: <reason>.
Spawn up to max_parallel experimenters in one message. Wait for all returns. Record the
seconds each took.
(d) Per experiment, in return order
Work through this list for each id; stop at the first branch that ends it.
- BLOCKED or malformed return →
$SB discard <id> --reason manual:blocked. Done. - Submit →
$SB submit <id>. Output"ok": false(exit 1) →$SB discard <id> --reason integrity. Done. (Two consecutive integrity failures halt the campaign; that is the engine's call.) - Stale head check (a parallel sibling may have been accepted since prereg):
W="$($SB worktree path <id>)"; B="$(git -C "$W" rev-parse HEAD^)"; H="$($SB status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["head_commit"])')" [ "$B" = "$H" ] || echo "STALE <id>"STALE→$SB discard <id> --reason manual:stale-head --archive(the diff is archived; re-propose it on the new head next cycle if it still applies). Done. Frontier campaigns: skip this step. Each worktree is rooted at its parent member's commit, which never moves, andacceptverifies the descent itself. - Screen →
$SB measure <id> --fidelity screenthen$SB judge <id>. Read"verdict"from the judge's first line:retry-screen→ run$SB measure <id> --fidelity screenand$SB judge <id>once more, then continue with the new verdict.discard→$SB discard <id> --reason <reason> --archivewhen the judge's"improved"list is non-empty or any goal comparison line shows a positivedelta; without--archiveotherwise.<reason>is the judge's"reason"when its prefix (before:) is one ofnoise|regression|invalid; elsenoise. Done.accept-naive(confirm wall off) →$SB confirm <id>then$SB accept <id>. Done.promote→ continue.
- Blind judge →
P="$($SB judge-payload <id>)", spawnsb-judge(strictlybetter:sb-judge) with only the payload path in the task text ("Judge the strictlybetter experiment described in<path>. Return only the verdict JSON."), write its four-key JSON to$SB_REPO/.strictlybetter/inbox/verdict-<id>.jsonwith the Write tool, then$SB judge-verdict <id> --file "$SB_REPO/.strictlybetter/inbox/verdict-<id>.json".gamed→$SB discard <id> --reason gamed. Done.cleanorsuspicious→ continue (the engine adds repeats forsuspicious).
- Confirm →
$SB confirm <id>. Read"verdict"from its first line. - Cost (before the final verb, so the bandit sees it) →
$SB cost <id> --wall-s <experimenter seconds> --tier <low|medium|high>. Tokens are unknown on this platform; the dollars column is an estimate from zero tokens, and the cycle summary says "estimated". - Final verb → confirm
accept→$SB accept <id>(fast-forwards the campaign branch, ratchets the baseline). Ifacceptreports "not a fast-forward", treat as STALE in step 3. Confirmdiscard→$SB discard <id> --reason <reason> --archivewith<reason>the confirm line's"reason"when its prefix is in the fixed vocabulary, elsenoise. In a frontier campaignacceptadds a member instead of fast-forwarding; its output hasmember,parent_member,retired,preferred_member. Quote them in (g). A confirm reasondominated:<member>is not in the discard vocabulary: discard with--reason dominated --archive(the ledger'sconfirmevent keeps the dominated reason). In a campaign withaudits(a proxy ladder, docs/15)acceptmay first run the real instrument: at the first accept and then everyevery_acceptsaccepts, blocking for as long aspairspairs of it take (hours). When it did,acceptprints one line before its own, and you quote it verbatim in (g):{"audit": {"recall": {"verdict": "direction", "p": 0.125, "n_pairs": 3, "median_improvement": 0.012}}, "wall_s": 43210.5}verdictis one ofconfirmed | direction | no-change | worse | invalid;directionmeans every pair improved but the pairs cannot reach alpha, and it is not a significance claim. Adiscardin such a campaign may print, before its own line, the sampled-audit line, which means the engine ran one pair of the real instrument on the discarded commit:
Quote it in (g) too. Neither line changes what you do next.{"discard_audit": "sampled", "id": "e0007"}
Judge payload (step 5): the engine composes it, so no transcript text can leak into it:
P="$($SB judge-payload <id>)" # writes .strictlybetter/inbox/judge-<id>.json and prints the path
The file holds the diff, the pre-registration, the screen comparisons, the affected cards'
gaming_risks, the frozen paths, and the checklist path. Hand the judge that path and nothing else.
(e) Cost is recorded per experiment
Step 7 above, every experiment, including discards. Nothing else to do here.
(f) Distill on schedule
$SB distill-stats --json | tee "$SB_REPO/.strictlybetter/inbox/stats.json" | python3 -c 'import json,sys; s=json.load(sys.stdin); print("decision", s["decision"], "| experiments", s["experiments"], "accepted", s["accepted"], "| false-promotion window", s["false_promotion_rate_window"], "| budget left", s["budget_left"])'
decisionstarts withstop→$SB report, then$SB campaign endunless$SB status --jsonsayshalted(a halt is for a human; ending it hides the reason). Print the report path.strictlybetter/reports/<campaign>.md. This is gate 2. Stop.Otherwise, if
experiments % distill_every == 0, oracceptedchanged during this cycle: spawnsb-distiller(strictlybetter:sb-distiller) with this task text:Distill the strictlybetter ledger at
<SB_REPO>/.strictlybetter/ledger.jsonlinto an inheritance body. Current body:<SB_REPO>/.strictlybetter/inheritance.md(may be missing). Stats:<SB_REPO>/.strictlybetter/inbox/stats.json. Template:<SB_ROOT>/templates/inheritance.md.tmpl. Write the new body to<SB_REPO>/.strictlybetter/inbox/inheritance.md. ReturnDONE <path>.then
$SB inheritance write --file "$SB_REPO/.strictlybetter/inbox/inheritance.md".explore:levelN→ nothing extra; the next brief widensallowed_diff_sizesand the mix.
(g) Cycle summary (three lines, four in a frontier campaign or a proxy-ladder campaign, quoted numbers)
cycle: <n> pre-registered, <k> accepted (<ids>), <m> discarded (<id: reason>, …), <b> blocked
frontier: <goal> best=<value> sigma=<sigma> (from `$SB next`)
frontier members: <n> active (<ids>) · preferred <id> · next parent <id> (frontier campaigns only, from `$SB next`)
audit: <the `audit` line `accept` printed, verbatim> · discard audits sampled: <ids from the `discard_audit` lines, or none> (campaigns with `audits` only, when either line appeared this cycle)
next: <decision from distill-stats> · budget left <…> · wall <…>s · dollars estimated $<…>
Then stop. The Stop hook decides whether another cycle runs.
Errors you may see, and what they mean
sb: campaign status is 'halted' (…): the engine stopped itself (integrity twice, gamed twice, holdout gap, budget, baseline invalid). Print the reason; do not resume it yourself.sb: <id> is not promoted/has no blind-judge verdict/was judged gamed: you skipped a step; go back to it.strictlybetter guard: denied edit …: you or an agent tried to edit outside a worktree, a frozen path, or harness state. Do not retry the edit; the experiment isBLOCKED.sb: hypothesis missing 'predicted'/unknown operator: fix the hypothesis file andpreregagain; nothing was charged.