Skip to content

zod bump (#19581 / PR #19658): the version that fixes the crash is the same version that breaks the view-authoring diagnosis — decide the landing order #19674

Closed

Description

@os-warren

Ruled: 5774631464 · letter A · 2026-09-22T10:05Z — batch #212 item 1, maintainer 「19674 反正要升级,建议一步该到位」; closed completed, execution rides #19581 / PR #19658

维护者速读

把 zod 升到修好崩溃的那一版,会让 AI 作者在改视图配置被拒时,收到的提示从「你用的 page 这个值已经被移除了」变成「请把你的内容包进一个容器里」。后者是错的,照做只会更错。

原本指望能绕开:换一个只带修复、不带这个副作用的版本。已穷尽实测 12 个已发布版本,不存在 —— 厂商把两件事放在同一次发布里(4.5.0)。所以只剩一个问题:先升级还是先修提示。

顺带一条:这张卡的裁决把修复版本写成 4.6.1,实测是 4.5.0,早五个版本。不影响已提的 PR(它的下限更高,只是没必要那么高),但裁决原文需要更正。

推荐 C:先修提示层,再把升级叠上去。请回一个字母:A / B / C。


一句话问题

用 AI 写视图配置的人,今天写错一个已被移除的取值时会收到一句点名该取值的拒绝;升级依赖之后,同一个错误换来一句把他引向错误改法的拒绝。

Governing text

  • 本卡裁决 5770530634,letter B —— 「Bumps zod to the fixed line … as its own dependency PR, with a regression pin」。⚠️ 该裁决内含两条实测不成立的前提,见下。
  • Prior rulings read: invalid_union,union diagnostics,zod bump,retirement prescription,view container → 0 hits; none; thread: none; repo: objectstack-ai/objectstack
  • ⛔ 协议本身不改:三个选项都不改任何已声明的 schema 形状,动的只是被拒时的提示文本与落地顺序。

裁决前提的两处更正,都是实测

  1. 修复落在 4.5.0,不是裁决写的 4.6.1。 穷尽扫过 4.4.3 到 4.6.5 共 12 个已发布稳定版:formatter 的九项结果在 4.4.3 是 8 坏,在 4.5.0 起全部干净。源码佐证:v4/core/errors.js 里 function node(obj, key, make) 与三处 Object.prototype.hasOwnProperty.call,4.4.3 为 0,4.5.0 与 4.6.1 逐字相同。
  2. 「every workspace range is ^4.4.3」在一处不成立 —— apps/docs 是精确钉死的 4.4.3,不接受修复版。13 个声明处只有它是例外,也正是它把这件事从「只改 lockfile」变成需要改声明。

⇒ 由此派生一个附带问题:PR 现在声明的下限是 ^4.6.1(裁决点名的线),实测最低只需 ^4.5.0。两者都在修复之上,所以 PR 没有错;要不要收到实测最小值,请在回信里一并说,dev 已明确不自行改动。

选项 × 真实代价表

做什么 客户可感知的后果
A 同一个 PR 里升级,并把视图诊断层一起修好 提示始终正确;但一次依赖升级变成一个横跨四个包的改动,出问题时无法单独回滚升级
B 先升级,接受提示退化,另立单跟进 从今天起到跟进单落地为止,每个用 AI 改视图的人拿到的都是错误改法;窗口期无机制保证关闭
C 先修视图诊断层,再把升级 rebase 上去 崩溃修复晚到一个回合;提示从头到尾正确;升级保持单提交可回滚
D 换一个只带修复不带退化的版本 ⛔ 已穷尽实测,不存在。修复与退化同在 4.5.0 —— 本项已关闭

实测补充,决定 B 的代价有多重:崩溃在本仓零调用点(亮控:同命令同半径下 safeParse 命中 627 个文件)。也就是说,崩溃伤的是下游消费者格式化本平台抛出的拒绝时,本仓自己碰不到;而提示退化伤的是每一个改视图的作者,天天碰。⇒ 「先升级止血」的直觉在这里不成立:本仓没有在流血。

业务含义直译

  • A = 把「换掉一个有毛病的零件」和「重写产品的错误提示」写进同一张工单。两件事以后要分开追溯就没得追。
  • B = 明知说明书这一页印错了,先把货发出去,承诺以后补一张勘误。补不补得上,没人担保。
  • C = 先把说明书印对,再换零件。换零件晚一天,但没有任何一天是拿错说明书在指导客户。

四轴,从业务立场

① 项目长远合理性。 C 不新增任何特例:两件事各自保持可单独回滚,升级仍是它本该是的一行依赖变更。B 反向——它引入一个「已知错误提示」的临时状态,而临时状态没有到期机制就是永久状态。A 把一次零件更换和一次公开文案修改绑成一个不可分割的提交,以后任何一方出事都得连坐回滚。⇒ C

② 实际业务拉动。 今天谁撞上:提示退化是每个用 AI 改视图配置的人,每次改错都撞;崩溃在本仓无人撞(零调用点,亮控 627),撞的是下游消费者。两者都真实,但一个是天天,一个是本仓碰不到。⇒ 拉动更强的一侧要求提示不能先坏,即 C

③ 防 AI 犯错。 本轴要的是「闭合枚举优于自由结构,响亮拒绝优于静默容忍」。三个选项都保住了「响亮拒绝」——没有任何一个会让错误配置静默通过。差别在拒绝说了什么:今天说「page 这个取值已被移除」,这是闭合枚举的正确出口;B 之后说「请把内容包进一个容器」,作者照做,运行时会兑现这个改法,然后事情更糟。⇒ 一个会被兑现却把人引向更坏结果的拒绝,是本轴最差的一格,比静默还难查,因为作者有理由相信它。B 在本轴出局。⇒ C

④ 创业阶段不扩散。 remove 优于 declare-and-maintain。C 不新增任何声明。B 新增一条没写进任何地方的隐性义务(「记得回来修提示」),而每条未声明的义务都会被下一轮遗忘。A 不新增声明,但把公开文案的修改埋进一个依赖升级里,等于声明了一件没人会去读的事。⇒ C

推荐 + 回退 + 置信缺口

推荐:C。 回退项:B,且仅在崩溃对某个已知下游消费者正在造成实际损失时才回退到它 —— 那会翻转②,而②是唯一可能翻转本推荐的轴。

自检行:只看①选 C;②③④ 是否翻转:否。

置信缺口,明说看不见什么:

  • ⛔ 退化的机制尚未定位。 dev 已逐一证伪三个假设(通用 union 聚合变化、preprocess/check 包装、lazySchema Proxy 成员),三者在两个版本上表现完全一致。剩余候选收窄到视图成员自身的 strictObject/superRefine、z.never 墓碑、或 assertViewIdentity 前置门。⇒ 修复成本未知,C 的「晚一个回合」可能不止一个回合。这是 C 最大的不确定处,也是唯一能把 B 拉回来的理由。
  • ⛔ 退化的广度是下界不是全量。 目前测到 78 处断言、17 个文件、至少三个测试分片;其中一个分片报告 12 个计划包有 11 个从未跑到。真实面只会更大。
  • ⛔ 本席未独立复现那条门口读数,采信的是 dev 的双工作树、同源字节一致的对照。at-tier 复核会重新推导,⛔ 不沿用。

裁后执行段


os-decision-facets

  • ① 项目长远合理性:C 不新增特例,两件事各自可回滚;B 引入无到期机制的临时错误状态,A 把依赖更换与公开文案修改绑成不可分割的一笔。
  • ② 实际业务拉动:提示退化每个改视图的作者天天撞;崩溃在本仓零调用点(亮控 627 文件 safeParse),撞的是下游消费者 —— 拉动更强的一侧要求提示不先坏。
  • ③ 防 AI 犯错:三者都保住响亮拒绝,但 B 之后的拒绝会把作者引向一个运行时会兑现却更糟的改法,是本轴最差一格,比静默更难查。
  • ④ 创业阶段不扩散:C 零新增声明;B 新增一条没写进任何地方的隐性义务;A 把公开文案改动埋进依赖升级。

Prior rulings read: invalid_union,union diagnostics,zod bump,retirement prescription,view container → 0 hits; none; thread: none; repo: objectstack-ai/objectstack

推荐 C。自检:只看①选 C;②③④ 是否翻转:否。置信缺口:退化机制未定位 ⇒ 修复成本未知,这是唯一能把 B 拉回来的理由;退化广度是下界,非全量。


相关单与 PR


Generated by Claude Code

Activity

  1. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    Ruling: batch #212 item 1 · letter A · maintainer 「19674 反正要升级,建议一步该到位」 · 2026-09-22T10:05Z

    Director seat, summon #27, session_0129ZpnaBcYZZ51rCvQiXg6C. The maintainer's words name no letter; read as A (one PR: the zod bump AND the view-diagnosis repair, one step to done), against the seat's C recommendation. Recorded verbatim; the seat's C stays on the card as history.

    Ruled shape — complete at ruling time, ⛔ no further decision card for the dev.

    1. One PR, PR fix(deps): bump zod to the fixed line (4.6.1+) workspace-wide, with a regression pin #19658, widened. The same PR carries (a) the workspace-wide zod bump with its regression pin (as ruled at 5770530634) and (b) the repair of the view-authoring diagnosis so that, under the bumped zod, a retired value (e.g. page) is refused by a message that NAMES the retired value, as today — ⛔ never the 「wrap your content in a container」 prescription. ⛔ No test is rewritten to accept the degraded message; the 78 assertions across 17 files (a lower bound) turn green by repairing the diagnosis layer, not by editing what they assert.
    2. Where the repair lands is the dev's to locate, ⛔ not the maintainer's: the three hypotheses already falsified on the card (union aggregation change · preprocess/check wrappers · lazySchema Proxy members) are not re-run; the remaining candidates named on the card (view members' strictObject / superRefine, the z.never tombstones, the assertViewIdentity pre-gate) are measured on both zod versions with the same lit control. The mechanism found and the fix are written into the PR body with the reading.
    3. Accept set unchanged. The repair may change diagnosis WORDING only within 「a retired-value refusal that names the value」; nothing becomes accepted that is refused today, nothing refused that is accepted today. If the repair cannot be done without moving the accept set or a published refusal's semantics ⇒ Clause-②: yes on the claim (re-declared from the delivered diff), at-tier contract review before landing; ⛔ not a return to the box.
    4. Declared floor stays ^4.6.1 as the PR already has it (above the measured fix at 4.5.0; harmless, and one fewer round under 「一步到位」); apps/docs's exact 4.4.3 moves to the same range so the workspace resolves one version; lockfile regenerated by pnpm, ⛔ never hand-edited.
    5. Premise correction: ruling 5770530634 on [rebuild of #19421] [finding] zod 4.4.3's treeifyError and error.format() throw on any issue path containing __proto__ — so the formatter crashes on exactly the refusal PR #19147 landed to make that key safe #19581 said the fix is 4.6.1; the card's exhaustive scan puts it at 4.5.0 — amended on [rebuild of #19421] [finding] zod 4.4.3's treeifyError and error.format() throw on any issue path containing __proto__ — so the formatter crashes on exactly the refusal PR #19147 landed to make that key safe #19581 in this stroke. every workspace range is ^4.4.3 was false for apps/docs — same amendment.
    6. PR body declares the full file surface beyond the dispatched 12 package.json + lockfile (the four packages the card names), as a scope widening under this ruling, quoting the maintainer's words; Fixes #19581; changeset level per the dev's delivered diff (a diagnosis-text repair inside packages/spec is at least a patch on @objectstack/spec).
    7. Rollback: one revert of the squash commit restores 4.4.3 and today's diagnosis together.
    8. Who: the domain:spec seat 2 (assignee os-warren, session session_01UDXER3sdqfeVYpEWZs5mZx) continues on [rebuild of #19421] [finding] zod 4.4.3's treeifyError and error.format() throw on any issue path containing __proto__ — so the formatter crashes on exactly the refusal PR #19147 landed to make that key safe #19581 with a patch round on PR fix(deps): bump zod to the fixed line (4.6.1+) workspace-wide, with a regression pin #19658 under this widened scope — ⛔ not a new card, ⛔ not a re-dispatch from the queue. Pointer left on [rebuild of #19421] [finding] zod 4.4.3's treeifyError and error.format() throw on any issue path containing __proto__ — so the formatter crashes on exactly the refusal PR #19147 landed to make that key safe #19581.

    Prior rulings read: 5770530634 (class-1, #19581, letter B — upheld in direction, amended on the version premise); ruling A-narrow 5725370319 on #17852 (no __proto__ sweep — untouched); ADR-0059 §3, ADR-0005 §3, ADR-0032 §1, ADR-0059 §5, ADR-0076 D9, ADR-0081 §3 (the prior-rulings scan's broad-term hits; none rules on a dependency bump's landing order); thread: none.

    State in this stroke. needs-user-decision removed; the card closed completed (the decision is made; execution rides #19581 / PR #19658). Body first line gains Ruled: <this comment's id> · letter A · <UTC>.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions