Skip to content

feat(spec)!: 收紧 action 选项/公开分享/报表排序/数据集语义层/仪表盘小组件的嵌套未知键,并把两个站点按实测改判(#4001 批 14) - #5042

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

feat(spec)!: 收紧 action 选项/公开分享/报表排序/数据集语义层/仪表盘小组件的嵌套未知键,并把两个站点按实测改判(#4001 批 14)#5042
xuyushun441-sys merged 4 commits into
mainfrom
claude/issue-4001-ui-batch14

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Part of #4001 —— ui/ 方向第二波(批 14),六个文件,账本重测记的 11 个 strip 站点。

结论先说:11 个站点,9 个收紧、2 个改判

批 13 连着五个文件的 (p) 解出「没有门」,留下一个新的隐含预期:验证 = 找不到门。批 14 用同一套程序、同一次运行里带阳性对照,十一个站点里九个有真门 —— 而且其中四个正是批 13 在 page.components[] 上找到的嵌套洞形状。两批合起来才说出各自说不出的那句:(p) 是真问题,两种答案都常见。

文件 站点 (p) 解出来是 本 PR 做了什么
action.zod.ts 1 真门(嵌套洞) strictObject
sharing.zod.ts 2 一行两判 SharingConfig 收紧;EmbedConfig 改判
report.zod.ts 2 真门(嵌套洞) ✅ 2 个 strictObject
dataset.zod.ts 3 真门(嵌套洞) ✅ 3 个 strictObject
dashboard.zod.ts 2 真门(嵌套洞) ✅ 2 个 strictObject
notification.zod.ts 1 没有授权门 改判 + 三处记录,不收紧

站点数完全对上(1+1+2+2+3+2 = 11,动手前先用闸门自己的 AST 计数器复核)。批 13 证明了 authorable(p) 会在测量下溶解;计数自 finding 19 改读 AST 之后没有再错过。

一、四个嵌套洞 —— 严格性不递归

四个容器早就 strict,而承载真正契约的子块不是:ActionParamSchema#3746DashboardWidgetSchema 自 ADR-0021 cutover、ReportSchemaDatasetSchema

最能说明问题的一条,走的是真门getMetadataTypeSchema('action') —— MetadataManager.validate / GET /api/v1/meta / Studio 表单都过它):

options: [{ label: 'Overload', value: 'overload',
            color: 'red', visibleWhen: "record.status == 'open'", icon: 'x', disabled: false }]

parse 成功,出来是 {"label":"Overload","value":"overload"}。四个键在任何 renderer 看到之前就没了,报告有效。一个 strict 的壳套着 strip 的子块,不是一个关上的面,是关上的面的剪影。

action 的选项为什么走 strict,而兄弟 bulk-action.passthrough()

测出来的,不是照搬的。 #4909 给 bulk-action 选项条目 .passthrough() 的两条理由在这里都不成立:

  1. 那边的 def「left as-authored」逐字到达 grid,中间没有 spec 门;这边有一道已经在剥的门(上面那次实测)。.passthrough() 在这里不是保住一条活的流,而是打开一条。
  2. 那边 objectui 的 BulkActionParam 声明了显式 [key: string]: unknown 兜底;这边的落点 SelectOptionMetadata封闭接口。目标词汇封闭,正是「声明」胜过「容忍」的场合。

留下的是一个独立的能力问题(action 选项该不该讲字段级逐选项词汇)—— 归档 #5016,不在收紧批次里猜。

二、ui/sharing.zod.ts —— 账本第一个一行两判的文件

同一个文件、两个相反的判决,这也是「per SCHEMA, not per file」第一次真正生效:

  • SharingConfigSchema活门FormViewSchema.sharing 承载,rest-server.tssharing.allowAnonymous + sharing.publicLink 挂匿名表单路由,两个示例应用都在写。→ 收紧。
  • EmbedConfigSchema 没有门:全仓没有任何模块提到这个符号,BFS 不可达,零 parse。→ 改判。

按文件下判断,无论落哪边都会错一半。

三、两个 no door 站点(三条测量,均带阳性对照)

NotificationActionSchemaEmbedConfigSchema

  1. 承载键 —— barrel 之外无 importer。(匹配器按 specifier 解析而非子串比对 —— 仓里有两个 sharing.zod,子串法会把 stack.zod.ts 误记进来。这条是先证红发现的。)
  2. 图可达性 —— 从 24 个 metadata-type root + defineStack 做 BFS,6860 节点,两个都走不到。四个阳性对照 Page / Action / DashboardWidget / Webhook 同一次运行全部 root-graph;注入合成承载键后两个都翻成 root-graph
  3. 调用点 —— 除各自单测外零 .parse()。objectui 读 NotificationActionSchema.shape.variant词汇表,从不 parse 授权 payload —— 这恰好说明收紧它一分钱也买不到。

ADR-0049 定去留:#5015。三处留痕(JSDoc + 测试 pin + 账本行),pin 是会红的

四、策展锚在同仓的兄弟契约上

  • dataset 锚在本模块 header 自己点名的 data/analytics.zod.ts Cube 层。Cube metric 的 type 就是聚合函数,所以 { name: 'revenue', type: 'sum', field: 'amount' }(LLM 见了 Cube/LookML 就会这么写)过去 parse 干净、算出来是 countsql 只给 guidance 不给别名 —— 把 SUM(amount) 指向吃字段路径的 field,正是 finding 7 的陷阱。
  • report 的排序键是作者遇到的第三种 sort 拼法(SortNodeSchema{field, order}、小组件的扁平 sortBy/sortOrder、这里的 {by, direction}),映射方向相反,推不出来。
  • dashboard layout 锚在 React-Grid-Layout:minW/static/i 给逐键 guidance,而不是改名到无关的位置键上。
  • sharing 全是 camelCase 目标,兜底系统性够不着(未知键的「你是不是想写」兜底对 camelCase 键系统性偏弱:输入被小写化、候选没有 —— 每个大写字母白扣一次编辑距离 #4990)。allowAnonymous 拼错的后果是表单保持私有而作者以为公开

五、仪器自证(每一类新断言都先证红,再信绿)

破坏 应红的断言 结果
ActionParamSchema.options 退回 z.object 收紧类断言 ✅ 红(3 条)
EmbedConfig 接上真实承载键 「barrel 之外无 importer」 ✅ 红,并点名 ui/dashboard.zod.ts
把别名指向 schema 拒绝的键 处方完整性 ⚠️ 第一版绿 —— 见下
抽掉 compareTo 的 union guidance 分支错误 pin ✅ 红
声称 icon 活在 SelectOptionSchema guidance 分档 ✅ 红
加一条死别名条目 处方完整性 ✅ 红
修掉一个 #5013 的存量缺陷 豁免表反向 pin ✅ 红
台账表头写 123 / 保留已关文件的行 check:strictness-ledger 算术 ✅ 红

第三条破坏抓到了我自己的仪器欠缺,而且是本战役反复记录的那一种。 「处方完整性」测试第一版把被处方的键手抄在断言旁边 —— 真相的第二份拷贝 —— 所以改别名表根本不改它检查的东西,破坏保持全绿。重写成 AST 读别名表 + 运行时 .shape 判目标(运行时,才看得见 ...MetadataProtectionFields 这类展开)。重写后同一次破坏立刻红。strictObject 存在的意义就是把两份拷贝并成一份;测它的测试不该把拷贝再造回来。

重写过程中 walker 的硬失败护栏也真的救了一次:compareToZodOptional(ZodUnion),第一版取不到 union,测试红在「could not resolve the shape behind this comparison window」而不是静默跳过这个面。

六、ADR-0087 示例应用验证(按承载路径溯源,over BUILT 产物)

⚠️ #5000validate 从不按 PageSchema 解析页面,所以「三个应用 validate 全过」对 page 面是恒真的空证。这里走真门直接 parse 构建产物,并且沿真实承载路径下降(批 12 警告:这些形状近乎全 optional,{} 能过,按形状匹配会「找到」成千上万个无关对象、给出毫无意义的绿)。

app-showcase     78 authored slot(s) at a real carrier, 0 rejected
app-crm          13 ...
app-todo         20 ...
by carrier: datasets=43  reports=2  dashboards=51  actions=13  views=2
TOTAL 111 slots parsed against the 批 14 contracts, 0 rejected

阴性对照三个应用全红(往真实槽位注入 aggregation):

[negative control] injected `aggregation` into app-showcase datasets[showcase_task_metrics].measures[0]
  REJECTED  Unrecognized key(s) on this dataset measure: `aggregation`.
… app-crm、app-todo 同样红

探针自己也先红过一次,而且是有意义的一次views[].sharing 第一版报 0 slot,而两个示例应用明明在写 —— 我猜的是 views[].form.sharing,产物里的真实路径是 views[].formViews.<key>.sharing。一个说不出自己覆盖什么的探针,就是 finding 9 的形状;这个静默的零本来会以「已验证」落地。

三个应用 validate 全过(exit 0)。

七、账本

表头与小计由存活行重算 = 110(29+20+14+9+7+7+6+4+4+4+3+1+1+1)。批 13 写的是 119、批 14 写的是 114 —— 各自对着自己的分支都对,对着合并都错,而正确答案 110 两边都没写过。这是 automation/ 段记录的那个失败的第 5、6 例,也是头一次两个错数字都落在这一行。git 只报了一个冲突区,两批要是碰的是不同段落就一个都不会报。check:strictness-ledger 仲裁,绿。小计 88 of 110 authorable,22 属第四类

验证(全部在合并 origin/main 之后的树上重跑)

归档的范围外发现(均未指派)

已知触及面限制(实测记录,非推断)

compareTo 是 union,所以本 PR 的收紧拒绝可靠、处方送不到#5014)。拒绝仍是 #4001 的收益:静默半丢弃变成了 compareTo 上的硬失败。这条限制写进了 dashboard.zod.ts,测试对两半分开 pin —— 作者今天真正看到的裸 message,和等着被传递的处方文案 —— 免得一个绿测试冒充一条没人打印的消息。

变更集:.changeset/unknown-key-strictness-ui-batch14.md(major,v17 rc 窗口)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9


Generated by Claude Code

claude added 2 commits August 3, 2026 20:42
…as no-door (#4001 批 14)

Eleven strip sites measured per the #4852 remeasure; the counts held exactly
(1+1+2+2+3+2, confirmed against the ledger gate's own AST counter). Per-schema
door measurement then split them nine / two.

Closed with strictObject:
  ui/action.zod.ts     ActionParamSchema.options[]
  ui/sharing.zod.ts    SharingConfigSchema
  ui/report.zod.ts     ReportSortSchema, JoinedReportBlockSchema
  ui/dataset.zod.ts    DatasetDimensionSchema, DatasetMeasureSchema, .derived
  ui/dashboard.zod.ts  DashboardWidgetSchema.compareTo (object arm), .layout

Four of those are strict shells over strip children: the container was already
strict and strictness does not recurse. An action param option carrying
color/visibleWhen/icon/disabled parsed clean through getMetadataTypeSchema('action')
and came back {label, value}.

Reclassified as the fourth class (no door), NOT tightened:
  ui/notification.zod.ts  NotificationActionSchema
  ui/sharing.zod.ts       EmbedConfigSchema

Both measured three ways with positive controls in the same run: no carrier key,
unreachable in a 6860-node BFS from the 24 metadata-type roots + defineStack, zero
parse outside their own tests. Injecting a synthetic carrier flipped both, so the
verdict is a fact about the graph rather than a broken walker. sharing.zod.ts is
the ledger's first file that splits across the class.

action's option entry goes strict rather than following bulk-action's
.passthrough() (#4909): measured, not inherited. That def reaches the grid
verbatim with no spec door and objectui's BulkActionParam declares an explicit
index signature; this path has a door that already strips and lands in the CLOSED
SelectOptionMetadata.

Curation anchored on named sibling contracts, not edit distance: dataset on the
Cube layer this module's header names (a Cube metric's `type` IS its aggregation),
report's order key on SortNodeSchema and the widget's flat sortBy/sortOrder,
layout on React-Grid-Layout.

Known reach limit, measured: compareTo is a union, so its prescription is produced
but not delivered (zodIssuesToFields maps only top-level issues; #5014). The
rejection is unaffected. Tests pin both halves separately.

Out-of-scope findings filed unassigned: #5013 (ReportSchema's `filter` alias
prescribes `filters`, which it also rejects, plus 5 dead entries), #5014, #5015
(ADR-0049 verdict for the two no-door shapes), #5016 (should action options carry
the field-level per-option vocabulary).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
Brings in #5003 (批 13), #5006, #4983, #4991, #4855, #4729, #4250.

Ledger `ui/` section conflicted, as planned — 批 13 and 批 14 both edit it. Every
row from both sides kept; nothing resolved in favour of a side:

  - classification table: ONE merged `no door` row (批 13 authored the class;
    批 14 adds the positive-control requirement and the first file that SPLITS
    across it, ui/sharing.zod.ts).
  - triage table: structure from 批 13 (it split `responsive` out and grouped the
    five no-door files); 批 14's four measured verdicts overlaid.
  - remaining-strip map: `responsive` row deleted by 批 13, `action`/`report`/
    `dataset`/`dashboard` rows deleted by 批 14 — all five by the reverse pin.

Header and subtotal RECOMPUTED FROM THE SURVIVING ROWS, not decremented:
29+20+14+9+7+7+6+4+4+4+3+1+1+1 = 110. 批 13 wrote 119 (against a tree where 批 14's
four rows still existed), 批 14 wrote 114 (against one where `responsive` still
did); both were right against their own branch and both are wrong against the
merge. The fifth and sixth instances of the failure the automation/ section
documents, and the first where the two wrong numbers were both this line. Subtotal
88 of 110 authorable, 22 in the fourth class. check:strictness-ledger arbitrates
and is green.

os-regen four-step: merged (never rebased), refreshed install + spec build, reset
all seven generator-owned paths to origin/main, regenerated wholesale. Delta vs
origin/main is exactly one file — content/docs/references/ui/sharing.mdx — so the
regeneration reproduced main's artifacts byte-identically and no sibling entry was
dropped (#5006's RateLimitConfig/RateLimitConfigSchema and 批 13's responsive
entries confirmed present).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
@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 1:23am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/xl 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.

Copy link
Copy Markdown
Contributor Author

修正一处被 body sanitizer 吃掉的字符

正文第六节里 views[].formViews. 后面原本是一个占位符(< + key + >),GitHub 的 body sanitizer 把 < 加字母当成 HTML 标签在存储时剥掉了,于是存下来变成 views[].formViews..sharing。真实的承载路径是:

views[] . formViews . KEY . sharing

KEY 是 formViews 这个 map 的键,例如 showcase 里的 contact。)这正是本 PR 的探针第一版报 0 slot 的原因 —— 我当时猜的是 views[].form.sharing


合并 origin/main 之后的完整验证输出

所有数字都是在合并后的树(52723bf)上重跑的,不是中断前那一轮。

@objectstack/spec 全量测试

 RUN  v4.1.10 /home/user/objectstack-issue-4001-b14/packages/spec

 Test Files  299 passed (299)
      Tests  7595 passed (7595)
   Start at  00:39:52
   Duration  160.92s (transform 11.74s, setup 0ms, import 44.86s, tests 201.53s, environment 53ms)

全仓 typecheck

 Tasks:    123 successful, 123 total
Cached:    65 cached, 123 total
  Time:    4m13.953s

全仓 build

 Tasks:    71 successful, 71 total
Cached:     3 cached, 71 total
  Time:    7m44.364s

十六道闸门

8 个生成物(build 在前):

  ✓ check:spec-changes         spec-changes.json
  ✓ check:upgrade-guide        docs/protocol-upgrade-guide.md
  ✓ check:skill-docs           skill docs (from SKILL.md frontmatter)
  ✓ check:skill-refs           skill references
  ✓ check:react-blocks         react-blocks contract
  ✓ check:authorable-surface   authorable-surface.json + JSON schemas
  ✓ check:api-surface          api-surface.json
  ✓ check:docs                 content/docs/references/**

✓ All 8 generated artifacts are up to date.

8 个源审计(check:generated 明确列为「不在这里跑」的那些):

check:liveness                  PASS
check:empty-state               PASS
check:react-declaration-parity  PASS
check:skill-examples            PASS
check:variant-docs              PASS
check:strictness-ledger         PASS
check:exported-any              PASS
check:dual-source-exports       PASS

台账闸门详细输出:

✓ strictness ledger: 67 file(s) across 5 triaged director(ies) — site counts match,
  no undeclared schema files, section totals balance.
✓ remaining-strip map: 37 open file(s) / 269 strip site(s) across 4 director(ies) —
  counts match, no file with strip sites is missing a row, no closed file still carries one.

下游消费方(CI 会跑而 packages/spec 单独不跑的那些)

=== @objectstack/lint ===               Test Files  56 passed (56)    Tests  1052 passed (1052)
=== @objectstack/metadata ===           Test Files  15 passed (15)    Tests   336 passed (336)
=== @objectstack/metadata-protocol ===  Test Files  34 passed (34)    Tests   301 passed (301)
=== @objectstack/objectql ===           Test Files 112 passed (112)   Tests  1788 passed (1788)
=== @objectstack/cli ===                Test Files  68 passed (68)    Tests   600 passed (600)
=== @objectstack/rest ===               Test Files  38 passed (38)    Tests   577 passed (577)

这一组是有针对性的:账本 finding 18 记着「关闭一个注册类型的爆炸半径是每一个 parse 该类型的包」,本战役此前四条假处方里有三条正是被 metadata-protocol / objectql / cli 抓到的,而不是被 spec 自己。

ADR-0087 示例应用(BUILT 产物直接 parse,按承载路径溯源)

测量:

app-showcase     78 authored slot(s) at a real carrier, 0 rejected
app-crm          13 authored slot(s) at a real carrier, 0 rejected
app-todo         20 authored slot(s) at a real carrier, 0 rejected

by carrier: datasets=43  reports=2  dashboards=51  actions=13  views=2
TOTAL 111 slot(s) parsed against the 批 14 contracts, 0 rejected

阴性对照(往每个应用的真实槽位注入 aggregation):

  [negative control] injected `aggregation` into app-showcase datasets[showcase_task_metrics].measures[0]
  REJECTED datasets[showcase_task_metrics].measures[0]
      Unrecognized key(s) on this dataset measure: `aggregation`.
app-showcase     78 authored slot(s) at a real carrier, 1 rejected
  [negative control] injected `aggregation` into app-crm datasets[opportunity_metrics].measures[0]
  REJECTED datasets[opportunity_metrics].measures[0]
      Unrecognized key(s) on this dataset measure: `aggregation`.
app-crm          13 authored slot(s) at a real carrier, 1 rejected
  [negative control] injected `aggregation` into app-todo datasets[task_metrics].measures[0]
  REJECTED datasets[task_metrics].measures[0]
      Unrecognized key(s) on this dataset measure: `aggregation`.
app-todo         20 authored slot(s) at a real carrier, 1 rejected

TOTAL 111 slot(s) parsed against the 批 14 contracts, 3 rejected

三个应用 objectstack validate 全过(exit 0,零 error)—— 按 #5000,这是必要但不充分的那一半。

os-regen 四步

$ git merge origin/main          # 未 rebase、未 force-push
$ pnpm install --frozen-lockfile && rm -rf packages/runtime/.objectstack
$ pnpm --filter @objectstack/spec build
$ git checkout origin/main -- packages/spec/spec-changes.json \
    packages/spec/authorable-surface.json packages/spec/json-schema.manifest.json \
    packages/spec/api-surface.json packages/spec/api-surface-signatures.json \
    docs/protocol-upgrade-guide.md content/docs/references/
$ pnpm --filter @objectstack/spec check:generated --fix

第四步「断言兄弟条目存活」的结果,比逐条 grep 强:整体重生成后与 origin/main 的差异恰好只有一个文件

packages/spec/spec-changes.json               0 changed line(s)
packages/spec/authorable-surface.json         0 changed line(s)
packages/spec/json-schema.manifest.json       0 changed line(s)
packages/spec/api-surface.json                0 changed line(s)
packages/spec/api-surface-signatures.json     0 changed line(s)
docs/protocol-upgrade-guide.md                0 changed line(s)

content/docs/references/ui/sharing.mdx | 26 ++++++++++++++++++++++++--

也就是说重生成逐字节复现了 main 的产物,#5003 / #5006 / #4876 的条目一条都没被冲掉(RateLimitConfig / RateLimitConfigSchemaapi-surface.json 中确认存在;批 13 的 responsive 条目在 authorable-surface.json 中确认存在)。

authorable-surface.json 零变化是预期且正确的:本批只改 posture(未知键怎么办),不增删任何可授权键。


Generated by Claude Code

claude added 2 commits August 4, 2026 01:16
Ledger `ui/` section conflicted in four regions, as planned — 批 14 and 批 15
both edit it. Every row and both sides' prose kept; nothing resolved in favour
of a side.

  - classification table: kept 批 14's fuller `no door` wording (it adds the
    call-site half of the measurement and names #4988/#5015) AND 批 15's new
    fifth class `no gate`. The two are NOT the same verdict: `no door` has no
    carrier key at all and points at ADR-0049 removal; `no gate` has a LIVE
    carrier (`<ObjectChart aggregate>`) with no parse between author and
    runtime and points at wiring the gate. Opposite follow-ups — collapsing
    them would prescribe the destructive action on one of the two.
  - class prose: both blocks kept (批 14's positive-control requirement and
    per-SCHEMA split finding; 批 15's no-door/no-gate table).
  - rows: auto-merged clean — 批 15 deleted `theme` and took `chart` 7 -> 2,
    批 14 had deleted `action`/`report`/`dataset`/`dashboard` and taken
    `sharing` 2 -> 1.

Header and subtotal RECOMPUTED FROM THE SURVIVING ROWS, not decremented:
29+20+9+2+7+6+4+4+4+3+1+1+1 = 91. Cross-checked from both directions (110 - 14
theme - 5 chart = 91; 100 - 3 dataset - 2 report - 2 dashboard - 1 action - 1
sharing = 91). 批 13 wrote 119, 批 14 wrote 110, 批 15 wrote 100 — each correct
against its own branch, all three wrong against the merge, and 91 is a number no
side ever wrote down. Seventh instance of the failure the automation/ section
documents. Subtotal 65 of 91 authorable; 26 in the two no-parse classes (24
`no door`, 2 `no gate`). check:strictness-ledger arbitrates and is green.

os-regen four-step: merged (never rebased), reinstalled + rebuilt spec, reset all
seven generator-owned paths to origin/main, regenerated wholesale. Delta vs
origin/main is exactly one file — content/docs/references/ui/sharing.mdx — so the
regeneration reproduced main's artifacts byte-identically. Sibling coexistence
asserted: 批 15's ui/theme.mdx and ui/chart.mdx both present and 0 changed lines
against main, beside 批 14's sharing.mdx.

Re-verified on the merged tree: spec 299 files / 7639 tests passed, tsc --noEmit
clean, all sixteen gates green (8 generated + 8 source audits).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 01:30
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 785a748 Aug 4, 2026
25 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4001-ui-batch14 branch August 4, 2026 01:46
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
…#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.
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
冲突两处,都在账本的 ui/ 节,与 #5042(批 14)预期内的重叠:

1. 分类定义行 —— 保留 main 对 `no door` 的措辞改进(补上「除自身单测外
   无人 .parse()」),并把 批 17 的 `no gate` 补充语句嫁接到 main 的行上。
   两边内容都保留,不偏袒任一侧。
2. authorable 小计段 —— 采用 main 的叙述段(它记录了第五/六/七次
   「两边都算错」),然后从存活行重算。

重算(批 14 关掉 dataset/dashboard/report/action 四行,并把 sharing/
notification 重分类为 no door,总数 100 → 91):

  authorable = view 20 + widget 9 + i18n 6 + app 1        = 36
  no door    = touch 7 + animation 4 + dnd 4 + keyboard 4
             + offline 3 + sharing 1 + notification 1     = 24
  no gate    = component 29 + chart 2                     = 31
                                                     合计 = 91 ✓

批 17 自己就是第八次实例:它在自己分支上算的 47 of 100 对本分支正确、对
合并结果错误(正确值 36 of 91)。表格干净合并、散文两边都错——正是本节
反复记录的那种失败,已按此如实写下。

os-regen 四步已执行:merge(未 rebase)→ 从 origin/main 取回生成物 →
重装 + 重建 spec + 整体重生成 → check:generated 8/8 全绿,并断言兄弟批次
条目存活(批 13 responsive 8 / 批 14 sharing 13 + notification 3 /
批 15 theme 12 + chart 44 键均在)。

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

Part of #4001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants