Skip to content

fix(feedback): make comment delivery exactly-once across channels#14

Merged
benvinegar merged 1 commit into
mainfrom
fix/cross-channel-feedback-cursor
Jun 12, 2026
Merged

fix(feedback): make comment delivery exactly-once across channels#14
benvinegar merged 1 commit into
mainfrom
fix/cross-channel-feedback-cursor

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Problem

The server's agentSeq cursor ("highest comment seq already delivered to the agent") was advanced by every delivery channel but read by none. Each client kept a private cursor defaulting to 0:

  • the CLI (sideshow wait) in a per-process state file
  • the stdio MCP server in a process-local variable
  • the /mcp transport expected the model to pass afterSeq back, defaulting to 0

So mixing channels — exactly the combo the docs recommend (piggyback on writes + a background sideshow wait) — replayed comments the agent had already received. Found in the wild: a background sideshow wait armed after an MCP wait_for_feedback immediately re-delivered the same comment.

Fix

  • server/app.ts: an author=user session read with no explicit after now resumes from the session's agentSeq — "where the agent left off" lives server-side and is shared by every channel. An explicit after/afterSeq remains as a deliberate replay (and can't regress the cursor; markAgentSeen is monotonic in both stores).
  • bin/sideshow.js, mcp/server.ts, server/mcpHttp.ts: the three client-side cursors are deleted; clients omit after and let the server resume.
  • Viewer behavior unchanged: unfiltered reads never touch the cursor.
  • Docs updated (guide/, MCP tool descriptions, AGENTS.md invariant, CHANGELOG).

Tests

  • New: cursor-less author=user reads deliver once and not again across fresh processes; piggyback delivery advances the cursor seen by waits; viewer reads stay unconsumed.
  • Updated: the author/after filter test passes after=0 explicitly, since a bare author=user read now (correctly) consumes the cursor.
  • npm test (51/51), npm run typecheck, npm run lint, npm run format:check all pass; also verified live against a throwaway server (deliver once → fresh process gets nothing → piggybacked comment not re-delivered → --after 0 replays).

🤖 Generated with Claude Code

The server's agentSeq cursor was advanced by every delivery channel but
read by none: the CLI kept its own cursor in a state file, the stdio MCP
in a process-local variable, and the /mcp transport defaulted to 0. A
fresh `sideshow wait` process (or restarted stdio MCP server) replayed
comments the agent had already received via piggyback or another channel.

An author=user session read with no explicit `after` now resumes from
the session's agentSeq, and all three clients drop their private cursors
— CLI, both MCP transports, and piggyback share one exactly-once stream.
Explicit `--after <seq>` / `afterSeq` remains as a deliberate replay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@benvinegar benvinegar merged commit ff79524 into main Jun 12, 2026
6 checks passed
@benvinegar benvinegar deleted the fix/cross-channel-feedback-cursor branch June 12, 2026 19:05
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