Route exhausted work through emergency Codex
Why: Codex CLI is permitted only after every normal execution profile exhausts three safe attempts, with no host implementation fallback. Evidence: The emergency RED test lacked the fourth Codex attempt before implementation; 32 controller/history executions now pass. Impact: Adds one emergency attempt for implementation, repair, and test roles while preserving fail-stop statuses and review isolation. Refs: feature/agent-cli-enforcement
This commit is contained in:
+98
-34
@@ -47,6 +47,9 @@ EXIT_SCOPE_EXPANSION = 5
|
||||
EXIT_CANCELLED = 6
|
||||
EXIT_HOST_FALLBACK = 10
|
||||
|
||||
_EMERGENCY_ROLES = frozenset({"implementation", "repair", "test"})
|
||||
_SAFE_EXHAUST_STATUSES = frozenset({"unavailable", "failed-clean"})
|
||||
|
||||
# Transport commits must not depend on machine-global Git identity.
|
||||
TRANSPORT_AUTHOR_NAME = "agent-exec-transport"
|
||||
TRANSPORT_AUTHOR_EMAIL = "agent-exec-transport@localhost.invalid"
|
||||
@@ -195,6 +198,46 @@ def cmd_validate(repo: Path) -> int:
|
||||
return EXIT_SUCCESS
|
||||
|
||||
|
||||
def _apply_attempt_route(
|
||||
attempt: AttemptRecord,
|
||||
*,
|
||||
attempt_id: str,
|
||||
parent: str,
|
||||
terminal: str,
|
||||
reason: str | None,
|
||||
changed_paths: list[str],
|
||||
transport: dict[str, Any] | None,
|
||||
) -> tuple[str, str, str | None, list[str], dict[str, Any] | None]:
|
||||
status = attempt.terminal_status
|
||||
if status == "success":
|
||||
return (
|
||||
"stop",
|
||||
"success",
|
||||
None,
|
||||
list(attempt.changed_paths),
|
||||
{
|
||||
"ref": attempt.transport_ref,
|
||||
"parent": parent,
|
||||
"attempt_id": attempt_id,
|
||||
},
|
||||
)
|
||||
if status in _SAFE_EXHAUST_STATUSES:
|
||||
return ("continue", "unavailable", attempt.reason, changed_paths, transport)
|
||||
if status == "scope-expansion":
|
||||
return (
|
||||
"stop",
|
||||
"scope-expansion",
|
||||
attempt.reason,
|
||||
list(attempt.changed_paths),
|
||||
transport,
|
||||
)
|
||||
if status in {"failed-unknown", "blocked"}:
|
||||
return ("stop", status, attempt.reason, changed_paths, transport)
|
||||
if status == "cancelled":
|
||||
return ("stop", "cancelled", attempt.reason, changed_paths, transport)
|
||||
return ("next-profile", terminal, reason, changed_paths, transport)
|
||||
|
||||
|
||||
def cmd_run(
|
||||
repo: Path,
|
||||
*,
|
||||
@@ -316,48 +359,69 @@ def cmd_run(
|
||||
attempt.attempt_ordinal = attempt_ordinal
|
||||
attempt.emergency_fallback = False
|
||||
attempts.append(attempt)
|
||||
|
||||
if attempt.terminal_status == "success":
|
||||
terminal = "success"
|
||||
reason = None
|
||||
changed_paths = list(attempt.changed_paths)
|
||||
transport = {
|
||||
"ref": attempt.transport_ref,
|
||||
"parent": base["head"],
|
||||
"attempt_id": attempt_id,
|
||||
}
|
||||
stop = True
|
||||
break
|
||||
if attempt.terminal_status in {"unavailable", "failed-clean"}:
|
||||
terminal = "unavailable"
|
||||
reason = attempt.reason
|
||||
action, terminal, reason, changed_paths, transport = _apply_attempt_route(
|
||||
attempt,
|
||||
attempt_id=attempt_id,
|
||||
parent=base["head"],
|
||||
terminal=terminal,
|
||||
reason=reason,
|
||||
changed_paths=changed_paths,
|
||||
transport=transport,
|
||||
)
|
||||
if action == "continue":
|
||||
continue
|
||||
if attempt.terminal_status == "scope-expansion":
|
||||
terminal = "scope-expansion"
|
||||
reason = attempt.reason
|
||||
changed_paths = list(attempt.changed_paths)
|
||||
stop = True
|
||||
break
|
||||
if attempt.terminal_status in {"failed-unknown", "blocked"}:
|
||||
terminal = attempt.terminal_status
|
||||
reason = attempt.reason
|
||||
stop = True
|
||||
break
|
||||
if attempt.terminal_status == "cancelled":
|
||||
terminal = "cancelled"
|
||||
reason = attempt.reason
|
||||
stop = True
|
||||
break
|
||||
break
|
||||
stop = action == "stop"
|
||||
break
|
||||
|
||||
exhausted_ok = (
|
||||
not stop
|
||||
and attempts
|
||||
and all(a.terminal_status in {"unavailable", "failed-clean"} for a in attempts)
|
||||
and all(a.terminal_status in _SAFE_EXHAUST_STATUSES for a in attempts)
|
||||
and len(attempts) == len(profiles) * max_attempts
|
||||
)
|
||||
if exhausted_ok and cfg.execution.fallback_to_host == "all-unavailable":
|
||||
emergency_name = cfg.execution.emergency_profile
|
||||
if exhausted_ok and emergency_name and role in _EMERGENCY_ROLES:
|
||||
emergency_profile = cfg.profiles.get(emergency_name)
|
||||
if emergency_profile is None:
|
||||
terminal = "blocked"
|
||||
reason = f"emergency profile {emergency_name!r} is not configured"
|
||||
else:
|
||||
attempt_id = uuid.uuid4().hex[:12]
|
||||
attempt = _run_attempt(
|
||||
repo=repo,
|
||||
cfg=cfg,
|
||||
profile=emergency_profile,
|
||||
unit=unit,
|
||||
unit_path=unit_path,
|
||||
base=base,
|
||||
run_id=run_id,
|
||||
attempt_id=attempt_id,
|
||||
run_dir=run_dir,
|
||||
secret_values=secret_values,
|
||||
)
|
||||
attempt.attempt_ordinal = 1
|
||||
attempt.emergency_fallback = True
|
||||
attempts.append(attempt)
|
||||
action, terminal, reason, changed_paths, transport = _apply_attempt_route(
|
||||
attempt,
|
||||
attempt_id=attempt_id,
|
||||
parent=base["head"],
|
||||
terminal=terminal,
|
||||
reason=reason,
|
||||
changed_paths=changed_paths,
|
||||
transport=transport,
|
||||
)
|
||||
if action == "continue":
|
||||
terminal = "blocked"
|
||||
reason = attempt.reason
|
||||
stop = True
|
||||
|
||||
if (
|
||||
exhausted_ok
|
||||
and not stop
|
||||
and not emergency_name
|
||||
and cfg.execution.fallback_to_host == "all-unavailable"
|
||||
):
|
||||
terminal = "host-fallback-authorized"
|
||||
host_fallback_authorizations = 1
|
||||
host_scope = {
|
||||
|
||||
Reference in New Issue
Block a user