Skip to main content
Smithers records a JJ (Jujutsu) change ID after each successful task attempt. Revert restores the workspace to the exact filesystem state at that point.

Prerequisites

  • JJ installed and in PATH (brew install jj or cargo install jj-cli)
  • Workspace is a JJ repository (jj git init or jj init)
  • Target attempt has a recorded JJ pointer (captured only when JJ was available at attempt completion)

How It Works

  1. On successful attempt completion, Smithers captures the JJ change ID via jj log -r @ --no-graph --template change_id.
  2. The change ID is stored in _smithers_attempts.jj_pointer.
  3. smithers revert runs jj restore --from <change_id> to restore the workspace.
  4. Database frames recorded after the attempt started are deleted so the DB state matches the reverted filesystem.
Revert restores files but does not alter JJ history. It creates a new change on top of the current working copy.

CLI Usage

smithers revert <workflow.tsx> \
  --run-id <run-id> \
  --node-id <node-id> \
  [--attempt N] \
  [--iteration N]

Flags

FlagDefaultDescription
--run-id IDrequiredRun containing the target attempt.
--node-id IDrequiredTask node to revert to.
--attempt N1Attempt number (1-indexed).
--iteration N0Loop iteration number.

Examples

# Revert to first attempt of "analyze"
smithers revert workflow.tsx --run-id abc123 --node-id analyze

# Revert to second attempt (after retry)
smithers revert workflow.tsx --run-id abc123 --node-id analyze --attempt 2

# Revert to a specific loop iteration
smithers revert workflow.tsx --run-id abc123 --node-id fix --attempt 1 --iteration 2

Exit Codes

CodeMeaning
0Revert succeeded.
1Revert failed.

Output

{ "success": true, "jjPointer": "zxkqmrstvwxy" }
On failure:
{ "success": false, "error": "Attempt has no jjPointer recorded", "jjPointer": null }
RevertStarted and RevertFinished events are printed as JSON lines to stdout during execution.

Programmatic Usage

import { SmithersDb, ensureSmithersTables, revertToAttempt } from "smithers-orchestrator";

const adapter = new SmithersDb(db);

const result = await revertToAttempt(adapter, {
  runId: "abc123",
  nodeId: "analyze",
  iteration: 0,
  attempt: 1,
  onProgress: (event) => console.log(event.type),
});

if (result.success) {
  console.log("Reverted to", result.jjPointer);
} else {
  console.error("Revert failed:", result.error);
}

RevertOptions

type RevertOptions = {
  runId: string;
  nodeId: string;
  iteration: number;
  attempt: number;
  onProgress?: (event: SmithersEvent) => void;
};

RevertResult

type RevertResult = {
  success: boolean;
  error?: string;
  jjPointer?: string;
};

Events

  • RevertStarted — Before jj restore runs. Includes jjPointer.
  • RevertFinished — After restore completes. Includes success and error (if failed).
See Events for full type definitions.

Troubleshooting

ErrorCause
”Attempt has no jjPointer recorded”JJ was not available when the attempt finished. Pointers are captured opportunistically.
”jj exited with code 1”Change ID pruned/GC’d, workspace conflicted, or JJ misconfigured.
Revert restores the filesystem and cleans up database frames recorded after the reverted attempt started. Task outputs, attempt records, and run state remain unchanged. To re-run a task, resume the workflow.
  • Events — RevertStarted and RevertFinished types.
  • VCS Integration — Version control integration.
  • CLI — Full CLI reference.