Skip to content

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

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

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

@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 6, 2026 — with Claude
@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 22:18
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Any commits made after this event will not be merged.
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