现象
2026-08-07 15:02,PR #6356(只改 driver-memory / driver-mongodb 的类型签名,一行 packages/spec 都没碰)在 TypeScript Type Check 上红:
❌ 1 authorable baseline line(s) were deleted without proof (#4650):
- ui/BulkActionDef:requiredPermissions — def reachable from the metadata-type roots;
the entry at d8e8d9cbc892 was LIVE (never tombstoned).
同一 job 在上面几行已经自陈了原因:
(shallow history — using origin/main tip d8e8d9cbc892 as the baseline anchor)
归因(已坐实,非猜测)
d8e8d9cbc 是 main 当前 tip,提交标题逐字是 feat(spec): declare requiredPermissions on BulkActionDefSchema (#6257) (#6332) —— 它新增了这个 authorable key。PR #6356 的分叉点是 4d552af3f,落后 main 三个提交:
d8e8d9cbc feat(spec): declare requiredPermissions on BulkActionDefSchema (#6257) (#6332)
cfb549db8 feat(runtime): standalone stack dispatches mysql:// ... (#6344)
fb363b20e docs(lint): 按实测改正 `normalized` 输入层的三条依据 ... (#6340)
4d552af3f ← #6356 / #6355 的 base
于是「main 上新增的键」在分支侧看起来就是「本 PR 删掉的键」。方向恰好反了。
根因是一行 CI 配置
resolveSurfaceBase()(packages/spec/scripts/build-schemas.ts:1024-1031)的逻辑本身是对的,注释也写明了意图:
// Merge base, so a branch behind origin/main is compared against what it
// FORKED from (keys added on main since then are not "deleted" here). In a
// shallow clone there is no walkable ancestry — fall back to the tip, which
// on a PR's synthetic merge commit is the merge base anyway.
const mergeBase = git('merge-base', 'HEAD', tip);
const rev = mergeBase.status === 0 ? mergeBase.stdout.trim() : tip;
括号里那句假设正是失效的一环。而问题在于 fallback 不是罕见降级,它是这个 job 的常态路径:
.github/workflows/lint.yml:378 的 typecheck job 用的是 actions/checkout@v7 默认 fetch-depth: 1。加上门自己那句 git fetch --quiet --depth=1,merge-base HEAD origin/main 在这个 job 里永远走不通 —— 每一次运行都落到 tip 分支。
而 tip == merge base 这个假设只在「合并 ref 是对着 main 当前 tip 生成的」时成立。合并 ref 是 PR 打开/更新时生成、随 main 前进而变陈旧的;本例中合并 ref 建于 4d552af3f,--depth=1 却抓到了 d8e8d9cbc,两者不等,门就看见了幽灵删除。
同一个文件里的兄弟 job 已经踩过并修好了同一个坑(.github/workflows/lint.yml:32-38,ESLint job):
- name: Checkout repository
uses: actions/checkout@v7
with:
# The slot-lookup ratchet compares the baseline against its state at
# the merge base with main — the only way to see a file being ADDED
# to the grandfather list. A shallow clone has no merge base, and the
# check would degrade to "not verified" on every run.
fetch-depth: 0
typecheck job 需要的是同一句话,只是失效方式更糟:ESLint 那道门 shallow 时降级为不校验,这道门 shallow 时降级为误报红。
影响面
任何分叉点早于「某个新增 authorable key 的提交」、且此后跑过 TypeScript Type Check 的开放 PR,都会在一个自己从未碰过的文件上红。#6332 于本日 15:00 前后合入,此刻 #6356、#6355 均命中。这是一个假红发生器:它按 main 的合并节奏周期性地扫过所有在飞 PR,而报错文案(「删除了 authorable 键」「未经证明」)指向的是一个严重的规范违规,读起来完全不像环境问题 —— 排查成本远高于修复成本。
建议修法
- 一行修复:给
lint.yml 的 typecheck job 加 fetch-depth: 0,注释比照兄弟 job 写明「这道门要走 merge base」。
- 把假设变成断言(建议一并做):
resolveSurfaceBase() 的 tip fallback 目前是静默正确性降级。既然 tip ≠ merge base 会产生假红而非假绿,这一路不该悄悄执行 —— 要么在 rev !== tip 无法判定时明确拒绝把「删除」判成违规(只报 ℹ️ 未验证),要么把「拿不到 merge base」直接变成对 CI 配置的显式报错。这样下次某个 job 忘了 fetch-depth: 0,红的是配置本身,而不是无辜 PR 的规范合规性。
现场处置
两个 PR 均已 update branch 合入 main 重跑,问题消失 —— 但那是绕开,不是修复;本单记录的是 CI 配置本身。
按 PD#10 只记录不修,未认领。
现象
2026-08-07 15:02,PR #6356(只改
driver-memory/driver-mongodb的类型签名,一行packages/spec都没碰)在TypeScript Type Check上红:同一 job 在上面几行已经自陈了原因:
归因(已坐实,非猜测)
d8e8d9cbc是 main 当前 tip,提交标题逐字是feat(spec): declare requiredPermissions on BulkActionDefSchema (#6257) (#6332)—— 它新增了这个 authorable key。PR #6356 的分叉点是4d552af3f,落后 main 三个提交:于是「main 上新增的键」在分支侧看起来就是「本 PR 删掉的键」。方向恰好反了。
根因是一行 CI 配置
resolveSurfaceBase()(packages/spec/scripts/build-schemas.ts:1024-1031)的逻辑本身是对的,注释也写明了意图:括号里那句假设正是失效的一环。而问题在于 fallback 不是罕见降级,它是这个 job 的常态路径:
.github/workflows/lint.yml:378的typecheckjob 用的是actions/checkout@v7默认fetch-depth: 1。加上门自己那句git fetch --quiet --depth=1,merge-base HEAD origin/main在这个 job 里永远走不通 —— 每一次运行都落到 tip 分支。而 tip == merge base 这个假设只在「合并 ref 是对着 main 当前 tip 生成的」时成立。合并 ref 是 PR 打开/更新时生成、随 main 前进而变陈旧的;本例中合并 ref 建于
4d552af3f,--depth=1却抓到了d8e8d9cbc,两者不等,门就看见了幽灵删除。同一个文件里的兄弟 job 已经踩过并修好了同一个坑(
.github/workflows/lint.yml:32-38,ESLint job):typecheckjob 需要的是同一句话,只是失效方式更糟:ESLint 那道门 shallow 时降级为不校验,这道门 shallow 时降级为误报红。影响面
任何分叉点早于「某个新增 authorable key 的提交」、且此后跑过
TypeScript Type Check的开放 PR,都会在一个自己从未碰过的文件上红。#6332 于本日 15:00 前后合入,此刻 #6356、#6355 均命中。这是一个假红发生器:它按 main 的合并节奏周期性地扫过所有在飞 PR,而报错文案(「删除了 authorable 键」「未经证明」)指向的是一个严重的规范违规,读起来完全不像环境问题 —— 排查成本远高于修复成本。建议修法
lint.yml的typecheckjob 加fetch-depth: 0,注释比照兄弟 job 写明「这道门要走 merge base」。resolveSurfaceBase()的 tip fallback 目前是静默正确性降级。既然 tip ≠ merge base 会产生假红而非假绿,这一路不该悄悄执行 —— 要么在rev !== tip无法判定时明确拒绝把「删除」判成违规(只报 ℹ️ 未验证),要么把「拿不到 merge base」直接变成对 CI 配置的显式报错。这样下次某个 job 忘了fetch-depth: 0,红的是配置本身,而不是无辜 PR 的规范合规性。现场处置
两个 PR 均已
update branch合入 main 重跑,问题消失 —— 但那是绕开,不是修复;本单记录的是 CI 配置本身。按 PD#10 只记录不修,未认领。