双终端协作模式

Dual Terminal Skill — 白皮书
v4.5 · 2026-07-11
进程级隔离的双模型架构 — Worker 和 Verifier 运行在两个完全独立的终端中,通过文件系统状态机协调,把"审查是否走过场"的风险从技术上的自我偏袒,转移成协议纪律问题。具体哪些是技术保证、哪些只是协议约定,见下方"防作弊原理"一节。
目录
一、架构概述 二、核心优势 三、文件结构 四、安装步骤 五、使用流程 六、状态机说明 七、等待机制说明 八、防作弊原理 九、Verifier 问责机制 十、v3:真实项目复盘 十一、里程碑清单 十二、故障排查 十三、自定义配置 十四、架构原理(深入) 附录:人最好在哪些地方介入

一、架构概述

双终端协作架构
Worker 终端 A
• 读任务 + 违规记录
• 规划里程碑 (plan.md)
• checkpoint + 按里程碑改码
• 确认改动落地 → 提交
Verifier 终端 B
• 解析 LIMIT + 读取漏判记录
• 核对里程碑 + 交叉核对
• 审查(技术一~四)
• 勾选里程碑 + 结论
.dual-claude/ — status.txt · task.txt · plan.md · worker-output.txt · verifier-report.txt · iteration.txt · limit.txt · violation-log.txt · event-log.txt · ...
两终端完全独立,仅通过 .dual-claude/ 目录异步通信 · Worker 无迭代上限(while true)· Verifier 有迭代上限(while ITER < LIMIT)

关键设计:两个终端的 Claude 实例完全不共享内存、上下文或进程。它们只通过 .dual-claude/ 目录下的文本文件进行异步通信。

关于隔离性的诚实说明:进程级隔离是真实的——两个终端确实是两个独立的 Claude Code 会话,互相看不到对方的上下文。但 .dual-claude/status.txt 只是一个普通文本文件,没有任何操作系统级的权限隔离、加锁或签名校验。Worker 在技术上完全可以直接写入 APPROVED。真正让它比单模型可靠的地方是:没有任何一个模型实例同时控制"审查者看到什么"和"审查结果如何呈现给你"

二、核心优势

特性双终端Subagent单模型
进程隔离✅ 真正独立进程✅ 独立 context(同一 CLI 内)❌ 同一实例
上下文隔离✅ 互不可见✅ Subagent 本身有独立 context❌ 完全共享
编排控制权✅ 分散在两个独立会话 + 人类⚠️ 单一父级实例控制 prompt 构造与结果转述❌ 单一实例
模型权重隔离✅ 可配置不同模型⚠️ 通常同一模型实例❌ 同一模型
绕过审查门槛需主动违反协议(无 OS 级强制)父级实例可单方面构造/转述审查结果无隔离,风险最高
自动化程度⚠️ 需人工/脚本协调✅ 内置调度✅ 完全自动
适用场景高 stakes 代码审查日常开发简单任务

关于和 Subagent 的真实差异:Claude Code 的 subagent 本身也运行在独立的 context 窗口里,"同一进程共享内存"不是它的真实弱点。真正的风险点是:subagent 的审查 prompt 由父级实例构造、审查结果也由父级实例读取后转述给你——如果父级实例本身有偏向,它可以在"喂给审查者看什么"和"把审查结果说成什么"这两个环节动手脚。双终端方案的价值在于去掉了这个单点:人类分别启动两个会话、分别输入指令,没有一个模型实例同时握着"构造审查输入"和"转述审查输出"这两把钥匙。


三、文件结构

解压后的文件

dual-terminal-skill/
├─ SKILL-worker.md              # Worker Skill 定义
├─ SKILL-verifier.md            # Verifier Skill 定义
├─ scripts/
│  ├─ start-dual-terminal.sh    # 初始化脚本
│  ├─ watch-status.sh           # 状态监控(结构化仪表盘)
│  ├─ reset.sh                  # 重置脚本
│  ├─ wait-for-status.sh        # 有界轮询等待
│  ├─ set-status.sh             # 状态原子写入
│  ├─ bump-iteration.sh         # 迭代计数递增
│  ├─ log-verifier-miss.sh      # 记录 Verifier 漏判
│  ├─ checkpoint.sh             # 创建回滚点
│  ├─ rollback-help.sh          # 回滚指引(不自动执行)
│  ├─ set-task.sh               # 更新 task.txt + 归档
│  ├─ next-round.sh             # 新一轮需求
│  ├─ track-blocker-streak.sh   # 追踪阻塞问题轮数
│  └─ heartbeat.sh              # 阶段上报
├─ README.md                    # 本文件
└─ .gitignore

