结对编程辅助
本页定义 Driver/Navigator 职责分工、25 分钟 Pomodoro 轮换节奏、真实的 Driver+Agent 协作会话记录、分歧决策规则,以及增量提交的 git commit 格式规范。
SKILL 可规定会话结构:先对齐任务边界与验收标准,再由「驾驶员」实现小块逻辑,「导航员」(人或 Agent)负责命名、边界情况与与现有模式一致性。下文按节展开,并附一轮驾驶员/导航员循环示意图。
角色分工
驾驶员(Driver)拥有编辑焦点:输入代码、运行命令、触发测试。职责是保持小步前进,并在每个可验证点停下来。导航员(Navigator)不抢键盘(除非轮换),持续对照团队约定:可读性、错误处理、与周边模块的耦合方式。
Driver 负责(持有键盘): ✓ 打开编辑器,输入具体代码 ✓ 运行测试命令:npm test / pytest / go test ./... ✓ 执行 git add / commit(按导航员确认后) ✓ 保持每步 ≤ 10 行改动,每步可通过测试或失败明确 ✗ 不做架构决策,不跳过测试验证步骤 Navigator 负责(不持有键盘): ✓ 大声检查命名是否与仓库约定一致 ✓ 标出遗漏的错误处理分支与边界情况 ✓ 对照已有模块检查耦合方向是否合理 ✓ 记录分歧点(见下方分歧决策规则) ✓ 在交换前用一句话总结:「接下来要做 X,风险是 Y」 ✗ 不在驾驶员打字时并行重写代码 每 25 分钟强制交换(Pomodoro 变体): [0-25 min] Driver 实现 → Navigator 导航 [25 min] 5 分钟切换:交接上下文 + 提交当前增量 [30-32 min] 2 分钟复盘:本轮完成了什么、下轮目标是什么 [32-57 min] 新 Driver 接手(原 Navigator 变 Driver)
轮换节奏
每完成一个可运行增量(绿测、或明确失败的单测)、或每修好一个与本轮目标直接相关的失败用例后,考虑交换角色,减轻疲劳与认知偏差。紧急 hotfix 可压缩为「你写我审」的短回合,但仍要口头过一遍变更面与回滚点。
[ 对齐本轮目标 + 验收标准 ]
│
▼
┌───────────────┐
│ DRIVER │ 敲键盘、跑测、小步提交
└───────┬───────┘
│
▼
┌───────────────┐
│ NAVIGATOR │ 命名 / 边界 / 与现有模式一致
└───────┬───────┘
│
▼
[ 本轮可运行增量完成? ]
│
┌──────────┴──────────┐
│ no │ yes
▼ ▼
[ 继续:缩小范围或补信息 ] [ 轮换角色 或 记录决策 ]
- 轮换前用一句话交接:「接下来文件 X 里要实现 Y,已知风险是 Z。」
- 长时间会话建议设墙上时钟提醒,避免两人同时陷入实现细节。
Agent 作导航员
Agent 适合承担导航员角色时:主动要求展示调用栈、相关文件片段或测试输出,避免在信息不足时大段改写。人类驾驶员应定期口述当前假设(「我认为这里会先走缓存」),便于模型纠正方向而非附和幻觉。
下面是一段真实的 Driver(人类)+ Agent(导航员)协作会话记录:
Driver:「我要给 checkout API 加一个优惠码校验,现在在写 validateCoupon 函数」
Agent(导航员):「好,我看到你要调用 coupon.findOne(),在写之前:
1. findOne 返回 null 时你打算怎么处理?
2. 同一个用户同一订单用两次同一优惠码的情况考虑了吗?」
Driver:「第一个我返回 { valid: false, reason: 'NOT_FOUND' },
第二个还没考虑,先跳过吧」
Agent:「跳过可以,但先加个 TODO 注释:
// TODO(JIRA-456): 防重用校验,同用户同码只能用一次
这样后续 PR 可以追踪」
Driver:「好,加上了。现在写测试——」
Agent:「先不写实现,先写一个失败的测试:
it('should reject invalid coupon code', async () => {
const result = await validateCoupon('INVALID', orderId);
expect(result.valid).toBe(false);
expect(result.reason).toBe('NOT_FOUND');
});
测试红了之后再写实现,TDD 步骤。」
Driver:「明白,运行 npm test — 测试红了,现在写实现」
(25 分钟后)
Driver:「实现完了,测试绿了,准备提交」
Agent:「提交信息格式:feat(checkout): add coupon validation with NOT_FOUND handling
然后交换角色——下一轮你来导航员」
当 Agent 暂时充当驾驶员(例如生成脚手架)时,人类应切回导航员,显式验收每一步:编译、类型检查、与现有目录结构是否一致。
意见分歧决策规则
分歧发生时的处理步骤: 1. 导航员提出异议,驾驶员暂停,双方各用 1 句话陈述理由 2. 判断类型: a. 技术事实分歧(「这样写会有内存泄漏」)→ 写测试验证,数据说话 b. 风格/命名分歧 → 查现有仓库规范;若无规范则 Driver 决定,记录 TODO c. 架构/设计分歧 → 超出本轮范围,记录为 ADR 草稿,本轮用最简单实现 3. 最终决策者:Driver 在本轮持有最终决策权 4. 分歧记录格式(写进 git commit body 或 PR 描述): [DECISION] 选择方案 A 而非 B,原因:XXX [DEFERRED] 方案 C 暂缓,待 JIRA-789 中确认
增量提交的 git commit 格式
# 每次 Pomodoro 结束后提交,格式: # <type>(<scope>): <简短描述>(50 字内) # # [可选 body:本轮实现了什么 / 决策记录] # 示例: feat(auth): add JWT token refresh endpoint - 实现了 POST /api/auth/refresh - 过期 token 返回 401,非法签名返回 403 - [DECISION] 用 RS256 而非 HS256,支持多服务验证 test(auth): add unit tests for token refresh - 覆盖正常刷新、过期 token、签名错误三个分支 - 使用 jest.useFakeTimers 模拟 token 过期 # 禁止的提交格式: # git commit -m "fix stuff" ← 无法追踪 # git commit -m "WIP" ← 不可 revert 到有意义状态 # git commit -m "更新了很多文件" ← 无具体语义
PR 与审查卫生
结对的价值一部分要沉淀给异步审查者:在 PR 描述中写清「结对结论」——哪些方案被否决、为什么、有没有未竟事项。复杂重构可链接 ADR 或 issue,避免审查者重复争论已在键盘前 resolved 的点。
- 复杂改动结对前先跑通基线测试与类型检查,PR 只承载增量差异。
- 需要后续跟进的项用 checklist 或 TODO(带 owner),不要只留在对话记录里。
---
name: pair-programming
description: Driver/Navigator 结对协作、Pomodoro 轮换、增量提交与分歧决策
---
# 会话开始(每次结对前执行)
1. 明确本轮目标(1 句话):「本轮实现 POST /api/coupon/validate,含 NOT_FOUND 和 EXPIRED 两个错误路径」
2. 明确验收标准:「npm test 绿,类型检查通过,有 ≥ 2 个测试用例」
3. 指定当前角色:Driver=人类,Navigator=Agent
# Driver 每步规范
4. 写测试(先 RED)→ 写实现(GREEN)→ 重构(REFACTOR)
5. 每步改动 ≤ 10 行,每步运行一次测试命令
6. 发现思路卡住时,出声描述当前状态,让 Navigator 介入
# Navigator 检查项(每步结束时)
7. 命名与仓库约定一致?(检查 src/utils/ 里的命名模式)
8. 错误分支有没有遗漏?(网络超时 / 数据库异常 / 并发情况)
9. 是否引入了新的外部依赖(需 review deps)?
10. 测试是否断言了可观察行为(而不只是实现细节)?
# 25min Pomodoro 轮换步骤
11. [25min] Driver 提交当前增量:git add -p && git commit -m "feat: ..."
12. [+5min] 交接口述:「我实现了 X,已知问题是 Y,下一步是 Z」
13. [+2min] 复盘:本轮达成了什么,下轮从哪里开始
14. 角色互换:原 Driver 变 Navigator,原 Navigator 变 Driver
# 分歧决策
15. 技术分歧 → 写测试验证,数据说话
16. 风格分歧 → 查仓库规范,无规范则 Driver 决定
17. 架构分歧 → 本轮用最简实现,记录 [DECISION] 注释待后续 ADR
# 会话结束
18. 推送分支,检查 CI 状态
19. 在 PR 描述中写「结对结论」:否决了哪些方案、遗留了哪些 TODO