Skip to content

Enable Hermes in-app updates and guard removal - #10613

Merged
spencerbull merged 4 commits into
omacom:quattrofrom
spencerbull:hermes-desktop-in-app-updates
Sep 7, 2026
Merged

Enable Hermes in-app updates and guard removal#10613
spencerbull merged 4 commits into
omacom:quattrofrom
spencerbull:hermes-desktop-in-app-updates

Conversation

@spencerbull

@spencerbull spencerbull commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hermes' in-app updater rebuilds and relaunches the desktop inside the user's Git checkout. The current install flow starts the immutable /opt app and leaves that native desktop absent, so fixing the tag-only checkout alone does not enable desktop updates.

Use the upstream installer shipped by omarchy-pkgs #335 to prepare the matching release runtime while tracking main, then place the package's prebuilt desktop in the native release directory before opening it. Start local main at the packaged release with connected history, so the first update detects newer commits and runs dependency synchronization and the desktop rebuild even when upstream has not moved since installation. Use the installer's --force-commit only when preparing a new runtime or repairing an admitted clean release checkout. Local branch work is checked before the upstream installer can reset it.

Back up existing hermes, hermes-agent, and hermes-acp files or symlinks before the upstream installer replaces them, and report the backup directory. An incompatible installed package stops with omarchy update guidance before CLI handoff. Record the prebuilt app with Hermes' own content stamp; existing desktop builds and modified sources are preserved.

Apply the package's two-line upstream updater gate backport only to the matching release checkout, allowing the first update handoff to relaunch with the user-namespace sandbox. The package launcher opens the native executable directly and refuses native launch if user namespaces are unavailable. Subsequent updates use upstream's current implementation. Keep the package's URL-capable launcher visible and hide the upstream-generated hermes entry through Omarchy's existing launcher hide list. Rebuilds can recreate that file without adding a second Hermes row to the Apps menu.

Refuse removal while a current-user process is using the Hermes runtime or data, before package/runtime cleanup and again after confirming data deletion. Removing an open SQLite database can leave a terminal holding deleted WAL files across a reinstall, which the updated desktop correctly refuses to write alongside. Match process arguments, executable, working directory and open files, including deleted descriptors, and report the PIDs to close without killing sessions.

Requires hermes-desktop 2026.8.31-2 from the companion package PR.

🤖 Generated by GPT-6 in Codex/T3 Code. Reviewed by GPT-6 Codex at xhigh.

Use the packaged upstream installer and matching prebuilt app to prepare the writable user installation before launch. Preserve existing builds and modified sources; record the upstream build stamp only for a matching prebuilt app.

Co-Authored-By: GPT-6 Codex (xhigh) <noreply@openai.com>
@spencerbull

Copy link
Copy Markdown
Contributor Author

Reviewed at aa3868b with omabot's review-pr procedure. Verdict: waiting on the author. Three defects worth a decision, two one-line test fixes proven, nothing pushed.

What ran. The new hermes-desktop-install-test.sh, the six existing Hermes tests, bin-style-test.sh and ./test/cli all pass on a disposable Omarchy worker. Eight mutations of the script each fail exactly one named assertion, so the test earns its place, with one exception below. The upstream Hermes tree at the pinned commit 29112bef and the omarchy-pkgs #335 diff were read, not executed: no live install, launch or updater ran here.

