Skip to content

wait_for_message: never advance the cursor past an unclassified message - #1

Merged
markmnl merged 1 commit into
mainfrom
fix/wait-cursor-unclassified
Sep 5, 2026
Merged

wait_for_message: never advance the cursor past an unclassified message#1
markmnl merged 1 commit into
mainfrom
fix/wait-cursor-unclassified

Conversation

@markmnl

@markmnl markmnl commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • A message announced over the host WebSocket can be briefly unreadable. The thread lookup then failed, the message was treated as belonging to another thread, and `after_id` advanced past it, so a caller following the documented loop lost the message. Observed today on both mcp.fmsg.io and mcp.fmsg.live during a long two-agent conversation (21 consecutive occurrences on one side).
  • `rootOf` now retries transient failures and otherwise throws; unknown is never equal to "other thread".
  • Results gain `skipped` (id + reason; the cursor advances past these) and `unclassified` (the cursor is held before these; `note` says so; call again to retry). Lookups still in flight when the call returns count as unclassified.
  • Invariant: `after_id` is the highest explicitly accounted contiguous event.

Test plan

  • `npx vitest run` (38 tests) including two new regressions: other-thread skip followed by an in-thread match in one result; an unclassified lower id holds the cursor below a valid higher id, and a later wait from the held cursor delivers both in order.
  • Release as 0.1.4 and redeploy fmsg.io / fmsg.live (not done in this PR).

🤖 Generated with Claude Code

https://claude.ai/code/session_01AoApk9grYkP1EMd4F4GVMv

…d is unknown

A message announced over the WebSocket can be briefly unreadable; the thread
lookup then failed, the message was mistaken for one on another thread, and the
cursor advanced past it, so the caller lost it (observed 21 times in a row on a
live deployment; both sides of a two-host conversation saw it).

- rootOf retries transient lookup failures and otherwise throws instead of
  guessing; unknown never equals another thread
- results carry `skipped` (id + reason, cursor advances) and `unclassified`
  (cursor held before them, note explains, call again to retry)
- lookups still running when the call returns count as unclassified
- after_id is the highest explicitly accounted contiguous event
- two regression tests: other-thread skip followed by in-thread match;
  unclassified lower id holds the cursor below a valid higher id

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AoApk9grYkP1EMd4F4GVMv
@markmnl
markmnl merged commit 1d81afe into main Sep 5, 2026
4 checks passed
@markmnl
markmnl deleted the fix/wait-cursor-unclassified branch September 5, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant