Commit Graph

2 Commits

Author SHA1 Message Date
Teknium 40b4a3bfe1 fix(update): every begun update receipt is now persisted — command-boundary finalization
Review on #91283: begin_update_receipt() fires early in _cmd_update_impl,
but finalization only existed on the success/ZIP/CalledProcessError
paths. Early sys.exit paths (Windows concurrent-instance preflight,
venv-holder refusal, head-pinned no-op, fetch failure) terminated with
the receipt started but never written — losing exactly the refused/
failed runs the receipt matters most for.

- update_receipt.py: finalize_pending_update_receipt(exit_code,
  stop_reason) — boundary safety net; maps exit 2 → 'refused', other
  non-zero → 'failed'; records exit_code + stop_reason. Exactly-once by
  construction (singleton popped in finalize_update_receipt), so runs
  the inner paths already finalized are untouched.
- main.py cmd_update: SystemExit/BaseException/else arms around
  _cmd_update_impl persist any still-open receipt with the real exit
  code, then re-raise unchanged. Future early exits are covered without
  per-site finalize patches.
- 5 regression tests incl. end-to-end through the real cmd_update
  wrapper (exit 2 preserved, outcome 'refused', stop reason recorded,
  singleton cleared, exactly one receipt file).
2026-08-21 03:44:55 -07:00
Teknium 1d74833d8d feat(update): structured update receipts + post-update fleet version verification
Phase 1 of the fleet-update reliability plan (#91277): the updater now
proves its outcome instead of assuming it.

- hermes_cli/build_info.py: get_code_identity() — process-cached code
  identity (git sha for source installs, baked .hermes_build_sha for
  Docker images, pyproject version).
- gateway/status.py: every runtime-status write stamps the writer's
  code_sha/code_version into gateway_state.json, so a running gateway's
  actual code generation is observable from disk.
- hermes_cli/update_receipt.py (new): machine-readable receipt of each
  update run (steps, skips with reasons, gateway restart outcome, fleet
  snapshot) under ~/.hermes/logs/update_receipts/ with a latest.json
  pointer for the dashboard/desktop; plus collect_fleet_versions() /
  print_fleet_version_matrix() comparing every live profile gateway
  against the freshly updated checkout.
- hermes_cli/update_cmd.py: wires receipt begin/steps/finalize into the
  git, ZIP, and hard-failure paths; after the restart phase, prints the
  fleet version matrix and escalates provably-stale gateways into the
  existing gateway_fleet_restart_incomplete exit-1 contract. Pre-stamp
  gateways report 'unknown' and never fail the update (no false
  positives during rollout).

Silent-failure classes made visible: #88848, #74973, #85753, #81193.
Mixed-version fleet classes made loud: #88654, #69754, #77553, #56717.
2026-08-20 21:54:25 -07:00