Phase 10.2B: planned-stop runtime correction (stdlib-only marker protocol + H3 standalone stop) - #2
Closed
yshen92 wants to merge 4 commits into
Closed
Conversation
…module The H3 deployment transaction needs to stop a running gateway from the exact bytes of a reviewed Git blob, executed via "python -I -S -B -" on stdin, without importing the broad Hermes package graph (hermes_cli.main bootstrap, hermes_cli.config provider discovery, user plugins, dotenv). gateway/planned_stop_protocol.py is now the single definition of the planned-stop marker protocol (filename, 60s TTL, record fields, staleness, PID/start-time matching, /proc start-time observation, atomic wire-compatible write). gateway/status.py and hermes_cli/update_cmd.py delegate to it via thin wrappers; marker bytes and consumer semantics are unchanged, so a marker written by the new helper is consumed by the currently deployed R0 gateway (01edcad; its gateway/status.py blob ce02648 is byte-identical at this commit's base). The standalone half is hard-bound to the production shape: Linux, the hermes account (uid resolved from passwd and required to match euid), verified HOME/HERMES_HOME/XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESS, the user-scope hermes-gateway.service at its expected fragment path, /usr/bin/systemctl --user only (never system scope), MainPID + Environment-reported HERMES_HOME from "systemctl --user show" as the sole authority, no filesystem mutation before the marker write, then exactly one "systemctl --user stop" with a 90s timeout. Everything else fails closed. Tests cover R0 compatibility by executing the exact historical status.py blob, stdlib-only stdin execution under -I/-P -S -B with fake managers, import/bootstrap closure, adversarial plugin and sitecustomize/.pth fixtures, scope/PID/start-time rejection matrices, pre-marker mutation freedom, and stop-failure reporting. No test can reach a real service manager. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
yshen92
marked this pull request as ready for review
August 14, 2026 13:29
…robe The standalone planned-stop path fails closed at the platform stage unless the host is Linux, so the signal-0 probe can never execute on Windows; psutil is unavailable by design in this stdlib-only module. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
PR #2: APPROVED for Phase 10.2B planned-stop runtime-correction scope. |
yshen92
pushed a commit
that referenced
this pull request
Aug 18, 2026
Two independent bugs let a deleted profile reappear / leave orphaned resources on next launch: 1. hermes_cli/profiles.py's backend-process scanner required argv[0] to resolve to an executable literally named "hermes". Electron's pool-backend spawn resolves the hermes console-script shim's path and execs it via the interpreter directly (python3 /path/to/hermes ...), so argv[0] reports as "python3" and the scanner never matched the running backend -- delete removed the profile's files but left its live backend process running (still bound to a port via uvicorn), which accumulates across repeated delete/recreate cycles. 2. The desktop sidebar's ProfileRail only refreshed its cached profile list once, on mount, so a delete/create/rename from another surface (another window, or the CLI) left a stale ghost entry until something unrelated triggered a refetch. Note: a delete via this window's own Manage-Profiles view already refreshes the shared $profiles atom ProfileRail subscribes to (confirmed by reading refreshProfiles() and handleConfirmDelete()) -- this fix only covers the cross-window/cross- process staleness gap, not a duplicate of the already-merged NousResearch#57329's Manage-Profiles rail-refresh work. Fix 1: recognize a python-interpreter argv[0] exec'ing a hermes-named console-script shim via argv[1]. Fix 2: refresh the profile list on window focus/visibilitychange, matching the existing pattern used elsewhere in the sidebar (sidebar/index.tsx, use-background-sync.ts, star-map.tsx, use-gateway-boot.ts all use the same focus+visibilitychange pattern). ## Related work already on main PR NousResearch#57329 (merged) fixed the *headline* symptom from issue NousResearch#52279 (deleted profile respawns) via a different, non-overlapping mechanism: routing profile-delete through the primary backend instead of spawning a fresh pool backend, plus a separate recreation guard in ensure_hermes_home() (NousResearch#49435, merged) that makes a backend spawned into a deleted profile's directory raise FileNotFoundError instead of silently recreating it. This PR is NOT a duplicate of that fix. Verified: even with both of those merged, a backend process that survives because of gap #1 above still holds a bound port via uvicorn -- it just can no longer resurrect the profile directory. That's real resource-hygiene, not a symptom already covered. Gap #2 touches a different file/component (ProfileRail / profile-switcher.tsx) than NousResearch#57329's rail-refresh half (which touched the Manage-Profiles view's own $profiles.ts / index.tsx) and covers a distinct staleness path (cross-window/cross-process, not same-window delete-then-refresh). Tests: tests/hermes_cli/test_profiles.py -- 156 passed (existing + regression coverage for the argv[0] python-interpreter detection case). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
Closing without merge following the 2026-08-21 Dev-Review v2 strategic reassessment. Restricted-worker/H3 hardening is now Deferred, Evidence-Triggered and is no longer a prerequisite for framework activation. This PR and its exact reviewed commit are retained as historical/future hardening evidence; the branch should not be deleted. If the capability is needed again, reassess it against the then-current Hermes runtime/upstream rather than automatically resuming this deployment path. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 10.2B planned-stop runtime correction
This PR provides the minimal Phase 10.2B correction needed for a standalone H3 stop to cleanly stop the immutable deployed R0 gateway before the candidate checkout.
It keeps the ordinary planned-stop wire format intact and reuses R0's existing takeover marker as the standalone stop intent channel. That marker is intentionally invisible to R0's planned-stop filesystem watcher, so the actual service-manager
SIGTERMis the first shutdown-handler invocation and R0 consumes the intent as planned. The candidate runtime also retains a compact monotonic planned-shutdown classification so a planned shutdown cannot be downgraded by a duplicate later signal after upgrade.Runtime identities
fe085446b864cb7d31f57499e407ce0f7275c51101edcadbd194f81bd7eceb9ca267737830ce24c0e6982c8b7e25367c9cfb15d8b95da185feb7fc232bc81537c1— extract the planned-stop protocol into a stdlib-only module287f132ec8— suppress the Windows-footgun lint on the Linux-gated liveness probebd3c22d29e— preserve planned classification across duplicate shutdown notifications in the candidate runtimee6982c8b7e— make the standalone stop cleanly stop immutable exact R0 without the watcher racegateway/planned_stop_protocol.py246e57e3e9a341b7343fc0b4067efba89b18ac5e433022971acac19388eff86a07c6ca2037e772b82be121563c41f5ccbaa1c73fExact failure and correction
Live H3 qualification was run against the earlier candidate
287f132ec8410bff19a8cfd553ab1b0bf7f90dd1. Step E wrote a valid ordinary planned-stop marker andsystemctl --user stop hermes-gateway.servicereturned success, but immutable R0's watcher invokedshutdown_signal_handler(None)first and consumed that marker. About 6.9 ms later, systemd's realSIGTERMinvoked the handler again with no marker remaining, which latched an unexpected shutdown and producedResult=exit-code,ExecMainStatus=1. The transaction stopped at Step E; the candidate was not checked out or deployed.The standalone helper now writes R0's already-supported
.gateway-takeover.jsonrecord before issuing the same singlesystemctl --user stop. R0's watcher does not observe that filename. The subsequent realSIGTERMconsumes the takeover record and follows R0's clean planned/takeover exit path. No sleep, retry, service-policy change, or H3 expectation workaround is involved.Protocol and safety guarantees
target_pid,target_start_time,stopper_pid,written_at; PID/start-time and TTL checks are unchanged.gateway/status.pydelegating to it to avoid parallel implementations.<absolute-python> -I -S -B -over stdin.HERMES_HOME,/procstart-time, path-containment, and stale-marker protections remain fail-closed./usr/bin/systemctl --user stop hermes-gateway.serviceinvocation.Qualification and review history
gateway/run.pyblob (24d501b5b752fe37d201843990847d1bbd306a8d) andgateway/status.pyblob (ce02648a958f9a281303dd825ad45b2fdc8eb046). They first reproduce the old watcher-first failure, then drive the current standalone helper through the exact R0 shutdown handler and assert the clean manager-equivalent result:ActiveState=inactive,Result=success,ExecMainCode=1,ExecMainStatus=0.handler(None) -> handler(SIGTERM)remains planned, while a standalone unmarkedSIGTERMremains unexpected/non-zero./procparity test is skipped on macOS; its exact-R0 compatibility siblings execute against the pinned historical blobs.ruff,py_compile, andgit diff --checkpass.No
hermes-config, H3 runner, Phase 10.2B binding, or deployment files are changed by this PR. A fresh adversarial review, authority rebind, readiness/authorization cycle, and live H3 rerun are still required.Do not merge. Keep this PR open as input to the next Phase 10.2B binding.