# 流程人工核查与恢复 API / SDK 状态:六类内部恢复已接入公共HTTP和独立SDK。所有路径以 `/v1/workflows/{id}/recovery/` 开头,均为POST,要求 `workflows:recover`、全部固定队列范围、Bearer凭据、稳定Idempotency-Key及application/json。成功首次与重复响应均为200。身份、读查询、错误码见[实例接口](workflows.md)。 恢复只针对NEEDS_REVIEW实例。SDK不会自动重试、补齐声明、刷新版本或生成新键。核查前应读取当前实例、当前步骤、相关任务及外部证据;每次新决定使用当前版本,响应未知则用原决定原键核对。输入和结果最多256 KiB,请求体总计最多512 KiB。 ## 1. 公共字段 所有请求包含WorkflowRecoveryDecision的**扁平字段**: | 字段 | 必填与语义 | | --- | --- | | expected_revision | 必填,当前实例的非零revision十进制字符串,不能用control_revision替代 | | reason | 必填,1–1024个Unicode字符;不允许全空白或NUL | | evidence_ref | 必填,1–512个Unicode字符,不含空白或控制字符;平台不访问引用、不存放秘密 | 步骤类请求还包含WorkflowStepDecision的扁平字段:step_key(固定定义步骤名,最多64个ASCII字符)、activation_id(当前32位激活ID)、expected_step_revision(当前非零步骤版本字符串)。实例版本、步骤版本及激活身份一起检查,旧激活不能操作新工作。 服务端检查待消费/隔离Inbox、历史任务归属、仍在执行的尝试及未释放许可。声明为true是操作者确认,不替代服务端验证。恢复不会重置原绝对截止、次数或业务关联;已有停止目标不会被撤销。 ## 2. 六类命令 | 路径后缀 | SDK方法 / 请求类型 | 额外必填字段与结果 | | --- | --- | --- | | task-review | ReviewWorkflowTask / ReviewWorkflowTaskRequest | task_id、expected_task_revision字符串、execution_settled=true、outcome=SUCCEEDED或FAILED、非null result;核实本实例任一历史激活的待核查执行 | | step-resolve | ResolveWorkflowStep / ResolveWorkflowStepRequest | 步骤公共字段、execution_settled=true、outcome=SUCCEEDED或FAILED、result;确认当前叶子步骤的逻辑结论 | | continue | ContinueWorkflow / ContinueWorkflowRequest | review_resolved=true;确认全部核查事项已处理后解除核查 | | step-retry | RetryWorkflowStep / RetryWorkflowStepRequest | 步骤公共字段、safe_to_retry=true;为TASK/POLL创建新激活,保留原预算和截止 | | poll-continue | ContinueWorkflowPoll / ContinueWorkflowPollRequest | 步骤公共字段、pending_confirmed=true;权威结果已确认为PENDING时按原间隔恢复等待,不重查同轮 | | wait-rearm | RearmWorkflowWait / RearmWorkflowWaitRequest | 步骤公共字段、wait_verified=true;独立SIGNAL/TIMER在原截止内更换等待代次,含首次最多32代 | 所有确认布尔值必须在HTTP中显式提供且为true。拒绝嵌套`Decision`、伪造actor_id、大小写别名和未声明字段。 执行核查请求示例: ```json { "expected_revision":"8", "reason":"已核实请求完成且外部作业仍在处理", "evidence_ref":"evidence:query-001", "task_id":"88888888888888888888888888888888", "expected_task_revision":"4", "execution_settled":true, "outcome":"SUCCEEDED", "result":"PENDING" } ``` ReviewTask只改变执行结论,旧UNKNOWN尝试和证据保持原样。它产生新的内部结果,必须等待后台消费,当前步骤及实例不会自动解除核查。POLL请求成功不等于流程业务成功;ResolveStep对POLL的成功结果必须映射到固定的业务成功值,不能把PENDING写成步骤成功。TIMER成功只能在首次等待时长满足后确认,result必须为显式null。 PENDING接续请求示例: ```json { "expected_revision":"11", "reason":"沿原间隔继续查询外部作业", "evidence_ref":"evidence:query-001", "step_key":"query", "activation_id":"66666666666666666666666666666666", "expected_step_revision":"3", "pending_confirmed":true } ``` 相同字段布局适用于step-resolve、step-retry和wait-rearm,额外字段按上表替换。Continue只有实例公共字段及review_resolved,不要求步骤字段。 ## 3. 回执与当前视图 六个方法统一返回WorkflowRecoveryResult;HTTP的data字段为该对象: | 字段 | 含义 | | --- | --- | | instance | 首次管理事务完成时的完整实例快照,字段同[启动回执](workflows.md#2-固定版本启动) | | audit_id | 本次流程管理审计ID,可用GetWorkflowOperation读取原因、证据及前后摘要 | | step | 步骤类操作返回当前步骤快照,包含激活代次;不是后续实时状态 | | task | task-review或step-retry返回执行任务快照;字段同GetTask | | task_audit_id | task-review对应的独立任务审计ID,与流程审计同事务保存 | | deduplicated | 是否恢复原回执;恢复原键仍检查当前权限 | 步骤恢复回执示意,省略字段按协议确实不存在,并非JSON占位: ```json { "request_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "data":{ "instance":{ "id":"22222222222222222222222222222222", "namespace_id":"33333333333333333333333333333333", "definition_id":"11111111111111111111111111111111", "definition_version_id":"44444444444444444444444444444444", "version":"v1","state":"NEEDS_REVIEW","revision":"13", "paused":false,"control_revision":"3", "input":{"n":9007199254740993},"business_key":"operation-001 ", "deadline_at":"2026-09-18T08:01:00Z", "created_at":"2026-09-18T08:00:00Z","updated_at":"2026-09-18T08:00:10Z" }, "audit_id":"99999999999999999999999999999999", "step":{ "id":"55555555555555555555555555555555","key":"query","kind":"POLL", "state":"WAITING","revision":"4", "activation_id":"66666666666666666666666666666666","activation_sequence":1 }, "deduplicated":false } } ``` ContinuePoll保留当前激活与信号代次;RetryStep及RearmWait创建新激活。恢复等待或创建新任务后仍须显式Continue解除核查;paused仍为true时还需ControlWorkflow(RESUME)。任何恢复操作都不盲目撤回已发生的外部业务效果。 ## 4. SDK接入顺序 以下为嵌入接入方函数的调用片段,api是已认证Client,workflowID为目标实例,operationKey与本次完整命令需要由调用方持久保存: ```go current, err := api.GetWorkflowStep(ctx, workflowID, "query") if err != nil { return err } if current.Step.ActivationID == nil { return errors.New("query is not activated") } command := orbit.ContinueWorkflowPollRequest{ WorkflowStepDecision: orbit.WorkflowStepDecision{ WorkflowRecoveryDecision: orbit.WorkflowRecoveryDecision{ ExpectedRevision: current.Revision, Reason: "已核实权威PENDING结果,沿原预算接续", EvidenceRef: "evidence:query-001", }, StepKey: current.Step.Key, ActivationID: *current.Step.ActivationID, ExpectedStepRevision: current.Step.Revision, }, PendingConfirmed: true, } receipt, err := api.ContinueWorkflowPoll(ctx, workflowID, operationKey, command) if err != nil { return err } // 不生成新键绕过冲突;结果不明时原命令原键核对。 audit, err := api.GetWorkflowOperation(ctx, workflowID, receipt.AuditID) if err != nil { return err } fmt.Println(audit.Action, receipt.Instance.State) ``` 完整、可执行的六类SDK场景位于[真实服务集成测试](../../sdk/go/workflow_instances_integration_test.go):UNKNOWN→执行核查→逻辑结论→解除核查;已知失败→剩余预算重试,同时重建信号等待;UNKNOWN查询→核查为PENDING→原间隔接续。运行方式为仓库根目录`make integration`,由脚本创建隔离MySQL、实际服务及凭据。 一般步骤是:查询当前事实 → 提交一个有证据的决定 → 等待该决定产生的内部结果消费 → 重新读取当前版本 → 再提交下一决定。不要自动将任意STATE_CONFLICT重试为“最新版本”;冲突可能意味着新的在途结果、权限变化、原截止已到或预算耗尽,应重新核查。相同命令的响应未知则仍使用原版本和原键。 ## 5. 兼容与剩余范围 新路由仅在本开发版本及之后可用;旧服务返回404时不能降级为普通任务重试来绕过Weave所有权。SDK类型独立、版本字段保留uint64精度。规则及数据模型未增加表、字段、外键或级联。 本批尚不提供完整历史激活/轮次遍历、实例筛选列表或信号资源审计列表;当前输入结果、当前查询预算、单条信号和流程管理审计可读。生产数据库切换、容量及恢复目标继续按P6验证,见[阶段报告](../reports/phase-5-public-api.md)。