fix(kanban): route unblock_task through parent-dependency gate - #22405
fix(kanban): route unblock_task through parent-dependency gate#22405wesleysimplicio wants to merge 1 commit into
Conversation
Closes NousResearch#22375. `unblock_task` jumped `blocked -> ready` unconditionally, so a child task could land in `ready` while its parent was still `todo`/`ready` — the only path in the DB layer that bypassed the parent-status check that `recompute_ready` enforces. The dispatcher could then claim the child and start it before the parent ever ran. Re-evaluate the gate inside the same write txn: select parent statuses (same query `recompute_ready` uses) and land in `ready` only when every parent is `done`, otherwise land in `todo` so the next dispatcher tick re-runs `recompute_ready` and promotes the row when it's actually unblocked by dependencies. The no-parents case (the only one covered by `test_block_then_unblock`) is preserved: `all([])` is True, so a parent-free task still goes straight to `ready`. Regression test in tests/hermes_cli/test_kanban_unblock_parent_gate.py covers: open parent → todo, all parents done → ready, partial parents done → todo, no parents → ready, and the unchanged `returns False when not blocked` contract.
There was a problem hiding this comment.
Pull request overview
This PR fixes a kanban DB-layer status transition bug where unblock_task could move a task from blocked directly to ready without re-checking parent dependencies, allowing children to become dispatchable while parents were still open.
Changes:
- Update
unblock_taskto computenext_statususing the same parent-status gate asrecompute_ready(ready only if all parents aredone, elsetodo). - Extend
unblock_taskdocumentation to reflect the new conditional landing status. - Add a regression test suite covering parent-gated unblock scenarios and the existing “returns False when not blocked” contract.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
hermes_cli/kanban_db.py |
Routes unblock_task through the parent-dependency gate (matching recompute_ready) before choosing ready vs todo. |
tests/hermes_cli/test_kanban_unblock_parent_gate.py |
New regression tests for #22375 validating unblock behavior with open/done/partial/no parents plus non-blocked no-op behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Closing as redundant — issue #22375 was fixed on |
Summary
Closes #22375.
unblock_taskjumpedblocked -> readyunconditionally, bypassing the parent-status check thatrecompute_readyenforces. A child task could land inreadywhile its parent was stilltodo/ready, and the dispatcher could then claim and start the child before the parent ever ran. This was the only path in the DB layer that could promote a task toreadywithout consulting the dependency gate.Fix
Re-evaluate the gate inside the same
write_txn: query parent statuses (sameJOIN task_linksqueryrecompute_readyuses) and land inreadyonly when every parent isdone, otherwise land intodo. The next dispatcher tick re-runsrecompute_readyand promotes the row when it is actually unblocked by dependencies.The no-parents case is preserved by construction —
all([])is True, so a parent-free task still goes straight toready(the existingtest_block_then_unblockcontinues to pass unchanged).Test plan
tests/hermes_cli/test_kanban_unblock_parent_gate.pycovers:todoreadytodoreadyunblock_taskstill returnsFalsewhen row isn't inblockedassert 'ready' == 'todo'. With the fix, all 5 pass.test_kanban_db.py,test_kanban_specify*.py,test_kanban_core_functionality.py,test_kanban_tools.py,test_kanban_dashboard_plugin.py): 350 passed, 1 skipped.pytest -q: 110 pre-existing failures (file_read_guards, file_staleness, file_state_registry, gateway_wsl, gateway_service, model_switch_custom_providers, terminal_tool_requirements, tui_gateway_server) — confirmed pre-existing by stashing the fix and re-running the same files (identical 8/8 failures intest_file_read_guards.pyandtest_gateway_wsl.py). None touchkanban_db.pyor are in the dependency graph of this change.🤖 Generated with Claude Code