Snapshot the bench, analyze forever¶
Live data ages out of the agent; a .trz file is the permanent record. This notebook exports the last few minutes of the bench, re-opens the file, and runs the same call against it — then puts two recordings side by side on one relative clock, the natural way to compare runs.
You'll use: export() with per-producer results · trace() · "-60s" and "start" / "+30s" bounds · trace.check · traces() and series() for run-vs-run
Run it¶
Copy the notebook below into trace-roundtrip.md, then:
The notebook¶
---
description: Export live data to a .trz file and analyze the frozen copy the same way.
requires-python: ">=3.10"
dependencies:
- zelos-sdk[notebook]
params:
window: "-2m"
---
Live data ages out; a `.trz` file is forever. This notebook captures the last
few minutes of the bench into a trace file, then re-opens it and runs the
*same* query against the frozen copy — the analysis you write against live
data works unchanged against the archive.
## Capture
`export()` writes the file on the agent's host and reports per producer, so
one failing source can't silently hollow out the archive. The run id in the
name keeps two runs of this notebook from overwriting each other's evidence:
```python
from uuid import uuid4
from zelos_sdk import connect
agent = connect()
run_id = uuid4().hex[:8]
result = agent.export(f"/tmp/zelos-bench-{run_id}.trz", start=params.window)
result
```
## Re-open and orient
```python
trace = result.open()
trace
```
```python
trace.signals().match("bus0/BMS_message/cells.*")
```
## Query the archive like it's live
The same call, word for word — `query`, `at`, `window`, `check` and `export`
all read the same on a `Trace` as they do on the `Agent`:
```python
frame = trace.query("bus0/BMS_message/cells.*", start="-60s")
frame
```
On a file, `"-60s"` counts back from **the end of the recording**, not from
the wall clock — the file is the clock. To read from the other end, `"start"`
anchors at the beginning and `"+30s"` offsets from it, which is how you say
"the first thirty seconds of the run":
```python
trace.query("bus0/BMS_message/cells.cell_0", start="start", end="+30s")
```
## Check the archive
Checks work on a trace exactly as they do live — same call, same result:
```python
trace.check.that(frame["bus0/BMS_message/cells.cell_0"], ">", 3.0)
```
## Comparing runs
Open several archives together under labels of your choosing. Each label is a
*run*, and offset bounds slice every run from its own start, so the rows line
up by seconds-into-test rather than by wall clock:
```python
recent = agent.export(f"/tmp/zelos-bench-{run_id}-recent.trz", start="-1m")
runs = agent.traces({"baseline": result.path, "candidate": recent.path})
runs
```
A set frame carries one column per (run, signal) under neutral labels;
`series()` names them, one `SignalSeries` per run for the path you ask about:
```python
compared = runs.query("bus0/BMS_message/cells.cell_3", start="start", end="+30s")
by_run = runs.series(compared, "bus0/BMS_message/cells.cell_3")
by_run
```
Two runs on one clock subtract, and the difference is the comparison — one
line, zero where the runs agree:
```python
delta = by_run["candidate"].rename("candidate") - by_run["baseline"].rename("baseline")
delta.plot()
```
```python
runs.close()
trace.close()
```
Ship the `.trz` with the bug report, attach it to the test record, or hand it
to a teammate — whoever opens it gets the exact bytes the bench produced,
queryable with the notebook they already have.
What to notice¶
- The analysis API is identical live and archived.
query,at,window,checkandexportare the same calls on anAgent, aTrace, and aTraceSet. Write the notebook once; point it at either. See Open trace files. - On a file, time is measured from the file.
"-60s"means the last minute of the recording;"start"and"+30s"count from its beginning. The wall clock never enters into it. See Time on a trace. ExportResultis per-producer — one failed source is reported, not hidden, so an archive can't silently miss a bus.result.open()hands you theTracewithout repeating the path.- A
TraceSetis labeled runs on a shared clock. Two recordings of different lengths still line up sample-for-sample from their own starts, which is what makes a run-vs-run table and chart mean something. - The file lands on the agent's filesystem — ship it with the bug report and the recipient queries the exact bytes your bench produced.