Recover a run with retry, resume, and rollback

A run can fail halfway through a hank, and the useful question is rarely "how do I restart it" but "what is the smallest move that fixes the cause without throwing away finished work?" Hankweave gives you four: retry a temporary failure, edit the instructions or environment and try again, roll back to a known-good checkpoint when file state is damaged, or start a fresh run when the persisted plan itself is the problem. A hank (the JSON program) defines the codons; a checkpoint is the file state a codon seals so recovery has somewhere safe to land.

This page walks through each move in the order you are likely to need it: automatic and manual retry, resuming an interrupted run, rollback through the control surface, the special case of a failed exhaust extension, and replaying recorded output when you want to inspect a run without executing it. It closes with a decision table for choosing between the moves and a list of recovery patterns that make failures worse.

Retry a failed codon that might work#

Retry is the right move when the failure is transient and nothing about the codon needs to change. Hankweave supports it in two forms: an automatic policy declared on the codon, and a manual command sent to a live server.

For the automatic form, set onFailure on the codon that needs a failure policy. It accepts abort, retry, or ignore; the default is abort. The hank schema (the JSON contract that defines the field) and its defaults:

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
onFailureenumnoabort | retry | ignoreHow to handle codon failure. 'abort' (default): Use existing failure behavior (server stays active for retriable errors, shuts down for non-retriable). 'retry': Automatically retry up to maxAttempts times if the error i…
maxAttemptsinteger3nomin 1; max 10Maximum number of retry attempts (1-10, default: 3)
delayMsinteger1000nomin 0; max 60000Base delay before the first retry, in milliseconds (0-60000, default: 1000). Subsequent retries back off exponentially (delayMs * 2^attempts), capped by maxDelayMs.
maxDelayMsinteger60000nomin 0; max 600000Upper bound on any single retry wait, in milliseconds (0-600000, default: 60000). Caps both the exponential backoff and a provider-supplied Retry-After.

Before picking a value, match the policy to the failure you want to handle. Each option produces a different server decision:

Scroll to explore the table →
onFailureDecision
abortStay active for a retriable error; shut down for a permanent error.
retryRetry while attempts are below maxAttempts for a retriable error; shut down otherwise.
ignoreRecord the failure and continue to the next codon.

When you choose retry, the companion settings control how many retries follow the first run and how long the server waits between them. maxAttempts counts retries after the first run, so the default of 3 allows up to four runs of the codon; its bounds are 1–10. delayMs defaults to 1000 ms (0–60000) and maxDelayMs to 60000 ms (0–600000). This example is the shipped retry-configured codon, with the full retryConfig block in place:

JSON
      "id": "survey-and-extracts",
      "name": "Prepare native exports and review raw intake",
      "model": "haiku",
      "continuationMode": "fresh",
      "promptFile": "./prompts/survey-and-extracts.md",
      "rigSetup": [
        {
          "type": "copy",
          "copy": {
            "from": "rigs/native-inputs.ts",
            "to": "pipeline/native-inputs.ts"
          }
        },
        {
          "type": "command",
          "command": {
            "run": "bun pipeline/native-inputs.ts"
          }
        }
      ],
      "onFailure": "retry",
      "retryConfig": {
        "maxAttempts": 3,
        "delayMs": 20000,
        "maxDelayMs": 120000
      },

Retry delays grow as delayMs × 2^attempts and are clamped to maxDelayMs. A provider Retry-After hint replaces the computed delay and is clamped to that maximum; a hint above one hour is ignored instead of clamped. Retry counters live in memory and are per run, so a server restart mid-retry loses the count. The codon is not left failed, though: on the next start, a failed thread rolls back automatically to the newest git-confirmed completion checkpoint, and with autostart on it resumes with a fresh retry count. Without autostart, the rollback is done and the run waits for a client.

Automatic retry only helps if the failure is actually retriable, so check the retriable-versus-permanent failure classification before enabling it. Network failures, timeouts, 429s, and 5xx responses are retriable; authentication, invalid-request, and billing-quota failures are permanent; unrecognized text uses the retriable fallback.

