Skip to main content
Smithers supports Hot Module Replacement (HMR) for workflows. When enabled, the engine watches your source files and reloads the workflow on save. In-flight tasks continue uninterrupted; only newly scheduled tasks use the updated code. This is especially useful for long-running workflows where you want to tweak prompts, adjust agent configuration, or restructure the task graph without losing hours of accumulated state.

Quick Start

Add --hot to any run or resume command:
smithers run workflow.tsx --hot
smithers resume workflow.tsx --run-id abc123 --hot
That’s 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 this architecture:
  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

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] ⚠ Workflow reload blocked: Schema change detected; restart required to apply schema changes.

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 PLANNING_PROMPT v1 will finish with v1, even if you’ve 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.

CLI Output

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

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 use case:
// 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. Keep side effects inside build() or use createSmithers() which is automatically cached in hot mode.

Use with resumability

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