Files
qinglong/docs/adr/ADR-0355-human-approval-decision-product-gate.md
T

65 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0355:强人类认证、摘要绑定的 Approval 决策产品门
- 状态:Accepted
- 日期:2026-08-10
- 关联 RFCQL-RFC-0001 D-05、D-06、D-08、D-13、D-17、D-28、D-75、D-85、D-87、D-157、D-257、D-259、D-260、D-263、D-265、D-266、D-267
- 关联 ADRADR-0031、ADR-0138、ADR-0353、ADR-0354
## 背景
MCP 已能列出 Approval 并读取一个经过 redaction contract 的 preview,但仍故意没有写 authority。若把 approve/reject
直接加入同一 MCP 进程,Agent、API credential、Artifact read 和人类决定会混在一个 authority 中;若本机 Owner CLI
只接受 request ID,则人类无法证明决定针对自己实际检查过的 action/preview。Edge 路由器还不能为一次人工决定常驻新的
Web 服务,而 Cluster 也不能为每个决定建立新 Pool 或绕过既有 mTLS/OIDC 管理边界。
## 决策
1. Profile-neutral `ApprovalDecisionService` 只接受强认证 User,允许的 assurance 固定为 `multi_factor``hardware`
`local_console`,并重新执行当前 Project 的 `approval.decide` Policy。
2. 决策命令除 request/project/version/decision/reason 外,必须携带完整 `expectedAction`permission、action type、action
reference、action digest 和 preview digest。服务从 durable Approval 重新读取 canonical binding,任何一项漂移均在写前拒绝。
3. `expectedVersion` 固定为 1success 把 allowed Security Audit 与 Approval version 1→2 放在同一 storage transaction。
同一 decision ID、决定、reason 和 User 的重试返回 `existing`,不重复写 audit;不同语义复用 ID 失败关闭。
4. 本机产品面是独立 `ql3-approval` 私有 command-file CLI,提供 `approval.inspect``approval.decide`。inspect 同时要求
`approval.read``artifact.read`,返回 canonical `expectedAction` 和有界 redacted previewdecide 必须把该 binding 原样带回。
5. 本机认证继续复用 POSIX private-path proof、Owner Pepper、active User credential 和 `local_console` principal。写前复验
command proof/credential fileSQLite Approval repository 又在 `BEGIN IMMEDIATE` 后、读取或写入任何 Approval 前复验同一
credential fence,从而关闭 revoke 与写事务之间的竞态。认证、Policy、binding、state/fence 失败使用独立 failure event 审计。
6. MCP 保持纯只读,不 import decision service、不新增 Tool,也不取得 `approval.decide`。consume、dispatch 和 execute 仍由
各自的受信执行面完成,人工决定不能直接产生外部副作用。
7. Cluster 复用同一 Profile-neutral service,由 `@qinglong/cluster-admin/approval-decision-management` 在调用方拥有的
PostgreSQL Pool 上组合现有 Approval repository 与 Project Policy repository。它不建立 listener、Pool、timer 或认证协议;
未来远程 route 必须接在既有强认证管理 transport 后,并在 transport 层重新确认认证。
## 资源与 Package 裁决
- Edge/Standalone CLI 是短进程、单 SQLite connection、无后台 timer/listener/cache;未调用时资源为零。
- Cluster factory 复用调用方 Pool,不创建 per-request Pool,也不改变 HA schema 或连接拓扑。
- 公共协议归 Runtime Core 的 `approved-action/` domainSQLite 短期 authority 归现有 adapter;本机命令归 Local Owner CLI
Cluster 组合归 Cluster Admin。没有独立制品、依赖或生命周期理由,因此不新增 workspace package。
## 被否决方案
1.`qinglong.approval.get` 增加 approve/reject:混合 Agent read authority 与 Human write authority。
2. 决策只带 request ID/version:不能证明人类决定与已检查 preview/action binding 相同。
3. 交互式 CLI 从 stdin 询问 yes/no:难以审计、重放和自动生成受保护的变更单,且容易被终端注入或默认值误导。
4. 为 Approval CLI/decision service 新建 package:会制造用户已质疑的微包,没有新的部署或 authority 边界。
5. 为本切片新建表或队列:现有 Approval/audit/preview schema 已满足 exact binding 与原子决定。
6. 同时开放通用 consume/execute:会把人类审查与外部副作用重新耦合,越过独立 start/recovery 门禁。
## 发布边界
本 ADR 完成本机 Owner 的 inspect/decide 产品门和 Cluster 的可复用组合边界,但不声称已开放 Cluster 远程管理 API/UI。
Cluster route 仍须完成 transport principal、独立限流、失败审计、部署配置和端到端 PostgreSQL 测试后才能注册。MCP 永久保持
只读;任何通用消费或执行入口必须另立 ADR。
## 验证证据
- Runtime Core 455/455、Local SQLite 209/209、Local Owner CLI 137/137、Cluster Admin 258 pass/2 条件 skip、
PostgreSQL 290 pass/1 条件 skip。
- package boundary、cluster dependency、edge import 与十二档 artifact 门全部 compatible17 个 package、1004 个源码文件中
978 个位于领域子目录,root 仅 26 个,没有单文件或浅目录微包。
- 默认 Edge/Standalone Application loaded module 仍为 120MCP 为 203;新增决策语义只有显式 Owner/Cluster composition
import 时才加载。Standalone Application AI 为 6,275,925 bytes/639 files,距 6 MiB 上限余 15,531 bytesStandalone
MCP 为 9,872,053 bytes/949 files/203 modulesRSS 38,404,096 bytes,均未放宽预算。