fix(sidebar): restore the in-flow "Show all older sessions" row - #8720
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS A documented reversal of an over-aggressive de-clutter, with the rationale for both controls now pinned in a code comment so it won't be re-removed. [DESIGN-REVIEWED] f5842a1 |
UX Review (Fable 5) — 🟡 CONCERNSUX-level review of UX-Verdict: CONCERNS Keyboard activation of the new row strands focus: the button unmounts itself, so the user who just asked "where did my chat go" also loses their place. Watch
Suggestions
[UX-REVIEWED] f5842a1 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsNo candidates were proposed by the discovery pass, and none survive to add. The diff is a UI-only change: an always-rendered (when the pane is closed) in-flow "Show all older sessions" button dispatching the same action as the existing footer, the matching i18n key across all catalogs, a dev-only screenshot harness, and a test. I re-derived the behavioral surface — the hint's action path ( No findings. [OPUS-REVIEWED] f5842a1 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of First-Principles-Verdict: CONCERNS The fix's own worst case is uncovered: a list-view sidebar emptied entirely by eviction renders no row, because the hint mounts inside the ungrouped bucket's existence guard. What this change shipsIntent: give a user who scanned to the end of the sidebar without finding their chat an in-flow pointer to Older Sessions — a FIX for reported defect #8710.
Watch
[FIRST-PRINCIPLES-REVIEWED] f5842a1 |
The text row below the last session of a lane opens the Older Sessions pane in place. It was removed as a duplicate of the pane's footer, but the two answer different questions: the footer is a persistent control for a user who already knows the pane exists, while the in-flow row sits where a user scanning the list runs out of rows without finding their chat -- exactly where a session evicted from the open-tab list (idle eviction, restart, cleanup, a closed tab) has gone. A new user has no other cue that sessions move anywhere, so removing the row removed the only in-flow answer to "where did my chat go". Restore it in the list-view root lane and in flat view, placed after the dormant-sessions expander so it stays the lane's last line, and hidden while the pane is open. The catalog key comes back in all 12 languages. Closes #8710
52ddbe3 to
f5842a1
Compare
Closes #8710
Summary
Restores the in-flow "Show all older sessions" row that follows the last session of a sidebar lane (list-view root lane and flat view). It opens the Older Sessions pane and fetches history — the same action as the footer — and is hidden while the pane is open.
The row was removed in #1211 as a duplicate of the Older Sessions footer. The two answer different questions: the footer is a persistent structural control for a user who already knows the pane exists; the in-flow row sits where a user scanning the list runs out of rows without finding their chat — exactly where a session evicted from the open-tab list (idle eviction, restart, cleanup, a closed tab) has gone. A new user has no other cue that sessions move anywhere, so removing the row removed the only in-flow answer to "where did my chat go". Details in #8710.
What changed
ChatSidebar.tsx: onerenderOlderSessionsHint(lane)helper, mounted in the flat lane (after the hidden-folders reveal row) and in the root lane's ungrouped bucket after the dormant-sessions expander, so it stays the lane's last line even when rows are folded. Hidden whilehistoryOpen.pages.chatSidebar.show_all_older_sessionsrestored inen.manual.jsonand all 11 translations (ja/konewly authored, consistent with their existing "older sessions" wording);en-XAregenerated withnpm run i18n:pseudo.ChatSidebar.olderSessionsHint.test.tsx: last element of the root bucket after the dormant expander; opens the pane and calls the sessions API; hidden while the pane is open via the footer and back when it closes; same for the flat lane.scripts/capture-older-sessions-hint.mjs(real built SPA, stubbed API).Footer unchanged. No backend change.
Screenshots
Root lane, light — the row follows the dormant expander:
Root lane, dark:
After clicking it — the pane opens with the closed sessions, the row is gone:
Flat view, dark:
Pattern harvest
Rule candidate: a "remove as duplicate" refactor must first name the distinct user context each of the two affordances serves, not just their shared action. #1211 deleted the in-flow row purely because it fired the same handler as the footer, treating equal behavior as equal purpose. But an affordance's value is where it appears, not only what it does: the footer answers "open the pane" for a user who already knows it exists; the in-flow row answers "where did my chat go" for a user who has scanned to the end of the list and found nothing. A dedup that collapses two placements into one silently removes discovery for the second context.
Gate this class points at: the reviewer/design-lane checklist for a deletion labeled "duplicate" — require the PR to state the user journey the removed instance covered and confirm a surviving instance covers the same journey, not merely the same code path. Not expressible as a semgrep/static rule: "same handler, different discovery context" is a UX-intent judgement, not a syntactic pattern in the diff. The regression-guard is behavioral: the new
ChatSidebar.olderSessionsHint.test.tsxpins the in-flow row as the lane's last line after the dormant expander and asserts it opens the pane, so a future dedup that removes it again reddens a test instead of shipping silently.Verification
Static gates run locally (this desk does not run test suites — CI is the test oracle):
npx tsc -bclean;npx eslinton the changed files clean;npm run i18n:checkgreen.origin/main.