Run one A/B build loop: dispatch prompt variant A and prompt variant B to the local LLM, blind-judge the pair on the 12-dim rubric, and persist every loop artefact locally (memory-layer degrades to a replayable backfill file). This is the invokable form of the A/B backbone.
Slash-command source for bin/ab-loop.mjs. It becomes /review-ab once symlinked into
~/.claude/commands/; nothing in this repository creates that link (the convention internal-d's
own command corpus uses — see ~/.claude/CLAUDE.md's @fn: resolution rule). The templates it loads live in spine's prompts/ directory, named by CW_AB_PROMPTS_DIR; this
checkout holds no copy.
Usage: /review-ab <fileA> <fileB> [question]
Migrated from internal-d 2026-09-01 — bin/ab-loop.mjs now lives in this repo, resolves its LLM
endpoint from manifests/llm-hosts.json (the same declaration admin/ and monitor/ use, not a
hardcoded port), and persists through lib/memory-layer-client.mjs's upsert/verifyReceipt (three-
state receipts, redaction gate) instead of a raw POST. @fn: primitive resolution is unchanged —
$HOME/.claude/_functions/{p0,p1} was always a user-scope convention, not a internal-d-repo one.
What to do:
- Write the two prompt variants to files (or take the paths the user gave).
- Run:
node bin/ab-loop.mjs \ --a <fileA> --b <fileB> \ --question "<the comparison question>" \ --label <short-slug> [--tool <template>] [--parent latest] [--double-judge]--tool <name>loads$CW_AB_PROMPTS_DIR/<name>.mdas the template and injects each file at{{context}}(with recursive@fn:resolution from~/.claude/_functions/p0thenp1).- Omit
--toolfor raw prompt-vs-prompt. - Omit
--bfor single-variant mode (baseline run, no judge). --parent latestchains this loop to the previous run automatically (reads the runs dirLATESTpointer); pass an explicit runId to chain elsewhere.--double-judgere-judges with presentation order swapped and flags self-disagreement.--llm-urloverrides the resolved default host;--runs-diroverridesreports/ab-runs/.- Unknown flags are REJECTED with a human-language report and a did-you-mean — never silently ignored. If the runner refuses, relay its message verbatim and fix the flag; do not retry blindly.
- Read
reports/ab-runs/<runId>/report.mdandrun.json. Relay the winner, the key trade-offs, the warnings (truncation, reasoning-exhaustion retries, judge parse failures, position inconsistency), and the "Next loop" guidance.reports/ab-runs/index.jsonholds the cross-run trajectory;LATESTnames the newest run.reports/is gitignored — these are generated artefacts, not durable records. - Report
run.json'sinternalCblock: writes / verified / degraded. Any degraded write has its exact payload inreports/ab-runs/<runId>/memory-layer-backfill.jsonfor later replay. - Audit before trusting: skim
judge.md— if scores lack cited observations or the delta table is malformed, treat the verdict as unparsed and audit the pair yourself.judge-reasoning.md(thinking models) shows how the verdict was reached.
Exit codes: 0 loop complete · 1 config/variant failure (readable reason on stderr) · 2 variants ok, judge failed.
$ARGUMENTS