Skip to content

fix(gateway): stop Desktop cron from contending with profile gateways - #102174

Closed
fangliquanflq wants to merge 3 commits into
NousResearch:mainfrom
fangliquanflq:fix/gateway-desktop-multiplex-cron-ownership
Closed

fangliquanflq wants to merge 3 commits into
NousResearch:mainfrom
fangliquanflq:fix/gateway-desktop-multiplex-cron-ownership

Conversation

@fangliquanflq

Copy link
Copy Markdown

What does this PR do?

Prevents the Desktop cron scheduler from entering profiles whose jobs are already owned by either a profile-local gateway or the live default-profile multiplexer. On Windows, the duplicate manager path reported in #102156 left the same profile's cron, state, and log resources open from competing long-lived processes while Desktop tried to start that bot's backend.

Symptom

With a standalone default gateway multiplexing several profiles, opening a sleeping bot in Desktop can fail while both the gateway and Desktop cron scheduler manage the same named profile.

Impact

Affected Windows Desktop users can be unable to open bot chats until the standalone gateway is stopped and the affected profiles are rebuilt.

Bug Cause

Trigger: hermes_cli/web_server.py:270 / _start_desktop_cron_ticker() when the default gateway multiplexes a named profile.

Causal chain:

  1. The Desktop backend enumerates every local profile for its multiplex cron scheduler.
  2. Its ownership gate checks only _check_gateway_running(profile_home), but a multiplexer-owned named profile has no profile-local gateway PID or runtime state.
  3. Desktop admits that profile and the shared scheduler performs startup recovery and recurring ticks in the same profile already owned by the default gateway.

Why it is wrong: The gate omits the existing authoritative multiplexer ownership probe, and the shared scheduler does not apply its supplied gate during startup recovery.

Working sibling / contrast: A profile with its own gateway is already excluded by _check_gateway_running(). Profile, gateway, and cron status paths also already use _served_by_running_multiplexer() for named profiles.

Ruled out: Generic concurrent SQLite access is not disabled; Hermes supports multiple readers and writers. This change removes duplicate cron-manager ownership rather than restricting unrelated session access.

Fix

Extend the Desktop ownership gate to reject profiles served by the live default multiplexer. Apply the scheduler's profile gate to startup recovery and heartbeat initialization as well as recurring tick cycles, so rejected profiles are never entered by the Desktop cron manager.

Related Issue

Fixes #102156

Type of Change

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

Changes Made

  • hermes_cli/web_server.py - recognize default-multiplexer ownership in the Desktop cron gate.
  • cron/scheduler_provider.py - honor the profile gate before startup recovery and heartbeat writes.
  • tests/cron/test_cron_multiplex_desktop_ticker_scope.py - cover direct and multiplex ownership plus zero startup writes for rejected profiles.

How to Test

  1. Start a default-profile gateway with gateway.multiplex_profiles: true and at least one named profile.
  2. Start Desktop and open the named bot profile.
  3. Confirm the Desktop cron scheduler does not create or update ticker ownership files for that gateway-owned profile while the default gateway continues firing its jobs.
  4. Run the focused regression suite:
scripts/run_tests.sh tests/cron/test_cron_multiplex_desktop_ticker_scope.py -q

Result: 3 passed.

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 the repository test wrapper on the related regression suite and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested the automated regression on Windows 11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) - N/A; behavior is covered by code comments and tests
  • I've updated cli-config.yaml.example if I added/changed config keys - N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows - N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide
  • I've updated tool descriptions/schemas if I changed tool behavior - N/A

Screenshots / Logs

N/A - scheduler ownership is covered by the automated regression test.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) platform/windows Native Windows-specific behavior or breakage area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Sep 3, 2026
@Enough1122

Copy link
Copy Markdown

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

Chunk5 review — fix(gateway): stop Desktop cron contending with profile gateways

Two coordinated changes: eligibility-aware cron initialisation plus a desktop-ticker stand-down for gateway-owned profiles. Addresses real contention (#100489, #102156).

  • cron/scheduler_provider.py:713-790 — initialize_eligible_profiles with the initialized_homes epoch set is well-reasoned: losing eligibility ends ownership, and re-eligibility re-runs recovery before ticking. Returning only successfully initialised homes means one profile's corrupt store can't wedge the cycle (preserves the multiplex_profiles: secondary profiles' cron jobs never fire — no ticker, and no warning #74878 guarantee). Lifting the open out of the if-guard while keeping single refcount/close accounting avoids a leak/double-close — good.
  • hermes_cli/web_server.py:306-331 — the stand-down predicate (_check_gateway_running or _served_by_running_multiplexer) correctly identifies profiles ticked with live adapters elsewhere, so the adapter-less ticker stops winning the tick-lock race.
  • Non-blocking: profile_gate is now consulted on every cycle — if the gate itself is expensive or flaky (e.g. transient "not running" reads), profiles could flap between owned/unowned and re-run recovery each time. Worth confirming the gate is cheap/stable, or caching its verdict briefly.

Non-blocking overall. Solid multiplex work.

@teknium1

Copy link
Copy Markdown
Collaborator

Superseded by #108428 (on main as 0e57543), with Co-authored-by credit to you. Same direction you took — the Desktop cron ticker stands down for profiles owned by another process — implemented as a per-tick gate that consults both the profile's own gateway.pid and named_profile_served_by_running_multiplexer, and the tick set now honours the default profile's multiplex_profile_allowlist. Thanks, @fangliquanflq.

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/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows 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]: Desktop: clicking a Bot row in the Bots pane does nothing for most profiles (Windows 11, v0.21.0)

4 participants