Repository navigation
perf(native-chat): stop long replies from freezing while they stream - #35462
brennanb2025 wants to merge 33 commits into
Conversation
[High impact] The PR changes saved text cadence and durability across native-chat provider streams. Fix the extra argument in the history test before merging. Findings
SummaryStreamed assistant and reasoning text now use a 400 ms elapsed-time gate instead of percentage growth.
One new helper call blocks type checking. brennanb2025 acknowledged full-copy storage growth and deferred its fix to #36024 and #36053, with no interim shipping approved.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
Actionable comments posted: 2

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
5be73157-8d85-44d4-a632-6f2418c8f9dd
Files selected for processing: 19-
src/main/claude/claude-streamed-text-checkpoints.test.tssrc/main/claude/claude-streamed-text-checkpoints.tssrc/main/claude/claude-streamed-thinking.tssrc/main/claude/claude-structured-journal-translation.tssrc/main/codex/codex-structured-item-streams.tssrc/main/codex/codex-structured-journal-sink.tssrc/main/codex/codex-structured-journal-translation-settlement.test.tssrc/main/codex/codex-structured-journal-translation-streams.test.tssrc/main/native-chat/agent-session-journal/journal-row-writer.spec.tssrc/main/native-chat/agent-session-journal/journal-row-writer.tssrc/main/native-chat/agent-session-journal/journal-streamed-text-revision.test.tssrc/main/native-chat/agent-session-journal/journal-streamed-text-revision.tssrc/main/native-chat/agent-session-journal/journal-text-predecessors.tssrc/main/native-chat/agent-session-timeline/provider-timeline-assembler-streams.test.tssrc/main/native-chat/agent-session-timeline/provider-timeline-text-streams.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.tssrc/main/native-chat/agent-session-wire/agent-session-text-prefix.tssrc/main/native-chat/agent-session-wire/structured-agent-session-item-publication.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.
-
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

