第 20 章 | 基于文件的状态机

更新于 2026/5/11
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)

第 20 章:基于文件的状态机

学习目标

理解为什么状态要存在文件里而不是 main Claude 的"记忆"里——这是系统从"玩具"变"生产级"的分水岭。

内存状态 vs 文件状态

flowchart LR
    subgraph Bad["内存状态(坏)"]
        M1["main Claude 记得
'组 3 测试通过了'"] end subgraph Good["文件状态(好)"] F1["test-reports/3.md
**Status:** PASS"] end M1 -->|/clear / 崩溃| Lost["状态丢失"] F1 -->|/clear / 崩溃| Recover["重读文件即恢复"] style Bad fill:#ffcdd2 style Good fill:#c8e6c9

→ 文件状态 = crash-safe + 可审计 + 可恢复

完整状态机

stateDiagram-v2
    [*] --> PENDING
    PENDING --> DEVELOPING : dispatch developer
    DEVELOPING --> TESTING : DEV_DONE
    TESTING --> DEVELOPING : FAIL
    TESTING --> REVIEWING : PASS
    REVIEWING --> DEVELOPING : REJECTED
    REVIEWING --> GROUP_DONE : APPROVED
    GROUP_DONE --> [*]

    DEVELOPING --> ESCALATION : round >= 3
    TESTING --> ESCALATION : fail >= 5
    REVIEWING --> ESCALATION : reject >= 3
    ESCALATION --> STUCK : architect dispatched
    ESCALATION --> DEVELOPING : developer-deep PATH B

    STUCK --> [*] : 人决策

状态读取表(CLAUDE.md 的核心)

每个状态对应读哪个文件、看哪个字段

状态 判定方式
PENDING 这个组的 tasks 全是 - [ ] 且无 review/N.md
DEVELOPING 部分 - [x] 但未全部
DEV_DONE 全部 - [x]
TEST_PASSED test-reports/N.md 存在 + **Status:** PASS
APPROVED review/N.md 存在 + **Status:** APPROVED
STUCK STUCK.md 提到 group N
GROUP_DONE DEV_DONE && TEST_PASSED && APPROVED

→ main Claude 每轮重读文件判定状态,不缓存。

状态文件的 schema

每个状态文件必须有固定可解析格式:

# Review for Group 3

**Status:** APPROVED          ← 这一行是状态机读的
**Reviewer Round:** 1         ← 这是计 round 数的
**Reviewed Commit:** abc123

## Findings
(具体内容)

## Required Changes
(仅 REJECTED 时)

格式必须严格——**Status:** APPROVED 中间有空格、有冒号、有星号,少一个 main Claude 都解析不到。

三个核心状态文件的关系

flowchart TB
    Tasks["tasks.md
(checkbox 勾选状态)"] -.-> ST1["DEV 状态"] TR["test-reports/N.md
**Status:** PASS|FAIL"] -.-> ST2["TEST 状态"] Rev["review/N.md
**Status:** APPROVED|REJECTED"] -.-> ST3["REVIEW 状态"] Stuck["STUCK.md
(架构师诊断)"] -.-> ST4["STUCK 状态"] ST1 --> Combined["组 N 综合状态"] ST2 --> Combined ST3 --> Combined ST4 --> Combined style Combined fill:#c8e6c9

一次状态判定的代码(伪)

main Claude 心里跑的逻辑:

def get_group_state(N):
    if exists(f"STUCK.md") and N in STUCK.md:
        return "STUCK"
    
    tasks = parse_tasks_md()
    group_tasks = tasks[N]
    if all(t.checked for t in group_tasks):
        dev_done = True
    elif any(t.checked for t in group_tasks):
        return "DEVELOPING"
    else:
        return "PENDING"
    
    if exists(f"test-reports/{N}.md"):
        report = read(f"test-reports/{N}.md")
        if "**Status:** PASS" in report:
            test_passed = True
        else:
            return "DEVELOPING (回炉)"
    else:
        return "TESTING (待跑)"
    
    if exists(f"review/{N}.md"):
        review = read(f"review/{N}.md")
        if "**Status:** APPROVED" in review:
            return "GROUP_DONE"
        else:
            return "DEVELOPING (回炉)"
    else:
        return "REVIEWING (待评)"

→ 这就是 dev.md Step 3 在做的事。

Crash recovery 演示

你跑 /dev 1
  ↓
组 1 跑到 REVIEWING 阶段
  ↓
你机器突然死机 / Claude Code 崩溃
  ↓
重启 Claude Code
你: /dev 1
  ↓
main Claude 读文件:
  - tasks.md 显示 1.1~1.4 全勾
  - test-reports/1.md 有 **Status:** PASS
  - review/1.md 不存在
  ↓
判定: 状态 = REVIEWING (待评)
  ↓
直接 dispatch reviewer,不重做前面的工作

→ 完美恢复,没丢一步。

这套设计的隐藏好处

✅ git diff 即可看出 "这次跑改了什么状态"
✅ git log 即历史记录,知道每个 round 何时发生
✅ 状态可被外部脚本读取(监控、报表)
✅ 切换设备工作(笔记本 → 台式机)零成本
✅ 多人协作(A 跑了一半,B 接上)只要看文件

反模式

❌ main Claude 用对话历史记状态
   → /clear 后丢失

❌ 状态文件没固定 schema
   → "**Status: APPROVED**" 和 "**Status:** APPROVED" 都有
   → 解析失败

❌ 用 emoji 当状态标记 ("✅ 完成")
   → 看着好看,但解析脆弱

❌ 状态分散在多个无关文件
   → 还原状态要读 10 个文件 = 心累

❌ 写状态又改变状态
   → e.g. reviewer.md 写完 review 后又改 tasks.md → 角色越界

你现在能做什么

  • 画出自己项目的状态机
  • 设计每个状态文件的 schema
  • 解释 crash-safe 是怎么实现的
  • 知道为什么要"每轮重读文件而不是缓存"

flowchart LR
    Ch1to10["Ch 1~10
第一个 change
(知识层基础)"] --> Ch11to13["Ch 11~13
spec/design/tasks
写到能跑"] Ch11to13 --> Ch14to18["Ch 14~18
多 agent 设计
含升级链"] Ch14to18 --> Ch19to20["Ch 19~20
CLAUDE.md
+ 状态机"] Ch19to20 --> You["✓ 拥有完整
'治理层 + 知识层'
等待和工具层结合"] style You fill:#c8e6c9

核心检查点:现在你应该已经能:

  • 为自己的项目写一份 4 件套(proposal/design/spec/tasks)
  • 设计 4~6 个角色的 agent 文件
  • 写出 CLAUDE.md 让 main Claude 按规则办事
  • 解释整个状态机和升级链如何工作

如果还没——回到对应章节重做。