# Weave 执行决策与内部结果交付 状态:P4 内部基础已实现。已接通Pulse结果到Weave持久化Inbox,并提供有限步骤决策器;[实例受理和首批存储适配](11-workflow-admission.md)已补齐。[Inbox消费推进及根检查点运行器](12-workflow-runtime.md)已接入;[定时和查询时钟](13-workflow-clock.md)已接入,信号运行器及实例公共API/SDK仍待实现。不能把本页能力解释成完整公开端到端流程已交付。 ## 1. 为什么结果要独立持久化 任务已执行成功之后,流程节点可能立即退出。依赖进程内回调会丢失后继工作;从可变任务当前状态读取历史结果,也可能把后来的核查决定误当成旧事件。 当前结果事务顺序如下,任何一步失败均回滚: ```mermaid flowchart LR A[更新Pulse任务和尝试] --> B[结算执行许可与用量] B --> C[写入原始状态事件] C --> D[核验流程激活归属] D --> E[写入Weave Inbox和冻结快照] E --> F[锁定对外结果流并写入结果日志] F --> G[写入审计和执行回执] G --> H[提交事务] ``` 普通任务和计划任务仍使用原有结果链路,不产生Weave Inbox。流程任务使用`owner_kind=workflow_activation`、`owner_id=激活ID`;TASK直接关联激活task_id,POLL通过轮次记录关联。所有关系均限定同一命名空间,核对步骤、实例、门控、固定定义和版本,不使用外键。 已实现的内部交付触发为`task.succeeded`、`task.failed`、`task.cancelled`、`task.needs_review`。任务暂时失败并等待自动重试,不新增流程激活或内部结果事件。相同完成回执重报不产生第二个事件;同一来源任务状态版本还有独立唯一约束。 发布后使用旧激活的在途结果仍保留原激活身份;生产者不自行判定是否推进当前激活。当前消费者识别旧激活/旧轮次并留痕,不能将其用于当前工作。 生产者在现有结果快照查询中一起读取所有者和关联标记;普通结果不增加一次单独网络查询。流程关联核验使用只读关联查询,不在已持有任务锁后再锁实例、步骤或对外订阅。内部Inbox写入发生在外部结果流行锁之前,保持既有锁顺序。 ## 2. 数据模型与升级 迁移`000017_workflow_result_inbox`为`weave_inbox`新增四个中文注释字段: | 字段 | 含义 | | --- | --- | | source_task_id | 来源Pulse任务 | | source_activation_id | 事件发生时的流程激活 | | source_revision | 来源任务的状态版本,大于零 | | snapshot | schema_version=1的独立结果JSON文本,最多512 KiB,保留原始数字精度 | snapshot包含任务/队列ID、当时的状态与版本、结果、效果不确定标记和当前尝试ID;命名空间及实例由Inbox行固定。快照不依赖对外`orbit_result_events`的保留时间,也不从后来的任务结果重建。 `(namespace_id,event_id)`沿用原去重约束,新增`(namespace_id,source_task_id,source_revision)`唯一键。有效待处理记录必须四项完整且快照为合法JSON;只有迁移前的PROCESSED/QUARANTINED历史记录可以四项均为空。没有外键或级联。 v16及以前的未处理记录缺少冻结来源,升级后统一标为QUARANTINED,原租约清空、代次保留,error_code为LEGACY_RESULT_UNVERIFIED;已处理历史保持原状态。迁移不使用当前任务倒推旧结果,不赋予旧记录新的执行权。 新字段对应`weave/model/InboxEvent`,Go与SQL均有中文注释。原有扫描索引`(state,available_at,namespace_id,id)`保留,后续消费仍按持久化待处理状态和有限批次驱动。 关联丢失或类型异常时拒绝提交权威任务结果,不能悄悄丢掉内部交付后返回成功。此类持续异常需要修复存储关系后原身份核对;本阶段尚未提供自动修复工具。 ## 3. 纯步骤决策器 `weave/engine.Compile`校验并独立拷贝已发布的结构版本1定义。Program可并发读取,调用方修改原定义或取出的步骤副本不会改变它。 `Program.Plan(snapshot, limit)`接受事务内读取、已校验归属的当前步骤快照,返回最多1–32个ACTIVATE、COMPLETE或SKIP决策。它不执行HTTP、业务Go函数、数据库写入,也不读取当前时间。绝对截止、许可与执行授权继续由存储及Pulse负责。 | 能力 | 已实现的决策行为 | | --- | --- | | 顺序 | 仅消费已成功步骤输出;后继不会提前读取结果 | | 分支 | 首次按条件选择,保存case索引;再次推进复用选择,未选择路径分批标为SKIPPED | | 并行 | 分支分别使用进入节点前的数据,全部完成才产生组合输出;兄弟结果通过命名汇合读取 | | TASK | 解析输入与追踪/资源键,按Pulse运行数据边界检查;返回待持久化任务参数,不授予执行权 | | SIGNAL | 解析并冻结非空业务关联、名称与参数版本,返回等待命令 | | TIMER | 返回持久化等待命令,不分配业务Worker | | POLL | 解析固定查询输入及可选回执关联;成功查询结果有独立分类器,PENDING不等于节点成功 | | 暂停门控 | AllowNew=false时不激活新业务工作,允许整理已有组合结果;不替代数据库门控校验 | | 有限推进 | More=true时说明本轮变更预算耗尽,必须由适配层保存待续记录;已有活动分支仍继续校验,不能仅安排内存回调 | 只在已保存的SUCCEEDED投影上读取输出;类型不匹配、未知/失败状态、不可达的执行、已完成组合下仍有未结清子节点、伪造组合输出等都会拒绝整轮决策。不返回可以部分应用的错误结果。 引用与对象/数组构造保留JSON原始数值,输出及累计构造有大小上限;数字条件使用精确有理数比较。相邻大整数、长小数和科学计数法不会先转成float64。规则不提供任意脚本、动态时间或随机数据。 `ClassifyPoll`仅处理“查询请求成功”的业务结果,返回PENDING、SUCCEEDED、FAILED或NEEDS_REVIEW;未知值/类型错误拒绝。即使快照将POLL标为SUCCEEDED,结果仍在处理中、失败或待核查时也不能推进后继。 ## 4. 后续存储适配必须满足的条件 以下保留存储运行器的完整要求。第1项已按[受理报告](reports/phase-4-admission.md)验证;第2–4项结果消费和根接续已接入[实际运行器](12-workflow-runtime.md),第5项的TIMER/POLL已接入[时钟运行器](13-workflow-clock.md),第5项的信号等待已接入[信号运行器](14-workflow-signals.md),内部控制与结清已接入[控制运行器](15-workflow-controls.md),[核查与TASK新激活](16-workflow-recovery.md)已接入;公共接口和SDK已接入,完整历史及来源继续实现: 1. 启动时认证、固定不可变版本/输入和策略、校验全部队列、计算实例截止,并在同事务创建实例、门控、首批激活/任务及审计回执。相同身份返回原实例。 2. 消费者领取内部事件后核对命名空间、来源任务版本、激活/轮次及当前步骤版本;消费确认、步骤变化、后续任务或持久化待续记录同事务提交。 3. Plan返回值只有存储事务提交后才生效。额度不足或事务失败不能留下孤立激活/许可;重启从持久化快照重新计算,不重复创建已有任务。 4. AllowNew来自权威实例状态、控制版本、取消、总截止和门控。程序参数本身不是权限证明。任务deadline不得超过实例或查询节点绝对截止。 5. 数据库时间驱动真实等待与超时,按可靠受理时间裁决信号/结果,原截止和预算在恢复时保持。单轮重试与查询轮次不能互相补充次数。 6. 完成实例前核对所有活动工作结清和未知效果;失败/取消需要停止新派发并收集在途结果。人工恢复通过新激活保留历史。 7. 增加流程来源查询、实例/步骤/信号/操作HTTP和独立SDK、可运行端到端场景、真实节点强杀和竞争回归。其中未完成项继续属于P4–P5剩余工作。 本页的结果交付专用测试夹具通过SQL构造流程关联,只验证结果事务边界;后续[受理测试](reports/phase-4-admission.md)已使用真实内部Start创建实例,两者均不代表公共实例API或完整运行器已经存在。纯决策器的JSON往返测试验证可从快照重算,也不等同于真实进程恢复验收。