Skip to content

fix(kanban): hard-error when an explicit board selection names a nonexistent board - #65846

Open
ahmadashfq wants to merge 22 commits into
NousResearch:mainfrom
ahmadashfq:fix/kanban-explicit-board-hard-error
Open

fix(kanban): hard-error when an explicit board selection names a nonexistent board#65846
ahmadashfq wants to merge 22 commits into
NousResearch:mainfrom
ahmadashfq:fix/kanban-explicit-board-hard-error

Conversation

@ahmadashfq

Copy link
Copy Markdown
Contributor

Summary

  • raise BoardNotFoundError when an explicit kanban board selection (HERMES_KANBAN_BOARD or scoped_current_board(...)) names a missing board
  • keep the ambient kanban/current pointer on the existing warn-and-fallback behavior, but make the fallback loud on stderr instead of silent
  • add regression coverage for the explicit-selection hard error path and the stale-pointer warning path

Why

Explicit board intent should never be silently redirected to another board's DB. On 2026-07-16, HERMES_KANBAN_BOARD=fleet-infra hermes kanban create ... routed to default after fleet-infra had been archived, with no error or warning.

How to test

  • python -m pytest tests/hermes_cli/test_kanban_boards.py -q
  • python -m pytest tests/hermes_cli/test_kanban_db.py tests/hermes_cli/test_kanban_decompose.py tests/hermes_cli/test_kanban_lifecycle_hooks.py -q
  • python -m pytest tests/tools/test_kanban_tools.py tests/plugins/test_kanban_dashboard_plugin.py -q
  • manual repro on a throwaway HERMES_HOME:
    • create a real board and pin HERMES_KANBAN_BOARD to a missing board
    • confirm BoardNotFoundError is raised and zero tasks land on the fallback board
    • remove the explicit env pin, write a stale kanban/current pointer, and confirm resolution returns default with a stderr warning
  • full repo sweep: scripts/run_tests.sh remains red on both base 5ee51c696 and patched a32b5b195 on this machine in unrelated areas; touched kanban suites are green

Platforms tested

  • macOS 26.5.2

Related context

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets tool/code-exec execute_code sandbox tool/memory Memory tool and memory providers platform/slack Slack app adapter platform/telegram Telegram bot adapter needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 16, 2026
@tonydwb

tonydwb commented Jul 16, 2026

Copy link
Copy Markdown

Code Review Summary

Verdict: Comment

Kanban explicit board hard-error. +8647/-453, 51 files. Very large refactor/feature. Surface-area exceeds shallow-review threshold. No security issues detected but full review deferred to human reviewer.

--
Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting the misrouting scenario and adding focused regression coverage.

Problems

  • Current main deliberately treats stale HERMES_KANBAN_BOARD values as recoverable: get_current_board() falls through at hermes_cli/kanban_db.py:426-460, and 8a64e1580 added tests/hermes_cli/test_kanban_boards.py:361-375 to assert that a removed-board pin routes to the persisted board without recreating it. Changing that established behavior to a hard error needs an explicit maintainer compatibility decision.
  • The PR contains 51 files / +8647/-453, although the stated change is concentrated in a32b5b195 (hermes_cli/kanban_db.py and tests/hermes_cli/test_kanban_boards.py). The unrelated changes should not be reviewed or salvaged as part of this focused behavior change.

Suggested changes

  • After maintainer direction, salvage the focused board-resolution commit and its tests independently of the unrelated branch stack.

Automated hermes-sweeper review.

kb.create_board("persisted")
kb.set_current_board("persisted")
monkeypatch.setenv("HERMES_KANBAN_BOARD", "missing-board")
assert kb.get_current_board() == "persisted"
with pytest.raises(kb.BoardNotFoundError) as exc_info:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion intentionally reverses the stale-environment compatibility behavior added by 8a64e1580 and currently covered by test_connect_stale_env_uses_fallback_board_without_recreating_it. Please obtain a maintainer decision on making stale worker pins fatal before changing this contract.

@teknium1 teknium1 added the sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit label Jul 18, 2026
reapply-prep and others added 10 commits August 2, 2026 07:21
(cherry picked from commit 3ee28fa)
(cherry picked from commit eeaee70b722ba8ac2b20516397ca49c93ebaa978)
(cherry picked from commit 59eb55a6b3c895f1cdfe7a4b1088b460ef4b1135)
(cherry picked from commit ee222bc)
(cherry picked from commit 71513d62bcca3b8044860a4775796a8c4001b57f)
(cherry picked from commit 26637e2)
(cherry picked from commit 8fdeeb21abe46016a3dce4bb5a6eabadea878e87)
(cherry picked from commit 1861bc1c1ca08be3b8b6b0ad7d7aabea0e717513)
(cherry picked from commit 3f7b4a4)
(cherry picked from commit 52b8f40ebd180ec26e5c3a2c79f71887a05de9e8)
(cherry picked from commit 95205233ea07577758f0f2b241218b4e304f840f)
(cherry picked from commit a81bd22)
(cherry picked from commit 40856a04f9aa9a77db766054fa0b1961df0d5e26)
Two fixes so the consolidated worktree/packet-capture work passes its full
acceptance suite on current main (5 tests were red after merging 692de068a):

- live_worker_workspace_snapshot: honor the active worker's own task
  (HERMES_KANBAN_TASK == row id) as authoritative over stale persisted
  workspace_kind, so show/list packets reflect the real worktree branch even
  when the row says scratch. The git-worktree probe still gates branch names,
  so non-worktree active workspaces stay empty.
- kanban_create: invert a self-parent edge (parents=[self]) instead of taking
  it literally — the child is created with no blocking parent (promotes
  immediately) and the spawning task becomes a CHILD of it (wakes after),
  mirroring decompose_triage_task. Prevents the orchestrator-reroute deadlock.

Full kanban suite: 359 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 52bb6de)
(cherry picked from commit 0b012aaa71052138e5278b85e4dd3902828e0624)
(cherry picked from commit 01b9df3e42f528e77e80c60e56e251c1c82d3ada)
(cherry picked from commit 50b44d5)
(cherry picked from commit d81eae644c4684b864085e81a7a4b4c61e935024)
…main)

The worktree claim flow hardcoded `origin/main` as the base for new worker
worktrees. For a local-only, patch-maintained install whose `origin` is a
public fork the fleet must never contact, that is wrong twice over: origin/main
lacks all local work, and reaching it risks exposing private code.

- Add per-board `worktree_base_ref` config (board.json), default local `main`.
  Workers now branch `wt/t_<id>` from the local default branch — the source of
  truth — and `_resolve_worktree_base_commit` fetches ONLY when the ref is a
  configured remote-tracking ref (e.g. a board that opts into `origin/main`).
  Local refs never trigger a network fetch.
- Thread base_ref through `_ensure_git_worktree` / `_resolve_worktree_workspace`;
  persist it as `workspace_base_ref`.
- Tests: existing origin/main worktree tests opt into `worktree_base_ref=
  origin/main` (remote path still covered); add a local-main default test
  proving no remote is contacted.

Full kanban suite: 361 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 8b431ba)
(cherry picked from commit aed7aab387d260faab251dc17ebac254ec882bd8)
(cherry picked from commit c243229d34b394772dd89952bc709c8051b9e2c5)
(cherry picked from commit e960a47)
(cherry picked from commit f4cff95ce039026b8c90e45377bd53dd50ef22d0)
dispatch_once force-converted EVERY claimed task to a worktree, which (a)
required every board to have a git default_workdir, (b) failed any task
carrying a legacy scratch workspace_path (not in a git repo -> spawn_failed),
and (c) contradicted the documented scratch/dir/worktree coexistence for
research/ops workloads.

Gate worktree resolution on `_board_is_git_backed(board)` (default_workdir
resolves to a repo root) OR an explicit worktree-kind task. Other boards keep
their scratch/dir workspace. Restores scratch dispatch while git-backed boards
(e.g. fleet-autonomy) still get branch-based, verifiable handoffs from local
main. Adds a regression test for scratch dispatch on a non-git board.

Full kanban suite: 362 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 849a1c1)
(cherry picked from commit 77003890eafc0e5443b35867baaea8ac4e35b555)
(cherry picked from commit dfa32a2277b29a2c9705e79823b9e743575b48fe)
(cherry picked from commit 797c6b6)
(cherry picked from commit 1a011b6924536264e7a2ac7356bc04c04e28b3e6)
(cherry picked from commit 6258469)
(cherry picked from commit 5d7d516edad23cc42a07923ec33f108dca46477b)
(cherry picked from commit 5c15184a922cca5cc0a84a0fa709b631e4a7dc09)
(cherry picked from commit c7069f6)
(cherry picked from commit a38256cd2712d0854915c07d944b117868cb80ea)
ahmadashfq and others added 11 commits August 2, 2026 07:21
(cherry picked from commit 5036a23)
(cherry picked from commit 67d8fd77feca57e176f14fb812e12395105bf6a9)
(cherry picked from commit 325f89ece766370934660f2f484b941b88937fe9)
(cherry picked from commit fc38287)
(cherry picked from commit 948ea1d486ad7f427a9af7db88406cfe939041f0)
The board-conditional dispatch typed EVERY task on a git-backed board as a
worktree, including goal_mode coordination roots (orchestrator goal/epic loops
that fan out and wake on child completion, producing no code of their own).
The worktree completion gate then blocked them forever (real CI on a code
branch they never created). Exempt goal_mode tasks from auto-worktree at
dispatch; an explicit workspace_kind=worktree still wins for the rare goal
card that genuinely edits code. Adds a regression test.

Full kanban suite: 364 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit e708df4)
(cherry picked from commit 39a4a6e937abdcc58ce3480d12f77117c73e8708)
(cherry picked from commit 799464249096bc0c7d39de8e5f21b6d17f9cc35d)
(cherry picked from commit 575bdff)
(cherry picked from commit 996bdcdc4a78db07da9d8b030bdec816db9259c8)
(cherry picked from commit bce14134deea070b35fd1603c5851651996f0824)
(cherry picked from commit 6ee0419ac5aa041696ef66e9032bc201f085f2ec)
(cherry picked from commit c24430d)
(cherry picked from commit 4a6094df13baabaf3ff0e0a58119987cf3c962ac)
(cherry picked from commit 5744baf78ec0e39c1983f2a07361bf667e1a99c1)
(cherry picked from commit 248652a)
(cherry picked from commit 25b17380ff6f07f61dbab3552cf6a790cc42ba3e)
Carry canonical Nostr thread anchors into SessionSource so lifecycle messages can remain attached to an existing Buzz thread. Prefer root markers over immediate reply markers and retain legacy fallback behavior.

Co-authored-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Signed-off-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Use the triggering Buzz event as the synthetic thread root when an inbound source has no existing thread. This mirrors the upstream behavior proposed in PR NousResearch#77080 and complements canonical inbound root preservation.

Co-authored-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Signed-off-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Co-authored-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Signed-off-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
@ahmadashfq

Copy link
Copy Markdown
Contributor Author

Restaged this fix against current local main (5d3b31e2e) in isolated replay branch replay/explicit-board-hard-error-20260804 without touching the preserved original branch/worktree packet.

New replay commit: cb39338da

What changed

  • re-applied the explicit missing-board hard-error path (BoardNotFoundError) to current hermes_cli/kanban_db.py
  • reconciled the regression coverage in tests/hermes_cli/test_kanban_boards.py against current main's slimmer test surface
  • preserved the intended distinction: explicit env/scoped board selections hard-error; stale ambient kanban/current pointer still falls back with a stderr warning

Fresh current-main verification

$ ~/.hermes/hermes-agent/venv/bin/python -m pytest tests/hermes_cli/test_kanban_boards.py -q
31 passed in 2.29s

$ ~/.hermes/hermes-agent/venv/bin/python -m pytest tests/hermes_cli/test_kanban_db.py tests/hermes_cli/test_kanban_decompose.py tests/hermes_cli/test_kanban_lifecycle_hooks.py -q
48 passed in 5.60s

$ ~/.hermes/hermes-agent/venv/bin/python -m pytest tests/tools/test_kanban_tools.py tests/plugins/test_kanban_dashboard_plugin.py -q
49 passed, 8 warnings in 4.80s

Manual repro on the replay lane

explicit_env_exception BoardNotFoundError
persisted_tasks 0
current_pointer_result default
current_pointer_stderr kanban: warning: current-board pointer ... names board 'missing-board' which no longer exists; using 'default'

Broad repo sweep

$ scripts/run_tests.sh
Timed out after 600s at ~61.3%.
Visible failures before timeout were unrelated to this kanban change:
- tests/agent/test_credential_pool_routing.py::TestFailureAttribution::test_unmatched_key_does_not_retry_only_pool_entry
- tests/hermes_cli/test_runtime_provider_resolution.py::test_qwen_oauth_auto_fallthrough_on_auth_failure
- tests/hermes_cli/test_service_manager.py::test_seed_supervise_skeleton_creates_expected_layout
- tests/hermes_cli/test_update_eol_churn.py::test_churn_across_more_files_than_fit_in_one_argv
- plus other pre-existing reds already in progress before timeout

I am pushing this replay commit onto the PR head branch so #65846 reflects current-main state while the original local preservation packet (fix/kanban-explicit-board-hard-error @ a32b5b195, .worktrees/board-fallback-guard) remains intact locally for audit/history.

@ahmadashfq
ahmadashfq force-pushed the fix/kanban-explicit-board-hard-error branch from a32b5b1 to cb39338 Compare August 4, 2026 06:54
…xistent board

HERMES_KANBAN_BOARD (and the scoped_current_board override) naming a
board with no directory under boards_root() used to fall through
silently to the on-disk current pointer and then 'default' — explicit
intent got redirected to another board's DB with no error. Observed
2026-07-16: HERMES_KANBAN_BOARD=fleet-infra 'hermes kanban create'
placed cards on the default board because fleet-infra had been archived
to boards/_archived/.

Explicit selections (env var, scoped override) now raise the new
BoardNotFoundError with remediation hints. The ambient <root>/kanban/
current pointer keeps its fall-through (upstream PR NousResearch#20183 behaviour —
hand-edited/stale files must not crash the dispatcher) but now warns on
stderr instead of falling back silently.

Same failure family as the HERMES_HOME silent-profile-fallback fix
(registry: hermes-cli-no-hermes-home-scoped-resolution).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
Signed-off-by: Hafiz Ahmad Ashfaq <28647270+ahmadashfq@users.noreply.github.com>
@ahmadashfq
ahmadashfq force-pushed the fix/kanban-explicit-board-hard-error branch from cb39338 to 8b5b46a Compare August 4, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have platform/slack Slack app adapter platform/telegram Telegram bot adapter sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/code-exec execute_code sandbox tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants