# 流程激活、查询轮次、信号与信号审计 状态:HTTP与独立Go SDK已接入;本页查询明确指定的历史激活,不跟随逻辑步骤的当前激活。[实例筛选分页](workflow-queries.md)和[状态事件时间线](workflow-events.md)已接入,更多独立恢复场景仍按[开发计划](../08-development-plan.md)交付。测试与范围见[本批报告](../reports/phase-5-history.md)。 ## 1. 路由与调用规则 所有接口为GET,成功200,使用`Authorization: Bearer `,不需要请求体或幂等键。`{id}`是流程实例ID,`{activation}`是激活ID,`{signal}`是信号ID,`{operation}`是审计记录ID;均为32位小写十六进制。`{key}`是固定定义中的步骤键,1–64个ASCII字符,首位字母或数字,之后允许字母、数字、`_ . -`,禁止内部`$root`。操作审计的operation_id不是查询路径中的审计ID。 | 路径 | 权限 | SDK方法 | | --- | --- | --- | | `/v1/workflows/{id}/steps/{key}/activations` | workflows:read | ListWorkflowActivations(ctx, id, key, page) | | `/v1/workflows/{id}/steps/{key}/activations/{activation}` | workflows:read | GetWorkflowActivation(ctx, id, key, activation) | | `/v1/workflows/{id}/steps/{key}/activations/{activation}/rounds` | workflows:read | ListWorkflowPollRounds(ctx, id, key, activation, page) | | `/v1/workflows/{id}/signals` | workflows:read | ListWorkflowSignals(ctx, id, page) | | `/v1/workflows/{id}/signals/{signal}/operations` | workflows:audit:read | ListWorkflowSignalOperations(ctx, id, signal, page) | | `/v1/workflows/{id}/signals/{signal}/operations/{operation}` | workflows:audit:read | GetWorkflowSignalOperation(ctx, id, signal, operation) | 服务端先校验命名空间和固定流程引用的全部队列,再读取子资源。普通流程读取权限不包含审计权限;bootstrap用`--workflow-audit-access`显式签发审计权限。知道激活、任务、信号或审计ID不能绕过父资源权限。 四个列表只接受可选`limit`、`cursor`。limit为十进制整数1–100,省略默认20;SDK的`HistoryPageRequest.Limit=0`表示省略。cursor最多1024字节,按URL查询参数编码,必须来自相同查询种类、实例以及对应步骤/激活/信号。未知、重复或非法参数返回400;详情不接受查询参数。 每页最多读取limit+1条判断后续,空列表返回`items: []`,没有下一页时省略next_cursor。游标不是授权凭据;改权限后继续分页仍重新授权。翻页之间不是固定数据库快照,状态可能变化、新记录可能产生;要看最新记录需重新从第一页开始,不能把游标用于执行或确认操作。 ## 2. 激活列表和单次详情 列表按sequence倒序,不聚合input、output、查询预算或等待正文。返回类型是[WorkflowActivationPage](../../sdk/go/workflow_history_types.go): | 字段 | 类型与含义 | | --- | --- | | instance_id、step_id | 字符串,已验证父实例与逻辑步骤 | | revision | 十进制字符串,本页读取时的实例版本;SDK为uint64 | | current_activation_id | 可选字符串,本页读取时的当前激活;尚未激活时省略 | | items | 激活摘要数组,未激活步骤为[] | | next_cursor | 可选字符串,后续位置 | 每项含id、step_id、sequence(正uint32)、state、revision(uint64字符串)、created_at、updated_at;task_id仅用于TASK直接任务,completed_at仅已保存结束时间时返回。时间均为UTC RFC3339。state反映这条激活当前保存的状态,不代表整个实例状态;核查可能改变记录状态,列表不是每次状态变化的事件流水。 下面是恢复后的第二页,当前激活已经是第2代,本页仍读取第1代: ```json { "request_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "data": { "instance_id": "11111111111111111111111111111111", "revision": "12", "step_id": "22222222222222222222222222222222", "current_activation_id": "44444444444444444444444444444444", "items": [{ "id": "33333333333333333333333333333333", "step_id": "22222222222222222222222222222222", "sequence": 1, "state": "FAILED", "revision": "3", "task_id": "55555555555555555555555555555555", "created_at": "2026-09-18T08:00:00Z", "updated_at": "2026-09-18T08:00:01Z", "completed_at": "2026-09-18T08:00:01Z" }] } } ``` 详情的data为`{instance_id, revision, step, activation, input, output?, poll?, wait?}`: - `step`是[逻辑步骤当前视图](workflows.md#3-步骤及当前激活),`activation`是明确请求的历史激活摘要,两者的激活ID和代次允许不同。 - `input/output`只属于指定激活。缺失output与显式null不同,精确JSON数字由SDK的json.RawMessage保留;不会展开其他激活大正文。 - POLL的`poll`返回这一激活的first_round、first_round_attempts_used、used_rounds、冻结上限、原截止和最后任务。字段与[当前查询详情](workflows.md#3-步骤及当前激活)一致,范围改为指定激活。 - SIGNAL、TIMER和有最终信号的POLL返回该激活的wait,包含原等待ID、原截止、状态和已有赢家。恢复后旧等待仍可读,新激活不能延长原截止或替换旧winner。 - TASK尝试、未知结果和迟到证据通过task_id继续查询[执行历史](execution-history.md)。人工决定的前后摘要通过[流程操作审计](workflows.md#6-操作审计与错误处理)读取。 ## 3. POLL轮次列表 data为`{instance_id, revision, activation_id, items, next_cursor?}`,items按业务轮round倒序。每项为`{id, activation_id, round, task_id, created_at}`;round是正uint32,其他身份为字符串。 必须明确指定一个POLL激活;TASK或其他节点调用返回400。人工恢复同一业务轮会新建激活和任务,因此“第1代的业务第1轮”和“第2代的业务第1轮”可以同时存在。先遍历激活,再遍历每个激活的轮次,不能把激活序号、业务轮次和Pulse尝试epoch混为一谈。 ```json { "request_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "data": { "instance_id": "11111111111111111111111111111111", "revision": "20", "activation_id": "44444444444444444444444444444444", "items": [{ "id": "66666666666666666666666666666666", "activation_id": "44444444444444444444444444444444", "round": 1, "task_id": "77777777777777777777777777777777", "created_at": "2026-09-18T08:00:02Z" }] } } ``` 列表显式验证每轮任务属于该命名空间、激活、固定队列和门控。关联损坏不返回部分成功,也不利用外键推断归属。GetTaskSource可以反向核对[固定来源](task-source.md#5-流程固定来源)。 ## 4. 信号与信号资源审计 信号列表data为`{workflow_id, revision, items, next_cursor?}`,按accepted_at和id倒序。每项含id、workflow_id、revision、name、payload_version、target_step_key、target_activation_sequence、state、accepted_at、expires_at,有处置原因时增加disposition。列表不携带payload、correlation_key、actor_id或evidence_ref;完整原信号使用[GetWorkflowSignal](workflows.md)读取。 早期未核验历史可能payload_version为空、target_activation_sequence为0,列表保留这些事实,不补造可执行身份。CONSUMED表示该信号已消费,单条详情的consumed_wait_id与指定历史激活的wait.winner_signal_id可以交叉核对。 信号操作列表data为`{items, next_cursor?}`,按created_at和id倒序,每项含id、workflow_id、signal_id、operation_id、actor_id、action、created_at。当前受理动作action为`workflow.signal`。操作详情的data直接是该摘要,再增加reason、可选evidence_ref、before和after: ```json { "request_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "data": { "id": "88888888888888888888888888888888", "workflow_id": "11111111111111111111111111111111", "signal_id": "99999999999999999999999999999999", "operation_id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "actor_id": "cccccccccccccccccccccccccccccccc", "action": "workflow.signal", "created_at": "2026-09-18T08:00:02Z", "reason": "verified external completion", "evidence_ref": "proof:event-1", "before": null, "after": {"signal_id": "99999999999999999999999999999999", "state": "PENDING"} } } ``` 上例after省略其他受理身份字段,完整内容以原操作保存的JSON为准。after是原受理审计,之后信号已消费也不能把其中PENDING改成CONSUMED。此接口只查`workflow_signal`资源,流程推进/控制/恢复操作在流程审计中。普通信号消费状态通过Get/List查看,不能将本接口解释成所有内部状态事件的统一时间线。 ## 5. SDK调用场景 以下代码接在已配置client与ctx、已知workflowID及stepKey的调用方中;page使用SDK类型,与服务端模型无依赖: ```go cursor := "" for { page, err := client.ListWorkflowActivations(ctx, workflowID, stepKey, orbit.HistoryPageRequest{Limit: 20, Cursor: cursor}) if err != nil { return err } for _, activation := range page.Items { detail, err := client.GetWorkflowActivation(ctx, workflowID, stepKey, activation.ID) if err != nil { return err } fmt.Printf("activation=%s sequence=%d state=%s\n", detail.Activation.ID, detail.Activation.Sequence, detail.Activation.State) // 按需读取单次结果;禁止拿当前step的代次冒充历史激活代次。 } if page.NextCursor == "" { break } cursor = page.NextCursor } ``` POLL继续用ListWorkflowPollRounds(ctx, workflowID, stepKey, activation.ID, page)分页获取该次激活的任务,再按任务查询尝试和证据。信号场景用ListWorkflowSignals定位旧代次和当前代次的信号;具备审计权限后用ListWorkflowSignalOperations和GetWorkflowSignalOperation读取原因与原受理证据。 [可运行并行作业示例](../examples/workflow.md)会核对已完成等待的历史赢家与CONSUMED信号。[SDK真实链路测试](../../sdk/go/workflow_instances_integration_test.go)另外覆盖TASK恢复、SIGNAL换代、POLL同轮恢复及下一业务轮分页;入口为`make integration`。调用方法见[SDK源码](../../sdk/go/workflow_history.go),各字段中文注释见[独立类型](../../sdk/go/workflow_history_types.go)。 ## 6. 错误、部署与兼容 | HTTP / 错误码 | 含义与处理 | | --- | --- | | 400 INVALID_ARGUMENT | 身份、步骤键、分页参数或游标范围错误;或非POLL读取轮次。修正查询,不能丢掉筛选继续 | | 401 UNAUTHENTICATED | 凭据无效或过期,按[认证协议](authentication.md)处理 | | 403 PERMISSION_DENIED | 缺少读取或显式审计权限 | | 404 NOT_FOUND | 父/子资源不属于该作用域、不存在或队列不可访问;不跨资源查找 | | 503 UNAVAILABLE | 关联损坏或数据库暂不可用;保留资源身份并检查服务记录,不能将空列表当作成功 | 所有查询只读,不重算截止、重置预算、消耗轮次或触发恢复。SDK拒绝非法路径/分页及响应父资源不匹配;不自动创建新身份。读操作网络失败可以按原资源重读,状态可能已经前进。 服务端需先应用`000023_workflow_signal_history`,只增加(namespace_id,workflow_id,accepted_at,id)索引;原数据、迁移历史、模型字段和零外键要求保持。维护窗口和DDL锁需按目标数据库演练。当前SDK仍为开发版,使用与服务端匹配的开发提交;历史/事件功能已进入P4/P5阶段验收,生产容量、跨版本和高可用验收仍未完成。