Release v0.50.279 — 8-PR batch from full PR sweep
End-to-end PR sweep of all 16 open PRs. Routes:
- Merged in this batch (8): #1523, #1519, #1518, #1516, #1517, #1532, #1525, #1526
- Closed as duplicates (2): #1528 (identical to #1517), #1529 (superseded by #1516)
- Routed to maintainer-review (1): #1531 (Asunfly stowaway change in force-push to title aux generation that wasn't in PR description)
- Stay on hold (5): #1418 (prereq hermes-agent#18534), #1464 (blocker — noResults ternary inverted), #1404 (UX — aronprins width feedback unresolved), #1353 (durability path, ready-for-review tagged for next batch), #1311 (draft + CONFLICTING)
Constituent PRs (8 merged)
Polish / UX (5 small)
| PR | Author | LOC | What |
|---|---|---|---|
| #1523 | @franksong2702 | 2/2 | Fork-indicator glyph: \u2482 → \u2442 (was ⒂ PARENTHESIZED DIGIT FIFTEEN, now ⑂ OCR FORK). Closes #1522.
|
| #1519 | @franksong2702 | 1/1 | Onboarding API-key field stops losing focus during 400ms-debounced probe re-render — moved probe trigger from oninput to onblur. Closes #1503.
|
| #1518 | @franksong2702 | 5/2 | Voice-mode pref toggle-off now stops the SpeechRecognition (was leaving recognizer running with no UI to stop it). Closes #1491. |
| #1516 | @franksong2702 | 4/0 | YAML code blocks render with newlines (Prism token spans had white-space: normal collapsing \n). Closes #1463.
|
| #1517 | @franksong2702 | 13/13 | Consolidate __CACHE_VERSION__ → __WEBUI_VERSION__ placeholder name (pure rename, no behavior change). Closes #1509.
|
State recovery + provider quota (3 backend)
| PR | Author | LOC | What |
|---|---|---|---|
| #1532 | @ai-ag2026 | 38/3 | Recover WebUI-origin state.db sessions when JSON sidecar missing (slice 1 of #1471). Removes hard-coded s.source != 'webui' predicate from read_importable_agent_session_rows().
|
| #1525 | @ai-ag2026 | 85/2 | Clear stale runtime stream state proactively (slice 2 of #1471). New _clear_stale_stream_state() helper runs at /sessions/<id> GET boundary + before new turns. Frontend half: INFLIGHT[sid] cleared when server reports no active_stream_id.
|
| #1526 | @ai-ag2026 | 68/0 | Forward configured max_tokens into AIAgent (was using provider-native ceilings → OpenRouter 402 quota errors). Adds 3 new quota-phrase classifiers. Refs #1524.
|
Test status
3936 → 3946 tests passing (+9 from constituent PRs + 1 conflict-marker regression guard added in-release per Opus MUST-FIX). Zero regressions.
Pre-release Opus advisor pass — MUST-FIX caught and absorbed
This release was nearly broken. Opus pass on stage-279 caught that the merge-conflict resolution between #1525 (cache-name manual suffix) and #1517 (rename) had never actually landed — static/sw.js lines 10-14 still contained the literal <<<<<<< HEAD / ======= / >>>>>>> pr-1525 markers. The substring-based source-string tests still passed because __WEBUI_VERSION__ WAS in the file (just inside the conflict block).
Concrete impact pre-fix when shipped:
- Service worker
installevent would throw on script load (SyntaxError: Unexpected token '<<') - SW would never reach
activatedstate - Old SW (from v0.50.278) would keep controlling the page indefinitely
- Frontend cache-bust pathway silently broken
- The
INFLIGHT[sid]clear (the frontend half of #1525's stale-stream cleanup) would never deliver to existing browsers
Resolution applied in stage: sw.js conflict markers removed, kept HEAD's CACHE_NAME = 'hermes-shell-__WEBUI_VERSION__' (post-#1517 rename) over the PR's -stale-stream-cleanup1 manual suffix. Natural version-token bump preserves the cache-invalidation guarantee.
Regression guard added in-release: new test_sw_js_has_no_merge_conflict_markers test scans for <<<<<<<, =======, >>>>>>> markers in static/sw.js. This couldn't happen silently again — a future merge that leaves conflict markers will fail CI.
Opus SHOULD-FIX deferred to follow-up
Filed as #1533: _clear_stale_stream_state() mutates session.active_stream_id outside the per-session lock (release pattern: hold STREAMS_LOCK only across the registry lookup, then mutate after release). Race window between registry-check and session-mutate could clobber a concurrent _handle_chat_start registration. Effect bounded (orphaned stream → retry, no data corruption). Opus explicitly said "probably fine to defer given the narrow window."
Diff stats
api/agent_sessions.py | 6 +-
api/routes.py | 30 +++-
api/streaming.py | 32 +++++-
static/boot.js | 7 +-
static/onboarding.js | 2 +-
static/sessions.js | ~20
static/style.css | 4 +
static/sw.js | 6 +-
tests/* (8 new files + edits) | ~250 lines
CHANGELOG.md | +
ROADMAP.md | 2 +-
TESTING.md | 4 +-
Sweep methodology
Followed webui-full-pr-triage-release/references/extended-sweep-procedure.md Phases 0-9. Phase 0 fit-screen on all 16 open PRs, hold-readiness sweep, UX gate, merge queue build (small-first), stage merges (1 conflict resolved live), pre-release gate (clean state pytest + QA harness), Opus pass (MUST-FIX caught and absorbed), stamp + push.