Skip to content

feat(assets): classify jobs cursor rejection via structured errorCode#13781

Open
mattmillerai wants to merge 2 commits into
matt/fe-962-fe-adopt-cursor-pagination-in-the-generated-tab-jobs-sourcedfrom
matt/be-3422-invalid-cursor-errorcode
Open

feat(assets): classify jobs cursor rejection via structured errorCode#13781
mattmillerai wants to merge 2 commits into
matt/fe-962-fe-adopt-cursor-pagination-in-the-generated-tab-jobs-sourcedfrom
matt/be-3422-invalid-cursor-errorcode

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

ELI-5

When you scroll the Generated tab, the app pages through your job history with a
"cursor". If that cursor goes stale (e.g. after a server restart) the server
rejects the page with a 400 and the app restarts the walk from the beginning so
you don't get skipped or duplicated rows. Today the app treats any 400 on that
request as "stale cursor". That's fine right now because a rejected cursor is the
only 400 the endpoint can return — but if the endpoint ever starts returning 400
for other reasons, an unrelated error would wrongly nuke your loaded history back
to the top. This teaches the app to read the server's actual machine-readable
error code and only reset when it really is a rejected cursor.

Summary

Classify a rejected jobs pagination cursor by the server's structured error code
instead of the bare HTTP 400 status, so future non-cursor 400s don't trigger the
history offset fallback.

Changes

  • What:
    • JobsApiError now parses the machine-readable code from the JSON error
      body and exposes it as errorCode (undefined when the body isn't the
      structured error shape, e.g. a proxy error page).
    • assetsStore.isRejectedCursorError narrows from status === 400 to
      errorCode === 'INVALID_CURSOR'.
    • Recovery tests construct errors with realistic JSON bodies; added coverage
      for a non-JSON body (no errorCode) and a non-cursor 400 (cursor preserved).
  • Breaking: none — behavior is identical for today's traffic (see below).

Review Focus

  • Server contract confirmed first. The /jobs list endpoint returns its
    errors as a structured JSON body carrying a machine-readable code field, and
    emits INVALID_CURSOR for a rejected pagination cursor. That endpoint's only
    400 today is the rejected cursor, so old (status === 400) and new
    (errorCode === 'INVALID_CURSOR') conditions cannot disagree for current
    traffic — no behavior change now. The narrowing is defense-in-depth for when
    the endpoint gains other 400 cases, and is strictly more correct: an unrelated
    400 or a non-JSON proxy 400 no longer resets the walk to offset 0.
  • Judgment call: the deferred review note suggested parsing the code in
    fetchJobsRaw. I parse it in the JobsApiError constructor instead — the body
    already flows in there, so the single throw site is unchanged and the parsing
    lives with the field it populates. Same observable result.
  • Stacked on the cursor-pagination PR (its branch is the base) since
    JobsApiError / isRejectedCursorError only exist there; GitHub retargets to
    main once that merges.

Verification

  • vitest run on fetchJobs.test.ts + assetsStore.test.ts: 124 passed.
  • eslint + oxfmt clean on the four changed files.
  • Type-check introduces zero new errors (identical error count with the change
    stashed vs applied, against the base branch).

Parse the machine-readable code from the /jobs JSON error body onto
JobsApiError.errorCode and narrow the store's cursor-recovery trigger to
errorCode === 'INVALID_CURSOR' instead of any 400. Defense-in-depth for
when the endpoint gains other 400 cases; a non-cursor 400 or a non-JSON
proxy error no longer resets the history walk to offset 0.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30ce81cd-4270-4a72-bbdb-1a79766c69d4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-3422-invalid-cursor-errorcode

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

@mattmillerai mattmillerai added agent-coded Opened by the agent-work code loop cursor-review Trigger multi-model Cursor agent review on this PR labels Jul 18, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 18, 2026 01:48
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jul 18, 2026
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1691 passed, 0 failed · 1 flaky

📊 Browser Reports
  • chromium: View Report (✅ 1670 / ❌ 0 / ⚠️ 1 / ⏭️ 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)

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/18/2026, 03:46:47 AM UTC

Links

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.8 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.1 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.7 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.8 MB heap
large-graph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.6 MB heap
large-graph-pan: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 77.0 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.2 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 57.6 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.4 MB heap
subgraph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.4 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 48.0 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 122ms TBT · 77.0 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.9 MB heap
vue-large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 164.1 MB heap
vue-large-graph-pan: · 59.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 161.1 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.2 MB heap

ℹ️ No baseline found — significance unavailable.

Absolute values
Metric Value
canvas-idle: avg frame time 17ms
canvas-idle: p95 frame time 17ms
canvas-idle: layout duration 0ms
canvas-idle: style recalc duration 6ms
canvas-idle: layout count 0
canvas-idle: style recalc count 11
canvas-idle: task duration 328ms
canvas-idle: script duration 13ms
canvas-idle: TBT 0ms
canvas-idle: heap used 55.8 MB
canvas-idle: DOM nodes -278
canvas-idle: event listeners -196
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 2ms
canvas-mouse-sweep: style recalc duration 25ms
canvas-mouse-sweep: layout count 12
canvas-mouse-sweep: style recalc count 74
canvas-mouse-sweep: task duration 586ms
canvas-mouse-sweep: script duration 78ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 51.1 MB
canvas-mouse-sweep: DOM nodes -250
canvas-mouse-sweep: event listeners -197
canvas-zoom-sweep: avg frame time 17ms
canvas-zoom-sweep: p95 frame time 17ms
canvas-zoom-sweep: layout duration 0ms
canvas-zoom-sweep: style recalc duration 12ms
canvas-zoom-sweep: layout count 6
canvas-zoom-sweep: style recalc count 31
canvas-zoom-sweep: task duration 282ms
canvas-zoom-sweep: script duration 14ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 62.7 MB
canvas-zoom-sweep: DOM nodes -222
canvas-zoom-sweep: event listeners -182
dom-widget-clipping: avg frame time 17ms
dom-widget-clipping: p95 frame time 17ms
dom-widget-clipping: layout duration 0ms
dom-widget-clipping: style recalc duration 6ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 12
dom-widget-clipping: task duration 284ms
dom-widget-clipping: script duration 38ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 56.8 MB
dom-widget-clipping: DOM nodes -290
dom-widget-clipping: event listeners -202
large-graph-idle: avg frame time 17ms
large-graph-idle: p95 frame time 17ms
large-graph-idle: layout duration 0ms
large-graph-idle: style recalc duration 6ms
large-graph-idle: layout count 0
large-graph-idle: style recalc count 11
large-graph-idle: task duration 430ms
large-graph-idle: script duration 58ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 56.6 MB
large-graph-idle: DOM nodes -260
large-graph-idle: event listeners -195
large-graph-pan: avg frame time 17ms
large-graph-pan: p95 frame time 17ms
large-graph-pan: layout duration 0ms
large-graph-pan: style recalc duration 11ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 68
large-graph-pan: task duration 751ms
large-graph-pan: script duration 260ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 77.0 MB
large-graph-pan: DOM nodes -272
large-graph-pan: event listeners -96
large-graph-zoom: avg frame time 17ms
large-graph-zoom: p95 frame time 17ms
large-graph-zoom: layout duration 6ms
large-graph-zoom: style recalc duration 13ms
large-graph-zoom: layout count 60
large-graph-zoom: style recalc count 65
large-graph-zoom: task duration 888ms
large-graph-zoom: script duration 306ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 63.2 MB
large-graph-zoom: DOM nodes 13
large-graph-zoom: event listeners 7
minimap-idle: avg frame time 17ms
minimap-idle: p95 frame time 17ms
minimap-idle: layout duration 0ms
minimap-idle: style recalc duration 6ms
minimap-idle: layout count 0
minimap-idle: style recalc count 8
minimap-idle: task duration 391ms
minimap-idle: script duration 55ms
minimap-idle: TBT 0ms
minimap-idle: heap used 57.6 MB
minimap-idle: DOM nodes -129
minimap-idle: event listeners -98
subgraph-dom-widget-clipping: avg frame time 17ms
subgraph-dom-widget-clipping: p95 frame time 17ms
subgraph-dom-widget-clipping: layout duration 0ms
subgraph-dom-widget-clipping: style recalc duration 8ms
subgraph-dom-widget-clipping: layout count 0
subgraph-dom-widget-clipping: style recalc count 48
subgraph-dom-widget-clipping: task duration 278ms
subgraph-dom-widget-clipping: script duration 82ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 55.4 MB
subgraph-dom-widget-clipping: DOM nodes -124
subgraph-dom-widget-clipping: event listeners -93
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 6ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 11
subgraph-idle: task duration 310ms
subgraph-idle: script duration 12ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 56.4 MB
subgraph-idle: DOM nodes -122
subgraph-idle: event listeners -95
subgraph-mouse-sweep: avg frame time 17ms
subgraph-mouse-sweep: p95 frame time 17ms
subgraph-mouse-sweep: layout duration 3ms
subgraph-mouse-sweep: style recalc duration 25ms
subgraph-mouse-sweep: layout count 16
subgraph-mouse-sweep: style recalc count 77
subgraph-mouse-sweep: task duration 502ms
subgraph-mouse-sweep: script duration 57ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 48.0 MB
subgraph-mouse-sweep: DOM nodes -84
subgraph-mouse-sweep: event listeners -97
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 9ms
subgraph-transition-enter: style recalc duration 21ms
subgraph-transition-enter: layout count 6
subgraph-transition-enter: style recalc count 16
subgraph-transition-enter: task duration 576ms
subgraph-transition-enter: script duration 19ms
subgraph-transition-enter: TBT 122ms
subgraph-transition-enter: heap used 77.0 MB
subgraph-transition-enter: DOM nodes 13833
subgraph-transition-enter: event listeners 2531
viewport-pan-sweep: avg frame time 17ms
viewport-pan-sweep: p95 frame time 17ms
viewport-pan-sweep: layout duration 0ms
viewport-pan-sweep: style recalc duration 38ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 251
viewport-pan-sweep: task duration 2701ms
viewport-pan-sweep: script duration 842ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 67.9 MB
viewport-pan-sweep: DOM nodes -264
viewport-pan-sweep: event listeners -179
vue-large-graph-idle: avg frame time 17ms
vue-large-graph-idle: p95 frame time 17ms
vue-large-graph-idle: layout duration 0ms
vue-large-graph-idle: style recalc duration 0ms
vue-large-graph-idle: layout count 0
vue-large-graph-idle: style recalc count 0
vue-large-graph-idle: task duration 7647ms
vue-large-graph-idle: script duration 352ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 164.1 MB
vue-large-graph-idle: DOM nodes -3311
vue-large-graph-idle: event listeners -16374
vue-large-graph-pan: avg frame time 17ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 17ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 64
vue-large-graph-pan: task duration 9213ms
vue-large-graph-pan: script duration 646ms
vue-large-graph-pan: TBT 0ms
vue-large-graph-pan: heap used 161.1 MB
vue-large-graph-pan: DOM nodes -3292
vue-large-graph-pan: event listeners -11891
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 1ms
workflow-execution: style recalc duration 17ms
workflow-execution: layout count 5
workflow-execution: style recalc count 18
workflow-execution: task duration 108ms
workflow-execution: script duration 13ms
workflow-execution: TBT 0ms
workflow-execution: heap used 53.2 MB
workflow-execution: DOM nodes -59
workflow-execution: event listeners -53
Raw data
{
  "timestamp": "2026-07-18T03:55:04.440Z",
  "gitSha": "7f74d57aae5b1e4d168db9eb258681b8b053a097",
  "branch": "matt/be-3422-invalid-cursor-errorcode",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2053.0270000000146,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.650000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 320.721,
      "heapDeltaBytes": 13198912,
      "heapUsedBytes": 65380984,
      "domNodes": -267,
      "jsHeapTotalBytes": 16117760,
      "scriptDurationMs": 13.222999999999999,
      "eventListeners": -195,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2008.0490000000282,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 5.777000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 335.552,
      "heapDeltaBytes": -1008260,
      "heapUsedBytes": 51644064,
      "domNodes": -289,
      "jsHeapTotalBytes": 16642048,
      "scriptDurationMs": 12.129000000000001,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1706.8780000000174,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 25.193,
      "layouts": 12,
      "layoutDurationMs": 2.313,
      "taskDurationMs": 606.383,
      "heapDeltaBytes": -12757496,
      "heapUsedBytes": 56615216,
      "domNodes": -240,
      "jsHeapTotalBytes": 20344832,
      "scriptDurationMs": 79.458,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1720.5670000000168,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 24.539,
      "layouts": 12,
      "layoutDurationMs": 2.1060000000000003,
      "taskDurationMs": 564.8879999999999,
      "heapDeltaBytes": -1737080,
      "heapUsedBytes": 50454316,
      "domNodes": -259,
      "jsHeapTotalBytes": 16904192,
      "scriptDurationMs": 77.016,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1709.0319999999792,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 11.883999999999999,
      "layouts": 6,
      "layoutDurationMs": 0.436,
      "taskDurationMs": 284.112,
      "heapDeltaBytes": -3725748,
      "heapUsedBytes": 65704900,
      "domNodes": -222,
      "jsHeapTotalBytes": 13529088,
      "scriptDurationMs": 13.958000000000002,
      "eventListeners": -182,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1744.0879999999765,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 12.575000000000003,
      "layouts": 6,
      "layoutDurationMs": 0.51,
      "taskDurationMs": 280.04400000000004,
      "heapDeltaBytes": -3550560,
      "heapUsedBytes": 65856952,
      "domNodes": -222,
      "jsHeapTotalBytes": 13266944,
      "scriptDurationMs": 13.918999999999999,
      "eventListeners": -182,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 500.583000000006,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 5.639,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 298.016,
      "heapDeltaBytes": 5616112,
      "heapUsedBytes": 57829804,
      "domNodes": -289,
      "jsHeapTotalBytes": 10350592,
      "scriptDurationMs": 38.57,
      "eventListeners": -203,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 413.68599999998423,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 5.433999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 270.12199999999996,
      "heapDeltaBytes": 8931964,
      "heapUsedBytes": 61188216,
      "domNodes": -291,
      "jsHeapTotalBytes": 7204864,
      "scriptDurationMs": 37.414,
      "eventListeners": -201,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2028.0400000000327,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.232000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 438.0729999999999,
      "heapDeltaBytes": -24360820,
      "heapUsedBytes": 59578232,
      "domNodes": -259,
      "jsHeapTotalBytes": 2707456,
      "scriptDurationMs": 59.643,
      "eventListeners": -195,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2026.7839999999637,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 5.693999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 421.934,
      "heapDeltaBytes": -25155636,
      "heapUsedBytes": 59138328,
      "domNodes": -261,
      "jsHeapTotalBytes": 2183168,
      "scriptDurationMs": 56.188999999999986,
      "eventListeners": -195,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2049.88400000002,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 11.573000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 773.182,
      "heapDeltaBytes": 12847436,
      "heapUsedBytes": 80824712,
      "domNodes": -271,
      "jsHeapTotalBytes": -1105920,
      "scriptDurationMs": 262.434,
      "eventListeners": -195,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2046.7800000000125,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 10.555,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 729.165,
      "heapDeltaBytes": 27236416,
      "heapUsedBytes": 80592068,
      "domNodes": -273,
      "jsHeapTotalBytes": 466944,
      "scriptDurationMs": 258.355,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3037.360999999976,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 13.541,
      "layouts": 60,
      "layoutDurationMs": 6.082,
      "taskDurationMs": 912.4590000000001,
      "heapDeltaBytes": 11744756,
      "heapUsedBytes": 66588024,
      "domNodes": 12,
      "jsHeapTotalBytes": 2883584,
      "scriptDurationMs": 316.394,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3041.058000000021,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 11.988999999999999,
      "layouts": 60,
      "layoutDurationMs": 5.091,
      "taskDurationMs": 862.833,
      "heapDeltaBytes": 11731416,
      "heapUsedBytes": 66046576,
      "domNodes": 14,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 295.39200000000005,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2043.7539999999785,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.099000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 409.95300000000003,
      "heapDeltaBytes": -2312996,
      "heapUsedBytes": 59144028,
      "domNodes": -271,
      "jsHeapTotalBytes": -557056,
      "scriptDurationMs": 55.62200000000001,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "minimap-idle",
      "durationMs": 1991.9840000000022,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 3.6720000000000015,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 371.48099999999994,
      "heapDeltaBytes": 7100644,
      "heapUsedBytes": 61708032,
      "domNodes": 14,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 53.385000000000005,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 438.4110000000305,
      "styleRecalcs": 49,
      "styleRecalcDurationMs": 7.952000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 261.718,
      "heapDeltaBytes": 8202984,
      "heapUsedBytes": 67025820,
      "domNodes": 24,
      "jsHeapTotalBytes": 18350080,
      "scriptDurationMs": 82.228,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 441.55600000004824,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 7.414000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 293.60600000000005,
      "heapDeltaBytes": -2757724,
      "heapUsedBytes": 49132596,
      "domNodes": -271,
      "jsHeapTotalBytes": 1175552,
      "scriptDurationMs": 81.16,
      "eventListeners": -193,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2033.2109999999943,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 5.8,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 330.214,
      "heapDeltaBytes": -8112664,
      "heapUsedBytes": 61235668,
      "domNodes": -266,
      "jsHeapTotalBytes": 19820544,
      "scriptDurationMs": 11.671,
      "eventListeners": -195,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2003.3900000000244,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.103999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 290.31600000000003,
      "heapDeltaBytes": -1825576,
      "heapUsedBytes": 56997820,
      "domNodes": 22,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 11.437000000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1706.4279999999599,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 27.051000000000002,
      "layouts": 16,
      "layoutDurationMs": 2.702,
      "taskDurationMs": 522.2130000000001,
      "heapDeltaBytes": -17139864,
      "heapUsedBytes": 52312216,
      "domNodes": -231,
      "jsHeapTotalBytes": 19820544,
      "scriptDurationMs": 57.826,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1691.1670000000072,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 23.009,
      "layouts": 16,
      "layoutDurationMs": 2.3649999999999998,
      "taskDurationMs": 481.774,
      "heapDeltaBytes": -10507240,
      "heapUsedBytes": 48357108,
      "domNodes": 64,
      "jsHeapTotalBytes": 25427968,
      "scriptDurationMs": 57.145999999999994,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 848.479999999995,
      "styleRecalcs": 16,
      "styleRecalcDurationMs": 21.379,
      "layouts": 6,
      "layoutDurationMs": 8.581999999999999,
      "taskDurationMs": 575.8149999999999,
      "heapDeltaBytes": 4396480,
      "heapUsedBytes": 80720348,
      "domNodes": 13833,
      "jsHeapTotalBytes": 16515072,
      "scriptDurationMs": 19.062,
      "eventListeners": 2531,
      "totalBlockingTimeMs": 122,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8135.240000000011,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 41.098000000000006,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 2659.645,
      "heapDeltaBytes": -15829304,
      "heapUsedBytes": 68016200,
      "domNodes": -268,
      "jsHeapTotalBytes": 6320128,
      "scriptDurationMs": 804.307,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8099.950000000035,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 33.999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 2743.195,
      "heapDeltaBytes": 7286584,
      "heapUsedBytes": 74363380,
      "domNodes": -260,
      "jsHeapTotalBytes": 5447680,
      "scriptDurationMs": 879.74,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 7922.122000000001,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 7883.895,
      "heapDeltaBytes": -21641372,
      "heapUsedBytes": 173893324,
      "domNodes": -3331,
      "jsHeapTotalBytes": 23826432,
      "scriptDurationMs": 356.28900000000004,
      "eventListeners": -16378,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 7470.566000000019,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 7410.847000000001,
      "heapDeltaBytes": -33201608,
      "heapUsedBytes": 170179116,
      "domNodes": -3291,
      "jsHeapTotalBytes": 19398656,
      "scriptDurationMs": 346.764,
      "eventListeners": -16370,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 9416.074999999979,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 17.469999999999985,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 9393.162999999999,
      "heapDeltaBytes": -24201276,
      "heapUsedBytes": 169028952,
      "domNodes": -3291,
      "jsHeapTotalBytes": 20971520,
      "scriptDurationMs": 667.397,
      "eventListeners": -9972,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 9068.934000000012,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 15.755999999999993,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 9032.37,
      "heapDeltaBytes": -21734400,
      "heapUsedBytes": 168721096,
      "domNodes": -3292,
      "jsHeapTotalBytes": 20709376,
      "scriptDurationMs": 624.0740000000001,
      "eventListeners": -13810,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333297,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 429.44599999998445,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 18.186,
      "layouts": 5,
      "layoutDurationMs": 1.2799999999999998,
      "taskDurationMs": 136.781,
      "heapDeltaBytes": -6737912,
      "heapUsedBytes": 46494300,
      "domNodes": -276,
      "jsHeapTotalBytes": 1175552,
      "scriptDurationMs": 13.583,
      "eventListeners": -175,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "workflow-execution",
      "durationMs": 434.2670000000908,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 15.457,
      "layouts": 4,
      "layoutDurationMs": 0.849,
      "taskDurationMs": 78.92099999999999,
      "heapDeltaBytes": 5181896,
      "heapUsedBytes": 65111160,
      "domNodes": 159,
      "jsHeapTotalBytes": 3145728,
      "scriptDurationMs": 12.757000000000001,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 3 finding(s).

Severity Count
🟡 Medium 1
🟢 Low 2

Panel: 6/8 reviewers contributed findings.

Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)

Comment thread src/stores/assetsStore.ts Outdated
Comment thread src/stores/assetsStore.ts Outdated
Comment thread src/platform/remote/comfyui/jobs/fetchJobs.ts
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

@@                                             Coverage Diff                                              @@
##           matt/fe-962-fe-adopt-cursor-pagination-in-the-generated-tab-jobs-sourced   #13781      +/-   ##
============================================================================================================
- Coverage                                                                     76.26%   75.80%   -0.47%     
============================================================================================================
  Files                                                                          1687     1637      -50     
  Lines                                                                        116777   110484    -6293     
  Branches                                                                      37481    36525     -956     
============================================================================================================
- Hits                                                                          89063    83751    -5312     
+ Misses                                                                        26969    25852    -1117     
- Partials                                                                        745      881     +136     
Flag Coverage Δ
unit 65.43% <100.00%> (-1.86%) ⬇️

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

Files with missing lines Coverage Δ
src/platform/remote/comfyui/jobs/fetchJobs.ts 95.45% <100.00%> (+0.35%) ⬆️
src/stores/assetsStore.ts 70.19% <100.00%> (+0.03%) ⬆️

... and 552 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.

Address Cursor review findings on the errorCode narrowing:
- Bound the error body before JSON.parse so an oversized (multi-MB) error
  payload can't block the UI thread synchronously.
- Require status === 400 alongside errorCode === 'INVALID_CURSOR', since a
  rejected cursor is only ever a 400; the same code on another status is a
  real server error and must not reset the walk to the offset fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded Opened by the agent-work code loop cursor-review Trigger multi-model Cursor agent review on this PR size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant