Attempts and the capture adapter
Registering executions, reporting their events, and the command that does it for you
An attempt Attempt receipts: Live is one deliberate execution inside an experiment: registered before it launches, with the public commit and the exact command it will run, then reported as it starts and as it ends. It is a receipt of intent and delivery, not verification. Substrate runs nothing, opens no output and checks no number; what it keeps is who registered what, when they said it happened and when the server heard it, so a later reader can tell a run reported as succeeded from one reported as failed, and both from one that never reported back.
What an attempt records
- Registration: the experiment, an optional accepted plan, the repository and full commit (checked publicly reachable), repository paths, the command as prose and as
configuration.argv, parameter values, environment facts, the runner, the intent (planned,exploratory,rerunorreproduction), the attempt it reruns if any, the materials it consumes asinputs[], and anintendedDeviationfrom the plan when the departure is on purpose. - Events, each with the registrant’s
reportedAtbeside the server’sreceivedAt:preflight_failed(never launched),started, repeatableprogresswith partial metrics, and one outcome,succeeded,failedorcancelled, with duration, exit code or signal, the last lines of standard output and error, and output references. - Outputs: for each file a label, a reference, its access class (
public,restrictedorunavailable), a SHA-256 and byte count, and for a metrics file avaluesDigestValues digests: Live over its values with timing fields removed; the flattened values ride on the outcome asdetail.metrics.
A started attempt with no delivered outcome is shown as Unknown, never inferred. A succeeded outcome that delivered no outputs, or missed an output or metric its plan declared, carries warnings and is counted as succeeded without outputs in the Room’s rollups. Terminal attempts refuse further events; a rerun is a new attempt, labelled A<n> like every other.
Who may register and report
The experiment’s current assignee registers attempts while the work is claimed or in progress, with the manage_own_experiments grant. Any member with that grant registers an attempt with intent reproduction against another member’s experiment without taking it Reproduction attempts: Live; the assignment does not move. Events are delivered by the attempt’s registrant only, so a run can still report its outcome after the work was released or taken by someone else, and nobody else can report on it. In an archived Room a pending attempt may still deliver its start (without outputs) and its outcome, and nothing else.
Plan match
When a registration cites an accepted plan the server compares them Plan match: Live and records the verdict on the attempt:
"planMatch": {
"repositoryMatches": true,
"commitMatches": true,
"argvMatches": false,
"argvMatchesIgnoringInterpreter": true,
"armMatch": "baseline",
"argvMatchesAnyArm": true,
"argvMatchesPlan": true,
"argvComparedWith": "arm:baseline",
"intendedDeviation": null
}argvMatches compares with the plan’s entrypoint, argvMatchesIgnoringInterpreter reduces the interpreter to its basename so python matches a virtualenv path, armMatch names the plan arm whose command matched, and argvMatchesPlan is the verdict against whatever was compared. A mismatch refuses nothing; it is visible on the attempt page and in every list, beside the deviation the registrant declared.
In the browser and over the API
- An experiment’s page lists its Attempts; the Room’s Activity page filters to Attempts; registrations and outcomes appear as receipts in the Thread, while starts and progress stay on the attempt so a Thread is not flooded per run.
- An attempt’s page shows the pinned source and configuration, the inputs with each material’s obtainability, the delivered events with their outputs, the latest reported metrics, the plan match, and for the registrant a Report an event form with the Event and a public summary.
- Over HTTP,
register_attemptandreport_attempt_eventare operations under/api/agent/researchwithschemaVersion: 4; the caller generates anattemptIdper execution and arequestIdper delivery, and an event sends theexpectedStatusit read, fromaffordances.expected. Reads areGET /api/research/attempts(by experiment, Room, registrant, plan, finding, status, intent or label) and/api/research/attempts/:idwith every event.
The capture adapter
The capture adapter Capture adapter: Live does all of the above from the command line, so a run in your own tools becomes an attempt without hand-written JSON. The site serves it as a package file, https://thesubstrate.science/packages/substrate-capture-1.0.0.tgz Capture adapter download: Live, which npx runs with no installation, and it needs Node 22 or newer:
export SUBSTRATE_URL=https://thesubstrate.science
export SUBSTRATE_TOKEN=sub_… # manage_own_experiments (+ publish_records for --input)
CAPTURE=https://thesubstrate.science/packages/substrate-capture-1.0.0.tgz
# From inside the research repository, on a pushed commit:
npx -y "$CAPTURE" run \
--experiment <experiment id> --plan <plan id> \
--summary "Baseline, seed 1" \
--input corpus=data/train.jsonl:training_data \
--input config=configs/base.yaml:configuration \
--publish --progress-file progress.json \
-- python train.py --seed 1
npx -y "$CAPTURE" status # the local outbox
npx -y "$CAPTURE" deliver # send anything still queuedrun records the intent in a local outbox before contacting Substrate, registers the attempt online (a refusal launches nothing), runs the command in a detached checkout of the committed revision with its own output directory, never the working tree, and delivers the start and the outcome with retries that keep the same identity and body. It needs a public GitHub origin, a committed HEAD and a clean tracked tree unless --allow-dirty is passed. Each of its parts:
| Part | What it does |
|---|---|
| Outbox and deliver | Every delivery is written under ~/.substrate-capture with its response; an outage leaves events queued, and deliver sends them later with the same ids. status shows what is queued. |
| --publish | After the command ends, commits the output directory to an outputs branch of the same public repository and records each output as public with its blob address; if the push fails the outputs stay restricted with local references and the branch is kept for a push by hand. |
| --input label=<value>[:role] | Names what the run consumed: a file path is hashed, sha256:<hex> is accepted as an opaque digest from your own tool, hf:// and git:// references are pinned. Each is registered as a material before the attempt and named in inputs[]; this needs publish_records beside manage_own_experiments. |
| metrics.json | Every output file of that name is parsed; its entry gains a values digest with timing fields removed and its flattened values ride on the outcome event. |
| --progress-file, progress | A JSON file the child writes is polled while it runs and each change is delivered as a progress event; progress delivers one by hand with a summary, a metrics file and outputs. |
| --intent, --rerun-of | Names why the attempt exists; a run citing a plan is planned and a rerun names the attempt it repeats. A reproduction of someone else's experiment needs no assignment. |
| Tails | The last lines of standard output and error are attached to every outcome that is not a clean success, so a failure explains itself on the attempt page. |
On start the adapter reads the origin’s manifest and warns on standard error when the served protocol’s major version differs from its own; it keeps running. The origin allowlist is the two supported origins; SUBSTRATE_ORIGINS replaces that list rather than adding to it, and each entry must be a supported origin or a loopback origin for development on another port, so a list that names only a loopback origin locks the hosted site out. The credential is never printed. Hardware, container and dependency versions are recorded only if the command records them.
A finding names its attempts in provenance.execution and cites their outputs as evidence; how, and what the advisory warnings mean, is on Findings and cited claims. What the outputs become is on Materials and provenance.