Skip to content

Tracking: Sessions tab — finding, identity and readability #144

Description

@grimmerk

One place to see what the Sessions tab still needs, in what order, and what is blocked on a decision rather than on work.

Why this exists. The ordering used to live in three committed documents, and they drifted within a day of a feature merging — one still described shipped work as upcoming, two disagreed about what was a blocker, and six open issues were missing from a hand-maintained list. So the split here is deliberate and follows how long a statement stays true:

  • Order, priority, "is this done" → this issue, because it can be edited without a pull request.
  • Why A must precede B, how a mechanism works, why an approach was rejecteddocs/session-finding-plan.md, because it is reviewed and versioned with the code.
  • The detail for one item → that item's own issue, pointed at rather than restated.

Shipped (do not redo)

PR What Version
#132 Full-prompt search across every session, match #N snippets, amber highlight, minor-session fold v1.0.83
#136 Pins + manual hide (zone only), session-marks.json store
#137 Pin browse modes: recency order inside the zone, collapse became ungroup rather than hide, only chip scoping both list and search v1.0.85
#139 Row readability: titles shortened from the middle (~60 chars, was a 35-char hard cut) with full title on hover, search window moved to the match, match #N chip recoloured to tie it to the highlight v1.0.86
#147 Live view + saved session lists: ● live scope (ps joined against the registrations; ⚠ unregistered, ⚠ 2nd process), stats toggle, save list… / 🗂 with rename & delete, the transcript recap on a member row, session-id prefix search, a scope survives resuming from it. Shared atomic-json-store.ts. 138 tests. Merged 2026-09-05 v1.0.87 (unreleased)
#151 Query operators, PR references in any spelling, persisted enrichment cache, ▶ open N: title: branch: msg: project: account: recap: has: `is:live pinned after: before:; #N/pr:N/owner/repo#N/ URL with three strictness levels; assistant replies mined for PR references (in-process, incremental by byte offset);~/.config/codev/enrichment-cache.json` + one background pass; open the not-running members of a saved list. Closed #140 #134 #145. 5 review rounds, 32 threads. Merged 2026-09-06
#152 A result explains itself and can be walked, tty-first switch, memory chip, resizable window: up to 20 hits per session with ‹ k/N ›, ▸ context (the prompt before/after), by match chip, match path / match assistant / match recap / match reply lines; iTerm2 / Terminal.app switch by tty when the pid is registered, title when it was guessed (terminal-switch.ts, isGuessedPid), ·ttysNNN tag on same-titled live rows; swap / pressure chip; normal-mode window resizable with remembered bounds, width-aware line caps, reset, menu-bar mode re-asserted; tooltips drawn by the app; stale working shown idle after 10 min. Design doc opens with three mermaid flow diagrams (list + detection, open, switch). Closed #110 #141 #146 #148; #142 C0 done. 4 review rounds, 34 threads. Merged 2026-09-06 v1.0.89
#154 The match … markers are outlined, not filled: at up to four per row they competed with the highlighted text they point at and could not be told apart from it, since both were the same solid amber. Now amber-outlined pills, the shape the PR / account / terminal badges already use; display stays inline so no row is taller (measured: inline 0px, inline-block 2–8px). Merged 2026-09-08 v1.0.90

Measured on the reference machine 2026-08-20 — these numbers are why the above was built, and they do not need recomputing:

  • Titles were the biggest problem. 125 unique titles, median 44 characters, 64% longer than the old 35-char cut, and 38% written as A -> B > C chains where the newest progress is at the tail. 48 of 125 (38%) collided on their first 35 characters; the largest group was 8 sessions all rendering as the same 35-character prefix.
  • 39% of first prompts exceed 50 characters; 42% of last prompts exceed 40.
  • PR references: of 2,506 (session, PR number) pairs, only 19.4% appear in both forms. URL-only 38.3%, #N-only 42.3% — so 80.6% are findable by one spelling only.
  • Frecency does not replace pins. Only 3 of the frecency top-10 were outside the recency top-20, and of 7 real pins, 3 reached the top-10 while three others ranked Refactor6 renaming more #26 / Ghostty: no per-tab TTY/PID — missing cross-reference and TTY switch fallback #63 / feat: UX improvements — Settings redesign, custom shortcuts, hover fix #72. Pins are often low-activity but must-not-lose. Computable from existing history.jsonl — no click instrumentation needed.

