fix(engine): fail resume closed when the pinned external agent is unavailable (#10)

A company-runtime resume checkpoint pins each task to one exact
execution backend. When that pinned external agent is disabled in the
config (issue #10: all external agents off), every resume claimed the
card, transitioned it to RUNNING, and crashed deterministically in the
dispatch-time selector ("company runtime resume requires unavailable
external agent"), feeding the restart -> resume -> crash loop.

Gate availability at resume preparation instead, before any pin or
claim: fail the single work item closed (FAILED + diagnostic
blocked_reason + progress note) while the rest of the organization
resumes normally. attempt_seq stays 0 - no dispatch attempt is burned.
A missing adapter registry means availability is unknown, so the gate
fails open and leaves the decision to the dispatch-time guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LZH-YS1998
2026-07-21 11:18:33 +08:00
parent 9aed328d02
commit d380684b78
+75
View File
@@ -6363,6 +6363,81 @@ class OPCEngine:
f"company runtime resume checkpoint has no task identity snapshot for {task.id}"
)
identity = self._checkpoint_task_execution_identity(task_snapshot)
# Resume availability gate: the checkpoint pins one exact
# execution backend, so a pinned external agent that is disabled
# (or whose binary vanished) makes this work item deterministically
# un-runnable — every dispatch would claim, transition to RUNNING,
# and crash on the selector's availability check. Fail the item
# closed HERE, before any pin/claim, with a visible diagnostic;
# the rest of the organization resumes normally and the failed-
# child settlement machinery hands the card to manager triage.
pinned_agent = str(
identity.get("selected_execution_agent", "") or "native"
).strip() or "native"
work_item_already_terminal = bool(
work_item is not None
and getattr(work_item, "phase", None) in DONE_PHASES
)
# A missing adapter registry means availability is UNKNOWN (engine
# not fully initialized / delegate context) — fail open and let the
# dispatch-time selector guard decide; only a registry that exists
# and excludes the pinned agent is proof of unavailability.
if (
pinned_agent != "native"
and not work_item_already_terminal
and self.adapter_registry is not None
and pinned_agent not in self._available_external_agents()
):
diagnostic = (
f"Cannot resume work item: its execution is pinned to external agent "
f"'{pinned_agent}', which is currently disabled or unavailable. "
f"Re-enable '{pinned_agent}' in the config and restart to run it, "
"or re-issue the work as a new request."
)
task.metadata = dict(task.metadata or {})
for key in _COMPANY_RUNTIME_CONTROL_METADATA_KEYS:
task.metadata.pop(key, None)
progress = list(task.metadata.get("progress_log", []) or [])
progress.append(diagnostic)
task.metadata["progress_log"] = progress[-20:]
task.metadata["resume_unavailable_external_agent"] = pinned_agent
existing_result = dict(task.result or {})
existing_result["content"] = diagnostic
task.result = existing_result
await self._fail_task_via_phase(
task,
reason="resume_external_agent_unavailable",
)
if work_item_id and callable(update_work_item):
try:
await update_work_item(
work_item_id,
blocked_reason=diagnostic,
metadata_updates={"dispatch_hold": ""},
)
except Exception:
logger.opt(exception=True).debug(
"company runtime resume: blocked_reason write failed for {}",
work_item_id,
)
if self.on_progress:
try:
await self.on_progress(
f"[Company:{projection_id_for_task(task) or task.title}] {diagnostic}"
)
except Exception:
logger.opt(exception=True).debug(
"company runtime resume: unavailable-agent progress emit failed"
)
logger.warning(
"company runtime resume: failed work item {} closed — pinned external "
"agent {!r} unavailable",
work_item_id or task.id,
pinned_agent,
)
fresh = await self.store.get_task(task.id)
refreshed.append(fresh or task)
continue
expected_role_session_id = str(
identity.get("role_runtime_session_id", "") or ""
).strip()