clawback A/B knob block (end-to-end orchestrator)
Run .skills/ab/scripts/ab_block.sh from the project root. It is the single command
that ties the validation harness together: capture (separately, once) →
this (drive both arms + analyze + plot). It runs one arm at a time:
- start a clawback proxy with the arm's knob set + its own
--statefile + a shared--turn-log(via.skills/monitor'srun_monitor.sh --detach); - drive
--turns Nof load through it (--driver pty= real claude via.skills/drive;--driver replay= HTTP fixture via.skills/replay); - stop the proxy;
- repeat for the next arm;
- analyze the shared turn-log (
.skills/bench) and render charts (.skills/plot) into the--outdirectory.
Cache hygiene WITHOUT a salt (production fidelity)
A knob A/B is only honest if it measures what production does, and
production forwards no salt. The harness used to prepend a per-arm
--cache-salt block to system to force each arm cold — but that salt
forced whole-body re-serialization and zeroed within-arm cache warming
(salted arms measured ~0% warm vs a pristine A0 at ~98%), measuring a
virtual system instead of the proxy. It is gone. Arms are
isolated three production-faithful ways instead:
- clawback-side: each arm gets its own
--statefile, so no arm RESUMES a prior arm's persisted SESSION KEYs. - Anthropic-side, byte-MUTATING arms: a treatment arm that mutates forwarded bytes (strip-ephemeral, 1h-TTL, mobile) already gets a distinct ANTHROPIC KEY, so it cannot read a prior arm's warm entries.
- Anthropic-side, byte-IDENTICAL arms: keep-alive-only forwards the
same real-turn bytes as passthrough, so per-arm
--statedoes not change the content-addressed ANTHROPIC KEY. Two production-faithful guards, neither of which deletes a turn: (a) the analyzer reports cold-starts in their own"first"gap bucket, so a carry-over-warmed open is never pooled into the warm-loop buckets it's compared within; and (b)--cooldown-secspaces serial arms past Anthropic's 5-min cache TTL so each arm's opening turn is a genuine cold-start (counted, exactly as a production user pays it after an idle), not a fake-warm read of the prior arm's cache — set~330for a byte-identical-arm headline. The analyzer's--warmupdiscard (drop each arm's first N turns) stays an opt-in diagnostic (default0), not the standing guard: dropping opening turns also deletes legitimate production cold-start cost, so the default counts them.
This is driver-independent — the proxy forwards the arm's bytes the same way regardless of how the turn was produced.
How the analyzer pairs the arms
The proxy labels every turn passthrough or treatment; this script writes
one turn-log per arm and labels it into the analyzer as knobProfile <arm>.
The analyzer pools all passthrough turns into the per-bucket baseline,
then compares each treatment knobProfile against that baseline. So:
- The block must include A0 (passthrough) for a savings % — it is the baseline every treatment arm is measured against.
- You can put several treatment arms in one block (e.g.
--arms "A0 A2 A5"): each is labeled separately and compared against the shared A0 baseline in the same run.
Usage
# short L0 no-regression smoke, baseline vs recommended stack, on Haiku:
.skills/ab/scripts/ab_block.sh --profile L0 --turns 8 --driver pty \
--model claude-haiku-4-5-20251001
# headline L2 (5-30 min gaps) block, >=200 turns/arm for a reportable %:
.skills/ab/scripts/ab_block.sh --profile L2 --turns 200 --driver pty \
--model claude-haiku-4-5-20251001 --out runs/L2-headline
# clean HTTP replay arm (needs a CLAWBACK_OAUTH_TOKEN bearer + a captured
# fixture; never an API key):
CLAWBACK_OAUTH_TOKEN=… .skills/ab/scripts/ab_block.sh --profile L2 --turns 200 \
--driver replay --fixture benchmark/fixtures/ccode.json
# keep-alive warmth test: does A1 stay warm across a 15-min idle on the
# native 5m TTL?
CLAWBACK_OAUTH_TOKEN=… .skills/ab/scripts/ab_block.sh --driver replay \
--arms "A1 A0" --gap-sec 900 --turns 5 --out runs/keepalive-15m
Arms
A0passthrough baseline (forces all knobs off)A1keep-alive onlyA21h-TTL only (nested-cache-control rewrite on — the moat)A31h-TTL + keep-aliveA4strip-ephemeral onlyA5recommended stack (keep-alive + 1h-TTL + strip-ephemeral)
Each arm's knobs are set explicitly on the proxy command, not inherited
from DEFAULTS, so the arm is the arm regardless of the operator's global
CLAWBACK.md.
Options
--profile L0|L1|L2|L3|L4— inter-turn gap regime (defaultL0).--gap-sec N— fixed inter-turn gap in seconds; overrides--profile's jittered range (forwarded to both drivers). For a precise idle, e.g. the 15-min keep-alive warmth test above.--turns N— turns per arm (default8; analyzer marks savingsinsufficientbelow 30/arm; target ≥200 for a reportable %).--arms "A0 A5"— space-separated arm list (default"A0 A5").--driver pty|replay— load driver (defaultpty).--model ID— pin claude's model for the pty driver (cost control).--fixture PATH— replay fixture (defaultbenchmark/fixtures/ccode.json).--prompts PATH— pty prompt file (defaultbenchmark/prompts/coding.txt).--settle-sec N— pty quiescence threshold (default8).--host,--port— proxy bind (default127.0.0.1:8787).--out DIR— output dir (defaultruns/ab-<timestamp>).--no-plot— skip chart rendering.
Spends real tokens
--driver pty runs the real claude binary once per arm against your own
Anthropic limits. Keep --turns small and pin --model (e.g.
claude-haiku-4-5-20251001) for plumbing / no-regression runs; reserve a
larger, default-model run for the headline numbers. --driver replay also
spends tokens but forwards a CLAWBACK_OAUTH_TOKEN bearer (never an API key)
and is cheaper to script.
Outputs
In the --out directory:
turns.<arm>.ndjson— one turn-log per arm (labeled into the analyzer as knobProfile<arm>; the passthrough arm pools into the baseline).proxy.<arm>.log— each arm's proxy stdout+stderr.state.<arm>.json— each arm's own proxy state file, so a restart never resumes the previous arm's persisted SESSION KEYs (the clawback-side carry-over guard described above).report.md,report.csv,summary.json,manifest.json— the analyzer outputs (.skills/bench).charts/— the four §9 marketing charts (.skills/plot).
Read the headline with sed -n '1,40p' <out>/report.md.
Where this sits
Capture a fixture once with .skills/capture. This skill is the runner;
.skills/{drive,replay} are the load arms it calls, .skills/bench is the
analyzer, .skills/plot is the chart renderer. To run an arm by hand
instead, see those skills.