ADR-028:Contracts First 与 Core 准入纵切
OceanWay 十四仓进入实施后的首个跨仓契约、模块边界和 PostgreSQL 事务纵切决策
ADR-028:Contracts First 与 Core 准入纵切
| 属性 | 内容 |
|---|---|
| 状态 | 已采用,首个实现基线已通过独立 CI 验收 |
| 决策日期 | 2026-09-03 |
| 适用范围 | oceanway-contracts、oceanway-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. 模型公开面在导入边界隔离
web、api、internal 是发布目标,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,并携带 eventId、eventType、schemaVersion、occurredAt、producer、actorPrincipalId、Organization、Workspace、可选 Project、correlationId、可选 operationId、causationId、traceId 和 payload。不得恢复含义模糊的 subjectId 或缩写 orgId。
5. 本纵切明确不执行外部副作用
当前基线只完成准入与预占,不调用 Text/Media Gateway、Provider、Agent、MCP 或 Asset 写入,也不开放客户写 API。这样可以先验证租户、授权、模型发布面、账务与幂等不变量,而不制造未知提交或重复生成。
实施快照
@oceanway-ai/contracts@0.1.0已作为首个固定版本发布;包根只暴露公共安全契约,公共模型目录使用public/model-catalog,执行、模型控制和网关契约使用internal/*子路径。oceanway-core已在 Commit77f336a形成 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 时只允许两种模式:
- 旧入口代理到 Core,Core 是唯一 Writer;
- 旧实现继续写,Core 只做不产生事实和费用的 Shadow Read/校验。
禁止旧、新实现同时创建 Run、Reservation、Ledger Entry、Asset 或 Provider Task。回滚只改变后续新命令的路由;已经受理的 Operation 由原 Owner 处理到终态或进入 Reconciliation。
验证门禁
本决策完成需要同时通过:
- Contracts 的运行时校验、TypeScript Node/Web 消费测试、JSON Schema 漂移检查、打包检查和依赖审计;
- 公共模型导出无法导入内部 Execution Target 的负向测试;
- Core 模块聚合唯一所有权测试;
- 专用
_testPostgreSQL 上的真实事务测试,且关闭文件级并行; - 并发同幂等键只产生一个 Run 和一份 Reservation;
- 相同幂等键不同语义被拒绝;
- 跨租户、无授权、余额不足或中途约束失败时没有部分写入;
- Manifest 数据库级不可修改;
- Run 读取受 Organization、Workspace 和可选 Project 限制;
- Outbox 包含完整 Operation/Correlation/Trace 关联,消费者回执可幂等去重;
- 包、Tag、Changelog、Commit 和 CI 结果可追溯。
后续顺序
本纵切完成后按下列顺序推进,不同时展开全部产品迁移:
- API Edge 适配:终止 DeveloperCredential,构造受信 Request Context,调用 Core;API Edge 不保存第二份 Run、余额或模型事实。
- Outbox 与运维读模型:发布事件,建立 Operation/Run/Reservation 查询投影,为 Admin 的只读 Run Explorer 提供第一条真实数据链。
- Metering 与结算:增加不可变 Usage/Provider Cost Fact,以及成功结算、失败释放和不确定状态对账。
- Text Gateway canary:使用固定 Gateway Contract 接通一条文本执行链;Media 按同级接口随后接入,不做同 Attempt 双投。
- Asset Output:将成功结果登记为 Asset Version 并保留 Run/Attempt/Blob lineage。
- Surface 迁移:先迁 Console
/ai的一个最小调用链,再迁 Studio;旧实现逐命令切换 Writer,始终禁止业务双写。
Canvas Runtime、完整 Agent/MCP 组合、Drama、Commerce 和 FDE 私有交付在上述链路通过后进入各自阶段。
影响
收益是所有平台从第一条调用开始就共享同一租户、付款方、模型发布面、Run 和追踪语义,且 Admin 能基于真实事件建设诊断能力。代价是首个可见页面功能会稍晚,但可以避免后续为多套钱包、模型目录和任务表做高风险合并。