Add execution retry and emergency profile settings

Why: Routing needs an explicit per-profile attempt limit and a named Codex emergency profile before controller enforcement can be implemented.

Evidence: The new parser test failed on unknown keys before implementation; all 15 config test executions now pass and git diff --check is clean.

Impact: Adds backward-compatible optional configuration fields only; routing behavior is unchanged in this commit.

Refs: feature/agent-cli-enforcement
This commit is contained in:
2026-10-15 15:22:51 +00:00
parent 343a56e2b6
commit dd3140b391
2 changed files with 55 additions and 1 deletions
+26 -1
View File
@@ -54,9 +54,13 @@ EXECUTION_KEYS = frozenset(
"idle_timeout_seconds",
"summary_bytes",
"raw_output_bytes",
"max_attempts_per_profile",
"emergency_profile",
}
)
EXECUTION_REQUIRED = EXECUTION_KEYS - frozenset({"idle_timeout_seconds"})
EXECUTION_REQUIRED = EXECUTION_KEYS - frozenset(
{"idle_timeout_seconds", "max_attempts_per_profile", "emergency_profile"}
)
PROFILE_KEYS = frozenset({"adapter", "executable", "model", "enabled"})
CUSTOM_ADAPTER_KEYS = frozenset(
{
@@ -84,6 +88,8 @@ class ExecutionPolicy:
summary_bytes: int
raw_output_bytes: int
idle_timeout_seconds: int = DEFAULT_IDLE_TIMEOUT_SECONDS
max_attempts_per_profile: int = 1
emergency_profile: str | None = None
@dataclass(frozen=True)
@@ -391,6 +397,23 @@ def _build_execution(raw: Any) -> ExecutionPolicy:
if raw_output_bytes < summary_bytes:
raise ConfigError("raw_output_bytes must be >= summary_bytes")
if "max_attempts_per_profile" in raw:
max_attempts_per_profile = _positive_bounded(
raw["max_attempts_per_profile"],
"max_attempts_per_profile",
1,
10,
)
else:
max_attempts_per_profile = 1
if "emergency_profile" in raw:
emergency_profile = raw["emergency_profile"]
if not isinstance(emergency_profile, str) or not emergency_profile:
raise ConfigError("emergency_profile must be a non-empty string")
else:
emergency_profile = None
return ExecutionPolicy(
fresh_process_per_unit=True,
permission=permission,
@@ -400,6 +423,8 @@ def _build_execution(raw: Any) -> ExecutionPolicy:
summary_bytes=summary_bytes,
raw_output_bytes=raw_output_bytes,
idle_timeout_seconds=idle_timeout_seconds,
max_attempts_per_profile=max_attempts_per_profile,
emergency_profile=emergency_profile,
)
+29
View File
@@ -405,6 +405,35 @@ class AgentExecConfigTests(unittest.TestCase):
with self.assertRaises(ConfigError):
load_config(self.repo)
def test_exposes_max_attempts_per_profile_and_emergency_profile(self) -> None:
block = MINIMAL_BLOCK.replace(
" raw_output_bytes: 10485760\n",
" raw_output_bytes: 10485760\n"
" max_attempts_per_profile: 3\n"
" emergency_profile: codex\n",
).replace(
" claude:\n"
" adapter: claude\n"
" executable: claude\n"
" model: auto\n"
" enabled: true\n",
" claude:\n"
" adapter: claude\n"
" executable: claude\n"
" model: auto\n"
" enabled: true\n"
"\n"
" codex:\n"
" adapter: codex\n"
" executable: codex\n"
" model: auto\n"
" enabled: true\n",
)
write_repo(self.repo, block)
cfg = load_config(self.repo)
self.assertEqual(cfg.execution.max_attempts_per_profile, 3)
self.assertEqual(cfg.execution.emergency_profile, "codex")
if __name__ == "__main__":
unittest.main()