Initial commit

This commit is contained in:
LZH-YS1998
2026-07-01 17:56:31 +08:00
commit d78931979d
731 changed files with 311088 additions and 0 deletions
@@ -0,0 +1,621 @@
"""Regression tests for Fix 1 (empty-verdict rework loop) and Fix 3
(dep-frontier refresh on all terminal child transitions).
Both bugs were observed live in project ``new16`` session ``app12``
(session_id ``875bdbde-7b97-4239-8e32-f2e52c96b289``), 2026-04-20:
Fix 1 — reviewer produced
``{"label":"reject","summary":"reject","blocking_issues":[],"followups":[]}``
for 56 consecutive rounds; each rework turn had no actionable guidance
and reproduced the same output, burning the full ``max_review_reworks``
budget before forced escalation.
Fix 3 — cto parent ``cdb248d8`` sat in WAITING_FOR_CHILDREN for 13+
minutes with the claim held by an idle session. Cause: refresh only
fired on the APPROVED-verdict branch of ``_finalize_review_work_item``
(line 5480). When children escalated to AWAITING_HUMAN or got
CANCELLED/FAILED, the parent was never re-evaluated.
"""
from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
from opc.core.models import (
DelegationWorkItem,
Phase,
Task,
TaskResult,
TaskStatus,
)
from opc.database.store import OPCStore
from opc.layer2_organization import phase_hooks # noqa: F401 (register hooks)
from opc.layer2_organization.company_mode import CompanyWorkItemExecutor
from opc.layer2_organization.work_item_links import set_linked_work_item_id
from opc.layer2_organization.work_item_transition import refresh_dependents_for_run
# ── Fix 1 / Fix 4 verdict-shape tests removed: runtime no longer makes
# shape-based decisions about verdicts. The reviewer agent's verdict is
# applied mechanically. See tests/test_verdict_parse_retry.py for the
# only remaining structural fallback (verdict cannot be parsed at all).
# ── Fix 3: refresh_dependents_for_run + hook wiring ──────────────────────
def _make_work_item(
*,
work_item_id: str,
run_id: str,
phase: Phase,
role_id: str = "w",
dependency_ids: list[str] | None = None,
claimed_by: str = "",
team_instance_id: str = "ti",
team_id: str = "team::x",
parent_work_item_id: str | None = None,
metadata: dict | None = None,
) -> DelegationWorkItem:
metadata = dict(metadata or {})
if dependency_ids:
metadata["dependency_work_item_ids"] = list(dependency_ids)
return DelegationWorkItem(
work_item_id=work_item_id,
run_id=run_id,
cell_id="c",
team_instance_id=team_instance_id,
team_id=team_id,
role_id=role_id,
seat_id=f"seat::{role_id}",
parent_work_item_id=parent_work_item_id,
manager_role_id="m",
manager_seat_id="ms",
title=f"item-{work_item_id}",
phase=phase,
claimed_by_role_runtime_session_id=claimed_by,
claimed_by_seat_id=f"seat::{role_id}" if claimed_by else "",
metadata=metadata,
)
class RefreshDependentsForRunTests(unittest.IsolatedAsyncioTestCase):
"""Integration tests against a real OPCStore. Each test constructs a
parent + children, transitions children, and asserts the parent's
phase + claim after the refresh pass."""
async def asyncSetUp(self) -> None:
self._tmpdir = tempfile.TemporaryDirectory()
self.store = OPCStore(db_path=Path(self._tmpdir.name) / "store.db")
await self.store.initialize()
async def asyncTearDown(self) -> None:
await self.store.close()
self._tmpdir.cleanup()
async def _save(self, *items: DelegationWorkItem) -> None:
for it in items:
await self.store.save_delegation_work_item(it)
def _executor(self) -> CompanyWorkItemExecutor:
async def execute_task(task: Task) -> TaskResult:
return TaskResult(status=task.status, content="", artifacts={})
return CompanyWorkItemExecutor(
org_engine=SimpleNamespace(),
communication=SimpleNamespace(on_kanban_changed=None, on_work_items_created=None),
approval_engine=SimpleNamespace(),
memory=None,
execute_task=execute_task,
save_task=self.store.save_task,
store=self.store,
)
async def test_waiting_dependency_work_item_releases_when_dependencies_already_approved(self) -> None:
dep_a = _make_work_item(work_item_id="dep-a", run_id="run-follow", phase=Phase.APPROVED)
dep_b = _make_work_item(work_item_id="dep-b", run_id="run-follow", phase=Phase.APPROVED)
follow = _make_work_item(
work_item_id="follow",
run_id="run-follow",
phase=Phase.WAITING_DEPENDENCIES,
role_id="report_producer",
dependency_ids=["dep-a", "dep-b"],
)
await self._save(dep_a, dep_b, follow)
task = Task(
id="follow-task",
title="Follow-up",
project_id="proj1",
assigned_to="report_producer",
status=TaskStatus.BLOCKED,
metadata={"work_item_projection_id": "follow", "work_item_turn_type": "execute"},
)
set_linked_work_item_id(task, "follow")
await self.store.save_task(task)
await self.store.link_work_item_runtime_task("follow", task.id)
executor = self._executor()
work_items = await executor._refresh_ready_work_items(
await self.store.list_delegation_work_items("run-follow"),
tasks=[task],
)
await executor._sync_task_projection_from_work_items([task], work_items)
refreshed = await self.store.get_delegation_work_item("follow")
refreshed_task = await self.store.get_task(task.id)
self.assertEqual(refreshed.phase, Phase.READY)
self.assertEqual(refreshed_task.status, TaskStatus.PENDING)
async def test_waiting_dependency_work_item_stays_waiting_until_hard_dependencies_approve(self) -> None:
dep_a = _make_work_item(work_item_id="dep-a", run_id="run-wait", phase=Phase.APPROVED)
dep_b = _make_work_item(work_item_id="dep-b", run_id="run-wait", phase=Phase.RUNNING)
follow = _make_work_item(
work_item_id="follow",
run_id="run-wait",
phase=Phase.WAITING_DEPENDENCIES,
role_id="report_producer",
dependency_ids=["dep-a", "dep-b"],
)
await self._save(dep_a, dep_b, follow)
executor = self._executor()
await executor._refresh_ready_work_items(
await self.store.list_delegation_work_items("run-wait"),
tasks=[],
)
refreshed = await self.store.get_delegation_work_item("follow")
self.assertEqual(refreshed.phase, Phase.WAITING_DEPENDENCIES)
self.assertEqual(refreshed.metadata["waiting_on_work_item_ids"], ["dep-b"])
async def test_materialized_follow_up_refreshes_already_approved_dependencies(self) -> None:
parent = _make_work_item(
work_item_id="parent",
run_id="run-materialize",
phase=Phase.RUNNING,
role_id="chief_analyst",
)
dep_a = _make_work_item(work_item_id="dep-a", run_id="run-materialize", phase=Phase.APPROVED)
dep_b = _make_work_item(work_item_id="dep-b", run_id="run-materialize", phase=Phase.APPROVED)
await self._save(parent, dep_a, dep_b)
manager_task = Task(
id="manager-task",
title="Chief Analyst Intake",
project_id="proj1",
assigned_to="chief_analyst",
status=TaskStatus.RUNNING,
metadata={
"work_item_runtime": True,
"work_item_runtime_version": 1,
"runtime_model": "multi_team_org",
"delegation_run_id": "run-materialize",
"delegation_seat_id": "seat::chief_analyst",
"runtime_topology": {
"seats": [
{
"role_id": "report_producer",
"seat_id": "seat::report_producer",
"team_id": "team::report",
"team_instance_id": "team-instance::report",
"seat_state_id": "seat-state::report",
}
]
},
},
)
set_linked_work_item_id(manager_task, "parent")
await self.store.save_task(manager_task)
await self.store.link_work_item_runtime_task("parent", manager_task.id)
executor = self._executor()
executor._active_tasks = [manager_task]
created = await executor._materialize_follow_up_work_items(
manager_task,
TaskResult(
status=TaskStatus.DONE,
content="Create a PPT deck.",
artifacts={
"follow_up_actions": [
{
"action": "delegate_followup",
"target_role_id": "report_producer",
"title": "Generate PPT deck",
"summary": "Create a PPT with image2 visuals.",
"depends_on_work_item_ids": ["dep-a", "dep-b"],
}
]
},
),
)
self.assertEqual(len(created), 1)
follow = await self.store.get_delegation_work_item(created[0])
parent_after = await self.store.get_delegation_work_item("parent")
self.assertEqual(follow.phase, Phase.READY)
self.assertEqual(parent_after.phase, Phase.WAITING_FOR_CHILDREN)
async def test_parent_wakes_when_last_child_approved(self) -> None:
"""The canonical app12 fix: parent in WAITING_FOR_CHILDREN with a
stale claim unblocks to RUNNING and releases the claim when all
children reach APPROVED."""
parent = _make_work_item(
work_item_id="parent",
run_id="run-a",
phase=Phase.RUNNING, # will move to WAITING_FOR_CHILDREN via refresh
role_id="cto",
dependency_ids=["child-1", "child-2"],
claimed_by="role-runtime::run-a::seat::team::ceo::cto",
)
child1 = _make_work_item(
work_item_id="child-1",
run_id="run-a",
phase=Phase.APPROVED,
parent_work_item_id="parent",
)
child2 = _make_work_item(
work_item_id="child-2",
run_id="run-a",
phase=Phase.RUNNING, # still in progress
parent_work_item_id="parent",
)
await self._save(parent, child1, child2)
# First pass: one child not approved → parent transitions
# RUNNING → WAITING_FOR_CHILDREN.
await refresh_dependents_for_run(self.store, run_id="run-a")
after = await self.store.get_delegation_work_item("parent")
self.assertEqual(after.phase, Phase.WAITING_FOR_CHILDREN)
self.assertEqual(
after.claimed_by_role_runtime_session_id,
"role-runtime::run-a::seat::team::ceo::cto",
)
# Second child completes → hook fires refresh → parent unblocks.
await self.store.update_delegation_work_item(
"child-2", phase=Phase.APPROVED
)
after = await self.store.get_delegation_work_item("parent")
self.assertEqual(after.phase, Phase.RUNNING)
self.assertEqual(after.claimed_by_role_runtime_session_id, "")
self.assertEqual(after.claimed_by_seat_id, "")
async def test_final_delivery_parent_resumes_as_delivery_after_followup_child_approved(self) -> None:
"""A final delivery card reopened by owner follow-up parks while its
new child runs, then resumes as a delivery/synthesis turn when that
child is approved."""
parent = _make_work_item(
work_item_id="delivery-parent",
run_id="run-delivery-followup",
phase=Phase.WAITING_FOR_CHILDREN,
role_id="ceo",
dependency_ids=["old-research", "new-ppt"],
claimed_by="role-runtime::run-delivery-followup::ceo",
metadata={
"work_kind": "delivery",
"delegation_turn_kind": "delivery",
"work_item_turn_type": "deliver",
"current_turn_mode": "dispatch_required",
"feedback_scope": "final",
"authoritative_output": True,
"user_visible": True,
"requires_user_feedback": True,
},
)
old_research = _make_work_item(
work_item_id="old-research",
run_id="run-delivery-followup",
phase=Phase.APPROVED,
parent_work_item_id="delivery-parent",
)
new_ppt = _make_work_item(
work_item_id="new-ppt",
run_id="run-delivery-followup",
phase=Phase.RUNNING,
parent_work_item_id="delivery-parent",
)
await self._save(parent, old_research, new_ppt)
await self.store.update_delegation_work_item("new-ppt", phase=Phase.APPROVED)
after = await self.store.get_delegation_work_item("delivery-parent")
self.assertEqual(after.phase, Phase.RUNNING)
self.assertEqual(after.claimed_by_role_runtime_session_id, "")
self.assertEqual(after.claimed_by_seat_id, "")
self.assertEqual(after.metadata.get("work_kind"), "delivery")
self.assertEqual(after.metadata.get("delegation_turn_kind"), "delivery")
self.assertEqual(after.metadata.get("work_item_turn_type"), "deliver")
self.assertEqual(after.metadata.get("current_turn_mode"), "deliver_required")
self.assertEqual(after.metadata.get("waiting_on_work_item_ids"), [])
async def test_child_cancelled_triggers_parent_refresh(self) -> None:
"""Fix 3 core: non-APPROVED terminal (CANCELLED) must still fire
the refresh hook. Before Fix 3, only APPROVED did."""
parent = _make_work_item(
work_item_id="parent-c",
run_id="run-b",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["child-a", "child-b"],
claimed_by="claim-x",
)
child_a = _make_work_item(
work_item_id="child-a",
run_id="run-b",
phase=Phase.APPROVED,
)
child_b = _make_work_item(
work_item_id="child-b",
run_id="run-b",
phase=Phase.RUNNING,
)
await self._save(parent, child_a, child_b)
await self.store.update_delegation_work_item(
"child-b", phase=Phase.CANCELLED
)
# Parent still WAITING_FOR_CHILDREN (not all approved), but the
# hook ran — verify by checking waiting_on_work_item_ids.
after = await self.store.get_delegation_work_item("parent-c")
self.assertEqual(after.phase, Phase.WAITING_FOR_CHILDREN)
# Waiting list should reflect the deps (hook wrote it).
self.assertEqual(
list(after.metadata.get("waiting_on_work_item_ids", [])),
["child-a", "child-b"],
)
async def test_manager_deleted_child_is_pruned_from_parent_dependencies(self) -> None:
"""Manager-deleted hidden children are graph edits, not hard blockers.
A normal CANCELLED dependency still blocks (covered above). This case
mirrors a recovery dispatch: an obsolete child was cancelled/hidden by
the manager, a replacement child finished, and the parent should not
wait forever on the stale id.
"""
parent = _make_work_item(
work_item_id="parent-prune",
run_id="run-prune",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["approved-child", "deleted-child", "replacement-child"],
claimed_by="stale-parent-claim",
)
approved_child = _make_work_item(
work_item_id="approved-child",
run_id="run-prune",
phase=Phase.APPROVED,
parent_work_item_id="parent-prune",
)
deleted_child = _make_work_item(
work_item_id="deleted-child",
run_id="run-prune",
phase=Phase.CANCELLED,
parent_work_item_id="parent-prune",
)
deleted_child.metadata.update(
{
"deleted_by_manager_tool": True,
"hidden_from_company_kanban": True,
"upstream_visibility": "hidden",
}
)
replacement_child = _make_work_item(
work_item_id="replacement-child",
run_id="run-prune",
phase=Phase.APPROVED,
parent_work_item_id="parent-prune",
)
await self._save(parent, approved_child, deleted_child, replacement_child)
changed = await refresh_dependents_for_run(self.store, run_id="run-prune")
self.assertTrue(changed)
after = await self.store.get_delegation_work_item("parent-prune")
self.assertEqual(after.phase, Phase.RUNNING)
self.assertEqual(after.claimed_by_role_runtime_session_id, "")
self.assertEqual(
list(after.metadata.get("dependency_work_item_ids", [])),
["approved-child", "replacement-child"],
)
self.assertEqual(after.metadata.get("waiting_on_work_item_ids"), [])
self.assertIn("deleted-child", after.metadata.get("pruned_dependency_work_item_ids", []))
async def test_child_awaiting_human_triggers_refresh(self) -> None:
"""Fix 3 regression: when max_review_reworks escalates a child to
AWAITING_HUMAN, the hook must fire so the parent's dep metadata
is updated. Previously this transition was silent and the parent
drifted into a zombie state."""
parent = _make_work_item(
work_item_id="parent-h",
run_id="run-c",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["child-h1", "child-h2"],
claimed_by="claim-z",
)
child_h1 = _make_work_item(
work_item_id="child-h1",
run_id="run-c",
phase=Phase.APPROVED,
)
child_h2 = _make_work_item(
work_item_id="child-h2",
run_id="run-c",
phase=Phase.AWAITING_MANAGER_REVIEW,
)
await self._save(parent, child_h1, child_h2)
await self.store.update_delegation_work_item(
"child-h2", phase=Phase.AWAITING_HUMAN
)
# Parent stays waiting (AWAITING_HUMAN is not APPROVED), but the
# hook ran. Next step: a human approves → parent should unblock.
await self.store.update_delegation_work_item(
"child-h2", phase=Phase.APPROVED
)
after = await self.store.get_delegation_work_item("parent-h")
self.assertEqual(after.phase, Phase.RUNNING)
self.assertEqual(after.claimed_by_role_runtime_session_id, "")
async def test_refresh_is_reentrancy_safe(self) -> None:
"""The hook fires during a write; the write itself can be
triggered by the hook's update (parent phase change). Verify we
don't recurse forever — 3 levels deep should converge."""
grandparent = _make_work_item(
work_item_id="gp",
run_id="run-d",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["parent-d"],
claimed_by="gp-claim",
)
parent_d = _make_work_item(
work_item_id="parent-d",
run_id="run-d",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["leaf"],
claimed_by="p-claim",
parent_work_item_id="gp",
)
leaf = _make_work_item(
work_item_id="leaf",
run_id="run-d",
phase=Phase.RUNNING,
parent_work_item_id="parent-d",
)
await self._save(grandparent, parent_d, leaf)
await self.store.update_delegation_work_item(
"leaf", phase=Phase.APPROVED
)
# One approval should cascade: leaf approved → parent_d runs →
# parent_d needs to hit APPROVED for grandparent to unblock.
# parent_d will run but not be approved in this test, so
# grandparent should still be WAITING_FOR_CHILDREN but its dep
# waiting_on_work_item_ids should reflect the current state.
p_after = await self.store.get_delegation_work_item("parent-d")
gp_after = await self.store.get_delegation_work_item("gp")
self.assertEqual(p_after.phase, Phase.RUNNING)
self.assertEqual(p_after.claimed_by_role_runtime_session_id, "")
self.assertEqual(gp_after.phase, Phase.WAITING_FOR_CHILDREN)
async def test_refresh_no_change_returns_false(self) -> None:
"""Calling refresh with no eligible work items is a no-op."""
lonely = _make_work_item(
work_item_id="solo",
run_id="run-e",
phase=Phase.RUNNING,
# no dependency_ids → nothing to refresh
)
await self._save(lonely)
changed = await refresh_dependents_for_run(
self.store, run_id="run-e"
)
self.assertFalse(changed)
# ── RC3 Step-1 fixes: READY_FOR_REWORK triggers refresh + broadened claim release ──
async def test_child_ready_for_rework_bubbles_refresh(self) -> None:
"""Step-1 core: reviewer rejects child with rework → child flips to
READY_FOR_REWORK. Before the fix, this transition was not in
_DEPENDENT_REFRESH_TARGETS so the parent's waiting_on_work_item_ids
stayed stale. Verify the hook now fires and parent's frontier
reflects the rework child."""
parent = _make_work_item(
work_item_id="parent-r",
run_id="run-r",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["child-r1", "child-r2"],
claimed_by="stale-claim",
)
child_r1 = _make_work_item(
work_item_id="child-r1",
run_id="run-r",
phase=Phase.APPROVED,
)
child_r2 = _make_work_item(
work_item_id="child-r2",
run_id="run-r",
phase=Phase.AWAITING_MANAGER_REVIEW,
)
await self._save(parent, child_r1, child_r2)
# Reviewer returns rework verdict → child flips to READY_FOR_REWORK.
# Before Step 1 fix: parent frontier untouched, claim stale forever.
# After fix: hook runs, waiting_on_work_item_ids is rewritten.
await self.store.update_delegation_work_item(
"child-r2", phase=Phase.READY_FOR_REWORK
)
after = await self.store.get_delegation_work_item("parent-r")
self.assertEqual(after.phase, Phase.WAITING_FOR_CHILDREN)
self.assertEqual(
list(after.metadata.get("waiting_on_work_item_ids", [])),
["child-r1", "child-r2"],
)
async def test_parent_wakes_from_waiting_with_stale_claim_released(self) -> None:
"""Step-1 broadened claim release: parent in WAITING_FOR_CHILDREN
exits toward ANY non-terminal phase → stale claim is cleared.
Earlier, claim release only fired when target==RUNNING and
all_approved; if the parent instead went to e.g. READY through
dependency regression propagation, claim stayed held.
This test drives the WAITING_FOR_CHILDREN → RUNNING path (the
canonical case), but the new condition also covers other wakes.
"""
parent = _make_work_item(
work_item_id="parent-w",
run_id="run-w",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["child-w"],
claimed_by="idle-session-123",
)
child_w = _make_work_item(
work_item_id="child-w",
run_id="run-w",
phase=Phase.RUNNING,
)
await self._save(parent, child_w)
await self.store.update_delegation_work_item(
"child-w", phase=Phase.APPROVED
)
after = await self.store.get_delegation_work_item("parent-w")
self.assertEqual(after.phase, Phase.RUNNING)
self.assertEqual(after.claimed_by_role_runtime_session_id, "")
self.assertEqual(after.claimed_by_seat_id, "")
async def test_parent_terminal_keeps_claim_as_audit(self) -> None:
"""Step-1 broadened claim release explicitly excludes DONE_PHASES:
a WAITING_FOR_CHILDREN parent that transitions to CANCELLED/FAILED
MUST retain the claim as a "last executor" audit record.
Force this path by simulating a higher-level cancel that flips the
parent directly to CANCELLED via the store.
"""
parent = _make_work_item(
work_item_id="parent-t",
run_id="run-t",
phase=Phase.WAITING_FOR_CHILDREN,
dependency_ids=["child-t"],
claimed_by="historical-session",
)
child_t = _make_work_item(
work_item_id="child-t",
run_id="run-t",
phase=Phase.RUNNING,
)
await self._save(parent, child_t)
# Directly cancel the parent (as if a higher-level cancel cascaded).
await self.store.update_delegation_work_item(
"parent-t", phase=Phase.CANCELLED
)
after = await self.store.get_delegation_work_item("parent-t")
self.assertEqual(after.phase, Phase.CANCELLED)
# Claim preserved — it is a historical audit record for terminal items.
self.assertEqual(
after.claimed_by_role_runtime_session_id, "historical-session"
)
if __name__ == "__main__":
unittest.main()