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

Relationships

#3536 extension quality and push report source paths through the repo's resolved symlink: quality's entry.file climbs to / and back

Opened by skunk-ape · 10/16/2026· Shipped 10/16/2026

What happens

When the repository is reached through a symlink, swamp extension quality and swamp extension push report extension source files under the symlink's resolved path instead of the path the user gave. quality then builds a "relative" path from two spellings of the same directory, and that path climbs to the filesystem root and back down.

macOS hits this for every repo in $TMPDIR, because /var is a symlink to /private/var. A repo under a symlinked home or projects directory hits it on any OS.

Seen in swamp-uat's nightly macOS UAT on stable (every run since it started, 2026-10-13), for example https://github.com/swamp-club/swamp-uat/actions/runs/38680054810. The repo is /var/folders/36/…/T/swamp-uat-…:

swamp extension quality <manifest> --json, declaredAcceptances.accepted[0].file:

expected  extensions/models/ext/fields.ts
actual    ../../../../../../private/var/folders/36/…/T/swamp-uat-…/extensions/models/ext/fields.ts

swamp extension push <manifest> --dry-run --json --yes, resolved.models[0].fileName:

expected  /var/folders/36/…/T/swamp-uat-…/extensions/models/ext/global-args.ts
actual    /private/var/folders/36/…/T/swamp-uat-…/extensions/models/ext/global-args.ts

push ../other/manifest.yaml --repo-dir ../other --dry-run --json --yes shows the same.

Linux reproduction (stable 20261015.132835.0-sha.884bfffd)

Not macOS-specific. With TMPDIR set to a symlink linktmp -> realtmp, so each test repo is linktmp/swamp-uat-…, the same three swamp-uat tests fail:

quality   expected extensions/models/ext/fields.ts
          actual   ../../realtmp/swamp-uat-…/extensions/models/ext/fields.ts
push      expected …/linktmp/swamp-uat-…/extensions/models/ext/global-args.ts
          actual   …/realtmp/swamp-uat-…/extensions/models/ext/global-args.ts

The standalone repro: mkdir real && ln -s real link && cd link, swamp init, add an extension with a declared inline acceptance, then run swamp extension quality <manifest> --json and swamp extension push <manifest> --dry-run --json --yes.

Why it's wrong

  • entry.file should be the file's path relative to the manifest. The path swamp prints leaks the machine's absolute layout, is unreadable, and breaks when the repo moves.
  • Paths swamp prints should use the spelling the user gave (cwd or --repo-dir). Otherwise the same repo prints different paths depending on how the machine's directories are linked.

Cause (from reading the code)

The extension loader resolves source files with Deno.realPathSync (src/domain/extensions/extension_loader.ts), but the manifest/repo directory keeps the user's spelling. fileRelativeToManifest (src/domain/extensions/extension_acceptances.ts) calls relative(manifestDir, file), which only compares strings, on the two different spellings.

Expected

swamp may resolve symlinks internally (for loading and de-duplication), but every path it reports for a file under the repo uses the repo directory as the user gave it:

  • quality: entry.file = extensions/models/ext/fields.ts
  • push: fileName = <repo dir as given>/extensions/models/ext/global-args.ts

Acceptance

swamp-uat's tests/cli/extension/quality_test.ts ("declared acceptance") and tests/cli/extension/push_output_test.ts (--dry-run --json --yes, and --repo-dir ../other) pass unchanged on macOS. They also pass on Linux with TMPDIR pointing through a symlink.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 11 MOREREVIEW+ 10 MOREPR_LINKED+ 1 MORESESSION_SUMMARIZED

Shipped

10/16/2026, 8:38:00 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
zack assigned zack10/16/2026, 5:05:58 PM

Sign in to post a ripple.