Skip to content

feat(scanner): make the walk's no-follow symlink policy explicit - #12

Merged
ohing504 merged 1 commit into
mainfrom
walk-explicit-symlink-policy
Jul 17, 2026
Merged

feat(scanner): make the walk's no-follow symlink policy explicit#12
ohing504 merged 1 commit into
mainfrom
walk-explicit-symlink-policy

Conversation

@ohing504

Copy link
Copy Markdown
Owner

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 with os.ModeSymlink explicitly, instead of relying on os.ReadDir's incidental IsDir()==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

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
@ohing504
ohing504 merged commit 297510e into main Jul 17, 2026
3 checks passed
@ohing504
ohing504 deleted the walk-explicit-symlink-policy branch July 17, 2026 07:39
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.

1 participant