Skip to content

feat(dashboard): Desktop reads the update receipt — no more 'Backend update failed' on successful updates (#81193/#87359, salvage #88462) - #92780

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-83bfdb1e
Aug 23, 2026
Merged

teknium1 merged 2 commits into
mainfrom
hermes/hermes-83bfdb1e

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Summary

The Desktop and dashboard now READ the update receipt instead of inferring update success from liveness probes — completing Phase-1 bullet 3 of #91277, the last open Phase-1 deliverable. Salvage of #88462 by @mrsucesso (dashboard-side durable-marker recovery, authorship intact) + the receipt layer on top.

Root cause of the class: a successful backend update restarts the dashboard, wiping the in-memory action registries; the Desktop's poll then saw exit_code: null until timeout and reported "Backend update failed" on a SUCCESSFUL update (#81193), and its liveness probe misread the update's own stop/start gap as "boot failed" (#87359).

Changes

  • hermes_cli/web_server.py (@mrsucesso): recover the completed-update identity from the durable update.log marker after a dashboard restart; stale markers preceding a newer start are rejected.
  • hermes_cli/web_server.py (ours): GET /api/hermes/update/receipt — full durable receipt (steps, skips, gateway restart, fleet matrix) + compact summary. /api/actions/hermes-update/status attaches the receipt summary; when registries AND marker are both gone (restart + log rotation), a finished receipt reports the outcome (success→0, partial→1); a still-running receipt proves nothing.
  • apps/desktop/src/store/updates.ts + types: the apply poll treats a finished receipt whose run started at/after this apply as authoritative — no more timeout-based failure inference across the restart gap.

Validation

Check Result
New endpoint/status tests + salvaged recovery test + web_server update suites 7/7 new, all existing green
Live E2E: real uvicorn server + real UpdateReceipt writer (the exact code hermes update runs) over real HTTP receipt endpoint 200 w/ summary · #81193 state (no registries, no marker) reports success from the receipt alone · partial receipt with a DOWN fleet row → exit 1, no false success
Desktop tsc -p tsconfig.json error count identical to main (714 pre-existing env errors; zero in touched files)

With this + #92751 + #92769, Phase 1 of #91277 is complete: receipts, verification handshake (incl. DOWN rows and fork coverage), and receipt consumption. Acceptance issues #81193/#87359 close on merge.

Infographic

Read the receipt

mrsucesso and others added 2 commits August 23, 2026 00:24
Persisted update completion markers survive the dashboard restart that clears in-memory action state. Recover the latest safe marker from update.log so remote Desktop clients do not report a successful backend update as failed.
…d of inferring success (#91277 Phase-1 bullet 3)

Builds on @mrsucesso's durable-marker recovery (previous commit):

- GET /api/hermes/update/receipt — the full durable receipt (steps,
  skips, gateway restart outcome, fleet matrix) + compact summary; the
  authoritative update-outcome record (written by every run since
  #91283, including refused/failed).
- /api/actions/hermes-update/status now attaches the receipt summary,
  and when BOTH the in-memory registries and the update.log marker are
  gone (dashboard restarted + log rotated — the #81193 state), a
  finished receipt reports the outcome: success→0, partial→1. A
  still-running receipt proves nothing (clients keep polling).
- Desktop (updates.ts): the apply poll reads the attached receipt — a
  finished receipt whose run started at/after this apply is
  authoritative, replacing timeout-based failure inference across the
  update's restart gap ('Backend update failed' on successful updates,
  #81193; 'boot failed' during update restarts, #87359).

Live-verified: real uvicorn server + real UpdateReceipt writer (the
exact code hermes update runs) over real HTTP — receipt endpoint 200
with summary; #81193 state (no registries, no marker) reports success
from the receipt alone; partial receipt with a DOWN fleet row maps to
exit 1 (no false success).
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 1cd5fdb — feat(dashboard): Desktop and dashboard read the update recei

⚠️ Warnings

OSV vulnerability scan · View job

7 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 2m51s vs 4m53s (-41.6%). 3 job(s) slower, 8 faster, 2 unchanged.

  • OSV scan / Emit review status: -52.0s
  • OS-specific tests / Windows-only tests: +48.0s
  • Check contributors / check-attribution: -14.0s
  • JS & TS checks / JS & TS checks: -11.0s
  • Python tests / e2e: -4.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) area/install-update Installer, updater, packaging, wheels, doctor sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 23, 2026
@teknium1
teknium1 merged commit 8804e78 into main Aug 23, 2026
40 checks passed
@teknium1
teknium1 deleted the hermes/hermes-83bfdb1e branch August 23, 2026 07:50
teknium1 added a commit that referenced this pull request Sep 14, 2026
…classes

Two weeks of closed issues/merged PRs show the same areas regenerating:
each salvage pinned its instance while the class invariant had no test.
These suites pin the invariants themselves:

- tests/conformance/test_profile_write_tripwire.py — no writes to the
  default profile tree while a profile is active (#88532 #92662 #89190
  #89625 #92156); reusable tripwire fixture, 4 surfaces
- tests/hermes_cli/test_env_deprecation_truthtable.py — 18-row truth
  table for the Deprecated-.env warning (#88829 #89016 #89389 #90299)
- tests/cron/test_cron_memory_contract.py — cron<->memory contract that
  flipped twice in Aug (#91269 -> #91384 -> #91447)
- tests/agent/test_injected_param_strip_retry_registry.py — every
  strippable injected param x real 400 shapes must strip-and-retry;
  unknown params must still fail (#90257 #89897 #91164 #89503)
- tests/agent/test_transcript_decoration_idempotence.py — f(f(x))==f(x)
  law + 4-breakpoint budget for apply_anthropic_cache_control (#90971)
- tests/state/test_state_db_maintenance_conformance.py — registry-
  enumerated maintenance ops refuse/degrade under a live writer; copies
  of corrupt DBs are refused or flagged (#91839 #90806 #90613 #88235)
- tests/tools/test_bot_mode_canonical_chat_resolution.py — canonical
  Bot Chat resolution is idempotent, never mints, unique per profile,
  race-safe (#92040 #90705 #92692 #90005 #90732, PR #92129)
- tests/hermes_cli/test_update_receipt_truthfulness.py — receipts:
  crash never claims success; success requires full fleet accounting;
  refusal != failure (#91283 #91439 #92902 #92780)

117 tests, all sabotage-verified (each suite proven to FAIL when its
bug class is reintroduced).
teknium1 added a commit that referenced this pull request Sep 14, 2026
…classes

Two weeks of closed issues/merged PRs show the same areas regenerating:
each salvage pinned its instance while the class invariant had no test.
These suites pin the invariants themselves:

- tests/conformance/test_profile_write_tripwire.py — no writes to the
  default profile tree while a profile is active (#88532 #92662 #89190
  #89625 #92156); reusable tripwire fixture, 4 surfaces
- tests/hermes_cli/test_env_deprecation_truthtable.py — 18-row truth
  table for the Deprecated-.env warning (#88829 #89016 #89389 #90299)
- tests/cron/test_cron_memory_contract.py — cron<->memory contract that
  flipped twice in Aug (#91269 -> #91384 -> #91447)
- tests/agent/test_injected_param_strip_retry_registry.py — every
  strippable injected param x real 400 shapes must strip-and-retry;
  unknown params must still fail (#90257 #89897 #91164 #89503)
- tests/agent/test_transcript_decoration_idempotence.py — f(f(x))==f(x)
  law + 4-breakpoint budget for apply_anthropic_cache_control (#90971)
- tests/state/test_state_db_maintenance_conformance.py — registry-
  enumerated maintenance ops refuse/degrade under a live writer; copies
  of corrupt DBs are refused or flagged (#91839 #90806 #90613 #88235)
- tests/tools/test_bot_mode_canonical_chat_resolution.py — canonical
  Bot Chat resolution is idempotent, never mints, unique per profile,
  race-safe (#92040 #90705 #92692 #90005 #90732, PR #92129)
- tests/hermes_cli/test_update_receipt_truthfulness.py — receipts:
  crash never claims success; success requires full fleet accounting;
  refusal != failure (#91283 #91439 #92902 #92780)

117 tests, all sabotage-verified (each suite proven to FAIL when its
bug class is reintroduced).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants