Twinned from https://github.com/cfsh/ninefold/issues/724 by tools/beads/import_github.py's reconcile pass.
Filed unclaimed and unlabelled from a strategic-uplift window, working #702. Backlog.
~/.ninefold-reviewer/dispatch.log is the only durable record of what the reviewer does, and it records only scheduled ticks. A run started by review_now.py shells out to dispatch.py directly, so its stdout goes to the caller's terminal and nothing is appended to dispatch.log. Measured on the sandbox box, 2026-08-07:
| | count |
|---|---|
| transcripts in logs/ on disk | 184 |
| distinct transcript paths named anywhere in dispatch.log | 91 |
| occurrences of on-demand in dispatch.log | 0 |
So roughly half of all reviews leave no trace in the file every cost and cadence question gets answered from.
#678 measured Tier-0 review cost — *"$69.66 for one chunk, 54 full reviews and 0 restamps in a day"* — from dispatch.log. If on-demand runs are absent from that file, that figure undercounts by roughly the on-demand share. At the split above it is closer to double. I am not claiming #678's number is wrong as computed; I am claiming its denominator is not "reviews", it is "scheduled reviews", and nothing says so.
⚠️ #702 makes this structural rather than incidental. Before it, on-demand was a convenience and the cron did most of the work. After it, the dev pulling a review is the *primary* path and the cron is a backstop — so the fraction dispatch.log cannot see goes from "a lot" to "most", and the reviewer's own record of itself degrades exactly as it becomes the thing worth measuring.
To get the split for #702 I cross-referenced transcript filenames on disk against the paths dispatch.log names, then filtered by file size (>100 KB = a real review, <1 KB = an aborted spawn). That works today and is fragile in two ways:
logs/ retaining everything, which #483 proposes to change — a retention policy would silently destroy the only way to reconstruct on-demand history;- Cheapest: have dispatch.py append its per-PR outcome line to dispatch.log on --on-demand too, tagged so the two are distinguishable (#N: done (on-demand) — …). ⚠️ It must not stamp the heartbeat — review_now.py's guardrail 2 exists precisely because on-demand runs holding the heartbeat green would mask a dead cron (#352). Writing a log line and stamping a heartbeat are separate acts and only the second is dangerous.
⚠️ Interacts with #483: poll.py reads this file's *mtime* as the dispatcher-liveness signal, so adding a second class of writer means a dead cron could read as warm whenever any session pulls a review. That is the #462 detection this would break, and it is the reason this is not a one-liner.
- Better: a small append-only ledger separate from dispatch.log (one JSON line per review: sha, result, trigger, on-demand or tick, duration), which sidesteps the mtime problem entirely because nothing reads *its* mtime for liveness.
Related: #483 (log growth and retention), #678 (the cost measurement this undercounts), #576/#578 (where on-demand came from), #702 (which makes it the primary path).
None.
No comments.