aps-agent/docs/round-67-approval-migration...

75 lines
4.7 KiB
Markdown
Raw Permalink 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.

# Round 67 工作计划:审批迁移 Fail-Closed 与 Grant Digest 归一化
日期:2026-08-03
## 1. 方向选择
Round 66 已完成并通过独立复审。并行候选审计发现:
- 审批文件迁移把原始 grant token 直接写入数据库 token_hash,迁移后 P3 grant 无法被数据库后端消费;
- 损坏 JSON/非法结构会被当成空审批源并报告迁移成功;
- 数据库 TTL 仍依赖应用主机时钟,留到后续 R67-B2;
- SAP outbound P3 evidence envelope、folder.schedule TOCTOU 与 Automation G4 payload 绑定列入后续 P0。
本轮选择 R67-B1,因为两个 CRITICAL 缺陷可在本地 SQLite 完整复现和闭环,且写边界最小。
## 2. GitNexus 门禁
- `load_file_store`:LOW,1 个直接调用、6 个总影响,无执行流。
- `migrate_file_to_db`:MEDIUM,5 个直接测试调用,无执行流。
- 不修改审批 API、Harness、数据库 consume/TTL 或真实 MySQL 部署路径。
## 3. 目标
1. 迁移 grant 时只存 `token_digest(raw_token)`,不持久化原始 token。
2. 文件不存在维持空源语义;文件存在但 JSON/schema/记录损坏必须明确失败。
3. 所有源记录先完成规范化与交叉引用预检;grant 缺 pending request 时依据 grant/history 重建终态 APPROVED request,满足数据库外键。
4. 全部请求、grant 修复和事件在一个 SQLAlchemy Session 事务中 flush/commit;后段异常整体回滚。
5. 幂等比较拆分为不可变身份字段与单调状态:允许数据库端合法前进到 APPROVED/CONSUMED/EXPIRED,禁止状态回退或覆盖。
6. 同时预检 expected digest、历史 raw-token 主键与 confirm_id;一致 legacy 行在事务内原子修复为 digest,冲突则 fail-closed,禁止对 DB 中已知 digest 再哈希。
7. history 使用完整 canonical event digest 去重;同秒同状态但 approver/note/payload 不同的事件必须保留。
8. 迁移要求 `APS_APPROVAL_MIGRATION_QUIESCED=1`,并持有与文件后端相同的 `.lock` 到事务提交;读取、flush 后复核 source SHA-256 一致,再 commit。
9. 迁移后 active P3 grant 可成功消费一次,重放失败。
## 4. 写边界
- 业务实现:`server/agent_core/approval_migrate.py`
- 黄金测试:`tests/golden/test_approval_migration.py`
- 文档:本计划、并行计划、状态/运行证据、plan/matrix/changelog
## 5. Out of scope
- 数据库权威时钟、TTL/CAS 跨主机语义(R67-B2)。
- 真实 MySQL 多主机、网络断连/死锁恢复。
- SAP outbound、folder.schedule、Automation G4 evidence envelope。
- 提交、推送、合并、发布或修改现场审批文件。
## 6. 验收
- 文件后端 active P3 grant -> 迁移 -> 数据库后端 consume 一次成功、第二次失败。
- DB 仅包含 64 位 SHA-256 digest,不包含 raw token。
- 损坏 JSON、错误顶层 schema、非法 pending/grant/history 均失败且三张审批表不变。
- 同键不同不可变身份字段报冲突;数据库端合法状态前进不误报冲突,状态回退不覆盖。
- legacy raw-token 行一致时原子修复,不一致时 fail-closed。
- history canonical digest 保留合法近似事件并保证重跑幂等。
- 后段事件构造/flush 故障注入后,三表及迁移前目标数据整体不变。
- 未设置 quiesced 明确拒绝;锁持有期间 source digest 漂移则回滚。
- dry-run、幂等迁移、缺失文件原有行为保持。
- focused、相关 approval suites、全量黄金、Node/Web(如受影响)、真实临时数据库运行、diff-check、detect_changes 和独立审计通过。
## 7. 数据保护
所有测试使用临时审批文件和临时 SQLite;fixture 显式设置 quiesced。不得读取后写回 `server/data/approvals.json` 或现场 `master.db`。验收前后记录现场审批文件 hash 与 `server/data/.env` Git 状态。
## 8. 首轮 Plan Audit 修订
首轮 `PLAN AUDIT: FAIL` 的六项阻断已纳入:终态 request 重建、单调状态幂等、legacy raw-token 修复/冲突、canonical event digest、单事务后段回滚、quiesced + 同锁稳定快照。真实跨主机停写编排仍是部署验收,不在本地 SQLite 冒充完成。
## 9. 执行结果
- 首轮计划审计 6 项 blocking 已修订,最终 `PLAN AUDIT: PASS`。
- 并行 worker 超时失效后由主 agent 接管其已落入工作区的代码/测试并完成集成。
- 初始 focused 3 failures 已修复;最终 migration 29、approval related 74、full golden 1053 全绿。
- 真实临时 SQLite 迁移/consume/corrupt fail-closed 通过;现场 approvals/world hashes 不变。
- 首轮代码审计发现 legacy payload 身份未校验,补 request/grant payload immutable gate 与正负回归后最终 `AUDIT: PASS`。