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.tsswamp 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.tspush ../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.tsThe 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.fileshould 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.tspush: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.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.