For a manual retry, send codon.redo through the WebSocket control surface. It reruns the most recently attempted codon with the same configuration, whatever its status, and is rejected while a codon is running. With onFailure: "abort", an interactive server stays active after a retriable failure so a client can issue that command; the log reads Codon failed with retriable error. Server remains active. In --headless mode there is no client to retry, so the same failure records RunFailed, shuts down with reason codon failure (headless, no client to retry), and exits 1.

The capture below shows an automatic retry running to exhaustion: the policy resolves three times, then the server gives up and shuts down. Captured 2026-09-03 against hankweave@0.10.0; it records a rig-failure retry exhaust, not a mid-codon provider retry that later succeeds.

Output
[<ts>] [INFO] Resolving failure policy for codon update#0: onFailure=retry, retriable=true
[<ts>] [INFO] Resolving failure policy for codon update#0: onFailure=retry, retriable=true
[<ts>] [INFO] Resolving failure policy for codon update#0: onFailure=retry, retriable=true
[<ts>] [INFO] Codon update#0 exhausted 2 retry attempts, aborting
[<ts>] [DEBUG] [SentinelManager] Shutdown complete
[<ts>] [INFO] State transition: RunFailed
[<ts>] [INFO] Shutdown: Fatal error: Rig setup item 1 (exit code: 1)

Check-it: In the retry capture, find Resolving failure policy for codon update#0: onFailure=retry, retriable=true three times and then the retry-exhaustion message. Before using codon.redo, confirm that the codon is stopped.

Resume without re-running sealed work#

When a run stops for any reason other than a completed hank, you usually do not need to configure anything to continue it. Resume is the default; there is no --resume flag. Re-run the same command to select the newest execution directory for the same data hash, or use -e/--execution <dir> to select a specific directory that has state. Directory selection is covered in execution-directory.

The runtime opens a continuation run after the last completed codon and anchors it at that codon's completion checkpoint, so sealed codons are not run again. A codon in terminal completed state has no valid transitions in that run; --start-new creates a fresh run in which all codons can execute again. The captures below cover two different paths: Resuming from last completed codon: <id> comes from a completed run resumed in place, which does no new codon work; recovery after interruption during write-second (codon 2) takes the thread-failed rollback path and continues after write-line.

Completed-run reuse – resume log line:

Output
[<ts>] [INFO] Resuming from last completed codon: write-line in run <id>

Interrupted codon 2 – codon-start counts across the original and resumed runs:

Output
codon.started events across both runs — write-line: 1 · write-second: 2 (codon 1 was sealed before the kill, so it must start exactly once)

Interrupted codon 2 – checkpoint log after recovery:

Output
<sha7> completed:write-second [run:<id>] Write a second line
<sha7> Recovery snapshot before rollback to b88b7a2
<sha7> exit:write-second [run:<id>] Write a second line
<sha7> completed:write-line [run:<id>] Write one line
<sha7> Initial checkpoint setup

Check-it: The completed-run capture contains Resuming from last completed codon: write-line in run <id> and records zero new codon events. Across the interruption capture, write-line starts once and write-second starts twice; the checkpoint log keeps completed:write-second above completed:write-line and Initial checkpoint setup.

Resume has one behavior that surprises people: a continuation run reuses the persisted execution plan verbatim without re-validating it; only legacy provider-id migration can mutate stored models. Every entry already in the plan, failed or not yet started, keeps its persisted model and per-codon budget. Run-level ceilings such as maxDollars and maxTimeSeconds are read afresh when a new run starts. Therefore --model and edited-and-accepted hank values do not re-resolve any planned codon on resume.

Use this table when deciding whether an edit belongs in a resumed attempt or a fresh execution. "Saved codon" includes entries already in the persisted plan that have not started yet.

