Files
kandev/docs/decisions/2026-07-19-workspace-symlink-entries.md
T

2.3 KiB

ADR-2026-07-19-workspace-symlink-entries: Treat Nested Workspace Symlinks as Entries

Status: accepted Date: 2026-07-19 Area: backend, infra

Context

Kandev workspaces routinely contain repository-owned symlinks, including checked-in links such as CLAUDE.md -> AGENTS.md and package-manager links beneath node_modules. Storage analysis and orphan cleanup rejected any nested symlink, which made normal task roots unmeasurable and permanently ineligible for quarantine even when authoritative inventory classified them as orphaned.

Decision

Workspace storage operations treat a nested symlink as an opaque directory entry and never follow its target. internal/system/storage/workspaces measures roots with filepath.WalkDir, skips symlink entries, quarantines the complete root with an atomic rename, and permanently deletes the quarantined tree with os.RemoveAll; these operations do not traverse nested symlink targets.

The fail-closed boundary remains unchanged for owned control paths. A symlinked task root, tasks or trash ancestor, quarantine path, ownership marker, or quarantine manifest is rejected before any mutation. Authoritative liveness inventory, containment validation, grace periods, and quarantine retention remain mandatory.

Consequences

  • Normal Git and package-manager workspaces can be measured, quarantined, and deleted.
  • Files reached only through a nested symlink target are not counted and are never changed.
  • Reviewers must preserve no-follow filesystem primitives throughout workspace maintenance; a future traversal API that follows symlinks must supersede this decision or restore rejection.
  • Size totals describe bytes stored inside the workspace tree, excluding target data referenced by symlinks.

Alternatives Considered

  • Reject every nested symlink. Rejected because ordinary Kandev repositories then cannot be analyzed or reclaimed.
  • Resolve each target and allow only links contained by the task root. Rejected because cleanup does not need target contents, relative and temporarily broken links are legitimate repository state, and resolution adds race-prone complexity.
  • Follow symlinks while measuring or deleting. Rejected because it can count or destroy data outside Kandev-owned task roots.