Coming from Temporal
This page covers how a Hyperspool workflow runs, the rules for writing one, and what happens when you change code while runs are in flight.
The model
Section titled “The model”A workflow is a normal program the engine can run more than once. Each
durable call (step, sleep, wait signal, wait event,
activity wait) is a checkpoint whose result is recorded the first time
it completes. When the run resumes, the script executes again from the top;
calls already in the log return their recorded results at once, and
execution runs through to where it left off.
Like this:
spool step build -- makespool sleep 7dspool wait signal approvespool step deploy -- ./deploy.shIt can be written in any language the agent can run, and the durable
calls are a subprocess away (spool step …) or a socket
write away (the Python SDK, which the managed environments import as
hyperspool). See workflows in your
language for the other languages.
Suspension is a real process exit. When a script sleeps or waits, its process ends and the agent is freed, so a run waiting a month holds no worker and no memory.
The rules
Section titled “The rules”Five of them; the authoring model is the full treatment:
- Wrap side effects and nondeterministic reads in a step. Anything touching
the outside world, and anything a later decision depends on that could
change between passes, belongs inside a
step. - Make steps idempotent where you can. A step that dies after acting but before its result is recorded will run again.
- Keep between-step code cheap and repeatable. It re-runs on every pass, so branch on values you captured in a step.
- A step name is a place in the script. Reaching it again is an ordinary loop, and each pass records its own checkpoint, so nothing needs a per-iteration name.
- Some names stay unique per run, because something refers back to
them: an
activity(activity waitretrieves it), a barelock <group>, and astate cas/state incr--opname.
If a replay reaches a different durable call than the first pass recorded
at that position, the run fails with a determinism error instead of
quietly re-running work. SPOOL_REPLAY_MODE=lenient in an agent’s
environment turns that check off for everything the agent runs, so it is
available only on agents you run yourself; managed environments always
check. When replay goes wrong is the page to
read next: a real determinism error, how to trace it back to the branch
that caused it, and what the engine doesn’t catch.
Changing code under a live run
Section titled “Changing code under a live run”A run replays against the definition it started with. Shipping new workflow code leaves in-flight runs alone: they finish on the definition they were submitted with, and new runs pick up the change. The definition is stored with the run, so editing a workflow doesn’t touch your agents.
The pin covers the submitted file only. Commands a script calls
(./deploy.sh, a binary on the agent) and the spool binary itself are
whatever is on the agent when the run resumes.
The cost: a long-lived run finishes on the logic it started with, so a bug fix reaches it only if you cancel and resubmit.
Vocabulary
Section titled “Vocabulary”| Your word | Here | Notes |
|---|---|---|
| workflow | workflow | a script, in whichever language you like |
| activity | step | spool step name -- cmd, or step_fn() in the Python SDK |
| worker | agent | claims work matching its advertised capabilities; see agents |
| task queue | requires: |
a job goes only to agents carrying those capabilities |
| timer | spool sleep |
the run is asleep and holds nothing |
| signal | signal | spool wait signal <name>; the approval job kind uses one |
| child workflow | child run | spool call -w <name>, or the workflow: job kind |
| event history | run history | what the run page renders |
| retry policy | retry:, spool activity run --attempts |
per job or per activity-backed node; see retry a flaky call |
| workflow id | run id | <org>:<project>:run-<random> |
| search attributes | tags | spool submit --tag, filterable in the run list |
| schedule | schedule | spool schedule; each tick is a fresh run |
| cancellation | spool run cancel, cancel_on: |
cancel_on: cancels a run when a matching event arrives |
| side effect API | a step | the recorded result is the same on every pass |
| patching / versioning | nothing to do | a run is pinned to its definition |
| continue-as-new | a schedule, or a graph that calls itself | each pass is a fresh run with no accumulated log |
| query | state cell | spool state get, entity-scoped for cross-run reads |
| workflow id reuse / idempotency key | spool submit --idempotency-key, spool state add |
the first dedups a submit; the second claims a key inside a run |
| saga | a script that records undo steps | see the saga recipe |
Durable patterns has recipes for sagas, timers that an event cuts short, approvals with escalation, fan-out and retries.
What the replay check doesn’t catch
Section titled “What the replay check doesn’t catch”The engine can’t catch a rule violation for you. Code between steps that
misbehaves (an unwrapped random() steering a branch, a side effect
outside a step) produces a run that is plausible and wrong rather than a
loud error. The divergence check fires when the sequence of durable calls
changes, and that is the only guard rail.
Two more things to size up front:
- Step results live in the run’s history. Record a path, an id or a
summary, and put a build artifact in
spool artifact put. - Pick the loop shape deliberately. A
whileloop aroundspool sleepre-executes every unwrapped command above it on each pass. Cron-shaped work is its own primitive where each tick is a fresh run, and a graph that has to iterate until a condition calls itself. See Repeating work.
What we don’t have yet
Section titled “What we don’t have yet”- Queries and updates as a protocol. There is no request-response handler you register on the workflow, so you can’t ask a run a question it didn’t prepare an answer for. Reading what it did record works: a run’s durable state cells are readable from outside it, live or long finished (see reading a cell from outside the run). You decide in advance what to publish; the run writes the cell and the reader fetches it.
- Compensation as a first-class construct. Sagas are yours to write; the saga recipe shows the shape.
- A replay test harness.