Skip to content

feat(sessions): opt-in renaming of sessions after their pull requests - #172

Merged
ghackett merged 2 commits into
mainfrom
pr-title-rename
Aug 3, 2026
Merged

feat(sessions): opt-in renaming of sessions after their pull requests#172
ghackett merged 2 commits into
mainfrom
pr-title-rename

Conversation

@ghackett

@ghackett ghackett commented Aug 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds a "Rename sessions after their pull requests" switch (default off) to the Session list preferences group. When on, a session is retitled to match the newest pull request detected in it, so the sidebar reads as a list of the PRs being worked on.

  • Every detection path is covered: an open tab's PR poll (MainWindow._on_tab_prs_changed), the sidebar row's PR-menu refresh for sessions with no open tab (SessionRow._pr_menu_refreshed), and a one-time sweep over all saved PR lists when the setting is switched on (SessionStore.apply_pr_titles from _apply_preferences) — so existing sessions with saved PRs retitle immediately.
  • Manual renames always win: the PR title lands in the generated-name slot (like auto-titling), so display_name's precedence keeps an explicit rename on top, and the auto-title paths already know to skip a session that has a generated name.
  • Waits for a real title: a fresh pr-link transcript record carries no title until a gh reply enriches it; the new prstatus.newest_title helper skips untitled records, so nothing is renamed until the title is actually known (and a titled predecessor is never blanked by a newer bare link).
  • Multiple PRs: saved lists are oldest-first, so the newest titled PR — the one being worked on now — is what the session is named after. If the PR's title later changes on GitHub, the next refresh that lands it renames the session to match.
  • No churn: a rename only happens when the newest title differs from the current generated name, so the 1s tab poll doesn't rewrite state.json on every tick.

The core logic lives in GTK-free modules (prstatus.newest_title, SessionStore.apply_pr_title), with tests in test_prstatus.py, test_store.py, and test_state.py.

Screenshots

The new switch in Preferences → Session list:

Full window with the Preferences dialog open, showing the Rename sessions after their pull requests switch in the Session list group

Detail: the Session list preferences group with the new switch turned on

A session renamed to its PR's title in the sidebar:

Full window with a sidebar session titled after its pull request

Detail: the alpha-widgets group with one session titled feat(settings): dark mode support for the settings page

🤖 Generated with Claude Code

https://claude.ai/code/session_01REtnD8HVyX4PBz1cNYDpmk

A new "Rename sessions after their pull requests" switch (default off)
in the Session list preferences group. When on, a session is retitled to
the newest PR detected in it — from a tab's PR poll, the sidebar row's
PR menu refresh, and a one-time sweep over saved PR lists when the
setting is switched on. The title lands in the generated-name slot, so a
manual rename always wins and the auto-title paths keep skipping the
session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REtnD8HVyX4PBz1cNYDpmk
@ghackett
ghackett marked this pull request as ready for review August 3, 2026 12:39
@ghackett

ghackett commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @ghackett's task in 2m 50s —— View job


Claude finished reviewing

  • Gather context (read diff, CLAUDE.md)
  • Review core logic (prstatus.py, store.py)
  • Review UI/wiring (prefs.py, sidebar.py, window.py, state.py)
  • Review tests
  • Post final review

Summary

This is a clean, well-scoped feature. I traced all three write paths (_on_tab_prs_changed, _pr_menu_refreshed, apply_pr_titles sweep on preferences apply) and the precedence chain (display_nameget_nameget_generated_name → preview → id prefix). Everything lines up with the PR description:

  • newest_title correctly relies on the documented invariant that saved PR lists are oldest-first (confirmed via prstatus.py's "chronological" comment and merge_ordered's tests), and skips untitled records so a titled predecessor is never blanked by a fresh bare pr-link.
  • Writing to the generated-name slot (not names) is the right call — it reuses the existing "manual rename wins" precedence and the existing auto-title skip-if-generated-name-set guards, without needing new state or new skip logic.
  • The no-churn guard (title == get_generated_name(...)) correctly prevents the 1s tab poll from rewriting state.json every tick.
  • set_generated_names batches to one save() call, matching apply_pr_titles' one-sweep intent.
  • GPL modification-notice headers are correctly updated on every pre-existing file touched (prefs.py, sidebar.py, state.py, store.py, window.py); prstatus.py's newest_title addition needs no notice since the file postdates the fork.
  • po/collins.pot strings match the two new user-facing strings in prefs.py.
  • Tests are thorough: test_prstatus.py covers the ordering/skip/none cases for newest_title, test_store.py covers off-by-default, the win-over-manual-rename case, waiting for a title, and the preferences-toggle sweep (including a second session that's correctly left alone because it has no title yet), and test_state.py covers the new setting's persistence/default.

Minor observations (non-blocking)

  • store.py:454-474apply_pr_titles() is called unconditionally from _apply_preferences() on every preferences save, not just when pr_title_sessions is toggled (e.g. also fires when the user just changes font size). It's guarded by the setting check and the diff-before-write check, so it's a no-op walk over self.sessions rather than actual churn — fine given the size of that dict, just noting it's broader than strictly necessary.
  • collins/prstatus.py:428-436newest_title's docstring nicely documents the oldest-first assumption, but nothing in newest_title itself asserts/enforces it; it's an implicit contract with whatever writes the saved list (to_records/merge_ordered). Not a bug — just worth keeping in mind if a future change ever reorders that list.

I was unable to execute python3 -m pytest in this sandbox (the command requires approval that isn't available in this run) — you may want to add python3/pytest to --allowedTools so a future review can actually execute the suite rather than just reading it. I did not find anything in the code or tests that suggests they'd fail.

No bugs found; nothing here I'd block on.
(branch)

Review follow-up: _apply_preferences fires on every preferences save, so
apply_pr_titles walked all sessions when e.g. only the font changed. The
store now remembers the last-seen pr_title_sessions value and sweeps only
on the off-to-on flip; steady-state saves return immediately, and the
per-detection apply_pr_title hooks keep names current while the setting
stays on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REtnD8HVyX4PBz1cNYDpmk
@ghackett

ghackett commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Addressed the review's two observations in a8638dc:

  • Sweep on every preferences saveapply_pr_titles now detects the off→on flip itself (the store remembers the last-seen setting value), so a save that didn't just enable the setting returns without walking the sessions. A new test (test_apply_pr_titles_sweeps_only_on_the_off_to_on_flip) pins the semantics, including that a list saved behind the sweep's back is left to the per-detection hooks until a fresh flip.
  • newest_title's oldest-first assumption — left as documentation: the docstring names the invariant, and its writers (to_records over merge_ordered output) are covered by test_records_roundtrip_in_order and the merge-order tests, so a future reordering would surface there.

@ghackett
ghackett merged commit d51a3b2 into main Aug 3, 2026
2 checks passed
@ghackett
ghackett deleted the pr-title-rename branch August 3, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant