Skip to content

fix(spec): gen:docs 内联形状里的长枚举按测得阈值省略,并印出被隐藏的成员数 (#5340) - #6211

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5340-inline-enum-width
Aug 7, 2026
Merged

fix(spec): gen:docs 内联形状里的长枚举按测得阈值省略,并印出被隐藏的成员数 (#5340)#6211
os-zhuang merged 2 commits into
mainfrom
claude/issue-5340-inline-enum-width

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5340

先复价:立单时的行号与文件归属都已过期,但前提仍然成立

按派发要求先在 origin/main 上核实,不沿用立单时的坐标:

~900 字符的实例仍然复现(content/docs/references/ui/bulk-action.mdxBulkActionDef.params)。而且量了整个语料后发现:立单时那个实例远不是最坏的 —— 261 个成员的错误码词表被内联进 13 个 api/*.mdxerror 形状里,共 80 行,单个单元格 6242 字符

读者现在看到什么,完整词表还在哪里

一个被省略的枚举渲染成:

type: Enum<'text' | 'textarea' | 'email' | 'url' | 'phone' | 'password' | 'secret' | … +42 more>

沿用本文件已有的「还有更多」记号(键省略用的就是它),+42 more让这次省略在信息上安全的那一半:纯前缀截断会让页面看起来是完整的而实际不是 —— 读者无法区分「一个 7 项的词表」和「49 项里的前 7 项」。这些页面是 AI 作者的权威输入(ADR-0033),一个靠省略说谎的页面比一个过宽的单元格更糟(与 #5059 同类)。⛔ 所以没有做无标记截断。

完整词表在哪里,按读者会先撞到的顺序:

  1. 词表自己那一行永不省略。 ctx.inShapeSummary 只在摘要的 { … } 之下被置位,所以下面四种位置全部原样打印每一个成员:schema 自己的行(BulkActionParam.typeErrorResponse.code)、顶层联合分支(Enum< … > | string,即 PageComponent.type 那个形状)、顶层 Record< string, Enum< … > >、以及顶层枚举的数组。量过:805 个内联位置里有 457 个,同一页上就有这份完整拷贝(BulkActionDef.params 就是 —— 往下两节的 BulkActionParam.type 带着全部 49 项;每个 api/*.mdxerror 形状也是,它的码表印在 ErrorResponse.code 上)。
  2. 剩下 348 个,该页上确实只有这一处,由计数本身承担「这是一个 N 项词表的样本」这句话;json-schema/ 下的 JSON Schema 仍然是它一直以来的权威。

标记不能做成指向 (1) 的锚点:Zod 会把枚举内联,到达 formatType 的节点是一个裸的 { type: 'string', enum: [...] },既没有 $ref 也没有名字可链;硬造一个等于去猜页面上哪个小节恰好带着同一批成员。

阈值是量出来的,不是拍的

语料:216 个生成页 / 8541 个类型单元格 / 1768 处 Enum,其中 805 处在摘要内联位置。这 805 处的 body 宽度是明显双峰的,每字符的分布密度正好在 80 处塌掉:

body 宽度 (48,56] (56,64] (64,80] (80,100] (100,200] >200
出现数 57 51 57 31 51 109
每字符密度 7.1 6.4 3.6 1.6 0.5

80 以下是读者本来就想看全的短词表('asc' | 'desc'、四项的控件模式),80 以上是清单

候选阈值实测(在真实语料上跑,不是估算):

方案 命中 / 805 >200 字符的单元格 p99 单元格宽 省下字符
现状 0 246 643 0
budget=24 635 (79%) 111 216 460411
budget=40 371 (46%) 128 222 452598
budget=80 189 (23.5%) 145 247 442985
budget=120 134 169 262 436962
成员数上限=4 291 134 240 445644

收紧到 24 要多省略 446 处,只多省 4% 的字符 —— 因为宽度几乎全在那 ~91 个巨型清单里,任何阈值都抓得到。固定成员数上限在每一档都更差:上限 4 会把 Enum<'a' | 'b' | 'c' | 'd' | 'e'>(31 字符,完全可读)也切掉,却仍留下 134 个 200+ 的单元格。

另外加了一条**「标记必须挣回自己的位置」**的守卫:只超出一两个成员的 body,省下的还不如 … +N more 本身占的多。没有这条守卫时,189 次省略里有 29 次省下的字符少于标记占的位置(其中 14 次只藏了一个成员、省 2–3 个字符)—— 拿一个真实拼写换一个计数和一次页面改写,是净亏。加上之后实际生效 160 处,另有 31 处(body 81–107)原样保留,阈值因此不是 81 处的悬崖。

重生成结果(独立 commit)

gen:schema && gen:docs 产物,未手改。42 页 / 144 行,方向单一:

  • 144 行全部变短,0 行变长,共移除 462,140 字符,单行最大 -6266;
  • 144 行全部带 … +N more 标记 —— 无一行是无标记截断(已机器核对:diff 里新增行中不含标记的有 0 行);
  • 900 字符以上的单元格 76 → 4;200 字符以上 246 → 145;p99 643 → 247;p95 保持 145 不变(普通单元格不动,这正是目的);
  • 摘要内联位置的最大枚举宽度 6092 → 107;wholeCell / topUnion / inRecord 三类位置一处未动。

剩下最宽的单元格是 api/contract.mdxcode,6092 字符 —— 错误码词表自己那一行,故意不动,因为它是该页上唯一一份完整拷贝。

Pin 与反向验证(两个方向都实测,不是预测)

新增 15 条 pin(packages/spec/scripts/format-type.test.ts),覆盖派发要求的三档:阈下不变、阈上带标记省略、边界;外加 #5729 / #5606 的交叉影响与「无 ctx 不省略」的显式降级声明。

  • 还原改动前的枚举分支(任何深度都不省略):7 failed | 44 passed。7 个红全是断言 … +N more 的用例。
  • 无条件省略(formatEnum(prop.enum, true),即这次改动最显然的一行版本):5 failed | 46 passed —— NOT elided 那组里的 4 条,加上「无 ctx 不省略」那条。

第一版注释里我预测的是「6 failed | 41 passed」和「那五条」,两个都猜错了,已按实测数字改写(#5729 的注释里也记过同样的自我更正)。leaves short enums inside a summary 是两个方向下都绿的那条,原因诚实地写在注释里:两项枚举在哪一版都在预算内,所以它守的是预算而不是位置

验证

pnpm --filter @objectstack/spec test          → 330 files / 8435 tests passed
pnpm --filter @objectstack/spec typecheck     → tsc --noEmit + check:test-typecheck OK
pnpm --filter @objectstack/spec check:generated → 10/10 artifacts up to date
pnpm --filter @objectstack/spec check:docs    → 232 generated files in sync
node scripts/check-nul-bytes.mjs              → OK (5920 files)

消费半径已扫:formatType 只有 build-docs.ts 三处调用,其他包没有任何 fixture 拼写内联枚举(grep 过),所以不存在 #5046 那种「改在 lint、坏在 cli fixture」的跨包遗漏。

一点观察(仅建议,本 PR 不做)

format-type.ts 的摘要装配面现在同时承载三件事:墓碑剔除(#5606)、逐值引号(#5729)、宽度省略(本单)。我认为目前仍是内聚的,理由与是否该抽取的判断写在给 PM 的报告里,这里不展开,也没有在本 PR 里做任何抽取。


Generated by Claude Code

claude added 2 commits August 7, 2026 10:56
…, with a count of what was hidden (#5340)

`formatType()` capped how many KEYS an inline object summary prints
(`INLINE_KEY_LIMIT = 4`) but never capped how wide one key's TYPE could be, so a
long enum reached through a summary printed every member into a single table
cell. Filed on `BulkActionDef.params` (~900 chars); the corpus measurement found
worse — the 261-member error-code vocabulary inlined into `error` shapes on 80
rows across 13 `api/*.mdx` pages, at 6242 characters in one cell.

An `Enum<…>` body rendered below a summary's `{ … }` is now cut to
`INLINE_ENUM_WIDTH_LIMIT` (80 characters) and the count of what was cut is
printed in its place: `Enum<'text' | 'textarea' | … +42 more>`. The count is the
safety property — a silent prefix would leave the page looking complete while it
was not, which is a worse defect than a wide cell for pages that are the
authoritative input for AI authors (ADR-0033).

Only a SECOND copy is cut. `ctx.inShapeSummary` is set once, where a summary
renders a child, and inherited downward; a schema's own row, a union variant on
its own row (`Enum<…> | string`), a top-level `Record<string, Enum<…>>` and an
array of a top-level enum all still print every member. For 457 of the 805
in-shape occurrences that is where the elided copy's full list still lives, on
the same page.

The 80 is measured, not chosen: across 216 pages / 8541 type cells / 1768 enum
occurrences the in-shape population is bimodal and its per-character density
collapses at 80 (3.6/char over (64,80], 1.6 over (80,100], 0.5 over (100,200]).
An elision must also pay for its own marker, so a body a member or two over
budget is left whole and the limit is not a cliff at exactly 81.

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

`pnpm --filter @objectstack/spec gen:schema && gen:docs` output only — no hand
edits (`content/docs/references/` is AUTO-GEN).

42 pages, 144 rows, one direction: every changed row is shorter (462,140
characters removed in total, largest single row -6266) and every one carries a
`… +N more` marker — no row was silently truncated and none grew. Cells over 900
characters 76 → 4, over 200 characters 246 → 145, p99 cell width 643 → 247,
p95 unchanged at 145.

The widest cell left is `api/contract.mdx` `code` at 6092 — the error-code
vocabulary on its OWN row, deliberately untouched because it is the only full
copy of that list on the page.

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 10:58am

Request Review

@github-actions github-actions Bot added the size/l label Aug 7, 2026
@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). ✅

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 7, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 7, 2026 11:18
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 2a61116 Aug 7, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5340-inline-enum-width branch August 7, 2026 11:33
os-zhuang added a commit that referenced this pull request Aug 7, 2026
生成物 `content/docs/references/**` 一律取 origin/main 版本(#4675 第二步:
生成树用 checkout 定向取一侧,不手工解冲突),使本合并提交成为确定性基线 ——
两侧各自从不同源码状态生成过,文本自动合并虽无冲突,得到的却是「陈旧组合」。

主线带入 #6211(#5340,内联长枚举省略,落点 `lib/format-type.ts`);
本分支落点 `lib/file-description.ts`,两者互不重叠。

注:本提交刻意以 `--no-verify` 落下 —— os-regen 钩子(正确地)指出生成物相对
源码已陈旧。#4675 要求合并与重跑分成两个提交以便分别 review,故此处保留陈旧
状态,由紧随其后的提交整体重跑 `gen:schema && gen:docs` 修正。PR head 上的
CI 校验的是最终树,不是这个中间提交。
os-zhuang added a commit that referenced this pull request Aug 7, 2026
纯生成物,由 `pnpm --filter @objectstack/spec build && … gen:docs` 在合并后的
源码树上整体产出(#4675 第四步),而非把两侧各自的生成物文本拼接。

与 #6211(#5340 内联长枚举省略)同树共存已核实:
- 省略标记 `… +N more` 全语料 160 处,与 origin/main 完全一致(160)。
- 两者受影响页面交集 33 张,逐页复核 description 区(本 PR)与 type 单元格
  (#6211)各自的效果同时在位。例如 integration/connector.mdx 第 68 行「另见」
  为单个可点链接(#6136),同页 3 处 `… +N more`(#6211);security/explain.mdx
  开篇跨行行内代码跨度完整(#5553),同页 4 处省略标记。
- 验收复跑:`[../[` 归零;按段落计未配对反引号归零;代码跨度内 `\{` 残留归零。
- 全量 216 张参考页经 @mdx-js/mdx 编译通过。
- 无页面新增或丢失开篇描述。
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 size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gen:docs 内联形状里的长枚举不省略,单个类型单元格可达约 900 字符(BulkActionDef.params 实例)

2 participants