aps-agent/docs/round-68-sap-outbound-p3-wo...

81 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Round 68 工作计划:SAP Outbound P3 Evidence Envelope
日期:2026-08-03
## 1. 背景与风险
`sap.sync.outbound` 会调用 SAP client `push_receipt`,属于外部副作用,但当前被标为 P2:单审批、无 execution grant、无批准载荷摘要、无世界指纹绑定。执行阶段重新预览,审批后版本/工单/数量变化可能被静默带入外部回写。
GitNexus 影响:
- `apply_outbound`:HIGH,2 个直接依赖、24 个总影响。
- `stage_sap_sync`:HIGH,3 个直接依赖、33 个总影响,影响 handle_intent 流程。
- `execute_confirmed`:HIGH,17 个直接依赖、22 个总影响。
- `power_of`:LOW,4 个直接依赖、10 个总影响。
动态 action 分派会低估流程影响,本轮按 HIGH 门禁处理。
## 2. 目标
1. 将 `sap.sync.outbound` 从 P2 升为 P3;inbound 保持 P2。
2. stage 时冻结版本、canonical outbound items、idempotency keys、payload digest、world fingerprint 和 evidence refs,并由 confirmation paramsHash 绑定。
3. 第二位不同审批人批准后,任何 SAP 调用之前校验:confirmId、execution grant、checkpoint、version、payload digest、world fingerprint、evidence refs、idempotency keys。
4. 校验通过后一次性消费 execution grant;重放失败。
5. 成功/拒绝/异常/部分失败均写 P3 审计,包含 approvers、confirmId、beforeSnapshot、evidenceRefs、payloadDigest、worldFingerprint 和 pushed/duplicate/failed idem keys。
6. 维持 Mock SAP 幂等;同 idem key 不产生第二次真实 receipt。
## 3. 契约
### Approval params
`{direction:'outbound', versionId, versionNo, payloadDigest, worldFingerprint, idemKeys, evidenceRefs}`
### Evidence refs
- `sap-outbound-version:<versionId>`
- `sap-outbound-payload:<sha256>`
- `sap-outbound-world:<sha256>`
### World fingerprint
只包含审批相关切片:目标 flex schedule version、该版本非冻结 work orders、关联 flex orders 的数量/外部 AUFNR;剔除 audit/journal/技术时间。
### 执行顺序
stage-time checkpoint + canonical approvalParams -> final execution recompute once -> validate current/checkpoint/approvalParams completely -> persist READY execution record -> consume grant with the exact stored approvalParams -> push the frozen approved items without rereading world -> per-item durable result -> final audit/save。任何 pre-push 失败调用次数必须为 0。
## 4. 写边界
- W68-SAP:`server/aps_domain/sap_sync.py`
- W68-WORKFLOW:`server/aps_domain/workflow.py`、`server/agent_core/harness.py`
- W68-TEST:`tests/golden/test_sap_sync.py` 与新增 `tests/golden/test_sap_outbound_evidence.py`
- 主 agent:集成、运行验证、文档、detect_changes、独立审计。
## 5. 验收
- P3 双人 SOD:第一次审批零调用,第二个不同用户后才 push。
- approval 后版本/WO/订单数量/外部 AUFNR 任一漂移均拒绝,push=0。
- digest/action/params/grant/checkpoint/evidence 替换或缺失均拒绝。
- grant 重放拒绝;成功后相同 idem key 幂等。
- success/failed audit evidence envelope 字段齐全。
- direct apply 不携带 P3 envelope 必须拒绝。
- SAP focused、gateway/confirmation/evidence 相关 suites、full golden、真实 Mock SAP 运行、服务健康、现场 hash 不变。
## 6. Out of scope
- 真实 SAP RFC/PI 凭据、白名单、网络和客户回执 schema。
- 分布式 Saga 补偿/撤销 SAP receipt;部分失败仅留结构化审计和幂等重试证据。
- inbound P2、folder.schedule TOCTOU、Automation G4。
- commit/push/merge/publish。
## 7. 首轮 Plan Audit 修订(权威覆盖前文歧义)
1. **Checkpoint 在 stage 时创建**:`stage_sap_sync` 在出确认卡前创建 approval-time checkpoint;`beforeSnapshot` 写入唯一 `approvalParams`。final confirm 只能读取并验证该 pairId,禁止重新创建。
2. **唯一 canonical approvalParams**:helper 生成并排序全部字段;`stage_confirmation` 原样存储,workflow/apply 原样透传,`consume_execution_grant` 使用同一对象。当前重算 projection 仅用于逐字段比较,不能替代批准对象。
3. **consume 前完成全部 world 读取和校验**:execution 使用已批准 items 的深拷贝;consume 后不再读取 world。任何漂移都在 grant 消费前拒绝。
4. **可恢复 execution/outbox**:`sapOutboundExecutions` 以 `confirmId + payloadDigest` 为身份;push 前持久化 READY/PENDING;grant consumed 后持久化 GRANT_CONSUMED;每个 idem key 逐条持久化 PUSHED/DUPLICATE/FAILED。部分失败写 FAILED/PARTIAL_FAILED 审计;恢复必须重新 P3 审批,并通过 `previousExecutionId` 关联前次 attempt,外部幂等键复用。
5. **纯 projection**:新增无 client 调用的 projection helper。完整候选 WO 稳定排序,fingerprint 覆盖 version id/no/status、候选总数、每个 WO 的 versionId/id/orderNo/flexOrderNo/operation/equipment/start/end/frozen、关联订单 quantity/AUFNR;排除 audit/journal/sapLinks/actor/time/client status。保持现有 50 条发送上限:先全量 canonical sort,再选前 50;fingerprint 同时包含 fullCandidateDigest/totalCount/limit,确保第 51 条漂移也被发现。
6. **Client 边界**:stage/preview 可调用只读 `status()`;安全门禁明确针对 `push_receipt`。纯 projection 与所有失败路径不得调用 push;client 实例化应位于 grant consume 后。
7. **HTTP/调用边界**:`apply_outbound` 的 P3 envelope 均为无默认值 keyword-only 必填;盘点全部 callers。preview/stage 路由永不 push,只有 `/api/actions/confirm` 最终 P3 分支可执行 push。
8. **可信审批身份**:requester/approvals 只取 `take_confirmation` 返回的审批存储 decision record,不能取请求 params;沿用现有策略:两名 approver userId 必须不同,requester 是否可参与由既有 RBAC/allowed 决定。审计记录 requester、两名 approver、顺序/角色、confirmId 和 grant consumption。