--- id: INT-6 family: RT-INT level: extended profiles: [runtime] posture: normative-target added: "1.0" changed: "1.0" source: https://flowdrop.io/spec/rules/rt-int/int-6 specification: FlowDrop Workflow Specification 1.0-draft licence: CC BY 4.0 --- # INT-6 — What a run snapshot contains, and that it never fails the run *RT-INT (Part II) · level: extended · profiles: runtime · added in 1.0* A snapshot is a best-effort record of progress. It is precise about what it carries, and it is never allowed to be the reason a run breaks. ## The rule > **Normative.** This is the rule. > > 1. A snapshot of a run carries the workflow identifier (the literal `unknown`, with an empty workflow version, when the run has no workflow), the structural workflow version (INT-7), the execution identifier, the caller-supplied status, the run's initial input, metadata naming the engine and the run, and one node snapshot per job keyed by node identifier. > > 2. Per node, job status maps one-to-one onto node status except that both `skipped` and `cancelled` become `skipped`, and any unrecognised status becomes `idle`; a node's output is carried only for a completed job and its error only for a failed one, alongside whether the node was injected, its execution order, and its job identifier. > > 3. Snapshot generation must never fail the run: where no state is available, or generation raises an error, the result is no snapshot, recorded in the log. ## What it means A snapshot's per-node status is not a direct copy of the job status behind it — two collapses are deliberate, and both go against the more intuitive guess. A cancelled job is recorded as skipped, not as failed and not with its own separate status: a cancelled node did not run, and a resume must not treat "did not run because cancelled" any differently from "did not run because the workflow finished first." A status the reader does not recognise at all becomes idle, on the same reasoning: unrecognised means "not done yet," the only assumption that cannot make a resume worse. Output and error are gated the other way round from what a merge might suggest: a job's output is carried only when that job actually completed, and its error only when it actually failed — a failed job never carries stale output forward, and a completed one never carries a phantom error. None of this may cost the run anything. Where there is no state to read, or producing the snapshot itself raises an error, the result is simply no snapshot — recorded in the log, not thrown. ## Example Four jobs mid-pause, and what the snapshot records for each. ```json title="Four jobs' own statuses" verdict="mid-run" { "n_done": "completed", "n_failed": "failed", "n_cancelled": "cancelled", "n_bogus": "not_a_status" } ``` ```json title="What the snapshot records for each" verdict="recorded" { "n_done": { "status": "completed", "output": { "result": "ok" } }, "n_failed": { "status": "failed", "error": "it blew up" }, "n_cancelled": { "status": "skipped" }, "n_bogus": { "status": "idle" } } ``` A run with no workflow behind it still gets a snapshot, with a placeholder identity rather than a missing one: ```json title="A run with no workflow to snapshot" verdict="unknown" { "workflowId": "unknown", "workflowVersion": "" } ``` ## Related rules - Names: INT-7, INT-9, INT-13 - Referenced by: INT-7, INT-9, INT-10, INT-13 --- Rule identifiers are permanent and are never renumbered. This specification carries no implementation status: each implementation publishes its own standing against these rules. Licensed CC BY 4.0.