- Add cache limit number input with tier cap validation and usage display
- Add segmented control for simplified vs detailed analysis progress (D-06)
- Add segmented control for pause-batch vs continue-item failure behavior (D-11)
- Load cache settings on mount via loadCacheSettings; surface API errors
- Keep existing connection health/reconnect/disconnect UX intact
- All 471 frontend tests pass; no regressions
- Fix getCacheSettings/updateCacheSettings URLs to use /api/cloud/analysis/cache
and /api/cloud/analysis/cache/settings (Rule 1: wrong URLs per Plan 14-03 backend)
- Add loadCacheSettings action: hydrates tierCapBytes, cacheUsage, cacheLimit,
detailedAnalysisProgress, failureBehavior from server without clearing on error
- Add tierCapBytes, cacheUsage, cacheSettingsLoading, cacheSettingsError state
- updateCacheSettings merges full CacheStatusOut response into local state
- Add cache_backed_response_has_no_object_key describe block to CloudFolderOpenPreview.test.js
- Verifies openCloudFile response processed by CloudFolderView never exposes object_key, MinIO paths, or credentials_enc in rendered HTML or API call arguments (T-14-02)
- All 9 tests pass (8 pre-existing + 1 new)
- Add hydrate_and_cache_bytes to services/cloud_cache.py (Plan 06 cache lifecycle)
- Cache-hit path: pin existing entry, read from MinIO, release pin in finally
- Cache-miss path: fetch from provider, store in MinIO, create/reactivate entry, pin during response, release pin in finally
- CacheQuotaExceeded and MinIO failures are non-fatal (bytes returned from provider path)
- evict_lru_entries run after pin release to stay within user cache limit
- Update preview_cloud_file and download_cloud_file in operations.py to call hydrate_and_cache_bytes
- Version key computed from CloudItem metadata (no provider call required for key resolution)
- Shared _make_minio_cache_wrapper helper eliminates duplicate adapter code
- Falls through to direct provider fetch when no CloudItem row found
- Add _make_minio_cache_wrapper helper to operations.py (DRY, shared by preview and download)
- Add 5 new tests to test_cloud_cache.py: cache-miss fetches provider, cache-hit skips provider, pin released on hit, pin released on miss, user isolation
- Add 3 new security tests to test_cloud_security.py: preview response excludes object_key, download response excludes object_key, foreign-user IDOR via download blocked
- Implement services/cloud_analysis_processing.py with full pipeline:
queued -> downloading -> extracting -> classifying -> indexed
- Stale guard: version key comparison fires before provider byte fetch (T-14-13)
- Cache pin lifecycle: pin acquired before bytes used, released in finally block (T-14-12)
- No provider mutation methods called: adapter only used via get_object (T-14-03)
- No local Document row created: classification targets CloudItem directly
- Cooperative cancellation check at job/item level before each stage
- ProcessingResult dataclass with status, cache_entry_id, error_code fields
- Add 4 contract tests: status_transitions, stale_detection, pin_release, no_mutations
- 14-02-SUMMARY.md: documents migration 0007, ORM models, version-key
precedence helper, settings service, and Phase 14 cache functions
- STATE.md: advance to Plan 3 of 9, record P02 metrics and decisions
- cloud_analysis_versioning.py: compute_version_key with version>etag>fingerprint
precedence (D-19); compute_fingerprint helper; content_hash is optional post-
hydration only (D-20); raises ValueError only, no HTTPException
- cloud_analysis_settings.py: get_or_create_user_analysis_settings (upsert on
first access), update_user_analysis_settings with enum validation and tier-capped
cache limit, build_default_settings for API responses; raises ValueError only
- cloud_cache.py: Phase 14 durable cache functions added alongside existing Phase 12
in-memory TTL cache — create_cache_entry, list_cache_entries (owner-scoped),
evict_lru_entries (LRU, skips pin_count>0 and active_job_count>0), increment/
decrement_quota_for_cache (atomic UPDATE pattern); re-exports compute_version_key
from cloud_analysis_versioning so tests have one import site
Implemented write_audit_log calls for open_cloud_file, create_cloud_folder,
and rename_cloud_item in api/cloud/operations.py. Removed xfail markers from
test_cloud_audit.py. All 11 audit tests pass; 360 cloud tests pass total.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CloudFolderRenderedFlow.test.js: mock store uses Vue refs so folderFreshness propagates
reactively to StorageBrowser via CloudFolderView's template binding (D-12 GREEN)
- setBrowseState in mock now updates _mockFolderFreshness so cloud-health-banner appears
when API returns warning freshness (T-13-30 mitigated)
- Added testCloudConnection to API mock so D-13 no-probe assertion can verify it was not called
- beforeEach resets reactive refs for test isolation
- 11 rendered-flow tests pass; 70 total across health/store/view/rendered-flow suites
- D-12: connectionHealth ref as single source for browser compact status and Settings diagnostics
- D-13: handleHealthFailure schedules pendingHealthRetest; navigation never triggers probe
- D-13: testConnection method in store (explicit action only, not navigation side-effect)
- D-14: markReconnecting preserves cached browseItems as stale; markReconnectRefreshPending flag
- D-15: degraded vs requires_reauth health states distinguished in store vocabulary
- D-17: Google Drive scope notice in SettingsCloudTab for all connect/reconnect paths
- StorageBrowser: cloud-health-banner (warning/stale) and requires-reauth prompt with reconnect action
- 59 tests passing: store, Settings, and CloudFolderView health/reconnect suites
Move endpoint (D-07, D-08, D-09, T-13-27, T-13-28, T-13-29):
- Descendant destination detection via cloud_items ancestor-chain walk (D-09)
- Stale provider result marks source folder non-fresh, returns typed stale body (D-07)
- Success: upsert_cloud_item with new parent_ref before returning
- Success: update_folder_state for both source and destination folders
- Success: write_audit_log 'cloud.item_moved' with metadata-only payload
Delete endpoint (D-10, D-11, T-13-29):
- Pre-fetch item kind for D-10 folder-vs-file disclosure
- Success: update_folder_state for parent folder before returning
- Success: write_audit_log 'cloud.item_deleted' with delete_kind metadata
- Response carries is_folder/item_kind for frontend disclosure
- Failed deletes never emit audit rows
Promote xfail markers in test_cloud_audit.py for move and delete audit rows