Skip to content

Fix cloud output deletion without deleting sibling assets - #14493

Closed
dante01yoon wants to merge 6 commits into
mainfrom
fix/ir-91-output-asset-delete
Closed

Fix cloud output deletion without deleting sibling assets#14493
dante01yoon wants to merge 6 commits into
mainfrom
fix/ir-91-output-asset-delete

Conversation

@dante01yoon

@dante01yoon dante01yoon commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix IR-91 without widening an individual output deletion into a job-wide deletion.

A cloud-generated card in the Assets sidebar is history-backed, while Load Image options are asset-record-backed. Deleting only history caused the original stale Load Image option. The earlier implementation in this PR went too far in the other direction: it deleted every asset linked to the job and then deleted history, so deleting one output from a multi-output job removed its siblings.

This revision resolves the selected output to one exact asset UUID, deletes only that asset record, and preserves both sibling assets and the job/history record.

  • Datadog incident: IR-91
  • Follow-up architecture work: FE-1442

Root cause

The two UI surfaces currently read from different sources:

  • Assets sidebar generated cards: completed job/history data
  • Load Image generated options: output-tagged /api/assets records

Consequently, a history-only deletion cannot remove the Load Image option, while a job-wide cleanup destroys unrelated outputs.

AS IS

flowchart TD
  A[User deletes one generated output] --> B[Delete the history job]
  B --> C[Assets sidebar card disappears]
  B --> D[Output asset record remains]
  D --> E[Deleted output still appears in Load Image]
Loading

The first version of this PR replaced the stale-record bug with a broader destructive flow:

flowchart TD
  A[User deletes one generated output] --> B[List every asset for the job]
  B --> C[Delete every linked asset]
  C --> D[Delete the history job]
  C --> E[Sibling outputs are also removed]
Loading

TO BE

flowchart TD
  A[User deletes one cloud-generated output] --> B[Read its job ID and content hash]
  B --> C[Fetch every page from GET /api/jobs/:jobId/assets]
  C --> D{Exactly one hash match and optional name is consistent?}
  D -- Yes --> E[Resolve the exact asset UUID]
  E --> F[DELETE /api/assets/:assetId]
  F --> G[Refresh history and flat-output stores]
  G --> H[Selected Load Image option disappears]
  G --> I[Sibling assets remain]
  G --> J[Job and history remain]
  D -- No or endpoint failure --> K[Fail closed]
  K --> L[Delete no sibling asset and no history]
Loading

Implementation

  • Resolve history-backed outputs through GET /api/jobs/{jobId}/assets using the selected output's content hash.
  • Read the full paginated result before deleting anything.
  • Require a stable pagination total and exactly one hash match.
  • Treat display_name as an additional consistency check, never as permission to choose between duplicate hashes.
  • Fail closed on 404, malformed pagination, changing pagination totals, ambiguous duplicate hashes, or missing matches.
  • Delete only the resolved /api/assets/{assetId} record.
  • Never call /api/history for an individual cloud output deletion.
  • Serialize bulk deletions so one deletion cannot shift another lookup's offset pages in the same operation.
  • Preserve existing OSS behavior, which remains history-backed and uses history deletion.
  • Refresh both history and flat-output stores and clear only successfully deleted widget/preview references.

Scope and known limitation

This PR fixes the IR-91 Load Image inconsistency and prevents sibling loss. It does not migrate the Assets sidebar to asset records.

Because the generated sidebar card is still reconstructed from the preserved history job, the deleted output can remain visible in the sidebar even though its asset record and Load Image option are gone. FE-1442 is the follow-up required to make the sidebar asset-backed (or to provide a backend per-output history mutation) so the card itself can disappear while the job and surviving outputs remain.

Also out of scope:

  • cleanup/backfill of historical orphaned asset records;
  • rewriting existing job output payloads;
  • changing the backend identity contract for outputs with identical content.

Regression coverage

The browser regression now models a two-output job and verifies:

  • only the selected UUID receives DELETE /api/assets/:assetId;
  • the sibling UUID is not deleted;
  • /api/history is not called;
  • the selected output disappears from Load Image;
  • the sibling remains available;
  • the history-backed job/card remains.

Unit coverage additionally verifies pagination, unstable pagination rejection, duplicate-hash ambiguity, optional filename consistency, endpoint unavailability, repeated DELETE idempotency, cloud/OSS routing, and store refresh behavior.

Visual evidence

AS IS

Before deletion, the generated output appears in the real Load Image picker.

AS IS - generated output present in Load Image

TO BE (single-output visual flow)

After deleting the only output, the real Load Image picker no longer contains it. For a multi-output job, only the selected option is removed and siblings remain.

TO BE - selected generated output removed from Load Image

Existing real-app flow

Delete generated output and verify Load Image refresh

The visual capture uses the real app UI. API responses are mocked, so it demonstrates the user-facing flow rather than live backend deletion semantics.

Validation

  • pnpm test:unit src/platform/assets/composables/useMediaAssetActions.test.ts src/platform/assets/services/assetService.test.ts — 122 tests passed
  • pnpm typecheck — passed
  • pnpm typecheck:browser — passed
  • type-aware oxlint on all modified files — passed
  • oxfmt --check on all modified files — passed
  • pre-push knip --cache — passed
  • git diff --check — passed

The updated two-output Playwright regression is committed, but a standalone local Vite run could not reach the assertion because the cloud fixture setup requires the backend /api/users endpoint. This PR therefore proves frontend request selection and state behavior through unit tests and typed browser mocks; it does not claim live-backend E2E coverage.

@dante01yoon
dante01yoon requested a review from a team July 31, 2026 17:55
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 31, 2026
@dosubot

dosubot Bot commented Jul 31, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-08-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about ComfyUI_frontend Add Dosu to your team

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 08/05/2026, 10:48:26 PM UTC

Links

🎭 Playwright: ✅ 1792 passed, 0 failed

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

📦 Bundle: 8.26 MB gzip 🔴 +769 B

Details

Summary

  • Raw size: 34.7 MB baseline 34.7 MB — 🔴 +4.61 kB
  • Gzip: 8.26 MB baseline 8.26 MB — 🔴 +769 B
  • Brotli: 5.72 MB baseline 5.72 MB — 🔴 +569 B
  • Bundles: 432 current • 432 baseline • 144 added / 144 removed

