Commit Graph

27 Commits

Author SHA1 Message Date
kshitijk4poor 8c3a35b69d fix(gateway-windows): bind the attestation to process birth, not the ledger claim
Review follow-ups on the identity binding:
- The sentinel's `start_time` is `time.time()` at `record_startup`, seconds
  after the process was born once imports finish, so comparing it with
  psutil's create_time within 2 s would have read every real gateway as
  undecidable and silently stopped the #109538 cold-start. `record_startup`
  now stamps `create_time` (psutil birth via the existing
  `process_identity._process_create_time`), `mark_exited` carries it, and
  the attestation compares birth to birth. A sentinel from a gateway older
  than the stamp falls back to the PID-only rule.
- A resume token written by pre-generation code and resumed by this code
  probes the marker again instead of skipping the spawn.
- Horizon allows a 60 s backwards clock step; the unused `now` parameter is
  gone; the create-time tolerance is a named constant; the read-then-unlink
  in `_consume_start_attestation` is documented as best-effort.
2026-09-14 20:51:37 +05:30
kshitijk4poor 060c0cd0f8 fix(update): authorize the Windows cold-start from the resume token's attestation generation
The resume token only recorded `cold_start_if_installed: bool` and execution re-read the
mutable one-shot start-attestation marker to decide whether a Desktop-owned install still owed
a cold-start. A concurrent `hermes gateway status`/`start` (`check_start_attestation`) consumes
that marker between plan and execution, so the spawn was skipped and the token cleared with no
gateway running.

The marker now carries a `generation` nonce. The plan records the generation whose unclean
death authorized the cold-start on the token (`attested_generation`); execution authorizes the
spawn from the token, still re-checks live gateway PIDs, and consumes the marker only while it
is still that generation — a newer marker written by a concurrent start keeps its own report.
`attested_gateway_died` becomes `attested_death_generation` (`None` = undecidable, fail closed).

Follow-up to #110020 review thread (a).
2026-09-14 20:51:37 +05:30
kshitijk4poor ccc335cdba fix(update): consume the start attestation only after the cold-start is ready
_cold_start_windows_gateway_after_update cleared the dead attestation as soon
as _spawn_detached() returned a PID, before _wait_for_gateway_ready() proved
the gateway survived. When readiness failed, the RuntimeError registered the
retry, but the retry then saw Desktop lifecycle ownership with no marker and
returned success without spawning anything — the silent outage of #109538
came back through the retry path (#110020 review). The marker is now
consumed after readiness is confirmed, so a failed spawn leaves the retry
its recovery obligation.
2026-09-14 20:51:37 +05:30
kshitijk4poor 7e7641561b refactor(gateway-windows): one death predicate, no second process scan
attested_gateway_died() re-ran find_gateway_pids() (current profile only)
although both callers had just proven the process table empty with
all_profiles=True, and it re-implemented check_start_attestation's
liveness rule. Callers now pass the liveness they hold (current_pids=[])
and both probes share _attested_dead(), so the consuming and read-only
twins cannot drift.
2026-09-14 19:47:09 +05:30
kshitijk4poor 674e3f3cd1 fix(update): consume the start attestation once the cold-start spawns
attested_gateway_died() is deliberately read-only so the CLI-start warning
still fires, but that left the dead marker in place after the update path
acted on it. If the restored gateway never became ready (or died again
before the next CLI start consumed the marker), the same stale crash marker
would re-authorize another cold start against Desktop ownership on the
next update. Clear it via the existing _clear_start_attestation() path
right after _spawn_detached() succeeds - the marker has done its job at
that point; a new one is written once the spawn is confirmed ready.
2026-09-14 19:47:09 +05:30
ennheng 830c8f443d fix(update): keep the Windows cold-start plan for a dead attested gateway
A Desktop self-update hand-off exits the app before the updater runs and can
kill the messaging gateway in those same seconds (#109538), so the updater's
discovery finds no live PID while the one-shot start attestation still
vouches for the dead one. Both Desktop-ownership checks then read "nothing
running" as "nothing to restore" and the bot stayed down until a manual
start.

Consult the attestation non-destructively before Desktop-owned lifecycle
suppresses a cold-start: a vouched-for PID gone without a clean ledger exit
keeps the plan and is restored; no attested death preserves the #76129 skip
unchanged.
2026-09-14 19:47:09 +05:30
teknium1 466ccac5ab fix(windows-update): a user-launched hermes serve/dashboard is never a Desktop backend
Canonicalising _is_backend_argv onto _hermes_holder_subcommand dropped the
'-m hermes_cli.main' entry-shape discriminator the old predicate had. That
widened _orphaned_desktop_backend_pids to tree-kill a standalone hermes.exe
serve / hermes dashboard whose console parent died. The Desktop's only spawn
shape is -m hermes_cli.main (apps/desktop/electron/main.ts), so _is_backend_argv
now forwards to _looks_like_desktop_control_plane — the same canonical-subcommand
AND entry-shape predicate — instead of a second copy. Test matrix gains a
'Desktop backend?' column with plain hermes serve / hermes.exe dashboard rows.
2026-09-13 05:21:02 -07:00
teknium1 c1e58f4cb2 fix(process-identity): desktop reap, Windows updater and profile liveness use the canonical matchers
Two kill/relaunch predicates decided identity by argv substring, the bug class root AGENTS.md
forbids: hermes_cli/dashboard_procs.py::_is_desktop_local_serve_cmdline (`"serve" not in cmd`,
on the orphan-reap KILL path) and hermes_cli/update_cmd_windows.py::_is_backend_argv
(`" serve" in argv_low`, in the very file that defines _hermes_holder_subcommand). Both now ask
the canonical token classifier; host/port are read as flag values, not substrings.

hermes_cli/profiles.py::_check_gateway_running open-coded rungs 1/3 of
gateway.status.resolve_gateway_liveness and skipped the multiplexer rung; it is now that ladder
scoped to the profile dir (pid probe keeps cleanup_stale=False so a probe for another profile
never unlinks its PID file). The gateway/status.py ladder itself is untouched.

Behavior change: `hermes kanban --preserve-cache --host 127.0.0.1 --port 0` and
`-m dashboard serve`-style argv are no longer classified as serve backends (never killed /
relaunched as one); a named profile served by the live default multiplexer now reads as
running from _check_gateway_running (previously only via the separate
_served_by_running_multiplexer OR at some call sites).
2026-09-13 05:21:02 -07:00
Teknium e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium d1bb7fb9cf refactor(hermes_cli/update): fold venv-holder classification and service-stop guards in the Windows update module 2026-09-03 00:02:04 -07:00
Teknium 01d07b6b6c refactor(hermes_cli/update): unify try/log/default + SCM service handle in the Windows update module; compact docstrings 2026-09-02 23:58:28 -07:00
Teknium b5605b7a82 refactor(hermes_cli/update): compact Windows gateway pause/resume module — walrus rungs, poll helper reuse, shared purpose tuple 2026-09-02 23:52:18 -07:00
Teknium 5bf59a2e3a refactor(hermes_cli/update): fold argv snapshot + SCM token bookkeeping in the Windows pause path 2026-09-02 22:59:37 -07:00
Teknium 5607b7beeb refactor(hermes_cli/update): hoist stdlib imports, single-line remaining signatures in update_cmd_windows 2026-09-02 22:55:58 -07:00
Teknium d8a1ee3bfd refactor(hermes_cli/update): table-drive the ledger/orphan tree-reap rungs 2026-09-02 22:50:53 -07:00
Teknium 35b0d18987 refactor(hermes_cli/update): _poll_until + module-level identity-alive check for SCM service stop/start 2026-09-02 22:39:13 -07:00
Teknium c99cefd33c refactor(hermes_cli/update): table-drive SCM rollback, compact pause/resume/venv-guard call sites in update_cmd_windows 2026-09-02 22:30:34 -07:00
Teknium 36b9c06778 refactor(hermes_cli/update): compact venv-holder classifier, ledger/orphan rungs, and service-identity checks in update_cmd_windows 2026-09-02 22:26:28 -07:00
Teknium 1a3c611e02 refactor(hermes_cli/update): _abort_on_error ctx manager for mandatory Windows steps; fold relaunch try/except pairs; compact multi-line prints/raises 2026-09-02 22:00:23 -07:00
Teknium 15569c3d8d refactor(hermes_cli/update): compact update_cmd_windows docstrings (keep every WHY) 2026-09-02 21:33:04 -07:00
Teknium 426bac85bf refactor(hermes_cli/update): split Windows pause/resume/venv-guard god functions into phase helpers; unify psutil/sc.exe/reap-rescan patterns; compact restart_recovery bucketing 2026-09-02 21:16:03 -07:00
Teknium 60041b787a refactor(update): join short multi-line statements onto one line (AST-identical, -416 lines) 2026-09-02 16:52:38 -07:00
Teknium 15651bd877 refactor(update): lift cold-start plan and SCM service pause out of _pause_windows_gateways_for_update (261 -> 163 LOC) 2026-09-02 16:50:01 -07:00
Teknium 0bfcf42133 refactor(update): unify venv-prefix and backend-argv helpers in update_cmd_windows (3+2 call sites) 2026-09-02 16:48:45 -07:00
Teknium d46eb1f964 refactor(update): collapse 90 try/except-pass and try/except-logger.debug blocks into suppress()/_best_effort() (new leaf update_cmd_common.py) 2026-09-02 16:34:55 -07:00
Teknium a59680217f refactor(update): hand-compact comments/docstrings in split modules (AST-identical); re-export get_hermes_home/shutil; repoint source-inspection tests 2026-09-02 16:11:02 -07:00
Teknium 5206a504ca refactor(update): split Windows gateway lifecycle into update_cmd_windows.py 2026-09-02 15:40:14 -07:00