From de412cef0e7b92222eeaacaafb32978be09eb738 Mon Sep 17 00:00:00 2001 From: Hermes Bot Date: Sun, 3 May 2026 21:18:58 +0000 Subject: [PATCH] =?UTF-8?q?release:=20stamp=20v0.50.287=20=E2=80=94=20PR?= =?UTF-8?q?=20#1565=20self-update=20active-stream=20guard=20(4051=20?= =?UTF-8?q?=E2=86=92=204053=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 18 ++++++++++++++++++ ROADMAP.md | 2 +- TESTING.md | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ef32abd..2ce43aee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Hermes Web UI -- Changelog +## [v0.50.287] — 2026-05-03 + +### Fixed (1 PR — closes another vector for the pending-message-loss class) + +- **Self-update refuses to re-exec while chat streams are active** (#1565, @ai-ag2026) — closes the last known vector for the pending-message-loss class fixed in #1471/#1543/#1558. The WebUI self-update path schedules an in-process `os.execv()` re-exec after applying updates. That restart-equivalent path is independent of systemd, so when a browser user clicks "Update Now" while a chat is streaming, the process can be replaced mid-stream — same data-loss class as the stale-stream/pending-message work in v0.50.279/v0.50.284. **Fix:** new `_active_stream_count()` helper reads `len(STREAMS)` under `STREAMS_LOCK`; both `apply_update(target)` and `apply_force_update(target)` short-circuit at function entry with a structured `{ok: False, restart_blocked: True, active_streams: N, message: "Cannot update {target} while {N} active chat stream{s} is running. Wait for the response to finish, then retry the update."}` response — **before** any git command runs and **before** scheduling restart. Frontend integration: `_showUpdateError` in `static/ui.js:2882` already routes `res.message` to the persistent error element, and the "Force update" button only reveals on `res.conflict || res.diverged` (neither set for `restart_blocked`), so the user gets a clean error and correctly cannot escalate to force-update (which has the same restart problem and is also blocked by the same guard). 2 new regression tests in `tests/test_update_banner_fixes.py::TestApplyUpdateRestartSafety` pin the refusal shape AND the absence of side effects (`_run_git` never called; `_schedule_restart` raises if invoked). Pre-release Opus advisor: SHIP AS-IS — verified that the residual race window (between guard release and `_apply_lock` acquire) is bounded by design and recoverable via the #1543 pending-message recovery path. Closing the window would require holding `STREAMS_LOCK` across the whole git+restart sequence, which would block every new chat for the duration of an update — worse UX than the residual race. + +### Tests + +4051 → **4053 passing** (+2 from PR #1565). 0 regressions. Full suite in 120s. + +### Pre-release verification + +- All 31 update-banner tests pass standalone in 3.5s (29 existing + 2 new). +- All 4053 tests pass in the full suite. +- Browser sanity (HTTP API checks against port 8789): 11/11 endpoints verified. +- Pre-release Opus advisor: SHIP AS-IS — all 5 verification questions resolved (race-window bounded, lock ordering safe, no deadlock, frontend integration clean, test isolation robust against assertion failures). + + ## [v0.50.286] — 2026-05-03 ### Fixed (1 PR — closes #1560) diff --git a/ROADMAP.md b/ROADMAP.md index 7351ff5b..a4a83eb5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ > Web companion to the Hermes Agent CLI. Same workflows, browser-native. > -> Last updated: v0.50.286 (May 03, 2026) — 4051 tests collected +> Last updated: v0.50.287 (May 03, 2026) — 4053 tests collected > Test source: `pytest tests/ --collect-only -q` > Per-version detail: see [CHANGELOG.md](./CHANGELOG.md) diff --git a/TESTING.md b/TESTING.md index 465a0740..7ac8ffd9 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1835,8 +1835,8 @@ Bridged CLI sessions: --- -*Last updated: v0.50.286, May 03, 2026* -*Total automated tests collected: 4051* +*Last updated: v0.50.287, May 03, 2026* +*Total automated tests collected: 4053* *Regression gate: tests/test_regressions.py* *Run: pytest tests/ -v --timeout=60* *Source: /*