Run Lifecycle
A run is one recording session. stax-server tracks every run it has
hosted and enforces one simple rule. This page covers that rule and the
commands that observe, select, save, and control runs.
One active run at a time
stax-server rejects a second start_run while one is in flight. If
stax record reports another run is already active, you have two
choices:
stax wait # block until the active run stops on its own
stax stop # ask the server to stop it now This is deliberate: the live aggregator holds one run's data at a time, and
a single active run keeps stax top / stax flame unambiguous about which
run they describe.
Which run do queries see?
The query commands — top, flame, threads, annotate — operate on the
daemon's current query state. While a recording is active, that state is
the active run. After a run stops, its snapshot stays selected. stax open
selects an archive, and stax select-run <ID> selects a stopped in-memory run
from stax list. So:
stax record -- ./bench # run 1 — aggregator now holds run 1
stax wait --for-samples 5000
stax top # snapshot of run 1
stax stop # run 1 stops; aggregator keeps run 1
stax top # still works — same data
stax record -- ./bench # run 2 — current query state switches to run 2
stax stop
stax select-run 1 # restore run 1 into the current query state
stax top --run 1 # one-off query of run 1; current state unchanged In-memory run history does not survive a daemon restart. To keep a run beyond the current server process, save it:
stax stop
stax save /tmp/stax-demo.staxdir
stax open /tmp/stax-demo.staxdirstax open loads the saved run back into the current query state. For
stopped in-memory history, top, flame, threads, annotate, and
diagnose also accept --run <ID> for a non-mutating one-off query. It has
the same server-memory lifetime as select-run: save anything that must
survive a daemon restart.
stax status
A snapshot of the daemon: the active run if there is one, plus when the daemon itself started.
stax statusactive run:
run 1 [recording] pid 12345 4824 samples / 119 intervals (./bench)stax list
Every run the daemon has hosted — active and finished, oldest first. History
is server-memory history; it does not survive a daemon restart unless you save
the current queryable run with stax save. Use
stax select-run to make an older stopped row current
again before querying it.
stax list run 1 [stopped] pid 11000 9421 samples / 244 intervals (./bench)
run 2 [recording] pid 12345 4824 samples / 119 intervals (./bench)stax wait
Block until something happens. With no flags, it waits for the active run to
reach Stopped. With a condition flag, it returns as soon as that condition
fires.
stax wait --for-samples 5000 --timeout-ms 10000condition met:
run 2 [recording] pid 12345 5012 samples / 124 intervals (./bench)| flag | meaning |
|---|---|
| (none) | wait for the active run to stop |
--for-samples <N> | return after at least N PET samples have been ingested |
--for-seconds <N> | return after N seconds of wall-clock time |
--until-symbol <S> | return once a symbol containing substring S is seen (case-sensitive) |
--timeout-ms <MS> | hard cap on the whole wait |
The first three are mutually exclusive — pass at most one. --timeout-ms
is independent and can be combined with any of them.
stax wait is the backbone of scripted and agent-driven profiling: start a
recording in the background, wait until there's enough data, then query.
Exit codes
| code | situation |
|---|---|
0 | condition met, or the run reached Stopped cleanly |
1 | timed out, or no active run, or any other error |
A timeout prints timed out and exits 1, so stax wait … || handle-it
works as expected in a script.
stax stop
Ask the daemon to stop the active run cleanly. It prints the final summary.
stax stopstopped:
run 2 [stopped] pid 12345 5012 samples / 124 intervals (./bench)stax stop exits non-zero if there is no active run. Stopping a run does not
discard its data — the aggregator stays queryable until the next recording
(see above).
stax save
Write the current or most recent queryable run to an archive.
stax save /tmp/stax-demo.staxdir
stax save /tmp/stax-demo.staxPaths ending in .stax create a single-file facet-json package. Other paths
create the v2 directory layout: manifest.json plus typed facet-json chunks
(aggregator.json, binaries.json, and target-ingest.json) plus an
append-friendly events.jsonl sidecar; copied text bytes live under blobs/.
The manifest/package records archive version, save time, producer/version,
OS/arch, and run summaries. The chunks or package store raw aggregator
streams, binary/symbol metadata, target-ingest diagnostics, typed
SavedEventLogEntry records, and any code-byte blobs needed by annotate.
New readers replay those records when present and keep the aggregate
chunks/package members as a compatibility and inspection path. The archive
preserves target spans and origin links for later threads, top,
flame, and diagnose queries.
stax save works while a run is active, and after stax stop, until the
next stax record resets the live aggregator.
Archive compatibility is strict in the current format: stax open and
stax compare accept v2 directory archives, .stax packages, and legacy v1
archive.json archives, and reject other versions loudly. Treat saved
archives as developer/regression artifacts for the matching stax format until
a migration policy lands.
stax open
Load a saved run archive into the daemon's current query state.
stax open /tmp/stax-demo.staxdir
stax threads -n 0
stax top -n 20
stax flame --threshold-pct 0stax open accepts an archive directory, a .stax package, the v2
manifest.json inside a directory archive, or a legacy v1 archive.json. It
replays v2 events.jsonl or embedded package events when present; legacy and
minimal archives fall back to aggregate chunks. It refuses to replace state
while a recording is active; stop the active run first.
stax select-run
Restore a stopped in-memory run from stax list into the daemon's current
query state.
stax list
stax select-run 1
stax top -n 20
stax flame --threshold-pct 0
stax diagnose --run 1select-run refuses to replace state while a recording is active. It is a
server-memory selector, not persistence: after a daemon restart, use
stax open on a saved archive instead.
For one-off inspection, stax threads --run 1, stax top --run 1,
stax flame --run 1, stax annotate --run 1 …, and
stax diagnose --run 1 query the stopped run without changing the current
query state.
stax compare
Compare two saved archives without loading either one into stax-server.
stax compare /tmp/before.staxdir /tmp/after.staxdir
stax compare --json /tmp/before.staxdir /tmp/after.staxdir > /tmp/stax-compare.json
stax compare \
--fail-target-delta-ms 25 \
--fail-target-delta-pct 10 \
--fail-unlinked-origins-delta 0 \
/tmp/before.staxdir /tmp/after.staxdirThe command reads each archive directly (directory, .stax package, v2
manifest path, or legacy v1 archive.json) and prints deltas for PET samples,
on/off-CPU interval time, target time, target span counts, origin-link counts,
ingest drops, and the top target lanes by duration. V2 inputs use the same
event-replay preference as stax open. The default is a human table; --json
prints a facet-json report with named baseline/candidate/delta fields for
scripts and benchmark notes.
Threshold flags make the command fail directly when a candidate regresses
past your limit, and JSON output includes the same decision in
threshold_failures.
Legacy v1 archive.json inputs are accepted too. Use it for quick
before/after checks and regression notes; use stax open when you want to
inspect one archive through threads, top, flame, diagnose, or the web
UI.
For a live persistence smoke with the blessed target-span corpus:
just archive-smokeThat recipe starts a checkout-local stax-server on a temporary socket,
records stax-target/examples/corpus.rs with the checkout CLI, saves the run
to both a temporary archive directory and a .stax package, reopens both
forms, exercises stax select-run and non-mutating per-command --run,
queries the restored run through threads/top/flame/diagnose, and runs
text plus JSON stax compare across the saved artifacts.
The focused target-span workflow runs the static verifier on normal PRs. The
same workflow also runs the archive and web smokes on the
bearcove-ubuntu-24.04 runner, which can manage local stax servers,
recording, Vite, and Playwright. Manual workflow_dispatch keeps the
live_smokes switch for explicit reruns.
Putting it together
stax record -- ./bench & # background the recording
stax wait --for-samples 10000 \
--timeout-ms 60000 || { # bail if it never gets there
echo "not enough samples in 60s" ; stax stop; exit 1
}
stax top -n 20
stax stop
stax save /tmp/stax-demo.staxdir