fix(company): route delete cascade through transition_work_item; centralize resume identity restore
- delete_work_item descendant cascade now goes through transition_work_item (audit reason, attempt-ledger settlement, phase hooks) instead of raw store phase writes plus manual task.status mutation; task-owned audit stamps and execution-lock release only happen when a cancellation actually occurred, removing a desync path (task=CANCELLED under work_item=APPROVED) in drift scenarios. - transition_work_item gains blocked_reason/handoff_status passthrough so callers no longer need a second store write for the same transition. - new build_company_resume_identity_restore helper in metadata_ownership replaces the ad-hoc delegation_seat_id/role/session literals in engine._restore_and_pin_company_resume_execution_identity, keeping seat identity writes inside the ownership contract module. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -387,6 +387,33 @@ def build_work_item_owner_execution_copy(work_item: DelegationWorkItem | None) -
|
||||
}
|
||||
|
||||
|
||||
def build_company_resume_identity_restore(
|
||||
*,
|
||||
role_id: str,
|
||||
seat_id: str,
|
||||
role_runtime_session_id: str,
|
||||
) -> dict[str, Any]:
|
||||
"""Map checkpoint-validated resume identity onto Task execution-copy keys.
|
||||
|
||||
Company resume restores missing Task projection fields from the durable
|
||||
checkpoint after the authoritative WorkItem has been cross-checked (the
|
||||
checkpoint may hold values the WorkItem row lacks, so this is not always
|
||||
derivable via ``build_work_item_owner_execution_copy``). The key spelling
|
||||
lives here, next to the owner spec, so runtime code never hand-writes
|
||||
WorkItem-owned execution-copy keys.
|
||||
"""
|
||||
payload: dict[str, Any] = {
|
||||
"work_item_role_id": str(role_id or "").strip(),
|
||||
}
|
||||
seat = str(seat_id or "").strip()
|
||||
if seat:
|
||||
payload["delegation_seat_id"] = seat
|
||||
session = str(role_runtime_session_id or "").strip()
|
||||
if session:
|
||||
payload["delegation_role_session_id"] = session
|
||||
return payload
|
||||
|
||||
|
||||
def strip_disallowed_work_item_metadata_from_runtime_task(task: Task) -> list[str]:
|
||||
"""Remove WorkItem-owned fields that are not valid Task execution copies."""
|
||||
metadata = dict(getattr(task, "metadata", {}) or {})
|
||||
|
||||
@@ -102,6 +102,8 @@ async def transition_work_item(
|
||||
metadata_updates: dict[str, Any] | None = None,
|
||||
release_claim: bool = False,
|
||||
attempt_outcome: str | None = None,
|
||||
blocked_reason: str | None = None,
|
||||
handoff_status: str | None = None,
|
||||
) -> DelegationWorkItem | None:
|
||||
"""Transition a work item to ``target_phase``.
|
||||
|
||||
@@ -124,6 +126,10 @@ async def transition_work_item(
|
||||
metadata_updates: Extra metadata keys to merge onto the work item.
|
||||
release_claim: When True, clears the current claim so the dispatcher
|
||||
can re-acquire. Useful for cancel / timeout / forced-release paths.
|
||||
blocked_reason: Optional ``blocked_reason`` column value, folded into
|
||||
the same write as the phase change (pass ``""`` to clear).
|
||||
handoff_status: Optional ``handoff_status`` column value, folded into
|
||||
the same write as the phase change.
|
||||
|
||||
Returns:
|
||||
The updated ``DelegationWorkItem``, or ``None`` when the store lacks
|
||||
@@ -166,6 +172,10 @@ async def transition_work_item(
|
||||
}
|
||||
if summary is not None:
|
||||
kwargs["summary"] = summary
|
||||
if blocked_reason is not None:
|
||||
kwargs["blocked_reason"] = blocked_reason
|
||||
if handoff_status is not None:
|
||||
kwargs["handoff_status"] = handoff_status
|
||||
if release_claim:
|
||||
# Fold claim release into the same write as the phase change: the
|
||||
# legacy two-call sequence could commit the phase and then fail the
|
||||
|
||||
Reference in New Issue
Block a user