Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
Assigneesstack72

#331 Expose workflow run ID in CEL so resource keys can be run-scoped

Opened by bixu · 5/12/2026· Shipped 5/12/2026

Problem

swamp workflows that write intermediate resources have no way to namespace those resources by the current run. The CEL surface exposes inputs.X, self.X, and data.latest(...) — but no equivalent of "the ID of this workflow execution." That means two concurrent runs of the same workflow against the same model instance silently overwrite each other's per-VM (or per-anything-else) state, and downstream data.latest(...) reads can see the wrong run's data.

This bit me on PR #237 in hivemq/hivemq-terraform-harvester, an emergency kernel-update fleet workflow. The workflow chain writes a filtered-vms resource, then per-VM current-kernel-<vm> and boot-id-<vm> resources, then reads them back to gate the reboot chain. The keys are deterministic per VM, so two simultaneous operator runs would step on each other — the reboot-gate's data.latest could read the OTHER run's current-kernel-<vm>, decide "no reboot needed" against the wrong baseline, and skip the verification path.

The pi-judge bot flagged this as HIGH severity. I mitigated it by documenting the limitation prominently on all three workflow files (the parent dev/prod workflows and the child set-harvester-vm-kernel) — "do not run two instances against the same fleet in parallel." That works for emergency kernel updates which are operator-serialized in practice, but it's a band-aid: any workflow that ever needs concurrent execution against shared state has no clean answer today.

swamp clearly knows the run ID internally — every .swamp/workflow-runs/<workflow-id>/workflow-run-<uuid>.yaml file starts with id: <UUID>. The missing piece is the CEL binding.

Proposed Solution

Add a run (or workflowRun / context.run — name TBD) variable to the CEL evaluation context for workflow templating. Initial shape:

  • run.id — the UUID from the workflow-run record (e.g. 790f565a-c2e4-476f-88d9-39090bca11c5)
  • Optional follow-ups: run.startedAt, run.workflowName, run.workflowId — but id is the load-bearing one

With this, the kernel workflow's intermediate keys become:

  • filtered-vms-${{ run.id }}
  • current-kernel-${{ inputs.vmName }}-${{ run.id }}
  • boot-id-${{ inputs.vmName }}-${{ run.id }}

…and data.latest reads in dependent steps switch to the same suffixed names. Concurrent runs each write under their own UUID; no collision, no documentation footnote required.

Affected Components

  • CEL evaluator / templating layer — extend the variable resolution to include the run binding
  • Workflow runner — pass the run record (or just the ID) into the evaluator's context when expanding inputs
  • Documentation — add run.id to whatever lists inputs.* / self.* / data.latest today

High-Level Approach

The run UUID is already generated at run-start and persisted to the run record before any step executes. Plumbing it into the CEL context should be a small extension at whatever layer currently injects inputs and self. No new state, no new schema — just exposing existing state.

Why It Matters

Without this, every workflow that writes intermediate state has to choose between:

  1. Documenting "don't run me concurrently" (band-aid; works for operator-driven incident response, breaks for scheduled/automated rollouts).
  2. Threading a synthetic run-id through every method as an input parameter (boilerplate; every helper method needs an extra arg, every step has to pass it, and operators have to remember to supply one).
  3. Per-VM (or other natural-key) serialization at the swamp level via locks (heavy; doesn't compose).

Exposing the UUID swamp already has costs nearly nothing on the engine side and unblocks the natural pattern (keys suffixed with run ID). Without it, every fleet/orchestration workflow in the registry is silently exposed to the same race.

  • Companion feature request from the same review cycle: swamp doctor workflows for preflight YAML validation (Lab #330).
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 4 MOREREVIEW+ 3 MOREPR_MERGEDSHIPPED

Shipped

5/12/2026, 4:29:42 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack725/12/2026, 3:09:58 PM

Sign in to post a ripple.