From b8eedfefa24cd8eb2e43ceeb69b4aa6d555b34d6 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:15:03 +0530 Subject: [PATCH] fix(tui-gateway): count live rows in the 4033 archived_messages on a carrier cut Gate C18 r1 quality [Low, unverified -> reproduced]: on the durable-carrier path `truncated` is the physical durable_prefix, so `len(history) - len(truncated)` mixed live and physical rows. A probe with target row 101 absorbed into a repaired live carrier reported archived_messages=5 for a cut that drops all 6 live rows (the carrier and everything after it). The refusal now counts from the live cut index on that path; the plain path is unchanged. --- .../test_tui_gateway_server_deep_truncate.py | 11 +++++++++++ tui_gateway/methods_prompt.py | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/tui_gateway/test_tui_gateway_server_deep_truncate.py b/tests/tui_gateway/test_tui_gateway_server_deep_truncate.py index c3cf9ce4f7..584e7767c1 100644 --- a/tests/tui_gateway/test_tui_gateway_server_deep_truncate.py +++ b/tests/tui_gateway/test_tui_gateway_server_deep_truncate.py @@ -51,6 +51,17 @@ def test_prompt_submit_refuses_deep_truncation_without_confirm(monkeypatch): assert _submit(confirm_deep_truncate=True).get("error") is None assert replaced == [history[:2]] + + # Durable-carrier cut: the target (101) was absorbed into a repaired live carrier; + # the refusal counts live rows from the carrier on, never live minus physical. + physical = [{"_row_id": 100, "role": "user", "content": "u0"}, + {"_row_id": 101, "role": "user", "content": "u0b"}, *history[1:]] + _FakeDB.get_messages_as_conversation = lambda self, key, **_kw: [dict(m) for m in physical] # type: ignore[attr-defined] + sess["running"] = False # the confirmed submit above left a (never-started) turn + sess["history"] = [{"_row_id": 100, "_absorbed_row_ids": [101], "role": "user", + "content": "u0\n\nu0b"}, *history[1:]] + carrier = _submit(truncate_before_row_id=101) + assert carrier["error"]["data"] == {"archived_messages": 6, "archived_user_turns": 3} finally: server._sessions.pop("deep-trunc-sid", None) diff --git a/tui_gateway/methods_prompt.py b/tui_gateway/methods_prompt.py index e0f0edaf3e..0cef613fda 100644 --- a/tui_gateway/methods_prompt.py +++ b/tui_gateway/methods_prompt.py @@ -466,7 +466,9 @@ def _truncate_history_for_submit(rid, sid, session, params, requested_rebind_ids # drop exactly one user turn, deeper cuts need confirm_deep_truncate. archived_user_turns = user_turn_count - ordinal if archived_user_turns > 1 and not is_truthy_value(params.get("confirm_deep_truncate")): - archived_messages = len(history) - len(truncated) + # A durable-carrier cut's ``truncated`` is physical rows: count the live cut instead. + archived_messages = len(history) - ( + cut_index if durable_prefix is not None else len(truncated)) logger.warning( "prompt.submit: REFUSED deep truncation of session %s (%d messages / %d user " "turns would be archived; ordinal=%d).",