A tour of Hyperspool
Thirty minutes with a local instance, one shape at a time: durable steps, timers, human gates, signals, events, and child workflows. Along the way you kill the engine mid-run and watch the runs carry on.
You need the spool binary
(curl -fsSL https://hyperspool.com/install.sh | sh) and nothing
else. Everything below runs locally.
0. A local instance
Section titled “0. A local instance”spool devOne process: server, an embedded agent, and the console at
http://localhost:18753. Leave it running in this terminal; the rest of
the tour happens in a second one. If the CLI points at another server,
local and the server shows how to switch.
1. A shell job is one checkpoint
Section titled “1. A shell job is one checkpoint”Save and submit this:
jobs: work: steps: - name: fetch run: echo "fetched 1200 records" - name: transform run: | echo "transforming..." sleep 5 echo "done"spool submit steps.yaml --followWhile transform’s five seconds tick, stop spool dev (Ctrl-C, or
kill -9 for a real crash), then start it again. The server resumes the
in-flight run (resumed 1 in-flight run(s) in the startup log) and the
work job runs again from its first step: a shell job is one
checkpoint, so its steps run again together. Per-step checkpoints that
survive a restart come from spool step, and section 5 shows the same
kill leaving completed steps untouched.
2. A timer puts the run to sleep
Section titled “2. A timer puts the run to sleep”jobs: deploy: steps: - name: ship run: echo "deployed v2" soak: depends_on: [deploy] wait: duration: 2m verify: depends_on: [soak] steps: - name: check run: echo "metrics clean, keeping v2"Submit it and open the run in the console: for the two minutes, the run
is asleep. No agent is held and no process waits on your behalf; the
run is rows in a database until the timer fires. Change 2m to
30d and the mechanics stay the same; restart spool dev during the
wait and the wake-up still happens on time.
3. Approvals: a human in the DAG
Section titled “3. Approvals: a human in the DAG”jobs: build: steps: - name: build run: echo "built v2" review: depends_on: [build] outputs: env: env approval: prompt: "Ship v2 where?" fields: - { name: env, type: select, options: [staging, production] } ship: depends_on: [review] steps: - name: ship run: echo "shipping to ${{ steps.review.output.env }}"The run waits for approval at review. Like the timer, it can wait a
week without holding an agent. Open the run in the console
and the approval form is inline; submit it and ship templates against
what the person chose. Reject instead and the run is cancelled, with the
reject and its reason recorded on the approval.
From a terminal it’s the same form, asked one field at a time:
spool approve <run>The job name is optional because the run is waiting for only one
thing. spool run get <run> shows what that is, waiting for approval `review`, and spool run get <run> -o json has it as an awaiting_signal
substate named after the approval job.
spool run follow <run> gets you the same form without leaving the
terminal you’re watching the run in: select the waiting step, press a.
4. Signals and events: the outside world
Section titled “4. Signals and events: the outside world”A wait: {signal: ...} job waits until something delivers a named
signal:
spool run signal <run> go --payload '{"reason":"maintenance window open"}'Signals target one run by id. Events are the broadcast version - any
run (or spool event emit from your terminal) can publish on the project
bus, and runs subscribe by type:
cancel_on: events: [pr.closed]jobs: deploy: steps: - name: up run: echo "preview environment up" watch: depends_on: [deploy] wait_for_event: event: deploy.approved promote: depends_on: [watch] steps: - name: promote run: echo "promoted"Submit it, then from your terminal:
spool event emit deploy.approved# or end the run early instead:spool event emit pr.closedwait_for_event resumes the run when the event lands; cancel_on
tears the whole run down. Both are registered at submit, so an event
arriving before the waiting job starts still counts.
A run started for something, such as a pull request, only reacts to that thing’s events; subjects explains how.
5. Polyglot: the same shapes in a program
Section titled “5. Polyglot: the same shapes in a program”Everything above was YAML. The same primitives are available inside a normal program, where checkpoints can be as fine as you like:
before="$(spool step baseline -- date +%s)"spool sleep 30safter="$(spool step recheck -- date +%s)"echo "healthy at $before, still healthy $(( after - before ))s later"(In a real canary those are curl -fsS calls against your service
returning a metric; a non-zero exit fails the step, and the run.)
spool submit canary.sh --followKill spool dev during the spool sleep. On restart the script runs
from the top again and baseline returns its recorded timestamp, so the
subtraction measures how long the canary soaked. That’s
replay at work.
The re-run-from-the-top model has rules. Read the authoring model before you write something real, and see workflows in your language for the same thing with the Python SDK.
6. Workflows spawning workflows
Section titled “6. Workflows spawning workflows”A pipeline can embed a polyglot child as a job, and the child gets its own run, its own checkpoint timeline, and its own page in the console, while its structured output flows back into the parent’s DAG:
jobs: canary: outputs: verdict: verdict workflow: type: hyperspool/script/v1 script: | spool step probe -- echo "canary healthy" spool sleep 10s spool output set verdict=ok ship: depends_on: [canary] steps: - name: ship run: echo "canary said ${{ steps.canary.output.verdict }} - shipping"The other direction works too: a script starts another run with
spool spawn, or with spool call to wait for it. See
composition for where each is the right tool.
Where to go next
Section titled “Where to go next”- Concepts - the vocabulary.
- YAML reference - every field of every job
kind, plus
concurrency:andcancel_on:. - CI from a repo - webhook in, statuses back: the CI layer on the same engine.
- Agents - where steps run on hyperspool.dev, and running your own agent.