# 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: ```text 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. 验证命令 ```powershell .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 实例后补双节点时钟偏移和故障注入,不影响本地合同实现。