Commit Graph

15 Commits

Author SHA1 Message Date
teknium1
fd14f50b5d feat(metrics): hermes.update.run/stage derived from the final update receipt
The update pipeline already writes one machine-readable receipt per run, so the
run/stage counters are derived from it in the process that finalizes it instead
of instrumenting stages for metrics. The receipt gains minimal stage END marks
(plan, snapshot, apply[mode], deps, build, restart; verify is inferred from the
fleet matrix), an initiator fact (desktop when a live orchestrator claim
names another pid) and the pre-update commit date, which is all the derivation
needs for outcome, failed stage, duration and from-version-age buckets.

A pre-pull interpreter must never import pulled code: when it is the finalizer
(same pid, checkout sha moved) it parks the receipt (stdlib only) and the next
Hermes start records it. Collection off => nothing imported beyond a config read.
The completion-process test stub gains the new record_stage API the real
module now exposes (no assertion changed).
2026-09-28 12:43:03 -07:00
teknium1
05c5cbf190 test(update): follow the SHA-less obligation rule in the reconciliation matrix and completion harness
Two existing tests pinned the pre-#125952 shape and went red on the salvage:

- tests/hermes_cli/test_update_scoped_reconciliation.py `marker-no-sha`: expected an
  inventory-less marker armed with expected_sha="" to stay pending with every live gateway
  current on the checkout. That was the "no expected_sha — nothing to verify against"
  fail-closed default from 8130274b37, which #125952 deliberately changes: with no owed set
  and no SHA the checkout is the only code the record can be held to, so the case now settles
  (False) exactly like `marker-external-restart`. Added `marker-no-sha-stale` so the boundary
  stays pinned: the same record keeps the warning while a live row is stale.

- tests/hermes_cli/test_update_completion_process.py
  `test_interrupt_after_child_success_demotes_gateway_marker_at_boundary`: the harness hooked
  the FIRST subprocess.Popen as the completion child. The armer now falls back to
  _current_checkout_sha() when the request carries no SHA, which spawns `git rev-parse`
  before the completion child, so the hook wrapped git's Popen and the recorded waits were
  subprocess.run's own (`[2.99, 2.99, None]`). The hook now matches the completion child by
  its argv; the invariant under test (interrupt after published success kills, waits with a
  bounded timeout and closes the child's stdout) is unchanged.

No product change.
2026-09-28 04:44:29 -07:00
teknium1
ccff8eb3ce test(pm): keep the takeover generation assert; drop call-order checks
Trim the salvaged tests to one invariant: after a takeover rebuild, the
superseded generation built two days ago is gone. The update-completion
event-order asserts and the repair_dependencies `events == [sync,
collect]` test pin the call sequence, not the outcome; the update and
repair outcomes are covered live by
tests/e2e/core/upgrade/pm/test_generation_gc.py. The fake venv_sync in
the completion-process fixture keeps its collect_superseded_generations
stub so the module still imports.
2026-09-27 02:55:39 -07:00
JoaoMarcos44
c11a6bc0b2 fix(pm): collect generations after maintenance syncs 2026-09-27 02:55:39 -07:00
ethernet
ccf631701a fix(pm): disable plugins that no longer fit instead of failing the update
An update resolves the enabled plugin union against the NEW core. A plugin
admitted against the old core can stop fitting when core moves (managed
Python 3.13 -> 3.14 vs a member's requires-python <3.14, a requires_hermes
upper bound, a bumped pin), and the whole update then died after the
source swap with a non-resolver InstallError whose 'retry' hint failed the
same way every time.

Update syncs now pass evict_incompatible_plugins=True (update completion,
historical takeover, launch-time completion, venv_sync, post-update
drift). PM screens statically first (requires-python vs the target
interpreter, manifest/requires_hermes), then, if the rest still fails,
builds core alone to prove the plugins are the cause and re-adds members
in config order, disabling each one that breaks the build. Misfits land in
plugins.disabled (memory.provider cleared) in every home that enables
them, published through the existing journaled change hook (the journal
now carries several configs), and are reported on stderr + receipt
warnings. Admission and ordinary syncs still refuse; only a core that
cannot build on its own fails an update.
2026-09-25 00:00:09 -04:00
ethernet
f67b3fcece fix(update): install required PM tools before the venv sync on every update
A normal `hermes update` only re-synced the venv. The sync pulls uv/python
in through its own dependency, but a ripgrep/ffmpeg/node/npm pin bump in
pm/lock.json was never installed, so PATH activation warned and skipped the
managed tool dirs on every CLI start and gateway boot. Only the takeover
route for historical releases ensured the tool roots.

Move the takeover's loop into pm.client.ensure_tools_for_sync() and call it
from both routes before the sync: update_completion._prepare (the CLI and
Desktop route, running from the new tree so the new lockfile applies) and
_update_takeover.prepare. It uses explicit=True like the takeover (an update
is an explicit user action) and a failed download fails the update.

post_update.step_provision_runtimes / MACHINE_STEPS stay: `python -m
hermes_cli.post_update --scope machine` and tests still reference them.
The ffmpeg docstring no longer claims that step re-ensures it.
2026-09-24 17:30:44 -04:00
ethernet
501ab5eb09 fix: preserve source update completion and runtime ownership boundaries 2026-09-24 02:03:58 -04:00
ethernet
7d583e7d12 test(update): fake the source stamp writer in the completion transition tree
The completion tail now stamps source identity via hermes_cli.source_stamp;
the hand-built new tree lacked it, so every transition child exited 1.
2026-09-23 15:55:58 -04:00
ethernet
a60bc9cc64 fix(update): completion tail skips the fleet restart it does not owe
Every update route now finishes through update_completion._complete_selected,
which restarted the whole fleet unconditionally -- including the "Already up to
date" route that main sent through the pending-restart catch-up. Net effect:
each cron tick and each profile's `hermes update` drained and re-killed the one
multiplexed gateway.

Port the catch-up path's two live guards into the completion tail:
- host_restart_already_completed(checkout sha): a sibling profile attaches to
  the restart this host already stamped (#95294); the restart phase now stamps
  it via mark_host_restart_completed.
- every planned runtime AND every live fleet row current at the checkout sha
  (#117051, d6b0d37ece). Both are required: the live matrix lists gateways
  only, so a planned serve still on pre-update code keeps the restart.

The already-current route also arms the host obligation with the checkout sha;
an SHA-less arm replaced the standing record and wiped the restarted proof.
2026-09-21 19:03:35 -04:00
ethernet
0533b03268 test: update flows resolve their channel locally; desktop checks get real deps
Every unflagged `hermes update` resolves its channel through R2 now, so the
mocked update harnesses reached the network (and 404 today). An autouse
fixture in tests/hermes_cli defaults every channel to a source-branch record
through the documented `_resolve_channel` seam; archive/transport tests keep
the real reader via the `real_release_channels` marker. The stable-identity
test pins the channel's commit through that seam instead of the retired
release-candidates pointer, and the completion-process fixture ships
source_completion.py in its NEW tree.

Desktop: js-tests installs the dev extra (the electron contracts drive real
hermes_cli/pm code through HERMES_PYTHON); the source-backend fixture runs PM's
worker on PM's staged runtime; async canImportHermesCli is awaited; the
feature-flag test pins the platform rule; a mocked profile store exports what
settings-scope reads; the peer-device e2e imports the mock-provider helpers
from tests-js like its siblings.
2026-09-19 03:28:27 -04:00
ethernet
bbec973514 refactor(pm): pm owns the dependency-environment layout and interpreter paths
hermes_cli.runtime_paths (venv generations, selection, activation) moves to
pm.environments, and gains venv_bin_dir / venv_python / project_python. Every
in-tree caller asks pm for an interpreter now; pm no longer reaches back into
hermes_cli for its own environment layout (pm.packages, pm.extras, pm.ensure,
pm.paths imported hermes_cli.runtime_paths). The three open-coded
"Scripts/python.exe or bin/python" ladders in pm collapse onto venv_python.

hermes_constants.venv_python_path / venv_bin_dir and hermes_cli.runtime_paths
stay as frozen-updater-surface shims only (tests/compat/old_updater_surface.json).

To keep the boot path light, pm/__init__ resolves its facade lazily (PEP 562)
and pm.registry loads the built-in package definitions on first read instead of
at import: `import hermes_bootstrap` now loads pm + pm.environments only (25ms,
was 37ms with the eager facade dragging in the downloader). The stripped-payload
fixtures that ship only pre-import files keep working for the same reason.

Also restores two frozen-surface re-exports the F401 sweep dropped
(banner._github_compare_behind, cua_backend.resolve_cua_driver_cmd).
2026-09-18 20:02:36 -04:00
ethernet
5623942736 fix(update): tolerate BOM receipts and preserve rollback bytes 2026-09-13 18:21:59 -04:00
ethernet
cfee19fc90 Bound completion cancellation cleanup without hiding the original failure 2026-09-12 19:18:40 -04:00
ethernet
9e65206fe2 Keep completion progress live and cancellation outcomes consistent 2026-09-12 19:14:25 -04:00
ethernet
93cffdbd3a refactor(update): finish source updates in fresh selected Python 2026-09-12 19:10:32 -04:00