Category Glance
Data & Services 🔴 +4.61 kB (3.49 MB) · Vendor & Third-Party ⚪ 0 B (15.7 MB) · Other ⚪ 0 B (12.8 MB) · Graph Workspace ⚪ 0 B (1.35 MB) · Panels & Settings ⚪ 0 B (552 kB) · Utilities & Hooks ⚪ 0 B (387 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CoVh_Zdq.js (new) 3.64 kB 🔴 +3.64 kB 🔴 +1.82 kB 🔴 +1.56 kB
assets/index-Dl475you.js (removed) 3.64 kB 🟢 -3.64 kB 🟢 -1.82 kB 🟢 -1.59 kB

Status: 1 added / 1 removed

Graph Workspace — 1.35 MB (baseline 1.35 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-DxFA9g4P.js (new) 1.35 MB 🔴 +1.35 MB 🔴 +294 kB 🔴 +221 kB
assets/GraphView-YolSlfas.js (removed) 1.35 MB 🟢 -1.35 MB 🟢 -294 kB 🟢 -221 kB

Status: 1 added / 1 removed / 1 unchanged

Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-Cf9z4gUX.js (removed) 24.9 kB 🟢 -24.9 kB 🟢 -6.22 kB 🟢 -5.51 kB
assets/CloudSurveyView-JcFbj0jT.js (new) 24.9 kB 🔴 +24.9 kB 🔴 +6.22 kB 🔴 +5.51 kB
assets/CloudLayoutView-0WxPFfh-.js (removed) 21.8 kB 🟢 -21.8 kB 🟢 -6.56 kB 🟢 -5.74 kB
assets/CloudLayoutView-BxlOsXcO.js (new) 21.8 kB 🔴 +21.8 kB 🔴 +6.56 kB 🔴 +5.73 kB
assets/UserCheckView-CUQH0uc8.js (new) 8.75 kB 🔴 +8.75 kB 🔴 +2.19 kB 🔴 +1.9 kB
assets/UserCheckView-VBToyH8Q.js (removed) 8.75 kB 🟢 -8.75 kB 🟢 -2.19 kB 🟢 -1.9 kB
assets/CloudLoginView-BiOUr_A-.js (new) 8.74 kB 🔴 +8.74 kB 🔴 +2.55 kB 🔴 +2.26 kB
assets/CloudLoginView-CaZcggTR.js (removed) 8.74 kB 🟢 -8.74 kB 🟢 -2.55 kB 🟢 -2.26 kB
assets/useCloudAuthPage-DOZWvYZX.js (new) 7.08 kB 🔴 +7.08 kB 🔴 +2.51 kB 🔴 +2.21 kB
assets/useCloudAuthPage-Ivr1D65z.js (removed) 7.08 kB 🟢 -7.08 kB 🟢 -2.51 kB 🟢 -2.2 kB
assets/CloudSignupView-CqkGWrAC.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +2.17 kB 🔴 +1.91 kB
assets/CloudSignupView-PpTciaIL.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -2.17 kB 🟢 -1.9 kB
assets/WidgetTextPreview-C_EUPTd7.js (removed) 6.07 kB 🟢 -6.07 kB 🟢 -2.13 kB 🟢 -1.88 kB
assets/WidgetTextPreview-C6HyWNvs.js (new) 6.07 kB 🔴 +6.07 kB 🔴 +2.13 kB 🔴 +1.88 kB
assets/CloudSubscriptionRedirectView-BIzqSVgL.js (removed) 6.01 kB 🟢 -6.01 kB 🟢 -2.24 kB 🟢 -1.94 kB
assets/CloudSubscriptionRedirectView-D4mKWMCP.js (new) 6.01 kB 🔴 +6.01 kB 🔴 +2.24 kB 🔴 +1.94 kB
assets/UserSelectView-BIN6cpJJ.js (removed) 5.49 kB 🟢 -5.49 kB 🟢 -1.95 kB 🟢 -1.71 kB
assets/UserSelectView-BTgMcGv8.js (new) 5.49 kB 🔴 +5.49 kB 🔴 +1.95 kB 🔴 +1.71 kB
assets/CloudForgotPasswordView-BE0RzO6p.js (removed) 4.97 kB 🟢 -4.97 kB 🟢 -1.72 kB 🟢 -1.49 kB
assets/CloudForgotPasswordView-moppDKt9.js (new) 4.97 kB 🔴 +4.97 kB 🔴 +1.72 kB 🔴 +1.49 kB
assets/CloudAuthTimeoutView-CBJXPhvf.js (new) 4.43 kB 🔴 +4.43 kB 🔴 +1.54 kB 🔴 +1.34 kB
assets/CloudAuthTimeoutView-p_d5xkJz.js (removed) 4.43 kB 🟢 -4.43 kB 🟢 -1.54 kB 🟢 -1.34 kB
assets/OAuthLayoutView-BjM5iCL1.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +698 B 🔴 +596 B
assets/OAuthLayoutView-T988hRew.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -697 B 🟢 -596 B
assets/WidgetTextPreview-CCUsWPr2.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +89 B
assets/WidgetTextPreview-YJ4g1PKx.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -94 B

Status: 13 added / 13 removed / 4 unchanged

Panels & Settings — 552 kB (baseline 552 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-CJ44GIkU.js (removed) 49.4 kB 🟢 -49.4 kB 🟢 -9.94 kB 🟢 -8.82 kB
assets/KeybindingPanel-D1hzVE_M.js (new) 49.4 kB 🔴 +49.4 kB 🔴 +9.94 kB 🔴 +8.8 kB
assets/SecretsPanel-CRIVq7DA.js (removed) 33.7 kB 🟢 -33.7 kB 🟢 -7.86 kB 🟢 -6.9 kB
assets/SecretsPanel-DQC3_eVm.js (new) 33.7 kB 🔴 +33.7 kB 🔴 +7.86 kB 🔴 +6.89 kB
assets/CreditsPanel-DPODrNXu.js (removed) 15.9 kB 🟢 -15.9 kB 🟢 -4.62 kB 🟢 -4.07 kB
assets/CreditsPanel-Dxme2ZLX.js (new) 15.9 kB 🔴 +15.9 kB 🔴 +4.62 kB 🔴 +4.07 kB
assets/SubscriptionPanel-BqYvgHLM.js (new) 11.5 kB 🔴 +11.5 kB 🔴 +3.66 kB 🔴 +3.21 kB
assets/SubscriptionPanel-D1teUoPz.js (removed) 11.5 kB 🟢 -11.5 kB 🟢 -3.66 kB 🟢 -3.21 kB
assets/AboutPanel-CjYsUY8r.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.02 kB 🔴 +2.71 kB
assets/AboutPanel-pR3zpuzA.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.02 kB 🟢 -2.71 kB
assets/ExtensionPanel-CDY9hHQo.js (new) 9.19 kB 🔴 +9.19 kB 🔴 +2.51 kB 🔴 +2.22 kB
assets/ExtensionPanel-QPLveFq4.js (removed) 9.19 kB 🟢 -9.19 kB 🟢 -2.51 kB 🟢 -2.23 kB
assets/ServerConfigPanel-1YR_3KtK.js (removed) 6.09 kB 🟢 -6.09 kB 🟢 -1.93 kB 🟢 -1.72 kB
assets/ServerConfigPanel-Clqt-Tqq.js (new) 6.09 kB 🔴 +6.09 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/UserPanel-BkdCNs8Z.js (removed) 5.73 kB 🟢 -5.73 kB 🟢 -1.78 kB 🟢 -1.54 kB
assets/UserPanel-TRRaYnvj.js (new) 5.73 kB 🔴 +5.73 kB 🔴 +1.78 kB 🔴 +1.54 kB
assets/refreshRemoteConfig-BTjwguPO.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.39 kB 🟢 -1.23 kB
assets/refreshRemoteConfig-DBjcNRy5.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.39 kB 🔴 +1.23 kB
assets/cloudRemoteConfig-DJAVW6gR.js (new) 951 B 🔴 +951 B 🔴 +513 B 🔴 +421 B
assets/cloudRemoteConfig-DwZRKl-b.js (removed) 951 B 🟢 -951 B 🟢 -515 B 🟢 -421 B
assets/refreshRemoteConfig-DVnllBd5.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -86 B
assets/refreshRemoteConfig-DyfGDRRX.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +83 B

Status: 11 added / 11 removed / 16 unchanged

User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SignUpForm-C18zmznR.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -4.31 kB 🟢 -3.76 kB
assets/SignUpForm-C8Ncx_23.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +4.31 kB 🔴 +3.76 kB
assets/auth-Cx5u2Uy6.js (removed) 3.65 kB 🟢 -3.65 kB 🟢 -1.27 kB 🟢 -1.09 kB
assets/auth-DfiNJYWq.js (new) 3.65 kB 🔴 +3.65 kB 🔴 +1.27 kB 🔴 +1.09 kB
assets/UpdatePasswordContent-C4pz5C4a.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +842 B 🔴 +732 B
assets/UpdatePasswordContent-M0DjbGgc.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -842 B 🟢 -732 B
assets/authStore-7v2muAf5.js (new) 128 B 🔴 +128 B 🔴 +104 B 🔴 +104 B
assets/authStore-h9dfihJN.js (removed) 128 B 🟢 -128 B 🟢 -104 B 🟢 -110 B
assets/workspaceAuthStore--SdbvmSG.js (new) 108 B 🔴 +108 B 🔴 +99 B 🔴 +101 B
assets/workspaceAuthStore-DBzUoDTc.js (removed) 108 B 🟢 -108 B 🟢 -99 B 🟢 -106 B
assets/auth-mskdCPU6.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +87 B
assets/auth-VOgB0SuY.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -80 B

Status: 6 added / 6 removed / 4 unchanged

Editors & Dialogs — 124 kB (baseline 124 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-Dw8OZmt8.js (new) 90 kB 🔴 +90 kB 🔴 +19.2 kB 🔴 +16.5 kB
assets/ComfyHubPublishDialog-DwQhPyjz.js (removed) 90 kB 🟢 -90 kB 🟢 -19.2 kB 🟢 -16.5 kB
assets/useShareDialog-BDay1OO3.js (new) 23.9 kB 🔴 +23.9 kB 🔴 +5.69 kB 🔴 +5.03 kB
assets/useShareDialog-D_Gkgiar.js (removed) 23.9 kB 🟢 -23.9 kB 🟢 -5.69 kB 🟢 -5.03 kB
assets/feedbackDialog-B7i20yju.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.87 kB 🟢 -1.59 kB
assets/feedbackDialog-CISC306h.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.87 kB 🔴 +1.59 kB
assets/useRangeEditor-DYc4v69E.js (new) 3.23 kB 🔴 +3.23 kB 🔴 +1.13 kB 🔴 +1.02 kB
assets/useRangeEditor-FirlIe8R.js (removed) 3.23 kB 🟢 -3.23 kB 🟢 -1.13 kB 🟢 -1.02 kB
assets/ComfyHubPublishDialog-BCFLkqTA.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -92 B
assets/ComfyHubPublishDialog-Ze1wF2py.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +87 B
assets/useSubscriptionDialog-BSGftH0K.js (removed) 108 B 🟢 -108 B 🟢 -102 B 🟢 -93 B
assets/useSubscriptionDialog-CpO2XhJW.js (new) 108 B 🔴 +108 B 🔴 +102 B 🔴 +91 B

Status: 6 added / 6 removed / 1 unchanged

UI Components — 72.1 kB (baseline 72.1 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-Dk0crNY4.js (new) 14.5 kB 🔴 +14.5 kB 🔴 +3.95 kB 🔴 +3.51 kB
assets/ComfyQueueButton-pUFGK2Jz.js (removed) 14.5 kB 🟢 -14.5 kB 🟢 -3.96 kB 🟢 -3.51 kB
assets/useTerminalTabs-BMvAlogP.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.69 kB 🔴 +3.26 kB
assets/useTerminalTabs-CEiIf_H-.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.69 kB 🟢 -3.26 kB
assets/InviteMembersForm-CS9KHWH0.js (new) 8.2 kB 🔴 +8.2 kB 🔴 +2.72 kB 🔴 +2.44 kB
assets/InviteMembersForm-tfgZTS66.js (removed) 8.2 kB 🟢 -8.2 kB 🟢 -2.72 kB 🟢 -2.42 kB
assets/SubscribeButton-BEt_DahL.js (new) 2.15 kB 🔴 +2.15 kB 🔴 +965 B 🔴 +844 B
assets/SubscribeButton-Dtk2u3u3.js (removed) 2.15 kB 🟢 -2.15 kB 🟢 -966 B 🟢 -845 B
assets/cloudFeedbackTopbarButton-BE4u38qT.js (new) 705 B 🔴 +705 B 🔴 +422 B 🔴 +360 B
assets/cloudFeedbackTopbarButton-DTZtnJk6.js (removed) 705 B 🟢 -705 B 🟢 -423 B 🟢 -359 B
assets/ComfyQueueButton-CXfWwbbQ.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -89 B
assets/ComfyQueueButton-D0IRJSIA.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +88 B

Status: 6 added / 6 removed / 9 unchanged

Data & Services — 3.49 MB (baseline 3.48 MB) • 🔴 +4.61 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/settingStore-ashYbFIh.js (new) 3.21 MB 🔴 +3.21 MB 🔴 +743 kB 🔴 +560 kB
assets/settingStore-Dxr_YQXs.js (removed) 3.21 MB 🟢 -3.21 MB 🟢 -742 kB 🟢 -559 kB
assets/load3dService-AL0KqLhr.js (removed) 132 kB 🟢 -132 kB 🟢 -29.2 kB 🟢 -24.6 kB
assets/load3dService-C_u9akjp.js (new) 132 kB 🔴 +132 kB 🔴 +29.2 kB 🔴 +24.6 kB
assets/api-Cp4l0CLS.js (removed) 97.1 kB 🟢 -97.1 kB 🟢 -26.8 kB 🟢 -23 kB
assets/api-pfsfhUhQ.js (new) 97.1 kB 🔴 +97.1 kB 🔴 +26.8 kB 🔴 +23 kB
assets/workflowShareService-Blnu-Cpq.js (removed) 16.4 kB 🟢 -16.4 kB 🟢 -4.89 kB 🟢 -4.34 kB
assets/workflowShareService-BN7ST4Sz.js (new) 16.4 kB 🔴 +16.4 kB 🔴 +4.89 kB 🔴 +4.34 kB
assets/keybindingService-DaWpUM6Y.js (removed) 6.89 kB 🟢 -6.89 kB 🟢 -1.73 kB 🟢 -1.5 kB
assets/keybindingService-DFUjiqPR.js (new) 6.89 kB 🔴 +6.89 kB 🔴 +1.73 kB 🔴 +1.49 kB
assets/releaseStore-_xi3TvVW.js (removed) 6.72 kB 🟢 -6.72 kB 🟢 -2.03 kB 🟢 -1.77 kB
assets/releaseStore-DYWDVqqA.js (new) 6.72 kB 🔴 +6.72 kB 🔴 +2.03 kB 🔴 +1.78 kB
assets/systemStatsStore-Br5ow4gz.js (removed) 4.93 kB 🟢 -4.93 kB 🟢 -1.74 kB 🟢 -1.47 kB
assets/systemStatsStore-cNQYaQad.js (new) 4.93 kB 🔴 +4.93 kB 🔴 +1.74 kB 🔴 +1.47 kB
assets/userStore-BAXtlmLl.js (removed) 2.38 kB 🟢 -2.38 kB 🟢 -895 B 🟢 -794 B
assets/userStore-Bh_Ail47.js (new) 2.38 kB 🔴 +2.38 kB 🔴 +893 B 🔴 +793 B
assets/audioService-BdG3ezPs.js (removed) 1.71 kB 🟢 -1.71 kB 🟢 -830 B 🟢 -724 B
assets/audioService-DFBjP_zi.js (new) 1.71 kB 🔴 +1.71 kB 🔴 +829 B 🔴 +725 B
assets/dialogService-CbZESEMi.js (removed) 98 B 🟢 -98 B 🟢 -97 B 🟢 -85 B
assets/dialogService-CPWPsONT.js (new) 98 B 🔴 +98 B 🔴 +97 B 🔴 +84 B
assets/releaseStore-BWg46nqo.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +85 B
assets/releaseStore-Cj-71wZ8.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -89 B
assets/settingStore-BP1c-xgE.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +83 B
assets/settingStore-sk_mmH5j.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -86 B
assets/assetsStore-CVvMDGX6.js (new) 94 B 🔴 +94 B 🔴 +92 B 🔴 +83 B
assets/assetsStore-DJ4iKehk.js (removed) 94 B 🟢 -94 B 🟢 -92 B 🟢 -86 B
assets/api-ktXR2X3P.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +62 B
assets/api-zCthjer9.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B

Status: 14 added / 14 removed / 3 unchanged

Utilities & Hooks — 387 kB (baseline 387 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-C1SuN6nS.js (new) 235 kB 🔴 +235 kB 🔴 +52.5 kB 🔴 +42.7 kB
assets/useConflictDetection-CzS6i3mk.js (removed) 235 kB 🟢 -235 kB 🟢 -52.5 kB 🟢 -42.7 kB
assets/useLoad3d-CDbMXh3s.js (new) 25.7 kB 🔴 +25.7 kB 🔴 +5.79 kB 🔴 +5.13 kB
assets/useLoad3d-DKGw6VMS.js (removed) 25.7 kB 🟢 -25.7 kB 🟢 -5.79 kB 🟢 -5.13 kB
assets/useLoad3dViewer-DERYlkFE.js (new) 21.2 kB 🔴 +21.2 kB 🔴 +4.98 kB 🔴 +4.35 kB
assets/useLoad3dViewer-DIDN5r5K.js (removed) 21.2 kB 🟢 -21.2 kB 🟢 -4.98 kB 🟢 -4.36 kB
assets/useImageCrop-DosVctn-.js (removed) 15 kB 🟢 -15 kB 🟢 -3.42 kB 🟢 -2.98 kB
assets/useImageCrop-DZQrvk6C.js (new) 15 kB 🔴 +15 kB 🔴 +3.42 kB 🔴 +2.98 kB
assets/useDowngradeToPersonal-BHfscOu9.js (new) 8.85 kB 🔴 +8.85 kB 🔴 +2.53 kB 🔴 +2.17 kB
assets/useDowngradeToPersonal-DDKzgtrK.js (removed) 8.85 kB 🟢 -8.85 kB 🟢 -2.53 kB 🟢 -2.17 kB
assets/useFeatureFlags-CwTzfxDb.js (removed) 7.8 kB 🟢 -7.8 kB 🟢 -2.28 kB 🟢 -1.91 kB
assets/useFeatureFlags-HMobI-fY.js (new) 7.8 kB 🔴 +7.8 kB 🔴 +2.28 kB 🔴 +1.92 kB
assets/assetPreviewUtil-BYQC0dh-.js (new) 2.35 kB 🔴 +2.35 kB 🔴 +968 B 🔴 +838 B
assets/assetPreviewUtil-DVlKpgRi.js (removed) 2.35 kB 🟢 -2.35 kB 🟢 -971 B 🟢 -835 B
assets/useUpstreamValue-BJns0zuI.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -756 B 🟢 -676 B
assets/useUpstreamValue-DTUzHDoA.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +754 B 🔴 +667 B
assets/useWorkspaceTierLabel-DY9YgJog.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -813 B 🟢 -693 B
assets/useWorkspaceTierLabel-HcIYND5Q.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +811 B 🔴 +693 B
assets/subscriptionCheckoutUtil-CGLT1K9t.js (removed) 773 B 🟢 -773 B 🟢 -472 B 🟢 -407 B
assets/subscriptionCheckoutUtil-DsAXeDJT.js (new) 773 B 🔴 +773 B 🔴 +470 B 🔴 +406 B
assets/useSessionCookie-C-wl5w-B.js (removed) 692 B 🟢 -692 B 🟢 -356 B 🟢 -333 B
assets/useSessionCookie-V6zoX8TM.js (new) 692 B 🔴 +692 B 🔴 +353 B 🔴 +313 B
assets/useLoad3d-2LoFkDA4.js (new) 311 B 🔴 +311 B 🔴 +163 B 🔴 +147 B
assets/useLoad3d-2ZJrkeUK.js (removed) 311 B 🟢 -311 B 🟢 -165 B 🟢 -146 B
assets/useSessionCookie-C6XULHgH.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -82 B
assets/useSessionCookie-CVPpHq0l.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +83 B
assets/useFeatureFlags-D3N0cYff.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -82 B
assets/useFeatureFlags-iS4D_RYS.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +82 B
assets/useLoad3dViewer-BEU4Ceei.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -88 B
assets/useLoad3dViewer-EPwOKX-6.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +85 B
assets/useCurrentUser-CKbOoo1X.js (new) 94 B 🔴 +94 B 🔴 +95 B 🔴 +90 B
assets/useCurrentUser-CkIyR1bP.js (removed) 94 B 🟢 -94 B 🟢 -95 B 🟢 -88 B

Status: 16 added / 16 removed / 20 unchanged

Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 B

External libraries and shared vendor chunks

Status: 16 unchanged

Other — 12.8 MB (baseline 12.8 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-BjK5WEKa.js (removed) 111 kB 🟢 -111 kB 🟢 -28.7 kB 🟢 -24.3 kB
assets/core-CwJ_ayQc.js (new) 111 kB 🔴 +111 kB 🔴 +28.7 kB 🔴 +24.3 kB
assets/WidgetSelect-6gKGhX1G.js (removed) 88.7 kB 🟢 -88.7 kB 🟢 -20.1 kB 🟢 -17.2 kB
assets/WidgetSelect-DK3JIjmu.js (new) 88.7 kB 🔴 +88.7 kB 🔴 +20.1 kB 🔴 +17.2 kB
assets/Load3D-B1Mfe2ZD.js (removed) 71.3 kB 🟢 -71.3 kB 🟢 -11.7 kB 🟢 -9.97 kB
assets/Load3D-Bzpim3ie.js (new) 71.3 kB 🔴 +71.3 kB 🔴 +11.7 kB 🔴 +9.98 kB
assets/WidgetVideoEdit-BVLzEQus.js (removed) 63.4 kB 🟢 -63.4 kB 🟢 -14.6 kB 🟢 -12.8 kB
assets/WidgetVideoEdit-C9LekNxo.js (new) 63.4 kB 🔴 +63.4 kB 🔴 +14.6 kB 🔴 +12.8 kB
assets/SubscriptionTransitionPreviewWorkspace-Cwwtn0X6.js (removed) 63.2 kB 🟢 -63.2 kB 🟢 -13 kB 🟢 -11.4 kB
assets/SubscriptionTransitionPreviewWorkspace-D5aupqSa.js (new) 63.2 kB 🔴 +63.2 kB 🔴 +13 kB 🔴 +11.4 kB
assets/WorkspaceSettingsPanelContent-DYyPGNmu.js (new) 61.6 kB 🔴 +61.6 kB 🔴 +13.2 kB 🔴 +11.5 kB
assets/WorkspaceSettingsPanelContent-FXVBYKMb.js (removed) 61.6 kB 🟢 -61.6 kB 🟢 -13.2 kB 🟢 -11.5 kB
assets/Preview3d-DRN3_ieI.js (removed) 50.9 kB 🟢 -50.9 kB 🟢 -8.3 kB 🟢 -7.24 kB
assets/Preview3d-xaBfKdTR.js (new) 50.9 kB 🔴 +50.9 kB 🔴 +8.3 kB 🔴 +7.25 kB
assets/main-CGnVwS33.js (new) 49.4 kB 🔴 +49.4 kB 🔴 +14.4 kB 🔴 +12.5 kB
assets/main-DDzfe26t.js (removed) 49.4 kB 🟢 -49.4 kB 🟢 -14.4 kB 🟢 -12.5 kB
assets/SubscriptionRequiredDialogContentUnified-B2V_XdSA.js (removed) 42.8 kB 🟢 -42.8 kB 🟢 -9.45 kB 🟢 -8.23 kB
assets/SubscriptionRequiredDialogContentUnified-CrV0QJLZ.js (new) 42.8 kB 🔴 +42.8 kB 🔴 +9.45 kB 🔴 +8.23 kB
assets/MembersPanelContent-Bw7tHMfz.js (new) 42.6 kB 🔴 +42.6 kB 🔴 +8.96 kB 🔴 +7.96 kB
assets/MembersPanelContent-D-689zbK.js (removed) 42.6 kB 🟢 -42.6 kB 🟢 -8.96 kB 🟢 -7.96 kB
assets/WidgetBoundingBoxes-BiAFaXei.js (removed) 33.7 kB 🟢 -33.7 kB 🟢 -9.15 kB 🟢 -8.13 kB
assets/WidgetBoundingBoxes-kBzcURGD.js (new) 33.7 kB 🔴 +33.7 kB 🔴 +9.15 kB 🔴 +8.13 kB
assets/WidgetPainter-CmTyNdH2.js (removed) 32.6 kB 🟢 -32.6 kB 🟢 -7.86 kB 🟢 -6.97 kB
assets/WidgetPainter-DLDguPB3.js (new) 32.6 kB 🔴 +32.6 kB 🔴 +7.86 kB 🔴 +6.95 kB
assets/SubscriptionPanelContentWorkspace--vfGxh8R.js (new) 31.9 kB 🔴 +31.9 kB 🔴 +7.07 kB 🔴 +6.22 kB
assets/SubscriptionPanelContentWorkspace-85N1Sl4d.js (removed) 31.9 kB 🟢 -31.9 kB 🟢 -7.07 kB 🟢 -6.23 kB
assets/Load3dViewerContent-BLXKEOto.js (removed) 30.8 kB 🟢 -30.8 kB 🟢 -6.27 kB 🟢 -5.43 kB
assets/Load3dViewerContent-ROn03LrY.js (new) 30.8 kB 🔴 +30.8 kB 🔴 +6.27 kB 🔴 +5.43 kB
assets/SubscriptionRequiredDialogContent-BOC9XdRJ.js (removed) 26.1 kB 🟢 -26.1 kB 🟢 -6.14 kB 🟢 -5.41 kB
assets/SubscriptionRequiredDialogContent-CTDOaPzs.js (new) 26.1 kB 🔴 +26.1 kB 🔴 +6.14 kB 🔴 +5.4 kB
assets/SubscriptionRequiredDialogContentWorkspace-CO-9K9gE.js (removed) 25.1 kB 🟢 -25.1 kB 🟢 -5.8 kB 🟢 -5.1 kB
assets/SubscriptionRequiredDialogContentWorkspace-DIFezWn1.js (new) 25.1 kB 🔴 +25.1 kB 🔴 +5.8 kB 🔴 +5.11 kB
assets/load3d-BwtDfM6s.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.4 kB 🔴 +4.67 kB
assets/load3d-fdywiWLt.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.4 kB 🟢 -4.67 kB
assets/CurrentUserPopoverWorkspace-BkN2rrkk.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -4.83 kB 🟢 -4.3 kB
assets/CurrentUserPopoverWorkspace-DYkOHemn.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +4.83 kB 🔴 +4.3 kB
assets/SignInContent-57-J4SAn.js (removed) 20.2 kB 🟢 -20.2 kB 🟢 -5.1 kB 🟢 -4.45 kB
assets/SignInContent-CI5TFDft.js (new) 20.2 kB 🔴 +20.2 kB 🔴 +5.1 kB 🔴 +4.45 kB
assets/CreditsTile-BD3gmu19.js (removed) 19.2 kB 🟢 -19.2 kB 🟢 -4.83 kB 🟢 -4.24 kB
assets/CreditsTile-DypjP0HS.js (new) 19.2 kB 🔴 +19.2 kB 🔴 +4.83 kB 🔴 +4.25 kB
assets/WidgetRecordAudio-B5pXSEht.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.59 kB 🟢 -4.1 kB
assets/WidgetRecordAudio-BPaJyt04.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.6 kB 🔴 +4.1 kB
assets/WidgetInputNumber-B7208g73.js (removed) 13.9 kB 🟢 -13.9 kB 🟢 -3.63 kB 🟢 -3.21 kB
assets/WidgetInputNumber-Cv8ENtBp.js (new) 13.9 kB 🔴 +13.9 kB 🔴 +3.63 kB 🔴 +3.21 kB
assets/WidgetRange-D9J5LSAd.js (removed) 13.7 kB 🟢 -13.7 kB 🟢 -3.53 kB 🟢 -3.11 kB
assets/WidgetRange-hRhaVP7v.js (new) 13.7 kB 🔴 +13.7 kB 🔴 +3.53 kB 🔴 +3.12 kB
assets/WaveAudioPlayer-BHrvksA_.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.46 kB 🔴 +3.03 kB
assets/WaveAudioPlayer-DEF_Izt0.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.46 kB 🟢 -3.04 kB
assets/WidgetCurve-D_SNOUS8.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.47 kB 🟢 -3.15 kB
assets/WidgetCurve-DU8vd3bA.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.47 kB 🔴 +3.14 kB
assets/TeamWorkspacesDialogContent-B9udtHXj.js (new) 10.3 kB 🔴 +10.3 kB 🔴 +2.96 kB 🔴 +2.63 kB
assets/TeamWorkspacesDialogContent-Dm_qtPa7.js (removed) 10.3 kB 🟢 -10.3 kB 🟢 -2.96 kB 🟢 -2.62 kB
assets/onboardingCloudRoutes-Cm2xMsQU.js (removed) 9.12 kB 🟢 -9.12 kB 🟢 -2.79 kB 🟢 -2.39 kB
assets/onboardingCloudRoutes-CN_lptXy.js (new) 9.12 kB 🔴 +9.12 kB 🔴 +2.79 kB 🔴 +2.4 kB
assets/Load3DConfiguration-DJs7qDKl.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.61 kB 🔴 +2.3 kB
assets/Load3DConfiguration-DldryrNB.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.61 kB 🟢 -2.3 kB
assets/WidgetImageCrop-6Sz8dEGX.js (new) 8.49 kB 🔴 +8.49 kB 🔴 +2.65 kB 🔴 +2.33 kB
assets/WidgetImageCrop-jYu82-sj.js (removed) 8.49 kB 🟢 -8.49 kB 🟢 -2.65 kB 🟢 -2.33 kB
assets/SetMemberCreditLimitDialogContent-BY_pawjh.js (new) 8.47 kB 🔴 +8.47 kB 🔴 +2.34 kB 🔴 +2.05 kB
assets/SetMemberCreditLimitDialogContent-Du-4z6ax.js (removed) 8.47 kB 🟢 -8.47 kB 🟢 -2.34 kB 🟢 -2.05 kB
assets/nodeTemplates-BrJZT1sN.js (new) 8.28 kB 🔴 +8.28 kB 🔴 +2.84 kB 🔴 +2.5 kB
assets/nodeTemplates-CNw-kUBi.js (removed) 8.28 kB 🟢 -8.28 kB 🟢 -2.84 kB 🟢 -2.5 kB
assets/NightlySurveyController-DQ3NrONM.js (removed) 7.5 kB 🟢 -7.5 kB 🟢 -2.55 kB 🟢 -2.24 kB
assets/NightlySurveyController-szyWAnTu.js (new) 7.5 kB 🔴 +7.5 kB 🔴 +2.55 kB 🔴 +2.24 kB
assets/CloudRunButtonWrapper-C_DuzteO.js (new) 6.82 kB 🔴 +6.82 kB 🔴 +2.17 kB 🔴 +1.91 kB
assets/CloudRunButtonWrapper-DngL_9gU.js (removed) 6.82 kB 🟢 -6.82 kB 🟢 -2.18 kB 🟢 -1.91 kB
assets/WidgetWithControl-BJPSUb_h.js (removed) 6.37 kB 🟢 -6.37 kB 🟢 -2.59 kB 🟢 -2.3 kB
assets/WidgetWithControl-ClFveRdk.js (new) 6.37 kB 🔴 +6.37 kB 🔴 +2.6 kB 🔴 +2.3 kB
assets/tierBenefits-B3nId4m7.js (removed) 6.2 kB 🟢 -6.2 kB 🟢 -1.96 kB 🟢 -1.72 kB
assets/tierBenefits-Dbxg7pHV.js (new) 6.2 kB 🔴 +6.2 kB 🔴 +1.96 kB 🔴 +1.72 kB
assets/CancelSubscriptionDialogContent-BOIdJ8SJ.js (removed) 5.97 kB 🟢 -5.97 kB 🟢 -1.98 kB 🟢 -1.75 kB
assets/CancelSubscriptionDialogContent-C6I7RtFb.js (new) 5.97 kB 🔴 +5.97 kB 🔴 +1.98 kB 🔴 +1.75 kB
assets/load3dPreviewExtensions-Bi9jp92E.js (removed) 5.88 kB 🟢 -5.88 kB 🟢 -1.81 kB 🟢 -1.6 kB
assets/load3dPreviewExtensions-C_LoUiTu.js (new) 5.88 kB 🔴 +5.88 kB 🔴 +1.81 kB 🔴 +1.59 kB
assets/missingModelDownload-BFx7JGP6.js (new) 5.88 kB 🔴 +5.88 kB 🔴 +2.07 kB 🔴 +1.81 kB
assets/missingModelDownload-BksZZyAX.js (removed) 5.88 kB 🟢 -5.88 kB 🟢 -2.07 kB 🟢 -1.81 kB
assets/launchCancellationFlow-B6CM6geS.js (removed) 5.18 kB 🟢 -5.18 kB 🟢 -1.78 kB 🟢 -1.56 kB
assets/launchCancellationFlow-nrL1yN2A.js (new) 5.18 kB 🔴 +5.18 kB 🔴 +1.78 kB 🔴 +1.56 kB
assets/CreateWorkspaceDialogContent-BWWb8TC3.js (removed) 5.12 kB 🟢 -5.12 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/CreateWorkspaceDialogContent-gGkugJuK.js (new) 5.12 kB 🔴 +5.12 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/ChangeMemberRoleDialogContent-B74loAvl.js (removed) 4.97 kB 🟢 -4.97 kB 🟢 -1.64 kB 🟢 -1.42 kB
assets/ChangeMemberRoleDialogContent-DmOCXeQD.js (new) 4.97 kB 🔴 +4.97 kB 🔴 +1.64 kB 🔴 +1.42 kB
assets/InviteMemberDialogContent-BL3_5Kar.js (new) 4.96 kB 🔴 +4.96 kB 🔴 +1.64 kB 🔴 +1.43 kB
assets/InviteMemberDialogContent-cCdPTPfL.js (removed) 4.96 kB 🟢 -4.96 kB 🟢 -1.64 kB 🟢 -1.43 kB
assets/EditWorkspaceDialogContent-BUEV_bKs.js (removed) 4.93 kB 🟢 -4.93 kB 🟢 -1.76 kB 🟢 -1.53 kB
assets/EditWorkspaceDialogContent-mVeDAv3K.js (new) 4.93 kB 🔴 +4.93 kB 🔴 +1.76 kB 🔴 +1.53 kB
assets/WidgetTextarea-99-mE0qS.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.87 kB 🔴 +1.63 kB
assets/WidgetTextarea-BQHTM8W_.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.87 kB 🟢 -1.63 kB
assets/WorkspacePanelContent-BEmyfop0.js (new) 4.79 kB 🔴 +4.79 kB 🔴 +1.63 kB 🔴 +1.45 kB
assets/WorkspacePanelContent-CcF7VKr7.js (removed) 4.79 kB 🟢 -4.79 kB 🟢 -1.64 kB 🟢 -1.45 kB
assets/saveMesh-BtHG3f_k.js (removed) 4.76 kB 🟢 -4.76 kB 🟢 -1.52 kB 🟢 -1.34 kB
assets/saveMesh-Cmk_yduc.js (new) 4.76 kB 🔴 +4.76 kB 🔴 +1.52 kB 🔴 +1.34 kB
assets/ValueControlPopover-D96OxD3_.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.55 kB 🔴 +1.38 kB
assets/ValueControlPopover-DrT-9gCZ.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.55 kB 🟢 -1.38 kB
assets/DeleteWorkspaceDialogContent-D0a2qU9R.js (new) 3.84 kB 🔴 +3.84 kB 🔴 +1.44 kB 🔴 +1.24 kB
assets/DeleteWorkspaceDialogContent-FRj3QpcX.js (removed) 3.84 kB 🟢 -3.84 kB 🟢 -1.44 kB 🟢 -1.23 kB
assets/RemoveMemberDialogContent-CDj8gE3Z.js (new) 3.76 kB 🔴 +3.76 kB 🔴 +1.38 kB 🔴 +1.2 kB
assets/RemoveMemberDialogContent-CKQQcm21.js (removed) 3.76 kB 🟢 -3.76 kB 🟢 -1.38 kB 🟢 -1.2 kB
assets/RevokeInviteDialogContent-CmHqRVo-.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.39 kB 🔴 +1.21 kB
assets/RevokeInviteDialogContent-DLKHCBeT.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.39 kB 🟢 -1.21 kB
assets/LeaveWorkspaceDialogContent-BA6PDfc0.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.38 kB 🟢 -1.19 kB
assets/LeaveWorkspaceDialogContent-C6B9DXxW.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.38 kB 🔴 +1.19 kB
assets/InviteMemberUpsellDialogContent-Dt5Q6V42.js (removed) 3.47 kB 🟢 -3.47 kB 🟢 -1.24 kB 🟢 -1.07 kB
assets/InviteMemberUpsellDialogContent-FshOSE8T.js (new) 3.47 kB 🔴 +3.47 kB 🔴 +1.24 kB 🔴 +1.08 kB
assets/workspaceCheckoutTelemetry-D9Jnnv6l.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.52 kB 🔴 +1.33 kB
assets/workspaceCheckoutTelemetry-JvclgoGn.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.53 kB 🟢 -1.33 kB
assets/Media3DTop-1iX0uT2l.js (removed) 3.21 kB 🟢 -3.21 kB 🟢 -1.26 kB 🟢 -1.1 kB
assets/Media3DTop-C95yMHWt.js (new) 3.21 kB 🔴 +3.21 kB 🔴 +1.26 kB 🔴 +1.1 kB
assets/GlobalToast-CvwKngzZ.js (removed) 3 kB 🟢 -3 kB 🟢 -1.22 kB 🟢 -1.04 kB
assets/GlobalToast-DKtBvJJH.js (new) 3 kB 🔴 +3 kB 🔴 +1.22 kB 🔴 +1.04 kB
assets/load3dAdvanced-CASJMCkp.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.09 kB 🟢 -956 B
assets/load3dAdvanced-CUVBQmaO.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.09 kB 🔴 +955 B
assets/SubscribeToRun-CkqDwOl9.js (removed) 2.39 kB 🟢 -2.39 kB 🟢 -1.03 kB 🟢 -904 B
assets/SubscribeToRun-Dem-xDmK.js (new) 2.39 kB 🔴 +2.39 kB 🔴 +1.03 kB 🔴 +904 B
assets/MediaAudioTop-DBQKuUAz.js (removed) 1.62 kB 🟢 -1.62 kB 🟢 -806 B 🟢 -667 B
assets/MediaAudioTop-LraZ2nH9.js (new) 1.62 kB 🔴 +1.62 kB 🔴 +806 B 🔴 +667 B
assets/cloudSessionCookie-BoIxT-b1.js (removed) 933 B 🟢 -933 B 🟢 -433 B 🟢 -379 B
assets/cloudSessionCookie-CRmti4A-.js (new) 933 B 🔴 +933 B 🔴 +434 B 🔴 +378 B
assets/cloudBadges-CIUSGDpE.js (new) 922 B 🔴 +922 B 🔴 +513 B 🔴 +440 B
assets/cloudBadges-DPtU1FP_.js (removed) 922 B 🟢 -922 B 🟢 -513 B 🟢 -440 B
assets/Load3DAdvanced-r1bmPMsv.js (removed) 761 B 🟢 -761 B 🟢 -422 B 🟢 -358 B
assets/Load3DAdvanced-Ry0zJq4m.js (new) 761 B 🔴 +761 B 🔴 +420 B 🔴 +357 B
assets/nightlyBadges-CbkAjggk.js (removed) 411 B 🟢 -411 B 🟢 -272 B 🟢 -264 B
assets/nightlyBadges-DLaYTbwq.js (new) 411 B 🔴 +411 B 🔴 +273 B 🔴 +229 B
assets/missingModelDownload-DnXEskBL.js (new) 371 B 🔴 +371 B 🔴 +204 B 🔴 +172 B
assets/missingModelDownload-DoO9c5Vw.js (removed) 371 B 🟢 -371 B 🟢 -203 B 🟢 -173 B
assets/SubscriptionPanelContentWorkspace-D1PG1FT7.js (removed) 179 B 🟢 -179 B 🟢 -117 B 🟢 -93 B
assets/SubscriptionPanelContentWorkspace-WIPGX2_c.js (new) 179 B 🔴 +179 B 🔴 +117 B 🔴 +99 B
assets/Load3dViewerContent-CkFu0D1r.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +93 B
assets/Load3dViewerContent-CNMrYmhT.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -92 B
assets/Load3DAdvanced-C_zPAcY6.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +88 B
assets/Load3DAdvanced-ToPgDeB3.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -88 B
assets/WidgetLegacy-CdtdVeEl.js (new) 117 B 🔴 +117 B 🔴 +106 B 🔴 +101 B
assets/WidgetLegacy-DId9SGJJ.js (removed) 117 B 🟢 -117 B 🟢 -106 B 🟢 -99 B
assets/workflowDraftStoreV2-BAWeSVKD.js (removed) 112 B 🟢 -112 B 🟢 -101 B 🟢 -116 B
assets/workflowDraftStoreV2-Ca0468n7.js (new) 112 B 🔴 +112 B 🔴 +101 B 🔴 +109 B
assets/Load3D-BHEZBgfK.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -81 B
assets/Load3D-xtdONS8D.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +81 B
assets/changeTracker-BwZ3aSPJ.js (removed) 91 B 🟢 -91 B 🟢 -93 B 🟢 -84 B
assets/changeTracker-BZHCrwiO.js (new) 91 B 🔴 +91 B 🔴 +93 B 🔴 +82 B

Status: 70 added / 70 removed / 214 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.1 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 49.4 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.3 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.6 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.2 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.4 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.7 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.2 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.3 MB heap
subgraph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 54.8 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 44.5 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 124ms TBT · 84.8 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.1 MB heap
vue-large-graph-idle: · 56.3 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 160.2 MB heap
vue-large-graph-pan: · 56.3 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 218ms TBT · 163.8 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.3 MB heap

⚠️ 6 regressions detected

Show regressions
Metric Baseline PR (median) Δ Sig
canvas-idle: task duration 524ms 481ms -8% ⚠️ z=2.8
canvas-mouse-sweep: task duration 953ms 1006ms +6% ⚠️ z=2.4
large-graph-idle: task duration 807ms 669ms -17% ⚠️ z=2.3
large-graph-pan: task duration 1404ms 1218ms -13% ⚠️ z=3.2
minimap-idle: task duration 786ms 658ms -16% ⚠️ z=2.8
subgraph-idle: task duration 537ms 469ms -13% ⚠️ z=3.1
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=-0.1
canvas-idle: p95 frame time 17ms 17ms -0%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 9ms 7ms -17% z=-4.0
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 8 10 +25% z=-2.1
canvas-idle: task duration 524ms 481ms -8% ⚠️ z=2.8
canvas-idle: script duration 24ms 15ms -35% z=-4.4
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 66.2 MB 55.1 MB -17%
canvas-idle: DOM nodes 16 20 +25% z=-2.0
canvas-idle: event listeners 4 4 +0% z=-1.6
canvas-mouse-sweep: avg frame time 17ms 17ms +0% z=-0.4
canvas-mouse-sweep: p95 frame time 17ms 17ms -1%
canvas-mouse-sweep: layout duration 4ms 4ms -4% z=0.5
canvas-mouse-sweep: style recalc duration 40ms 42ms +4% z=-0.3
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 74 79 +7% z=0.1
canvas-mouse-sweep: task duration 953ms 1006ms +6% ⚠️ z=2.4
canvas-mouse-sweep: script duration 136ms 128ms -6% z=-1.2
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 49.8 MB 49.4 MB -1%
canvas-mouse-sweep: DOM nodes -280 -281 +0% z=-132.7
canvas-mouse-sweep: event listeners -153 -153 +0% z=-38.7
canvas-zoom-sweep: avg frame time 17ms 17ms -0% z=-0.3
canvas-zoom-sweep: p95 frame time 17ms 17ms +0%
canvas-zoom-sweep: layout duration 1ms 1ms -7% z=-0.4
canvas-zoom-sweep: style recalc duration 19ms 15ms -20% z=-2.4
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 31 32 +2% z=0.5
canvas-zoom-sweep: task duration 438ms 368ms -16% z=1.8
canvas-zoom-sweep: script duration 26ms 19ms -24% z=-2.6
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 69.3 MB 69.3 MB -0%
canvas-zoom-sweep: DOM nodes 77 78 +1% z=-1.6
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.5
dom-widget-clipping: p95 frame time 17ms 17ms -0%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 8ms 7ms -4% z=-3.3
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 10 12 +20% z=-2.2
dom-widget-clipping: task duration 394ms 381ms -3% z=1.0
dom-widget-clipping: script duration 64ms 55ms -15% z=-3.9
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 50.6 MB 50.6 MB -0%
dom-widget-clipping: DOM nodes 16 20 +25% z=-1.5
dom-widget-clipping: event listeners 2 1 -50% variance too high
large-graph-idle: avg frame time 17ms 17ms +0% z=-0.2
large-graph-idle: p95 frame time 17ms 17ms +0%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 7ms -24% z=-4.8
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 9 9 +0% z=-8.3
large-graph-idle: task duration 807ms 669ms -17% ⚠️ z=2.3
large-graph-idle: script duration 127ms 91ms -28% z=-1.1
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 63.1 MB 63.2 MB +0%
large-graph-idle: DOM nodes -281 -282 +0% z=-338.8
large-graph-idle: event listeners -149 -149 +0% z=-28.7
large-graph-pan: avg frame time 17ms 17ms -0% z=-0.2
large-graph-pan: p95 frame time 17ms 17ms +0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 15ms 14ms -5% z=-4.2
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 69 69 +0% z=-0.9
large-graph-pan: task duration 1404ms 1218ms -13% ⚠️ z=3.2
large-graph-pan: script duration 464ms 394ms -15% z=-0.7
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 62.1 MB 62.4 MB +0%
large-graph-pan: DOM nodes -283 -283 +0% z=-183.0
large-graph-pan: event listeners -149 -148 -1% z=-184.7
large-graph-zoom: avg frame time 17ms 17ms +0%
large-graph-zoom: p95 frame time 17ms 17ms +0%
large-graph-zoom: layout duration 8ms 8ms -2%
large-graph-zoom: style recalc duration 15ms 15ms -0%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 64 65 +2%
large-graph-zoom: task duration 1603ms 1479ms -8%
large-graph-zoom: script duration 559ms 514ms -8%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 65.1 MB 56.7 MB -13%
large-graph-zoom: DOM nodes -286 -288 +1%
large-graph-zoom: event listeners -153 -153 +0%
minimap-idle: avg frame time 17ms 17ms -0% z=-0.4
minimap-idle: p95 frame time 17ms 17ms +0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 7ms 6ms -16% z=-4.3
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 7 8 +14% z=-2.3
minimap-idle: task duration 786ms 658ms -16% ⚠️ z=2.8
minimap-idle: script duration 126ms 89ms -29% z=-0.9
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 64.0 MB 64.2 MB +0%
minimap-idle: DOM nodes -284 -282 -1% z=-220.2
minimap-idle: event listeners -151 -164 +9% z=-255.3
subgraph-dom-widget-clipping: avg frame time 17ms 17ms -0% z=-0.9
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms -0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 10ms -14% z=-3.1
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 47 47 +0% z=-1.6
subgraph-dom-widget-clipping: task duration 425ms 397ms -6% z=1.0
subgraph-dom-widget-clipping: script duration 126ms 125ms -1% z=-0.6
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 51.2 MB 51.3 MB +0%
subgraph-dom-widget-clipping: DOM nodes 20 20 +0% z=-1.9
subgraph-dom-widget-clipping: event listeners 8 6 -25% z=-1.7
subgraph-idle: avg frame time 17ms 17ms +0% z=0.4
subgraph-idle: p95 frame time 17ms 17ms +1%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 7ms -28% z=-4.8
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 10 10 +0% z=-1.4
subgraph-idle: task duration 537ms 469ms -13% ⚠️ z=3.1
subgraph-idle: script duration 21ms 13ms -39% z=-2.8
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 66.0 MB 54.8 MB -17%
subgraph-idle: DOM nodes 20 20 +0% z=-1.2
subgraph-idle: event listeners 4 4 +0% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms -0%
subgraph-mouse-sweep: layout duration 5ms 4ms -2% z=-0.9
subgraph-mouse-sweep: style recalc duration 38ms 34ms -11% z=-2.6
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 78 +2% z=-1.4
subgraph-mouse-sweep: task duration 825ms 775ms -6% z=0.1
subgraph-mouse-sweep: script duration 100ms 83ms -17% z=-2.7
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 57.2 MB 44.5 MB -22%
subgraph-mouse-sweep: DOM nodes 62 -130 -310% z=-88.1
subgraph-mouse-sweep: event listeners 4 -153 -3925% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms +0%
subgraph-transition-enter: p95 frame time 17ms 17ms -1%
subgraph-transition-enter: layout duration 14ms 11ms -20%
subgraph-transition-enter: style recalc duration 33ms 28ms -16%
subgraph-transition-enter: layout count 13 14 +8%
subgraph-transition-enter: style recalc count 17 18 +6%
subgraph-transition-enter: task duration 1061ms 907ms -14%
subgraph-transition-enter: script duration 43ms 32ms -25%
subgraph-transition-enter: TBT 145ms 124ms -14%
subgraph-transition-enter: heap used 95.9 MB 84.8 MB -12%
subgraph-transition-enter: DOM nodes 13673 13673 +0%
subgraph-transition-enter: event listeners 2375 2375 +0%
viewport-pan-sweep: avg frame time 17ms 17ms -0%
viewport-pan-sweep: p95 frame time 17ms 17ms +0%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 40ms 37ms -7%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 249 250 +0%
viewport-pan-sweep: task duration 4915ms 4207ms -14%
viewport-pan-sweep: script duration 1506ms 1261ms -16%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 68.0 MB 67.1 MB -1%
viewport-pan-sweep: DOM nodes -282 -282 +0%
viewport-pan-sweep: event listeners -131 -133 +2%
vue-large-graph-idle: avg frame time 18ms 18ms -0%
vue-large-graph-idle: p95 frame time 17ms 17ms -0%
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 17668ms 16990ms -4%
vue-large-graph-idle: script duration 615ms 531ms -14%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 159.5 MB 160.2 MB +0%
vue-large-graph-idle: DOM nodes -8313 -8312 -0%
vue-large-graph-idle: event listeners -16387 -16387 +0%
vue-large-graph-pan: avg frame time 18ms 18ms -3%
vue-large-graph-pan: p95 frame time 17ms 17ms +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 20ms 18ms -9%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 147 144 -2%
vue-large-graph-pan: task duration 21333ms 20931ms -2%
vue-large-graph-pan: script duration 917ms 859ms -6%
vue-large-graph-pan: TBT 397ms 218ms -45%
vue-large-graph-pan: heap used 168.3 MB 163.8 MB -3%
vue-large-graph-pan: DOM nodes -8312 -8312 +0%
vue-large-graph-pan: event listeners -16383 -16381 -0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.1
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 1ms 1ms -1% z=-5.9
workflow-execution: style recalc duration 21ms 18ms -11% z=-2.6
workflow-execution: layout count 3 3 +0% z=-3.6
workflow-execution: style recalc count 13 12 -8% z=-2.8
workflow-execution: task duration 122ms 109ms -11% z=-1.3
workflow-execution: script duration 11ms 10ms -13% z=-6.5
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 65.2 MB 65.3 MB +0%
workflow-execution: DOM nodes 125 127 +1% z=-4.8
workflow-execution: event listeners 97 97 +0% z=10.3
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-08-05T22:58:28.992Z",
  "gitSha": "af3cf134d9feaa76132d3b756978f4ecb679068d",
  "branch": "fix/ir-91-output-asset-delete",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2034.0529999999717,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 7.55,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 506.48499999999996,
      "heapDeltaBytes": -18291952,
      "heapUsedBytes": 45749884,
      "domNodes": 20,
      "jsHeapTotalBytes": 23048192,
      "scriptDurationMs": 15.466000000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2041.3519999999608,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 7.017999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 455.07399999999996,
      "heapDeltaBytes": 5648512,
      "heapUsedBytes": 69710384,
      "domNodes": 20,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 15.467000000000002,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2108.967000000007,
      "styleRecalcs": 84,
      "styleRecalcDurationMs": 48.626000000000005,
      "layouts": 12,
      "layoutDurationMs": 3.972,
      "taskDurationMs": 1140.803,
      "heapDeltaBytes": -12101108,
      "heapUsedBytes": 52094296,
      "domNodes": -281,
      "jsHeapTotalBytes": 24096768,
      "scriptDurationMs": 150.46099999999998,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1855.5969999999888,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 34.573,
      "layouts": 12,
      "layoutDurationMs": 3.4530000000000003,
      "taskDurationMs": 870.666,
      "heapDeltaBytes": -12703976,
      "heapUsedBytes": 51416396,
      "domNodes": -281,
      "jsHeapTotalBytes": 24096768,
      "scriptDurationMs": 105.711,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1723.9849999999706,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 14.928999999999998,
      "layouts": 6,
      "layoutDurationMs": 0.5710000000000001,
      "taskDurationMs": 361.712,
      "heapDeltaBytes": 8707788,
      "heapUsedBytes": 72676448,
      "domNodes": 77,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 17.665,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1756.3580000000911,
      "styleRecalcs": 33,
      "styleRecalcDurationMs": 15.887000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.6769999999999998,
      "taskDurationMs": 374.492,
      "heapDeltaBytes": 8716240,
      "heapUsedBytes": 72684568,
      "domNodes": 79,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 21.264,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 625.7800000000202,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.49,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 397.776,
      "heapDeltaBytes": -10972388,
      "heapUsedBytes": 53014720,
      "domNodes": 22,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 56.513000000000005,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 606.3609999999926,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.084999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 363.36,
      "heapDeltaBytes": -11079600,
      "heapUsedBytes": 53097220,
      "domNodes": 18,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 53.474,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2020.1220000000149,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 6.255,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 672.462,
      "heapDeltaBytes": 6274456,
      "heapUsedBytes": 66737380,
      "domNodes": -282,
      "jsHeapTotalBytes": 3514368,
      "scriptDurationMs": 94.958,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2014.0589999999747,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.084000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 666.04,
      "heapDeltaBytes": 6603304,
      "heapUsedBytes": 65863472,
      "domNodes": -281,
      "jsHeapTotalBytes": 1679360,
      "scriptDurationMs": 87.008,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2190.8899999999676,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 13.793000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1221.722,
      "heapDeltaBytes": 4915660,
      "heapUsedBytes": 65271656,
      "domNodes": -284,
      "jsHeapTotalBytes": 3706880,
      "scriptDurationMs": 393.451,
      "eventListeners": -147,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2157.149000000004,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 14.124999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1214.376,
      "heapDeltaBytes": 5252984,
      "heapUsedBytes": 65578712,
      "domNodes": -282,
      "jsHeapTotalBytes": 3706880,
      "scriptDurationMs": 394.573,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3247.068000000013,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 15.778,
      "layouts": 60,
      "layoutDurationMs": 7.643000000000001,
      "taskDurationMs": 1489.4050000000002,
      "heapDeltaBytes": 669584,
      "heapUsedBytes": 62692128,
      "domNodes": -287,
      "jsHeapTotalBytes": 5873664,
      "scriptDurationMs": 519.9149999999998,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3146.335000000022,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 13.437999999999999,
      "layouts": 60,
      "layoutDurationMs": 7.643000000000001,
      "taskDurationMs": 1467.779,
      "heapDeltaBytes": -5871720,
      "heapUsedBytes": 56167068,
      "domNodes": -289,
      "jsHeapTotalBytes": 6397952,
      "scriptDurationMs": 507.858,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2039.527000000021,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 6.109,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 659.5389999999999,
      "heapDeltaBytes": 6811344,
      "heapUsedBytes": 68068564,
      "domNodes": -282,
      "jsHeapTotalBytes": 2990080,
      "scriptDurationMs": 88.901,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2020.9299999999075,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 6.023999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 657.086,
      "heapDeltaBytes": 6371056,
      "heapUsedBytes": 66664388,
      "domNodes": -282,
      "jsHeapTotalBytes": 3776512,
      "scriptDurationMs": 89.60300000000001,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 577.951999999982,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 9.697000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 393.312,
      "heapDeltaBytes": -10559168,
      "heapUsedBytes": 53703736,
      "domNodes": 20,
      "jsHeapTotalBytes": 25952256,
      "scriptDurationMs": 124.45899999999999,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 576.9030000000157,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 9.969999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 401.137,
      "heapDeltaBytes": -10380312,
      "heapUsedBytes": 53824428,
      "domNodes": 20,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 124.91600000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1997.5039999999922,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.784000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 446.498,
      "heapDeltaBytes": 4910764,
      "heapUsedBytes": 69151704,
      "domNodes": 20,
      "jsHeapTotalBytes": 25427968,
      "scriptDurationMs": 12.776,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.4329999999254,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.568999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 490.7,
      "heapDeltaBytes": -18706800,
      "heapUsedBytes": 45703972,
      "domNodes": 20,
      "jsHeapTotalBytes": 23310336,
      "scriptDurationMs": 12.653,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1753.7490000000275,
      "styleRecalcs": 79,
      "styleRecalcDurationMs": 33.69,
      "layouts": 16,
      "layoutDurationMs": 4.329,
      "taskDurationMs": 783.9309999999999,
      "heapDeltaBytes": -18451248,
      "heapUsedBytes": 45956232,
      "domNodes": 20,
      "jsHeapTotalBytes": 22786048,
      "scriptDurationMs": 82.727,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1721.2429999999586,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 33.783,
      "layouts": 16,
      "layoutDurationMs": 4.474,
      "taskDurationMs": 766.054,
      "heapDeltaBytes": -16719576,
      "heapUsedBytes": 47432024,
      "domNodes": -280,
      "jsHeapTotalBytes": 23048192,
      "scriptDurationMs": 83.239,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1384.555999999975,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 27.877999999999993,
      "layouts": 14,
      "layoutDurationMs": 10.775,
      "taskDurationMs": 907.046,
      "heapDeltaBytes": 1159528,
      "heapUsedBytes": 88963724,
      "domNodes": 13673,
      "jsHeapTotalBytes": 13107200,
      "scriptDurationMs": 32.44,
      "eventListeners": 2375,
      "totalBlockingTimeMs": 124,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8254.863999999998,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 38.091,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4194.518,
      "heapDeltaBytes": 10587224,
      "heapUsedBytes": 69950636,
      "domNodes": -282,
      "jsHeapTotalBytes": 3706880,
      "scriptDurationMs": 1272.199,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8195.373000000018,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 36.763999999999996,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4218.689,
      "heapDeltaBytes": 11299544,
      "heapUsedBytes": 70715116,
      "domNodes": -282,
      "jsHeapTotalBytes": 5279744,
      "scriptDurationMs": 1248.8509999999999,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 17126.934999999947,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17086.608,
      "heapDeltaBytes": -54193236,
      "heapUsedBytes": 167010856,
      "domNodes": -8312,
      "jsHeapTotalBytes": -6492160,
      "scriptDurationMs": 548.258,
      "eventListeners": -16389,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 16916.922,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16892.422000000002,
      "heapDeltaBytes": -37241640,
      "heapUsedBytes": 168989100,
      "domNodes": -8312,
      "jsHeapTotalBytes": -5447680,
      "scriptDurationMs": 512.832,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20853.160000000003,
      "styleRecalcs": 142,
      "styleRecalcDurationMs": 17.10800000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20790.675,
      "heapDeltaBytes": -29033680,
      "heapUsedBytes": 176509056,
      "domNodes": -8312,
      "jsHeapTotalBytes": -9969664,
      "scriptDurationMs": 858.71,
      "eventListeners": -16383,
      "totalBlockingTimeMs": 204,
      "frameDurationMs": 17.77333333333336,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 21122.95800000004,
      "styleRecalcs": 145,
      "styleRecalcDurationMs": 18.398999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 21071.642,
      "heapDeltaBytes": -38769404,
      "heapUsedBytes": 166908660,
      "domNodes": -8312,
      "jsHeapTotalBytes": -7544832,
      "scriptDurationMs": 859.4119999999999,
      "eventListeners": -16379,
      "totalBlockingTimeMs": 231,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 448.5730000000103,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 19.715999999999998,
      "layouts": 3,
      "layoutDurationMs": 0.7030000000000001,
      "taskDurationMs": 118.02200000000002,
      "heapDeltaBytes": 5317368,
      "heapUsedBytes": 68518952,
      "domNodes": 134,
      "jsHeapTotalBytes": 5242880,
      "scriptDurationMs": 9.819,
      "eventListeners": 97,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "workflow-execution",
      "durationMs": 466.5390000000116,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 17.162000000000003,
      "layouts": 3,
      "layoutDurationMs": 0.673,
      "taskDurationMs": 100.576,
      "heapDeltaBytes": 5073840,
      "heapUsedBytes": 68375908,
      "domNodes": 119,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 9.982,
      "eventListeners": 97,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds cloud-aware media asset deletion, linked job-asset resolution, deleted-output history tracking, and history replacement. It updates output stacks and sidebar behavior. Tests cover cloud, OSS, service, composable, store, and browser flows.

Changes

Media asset cleanup

Layer / File(s) Summary
Asset lookup and history state
src/platform/assets/utils/outputAssetUtil.ts, src/stores/assetsStore.ts, related tests
The utility resolves unique job output assets. The assets store tracks deleted output keys and replaces or removes history assets while preserving job identity.
Media deletion workflow
src/platform/assets/composables/useMediaAssetActions.ts, src/platform/assets/services/assetService.ts, related tests
Cloud deletion resolves linked asset records. OSS deletion uses history deletion. Batch deletion runs sequentially and applies refresh, cache, and history updates only to successful deletions. HTTP 404 asset deletion succeeds.
Deleted output visibility
src/platform/assets/composables/useOutputStacks.ts, related tests
Expanded output stacks exclude outputs marked as deleted in asset history.
Cloud sidebar deletion validation
browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
The browser test verifies that deleting one generated output removes only that output from the Load Image picker and does not send a history deletion request.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AssetsSidebar
  participant useMediaAssetActions
  participant findJobOutputAsset
  participant assetService
  participant assetsStore
  AssetsSidebar->>useMediaAssetActions: Delete generated output
  useMediaAssetActions->>findJobOutputAsset: Resolve linked job asset
  findJobOutputAsset->>assetService: Get job assets
  assetService-->>findJobOutputAsset: Matching asset
  findJobOutputAsset-->>useMediaAssetActions: Asset record
  useMediaAssetActions->>assetService: Delete asset
  useMediaAssetActions->>assetsStore: Mark deleted output and refresh history
  assetsStore-->>AssetsSidebar: Updated picker state
Loading

Possibly related PRs

Suggested labels: cursor-review

Suggested reviewers: jaeone94


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❓ Inconclusive The PR description and changed files are available, but the PR title and commit subjects are not provided, so the required bug-fix signal cannot be verified. Provide the PR title and commit subjects to determine whether the end-to-end regression coverage rule applies.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary fix: deleting one cloud output without deleting sibling assets.
Description check ✅ Passed The description explains the root cause, implementation, scope, regression coverage, visual evidence, and validation results in sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Adr Compliance For Entity/Litegraph Changes ✅ Passed The changed-file list contains no files under src/lib/litegraph/, src/ecs/, or graph-entity-related paths, so this check does not apply.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ir-91-output-asset-delete

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/platform/assets/composables/useMediaAssetActions.ts (1)

108-133: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Group cloud output deletions by resolved jobId before calling getJobAssetIds.

deleteAssets accepts arrays and deletes with Promise.allSettled(assetArray.map((asset) => deleteAssetApi(...))), so selecting multiple outputs from the same job calls assetService.getJobAssetIds(jobId) for each selected asset under the same job. getJobAssetIds pages using server offsets and can fail later pages with Invalid job assets pagination offset if another concurrent cleanup call deletes assets before the later page fetch resolves. Resolve jobId first, call getJobAssetIds once per unique cloud output job, and mark all assets that share that job based on that one cleanup result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/assets/composables/useMediaAssetActions.ts` around lines 108 -
133, The deleteAssets flow should group cloud output and temp assets by their
resolved jobId before cleanup. Update deleteAssetApi and its caller so
getJobAssetIds is invoked once per unique job, then apply that single deletion
result and deleteHistoryJob outcome to all selected assets sharing the job;
preserve individual input-asset deletion behavior and existing error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/platform/assets/services/assetService.ts`:
- Around line 882-921: Add the same MAX_PAGINATION_BATCHES guard used by
getAllAssetsByTag to the getJobAssetIds pagination loop, tracking batches before
each request and stopping with the existing truncated-list behavior when the
limit is reached. Preserve the current offset validation, response handling, and
normal completion paths.

---

Outside diff comments:
In `@src/platform/assets/composables/useMediaAssetActions.ts`:
- Around line 108-133: The deleteAssets flow should group cloud output and temp
assets by their resolved jobId before cleanup. Update deleteAssetApi and its
caller so getJobAssetIds is invoked once per unique job, then apply that single
deletion result and deleteHistoryJob outcome to all selected assets sharing the
job; preserve individual input-asset deletion behavior and existing error
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6d3ac5d-6ecc-4919-9f14-cd1b016b3693

📥 Commits

Reviewing files that changed from the base of the PR and between 15a8601 and 709b190.

📒 Files selected for processing (4)
  • src/platform/assets/composables/useMediaAssetActions.test.ts
  • src/platform/assets/composables/useMediaAssetActions.ts
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts

Comment thread src/platform/assets/services/assetService.ts Outdated
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.30894% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...latform/assets/composables/useMediaAssetActions.ts 95.89% 3 Missing ⚠️
src/platform/assets/composables/useOutputStacks.ts 77.77% 2 Missing ⚠️
src/platform/assets/services/assetService.ts 50.00% 1 Missing ⚠️
src/stores/assetsStore.ts 94.73% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main   #14493      +/-   ##
==========================================
- Coverage   78.71%   78.66%   -0.05%     
==========================================
  Files        1804     1803       -1     
  Lines      107023   109336    +2313     
  Branches    30683    33828    +3145     
==========================================
+ Hits        84238    86011    +1773     
- Misses      22330    22846     +516     
- Partials      455      479      +24     
Flag Coverage Δ
unit 71.58% <99.01%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/platform/assets/utils/outputAssetUtil.ts 97.69% <100.00%> (-1.38%) ⬇️
src/platform/assets/services/assetService.ts 67.07% <50.00%> (+6.03%) ⬆️
src/stores/assetsStore.ts 82.58% <94.73%> (-11.88%) ⬇️
src/platform/assets/composables/useOutputStacks.ts 77.65% <77.77%> (-17.35%) ⬇️
...latform/assets/composables/useMediaAssetActions.ts 70.78% <95.89%> (-0.55%) ⬇️

... and 219 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dante01yoon dante01yoon added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch core/1.48 labels Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts`:
- Around line 243-263: Type both mocked route payloads: annotate the jobs
response in the `/api/jobs` handler with the existing job list response type,
and annotate the job-assets payload in the corresponding job-assets route using
the shape derived from the service schema. Apply the `satisfies` annotations to
the objects passed to `route.fulfill()` while preserving the current response
values and pagination behavior.

In `@src/platform/assets/composables/useMediaAssetActions.ts`:
- Around line 120-134: Update deleteAssets to derive deletedValues from all
resolved output asset items when grouped job deletion runs, not only the
selected assetArray entries. Reuse the asset IDs returned by
assetService.getJobAssetIds(jobId) to find the corresponding [output] widget
values and preview caches, while preserving the existing selected-item behavior
for non-grouped deletions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eb8a01d2-31a8-4d5c-9f44-fcea95e62487

📥 Commits

Reviewing files that changed from the base of the PR and between 709b190 and 5c5466d.

📒 Files selected for processing (5)
  • browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
  • src/platform/assets/composables/useMediaAssetActions.test.ts
  • src/platform/assets/composables/useMediaAssetActions.ts
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts

Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
Comment thread src/platform/assets/composables/useMediaAssetActions.ts Outdated
@dante01yoon
dante01yoon force-pushed the fix/ir-91-output-asset-delete branch from 5c5466d to 2e51602 Compare July 31, 2026 20:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts`:
- Line 425: Update the route pattern in the page.route call to use
**/api/jobs/alpha/assets**, allowing requests both with and without query
parameters to match the mock.
- Around line 438-442: Filter both in-test routes by HTTP method: in
browser_tests/tests/sidebar/assetsSidebarTab.spec.ts#L438-L442, update the route
handler to process only DELETE requests before mutating deletedAssetIds and
outputAssetsByPage, calling route.fallback() for other methods; at
browser_tests/tests/sidebar/assetsSidebarTab.spec.ts#L460-L464, process only
POST requests before updating historyDeleteRequests and generatedJobsByPage,
falling back otherwise.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72e99757-ec85-4833-825b-a315f53b3e4e

📥 Commits

Reviewing files that changed from the base of the PR and between 5c5466d and 2e51602.

📒 Files selected for processing (5)
  • browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
  • src/platform/assets/composables/useMediaAssetActions.test.ts
  • src/platform/assets/composables/useMediaAssetActions.ts
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts

Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts Outdated
Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts Outdated
@dante01yoon dante01yoon changed the title Fix generated assets remaining in Load Image options Fix cloud output deletion without deleting sibling assets Aug 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts`:
- Around line 518-524: Update the assertion in the test around deleteAssets to
await the sidebar refresh and verify that getAssetCardByName('multi-output-a')
is no longer visible. Remove the contradictory visibility expectation while
preserving the existing image-picker assertions that confirm the deleted output
is gone.

In `@src/platform/assets/services/assetService.test.ts`:
- Around line 552-559: Add an invalid-response test alongside the existing
getJobAssetId cases, using fetchApiMock to return a successful response with
malformed job-assets data such as a missing pagination block or non-string id,
and assert that assetService.getJobAssetId('job-1', 'selected.png') rejects with
“Invalid job assets response.”

In `@src/platform/assets/services/assetService.ts`:
- Around line 888-897: Add a JSDoc block immediately before getJobAssetId
documenting its parameters, fail-closed null-return cases, and that transport,
schema, and pagination faults are thrown. Explicitly state that callers must
treat null as “do not delete” for deleteAssetApi, and that assetName only
verifies the uniquely matched hash rather than selecting among duplicate hashes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db4616a2-aac9-454a-acf5-0b5f51981f82

📥 Commits

Reviewing files that changed from the base of the PR and between 2e51602 and 0c4fac6.

📒 Files selected for processing (5)
  • browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
  • src/platform/assets/composables/useMediaAssetActions.test.ts
  • src/platform/assets/composables/useMediaAssetActions.ts
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts

Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
Comment thread src/platform/assets/services/assetService.test.ts Outdated
Comment thread src/platform/assets/services/assetService.ts Outdated
@christian-byrne christian-byrne added the cloud/1.49 Backport PRs for cloud 1.49 label Aug 5, 2026

@christian-byrne christian-byrne left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ran a multi-reviewer pass over this (correctness + test lenses) and verified every claim against source before writing it up. The direction is right and the lifecycle semantics are correct — deleting the asset and leaving the job alone is exactly what we want, and the Promise.allSettled → serial-loop rewrite quietly fixes a real pre-existing index-misalignment bug in the failure-logging path. Thank you for that.

But there are two blocking problems, and the second one is why the first wasn't caught.

1. Blocking — the hash comparison can never match, so cloud deletion always fails

useMediaAssetActions.ts computes const assetHash = asset.hash ?? asset.name.

Sidebar cards come from mapTaskOutputToAssetItem (src/platform/assets/composables/media/assetMappers.ts:39-51), which never sets hash. So assetHash is always output.filename — e.g. ComfyUI_00012_.png.

It's then compared against the hash field of GET /api/jobs/{job_id}/assets, which is blake3:-prefixed. This repo already documents that, in src/platform/assets/utils/outputAssetUtil.ts:138-141:

The endpoint's hash is deliberately not copied: downstream getAssetUrlFilename treats hash as the storage filename, which the endpoint's (blake3:-prefixed) value is not.

Corroborated by the existing fixture at src/platform/remote/comfyui/jobs/fetchJobs.test.ts:416hash: 'blake3:abc' (and null at :475).

So: 'blake3:9af3c1' === 'ComfyUI_00012_.png' → 0 matches → matchingAssets.length !== 1nulldeleteAssetApi throws → "Failed to delete asset". Nothing is ever deleted on the cloud sidebar path. That trades Incident 91's "deletes the wrong thing" for "deletes nothing", and it fails closed silently.

There's already a resolver for exactly this in the codebase — findOutputAsset() (src/platform/assets/utils/assetPreviewUtil.ts:41-51) goes filename → asset record via GET /api/assets?hash=<filename> (the server normalizes both hash representations) with a name_contains fallback. One request, no pagination, no ambiguity logic.

Separately worth noting: for expanded stack children the real asset UUID is already in handoverlayJobAsset (outputAssetUtil.ts:144-153) sets id: asset.id. The branch discriminator is "does user_metadata exist", so those items take the lookup path anyway and discard an id we already resolved correctly.

2. Blocking — the deleted output stays visible in Media Assets

browser_tests/tests/sidebar/assetsSidebarTab.spec.ts:522 asserts the card is still visible after deletion, then :526-530 asserts the same output is gone from the Load Image picker. Both pass, and they're not contradictory — they're describing two surfaces that genuinely disagree:

Surface Composable Endpoint
Media Assets → Generated useAssetsApi('output') (AssetsSidebarTab.vue:299) /api/jobs
Load Image / Load Video useFlatOutputAssets() (WidgetSelectDropdown.vue:63) /api/assets

mapHistoryToAssets builds every sidebar card from the JobListItem, and cloud serves preview_output/outputs_count from the job table (services/ingest/server/implementation/job.go:601). So the job surviving — which is correct per the lifecycle decision — means the sidebar keeps rendering the output, and a hard refresh doesn't clear it.

That's the mirror image of the original bug, in the panel the user clicked delete in, and it misses the incident's own acceptance criterion: "Deleted output no longer appears in Media Assets, Load Image, or Load Video after refresh and a new session."

This isn't a reason to abandon the approach — the data-level fix is right and strictly better than today. But it needs either an optimistic client-side removal from historyAssets after a successful delete, or /api/jobs needing to stop listing outputs whose asset row is gone (worth folding into the histories.outputs eviction work rather than doing standalone). To be explicit, that is not the pattern we rejected in Comfy-Org/cloud#5864 — that made asset listings depend on job state; this is the inverse, and it's legitimate.

Why CI is green

The fixtures encode a contract that contradicts the documented one. hash: 'multi-output-a.png' in the e2e mock (:446, :454) and hash: 'first.png'/'shared-hash' in assetService.test.ts are all filename-shaped, so the comparison in #1 matches in tests and can't in production. And the jobs mock never removes the deleted output, so #2 is locked in as expected behavior.

Also worth fixing

  • Schema is stricter than the endpoint. The new jobAssetsResponseSchema requires pagination and all four sub-fields non-nullable. The existing schema for the same endpoint — zJobAssetsResponse (src/platform/remote/comfyui/jobs/jobTypes.ts:127-138) — makes them optional/nullable, with a comment that the cloud serialiser drops null fields (exclude_none=True). A response without pagination throws here while enrichWithJobAssets handles it fine.
  • Duplicate implementation. fetchJobAssets (src/platform/remote/comfyui/jobs/fetchJobs.ts:202-244) already paginates this endpoint, with a 20-page cap and an LRU cache (src/services/jobOutputCache.ts:127). This adds a second one with a 1000-batch cap and no cache. Two implementations of one unconfirmed contract will drift — and a backend wrongly pinning has_more: true would fire 1000 sequential requests per delete click.
  • jobAssetsCache is never invalidated after delete, so collapsing and re-expanding a stack re-shows the deleted child.
  • assetName never disambiguates. It's only checked after narrowing to exactly one match, so two same-hash siblings are permanently undeletable rather than resolved by name. Both tests at assetService.test.ts:498 and :514 hit the earlier length !== 1 guard, so line 947 is never exercised — rewriting it to return asset.id passes all 10 tests.
  • 404-as-success is too broad. It now covers the deleteAsset(asset.id) fallback path, where asset.id may be a history-composite id — that 404s, gets counted as success, and shows "deleted successfully" while the asset is still live.
  • 409 ASSET_IN_USE (asset pinned by a workflow version) surfaces only as the generic failure toast — indistinguishable from a network error.
  • Route mocks don't filter by HTTP method, unlike mockViewFiles/mockInputFiles in the same file. **/api/assets/multi-output-a-asset-id matches any verb, so a future GET/PUT on that id would be recorded as a deletion.
  • Uncovered: bulk partial-failure (criterion 7) entirely, Load Video, post-refresh/new-session, malformed-response and non-404 HTTP errors in getJobAssetId.

Happy to take the findOutputAsset rewrite myself if that's easier — say the word. Not resolving the existing threads since the code they point at is still current.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/platform/assets/services/assetService.ts (1)

850-867: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the 404 no-op in the deleteAsset contract.

The JSDoc still states @throws Error if deletion fails. After this change a 404 resolves successfully. Callers treat a resolved promise as proof of deletion, so record that a 404 is accepted as "already absent" and does not confirm the record existed.

♻️ Proposed documentation
   /**
    * Deletes an asset by ID
    * Only available in cloud environment
    *
+   * Idempotent: a 404 response resolves successfully because the record is
+   * already absent. A resolved call therefore does not prove the record
+   * existed under the supplied id.
+   *
    * `@param` id - The asset ID (UUID)
    * `@returns` Promise<void>
-   * `@throws` Error if deletion fails
+   * `@throws` Error if the server returns a non-404 failure status
    */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/assets/services/assetService.ts` around lines 850 - 867, Update
the JSDoc for deleteAsset to document that a 404 resolves successfully as an
already-absent asset and does not confirm the record existed, while retaining
the `@throws` description for other deletion failures.
♻️ Duplicate comments (1)
browser_tests/tests/sidebar/assetsSidebarTab.spec.ts (1)

267-277: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Type the mocked jobs and job-assets payloads.

The assets route at Lines 250-256 uses satisfies ListAssetsResponse. The jobs payload here and the job-assets payload at Lines 293-298 remain untyped object literals. The job-assets payload sends pagination: { has_more: false } without offset, limit, or total. If the production schema requires those fields, the mock drifts from the contract and the failure surfaces only at run time.

Add satisfies annotations to both payloads, derived from the job list and job-assets response types.

As per coding guidelines: type mocked API responses in route.fulfill().

Also applies to Lines 293-298.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts` around lines 267 - 277,
Type both mocked response payloads in the jobs and job-assets route.fulfill
handlers using satisfies annotations with the corresponding job-list and
job-assets response types. Include all required pagination fields, including
offset, limit, and total, in the job-assets payload so both mocks conform to
their production contracts.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts`:
- Line 282: Update the route glob in the multi-output assets handler passed to
page.route so it uses **/api/jobs/multi-output/assets**, matching requests both
with and without query parameters while preserving the existing mock behavior.
- Around line 502-506: After pressing Escape in the image-picker flow, wait for
the picker itself to be hidden before calling assetsTab.open(). Update the
imagePicker locator to target the picker-specific element rather than the broad
page.getByRole('dialog') locator, so it remains unambiguous when the confirm
dialog appears later.
- Around line 522-525: Move the historyDeleteRequests assertion to after the
retrying sidebar assertions that settle the deletion flow, preserving the
existing empty-array expectation. Keep the deletedAssetIds poll unchanged and
ensure the negative history-request check runs only after the sidebar end state
is confirmed.

In `@src/platform/assets/composables/useMediaAssetActions.ts`:
- Around line 728-731: Update the hasOutputAssets calculation in the asset
deletion flow to include only assets whose corresponding results entry has
status "fulfilled", matching the hasInputAssets logic. Preserve the existing
output/temp type check so updateHistory and updateFlatOutputs run only when a
successful deletion includes an output or temporary asset.
- Around line 756-804: Wrap each per-job reconciliation body in the loop over
deletedOutputKeysByJob with its own try/catch, including resolveOutputAssetItems
and subsequent replacement logic. On failure, swallow or log the reconciliation
error and continue processing remaining jobs so the outer cleanup
steps—markDeletedAssetsAsMissingMedia, clearNodePreviewCacheForValues,
clearDeletedAssetWidgetValues, and invalidateModelsForCategory—always execute;
preserve the existing successful reconciliation behavior.

In `@src/platform/assets/composables/useOutputStacks.test.ts`:
- Around line 231-234: Update the mocks in the relevant useOutputStacks test to
use a reactive Set matching the production deletedOutputKeysByJob ref, then
remove the assets reassignment so adding the deletion key alone triggers
assetItems recomputation. Use a settled or retrying assertion for the reactive
refresh, and ensure the test title reflects that it verifies deletion-driven
updates rather than an asset-list refresh.

In `@src/platform/assets/composables/useOutputStacks.ts`:
- Around line 66-75: Extract the shared output-key computation from
isDeletedOutput and the delete action into a helper in outputAssetUtil.ts,
preserving the getOutputKey(...) fallback to the asset filename. Update both
call sites to use this helper, while leaving excludeOutputKey’s ?? undefined
behavior unchanged.

In `@src/platform/assets/utils/outputAssetUtil.ts`:
- Around line 177-182: Replace the `nodeScopedMatches` comparison with a direct
comparison of normalized node IDs, adding the proposed `normalizedNodeId` helper
alongside `nodeScopedName`. Preserve the existing uniqueness guard so exactly
one matching node-scoped asset resolves, including the intentional
null-versus-null match for node-id-less entries.

In `@src/stores/assetsStore.test.ts`:
- Around line 635-657: The replaceHistoryAsset fallback lacks behavioral
coverage. In src/stores/assetsStore.test.ts:635-657, add a test calling
replaceHistoryAsset(jobId) without a replacement and assert the corresponding
row is removed from historyAssets. In
src/platform/assets/composables/useMediaAssetActions.test.ts:1447-1498, add a
single-output deletion test asserting replaceHistoryAsset is called with 'job-1'
and undefined.

---

Outside diff comments:
In `@src/platform/assets/services/assetService.ts`:
- Around line 850-867: Update the JSDoc for deleteAsset to document that a 404
resolves successfully as an already-absent asset and does not confirm the record
existed, while retaining the `@throws` description for other deletion failures.

---

Duplicate comments:
In `@browser_tests/tests/sidebar/assetsSidebarTab.spec.ts`:
- Around line 267-277: Type both mocked response payloads in the jobs and
job-assets route.fulfill handlers using satisfies annotations with the
corresponding job-list and job-assets response types. Include all required
pagination fields, including offset, limit, and total, in the job-assets payload
so both mocks conform to their production contracts.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a871c26-57dd-4c82-91cd-0d8beacb44ea

📥 Commits

Reviewing files that changed from the base of the PR and between 24d6c30 and 539cbd7.

📒 Files selected for processing (11)
  • browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
  • src/platform/assets/composables/useMediaAssetActions.test.ts
  • src/platform/assets/composables/useMediaAssetActions.ts
  • src/platform/assets/composables/useOutputStacks.test.ts
  • src/platform/assets/composables/useOutputStacks.ts
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts
  • src/platform/assets/utils/outputAssetUtil.test.ts
  • src/platform/assets/utils/outputAssetUtil.ts
  • src/stores/assetsStore.test.ts
  • src/stores/assetsStore.ts

Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts Outdated
Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts
Comment thread browser_tests/tests/sidebar/assetsSidebarTab.spec.ts Outdated
Comment thread src/platform/assets/composables/useMediaAssetActions.ts Outdated
Comment thread src/platform/assets/composables/useMediaAssetActions.ts
Comment thread src/platform/assets/composables/useOutputStacks.test.ts
Comment thread src/platform/assets/composables/useOutputStacks.ts
Comment thread src/platform/assets/utils/outputAssetUtil.ts
Comment thread src/stores/assetsStore.test.ts

@christian-byrne christian-byrne left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR is somewhat outdated because we are moving the asset sidebar to be asset-driven rather than job-driven now. Also, we decided that deleting an asset without deleting the job has potential value. To compensate, we will explore some tooltips/warnings about the behavior (if the user expects deleting an asset also deletes the job)

@christian-byrne

Copy link
Copy Markdown
Contributor

Closing this — the approach has been overtaken by decisions made after it was opened, not because the work was wrong. Thanks @dante01yoon for two solid rounds on it, and sorry it landed this way.

Why it's superseded. The Domain Modeling Weekly on 2026-08-06 decided the Media Assets sidebar becomes asset-backed rather than jobs-backed (FE-1442, now owned by @AustinMroz, tracked under the "Assets sidebar is assets-backed" milestone). Both blockers I raised in the 08-05 review dissolve under that change rather than needing fixes here:

  • The asset.hash ?? asset.name lookup only exists because sidebar cards built from job history carry no real asset id. Asset-backed cards carry one, so the whole getJobAssetId resolution step — and the findOutputAsset() rewrite I suggested — becomes unnecessary code.
  • The "deleted output stays in the sidebar" asymmetry was the sidebar reading /api/jobs while the picker reads /api/assets. Both read /api/assets after the migration.

And the incident itself is being closed from the backend. Simon's RFC (Asset deletion should be soft and backfilled from job deletion, 2026-08-07) adds assets.deleted_at, marks on delete, backfills from job.delete_time, and filters the listing surfaces. Its steps 1–5 close Incident 91 with no frontend work at all. Fixing this PR would mean shipping client-side resolution logic that two separate workstreams are actively removing.

What should carry forward, and shouldn't be lost in the close:

  1. A real pre-existing bugfix. The old bulk-delete path did failed.forEach((r, i) => assetArray[i]), indexing the compacted failures array against the full asset array — so after the first success, every failure was attributed to the wrong asset. The serial loop here corrects that. It's independent of the deletion approach and worth landing on its own.
  2. The test coverage. The multi-output Playwright case and the unit tests around sibling preservation encode the actual acceptance criteria for the incident. They should be re-pointed at the asset-backed flow rather than deleted with the branch.
  3. The lifecycle semantics were right. No POST /api/history on cloud, job and siblings preserved. That's what the org ratified on 08-06 (delete asset → asset only), so the direction of this PR was correct and is now the documented rule.

Also worth recording, since it was found here: CI was green throughout because every fixture uses filename-shaped hashes (hash: 'multi-output-a.png', 'first.png'), which encodes a contract the repo documents as false at outputAssetUtil.ts:138-141. Whatever replaces this should not inherit those fixtures.

Follow-on work lives in FE-1520 (gate deletion on the asset_deletion_enabled capability flag), FE-1522 (how expanded stack children get asset IDs / whether they can be deleted), FE-1523 (clear-history dialog copy), and BE-6736 / BE-6741 / BE-6407 on the backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cloud/1.49 Backport PRs for cloud 1.49 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants