Skip to content

fix(ui): guard chat adoption against stale end-of-turn snapshots - #3119

Open
daryllimyt wants to merge 6 commits into
mainfrom
daryl/eng-1554-adopt-guard-refetch
Open

fix(ui): guard chat adoption against stale end-of-turn snapshots#3119
daryllimyt wants to merge 6 commits into
mainfrom
daryl/eng-1554-adopt-guard-refetch

Conversation

@daryllimyt

@daryllimyt daryllimyt commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Frontend backstop for ENG-1554: the final assistant message streams in fully, then vanishes at end of turn until reload.

Root cause is a backend end-of-turn race: the stream END token is emitted before finalize_turn clears curr_run_id, so the onFinish refetch of GET /sessions/{id}/vercel can return a snapshot that hides the just-finished turn. useAdoptServerTranscript then replaces the live transcript wholesale — its count-based guard passes because DB row segmentation can make a stale snapshot equal-or-longer than the live transcript. With no follow-up refetch until remount, the message stays gone until reload.

The backend root fix (emit terminal END only after finalize) ships separately. This PR is the defense-in-depth layer that kills the user-visible symptom and self-corrects without a reload.

Changes

  • Content-aware adopt guard (useAdoptServerTranscript): in addition to the existing count guard, a server snapshot must contain the live transcript's final assistant text (concatenated text parts; substring containment across all server messages tolerates DB row-splitting). Tool-only / textless final messages fall back to the count guard — tool, reasoning, and approval parts serialize too differently to compare.
  • Bounded follow-up refetch: a rejected adoption schedules invalidations of the /vercel transcript query at 1s / 3s / 8s. Timers cancel on adoption, unmount, or a new streaming turn. After the final retry, a snapshot that still fails the content check but passes the count guard is adopted anyway — the server is canonical at rest, so eventual convergence beats a stuck transcript.
  • Adoption stays wholesale replace (never merge), gated on status === "ready"; resolved-approval snapshots still adopt.

Tests

  • 8 new regression tests in frontend/src/hooks/use-chat.test.tsx covering: stale equal-length snapshot rejection + scheduled retries, adoption once the snapshot covers the final text (including text split across DB rows), resolved-approval adoption, retry-exhaustion eventual adoption, tool-only fallback, streaming/unmount timer cancellation.
  • Deliberately updated the chat-session-pane.test.tsx "adopts a same-length server copy with different content" expectation — that test pinned exactly the residual hole (it used a user-role message, so it never exercised the assistant-content path). It now asserts a same-length snapshot missing the final assistant content is not adopted immediately.
  • pnpm -C frontend check, typecheck, and test (99 suites / 556 tests) all pass.

Summary by cubic

Fixes ENG-1554 where the final assistant message disappears at end of turn by guarding adoption against stale server snapshots. Coverage is anchored on the final turn’s user prompt and assistant text, with bounded refetch that self-corrects without reload and retires failed retry series so recovery stays possible.

  • Bug Fixes
    • Content guard scoped to the final turn: require the last live user prompt and assistant text after it; handle repeated prompts via prompt occurrence count; promptless/textless finals fall back to the count guard; approval-resolved snapshots still adopt; tool-only finals use the count guard.
    • Bounded refetch: invalidate ["chat", chatId, workspaceId, "vercel"] at 1s/3s/8s; cancel on adopt/unmount/streaming; count only successful refetches; retire the series if no attempts deliver and start a fresh one on later snapshot changes; after the last successful retry, adopt count-covered snapshots even if text differs.
    • Hook/test updates: useAdoptServerTranscript now accepts chatId and workspaceId; added tests for repeated-prompt anchoring, retry freshness and series retirement, split-row adoption, retry cancellation, and not adopting same-length snapshots missing final assistant content.

Written for commit 72f038d. Summary will update on new commits.

Review in cubic

@daryllimyt daryllimyt added ui Improvements or additions to UI/UX fix Bug fix labels Jul 22, 2026
@zeropath-ai

zeropath-ai Bot commented Jul 22, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 72f038d.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► frontend/src/hooks/use-chat.ts
    Add ADOPT_SERVER_TRANSCRIPT_RETRY_DELAYS_MS constant and new helpers for server transcript adoption logic
► frontend/src/components/chat/chat-session-pane.tsx
    Update usage of adopt server transcript hook to pass chatId and workspaceId and enable adoption behavior
Enhancement ► frontend/src/hooks/use-chat.ts
    Export ServerTranscriptAdoptionDecision type and related helper functions (lastUserMessageIndex, messageText, getFinalLiveAssistantText, serverTranscriptCoversLiveFinalAssistantText, decideServerTranscriptAdoption)
Enhancement ► frontend/src/hooks/use-chat.ts
    Implement useAdoptServerTranscript with retry logic, cancellation, and decision-based adoption behavior
Enhancement ► frontend/src/hooks/use-chat.ts
    Introduce bounded retry episode mechanism and query invalidation handling for transcript adoption
Bug Fix ► frontend/tests/chat-session-pane.test.tsx
    Update test descriptions and scenarios to reflect new adoption behavior expectations (ownership swap and final content considerations)
Enhancement ► frontend/src/hooks/use-chat.ts
    Add logic to cancel retries on unmount or status changes and to schedule bounded retry episodes based on transcript adoption decisions
Enhancement ► frontend/src/hooks/use-chat.ts
    Extend tests to cover various adoption scenarios including content vs count guard, cancelled turns, and streaming transitions

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2346c12119

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/hooks/use-chat.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f0f0ca83c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/hooks/use-chat.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a78a46499

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/hooks/use-chat.ts Outdated
Comment thread frontend/src/hooks/use-chat.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af38a29ddc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/hooks/use-chat.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7bac3ab54b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/hooks/use-chat.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Bug fix ui Improvements or additions to UI/UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant