Skip to content

fix(kanban): preflight worktrees and forced skills - #104257

Open
chrisbawden wants to merge 1 commit into
NousResearch:mainfrom
chrisbawden:fix/kanban-prerequisite-guards
Open

chrisbawden wants to merge 1 commit into
NousResearch:mainfrom
chrisbawden:fix/kanban-prerequisite-guards

Conversation

@chrisbawden

Copy link
Copy Markdown

What does this PR do?

Stops two classes of Kanban tasks from ever reaching spawn when their prerequisites
cannot be satisfied, and makes every route into running re-validate stored forced
skills atomically.

1. Worktree tasks without a repository anchor are rejected up front. A
workspace_kind=worktree task with no absolute workspace_path, no project-linked
primary repo and no board default_workdir used to be accepted at creation and only
fail later at spawn (burning retries on the way). Creation and the dispatcher now
reject unanchored worktree tasks immediately; explicit absolute paths, project-linked
repos and board defaults remain valid (hermes_cli/kanban_prerequisites.py).

2. Forced skills are resolved against the assignee before the task becomes
runnable.
skills=[...] on kanban_create used to be accepted unchecked: a typo or
a skill the assignee profile doesn't have surfaced only at worker spawn. Now:

  • create_task validates the forced-skill list against the assignee's profile home
    before insertion (no secrets read).
  • assign_task re-validates when a card is reassigned.
  • Dependency promotion (recompute_ready) blocks a card whose stored skills no longer
    resolve, before it becomes ready.
  • The single _claim_and_open_run boundary re-validates stored forced skills on
    every route into running — direct/control-plane claims, dispatcher claims, and
    any requeue that restored ready/review (reclaim, stale lock, crash, timeout,
    spawn failure): an incompatible card is blocked (one prerequisite_blocked event,
    no run opened, no retry consumed) before the CAS can open a run.

3. Legacy invalid cards are blocked before spawn. When the dispatcher
(_dispatch_lane_task) encounters a card whose stored prerequisites are now invalid,
it atomically flips the card to blocked with a prerequisite_blocked event instead
of opening a run and letting it crash-loop.

Existing API shapes are preserved: the dashboard plugin maps the new
ValueError rejections to HTTP 409 alongside the existing 400/409 mappings, and all
previously valid task paths (absolute worktree paths, project links, board defaults,
valid forced skills) keep working — covered by regression tests for each.

Related open PRs (different layers, not duplicates): #103714 anchors decomposed
worktree children at decompose time; #39179 drops invalid skills at spawn instead of
blocking; #85551 gates on base-ref/required-paths. This PR validates at creation,
reassignment, promotion and claim time so nothing invalid survives to spawn.

Related Issue

No existing issue covers the crash-loop class; linking none rather than guessing.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • hermes_cli/kanban_prerequisites.py (new): validate_worktree_anchor,
    validate_forced_skills, validate_task.
  • hermes_cli/kanban_db.py: creation/assignment validation, promotion-time guard in
    recompute_ready, atomic claim-boundary guard in _claim_and_open_run.
  • hermes_cli/kanban_db_dispatch.py: pre-spawn validation in _dispatch_lane_task
    with atomic block + prerequisite_blocked event.
  • plugins/kanban/dashboard/plugin_api.py: 409 mapping for the new rejections.
  • tools/skills_tool.py: profile-scoped skill_is_loadable helper.
  • website/docs/user-guide/features/kanban.md: prerequisite behaviour documented.
  • Tests: tests/hermes_cli/test_kanban_prerequisites.py (new, 440 lines),
    tests/hermes_cli/test_kanban_cli_exit_status.py,
    tests/hermes_cli/test_kanban_review_lifecycle.py,
    tests/hermes_cli/test_kanban_swarm.py, tests/plugins/test_kanban_dashboard_plugin.py,
    tests/tools/test_kanban_tools.py.

How to Test

  1. bash scripts/run_tests.sh tests/hermes_cli/test_kanban*.py tests/tools/test_kanban*.py tests/plugins/test_kanban*.py tests/tools/test_skills_tool.py
    → 62 files, 505 passed, 0 failed, 2 Windows-only skipped.
  2. .venv/bin/ruff check on all 12 changed Python files → passed.
  3. .venv/bin/python -m compileall -q on the 8 non-test-source files → passed.
  4. .venv/bin/ty check hermes_cli/kanban_prerequisites.py tests/hermes_cli/test_kanban_prerequisites.py → passed.
  5. git diff --check origin/main..HEAD → clean; working tree clean.

Checklist

Code

Documentation & Housekeeping

  • I've updated relevant documentation (website/docs/user-guide/features/kanban.md)
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

N/A — no skill added.

Screenshots / Logs

Focused suite summary (local run, 4 workers):

=== Summary: 62 files, 505 tests passed, 0 failed, 2 skipped (100% complete) in 111.0s ===

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cron Cron scheduler and job management tool/skills Skills system (list, view, manage) labels Sep 6, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

PR #104257 — fix(kanban): preflight worktrees and forced skills

Good defense-in-depth: invalid worktrees and unresolvable forced skills now fail at creation/dispatch (preflight) instead of midway through a worker run. The 440-line test_kanban_prerequisites.py suite plus CLI exit-status tests show this was taken seriously.

What looks right

  • hermes_cli/kanban_prerequisites.py (validate_worktree_anchor): workspace_kind=worktree requires an explicit absolute repository anchor — kills the "worktree anchored at wherever the dispatcher happens to run" class.
  • Forced-skill resolution runs under the assignee's home override without reading secrets, with a test that throws on any secret read (preflight read profile secrets) — the right invariant, pinned by test rather than comment.
  • Preflight failures don't open a run or increment retries (no run, no retry) — correct accounting; validated in kanban_db.py (_claim_and_open_run, recompute_ready hunks) with lifecycle tests.
  • CLI surfaces refusals as nonzero exit with the reason on stderr (test_unanchored_worktree_create_returns_nonzero_exit_status).

Non-blocking points

  • Perpetual-preflight-failure cards return to ready and are retried each tick without consuming failure_limit. Intended (it was never a run), but confirm dispatch backoff prevents a hot retry loop for a permanently-bad card.
  • Absolute-path validation: confirm it uses os.path.isabs-style semantics that accept Windows drive-anchored paths, since worktree anchors are host paths.
  • tools/skills_tool.py security-warning hunk rides along — check it's related and covered, not an drive-by.

This branch has not been deployed

No deployments
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 tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants