Repository navigation
fix(kanban): avoid false dispatcher stuck warnings - #65581
jtstothard wants to merge 2 commits into
Conversation
Treat concurrency caps and deliberate dispatch deferrals as healthy, and make gateway status clearly profile-scoped. Co-Authored-By: Claude <noreply@anthropic.com>
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tracing the false-stuck telemetry path. The current-main premise is real: a full global cap returns without spawning at hermes_cli/kanban_db.py:7600-7605, while the gateway increments its aggregate streak when any board still has spawnable ready work at gateway/kanban_watchers.py:1253-1258.
Problems
gateway/kanban_watchers.py:1255computesintentionally_deferredwithany(...)across every board, whileready_pendingis also an any-board probe. A cap, lock, or respawn guard on board A can therefore clear the aggregate streak while board B has genuinely stuck spawnable work.- The added flag test in
tests/hermes_cli/test_kanban_per_profile_cap.py:121does not exercise either changed health predicate.
Suggested changes
- Make gateway health accounting board-local, so only the deferred board is exempted from a stuck warning.
- Add a multi-board regression plus direct health-predicate coverage for gateway and the legacy daemon path.
Automated hermes-sweeper review.
| # Health telemetry (aggregate across boards) | ||
| ready_pending = await asyncio.to_thread(_ready_nonempty) | ||
| if ready_pending and not any_spawned: | ||
| intentionally_deferred = any( |
There was a problem hiding this comment.
results spans every board, while ready_pending is also an any-board probe. A cap/lock/guard on board A therefore resets bad_ticks even if board B has spawnable work that fails every tick. Please account for health per board, or suppress only when the board containing ready work is itself intentionally deferred.
Address teknium1 review on NousResearch#65581: the aggregate any()-board intentionally_deferred check could mask genuinely stuck work on one board when another board was capped/locked/respawn-guarded. Scope the deferral exemption per-board so only the board with ready work is exempted when it is itself deferred. Add multi-board regression test and direct health-predicate coverage for both gateway and daemon paths.
|
Addressed in b84456b. The gateway health predicate is now board-local: ready work on a board only exempts bad_ticks when that same board is intentionally deferred, so a cap/lock/guard on board A no longer masks stuck work on board B. Added a multi-board regression test (board A deferred, board B stuck → warning still fires) plus direct health-predicate coverage for both gateway and daemon paths. |
SummaryThirteen PRs address or reference four Kanban gateway issues through distinct diffs covering dispatcher exclusivity, false stuck telemetry, auto-decompose diagnostics and secret scope, concurrency parsing, activity and notifier reporting, archived-board races, and worker-log cleanup. Material overlap is concentrated in the singleton-lock, false-stuck, max_spawn-coercion, and auto-decompose secret-scope groups. Related pull requests
Duplicates#41679 and #44919 overlap on #41448, with #49068 as the merged machine-global consolidation; #46892 and #65581 overlap on #46800 but contain complementary health-accounting work; #59588 overlaps #65644 on max_spawn coercion; the Kanban half of #76864 duplicates #57837. Suggested consolidationAuthor action: rebase #65581 onto main or split out a focused #46800 change that preserves its board-local gateway and daemon health accounting, incorporates #46892's externally occupied max_spawn detection per physical board, and removes the unrelated gateway-status changes; this respects both keep_open reviews and the recorded best-fix status of #46892 and #65581. Keep #49861, #57837, #62893, #64605, and #65321 open on their documented salvage paths, require #65644's finally-based exceptional-Popen cleanup before closing #59588 as duplicate of #65644, and leave #41679, #44919, and #76864 closed in favor of #49068, #57837, and #76871 respectively. Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I46800(["issue #46800 (open)"])
subgraph Dup46892 ["PRs duplicating each other"]
P46892["PR #46892 (open)"]
P65581["PR #65581 (open)"]
end
P65581 -->|best fix| I46800
class I46800 open
class P46892 open
class P65581 open
class P46892 best
class P65581 best
class P65581 target
click I46800 "https://github.com/NousResearch/hermes-agent/issues/46800"
click P46892 "https://github.com/NousResearch/hermes-agent/pull/46892"
click P65581 "https://github.com/NousResearch/hermes-agent/pull/65581"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 13 pull requests and 4 issues in this complex. Each diff was read against this issue; Assessment working set: 142 kB of PR diffs, 45 kB of issue/PR text, 29 kB of discussion (31 comments), 11 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
Problem
The kanban dispatcher's health telemetry raises "stuck" warnings whenever the ready queue is non-empty and no workers spawn on a tick — even when the non-spawn is intentional and correct (concurrency caps reached, profile already at
max_in_progress_per_profile, a task is locked, or respawn is guarded). This produces noisy false-positive stuck warnings that make real stalls harder to spot and erode trust in health monitoring.What changed
DispatchResultgainsskipped_global_capped— set when the board's globalmax_in_progressis already filled, mirroring the existingskipped_per_profile_capped.bad_ticksonly increments when spawnable work is genuinely stuck, not deliberately deferred.hermes kanban dispatchCLI surfacesskipped_global_cappedso operators see "Deferred (global in-progress cap reached)" instead of silent nothing.hermes gateway statusnow scopes its running/not-running messaging to the active profile, and when the active profile is down but another profile's gateway is live, it says so explicitly ("not a system-wide outage") with a "Live gateways in other profiles" section.Why it matters
On multi-profile hosts the gateway-status wording previously implied a system-wide outage when only one profile was idle, and the health telemetry cried "stuck" on every tick where caps correctly prevented over-spawning. Both undermine operator confidence in the very signals meant to catch real problems.
Verification
tests/hermes_cli/test_kanban_per_profile_cap.py— newtest_global_cap_marks_dispatch_as_intentionally_deferred; all 10 tests passtests/hermes_cli/test_gateway_service.py— newtest_gateway_status_promotes_live_other_profile; all 189 tests passRisks / follow-ups
bad_ticks. This is a deliberate trade (false-positive reduction) but means a real stall masked by concurrent deferral would rely on subsequent ticks where the deferral clears.