omo-senpi 适配器 Live 驱动验证:DAG 依赖前沿(dependency-frontier)调度变更的隔离沙箱端到端证据
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
本文讲解 omo-senpi 适配器(packages/omo-senpi与packages/senpi-task)在调度器语义变更时的 Live 验证方法论:如何用drive.mjs驱动真实 senpi 进程在严格隔离的沙箱中加载重新生成的omo-task.js扩展包,并通过resolve-evidence-dir.mjs把证据写入仓库唯一的规范目录。文章以20260825-dag-dep-frontier变更(DAG 调度器以"依赖前沿准入"取代"严格 wave 屏障")为完整案例,读者可借此掌握 senpi 适配器的隔离 QA 流程、结果 JSON 解读与覆盖边界判断。
一、场景起点:一次 DAG 调度变更为何需要 Live 验证
2026-08-25 的 incidentdag_530ad299(sisyphuslabs "omo startup fixes PR wave")暴露了 DAG 调度器的一个真实缺陷:严格 wave 屏障(strict barrier)让 wave N+1 只能等待 wave N 最慢的节点全部结束,即使某个节点的依赖早已就绪,也会被无关的兄弟节点阻塞。修复分支fix/dag-dep-frontier将准入语义改为依赖前沿(dependency-frontier):节点准入只看"每个dependsOn节点是否completed+ 是否有空闲 resident 槽位",不再等待整波静止,详见变更证据 .omo/evidence/20260825-dag-dep-frontier/README.md。
关键点在于:调度器变更会被打进packages/omo-senpi/plugin/extensions/omo-task.js(minified 重写 + build marker)——重新生成的 bundle 才是调度器变更真正对外发布的表面。因此仅靠引擎级单元测试还不够,必须验证"真实 senpi 进程能否加载这个重新生成的 bundle,且适配器在 live harness 上仍然工作"。这正是本文主角——Live Senpi 驱动验证——的职责:
- 驱动脚本:packages/omo-senpi/scripts/qa/drive.mjs(含
--self-test) - 验证对象:分支 worktree 上重新生成的
omo-task.js扩展包 - 运行方式:一个真实
senpi会话加载插件,跑在 driver 自建的隔离沙箱中 - 证据落盘:.omo/evidence/omo-senpi-adapter/20260825-dag-dep-frontier/
二、证据目录契约:resolve-evidence-dir.mjs 唯一路径
Live Senpi QA 的证据只能放在.omo/evidence/omo-senpi-adapter/<slug>/,这是仓库级约定(根目录AGENTS.md与 senpi-qa 技能共同指向的规范位置)。手工输入的替代路径(local-ignore/qa-evidence/...、临时目录、路径穿越)会静默地把证据丢弃在 PR 无法引用的地方,因此解析路径必须走脚本而不是约定。
resolve-evidence-dir.mjs(.agents/skills/senpi-qa/scripts/resolve-evidence-dir.mjs)只做"计算 + 校验",不创建目录:
ev="$(node .agents/skills/senpi-qa/scripts/resolve-evidence-dir.mjs \ --repo-root "$(git rev-parse --show-toplevel)" --slug 20260825-dag-dep-frontier)" mkdir -p "$ev"校验规则(源码见resolveEvidenceDir与SAFE_SLUG正则/^[a-z0-9]+(?:-[a-z0-9]+)*$/):
| 约束 | 说明 |
|---|---|
| repoRoot 必须是 git worktree | 根目录下必须存在.git,否则以非零退出码拒绝 |
| slug 必须是单个相对段 | 小写字母、数字、单连字符;/、.、..、路径穿越、绝对路径全部被正则拒绝 |
| 只解析不创建 | 目录是否出现由调用方决定,被拒绝的 slug 不会留下游离目录 |
| 输出为绝对路径 | stdout 打印,退出码 0 表示通过 |
三、drive.mjs 隔离沙箱:让真实 senpi 运行在受控环境
3.1 沙箱结构与种子文件
createSandbox()在系统临时目录(mkdtempSync(join(tmpdir(), "omo-senpi-qa-")))下创建一整套隔离目录,并用realpathSync.native解析出规范路径,防止符号链接逃逸(drive.mjs 中createSandbox/seedSandbox):
| 沙箱成员 | 用途 |
|---|---|
root | 任务专属沙箱根(如.../omo-senpi-qa-GAEzts) |
project | 沙箱工作目录(cwd) |
agent | 隔离的 SENPI/OMO/PI coding agent 目录 |
xdg/xdg-data/xdg-cache | 隔离的 XDG 三目录 |
home | 隔离的 HOME |
seedSandbox在 agent 目录写入两个关键文件:
settings.json:{"defaultProjectTrust": "ask", "packages": [pluginRoot]}——只加载被测插件包(packages/omo-senpi/plugin),不继承开发者的真实配置;trust.json:{ "<canonicalCwd>": true }——只信任沙箱项目目录。
注释(源码原文)特别指出:omo 配置加载器从XDG_CONFIG_HOME读取用户作用域,如果不隔离,每条 lane 都会继承开发者真实的~/.config/omoagents 与 categories,结果就不可复现。
3.2 环境变量隔离与"调用者目录被忽略"
runSenpi派生子进程时强制覆写环境变量(drive.mjs):
OMO_CODING_AGENT_DIR / SENPI_CODING_AGENT_DIR / PI_CODING_AGENT_DIR → 沙箱 agent 目录 HOME / USERPROFILE → 沙箱 home XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME → 沙箱 xdg 三目录 PI_OFFLINE=1, OMO_SENPI_QA=1其中最关键的行为:driver 会忽略调用者通过环境变量传入的SENPI_CODING_AGENT_DIR。证据 JSON 中providedSenpiCodingAgentDir: "IGNORED"即表示调用者提供的目录被设计性地无视——driver 永远使用自己新建的沙箱 agent 目录,因此真实的~/.senpi/agent永远不会被写入(对应 SKILL.md 的黄金规则:"The real agent dir stays untouched")。runSelfTest也会显式断言沙箱目录不等于process.env.SENPI_CODING_AGENT_DIR和process.env.XDG_CONFIG_HOME。
3.3 认证根目录(certification roots)
为了证明隔离真实有效,driver 会在沙箱内预置"认证根目录"并做前后快照对比(seedCertificationRoots/snapshotCertificationRoots):
- 沙箱 home 下的
~/.senpi/agent、~/.omo/agent(预置PROTECTED_STATE_FILES各文件与persistent-state.json); - 三个 xdg 目录各写入
.qa-sentinel哨兵文件。
同时用snapshotProtectedState/snapshotRealObserved对真实的~/.senpi/agent与~/.omo/agent做前后快照。最终realSenpiUntouched: true只有在目录身份可用(directoryIdentityAvailable())且 senpi 判定为 untouched 时才为真——注意 SKILL.md 的提醒:整目录 digest 是佐证而非证明本身,结论要看 changed-path / untouched 字段。
四、验证流程:--self-test 与 live run 的判定逻辑
drive.mjs的入口分支(import.meta.url判定):带--self-test走runSelfTest(),否则走main()。
4.1--self-test:先证明 harness 本身可靠
runSelfTest()创建沙箱、种子化、然后断言:
trust.json包含规范 cwd;- 沙箱 agent 目录不等于调用者传入的
SENPI_CODING_AGENT_DIR(未被复用); - 沙箱 xdg config home 不等于调用者的
XDG_CONFIG_HOME; - 沙箱 xdg config home 确实存在;
- 不存在的目录 digest 应为
absent。
全部通过输出SELF-TEST OK(即证据文件 drive-self-test.txt 的内容)。
4.2 Live run:两个真实 senpi 会话
main()的流程(scheduler 变更验证时实际执行):
- 前置快照:对真实
~/.senpi/agent、~/.omo/agent做保护态与观测态快照; - 二进制解析:
SENPI_BIN环境变量指定 senpi 路径;若含/但文件不存在,或 PATH 上找不到,则结果为SKIP(reason:senpi-binary-unavailable)——无二进制时报 SKIP/FAIL 而不是静默降级到真实 home(黄金规则); - 会话一(ultrawork 注入):以
senpi -e <environment-receipt-entry> -e <mock-provider-entry> -p --provider omo-mock --model mock-1 "ulw please respond"启动,mock 脚本为steps: [{type:"text", text:"ultrawork scenario complete"}]。判定ultraworkInjected:子进程退出码为 0,且沙箱 agent 目录内收集到的.json/.jsonl/.log/.md文本包含<ultrawork-mode>标记; - 会话二(comment-checker):以
"write qa slop"提示词驱动 mock 的write工具调用写出qa-slop.ts,并通过OMO_COMMENT_CHECKER_BIN注入 comment-checker CLI(resolveCommentCheckerBin用createRequire从仓库解析@code-yeongyu/comment-checker/cli.js)。判定commentChecker:退出码 0 且沙箱文本包含comment-checker found issues in头则PASS,否则FAIL;找不到二进制则为SKIPPED-no-binary; - 汇总判定:
result = ultraworkInjected && (commentChecker === "PASS" || commentChecker === "SKIPPED-no-binary") ? "PASS" : "FAIL"- 收尾:
finally中rmSync(sandbox.root)删除任务专属沙箱(omo-senpi-qa-*目录在运行后被清理,driver 已退出,不留 QA 子进程)。
五、结果解读:drive-live.txt 逐字段
本次运行的证据 JSON 见 drive-live.txt:
{"result":"PASS","ultraworkInjected":true,"commentChecker":"PASS","realSenpiUntouched":true,"providedSenpiCodingAgentDir":"IGNORED","sandboxAgentDir":".../omo-senpi-qa-GAEzts/agent","sandboxCwd":".../omo-senpi-qa-GAEzts/project"}| 字段 | 值 | 含义 |
|---|---|---|
result | PASS | ultrawork 注入成功且 comment-checker 通过(或按设计跳过) |
ultraworkInjected | true | 真实 senpi 会话加载插件后注入了<ultrawork-mode> |
commentChecker | PASS | comment-checker 在隔离会话中工作并识别出问题 |
realSenpiUntouched | true | 真实的~/.senpi/agent未被写入 |
providedSenpiCodingAgentDir | IGNORED | 调用者传入的 agent 目录被设计性忽略,符合预期 |
sandboxAgentDir | .../omo-senpi-qa-GAEzts/agent | 沙箱内隔离的 agent 目录 |
sandboxCwd | .../omo-senpi-qa-GAEzts/project | 沙箱工作目录 |
printResult完整输出还包含isolationCertified、realHomeIsolationCertified、certificationLane、realSenpiChangedPaths、observationLimits、protectedStateFiles、realHomesChecked等字段(见 drive.mjs 中printResult的 payload 构造),供证据 README 引用 changed-path / isolation 字段。
清理观察:任务专属沙箱omo-senpi-qa-GAEzts在运行后被删除;driver 已退出,因此本次 QA 不残留子 PID。主机上长期存在的 senpi 进程属于用户全局 omo-ai 安装(~/.bun/install/global/...),与本 QA 无关。
六、为什么这就够了:覆盖边界与省略项
关联证据文档明确论证了"为什么 Live 驱动证明已足够":
- bundle 即发布表面:重新生成的
omo-task.js是调度器变更的对外载体;真实 senpi 进程端到端加载它,证明 bundle 可加载、适配器在 live harness 上仍然工作; - 准入语义由引擎级测试证明:admission 语义本身是 engine-level 的,由
20260825-dag-dep-frontier目录中的 failing-first 回归证明;senpi-qa 路由表将 "DAG state machine / runners" 映射到bun test packages/senpi-task,不存在专门的 live DAG-admission driver(dag-gate-proof.ts只是 manager 级 start 校验)——这与兄弟 PR #7320 的作用域决策一致。
明确的省略项(原文记录):
task-e2e.mjs/team-e2e.mjs未运行:它们演练 task/team 引擎,而本 PR 不修改该引擎(DAG 通过不变的startOwned契约驱动它,由bun test packages/senpi-task与消费方 gate 覆盖);- 捕获的 JSON 中不包含任何 secrets、token 或凭据。
七、配套门禁:回归 RED/GREEN 与引擎级 gate
Live 驱动只是证据链的一环,DAG 变更本身由完整门禁矩阵支撑(见 .omo/evidence/20260825-dag-dep-frontier/README.md):
| 门禁 | 结果 |
|---|---|
bun test packages/senpi-task/src/dag/scheduler-frontier.test.ts(pristineorigin/dev) | RED:适配后的 R2 回归在 barrier 上失败(red-origin-dev.txt) |
| 同一测试(本分支) | GREEN(green-branch.txt) |
bun test packages/senpi-task×2 | 1749 pass / 1 skip / 0 fail(rebased 后 1753 pass / 1 skip / 0 fail,dag suite 250 pass / 0 fail) |
tsgo --noEmit -p packages/senpi-task/tsconfig.json | 通过,exit 0 |
bun test packages/omo-senpi/src/components/task | 483 pass / 0 fail(消费方 wiring:dag tool + runtime) |
bun test packages/omo-senpi/src/bundle-size.test.ts | 1 pass / 0 fail(重新生成的 bundle 体积) |
node packages/omo-senpi/scripts/qa/drive.mjs(+--self-test) | {"result":"PASS",...,"realSenpiUntouched":true} |
回归测试精确钉住生产症状(就绪的依赖节点被无关的 running 兄弟节点饿死),且是事件驱动、journal-backed(whenStarted/状态订阅,无 sleep),不会靠时序运气通过。
值得注意的连带变化:SCHEDULER_FINGERPRINT_INPUT.waveAdmission从strict-barrier迁移到dependency-frontier(manager 层、fingerprint.ts中定型)——准入语义变化时指纹输入必须变化,否则旧指纹下的运行不会被复用:同一 key 重新提交同一定义会报definition_conflict,直到 7 天保留期清理该 key。
八、写在最后:Live 驱动的适用边界
从 .agents/skills/senpi-qa/SKILL.md 的路由表可以总结出适用范围:Live driver 是 harness 证明,单元测试不算 Live QA。修改适配器 wiring 时用drive.mjs(快速前置条件先跑--self-test),改 DAG 状态机/runner 时跑bun test packages/senpi-task(单元 + chaos 不变量),两者各司其职。每条证据 README 都必须包含仓库统一的四段式(tested / observed / why it is enough / omitted),并记录 driver 的 changed-path / isolation 字段与沙箱 agent 目录路径;若 driver 未自删沙箱,调用方必须在写入清理回执前删除任务专属沙箱并确认子 PID 已终止。
一句话总结这套方法论的核心:用真实进程、隔离沙箱、可复现路径,把"bundle 可加载、适配器仍工作、真实 home 未被碰"变成一份 reviewer 无需重跑即可审计的 JSON 证据。
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考