Skip to content

fix(chat): keep a folded run folded when the words before its tools join it - #2098

Merged
devswha merged 2 commits into
mainfrom
fix/chat-run-block-key
Oct 28, 2026
Merged

devswha merged 2 commits into
mainfrom
fix/chat-run-block-key

Conversation

@devswha

@devswha devswha commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Change

Fixes #2063 (the MEDIUM follow-up from the merge-pass review of #2060).

A work block the reader folded opened again when the words before its tools joined its run: a
later Codex record marks unphased text phase: "commentary", or a blank leading commentary gets
its text. Turn (src/components/ChatView.tsx) keyed each block, and its fold memory, by the
index of the run's first part (work:<start>). When the words joined, start moved, so the
React key and the memory key both changed and the block came back open.

Now a run keeps its name while its parts change. runNames (src/lib/workBlocks.ts) gives each
run the name its parts' run had the last time the turn was drawn. The earliest run claims a name
first, which keeps #2060's rule that the earlier half of a split keeps the name. Only a run with
no name gets work:<start>, and never a name a shown run holds. The row's memory
(ChatRowMemory.runs, part index → name) records the names at commit, so they also survive the
row scrolling out of the virtual list and back.

Design: no new UI. This extends the fold contract that #2060 set for runs that split to runs that
grow or shrink at either end (#2060's runs follow the Claude app's interleaved tool groups): a
block keeps the fold the reader gave it.

Not changed: the LOW items in #2063 (the stand-in's handover to the first real run, which has no
visible effect).

Tests

  • src/components/ChatTurn.test.ts, stateful react-test-renderer cases with a row memory:
    • The issue's reproduction folds a live [text, tool] run, then redraws it with the text
      marked commentary, and also with a blank commentary filled in. The block stays folded, and
      stays folded again after a remount from the same memory. Fails on main.
    • Codex review case: [tool, answer, commentary]. The answer becomes commentary, so the runs
      merge, and the reader folds the merged block. Then the last part becomes the answer, and the
      block stays folded. Main passes it; the first commit of this PR failed it.
  • src/lib/workBlocks.test.ts: runNames keeps a name while parts change, gives a split run's
    first part the old name, and never hands a shown name to a new run.
  • scripts/chat-fallback-browser-qa.ts: the issue's reproduction in the real ChatView. On main's
    ChatView it fails at "words that join the run keep it folded" ([true]); here the whole script
    passes.
  • bun run check fast: green (7723 unit tests).

Found along the way, also on main: a conversation without a history_id keeps its row slots by
position when its turns are replaced (ChatView.tsx showAnswer), so a row's memory goes to the
next turn shown at that position. The QA scenario places its turn on a row with no memory.

Reviews

  • omo-native-code-reviewer on 8fb4784: PASS, LOW notes only. One LOW said a fold was still lost when words left a run again; the second commit covers that case.
  • Codex Sol 6.1 Fast xhigh on 8fb4784: MEDIUM, a merged block lost its fold when its remembered part left the run (reproduced: it passes on main and fails on 8fb4784). Fixed in 439678c (runNames); the case is now the second ChatTurn test.
  • Codex Sol 6.1 Fast xhigh, final round on 8fb4784..439678c: PASS. The MEDIUM is fixed, with no new CRITICAL/HIGH/MEDIUM. It also checked splits, name collisions, StrictMode, uncommitted renders, and rendering without a memory provider.

…oin it

A run's block and fold memory were keyed by its first part's index. When
leading words joined the run (a later Codex record marks them commentary,
or a blank commentary is filled in), the index moved and the folded block
reopened. A run now keeps the key of its part that the row's memory holds
a fold for.

Fixes #2063
Keying a run by the remembered part it contained lost a merged block's
fold once that part left the run (Codex review). Each run now keeps the
name its parts' run had at the last draw (runNames); the row's memory
records it at commit. A run only takes a fresh work:<start> name when it
has none, and never one a run already shown holds.
@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 65ffea6 and 439678c.

Files selected for processing: 8
  • changelog.d/chat-run-fold-first-part.md
  • scripts/chat-fallback-browser-qa.ts
  • src/components/ChatTimeline.tsx
  • src/components/ChatTurn.test.ts
  • src/components/ChatView.tsx
  • src/lib/chatRowState.tsx
  • src/lib/workBlocks.test.ts
  • src/lib/workBlocks.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devswha

devswha commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

CI on 439678c: two failures, both wall-clock bounds in server tests this PR does not touch (it changes only client chat code and tests). I reran the failed jobs once.

  • Fast checks: server/codex.test.ts "reads short answers in bounded time however many links a large answer has" took 1062 ms against < 1000.
  • Integration tests: server/opencode.contract.test.ts:1037 "never waits on the event stream before the first key": prompt/slow gap of 1388 ms against < 1000.

Both browser regression jobs passed, and so did bun run check fast locally on this head (7723 unit tests).

@devswha

devswha commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

Rerun result: Integration tests passed (the OpenCode first-key bound was load). Fast checks did not run its tests: the job hit GitHub's 10-minute limit in its "Release guards" step (the first attempt took 3m15s in total), so I reran that one job again.

@devswha

devswha commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

Merging 439678c: omo-native-code-reviewer and Codex both pass this head, and the required checks are green (Fast checks passed on a rerun after its job was cancelled). CodeRabbit did not review this head. Codex's MEDIUM also happens on main, so it goes to a follow-up issue with the LOW notes.

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.

chat: keep a run's fold when its first part changes (follow-up to #2060)

1 participant