OpenProse
Reactor DevTools

--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> --describe

Because 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

BlockWhat it answers
headerHow big is the run? Is the topology present and acyclic? How many receipts?
dispositionsHow much actually re-rendered vs memo-skipped vs failed. A high skipped count is a healthy, quiet system.
wake-causeWhy nodes woke -- external (a signal arrived), input (an upstream moved), self (the audit floor).
COST ROLLUPFresh vs reused tokens, the reuse %, and the same split by cause. peak fresh names the single most expensive frame -- the biggest surprise.
PER-NODEPer node: r/s/f counts, total fresh tokens, and a chain✓ badge (its receipt chain is prev-linked and consistent).
CHAIN-VERIFYThe global tamper check -- every node's chain verified against the content-addressed prev links.
FRAMESOne 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 1

This is how the demo-suite fixtures lock their invariants (see Recording) -- the same trace a reviewer reads is the trace CI asserts on.

On this page