feat(scanner): make the walk's no-follow symlink policy explicit - #12
Merged
Conversation
The walk skipped symlinked directories only as a side effect of os.ReadDir's IsDir()==false. Assert it directly with an os.ModeSymlink check so a future refactor can't silently start following links. Following a symlink would double-count its target (real content that lives on disk elsewhere), inflating reported reclaimable space, and a delete of a symlinked artifact reclaims only the link while risking a shared target — so no-follow is the correct policy, not an accident. Behavior is unchanged; symlink cycles stay unwalkable, so no separate cycle guard is needed. Reclaimable content behind such links is the job of the Global Caches scanner and hardlink-aware sizing. Strengthen the test to also prove the walk never descends *through* a symlinked directory to match an artifact inside it, and document the policy in docs/architecture.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YSTf3ozF4SnboZHyEi7Ycc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
P2-5. Formalizes the walk engine's symlink policy. Investigation confirmed the current behavior (no-follow) is already the correct one — following symlinks would double-count the target's disk space and risk deleting a shared target — so this does not add symlink-artifact detection; it locks the correct policy in place.
Changes
internal/scanner/walk.go— skip any entry withos.ModeSymlinkexplicitly, instead of relying onos.ReadDir's incidentalIsDir()==false. A future refactor can no longer silently start following links. Behavior is unchanged.internal/scanner/walk_traversal_test.go— the existing no-follow test now also proves the walk never descends through a symlinked directory to match an artifact inside it (target kept outside the scan root so it's reachable only via the link).docs/architecture.md— a "Symlink policy — never follow" paragraph explaining the rationale (double-count avoidance, shared-target safety, cycle-free by construction) and where real reclaimable space is surfaced instead (Global Caches scanner + hardlink-aware sizing).Why no-follow (not detection)
A symlinked artifact (e.g. a pnpm/monorepo
node_modules) points to content that already lives on disk elsewhere. Reporting it would inflate the reclaimable total and, on delete, reclaim only the link while risking a target other projects share. The real space belongs to the Global Caches scanner (pnpm store) and the future hardlink dedup (P2-3).Verification
go test ./...— all pass;go test -race ./internal/scanner/clean.golangci-lint run ./...— 0 issues.🤖 Generated with Claude Code
https://claude.ai/code/session_01YSTf3ozF4SnboZHyEi7Ycc