Files
2026-07-01 17:56:31 +08:00

366 lines
14 KiB
Python

from __future__ import annotations
import unittest
from datetime import datetime
from opc.core.models import (
DelegationRun,
DelegationWorkItem,
ExecutionCheckpoint,
Phase,
SessionMessageRecord,
SessionPartRecord,
Task,
TaskStatus,
)
from opc.plugins.cli_board.services.board_repository import BoardRepository
from opc.layer2_organization.work_item_links import set_linked_work_item_id
class _StubStore:
def __init__(
self,
tasks,
checkpoints,
transcripts,
*,
delegation_runs=None,
work_items_by_run=None,
) -> None:
self._tasks = tasks
self._checkpoints = checkpoints
self._transcripts = transcripts
self._delegation_runs = list(delegation_runs or [])
self._work_items_by_run = dict(work_items_by_run or {})
async def get_tasks(self, **_kw):
return list(self._tasks)
async def get_pending_checkpoints(self, **_kw):
return list(self._checkpoints)
async def get_session_transcript(self, session_id):
return list(self._transcripts.get(session_id, []))
# The company-mode branch in BoardRepository feature-detects these methods.
# Only present them when the test wires a delegation run, so the standard
# tests above keep exercising the Task path.
async def list_open_delegation_runs(self, *, project_id=None):
return list(self._delegation_runs)
async def list_delegation_work_items(self, run_id):
return list(self._work_items_by_run.get(run_id, []))
class _StubFacade:
def __init__(self, store) -> None:
self.project_id = "demo"
self._engine = type("Engine", (), {"store": store})()
async def ensure_ready(self):
return self._engine
class BoardRepositoryTests(unittest.IsolatedAsyncioTestCase):
async def test_snapshot_hides_internal_origin_tasks(self) -> None:
visible = Task(
id="visible-1",
title="Visible task",
description="Board-visible task",
status=TaskStatus.PENDING,
session_id="session-visible",
project_id="demo",
)
hidden = Task(
id="hidden-1",
title="Internal task",
description="Background execution",
status=TaskStatus.RUNNING,
session_id="session-hidden",
project_id="demo",
metadata={"origin_task_id": "visible-1"},
)
checkpoint = ExecutionCheckpoint(
checkpoint_id="cp-1",
project_id="demo",
session_id="session-visible",
checkpoint_type="company_delivery_feedback",
status="pending",
payload={"prompt": "Approve delivery?"},
)
repo = BoardRepository(_StubFacade(_StubStore([visible, hidden], [checkpoint], {})), project_id="demo")
snapshot = await repo.load_snapshot()
self.assertEqual(len(snapshot.tasks), 1)
self.assertEqual(snapshot.hidden_task_count, 1)
self.assertEqual(snapshot.tasks[0].task_id, "visible-1")
self.assertIsNotNone(snapshot.tasks[0].pending_checkpoint)
self.assertEqual(len(snapshot.session_summaries), 1)
self.assertEqual(snapshot.metrics.visible_tasks, 1)
self.assertEqual(snapshot.metrics.pending_checkpoint_count, 1)
self.assertTrue(any(alert.task_id == "visible-1" for alert in snapshot.alerts))
async def test_load_task_detail_includes_transcript_and_linked_executions(self) -> None:
task = Task(
id="task-1",
title="Primary task",
description="Do the work",
status=TaskStatus.RUNNING,
session_id="session-1",
project_id="demo",
metadata={"progress_log": ["planning", "running"]},
)
linked = Task(
id="task-2",
title="Linked task",
description="Hidden execution",
status=TaskStatus.RUNNING,
session_id="session-2",
project_id="demo",
metadata={"origin_task_id": "task-1"},
)
transcript = {
"session-1": [
{
"message": SessionMessageRecord(
message_id="m1",
session_id="session-1",
role="user",
created_at=datetime.now(),
),
"parts": [
SessionPartRecord(
message_id="m1",
session_id="session-1",
part_type="text",
payload={"text": "Please implement the feature"},
)
],
},
{
"message": SessionMessageRecord(
message_id="m2",
session_id="session-1",
role="assistant",
created_at=datetime.now(),
),
"parts": [
SessionPartRecord(
message_id="m2",
session_id="session-1",
part_type="text",
payload={"text": "Working on it"},
)
],
},
]
}
repo = BoardRepository(_StubFacade(_StubStore([task, linked], [], transcript)), project_id="demo")
detail = await repo.load_task_detail("task-1")
self.assertIsNotNone(detail)
assert detail is not None
self.assertEqual(detail.task.task_id, "task-1")
self.assertEqual(len(detail.transcript), 2)
self.assertEqual(len(detail.linked_executions), 1)
self.assertEqual(detail.linked_executions[0].task_id, "task-2")
self.assertEqual(detail.progress_entries, ["planning", "running"])
self.assertEqual(detail.task.display_id, "OPC-1")
async def test_company_mode_snapshot_uses_work_items_as_cards(self) -> None:
"""In company mode, kanban cards come from DelegationWorkItem (not Task).
Asserts: card task_id == work_item_id; canonical four-state column
mapping (todo / in-progress / in-review / done); session_id remains
only as an audit reference; column_order includes "in-review".
"""
run = DelegationRun(
run_id="run-1",
project_id="demo",
company_profile="corporate",
lifecycle_status="active",
)
# Synthetic root work item — must be hidden (no parent_work_item_id).
root_item = DelegationWorkItem(
work_item_id="wi-root",
run_id="run-1",
title="Root",
summary="root",
phase=Phase.READY,
)
wi_todo = DelegationWorkItem(
work_item_id="wi-todo",
run_id="run-1",
parent_work_item_id="wi-root",
role_id="researcher",
title="Investigate API",
summary="Find current rate-limit behavior.",
phase=Phase.READY,
kind="execute",
metadata={"dependency_work_item_ids": []},
)
wi_running = DelegationWorkItem(
work_item_id="wi-running",
run_id="run-1",
parent_work_item_id="wi-root",
role_id="engineer",
title="Implement fix",
summary="Patch the throttle.",
phase=Phase.RUNNING,
kind="execute",
claimed_by_role_runtime_session_id="role-rt-1",
metadata={"activation_state": "active"},
)
wi_review = DelegationWorkItem(
work_item_id="wi-review",
run_id="run-1",
parent_work_item_id="wi-root",
role_id="qa",
title="Verify deliverable",
summary="Check the test plan.",
phase=Phase.AWAITING_MANAGER_REVIEW,
kind="review",
metadata={"review_state": "pending_manager"},
)
wi_done = DelegationWorkItem(
work_item_id="wi-done",
run_id="run-1",
parent_work_item_id="wi-root",
role_id="engineer",
title="Earlier patch",
summary="Already shipped.",
phase=Phase.APPROVED,
kind="execute",
)
# A runtime Task linked to wi-running for audit/transcript only.
linked_task = Task(
id="task-running",
title="Implement fix",
description="runtime execution",
status=TaskStatus.RUNNING,
session_id="session-running",
project_id="demo",
metadata={},
)
set_linked_work_item_id(linked_task, "wi-running")
store = _StubStore(
tasks=[linked_task],
checkpoints=[],
transcripts={},
delegation_runs=[run],
work_items_by_run={"run-1": [root_item, wi_todo, wi_running, wi_review, wi_done]},
)
repo = BoardRepository(_StubFacade(store), project_id="demo")
snapshot = await repo.load_snapshot()
self.assertEqual(snapshot.mode, "company")
self.assertEqual(snapshot.column_order, ["todo", "in-progress", "in-review", "done"])
# Root item is filtered; the four leaf work items become cards.
self.assertEqual([t.task_id for t in snapshot.tasks], ["wi-todo", "wi-running", "wi-review", "wi-done"])
# Card identity is the work_item_id, not Task.id.
self.assertEqual(snapshot.tasks[0].work_item_id, "wi-todo")
# Four-state canonical column mapping.
column_by_id = {t.task_id: t.column_id for t in snapshot.tasks}
self.assertEqual(column_by_id["wi-todo"], "todo")
self.assertEqual(column_by_id["wi-running"], "in-progress")
self.assertEqual(column_by_id["wi-review"], "in-review")
self.assertEqual(column_by_id["wi-done"], "done")
# session_id is an audit reference only (populated for the linked card).
running_card = next(t for t in snapshot.tasks if t.task_id == "wi-running")
self.assertEqual(running_card.session_id, "session-running")
self.assertEqual(running_card.runtime_task_id, "task-running")
self.assertEqual(running_card.execution_turn_id, "task-running")
todo_card = next(t for t in snapshot.tasks if t.task_id == "wi-todo")
self.assertIsNone(todo_card.session_id)
self.assertIsNone(todo_card.runtime_task_id)
self.assertIsNone(todo_card.execution_turn_id)
# Metrics surface the new in_review_count.
self.assertEqual(snapshot.metrics.in_review_count, 1)
self.assertEqual(snapshot.metrics.in_progress_count, 1)
self.assertEqual(snapshot.metrics.todo_count, 1)
self.assertEqual(snapshot.metrics.done_count, 1)
# Hidden count = synthetic root item.
self.assertEqual(snapshot.hidden_task_count, 1)
async def test_company_mode_load_task_detail_resolves_work_item_id(self) -> None:
run = DelegationRun(run_id="run-2", project_id="demo", lifecycle_status="active")
wi = DelegationWorkItem(
work_item_id="wi-x",
run_id="run-2",
parent_work_item_id="wi-root",
role_id="engineer",
title="Detail target",
summary="check detail path",
phase=Phase.RUNNING,
metadata={"handoff_context": "from upstream"},
)
linked_task = Task(
id="task-x",
title="runtime",
description="",
status=TaskStatus.RUNNING,
session_id="session-x",
project_id="demo",
metadata={"progress_log": ["start"]},
)
set_linked_work_item_id(linked_task, "wi-x")
transcript = {
"session-x": [
{
"message": SessionMessageRecord(
message_id="m1", session_id="session-x", role="assistant", created_at=datetime.now()
),
"parts": [SessionPartRecord(message_id="m1", session_id="session-x", part_type="text", payload={"text": "ok"})],
}
]
}
store = _StubStore(
tasks=[linked_task],
checkpoints=[],
transcripts=transcript,
delegation_runs=[run],
work_items_by_run={"run-2": [wi]},
)
repo = BoardRepository(_StubFacade(store), project_id="demo")
detail = await repo.load_task_detail("wi-x")
self.assertIsNotNone(detail)
assert detail is not None
self.assertEqual(detail.task.task_id, "wi-x")
self.assertEqual(detail.task.work_item_id, "wi-x")
self.assertEqual(detail.task.runtime_task_id, "task-x")
self.assertEqual(detail.task.execution_turn_id, "task-x")
self.assertEqual(detail.context_preview, "from upstream")
self.assertEqual(len(detail.transcript), 1)
self.assertEqual(detail.progress_entries, ["start"])
async def test_snapshot_preserves_adaptive_metadata_for_widgets(self) -> None:
task = Task(
id="task-adaptive",
title="Adaptive task",
description="Blocked on signals",
status=TaskStatus.BLOCKED,
session_id="session-adaptive",
project_id="demo",
metadata={
"adaptive": {
"normalized_state": "waiting_for_gate",
"blocked_reason": "Waiting for required signals: implementation_ready",
"work_item_profile": {"gate_owner_role_id": "cto"},
"signals": [
{"name": "implementation_ready", "required": True, "satisfied": False},
],
"confidence": 0.72,
}
},
)
repo = BoardRepository(_StubFacade(_StubStore([task], [], {})), project_id="demo")
snapshot = await repo.load_snapshot()
self.assertEqual(snapshot.tasks[0].metadata["adaptive"]["normalized_state"], "waiting_for_gate")
self.assertEqual(snapshot.tasks[0].metadata["adaptive"]["work_item_profile"]["gate_owner_role_id"], "cto")