getsentry / getsentry/XcodeBuildMCP

[Bug]: Orphaned workspaces are never swept — test-products for deleted project paths persist forever (126 GB past max age)

Offen
#526 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
TypeScript
Sterne
6.4k
Forks
319
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

## Bug Description

`~/Library/Developer/XcodeBuildMCP/workspaces` reached **143 GB** on my machine — 285 `.xctestproducts` bundles across 391 workspace directories, with 141 GB of that under `*/test-products/`. It was the single largest consumer on a ~1 TB disk that had run down to its last 96 GB.

This is adjacent to #524, but I believe it is a **distinct root cause**, and the retention knobs proposed there would not reclaim any of this space.

**A workspace is keyed by its project path, and it is never garbage-collected when that path stops existing.** `pruneManagedTestProductsDirectory` only sweeps while a server is active for that workspace (with a 1 h cooldown). In agent-driven workflows that create and delete a git worktree per task, the worktree path is gone permanently once the task finishes — so no server ever starts for that workspace again, and neither `TEST_PRODUCTS_MAX_AGE_MS` nor `TEST_PRODUCTS_MAX_COUNT` is ever evaluated against its bundles. They become effectively immortal.

### Evidence that the sweep never runs for these workspaces

| Bundle age | Bundles | Size |
| --- | ---: | ---: |
| > 3 days (`TEST_PRODUCTS_MAX_AGE_MS`) | 256 | 126.4 GB |
| > 7 days | 252 | 124.4 GB |
| > 14 days | 242 | 119.4 GB |
| > 30 days | 5 | 2.4 GB |

256 bundles are past the 3-day max age and still on disk. Had the age-based sweep run for those workspaces, all of them would have been removed.

Count-based GC cannot catch this either: the **largest number of bundles in any single workspace is 16**, far below `TEST_PRODUCTS_MAX_COUNT = 100`. Every workspace is individually "under limit" while the aggregate sits at 141 GB.

### The orphan ratio

A single project accounts for **223 of the 391 workspace directories**. That project currently has **17 git worktrees**. The remaining ~206 workspaces are keyed to worktree paths deleted weeks ago that will never be built again.

Because each workspace is separately under both thresholds, total consumption is unbounded in *the number of project paths ever built*, rather than in builds per path. Heavy agent/worktree use makes that number grow monotonically and silently — this accumulated over roughly three months without ever surfacing anywhere a user would normally look, since `~/Library` is hidden in Finder and macOS Storage settings folds it into an undifferentiated "Developer" bucket.

## Steps to Reproduce

1. Use an MCP client workflow that creates a fresh git worktree per task (`git worktree add`), which is the default for several coding-agent harnesses.
2. Have the agent call `build_sim` / `build_run_sim` / `test_sim` inside that worktree.
3. Complete the task and `git worktree remove` the worktree.
4. Repeat across a few hundred tasks over several months.
5. `du -sh ~/Library/Developer/XcodeBuildMCP/workspaces`

A quick way to see the orphaned share on an affected machine:

```bash
WS=~/Library/Developer/XcodeBuildMCP/workspaces
# bundles past the 3-day max age that are still present
find "$WS" -mindepth 3 -maxdepth 3 -name '*.xctestproducts' -type d \
! -newermt "$(date -v-3d +%Y-%m-%d)" -print0 | xargs -0 du -sch | tail -1
# most bundles retained in any single workspace (compare against MAX_COUNT = 100)
find "$WS" -mindepth 3 -maxdepth 3 -name '*.xctestproducts' -type d \
| awk -F/ '{print $(NF-2)}' | sort | uniq -c | sort -rn | head -1
```

## Expected Behavior

Test-product bundles belonging to a workspace whose project path no longer exists should be reclaimed. Two options that would each fix this:

- A **global sweep at server start** that drops workspace directories whose keyed path is missing from disk, independent of whether a server is ever active for that workspace again.
- A **total-size budget across all workspaces** (e.g. `XCODEBUILDMCP_TEST_PRODUCTS_MAX_TOTAL_BYTES`), rather than per-workspace age/count limits that each pass individually while the aggregate grows without bound.

## Actual Behavior

Every workspace ever created persists indefinitely along with its bundles. 126 GB across 256 bundles sits well past the 3-day max age, completely untouched, because the only code path that would delete it requires an active server for a directory that no longer exists.

## Notes

- Each workspace does carry `state/filesystem-lifecycle/last-cleanup`, so the lifecycle bookkeeping is already in place — it simply never runs again once a workspace is orphaned.
- A global sweep would also help users who do not use worktrees at all but who rename, move, or delete projects over time.
- Roughly 90% of the bytes are byte-identical across bundles (unchanged third-party dependency output recopied verbatim on every build), so content-addressed storage or hardlinking would be a further large win, though it is secondary to the missing GC.
- Related: #524 (retention limits too permissive in practice), #525 (DerivedData duplication between Xcode.app and the MCP).

## Debug Output

```
XcodeBuildMCP Doctor
Server Version: 2.7.0

## Checks
- xcode: ok Xcode 26.6 - Build version 17F113 (/Applications/Xcode.app/Contents/Developer)
- process-tree: ok Running under Xcode: No; 5 process entries
- axe: ok Available: Yes; UI automation: Yes; Video capture: Yes
- xcodemake: ok Enabled: No; Binary: No; Makefile: Not checked
- mise: warning Running under mise: No; Available: No
- debugger-dap: ok Selected backend: lldb-cli; lldb-dap available: Yes
- manifest-tools: ok Total tools: 82; Workflows: 15
- runtime-registration: ok Enabled workflows: debugging, doctor, project-discovery,
session-management, simulator-management, simulator,
ui-automation, utilities, xcode-ide; Registered tools: 58
- xcode-ide-bridge: ok Workflow enabled: Yes; Connected: No; Proxied tools: 0
- sentry: ok Enabled: Yes
```

## Editor/Client

Claude Code (desktop app)

## MCP Server Version

2.7.0

## LLM

Claude Opus 5

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Start by locating pruneManagedTestProductsDirectory, the workspace lifecycle cleanup, and the server-start path; review how TEST_PRODUCTS_MAX_AGE_MS and TEST_PRODUCTS_MAX_COUNT are applied. Reproduce the orphaned-workspace case with deleted project paths, then verify that stale workspace data is reclaimed or bounded across workspaces without requiring a server for each path.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
devtools
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Aktiv
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
48/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.