- the run input payload
- task output rows
- internal workflow metadata
Run Input
When you kick off a workflow, you hand it a payload. That payload is the entire context your workflow gets from the outside world:ctx.input. If the run crashes and resumes, the same input is still there — unless you explicitly override it.
So what should go in input? Three things:
- user-supplied run context
- durable across resume
- available everywhere through
ctx.input
Task Outputs
Here is where your domain data lives. Most Smithers workflows define output schemas up front withcreateSmithers(...):
- creates the SQLite table
- maps the schema key to a snake_case table name
- adds
runId,nodeId, anditerationbookkeeping columns - validates agent output before persisting it
runIdnodeIditerationattempt- approval metadata
Identity of an Output Row
Here is a subtlety that trips people up. Two different tasks can write to the same output schema. The same task can write to it ten times inside a loop. So how does Smithers know which row is which? The answer: output identity is not “table name only.” Each row is keyed by:- run id
- task id (
nodeId) - iteration when the task is inside a loop
ctx.output(...), ctx.outputMaybe(...), and ctx.latest(...) all require both an output target and a nodeId. The table tells Smithers where to look. The node ID and iteration tell it which row you mean.
Custom Drizzle Tables
Sometimes you already have a table, or you need a schema that Smithers cannot auto-generate. In that case,<Task output={...}> can point at a custom Drizzle table.
Fair warning: when you go this route, you take on responsibility that Smithers normally handles for you:
- creating and migrating the table
- including Smithers bookkeeping columns such as
runIdandnodeId - including
iterationin looped tasks - optionally pairing the table with
outputSchemafor stricter validation
createSmithers(...) can express your schema, use it.
Internal Smithers Metadata
Open your database and you will see tables prefixed with_smithers_. Do not be alarmed. These are Smithers’ own operational tables:
- runs
- node state
- task attempts
- render frames
- approvals
- cache entries
- tool-call logs
- event journal
- loop state
Why the Separation Matters
Ask two questions about any completed task: Your workflow output answers: what did this task produce? Smithers metadata answers:- when did it run?
- how many attempts did it take?
- was it cached?
- did it wait for approval?
- which loop iteration produced it?
Schema Changes
Changing a Zod output schema is not just a prompt tweak. It is a persistence change. The table on disk has to match the schema in code. Typical examples:- adding a field
- removing a field
- changing a field type
- tightening validation rules
Direct Queries
Smithers does not hide SQLite from you. The database is right there. Open it, poke around, write queries. Use output tables when you care about business results. Use_smithers_* tables when you care about execution history.
This is one of the advantages of keeping the layers separate: you can hand your output tables to an analyst who has never heard of Smithers, and the data makes sense on its own.
Mental Model
When in doubt, apply this rule of thumb:ctx.inputis run-scoped input- output tables hold validated task results
_smithers_*tables hold orchestration state
_smithers_*. The line is clean. Keep it that way.
Next Steps
- Execution Model — See how these tables participate in render, scheduling, and resume.
- Structured Output — Validation and persistence details for task outputs.
- Debugging — Query the internal tables directly when a run behaves unexpectedly.