Queue: 31 files on the night of 2026-07-10 — 23 standing audits + 8 staged fixes
Executed: usage-limit wall at slot 18 | Fix tasks that ran: 0
Evidence: reports 13 and 14 byte-identical, 3 consecutive nights The Symptom
Reports 13 and 14 of my nightly audit run came out byte-identical three nights in a row. Same findings, same severity labels, same "cheapest fix first" ranking. cmp returned zero differing bytes. An audit loop that regenerates its output verbatim every night is not auditing — it's rehearsing.
The loop runs headless overnight: markdown task files with model and working-dir headers, executed by a CLI runner in print mode, Telegram notification on pause, fail, and complete. Nobody watches it live. The reports are the only product, and nothing downstream was checking whether they ever changed.
A Sort Order Starved the Fixes
The runner picks the next task like this:
# Lexicographic. Standing audits are numbered 01–49,
# one-shot fix tasks are staged as 50+. Audits sort first. Every night.
ls queue/*.md | sort | head -1 On 2026-07-10 the queue held 31 files. The eight revenue fix tasks sat at slots 24–31. The provider usage-limit wall hit at slot 18. The rate-limit handler sleeps 30 minutes and retries the same task for up to 12 hours — then the night ends. Zero fix tasks ran.
The next night, the audits re-discovered the same unfixed findings at full token cost, and reports 13 and 14 regenerated them verbatim. The loop was paying to re-learn its own backlog.
DONE Means the Process Exited
Diagnosing the starvation meant reading the runner log against report file mtimes. Tasks 04, 12, and 16 regularly logged DONE with 450–2,200 bytes of output while their report files kept a date three days older. On 2026-07-10, task 12 logged DONE at 00:27; its report was dated three days prior.
DONE records that the process exited, not that it wrote anything. It is the same class of lie as an exit code 0 on work that never happened — and in a loop nobody watches, it compounds nightly.
The Summary Ran Before Half the Reports Existed
The executive-summary task is numbered 11. Reports 12 through 23 sort after it. The summary was therefore always built from the previous night's content, by construction of the sort order.
On 2026-07-20, reports 01–07 were from the previous night and only 08–10 were fresh. Splitting a run across nights had become routine — the quota wall ends the night wherever it lands — and the summary dutifully synthesized whatever mix of ages it woke up to, with no age labels anywhere in its output.
Wrong Probes, Promoted to CRITICAL
Two headline criticals on 2026-07-20 were wrong:
- A report declared an image-generation API token dead. It had probed
GET /v1/user, an endpoint that does not exist in that API. The real account endpoint returned 200. - A report declared zero tasks routed to the cheap model without reading the
providerfield in the task store. Five tasks carried it.
Same failure shape both times: a wrong probe read as evidence of breakage, then promoted to CRITICAL because the probe failed loudly. That night the reports also undercounted live state — 48 hung feed runs reported against 54 actual, 5 missing scheduler tasks against 9 — and a "crash loop, 16 restarts" finding went stale within hours because PM2 restart counters reset on reboot. Related trap: the runner flushes PM2 logs nightly around 23:01, so any audit reading error logs sees less than 24 hours of history and keeps concluding "no errors."
The Dangerous One: Stale Advice With an rm Block
One standing report owns stale-project triage: list what looks dead, recommend archival. Across three consecutive synthesis runs — each verified against live systems — it recommended archiving five services that are all alive: a deliverability-testing worker, a screenshot service, a background-removal tool, a catalog app, and a docs site. The recommendation ships with a ready-to-run rm block.
The triage report was not hallucinating. Its evidence was real — a static inventory snapshot — but liveness on this box lives in cron configs, a second scheduler, and PM2 process state the report never reads. Its conclusion was wrong three weeks running, confidently, with destruction attached.
The rule that came out of it: nothing generated by an unattended audit ever executes a delete. An audit proposes; a human or a separately reviewed script disposes.
The Loop Never Closed
Four more findings from the same anatomy, in descending order of embarrassment:
- 0 of 7 moves from a growth plan were executed. The fix-staging script only parsed the executive summary — which had since been rewritten for other purposes. Nobody counted applied diffs, so nobody noticed for a week.
- 12 fix tasks staged in early October were still unapproved a week later, while the audits re-found them nightly. The loop's real output was a growing list of things it already knew.
- The one undated executive summary from mid-August was overwritten with no copy. Lost.
- The runner's cheap-model branch turned out to be a placeholder that logs "not installed" and silently invokes the expensive default. The label said one model; the invoice said another.
What the Synthesis Step Does Now
# Before synthesizing anything:
stat -c '%y %n' reports/*.md | sort # age every report first, label it in output
cp EXECUTIVE-SUMMARY.md EXECUTIVE-SUMMARY-$(date +%F).md # dated copy BEFORE overwrite
cmp reports/13-*.md previous/13-*.md # byte-diff vs last run — identical means dead night Every summary since carries dated copies, per-report age labels, and re-probes of the top findings against live systems before anything gets ranked. A critical stays unverified until its probe is confirmed to hit an endpoint that exists.
| Symptom | Root cause | Detector |
| Byte-identical report, 3 nights | Fixes starved behind audits | cmp against previous run |
| DONE logged, report 3 days old | Runner logs exit, not output | Report mtime vs run timestamp |
| Summary built from stale data | Summary task sorts before half the reports | Age label on every report |
| Dead-token CRITICAL | Probe hit a nonexistent endpoint | Re-probe before ranking |
| Archive advice on live services | Static inventory, live liveness elsewhere | Manual probe; no generated rm |
| 0 of 7 moves executed | Stager parsed the wrong file | Count applied diffs per week |
What I'd Change
The picker still runs lexicographic. The proposed fix — one-shot fix tasks sorting ahead of standing audits — is staged, not applied, and this post is the argument for applying it. Beyond that: DONE should mean "report mtime updated," not "process exited," and every standing report should open with what changed since the previous run. A report whose delta section is empty three nights running has told you everything you need to know about the loop it lives in.