Files
OpenOPC/tests/test_kanban_push_runtime.py
T
2026-07-01 17:56:31 +08:00

611 lines
26 KiB
Python

"""Tests for the kanban-push runtime model.
Covers the core state-machine -> queue routing + UI lane derivation changes
that let the company-mode kanban actively drive the runtime:
- `effective_assignee_for_company_work_item` swaps owner to the manager on
`in_review` and back to the worker otherwise.
- `_apply_done_transition` spawns a hidden review `DelegationWorkItem`
for the manager when a child enters manager review, and terminal worker
cleanup abandons the current review attempt if no verdict was produced.
- `CompanyRuntime._pop_next_queue_entry` always drains review work items
before regular work, enforcing "manager clears reviews before dispatching".
- `CompanyRuntime.claim_runnable_tasks` soft-wakes a `blocked` manager
session when its queue has a review work item, then restores the prior
focus after the review turn ends.
"""
from __future__ import annotations
import tempfile
import unittest
from collections import deque
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock
from opc.core.config import OPCConfig, RoleConfig
from opc.core.events import EventBus
from opc.core.models import (
CompanyMemberSession,
DelegationWorkItem,
Phase,
Task,
TaskStatus,
)
from opc.database.store import OPCStore
from opc.layer2_organization.communication import CommunicationManager
from opc.layer2_organization.company_mode import (
CompanyWorkItemExecutor,
report_work_item_id_for_attempt,
review_work_item_id_for_attempt,
)
from opc.layer2_organization.company_runtime import CompanyRuntime
from opc.layer2_organization.org_engine import OrgEngine
from opc.layer2_organization.phase import effective_owner
from opc.layer2_organization.work_item_links import linked_work_item_id_for_task, set_linked_work_item_id
WORK_ITEM_STATUS_IN_REVIEW = Phase.AWAITING_MANAGER_REVIEW
WORK_ITEM_STATUS_IN_PROGRESS = Phase.RUNNING
WORK_ITEM_STATUS_TODO = Phase.READY
def effective_assignee_for_company_work_item(item: dict[str, object]) -> tuple[str, str]:
raw_phase = item.get("phase") or item.get("status") or WORK_ITEM_STATUS_TODO
if not isinstance(raw_phase, Phase):
raw_phase = {
"todo": Phase.READY,
"in_progress": Phase.RUNNING,
"in_review": Phase.AWAITING_MANAGER_REVIEW,
}.get(str(raw_phase), Phase.READY)
return effective_owner(raw_phase, item)
class EffectiveAssigneeHelperTests(unittest.TestCase):
def test_in_progress_returns_worker_identity(self) -> None:
item = {
"status": "in_progress",
"role_id": "engineer",
"seat_id": "seat::team::cto::engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"metadata": {"activation_state": "active"},
}
self.assertEqual(
effective_assignee_for_company_work_item(item),
("engineer", "seat::team::cto::engineer"),
)
def test_in_review_returns_manager_identity(self) -> None:
item = {
"status": "in_review",
"role_id": "engineer",
"seat_id": "seat::team::cto::engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"metadata": {
"activation_state": "awaiting_review",
"review_owner_role_id": "cto",
"review_owner_seat_id": "seat::team::cto::cto",
},
}
self.assertEqual(
effective_assignee_for_company_work_item(item),
("cto", "seat::team::cto::cto"),
)
def test_rework_returns_worker_identity(self) -> None:
item = {
"status": "todo",
"role_id": "engineer",
"seat_id": "seat::team::cto::engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"metadata": {"activation_state": "ready_for_rework", "rework_feedback": "redo X"},
}
self.assertEqual(
effective_assignee_for_company_work_item(item),
("engineer", "seat::team::cto::engineer"),
)
class ReviewQueuePriorityTests(unittest.TestCase):
def test_review_work_item_pops_before_work_item(self) -> None:
queue: deque[str] = deque(
[
"work-item::wi-1",
"review-work-item::review::wi-2",
"task-3",
]
)
popped = CompanyRuntime._pop_next_queue_entry(queue)
self.assertEqual(popped, "review-work-item::review::wi-2")
# The remaining order must still preserve the non-review entries.
self.assertEqual(list(queue), ["work-item::wi-1", "task-3"])
def test_review_work_item_at_head_is_popped_directly(self) -> None:
queue: deque[str] = deque(["review-work-item::review::wi-1", "work-item::wi-2"])
popped = CompanyRuntime._pop_next_queue_entry(queue)
self.assertEqual(popped, "review-work-item::review::wi-1")
self.assertEqual(list(queue), ["work-item::wi-2"])
def test_no_review_task_falls_back_to_fifo(self) -> None:
queue: deque[str] = deque(["task-1", "work-item::wi-2"])
popped = CompanyRuntime._pop_next_queue_entry(queue)
self.assertEqual(popped, "task-1")
self.assertEqual(list(queue), ["work-item::wi-2"])
def _build_executor(store: OPCStore, org_engine: OrgEngine) -> CompanyWorkItemExecutor:
communication = CommunicationManager(store, EventBus(), org_engine=org_engine)
return CompanyWorkItemExecutor(
org_engine=org_engine,
communication=communication,
approval_engine=MagicMock(),
memory=None,
execute_task=AsyncMock(),
save_task=store.save_task,
store=store,
)
class ReviewWorkItemLifecycleTests(unittest.IsolatedAsyncioTestCase):
async def test_in_review_transition_spawns_manager_review_work_item(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
store = OPCStore(root / "tasks.db")
await store.initialize()
try:
config = OPCConfig()
config.org.company_profile = "custom"
config.org.roles = [
RoleConfig(id="ceo", name="CEO", responsibility="Set direction.", reports_to="owner"),
RoleConfig(id="cto", name="CTO", responsibility="Lead engineering.", reports_to="ceo"),
RoleConfig(id="engineer", name="Engineer", responsibility="Build features.", reports_to="cto"),
]
org_engine = OrgEngine(config, root)
executor = _build_executor(store, org_engine)
child_work_item = DelegationWorkItem(
work_item_id="wi-child",
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="engineer",
seat_id="seat::team::cto::engineer",
manager_role_id="cto",
manager_seat_id="seat::team::cto::cto",
title="Build feature",
summary="Ship the feature.",
kind="execute",
projection_id="wi-child",
phase=WORK_ITEM_STATUS_IN_PROGRESS,
metadata={
"team_id": "team::cto",
"seat_id": "seat::team::cto::engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"runtime_model": "multi_team_org",
"activation_state": "active",
},
)
await store.save_delegation_work_item(child_work_item)
worker_task = Task(
id="task-engineer",
title="Build feature",
project_id="proj1",
session_id="session-root",
parent_session_id="session-root",
assigned_to="engineer",
status=TaskStatus.DONE,
metadata={
"execution_mode": "company_mode",
"runtime_model": "multi_team_org",
"work_item_runtime": True,
"delegation_run_id": "run-1",
"delegation_team_id": "team::cto",
"delegation_seat_id": "seat::team::cto::engineer",
"work_item_role_id": "engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"work_item_projection_id": "wi-child",
},
)
set_linked_work_item_id(worker_task, "wi-child")
await store.save_task(worker_task)
# Engineer's turn ended with DONE. Under the two-turn
# worker→review handoff the canonical done transition
# now moves the parent work item to manager review and
# spawns a hidden REPORT card (not the review card)
# against the worker seat. The review card spawns later,
# when the report turn finishes — see
# tests/test_worker_report_handoff.py for that phase.
from opc.core.models import TaskResult
await executor._apply_done_transition(
worker_task,
result=TaskResult(status=TaskStatus.DONE, content="All shipped."),
)
updated_child = await store.get_delegation_work_item("wi-child")
assert updated_child is not None
self.assertEqual(updated_child.phase, WORK_ITEM_STATUS_IN_REVIEW)
# No review card yet — the report card has to run first.
self.assertIsNone(
await store.get_delegation_work_item(
review_work_item_id_for_attempt("wi-child", 1)
),
"review card must wait for the report turn to finish",
)
report_work_item_id = report_work_item_id_for_attempt("wi-child", 1)
report_item = await store.get_delegation_work_item(report_work_item_id)
assert report_item is not None, "report card must be spawned when worker DONE"
self.assertEqual(report_item.kind, "report")
self.assertEqual(report_item.phase, WORK_ITEM_STATUS_TODO)
self.assertEqual(report_item.role_id, "engineer")
self.assertEqual(report_item.seat_id, "seat::team::cto::engineer")
self.assertTrue(report_item.metadata.get("report_execution_work_item"))
self.assertTrue(report_item.metadata.get("work_item_runtime"))
self.assertEqual(report_item.metadata.get("work_item_projection_id"), report_work_item_id)
self.assertEqual(report_item.metadata.get("work_item_turn_type"), "report")
self.assertTrue(report_item.metadata.get("hidden_from_company_kanban"))
self.assertTrue(report_item.metadata.get("skip_work_item_sync"))
self.assertEqual(report_item.metadata.get("current_turn_mode"), "report_required")
self.assertEqual(report_item.metadata.get("report_target_work_item_id"), "wi-child")
materialized = await executor._materialize_work_item_tasks(
[worker_task],
await store.list_delegation_work_items("run-1"),
)
projected_report_task = next(
task
for task in materialized
if linked_work_item_id_for_task(task) == report_work_item_id
)
self.assertEqual(projected_report_task.assigned_to, "engineer")
self.assertEqual(projected_report_task.status, TaskStatus.PENDING)
self.assertTrue(projected_report_task.metadata.get("report_execution_work_item"))
self.assertTrue(projected_report_task.metadata.get("work_item_runtime"))
self.assertEqual(projected_report_task.metadata.get("work_item_projection_id"), report_work_item_id)
self.assertEqual(projected_report_task.metadata.get("work_item_turn_type"), "report")
finally:
await store.close()
async def test_terminal_worker_close_abandons_current_review_work_item(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
store = OPCStore(root / "tasks.db")
await store.initialize()
try:
config = OPCConfig()
config.org.company_profile = "custom"
config.org.roles = [
RoleConfig(id="ceo", name="CEO", responsibility="Set direction.", reports_to="owner"),
RoleConfig(id="cto", name="CTO", responsibility="Lead engineering.", reports_to="ceo"),
RoleConfig(id="engineer", name="Engineer", responsibility="Build features.", reports_to="cto"),
]
org_engine = OrgEngine(config, root)
executor = _build_executor(store, org_engine)
child_work_item = DelegationWorkItem(
work_item_id="wi-child",
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="engineer",
seat_id="seat::team::cto::engineer",
manager_role_id="cto",
manager_seat_id="seat::team::cto::cto",
title="Build feature",
summary="Ship the feature.",
kind="execute",
projection_id="wi-child",
phase=WORK_ITEM_STATUS_IN_REVIEW,
metadata={
"runtime_model": "multi_team_org",
"activation_state": "awaiting_review",
"review_state": "pending_manager",
"review_owner_role_id": "cto",
"review_owner_seat_id": "seat::team::cto::cto",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"review_attempt_count": 1,
},
)
await store.save_delegation_work_item(child_work_item)
worker_task = Task(
id="task-engineer",
title="Build feature",
project_id="proj1",
session_id="session-root",
parent_session_id="session-root",
assigned_to="engineer",
status=TaskStatus.AWAITING_MANAGER_REVIEW,
metadata={
"execution_mode": "company_mode",
"runtime_model": "multi_team_org",
"work_item_runtime": True,
"delegation_run_id": "run-1",
"delegation_team_id": "team::cto",
"delegation_seat_id": "seat::team::cto::engineer",
"work_item_role_id": "engineer",
"manager_role_id": "cto",
"manager_seat_id": "seat::team::cto::cto",
"work_item_projection_id": "wi-child",
},
)
set_linked_work_item_id(worker_task, "wi-child")
await store.save_task(worker_task)
await store.link_work_item_runtime_task("wi-child", worker_task.id)
review_work_item_id = review_work_item_id_for_attempt("wi-child", 1)
review_item = DelegationWorkItem(
work_item_id=review_work_item_id,
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="cto",
seat_id="seat::team::cto::cto",
parent_work_item_id="wi-child",
title="Review: Build feature",
kind="review",
projection_id=review_work_item_id,
phase=WORK_ITEM_STATUS_IN_PROGRESS,
manager_role_id="cto",
manager_seat_id="seat::team::cto::cto",
metadata={
"work_item_runtime": True,
"review_task": True,
"review_execution_work_item": True,
"review_execution_state": "active",
"runtime_model": "multi_team_org",
"review_target_work_item_id": "wi-child",
"hidden_from_company_kanban": True,
"skip_work_item_sync": True,
},
)
await store.save_delegation_work_item(review_item)
# Simulate a worker terminal failure while a review attempt
# is still in flight. The hidden review work item must be
# abandoned because no verdict will arrive.
worker_task.status = TaskStatus.FAILED
worker_task.metadata = dict(worker_task.metadata)
child_work_item.phase = Phase.FAILED
child_work_item.metadata = {
**dict(child_work_item.metadata or {}),
"activation_state": "failed",
}
await store.save_delegation_work_item(child_work_item)
await executor._close_review_work_item_for_work_item("wi-child", outcome=Phase.FAILED.value)
refreshed_review = await store.get_delegation_work_item(review_work_item_id)
assert refreshed_review is not None
self.assertEqual(refreshed_review.phase, Phase.CANCELLED)
self.assertEqual(refreshed_review.metadata.get("review_work_item_outcome"), Phase.FAILED.value)
finally:
await store.close()
class ReviewWorkItemRoutingTests(unittest.TestCase):
def test_review_work_item_enqueue_goes_to_front_of_manager_queue(self) -> None:
runtime = CompanyRuntime(
org_engine=MagicMock(),
communication=None,
store=None,
)
manager_task = Task(
id="regular-task",
title="Dispatch",
assigned_to="cto",
project_id="proj1",
session_id="scope-1",
parent_session_id="scope-1",
status=TaskStatus.PENDING,
metadata={
"delegation_seat_id": "seat::team::cto::cto",
"work_item_role_id": "cto",
"runtime_model": "multi_team_org",
},
)
review_task = Task(
id="task-review",
title="Review: build feature",
assigned_to="cto",
project_id="proj1",
session_id="scope-1",
parent_session_id="scope-1",
status=TaskStatus.PENDING,
metadata={
"delegation_seat_id": "seat::team::cto::cto",
"work_item_role_id": "cto",
"review_task": True,
"review_execution_work_item": True,
"runtime_model": "multi_team_org",
"review_target_work_item_id": "wi-child",
},
)
set_linked_work_item_id(review_task, "review::wi-child")
review_item = DelegationWorkItem(
work_item_id="review::wi-child",
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="cto",
seat_id="seat::team::cto::cto",
parent_work_item_id="wi-child",
title="Review: build feature",
kind="review",
projection_id="review::wi-child",
phase=WORK_ITEM_STATUS_TODO,
metadata={
"runtime_model": "multi_team_org",
"session_scope_id": "scope-1",
"work_kind": "review",
"review_execution_work_item": True,
"review_execution_state": "pending",
"review_target_work_item_id": "wi-child",
"hidden_from_company_kanban": True,
},
)
runtime.enqueue_runnable_tasks([manager_task])
runtime.enqueue_runnable_work_items(
[review_item],
task_by_work_item_id={"review::wi-child": review_task},
)
queue_key = runtime._queue_key_for_task(manager_task)
queue = runtime.role_queues.get(queue_key)
assert queue is not None
self.assertEqual(queue[0], "review-work-item::review::wi-child")
popped = CompanyRuntime._pop_next_queue_entry(queue)
self.assertEqual(popped, "review-work-item::review::wi-child")
def test_soft_wake_allows_blocked_manager_to_claim_review_work_item(self) -> None:
runtime = CompanyRuntime(
org_engine=MagicMock(),
communication=None,
store=None,
)
session = CompanyMemberSession(
member_session_id="member::cto",
role_id="cto",
employee_id="cto-default",
seat_id="seat::team::cto::cto",
status="blocked",
current_task_id="cto-own-task",
focused_work_item_id="wi-cto-dispatch",
metadata={
"seat_id": "seat::team::cto::cto",
"session_scope_id": "",
},
)
runtime.member_sessions[session.member_session_id] = session
review_work_item_id = "review::wi-child"
runtime.role_queues[runtime._queue_key_for_session(session)].append(
f"review-work-item::{review_work_item_id}"
)
queue = runtime.role_queues.get(runtime._queue_key_for_session(session))
self.assertIsNotNone(queue)
self.assertEqual(queue[0], f"review-work-item::{review_work_item_id}")
can_soft_wake = (
session.status == "blocked"
and queue is not None
and any(entry.startswith("review-work-item::") for entry in queue)
)
self.assertTrue(can_soft_wake)
class ReviewWorkItemClaimE2ETests(unittest.IsolatedAsyncioTestCase):
async def test_idle_manager_seat_claims_spawned_review_work_item(self) -> None:
"""A review work item spawned on in_review must actually be claimed
by the manager seat session via the normal runtime path.
"""
runtime = CompanyRuntime(
org_engine=MagicMock(),
communication=None,
store=None,
)
# Idle manager session for the CTO-as-team-manager seat.
manager_session = CompanyMemberSession(
member_session_id="member::cto::manager",
role_id="cto",
employee_id="cto-default",
seat_id="seat::team::cto::cto",
status="idle",
metadata={
"seat_id": "seat::team::cto::cto",
"session_scope_id": "scope-1",
},
)
runtime.member_sessions[manager_session.member_session_id] = manager_session
child_work_item = DelegationWorkItem(
work_item_id="wi-child",
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="engineer",
seat_id="seat::team::cto::engineer",
title="Build feature",
kind="execute",
projection_id="wi-child",
phase=WORK_ITEM_STATUS_IN_REVIEW,
manager_role_id="cto",
manager_seat_id="seat::team::cto::cto",
metadata={
"runtime_model": "multi_team_org",
"activation_state": "awaiting_review",
"review_state": "pending_manager",
"review_owner_role_id": "cto",
"review_owner_seat_id": "seat::team::cto::cto",
},
)
review_task = Task(
id="task-review",
title="Review: build feature",
assigned_to="cto",
project_id="proj1",
session_id="scope-1",
parent_session_id="scope-1",
status=TaskStatus.PENDING,
metadata={
"delegation_seat_id": "seat::team::cto::cto",
"work_item_role_id": "cto",
"review_task": True,
"review_execution_work_item": True,
"runtime_model": "multi_team_org",
"review_target_work_item_id": "wi-child",
},
)
set_linked_work_item_id(review_task, "review::wi-child")
review_item = DelegationWorkItem(
work_item_id="review::wi-child",
run_id="run-1",
cell_id="team::cto",
team_id="team::cto",
role_id="cto",
seat_id="seat::team::cto::cto",
parent_work_item_id="wi-child",
title="Review: build feature",
kind="review",
projection_id="review::wi-child",
phase=WORK_ITEM_STATUS_TODO,
manager_role_id="cto",
manager_seat_id="seat::team::cto::cto",
metadata={
"runtime_model": "multi_team_org",
"session_scope_id": "scope-1",
"work_kind": "review",
"review_execution_work_item": True,
"review_execution_state": "pending",
"review_target_work_item_id": "wi-child",
"hidden_from_company_kanban": True,
},
)
runtime.enqueue_runnable_work_items(
[review_item],
task_by_work_item_id={"review::wi-child": review_task},
)
claims = await runtime.claim_runnable_tasks(
[review_task],
work_items=[child_work_item, review_item],
)
self.assertEqual(len(claims), 1, "idle manager seat must claim the review work item")
claimed_session, claimed_task = claims[0]
self.assertEqual(claimed_session.member_session_id, manager_session.member_session_id)
self.assertEqual(claimed_task.id, "task-review")
self.assertEqual(claimed_session.status, "running")
self.assertEqual(claimed_session.current_task_id, "task-review")
if __name__ == "__main__":
unittest.main()