Repository navigation
fix(workflow): drop a deleted workflow's task claims so the task can match again - #1633
Conversation
delete() judged every claim by the route map it had just emptied, so the condition never held and the claim stayed on disk. ingest() then trusted the dangling claim and asked assign() for a workflow that no longer existed, leaving the task unmatched forever, across reloads. Judge a claim on its own record, kind included, so a Bot claim that shares the id is kept. The load path also drops a workflow claim whose workflow is gone, so a store written before this fix heals on the next start. Fixes ThinkFlowLab#1632
15f06a6 to
c7dfaa9
Compare
|
@Birfy review request for #1633 at
|
|
Verdict: PASS at c7dfaa9 Ran at this tip: The last two lines are the red-first check: the same tip tree with only Scope: tip vs base Review of the change:
Checks at this tip (2026-10-29T05:0xZ): 16 check runs, all Not run: the full |
Fixes #1632
Owner: Dev3
Written-by: Dev-3
Reviewer: Reviewer
Branch:
dev/1632-workflow-delete-leaves-the-task-claims-bBase:
331dd62068d41d1a8bc5e9c9d8d6e3d42b6d7d34(main)Tip:
c7dfaa914c4b0d5da5cd7349b7253c706f9c2b57Tree:
ad0db5007d71f4002a7db6d0b12790f725ba4c89What happens
Delete a Workflow that has already claimed a board task, and the claim stays in the store. From then on that task never matches anything again:
ingestreads the stale claim, skips matching, and callsassign()with the id of the Workflow that no longer exists, so the call ends{ok:false, error:'No such workflow'}. The task is stuck across reloads because the claim is what got written to disk, andclient.jsonly readsvalue.workflows, so the screen shows nothing at all.Why
plugins/workflow/src/service.js,delete()→finish():idis the Workflow being deleted;state.routes[taskId]?.id === workflowId— but those routes were removed a line earlier, so the test is always false and no claim is ever deleted;ingest()then trusts any claim (claimed?.kind === 'bot' || claimed?.kind === 'workflow') and goes straight toassign({ workflowId: <deleted id> }).The two maps do not hold the same keys:
rememberClaim()writes both, butingest()'s already-queued path writesroutesonly, so after normal writesroutes ⊇ claims— which is exactly why judging claims by routes was already the wrong test. This predates #1622: the same order is in3dc44ec9.What changed
finish()judges each claim on its own record:claim?.kind === 'workflow' && claim.id === workflowId. Thekindis checked too, so a Bot claim whose id happens to equal the Workflow's keeps its claim.migrateRuns()(which runs on every load) drops akind === 'workflow'claim whose Workflow is no longer instate.workflows, so a store already poisoned by this bug heals on the next start. This is the optional part of the issue; the sweep sits inmigrateRuns()before itslaneFormatearly return, so it also runs for stores the old bug wrote.docs/subsystems/workflow.mdlists claims among whatdeletecleans;changelog.d/1632.fixed.mdcarries the user-visible change.Red first
Test-only commit
15f06a68f94cb13df6328b9a6bb88ab8fdbc1f7c— onlyplugins/workflow/test/delete-claims.test.js, source untouched, on live base331dd62068d41d1a8bc5e9c9d8d6e3d42b6d7d34:cd plugins/workflow && node --import ./test/resolve.mjs --test test/delete-claims.test.js— 3 tests / 1 pass / 2 fail:The fourth case (a store already poisoned by the old
delete) arrived with the fix; it fails on its own when only the load sweep is removed, and the third case pins thekindcheck: a Bot claim that shares the id must survive the delete.Green at this tip
TZ=UTC ARCH_DOCS_BASE=331dd62068d41d1a8bc5e9c9d8d6e3d42b6d7d34 npm run verify— exit 0: core 1916, social 531, tree 125, timeline 129, workflow 234 (was 230; +4 from this test file),.github549, interop 37, live-e2e 24, every suite# fail 0; oxlint 0 errors (warnings only, unchanged in kind: core 64, social 36, timeline 9, tree 2, workflow 15,.github1, interop 0);arch-docs: OK (4 changed file(s));ds-bot-patches check→ok 10 patches apply cleanly in order+ok core/ equals ds-bot/ + patches/ds-bot/;changelog: 13 fragment(s) in changelog.d/ are well formed.Not run: the smoke test and the test bench (no
index.js/core/src/host/change), and no browser check — the defect and the fix are both asserted on the service and its store file. CI checks on this tip are not waited for (the repository queue was reported stuck since 03:54Z); the gate here is the localverifyabove.Risk:
plugins/workflow/src/service.js— the tier is computed byrisk-gatefrom the diff.Checkpoint
{ "pr": 1633, "claim": "Dev3", "issue": 1632, "summary": "Deleting a Workflow left every task claim it had recorded in the store, because the claim loop tested the route map that had just been emptied. The next ingest trusted the dangling claim and asked assign() for a Workflow that no longer existed, so the task never matched again. The fix judges each claim on its own record, kind included, and drops workflow claims whose Workflow is gone when the store loads.", "files": [ "plugins/workflow/src/service.js", "plugins/workflow/test/delete-claims.test.js", "docs/subsystems/workflow.md", "changelog.d/1632.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=331dd62068d41d1a8bc5e9c9d8d6e3d42b6d7d34 npm run verify", "result": "green", "at": "c7dfaa914c4b0d5da5cd7349b7253c706f9c2b57" }, "next": "none — ready for review" }