diff --git a/planning/claude-code-channel.md b/planning/claude-code-channel.md index 6ab3a63..7fb3a32 100644 --- a/planning/claude-code-channel.md +++ b/planning/claude-code-channel.md @@ -334,7 +334,10 @@ put that backlog into context on every Claude Code restart. Decided (ยง14 C): - An event whose `created_at` is older than the process start time minus 60 seconds is consumed without a push. Instead, once the stream has caught up (the first heartbeat after replay), push one `kind="backlog"` control event: how many messages were skipped, per topic id, and the oldest - and newest timestamps. The skill tells the sponsor, as it does today. + and newest timestamps. The skill tells the sponsor, as it does today. The heartbeat is the + boundary because the server sends a `: ping` only while it has nothing past the client's + position. A `cursor` frame is a skip marker, not a boundary: it can arrive mid-replay, or not + at all. - In-process reconnects resume from the cursor and push everything, since nothing they replay is from before the session. - With no cursor file, `rooms watch` starts from the beginning of the retained stream (`--since` diff --git a/src/pepper_cli/api_client.py b/src/pepper_cli/api_client.py index 7c5d72e..45ecb03 100644 --- a/src/pepper_cli/api_client.py +++ b/src/pepper_cli/api_client.py @@ -485,7 +485,12 @@ class FeedFrame: ``"comment"`` (empty ``payload``) stands for an SSE comment line, the server's ``: connected`` / ``: ping`` heartbeat: it carries nothing to act - on but tells the caller the connection is healthy. + on but tells the caller the connection is healthy. A ``: ping`` is also the + replay boundary: the server sends one only while it has nothing past the + caller's position, so the first ping after connecting means the stream has + caught up. A ``cursor`` frame is not a boundary: it can arrive mid-replay, + or not at all. A ``start=head`` stream opens with its head ``cursor`` frame + (after ``scope``) before any event or ping. ``id`` is the frame's SSE ``id:``: for an event frame the event id, for a ``cursor`` frame the cursor, ``None`` on ``error`` and ``scope``. Either