Skip to content

fix(kanban): deliver actionable active-profile notifications - #62380

Closed
salimhamed wants to merge 1 commit into
NousResearch:mainfrom
salimhamed:fix/age157-actionable-kanban-notification
Closed

fix(kanban): deliver actionable active-profile notifications#62380
salimhamed wants to merge 1 commit into
NousResearch:mainfrom
salimhamed:fix/age157-actionable-kanban-notification

Conversation

@salimhamed

Copy link
Copy Markdown
Contributor

Summary

  • route explicitly stamped active named profiles through the active adapter map
  • preserve actionable blocked-card response instructions instead of truncating at 160 characters
  • retain fail-closed adapter routing for multiplex secondary profiles

Reproduction

A gateway running profile main with a Kanban subscription stamped notifier_profile=main treated main as a secondary profile. _authorization_adapter() returned None, so the notifier rewound each claim and never delivered. Separately, a 413-character approval reason was truncated before its Approve / Request changes choices.

Tests

uv run --with pytest --with pytest-asyncio pytest -q tests/gateway/test_kanban_notifier.py tests/gateway/test_multiplex_profile_authz.py tests/gateway/test_kanban_notifier_watcher_dispatch_gate.py

18 passed.

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have 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 labels Jul 11, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused regression fix. The premise is confirmed on current main: gateway/authz_mixin.py:50-57 treats a stamped active named profile as an unregistered secondary and returns None, while the notifier resolves delivery through that helper at gateway/kanban_watchers.py:313-335. This conflicts with the established adapter topology: the active profile is stored in self.adapters and only non-active profiles use _profile_adapters (gateway/run.py:2816-2822).

The blocked-event formatter also truncates the reason at gateway/kanban_watchers.py:368-372; the proposed larger bounded preview addresses the reported loss of response instructions. The added tests cover both the named-active-profile route and actionable blocked text. The PR is mergeable, and the commits after its base do not touch these paths.

Automated hermes-sweeper review.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 11, 2026
@salimhamed
salimhamed force-pushed the fix/age157-actionable-kanban-notification branch from 49fc71e to 5037514 Compare July 15, 2026 07:41
@qualiobra

Copy link
Copy Markdown

Independently confirmed this exact failure in a real non-multiplex gateway running under a named active profile.

Observed chain:

  1. The gateway was launched with --profile <named-profile> and its connected Telegram adapter lived in self.adapters.
  2. Kanban auto-subscribe stamped the same active name into notifier_profile.
  3. A completed event existed, but the subscription remained at last_event_id=0 across multiple watcher cycles.
  4. _authorization_adapter(platform, <named-profile>) returned None because the name was treated as multiplex-secondary and looked up only in _profile_adapters.
  5. The watcher therefore rewound the claim before adapter.send() and emitted no normal-level warning.

I also reproduced the resolver/notifier behavior independently with an ephemeral database and recording adapter:

lookup-none-is-primary: True
lookup-default-is-primary: True
lookup-named-active: None
named-active-send-count: 0
named-active-sub-count-after: 1
named-active-last-event-id-after: 0
default-send-count: 1
default-sub-count-after: 0

I checked current remote main and the defective profile_name != "default" branch is still present there. The change in this PR to recognize active_profile matches the observed root cause, while preserving fail-closed behavior for genuinely secondary profiles without an adapter.

I did not check out or modify the PR branch because my local Hermes checkout contains unrelated concurrent work. This comment confirms the bug and the proposed routing direction, not a full branch test or formal approval.

Environment of the operational reproduction: macOS arm64, Python 3.11, Hermes Agent 0.18.2. Private chat IDs, task IDs, and local paths intentionally omitted.

@teknium1 teknium1 added the area/profiles Multi-profile isolation, HERMES_HOME scoping label Jul 19, 2026

@KoryakovDmitry KoryakovDmitry left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I independently reproduced the named-primary notifier failure, but the current resolver condition leaves one cross-profile routing hole:

if profile_name and profile_name not in {"default", active_profile}:

