Skip to content

Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch - #2596

Merged
nesquena-hermes merged 26 commits into
masterfrom
stage-386
May 19, 2026
Merged

nesquena-hermes merged 26 commits into
masterfrom
stage-386

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch

Ten contributor PRs across RFC docs, workspace UX polish, prompt-cache visibility, terminal hygiene, provider-routing, profile-aware naming, state.db reconciliation slice 1, queued-message race, and stale-stream supersede.

PRs in this batch

Fixed

Changed

Documentation

Verification

Pre-Opus 7-point gate — green

  • JS syntax (panels/sessions/messages/ui/i18n/boot/workspace) — clean
  • Python ast.parse over all api/*.py + tests/*.py — clean
  • Merge marker scan — none
  • CHANGELOG TBD placeholders — none
  • No CJK escape literals added
  • No Docker surface touched
  • No new module with concurrent primitives (api/usage.py is pure functions)

Tests

  • Pytest: 6018 passed, 6 skipped, 3 xpassed, 8 subtests passed in 136.70s (0:02:16) — +33 tests vs v0.51.92
  • Browser API sanity (port 8789): 11/11 PASS

Opus advisor — GO-WITH-FOLLOWUP (1 filed as tracker issue)

Verified each of five self-verify concerns with file:line citations:

  1. Recovery slice 1 from the closed reconciliation pr #2581 + Fix stale stream recovery writeback race #2587 composition on api/streaming.py — Safe. _previous_messages (line 4023-4028) is a local at turn start, doesn't mutate s.messages. The recovery marker on s.messages is detected directly by _stream_writeback_can_supersede_recovery_marker (line 2372). Merge function at line 4198 drops the marker from the merged result since _previous_messages doesn't carry it. ✅

  2. Recovery slice 1 from the closed reconciliation pr #2581 metadata fast-path safety — Mostly safe. Sidecar-only legacy session case (no state.db row) is safe — get_state_db_session_summary returns {} and _summary_message_count is None. One narrow theoretical edge case filed as tracker issue Metadata fast-path message_count can diverge from merged count when state.db retains old rows #2594: if state.db retains rows the sidecar lost AND those rows are timestamp-filtered by merge_session_messages_append_only, state_count > merged_count keeps remoteCount > localCount firing every 5s. Atypical (both stores normally written from same source) but worth a precise fix.

  3. Fix profile-aware assistant display names #2583 assistantDisplayName() consistency — Clean. grep window._botName returns 4 hits, all are assignments (no stray reads). All 8 read sites use the new helper. Profile switch correctly re-invokes applyBotName(). ✅

  4. fix: prevent queued follow-up message from draining into wrong chat #2584 queued-message guard tightness — The gap between _sendInProgress=true and _sendInProgressSid=activeSid exists but is bounded by two layers of defense: concurrent rejected send() falls back to S.session.session_id, in-flight send eventually captures activeSid, and the 120ms drain guard re-queues if mismatched. Tightening is a refinement, not a correctness fix. Acceptable as shipped. ✅

  5. fix: reap terminal shells on shutdown #2582 pdeathsig portability — Safe. On macOS, libc.prctl raises AttributeError, caught by the bare except Exception. Child proceeds normally. On Linux, prctl(PR_SET_PDEATHSIG, SIGTERM) succeeds. Reasonable belt-and-suspenders with wait(timeout=1.0) after SIGKILL and atexit.register(close_all_terminals). ✅

Triage actions taken this sweep (not in the batch)

Notes

Michael Lam and others added 26 commits May 18, 2026 21:06
When API server runs append messages directly to state.db, reconcile WebUI sidecar sessions with those canonical rows across API responses, model-facing streaming context, and active browser refresh.

Add append-only state.db merge helpers, metadata-only counts for refresh polling, and regression coverage for API visibility, context incorporation, and frontend refresh behavior.
Force same-session external refreshes to dismiss stale approval and clarification prompts immediately so completed state.db updates do not leave the composer blocked.
When a queued message was waiting for the active stream to finish,
the 120ms setTimeout drain in setBusy(false) would write the queued
text to the shared #msg composer and call send(), which reads
S.session.session_id at call time. If the user switched to a different
chat during the 120ms window, the queued message was sent to the
wrong session.

Two fixes:

1. setBusy(false) drain: guard the setTimeout callback — if the
   currently viewed session no longer matches the drain session,
   put the message back into the original session's queue instead
   of sending it.

2. _sendInProgress re-queue: track _sendInProgressSid alongside
   _sendInProgress so that when a concurrent send() is caught by the
   guard, the re-queued message targets the in-flight session rather
   than the currently viewed one.
# Conflicts:
#	CHANGELOG.md
# Conflicts:
#	CHANGELOG.md
# Conflicts:
#	api/streaming.py
# Conflicts:
#	CHANGELOG.md
@nesquena-hermes
nesquena-hermes merged commit 0310fcc into master May 19, 2026
3 checks passed
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch
@nesquena-hermes
nesquena-hermes deleted the stage-386 branch June 28, 2026 06:06
bernyforce pushed a commit to bernyforce/hermes-webui that referenced this pull request Jul 29, 2026
Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants