Skip to content

fix(server): report orphaned managed worktrees - #19749

Merged
maria-rcks merged 17 commits into
mainfrom
t3/storage-cleanup-orphan-worktrees
Oct 15, 2026
Merged

maria-rcks merged 17 commits into
mainfrom
t3/storage-cleanup-orphan-worktrees

Conversation

@maria-rcks

@maria-rcks maria-rcks commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #19748. Folders inside the managed worktree directories that no thread row references were invisible to storage cleanup. One host had 43 of them, another a single 6.6 GB one.

Cleanup now reports them as kept worktree entries with their full paths and measured sizes, without deleting their files. Failed and removed entries come first, then orphan kept entries ahead of other kept entries so thread results cannot crowd them out of the 200-entry report. Linked worktrees read "No thread uses this worktree"; other folders read "No thread uses this folder, and it isn't a git worktree". Both root/repository/worktree and direct linked-worktree layouts are scanned. Folders referenced by any thread row (including archived, deleted, and subagent threads), ancestors of referenced paths, project workspace roots (including deleted projects), and symlinks are excluded. Path comparisons use canonical paths and ignore case on Windows. A one-hour mtime grace skips new folders while launches and review-guide checkouts are being prepared. Unreadable folders are skipped, and project-root probe errors preserve the root as an exclusion instead of failing the report. The reference query runs on the read-only SQLite worker.

Orphan sizes are cached in memory across sweeps, with cache entries dropped when their paths stop being orphans. New measurement attempts share a 30-second budget per sweep and keep the existing two-million-entry limit. Sizes cut off by the time budget are reported as unknown and retried on the next sweep; folders that cannot be measured are reported without a size and are not re-walked. The settings card shows full paths for orphan rows and measured sizes for kept entries; thread row titles stay unchanged, and kept bytes do not count toward bytes recovered.

On Blacksmith at b741fbb, storageCleanup.test.ts (137 tests), StorageSettings.test.tsx (4 tests), vp run --filter t3 typecheck, and lint for both touched TypeScript files passed (141 tests total). vp run --filter @t3tools/web typecheck previously passed at fa516f0. Regression coverage includes report crowding, canonical and Windows-normalized ancestor paths, fresh versus old folders, project-root probe errors, and a counter proving cached orphans are not walked again. On a dev server with a seeded database copy, the manual cleanup RPC reported a 125-byte orphan linked worktree and a 6,600,000-byte plain folder ahead of thread entries. It did not report the folder containing a live thread checkout or a fresh review-guide checkout as orphans, and all their files remained on disk (report JSON). The real Storage settings "Delete now" action and expanded "Last run" rows showed the full orphan paths and sizes. Native Windows was not run; case-insensitive matching is covered with injected host paths.

Before: kept orphan sizes are hidden

After: orphan rows show full paths and measured sizes

Written by gpt-6.1-sol (Codex) in T3 Code on behalf of Maria.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 15, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 15, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 15, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR not approvable — The server now performs filesystem and database scans during storage-cleanup sweeps and exposes newly reported orphan folders with measured sizes in the settings UI. The implementation is substantial new runtime behavior rather than a small isolated fix, so its operational impact merits human review.