Scroll to explore the table →
What changedWhat a resumed attempt usesRecovery choice
Codon model, per-codon budget, rig command/copy definitions, outputFiles, or prompt-file pathsThe saved codon definition, not the edited definition in hank.json. Inline promptText and appendSystemPromptText are saved values too.Use --start-new to build a plan with those changes. Rollback alone also reuses the plan.
Contents of a file at a saved promptFile or appendSystemPromptFile pathThe file is read again when the prompt is built for the next attempt. A running session does not receive an edit retroactively.Edit the existing file, then retry or resume the affected work. Changing the path itself is a plan change.
A script or template already copied into the workspace by a rigThe prepared workspace copy; editing its hank-directory source does not update that copy. A resume starts a new continuation run, so the codon's rig runs again.Choose a rollback that reruns setup, or start fresh. If setup runs again, its saved copy operation reads the source then. (Replay's rig skip is replay mode, not resume.)
Run-level budget ceiling, such as --max-cost or --max-timeThe current run-level setting, combined with prior usage and the saved per-codon limits.Adjust the run envelope deliberately; this does not raise a frozen per-codon cap.

For a fresh isolated attempt at a later stage, explicitly stage the prior artifacts it needs and check them before agent work. Do not assume --start-new imports a previous execution's handoffs.

Two hash checks guard resume against changed inputs, and both apply only when you name the directory with -e/--execution. A managed rerun (no -e, no --start-new) never compares the hank hash: it picks the newest execution whose data hash matches and resumes on the saved plan, with no warning or prompt in any mode. On the explicit-directory path, a changed hank.json behaves differently by mode. Interactively, resume prints ⚠️ WARNING: hank.json has changed since last execution. and asks Continue with modified config?. In a non-interactive environment (headless, CI, piped stdio) it refuses before the server starts and exits 1, with an error that names the three ways out: -y to accept the changed config and resume, --start-new without -e for a fresh managed execution, or --start-new --force to restart in that directory. Either -y/--yes or -f/--force skips the refusal and the prompt; the resume still uses the saved plan, so the confirmation does not re-plan its codons.

The data-hash check is also explicit-directory-only. If the data hash changed on a -e resume, the CLI errors unless you pass --force or --ignore-data-mismatch, and the error names --execution <dir> --force, --execution <dir> --start-new --force, and plain hankweave with --execution omitted. A managed rerun with changed data is never blocked: when no execution matches the current data hash, Hankweave creates a new execution directory and runs every codon from the start.

Two terminal cases round out the behavior. If the execution has already completed, resuming it is announced, not silent: the runtime prints Resumed completed execution <id> — nothing to do. All codons already completed. Use --start-new to run fresh. (on stdout under --headless; as an Info: line in the interactive TUI), runs no codon, shuts down with reason all codons completed, and exits 0. Use --start-new when you want fresh work. With --no-autostart the notice does not appear: the handshake emits server.idle with reason startup and the server stays up. If the previous execution thread failed, startup automatically rolls back to the last successful checkpoint before continuing and logs Execution thread failed, rolling back....

Check-it: For a changed hank on an explicit -e resume, verify the interactive prompt or the non-interactive refusal that names -y, --start-new, and --start-new --force. For a completed execution, verify the Resumed completed execution notice and a zero codon-event delta. After a thread failure, look for the rollback message before the continuation run proceeds.

Roll back when file state is wrong#

