Skip to content
Touchskyer's Thinking Wall
S3E06
11 min read

硅基团队 S3E06: 第一次真正验证 FAIL 路径

硅基团队 S3E06

S2E08 发现了一个尴尬的事实:OPC 最核心的承诺——“不过不放行”——从未真正行使过。八个产品,几十次门禁判定,FAIL/ITERATE 回环一次都没触发。maxLoopsPerEdge=3 的上限从未被考验。

一个从未触发的强制机制,和一个不存在的强制机制,在生产环境里没有区别。

这一集还这笔债。

为什么 FAIL 从未触发

先理解问题的根源。FAIL 路径不触发有三种可能的解释:

解释一:审查标准太宽松。 大语言模型审查者倾向于给正面评价。S1E03 就观察到四个审查者全部判定 PASS 的现象——即便其中有一个应该被发现的方向性问题。加了 skeptic-owner 角色之后有改善,但”倾向 PASS”的基线没有根本改变。如果审查者几乎不给 🔴(critical)标记,门禁的 emoji 计数就永远不会达到 FAIL 阈值。这不完全是模型能力的问题——它反映了训练目标的副作用。大语言模型被优化为”有帮助的”,而”有帮助”在大多数训练数据中表现为肯定和建设性反馈,不是否定和拒绝。让一个被训练为 helpful 的模型去做 gatekeeper,本身就存在目标张力。

解释二:测试的任务太简单。 S2 的八个产品覆盖了不同领域(日历、教育工具、知识管理、可视化),但复杂度集中在产品设计而非代码实现。大部分实现是标准的 Web 应用——React 组件、REST API、数据库 CRUD。这些模式大语言模型已经非常熟悉,写出的代码通常能通过基础审查。如果没有刻意引入困难的边界场景——并发冲突、分布式一致性、性能热路径——FAIL 不会自然触发。换句话说,S2 的产品复杂度集中在”做什么”(产品设计),而不是”怎么做”(实现难度)。大语言模型的实现能力恰好覆盖了”怎么做”的大部分,所以代码质量本身没有触发审查红线的内在压力。

解释三:OPC 的构建质量确实够高。 也许 FAIL 没有触发的原因很简单:代码确实过了。不是审查太松,不是任务太简单,而是 OPC 的构建节点(implementer + 项目约束 + 测试)产出的代码质量足够达到审查门槛。

S2E08 的诚实结论是:无法区分这三种解释。这一集不试图区分——这一集试图回答一个更实际的问题:如果给 FAIL 一个触发的机会,它能不能正确工作?

设计一个必须 FAIL 的场景

验证 FAIL 路径的方法不是等它自然发生——而是刻意制造一个会触发 FAIL 的场景。

方案:故意提交一个有已知缺陷的实现。 让构建节点产出的代码有一个显著但可检测的问题——在本次验证中是一个 O(n²) 的排序算法处理大数据集,以及一个缺少错误处理的 API 调用。然后看审查节点能不能检测到、门禁能不能判 FAIL、回环能不能正确触发。

这不是在测审查者聪不聪明——是在测管道的机械部分:

  1. 审查者标记了 🔴 后,synthesize 能不能正确计数?
  2. emoji 计数达到 FAIL 阈值后,门禁(gate)能不能正确判定 FAIL?
  3. gate 判 FAIL 后,flow-transition 能不能正确路由回构建节点?
  4. 回环时,之前的审查发现会不会被传递给构建者?
  5. 第二轮构建修复了问题后,审查者能不能看到改进并改判 PASS?
  6. maxLoopsPerEdge 计数器会不会正确递增?

六个检查点,每一个都是独立的故障点。之前从未被端到端测试过。

第一轮:FAIL 触发了

刻意提交的缺陷实现进入审查节点。三个审查角色同时审查。

结果:两个角色给出了 🔴 标记。synthesize 扫到 emoji,计数正确。gate 判定 FAIL。

FAIL 路径第一次被触发了。

但这不够——触发 FAIL 只是回环机制的起点。关键问题是:回环之后呢?

回环的问题

Gate 判 FAIL 后,flow-transition 正确地将流程路由回构建节点。回环计数器从 0 变为 1。到此为止,机械部分工作正常。

然后问题出现了。

构建节点重新启动时,它不知道上一轮审查发现了什么。S2E08 已经指出了这个问题:“当前的流程:审查发现 → 门禁判定 FAIL → 回环到构建节点 → ???。没有机制追踪哪些审查发现被修复了、哪些被推迟、哪些是误判。”

这不是一个理论问题——它在实际触发 FAIL 后变成了一个实际问题。构建者在第二轮不知道第一轮的 🔴 具体指的是什么。它需要重新审查整个代码库,而不是针对性地修复已知问题。这意味着第二轮构建的效率和第一轮几乎一样低——构建者不是在”修复已知问题”,而是在”重新理解整个任务”。如果问题不只一个,回环的成本会随问题数量线性增长,因为每个问题都需要被重新发现。这直接违背了回环机制的设计初衷:回环应该是增量修复,不是全量重做。

FAIL 路径的机械部分(判定 + 路由 + 计数)工作正常。FAIL 路径的信息传递部分(审查发现 → 构建修复)是断裂的。

这就像一个法庭:法官说”有罪”,被告被送回去”改正”,但没有人告诉他罪名是什么。

审查发现处置追踪

修复这个断裂需要一个新的机制:审查发现处置追踪(Finding Disposition Tracking)

每一轮审查产出的发现(findings)需要被结构化记录:

{
  "round": 1,
  "findings": [
    {
      "id": "F001",
      "severity": "critical",
      "source": "performance-reviewer",
      "description": "O(n²) sort in data processing pipeline",
      "file": "src/pipeline/transform.ts",
      "line": 42,
      "status": "open"
    }
  ]
}

当回环到构建节点时,这个 findings 列表被传递给构建者。构建者修复后,标记每个 finding 的处置:

  • fixed:已修复,附带修复的 commit 或行号
  • deferred:推迟到后续版本,附带理由
  • false-positive:误判,附带解释

第二轮审查时,审查者收到的不是”从零开始审查整个代码库”,而是”验证这些 findings 的处置是否正确”。这把审查从全量复审变成了增量验证。增量验证和全量复审的成本差异不是线性的——全量复审要求审查者重新建立上下文,而增量验证只需要在已知位置检查修复。对人类审查者如此,对大语言模型审查者也如此:上下文窗口是有限资源,全量复审浪费大量窗口在已经通过的代码上。

第二轮:修复 + PASS

在手动传递了第一轮的审查发现后,构建节点修复了已知缺陷。第二轮审查:审查者确认问题已修复,没有新的 🔴。synthesize 计数正确。gate 判定 PASS。

回环计数器显示 1——意味着这是经过一轮回环后通过的。

完整的 FAIL → 修复 → PASS 路径第一次被端到端走通了。

Emoji 解析的旧 bug

验证过程中顺便确认了 S2E08 发现的 emoji 解析 bug 是否已修复。

旧 bug:审查者写”### 🔴 Must Fix: None.”——synthesize 扫到 🔴 emoji,判定为 critical 审查发现。“None”被忽略了——机械解析只看 emoji,不看语义。

这个 bug 的状态:已修复。synthesize 现在检查 🔴 后面是否紧跟否定词(“None”、“N/A”、“无”)。如果是,不计入 critical 计数。

但这个修复暴露了一个更深的问题:emoji 计数本质上是一种启发式(heuristic)方法。它能处理”🔴 None”的情况,但如果审查者写”🔴 Not a dealbreaker but worth noting”呢?这不是 critical,但机械解析可能把它算作 critical。

S1E07 的结论说机械门禁优于大语言模型门禁。这个结论依然成立——启发式方法的边界情况是可枚举、可修复的;大语言模型的判定漂移是不可预测的。但启发式方法的每一个边界情况都需要被发现后手动修复。它把失效模式从”不可预测”变成了”可调试”——但调试的工作量是持续的。

安全网必须被测试

回到信任的话题。

EP01-EP05 讲的是信任的前四层:基建、模式、贡献、核心。这一集处理的是第五层:承受——系统能不能承受失败?

一个审查系统的价值不在于它通过了多少东西——在于它拦住了多少东西。一个从未拦住过任何东西的审查系统,它的”拦”是一个未验证的承诺。

安全网必须被测试过才算安全网。

消防演习不是因为办公室真的着火了。负载测试不是因为生产环境真的有一百万并发请求。混沌工程(Chaos Engineering)不是因为 Netflix 真的想让服务器宕机。这些测试的目的不是触发问题——是验证在问题触发时,响应机制能不能正确工作。

OPC 的 FAIL 路径验证是同一类实践。不是为了证明代码能写错——是为了证明当代码写错时,审查 + 门禁 + 回环的链条能正确响应。

不做这个验证的代价是什么?是你无法对外部用户说”这个门禁确实能拦住问题”——因为你自己都没有证据。163 个 star 的用户里,如果有人问”你的审查系统真的有用吗”,在 EP06 之前我只能说”设计上应该有用”。现在可以说”我亲手测过,它能拦住问题、能回环修复、能正确放行”。从 should work 到 did work,中间差的就是一次端到端验证。

还剩什么债

这一轮验证还了 S2 最大的一笔债,但留下了新的债:

1. 审查发现处置追踪还未机械化。 这次验证中,第一轮的审查发现是手动传递给构建节点的。真正的解决方案需要约束工程(harness)在 FAIL 回环时自动提取 findings、结构化存储、传递给下一轮。这是一个核心代码改动——EP04 说的那道墙。

2. 单次验证不代表持续可靠。 FAIL 路径被走通了一次,不代表它在任何场景下都能正确工作。需要更多的触发场景:不同类型的 FAIL(安全问题 vs 性能问题 vs 设计问题)、不同数量的 🔴(刚好到阈值 vs 远超阈值)、连续 FAIL(达到 maxLoopsPerEdge=3 的上限)。特别是达到上限后的行为——flow 应该终止并输出错误信息,还是进入人工干预流程?这个分支从未被设计过,更别说被测试。

3. 外部用户触发 FAIL 的体验。 我作为维护者知道 FAIL 意味着什么、回环意味着什么、怎么看审查发现。外部用户第一次看到 gate 判 FAIL——会发生什么?错误信息清楚吗?知道下一步该做什么吗?FAIL 的用户体验是信任第五层(承受)的一部分。

从”有牙齿”到”能咬人”

S2E08 问了一个问题:“不过不放行”到底有没有牙齿?

这一集的回答:有牙齿。但从有牙齿到能咬人,中间还差肌肉(审查发现追踪)和练习(更多触发场景)。

门禁能判 FAIL。回环能路由回去。计数器能递增。这是骨架——机械部分工作了。但骨架不够——还需要信息传递(找到了什么问题→怎么修→修了没有→验证修复了没有)的完整闭环。没有这个闭环,回环就是在浪费计算资源——每一轮都在重复发现已知问题,而不是在修复问题的基础上前进。

这和信任五层模型是同构的:有结构(骨架)不等于有能力(肌肉)不等于有信心(练习)。

下一集看贡献治理:当五个人开始往你的工具里加东西,你需要的不只是接口——还有规则。


硅基团队 S3: 从”我能用”到”别人也能用” ← S3E05: 把个人工具改成陌生人能跑的工具 | S3E07: 从角色贡献到扩展生态,中间差一个治理层 →

留言