mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(kanban): failure diagnostics exempt done/archived tasks
A manual done (dashboard/desktop drag) runs complete_task but ends no run, so a trailing crashed/crashed run history never gains the 'completed' outcome that breaks the repeated_crashes streak — the card kept flagging "needs attention" forever after being finished. repeated_failures had the same hole via a stale counter. Terminal statuses are now exempt from both: done means done; the history stays on the event log for audit. Regression test included.
This commit is contained in:
parent
111544d544
commit
5829fe1378
2 changed files with 32 additions and 3 deletions
|
|
@ -530,7 +530,14 @@ def _rule_repeated_failures(task, events, runs, now, cfg) -> list[Diagnostic]:
|
|||
|
||||
Accepts the legacy ``spawn_failure_threshold`` config key for
|
||||
back-compat.
|
||||
|
||||
Terminal statuses are exempt: a done/archived card has nothing left
|
||||
to retry, so a lingering failure streak is history, not a signal.
|
||||
(``complete_task`` resets the counter, but a manual done — e.g. a
|
||||
dashboard drag — ends no run and used to leave the flag stuck.)
|
||||
"""
|
||||
if _task_field(task, "status") in ("done", "archived"):
|
||||
return []
|
||||
threshold = _positive_int(cfg.get(
|
||||
"failure_threshold",
|
||||
cfg.get("spawn_failure_threshold", 3),
|
||||
|
|
@ -649,7 +656,15 @@ def _rule_repeated_crashes(task, events, runs, now, cfg) -> list[Diagnostic]:
|
|||
total failures) so the operator gets a crash-specific heads-up
|
||||
before the unified rule kicks in. Suppresses itself when the
|
||||
unified rule is also about to fire, to avoid double-flagging.
|
||||
|
||||
Terminal statuses are exempt for the same reason as
|
||||
``repeated_failures`` — with one extra wrinkle: this rule reads run
|
||||
history, and a manual done (dashboard drag) appends no ``completed``
|
||||
run to break the crash streak, so the flag was permanent (#kanban
|
||||
desktop dogfood). Done means done.
|
||||
"""
|
||||
if _task_field(task, "status") in ("done", "archived"):
|
||||
return []
|
||||
failure_threshold = int(cfg.get(
|
||||
"failure_threshold",
|
||||
cfg.get("spawn_failure_threshold", 3),
|
||||
|
|
|
|||
|
|
@ -272,6 +272,16 @@ def test_repeated_crashes_escalates_on_many_crashes():
|
|||
assert diags[0].severity == "critical"
|
||||
|
||||
|
||||
def test_failure_rules_exempt_terminal_statuses():
|
||||
# A manual done (dashboard drag) ends no run, so the trailing crash
|
||||
# streak survives in run history — but done means done: neither
|
||||
# failure rule may keep flagging a terminal card.
|
||||
runs = [_run(outcome="crashed", run_id=1), _run(outcome="crashed", run_id=2)]
|
||||
for status in ("done", "archived"):
|
||||
task = _task(status=status, assignee="crashy", consecutive_failures=3)
|
||||
assert kd.compute_task_diagnostics(task, [], runs) == []
|
||||
|
||||
|
||||
def test_stuck_in_blocked_fires_past_threshold():
|
||||
now = int(time.time())
|
||||
task = _task(status="blocked")
|
||||
|
|
@ -368,15 +378,19 @@ def test_repeated_crashes_truncates_huge_tracebacks():
|
|||
|
||||
def test_diagnostics_sorted_critical_first():
|
||||
"""A task with both a critical (many spawn failures) and a warning
|
||||
(prose phantoms) diagnostic should list the critical one first."""
|
||||
task = _task(status="done", consecutive_failures=10,
|
||||
(prose phantoms) diagnostic should list the critical one first.
|
||||
|
||||
Status must be non-terminal: done/archived are exempt from the
|
||||
failure rules (done means done). ``now=300`` keeps the synthetic
|
||||
timestamps from tripping stranded_in_ready — same dodge as above."""
|
||||
task = _task(status="ready", consecutive_failures=10,
|
||||
last_failure_error="nope")
|
||||
events = [
|
||||
_event("completed", ts=100, summary="referenced t_missing"),
|
||||
_event("suspected_hallucinated_references", ts=101,
|
||||
phantom_refs=["t_missing11"]),
|
||||
]
|
||||
diags = kd.compute_task_diagnostics(task, events, [])
|
||||
diags = kd.compute_task_diagnostics(task, events, [], now=300)
|
||||
kinds = [d.kind for d in diags]
|
||||
assert kinds[0] == "repeated_failures" # critical
|
||||
assert "prose_phantom_refs" in kinds
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue