Skip to content

fix(imagine): canonicalize dry-run worktree path so per-file attribution works on macOS#99

Merged
CodeWithJuber merged 1 commit into
masterfrom
claude/parallel-solving-3dc0ji
Jul 19, 2026
Merged

fix(imagine): canonicalize dry-run worktree path so per-file attribution works on macOS#99
CodeWithJuber merged 1 commit into
masterfrom
claude/parallel-solving-3dc0ji

Conversation

@CodeWithJuber

Copy link
Copy Markdown
Owner

What & why

Fixes the one macOS-only test failure that the new CI matrix (#90) exposed — and it turned out to be a real production bug in forge imagine --dry-run, not just a test artifact.

Root cause. dryRun() (src/imagine.js) attributes each not ok block to a test file by string-comparing node --test's location: diagnostic against join(wt, testfile), where wt is a mkdtempSync(join(tmpdir(), …)) worktree. node --test reports location: as a canonical realpath. On macOS, tmpdir() lives under a symlink (/var/private/var, /tmp/private/tmp), so wt (e.g. /var/folders/…/wt/test/fail.test.js) never matched node's realpath'd /private/var/folders/…/wt/test/fail.test.js. Attribution failed, attributable flipped false, and perFile was omitted entirely — so forge imagine --dry-run's pass/fail-per-file breakdown was silently broken for all macOS users. On Linux /tmp isn't a symlink, so it happened to match — which is why it was Linux-green, macOS-red, and invisible until the suite ran on macOS.

Fix (code, not the assertion). Canonicalize the worktree root once with realpathSync(wt) right after it's created, and compare against join(wtReal, …). No-op on Linux; correct on macOS. The strict deepEqual assertion in the test is unchanged.

How it was found. Reproduced deterministically on Linux by pointing TMPDIR at a symlink (mimicking macOS's /var/private/var) — no macOS box or CI round-trip needed — which failed test/imagine.test.js:176 exactly as CI did.

Re-enables macOS coverage. .github/workflows/smoke.yml now runs the full unit suite on macos-latest again (the ubuntu-only stopgap gate + its TODO are removed).

Verification

  • npm test (normal TMPDIR): 1057 tests, 0 fail.
  • TMPDIR=<symlink> npm test (macOS repro): the previously-failing imagine test now passes (11/11 in that file; full suite 0 fail).
  • Biome + typecheck + forge docs check green.

Checklist

  • npm test passes (both normal and symlinked-tmpdir)
  • npm run check / npm run typecheck / docs check pass
  • Conventional commit; CHANGELOG.md [Unreleased] updated
  • No new runtime dependency

Risk & rollback

  • Risk: low — one added realpathSync call, no-op where tmpdir() isn't a symlink; re-enables a CI step.
  • Rollback: revert the single commit.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KfCEnSJzSG914yyVesjjD6


Generated by Claude Code

…n macOS (symlinked tmpdir)

dryRun() attributes test failures to files by string-matching node --test's
`location:` diagnostic (a canonical realpath) against join(wt, testfile). On
macOS the sandbox worktree lives under tmpdir(), which is itself a symlink
(/var → /private/var, /tmp → /private/tmp), so the raw wt path never matched
the realpath'd locations: attribution failed, perFile was omitted, and
test/imagine.test.js "dryRun executes the suite in an ephemeral worktree and
removes it (pass + fail)" saw perFile === undefined instead of the expected
{ "test/pass.test.js": "pass", "test/fail.test.js": "fail" }.

Fix the code (not the test): realpathSync(wt) once after the worktree is
created and compare against that canonical root. On Linux (no symlinked tmpdir)
this is a no-op. Re-enable the full unit suite on macos-latest in smoke.yml.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KfCEnSJzSG914yyVesjjD6
@CodeWithJuber
CodeWithJuber marked this pull request as ready for review July 19, 2026 20:03
@CodeWithJuber
CodeWithJuber merged commit 7cac532 into master Jul 19, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants