# 架构图绘制规范：从「目录树配色版」到「可被工程师质询的图」

> **给谁看**：负责生成 `six_projects_architecture_gallery.md` 及后续架构图的 Agent。
> **写作立场**：产品架构师视角。下面每一条都是**可执行的判据**，不是审美建议。
> **底稿**：本仓库 `项目分析/` 下的十二份逐文件解析（全部标注 `文件:行号` 并已回验）+ `参考项目/` 下的源码树。
> **一句话诊断**：现在这六张图的问题**不在画得不好看，而在选错了体裁**——它们是「带配色的目录树」，不是架构图。

---

## 第一部分 · 先把病因说准

### 1.1 现在这六张图共享同一个结构，这本身就是信号

把六张 Mermaid 抽象一下，它们**全是同一个形状**：

```
subgraph 层1 { A }
subgraph 层2 { B }
subgraph 层3 { C, D }
subgraph 层4 { E }
A --> B --> C --> E
```

六个技术栈、语言、进程模型、交付物**完全不同**的项目，画出来的图却可以互相替换——**这说明图里没有承载任何项目特有的信息**。

一个可靠的自检：**把图上所有文字遮住，只留框和线。如果六张图长得一样，那六张图都没画对。**

> 真正的架构差异应该体现在**拓扑**上：OpenCode 是「一个服务器 + N 个客户端」的星形；Open Design 是「三个进程 + 一个特权中心」的信任分层；grok-build 是「单进程 + Actor 邮箱」的消息拓扑；OpenManus 是「一条继承链 + 一个工具池」的类型拓扑。**这四种拓扑不该长成同一个竖直层叠。**

### 1.2 「分层」是最容易被误用的隐喻

现在的图用 `第 1 层 / 第 2 层 / …` 给 Claude Code 编号。这暗示了一个**严格的自上而下调用栈**。但真实情况是：

- 权限系统（`permissions/`）**不在第 4 层**——它被工具层、主循环、UI 层**同时**调用，是一条**贯穿的横切关注点（cross-cutting concern）**；
- 上下文组装**不是发生在主循环之前的一次性动作**，而是**每一轮都重跑**；
- 工具执行结果要**回流**进上下文，这条回边是整个 Agent 之所以是 Agent 的原因——图上却只有一根单向箭头。

**规则**：只有当 **A 层只能调用 B 层、且 B 层不知道 A 层存在**时，才可以画成上下层。否则请改用别的组织方式（进程分区 / 平面分区 / 生命周期分区）。

### 1.3 图上没有任何「会让人吵起来」的信息

架构图的价值在于**承载可被质疑的判断**。现在的图上每一句话都是不可证伪的描述：「工具注册表」「Agent 主循环」「模型适配层」——没人会反对，也没人能从中学到东西。

**好的架构图应该让读者产生这些反应之一**：
- 「等等，这里为什么是同步的？」
- 「这条边跨进程了，序列化成本呢？」
- 「这个状态放在这一侧，那另一侧崩了怎么办？」
- 「这两个框之间没有线，是真的不通信吗？」

**如果一张图不可能引发上述任何一句话，它就是装饰品。**

---

## 第二部分 · 画之前必须回答的三个问题

**不要一上来就画。**先在草稿里用文字写完这三问，写不出来就不要动笔。

### 问题 1：这张图要回答什么问题？（Question）

一张图**只能**回答一个问题。写不出一句话的问题，就是没想清楚。

| ❌ 不合格的问题 | ✅ 合格的问题 |
|---|---|
| 「Claude Code 的架构」 | 「用户按下回车后，到第一个 token 出现在屏幕上，控制权依次经过哪些组件？」 |
| 「OpenCode 的系统设计」 | 「为什么 OpenCode 能让 TUI、桌面、Zed 三个客户端共享同一个会话状态？」 |
| 「Open Design 有哪些模块」 | 「Open Design 自己不写主循环，那它凭什么保证 25 个第三方 CLI 的产出质量一致？」 |
| 「grok-build 的分层」 | 「Goal Mode 下，一个目标如何在数小时内跨越多次模型调用而不丢失？」 |

### 问题 2：读者是谁，他要拿这张图做什么决定？（Audience & Decision）

