Files
kandev/docs/plans/storage-progressive-loading/plan.md
T
2026-08-02 20:11:23 +01:00

7.9 KiB

spec, created, status
spec created status
docs/specs/system-page/storage-maintenance.md 2026-08-02 complete

Implementation Plan: Progressive Storage Loading and Totals

Overview

Decouple the lightweight Storage policy read from the scan-backed overview, then let the frontend publish policy, history, quarantine, and analysis results independently. Add derived totals for the analysis snapshot and current quarantine list, with explicit partial semantics when a top-level measurement is unavailable. Preserve the existing GET /storage response for compatibility.

Backend

Lightweight policy read

  • In apps/backend/internal/system/storage/handler.go, register GET /api/v1/system/storage/settings beside the existing PATCH route.
  • Implement getSettings by calling SettingsManager.GetSettings and OverviewReader.SettingsCapabilities; return { settings, capabilities } without calling OverviewReader.Get, RunLister.ListRuns, or any analysis provider.
  • Keep GET /api/v1/system/storage unchanged so existing clients still receive { settings, capabilities, summary, analyzed_at, last_run }.
  • Cover the route directly in apps/backend/internal/system/storage/handler_test.go with a recording overview reader that fails the test if Get is invoked, and through the composed System router in apps/backend/internal/system/storage_routes_test.go.

Frontend

API and state contracts

  • Add StoragePolicyResponse to apps/web/lib/types/system.ts and fetchStoragePolicy() to apps/web/lib/api/domains/system-api.ts for the new lightweight GET.
  • Add policy: StoragePolicyResponse | null and setSystemStoragePolicy to the System Zustand slice in apps/web/lib/state/slices/system/types.ts and system-slice.ts.
  • Extend the existing API and slice tests for the route, response shape, and policy update.

Independent loading and refresh

  • Refactor apps/web/hooks/domains/system/use-storage-maintenance.ts so policy, overview, runs, and quarantine requests start together but commit independently. Track per-section loading and error state plus per-section request generations so a slow older response cannot overwrite newer data from the same section.
  • Remove first-load completion from the global pendingAction; policy-save coordination depends on policyLoading, while analysis, history, and quarantine can remain independently pending.
  • Make save refresh only the lightweight policy contract, and apply the existing adoption response directly to policy state. Restore refreshes quarantine without waiting for an overview scan. Terminal analysis/cleanup/delete jobs refresh the affected sections concurrently so history and quarantine can settle even when the invalidated overview needs a new scan.
  • Update storage-maintenance-settings.tsx to derive drafts and scheduling copy from policy, and pass section loading/error state to the analysis, history, and quarantine cards.
  • Give storage-overview-card.tsx, storage-run-history.tsx, and storage-quarantine-card.tsx distinct progress, empty, and error states; a failure in one card must not suppress sibling cards.

Totals

  • Add apps/web/components/settings/system/storage/storage-totals.ts with pure helpers that:
    • sum workspace total, quarantine, managed and distinct user Go caches, managed-container writable layers, Docker image layers, and Docker build cache;
    • exclude active/candidate workspace and unused-image subset bytes to avoid double-counting;
    • return a partial flag when a top-level measurement is unavailable or missing; and
    • sum size_bytes across every listed quarantine entry.
  • Render a localized Total counted value and partial indicator in storage-overview-card.tsx, and a localized total in storage-quarantine-card.tsx.
  • Add the new user-facing strings to apps/web/src/locales/en/settings.json; do not localize identifiers, test IDs, confirmation tokens, or values used in logic.

Mobile design contract

  • Desktop outcome: the existing full-width card stack renders lightweight cards before analysis, and totals appear in the relevant card headers without changing actions or navigation.
  • Mobile entry point and exemplar: keep the Settings sheet → System → Storage path and existing one-column card composition proven by apps/web/e2e/tests/system/mobile-storage-maintenance.spec.ts.
  • Mobile hierarchy: policy, history, and quarantine remain ordinary inline cards in the page's single document scroll; Storage analysis owns only its card-level progress state. No drawer, overlay, fixed control, or second scroll owner is introduced.
  • Touch behavior and state are shared with desktop. Existing action targets stay at least 44px; the totals are read-only and add no touch affordance. The mobile E2E scenario delays analysis, proves the other cards and totals render, and asserts no horizontal document overflow.

Tests

  • Lightweight policy route: apps/backend/internal/system/storage/handler_test.go verifies the response and proves no overview scan or run-history read occurs.
  • Composed route: apps/backend/internal/system/storage_routes_test.go verifies GET /api/v1/system/storage/settings is registered and returns 200.
  • API and store: apps/web/lib/api/domains/system-api.test.ts and apps/web/lib/state/slices/system/system-slice.test.ts verify the new policy contract.
  • Progressive controller: apps/web/hooks/domains/system/use-storage-maintenance.test.tsx uses deferred promises to prove policy, runs, and quarantine publish before overview; covers isolated failures, same-section stale-response protection, and mutation-specific refreshes.
  • Totals: apps/web/components/settings/system/storage/storage-totals.test.ts verifies exact sums, subset exclusion, partial analysis, zero/empty data, and quarantine aggregation.
  • Rendered states: storage-maintenance-settings.test.tsx, storage-overview-card.test.tsx, storage-run-history.test.tsx, and storage-quarantine-card.test.tsx verify policy composition, loading/error/empty distinctions, and localized totals.

E2E Tests

  • Desktop progressive load: extend apps/web/e2e/tests/system/storage-maintenance.spec.ts to delay GET /storage, then verify the policy controls, maintenance history, and quarantine list are visible before analysis finishes; after release, verify the analysis total and quarantine total.
  • Mobile parity: extend apps/web/e2e/tests/system/mobile-storage-maintenance.spec.ts with the same delayed-analysis outcome on mobile-chrome, including totals and absence of horizontal overflow.

Verification Results

Completed. Each task records its exact commands and outcomes in its ## Results section.

Implementation Waves And Parallel Candidates

Wave 1:

Wave 2:

Wave 3:

Wave 4:

The tasks run sequentially in the primary conversation. The waves do not authorize subagents.

Risks

  • Docker unused-image bytes are a reclaimable subset of image-layer usage; adding both would overstate the total. The pure total helper and tests make that exclusion explicit.
  • Cleanup and restore invalidate the overview cache, so an automatic overview refresh may scan again. Section-specific refreshes must let history and quarantine update without awaiting it.
  • Empty arrays currently mean both “not loaded” and “loaded with no items”; explicit section state is required to prevent misleading empty-state copy during requests.
  • Capability reads ping Docker. They are expected to be much cheaper than DiskUsage and filesystem traversal, but remain isolated from the overview scan and failure-scoped to policy.