Files
kandev/docs/plans/storage-analysis-progress/plan.md
T
2026-09-13 12:08:24 +01:00

8.3 KiB

created, status, requirements, system_design, legacy_specs
created status requirements system_design legacy_specs
2026-09-05 complete
REQ-SYSTEM-PAGE-STORAGE-OVERVIEW-PARALLEL-SCAN-002
../../specs/system-page/system-design/storage-analysis-progress.md

Implementation Plan: Progressive Storage Analysis

Overview

Reduce scan time with bounded filesystem concurrency. Then make overview reads nonblocking and stream coarse progress to the Storage page.

The order protects the cache contract. The scanner lands first, the backend state contract follows, and the web work consumes that contract.

The temporary storage visibility and cleanup package extends this completed source and progress contract with an informational system temporary-folder source.

Scope

In scope

  • Bounded, context-aware byte measurement for read-only storage analysis.
  • Nonblocking overview reads with stale-while-revalidate behavior.
  • Coarse progress notifications with a polling fallback.
  • First-scan partial results and atomic replacement of older snapshots.
  • Scan duration, cache lifetime, and next-refresh details.
  • Desktop and phone access to the timing disclosure.
  • Focused public operator documentation.

Out of scope

  • Changes to destructive cleanup or quarantine rules.
  • A persistent overview snapshot across backend restarts.
  • A user-configurable scan worker count or cache lifetime.
  • File-level progress events or exact time-remaining estimates.
  • Remote-executor disk analysis.
  • Automatic scans when no client reads the Storage overview.

Technical approach

Bounded filesystem measurement

Add apps/backend/internal/system/storage/filescan. Its shared limiter will permit four active directory partitions across workspace, Go-cache, and temporary-artifact analysis.

Partition each root by immediate children. Preserve provider-specific symlink rejection, marker exclusion, warning order, and path ownership.

Inject the limiter through storageOverview provider construction. Keep destructive provider paths on their current serial code.

Progressive cache and API

Extend OverviewCache with process-local analysis state and a nonblocking read. Keep Refresh as the tracked manual path and join every concurrent request to one scan generation.

Add source progress callbacks to storageOverview. Keep the last successful Summary atomic and store cold-scan partial results separately.

Extend GET /api/v1/system/storage with nullable first-scan snapshot fields and an analysis object. Include scan timestamps, duration, cache lifetime, refresh time, source progress, and a partial summary.

Publish system.storage.analysis.updated with generation and state only. Add the event constant, gateway action, subscription, and frontend revision field.

Web state and presentation

Update storage API types and the Zustand system slice for analysis state and revision ordering.

Refactor useStorageMaintenance so WebSocket revisions reload only the overview. Poll every 1.5 seconds while analysis is active and stop in a terminal state.

Schedule one read at refresh_due_at while the Storage page remains mounted. Preserve request generation checks and terminal Analyze refresh behavior.

Update StorageOverviewCard to show cold-scan source progress and counted-so-far totals. Keep a stale completed snapshot visible during revalidation.

Add an information icon beside the completed snapshot time. Reuse StorageSettingHelp interaction and permit structured content for the three timing values.

Documentation and observability

Update docs/public/operations.md with the asynchronous scan, 15-minute cache, and Analyze behavior.

Add structured scan completion logs and storage scan metrics. Do not include task paths in logs or metrics.

Tests

Acceptance criteria Evidence
AC-SYSTEM-PAGE-STORAGE-OVERVIEW-PARALLEL-SCAN-002.1, .10 Cache and handler tests block the provider and prove immediate, single-flight reads.
.2, .3 Provider, cache, hook, and card tests publish sources progressively and mark partial totals.
.4, .9 Cache tests keep the last snapshot through expired and failed refreshes.
.5 Event, hook, and fake-timer tests cover WebSocket refresh and the 1.5-second fallback.
.6, .7 Component and Playwright tests cover timing values, focus, hover, tap, and touch size.
.8 Hook fake-timer tests request at refresh_due_at. Analyze tests retain forced refresh.

The filesystem scanner tests will cover exact bytes, no-follow behavior, deterministic warnings, cancellation, and a four-worker ceiling.

The provider tests will use barriers instead of elapsed-time assertions. They will prove that two partitions enter before either partition is released.

E2E tests

Update apps/web/e2e/tests/system/storage-maintenance.spec.ts for progressive cold-scan values, stale refresh, and pointer or keyboard timing disclosure.

Update apps/web/e2e/tests/system/mobile-storage-maintenance.spec.ts for tap disclosure, a 44-pixel trigger, progressive values, and no horizontal overflow.

Both flows map to AC-SYSTEM-PAGE-STORAGE-OVERVIEW-PARALLEL-SCAN-002.2 through .8.

Mobile design contract

Desktop and phone users enter through /settings/system/storage. Both receive the same snapshot, progress, and timing data.

The existing Storage page and StorageSettingHelp are the nearest shipped examples. They provide the one-column card flow and the hover, focus, and pinned-tap help interaction.

Storage analysis remains an inline card in the page scroll. No drawer or full-height surface is necessary because the timing content contains three short values.

The information trigger will use the existing 44-pixel phone hit area and compact desktop size. The page keeps one scroll owner and no horizontal overflow.

Business state, progress derivation, and refresh scheduling remain shared across viewports. Only the responsive trigger size differs.

Work orders

Dependency order

  1. Task 01 creates the scan and progress primitives.
  2. Task 02 uses those primitives for the cache, API, events, and observability.
  3. Task 03 consumes the backend contract and updates public operator documentation.
  4. Task 04 proves the integrated desktop and phone behavior.

All tasks are sequential. They share contracts, fixtures, and storage test files.

Verification results

  • Task 01: go test -race ./internal/system/storage/filescan ./internal/system/storage/workspaces ./internal/system/storage/gocache ./internal/system/storage/tempartifacts ./internal/backendapp passed (853 tests).
  • Task 01 benchmark: go test ./internal/system/storage/filescan -run '^$' -bench 'BenchmarkMeasureTrees' -benchmem passed at 81,604 ns/op and 11,367 B/op.
  • Task 02: go test -race ./internal/system/storage ./internal/backendapp ./internal/gateway/websocket passed (1,423 tests).
  • Task 03: the focused web suite passed (46 tests), the extracted terminal-refresh suite passed (2 tests), typecheck, lint, i18n checks, and public-doc validation passed.
  • Task 04: make build-web, make build-backend, Chromium storage E2E (8 tests), and mobile Chromium storage E2E (6 tests) passed.
  • Changed-file Go lint passed with golangci-lint run ./... --new-from-rev=481359ddb282fd7d3e8c9cdff99da6f9bdd24c7c --timeout=5m.
  • Specification lint passed with python3 scripts/lint-spec-files.py --all.

Risks

  • Parallel directory reads can increase contention on rotating disks. The shared four-walker limit bounds this risk, and timing evidence will record regressions.
  • Partial results can look final. The API separates them from completed snapshots, and the UI marks aggregates as counted so far.
  • Late progress can corrupt a new scan. Generation checks must guard every progress and completion write.
  • Broadcast events can disclose storage facts. The event carries only generation and state.
  • A missed event can leave stale UI. The active-scan poll provides recovery.
  • A page timer can drift after sleep. The timer callback reads the server state instead of assuming that a scan started.