
上一集解释了角色系统为什么能被外部理解——三个信号同时满足。这一集从反面看同一个问题:为什么核心不行?同样的仓库、同样的贡献者群体、同样的开源许可证——为什么叶子层有人碰,骨头层没人碰?
零的含义
再看一遍这个数字:
- 碰
roles/*.md的外部 PR:5 - 碰 harness 核心代码的外部 PR:0
- 碰 gate 逻辑的外部 PR:0
- 碰 review flow 的外部 PR:0
- 碰 extension system 的外部 PR:0
零不是”还没有”。零是一个信号。
它说明了一个筛选过程:有能力的人看了核心代码,评估了不确定性,然后选择不碰。这不是缺乏能力或兴趣——恰恰相反,能打开 flow-transition.mjs 读懂控制流的人,技术水平不会低。但读懂控制流和理解设计意图之间有一道鸿沟:代码告诉你系统做了什么,但不告诉你系统为什么不做别的。他们感知到了这道鸿沟,所以做了理性的选择:转向确定性更高的领域。
163 个人点了 star。39 个人 fork 了仓库。其中可能有人打开了 bin/opc-harness.mjs、读了 flow-transition.mjs、看了 gate-protocol.md。然后——关掉了文件,转头去写了一个角色 PR。
#12 是唯一一个接近核心的外部 PR——EP02 已经讲过这个故事:一行合理的 JSON.parse try-catch 保护,被关闭了,因为贡献者无法在 PR 描述里证明他理解了 catch 之后的连锁反应。EP02 从贡献者行为的角度分析了这件事——他们是理性的,碰最安全的层是最合理的选择。这一集从维护者的角度看同一件事:为什么核心没有发出”可以改”的邀请?
一行代码的修复,牵出了整个状态机的设计决策。 如果你不理解这些决策背后的权衡,你写的 try-catch 可能引入比它解决的更多的问题。
#12 是所有数据点里最有信息量的一个。它不是”没人来”——有人来了,写了完全正确的代码,但还是被拒绝了。原因不是代码质量,而是上下文缺失。这位贡献者面对的不是技术难题,而是信息不对称:他能看到代码应该做什么,但看不到代码为什么不能那样做。维护者脑子里有一张依赖关系图,贡献者手里没有。这种不对称不会因为贡献者更努力地读代码而消失——它只能通过维护者主动暴露决策上下文来弥合。
核心的三道墙
EP03 说外部人碰角色文件时,三个信号(看得懂、改得动、改坏了不怕)全部满足。核心代码的情况正好相反——三道墙挡在贡献者面前。
第一道墙:隐含知识
打开 flow-transition.mjs,你会看到代码。你看不到的是:
为什么选 digraph 不选 DAG? OPC 的流水线允许回环(gate FAIL → 回到 build 节点),所以不能用有向无环图。但回环有上限(maxLoopsPerEdge=3),所以也不是任意图。这个设计决策没有写在任何地方——它在我的脑子里。
为什么 synthesize 用 emoji 计数而不是大语言模型判定? S1E07 的结论:机械门禁优于大语言模型门禁,因为大语言模型门禁受语气锚定影响。这个决策的上下文分散在 S1E07 的叙述里——不在代码注释里,不在 ADR(架构决策记录)里。
为什么 maxLoopsPerEdge 设为 3? 不是计算出来的。是 S1 早期试了几次后拍脑袋定的。2 次太少(有时候第一轮审查的反馈需要两轮迭代才能完全修复),5 次太多(超过 3 次说明问题不在细节而在方向)。这个数字的理由不在代码里——在两个月前一个已经被上下文压缩清理掉的会话里。
这就是隐含知识(tacit knowledge)。代码告诉你”是什么”,不告诉你”为什么不是别的”。 理解核心的真实成本不是读 N 行代码——是重建 N 个被放弃的替代方案的上下文。
Cline(S2E04 分析过的项目)的 3,764 行 Cline.ts 有同样的问题。代码里能看到它用一个巨型 switch-case 处理所有工具调用。看不到的是:为什么不拆成多个文件?是有意的设计还是技术债?如果拆会破坏什么?没有人知道——除了写它的人。
隐含知识最棘手的地方在于:拥有它的人往往意识不到它是隐含的。作为维护者,我看 flow-transition.mjs 觉得逻辑清晰——因为我脑子里有全部上下文。但一个外部人看同样的代码,看到的是一堆没有注释的分支判断,每个分支背后都有一个他不知道的理由。我觉得”代码就是文档”,是因为代码对我来说确实就是文档——但那是因为我同时拥有代码和它背后的决策记忆。剥掉记忆,代码只是语法。
这就是为什么维护者常常低估外部人理解核心的难度。他们不是故意设门槛——他们真的以为代码已经说清楚了。知识的诅咒(curse of knowledge)在这里体现得淋漓尽致。
第二道墙:调试环境
改角色文件不需要跑任何代码,甚至不需要开发环境。改核心代码,你需要:
- 完整的本地环境:Node.js、npm、正确版本的依赖
- 真实的测试运行:
bash test/run-all.sh跑 109 个测试文件 - 端到端验证:不只是单元测试通过——需要跑一个完整的流水线(init → build → review → gate → transition)来确认改动没有破坏状态转移
- Claude Code 的 API 密钥:OPC 的审查节点调用真实的大语言模型。测试时需要 API 访问权限
第 4 点最致命。OPC 不是一个纯粹的确定性系统——审查节点的输出依赖于大语言模型的响应。你改了 gate 逻辑,想验证它能正确处理一个 FAIL 判定?你需要一个真实的审查输出,而那需要 API 调用。
角色文件的验证成本是零——不需要验证。核心代码的验证成本是搭建完整环境 + 消耗 API 费用。
这产生了一个筛选效应。能跨过这道门槛的人,必须已经拥有完整的开发环境、API 密钥、以及足够的动力来承担验证成本。换句话说,只有已经深度投入的人才有资格贡献核心代码。但深度投入的人通常已经在做自己的项目了——他们没有理由把时间花在别人的核心上。结果是:最有能力贡献的人没有动力,有动力的新手被门槛挡住。这不是一个能通过”欢迎贡献”的 README 标语解决的问题。
第三道墙:回滚信心
你提交了一个角色文件的 PR,合并后发现有问题。回滚:git revert,删掉那个文件,完成。影响范围:一个审查节点的一个视角消失。
你提交了一个 gate 逻辑的 PR,合并后发现有问题。回滚:git revert——但等一下。在 revert 之前的这段时间里,已经有多少个流水线运行经过了这个改动后的 gate?那些运行的判定结果是否正确?如果一个本应 FAIL 的审查被错误地判为 PASS 并进入了下一个节点,revert gate 逻辑并不能撤销已经发生的错误判定。
角色文件的回滚是无状态的——删掉文件就行。核心代码的回滚可能是有状态的——已经做出的判定不可撤销。
这种不可逆性改变了贡献者的心理预期。改角色文件时,最坏的结果是回滚,代价很小。改核心代码时,最坏的结果不只是回滚——还包括在发现问题之前已经造成的连锁影响。当下行风险从”可逆的不便”变成”不可逆的损失”,理性的贡献者会选择不冒这个险。这不是胆怯,这是风险管理。零个核心 PR 不是社区缺乏勇气的证据——是核心缺乏安全网的证据。
发出”可改”的邀请
问题定义清楚了。核心有三道墙:隐含知识、调试环境、回滚信心。三道墙的共同特征是:它们都不是技术难度造成的,而是信息缺失造成的。这不是贡献者的问题——是核心维护者(也就是我)的问题。
如果想让核心成为公共资产,需要正面应对这三道墙:
注意,这三道墙没有一道是关于代码质量的。代码可以写得很干净、测试覆盖率可以很高、命名可以很规范——核心仍然不会是公共资产。因为问题不在代码本身,而在代码之外的信息。很多项目把”提高代码质量”当成降低贡献门槛的手段,但对核心模块来说,这是在解决错误的问题。
应对隐含知识:写架构决策记录(ADR)。 不是代码注释——注释解释”这行做什么”,ADR 解释”为什么选 A 不选 B”。每个关键设计决策一条记录:digraph 不选 DAG 的理由、emoji 计数不选大语言模型判定的理由、maxLoopsPerEdge=3 的理由。让外部人能重建决策上下文,而不是只看到决策结果。
应对调试环境:提供最小化的本地验证路径。 Docker 化的测试环境,让 docker run opc-test 就能跑完所有测试。提供 mock 审查输出,让 gate 逻辑可以在没有 API 密钥的情况下被测试。降低验证的准入门槛。
应对回滚信心:明确标注变更的影响范围。 每个核心模块旁边有一个 IMPACT.md:改这个文件会影响哪些下游行为?最坏的失效模式是什么?如何验证改动是正确的?让贡献者在提交 PR 之前就能评估风险。
代码公开 ≠ 理解公开
这一集的核心论点可以用一句话概括:
代码公开不等于理解公开。
OPC 的代码在 GitHub 上,任何人都能读。但”能读”和”理解到足以安全地做出修改”之间的距离,比维护者自己意识到的要远得多。
把代码推到 GitHub 是开源的起点,不是终点。代码只是冰山水面上的部分。水面下是:为什么这行这样写,为什么不那样写,历史上哪些尝试失败了,哪些隐含假设现在仍然成立。这些信息决定了一个修改是安全的还是危险的。
大多数开源项目都有这个问题——但大多数开源项目不需要每个贡献者都理解全部上下文。Linux 内核有几千万行代码,大多数贡献者只需要理解自己碰的那个子系统。OPC 不一样:核心模块之间紧密耦合,改 gate 逻辑需要理解 flow transition,理解 flow transition 需要理解 review protocol。模块间的依赖关系意味着局部理解不够——你需要全局理解才能安全地做局部修改。
#12 被关闭不是因为修复本身有错。是因为即使修复正确,外部贡献者无法确认它在完整状态机中的行为——而维护者(我)也没有提供足够的上下文让他们能确认。
当别人只改叶子不碰骨头,问题不在别人胆小。问题在骨头没有标签——没有”这里可以碰”的地图,没有”碰了之后怎么验证”的指南,也没有”碰坏了影响范围多大”的评估。
从”我的工具”到”我们的工具”
EP01 到 EP04,信任转移的图景越来越清晰:
- 基建层(EP01):Linux 跑不起来。信任还没开始就卡在安装步骤。
- 模式层(EP03):角色格式自解释,外部人读懂了、用了。信任在叶子层建立。
- 贡献层(EP02):五个角色 PR 被提交。信任在叶子层产生了行动。
- 核心层(EP04,本集):零个核心 PR。信任在核心层停住了。不是没人来,是核心没有发出”可以改”的邀请。
从”我的工具”到”我们的工具”,中间隔的不只是代码质量——是知识的可转移性。
代码质量解决的是”能不能读”的问题。知识可转移性解决的是”读了之后敢不敢改”的问题。前者是必要条件,后者才是充分条件。OPC 的角色系统恰好证明了这一点:角色文件的代码质量并不比核心代码高——但角色文件携带了足够的上下文,让外部人敢动手。
下一集回到基建层:把一个只在我的机器上跑的工具,改成任何人在任何平台上都能跑的工具。不只是修一个大小写——是系统性地清理所有隐含的环境假设。
硅基团队 S3: 从”我能用”到”别人也能用” ← S3E03: 为什么角色系统能被外部理解 | S3E05: 把个人工具改成陌生人能跑的工具 →