fix(ui): stabilize company chat result topology

This commit is contained in:
LZH-YS1998
2026-07-14 20:31:10 +08:00
parent 0a6246aff9
commit 5938b4c215
25 changed files with 1917 additions and 381 deletions
+47
View File
@@ -37,6 +37,7 @@ from opc.layer2_organization.work_item_identity import (
migrate_work_item_projection_metadata,
projection_id_for_work_item,
rework_projection_id_for_gate,
result_delivery_identity_payload_for_task,
target_projection_id_for_decision,
target_projection_ids_for_decision,
work_item_identity_payload,
@@ -217,6 +218,52 @@ class WorkItemProjectionIdentityTests(unittest.TestCase):
self.assertEqual(payload[WORK_ITEM_PROJECTION_ID_KEY], "task-proj")
self.assertEqual(payload[WORK_ITEM_TURN_TYPE_KEY], "report")
def test_result_delivery_identity_uses_canonical_turn_and_retry_attempt(self) -> None:
task = SimpleNamespace(
id="task-1",
retry_count=2,
metadata={"runtime_v2_current_turn_id": "canonical-turn-1"},
)
payload = result_delivery_identity_payload_for_task(task)
self.assertEqual(
payload["result_delivery_id"],
"result:task:task-1:turn:canonical-turn-1:attempt:2",
)
self.assertEqual(payload["source_task_id"], "task-1")
self.assertEqual(payload["canonical_turn_id"], "canonical-turn-1")
def test_result_delivery_identity_does_not_collide_for_parallel_tasks(self) -> None:
first = SimpleNamespace(id="task-1", retry_count=0, metadata={})
second = SimpleNamespace(id="task-2", retry_count=0, metadata={})
first_payload = result_delivery_identity_payload_for_task(
first,
canonical_turn_id="shared-parent-turn",
)
second_payload = result_delivery_identity_payload_for_task(
second,
canonical_turn_id="shared-parent-turn",
)
self.assertNotEqual(
first_payload["result_delivery_id"],
second_payload["result_delivery_id"],
)
self.assertIn(":task-1:", first_payload["result_delivery_id"])
self.assertIn(":task-2:", second_payload["result_delivery_id"])
def test_result_delivery_identity_requires_an_execution_scope(self) -> None:
task = SimpleNamespace(id="reused-task", retry_count=0, metadata={})
self.assertEqual(result_delivery_identity_payload_for_task(task), {
"source_task_id": "reused-task",
})
first = result_delivery_identity_payload_for_task(task, execution_id="execution-1")
second = result_delivery_identity_payload_for_task(task, execution_id="execution-2")
self.assertNotEqual(first["result_delivery_id"], second["result_delivery_id"])
def test_migrate_projection_metadata_backfills_from_fallbacks_without_overwriting(self) -> None:
migrated, changed = migrate_work_item_projection_metadata(
{},
+153 -1
View File
@@ -67,8 +67,95 @@ class MemoryManagerCompactionTests(unittest.IsolatedAsyncioTestCase):
compactor.maybe_compact_after_message.assert_not_awaited()
async def test_parent_child_result_preserves_structured_source_lineage(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
store = _MemoryStoreStub()
memory = MemoryManager(Path(tmpdir), project_id="0009", store=store)
task = Task(
id="task-cto-result",
title="CTO Result",
assigned_to="cto",
project_id="0009",
session_id="child-session",
parent_session_id="parent-session",
metadata={"work_item_projection_id": "cto::analysis"},
)
await memory.record_child_session_result(
"parent-session",
"child-session",
task=task,
result_content="done",
result_delivery_id=(
"result:task:task-cto-result:turn:canonical-cto-turn:attempt:0"
),
source_result_message_id="source-result-message",
canonical_turn_id="canonical-cto-turn",
)
metadata = store.session_messages[-1].metadata
self.assertEqual(
metadata["result_delivery_id"],
"result:task:task-cto-result:turn:canonical-cto-turn:attempt:0",
)
self.assertEqual(metadata["source_result_message_id"], "source-result-message")
self.assertEqual(metadata["source_task_id"], "task-cto-result")
self.assertEqual(metadata["child_session_id"], "child-session")
self.assertEqual(metadata["canonical_turn_id"], "canonical-cto-turn")
class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
def test_fallback_delivery_identity_is_per_result_not_per_provider_session(self) -> None:
task = Task(
id="shared-role-task",
title="Shared role",
assigned_to="cto",
retry_count=0,
)
first = TaskResult(
status=TaskStatus.AWAITING_HUMAN,
content="first pause",
artifacts={"provider_session_id": "provider-session-reused"},
)
second = TaskResult(
status=TaskStatus.DONE,
content="second result",
artifacts={"provider_session_id": "provider-session-reused"},
)
first_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, first)
second_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, second)
self.assertNotEqual(
first_identity["result_delivery_id"],
second_identity["result_delivery_id"],
)
self.assertNotEqual(
first.artifacts["result_execution_id"],
second.artifacts["result_execution_id"],
)
self.assertNotIn("provider-session-reused", first_identity["result_delivery_id"])
def test_fallback_delivery_identity_is_stable_after_result_persistence(self) -> None:
task = Task(id="external-task", title="External", assigned_to="cto")
result = TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={"runtime_session_id": "runtime-session-reused"},
)
first_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, result)
persisted_artifacts = dict(result.artifacts)
reloaded = TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts=persisted_artifacts,
)
replay_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, reloaded)
self.assertEqual(replay_identity, first_identity)
self.assertEqual(reloaded.artifacts, persisted_artifacts)
async def test_company_shared_role_session_keeps_results_local(self) -> None:
engine = OPCEngine()
engine.store = SimpleNamespace(
@@ -81,7 +168,11 @@ class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
record_task_completion_async=AsyncMock(),
)
engine._run_task_once = AsyncMock(
return_value=TaskResult(status=TaskStatus.DONE, content="done", artifacts={})
return_value=TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={"result_delivery_id": "delivery-shared-role"},
)
)
engine._apply_runtime_state_to_task = lambda task, result: None
@@ -105,3 +196,64 @@ class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
engine.memory.record_assistant_turn.assert_awaited_once()
engine.memory.record_child_session_result.assert_not_awaited()
metadata = engine.memory.record_assistant_turn.await_args.kwargs["metadata"]
self.assertEqual(metadata["result_delivery_id"], "delivery-shared-role")
self.assertEqual(metadata["source_task_id"], "task-cmo-review")
self.assertEqual(metadata["child_session_id"], "app14:role:cmo")
async def test_child_result_and_parent_mirror_share_delivery_identity(self) -> None:
engine = OPCEngine()
engine.store = SimpleNamespace(
get_task=AsyncMock(return_value=None),
save_task=AsyncMock(),
)
engine.memory = SimpleNamespace(
record_assistant_turn=AsyncMock(
return_value=SimpleNamespace(message_id="source-result-message")
),
record_child_session_result=AsyncMock(),
record_task_completion_async=AsyncMock(),
)
engine._run_task_once = AsyncMock(
return_value=TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={
"canonical_turn_id": "runtime-generated-turn",
"result_delivery_id": (
"result:task:task-cto-result:turn:runtime-generated-turn:attempt:0"
),
"runtime_session_id": "runtime-session-1",
},
)
)
engine._apply_runtime_state_to_task = lambda task, result: None
task = Task(
id="task-cto-result",
title="CTO Result",
assigned_to="cto",
status=TaskStatus.PENDING,
project_id="0009",
session_id="child-session",
parent_session_id="parent-session",
metadata={
"execution_mode": "company_mode",
"work_item_projection_id": "cto::analysis",
},
)
await engine._execute_task(task)
child_metadata = engine.memory.record_assistant_turn.await_args.kwargs["metadata"]
parent_call = engine.memory.record_child_session_result.await_args.kwargs
self.assertEqual(
child_metadata["result_delivery_id"],
"result:task:task-cto-result:turn:runtime-generated-turn:attempt:0",
)
self.assertEqual(
parent_call["result_delivery_id"],
child_metadata["result_delivery_id"],
)
self.assertEqual(parent_call["source_result_message_id"], "source-result-message")
self.assertEqual(parent_call["canonical_turn_id"], "runtime-generated-turn")
+8
View File
@@ -414,6 +414,13 @@ class NativeRuntimeV2Tests(unittest.IsolatedAsyncioTestCase):
# cannot swallow it into the frozen shared-iteration row.
self.assertTrue(metadata["company_final_turn"])
self.assertEqual(metadata["ui_message_id"], "runtime-v2-company-assistant-final:ui-turn:company")
self.assertEqual(
metadata["result_delivery_id"],
"result:task:company-role-task:turn:ui-turn:company:attempt:0",
)
self.assertEqual(metadata["source_task_id"], "company-role-task")
self.assertEqual(metadata["child_session_id"], "sess-company")
self.assertEqual(metadata["work_item_projection_id"], "chao::intake")
self.assertNotIn("visible_speaker", metadata)
await runtime._persist_assistant_turn(
@@ -429,6 +436,7 @@ class NativeRuntimeV2Tests(unittest.IsolatedAsyncioTestCase):
intermediate_metadata = memory.appended_messages[-1]["kwargs"]["metadata"]
self.assertEqual(intermediate_metadata["kind"], "runtime_v2_company_assistant")
self.assertNotIn("company_final_turn", intermediate_metadata)
self.assertNotIn("result_delivery_id", intermediate_metadata)
self.assertEqual(
intermediate_metadata["ui_message_id"],
"runtime-v2-company-assistant:ui-turn:company",
+315 -1
View File
@@ -16,10 +16,34 @@ from opc.plugins.office_ui.chat_store import (
_MessageMatchIndex,
_MessageMatchState,
)
from opc.plugins.office_ui.snapshot_builder import build_transcript_ui_messages
from opc.plugins.office_ui.snapshot_builder import (
build_transcript_ui_messages,
collapse_adjacent_transcript_duplicates,
)
from opc.plugins.office_ui.ws_handler import WSHandler
_WORK_ITEM_RESULT = """Both work items have been successfully dispatched and can execute in parallel.
**Dispatch Summary**
**Work Item 1: OpenOPC Source Code Architecture Deep-Dive Analysis**
- ID: `1ed5f5f1-ac41-49a1-b1fa-23bbc9adab82`
- Owner: senior_engineer
- Scope: `openopc-source-analysis`
- Output: `/workspace/openopc-architecture-analysis.md`
- Covers: Layered architecture, work-item state machines, collaboration policy, and seat executors.
**Work Item 2: External Multi-Agent Frameworks Architecture Research**
- ID: `d0307208-6b95-44c1-9b51-6bf073bbdcef`
- Owner: senior_engineer
- Scope: `external-frameworks-research`
- Output: `/workspace/external-frameworks-analysis.md`
- Covers: Architecture models, coordination, communication, extensibility, and implementation details.
Both are independent and can execute in parallel."""
class TranscriptStorePaginationTests(unittest.IsolatedAsyncioTestCase):
async def test_summary_page_filters_full_detail_rows_before_limit(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
@@ -216,6 +240,43 @@ class TranscriptStorePaginationTests(unittest.IsolatedAsyncioTestCase):
detail_level="summary",
))
def test_renderer_preserves_structured_result_lineage(self) -> None:
lineage = {
"canonical_turn_id": "turn-canonical",
"turn_id": "turn-execution",
"result_delivery_id": "delivery-1",
"source_result_message_id": "source-message-1",
"source_task_id": "source-task-1",
"child_session_id": "child-session-1",
"conversation_turn_id": "conversation-turn-1",
"execution_turn_id": "execution-turn-1",
"work_item_projection_id": "architecture",
"work_item_turn_type": "delivery",
"runtime_session_id": "runtime-session-1",
}
rendered = build_transcript_ui_messages(
[{
"message": SimpleNamespace(
message_id="result-message-1",
role="assistant",
agent_id="cto",
created_at=datetime(2026, 7, 14, 10, 0, 0),
summary_flag=False,
metadata={"kind": "child_result", **lineage},
),
"parts": [SimpleNamespace(
part_type="text",
payload={"text": "Completed architecture analysis."},
)],
}],
channel_id="session:parent-task",
task_id="parent-task",
)
self.assertEqual(len(rendered), 1)
for key, value in lineage.items():
self.assertEqual(rendered[0]["metadata"].get(key), value)
class ChatStorePaginationTests(unittest.TestCase):
@staticmethod
@@ -293,6 +354,129 @@ class ChatStorePaginationTests(unittest.TestCase):
actual = store._dedupe_messages(messages)
self.assertEqual(actual, expected)
def test_work_item_colons_are_not_treated_as_narrative_wrappers(self) -> None:
normalized = ChatStore._normalize_duplicate_content(_WORK_ITEM_RESULT)
self.assertEqual(normalized, _WORK_ITEM_RESULT)
self.assertEqual(ChatStore._normalize_duplicate_content(normalized), normalized)
def test_duplicate_merge_keeps_work_item_display_content_across_replays(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
current = {
"message_id": "runtime-final",
"channel_id": "session:cto",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"reply_to_id": None,
"mentions": [],
"metadata": {
"source": "engine",
"transcript_kind": "runtime_v2_company_assistant",
},
}
canonical = {
**current,
"message_id": "child-task-result",
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_task_result",
},
}
for _ in range(6):
current = store._merge_duplicate_messages(current, canonical)
self.assertEqual(current["content"], _WORK_ITEM_RESULT)
def test_duplicate_merge_reuses_an_original_unwrapped_surface(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
body = "Canonical answer " + ("with implementation details. " * 8)
wrapped = {
"message_id": "runtime-final",
"channel_id": "session:answer",
"sender": "assistant",
"sender_name": "OPC",
"content": f"**Top level answer**: {body}",
"created_at": 1.0,
"metadata": {
"source": "engine",
"transcript_kind": "runtime_v2_assistant",
},
}
unwrapped = {
**wrapped,
"message_id": "top-level-reply",
"content": body,
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "top_level_reply",
},
}
merged = store._merge_duplicate_messages(wrapped, unwrapped)
self.assertEqual(merged["content"], unwrapped["content"])
self.assertIn(merged["content"], (wrapped["content"], unwrapped["content"]))
def test_result_delivery_identity_is_namespaced_without_canonical_turn_fallback(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
existing = {
"message_id": "raw-final",
"channel_id": "session:result",
"sender": "assistant",
"content": "runtime wording",
"created_at": 1.0,
"metadata": {
"result_delivery_id": "delivery-1",
"canonical_turn_id": "turn-shared",
},
}
delivered = {
**existing,
"message_id": "child-task-result",
"content": "canonical result wording",
"metadata": {
"result_delivery_id": "delivery-1",
"canonical_turn_id": "turn-shared",
},
}
unrelated_user = {
**existing,
"message_id": "user-turn",
"sender": "user",
"content": "different user text",
"metadata": {"canonical_turn_id": "turn-shared"},
}
self.assertIn("result_delivery:delivery-1", store._message_identity_keys(existing))
self.assertTrue(store._messages_semantically_match(existing, delivered))
self.assertFalse(store._messages_semantically_match(existing, unrelated_user))
def test_transcript_collapse_keeps_work_item_display_content_across_replays(self) -> None:
runtime = {
"message_id": "runtime-final",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"metadata": {"transcript_kind": "runtime_v2_company_assistant"},
}
canonical = {
**runtime,
"message_id": "child-task-result",
"created_at": 2.0,
"metadata": {"transcript_kind": "child_task_result"},
}
collapsed = collapse_adjacent_transcript_duplicates([runtime, canonical])
self.assertEqual(len(collapsed), 1)
self.assertEqual(collapsed[0]["content"], _WORK_ITEM_RESULT)
replayed = collapse_adjacent_transcript_duplicates([runtime, collapsed[0]])
self.assertEqual(len(replayed), 1)
self.assertEqual(replayed[0]["content"], _WORK_ITEM_RESULT)
def test_indexed_dedupe_normalizes_long_content_once_per_row(self) -> None:
class CountingChatStore(ChatStore):
normalize_calls = 0
@@ -501,6 +685,136 @@ class ChatStorePaginationTests(unittest.TestCase):
await db.close()
tmpdir.cleanup()
def test_backfill_same_id_repairs_destructively_normalized_content(self) -> None:
asyncio.run(self._exercise_backfill_same_id_repairs_content())
async def _exercise_backfill_same_id_repairs_content(self) -> None:
tmpdir = tempfile.TemporaryDirectory()
db = _SQLiteConnectionAdapter(str(Path(tmpdir.name) / "ui-state.db"))
store = ChatStore(db) # type: ignore[arg-type]
await store.initialize()
channel_id = "session:work-item-repair"
project_id = "test-project"
damaged_content = _WORK_ITEM_RESULT[_WORK_ITEM_RESULT.index("OpenOPC Source"):]
metadata = {
"source": "engine",
"transcript_kind": "child_result",
"legacy_cache_marker": True,
}
authoritative = {
"message_id": "result-message-1",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_result",
},
}
try:
await store.insert_message(
channel_id,
"assistant",
"CTO",
damaged_content,
metadata=metadata,
message_id="result-message-1",
project_id=project_id,
created_at=1.0,
)
repaired = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
replayed = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
rows = await store.get_channel_messages(
channel_id,
limit=20,
project_id=project_id,
)
self.assertEqual([message["message_id"] for message in repaired], ["result-message-1"])
self.assertEqual(replayed, [])
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["content"], _WORK_ITEM_RESULT)
self.assertTrue(rows[0]["metadata"].get("legacy_cache_marker"))
finally:
await db.close()
tmpdir.cleanup()
def test_backfill_semantic_duplicate_upgrades_existing_row_in_place(self) -> None:
asyncio.run(self._exercise_backfill_semantic_duplicate_upgrade())
async def _exercise_backfill_semantic_duplicate_upgrade(self) -> None:
tmpdir = tempfile.TemporaryDirectory()
db = _SQLiteConnectionAdapter(str(Path(tmpdir.name) / "ui-state.db"))
store = ChatStore(db) # type: ignore[arg-type]
await store.initialize()
channel_id = "session:semantic-result-repair"
project_id = "test-project"
authoritative_content = _WORK_ITEM_RESULT
authoritative = {
"message_id": "source-result-message",
"sender": "cto",
"sender_name": "CTO",
"content": authoritative_content,
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_task_result",
},
}
try:
await store.insert_message(
channel_id,
"assistant",
"CTO",
f"**Legacy result**: {_WORK_ITEM_RESULT}",
metadata={
"source": "engine",
"transcript_kind": "runtime_v2_company_assistant",
"legacy_cache_marker": True,
},
message_id="mounted-cache-row",
project_id=project_id,
created_at=1.0,
)
upgraded = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
replayed = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
rows = await store.get_channel_messages(
channel_id,
limit=20,
project_id=project_id,
)
self.assertEqual([message["message_id"] for message in upgraded], ["mounted-cache-row"])
self.assertEqual(replayed, [])
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["message_id"], "mounted-cache-row")
self.assertEqual(rows[0]["created_at"], 1.0)
self.assertEqual(rows[0]["content"], authoritative_content)
self.assertEqual(rows[0]["metadata"].get("transcript_kind"), "child_task_result")
self.assertTrue(rows[0]["metadata"].get("legacy_cache_marker"))
finally:
await db.close()
tmpdir.cleanup()
def test_summary_cache_page_filters_before_raw_fetch_limit(self) -> None:
asyncio.run(self._exercise_summary_cache_page())