---
title: VCS Integration
description: How Smithers integrates with JJ (Jujutsu) and Git for filesystem snapshots, worktree management, and revert support.
---

Smithers integrates with [JJ (Jujutsu)](https://github.com/martinvonz/jj) and Git to record filesystem snapshots at each task completion. This enables reverting to the exact workspace state after any attempt.

## VCS Detection

Smithers walks up the directory tree from `rootDir` looking for `.jj` or `.git`. When both exist in the same directory (a colocated repo), Smithers prefers `.jj` and uses JJ semantics. Pure Git repos work without JJ installed.

## VCS Pointer Flow

When a supported VCS is detected:

1. A task executes, making filesystem changes via agent tools.
2. The task completes (success or failure).
3. Smithers captures the current revision into `_smithers_attempts.jj_pointer`:
   - **JJ**: the change ID from `jj log -r @ --template change_id`
   - **Git**: the commit SHA from `git rev-parse HEAD`
4. The next task continues from this point.

Each attempt gets its own pointer.

The revision is also recorded on the run row as `vcs_revision` so Smithers can detect workspace drift between a run's start and a later resume — if the revision changed, the engine warns of a potential durability mismatch.

## Recorded Data

| Column | Type | Description |
|---|---|---|
| `jj_pointer` | text (nullable) | JJ change/operation ID after attempt completion. `null` if JJ unavailable. |

```bash
sqlite3 smithers.db "SELECT run_id, node_id, iteration, attempt, jj_pointer FROM _smithers_attempts WHERE run_id = '<id>';"
```

```
smth_a1b2|analyze|0|1|zqkopwvn
smth_a1b2|fix|0|1|xrlmqkts
smth_a1b2|fix|0|2|ynpwzrmv
smth_a1b2|report|0|1|kutswxqp
```

## Revert

```bash
smithers revert workflow.tsx \
  --run-id <run-id> \
  --node-id <node-id> \
  --attempt <attempt-number> \
  --iteration <iteration-number>
```

```bash
smithers revert workflow.tsx --run-id smth_a1b2 --node-id fix --attempt 1 --iteration 0
```

Restores the filesystem to the exact post-attempt state. Emits `RevertStarted` and `RevertFinished` events.

## Without VCS

- `jj_pointer` is `null` for all attempts.
- `revert` fails with an error.
- All other Smithers functionality is unaffected.

JJ is optional. Install only if revert support is needed. Git-only repos also provide pointer tracking and worktree support without JJ.

## Setup

```bash
brew install jj
```

```bash
# New JJ repo
cd /path/to/my-project
jj git init

# Colocate with existing Git repo
jj git init --colocate
```

Smithers auto-detects JJ and starts recording pointers.

## Programmatic Helpers

Smithers exports helpers for running raw `jj` commands, checking repo status, reading/restoring pointers, and managing JJ workspaces. See [VCS Helper Reference](/reference/vcs-helpers).

## Worktrees

Smithers can isolate each workflow run in its own worktree (a separate checkout sharing the same object store). This lets multiple runs modify the filesystem concurrently without stepping on each other.

### Git Worktrees

When `rootDir` points to a Git repository, Smithers calls `git worktree add` to create a new working tree at `worktreePath`. A named branch (`-B`) is created from the best available base ref:

1. `baseBranch` (if configured)
2. `origin/<baseBranch>`
3. `main` / `origin/main`
4. `HEAD`

It tries each in turn and uses the first that succeeds.

### JJ Workspaces

For JJ repos, Smithers calls `workspaceAdd` to create a JJ workspace at `worktreePath`. When a `branch` is supplied, it runs `jj bookmark set <branch> -r @` to point a bookmark at the new workspace's working copy.

You can create a workspace at a specific revision:

```ts
import { workspaceAdd } from "smithers-orchestrator/vcs";

await workspaceAdd("feature-fix", "/workspaces/feature-fix", {
  cwd: "/my-repo",
  atRev: "main",   // start the workspace at the tip of main
});
```

`workspaceAdd` tries multiple invocation styles (`jj workspace add --name`, positional, `--wc-path`) to stay compatible across JJ versions. Stale workspaces with the same name are forgotten before creating a new one.

### Rebase on Resume

When Smithers resumes a workflow that already has a worktree, it syncs the worktree to the current tip of the base branch before continuing:

- **JJ**: `jj git fetch` then `jj rebase -d <base>`
- **Git**: `git fetch origin` then `git rebase origin/<base>`

If the rebase fails, Smithers logs a warning and continues anyway — the resume is not blocked.

The base branch defaults to `main` when no `baseBranch` is configured.

## Running Workflows at a Specific Revision

When a run is started, Smithers records the current VCS revision (`vcs_revision` on the run row). On resume, it checks the current revision against the stored one. A mismatch emits a warning but does not block the resume.

This revision snapshot is available in the database:

```bash
sqlite3 smithers.db "SELECT run_id, vcs_type, vcs_root, vcs_revision FROM _smithers_runs WHERE run_id = '<id>';"
```

## JJ Operation ID Tracking

For JJ repos, the pointer stored in `jj_pointer` is the JJ **change ID** (the stable identifier that persists across amends and rebases), not the operation ID. This means:

- The pointer survives `jj amend`, `jj rebase`, and other history-rewriting commands.
- `jj restore --from <change_id>` reliably restores the working copy to the exact post-attempt state.

The change ID is read via `jj log -r @ --no-graph --template change_id`.

## Cache Key Integration

VCS pointers are included in the cache key when caching is enabled (`<Workflow cache>` or `{ cache: true }`):

| Component |
|---|
| Workflow name + nodeId |
| Prompt text or static payload |
| Model ID and parameters |
| Tool allowlist and versions |
| Output schema signature |
| **VCS pointer (JJ change ID or Git SHA)** |

Workspace changes invalidate cached results. Returning to a previous state (same pointer) reuses the cached result.

## Next Steps

- [Resumability](/guides/resumability) -- Crash recovery and state persistence.
- [Caching](/concepts/caching) -- Cache key mechanics.
- [CLI Reference](/cli/overview) -- All CLI commands including `revert`.
