fix: clear workflow status when an account precondition ends a run - #15161
Conversation
A runtime INSUFFICIENT_CREDITS / WORKSPACE_INSUFFICIENT_CREDITS error is routed to its own modal and returns early from handleExecutionError, so the workflow's entry in workflowStatus keeps the 'running' value written by execution_start. resetExecutionState drops queuedJobs and jobIdToWorkflow but never touches workflowStatus, leaving every reader of getWorkflowStatus promising a result that will never arrive. Clear the status before resetExecutionState drops the jobId -> workflow mapping, matching handleExecutionInterrupted.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe execution store now clears a workflow’s running status when an account-precondition error occurs. A test verifies this behavior for ChangesWorkflow status cleanup
Estimated code review effort: 1 (Trivial) | ~5 minutes Mergeability Score: ⚪ Minimal · up to The change clears stale workflow status when an account precondition ends an accepted run and includes a focused regression test; no actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
🎭 Playwright: ✅ 1802 passed, 0 failed · 3 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 8.71 MB gzip 🔴 +7 BDetailsSummary
Category Glance App Entry Points — 3.67 kB (baseline 3.67 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.36 MB (baseline 1.36 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 2 added / 2 removed / 1 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 13 added / 13 removed / 4 unchanged Panels & Settings — 565 kB (baseline 565 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 16 unchanged User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 6 added / 6 removed / 4 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 7 added / 7 removed / 1 unchanged UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 BReusable component library chunks
Status: 6 added / 6 removed / 8 unchanged Data & Services — 3.51 MB (baseline 3.51 MB) • 🔴 +105 BStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 550 kB (baseline 550 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 18 added / 18 removed / 20 unchanged Vendor & Third-Party — 16.3 MB (baseline 16.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 17 unchanged Other — 14.1 MB (baseline 14.1 MB) • ⚪ 0 BBundles that do not match a named category
Status: 68 added / 68 removed / 217 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-12T22:51:46.272Z",
"gitSha": "870ef39da5667a6daa076e9a7ce411a5498e25e6",
"branch": "fix/clear-workflow-status-on-account-precondition-error",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2070.451999999989,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.370000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 477.09200000000004,
"heapDeltaBytes": 5585920,
"heapUsedBytes": 70111532,
"domNodes": 16,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 16.540000000000003,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2010.499999999979,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.308000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 464.1719999999999,
"heapDeltaBytes": 5784616,
"heapUsedBytes": 70384220,
"domNodes": 18,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 16.733999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1989.4819999999527,
"styleRecalcs": 77,
"styleRecalcDurationMs": 41.759,
"layouts": 12,
"layoutDurationMs": 3.9819999999999993,
"taskDurationMs": 1040.124,
"heapDeltaBytes": -12824744,
"heapUsedBytes": 51618296,
"domNodes": -284,
"jsHeapTotalBytes": 24088576,
"scriptDurationMs": 130.138,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1823.759999999993,
"styleRecalcs": 74,
"styleRecalcDurationMs": 34.941,
"layouts": 12,
"layoutDurationMs": 3.0260000000000002,
"taskDurationMs": 895.6039999999999,
"heapDeltaBytes": -14794396,
"heapUsedBytes": 49574516,
"domNodes": -280,
"jsHeapTotalBytes": 23302144,
"scriptDurationMs": 124.73799999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1675.907000000052,
"styleRecalcs": 32,
"styleRecalcDurationMs": 16.449,
"layouts": 6,
"layoutDurationMs": 0.4940000000000001,
"taskDurationMs": 394.78099999999995,
"heapDeltaBytes": 8506532,
"heapUsedBytes": 73029176,
"domNodes": 78,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 20.496,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1728.2319999999345,
"styleRecalcs": 30,
"styleRecalcDurationMs": 17.228,
"layouts": 6,
"layoutDurationMs": 0.6069999999999999,
"taskDurationMs": 389.74699999999996,
"heapDeltaBytes": 8750096,
"heapUsedBytes": 73210816,
"domNodes": 76,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 19.552999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 576.3110000000324,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.453999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 391.70099999999996,
"heapDeltaBytes": -11410068,
"heapUsedBytes": 53329088,
"domNodes": 18,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 60.85,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 581.6800000000057,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.309999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 386.677,
"heapDeltaBytes": -11177380,
"heapUsedBytes": 53186740,
"domNodes": 20,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 61.04600000000001,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2055.138999999997,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.7909999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 685.705,
"heapDeltaBytes": 7482364,
"heapUsedBytes": 66971576,
"domNodes": -283,
"jsHeapTotalBytes": 2981888,
"scriptDurationMs": 110.10600000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2020.7639999999856,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.434999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 694.462,
"heapDeltaBytes": 6897484,
"heapUsedBytes": 66473104,
"domNodes": -283,
"jsHeapTotalBytes": 3506176,
"scriptDurationMs": 107.03800000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2184.6689999999853,
"styleRecalcs": 69,
"styleRecalcDurationMs": 13.376,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1293.3590000000002,
"heapDeltaBytes": 1144320,
"heapUsedBytes": 61801968,
"domNodes": -283,
"jsHeapTotalBytes": 3698688,
"scriptDurationMs": 437.34599999999995,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2186.914999999999,
"styleRecalcs": 68,
"styleRecalcDurationMs": 12.538999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1270.997,
"heapDeltaBytes": -5917300,
"heapUsedBytes": 54682028,
"domNodes": -286,
"jsHeapTotalBytes": 3506176,
"scriptDurationMs": 431.576,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3211.19299999998,
"styleRecalcs": 63,
"styleRecalcDurationMs": 12.676999999999998,
"layouts": 60,
"layoutDurationMs": 7.147,
"taskDurationMs": 1517.643,
"heapDeltaBytes": -2461544,
"heapUsedBytes": 60229320,
"domNodes": -289,
"jsHeapTotalBytes": 6389760,
"scriptDurationMs": 538.237,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3247.463000000039,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.376,
"layouts": 60,
"layoutDurationMs": 7.236999999999999,
"taskDurationMs": 1548.7380000000003,
"heapDeltaBytes": 26538744,
"heapUsedBytes": 89426356,
"domNodes": 12,
"jsHeapTotalBytes": 8388608,
"scriptDurationMs": 574.296,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2027.99600000003,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.194000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 668.2099999999999,
"heapDeltaBytes": 7222748,
"heapUsedBytes": 68278872,
"domNodes": -284,
"jsHeapTotalBytes": 1933312,
"scriptDurationMs": 102.457,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2035.4559999999537,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.160999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 692.1579999999998,
"heapDeltaBytes": 6743460,
"heapUsedBytes": 67243184,
"domNodes": -284,
"jsHeapTotalBytes": 3768320,
"scriptDurationMs": 112.98500000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 590.4629999999997,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.413,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 403.6930000000001,
"heapDeltaBytes": -10726500,
"heapUsedBytes": 53814756,
"domNodes": 18,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 122.442,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 591.5559999999687,
"styleRecalcs": 45,
"styleRecalcDurationMs": 10.192,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 405.143,
"heapDeltaBytes": -11171532,
"heapUsedBytes": 53611468,
"domNodes": 16,
"jsHeapTotalBytes": 26214400,
"scriptDurationMs": 122.82,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-idle",
"durationMs": 2028.250000000014,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.302000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 483.69100000000003,
"heapDeltaBytes": 5927828,
"heapUsedBytes": 70360324,
"domNodes": 20,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 13.845999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2004.899000000023,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.254999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 476.0179999999999,
"heapDeltaBytes": 5810564,
"heapUsedBytes": 70427152,
"domNodes": 18,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 15.623,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1709.2979999999898,
"styleRecalcs": 77,
"styleRecalcDurationMs": 35.367000000000004,
"layouts": 16,
"layoutDurationMs": 4.072,
"taskDurationMs": 795.0619999999999,
"heapDeltaBytes": -18369132,
"heapUsedBytes": 46221816,
"domNodes": -280,
"jsHeapTotalBytes": 23302144,
"scriptDurationMs": 89.678,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1726.643000000081,
"styleRecalcs": 77,
"styleRecalcDurationMs": 37.473,
"layouts": 16,
"layoutDurationMs": 4.27,
"taskDurationMs": 769.355,
"heapDeltaBytes": -3781620,
"heapUsedBytes": 60722488,
"domNodes": 65,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 91.464,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1430.1459999999793,
"styleRecalcs": 18,
"styleRecalcDurationMs": 31.079000000000008,
"layouts": 14,
"layoutDurationMs": 12.770000000000003,
"taskDurationMs": 994.5460000000002,
"heapDeltaBytes": 31067372,
"heapUsedBytes": 100555060,
"domNodes": 13673,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 38.868,
"eventListeners": 2375,
"totalBlockingTimeMs": 147,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8389.791000000003,
"styleRecalcs": 251,
"styleRecalcDurationMs": 37.26599999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4625.717000000001,
"heapDeltaBytes": 9807680,
"heapUsedBytes": 68742916,
"domNodes": -280,
"jsHeapTotalBytes": 7106560,
"scriptDurationMs": 1451.786,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8438.444000000003,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.596,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4672.519,
"heapDeltaBytes": 15314212,
"heapUsedBytes": 74894492,
"domNodes": -285,
"jsHeapTotalBytes": 5533696,
"scriptDurationMs": 1437.297,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17279.453999999987,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17257.038,
"heapDeltaBytes": -20142260,
"heapUsedBytes": 183492764,
"domNodes": -8312,
"jsHeapTotalBytes": -10686464,
"scriptDurationMs": 603.328,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 16833.782000000043,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16816.584,
"heapDeltaBytes": -35310544,
"heapUsedBytes": 170969040,
"domNodes": -8312,
"jsHeapTotalBytes": -10428416,
"scriptDurationMs": 544.8550000000001,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 20648.707000000002,
"styleRecalcs": 142,
"styleRecalcDurationMs": 16.18699999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20602.199,
"heapDeltaBytes": -28548276,
"heapUsedBytes": 176873104,
"domNodes": -8312,
"jsHeapTotalBytes": -8400896,
"scriptDurationMs": 885.192,
"eventListeners": -16381,
"totalBlockingTimeMs": 180,
"frameDurationMs": 17.77333333333336,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20177.831999999966,
"styleRecalcs": 134,
"styleRecalcDurationMs": 15.644000000000046,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20152.151000000005,
"heapDeltaBytes": -51497244,
"heapUsedBytes": 153939172,
"domNodes": -8312,
"jsHeapTotalBytes": -12259328,
"scriptDurationMs": 858.396,
"eventListeners": -16387,
"totalBlockingTimeMs": 216,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 453.44699999998284,
"styleRecalcs": 13,
"styleRecalcDurationMs": 18.154999999999998,
"layouts": 2,
"layoutDurationMs": 0.39800000000000013,
"taskDurationMs": 108.06900000000002,
"heapDeltaBytes": 5063304,
"heapUsedBytes": 68583072,
"domNodes": 126,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 8.912999999999998,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999975,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 512.0480000000498,
"styleRecalcs": 18,
"styleRecalcDurationMs": 21.995999999999995,
"layouts": 3,
"layoutDurationMs": 1.463,
"taskDurationMs": 141.00099999999998,
"heapDeltaBytes": 5175800,
"heapUsedBytes": 68656740,
"domNodes": 145,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 9.836999999999998,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
🎨 Storybook: 🚧 Building... |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #15161 +/- ##
=======================================
Coverage 81.08% 81.08%
=======================================
Files 1882 1882
Lines 108347 108349 +2
Branches 34020 34021 +1
=======================================
+ Hits 87851 87853 +2
+ Misses 20146 20137 -9
- Partials 350 359 +9
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 7 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Filed the sibling-handler question as #15171 rather than folding it in here, since it needs a call on whether that test asserts @benjcooley if you have context on that assertion, it would unblock the issue. |
…nning Addresses @benjcooley's three SHOULD FIX items. The status watcher's `status === 'running'` branch was unconditional, and its source re-evaluates whenever the `workflowStatus` map is replaced — which `mutateStatus` does for *any* workflow — or whenever an error flag flips. On paths that drop a job without clearing its status the stale `running` survives, so each of those re-evaluations put the card back on "your result lands right here" after the acceptance-loss watcher had already failed the run. Gated on an actual transition. The comments named the wrong handler. #15161 made `handleAccountPreconditionError` clear the status, so the mid-run credits path now ends via the existing `undefined`-after-`running` branch. The paths this watcher actually covers are an accepted job dropped with no status ever written, and `handleServiceLevelError`, which drops the job and records a prompt error but never touches `workflowStatus`. The gate also removes the registration-order dependency the old comment overclaimed away: with it, the terminal branches are the only ones that write, and they overwrite unconditionally. Tests: 60 green. `stays failed when an unrelated workflow churns the status map` fails without the gate (`expected 'generating' to be 'failed'`).
|
@christian-byrne Commit |
1 similar comment
|
@christian-byrne Commit |
|
@christian-byrne Commit |
…15172) Stacked on #15091 — base is `fix/14619-tour-unaccepted-run`, merge after it. #15091 gave the first-run tour a 15s acceptance deadline, which covers a submission the backend refuses outright. It left one hole: a run that *was* accepted and then loses its job without an outcome. Two live paths do that, and **neither is the mid-run credits path this description originally led with** — thanks @benjcooley for catching that: | path | what it leaves behind | covered before this PR? | | --- | --- | --- | | accepted job dropped with **no status ever written** — the cloud "waiting for a machine" job cancelled or reconciled away | nothing | ❌ no branch can fire | | `handleServiceLevelError` ("Job has stagnated") — drops the job, records a prompt error, never touches `workflowStatus` | a stale `running` | ❌ the stale `running` shadowed the error branch | | ~~mid-run credits (`handleAccountPreconditionError`)~~ | ~~stale `running`~~ | ✅ **already covered** — #15161 clears the status, so it ends via the existing `undefined`-after-`running` branch | In the uncovered cases `runState` sits on `generating` forever and the card keeps promising "Hang tight, your result lands right here the moment it's ready." The controller had two disarms and no re-arm. This makes losing acceptance a signal in its own right: extend the existing `tourRunAccepted` watcher so true -> false fails the run when it is still `generating`. ```ts watch(tourRunAccepted, (accepted) => { if (accepted) stopAcceptDeadline() else if (runState.value === 'generating') runState.value = 'failed' }) ``` No re-armed deadline — this is immediate, not a second timer. The `'generating'` gate is what keeps healthy runs safe: every run leaves the queue when it finishes. `handleExecutionSuccess` and `handleExecutionError` write the terminal status and call `resetExecutionState` in the same synchronous handler, and the status watcher is registered before this one, so a finished run has already left `generating` by the time this fires. Test 3 below covers exactly that tick. Tests, in the existing `'a run behind a dropped socket'` describe: - accepted job removed with no status -> `failed` - accepted job, status `running`, removed (the stagnation case) -> `failed` - accepted job, status `completed` written in the same tick as the removal -> stays `succeeded` - accepted job, status `failed`, then removed -> stays `failed` The first two fail on the base branch (`expected 'generating' to be 'failed'`). Of the last two terminal-status guards, **only the `completed` one discriminates** — ungated, the `failed` case writes `'failed'` over `'failed'` and still passes. It is kept as documentation of intent, not as a guard; the earlier claim that both pinned the gate was wrong. Two tests added in review: - `stays failed when an unrelated workflow churns the status map` — pins the transition gate below. Fails without it (`expected 'generating' to be 'failed'`). - `gives up on a stagnated job that leaves an error and a stale status` — the other half of the stagnation path. Documents it; does not discriminate the gate. **Review fix:** the status watcher's `status === 'running'` branch was unconditional, and its source re-evaluates whenever the `workflowStatus` map is replaced (`mutateStatus` swaps the whole map, so *any* workflow's change re-runs it) or an error flag flips. With a stale `running` sitting there, each re-evaluation put the card back on "your result lands right here" after this watcher had already failed the run. It is now gated on an actual transition into `running`, which also removes the registration-order dependency the old comment overclaimed away. Complementary to #15161 (**merged 2026-08-13**), which fixes the same user-visible symptom from the store side by clearing the stale status. Independent of it: this is the controller refusing to promise regardless of whether the store cleans up. Both are wanted; neither duplicates the other. Requested by @benjcooley in review on #15091 (acceptance-loss watch for the mid-run credits variant) and by CodeRabbit (acceptance-loss coverage, terminal statuses stay terminal). ## Tests - 58 unit tests green in `useFirstRunTourController.test.ts` (54 existing + 4 new), full-file and each new test in isolation. - `pnpm lint`, `pnpm typecheck`, `pnpm format:check` pass. --------- Co-authored-by: Connor Byrne <c.byrne@comfy.org>
Summary
A runtime credits refusal leaves the workflow's execution status stuck at
runningforever.Changes
handleAccountPreconditionErrorinsrc/stores/executionStore.tsnow clears the workflow'sworkflowStatusentry before resetting execution state.Mechanism on
main:handleExecutionErrorcallshandleAccountPreconditionError(e.detail)and returns early when it returns true. These errors (INSUFFICIENT_CREDITS/WORKSPACE_INSUFFICIENT_CREDITS, routed viaresolveAccountPrecondition) open their own modal and are deliberately kept out of the error panel and error count.handleAccountPreconditionErroronly calledclearInitializationByJobIdthenresetExecutionState.resetExecutionStatedeletesqueuedJobs[jobId]and thejobIdToWorkflowentry, but never touchesworkflowStatus.These are runtime errors: the job was accepted, got a machine, and
execution_startalready wroterunningbefore the refusal arrives. So the status map keepsrunningindefinitely, and every reader ofgetWorkflowStatus(status badges, the first-run tour result card) promises a result that will never arrive.handleExecutionInterruptedalready does the right thing and is the precedent this copies. Ordering matters: thejobIdToWorkflowlookup and status clear must happen beforeresetExecutionState, which deletes that mapping.Review Focus
failedis intentional and matches the interrupt path — an account precondition is a gating state with its own modal, not a workflow failure, so it should leave no badge.handleServiceLevelError,handleCloudValidationError) were left alone. Service-level errors have an existing test asserting the status staysrunning, so changing that is a separate decision.src/stores/executionStore.test.ts: paid-style credits payload afterexecution_start, asserting the status goes fromrunningto cleared. Verified it fails onmain(expected 'running' to be undefined) and passes with the fix.Found while reviewing #15091. Distinct from #14619, which covers the submit-time refusal where no
prompt_idis ever issued and the status never passes throughrunningat all; this is the accepted-then-refused runtime path.