Skip to content

gen:docs 把数组内的 passthrough 对象渲染成 Record<string, any>[],抹掉已声明键 —— #4001 战役每个 open 分类站点都会复发 #4912

Closed

Description

@xuyushun441-sys

从 PR #4909(bulk-action options → .passthrough())的执行报告转立案,发现者为该单 dev,记录在报告的 out_of_scope_findings 里但未立案 —— 这里补上(PD#10)。未认领。

症状

gen:docs 对「数组元素是 .passthrough() 对象」的站点,把参考页上的类型渲染成 Record<string, any>[],把已声明键的形状整个抹掉。实例见 PR #4909 的 content/docs/references/ui/bulk-action.mdx diff:

- | **options** | `{ label: string; value: string \| number \| boolean }[]` | …
+ | **options** | `Record<string, any>[]` | …

options 仍有两个必填键(label / value,零容忍缺失,测试钉着),但作者面文档显示成「无形状」。一个有形状、部分开放的对象,在文档上退化成完全无形状 —— 正是 strip 时代那种「声明与呈现不符」反过来。

为什么会复发

#4001 战役的分类规则会持续产出「declared 键 + 刻意 passthrough」的站点(dashboard widget config、BulkActionParamSchema 本体等已是;后续 data/ 的 mixed 裁定还会新增)。每个这样的站点落地那天,它的参考页都会把声明的键抹掉。#4909 用该键的 .describe() 手工补了散文 —— 那是逐点补偿,不是修复。

建议修法

渲染器对 passthrough 对象输出「声明键的完整形状 + 开放性标记」(如 { label: string; value: … } & Record<string, unknown> 或表格 + 「接受额外键」一行),而不是塌缩成 Record<string, any>。落点在 packages/spec/scripts 的文档生成(build-docs / json-schema 投影),动之前按战役规矩先在 bulk-action 这个已知站点上证红。

关联:#4001(战役)、PR #4909(实例与临时补偿)、#4852(台账重测)。

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:specDomain lane — seat card indexed by label:pm:seat
    on Aug 3, 2026
  2. added
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 4, 2026
  3. self-assigned this
    on Aug 4, 2026
  4. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    Contributor

    认领:PM 循环第 1 轮(v17 ci-tooling 工作集 / C 包 #5163)
    会话:session_01FTszibd6C8sUCCZnM4VcrL
    分支:claude/issue-4912-gen-docs-passthrough-shape
    Worktree:objectstack-issue-4912
    域:domain:spec-tooling
    文件面:packages/spec/scripts/**(build-docs / json-schema 投影渲染器)、content/docs/references/**(整体重生成,先在 ui/bulk-action.mdx 站点证红)、.changeset/*

    ⛔ 本单不碰任何 packages/spec/src/** zod 源文件;⛔ content/docs/releases/ 零改动。


    Generated by Claude Code

  5. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    Contributor

    PM 复核(第 1 轮):ACCEPT → PR #5339

    会话 session_01FTszibd6C8sUCCZnM4VcrL。逐条对 GitHub 与 origin/main 实况核验:

    通过项

    • 前提成立,且是先证红后动手:bulk-action.mdx:98 在 origin/main 上确为 Record<string, any>[],而 gen:schema 给该节点产出的是 properties + required: [label, value] + additionalProperties: {} —— 两个事实同时存在,渲染器把它们做成了互斥。
    • 根因判断正确且有普遍性:formatType() 先判 additionalProperties 后判 properties;全量语料 130 个节点同时带两者,分布在 40 个 schema 文件。这解释了「未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 战役每个新分类站点都会复发」。
    • 括号是 dev 自己发现的必要条件,不在 issue 里:A & B[] 在 TypeScript 里是 A & (B[]),朴素修法会用另一种错误换掉原来的错误。hasTopLevelIntersection() 的深度扫描正确忽略 {} / <> / [] / () 内部运算符,测试里 Enum<'a' | 'b'> 与 markdown 链接两个用例专门钉住这点。
    • 抽取先证明行为中性(244 文件重生成、diff 为空)再落修复 —— 这一步把「只能跑完整个生成器再 grep .mdx」变成可单测,正是该塌缩能整场战役无人察觉的原因,方法论对。
    • 反向验证方向事前预判为红且如实:恢复缺陷后 6 failed / 6 passed;绿的那 6 个恰是「本来就渲染对的形状」——dev 对此的解读(渲染器在所有有人想到去看的形状上都是对的)是本单最有价值的一句。
    • 重生成影响面我独立点算过:protocol 3 + startup-orchestrator 1 + auth-config 1 + bulk-action 2 + dashboard 1 + view 4 = 12 个单元格 / 6 页,与报告一致;逐页看均为恢复被抹掉的声明键,无一页丢失形状。socialProviders 那处嵌在 Record<string, …> 值位的交叉类型无需括号,正确。
    • 空 properties: {} 的 6 个退化节点行为逐字保持,未印出 { } & Record<…>。
    • 范围合规:未碰 packages/spec/src/**、未碰 strictness 台账、⛔ content/docs/releases/ 零改动;changeset 齐备;check:docs 244 文件同步。

    衍生单(已核验并分诊)

    ⚠️ 入队纪律(落地时执行):content/docs/references/** 在 .gitattributes:46 是 os-regen 驱动路径,该驱动会 exit 0、零冲突标记地静默吞掉一侧改动。入队前四步不得省:git merge origin/main → git checkout origin/main -- content/docs/references/** → 整体重新生成(⛔ 绝不文本合并)→ 断言 12 个单元格与兄弟单条目都还在。spec 车道当前有落地链在飞,该目录有并发重生成风险。

    CI 全绿后转 ready 入队。#5338 / #5340 与本单同文件面(format-type.ts),须串行,排在本 PR 合入之后。


    Generated by Claude Code

  6. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    入队状态(避免留下不可解释的半状态):PR #5339 24/24 检查全绿,验收已通过,但 draft→ready 与 auto-merge 都是 GraphQL-only 操作,而本账号 GraphQL 配额此刻 0/5000(REST core 尚有 14907/15000)。按 skill 的配额纪律排队等恢复(~12 分钟)而非重试,复核结论不因配额延迟 —— 已在上一条发出。

    恢复后转 ready 入队,并按 os-regen 四步核对 content/docs/references/**(该目录在 .gitattributes 是驱动路径,spec 车道有并发重生成)。入队后按 gh-readonly-queue/* 分支 + origin/main 双读数跟到 MERGED。


    Generated by Claude Code

  7. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    入队前拦下:os-regen 路径已被 main 改动,先同步再入队

    PR #5339 验收结论不变(ACCEPT),CI 24/24 全绿、mergeable_state: clean。但未入队 —— 入队前按纪律核对生成物冲突面,发现自本 PR 的 base(c7406b0)以来 main 已改动它要整体重新生成的同一批 os-regen 驱动路径:

    content/docs/references/ui/{animation,dnd,index,keyboard,offline,touch}.mdx + meta.json
    docs/audits/2026-07-unknown-key-strictness-ledger.counts.md
    docs/protocol-upgrade-guide.md
    packages/spec/{api-surface,authorable-surface,authorable-surface.base,json-schema.manifest,spec-changes}.json
    

    .gitattributes 把这些路由到 merge=os-regen,该驱动 exit 0、零冲突标记,却静默丢掉一侧改动。若直接入队,要么吞掉本 PR 的 12 个单元格,要么吞掉 spec 车道刚落地的那 7 张 references/ui/* 页 —— 而且两种情况下 CI 都不会红。

    已回派同一 dev 走四步(同路径同分支恢复 worktree → git merge origin/main → git checkout origin/main -- <生成物> → 整体重新生成 → 断言两侧条目都还在)。同步推送、CI 重跑全绿后入队。

    同批另外三单(#5344 / #5342 / #5336)已入合并队列 —— 它们不拥有任何 os-regen 文件的改动,merge 驱动没有属于它们的东西可吞;若其生成物因 main 变动而 stale,check:generated 会在队列重建时判红而非静默落地。


    Generated by Claude Code

  8. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    os-regen 四步同步复核:通过;dev 的一处更正我采纳,并已立单 #5358

    会话 session_01FTszibd6C8sUCCZnM4VcrL。

    ⚠️ 先记我派发词里的错误(dev 更正,PM 已核实)

    我在同步指令里写「核 main 新落地的那 7 张 references/ui/* 页内容完好、没有被你的重生成回退掉」——说反了。实测:

    $ git log --oneline origin/main -1 -- content/docs/references/ui/animation.mdx
    c89d18c16 feat(spec)!: 退役 ui/ 五个没有承载键的交互配置文件 …(#4988)(#5321)
    $ git cat-file -e origin/main:content/docs/references/ui/animation.mdx
    → animation.mdx DELETED on main
    

    main 是删掉了其中五张(animation / dnd / keyboard / offline / touch),只重生成了 index.mdx 与 meta.json。所以第 4 步对那一侧的断言应当反转:那些页必须保持删除,而不是完好存活 —— 因为用退役前的 schema 重生成,完全可能把它们复活,那才是这个方向真正的危险。dev 自行发现并按正确方向断言,证实五页仍然缺席、meta.json 里退役名零命中。这比我要求的检查更严。

    四步同步核验

    步 证据
    1 merge exit 0,零冲突标记;os-regen-pending 为空 —— 两侧生成物路径实际不相交,驱动从未被调用
    2 取回 9 条 os-regen 路径全部 git checkout origin/main --;并确认回退真实发生(bulk-action.mdx:98 读回旧的 Record<string, any>[])
    3 重生成 gen:schema && gen:docs → 1619 schemas / 239 pages
    4 双侧断言 我方:git diff --stat origin/main -- content/docs/references/ = 恰好 6 文件 / 12 增 / 12 删,逐行复看均为恢复声明键;main 方:五张退役页仍缺席、ui/index.mdx 与 meta.json 与 main 逐字节相同

    最硬的一条旁证:重生成后的树与 merge 提交逐字节相同(git commit 报 nothing to commit)—— 独立重生成证明了没有一侧被吞,而不是假设。实现体抽查也做了(ViewItemWire 在重生成的 ui/view.mdx 里 3 处、退役墓碑在升级指南里完好)。

    如实记下:本次的具体碰撞风险没有兑现 —— 两侧改的是不同文件,驱动根本没被调用。但同步仍有净收益:它暴露出 PR 正文里 244 页 / 313 files / 8034 tests 的验证数字已因 main 的退役而过时(实为 239 / 309 / 7897),dev 已在 PR 评论里更正而不是留着让人当作已核验的事实读。入队前做这个检查的成本是一轮,不做的代价是静默丢改动 —— 这个不对称仍然成立。

    dev 的 open question:确认选 A(保持 main 的字节)

    packages/spec/authorable-surface.base.json 不随本 PR 推进锚点。理由即 dev 自己给的:该文件描述写着「written only from a git-resolved baseline — never from the build that is being checked」,推进它是维护者可见的动作,不该由一个 packages/spec/src/** 零改动的文档渲染 PR 顺手做掉。这属于 PM 权限内的范围纪律判断,不上交。

    它附带报告的那个观察是真问题,已立 #5358:build-schemas.ts 每次运行都重锚该文件,包括 --check 模式,于是 (1) 一个「检查」会改工作区(与 #4723 同类),(2) 任何无关 PR 只要本地跑过门禁并提交,就能把删除门的锚点静默推进、连带抹掉 110 个刚退役的键,而两种状态门禁都判绿。dev 没有自行处置而是上报,处置正确。

    CI 正在新 head(4ee6dc87)上重跑,全绿后入队。


    Generated by Claude Code

  9. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    已落地 —— PR #5339 经合并队列合入 main(0b51bb600)

    会话 session_01FTszibd6C8sUCCZnM4VcrL。本单是 os-regen 驱动路径单,落地判据按双侧核(队列合并同样会静默吞并):

    A 侧 —— 本 PR 的 12 个单元格是否都在:

    $ git grep -c "label: string; value: string" origin/main -- content/docs/references/ui/bulk-action.mdx
    1                                    # 立案样本 options 已恢复声明键
    $ git grep -c "& Record<string, any>" origin/main -- content/docs/references/ui/view.mdx
    4                                    # gantt ×2 + tree ×2 的交叉类型都在
    $ git grep -c '`Record<string, any>\[\]`' origin/main -- .../bulk-action.mdx
    0                                    # 旧的塌缩形态零残留
    

    B 侧 —— main 的退役页是否被重生成复活:

    animation.mdx / dnd.mdx / keyboard.mdx / offline.mdx / touch.mdx → 全部仍缺席 ✓
    

    两侧都对。B 侧才是真正的风险面:#5321 刚把这五个交互配置文件退役,如果同步时用退役前的 schema 重生成,这五张页会被复活,而 os-regen 驱动会 exit 0、零冲突标记地让它过去。dev 在同步时自行发现我的派发指令把方向写反了(我写「核这些页完好」,实际应是「核这些页保持删除」)并正确反转了断言 —— 这个更正比我要求的检查更严。

    C 包升级体验 4 单已落地 3 单(#4971 / #4749 / #4912),#4990 在飞。

    同文件面的两个衍生单 #5338(164 处数组括号)与 #5340(长枚举撑爆单元格)现已解锁串行位,但本会话按维护者暂停指示不再派发,留给下一任。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looptarget:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GAtoolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions