Description
tart prune can delete an OCI digest directory while leaving its tag symlink behind as a broken link.
This does not retain the VM's disk usage, but it leaves the OCI cache in an inconsistent state until the hidden garbage collector is run separately.
Steps to reproduce
-
Pull a tagged image:
tart pull ghcr.io/cirruslabs/macos-tahoe-base:latest
-
Prune the cache:
tart prune --entries caches --space-budget 0
-
Inspect the cache:
$ du -sh ~/.tart/cache
0B ~/.tart/cache
$ du -shA ~/.tart/cache
3.5K ~/.tart/cache
$ find ~/.tart/cache -type l -print -exec readlink {} \;
~/.tart/cache/OCIs/ghcr.io/cirruslabs/macos-tahoe-base/latest
~/.tart/cache/OCIs/ghcr.io/cirruslabs/macos-tahoe-base/sha256:a8e1c8305758643f513fdccdd829c2243687c60791083dea42f73f0b7aeb435c
The digest target no longer exists, but the latest symlink remains.
Expected behavior
After pruning an OCI digest, Tart should remove any tag symlinks that now point to the deleted digest.
Actual behavior
The digest directory is deleted successfully, but its tag symlink remains broken.
The current workaround is a second command:
Root cause
VMStorageOCI.prunables() explicitly excludes symlinks and returns the digest VMDirectory objects:
https://github.com/openai/tart/blob/2.34.0/Sources/tart/VMStorageOCI.swift
The normal prune paths call Prunable.delete() directly:
https://github.com/openai/tart/blob/2.34.0/Sources/tart/Commands/Prune.swift
This bypasses VMStorageOCI.delete(_:), which is the deletion path that calls gc().
Additionally, when --gc is combined with a pruning criterion, Prune.run() invokes GC before deleting entries. Therefore this still leaves links broken by the subsequent prune:
tart prune --gc --entries caches --space-budget 0
Suggested fix
Run VMStorageOCI.gc() once after OCI cache pruning completes.
A regression test could:
- Create a digest directory and tag symlink.
- Prune the digest using both the age and space-budget paths.
- Verify that the digest and newly broken tag symlink are both removed.
- Verify that combining
--gc with a pruning criterion also leaves no broken links.
Related history
Environment
Tart 2.34.0
macOS 27.0
Architecture: arm64
Description
tart prunecan delete an OCI digest directory while leaving its tag symlink behind as a broken link.This does not retain the VM's disk usage, but it leaves the OCI cache in an inconsistent state until the hidden garbage collector is run separately.
Steps to reproduce
Pull a tagged image:
Prune the cache:
Inspect the cache:
The digest target no longer exists, but the
latestsymlink remains.Expected behavior
After pruning an OCI digest, Tart should remove any tag symlinks that now point to the deleted digest.
Actual behavior
The digest directory is deleted successfully, but its tag symlink remains broken.
The current workaround is a second command:
Root cause
VMStorageOCI.prunables()explicitly excludes symlinks and returns the digestVMDirectoryobjects:https://github.com/openai/tart/blob/2.34.0/Sources/tart/VMStorageOCI.swift
The normal prune paths call
Prunable.delete()directly:https://github.com/openai/tart/blob/2.34.0/Sources/tart/Commands/Prune.swift
This bypasses
VMStorageOCI.delete(_:), which is the deletion path that callsgc().Additionally, when
--gcis combined with a pruning criterion,Prune.run()invokes GC before deleting entries. Therefore this still leaves links broken by the subsequent prune:Suggested fix
Run
VMStorageOCI.gc()once after OCI cache pruning completes.A regression test could:
--gcwith a pruning criterion also leaves no broken links.Related history
tart prune --gccommand.tart listquestions #776 confirms that healthy tag symlinks are expected; this report concerns only broken ones.Environment