| 读者 | 他要做的决定 | 图必须给他 |
|---|---|---|
| 要选型的架构师 | 选哪个项目做二次开发 | 扩展点在哪、哪些是硬编码、改动半径 |
| 要读源码的工程师 | 从哪个文件开始读 | **入口 + 主干路径 + 精确文件名**，支路可省 |
| 要评估风险的 TL | 能不能上生产 | 信任边界、失败路径、状态持久化点 |
| 要抄设计的产品工程师 | 哪个模式值得借鉴 | **那个模式的前因后果**，而不是全景 |

**现在这六张图默认服务第 4 类读者，但内容只满足第 2 类的一半。**

### 问题 3：这张图的「承重墙」是什么？（Load-bearing claim）

每张图必须有**一个**核心论点，图上其余元素都是为了支撑它。找不到承重墙，说明你只是在罗列。

已验证的承重墙示例（来自本仓库的解析文档）：

| 项目 | 承重墙 | 依据 |
|---|---|---|
| Open Design | **daemon 是唯一特权进程**——它绑 loopback，独占 SQLite / 凭证 / 项目文件 / spawn 权；web 只是壳 | `项目分析/open-design-源码分析.md:159` |
| Open Design | **适配器是数据不是类**——26 条定义只描述「怎么跟这个 CLI 说话」，绝不描述「说什么」和「怎么调度」 | 同上 `:453` |
| OpenCode | **服务器即本体**——TUI 与同进程服务器**也走完整 HTTP/RPC**，所以多客户端是免费的 | `项目分析/opencode-源码分析.md` |
| OpenWorker | **带外审批**——权限请求不阻塞会话，落进收件箱，人有空再处理 | `项目分析/openworker-源码分析.md` |
| grok-build | **Goal 不是 prompt，是状态机**——规划/执行/验证/战略师恢复四态持久化 | `项目分析/grok-build-源码分析.md` |
| OpenManus | **继承链是能力的偏序**——`BaseAgent`(循环+stuck 检测) → `ReActAgent`(think/act) → `ToolCallAgent`(工具解析) → 具体 Agent | `项目分析/OpenManus-源码分析.md:91` |

---

## 第三部分 · Agent 系统的六种图（核心方法论）

**「架构图」不是一种图，是六种。**混在一张里画，必然退化成目录树。这是本文最重要的一节。

### 图型 A · 进程与信任拓扑（Deployment / Trust Topology）

**回答**：代码跑在几个地址空间里？谁能碰什么？边界怎么穿越？

**必须画出**：
- 每个**独立进程 / 地址空间**用粗边框区分（不是 subgraph 随手一圈）
- 每条**跨进程边**标注：**传输方式**（HTTP loopback / stdio / Unix socket / IPC 文件）+ **同步性**（阻塞 / 流式 / 轮询）
- **特权不对称**：谁能读凭证、谁能写文件系统、谁能 spawn 进程
- **网络出口**：唯一能出公网的组件（这决定了数据外泄面）

**为什么这张图最该先画**：它决定了后面所有讨论的物理约束。而现在的六张图**一张都没有画进程边界**——OpenWorker 的 Rust 壳和 Python 引擎明明是两个进程，图上却是两个并排的框，看不出中间有一次序列化。

**示例（Open Design，基于已验证事实）**：

```mermaid
flowchart LR
    subgraph BROWSER["① 浏览器 · 无特权"]
        UI["Next.js 16 App Router + React 18"]
    end
    subgraph SIDECAR["② Next.js server 侧车 · 低特权"]
        SSR["SSR / 静态资源"]
    end
    subgraph DAEMON["③ Daemon 进程 · <b>唯一特权方</b>"]
        API["/api/* · 绑 loopback"]
        DB[("SQLite<br/>projects · conversations · runs")]
        CRED["凭证库"]
        SPAWN["子进程 spawn 权"]
    end
    subgraph CHILD["④ 被 spawn 的第三方 CLI · 独立进程 ×N"]
        CLI["claude / codex / cursor-agent …"]
    end
    UI -->|HTTP| SSR
    UI -->|"HTTP · loopback only"| API
    API --- DB
    API --- CRED
    API --> SPAWN
    SPAWN -->|"stdio · SSE/JSON-RPC"| CLI
    CLI -->|"写文件（不回传内容）"| FS[("项目 cwd 文件系统")]
    API -.读取产物.-> FS
```

**这张图立刻能引发的质询**（这就是价值）：
- 「第三方 CLI 是独立进程，那它拿到的凭证是 daemon 转发的还是它自己的？」
- 「CLI 直接写文件系统而不经过 daemon，那 lint 和评审是事后补的？」——**是的，这正是 Open Design 的设计**，图把它暴露出来了。

### 图型 B · 一次回合的时序（Turn Sequence）

**回答**：一次交互里，控制权和数据在时间轴上怎么流动？

Agent 系统的本质是**循环**，而循环**无法用流程图表达**——流程图只能画一次通过。必须用 `sequenceDiagram`。

**必须画出**：
- **循环边界**（`loop` 块）——把「一轮」和「一次会话」在视觉上分开
- **每一轮重跑的东西**（上下文重组、工具集重算）vs **一次性的东西**（会话初始化）
- **回流边**：工具结果如何进入下一轮的输入
- **提前退出点**：哪些条件会中断循环

**示例（Claude Code 主循环骨架，路径已验证存在）**：

```mermaid
sequenceDiagram
    autonumber
    participant U as 用户
    participant R as REPL.tsx
    participant H as handlePromptSubmit.ts
    participant Q as query.ts::queryLoop
    participant C as 上下文组装
    participant A as services/api/claude.ts
    participant T as tools/（43 个）
    participant P as utils/permissions/

    U->>R: 回车
    R->>H: 原始输入
    H->>H: 分流：/命令 · !shell · 普通 prompt
    H->>Q: 进入循环

    loop 每一轮（不是每次会话）
        Q->>C: 重新组装上下文
        Note over C: 系统提示 + CLAUDE.md + git 状态<br/>每轮都重跑，不是启动时一次
        C-->>Q: messages[]
        Q->>A: SSE 流式请求
        A-->>Q: token 流 + tool_use 块
        alt 模型要调工具
            Q->>P: 该工具此次调用允许吗
            P-->>Q: 允许 / 拒绝 / 需询问用户
            opt 需询问
                P->>U: 权限弹窗
                U-->>P: 决定
            end
            Q->>T: 执行
            T-->>Q: 结果
            Note over Q: 结果<b>回流</b>进 messages，进入下一轮
        else 只有文本
            Q-->>R: 收敛，退出循环
        end
    end
```

> **这张图讲清了流程图讲不清的三件事**：① 上下文是**每轮重组**的；② 权限检查在**工具执行之前**、且可能**打断到用户**；③ 循环的**退出条件**是「模型只回文本」。

### 图型 C · 控制平面 vs 数据平面（Control / Data Plane Split）

**回答**：哪些组件在**决定做什么**，哪些在**搬运内容**？

这是电信和云基础设施的经典切分，**在 Agent 系统里同样成立且极其有用**，但现在六张图一张都没用。

- **控制平面**：主循环、权限裁决、目标状态机、调度器、压缩决策
- **数据平面**：token 流、工具 I/O、文件读写、上下文缓冲区

**画法**：用**左右分栏**而不是上下分层，控制平面的线用**实线**，数据平面用**粗线/不同色**。

**为什么值钱**：一眼能看出「这个项目把决策权放在哪」。
- Claude Code：控制平面**全在本地进程**，模型只提建议
- Open Design：控制平面**外包给第三方 CLI**，自己只保留**入口的提示词**和**出口的质量闸门**——这是它最反直觉的地方，也是最该被画出来的
- grok-build：`GoalTracker` 是一个**独立于模型的控制平面状态机**，模型跑偏了它还在

### 图型 D · 状态归属与生命周期（State Ownership）

**回答**：状态存在哪、谁拥有它、活多久、崩了丢什么？

**必须标注每个状态容器的三个属性**：

| 属性 | 取值 |
|---|---|
| **介质** | 内存 / SQLite / JSONL 文件 / 远端服务 |
| **生命周期** | 单次回合 / 单次会话 / 跨会话持久 / 跨机器 |
| **崩溃语义** | 丢失 / 可重建 / 已持久化 |

**示例片段**：

```mermaid
flowchart TB
    subgraph EPH["回合级 · 崩溃即丢"]
        S1["step_context 工具可见集快照"]
        S2["本轮 token 缓冲"]
    end
    subgraph SESS["会话级 · 内存"]
        S3["messages[] 对话历史"]
        S4["审批缓存 approval store"]
    end
    subgraph PERSIST["持久 · 落盘"]
        S5[("rollout / JSONL 会话资产")]
        S6[("SQLite: projects · runs")]
        S7["项目 cwd 的真实文件"]
    end
    S3 -.压缩时截断.-> S3
    S3 -->|每轮追加| S5
```

> **一个不画就看不出的事实**：多数 Agent 的「记忆」其实是**三段不同介质拼出来的**，而工程师最常踩的坑正是搞错了某一段的生命周期。

### 图型 E · 扩展点与改动半径（Extension Surface）

**回答**：我要加一个新能力，改哪里？会波及谁？

**画法**：以**扩展点**为中心，向外画「加这个东西需要动的文件」。

**必须区分三种扩展点**：
1. **数据扩展**（加一条定义 / 一个 JSON）——零代码，改动半径 = 1 个文件
2. **接口扩展**（实现一个 trait / interface）——改动半径 = 新文件 + 一处注册
3. **核心改造**（改主循环）——改动半径不可控

**这张图对「选型」读者是最高价值的**，而现在六张图完全没有。

**Open Design 的例子极具说服力**：接入第 26 个 CLI = **往 `registry.ts` 加一条 `RuntimeAgentDef` 数据**，不写一行新逻辑。这是「适配器即数据」的**可验证后果**，比在框里写「26 条 CLI 适配器」有力一百倍。

### 图型 F · 失败与降级路径（Failure Modes）

**回答**：出错时会发生什么？

**几乎所有业余架构图的共同特征：只画 happy path。**

**必须画出至少三类**：
- **重试边**（带退避策略）
- **降级边**（能力下降但不中断，例：语义检索挂了退回关键词匹配）
- **熔断/终止边**（stuck 检测、步数上限、取消令牌传播）

用**虚线 + 红色**与主路径区分。

---

## 第四部分 · 视觉通道的纪律

**一条铁律：一个视觉通道只编码一个变量。** 现在的图里颜色和框线是纯装饰，浪费了最强的两个通道。

| 通道 | 建议编码 | 禁止 |
|---|---|---|
| **边框粗细** | 进程 / 地址空间边界 | 随意强调 |
| **填充色** | 平面归属（控制/数据/存储/外部） | 按「好看」上色 |
| **线型** | 实线=同步调用；虚线=异步/事件；点线=可选/降级 | 混用 |
| **箭头** | 单向=调用；双向=请求-响应（**慎用，多数时候该拆成两条**） | 用双向箭头掩盖不清楚 |
| **线标签** | 传输方式 + 同步性（`HTTP/SSE`、`stdio`、`轮询 500ms`） | 留空 |
| **形状** | 矩形=组件；圆柱=持久化；菱形=判定；平行四边形=外部系统 | 全用矩形 |

**必须配图例（legend）。** 没有图例的自定义编码 = 没有编码。

### 关于双向箭头

`A <--> B` 在现在的六张图里出现了 5 次，**几乎每次都是在掩盖没想清楚的地方**。

`LOOP <--> PERM` 到底是什么意思？是主循环调权限、权限回答（那就是**一次同步调用**，画单向 + 返回值标注）？还是权限系统会**主动打断**主循环（那是**两条不同的边**，语义完全不同）？

**规则：只有真正的双向对等通信（如 WebSocket 全双工）才用双向箭头。请求-响应请画单向 + 在标签写返回内容。**

---

## 第五部分 · Agent 架构特有的七类承重信息

这是把图从「通用软件架构图」提升到「**Agent 架构图**」的关键。以下七类信息，**每张 Agent 架构图至少要承载三类**。

### ① 上下文装配管线（Context Assembly Pipeline）

Agent 的核心竞争力在这里，但六张图里只有 Open Design 提了一句「20 层上下文组装」。

**该画成**：一条从左到右的管线，每一段标注：
- **内容来源**（系统提示 / 项目说明书 / 工具目录 / 历史 / 环境快照）
- **变化频率**（全局静态 / 会话稳定 / 项目稳定 / 回合可变）
- **是否在缓存前缀内**

> **为什么频率这么重要**：变化频率决定**缓存前缀能有多长**，而缓存前缀直接决定**成本和首字延迟**。把高频变化的内容不小心放在前面，会让整个前缀作废。这是 Agent 工程里最贵的一类 bug，而它在架构图上完全可见。

### ② 停机与循环控制（Termination Protocol）

「什么时候停」是 Agent 最难的部分，也是各家差异最大的地方，**六张图全都没画**。

- Claude Code：模型只回文本即收敛
- Codex：`follow-up × 压缩 × stop hook` 三方协议
- OpenManus：`BaseAgent` 的 **stuck 检测** + `max_steps` 上限
- grok-build：`GoalTracker` 判定目标达成
- MiMo Code：独立裁判模型 + 四道死循环闸门

**该画成**：一个 `stateDiagram-v2`，把「继续 / 压缩后继续 / 钩子要求续跑 / 终止」画成显式状态转移。

### ③ 权限与信任边界（Authorization Boundary）

**必须回答**：谁批准？在什么时候批准？批准被缓存吗？内核层还有第二道墙吗？

Codex 的例子最清晰：**`AskForApproval`（人同不同意）× `SandboxType`（内核允不允许）是两道正交的墙**——用户点了同意，OS 沙箱照样可能拒绝。**这种正交关系必须画成二维，不能画成串联的两个框。**

### ④ 工具装配的动态性（Tool Set Resolution）

现在的图都把工具画成一个静态的框（「40+ 工具」「~25 Rust 工具」）。但在成熟实现里，**「这一轮模型能看见哪些工具」是每轮算出来的**，输入包括：模型家族、当前模式、已连接的 MCP server、技能激活状态、权限策略。

**该画成**：一个有输入的**求值节点**，而不是一个静态清单。

### ⑤ 模型边界（Model Boundary）

**必须明确标出「这条线之外就是模型」**，并标注协议形态（Anthropic Messages / OpenAI Responses / 自研 runtime）。

**理由**：模型边界是**唯一不可靠、不可调试、有成本、有延迟**的边。它在图上必须视觉上最醒目。现在六张图把它画成一个和别人一样的普通框。

### ⑥ 子 Agent / 并发拓扑

有子 Agent 的项目（Claude Code 的 `AgentTool`、grok-build 的 `WorkflowManager`、OpenManus 的 `PlanningFlow`）必须画出：
- **谁能 spawn 谁**（有向图，注意是否允许递归）
- **深度上限**在哪
- 子 Agent 的**上下文是继承还是隔离**
- 结果**怎么汇回**

### ⑦ 交付物出口（Artifact Egress）

Agent 最终改变世界的那个点：写文件 / 发消息 / 改日历 / 提 PR。

**该标注**：是否可逆、是否经过审批、是否有 dry-run。

---

## 第六部分 · 六个项目的具体处方

针对现有六张图，各给一个**替换方案**（不是补充——现在这张应当被替换）。

| 项目 | 现在画的 | **应该画的** | 主图型 |
|---|---|---|---|
| **Claude Code** | 5 层竖直堆叠 | **一次回合的完整时序**：分流 → 每轮重组上下文 → SSE → 权限裁决 → 工具 → 结果回流 → 收敛判定 | B + ② |
| **Open Design** | 4 个功能框 | **三进程信任拓扑**：daemon 是唯一特权方；第三方 CLI 是独立进程且**直接写文件系统**；质量闸门是**事后**介入 | A + E |
| **OpenCode** | 客户端/服务端/模型三段 | **星形拓扑 + 「本地 TUI 也走完整 HTTP」这一条反直觉的边**——它解释了为什么多客户端是免费的 | A + C |
| **OpenWorker** | 三个并排框 | **跨语言进程边界（Rust 壳 ↔ Python 引擎）+ 统一事件流 + 带外审批收件箱**：审批**不阻塞**主循环 | A + D |
| **grok-build** | 4 层 crate 罗列 | **Goal Mode 状态机**：规划/执行/验证/战略师恢复四态 + 跨小时持久化 + 与模型输出解耦 | ② + D |
| **OpenManus** | 继承链 + 工具箱 | **继承链作为能力偏序**（每层新增什么能力）+ **stuck 检测与 max_steps 的双终止条件** | ② + E |

### 关于「Kimi K3 风格」的误用

