Skip to content

feat(persistence): add LRU draft cache with quota management#8518

Merged
christian-byrne merged 3 commits intomainfrom
01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_
Feb 20, 2026
Merged

feat(persistence): add LRU draft cache with quota management#8518
christian-byrne merged 3 commits intomainfrom
01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Feb 1, 2026

Summary

Adds an LRU (Least Recently Used) cache layer and storage I/O utilities that handle localStorage quota limits gracefully. When storage is full, the oldest drafts are automatically evicted to make room for new ones.

Changes

  • What:
    • draftCacheV2.ts - In-memory LRU cache with configurable max entries (default 32)
    • storageIO.ts - Storage read/write with automatic quota management and eviction
  • Why: Users experience QuotaExceededError when localStorage fills up with workflow drafts, breaking auto-save functionality

Review Focus

  • LRU eviction logic in draftCacheV2.ts
  • Quota error handling and recovery in storageIO.ts

Part 2 of 4 in the workflow persistence improvements stack

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Walkthrough

Adds a V2 immutable draft cache (LRU semantics), a V2 storage I/O module for localStorage/sessionStorage, comprehensive Vitest tests for both, and a minor config/comment update adding a knip ignore tag.

Changes

Cohort / File(s) Summary
Configuration / Types
knip.config.ts, src/platform/workflow/persistence/base/draftTypes.ts
Added knip ignore tag -knipIgnoreUsedByStackedPR to knip.config.ts; replaced a comment in draftTypes.ts referencing the tag (documentation/comment-only edit).
Draft Cache V2 (impl + tests)
src/platform/workflow/persistence/base/draftCacheV2.ts, src/platform/workflow/persistence/base/draftCacheV2.test.ts
New immutable DraftIndexV2 implementation (createEmptyIndex, touchOrder, upsertEntry, removeEntry, moveEntry, getMostRecentKey, getEntryByPath, removeOrphanedEntries) with path hashing and LRU eviction; comprehensive tests covering insertion, update, eviction, moves, removals, and edge cases.
Storage I/O V2 (impl + tests)
src/platform/workflow/persistence/base/storageIO.ts, src/platform/workflow/persistence/base/storageIO.test.ts
New storage I/O utilities for V2: availability flag, read/write/delete index and payloads, enumerate payload keys, delete orphan payloads, session pointers for active/open paths, clearAllV2Storage; tests cover index/payload ops, pointers, orphan cleanup, and full clearance.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Cache as draftCacheV2
    participant Storage as storageIO
    participant LS as localStorage

    App->>Storage: readIndex(workspaceId)
    Storage->>LS: getItem(indexKey)
    LS-->>Storage: JSON / null
    Storage-->>App: DraftIndexV2 | null

    App->>Cache: upsertEntry(index, path, meta, limit)
    Cache->>Cache: hashPath(path)
    Cache->>Cache: touchOrder(order, key)
    Cache->>Cache: evictIfNeeded(limit)
    Cache-->>App: { index, evicted }

    App->>Storage: writeIndex(workspaceId, index)
    Storage->>LS: setItem(indexKey, JSON.stringify(index))
    LS-->>Storage: success / quota error
    Storage-->>App: boolean

    App->>Storage: deleteOrphanPayloads(workspaceId, indexKeys)
    Storage->>LS: get all payload keys
    Storage->>LS: removeItem(orphanKey) [for each orphan]
    Storage-->>App: numberOfRemoved
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through lines with careful eyes,
New drafts that learn to live and rise.
Storage tidy, orders aligned,
Tests ensure none fall behind.
A tiny tag, a joyful prize.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(persistence): add LRU draft cache with quota management' accurately summarizes the main changes—introducing an LRU cache and quota management for draft persistence.
Description check ✅ Passed The description covers the required template sections: Summary explains the purpose, Changes details the core additions, and Review Focus highlights critical areas. All essential information is present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor Author

christian-byrne commented Feb 1, 2026

@github-actions
Copy link

github-actions bot commented Feb 1, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 02/20/2026, 05:58:01 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Feb 1, 2026

