cwright trace
cwright trace <flow.parquet> [--entity N] [--server RESOURCE:N] [--limit N]
Reads a flow-record Parquet file back and prints one entity's history or one physical server's
work log — the question a summary statistic cannot answer: why was this specific lot late, and
what was this specific machine doing while it happened. Exactly one of --entity or --server
is required.
Flags
| Flag | Meaning |
|---|---|
--entity N | Every recorded step for entity N, in the order it visited them. |
--server RESOURCE:N | Every recorded step handled by server N of resource RESOURCE, both given as their internal indices. |
--limit N | Cap the number of rows printed (default 60); the total count and an aggregate are always printed even when the output is truncated. |
⚠️⚠️ An entity id can name more than one lot, and nothing here can tell
An entity id is an entity slot plus a reuse counter, and the counter is 12 bits wide — so
after 4,095 reuses of one slot the same id is handed out again. That margin is generous for a
fab: a plant holding three thousand lots at once and releasing a hundred and seventy thousand
reuses each slot about fifty times. ⭐ It is not generous at the other end — a small model
run for a long time — and that is what an analytical target or a benchmark looks like.
Measured on an M/M/1 at 70% utilisation over 3,000,000 seconds: 2,998,093 lots released with
44 alive at the peak, and one id carrying four different lots, exactly 4,096 seconds apart.
⚠️ So on a long run of a small model, --entity N can select the rows of several different
lots, and the closing total then describes a lot that never existed. trace does not detect
this, and the plausible way to detect it does not work: "one lot visits each step once" is
false, because a rework loop revisits its steps, and the visit column counts only rework
passes at the loop head — it is reset as the lot moves on. Timestamps do not separate the cases
either, since rows are written in completion order, so a merged id's times run in order too.
⭐ How to keep lot identities unique: keep released per replication well under
4,095 × peak_live_entities, which the results document reports for every run. Shortening the
horizon works; more replications do not, because each replication runs the full horizon.
Output
ENTITY STEP VISIT RES SRV ARRIVED QUEUED SETUP PROCESS
4471 2 1 0 1 182930000 1200000 0 45000000
...
37 records: 12500000 ticks queued, 291000000 ticks processing (4.1% of the time was waiting)
Times are in ticks (see Determinism — divide by a model's
tick_scale_per_second to convert to seconds). The closing line is a quick sanity check on its
own: a high proportion of time queued relative to processing, for one entity or one server, is
usually the first sign of where a bottleneck actually is.
Getting a flow file
Flow records are written by run --flow DIR, one Parquet file per replication. See
The results document for the full schema and how to query it directly with
pandas, Polars, or DuckDB instead of trace — trace is for looking at one entity or server by
eye; the Parquet file itself is for everything else.