Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesstack72

Relationships

#2601 Run-scoped data get/list return the latest version, not the version the run produced

Opened by stack72 · 9/28/2026· Shipped 9/28/2026

Problem

Resolving data through a workflow run returns the latest version of each data item, not the version that run produced. When the recorded artifact no longer matches by id, it can return a same-named item from a different model.

Affected:

  • swamp data get <name> --workflow <wf> --run <runId> (and data.get over swamp serve with workflowName + runId)
  • swamp data list --workflow <wf> --run <runId>

Cause

WorkflowDataService.findAllForWorkflowRun (src/domain/data/workflow_data_service.ts:62):

  1. Loads dataRepo.findAllGlobal(), which holds only the latest version of every data name.
  2. Matches each run artifact ref ({dataId, name, version, tags}) by dataId. The comment there notes data ids change per version, so refs from older runs miss.
  3. Falls back to the first global item with the same name across any model (lines 131-140).
  4. findByNameInWorkflowRun compares an explicit version against that latest item, so asking for the run's actual (older) version returns not_found even when it is still on disk.

The version the run recorded (DataArtifactRef.version) is never used for resolution.

Reproduction

  1. A workflow whose step writes data result (or a workflow-scope report report-<name>).
  2. Run it twice: run A writes v1, run B writes v2.
  3. swamp data get result --workflow <wf> --run <runA> returns v2 (run B's content).
  4. swamp data get result --workflow <wf> --run <runA> --version 1 returns not_found.

Expected

Run-scoped resolution returns the exact version recorded on the run:

  • Workflow-scope artifacts (run.workflowDataArtifacts) resolve under model type workflow / the run's workflow id at the recorded version.
  • Step artifacts use the existing lookup only to identify the owning model, then read that owner's data at the recorded version.
  • An explicit version selects that version of the run's artifact.
  • A recorded version that has been garbage-collected yields not_found. It must never fall back to newer data or to another model's data.

Impact

Silent wrong content: a link or script that targets a specific run sees a later run's output. This blocks swamp-club#2595 (dashboard deep links to a run's reports), which relies on run-scoped data.get being pinned.

Only two callers are affected: src/libswamp/data/get.ts:244 and src/libswamp/data/list.ts:228. Output without --run (latest run) is normally unchanged.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 6 MOREREVIEW+ 9 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/28/2026, 7:09:04 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack729/28/2026, 6:12:22 PM
Editable. Press Enter to edit.

stack72 commented 9/28/2026, 5:42:37 PM

Findings from the adversarial review of swamp-club#2595 that belong to this fix:

  1. Owner identification. Do not rely on the latest-id index or the first-name-match-in-any-model fallback and then re-read at the pinned version: a wrongly guessed owner at the pinned version looks authoritative. Resolve the owner from run-record hints first (step output resources/files carry modelType + modelId; report artifact tags carry modelName). Accept a candidate only when the pinned version's metadata id equals the artifact's dataId (each write mints a new id). Renamed data should still resolve at the pinned old-name version.
  2. Repeated writes in one run. When a run writes the same (owner, name) more than once (step A v3, then step B v4), return the highest recorded version, not the first in job/step order.
  3. Cost. Resolution walks findAllGlobal and calls findById against every workflow per request (workflow_data_service.ts:67, get.ts:236-251). Pass the already-loaded run to the service; keep the global walk as a fallback only.
  4. Docs. .claude/skills/swamp/references/data/reference.md:80-88 documents data list/get with --workflow --run and --version; update it (deno fmt and skill review) along with data/guide.md:49 and workflow/guide.md:58 if they imply latest.

Tests to include: a same-name item in a second model, rename after the run, a GC'd recorded version (not_found, never newer data), and a repeated (owner, name) within one run.

Sign in to post a ripple.