Skip to content

fix(tui): deliver Kanban notifications to TUI sessions - #66435

Closed
cucurigoo wants to merge 2 commits into
NousResearch:mainfrom
cucurigoo:fix/tui-kanban-notify-current-main
Closed

fix(tui): deliver Kanban notifications to TUI sessions#66435
cucurigoo wants to merge 2 commits into
NousResearch:mainfrom
cucurigoo:fix/tui-kanban-notify-current-main

Conversation

@cucurigoo

Copy link
Copy Markdown
Contributor

Summary

Current Hermes main still creates platform="tui" Kanban notification subscriptions but has no TUI consumer for them. Terminal task events therefore remain in task_events while kanban_notify_subs.last_event_id stays at zero, so a TUI/Desktop orchestrator never receives the documented callback or wake turn.

This is a current-main salvage/rebase of #60085. It preserves @falkoro's authorship and resolves the one docstring conflict introduced by later multi-session notification ownership hardening on main.

  • poll kanban_notify_subs for the exact live TUI session key;
  • enumerate all active boards and deduplicate aliased DB paths;
  • claim terminal events using the existing atomic cursor API;
  • emit a visible status update and start an agent turn when idle;
  • buffer claimed events while the session is busy;
  • keep blocked subscriptions through unblock/reblock cycles;
  • remove subscriptions only after done/archived;
  • claim unblocked/archived silently so they cannot wedge later events.

Fixes #59890.
Also addresses the TUI half of #59960.
Supersedes #60085 only if maintainers prefer this current-main salvage branch.

Reproduction confirmed

On stock Docker Hermes v2026.7.7.2, a TUI-created card correctly wrote a platform="tui" subscription. The worker then emitted a blocked event, but the subscription cursor remained 0 and no orchestrator turn ran. Current main still contains the producer-side comment claiming tui_gateway/server.py consumes these rows, while tui_gateway/server.py has no such consumer.

Verification on current main

Rebased onto 594308d4bbe95548c9fe418bb10c449099426f93.

  • pytest tests/tui_gateway/test_kanban_notify_poller.py ... — 10 focused regression tests pass.
  • Focused serial run including the current child-run ownership test — 11 passed.
  • ruff check tui_gateway/server.py tests/tui_gateway/test_kanban_notify_poller.py — passed.
  • python -m py_compile ... — passed.
  • git diff --check — passed.

The broader parallel TUI/gateway slice shows the existing order-dependent test_prompt_submit_rejected_while_child_run_active failure. The same failure reproduces on untouched current main; it passes in the focused serial run and is unrelated to this patch.

Scope

Two files only:

  • tui_gateway/server.py
  • tests/tui_gateway/test_kanban_notify_poller.py

No gateway platform behavior, Kanban schema, model tool schema, or deployed runtime configuration changes.

falkoro added 2 commits July 17, 2026 17:46
kanban_create auto-subscribes TUI/desktop sessions with platform="tui" and
chat_id=HERMES_SESSION_KEY, and tools/kanban_tools.py documents that the
TUI notification poller (tui_gateway/server.py) reads kanban_notify_subs
and posts completion messages into the running session — but that reader
was never implemented. The poller only watched process_registry completion
events, and the gateway notifier skips "tui" rows because no such
messaging adapter exists. Result: subscriptions accumulate with
last_event_id=0 forever and no task event is ever delivered (18 subs,
29 terminal events, 0 deliveries in the report).

Implement the missing delivery path in the TUI notification poller:

- every 5s, claim unseen terminal events for this session's
  platform="tui" subscriptions via claim_unseen_events_for_sub — the
  same atomic cursor-claim the gateway notifier uses, so an event is
  delivered exactly once even with a gateway polling the same board DB
- format events with the same wording as the gateway notifier
  (done/blocked/gave up/crashed/timed out/status; archived and
  unblocked are claimed but silent, so they can't wedge the cursor)
- emit a status.update for user visibility, then chain an agent turn
  when the session is idle — mirroring process-completion handling;
  claimed events buffer in the session until it goes idle since the
  cursor (unlike the process queue) cannot re-queue
- unsubscribe only at a truly final task status (done/archived),
  matching the gateway rule so respawned tasks keep notifying
- multi-board: iterate boards, polling each resolved DB path once

Fixes NousResearch#59890
Covers the poller wiring above _collect_kanban_notifications, per the
hermes-sweeper review: status.update emission, agent-turn dispatch via
_run_prompt_submit when the session is idle, and the busy-session
pending buffer that flushes once the session goes idle.
@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 17, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #60085 is the stale predecessor for this current-main salvage, while #59963 is a competing narrower implementation. This branch uses the per-session key, handles multi-board delivery and terminal cleanup, and buffers/wakes the TUI session safely; maintainer selection is needed.

@falkoro

falkoro commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Thank you — appreciate the careful salvage. Confirmed the cherry-pick is faithful: both commits carry the original falkoro authorship, it's on current main, and it includes the poller-level regression tests the sweeper asked for on #60085. Fine by me to merge this as the current-main version; I'll leave the original open too so maintainers can take whichever branch is cleanest. 🙏

@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 preserving the original authorship and adding poller-level coverage. The current-main premise is verified: tools/kanban_tools.py:1204-1207 writes/document TUI subscriptions, while current tui_gateway/server.py:9123-9125 only polls process completions.

Problems

  • Profile routing: the new collector resolves boards at tui_gateway/server.py:9187, but does not bind session["profile_home"]. Global-remote Desktop sessions explicitly retain this field and bind it around their agent construction at tui_gateway/server.py:1522-1534; set_hermes_home_override is context-local (hermes_constants.py:23-35). Because the poller is a separate thread, a non-launch profile session will poll the launch profile's Kanban DB instead of the profile that created its subscription.

Suggested changes

  • Scope the collector's Kanban reads to session["profile_home"] using the existing override/reset helpers, and add a two-profile regression test proving the selected profile's subscription is claimed.

Automated hermes-sweeper review.

Comment thread tui_gateway/server.py
return []
texts: list = []
try:
boards = _kb.list_boards(include_archived=False)

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 board lookup must be scoped to session["profile_home"]. In global-remote Desktop mode, non-launch sessions persist that profile home and build turns under a context-local override; this poller runs in another thread and otherwise reads the launch profile's Kanban DB, so it cannot see the subscription created by the selected profile.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/sessions Session lifecycle, resume, persistence, history labels Jul 18, 2026
teknium1 added a commit that referenced this pull request Jul 26, 2026
… override

The sweeper review on #66435 flagged that the collector doesn't bind
session["profile_home"]. That binding is intentionally unnecessary: the
kanban board is shared across profiles by design — kanban_home() anchors
on get_default_hermes_root(), which resolves the process env and ignores
context-local profile overrides (see the kanban_db.py module docstring).
Add a regression test that claims a subscription while a foreign-profile
set_hermes_home_override() is active, proving delivery still works for
non-launch-profile Desktop sessions.
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #72177 with both commits cherry-picked onto current main and @falkoro's authorship preserved in git log (badb240, 6247712). Thanks @cucurigoo for the faithful current-main salvage and @falkoro for the original fix and poller-level tests.

One note on the sweeper's profile_home concern: the suggested binding turned out to be intentionally unnecessary — the kanban board is shared across profiles by design (kanban_home() anchors on get_default_hermes_root() and deliberately ignores context-local profile overrides; see the hermes_cli/kanban_db.py module docstring). Instead of the binding, #72177 adds a regression test proving the collector delivers correctly while a foreign-profile set_hermes_home_override() is active.

@teknium1 teknium1 closed this Jul 26, 2026
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
… override

The sweeper review on NousResearch#66435 flagged that the collector doesn't bind
session["profile_home"]. That binding is intentionally unnecessary: the
kanban board is shared across profiles by design — kanban_home() anchors
on get_default_hermes_root(), which resolves the process env and ignores
context-local profile overrides (see the kanban_db.py module docstring).
Add a regression test that claims a subscription while a foreign-profile
set_hermes_home_override() is active, proving delivery still works for
non-launch-profile Desktop sessions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cron Cron scheduler and job management comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Kanban task event notifications never delivered — all 18 subscriptions have last_event_id=0

4 participants