v0.51.75 — Release AY
Stage-368: 11-PR safe-lane batch + maintainer pytest-loop fix.
Composition
Test infrastructure
- stage-368 maintainer fix — Permanent
os.execvguard intests/conftest.pyto prevent pytest from self-looping on the_schedule_restartdaemon thread. Tests intests/test_update_banner_fixes.pyexerciseapi.updates._schedule_restart(), which spawns a daemon thread that eventually callsos.execv(). Those tests monkeypatchos.execvfor the test scope, but monkeypatch teardown can win the race against the daemon thread — the realos.execvis restored before the thread fires it, and the daemon re-execs the entire pytest process with the original argv. From the outside this looked like pytest hanging at 99% and then restarting the entire suite from 0% in an infinite loop. The fix shadowsos.execvwith a permanent no-op wrapper at conftest module-import time; tests that need to verify execv was called still patch it themselves. Newtests/test_pytest_execv_guard.pypins the guard against future conftest refactors.
Added
- PR #2377 by @franksong2702 (refs #2283, refs #2363, refs #1925) — Run-journal replay timeline parity checks.
Fixed
- PR #2391 by @Michaelyklam (fixes #2389) — Reduce browser storage pressure: SW dual-cache window during version bumps + unbounded per-session localStorage map growth.
- PR #2387 by @Michaelyklam (fixes #2386) — Guard
localStorage.setItem('hermes-webui-session', ...)and workspace-panel writes with try/catch. - PR #2368 by @Michaelyklam — Hybridize background profile env routing: thread-local +
os.environ.updatemirror under narrow_ENV_LOCK. - PR #2382 by @Michaelyklam (fixes #2380) — Serve raw chat attachments from per-session inbox via
api/file/rawwith path-traversal guards intact. - PR #2385 by @franksong2702 — Keep fuller compression snapshots reachable in sidebar.
- PR #2371 by @franksong2702 — Clarify interrupted turn recovery after WebUI restart.
- PR #2378 by @Michaelyklam — Strip historical images in text-only mode via new
cfg=kwarg on_sanitize_messages_for_api. - PR #2375 by @Michaelyklam — Keep Markdown tables block-level (add
tableto paragraph-wrap skip list). - PR #2372 by @mccxj — Settings → Conversation page action buttons respect locale selection.
- PR #2381 by @Michaelyklam (fixes #2379) — German session-time labels now interpolate
{n}correctly.
Verification
- pytest: 5747 passed, 10 skipped, 1 xfailed, 2 xpassed, 8 subtests passed in 106.70s. EXIT=0. Single run, no self-loop.
- Opus advisor: SHIP verdict. Risk areas A–G verified clean. No SHOULD-FIX items.
- Production server health verified.
DEFER (follow-up)
- Complete #2297 coverage in
_handle_chat_sync(api/routes.py:7871) — passcfg=_cfgto_sanitize_messages_for_apiin the fallback POST /api/chat path. ~2 LOC, fallback endpoint unused by frontend. Will be filed as follow-up issue.
Auto-close issues
What's Changed
- v0.51.75 — Release AY (stage-368: 11-PR safe-lane batch + pytest-loop fix) by @nesquena-hermes in #2397
- fix: hybridize background profile env routing by @Michaelyklam in #2368
- Clarify interrupted turn recovery after WebUI restart by @franksong2702 in #2371
- fix: add i18n support to settings conversation page action buttons by @mccxj in #2372
- Add run-journal replay timeline parity checks by @franksong2702 in #2377
- fix: strip historical images in text mode by @Michaelyklam in #2378
- fix: interpolate German session time labels by @Michaelyklam in #2381
- fix: serve raw chat attachments from inbox by @Michaelyklam in #2382
- Keep fuller compression snapshots reachable in sidebar by @franksong2702 in #2385
- fix: guard localStorage quota writes by @Michaelyklam in #2387
- fix: reduce browser storage pressure by @Michaelyklam in #2391
- fix: keep markdown tables block-level by @Michaelyklam in #2375
New Contributors
Full Changelog: v0.51.74...v0.51.75