Core Concepts
Rigs

Rigs

Every codon needs a stage to perform on. Rigs build that stage before the agent arrives, preparing the environment deterministically. They copy files, create directories, install dependencies—any setup that must exist for the agent to do its work.

What is a Rig?

Need a TypeScript project with dependencies installed before your agent starts coding? That's a rig. Need a template structure copied into place? Rig. Directories created, config files prepared? Also rigs.

Rig Setup Flow

The defining property of rigs is that they're deterministic. Same inputs, same outputs, every time. This is critical for reproducibility: when you roll back and re-run a codon, the rig sets up the exact same environment it did before.

Key Insight: Rigs run during the preparing phase, before the agent spawns. If a rig fails, the codon fails immediately. This gives you fast feedback on setup problems, rather than burning tokens on agent work that was destined to fail.

Anatomy of a Rig

What is the execution directory? Hankweave creates an isolated workspace for each run, separate from your source files. The "execution directory" is where your codons run, rigs copy files, and the agent performs its work. This separation prevents accidental modifications to your project files.

Rigs are defined in the rigSetup field of a codon—an array of operations that execute in order:

Text
{
  "hank": [
    {
      "id": "schema-generator",
      "name": "Generate Schemas",
      "model": "sonnet",
      "continuationMode": "fresh",
      "promptText": "Generate TypeScript schemas from the CSV files...",
      "rigSetup": [
        {
          "type": "copy",
          "copy": {
            "from": "./typescript_structure",
            "to": "typescript_code"
          }
        },
        {
          "type": "command",
          "command": {
            "run": "bun install",
            "workingDirectory": "lastCopied"
          }
        }
      ]
    }
  ]
}

This rig:

  1. Copies the typescript_structure directory to typescript_code in the execution directory.
  2. Runs bun install inside the newly copied typescript_code directory.

Operation Types

Rigs support two types of operations: copying files and running commands. Most rigs use a combination of both.

Copy Operations

Copy operations move files or directories into the execution environment.

Text
{
  "type": "copy",
  "copy": {
    "from": "./templates/react-app",
    "to": "frontend"
  }
}
FieldDescription
fromSource path. Can be relative to the config file or absolute.
toTarget path. Must be relative to the execution directory.

Path Resolution Examples:

Assume the config file is at /projects/my-hank/hank.json.

Text
// Copy a sibling directory
{ "from": "./templates/app", "to": "my-app" }
// Source: /projects/my-hank/templates/app
// Target: <execution>/my-app
 
// Copy a file and rename it
{ "from": "./templates/base-config.json", "to": "src/config.json" }
// Source: /projects/my-hank/templates/base-config.json
// Target: <execution>/src/config.json
 
// Copy from an absolute source path
{ "from": "/shared/templates/common", "to": "libs/common" }
// Source: /shared/templates/common
// Target: <execution>/libs/common
⚠️

Important: The to path specifies the full destination, including the name. The operation copies from as to, not into to. (See "Common Mistakes" for a detailed example.)

Command Operations

Command operations run shell commands within the execution environment.

Text
{
  "type": "command",
  "command": {
    "run": "npm install",
    "workingDirectory": "project"
  }
}
FieldDescription
runThe shell command to execute.
workingDirectoryWhere to run the command: "project" (the execution directory root) or "lastCopied" (the target directory of the previous copy operation).

The lastCopied option is especially useful for running commands inside a directory you just copied:

Text
{
  "rigSetup": [
    {
      "type": "copy",
      "copy": {
        "from": "./typescript_structure",
        "to": "typescript_code"
      }
    },
    {
      "type": "command",
      "command": {
        "run": "bun install",
        "workingDirectory": "lastCopied"
      }
    },
    {
      "type": "command",
      "command": {
        "run": "bun run build",
        "workingDirectory": "lastCopied"
      }
    }
  ]
}

Here, both bun install and bun run build execute inside the typescript_code directory.

Execution Order and Failure

Rig operations run sequentially. If any operation fails, the entire codon fails immediately—there's no point spawning an agent into a broken environment.

Rig Execution Order

This fail-fast behavior is usually what you want, but some operations are expected to fail on certain runs.

The allowFailure Flag

The allowFailure flag lets an operation fail without halting the entire codon. This is critical for loops where an operation might only succeed after the first iteration.

You can also globally ignore all rig failures using the --ignore-rig-failures CLI flag, which is useful when resuming workflows where rig setup has already partially completed:

Text
hankweave --execution ./my-run --ignore-rig-failures
Text
{
  "type": "loop",
  "id": "refinement-loop",
  "codons": [
    {
      "id": "refine",
      "rigSetup": [
        {
          "type": "command",
          "command": {
            "run": "cp output/result.json backup/result.json"
          },
          "allowFailure": true
        }
      ]
    }
  ]
}

On the first iteration, output/result.json doesn't exist yet. The cp command fails, but because allowFailure is true, the operation logs a warning and the codon continues. On subsequent iterations, the file exists and the copy succeeds.

Rig Allow Failure

⚠️

Critical for Loops: Any operation that might fail on some iterations (e.g., copying a file that doesn't exist on the first pass) should use allowFailure: true. Without it, your loop will break unexpectedly.

When Rigs Run

Rigs execute during the preparing phase of a codon's lifecycle, before the agent is spawned.

When Rigs Run

After the rig completes successfully, Hankweave creates a rig-setup checkpoint. This is important for rollbacks, as it allows you to re-run just the agent's work without re-running the rig.

Checkpointing and Rollback

Rig setup gets its own checkpoint, separate from the one created when the agent finishes its work.

Text
Checkpoint: rig-setup:schema-generator [run:abc123] Schema Generation
    └── Files copied, dependencies installed

Checkpoint: completed:schema-generator [run:abc123] Schema Generation
    └── Agent work complete, schemas generated

This separation enables more precise rollbacks:

ScenarioRollback ToResult
The agent made a mistakerig-setup checkpointRe-run the agent with the same initial setup.
The setup itself was wrongPrevious codon's checkpointRe-run both the rig and the agent.
Everything is wrongStart of the hankRe-run the entire process from scratch.

Archiving Rig Files

When your rig copies large directories (like project templates), you can archive them after the codon completes successfully. This keeps the agent's workspace (agentRoot/) clean across loop iterations while preserving the files for debugging.

Text
{
  "id": "build-step",
  "rigSetup": [
    {
      "type": "copy",
      "copy": {
        "from": "./templates/large-project",
        "to": "project"
      },
      "archiveOnSuccess": true
    }
  ],
  "promptText": "Build the project..."
}

When archiveOnSuccess is true:

  • After the codon completes successfully, files are moved from agentRoot/ to rigArchive/<codonId>/
  • For loops, archives are organized as rigArchive/<loopId>-<iteration>/
  • Rolling back automatically restores archived files

Archiving in Loops: The Dynamic Programming Pattern

Archiving is particularly powerful in loops where each iteration needs a clean workspace but you want to preserve results. This is similar to dynamic programming's memoization—compute once, archive, continue:

Text
{
  "type": "loop",
  "id": "process-reports",
  "terminateOn": { "type": "iterationLimit", "limit": 5 },
  "codons": [
    {
      "id": "generate-report",
      "rigSetup": [
        {
          "type": "copy",
          "copy": { "from": "./templates/report", "to": "current-report" },
          "archiveOnSuccess": true
        }
      ],
      "continuationMode": "continue-previous",
      "promptText": "Generate the next report from the queue..."
    }
  ]
}

After 3 iterations:

Text
execution-dir/
├── agentRoot/
│   └── current-report/     # Only current iteration's files
├── rigArchive/
│   ├── process-reports-0/  # Iteration 0's completed report
│   │   └── current-report/
│   ├── process-reports-1/  # Iteration 1's completed report
│   │   └── current-report/
│   └── process-reports-2/  # Iteration 2's completed report
│       └── current-report/
└── .hankweave/

Each iteration gets a fresh template, works on it, and the result is preserved before the next iteration begins.

When to use archiving: Use archiveOnSuccess when copying large template directories that would otherwise accumulate across loop iterations, filling up your workspace. Also useful when each iteration should start with a clean slate but you need to preserve all intermediate results.

Path Security

To prevent unintended changes to your system, rig copy operations enforce several security constraints:

RuleExampleResult
No .. in target paths{ "to": "../outside" }Rejected
Target path must be relative{ "to": "/etc/passwd" }Rejected
Target parent must exist{ "to": "nonexistent/file.txt" }Fails at runtime
Source can be absolute{ "from": "/templates/app" }Allowed

The bottom line: you can copy from anywhere, but you can only write inside the isolated execution environment. No escaping.

Common Patterns

These are rig patterns we use most often in production.

Pattern 1: TypeScript Project Setup

Copy a template project structure and install its dependencies.

Text
{
  "id": "typescript-setup",
  "rigSetup": [
    {
      "type": "copy",
      "copy": {
        "from": "./templates/typescript-api",
        "to": "api"
      }
    },
    {
      "type": "command",
      "command": {
        "run": "npm install",
        "workingDirectory": "lastCopied"
      }
    },
    {
      "type": "command",
      "command": {
        "run": "npm run build",
        "workingDirectory": "lastCopied"
      }
    }
  ],
  "promptText": "Implement the API endpoints in src/api/..."
}

Pattern 2: Directory Creation

Create empty directories that the agent will need to write to.

Text
{
  "id": "analyze-data",
  "rigSetup": [
    {
      "type": "command",
      "command": {
        "run": "mkdir -p notes output/charts output/reports"
      }
    }
  ],
  "promptText": "Analyze the data and write findings to notes/..."
}

Pattern 3: Config File Setup

Copy standard configuration files like .eslintrc.json or tsconfig.json into the execution environment.

Text
{
  "id": "setup-config",
  "rigSetup": [
    {
      "type": "copy",
      "copy": {
        "from": "./configs/base-eslint.json",
        "to": ".eslintrc.json"
      }
    },
    {
      "type": "copy",
      "copy": {
        "from": "./configs/base-tsconfig.json",
        "to": "tsconfig.json"
      }
    }
  ],
  "promptText": "Set up the application configuration..."
}

Pattern 4: Loop-Safe Backup

In a loop, back up the results from the previous iteration before starting the next one. This works even on the first iteration when there's nothing to back up yet.

Text
{
  "type": "loop",
  "id": "iterative-improvement",
  "terminateOn": { "type": "iterationLimit", "limit": 5 },
  "codons": [
    {
      "id": "improve",
      "rigSetup": [
        {
          "type": "command",
          "command": {
            "run": "cp -r src backup_src_$(date +%s)"
          },
          "allowFailure": true
        }
      ],
      "promptText": "Review and improve the code in the `src` directory..."
    }
  ]
}

On the first pass, src might not exist, causing the cp command to fail. With allowFailure: true, the operation logs a warning and continues. On subsequent iterations, the command succeeds.

Common Mistakes

These issues trip up even experienced users. Review them to save yourself some debugging time.

⚠️

Mistake: Misunderstanding the to path

The to path specifies the full destination, including the final name. It copies from as to, not into to.

Example: Given a source directory ./templates:

Text
templates/
└── tsconfig.json

This operation renames templates to app:

Text
{ "from": "./templates", "to": "app" }

Resulting structure:

Text
app/
└── tsconfig.json

To copy the templates directory inside an app directory, be explicit:

Text
// Correct
{ "from": "./templates", "to": "app/templates" }

This correctly creates app/templates/tsconfig.json.

⚠️

Mistake: Forgetting allowFailure in loops

Text
{
  "type": "loop",
  "codons": [
    {
      "rigSetup": [
        {
          "type": "command",
          "command": { "run": "cp output.json backup.json" }
          // Missing allowFailure: true!
        }
      ]
    }
  ]
}

On the first iteration, output.json likely doesn't exist. The copy command fails, which causes the codon to fail, which stops the entire loop. Your 50-iteration refinement process dies before it even starts. Add allowFailure: true for any operation that isn't guaranteed to succeed on every iteration.

⚠️

Mistake: Assuming parent directories exist

Text
{ "from": "./config.json", "to": "deep/nested/path/config.json" }

If the deep/nested/path/ directory doesn't exist in the execution environment, this copy operation will fail. You must first create the directory with a command operation (mkdir -p ...) before copying a file into it.

⚠️

Mistake: Using lastCopied without a prior copy

The workingDirectory lastCopied refers to the destination of the most recent copy operation. If no copy operation has run yet in the rig, this value is undefined and the command will fail.

Text
// Invalid rig setup
{
  "rigSetup": [
    {
      "type": "command",
      "command": {
        "run": "npm install",
        "workingDirectory": "lastCopied" // Fails: no preceding copy
      }
    }
  ]
}

Always ensure a copy operation precedes any command that uses lastCopied.

Rigs vs. Agent Work

When should setup happen in a rig versus in the agent's prompt?

Use a Rig When…Use Agent Work When…
The setup is deterministic.The setup requires judgment or creativity.
The same setup is needed for every run.The setup depends on runtime data or context.
You need a checkpoint before agent work.The setup is the core task for the agent.
You need to install dependencies.You are creating novel files from scratch.

Rule of thumb: If you could write a shell script to do it, use a rig. If it requires reasoning about the specific situation, let the agent handle it.

  • "Copy a project template and install dependencies" → Rig
  • "Create a new React component based on these requirements" → Agent
  • "Run database migrations before starting" → Rig
  • "Write new migration files for this schema change" → Agent

Related Pages

  • Codons — The building blocks that contain rigs
  • Loops — Where allowFailure becomes critical
  • Execution Flow — How rigs fit into the execution lifecycle

Next Steps

With rigs, your codons start from a known, reproducible state every time. This is the foundation for reliable and complex agent workflows.

Rig failures appear clearly in the event stream with specific error messages, making them straightforward to diagnose when things go wrong.