Skip to content

[Decision] The lockfile merge driver is DEAD in CI and LIVE for contributors — and the pin test cannot express both. Remove, re-point, or leave? #6436

Description

@os-warren

Filed by the domain:devx @ objectui execution seat (#5748), PM session session_012CZgmFFzqA9cX8tBMhvpFe, R35. ⛔ I do not set domain:* or type labels. needs-user-decision because the live half is a repository-wide contract and the recommended option relaxes an anti-vacuity guard — a gate-strength change, which this seat never self-adjudicates.

⚠️ #6391's implementation returned premise_still_valid: false and status: needs_decision. Half the premise is confirmed, half is falsified, and the falsified half is why nothing was deleted. PR #6434 ships only the measurement record; ⛔ nothing was removed.

✅ Confirmed — the CI consumer cannot fire

changeset-release.yml performs no local merge. Every git in it is the two git config lines, git status --porcelain twice, and git checkout -- . + git clean -fdq. merge / rebase / pull / cherry-pick / am / apply / revert all zero — ⭐ each zero-hit taken with a control term that hit the same file: rebase 0 vs base 2 · cherry-pick 0 vs pick 4 · apply 0 vs app 8 · revert 0 vs rev 5 · git am 0 vs git 10. The 28 merge hits are 26 lines of prose plus the 2 config lines.

The load-bearing reading, which the workflow file could not answer: changesets/action@v1 cloned and read at the tag (v1.9.0 = a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d), with src/git.ts and the executed dist/ bundle agreeing line for line. The action's complete git surface is checkout, reset --hard, add ., commit, push --force, config user.*. prepareBranch is checkout → reset --hard → commit → force-push. It never merges, rebases, or three-ways anything, and the bundle reads no .gitattributes and implements no merge driver at all — isomorphic-git's mergeTree/diff3 are not even bundled, only its error classes.

Confirmed rather than assumed: git checkout -- . restores tracked paths from the index to undo pnpm changeset:version — a discard, not a merge; and there is no pull_request trigger, so actions/checkout never takes a GitHub-computed merge ref either.

⛔ Falsified — the .gitattributes line is NOT dead

Item 4 of the removal unit has a live consumer, and it is the contributor path the guide section already cross-references: CONTRIBUTING.md tells contributors to configure this same driver and then run git merge upstream/main — a real local merge on the attributed path.

⭐ Measured in a scratch repo with one variable changed:

.gitattributes line result
present driver fires (DRIVER_FIRED_ON=.merge_file_IkVqkr), pnpm-lock.yaml regenerated
absent, nothing else altered identical merge ends in CONFLICT (content), conflict markers in pnpm-lock.yaml

⭐ The driver used was a marker script rather than pnpm install, so firing is observable rather than inferred.

The bind

The CI half is dead. The repository-wide half is live. And ci-cd-pipeline-doc.test.ts cannot express that — removing the CI config makes its own failure message demand deleting the section and the .gitattributes line, which would break the live contributor path.

⚠️ The unit is bigger than the card said. Not four things but six: removing .gitattributes also falsifies the pin test's own "keeps the .gitattributes half of the mechanism true" assertion, so that describe block joins any removal; and under full removal CONTRIBUTING.md's "Configure Git Merge Driver" section becomes instructions for a driver no attribute can ever select.

Options

A — Full removal as #6391 scoped it. Workflow config, last row, guide section, .gitattributes, plus (forced, not optional) the pin test's describe block and CONTRIBUTING.md's section. Honest and maximally simple — ⛔ but it retires a mechanism that is measurably live: every contributor who followed CONTRIBUTING.md goes back to hand-resolving pnpm-lock.yaml conflicts.

B — Remove only the dead CI half (the changeset-release.yml config and the table row), keep .gitattributes and CONTRIBUTING.md, and re-point the guide section at what the mechanism now is: a contributor-facing lockfile merge driver with zero CI consumers. ⚠️ Requires rewriting the pin test's model — its configuring.length > 0 anti-vacuity guard must become "zero is the correct measured answer", while the phantom direction and the .gitattributes assertion stay.

C — Change nothing beyond PR #6434. Keep the CI config as documentation-by-configuration; leave the recorded measurement in place.

四轴分析

① 实际业务需求. 两个消费者都被实测过,不是推断:CI 消费者证明不可能触发(force-push,无合并),贡献者消费者证明会触发(一变量对照的 scratch repo 两次运行)。服务活的、丢掉死的,是唯一与测量一致的选项。⛔ A 丢掉一个活的,C 留着一个死的。

② 项目长远合理性. 契约优先。.gitattributes 那条属性就是契约,workflow 里那份配置只是它的一个消费者,而这个消费者死了。⭐ 因为一个消费者死了就删掉契约,是把关系倒过来 —— 而这恰恰是本卡族已经清理掉的那两份死拷贝当初产生的同一种倒置。B 还让指南说的话在 workflow 来来去去时保持为真。

③ 防 AI 写代码犯错. 这一轴最强,而且是反对 A 的最硬理由。⚠️ 没有那条属性,冲突的 lockfile 合并会在 pnpm-lock.yaml 里面留下冲突标记,而一个被手工或 agent 编辑过的 lockfile 是高爆炸半径、且在远离病因处才失败的产物。有了它,文件由拥有它的包管理器重新生成。保留自动解决 = 让一整类错误在结构上不可达。

④ 创业阶段不扩散需求. B 删两行死配置,不新增任何面或能力,严格是收缩。代价是一次 pin 测试模型改写 —— 但那个测试已经是三张卡的主题,把它的模型改成与测量一致,比第四次重新推导便宜。

PM 推荐:B

⚠️ 但 B 的代价必须说清,这也是它必须由你裁而不是我裁的原因:那条 anti-vacuity 守卫存在,是因为一次零匹配的 grep 曾让双向断言同时空绿。放松它,就必须让替代断言(.gitattributes 半边,加上 phantom 方向)刻意地承接那份重量。⛔ 一个把 > 0 改成 === 0 却没有别的东西顶上的改写,就是在把这张卡族一直在修的缺陷重新装回去 —— 那属于门禁强度变更,本席不自裁。

⛔ 推荐是输入,不是放行。

⭐ The deeper defect, which will block the next attempt too

ci-cd-pipeline-doc.test.ts encodes a CI-only model of a mechanism that is repository-wide, so it cannot represent "dead in CI, live for contributors". That model, not the row, is what blocks the removal, and it will block the next attempt the same way unless it is the thing that gets changed.

Filed separately as #6436 so it outlives whichever option is chosen.

What PR #6434 already ships, under every option

The falsified-branch deliverable the dispatch asked for — the firing path recorded in the file so nobody re-derives this a fourth time — plus a correction: the row's stated reason ("version bumps rewrite the lockfile on the release branch") is measured false, because a rewrite is not a merge. ⛔ Nothing deleted; .gitattributes, CONTRIBUTING.md, the pin test and dependabot-auto-merge.yml are untouched.

Refs: #6391 · PR #6434 · #6358 / PR #6367 · #6369 / PR #6389 (the two dead rows already removed) · #6436


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions