feat(ack): add structured worker model routing

This commit is contained in:
2026-07-31 23:34:13 +08:00
parent ee66dbe9ce
commit ae3bce7b5d
23 changed files with 6491 additions and 327 deletions
+119 -4
View File
@@ -1,7 +1,7 @@
---
title: ACK 设计评审记录
date: "2026-07-31T17:15:37+08:00"
updated: "2026-07-31T20:45:09+08:00"
updated: "2026-07-31T23:31:48+08:00"
---
# ACK 设计评审记录
@@ -11,7 +11,8 @@ updated: "2026-07-31T20:45:09+08:00"
本文同时记录后续方案和实施结果。带“建议”“目标”或“待验证”的内容默认是设计
方向,不代表当前版本已经具备;实际运行契约仍以 `skills/ack/SKILL.md`、schema、
校验器和测试为准。知识护栏方案的 v0.9.0 落地边界见下文状态表。
校验器和测试为准。知识护栏方案的 v0.9.0 落地边界、model-routing v0.10.0 的
实施结论见下文状态表。
## 背景
@@ -173,6 +174,10 @@ transfer:
## P0:Worker 启动策略改为安全默认
> v0.10 已以结构化 profile 和可信 launcher 取代本节描述的自由 command。下方是
> 当时的评审记录,不再代表当前实现;其中“full-access 写入任务板授权”的建议已被
> v0.10 决策取代,当前明确为 Deferred。
当前校验器强制 Codex 使用 bypass、Cursor 使用 YOLO,并写死模型和 CLI。项目文档
允许收紧权限或替换模型,但合法的安全覆盖会被校验器拒绝。
@@ -204,8 +209,10 @@ workers:
permissionMode: sandbox
```
可信代码根据这些字段构造 argv,不接受自由 shell 拼接。安全模式默认通过
full-access 需要用户单独授权,并把授权范围和时间写入任务板。
可信代码根据这些字段构造 argv,不接受自由 shell 拼接。安全模式默认通过
原建议设想 full-access 用户单独授权并写入任务板;v0.10 没有实现这条授权通道,
项目文件也不能充当授权证据。full-access 的可信授权、期限、撤销和外层隔离均为
Deferred,当前 launcher 必须 fail closed。
## P0:初始化需要原子化
@@ -627,6 +634,114 @@ issue、日志和外部网页只能作为不可信 evidence。进入知识库前
触发条件限定,自动匹配会漏掉关键项或产生大量噪声。这类知识应继续由人维护在 ADR
或项目文档中,不能强行进入自动派发流程。
## v0.10.0 实施结论:结构化 model-routing
### 当前决策
旧的 `validate_worker_command.py --command '<shell>'` 只能检查少量 token,无法证明
整段 shell 没有追加命令、重复参数、环境注入或 cwd 漂移。v0.10 删除这条自由命令
配置面:旧脚本只保留 fail-closed 迁移提示;自动 worker 的唯一入口是
`launch_worker.py profile-hash|plan|launch`
机器 SSOT 固定为 `tasks.yaml.project.orchestration`。profile 只声明 role、CLI、
tier、model、reasoning effort 和 permission modemodel 必须命中按
CLI/role/tier 分组的 allowlist。自动权限只允许 `read-only`
`workspace-write`full-access、Codex bypass、Cursor YOLO/force 和关闭 sandbox
全部 fail closed。
### 已落地约束
| 控制面 | v0.10.0 实现 |
|--------|--------------|
| 自由 command / extra argv / env / cwd | schema 与语义校验拒绝;旧 validator fail closed |
| Agent argv | 由 `worker_profiles.py` 按 profile 生成唯一 argv;子进程 `shell=False` |
| plan → launch 漂移 | `launch` 强制接收刚审阅的 `--expected-launch-fingerprint`slot 也纳入 hash |
| PATH / loader / 凭据串用 | 不按调用者 PATH 找可执行文件;固定可信目录;控制进程无供应商凭据,worker 使用 `per-cli-allowlist-v1` |
| worktree 冒充 | 绝对规范路径、无 symlink、同 Git common-dir,并命中 `git worktree list -z` |
| Orca command 注入 | `--command` 只含固定 bootstrap 和随机 launch ID |
| bootstrap 抢跑 | 先绑定 runtime/handle/incarnation/worktree,再用 terminal stdin nonce/proof 授权 |
| cleanup / bootstrap 竞态 | Popen 前在同一 record lock 内复核 awaiting 状态;cleanup 先发生则拒绝启动 Agent |
| create 部分失败 | 取得确定 handle 前的超时、transport/解码异常、中断、非零、畸形/缺字段响应均记为 indeterminate,禁止自动重试 |
| handle 后失败清理 | record 写失败不阻断 close;只有 `terminal close --tab --json` 完整匹配且最终 failed 状态可靠落盘才记普通失败,否则保持 indeterminate/reconcile-required |
| receipt 漂移 | 规范化 profileHash、argvHash、slot、launchFingerprint 与 receiptHash 交叉校验 |
| dispatch 旧 receipt 改挂 | 每个角色显式记录 attemptIdreceipt 必须绑定当前 ACK task.id、同 role/profile/attemptreceiptId/attemptId 同空同填 |
| 配置兼容 | ackVersion 必须是合法 SemVerv0.10+ 的 orchestration/workerReceipts 必须同时存在,v0.9 旧板仍可只读校验和手动协作 |
### 不能被弱证明掩盖的边界
`receiptHash` 是无密钥 checksum,不是签名。项目内可写方能修改 receipt 后重算
hash;而当前 Orca terminal metadata 不提供原始 Agent argv、模型或权限
attestation。因此 v0.10 **不根据持久化 receipt 自动复用旧终端**。每次自动派发都
重新执行 plan,并用 expected fingerprint 启动 fresh worker;持久化 receipt 只作
审计和 dispatch 关联。
`launchFingerprint` 只证明完整计划没有漂移,不是一次性授权或幂等键;同一计划重复
执行仍会创建新的 fresh terminal。成功后不得重放,结果不确定时必须先 reconcile。
自动防重放要依赖后端原子 idempotency/claim 或项目外可信 launch intent,不能由
checksum 伪装提供。
任务板里的 dispatch 关联也不是宽松的历史索引。`dispatch.<role>` 显式记录
`profileId``receiptId``attemptId`,校验器要求 receipt 的 `createdFor` 精确绑定
当前 ACK `tasks[].id`、同一角色和同一 attempt,并要求 profile 一致。该约束阻止把
旧任务、旧角色或旧轮次 receipt 改挂到当前 dispatch,但不会把无密钥 receipt 升级成
可信 attestation。
同样,receipt 证明的是 launcher 请求和本地 CLI/runtime 绑定,不证明模型供应商最终
执行的模型。开放自动复用需要 Orca/ACP 提供启动参数 attestation,或项目外可信签发
与校验通道。开放 full-access 还需要不可由项目文本伪造的用户授权、期限、撤销和外层
隔离。这两项均留待后续版本。
威胁模型也明确到 Coordinator 账户边界:v0.10 防止任务板、任务内容、普通环境和
受限 worker 把数据变成第二个启动命令入口;不抵御已经完全控制 Coordinator OS
账户、Orca runtime、受信 Agent/Orca 可执行文件或用户级 Agent 配置/插件/MCP 的
攻击者。`permissionMode` 也不是模型供应商或外部工具能力 attestation;后者必须依赖
独立 OS 身份、受控 Agent 配置或平台能力。
## 后续版本提案:ACP 与可替换编排后端
### 当前决策
ACK 当前版本继续以 **Orca** 作为唯一自动编排后端。闭环语义保持工具无关,但
worker 创建、终端身份绑定、dispatch 和 wait 的可执行适配仍由 Orca 完成。
ACPAgent Client Protocol)作为后续版本的优先候选,当前只记录方案,不加入运行
时依赖、不新增半成品 adapter,也不让现有流程在 Orca 与 ACP 之间自动猜测。
| 能力 | 当前版本 | 后续方向 |
|------|----------|----------|
| 自动创建并监督 worker | Orca | ACP capability negotiation 后可增加 ACP adapter |
| 结构化模型与权限 profile | ACK 自己定义,Orca 只承载启动 | 保持为 ACK 稳定契约,不交给后端自由解释 |
| dispatch / wait / terminal identity | Orca runtime handle | 映射到 ACP session / request / event identity |
| 无自动后端时运行闭环 | 手动模式 | 可增加 PTY / Zellij 类低层 fallback,但不冒充语义协议 |
| 后端选择 | 项目显式配置 | 未来仍需显式配置,不按已安装命令静默切换 |
### 后端无关的最小语义
未来 adapter 只应实现以下能力,不应接管 ACK 的角色、状态机或安全策略:
1. `createWorker(profile, worktree)`:按 ACK 已验证的结构化 profile 创建 worker。
2. `inspectWorker(identity)`:返回可绑定的 runtime、session/incarnation 和活性。
3. `dispatch(identity, taskContext)`:把一个明确任务投递给已验证 worker。
4. `wait(identity, eventTypes)`:等待完成、复测、升级或 decision gate 事件。
5. `closeWorker(identity)`:只关闭本次创建且身份仍匹配的 worker。
模型、reasoning effort、权限模式、worktree 和 receipt hash 仍由 ACK 校验。adapter
不能重新开放自由 shell、任意 argv、任意环境变量或“后端默认模型”作为旁路。
### ACP 采用门槛
满足以下条件后再实现 ACP adapter
- 目标 Agent CLI 对 ACP 的启动、会话身份、取消和事件语义足够稳定。
- 能把 Developer/Test 的独立身份、worktree 和结构化 profile 映射到可核对字段。
- 能区分“已请求的模型/权限”和“运行时可观测事实”,不把客户端请求冒充供应商证明。
- 超时、重连、重复 dispatch 和 runtime 重启具有明确的幂等或恢复语义。
- 与 Orca adapter 使用同一组 ACK conformance tests,且不会降低安全默认。
在这些门槛满足前,ACP 保持 **Deferred**。PTY 或 Zellij 一类方案只能作为较低层的
进程/终端承载,不提供 session 语义、模型证明或任务状态机;如果以后加入,也必须
经过独立 adapter,不能散落为文档中的自由命令。
### 参考依据
- [GitHub 仓库自定义指令](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions)