# P3 人工核查、关联重试与操作审计回归 验证日期:2026-09-17。服务端、SDK、接口及示例已实现,完整回归已通过,脚本终态退出码0。P3剩余查询筛选与事务Outbox接入继续推进。 ## 1. 实现范围 - [服务契约](../../pulse/service/recovery.go):显式历史执行结束/安全重试声明、原因、证据、版本及预算校验。 - [数据库事务](../../internal/storage/mysql/task_recovery.go):NEEDS_REVIEW核查为SUCCEEDED/FAILED,原尝试保留;独立FAILED/CANCELLED任务生成retry_of关联后继,原任务不改写。 - [审计查询](../../internal/storage/mysql/task_operations.go):专门权限与父资源范围,摘要游标分页、详情原因和证据,真实凭据身份。 - [Go SDK](../../sdk/go/recovery.go)、[接口手册](../api/task-recovery.md)、[完整运行场景](../examples/recovery.md)及[源码](../../sdk/go/examples/recovery/main.go)。 - [迁移000015](../../internal/storage/mysql/migrations/000015_task_manual_recovery.up.sql):现有RetryOf字段中文语义、同父后继唯一索引与禁止自身关联CHECK。35个模型、289个持久化字段,无新增字段,无外键/级联。 ## 2. 已覆盖的数据库与协议场景 [真实MySQL测试](../../internal/storage/mysql/task_recovery_integration_test.go)包含八组场景: 1. 核查保存新权威结果、保留旧尝试,释放一次额度;同键重报原audit_id,旧needs_review快照不改写,新终态单独交付,迟到成功只记录证据。 2. 普通凭据不能核查或读取管理审计;显式授权后可用。跨命名空间/错误父任务拒绝,游标绑定任务/种类;撤销队列范围后原回执也不能绕过权限。 3. 审计INSERT触发器故障整体回滚核查、额度、幂等及新结果事件;两个不同键竞争同一版本仅一个成功。 4. 未核查任务不能重试,队列满额不残留身份/子任务;释放后原键成功,新任务新预算、原载荷/冻结策略,原任务不变;迟到报告不覆盖核查。 5. 人工重试审计故障回滚新任务及额度;八个不同键竞争同一父任务仅一个直接后继和一条task.retry审计。 6. 真实串行计划因未知结果停止推进;合法归属核查后可推进下一触发,但计划任务不允许独立人工重试。 7. 等待、运行、成功、带门控或无效计划关联的恢复均拒绝,不能借无外键设计绕过所有权。 8. 流程所属任务拒绝直接核查,归属恢复后普通任务可核查;计划任务拒绝独立派生。 [HTTP输入边界](../../internal/transport/httpserver/recovery_test.go)与[服务参数边界](../../pulse/service/recovery_test.go)覆盖缺失/null/false声明、未知actor_id/owner_id、重复JSON键、非规范revision、证据空白/长度、结论/结果/预算限制。 [SDK测试](../../sdk/go/recovery_test.go)核验原幂等头、字符串版本精度、审计游标编码、非法路径拒绝、矛盾核查回执及缺少重试来源时拒绝确认成功。 ## 3. 无外键与迁移回归 真实v7→当前:70个历史外键全部删除,旧任务数据不变;323个未替换索引组件保留,单独核验(namespace_id,retry_of)新唯一索引。新装库外键为0。一次性库注入违规外键后启动结构检查拒绝就绪,移除后恢复。 本轮初次升级测试发现MySQL在新增同列唯一索引时会撤去原隐式非唯一索引fk_pulse_tasks_3。新迁移明确用唯一索引替换该冗余索引,测试分别核验替换对象和其他索引,避免将有意替换误报为数据损坏;没有增加数据库外键。 全量回归还发现旧v9升级夹具没有撤除新迁移15的约束,导致重放时重复CHECK名称;已修正一次性库夹具的还原步骤,保留真实生产迁移内容和校验和规则。 ## 4. 环境与复现 macOS、Go1.27.1、Docker单实例MySQL8.4.10,隔离临时数据库与回环端口,结束自动清理。 ```sh make fmt test vet build make integration ``` 完整脚本为人工恢复单独初始化--worker-access与--recovery-access凭据,普通Worker测试凭据不扩权。集成SDK通过[真实HTTP代理](../../sdk/go/recovery_integration_test.go)在核查和重试提交后丢弃响应,再原身份核对;并在真实WSS派发中验证retry_of。 | 验证组 | 本轮已取得的结果 | | --- | --- | | fmt / race / vet / build | PASS | | 人工恢复八组数据库用例 | PASS,5.32秒 | | v7无外键升级 / 全量MySQL集成 | PASS,4.85 / 104.575秒 | | SDK Worker完整组 | PASS,87.918秒 | | SDK计划 / 队列 / 批量 | PASS,5.659 / 4.536 / 1.366秒 | | SDK订阅管理 / 事件消费 | PASS,33.379 / 51.798秒 | | 新人工恢复SDK及两次HTTP丢回执 | PASS,3.371秒(含包开销) | | Inbox六组真实数据库与消费者故障 | PASS,35.606秒 | | SDK应用配额 | PASS,1.357秒 | | 九项节点/Worker进程故障 | PASS,60.644秒;连同Inbox消费者为十项真实进程故障 | | 全部运行示例及脚本终态 | 16个核心SDK示例 + 1个独立Inbox示例通过,完整脚本正常退出0 | 新人工恢复完整示例已通过;核心SDK依赖图未包含服务端包或MySQL驱动,文档575个本地链接、45个JSON块检查通过。最终结果取自已经结束的进程,未将中途日志当作全量通过。单次耗时仅为回归记录,不作为性能指标。 ## 5. 实际HTTP响应丢失与运行结果 SDK集成代理先完整收到服务端成功响应,再主动关闭客户端连接。核查和人工重试各丢一次响应;测试关闭HTTP连接复用,确保不会因传输层隐式重发掩盖首次网络错误。原键重报后分别恢复同一audit_id和同一子任务,审计分页确认仅一次task.review和一次task.retry。 WSS接收的新任务带有正确retry_of,执行成功后原任务仍FAILED。旧Worker再报成功时返回EVIDENCE_ONLY,原任务版本/结果不变;原执行尝试也未被核查改写。 完整示例实际输出模式: ```text source=<原任务ID> state=FAILED retry=<新任务ID> state=SUCCEEDED audit=<审计ID> operations=4 ``` 本例四条审计对应受理、原结果、核查与人工重试,开始事件本身不额外增加一条管理审计。没有自动删除历史。 ## 6. 生产边界 - 人工执行结束与安全重试是获授权主体基于外部证据的声明;平台不会自动证明外部物理停止或恰好一次。旧迟到报告保留证据,不重新打开已核查终态。 - 计划生成任务可核查但不能独立人工重试;流程所属任务/门控恢复留Weave控制器,不声称本阶段已实现流程恢复。 - 独立新任务仍受原应用/队列配额、冻结自动重试策略和执行权限约束;业务幂等身份必须沿原操作保持,不能用新taskID盲目调用外部接口。 - 任务详情和派发新增可选retry_of;旧SDK忽略未知响应字段,旧严格Schema需更新。迁移前停旧写入节点,不回改历史迁移,不自动清理非法历史关联。 - 本机正确性/进程故障不等于生产多故障域高可用、MySQL切换或百万租户容量验收;P4–P7仍按持续目标推进。