# OpenCode 内部 Fork 维护策略研究

> **Status**: Research / Proposal
> **Author**: Investibook Team
> **Date**: 2026-04-24
> **Scope**: OpenCode internal fork, patch governance, upstream sync strategy, boundary control

---

## 目录

- [1. 背景与问题](#1-背景与问题)
- [2. 结论](#2-结论)
- [3. 为什么不能只靠“有 bug 就修”](#3-为什么不能只靠有-bug-就修)
- [4. 适合 Investibook 的策略](#4-适合-investibook-的策略)
- [5. Patch 治理模型](#5-patch-治理模型)
- [6. 上游同步策略](#6-上游同步策略)
- [7. 如何控制 conflict 成本](#7-如何控制-conflict-成本)
- [8. 如何避免滑向自研 runtime](#8-如何避免滑向自研-runtime)
- [9. 建议的落地方案](#9-建议的落地方案)
- [10. 需要团队讨论确认的问题](#10-需要团队讨论确认的问题)

---

## 1. 背景与问题

Investibook 当前依赖 `opencode` 作为核心 agent runtime。

这不是一个边缘依赖，而是直接承接了以下关键能力：

- ACP 会话与 agent 子进程启动
- `.opencode/opencode.json` 配置与 MCP server 装载
- skills / plugins 发现与运行
- tool event / session event 的上报语义

从代码结构看，backend 已经把 runtime 抽象为外部二进制，通过 `ACP_AGENT_BIN` 可切换到自定义构建版本。这意味着从工程接入上，维护一个内部 patched fork 是可行的，并且接入成本不高。

但问题不在“能不能 fork”，而在：

1. 后续预计会持续修很多小 bug，内部 patch 数量会增长
2. 上游发版速度快，频繁合并会带来持续 conflict
3. 如果没有边界控制，内部 fork 很容易从“补丁仓”滑向“事实上的自研 runtime”

因此，这个议题的关键不是是否 fork，而是：

**如何把内部 fork 控制为一个可持续的 patch 层，而不是长期失控的分叉产品。**

## 2. 结论

结论可以先简化成三句话：

1. **值得建立内部 fork**，用于承接阻塞业务的小 bug 修复与少量必要 backport。
2. **不应该把内部 fork 当成独立产品线维护**；它的定位必须是 `patched upstream`。
3. **真正需要治理的是 patch 面，而不是 conflict 本身**。Conflict 只是边界失控后的表象。

对 Investibook 更稳妥的路线是：

- 建立内部 `opencode` fork
- 用 patch queue 的方式维护少量可追踪补丁
- 固定节奏同步 upstream，而不是追每一个 release
- 强制优先在 Investibook 外层修复问题，限制 runtime 定制
- 定期回收已经被 upstream 吸收的 patch

## 3. 为什么不能只靠“有 bug 就修”

“有 bug 就在 fork 里修掉”听起来合理，但随着时间推移会出现三个问题。

### 3.1 Patch 会自然堆积

单个 patch 通常都很小，也都看起来合理：

- 一个 tool event 标题问题
- 一个 ACP schema 兼容问题
- 一个 plugin 生命周期问题
- 一个 skill 加载边界问题

但真正的维护成本不是单个 patch，而是 patch 集合。

当 patch 数量增长后，团队实际面对的是：

- 这些 patch 是否互相耦合
- 哪些 patch 已经被 upstream 覆盖
- 哪些 patch 还值得保留
- 哪些 patch 每次升级都反复冲突

如果没有 patch 级治理，内部 fork 很快就会变成“能跑，但没人说得清为什么这样改”的状态。

### 3.2 Conflict 频繁通常不是操作问题，而是边界问题

如果某类 patch 每次同步 upstream 都反复冲突，通常意味着：

- 改动落在 upstream 高速演化区域
- patch 粒度过大
- 本地修改的是 runtime 核心演进方向，而不是 isolated bugfix

所以，频繁 conflict 本身就是一个信号：

**我们可能已经越过了“补丁层”的边界，开始侵入 runtime 的主路径设计。**

### 3.3 Fork 最终可能演变成隐性自研

真正危险的情况不是“冲突太多”，而是以下路径：

```
先修几个业务阻塞 bug
  -> 为了兼容业务再加一点 runtime 定制
  -> 某些功能在外层不好做，继续往 runtime 下沉
  -> 升级 upstream 越来越痛
  -> 团队不再愿意同步 upstream
  -> fork 事实上变成自研 runtime
```

这条路径一旦发生，就不再是简单的维护策略问题，而是团队是否愿意承担 runtime 产品线的长期投入问题。

## 4. 适合 Investibook 的策略

### 4.1 仓库定位

内部仓应明确命名为：

**OpenCode Internal Fork for Investibook Bugfix / Backport**

而不是：

- Investibook Runtime
- Investibook Agent Engine
- Customized OpenCode Platform

命名不是形式问题，而是在提醒团队这个仓库的边界：

- 目标是更快修阻塞问题
- 目标不是建立一条独立 runtime 演进路线

### 4.2 允许进入 fork 的改动类型

建议只允许三类改动进入内部 fork：

1. **Bugfix**
   - 明确属于 `opencode` 行为错误
   - 对 Investibook 业务有直接影响
   - 改动范围小，语义清晰

2. **Backport**
   - upstream 已经有 issue / PR / 合理方向
   - 但 release 节奏无法满足业务需求
   - 先在内部版本提前带上

3. **Local-only compatibility fix**
   - 只有在 Investibook 集成方式下才暴露的问题
   - 无法在 Investibook backend / plugin / skill / UI 层规避
   - 改动可局部封装，不改变 runtime 的总体抽象

### 4.3 不允许进入 fork 的改动类型

以下内容原则上不应进入内部 fork：

- Investibook 业务逻辑
- 产品偏好类行为差异
- 大规模架构重构
- 与上游方向长期背离的功能增强
- 本可在外层规避、但为了图快直接改 runtime 的逻辑

一句话：

**内部 fork 解决的是 runtime 缺陷，不承担业务特化。**

## 5. Patch 治理模型

### 5.1 用 patch queue，而不是无限演进的主分支

建议的维护模型：

- `upstream/main`
  - 纯上游镜像分支
- `internal/main`
  - 当前生产基线
- `patch/*`
  - 每个补丁一个分支，或至少一个独立 commit

这样做有几个直接好处：

- 上游同步时可以按 patch 粒度 replay
- 某个 patch 被 upstream 吸收后可以直接 drop
- 容易定位某个 patch 是否引入行为偏差

### 5.2 每个 patch 必须具备元数据

建议维护一个 `PATCHES.md` 或 `patch-registry.yaml`，每个 patch 至少记录：

- patch id
- 标题
- 类型：`bugfix` / `backport` / `local-only`
- 对应 upstream issue / PR
- 首次引入日期
- owner
- 影响模块
- 风险等级
- 是否计划回收
- 回收条件

如果某个 patch 没有这些元数据，后续几乎一定会失去上下文。

### 5.3 控制 patch 总量

建议设定明确阈值，而不是凭感觉：

- 常态 patch 总数尽量控制在 `10-20` 个以内
- `local-only` patch 占比不超过 `20%-30%`
- 超过阈值时，暂停新增 patch，优先做回收和上游化

这些数字不是绝对标准，但它们能起到团队治理上的约束作用。

### 5.4 每个 patch 必须小而单一

规范建议：

- 一个 patch 只解决一个问题
- 一个 patch 最好只触及一个主题区域
- 避免“顺手再改一点”

原因很简单：

- patch 越小，越容易 upstream
- patch 越小，升级时越容易 drop / replay
- patch 越小，冲突定位越清楚

## 6. 上游同步策略

### 6.1 不追每一个 release

如果 upstream 发版很快，而团队每次都立即跟，结果通常是：

- 不断处理中间版本的兼容细节
- 业务 patch 被同步工作打断
- 升级成本高，但收益未必线性增加

更合理的方式是：

- 固定每周一次或每两周一次同步窗口
- 只同步到通过内部 smoke test 的 tag / commit
- 对大版本或 ACP/schema 变化单独开升级分支验证

### 6.2 同步流程建议

建议的标准流程：

1. 更新 `upstream/main`
2. 从 `upstream/main` 拉出升级分支
3. 按 patch queue 顺序 replay 内部补丁
4. 运行最小 smoke test
5. 删除已被 upstream 覆盖的 patch
6. 合并回 `internal/main`

这里最关键的不是“自动化程度有多高”，而是团队每次都按同一套路执行。

### 6.3 什么时候不应该立即同步

以下情况下不建议强行追上游：

- 当前线上有高优先级业务问题正在处理
- 上游 release 包含大规模 ACP / tool schema / permission 模型变动
- 团队没有时间完成回归验证

这时宁可继续跑已知稳定的内部基线，也不要为了“看起来跟得很近”而引入额外不确定性。

## 7. 如何控制 conflict 成本

### 7.1 接受 conflict 是常态，但要让它可预测

目标不是消灭 conflict，而是让 conflict 的处理成本足够低、足够稳定。

核心方法只有三个：

- patch 小
- patch 少
- patch 边界清晰

### 7.2 把反复冲突的 patch 视为治理信号

某个 patch 如果连续多次在同一片区域冲突，应该触发一次审查：

- 这个 patch 能否在 Investibook 外层实现
- 这个 patch 是否应尽快推动 upstream 吸收
- 这个 patch 是否已经不值得保留

不要把反复冲突仅仅当成“升级麻烦”，它更像一盏红灯。

### 7.3 优先修“高价值低分叉面”的问题

内部 patch 候选很多时，优先级建议如下：

- P0: 稳定性、正确性、可用性问题
- P1: 高频影响用户体验或诊断效率的问题
- P2: 中低频体验问题，优先考虑外层规避
- P3: 偏好类改动，不进 fork

这会自然减少低价值 patch 对分叉面的侵蚀。

## 8. 如何避免滑向自研 runtime

### 8.1 先定义什么叫“已经不是 fork 维护”

以下信号出现两个以上，通常说明团队已经开始走向自研 runtime：

- `local-only` patch 持续增加，且难以上游
- 开始大量修改核心调度、权限、插件体系、事件模型
- 升级 upstream 已经不是 replay patch，而是在做迁移工程
- 团队新增功能优先考虑改 fork，而不是外层适配
- 内部版本行为与 upstream 的差异越来越难解释

这时需要做正式决策：

- 要么收缩 patch 面，回到 patched upstream 模式
- 要么承认进入自研阶段，并配套维护预算、测试体系和 roadmap

### 8.2 建立“优先外层修复”的默认原则

对于 Investibook，很多需求其实可以优先在以下层解决：

- backend ACP client 适配层
- tool event 解释与 UI 渲染层
- plugin / skill 包装层
- workspace 配置生成层

只有这些层都无法合理解决时，才进入 runtime fork。

这个原则非常关键，因为它决定 patch 面是否会无限扩大。

### 8.3 给 patch 设回收期限

建议每个 patch 默认都带一个问题：

- upstream 是否已有等价修复
- 如果有，什么时候删掉内部 patch
- 如果 60-90 天后仍未回收，原因是什么

没有回收机制，patch 只会增加，不会减少。

## 9. 建议的落地方案

### 9.1 第一阶段：建立最小可控 fork

目标：

- 建一个内部 GitHub 仓库
- 同步 upstream 默认分支
- 验证 Investibook 通过 `ACP_AGENT_BIN` 切换到内部构建版本
- 先承接 1-3 个明确阻塞的小 bug patch

关键不是一次性做完整治理，而是先形成受控的最小闭环。

### 9.2 第二阶段：把 patch 管理制度化

建议补齐以下内容：

- patch registry
- 上游同步 SOP
- smoke test 列表
- patch review checklist

团队开始长期维护 fork 后，这些制度的价值会迅速体现出来。

### 9.3 第三阶段：引入定期回收机制

建议每月做一次 patch 审查，内容包括：

- 哪些 patch 已可删除
- 哪些 patch 需要补 upstream issue / PR
- 哪些 patch 不应该继续保留在 runtime 层

这一步的目标不是文档完美，而是防止 patch 面失控。

## 10. 需要团队讨论确认的问题

以下问题建议作为团队后续讨论输入：

1. 内部 fork 的 owner 是谁，谁负责同步 upstream
2. patch 总量阈值设多少合适
3. `local-only` patch 的准入标准要多严格
4. 同步节奏选每周还是双周
5. 最小 smoke test 覆盖哪些 Investibook 关键路径
6. 团队是否接受“有些问题先在外层绕开，不进入 fork”
7. 出现哪些信号时，需要正式讨论是否转向自研 runtime

---

## 最终建议

对 Investibook 来说，内部 fork 是合理的，但只能在一个前提下成立：

**团队必须把它当成受控 patch 层维护，而不是默认的功能承载层。**

如果边界清晰，内部 fork 会带来很高的业务响应速度；
如果边界失控，它最终会变成一个没有被正式承认、但已经需要正式维护成本的自研 runtime。

因此，后续讨论的重点不应是“fork 会不会有 conflict”，而应是：

**我们准备用什么规则，把 patch 面控制在可持续范围内。**
