# src/core/reclaim — pure prune orphan computation

## Scope

Houses the safety-critical "which files are orphans" decision for the file-lifecycle
(prune) pillar — filesystem-free, unit-tested before any I/O exists. The filesystem
enumeration and the actual deletion live in `shell/persist` (`prune_fs`), not here.

## Invariants

- **The load-bearing rule: remove creates orphans; prune reclaims them.**
  Sample-remove and delete-bank drop index entries and may leave a file referenced
  by nothing. Prune is the single path that turns such an orphan back into free
  disk space. No other operation deletes a file; prune deletes only files that no
  index references.
- Prune reuses the shape Design View already shipped (`view_mode_model`'s
  `reconcile(liveGuids)`): prune reconciles files on disk against referenced files
  (the union of every bank's index) and returns the orphan set to delete — same
  pure pattern, one level down (files instead of GUIDs).
- Referenced-set is the union across ALL banks, pool included: a file is an orphan
  iff no bank in the book references it. This is the safety-critical computation —
  the prune null test is *prune never deletes a file that any index references.*
- Orphan attribution comes from the tracking ledger (`core/tracking`): the book
  tracks the files it has created; prune reclaims `(owned ∩ on-disk) − referenced`.
  This rejects folder-sweep (which would delete hand-dropped files).
- **Prune null test:** a prune of a folder whose every file is referenced by some
  bank deletes nothing; a prune deletes exactly the `present − referenced` orphan
  set and nothing else.
- Never a referenced file; never a non-bank file — the union-across-all-banks rule
  protects referenced files; the ownership-attribution rule (fork R-D) protects
  hand-dropped files.

## Modules

- `prune_reconcile` — pure prune core: `pruneOrphans(present, referenced, owned)` computes `(owned ∩ present) − referenced`; the safety-critical "which files are orphans" decision, filesystem-free and hard-tested before any I/O exists. `mergeReferenced(bankRefs, heldPaths)` unions live instance holds into the referenced-set. Two of the three inputs (`owned`, and the held half of `referenced`) come from `core/tracking`'s authority, never assembled ad hoc by a shell. `PruneReport` carries the authority's verdict: `blockedByTracking` + `ledgerUnreadable` / `ledgerFutureVersion` / `unreadableUsageKeys`.

## Gotchas

- Dry-run/confirm UX, trash-preferred deletion mechanics (fork R-C), and the
  manual-trigger guardrail (fork R-E) are deletion-*mechanics* concerns, not
  orphan-*computation* ones — they live in `shell/persist`, not here.
