Harden Codex emergency profile configuration

Why: Normal execution roles must exclude Codex while a single enabled Codex adapter remains available for emergency routing and review.

Evidence: Parser invalid-case tests were RED before validation; all 17 config tests and agent_exec validate now pass.

Impact: Enables three attempts per normal profile, stable Cursor routing, and a validated emergency Codex profile.

Refs: feature/agent-cli-enforcement
This commit is contained in:
2026-10-15 16:45:14 +00:00
parent 71fd4486f5
commit d54306b65e
3 changed files with 116 additions and 7 deletions
+18 -7
View File
@@ -7,11 +7,14 @@ parser accepts exactly one `agent-execution` fenced block.
U3's execution controller and U4's project wiring are integrated. This file is
the execution-routing authority for implementation, repair, test, and review.
Active routes are intentionally limited to Cursor, Antigravity (`agy`), and
PI. `model: auto` lets each CLI use its configured default model; PI therefore
uses the operator-selected MiniMax default. A provider that makes no successful
stdin write or stdout/stderr read for `idle_timeout_seconds` is stopped before
`timeout_seconds`.
Normal implementation, repair, and test routes stay Cursor, Antigravity
(`agy`), and PI. Each profile may be attempted up to
`max_attempts_per_profile`. Only after those candidates safely exhaust their
attempts does the enabled emergency `codex` profile run once. Codex remains out
of the three normal role lists and may also appear in review. A configured
emergency profile replaces host write fallback. Cursor uses
`cursor-agent-stable`. `model: auto` lets each CLI use its configured default
model; PI therefore uses the operator-selected MiniMax default.
```agent-execution
schema: agent-execution/v1
@@ -25,17 +28,19 @@ execution:
idle_timeout_seconds: 600
summary_bytes: 16384
raw_output_bytes: 10485760
max_attempts_per_profile: 3
emergency_profile: codex
roles:
implementation: [cursor, antigravity, pi]
repair: [cursor, antigravity, pi]
test: [pi, antigravity, cursor]
review: [pi, antigravity, cursor]
review: [pi, antigravity, cursor, codex]
profiles:
cursor:
adapter: cursor
executable: cursor-agent
executable: cursor-agent-stable
model: auto
enabled: true
@@ -50,4 +55,10 @@ profiles:
executable: pi
model: auto
enabled: true
codex:
adapter: codex
executable: codex
model: auto
enabled: true
```
+24
View File
@@ -343,6 +343,8 @@ def _build_config(raw: dict[str, Any]) -> ExecutionConfig:
if profile.adapter not in custom_adapters:
raise ConfigError(f"unknown adapter: {profile.adapter!r}")
_validate_emergency_profile(execution, profiles, roles)
return ExecutionConfig(
schema=schema,
execution=execution,
@@ -428,6 +430,28 @@ def _build_execution(raw: Any) -> ExecutionPolicy:
)
def _validate_emergency_profile(
execution: ExecutionPolicy,
profiles: Mapping[str, Profile],
roles: Mapping[str, tuple[str, ...]],
) -> None:
name = execution.emergency_profile
if name is None:
return
profile = profiles.get(name)
if profile is None:
raise ConfigError(f"emergency_profile {name!r} does not exist")
if not profile.enabled:
raise ConfigError(f"emergency_profile {name!r} must be enabled")
if profile.adapter != "codex":
raise ConfigError(f"emergency_profile {name!r} must use adapter codex")
for role in ("implementation", "repair", "test"):
if name in roles[role]:
raise ConfigError(
f"emergency_profile {name!r} must not appear in role {role!r}"
)
def _positive_bounded(value: Any, name: str, lo: int, hi: int) -> int:
if type(value) is not int or isinstance(value, bool):
raise ConfigError(f"{name} must be an integer")
+74
View File
@@ -434,6 +434,80 @@ class AgentExecConfigTests(unittest.TestCase):
self.assertEqual(cfg.execution.max_attempts_per_profile, 3)
self.assertEqual(cfg.execution.emergency_profile, "codex")
def test_configured_emergency_profile_must_exist_be_enabled_and_use_adapter_codex(
self,
) -> None:
write_repo(self.repo, _emergency_block(include_profile=False))
with self.assertRaises(ConfigError) as ctx:
load_config(self.repo)
self.assertIn("does not exist", str(ctx.exception))
write_repo(self.repo, _emergency_block(enabled="false"))
with self.assertRaises(ConfigError) as ctx:
load_config(self.repo)
self.assertIn("must be enabled", str(ctx.exception))
write_repo(self.repo, _emergency_block(adapter="pi"))
with self.assertRaises(ConfigError) as ctx:
load_config(self.repo)
self.assertIn("adapter codex", str(ctx.exception))
def test_emergency_profile_is_excluded_from_normal_roles(self) -> None:
for role, replacement in (
("implementation", "[cursor, claude, codex]"),
("repair", "[cursor, codex]"),
("test", "[claude, codex]"),
):
write_repo(self.repo, _emergency_block(**{role: replacement}))
with self.assertRaises(ConfigError) as ctx:
load_config(self.repo)
self.assertIn("must not appear", str(ctx.exception))
self.assertIn(role, str(ctx.exception))
def test_review_may_include_emergency_profile(self) -> None:
write_repo(self.repo, _emergency_block(review="[claude, codex]"))
cfg = load_config(self.repo)
self.assertEqual(cfg.execution.emergency_profile, "codex")
self.assertTrue(cfg.profiles["codex"].enabled)
self.assertEqual(cfg.profiles["codex"].adapter, "codex")
self.assertEqual(cfg.roles["review"], ("claude", "codex"))
self.assertNotIn("codex", cfg.roles["implementation"])
self.assertNotIn("codex", cfg.roles["repair"])
self.assertNotIn("codex", cfg.roles["test"])
def _emergency_block(
*,
include_profile: bool = True,
adapter: str = "codex",
enabled: str = "true",
implementation: str = "[cursor, claude]",
repair: str = "[cursor]",
test: str = "[claude]",
review: str = "[claude]",
) -> str:
block = MINIMAL_BLOCK.replace(
" raw_output_bytes: 10485760\n",
" raw_output_bytes: 10485760\n emergency_profile: codex\n",
)
block = block.replace("implementation: [cursor, claude]", f"implementation: {implementation}")
block = block.replace("repair: [cursor]", f"repair: {repair}")
block = block.replace("test: [claude]", f"test: {test}")
block = block.replace("review: [claude]", f"review: {review}")
if not include_profile:
return block
return block.replace(
" enabled: true\n",
" enabled: true\n"
"\n"
" codex:\n"
f" adapter: {adapter}\n"
" executable: codex\n"
" model: auto\n"
f" enabled: {enabled}\n",
1,
)
if __name__ == "__main__":
unittest.main()