Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ If the docs themselves feel stale or scattered, also read `docs/reference/DOCS_R
- Settings panel: `docs/features/settings.md`
- Settings sync: `docs/features/settings-sync.md`
- Agent Chat (Beta-gated chat pane, providers, sidecar, attachments): `docs/features/agent-chat.md`
- Agent run checkpoints (issue #80 — opt-in background rollback snapshot at run start + restore): `docs/features/agent-chat.md` (§ Run checkpoints); design note at `docs/plans/agent-run-checkpoint.md`
- Multi-provider chat (Step 12): `docs/features/multi-provider-chat.md`; plan + final-state summary at `docs/plans/step-12-opencode-implementation-plan.md`; research at `docs/plans/step-12-opencode-research.md`; operator UI smoke at `docs/plans/step-12-ui-smoke-checklist.md`
- Skills sync (E2E, Step 10): `docs/features/skills-sync.md`; plan + per-stage history at `docs/plans/step-10-skills-sync.md`; research at `docs/plans/step-10-skills-sync-research.md`; operator UI smoke at `docs/plans/step-10-ui-smoke-checklist.md`
- Attachments + context system (Step 8): `docs/plans/step-8-attachments.md` (research + locked plan)
Expand Down
1 change: 1 addition & 0 deletions docs/core/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ The repo structure is clean and domain-split:
- Permissions settings page with per-tool body rendering
- Wired Debug mode pill with marker cleanup flow
- Plain-quit on Beta toggle off (no auto-restart)
- **Run checkpoints (issue #80, opt-in)**: background working-tree snapshot at session start (shadow ref via temp index — zero first-token latency, user's index/worktree/stash untouched), restore button in the pane header (confirm dialog, disabled mid-turn), Settings → Agent toggle, per-thread bookkeeping in `agent_chat_checkpoints`, ref pruning. See `docs/features/agent-chat.md` § Run checkpoints + `docs/plans/agent-run-checkpoint.md`
- See `docs/features/agent-chat.md` for the canonical feature breakdown

### Infrastructure
Expand Down
39 changes: 39 additions & 0 deletions docs/features/agent-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,12 +508,51 @@ string when the target provider is missing from the registry.
| `agent_chat_set_model` | Swap a thread's model mid-session. |
| `agent_chat_set_permission_mode` | Swap a thread's permission mode mid-session. |
| `agent_chat_stop_session` | Gracefully close a session. Idempotent. |
| `agent_chat_get_checkpoint` | Return the run-start rollback checkpoint recorded for a thread (or null). Not flag-gated — renders as "no checkpoint" instead of an error. |
| `agent_chat_restore_checkpoint` | Roll the workspace back to the thread's run-start checkpoint. Mutates the working tree; the UI confirms first. |

Provider errors are serialized as `SerializableProviderError` JSON so
the UI can inspect the error subtype (e.g.
`{"kind":"not_authenticated", ...}`) instead of parsing a free-form
string.

## Run checkpoints (issue #80)

Opt-in rollback point taken when a chat session starts. Off by
default; toggled via Settings → Agent → "Checkpoint before agent
runs" (`UserSettings.agent_chat.checkpoints_enabled`, synced
settings).

- **Background, never on the first-token path.** `agent_chat_start_session`
spawns `tauri::async_runtime::spawn` + `spawn_blocking` AFTER the
provider session is up; even the settings-cache read happens inside
the spawned task.
- **Non-destructive snapshot.** `git_checkpoint_create`
(`src-tauri/src/git.rs`) stages the worktree (tracked + untracked,
`.gitignore` respected) into a temporary `GIT_INDEX_FILE`, writes a
tree, and `commit-tree`s it onto HEAD — the user's index, worktree,
and stash list are untouched and no hooks run. The commit is
anchored at `refs/codemux/checkpoints/<sanitized-thread-id>` so gc
can't reap it. Skipped silently for non-repos and unborn-HEAD repos.
- **Recorded per thread.** `agent_chat_checkpoints` table (FK →
`agent_chat_sessions`, ON DELETE CASCADE) stores snapshot commit,
HEAD commit, branch, repo path, ref name. On success the backend
emits `agent_chat_checkpoint` (`{thread_id, checkpoint}`); the
frontend hook `useAgentChatCheckpoint` (event + on-mount fetch)
feeds the pane header's restore button (history icon, hover-reveal,
hidden when no checkpoint, disabled mid-turn).
- **Restore** (`git_checkpoint_restore`): refuses on branch mismatch
or pruned commits; takes a safety snapshot of the current state to
`refs/codemux/pre-restore/<id>` first; then `read-tree --reset -u
<snapshot>` + `clean -fd` + `reset --mixed <pre-run HEAD>`. Result:
run commits undone, run-created files deleted (ignored files
spared), pre-run changes back as unstaged edits / untracked files.
Known loss: the pre-run staged/unstaged split flattens to unstaged.
- **Pruning.** Each create prunes both `refs/codemux/` namespaces to
the 20 newest refs and drops the matching bookkeeping rows.

Design note: `docs/plans/agent-run-checkpoint.md`.

## Event bridge

Every registered provider's canonical event stream is forwarded to
Expand Down
107 changes: 107 additions & 0 deletions docs/plans/agent-run-checkpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Agent Run Checkpoint (issue #80)

- Purpose: Track the design and rollout of the optional background rollback checkpoint taken at agent-chat run start.
- Audience: Anyone changing agent-chat session start, git snapshot helpers, or the restore UI.
- Authority: Active work plan only, not current truth.
- Update when: Scope, restore semantics, or follow-ups change.
- Read next: `docs/features/agent-chat.md`, `docs/core/STATUS.md`

## Goal

When an agent-chat session starts, take an **opt-in, background** snapshot of the
workspace working tree so the user can roll back everything the run changed. The
snapshot must add **zero latency** to the first token and must **not disturb** the
user's index, worktree, or stash list.

## Design

### Snapshot (non-destructive, shadow ref)

`git stash create` ignores untracked files, so the snapshot instead builds a commit
through a **temporary index** (`GIT_INDEX_FILE`), which never touches the user's real
index or worktree:

1. `git read-tree HEAD` into a temp index file
2. `git add -A` (temp index — captures tracked changes **and** untracked files, respects `.gitignore`)
3. `git write-tree` → snapshot tree
4. `git commit-tree <tree> -p HEAD` → snapshot commit (no hooks run, identity forced via `-c user.*`)
5. `git update-ref refs/codemux/checkpoints/<sanitized-thread-id> <commit>` → protects the snapshot from gc

Recorded against the thread in SQLite (`agent_chat_checkpoints`, FK → `agent_chat_sessions`
ON DELETE CASCADE): snapshot commit, HEAD commit, branch name, repo path, ref name.

Skipped silently (no checkpoint row) when: the setting is off, `cwd` is not a git repo,
or the repo has an unborn HEAD (no commits yet).

### Background execution

`agent_chat_start_session` spawns `tauri::async_runtime::spawn` + `spawn_blocking`
**after** the provider session has started and the session row is persisted. Nothing
git-related runs on the start-session (or first-token) path. On success the backend
emits `agent_chat_checkpoint` (`{ thread_id, checkpoint }`) so the pane header can
reveal the restore affordance.

### Restore semantics

`agent_chat_restore_checkpoint(thread_id)`:

1. Refuse if the snapshot/HEAD commits are gone (pruned) or the repo is now on a different branch.
2. Safety snapshot of the **current** state to `refs/codemux/pre-restore/<id>` (parents at the
agent's last commit, so even agent-made commits stay reachable after the branch resets).
3. `git read-tree --reset -u <snapshot>` — worktree + index now match the snapshot tree
(restores modified/deleted files, including formerly-untracked ones).
4. `git clean -fd` — deletes run-created files (ignored files are spared).
5. `git reset --mixed <head-at-checkpoint>` — undoes run-made commits, leaves the restored
content as **unstaged** changes; formerly-untracked files show as untracked again.

Known, documented loss: the staged/unstaged split of the pre-run state is flattened to
unstaged (the snapshot records one tree, not the index).

### Pruning

Each successful checkpoint prunes both `refs/codemux/checkpoints/*` and
`refs/codemux/pre-restore/*` to the 20 newest per namespace (by committer date) and
deletes the matching `agent_chat_checkpoints` rows, so shadow refs cannot grow unboundedly.

### Opt-in setting

`UserSettings.agent_chat.checkpoints_enabled` (synced settings, **default `false`**).
Toggle lives in Settings → Agent (visible when the agent-chat beta flag is on). The
background task reads the settings cache (`settings_sync::load_cache()`), same pattern
as session-restore.

## Active Priorities

1. ~~Backend: git helpers + DB table + commands + start-session hook~~ (landed)
2. ~~Frontend: setting toggle, store slice, header restore button + confirm dialog~~ (landed)
3. Follow-ups below

## Open Questions / Follow-ups

- Checkpoint continuity across silent restarts (permission-mode change restarts the
session under a new thread id, which takes a fresh checkpoint — "before this run"
then means "before the restart"). Possible fix: carry the checkpoint forward when
`resume_cursor` is set.
- Surface checkpoints over the socket API / MCP server so agents can self-rollback.
- Per-turn checkpoint history (explicitly out of scope for v1 per issue #80).
- Restore button for *past* sessions from the SessionSelector dropdown.

## Likely Touch Points

- `src-tauri/src/git.rs` — `git_checkpoint_create` / `git_checkpoint_restore` / `git_checkpoint_prune`
- `src-tauri/src/database.rs` — `agent_chat_checkpoints` table + CRUD
- `src-tauri/src/commands/agent_chat.rs` — spawn hook, `agent_chat_get_checkpoint`, `agent_chat_restore_checkpoint`
- `src-tauri/src/settings_sync.rs` — `AgentChatSettings`
- `src/components/chat/AgentChatPaneHeader.tsx` — restore affordance
- `src/stores/agent-chat-store.ts` — per-thread checkpoint slice
- `src/components/settings/settings-view.tsx` — opt-in toggle
- `src/dev/tauri-mock.ts` — mock handlers for browser-pane smoke tests

## Already Landed

- (see git history of this branch)

## Notes

- The anti-pattern this feature exists to avoid: any synchronous `git add -A`-style
walk between the user pressing send and the provider's first token.
Loading
Loading