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")