fix(tui-gateway): merge agent output on personality-marker history race - #77860
Open
pierrenode wants to merge 1 commit into
Open
fix(tui-gateway): merge agent output on personality-marker history race#77860pierrenode wants to merge 1 commit into
pierrenode wants to merge 1 commit into
Conversation
config.set's "personality" key applies immediately regardless of session["running"], unlike "model" (which defers to next-turn-start while a turn is in flight). A personality change mid-turn appends a history marker and bumps history_version exactly like a model switch does, but the turn-completion mismatch guard only recognized model-switch markers as a safe, mergeable mutation (NousResearch#76870) — a personality-marker mismatch fell into the "genuine desync" branch and the agent's response for that turn was silently discarded instead of persisted. Extend the merge-recovery check in _run_prompt_submit to also recognize personality-pivot markers (by content-prefix, or by display_kind == "personality_switch" for forward-compat with the unrelated NousResearch#74350 marker-persistence fix) as safe to merge, mirroring the exact technique NousResearch#76870/cc04825c5d already established for model-switch markers.
This was referenced Aug 5, 2026
refactor(tui): extract pet payload into tui_gateway/pet_payload (server.py god-file slice R3)
#79261
Open
Open
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.
Summary
config.set's"personality"key applies immediately regardless ofsession["running"], unlike"model"(which defers to next-turn-start while a turn is in flight — see thesession.get("running")check inconfig.set'skey == "model"branch).session["history"]and bumpshistory_version— the exact same shape of mutation a model switch makes._run_prompt_submit(added by [Bug]: Model switch mid-session triggers history_version mismatch — all subsequent agent output discarded (empty assistant messages in DB) #76870, three days ago) only recognized model-switch markers as a safe, mergeable mutation. A personality-marker-only mismatch fell into the "genuine desync" branch instead: the agent's response for that turn was silently discarded from persisted history (only shown transiently with a "not saved to session history" warning).Root cause
_apply_personality_to_session(tui_gateway/server.py) has nosession.get("running")guard on theconfig.setRPC path — it calls straight through, appending{"role": "user", "content": marker}and incrementinghistory_versionundersession["history_lock"], whether or not a turn is currently in flight._run_prompt_submit's post-turnhistory_versioncheck (introduced by #76870 for the analogous model-switch race) only ever compared against_is_model_switch_marker, so a personality marker landing mid-turn was treated identically to a genuine external desync (/undo,/compress,/retry) — the agent's output for that turn was dropped instead of merged.Fix
Extend the merge-recovery check to also recognize personality-pivot markers as safe to merge, using the exact content-diff technique #76870/cc04825c5d already established and reviewed for model-switch markers:
_is_personality_marker()(mirrors_is_model_switch_marker()), matching either the marker's stable"[System: ..."content-prefix (today's shape) ordisplay_kind == "personality_switch"(the structured shape an unrelated open PR, fix(history): stop personality marker from swallowing the next real prompt #74350, would give newly-appended markers — checking both keeps this correct regardless of merge order between the two fixes)._run_prompt_submit's mismatch branch now strips both model-switch and personality markers before the content-diff comparison, and accepts either marker type as the sole safe mutation.Test plan
test_prompt_submit_merges_on_personality_marker, mirroring the existingtest_prompt_submit_merges_on_model_switch_markerharness. Covers both cases (no prior personality marker, and a prior marker already in the turn-start history) — simulates the race by appending the marker from inside the stub agent'srun_conversation(), exactly as a concurrentconfig.setRPC would mid-turn.tui_gateway/server.pychange and confirmed the new test fails with the exact pre-fix symptom (agent output NOT written to session history, 0 assistant messages merged).tests/test_tui_gateway_server.pyfull suite: 517 passed, 1 pre-existing unrelated failure (test_write_json_serializes_concurrent_writes, a thread-scheduling-timing test — confirmed flaky/pre-existing by re-running it in isolation both with and without this change, unaffected either way).ruff checkclean on both changed files.Competitor check
tui_gateway/server.py'sconfig.set/_apply_personality_to_session/_run_prompt_submitmerge-recovery logic)._apply_personality_to_sessionto persist the marker durably with structureddisplay_kind/display_metadata, fixing a different bug (the marker swallowing the next real prompt on resume/reload via alternation-repair). It never touches_run_prompt_submit's mismatch-merge block. This PR's_is_personality_marker()is written to stay correct whether fix(history): stop personality marker from swallowing the next real prompt #74350 lands before or after it.gateway/run.py/gateway/slash_commands.pypath (a different gateway surface entirely, nottui_gateway/server.py).