fix(tui_gateway): candidate-inclusive display on warm/live + child-watch resume (#65919 fallout) - #69040
Merged
Merged
Conversation
#65919 persists verification candidates (finish_reason=verification_required / verify_hook_continue) to state.db but collapses them out of the in-memory model history via repair_message_sequence. The eager session.resume + REST paths read the verbatim display lineage (candidate present), but the warm/live-reuse payload (_live_session_payload) built its user-visible messages from the collapsed in-memory model history — so switching to a still-live session dropped the substantive verification answer that a cold resume of the SAME session showed. That divergence is the cross-session "substantive text vanishes on switch" class, and the direct sibling of the resume-duplication regression fixed in #68149. Reconcile the persisted display lineage (candidate-inclusive, the same get_messages_as_conversation(..., include_ancestors=True) read the eager resume + REST paths use) with the fresh in-memory tail in _live_visible_history, so all three surfaces agree by construction while a not-yet-flushed live turn is still shown. Extracted _reconcile_display_with_live as a pure, DI-testable function (anchors on the last persisted row's (role, text); appends only the uncovered in-memory tail; trusts the DB display when the tail can't be anchored). Tests: unit coverage for candidate-inclusion, freshness, empty/raising-DB fallback, and the combined candidate+fresh-tail case. The existing freshness guard (test_session_resume_live_payload_uses_current_history_with_ancestors) stays green.
Contributor
૮ >ﻌ< ა ci reviewrunning on 77855ce CI timingsCI timings · View jobWall time 7m54s vs 10m10s (-22.3%). 9 job(s) slower, 9 faster, 2 unchanged.
|
… E2E Complete the #65919 warm/live-payload fix across its sibling path and add real-SessionDB cross-builder coverage. - Child-watch (lazy) resume: the delegated-subagent watch window served _history_to_messages(repaired_history) for its user-visible messages, which collapses out persisted verification candidates just like the warm-payload path did. Build the visible messages from the verbatim child-only display projection (repair_alternation=False) while the repaired history still feeds live replay; fall back to the repaired history if the display read fails. - E2E cross-builder consistency (real SessionDB, not mocks): a persisted verification candidate is collapsed out of the model projection but kept in the display projection, and _live_visible_history now equals the eager session.resume display projection (candidate present). Adds the combined candidate + fully-flushed-second-turn case and a lazy child-watch handler test that asserts the candidate survives in resp["result"]["messages"].
ethernet8023
approved these changes
Jul 22, 2026
13 tasks
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…-candidate-warm-payload fix(tui_gateway): candidate-inclusive display on warm/live + child-watch resume (NousResearch#65919 fallout)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
#65919 ("don't wipe messages before final message") made a durable, unconditional change: verification candidates (
finish_reason=verification_required/verify_hook_continue) are persisted tostate.db, butrepair_message_sequencecollapses them out of the in-memory model history. That leaves two projections of one session:session.resumepath and REST/messages.messages.So switching to a still-live session (or opening a delegated-child watch window) dropped the substantive verification answer that a cold resume of the same session showed. The payloads disagreed about one session — the cross-session "substantive text vanishes on switch" class, and the direct sibling of the resume-duplication regression already fixed in #68149. The #65919 reviewer note called this out pre-merge: the config flag gates only the display/transport callback; the agent-core persistence + collapse ships unconditionally, so every consumer that mixes the two projections must reconcile them — two didn't.
Fix
Reconcile the projections in one place, and apply it to both offending payload builders.
_reconcile_display_with_live(db_display, in_memory)— pure, DI-testable. Candidate-inclusive DB display is the base; anchors on the last persisted row's(role, text); appends only the in-memory tail the DB doesn't yet cover; trusts the DB display when the tail can't be anchored (DB ahead / diverged)._live_visible_history(warm/live reuse) reads the sameget_messages_as_conversation(..., include_ancestors=True)the eager resume + REST paths use, reconciled with the fresh in-memory tail — so all surfaces agree by construction while a not-yet-flushed live turn still shows.repair_alternation=False) instead of the repaired history; the repaired history still feeds live replay. Falls back to the repaired history if the display read fails.No schema change, no new config, no mid-conversation cache mutation, no desktop/TS change.
Testing
scripts/run_tests.sh tests/test_tui_gateway_server.py tests/tui_gateway/ tests/test_hermes_state.py→ 1166 passed, 0 failed.Coverage (incl. real-SessionDB E2E, not mocks):
_live_visible_historyequals the eagersession.resumedisplay projection (candidate present).resp["result"]["messages"].session_keymissing, DB empty (unflushed first turn), DB raising.test_session_resume_live_payload_uses_current_history_with_ancestorsstays green — the reconciliation satisfies both invariants at once.Scope
Fixes the confirmable backend fallout of #65919 across both remaining offending paths. The other confirmed regression (#68149) is already merged. #68231 (desktop live-render) does not reproduce on current
main—apps/desktop/.../interim-sealing.test.tsx:100("preserves interim text that the final response does not include") already covers that scenario and is green; that report matches a pre-merge desktop build. This PR additionally guarantees the persisted substantive answer recovers consistently on any refresh/switch.Relates to #65919, #68149.
sweeper:risk-session-state.