AtlatestRenderedmarkdown

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

Read the source

Readme

Off-device repro evidence — courier second spam cause (2026-07-01)

All runs used a LOCAL mock (mock_telegram.py) via COURIERTELEGRAMAPI_URL. NOTHING reached real Telegram. Mock models Telegram's getUpdates offset semantics (offset N confirms/drops updates < N) and records every request.

Binaries:

  • FIXED: build/dev/bin/courier (branch fix/courier-log-persist-repro)
  • CONTROL: master + api-url-only change (isolates the offset-persistence fix)

Deliverable 1 — --log now APPENDS across restarts (was: truncate)

2026-07-01T13:50:34Z [INFO] Courier starting mode=leader relay=none telegram=not configured
2026-07-01T13:50:34Z [INFO] MCP server ready name=courier

--- courier restart ---
2026-07-01T13:50:36Z [INFO] Courier starting mode=leader relay=none telegram=not configured
2026-07-01T13:50:36Z [INFO] MCP server ready name=courier

--- courier restart ---
2026-07-01T13:50:38Z [INFO] Courier starting mode=leader relay=none telegram=not configured
2026-07-01T13:50:38Z [INFO] MCP server ready name=courier

3x 'Courier starting' + 2x restart markers => full history preserved.

SECOND CAUSE — BEFORE (pre-fix control): backlog delivered + re-delivered

Cold start with a 2-update backlog. Each delivered message = one inbound the leader would ACK = one outbound Telegram send => storm.

control RUN 1 (cold start) stdout events:
{"type":"hello","pid":3748}
{"type":"heartbeat"}
{"type":"message","text":"BACKLOG-1","sender":"daviwil","sender_id":"42","chat_id":"1001"}
{"type":"message","text":"BACKLOG-2","sender":"daviwil","sender_id":"42","chat_id":"1001"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}

control RUN 1 stderr (no offset persistence, no drain):
2026-07-01T13:49:48Z [INFO] Telegram poller child started pid=3748

control RUN 2 (restart, backlog still pending) -> RE-DELIVERS:
{"type":"message","text":"BACKLOG-1","sender":"daviwil","sender_id":"42","chat_id":"1001"}
{"type":"message","text":"BACKLOG-2","sender":"daviwil","sender_id":"42","chat_id":"1001"}

SECOND CAUSE — AFTER (fixed): drain on cold start, deliver-once, no re-delivery

RUN 1 (cold start, 2-update backlog) stdout events:
{"type":"hello","pid":30027}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"heartbeat"}
RUN 1 stderr:
2026-07-01T13:44:45Z [INFO] Cold start: drained Telegram backlog (not delivered) count=2 offset=3

RUN 2 (restart; 1 NEW update injected) -> delivers NEW once:
{"type":"message","text":"NEW-live-msg","sender":"daviwil","sender_id":"42","chat_id":"1001"}
2026-07-01T13:44:51Z [INFO] Restored Telegram getUpdates offset offset=3

RUN 3 (restart, no new updates) -> NO re-delivery:
message events: 0
2026-07-01T13:44:57Z [INFO] Restored Telegram getUpdates offset offset=4