Skip to content
Touchskyer's Thinking Wall
S3E05
10 min read

硅基团队 S3E05: 把个人工具改成陌生人能跑的工具

硅基团队 S3E05

EP01 讲了一个 bug:skill.mdSKILL.md 在 Linux 上不是同一个文件。修了 11 个文件。但那只是冰山一角。

#21 不是本集主题本身,而是入口。本集要从一个文件名扩展到个人工具公开化的四类系统问题——路径硬编码、配置分层缺失、会话(session)隔离不存在、崩溃恢复靠手动。当你把一个在自己机器上打磨了两季的工具推向公开,“跑不起来”的方式远不止大小写。

从 #21 开始拆

PR #21 的表面修复是 git mv skill.md SKILL.md。但改名本身只是触发器——它暴露了四类系统性问题。

问题一:路径硬编码

OPC 的安装脚本和 skill 定义里,路径是这样写的:

const MANAGED_ENTRIES = [
  'skill.md',
  'bin/',
  'pipeline/',
  'roles/',
  // ...
];

skill.md 写死在数组里。package.jsonfiles 字段也硬编码了 skill.md。hook 脚本 opc-post-compact.sh 里有一行恢复提示引用了 skill.md。五个 pipeline 文档提到了”加载 skill.md”。

一个文件名改动,四层基础设施要跟着改。路径硬编码的本质问题不是”难改”——是”不知道哪些地方引用了它”。 没有一个集中的配置文件说”这个文件叫什么名字”。名字分散在代码、配置、脚本、文档四个层面,每一层都是独立维护的。

S2E08 的 skill-audit 发现过类似问题:Logex 的 skill.md 硬编码了 ~/Code/logex-data,但实际目录是 ~/Code/logex-projects/logex-data。路径硬编码是个人工具的通病——在你自己的机器上,路径永远对。

换一个角度想:如果你从第一天就知道会有外部用户在不同的目录结构下使用这个工具,你不会把路径写死在五个不同的地方——你会在第一天就建一个 manifest。但”第一天”的时候,你不知道会有外部用户。你只是在自己的机器上快速迭代,把路径写在最方便的地方。硬编码不是设计失误,是时间线的必然产物。

修复方向: 把路径集中到一个配置文件或环境变量。MANAGED_ENTRIES 从一个 manifest.json 读取,文档从 manifest 生成引用。改名只改一个地方。

问题二:配置分层缺失

OPC 的配置当时只有一层:代码里的默认值。

const DEFAULT_MAX_LOOPS = 3;
const DEFAULT_FLOW = 'build-verify';

没有用户级配置文件。没有项目级配置覆盖。没有环境变量覆盖。

对一个人来说这没问题——我想改默认值,直接改代码。但对外部用户,“改代码”不是一个合理的配置方式。如果你想把 maxLoopsPerEdge 从 3 改成 5,你需要复刻(fork)仓库、改源码、维护自己的分支。

成熟的开源工具通常有三到四层配置:

代码默认值 → 系统级配置 (~/.opc/config.yaml)
  → 项目级配置 (.opc/config.yaml)
  → 环境变量 (OPC_MAX_LOOPS=5)
  → 命令行参数 (--max-loops 5)

每一层覆盖上一层。用户不需要改代码就能调整行为。

Cherry Studio 的做法是一个参考:它用 Electron 的 app.getPath('userData') 存用户配置,支持 GUI 修改,持久化到磁盘。用户永远不需要碰源码。VS Code 更极端——几乎所有行为都可以通过 settings.json 配置,从缩进宽度到 Git 自动 fetch 的间隔。

修复方向: 引入分层配置。代码默认值 → ~/.opc/config.yaml.opc/config.yaml → 环境变量。读取时按层覆盖。文档说明每层的用途。

问题三:Session 隔离

OPC 的 session 状态存在 .harness/ 目录下——是项目级的。这意味着:

  • 同一个项目同一时间只能运行一个 OPC 流水线
  • 如果两个终端窗口同时跑 opc-harness init,后一个会覆盖前一个的状态
  • 没有机制检测冲突

对一个人来说,不会同时开两个 OPC。但”不会”和”不能”是两回事。一个外部用户完全可能在一个终端跑完整的 full-stack flow,在另一个终端对同一个项目跑一个快速的 review flow。

修复方向: Session 级隔离。每次 init 创建一个唯一的 session 目录(~/.opc/sessions/{project-hash}/{session-id}/),用 symlink 指向 latest。多个 session 并存,互不干扰。这个方案后来确实被实施了——plan 文件里的 OPC 执行协议已经使用了 session 目录。

问题四:崩溃恢复

OPC 的上下文压缩后会话(compaction)会丢失当前节点位置。S1 和 S2 的做法:每次压缩后手动检查 .harness/flow-state.json,找到 currentNode,手动继续。

手动。每次。

对维护者来说,知道上下文压缩意味着什么,知道去哪里找状态。对外部用户,会话突然变短了——然后工具停了——然后不知道怎么继续。

修复方向: 压缩后自动恢复。hook 脚本 opc-post-compact.sh 在上下文压缩后自动读取 flow-state.json,把当前节点和 session 路径注入新的上下文。这个方案也被实施了——CLAUDE.md 里的 OPC Resume Protocol 就是这个机制。

四类问题的共同根因

路径硬编码、配置分层缺失、会话(session)隔离不存在、崩溃恢复靠手动——这四个问题表面上不同,根因相同:

开发者即用户时,很多东西不需要显式设计。

配置?改代码就是配置。隔离?我只跑一个实例。恢复?我知道在哪找状态文件。路径?我的机器上永远对。

这些都是”在我的环境里永远不会出问题”的假设。EP01 说”隐式环境假设是定时炸弹”。这一集的补充是:炸弹不只一颗。它们是一个地雷阵——你自己走过去没事,因为你知道哪里有雷。第一个不知道雷在哪的人,第一步就炸了。

这个模式之所以特别危险,是因为它对创造者完全不可见。你跑所有测试——全过。你走所有工作流——全通。bug 不是藏在代码里,而是藏在你和代码之间的环境假设里。你自己的测试永远无法捕捉这些问题,因为你的测试环境本身就是那个”隐含 spec”的一部分——用你的环境测你的工具,等于用尺子量尺子,永远是准的。

修复的真实成本

拆完问题,看修复。一个诚实的估算:

问题修复工作量改动范围
路径硬编码manifest.json + 所有引用处
配置分层新增配置读取层 + 文档 + 迁移
Session 隔离重写 harness init/route/transition
崩溃恢复hook 脚本 + 状态读取逻辑
跨平台兼容CI 矩阵 + Docker + 测试

“高”意味着不是一个周末的活。配置分层需要定义格式、写解析器、处理覆盖优先级、写文档说明每层的用途。Session 隔离需要重写核心的 init 和 transition 逻辑——这是核心代码改动。跨平台兼容需要搭 CI 矩阵,在 Linux/macOS/Windows 上跑测试。

跨平台兼容是这张表里最隐蔽的一行。前四个问题至少能在本机复现——改配置、开两个终端、模拟一次崩溃,就能看到问题。但跨平台 bug 在你的 macOS 上根本不存在。你需要一台你没有的机器、一个你没用过的 shell、一套你没配过的权限模型,才能发现一个你不知道存在的 bug。这就是为什么它的工作量是”高”——不是因为修复代码本身复杂,而是因为发现问题的反馈循环极长。

从”我能跑”到”任何人能跑”的距离,不是修 11 个文件——是重写基础设施的一部分。

可用性不是 polish,是信任的地基

EP01 定义了信任五层模型。这一集处理的是第一层:基建层。

基建层是信任的地基。不管你的审查流程多精妙、角色系统多好用、门禁逻辑多严密——如果第一个用户 npm install 之后工具不工作,后面四层信任全部不存在。

这就是为什么”可用性”不是产品 polish 的最后一步,而是信任建设的第一步。

S2 花了八集优化审查准确度——加角色、加门禁、加设计审查、让过程可见。然后第一个外部用户来了,卡在了文件名大小写。

你以为的优先级:功能 → 质量 → 可用性。真实的优先级:可用性 → 功能 → 质量。 因为用户体验到它们的顺序是:能不能装上 → 能不能用 → 好不好用。

优先级反转之所以普遍发生,是因为开发者的视角天然从功能出发——你启动项目是为了解决一个具体问题,不是为了做安装体验。可用性看起来像”额外工作”,而不是核心交付物。但从用户的视角看,可用性就是产品——他们接触到的第一个界面不是你精心设计的功能,而是 npm install 之后的一段报错信息。如果这一步就卡住了,你在功能和质量上投入的所有时间,对这个用户来说等于零。

行业参照

这不是 OPC 独有的问题。很多从个人工具成长为公共项目的开源软件都经历过类似的阶段。

Node.js 的 nvm(Node Version Manager):最初是一个 shell 脚本,假设用户用 bash。fish 用户发现它不工作——需要一个完全不同的 nvm.fish 实现。zsh 用户需要在 .zshrc 里加特殊配置。三个 shell,三套兼容方案。

Docker 的早期版本:最初只支持 Linux。macOS 和 Windows 用户需要通过虚拟机运行。Docker for Mac/Windows 花了几年才做到原生体验。从”Linux 能用”到”任何 OS 能用”的距离,是数年的工程投入。

Homebrew:最初是 macOS 专属。Linuxbrew 作为移植版本出现,花了多年才合并回主线成为 Homebrew on Linux。一个包管理器的跨平台之路,比代码量大得多的是环境假设的清理工作。

模式是一致的:创造者的开发环境就是产品的隐含 spec。 当产品只有创造者使用时,这个 spec 是无形的、零成本的。当第一个不共享这个环境的用户出现,每一条隐含假设都变成了一个 bug。

这一层修不好,上面四层都悬空

回到信任五层模型:

第一层 基建 ← 本集
第二层 模式 ← EP03(角色格式自解释)
第三层 贡献 ← EP02(五个角色 PR)
第四层 核心 ← EP04(零个核心 PR)
第五层 承受 ← 还没到

基建层是地基。模式理解建在”能跑起来”之上。贡献建在”理解模式”之上。核心信任建在”成功贡献”之上。承受能力建在”核心被多人理解和维护”之上。

地基不牢,上面每一层都悬空。 你可以有最好的审查流程(第四层),但如果 Linux 用户装不上(第一层),那个流程对他们不存在。

#21 还是 OPEN 状态。这一集分析的其他三类问题,也还在进行中。基建层还没有完全修好。

下一集还 S2 最大的债:FAIL 路径从未被触发——“不过不放行”到底有没有牙齿。


硅基团队 S3: 从”我能用”到”别人也能用” ← S3E04: 没人碰 gate,说明核心还不是公共资产 | S3E06: 第一次真正验证 FAIL 路径 →

留言