fix: guarantee engine replies reach the UI channel and stop same-scope duplicate rows

Project 000, 19:21/20:27: the engine recorded its assistant reply in
session_messages, but the post-turn transcript sync never surfaced it in the
ui_state channel, and nothing reconciled afterwards — the user watched an
empty conversation while the reply sat in the DB. And at 19:13 the same reply
was persisted twice in one channel under `<id>` and `<id>::<project>::<channel>`.

Two fixes:
- _ensure_reply_projected: after the transcript sync, if the session's newest
  persisted top_level_reply row is absent from the chat store (checked by
  transcript message id, so nothing user-visible is ever duplicated or leaked),
  insert and broadcast it directly. Reproduced by test: with the sync disabled
  the reply previously never reached the channel.
- backfill_messages: a live insert racing the backfill snapshot now merges into
  the existing same-scope row (new _merge_into_same_scope_row, also used by the
  IntegrityError fallback) instead of minting a `::`-scoped alias id in the
  message's own channel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LZH-YS1998
2026-07-07 22:59:53 +08:00
parent 3a2c935025
commit 818189a1cc
4 changed files with 365 additions and 0 deletions
+44
View File
@@ -6476,6 +6476,50 @@ class TestSnapshotBuilderSessionData(unittest.IsolatedAsyncioTestCase):
finally:
await chat_store._db.close()
async def test_backfill_merges_same_scope_row_that_raced_the_snapshot(self) -> None:
"""A live insert landing after the backfill snapshot must merge in place,
never persist a second copy under a `::`-scoped alias id (project 000:
the same reply was stored twice in one channel)."""
chat_store = await _make_chat_store()
try:
await chat_store.create_session_channel("race-task", "Race", project_id="p1")
channel_id = "session:race-task"
original_scope = chat_store._message_scope
async def racing_scope(message_id: str):
# Simulate the live insert path landing the same row after the
# backfill snapshot was taken but before its INSERT runs.
if message_id == "engine-race-1" and await original_scope(message_id) is None:
await chat_store.insert_message(
channel_id=channel_id,
sender="assistant",
sender_name="OPC",
content="Reply text",
message_id="engine-race-1",
project_id="p1",
metadata={"note": "live-copy"},
)
return await original_scope(message_id)
chat_store._message_scope = racing_scope
await chat_store.backfill_messages(channel_id, [{
"message_id": "engine-race-1",
"sender": "assistant",
"sender_name": "OPC",
"content": "Reply text",
"timestamp": time.time(),
"metadata": {"source": "engine", "role": "assistant"},
}], project_id="p1")
rows = await chat_store.get_channel_messages(channel_id, limit=20, project_id="p1")
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["message_id"], "engine-race-1")
self.assertNotIn("::", rows[0]["message_id"])
finally:
await chat_store._db.close()
async def test_build_collab_sync_marks_primary_as_company_runtime_from_children(self) -> None:
"""Legacy primary sessions should still be marked as company runtimes when child work items exist."""
from opc.plugins.office_ui.snapshot_builder import build_collab_sync