When the active gateway profile is named (for example job-search), an explicit stamped profile="default" still falls through to self.adapters, i.e. the named primary bot. That does not preserve the documented fail-closed invariant for every non-primary stamp.

The safer condition is to resolve exactly one primary identity:

active = getattr(self, "_active_profile_name", None)
primary_profile = active() if callable(active) else "default"
primary_profile = str(primary_profile or "default").strip() or "default"
if profile_name and profile_name != primary_profile:
    # secondary lookup, then fail closed

A regression contract that fails on the current PR implementation:

runner._active_profile_name = lambda: "job-search"
runner._profile_adapters = {}
assert runner._authorization_adapter(Platform.WECOM, profile="default") is None

I verified the stricter form against the notifier + multiplex/upstream authz suites: 30 passed; Ruff and ty pass. The PR is also currently conflicting with main, so it needs a rebase before this can land.

teknium1 pushed a commit that referenced this pull request Jul 26, 2026
A gateway running under a named active profile (e.g. `hermes -p main gateway`)
stamps kanban auto-subscriptions with notifier_profile=main, but
_authorization_adapter() treated any name other than the literal "default"
as a multiplex secondary and consulted only _profile_adapters — empty on
standalone gateway-per-profile deployments. The helper failed closed, the
notifier rewound the claim, and the notification was silently retried
forever (#71340).

Recognize the gateway's own active profile name as primary so its stamped
subscriptions resolve via self.adapters; genuinely secondary profiles keep
the fail-closed lookup.

Salvaged from PR #62380 (the unrelated blocked-reason truncation change is
intentionally not taken).
teknium1 pushed a commit that referenced this pull request Jul 26, 2026
A gateway running under a named active profile (e.g. `hermes -p main gateway`)
stamps kanban auto-subscriptions with notifier_profile=main, but
_authorization_adapter() treated any name other than the literal "default"
as a multiplex secondary and consulted only _profile_adapters — empty on
standalone gateway-per-profile deployments. The helper failed closed, the
notifier rewound the claim, and the notification was silently retried
forever (#71340).

Recognize the gateway's own active profile name as primary so its stamped
subscriptions resolve via self.adapters; genuinely secondary profiles keep
the fail-closed lookup.

Salvaged from PR #62380 (the unrelated blocked-reason truncation change is
intentionally not taken).
teknium1 pushed a commit that referenced this pull request Jul 26, 2026
A gateway running under a named active profile (e.g. `hermes -p main gateway`)
stamps kanban auto-subscriptions with notifier_profile=main, but
_authorization_adapter() treated any name other than the literal "default"
as a multiplex secondary and consulted only _profile_adapters — empty on
standalone gateway-per-profile deployments. The helper failed closed, the
notifier rewound the claim, and the notification was silently retried
forever (#71340).

Recognize the gateway's own active profile name as primary so its stamped
subscriptions resolve via self.adapters; genuinely secondary profiles keep
the fail-closed lookup.

Salvaged from PR #62380 (the unrelated blocked-reason truncation change is
intentionally not taken).
@teknium1

Copy link
Copy Markdown
Contributor

Partially merged via PR #72241 — your adapter-routing fix (active named profile resolves through the active adapter map, killing the #71340 rewind-forever loop) was cherry-picked with your authorship preserved. The 160→3000-char blocked-reason truncation change was out of scope for that cluster and was not taken — feel free to resubmit it as a focused PR if you still want it. Thanks!

@teknium1 teknium1 closed this Jul 26, 2026
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
A gateway running under a named active profile (e.g. `hermes -p main gateway`)
stamps kanban auto-subscriptions with notifier_profile=main, but
_authorization_adapter() treated any name other than the literal "default"
as a multiplex secondary and consulted only _profile_adapters — empty on
standalone gateway-per-profile deployments. The helper failed closed, the
notifier rewound the claim, and the notification was silently retried
forever (NousResearch#71340).

Recognize the gateway's own active profile name as primary so its stamped
subscriptions resolve via self.adapters; genuinely secondary profiles keep
the fail-closed lookup.

Salvaged from PR NousResearch#62380 (the unrelated blocked-reason truncation change is
intentionally not taken).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

5 participants