Desktop app
Copies screen
Every second full checkout under the roots you name, and the one verb that
deletes them. The engine is the sealed CLI's copies group
(cli/copies), run as tama request copies/list
and tama request copies/remove, so the screen and the terminal cannot
disagree about what a pass would do.
The inventory includes dependency checkouts under package-manager and build directories, submodule checkouts, and bare Git caches. All stored Git history is checked, including dangling commits and annotated tags. Roots contribute their canonical repositories together, so their order does not change ownership. Linked worktrees appear only once, under their primary repository.
#Scope, header, refusals
The roots are scope, not a destination: Add root… opens a directory
chooser, because the walk refuses a relative path and a directory that does
not exist, and both are avoidable before a request leaves. With no root the
screen states the command's own refusal rather than sending anything:
tama copies needs at least one --root; a machine-wide default would walk
directories Tama does not own.
Title Copies; scope is the number of roots; freshness reads
no root selected, not scanned, scanning now, scan refused,
or the copy count with the apparent size the pass found. Actions, in the
order the decision is made: Add root…, Scan, Preview removal,
Remove copies.
#What a row says
One row per copy: its path, the checkout it duplicates (OF), its
apparent size, its state, and what this pass would do with it. The state
chip carries the same facts the refusals are made of — uncommitted changes,
history not retained, no origin, only checkout here, owns worktrees — and a
clean row reads Clean. The inspector adds the origin, size and
command's refusal sentence. Git history retained by names the
canonical checkout whose independent, referenced objects preserve that history.
This proof removes the history and missing-origin refusals, but does not
override dirty files or replace an explicit claim when the owner is unknown.
A failed Git observation shows Unreadable Git state, never
Clean. The inspector's Git inspection error field
shows the actual failed command and error. Normal removal refuses this row;
the explicit force override may remove it without a history or working-tree
proof. Repository identification and filesystem errors remain failures.
#Keeping and claiming
Keep marks a copy to leave alone and travels as --except: it is
neither removed nor refused. Claim appears only on a row whose owner is
absent — the one refusal an operator can answer — and travels as
--only, narrowing the pass to exactly the claimed paths. The two cannot
travel together, so claiming clears every kept mark, and the screen says so
in both lists. Changing a mark, a root, or the override drops the preview:
an apply must never run against a plan that no longer describes it.
A claimed pass reads canonical repository headers and any object storage needed for its retention proof, then walks only the claimed subtrees. Unrelated working trees are not traversed. The ordinary scan and an unclaimed removal still walk all roots; a successful claimed removal is not evidence that no other copies remain.
Remove past every refusal (--force) is off until it is turned on, because
it is the difference between a safety refusal and deleted history. It does
not override an unreadable directory in the required walk, an unknown selection or a deletion error.
#What the screen never removes
Linked worktrees are counted and named, not removed: git tracks those and the Worktrees surface owns them. Two checkouts of one repository that both sit directly under a root are reported as a twin pair and neither is removable, because which one to keep is the operator's call. A candidate git will not answer for is reported as skipped.
A directory the walk could not read at all is reported too, as a danger panel naming the count and a warning panel per directory carrying the operating system's own reason. That is the difference between "one checkout per repository here" and "none where this pass could look", and the screen states it beside the count for the same reason the CLI exits non-zero: a pass over a tree it could not list would otherwise answer that the machine holds no second checkouts, and that answer would be quoted as proof.
#The decision
Remove copies is enabled only after a preview with no refusal
outstanding. It opens a confirmation that lists exactly the paths the
preview reported, states how much apparent size comes back, and names the
override and every mark in force. Refusals and unreadable directories are
checked before any deletion starts. Each deletion checks the current Git
state and retention proof again. Nested selections are removed before
their parents, and shared checkouts before selected Git object stores.
Cyclic dependencies refuse the pass before deletion. An operational error
is not a rollback guarantee. A clean pass re-reads the tree afterwards
and keeps the removed list on screen.
Control is required for this destination: a signed-out operator does not see it, rather than seeing a dead row.
Return to the desktop application or the copies command group.