7.9 KiB
7.9 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/system-page/requirements/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, registerGET /api/v1/system/storage/settingsbeside the existingPATCHroute. - Implement
getSettingsby callingSettingsManager.GetSettingsandOverviewReader.SettingsCapabilities; return{ settings, capabilities }without callingOverviewReader.Get,RunLister.ListRuns, or any analysis provider. - Keep
GET /api/v1/system/storageunchanged so existing clients still receive{ settings, capabilities, summary, analyzed_at, last_run }. - Cover the route directly in
apps/backend/internal/system/storage/handler_test.gowith a recording overview reader that fails the test ifGetis invoked, and through the composed System router inapps/backend/internal/system/storage_routes_test.go.
Frontend
API and state contracts
- Add
StoragePolicyResponsetoapps/web/lib/types/system.tsandfetchStoragePolicy()toapps/web/lib/api/domains/system-api.tsfor the new lightweight GET. - Add
policy: StoragePolicyResponse | nullandsetSystemStoragePolicyto the System Zustand slice inapps/web/lib/state/slices/system/types.tsandsystem-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.tsso 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 onpolicyLoading, 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.tsxto derive drafts and scheduling copy frompolicy, and pass section loading/error state to the analysis, history, and quarantine cards. - Give
storage-overview-card.tsx,storage-run-history.tsx, andstorage-quarantine-card.tsxdistinct 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.tswith 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
partialflag when a top-level measurement is unavailable or missing; and - sum
size_bytesacross every listed quarantine entry.
- Render a localized Total counted value and partial indicator in
storage-overview-card.tsx, and a localized total instorage-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.goverifies the response and proves no overview scan or run-history read occurs. - Composed route:
apps/backend/internal/system/storage_routes_test.goverifiesGET /api/v1/system/storage/settingsis registered and returns200. - API and store:
apps/web/lib/api/domains/system-api.test.tsandapps/web/lib/state/slices/system/system-slice.test.tsverify the new policy contract. - Progressive controller:
apps/web/hooks/domains/system/use-storage-maintenance.test.tsxuses 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.tsverifies 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, andstorage-quarantine-card.test.tsxverify policy composition, loading/error/empty distinctions, and localized totals.
E2E Tests
- Desktop progressive load: extend
apps/web/e2e/tests/system/storage-maintenance.spec.tsto delayGET /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.tswith the same delayed-analysis outcome onmobile-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:
- Task 03: Storage totals — done
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
DiskUsageand filesystem traversal, but remain isolated from the overview scan and failure-scoped to policy.