Skip to content

Scan engine QoL: exclusions, streaming, cancel-keeps-partial, pause/resume, rate display - #114

Merged
RasputinKaiser merged 2 commits into
mainfrom
worktree-agent-a1eb1df161bfa8bce
Jul 1, 2026
Merged

Scan engine QoL: exclusions, streaming, cancel-keeps-partial, pause/resume, rate display#114
RasputinKaiser merged 2 commits into
mainfrom
worktree-agent-a1eb1df161bfa8bce

Conversation

@RasputinKaiser

Copy link
Copy Markdown
Owner

Summary

  • Folder exclusion list: ScanOptions.excludeEnabled/excludedPathComponents/excludedAbsolutePrefixes, wired into Settings (toggle + add/remove list, seeded with node_modules, .git, ~/Library/Caches, ~/Library/Application Support) and an "Exclude This Folder" context-menu action in StorageItemTable, TreeExplorerView, CleanupReviewView, and DuplicateCandidatesView (which targets the containing folder, since duplicate rows are always files).
  • Cancel now keeps partial results instead of discarding them; StorageScan.isPartial marks in-progress snapshots.
  • Live streaming: FileSystemScanner.scan(...) gains an onSnapshot callback fired at the same throttled cadence as progress, publishing ranked lists into session.scan while a scan is running.
  • Rate/elapsed display: ScanProgress.itemsPerSecond (EMA-smoothed in ScanStore), plus an elapsed-time readout in the sidebar footer.
  • In-memory pause/resume: ScanCancellation rewritten on NSCondition with pause()/resume()/waitIfPaused(); a toolbar Pause/Resume button next to Cancel.

Notable fixes made during self-review

  • Fixed a one-tick lag where the scanner's onSnapshot fired after progress inside emitProgressLocked, so ScanStore's snapshot/progress pairing was always one throttle-tick stale — reordered so onSnapshot fires first.
  • Standardized the paths stored by excludeFolder/excludeFolder(atPath:) to match FileSystemScanner.isExcluded's comparison (both now use standardizedFileURL.path), so exclusions set from a symlinked-ancestor path still match on rescan.
  • Gated the "Exclude This Folder" context-menu action on !store.isScanning in all four views — it routes through rescan(), which no-ops silently while a scan is already in flight (reachable now that streaming makes store.scan non-nil mid-scan).
  • Flagged (documented, not solved) a known thread-pool-exhaustion risk: waitIfPaused() blocks inside DispatchQueue.concurrentPerform closures, so pausing a very wide directory scan can approach GCD's shared worker-pool ceiling.

Test plan

  • swift test — 184 tests, 3 consecutive full-suite runs green
  • swift build and swift build -c release — both clean
  • New tests: exclusion (name/prefix/nested/toggle-off), snapshot() mid-walk, pause/resume blocking + cancel-while-paused, ScanSession.canPauseScan/canResumeScan guards, ScanStore cancel-keeps-partial-snapshot
  • 5-angle automated code review (correctness × 3, cleanup, conventions) run against the diff; confirmed findings fixed, documented/deferred findings noted above

🤖 Generated with Claude Code

RasputinKaiser and others added 2 commits June 30, 2026 23:03
…esume, rate display

Unit 2 of the scan-engine QoL patch: adds a folder exclusion list (name and
absolute-prefix matching, wired into Settings and four context menus),
cancel-keeps-partial-results, live streaming of scan snapshots into list
views during a scan, an items/sec + elapsed-time display, and in-memory
pause/resume backed by an NSCondition-based ScanCancellation.

Core changes:
- ScanOptions gains excludeEnabled/excludedPathComponents/excludedAbsolutePrefixes;
  FileSystemScanner.scanItem skips matching children before recursing.
- StorageScan gains isPartial; ScanAccumulator gains a lock-guarded snapshot()
  used both mid-scan (streaming) and by tests.
- ScanCancellation is rewritten on NSCondition to add pause()/resume()/waitIfPaused(),
  re-checking cancellation on every wake so cancel-while-paused can't hang.
- ScanStore pairs each throttled progress tick with the scanner's paired snapshot
  (onSnapshot fires before progress in the same emitProgressLocked call, so the
  pairing is same-tick rather than one-tick-stale) and publishes it to session.scan
  as the scan progresses; cancelScan() now preserves rather than resets state.
- Added items/sec EMA + elapsed-time display in the sidebar footer, and a
  toolbar Pause/Resume control alongside Cancel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…f161bfa8bce

# Conflicts:
#	Sources/StorageScope/Views/CleanupReviewView.swift
#	Sources/StorageScope/Views/StorageItemTable.swift
@RasputinKaiser
RasputinKaiser merged commit 5d30a9f into main Jul 1, 2026
6 of 7 checks passed
@RasputinKaiser
RasputinKaiser deleted the worktree-agent-a1eb1df161bfa8bce branch July 1, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant