You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
用标准目录——多数情况下这是对的,ADR-0112 自己就写着「If the condition is generic … use the standard catalog instead of registering a synonym」。cloud#930 正是这么解决的(ai_quota_exhausted → QUOTA_EXCEEDED)。
Scope = exactly the three converged objectstack-side items from the ruling comment above: (1) error-code-ledger.zod.ts header constraint made explicit (framework packages only; downstream repos maintain their own ledgers and compose validation); (2) contract.zod.ts:19 description → "StandardErrorCode ∪ 服务方登记的账本"; (3) optional non-blocking makeApiErrorSchema(extraCodes) factory export. The cloud-side ledger/migration is the cloud shard PM's dispatch under cloud#944 — NOT in scope here.
Concurrency note: several spec PRs in flight share generated artifacts; serial landing via the PM relay.
PM bookkeeping: pm:dispatched → pm:queue. The dispatched os-dev was stopped externally (mass session-level cancellation, 15:21Z patrol census) before pushing any code — no branch, zero work product. Claim released; card returns to the queue intact. The federated-ledger ruling and the three-item objectstack-side scope stand unchanged; re-dispatch awaits the maintainer's word.
RE-CLAIM — PM seat (session session_01PiRUoQkTSBBmpyXBY3cVn2), re-dispatched as a mode:cloud card under the maintainer's re-acceleration (2026-08-09 chat). Branch claude/issue-4805-federated-error-ledger. Scope stays exactly the three converged objectstack-side items of the federated ruling (13:33Z claim comment); the cloud-side ledger remains the cloud seat's work.
{
"issue": 4805,
"status": "delivered",
"branch": "claude/issue-4805-federated-error-ledger",
"head": "d3a3a9e",
"pr": "https://github.com/objectstack-ai/objectstack/pull/7110",
"pr_state": "draft (not queued — PM lands serially)",
"worktree": "../objectstack-issue-4805",
"scope": "exactly the three converged objectstack-side items of the federated ERROR_CODE_LEDGER ruling (2026-08-03, re-confirmed 2026-08-09); cloud-side ledger untouched",
"items": [
{
"n": 1,
"landing_site": "packages/spec/src/api/error-code-ledger.zod.ts (file header)",
"status": "done",
"before": "framework-only was a property of the list — inferable by scanning 23 package names, stated nowhere",
"after": "stated rule 'Scope: THIS ledger registers framework packages only (#4805)': downstream repos keep their own ledger and compose validation (envelopeViolations shape + code ∈ StandardErrorCode ∪ own ledger / one makeApiErrorSchema parse); records why federated (commercial vocabulary vs Apache-2.0 spec; per-code cross-repo friction breeds semantic reuse); corollary that an owner key for a non-repo package is out of scope by construction"
},
{
"n": 2,
"landing_site": "packages/spec/src/api/contract.zod.ts (ApiErrorSchema.code description, was :19)",
"status": "done",
"before": "'Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ ERROR_CODE_LEDGER)'",
"after": "'Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)'; JSDoc names the federation and points at makeApiErrorSchema. Description only — parsed vocabulary unchanged"
},
{
"n": 3,
"landing_site": "packages/spec/src/api/contract.zod.ts (new export)",
"status": "done (shipped — did not fight)",
"after": "makeApiErrorSchema(extraCodes): envelope with StandardErrorCode ∪ extraCodes as the code vocabulary; shape reused from ApiErrorSchema via .extend rather than restated; ERROR_CODE_LEDGER entries deliberately NOT folded in (a relaying service passes [...REGISTERED_ERROR_CODES, ...MY_CODES] explicitly); ApiErrorSchema additive-unchanged"
}
],
"not_in_scope_confirmed": [
"no ERROR_CODE_LEDGER entry added/removed/changed",
"no new error codes",
"no cloud-side work (cloud#944 remains the cloud seat's)",
"no content/docs/releases/ edit"
],
"files": [
{"path": "packages/spec/src/api/error-code-ledger.zod.ts", "stat": "+42/-1 (docs only)"},
{"path": "packages/spec/src/api/contract.zod.ts", "stat": "+51/-5"},
{"path": "packages/spec/src/api/contract.test.ts", "stat": "+62/-0 (4 pins)"},
{"path": "packages/spec/api-surface/api.json", "stat": "+1 (gen:api-surface)"},
{"path": "packages/spec/export-origins/api.json", "stat": "+1 (gen:export-origins)"},
{"path": "content/docs/references/api/error-code-ledger.mdx", "stat": "+42/-1 (gen:schema && gen:docs)"},
{"path": "content/docs/references/api/contract.mdx", "stat": "+1/-1 (gen:schema && gen:docs)"},
{"path": ".changeset/error-code-ledger-federation-contract.md", "stat": "new — @objectstack/spec minor"}
],
"reverse_verification": {
"direction_predicted_first": true,
"experiment": "revert the factory union to [...StandardErrorCode.options] with extraCodes ignored",
"predicted": "the 2 pins exercising an extra code flip red; the standard-catalog pin and the unregistered-rejection pin stay green",
"measured": "Tests 2 failed | 48 passed (50) — exactly 'accepts an extra code ONLY through the factory' and 'reuses the base envelope shape'; 50 passed after restore",
"apierrorschema_unchanged": "its own tests untouched; contract + error-code-ledger + errors + envelope-violations = 99 passed unchanged. Rejection pins assert Zod issue path ['code'] and code 'invalid_value'"
},
"gates": {
"lint (eslint --no-inline-config)": "pass",
"check:nul-bytes": "pass",
"check:error-code-casing": "pass (0 lowercase in 3348 files)",
"tsc --noEmit (@objectstack/spec)": "pass",
"typecheck (src + scripts + tests)": "pass",
"check:generated --reconcile-only": "pass (20 check: + 14 gen: classified)",
"check:export-origins": "pass (4992 exports / 16 entry points; snapshot regenerated + committed)",
"check:api-surface": "pass (snapshot regenerated + committed)",
"check:authorable-surface": "pass",
"check:docs": "pass (references deltas committed)",
"check:skill-docs / check:spec-changes / check:upgrade-guide": "pass",
"check:exported-any / check:dual-source-exports": "pass",
"check:spec-parsed-alias (ADR-0122)": "pass (no new z.infer alias)",
"check-adr-0087-registration --base origin/main": "pass — nothing owed (no declared-breaking changeset)",
"spec test suite": "355 files / 9275 tests passed"
},
"findings": [
"Design call worth review: the factory unions StandardErrorCode ∪ extraCodes, NOT ErrorCode ∪ extraCodes — matching the ruling's own formula rather than quietly widening it. Consequence: a downstream ledger does not silently inherit ~200 framework codes it never audited; a service that relays framework errors opts in explicitly via [...REGISTERED_ERROR_CODES, ...MY_CODES], documented on the factory.",
"gen:api-surface requires the built dist/*.d.ts — it fails with 'Could not resolve module symbol' on a clean worktree until `pnpm --filter @objectstack/spec build` has run. Relevant to anyone repeating the #7103 snapshot lesson.",
"Left alone deliberately: prose in packages/types/src/response-envelope.ts and packages/metadata-protocol/src/protocol.ts still reads 'StandardErrorCode ∪ ERROR_CODE_LEDGER'. Both describe the framework side, where the union is still exactly right; ErrorCode itself is unchanged."
],
"next_owner": "PM (serial landing). No auto-merge, no queue enabled."
}
Activity
xuyushun441-sys commentedon Aug 3, 2026
升级:这条已经在卡住具体工作了,不再只是结构观察
cloud 分片 PM(会话
session_01RnouiyjNYm6KGMuCe3YLWT)补充。本 issue 原文是从 cloud#930 的实施中抽象出来的缺口;今天 cloud#944 的实施把它变成了一个有明确成本的阻塞,附实测数字与两轴分析,供裁定。实测规模
objectstack-ai/cloud的packages/service-cloud里,failWithCode()使用 36 个自造 code(覆盖 25 个调用点),均不在StandardErrorCode ∪ ERROR_CODE_LEDGER中。后果是约 150 个响应体同时不过ApiErrorSchema和BaseResponseSchema。用 pinad047d2的 spec 实测:注意第一列与第二列的组合:
envelopeViolations=0却ApiErrorSchemaFAIL —— 这正是 cloud#948 当初能带病绿灯的缝隙,只做形状检查看不见词表违规。为什么不能靠 cloud 单方面「映射到标准目录」了事
我最初给 cloud 的 dev 下的指令就是「映射到最近的
StandardErrorCode成员」。dev 拒绝执行并给了理由,我采纳并撤销了那条指令:那 36 个里相当一部分确实不 generic。
PRODUCTION_ENV_LIMIT/DEV_ENV_DOMAIN_LOCKED/CONTACT_SALES_PLAN/BILLING_CHECKOUT_REQUIRED在产品上是四句不同的话(升级套餐 / 换环境类型 / 联系销售 / 去结账),塌缩进PERMISSION_DENIED或QUOTA_EXCEEDED是不可逆的语义丢失 —— 信息一旦不在 wire 上消费者无法恢复,而 objectui 的付费引导分支正是按这些 code 分流的。而 ADR-0112 D3 的账本注释本身就写着:generic 的用标准目录,非 generic 的登记进账本。按 ADR 自己的规则,这批 code 的正确归宿是账本 —— 只是账本今天没有 cloud 包的位置。
两轴评估
StandardErrorCodesatisfies readonly ErrorCode[]编译期挡编造、ApiErrorSchema.safeParse测试期二次挡 —— 就是 cloud#930envelope.ts拿到的那个性质code用标准目录,专有码塞error.details.reasondetails里另开一套未声明小词表 —— 换个地方长第七种方言details: z.unknown()完全不校验,AI 写的路由照样能编造details.reason建议 B。 若裁定为「账本不接纳非 framework 包」,那 A 成为唯一合法路径,但届时应连同「哪些语义允许丢失」一起裁定,而不是让 cloud 侧逐个映射 —— 那等于把一次产品决策拆成 36 次无人复核的小决定。
裁定之前 cloud 侧已做的止损
cloud PR #1044(已入合并队列)把这 36 个 code 从源码实测冻成 ratchet:新造一个就红。漂移不会在等待期间继续长,但也无法收口。
需要裁定的就一句话
ERROR_CODE_LEDGER是否接纳非 framework(cloud / 闭源商业)包的条目? 接纳 ⇒ cloud 提 spec 侧注册 PR;不接纳 ⇒ 请一并说明允许丢失哪些语义,并建议在error-code-ledger.zod.ts文件头把「本账本只登记 framework 包」写成显式约束,而不是靠读列表推断。关联:cloud#944、cloud#930、cloud#1045、cloud#1046。
Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
维护者方向 + PM 修正建议:联邦式账本 —— cloud 条目在 cloud 仓维护,spec 只管机制与标准目录
维护者对本单表态:「cloud 条目应该在 cloud 项目中维护」。PM 复核后同意,并撤回此前推荐的 B(cloud 条目登记进 spec 的
ERROR_CODE_LEDGER)。B 有两个我先前低估的缺陷:packages/spec是 Apache-2.0,而这 36 个 code 里最有价值的正是商业语义(CONTACT_SALES_PLAN/BILLING_CHECKOUT_REQUIRED/PRODUCTION_ENV_LIMIT)。登记进去等于让开源 spec 枚举闭源产品的计费状态,还枚举自己发行版里不存在的包(@objectstack/service-cloud是 private/UNLICENSED)。与 cloud#1027 刚划的BillingPlan∉TenantPlan是同一条产品归属线。机制(关键:cloud 侧零 spec 依赖即可起步)
ApiErrorSchema.safeParse在语义上等于两个正交断言,可拆开验,不必复制信封实现:envelopeViolations(body) === []—— 形状,用平台自己的判定;code ∈ StandardErrorCode ∪ CLOUD_ERROR_CODE_LEDGER—— 词表,后者为 cloud 仓内单文件账本。防 AI 写错的性质完整保留:账本
as const satisfies+failWithCode参数收窄为闭合联合(编译期 TS2322,cloud#930envelope.ts已验证该形状);一致性套件两步校验(测试期);复刻 spec 账本的三条 lint 测试(SCREAMING 格式 / 非StandardErrorCode同义词 / 按包登记);cloud#1044 的 36-code ratchet 删除,临时冻结变真闸门。部署级 wire 联合仍是闭合可校验的——ADR-0112「no silent fourth state」要的是这个,不是所有条目物理同住一个文件。迁移纪律:36 个不全数入账。入账前逐条同义词审计,generic 的仍归标准目录(
HOSTNAME_TAKEN要认真问是不是DUPLICATE_VALUE;CONTACT_SALES_PLAN这类产品流程码显然留),结果逐条记录。代价如实记录:注册表一处变两处,跨仓工具要 grep 两份才是全集;objectui 跨仓读这些 code 继续用字面量匹配,cloud 侧加「objectui 依赖的 code 必须在账本内」的成员测试钉住。
本仓(objectstack)需要的动作 —— 本 issue 收敛为这个小范围
error-code-ledger.zod.ts文件头:把「本账本只登记 framework 包;下游产品仓维护自己的账本」写成显式约束,不再靠读包名列表推断;contract.zod.ts:19的描述句同步修订为「StandardErrorCode∪ 服务方登记的账本」;makeApiErrorSchema(extraCodes)工厂,让下游单次 parse 替代两步断言。cloud 侧的账本建立与 36 码迁移由 cloud 分片 PM 派发(cloud#944 名下),cloud#1045 / cloud#1046 排在其后。
等待维护者对本条最终确认后执行。
Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
✅ 维护者裁定:同意联邦式账本 —— cloud 条目在 cloud 仓维护
裁定原文:「4805 同意」(对上一条分析的方案确认)。据此:
本 issue 的剩余范围收敛为 objectstack 侧三件小事(供主 backlog PM 派发)
packages/spec/src/api/error-code-ledger.zod.ts文件头:把「本账本只登记 framework 包;下游产品仓维护自己的账本并自行组合校验」写成显式约束;packages/spec/src/api/contract.zod.ts:19的描述句修订为「StandardErrorCode∪ 服务方登记的账本」;makeApiErrorSchema(extraCodes)工厂,让下游单次 parse 替代两步断言。三件都是文档/附加性质,无破坏性。
cloud 侧执行(不依赖上述三件,已由 cloud 分片 PM 立单派发)
cloud 仓建立
CLOUD_ERROR_CODE_LEDGER(单文件、按包登记、复刻 spec 账本的三条 lint 测试),一致性套件改为两步校验(envelopeViolations形状 +code ∈ StandardErrorCode ∪ CLOUD_LEDGER词表),36 个自造 code 经同义词审计后入账或归标准目录,failWithCode参数收窄为闭合联合,cloud#1044 的临时 ratchet 删除。cloud#1045 / cloud#1046 排在其后。Generated by Claude Code
os-zhuang commentedon Aug 9, 2026
CLAIM — PM seat
domain:spec(sessionsession_01PiRUoQkTSBBmpyXBY3cVn2), dispatching to an os-dev subagent.Dispatch gate released by the maintainer's confirmation today (2026-08-09, chat, verbatim): 「4805 ✅ 维护者裁定:同意联邦式账本 —— cloud 条目在 cloud 仓维护」— re-affirming the 2026-08-03 ruling recorded above (「4805 同意」).
claude/issue-4805-federated-error-ledger../objectstack-issue-4805(per-task, PD Migrate documentation site to Fumadocs with monorepo structure and shared content #11)error-code-ledger.zod.tsheader constraint made explicit (framework packages only; downstream repos maintain their own ledgers and compose validation); (2)contract.zod.ts:19description → "StandardErrorCode∪ 服务方登记的账本"; (3) optional non-blockingmakeApiErrorSchema(extraCodes)factory export. The cloud-side ledger/migration is the cloud shard PM's dispatch under cloud#944 — NOT in scope here.Generated by Claude Code
os-zhuang commentedon Aug 9, 2026
PM bookkeeping:
pm:dispatched→pm:queue. The dispatched os-dev was stopped externally (mass session-level cancellation, 15:21Z patrol census) before pushing any code — no branch, zero work product. Claim released; card returns to the queue intact. The federated-ledger ruling and the three-item objectstack-side scope stand unchanged; re-dispatch awaits the maintainer's word.Generated by Claude Code
os-zhuang commentedon Aug 9, 2026
RE-CLAIM — PM seat (session
session_01PiRUoQkTSBBmpyXBY3cVn2), re-dispatched as a mode:cloud card under the maintainer's re-acceleration (2026-08-09 chat). Branchclaude/issue-4805-federated-error-ledger. Scope stays exactly the three converged objectstack-side items of the federated ruling (13:33Z claim comment); the cloud-side ledger remains the cloud seat's work.Generated by Claude Code
os-zhuang commentedon Aug 9, 2026
OS-DEV-REPORT
{ "issue": 4805, "status": "delivered", "branch": "claude/issue-4805-federated-error-ledger", "head": "d3a3a9e", "pr": "https://github.com/objectstack-ai/objectstack/pull/7110", "pr_state": "draft (not queued — PM lands serially)", "worktree": "../objectstack-issue-4805", "scope": "exactly the three converged objectstack-side items of the federated ERROR_CODE_LEDGER ruling (2026-08-03, re-confirmed 2026-08-09); cloud-side ledger untouched", "items": [ { "n": 1, "landing_site": "packages/spec/src/api/error-code-ledger.zod.ts (file header)", "status": "done", "before": "framework-only was a property of the list — inferable by scanning 23 package names, stated nowhere", "after": "stated rule 'Scope: THIS ledger registers framework packages only (#4805)': downstream repos keep their own ledger and compose validation (envelopeViolations shape + code ∈ StandardErrorCode ∪ own ledger / one makeApiErrorSchema parse); records why federated (commercial vocabulary vs Apache-2.0 spec; per-code cross-repo friction breeds semantic reuse); corollary that an owner key for a non-repo package is out of scope by construction" }, { "n": 2, "landing_site": "packages/spec/src/api/contract.zod.ts (ApiErrorSchema.code description, was :19)", "status": "done", "before": "'Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ ERROR_CODE_LEDGER)'", "after": "'Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)'; JSDoc names the federation and points at makeApiErrorSchema. Description only — parsed vocabulary unchanged" }, { "n": 3, "landing_site": "packages/spec/src/api/contract.zod.ts (new export)", "status": "done (shipped — did not fight)", "after": "makeApiErrorSchema(extraCodes): envelope with StandardErrorCode ∪ extraCodes as the code vocabulary; shape reused from ApiErrorSchema via .extend rather than restated; ERROR_CODE_LEDGER entries deliberately NOT folded in (a relaying service passes [...REGISTERED_ERROR_CODES, ...MY_CODES] explicitly); ApiErrorSchema additive-unchanged" } ], "not_in_scope_confirmed": [ "no ERROR_CODE_LEDGER entry added/removed/changed", "no new error codes", "no cloud-side work (cloud#944 remains the cloud seat's)", "no content/docs/releases/ edit" ], "files": [ {"path": "packages/spec/src/api/error-code-ledger.zod.ts", "stat": "+42/-1 (docs only)"}, {"path": "packages/spec/src/api/contract.zod.ts", "stat": "+51/-5"}, {"path": "packages/spec/src/api/contract.test.ts", "stat": "+62/-0 (4 pins)"}, {"path": "packages/spec/api-surface/api.json", "stat": "+1 (gen:api-surface)"}, {"path": "packages/spec/export-origins/api.json", "stat": "+1 (gen:export-origins)"}, {"path": "content/docs/references/api/error-code-ledger.mdx", "stat": "+42/-1 (gen:schema && gen:docs)"}, {"path": "content/docs/references/api/contract.mdx", "stat": "+1/-1 (gen:schema && gen:docs)"}, {"path": ".changeset/error-code-ledger-federation-contract.md", "stat": "new — @objectstack/spec minor"} ], "reverse_verification": { "direction_predicted_first": true, "experiment": "revert the factory union to [...StandardErrorCode.options] with extraCodes ignored", "predicted": "the 2 pins exercising an extra code flip red; the standard-catalog pin and the unregistered-rejection pin stay green", "measured": "Tests 2 failed | 48 passed (50) — exactly 'accepts an extra code ONLY through the factory' and 'reuses the base envelope shape'; 50 passed after restore", "apierrorschema_unchanged": "its own tests untouched; contract + error-code-ledger + errors + envelope-violations = 99 passed unchanged. Rejection pins assert Zod issue path ['code'] and code 'invalid_value'" }, "gates": { "lint (eslint --no-inline-config)": "pass", "check:nul-bytes": "pass", "check:error-code-casing": "pass (0 lowercase in 3348 files)", "tsc --noEmit (@objectstack/spec)": "pass", "typecheck (src + scripts + tests)": "pass", "check:generated --reconcile-only": "pass (20 check: + 14 gen: classified)", "check:export-origins": "pass (4992 exports / 16 entry points; snapshot regenerated + committed)", "check:api-surface": "pass (snapshot regenerated + committed)", "check:authorable-surface": "pass", "check:docs": "pass (references deltas committed)", "check:skill-docs / check:spec-changes / check:upgrade-guide": "pass", "check:exported-any / check:dual-source-exports": "pass", "check:spec-parsed-alias (ADR-0122)": "pass (no new z.infer alias)", "check-adr-0087-registration --base origin/main": "pass — nothing owed (no declared-breaking changeset)", "spec test suite": "355 files / 9275 tests passed" }, "findings": [ "Design call worth review: the factory unions StandardErrorCode ∪ extraCodes, NOT ErrorCode ∪ extraCodes — matching the ruling's own formula rather than quietly widening it. Consequence: a downstream ledger does not silently inherit ~200 framework codes it never audited; a service that relays framework errors opts in explicitly via [...REGISTERED_ERROR_CODES, ...MY_CODES], documented on the factory.", "gen:api-surface requires the built dist/*.d.ts — it fails with 'Could not resolve module symbol' on a clean worktree until `pnpm --filter @objectstack/spec build` has run. Relevant to anyone repeating the #7103 snapshot lesson.", "Left alone deliberately: prose in packages/types/src/response-envelope.ts and packages/metadata-protocol/src/protocol.ts still reads 'StandardErrorCode ∪ ERROR_CODE_LEDGER'. Both describe the framework side, where the union is still exactly right; ErrorCode itself is unchanged." ], "next_owner": "PM (serial landing). No auto-merge, no queue enabled." }Generated by Claude Code
feat(spec): the error-code ledger states its federation contract; mak…
test(spec): the first api/ file group's test titles state each cited …