Replace HERMES_DESKTOP_BUNDLED with HERMES_DESKTOP_VARIANT. The variant is one value: bootstrap, bundled, or light. The stamp payload field records it.
Bake install-stamp.json into the electron bundle as an object literal. The app does not load a stamp file from resources anymore. Remove the extraResources entry and the loose-file loader. A new install-stamp.ts module owns the InstallStamp and ArtifactKind types.
Python reads a light stamp as an error: a light artifact has no Python runtime, so this state means the build is bad.
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.
The uninstall flow now reads the install stamp to learn who owns the
code. Three install kinds exist:
- standard (git checkout from the installer or 'hermes desktop'):
keeps the full gui/lite/full flow.
- bundled (embedded payload): the app can only remove user data. The
embedded CPython runs the uninstall module. The OS removes the app
(Apps & Features / Trash / delete the AppImage).
- nix: the app can only remove user data. The UI shows 'managed by
Nix' guidance instead of code-removal options.
A new mode 'data' (hermes uninstall --data) removes ~/.hermes user
data and the Electron userData dir, and keeps all code. This mode is
valid on every install kind.
The Python uninstaller enforces the same rule on its own: it reads
.hermes_build_info.json through runtime_tree and refuses the
code-removal modes on sealed trees, with instructions from the
steward. The renderer cannot make a managed install delete code.
loadInstallStamp now accepts a stamp with commit:null. A dirty Nix
build writes such a stamp, and the provenance fields must survive.
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.