安装到项目后的结构

your-project/
├─ .claude/skills/
│  ├─ worker-terminal.md        ← 从 SKILL-worker.md 复制
│  └─ verifier-terminal.md      ← 从 SKILL-verifier.md 复制
├─ scripts/                     ← 13 个脚本(同上)
├─ .dual-claude/                ← 运行时自动生成
│  ├─ status.txt                ← 状态机
│  ├─ created_at.txt            ← 启动时间
│  ├─ task.txt                  ← 当前任务描述
│  ├─ task-history.md           ← 任务历史归档
│  ├─ plan.md                   ← 里程碑清单
│  ├─ plan-history.md           ← 计划历史归档
│  ├─ worker-output.txt
│  ├─ verifier-report.txt
│  ├─ verifier-report-round-N.txt  ← 每轮审查归档
│  ├─ iteration.txt             ← 迭代计数
│  ├─ limit.txt                 ← 迭代上限
│  ├─ violation-log.txt         ← Worker 违规记录
│  ├─ verifier-violation-log.txt  ← Verifier 漏判记录
│  ├─ no-blocker-streak.txt     ← 连续无阻塞轮数
│  ├─ checkpoint-count.txt      ← 回滚点计数
│  ├─ checkpoints/round-N-*/    ← 文件快照
│  ├─ worker-phase.txt          ← Worker 当前阶段
│  ├─ verifier-phase.txt        ← Verifier 当前阶段
│  ├─ event-log.txt             ← 状态转移日志
│  ├─ .wait-elapsed-worker      ← Worker 累计等待
│  └─ .wait-elapsed-verifier    ← Verifier 累计等待
├─ .git/                        ← 自动 git init(可跳过)
└─ .gitignore                   ← 自动追加 .dual-claude/

四、安装步骤

第一步:放置 Skill 文件

mkdir -p .claude/skills
cp SKILL-worker.md .claude/skills/worker-terminal.md
cp SKILL-verifier.md .claude/skills/verifier-terminal.md

文件名必须是 worker-terminal.mdverifier-terminal.md

第二步:放置脚本文件

mkdir -p scripts
cp scripts/*.sh scripts/
chmod +x scripts/*.sh

start-dual-terminal.sh 用自身所在路径的上一级作为项目根目录来创建 .dual-claude/,必须放在项目根下的 scripts/ 子目录里。

第三步:验证安装

# 检查文件是否存在
ls -la .claude/skills/worker-terminal.md
ls -la .claude/skills/verifier-terminal.md
ls -la scripts/

# 预期 13 个脚本:
# start-dual-terminal.sh  watch-status.sh  reset.sh  wait-for-status.sh
# set-status.sh  bump-iteration.sh  log-verifier-miss.sh
# checkpoint.sh  rollback-help.sh  set-task.sh  next-round.sh
# track-blocker-streak.sh  heartbeat.sh

五、使用流程

准备阶段

cd your-project
bash scripts/start-dual-terminal.sh "帮我写一个 JWT 认证中间件"
# 或从文件加载:bash scripts/start-dual-terminal.sh task.md

初始化后,终端输出指引说明,告诉你分别在终端 A 和 B 启动 Claude Code,输入"开始工作"和"开始审查"。

提示:同一个项目,审查完一版想给新一轮需求继续做,用 bash scripts/next-round.sh "<新一轮目标>"。它更新 task.txt(旧内容自动归档到 task-history.md),归零 status.txt / iteration.txt / no-blocker-streak.txt,但保留 violation-log.txt / verifier-violation-log.txt 两份信誉记录。真要连信誉记录都清空才用 reset.sh

终端 A — Worker 执行

打开新终端窗口,执行 cd your-project && claude,输入 开始工作

Claude 自动:读取任务 → 创建回滚点 → 查看项目 → 生成代码 → 写入 worker-output.txt → 设置 WORKER_DONE → 进入有界轮询等待审查。

[Worker] 读取任务: 帮我写一个 JWT 认证中间件
[Worker] 查看项目上下文...
[Worker] 生成代码...
[Worker] 写入输出文件...
[Worker] 设置状态: WORKER_DONE
[Worker] 等待 Verifier 审查...

终端 B — Verifier 执行

打开另一个新终端窗口,执行 cd your-project && claude,输入 开始审查

检测到 WORKER_DONE → 读取 Worker 输出 → 独立审查 → 写入审查报告 → 设置 NEEDS_FIX / APPROVED / REJECTED

[Verifier] 检测到状态: WORKER_DONE
[Verifier] 读取 Worker 输出...
[Verifier] 独立审查中...
[Verifier] 发现问题: JWT_SECRET 未做非空检查
[Verifier] 写入审查报告...
[Verifier] 设置状态: NEEDS_FIX

终端 C — 监控状态

bash scripts/watch-status.sh

v3.3 之后重写为结构化仪表盘,每秒检查变化,有变才重绘。展示内容:

双终端协作监控        刷新: 14:33:55   (Ctrl+C 退出)

状态机
  当前状态: NEEDS_FIX       迭代: 2 / 5
  连续无新增[阻塞]轮数: 0

Worker(终端 A)
  当前阶段: 等待 Verifier 审查中
  阶段更新于: 3s前
  累计等待: 12s
  已创建回滚点: 2 个
  历史违规记录: 0 条

Verifier(终端 B)
  当前阶段: 审查中 - 核验测试断言/mock语义/根因
  阶段更新于: 8s前
  累计等待: 0s
  历史漏判记录: 1 条 [!]

任务
  修复棋盘坐标偏移,标签与边框重叠的问题...

计划进度
  里程碑: 2 / 5 已完成
  当前: M3: 棋盘外框 padding 重新计算

最近事件(最多 10 条)
  14:32:10 [Worker] IDLE -> WORKER_DONE (iter=0)
  14:32:45 [Verifier] WORKER_DONE -> NEEDS_FIX (iter=1)
  14:33:20 [Worker] NEEDS_FIX -> WORKER_DONE (iter=1)

需要关注
  (暂无)

需要关注区块会在这些情况下报警:一方累计等待超过 5 分钟升级阈值、迭代已接近上限、违规/漏判记录新增。APPROVED 且 Verifier 有历史漏判记录时,额外提示"建议人工抽查一遍"。


六、状态机说明

IDLE(等待开始)
↓ Worker 开始工作
WORKER_DONE
↓ Verifier 开始审查
NEEDS_FIX / APPROVED / REJECTED
APPROVED 通过
NEEDS_FIX Worker 修正
REJECTED 重新理解任务
↓ ↑ 循环
WORKER_DONE(循环)
迭代达到上限时:Verifier 最后一轮只给出 APPROVED 或 REJECTED
状态含义谁可以操作
IDLE等待 Worker 开始仅 reset.sh
WORKER_DONEWorker 已提交,等待审查仅 Worker
NEEDS_FIX需要 Worker 修正仅 Verifier
APPROVED审查通过,任务完成仅 Verifier
REJECTED严重问题,需重新理解任务仅 Verifier
上面"谁可以操作"是协议约定,不是文件权限强制——详见下方"防作弊原理"一节。

七、等待机制说明

v2 之前,等待直接写 while [ status != X ]; do sleep 10; done,期望模型一次调用到底。问题是等待对象通常是"人类去开另一个终端、启动 Claude Code、敲下指令"的纯人工耗时,几分钟到几十分钟不等,很容易撞上 Claude Code Bash 工具的执行时长上限。

v2 版拆成 scripts/wait-for-status.sh

v3 加了中间档:累计等待超过 300 秒 / 5 分钟时,返回码仍然是 1,但输出 TIMEOUT_ESCALATE 并发出响铃,模型会顺手提醒用户看一眼对方终端——"顺手说一声",不是"停下来"。

三档超时小结:90 秒单次窗口 → 5 分钟升级提醒 → 30 分钟硬上限。


八、防作弊原理

.dual-claude/ 下的所有文件都是普通文本文件,Worker 理论上可以直接 echo APPROVED > .dual-claude/status.txt。这套方案不提供技术上"不可能"的保证。

作弊方式实际情况这套方案做了什么
伪造审查结果技术上做不到——Worker 在终端 A,看不到终端 B 里 Verifier 的推理过程靠上下文隔离防住
跳过审查技术上可以(直接写 APPROVED),但需主动违反规则set-status.sh 防手滑,防不住故意违规
篡改审查报告Worker 可以编辑 verifier-report.txt协议纪律,无文件权限强制
影响 Verifier 判断两个终端完全独立,Worker 无法访问 Verifier靠隔离防住
隐藏已知问题Verifier 若只采信自我声明,隐瞒可以蒙混Verifier 要求实际验证,隐瞒写入 violation-log.txt

真正被技术手段锁死的只有两条:Worker 看不到 Verifier 的推理过程,以及Worker 无法在 Verifier 生成结论的当下影响它。除此之外的所有防线都是"skill 文件写清楚规则 + 两个独立会话之间没有单一控制点能同时操纵输入和输出"。

隔离性对比

单模型(一个 Claude):
  ┌─────────────────┐
  │   Claude Code   │
  │  Worker + Verifier │  ← 同一上下文,容易自我偏袒
  └─────────────────┘

Subagent(同一父级实例编排):
  ┌─────────────────┐
  │   父级 Claude    │
  │  ┌───┐  ┌───┐  │
  │  │W  │  │V  │  │  ← W/V 各有独立 context,
  │  └───┘  └───┘  │     但父级实例构造两者的 prompt
  └─────────────────┘     并转述 V 的结论——单点

双终端(独立进程):
  ┌─────────┐   ┌─────────┐
  │ Claude  │   │ Claude  │
  │ Terminal│   │ Terminal│  ← 完全独立,不共享任何资源
  │    A    │   │    B    │
  └─────────┘   └─────────┘
       │              │
       └──────┬───────┘
              ↓
        .dual-claude/  ← 仅通过文件系统通信

九、Verifier 问责机制

为什么 Verifier 比 Worker 更容易偷懒

机制设计

这个机制解决不了什么


十、v3:真实项目复盘带来的修复

Verifier 红线:永远不碰代码

复盘中发生过一次 Verifier 直接改代码的越权事件。一旦 Verifier 改过代码,就不再是独立第三方——审查的是自己参与生成的东西,整个双终端隔离模型的信任基础就没了。

红线管的是代码/实现文件,不包括 plan.md——确认里程碑达标后把 - [ ] 改成 - [x] 是 Verifier 的职责,属于协调文档,不是"碰代码"。

三次真实漏判,同一个模式

看到可验证的数值("22/22 通过")→ 数值本身没问题 → 停止思考。

技术一·数值先翻译再采信:聚合数值不能直接当证据,先为每条测试写一句人话描述它在验证什么,再问覆盖了什么、漏了什么。

技术二·Mock 语义审计:测试里出现 mock 时,验证 mock 的行为语义和真实环境是否一致。不报错不代表测了该测的东西。

技术三·根因三层问:解决现象了吗?打在根因上还是表层?波及对称/关联维度了吗?任何一层答不出具体内容就不能标 PASS。

技术四·实测验证具体化:自动化测试通过 ≠ 改动被验证了。涉及 UI/布局/视觉呈现的改动,必须实际观察渲染结果并贴出具体观察。

记录不是防线

"记录漏判、自我批评本身不是防线——它们制造了'已经处理过了'的心理完结感,反而降低了后续的警觉。" 真正顶用的是"必须产出具体内容才算做到"的强制步骤。

问题分级 + 收敛机制

每条发现强制标注 [阻塞][细节],只有 [阻塞] 能触发 NEEDS_FIX / REJECTEDtrack-blocker-streak.sh 追踪"连续多少轮没有新增阻塞问题",连续 2 轮以上时倾向于结束循环。

终局判断三问(→ 四问)

判 APPROVED 之前必须写出具体内容:

  1. 如果现在判 APPROVED 最可能错在哪里?
  2. 是否处于疲劳状态?标准是否已经漂移?
  3. 是否有"差不多行了"的念头?
  4. plan.md 里的里程碑是不是全部勾选了?(有未完成的不能判 APPROVED)

强制 REJECTED 的情形

情形一:Worker 报告与代码事实不符(诚信问题,直接 REJECTED)。

情形二:同一个 [阻塞] 问题连续 3 轮出现仍未解决(循环没在收敛,REJECTED 打断重启)。

基础设施补丁

v3.2:自动 git init

项目不是 git 仓库时自动 git init(可设 DUAL_CLAUDE_NO_GIT_INIT=1 跳过)。同时确保 .dual-claude/ 被追加到 .gitignore

v3.3:长会话压力测试(68/71 轮)

核心发现:靠脚本/状态机强制的环节全部扛住了;只靠 SKILL 文件文字指令的环节全面衰减。修复思路:能变成强制步骤或具体触发条件的,都不再只是文字建议。


十一、里程碑清单(v3.4)

新增 .dual-claude/plan.md:Worker 在新任务的第一轮,写代码之前,先把 task.txt 拆解成里程碑清单,每条带具体、可核查的验收标准:

# 项目计划

## 里程碑清单

- [ ] M1: hard 模式威胁检测重写
  验收标准: AI 能正确识别并阻止对手活三/冲四
- [ ] M2: AI 防守测试补全
  验收标准: 测试断言检查阻断位置而非仅返回值非空

拆解粒度:小到能一轮做完、独立提交、独立审查。任务本身足够小可以只有一条。

职责分工:Worker 声明"我这轮做的是哪个里程碑",但不能自己勾选完成——完没完成由 Verifier 说了算(在 plan.md 打勾不违反"永不碰代码"的红线)。

这个机制同时防住两种失败模式:防"一次性做完"(Verifier 核对改动范围是否和声明的里程碑一致);防"做到哪里算哪里"(计划完整性变成 APPROVED 的硬性前提)。

没有解决的:拆解质量本身取决于 Worker。Verifier 只核对范围是否一致,不核对这份计划本身的颗粒度是否合理——粒度合不合理还是要靠人看一眼。


十二、故障排查

问题 1:Worker 无法开始工作

检查 .dual-claude/ 目录是否存在,不存在则重新 bash scripts/start-dual-terminal.sh

问题 2:Verifier 无法读取 Worker 输出

查看 status.txt,应该是 WORKER_DONE。如果不是,用 bash scripts/set-status.sh WORKER_DONE 手动修复(不要直接 echo)。

问题 3:Worker 无法读取审查报告

查看 status.txt,应该是 NEEDS_FIX / APPROVED / REJECTED。如果 Verifier 已完成但状态未更新,查看 verifier-report.txt 内容后手动设置状态。

问题 4:迭代次数达到上限,循环无法结束

到达上限那轮 Verifier 必须直接判 APPROVED 或 REJECTED。如果卡住,人工介入查看报告和违规记录后手动决定。

问题 5:状态混乱,想重新开始

bash scripts/reset.sh
# 输出:
# 🔄 重置双终端协作状态...
# ✅ 状态已重置
# 备份目录: .dual-claude/backup-20260706-143020
# 当前状态: IDLE
# 迭代次数: 0

问题 6:等待轮询一直卡着不动

这是预期行为——wait-for-status.sh 单次最多等 90 秒,等不到会自己重试。累计超过 30 分钟会提示你检查另一个终端。


十三、自定义配置

修改迭代次数限制

默认值只在 verifier-terminal.md 里:

# 如果说了"循环N次"或"循环N轮",LIMIT = N
# 如果没说,默认 3 次

也可以在启动 Verifier 时口头指定"开始审查,本次循环5次"。

修改审查维度

编辑 .claude/skills/verifier-terminal.md 的审查清单章节。

使用不同的模型

两个终端可以独立配置不同的 Claude 模型:

# .claude/skills/worker-terminal.md
---
name: worker-terminal
model: claude-sonnet-4

# .claude/skills/verifier-terminal.md
---
name: verifier-terminal
model: claude-opus-4

更换共享目录位置

同时改全部 13 个脚本里的 DUAL_DIR 定义。

关闭自动 git init

DUAL_CLAUDE_NO_GIT_INIT=1 bash scripts/start-dual-terminal.sh "任务描述"

十四、架构原理(深入)

文件系统状态机解决的是"协调"问题,不是"信任"问题

Subagent(父级实例编排):
  W/V 各自有独立 context → 但父级实例读取 V 的输出后,
  由父级实例决定怎么转述给你
  └─ 风险不在"内存篡改",在于转述环节是单点

文件系统状态机(双终端):
  Worker 生成结果 → 写入文件 A → 设置状态 S1
  Verifier 读取文件 A → 写入文件 B → 设置状态 S2
  Worker 读取文件 B → 等待状态 S2
  └─ 好处是"协调节点"客观存在、双方都能看到同一份文件;
      不代表 Worker 没有能力去写 S1/S2 之外的值

状态写入为什么用 set-status.sh

# v1 的写法(有风险):
echo "WORKER_DONE" > .dual-claude/status.txt
# 写入过程中被中断,另一端可能读到半截内容;且无取值校验

# v2 的写法:
bash scripts/set-status.sh WORKER_DONE
# 写临时文件 → mv 覆盖(原子操作);同时校验状态名是否合法

关于 Worker 试图伪造状态

如果 Worker 直接把状态写成 APPROVED,而人类看到后认为任务完成,这个伪造在那一刻就已经"生效"了——不会有任何机制自动纠正。

真正能用的检测依据是伪造的状态前面缺了该有的铺垫:正常的 APPROVED 前面必然有 iteration.txt 递增、verifier-report.txt 更新、verifier-phase.txt 走过各阶段。如果状态突然跳到 APPROVED 但迭代数没变、报告还是旧的,就需要人工核对了。


附录:这套框架里人最好在哪些地方介入

下面不是泛泛的"注意事项"清单,是按系统自己在设计上已经承认弱、或者主动选择不自动决定这个标准排的。

第一档:系统结构性弱的地方,人必须补位

1. APPROVED 之前的抽查,尤其当 Verifier 有历史漏判记录时

这是整套框架里最薄弱的一环。watch-status.sh 专门为这个场景写了警告。Verifier 偷懒的产出和认真审查的产出长得一模一样——都叫"PASS"——而 APPROVED 是终局,没有下一轮纠错。如果只挑一个地方投入人工精力,是这里。

2. plan.md 里程碑拆解质量

Verifier 只核对"这轮改动是否符合声明的里程碑",不核对"这份拆解本身合不合理"。一个拆得过粗的清单能让所有防一次性做完的机制形同虚设。建议在 Worker 第一轮写出 plan.md 后、进入正式循环前,人看一眼。

3. 强制 REJECTED 触发时(尤其"同一[阻塞]连续 3 轮未解决")

系统识别了"没在收敛"的现象,但诊断不出为什么没收敛。看到 REJECTED,先看报告里的根因和几轮修复尝试,别直接 next-round.sh 重来。

4. 长会话下持续盯 watch-status.sh

wait-for-status.sh 解决的是"单次调用不会被系统杀掉",解决不了"模型在长会话里会不会持续轮询"。这是主动放弃在脚本层面继续打补丁、转而要求人介入的地方。

第二档:低频出现,但一次判断影响很大

5. 初始 task.txt 和 LIMIT 的设定——没人能替你想清楚要做什么、允许几轮。

6. rollback-help.sh 之后要不要真的回滚——刻意设计成人工专属:回滚是有损操作,只有人清楚值不值得。

7. REJECTED 之后怎么办——系统完全没涉足这个决策树,全部留给人。

第三档:出现信号时值得看一眼

一句话总结:系统自己已经在设计上明说"这里我不替你决定"的地方(APPROVED 前、REJECTED 后、rollback 执行),和系统在文档里承认"这里防不住"的地方(plan.md 粒度、长会话轮询纪律),加起来就是人真正该花精力的地方——其余被脚本和强制步骤覆盖的环节,人工重复检查的边际价值很低。

双终端协作模式 · 基于 MIT 许可开源

GitHub →

去掉编排过程里的单一控制点,让审查结论不由同一个模型实例既构造又转述。