one artifact replaces three: install-stamp.json (code-scoped) subsumes
.hermes_build_info.json (same schema, different name) and .install_method
(derivable). git checkouts carry no stamp at all — .git plus location is
the fact.
detect_install_method() now delegates to runtime_tree.install_method():
stamp distribution (docker/nix/desktop-app)
-> .git at a managed install root => git
-> .git anywhere else => source (new)
-> unknown
the new 'source' method makes hermes update refuse random src checkouts
outright and point at git pull (replaces the --yes-overridable ask-first
guard). nixos dies as a method value; /nix/store sniffing and the
HERMES_MANAGED ladder step die with it. HERMES_MANAGED keeps exactly one
job: the NixOS module's declarative config-write guard.
lazy_deps drops install-method inference entirely: the read-only guard
now probes site-packages writability directly.
no backwards compat: nothing reads the legacy stamps anymore. stage2-hook
keeps deleting stale home-scoped .install_method markers left by old
images.
hermes_cli/runtime_tree.py replaces install_manifest.py. A tree with
.git is a git checkout and `hermes update` owns it. A tree without
.git is sealed, and the distribution field of the build stamp names
the steward that replaces it (desktop-app, docker, nix). The refusal
message comes from a per-steward table.
.hermes-install.json dies: staging stops writing it into the payload,
the CLI never reads it, and the update channel lives in config.yaml
(update.channel; main is the default and keeps the current behavior).
Eject gates on Sealed(desktop-app) and is a full handoff: it tells
the user that Setup replaces the desktop app. --channel on a git
checkout writes config instead of a manifest.
.hermes-install.json marks a checkout as source-managed or
desktop-bundled and records its update channel. A missing file means
source mode on the main channel, so no existing install changes.
`hermes update` reads the manifest:
- On a bundled install it refuses and points at the in-app updater.
- On the stable channel it fast-forwards the checkout to the newest
final release tag (vX.Y.Z, three-digit major cap so legacy CalVer
tags never match) instead of origin/main. The ZIP fallback resolves
the tag through the GitHub API because that path runs when git file
I/O is broken.
- `update.channel` in config.yaml overrides the channel for source
installs. "auto" defers to the manifest.
`hermes update --eject` is the exit from desktop management. On a
bundled install it downloads Hermes Setup and launches it pinned to
the exact commit the bundle was built from; the installer creates a
normal source checkout at ~/.hermes/hermes-agent. Hermes Setup accepts
the new `--pin-commit <sha>` argument for this flow. On a
source-managed install, --eject with --channel only switches the
channel. The "ejected" manageStyle is the permanent opt-out that stops
future auto-adoption.