Skip to content

收紧 AriaProps 的未知键,并按门测量把 widget 全文件 + i18n 五个形状判为 no door(#4001 批 16) - #5069

Merged
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-4001-ui-batch16
Aug 4, 2026
Merged

收紧 AriaProps 的未知键,并按门测量把 widget 全文件 + i18n 五个形状判为 no door(#4001 批 16)#5069
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-4001-ui-batch16

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Part of #4001 —— ui/ 波次批 16(T1 必保清单序首位),widget + i18n 两文件。

账面 15 站点,AST 复测分毫不差(widget 9/9,i18n 6/6,用台账门禁自己的计数器)。逐 schema 门测量后一分为十四:收紧 1 个,改判 14 个


收紧(1 站点)

ui/i18n.zod.ts · AriaPropsSchemastrictObject

这是 ui/ 波次目前收紧的扇出最大的单个站点:作为 aria: 挂在约 30 个活形状上,横跨六个 metadata-type 根 —— ListViewSchema / PageSchema / PageComponentSchema / DashboardWidgetSchema / ChartConfigSchema / ActionSchema,以及 20 个 SDUI component def。

它在真门上真的在剥。经 view 根:

getMetadataTypeSchema('view').parse({
  listViews: { my_view: { type: 'grid', columns: ['name'],
    aria: { label: 'Accounts', describedBy: 'accounts-help' } } },
})
// → aria: {}

两个键整个消失,报告合法。作者写的无障碍名只存在于源文件里,屏幕阅读器用户听到的是 DOM 默认值,而工具链从头到尾没有一句话提过。

而且这两个拼法不是假想的:它们逐字就是 objectui normalize-list-view.tsARIA_KEY_ALIASES 折叠的那两个(objectui#2890),也就是存量视图元数据实际携带的拼法。

处方(逐条锚定具名姊妹契约,不靠编辑距离)

你写了 应写 错词从哪来
label ariaLabel objectui 的存量旧拼法,normalizeListViewSchema 在 ListView 边界折叠
describedBy ariaDescribedBy 同上
ariaRole role 本形状自己的不一致:三个键里两个带 aria 前缀,role 不带

arialabel / ariaLabell / ariadescribedby / aria-label / roles 留给编辑距离兜底 —— 先测了它够得着才不手写:一条距离已经能到的别名是誊抄,不是判断。

两个键给处方而不给重命名(finding 7 纪律)

顺带在四个本批不改的文件里加了 .strip()

animation.zod.ts / dnd.zod.ts(×2)/ keyboard.zod.ts / touch.zod.tsz.object({…}).merge(AriaPropsSchema.partial()) 构形,而 .merge() 采纳传入 schema 的未知键姿态 —— 收紧 AriaProps 会把这五个形状一并悄悄收紧:用 zod 的通用文案(不是本战役的)、没有 changeset、而且违背 #4988 实测出的「没有任何东西 parse 它们」。显式 .strip() 守住这条线,i18n.test.ts 钉住。

这是 finding 16 的 .extend() 陷阱换了个方法名出现。


改判为 no door(14 站点,⛔ 不收紧)

ui/widget.zod.ts 全文件 6 个 schema / 9 站点,ui/i18n.zod.tsI18nObject / PluralRule / NumberFormat / DateFormat / LocaleConfig 5 站点。

三条独立测量,正对照与负对照在同一轮跑:

  1. 无载体键 —— packages/spec/src 下除 ui/index.ts 桶文件外没有任何模块 import widget.zod;field.widget 是个 z.string(),命名的是已注册组件,从不指向 WidgetManifest
  2. 图上不可达 —— 从 24 个 metadata-type 根 + defineStack 出发的 BFS(4766 节点)全都够不到;PageSchema / ObjectListViewSchema 同轮解析为 direct,一个新建 z.object 和一个刻意长得像的负对照都判不可达,注入合成载体后全部翻转为 direct。
  3. 零 parse —— objectstack / objectui / cloud 三仓,除各自单测外零 .parse()。objectui 只 re-export 推导 TYPE 且改了名(RuntimeWidgetManifest / FieldWidgetComponentProps);cloud 用 code search 对全部符号 0 命中,正对照 "@objectstack/spec" 同仓 345 命中(索引可用)。

.strict()parse 的属性;没有 parse 就什么都不闸,只会把死槽装扮成受校验的活槽(#4583)。ADR-0049 enforce-or-remove 另开 #5055(与 #4988 同类)。

NumberFormat / DateFormat 确有载体(LocaleConfig.numberFormat / .dateFormat),但载体自己无门,所以整棵子树是 no door 而不是 no gate

重测的那条标注警告,落点比警告本身更有用

台账原文警告「i18n 的 label 形状按设计是宽开 record —— 收紧前必验」。验完的结论是警告没错,但瞄偏了一层:

  • 它描述的那个宽开 record 是真的 —— I18nObject.params,插值参数袋,键空间就是消息模板命名的任何东西。但它从来不是那 6 个站点之一(它是 z.record,不是 z.object),所以本棘轮压根没有东西可关。
  • 而地图假定和它一起宽开的那个配置块(AriaProps),测出来是本目录扇出最大的活形状

即:标准警告与实测指向相反方向,而两边都被记了下来(schema 头注释 + 测试钉 + 台账行,三处标准)。


台账 finding 20 —— 战役自己的门测量会误报「可达」(#5056)

第一轮 BFS 把 WidgetManifestSchema 报成 REACHABLE,而这个文件全仓没人 import。差点据此对一个死文件做 9 站点的 breaking 收紧。

机制:批 15 引入的 derived-clone bridge 在「任意一个同名属性 def 相同」时判可达 —— 它存在是有真实理由的(.extend() 克隆不共享身份但共享逐属性实例)。可是 zod 的 .describe() 返回的克隆共享同一个 _zod.def 对象(实测 defOf(z.string().describe('x')) === defOf(z.string()) 为 true),于是 SnakeCaseIdentifierSchema / I18nLabelSchema 这类共享叶子在全仓 def-同一,而 name / label 几乎是每个可授权形状都有的两个键。20 个键里命中 2 个(重合度 11%),判可达。

误差只朝一个方向:只会造门,不会藏门 —— 它全部的失效预算都花在让批次去收紧没人 parse 的东西上。

修法与阈值都写进 ui/door-reachability.testkit.ts:问「共享了多少形状」而不是「有没有共享」,阈值对着实测区间两端定(批 15 的真派生远在其上,批 16 的假阳远在其下)。widget.test.ts 有回归钉。批 15 留在 chart.test.ts 的那份拷贝属 #5056 —— 战役自己反复在记的「真相的第二份拷贝」,这次出现在它的仪器里。

方法论上值得记一句:批 15 的擦肩(typeof v !== 'object' 跳过所有 lazy Proxy)是正对照抓到的;这一条对正对照完全隐形(每个根都照样解析),要负对照 + 合成载体翻转才抓得到。一次门测量欠三样,同一轮跑:一个已知活的、一个已知死的、一个注入后必须翻转的载体。


验证

先证仪器会红,再信绿 —— 六次破坏,每个断言类目一次,逐次回退:

破坏 红的是
走图器重新引入 typeof v !== 'object' 合成载体对照(unreachabledirect)。注意 no-door 断言仍绿 —— 这正是为什么需要这条对照
bridge 退回「任意单属性」 widget 的 no-door 断言 + #5056 重合度钉(expected 1 to be less than 0.2)
AriaProps 退回 z.object 10 条断言全红
删掉 objectui 实测别名 两条重命名断言
live 的 guidance 改成错误 alias 「给处方不给重命名」断言
删掉一个 .strip() .merge() 不外溢钉

零破坏实证:

  • @objectstack/spec 301 文件 / 7709 用例通过,tsc --noEmit 干净
  • spec 十个 check:* 闸门全绿(8 个生成物 + 8 个源审计,含 check:docs —— 本批的判决全部写成 // 行注释,不会顶替参考页正文,批 15 的教训)
  • 下游消费包:lint 1064 / metadata-protocol / metadata-core 103 / platform-objects 266 / sdui-parser 6 全过
  • objectstack validate 三个示例应用 exit=0
  • ADR-0087 直解探针(批 14 方法,validate 对页面内嵌形状是空转 —— objectstack build / validate 从不按 PageSchema 解析页面元数据:ADR-0089 D3a 早就该拒绝的键一路通过,#4001 的「三个示例应用 validate 全过」对 page 面是空证 #5000):遍历三个应用构建产物 dist/objectstack.json,按载体槽路径追踪 —— aria0 个,即零破坏;探针的负对照(一个带旧拼法的块)证明会红。另 250 个同名槽命中经 provenance 核对全部属于别的 schema(翻译包 / 校验规则的 JSON-Schema properties / dashboard widgets / action params / 连接器 IO schema),没有一个是本批形状的载体

台账

ui/ 段三方竞争如预期冲突。两边的行全留,表头与可授权小计从存活行重算,不向任一方解决:

行:  29+20+9+2+7+5+4+4+4+3+1+1+1 = 90 strip of 198
可授权 = 90 − 40(38 no door + 2 no gate)= component 29 + view 20 + app 1 = 50

哪一方的数字都不是最终值 —— 批 16 在还有批 14/15 行的树上算出 118,main 上写着 91。这是该表自己记录的这个模式的第八次,已按例记进去。

os-regen 四步做完:merge → 从 origin/main checkout 七条 driver 托管路径 → 重建 spec → check:generated --fix(八个全部已是最新;收紧不改任何生成快照,与批 14/15 一致)→ 断言姊妹条目存活(批 13/14/15/16 changeset 齐全,theme/chart/dataset/sharing 的 strictObject 计数完好)。

范围外发现(已建 issue,本 PR 不改)

已知限制,诚实记录


⛔ 草稿,不合并、不置 ready。content/docs/releases/ 零触碰。


Generated by Claude Code

claude added 2 commits August 4, 2026 01:49
…+ five i18n shapes as no-door (#4001 批 16)

Fifteen strip sites measured per the #4852 remeasure; the counts held exactly
(widget 9/9, i18n 6/6, confirmed against the ledger gate's own AST counter).
Per-schema door measurement then split them one / fourteen.

Closed with strictObject:
  ui/i18n.zod.ts   AriaPropsSchema

The highest-fan-out single site the ui/ wave has closed — carried as `aria:` on
~30 shapes under six metadata-type roots (ListView, Page, PageComponent,
DashboardWidget, ChartConfig, Action, 20 SDUI component defs). Through the view
root, `aria: { label, describedBy }` parsed clean and came back `aria: {}`: the
accessible name existed in the source file and nowhere else. Those two spellings
are objectui's own ARIA_KEY_ALIASES, i.e. what stored view metadata carries.

Reclassified as no door, NOT tightened (14 sites):
  ui/widget.zod.ts  all six shapes / nine sites
  ui/i18n.zod.ts    I18nObject, PluralRule, NumberFormat, DateFormat, LocaleConfig

Measured three ways with positive AND negative controls in the same run: no
carrier key (nothing imports widget.zod but the barrel), unreachable in a
4766-node BFS from the 24 roots + defineStack, zero parse in all three repos
(cloud checked by code search against a proven-working index). A synthetic
carrier flips all of them, so the verdict is a fact about the graph.

Curation anchored on named sibling contracts, not edit distance: the two legacy
spellings on objectui's normalizer, and `ariaRole` on this shape's own
inconsistent prefix (two of three keys carry `aria`, `role` does not). The four
the distance fallback already reaches were measured first and left to it.

`live` and `ariaLabelledBy` get a prescription rather than a rename — finding 7
discipline. `live` is real and rendered by objectui's ListView alone, which
declares it via `AriaPropsSchema.extend({ live })`, so that surface keeps
accepting it and now inherits this error map; promoting it into the shared shape
would advertise aria-live on 29 renderers that do not implement it (#5058).

A `.strip()` was added to four files this batch does not otherwise touch:
animation, dnd (x2), keyboard, touch build their shapes as
`z.object({…}).merge(AriaPropsSchema.partial())`, and `.merge()` adopts the
incoming posture — closing AriaProps would have silently closed all five of those
no-door shapes with zod's generic message and no changeset. Pinned.

Ledger finding 20: the campaign's own BFS reported widget.zod.ts as REACHABLE.
Zod's `.describe()` returns a clone sharing the original `_zod.def`, so a bridge
firing on any ONE shared property under a shared name links unrelated shapes via
`name`/`label`. 2 keys of 20. The error is one-directional — it can only make a
batch tighten something dead (#4583). Corrected to whole-shape overlap in
ui/door-reachability.testkit.ts, pinned as a regression test, filed as #5056.

Instruments proven red before trusting green: six sabotages, one per assertion
class (walker, clone bridge, strictness, curation, finding-7 guidance,
merge-strip), each reverted.

Ledger: ui/ 118 strip of 198, authorable subtotal 82 — both recomputed from the
surviving rows, never decremented by this batch's count.

Refs #4001. Filed out of scope: #5055 (ADR-0049 for the 14 no-door sites),
#5056, #5058, #5059.
…#5043 landed)

Ledger ui/ section was the only conflict, as expected for a three-way contended
table. Every side's rows kept; header and authorable subtotal RECOMPUTED from the
surviving rows rather than resolved in favour of a side:

  rows: 29+20+9+2+7+5+4+4+4+3+1+1+1 = 90 strip of 198
  authorable = 90 - 40 no-parse (38 no door + 2 no gate) = component 29 + view 20
               + app 1 = 50

Neither side's number survives — 批 16 computed 118 against a tree where 批 14's
and 批 15's rows still existed, main carried 91. Eighth instance of the pattern
this table documents; recorded as such.

Merge resolution detail: main split the combined `chart / i18n` triage row into
two, so 批 16's i18n verdict was rehomed onto main's standalone i18n row, and
main's 批 15 chart row was kept verbatim. `theme.zod.ts` left the remaining-strip
map on main's reverse pin (0 strip) and stays gone.

os-regen four-step done: merge → restore the seven driver-managed artifact paths
from origin/main → rebuild spec → `check:generated --fix` (all 8 already current;
strictness changes no generated snapshot, same as 批 14/15) → sibling entries
asserted surviving (批 13/14/15/16 changesets all present, theme/chart/dataset/
sharing strictObject counts intact).

Post-merge verification (§10, overlapping packages): @objectstack/spec 301 files
/ 7709 tests pass, tsc --noEmit clean, check:strictness-ledger green.
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 2:11am

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 03:51
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 866ff16 Aug 4, 2026
25 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4001-ui-batch16 branch August 4, 2026 04:02
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
Ledger `ui/` section conflicted as expected — batch 16 (#5069) landed while
batch 18 was in flight and both edit it. Resolved by taking main's section
wholesale (so batch 16's `widget` no-door reclass, the `i18n` split row and the
AriaProps closure survive verbatim) and re-applying batch 18's three edits on
top.

Header and subtotal recomputed FROM THE SURVIVING ROWS, not carried from either
side: 29+4+9+2+7+5+4+4+4+3+1+1+1 = 74 strip, authorable 34 of 74. Batch 18 is
the ninth wrong-on-both-sides instance — it computed 84 against a tree where
batch 16's rows still existed, batch 16 computed 90 against one where view was
still 20, and the merge is neither. Recorded in the section's own prose, since
the two batches moved the same numbers for OPPOSITE reasons (16 reclassified 14
sites it never touched; 18 closed 16 it did).

check:strictness-ledger green on the merged tree: 36 open file(s) / 233 strip
site(s).

Part of #4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
冲突一处:账本 ui/ 小计段(批 16 与 批 17 都重写了它)。保留 main 的
批 16 叙述与其发现段(AriaProps 收紧 + widget/i18n 重分类),把 批 17 作为
第九次实例追加,再合成单一小计并从存活行重算。

批 16 关掉 AriaProps(i18n 6 → 5 strip)并把 widget(9)与 i18n 其余 5 项
移出 authorable,总数 91 → 90。批 17 再把 component 的 29 从 authorable
移入 no gate:

  存活行 = 29+20+9+2+7+5+4+4+4+3+1+1+1                    = 90
  no door = touch 7 + animation 4 + dnd 4 + keyboard 4
          + offline 3 + sharing 1 + notification 1
          + widget 9 + i18n 5                            = 38
  no gate = component 29 + chart 2                       = 31
  authorable = view 20 + app 1                           = 21
                                        21 + 38 + 2 + 29 = 90 ✓

由此:ui/ 剩余 90 个 strip 站点里有 69 个(超过四分之三)不是本 ratchet
的工作。批 17 自身在同一分支上把这行算错了两次(47 of 100 → 36 of 91 →
21 of 90),三次对各自的树都正确、对合并都错误,已如实写下——九次全中。

os-regen 四步:merge(未 rebase)→ 从 origin/main 取回生成物 →
install --frozen-lockfile + 重建 spec + 整体重生成 → check:generated 8/8
全绿,并断言九组兄弟条目共存(批 13 responsive 8 / 批 14 sharing 13 +
notification 3 / 批 15 theme 12 + chart 44 / 批 16 aria 3 + widget 40 +
i18n 25 / 批 17 component 26 键)。

Part of #4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
#5069(批 16)关掉 AriaProps 并把 widget/i18n 移出 authorable,总数
91 → 90。批 17 的 changeset 正文会作为 CHANGELOG 发给使用者,不能停留在
上一次合并时的数值:authorable 50 → 21 of 90,两个 no-parse 类合计
69 of 90(38 no door + 31 no gate)。

Part of #4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 4, 2026
…k-ai#4001 批 17) (objectstack-ai#5071)

* docs(spec): 把 ui/component.zod.ts 的 29 个站点判定为 no gate 而非收紧 (objectstack-ai#4001 批 17)

SDUI 组件 props 是 ui/ 目录最大的待收紧面,批 17 的测量结果是它根本没有
parse:.strict() 在这里不会强制任何东西,只会花掉一次 v17 破坏性变更换来
objectstack-ai#4583 所说的「一个被精确校验的死槽位」。

三条独立测量,控制组同轮为真:
- 承载键 PageComponentSchema.properties 是 z.record(z.string(), z.unknown());
  PageComponentSchema 自 ADR-0089 D3a 起是 .strict(),但严格性不递归。
- 从 24 个 metadata-type root + ObjectStackSchema 做 BFS(复用 build-schemas.ts
  的 objectstack-ai#4650 闭包,6899 节点),52 个目标全部 UNREACHABLE;六个正控制组全部
  root-graph;批 13 的 no-door 形状保持 unreachable。
- 三个仓库里对本文件的 parse/safeParse 全部落在本文件自己的单测内。

经验证据(definePage() 即 PageSchema.parse()):example 语料 10/10 页面上,
写进 components[].properties 的未声明键原样通过并被保留;同一个键放到
properties 的兄弟位上 10/10 被拒(负控制组)。

判定是 no gate 而非 no door —— 词汇是活的(objectui SchemaRenderer 把
properties 里每个键 spread 成 React prop),因此不能按 ADR-0049 退役。修法是
把 parse 接到承载键的闸门上,已立为 objectstack-ai#5068。

判定写在三处:文件头、component.test.ts 的钉子(含一条 properties 一旦获得
类型化分派就变红的断言)、账本 ui/ 两张表。账本 authorable strip 76 → 47,
no gate 2 → 31,均从存活行重算。

Part of objectstack-ai#4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9

* docs(spec): 把 批 17 判定从 JSDoc 改为行注释,避免泄漏进面向用户的生成文档

build-docs.ts 会把文件第一个 JSDoc 块当作模块描述,原样发布到
content/docs/references/ui/component.mdx 和 skills/objectstack-ui/references/
_index.md。内部战役判定不该出现在协议参考文档里(而且生成器会把段落打散,
渲染也是坏的)。

改为 imports 之后的 // 注释块,与 批 13 在 touch.zod.ts 的做法一致 ——
touch.mdx 至今仍显示 "Touch Target Configuration Schema",证明这个位置对
生成器不可见。check:generated 8/8 恢复全绿。

WIP checkpoint:实现 + 测试钉子 + 账本 + changeset 已完成并已验证;
待办 = 全量 typecheck/test、三个 example app 的 validate、os-regen 四步、
开 draft PR。

Part of objectstack-ai#4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9

* docs(spec): 记录 objectstack-ai#5056 桥接缺陷对 批 17 判定不适用的理由

批 16 发现 derived-clone 桥(共享 .describe() 克隆)会把死形状误报为
REACHABLE。其误差方向与本判定相反——只可能掩盖 no-gate 发现,不可能制造
一个。且本判定不依赖该桥:六个正控制组全部 root-graph(自身实例在闭包内),
52 个目标 root-graph 与 derived-clone 双双未命中。两条非 BFS 测量(开放
承载键 + 三仓零 parse 站点)独立成立。

Part of objectstack-ai#4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9

* docs(changeset): 把 批 17 changeset 的账本算术更新为合并后数值

合并 objectstack-ai#5042(批 14)后总数 100 → 91,authorable 65 → 36,两个 no-parse 类
合计 55 of 91。changeset 正文会作为 CHANGELOG 发给使用者,不能停留在
合并前的分支局部数值。

Part of objectstack-ai#4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9

* docs(changeset): 把 批 17 changeset 算术更新为合并 批 16 后的数值

objectstack-ai#5069(批 16)关掉 AriaProps 并把 widget/i18n 移出 authorable,总数
91 → 90。批 17 的 changeset 正文会作为 CHANGELOG 发给使用者,不能停留在
上一次合并时的数值:authorable 50 → 21 of 90,两个 no-parse 类合计
69 of 90(38 no door + 31 no gate)。

Part of objectstack-ai#4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants