"""Diagnostics for company WorkItem runtime Task projections.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Iterable, Mapping from opc.core.models import DelegationWorkItem, Phase, Task from opc.layer2_organization.phase import ( DONE_PHASES, is_report_execution_work_item_metadata, is_review_execution_work_item_metadata, should_hide_work_item_from_company_kanban, ) from opc.layer2_organization.metadata_ownership import validate_metadata_ownership from opc.layer2_organization.work_item_identity import ( WORK_ITEM_TURN_TYPE_KEY, canonical_work_item_turn_type_for_kind, normalize_work_item_turn_type, projection_id_for_work_item, work_item_projection_id_from_metadata, ) from opc.layer2_organization.work_item_links import linked_work_item_id_for_task from opc.layer2_organization.work_item_runtime import is_work_item_runtime_metadata WORK_ITEM_RUNTIME_INVARIANT_EVENT_TYPE = "work_item_runtime_invariant_violation" _RUNTIME_MODELS = {"multi_team_org"} _AUXILIARY_TURN_KINDS = {"review", "report", "followup", "follow_up", "delivery", "deliver"} _HIDDEN_RUNNABLE_TURN_KINDS = _AUXILIARY_TURN_KINDS | {"aggregate", "synthesize"} @dataclass(frozen=True) class WorkItemRuntimeInvariantIssue: code: str severity: str run_id: str = "" work_item_id: str = "" runtime_task_id: str = "" projection_id: str = "" message: str = "" details: dict[str, Any] = field(default_factory=dict) def fingerprint(self) -> tuple[str, str, str, str]: return ( str(self.code or "").strip(), str(self.work_item_id or "").strip(), str(self.runtime_task_id or "").strip(), str(self.projection_id or "").strip(), ) def to_event_payload(self) -> dict[str, Any]: payload = { "code": self.code, "severity": self.severity, "run_id": self.run_id, "work_item_id": self.work_item_id, "runtime_task_id": self.runtime_task_id, "projection_id": self.projection_id, "message": self.message, } if self.details: payload["details"] = dict(self.details) return payload def _clean(value: Any) -> str: return str(value or "").strip() def _lower(value: Any) -> str: return _clean(value).lower() def _phase_value(value: Any) -> str: if isinstance(value, Phase): return value.value return _lower(value) def _is_done_phase(value: Any) -> bool: if isinstance(value, Phase): return value in DONE_PHASES normalized = _lower(value) return normalized in {phase.value for phase in DONE_PHASES} def _runtime_model(metadata: Mapping[str, Any]) -> str: return _lower(metadata.get("runtime_model") or metadata.get("execution_model")) def _turn_kind(metadata: Mapping[str, Any], *, fallback: str = "") -> str: turn = normalize_work_item_turn_type(metadata.get(WORK_ITEM_TURN_TYPE_KEY), fallback="") if turn: return turn for key in ("work_kind", "delegation_turn_kind"): owner_kind = canonical_work_item_turn_type_for_kind(metadata.get(key), fallback="") if owner_kind: return owner_kind return canonical_work_item_turn_type_for_kind(fallback, fallback="") def _task_projection_id(task: Task | None) -> str: if task is None: return "" return work_item_projection_id_from_metadata(dict(task.metadata or {}), fallback=_clean(getattr(task, "id", ""))) def _issue( code: str, severity: str, *, task: Task | None = None, work_item: DelegationWorkItem | None = None, work_item_id: str = "", projection_id: str = "", message: str, details: Mapping[str, Any] | None = None, ) -> WorkItemRuntimeInvariantIssue: task_metadata = dict(getattr(task, "metadata", {}) or {}) item_metadata = dict(getattr(work_item, "metadata", {}) or {}) wid = ( _clean(work_item_id) or _clean(getattr(work_item, "work_item_id", "")) or linked_work_item_id_for_task(task) ) projection = _clean(projection_id) or _task_projection_id(task) if not projection and work_item is not None: projection = projection_id_for_work_item(work_item) return WorkItemRuntimeInvariantIssue( code=code, severity=severity, run_id=( _clean(getattr(work_item, "run_id", "")) or _clean(item_metadata.get("delegation_run_id")) or _clean(task_metadata.get("delegation_run_id")) ), work_item_id=wid, runtime_task_id=_clean(getattr(task, "id", "")), projection_id=projection, message=message, details=dict(details or {}), ) def is_company_runtime_projection_task(task: Task | None) -> bool: """Return true for company WorkItem runtime projection Tasks. The predicate intentionally does not read legacy owner metadata. A task is a projection only when it carries the canonical runtime marker and the canonical projection identity. """ if task is None: return False metadata = dict(getattr(task, "metadata", {}) or {}) if not is_work_item_runtime_metadata(metadata): return False if bool(metadata.get("synthetic_inbox_turn") or metadata.get("synthetic_company_inbox")): return False runtime_model = _runtime_model(metadata) if runtime_model and runtime_model not in _RUNTIME_MODELS: return False return bool(work_item_projection_id_from_metadata(metadata, fallback="")) def _expected_work_kind(work_item: DelegationWorkItem | None) -> str: if work_item is None: return "" metadata = dict(work_item.metadata or {}) return _turn_kind(metadata, fallback=_clean(getattr(work_item, "kind", "")) or "execute") or "execute" def _expected_role_id(work_item: DelegationWorkItem | None) -> str: return _clean(getattr(work_item, "role_id", "")) if work_item is not None else "" def _expected_seat_id(work_item: DelegationWorkItem | None) -> str: if work_item is None: return "" return _clean(getattr(work_item, "seat_id", "")) or _clean(dict(work_item.metadata or {}).get("seat_id")) def _expected_turn_mode(work_item: DelegationWorkItem | None) -> str: if work_item is None: return "" return _clean(dict(work_item.metadata or {}).get("current_turn_mode")) def validate_work_item_runtime_projection( task: Task, work_item: DelegationWorkItem | None, *, work_item_by_id: Mapping[str, DelegationWorkItem] | None = None, ) -> list[WorkItemRuntimeInvariantIssue]: """Validate that a projected runtime Task is backed by a WorkItem link. This function is read-only. It reports mismatches; it never repairs links and never uses legacy Task metadata as an owner source. """ if not is_company_runtime_projection_task(task): return [] task_metadata = dict(getattr(task, "metadata", {}) or {}) item_metadata = dict(getattr(work_item, "metadata", {}) or {}) if work_item is not None else {} linked_work_item_id = linked_work_item_id_for_task(task) projection_id = _task_projection_id(task) issues: list[WorkItemRuntimeInvariantIssue] = [] if not linked_work_item_id: issues.append( _issue( "missing_link", "error", task=task, work_item=work_item, projection_id=projection_id, message="Company runtime projection task is not hydrated from work_item_runtime_links.", ) ) if work_item is None: issues.append( _issue( "missing_work_item", "error", task=task, work_item_id=linked_work_item_id, projection_id=projection_id, message="Company runtime projection task link does not resolve to a WorkItem.", ) ) return issues expected_work_item_id = _clean(getattr(work_item, "work_item_id", "")) if linked_work_item_id and expected_work_item_id and linked_work_item_id != expected_work_item_id: issues.append( _issue( "link_work_item_mismatch", "error", task=task, work_item=work_item, work_item_id=linked_work_item_id, projection_id=projection_id, message="Runtime task link points at a different WorkItem than the supplied WorkItem.", details={"expected_work_item_id": expected_work_item_id, "linked_work_item_id": linked_work_item_id}, ) ) expected_projection_id = projection_id_for_work_item(work_item) if projection_id and expected_projection_id and projection_id != expected_projection_id: issues.append( _issue( "projection_mismatch", "warning", task=task, work_item=work_item, projection_id=projection_id, message="Runtime task projection id differs from the WorkItem projection id.", details={"expected_projection_id": expected_projection_id, "task_projection_id": projection_id}, ) ) expected_kind = _expected_work_kind(work_item) task_kind = _turn_kind(task_metadata, fallback=_lower(task_metadata.get("work_kind")) or expected_kind) if expected_kind and task_kind and task_kind != expected_kind: issues.append( _issue( "work_kind_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Runtime task turn kind differs from the WorkItem turn kind.", details={"expected_work_kind": expected_kind, "task_work_kind": task_kind}, ) ) expected_role_id = _expected_role_id(work_item) task_role_id = _clean(getattr(task, "assigned_to", "")) or _clean(task_metadata.get("work_item_role_id")) if expected_role_id and task_role_id and task_role_id != expected_role_id: issues.append( _issue( "owner_role_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Runtime task owner role differs from the WorkItem role.", details={"expected_role_id": expected_role_id, "task_role_id": task_role_id}, ) ) expected_seat_id = _expected_seat_id(work_item) task_seat_id = _clean(task_metadata.get("delegation_seat_id") or task_metadata.get("seat_id")) if expected_seat_id and task_seat_id and task_seat_id != expected_seat_id: issues.append( _issue( "owner_seat_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Runtime task owner seat differs from the WorkItem seat.", details={"expected_seat_id": expected_seat_id, "task_seat_id": task_seat_id}, ) ) expected_turn_mode = _expected_turn_mode(work_item) task_turn_mode = _clean(task_metadata.get("current_turn_mode")) if expected_turn_mode: if task_turn_mode and task_turn_mode != expected_turn_mode: issues.append( _issue( "turn_mode_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Runtime task current_turn_mode differs from the WorkItem mode.", details={"expected_turn_mode": expected_turn_mode, "task_turn_mode": task_turn_mode}, ) ) elif not task_turn_mode and expected_kind in _AUXILIARY_TURN_KINDS: issues.append( _issue( "turn_mode_missing", "warning", task=task, work_item=work_item, projection_id=projection_id, message="Auxiliary runtime task is missing the WorkItem current_turn_mode execution copy.", details={"expected_turn_mode": expected_turn_mode, "work_kind": expected_kind}, ) ) has_work_item_context = work_item_by_id is not None work_item_by_id = dict(work_item_by_id or {}) if is_review_execution_work_item_metadata(item_metadata): target_id = _clean(item_metadata.get("review_target_work_item_id")) target = work_item_by_id.get(target_id) if target_id else None if not target_id: issues.append( _issue( "review_target_missing", "error", task=task, work_item=work_item, projection_id=projection_id, message="Review runtime WorkItem is missing review_target_work_item_id.", ) ) elif has_work_item_context and target is None: issues.append( _issue( "review_target_unresolved", "error", task=task, work_item=work_item, projection_id=projection_id, message="Review runtime WorkItem target does not exist in the active WorkItem set.", details={"review_target_work_item_id": target_id}, ) ) elif target is not None: target_metadata = dict(target.metadata or {}) expected_review_role = ( _clean(target_metadata.get("review_owner_role_id")) or _clean(getattr(target, "manager_role_id", "")) ) expected_review_seat = ( _clean(target_metadata.get("review_owner_seat_id")) or _clean(getattr(target, "manager_seat_id", "")) ) if expected_review_role and expected_review_role != expected_role_id: issues.append( _issue( "review_owner_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Review WorkItem owner role does not match the target WorkItem review owner.", details={"expected_review_role_id": expected_review_role, "review_work_item_role_id": expected_role_id}, ) ) if expected_review_seat and expected_review_seat != expected_seat_id: issues.append( _issue( "review_owner_mismatch", "error", task=task, work_item=work_item, projection_id=projection_id, message="Review WorkItem owner seat does not match the target WorkItem review owner.", details={"expected_review_seat_id": expected_review_seat, "review_work_item_seat_id": expected_seat_id}, ) ) review_meta_role = _clean(item_metadata.get("review_owner_role_id")) review_meta_seat = _clean(item_metadata.get("review_owner_seat_id")) if expected_review_role and review_meta_role and review_meta_role != expected_review_role: issues.append( _issue( "review_owner_metadata_mismatch", "warning", task=task, work_item=work_item, projection_id=projection_id, message="Review WorkItem metadata owner role differs from the target review owner.", details={"expected_review_role_id": expected_review_role, "metadata_review_owner_role_id": review_meta_role}, ) ) if expected_review_seat and review_meta_seat and review_meta_seat != expected_review_seat: issues.append( _issue( "review_owner_metadata_mismatch", "warning", task=task, work_item=work_item, projection_id=projection_id, message="Review WorkItem metadata owner seat differs from the target review owner.", details={"expected_review_seat_id": expected_review_seat, "metadata_review_owner_seat_id": review_meta_seat}, ) ) if is_report_execution_work_item_metadata(item_metadata): target_id = _clean(item_metadata.get("report_target_work_item_id")) if not target_id: issues.append( _issue( "report_target_missing", "error", task=task, work_item=work_item, projection_id=projection_id, message="Report runtime WorkItem is missing report_target_work_item_id.", ) ) elif has_work_item_context and target_id not in work_item_by_id: issues.append( _issue( "report_target_unresolved", "error", task=task, work_item=work_item, projection_id=projection_id, message="Report runtime WorkItem target does not exist in the active WorkItem set.", details={"report_target_work_item_id": target_id}, ) ) for ownership_issue in validate_metadata_ownership(work_item, task): issues.append( _issue( ownership_issue.code, ownership_issue.severity, task=task, work_item=work_item, projection_id=projection_id, message=ownership_issue.message, details={ "metadata_key": ownership_issue.key, "owner": ownership_issue.owner, **dict(ownership_issue.details or {}), }, ) ) return issues def _work_item_expects_runtime_projection(work_item: DelegationWorkItem) -> bool: metadata = dict(work_item.metadata or {}) if not is_work_item_runtime_metadata(metadata): return False runtime_model = _runtime_model(metadata) if runtime_model and runtime_model not in _RUNTIME_MODELS: return False if _is_done_phase(getattr(work_item, "phase", "")): return False kind = _expected_work_kind(work_item) if should_hide_work_item_from_company_kanban(metadata) and kind not in _HIDDEN_RUNNABLE_TURN_KINDS: return False return bool(projection_id_for_work_item(work_item)) def diagnose_work_item_runtime_projections( tasks: Iterable[Task], work_items: Iterable[DelegationWorkItem], ) -> list[WorkItemRuntimeInvariantIssue]: """Return projection invariant issues without mutating Tasks or the DB.""" task_list = list(tasks or []) work_item_list = list(work_items or []) work_item_by_id = { _clean(getattr(item, "work_item_id", "")): item for item in work_item_list if _clean(getattr(item, "work_item_id", "")) } issues: list[WorkItemRuntimeInvariantIssue] = [] linked_work_item_ids: set[str] = set() for task in task_list: if not is_company_runtime_projection_task(task): continue linked_work_item_id = linked_work_item_id_for_task(task) if linked_work_item_id: linked_work_item_ids.add(linked_work_item_id) issues.extend( validate_work_item_runtime_projection( task, work_item_by_id.get(linked_work_item_id) if linked_work_item_id else None, work_item_by_id=work_item_by_id, ) ) for work_item in work_item_list: wid = _clean(getattr(work_item, "work_item_id", "")) if not wid or wid in linked_work_item_ids: continue if not _work_item_expects_runtime_projection(work_item): continue issues.append( _issue( "work_item_missing_runtime_task", "error", work_item=work_item, projection_id=projection_id_for_work_item(work_item), message="Non-terminal company WorkItem has no linked runtime Task projection after materialization.", details={"phase": _phase_value(getattr(work_item, "phase", "")), "work_kind": _expected_work_kind(work_item)}, ) ) return issues