Harness Engineering 解析:AI Coding Agent 的可靠性工程
一个开发团队在用 Claude Code 重构认证模块时记录了这样一个过程:第一个 Agent 会话完成了 JWT 登录逻辑,干净地提交了代码;第二个会话接手时,Agent 读不到上一次的决策上下文,重新推断了一套 HS256 方案,与第一个会话的 RS256 实现产生了冲突;第三个会话在修复冲突时又顺手重构了不在任务范围内的密码哈希模块,把一个已经通过测试的部分改出了新的 bug。三个会话,每个单独看都显示"任务完成",合在一起却是一片混乱。
这不是一个极端案例。AI 编程 Agent 当前最普遍的失效模式不是推理错误,而是环境问题——多会话之间状态不衔接,任务边界不清晰,完成标准没有外部锚点。同一个 Claude Code,在精心设计的环境里能连续完成数十步骤的复杂重构,在另一个项目里却在第三步就开始偏轨。模型没变,变的是工作环境。
Walkinglabs 的 Learn Harness Engineering 正是针对这个问题构建的工程体系。它的核心主张可以用一句话概括:与其让模型更聪明,不如把模型的工作环境设计得更可靠。这个主张背后有一套完整的方法论,涵盖 AI Agent 从指令设计、状态管理、验证机制到会话生命周期的全链路约束。本文将逐层拆解这套方法论的设计逻辑,并在必要的地方指出它与现有实践之间的分歧与权衡。
1 能力与可靠性之间的裂缝
1.1 失效的根源不在模型
当一个 AI Agent 在项目中频繁失效时,直觉上的归因通常落在模型能力上。但如果仔细审视失效的具体场景,会发现大多数问题指向的是另一个方向。
第一类是任务欠规格(underspecification)。“实现用户认证功能"对模型来说是一个开放空间:使用哪个框架、认证状态存在哪里、哪些文件不应该被改、“完成"意味着 happy path 跑通还是含错误处理的全流程——这些信息都缺失。模型会用统计先验填空,填空的结果可能合理,也可能偏离预期,而模型在两种情况下的表现一样自信。
第二类是跨会话状态丢失。真实开发任务很少能在单次会话里完成。上一次会话确定的技术选型、做出的妥协决定、标记为"暂不处理"的边缘案例——会话结束后全部消失。下一次 Agent 启动时,需要重新建立这些上下文,而重建的结果不一定与原来一致,不一致的决定在代码库里积累,就成了开篇那个案例的样子。
第三类是提前宣告完成(premature victory declaration)。Agent 完成了任务的核心路径后,将其汇报为整体完成。这不是欺骗,而是对"完成"边界的不同理解——模型基于它看到的信息做出判断,但它不知道自己遗漏了什么。在没有外部验证机制的情况下,这个误判只能在代码审查或 QA 阶段才能被发现。
失效类型 根源 通常的归因误区
──────────── ────────────────── ──────────────────
任务欠规格 意图传递有损 "模型理解能力不够"
跨会话状态丢失 环境无持久化机制 "上下文窗口太短"
提前宣告完成 无外部完成标准 "模型不够严谨"
这三类失效指向的不是模型能力,而是意图传递机制和环境设计的缺陷。给模型更大的上下文窗口、更强的 RAG 检索,都不能从根本上解决任务边界模糊或完成标准缺失的问题,因为这些问题的根源在系统层面,不在模型层面。
1.2 两种工程路线的分叉,以及它们为什么不互斥
既然根源在系统层面,那么两个团队面对同样的问题,为什么会形成截然不同的应对方向——一个去优化模型,一个去约束环境?
面对 Agent 可靠性问题,当前社区的应对方向形成了两条路线。一条路线投资于模型感知增强:扩大上下文窗口、加入语义检索、提升推理深度,逻辑是 Agent 失效因为"看不到"或"想不到”,给更多信息和更强推理即可解决。另一条路线投资于环境结构化约束:显式的规则文件、持久化的状态追踪、强制的验证步骤,逻辑是 Agent 失效因为工作环境没有给它足够的结构,模型的能力在非结构化环境里被浪费在了错误方向上。
Harness Engineering 站在第二条路线上,但它的论据并不是"第一条路线错了”。OpenAI 在长达五个月的 Codex 实战中记录了同样的发现:即使是最先进的模型,在缺乏结构化环境的情况下仍然需要密集的人工监督;Agent 在训练数据分布之外的场景中依然"脆弱"。这表明模型能力提升和环境结构化是正交的两个维度,不能相互取代。更强的模型会让 harness 中的每一个步骤执行得更好;harness 的存在,使得更强模型的能力被引导在正确方向上。认为模型能力提升会使 harness 变得多余,类似于认为更快的 CPU 会使操作系统的进程隔离机制变得多余——能力和约束服务于不同的目的。
2 Harness 的本质:封闭循环的控制系统
2.1 “Harness"这个词意味着什么
“Harness"在测试工程中是测试框架(test harness),在航空工程中是束线(wire harness)。两种含义有一个共同点:它们都是约束性的基础设施,使被约束的对象在规定的路径内可预测地工作。在 AI Agent 工程中,harness 指的是:一套封闭循环的控制系统,通过明确的输入约束、状态追踪和输出验证,使 Agent 的行为在复杂任务中保持可预测。
“封闭循环"是理解 harness 价值的关键词。一个没有 harness 的 Agent 工作流是开环的:用户发出指令 → 模型生成输出 → 用户审阅。这个循环里的"审阅"依赖人类的时间和注意力,不可扩展。引入 harness 后,这个循环变成:Agent 执行 → 验证步骤自动检查规格符合性 → 结果写入状态文件 → 下次会话读取状态继续执行。人类从每个步骤的守门人,变为异常情况的决策者。
一个完整的 harness 由五个子系统协同构成:
┌─────────────────────────────────────────────────────────────────┐
│ Harness 架构 │
├──────────────┬──────────────┬───────────────────────────────────┤
│ 指令子系统 │ 状态子系统 │ 验证子系统 │
│ AGENTS.md │ progress.md │ feature_list.json │
│ 分层规则 │ session state│ E2E 测试套件 │
│ 工具约束 │ repo 状态 │ 完成度检查器 │
├──────────────┼──────────────┴───────────────────────────────────┤
│ 范围子系统 │ 生命周期子系统 │
│ 任务边界 │ 初始化阶段 │ 会话连续性 │ 清理流程 │
│ 超限检测 │ │
└──────────────┴──────────────────────────────────────────────────┘
│
Agent 行为 → 验证 → 状态更新 → 下次会话
这五个子系统不是互相独立的模块:指令子系统定义允许与禁止的操作边界;状态子系统保证跨会话的信息不丢失;验证子系统提供客观的完成度标准;范围子系统防止 Agent 走出分配给它的任务边界;生命周期子系统管理每次会话的启动和结束状态。五者共同作用,才能形成第一章描述的那种封闭循环。
2.2 Harness 约束的是行动,而不是推理
在理解 harness 能做什么之前,有必要厘清它不能做什么:它不试图约束或预测模型的推理过程。一个极度详尽的 prompt 试图覆盖所有可能情况,这是 prompt engineering 的思路,在复杂任务面前通常失效——因为没有任何 prompt 能穷举所有场景。
Harness 的逻辑不同。操作系统的进程隔离不关心进程产生什么计算结果,它只保证进程不能随意越出自己的内存边界。Harness 与此类似:不关心 Agent 如何推理,只关心 Agent 的行动是否在允许范围内、输出是否满足可检验标准、状态是否在会话结束时正确持久化。约束施加在行动层面,推理空间保持开放。
3 Repository 作为系统的唯一真相来源
3.1 状态丢失是结构性问题
如果你没有经历过多会话 Agent 工作流的状态失控,可以做一个思想实验:把你自己想象成下一个进入项目的 Agent。你会看到什么?你会看到当前的代码,但不会知道某个设计选择是刻意为之还是临时妥协,不会知道某段代码是已完成的还是写了一半的,不会知道哪个功能在上个会话里被决定暂不实现。你只能推断——而推断的结果,可能与上一个 Agent(或上一个自己)的决定不一致。
不一致的决定在代码库里积累,就成了"幻觉蔓延"的根源:不是单次推理错误,而是多次会话之间的决定没有协调。
会话 1 会话 2 会话 3
───────── ───────── ─────────
选择 RS256 → 上下文丢失 → 重新推断
提交代码 改用 HS256 修冲突时
JWT 正常 与会话1冲突 扩大改动范围
新 bug 引入
↑ ↑ ↑
无问题 无报错 "任务完成"
每个会话单独看都成立,但三个会话之间没有共享的决策记录,导致整体状态持续漂移。progress.md 里记录的选择和代码里实际存在的实现开始分叉,直到没有人确切地知道当前代码库处于什么状态。
Harness Engineering 对此的解法是:repository 作为所有 Agent 状态的单一真相来源(system of record)。任何跨会话需要持久化的信息,都必须写入 repository 的文件里,而不是依赖模型的记忆或开发者的记忆。这个原则的推论是:代码本身也是状态——Agent 应该从阅读现有代码和状态文件开始,而不是从零推断应该做什么。
.
├── AGENTS.md # Agent 行为规则(指令子系统)
├── docs/
│ ├── architecture.md # 已做出的架构决定
│ └── progress.md # 当前任务进度(状态子系统)
├── feature_list.json # 功能清单与完成标准(验证子系统)
├── scripts/
│ └── init.sh # 初始化脚本(生命周期子系统)
└── tests/
└── e2e/ # 端到端测试套件(验证子系统)
3.2 进度文件的设计密度
Repository 成为真相来源之后,一个自然而然的问题是:写入 repository 的状态文件,应该包含什么、不包含什么?如果只记录"做了什么”,信息密度不足以支撑会话连续性;如果记录一切,文件会变成无法快速读取的日志。
Repository-centric 的设计要求进度文件不只记录"做了什么”,还要记录"为什么这样做"和"下一步做什么”。前者防止新会话重复已完成的工作,后者消除任务转移时的上下文重建成本:
## 当前状态
- 已完成:用户登录 API(JWT 方案,见 auth/jwt.py)
- 进行中:密码重置流程(卡在邮件服务集成,见 email_service.py:47)
- 未开始:第三方 OAuth 登录
## 关键决定
- RS256 而非 HS256:多服务部署需要公钥验证
- token 有效期 15 分钟(refresh token 7 天):合规要求
## 下一步
1. 完成 email_service.py 中的 send_reset_email 方法
2. 运行 tests/auth/test_reset_flow.py 验证全流程
3. 更新 feature_list.json 中 password-reset 条目状态
这种格式的目标读者首先是下一个 Agent 会话,其次才是人类开发者。与 ADR(Architecture Decision Record)相比,它的侧重点不同:ADR 关注永久性的架构决定,进度文件关注动态的任务状态。前者是文档,后者是带时间戳的交接备忘。它有一个值得关注的副产品:出现"这里为什么这样实现"的疑问时,可以翻阅历史 commit 中的 progress.md 找到决策背景,这和事件溯源(event sourcing)“不只记录结果,也记录产生结果的决策序列"的理念是相通的。
4 指令体系:从巨型 Prompt 到分层约束
Repository-centric 的设计解决了"状态存在哪里"的问题,但还有另一个问题没有回答:Agent 应该如何行动?这两个问题是分开的。状态文件告诉 Agent 当前的任务是什么,但不告诉它在执行过程中哪些操作是被允许的、哪些边界不能越过。指令体系正是为此而存在——它不管理状态,它管理行为规则。
4.1 单体指令文件的失效不是写得不够详细
“把所有规则写进一个大的 system prompt"是早期 AI Agent 工程的标准实践。当这个做法失效时,开发者的第一反应通常是往里面加更多规则——但更多规则不会解决问题,因为失效的根源不在规则的数量上。
单体指令文件有三个结构性缺陷,逐一拆解:
规则冲突的无处仲裁:数十条规则之间的优先级关系无法穷举,当两条规则在某个具体情况下冲突时,模型依据自己的推理裁决,裁决结果不可预测,且随着规则数量增长,冲突概率超线性上升。
文件与代码库的同步滞后:“遵循现有命名约定"这类规则,在代码库里同时存在 camelCase 旧代码和 snake_case 新代码时,成为无法执行的指令——规则和现实脱节,但没有任何机制会报告这个脱节。
注意力衰减:LLM 在处理长 prompt 时,位于中间部分的内容被遵守的概率低于头部和尾部。这不是模型能力的缺陷,而是注意力机制的物理特性。把所有规则堆在一起,意味着部分规则从来没有被有效激活过,而你不知道是哪些。
4.2 分层指令体系
针对这三个缺陷,Harness Engineering 的解法是按变化率(rate of change)将指令拆分为不同层级:
┌──────────────────────────────────────────────────────┐
│ 全局层:AGENTS.md │
│ 不变规则 │ 安全约束 │ 工具许可清单 │ 完成标准定义 │
│ 更新频率:极低 │
├──────────────────────────────────────────────────────┤
│ 项目层:docs/architecture.md │
│ 本项目技术约束 │ 已做出的架构决定 │ 禁用模式 │
│ 更新频率:低 │
├──────────────────────────────────────────────────────┤
│ 任务层:progress.md + feature_list.json │
│ 当前 sprint 范围 │ 完成标准 │ 阻塞项 │
│ 更新频率:高(每次会话结束时更新) │
└──────────────────────────────────────────────────────┘
变化率隔离解决了单体文件的核心问题:高频变化的任务状态和低频变化的全局约束互不干扰,更新 progress.md 不会破坏全局规则,调整全局规则也不需要重写任务描述。这是关注点分离(separation of concerns)在 prompt 层面的应用。
AGENTS.md 是分层体系中设计约束最强的文件。它的目标不是覆盖所有情况,而是让 Agent 对四个核心问题有明确的答案:我能使用什么工具?我不能触碰哪些路径?什么情况下必须停下来询问人类?如何判断一个功能真正完成了?这四个问题涵盖了能力边界、安全边界、人机协作触发点和完成标准,是 Agent 行为约束的最小充分集:
# AGENTS.md
## 工具约束
可运行:npm test, pytest, git status, git diff
禁止运行:git push, rm -rf, 任何写入 .env 的命令
## 受保护路径
- **/migrations/**(数据库迁移不可逆)
- .env 和 .env.* 文件
## 完成标准
功能完成当且仅当:
1. feature_list.json 对应条目的所有验收标准通过
2. tests/e2e/ 相关测试套件全部通过
3. progress.md 已更新,下一步明确标注
## 提问触发条件
停止执行,向开发者确认:
- 实现方案需要修改数据库 schema
- 现有测试与新实现产生不可避免的冲突
- 任务范围超出当前 sprint 的 feature_list 条目
这里有一个值得直说的权衡:AGENTS.md 的详细程度与它被完整遵守的概率之间存在反比关系。文件越长,重要规则越容易被淹没在次要信息里。Harness Engineering 的建议是保持 AGENTS.md 精简,把项目专有的长篇约束下沉到 architecture.md,AGENTS.md 只保留每次 Agent 初始化都需要激活的核心规则。
5 验证机制:让完成标准从主观变成客观
5.1 “完成了吗"这个问题需要外部锚点
在没有 harness 的工作流里,“这个功能完成了吗"由 Agent 自己回答。这个自我评估在 Agent 系统中是已知的弱点:Agent 对自己的实现有承诺感,在验证阶段会在同样的认知偏差方向上产生判断。这不是道德问题,而是从统计上讲,完成了实现的 Agent 对同一份代码的评估,与未参与实现的独立评估者相比,存在系统性的偏差。
解决这个问题需要一个外部的、可机器检验的完成标准,而不是依赖任何一个参与者的主观判断。这正是 feature_list.json 的设计动机。
5.2 Feature List 作为 Harness 原语
Jira、Linear 等项目管理工具中的任务条目是为人类设计的,语言上允许模糊性。“实现用户注册功能"没有告诉 Agent 哪些场景需要覆盖、哪些错误码需要返回、哪些测试需要通过。feature_list.json 要求每个功能条目具备机器可检验的验收标准:
{
"features": [
{
"id": "auth-001",
"name": "用户注册",
"status": "in_progress",
"acceptance_criteria": [
"POST /api/auth/register 返回 201 且含 user_id 字段",
"重复邮箱注册返回 409",
"密码少于 8 位返回 400",
"tests/e2e/auth/test_register.py 全部通过"
],
"blocked_by": [],
"notes": "bcrypt,cost factor 12"
}
]
}
acceptance_criteria 中的每一条都可以由 Agent 自行验证——HTTP 响应码加字段检查,或测试套件的通过/失败。验证逻辑从人脑转移到了代码。这与 BDD(行为驱动开发)中的验收标准有相似之处,但目标不同:BDD 的验收标准是为了在开发者和业务方之间建立共同理解;feature_list.json 的验收标准是为了消除 Agent 和人类之间对"完成"的歧义。接收方从人类变成了模型,对无歧义性的要求反而更高。
5.3 E2E 测试的角色:不只是回归保护
在 harness 体系中,E2E 测试被赋予了一个与传统软件工程不同的角色定位。在传统工程里,E2E 测试是回归保护——在新代码合入后,确认已有功能没有被破坏。在 harness 中,E2E 测试还是Agent 的实时反馈信号。
Agent 实现一个功能后,应立即运行对应的 E2E 测试,根据结果决定继续下一个功能还是修复当前失败。这个"实现 → 测试 → 修复 → 再测试"的循环是 harness 封闭反馈回路的具体实例化。没有这个循环,Agent 依赖的是自我评估,而自我评估是已知的弱点。
这里有一个在实践中经常被忽视的前提:E2E 测试必须能被 Agent 无需人工干预地独立运行。如果测试需要手动启动服务、配置环境变量或访问开发者机器上的本地资源,Agent 就无法完整执行这个验证循环。Harness Engineering 把"测试的可自动化运行"列为硬性要求——不是"如果可以的话”,而是"测试不能被 Agent 独立运行,验证子系统就是不完整的”。
5.4 Gate Policy:依赖关系的显式声明
feature_list.json 还需要定义门控策略(gate policy):哪些功能的完成是后续功能开始的前提。这是对任务依赖图的显式声明,解决的是 Agent 的另一类失效:顺序混乱。Agent 在执行任务时会自行判断顺序,这个判断通常合理,但在存在强依赖关系的功能之间,自主排序可能导致 Agent 在未完成基础层的情况下开始上层实现。
{
"gate_policies": [
{
"gate": "auth-foundation",
"requires_completion": ["auth-001", "auth-002"],
"unlocks": ["auth-003", "auth-004", "user-profile-001"]
}
]
}
门控策略把任务依赖关系从模型的推理过程提取出来,变成 harness 层面的硬约束。在多开发者和多 Agent 会话并行的场景下,它还提供了一个对所有参与者可见的任务状态全景图——这个价值超出了 AI Agent 的范畴,是一种普适的项目协调机制。
6 会话生命周期:从初始化到干净状态
6.1 初始化为什么需要是独立阶段
当初始化被视为"任务的一部分"时,它的执行是机会性的——如果模型认为自己已经有了足够的上下文,它可能跳过某些步骤直接开始执行。这种跳过很难被发现,因为大多数情况下它不会立即导致错误,只是为后续的偏轨埋下了伏笔。
把初始化定义为独立阶段意味着它是强制的:在初始化阶段的所有步骤完成之前,Agent 不能开始任何任务执行。这个区别在代码层面是一个检查清单:
初始化阶段检查清单
□ 读取 AGENTS.md,加载工具约束和安全规则
□ 读取 progress.md,了解当前任务状态
□ 读取 feature_list.json,确认当前 sprint 范围
□ 运行 git status,确认 working tree 干净
□ 执行 init.sh,启动必要的后台服务
□ 运行 smoke test,确认测试环境可用
□ 向开发者汇报初始化结果,确认理解一致后开始执行
最后一步尤为重要:让 Agent 对初始化结果做一次主动汇报,用自己的语言重述"我现在知道什么、打算做什么、有什么不确定的地方”。这类似于飞机起飞前的 crew briefing——在不可逆的动作开始之前,确认所有参与方的理解是一致的。
6.2 Agent 的两种范围失控及其对应约束
Agent 在任务执行过程中存在两个方向的范围失控。一个方向是超限(overreach):完成分配功能时顺手修改了不在当前任务范围内的代码。这种修改通常出于好意——Agent 发现了可以优化的地方——但它破坏了任务边界的可预测性,让代码审查者无法快速判断这次会话的影响范围。
另一个方向是欠完成(under-finish):功能实现后宣告完成,但遗漏了错误处理、边缘案例或文档更新等本应属于任务范围的工作。欠完成和提前宣告完成的区别在于:前者是对任务范围的不同理解,后者是对完成标准的不同理解。表现相同,应对的子系统不同。
对超限的约束来自指令子系统(AGENTS.md 中的受保护路径和工具约束)和门控策略的显式范围定义。对欠完成的约束来自验证子系统(feature_list.json 中的验收标准和 E2E 测试)。这两个失控方向各有其对应的 harness 层级,正是五个子系统分别承担不同职责、协同发挥作用的体现。
6.3 会话连续性与状态交接
每次 Agent 会话必须以明确的状态交接结束,而不是直接结束。这个要求可以操作化为一个可检验的标准:下一个进入这个 repository 的 Agent 或开发者,应该能够在不询问上一个会话参与者的情况下,理解当前状态并继续工作。
满足这个标准需要会话结束时的几个动作:progress.md 和 feature_list.json 必须反映实际的最新状态;git working tree 不能有未提交的中间状态;测试套件应处于可运行状态,如果有失败,原因需要在 progress.md 中注明。
会话结束时的状态交接检查
会话内 交接文件
────────── ────────────────────────────
已完成功能 ───────────────► feature_list.json 状态更新
关键决定 ───────────────► progress.md 决定记录
遗留问题 ───────────────► progress.md 阻塞项注明
代码变更 ───────────────► git commit(无 working tree 残留)
测试状态 ───────────────► 可独立运行(失败原因已注明)
这些要求不应该依赖 Agent 的主动性,而应该通过 AGENTS.md 中的明确规则强制触发。当连续多个会话都以污染状态结束时,类似于 circuit breaker 的机制可以自动触发一个专门的"清理会话”,而不是让每个业务会话都携带清理负担——这与 Claude Code 源码中自动压缩的 circuit breaker 设计解决的是同一类问题:防止局部的状态混乱通过累积效应影响整个系统。
6.4 可观测性与角色分离:封闭循环的最后一块
可观测性在 harness 中不是事后添加的监控系统,而是系统设计的组成部分。当 Agent 在没有观测点的系统里做出错误决定时,通常只能看到最终结果,无法重建 Agent 在什么时刻基于什么信息做出了什么推理,调试退化为猜谜。
Harness Engineering 对可观测性的具体落地是评估者角色分离:执行任务的 Agent 和验证任务结果的 Agent 必须分离,不能用同一个 Agent 既执行又验证。这个原则源于一个系统性观察:对自己实现做出承诺的 Agent,在验证阶段会在同样的认知偏差方向上产生判断。独立的评估者没有这个包袱,它只检查当前代码是否满足 feature_list.json 中的验收标准:
{
"evaluation": {
"feature_id": "auth-001",
"verdict": "partial",
"passing_criteria": [
"POST /api/auth/register 返回 201",
"重复邮箱注册返回 409"
],
"failing_criteria": [
"密码少于 8 位返回 400(实际返回 500)",
"tests/e2e/auth/test_register.py:3 个测试失败"
],
"notes": "auth/validators.py:47 存在边界条件缺失"
}
}
结构化的评估报告有两个用途:为下一个执行会话提供精确的修复目标(不是"测试没过”,而是具体的失败位置和原因),以及作为可聚合的质量指标,让团队追踪 Agent 系统整体的成功率趋势。后者对于采用 harness 工程化的团队来说尤为重要——Agent 系统的输出质量具有更高的方差,只有持续的客观度量才能判断 harness 的设计是否在朝正确的方向演进。
6.5 回到开篇的三个会话
文章开头描述的那个认证模块案例,可以用来检验 harness 五个子系统是否真的各司其职。
第二个会话将 RS256 重新推断为 HS256,根源是状态子系统缺失:会话一的技术决定没有写入 progress.md,会话二启动时看不到这个决定。如果 progress.md 里有"RS256,原因:多服务公钥验证"这一行,HS256 的错误推断就不会发生。
第三个会话在修复冲突时顺手重构了密码哈希模块,根源是指令子系统和范围子系统缺失:没有 AGENTS.md 标注受保护路径,也没有门控策略限制当前 sprint 的操作范围。如果 AGENTS.md 里有"当前任务范围之外的文件不得修改"这条规则,范围蔓延就会在行动前被拦截。
每个会话单独汇报"任务完成"却不触发任何警报,根源是验证子系统缺失:没有独立的评估者,没有 E2E 测试作为客观信号,“完成"只能依赖 Agent 的自我评估。如果 feature_list.json 中的验收标准明确要求测试套件通过,第三个会话引入的新 bug 会在会话内被检测到,而不是留到代码审查时才暴露。
三个会话的混乱,对应着 harness 三个子系统的缺失。这不是事后的合理化,而是 harness 体系从问题出发做反向设计的基本逻辑。