Repository navigation
fix(server): report orphaned managed worktrees - #19749
Conversation
ApprovabilityVerdict: 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 You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 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.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Dismissing prior approval to re-evaluate 6bf79a0
…anup-orphan-worktrees
…anup-orphan-worktrees
…anup-orphan-worktrees
…phan-worktrees # Conflicts: # apps/server/src/storageCleanup.test.ts
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>
…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>
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
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 typecheckpreviously 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.Written by gpt-6.1-sol (Codex) in T3 Code on behalf of Maria.