参考 Sebastian Raschka 那类图是好想法，但要理解**它为什么有效**：

那类图的信息密度来自**「主结构 + 局部放大」的双层次**——主图给全局骨架，callout 框给**关键局部的内部细节**（张量形状、维度变换、具体算子）。

**现在的实现只学了版式，没学信息结构**：右侧 callout 里放的是「模块简介」，和主图是**同一抽象层级**的重复，而不是**下钻一层**的细节。

**正确用法**：主图画进程拓扑，callout 放**那一个承重机制的内部实现**——例如 Open Design 主图画三进程，callout 放 `RuntimeAgentDef` 的**真实字段清单**，让读者看到「适配器就是这 20 个字段的数据结构，没有一行行为代码」。

---

## 第七部分 · Mermaid 工程技巧

1. **`flowchart LR` 优先于 `TB`**。竖排天然诱导「分层」误读；横排诱导「流动」正读。只有真的表达层级时才用 TB。
2. **善用 `stateDiagram-v2` 和 `sequenceDiagram`**。循环、状态机、时序**不要**用 flowchart 硬画。现在六张图 100% 是 flowchart，这本身就是体裁选择失误。
3. **`classDef` 做语义着色**，不要内联 style：
   ```
   classDef ctrl fill:#eef4ff,stroke:#4a6fa5
   classDef data fill:#f4fff0,stroke:#4a8f3c
   classDef ext  fill:#fff4ec,stroke:#c96442,stroke-dasharray:4 3
   class LOOP,PERM ctrl
   class API,SDK ext
   ```
4. **节点文字 ≤ 3 行**。超过就说明该拆节点或该下钻成子图。
5. **文件路径写在节点第二行**，用 `<br/>` 分隔，且**必须是可点开的真实路径**。
6. **每张图配 `figcaption`**，一句话写清承重墙。图和说明是**一个整体**，不能只交图。
7. **子图嵌套不超过 2 层**。第 3 层说明该拆成两张图。
8. **单图节点数 ≤ 15**。超过就是塞了两个问题进一张图。

---

## 第八部分 · 反模式清单（逐条对照现有产物）

| # | 反模式 | 现有产物中的例子 | 修法 |
|---|---|---|---|
| 1 | **目录树伪装成架构** | 六张图全部 | 先答第二部分三问 |
| 2 | **虚假分层** | Claude Code「第 1–5 层」 | 权限是横切，不是某一层 |
| 3 | **双向箭头掩盖语义** | `LOOP <--> PERM`、`WEB <--> DAEMON` | 拆成带标签的单向边 |
| 4 | **只有 happy path** | 六张图全部 | 补重试/降级/熔断边 |
| 5 | **无进程边界** | 六张图全部 | 补图型 A |
| 6 | **无时间维度** | 六张图全部 | 循环必须用 sequenceDiagram |
| 7 | **静态工具清单** | 「40+ 工具」「~25 Rust 工具」 | 改成每轮求值节点 |
| 8 | **模型边界不突出** | 六张图全部 | 模型是唯一不可靠边，视觉上必须最重 |
| 9 | **数量当洞察** | 「26 条适配器」「25+ 连接器」 | 数字要服务论点：26 条**因为是数据所以能到 26 条** |
| 10 | **颜色无语义** | 全部 | `classDef` + 图例 |
| 11 | **callout 与主图同层级** | Kimi 风格图 | callout 必须下钻一层 |
| 12 | **对比表混维度** | 「架构特色」列混语言/模式/特性 | 一列一个维度 |

---

## 第九部分 · 事实核对协议（本次发现的错误促成）

本次审阅在六张图里查出 **4 处硬错误 + 若干精度问题**，全部属于**可以避免**的类型。以下是必须执行的流程。

### 9.1 本次发现的错误