Groups, in suggested order

A. Aim the query, resolve PR references, persist the index — ★★★

The five items below are one change economically: one parser, one grep pass, one index. Split apart, the same file gets rewritten three times.

title: is wanted early: titles get renamed often, so a bare query keeps matching body noise instead.

B. Session-list snapshots — ★★★

C. Session identity — ★★☆

D. Make a result legible and ordered — ★★☆

E. Preview / reader — ★★★ but expensive

F. Perf and small bugs — ★☆☆

G. Parked — candidates for closing

#90 · #93 · #107 · #108 · #111 · #118 · #121 · #127 · #128


Decisions taken (2026-09-05)

# Question Decision
1 Sort search results by match time by default? No — keep session last-activity as the default, add a toggle. Changing the default makes "find what I was just working on" harder, and that is the more common case.
2 #141: extra line naming the field, or tint the matched field in place? Reuse the existing match #N rule — add a line only when the hit is not already visible on the row. Vertical space is the scarce resource in a menu-bar popup.
3 Design stable task identity first, or fix group C's symptoms one at a time? Neither exclusively. C0 touches none of that abstraction and proceeds now; C1–C3 wait. No candidate designs have been drafted anywhere yet#142 and §4.7 state the problem, not a design. Drafting them is separate work and does not block C0.
4 Switch to /fork instead of the three-step rename? No. The upstream complaint about forking is that nothing comes back — no merge-back, no branch diff, no tree view. See the /fork version table in #142; note the semantics changed twice and any note about it must carry a version.
5 Should a snapshot capture pin / hidden state? Optional, and explicitly point-in-time. A snapshot shows what was pinned then; unpinning later does not rewrite the snapshot. That is the correct semantics for a snapshot, and it is why it must be stored beside the list rather than merged into it.
6 #134: persist the mtime cache, or let a full-text index subsume it? Persist the cache — this reversed on 2026-09-05 once full-text indexing was deferred (Open question 8). What group A now extracts is structured rows (sessionId, prNumber, url, timestamp), which need a small indexed table, not FTS. Widen the persisted file to carry those rows. FTS returns to the table only if question 8 is answered yes.
7 Start recording review-round calibration data? Being handled in a separate session — deliberately not tracked here, to avoid two places recording the same thing.

Open questions — still undecided

