Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"""Regression: relay-plane delivery defects (staging incident 2026-08-09).
|
||||
|
||||
Defect A — the "skip redundant final edit" branch records ``self._accumulated``
|
||||
as the delivered turn-final payload even when the last edit that actually
|
||||
reached the platform was an earlier throttled preview snapshot. The recorded
|
||||
payload then satisfies ``delivered_final_matches`` and the gateway suppresses
|
||||
the corrective final send, leaving the user staring at a frozen preview ending
|
||||
in the streaming cursor.
|
||||
|
||||
Contract under test (end-to-end): the payload recorded as *delivered* must be
|
||||
what was last acknowledged on the wire, so a preview/final mismatch yields
|
||||
``delivered_final_matches(final) is False`` and the normal final send fires.
|
||||
|
||||
Defect B — ``_classify_completion_target`` treats every ended parent session
|
||||
as terminal unless it ended by compression. Relay-plane sessions end on idle
|
||||
by design (scale-to-zero); the chat route remains valid, so async delegation
|
||||
completions must classify "deliver", not be terminally dropped. Explicit user
|
||||
boundaries (/new -> session_reset / user_exit) stay terminal.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from gateway.run import GatewayRunner
|
||||
from gateway.stream_consumer import GatewayStreamConsumer, StreamConsumerConfig
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Defect A: stale preview recorded as delivered final
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class _EditAdapter:
|
||||
"""Adapter stub: every send/edit succeeds and remembers the last payload."""
|
||||
|
||||
name = "stub"
|
||||
|
||||
def __init__(self):
|
||||
self.sent = []
|
||||
|
||||
async def send_message(self, chat_id, content, **kw):
|
||||
self.sent.append(content)
|
||||
return SimpleNamespace(success=True, message_id="m1")
|
||||
|
||||
async def edit_message(self, chat_id, message_id, content, **kw):
|
||||
self.sent.append(content)
|
||||
return SimpleNamespace(success=True, message_id=message_id)
|
||||
|
||||
|
||||
def _consumer(adapter):
|
||||
cfg = StreamConsumerConfig(edit_interval=0.01)
|
||||
return GatewayStreamConsumer(adapter, "C1", config=cfg)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skip_redundant_finalize_records_acked_payload_not_accumulated():
|
||||
"""The delivered record must reflect the last ACKED edit, so a stale
|
||||
preview cannot masquerade as the delivered final (incident class:
|
||||
'It launched but ▉')."""
|
||||
adapter = _EditAdapter()
|
||||
sc = _consumer(adapter)
|
||||
|
||||
preview = "It launched but"
|
||||
final = (
|
||||
"It launched but the worker had no credentials, so the check did "
|
||||
"not run. The delegates completed; results follow."
|
||||
)
|
||||
|
||||
# Simulate: a mid-stream edit delivered the throttled preview snapshot,
|
||||
# then the turn finished with more content accumulated and the consumer
|
||||
# took the skip-redundant-finalize branch (no further edit issued).
|
||||
sc._message_id = "m1"
|
||||
sc._last_sent_text = preview + sc.cfg.cursor
|
||||
sc._accumulated = final
|
||||
sc._mark_skip_redundant_finalize()
|
||||
|
||||
verdict = sc.delivered_final_matches(final)
|
||||
assert verdict is False, (
|
||||
"stale preview snapshot must NOT be reconciled as the delivered "
|
||||
f"final (got verdict={verdict!r}); the normal final send would be "
|
||||
"suppressed and the user left with a frozen preview"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_finalize_edit_success_still_reconciles_true():
|
||||
"""Control: when the finalize edit actually delivered the full final
|
||||
text, reconciliation must remain True (no dup sends regression)."""
|
||||
adapter = _EditAdapter()
|
||||
sc = _consumer(adapter)
|
||||
final = "Complete final answer."
|
||||
sc._message_id = "m1"
|
||||
sc._last_sent_text = final
|
||||
sc._accumulated = final
|
||||
sc._mark_skip_redundant_finalize()
|
||||
assert sc.delivered_final_matches(final) is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Defect B: idle-ended relay session terminally drops completions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class _SessionDB:
|
||||
def __init__(self, row):
|
||||
self._row = row
|
||||
|
||||
async def get_session(self, session_id):
|
||||
return self._row
|
||||
|
||||
async def get_compression_tip(self, session_id):
|
||||
return None
|
||||
|
||||
|
||||
def _classify_runner(row):
|
||||
runner = object.__new__(GatewayRunner)
|
||||
runner._session_db = _SessionDB(row)
|
||||
return runner
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("end_reason", ["idle_timeout", "timeout", None, ""])
|
||||
async def test_idle_ended_parent_classifies_deliver(end_reason):
|
||||
"""Relay-plane norm: session ended on idle, chat still routable ->
|
||||
the completion must be deliverable, not terminally dropped."""
|
||||
runner = _classify_runner(
|
||||
{"ended_at": 1786288000.0, "end_reason": end_reason}
|
||||
)
|
||||
verdict = await runner._classify_completion_target("sess-idle")
|
||||
assert verdict == "deliver", (
|
||||
f"end_reason={end_reason!r} must classify 'deliver' "
|
||||
f"(got {verdict!r}); completed delegation work was dropped in "
|
||||
"staging because idle-ended sessions classified terminal"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("end_reason", ["session_reset", "user_exit", "session_switch"])
|
||||
async def test_user_boundary_still_terminal(end_reason):
|
||||
"""Explicit user boundaries remain terminal — /new means the user
|
||||
closed the thread of work on purpose."""
|
||||
runner = _classify_runner(
|
||||
{"ended_at": 1786288000.0, "end_reason": end_reason}
|
||||
)
|
||||
verdict = await runner._classify_completion_target("sess-reset")
|
||||
assert verdict == "terminal"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unknown_session_still_terminal():
|
||||
runner = _classify_runner(None)
|
||||
assert await runner._classify_completion_target("gone") == "terminal"
|
||||
Reference in New Issue
Block a user