# Revert Timeline Design

Threadwell revert is a project-local change-set timeline. It lets a user inspect and safely undo one or more agent turns without using or mutating the user's real Git repository.

## Goals

- Record file changes step by step for mutating agent turns.
- Support reverting a single change set, the last N change sets, or a contiguous range.
- Block unsafe reverts when current file content no longer matches the expected post-change state.
- Work in Git and non-Git projects.
- Keep the UX terminal-native and Windows Terminal-friendly: slash commands, compact change cards, and editor-replacement confirmations.

## Non-goals

- Do not revert external side effects such as package installs, network calls, database mutations, processes, or files outside the project root.
- Do not write to the user's `.git` index, branches, refs, commits, or stash.
- Do not ship force-overwrite revert in the first version.
- Do not depend on mouse-only buttons or floating terminal overlays.

## Storage

Preferred storage is project-local and Threadwell-owned:

```text
.threadwell/snapshots/
  blobs/<sha256-prefix>/<sha256>
  changesets.jsonl
  reverts.jsonl
```

The store is internal runtime state and must be excluded from snapshot capture. The storage implementation may use an internal bare Git repository if that proves simpler, but the exposed service contract is content-addressed snapshots plus metadata. Either implementation must remain separate from the user's real `.git`.

## Data model

```ts
type ChangeSetStatus = "active" | "reverted" | "partially_reverted";
type ChangedFileStatus = "created" | "modified" | "deleted" | "skipped";

type ChangedFile = {
  path: string;                 // project-relative, normalized with forward slashes
  status: ChangedFileStatus;
  beforeHash?: string;          // absent for created files
  afterHash?: string;           // absent for deleted files
  currentExpectedHash?: string; // normally afterHash before revert
  binary?: boolean;
  large?: boolean;
  skippedReason?: string;
};

type ChangeSet = {
  id: number;                   // monotonic per project/session timeline
  sessionId: string;
  turnId?: string;
  userMessageId?: string;
  assistantMessageId?: string;
  createdAt: number;
  status: ChangeSetStatus;
  files: ChangedFile[];
  summary: {
    created: number;
    modified: number;
    deleted: number;
    skipped: number;
  };
};
```

A revert record stores the selected range, the pre-revert snapshot refs, affected files, timestamp, and conflict/result status so `/unrevert` can restore when safe.

## Capture lifecycle

1. Before a normal mutating-capable prompt starts, create a turn baseline.
2. Before a known file-mutating tool writes a file, capture that file's before content if it was not already captured.
3. At turn end, detect changed files and store after content.
4. If no files changed, do not create a change set.
5. If the turn aborts or errors after mutating files, still create a change set.

`/fast` is currently read-only and can skip snapshot capture or record a no-op.

## Change detection

V1 should combine:

- known file-tool paths from `edit`, `write`, and related built-in tools;
- safe worktree status/diff detection for bash side effects when the project is a Git repo;
- bounded scans for non-Git projects only when safe and configured by the snapshot policy.

Threadwell should never scan or snapshot excluded runtime/build/cache paths.

## Safety policy

Before applying revert, validate current file state:

- modified/deleted files: current hash must match the expected `afterHash` from the newest applicable change set for that file;
- created files: current file must still match the created content before deletion;
- deleted files: file must still be absent before restoration;
- skipped files: block or warn according to severity; do not pretend they were reverted.

If any file diverges, block the revert and show conflicts. V1 does not provide `--force`.

## Revert semantics

Supported references:

```text
/revert 42
/revert last
/revert last 4
/revert 41..44
```

A range is applied newest-to-oldest:

```text
44^-1, 43^-1, 42^-1, 41^-1
```

File operations:

- created by the reverted change set: delete if unchanged;
- modified by the reverted change set: restore `beforeHash`;
- deleted by the reverted change set: restore `beforeHash`;
- skipped/non-revertible: block or leave unchanged with explicit warning.

## Revert-to semantics

Future `/revert-to 40` means: revert every active change set after `#40` in reverse chronological order. It is equivalent to `/revert 41..latest` after resolving the current active timeline.

## Unrevert semantics

`/unrevert` restores the pre-revert snapshot from the most recent revert record when current files still match the expected post-revert state. If files changed after revert, unrevert is blocked with conflicts.

## UX

After a changed turn, show a compact card:

```text
changes #42 › 2 modified · 1 created
/changes 42 inspect · /revert 42 restore
```

Commands:

- `/changes` lists recent change sets.
- `/changes 42` shows detail for one set.
- `/changes 41..44` shows a range summary.
- `/revert ...` opens confirmation.
- `/revert-to ...` returns to a timeline point.
- `/unrevert` restores the most recent revert when safe.

Confirmation uses editor replacement, not floating overlays:

```text
Revert change sets #41..#44?

Will undo:
  #44 2 files
  #43 1 file
  #42 5 files
  #41 1 file

Type REVERT to confirm:
```

Esc/cancel is always no-op.

## Transcript semantics

After a revert, the next model call must not treat reverted assistant messages as active context if their filesystem effects were undone. The first implementation may mark post-revert transcript entries as inactive/ignored before the next prompt, similar to opencode's deferred cleanup, as long as the UI clearly indicates active versus reverted history.

## Risks

- Bash commands can mutate files outside known tool paths.
- Generated files can be large or noisy.
- Manual edits between change sets can block revert.
- Binary files may be restorable but not diffable.
- Windows path casing and separators must be normalized consistently.
