AtlatestRepositorycourier

courier / tree / reproevidence-2026-07-01.md

Read it rendered

1# Off-device repro evidence — courier second spam cause (2026-07-01)
2
3All runs used a LOCAL mock (`mock_telegram.py`) via COURIER_TELEGRAM_API_URL.
4NOTHING reached real Telegram. Mock models Telegram's getUpdates offset
5semantics (offset N confirms/drops updates < N) and records every request.
6
7Binaries:
8- FIXED: build/dev/bin/courier (branch fix/courier-log-persist-repro)
9- CONTROL: master + api-url-only change (isolates the offset-persistence fix)
11## Deliverable 1 — --log now APPENDS across restarts (was: truncate)
12```
132026-07-01T13:50:34Z [INFO] Courier starting mode=leader relay=none telegram=not configured
142026-07-01T13:50:34Z [INFO] MCP server ready name=courier
16--- courier restart ---
172026-07-01T13:50:36Z [INFO] Courier starting mode=leader relay=none telegram=not configured
182026-07-01T13:50:36Z [INFO] MCP server ready name=courier
20--- courier restart ---
212026-07-01T13:50:38Z [INFO] Courier starting mode=leader relay=none telegram=not configured
222026-07-01T13:50:38Z [INFO] MCP server ready name=courier
23```
243x 'Courier starting' + 2x restart markers => full history preserved.
26## SECOND CAUSE — BEFORE (pre-fix control): backlog delivered + re-delivered
27Cold start with a 2-update backlog. Each delivered message = one inbound the
28leader would ACK = one outbound Telegram send => storm.
29```
30control RUN 1 (cold start) stdout events:
31{"type":"hello","pid":3748}
32{"type":"heartbeat"}
33{"type":"message","text":"BACKLOG-1","sender":"daviwil","sender_id":"42","chat_id":"1001"}
34{"type":"message","text":"BACKLOG-2","sender":"daviwil","sender_id":"42","chat_id":"1001"}
35{"type":"heartbeat"}
36{"type":"heartbeat"}
37{"type":"heartbeat"}
38{"type":"heartbeat"}
40control RUN 1 stderr (no offset persistence, no drain):
412026-07-01T13:49:48Z [INFO] Telegram poller child started pid=3748
43control RUN 2 (restart, backlog still pending) -> RE-DELIVERS:
44{"type":"message","text":"BACKLOG-1","sender":"daviwil","sender_id":"42","chat_id":"1001"}
45{"type":"message","text":"BACKLOG-2","sender":"daviwil","sender_id":"42","chat_id":"1001"}
46```
48## SECOND CAUSE — AFTER (fixed): drain on cold start, deliver-once, no re-delivery
49```
50RUN 1 (cold start, 2-update backlog) stdout events:
51{"type":"hello","pid":30027}
52{"type":"heartbeat"}
53{"type":"heartbeat"}
54{"type":"heartbeat"}
55{"type":"heartbeat"}
56{"type":"heartbeat"}
57{"type":"heartbeat"}
58{"type":"heartbeat"}
59RUN 1 stderr:
602026-07-01T13:44:45Z [INFO] Cold start: drained Telegram backlog (not delivered) count=2 offset=3
62RUN 2 (restart; 1 NEW update injected) -> delivers NEW once:
63{"type":"message","text":"NEW-live-msg","sender":"daviwil","sender_id":"42","chat_id":"1001"}
642026-07-01T13:44:51Z [INFO] Restored Telegram getUpdates offset offset=3
66RUN 3 (restart, no new updates) -> NO re-delivery:
67message events: 0
682026-07-01T13:44:57Z [INFO] Restored Telegram getUpdates offset offset=4
69```