Playwright: ✅ 516 passed, 0 failed · 5 flaky

📊 Browser Reports
  • chromium: View Report (✅ 504 / ❌ 0 / ⚠️ 5 / ⏭️ 10)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@christian-byrne christian-byrne changed the title feat(persistence): add V2 cache and storage I/O feat(persistence): add LRU draft cache with quota management Feb 2, 2026
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_infrastructure_1_4_ branch from 86cf743 to 8212e12 Compare February 3, 2026 04:45
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from 436fa0b to c2e62c7 Compare February 3, 2026 04:46
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_infrastructure_1_4_ branch from 8212e12 to 30b3c47 Compare February 3, 2026 20:38
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from c2e62c7 to c5de23a Compare February 3, 2026 20:38
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_infrastructure_1_4_ branch from 30b3c47 to 186a3b9 Compare February 9, 2026 02:33
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from c5de23a to 7c42cd4 Compare February 9, 2026 02:33
simula-r
simula-r previously approved these changes Feb 10, 2026
Copy link
Contributor

@simula-r simula-r left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Nice job!

@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from 7c42cd4 to 85a6545 Compare February 18, 2026 23:25
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_infrastructure_1_4_ branch from ba3ec21 to 64fd51d Compare February 18, 2026 23:25
Base automatically changed from 01-31-feat_persistence_add_v2_infrastructure_1_4_ to main February 19, 2026 03:31
@christian-byrne christian-byrne dismissed simula-r’s stale review February 19, 2026 03:31

The base branch was changed.

@github-actions
Copy link

github-actions bot commented Feb 20, 2026

📦 Bundle: 4.26 MB gzip ⚪ 0 B

Details

Summary

  • Raw size: 20 MB baseline 20 MB — ⚪ 0 B
  • Gzip: 4.26 MB baseline 4.26 MB — ⚪ 0 B
  • Brotli: 3.3 MB baseline 3.3 MB — ⚪ 0 B
  • Bundles: 219 current • 219 baseline

Category Glance
Vendor & Third-Party ⚪ 0 B (8.69 MB) · Other ⚪ 0 B (7.38 MB) · Data & Services ⚪ 0 B (2.4 MB) · Graph Workspace ⚪ 0 B (914 kB) · Panels & Settings ⚪ 0 B (430 kB) · Views & Navigation ⚪ 0 B (68.6 kB) · + 5 more

App Entry Points — 21.4 kB (baseline 21.4 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-BtWTqsaw.js 21.4 kB 21.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Graph Workspace — 914 kB (baseline 914 kB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BD9SOPK1.js 914 kB 914 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Views & Navigation — 68.6 kB (baseline 68.6 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudAuthTimeoutView-BnUtrlqh.js 4.88 kB 4.88 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudForgotPasswordView-BO2JpwAD.js 5.53 kB 5.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudLayoutView-FZVJheTE.js 6.4 kB 6.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudLoginView-DlL7sOh4.js 10 kB 10 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudSignupView-DUjxXSG6.js 7.38 kB 7.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudSorryContactSupportView-D9UxxFTg.js 1.02 kB 1.02 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudSubscriptionRedirectView-Gv0xsL9P.js 4.68 kB 4.68 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudSurveyView-DRYH6ryv.js 15.5 kB 15.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-Bn5Nxcmc.js 296 B 296 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserCheckView-LVuSGj3I.js 8.41 kB 8.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserSelectView-DmHiviWL.js 4.5 kB 4.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Panels & Settings — 430 kB (baseline 430 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/AboutPanel-BtqaTW3w.js 8.53 kB 8.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/cloudRemoteConfig-CJ4CDE9Y.js 1.41 kB 1.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/config-C5QaqZix.js 996 B 996 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ExtensionPanel-DOORkZJB.js 9.35 kB 9.35 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/KeybindingPanel-C_5Vku7-.js 12.3 kB 12.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LegacyCreditsPanel-DfyCG7CC.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/refreshRemoteConfig-DC-SJU4x.js 1.14 kB 1.14 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SecretsPanel-DwlvPZX5.js 21.5 kB 21.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ServerConfigPanel-BlIDd3hn.js 6.41 kB 6.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B0mOvnTo.js 32 kB 32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BcrCrxYO.js 27.4 kB 27.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BdMQ9yxb.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BMpJMaIw.js 33.7 kB 33.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C-rh-es6.js 30.1 kB 30.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CgiUGcth.js 23.6 kB 23.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CzjpXwlp.js 38 kB 38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CzpC_oh3.js 28.4 kB 28.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dp3u6l4N.js 29.5 kB 29.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DZjzU5N0.js 28.4 kB 28.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-rQc1NwAI.js 27.6 kB 27.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionPanel-BbL2skun.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserPanel-C3Jde4DB.js 6.13 kB 6.13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
User & Accounts — 16 kB (baseline 16 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-Ch5QGsX-.js 357 B 357 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/auth-CKLXCr7I.js 3.4 kB 3.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/firebaseAuthStore-qCdAm4pT.js 758 B 758 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/PasswordFields-ChCYPNPm.js 4.51 kB 4.51 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SignUpForm-ne7JPefJ.js 3.01 kB 3.01 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UpdatePasswordContent-qmazMGgN.js 2.34 kB 2.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WorkspaceProfilePic-DwMbocAN.js 1.57 kB 1.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Editors & Dialogs — 706 B (baseline 706 B) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CyItR4Ez.js 706 B 706 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
UI Components — 42.3 kB (baseline 42.3 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Button-CC5RleXn.js 2.98 kB 2.98 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-OjhGOi8-.js 1.24 kB 1.24 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/cloudFeedbackTopbarButton-BpqTgxt4.js 1.56 kB 1.56 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyQueueButton-BRWR-muM.js 763 B 763 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyQueueButton-cOb8247v.js 7.17 kB 7.17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ScrubableNumberInput-DabUIV79.js 5.96 kB 5.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscribeButton-B2stgrS0.js 2.35 kB 2.35 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-VWWyqurE.js 7.45 kB 7.45 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-DsMH6rUM.js 1.17 kB 1.17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useTerminalTabs-6aysGxj7.js 9.81 kB 9.81 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-DwYqMhCJ.js 1.84 kB 1.84 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Data & Services — 2.4 MB (baseline 2.4 MB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/api-QGtyGNO8.js 648 kB 648 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/audioService-uHAeRyK7.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/bootstrapStore-K2CXw5-5.js 2.08 kB 2.08 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/dialogService-BJ8CgYOs.js 695 B 695 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/dialogService-CyHDGI9s.js 1.62 MB 1.62 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/keybindingService-BpYwtN6b.js 6.52 kB 6.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/load3dService-4uvZy3F8.js 90.9 kB 90.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/releaseStore-CRLa52Hg.js 730 B 730 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/releaseStore-nKFBiRjS.js 7.96 kB 7.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/serverConfigStore-DWKub1Pu.js 2.32 kB 2.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settingStore-Bg7-kvKY.js 714 B 714 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/systemStatsStore-Bif9zb9o.js 12.2 kB 12.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/userStore-DVkPm28v.js 1.85 kB 1.85 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/workflowDraftStore-GdOG_5i2.js 706 B 706 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
Utilities & Hooks — 57.6 kB (baseline 57.6 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/_plugin-vue_export-helper-CY4XIWDa.js 315 B 315 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/audioUtils-eGzaAPVs.js 858 B 858 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-CeP50apf.js 7 kB 7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/envUtil-BQSmRN2Q.js 466 B 466 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-Cu9hIV7U.js 1.56 kB 1.56 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SkeletonUtils-Dbobu-mZ.js 133 B 133 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/subscriptionCheckoutUtil-Dht0M5G3.js 2.53 kB 2.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useCurrentUser-sGDawnsL.js 692 B 692 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useErrorHandling-BIQP3N7P.js 1.5 kB 1.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useExternalLink-CT2LWObJ.js 1.66 kB 1.66 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useFeatureFlags-DRaxmk3F.js 3.5 kB 3.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useLoad3d-BLK6tKql.js 14.6 kB 14.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useLoad3d-D9Xrkmwo.js 829 B 829 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useLoad3dViewer-CP_g29-u.js 808 B 808 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useLoad3dViewer-DGVSLHxP.js 14.1 kB 14.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useSubscriptionCredits-D6A6yf1L.js 2.75 kB 2.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useWorkspaceSwitch-C3vO0Yet.js 1.25 kB 1.25 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useWorkspaceUI-BIs5N5q9.js 3 kB 3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Vendor & Third-Party — 8.69 MB (baseline 8.69 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-axios-C4mPrLmU.js 70.3 kB 70.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-chart-BVph5xqx.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-firebase-BvMr43CG.js 836 kB 836 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-i18n-cR3vmlFu.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-markdown-oliHT-H5.js 102 kB 102 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-DJyFg6na.js 1.52 MB 1.52 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-gefG45vj.js 1.73 MB 1.73 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-DAi_xVZa.js 255 kB 255 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-sentry-SQwstEKc.js 182 kB 182 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-DSpQy18i.js 1.8 MB 1.8 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-Bi_34iZD.js 625 kB 625 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-core-BjA-tjXK.js 311 kB 311 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vueuse-DcEOrMQz.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-C4cqihSk.js 374 kB 374 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-yjs-CP_4YO8u.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-zod-DcCUUPIi.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.38 MB (baseline 7.38 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/AnimationControls-BzKwvrey.js 4.61 kB 4.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ApiNodesSignInContent-DPS3_zY7.js 2.69 kB 2.69 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/AudioPreviewPlayer-DLXYlsmA.js 10.8 kB 10.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/auto-P6cmVFBq.js 1.7 kB 1.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/BaseViewTemplate-CPWbq97h.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CancelSubscriptionDialogContent-BSDXF90p.js 4.76 kB 4.76 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/changeTracker-CHo4arbo.js 9.38 kB 9.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/changeTracker-CIyypa7g.js 727 B 727 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/cloudBadges-Cj45cMR6.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudRunButtonWrapper-DNg-RfDb.js 1.65 kB 1.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/cloudSessionCookie-D7bVyLWE.js 3.07 kB 3.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/cloudSubscription-DJhON_2p.js 1.3 kB 1.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/comfy-logo-single-Bzuu84gN.js 198 B 198 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyOrgHeader-CR3OyyCW.js 910 B 910 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-6ntdtqc9.js 16.6 kB 16.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B4RbG9Th.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BmIbopvA.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Br2FiHkj.js 17.1 kB 17.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BrlWvwVz.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV_NDvWz.js 15.5 kB 15.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CSxUmE31.js 14.7 kB 14.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-D3gGGUgv.js 16.3 kB 16.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DBqn--Z9.js 15.8 kB 15.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DgX9pWvG.js 15.8 kB 15.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DlSpFU8-.js 18.4 kB 18.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/constants-BKU8e69-.js 579 B 579 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/core-DX3V6Vwo.js 72.3 kB 72.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CreateWorkspaceDialogContent-B8-U8Tjt.js 5.5 kB 5.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CurrentUserPopoverWorkspace-Cu4uTtfo.js 19.8 kB 19.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/DeleteWorkspaceDialogContent-DTIOxdtx.js 4.2 kB 4.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/EditWorkspaceDialogContent-D5NszCh5.js 5.3 kB 5.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/GlobalToast-DmDwDpJE.js 2.91 kB 2.91 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/graphHasMissingNodes-9Xe1ko4t.js 761 B 761 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/groupNode-CdpDJNQV.js 72.1 kB 72.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-B21P2kuA.js 199 B 199 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-BPsuwzFt.js 505 kB 505 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/InviteMemberDialogContent-T1h71iE9.js 7.35 kB 7.35 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/InviteMemberUpsellDialogContent-CJ4m3jv8.js 3.79 kB 3.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-C812ZTaN.js 12.3 kB 12.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LeaveWorkspaceDialogContent-svL4U2f2.js 4.03 kB 4.03 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3D-65sEo9ac.js 1.04 kB 1.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/load3d-D27OscDv.js 14.7 kB 14.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3D-PkaGex_e.js 16.2 kB 16.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3DConfiguration-C_FbZxHD.js 6.27 kB 6.27 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3DControls-PpKMGGVl.js 30.9 kB 30.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3dViewerContent-CGsVBAzC.js 963 B 963 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Load3dViewerContent-jA36aSpC.js 23 kB 23 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bg-1LcRk.js 120 kB 120 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BhTuc8pp.js 144 kB 144 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BjNyGyrP.js 136 kB 136 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bl8Fy3rY.js 141 kB 141 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bo3hm2ug.js 190 kB 190 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C-XiaTzV.js 157 kB 157 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D-LaWT4q.js 121 kB 121 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DkPPQYpL.js 170 kB 170 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-paKyiK5-.js 137 kB 137 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-wXc1MGIE.js 139 kB 139 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-XEp46eGp.js 164 kB 164 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-uVW6UxXn.js 1.82 kB 1.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-Bo-yoCQs.js 1.43 kB 1.43 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-BWaFQR3Q.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-_lQOgDGQ.js 2.23 kB 2.23 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nightlyBadges-93FGyyg2.js 971 B 971 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bheu43Bb.js 391 kB 391 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BPuceShu.js 346 kB 346 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BvgDpig6.js 377 kB 377 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BZpu0NoU.js 463 kB 463 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cev_L1ap.js 381 kB 381 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CGC_NG3k.js 369 kB 369 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cj5X8IGf.js 343 kB 343 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DAEaB4hJ.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DJG4rq9g.js 377 kB 377 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DJl7ITPK.js 424 kB 424 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dywtk58C.js 424 kB 424 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeTemplates-RuUI3reo.js 9.27 kB 9.27 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Preview3d-CiG4atZD.js 4.78 kB 4.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-CxcvRxyJ.js 665 B 665 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/RemoveMemberDialogContent-DQ65ACEn.js 4.01 kB 4.01 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/RevokeInviteDialogContent-C2U-LaBC.js 3.92 kB 3.92 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-DLICfi3-.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/saveMesh-DkZXfjZB.js 3.35 kB 3.35 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SelectValue-CKdTjksa.js 8.94 kB 8.94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SignInContent-BlHqlNk5.js 18.9 kB 18.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/signInSchema-FyXu3mPx.js 1.53 kB 1.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-D_ngVjVT.js 3.52 kB 3.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/src-hEkx01Ix.js 251 B 251 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscribeToRun-Dfg6v1UV.js 2.2 kB 2.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionPanelContentWorkspace-D5AIKzc8.js 900 B 900 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionPanelContentWorkspace-vUVzpZ5Z.js 21.6 kB 21.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionRequiredDialogContent-CcvSqRVo.js 26.2 kB 26.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionRequiredDialogContentWorkspace-UCS4eMeF.js 45.8 kB 45.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/telemetry-zZf2dHJ2.js 226 B 226 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/types-DT3N7am7.js 204 B 204 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ValueControlPopover-BpjtO6Cu.js 4.89 kB 4.89 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-DTUjK0ZE.js 445 B 445 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-CXf2UpG7.js 3.19 kB 3.19 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-DnTyITVB.js 283 B 283 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-B9WHn-7I.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-LYMr5zb-.js 2.9 kB 2.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-Daz0soUA.js 3.61 kB 3.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-Ds3saEb3.js 3.1 kB 3.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCrop-D2UL07cA.js 22.1 kB 22.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputNumber-6WozkObW.js 11.8 kB 11.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputNumber-BnIg99dg.js 437 B 437 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-DTX5OxDV.js 1.86 kB 1.86 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-CDiEG33G.js 1.95 kB 1.95 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLegacy-CNPwmjel.js 715 B 715 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-CYq8CHI1.js 2.88 kB 2.88 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-DfMXJwGW.js 1.1 kB 1.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetRecordAudio-CScDSvtT.js 17.3 kB 17.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetSelect-C2CP_nAQ.js 57.8 kB 57.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-BCCY3me8.js 3.18 kB 3.18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-BfjjyC7J.js 2.5 kB 2.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-Cp8f93Pk.js 393 B 393 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetWithControl-C4n-nBbv.js 7.01 kB 7.01 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WorkspacePanelContent-c5XpAquS.js 29.2 kB 29.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/platform/workflow/persistence/base/draftCacheV2.ts`:
- Around line 108-143: moveEntry currently overwrites any existing entry at
newPath; add a collision guard that detects if entries[newKey] already exists
and surface that to callers instead of silently replacing it: inside moveEntry
(use symbols oldKey, newKey, entries, oldEntry) check const existing =
entries[newKey] before deleting oldKey; if existing and existing !== oldEntry
set a replacedKey variable (or return null if you prefer rejection) and include
replacedKey in the returned object so callers can evict/merge payloads
explicitly; ensure you still remove oldKey and update order (filter only oldKey,
avoid duplicating newKey) and set updatedAt as before.

In `@src/platform/workflow/persistence/base/storageIO.ts`:
- Around line 129-164: getPayloadKeys and deleteOrphanPayloads currently
enumerate localStorage without guarding against environments that block storage
access; wrap the enumeration in a defensive availability check and try/catch so
it returns an empty array (for getPayloadKeys) or zero deletions (for
deleteOrphanPayloads) instead of throwing. Concretely, in getPayloadKeys (and
the similar clearAllV2Storage routine) first check a storage-availability helper
if one exists (e.g., isStorageAvailable) or wrap the for-loop/localStorage calls
in try { ... } catch (e) { return []; } and in deleteOrphanPayloads handle the
case where getPayloadKeys returns an empty array (or propagate 0) so deletion is
skipped safely; ensure any calls to deletePayload are only invoked when storage
is accessible.

@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from f3fe2a7 to 2ad08d6 Compare February 20, 2026 04:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/platform/workflow/persistence/base/draftCacheV2.test.ts`:
- Line 15: Replace string literal suite titles with function/identifier
references to satisfy vitest/prefer-describe-function-title: change
describe('draftCacheV2', ...) to describe(draftCacheV2, ...), and any inner
suites like describe('createEmptyIndex', ...) and describe('touchOrder', ...) to
describe(createEmptyIndex, ...) and describe(touchOrder, ...); ensure the
referenced symbols (draftCacheV2, createEmptyIndex, touchOrder, etc.) are
imported or available in the test file so the describe calls receive the actual
function/identifier instead of a string.

---

Duplicate comments:
In `@src/platform/workflow/persistence/base/draftCacheV2.ts`:
- Around line 108-143: moveEntry currently overwrites an existing entry when
newPath hashes to an existing key; add a collision guard at the top of
moveEntry: compute oldKey/newKey as now, and if newKey !== oldKey and
index.entries[newKey] exists, return null (or alternately throw) to prevent
silent overwrite; update callers (usages of moveEntry) to handle the null return
(run the suggested rg search to find call sites) and add tests for the collision
case.

In `@src/platform/workflow/persistence/base/storageIO.ts`:
- Around line 132-143: Guard storage enumeration in getPayloadKeys (and
similarly in deleteOrphanPayloads) by first checking storage availability and
wrapping any localStorage/sessionStorage access and loops in try/catch;
specifically, when iterating over localStorage keys using
StorageKeys.prefixes.draftPayload and localStorage.key(i), catch exceptions and
return an empty array (or no‑op for deletions) so the utilities degrade safely
in restricted environments instead of throwing.

@christian-byrne christian-byrne marked this pull request as ready for review February 20, 2026 05:32
@christian-byrne christian-byrne requested a review from a team as a code owner February 20, 2026 05:32
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Feb 20, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/platform/workflow/persistence/base/draftCacheV2.ts (1)

55-60: Prefer immutable eviction/removal (avoid in-place mutation).

order.shift() and delete entries[...] mutate local state. To align with the immutability guideline, consider deriving trimmed order and entries via non-mutating transforms (slice/filter/object rest) instead.

As per coding guidelines: Avoid mutable state; prefer immutability and assignment at point of declaration.

Also applies to: 89-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/persistence/base/draftCacheV2.ts` around lines 55 - 60,
The loop mutates local state using order.shift() and delete entries[oldest];
instead, create new immutable versions: compute a trimmedOrder (e.g., with slice
or filter excluding evicted keys and draftKey) and a newEntries object built via
object spread/selection that omits evicted keys rather than using delete; update
the variables order and entries by reassigning these new immutable values (also
apply the same approach for the removal at the later place that uses delete on
entries), referencing the same variables order, entries, evicted, and draftKey
in the eviction logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/platform/workflow/persistence/base/storageIO.ts`:
- Around line 58-63: The quota-detection in the catch blocks only checks for
error.name === 'QuotaExceededError', which misses Firefox's
NS_ERROR_DOM_QUOTA_REACHED (error.code === 1014); update the catch logic in both
writeIndex and writePayload to treat quota errors as false by checking for a
DOMException whose name is 'QuotaExceededError' OR 'NS_ERROR_DOM_QUOTA_REACHED'
and/or whose code equals 1014 (and include legacy numeric codes like 22 if
desired), then return false; otherwise rethrow the error.

---

Duplicate comments:
In `@src/platform/workflow/persistence/base/draftCacheV2.test.ts`:
- Around line 15-37: Update the test suites to use function/identifier
references instead of string literals: replace describe('createEmptyIndex', ...)
with describe(createEmptyIndex, ...) and replace describe('touchOrder', ...)
with describe(touchOrder, ...); keep the inner it blocks and expectations the
same so only the describe titles change to satisfy the
vitest/prefer-describe-function-title lint rule (referencing the
createEmptyIndex and touchOrder symbols).

---

Nitpick comments:
In `@src/platform/workflow/persistence/base/draftCacheV2.ts`:
- Around line 55-60: The loop mutates local state using order.shift() and delete
entries[oldest]; instead, create new immutable versions: compute a trimmedOrder
(e.g., with slice or filter excluding evicted keys and draftKey) and a
newEntries object built via object spread/selection that omits evicted keys
rather than using delete; update the variables order and entries by reassigning
these new immutable values (also apply the same approach for the removal at the
later place that uses delete on entries), referencing the same variables order,
entries, evicted, and draftKey in the eviction logic.

christian-byrne and others added 3 commits February 19, 2026 21:55
Add core V2 logic for draft management:

- draftCacheV2: Pure functions for index manipulation (LRU ordering, entry
  management, orphan cleanup)
- storageIO: localStorage/sessionStorage read/write with error handling,
  quota management, and workspace-scoped operations

These modules are not yet wired to the app - they provide the foundation
for the store rewrite in the next commit.

Amp-Thread-ID: https://ampcode.com/threads/T-019c16f4-05a2-779d-aa0e-a0e098308a95
Co-authored-by: Amp <amp@ampcode.com>
@christian-byrne christian-byrne force-pushed the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch from b107ee5 to 4d6cd54 Compare February 20, 2026 05:56
@christian-byrne christian-byrne merged commit 351d43a into main Feb 20, 2026
28 checks passed
@christian-byrne christian-byrne deleted the 01-31-feat_persistence_add_v2_cache_and_storage_i_o_2_4_ branch February 20, 2026 06:04
huntcsg pushed a commit that referenced this pull request Feb 21, 2026
## Summary

Adds an LRU (Least Recently Used) cache layer and storage I/O utilities
that handle localStorage quota limits gracefully. When storage is full,
the oldest drafts are automatically evicted to make room for new ones.

## Changes

- **What**: 
- `draftCacheV2.ts` - In-memory LRU cache with configurable max entries
(default 32)
- `storageIO.ts` - Storage read/write with automatic quota management
and eviction
- **Why**: Users experience `QuotaExceededError` when localStorage fills
up with workflow drafts, breaking auto-save functionality

## Review Focus

- LRU eviction logic in `draftCacheV2.ts`
- Quota error handling and recovery in `storageIO.ts`

---
*Part 2 of 4 in the workflow persistence improvements stack*

---------

Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants