aps-agent/docs/round-70-approval-database-...

6.2 KiB
Raw Permalink Blame History

Round 70 工作计划:审批数据库权威 Clock / TTL / CAS

日期:2026-08-04 来源:plan.md R67-B2、docs/product/plan-completion-matrix.md P0 可信试点基线 当前状态:计划完成,待进入实现 外部边界:当前机器 Docker daemon 不可用、无 mysql client、无真实 MySQL 8 测试实例。

1. 背景与问题

DatabaseApprovalStore 已具备共享 DB、revision CAS、一次性 grant、同事务 grant/event 和 InnoDB/Alembic 门禁,但审批和 grant 的有效期仍由 Gateway 进程传入 now_epoch 或 time.time()裁决。两个节点存在正负时钟偏移时,可能对同一请求得出不同 TTL 结论;请求状态转换 CAS 也未把 expiry 条件放入 SQL UPDATE,存在跨过到期边界后的 TOCTOU。

2. 本轮目标

  1. MySQL 的审批、转派、过期、grant 创建/消费、事件时间全部使用同一写事务连接取得的数据库权威时间。
  2. MySQL 安全判断忽略调用方绝对 now_epoch;SQLite 继续保留 test-only 注入时间以保证确定性测试。
  3. 所有 request CAS 增加 expires_at_epoch > db_now,过期 CAS 使用 <= db_now。
  4. grant consume/expire 使用同事务 db_now,状态和 event 不得部分提交。
  5. 请求 TTL 仅继承调用方给出的持续时间 expiresAtEpoch-createdAtEpoch,起点重锚到 MySQL db_now;拒绝非有限、非正 TTL。
  6. MySQL epoch 列由 FLOAT 升级为 DOUBLE,避免 2026 年 epoch 精度损失。
  7. 保持文件审批 backend、Harness/API 合同和 SQLite test-only 边界不变。

3. 已确认设计决策

3.1 权威时钟

  • MySQL:在实际写事务 session 上执行 SELECT UNIX_TIMESTAMP(CURRENT_TIMESTAMP(6))。
  • SQLite:使用测试传入 now_epoch;未传时只为测试/维护兼容使用 time.time()。
  • 其他数据库:继续失败关闭。
  • DB 时钟查询失败:禁止回退应用时钟,操作失败关闭。

3.2 TTL 与边界

  • now < expires:有效。
  • now == expires:已过期。
  • now > expires:已过期。
  • request TTL = payload 的 expiresAtEpoch-createdAtEpoch,验证为有限且大于 0,再以 db_now 重建绝对时间。
  • grant TTL 继续使用 grant_ttl_seconds,但起点改为 db_now。

3.3 CAS 与事务

每次尝试采用新 session:

BEGIN
→ 获取 db_now
→ 读取当前 row/revision
→ 权限、SOD、payload 校验
→ status + revision + expiry 条件 UPDATE
→ 插入 grant(如有)
→ 插入 event
→ COMMIT

rowcount=0 视为 CAS/TTL 未命中并有限重试;数据库断连、权限、schema 或 DB clock 错误不得伪装为普通冲突。

3.4 Schema

当前 SQLAlchemy MySQL DDL 实测生成 FLOAT。本轮:

  • request:created_at_epoch / expires_at_epoch
  • grant:created_at_epoch / expires_at_epoch / consumed_at_epoch
  • event:decided_at_epoch

升级为 MySQL DOUBLE(asdecimal=False),SQLite 继续保持现有浮点合同;新增 Alembic revision 20260804_01,并更新数据库后端启动门禁要求。

4. 写边界

允许修改

  • server/agent_core/approval_db_store.py
  • server/db/models.py
  • server/db/migrations/versions/20260804_01_approval_database_clock.py
  • tests/golden/test_approval_database_clock.py
  • 必要时最小更新 tests/golden/test_approval_database_store.py
  • Round 70 文档、plan.md、完成矩阵和 CHANGELOG

禁止修改

  • 文件审批 backend approval_store.py
  • Harness、Gateway API、前端
  • approval migration 的 B1 业务逻辑
  • SAP/MES/WMS 集成
  • Round 69 数据生成器和 canonical 数据包
  • 受保护 server/data 文件

5. 验收矩阵

5.1 本地必须通过

  • SQLite 边界:到期前/等于/到期后审批、转派和 grant 消费。
  • SQLite 并发:final approve、approve/reject、approve/expire、consume/expire、consume-once。
  • DB clock 查询失败 fail-closed,不回退应用时间。
  • grant/event/commit 故障注入整体回滚。
  • MySQL mock/编译:权威时间 SQL、CAS expiry 条件、六个 epoch 列为 DOUBLE。
  • Alembic upgrade/downgrade 与 ORM 一致。
  • 原有 approval database/migration/store/harness 回归。
  • 全量 golden、Ruff、compileall、8003/5173 健康和受保护数据哈希。

5.2 外部阻断,不冒充完成

当前无 Docker daemon、mysql client 和真实 MySQL 实例,因此本地不能证明:

  • 两个独立 Gateway 节点正负时钟偏移;
  • MySQL REPEATABLE READ 下的真实多连接并发;
  • deadlock/lock wait timeout、commit 前后断连;
  • session timezone、主从/HA 切换;
  • 生产账号、代理、连接池和真实部署拓扑。

本轮完成后矩阵可标记 Done (local contract) / Partial (external MySQL),不得标记生产完成。

6. 验证命令

.venv\Scripts\python.exe -m ruff check server/agent_core/approval_db_store.py server/db/models.py server/db/migrations/versions/20260804_01_approval_database_clock.py tests/golden/test_approval_database_clock.py tests/golden/test_approval_database_store.py
.venv\Scripts\python.exe -m compileall -q server/agent_core/approval_db_store.py server/db/models.py server/db/migrations/versions/20260804_01_approval_database_clock.py
.venv\Scripts\python.exe -m pytest -q tests/golden/test_approval_database_clock.py tests/golden/test_approval_database_store.py tests/golden/test_approval_migration.py tests/golden/test_approval_store.py tests/golden/test_harness_p3.py
.venv\Scripts\python.exe -m pytest tests/golden -q
npm run test:node
npm run build:web

完成代码后刷新 GitNexus 并执行 detect_changes(compare main);未经用户授权不 commit/push/merge/publish。

7. 风险门禁

  • DatabaseApprovalStore upstream impact:LOW,7 个受影响符号。
  • ApprovalRequestRecord upstream impact:HIGH,127 个受影响符号;修改模型前必须向用户明确预警,并用迁移、DDL 编译和全量回归保护。
  • 若 implementation 发现需修改 Harness/API 或文件审批 backend,立即停止扩界并重新做 impact/计划审计。

8. Plan Audit

PLAN AUDIT: PASS

Blocking issues:无。 Clarification needed:无;MySQL 真实环境已明确列为外部验收门禁。 Non-blocking improvements:未来取得 MySQL 8 实例后补双节点时钟偏移和故障注入,不影响本地合同实现。