Skip to content

ops agent → Pi Durable 整管线迁移(spec #223) - #231

Merged
steponeerror merged 33 commits into
masterfrom
feature/ops-agent-pidurable
Oct 28, 2026
Merged

steponeerror merged 33 commits into
masterfrom
feature/ops-agent-pidurable

Conversation

@steponeerror

@steponeerror steponeerror commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

问题与结果

ops 旧管线(ops/agent,自研 while 循环状态机)崩溃即丢轮、无幂等、台账/熔断自维护漂移。本 PR 按 spec 2026-10-28(七定案)整管线一次性迁移到 Pi Durable(checkpoint 任务框架):旧管线原地不动,v2 住 ops/agent-v2/ 以 shadow 模式对拍运行,切流另裁(用户裁决)。

T0-T6 全票完成(#224-#230);整分支双轴终审 + 两轮评审环(三路评审员×2 轮 + 修复批)通过,全部发现已收口;47/47 测试绿;golden 全集对拍补齐立票 #232 作切流硬门槛。

触发/前后

  • 前:旧 pipeline.mjs 轮内崩溃=轮废(SIGKILL 后不可续);requestId 无幂等;预算/台账双面各写一份。
  • 后:v2 轮=checkpoint 任务(SIGKILL 后 resume 续跑到 CLOSE,requestId exactly-once);预算四界 budget-guard 单源双点执法(重试回合同计,attempt 即调用);轮目录认领守卫(跨库撞轮 bad_round);台账/queue 兼容行同点同形流出(月度准入与前端零改动);shadow 对拍 harness 实测 15/15 工件+台账行位等价(归一化窄面:时间/时长/tmp/-rN/usage 计数,cost 逐字比)。

结构

  • ops/agent-v2/:骨架(T1)→ budget-guard(T2)→ 发现轮(T3)→ 健康轮+微探针(T4)→ cron 入口冻结+shadow 对拍+RUNBOOK(T5)→ ADR 0012+GLOSSARY(T6)→ 终审收口+评审环修复批(9 commit:信封码链/exitCode 判例/重试计数/认领守卫/stage usage 求和律/--model 校验)
  • run-health.mjs / run-discover.mjs:cron 短命进程(open→submit→信封→exit;exit 0/1/2/3 冻结,stdout 末行=旧形信封,ENVELOPE.md)
  • shadow-diff.mjs:双世界罐装对拍闸门(含双面皆败不假 SAME、闭集外残余哨);SHADOW-RUNBOOK.md:观察面/SAME 门槛/影子世界置备/回退/切流四步
  • 旧管线零改动(红线,父会话+评审双验);pi-durable@1.4.0 三包 exact 锁定+薄适配层

验证

  • cd ops/agent-v2 && node --test 'tests/*.test.mjs' = 47/47(含 SIGKILL resume、预算四界负例与重试计数负向对照、golden discover 对拍、shadow 健康位等价、cron CLI 冻结面、跨库撞轮认领)
  • node shadow-diff.mjs <id> = 15/15 SAME exit 0;双面皆败负例 SIDE-FAILED exit 1;残余哨正例单测
  • 凭证面 smoke(zai-coding-cn/bigmodel)与真实 SIGKILL 演练:T0 报告

已知边界

  • golden 全集逐条对拍 = 切流硬门槛,票 golden 全集新旧对拍补齐(spec 验收缺口;切流硬门槛) #232(shadow 期内补齐)
  • v2 usage 计量边界差(in 含 cache_read、total 再计缓存)→ 对拍归一,cost 不受影响(ENVELOPE.md)
  • T0 遗留 6 项(SQLite NORMAL 断电窗口、月轮↔conversation 粒度等)归切流前清单
  • ADR/GLOSSARY 在嵌套 docs 仓的文件改动随本 PR(worktree 内),嵌套仓提交时机另裁

Closes #223, closes #224, closes #225, closes #226, closes #227, closes #228, closes #229, closes #230

huxiao and others added 25 commits October 28, 2026 14:17
…LM smoke(票 #225)

- ops/agent-v2/:entry.mjs(Harness.open SQLite→resume→submit→信封→退出;cron 式,定案5)
- models.mjs:零复制 re-export 旧管线 loadModels(定案7 同源;ops/agent 零改动)
- registry.mjs+tasks/:空工具注册骨架(ops 红线:不装内建 CodingTools)
- compaction 默认关(t1-notes 风险5);journal=WAL(chord 缺省)
- tests/ 7 例:registry 红线/WAL/pi-ai 单实例@1.4.0/GLM smoke 落库+requestId 幂等/kill -9 resume
- spec: docs/superpowers/specs/2026-10-28-ops-agent-pidurable-migration-design.md 定案 1/5/6/7
- budget.mjs:零复制 re-export 旧管线 ops/agent/pipeline/budget.mjs(定案 3 原样;该文件
  只 import node 内建零内部耦合,与 models.mjs 同模式单一真源;执法语义一字不改)
- tasks/budget-guard.mjs:双点执法接线面(entry/tasks 层,不 hook/memo 化)——
  步前准入 admitRound(月帽 ¥30 ∧ est,拒跑零花费先于 harness)+ 环内检查
  preModelCall(①③ attempt 前)/postModelCall(②④ 结算后)/stepBoundary(③ 相位边界),
  决策对象带剩余量;文案逐字对齐旧 pipeline.mjs 检查点(per-type 旋钮名)
- entry.mjs runRound:submit 前准入+步前查,结算后②④查(轮增量=harness.usage 差分,
  ≡旧 subscribe 回合累计);拒跑/违界信封(status=refused/failed+code),旋钮非法 exit 2
- tests/budget-parity.test.mjs 10 例(零网络):零复制同引用/旋钮读面/四界决策+文案+剩余量
  /GLM usage 固定样本/月度准入同裁决同留痕/同参哈希 dupKey+dupRecord/entry 接线信封面
- 语义对照 ops/agent/DESIGN.md §6 预算四界;旧管线 ops/agent/ 零改动
…套(前任超时遗产,parse+18 存量测试绿;模型轮/健康轮任务面待续作)
…P/SYNTH,requestId 幂等)+ops-health-round(十态相位机+WAIT_MODEL join+FAILED 书挡);steps/gates/budget-guard 零复制;微探针三只入 ops 扩展(replay 全 unsafe);G_CLOSE 回声哨/CLOSE 09-budget.json 快照零复制接线;测试 5 例(含 faux E2E)全绿 23/23
…#227 T3,续作)

- discover-round defineTask:SCAN/SHORTLIST/MEASURE/ENQUEUE/RETRO/CLOSE 逐相位映射,
  零复制驱动旧步(pipeline/steps+gates);相位级 startedAt 入 checkpoint(墙钟续算);
  budget-guard 双点执法逐 attempt;requestId=round-<id>-shortlist[-aN] 幂等
- 粒度决策(t1-notes 建议4):一轮=自有 conversation(ownerless;启动器带 agent 面
  创建:模型+shortlist 提示词+发现工具扩展;任务内无 configure 面,会话必在任务外造)
- run-discover.mjs 轮启动器:准入→轮会话→任务绑定(与轮索引 session doc 同 commit
  原子登记)→waitForTask→stdout 末行信封;faux/real 双面;--crash-after-phase 测试面
- 五通道工具 defineTool 化:web_read/web_search/rss_parse 三只只读闭集(gh_search/
  fetch_url 留 legacy 面,spec T3);replay 逐工具裁(web_read/rss_parse=safe,
  web_search=unsafe);breaker 五通道写侧零复制随迁(mkDiscoveryHost 适配器内
  breaker.record;ops/agent-v2/breaker.mjs 纯 re-export,旧文件单一真源)
- registry:ops-discover-tools 扩展(root 面钉 extensions=[] 防缺省全装渗漏)
- 前任半成品修复:createConversationViaTx API 误用(commit 返回值/tx 无 agent 面)、
  相位不推进死循环、ConversationHandle.submit 缺 context、usage 形状错位、
  REPO_ROOT 少算一层(T2 F1 同类病)、structuredContent→structuredOutput、
  appendGate 返回值误用、stage.usage 回声、dup 登记表/准入读数跨相位丢失
- 测试:全链 faux 轮+同库重放零双写/跨库守卫+breaker 计数码落盘+SIGKILL 崩溃恢复
…T3)

旧 runPipeline(in-proc)vs v2 run-discover(子进程)逐工件对拍:归一化(时间戳/
dur/路径/usage 口径)后 d1-d4+gates+transcript+09-budget+queue 数据行全 SAME;
已知差异面留清单打印:state.json 镜像(checkpoint 真源)/ledger usage 列/
envelope.resumed_from(v2 恒 null,T5 冻结面)/envelope.usage(UsageDoc 活和含
cacheWrite vs 旧事件流累计)
…waiting+on+allSettled 与 spawn 同 commit)/model-round transcript 工件面接回(行型位等价旧 faux.mjs:87-94/135-139,留痕先于 io.consume);faux-face.mjs=旧私有面逐字副本(fixture/seed 行/transcript 名);rounds ③ 测试补 fixturesDir+transcript 行型断言,23/23 绿
…alls/usage/startedAtIso)入子任务 input 做基数(pre①③/post②④ 以轮为基);WAIT_MODEL 收口补①②④复查(违界即停不降级);failed 子任务花费留痕=会话 transcript 实计(宁多算不漏记,childConvs 入 checkpoint);monthly_admission 读数链 submitter→input→initial→ck.state(09-budget monthly 维度对齐旧轮);测试 +1(initial/rebuild 透传),24 绿
- health-round spawn:input.modelRef 显式注入面优先;缺省 modelFace=faux(零网络
  安全缺省,生产 cron 须显式 real;崩溃重启 faux provider 不可复现=限已知并注明)
  →逐子任务 mkFauxFace+setProvider+setResponses(罐装回合);真面=stepModelRef+
  OPS_PROBE_TOOLS。暗病两处随执行面暴露一并修:modelTools 传 ToolRegistration
  对象(名字串会被 applyChange names() 映射 .name 出 undefined 炸 strict JSON)、
  modelRef 取 faux.getModel()(faux.provider 是 provider 对象非 id 字串)。
- fromPhase 缝:startPhaseOf(旧 pipeline.mjs replay --from 同义;G_PROBE 需 ≥30
  URL 真探测,零网络测试从 TRIAGE 起步;生产不传)。
- spawn 收口对齐框架执行面:tx.createTask 是 async(types.d.ts:858)须 await;
  TaskOutcome 面值即 {status,result|error}(无 .state 包裹);failed 留痕读
  o.error。
- tests/health-e2e.test.mjs:链路 E2E TRIAGE→WAIT_MODEL(join)→DISPATCH→DEEP
  (evidence 宿主回填+registry_seed 行)→ITEMIZE→SYNTH(meta.injected)→
  ANCHOR_SWEEP(降级旁路)→RETRO→CLOSE;钉 MF2 join 契约/MF3 transcript 断链/
  MF1 import 面;P1-5 readings.monthly+P1-4 model_calls=4 轮级口径。负例:
  OPS_PIPELINE_MAX_MODEL_CALLS=2→收口终态 budget_exceeded(并行子任务穿透基数)。
- tests/.e2e-env.mjs 首位 import:EVAL_PY=shared.mjs 加载期常量,晚设无效——
  首跑曾跑真 backend python(非 hermetic)。stub 永败→anchorSweepCli infra 信封
  →ANCHOR_SWEEP 降级旁路(旧 selftest ⑫ 同契约)。

旧管线零改动;26/26 绿(24+2 新增)。
# Conflicts:
#	ops/agent-v2/breaker.mjs
#	ops/agent-v2/registry.mjs
#	ops/agent-v2/tasks/index.mjs
#	ops/agent-v2/tests/scaffold.test.mjs
…toPhase 停笔)+ discovery 工具 stderr 留痕行;discover 测试断言迁到冻结信封面
… v2 checkpoint 任务,15 工件+台账行位等价,归一化窄面收口宽缝)
… 非假 SAME、CLI exit 3、诊断信封);--model 接线+usage 补 --to-phase;toPhase 与 stageDone 同 commit 原子停笔;双 launcher 轮 id 形状律(先于准入);RUNBOOK/run-discover 文案纠偏;回归测试 +3(40/40 绿)
…state.outcome 类型面 TypeError(health-round);门败 gateRec 重试语义恢复(model-round,旧 pipeline.mjs:236 同律);CLI roundId 位置首参解析(3 处);base 遮蔽/mkAbort 死参/run-discover 缩进;ENVELOPE 补 queue.md 兼容声明;RUNBOOK 补发现轮 shadow 排程+golden 全集切流硬门槛(#232)。40/40 绿
- P0-② process.exit 截流 stdout:run-health/run-discover/entry/shadow-diff 四处
  CLI 尾部 stdout.write+process.exit(N) 全改 process.exitCode=N 赋码后自然返回
  (旧律 ops/agent/pipeline.mjs:547 判例:exitCode 而非 exit——管道写异步落管,
  process.exit 可截断单行信封);用法错分支同改(exit 2 纯 stderr)。
- P0-① run-discover 失败信封错误码塌缩:任务域失败三元组真码在
  error.detail.code(finishFailed 的 scriptPhase 包装),CLI 读序改
  r.error?.detail?.code ?? r.error?.code ?? "infra"(原恒 infra)。
- P0-⑥ run-discover --model 校验:run-health 同律(无斜杠/空段 → {usage:true}
  throw → exit 2);real 面漏 --model 的裸 Error 同改 {usage:true}(原落 exit 3)。
- P2-⑫(随批)run-health parked 信封 message 带真错:
  settled.state.outcome?.error ?? settled.state.summary ?? {}。

测试面:CLI 级断言(bad_round/exit 2/exit 3)随 P0-④⑩ 测试批补(下一提交)。
- model-round.mjs 重试 commit(attempt+1)同步 checkpoint.modelCalls+1:
  失败 attempt 的调用不再丢(成功面 ck.modelCalls+1 自动含全部重试回合;
  P1-4 WAIT_MODEL 收口/09-budget 轮累计口径随之不再少算)。usage 不补:
  结算面 usageSince(view,0) 按会话全量,失败回合已含。
- discover-round.mjs SHORTLIST 两处重试 advance(unsettled/empty_text)同型同修。
- P2-⑬ usageSince(view, base)→usageSince(view, 0):一次性会话恒全量;原传
  base 对象靠 slice(NaN) 兑回 0 的暗病消除(显式游标+注释)。
- P2-⑦ discover buildCtx.log 接 stdout 改 stderr(stdout 只留信封末行)。
- P2-⑧ discoverRoundTask 补 abort handler(terminal aborted;model-round/
  health-round 同款)。

重试测试面:faux 首败(stopReason=error / 空 content)后成可构造,
health-e2e/discover-round 测试批补(modelCalls/attempts 断言)。
- P0-④ claimRoundDir:轮目录落认领件 .v2-claim.json{taskId},全部轮目录工作
  相位(含 WAIT_MODEL/CLOSE)套 claimGuard;异主认领 → bad_round 终态(旧
  discover bootstrapRoundDir 同义)。同库复用走 run-health HealthRoundsDoc
  不触发;--from-phase TRIAGE 前置工件(01-/02-)合法故不能以目录非空判。
- P0-⑤ waitModel stages[].usage 由轮累计改本步增量(终值−ck.usage 基线,
  基线 null 按字段 0):恢复旧「各步 usage 求和==信封 usage」律(golden
  pipeline_ok 断言面);checkpoint.usage(轮累计真源)不变。
- 认领件已同步入 shadow-diff NEW 侧白名单(前提交 P2-⑪)与 RUNBOOK 差异表
  (随文档批)。

验证:node --test 'tests/*.test.mjs' 40/40 绿(幂等复用/对拍/全链均不受认领件
影响);跨库撞轮 CLI 级测试随测试批补。
- P0-① 实测面补修:bootstrapRoundDir 原裸抛逃逸 scriptPhase handler → 框架
  fault+重启循环,真码(bad_round)永不落 outcome、CLI 信封恒 infra——收进
  runStage 同款 finishFailed(detail.code)终态(原注释「域错→终态」的语义
  补齐)。CLI 级测试:跨库撞轮 → exit 1 + error.code=bad_round(非 infra);
  in-proc 测试加 detail.code 断言。
- P0-⑥ --model 校验 CLI 测:缺斜杠/空段/real 面漏给 → 全 exit 2 且不发信封。
- P0-③ SHORTLIST empty_text 重试测(faux 首回合空 content 次回合好):attempts=2
  + 09-budget readings.model_calls=2(失败 attempt 同计,旧律 budget.mjs:34;
  重试测在本批已构造,不留 #232)。
- P2-⑨ 恒真 filter(s !== 'RETRO' || true)删,改 records.length===6 干净断言。
- runRound 辅函数加 fixturesDir 覆盖参(重试测专用)。

node --test tests/discover-round.test.mjs 8/8 绿。
- P0-④ 实测修正:认领键由裸 taskId 改 checkpoint 携带的随机 claimToken——
  taskId 是 per-db 计数器,两库各自新建任务数值相撞(db1/task1 vs db2/task1),
  裸 id 判同主失效(跨库测试实证);token 随 checkpoint 持久,resume 恒自识、
  异任务恒异主。CLI 级测:同轮 id 换新 db → exit 1 bad_round + 台账不双写
  (修复前实际穿透到 SYNTH 才炸 model_face)。
- P0-③ 健康轮重试测(faux TRIAGE 首回合 stopReason=error):轮累计
  readings.model_calls=5(2+2+1)钉「失败 attempt 同计」;负向对照已验
  (回退修复 → 4 → 测红)。
- P0-⑤ 求和律:E2E 与重试轮各补「各步 usage 增量之和==信封 usage」断言。
- P2-⑩ cron 测 1 补健康成功信封键闭集 deepEqual;新增 exit 3 测(--db 父
  路径是文件 → ENOTDIR → launcher 异常面,stdout 零信封)。

node --test tests/cron-launchers.test.mjs tests/health-e2e.test.mjs 12/12 绿。
ENVELOPE.md:
- usage 机制句按实况重写:发现轮=UsageDoc 快照求和(roundUsage)、健康轮=
  逐子任务 result 求和(WAIT_MODEL 收口)、entry=harness.usage() 差分;
  共同点=轮累计事件流口径(原文把 entry 面机制错写成全域机制)。
- 「composeEnvelope 单源」改「形状由对拍测试钉(round-ctx 逐字副本+discover
  第二副本,golden/shadow 双闸)」——单源在两副本下不成立。
- 失败信封表补注:旧失败信封含 pipeline 字段,v2 无——已声明差异面。

SHADOW-RUNBOOK.md:
- 09-budget 行纠错:旧 CLOSE 也落件;真实差异=对拍载具双侧停笔 RETRO 故均不落。
- 「38 测」→46(含 40/40→46/46,SAME 门槛同步)。
- 补「影子世界置备」小节:shadow 期 DOCS_ROOT 指影子根;rsync 生产 docs/ops
  清 rounds//ledger.md/queue.md,保留 precedents/rules/items——防误指生产写脏。
- 已知差异表补 .v2-claim.json 行(P0-④ 认领件,白名单已列)。
- 轮次:补发现轮例外——一轮=一 ownerless 会话(tasks/discover-round.mjs:16-24
  决策;旧发现轮每模型步全新 Agent 上下文,跨轮零渗漏旧律保持)。
- 台账:「ledger.md 停写」改「降级为兼容输出面,不再作真源(RETRO 兼容行照写,
  ops/agent-v2/ENVELOPE.md)」——与 T5 台账兼容面实况一致(停写与兼容行矛盾)。

配套:docs/adr/0012(嵌套 docs 仓)同句同改——只改文件不 commit(嵌套仓
提交归用户/docs 仓节奏,工作区留改动待用户处置)。
@steponeerror
steponeerror marked this pull request as ready for review October 28, 2026 13:23
@steponeerror
steponeerror merged commit 14c4c83 into master Oct 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment