OceanWayOceanWay
架构决策记录

ADR-028:Contracts First 与 Core 准入纵切

OceanWay 十四仓进入实施后的首个跨仓契约、模块边界和 PostgreSQL 事务纵切决策

ADR-028:Contracts First 与 Core 准入纵切

属性内容
状态已采用,首个实现基线已通过独立 CI 验收
决策日期2026-09-03
适用范围oceanway-contractsoceanway-core 以及随后接入的 API Edge、Surface 与 Gateway
前置决策OW-ADR-006、011–015、019、022–027

背景

十四仓边界建立后,最大的风险不是仓库数量,而是各产品并行开发出不同的身份、模型、Run、资产和钱包语义。如果先迁页面、再补共享契约,Studio、Console、API Edge、Drama 和 Commerce 会各自形成临时事实源;后续即使 UI 看起来统一,调度、计费和追溯仍无法闭环。

因此下一阶段不以“先完成某个页面”为目标,而以一条可执行、可回滚、可诊断的最小业务纵切作为系统骨架。

决策

1. Contracts 必须先于运行仓发布

oceanway-contracts 是跨仓 wire contract 的唯一来源。运行仓只能依赖固定 Release 版本,不允许使用 Git 分支、本地相对路径、复制 TypeScript 文件或跨仓源码导入。

首发基线至少包含:

  • opaque/branded 稳定 ID;
  • Request Context、Tenant Context 和 Developer Access Context;
  • credits、entitlement 与真实 ISO money 的判别联合;
  • Asset Version 引用;
  • Model Offering 公共投影与 Model Execution Target 内部投影;
  • Agent Revision 与 MCP Grant 引用;
  • Run Admission、Execution Manifest 和 Run 事件;
  • Text/Media Gateway 的提交、观测、Usage、Cost 与安全错误;
  • Canonical Event Envelope;
  • 由运行时 Schema 自动生成的 JSON Schema 制品。

所有 wire object 首版采用严格字段校验。因此给既有对象增加字段也按不兼容变更处理;兼容性扩展优先新增独立 Schema 或新的事件版本。不能一边拒绝未知字段,一边把“新增可选字段”声明为 minor 兼容。

1.1 Contracts 依赖必须绑定私有 Registry 与同一发布制品

  • GitHub Packages 中的 Contracts 包只向明确授权的消费仓开放;oceanway-core 的 GitHub Actions 获得 Read 权限。
  • Core 使用 pnpm 的 gh: registry-qualified dependency 锁定 @oceanway-ai/contracts@0.1.0,不能因 Registry 配置异常解析到同名公共包。
  • pnpm 的 minimumReleaseAge 例外只允许精确版本 @oceanway-ai/contracts@0.1.0,不对整个组织 Scope 或浮动版本放宽供应链门禁。
  • Contracts Release Workflow 先生成唯一 tgz,再用同一个文件发布 GitHub Package 和 GitHub Release;不得分别重建两个制品。
  • Contracts 新增制品比对 Workflow,按版本下载 Registry 与 Release tgz、校验摘要并递归比较解包内容。该 Workflow 是后续发布验收门禁,不把“Workflow 已存在”误写成所有历史制品均已自动验证。

2. 模型公开面在导入边界隔离

webapiinternal 是发布目标,unpublished 是发布状态而不是第四个 Surface。公共 Model Offering 与内部 Model Execution Target 使用不同导出入口:

  • Site、Developer Center、Console 和产品 UI 只能导入公共模型目录契约;
  • Core 与 Gateway 可以导入内部执行目标契约;
  • codex-auto-review、规划模型、自动评测模型和 Provider 绑定没有对应公开 Offering 时,无法进入网页或公共 API 目录。

这项限制必须由包导出和 CI 验证,不能只依赖页面过滤。

3. Core 保持模块化单体

首期 Core 在一个仓库和一个 PostgreSQL 事务边界内实现以下模块:Identity、Tenancy、Authorization、Developer Access、Resource Registry、Asset、Execution、Metering、Billing、Agent、MCP、Model Control、Operations 与 Audit。

模块拥有各自的聚合、Repository、表和事件。Agent 只拥有 Agent Definition、Revision 与 Deployment;Run、Step、Attempt、Execution Manifest 和 Output 只由 Execution 拥有。Provider Credential Version、物理 Channel/Supply 和 Provider Task 保持 Gateway 私有。

Canvas Runtime 延后到 Phase 4;当前 Contracts/Core 基线不提前冻结 Canvas 节点协议。

4. 首个纵切采用 Run Admission

首个可执行纵切固定为 API Surface 的 Run Admission:

上述动作、幂等结果与 Outbox 必须在一个 PostgreSQL 事务中提交。projectId 可选,billingAccountId 必填并由服务端验证;首版只接受 credits,不能把积分伪装成货币,也不能把 credits、entitlement 和 money 相加。

Execution Manifest 冻结创建时的 Actor、Execution Principal、租户、付款账户、认证上下文、授权决策、Offering Revision、Execution Target、Reservation、pricingSnapshotId、关联 ID 和数据分类。Developer Access 链由准入事务校验,不在 Manifest 中复制另一份可变资源投影。Manifest 创建后不可修改;执行变化通过 Run/Step/Attempt 和事件表达。

Canonical Event Envelope 固定使用 aggregateId 与正整数 aggregateRevision,并携带 eventIdeventTypeschemaVersionoccurredAtproduceractorPrincipalId、Organization、Workspace、可选 Project、correlationId、可选 operationIdcausationIdtraceId 和 payload。不得恢复含义模糊的 subjectId 或缩写 orgId

5. 本纵切明确不执行外部副作用

当前基线只完成准入与预占,不调用 Text/Media Gateway、Provider、Agent、MCP 或 Asset 写入,也不开放客户写 API。这样可以先验证租户、授权、模型发布面、账务与幂等不变量,而不制造未知提交或重复生成。

实施快照

  • @oceanway-ai/contracts@0.1.0 已作为首个固定版本发布;包根只暴露公共安全契约,公共模型目录使用 public/model-catalog,执行、模型控制和网关契约使用 internal/* 子路径。
  • oceanway-core 已在 Commit 77f336a 形成 PostgreSQL Run Admission 功能基线,包含模块所有权清单、数据库迁移、单事务准入、数据库不变量与真实 PostgreSQL 集成测试;本地已通过 5 项单元测试、14 项专用 PostgreSQL 集成测试、类型检查、构建、依赖审计以及 /livez/readyz 启动验证。
  • Core 随后的 4d66b6c 已锁定私有 Contracts 依赖,并由 独立 CI Run 33745852471 通过格式、类型、单元测试、构建、专用 PostgreSQL 集成测试和生产依赖审计。
  • 首个 Contracts-first/Core Run Admission 实现基线据此转为“已验收”;这不代表生产部署、Public API、Outbox 发布、Gateway 执行或跨仓联调已经完成。

Writer 与迁移规则

在某类命令切换前,oceanway-vozeb 仍可以是旧事实 Owner。接入新 Core 时只允许两种模式:

  1. 旧入口代理到 Core,Core 是唯一 Writer;
  2. 旧实现继续写,Core 只做不产生事实和费用的 Shadow Read/校验。

禁止旧、新实现同时创建 Run、Reservation、Ledger Entry、Asset 或 Provider Task。回滚只改变后续新命令的路由;已经受理的 Operation 由原 Owner 处理到终态或进入 Reconciliation。

验证门禁

本决策完成需要同时通过:

  • Contracts 的运行时校验、TypeScript Node/Web 消费测试、JSON Schema 漂移检查、打包检查和依赖审计;
  • 公共模型导出无法导入内部 Execution Target 的负向测试;
  • Core 模块聚合唯一所有权测试;
  • 专用 _test PostgreSQL 上的真实事务测试,且关闭文件级并行;
  • 并发同幂等键只产生一个 Run 和一份 Reservation;
  • 相同幂等键不同语义被拒绝;
  • 跨租户、无授权、余额不足或中途约束失败时没有部分写入;
  • Manifest 数据库级不可修改;
  • Run 读取受 Organization、Workspace 和可选 Project 限制;
  • Outbox 包含完整 Operation/Correlation/Trace 关联,消费者回执可幂等去重;
  • 包、Tag、Changelog、Commit 和 CI 结果可追溯。

后续顺序

本纵切完成后按下列顺序推进,不同时展开全部产品迁移:

  1. API Edge 适配:终止 DeveloperCredential,构造受信 Request Context,调用 Core;API Edge 不保存第二份 Run、余额或模型事实。
  2. Outbox 与运维读模型:发布事件,建立 Operation/Run/Reservation 查询投影,为 Admin 的只读 Run Explorer 提供第一条真实数据链。
  3. Metering 与结算:增加不可变 Usage/Provider Cost Fact,以及成功结算、失败释放和不确定状态对账。
  4. Text Gateway canary:使用固定 Gateway Contract 接通一条文本执行链;Media 按同级接口随后接入,不做同 Attempt 双投。
  5. Asset Output:将成功结果登记为 Asset Version 并保留 Run/Attempt/Blob lineage。
  6. Surface 迁移:先迁 Console /ai 的一个最小调用链,再迁 Studio;旧实现逐命令切换 Writer,始终禁止业务双写。

Canvas Runtime、完整 Agent/MCP 组合、Drama、Commerce 和 FDE 私有交付在上述链路通过后进入各自阶段。

影响

收益是所有平台从第一条调用开始就共享同一租户、付款方、模型发布面、Run 和追踪语义,且 Admin 能基于真实事件建设诊断能力。代价是首个可见页面功能会稍晚,但可以避免后续为多套钱包、模型目录和任务表做高风险合并。

On this page