Findings.

  1. Medium, bin/omarchy-install-ai-hermes:50. install.sh --commit leaves HEAD detached at the release commit with the local main branch at the clone tip. hermes update checks out main before it counts commits (update_cmd.py:8372, :8426), so if upstream main has not moved since the install it takes the "already up to date" path (:8492): HEAD moves from the release to the clone tip with no dependency sync and no desktop rebuild, the shim relaunches the release build against the newer backend, and the package launcher's --skip-build keeps it that way until upstream commits again. hermes update --check compares the detached HEAD, so the app offers exactly this update right after install. Upstream's own first-launch bootstrap produces the same state, so this is inherited rather than introduced, but this PR is the one promising that in-app updates work. One mitigation is to make main the release commit after install.sh returns and deepen the history between it and the clone tip so the first pull can fast-forward; that is a design call. Found by Codex at xhigh and verified against upstream.

  2. Medium, bin/omarchy-install-ai-hermes:50. install.sh's setup_path removes and rewrites ~/.local/bin/hermes, hermes-agent and hermes-acp unconditionally (scripts/install.sh:2037, :2066, :2091). omarchy-install-hermes-cli leaves a foreign wrapper alone by policy and the previous package launcher refused to install over one; this flow silently replaces it with the new runtime's shim. Refuse, back up, or accept is a product call. Both reviewers.

  3. Medium-low, bin/omarchy-install-ai-hermes:31. omarchy-pkg-add does nothing when the package is installed at any version, so a user still on hermes-desktop 2026.8.31-1 gets jq: error: Could not open /opt/hermes-desktop/resources/install-stamp.json and nothing says omarchy update is the fix. Every current Hermes Desktop user is on -1. Both reviewers.

  4. Low, test/shell.d/hermes-desktop-install-test.sh:97. The concurrent-app fixture is a non-empty directory, which rename(2) refuses with or without --no-clobber, so dropping the flag from line 88 passes the whole test. An empty directory is what the flag guards: with that fixture the original passes and the mutant fails on "concurrent native app stops publication". This review.

  5. Low, test/shell.d/hermes-desktop-install-test.sh:190. The systemd-run mock joins on any existing launch event and this run does not clear the log first, so its background launch is never waited for. With a 50 ms lag in the hermes-desktop mock the stray launch-before-copy lands in a later negative test and the suite fails, twice of two runs; adding : >"$test_tmp/events" before line 190 passes three of three under the same lag. Found by Codex at xhigh, proven on the worker.

  6. Low, bin/omarchy-install-ai-hermes:50. A full-history checkout sitting clean at the release commit without a completed runtime is admitted, then install.sh checks out and pulls main and ignores --commit as already an ancestor, and the script refuses to seed the now-newer runtime at line 74. A shallow clone left by an interrupted first run cannot prove the ancestry and re-pins, so the common retry works; only a hand-made clone hits this. Found by Codex at xhigh.

Pre-existing and not against this PR: the exported HERMES_HOME reaches uwsm-app (a scope) but not systemd-run, and omarchy-install-hermes-cli --check and omarchy-remove-ai-hermes hardcode ~/.hermes, so a custom home is prepared here and then missed by theme activation and removal.

Checked and correct. _write_desktop_build_stamp(Path, source_mode=False) exists at the pinned commit with that signature and hashes the same tree hermes desktop checks, and the stamp is load-bearing because the native menu entry runs hermes desktop without --skip-build. hermes update switches a detached HEAD to main and autostashes the applied runtime.patch; a conflicting restore resets to a clean tree and keeps the stash. install.sh restores dirty package-lock.json after npm and syncs uv --locked, so the dirty-tree checks are not tripped by a normal install, and --skip-setup still writes the bootstrap marker and the ~/.local/bin/hermes shim the theme unit needs. mv -T --no-clobber on coreutils 9.11 skips with exit 0 and the staging check catches it.

Codex ran at xhigh from the worktree. Findings 1, 5 and 6 are its own contributions; where it agreed with conclusions already reached here its independence is not guaranteed, since it can read this session's files. Its "high" on findings 1 and 2 is downgraded here: 1 self-heals at the next upstream commit and 2 replaces the wrapper with a working hermes.

Related: #10443 is this author's earlier draft of the same change; omarchy-pkgs #335 is required and must publish first; omarchy-pkgs #302 bumps the same package without the prebuilt design.

Nothing pushed: this is the author's active draft and findings 1 to 3 need decisions. The two test fixes are one line each.

🤖 Generated by Claude Fable 5.1 in T3 Code. Reviewed by Codex XHigh.

@spencerbull
spencerbull marked this pull request as ready for review September 7, 2026 08:38
Align main with the packaged release and fetch connected history so the updater detects and rebuilds the first update. Guard local branch work before upstream installation, force only the admitted incomplete release pin, preserve existing command files, and explain incompatible packages. Exercise empty-directory publication races and delayed launches.

Co-Authored-By: GPT-6 Codex (xhigh) <noreply@openai.com>
@spencerbull

Copy link
Copy Markdown
Contributor Author

Addressed the six findings in 827266f:

  1. Local main starts at the packaged release, with shallow history completed before the first update. A real Git fixture holds upstream unchanged after installation, then verifies that the updater's checkout/count/pull sequence detects a newer commit and fast-forwards. The package launcher now opens the native executable directly, but the skipped dependency/rebuild finding still needed this fix.
  2. Existing hermes, hermes-agent, and hermes-acp files and symlinks are copied into a unique backup directory before upstream replaces those names. The installer reports the backup path; working and dangling symlinks are preserved without touching their targets.
  3. Missing or incompatible package setup files/stamps now produce omarchy update guidance before CLI handoff or runtime setup.
  4. The concurrent-publication fixture now creates an empty directory. Removing --no-clobber fails the test.
  5. The updated-runtime case clears its event log before launching. The desktop mock includes a 50 ms delay; removing the clear reproduces the cross-test failure.
  6. The pinned upstream installer already supports --force-commit. It is now used only for a fresh runtime or the guarded incomplete release checkout, ensuring the release is selected before dependencies are installed. Existing local main commits are checked before upstream can reset that branch.

All 14 installer fixture groups and the full ./test/cli suite pass. Eight deliberate mutations are rejected. An independent xhigh review found no remaining substantive issues. No live runtime was modified or GUI update run for this review follow-up.

The companion package PR #335 now uses 2026.8.31-2, the requested single release bump from -1, and the dependency reference here matches it. The previously noted custom-home behavior in theme/removal remains outside this change.

🤖 Generated by GPT-6 in Codex/T3 Code. Reviewed by GPT-6 Codex at xhigh.

An open terminal can retain deleted SQLite WAL files across removal and reinstall, causing the updated desktop to refuse session writes. Check user processes before package/runtime removal and again after data confirmation, without killing sessions. Cover the failure with real SQLite writers in isolated fixtures.

Co-Authored-By: GPT-6 Codex (xhigh) <noreply@openai.com>
@spencerbull spencerbull changed the title Prepare Hermes native desktop for in-app updates Enable Hermes in-app updates and guard removal Sep 7, 2026
@spencerbull

Copy link
Copy Markdown
Contributor Author

Added the removal guard in 1b4ac83 after reproducing the post-update session-storage failure with an actual SQLite writer in a temporary home. The removal command could delete the database while a terminal remained open; that writer then held deleted WAL/SHM files across reinstall, and the updated Hermes correctly refused further writes.

Removal now reports current-user PIDs using Hermes paths and stops before package or runtime cleanup. It checks again after the data-deletion confirmation. It detects process arguments, executables, working directories, and open files, including deleted descriptors, without terminating sessions.

Existing removal tests, real live/deleted SQLite writer fixtures, the prompt race, and the full CLI suite pass. Four deliberate mutations are rejected. Independent xhigh review found no remaining substantive issues. The guard does not provide an atomic lock against arbitrary new processes starting after a scan. No live sessions or data were changed; package #335 remains at 2026.8.31-2.

🤖 Generated by GPT-6 in Codex/T3 Code. Reviewed by GPT-6 Codex at xhigh.

Keep the package launcher visible when Hermes builds register a second desktop entry. Use the existing launcher hide list so in-app updates cannot restore the duplicate menu row.
@spencerbull
spencerbull merged commit a703092 into omacom:quattro Sep 7, 2026
@spencerbull
spencerbull deleted the hermes-desktop-in-app-updates branch September 7, 2026 09:26
ryanrhughes pushed a commit that referenced this pull request Sep 8, 2026
Enable Hermes in-app updates and guard removal

(cherry picked from commit a703092)
(cherry picked from commit 2438293d775d679ce99364cdbced108596c5b2bc)
ryanrhughes pushed a commit that referenced this pull request Sep 8, 2026
Enable Hermes in-app updates and guard removal

(cherry picked from commit a703092)
(cherry picked from commit 2438293d775d679ce99364cdbced108596c5b2bc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant