Release v0.51.765 — Wave 1 batch (#5276 #5274 #5272 #5256 #5254 #5257) - #5277
Conversation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…o franksong2702/fix-transparent-live-snapshot Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Extract _turn_transcript_lacks_final_assistant_answer() so settlement logic can inspect an already-merged transcript. The merge wrapper now delegates to the pure evaluator without changing behavior. Fixes #5141
|
| Filename | Overview |
|---|---|
| api/models.py | Adds _session_messages_have_prefix helper and expands reconciled_state_db_messages_for_session to prefer the longer authoritative CLI transcript over a stale sidecar context prefix on the first WebUI continuation; compression-anchor path is preserved when the CLI override is not triggered. |
| api/routes.py | Adds refresh_cli_messages keyword arg to _get_or_materialize_session; when True and the session is a CLI session, refreshes messages from get_cli_session_messages if the DB copy is a prefix-compatible superset of the sidecar. _handle_chat_start passes this flag, and test mocks updated to accept **kwargs. |
| api/streaming.py | Behavior-preserving refactor: extracts _turn_transcript_lacks_final_assistant_answer (operates on an already-merged transcript) and makes _merged_transcript_lacks_final_assistant_answer a thin delegating wrapper that first merges then delegates; original signature and semantics unchanged. |
| static/messages.js | Computes unified completedMessageCount via a 4-level fallback (payload count → payload messages.length → S.session.message_count → S.messages.length) and advances _markSessionViewed to fire before the sidebar cache update so the actively-viewed session never shows a stale unread dot after compaction. |
| static/ui.js | Adds _renderLiveAnchorActivitySceneTransparent for transparent-stream live activity and dispatches to it before the compact-mode guard; conditionally stamps data-anchor-settled-scene-row (settled only) vs data-anchor-live-scene-row (live only) to prevent live rows from masquerading as final anchors. Complex DOM manipulation verified by idempotency tests. |
| static/sessions.js | Context-menu Delete now passes an immediate ()=>Promise.resolve() beforeDelete hook through the existing deleteSession path so the sidebar row disappears optimistically instead of lingering until the slow server round-trip completes. |
| static/index.html | Settings nav buttons wrapped in a new .settings-menu-items div to move scroll ownership off the parent #settingsMenu, allowing the absolutely-positioned search-results dropdown to escape the clip boundary. |
| static/style.css | Sets #settingsMenu to overflow: visible and introduces .settings-menu-items as the scrollable flex column for nav buttons, enabling the search dropdown to overflow the panel boundary. |
| tests/test_issue5273_active_done_viewed_sync.py | New Node.js-executed harness verifying all four fallback branches of completedMessageCount and confirming the pre-cache-update viewed sync for actively-viewed sessions across compaction scenarios. |
| tests/test_issue5270_cli_webui_continuity.py | Four integration tests covering fresh materialization, claimed-sidecar refresh, chat-start flag propagation, and writability preservation for the CLI-to-WebUI continuity fix. |
| tests/test_live_to_final_anchor_visible_order.py | Adds two new tests: a structural assertion verifying transparent dispatch precedes compact gate, and a Node.js-executed DOM test confirming idempotent render, legacy surface hiding, and correct attribute stamping for the transparent live renderer. |
| tests/test_issue5141_terminal_failure_transcript_evaluator.py | New tests verifying the refactored evaluator split: both the direct _turn_transcript_lacks_final_assistant_answer path and the delegating wrapper produce identical results, and the wrapper's delegation is verified via mock patching. |
| tests/test_session_active_profile_authorization.py | Two existing monkeypatches updated to accept **_kwargs on _get_or_materialize_session to stay compatible with the new refresh_cli_messages keyword argument. |
| tests/test_issue3975_cron_reply_materialization.py | One-line update: materialize stub now accepts **_kwargs to accommodate the new keyword argument on _get_or_materialize_session. |
| tests/test_issue5250_settings_search_dropdown_escape.py | New Playwright-based browser hit-test verifying the search results dropdown extends below #settingsMenu's visible boundary and is click-target-accessible. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CLI as Hermes CLI
participant StateDB as state.db
participant Routes as routes.py _handle_chat_start
participant Materialize as _get_or_materialize_session
participant Models as reconciled_state_db_messages
participant Stream as _run_agent_streaming
CLI->>StateDB: write turns (user + assistant)
CLI->>Routes: WebUI chat-start (session_id)
Routes->>Materialize: "sid, refresh_cli_messages=True"
Materialize->>StateDB: get_cli_session_messages(sid)
Note over Materialize: if latest >= current AND prefix matches<br/>s.messages = latest_messages
Materialize-->>Routes: session (messages refreshed)
Routes->>Models: reconciled_state_db_messages_for_session
Note over Models: sidecar_messages = session.messages (refreshed)<br/>if sidecar longer than context_messages AND prefix OK<br/>local_messages = sidecar, skip compression path
Models-->>Routes: merged history (CLI turns preserved)
Routes->>Stream: _run_agent_streaming(conversation_history)
Stream-->>Routes: result (CLI context intact)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CLI as Hermes CLI
participant StateDB as state.db
participant Routes as routes.py _handle_chat_start
participant Materialize as _get_or_materialize_session
participant Models as reconciled_state_db_messages
participant Stream as _run_agent_streaming
CLI->>StateDB: write turns (user + assistant)
CLI->>Routes: WebUI chat-start (session_id)
Routes->>Materialize: "sid, refresh_cli_messages=True"
Materialize->>StateDB: get_cli_session_messages(sid)
Note over Materialize: if latest >= current AND prefix matches<br/>s.messages = latest_messages
Materialize-->>Routes: session (messages refreshed)
Routes->>Models: reconciled_state_db_messages_for_session
Note over Models: sidecar_messages = session.messages (refreshed)<br/>if sidecar longer than context_messages AND prefix OK<br/>local_messages = sidecar, skip compression path
Models-->>Routes: merged history (CLI turns preserved)
Routes->>Stream: _run_agent_streaming(conversation_history)
Stream-->>Routes: result (CLI context intact)
Reviews (1): Last reviewed commit: "docs(changelog): wave-1 batch (#5276 #52..." | Re-trigger Greptile
🎬 Cutter preview — PR #5277
|

Release v0.51.765 — Wave 1 batch (6 fix-class PRs)
Batched release of 6 gate-certified, file-disjoint contributor fix PRs. Each was individually
gate-certified GREEN (Codex SAFE + full suite + per-PR tests) at its current head; this combined
stage was re-gated as a unit.
Included
Gate
the pre-existing
test_scheduled_jobs_profile_isolation.pyserial-ordering artifact (passes12/12 in isolation; no PR here touches cron code) — environmental, not a regression.
CHANGELOG updated under
[Unreleased] → Fixedwith per-author credit.