Skip to content

State and memory

A step’s output belongs to one run. State is what persists across runs: a watermark, the last heartbeat, the id of the issue already filed. It lives in internal/engine/state.go and has four scopes, from narrowest to widest:

Scope Read in a template as Shared by
step {{ .Step.x }} this step of this workflow, across runs
workflow {{ .Workflow.x }} this workflow, across runs
project {{ .Project.x }} every workflow in this repository
global {{ .Global.x }} everything on this platform

These are four namespaces, not a cascade. .Step.last_id never falls back to .Workflow.last_id. A silent fallback would turn “nobody has written this yet” into “here is somebody else’s value”, which is the worst answer for a workflow resuming from a watermark it never set.

A step’s state: block maps a scope to keys, and each value is a template rendered against the step’s own accepted output. This is from templates/prod-watch:

state:
project:
prod-watch.heartbeat: "{{ .Output.stdout }}"

A step never names which workflow or project it writes to. The run already knows.

Terminal window
wfx state list --workflow <w> # what this workflow remembers between runs
wfx state set --workflow <w> k v # also --step, --project, --global
wfx state get --step k # one value; empty when never written

Longer-lived agent memory, meaning conversation memory and a persona or “agent home”, is pending. It is listed in docs/ROADMAP.md and has not shipped. What ships today is the state described above.