fix(desktop): preserve message visibility across transcript navigation - #5001
Conversation
1bbe27a to
40d4bd9
Compare
Complete bookmark restoration once per Session activation and separate history reading from following the latest Turn. Admit navigation before asynchronous work so stale pages and recovery responses cannot replace the range selected by a newer action. Protect the current and newly loaded neighboring Turns while paging in either direction. Preserve the identity of a live Turn until RuntimeEvent persistence supplies its sequence, and settle completed overlays without allowing a newer oversized Turn to evict the reader's selected history. Keep local Message admission independent of history loading and move reading-position coordination into the conversation feature. Cover oversized history, navigation races, recovery, and live-to-durable Session switching with regression tests. Generated-by: OpenAI Codex
40d4bd9 to
5859542
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for taking this on. #4990 affects basic conversation continuity, so please prioritize getting this fix landed quickly. The main direction is right: background persistence must not create navigation intent, and a completed bookmark restoration must not restart on message updates.
Reviewed head: 5859542c. There is one P1 regression below to fix before merging. Please keep the revision focused rather than undertaking a broader navigation rewrite.
For the design, I suggest keeping one clear owner for each decision: Conversation accepts navigation intent, the replica applies that intent to its resident range, and the range store commits message updates. Background writes and layout changes should update facts or geometry without creating a competing navigation command.
A few focused simplifications would make this easier to maintain and validate:
- Keep range updates on the existing
store.accept → applyTranscript → setMessagespath. Remove the duplicatesetMessagescalls after send preparation and bookmark restoration, the fallback for controllers withoutsetReadingAnchor(the production caller requires it), and unused command/facade exports. Preserve stale-operation checks, adjacent-turn protection, overlay settlement, and the snapshot baseline required by the current delivery protocol. - Test user obligations with the smallest representative data. The exact 233-record incident shape is not itself a contract. Reduce that fixture while retaining the real SQLite/RuntimeEvent/Host reader path and the oversized-turn, sparse-watermark, and live-to-durable boundaries. Demonstrate failure for the expected behavioral reason on the affected implementation; failure because an older version lacks a newly introduced helper is not that evidence. Also distinguish the adjacent-turn paging protection already present in the current base from this PR's new fixes.
- Choose the lowest sufficient test layer: first decide whether a test adds necessary protection, then prefer unit/integration tests, then existing Storybook coverage for actual browser geometry. Use Electron only for a concrete boundary those layers cannot verify. The new spec's end-to-end composition alone does not establish that requirement. Either identify that Electron-specific gap or move its remaining obligations down and remove the spec and its exclusively used support. Existing transcript geometry stories already provide a starting point.
Could you prioritize the P1, then make the focused simplifications above where the existing behavior can be preserved? There is no need to redesign the whole navigation stack or hit an arbitrary diff-size target. We would like to move this fix forward promptly, with a smaller, clear set of user-behavior regressions.
Review prepared with OpenAI Codex, using source inspection and targeted boundary reproductions. The full suites and Electron scenario were not rerun during this review.
Route coordination tail completion through the range controller so request versions and renderer expectations advance together. Ignore stale batches before updating loading state and retain the active guard across a range reset, including fragmented responses and sparse durable watermarks. Cover the consumer handshake through the real preload and verify that tail completion converges after one request. Generated-by: OpenAI Codex
Publish messages only through accepted transcript subscriptions. Remove completion-time projections, unsupported controller fallbacks, and unused navigation exports while preserving restoration and stale-operation guards. Replace the incident-sized fixture with small real SQLite and RuntimeEvent cases using message-ID checkpoints. Verify complete, unique durable answers after reopening, and move send-from-history geometry into the existing Storybook coverage. Remove the redundant Electron scenario and its exclusive fake-backend support. Generated-by: OpenAI Codex
Use the store's stable Session identity when preparing a send so the latest navigation supersedes queued bookmark restoration before transcript open finishes. Keep message admission independent of background range loading. Cover sending during initial open, rejection of late history frames, and previous-Session controllers both before and after initialization. Generated-by: OpenAI Codex
Track the last live generation separately from the displayed cached snapshot so reopening a surviving replica can resume live delivery. Retire that generation only when a different live replica replaces it, while continuing to reject departed cache generations and stale navigation frames. Cover repeated cached reloads into the same live generation, subsequent live updates, and rejection of old resets and deltas after a real replacement. Generated-by: OpenAI Codex
Fixed P1 in a2acf15.WorkHub tail completion now uses the shared range controller’s The callback checks Added a regression using the real WorkHub port, store, controller, and preload, with stubbed IPC replies. It fails before the fix and now verifies exactly one tail-completion request, one complete projection, and no partial publication caused by rejected stale batches. Addressed the focused simplifications in bcb754b:
I also checked the behavioral reproductions against base At the current head, build, lint, format, typecheck, both Knip checks, architecture/inventory checks, and all 2,451 Desktop tests pass locally. |
Keep the navigation controller and testing entry point alongside main's composer attachment exports. Regenerate the renderer and surface inventories from the combined source. Generated-by: OpenAI Codex
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for addressing the WorkHub regression and simplifying the implementation and tests. Reviewed e0dd860993246e5a083fa77f4feb93f9d89b86f7, including the main integration. The original P1 is resolved; the follow-up authority, navigation, recovery, consumer, and test reviews found no remaining P0-P2 issues.
Validation on this head: builds and typechecks passed, 116 targeted Desktop tests and 3 UI scroll tests passed, and five Storybook transcript scenarios passed in both light and dark with browser geometry/console checks. Required GitHub checks must pass before merging.
Review and validation performed with OpenAI Codex; merge authorized by the maintainer.
The CI run on 11aa81e failed the renderer-architecture gate twice: the regression test imported `WorkbarIngestInput` straight from `ports.js`, which only `index`/`testing` re-exports may reach from feature code, and the `allowAttachmentOnlySend` line grew the frozen `app-shell.tsx` token budget by one. The type now ships through the workbar `testing.js` entry, and the side-chat panel — not the frozen shell — opts into attachment-only sends, which is where the apache#4804 acceptance scenario actually sends from. The branch also merges the current `main` (apache#5001 included), so the frozen-file budget is evaluated against the live baseline. Generated-by: GLM-5.3-Flash (ZCode)
|
Thanks for the focused fixes and simplifications. We have now completed additional post-merge validation on The checks went beyond the happy path: a ~900 KiB tool-result turn, 18-turn pagination in both directions, history navigation during background streaming and durable completion, rapid session switching, completion while another session is active, injected transcript-open failure, delayed history responses after newer navigation, and a full application/Host restart against the same persisted data. 17 behavioral checks passed. The selected history remained available during background updates; delayed results did not override the newer session selection; and completed answers remained complete and appeared once after reopening and cold restart. The 3 relevant WorkHub tests and 2 Runtime abort/error tests also passed. We did not reproduce #4990 or find another confirmed regression in this validation. The previously reported WorkHub P1 is resolved, and we have no remaining blocking findings for this PR. One explicit boundary: execution used the repository's FakeBackend. Its hold-open/stop scenario does not emit a final text record, so persistence of interrupted answers with a real provider remains unverified; we are not counting that scenario as a pass or attributing it to this PR. An initial fixed-delay tail-position check sampled before layout settled; the subsequent geometry-based check passed. No permanent Electron tests were added. Thank you for addressing the navigation authority issue while reducing the duplicate publication paths and test fixtures. This gives us confidence in the fix for the reported visibility regression. |
Upstream 5a1514c..93a8dd7. Backend, preload and shared take upstream verbatim. Renderer adaptations: - apache#5001 transcript navigation: `lib/ported/desktop-transcript-range-store.ts` and `lib/ported/transcript-reading-position.ts` re-ported from the new upstream versions (navigationVersion admission, retired generations, the restore lifecycle, prepareTranscriptForSend). `store/active-session-store.ts` owns what the old React controller did: one restore lifecycle per selection, `prepareSend` (called from the send path, never awaited), `setReadingAnchor` → controller `setReadingAnchor`/`loadLatest`, gate replacement on `latest`, and one `createTranscriptViewportNavigation()` passed to `useChatScroll`. - apache#4551 coded settings results: memory, config-import, permission and health surfaces resolve Host codes through the upstream catalogs (`capability-reason-copy.ts`, `settings-memory-copy.ts` `results`, `importFailures`, `reasonFallback`); Copilot `SubscriptionActionCode`s added to `settings-models-copy.ts` and read first by `oauthFailureMessage`. Enterprise-only memory copy keys re-applied over the upstream file. - Old-renderer / Astryx / stories / WorkHub files re-added by rename detection deleted again; upstream main tests retargeted to `lib/ported/` where they only need the ported modules, deleted where they drive the removed React controller (recorded in the release checklist). - e2e fixture now collapses CSS transitions (`[data-maka-e2e-fixture]`), which the scroll-cost spec assumed but nothing implemented; the message action bar's hover fade made that spec flaky. Gates: build, typecheck, lint, format, asf, locale hygiene, renderer architecture, knip (per workspace), renderer-state 162, desktop dist 1470, ui dist 208, renderer smoke 43 checks, e2e 28/28, real-window smoke, root npm test all workspaces. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s main After the upstream apache#5001 port, sending into an existing task rendered nothing — not the user's message, not the stream — until the reply had finished. Main answers a `followTail` navigation by re-reading the tail and settling every overlay message up to the sequence it captured on arrival, and withholds every transcript batch from the consumer while it does. The send's IPC left before the tail command's (the range controller awaits its open handle first), so the turn had already started, the captured sequence covered the streaming reply, and the settlement — and the blackout — lasted the whole turn. `turnActionsStore.send/submit` now await `onFollowLatest` and yield one macrotask before the send admission so the command's invoke leaves first; `prepareSend` still resolves as soon as the command is issued, never on the tail load. Renderer-state test covers the ordering. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Fixes #4990
Preserve conversation visibility when background persistence, bookmark restoration, and explicit navigation overlap. Conversation owns navigation intent; the replica applies it to its resident range, and accepted transcript batches remain the single message publication path.
Remove duplicate completion-time message publication, obsolete fallback/export paths, and the incident-sized fixture. Keep representative real SQLite/RuntimeEvent/Host reader coverage. Real geometry coverage extends existing Storybook stories; the added Electron spec and its exclusive support have been removed.
Verification
After merging current main into the PR at
e0dd86099:The full repository test suite was not rerun locally during the merge review. Current-head CI is reported in GitHub checks.
AI use
OpenAI Codex contributed to implementation, regression tests, independent review, conflict resolution, validation, and PR drafting. Retain the
Generated-by: OpenAI Codextrailer when squash-merging.Checklist