* fix(workspaces): support full cleanup scans
* feat(workspaces): persist cleanup snapshots
* feat(workspaces): add cleanup filter model
* refactor(workspaces): remove cleanup presets
* feat(workspaces): rework cleanup dialog
* fix(workspaces): keep cleanup row ordering render-pure
* refactor(workspaces): simplify cleanup browsing
* refactor(workspaces): show cleanup facts
* refactor(workspaces): surface cleanup row facts
* fix(workspaces): remove misleading cleanup count
* fix(workspaces): preserve full scan semantics
* fix(workspaces): scope snapshot persistence
* fix(workspaces): preserve cleanup browse compatibility
* fix(workspaces): reconcile cleanup dialog state
* test(workspaces): update snapshot store fixtures
* test(workspaces): preserve cleanup scan modes
* perf(workspace-cleanup): stream scan progress and size results
* fix(workspace-cleanup): address review feedback
* fix(workspace-cleanup): preserve host-scoped cleanup metadata
* fix(workspace-cleanup): declare review source dependencies
* fix(workspace-cleanup): align size scan banner
* fix(workspace-cleanup): shorten scan action
* perf(workspace-cleanup): avoid redundant scan IO
* perf(workspace-cleanup): bound restarted evidence scans
* fix(workspace-cleanup): satisfy scan queue lint
* perf(workspace-cleanup): bound scan and snapshot work
* perf(workspace-cleanup): serialize final enrichment
* test(workspace-cleanup): assert final enrichment drain
* fix(workspace-cleanup): stop progress after renderer teardown
* perf: batch workspace cleanup git evidence scans
* perf(workspace-cleanup): stop redundant snapshot and scan work
* fix(workspace-cleanup): resolve review findings across scan, store, and dialog
Correctness:
- Chunk git-evidence dispatches at the shared 500-target limit and exclude
queued/in-flight ids from target selection, so fleets past the limit can no
longer strand rows permanently mislabeled as checked-but-unknown.
- Key destructive selection pruning on the user's filter state instead of the
per-tick matched-set identity; streaming reclassification no longer silently
deselects rows.
- Clamp the facet clock to max(scannedAt, open time): a stale hydrated
snapshot no longer misbuckets idle thresholds or keeps dead agents fresh;
row labels use the same clock.
- Supersede and cancel the previous broad scan when a new one starts (renderer
registry and same-sender guard in main) instead of racing two fleet scans.
- Gate snapshot persistence on hasTargetedWorkspaceCleanupScan so
worktreeIds: [] can never persist an empty fleet snapshot.
- Re-apply dismissals at set-time in progress application so a dismissal
landing mid-enrichment is not clobbered.
- Record a one-off local snapshot prune for single (unbatched) remote deletes
so removed workspaces cannot resurrect from cache.
- Strip .exe when normalizing foreground process names so Windows agent
processes match.
Performance:
- Cache per-candidate facet and review-info objects on candidate identity;
no-op streaming ticks reuse the previous rows array and skip every
downstream pass; matched-set identity is stable under equal membership.
- Compute facet counts/options only while the filter popover is open.
- Equality-bail git-evidence publishes; structural (non-stringify) facet-group
comparison memoized in the toolbar.
- Identity-token fast path for the enrichment cache (cache hits skip both
JSON.stringify signatures); prune viewed/dismissal records on removal and
expiry; bound the superseded-scan-id set.
- Restore the no-op bail in removeWorkspaceSpaceWorktrees (regression).
- Abort main-side scans when the renderer is destroyed; module-scope
controller maps survive handler re-registration.
- Batch removal preflight into one targeted scan (with refreshActivity) per
500 ids instead of one scan per row.
- Scan repos at concurrency 2, report discovered counts upfront for honest
progress, share fs-activity probes per path (folder workspaces), read only
the reflog tail, and skip the snapshot read-before-write via a remembered
scannedAt.
Split workspace-cleanup-worktree-listing, workspace-cleanup-facet-row-caches,
and workspace-cleanup-selection-model out of files that crossed max-lines.
* fix(workspace-cleanup): address verifier findings
- Fall back to a full reflog read when the newest record exceeds the 8KB
tail window, so an oversized subject cannot hide recent ref activity.
- Bound the single-removal snapshot prune batch id with a UUID; embedding
the unbounded worktreeId silently failed main's 128-char validation and
skipped the prune for long remote ids.
- Key the main-side broad-scan supersession by sender AND scan mode so
legacy suggestion-only and full-workspace scans stay isolated, matching
the renderer registry.
* fix(workspace-cleanup): own facet caches with useMemo instead of render-time ref writes
React Doctor (CI changed-lines gate) correctly flagged the three cache refs
written during render. Each per-candidate cache now lives in one memo with
the derived context it is keyed on, so the memo deps are the invalidation
and interior fills stay content-addressed; the matched-set identity
stabilization is dropped since its only consumer reads through a
useEffectEvent and never keys on identity.