`connect()` calls `_seed_channel()` unconditionally, and seeding marks every event currently in the channel as seen so a start never replays history at the agent. A message that arrives after the process starts but before the seed completes — or at any point while the gateway is down — sits in exactly that history, so the seed swallows it permanently even though the Buzz relay still has it. The `seen` set and `last_ts` lived only in memory, so there was nothing to distinguish "already handled" from "never seen". Each watched channel's cursor (`chat_type`, `last_ts`, and the bounded `seen` id list) is now persisted under `HERMES_HOME/buzz/channel-cursors.json` and restored at connect. Where a cursor exists the channel resumes from it and the history fetch is skipped entirely; where none exists the old seed-from-history behaviour is unchanged, so a first-ever run still never replays a backlog. Details worth noting: - The file records the identity and relay it was written for. A cursor from a different bot or relay is ignored rather than trusted — the channel ids would collide while the event stream behind them is a different one. - Any read or parse failure leaves the cursors empty, which degrades to seeding instead of failing the connect. Writes go through `utils.atomic_json_write` (temp + fsync + replace), so a crash mid-write cannot leave a truncated cursor behind. - The restored `seen` list is trimmed to `_SEEN_CAP` on load, keeping the newest ids, so a hand-edited or legacy file cannot grow the de-dupe set without bound. - Saves are gated on the cursor actually moving, so an idle channel does not rewrite the file every poll interval. Both inbound transports are covered: the poll sweep and the WebSocket event path share the same check. Tests: six new cases in `TestChannelCursorPersistence` — the cursor is written on seed, a restart resumes without spending a CLI call on history and then delivers the mention that landed while the gateway was down, a foreign identity or relay is ignored, a corrupt file falls back to seeding, the restored `seen` set stays bounded, and an idle poll leaves the file untouched. All six fail on main. Tested on: Windows 11, Python 3.12. `python -m pytest tests/gateway/test_buzz_adapter.py tests/gateway/test_buzz_websocket.py -q` — 33 passed (23 pre-existing + 6 new here, plus 4 WebSocket). Requires `pytest-asyncio` (pinned at 1.3.0 in pyproject) — without it the async cases in this file error out as unknown marks.
3 lines
53 B
Plaintext
3 lines
53 B
Plaintext
sandrohub013
|
|
# buzz durable channel cursors (#90464)
|