维护者速读
一个动作可以写 undoable: true(「做完给我一个撤销按钮」)。本仓的框架运行时只在动作同时写了 operation: 'update' 时才真的去拍快照;而我们自己钉住的控制台(.objectui-sha = 53ded82b)里有两个读者,只看 undoable、根本不读 operation —— 而且这两个读者就是本仓活性账本判 action/undoable = live 时引用的全部证据。
所以原卡那句「写了 undoable 却没有 operation ⇒ 什么都不会发生」不成立。真实情况是:有两拨读者,各自的锚不一样。
这就把一张本以为是「补个拒绝」的卡,变成一个契约形状问题:undoable 的合法集,该按哪一拨读者来定?
⛔ 本席没有让 dev 写任何代码,因为按原卡方向改会删掉今天活着的行为:
- 我们已发布的技能示例
ReassignLeadAction(skills/objectstack-ui/rules/actions.md,挂着 os:check)正是 type: 'api' + undoable: true + 没有 operation。defineAction 就是 ActionSchema.parse,所以原卡的拒绝会让我们自己发的示例在 import 时就抛错。
- 控制台里每一个
type: 'api' 的可撤销动作都会被拒。
- 本仓活性账本对
undoable 的 live 判定会当场失去它的依据。
四个选项,代价都已测量:
- A —— 原卡方向:
undoable: true 必须配 operation: 'update'。☠️ 代价:上面三条全中,且必须连带改 objectui、抬 pin、重验活性、重写已发布示例,是一次跨仓破坏性落地,⛔ 不是本卡划的两文件面。
- B —— 让运行时在没有
operation 时也支持 undoable。☠️ 测到的反证:框架对 type: 'api' 根本没有服务端派发(action-execution.ts 自己的注释:UI-only types (url, modal, form) and api have no server dispatch here),所以在这些文档真正被编写的地方,加宽后的运行时没有东西可以锚。
- C —— 精准拒绝:只拒绝没有任何读者能锚的组合,即
operation !== 'update' 且 type !== 'api'。实测:拒掉 script 与 url 两种真正悬空的写法,api、operation: update、已发布示例全部照常通过。仍是收窄 ⇒ 仍要至档复核与 changeset,但删掉的活行为是零。⚠️ 代价我不能替您裁:它等于把「type: 'api' 是控制台能捕获的形状」这条控制台派发事实写进 spec。
- D —— 不改 schema,只改说明文字:
undoable 的 .describe() 现在那句「An action with no operation declares no write set, so nothing anchors the capture there.」实测为假,连同活性账本条目一起改口径。最便宜也最准确,但对真正悬空的那一类(script / url / flow / modal / form)北极星第 4 条仍然没被满足。
我荐 C,并把 D 的文字改正并进同一个 PR。 请回一个字母:A / B / C / D。
os-decision-facets
- ① 项目长远合理性:北极星〈优先级〉4 要求「声明了的…在运行时兑现」。
undoable 是被兑现的 —— 只是兑现它的是控制台而不是框架运行时。⇒ 真正的缺陷是契约没有说清楚谁来兑现,不是「没人兑现」。C 把合法集收到「至少有一个读者能锚」,是唯一让声明与现实对齐而不撤回能力的走法。⇒ 本棱指向 C。
- ② 实际业务拉动:⚠️ 本席测不到有多少人在写
type: 'api' + undoable,但测得到我们自己在已发布技能里教了这个写法。A 会让照着我们文档写的人在 import 时炸掉。⇒ 这一棱不是「未知」,是已知的负拉动,指向 ⛔ 不选 A。
- ③ 防 AI 犯错:现状对
script / url 上的 undoable 是静默放行 —— AI 作者写了,既不被拒也拿不到处方,正是北极星第 4 条禁止的形状。A、C 都终结这一类静默;D 不终结。⇒ 指向 A 或 C,而 ② 已排除 A。
- ④ 创业阶段不扩散:「remove 优于 declare-and-maintain」这一棱表面指向 A(撤掉这个键的一半用法)。⛔ 本席不据此翻字母:它管的是键,而 A 撤掉的是一个已发布、已教、已被活性账本判活的能力,那是撤能力不是撤键。C 新增的判定是一条 refine,不是一个新子系统。
自检行
只看①选 C;②③④ 是否翻转:否 —— ② 已知负拉动排除 A,③ 与①同向,④ 反向但它管的是键不是能力。
⚠️ 以下是原卡正文,保留不删 —— 其中被证伪的那一句已就地划掉并给出读数,因为本卡的整个框架是从它长出来的。
Path: P3 | 那条路第 3 步「验证响亮拒绝错的」 | undoable 缺 operation 被静默接受
What was measured
At objectstack-ai/objectstack head 889d9c7f22613e73d5a58d5eeacc22c9d907f4a7 (PR #19283, card #19148), an isolated at-tier contract review re-took the following on the tree:
ActionSchema.undoable is a plain optional boolean. The refine chain in packages/spec/src/ui/action.zod.ts never reads it — the key's only two occurrences in that file are its docblock and the key itself.
- The
undo envelope is built only inside executeDeclarativeUpdateAction (packages/runtime/src/action-execution.ts), which is reached only when operation is set.
So an action metadata document that sets undoable: true and declares no operation parses clean and produces no undo behaviour at runtime. The value is accepted and un-anchored.
⏹ FALSIFIED — verification read by this seat at 2026-09-20T20:12Z — measured on tip 488f4f54, and re-verified first-hand by the seat, ⛔ not taken from the dev's report. The document parses clean — that half stands. 「produces no undo behaviour」 does not: at the pinned console sha 53ded82b two readers build the undo envelope gated on action.undoable alone, with zero action.operation hits in either file — packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:487 and packages/app-shell/src/views/RecordDetailView.tsx:831. Those same two files are the entire recorded evidence for this repo's own packages/spec/liveness/action.json verdict props/undoable: live. ⇒ the value is anchored, by a different reader than the one the card looked at.
Why PR #19283 does not close it
#19283 rewrites the undoable describe text so that it documents this state accurately. The reviewer's finding, verbatim:
The new sentence documents that undoable: true on an action with no operation is accepted and un-anchored. The dev records this as "resolved by this diff, no trap left". The prose is accurate, but accuracy is not enforcement: contract-first (Prime Directive #12) would refuse that pair at parse time, which narrows the accept set and is a separate contract card by 基本裁决原则 — not something this prose closes.
Direction is not settled here
Refusing the pair moves the accepted metadata set, so this is a contract-shape question, not a mechanical fix. It is filed for triage to grade and route; this seat chooses no direction.
Dedup words
undoable, un-anchored, ActionSchema, executeDeclarativeUpdateAction, undo envelope
Origin: at-tier contract review of PR #19283, comment 5749186061 (2026-09-20T10:18Z), boundary flag 1.
Filed-by: session_01LvwGppdonww4zGLWZo5rho (domain:spec execution seat 1) — filed as a finding, not graded or routed by this seat.
Generated by Claude Code
维护者速读
一个动作可以写
undoable: true(「做完给我一个撤销按钮」)。本仓的框架运行时只在动作同时写了operation: 'update'时才真的去拍快照;而我们自己钉住的控制台(.objectui-sha=53ded82b)里有两个读者,只看undoable、根本不读operation—— 而且这两个读者就是本仓活性账本判action/undoable = live时引用的全部证据。所以原卡那句「写了
undoable却没有operation⇒ 什么都不会发生」不成立。真实情况是:有两拨读者,各自的锚不一样。这就把一张本以为是「补个拒绝」的卡,变成一个契约形状问题:
undoable的合法集,该按哪一拨读者来定?⛔ 本席没有让 dev 写任何代码,因为按原卡方向改会删掉今天活着的行为:
ReassignLeadAction(skills/objectstack-ui/rules/actions.md,挂着os:check)正是type: 'api'+undoable: true+ 没有operation。defineAction就是ActionSchema.parse,所以原卡的拒绝会让我们自己发的示例在 import 时就抛错。type: 'api'的可撤销动作都会被拒。undoable的live判定会当场失去它的依据。四个选项,代价都已测量:
undoable: true必须配operation: 'update'。☠️ 代价:上面三条全中,且必须连带改 objectui、抬 pin、重验活性、重写已发布示例,是一次跨仓破坏性落地,⛔ 不是本卡划的两文件面。operation时也支持undoable。☠️ 测到的反证:框架对type: 'api'根本没有服务端派发(action-execution.ts自己的注释:UI-only types (url, modal, form) and api have no server dispatch here),所以在这些文档真正被编写的地方,加宽后的运行时没有东西可以锚。operation !== 'update'且type !== 'api'。实测:拒掉script与url两种真正悬空的写法,api、operation: update、已发布示例全部照常通过。仍是收窄 ⇒ 仍要至档复核与 changeset,但删掉的活行为是零。type: 'api'是控制台能捕获的形状」这条控制台派发事实写进 spec。undoable的.describe()现在那句「An action with nooperationdeclares no write set, so nothing anchors the capture there.」实测为假,连同活性账本条目一起改口径。最便宜也最准确,但对真正悬空的那一类(script/url/flow/modal/form)北极星第 4 条仍然没被满足。我荐 C,并把 D 的文字改正并进同一个 PR。 请回一个字母:A / B / C / D。
os-decision-facets
undoable是被兑现的 —— 只是兑现它的是控制台而不是框架运行时。⇒ 真正的缺陷是契约没有说清楚谁来兑现,不是「没人兑现」。C 把合法集收到「至少有一个读者能锚」,是唯一让声明与现实对齐而不撤回能力的走法。⇒ 本棱指向 C。type: 'api'+undoable,但测得到我们自己在已发布技能里教了这个写法。A 会让照着我们文档写的人在 import 时炸掉。⇒ 这一棱不是「未知」,是已知的负拉动,指向 ⛔ 不选 A。script/url上的undoable是静默放行 —— AI 作者写了,既不被拒也拿不到处方,正是北极星第 4 条禁止的形状。A、C 都终结这一类静默;D 不终结。⇒ 指向 A 或 C,而 ② 已排除 A。自检行
只看①选 C;②③④ 是否翻转:否 —— ② 已知负拉动排除 A,③ 与①同向,④ 反向但它管的是键不是能力。
Path: P3 | 那条路第 3 步「验证响亮拒绝错的」 |
undoable缺operation被静默接受What was measured
At
objectstack-ai/objectstackhead889d9c7f22613e73d5a58d5eeacc22c9d907f4a7(PR #19283, card #19148), an isolated at-tier contract review re-took the following on the tree:ActionSchema.undoableis a plain optional boolean. The refine chain inpackages/spec/src/ui/action.zod.tsnever reads it — the key's only two occurrences in that file are its docblock and the key itself.undoenvelope is built only insideexecuteDeclarativeUpdateAction(packages/runtime/src/action-execution.ts), which is reached only whenoperationis set.So an action metadata document that setsundoable: trueand declares nooperationparses clean and produces no undo behaviour at runtime. The value is accepted and un-anchored.⏹ FALSIFIED — verification read by this seat at 2026-09-20T20:12Z — measured on tip
488f4f54, and re-verified first-hand by the seat, ⛔ not taken from the dev's report. The document parses clean — that half stands. 「produces no undo behaviour」 does not: at the pinned console sha53ded82btwo readers build the undo envelope gated onaction.undoablealone, with zeroaction.operationhits in either file —packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:487andpackages/app-shell/src/views/RecordDetailView.tsx:831. Those same two files are the entire recorded evidence for this repo's ownpackages/spec/liveness/action.jsonverdictprops/undoable: live. ⇒ the value is anchored, by a different reader than the one the card looked at.Why PR #19283 does not close it
#19283 rewrites the
undoabledescribe text so that it documents this state accurately. The reviewer's finding, verbatim:Direction is not settled here
Refusing the pair moves the accepted metadata set, so this is a contract-shape question, not a mechanical fix. It is filed for triage to grade and route; this seat chooses no direction.
Dedup words
undoable,un-anchored,ActionSchema,executeDeclarativeUpdateAction,undo envelopeOrigin: at-tier contract review of PR #19283, comment 5749186061 (2026-09-20T10:18Z), boundary flag 1.
Filed-by:
session_01LvwGppdonww4zGLWZo5rho(domain:specexecution seat 1) — filed as a finding, not graded or routed by this seat.Generated by Claude Code