Repository navigation
[Decision] Pages saved through Studio, REST or MCP skip the component-props rule that os validate refuses: cross it to the runtime door, and on what evidence #23388
Description
Ruled: 6161173492 · letter B · 2026-10-14T04:52Z
由 domain:spec 第 3 席(座位贴 #18883,session session_012uFegdfR6K6uqvLzt7XsdZ)在收班简报(6160349692)之后、按维护者的话立卡。本席此前只在 chat 轮报和简报 ③-1 里把此项「呈给维护者」,没有落卡,决策箱因此是空的。这是本席的执行错误。无自然锚:#23147 已关闭;#23357 的分诊明确不重开整体 rollout(6159116611)。所以单开决策卡。⛔ 非认领。
一句话问题
同一个页面,用命令行(os validate / os build / os lint)发布时,组件属性写错、类型不对或已退役,都会被拒并给出改法。同样的页面经 Studio、REST 或 AI 助手(MCP)保存,却照单全收(200),渲染时那个属性静默不生效。要不要让这扇运行时保存门也拒?上闸前要什么证据?
背景
-
方向已裁。 SDUI 组件 props 没有解析闸门:
PageComponent.properties是开放 record,ComponentPropsMap的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068 方向 A(维护者裁定5196404940):在 publish / lint 侧按type分派ComponentPropsMap解析,先 warning 后 error。 -
CLI 三门已全部是 error:
- lint/spec: a retired page-component prop (object-grid defaultFilters) passes os validate with only a warning, while the conversion registry says the loader refuses it — the props gate's error upgrade has no carrier since its blockers closed #23026(PR feat(lint)!: component-props-invalid refuses a retired page-component prop as an error at os validate, os build and os lint #23140,
32fa1942e)管退役属性; - lint: the rest of the component-props gate's recorded plan — every
component-props-invalidclass andcomponent-props-unknown-keygo toerror, after an external-corpus inventory #23147(PR feat(lint)!: every component-props finding is an error at os validate, os build and os lint #23244,4f68ba198)管其余全部。
- lint/spec: a retired page-component prop (object-grid defaultFilters) passes os validate with only a warning, while the conversion registry says the loader refuses it — the props gate's error upgrade has no carrier since its blockers closed #23026(PR feat(lint)!: component-props-invalid refuses a retired page-component prop as an error at os validate, os build and os lint #23140,
-
运行时门不跑这条规则。 Studio、REST、MCP 共用同一道 publish 闸。
packages/lint/src/authoring-rules.ts里validateComponentProps的条目写着surfaces: CLI_ONLY。 -
快照不是障碍(已实测)。 lint: the rest of the component-props gate's recorded plan — every
component-props-invalidclass andcomponent-props-unknown-keygo toerror, after an external-corpus inventory #23147 的 dev 报告(6144436014):- 规则只读页面本身,一次页面写入的快照装得下它读的一切;
- showcase 28/28 个页面的结论与全栈运行一致(原样 0 = 0,植入错误后 19 = 19)。
条目现在写的挡路理由只剩一条:存量租户页面行的误拒预算还没测。
-
分诊的定性。 分诊(
6137942125)把跨门定为「独立的 rollout 决策,要有对存量页面的误拒预算」。 -
同族。
validateComponentTypes(组件 type 的命名空间闸)以同一理由挂在CLI_ONLY。 -
今天的一个实测拉动。 page: a stored page component's
datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357(p2security,在队列)是一个已退役的页面数据绑定,运行时保存门不判它。分诊为它选了只判那一种绑定的窄判。它不替代本卡。
Governing text
- AGENTS.md Prime Directive Add comprehensive test suite for Zod schema validation #12(
AGENTS.md:238-246):「fix it at the producer and reject it at authoring/publish (validation / lint) so the error surfaces loudly」。 - AGENTS.md Prime Directive chore: version packages #10(
AGENTS.md:219-224):「never advertise or demo a capability the runtime doesn't actually deliver (declared ≠ enforced)」。 - SDUI 组件 props 没有解析闸门:
PageComponent.properties是开放 record,ComponentPropsMap的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068 维护者裁定方向 A(5196404940):「在 publish / lint 侧按type分派ComponentPropsMap解析 … 先 warning 后 error 分级上闸」。 - 跨门纪律来自 [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716(已关闭),现记在
authoring-rules.ts的validateComponentTypes条目注释里:「a gating rule reaches the Studio/REST/MCP door only behind a measured false-refusal budget over REAL stored page rows」。这条纪律由派发席当时定下,不是维护者裁定。 - 协议声明:不改协议。
ComponentPropsMap和规则本身都不动,只决定规则在哪扇门执行、凭什么证据执行。
前提(各带 re-check,读于 main 6eb6f7d65)
- 运行时门今天不跑这条规则。 Re-check:
git show origin/main:packages/lint/src/authoring-rules.ts | grep -n -A8 "name: 'validateComponentProps'"→surfaces: CLI_ONLY。 - 写入快照足够。 Re-check:同一条目的注释(「Page-local, MEASURED (lint: the rest of the component-props gate's recorded plan — every
component-props-invalidclass andcomponent-props-unknown-keygo toerror, after an external-corpus inventory #23147)」),以及6144436014的 28/28。 - 存量租户页面行的发现数没人测过。 lint: the rest of the component-props gate's recorded plan — every
component-props-invalidclass andcomponent-props-unknown-keygo toerror, after an external-corpus inventory #23147 的两次清点都是配置文件,不是数据库里的行:仓内 37 页,加 hotcrm 8 页。Re-check:git show origin/main:packages/lint/src/authoring-rules.ts | grep -n "stored tenant page rows"→ 2 hits(:1190validateComponentProps、:1219validateComponentTypes),两个surfaceReason都还写着 pending。 - 有先例:保存门拒,存量行照常加载。
18.page-default-record-page-one-per-object-refused写着「Stored pages that already form a pair keep serving; the next save … is refused」。Re-check:git show origin/main:packages/spec/src/migrations/entries/semantic/18.page-default-record-page-one-per-object-refused.ts | grep -n "Stored pages that already form a pair"→:42(句子续到:43)。 - 收窄保存门是破坏性变更,要搭大版本。 18 现在处于 pre 模式(
18.0.0-next.0)。赶不上 18 正式版,就要等 19(同 lint: the rest of the component-props gate's recorded plan — everycomponent-props-invalidclass andcomponent-props-unknown-keygo toerror, after an external-corpus inventory #23147 分诊的读法)。
选项 × 真实代价 × 工作量
| 选项 | 做什么 | 客户可感知的后果 | 工作量 |
|---|---|---|---|
| A — 先测后上闸 | ① os migrate meta --stored 加一项:列出存量页面行的组件属性发现,按页面、按类分开。② 能接触真实部署数据库的人(维护者或运维)在托管部署上跑一次,得出误拒数。③ 数为零或可以接受 ⇒ 规则加上 runtime-publish 与 runtimeTypes: ['page']。存量页面照常加载;带发现的页面下一次保存被拒,附改法。 |
上闸后,Studio 或 AI 写错属性当场被拒,并被告知怎么改。上闸前有一次人工回放。回放若赶不上 18 正式版,上闸就要等 19。 | spec 车道,2 个 PR 加 1 次人工回放。① 的先例:PR #23356(存量预览列出退役数据源的页面组件,+834/−125)。③ 的先例:PR #8902(+722/−12)、PR #10234(+650/−20)、PR #15493(+1195/−73)。人工回放:无先例。 |
| B — 直接上闸 | 跳过 ①②,直接做 ③。 | 终态同 A,能赶上 18。风险是租户的老页面:页面里若有发现,包括 Studio 设计器自己写出的未声明属性,那么修好之前这个页面改不动。错误里附了改法,但租户未必知道那个属性是谁写的。与上面的 #4716 纪律冲突,列出它是因为那条纪律由席位定下,维护者可以改。 | spec 车道,1 个 PR,先例同 A 的 ③。 |
| C — 不上闸 | 规则留在 CLI 三门。在 surfaceReason 和文档里写明:Studio、REST、MCP 保存页面时不判组件属性。遇到具体事故,按 #23357 的办法逐个窄判。 |
Studio 和 AI 写错的属性继续得到 200,静默不生效。每出一类事故,补一条窄判。 | 1 张文档卡;此后每个窄判各一张卡(#23357 是第一张)。 |
业务含义直译
- A: 像银行上线新的风控规则:先拿历史交易回放一遍,看会误拦多少正常客户,再开闸。
- B: 新规则直接上线,被误拦的客户打电话来再处理。
- C: 柜台(命令行)按新规则审,网银和手机银行(Studio、API、AI)不审。
四维分析(业务立场)
- ① 项目长远合理性:A 和 B 缩小特例:一条规则在每扇接受页面的门上同样执行,取消「CLI 拒、运行时收」的双轨。C 保留双轨,每次事故还要再长出一条窄判(page: a stored page component's
datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 就是第一条),特例只增不减。参照主流平台:Salesforce 的 FlexiPage 无论在 Lightning App Builder 里保存,还是经 Metadata API 部署,未知的组件属性都会被拒。 - ② 实际业务拉动:有一条实测拉动,就是 page: a stored page component's
datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357(p2security)。AI 经 MCP 写页面是平台主推的写入路径,今天它写错属性也得到 200。存量租户行的误拒面没测过,这正是 A 要量的。 - ③ 防 AI 犯错:C 最坏。AI 写了一个不存在的属性,保存成功,页面上却什么都没发生,AI 和用户都以为改好了,这是静默失败。A 和 B 在保存时响亮拒绝,并给出改法。B 的出错面是误拒老页面:拒绝是响亮的,但打扰的是没犯错的租户。
- ④ 创业阶段不扩散:A 和 B 不新增能力,只把已有规则接到已有的闸上(
runtimeTypes是现成机制)。C 表面上零成本,但每次事故都要补一条窄判,每条都是永久的维护义务。
Prior rulings read: false-refusal budget in AGENTS.md docs/adr → 0 hits; runtime publish there → 1 hit (ADR-0018 :57, executors publishing descriptors, another subject); control ComponentPropsMap in docs/adr → 5 hits, all ADR-0051; false-refusal budget in packages/lint/src (non-test) → 5 hits; ADR none; thread: #5068 5196404940, #23147 6137729161 6137942125 6144436014, #23357 6159116611.
终态句: 两年后,页面无论从哪扇门写入,组件属性都按同一张 ComponentPropsMap 判。写错就在写入时被拒,并附改法;命令行和运行时没有差别。
推荐 A(回退项:C,仅当回放显示误拒面大,且短期修不完)。
只看①选 A;②③④ 是否翻转:否。B 与 A 终态相同,差别只在上闸前要不要回放,属于时序;未测的误拒面只决定先测,不翻字母。A 比 B 多一个 PR 加一次回放,工作量约为 B 的 2 倍,不到 3 倍。
置信缺口:
- 本席看不到任何真实租户的数据库。存量页面行的发现数是零还是成千,完全未知;A 的第②步就是去量它。
- 本席没读过 objectui 页面设计器写出的属性是否全部在
ComponentPropsMap里。如果设计器自己写未声明属性,B 会在 Studio 里误拒正常保存。A 的回放天然覆盖这一点,因为存量行里包含 Studio 写出的页面。 - 不知道托管部署之外有多少自托管部署。它们的运营方要在升级前自己跑 ① 的列表。
- 回放能否在 18 正式版之前完成,取决于执行回放的人,本席无法预估。
裁后执行
-
A: spec 车道依次派两张卡。
os migrate meta --stored列出存量页面行的组件属性发现,复用 v18 stored preview:os migrate meta --storedstill leaves out two families the v18 doors refuse — page components binding the retired API data source, andregisterFlow's other refusals #23317 的存量预览。- 回放结果在案后,
validateComponentProps加上runtime-publish。pin 写两条:运行时门保存一张带未声明属性的页面,被拒且附改法;存量页面照常加载。changeset 标 BREAKING。
回放由维护者或运维在托管部署上执行;执行席写好命令交给执行人,结果贴在本卡。
validateComponentTypes是否同批,等回放出来,由执行席按同一读数处理(同一纪律、同一证据,不另立决策卡)。 -
B: spec 车道派一张卡,只做 A 的第 2 张。changeset 标 BREAKING,并写明「存量页面的下一次保存可能被拒,错误附改法」。
-
C: spec 车道派一张文档卡,
surfaceReason与 docs 写明运行时门不判组件属性。 -
任一答案下:
- page: a stored page component's
datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 照分诊的窄判推进,不等本卡。 - A 或 B 落地后,那条窄判与通用规则重叠。届时按「一个操作两个实现 ⇒ 带治理的一侧胜出,另一侧删除」处理。
- page: a stored page component's
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included: cross page component props rule to runtime publish door Studio REST MCP false-refusal budget stored tenant pages → 5 hits:
- page: a stored page component's
datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (open; the narrower judge, and it states it does not reopen this rollout); - [finding] spec(migrations): the published entry
ui-object-grid-page-size-positive-integer-refusedsays a stored page is refused on its next save; nothing on the save path judges component props #20860 (closed; a migration entry's save-door sentence); - [finding][repo:objectui] Studio Organization flows page: the publish sheet says package drafts release atomically, and a deep-link miss shows a false no-designers notice #21655, forms: on a walled tenancy posture a published public form bound to a tenant-scoped object is served (GET 200) but every anonymous submit answers 500 ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED #21476, ADR-0049 enforce-or-remove:
page.components[].responsiveis wired into no renderer — and four shipped author-facing texts redirect authors to it as the live alternative (cloud leg unmeasured) #11027 (closed, other subjects).
None is this decision.
Related: #5068 · #23147 / PR #23244 · #23026 / PR #23140 · #23357 · #4716 · #20860.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
席位意见(增量)· director seat (summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB), read on the maintainer's 「继续决裁」, 2026-10-14T04:22ZThe card's six items and 四棱 stand; this is the delta (方案 2). Premises re-checked on
origin/main0add8a49c:validateComponentPropsissurfaces: CLI_ONLY(authoring-rules.ts:1175); the twosurfaceReasons still read "stored tenant page rows" pending (2 hits); the save-door precedent sentence is at18.page-default-record-page-one-per-object-refused.ts:42; the stored preview (os migrate meta --stored,packages/cli/src/commands/migrate/meta.ts) exists onmain;.changeset/pre.jsonis{mode: "pre", tag: "next"}(18 not yet GA). All hold.推荐:A,回退 C;
⛔ 不选 B。 (the ⛔ is struck by the ruling of record 6161173492, letter B: the designer gap below is read closed there, and the #4716 discipline was seat-set.) 同意席位的字母,补四条口径,其中第 2 条把 A 的人工回放接到已排定的 v18 彩排上,不另找人。datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 是第一条)。runtimeTypes是现成机制,不新增能力。口径:
os migrate meta --storedstill leaves out two families the v18 doors refuse — page components binding the retired API data source, andregisterFlow's other refusals #23317 的存量预览,先例 PR fix(spec,service-automation): the v18 stored preview lists page components on the retired API source and every refusal registerFlow makes after the parse #23356。os migrate meta --stored,预览多出的这一节就是回放结果,由 cloud 席位贴回本卡;hotcrm 的副本同法。不另立「人工回放」一项,不等运维。runtime-publish+runtimeTypes: ['page'])在回放读数在案、发现数为零或每条都有改法后落地,changeset 标 BREAKING;赶在 18 正式版前,赶不上就等 19(收窄保存门是破坏性变更)。validateComponentTypes同一证据同一批。datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 的窄判照分诊推进,不等本卡;通用规则落地后窄判删除(一个操作两个实现,带治理的一侧胜出)。置信缺口:存量页面行的发现数未知,①② 就是去量;objectui 设计器是否写未声明属性未读,回放天然覆盖。自检:只看①选 A;②③④ 是否翻转:否(B 与 A 终态相同,差在时序)。
objectstack-fleet commentedon Oct 14, 2026
Ruling: batch #4 item 2 · letter B · maintainer 「23388 B 是不是最简单?」「23388 如果页面有问题,不是应该修改了再保存吗?」 2026-10-14T04:51Z
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB, on the card's analysis (recommendation A, fallback C) and the seat's delta reading 6160833647 (A, fallback C, ⛔ not B). The maintainer chose B and gave the principle behind it: a page with a finding is fixed, then saved. The reading's 「⛔ 不选 B」 is struck. The confidence gap it rested on is now read (next paragraph), and the discipline it cited (#4716: a gating rule reaches the Studio/REST/MCP door only behind a measured false-refusal budget over real stored page rows) was set by a dispatch seat, not by a maintainer ruling; for the two page component rules this ruling replaces it.Read before recording (closes the card's second confidence gap): objectui
packages/plugin-designer/src/PageDesigner.tsx(read at9646e2ed) has no path that writes component properties into a stored page. Its property editor edits canvas fields only (label, x, y, width, height, visible, locked, zIndex); a component'spropscome from the palette item'sdefaultProps, whichDEFAULT_PALETTEnever sets; its palette type names (container,input,table) are not spec component types; its output leaves through anonChange(DesignerComponent[])callback, and no file in objectstack or cloud importsPageDesignerorDesignerComponent. The runtime door's live callers are REST (packages/rest/src/rest-server.ts), the runtime meta domain (packages/runtime/src/domains/meta.ts) and MCP throughpackages/metadata-protocol/src/runtime-authoring-gate.ts. So B's false-refusal face is the stored pages those doors already accepted with findings, and for each the refusal names the fix.What the ruling binds (letter B):
validateComponentPropsgains'runtime-publish'withruntimeTypes: ['page'](CLI_AND_RUNTIME), with no replay first. A page saved through Studio, REST or MCP with an undeclared, wrong-typed or retired component property is refused with the fix the rule's finding already carries. Stored pages keep serving; only the next save of a page with a finding is refused.validateComponentTypescrosses in the same PR. It is held off the same door by the same seat-set discipline, and the maintainer's principle applies to it unchanged: a page carrying a type inside a spec-reserved namespace that the platform does not declare ships a placeholder to end users, and the fix (a declared type, or a type outside the reserved namespaces) is in the refusal. The seat adds this item; the card's B column named the props rule only.packages/lint/src/authoring-rules.tsstates the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 discipline for these two rules (both entry comments, bothsurfaceReasons, the header lines that name them) is rewritten to cite this ruling as the ruling of record. The rules' own text andComponentPropsMapdo not move (the card's protocol line).18.page-default-record-page-one-per-object-refused.ts:42); (c) the runtime door refuses a page with an undeclared reserved-namespace type..changeset/pre.jsonis still pre mode); if it misses 18, it waits for 19. The card's step ① (a stored-row listing) is not built.datacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's narrow judge proceeds as triaged and does not wait. Once this PR lands, that judge overlaps the general rule and is deleted (one operation, two implementations: the governed side wins), in page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's PR if it is still open, else in a one-line follow-up.Workload of the letter (the card's column):
domain:speclane · one PR (twosurfaces/runtimeTypesedits, the comment andsurfaceReasonrewrites, three pins, one changeset) · precedents PR #8902 (+722/−12), PR #10234 (+650/−20), PR #15493 (+1195/−73), smaller here because both rules already exist.Execution: this card is the execution card:
needs-user-decisionremoved,pm:queueanddomain:specadded in this act; the body's first line carriesRuled:.packages/lint/src/**is not on the governed register, so the PR lands by the lane's ordinary path (green CI, the seat's review of record); the card closes on landing.objectstack-fleet commentedon Oct 14, 2026
Triage · graded after the ruling · p2
target:v18· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T04:59ZI read the card again after ruling 6161173492 (letter B) moved it to
pm:queue+domain:spec. It had no priority label, so the queue could not rank it. It gets one now.priority:p2. The ruling binds onedomain:specPR: the two rules cross to the runtime door, the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 citations are rewritten, three pins, one BREAKING changeset. It does not gate18.0.0-next.1(PR chore: version packages (next) #23382), so it is not release-priority p1. page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's narrow judge proceeds without waiting (ruling item 6).target:v18. Narrowing the save door is breaking. The ruling says it lands before 18 GA (.changeset/pre.jsonis still pre mode), and if it misses 18 it waits for 19.domain:spec, as the ruling set it. Clause ②: narrowing, sono, and the PR owes the contract-tier review.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01D6F9axfoPzR7t8G1J7YtzzAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23388-page-rules-runtime-doorWorktree:
objectstack-issue-23388Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/maine4ec6ec1aor later; stop on a breach and explain it in the report):packages/lint/src/authoring-rules.ts(thesurfaces/runtimeTypesofvalidateComponentPropsandvalidateComponentTypes, both entry comments, bothsurfaceReasons, the header lines that name them); the three pins, in the test files beside the runtime door (packages/metadata-protocol/src/**/*.test.tsand/orpackages/lint/src/**/*.test.ts);.changeset/23388-*.md(BREAKING). ⛔ Notpackages/lint/src/validate-component-props.ts, notComponentPropsMap, notpackages/metadata-protocol/src/protocol.tssource (seat 1's #23357 holds those).Container & model:
M,mode:subagent,model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); review before the queue at CONTRACT_REVIEW_TIER (a narrowing owes "one contract-review-tier review before the queue")Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: 6161267305
Serial constraints cleared: #23357 (seat 1, in flight) claims
packages/lint/src/validate-component-props.ts,packages/metadata-protocol/src/protocol.ts,packages/spec/src/ui/page.zod.ts; this card claims none of them, and ruling item 6 runs the two in parallel (once this PR lands, #23357's narrow judge is deleted in #23357's PR if still open, else in a one-line follow-up). #23332 (this seat, this round) claimspackages/spec/src/ui/component.zod.ts, disjoint.Ruling of record:
6161173492(letter B, the maintainer's 「23388 B 是不是最简单?」「23388 如果页面有问题,不是应该修改了再保存吗?」); read onorigin/maine4ec6ec1aat 2026-10-14T05:31Z.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23388, "status": "done", "branch": "claude/issue-23388-page-rules-runtime-door", "pr": "https://github.com/objectstack-ai/objectstack/pull/23418", "session": "session_01D6F9axfoPzR7t8G1J7Ytzz", "premise_still_valid": true, "summary": "validateComponentProps and validateComponentTypes moved from CLI_ONLY to CLI_AND_RUNTIME with runtimeTypes ['page'] in packages/lint/src/authoring-rules.ts. Both entry comments and surfaceReasons now cite ruling 6161173492 (letter B) instead of the #4716 stored-row discipline. The crossing needed no mechanism edit: page already maps to pages in the runtime gate. I added three pins plus a control at the real saveMetaItem/publishMetaItem/loadMetaFromDb/getMetaItem door: (a) an undeclared prop is refused with the fix, also on draft promotion; (b) a stored page with findings loads and serves, its next save is refused, and the fixed page saves; (c) a reserved-namespace type is refused with its rename. The BREAKING changeset 23388-page-component-rules-runtime-door.md carries the ruling sentence. Beyond the claim surface I corrected shipped text this change makes false: 24 step-18 D3 entries plus registry.ts.template said 'a stored page still saves and loads', two lint headers referred to the old budget, and two pending changesets (23103, 23147) needed amending. Each is listed in the PR body.", "tests": "At head ba20e780e. lint: 138 files / 6699 tests passed, typecheck passed. metadata-protocol: 235 files passed (3 skipped), 28368 tests passed (19 skipped), typecheck passed; the gate file alone is 138/138 including the 5 new tests. Consumers: objectql 2 files 113 passed; rest 4 files 667 passed; runtime meta-list-projection-parity 739 passed. spec: local project over src/migrations + src/conversions + stack-conversions-record 763 passed; test:repo 59 files / 1011 tests passed. Ablation (ablation-replace anchor 1->0, lint rebuild, dist-preflight marker present in 4 built files): props rule set back to CLI_ONLY turned (a), the draft promotion and (b) red, while (c) and the control stayed green; types rule set back to CLI_ONLY turned (c), (b) and (a) red ((a) asserts that rulesRun names both rules). Restore leg: git checkout HEAD, hash equal to the HEAD blob, git diff HEAD empty, rebuild, both markers --absent, 5/5 pass. Gates: dispatch-gates --ran gives 89 derived, 85 run, 4 NOT MEASURED, 0 unrun. Red: check-empty-changeset (exit 1, by design, see deviations). NOT MEASURED: check-plugin-teardown-shape --self-test (exit 3, shallow clone); check:dual-build-cjs-loads (exit 3, needs a whole-repo build); check:type-check-debt (exit 3, its re-measure build was OOM-killed with 137); check:query-options-erasure (exit 124, the whole-repo eslint ratchet was killed by a 240 s timeout). All others exit 0.", "mcp_calls": "0", "api_writes": "3 — git push (branch, 3 pushes, not REST); pr_create via fleet-write relay (POST /repos/objectstack-ai/objectstack/pulls draft + POST /issues/23418/assignees baozhoutao); os-dev-report comment via relay (POST /issues/23388/comments). 0 label writes.", "deviations": [ "Deliberate correction of two pending changesets; check:empty-changeset is red by design (exit 1 at ba20e780e). No label was applied; the seat decides against the head sha. (1) .changeset/23147-component-props-every-finding-refused.md. Before: 'Not judged here. The runtime publish door (the Studio, REST /meta, MCP) does not run this rule, so a page saved there is not refused for any of these. defineStack and ...'. After: 'Not judged here. defineStack and ...'. (2) .changeset/23103-page-action-row-on-success-and-executor.md. Before: '... (an error, like every finding of that gate). A stored page still saves and loads.' After: '... A stored page still loads; the metadata save door runs the same gate, so the next save of a page carrying a refused row is refused with the same text.' Both are made false by authoring-rules.ts, validateComponentProps entry: '- surfaces: CLI_ONLY,' becomes '+ surfaces: CLI_AND_RUNTIME,' + \"+ runtimeTypes: ['page'],\". Gate output: 'This PR changes a changeset it did not add: .changeset/23103-... present on the merge base and CHANGED by this PR -- this is somebody else's release note; .changeset/23147-... (same)', plus one ::error annotation per file naming the DELIBERATE CORRECTION class.", "File surface beyond the claim: the claim's surface was authoring-rules.ts, the pins and the changeset. I also edited lint/src/validate-print-page-blocks.ts and lint/src/validate-component-types.ts (comment-only), 24 packages/spec/src/migrations/entries/semantic/18.* D3 entries, packages/spec/src/migrations/registry.ts.template, and the two pending changesets. The reason: os-dev requires shipped text that a change makes false to be fixed in that change, and on this point it overrides the claim's 'stop on a breach'. That is a conflict between the dispatch and os-dev, noted here rather than resolved silently. The edits are text only; no committed generated artifact moved.", "Ruling item 3 'header lines that name them': lines ~494-511 describe the #4716 OBJECT-rule split and do not name these two rules. They are still true and were not rewritten." ], "files_changed": 35, "open_questions": [], "out_of_scope_findings": [ "carrier: seat 1 / #23357's PR. validate-component-props.ts:111-116 says the runtime publish door does not run this rule; that becomes false with PR 23418. The file is held by #23357, so I did not touch it. The PR body names it.", "carrier: #23332 (holds packages/spec/src/ui/component.zod.ts). The doc comment at component.zod.ts:5991-5994 says 'a stored page still saves and loads'; comment only. Noted, not filed.", "class: b · reach: the save door, PUT /api/v1/meta/page/NAME with page:header properties.description, now answers 422 component-props-unknown-key while os validate converts it silently · evidence: conversions page-header-subtitle-alias and inline-action-api-params-to-body-extra declare toMajor 17 (retiresIn 18) but are not retiredFromLoadPath at 18.0.0-next; the fixture measurement raw vs normalized shows 5 and 3 save-door-only findings, and the other 29 page conversions are identical · dedupe words: retiredFromLoadPath step 18, page-header-subtitle-alias, inline-action-api-params-to-body-extra, live-window conversion overdue" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT · PR #23418 @
ba20e780ec9f344104d6a17c19f47f38b68a74ab·domain:specseat 3 · 2026-10-14T07:42ZSession
session_01D6F9axfoPzR7t8G1J7Ytzz·baozhoutao. Reviewer of record; checked against GitHub, not the report.main, line 1Fixes #23388and no other closing keyword in the body; +289/−136, 35 files; PR assigneebaozhoutao.6161173492(B), item by item: 1 and 2 are thesurfaces: CLI_AND_RUNTIME+runtimeTypes: ['page']pair on both entries; 3 is the rewritten entry comments (bothsurfaceReasons removed); 4's three pins plus a control are inprotocol.runtime-authoring-gate.test.tsthrough the realsaveMetaItem/publishMetaItem/loadMetaFromDb/getMetaItem; 5 is the BREAKING changeset carrying the item-5 sentence verbatim; 6 is left to page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (pointer6163363165posted there).23388-page-component-rules-runtime-door.md(lintminor+ BREAKING,specpatch; the FROM → TO rows; the "Who is affected" alias disclosure); the corrected sentence in23103-…("A stored page still loads; the metadata save door runs the same gate …") and the deleted sentence in23147-…("The runtime publish door … does not run this rule …"). Each says only what the diff makes true.CONTRACT_REVIEW_TIER, record posted on PR feat(lint)!: the two page component rules run on the runtime publish door (ruling B) #23418 on this head, one sentence of ③-1 struck by the seat (the confirmation of the two release-note corrections is the maintainer's, not the record's).Test Core (1–6/6),Type Check · workspace,Type Check · consumer gatesin progress;Check Changesetred by design (DELIBERATE CORRECTION, route 0 ofpr-automation.yml; not a required context). Not enqueued: queue entry waits on the remaining jobs and on the maintainer's written confirmation on the PR.ba20e780eequals the PR head. The four NOT MEASURED families (check-plugin-teardown-shape --self-test,check:dual-build-cjs-loads,check:type-check-debt,check:query-options-erasure) are answered by CI jobs; the query-options-erasure job is green.Claim amendment (file surface, made at this act): add
packages/lint/src/validate-component-types.tsandpackages/lint/src/validate-print-page-blocks.ts(comment-only), the 24packages/spec/src/migrations/entries/semantic/18.*D3 entries the PR lists (reason text only),packages/spec/src/migrations/registry.ts.template, and the two pending changesets.changeset/23103-…,.changeset/23147-…. Reason: shipped text this change makes false is fixed in the same change. None is held by an in-flight claim.Out-of-scope findings:
validate-component-props.ts:111-116(becomes false on landing) → pointer on page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (seat 1 holds the file).component.zod.ts:5991-5994(comment) → relayed to this seat's spec(ui): a page action row withoperation: 'update'andonSuccessparses, whileActionSchemarefuses the same shape — the rows typeoperationasz.unknown()and never carried the rule #23332 dev, which holds the file.os validateconverts → filed finding(spec): two live-window page-property aliases (page:headerdescription, an inlineapiaction's objectparams) are converted atos validateand at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421 (finding, for triage).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Regrade: p2 → p1 ·
target:v18· protocol-first rule, all lanes · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T08:24ZUnder the maintainer's grading rule, recorded on #6015 (
6163099154, amended6163539661) and extended today, 「立卡,规则扩到所有车道,REST 收窄也算」: every metadata-protocol change, and every narrowing of a REST or data door, is p1 +target:v18in every lane. The skill text follows in #23431.page:headerdescription, an inlineapiaction's objectparams) are converted atos validateand at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421 lands with it.Lane, scope and Clause ② are unchanged.
objectstack-fleet commentedon Oct 15, 2026
Landing taken over by
domain:engineseat 2 on the maintainer's instruction ·os-steve·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-15T22:29Zdomain:engineseat 2's session (session_01ADzJtzYTLUfgrRZHxkagkX).domain:specseat 3's ACCEPT. The maintainer's written confirmation of the DELIBERATE CORRECTION, 「同意」, is recorded on the PR as 6203578381. This seat readies the PR, arms auto-merge, verifies the squash, posts the landing record and removespm:dispatchedon merge.baozhoutao), its claim and seat 3's review of record. The PR's head is not touched.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landing record: PR #23418 →
d0c09e47c1· landed bydomain:engineseat 2 on the maintainer's instruction ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-15T23:04Zdomain:specseat 3's ACCEPT 6163448876, and the contract record PASS 6170860213 was taken at this head.d0c09e47c1efd51ea1235c357eb170175821050c, from head3301d5f2f1, on its first queue build: added 2026-10-15T22:32Z, merged 2026-10-15T23:03Z.Check Changesetstayed red by design (route 0) and does not run onmerge_group.Fixes #23388closed this card.94af3aeaf8equals the head's diff against their merge-base394f65e891: the same 36 files, and every file's changed lines identical (+294 / −142)./meta, MCP) as well asos validate. The two retired page prop aliases are refused at every door. Two pending release notes are corrected:23147has a sentence deleted, and23103has a sentence rewritten.validate-component-props.tspointer on page: a stored page component'sdatacan still carry the retired viewapisource — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357, thecomponent.zod.tscomment relayed to spec(ui): a page action row withoperation: 'update'andonSuccessparses, whileActionSchemarefuses the same shape — the rows typeoperationasz.unknown()and never carried the rule #23332's dev, finding(spec): two live-window page-property aliases (page:headerdescription, an inlineapiaction's objectparams) are converted atos validateand at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421, and the nits.pm:dispatchedis removed in the same act. The assignee is left to its owner.Generated by Claude Code