Skip to content

fix(kanban): keep initial_status='blocked' tasks sticky - #76718

Closed
Jeffgithub0029 wants to merge 2 commits into
NousResearch:mainfrom
Jeffgithub0029:feat/kanban-update-body-sticky-block
Closed

fix(kanban): keep initial_status='blocked' tasks sticky#76718
Jeffgithub0029 wants to merge 2 commits into
NousResearch:mainfrom
Jeffgithub0029:feat/kanban-update-body-sticky-block

Conversation

@Jeffgithub0029

@Jeffgithub0029 Jeffgithub0029 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What

create_task(initial_status='blocked') is an explicit human parking decision, not a recoverable circuit-breaker failure. Until now it wrote tasks.status='blocked' without a blocked event row, so _has_sticky_block saw no sticky signal and the next dispatcher recompute_ready silently promoted the card to ready — after which it was dispatched to a worker that had nothing left to do.

This PR records a typed blocked event (kind: needs_input) at creation, making the initial parking decision sticky the same way a worker-initiated kanban_block is. unblock_task remains the only way out.

Scope (narrow by design)

  • Only create_task gains an event emission. No block_kind mutation, no recompute_ready / reclaim_task changes.
  • Consequently unblock → parent-completion, unblock → claim → reclaim, and circuit-breaker auto-recovery semantics are untouched (regression tests included).

Relationship to other PRs

Tests

python -m pytest tests/hermes_cli/test_kanban_blocked_sticky.py -q
→ 5 passed (was 2 baseline + 3 new: initial-block sticky, unblock→ready, unblock→claim→reclaim→ready)

Add `kanban update-body` to atomically replace a task's canonical body with an audited body_updated event (sha256 + length; repeat is a no-op). Make initial_status='blocked' a typed sticky block (block_kind 'needs_input') so dispatcher recompute_ready cannot silently promote a human parking decision.

Cherry-picked from df778fd765 (original message was an accidental API-error dump); preserved as backup/kanban-sticky-body-update-20260802-175124.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cron Cron scheduler and job management needs-decision Awaiting maintainer decision before any implementation labels Aug 2, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #34735 covers the initial-blocked event. This PR also adds the audited body-update operation and typed-gate/reclaim/legacy-repair behavior, so it is not a duplicate; maintainers should choose the desired sticky-gate scope.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the focused initial-blocked and audited body-update work. The initial-status premise is real on current main: create_task() records only created for an initially blocked task (hermes_cli/kanban_db.py:3254-3273), while _has_sticky_block() only recognizes blocked/unblocked events (hermes_cli/kanban_db.py:4126-4132).

Problems

  • hermes_cli/kanban_db.py:4253 conflates historical block_kind with an active block. unblock_task() intentionally preserves that field (hermes_cli/kanban_db.py:5942-5951) but sends tasks with unfinished parents to todo; the new early continue prevents their normal later promotion.
  • hermes_cli/kanban_db.py:4708 has the same conflation. A task explicitly unblocked, claimed, then manually reclaimed becomes blocked again merely because its historical block_kind remains. Current reclaim semantics are explicitly reset-to-ready (hermes_cli/kanban_db.py:4607-4616).

Suggested changes

  • Limit the sticky fix to emitting the initial blocked event, or introduce separate active-gate state that unblock_task() clears while retaining loop history.
  • Add the unblock/parent-completion and unblock/claim/reclaim regressions; document update-body in website/docs/reference/cli-commands.md.

Automated hermes-sweeper review.

Comment thread hermes_cli/kanban_db.py Outdated
for row in todo_rows:
task_id = row["id"]
cur_status = row["status"]
if row["block_kind"] in {"needs_input", "capability"}:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

block_kind is intentionally preserved after unblock_task() as loop-history (hermes_cli/kanban_db.py:5942-5951). An explicitly unblocked task with an unfinished parent is therefore todo plus needs_input, and this guard prevents it from ever becoming ready after the parent completes. Gate on current sticky state, or store a separate active-gate marker that unblock clears.

Comment thread hermes_cli/kanban_db.py
return False
if row["status"] != "running" and row["claim_lock"] is None:
# Nothing to reclaim — already ready / blocked / done.
return False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This treats retained loop-history as a current gate. After initial_status='blocked' -> unblock -> claim, block_kind remains needs_input by design, so a later operator reclaim changes an explicitly released task back to blocked. That contradicts reclaim's current reset-to-ready behavior; do not use historical block_kind alone here.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/install-update Installer, updater, packaging, wheels, doctor labels Aug 2, 2026
…vent

create_task(initial_status='blocked') is an explicit human parking
decision, not a recoverable circuit-breaker failure. Record a 'blocked'
event (kind: needs_input) at creation so _has_sticky_block sees it and
the dispatcher recompute_ready cannot silently promote the card after an
assignment or dependency tick. unblock_task remains the only way out.

Narrow fix: no block_kind mutation, no recompute_ready/reclaim changes,
so unblock/parent-completion and unblock/claim/reclaim semantics are
untouched. This converges the earlier combined PR (update-body command
moved to NousResearch#76765).
@Jeffgithub0029 Jeffgithub0029 changed the title feat(kanban): body update command and sticky initial-block fix(kanban): keep initial_status='blocked' tasks sticky Aug 2, 2026
@Jeffgithub0029

Copy link
Copy Markdown
Contributor Author

Thanks for the triage note — acting on the scope guidance.

Converged per your suggestion: this PR is now a deliberately minimal sticky-initial-block fix only. It records a typed blocked event (kind: needs_input) in create_task when initial_status='blocked', so _has_sticky_block fires and recompute_ready cannot silently promote a parked card. No block_kind mutation, no recompute_ready / reclaim_task changes — which avoids the unblock/parent-completion and unblock/claim/reclaim conflation issues the sweeper review flagged.

The audited update-body operation is now its own PR: #76765 (focused, with docs in cli-commands.md).

Reclaim/legacy-repair/typed-gate behavior dropped from this branch entirely. Since the sticky-gate space already has several open PRs (#34735, #71977, #35832, #46565, #71147, #69679, #34756), maintainers deciding the desired sticky-gate scope can use this PR as the minimal reference implementation; if #34735 or another is preferred, this one can be closed.

@Jeffgithub0029

Copy link
Copy Markdown
Contributor Author

Closing this in favor of #34735, which already has maintainer attention (Kayhusk commented) and covers the same minimal fix (emit a blocked event at create_task so initial_status='blocked' is sticky). My implementation here converged to the same narrow shape; keeping both open adds noise in a space that already has 8+ overlapping PRs. The audited update-body command that originally lived on this branch is now its own PR (#76765) — that's the part with no competitors. Thanks for the triage guidance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cron Cron scheduler and job management needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants