Skip to content

feat(konsole): ✨ add session save/restore with staggered agent resume - #73

Open
justapithecus wants to merge 1 commit into
mainfrom
andrew/feat/konsole/session
Open

feat(konsole): ✨ add session save/restore with staggered agent resume#73
justapithecus wants to merge 1 commit into
mainfrom
andrew/feat/konsole/session

Conversation

@justapithecus

Copy link
Copy Markdown
Owner

Summary

Add konsole/session/, an openSUSE Tumbleweed + KDE Plasma 6 utility that snapshots every Konsole window and tab before a restart and rebuilds them at the next login, resuming the Claude Code and Codex conversations that were running in them instead of starting fresh sessions.

Highlights

  • konsole-session save — records desktop, screen, geometry, tab order, titles, colors, profiles, working directories, and a resume command per tab (Claude Code session id, Claude Code wrapper transcript, Codex rollout id). Privileged commands are never recorded for replay.
  • konsole-session restore — opens tabs immediately but starts their commands one every --stagger seconds (default 20), most recently active first, to avoid a CPU/memory spike; Ctrl-C in a tab skips its command. Conversations already running elsewhere are not resumed twice.
  • konsole-session-restore.service — oneshot login unit wanted by xdg-desktop-autostart.target; hooking it to plasma-workspace.target is an ordering cycle that systemd silently drops.
  • X11 and Wayland — window class matching and maximize handling work under both sessions.
  • install.sh — copies (no symlinks) into ~/.local/bin and ~/.config/systemd/user, guarded to openSUSE; installed separately from konsole/install.sh.

Known Limitations

  • Scrollback, split views, and in-flight agent replies are not restored.
  • Screen names differ between X11 and Wayland, so a snapshot taken under one and restored under the other can place windows on the wrong screen.

Test plan

  • Restore on a throwaway window: commands start most-recent-first at the stagger interval, countdown shows, Ctrl-C skips, an already-running conversation opens as a plain shell
  • Restore of a real 3-window / 28-tab snapshot under Wayland: windows on their desktops and maximized, all 24 commands started on schedule
  • save on a live Wayland session
  • systemd-analyze --user verify shows no ordering cycle for the X11, Wayland, and autostart targets
  • Restore triggered by the unit at an actual login

No-Issue: personal dotfiles tooling, not tracked in Linear

🤖 Generated with Claude Code

https://claude.ai/code/session_01DeWM5Cg4gj6bBkaTBWjsaT

## Summary

Add `konsole/session/`, an openSUSE Tumbleweed + KDE Plasma 6 utility that
snapshots every Konsole window and tab before a restart and rebuilds them at
the next login, resuming the Claude Code and Codex conversations that were
running in them instead of starting fresh sessions.

## Highlights

- **`konsole-session save`** — records desktop, screen, geometry, tab order,
  titles, colors, profiles, working directories, and a resume command per tab
  (Claude Code session id, Claude Code wrapper transcript, Codex rollout id).
  Privileged commands are never recorded for replay.
- **`konsole-session restore`** — opens tabs immediately but starts their
  commands one every `--stagger` seconds (default 20), most recently active
  first, to avoid a CPU/memory spike; Ctrl-C in a tab skips its command.
  Conversations already running elsewhere are not resumed twice.
- **`konsole-session-restore.service`** — oneshot login unit wanted by
  `xdg-desktop-autostart.target`; hooking it to `plasma-workspace.target`
  is an ordering cycle that systemd silently drops.
- **X11 and Wayland** — window class matching and maximize handling work
  under both sessions.
- **`install.sh`** — copies (no symlinks) into `~/.local/bin` and
  `~/.config/systemd/user`, guarded to openSUSE; installed separately from
  `konsole/install.sh`.

## Known Limitations

- Scrollback, split views, and in-flight agent replies are not restored.
- Screen names differ between X11 and Wayland, so a snapshot taken under one
  and restored under the other can place windows on the wrong screen.

## Test plan

- [x] Restore on a throwaway window: commands start most-recent-first at the
      stagger interval, countdown shows, Ctrl-C skips, an already-running
      conversation opens as a plain shell
- [x] Restore of a real 3-window / 28-tab snapshot under Wayland: windows on
      their desktops and maximized, all 24 commands started on schedule
- [x] `save` on a live Wayland session
- [x] `systemd-analyze --user verify` shows no ordering cycle for the X11,
      Wayland, and autostart targets
- [ ] Restore triggered by the unit at an actual login

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DeWM5Cg4gj6bBkaTBWjsaT
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T16:17:21.810108Z befd0cf PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: befd0cf12b

ℹ️ 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".

Comment on lines +404 to +405
for pid in pids():
ids.update(UUID_RE.findall(" ".join(proc_argv(pid))))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include live Codex rollouts in duplicate detection

When a snapshot is restored manually while its original codex process is still running, a session started as plain codex has no conversation UUID in its argv, so this scan misses it and launches codex resume <id> concurrently despite the stated duplicate-prevention behavior. Inspect live Codex processes' rollout file descriptors here, as codex_rollout() already does during saving, to avoid forking or concurrently writing the conversation.

Useful? React with 👍 / 👎.

Comment on lines +559 to +560
shutil.move(path, os.path.join(archive, "snapshot.json"))
log(f"restored {launched} windows; {len(delays)} commands start over {max(delays.values(), default=0)}s; archived in {archive}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the snapshot when window launches fail

If launch_window() fails for one or even every window—for example because Konsole or its D-Bus service is temporarily unavailable—the exception is only logged and this unconditional move consumes the default snapshot while the service still exits successfully. The next login therefore cannot retry the missing windows, and manually restoring the archived full snapshot would also duplicate any windows that did succeed; retain a retryable snapshot containing failed windows or return a failure instead.

Useful? React with 👍 / 👎.

Comment on lines +336 to +337
else: # several windows in one process: the caption is the active tab's title
placement = next((w for w in kwin_windows if w["caption"].startswith(tabs[active]["title"])), None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disambiguate windows that share active-tab titles

When one Konsole process owns multiple windows whose active tabs have the same title—or one title is a prefix of another—next() selects the same first KWin window for each match. Both saved windows consequently receive identical placement data while another window's desktop, screen, and geometry are lost; matched KWin entries need to be consumed or correlated using a unique property.

Useful? React with 👍 / 👎.

Comment on lines +471 to +474
if tab["command"]:
launcher = os.path.join(tab_dir, f"window{index}-tab{n}")
write_tab_launcher(launcher, tab, delays[id(tab)])
fields.append(f"command: {launcher}") # Konsole splits this on spaces, hence a script path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Base stagger deadlines on one restore start time

Each delay is relative to when that tab's launcher starts, but windows are launched sequentially, so tabs in later windows begin their countdown later than tabs with the same global schedule. If an earlier window takes longer than the stagger interval to initialize, an older rank in that window can run before the rank-zero conversation in a later window, defeating both the promised ordering and spacing; pass absolute deadlines from a common restore epoch instead.

Useful? React with 👍 / 👎.

Comment on lines +346 to +348
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path + ".tmp", "w") as f:
json.dump({"savedAt": time.strftime("%Y-%m-%dT%H:%M:%S%z"), "windows": windows}, f, indent=2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep captured command lines private

On a system using a conventional 022 umask with a traversable home or custom state directory, this creates snapshot.json as mode 0644, even though it records arbitrary process argv that can contain access tokens, passwords, or private URLs. The archived launcher scripts repeat those commands and are explicitly made 0755, so the state directory and command-bearing files should be created with user-only permissions.

Useful? React with 👍 / 👎.

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