Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/pepper_cli/claude_channel.py
Comment thread
vycari-review[bot] marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,6 @@ def _flush_backlog(self) -> None:
self._push(content, meta)
self.skipped.clear()
self.skipped_times.clear()
self.first_run = False

def _live_edge(self) -> None:
"""Tell Claude, once, that the channel started at the live edge: there is no backlog to
Expand Down Expand Up @@ -549,6 +548,9 @@ def _topic_words(self, topic_id: str) -> str:
def _caught_up(self) -> None:
self._flush_backlog()
self._flush_resync()
# The catch-up is over whether or not anything was skipped. Left set when nothing was,
# ``first_run`` would go on suppressing resyncs for the rest of the process.
self.first_run = False
Comment thread
bniladridas marked this conversation as resolved.
Resolved
self._persist()
Comment thread
vycari-review[bot] marked this conversation as resolved.
Resolved

def _resync(self, cause: str) -> None:
Expand Down Expand Up @@ -829,7 +831,10 @@ def _handle_frame(self, frame: api_client.FeedFrame) -> str | None:
if frame.kind == "error":
return self._frame_problem(frame)
if frame.kind == "comment":
if frame.payload.get("comment") != "connected":
# ``: ping`` is the contract's replay boundary: the server sends one only while it
# has nothing past this position. ``: connected`` opens a connection, and any other
# comment (a proxy keepalive, a newer server's frame) says neither has happened.
if frame.payload.get("comment") == "ping":
self._caught_up()
return None
scoped = self.config.room is not None
Expand Down
37 changes: 37 additions & 0 deletions tests/test_claude_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,43 @@ def test_a_resync_names_its_cause_gives_room_ids_and_is_safe_on_a_first_run(monk
assert f"{_TOPIC} (room_id {_ROOM}; Kitchen › Dinner)" in note["content"]


def test_an_old_event_replayed_after_a_first_run_still_owes_a_resync(monkeypatch):
"""A first run whose window holds no messages pushes no backlog, so nothing but the completed
catch-up can clear ``first_run``. The stream then drops, and the reconnect replays an older
unrecognised event, which was missed and owes a resync. Left set, the flag takes it for
first-run history and drops it."""
old = _now() - timedelta(days=3)
fake = _Fake(
_CONNECTED + _scope() + _PING,
503,
_CONNECTED + _scope() + _event(7, kind="group.renamed", created=old) + _PING,
)

result = run_channel(monkeypatch, fake, ["--room", _ROOM, "--start", "beginning"])

assert result.kinds() == ["resync"]


def test_a_non_ping_comment_does_not_end_the_first_run_catch_up(monkeypatch):
"""Only ``: ping`` marks replay completion; ``: connected`` opens the connection, and any
other comment (a proxy keepalive, a newer server's frame) proves neither. Left unfiltered,
such a comment ends the catch-up early, clears ``first_run``, flushes the backlog it owed,
and makes an old event replayed after it owe a spurious resync."""
old = _now() - timedelta(days=3)
fake = _Fake(
_CONNECTED + _scope() + _event(3, created=old) + ": keepalive\n\n",
503,
_CONNECTED + _scope() + _event(7, kind="group.renamed", created=old) + _PING,
)

result = run_channel(monkeypatch, fake, ["--room", _ROOM, "--start", "beginning"])

assert result.kinds() == ["backlog"]
(note,) = result.notifications
assert note["meta"]["first_run"] == "true"
assert "live_edge" not in note["meta"]


def test_a_first_run_replay_of_old_room_events_owes_no_resync(monkeypatch):
"""The retained window on a first run holds old room events of every kind (a rename, an
app install); none was missed, and the backlog already says to catch up."""
Expand Down
Loading