agent-bios 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/DEPENDENCIES.md +89 -0
  2. package/LICENSE +21 -0
  3. package/README.md +86 -0
  4. package/claude/CLAUDE.md +138 -0
  5. package/claude/guides/cli-multi-model-workflow.md +194 -0
  6. package/claude/guides/coding-staged-workflow.md +70 -0
  7. package/claude/guides/implementation-map.md +34 -0
  8. package/claude/guides/llm-capability-boundary-examples.md +123 -0
  9. package/claude/guides/llm-capability-boundary-patterns.md +339 -0
  10. package/claude/guides/llm-capability-boundary.md +255 -0
  11. package/claude/guides/mock-realization-boundary.md +275 -0
  12. package/claude/guides/svg-visualization-guide.md +321 -0
  13. package/codex/AGENTS.md +139 -0
  14. package/codex/agents/frontier.toml +8 -0
  15. package/codex/agents/reviewer.toml +9 -0
  16. package/codex/agents/sweep.toml +9 -0
  17. package/codex/agents/workhorse.toml +8 -0
  18. package/codex/guides/cli-multi-model-workflow.md +194 -0
  19. package/codex/guides/coding-staged-workflow.md +70 -0
  20. package/codex/guides/implementation-map.md +34 -0
  21. package/codex/guides/llm-capability-boundary-examples.md +123 -0
  22. package/codex/guides/llm-capability-boundary-patterns.md +339 -0
  23. package/codex/guides/llm-capability-boundary.md +255 -0
  24. package/codex/guides/mock-realization-boundary.md +275 -0
  25. package/codex/guides/svg-visualization-guide.md +321 -0
  26. package/config/agent-launch.toml +94 -0
  27. package/package.json +54 -0
  28. package/scripts/agent-launch.py +1742 -0
  29. package/scripts/check-parity.sh +1703 -0
  30. package/scripts/codex-helm.sh +370 -0
  31. package/scripts/codex-run.sh +176 -0
  32. package/scripts/install.sh +310 -0
  33. package/scripts/provision-venv.sh +28 -0
  34. package/scripts/session-cost.py +106 -0
  35. package/shell/agent-launch.zsh +38 -0
