现象
以 origin/main = d8e7af141 为基线,34 个 open PR 中有 19 个 mergeStateStatus=DIRTY、14 个 BEHIND。三个被评审阻塞的 head(#4662/#4663/#4651)只是其中之一。
冲突锚点普查(git merge-tree --write-tree --name-only origin/main <head> × 全部 19 个 DIRTY head)
| 冲突锚点 |
PR |
数量 |
docs/architecture/rfcs/semantic-vocabulary-convergence-v0.md(+.zh-CN) |
4651 / 4662 / 4663 / 4664 |
4 |
docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md(+.zh-CN) |
3820 / 4061 / 4672 |
3 |
loopx/web/chat/index.html + loopx/web/chat/assets/index-*.js |
4156 / 4159 / 4363 |
3 |
loopx/bootstrap.py + loopx/chat_server.py + loopx/control_plane/projects/registry.py |
4418 / 4435 |
2 |
apps/presentation/dashboard/** |
3200 / 4023 |
2 |
| 单文件代码锚点 |
3412, 3718, 3834, 4111, 4125 |
5 |
也就是说:19 个冲突里有 7 个只卡在两对 RFC 文档上,3 个卡在同一个已提交的前端 bundle 上,两者都是可消除的结构性来源,而不是 19 个互不相关的漂移(我一开始只抽查了 3412/4111/4363 三个 PR,得到的结论是"互不相关",普查推翻了那个结论)。
两条结构性成因与可选做法
-
两组 RFC 文档的 execution ledger 是追加式的。 每个贡献者都在同一锚点追加自己的条目,于是任意两个并行 PR 必然冲突。这两组一共 7 个 PR 全部属于此类,冲突内容是机械的(双方都在同一位置追加),但每个作者都要各自 rebase 一次,评审方还要各自复跑一次 merge-readiness。
- 可选做法:每条 ledger 条目独立成文件;或在文档里约定与顺序无关的分节/命名;或明确一条"保留双方、只删冲突标记"的标准解法并写进
CONTRIBUTING.md。三者任选其一即可,本文不建议哪种,只指出现状的成本。
-
loopx/web/chat/ 下跟踪了构建产物。 index.html 与 assets/index-<hash>.js 每次重建都会改文件名,因此在任何一次重建之前打开的 PR 都会遇到 rename/delete 冲突(#4363 就是 index-DZ4wXv1S.js 在 main 被删除、在 PR 里被修改)。
- 可选做法:不跟踪构建产物(改为随发布重建),或只在发布流程里更新。
边界
- 这里没有任何内容层面的争议,也不建议降低任何合并护栏;只是说明"冲突数量"主要来自两条可消除的结构性来源。
- 其余 7 个 DIRTY 是各 PR 与快速前进的 main 之间的正常漂移,不属于上述两类。
- 复现:
git fetch origin '+refs/pull/<n>/head:refs/remotes/origin/pr-<n>' 后对每个 head 运行 git merge-tree --write-tree --name-only origin/main refs/remotes/origin/pr-<n>。
现象
以
origin/main = d8e7af141为基线,34 个 open PR 中有 19 个mergeStateStatus=DIRTY、14 个BEHIND。三个被评审阻塞的 head(#4662/#4663/#4651)只是其中之一。冲突锚点普查(
git merge-tree --write-tree --name-only origin/main <head>× 全部 19 个 DIRTY head)docs/architecture/rfcs/semantic-vocabulary-convergence-v0.md(+.zh-CN)docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md(+.zh-CN)loopx/web/chat/index.html+loopx/web/chat/assets/index-*.jsloopx/bootstrap.py+loopx/chat_server.py+loopx/control_plane/projects/registry.pyapps/presentation/dashboard/**也就是说:19 个冲突里有 7 个只卡在两对 RFC 文档上,3 个卡在同一个已提交的前端 bundle 上,两者都是可消除的结构性来源,而不是 19 个互不相关的漂移(我一开始只抽查了 3412/4111/4363 三个 PR,得到的结论是"互不相关",普查推翻了那个结论)。
两条结构性成因与可选做法
两组 RFC 文档的 execution ledger 是追加式的。 每个贡献者都在同一锚点追加自己的条目,于是任意两个并行 PR 必然冲突。这两组一共 7 个 PR 全部属于此类,冲突内容是机械的(双方都在同一位置追加),但每个作者都要各自 rebase 一次,评审方还要各自复跑一次 merge-readiness。
CONTRIBUTING.md。三者任选其一即可,本文不建议哪种,只指出现状的成本。loopx/web/chat/下跟踪了构建产物。index.html与assets/index-<hash>.js每次重建都会改文件名,因此在任何一次重建之前打开的 PR 都会遇到 rename/delete 冲突(#4363就是index-DZ4wXv1S.js在 main 被删除、在 PR 里被修改)。边界
git fetch origin '+refs/pull/<n>/head:refs/remotes/origin/pr-<n>'后对每个 head 运行git merge-tree --write-tree --name-only origin/main refs/remotes/origin/pr-<n>。