Skip to main content
Your workflow has been running for forty minutes. Three tasks are done. The fourth is in progress. You just realized the prompt for the fifth task is wrong. Without hot reload, you kill the process, fix the prompt, and start over. Forty minutes gone. With hot reload, you save the file. The engine picks up the change. The in-flight task finishes with its original prompt. The fifth task uses your new prompt. Zero wasted work.

Quick Start

Add --hot true to any up command:
smithers up workflow.tsx --hot true
smithers up workflow.tsx --run-id abc123 --resume true --hot true
That is it. Edit any file in your workflow’s directory tree, save, and the engine picks up the changes on the next render cycle.

How It Works

Smithers is built on React. Your workflow’s build(ctx) function is a React component tree that the engine re-renders every loop iteration using a custom React reconciler. All run state lives in SQLite, not in the React fiber tree. Hot reload leverages that architecture in five steps:
  1. Watch — The engine watches your workflow’s directory tree (excluding node_modules/, .git/, .jj/, .smithers/).
  2. Overlay — On file change, Smithers creates a “generation overlay” — a copy of your source tree with fresh file URLs so every module (including transitive dependencies) is re-evaluated.
  3. Import — The new workflow module is imported from the overlay. createSmithers() returns the cached DB connection and schema maps (no duplicate connections).
  4. Swap — Only workflow.build is replaced. The database, schema registry, and all persisted state remain untouched.
  5. Wake — The engine loop is woken immediately (even if tasks are still running) so it re-renders with the new code.
File saved -> watcher detects -> overlay built -> module imported -> build swapped -> re-render
                                                                                       |
                                                                         new tasks use new code
                                                                         in-flight tasks unaffected
The key insight: state is in SQLite, not in your code. Swapping the code does not lose state.

What You Can Change Live

These changes take effect on the next render cycle:
ChangeEffect
Prompt strings / .md filesNew tasks get the updated prompt
Focus lists, config valuesScheduler sees new priorities
Agent configuration (model, timeout, system prompt)New agent instances for new tasks
JSX tree structure (add/remove/reorder tasks)New plan tree on next render
Concurrency / retry settingsApplied to newly scheduled tasks

What Requires a Restart

These changes are blocked to prevent data corruption:
ChangeWhy
Output Zod schemas (shape changes)Schema identity is used for output table resolution
createSmithers() dbPathWould create a second database connection
Adding/removing output schema keysChanges the schema registry
If you attempt a schema change, the engine logs a warning and keeps running with the previous code:
[00:12:34] Warning: Workflow reload blocked: Schema change detected; restart required to apply schema changes.
Why the hard line on schemas? Because output tables are keyed by schema identity. If you change a schema mid-run, existing rows would no longer match the new shape. That is data corruption. So Smithers refuses and tells you to restart.

In-Flight Task Behavior

When a hot reload changes the task graph, tasks that are already running are not cancelled. They continue with the code they were launched with and their output is persisted normally. This means:
  • A task started with prompt v1 will finish with v1, even if you have since saved v2.
  • If a reload removes a task from the tree, its in-flight attempt still completes. The output may go unused by downstream tasks.
  • If a reload changes a task’s id, the old in-flight attempt is treated as a different node. Both may run.
That last point is worth repeating. Changing a task ID during a hot reload does not “rename” the task. It creates a new one. The old one is orphaned.

CLI Output

When hot reload detects and applies changes, you will see:
[00:05:12] File change detected: 1 file(s)
[00:05:12] Workflow reloaded (generation 1)
On errors:
[00:05:12] Warning: Workflow reload failed: SyntaxError: Unexpected token
The workflow continues running with the last valid code. Fix the error and save again. No panic required.

Events

Hot reload emits events through the standard event system:
EventWhen
WorkflowReloadDetectedFile changes detected (before reload attempt)
WorkflowReloadedReload succeeded; includes generation number and changedFiles
WorkflowReloadFailedReload failed (syntax error, import error); includes error
WorkflowReloadUnsafeReload blocked (schema change); includes reason
These events are persisted to the NDJSON event log and visible via onProgress.

Options

CLI FlagRunOptions fieldDescriptionDefault
--hothot: trueEnable hot reloadDisabled
Advanced options (via RunOptions.hot object):
FieldDescriptionDefault
rootDirDirectory to watchWorkflow file’s directory
outDirOverlay output directory.smithers/hmr
maxGenerationsNumber of overlay generations to keep3
cancelUnmountedCancel in-flight tasks that become unmounted after reloadfalse
debounceMsDebounce interval for file change events100

Tips

Keep prompts in separate files

If your prompts live in .md or .ts files imported by your workflow, editing them triggers a hot reload automatically. This is the most common workflow: edit a prompt, save, watch the next task pick it up.
// prompts/planning.md changes -> hot reload -> new tasks use updated prompt
import planningPrompt from "./prompts/planning.md";

export default smithers((ctx) => (
  <Workflow name="my-workflow">
    <Task id="plan" agent={new ClaudeCodeAgent({ systemPrompt: planningPrompt })}>
      ...
    </Task>
  </Workflow>
));

Avoid module-scope side effects

Code that runs at import time (outside of build()) is re-executed on every reload. If your module-level code opens a file, starts a server, or prints a banner, that will happen again on every save. Keep side effects inside build() or use createSmithers() which is automatically cached in hot mode.

Use with resumability

Hot reload and resumability work together naturally. You can:
  1. Start a run with --hot
  2. Edit files while it runs
  3. Kill the process (Ctrl+C)
  4. Resume with smithers up workflow.tsx --run-id <id> --resume true --hot true
The resumed run picks up your latest code and continues watching for changes.