No code changes detected at 983bf01. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB +7 B (+0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB +7 B (+0.6%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: e733a40 · PR result: 983bf01 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp
macroscopeapp Bot dismissed their stale review October 15, 2026 04:14

Dismissing prior approval to re-evaluate 6bf79a0

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Oct 15, 2026
Base automatically changed from t3/storage-cleanup-subagent-owners to main October 15, 2026 23:42
…phan-worktrees

# Conflicts:
#	apps/server/src/storageCleanup.test.ts
@maria-rcks
maria-rcks merged commit fa1184d into main Oct 15, 2026
29 checks passed
@maria-rcks
maria-rcks deleted the t3/storage-cleanup-orphan-worktrees branch October 15, 2026 23:50
GwydionBr added a commit to GwydionBr/t3code that referenced this pull request Oct 16, 2026
Storage cleanup now follows upstream, which handles worktrees shared by
subagents on its own (pingdotgg#19748, pingdotgg#19749). Only the missing-.git guard from #14
stays on top. Branch adoption in ThreadPullRequestService is kept and uses
the new crypto.randomUUIDv4() call. Branch-group sidebar code is kept with
upstream's pruned comments, and its header focus ring is now inset for the
new lint rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
daehyeon-mun added a commit to ccheever/exact2 that referenced this pull request Oct 18, 2026
…766)

Storage can run cleanup on selected machines and explain each removed,
kept or failed item. Worktree settings add a folder, local-change policy
and zero-day settled-thread retention. Threads with pull requests can
stay active; Stop ends a PR watch even before the first run. Project
actions can run on settle, and scheduled tasks can accept signed
webhooks with delivery details.

Task: `storage-settle`. Base: `daehyeon/t3-code-main`, including landed
runtime #740 and machine settings #750. Reference: T3 Code `9c161462c9`.
App files only; framework sources are unchanged.

| Scoped upstream change | Result | Proof |
| --- | --- | --- |
| `0caa95d6eb` [#17563](pingdotgg/t3code#17563),
`fa1184d29e` [#19749](pingdotgg/t3code#19749):
cleanup and orphan reports | Delete now uses each selected permitted
transport; Last run shows per-item reasons, sizes, files and omitted
results. Orphans retain full paths. | Focused rules/scope tests;
[Real3302 RPC
comparison](#766 (comment))
returned 0 removed, 1 kept, 0 failed |
| `21bedfb175` [#15208](pingdotgg/t3code#15208),
`65d390fc52` [#19437](pingdotgg/t3code#19437),
`1024941575` [#16231](pingdotgg/t3code#16231):
keep rules, settled retention, database notice, worktree location |
Capability-gated controls preserve per-checkout inheritance. Database
status streams show a copyable offline command when reclaimable space
reaches 1 GiB without incremental vacuum; dismissal persists per
machine. | Zero-day/mixed-scope/stale-target tests,
database-threshold/dismissal tests; real server settings round-trip |
| `bf8ffe81a6` [#14642](pingdotgg/t3code#14642):
keep PR threads | Project-scoped Keep threads with a PR requires
auto-settle and support on all selected machines. | Scope planner and
settings tests |
| `207f6a10b0` [#16204](pingdotgg/t3code#16204),
`51f7f77f87` [#16095](pingdotgg/t3code#16095):
PR watches and Stop | Never-run watched threads stay Working. Stop sends
watch=false for each visible active PR. The runtime from #740 ends
watches when threads settle. | Actual composer-command and sidebar
projection regression tests |
| `079e4bccdc` [#16290](pingdotgg/t3code#16290):
actions on settle | Setup and settle roles remain independent.
Settle-only actions stay reachable in Actions and never become the
primary Run button, including a remembered selection. | Role
transfer/import/primary-action tests; [Real server retained
runOnSettle=true](#766 (comment))
|
| `7dfb86a32b` [#15085](pingdotgg/t3code#15085),
`ea4b44343d` [#15088](pingdotgg/t3code#15088):
webhooks | Editor supports placeholders, signature fields, write-only
existing secrets, age limits, URL copy/rotation confirmation. Deliveries
show outcomes, redacted request, missing fields and rendered prompt.
Both dialogs trap and restore focus. | Validation/round-trip/delivery
tests; [Real signed-webhook
fixture](#766 (comment))
rejected an unsigned POST with 401; [signed
request](#766 (comment))
returned 202, verified the signature and rendered `{{body}}` |

Final source: `d1c3ad34b964fd486fcd367fa37ce648b6303008`, including
qualified app integration `70699d0379`. The two manual merge resolutions
retain all three environment-wide keys (`worktreesDirectory`,
`favorites`, `providerModelPreferences`), Storage's keep-PR default of
false and the incoming restart-continuation default of true. All changes
relative to that base are app files.

- Generated inputs refreshed with pinned Bun 1.4.2. Final affected
checks pass 539 tests / 4321 assertions across 35 files, strict app
TypeScript, caps and diff checks.
- The task-owned Contract compiler was rebuilt against this live SDK.
Both existing ambient-analysis integration tests pass. Final merged app
preflight passes: 6391 slots, 30 derives, 80 resources, 10731 actions,
118308 nodes, 49747 regions, 37815496 bytes. No deleted-worktree SDK
path or ambient bypass is used.
- Earlier broad validation on `9f4c6316b` passed 3807 tests / 41936
assertions across 256 files. Terminal-host build and offline 3302
staging passed, including archive hash, SHA256SUMS, 11 Mach-O signatures
and scratch smoke. The coordinator owns the final combined suite/native
qualification for incoming app tasks.
- [Isolated real-server
grants](#766 (comment))
confirm `settings:write` alone runs cleanup while `environment:maintain`
alone is denied. Per-target UI/dispatch tests independently cover the
same gate. The [signed
request](#766 (comment))
returned 202, verified the signature and rendered `{{body}}`; the
unsigned request returned 401.
- The first native task-editor drive exposed five persistence-only
fields leaking into the strict public resource shape. Repair `64876e527`
projects only declared editor and webhook fields. Its exact-key
regression derives the public fields from the actual Contract
declaration and fails before the repair. Expanded repair checks passed
83 tests / 327 assertions; these regressions are included in final
affected checks.

The final directory guard `1b69d667df` keys the draft occurrence by
scope, mixed state and saved path, matching pinned upstream’s input
identity. An actual Contract/Runner probe importing the product
StoragePanel reproduced stale display after switching machines, Reset,
value replacement and mixed-state changes; an edit then targeted machine
B with machine A’s stale path. The repaired source passes nine runtime
assertions, including scope-only changes with matching paths and
preserving an unsaved edit during an unrelated busy update. The
coordinator independently reviewed the two-line patch and reran the
probe with no findings. This guard is source-qualified after the frozen
native `64876e527` proof; the final combined native bake will cover it.

Native evidence is attached with exact snapshot qualifiers:

| Phase | Source and result |
| --- | --- |
| Shared BEFORE | Clean `4e522ea9ba67c7599fc69f5013299ad8cca7f4ca`,
built `2026-10-18T13:26:06.707Z`, independently verified from this
bundle's Info.plist and executable. SHA-256
`ba0d0ba66bf413b6a5b0d599478c106de988ec8d74517d6fd5242556e5604320`. |
| Storage AFTER | Signed `a8565502297a0c3ece4c35e087217ebdaede91f8`
(Cargo data tests 29/29); [independent native
comparison](#766 (comment))
proves the connected page, location, Any local files, 0-day settled
retention and Delete now → Last run / 0 removed / 2 kept. |
| Repaired webhook AFTER | Clean signed
`64876e527242a7308b60896bcc7d158db20457ce`, executable SHA-256
`a0cd91f5fc8777cf5ba5aff459beaa25c057afcbf44dbf2980d59647bac59717`; [18
assertions and six inspected window
captures](#766 (comment))
prove the real editor, signature/age/blank existing secret, Copy
preserving its draft, Rotate → Cancel preserving the draft, and
rejected/accepted delivery details with redaction, verified signature
and rendered prompt. |

The webhook drive used authored ArrowDown/Enter input for
Actions/Edit/Deliveries and native taps/text elsewhere. A preceding
pointer tap left the popup closed; pointer-menu acceptance is not
claimed. Rotation was cancelled, so URL replacement was not exercised in
this drive. Relative-age labels reflect the agent's virtual epoch versus
fixture timestamps and were not asserted. The native source qualifiers
above are retained after integration; the incoming app tasks were not
rebaked in this lane.

The isolated fixture uses port16771 and its own home under ignored
target/. Its provider is the local ACP stand-in; no real provider
account is used. Evidence files are attached through GitHub and are
never committed.

Prepared with Codex; continues work begun in the prior Claude session.

---------

Co-authored-by: Codex <noreply@openai.com>
aorwall added a commit to aorwall/t3code that referenced this pull request Oct 19, 2026
Merges `pingdotgg/t3code` `main` at `3962ae8586` (96 commits since
`e536bfd33b`) into the fork. One merge commit; the tracker entry in
`docs/fork/upstream-merge-log.md` has the full decision list.

## Conflicts (8)

- **`ChatView.tsx`** (`converged`): upstream pingdotgg#19869 replaced
`useCallback` with plain functions plus `useStableCallback`. I took
upstream's file and re-applied each fork delta in that style: the
sandbox start for scripts, the draft-thread create and host-run path in
`runProjectScript`, Workspace script saves, the sandbox surface, file
retargeting, the announced preview and terminal effects, the
sandbox-commands banner, the PR-surface gate and the thread-fork dialog.
**This file is the one to review closely.**
- **`rightPanelStore.ts`** / **`FilePreviewPanel.tsx`**: the fork's
`retargetFile` sits beside upstream's new `navigateFolder` and
`folderTrail` (pingdotgg#15045).
- **`ci.yml`**: upstream folded the `typecheck` job into `build`
(pingdotgg#19633). The Moatless spec check now follows `build`'s final
`Typecheck` step.
- **`Sidebar.tsx`, `ProjectSettingsPanel.tsx`**: upstream's code plus
the fork's additions.
- **`index.html`**: kept the Moatless title. Added a `branding-shell`
path-policy entry for it and `branding.ts`.
- **`source-control-github/package.json`**: took upstream's new
`generate` script and kept `tsc --noEmit` (the `tsc-rs` tripwire).
- **`pnpm-lock.yaml`**: took upstream's copy, stripped `tsc-rs`, and
re-derived it with a real install.

Merge stats: 738 files landed against 736 in the upstream range. The one
upstream file that didn't land is `index.html`, whose only upstream
change was the title. The three extra landed files are fork docs. The
fork delta is 815 files.

## Contract
- Added `UnsupportedMethodError` to
`orchestration.subscribeCommandOutput` and `server.getDatabaseStorage`.
Both are new upstream methods Moatless doesn't dispatch.
- Moatless now serves 11 more `pullRequests.*` reads (moatless pingdotgg#1161,
pingdotgg#1164, pingdotgg#1169). They share `PullRequestRpcError` with methods it still
refuses, so the union keeps `UnsupportedMethodError`. The 11 are
declared as `pull-requests-*-shared-union` exceptions in
`inventory.json`, which retire together once the rest of the family is
served.

## Also in this PR
`sandboxSurfaces.test.ts` had been red on `main` since `95dcaca54f` made
the `pull-request` surface sandbox-independent without updating the
test. The test and module doc now match the code.

## Verification
Duplicate-adds, tripwires, resolution-check, unsupported-methods,
lockfile, format, lint, typecheck and the production build are all
green.

The full test pass hit its one-hour deadline, so I ran the remaining
packages one at a time. These still fail when run alone, in the same
files the 2026-10-15 merge logged as sandbox failures:
- `shared`: `AgentScope`
- `t3`: `ServerBrowserPage`, `AcpAdapterV2`
- `provider-opencode`: `OpenCodeServerLedger`
- `ssh`: `tunnel.test.ts`

`t3`'s `OrchestratorReplayFixtures` `claude_result_is_error` fails only
because this sandbox sets `CLAUDE_CONFIG_DIR`; it passes with that
unset. Every other package passes.

## Upstream features

### Usable as-is
- Folder breadcrumbs navigate to parent folders (pingdotgg#15045).
- Chat view, composer and sidebar no longer keep old renders alive
(pingdotgg#19869, pingdotgg#19852).
- Thread list cache saves without re-encoding every thread (pingdotgg#19915).
- React Compiler runs natively (pingdotgg#20114).
- Linked pull requests are virtualized (pingdotgg#20107).
- A linked PR can be dragged into the composer as a context chip
(pingdotgg#20102).
- HTML renders show a skeleton and open images in the image viewer
(pingdotgg#19654, pingdotgg#20036).
- Model picker search lists the newest models first (pingdotgg#18494).
- Wrapped split diffs stop jumping while scrolling (pingdotgg#20022).
- Text selection continues under the turn minimap (pingdotgg#16748).
- Sent queued messages say "from queue" (pingdotgg#19849).
- Settings stay compact with many connected machines (pingdotgg#19744).
- Middle-click and a setting to open PR links on GitHub (pingdotgg#19854,
pingdotgg#19719).
- Text generation no longer falls back to the flagship model (pingdotgg#19722).
- Pending context handoff is hidden on threads with no sent message
(pingdotgg#20033).
- Mobile and desktop changes don't affect the fork's web client.
- Not taken: the Beta branding (pingdotgg#20032).

### Unsupported in Moatless / needs implementation
- **Live command output** (pingdotgg#15094):
`orchestration.subscribeCommandOutput`, shown in
`CommandOutputPanel.tsx` behind `capabilities.commandOutputStreaming`.
Covered by the new union entry.
- **Database storage notice and `t3 doctor` reclaim** (pingdotgg#19436, pingdotgg#19437):
`server.getDatabaseStorage`, shown in `DatabaseStorageNotice.tsx` behind
`capabilities.databaseStorage`. Covered by the new union entry.
- **Delete a branch after its PR merges or closes** (pingdotgg#19257): a
`pullRequests.runAction` option. The action is refused here.
- **Device setup through a remote device host only** (pingdotgg#20077): behind
`FEATURES.deviceHub`.

### Backend behavior to consider reproducing in Moatless
- Resumed Claude subagents keep updating their own thread and pick up
the parent's renamed worktree branch (pingdotgg#20061, pingdotgg#20079).
- Steering no longer ends running subagents (pingdotgg#19210).
- Worktrees shared by subagents are cleaned up, and orphaned ones are
reported (pingdotgg#19748, pingdotgg#19749).
- Superseded history is compacted in the background (pingdotgg#19435).
- A run whose provider stream ends without a terminal event fails
instead of hanging (pingdotgg#19131).
- One malformed ACP session update no longer ends the session (pingdotgg#20015).
- A dropped HTTP/2 connection no longer crashes the server (pingdotgg#18378).
- PR refreshes are bounded by tracker reads (pingdotgg#19403).
- Cursor threads in Auto use Cursor's Auto-review (pingdotgg#19712).
- Grok health checks no longer start its updater (pingdotgg#18075).
- Expanding a wide ignored folder no longer classifies every child
(pingdotgg#20062).
- OTLP exports name the machine and build they came from (pingdotgg#19370).

Recorded in `docs/fork/gaps.md` under "Runtime fixes upstream made to
its own server".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---
Moatless task:
https://moatless.soaplabstest.com/tasks/d6f425f2-8234-412e-8189-1c71b309c04a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants