Skip to content

fix(kanban): launch workers in verified systemd user scopes - #71259

Open
stigrunar wants to merge 1 commit into
NousResearch:mainfrom
stigrunar:hri-008-systemd-worker-scopes
Open

stigrunar wants to merge 1 commit into
NousResearch:mainfrom
stigrunar:hri-008-systemd-worker-scopes

Conversation

@stigrunar

@stigrunar stigrunar commented Jul 25, 2026 •

Copy link
Copy Markdown

Summary

  • launch Kanban workers through transient systemd --user scopes when the dispatcher is itself running under a same-UID user service
  • verify the exact launched PID, scope unit, control group, manager UID, and active state before accepting the launch
  • fail open to the existing direct spawn path when the host cannot prove that boundary, and route failed verification through the existing spawn-failure cleanup
  • preserve the existing integer worker_pid and direct-launch event contract while adding truthful scope receipt fields

This is a current-main successor to the verified per-worker systemd-scope portion of #63073. It intentionally does not include the fleet-cap portion, which overlaps later scheduler work in #69942. Thanks to the original #63073 contributor for identifying and implementing the systemd-scope direction.

Why

A worker launched directly by a long-lived dispatcher remains in the dispatcher's service cgroup. If the worker or one of its descendants survives the Python task lifecycle, process ownership and cleanup become ambiguous. A verified transient user scope gives each worker run a manager-owned cgroup boundary without changing behavior on unsupported or unverified hosts.

The scope path is deliberately gated: Linux only, same-UID user manager, dispatcher hosted by its own .service, and exact post-launch PID/cgroup verification. Any inability to prove those conditions preserves the direct launch behavior.

Verification

  • rebased onto upstream main at d5e135a51353c2dbc489d5c2583158b22d8efd7b; exact head b3b692dab6f2f41790ef9783d655f73c0f27ec03
  • scripts/run_tests.sh tests/hermes_cli/test_kanban_worker_systemd_scope.py tests/hermes_cli/test_kanban_worker_spawn_toolsets.py tests/hermes_cli/test_kanban_worker_terminal_cwd.py tests/hermes_cli/test_kanban_worker_session_source.py tests/hermes_cli/test_kanban_boards.py — 42 passed
  • the native scope lifecycle ran under a real systemd user scope and verified that timeout/requeue terminates the persisted scoped worker PID
  • ruff check hermes_cli/kanban_db.py tests/hermes_cli/test_kanban_worker_systemd_scope.py — clean
  • python -m py_compile hermes_cli/kanban_db.py — clean
  • git diff --check — clean

Boundaries

No deploy, service restart, live-runtime mutation, release-ref movement, merge, or auto-merge is included or implied.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cron Cron scheduler and job management labels Jul 25, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for preserving the direct-launch fallback and attempting real user-manager verification. The underlying premise remains live: current main directly launches workers at hermes_cli/kanban_db.py:8968-8989.

Problems

  • hermes_cli/kanban_db.py:9243 starts systemd-run --user --scope with Popen, but :9273 verifies and later returns that launcher's PID. The installed systemd-run(1) documentation describes scope mode as synchronous and the scoped command as manager-owned; therefore proc.pid is not a demonstrated scoped worker PID. This conflicts with current reclaim/timeout lifecycle code, which signals the persisted PID (hermes_cli/kanban_db.py:6902-6928, :7094-7106).

Suggested changes

  • Track a manager-reported scoped worker PID, or make scope-unit operations authoritative for liveness and termination; do not persist the systemd-run wrapper as worker_pid.
  • Extend the native lifecycle test to assert the persisted PID is in the scope and that reclaim/timeout actually stops the scoped worker.

Automated hermes-sweeper review.

Comment thread hermes_cli/kanban_db.py Outdated
@stigrunar

Copy link
Copy Markdown
Author

Ready for upstream re-review at exact head 56ee10a. The actionable scoped-worker PID/lifecycle finding is addressed with focused tests and live scope proof; details are in the original review thread.

