Skip to content

fix(desktop): preserve message visibility across transcript navigation - #5001

Merged
Astro-Han merged 6 commits into
apache:mainfrom
chinawch007:fix/transcript-navigation-ownership
Sep 8, 2026
Merged

fix(desktop): preserve message visibility across transcript navigation#5001
Astro-Han merged 6 commits into
apache:mainfrom
chinawch007:fix/transcript-navigation-ownership

Conversation

@chinawch007

@chinawch007 chinawch007 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

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.

  • Restore a saved reading position once per Session activation. Message updates cannot restart a completed restore.
  • Preserve the selected history range during background durable updates. Retain the existing current/adjacent Turn paging protection across catch-up, including oversized Turns.
  • Reject obsolete pages, fragments, and resets using navigation versions, while retaining ACK and recovery handling.
  • Follow latest when sending without delaying local message admission; subsequent reader navigation can supersede the pending catch-up.
  • Settle persisted streaming overlays independently of the visible range.
  • Route WorkHub tail completion through the same range-controller admission path; preserve live generation identity across provisional cached reloads.

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:

  • Affected workspace dependencies and Desktop main/preload/renderer builds passed.
  • Desktop typecheck, Biome format/lint, renderer architecture against main, surface inventory, and AppShell hook checks passed.
  • 116 targeted Desktop tests and 3 UI scroll tests passed, with no failures or skips.
  • Fresh Storybook build: five transcript stories passed in both light and dark (10 runs), including send/stream tail-follow, reader-scrolled-up, oversized Turn anchoring, and history insertion at/near the top. Browser assertions, DOM geometry, console checks, and accessibility-tree checks passed in headless Chromium.
  • Fixed-source behavioral comparisons demonstrate the background-history replacement failure on base and the WorkHub navigation loop on the previous PR head; both pass after the fixes. Adjacent-Turn paging already passes on the base and is retained coverage, not a newly discovered base regression.

The full repository test suite was not rerun locally during the merge review. Current-head CI is reported in GitHub checks.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

OpenAI Codex contributed to implementation, regression tests, independent review, conflict resolution, validation, and PR drafting. Retain the Generated-by: OpenAI Codex trailer when squash-merging.

Checklist

  • Tests cover the changed behavior; the key failures have behavioral before/after evidence
  • Local build, typecheck, lint/format, and targeted checks pass
  • Behavior changes are described above

@github-actions github-actions Bot added the effort/XXL Over 2500 readable lines label Sep 7, 2026
@chinawch007
chinawch007 force-pushed the fix/transcript-navigation-ownership branch from 1bbe27a to 40d4bd9 Compare September 7, 2026 20:04
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
@chinawch007
chinawch007 force-pushed the fix/transcript-navigation-ownership branch from 40d4bd9 to 5859542 Compare September 7, 2026 20:11

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 → setMessages path. Remove the duplicate setMessages calls after send preparation and bookmark restoration, the fallback for controllers without setReadingAnchor (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.

Comment thread apps/desktop/src/preload/preload.ts
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
@chinawch007

Copy link
Copy Markdown
Contributor Author

Fixed P1 in a2acf15.

WorkHub tail completion now uses the shared range controller’s loadLatest(), so navigation-version allocation and the store’s expected version advance together before dispatch.

The callback checks store.accepts(batch) before changing reset/ready state. This keeps admission separate from accept() returning false for a valid incomplete batch. The in-flight loading guard also survives the request’s own reset replies, preventing repeated completion requests with fragmented responses or sparse durable watermarks.

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:

  • Message publication now stays on the existing store.accept → applyTranscript → setMessages path. Removed the duplicate setters after send preparation and bookmark restoration, the revision bookkeeping used only by those setters, the fallback for controllers without setReadingAnchor, and unused command/facade exports. The remaining stale-operation guards, adjacent-turn protection, overlay settlement, and protocol snapshot baseline are preserved.

  • Replaced the incident-sized fixture with smaller representative cases and message-ID checkpoints, retaining the real SQLite → RuntimeEvent → Host reader path. Coverage still exercises oversized turns, sparse watermarks, and live-to-durable settlement, including complete, non-duplicated answers after reopening.

  • Moved the send-from-history geometry check into the existing StreamingTailFollow Storybook story and verified it in headless Chrome. Removed the new Electron spec and its exclusive fake-backend support; loading, navigation, and persistence obligations remain covered by unit/integration tests.

I also checked the behavioral reproductions against base 00f2c9e. Adjacent-turn paging already passes there. The two regression checks fail because persistence replaces the reader’s selected history range and message updates revive a completed bookmark restoration; both pass with the fix.

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 Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@Astro-Han
Astro-Han merged commit 8b3655a into apache:main Sep 8, 2026
2 checks passed
ggbdpq added a commit to ggbdpq/maka that referenced this pull request Sep 8, 2026
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)
@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for the focused fixes and simplifications. We have now completed additional post-merge validation on 8b3655a1710a4c52b09759249d8c2dbb57284a7a, using a freshly built Desktop app and CDP against an isolated workspace.

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.

Shouly pushed a commit to Shouly/maka that referenced this pull request Sep 8, 2026
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>
Shouly pushed a commit to Shouly/maka that referenced this pull request Sep 8, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XXL Over 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(desktop): follow-up messages become invisible, and switching sessions leaves conversation history incomplete

2 participants