Commit Graph

8 Commits

Author SHA1 Message Date
ethernet
501ab5eb09 fix: preserve source update completion and runtime ownership boundaries 2026-09-24 02:03:58 -04:00
ethernet
54ad8c7fd2 fix(update): every published checkout identity moves the bootstrap receipt
ece7c172f8 refreshed .hermes-bootstrap-complete only from
complete_source_checkout, but two other paths publish a new checkout identity
through write_source_stamp without it: the PM updater's finish
(update_finish.py, the path an old updater's handoff takes) and boot-time
blessed-checkout adoption (post_update.py). The Windows E2E leg
installer-script -> hermes-update (v2026.6.19 -> HEAD) takes the handoff, so
it still failed "pinnedCommit 2bd1977d8f != installed HEAD ece7c172f8d8".

The refresh now lives inside write_source_stamp, so every caller moves the
receipt with the identity it publishes. The tests pin that seam (red on
ece7c172f8 with the CI message, green here).
2026-09-23 22:05:57 -04:00
ethernet
ece7c172f8 fix(update): a finished update moves the installer's bootstrap receipt to its HEAD
install.sh / install.ps1 write .hermes-bootstrap-complete once, at the commit
they installed. The shared completion tail republished install-stamp.json but
never the receipt, so after `hermes update` it kept naming the replaced release
(Windows E2E, installer-script -> hermes-update v2026.6.19 -> HEAD: "pinnedCommit
2bd1977d8f != installed HEAD"). The desktop's own repair bootstrap already
stamps the live HEAD; the CLI update now does the same through
complete_source_checkout, which both the completion handoff and a pre-handoff
updater's --finish-update startup reach.

Only an existing receipt is refreshed: its presence marks a script install, so
a manual clone never grows one. The test uses the E2E's own verifier.
2026-09-23 20:41:16 -04:00
ethernet
c13ea774e6 refactor: make install-stamp.json the single runtime version identity
Runtime identity resolved through hermes_cli.__version__ (a static 0.0.0
on source installs, rewritten by release stamping) leaked v0.0.0 into
About, /api/health, User-Agents, and plugin compat, and source updates
showed "couldn't reach update server" because identity and channel
authority disagreed with the checkout.

Now: get_version_info() resolves install stamp -> live git -> unknown,
never pyproject metadata, never a package constant. Source checkouts
derive identity from their reachable release tag; the completion tail of
every successful install/update/historical takeover atomically rewrites
install-stamp.json with that identity; a stale source stamp whose commit
no longer matches HEAD defers to live git. ACP/TUI use derived_version
for display and base_version for protocol fields; all ~44 runtime
__version__ consumers migrated; hermes_cli.__version__ and generated
_version.py are gone; release stamping only touches the native manifests
external builders consume (nix/tauri/cargo) and passes release identity
straight into write_install_stamp.py; pyproject.toml stays inert 0.0.0.
Desktop no longer synthesizes a competing install-stamp.json: the
checkout owns its stamp, and desktop-bootstrap classification keys on
the bootstrap-complete marker. verify-bootstrap-version-stamp.py now
cross-checks the checkout's stamp (baseVersion + commit == HEAD).

Validation: 31-file focused suite green (version identity, stamping,
adoption, providers, gateway, acp/tui runtime identity, api server via
extras env, release graph); desktop tsc + 25 vitest green; real-repo
probe: base=unknown derived=git.0635606.dirty source=git on this
checkout; clean-env imports resolve entirely from this tree; windows
footgun + compat-pointer scans clean.
2026-09-23 11:41:01 -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
dbe4bfbc06 fix(update): keep --finish-update through the completion self re-exec
source_completion.py re-execs itself into PM's selected interpreter before
touching any product, and the passthrough argv only carried --desktop. So
the startup heal's --finish-update was dropped, and the prepared invocation
fell through to the install tail: an update reported itself as an install.

    ✓ Install complete! [main @ c4056e3d6b]

Every flag that decides WHICH tail runs has to survive that boundary.

Verified with a stub that captures the bootstrap command instead of running
it: --finish-update present in the re-exec, --desktop still passed, and
without the flag the install path is unchanged. File compiles.
2026-09-18 17:34:14 -04:00
ethernet
225fd5bcd6 fix(update): finish the whole source-update tail when a startup completes it
prepare_launch is the startup heal: it detects a stale PM generation
(pm.venv_is_current) and completes the update. But it only synced the
dependency generation and published the launchers -- so an install that
came through it landed in a different state than one that came through a
normal update or the installer.

The tail they share is complete_source_checkout (source_completion.py):
publish launchers, build the products, run the post-build maintenance.
"Its" own docstring: a fresh install and a finished update must land in the
same state, one implementation and two callers. The startup heal was a
third path taking a subset, and the e2e verifier caught exactly that -- it
requires ui-tui/dist fresh, which only build_update_products produces.

So drive the shared tail from the heal, the same way the installers do:
hand source_completion.py THIS interpreter (pre-sync, so it stays a
stdlib-only import for the old tree) and let it re-exec into PM's selected
python before touching any product. A failed tail is loud, matching
build_update_products' "a failed product aborts the update".

Verified: both files compile; the bare-3.11 pre-flip import check still
passes (bootstrap / prepare_launch / takeover_child, no third-party).
2026-09-18 14:50:06 -04:00
ethernet
dbeebaadfc refactor(install): one completion tail shared by install and update
The installer ladder stopped at node-deps/path/desktop with its own
semantics while an update ran launchers, product builds and post-build
maintenance, so a fresh install and a finished update ended in different
states: after re-running the installer at HEAD the products had no receipts
and the read-only source acceptance failed.

hermes_cli/source_completion.py now owns that tail -- publish launchers,
build the products, run the maintenance -- and update_completion's
_complete_selected calls it, so there is one implementation. install.sh and
install.ps1 keep the bootstrap stages (prerequisites, repository, venv,
python-deps, config) and hand off to it in a single `products` stage;
--include-desktop selects the desktop product inside that stage instead of
adding a second build stage, and `desktop` stays dispatchable via --stage for
external callers.

Windows keeps its installer-owned PATH publication (expose_cli answers
"windows-installer-owned" on Windows) plus the packaged-artifact probe, ACL
grant and shortcuts. The desktop stage no longer pre-syncs wake/voice: pm
lazy-installs them at first use, as the update path does.
2026-09-17 15:26:05 -04:00