Retry and resume assume the files on disk are still a good basis for the next attempt. When they are not, roll back. Rollback is the right move when file state is corrupted or when you want a different approach from a known-good state. It is a WebSocket/TUI operation, not a CLI action; there is no CLI rollback flag. Use checkpoint.list to enumerate targets, then choose rollback.toLastSuccess, rollback.toCodon, or rollback.toCheckpoint. rollback.toCheckpoint takes a commit SHA. rollback.toCodon takes a codon id plus a checkpointType of start, end, rig-setup, completed, error, or skipped: start and end are selectors (the codon's first checkpoint, or the checkpoint its status left), while a checkpoint itself records one of the four stored types rig-setup, completed, error, or skipped. With no runId, checkpoint.list lists every run's checkpoints, most recent first. The command catalog slice below shows the exact shapes the server accepts:

TYPESCRIPT
    type: z.literal("codon.next"),
  }),
  z.object({
    id: z.string(),
    type: z.literal("codon.skip"),
  }),
  z.object({
    id: z.string(),
    type: z.literal("codon.redo"),
  }),
  z.object({
    id: z.string(),
    type: z.literal("server.shutdown"),
    data: z
      .object({
        reason: z.string().optional(),
      })
      .optional(),
  }),
  z.object({
    id: z.string(),
    type: z.literal("server.force_shutdown"),
    data: z
      .object({
        reason: z.string().optional(),
      })
      .optional(),
  }),

  // Query checkpoints
  z.object({
    id: z.string(),
    type: z.literal("checkpoint.list"),
    data: z
      .object({
        runId: z.string().optional(), // Defaults to current run
      })
      .optional(),
  }),

  // Force stop current codon
  z.object({
    id: z.string(),
    type: z.literal("codon.forceStop"),
    data: z
      .object({
        reason: z.string().optional(),
      })
      .optional(),
  }),

  // Rollback to specific checkpoint
  z.object({
    id: z.string(),
    type: z.literal("rollback.toCheckpoint"),
    data: z.object({
      checkpointSha: z.string(),
      autoRestart: z.boolean().optional().default(false),
    }),
  }),

  // Rollback to codon + checkpoint type
  z.object({
    id: z.string(),
    type: z.literal("rollback.toCodon"),
    data: z.object({
      codonId: codonIdSchema,
      checkpointType: z.enum(["start", "end", "rig-setup", "completed", "error", "skipped"]),
      autoRestart: z.boolean().optional().default(false),
    }),
  }),

  // Rollback to last successful codon
  z.object({
    id: z.string(),
    type: z.literal("rollback.toLastSuccess"),
    data: z
      .object({
        autoRestart: z.boolean().optional().default(false),
      })
      .optional(),
  }),

  // Ping commands for testing
  z.object({
    id: z.string(),
    type: z.literal("ping"),
  }),
  z.object({

All three rollback commands default autoRestart to false. With that default, rollback ends at server.idle with reason rollback-completed and message Rollback completed. Use 'codon.next' to continue. Use codon.next, codon.skip, codon.forceStop, or codon.redo for manual sequencing around the rollback. rollback.toLastSuccess targets the newest completed codon's git-confirmed completion checkpoint; with none, it takes the newest codon that has any confirmed checkpoint (for a failed codon, its rig-setup checkpoint), and with nothing restorable it snapshots the work tree to a recovery/* branch and falls back to a continuation or a fresh run. The command signatures and WebSocket handshake belong to the protocol reference.

While rollback is in progress, state-modifying WebSocket commands are blocked. checkpoint.list, history.sync, shutdown commands, and pings remain allowed. Wait for rollback to finish before sending another state-changing command.

Rollback also restores archived files, and it does so by git reachability rather than by list order: entries archived strictly after the target on the abandoned line (and orphaned entries) are restored to agentRoot/, entries at or before the target or on unrelated timelines stay archived, and only restored entries leave the archive manifest. The target checkpoint's tree is authoritative, so an archived copy never overwrites a path the target already contains. An earlier release could report Target checkpoint … not found in manifest and un-archive the wrong entries; [removed 0.11.0] that error is gone.

A failed codon's workspace files survive in its error: checkpoint, committed in the shadow Git repository. Inspect that checkpoint using the explicit repository paths described in checkpoints, not the project's .git directory. The recipe below is illustrative: run it from the execution directory you are inspecting, substituting that directory's path for <execution-dir>:

⌁ Terminal
cd <execution-dir>
export GIT_DIR=.hankweave/checkpoints/.hankweavecheckpoints
export GIT_WORK_TREE=agentRoot
git --git-dir="$GIT_DIR" log --oneline --all

Do not confuse this operator rollback with the automatic rollback that resume performs after a thread failure. checkpointSha in an automatic resume rollback identifies the checkpoint commit; it is not an operator rollback.toLastSuccess command. When a later codon was interrupted, the continuation's startingConditions records reason: "rollback", the afterCodon anchor (the target codon for any non-rig-setup checkpoint, including a failed codon's error checkpoint), and the selected checkpointSha; a rig-setup rollback writes afterCodon: null with that codon's own rig-setup checkpoint. If codon 1 is killed before any successful checkpoint exists, the logs say Recovery degraded: no git-confirmed checkpoint in execution history; falling back to continuation or a fresh run (work tree snapshotted to recovery/<timestamp>); codon 1 then runs from scratch. The state and log captures below show both situations:

Output
{
  "currentRunId": null,
  "latestRun": {
    "runId": "<id>",
    "status": "completed",
    "startingConditions": {
      "type": "continuation",
      "source": {
        "runId": "<id>",
        "afterCodon": "write-line",
        "checkpointSha": "<sha>"
      },
      "reason": "rollback"
    },
    "codons": [
      {
        "codonId": "write-second",
        "status": "completed",
        "exitCode": 0
      }
    ]
  },
  "runs": [
    {
      "runId": "<id>",
      "status": "completed",
      "startingConditions": {
        "type": "continuation",
        "source": {
          "runId": "<id>",
          "afterCodon": "write-line",
          "checkpointSha": "<sha>"
        },
        "reason": "rollback"
      },
      "codons": [
        {
          "codonId": "write-second",
          "status": "completed",
          "exitCode": 0
        }
      ]
    },
    {
      "runId": "<id>",
      "status": "failed",
      "startingConditions": {
        "type": "fresh"
      },
      "codons": [
        {
          "codonId": "write-line",
          "status": "completed",
          "exitCode": 0
        },
        {
          "codonId": "write-second",
          "status": "running",
          "exitCode": null
        }
      ]
    }
  ]
}
Output
[<ts>] [INFO] [DEBUG] Initializing checkpoints...
[<ts>] [INFO] Fresh run <id> - branch will be created on first checkpoint
[<ts>] [DEBUG] Built execution thread: 0 codons across 1 runs, next codon: write-line
[<ts>] [INFO] Added checkpoint patterns: out.txt
[<ts>] [INFO] Shutting down server: SIGTERM
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-line with status completed
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-line with status exit
[<ts>] [ERROR] Exit checkpoint skipped: Checkpoint creation failed for codon write-line (exit): fatal: a branch named 'run-<id>' already exists
[<ts>] [INFO] Shutting down sentinel manager...
[<ts>] [INFO] Shutdown: SIGTERM (exit code: 0)
[<ts>] [INFO] [DEBUG] Initializing checkpoints...
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: none
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: none
[<ts>] [ERROR] Recovery degraded: no git-confirmed checkpoint in execution history; falling back to continuation or a fresh run (work tree snapshotted to recovery/2026-09-27T08-46-08-460Z)
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: none
[<ts>] [INFO] Fresh run <id> - branch will be created on first checkpoint
[<ts>] [DEBUG] Built execution thread: 0 codons across 1 runs, next codon: write-line
[<ts>] [INFO] Added checkpoint patterns: out.txt
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-line with status completed
[<ts>] [INFO] [CHECKPOINT-DEBUG] Created checkpoint: <sha> (completed) on branch run-<id>
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: write-second
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: write-second
[<ts>] [INFO] Added checkpoint patterns: out.txt, second.txt
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-second with status completed
[<ts>] [INFO] [CHECKPOINT-DEBUG] Created checkpoint: <sha> (completed) on branch run-<id>
[<ts>] [DEBUG] Built execution thread: 2 codons across 1 runs, next codon: none
[<ts>] [DEBUG] Built execution thread: 2 codons across 1 runs, next codon: none
[<ts>] [INFO] Shutting down server: all codons completed
[<ts>] [INFO] Shutting down sentinel manager...
[<ts>] [INFO] Shutdown: all codons completed (exit code: 0)

A rollback that is rejected before it changes anything (an unknown checkpoint, for example) leaves the work tree untouched. One that fails after it has checked out a checkpoint or removed rig directories throws RollbackMutatedWorkspaceError: the tree may be half-restored, startup stops with Recovery stopped: … the work tree may be half-restored …, and the pre-rollback tree is kept on a recovery/* branch. Do not start new work on that tree until you have inspected it.

Check-it: Confirm reason: "rollback" in the state capture. In the no-successful-checkpoint capture, verify Recovery degraded: no git-confirmed checkpoint in execution history; falling back to continuation or a fresh run and that codon 1 starts from scratch. After any rollback, confirm the restored files match the target checkpoint before continuing.

Recover after an exhaust extension fails#

An exhaustWithPrompt extension is attempted only after a clean codon exit: exit code 0, a received result, no failure reason, no interruption, intact budget, and fewer than maxExtensions extensions. Extension rounds run inside one codon lifecycle with no checkpoint between them, so a failure in the final round does not strike after a seal: the codon is recorded failed with an error checkpoint, resume or retry runs it again, and onFailure decides the run outcome like any other failure. [changed 0.11.0] An earlier release appeared to fail after the codon had sealed; that observation came from a 0.10.0 forge run.

maxExtensions is the extension cap; its documented default is 100. A successful extension emits the journaled, sentinel-routed codon.extended event (a sentinel is a configured observer that can react to run events) with the extension number and cumulative token and cost totals:

Scroll to explore the table →
idcategoryjournaledsentinelRoutedpayloadFieldsreceipts
codon.extendedserver-statetruetruecodonId, codonName, extensionNumber, exhaustWithPrompt, cumulativeTokens, cumulativeCostschemas/event-schemas.ts:677, schemas/event-schemas.ts:1076, schemas/event-schemas.ts:1350, hankweave-runtime.ts:3378

Check-it: Inspect codon.extended for extensionNumber, cumulativeTokens, and cumulativeCost. If an extension fails, expect the codon to be recorded failed with an error checkpoint, then apply the normal failure policy and use the checkpoint history to choose the next recovery move.

Replay recorded codon output#

Sometimes you do not want to recover a run at all; you want to see what it did. Use --replay <dir> to reproduce recorded codon output from per-codon logs on a temporary copy. No codon makes a model call, but replay is not offline or free: the runtime's startup provider health checks still run and can make billable calls when provider credentials are set. With no provider credentials in the environment, those checks skip every provider.

Replay the kept execution directory (substitute its path for <kept-execution-dir>) with the pinned launcher:

⌁ Terminal
bunx hankweave@0.11.0 --replay <kept-execution-dir>

Replay discovers the hank and data paths from <dir>/.hankweave/execution-meta.json, copies the execution directory to a temporary directory, removes the copied runtime lock, and preserves the original. It starts a fresh replay run even when the source execution is complete. It requires every codon's log file; if one is missing, it hard-errors and lists the missing file instead of falling back to a real execution. Rig setup is skipped because the copied directory already holds the post-rig-setup filesystem state; sentinels are skipped because replay reproduces only recorded codon output, and sentinel analysis would make fresh provider calls that were never recorded. The temporary copy is removed when the replay process exits.

Replay also skips the per-model startup self-tests and the Pi model_catalog preflight, which a normal startup (fresh or resumed) and --validate run. The skip is possible because replay's codons use a replay process manager and never launch a harness, not because replay makes no provider calls. --replay is documented in --help.

Two details matter when you script around replay. Replay pacing comes from recorded timestamps; for recordings without usable timestamps, HANKWEAVE_REPLAY_SPEED_MS sets the per-line floor, which defaults to 5 ms. --replay and --execution are mutually exclusive, and the exit-code-1 error names both options.

The capture below shows a successful replay of a completed execution, including the metadata lookups and the temporary copy:

Output

╭────────────────────────────────────────────────────────────────────╮
│  Hankweave v0.11.0                                                 │
│  linux x64 • node v22.23.2                                         │
╰────────────────────────────────────────────────────────────────────╯

[REPLAY] Using hank config from execution metadata: <workspace>/fixtures/scenarios/silent-reuse/hank.json
[REPLAY] Using data source from execution metadata: <workspace>/fixtures/scenarios/silent-reuse/data
[REPLAY] Copied execution dir to <capture-attempt><tmp>/hankweave-replay-1790499103051-okkd
Removed a stale diet pair found beside the live events.jsonl (verified duplicate of the journal; interrupted prune or diet)
Resuming: hankweave-replay-1790499103051-okkd
  Source → data
  Exec   → <capture-attempt><tmp>/hankweave-replay-1790499103051-okkd
  SDKs   → Claude node_modules ✓

╭──────────────────────────────────────────────────────────────────────────────╮
│  Silent reuse v1.0.0                                                         │
│  1 codon • 0 loops                                                           │
╰──────────────────────────────────────────────────────────────────────────────╯

└─ [1] write-line (Write one line)
      model: haiku │ mode: fresh │ prompts: 1 (2 lines)
      checkpointedGlobs: 1


══════════════════════════════════════════════════
  Hankweave Server Started
  WebSocket: ws://localhost:<port>
══════════════════════════════════════════════════

Running in headless mode on port <port>
➜ Listening on: http://localhost:<port>/ (all interfaces)

exit=0

Check-it: The completed-execution capture shows both metadata lookups, the copied replay directory, Resuming: hankweave-replay-…, and exit 0. Its manifest also checks that the original execution state and journal remain byte-identical. Do not use this capture to claim that changed rigs, external side effects, or the startup health checks were replayed.

Choose retry, edit, rollback, or a new run#

With the failure record in hand, the four moves reduce to one question: what needs to change before the next attempt? The table maps each answer to its move:

Scroll to explore the table →
MoveChoose it whenWhat changes
RetryfailureReason.retriable is true and nothing needs fixing.The codon retries the same plan; a predictable wrong prompt or missing file reproduces while consuming tokens.
EditInstructions, a prompt file, or the environment needs correction.Prompt contents are read from disk when a codon starts, so a prompt-file edit applies to the next attempt. Editing fields in an already-planned hank.json does not change that plan.
Roll backFile state is corrupted, or you want a different approach from a known-good state.Restore the last successful checkpoint rather than the beginning, preserving valid partial work.
Start overThe failed codon's model must change, or the persisted plan is no longer the basis you want.Use --start-new to create a fresh run and resolve the hank again.
FIG. 1 Choosing the smallest recovery move for a failed run
Read the diagram as text
Output
                      +------------------------+
                      | what needs to change?  |
                      +-----------+------------+
        +--------------+----------+-----------+-----------------------+
        | a retriable  | the prompt or        | corrupted | the model or the
        | failure      | instructions         | file state| persisted plan
        v              v                      v           v
+-----------------+ +---------------------+ +------------------+ +---------------------+
| retry the codon | | edit the prompt file| | roll back to a   | | start over with     |
|                 | | and resume          | | checkpoint       | | --start-new         |
+-----------------+ +---------------------+ +------------------+ +---------------------+

Two constraints from the earlier sections apply to any choice. Rollback, like plain resume, starts a continuation that reuses the persisted execution plan, so it does not re-resolve edited model or budget fields; changing the model of any planned codon requires --start-new, ideally with a separate execution and output directory for a clean comparison. And there are no --start-at or --end-at codon-range flags: isolate one codon with the state file and a targeted rollback instead.

Check-it: Name the deciding signal before acting: failureReason.retriable, the prompt-file change, corrupted file state, or the need for --start-new.

Avoid recovery patterns that compound failures#

The moves above are safe in themselves; the damage usually comes from applying the wrong one repeatedly. Four patterns account for most of it:

  1. Do not retry an unchanged predictable failure. Check failureReason.retriable first. A wrong prompt or missing file will reproduce instead of benefiting from another attempt.
  2. Rewrite instead of patching a prompt forever. Appending a fix after each failure accumulates contradictory instructions. Identify what the agent misunderstood, then rewrite the prompt with a clear structure.
  3. Isolate instead of deleting codons. Removing codons can remove the codon that exposes the bug rather than the codon that causes it. Start with the state file and a targeted rollback; do not use the nonexistent --start-at or --end-at flags.
  4. Keep useful partial progress. Roll back to the last successful checkpoint, not the beginning, so valid work completed before the failure remains available.

Check-it: Record the deciding signal, the rewritten prompt when instructions were wrong, or the failed codon and rollback target when file state was involved.