Minor · Gate predecessor blanking on structural equality. · journal-text-predecessors.ts:65-96
src/main/native-chat/agent-session-journal/journal-text-predecessors.ts:65-96
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winGate predecessor blanking on structural equality.
JournalRowWriterstages every inserted row. The structural comparison inisJournalStreamedTextRevision()only selects the fsync mode.
appendItem()can create a higher-revision row with text-only blocks and changed metadata such asownerFence,executionOwner,turnScope, linkage, or body fields.JournalTextPredecessors.stage()checks only role and revision before updating the predecessor with blank text. This can erase the previous revision’s text for a non-text-equivalent revision.Add a structural-equality gate to
stage(). Reuse the normalized comparison used byisJournalStreamedTextRevision().

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
d817adce-feba-4653-90e2-859dcb4631d3
Files selected for processing: 15-
src/main/claude/claude-journal-turn-scope.test.tssrc/main/claude/claude-open-turn.tssrc/main/claude/claude-streamed-text-checkpoints.test.tssrc/main/claude/claude-streamed-text-checkpoints.tssrc/main/claude/claude-streamed-thinking.tssrc/main/claude/claude-structured-journal-translation.tssrc/main/codex/codex-structured-item-stream-bounds.tssrc/main/codex/codex-structured-item-streams.tssrc/main/codex/codex-structured-journal-translation-streams.test.tssrc/main/native-chat/agent-session-journal/journal-row-writer.spec.tssrc/main/native-chat/agent-session-journal/journal-text-predecessors.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.tssrc/main/native-chat/agent-session-wire/structured-agent-session-item-publication.test.tssrc/main/runtime/orchestration/structured-worker-journal-page.test.ts
Files skipped from review as they are similar to previous changes: 2-
- src/main/native-chat/agent-session-journal/journal-row-writer.spec.ts
- src/main/native-chat/agent-session-journal/journal-text-predecessors.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
-
There was a problem hiding this comment.
Actionable comments posted: 2

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
5ea1b33f-ac2e-4e8f-99c5-ba1dc773d9ee
Files selected for processing: 44-
src/main/acp/acp-history-journal-overlay.tssrc/main/claude/claude-checkpoint-import.test.tssrc/main/claude/claude-checkpoint-input.tssrc/main/claude/claude-checkpoint-intent.tssrc/main/claude/claude-checkpoint-test-support.tssrc/main/claude/claude-context-facts.tssrc/main/claude/claude-context-usage-test-support.tssrc/main/claude/claude-final-output-coverage.tssrc/main/claude/claude-journal-translator-contract.tssrc/main/claude/claude-journal-turn-scope.test.tssrc/main/claude/claude-message-journaling.tssrc/main/claude/claude-provisional-row-corrections.tssrc/main/claude/claude-stream-json-connection-close.test.tssrc/main/claude/claude-stream-json-connection.tssrc/main/claude/claude-stream-json-output.tssrc/main/claude/claude-streamed-text-checkpoints.test.tssrc/main/claude/claude-streamed-text-checkpoints.tssrc/main/claude/claude-streamed-thinking.tssrc/main/claude/claude-structured-event-delivery.tssrc/main/claude/claude-structured-journal-translation-subagents.test.tssrc/main/claude/claude-structured-journal-translation.test.tssrc/main/claude/claude-structured-journal-translation.tssrc/main/claude/claude-structured-resume-point.tssrc/main/claude/claude-structured-session-close.test.tssrc/main/claude/claude-structured-session-close.tssrc/main/claude/claude-structured-session-exit-lifecycle.tssrc/main/claude/claude-structured-session-journal-control.tssrc/main/claude/claude-transcript-catch-up-test-support.tssrc/main/claude/claude-transcript-catch-up.tssrc/main/claude/claude-transcript-import-plan.tssrc/main/claude/claude-transcript-item-reader.tssrc/main/claude/structured/claude-structured-session-acquisition.tssrc/main/claude/structured/claude-structured-session-open.tssrc/main/native-chat/agent-session-journal/journal-step-writer.tssrc/main/native-chat/agent-session-timeline/provider-timeline-assembler-streams.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-child-exit.tssrc/main/native-chat/agent-session-wire/structured-agent-session-event-sink-estimate.tssrc/main/native-chat/agent-session-wire/structured-agent-session-event-sink-queue.tssrc/main/native-chat/agent-session-wire/structured-agent-session-event-sink.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-event-sink.tssrc/main/native-chat/agent-session-wire/structured-agent-session-history-catch-up.tssrc/main/native-chat/agent-session-wire/structured-agent-session-output-drain.tssrc/main/native-chat/agent-session-wire/structured-agent-session-sink-operation.tssrc/main/native-chat/agent-session-wire/structured-agent-session-transition.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
-
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
7cf60e25-fe00-42c1-9d64-336f81640c71
Files selected for processing: 10-
src/main/claude/claude-checkpoint-input.tssrc/main/claude/claude-final-output-coverage.tssrc/main/claude/claude-streamed-text-checkpoints.tssrc/main/claude/claude-streamed-thinking.tssrc/main/codex/codex-structured-journal-streams-scaling.test.tssrc/main/native-chat/agent-session-timeline/provider-timeline-assembler-refusal.test.tssrc/main/native-chat/agent-session-timeline/provider-timeline-assembler-streams.test.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.tssrc/main/native-chat/agent-session-wire/agent-session-delta-coalescer.tssrc/main/native-chat/agent-session-wire/agent-session-delta-work-test-support.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
-
Comments Outside DiffThese findings could not be posted inline.
|
| expect(storedBytes).toBeGreaterThan( | ||
| Buffer.byteLength(JSON.stringify(final)) + (4096 * 100 * 101) / 2 | ||
| ) |
There was a problem hiding this comment.
Long replies bloat saved conversations
Removing predecessor retirement keeps every full-text checkpoint on disk while replies now save roughly every 400 ms. This test confirms that a roughly 400 KiB answer retains over 20 MiB of growing prefixes. Longer replies increase that multiplier, and replayJournal reads and parses every saved prefix when the conversation opens.
Keep failed writes safe without dropping the retained-text bound, or add a replacement cleanup path before enabling the faster cadence.
Knowledge Base Used: Native agent conversations
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
NOT READY — repairs independently accepted and CI green at Usage reports could force repeated saves of a growing reply. Codex could also lose a context-usage update when the journal queue was full. Continuing text now keeps its save cadence, and a refused Codex update stays queued for retry. Independent review accepts the runtime repair and its test-only follow-up at Usage reports leave continuing named assistant and reasoning text on its ordinary save cadence; explicitly suppressed frames on the shared timeline follow the same rule. An anonymous row that becomes visible still places its meaningful unpublished predecessors first; real tools, plans, visible fallback rows and lifecycle barriers retain their ordering rules. Primary Codex usage returns the selected revision/publication admission unchanged. A refused reading remains in the existing retry path; admission transfers ownership to the native queue and does not mean COMMIT. Missing publishing support and invalid readings retain their accepted no-op behavior; no fallback capability, eager target read, extra owner or timer was added. The execution-late revision preserves the current row’s lifecycle and other fields, and creates no context-only row when no target exists. The new native proof exercises pending assistant text and clean reasoning against actual saturated ordinary and lifecycle queues, an actual 25 ms attempt while held, then legitimate low-water retry. It observes context 17,611/258,400 before another provider frame or disposal, with exactly one committed context input and one context publication. Textless final, dispatch echo and fresh reopen retain exact text and the original item envelope. Old production returns fail both cases by consuming usage without the context commit. Three selected-refusal cases reach the real adapter/retry through closed, failed and replaced owners and preserve the original reason, release read/timer resources and protect the successor. Their native SQL/close primitive coverage is carried separately; these are focused tests, not new app runs. Earlier QA carries for unchanged long replies, Stop/restart/reopen, copied-reader and adopted-Plan flows at QA head Long native-chat replies could arrive in coarse bursts while an agent was still producing text. This change uses the common pattern of whole-text updates on an ordinary 400 ms active-stream cadence, keeping meaningful text, first position and ordering intact. Review repairs preserve pressure retry/drain and closed-owner termination, prevent empty increments from consuming a delivery window, and remove foreground rewriting of older saved revisions. A failed write cannot use that removed rewrite path to blank previously committed text; only committed successor content may become visible. An exact refresh of the intact old committed snapshot is permitted separately. The plan producer now schedules a retained changed tail for 400 ms after its last successfully appended-and-published full value. Its one wake selects only due plans and leaves an assistant's existing delivery window alone. Established barriers save or retire the same pending value, so a later timer cannot recreate a completed row or move its original position. Pressure retains the full value for a positive retry; terminal failure reaches the original current session, with text retained until actual settlement. Plans remain FULL-durability complete status snapshots; no saved field, wire operation or row kind is added. Assistant/reasoning ordinary delivery still depends on arrivals or real barriers. Neither path promises a universal 400 ms visible gap, and event-loop stalls, provider pauses, forced saves and refusal are distinct limits. Retained Claude consumed-attempt and legacy pressure qualifications remain disclosed exclusions. Independent source/proof review accepted implementation CI run 41231657928 on Test consolidation retains 10 of the 12 introduced metadata cases and transfers two obligations into existing cases; the ten new admission cases are separate. The final native/terminal file passed 5 cases; the context file passed 14 before an equivalent Number.NaN spelling change; the five-file targeted contracts passed 11 with 50 skipped; the Pi file passed 12 before an equivalent last-match expression change. Actual recorded Pi usage at a dirty text predecessor does no full materialization, and final canonical/public/history/worker/reopened text remains exact. Fixed N/10N workloads retain both text roles, usage shapes, whitespace, real-boundary calibration and exact finals. ACP history retains usage plus named and anonymous finalization; its role assertion caused the historical CI typing failure, now repaired by the accepted equivalent local alias; fresh exact-head CI now passes. No runtime failure was observed in that assertion. Both ancillary-read cases now execute eight retries and later mailbox/dispatch assertions with the unchanged history scan bound. Historical 53 focused passes, seven selected existing cases and ten private replay cases are separate carried populations, not new test inventory. Old-force, dropped-text and empty-order negative controls remain discriminating. Verification and limits. Real Windows-low runs used Claude
These are recorded real replies, not constructor replay. Generated text, durations and conversation context differ; main also imported six earlier provider items. DOM mutation timing is not compositor paint or a controlled startup/throughput/CPU comparison. Historical measurements on a different machine/model are supplementary only. Removing foreground retirement increases retained payload until background retirement. For the original four replies on pre-#36024 storage, branch retained/cumulative direct INSERT JSON was 9,233,713 bytes for 79,323 final-text bytes, versus main 790,188 for 71,049. Stopped DB allocation was 10,203,136 versus 1,208,320 bytes; main was sampled later and its whole DB includes the imported history. Logical payload, cumulative inserted bytes and physical DB/WAL allocation are different measurements. Compaction cannot undo cumulative inserted bytes; no universal in-flight WAL bound is claimed. #36024 landed, replacing ordinary raw input-row persistence with keyed saved-state outcomes. These old samples are not current outcome-record costs. Each cadence save that executes, including a successful Plan wake, synchronously validates the full transient input, encodes complete changed items/scalars, performs per-emitted-key indexed lookup/INSERT, updates coverage and commits in the same transaction. It does not add a second whole-chat save after a raw-row INSERT. Journal writes do not coalesce; separate publications can. Suppressed/stale/refused append work does no outcome SQL, but can pay translation/estimation; publication refusal can follow an admitted append. Development builds additionally sample a full restore/equality check at most once per chat per minute. Historical save/COMMIT/body counters omit these operations, so no numeric total per-save cost is claimed for this merged head. #36053 retirement plus integrated body/outcome-record/metadata and DB/WAL measurement remain required. Codex still displayed “Codex could not confirm Stop. Check the chat before continuing.” The targeted case and matched main control both reached Interrupted / Working 0 with preserved text, acknowledged pause, accepted interrupt, root exit 0 and no recorded errors, but process-tree verification remained unavailable and workload exit unproven. Reproduced on main; tracked in STA-12558. Under the existing scoped disposition this is not a #35462 regression, and it is not a clean Stop pass. Neither descendant exit nor surviving descendants were proved; this PR includes no lifecycle fix or warning waiver. Adopted-Plan QA passed with the limits below. Fresh native chats still have no Plan-mode control. On M4Air ticket 3583, real Codex CLI 0.165.0 ( During the observed Plan stream, the head's largest positive DOM update gap was 567 ms; pinned main's was 5,714 ms. Seven sampled head provider pauses contained a publication before the next outgoing frame; main's two sampled pauses contained none, with the next saves about 0.93–1.02 seconds after the preceding frame. Five head publications at the preceding accepted save plus 401–404 ms strongly support the scheduled wake. Outgoing trace times do not identify callbacks or exclude every delayed generic callback; two other gaps fit the generic 60 ms window. These are different generated plans and small natural-pause samples, not a universal 400 ms promise. The head's early provider trace and final approximately 2.6 seconds before abort were not retained; accepted-text preservation does not prove every upstream byte arrived before close. The late reopen observer cannot exclude an earlier transient flash. Injected refusal/failure cases remain focused-test evidence. Pinned main independently reproduced three existing observations: a completed Plan folds behind “Worked for” until expanded; the CLI seed's completed Plan is omitted from imported native history; and an unhandled read-state notification is saved as a raw row, not seen rendered in these runs. This is a pass for the changed native Plan stream/lifecycle, not complete seed-history import. The rigs used separate stable throwaway homes without profile copies. Config hash values matched; secret scanning was bounded. All 42 recorded numeric process identities were absent in the final check, the lead terminal closed, assigned ports had no listeners, and ticket 3583 was released. Earlier lifecycle QA at The independent review checked scheduler isolation, original failure ownership, no-head pressure progress, metadata/final/late-key behavior, native FULL/reopen contracts, operation-count scaling, references and maintainability. Cumulative source at Current totals against merged main Metadata work remains proportional to metadata arrivals. A resumed/newest target still uses the existing journal scan; context revisions still serialize complete changed metadata and can require FULL commits. In the previously accepted long-reply synthetic-usage replay, one Codex workload still made 4,251 native commits, including 3,983 FULL commits, versus 271/3 in its control. That unchanged-workload evidence is carried, not rerun. Independent metadata removes the extra text joins and saves; it does not establish a CPU, latency, physical-I/O or storage bound. Logical text, body, transient-row and saved-record byte populations remain separate. The integrated current-format storage measurements and retirement proof remain required. Authorized main merges For Brennan's review
Screenshot receipt limits: the branch/release captures above were downloaded from their historical uploads with exact original blob/SHA256 matches, uploaded through gh image, and downloaded again through authenticated GitHub requests with identical SHA256. The four targeted Stop links retain their original authenticated HTTP-200 download hashes. The eight main comparisons were recovered from the final remote receipt manifest, and the original PNGs were copied with their recorded hashes, uploaded through gh image and downloaded again with identical SHA256. The first former main URL returned authenticated HTTP 404 from this Mac; that negative receipt is retained. No outcome is inferred for the other seven former URLs. A Codex seed link remains unavailable in this bundle. The COPY reader's 17 checks have eight exact screenshot-path matches in this local upload snapshot; the linked representative views do not imply 17 individually uploaded captures. The main comparison links above are now mapped; the remaining seed/per-item screenshot limitations stay explicit. No passed flow needs another run. Human judgment points; producer links retain the QA head and storage points at the merged head:
|
Resolve import conflicts from main's module moves. #37216 stopped the journal sink refusing writes under backpressure, so drop the tests that forced a sink refusal and keep one that proves a time-gated tail is saved before an incoming stream evicts it. Move the scheduled plan owner check out of codex-structured-session-acquire.ts to stay under its line cap.


