Skip to content

fix(web): a thread started from a draft goes live without a reload - #2005

Merged
lidge-jun merged 2 commits into
devfrom
fix/web-draft-thread-detail
Oct 28, 2026
Merged

lidge-jun merged 2 commits into
devfrom
fix/web-draft-thread-detail

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

A thread started from a draft showed "Send a message to start the conversation." and no agent activity after the first send, until the page was reloaded.

#1982 made useQueuedRunEdit read pendingRequestsAtom(routeThreadRef) on every route. On a draft route that ref is the draft's reserved thread id, which the server does not have yet. Mounting it starts the thread's detail stream: the bounded snapshot answers 404, and the stream parks as deleted and never opens its socket. The route keeps the same ChatView mounted through promotion, so after the first send ChatView read that same parked node and rendered an empty timeline. A reload built a fresh node, which is why the reply appeared then.

useQueuedRunEdit now takes the detail ref ChatView already resolves with resolveThreadDetailRef (null while the draft waits for its shell), both for its render-time read and for beginEdit. I checked the other readers on draft routes: DiffPanel gets a null route ref, the route view only reads unlisted server routes, the queued-runs control renders only for server threads, and the remaining handler-time registry.get calls dispose at once (setIdleTTL(0)), so none of them can park the thread.

The thread stream itself is unchanged. It has no shell signal, rebuilding it on shell changes would refetch cold deep links, and a socket subscribe for an unknown id fails its snapshot and retries every 250 ms, so the reader-side gate is the contract.

Validation

  • New test in useQueuedRunEdit.browser.test.tsx runs the real client-runtime thread atoms over a fake environment: render as a draft, create the thread on the server, give the same mounted harness the detail ref, and expect live with the first message, then a reply delivered over the socket. On dev's source it fails with status deleted; with the fix all 5 tests pass. The 4 existing tests were already failing on dev (the mock had no pendingRequestsAtom) and pass again.
  • vp run check:changed (Node 26): format, lint, knip, typecheck, tests pass.
  • Real client on an isolated dev server: new thread, wait, send.
Before (dev) After
before after

Before: the draft route fetched the bounded snapshot (404) before sending; the reply stayed hidden until reload. After: no draft-time fetch, and the reply rendered live.

Follow-up, not in this PR: on mobile, HardwareKeyboardCommandProvider mounts useThreadProjection from the pathname, and NewTaskDraftScreen sets the route's thread id before creation is delivered, so a new mobile thread can park the same way.

Reviewed by gpt-6.1-sol (high) through GOAT task review.

Made by claude-opus-5-5 in the Codex harness on GOAT.

lidge-jun added a commit that referenced this pull request Oct 28, 2026
@lidge-jun
lidge-jun merged commit e108855 into dev Oct 28, 2026
17 checks passed
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