feat(session): #137 prune stale workspace snapshots at session boot

`run_interactive` now calls `session_manager::prune_workspace_snapshots_at_boot`
right after the system-skills installer, dropping any snapshot in the
side-git repo older than 7 days (default; configurable via the new
`[snapshots]` section in `config.example.toml`). The helper is
non-fatal: a missing `git` binary, read-only home, or absent snapshot
dir all log a single WARN (or DEBUG for the count of pruned commits)
and return, so the TUI keeps starting even when retention can't run.

Also document the snapshot subsystem in `config.example.toml` —
disk-footprint expectations, where the side repo lives, and how
`/restore` / `revert_turn` consume it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hunter Bown
2026-04-28 00:31:57 -05:00
parent fb12e331ab
commit 0781b7c203
3 changed files with 52 additions and 0 deletions
+21
View File
@@ -213,6 +213,27 @@ default_text_model = "deepseek-ai/deepseek-v4-pro"
# threshold_secs = 30
# include_summary = false
# ─────────────────────────────────────────────────────────────────────────────────
# Workspace Snapshots (#137)
# ─────────────────────────────────────────────────────────────────────────────────
# Each turn the TUI takes a `pre-turn:<seq>` and `post-turn:<seq>` snapshot of
# your workspace into a side-git repo at:
#
# ~/.deepseek/snapshots/<project_hash>/<worktree_hash>/.git
#
# Your own `.git` is never touched — `--git-dir` and `--work-tree` are always
# set together when shelling out to git. Use `/restore N` (slash command) or
# the `revert_turn` tool to roll the working tree back. Conversation history
# is unaffected.
#
# Disk footprint: ~1-2 GB worst case for a 100 MB workspace × 12 turns/day,
# typically far less thanks to git's content-addressed storage. The session
# boot prunes anything older than `max_age_days` (default 7).
#
# [snapshots]
# enabled = true # Snapshot workspace pre/post each turn for /restore
# max_age_days = 7 # Older snapshots pruned at session start
# ─────────────────────────────────────────────────────────────────────────────────
# Hooks (optional)
# ─────────────────────────────────────────────────────────────────────────────────
+5
View File
@@ -2788,6 +2788,11 @@ async fn run_interactive(
logging::warn(format!("Failed to install system skills: {e}"));
}
// Prune stale workspace snapshots from prior sessions (7-day default).
// Non-fatal: a flaky disk, missing `git`, or read-only home should
// never block the TUI from starting.
session_manager::prune_workspace_snapshots_at_boot(&workspace);
tui::run_tui(
config,
tui::TuiOptions {
+26
View File
@@ -370,6 +370,32 @@ pub fn default_sessions_dir() -> std::io::Result<PathBuf> {
Ok(home.join(".deepseek").join("sessions"))
}
/// Boot-time hook: prune workspace snapshots older than the configured
/// retention window for `workspace`. Failure is logged but never fatal —
/// the session boot must not block on a flaky disk or missing `git`.
///
/// Called once per `run_interactive` invocation. The default retention
/// (7 days) is governed by `crate::snapshot::DEFAULT_MAX_AGE`.
pub fn prune_workspace_snapshots_at_boot(workspace: &Path) {
prune_workspace_snapshots(workspace, crate::snapshot::DEFAULT_MAX_AGE);
}
/// Prune snapshots older than `max_age` for `workspace`.
///
/// Always non-fatal. Returns silently — callers don't need the count
/// (the underlying repo logs at WARN if anything blew up).
pub fn prune_workspace_snapshots(workspace: &Path, max_age: std::time::Duration) {
match crate::snapshot::prune_older_than(workspace, max_age) {
Ok(0) => {}
Ok(n) => {
tracing::debug!(target: "snapshot", "boot prune removed {n} snapshot(s)");
}
Err(e) => {
tracing::warn!(target: "snapshot", "boot prune failed: {e}");
}
}
}
/// Create a new `SavedSession` from conversation state
pub fn create_saved_session(
messages: &[Message],