Incident 与 Reconciliation
OceanWay 告警升级、事故协同、事实不一致对账与受控处置契约
Incident 处理“平台或客户正在受到什么影响”,Reconciliation 诊断处理“哪些执行、资产或经济事实仍不一致”。两者可以关联,但不能合并成一个万能工单,也不能用人工备注替代领域事实。Reconciliation 是一种核对过程,不等于一定创建 Reconciliation Case。
| 能力 | Owner | 当前状态 |
|---|---|---|
| Alert / Incident / Timeline | oceanway-core Operations 模块 | 目标契约,尚未实现 |
| Reconciliation Case / Evidence / Resolution | oceanway-core Operations 模块与 Billing Command Owner | 首期仅两种 Billing sourceScope,目标契约尚未实现 |
| 页面与 Workforce 协作 | oceanway-admin | 目标页面,尚未实现 |
| 部署、告警路由与运行证据 | oceanway-infrastructure | 随后续阶段交付 |
它们不属于当前 PostgreSQL Outbox 与第一版只读 Run Explorer 的退出门禁。只有基础事实、投影、权限和 Audit 可靠后,才进入本阶段。
Alert 与 Incident
一次错误不必产生 Alert,一次 Alert 也不必升级为 Incident。标准关系是:
Error / SLO Signal
→ Alert Instance
→ Acknowledge / Triage
→ Incident(存在实际或高风险影响时)
→ Mitigation
→ Recovery Validation
→ Affected Fact Scan
→ Reconciliation / Communication / PostmortemIncident 至少固定:
incidentId / severity / status
commander / participants / domainOwners
detectedAt / startedAt / mitigatedAt / resolvedAt
affectedProducts / surfaces / pools / deployments / offerings
affectedOrganizationCount / runCount / billingExposureSummary
linkedAlerts / errorGroups / traces / changes / cases
customerCommunicationStatus
rootCauseStatus / followUps / postmortemStatusTimeline 追加写入,后续修正以新记录说明旧判断为何变化,不覆盖历史。Incident Commander 负责协调,不自动继承供应、财务、安全或客户内容权限。
状态与关闭条件
具体状态由 Contracts 版本化;至少能区分 Open、Acknowledged、Mitigating、Monitoring 与 Resolved。阈值、升级时间和通知渠道来自 SLO、合同、值班策略与管理员配置,不在架构中写死。
关闭不能只看成功率恢复。必须按影响范围扫描:
- 仍在非终态或未知状态的 Run/Attempt;
- Provider 已成功但尚未登记的结果/Asset;
- 未结算、未释放或金额不一致的 Reservation;
- 未送达且需要重放的客户 Webhook;
- 临时路由、Silence、JIT Grant 和 Break-glass 是否已撤销。
Reconciliation 诊断与正式 Case 边界
以下情况都需要核对,但首期不会自动创建 Operations Reconciliation Case:
- 无法确认请求是否提交到 Gateway/Provider;
- Provider 成功但结果尚未取回或登记 Asset;
- Run 已终态但 Gateway Evidence、规范
MeterEvent/ProviderCostFact、Reservation 或 Ledger 未收敛; - Poll 与未来 Callback 报告冲突状态;
- 重复 Attempt、重复 Asset 或疑似重复结算;
- 客户退款与已发生供应成本之间需要明确承担方;
- 业务已完成但客户 Webhook 未送达。
这些信号首先进入 Owner 本地状态、Blocked Work、Error/Alert 或 Incident,并保留可定向读取的 Conflict Fact / Evidence。首期正式 Case 是封闭联合,只有:
sourceScope.kind | 打开条件 | 业务终态 Authority |
|---|---|---|
late_settlement_dimension | Billing 在终局后形成正差额 LateSettlementExposure | BillingCaseResolutionApplied@1 |
billing_finalization_run | Billing Finalization Decision 判定 Run 无法安全终局 | FinalizationReconciliationResolutionApplied@1 |
Provider Cost、Gateway、Execution、Asset、Webhook 或普通客户计量冲突不得伪造第三种 sourceScope。若未来需要正式队列,必须先发布该来源的 Canonical Event、Mandatory core_operations Delivery、严格 Owner Read、Case Identity、终态 Authority、响应丢失恢复与投影契约。
Case 至少记录 reconciliationCaseRef、类型、状态、Owner、影响对象、确定性、CaseEvidenceManifest完整四元组、提出的 Resolution、审批、执行命令与验证结果。Evidence只保存受控引用和低敏摘要,并统一使用 domain_fact | operational_observation | operations_runtime | telemetry_reference | derived_summary五值 evidenceKind;Delivery、Attempt、Receipt、Checkpoint、Source Snapshot、Revision Gap、Gateway Provider Evidence Link与 Quarantine均归为 operations_runtime,其中 Checkpoint不含标量位置。Case不复制 Provider原始 Payload、Prompt或 Secret。
其中 Gateway Evidence仍由对应私有 Gateway持有,但 Case不能只保存裸 providerUsageEvidenceRef / providerCostEvidenceRef。Operations为每个 Case Revision追加不可变、内容寻址的证据清单:
CaseEvidenceManifest@1 = {
caseEvidenceManifestRef / caseEvidenceManifestSchemaVersion
caseEvidenceManifestDigestAlgorithmVersion = jcs-sha256-v1
caseEvidenceManifestDigest
reconciliationCaseRef / caseRevision
evidenceLinkCount
evidenceLinks[]=sorted (
{ evidenceKind=operations_runtime; runtimeKind=gateway_provider_evidence;
providerEvidenceKind=provider_usage|provider_cost;
executionAttemptId / gatewayDeploymentId;
attemptRouteBindingRef / attemptRouteBindingSchemaVersion;
attemptRouteBindingDigestAlgorithmVersion / attemptRouteBindingDigest;
gatewayRouteSnapshotRef / gatewayRouteSnapshotSchemaVersion;
gatewayRouteSnapshotDigestAlgorithmVersion / gatewayRouteSnapshotDigest;
executionAnchor = {kind=text_invocation; gatewayInvocationId}
| {kind=media_task; gatewayTaskId};
availabilityRef / availabilitySchemaVersion;
availabilityDigestAlgorithmVersion / availabilityDigest / availabilityStateVersion;
evidenceRef / evidenceSchemaVersion;
evidenceDigestAlgorithmVersion / evidenceDigest }
| CaseEvidenceLink@1.domain_fact
| CaseEvidenceLink@1.operational_observation
| CaseEvidenceLink@1.operations_runtime_non_gateway
| CaseEvidenceLink@1.telemetry_reference
| CaseEvidenceLink@1.derived_summary
)
supersedesCaseEvidenceManifest =
{ state=root }
| { state=supersedes; ref / schemaVersion / digestAlgorithmVersion / digest; caseRevision }
recordedAt
}Manifest摘要覆盖除 Repository-owned Ref/Time和摘要自身外的严格 Candidate;数组按 Contracts注册键排序且 Count全等。Gateway条目必须是一个完整、历史不可变的关联包:Evidence、当时绑定它的 Availability Snapshot、AttemptRouteBinding、GatewayRouteSnapshot与执行锚点缺一不可,任一四元组半联合、同 Ref/Schema异 Digest、错 Attempt/Pool/Anchor均拒绝。Case更新证据只能追加直接后继 Manifest并推进 Case Revision,不能原地覆盖;查询权限不足时整体返回 redacted,不得裁成裸 Ref。
需要查看 Gateway正文时,Operations先基于当前 Case权限和精确 Manifest条目签发短期 ReconciliationEvidenceReadGrant@1,绑定 Case/Revision、Manifest四元组、完整 Gateway Evidence Link、目标 Gateway Audience、调用 Workload、JTI和有效期。Gateway的 readEvidenceForReconciliation只接受该 Grant,并严格读取指定历史 Evidence与 Availability,不要求它们仍为 Current;结果仅用于调查和提出 Resolution。它不能替代 readEvidence + validateAvailabilityCurrent,不能直接生成新的可结算 MeterEvent/ProviderCostFact。规范 Fact只能由 Metering按 Current契约追加或以新 Fact更正;Operational Observation、Case和 Admin Command都不能直接铸造规范 Fact或绕过 Billing结算命令。
状态
open
investigating
awaiting_external_evidence
resolution_proposed
approval_required
resolving
resolved
rejected状态迁移及字段集合由版本化契约控制。事实不足时保持 investigating 或 awaiting_external_evidence,不能为了关闭积压把未知改成失败或成功。
resolved | rejected 是 Case 状态集合中的终态,但当前两种正式 Billing Source 都采用来源 Refinement:维度级 late_settlement_dimension 只能由 Billing Owner 的 BillingCaseResolutionApplied@1 推进终态;Run 级 billing_finalization_run 只能由 FinalizationReconciliationResolutionApplied@1 推进终态。Operations/Admin 对两者都只能调查、补证、提案、审批或进入 resolving,不得本地写入 resolved | rejected | no_action。当前尚未定义平台承担、供应损失或核销的 Billing Disposition Command,否则 Operations 会与 Billing Case Link 形成双 Owner 分叉。
受控 Resolution
当前正式 Case 的写处置只通过 Admin Command Gateway 调用唯一 Billing Owner,包括:
late_settlement_dimension:基于 Current Exposure、Funding Decision、审批/JIT 与一次性 Grant 执行LateSettlementCommand@1;billing_finalization_run:基于 Current Case/Decision、调查引用与一次性 Grant 执行“重新读取当前 Owner 状态并重评”,不能携带替代金额、替代 Fact 或“忽略冲突”标志。
查询原 Gateway Execution Anchor、重新取回结果、重建投影、重放 Webhook 等仍可作为只读诊断或 Incident-scoped Domain Command 设计,但首期不属于上述 Case 联合,也不能借 Case 权限执行。未来开放必须分别具备明确 Target Owner、Expected Revision、幂等结果与 Audit 契约。
禁止直接编辑 Gateway Execution Anchor(Text Invocation 或 Media Task)、Run、AssetVersion、Reservation、Meter 或 Ledger 行。账务更正只追加新分录;未知提交不使用“重新执行看看”解决。Incident Timeline、Case Evidence/Resolution、Approval、Admin Command Result 与 Audit 必须由数据库约束为追加写入,普通服务角色不得 UPDATE/DELETE 历史证据。
契约与 Repository 测试必须分别覆盖两种 Billing Source 的本地终态命令同新 Open Exposure/Watermark、来源专属 Applied Fact 并发:本地命令恒定拒绝且零 Case 终态副作用;Owner Applied Fact 无论先于还是后于 Case Request/Create 都只产生一个可验证终态,且不能错关后继代次,晚到旧 Create/Ack 不得重开 Case。
每个命令必须具备稳定 adminCommandId、幂等键、目标 Revision、理由、Case/Incident、影响预览、所需职责、审批结果、执行结果和 Audit。高风险命令采用职责分离,发起者不能在不满足策略时自行审批。Admin Command BFF 不能自报 Actor:它必须以独立 Workload JWT,加 Workforce IdP/Identity Authorization 唯一签发或交换、绑定 Command Exchange 精确 Audience、boundWorkload、完整 Candidate Digest/Nonce、Session/Authn/MFA、一次性 jti 和有效期的 Actor Assertion 请求 Grant。Cookie、Assertion 和 Grant 都不能进入浏览器。
对账命令正式采用 Admin Command Gateway 定义的严格 ImpactPreview@1 与 AdminCommandCandidate@1,并冻结以下字段集关系:
ImpactPreview@1
= Command + Target/ExpectedRevision + CaseScope + RequestedScope
+ PolicyRevision + SourceFacts + AffectedResources + PredictedEffects
+ Risk/ApprovalRequirement + EvaluatedAt
AdminCommandCandidate@1
= AdminCommandId + Command + Actor + BoundWorkload
+ Target/ExpectedRevision + CaseScope/ExpectedCaseRevision + RequestedScope + Reason
+ IdempotencyKey + CommandRequestDigest
+ ImpactPreviewDigest + AggregateApprovalDecision/ApprovalSetDigest + JITAuthorization两个 Schema 及其所有嵌套类型都是封闭判别联合,拒绝未知字段、未知枚举、重复 Key、开放 Map、错误分支、隐式默认值和用 null 冒充缺失值。摘要固定使用 jcs-sha256-v1:严格 UTF-8 JSON 校验,拒绝无效 Unicode,以及任何 Key/字符串不是 NFC 或 NFC 归一化后发生 Key 冲突的输入;集合数组拒绝重复元素,再按 Contracts 注册键排序,执行 RFC 8785 JCS,最后对 canonical bytes 计算 SHA-256 小写十六进制。Preview Digest 排除自身摘要字段;Request Digest 排除自身摘要字段;Approval Set Digest 覆盖规范化的完整 Approval Records;完整 Candidate Digest 仅排除自身摘要字段,因此覆盖 Actor、Workload、Command、Target/Revision、Case、Scope、Reason、Idempotency Key 以及 Request、Preview、Approval 的版本、算法和摘要。Candidate Digest 是授权协议中的唯一完整 commandDigest,两者必须逐字节相等。所有实现必须通过 Contracts 仓库发布的同一组原始 JSON、canonical bytes 与摘要 Golden Vector;任何字段或摘要语义演进必须发布 @N+1,未知版本或算法一律拒绝。
Command Grant Exchange 必须使用 operation-first 恢复。CommandGrantExchangeResult@1.actorAssertionIssuer + actorAssertionJti 分别逐字节等于已验证 Actor Assertion JWT 的 iss + jti,commandGrantExchangeOperationId 由 Contracts 固定 Namespace 只按这两个 Result 字段确定性派生;Candidate 不进入 Operation ID,而与 Actor、boundWorkload、Exchange Request Digest/Nonce 一起成为首次 Issuance Result 的 insert-or-compare 正文。Core 先认证当前调用 Workload 并验证 Assertion 签名、Issuer/Audience 与 Workload Binding,再在 Assertion 时间/JTI检查前读取 Result:相同正文返回首次 Grant,即使响应丢失后 Assertion 已过期或 JTI 已消费;异正文返回 conflicting。只有 not_found 时,Core 才严格解析并重算所有摘要,锁定并验证 Case Revision、Aggregate Approval Decision/Set 与 JIT Current 状态,并在自己的事务中一次性消费 Assertion JTI、持久化内容可验证的 Issuance Result 与 SignedWorkforceCommandExecutionGrant@1、追加签发 Audit 后返回 Grant;唯一约束竞争必须回到首次 Result 读取分支。Grant 除标准 Claims 外必须令 sub = actorWorkforcePrincipalId,并绑定 boundWorkload、唯一 JTI、等于 Candidate Digest 的完整 commandDigest,以及 Command、Target Type/ID/Expected Revision、Case Scope/Revision、Approval Decision/Policy/Requirement/Set、JIT、Preview 和 Request 四元组;只绑定部分字段的 Grant 无效。Core 提交时刻是外部 Case/Approval/JIT 授权的线性化边界;Preview、Approval、JIT、Case 或 Request 任一变化都必须重新生成 Candidate 并换取 Grant。
Late Settlement Command 必须命中 Grant 的严格 commandSpecialization=late_settlement 分支,在完整 Request/Candidate Digest 之外逐项回显 Case Ref/Expected Revision/Generation/Identity Reservation完整四元组、Current Exposure Ref/Schema/Digest/Revision、Funding Decision Ref/Schema/Digest/JTI、Funding Policy Revision、正差额,以及 credits | entitlement{entitlementKey,unit} | money{currency} 经济身份。Finalization 重评命令必须命中 commandSpecialization=finalization_reconciliation_resolution,绑定 Run/Fence、Case/Generation/Revisions、Identity、Case Request Event、严格 Request、Resolution Decision、由该 Decision 四元组确定性派生的 Operation、唯一 reevaluate_current_owner_state Action 与调查引用集合;引用只说明授权依据,不证明 Owner 已修复。只有其余注册命令使用 none。Billing Domain 在消费 Grant JTI 前逐项比较 Request、Candidate、Specialization 与当前来源状态,任一跨 Case 代际、Revision、Bucket/Currency、Fence、Decision、Operation 或 Evidence 集合重放都拒绝。
Admin必须在调用领域命令前耐久追加带完整 Candidate Digest的 command.authorized;写入失败则不调用 Domain。Domain Service先认证当前调用 Workload、严格解码并验证 Grant签名/Issuer/Audience/Ref/Schema/Digest与完整 Candidate绑定,推导稳定 Domain Result Key;在检查 Grant当前时间窗、JTI未消费或 Target Revision之前必须读取已提交 Result。同 Key且完整 Candidate/Grant/JTI相等时直接返回首次结果,即使提交后响应丢失且重试已越过 Grant有效期;同 Key异 Candidate冲突。只有 Result不存在时才校验当前时间窗/JTI,逐项验证 Actor、Command、Target、Case、Approval/JIT、Preview、Request与 Candidate,并锁定 Domain自有 Target和业务不变量。通过后,在一个领域事务内以 Result Key与 Grant JTI唯一约束原子追加 Consumption、业务事实、命令结果事实和 Audit Outbox;任一必需写入失败则整体回滚,唯一键竞争后重新读取并比较 Result。签发前撤销必须阻止 Grant,签发后的 Case/Approval/JIT撤销不追溯这一次授权;若需硬停已签发动作,必须增加目标 Domain-owned Revocation Fence。
拒绝路径必须 fail closed:Grant Exchange拒绝追加 command.authorization_rejected,Domain拒绝通过 Audit Outbox追加 command.rejected,执行失败追加 command.failed。拒绝 Audit/Outbox无法耐久写入时仍保持零业务副作用,并只返回脱敏失败;禁止先执行再补记。契约负测至少覆盖错误 Audience/Workload、过期或撤销 Session、签发前 Case/Approval/JIT撤销、签发后撤销非追溯、Assertion JTI同正文重放与异 Candidate/Workload/Request 冲突、Core 已提交 Grant/Audit 后 HTTP 响应丢失并跨 Assertion 到期恢复、Grant JTI同 Candidate重放与异 Candidate偷换/并发消费、Domain提交后响应丢失跨 Grant到期重放、Exchange/Domain Result Key竞争、Actor与 Grant.sub不同、Target/Revision篡改、Case Revision切换、Approval/JIT摘要篡改、Preview内容或 Source/Policy篡改、Request Payload/Schema篡改、Candidate任一字段篡改、未知算法、重复 Key、未知字段、null/默认值、NFC冲突、集合数组未规范排序,以及 command.authorized、Consumption或 Domain Audit Outbox写入失败;每项都必须断言至多一个业务效果且存在相应 Audit。
权限与内容访问
- SRE 可以缓解运行和部署影响,但不自动获得退款或客户内容权限;
- Billing Reconciler 可以查看经济事实并提出账务 Resolution,但不读取 Prompt;
- Supply Operator 可以检查网关/Provider 证据,但不能改客户钱包;
- Support 可以查看授权范围内的客户影响摘要,但不能执行平台级命令;
- Auditor 只读查看事实、审批和时间线。
确需客户内容时必须从具体 Case/Incident 向 Core 申请最小、限时、只读的 SensitiveDebugGrant;只有 Core Authorization/Policy 模块可以签发,Admin BFF、IdP 与人工配置不能自行生成。它使用独立唯一 jti,并以 baseQueryGrantId + baseQueryGrantJti 引用父 Grant;Issuer/Audience、Workload Binding、Session/Policy/MFA、时间与撤销约束必须兼容,实际权限取父 Grant、活跃 Case 与审批范围交集。父子任一撤销/过期即失效,相同 JTI、错误父链或 Workload 错配均拒绝;浏览器不能取得或转授。每次内容释放仍须先追加 query.authorized,并在响应字节离开 Core 前追加 query.released | query.not_found | query.inconclusive | query.failed;任一必需 Audit 失败都 fail closed,不返回敏感内容。
与发布和可观测性的关系
Deployment、配置、Offering、路由与 Secret 变更以独立 Change Event 进入时间线。时间相邻只表示相关性,Root Cause 需要证据和人工确认。Metrics/Logs/Trace 可以触发或辅助调查,但 Incident、Case、Admin Command 与 Audit 本身是追加式治理事实。Gateway、Asset、Metering、Settlement 或 Webhook 尚未接入时只显示“未接入/未知”,不能从相邻事件、Observation 或 Telemetry 推断完成状态。
验收门禁
- Alert、Incident、Owner-local Conflict/Blocked Work 与正式 Reconciliation Case 使用不同对象、状态和 Owner;Case
sourceScope只接受两种 Billing 分支。 - Incident Timeline 与 Case Resolution 追加写入,不能覆盖历史判断。
- Incident 关闭前生成受影响 Run、Asset、Billing 与 Webhook 扫描结果。
-
unknown事实不会自动重试、切换 Provider 或强制终态。 - 所有写动作通过受控 Domain Command,具备幂等、影响预览、审批和 Audit。
-
ImpactPreview@1、AdminCommandCandidate@1、Request 与 Approval 均为严格 Schema,并按jcs-sha256-v1跨服务复算,通过统一 Golden Vector。 - Grant 的
sub等于 Candidate Actor 并绑定完整 Candidate Digest;Domain 逐项复算和校验 Actor、Target/Revision、Case、Approval、Preview、Request 与 Candidate。 -
command.authorized在调用前耐久提交;Domain Service 将业务事实、命令结果事实与 Audit Outbox 原子提交,任一 Audit 写入失败都 fail closed 且无业务副作用。 - 篡改、重放、过期/撤销、Schema/JCS 异常和 Audit 失败负测均证明无业务事实并产生脱敏拒绝 Audit。
- 账务更正只追加 Ledger Entry,不直接改余额或历史记录。
- Workforce 权限按职责分离,Grant 仅由 Core 签发;Sensitive Debug 与基础 Query Grant、活跃 Case/审批取交集并绑定 Workload,每次内容释放执行两阶段 Audit fail-closed。
- 普通页面和通知不包含 Prompt、媒体、Secret、完整 Provider Payload 或签名 URL。