Skip to content

fix(cli): honor display.background_process_notifications=off in the process drain - #123123

Closed
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-123114
Closed

liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-123114

Conversation

@liuhao1024

Copy link
Copy Markdown

What does this PR do?

display.background_process_notifications: off was only ever consulted by the gateway's completion injection (gateway/run_config_loaders.py); the CLI's _drain_process_notifications never read the key, so the documented off mode silently did nothing on the CLI. That left users hitting #123114 (a finished-but-unread background process restarts a new turn right after every Ctrl+C) with no configuration escape hatch at all — turning the key "off" changed nothing.

This PR makes the CLI drain honor off. It mirrors the gateway's semantics (gateway/run_notifications.py::_drain_watch_notifications): completion events are still drained, claimed and acknowledged — so durable async_delegations rows converge to delivered instead of replaying on the next restart — but the turn-starting _pending_input.put(...) injection is suppressed. Every other documented mode (concise/all/result/error) keeps today's CLI behavior unchanged.

With this fix, a user who sets off gets the Ctrl+C behavior the issue asks for as a stopgap: the interrupted turn ends and nothing new starts on its own, while wait/log/poll remain the way to read finished background output. The fuller purge UX proposed in the issue (ProcessRegistry.purge_notifications, /purge, ctrl_c_purge_notifications) is intentionally left open as a follow-up.

Related Issue

Relates to #123114 — restores the documented display.background_process_notifications: off escape hatch the issue reports as silently broken on the CLI; the proposed purge UX remains open for a follow-up.

Type of Change

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

Changes Made

  • hermes_cli/cli_process_notifications.py — add CLIProcessNotificationsMixin._background_notifications_suppressed() (reads CLI_CONFIG's display.background_process_notifications, fails open, only the exact off mode suppresses) and gate the injection loop in _drain_process_notifications with it; the drain/claim/acknowledge chain runs in every mode.
  • tests/hermes_cli/test_process_notification_display.py — parametrized regression: off consumes and acknowledges events while leaving _pending_input empty; concise and all keep injecting; the claim/ack chain is asserted to run under every mode.

How to Test

  1. python -m pytest tests/hermes_cli/test_process_notification_display.py tests/hermes_cli/test_cli_async_delegation_delivery.py tests/hermes_cli/test_subagent_notification_display.py -q — Observed result: 8 passed.
  2. python -m pytest tests/tools/test_async_delegation.py -q — Observed result: 36 passed (durable delivery chain unaffected).
  3. Red/green: reverting only the source change makes test_drain_honors_background_process_notifications_off[off-False] fail (_pending_input still receives the event); reapplying it turns the suite green.
  4. Manual equivalent of the issue's reproduction: set display.background_process_notifications: off in the CLI config, start a terminal(background=True) process, let it finish unread, interrupt the next turn with Ctrl+C — the turn should end and no new notification turn should start.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.4 (arm64)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A (no new key; the existing documented key now takes effect)
  • 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 (pure-Python config read, no platform surface touched)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

…rocess drain

The documented `off` mode gated only the gateway's completion injection
(NousResearch#9290); the CLI drain never consulted the key, so setting it silently did
nothing on the CLI — leaving no escape hatch against the post-Ctrl+C
notification turn restarts (NousResearch#123114).

Mirror the gateway semantics: the drain still claims and acknowledges
completion events (durable rows converge instead of replaying on restart),
but the turn-starting `_pending_input` injection is suppressed.

Fixes NousResearch#123114
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard tool/terminal Terminal execution and process management area/config Config system, migrations, profiles sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Sep 25, 2026

@arkheioncorp arkheioncorp 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.

Hermes Agent Review — COMMENT

Verdict: No blocking issues.

✅ Looks Good

  • hermes_cli/cli_process_notifications.py — reads and returns only for . The gate in skips the turn-starting injection but still drains, claims, and acknowledges events — matching the documented semantics (#9290).
  • tests/ — Parametrized over / / : → no injection; others → injection. All modes assert is non-empty, confirming the claim/ack chain always runs.

💡 Suggestions

  • None. The in is appropriate for defensive config reading.

No security concerns, no logic errors.

@arkheioncorp arkheioncorp 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.

Hermes Agent Review — COMMENT

Verdict: No blocking issues.

Looks Good

  • hermes_cli/cli_process_notifications.py — _background_notifications_suppressed reads CLI_CONFIG.display.background_process_notifications and returns True only for off. The gate in _drain_process_notifications skips the turn-starting injection but still drains, claims, and acknowledges events — matching the documented semantics (#9290).
  • tests/ — Parametrized over off / concise / all: off → no injection; others → injection. All modes assert acked is non-empty, confirming the claim/ack chain always runs.

Suggestions

  • None. The try/except Exception in _background_notifications_suppressed is appropriate for defensive config reading.

No security concerns, no logic errors.

@liuhao1024

Copy link
Copy Markdown
Author

Thanks for the review — good to have the drain/claim/ack chain and the off/concise/all parametrization confirmed against the documented semantics (#9290).

OutThisLife pushed a commit that referenced this pull request Sep 26, 2026
… as a user bubble

A `terminal(background=true, heartbeat=N)` tick queued a notification every N seconds
whether or not the process had printed anything, and every queued event costs the owning
session a full model turn. On Desktop and the TUI that turn painted the wake as a user
bubble ("[Background process ... heartbeat #9 ... (no new output since the last
heartbeat)]") followed by the model's "Still running normally." — over and over, for a
process whose row on the status stack already said it was running — and while the wake
held the session's turn, the user's own prompt sat queued behind it.

- `ProcessRegistry._emit_heartbeat` skips a tick with no new output. The sequence counts
  delivered beats only; the "(no new output)" placeholder in the formatter is gone.
- TUI/Desktop type heartbeat rows `display_kind: hidden` (the kind both clients and the
  transcript preview already honour); the CLI paints a one-line receipt and persists the
  row hidden, so reopening the session in Desktop shows only the agent's reply.
- Desktop hydration drops heartbeat rows persisted by older backends the same way.
- `display.background_process_notifications: off` is honored by the TUI/Desktop poller and
  the CLI drain, not just the messaging gateway. `off` mutes process-driven wakes only:
  a finished `delegate_task(background=true)` still lands.

Supersedes #123123 (cherry-picked; scoped so `off` keeps subagent results) and #119202
(cherry-picked; `heartbeat: 0` is schema-valid so models that materialize every field
stop tripping the foreground guard).
OutThisLife added a commit that referenced this pull request Sep 27, 2026
… as a user bubble

A `terminal(background=true, heartbeat=N)` tick queued a notification every N seconds
whether or not the process had printed anything, and every queued event costs the owning
session a full model turn. On Desktop and the TUI that turn painted the wake as a user
bubble ("[Background process ... heartbeat #9 ... (no new output since the last
heartbeat)]") followed by the model's "Still running normally." — over and over, for a
process whose row on the status stack already said it was running — and while the wake
held the session's turn, the user's own prompt sat queued behind it.

- `ProcessRegistry._emit_heartbeat` skips a tick with no new output. The sequence counts
  delivered beats only; the "(no new output)" placeholder in the formatter is gone.
- TUI/Desktop type heartbeat rows `display_kind: hidden` (the kind both clients and the
  transcript preview already honour); the CLI paints a one-line receipt and persists the
  row hidden, so reopening the session in Desktop shows only the agent's reply.
- Desktop hydration drops heartbeat rows persisted by older backends the same way.
- `display.background_process_notifications: off` is honored by the TUI/Desktop poller and
  the CLI drain, not just the messaging gateway. `off` mutes process-driven wakes only:
  a finished `delegate_task(background=true)` still lands.

Supersedes #123123 (cherry-picked; scoped so `off` keeps subagent results) and #119202
(cherry-picked; `heartbeat: 0` is schema-valid so models that materialize every field
stop tripping the foreground guard).
OutThisLife added a commit that referenced this pull request Sep 27, 2026
… as a user bubble

A `terminal(background=true, heartbeat=N)` tick queued a notification every N seconds
whether or not the process had printed anything, and every queued event costs the owning
session a full model turn. On Desktop and the TUI that turn painted the wake as a user
bubble ("[Background process ... heartbeat #9 ... (no new output since the last
heartbeat)]") followed by the model's "Still running normally." — over and over, for a
process whose row on the status stack already said it was running — and while the wake
held the session's turn, the user's own prompt sat queued behind it.

- `ProcessRegistry._emit_heartbeat` skips a tick with no new output. The sequence counts
  delivered beats only; the "(no new output)" placeholder in the formatter is gone.
- TUI/Desktop type heartbeat rows `display_kind: hidden` (the kind both clients and the
  transcript preview already honour); the CLI paints a one-line receipt and persists the
  row hidden, so reopening the session in Desktop shows only the agent's reply.
- Desktop hydration drops heartbeat rows persisted by older backends the same way.
- `display.background_process_notifications: off` is honored by the TUI/Desktop poller and
  the CLI drain, not just the messaging gateway. `off` mutes process-driven wakes only:
  a finished `delegate_task(background=true)` still lands.

Supersedes #123123 (cherry-picked; scoped so `off` keeps subagent results) and #119202
(cherry-picked; `heartbeat: 0` is schema-valid so models that materialize every field
stop tripping the foreground guard).
@liuhao1024

Copy link
Copy Markdown
Author

Closing — the fix landed on main as ede966b ("fix(cli): honor display.background_process_notifications=off in the process drain", carried with this PR's authorship), which carries the exact source and test changes from this branch. Nothing further to carry here.

@liuhao1024 liuhao1024 closed this Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/terminal Terminal execution and process management type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants