Skip to content

fix(history): tell the user when an older-history page fails to load (#1250 row 12) - #1413

Merged
Juliusolsson05 merged 7 commits into
mainfrom
fix/load-older-history-failure
Sep 27, 2026
Merged

Juliusolsson05 merged 7 commits into
mainfrom
fix/load-older-history-failure

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Refs #1250 (row 12). #1250 is a batch issue, so the other rows stay open.

Problem

Scrolling to the top of an agent feed pages in older history. When that page failed (the IPC rejected, or the transcript could not be read), useHistoryActions.loadOlderHistory only warned to the console and cleared loadingOlderHistory. It returned nothing, so no caller could tell a failure from a load, and the feed simply looked as if it had nothing older.

Change

  • loadOlderHistory answers 'loaded' | 'skipped' | 'failed' (OlderHistoryLoadResult).
    • failed comes only from the catch, which leaves hasOlderHistory set so the next scroll to the top retries.
    • skipped is every early return.
  • TileLeaf shows a pane toast on failed: "Couldn't load older messages. Scroll up again to retry."
    • Fixed words (q22): the error can name a transcript path.
    • Coalesced to one per 5 s per session, because every scroll tick near the top retries. The cooldown is keyed by session because a dispatch lane re-renders the same leaf for another agent (q106).
  • Main, older paging only: an unreadable transcript (a stat, open or read failure in readOlderTranscriptWindow) or one the resolver can no longer find (loadOlderHistoryChunk, e.g. a Codex rollout) now rejects instead of paging an empty hasMore: false. So it is a failed, retryable page. An empty file is still an empty page, and the initial-chunk reader is unchanged.
  • Feed: an upward wheel or a downward touch drag at scrollTop 0 also requests the older page, so "scroll up again" works at the top, where no scroll event fires.
  • AgentFeed / Feed keep onLoadOlderHistory: () => Promise<void>. The phone is untouched.

Plan: docs/plans/2026-09-27-load-older-history-failure.md.

Tests (red on main)

  • history.renderer.test.tsx: a rejecting page read gives failed and keeps hasOlderHistory; a page gives loaded; a missing marker gives skipped.

  • New TileLeaf.olderHistory.renderer.test.tsx: Feed is probed for the REAL onLoadOlderHistory prop TileLeaf passes through AgentFeed. A failure toasts the fixed text once per burst and again after the window; loaded and skipped toast nothing. A lane switch to another agent within the window still toasts that agent's failure.

  • historyLoader.test.ts: an older page of a transcript removed after its first page rejects with ENOENT; an empty file stays empty.

  • New Feed.olderHistory.renderer.test.tsx: the real Feed. Two upward wheels at the top make two requests; a downward wheel makes none. A downward finger drag makes one; an upward drag makes none.

  • historyLoader.providerOwned.test.ts: an older Codex page whose rollout can no longer be resolved rejects.

Verification

  • rm -rf .tsc-out; npx tsc -b: clean.
  • NODE_ENV=test npx vitest run src/main/sessions src/main/remote src/renderer/src/features/feed/ui src/renderer/src/workspace/hook/actions plus the TileLeaf older-history test: 499/499.
  • Review round 1 and verification: the dispositions are in the PR comments (the review is now capped).
  • The app was not launched.

🤖 Generated with Claude Code

Juliusolsson05 and others added 2 commits September 27, 2026 04:29
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1250 row 12)

loadOlderHistory now answers loaded/skipped/failed; TileLeaf shows a fixed,
coalesced pane toast on failed. hasOlderHistory stays set so the next scroll
to the top retries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05 Juliusolsson05 added type:bug Something works wrong class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor labels Sep 27, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Steering q106 fixed (e905104e). The older-history toast cooldown was one timestamp per mounted TileLeaf, and a dispatch lane re-renders the same leaf for another agent. So agent A's toast silenced agent B's first failure for 5 s. It is now keyed by sessionId. New same-leaf rerender test: A toasts, the lane switches to B, B toasts, and B's repeat coalesces. It fails with the per-leaf timestamp. tsc is clean. This is targeted, so there is no new review round; reviewers a, b and c are reviewing e9c9c980, and this commit changes only the cooldown key.

🤖 Generated with Claude Code

…view a, b)

Also records in the plan that e905104 carried the loader fix
(an unreadable transcript now rejects an older page instead of paging
it as empty).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Review disposition, round 1 (head f72f8670)

Correction first: my q106 comment said e905104e changed only the cooldown key. That is wrong. A background commit swept in the loader fix below as well (historyLoader.ts and its test), and the commit message names only q106. The plan now records it.

Verdicts: a FIX-BEFORE-MERGE, b FIX-BEFORE-MERGE; c's report is pending.

Finding Disposition
a1: an unreadable transcript was paged as an empty page with hasMore: false, so the feed silently lost "older exists" Fixed (e905104e). The older-page reader (used only for paging) now rejects on a stat, open or read failure, so the page is failed and retryable. An empty file is still an empty page, and the initial-chunk reader is unchanged. The test (the first page loads, the file is removed, the older page rejects with ENOENT) fails on the old loader.
a2 / b1: "Scroll up again to retry" was impossible at scrollTop 0 (no scroll event) Fixed (f72f8670). An upward wheel at the top makes the same request. The real-Feed test (two upward wheels at 0 make two requests; a downward wheel makes none) fails on the old Feed and kills a's loadingOlderRef survivor.
b2: the cooldown was shared across agents in one lane Fixed (q106, e905104e).
b3 (minor): the raw error reaches console.warn and the perf span Declined. These are developer diagnostics, not user-visible text, and q22 governs what the user sees. The perf journal records the file path on this path by design (finishOlderChunk).

Verification:

  • npx tsc -b: clean.
  • src/main/sessions, src/main/remote, features/feed/ui, workspace/hook/actions and the TileLeaf older-history test: 494/494.

🤖 Generated with Claude Code

… target pane (#1413 review c)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Reviewer c: MERGE-READY, four minor test gaps, all pinned in e349c814. Every early return is asserted skipped (it.each plus the no-meta and no-provider-session cases). A retry 1 s into the window stays suppressed, so a 500 ms window now fails. The toast text is asserted as the literal sentence and the target pane is asserted. c's surviving mutations M7 and T3 now fail. Verification a and b are running on f72f8670; this commit adds tests only.

🤖 Generated with Claude Code

#1413 verification b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Verification b: FIX-BEFORE-MERGE, fixed in 5ef56d77. The touch form of the retry gesture was missing: a downward finger drag at scrollTop 0 fires no scroll event. It now makes the same request as the upward wheel. The real-Feed test (an upward drag makes none, a downward drag makes one) fails on the previous Feed. tsc is clean.

🤖 Generated with Claude Code

…1413 verification a)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Verification a: FIX-BEFORE-MERGE, fixed in fd5c2ef9. A Codex rollout the resolver could no longer find still made an older page an empty hasMore: false: loadOlderHistoryChunk returned that before reaching the strict reader. An older page (only ever asked for after one loaded) with an unresolvable transcript now rejects, so it is a failed, retryable page. The phone's RemoteServer already turns a throw into a structured ok: false. The new test (Codex, resolver returns null) fails on the previous loader.

Verification: npx tsc -b is clean. src/main/sessions, src/main/remote, features/feed/ui, workspace/hook/actions and the TileLeaf older-history test: 499/499.

Round count: round 1 and verification are done, so the review is capped. The PR waits for CI and the manager gate.

🤖 Generated with Claude Code

Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
@Juliusolsson05
Juliusolsson05 merged commit 5ce0e29 into main Sep 27, 2026
2 checks passed
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
@Juliusolsson05
Juliusolsson05 deleted the fix/load-older-history-failure branch September 27, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor type:bug Something works wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant