Skip to content

stage-345: 2-PR low-risk batch — stream-ownership guard against stale writebacks + Refresh-usage button on provider quota card - #2153

Merged
nesquena-hermes merged 6 commits into
masterfrom
stage-345
May 12, 2026
Merged

stage-345: 2-PR low-risk batch — stream-ownership guard against stale writebacks + Refresh-usage button on provider quota card#2153
nesquena-hermes merged 6 commits into
masterfrom
stage-345

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

stage-345 — small 2-PR contributor batch (v0.51.52)

Composition

Per Nathan's "no high-risk merges today" directive: only the 2 low-risk PRs ship this batch. The two higher-risk PRs were deferred to tomorrow with reasons captured.

PR Author LOC Risk Surface
#2136 LumenYoung 139 Low api/streaming.py — stream-ownership guard against stale writebacks
#2150 Jordan-SkyLF 83 Trivial static/panels.js — Refresh-usage button on provider quota card

What ships

#2136 — stale stream writeback guard

cancel_stream() clears active_stream_id early so the UI can accept a new turn while the old worker unwinds. The old worker could still return later from run_conversation() and persist its stale result over the newer transcript — observed corruption pattern on cancel+retry over compressed continuations. New helper:

def _stream_writeback_is_current(session, stream_id):
    return bool(stream_id) and getattr(session, 'active_stream_id', None) == stream_id

…short-circuits both finalize paths (success path in _run_agent_streaming, cancel path in cancel_stream()). 89-line regression suite in tests/test_stale_stream_writeback.py.

#2150 — Refresh usage button on provider quota card

Frontend-only. Calls /api/provider/quota?refresh=1&ts=<now> with cache: 'no-store' to bypass browser/SW/proxy caches. The refresh=1 query param is a no-op at the server today (no in-process cache layer in get_provider_quota()); the win is at the transport layer. Future maintainer follow-up may add server-side TTL caching, at which point the same param becomes load-bearing on both sides.

What's deferred to tomorrow

Opus advisor finding (deferred to follow-up)

Opus flagged two additional api/streaming.py writeback sites that should also call _stream_writeback_is_current() but are pre-existing (not introduced by #2136):

  1. Outer except handler, line 3989 — materializes pending_user_message and appends _error_message without ownership check; if Worker A raises after rotation to Stream B, it stamps B's prompt with A's error.
  2. Self-heal retry success path, line 3947 — persists _heal_result without ownership check.

Both pre-existing bug paths, medium severity, low probability. Filing a follow-up issue rather than expanding scope today.

Verification

  • Full pytest: 5307 passed, 11 skipped, 1 xfailed, 2 xpassed in 95s (was 5306 before this stage — +1 net new test from fix: guard stale stream writebacks #2136 minus minor adjustments)
  • run-browser-tests.sh: 20/20 QA + 11/11 API checks PASSED in 113s
  • Live UI smoke test on port 8789 (fresh isolated env):
    • Server up, zero JS errors
    • _stream_writeback_is_current helper present in api/streaming.py (3 occurrences: def + 2 guard sites)
    • "Refresh usage" button live on quota card, enabled, correct label
    • Click flow verified: before → during (Refreshing…/disabled/aria-busy=true) → after (recovered cleanly)
    • Failure toast appears when refresh fails (expected in test env with no provider configured)
  • python -m py_compile clean on all modified .py files
  • node --check clean on static/panels.js
  • Merge-conflict markers: zero in any modified file
  • No file overlap between fix: guard stale stream writebacks #2136 and feat: add manual provider usage refresh #2150
  • Opus advisor: "Ship both PRs" + 1 follow-up issue (above)

Stats

 11 files changed, 232 insertions(+), 8 deletions(-)

Closes

No linked issues in this batch. #2136 and #2150 both describe behavior they fix without Closes #N references.

Jordan-SkyLF and others added 6 commits May 12, 2026 13:17
fix: guard stale stream writebacks (LumenYoung)

Prevents stale WebUI stream workers from writing old results into a session
after that session has already moved on to another stream. Adds new helper
_stream_writeback_is_current() (a token equality check against the session's
active_stream_id) and short-circuits the two finalize/cancel paths when the
worker no longer owns the session writeback.
feat: add manual provider usage refresh (Jordan-SkyLF)

Adds a 'Refresh usage' button on the Provider quota card in Settings → Providers,
with cache: 'no-store' fetch + browser cache-bust query string. Pure browser-side
cache-busting; the server-side /api/provider/quota endpoint has no cache layer
yet (refresh=1 query param is currently a no-op server-side; the win is bypassing
browser/proxy/SW caches).
@nesquena-hermes
nesquena-hermes merged commit 9268f41 into master May 12, 2026
3 checks passed
@nesquena-hermes
nesquena-hermes deleted the stage-345 branch May 12, 2026 23:57
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
stage-345: 2-PR low-risk batch — stream-ownership guard against stale writebacks + Refresh-usage button on provider quota card
bernyforce pushed a commit to bernyforce/hermes-webui that referenced this pull request Jul 29, 2026
stage-345: 2-PR low-risk batch — stream-ownership guard against stale writebacks + Refresh-usage button on provider quota card
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.

3 participants