Install
openclaw skills install @carloswonmore/codebuddy-cli-delegation把大批量编码任务派给「独立无头 CodeBuddy CLI」执行的标准流程。CodeBuddy CLI 专用(非通用 CLI 技能):干净环境启动(避免宿主注入环境变量导致 0 字节静默挂死)、指令预算与并行准入(四隔离 + 写集不相交)、模型与参数分配(模型由派发方指定,参数按阶段分配)、独立审计验收(对抗性探针 + 变异对照,不采信 CLI 自述)。含 21 个可用模型清单与厂商档位映射、启动器模板,全部结论基于 CodeBuddy CLI 2.156.0 实测。用于批量改造、批量补测试、批量文档回填、无人值守跑批次等前台会超时的大任务。触发词:用 CLI 派活、让 CLI 干一批、无人值守跑、交给 CLI 做、批量改造、批量补测试、前台会超时、代码审计外包、headless CLI、CodeBuddy CLI。英文关键词 / English keywords:delegate to headless CodeBuddy CLI, unattended batch run, batch refactor, batch test authoring, agent delegation audit, adversarial probe, mutation control, clean-env launcher, headless agent delegation.
openclaw skills install @carloswonmore/codebuddy-cli-delegation一句话:把一批活交给一个独立启动的无头 CodeBuddy CLI agent 去干,你只负责 写清指令 → 控预算 → 收产物 → 独立验收。
它解决什么:前台同步做会超时 / 上下文会被撑爆 / 需要并行多批次的大活。 它不解决什么:需要人来拍板的决策、需要访问你所在宿主私有状态的事、一次性的小改动。
⚠️ 适用范围:这是 CodeBuddy CLI 专用技能
本技能的全部实测结论都只对 CodeBuddy CLI 成立,具体包括: 干净环境启动器要剥离的变量名(
CODEBUDDY_*/ACC_PRODUCT_CONFIG_*/SERVER__*)、--setting-sources project、--permission-mode bypassPermissions、--effort六档档位与厂商映射、21个可用模型清单、--agents的 JSON schema、-c恢复会话、--tools白名单、以及会话日志路径~/.codebuddy/projects/。换用别的 CLI(Codex / Gemini CLI / Claude Code 等)时不要照搬本技能: 那些 CLI 的注入变量清单、参数名、会话日志结构、权限机制都不同, 必须各自重新实测,否则会踩到「按CodeBuddy 的假设去剥别的 CLI 的变量」这类错。
其中真正与CLI 无关、可直接复用的方法论(换 CLI 后仍成立,只需重新验证载体): §二(并行准入与四隔离)、§3.5(派发前契约核对六动作)、§7.3–7.7(对抗探针与变异对照)、§零铁律 3(不采信自述)。 建议这些部分另立一份
agent-delegation-audit之类的通用技能, 由本技能与将来的其他 CLI 技能共同引用 —— 但那需要另做一次提炼与验证。
EADDRINUSE」—— 该结论已于 2026-10-01 实测推翻,详见 §二。--model 当成输入而不是建议值。技能该提供的是「模型可用性事实表」(§一)
与「在给定模型下怎么分档」(§五之三),不是「这个阶段该用哪个模型」。
⚠️ 写过「阶段 → 推荐模型」的速查表是危险品:它会让下一次派活悄悄滑向「技能替人做技术选型」,
而选型责任与后果都在派发方。想按阶段换模型,必须由派发方显式拆成多批或多子代理(§五之三)。Agent 宿主(WorkBuddy / VS Code 扩展 / 桌面端)向子进程注入的变量里,至少有三类会让独立 CLI 出错:
| 注入变量 | 后果 |
|---|---|
*_SERVICE_PROXY_URL=http://127.0.0.1:<宿主端口>/... | 独立 CLI 会去绑定同一个端口提供 service-proxy → EADDRINUSE 未捕获异常 → 进程永久挂死(0 字节输出,300s 无任何反应) |
*_CONFIG_DIR=<宿主配置目录> | CLI 误读宿主的配置目录:技能 / 模型 / 插件全错位 |
SERVER__PORT / SERVER__HOST(没有前缀,容易被漏掉) | 同上,仍会去 listen 那个已被占用的端口 → 挂死 |
<产品身份配置>(如 ACC_PRODUCT_CONFIG_PATH) | 决定了 CLI 用哪个 authentication.id 去读凭据 → 去读宿主的凭据文件 → 报「未登录」(但其实是登录着的) |
做法:启动前把所有疑似注入变量 unset 掉,再让 CLI 回落到它自己的默认位置。
.js,三平台通用scripts/codebuddy-cli.js —— 一个文件覆盖 Windows / macOS / Linux,无需 .sh / .ps1 / .bat。
| 环境 | 调用方式 |
|---|---|
| 任意平台 | node scripts/codebuddy-cli.js -p "指令" --model hy3 --effort high |
| Linux/macOS(先授权一次) | chmod +x scripts/codebuddy-cli.js 后 ./scripts/codebuddy-cli.js -p "指令" |
用
node前缀最稳:Windows 上双击/直接执行需要文件关联,而.js的关联不总存在。
#!/usr/bin/env node),装了它就必然有 Node.js。
而 shell 的可用性没有这种保证(Windows 可能没 Git Bash,POSIX 未必有 Git Bash)。.md .txt .json .yaml .yml .js .cjs .mjs .ts .py .sh .png .jpg .svg。
.js 在内,.ps1 / .bat / .cmd 都不在 ⇒ 一份 .js 是唯一能同时满足
"跨平台"与"可发布"的形态。.sh/.ps1 上连续踩了:UTF-8 BOM、
sh 与 Node 的路径语义不一致、多层引号下 sed 静默失效、ProgramFiles(x86) 解析……
换成 Node 后这些整类消失(没有 shell 方言,也没有编码歧义)。CODEBUDDY_CODE_GIT_BASH_PATH —— 否则 CLI 会退化成 PowerShell;
探测顺序:PATH 里的 bash.exe → 各盘 Program Files\Git\bin\ → PATH 目录里任意 bash.exePATH 各目录找 codebuddy(Windows 上按 PATHEXT
覆盖 .cmd/.exe)→ 从 wrapper 所在目录反推真实 js 入口 → 扫 npm 常见全局安装位置-Prompt / -Model 这类具名参数会被转成 CLI 认识的形态--append-system-prompt —— 读取同目录的 skills-bootstrap.md(不存在则跳过)stdio: 'inherit' 启动 ⇒ 输出直接透传,日志不会被脚本污染;退出码原样传出💡
skills-bootstrap.md要放在codebuddy-cli.js同目录。 复制技能时把skills-bootstrap.template.md改名成skills-bootstrap.md放在旁边即可。
直接照抄 .ps1 版的写法会报错:
PS> node codebuddy-cli.js -Prompt "回答一个字:好" -OutputFormat json
error: unknown option '-Prompt' ← CLI 只认 -p / --print
原因:.ps1 版有 -Prompt/-Model/-Effort 具名参数,而 .js 版一开始是纯透传。
两版接口不一致 ⇒ 换版本时踩坑。
⇒ 现已加翻译层,以下三种风格实测均通过:
# 风格 A:PowerShell 具名参数(对 .ps1 版用户最自然)
node scripts/codebuddy-cli.js -Prompt "指令" -Model hy3 -Effort high -OutputFormat json
# 风格 B:CLI 原生参数
node scripts/codebuddy-cli.js -p "指令" --model hy3 --effort high --output-format json
# 风格 C:两者混用
node scripts/codebuddy-cli.js -Prompt "指令" --effort high --output-format json
翻译表(只覆盖 CLI 认识的那几个,未识别的参数原样透传 ⇒ CLI 未来新增参数仍可用):
| 具名写法 | 转为 |
|---|---|
-Prompt / -prompt | -p |
-Model / -model | --model |
-Effort / -effort | --effort |
-MaxTurns / -Max-turns | --max-turns |
-OutputFormat / -Output-format | --output-format |
-PermissionMode / -permission-mode | --permission-mode |
💡 只翻译「独立的参数项」(不带值),避免误改提示词内容里恰好相同的字样。
⚠️ 这是「接口不一致」类问题的典型:同一技能的不同版本/不同脚本用了不同参数风格, 而 CLI 侧只会报
unknown option,不会告诉你"你想要的参数其实是另一个名字"。 ⇒ 凡是提供多种调用风格,必须显式写进文档并实测,否则每个换风格的人都要重踩一次。
where / command -v 找 CLI(实测踩到)定位 CLI 时不要派生子进程去问 PATH:
// ❌ 实测在 Windows 上返回 error=EBUSY(宿主安全策略拦「进程派生进程」)
spawnSync('cmd.exe', ['/c', 'where', 'codebuddy'])
// ✅ 纯 fs 操作扫 PATH,不触发进程派生,稳
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
for (const ext of (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';')) {
const p = path.join(dir, name + ext.toLowerCase());
if (fs.existsSync(p) && fs.statSync(p).isFile()) return p;
}
}
同理,别用 npm root -g 命令来定位:① 同样要进程派生;② 它随「当前用哪个 npm」而变
(实测干净环境下它指向另一个 node 的目录 ⇒ 漏判)。改成直接 fs.existsSync 扫 npm 的标准落点。
若 stderr 出现 Git Bash not detected,说明脚本没能给 CODEBUDDY_CODE_GIT_BASH_PATH
指到可执行文件。按序自查:
// 逐条打印,看是哪一步没命中
process.env.PATH.split(path.delimiter).forEach(d => console.log(d));
// 手动确认目标存在
console.log(fs.existsSync('D:\\Program Files\\Git\\bin\\bash.exe'));
也可能是 Git Bash 确实没装(此时装一个即可,或确认退化成 PowerShell 对你的任务无影响—— 只在 CLI 需要真正执行 shell 命令时才有影响,纯问答/改文件不受影响)。
启动器从三平台 shell脚本换成单文件 .js,前后翻了七轮。前六轮是
「检查通过但实际不可用」,第七轮是接口不一致:
| 轮次 | 场景 | 我改了什么 | 检查结果 | 实际后果 |
|---|---|---|---|---|
| ① | .sh | 写三平台启动器 | sh -n 通过 | 逻辑错 5 处:多层引号下 sed 静默失效、exec 调shell 函数(无效语法)、npm root -g 漏判、wrapper 当 js 入口跑、路径语义不一致 |
| ② | .ps1 | 修 Join-Path 空值 | 花括号仍配平 | 从「能跑但 stderr 脏」退化为「跑不起来」 —— 引入 UTF-8 BOM 问题 |
| ③ | .ps1 | 加 UTF-8 BOM | 括号平衡 | ✅ 通过(用户实跑确认) |
| ④ | .js | 改用 where 定位 CLI | node --check 通过 | error=EBUSY —— 进程派生被宿主安全策略拦,定位恒失败 |
| ⑤ | .js | 改用纯 fs 扫 PATH | 语法通过 | ✅ 通过 |
| ⑥ | .js | 补 D 盘 Git Bash 探测 | 语法通过 | 探测命中 D:\Program Files\Git\bin\bash.exe,但未能验证 CLI 是否接受(沙箱无法执行 bash) |
| ⑦ | .js | 从 .ps1 版切换过来 | node --check 通过 | error: unknown option '-Prompt' —— .ps1 有具名参数、.js 是纯透传,两版接口不一致 |
⑦的性质与前六轮不同:前六轮是"环境里的坑",⑦是我自己制造的接口不一致 —— 把一个技能的两种实现写成两套参数风格,用户换过来就必然报错, 而 CLI只会说"我不认识这个参数",不会提示你想要的那个名字长什么样。
⇒ 固化三条纪律:
① 任何脚本改动,验证标准是「在目标环境跑一次并读真实输出」,不是「语法检查通过」。 ② 改完必须重跑 —— 包括你"刚改好的"那一次。 ②轮就是修 bug 后没重跑,直接把可用状态改成了不可用。
③ 同一技能的多份实现(不同语言/不同平台),对外接口必须逐条对齐并写进文档。 不同实现之间不能各自发明参数风格;若要支持多种风格,必须显式做翻译层 + 实测。
⇒ 推论一:报错位置与根因可能无关。②轮报"第 57 行意外的 }",真因是第 11 字节的
编码解码中断 —— 查花括号永远查不出来。看到"语法错误指向某行"时,先怀疑文件编码。
⇒ 推论二:定位依赖"问系统"(where / command -v / npm root -g)本身可能就是故障点。
在受限环境(沙箱、容器、CI)里这些命令会因进程派生被拦。
⇒ 优先用纯 fs 探测:自己扫 PATH、自己拼路径,比派生子进程问系统可靠得多。
⇒ 推论三:"unknown option"类报错要往"上游版本"查,而不只是看当前脚本。 它往往意味着你手里那份文档/示例来自另一个实现。
⇒ 这与 §零铁律 3「不采信自述」同源 —— 连"我自己刚写的脚本"都必须当作需要独立验收的对象。
这张表只陈述事实,不推荐选哪个。选哪个是派发方的决定(见 §零 铁律 4)。
codebuddy --model 实测可用 21 个,各跑一次最小调用(--effort high,提示词「只回答两个字:收到」),
21/21 成功,零失败零超时,耗时 5.5–14.8s:
| 档 | 模型 ID |
|---|---|
| 混元 | hy3、hy3-x、hy4-preview |
| DeepSeek | deepseek-v4.1-flash、deepseek-v4-pro |
| GLM | glm-5.3、glm-5.3-flash、glm-5.3-flashx、glm-5.2、glm-5.1、glm-5v-turbo |
| Kimi | kimi-k3-2、kimi-k2.8-preview、kimi-k2.7、kimi-k2.6 |
| 其他 | minimax-m3-pay、step-5-preview、space-bunny |
| 别名 | fast-model、balanced-model、deep-model |
fast / balanced / deep 三档实测思考量递增
(balanced ≈ kimi-k2.7 档,deep ≈ kimi-k3 档)。派发方只说「用 deep 档」也是合法的模型指定。glm-5v-turbo 最慢(14.8s)、hy4-preview 与 deep-model 次之(11.0s)、
deepseek-v4.1-flash 最快(5.5s)。⚠️ 这是单轮耗时;派活总耗时由轮数决定,别据此排序「谁最快」。codebuddy --help | grep -- --model 看当前清单,
不要照抄本表。⚠️ 本表没有「哪个模型最适合哪类任务」的结论,那是派发方的技术选型,不是技能该给的。 若你要「编码阶段用便宜模型、设计阶段用强模型」,请拆成多批(§2.3)或多子代理(§五之三)显式指定。
📌 下面「四隔离」与「写集不相交」是通用的,但示例里的
npm ci/prisma migrate/jest来自一套 Node + Prisma + Jest 的 monorepo。你用别的栈时,换成对应命令即可 (例如 Maven 的mvn dependency:go-offline、Python 的uv sync、.NET 的dotnet restore)—— 要隔离的本质是「各自独立的依赖目录」与「各自的数据库/schema」。
曾经的写法(错的):「无头模式的监听端口由工作区路径派生:同一工作区开第二个实例必然端口冲突并挂死。」
2026-10-01 实测(CodeBuddy CLI 2.156.0,经干净启动器):
| 实验 | 结果 |
|---|---|
| 同目录(同一工作区)并发两个实例,纯文本回复 | 两次均 subtype: success |
| 同目录并发两个实例,提示词要求真实调用 Bash 工具(关键:纯文本回复可能根本起不到 service-proxy,会让结论悬空) | 两个仍均成功(各自拿到 TOKEN-A / TOKEN-B),无 EADDRINUSE |
错误是怎么来的:把「宿主注入的固定 SERVER__PORT=37641 会让两个子进程撞同一端口」
过度概括成了「同工作区不能并发」。注入变量被启动器剥掉后,这个约束就不存在了。
方法论(比结论本身更重要):一条「实测沉淀」的规则,要能说清它约束的到底是哪个变量、哪个资源。 说成「同工作区」这种粗粒度,会把一个「注入导致的固定端口冲突」升级成一条假的技术限制, 进而挡住本来可行的设计。 判据:写下这类规则时追问一句 ——「如果我换一种启动方式,这条还成立吗?」 成立的是资源约束(该串行),不成立的只是启动方式的副作用(不该写成规则)。
默认串行的理由是工程的(共享库 / 契约 / git 工作区 + 逐批可归因审计),不是技术限制。 要并行,就逐项隔离下面这四样;缺一项就会出现「串了任务」的冲突或卡死:
| # | 隔离项 | 做法 | 不隔离的后果 |
|---|---|---|---|
| 1 | 目录 + git | 每个并行批各建 git worktree(独立目录 / 独立 index / 独立 HEAD / 独立分支) | 同工作区两批同时 commit → index.lock 争用;git add -A 把对方半成品一起暂存 |
| 2 | 依赖 | 各 worktree 各自 npm ci(node_modules 不在版本库里,worktree 不会共享) | 同目录两批同时 npm ci → 彼此清空重建 node_modules → ENOENT / 假失败;prisma generate 写坏生成的 client |
| 3 | 数据库 | 各批独立 database(CREATE DATABASE <db>_<批次>)或连接串加 ?schema=<批次> | 同库并发:prisma migrate 取 PG advisory lock 互等至超时;测试 afterEach 互相删数据;全局计数断言(family=1 这类)被别的套件干扰 → 偶发红 |
| 4 | 端口 + 产物 | 各批独立日志目录;若起服务则各用不同 PORT | 业务服务端口(如 3000)撞 EADDRINUSE —— 这是业务端口,与「CLI 自身注入端口」是两码事 |
加一条门槛:写集必须不相交。 准入前先把两批的预期改动文件清单列出来比对;
同一个文件、同一份契约(openspec/**)、同一条 migration 序列都算互斥资源,交集非空即不得并行。
apps/api/** ‖ 前端 apps/pwa/** —— 不同测试套件、不同依赖树;反例(不要并行):两个批都要改同一份源码或同一份契约、都要跑同一个库的成套测试、都要 npm ci。
openspec/**(或任何共享契约),其余批先停下等它合并。排活策略:任务大就拆成多次串行,每批控制在 ~15 分钟内;确有多余算力再按 §2.1 上并行。
只把要求写成风格描述("请保持最小改动")不会触发技能加载。 必须显式要求调用 Skill 工具,
或者把要求写进 system prompt(推荐,见 §五)。
最坏的模式是「你去把这个仓库里的 X 改好」——它会把预算全花在侦察上。 好指令的骨架:
目标:<一句话,可验收>
已知(不要重复侦察):
- 入口文件:<路径:行>
- 现有基建:<可复用的函数/测试替身/配置>
- 已有结论:<上一轮已查明的口径>
改动范围:<允许改哪些文件;明确禁止改什么>
预算:最多读 <N> 个文件;迭代期只构建/只跑受影响的工程;收尾**一次**全量
验证:<跑什么命令、期望什么输出>
产出:<要写哪些文件 / 要报什么>
| 项 | 建议值 | 为什么 |
|---|---|---|
| 单次运行时长 | ≲ 15 分钟 | 单条模型请求太久会被网关/流超时掐掉,日志里表现为 error=canceled / 499 / HTTP connection closed prematurely,零产出 |
| 子代理串行数 | ≤ 2 个/批 | ⚠️ 2026-10-06 实测:3 个子代理串行(architect→mechanic→auditor)跑满 5 分钟仍未结束;同一任务用 2 个子代理 16 秒完成。串行子代理的耗时是乘性叠加,不是取最大值 |
| 任务体量 | 超过就拆 | 宁跑 3 次各 5 分钟,不跑 1 次 25 分钟 |
| 思考档位 | 默认 high;仅超时/超预算才降 | ⚠️ 旧版写「显式设中低档」是错的(误以为降档省钱)。实测反证:arXiv 2607.02436(90 次独立 agent run,固定规格 + 14 项功能评分)把 reasoning effort 从 High 提到 xHigh,首次运行完美通过率 28% → 89%,纠正性提示减少约 5 倍,成本只涨 9–29%。「探索过深」在 agent 循环里常表现为反复读同一批文件(真卡住),而不是思考深;真超时了再降档,且降的理由要写进日志,否则事后无法归因 |
--effort 档位名 | 逐字核对 | ⚠️ 非法值被静默吞掉:--effort bogus-level 返回 subtype: success,与 minimal/high 的返回完全一致,无报错无警告(2026-10-06 实测,5 个模型 × none/bogus-level 全部 rc=0)。档位名拼错无法从日志发现,只能靠「改了参数但行为没变」反推 |
--effort medium | 别指望它省成本 | DeepSeek 官方原文:medium / xhigh are accepted and mapped to high;GLM-5.3 与 Kimi K3 压根没有 medium 档(只有 low/high/max)。写 medium 想省钱,很可能跑在 high 上 |
| 读文件数 | 明确给上限 | 没有上限时它会读遍全仓 |
| 思考 token 指标 | 不要用它判断档位是否生效 | ⚠️ reasoning_tokens 是噪声指标:同一模型同一档位 3 次重复采样,值在 0~150 间乱跳(glm-5.3 high → 87/3/77,minimal → 4/37/22,两档分布完全重叠)。曾据单次采样误判「minimal vs high 差 35 倍」,3 次重复即推翻。验证档位生效要找别的判据(如 --debug 也不行:-p 模式下不打请求体) |
表现为「跑很久 + 大量只读调用 + 零文件产出」。不要重试同一条指令,而是: ① 把上一轮侦察到的结论写进新指令;② 加硬性文件/步骤预算;③ 提高超时; ④ 只有确认是超时才降档,且日志里写明「因X 超时,从 high 降到 low」。
⚠️ 别把「降档」当默认动作。它降低的是首过率(见上表),换来的只是跑完的概率, 不是跑对���概率。先用「喂结论 + 加预算」解决探索过深,这是收益最高的手段。
📌 本节是通用方法论,但下面的实战案例来自特定技术栈(Spec 驱动的 monorepo:
openspec管契约、Prisma + Jest 做测试)。 你不用这套栈时,保留六个动作本身(它们与工具无关),把「spec」「Scenario」 替换成你项目里对应的契约文档与验收项即可。
由派发方做,不是 CLI。理由很直接:此刻改契约最便宜 —— 一行 spec 措辞 vs. 一个批次的返工 + 一次审计。 实测战绩:开工前核对一次抓出 3 处缺口(1 处规则留白 + 1 处规则相乘无规定 + 1 处命名与约束冲突), 全部在零代码状态下修掉。核对的六个动作:
WHEN=<操作路径> / THEN=<可观测结果>,
逐条确认实现里真的存在那条路径。找不到 = 「有要求、无落点」。
⚠️ 抄漏 WHEN 等于把该路径静默移出验收范围(实战:验收用了直接建库的成员,而 Scenario 的 WHEN 是
「经邀请码加入家庭」⇒ 真实路径零断言)。start)漏落点;
顺手实测确认「定时路径只做过期、不做收口」⇒ 排除并不存在的第三时点。xxx_level / xxx_status)而契约说
「不落库 / 零 migration」,就是会让实现者加错东西的陷阱 —— 改名并显式写「不得新增列」。grep -rn "不在本批\|待 [0-9]\|后续变更\|本批不" <测试目录>
moduleNameMapper / setupFiles / transform / 各类 mock:
grep -rn "moduleNameMapper\|jest.mock\|__mocks__\|setupFiles" <项目>
@nestjs/swagger 映射成无操作替身
(ApiTags/ApiBearerExt/ApiExtension 全变 no-op)。本批要新建「从产物核对 41 条鉴权声明」的守卫 ——
替身在时,该守卫必然全绿,与实现是否正确无关。
⇒ 处置(三选一,必须在指令里写明选哪个):① 让守卫改从真实包读元数据(可能要先修那条 ESM 报错的根因);
② 显式用 jest.mock/loader 局部打桩,只桩掉出问题的那一个子模块;③ 换一种不依赖该包的方式取证据。
MUST NOT 默认「守卫能读到真实行为」 —— 恒真断言比没有断言更糟:它会给你一个「已验证」的假信号,
而你的变异对照也会因为同一个替身而打不红(形成闭环自欺)。三条出口:① 只是漏落点 ⇒ 直接补 tasks.md;② 涉及产品语义 ⇒ 问用户裁定,再落到 spec + design;
③ 任何改动后 openspec validate --strict 必须绿。
契约没补齐前不派发 —— 派出去的不是代码,是「一个替你猜契约的 agent」。
如果宿主用户级配置(~/.<cli>/settings.json)里被工具(如团队脚手架)注入过 hooks,
无头运行会被它们掐死,典型症状:
2>nul / exit /b 0),却由 bash 执行 → nul: Permission denied;UserPromptSubmit operation blocked by hook → 整轮失败。绕法:只加载项目级设置(CodeBuddy CLI 为 --setting-sources project),牺牲用户级插件配置。
另外:无头运行一定要 </dev/null 重定向 stdin,别让子进程等着一个永远不关闭的 stdin。
--append-system-prompt 接受一个字符串(注意:那个「从文件读系统提示」的参数会整体覆盖系统提示,不能用)。
因此做法是:仓库里放一份 skills-bootstrap.md,启动器每次把它读进来追加到 system prompt。
skills-bootstrap.md 模板# 技能强制加载(本仓库约定)
执行任务时,**加载技能是第一步,未加载不得开始编辑文件**。
按任务性质选择(调用 Skill 工具,参数为技能名):
| 任务性质 | 必须加载 | 附加要求 |
|---|---|---|
| 改动规格 / 需求文档(spec / tasks / proposal) | `openspec`(**SDD**:规格驱动) | 改完必须跑校验:`<openspec validate --all --strict 的命令>`,有失败必须修到 0 |
| 任何编码任务(写代码 / 补测试 / 重构 / 修 bug) | `ponytail`(**纪律**:最小改动、YAGNI、不加未被要求的抽象) | 不引依赖、不加防御性冗余 |
| 多步开发(新功能 / 跨文件改造 / 测试体系) | `superpowers`(**TDD**:brainstorm → plan → TDD → review) | 测试先于或同于实现;结束自审 |
| 数据库连接 / 凭据配置 | `dbhub-setup` | 该技能可能平台特定(如 macOS+Codex);**不适用就说明理由并跳过**,不要硬套 |
## 汇报要求
最终报告必须有一行 **「已加载技能:…」**,列出本次**实际调用 Skill 工具**加载了哪些技能;
不适用要写 `不适用(理由)`。**不得谎报**。
## 例外
纯读取/查询类任务(读文件、grep、看日志、只读诊断)不必加载技能。
为什么要写「独立报告一行」:这是可审计的。事后能拿会话日志核对它到底调没调
Skill(见 §七), 谎报立刻暴露。
派活前必须知道的事:下面这些不是「设了没用」,是「根本没有入口」。
| 想调的东西 | 实际情况 |
|---|---|
--temperature / --top-p | ❌ CLI 无此参数(2026-10-06 实测 --help)。走云端内置模型时由服务端决定,改不了 |
| 各家思考模式开关 | ⚠️ 部分模型支持切换(DeepSeek/Kimi K2.x/GLM 的非思考模式),但 CLI 未暴露该开关;--effort none 不报错,是否真映射到厂商的 none 语义未验证 |
| Kimi K3 的采样参数 | 🔒 官方明确「固定值,建议不要显式传入」(temperature=1.0/top_p=0.95/n=1/两个 penalty=0)—— 厂商侧锁死 |
| DeepSeek 非思考模式降确定性 | ⚠️ 只能用 temperature。官方原文:top_p only takes effect in thinking mode… In non-thinking mode it is fixed at 1.0 and the value you pass is ignored |
models.json 的 temperature | ⚠️唯一入口,但同 id 覆盖 = 完全替换整个模型定义(官方原文「配置 url 后即使与云端同 id 也会按完全替换语义生效」)。这是换模型,不是调参,别在派活里顺手做 |
⇒ 转化规则(重要):当派发方/自己提出「这批要更确定 / 更保守」时, 不要去找参数——把诉求改写成指令层约束:
| 想表达 | ❌ 找参数 | ✅ 改成 |
|---|---|---|
| 要更确定 | 调低 temperature | --tools 白名单收窄可用动作 + 指令列「禁止改动文件清单」 |
| 要少发散 | 调低 temperature/effort | 指令里写死「只按 A 方案实现,不做替代设计」+ 改动范围白名单 |
| 要仔细审查 | 调高 temperature | 该阶段单独拆一批,或派 --agents 的审查子代理(§五之三) |
| 要省成本 | 降 effort 到 low | 缩任务体量、拆多批(§2.3);真要降档先确认不是超时的锅(§3.3) |
这是「模型由派发方指定、参数由技能分配」的唯一已验证手段(2026-10-06 实测)。
--agents 里每个子代理可独立指定 model / effort / tools / maxTurns,
而主会话的 --model / --effort 保持不变 ⇒
在不改变派发方所选模型的前提下,实现阶段级差异化。
node .workbuddy/bin/codebuddy-cli.js -p "$(cat prompt.md)" \
--model <派发方指定的模型> --effort high \
--permission-mode bypassPermissions \
--agents '{
"designer": {
"description": "当被要求做方案设计时必须使用",
"prompt": "你是架构师,负责设计方案并说明取舍理由。",
"model": "<派发方指定的模型>", "effort": "max",
"tools": ["Read", "Grep"]
},
"fixer": {
"description": "当被要求修改文件时必须使用",
"prompt": "你是实现工程师,直接改文件并运行验证命令。",
"model": "<派发方指定的模型>", "effort": "low",
"tools": ["Read", "Edit", "Bash"]
}
}' \
--output-format json --max-turns 8 </dev/null
主会话也可只给 --tools 收窄动作空间(实测 --tools "Read,Glob,Grep" 只调Glob,未越界)。
--agents 是「授权调用」,不是「强制调用」 —— 主 agent 有权判断「不需要」就跳过。
实测:空目录 +「设计方案」类任务 ⇒ 子代理一个都没被调用,报告却写得很完整。
⇒ 派活指令必须写「必须调用 X 子代理」;审计时数日志里 function_call 中 name == "Agent" 的次数
(同 §7.1 数 Skill 次数的套路),零次即任务未按要求执行。
⚠️ 不显式给权限档,它会「编理由放弃改动」 —— 实测:子代理带 Edit 但主命令未加
--permission-mode bypassPermissions 时,Edit 被拦,它的自述是
「两个子代理均已调用,但实际编辑被非交互权限限制阻断」,而文件实际未改。
补上权限档后同一任务 15 秒真改完文件。
⇒ 这是 §零 铁律 3「不采信自述」的教科书案例,且是最阴的一种:
它诚实交代了阻断,但整份报告的基调让「子代理已被调用」看起来像任务已完成。
⇒ 规则:凡写批次的派活必须显式给权限档;审计必须独立 cat 目标文件确认改动落地,
不能因为报告里列了工具调用序列就认定成功。
Agent 工具入参只有 subagent_type,没有 effort / model 覆盖 ——
实测日志里该次调用参数仅 {'subagent_type': 'worker'}。
⇒ 子代理的档位在 --agents 定义时就固定,主 agent 无法中途改。
想中途换档只有两条路:拆多批(§2.3)或恢复会话(§五之四)。
-c 恢复会话 + 改 --effort(实测可行)需求:「编码阶段用 low,测试阶段临时调高」——可以实现,但不在同一次运行内,而在两次运行的交界处。
# 第一批:编码,low
node .workbuddy/bin/codebuddy-cli.js -p "$(cat step1.prompt.md)" \
--model <模型> --effort low --output-format json --max-turns 60 </dev/null >log1.json 2>&1
# 第二批:同一个工作目录,-c 恢复上一轮会话,只换effort
node .workbuddy/bin/codebuddy-cli.js -c -p "$(cat step2.prompt.md)" \
--model <模型> --effort high --output-format json --max-turns 40 </dev/null >log2.json 2>&1
✅ 实测:第一轮 --effort minimal 记下编号 A1,第二轮 -c --effort high 准确读出 A1
⇒ 上下文跨档位保留,换档有效。
⚠️ 三条限制:
-c 是「继续该目录最近的对话」,按目录定位,不按任务名 ⇒ 同目录下多批并行会串味,
此时必须改用 -r <session-id> 显式指定会话。--max-turns 是新的上限,不继承第一批剩余额度。log1.json / log2.json),审计时逐批独立复核(§七)。做不到的事:单次运行内部,主 agent 无法给自己或已派出的子代理改档。 需要「设计→编码→测试」三段不同档位时,要么拆三批,要么三段各配一个子代理(§五之三)。
--permission-mode bypassPermissions),
否则会在某个确认点上原地等死。push --force / reset --hard / 删库 / 改生产)仍然要求人在对话里明确授权。Edit 会被拦,而它的自述是
「子代理已调用,但实际编辑被非交互权限限制阻断」,文件实际未改 ——
报告基调却像任务已完成。凡写批次必给权限档,审计必独立确认文件真改了(§五之三 事实2)。mkdir -p .cli-runs
LOG=".cli-runs/$(date +%H%M%S)-<任务名>.log"
timeout 1500 node .workbuddy/bin/codebuddy-cli.js \
-p "$(cat .cli-runs/<任务名>.prompt.md)" \
--model <派发方指定的模型> --effort high \
--permission-mode bypassPermissions \
--output-format json --max-turns 150 \
</dev/null >"$LOG" 2>&1
echo "exit=$? bytes=$(wc -c <"$LOG")"
--output-format json 只在结束时落盘:运行中途日志是 0 字节属正常,不要据此判死。无头运行的会话落在 CLI 自己的 projects 目录(如 ~/.codebuddy/projects/<工作区 slug>/*.jsonl)。
注意 JSON 结构:工具调用是 type == "function_call" + name / callId / arguments,
不是 tool_use,也不是 assistant.content[]。按错结构解析会得出「0 次工具调用」的错误结论。
# 统计一次运行真正调了哪些工具 / 有没有加载技能
import json, collections
data = json.load(open(log_path, encoding="utf-8", errors="replace"))
calls = collections.Counter()
def walk(o):
if isinstance(o, dict):
if o.get("type") == "function_call":
calls[o.get("name")] += 1
for v in o.values(): walk(v)
elif isinstance(o, list):
for v in o: walk(v)
walk(data)
print(calls) # 例:{'Edit': 19, 'Skill': 1, 'Read': 3, ...}
print("技能加载次数 =", calls.get("Skill", 0))
Skill 计数是 0 就说明技能根本没加载 —— 此时它自报的「已加载技能」是谎报。
进一步核验:找到 Skill 那条 function_call 的 callId,看对应 function_call_result 的
output.text 是不是真的有技能正文(而不是报错)。
它说「测试全通过 / 脚本已实测」之后,自己再跑一遍那条命令,看输出对不对得上。 重点核对:它自述的数字(通过数、耗时、步骤数)与实际是否一致。 不一致 → 按未完成处理。
⚠️ Tests: 0 total 永远是环境问题,不是「测试通过」。复跑时必须先确认三件事,
否则你拿到的可能是一套根本没执行的测试:
npx jest 常因根目录无 jest 配置而挂在 transform 阶段
(实测:根目录跑出 25 suites failed / Tests: 0 total,全是对 babel 的报错;换到
apps/api 下跑立刻 467 通过)。要跑 workspace 的测试就进 workspace 目录,
或走 workspace 脚本(npm test -w pkg)。node_modules 缺失/半装同样表现为 0 测试或整片suite 失败。NODE_OPTIONS 之类的变量,
复跑时先unset 再跑(§一列了要剥哪些)。症状是「错误信息与代码无关」——
典型如某个批量删文件的 shim 被注入,node 进程删文件时被拦下并挂起等确认。⇒ 规则:复跑结果里凡出现 0 tests / 0 passed / 全 suite 失败,先当环境故障排查,
MUST NOT 记作「通过」或「无回归」。 一个 suites failed = N, tests = 0 的输出不含任何关于
代码正确性的信息量。
📌 方法论通用,但示例命令来自 Jest(
jest --json --outputFile、断言改常量值)。 别的栈的等价物:pytest 用--junit-xml、Vitest 用--reporter=json、 Go 用go test -json。核心机制不变:让变体真的打红目标断言,并抓住失败用例的名字。
「独立复跑全绿」只证明它的测试通过;不证明契约被满足。要自己写探针,注入它没想到的违规形态:
.send({}),
那「完全不传 body」这条路径从来没被走过。
→ 规则:写完测试追问一句「这个测试有没有可能因为构造方式而绕过被测行为?」cp 还原
(用 git diff 确认还原干净)。
⚠️ 别改控制流:把 if (cond) 改成 if (false && cond) 会破坏 TS 的类型收窄 → 编译失败,
测试根本没跑,看着像红其实不是。改常量值 / 改枚举成员才不会连带破坏类型。
⚠️ 变异「奏效」不等于断言正确 —— 变红之后还要问一句:「红的是不是针对这条条件的断言?」
实测反例:删掉某处 where 的 status 守卫,测试确实红了 2 条,但红的是幂等用例
(「已过期的行被重复命中、第二次 count ≠ 0」),守门本身仍无覆盖 ——
换一种破坏方式(保留条件、只把集合内容改错,如误把 COMPLETED 纳入可过期集)时,幂等仍成立、
测试全绿,缺陷漏过。判据:改坏「内容」时还红不红,而不是删掉「条件」时红不红
—— 只删条件,常被幂等 / 计数一类的旁证接住。作废条件前先确认有没有针对性断言。
推论:变异要让靶子变红,而不是「有东西变红」。
⚠️ 「纯补验收」批另有一条判据:变异只打红对应的那一条。补断言批(禁改实现、只加测试)里
实现一秒未动 ⇒ 不能靠「测试变绿」自证,只能靠「改坏哪一格,只有那一条红」自证。
实测:两条新断言各配一处变异,各只打红自己那一条(用例 12 / 用例 13),既有 13 条全绿 ——
这才叫「这一格被钉住」。若某变异把新旧断言一起打红,只能说「有覆盖」。
⇒ 本类批次的审计重点是 git status --porcelain <实现目录> 必须为空(非空即越权,退回);
且变异脚本要抓失败用例名(jest --json --outputFile),不是只数 failed 个数 ——
「红的是哪条」才是判据。
⚠️ 靶集必须包含「承载该断言的测试文件」——否则「零红」不可信。
变异后零红有两种解释:(a) 真缺口(断言不存在);(b) 误报(断言存在,但你这次的靶集里没跑到那个文件)。
分不清就只能靠全量复核兜底。
实测:一次「非完成分支挂信号」的变异,靶集只选了 3 个 spec 文件 ⇒ 零红;
某 spec 里的用例确实覆盖了该路径,但不在靶集内 ⇒ 必须改用全量 465 条复核才能排除误报
(全量仍零红 ⇒ 确认是真缺口)。
⇒ 两条操作规则:① 零红结论一律用全量复核一次再落槌;
② 已知断言所在文件时,优先只跑那个文件当靶集 —— 此时该文件内的零红是决定性的,
不必全量(既省时,又排除「靶集选窄」的解释)。
⚠️ 正靶 + 负对照成对做,才能同时排除「断言空转」与「断言过宽」:
CreateTagDto 的 @MaxLength(60, { message: '标签名长度不能超过 60 个字符' }),
该行在 CreateTagDto 与 UpdateTagDto 各出现一次 ⇒ 命中 2 次(靠 harness 的
「src.count(frm) != 1 即中止」自检拦下,否则会「改了 A 却以为在说 B」)。改为含 export class 的
多行锚点后通过。harness 必须内置这条自检(命中 ≠ 1 就中止,不许猜)——它是「锚点唯一性」的唯一防线。src/**(实现已验证正确)。reason / 错误码 / 状态枚举 / level)必须与「全部门禁规则」求交集,
而不是只对着本次新写的那几道门设计取值。
实测:预览端点新引入 reason: 'NOT_SUPER_ADMIN' | 'CONSENT_REQUIRED',只覆盖了「角色门」与「同意门」,
漏掉先于它们命中的「凭据类型门」(设备令牌不得用于管理员关键操作,见 4.7)⇒
超管用设备令牌调预览时,响应体 message(设备令牌文案)与 reason(NOT_SUPER_ADMIN)自相矛盾,
该字段沦为噪声。CLI 主动把它标为「已知小瑕疵、按最小改动保留」,审计不接受 ——
判据不是「403 返回错了」,而是该字段的唯一用途就是判别拒绝原因,报一个已知为假的值等于把字段做废。
⇒ 操作化:把该字段的每个取值反查「哪种情形会落到它」,再拿全部门禁清单(本项目不止角色一条)
逐个过;漏掉先命中的门 = 缺口。| 自述类型 | 怎么核实 |
|---|---|
| 事实性陈述(版本号、环境、行数、字段名) | 逐条回查。踩过:它写「目标机 Node 22」,实测是 v24。 |
| 「已知局限」(它主动交代的缺陷) | 拿契约去判,不因为它「诚实地写出来了」就放行。关键问题:契约是否区分该情形。若契约的 SHALL 无条件成立,那它交代的就不是「设计权衡」,而是契约在该情形下被违反 ⇒ 仍是缺陷。踩过:它交代「并发完成两条不同子任务时可能双双漏收口」,实测 30 轮漏 29 轮,而 spec 是无条件 SHALL ⇒ 立为缺陷。 |
| 「不确定所以停下报告」 | 这是好行为,要奖励;但你仍要给出裁定,不能让它悬着。 |
审计者自己的中间输出(grep / head / tail 截断过的) | 回查完整输出再下结论。截断会伪装成发现:踩过 grep … | head -20 只回显了前 20 行匹配,于是判定「这个 22 个端点的控制器全都没标注权限」,实际是第 21 行之后全都有 —— 差点据此报一个不存在的缺口并派一批活。⇒ 任何「零匹配 / 全都没… / 都缺…」的结论,必须用不带 head/tail 或带计数的完整命令复核(如 grep -c)。 |
发现注释/文档声称某机制生效、而读代码怀疑不生效时,不要靠读文档或记忆下结论:
把它改过去、跑一次全量,看测试怎么变。
实测例子:ValidationPipe 注释称 whitelist 会丢弃伪造字段,而源码返回的是未剥离的原始对象;
把它改成 transform: true 跑全量 → 384/386(只剩两个真缺陷)⇒ 一次同时证明了
「原判断成立」「改动修好了别的缺陷」「无回归」。
→ 规则:凡涉及框架默认行为的判断,都必须有一个「改了它之后测试怎么变」的实测。
派发那批之前,先把「审计时我要逐条过什么」写下来(预埋)——它既是审计清单,也是给 CLI 的隐含约束。
写法直接决定审计深度:
task_assignee 上只有 (task_id, member_id) 唯一约束、没有「每行至多一条」的约束
(多负责人场景加不了),故 INSERT … WHERE NOT EXISTS 的谓词「尚无负责人」落在尚未存在的行上、
无唯一索引兜底 ⇒ READ COMMITTED 下两人各插一行、键不同互不冲突 ⇒ 写偏斜 ⇒ 条件插入根本不可用。⇒ 规则:凡有 A/B 选型的地方,预埋项要连「为什么不选另一个」一起问。 只问「选了哪个」,审计退化成形式核对;问「为什么」,审计才可能改变结论。
当一批任务是「端到端验收 / 覆盖度盘点」,常见做法是让它先出一张覆盖映射表(逐验收点标
已覆盖 / 只覆盖单模块 / 未覆盖),再只补后两种。这个做法本身很省力,但埋着一个结构性作弊面:
表由它自己写、自己判。 把一条「其实只覆盖单模块」记成「已覆盖」,就能少写一条用例 —— 省事且无人察觉。
⇒ 审计必修动作(不能只看它那张表):
已覆盖 的行:至少随机 3 条(实测抽到 7 条更稳),回到它点名的用例标题,
打开 test 体确认「真的验了那个验收点」,而不是「该文件里恰好有一条相关用例」。toEqual([自己]) 类是空转断言)。已覆盖 + 单模块 + 未覆盖 = 验收点总数,且总数要能与契约正文的子句数对上
(过拆无害、欠拆才是藏缺口)。| 症状 | 根因 | 对策 |
|---|---|---|
| 0 字节输出、进程不退出 | 端口冲突(宿主已占)→ EADDRINUSE 未捕获 | 补 unset 漏掉的无前缀变量(SERVER__PORT 等) |
| 报「未登录」但手跑正常 | 产品身份变量让它读了宿主的凭据文件 | unset <产品身份配置> |
UserPromptSubmit operation blocked by hook | 用户级 hooks 语法/超时问题 | --setting-sources project;stdin 重定向 |
跑 20+ 分钟、大量只读调用、零产出、日志有 499 canceled | 单条请求超时(探索过深) | 拆任务、喂结论、降思考档、加预算、提超时 |
| 自报「已加载技能」但审计计数为 0 | 只在对话里提了技能名 | 把约定写进 system prompt(§五) |
| 「同工作区不能并行」 | 串行是默认档;并行是支持的,但须先做四隔离(目录+git / 依赖 / 数据库 / 端口)且写集不相交(§2.1) | |
| 结论与代码不符(如"已标记异常"其实没调) | 只看自述 | §七 独立复核,复跑 + 读代码 |
| 它的测试全绿但契约仍被违反 | 测试的形状绕开了触发条件(并发打同一对象 / 带 body 的测试全都 .send({})) | 写对抗探针打「它没打的形状」+ 变异对照证明探针不空转(§7.3) |
| 它主动交代了「已知局限」,就放行了 | 把「诚实」当成了「可接受」 | 拿契约判:契约若为无条件 SHALL,则仍立为缺陷(§7.4) |
| 探针跑完污染了库 → 全局断言变红,误判成改动有副作用 | 探针 beforeAll 里的诊断代码抛异常 → 清理块没跑到 | 诊断与清理解耦;看到全局性断言失败先查库残留,再怀疑被测改动 |
| 全量/lint 门禁突然红,查来查去像是改动有问题 | 审计自己的探针文件还留在被测工程里(多为未使用导入一类),而它不在交付范围内 | 探针跑完立即挪出被测目录(如 test/ → 派发目录下的 probes/),再复验门禁;先排除自己的产物,再怀疑被测改动——与上面那条同源:审计方必须先清干净自己的痕迹 |
| 预埋检查项只问「选了 A 还是 B」 | 审计退化为形式核对,错失「某个选项结构性不成立」这类回答 | 追加问「为什么这样选、给依据」(§7.6) |
契约里某条 MUST NOT 没有对应的验收条目 | 该条其实是非目标(如「不对非指派人渲染逾期红字」的渲染侧属 UI,留 PWA),只是没写清去哪 | 区分两类:「本该做却没人做」⇒ 立条目;「本不该做(非目标)⇒ 在条目内标注分流」。两类都要记录,但处置不同(§7.4 的推论) |
| 逐条核对 Requirement↔清单,全都有落点,但系统仍泄露/出错 | 缺口藏在两条规则的交集:各自都有规定,却没规定它们相遇时谁继承谁 | 这类核对不出来,必须主动把两条规则的条件相乘去构造探针(例:「状态 A 的可见性」×「挂在 A 下的对象 B」——实测撞出「待审批 主任务下的子任务对全家可见」)(§7.3) |
| 并行两批跑完,一方的改动莫名消失 / 提交里混进对方的半成品 | 两批共用一个 git 工作区:一方 git add 时另一方的改动还在工作树上 | 目录+git 隔离:各批各自 git worktree + 各自 commit,由主会话串行合并(§2.1) |
并行两批同时 npm ci / prisma generate,一方报模块找不到或 TS 编译错 | node_modules 与生成的 client 只有一份,被对方清空/重写 | 依赖隔离:各 worktree 各自 npm ci(§2.1) |
并行两批同时跑测试,出现偶发红或 migrate 长时间阻塞 | 同一个库:prisma migrate 取 PG advisory lock 互等;测试 afterEach 互相删数据、全局计数断言被别的套件干扰 | 数据库隔离:独立 database 或连接串加 ?schema=<批次>(§2.1) |
| 指令里写了「这批要保守 / 要少发散」,但产物照样跑偏 | 以为 temperature / effort 能当"发散度旋钮"用 —— 而它们要么不可调,要么不是这个作用 | 翻译成动作空间约束:改动范围白名单 + 工具白名单 + 指令里写死禁止项(§五之二转化规则)。不是去调参数 |
改了 --effort 但行为完全没变 | ⚠️ 档位名拼错被静默吞掉:bogus-level 也返回 subtype: success,与合法档位结果完全一致,无任何日志痕迹 | 逐字核对档位名;用 --settings '{"reasoningEffort":"..."}' 交叉验证;仍无效应假设厂商侧忽略该参数(§3.3) |
| 想要"省成本"于是降 effort,结果首过率大跌 | 把降档当成默认省钱手段 | 降档降低的是首过率不是"跑对"概率。先缩任务体量 / 拆多批;真超时才降且写进日志(§3.3) |
| 报告说「N 个子代理均已完成」,但文件根本没改 | 未显式给权限档 → 子代理 Edit 被拦,而它编了个理由放弃并把基调写成"已完成" | 凡写批次必给 --permission-mode;审计独立 cat 目标文件确认落地,不能只看工具调用序列(§五之三 事实2) |
报告说「子代理已调用」,审计数 Agent 调用次数为 0 | --agents 只是授权,主 agent 判断「不需要」就跳过(实测空目录场景) | 指令写「必须调用 X 子代理」;审计数 name == "Agent" 的 function_call 次数,零次即未执行(§五之三 事实1) |
用 reasoning_tokens 判断 effort 是否生效,结论摇摆 | 该指标是噪声:同模型同档位 3 次采样值在 0~150 乱跳,两档分布重叠 | 换别的判据;-p 模式下 --debug 不打请求体,只能靠可观测行为差异判断(§3.3) |
| 三子代理串行的批次跑满 5 分钟没结束 | 串行子代理耗时是乘性叠加 | 每批 ≤ 2 个子代理(实测 3 个跑满 5 分钟未完,同任务 2 个 16 秒完)(§3.3) |
用 -c 恢复会话做第二批,串到了别的事 | -c 按当前工作目录定位最近对话,同目录多批会串味 | 同目录多批并行时改用 -r <session-id> 显式指定会话;每批日志分别落盘(§五之四) |
给用户汇报时,至少要说清这六件事,缺一样都算没交付:
Skill 调用次数及加载了哪些);medium 在 DeepSeek/GLM/Kimi 上会变成 high,汇报时要写实际生效的档);凡派了子代理,额外必须说:Agent 调用次数(零次即被跳过);写批次是否独立确认过文件真的改了。
□ 启动器存在,且最小指令能在数秒内返回
□ **最小验证已跑通且 stderr 干净**
(`node .workbuddy/bin/codebuddy-cli.js -p "回答一个字:好" --output-format json`;
若出现 Git Bash 提示,说明它没探测到 bash,按 §一的自查清单处理)
□ **参数风格已确认**:`-Prompt/-Model/...` 具名写法与 `-p/--model/...` 原生写法都能用
(防"照抄另一版示例"导致 `unknown option`,见 §一七轮翻车第 ⑦ 轮)
□ **若本次改过启动器:已改完立即重跑一次**(修 bug 后不重跑=可能把可用改不可用,见 §一七轮翻车复盘)
□ **模型由派发方显式指定**(`--model <id>` 或三个别名之一);技能没有替派发方选模型(§零 铁律 4)
□ **并行度已确认**:单写批 = 1 个实例;>1 时已逐项完成四隔离(目录+git / 依赖 / 数据库 / 端口)
□ **并行批写集不相交**,且不含 `openspec/**`(契约批独占,其余批先停下等它合并)
□ 指令里有:目标 / 已知结论 / 改动范围 / 预算 / 验证命令 / 产物
□ **改动范围与工具白名单已写死**(要"保守/少发散"时靠约束,不靠调参数——§五之二)
□ `--effort` 档位名**逐字核对**过;`--settings '{"reasoningEffort":"..."}'` 可作交叉验证(§3.3)
□ **每批子代理数 ≤ 2**(3 个串行实测跑满 5 分钟未完)
□ **派发前已做契约核对**(§3.5,六动作):Scenario 的 WHEN 与 THEN 都有落点;新规则与既有规则相乘过;
无「按…判定」的判据留白;命名与「不落库」类约束不冲突;**占位断言已普查并列进指令**;
**已确认被测对象在当前测试环境里没被替身/桩换掉**(否则新守卫恒真、变异也打不红)—— 补完契约且 `validate --strict` 绿
□ 技能约定已进 system prompt(不是只在对话里提)
□ 三原则已复述:**SDD**(列出该批契约文件)/ **TDD**(先枚举判据再一条判据一条用例)/ **ponytail**(不引新依赖、不提前抽象)
□ stdin 已 </dev/null,用户级 hooks 已隔离
□ **权限档已显式给出,危险动作仍未放开**(不给权限档,子代理会编理由放弃改动——§五之三 事实2)
□ 日志落在固定目录,--output-format json
□ 审计 Skill 调用次数 + 结果正文
□ **凡派了子代理:审计 `Agent` 调用次数**(零次即"授权非强制"下被跳过——§五之三 事实1)
□ **凡写批次的派活:独立 `cat`/`git diff` 确认文件真改了**,不能只信报告里的工具调用序列
□ 独立复跑它的验证命令,比对数字;**出现 `0 tests` / 全 suite 失败先当环境故障,不得记作通过**
□ **结论前回查完整输出**(凡 `grep`/`head`/`tail` 截断过的中间产物,不得直接当证据)
□ 预埋检查项含「**为什么**这样选」而非只问「选了哪个」
□ 变异对照至少做一处(只有红过,绿才有意义);**零红须用全量复核排除「靶集选窄」的误报**
□ **正靶 + 负对照成对**(负对照零红 ⇒ 断言未过宽);正靶别只打否定面(补一发「反向半边」)
□ **变异锚点唯一**(单行常量易多址重复 ⇒ 带类名/相邻行;harness 内置「命中≠1 即中止」自检)
□ **验收类批次**:抽查映射表里标 `已覆盖` 的行回原点名用例(含承重的分界依据行)
□ 汇报含「未验证项」与「待授权项」
□ **汇报里写明用的是哪个模型**(派发方指定的 ID 或别名)、每批的 effort 档位与实际厂商映射
□ **若用了「中途换档」(`-c` 恢复):确认是显式 `-r <session-id>` 或该目录下只有这一条会话链**