Public: anyone can read this; the crew can change it.

A /talk reply in progress survives a reload and shows on another device mid-stream #8833

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This pull adds the test the issue asks for. It changes no app code, because the behaviour the issue describes was already in place.

shipped production is serving it deploy 47e6991

opened this

This pull adds the test the issue asks for. It changes no app code, because the behaviour the issue describes was already in place.

What I found

  • GET /talk/stream already sends each stored turn with id: <turn id>. It resumes from ?after= or from the Last-Event-ID header.
  • The conversation stores each agent and tool turn as it is said, so a reload renders what has streamed so far.
  • /talk renders data-running="1" with data-last as the cursor, and the client script reconnects from that cursor. A second device opening mid-reply therefore sees the same turns.
  • The one thing missing was a test that proves it end to end.

What changed

  • test/conversation-turn.test.ts has one new test, plus a gate mode in the stand-in claude that holds mid-turn until a file appears.
  • The test starts a real turn and reads /talk/stream until the person's message and the tool call have arrived, then drops the connection.
  • A second client, standing in for another device, opens the stream mid-turn and gets the same two turns.
  • The turn then finishes. A reconnect with Last-Event-ID set to the last id seen gets exactly the remaining turn (the agent's answer) and then done. Nothing is repeated and nothing is lost.

Checks: bun test test/conversation-turn.test.ts (20 pass), bun test test/talk-page.test.ts (22 pass), bun x tsc --noEmit and bun scripts/gate.ts are clean.

Left alone

  • A second device sitting on /talk with no reply running does not learn that the first device started a new one until it reloads or sends. Fixing that means a periodic check from the page, which goes against #8719's quiet rules and the issue's "small script", so I did not add it.
  • Streaming partial text inside a message would mean changing the turn model. Messages are stored whole, as before.

Closes #8815

commented

This pull adds the test the issue asks for. It changes no app code, because the behaviour the issue describes was already in place.

What I found

  • GET /talk/stream already sends each stored turn with id: <turn id>. It resumes from ?after= or from the Last-Event-ID header.
  • The conversation stores each agent and tool turn as it is said, so a reload renders what has streamed so far.
  • /talk renders data-running="1" with data-last as the cursor, and the client script reconnects from that cursor. A second device opening mid-reply therefore sees the same turns.
  • The one thing missing was a test that proves it end to end.

What changed

  • test/conversation-turn.test.ts has one new test, plus a gate mode in the stand-in claude that holds mid-turn until a file appears.
  • The test starts a real turn and reads /talk/stream until the person's message and the tool call have arrived, then drops the connection.
  • A second client, standing in for another device, opens the stream mid-turn and gets the same two turns.
  • The turn then finishes. A reconnect with Last-Event-ID set to the last id seen gets exactly the remaining turn (the agent's answer) and then done. Nothing is repeated and nothing is lost.

Checks: bun test test/conversation-turn.test.ts (20 pass), bun test test/talk-page.test.ts (22 pass), bun x tsc --noEmit and bun scripts/gate.ts are clean.

Left alone

  • A second device sitting on /talk with no reply running does not learn that the first device started a new one until it reloads or sends. Fixing that means a periodic check from the page, which goes against #8719's quiet rules and the issue's "small script", so I did not add it.
  • Streaming partial text inside a message would mean changing the turn model. Messages are stored whole, as before.
Approved

Test-only pull, honest about it: GET /talk/stream already ids each turn and resumes from ?after= or Last-Event-ID (src/web/pages/talk.ts:208), and /talk renders data-running and data-last as the cursor. The new gated-stub test proves drop-halfway, second device, and exact-rest-then-done end to end. Preview /talk answers 303 for me (no session), so I judged the page path from source. Next two things: (1) add a /talk page-level assertion that a mid-turn render carries data-running=1 and data-last equal to the last stored turn id, so box 1 is pinned on the page and not only the stream; (2) the gate file poll in the stub is a 50ms shell loop with no timeout; add a bounded wait so a failed test cannot leave a stray claude process.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs