Skip to content

fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226) - #6377

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-6225-reference-cell-width
Aug 7, 2026
Merged

fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226)#6377
os-zhuang merged 2 commits into
mainfrom
claude/issue-6225-reference-cell-width

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #6225
Fixes #6226

打包派发:同一个文件面(packages/spec/scripts/lib/format-type.ts)、同一次 content/docs/references/** 重生成。分开做要付两次重生成,而且各自的 diff 会被对方淹没。

#5340(PR #6211)压掉内联摘要里的长枚举之后,语料仍有 27 个超过 400 字符的类型单元格。它们不是一种毛病,是两种。

两个前提都在当前 main 上重新验证过

origin/main(含 #6308#6243#6280#6281#6333 等 19 个新提交)上重生成后实测:27 个 >400 的单元格,9 个是顶层长枚举、18 个不是,max 仍是 6092。与立单时的数字一致,两个前提都成立。

#6225 —— 顶层长枚举

build-docs.ts 一直有一条更适合长词表的渲染路径(### Allowed Values + 每成员一行项目符号),但只在整个 schematype: 'string' + enum 时才走。同样 49 个成员,被 Zod 提升成具名 schema(data/FieldType)的走项目符号,内联在属性上的(Field.type)得到 561 字符的表格单元格,ApiError.code 的 261 个成员得到 6092 字符

新增的 formatPropertyType 就是那条分支的镜像,逐条件对齐。单元格印样本加数量,完整成员表由 build-docs.ts 印在表格正下方。省略与搬迁是同一个 elideEnum 调用的两个返回值,不可能各说各话;预算只在会搬迁的那个入口读取,formatType 本身一行未改,所以 #5340 的「词表自己那一行永不省略」在 formatType 上原样成立(它的 4 条 pin 全绿)。

关于 $ref 那个候选(派发时提到的第二方案)—— 实测它连数据都不存在。 我查了生成的 JSON Schema:这 9 个位置没有一个$ref,Zod 把枚举整个内联了(data/Field.jsontype 是裸的 { type: 'string', enum: [...] })。要走 $ref 就得靠成员集合比对去反推「这份内联枚举等于那个具名 schema」,那正是 #5340 注释里点名拒绝的「猜哪个页内标题恰好带着同样的成员」。所以 PM 的判断方向对,理由比原来更硬。

#6226 —— 联合变体重复(按维护者裁决)

anyOf 分支原本是 variants.map(...).join(' | '),对变体相似度一无所知 —— ui/app.mdxApp.navigation 把同一个 { id; label; icon?; order?; … } 印了 9 遍,其中 7 遍逐字相同。现在最多印 4 个,其余印 … +N more,自报被省略的变体数

阈值全部实测,没有拍脑袋的常数

逐候选值重生成再量真实语料:

枚举预算(216 页 / 8548 个单元格 / 893 个顶层 Enum):

预算 40 80 120 160 176 184 200 240 320
搬迁份数 227 72 41 25 24 24 17 14 9
>200 121 121 121 121 121 125 144 145 145
>400 18 18 18 18 18 18 18 18 18
p99 227 227 227 227 227 227 227 247 247

40 到 176 之间每一个值的宽度结果完全相同,所以这段区间里选哪个跟宽度无关,只跟搬走多少份词表有关(227 → 24)。取 160:拿到能拿到的最好宽度、只搬 25 份、距 184 的悬崖留有余量,且恰好是内联预算 80 的两倍 —— 把两个位置的不对称说明白:摘掉一份副本几乎免费,把词表搬出它自己那一行要在页面上多开一节。

变体上限(语料 353 个联合;2 个变体占 72.5%,累计到 4 个是 92.6%,之后是薄尾):上限 2 → 8 个宽单元格,3 → 8,4 → 9,5 → 11,6 → 18(与不设上限无异)。取 4:仍能做完几乎全部工作的最松上限(收到 3 只多救回 1 个单元格,却要省略 44 个联合而不是 26 个),且与读者上一行刚见过的 INLINE_KEY_LIMIT = 4 同源 —— 一个单元格里两个不同的「印几个之后 」正是裁决要避免的不一致。

「标记必须挣回自己的位置」守卫

沿用 #5340,并被两条新省略共用。实测(把守卫改成无条件省略再重生成对比):拒绝了 248 个候选省略里的 54 个,那 54 个加起来只省 328 字符 —— 平均每个 6 字符,而标记本身要花 12–15 字符。其中 7 个是搬迁候选,即七整节 ### Allowed Values 本会为了给一个单元格削掉个位数字符而被加进页面。守卫在搬迁上比在内联省略上更要紧:那边一次拒绝省下一个标记,这边省下一整节。

全语料重新测量

之前 之后
>200 字符 145 121
>400 字符 27 9
>900 字符 4 1
p99 247 227
p95 145 145(不动)
max 6092 1538

p95 纹丝不动是重点:普通单元格一个都没有移位。

这 9 份词表改完之后住在哪里(硬要求:信息不得离开页面)

页面 · 属性 成员数 改前本页有完整拷贝吗 改后完整拷贝在哪
api/contract.mdx ApiError.code 261 ❌ 单元格是唯一一份 本页新增 ### Allowed Values: ApiError.code
api/errors.mdx EnhancedApiError.code 53 ✅(## StandardErrorCode) 本页新增属性节;原节仍在
api/errors.mdx FieldError.code 27 ✅(## FieldErrorCode) 同上
api/events.mdx MetadataEvent.type 39 ✅(## MetadataEventType) 同上
data/field.mdx Field.type 49 ✅(## FieldType) 同上
ui/view.mdx FormField.type 49 ❌(FieldTypedata/field.mdx) 本页新增属性节
ui/action.mdx ActionParam.type 49 本页新增属性节
ui/bulk-action.mdx BulkActionParam.type 49 本页新增属性节
ai/solution-blueprint.mdx BlueprintField.type 49 本页新增属性节

25 份被搬迁的词表(这 9 份 + 160 字符档的另外 16 份)逐条核过:每一份的每一个成员都在本页,25/25,0 缺失。新增的 25 个标题按 schema + 属性双重限定,全页面唯一(api/errors.mdxEnhancedApiErrorFieldError 都有 code,只写属性名会撞锚点)。

刻意不匹配的位置

Enum< … &gt;[]Record< string, Enum< … &gt; &gt;、以及联合变体(Enum< … &gt; | string)—— 对它们来说「本属性的允许值」不是实话,成员是元素 / 值 / 某一个变体的词表,表格下面挂项目符号会宣称 schema 没说过的事。数字枚举同样不匹配:项目符号渲染成 `x` 分不出 2'2',正是 #5729 修掉的错(镜像的整 schema 分支也要求 type === 'string',所以这是对齐不是巧合)。三者都有 pin。

反向验证(方向在运行之前写死,含一处未命中)

三次结果(含这次未命中)都如实写进了测试文件的注释,没有拿模板凑数。

没有被这次改动够到的、如实记录 → 已另立 #6374

剩下 9 个 &gt;400 的单元格里,ui/page.mdxPage.slots(1538,本轮新的 max)恰恰不是变体重复 —— 它每个联合只有 2 个变体(TT[]),宽度来自 INLINE_KEY_LIMIT 的 4 个键 × 2 个变体 × 每个约 176 字符,任何 ≥2 的变体上限都够不到它。另有 4 个(Manifest.capabilitiesPluginRegistryEntry.capabilitiesGetTranslationsResponse.translationsPluginSecurityManifest.permissions)根本没有联合。这是第三种机制(嵌套形状深度),已按 PD#10 立为观察类 #6374,不在本 PR 范围。裁决本身没错 —— 它确实修掉了 9 个宽单元格 —— 只是 #6226 立单时标称的旗舰样本不属于它修的那一类。

变基说明

本分支开出后 main 前进了 19 个提交,其中 8 个参考页被重生成(ui/bulk-action.mdxautomation/state-machine.mdx 与本分支相交)。两棵独立重生成的树会零冲突合并却落地陈旧组合,故按 os-regen 钩子的要求从合并后的树重新 gen:schema && gen:docs,并在合并结果上重跑了全部验证。⛔ 未手改任何生成的 .mdx

验证

  • pnpm --filter @objectstack/spec test337 个测试文件 / 8614 个测试全绿(新增 17 条 pin)
  • pnpm --filter @objectstack/spec typecheck — 干净
  • check:docs — 232 个生成文件 in sync;check:generated — 10 个生成物全部最新;check:variant-docs — 19 个可辨联合,9 governed / 10 exempt
  • 全语料 MDX+GFM 编译 216/216
  • node scripts/check-nul-bytes.mjs — 5991 个文件,无裸控制字节
  • ESLint 三个改动文件干净

Changeset

content/docs/references/** 是已发布面且读者可见输出变了,按 #6211 / #6224 / #6308 的先例给具名 @objectstack/spec patch(⛔ 非空 frontmatter)。

🤖 Generated with Claude Code

https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5


Generated by Claude Code

claude added 2 commits August 7, 2026 15:25
#5340(PR #6211)压掉内联摘要里的长枚举后,`content/docs/references/**` 仍有 27 个
超过 400 字符的类型单元格。它们是两种机制,一次重生成一起收:

- #6225:9 个「整格就是一个 Enum」的顶层词表。新增 `formatPropertyType`,是
  `build-docs.ts` 里 `### Allowed Values` 项目符号分支的镜像,逐条件对齐
  (`type === 'string'` 且 enum 是数组)。单元格印样本加数量,完整成员表印在表格
  正下方 —— 信息没有离开页面。省略与搬迁来自同一个 `elideEnum` 调用,不会各说各话;
  预算只在会搬迁的入口读取,`formatType` 本身未改,#5340 的「自己那一行不省略」原样成立。
- #6226:联合变体数上限(维护者裁决),超出印 `… +N more`,自报被省略的变体数。

阈值全部实测:枚举预算 40–176 给出完全相同的宽度结果,区间内只影响搬走多少份词表
(227 → 24),取 160;变体上限按 353 个联合的分布(2 个变体占 72.5%,累计到 4 个
是 92.6%)取 4,与 `INLINE_KEY_LIMIT` 同源。「标记必须挣回自己的位置」守卫共用,
实测拒绝 248 个候选省略里的 54 个(合计只省 328 字符),其中 7 个是整节搬迁。

全语料:>400 27 → 9,>900 4 → 1,p99 247 → 227,max 6092 → 1538,p95 145 不动。
25 份被搬迁的词表逐条核过完整成员表就在本页(25/25)。MDX 编译 216/216。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
main 上有 8 个参考页在本分支开出后被重生成(#6243#6280#6281#6333 等),
其中 `ui/bulk-action.mdx` 与 `automation/state-machine.mdx` 与本分支相交。
两棵独立重生成的树会零冲突合并却落地陈旧组合,故按 os-regen 钩子的要求从合并后的
树重新 `gen:schema && gen:docs`,并在合并结果上重跑全部验证:

- 参考语料 216 页 / 8548 个单元格,check:docs 232 个生成文件 in sync
- MDX 编译 216/216
- @objectstack/spec: 337 个测试文件 / 8614 个测试通过,typecheck 干净

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

vercel Bot commented Aug 7, 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 7, 2026 3:35pm

Request Review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

Copy link
Copy Markdown
Contributor Author

PM 验收:ACCEPT — 已 ready + auto-merge。

CI:25 个 check,24 success + 1 按设计 skipped,零 failure。ESLint、TypeScript Type Check(15:50:41)、Build Docs、Test Core 1..3/3 + 聚合、Check Changeset(首跑即绿)、Spec property liveness 全 success。名单在场守卫已过。

我的 #6225 倾向经住了检验,但你给的理由比我的硬

我说「$ref 方案改动面更大」。你给的是它根本无从下手:实测生成的 JSON Schema 里,9 个位置没有一个带 $ref —— Zod 把枚举整个内联(data/Field.jsontype 就是裸的 {type:'string',enum:[...]})。所以 $ref 方案得靠成员集合比对去推断「这个内联枚举等于那个具名 schema」,而那正是 #5340 注释明确拒绝的猜测。「更大的改动」是偏好,「没有数据可依」是事实。 后者才是能写进注释挡住下一个人的理由。

⚠️ 一处前提更正,直接影响裁决的适用面 —— 我采纳,并已上报

派发令(我写的)把 18 个非枚举单元格统称「联合变体重复」。实测 5 个不是:4 个根本没有联合(Manifest.capabilitiesPluginRegistryEntry.capabilitiesGetTranslationsResponse.translationsPluginSecurityManifest.permissions),PageComponent.type 是联合变体里的长枚举。

更要紧的是:#6226 的旗舰样本 Page.slots(1538 字符)的联合每个只有 2 个变体 —— 它的宽度是 INLINE_KEY_LIMIT(4 键) × 2 变体 × ~176 字符,所以任何 ≥2 的变体上限都够不着它,而上限 1 会砍掉 256 个普通的 T | T[]

结论说得准:维护者的裁决对它所命名的机制仍然是对的(实测消掉了 9 个宽单元格,App.navigation 家族),但 issue 自己举的头号例子属于第三种机制 —— 已立 #6374(摘要沿数组/Record/联合无预算下钻)。这不是裁决错了,是 issue 正文误认了自己的样本。

阈值是量出来的,而且给出了取值的不对称理由

枚举预算 40–176 给出完全相同的宽度剖面,band 内唯一变量是搬走多少份词表(227 → 24)。取 160 的理由不是「中间值」,而是:flat band 的顶端、离 184 那个悬崖有余量、且恰为内联预算 80 的 2 倍 —— 并写明了那个不对称:砍掉第二份拷贝是免费的,把词表搬出它自己那一行要付一个页面小节

变体上限:353 个联合的分布(2 变体占 72.5%,累计到 4 是 92.6%),cap 3 只比 cap 4 多救回一个单元格却要多省略 18 个联合;取 4 并与 INLINE_KEY_LIMIT 同源,避免一个单元格里出现两个不同的「几个以后就 …」。

「标记必须挣回自己的位置」在这里比 #5340 更重要,你说对了

守卫拒绝了 248 个候选里的 54 个,那 54 个合计只省 328 字符(每个约 6,而标记占 12–15)。其中 7 个是搬迁候选 —— 七个整节 ### Allowed Values 会被加进页面,只为给某个单元格削掉个位数。你的判断成立:#5340 那里一次拒绝省下一个标记,在这里省下一个页面小节。

「信息不得离开页面」这条硬要求,是逐条程序化核过

25/25 搬迁,0 个成员丢失,每个成员都在本页作为项目符号出现。而且分清了两种情况:api/contract.mdxApiError.code(261 项)与四张 *.type(49 项)页原本就是该页唯一拷贝,搬迁后各自获得自己的小节;其余几张原本另有 ## XxxType 小节,搬迁后两者并存。标题用 Schema.prop 双重限定,因为 api/errors.mdx 有两个 schema 都带宽 code,并全语料核过零重复标题

刻意不搬的四类(Enum<...>[]Record<string, Enum<...>>、联合变体枚举、数值枚举)里,数值枚举那条最见功力:项目符号渲染成 `x`,分不清 2 和 '2' —— 那正是 #5729 的缺陷;而被镜像的整 schema 分支本来也要求 type === 'string',所以这是对齐,不是巧合

落空的那条预测(R3),产出的东西比命中更有用

预测「把预算放进 formatType 的枚举分支(naive 修法会放的位置)会让 5 条既有 #5340 测试变红」,实测 7 红:预测的 5 条 + 你自己新加的两条守卫。你的结论是对的:误放位置能被新块自己抓住,所以这个契约不依赖 #5340 那些测试继续存在。三条结果连同这次落空都写进了测试文件的文档注释,记为实测而非模板预设。

rebase 危险实测命中并正确处置

main 中途推进 19 个 commit、8 张参考页在那边被重生成(两张与本分支相交),os-regen 钩子拒绝了提交。你从合并后的树重生成并在合并结果上重跑全套验证;因为已经推送过,没有 force-push(被禁止),而是 reset 到已推送 tip → merge → 取已验证树,使更新成为 fast-forward,最终树与已验证的 rebase 逐字节相同。这是今天第三次有 dev 独立复现「两棵独立重生成的树会零冲突合并却落地陈旧组合」这条纪律 —— 而且这次没人提醒。

check:generated 那次 api-surface/ 报红你正确识别为未构建的幻影(纪律 ㉑),build 后即绿,没有照着提交一份虚假 diff。


Generated by Claude Code

Merged via the queue into main with commit 35f7fb4 Aug 7, 2026
26 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6225-reference-cell-width branch August 7, 2026 16:09
hotlong pushed a commit that referenced this pull request Aug 7, 2026
`content/docs/references/**` 走 `merge=os-regen`,合并驱动只把 contract.mdx /
metadata.mdx 记为 pending 而不做文本合并(AGENTS.md §11)。按其规定顺序重建
工作区后 `check:generated --fix`,产物现同时含 #6377 的新版式(顶层长枚举移入
Allowed Values 小节、联合变体印数量)与本单从 ledger 摘掉的那一个码。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JTSZAjgtL3oR6YcpNDhW3T
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment