Skip to content

fix(kanban): review tasks bypass dup-PR respawn guards (active_pr/recent_success) - #46549

Closed
demi-hl wants to merge 1 commit into
NousResearch:mainfrom
demi-hl:wt/fix-review-respawn-guard
Closed

demi-hl wants to merge 1 commit into
NousResearch:mainfrom
demi-hl:wt/fix-review-respawn-guard

Conversation

@demi-hl

@demi-hl demi-hl commented Jun 15, 2026

Copy link
Copy Markdown

Symptom (reproduced live on main, 2026-06-15)

Review-required cards never get reviewed. A card that shipped a PR sits in status='review' but the dispatcher refuses to spawn the reviewer for up to 24h — the review lane is wedged out by guards that only make sense for the build lane.

Root cause

hermes_cli/kanban_db.py :: check_respawn_guard(conn, task_id) runs in dispatch_once for every ready+assigned task, build and review alike. Two of its four guards exist solely to stop a builder re-running and opening a duplicate PR:

  • "recent_success" — a completed run exists within _RESPAWN_GUARD_SUCCESS_WINDOW (1h).
  • "active_pr" — a GitHub PR URL appears in a comment within _RESPAWN_GUARD_PR_WINDOW (24h).

But the build run that ships a PR is itself a recent completed run and leaves a PR-URL comment. When that same card transitions to status='review', both guards fire and defer the reviewer spawn — for up to the 24h PR window. A reviewer never opens a PR, so neither rationale applies to a review pass.

Fix (surgical — only check_respawn_guard + tests)

  1. Extend the existing task-row SELECT to also read tasks.status.
  2. is_review = (status == "review").
  3. Skip the recent_success and active_pr checks when is_review is true (return None path).
  4. Keep rate_limit_cooldown and blocker_auth active for review spawns — a rate-limited / auth-blocked reviewer should still defer.

No new config keys, no new env vars, no refactor. One extra column in one existing query, two if not is_review: guards.

Tests

Added 4 invariant tests in tests/hermes_cli/test_kanban_db.py (asserting the invariant, not a snapshot):

  • test_respawn_guard_review_bypasses_active_pr_build_does_not — same PR-comment task returns "active_pr" on status='ready' but None on status='review'.
  • test_respawn_guard_review_bypasses_recent_success_build_does_not — same recent-completed-run task returns "recent_success" on ready but None on review.
  • test_respawn_guard_review_with_pr_and_success_returns_none — acceptance shape: review task with both a PR comment and a recent completed run returns None; the build-lane equivalent stays guarded.
  • test_respawn_guard_review_still_honors_blocker_auth — review status still defers on an auth/quota error.

Verification

.venv/bin/python -m pytest tests/hermes_cli/test_kanban_db.py -q
218 passed

(The dup-PR protection for the build lane is fully intact — every existing check_respawn_guard test still passes.)

Acceptance criteria (from the task)

  • check_respawn_guard returns None for a review-status task that has a PR comment + recent completed run.
  • check_respawn_guard still returns "active_pr" for a ready-status task with a PR comment (build dup-PR protection intact).
  • New/extended tests pass.
  • PR opened, not merged.

…ent_success)

check_respawn_guard refused to respawn a reviewer for up to 24h on any
review-status card that shipped a PR: the build run that opened the PR
counts as a recent 'completed' run (recent_success guard) and left a
PR-URL comment (active_pr guard). Both guards exist solely to stop a
BUILDER re-running and opening a duplicate PR, but the review pass runs
through the same dispatch_once -> check_respawn_guard path, so a card in
status='review' got wedged out of review.

Read tasks.status alongside last_failure_error and skip the
recent_success + active_pr checks when status == 'review'. The
rate_limit_cooldown and blocker_auth guards still apply to review spawns
(a rate-limited / auth-blocked reviewer should still defer).

Adds 4 invariant tests asserting review bypasses the dup-PR guards while
a build-lane (ready) task in the same shape stays guarded, plus that
review still honors blocker_auth.
@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have labels Jun 15, 2026
@liuhao1024

Copy link
Copy Markdown

Code Review — LGTM ✅

Scope: Kanban check_respawn_guard — review-status tasks bypass recent_success and active_pr guards.

What was checked:

  • is_review derived from row["status"] read in the same query — no TOCTOU gap
  • Rate-limit cooldown (step 1) and auth blocker (step 2) still apply to review tasks — only dup-PR guards (steps 3-4) are bypassed
  • SQL change: SELECT last_failure_error → SELECT last_failure_error, status — minimal, no schema migration needed
  • 4 new tests covering: review bypasses active_pr (build doesn't), review bypasses recent_success (build doesn't), combined PR+success returns None for review, review still honors blocker_auth
  • Docstring updated to document the review-status exception for both guards

No issues found. The bypass is well-scoped: only the two guards that would wedge a reviewer are disabled, while rate-limit and auth-blocker protections remain intact.

cwest added a commit to cwest/hermes-agent that referenced this pull request Jun 21, 2026
Make PATCHES.md the accurate manifest of what topic/rebase-v2026.6.19
carries on upstream tag v2026.6.19, replacing the stale v2026.6.5 state.

- Base section: bump base tag v2026.6.5 (3c231eb) -> v2026.6.19 (681cd63).
- Bump base-tag to v2026.6.19 on the NousResearch#44023 and NousResearch#46549 rows and confirm
  both PRs are OPEN upstream (live gh check); add the NousResearch#44023 companion
  test note and its numpy-extra skip behavior.
- Add rows for every carried patch that previously had none, so the table
  matches the branch (git cherry shows 0 absorbed, nothing to drop):
  - dispatcher dedicated-executor (fork PR #4, OPEN; no upstream PR
    number yet -> behavior-keyed retire, not PR-merge-keyed);
  - skill-count external_dirs fix (fork PR #6, MERGED to
    integration; permanent-local);
  - webhook action allow-list (local feature; permanent-local);
  - daily upstream-sync and secret-scan CI workflows (permanent-local);
  - PATCHES.md itself (permanent-local).
cwest added a commit to cwest/hermes-agent that referenced this pull request Jun 28, 2026
The active_pr respawn guard in check_respawn_guard refuses to spawn a
ready task that has a GitHub PR URL in a recent comment (within the 24h
_RESPAWN_GUARD_PR_WINDOW), to prevent a builder re-opening a duplicate
PR. But in the one-card review lifecycle, when a reviewer bounces a card
back to the author to rework the SAME PR, the card lands in
status='ready' still carrying the build-handoff PR-URL comment — so the
guard wedges the author out of spawning for up to 24h and the rework
loop cannot complete.

Use the latest 'unblocked' task event as an additional lower bound on
the PR-comment scan window: PR URLs posted before a deliberate unblock
no longer veto the respawn (the unblock is the operator/orchestrator's
green light to resume work on that PR), while URLs at or after the
unblock still guard against a genuine duplicate-PR. Same-second comments
stay guarded conservatively (timestamps are second-granular).

This is additive over the carried NousResearch#46549 (which bypasses the guard only
for status='review'); the author-rework card is in 'ready', which NousResearch#46549
does not cover. Applied inside the existing `if not is_review:` block so
the two compose.

Carries the mechanism from upstream PR NousResearch#46204 by dannyfranca, adapted to
this fork's line context (our base already wraps the active_pr block in
the NousResearch#46549 is_review guard, so a raw cherry-pick would conflict). Adds 3
invariant tests and the matching docstring + kanban.md docs. Tracked in
PATCHES.md and docs/patches/2026-06-28-pr46204-active-pr-unblock-cutoff.md;
auto-retires when NousResearch#46204 merges upstream.

Upstream-PR: NousResearch#46204
Upstream-Issue: NousResearch#29458

Co-authored-by: dannyfranca <dannyfranca@users.noreply.github.com>
@Chadtao206

Chadtao206 commented Jun 30, 2026 •

Copy link
Copy Markdown

Independent confirmation — we hit this exact bug in production, and the root cause is just as described: a reviewer never opens a PR, so active_pr/recent_success shouldn't gate a review pass.

One gap worth covering before merge: keying the bypass on status == "review" misses dedicated review tasks that sit in status='ready'. When a re-review is delegated as its own card (assignee='reviewer', status='ready', not 'review'), the card posts its own review comment containing the PR URL it is reviewing (e.g. … blocker posted: https://github.com/<org>/<repo>/pull/<N>#issuecomment-…). Once it returns to ready after the remediation completes, active_pr matches that self-authored comment and the dispatcher skips it every tick (respawn_guarded {"reason":"active_pr"}) until the 24h _RESPAWN_GUARD_PR_WINDOW elapses — we observed ~10.5h. status is 'ready' throughout, so a status=='review' check wouldn't catch it.

Suggestion: gate on either status=='review' or the assignee being a review lane — cf. #32241, which exempts {tester,qa} by assignee. Combining the two covers both cards-awaiting-review and delegated review tasks.

Tangent: the same misfire also surfaces a misleading kanban dispatcher stuck … Check profile health (venv, PATH, credentials) warning, because has_spawnable_ready() counts the guarded task as spawnable — #44899 addresses that half. Refs: #29458 (umbrella), #42053 / #49870 (sibling recent_success).

cwest pushed a commit to cwest/hermes-agent that referenced this pull request Jul 1, 2026
…ent_success)

check_respawn_guard applied the recent_success and active_pr guards — which
exist only to stop a builder re-opening a duplicate PR — to review-lane spawns
too, so a card in status='review' (whose build run already completed and left a
PR-URL comment) got blocked from spawning its reviewer for up to 24h. Reads
tasks.status and skips those two guards for status='review' while keeping
rate_limit_cooldown and blocker_auth active.

upstream-pending: PR NousResearch#46549
cwest added a commit to cwest/hermes-agent that referenced this pull request Jul 1, 2026
Make the active_pr respawn guard honor an explicit unblock so the review->author
rework loop can spawn without waiting out the 24h PR window. Uses the latest
unblocked task event as an additional lower bound on the PR-comment scan window
(pr_cutoff = max(window, latest_unblock_ts)): PR URLs posted before a deliberate
unblock no longer veto respawn, while URLs at/after still guard. Additive over
NousResearch#46549 (which covers status='review' only; the author-rework card is in 'ready').

upstream-pending: PR NousResearch#46204
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for the focused reproduction and regression tests. This is an automated hermes-sweeper review; current main already provides the requested review-card behavior through a later dispatcher structure.

  • hermes_cli/kanban_db.py:7465-7485 calls check_respawn_guard() only while iterating ready tasks.
  • hermes_cli/kanban_db.py:7568-7639 dispatches status='review' cards in a separate loop via claim_review_task() without invoking that guard, so recent_success and active_pr cannot defer a review-card spawn.
  • tests/hermes_cli/test_kanban_db.py:3962-3977 covers dispatching a review task when no ready tasks exist.

The ready-state dedicated-reviewer case noted in the follow-up discussion is distinct: this PR's status == "review" condition would not address it. Closing as implemented on main.

@teknium1 teknium1 closed this Jul 14, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 14, 2026
cwest pushed a commit to cwest/hermes-agent that referenced this pull request Jul 26, 2026
…ent_success)

check_respawn_guard applied the recent_success and active_pr guards — which
exist only to stop a builder re-opening a duplicate PR — to review-lane spawns
too, so a card in status='review' (whose build run already completed and left a
PR-URL comment) got blocked from spawning its reviewer for up to 24h. Reads
tasks.status and skips those two guards for status='review' while keeping
rate_limit_cooldown and blocker_auth active.

upstream-pending: PR NousResearch#46549
(cherry picked from commit d2069ee)
cwest added a commit to cwest/hermes-agent that referenced this pull request Jul 26, 2026
Make the active_pr respawn guard honor an explicit unblock so the review->author
rework loop can spawn without waiting out the 24h PR window. Uses the latest
unblocked task event as an additional lower bound on the PR-comment scan window
(pr_cutoff = max(window, latest_unblock_ts)): PR URLs posted before a deliberate
unblock no longer veto respawn, while URLs at/after still guard. Additive over
NousResearch#46549 (which covers status='review' only; the author-rework card is in 'ready').

upstream-pending: PR NousResearch#46204
(cherry picked from commit e741b78)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants