跨分片移交:Part of objectstack-ai/cloud#930(cloud 分片 PM 转入 —— 落点是 packages/spec,属契约面,按分片规则归主 backlog)。不阻塞 cloud#930,那边已用标准目录绕开。
缺口
ADR-0112 D3 把 error.code 定为两层词表:闭合的 StandardErrorCode,加上 ERROR_CODE_LEDGER 里按所属包登记的扩展码;ApiErrorSchema.code 校验的是二者的并集,未登记的 code 解析失败(packages/spec/src/api/contract.zod.ts:19)。
但账本今天登记的包全部是 framework 包:
@objectstack/rest, runtime, service-storage, service-i18n, plugin-auth, plugin-sharing,
metadata-protocol, metadata-core, objectql, core, hono, service-messaging, trigger-api,
cloud-connection, service-settings, service-automation, service-analytics,
service-datasource, plugin-audit, plugin-approvals, plugin-security, spec, driver-mongodb
objectstack-ai/cloud 仓的服务包(@objectstack/service-ai、service-cloud、service-tenant、objectos-runtime、security-enterprise、organizations …)一个都不在。
后果
一个 cloud 服务需要 service-specific code 时,只有两条路:
- 用标准目录——多数情况下这是对的,ADR-0112 自己就写着「If the condition is generic … use the standard catalog instead of registering a synonym」。cloud#930 正是这么解决的(
ai_quota_exhausted → QUOTA_EXCEEDED)。
- 发一个未登记的 code——它会通过 cloud 侧只检查「
error.code 是字符串」的信封守卫,却无法通过 ApiErrorSchema.safeParse。也就是说:cloud 的一致性套件想把词表钉死时,唯一能钉的就是"别用自己的词"。
真正缺的是第三种情况:cloud 有一个确实不 generic 的条件(举例:EE 许可类拒绝、控制面环境供给的特定失败态),标准目录里没有对应语义,而它没有任何合法途径把这个 code 登记进权威集合 —— 跨仓 PR 到 packages/spec 是唯一出口,而 cloud 仓的开发流程里没有这一步。
为什么值得单独裁决
这不是「给账本加几行」的活,是一个协议归属问题:ERROR_CODE_LEDGER 要不要接纳非 framework(闭源 / 商业)包的条目?两个方向都自洽:
- 接纳:账本成为跨仓的单一权威词表,cloud 的 code 同样被 Zod 兜住;代价是开源 spec 里出现闭源包名,且每次 cloud 加 code 都要一次 spec PR + pin bump。
- 不接纳:cloud 侧永远只用
StandardErrorCode;代价是 cloud 无法表达真正专有的语义,长期会有人绕过校验发明 code(今天已经在发生 —— auth-proxy-plugin.ts 的 lowercase 码就是这么长出来的,且被 cloud#944 ratchet 收编)。第三条路是给 cloud 自己一份账本并让 ApiErrorSchema 可扩展校验,但那要改 spec 的校验形状。
按 ADR-0112 自己的「no silent fourth state」原则,现状恰恰是那个静默的第四态:声明了闭合集合,却有一整个仓的产出者无法进入这个集合。
建议
先做判定(接纳 / 不接纳 / 可扩展校验),再谈实现。若判定为"不接纳",建议至少在 error-code-ledger.zod.ts 的文件头注释里写明「本账本只登记 framework 包;其它仓一律使用标准目录」—— 让约束变成显式的,而不是靠读列表推断。
关联
- 来源:objectstack-ai/cloud#930(service-ai 十一处裸串错误方言的信封改造,实施中撞到此缺口)
- ADR-0112 D3、ADR-0049、ADR-0078
- cloud#944(cloud 侧
/api/v1 方言 ratchet)
未指派 —— 记录的 finding,谁开工谁认领。
跨分片移交:
Part of objectstack-ai/cloud#930(cloud 分片 PM 转入 —— 落点是packages/spec,属契约面,按分片规则归主 backlog)。不阻塞 cloud#930,那边已用标准目录绕开。缺口
ADR-0112 D3 把
error.code定为两层词表:闭合的StandardErrorCode,加上ERROR_CODE_LEDGER里按所属包登记的扩展码;ApiErrorSchema.code校验的是二者的并集,未登记的 code 解析失败(packages/spec/src/api/contract.zod.ts:19)。但账本今天登记的包全部是 framework 包:
objectstack-ai/cloud仓的服务包(@objectstack/service-ai、service-cloud、service-tenant、objectos-runtime、security-enterprise、organizations…)一个都不在。后果
一个 cloud 服务需要 service-specific code 时,只有两条路:
ai_quota_exhausted→QUOTA_EXCEEDED)。error.code是字符串」的信封守卫,却无法通过ApiErrorSchema.safeParse。也就是说:cloud 的一致性套件想把词表钉死时,唯一能钉的就是"别用自己的词"。真正缺的是第三种情况:cloud 有一个确实不 generic 的条件(举例:EE 许可类拒绝、控制面环境供给的特定失败态),标准目录里没有对应语义,而它没有任何合法途径把这个 code 登记进权威集合 —— 跨仓 PR 到
packages/spec是唯一出口,而 cloud 仓的开发流程里没有这一步。为什么值得单独裁决
这不是「给账本加几行」的活,是一个协议归属问题:
ERROR_CODE_LEDGER要不要接纳非 framework(闭源 / 商业)包的条目?两个方向都自洽:StandardErrorCode;代价是 cloud 无法表达真正专有的语义,长期会有人绕过校验发明 code(今天已经在发生 ——auth-proxy-plugin.ts的 lowercase 码就是这么长出来的,且被 cloud#944 ratchet 收编)。第三条路是给 cloud 自己一份账本并让ApiErrorSchema可扩展校验,但那要改 spec 的校验形状。按 ADR-0112 自己的「no silent fourth state」原则,现状恰恰是那个静默的第四态:声明了闭合集合,却有一整个仓的产出者无法进入这个集合。
建议
先做判定(接纳 / 不接纳 / 可扩展校验),再谈实现。若判定为"不接纳",建议至少在
error-code-ledger.zod.ts的文件头注释里写明「本账本只登记 framework 包;其它仓一律使用标准目录」—— 让约束变成显式的,而不是靠读列表推断。关联
/api/v1方言 ratchet)未指派 —— 记录的 finding,谁开工谁认领。