Repository navigation
[decision] the doc-snippet gate's specifier map reads dependencies only — and two packages were silently supplying React's types to the whole documentation corpus #8919
Description
Activity
- addeddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
on Sep 10, 2026 决策分析 —— 总监席第 21 场 · 批 #110 · 2/5(
session_01QVMnxyWBx8cAQMsV6akDV9,2026-09-10T08:2xZ)Governing text:
scripts/check-doc-snippet-types.mjs头注(dependencies-only「fails CLOSED, and widening it later is a visible edit with a reason, not a silent drift」);objectui#6120 裁定(2026-08-24,按名否决在根 manifest 声明);objectui#8059 pin 头注(已登记「one remedy reads as impossible on a peer」)。不改协议,改一道门禁的映射规则。一句话问题:文档示例的类型检查只认包的
dependencies;两个包在那里"顺手"装了 React,等于一直在替整个文档语料供应 React 的类型。把它们修对(React 应当是 peer——否则消费者树里出现第二份 React,Invalid hook call),34 个示例立刻失去类型检查、CI 长红,一个已发布的缺陷(objectui#8303)因此落不了地。选项 × 真实代价(卡面已用两棵对齐树测死 B / C / D / E)
做什么 客户可感知的后果 A 教门在 specifier 是工作区自身能解析的 peerDependencies时也映射它,并同笔放宽那条 pin#8303 的修复落地;34 个示例继续被检查;对真正可选的 peer 削弱了一点 fail-closed A′ A 再收窄:只对显式白名单( react,react-dom)放行同上,削弱面最窄;下一次撞到别的 peer 再来一次 E 退回 manifest 那一半 已发布的双 React 缺陷继续发布 业务含义直译:A=「一个 React 组件库的消费者不可能没装 React,把它当可选依赖是门在自欺」;E=「为了门绿,让已发布的缺陷继续发布」。
四轴:① A 是可见、带理由的放宽,正是门自己预告的演化路径;缩小「React 是可选 peer」这个假特例。② 拉动具名:#8303 修复已完成,PR #8911 两次判词在案、红在这一道上。③ 削弱的只是对真正可选 peer 的 fail-closed;限定「工作区自身能解析」(A′ 再加白名单)把削弱收到最窄;失败方向仍是拒收(TS 报错),不静默。④ 一次门禁改动,不新增机制。
推荐:A′(A 的形状 + 显式白名单
react/react-dom,白名单在门的头注里带理由)。回退:A。⛔ 不推荐 E。
置信缺口:本席未复跑 34 个 block 的 A/B;未验证packages/react-runtime/README.md里typeof import('react')那条扫描器盲区(卡面顺带项);门自己的范例(packages/layout/README.md"Usage with React Router")无论如何要重指。裁后执行:devx@objectui
pm:queue(改门 + 改 pin + 重指范例 + 头注写理由);#8303 加Blocked-by:指向本卡;PR #8911 待门落地后重跑。os-decision-facets
- ① 项目长远合理性:A′ 是门自己预告的可见放宽,缩小「React 是可选 peer」的假特例。
- ② 实际业务拉动:finding(deps): @object-ui/layout and @object-ui/plugin-dashboard pin react in dependencies while also asking for it as a peer #8303 已发布缺陷的修复被它挡住,PR fix(deps): stop layout and plugin-dashboard installing a React of their own #8911 红。
- ③ 防 AI 犯错:白名单 + 工作区可解析双限定,失败方向仍是拒收。
- ④ 创业阶段不扩散:一次门禁改动,零新机制。
推荐 A′;置信缺口:未复跑 34 block 的 A/B。
Generated by Claude Code
裁决落卡 —— 批 #110 第 2 项:A′ 的意图已由落地形状满足,本卡关闭(总监席第 21 场
session_01QVMnxyWBx8cAQMsV6akDV9,2026-09-10T08:4xZ)出处:维护者本场对话「其他同意」(对分析 5615377455 推荐 A′ 的回批)。鲜度门:本卡无新评论;但卡外事实先于回批:PR #8911 已于 2026-09-10T06:47Z 合并(objectui#8303 关闭 completed),其 diff 含
scripts/check-doc-snippet-types.mjs+173/−60 与该门的 pin +166/−25。origin/main上该文件头注(:327-:344, :1285-:1298)现读:specifier map =dependencies加上该包自己peerDependencies里、且peerDependenciesMeta未标 optional 的必需 peer,从该包目录解析。裁定:落地形状 = 「必需 peer 才映射,可选 peer 仍 fail-closed」。它比 A′ 的按名白名单更结构化:
peerDependenciesMeta.optional是机器可读的那条线,而react恰是必需 peer。⇒ A′ 的意图(削弱只及消费者不可能不装的 peer;失败方向仍是拒收)已被满足,⛔ 不再另开白名单。门自己的范例(packages/layout/README.md"Usage with React Router")是否已重指、typeof import('react')的扫描盲区,两条卡面顺带项未由本席核,留给 devx 席在下一次触碰该门时顺手看。状态:关闭
completed(决定已做且已执行)。同笔修 #8303 的半状态(关闭卡仍带pm:dispatched+needs-user-decision+ assignee)。
Generated by Claude Code
由
domain:ui派发席(session_01611D6ZaRaMmwTNQmSbk8MH)在 objectui#8303 的实现轮后立卡。全部读数由该卡实现席在两棵对齐的树(均为origin/main4fa0eb9c4)上实测,⛔ 非推断。Generated by Claude Code.一、被测出来的结构事实
scripts/check-doc-snippet-types.mjs的 third-party specifier map 只从dependencies构建(刻意如此,⛔ 不读peerDependencies、⛔ 不读devDependencies)。而
packages/layout与packages/plugin-dashboard是全仓仅有的两个在dependencies里声明react的已发布包。⇒ ⭐ 这两个包一直在为整个文档语料供应 React 的类型解析。 一旦把
react从它们的dependencies移走(objectui#8303 要修的正是这个缺陷 —— 一个库同时向宿主要 React 作为 peer、又自己 pin 一份,会把第二份 React 拉进消费者的树,是Invalid hook call的经典成因),这道门立刻拒收 34 个 block。其中只有 2 个在那两个包自己的 README 里;另外 32 个属于 19 个文件、分属本卡根本不碰的包。
对齐两棵树的 A/B(都在
4fa0eb9c4,都先跑过门自己的 scoped build)Doc Snippet Type CheckRoot bound: no block imports a specifier that resolves only through the ROOT manifest.·638 of 638 judged, 0 failed· exit 0Root bound: 34 block(s) … (react) … NOT semantically checked·604 of 638 judged, 1 failed· exit 1⭐ 机理由门自己打印,⛔ 非推断:main 上
89 specifier(s) mapped from the declared dependencies of 34 imported package(s),分支上88。恰好一个 specifier 离开了 map:react。 门自身的全部对照(resolution / sentinel / positive / undeclared / root-declared)两侧读数完全相同。Test (shard 1/4)就是scripts/__tests__/check-doc-snippet-types.test.ts里断言同一个 root-bound 不变量的那条 pin,收到的是同一批 34 条。⇒ 修一次两边都动,⛔ 不是两个问题。二、⛔ 为什么其它路线都已被实测关死
门自己推荐的解法对⚠️ 而那个范例本身就是被拒的 34 个之一(它 import
react不成立。 它的拒绝信息点名declare conststand-in,并把packages/layout/README.md的 "Usage with React Router" 作为范例 ——ComponentType/ReactNode,恰恰因为该包在dependencies里声明了 react)。带 trap 的一次性探针照做后:block 跳出 bound(
Root bound: 33·605 of 638),直接撞出 6 个新的 TS2786 —— 一个当作 JSX 组件使用的 stand-in,必须让返回类型可赋给 React 真实的ReactNode,⭐ 而真实的 React 类型正是刚离开 map 的那个。JSX 本身仍能编译(这就是另外 604 个还过的原因),因为 JSX runtime 类型经根解析、不走这张 map ⇒ 这类 block 既 import 不到 React 的类型,也无法自己手写出来。量化:34 个里 27 个含 JSX,属探针实测失败的那一类;仅 7 个不含 JSX,其中数个 import
useState/useEffect/forwardRef,stand-in 还得复刻它们真实的泛型签名才谈得上「检查了什么」。@types/react在全仓零个包的dependencies里(30 个包的devDependencies里有),⇒ React 的类型词汇经这张 map 无路可达。⛔ 也没有「记账接受」这条路。 那条 pin 是
expect(offenders).toEqual([])平铺断言 —— 无豁免表、无账本、无理由字段。⇒ 34 条不可能被记成已接受的债,即便有人想这么做;CI 会一直红,而不是安静降级。三、决策块
① 待决问题
doc-snippet 门的 specifier map 该不该把「工作区自己能解析的
peerDependencies」也纳入?② 选项 × 代价
check-doc-snippet-types.mjs在工作区自身能解析该 specifier 时,也映射peerDependencies里声明的它;并同笔放宽那条 pinBlocked-byfragment③ 已测量、可据以决策的事实
react在本仓的实际处境不匹配:门的理由是「a peer is a requirement ON the consumer that may be unmet」;④ 不决策的后果
objectui#8303 是一个已发布的缺陷(两个包同时向宿主要 React 又自己 pin 一份),修复已完成且正确,但无法落地:它一落地就让 34 个文档 block 失去语义检查,而那条 pin 不接受任何记账形式。⇒ 要么这个已发布缺陷继续留在库里,要么这道门变。没有第三种稳定状态。
四、⛔ 本卡不主张什么
react这一类(消费者不装就完全用不了的 peer)该不该在里面。五、⭐ 一条值得单独记下的方法论
实现席自陈:「我的『改了门/工具脚本就要跑它的测试』这条规则没有触发,因为这次 diff 改的是门所消费的 manifest 数据。」
⇒ ⭐ 一份被某道门消费的 manifest 数据,欠这道门的测试套件,而不只是欠它的可执行文件。
六、相关
objectui#8303(触发本卡的缺陷修复,PR #8911,因本卡而红)· objectui#8059(该 pin 的头注,已登记「one remedy reads as impossible on a peer」)· objectui#6120(2026-08-24 裁定,按名否决根 manifest 声明)· objectui#8300(同样两个 manifest 上的 react-dom peer 约定,仍开着)。
ⓘ 顺带两条,⛔ 不随本卡裁:门的范例本身(
packages/layout/README.md"Usage with React Router")就是被拒的 34 个之一 ⇒ 无论怎么裁,那个范例都得重指,否则门的建议自我否定;另packages/react-runtime/README.md里typeof import('react')这种写法不被 bound 扫描器当作 import,⇒ 同一个 block 会因 import 语法不同而拿到两种判决。