The tablet held 353 documents on 9-22 and 97 on 9-23, and nobody pruned it. Commit 76148e6 gave every document a last step: once its return is processed, the device copy is trashed. Today: lifecycles with an explicit end, and why the trigger is the processed file, not the done tag.
For a month the device-doc-count check failed every morning (budget 70) and the 8-26 rate-limit incident traced back to the poller listing hundreds of documents. The fix was not a cleanup; it was a missing step in the loop. When a loop has a start and no end, the backlog is the design.
Every document you receive has a life: sent, read, inked, tagged done, pulled, processed, applied. Until 9-22 the loop stopped at applied. The PDF stayed on the tablet forever because no code owned the step after applied. 246 finished application packages and every done-tagged briefing were still there.
The new function processed_still_on_device() in rm_pull.py compares three lists on every 15-minute poll: documents on the device, PDFs in returns/<date>/_processed/ (the proof that the ink landed), and PDFs in your outbox (the proof we sent it). A name in all three is finished, so the poll trashes it. Trash is recoverable; deletion is not, and the code never deletes.
Two guards make it safe. It skips anything in the manual-hold registry, so the System Audit and the Codex design studies are never touched. And it removes at most ten per poll, so the 256-document backlog drained over a few hours instead of one burst that would have hit the cloud rate limit again.
See the count, then the log lines the release step writes.
cat /Users/tom/Claude/PDB/data/rm-device-count.json
grep -n 'release' /Users/tom/Claude/PDB/logs/remarkable-returns.log | tail -5
grep -n 'def processed_still_on_device' -A 12 \
/Users/tom/Claude/PDB/scripts/rm_pull.py
A loop without a last step turns a device into a landfill; the fix is a step, not a sweep.