# Question Notes
8 Should assistant prose be full-text searchable, or only mined for PR references? Group A needs only the second (see #140). The first means a searchable copy of the corpus and is a much larger commitment; deferred for now, which is what flipped decision 6 back to persisting the cache. Answering this yes later re-opens 6. #159 measured the nearest cheap alternative and rejected it: indexing GitHub review/comment anchors costs +0.8 ms per full pass (+3.4%), so cost is not the objection, but an anchor names a position inside a GitHub page rather than a session, and it would not generalise to this question.
9 Is a live-session resource view worth building as its own thing? Resolved by PR #147: it is the LIVE half of the saved-lists screen, and a prerequisite for capturing what is open correctly.
10 Row numbering (1. 2. … per row) as a scanning / jump aid? Evaluated 2026-09-05, not now: recency order changes on every refresh so a number is a coordinate, not an anchor; ⌘1/2/3 already switch tabs; the row is dense. The real value is jump to the Nth result, which belongs with #146. Cheap alternative if keyboard reach is the ask: Home/End beside the existing PageUp/PageDown.
11 Resizable window in normal app mode Filed as #148; shipped in #152, both steps (resizable + remembered bounds, width-aware line caps, reset).
12 In the live scope, are per-row process figures noise when the scope is used as a plain "running only" browse? Yes — decided 2026-09-05, in PR #147: per-row figures sit behind a stats toggle, off by default and remembered; the total beside the search box stays; tty is never on the row. A noise-free is:live term in #140 remains the browse-only form.
13 Should the main list show a running session that has no history.jsonl line yet (a /branch child before its first prompt)? No for now (2026-09-05). It never did; the live scope now synthesizes a row for it, which is the workaround. The honest fix is the generation chain (#142 C2/C3); a blank interim row would be replaced by it. Repro and trade-off table in #149.
16 Should pr: gain an issue: alias? (#157) Issue references have been searchable in every spelling since #151 — GitHub numbers issues and PRs together, and (?:pull|issues) is in all four code paths — but the README, the section heading and the in-app ? sheet all say pull request, so the author himself did not know. Documenting it is certain; a second operator name for identical behaviour is the open part. Undecided (2026-09-15). has:pr stays PR-only either way: it is about the transcript's PR badge, which has no issue equivalent.
15 Grouping key for container folders (#153): ~/git is the largest "project" in the recent list (12 of 30) — cross-repo work, tests and quick one-offs started from the parent folder, plus ~/git's own sessions. One level by folder name, or sub-group by the session's repo context (sessionRepos from #151)? Undecided (2026-09-06). Also open: whether #153 and #142 C1/C3 (chain folding) share one "group" mechanism with two keys.
14 "Open all" for a saved list? Left out of #147 as too costly to do blindly. Now wanted (2026-09-05, after merge) for three real moments: after a reboot / macOS update, after closing every session to reclaim memory, and when switching terminal app. Constraints: open only members that are NOT running (idempotent), show the count and a projected memory figure on the button, stagger the launches, use the current terminal setting. Remaining scope of #145.

Facts worth not re-deriving

  • /branch copies the transcript to a new sessionId while the same process keeps writing — pid unchanged, sessions/<pid>.json updated in place, the parent left intact and still resumable. 21 of 88 transcripts on disk carry forkedFrom — 23.9%. Full evidence in sessions: /branch creates generation chains — 23.9% of transcripts, and CodeV cannot tell them apart #142; written up as docs/session-finding-plan.md §4.7 in PR docs: correct two assumptions a live experiment falsified #143.
  • The transcript is copied; history.jsonl is not. That one asymmetry produces all four symptoms — deep search finds the ancestor, messageCount under-reports, pins stay behind, and ordering lifts abandoned parents above the work.
  • Reading PR references with grep is not the expensive part. On the largest transcript in the corpus (62MB) a grep -oE sweep took 0.052s and returned 550 references after dedup. Enrichment currently reads each file three times; merging those into one alternation plus one PR sweep is two passes, i.e. cheaper than today. A hook would only cover sessions recorded from now on, and the use case is retrospective — so a hook's coverage of the actual need is zero.
  • Terminal choice is not where the memory goes. Measured 2026-09-05: iTerm2 itself was 368MB in one process, while 36 claude processes held 4.66GB (32 of them with a live TTY, 4.52GB), against 20.4GB of 21.5GB swap in use. Switching terminal emulator saves the 368MB, not the 4.5GB — so a session-management feature is worth building for what it does for the user, not as a memory fix.
  • The corpus is 88 session transcripts / 687MB (2026-09-05). A find across ~/.claude/projects returns 223 files / 775MB, but the extra 135 are nested subagent transcripts and do not belong in a session index — an easy over-count. A full background scan is a one-off few seconds; the steady state afterwards is one stat per file.
  • Claude Code writes a one-line "where we are, what's next" recap into the transcript: {"type":"system","subtype":"away_summary","content":"…"}. 74 of 88 sessions (84.1%) carry one; the last one runs a median of 251 characters. It is better recall material than the last assistant turn — which is often a table fragment — and costs one more pattern in the grep that already looks for custom-title / ai-title / pr-link. Used by sessions: save and restore named session-list snapshots #145.
  • Both search paths must share the parser — main-side full-prompt search and the renderer's filterSessionsLocally. One rule with two implementations is how this repo has produced bugs repeatedly; the read-authority saga in PR feat(sessions): pin browse modes — recency order, ungroup, pinned-only #137 took four rounds for exactly this reason.

🤖 On behalf of @grimmerk — generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions