Expand description
keel exec --flow <name> -- <program> [args…] — wrap one external command
as a journaled Tier-2 durable flow (CCR-4).
What keel exec gives an arbitrary process that has no Keel front end (a
shell script, a uvx server, a cron job): at-most-once dispatch per
identity and crash-safe retry gating. It is emphatically NOT
exactly-once execution inside the child — Keel cannot un-send whatever
side effects a subprocess performs. The honest guarantee is at the
dispatch boundary: a completed flow never re-runs its command (pure
replay), a live holder is fenced or waited on per policy, and a re-dispatch
after a failure is gated by a declared-side-effect snapshot compare
(KEEL-E033) and the flow-level attempt cap (KEEL-E032).
§Identity and fencing
Flow identity is (cmd:<name>, args_hash, --flow-id?); args_hash = sha256(argv.join("\0"))[..16] makes two different command lines distinct
flows, and code_hash = sha256(resolved_program_path + "\0" + argv.join("\0"))[..16] fences replay when the program itself changes on
disk. The lease holder is recorded as "{hostname}:{pid}:{started_ms}";
started_ms is kept for PID-reuse forensics but is not consulted in v1 —
the lease TTL remains the reuse backstop.
§Why the single step is driven through the Journal directly, not
A terminal error record would be replay-SUBSTITUTED on the next entry
(rule 3 of the tier-2 semantics), so a failed command could never be re-run
— but re-running after a failure IS keel exec’s retry model (gated by the
snapshot compare + attempt cap). The single step is therefore driven
through the Journal directly; FlowManager still owns
identity/lease/attempts/completion, and a crashed run (a running step)
resumes exactly like any tier-2 crash. Substitution is honored where it is
correct: the completed-flow pure-replay path. This is the one-step v1 shape;
multi-step cmd flows would revisit it.
Structs§
- Exec
Options keel execoptions (the parsed clap surface).
Functions§
- request_
force_ override - Arm the one-shot KEEL-E033 force override for
flow_id(CCR-6): the next [side_effect_gate] check that would otherwise refuse this flow’s re-dispatch (its declared side-effect files changed) proceeds instead, and clears the flag as it does so. - run
keel execforproject: wrap one command as acmd:durable flow.