Skip to content

fix(schema-catalog): grid 示例的列数键 cols → columns —— 3/4 列示例不再静默渲染成 2 列 (#4001) - #4008

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-4001-grid-cols-columns
Aug 10, 2026
Merged

fix(schema-catalog): grid 示例的列数键 cols → columns —— 3/4 列示例不再静默渲染成 2 列 (#4001)#4008
yinlianghui merged 2 commits into
mainfrom
claude/issue-4001-grid-cols-columns

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#4001

问题

GridSchema 声明的列数键是 columns(packages/types/src/layout.ts),grid 渲染器只读 schema.columns(packages/components/src/renderers/layout/grid.tsx:50-103),注册的 designer inputs 也只有 columns / smColumns / mdColumns / lgColumns / xlColumns / gap / classNamecols 从未被 spec、类型或注册表声明,core / react / types 里也没有任何把它归一到 columns 的处理(已 git grep 核实:packages/*/src 内除 Tailwind 的 grid-cols-* 字面量外无一处读 cols)。

13 个 catalog 示例把列数写成 cols,于是值被整个丢弃:baseCols 落到字面默认 2,而且 mobile-first 降级的门禁是 typeof schema.columns === 'number',连降级也不触发 —— 在所有断点上都是死板 2 列,production 构建同样如此。

修法(契约优先)

生产者:13 个 JSON 的 colscolumns没有给渲染器加 schema.columns ?? schema.cols 之类的别名兼容 —— 按 AGENTS.md #0.1,宽容的 consumer 会把错拼法固化成第二套事实契约,而 cols 从来不是契约的一部分。

命中清单(13 个,每个文件一行改动,均在 grid 节点上)

已用脚本逐个核验:每个文件恰好一处 cols 键、且落在 type: "grid" 节点上、改后仍为数字。

文件columns
report/report-header-with-kpis.json4
theme/semantic-color-palette.json4
components-layout-page/full-dashboard.json4
components-data-display-statistic/metrics-grid.json3
components-layout-page/page-with-header.json3
forms/payment-form.json3
components-data-display-statistic/sales-dashboard.json2
auth/signup.json2
components-complex-resizable/complex-layout.json2
components-basic-div/grid-layout.json2
theme/theme-aware-ui-elements.json2
plugin-view/form-view-mode.json2
forms/contact-form.json2

渲染变化(是修好,不是回归)

实测(经真 SchemaRenderer):

  • columns: 4grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4(原 grid grid-cols-2 gap-4)
  • columns: 3grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4(原 grid grid-cols-2 gap-4)
  • columns: 2grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 gap-4(原 grid grid-cols-2 gap-4)

一处需要向 issue 正文更正:正文把那 7 个「要 2 列」的示例记为「碰巧和默认值一致、渲染无变化」。实测并非如此 —— columns: 2 会触发 mobile-first 降级,base 断点从 2 列变成 1 列(sm 起才回到 2 列)。所以 13 个示例的渲染全部变化:6 个在 md 及以上从 2 列变成正确的 3/4 列,7 个只在 base(手机)断点从 2 列变成 1 列。后者正是渲染器写明的窄屏可读性意图,不是回归。

防回潮钉

新增 examples/schema-catalog/test/grid-columns-key.test.tsx(数据面 + 渲染面,与 #3972 / #3987 的 manifest 钉同族版位),钉三件事:

  1. 数据面禁令 —— catalog 全量 423 个 JSON,任何 type: "grid" 节点都不得出现 cols 键,失败时报出示例 id 与值。第 14 个 cols 进不来。
  2. 禁令非空转 —— 13 个示例各自仍声明其应有列数(只删键也能让禁令变绿,所以列数值单独钉);另有一条 grid 节点总数下限(21),保证遍历器确实看到了节点。
  3. 渲染面抽样 —— 5 个示例经真 SchemaRenderer 钉住完整 class 串(含 className / 非默认 gap 透传),值按上表的新正确值。抽样含 components-basic-div/grid-layout —— content/docs/components/basic/div.mdx 让作者照抄的迁移范本。

注意钉的范围刻意只针对 cols 这一个键,没有做 grid 节点的键白名单:type: "grid" 被两个 tier 共用 —— 布局 grid 的 columns 是数字,而 fields-grid/* 的字段网格 columns 是列定义数组(带 name / label / readonly),白名单会误伤后者。

反向验证(方向先书面预判,再跑变异,变异未提交)

变异 A —— 把 metrics-grid.jsoncolumns 改回 cols。预判:三条翻红(禁令列出 offender、declares columns: 3declared 变空、渲染钉收到旧的错值)。实跑与预判一致:

× no grid node in the catalog carries an undeclared `cols` key
+ "components-data-display-statistic/metrics-grid (cols: 3)"
× components-data-display-statistic/metrics-grid declares columns: 3
AssertionError: expected [] to include 3
× components-data-display-statistic/metrics-grid renders grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4
Expected: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"
Received: "grid grid-cols-2 gap-4"
Tests 3 failed | 17 passed (20)

第三条尤其值得记:渲染钉在变异下复现了 issue 实测的那个缺陷值,说明它钉的是真渲染而不是恒真式。

变异 B —— 把 report-header-with-kpis 的期望值改回修复前的 grid grid-cols-2 gap-4。预判:该条单独翻红。实跑一致(Expected: "grid grid-cols-2 gap-4" / Received: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4"),证明钉里写的是新正确值。

过程中踩到的一个坑,如实记录:变异 A 的还原用了 git checkout --,而当时改动尚未 commit,于是它把文件还原到了 HEAD(即未修复态),连带污染了变异 B 那一跑的输出(B 的日志里同时出现 metrics-grid 的三条红)。已重新施加修复、复跑全绿后才 commit;后续变异都在 commit 之后做。B 自身的预判判据(那一条 pin 的 Expected/Received)不受影响。

写钉时测出的一处渲染器事实

grid 渲染器的 Tailwind 覆盖读的是 className prop,而把 schema.className 灌进这个 prop 的是 SchemaRenderer(SchemaRenderer.tsx:479-523 两种拼法都设)。所以渲染钉走真 SchemaRenderer,而不是 ComponentRegistry.get('grid') 直渲 —— 后者会静默丢掉作者写的 className,钉就看不见它了。这是先按直渲写、被 full-dashboard / semantic-color-palette 两条翻红纠正后的结论,注释已写进测试文件。

验证

  • pnpm exec vitest run examples/schema-catalog --maxWorkers=2 → 5 files / 1113 passed(含新钉 20 条)
  • pnpm exec vitest run packages/components --maxWorkers=2 → 109 files / 929 passed
  • 仓根 pnpm exec turbo run type-check --concurrency=278/78 successful
  • node scripts/check-control-bytes.mjs → OK(3911 files);并对改动文件自查了门禁扫不到的控制字节区间,clean
  • pnpm --filter @object-ui/example-schema-catalog lint → 0 errors(2 条既有 warning 在我未碰的文件里)

changeset:无。node scripts/check-changeset-presence.mjs 判定 No source of a released package changed in this range, so no changeset is owed. —— @object-ui/example-schema-catalogprivate、且在 .changeset/config.jsonignore 组里;本 PR 只动 examples 数据与测试,按 AGENTS.md「纯 bug 修复不需要 changeset」处理。

测试面的一个既有缺口(未动):examples/schema-catalog/tsconfig.jsonexcludetest,所以本包的 type-check 覆盖不到任何测试文件(含本 PR 新增的)。这正是在飞 #3968 的面,按边界不碰;新文件的类型另行用一次性 tsc --noEmit --ignoreConfig ... 单独核过,exit 0。

边界

未碰 grid.tsx(不加别名)、未碰 div.tsx#3965 / #4003 的 div 词表面(components-basic-div/grid-layout.json 的根节点本就是 grid,只改了键)、未碰 #3968 的 tsconfig 面、未碰 content/docs/releases/

另有两类超范围发现(同一缺陷类、不同文件,未在本 PR 修、未立单,已回传 PM 定夺):skills/objectui/rules/protocol.mdskills/objectui/guides/mobile.mdapps/site/app/components/ReactVsObjectUI.tsx 仍在教 cols,且 AGENTS.md #5 自身的例子也写作 cols


Generated by Claude Code

…#4001)
`GridSchema` 声明的键是 `columns`,`grid` 渲染器也只读 `schema.columns`,
注册的 designer `inputs` 同样只有 `columns` / `smColumns` / `mdColumns` /
`lgColumns` / `xlColumns`。`cols` 从未被 spec / 类型 / 注册表声明过,`core`
`react` `types` 里也没有任何归一化 —— 13 个 catalog 示例写成 `cols`,值被整个
丢弃:`baseCols` 落到字面默认值 `2`,且 mobile-first 降级因为门禁是
`typeof schema.columns === 'number'` 而根本不触发,于是在所有断点上都是死板
2 列。要 3/4 列的示例在 docs 站(含 production 构建)渲染成 2 列。
按 AGENTS.md #0.1 改生产者,不给渲染器加 `schema.columns ?? schema.cols`
之类的别名兼容 —— 宽容的 consumer 会把错拼法固化成第二套事实契约。
新增 `grid-columns-key.test.tsx` 同族防回潮钉(数据面 + 渲染面):
catalog 全量禁止 grid 节点出现 `cols`;13 个示例各自仍声明其应有列数(否则
删键也能让禁令变空转);并按新的正确值钉住 5 个抽样示例的响应式 class ramp。
Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercelBot commented Aug 9, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 9, 2026 11:54pm

Request Review

@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

更正正文里一段已过期的说明(#4004 已落 main)

开工时的 BASE 是 f3b2874e1,那时 examples/schema-catalog/tsconfig.jsonexcludetest,所以正文写了「本包 type-check 覆盖不到测试文件,新文件只能用一次性 tsc 单独核」。这段现在不成立了 —— 工作期间 #4004 落到 main(94d1d8294),给本包补了 tsconfig.test.json,type-check 脚本变成 tsc --noEmit && tsc -p tsconfig.test.json,includetest/**/*.test.ts(x),新增的测试文件正好进入这道新覆盖面

这不是纯文字冲突问题,是语义交叉,所以没有只靠 CI 的 merge commit 蒙过去:已把 origin/main 合入本分支(8b5701d19,未 force-push),pnpm install 后按真配置复验:

> tsc --noEmit && tsc -p tsconfig.test.json
type-check-exit=0
node scripts/check-type-check-coverage.mjs
✅ type-check coverage: 43/45 via `type-check`, 1 via their own build, 0 known-broken (0 errors outstanding), 1 not compiled.
✅ test type-check coverage: 24/39 packages compile their tests, 15 declared debt (240 errors outstanding), 9 with a narrow type-assertion project.

新测试文件通过 #4004 的门禁(包括其 5b 节「测试工程必须读到 build 配置漏掉的每个测试文件」),无需申报 debt。合入后复跑:

  • pnpm exec vitest run examples/schema-catalog --maxWorkers=2 → 5 files / 1113 passed
  • node scripts/check-control-bytes.mjs → OK(3913 files)

顺带说明:components-layout-grid/2-column-grid.json 本来就写的是 columns,所以修完之后 catalog 里带数字 columns 的 grid 节点是 14 个(13 个本 PR 修的 + 这 1 个原本正确的),grid 节点总数 21 —— 与钉里那条下限 21 一致。

仍是 draft,不挂 auto-merge,等 PM 验收。


Generated by Claude Code

@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM 验收(session_01GTRjn8xBqp75dk7kFupVRt):通过,转 ready 并挂 auto-merge。#4001 落地 —— 13 个 catalog grid 示例 colscolumns,生产文档站列数错误修正 + 防回潮棘轮。

核验记录(head 8b5701d19,基 f3b2874e1 后 merge main,实物核验 + CI 亲读):

  1. 修在生产者,零别名:13 JSON 逐文件一行改,渲染器未加任何 ?? schema.cols 兼容;分支上 catalog schemas 的 "cols" 残留 0(邻近词 "columns" 35 文件反查,零命中成立)。
  2. 对 issue 的修正采纳:七个 cols: 2 并非「碰巧一致无渲染变化」—— columns: 2 触发 mobile-first ramp,base 断点也从 grid-cols-2grid-cols-1 sm:...,13 个全部变渲染且方向正确(窄屏降级是文档化意图)。前提核验做成了更准的事实。
  3. 棘轮三层:catalog 全量 ban(grid 节点禁 cols)+ 13 文件意图列数 pin(删键不能静默 ban)+ 5 条渲染 pin 走真 SchemaRenderer —— 后者是实测修正的产物:直连 registry 渲染会丢 schema.className 的下水管,pin 会瞎;修正理由写进了测试注释。
  4. mid-task main 前进处理正确:fix(scripts): test type-check 覆盖率改由解析后的 tsconfig 程序判定,schema-catalog 的测试接上编译 #4004 落地使 catalog 测试进入 type-check 覆盖,dev 识别出这是语义交互而非文本冲突,merge main(非 force-push)后实跑重验(覆盖门 24/39 绿、新测试无需 TEST_DEBT),并对 PR 正文的过期段落发了更正评论而不是留着误导。
  5. 反向验证两方向命中,过程错误(mutation A 用 git checkout -- 恢复时把未提交的修复一并回退,污染了 B 的首轮)自曝并已按正确顺序重做 —— B 的判据未受影响。
  6. 门与规程:catalog 5 文件 1113 测试绿(新 20)、components 929 绿、scripts 493 绿;type-check 78/78;控制字节门 + 自扫;fable 0;releases 零触碰;worktree 已清理。
  7. CI 亲读终态:18/18 completed、0 失败(shard×4 至 00:00:39Z;coverage/dependabot path-filter skipped)。

out-of-scope 三条(教学面仍教 cols:AGENTS.md #5、skills 两处、site 营销样例;及 className 直写 Tailwind 第二方言观察)由 PM 立单跟踪(另评)。#4003(106 div 节点转换)自本 PR 落 main 后解锁。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 10, 2026 00:08
@yinlianghui
yinlianghui added this pull request to the merge queueAug 10, 2026
Merged via the queue into main with commit 34a00bfAug 10, 2026
19 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4001-grid-cols-columns branch August 10, 2026 00:08
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@yinlianghui@claude