@@ -0,0 +1,1703 @@
1
+ #!/usr/bin/env bash
2
+ # Parity gate for mirrored instruction files.
3
+ # EN canonical (installed): claude/, codex/. KO reference (never installed): ko/claude, ko/codex.
4
+ # Checks:
5
+ # (1) EN guide dirs mirror: claude/guides == codex/guides (modulo config-home var)
6
+ # (2) KO guide dirs mirror: ko/claude/guides == ko/codex/guides (modulo config-home var)
7
+ # (3) EN globals mirror: claude/CLAUDE.md == codex/AGENTS.md (modulo title, config-home, declared Codex authorization)
8
+ # (4) KO globals mirror: ko/claude/CLAUDE.md == ko/codex/AGENTS.md (same declared exception)
9
+ # (5) EN and KO guide sets match — every English guide has a Korean counterpart
10
+ # (6) every guide referenced by claude/CLAUDE.md exists in both EN guide dirs
11
+ # (7) frontmatter: guide_id matches filename; language matches tree (ko/ => ko, else en); parent resolves
12
+ # (8) anchor phrases: each intentional global↔guide restatement pair shares a fixed anchor
13
+ # phrase in both EN files, so editing one side without the other fails here
14
+ # (9) launch-profile, Codex role-slot, and wrapper defaults match runtime projections
15
+ # Exit non-zero on any divergence. Run from the repo root; safe as a pre-commit hook.
16
+ set -u
17
+ cd "$(dirname "$0")/.."
18
+ fail=0
19
+
20
+ # Non-empty-subject guards: a parity check over missing inputs must fail, not pass vacuously.
21
+ for p in claude/guides codex/guides ko/claude/guides ko/codex/guides; do
22
+ [ -d "$p" ] || { echo "FAIL: required dir missing: $p"; exit 1; }
23
+ done
24
+ for p in claude/CLAUDE.md codex/AGENTS.md ko/claude/CLAUDE.md ko/codex/AGENTS.md; do
25
+ [ -f "$p" ] || { echo "FAIL: required file missing: $p"; exit 1; }
26
+ done
27
+ guide_count=$(ls claude/guides/*.md 2>/dev/null | wc -l | tr -d ' ')
28
+ [ "$guide_count" -ge 1 ] || { echo "FAIL: no guides found in claude/guides"; exit 1; }
29
+
30
+ # Guide dirs mirror across CLIs modulo the config-home variable (both sides normalized).
31
+ mirror_guides() { # $1 = claude-side dir, $2 = codex-side dir
32
+ if ! diff <(ls "$1") <(ls "$2"); then
33
+ echo "FAIL: guide dirs hold different file sets: $1 vs $2"; fail=1
34
+ fi
35
+ for f in "$1"/*.md; do
36
+ name=$(basename "$f")
37
+ if ! diff <(sed 's|${CLAUDE_CONFIG_DIR:-$HOME/.claude}|__CONFIG__|g' "$f") \
38
+ <(sed 's|${CODEX_HOME:-$HOME/.codex}|__CONFIG__|g' "$2/$name") >/dev/null 2>&1; then
39
+ echo "FAIL: guide mirror diverges beyond config-home var: $2/$name"; fail=1
40
+ fi
41
+ done
42
+ }
43
+ mirror_guides claude/guides codex/guides
44
+ mirror_guides ko/claude/guides ko/codex/guides
45
+
46
+ # Global files mirror across CLIs modulo title, config-home var, and the one
47
+ # Codex-only standing-authorization bullet required by the host trigger contract.
48
+ # The ko pair is reference-only but is held to the same bounded exception.
49
+ check_global_mirror() { # $1 = claude-side file, $2 = codex-side file
50
+ if ! diff <(sed -e 's|${CLAUDE_CONFIG_DIR:-$HOME/.claude}|__CONFIG__|g' -e '1s/.*/# GLOBAL/' "$1") \
51
+ <(sed -e 's|${CODEX_HOME:-$HOME/.codex}|__CONFIG__|g' -e '1s/.*/# GLOBAL/' -e '/^- Codex-only standing authorization:/d' "$2"); then
52
+ echo "FAIL: global files diverge beyond title/config-home/Codex-authorization exceptions: $1 vs $2"; fail=1
53
+ fi
54
+ }
55
+ check_global_mirror claude/CLAUDE.md codex/AGENTS.md
56
+ check_global_mirror ko/claude/CLAUDE.md ko/codex/AGENTS.md
57
+
58
+ expected_codex_authorization='- Codex-only standing authorization: on root/main local tasks, ordinary subagent dispatch is authorized when the `When To Spawn` gates fire. Explicit no-fan-out wins. Delegated agents may re-delegate only when their role allows. This grants no destructive, remote, credential, install, OAuth, push, live-network-expanding, or broader-sandbox authority.'
59
+ expected_ko_codex_authorization='- Codex-only standing authorization: root/main local task에서 `When To Spawn` gate가 발동하면 ordinary subagent dispatch를 상시 허용한다. Explicit no-fan-out이 우선하며 delegated agent는 role이 허용할 때만 재위임한다. 이는 destructive, remote, credential, install, OAuth, push, live-network-expanding, broader-sandbox authority를 주지 않는다.'
60
+ grep -qxF -- "$expected_codex_authorization" codex/AGENTS.md || { echo "FAIL: exact Codex standing authorization missing or changed: codex/AGENTS.md"; fail=1; }
61
+ grep -qxF -- "$expected_ko_codex_authorization" ko/codex/AGENTS.md || { echo "FAIL: exact Codex standing authorization missing or changed: ko/codex/AGENTS.md"; fail=1; }
62
+
63
+ # EN/KO guide-set parity: every English guide has a Korean counterpart (same basenames).
64
+ if ! diff <(ls claude/guides) <(ls ko/claude/guides); then
65
+ echo "FAIL: EN and KO guide sets differ (a guide lacks its Korean version, or vice versa)"; fail=1
66
+ fi
67
+
68
+ # Every guide referenced from the global file must exist in both EN guide dirs.
69
+ refs=$(grep -o 'guides/[a-z0-9-]*\.md' claude/CLAUDE.md | sort -u)
70
+ [ -n "$refs" ] || { echo "FAIL: global file references no guides (extraction empty)"; fail=1; }
71
+ for ref in $refs; do
72
+ name=$(basename "$ref")
73
+ for dir in claude/guides codex/guides; do
74
+ [ -f "$dir/$name" ] || { echo "FAIL: $dir/$name referenced from a global file but missing"; fail=1; }
75
+ done
76
+ done
77
+
78
+ # Frontmatter gate: every guide declares guide_id/language; guide_id matches the
79
+ # filename, language matches the tree (files under ko/ are ko, else en), and any
80
+ # declared parent resolves to a sibling guide in the same dir.
81
+ for f in claude/guides/*.md codex/guides/*.md ko/claude/guides/*.md ko/codex/guides/*.md; do
82
+ name=$(basename "$f")
83
+ case "$f" in ko/*) want_lang=ko ;; *) want_lang=en ;; esac
84
+ if [ "$(head -1 "$f")" != "---" ]; then
85
+ echo "FAIL: missing YAML frontmatter: $f"; fail=1; continue
86
+ fi
87
+ fm=$(awk '/^---$/{n++; next} n==1{print} n>=2{exit}' "$f")
88
+ gid=$(printf '%s\n' "$fm" | awk -F': ' '$1=="guide_id"{print $2; exit}')
89
+ lang=$(printf '%s\n' "$fm" | awk -F': ' '$1=="language"{print $2; exit}')
90
+ parent=$(printf '%s\n' "$fm" | awk -F': ' '$1=="parent"{print $2; exit}')
91
+ [ "$name" = "$gid.md" ] || { echo "FAIL: guide_id does not match filename: $f (guide_id=$gid)"; fail=1; }
92
+ [ "$lang" = "$want_lang" ] || { echo "FAIL: language does not match tree: $f (language=$lang, want=$want_lang)"; fail=1; }
93
+ if [ -n "$parent" ] && [ ! -f "$(dirname "$f")/$parent.md" ]; then
94
+ echo "FAIL: parent guide missing: $f (parent=$parent)"; fail=1
95
+ fi
96
+ done
97
+
98
+ # Anchor-phrase gate: the global↔guide restatement pairs kept on purpose share a
99
+ # fixed anchor phrase; a one-sided edit that drops or rewords the anchor fails here.
100
+ # Format: anchor|fileA|fileB (EN canonical only; ko is a translation, not checked).
101
+ while IFS='|' read -r anchor fa fb; do
102
+ [ -n "$anchor" ] || continue
103
+ for f in "$fa" "$fb"; do
104
+ grep -qF "$anchor" "$f" || { echo "FAIL: anchor phrase '$anchor' missing from $f (restatement pair drifted)"; fail=1; }
105
+ done
106
+ done <<'ANCHORS'
107
+ difficulty × blast radius|claude/CLAUDE.md|claude/guides/cli-multi-model-workflow.md
108
+ convergence heuristic by reviewer kind|claude/CLAUDE.md|claude/guides/cli-multi-model-workflow.md
109
+ code-level circuit breaker|claude/CLAUDE.md|claude/guides/cli-multi-model-workflow.md
110
+ current-state dashboard|claude/CLAUDE.md|claude/guides/implementation-map.md
111
+ Verification Menus|claude/CLAUDE.md|claude/guides/coding-staged-workflow.md
112
+ real Microsoft Excel engine|claude/CLAUDE.md|claude/guides/coding-staged-workflow.md
113
+ severity contract|README.md|claude/guides/coding-staged-workflow.md
114
+ ANCHORS
115
+
116
+ # The launcher's Textual preflight UI tests need the managed venv (textual).
117
+ # Provision it if missing; every non-UI check above runs under system python.
118
+ VENV="${AGENT_LAUNCH_VENV:-$HOME/.local/share/agent-launch/venv}"
119
+ if ! { [ -x "$VENV/bin/python" ] && "$VENV/bin/python" -c 'import textual' 2>/dev/null; }; then
120
+ echo "provisioning agent-launch venv for parity UI tests ($VENV) ..."
121
+ AGENT_LAUNCH_VENV="$VENV" bash "$(dirname "$0")/provision-venv.sh" >/dev/null 2>&1 || {
122
+ echo "FAIL: could not provision the textual venv required for UI parity tests"; exit 1; }
123
+ fi
124
+ export AGENT_LAUNCH_VENV="$VENV"
125
+
126
+ if ! "$VENV/bin/python" - <<'PY'
127
+ import pathlib
128
+ import importlib.util
129
+ import fcntl
130
+ import json
131
+ import re
132
+ import pty
133
+ import select
134
+ import signal
135
+ import struct
136
+ import subprocess
137
+ import sys
138
+ import os
139
+ import tempfile
140
+ import termios
141
+ import time
142
+ import tomllib
143
+
144
+ sys.dont_write_bytecode = True
145
+
146
+ fail = 0
147
+
148
+
149
+ def mark_fail(message):
150
+ global fail
151
+ print(f"FAIL: {message}")
152
+ fail = 1
153
+
154
+
155
+ agent_dir = pathlib.Path("codex/agents")
156
+ launch_profile_path = pathlib.Path("config/agent-launch.toml")
157
+ launcher = pathlib.Path("scripts/agent-launch.py")
158
+ shell_init = pathlib.Path("shell/agent-launch.zsh")
159
+
160
+ for required in (launch_profile_path, launcher, shell_init):
161
+ if not required.is_file():
162
+ mark_fail(f"required launch asset missing: {required}")
163
+
164
+ for doc_path, required_phrases in {
165
+ pathlib.Path("README.md"): (
166
+ "Every arrow-key TUI selection screen",
167
+ "persistent settings hub",
168
+ "Start with these settings",
169
+ "Exit without launching",
170
+ "`b` is the back command",
171
+ ),
172
+ pathlib.Path("ko/README.md"): (
173
+ "화살표 키 TUI의 모든 선택 화면",
174
+ "지속형 설정 허브",
175
+ "Start with these settings",
176
+ "Exit without launching",
177
+ "`b`가 뒤로가기 명령",
178
+ ),
179
+ }.items():
180
+ doc_text = doc_path.read_text()
181
+ for phrase in required_phrases:
182
+ if phrase not in doc_text:
183
+ mark_fail(f"agent-launch documentation contract missing from {doc_path}: {phrase!r}")
184
+
185
+ try:
186
+ launch_profile = tomllib.loads(launch_profile_path.read_text())
187
+ except Exception as exc:
188
+ mark_fail(f"launch profile does not parse: {launch_profile_path} ({exc})")
189
+ launch_profile = {}
190
+
191
+ expected_launch_tiers = {
192
+ "frontier": ("gpt-5.6-sol", "max"),
193
+ "helm": ("gpt-5.6-sol", "xhigh"),
194
+ "workhorse": ("gpt-5.6-terra", "high"),
195
+ "sweep": ("gpt-5.6-luna", "low"),
196
+ }
197
+ codex_launch_tiers = launch_profile.get("hosts", {}).get("codex", {}).get("tiers", {})
198
+ if set(codex_launch_tiers) != set(expected_launch_tiers):
199
+ mark_fail("launch profile Codex tiers must be exactly frontier/helm/workhorse/sweep")
200
+ for tier, (model, effort) in expected_launch_tiers.items():
201
+ binding = codex_launch_tiers.get(tier, {})
202
+ if (binding.get("model"), binding.get("effort")) != (model, effort):
203
+ mark_fail(f"launch profile Codex {tier} is {binding!r}, want {model}/{effort}")
204
+
205
+ expected_claude_tiers = {
206
+ "frontier": ("claude-fable-5", "max"),
207
+ "helm": ("claude-opus-4-8", "xhigh"),
208
+ "workhorse": ("claude-sonnet-5", "high"),
209
+ "sweep": ("claude-haiku-4-5", "low"),
210
+ }
211
+ claude_launch_tiers = launch_profile.get("hosts", {}).get("claude", {}).get("tiers", {})
212
+ if set(claude_launch_tiers) != set(expected_claude_tiers):
213
+ mark_fail("launch profile Claude tiers must be exactly frontier/helm/workhorse/sweep")
214
+ for tier, (model, effort) in expected_claude_tiers.items():
215
+ binding = claude_launch_tiers.get(tier, {})
216
+ if (binding.get("model"), binding.get("effort")) != (model, effort):
217
+ mark_fail(f"launch profile Claude {tier} is {binding!r}, want {model}/{effort}")
218
+
219
+ balanced = launch_profile.get("presets", {}).get("balanced", {})
220
+ if (balanced.get("main_tier"), balanced.get("review_setup")) != ("helm", "native-panel"):
221
+ mark_fail("balanced launch preset must start HELM with native-panel review setup")
222
+ deep_review = launch_profile.get("presets", {}).get("deep-review", {})
223
+ deep_frontier = deep_review.get("frontier_effort", {})
224
+ if (
225
+ deep_review.get("main_tier") != "helm"
226
+ or deep_frontier.get("codex") != "ultra"
227
+ or deep_frontier.get("claude") != "max"
228
+ ):
229
+ mark_fail("deep-review must keep HELM main, allow Codex FRONTIER Ultra, and cap Claude at max")
230
+
231
+ expected_agents = {
232
+ "frontier.toml": ("frontier", "gpt-5.6-sol", None),
233
+ "workhorse.toml": ("workhorse", "gpt-5.6-terra", "high"),
234
+ "sweep.toml": ("sweep", "gpt-5.6-luna", "low"),
235
+ "reviewer.toml": ("reviewer", "gpt-5.6-terra", "high"),
236
+ }
237
+
238
+ missing_agents = sorted(set(expected_agents) - {path.name for path in agent_dir.glob("*.toml")}) if agent_dir.is_dir() else sorted(expected_agents)
239
+ if missing_agents:
240
+ mark_fail(f"required Codex agent files missing: {', '.join(missing_agents)}")
241
+
242
+ for filename, (expected_name, expected_model, expected_effort) in expected_agents.items():
243
+ path = agent_dir / filename
244
+ if not path.is_file():
245
+ continue
246
+ try:
247
+ data = tomllib.loads(path.read_text())
248
+ except Exception as exc:
249
+ mark_fail(f"agent TOML does not parse: {path} ({exc})")
250
+ continue
251
+ if data.get("name") != expected_name:
252
+ mark_fail(f"{path} name is {data.get('name')!r}, want {expected_name!r}")
253
+ for required_text_field in ("description", "developer_instructions"):
254
+ value = data.get(required_text_field)
255
+ if not isinstance(value, str) or not value.strip():
256
+ mark_fail(f"{path} requires non-empty {required_text_field}")
257
+ if data.get("model") != expected_model:
258
+ mark_fail(f"{path} model is {data.get('model')!r}, want {expected_model!r}")
259
+ if expected_effort is None and "model_reasoning_effort" in data:
260
+ mark_fail(f"{path} must omit model_reasoning_effort so HELM can pin task-fit effort per dispatch")
261
+ elif expected_effort is not None and data.get("model_reasoning_effort") != expected_effort:
262
+ mark_fail(
263
+ f"{path} effort is {data.get('model_reasoning_effort')!r}, "
264
+ f"want {expected_effort!r}"
265
+ )
266
+
267
+
268
+ def table_row(path, slot):
269
+ for raw_line in path.read_text().splitlines():
270
+ line = raw_line.strip()
271
+ if not line.startswith("|") or not line.endswith("|"):
272
+ continue
273
+ cells = [cell.strip() for cell in line.strip("|").split("|")]
274
+ if cells and cells[0] == slot:
275
+ return cells
276
+ return None
277
+
278
+
279
+ def has_exact_model(text, model):
280
+ return re.search(rf"(?<![\w.-]){re.escape(model)}(?![\w.-])", text) is not None
281
+
282
+
283
+ for guide_name in [
284
+ "claude/guides/cli-multi-model-workflow.md",
285
+ "codex/guides/cli-multi-model-workflow.md",
286
+ "ko/claude/guides/cli-multi-model-workflow.md",
287
+ "ko/codex/guides/cli-multi-model-workflow.md",
288
+ ]:
289
+ guide = pathlib.Path(guide_name)
290
+ rows = {slot: table_row(guide, slot) for slot in ("FRONTIER", "HELM", "WORKHORSE", "SWEEP")}
291
+ for slot, row in rows.items():
292
+ if row is None or len(row) < 2:
293
+ mark_fail(f"{guide} missing Environment Binding row for {slot}")
294
+ if rows["FRONTIER"] and (
295
+ not has_exact_model(rows["FRONTIER"][1], "GPT-5.6 Sol")
296
+ or "max" not in rows["FRONTIER"][1]
297
+ or "Ultra" not in rows["FRONTIER"][1]
298
+ ):
299
+ mark_fail(f"{guide} FRONTIER must bind GPT-5.6 Sol at max with Ultra allowed")
300
+ if rows["HELM"] and (
301
+ not has_exact_model(rows["HELM"][1], "GPT-5.6 Sol")
302
+ or "xhigh" not in rows["HELM"][1]
303
+ or "bounded FRONTIER Ultra" not in rows["HELM"][1]
304
+ or not any(token in rows["HELM"][1] for token in ("explicit selection", "명시 선택"))
305
+ ):
306
+ mark_fail(f"{guide} HELM must keep the main at GPT-5.6 Sol xhigh, require explicit main Ultra selection, and allow bounded FRONTIER Ultra dispatch")
307
+ if rows["WORKHORSE"] and (
308
+ not has_exact_model(rows["WORKHORSE"][1], "GPT-5.6 Terra")
309
+ or "(high)" not in rows["WORKHORSE"][1]
310
+ ):
311
+ mark_fail(f"{guide} WORKHORSE must bind exact GPT-5.6 Terra (high)")
312
+ if rows["SWEEP"] and not has_exact_model(rows["SWEEP"][1], "GPT-5.6 Luna"):
313
+ mark_fail(f"{guide} SWEEP must bind exact GPT-5.6 Luna")
314
+ for slot, claude_model in (
315
+ ("FRONTIER", "Claude Fable 5"),
316
+ ("HELM", "Claude Opus 4.8"),
317
+ ("WORKHORSE", "Claude Sonnet 5"),
318
+ ("SWEEP", "Claude Haiku 4.5"),
319
+ ):
320
+ if rows[slot] and claude_model not in rows[slot][1]:
321
+ mark_fail(f"{guide} {slot} must bind exact {claude_model}")
322
+
323
+ def invoke(argv, *, input_text=None, env=None):
324
+ return subprocess.run(argv, input=input_text, capture_output=True, text=True, env=env)
325
+
326
+
327
+ def expect_text(result, label, present=(), absent=(), status=0):
328
+ if result.returncode != status:
329
+ mark_fail(f"{label} returned {result.returncode}, want {status}: {result.stderr.strip()}")
330
+ return False
331
+ for token in present:
332
+ if token not in result.stdout:
333
+ mark_fail(f"{label} missing {token!r}")
334
+ for token in absent:
335
+ if token in result.stdout:
336
+ mark_fail(f"{label} unexpectedly contains {token!r}")
337
+ return True
338
+
339
+
340
+ helm = pathlib.Path("scripts/codex-helm.sh")
341
+
342
+
343
+ def helm_dry(*args):
344
+ return invoke(["bash", str(helm), "--dry-run", "--reach", "hermetic", *args, "probe"])
345
+
346
+
347
+ if not helm.is_file():
348
+ mark_fail("required file missing: scripts/codex-helm.sh")
349
+ else:
350
+ if "Expert Codex config override" not in helm.read_text():
351
+ mark_fail("codex-helm must document -c as an expert override")
352
+ frontier_tokens = (
353
+ "Do not use native spawn_agent for FRONTIER",
354
+ "--model gpt-5.6-sol --effort <effort> --multi-agent auto --sandbox read-only",
355
+ "adapter enables native multi-agent only for ultra",
356
+ "agents.max_threads=4",
357
+ "agents.max_depth=1",
358
+ )
359
+ for mode in ("auto", "implement", "review", "scout", "single"):
360
+ result = helm_dry("--mode", mode)
361
+ if not expect_text(
362
+ result,
363
+ f"codex-helm {mode} dry-run",
364
+ ("--bypass-sandbox", "features.multi_agent=false"),
365
+ ("explicitly selected Codex Ultra",),
366
+ ):
367
+ continue
368
+ match = re.search(rf"^mode={mode} reach=hermetic model=(\S+) effort=(\S+) sandbox=(\S+) ", result.stdout, re.MULTILINE)
369
+ if not match or match.groups() != ("gpt-5.6-sol", "xhigh", "dangerously-bypass-approvals-and-sandbox"):
370
+ mark_fail(f"codex-helm {mode} defaults are missing or incorrect")
371
+ has_adapter = "Internal FRONTIER command base" in result.stdout
372
+ if has_adapter != (mode != "single"):
373
+ mark_fail(f"codex-helm {mode} FRONTIER adapter exposure is incorrect")
374
+ if mode != "single":
375
+ for token in frontier_tokens:
376
+ if token not in result.stdout:
377
+ mark_fail(f"codex-helm {mode} missing FRONTIER contract token: {token}")
378
+
379
+ expect_text(
380
+ helm_dry("--effort", "ultra"),
381
+ "codex-helm Ultra dry-run",
382
+ ("explicitly selected Codex Ultra", "features.multi_agent=true"),
383
+ )
384
+ single_ultra = helm_dry("--mode", "single", "--effort", "ultra")
385
+ if single_ultra.returncode == 0 or "Ultra requires fan-out" not in single_ultra.stderr:
386
+ mark_fail("codex-helm single + Ultra must fail with the fan-out explanation")
387
+ expect_text(
388
+ helm_dry("--no-fanout"),
389
+ "codex-helm no-fanout dry-run",
390
+ ("Do not spawn subagents",),
391
+ ("Internal FRONTIER command base",),
392
+ )
393
+ limited = helm_dry("--max-threads", "2", "--max-depth", "2")
394
+ expect_text(
395
+ limited,
396
+ "codex-helm FRONTIER limits",
397
+ ("Internal FRONTIER command base",),
398
+ )
399
+ frontier_line = next((line for line in limited.stdout.splitlines() if line.startswith("- Internal FRONTIER command base:")), "")
400
+ if "agents.max_threads=2" not in frontier_line or "agents.max_depth=2" not in frontier_line:
401
+ mark_fail("codex-helm FRONTIER command does not carry the requested 2/2 limits")
402
+ for args in (
403
+ ("--sandbox", "read-only"),
404
+ ("--sandbox", "read-only", "--allow-danger"),
405
+ ("--allow-danger", "--sandbox", "read-only"),
406
+ ):
407
+ expect_text(
408
+ helm_dry(*args),
409
+ f"codex-helm explicit sandbox order {' '.join(args)}",
410
+ ("sandbox=read-only",),
411
+ ("--bypass-sandbox",),
412
+ )
413
+
414
+ run = pathlib.Path("scripts/codex-run.sh")
415
+ if not run.is_file():
416
+ mark_fail("required file missing: scripts/codex-run.sh")
417
+ else:
418
+ with tempfile.TemporaryDirectory() as raw_tmp:
419
+ tmp = pathlib.Path(raw_tmp)
420
+ fake, argv_log = tmp / "codex", tmp / "argv"
421
+ fake.write_text(
422
+ "#!/usr/bin/env bash\n"
423
+ "printf '%s\\n' \"$@\" > \"$FAKE_CODEX_ARGV\"\n"
424
+ "printf 'progress-err\\n' >&2\n"
425
+ "printf 'final-out\\n'\n"
426
+ "exit \"${FAKE_CODEX_STATUS:-0}\"\n"
427
+ )
428
+ fake.chmod(0o755)
429
+ env = os.environ.copy()
430
+ env.update(PATH=f"{tmp}{os.pathsep}{env.get('PATH', '')}", FAKE_CODEX_ARGV=str(argv_log))
431
+
432
+ def fake_call(argv):
433
+ result = invoke(argv, input_text="probe\n", env=env)
434
+ return result, argv_log.read_text().splitlines()
435
+
436
+ def expect_args(label, received, present=(), absent=(), pair=None):
437
+ for token in present:
438
+ if token not in received:
439
+ mark_fail(f"{label} missing argument: {token}")
440
+ for token in absent:
441
+ if token in received:
442
+ mark_fail(f"{label} unexpectedly passed argument: {token}")
443
+ if pair and not any(received[i : i + 2] == list(pair) for i in range(len(received) - 1)):
444
+ mark_fail(f"{label} missing argument pair: {' '.join(pair)}")
445
+
446
+ result, received = fake_call([
447
+ "bash", str(run), "--profile", "inherit", "--bypass-sandbox",
448
+ "--model", "gpt-5.6-sol", "--effort", "max", "-",
449
+ ])
450
+ if (result.returncode, result.stdout, result.stderr) != (0, "final-out\n", "progress-err\n"):
451
+ mark_fail("codex-run does not preserve stdout/stderr/exit channels")
452
+ expect_args(
453
+ "codex-run bypass",
454
+ received,
455
+ ("--dangerously-bypass-approvals-and-sandbox", "gpt-5.6-sol", 'model_reasoning_effort="max"'),
456
+ ("--sandbox",),
457
+ )
458
+ result, received = fake_call(["bash", str(run), "--profile", "inherit", "--sandbox", "read-only"])
459
+ expect_args(
460
+ "codex-run explicit sandbox",
461
+ received,
462
+ absent=("--dangerously-bypass-approvals-and-sandbox",),
463
+ pair=("--sandbox", "read-only"),
464
+ )
465
+ if result.returncode != 0:
466
+ mark_fail("codex-run explicit sandbox fake runtime failed")
467
+
468
+ for effort, multi_agent in (("max", "false"), ("ultra", "true")):
469
+ result, received = fake_call([
470
+ "bash", str(run), "--profile", "inherit", "--effort", effort, "--multi-agent", "auto",
471
+ ])
472
+ if result.returncode != 0:
473
+ mark_fail(f"codex-run multi-agent auto failed at {effort}")
474
+ expect_args(
475
+ f"codex-run multi-agent auto {effort}",
476
+ received,
477
+ (f'model_reasoning_effort="{effort}"', f"features.multi_agent={multi_agent}"),
478
+ )
479
+
480
+ for effort, multi_agent in (("xhigh", "false"), ("ultra", "true")):
481
+ result, received = fake_call(["bash", str(helm), "--reach", "inherit", "--effort", effort, "probe"])
482
+ if result.returncode != 0:
483
+ mark_fail(f"codex-helm fake runtime failed at effort {effort}: {result.stderr.strip()}")
484
+ expect_args(
485
+ f"codex-helm {effort}",
486
+ received,
487
+ ("--dangerously-bypass-approvals-and-sandbox", f'model_reasoning_effort="{effort}"', f"features.multi_agent={multi_agent}"),
488
+ ("--sandbox",),
489
+ )
490
+
491
+ env["FAKE_CODEX_STATUS"] = "7"
492
+ failed, _ = fake_call(["bash", str(run), "--profile", "inherit", "--bypass-sandbox"])
493
+ if failed.returncode != 7:
494
+ mark_fail(f"codex-run returns {failed.returncode} instead of Codex exit status 7")
495
+
496
+ if launcher.is_file() and launch_profile:
497
+ syntax = invoke([sys.executable, "-c", f"compile(open({str(launcher)!r}).read(), {str(launcher)!r}, 'exec')"])
498
+ if syntax.returncode != 0:
499
+ mark_fail(f"agent-launch Python syntax failed: {syntax.stderr.strip()}")
500
+ zsh_syntax = invoke(["zsh", "-n", str(shell_init)])
501
+ if zsh_syntax.returncode != 0:
502
+ mark_fail(f"agent-launch zsh syntax failed: {zsh_syntax.stderr.strip()}")
503
+
504
+ try:
505
+ spec = importlib.util.spec_from_file_location("agent_launch_under_test", launcher)
506
+ if spec is None or spec.loader is None:
507
+ raise RuntimeError("could not create import specification")
508
+ launcher_module = importlib.util.module_from_spec(spec)
509
+ sys.modules[spec.name] = launcher_module
510
+ spec.loader.exec_module(launcher_module)
511
+ except Exception as exc:
512
+ mark_fail(f"agent-launch test import failed: {exc}")
513
+ launcher_module = None
514
+
515
+ if launcher_module is not None:
516
+ class ScriptedUI:
517
+ def __init__(self):
518
+ self.plan = None
519
+ self.checked = False
520
+ self.hub_visits = 0
521
+
522
+ def set_plan(self, plan):
523
+ self.plan = plan
524
+
525
+ def choose(self, title, options, default, allow_back, preview=None):
526
+ if title == "Preset":
527
+ if preview is not None:
528
+ balanced = launcher_module.setup_summary_lines(preview("balanced"))
529
+ deep = launcher_module.setup_summary_lines(preview("deep-review"))
530
+ if balanced == deep:
531
+ mark_fail(
532
+ "agent-launch Preset menu does not live-preview the "
533
+ "highlighted preset in the setup panel"
534
+ )
535
+ return launcher_module.CUSTOM_PRESET
536
+ if title == "Custom settings":
537
+ self.hub_visits += 1
538
+ if self.hub_visits == 1:
539
+ return "main"
540
+ summary = launcher_module.setup_summary_lines(self.plan)
541
+ option_by_value = {option.value: option for option in options}
542
+ if "Main WORKHORSE | Review native-panel" not in summary:
543
+ mark_fail(
544
+ "agent-launch did not project the selected main tier back "
545
+ "into the Custom hub"
546
+ )
547
+ if not {"tier:frontier", "tier:workhorse", "start", "exit"} <= set(option_by_value):
548
+ mark_fail("agent-launch Custom hub is missing tier or final actions")
549
+ if (
550
+ option_by_value["start"].label != "Start with these settings"
551
+ or option_by_value["start"].description
552
+ != "Confirm the complete setup shown above and continue to launch."
553
+ or option_by_value["exit"].label != "Exit without launching"
554
+ or option_by_value["exit"].description
555
+ != "Discard this launch and return to the shell."
556
+ ):
557
+ mark_fail("agent-launch Custom final actions have mismatched semantics")
558
+ self.checked = True
559
+ return "start"
560
+ if title == "Main tier":
561
+ if preview is not None:
562
+ summary = launcher_module.setup_summary_lines(preview("sweep"))
563
+ if "Main SWEEP | Review native-panel" not in summary:
564
+ mark_fail(
565
+ "agent-launch Main tier menu does not live-preview the "
566
+ "highlighted tier in the setup panel"
567
+ )
568
+ return "workhorse"
569
+ raise AssertionError(f"unexpected scripted menu: {title}")
570
+
571
+ scripted_ui = ScriptedUI()
572
+ scripted_plan = launcher_module.select_plan(
573
+ launch_profile,
574
+ "codex",
575
+ None,
576
+ False,
577
+ scripted_ui,
578
+ )
579
+ if not scripted_ui.checked or not scripted_plan.get("_launch_confirmed"):
580
+ mark_fail("agent-launch scripted Custom hub did not reach final confirmation")
581
+
582
+ with tempfile.TemporaryDirectory() as raw_tmp:
583
+ tmp = pathlib.Path(raw_tmp)
584
+ backend = tmp / "backend"
585
+ argv_log = tmp / "backend.argv"
586
+ backend.write_text(
587
+ "#!/usr/bin/env bash\n"
588
+ "printf '%s\\n' \"$@\" > \"$FAKE_LAUNCH_ARGV\"\n"
589
+ "printf 'backend-output\\n'\n"
590
+ "exit \"${FAKE_LAUNCH_STATUS:-0}\"\n"
591
+ )
592
+ backend.chmod(0o755)
593
+ fake_onto = tmp / "onto"
594
+ fake_ultracode = tmp / "ultracode-for-codex"
595
+ for capability in (fake_onto, fake_ultracode):
596
+ capability.write_text("#!/usr/bin/env bash\nexit 0\n")
597
+ capability.chmod(0o755)
598
+ fake_profile = tmp / "profiles.toml"
599
+ profile_text = launch_profile_path.read_text()
600
+ profile_text = profile_text.replace('command = "codex"', f"command = {json.dumps(str(backend))}", 1)
601
+ profile_text = profile_text.replace('command = "claude"', f"command = {json.dumps(str(backend))}", 1)
602
+ profile_text = profile_text.replace('command = "onto"', f"command = {json.dumps(str(fake_onto))}", 1)
603
+ profile_text = profile_text.replace(
604
+ 'command = "ultracode-for-codex"',
605
+ f"command = {json.dumps(str(fake_ultracode))}",
606
+ 1,
607
+ )
608
+ portable_profile_text = profile_text
609
+ for tier in ("frontier", "workhorse", "sweep"):
610
+ profile_text = profile_text.replace(
611
+ f'${{CODEX_HOME}}/agents/{tier}.toml', str((agent_dir / f"{tier}.toml").resolve()), 1
612
+ )
613
+ fake_profile.write_text(profile_text)
614
+ fake_data = tomllib.loads(profile_text)
615
+ fake_commands = {
616
+ host: binding.get("command") for host, binding in fake_data.get("backends", {}).items()
617
+ }
618
+ if set(fake_commands) != {"codex", "claude"} or any(
619
+ command != str(backend) for command in fake_commands.values()
620
+ ):
621
+ mark_fail(f"fake launch profile did not replace every backend: {fake_commands!r}")
622
+ fake_profile = pathlib.Path("/nonexistent/unsafe-fixture")
623
+ env = os.environ.copy()
624
+ env.update(
625
+ FAKE_LAUNCH_ARGV=str(argv_log),
626
+ AGENT_LAUNCH_CONFIG=str(fake_profile),
627
+ XDG_CACHE_HOME=str(tmp / "cache"),
628
+ )
629
+
630
+ pty_env = env.copy()
631
+
632
+ def run_picker_scenario(
633
+ name,
634
+ steps,
635
+ timeout=3,
636
+ term="xterm-256color",
637
+ host="codex",
638
+ config_path=fake_profile,
639
+ dry_run=True,
640
+ ):
641
+ scenario_env = pty_env.copy()
642
+ scenario_env["TERM"] = term
643
+ picker_pid, master = pty.fork()
644
+ if picker_pid == 0:
645
+ try:
646
+ child_argv = [
647
+ sys.executable,
648
+ str(launcher),
649
+ "--config",
650
+ str(config_path),
651
+ ]
652
+ if dry_run:
653
+ child_argv.append("--dry-run")
654
+ child_argv.append(host)
655
+ os.execve(
656
+ sys.executable,
657
+ child_argv,
658
+ scenario_env,
659
+ )
660
+ except Exception:
661
+ os._exit(127)
662
+ fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack("HHHH", 24, 100, 0, 0))
663
+ transcript = bytearray()
664
+ picker_status = None
665
+ search_offset = 0
666
+
667
+ def read_until(token):
668
+ nonlocal search_offset
669
+ deadline = time.monotonic() + timeout
670
+ while time.monotonic() < deadline:
671
+ found = transcript.find(token, search_offset)
672
+ if found >= 0:
673
+ search_offset = found + len(token)
674
+ return True
675
+ ready, _, _ = select.select([master], [], [], 0.1)
676
+ if not ready:
677
+ continue
678
+ try:
679
+ chunk = os.read(master, 4096)
680
+ except OSError:
681
+ break
682
+ if not chunk:
683
+ break
684
+ transcript.extend(chunk)
685
+ found = transcript.find(token, search_offset)
686
+ if found >= 0:
687
+ search_offset = found + len(token)
688
+ return True
689
+ return False
690
+
691
+ try:
692
+ for expected, key in steps:
693
+ if not read_until(expected):
694
+ mark_fail(f"agent-launch {name} missing {expected.decode()!r}")
695
+ break
696
+ if isinstance(key, tuple):
697
+ rows, columns = key[:2]
698
+ if len(key) == 3:
699
+ os.write(master, key[2])
700
+ time.sleep(0.1)
701
+ fcntl.ioctl(
702
+ master,
703
+ termios.TIOCSWINSZ,
704
+ struct.pack("HHHH", rows, columns, 0, 0),
705
+ )
706
+ try:
707
+ os.kill(picker_pid, signal.SIGWINCH)
708
+ except ProcessLookupError:
709
+ pass
710
+ else:
711
+ os.write(master, key)
712
+ deadline = time.monotonic() + timeout
713
+ while time.monotonic() < deadline:
714
+ ready, _, _ = select.select([master], [], [], 0.05)
715
+ if ready:
716
+ try:
717
+ chunk = os.read(master, 4096)
718
+ except OSError:
719
+ chunk = b""
720
+ if chunk:
721
+ transcript.extend(chunk)
722
+ waited_pid, raw_status = os.waitpid(picker_pid, os.WNOHANG)
723
+ if waited_pid:
724
+ picker_status = os.waitstatus_to_exitcode(raw_status)
725
+ break
726
+ if picker_status is not None:
727
+ while select.select([master], [], [], 0)[0]:
728
+ try:
729
+ chunk = os.read(master, 4096)
730
+ except OSError:
731
+ break
732
+ if not chunk:
733
+ break
734
+ transcript.extend(chunk)
735
+ finally:
736
+ if picker_status is None:
737
+ try:
738
+ os.kill(picker_pid, signal.SIGKILL)
739
+ except ProcessLookupError:
740
+ pass
741
+ _, raw_status = os.waitpid(picker_pid, 0)
742
+ picker_status = os.waitstatus_to_exitcode(raw_status)
743
+ os.close(master)
744
+ return bytes(transcript), picker_status
745
+
746
+ open_custom_steps = (
747
+ (b"native multi-perspective review", b""),
748
+ (b"Esc cancel | q cancel", b"\x1b[B"),
749
+ (b"hybrid onto", b"\x1b[B"),
750
+ (b"high-volume", b"\x1b[B"),
751
+ (b"Open a settings hub", b"\r"),
752
+ )
753
+ down = b"\x1b[B"
754
+
755
+ def hub_choice(offset):
756
+ return (b"Custom settings", down * offset + b"\r")
757
+
758
+ transcript, picker_status = run_picker_scenario(
759
+ "fixed layout",
760
+ ((b"Esc cancel | q cancel", b"q"),),
761
+ )
762
+ if picker_status != 130:
763
+ mark_fail(f"agent-launch fixed layout returned {picker_status}, want 130")
764
+ layout_tokens = (
765
+ b"Current setup",
766
+ b"FRONTIER",
767
+ b"HELM",
768
+ b"WORKHORSE",
769
+ b"SWEEP",
770
+ b"About highlighted option",
771
+ b"HELM default for everyday work",
772
+ b"Options (",
773
+ )
774
+ positions = {token: transcript.find(token) for token in layout_tokens}
775
+ if any(position < 0 for position in positions.values()):
776
+ missing = [token.decode() for token, position in positions.items() if position < 0]
777
+ mark_fail(f"agent-launch fixed layout missing sections: {missing!r}")
778
+ elif not (
779
+ positions[b"Current setup"]
780
+ < min(positions[token] for token in (b"FRONTIER", b"HELM", b"WORKHORSE", b"SWEEP"))
781
+ < positions[b"About highlighted option"]
782
+ < positions[b"HELM default for everyday work"]
783
+ < positions[b"Options ("]
784
+ ):
785
+ mark_fail("agent-launch layout is not setup then description then options")
786
+
787
+ _, picker_status = run_picker_scenario(
788
+ "root escape cancellation",
789
+ ((b"Esc cancel | q cancel", b"\x1b"),),
790
+ )
791
+ if picker_status != 130:
792
+ mark_fail(f"agent-launch root Esc returned {picker_status}, want 130")
793
+
794
+ if argv_log.exists():
795
+ argv_log.unlink()
796
+ _, picker_status = run_picker_scenario(
797
+ "launch confirmation q cancellation",
798
+ (
799
+ (b"Esc cancel | q cancel", b"\r"),
800
+ (b"Launch? [Y/n/q]:", b"q\r"),
801
+ ),
802
+ dry_run=False,
803
+ )
804
+ if picker_status != 130 or argv_log.exists():
805
+ mark_fail("agent-launch final confirmation q executed the backend")
806
+
807
+ if argv_log.exists():
808
+ argv_log.unlink()
809
+ transcript, picker_status = run_picker_scenario(
810
+ "Custom final start",
811
+ (
812
+ *open_custom_steps,
813
+ (b"Custom settings", down * 8),
814
+ (b"Confirm the complete setup shown above", b"\r"),
815
+ ),
816
+ dry_run=False,
817
+ )
818
+ if (
819
+ picker_status != 0
820
+ or not argv_log.exists()
821
+ or b"Launch? [Y/n/q]:" in transcript
822
+ or b"Start with these settings" not in transcript
823
+ ):
824
+ mark_fail("agent-launch Custom Start was not the final launch confirmation")
825
+
826
+ if argv_log.exists():
827
+ argv_log.unlink()
828
+ transcript, picker_status = run_picker_scenario(
829
+ "Custom explicit exit",
830
+ (
831
+ *open_custom_steps,
832
+ (b"Custom settings", down * 9),
833
+ (b"Discard this launch and return to the shell", b"\r"),
834
+ ),
835
+ dry_run=False,
836
+ )
837
+ if (
838
+ picker_status != 130
839
+ or argv_log.exists()
840
+ or b"Exit without launching" not in transcript
841
+ ):
842
+ mark_fail("agent-launch Custom Exit executed the backend")
843
+
844
+ transcript, picker_status = run_picker_scenario(
845
+ "Custom escape back",
846
+ (
847
+ *open_custom_steps,
848
+ (b"Custom settings", b"\x1b"),
849
+ (b"HELM default for everyday work", b"\r"),
850
+ ),
851
+ )
852
+ if picker_status != 0 or b"Preset Balanced" not in transcript:
853
+ mark_fail("agent-launch Custom Esc did not return to the preset picker")
854
+
855
+ _, picker_status = run_picker_scenario(
856
+ "Custom interior escape back",
857
+ (
858
+ *open_custom_steps,
859
+ (b"Custom settings", b"\r"),
860
+ (b"Primary orchestrator for planning, delegation", b"\x1b"),
861
+ (b"Choose the primary orchestrator used for this session", b"q"),
862
+ ),
863
+ )
864
+ if picker_status != 130:
865
+ mark_fail(
866
+ f"agent-launch interior Esc back returned {picker_status}, want 130"
867
+ )
868
+
869
+ _, picker_status = run_picker_scenario(
870
+ "current setup update",
871
+ (
872
+ *open_custom_steps,
873
+ (b"Custom settings", b"\r"),
874
+ (b"Primary orchestrator for planning, delegation", b"\x1b[B"),
875
+ (b"high-volume implementation", b"\r"),
876
+ (b"Main tier: WORKHORSE", b"q"),
877
+ ),
878
+ )
879
+ if picker_status != 130:
880
+ mark_fail("agent-launch main-tier selection flow did not reach Review setup")
881
+
882
+ transcript, picker_status = run_picker_scenario(
883
+ "Custom tier hub return",
884
+ (
885
+ *open_custom_steps,
886
+ hub_choice(3),
887
+ (b"Other (enter a model id)", down + b"\r"),
888
+ (b"strongest standard reasoning mode", down + b"\r"),
889
+ (b"FRONTIER: gpt-5.6-terra / ultra", down * 2 + b"\r"),
890
+ (b"Other (enter a model id)", down * 2 + b"\r"),
891
+ (b"Current value: gpt-5.6-terra", b"workhorse-local\r"),
892
+ (b"complex implementation and analysis", down + b"\r"),
893
+ (b"WORKHORSE: workhorse-local / xhigh", down * 3 + b"\r"),
894
+ ),
895
+ )
896
+ if picker_status != 0:
897
+ mark_fail(
898
+ f"agent-launch tier-to-hub flow returned {picker_status}, want 0"
899
+ )
900
+ for expected in (
901
+ "FRONTIER gpt-5.6-terra · ultra".encode(),
902
+ "WORKHORSE workhorse-local · xhigh".encode(),
903
+ ):
904
+ if expected not in transcript:
905
+ mark_fail(
906
+ "agent-launch tier-to-hub flow did not visit "
907
+ f"{expected.decode()!r}"
908
+ )
909
+
910
+ numbered_interior_back = invoke(
911
+ [
912
+ sys.executable,
913
+ str(launcher),
914
+ "--config",
915
+ str(fake_profile),
916
+ "--preset",
917
+ "balanced",
918
+ "--custom",
919
+ "--dry-run",
920
+ "codex",
921
+ ],
922
+ input_text="1\nb\n9\n",
923
+ env=env,
924
+ )
925
+ if (
926
+ numbered_interior_back.returncode != 0
927
+ or "b back | q cancel" not in numbered_interior_back.stdout
928
+ or numbered_interior_back.stdout.count("\nCustom settings\n") < 2
929
+ or "Preset Custom (Balanced)" not in numbered_interior_back.stdout
930
+ ):
931
+ mark_fail("agent-launch numbered interior back did not return to Custom hub")
932
+
933
+ _, picker_status = run_picker_scenario(
934
+ "Custom model q cancellation",
935
+ (
936
+ *open_custom_steps,
937
+ hub_choice(3),
938
+ (b"Other (enter a model id)", down * 3 + b"\r"),
939
+ (b"Current value: gpt-5.6-sol", b"q\r"),
940
+ ),
941
+ )
942
+ if picker_status != 130:
943
+ mark_fail(
944
+ f"agent-launch curses model q returned {picker_status}, want 130"
945
+ )
946
+
947
+ _, picker_status = run_picker_scenario(
948
+ "Custom model Esc cancellation",
949
+ (
950
+ *open_custom_steps,
951
+ hub_choice(3),
952
+ (b"Other (enter a model id)", down * 3 + b"\r"),
953
+ (b"Current value: gpt-5.6-sol", b"\x1b"),
954
+ ),
955
+ )
956
+ if picker_status != 130:
957
+ mark_fail(
958
+ f"agent-launch curses model Esc returned {picker_status}, want 130"
959
+ )
960
+
961
+ _, picker_status = run_picker_scenario(
962
+ "Custom q-prefixed model preservation",
963
+ (
964
+ *open_custom_steps,
965
+ hub_choice(3),
966
+ (b"Other (enter a model id)", down * 3 + b"\r"),
967
+ (b"Current value: gpt-5.6-sol", b"qwen\r"),
968
+ (b"Options (", b"\x1b"),
969
+ (b"Other (enter a model id)", b"\r"),
970
+ (b"Current value: qwen", b"\x1b"),
971
+ ),
972
+ )
973
+ if picker_status != 130:
974
+ mark_fail(
975
+ f"agent-launch q-prefixed model returned {picker_status}, want 130"
976
+ )
977
+
978
+ _, picker_status = run_picker_scenario(
979
+ "Custom model Ctrl-C cancellation",
980
+ (
981
+ *open_custom_steps,
982
+ hub_choice(3),
983
+ (b"Other (enter a model id)", down * 3 + b"\r"),
984
+ (b"Current value: gpt-5.6-sol", b"\x03"),
985
+ ),
986
+ )
987
+ if picker_status != 130:
988
+ mark_fail(
989
+ f"agent-launch curses model Ctrl-C returned {picker_status}, want 130"
990
+ )
991
+
992
+ numbered_model_q = invoke(
993
+ [
994
+ sys.executable,
995
+ str(launcher),
996
+ "--config",
997
+ str(fake_profile),
998
+ "--preset",
999
+ "balanced",
1000
+ "--custom",
1001
+ "--dry-run",
1002
+ "codex",
1003
+ ],
1004
+ input_text="4\nq\n",
1005
+ env=env,
1006
+ )
1007
+ if numbered_model_q.returncode != 130 or "Traceback" in numbered_model_q.stderr:
1008
+ mark_fail("agent-launch numbered model q did not cancel cleanly")
1009
+
1010
+ _, picker_status = run_picker_scenario(
1011
+ "arrow picker cancellation",
1012
+ (*open_custom_steps, (b"Custom settings", b"\x03")),
1013
+ )
1014
+ if picker_status != 130:
1015
+ mark_fail(f"agent-launch arrow picker returned {picker_status}, want 130")
1016
+
1017
+ transcript, picker_status = run_picker_scenario(
1018
+ "Custom completion",
1019
+ (*open_custom_steps, hub_choice(8)),
1020
+ )
1021
+ if picker_status != 0:
1022
+ mark_fail(f"agent-launch Custom completion returned {picker_status}, want 0")
1023
+ for expected in (
1024
+ b"Preset Custom (Balanced)",
1025
+ b"Main HELM",
1026
+ b"Review setup native-panel",
1027
+ b"Execution Codex bypass",
1028
+ ):
1029
+ if expected not in transcript:
1030
+ mark_fail(f"agent-launch Custom projection missing {expected.decode()!r}")
1031
+
1032
+ claude_auto_profile = tmp / "claude-auto.toml"
1033
+ claude_auto_profile.write_text(
1034
+ profile_text.replace(
1035
+ 'claude_permission_mode = "bypassPermissions"',
1036
+ 'claude_permission_mode = "auto"',
1037
+ 1,
1038
+ )
1039
+ )
1040
+ transcript, picker_status = run_picker_scenario(
1041
+ "Claude Custom auto preservation",
1042
+ (*open_custom_steps, hub_choice(8)),
1043
+ host="claude",
1044
+ config_path=claude_auto_profile,
1045
+ )
1046
+ if picker_status != 0:
1047
+ mark_fail(
1048
+ f"agent-launch Claude Custom auto returned {picker_status}, want 0"
1049
+ )
1050
+ for expected in (
1051
+ b"Preset Custom (Balanced)",
1052
+ b"Execution Claude auto",
1053
+ b'"--permission-mode", "auto"',
1054
+ ):
1055
+ if expected not in transcript:
1056
+ mark_fail(
1057
+ "agent-launch Claude Custom auto projection missing "
1058
+ f"{expected.decode()!r}"
1059
+ )
1060
+ if b'"--dangerously-skip-permissions"' in transcript:
1061
+ mark_fail("agent-launch Claude Custom auto escalated to permission bypass")
1062
+
1063
+ claude_dont_ask_profile = tmp / "claude-dont-ask.toml"
1064
+ claude_dont_ask_profile.write_text(
1065
+ profile_text.replace(
1066
+ 'claude_permission_mode = "bypassPermissions"',
1067
+ 'claude_permission_mode = "dontAsk"',
1068
+ 1,
1069
+ )
1070
+ )
1071
+ dont_ask = invoke(
1072
+ [
1073
+ sys.executable,
1074
+ str(launcher),
1075
+ "--config",
1076
+ str(claude_dont_ask_profile),
1077
+ "--preset",
1078
+ "balanced",
1079
+ "--custom",
1080
+ "--dry-run",
1081
+ "claude",
1082
+ ],
1083
+ input_text="9\n",
1084
+ env=env,
1085
+ )
1086
+ if dont_ask.returncode != 0:
1087
+ mark_fail(
1088
+ "agent-launch numbered Claude Custom dontAsk failed: "
1089
+ f"{dont_ask.stderr.strip()}"
1090
+ )
1091
+ else:
1092
+ dont_ask_argv = json.loads(dont_ask.stdout.splitlines()[-1])
1093
+ if dont_ask_argv[-2:] != ["--permission-mode", "dontAsk"]:
1094
+ mark_fail(
1095
+ "agent-launch numbered Claude Custom did not preserve dontAsk"
1096
+ )
1097
+
1098
+ # TERM=dumb routes to numbered prompts (textual renders on any usable
1099
+ # terminal, so the numbered fallback is gated on TERM/non-TTY/textual
1100
+ # availability, not a terminfo probe). 'b' is the numbered back command.
1101
+ transcript, picker_status = run_picker_scenario(
1102
+ "numbered fallback (TERM=dumb)",
1103
+ (
1104
+ (b"Open a settings hub", b"4\n"),
1105
+ (b"Custom settings", b"b\n"),
1106
+ (b"HELM default for everyday work", b"1\n"),
1107
+ ),
1108
+ term="dumb",
1109
+ )
1110
+ if (
1111
+ picker_status != 0
1112
+ or b"Traceback" in transcript
1113
+ or b"Preset Balanced" not in transcript
1114
+ ):
1115
+ mark_fail("agent-launch numbered fallback (TERM=dumb) did not preserve numbered back")
1116
+
1117
+ passed = invoke([
1118
+ sys.executable, str(launcher), "--no-tui", "codex", "--", "exec", "--json", "probe"
1119
+ ], env=env)
1120
+ if passed.returncode != 0 or argv_log.read_text().splitlines() != ["exec", "--json", "probe"]:
1121
+ mark_fail("agent-launch Codex argument-bearing bypass changed forwarded argv")
1122
+
1123
+ passed = invoke([
1124
+ sys.executable, str(launcher), "--no-tui", "claude", "--", "-p", "probe"
1125
+ ], env=env)
1126
+ if passed.returncode != 0 or argv_log.read_text().splitlines() != [
1127
+ "--dangerously-skip-permissions", "-p", "probe"
1128
+ ]:
1129
+ mark_fail("agent-launch Claude bypass did not preserve the existing backend default")
1130
+
1131
+ env["FAKE_LAUNCH_STATUS"] = "9"
1132
+ failed = invoke([sys.executable, str(launcher), "--no-tui", "codex", "--", "probe"], env=env)
1133
+ if failed.returncode != 9:
1134
+ mark_fail(f"agent-launch returns {failed.returncode} instead of backend exit status 9")
1135
+ env.pop("FAKE_LAUNCH_STATUS")
1136
+
1137
+ sleeper = tmp / "sleeper"
1138
+ sleeper.write_text("#!/usr/bin/env bash\nexec sleep 30\n")
1139
+ sleeper.chmod(0o755)
1140
+ signal_profile = tmp / "signal.toml"
1141
+ signal_profile.write_text(profile_text.replace(str(backend), str(sleeper)))
1142
+ signal_process = subprocess.Popen([
1143
+ sys.executable, str(launcher), "--config", str(signal_profile),
1144
+ "--no-tui", "codex",
1145
+ ], env=env)
1146
+ time.sleep(0.2)
1147
+ signal_process.terminate()
1148
+ try:
1149
+ signal_status = signal_process.wait(timeout=3)
1150
+ except subprocess.TimeoutExpired:
1151
+ signal_process.kill()
1152
+ signal_process.wait()
1153
+ mark_fail("agent-launch did not forward process lifetime through exec")
1154
+ else:
1155
+ if signal_status != -15:
1156
+ mark_fail(f"agent-launch SIGTERM status is {signal_status}, want -15")
1157
+
1158
+ invalid_args_profile = tmp / "invalid-args.toml"
1159
+ invalid_args_profile.write_text(
1160
+ profile_text.replace("passthrough_args = []", 'passthrough_args = "--bad"', 1)
1161
+ )
1162
+ invalid = invoke([
1163
+ sys.executable, str(launcher), "--config", str(invalid_args_profile),
1164
+ "--no-tui", "codex",
1165
+ ], env=env)
1166
+ if invalid.returncode != 2 or "passthrough_args" not in invalid.stderr:
1167
+ mark_fail("agent-launch accepted string passthrough_args instead of failing closed")
1168
+
1169
+ invalid_delegation_profile = tmp / "invalid-delegation.toml"
1170
+ invalid_delegation_profile.write_text(
1171
+ profile_text.replace("delegation = true", 'delegation = "false"', 1)
1172
+ )
1173
+ invalid = invoke([
1174
+ sys.executable, str(launcher), "--config", str(invalid_delegation_profile),
1175
+ "--preset", "balanced", "--dry-run", "codex",
1176
+ ], env=env)
1177
+ if invalid.returncode != 2 or "delegation must be boolean" not in invalid.stderr:
1178
+ mark_fail("agent-launch accepted non-boolean delegation instead of failing closed")
1179
+
1180
+ no_delegation_profile = tmp / "no-delegation.toml"
1181
+ no_delegation_profile.write_text(
1182
+ profile_text.replace(
1183
+ 'review_setup = "native-panel"\ndelegation = true',
1184
+ 'review_setup = "none"\ndelegation = false',
1185
+ 1,
1186
+ )
1187
+ )
1188
+ no_delegation = invoke([
1189
+ sys.executable, str(launcher), "--config", str(no_delegation_profile),
1190
+ "--preset", "balanced", "--dry-run", "claude",
1191
+ ], env=env)
1192
+ if no_delegation.returncode != 0:
1193
+ mark_fail(f"agent-launch rejected valid delegation=false: {no_delegation.stderr.strip()}")
1194
+ else:
1195
+ no_delegation_argv = json.loads(no_delegation.stdout.splitlines()[-1])
1196
+ if "--agents" in no_delegation_argv or "Delegation=off" not in " ".join(no_delegation_argv):
1197
+ mark_fail("agent-launch Claude delegation=false still projected child agents")
1198
+
1199
+ # The native-requires-delegation contradiction is a same-family concept
1200
+ # (cross native does not gate on delegation); test it under review_family=same.
1201
+ for review_setup in ("native-panel", "hybrid"):
1202
+ incompatible_review_profile = tmp / f"no-delegation-{review_setup}.toml"
1203
+ incompatible_review_profile.write_text(
1204
+ profile_text.replace(
1205
+ 'review_setup = "native-panel"\ndelegation = true',
1206
+ f'review_setup = "{review_setup}"\nreview_family = "same"\ndelegation = false',
1207
+ 1,
1208
+ )
1209
+ )
1210
+ incompatible_review = invoke([
1211
+ sys.executable,
1212
+ str(launcher),
1213
+ "--config",
1214
+ str(incompatible_review_profile),
1215
+ "--preset",
1216
+ "balanced",
1217
+ "--dry-run",
1218
+ "claude",
1219
+ ], env=env)
1220
+ if (
1221
+ incompatible_review.returncode != 2
1222
+ or "requires delegation" not in incompatible_review.stderr
1223
+ ):
1224
+ mark_fail(
1225
+ "agent-launch accepted delegation=false with review setup "
1226
+ f"{review_setup}"
1227
+ )
1228
+
1229
+ for field in ("codex_execution_policy", "claude_permission_mode"):
1230
+ missing_policy_profile = tmp / f"missing-{field}.toml"
1231
+ missing_policy_profile.write_text(
1232
+ profile_text.replace(f'{field} = "bypass"\n', "", 1)
1233
+ if field == "codex_execution_policy"
1234
+ else profile_text.replace(f'{field} = "bypassPermissions"\n', "", 1)
1235
+ )
1236
+ missing_policy = invoke([
1237
+ sys.executable, str(launcher), "--config", str(missing_policy_profile),
1238
+ "--preset", "balanced", "--dry-run", "codex",
1239
+ ], env=env)
1240
+ if missing_policy.returncode != 2 or field not in missing_policy.stderr:
1241
+ mark_fail(f"agent-launch accepted missing security policy: {field}")
1242
+
1243
+ for field, valid_value in (
1244
+ ("codex_execution_policy", "bypass"),
1245
+ ("claude_permission_mode", "bypassPermissions"),
1246
+ ):
1247
+ invalid_policy_profile = tmp / f"invalid-{field}.toml"
1248
+ invalid_policy_profile.write_text(
1249
+ profile_text.replace(
1250
+ f'{field} = "{valid_value}"',
1251
+ f'{field} = "unsupported-policy"',
1252
+ 1,
1253
+ )
1254
+ )
1255
+ invalid_policy = invoke([
1256
+ sys.executable,
1257
+ str(launcher),
1258
+ "--config",
1259
+ str(invalid_policy_profile),
1260
+ "--preset",
1261
+ "balanced",
1262
+ "--dry-run",
1263
+ "codex",
1264
+ ], env=env)
1265
+ if invalid_policy.returncode != 2 or field not in invalid_policy.stderr:
1266
+ mark_fail(f"agent-launch accepted invalid security policy: {field}")
1267
+
1268
+ invalid_schema_profile = tmp / "invalid-schema.toml"
1269
+ invalid_schema_profile.write_text(profile_text.replace("schema_version = 1", "schema_version = true", 1))
1270
+ invalid = invoke([
1271
+ sys.executable, str(launcher), "--config", str(invalid_schema_profile),
1272
+ "--no-tui", "codex",
1273
+ ], env=env)
1274
+ if invalid.returncode != 2 or "unsupported schema_version" not in invalid.stderr:
1275
+ mark_fail("agent-launch accepted boolean schema_version instead of failing closed")
1276
+
1277
+ invalid_review_profile = tmp / "invalid-review.toml"
1278
+ invalid_review_profile.write_text(
1279
+ profile_text.replace('review_setup = "native-panel"', 'review_setup = ["native-panel"]', 1)
1280
+ )
1281
+ invalid = invoke([
1282
+ sys.executable, str(launcher), "--config", str(invalid_review_profile),
1283
+ "--preset", "balanced", "--dry-run", "codex",
1284
+ ], env=env)
1285
+ if invalid.returncode != 2 or "unknown review setup" not in invalid.stderr:
1286
+ mark_fail("agent-launch raised outside LaunchError for malformed review_setup")
1287
+
1288
+ invalid_models_profile = tmp / "invalid-models.toml"
1289
+ invalid_models_profile.write_text(
1290
+ profile_text.replace(
1291
+ 'models = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]',
1292
+ "models = []",
1293
+ 1,
1294
+ )
1295
+ )
1296
+ invalid = invoke([
1297
+ sys.executable, str(launcher), "--config", str(invalid_models_profile),
1298
+ "--preset", "balanced", "--dry-run", "codex",
1299
+ ], env=env)
1300
+ if invalid.returncode != 2 or "must be a non-empty list of strings" not in invalid.stderr:
1301
+ mark_fail("agent-launch accepted an empty models catalog instead of failing closed")
1302
+
1303
+ invalid_claude_effort_profile = tmp / "invalid-claude-effort.toml"
1304
+ invalid_claude_effort_profile.write_text(
1305
+ profile_text.replace('codex = "ultra", claude = "max"', 'codex = "ultra", claude = "ultra"')
1306
+ )
1307
+ invalid = invoke([
1308
+ sys.executable, str(launcher), "--config", str(invalid_claude_effort_profile),
1309
+ "--preset", "deep-review", "--dry-run", "claude",
1310
+ ], env=env)
1311
+ if invalid.returncode != 2 or "unsupported effort" not in invalid.stderr:
1312
+ mark_fail("agent-launch accepted Claude Ultra instead of failing closed")
1313
+
1314
+ bare_dry_run = invoke([
1315
+ sys.executable, str(launcher), "--dry-run", "codex",
1316
+ ], env=env)
1317
+ if bare_dry_run.returncode != 0 or "Preset Balanced" not in bare_dry_run.stdout:
1318
+ mark_fail("agent-launch non-TTY bare --dry-run did not select Balanced")
1319
+
1320
+ no_balanced_profile = tmp / "no-balanced.toml"
1321
+ no_balanced_profile.write_text(
1322
+ profile_text.replace("[presets.balanced]", "[presets.daily]", 1)
1323
+ )
1324
+ no_balanced = invoke([
1325
+ sys.executable,
1326
+ str(launcher),
1327
+ "--config",
1328
+ str(no_balanced_profile),
1329
+ "--dry-run",
1330
+ "codex",
1331
+ ], env=env)
1332
+ if (
1333
+ no_balanced.returncode != 2
1334
+ or "requires a 'balanced' preset" not in no_balanced.stderr
1335
+ ):
1336
+ mark_fail(
1337
+ "agent-launch bare non-TTY dry-run selected an arbitrary custom preset"
1338
+ )
1339
+
1340
+ configured_runtime = invoke([
1341
+ sys.executable, str(launcher), "--preset", "balanced", "--yes", "codex", "--", "probe"
1342
+ ], env=env)
1343
+ if configured_runtime.stdout != "backend-output\n":
1344
+ mark_fail("configured non-TTY launch polluted backend stdout with its summary")
1345
+ if "Launch summary" not in configured_runtime.stderr:
1346
+ mark_fail("configured non-TTY launch did not disclose its summary on stderr")
1347
+ projected_agent_files = sorted((tmp / "cache/agent-launch/codex-agents").glob("*/*.toml"))
1348
+ if len(projected_agent_files) != 3:
1349
+ mark_fail("configured Codex launch did not materialize three pinned child agents")
1350
+ for path in projected_agent_files:
1351
+ data = tomllib.loads(path.read_text())
1352
+ tier = path.stem
1353
+ expected_model, expected_effort = expected_launch_tiers[tier]
1354
+ if (data.get("model"), data.get("model_reasoning_effort")) != (
1355
+ expected_model, expected_effort
1356
+ ):
1357
+ mark_fail(f"materialized Codex {tier} binding drifted: {path}")
1358
+
1359
+ custom_codex_home = tmp / "custom-codex-home"
1360
+ (custom_codex_home / "agents").mkdir(parents=True)
1361
+ for tier in ("frontier", "workhorse", "sweep"):
1362
+ (custom_codex_home / "agents" / f"{tier}.toml").write_text(
1363
+ (agent_dir / f"{tier}.toml").read_text()
1364
+ )
1365
+ portable_profile = tmp / "portable.toml"
1366
+ portable_profile.write_text(portable_profile_text)
1367
+ portable_env = env.copy()
1368
+ portable_env.update(
1369
+ CODEX_HOME=str(custom_codex_home),
1370
+ XDG_CACHE_HOME=str(tmp / "portable-cache"),
1371
+ )
1372
+ portable = invoke([
1373
+ sys.executable, str(launcher), "--config", str(portable_profile),
1374
+ "--preset", "balanced", "--yes", "codex", "--", "probe",
1375
+ ], env=portable_env)
1376
+ if portable.returncode != 0 or len(
1377
+ list((tmp / "portable-cache/agent-launch/codex-agents").glob("*/*.toml"))
1378
+ ) != 3:
1379
+ mark_fail("configured Codex launch did not honor non-default CODEX_HOME")
1380
+
1381
+ env["AGENT_LAUNCH_DEBUG"] = "1"
1382
+ configured = invoke([
1383
+ sys.executable, str(launcher), "--preset", "balanced", "--yes", "--dry-run",
1384
+ "codex", "--", "exec", "probe",
1385
+ ], env=env)
1386
+ expect_text(
1387
+ configured,
1388
+ "agent-launch Codex configured projection",
1389
+ (
1390
+ "configured/requested · completed: not enforced",
1391
+ 'model_reasoning_effort=\\"xhigh\\"',
1392
+ "features.multi_agent=true",
1393
+ "--dangerously-bypass-approvals-and-sandbox",
1394
+ "forwarded backend args appended last; may supersede defaults",
1395
+ '"exec", "probe"',
1396
+ ),
1397
+ )
1398
+ if configured.returncode == 0:
1399
+ codex_argv = json.loads(configured.stdout.splitlines()[-1])
1400
+ if codex_argv[-2:] != ["exec", "probe"]:
1401
+ mark_fail("agent-launch Codex expert overrides were not appended last")
1402
+ configured = invoke([
1403
+ sys.executable, str(launcher), "--preset", "balanced", "--yes", "--dry-run",
1404
+ "claude", "--", "-p", "probe",
1405
+ ], env=env)
1406
+ expect_text(
1407
+ configured,
1408
+ "agent-launch Claude configured projection",
1409
+ (
1410
+ "configured/requested · completed: not enforced",
1411
+ "--append-system-prompt",
1412
+ "--agents",
1413
+ "--dangerously-skip-permissions",
1414
+ '"-p", "probe"',
1415
+ ),
1416
+ )
1417
+ if configured.returncode == 0:
1418
+ try:
1419
+ projected_argv = json.loads(configured.stdout.splitlines()[-1])
1420
+ agents_arg = json.loads(projected_argv[projected_argv.index("--agents") + 1])
1421
+ except (ValueError, IndexError, json.JSONDecodeError) as exc:
1422
+ mark_fail(f"agent-launch Claude --agents projection is not valid JSON: {exc}")
1423
+ else:
1424
+ for tier, (model, effort) in expected_claude_tiers.items():
1425
+ role = agents_arg.get(tier, {})
1426
+ if role.get("model") != model or role.get("effort") != effort:
1427
+ mark_fail(f"agent-launch Claude {tier} role projection drifted: {role!r}")
1428
+ if projected_argv[-2:] != ["-p", "probe"]:
1429
+ mark_fail(
1430
+ "agent-launch Claude expert overrides were not appended last"
1431
+ )
1432
+
1433
+ # Cross-family default: each main routes every review route to the OPPOSITE
1434
+ # model family, with concrete tools/paths/bindings named in the contract.
1435
+ # The cross CODEX_HOME needs the reviewer wrappers (bin) and the same-family
1436
+ # agent templates (agents) that the codex fallback floor still materializes.
1437
+ cross_home = tmp / "cross-codex-home"
1438
+ (cross_home / "bin").mkdir(parents=True)
1439
+ (cross_home / "agents").mkdir(parents=True)
1440
+ for wrapper in ("codex-run", "codex-helm"):
1441
+ wrapper_path = cross_home / "bin" / wrapper
1442
+ wrapper_path.write_text("#!/usr/bin/env bash\nexit 0\n")
1443
+ wrapper_path.chmod(0o755)
1444
+ for tier in ("frontier", "workhorse", "sweep"):
1445
+ (cross_home / "agents" / f"{tier}.toml").write_text(
1446
+ (agent_dir / f"{tier}.toml").read_text()
1447
+ )
1448
+ cross_env = env.copy()
1449
+ cross_env["CODEX_HOME"] = str(cross_home)
1450
+
1451
+ claude_cross = invoke([
1452
+ sys.executable, str(launcher), "--preset", "deep-review", "--yes",
1453
+ "--dry-run", "claude",
1454
+ ], env=cross_env)
1455
+ expect_text(
1456
+ claude_cross,
1457
+ "agent-launch Claude cross-family (gpt/codex) review projection",
1458
+ (
1459
+ "Review family=cross",
1460
+ "run EVERY review route on OpenAI/Codex",
1461
+ "codex-run --profile hermetic",
1462
+ "onto: call onto_review",
1463
+ "so onto runs OpenAI/Codex",
1464
+ f"{fake_ultracode} (the $ultracode-for-codex Codex skill)",
1465
+ "frontier=gpt-5.6-sol/max",
1466
+ ),
1467
+ )
1468
+ codex_cross = invoke([
1469
+ sys.executable, str(launcher), "--preset", "deep-review", "--yes",
1470
+ "--dry-run", "codex",
1471
+ ], env=cross_env)
1472
+ expect_text(
1473
+ codex_cross,
1474
+ "agent-launch Codex cross-family (anthropic/claude) review projection",
1475
+ (
1476
+ "Review family=cross",
1477
+ "run EVERY review route on Anthropic/Claude",
1478
+ "-p --model <review tier>",
1479
+ "--permission-mode plan",
1480
+ "onto: call onto_review",
1481
+ "so onto runs Anthropic/Claude",
1482
+ "--effort ultracode -p",
1483
+ "Claude Code /workflows ultracode mode, headless",
1484
+ "frontier=claude-fable-5/max",
1485
+ ),
1486
+ )
1487
+
1488
+ # Module-level: the cross contract pins the exact onto llmOverride family+model.
1489
+ cross_cfg = tomllib.loads(profile_text)
1490
+ for main_host, provider, model in (
1491
+ ("claude", "openai", "gpt-5.6-sol"),
1492
+ ("codex", "anthropic", "claude-fable-5"),
1493
+ ):
1494
+ cross_plan = launcher_module.build_plan(cross_cfg, main_host, "deep-review")
1495
+ contract = launcher_module.run_contract(cross_plan)
1496
+ expected = f'llmOverride={{"provider":"{provider}","model":"{model}"}}'
1497
+ if expected not in contract:
1498
+ mark_fail(f"agent-launch {main_host} cross onto llmOverride missing: {expected}")
1499
+
1500
+ # A config with only one host degrades cross review to same-family instead of
1501
+ # crashing on the absent opposite host.
1502
+ single_host_cfg = tomllib.loads(profile_text)
1503
+ single_host_cfg["hosts"].pop("claude", None)
1504
+ single_host_cfg["backends"].pop("claude", None)
1505
+ try:
1506
+ single_plan = launcher_module.build_plan(single_host_cfg, "codex", "balanced")
1507
+ if single_plan["review_family"] != "same":
1508
+ mark_fail("agent-launch single-host config did not coerce cross review to same")
1509
+ launcher_module.run_contract(single_plan)
1510
+ except launcher_module.LaunchError as exc:
1511
+ mark_fail(f"agent-launch single-host config raised instead of degrading: {exc}")
1512
+ except Exception as exc:
1513
+ mark_fail(f"agent-launch single-host config crashed (not a clean degrade): {exc!r}")
1514
+
1515
+ # An all-disabled option list raises a clean LaunchError, not a hang.
1516
+ try:
1517
+ launcher_module.choose(
1518
+ "empty", [launcher_module.MenuOption("a", "A", "d", enabled=False)], "a"
1519
+ )
1520
+ mark_fail("agent-launch choose accepted an all-disabled option list")
1521
+ except launcher_module.LaunchError:
1522
+ pass
1523
+
1524
+ # review_family=same reproduces the earlier same-family hybrid contract verbatim.
1525
+ same_family_profile = tmp / "same-family.toml"
1526
+ same_family_profile.write_text(
1527
+ profile_text.replace(
1528
+ 'review_setup = "hybrid"', 'review_setup = "hybrid"\nreview_family = "same"', 1
1529
+ )
1530
+ )
1531
+ same_family_review = invoke([
1532
+ sys.executable, str(launcher), "--config", str(same_family_profile),
1533
+ "--preset", "deep-review", "--yes", "--dry-run", "claude",
1534
+ ], env=env)
1535
+ expect_text(
1536
+ same_family_review,
1537
+ "agent-launch review_family=same restores same-family contract",
1538
+ (
1539
+ "Review family=same",
1540
+ "Codex-backed Ultracode review kinds",
1541
+ f"Ultracode executable: {fake_ultracode}",
1542
+ ),
1543
+ ("cross-family review",),
1544
+ )
1545
+
1546
+ frontier_profile = tmp / "frontier.toml"
1547
+ frontier_profile.write_text(
1548
+ profile_text.replace(
1549
+ 'main_tier = "helm"\nfrontier_effort = "max"',
1550
+ 'main_tier = "frontier"\nfrontier_effort = "ultra"',
1551
+ 1,
1552
+ )
1553
+ )
1554
+ configured = invoke([
1555
+ sys.executable, str(launcher), "--config", str(frontier_profile),
1556
+ "--preset", "balanced", "--yes", "--dry-run", "codex",
1557
+ ], env=env)
1558
+ expect_text(
1559
+ configured,
1560
+ "agent-launch FRONTIER main effort projection",
1561
+ ('Main FRONTIER · gpt-5.6-sol · ultra', 'main=frontier (gpt-5.6-sol/ultra)'),
1562
+ )
1563
+
1564
+ unavailable_profile = tmp / "unavailable.toml"
1565
+ unavailable_profile.write_text(
1566
+ profile_text.replace(
1567
+ f"command = {json.dumps(str(fake_onto))}",
1568
+ f"command = {json.dumps(str(tmp / 'missing-onto'))}",
1569
+ 1,
1570
+ ).replace(
1571
+ f"command = {json.dumps(str(fake_ultracode))}",
1572
+ f"command = {json.dumps(str(tmp / 'missing-ultracode'))}",
1573
+ 1,
1574
+ ).replace(
1575
+ 'review_setup = "hybrid"', 'review_setup = "hybrid"\nreview_family = "same"', 1
1576
+ )
1577
+ )
1578
+ # Under review_family=same, missing external review capabilities (onto/ultracode
1579
+ # CLI absent) degrade to native same-model review instead of failing closed.
1580
+ unavailable = invoke([
1581
+ sys.executable, str(launcher), "--config", str(unavailable_profile),
1582
+ "--preset", "deep-review", "--yes", "--dry-run", "codex",
1583
+ ], env=env)
1584
+ expect_text(
1585
+ unavailable,
1586
+ "agent-launch codex degrade-to-native review",
1587
+ (
1588
+ "Review setup hybrid → effective native (onto,ultracode unavailable)",
1589
+ "degraded to native same-model",
1590
+ "fall back to native same-model subagent review",
1591
+ ),
1592
+ ("mcp_servers.onto.enabled=true", "Ultracode executable:"),
1593
+ )
1594
+
1595
+ unavailable_claude = invoke([
1596
+ sys.executable, str(launcher), "--config", str(unavailable_profile),
1597
+ "--preset", "deep-review", "--yes", "--dry-run", "claude",
1598
+ ], env=env)
1599
+ if expect_text(
1600
+ unavailable_claude,
1601
+ "agent-launch claude degrade-to-native review",
1602
+ (
1603
+ "Review setup hybrid → effective native (onto,ultracode unavailable)",
1604
+ "fall back to native same-model subagent review",
1605
+ ),
1606
+ ("--mcp-config",),
1607
+ ):
1608
+ claude_degraded_argv = json.loads(unavailable_claude.stdout.splitlines()[-1])
1609
+ if "--agents" not in claude_degraded_argv:
1610
+ mark_fail("agent-launch claude degrade dropped native --agents review")
1611
+
1612
+ # Stage C: a named preset save round-trips and stays host-scoped.
1613
+ save_target = tmp / "save-target.toml"
1614
+ save_target.write_text(profile_text)
1615
+ save_cfg = tomllib.loads(save_target.read_text())
1616
+ save_plan = launcher_module.build_plan(save_cfg, "codex", "balanced")
1617
+ save_plan["main_tier"] = "workhorse"
1618
+ save_plan["tiers"]["workhorse"]["model"] = "custom-wh"
1619
+ save_plan["tiers"]["workhorse"]["effort"] = "xhigh"
1620
+ save_plan["frontier_effort"] = "ultra"
1621
+ launcher_module.save_preset(save_plan, save_cfg, save_target, "mysetup")
1622
+ reloaded = launcher_module.load_config(save_target)
1623
+ if "mysetup" not in reloaded["presets"] or "balanced" not in reloaded["presets"]:
1624
+ mark_fail("save_preset did not persist the preset or clobbered existing ones")
1625
+ else:
1626
+ round_trip = launcher_module.build_plan(reloaded, "codex", "mysetup")
1627
+ if (
1628
+ round_trip["main_tier"] != "workhorse"
1629
+ or round_trip["tiers"]["workhorse"] != {"model": "custom-wh", "effort": "xhigh"}
1630
+ or launcher_module.tier_effort(round_trip, "frontier") != "ultra"
1631
+ ):
1632
+ mark_fail(f"saved preset did not round-trip: {round_trip['tiers']}")
1633
+ claude_view = launcher_module.build_plan(reloaded, "claude", "mysetup")
1634
+ if (
1635
+ claude_view["tiers"]["workhorse"]["model"]
1636
+ != save_cfg["hosts"]["claude"]["tiers"]["workhorse"]["model"]
1637
+ ):
1638
+ mark_fail("saved codex tier override leaked into the claude host")
1639
+ launcher_module.save_preset(round_trip, reloaded, save_target, "mysetup")
1640
+ if save_target.read_text().count("[presets.mysetup]") != 1:
1641
+ mark_fail("re-saving a preset duplicated its block")
1642
+
1643
+ # The Custom hub 'save' action persists the named preset then launches.
1644
+ ui_save_target = tmp / "ui-save.toml"
1645
+ ui_save_target.write_text(profile_text)
1646
+ if argv_log.exists():
1647
+ argv_log.unlink()
1648
+ ui_save = invoke([
1649
+ sys.executable, str(launcher), "--config", str(ui_save_target),
1650
+ "--preset", "balanced", "--custom", "codex",
1651
+ ], input_text="8\ndaily-driver\n", env=env)
1652
+ if ui_save.returncode != 0 or not argv_log.exists():
1653
+ mark_fail(f"agent-launch custom save-and-start did not launch: {ui_save.stderr.strip()}")
1654
+ elif "daily-driver" not in launcher_module.load_config(ui_save_target)["presets"]:
1655
+ mark_fail("agent-launch custom hub save did not persist the named preset")
1656
+
1657
+ shell_bin = tmp / "shell-bin"
1658
+ shell_bin.mkdir()
1659
+ dispatch_log = tmp / "dispatch.argv"
1660
+ shell_backend = shell_bin / "backend"
1661
+ shell_backend.write_text(
1662
+ "#!/usr/bin/env bash\n"
1663
+ "printf '%s\\n' \"$(basename \"$0\")\" \"$@\" > \"$FAKE_DISPATCH_ARGV\"\n"
1664
+ )
1665
+ shell_backend.chmod(0o755)
1666
+ (shell_bin / "codex").symlink_to(shell_backend)
1667
+ (shell_bin / "claude").symlink_to(shell_backend)
1668
+ shell_env = env.copy()
1669
+ shell_env.update(
1670
+ PATH=f"{shell_bin}{os.pathsep}{shell_env.get('PATH', '')}",
1671
+ FAKE_DISPATCH_ARGV=str(dispatch_log),
1672
+ )
1673
+ for command, expected in (
1674
+ ("codex exec probe", ["codex", "exec", "probe"]),
1675
+ ("claude --no-tui -p probe", ["claude", "--dangerously-skip-permissions", "-p", "probe"]),
1676
+ ("codex", ["codex"]),
1677
+ ):
1678
+ shell_result = invoke([
1679
+ "zsh", "-f", "-c", f"source {shell_init}; {command}"
1680
+ ], env=shell_env)
1681
+ received = dispatch_log.read_text().splitlines() if dispatch_log.is_file() else []
1682
+ if shell_result.returncode != 0 or received != expected:
1683
+ mark_fail(f"shell dispatch for {command!r} is {received!r}, want {expected!r}")
1684
+ alias_result = invoke([
1685
+ "zsh", "-f", "-c",
1686
+ f"alias codex='false'; alias claude='false'; source {shell_init}; codex exec alias-probe",
1687
+ ], env=shell_env)
1688
+ received = dispatch_log.read_text().splitlines() if dispatch_log.is_file() else []
1689
+ if alias_result.returncode != 0 or received != [
1690
+ "codex", "exec", "alias-probe"
1691
+ ]:
1692
+ mark_fail("shell init did not replace pre-existing codex/claude aliases")
1693
+
1694
+ if fail == 0:
1695
+ print("LAUNCH/BINDINGS OK: profile projections, bypass paths, role slots, and wrappers aligned")
1696
+ sys.exit(fail)
1697
+ PY
1698
+ then
1699
+ fail=1
1700
+ fi
1701
+
1702
+ [ "$fail" -eq 0 ] && echo "PARITY OK: mirrors, globals, guides, launch profile, bypass paths, role bindings, and wrapper defaults aligned"
1703
+ exit "$fail"