Codex review on f616bed.
Three gaps in the renewal added last commit:
- `collectQueuedFileIds` returned early at the server's 10-id cap, so a
remainder holding more than one batch renewed only its first message and
left the rest on their enqueue-time hold. Collect everything and split
into capped requests instead of truncating.
- A refused `ask()` restores the popped item, but renewal ran before the
send and covered only the pre-existing remainder. Since the run-end signal
is already consumed, nothing would touch that item again. Renewal now runs
after `ask` and includes the restored item.
- A single run can interrupt for approval more than once, each pause running
to the configured window, so renewing only at drain transitions leaves a
gap longer than `renewMs` with no renewal in it. The ceiling cannot help
when nothing renews.
The third is the same structural gap as the previous round along a new axis:
renewal tied to discrete events loses the file whenever two events are
further apart than the hold. Rather than hook each transition, renew on a
30 minute heartbeat while anything is queued, which is far below the
smallest hold (24h) and so covers any single gap regardless of cause.
Still bounded: every renewal is clamped against the file's upload time, so
the ceiling is unchanged. A queue nobody has open emits no heartbeat and
lapses one `renewMs` after its last touch, preserving the abandonment
behaviour.