Wires the delivery_worker's retry loop on top of the
erlang:send_after / cancel_timer primitives just landed on
loops/erlang (3709460d, 98b0104c, 779e53b2 — cherry-picked here
since origin/architecture hasn't caught up yet).
Surface:
- new :timers [{Cid, Ref}] state field tracks live timer refs
- handle_call(flush): drain (existing semantics) + arm_retry_timer
per retried Cid (computes backoff slot from the now-bumped attempt
count, sets next_retry_at, send_after self-cast). Reply shape
unchanged.
- handle_info({retry, Cid}, S): redrives that one Cid through
deliver_one_pure. Success → record_success_pure + clear pending.
Failure → schedule_retry_for (which bumps attempts, dead-letters on
slot 6, or arms next slot).
- cancel_timer_for/2 before arming a new timer so stale timers don't
keep the scheduler's run loop alive after the work is done.
- state_srv/1 + timer_ref_for/2 for test introspection.
5/5 in new delivery_retry_timer.sh; existing delivery_worker.sh
17/17 and delivery_retry.sh 11/11 still green. Conformance gate
771/771 (was 761/761; the +10 is the cherry-picked send_after
suite).
Closes Blockers #3. m2 is now feature-complete.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>