make test silently runs the suite N+1 times when a git worktree exists inside the checkout
#25
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Found by accident while verifying
mainimmediately after merging #23.make checkreported:The real suite is 18 files and 210 tests. There were four git worktrees under
.claude/worktrees/, each a full checkout of this repo. 18 + (4 x 18) = 90, and210 + (4 x 210) = 1050. vitest had globbed every worktree and run the entire suite five times.
vitest's default
excludecoversnode_modules,dist,.idea,.gitand.cache. It doesnot cover
.claude/, and there is novitest.config.tsin this repo to add it..gitignorelists
.claude/, but vitest does not consult.gitignorefor test discovery, so ignoring it forversion control does nothing here.
Why this matters more than it looks
script/testhard-caps at 30 seconds and the README claims a 20-second budget. A polluted run inflates the
number by a factor of N+1, so a suite that is actually fine can blow the cap and fail the
build, and a genuine regression can hide inside noise. A branch on this repo has already
failed once by exceeding that cap.
make checkon a cleanmain. The worktrees present during thatrun happened to hold passing code. Had any held a broken or mid-rework tree,
make checkwouldhave failed on
mainfor reasons entirely absent frommain, and the obvious next move —re-running it — would not have explained anything.
only symptom is a test count that a reader has to already know is wrong.
temporary worktrees created inside the checkout.
Definition of done
make testdiscovers only the tests in the checkout it was invoked from, regardless of howmany worktrees or nested checkouts exist beneath it. A
vitest.config.tsadding.claude/**(and any other nested-checkout location) to
excludeis the obvious fix; prefer excluding byan explicit list over anything clever.
guarantees that nothing enforces, so demonstrate it rather than assert it. Creating a throwaway
nested directory containing a test file and showing the suite count does not change is
sufficient.
make lintandmake fmt-checkare verified against the same hazard and fixed if they shareit. Prettier 3 does read
.gitignoreby default so it is likely already safe, but confirmrather than assume; eslint's flat config needs checking on its own terms.
Suggested addition
make cleancurrently removes build output only. Consider having it also prune worktrees under.claude/worktrees/, or add a separate target, so the cleanup is a documented operation ratherthan tribal knowledge.
Not a 1.0.0 blocker
It does not affect shipped behaviour — it affects the reliability of the gate. Worth doing early
regardless, because every measurement taken while it is unfixed is suspect.
Still live, and now confirmed by direct reproduction rather than by accident. During review of #31 a nested tree was planted under
.claude/worktrees/containing real test files: hostmake testpicked it up (23 files instead of 22, exit 2).Two things that change the shape of this issue:
.claude/, and the same planted tree provably did not reach them. Point 3 of the definition of done above is therefore satisfied for lint and fmt-check — prettier 3 reads.gitignoreand.dockerignoremirrors it, so host and container agree. What remains is the test path only.make checkis. That lowers the urgency but not the value, since local runs are exactly where the misleading measurements get taken.