decree 0.5 composes existing, well-specified ideas. Nothing here is new; the value is in the narrow combination. Where decree deviates, the reason is to keep machines small enough to validate statically and to run from bash.
| decree concept | Drawn from | What decree takes | Deliberate deviation |
|---|---|---|---|
Machine semantics: states, compound states, finals, initial, onentry/onexit, invoke |
W3C SCXML 1.0 (Recommendation, 2015), itself built on Harel statecharts (1987) | The normative standard. Terms, key names and behaviour are SCXML's; SCXML subset lists the exact subset. | YAML instead of XML. No <parallel>, <history>, cond on transitions, <send>, <raise>, <assign> or other executable content; one transition per event. Extensions and differences are listed in SCXML subset: root onentry/onexit, unhandled events becoming error, a failing onentry on a root final, and child results named after the final state. |
| Exit and entry order, transition domain | SCXML Appendix D (Algorithm for SCXML Interpretation) | Exit innermost-first below the transition domain; enter outermost-first; entering a compound state enters its initial. Transitions are external unless declared type: internal, as in SCXML. |
One transition per event, so SCXML's conflict resolution never arises. |
person and replies |
SCXML external events; AWS Step Functions callback pattern (.waitForTaskToken, SendTaskSuccess) |
A run pauses until an external event arrives; a token (the wait id) correlates the reply. | Replies are files in the inbox, so any tool can answer; decree does not know who is asked. |
invoke → done / error |
SCXML <invoke> (done.invoke.<id>, error.execution) |
Long-running work belongs to a state; its completion is an event. | The invoked thing is a script, not an SCXML session; done and error are the prefix descriptors SCXML would match. onentry and onexit scripts are processes that block, where SCXML executable content is instantaneous. |
Decision invokes (check, model, person) |
SCXML <invoke type> (types are platform-defined); AWS Step Functions Choice and Task states |
A state's work is one invoked function; its result is an event. | decree's own invoke types, each named by the one key under invoke. Conditions are typed YAML objects with one subject (output, data, visits or confidence) and one operator, evaluated by a check state rather than on transitions. |
| One key names the invoke's kind | serde's externally tagged enums ({ variant: value }); GitHub Actions steps (uses: or run:) |
The key that is present says what the value is, so a reader never infers the kind from other fields. | invoke: <name> is short for invoke: { script: <name> }. |
attempts, timeout, unhandled error → failed |
AWS Step Functions (Amazon States Language: Retry.MaxAttempts, TimeoutSeconds, Catch on States.ALL, Fail state); Temporal RetryPolicy; model gateway fallback lists (LiteLLM fallbacks, OpenRouter models) |
Per-task retry and timeout, and a catch-all failure path. Both sit inside the script invoke, as Step Functions puts them on the Task. attempts: <n> caps attempts as MaxAttempts does; a list tries each entry in order until one succeeds, as a fallback list does, and hands the script the entry as DECREE_ATTEMPT_VALUE. |
No backoff or jitter; attempts are immediate. |
model and router machines |
LangGraph conditional edges; typed-choice decision models (TypeSafe Jev); SCXML child sessions | A model picks the next edge from a fixed set, with a recorded confidence; the asking is itself a replaceable machine. | Options and descriptions come from the calling machine, the reply is validated by decree, and below min_confidence the state produces unsure. The model never names a state or a machine. |
events.jsonl as the record; decree process --retry |
Event sourcing; JSON Lines (jsonlines.org); durable-execution engines (Temporal) | An append-only log is the source of truth; current state is derived from it; work is at-least-once, so steps must be idempotent. | No automatic recovery: an interrupted run waits for decree process --retry, because a kill may be deliberate. Continuing re-runs root and ancestor onentry scripts, so setup such as a git baseline is re-established. |
| Telemetry | Grafana Loki and Alloy label practice; OpenTelemetry span model | Structured JSON Lines with a small label set; start time plus duration per unit of work. | No exporter or metrics endpoint; a log shipper tails the files. |
| Messages | Markdown with YAML frontmatter (the Jekyll convention, also used by Hugo and agent skill files); YAML 1.2 via serde_norway |
Structured header, free-text body. | None. |
| Inbox claim and writes | Maildir delivery (write to a temp name, rename into place); POSIX rename(2) atomicity |
Readers never see partial files; a rename is the claim. | One directory with dot-prefixed temp names instead of tmp//new//cur/. |
| Run lock | pidfile with O_EXCL and kill(pid, 0) liveness check |
Single owner per run; a stale lock marks a crash. | A stale lock never causes a takeover: the run becomes interrupted. |
| Long-running services | systemd user units (Conflicts=, Restart=, journald); llama-swap |
Supervision, mutual exclusion on a shared GPU, logs. | Outside decree: onentry scripts start what a state needs (Long-running services). |
Durations (timeout, prune --older-than, daemon --interval) |
Kubernetes and Go durations (time.ParseDuration: 90s, 10m, 12h) |
A number followed by its unit, readable where seconds are not (7d, not 604800). |
One whole-number term only: no fractions, no combinations such as 1h30m, and no sub-second units; d is added, as Kubernetes and Go have none. |
| Process control | POSIX process groups; SIGTERM, grace period, SIGKILL (as docker stop, 10 s default) |
Kill the whole script tree on stop or timeout. | None. |
| Exit codes and events | POSIX exit status; 128 + n for signals (130 = SIGINT); GitHub Actions' $GITHUB_OUTPUT file |
0 is done, non-zero is error. A script writes outputs to a file whose path is in an environment variable, never to stdout. |
One output only: the event name, written to $DECREE_EVENT_FILE. |
| Ids and timestamps | ISO 8601 basic format; RFC 3339 | Sortable, human-readable ids and log times. | 6 hex chars instead of a UUIDv7 (RFC 9562) suffix, to stay readable without a dependency. |
| Cron files | Standard cron expressions (the cron crate) |
Unchanged. | None. |
| Graph | Mermaid stateDiagram-v2 in Markdown (renders in VS Code 1.121+, GitHub, GitLab, Obsidian, mermaid.live; MIT, self-hostable) |
Generated from the arena the interpreter runs; viewing is left to existing tools. | No built-in image rendering, to keep decree small. SCXML (XML), DOT and other exports are out of scope. |