Add real execution agent probe harness

Why: verify configured Agent CLIs one profile at a time before declaring them usable

Evidence: 53 focused tests, agent-exec validation, 397-test governance regression, and diff check

Impact: adds opt-in source-external probes, acceptance checks, and current task records

Refs: U5; docs/superpowers/plans/2026-10-15-project-agent-execution.md
This commit is contained in:
2026-10-15 05:22:00 +00:00
parent 0f4c5c017d
commit dee06368ea
4 changed files with 797 additions and 16 deletions
+28 -13
View File
@@ -12,7 +12,7 @@
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
- 人工决策点:仅目标/范围/验收变更、未知外部副作用、高后果操作或无法自动判定的业务验收;计划完成后不停顿
- 当前阶段:实施与自检;当前增量:U4(项目治理接线;U1–U3 已合入规范基线)
- 当前阶段:实施与自检;当前增量:U5(opt-in 真实 CLI 探针夹具;U4 已规范合入 `0f4c5c017d444369470443faede3283e101c3be9`;七个 profile 探针 NOT_RUN)
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
## 目标与非目标
@@ -23,7 +23,7 @@
## Agent 实施收据
现行执行权威:根目录 [AGENT-EXECUTION.md](../AGENT-EXECUTION.md) 的 agent-per-unit 路由与收据门控的 host fallback(`host-fallback-authorized`)。历史 I1/P2 Cursor 行仅为早期 cursor-per-unit 时期记录,不是现行权威,仓库检索不得将其当作活跃固定 Cursor 要求。U1–U3 早于活跃 agent-exec 路由,由 ce-work 固定 Cursor 控制器实施,非本配置角色路由选型。源码外 ce-work 运行根:`/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015`(`manifest.json`)。
现行执行权威:根目录 [AGENT-EXECUTION.md](../AGENT-EXECUTION.md) 的 agent-per-unit 路由与收据门控的 host fallback(`host-fallback-authorized`)。历史 I1/P2 Cursor 行仅为早期 cursor-per-unit 时期记录,不是现行权威,仓库检索不得将其当作活跃固定 Cursor 要求。U1–U3 早于活跃 agent-exec 路由,由 ce-work 固定 Cursor 控制器实施,非本配置角色路由选型。源码外 ce-work 运行根:`/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015`(`manifest.json`)。U4 已由 host 集成:规范 `0f4c5c017d444369470443faede3283e101c3be9`(父 `8f11d002aec74ed21d3dae8ccbb760963b99e93a`,树 `687ac553730e12e67e6ff3540162a217b8e46e05`),accepted `attempt-2`,job `20261015T045650Z-ab6d6c41`,transport `2ef4f76887d5dfc8a069c74da03f32a8be12e88a`,`model_receipt_status=verified`(Cursor Agent / Auto);attempt-1 已放弃。以上为该 manifest 的源码外事实,本卡不手抄命令输出。U5 七个 profile 探针保持 NOT_RUN。
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|---|---|---|---|---|---|---|---|---|---|---|
@@ -33,8 +33,23 @@
| U1 | 配置合同 AGENT-EXECUTION.md / parser | 早于活跃 agent-exec 路由;ce-work 固定 Cursor 控制器;accepted `attempt-4`;job `20261015T021049Z-c63f0c9c`;见 `manifest.json` | implementation | 非角色路由;ce-work 固定 Cursor | attempts 1–4;accepted `attempt-4` | Cursor Agent / Auto(已核实) | 完成;规范 `bdad8ffc91beab7f0008a3382b650bfa15394065` | `5ce1c685ccf9b4da20f0ca7bb2449bf0ce8cef45` | 无 / 未授权 | `/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015`(manifest.json) |
| U2 | adapter seam | 早于活跃 agent-exec 路由;ce-work 固定 Cursor 控制器;accepted `attempt-4`;job `20261015T025219Z-8edd087e`;见 `manifest.json` | implementation | 非角色路由;ce-work 固定 Cursor | attempts 1–4;accepted `attempt-4` | Cursor Agent / Auto(已核实) | 完成;规范 `92330a225f9d839aaaf279e6ae1ad96a4bdfebb4` | `cfdae5f9cf0dbec3ad991df77d39852236927255` | 无 / 未授权 | `/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015`(manifest.json) |
| U3 | 可恢复执行控制器 | 早于活跃 agent-exec 路由;ce-work 固定 Cursor 控制器;accepted `attempt-9`;job `20261015T042945Z-e824725f`;见 `manifest.json` | implementation | 非角色路由;ce-work 固定 Cursor | attempts 1–9;accepted `attempt-9` | Cursor Agent / Auto(已核实) | 完成;规范 `8f11d002aec74ed21d3dae8ccbb760963b99e93a` | `6671fc2d6bdb7b64d8a83cc53a27d9443c98e61a` | 无 / 未授权 | `/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015`(manifest.json) |
| U4 | 项目治理接线(本单元) | NOT_RUN | implementation | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | `/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015` |
| U5 | 真实 CLI 探针与组合验收 | NOT_RUN | implementation | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
| U4 | 项目治理接线 | ce-work 固定 Cursor 控制器;accepted `attempt-2`;job `20261015T045650Z-ab6d6c41`;attempt-1 已放弃;见源码外 `manifest.json` | implementation | 非角色路由;ce-work 固定 Cursor | attempts 1–2;accepted `attempt-2` | Cursor Agent / Auto(manifest `model_receipt_status=verified`) | 完成;规范 `0f4c5c017d444369470443faede3283e101c3be9` | `2ef4f76887d5dfc8a069c74da03f32a8be12e88a` | 无 / 未授权 | `/tmp/compound-engineering-1000/ce-work/project-agent-exec-20261015` |
| U5 | 真实 CLI 探针与组合验收 | 夹具与验收说明已写入;七个 profile 探针 NOT_RUN | implementation | 每次恰好一个 `AGENT_EXEC_REAL_PROFILE` | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | 源码外收据待 host 执行后产生;不入库 |
### U5 真实探针字段(host 稍后逐 profile 执行)
门禁:`RUN_REAL_AGENT_EXEC=1`,且 `AGENT_EXEC_REAL_PROFILE` 每次恰好一个 profile。默认测试不接触 provider。缺失或不兼容的 OpenCode 记 `unavailable`,不得记 PASS。验收说明见 `tests/fixtures/agent-exec/README.md`。
| profile | 探针 | 模型收据 | transport | 变更路径 | 清理/保留 | 规范检出 | 能力结论 |
|---|---|---|---|---|---|---|---|
| cursor | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| antigravity | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| claude | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| codex | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| pi | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| kimi | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| opencode | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
## 关联方向与规划
@@ -58,7 +73,7 @@
- 方案引用:[2026-10-15 项目级执行 Agent 路由设计](../docs/superpowers/specs/2026-10-15-project-agent-execution-design.md)(已批准)
- 实施计划引用:[2026-10-15 项目级执行 Agent 路由实施计划](../docs/superpowers/plans/2026-10-15-project-agent-execution.md)(用户明确免除计划复审)
- 测试计划引用:U1–U3 本地 unittest / agent-exec validate 为 SELF;U4 聚焦任务卡模板与 validate;U5 真实 CLI 探针与完整治理回归(GATE 待 PR HEAD)
- 测试计划引用:U1–U3 本地 unittest / agent-exec validate 为 SELF;U4 聚焦任务卡模板与 validate(SELF,源码外 manifest verification summary);U5 真实 CLI 探针默认 NOT_RUN,仅 `RUN_REAL_AGENT_EXEC=1` 且单 profile 时由 host 执行;完整治理回归与 GATE 待 PR HEAD
## 阶段评审
@@ -69,7 +84,7 @@
| 需求表确认 | R1–R7 | 通过 | weichen | 2026-10-15 | 用户确认多 CLI 受控 adapter、`run-everything` 优先、主模型低消耗与全候选不可用后回退 |
| 方案设计 | `docs/superpowers/specs/2026-10-15-project-agent-execution-design.md` | 通过 | weichen | 2026-10-15 | 用户明确“批准” |
| 实施计划 | `docs/superpowers/plans/2026-10-15-project-agent-execution.md` | 授权连续执行 | weichen | 2026-10-15 | 用户明确“实施计划后不需要再让我审阅”,并指定 Cursor Agent 执行 |
| 实施与自检 | U1 `bdad8ffc` / U2 `92330a22` / U3 `8f11d002`;U4/U5 进行中 | 进行中 | Codex host + 外部 worker | 2026-10-15 | U1–U3 规范合入;U4 治理接线;U5 探针 NOT_RUN;历史 P2/`8b7e8ca` 仅作旧对象线索 |
| 实施与自检 | U1 `bdad8ffc` / U2 `92330a22` / U3 `8f11d002` / U4 `0f4c5c017d444369470443faede3283e101c3be9`;U5 探针 NOT_RUN | 进行中 | Codex host + 外部 worker | 2026-10-15 | U1–U4 规范合入;U5 夹具已写、真实探针 NOT_RUN;历史 P2/`8b7e8ca` 仅作旧对象线索 |
| 正式测试与验收 | 待定(U1–U5 组合候选) | 待执行 | — | — | 旧 `cursor-per-unit` 候选 `cc03710` 已停止交付;不发明 GATE/REPLAY |
| 代码 Review | 待定 | 未评审 | — | — | — |
| 合入 | 待定 | 未评审 | — | — | — |
@@ -103,16 +118,16 @@
## 结果与验证(完成后填写)
- 结果:旧 cursor-per-unit 候选已停止交付并保留为历史;U0 设计已批准;U1–U3 已合入规范基线(`bdad8ffc…` / `92330a22…` / `8f11d002…`);U4 将项目规则改为配置驱动 agent-per-unit;U5 真实探针 NOT_RUN。
- 验证:U1–U3 本地 SELF 见各单元聚焦测试与 host 集成;U4 本地聚焦任务卡测试与 `agent_exec.py validate`(SELF);完整 `make test-governance` / PR CI(GATE)由 host 权威事务执行,本卡不手抄。历史 P2 digest `d16726eb13aba7f5` 仅作旧对象线索。
- 影响与剩余项:U5 探针、独立 Review、精确 HEAD CI、合入与任务卡闭合仍 NOT_RUN。
- 结果:旧 cursor-per-unit 候选已停止交付并保留为历史;U0 设计已批准;U1–U4 已合入规范基线(`bdad8ffc…` / `92330a22…` / `8f11d002…` / `0f4c5c017d444369470443faede3283e101c3be9`);U5 真实探针 NOT_RUN。
- 验证:U1–U3 本地 SELF 见各单元聚焦测试与 host 集成;U4 本地聚焦任务卡测试、`agent_exec.py validate` 与 `make test-governance` 的结论见源码外 manifest verification summary(SELF,digest `8f277d49b61e57d97e39beb16d5a4cad702e7043d670094e3ebe0c9b1d26945f`),本卡不手抄该次输出。U5 默认测试不接触 provider,七个 profile 探针 NOT_RUN。完整 PR CI(GATE)由 host 权威事务执行。历史 P2 digest `d16726eb13aba7f5` 仅作旧对象线索。
- 影响与剩余项:U5 七个 profile 探针、host 回退演练、独立 Review、精确 HEAD CI、合入与任务卡闭合仍 NOT_RUN。
## 规划进展与下一步
- 本次推进/剩余:U1–U3 已完成;U4 治理接线进行中;U5 组合验收待办。
- 首选下一任务:U5 真实 CLI 探针与组合验收(新 agent-per-unit 进程)。
- 推荐依据:遵循本次 goal-first 与不扩围原则;U4 完成后才具备日常任务路由权威。
- 前置条件/待确认:U4 聚焦验证通过并由 host 权威集成;无待人工复审项。
- 本次推进/剩余:U1–U4 已规范合入;U5 已添加 opt-in 探针夹具与验收说明,七个 profile 的真实探针仍 NOT_RUN。
- 首选下一任务:host 按 profile 逐个执行 U5 真实探针(每次一个 `AGENT_EXEC_REAL_PROFILE`),再进入 Review 与精确 HEAD CI。
- 推荐依据:U4 已提供 agent-per-unit 路由权威;探针未执行前不得把已安装 CLI 记为就位。
- 前置条件/待确认:host 在源码外逐 profile 执行探针;无待人工复审项。
- 预期产物/验收条件:R1–R10 有代码/测试/证据映射;无活跃固定 Cursor 权威;U5 后决定是否声明已安装 CLI 就位。
- 重复任务核对:未发现需另建任务的重复范围。
- 启动授权:U1–U5 已授权连续执行;U5 之外新任务仍须明确授权
+4 -2
View File
@@ -5,8 +5,10 @@ authority for which Agent CLI profiles may implement, repair, test, or review
bounded units. Human-readable notes may appear outside the machine block; the
parser accepts exactly one `agent-execution` fenced block.
U1 delivers configuration parsing and validation only. Routing is not enabled
until later units wire adapters and the execution controller.
U3's execution controller and U4's project wiring are integrated. This file is
the execution-routing authority for implementation, repair, test, and review.
Installed CLI identity stays unproven until a host runs the U5 probes one
profile at a time.
```agent-execution
schema: agent-execution/v1
+68
View File
@@ -0,0 +1,68 @@
# U5 真实 Agent CLI 探针
本夹具描述 host 稍后执行的 opt-in 探针。七个 profile 的真实结果都还是 `NOT_RUN`。这里不记录探针 `PASS`。
## 门禁
默认测试不设置下面两个变量,因此不会启动真实 CLI,也不会联系 provider。
- `RUN_REAL_AGENT_EXEC=1`:显式打开真实探针。其他值(包括 `true`)保持关闭。
- `AGENT_EXEC_REAL_PROFILE`:每次恰好一个 profile。允许的名字是 `cursor`、`antigravity`、`claude`、`codex`、`pi`、`kimi`、`opencode`。空值、空白、逗号或空格列表都会拒绝,并且不会启动 CLI。
一次只跑一个 profile,不要对同一个目标并行开多个探针。
```bash
RUN_REAL_AGENT_EXEC=1 AGENT_EXEC_REAL_PROFILE=cursor \
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest \
tests.test_agent_exec.RealAgentCliProbeTests.test_opt_in_real_profile_probe -v
```
把 `cursor` 换成要测的那一个 profile。建议顺序:`cursor`,然后 `antigravity`(可执行文件 `agy`),`claude`,`pi`,`codex`,`kimi`。`opencode` 单独跑。
## 夹具
探针在临时 Git 仓库里进行,不使用本仓的规范工作区。
- 分支是非默认分支 `feature/u5-real-probe`。
- 配置里只启用被选中的那一个 profile,`model` 为 `auto`(省略模型参数,不把字符串 `auto` 传给 CLI)。
- 单元只允许一个文件:`src/u5-marker.txt`。
- 文件字节必须是 ASCII `U5-REAL-PROBE-MARKER` 再加一个换行 `0x0A`。
## 核对
Host 只在机械检查全部成立时把能力结论记为 `PASS`:
- transport 树和工作区里的标记内容与上面的字节完全一致。
- transport 提交只有一个父提交,且该父提交就是夹具基线 HEAD。
- `changed_paths` 只有 `src/u5-marker.txt`。
- 成功时保留隔离工作区,`transport_created` 为真,`exact_restore` 为假。
- 模型收据只记录实际看得到的状态。原始输出里没有 `model_receipt_status` 时记 `not-observable`,不得编造成 `verified`。观察到 `mismatch` 时结论不是 `PASS`。
- 规范检出的 HEAD、分支、状态和索引保持不变,标记文件不得出现在规范检出里。
## 不可用与回退
缺失或不兼容的 OpenCode 必须记 `unavailable`,不得记 `PASS`。不兼容指本地 help 缺少 adapter 要求的能力标志;这种情况下不得把 CLI 当成已经跑通。
唯一候选耗尽时,控制器的运行级终态可以是 `host-fallback-authorized`。那是回退授权,不是探针通过。夹具不会代替 CLI 去写标记文件。
Provider 配额、认证失败,或进程启动后没有留下标记且规范检出仍然完好,同样记 `unavailable`,不得记 `PASS`。范围外改动、规范检出被改写、标记内容不符,记 `failed`。
## 源码外收据
真实 opt-in 运行会保留临时仓库,并把收据复制到 `/tmp/agent-exec-<uid>/u5-probes/`。原始 CLI 日志留在控制器的私有 run 目录。这些文件不进入 Git。
默认单元测试使用隔离 PATH 上的假 CLI,只验证上述核对逻辑。
## 仍为 NOT_RUN
| profile | 探针 | 模型收据 | transport | 变更路径 | 清理/保留 | 规范检出 | 能力结论 |
|---|---|---|---|---|---|---|---|
| cursor | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| antigravity | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| claude | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| codex | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| pi | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| kimi | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
| opencode | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN |
Host 回退演练、完整治理回归、独立 Review、精确 HEAD CI 与合入也不在本夹具的已执行范围内。
+697 -1
View File
@@ -1,8 +1,14 @@
"""End-to-end execution controller contract for agent-exec (U3)."""
"""End-to-end execution controller contract for agent-exec (U3).
U5 adds an opt-in real CLI probe. Default discovery does not contact providers.
Set RUN_REAL_AGENT_EXEC=1 and AGENT_EXEC_REAL_PROFILE to exactly one profile.
"""
from __future__ import annotations
import json
import os
import re
import shlex
import shutil
import stat
import subprocess
@@ -1368,5 +1374,695 @@ class AgentExecControllerTests(unittest.TestCase):
self._assert_canonical_untouched()
# Opt-in real CLI probes (U5). Default unittest must not contact providers.
U5_PROFILES = (
"cursor",
"antigravity",
"claude",
"codex",
"pi",
"kimi",
"opencode",
)
U5_EXECUTABLE = {
"cursor": "cursor-agent",
"antigravity": "agy",
"claude": "claude",
"codex": "codex",
"pi": "pi",
"kimi": "kimi",
"opencode": "opencode",
}
U5_BRANCH = "feature/u5-real-probe"
U5_MARKER_REL = "src/u5-marker.txt"
U5_MARKER_BODY = "U5-REAL-PROBE-MARKER\n"
_MODEL_RECEIPT_RE = re.compile(
r'"model_receipt_status"\s*:\s*"(verified|not-observable|mismatch|unavailable)"'
)
def real_probe_opt_in(env: os._Environ[str] | dict[str, str] | None = None) -> bool:
"""True only for the explicit host gate. Other truthy strings stay closed."""
source = os.environ if env is None else env
return source.get("RUN_REAL_AGENT_EXEC") == "1"
def require_single_real_profile(env: dict[str, str]) -> str:
"""Select exactly one installed-CLI profile. Reject lists and unknown names."""
raw = env.get("AGENT_EXEC_REAL_PROFILE", "")
if not raw or raw.strip() != raw:
raise ValueError(
"AGENT_EXEC_REAL_PROFILE must name exactly one profile"
)
if any(sep in raw for sep in (",", " ", "\t", "\n", ";", ":")):
raise ValueError(
"AGENT_EXEC_REAL_PROFILE must name exactly one profile"
)
if raw not in U5_PROFILES:
raise ValueError(f"unknown real probe profile: {raw}")
return raw
def _observe_model_receipt(attempt: dict[str, object]) -> str:
"""Record a model receipt only when the raw output actually carries one."""
chunks = [str(attempt.get("summary") or "")]
raw_log = attempt.get("raw_log")
if isinstance(raw_log, str) and raw_log:
path = Path(raw_log)
if path.is_file():
chunks.append(path.read_text(encoding="utf-8", errors="replace"))
found = _MODEL_RECEIPT_RE.findall("\n".join(chunks))
if not found:
return "not-observable"
if "mismatch" in found:
return "mismatch"
return found[-1]
def _probe_tool_bindir(extra: Path | None = None) -> tempfile.TemporaryDirectory[str]:
"""PATH containing only git, python3, and an optional fake CLI directory."""
tool_dir = tempfile.TemporaryDirectory(prefix="u5-probe-path-")
bindir = Path(tool_dir.name)
if extra is not None:
for entry in extra.iterdir():
target = bindir / entry.name
if entry.is_file():
target.symlink_to(entry.resolve())
for tool in ("git", "python3"):
found = shutil.which(tool)
if not found:
tool_dir.cleanup()
raise RuntimeError(f"required tool missing from PATH: {tool}")
link = bindir / tool
if not link.exists():
link.symlink_to(found)
return tool_dir
def _write_probe_double(
bindir: Path,
name: str,
*,
help_text: str,
run_body: str,
) -> None:
path = bindir / name
# printf is a shell builtin. The offline PATH has no cat/mkdir.
help_lines = help_text.splitlines() or [""]
help_printf = "".join(
f" printf '%s\\n' {shlex.quote(line)}\n" for line in help_lines
)
path.write_text(
"#!/bin/sh\n"
'if [ "$1" = "--version" ] || [ "$1" = "-v" ] || [ "$1" = "-V" ]; then\n'
' echo "0.0.1-probe"\n'
" exit 0\n"
"fi\n"
'if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then\n'
+ help_printf
+ " exit 0\n"
"fi\n"
+ run_body,
encoding="utf-8",
)
path.chmod(path.stat().st_mode | stat.S_IEXEC)
def _cursor_help() -> str:
from scripts.agent_exec_adapters import _REQUIRED_HELP_MARKERS
return "\n".join(_REQUIRED_HELP_MARKERS["cursor"]) + "\n"
def _marker_run_body(*, body: str, model_receipt: str | None) -> str:
payload: dict[str, object] = {
"terminal_status": "completed",
"summary": "wrote marker",
"changed_files": [U5_MARKER_REL],
"evidence": [],
"scope_expansion": None,
}
if model_receipt is not None:
payload["model_receipt_status"] = model_receipt
encoded = json.dumps(payload, separators=(",", ":"))
writer = (
"from pathlib import Path; "
f"p=Path({json.dumps(U5_MARKER_REL)}); "
"p.parent.mkdir(parents=True); "
f"p.write_text({json.dumps(body)}, encoding='utf-8')"
)
return (
f"python3 -c {shlex.quote(writer)}\n"
f"printf '%s\\n' {shlex.quote(encoded)}\n"
"exit 0\n"
)
def _write_probe_unit(path: Path, timeout_seconds: int) -> None:
packet = {
"unit_id": "u5-real-probe",
"goal": (
"Create src/u5-marker.txt containing the ASCII bytes "
"U5-REAL-PROBE-MARKER followed by one newline (0x0A). "
"Do not create or modify any other path."
),
"allowed_paths": [U5_MARKER_REL],
"verification": ["test -f src/u5-marker.txt"],
"constraints": [
"Write only src/u5-marker.txt",
"Exact bytes are U5-REAL-PROBE-MARKER plus a single trailing newline",
"Leave the change uncommitted",
"Do not commit, push, open a PR, merge, or deploy",
],
"network": True,
"external_cost": True,
"timeout_seconds": timeout_seconds,
}
path.write_text(json.dumps(packet, indent=2) + "\n", encoding="utf-8")
def _cleanup_probe_runtime(repo: Path, receipt: dict[str, object] | None) -> None:
if not receipt:
return
run_dir = receipt.get("run_dir")
attempts = receipt.get("attempts")
if isinstance(attempts, list):
for attempt in attempts:
if not isinstance(attempt, dict):
continue
worktree = attempt.get("worktree")
if isinstance(worktree, str) and worktree:
_git(repo, "worktree", "remove", "--force", worktree, check=False)
shutil.rmtree(worktree, ignore_errors=True)
if isinstance(run_dir, str) and run_dir:
shutil.rmtree(run_dir, ignore_errors=True)
def execute_marker_probe(
profile: str,
*,
offline_bindir: Path | None,
timeout_seconds: int = 20,
retain_fixture: bool = False,
) -> dict[str, object]:
"""Run one marker unit against a single profile.
Offline fixtures pass ``offline_bindir`` and never use the ambient PATH.
A real profile requires RUN_REAL_AGENT_EXEC=1 and keeps its fixture.
"""
if profile not in U5_EXECUTABLE:
raise ValueError(f"unknown real probe profile: {profile}")
if offline_bindir is None and not real_probe_opt_in():
raise RuntimeError(
"real Agent CLI probe refused without RUN_REAL_AGENT_EXEC=1"
)
tool_dir: tempfile.TemporaryDirectory[str] | None = None
repo_dir: tempfile.TemporaryDirectory[str] | None = None
repo: Path | None = None
receipt: dict[str, object] | None = None
try:
if offline_bindir is None:
probe_path = os.environ.get("PATH", "")
else:
tool_dir = _probe_tool_bindir(offline_bindir)
probe_path = tool_dir.name
if retain_fixture:
repo_root = Path(tempfile.mkdtemp(prefix="u5-real-probe-"))
else:
repo_dir = tempfile.TemporaryDirectory(prefix="u5-real-probe-")
repo_root = Path(repo_dir.name)
repo = repo_root / "repo"
repo.mkdir()
receipts = repo_root / "receipts"
receipts.mkdir()
init = _git(repo, "init", "-b", U5_BRANCH, check=False)
if init.returncode != 0:
_git(repo, "init")
_git(repo, "checkout", "-b", U5_BRANCH)
_git(repo, "config", "user.email", "agent-exec@example.com")
_git(repo, "config", "user.name", "Agent Exec")
(repo / "README.md").write_text("fixture\n", encoding="utf-8")
_git(repo, "add", "README.md")
_git(repo, "commit", "-m", "init")
branch = _git(repo, "rev-parse", "--abbrev-ref", "HEAD").stdout.strip()
if branch != U5_BRANCH:
raise RuntimeError(
f"probe fixture must use non-default branch {U5_BRANCH}, got {branch}"
)
_write_config(
repo,
profiles={
profile: {
"adapter": profile,
"executable": U5_EXECUTABLE[profile],
"model": "auto",
}
},
role_order=[profile],
timeout_seconds=timeout_seconds,
summary_bytes=4096,
raw_output_bytes=65536,
)
before = _canonical_snapshot(repo)
unit = repo_root / "unit.json"
_write_probe_unit(unit, timeout_seconds)
receipt_path = receipts / "probe.json"
env = os.environ.copy()
env["PYTHONDONTWRITEBYTECODE"] = "1"
env["PATH"] = probe_path
workspace = Path(__file__).resolve().parents[1]
existing = env.get("PYTHONPATH", "")
env["PYTHONPATH"] = (
f"{workspace}{os.pathsep}{existing}" if existing else str(workspace)
)
proc = subprocess.run(
[
"python3",
"-m",
"scripts.agent_exec",
"run",
"--repo",
str(repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt_path),
],
cwd=str(repo_root),
capture_output=True,
text=True,
env=env,
timeout=timeout_seconds + 30,
)
receipt = (
json.loads(receipt_path.read_text(encoding="utf-8"))
if receipt_path.is_file()
else None
)
outcome = _classify_marker_probe(
profile=profile,
repo=repo,
before=before,
receipt=receipt,
exit_code=proc.returncode,
stderr=proc.stderr,
)
outcome["fixture_repo"] = str(repo)
if retain_fixture and receipt_path.is_file():
durable = Path(f"/tmp/agent-exec-{os.getuid()}") / "u5-probes"
durable.mkdir(mode=0o700, parents=True, exist_ok=True)
os.chmod(durable, 0o700)
stamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
copied = durable / f"{profile}-{stamp}.json"
shutil.copy2(receipt_path, copied)
outcome["source_external_receipt"] = str(copied)
return outcome
finally:
if not retain_fixture and repo is not None:
_cleanup_probe_runtime(repo, receipt if isinstance(receipt, dict) else None)
if tool_dir is not None:
tool_dir.cleanup()
if repo_dir is not None:
repo_dir.cleanup()
def _classify_marker_probe(
*,
profile: str,
repo: Path,
before: dict[str, str],
receipt: dict[str, object] | None,
exit_code: int,
stderr: str,
) -> dict[str, object]:
base: dict[str, object] = {
"profile": profile,
"exit_code": exit_code,
"canonical_preserved": _canonical_snapshot(repo) == before,
"marker_in_canonical": (repo / U5_MARKER_REL).exists(),
"configured_model": None,
"model_receipt_status": "not-observable",
"branch": None,
"changed_paths": [],
"transport_parent": None,
"transport_ref": None,
"attempt_status": None,
"run_terminal": None,
"cleanup": {},
"host_fallback_authorizations": 0,
"base_head": before["head"],
}
if not base["canonical_preserved"]:
base["capability"] = "failed"
base["reason"] = "canonical checkout mutated"
return base
if base["marker_in_canonical"]:
base["capability"] = "failed"
base["reason"] = "canonical checkout contains the probe marker"
return base
if not isinstance(receipt, dict):
base["capability"] = "failed"
base["reason"] = f"missing receipt: {stderr[-500:]}"
return base
base["run_terminal"] = receipt.get("terminal_status")
base["host_fallback_authorizations"] = receipt.get(
"host_fallback_authorizations", 0
)
summary = receipt.get("config_summary")
profiles = summary.get("profiles") if isinstance(summary, dict) else None
roles = summary.get("roles") if isinstance(summary, dict) else None
if not isinstance(profiles, dict) or set(profiles) != {profile}:
base["capability"] = "failed"
base["reason"] = "probe configured a profile other than the selection"
return base
selected = profiles[profile]
if isinstance(selected, dict):
base["configured_model"] = selected.get("model")
implementation = roles.get("implementation") if isinstance(roles, dict) else None
if implementation != [profile]:
base["capability"] = "failed"
base["reason"] = "implementation role is not exactly the selected profile"
return base
recorded_base = receipt.get("base")
if isinstance(recorded_base, dict):
base["branch"] = recorded_base.get("branch")
if base["branch"] in {"main", "master", "HEAD", None}:
base["capability"] = "failed"
base["reason"] = f"probe branch is not a non-default branch: {base['branch']}"
return base
attempts = receipt.get("attempts")
if not isinstance(attempts, list) or len(attempts) != 1:
base["capability"] = "failed"
base["reason"] = "probe must record exactly one attempt"
return base
attempt = attempts[0]
if not isinstance(attempt, dict) or attempt.get("profile") != profile:
base["capability"] = "failed"
base["reason"] = "attempt profile does not match the selection"
return base
base["attempt_status"] = attempt.get("terminal_status")
base["changed_paths"] = list(attempt.get("changed_paths") or [])
base["transport_ref"] = attempt.get("transport_ref")
cleanup = attempt.get("cleanup") if isinstance(attempt.get("cleanup"), dict) else {}
base["cleanup"] = {
"exact_restore": bool(cleanup.get("exact_restore")),
"transport_created": bool(cleanup.get("transport_created")),
"workspace_preserved": bool(attempt.get("workspace_preserved")),
"skipped": bool(cleanup.get("skipped")),
}
base["model_receipt_status"] = _observe_model_receipt(attempt)
reason = attempt.get("reason") or receipt.get("reason")
if attempt.get("terminal_status") == "unavailable":
base["capability"] = "unavailable"
base["reason"] = reason or "adapter unavailable"
if base["transport_ref"] or base["cleanup"]["transport_created"]:
base["capability"] = "failed"
base["reason"] = "unavailable probe still created transport"
return base
if (
receipt.get("terminal_status") == "success"
and attempt.get("terminal_status") == "success"
):
return _classify_successful_marker(
repo=repo,
receipt=receipt,
attempt=attempt,
before=before,
base=base,
)
changed = list(attempt.get("changed_paths") or [])
if (
base["canonical_preserved"]
and not base["marker_in_canonical"]
and not attempt.get("transport_ref")
and not changed
and attempt.get("terminal_status")
in {"failed-clean", "failed-unknown", "unavailable"}
):
base["capability"] = "unavailable"
base["reason"] = reason or "probe produced no marker"
return base
base["capability"] = "failed"
base["reason"] = reason or f"unclassified probe status {attempt.get('terminal_status')}"
return base
def _classify_successful_marker(
*,
repo: Path,
receipt: dict[str, object],
attempt: dict[str, object],
before: dict[str, str],
base: dict[str, object],
) -> dict[str, object]:
if base["model_receipt_status"] == "mismatch":
base["capability"] = "failed"
base["reason"] = "model receipt mismatch"
return base
if base["configured_model"] is not None:
base["capability"] = "failed"
base["reason"] = "auto model must omit the configured model value"
return base
changed = list(attempt.get("changed_paths") or [])
if changed != [U5_MARKER_REL]:
base["capability"] = "failed"
base["reason"] = f"changed paths are not exactly the marker: {changed}"
return base
cleanup = base["cleanup"]
if not isinstance(cleanup, dict):
base["capability"] = "failed"
base["reason"] = "missing cleanup record"
return base
if cleanup.get("exact_restore") or not cleanup.get("transport_created"):
base["capability"] = "failed"
base["reason"] = "success must retain transport and skip exact restore"
return base
if not cleanup.get("workspace_preserved"):
base["capability"] = "failed"
base["reason"] = "success must retain the isolated workspace until host review"
return base
transport = receipt.get("transport")
ref = attempt.get("transport_ref")
if not isinstance(transport, dict) or not isinstance(ref, str) or not ref:
base["capability"] = "failed"
base["reason"] = "success is missing a transport ref"
return base
if transport.get("ref") != ref:
base["capability"] = "failed"
base["reason"] = "receipt transport ref does not match the attempt"
return base
commit = _git(repo, "rev-parse", ref).stdout.strip()
parents = _git(repo, "rev-list", "--parents", "-n", "1", commit).stdout.split()
if len(parents) != 2:
base["capability"] = "failed"
base["reason"] = f"transport commit is not base-parented: {parents}"
return base
parent = parents[1]
base_head = before["head"]
recorded = receipt.get("base")
recorded_head = recorded.get("head") if isinstance(recorded, dict) else None
if parent != base_head or transport.get("parent") != base_head or recorded_head != base_head:
base["capability"] = "failed"
base["reason"] = "transport parent is not the fixture base HEAD"
return base
blob = _git(repo, "cat-file", "-p", f"{commit}:{U5_MARKER_REL}").stdout
worktree = attempt.get("worktree")
live = ""
if isinstance(worktree, str):
marker_path = Path(worktree) / U5_MARKER_REL
if marker_path.is_file():
live = marker_path.read_text(encoding="utf-8")
if blob != U5_MARKER_BODY or live != U5_MARKER_BODY:
base["capability"] = "failed"
base["reason"] = "marker bytes differ from the required probe content"
base["marker_bytes"] = blob
return base
base["transport_parent"] = parent
base["marker_bytes"] = blob
base["capability"] = "PASS"
base["reason"] = None
return base
class RealAgentCliProbeTests(unittest.TestCase):
"""U5 marker probe. Real providers run only behind RUN_REAL_AGENT_EXEC=1."""
def test_profile_selector_accepts_exactly_one_profile(self) -> None:
self.assertEqual(U5_PROFILES, tuple(U5_EXECUTABLE))
self.assertFalse(real_probe_opt_in({"RUN_REAL_AGENT_EXEC": "true"}))
self.assertFalse(real_probe_opt_in({}))
self.assertTrue(real_probe_opt_in({"RUN_REAL_AGENT_EXEC": "1"}))
self.assertEqual(
require_single_real_profile({"AGENT_EXEC_REAL_PROFILE": "pi"}),
"pi",
)
for raw in ("", " cursor", "cursor ", "cursor,codex", "cursor codex", "nope"):
with self.assertRaises(ValueError):
require_single_real_profile({"AGENT_EXEC_REAL_PROFILE": raw})
def test_real_probe_refuses_without_opt_in(self) -> None:
self.assertNotEqual(os.environ.get("RUN_REAL_AGENT_EXEC"), "1")
with self.assertRaises(RuntimeError) as caught:
execute_marker_probe("cursor", offline_bindir=None)
self.assertIn("RUN_REAL_AGENT_EXEC=1", str(caught.exception))
def test_offline_success_checks_marker_transport_and_canonical(self) -> None:
outcome = self._run_cursor(
_marker_run_body(body=U5_MARKER_BODY, model_receipt=None)
)
self.assertEqual(outcome["capability"], "PASS", outcome)
self.assertEqual(outcome["marker_bytes"], U5_MARKER_BODY)
self.assertEqual(outcome["changed_paths"], [U5_MARKER_REL])
self.assertEqual(outcome["transport_parent"], outcome["base_head"])
self.assertTrue(outcome["transport_parent"])
self.assertEqual(outcome["branch"], U5_BRANCH)
self.assertIsNone(outcome["configured_model"])
self.assertEqual(outcome["model_receipt_status"], "not-observable")
self.assertEqual(outcome["run_terminal"], "success")
cleanup = outcome["cleanup"]
self.assertIsInstance(cleanup, dict)
assert isinstance(cleanup, dict)
self.assertTrue(cleanup["transport_created"])
self.assertTrue(cleanup["workspace_preserved"])
self.assertFalse(cleanup["exact_restore"])
self.assertTrue(outcome["canonical_preserved"])
self.assertFalse(outcome["marker_in_canonical"])
self.assertEqual(outcome["host_fallback_authorizations"], 0)
def test_observable_verified_model_receipt_stays_attached_to_success(self) -> None:
outcome = self._run_cursor(
_marker_run_body(body=U5_MARKER_BODY, model_receipt="verified")
)
self.assertEqual(outcome["capability"], "PASS", outcome)
self.assertEqual(outcome["model_receipt_status"], "verified")
def test_model_receipt_mismatch_is_not_pass(self) -> None:
outcome = self._run_cursor(
_marker_run_body(body=U5_MARKER_BODY, model_receipt="mismatch")
)
self.assertEqual(outcome["capability"], "failed", outcome)
self.assertNotEqual(outcome["capability"], "PASS")
self.assertEqual(outcome["model_receipt_status"], "mismatch")
def test_wrong_marker_is_not_pass(self) -> None:
outcome = self._run_cursor(
_marker_run_body(body="WRONG\n", model_receipt=None)
)
self.assertEqual(outcome["capability"], "failed", outcome)
self.assertNotEqual(outcome["capability"], "PASS")
self.assertTrue(outcome["canonical_preserved"])
def test_missing_opencode_is_unavailable_not_pass(self) -> None:
outcome = self._run_offline("opencode", help_text="", run_body="exit 0\n", install=False)
self.assertEqual(outcome["capability"], "unavailable", outcome)
self.assertNotEqual(outcome["capability"], "PASS")
self.assertEqual(outcome["attempt_status"], "unavailable")
self.assertEqual(outcome["run_terminal"], "host-fallback-authorized")
self.assertEqual(outcome["host_fallback_authorizations"], 1)
self.assertFalse(outcome["marker_in_canonical"])
self.assertTrue(outcome["canonical_preserved"])
cleanup = outcome["cleanup"]
assert isinstance(cleanup, dict)
self.assertTrue(cleanup["skipped"])
self.assertFalse(cleanup["transport_created"])
def test_incompatible_opencode_is_unavailable_not_pass(self) -> None:
outcome = self._run_offline(
"opencode",
help_text="opencode\nusage: opencode\n",
run_body=(
"mkdir -p src\n"
f"printf '%s\\n' 'SHOULD-NOT-RUN' > {U5_MARKER_REL}\n"
"exit 0\n"
),
install=True,
)
self.assertEqual(outcome["capability"], "unavailable", outcome)
self.assertNotEqual(outcome["capability"], "PASS")
self.assertEqual(outcome["attempt_status"], "unavailable")
self.assertIn("missing required capability", str(outcome["reason"]))
self.assertFalse(outcome["marker_in_canonical"])
self.assertNotEqual(outcome["run_terminal"], "success")
def test_launched_failure_without_marker_is_unavailable_not_pass(self) -> None:
outcome = self._run_cursor("echo 'auth failed' >&2\nexit 3\n")
self.assertEqual(outcome["capability"], "unavailable", outcome)
self.assertNotEqual(outcome["capability"], "PASS")
self.assertNotEqual(outcome["run_terminal"], "success")
self.assertEqual(outcome["host_fallback_authorizations"], 0)
self.assertTrue(outcome["canonical_preserved"])
self.assertFalse(outcome["marker_in_canonical"])
@unittest.skipUnless(
real_probe_opt_in(),
"real CLI probe NOT_RUN: set RUN_REAL_AGENT_EXEC=1 and "
"AGENT_EXEC_REAL_PROFILE to exactly one profile",
)
def test_opt_in_real_profile_probe(self) -> None:
profile = require_single_real_profile(dict(os.environ))
outcome = execute_marker_probe(
profile,
offline_bindir=None,
timeout_seconds=1800,
retain_fixture=True,
)
print("U5-PROBE " + json.dumps(outcome, sort_keys=True, default=str))
if profile == "opencode" and outcome["capability"] == "unavailable":
self.assertNotEqual(outcome["capability"], "PASS")
self.assertEqual(outcome["attempt_status"], "unavailable")
self.assertFalse(outcome["marker_in_canonical"])
self.assertTrue(outcome["canonical_preserved"])
return
self.assertEqual(outcome["capability"], "PASS", outcome)
self.assertEqual(outcome["marker_bytes"], U5_MARKER_BODY)
self.assertEqual(outcome["changed_paths"], [U5_MARKER_REL])
self.assertTrue(outcome["transport_parent"])
self.assertEqual(outcome["branch"], U5_BRANCH)
self.assertNotEqual(outcome["model_receipt_status"], "mismatch")
self.assertTrue(outcome["canonical_preserved"])
def _run_cursor(self, run_body: str) -> dict[str, object]:
return self._run_offline(
"cursor",
help_text=_cursor_help(),
run_body=run_body,
install=True,
)
def _run_offline(
self,
profile: str,
*,
help_text: str,
run_body: str,
install: bool,
) -> dict[str, object]:
fake = tempfile.TemporaryDirectory(prefix="u5-fake-cli-")
self.addCleanup(fake.cleanup)
bindir = Path(fake.name)
if install:
_write_probe_double(
bindir,
U5_EXECUTABLE[profile],
help_text=help_text,
run_body=run_body,
)
return execute_marker_probe(
profile,
offline_bindir=bindir,
timeout_seconds=20,
retain_fixture=False,
)
if __name__ == "__main__":
unittest.main()