Skip to content

ci(lint): 把 check:merge-driver 的两个 self-test 接进 lint.yml 根 check 行 - #6079

Draft
os-zhuang wants to merge 1 commit into
mainfrom
claude/issue-6008-merge-driver-selftest-ci
Draft

ci(lint): 把 check:merge-driver 的两个 self-test 接进 lint.yml 根 check 行#6079
os-zhuang wants to merge 1 commit into
mainfrom
claude/issue-6008-merge-driver-selftest-ci

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #6008

改了什么

.github/workflows/lint.yml 的 ESLint job 末尾追加一步(全 PR 唯一改动,27 行新增,其中 25 行是解释性注释):

      - name: Merge-driver wiring gate
        run: pnpm check:merge-driver

位置按分诊指定:加在已有的那排根 check:* 步骤之后(同形先例就在同一 job:check:nul-bytes / check:doc-authoring / check:node-version / check:workflow-status-functions)。⛔ 不新建 job;⛔ 不动 typecheck job 段(#5827 停放中在那段有未落地编辑,与本改动不相交);⛔ 不动任何脚本、.gitattributescontent/docs/releases/

为什么

check:merge-driver = node scripts/git-merge-regen.mjs --self-test && node scripts/check-regen-pending.mjs --self-test,此前没有出现在任何 workflow 里.githooks/pre-commit 跑的是 check-regen-pending.mjs主路径(无 pending marker 时立即退出),不是 --self-test。所以这两个 self-test 在 CI 上从未执行过,只有有人手工跑根脚本时才现形 —— 与 #4690 同类形态(一道被归档为「在别处跑」的门禁,实际哪里都没跑)。

它们守的是 #4675「生成物不走文本合并」那套地基。merge=os-regen 的两半都是配置:一边 .gitattributes 的路由,一边 scripts/regen-artifacts.mjs 的表。两边可以在任一方向上失衡而全仓无人报错 —— 表里声明了但没路由的路径照旧走文本合并;路由了但表里没声明的路径,驱动拒绝处理、合并留在 CONFLICTED 且没有任何解释。而 git-merge-regen.mjs 自己的注释就写着:这类失衡「只有跑 self-test 才看得见」。

前提复核(第三遍,独立于 issue 正文与分诊读数)

origin/main 9e3709a4 上自己重跑了一遍,零命中成立;并按惯例做了邻近词阳性对照,证明 grep 本身是好的:

$ for f in $(git ls-tree -r --name-only origin/main .github/workflows/); do
    git show origin/main:$f | grep -Hn "merge-driver\|git-merge-regen\|check-regen-pending" /dev/stdin | sed "s|/dev/stdin|$f|"
  done
(空 —— 零命中)

$ # 阳性对照:同族根 check 确在
.github/workflows/lint.yml:124:        run: pnpm check:nul-bytes
.github/workflows/lint.yml:132:        run: pnpm check:doc-authoring

接线前先证明它在当前 main 上是绿的(把一条红的检查接进 CI 是另一件事,那会是 STOP-and-report):

$ pnpm check:merge-driver
git-merge-regen --self-test

✓ .gitattributes ↔ regen-artifacts.mjs agree on 9 path(s)
✓ all 18 gen:/check: names resolve in @objectstack/spec
✓ .githooks/pre-commit is executable in the index (100755)
✓ merge.os-regen.driver resolves in THIS worktree (scripts/git-merge-regen.mjs)
✓ end-to-end: conflicting packages/spec/spec-changes.json merged without markers and recorded as pending

✓ merge driver wiring is consistent (7 path(s) deliberately excluded).
✓ a directory with no dist/ reads as STALE (conservative default)
✓ a directory with no json-schema/ reads as STALE (conservative default)

✓ check-regen-pending self-test passed.

CI 可运行性(逐条对过,不是假设)

  • 两个 self-test 只用 node 内置模块 + git + 一个 POSIX shell,不需要 build,秒级;check-regen-pending --self-test 明确「touches no repo state」。
  • registeredDriverResolves() 在 CI 上两条分支都绿:pnpm install --frozen-lockfile 会跑根 prepare(setup-git-hooks.mjs)把 merge.os-regen.driver 注册成 worktree 相对的 $(git rev-parse --show-toplevel)/scripts/git-merge-regen.mjs,在检出里可解析;若 CI 以 --ignore-scripts 安装则读作「未注册」,脚本明确把它当受支持状态(回落到 spec 生成物没有 merge driver:两个 PR 各改几行,语义上是集合运算,却每次都打成文本冲突 #4675 之前的文本合并)并返回绿。
  • endToEnd()mkdtemp 的临时仓里 git init --initial-branch=main 并自带 user.email/user.name,不依赖 runner 的全局 git 身份。
  • hookIsExecutable() 读的是 index 里的 mode(git ls-files -s),普通检出即可。

同步跑过会被这次改动影响的相邻门禁,均绿:check:workflow-status-functions(真 YAML 解析,扫到 22 个 workflow / 39 job)、check:node-version(23 个 setup-node,全 Node 22)、check:nul-bytes(5780 个 tracked 文本文件,无原始控制字节)。

与在飞 #5837 的交互(给 spec 座位的知会,不是异议)

#5837S4 收尾会重写 .gitattributes 的 os-regen 路由(单体文件退役、路由改指分片路径),而本 PR 接进 CI 的这一步恰好强制 .gitattributesscripts/regen-artifacts.mjs双向对账。因此:S4 若只改其中一边(例如 .gitattributes 指向分片、表里仍是单体路径,或反过来),这一步会在你们的 PR 上直接红,报文会点名到具体路径。

这是门禁在做本职工作 —— 分片正是最容易让两边失衡的改动形状,而失衡的后果是合并驱动对某条路径静默失效。写在这里只是让 spec 座位提前知道多了这一道,不必等 CI 才发现。同理,S1/S2 若改了 gen:/check: 的脚本名而没同步表,reconcileScripts() 也会红。

changeset

无。纯 workflow 改动,不发布任何包,按现行惯例(#5292)走 skip-changeset 标签路径,不写空 frontmatter changeset(#6049 第二个提交记录了那条只作为最后手段的理由)。

自证

本 PR 自己就会跑到这条新步骤 —— lint.yml 对每个 PR 生效,所以这次 ESLint job 的 Merge-driver wiring gate 一步就是它确实在跑的直接证据。


Generated by Claude Code

`check:merge-driver`(`git-merge-regen.mjs --self-test` +
`check-regen-pending.mjs --self-test`)此前没有出现在任何 workflow 里:
`.githooks/pre-commit` 跑的是 check-regen-pending 的**主路径**(无 pending
marker 时立即退出),不是 `--self-test`,所以这两个 self-test 在 CI 上从未
执行 —— 只有有人手工跑根脚本时才现形(#4690 同类形态)。

它们守的是 #4675「生成物不走文本合并」的地基:`.gitattributes` 与
`scripts/regen-artifacts.mjs` 的双向对账、表里 `gen:`/`check:` 名字在
`@objectstack/spec` 可解析、pre-commit 在 index 里可执行(100755)、本 clone 的
`merge.os-regen.driver` 可解析(#4868),外加一次对真 git 的端到端合并证明;
第二个 self-test 钉住陈旧判定的危险方向(「该说 stale 时说了 fresh」)。
对账一旦失衡,合并驱动会对某个路径静默失效。

按分诊裁定加在 lint.yml 既有的那排根 `check:*` 步骤之后(同形先例:
`check:nul-bytes` / `check:doc-authoring` / `check:node-version`),⛔ 不新建
job。纯 git + fs,不需要 build,秒级。

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

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

Request Review

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

Labels

skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants