背景
调研 acpx(modules/1acp)的 flows 能力后,确定了两条把 pr-triage 式「judge→validate→escalate」自主编排引入任务管理的路子:
- 借鉴路(已落地,分支
worktree-verifier-needs-human):给 verifier 加第三向出口 needs_human,原生 Go 实现,复用声明式事件引擎升级到 awaiting_human。见提交 feat(verifier): 核验第三向出口 needs_human。
- 轻量路(本 issue,以后再增强):把整个 acpx flow 当作一个
executor=function 的任务来跑,让调度器管「何时跑/在哪跑/失败怎么重排」,flow 管「单个重任务内部怎么自主判断/校验/升级」。
轻量路的价值场景是跨厂商多步自主(codex 分类 + claude 写码 + gemini 总结,一次 run 内),当前 claude 为主时不急,故先记录。
接线(调度侧其实已通)
scheduler.go 已有 case run.Executor == TaskExecutorFunction → s.FunctionRunner(见 dispatch 分支),所以只需三步:
- 注册 handler:
RegisterFunction("flow.pr_triage", handler),handler 内 spawn("acpx", ["flow","run",path,"--input-json",...]),解析 ~/.acpx/flows/runs/<runId>/ 结果
- 派发任务
Executor: TaskExecutorFunction + FunctionType: "flow.pr_triage"(api.go DispatchTask 会转成 fn: label)
- handler 返回 JSON →
function.go 的 writeTerminal 落 completed/failed
硬缺口(不是接一下就行)
阅读 taskapi/function.go 后发现三个契约缺口:
-
function 契约无法挂起到 awaiting_human
FunctionHandler 签名是 (result, err),RunFunction 只写 completed/failed 两终态。flow 的 checkpoint 节点要等人,但 function 任务没有非终态出口。要支持得改 FunctionContext/RunFunction,加一个 suspend→awaiting_human 的返回通道。
注:借鉴路已经把 verifier 的 awaiting_human 出口打通(ActionAwaitHuman),将来这里可复用同一状态语义。
-
FunctionContext 不带 workspacePath
RunFunction(task, workspacePath, ...) 收了 workspacePath 但没塞进 FunctionContext(只有 Task+CostTokens)。flow 需要 cwd,得先补这个字段。
-
flow 的多步 trace 对 DB 时间线不可见
UI 吃 t.Replies,flow 状态活在 ~/.acpx/flows/runs/。不桥接的话,任务详情里只看到一个黑盒 completed。token 成本(flow 内 acpx 派生 agent 花的 token)也不进 CostTokens,需要 handler 解析 flow run 的用量回填。
其他注意
- 工作区锁:一个 pr-triage flow 按其 timeout 能跑 90+ 分钟,整段独占该 workspace 的 runner slot。长 flow 的锁持有时间要评估。
- 运行时依赖:需要宿主装 node + acpx(
modules/1acp submodule 已在)。
- 双持久化底座:flow run 状态不在 meta.db,接进来等于在 Go 调度器之外再养一套 Node 编排状态,两边任务状态要对账 —— 这是本路子最大的架构成本,增强时需正面设计。
验收(增强时)
背景
调研 acpx(
modules/1acp)的 flows 能力后,确定了两条把 pr-triage 式「judge→validate→escalate」自主编排引入任务管理的路子:worktree-verifier-needs-human):给 verifier 加第三向出口needs_human,原生 Go 实现,复用声明式事件引擎升级到awaiting_human。见提交feat(verifier): 核验第三向出口 needs_human。executor=function的任务来跑,让调度器管「何时跑/在哪跑/失败怎么重排」,flow 管「单个重任务内部怎么自主判断/校验/升级」。轻量路的价值场景是跨厂商多步自主(codex 分类 + claude 写码 + gemini 总结,一次 run 内),当前 claude 为主时不急,故先记录。
接线(调度侧其实已通)
scheduler.go已有case run.Executor == TaskExecutorFunction → s.FunctionRunner(见 dispatch 分支),所以只需三步:RegisterFunction("flow.pr_triage", handler),handler 内spawn("acpx", ["flow","run",path,"--input-json",...]),解析~/.acpx/flows/runs/<runId>/结果Executor: TaskExecutorFunction+FunctionType: "flow.pr_triage"(api.goDispatchTask 会转成fn:label)function.go的writeTerminal落 completed/failed硬缺口(不是接一下就行)
阅读
taskapi/function.go后发现三个契约缺口:function 契约无法挂起到
awaiting_humanFunctionHandler签名是(result, err),RunFunction只写completed/failed两终态。flow 的checkpoint节点要等人,但 function 任务没有非终态出口。要支持得改FunctionContext/RunFunction,加一个 suspend→awaiting_human 的返回通道。FunctionContext不带 workspacePathRunFunction(task, workspacePath, ...)收了 workspacePath 但没塞进FunctionContext(只有Task+CostTokens)。flow 需要cwd,得先补这个字段。flow 的多步 trace 对 DB 时间线不可见
UI 吃
t.Replies,flow 状态活在~/.acpx/flows/runs/。不桥接的话,任务详情里只看到一个黑盒 completed。token 成本(flow 内 acpx 派生 agent 花的 token)也不进CostTokens,需要 handler 解析 flow run 的用量回填。其他注意
modules/1acpsubmodule 已在)。验收(增强时)
FunctionContext带 workspacePathawaiting_human,并能被 complete_human_task / 看板完成路径解出flow.*handler:spawnacpx flow run,解析 run 结果落 resultt.Replies(至少 checkpoint / 终态)CostTokens