--describe
The headless, browser-free text summary of a run -- the surface an agent or a CI gate reads to verify a reactor without rendering pixels.
--describe
The viewer is for a human at a browser. --describe is for everyone else -- a terminal, an agent, a CI gate. It prints a full text summary of the same replayed run and exits. No server, no browser, no key.
reactor-devtools <state-dir> --describeBecause the primary consumer of the Reactor is often an agent, this is a first-class surface, not an afterthought: it is the text an agent reads to sanity-check a run -- or to assert on it -- without watching a video.
The output
This is the canonical masked-relay sample (the documented default --example). Its first line is the honest frame: a synthetic sample ledger, so the token magnitudes are illustrative, not a measured bill -- the checkable part is the structural shape (dispositions, surprise counts, and chain-verify).
reactor-devtools --describe
(synthetic sample ledger -- token counts are illustrative, not a bill)
state-dir ./fixtures/masked-relay
topology yes · 12 nodes · 23 edges · acyclic=true
receipts 77 frames
dispositions rendered=46 · skipped=31 · failed=0
surprise-cause external=8 · input=69 (a.k.a. wake-cause)
COST ROLLUP (tokens)
total fresh=27180 tokens · reused=12840 tokens · reuse=32%
input receipts=69 fresh=26100 tokens reused=12000 tokens
external receipts=8 fresh=1080 tokens reused=840 tokens
peak fresh 1620 tokens at frame 58 (viewport-masker)
PER-NODE
signal-inbox r=3 s=1 f=0 fresh=1080 tokens chain✓
viewport-masker r=3 s=6 f=0 fresh=3240 tokens chain✓
insight-synthesizer r=6 s=15 f=0 fresh=6840 tokens chain✓
diversity-auditor r=6 s=3 f=0 fresh=1080 tokens chain✓
...
CHAIN-VERIFY ok -- meaning-layer chain-consistency
(each receipt's content_hash matches its canonical payload and links its
prev -- NOT a cryptographic signature. v1 has a null signer, so this is
tamper-EVIDENT against accidental / independent edits, NOT against a forge.)
FRAMES (frame node status moved[output facets that changed] fresh tokens woke[…])
0 signal-inbox rendered moved[@atomic,inbox] fresh 0 tokens woke[signal-inbox]
6 viewport-masker rendered moved[@atomic,view_e1,view_e2] fresh 540 tokens woke[expander-1,expander-2,...]
8 viewport-masker skipped moved[--] fresh 0 tokens woke[--]
...What each block tells you
| Block | What it answers |
|---|---|
| header | How big is the run? Is the topology present and acyclic? How many receipts? |
| dispositions | How much actually re-rendered vs memo-skipped vs failed. A high skipped count is a healthy, quiet system. |
| wake-cause | Why nodes woke -- external (a signal arrived), input (an upstream moved), self (the audit floor). |
| COST ROLLUP | Fresh vs reused tokens, the reuse %, and the same split by cause. peak fresh names the single most expensive frame -- the biggest surprise. |
| PER-NODE | Per node: r/s/f counts, total fresh tokens, and a chain✓ badge (its receipt chain is prev-linked and consistent). |
| CHAIN-VERIFY | The global tamper check -- every node's chain verified against the content-addressed prev links. |
| FRAMES | One line per receipt: frame · node · status · moved[...] · fresh · woke[...]. The whole timeline, greppable. |
Reading memoization off the trace
The FRAMES block is where "cost scales with surprise" stops being a slogan. In the example above:
6 viewport-masker rendered moved[@atomic,view_e1,view_e2] fresh 540 tokens woke[expander-1,expander-2,...]
8 viewport-masker skipped moved[--] fresh 0 tokens woke[--]
10 viewport-masker skipped moved[--] fresh 0 tokens woke[--]Frame 6 renders and wakes its subscribers; frames 8 and 10 are byte-identical re-wakes that memo-skip at zero fresh cost and wake nothing. The skip is not a missing log line -- it is a recorded receipt proving the reactor was asked to re-check and correctly decided nothing moved.
Use it in CI
--describe is deterministic, so a state-dir checked into a repo is a fixture you can assert against without a browser or a key. Grep the output to gate behavior -- for example, that a quiet stretch stays free, that exactly one node failed, or that a selective wake stayed selective:
reactor-devtools fixtures/contract-redline --describe > out.txt
# a single-section edit must not re-render sibling sections:
grep -E "Summarize §[0-9]+ +r=1 " out.txt # untouched summarizers rendered once (cold boot) only
# the chain must verify:
grep -q "CHAIN-VERIFY ok" out.txt || exit 1This is how the demo-suite fixtures lock their invariants (see Recording) -- the same trace a reviewer reads is the trace CI asserts on.