awf v0.3.0
A workflow-format release. It renames several author-facing keys (migrate per
the BREAKING list below), adds container-less run: steps and native stall
detection with retry-as-continue, and tightens validation. The workflow-format
version (version: 1) is unchanged, but existing workflows must apply the key
migrations to keep validating.
BREAKING (v0.3.0 format)
reduce's quorumover:is renamed tofield:. The per-branch boolean
field aquorumcounts is now declared asreduce: { quorum: <k>, field: <field> }.
Migration: renameover:tofield:underreduce.quorumonly — amap's
ownover:(the fan-out worklist) is unrelated and unchanged.- The top-level workflow
input:schema is renamed toinput_schema:.
Migration: rename the workflow's top-levelinput:key toinput_schema:—
{{ input.<field> }}template references and acallstep's owninput:
(the instance binding passed to a subworkflow) are unrelated and unchanged. prune'skeep: top(<k>)is nowkeep: <k>. The function-call-shaped
wrapper is removed since top-k was always the only mode. Migration: replace
keep: top(<k>)with the plain integerkeep: <k>.- A signal
where:clause now requires the{{ }}envelope, and correlates
against the payload via asignal.<field>root. The old bare-identifier
form (an implicit substitute-into-string-then-parse against the payload,
with a string-quoting hazard) is removed. Migration: wrap the clause in
{{ }}and prefix payload fields withsignal.— e.g.
where: 'candidate_id == "{{ hyp.id }}"'becomes
where: "{{ signal.candidate_id == hyp.id }}". Every other root (input.*,
step.*,run.*, anas:binding, …) resolves against the surrounding
engine scope exactly as inif/loop/gateconditions, and the value is
compared as typed data — no more quoting workaround for strings.
Added
output_artifactis no longer containerless-only. Any agent step that
declaresoutput_schema— container-backed or containerless — may now also
declareoutput_artifact: <name>to publish its typed output as a
content-addressed artifact. The requires-output_schemaand
mutually-exclusive-with-output_filesrules (AWF3014) are unchanged.map'sover:accepts a literal YAML sequence. In addition to a
{{ }}expression evaluated at runtime,over:may now be an author-fixed
literal sequence (e.g.over: [a, b, c]) — a static, digest-pinned
parameter sweep known before the run, as opposed to a runtime-sized
worklist.- Container-less
run:steps (bare shell). Arun:code step may now omit
container:; it executes host-side under the native sandbox (auto-selected
when the workflow declares no image), so a hello-world needs no image or
digest procurement. A barerun:is rejected under--backend docker
(AWF1065) — declare acontainer:or run native. map'sconcurrency:is optional (default 1, serial). Omitting it runs
the map serially; an explicit value<= 0is now rejected (AWF1012).- Native stall detection + retry-as-continue. An idle watchdog cancels an
agent step that goes silent longer thantimeout.idle(distinct from the
wall-clocktimeout), turning a wedged agent into a retryable failure. The
openai/codexlive adapter forwards codex's reasoning-summary heartbeat and
gets a generous default idle (~300s); every other adapter is opt-in, and
AWF3016warns whenidle:is set on an adapter that surfaces no liveness
signal. On a stall, a persistent-session step resumes the same conversation
thread —retry: { recovery: continue }, the default for session adapters —
instead of restarting from scratch;recovery: restartis the escape hatch
(AWF1064rejects any other value at validation time). awf validatestrictly rejects unknown workflow/step keys (AWF1062). A
stray or typo'd key anywhere in a workflow document — previously silently
tolerated — is now a hard validation error.awf validatestrictly rejects bare-integer durations (AWF1063). A
timeoutorretry.initial/retry.maxvalue must be a quoted duration
string (e.g."300s", not300) — a bare integer previously parsed as
nanoseconds with no error, causing the step to time out instantly.
Changed
- Breaking (native backend): run workdirs moved from
work/towork/<run-id>/
to isolate concurrent native runs. A native run started before this change
cannot be resumed after upgrading (its workdir path moved). Docker runs are
unaffected (already run-id namespaced). Start affected native runs fresh.