| # | 位置 | 错误 | 事实 | 依据 |
|---|---|---|---|---|
| 1 | 标题 §4 | **「OpenWorker (OpenClaw)」** 把 OpenClaw 当作别名 | OpenClaw 是**被致谢的外部项目**，不是别名。原文：`Design (from OpenClaw): secrets **never enter the model's context, prompts, or traces**` | `参考项目/openworker/coworker/secrets.py:3` |
| 2 | §2 | Open Design daemon 写作 **「Fastify + SQLite」** | 是 **Express + better-sqlite3**。仓库自己的 `apps/daemon/AGENTS.md:96` 明写：*"Do not move daemon-only Node, SQLite, **Express**, filesystem, or process types into contracts."* | `项目分析/open-design-源码分析.md:126,1787` |
| 3 | §3 | **`acp/` 画成与 `packages/tui` 并列的顶层客户端包** | ACP 在 **`packages/opencode/src/acp/`**，即**服务端包内部**。图把「服务端的一个子目录」错画成「服务端的对等客户端」，**倒置了真实边界** | `参考项目/opencode/packages/opencode/src/acp` |
| 4 | §4 | `scheduler.py` 路径 | 实为 `coworker/automation/scheduler.py` | `参考项目/openworker/` |

**精度问题**（不算错，但达不到「专业」标准）：

- **Claude Code 主循环只标 `src/query.ts : queryLoop()`**，遗漏 `src/QueryEngine.ts`（**1 295 行**）与 `src/query/` 子目录（`config.ts` / `deps.ts` / `stopHooks.ts` / `tokenBudget.ts`）。**心脏是分布在多处的**，只指一个文件会误导读源码的人。
- **「40+ 工具」**：实际 `claude-code/src/tools/` 有 **43 个**条目。既然能数就写准。
- **OpenManus Agent 清单**漏了 `DataAnalysis`（`data_analysis.py`）和 `SandboxManus`（`sandbox_agent.py`）。
- **「26 条 CLI 运行时适配器层」与「25 个 CLI」在同一张图里并存且无解释**。正确表述：**26 条运行时定义对应 25 个不同的可执行文件**——`byok-opencode` 复用 OpenCode 的二进制，所以定义比二进制多一个。**这个「多出来的一条」本身就是一个值得画出来的设计细节。**

### 9.2 强制流程

**规则一 · 每个专有名词必须有出处。**
写进图里的每个名字（项目别名、框架名、文件路径、类名），必须能贴出 `文件:行号` 或命令输出。做不到就不写。

**规则二 · 命名三元组必须查清。**
仓库名 / 包名 / 命令名经常不同。OpenWorker 就是三个都不一样（repo `openworker` / Python 包 `coworker` / CLI `openworker`）。**在图上写哪个，取决于这张图给谁看**——读源码的人要包名，选型的人要产品名。

**规则三 · 路径必须在检出里 `ls` 过。**
```bash
for f in src/query.ts src/tools/ ...; do
  [ -e "$f" ] && echo "✅ $f" || echo "❌ $f"
done
```

**规则四 · 数字必须现算。**
`ls | wc -l`、`wc -l`、`grep -c`。禁止「约」「40+」这类从记忆里来的估计——除非确实是上游文档的说法，且注明来源。

**规则五 · 层级关系必须验证。**
把 X 画成 Y 的子级之前，确认 `X` 的真实路径在 `Y` 之下。第 3 条错误就是没做这一步。

**规则六 · 与既有解析文档交叉验证。**
`项目分析/` 下十二份文档已逐行回验过。**冲突时以解析文档为准，或回源码三方对质**——不要默默采信自己的印象。

---

## 第十部分 · 交付前自检（逐条打勾，不过不交）

### 内容
- [ ] 这张图能用**一句话**说出它回答什么问题
- [ ] 有一条明确的**承重墙论点**，其余元素都在支撑它
- [ ] 至少承载第五部分七类信息中的**三类**
- [ ] 遮住所有文字后，这张图和其它项目的图**形状不同**
- [ ] 图上至少有一处会让工程师**追问**的信息
- [ ] 有 **failure path**，不只是 happy path

### 结构
- [ ] 体裁选对（循环→sequence／状态→state／拓扑→flowchart LR）
- [ ] 进程 / 地址空间边界显式画出
- [ ] 跨进程边标注了**传输方式 + 同步性**
- [ ] 模型边界视觉上最醒目
- [ ] 无裸露的双向箭头（或已用图例定义）
- [ ] 节点 ≤ 15，子图嵌套 ≤ 2 层

### 表达
- [ ] 有**图例**，颜色/线型有语义
- [ ] 每个节点第二行是**可点开的真实路径**
- [ ] 有 **figcaption**，写清承重墙
- [ ] callout（若有）是**下钻一层**，不是同层复述

### 事实
- [ ] 每个专有名词有 `文件:行号` 出处
- [ ] 每条路径 `ls` 验证过存在
- [ ] 每个数字现算过
- [ ] 每处层级关系验证过
- [ ] 与 `项目分析/` 交叉核对过，冲突已解决

---

## 附录 · 一个完整的「好图」范例

**问题**：Open Design 自己不写主循环，凭什么保证 25 个第三方 CLI 的产出质量一致？
**承重墙**：**它把控制权让渡给第三方，只保留入口的提示词和出口的质量闸门——这是一个「两端收口、中间放手」的架构。**

```mermaid
flowchart LR
    IN["<b>① 入口收口</b><br/>prompts/system.ts · 2 075 行<br/>20 层上下文 · 按变化频率分带"]
    ASSETS["skills 164 · design-systems 153<br/>design-templates 115 · craft 11"]
    DEF["<b>RuntimeAgentDef ×26</b><br/><i>只描述「怎么说话」<br/>不描述「说什么」「怎么调度」</i>"]
    CLI["② 第三方 CLI · 独立进程 ×25<br/>claude / codex / cursor-agent<br/>opencode / devin / qwen …"]
    FS[("项目 cwd 文件系统<br/>HTML · PDF · PPTX · MP4")]
    OUT["<b>③ 出口收口</b><br/>lint-artifact 程序化反 AI 味<br/>Design Jury 五陪审评审"]

    ASSETS --> IN
    IN -->|"拼进 spawn 参数"| DEF
    DEF -->|"spawn · stdio"| CLI
    CLI -->|"<b>直接写盘</b><br/>产物不回传 daemon"| FS
    FS -->|"事后读取"| OUT
    OUT -.->|"不合格则重跑"| IN

    classDef own   fill:#eef4ff,stroke:#4a6fa5,stroke-width:2.5px
    classDef third fill:#fff4ec,stroke:#c96442,stroke-width:2px,stroke-dasharray:5 3
    classDef store fill:#f4fff0,stroke:#4a8f3c
    class IN,ASSETS,OUT own
    class DEF,CLI third
    class FS store
```

**图例**：<span style="color:#4a6fa5">■</span> 实线蓝框 = Open Design 自有代码 · <span style="color:#c96442">■</span> 虚线橙框 = 第三方进程，行为不可控 · <span style="color:#4a8f3c">■</span> 绿框 = 持久化 · 点线 = 反馈/降级边。

> ⚠️ **这里有一个必须记住的排版教训**：初稿曾把「入口」和「出口」用一个 `subgraph OWN` 圈在一起来表达「两端收口」。
> **结果 Mermaid 的 LR 布局把这个组整体推到了右边，反而破坏了从左到右的流向语义**——读者先看到第三方，再看到入口，逻辑倒转。
>
> **规则：当「分组」和「流向」冲突时，让位置表达流向，让颜色表达归属。**
> 上图取消了 `subgraph`，改用 `classDef` 上色：蓝色节点出现在**两端**、橙色在**中间**，「两端收口、中间放手」由**颜色的空间分布**自然浮现，而流向保持左→右不被破坏。
> 这比硬画一个框更准，也更好看。

**figcaption**：*两端收口、中间放手。Open Design 不控制第三方 CLI 的循环，因此质量保证只能落在**入口的提示词**和**出口的程序化检查**上——注意产物由 CLI **直接写盘**而不回传 daemon，所以 lint 与评审在架构上必然是**事后**的。这条约束不是实现偷懒，是让渡控制权的**必然代价**。*

**这张图为什么合格**：
1. 回答了一个具体问题，不是「展示架构」
2. 有承重墙，且图的**拓扑本身**（两端窄、中间宽、虚线包围）就在表达它
3. 暴露了一个可质询的事实（产物不回传 → 闸门只能事后）
4. 有降级边（不合格重跑）
5. 颜色和线型有语义并配了图例
6. 数字服务论点（26 条定义 vs 25 个二进制的差异被显式标注）
7. 遮住文字后，它和其它五个项目的图**形状完全不同**

---

*本规范基于本仓库 `项目分析/` 下十二份逐文件解析文档与 `参考项目/` 下的源码树写成。第九部分列出的全部错误均已用 `grep -n` / `ls` / 源码原文复核。*