ELI5
While an agent writes a long reply in Native Chat, the reply could sit still for several seconds and then jump ahead. Saves waited for the reply to grow by a fixed share of its length, so the longer the reply got, the longer each wait. Now the reply text is saved at most every 400 ms, so it keeps moving.
What Changed
Before: assistant and reasoning text saved a new copy only after the text grew by about 12.5%. A 20,000-character reply waited for another 2,500 characters between saves, which looked like a freeze. Replays of four recorded replies had maximum gaps of 6.1–15.0 s.
After: assistant and reasoning text use a 400 ms elapsed-time gate. The first save still lands in the existing 60 ms window. The same replays drop to maximum gaps of 0.5–1.3 s with identical final text. Windows low-spec runs measured 0.56–0.62 s for Claude against 7.7–8.7 s on main.
claude-streamed-text-checkpoints.ts,codex-structured-item-stream*.ts,provider-timeline-text-streams.ts) keep their existing full-text owners and switch only the gate. Command and file output keep their growth gate.codex-acquisition-scheduled-owner.ts) and routes a closed or failed save to that session's existing failure path.Changed in the takeover:
codex-text-cadence-pressure.test.ts, two cases incodex-context-usage-admission.test.ts, one case each incodex-structured-session-adapter-lifecycle.test.tsandclaude-streamed-text-checkpoints.test.ts, and the cases this PR added totimeline-lane.test.ts. fix(native-chat): keep every received reply and tool result when the journal save queue is busy #37216 dropped its own pressure tests the same way. The production retry paths stay, because a closed or failed sink still refuses, and so does the Codex size limit.codex-text-cadence-pressure.test.tsbecomescodex-text-cadence-eviction.test.tswith one test, rewritten without the forced refusal: a time-gated tail is saved before a new stream evicts its buffer. It fails when that save is removed.codex-structured-session-acquire.ts, which the merge pushed over its 300-line cap.Why
A time gate bounds how long text can sit unsaved, whatever the reply length. The growth gate's waits grow with the reply. This PR keeps the existing owners and save path instead of adding a second text store or a saved timer. Plans get a scheduled delivery because they often pause mid-document, and a paused plan looked unfinished.
The cost is more saved revisions while a reply streams. The PR was held until #36053 could reclaim them; the storage numbers are under Testing.
Linked Issue
No dedicated issue. Storage context: #34916 (journal growth), which #36053 addresses and this PR must not undo. This replaces the approach in #32315.
Visual Proof
Adopted Plan whole-window recordings from the earlier QA: reviewed head · pinned main. The difference is timing (largest DOM update gap 567 ms on the head vs 5,714 ms on main during the Plan stream), not layout.
Testing
Storage with #36053, the gate this PR was held for. I streamed the same long turn through the real translator, journal sink and SQLite journal on main (
b6467f0) and on this head, then ran #36053's compaction to completion followed by incremental vacuum. Codex streamed reasoning (4k chars), a plan (8k) and a reply (24k). Claude streamed thinking (4k) and a reply (24k). Deltas were 2–8 characters every 10–40 ms, with a 1–3 s pause about every 300 deltas: 227 s and 174 s of simulated streaming.revisionandsequencenumbers (283 vs 46, for example), not content.After the merge.
src/main/{codex,claude,native-chat,jsonl-rpc,pi,acp,runtime/orchestration}): 1,169 files and 10,858 tests pass.Carried from before the takeover. None of this was rerun on the merged head.
Risk
The riskiest part is saved chat history. A wrong gate or ordering could save a reply out of order or drop its tail.
Back out: a plain revert. Records already saved stay readable, and cleanup reclaims the extra revisions.
Level: Medium
AI Disclosure
Review
Takeover delta (merge resolution, removed tests, extracted owner check), reviewed against main
b6467f0with #36053 and #37216:Before the takeover, the cadence, ordering and plan-scheduling design went through several independent review rounds. The earlier body records them; they aren't repeated here.
Agent skill upstream boundary
Notes
Ensure no issues in: Security, Cross-platform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)