Skip to content

fix(tui_gateway): stop replaying live-turn user text after redirect - #84520

Closed
StanleyStetson wants to merge 2 commits into
NousResearch:mainfrom
StanleyStetson:fix/84417-post-compression-prompt-replay
Closed

StanleyStetson wants to merge 2 commits into
NousResearch:mainfrom
StanleyStetson:fix/84417-post-compression-prompt-replay

Conversation

@StanleyStetson

@StanleyStetson StanleyStetson commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Stops the tui_gateway server queue from re-firing the live turn's original user prompt P after a mid-turn correction Q (Desktop session.redirect / busy-input interrupt redirect). That re-fire produced a second agent turn and a second active role=user row with the same content (#84417).

Root cause (contract layer): while turn P was live, a text-only self-copy of P could sit in session["queued_prompt"] / queued_prompts (e.g. mid-busy re-submit while redirect was not available; consecutive-text merge could also glue P\n\nQ). Successful redirect/steer of Q did not scrub that self-duplicate, so post-settle _drain_queued_prompt restarted P.

Approach:

  1. Scrub text-only self-duplicates of inflight_turn.user on successful redirect/steer; rewrite merged {P}\n\n{Q}Q only.
  2. Refuse admitting a text-only self-copy of the live user in _enqueue_prompt (scrub first so merge cannot re-glue P+Q).
  3. On compression session rotation, bump _queued_prompt_generation so an in-flight drain claim cannot dispatch against the continuation with a stale generation (queue contents for legitimate follow-ups are kept).

Scope / residual (honest limits of this PR):

  • Fixes server self-duplicates of the live turn. The independent Desktop client composer queue ($queuedPromptsBySession) is unchanged; a user-queued follow-up can still drain by design.
  • Image-bearing queue envelopes are left alone (chronology/ownership).
  • Verification is production-path unit/integration on gateway helpers (class repro), not a full interactive long-session Desktop compression e2e.

Related Issue

Fixes #84417

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • tui_gateway/server.py_sanitize_queued_entry_vs_inflight_user, _drop_queued_duplicates_of_inflight_user; scrub on successful busy redirect/steer; refuse self-dup in _enqueue_prompt; bump _queued_prompt_generation in _sync_session_key_after_compress on rotation.
  • tui_gateway/methods_session.py — scrub on successful session.redirect and session.steer (Desktop production path).
  • tests/test_tui_gateway_queue_on_busy.py — redirect scrub, hard-interrupt path, merge rewrite, drain E2E (no second P), enqueue refuse, compress generation bump / no-op.
  • tests/test_tui_gateway_server.pysession.redirect RPC scrub + build-window queue of Q scrubs stale P.

How to Test

  1. Regression suite:
    scripts/run_tests.sh tests/test_tui_gateway_queue_on_busy.py -q --tb=short
    scripts/run_tests.sh tests/test_tui_gateway_server.py -k "session_redirect" -q --tb=short
    scripts/run_tests.sh tests/tui_gateway/test_protocol.py -k "sync_session_key_after_compress" -q --tb=short
  2. Evidence (local runner): tests/test_tui_gateway_queue_on_busy.py — 24 passed; related session.redirect / compress re-anchor tests passed via scripts/run_tests.sh.
  3. Key cases:
    • successful redirect drops queued self-dup of inflight user (busy-submit + Desktop session.redirect RPC)
    • hard-interrupt + queue of Q scrubs stale P ahead of Q
    • build-window session.redirect enqueue of Q scrubs P
    • after redirect(Q), _drain_queued_prompt does not start a turn with P
    • merged P\n\nQ rewrites to Q-only
    • compress rotation bumps generation; unrelated follow-up text is preserved
  4. Class repro (pre/post): P running + queued_prompt=P + redirect(Q) → pre-fix drain re-fired P; post-fix queue scrubbed, drain no-op.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run scripts/run_tests.sh on the affected tests and they pass (project canonical runner)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Linux (WSL/Ubuntu-class), via scripts/run_tests.sh

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) — pure Python gateway session-dict logic; no OS-specific paths
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

A mid-turn correction (Desktop session.redirect / busy interrupt redirect)
must not leave a server-queue self-copy of the live inflight user prompt.
Otherwise post-turn _drain_queued_prompt restarts that original text as a
fresh agent turn after Q completes — duplicate active role=user rows and a
second conversation turn (NousResearch#84417).

Scrub text-only self-duplicates of inflight_turn.user on successful
redirect/steer, refuse admitting them in _enqueue_prompt, rewrite merged
"{P}\n\n{Q}" slots to Q-only, and bump _queued_prompt_generation when
compression rotates session_key so in-flight drain claims cannot dispatch
against the continuation with a stale generation.

Fixes NousResearch#84417
@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 12, 2026
When _queued_prompt_generation advances between claim and dispatch
(Stop, compress re-anchor), do not drop the claimed envelope. Put it
back at the head of the queue so legitimate follow-ups survive.

Part of NousResearch#84417 belt accuracy.
@StanleyStetson

Copy link
Copy Markdown
Contributor Author

Superseded by #84589 — same fix as a single conventional commit (full scrub + generation-cancel requeue) with an up-to-date PR template body. Closing this two-commit branch to keep 1 PR = 1 commit.

@StanleyStetson
StanleyStetson deleted the fix/84417-post-compression-prompt-replay branch August 12, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: post-compression Desktop session replays an earlier user prompt as a fresh turn

2 participants