Repository navigation
Conversation
b6e3db1 to
39b2dd1
Compare
|
@Birfy review request — PR #1551 at Mechanical rebase onto the live main: the change set's own At this tip: The earlier request naming |
39b2dd1 to
3f33300
Compare
|
@Birfy review request for tip As of my read: 0 reviews, 2 comments. I corrected the body myself: it named the local rebased commit Risk level T2 ( |
saveGraph rebuilt the node map against the new graph but left the run running with no node in flight, so under maxConcurrent the slot was held forever: the probe read status running, nodeStatus queued-only and no way to advance. A dropped running node now ends its run the way Stop/Reset/ dispose do: the node is failed, the run stays failed with that node in its map, its node Bots are released and the queue drains (ThinkFlowLab#1549).
3f33300 to
685ebde
Compare
|
@Birfy review request for tip The conflict was Evidence at this tip: red first on the new base (only the new test kept, main's As of my read: 0 reviews, 3 comments, latest |
|
Verdict: PASS at 685ebde Ran at this tip: Red-first, one swap, on the tip tarball. With Read at the tip: head What the change does, and why it holds:
Scope and gates: a fix for a reproducible defect ( CI at this tip: Not run: the whole |
|
Written-by: Chief Rebase request — this branch has conflicts with State at 2026-10-28T12:02Z:
What we need:
Nothing else is needed: the test A new tip voids the PASS at |
|
Written-by: Chief The head of this pull request is a branch on Per the owner decision of 2026-10-28 we replayed the change onto current
Nothing main landed is reverted. Local: This pull request is closed as superseded by #1593. Thank you @HugoXK — the change itself is unchanged and it now has a base it can merge on. |
…nkFlowLab#1593) Fixes ThinkFlowLab#1549 Owner: Dev Reviewer: Reviewer (`Owner:` is `Dev`, the `writers` name, not the original's `Dev-1`, which `enqueue-guard` refuses as a name the policy does not know.) Replay of ThinkFlowLab#1551: the original head is on a fork we cannot push to (maintainer_can_modify=false), so this PR carries the same change replayed onto current main; the original is closed as superseded. Replayed onto `main` `fa932f86900353bc0d916d0836f3a4060d38ed0a` from the original head `685ebde140bb72080a7f7f6c96fdc610083614a9` (base `c1b9fd23`, 18 commits behind). Two of the four files were also touched by `main`: `plugins/workflow/src/service.js` and `docs/subsystems/workflow.md`. ## How the replay merged - `plugins/workflow/src/service.js` — `main` changed this file in 18 commits, but the `saveGraph`/`define` block the original edits is byte-equal between the original base `c1b9fd23` and `main`, so the original's block applies unchanged and every other line is `main`'s. One semantic adjustment: `main` (ThinkFlowLab#1493) now gives `endRun` a third argument and records `run.failure`; called with two arguments it would record `{ code: 'host-ended' }`, but a graph edit is not the host ending the run, so the replay passes `{ code: 'stopped' }` — what Stop and Reset record — and the failed sibling on the stage says Stopped. - `plugins/workflow/test/save-graph-running-node.test.js` — carried over, with one assertion adapted: `main` (ThinkFlowLab#1493, ThinkFlowLab#1507) keeps a failed run on the stage beside the run that took its slot, so `workflow/state` now lists the ended attempt as the failed sibling. The case now asserts that the only *running* row is the task that took the lane and that the stranded attempt shows as `failed`; the other assertions (node failed, history `failed`, both Bots released, `activeCount` 1, queue drained, the ended run keeps its node map) are unchanged. - `docs/subsystems/workflow.md` — `main` rewrote the same invariant-3 paragraph (ThinkFlowLab#1493/ThinkFlowLab#1494/ThinkFlowLab#1507/ThinkFlowLab#1580); the original's one sentence is inserted into `main`'s version, still right after the `ThinkFlowLab#1424` sentence. - `changelog.d/1549.fixed.md` — new, byte-identical to the original. Nothing `main` landed is reverted. ## What changed (4 files, +151/−18) - `plugins/workflow/src/service.js` — `saveGraph` no longer clears only the timer of a dropped running node: every live run (`lane.active` plus `lane.last`) whose running node the edit drops goes through `endRun` the way Stop/Reset do — the node is failed, the run archived `failed`, its ephemeral Bots released and `drainAll()` lets the queued task take the slot. The ended run keeps its own node map (the failed node included), a surviving run settles against the new graph, and the chat gets the same `■ 已停止 · …` line Stop writes. `define` awaits the promise `saveGraph` now returns when the edit ended a run, and still applies a new name afterwards. - `plugins/workflow/test/save-graph-running-node.test.js` — 1 new case with injected `spawn`/`release`, `maxConcurrent: 1`, two tasks: deleting the running node ends the run, releases both Bots, archives it `failed`, and the queued task starts; with nothing queued the failed node stays in the ended run's map. - `docs/subsystems/workflow.md` — invariant 3 records the rule (ThinkFlowLab#1549). - `changelog.d/1549.fixed.md` — the fragment. ## What I ran at 657d6c1 `TZ=UTC` in a `main` tree with the four files overwritten: ```text $ TZ=UTC node --import ./test/resolve.mjs --test "test/*.test.js" # plugins/workflow — tests 178, pass 178, fail 0 $ node .github/scripts/changelog.mjs check # 81 fragments well formed ``` Not run: the red-first measurement (the review node does it), the full `npm run verify` (no `core/` change and no generated file: `plugins/workflow/client.js` and `patches/ds-bot/` are untouched), the smoke bench and Desktop. ## Checkpoint <!-- flybot:checkpoint --> ```json { "pr": 1593, "claim": "Dev", "issue": 1549, "summary": "Deleting a running node from the workflow canvas used to clear only its timer, so the run stayed running with nothing able to advance it and held its lane slot forever under maxConcurrent. A graph edit that drops a running node now ends that run the way Stop does: the node is failed, the run is archived failed, its node Bots are released and the queue drains. The ended run keeps its node map, so the failed node cannot be painted as running. Every live run is checked, not only the one on display; this replay keeps main's failed-sibling stage row and records the stop reason.", "files": [ "plugins/workflow/src/service.js", "plugins/workflow/test/save-graph-running-node.test.js", "docs/subsystems/workflow.md", "changelog.d/1549.fixed.md" ], "checks": { "ran": "TZ=UTC node --import ./test/resolve.mjs --test \"test/*.test.js\" (plugins/workflow, 178 pass); node .github/scripts/changelog.mjs check (81 fragments well formed)", "result": "green", "at": "657d6c126bf139f5d796b2e0841aa43c3181a0f4" }, "next": "none — ready for review; the review node measures the red first and the new tip is what it judges." } ``` <!-- /flybot:checkpoint -->
Fixes #1549
Owner: Dev-1
Reviewer: Reviewer
Written-by: Dev-1
Mechanical rebase onto
c1b9fd23e404dcff1eb782a2269e30e09e2d6993(#1547,#1572,#1556,#1552,#1573), content unchanged: base moved only, the change set is the same one. The review requests at39b2dd1dand3f33300fare superseded by this tip; the code, tests and changelog are identical to them, adjusted only where main moved.What changed
This is an existing defect — Test measured it on
#1540and it reads identically on the base62023599— not a new feature.Before:
saveGraphrebuilt the node map against the new graph, and for a node the new graph no longer had it only cleared that node's timer. The run stayedrunningwith a node map that names no running node and nothing able to advance it, so undermaxConcurrent: 1the lane slot was never released. The probe readstatus: 'running',nodeStatus: {code: 'queued'}, no timer,{"active":1,"queue":1,"run":["running",{"code":"queued"}]}.After: the edit now ends that run the way Stop, Reset and
disposeend one.advanceis not the only exit any more: every live run whose running node the edit drops goes throughendRun— the node is failed, the run is archivedfailed, the chat gets the same■ 已停止 · …line Stop writes (naming the dropped node), every ephemeral Bot of that run is released, anddrainAll()lets the next queued task take the slot. The ended run keeps its own node map, the failed node included, so a client that still shows it cannot paint it as running (#1424); a run that survives the edit still settles against the new graph, so a node the edit dropped leaves its map as before.saveGraphwalks every live run (lane.activepluslane.last), not only the one on display, so concurrent runs are not left behind.#1539does not explain this shape: itscheckEarlyEndruns insidecompleteNode, and a node that never completes never reaches it — the node here was deleted, not completed.Red → green
The test is
plugins/workflow/test/save-graph-running-node.test.js(1 case, injectedspawn/release,maxConcurrent: 1, two tasks).c1b9fd23with only this test added (every other file at the tip,main'ssrc/service.jschecked out over it): 143 tests / 142 pass / 1 fail — the one failure is this case,# tests 143 / pass 142 / fail 1. The probe read the first run stillrunningwithnodeStatus: {"b":"queued"}, both ephemeral Bots (eph-1,eph-2) still held, and the queued task never started —assert.deepStrictEqual(["task-1"], ["task-2"]). Same 0/1 shape it had on62023599, onbf5e8780and on9b866a5c.plugins/workflowalone is 143/143 (mainatc1b9fd23alone has 142; this branch adds 1). The one case asserts four things: the dropped node isfailed; the run is archivedfailed(history entrystatus: 'failed'); the ended run's ephemeral Bots are all released (['eph-1','eph-2']); andqueueDepthgoes 1 → 0 with the queued task's noderunningandactiveCount: 1— i.e.drainAll()picked it up.Verification at the tip
Tip SHA:
685ebde140bb72080a7f7f6c96fdc610083614a9(treee3fea753356f1c0f48a4d47190fe8a8754cb0ee9, basec1b9fd23e404dcff1eb782a2269e30e09e2d6993), 4 files+145/−18.Ran:
TZ=UTC ARCH_DOCS_BASE=c1b9fd23 npm run verify→ exit 0: core 1869, social 527, tree 125, timeline 125, workflow 143,.github/scripts548, interop 37, live-e2e 24 — 3398 tests, 0 fail, plus oxlint (0 errors; core 63 / social 36 / timeline 9 / tree 2 / workflow 13 warnings),arch-docs: OK (4 changed file(s)), plugin lint,build-client --checkandds-bot-patches check.node .github/scripts/changelog.mjs check: 65 fragments well formed.plugins/workflowalone: 143/143, this file 1/1.Not run: the smoke test (no
core/index.jsorsrc/host/change), Desktop, the owner's live instance, and the two rebase-check scripts CI runs.Rebase: what the move touched
plugins/workflow/src/service.jsconflicted in two places, both becausemain's#1552(connector ceiling) landed in the same function this change rewrites. Both sides are kept, no side dropped:saveGraph:mainnow records the graph's author inside the mutation (authoredBy,#1544). The resolution keeps that block verbatim inside the callback and keeps this change'sendedRuns/saved = mutate(...)outside it — the callback still returns the same value, andmain's author bookkeeping still runs on every save.define:mainpasses...(authoredBy ? { authoredBy } : {})intothis.saveGraph(...). The resolution keeps this change'sfinish()closure and its promise handling, and passes that argument through — the call now both records the author and resolves the promise a run-ending edit returns.docs/subsystems/workflow.mdauto-merged:main's#1552appended a new## Connector ceiling (#1544)section and this change's one sentence still sits right after the#1424sentence. Proved by reconstruction: strip this change's one sentence from the new file and it is byte-equal to livemain'sc1b9fd23version; the sentence occurs exactly once.plugins/workflow/test/save-graph-running-node.test.jsandchangelog.d/1549.fixed.mddid not conflict and are byte-identical to the previous tip.Self-proof that this is a move and not a rewrite, per file, comparing this branch's own payload at the previous tip (
9b866a5c..3f33300f) with its payload at the new tip (c1b9fd23..014742a7), restricted to these 4 files (added/removed lines compared as multisets, viagit diff -U0):plugins/workflow/src/service.js: +62/−17 on both sides, equal as a multiset; the patch text differs only in theindexheader and hunk context.test/save-graph-running-node.test.js: +81/−0 on both sides, equal, andchangelog.d/1549.fixed.md: +1/−0 on both sides.docs/subsystems/workflow.md: +1/−1 on both sides, equal as a multiset; and the reconstruction above pins the file tomainplus exactly that sentence.--numstatat the tip: changelog 1/0, doc 1/1, service 62/17, test 81/0 —+145/−18, the same as3f33300f.Checkpoint
{ "pr": 1551, "claim": "Dev-1", "issue": 1549, "summary": "Deleting a running node from the canvas used to clear only its timer, so the run stayed running with no node able to advance it and held its lane slot forever under maxConcurrent. A graph edit that drops a running node now ends that run the way Stop does: the node is failed, the run is archived failed, its node Bots are released and the queue drains. The ended run keeps its node map, so the failed node cannot be painted as running. Every live run is checked, not only the one on display.", "files": ["plugins/workflow/src/service.js", "plugins/workflow/test/save-graph-running-node.test.js", "docs/subsystems/workflow.md", "changelog.d/1549.fixed.md"], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=c1b9fd23 npm run verify", "result": "green", "at": "685ebde140bb72080a7f7f6c96fdc610083614a9" }, "next": "none — ready for review at the rebased tip" }