fix: preflight kanban task skills before spawning - #33747
Conversation
Real-world reproduction: 1192 crashes, failure_limit bypass, system rebootThis PR addresses exactly what we hit in production. Sharing the case as additional context. Setup: macOS (M1 Ultra), hermes-agent 0.14.0 (upgraded to 0.15.1 today), What happened: A task was created with a misspelled skill name: The worker started, immediately raised Consequence: The dispatcher respawned the task every ~60 seconds for 2 days straight — 1192 consecutive crashes — without ever triggering the Why failure_limit didn't fire: The crash happens before the worker writes its first heartbeat/lock entry. Without a lock entry, the dispatcher's crash detection ( Downstream impact:
Workaround applied: Directly patched the skill name in the kanban SQLite DB, then blocked the task. Suggestion beyond this PR: Even with preflight validation at creation time, it would be valuable to also handle the race condition where a skill is deleted/renamed after a task is created. A sticky auto-block after even 1 pre-heartbeat crash (classified separately from post-heartbeat crashes) would prevent this class of infinite loop entirely. Thanks for the fix — this is a significant stability issue in long-running gateway deployments. |
|
Related-work note to avoid parallel duplicate Kanban skill-preflight branches: This PR overlaps the fail-closed preflight part of the existing forced-skill work:
Recommendation: reconcile this branch with #30025/#33640 rather than keep multiple independent preflight implementations open. If this branch has cleaner tests or simpler preflight structure, it may be a good base; #33640's unique contribution is the explicit allowlisted sync-before-block step and should be carried over only if maintainers want that operational behavior. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for targeting a real worker-startup failure. Current main still forwards task.skills directly into --skills at hermes_cli/kanban_db.py:8036-8039, so the preflight goal remains valid.
Problems
hermes_cli/kanban_db.py:5969-5975implements only a subset of the runtime lookup.tools/skills_tool.py:1164-1170also resolves a requested skill through its SKILL.md frontmattername; this preflight would instead block that valid task before spawning.hermes_cli/kanban_db.py:5983-5984permits any unresolved colon-qualified name. The runtime validates plugin namespaces and registry lookup intools/skills_tool.py:1002-1045, so an unavailable qualified skill can still reach startup and fail.
Suggested changes
- Share or extract the runtime candidate-resolution logic, then cover frontmatter-name, external-dir collision, and unavailable qualified-plugin cases.
- Salvage the logic into current
dispatch_oncepaths: the dispatcher moved tohermes_cli/kanban_db.py:7240+after84e1d31e5, and the review path remains separately force-loaded athermes_cli/kanban_db.py:7611-7616.
Automated hermes-sweeper review.
| elif categorized_path.with_suffix(".md").is_file(): | ||
| _record(categorized_path.with_suffix(".md")) | ||
|
|
||
| for found_skill_md in iter_skill_index_files(search_dir, "SKILL.md"): |
There was a problem hiding this comment.
This scan is not equivalent to skill_view: it accepts a matching SKILL.md frontmatter name even when the directory basename differs (tools/skills_tool.py:1164-1170). Please reuse/extract that candidate resolution or parse frontmatter here; otherwise a valid forced skill is falsely blocked.
| return None | ||
| if len(candidates) > 1: | ||
| return "ambiguous" | ||
| if ":" in name: |
There was a problem hiding this comment.
An unresolved qualified name is not necessarily available. The runtime validates plugin namespaces and registry lookup before local fallback (tools/skills_tool.py:1002-1045); allowing every namespace:skill here can still send an unavailable skill to the child and preserve the startup crash loop.
Summary
skillsagainst the assignee profile before Kanban dispatch claims/spawns a worker.Unknown skill(s)crash/retry loops.block_task()to blockreviewtasks.Test Plan
pytest -q tests/hermes_cli/test_kanban_db.py::test_dispatch_blocks_ready_task_with_missing_profile_skill tests/hermes_cli/test_kanban_db.py::test_dispatch_spawns_when_profile_skill_exists tests/hermes_cli/test_kanban_db.py::test_dispatch_promotes_ready_and_spawns tests/hermes_cli/test_kanban_db.py::test_dispatch_spawn_failure_releases_claim tests/hermes_cli/test_kanban_core_functionality.py::test_spawn_failure_auto_blocks_after_limit tests/hermes_cli/test_kanban_core_functionality.py::test_spawned_event_emitted_with_pidpython -m compileall -q hermes_cli/kanban_db.py tests/hermes_cli/test_kanban_db.pygit diff --check