feat(app): paginate long histories and recover stalled sessions - #90
Merged
Conversation
Opening an existing conversation could fail with errors from the vendored codex thread-store: "unknown variant `completed`" for a stored SubAgentActivity item, and "invalid paginated history lineage: cycle detected". Neither was our bug. Codex Desktop writes ~/.codex with a newer schema than deps/codex was pinned to, so our reader rejected records whose variants it lacked. Upstream already carries the reads: SubAgentActivityKind::Completed (openai/codex#40437), RolloutItem::RealtimeItem (#40508), and SessionMeta.forked_from_ordinal_exclusive (#40987), which decouples a thread's logical fork boundary from its physical history_base so a paginated thread survives revert. Merged rather than rebased, matching the two prior upstream syncs. The Windows console-suppression and ConPTY adaptations carried over without conflict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paginated threads reject a whole-history read, so opening one failed outright after the codex bump. They now load a bounded tail plus a skeleton of every turn, and the transcript fills in on demand: - `thread_read` branches on `historyMode`. Paginated threads walk `thread/turns/list` + `thread/items/list`; legacy threads keep the whole-history read, which is the only shape their rollout supports. - New `thread_older_page` / `thread_turn_items` continue backwards and fetch one turn, for scrolling up and for jumping via the rail. - Cursor walks copy upstream's `advancing_cursor`, so a server that repeats a cursor ends the walk instead of looping forever. - Items come from bounded item pages, never `itemsView: "full"`, which makes the server walk each turn's items in nested loops inside one request — a turn with hundreds of items blew past the request timeout. - `thread_summary` reads a summary view instead of the whole transcript for one sentence. The turn rail now draws from the turn skeleton rather than the loaded rows, so a long conversation shows its true length immediately instead of growing as history arrives. Hovering an unloaded tick prefetches it; the skeleton already carries the preview text, so previews cost nothing. Also lands the log plumbing that made this diagnosable: captured lines mirror to `<support>/logs/` with 6-hour retention, and every JSON-RPC call records its direction, duration, size and in-flight depth. Three rounds of reading code had blamed the wrong thing each time; the log found the real stall on the first try. `design/HANDOFF-thread-list-stall.md` documents an unrelated outage found along the way — `thread/list` never answers on a long-lived adopted app-server, so the client times out and reconnects every 72s. Root cause unconfirmed; the doc records the evidence, the leading hypothesis, and the dead ends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 378c2d267b
ℹ️ 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".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Long conversations now load history a page at a time, and failed app-server requests no longer leave the UI reporting a dead connection as ready. This PR completes the follow-up work from the thread-list stall handoff:
crates/pocket-codex-bridge/src/engine/app_session.rsandcrates/pocket-codex-bridge/src/api/bridge.rs:577.initializeplusthread/listwithin one budget. Seecrates/pocket-codex-codex/src/client.rs:253,crates/pocket-codex-codex/src/readiness.rs:244, andapps/flutter/lib/src/screens/app_session_screen.dart:1179.crates/pocket-codex-bridge/src/engine/account.rs:500.apps/flutter/lib/src/widgets/middle_click_scroll.dart:13andapps/flutter/lib/src/widgets/turn_minimap.dart:371.The branch incorporates current
main, including its Windows release-build stability fix. The Codex dependency update from #89 is already onmainand is not repeated in this PR's diff.Review follow-up
The initial eight review findings are addressed. History pages now merge by turn order with shared item IDs anchoring overlapping pages. A partially loaded turn remains navigable, selection waits for an in-flight prefetch and list layout, and request generations prevent stale completions from affecting another conversation. Programmatic rail jumps do not trigger older-page loading that shifts the target. Live turns join the snapshot-based rail and count.
All enumerated turn timestamps are retained for initial, older-page, and single-turn reads. Summary calls use an async FRB entry point and a separate blocking executor, so they occupy no interactive FRB worker slots even on a single-core device. Logs rotate hourly and prune every minute, including while idle in the tray. Ten Flutter tests and four Rust tests cover these cases.
Type of change
feat— paginated history and middle-button autoscrollfix— connection recovery, relay renewal, and turn-rail hoverUserspace impact
crates/pocket-codex-bridge/src/api/.The bridge adds older-history/turn-item operations and history metadata; generated Rust and Dart bindings are included together. Legacy threads retain their existing history-loading path. Diagnostic logs are written under the application's support directory with six-hour retention. No user migration is required.
Hosted mode still uses the backend for credential issuance and renewal; this PR does not persist relay credentials across app restarts or change their TTL or TLS validation. Session data continues to travel directly through the relay.
Tested
-- --config newline_style=Autofor the existing Windows CRLF checkout. No formatting or linting ofdeps/.cargo clippy --workspace --all-targets --locked -- -D warnings.cargo test --workspace --locked— 293 passed, 6 ignored after the review fixes.cargo check -p pocket_codex_bridge --locked, exercising the embedded Codex path.dart format --output=none --set-exit-if-changed lib test integration_test, and analyze.pubspec-desktop.yaml; verified bundled fonts, themes, close-to-tray behavior, and single-instance restore. The latest Release launches and displays real account history.pubspec-desktop.yamlcompleted successfully, including the regenerated async bridge and embedded Codex. Verified Figtree, Geist Mono, and Noto Sans SC in the output. The default mobile manifest was restored after the build.git diff --checkpasses.The new middle-button and hover behaviors are covered by widget tests, including the real session screen and virtual list. Native pointer automation was stopped when it detected active user input, so a physical mouse verification of those two interactions is not claimed.
The original long-lived Mac process's internal
thread/liststall was not reproduced locally, and its root cause remains unconfirmed. Regression tests do reproduce dead RPCs despite Pong traffic, unhealthy process adoption, misleading reconnect readiness, pagination defects, and stalled/failed credential renewal. Details and reproduction instructions are indesign/HANDOFF-thread-list-stall.md:1. Local history copies and validation logs are excluded from the commit.Linked issues
Follow-up to #89 and
design/HANDOFF-thread-list-stall.md.Sync checklist
unwrap()/expect()added.deps/.