A sweep that takes an hour and is only looked at afterwards is an hour spent finding out the sample drifted at row 12. The point of watching a measurement is to find out early — and the point of the design below is that watching costs the measurement nothing.
Three separate views, one run, no coordination between them. That is the whole of what the design below buys, and the rest of this page is why it costs the measurement nothing.
Three pieces
| Answers | Who writes it | |
|---|---|---|
| Source | What has appeared since I last asked? | The library |
| Monitor | What should be drawn for this row? | You — the only part that knows what the measurement means |
| Renderer | Where does it go? | The library — a window, a video, or nothing at all in a test |
Splitting it three ways is what lets one view work everywhere. A monitor never mentions a toolkit, so the same class runs in a window, in a test, or in a script rendering an MP4 on a headless machine. And because it is fed rows rather than reading files, it does not care whether they arrive live or are read back from a run that finished last week.
A monitor declares the channels it consumes, declares what it draws, and projects one onto the other. That is the whole obligation — a schema and two methods. Everything else is base machinery.
The record file is the stream
There is no subscription to set up, no port, and no callback the experiment
has to register. The run is already writing records.jsonl, one line per
completed step, and a line appears only once its step is fully persisted. A
source tails that file by byte offset.
Three consequences follow, and together they are the reason this design was chosen over a broadcast:
- Attach whenever you like. A source opened halfway through a run reads the file from the beginning, so its first poll delivers every row already written. Nothing that happened before you looked is lost.
- Watching costs the run nothing. The source only reads. The experiment does not know it is being watched, keeps no connections, serializes nothing, and behaves identically whether ten views are open or none.
- A finished run is watched exactly like a live one. Same file, same code. A finished measurement is a live one that stopped arriving.
A broadcast design would fail all three: a late subscriber misses what was sent before it connected, the producer does work per subscriber, and replaying a stored run becomes a second code path.
The mapper is where the experiment lives
A record carries everything its step measured; a view declares the few
channels it draws. Something has to project one onto the other, and that
something is a mapper — a plain function handed one record, returning the
channels a monitor declared, or None to drop the row.
The library refuses a frame carrying channels the monitor never declared, rather than quietly keeping the ones it recognises. That strictness is the point: a renamed column becomes an error on the first row instead of a plot that keeps drawing, half-empty, until someone notices during analysis.
Keeping the mapper outside the monitor is also what makes views reusable. A
spectrum view that declares wavelength and counts serves any spectroscopy
bench; only the mapper knows that this experiment calls its sweep axis
hwp_deg. Views general enough to outgrow one experiment live in
plesty-common-monitors
— worth looking at before writing a new one.
Read next
- The Monitor — write one, against a real run, in about twenty minutes.
- The Viewer — three views docked in one window, and the same run rendered to a video on a headless machine.
- Runs & Data — the file the views are reading, and why a line in it is a commit.