@teknium1 teknium1 added 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 sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 30, 2026
@stigrunar
stigrunar force-pushed the hri-008-systemd-worker-scopes branch from 56ee10a to b3b692d Compare August 1, 2026 17:01
@stigrunar

Copy link
Copy Markdown
Author

Ready for fresh upstream re-review at exact head b3b692dab6f2f41790ef9783d655f73c0f27ec03 (@teknium1). The branch is rebased onto current upstream main (d5e135a51353c2dbc489d5c2583158b22d8efd7b), is mergeable again, and retains the manager-reported scoped worker PID/lifecycle repair. Verification: 42 focused tests passed, including the native systemd-scope lifecycle; ruff, py_compile, and git diff --check pass.

@stigrunar

Copy link
Copy Markdown
Author

Fresh exact-head repair validation for @teknium1: the blocking review was submitted against 0179c0ffb2513c40f8b88a0fa6d1548ae42f1726; current head b3b692dab6f2f41790ef9783d655f73c0f27ec03 already contains the manager-reported scoped worker PID repair and lifecycle regression coverage, so no additional code change or empty commit was appropriate. Re-run proof on this exact head: tests/hermes_cli/test_kanban_worker_systemd_scope.py = 12 passed, 1 native-host precondition skipped; focused Ruff, py_compile, and git diff --check all pass. The fork branch was lease/read-back checked and remains exactly b3b692dab6f2f41790ef9783d655f73c0f27ec03. Please re-review this exact head.

@stigrunar
stigrunar force-pushed the hri-008-systemd-worker-scopes branch from b3b692d to 4eecc64 Compare August 13, 2026 20:01
@stigrunar

Copy link
Copy Markdown
Author

Current-main recut pushed at exact head 4eecc64778a9ff3600e57af85f3812ad23e992d0 on base bfff32ae8c6a9c585431997a6cc3d791b6ec9af5. The old conflicting kanban_db.py patch was not mechanically rebased: only the verified systemd-user-scope seam was ported while preserving current review/notify/observer lifecycle. Fresh controller verification: 21 focused scope/dispatch tests passed; Ruff, py_compile, and git diff --check pass. The native scope test is retained but skipped in this shell because it is not running inside an authenticated user-service cgroup; no live runtime mutation was performed. Ready for maintainer re-review.

@stigrunar

Copy link
Copy Markdown
Author

Fresh exact-head repair validation for @teknium1: the blocking scoped-worker PID finding is addressed in the current PR candidate at exact head 4eecc64778a9ff3600e57af85f3812ad23e992d0. The candidate already contains the focused manager-reported PID repair and lifecycle regression coverage; no additional code change or empty commit is appropriate.

The implementation reads the transient scope's manager-reported ControlGroup, enumerates cgroup.procs, verifies the matching worker command (including shebang-interpreter argv), and persists that verified worker PID rather than assuming the systemd-run wrapper PID. The existing reclaim/max-runtime path then terminates the persisted scoped worker PID.

Fresh proof on exact head:

  • python -m pytest -q tests/hermes_cli/test_kanban_worker_systemd_scope.py -> 12 passed, 1 skipped (native test precondition: this worker is not itself inside the authenticated gateway service cgroup).
  • python -m pytest -q tests/hermes_cli/test_kanban_db.py -> 29 passed, 1 skipped.
  • focused Ruff, py_compile, and git diff --check pass.
  • live transient-scope smoke reached the user manager, selected the worker PID from the manager-reported scope cgroup, asserted the PID/cgroup invariant, exercised enforce_max_runtime, observed ready requeue, and verified the scope was gone. The smoke output recorded launcher_pid=1903109, worker_pid=1903109, final_status=ready, scope_gone=true; equality is acceptable here because identity was established from cgroup membership plus worker argv, not assumed from Popen.pid.

The fork branch remains pushed at exact head 4eecc64778a9ff3600e57af85f3812ad23e992d0. Please perform a fresh upstream re-review of this exact head; no merge or deploy action is requested.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants