Skip to main content
← Back to list
01Issue
BugShippedExtensions
Assigneesstack72

Relationships

#1626 migrate-index still drops shallow-path entries on 2026.08.07.1 — #1584 marked shipped but no released version carries the fix (self-contained repro)

Opened by sntxrr · 8/12/2026· Shipped 8/12/2026

Re-filing swamp-club#1584, which is marked shipped but is still present in the only published artifact. Not a complaint about the triage — I suspect the fix is merged and the label ran ahead of the release. This issue exists to carry a self-contained repro so the fix can be confirmed against a version number.

The status does not match any released artifact

evidence value
#1584 filed 2026-08-10
@swamp/s3-datastore latest in registry 2026.08.07.1
that version published 2026-08-07, three days before the report
re-pull of that version today byte-identical source
comments on #1584 none naming a fix version

For contrast, #1554 was closed with a comment naming @swamp/s3-datastore@2026.07.31.1, which I could pull and verify. There is no such version here.

Repro — no S3, no bucket, no credentials, no network

partitionKeyFromPath is a public static on the exported S3CacheSyncService, so the shipped code can be called directly. Save as repro.ts:

const modPath = Deno.args[0];
const { S3CacheSyncService } = await import(`file://${await Deno.realPath(modPath)}`);

const cases: Array<[string, string]> = [
  ["data/@vendor/model/<uuid>/<dataName>/<n>/raw",     "data/@v/m/1111/current/3/raw"],
  ["outputs/@vendor/model/<method>/<uuid>-<ts>.yaml",  "outputs/@v/m/sync/1111-2026-01-01T00-00-00-000Z.yaml"],
  ["definitions-evaluated/@vendor/model/<uuid>.yaml",  "definitions-evaluated/@v/m/1111.yaml"],
  ["auto-definitions/@vendor/model/<uuid>.yaml",       "auto-definitions/@v/m/1111.yaml"],
  ["workflow-runs/<workflowId>/<file>.yaml",           "workflow-runs/wf-1/workflow-run-abc.yaml"],
  ["workflows-evaluated/<file>.yaml",                  "workflows-evaluated/workflow-wf-1.yaml"],
  ["<root file>",                                      ".catalog-export.json"],
];

let failures = 0;
for (const [shape, sample] of cases) {
  const key = S3CacheSyncService.partitionKeyFromPath(sample);
  if (key === undefined) failures++;
  console.log(`  ${key === undefined ? "DROPPED" : "ok     "}  ${shape}  ->  ${key ?? "undefined"}`);
}
console.log(`${failures} of ${cases.length} shapes dropped`);
Deno.exit(failures === 0 ? 0 : 1);

Run it against the installed extension (the --allow-env is only because importing the module pulls in the AWS SDK):

deno run --allow-read --allow-env repro.ts \
  <repo>/.swamp/pulled-extensions/@swamp/s3-datastore/datastores/_lib/s3_cache_sync.ts

Actual output on @swamp/s3-datastore@2026.08.07.1

  ok       data/@vendor/model/<uuid>/<dataName>/<n>/raw          ->  data--@v--m--1111
  ok       outputs/@vendor/model/<method>/<uuid>-<ts>.yaml       ->  outputs--@v--m--sync
  ok       definitions-evaluated/@vendor/model/<uuid>.yaml       ->  definitions-evaluated--@v--m
  ok       auto-definitions/@vendor/model/<uuid>.yaml            ->  auto-definitions
  ok       workflow-runs/<workflowId>/<file>.yaml                ->  workflow-runs--wf-1
  DROPPED  workflows-evaluated/<file>.yaml                       ->  undefined
  DROPPED  <root file>                                           ->  undefined
2 of 7 shapes dropped

Exit code 1. Expected: 0 of 7 dropped.

Any entry returning undefined is discarded by groupEntriesByPartition via if (!key) continue; with no log line, so it never reaches a shard.

Root cause (unchanged from #1584)

static partitionKeyFromPath(rel: string): string | undefined {
  const segments = rel.split("/");
  if (segments.length < 2) return undefined;        // (a) drops every root-level file
  const subdir = segments[0];
  switch (subdir) {
    ...
    case "workflow-runs":
    case "workflows-evaluated": {
      if (segments.length < 3) return undefined;    // (b) wrong for workflows-evaluated
      return `${subdir}--${segments[1]}`;
    }

The two share a case arm but are different depths by design: workflow-runs/<workflowId>/<file> is 3 segments, workflows-evaluated/<file>.yaml is 2. Guard (b) therefore rejects 100% of workflows-evaluated entries. Guard (a) rejects any root-level file, of which .catalog-export.json is the notable one.

Production evidence that it has not self-healed

Same datastore as #1584, now 478 commits later (_meta.json v2, commitSeq=478, 104 partitions, 23,019 sharded entries):

STILL MISSING  .catalog-export.json                              (object present in S3, 8.6 MB)
STILL MISSING  workflows-evaluated/workflow-063a0cb1-....yaml
STILL MISSING  workflows-evaluated/workflow-e14e03ef-....yaml

Nothing re-derives them, so the gap is permanent until the partitioner changes.

Severity: low, fails safe

Repeating this so it is not over-prioritised: the S3 objects still exist — this is index-only. toDelete is computed from index entries, so a shorter index yields fewer deletion candidates, never more; this cannot cause data loss. Practical cost is that affected files look absent and get re-pushed.

The reason to fix it anyway is the silent continue. That is the same shape as the silently-skipped push in swamp-club#1557, which went unnoticed here for seven days. And .catalog-export.json is the catalog itself.

Suggested fix

  1. Split the case arm: workflows-evaluated requires >= 2 and partitions on segments[0]; workflow-runs keeps >= 3.
  2. Give root-level files a partition (e.g. _root) instead of dropping at (a).
  3. Replace the silent continue with a counter and a log line — or fail the migration outright. A migration that drops entries without saying so is worse than one that refuses to run.
  4. Assert sum(shard entries) === entryCount before writing _meta.json. That single check would have caught this at authoring time and would prevent the whole class.

Point 4 is the one I would most like to see, independent of the specific paths.

Ask

Which released version carries the #1584 fix? If it is merged but unreleased, this issue can just track the release. The repro above exits non-zero on the current artifact and will exit zero once it lands, so it doubles as the acceptance check.

Environment

  • CLI 20260809.004828.0-sha.b61c9de2
  • @swamp/s3-datastore@2026.08.07.1 (latest available)
  • Deno 2.8.3, darwin/aarch64

Upstream repository: https://github.com/systeminit/swamp-extensions

Environment

  • Extension: @swamp/s3-datastore@2026.08.07.1
  • swamp: 20260812.013400.0-sha.278864d1
  • OS: darwin (aarch64)
  • Deno: 2.8.3
  • Shell: /bin/zsh
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 2 MORECODE_CONFORMANCE_REVIEW+ 2 MORESESSION_SUMMARIZED

Shipped

8/12/2026, 6:40:53 PM

Click a lifecycle step above to view its details.

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

stack72 commented 8/12/2026, 6:44:06 PM

The fix from PR #187 is now published. Pull @swamp/s3-datastore@2026.08.12.1 and @swamp/gcs-datastore@2026.08.12.1 — the reporter's repro script should exit 0 against these versions.

Sign in to post a ripple.