crucible
An experiment harness for systematic strategies. It runs champion/challenger experiments on a trading-day axis, writes one machine-readable manifest per run, and grades every arm every cycle against a control it can fail.
Crucible is the harness, not the trader. The trader reads one contract — champions/{slot}/current.json and predictions/{tradingday}.json — and lives in its own repository. The harness must complete every acceptance test with the trader switched off, and the trader must run a week on a frozen champion with the harness switched off. That mutual independence is what makes the harness the durable product.
The one-command promise
Every job is one command, with identical behaviour on a laptop, in a Lambda and on a spot instance. The scheduler does not know anything the CLI does not.
crucible experiment.run --slot r --arm <name-or-arm-id> --date 2026-09-01Exit 0 means a verdict exists. There is no third outcome: a run either writes a complete manifest with status: ok, or it is failed with a mandatory reason and it pages. partial, skipped, degraded and unknown are not representable — the manifest schema forbids them.
Jobs
| Job | What it does | |---|---| | data.daily | Compile one trading day of market and fundamental data | | data.weekly | Weekly refresh and coverage pass | | data.heal | Repair a named gap in the store, idempotently | | experiment.new | Register an immutable arm from a recipe spec | | experiment.run | Score one arm for one trading day | | experiment.grade | Run one slot's arena cycle: ladder, pairings, pointer | | promote | Move a slot's champion pointer, evidence-gated | | report | Reduce the week's manifests into the attribution table | | explain | Walk a runid or verdict back to everything that produced it | | migrate.history | Import v1 arm history, flagged with its provenance | | release.pin | Repoint a release, or pin the trader to one | | smoke | A real end-to-end run that gates a release flip |
Two rules that are not negotiable
Every key is a trading day. Artifact keys, manifest fields, ladder rungs, windows and horizons are NYSE trading days from krepis.tradingcalendar. A run launched on a Saturday binds to Friday's close. calendardate is recorded beside it for provenance and is never used as a key. Horizons are 21 / 63 / 126 / 252 trading days — never "1 month". A contract test walks the store and fails on any non-trading-day key.
Every run writes a manifest, including the ones that die. The runner writes runs/{job}/{tradingday}/run.json in a try/finally, so an exception produces status: failed with its reason, its spend and its lineage before the exception is re-raised. A run that produced no manifest is an absence, and absence is one of the two conditions that page.
Runbook
Six verbs. Each is one command; none needs a console. Any job that must run in-region (heal, a replay week) is dispatched to a box rather than run from a laptop. The dispatcher's function name is deployment configuration, not part of the framework, so it is read from the environment rather than published here (alpha-engine-config-I10156):
aws lambda invoke --function-name "$CRUCIBLE_DISPATCHER_FUNCTION" --payload '{"job": "<job>", "args": "<cli args>"}' out.jsonadd an experiment
An arm is one recipe file in the strategy tree, registered before it can score anything:
crucible experiment.new --slot r --arm <name> --run-mode live