# Ddev Diagram

> 用户需要绘制架构图、流程图、数据流图、对比图时使用。AI 直接按规范手写 ASCII 图到 .md，不经过 PlantUML。

- Skill: `docevilock/ddev-diagram` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add docevilock/ddev-diagram`
- Raw SKILL.md: https://api.skillmd.com/api/skills/docevilock/ddev-diagram/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: docevilock (https://skillmd.com/u/docevilock)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/docevilock/ddev-diagram

---


# 画图方法

## 作用

所有图文档共用的画图方法。业务 skill 只负责说"画什么"，这里负责说"怎么画"。

如果上游来自 `ddev-spec`，默认就是把已写出的实现架构文档细化成图，不重新讨论架构本身。

如果上游来自 `ddev-plan`，默认就是把已确认的计划画成支持执行的图，不反过来改计划目标。

## 基本规则

- **唯一产物：`.md` 文件**，ASCII 图直接写在 fenced code block 里
- **不再经过 PlantUML**：AI 按绘制规范直接手写 ASCII，不出 `.puml` 中间文件
- 文件名格式：`YY-MM-DD_name.md`
- 所有图默认 ASCII；SVG 只在确实需要时才作为补充
- 图文档按固定布局组织（标题 → 用途说明 → ASCII 图 → 补充说明）

## 默认动作

用户表达以下任一意图时，直接在 `.md` 中操作：

- 新建图
- 修改图
- 调整结构、连线、节点、布局、文案

执行顺序：

1. 确定图文档位置和文件名
2. 按绘制规范直接画 ASCII 图到 `.md`
3. 补齐标题、用途说明、补充说明

## 更新策略

更新 `.md` 时先判断类型：

- **纯图文档**（只有标题、ASCII 图、极少量备注）：可整份刷新
- **带正文的图文档**（含分析说明、表格、补充备注）：只替换对应图块，不整文件覆盖

默认目标：图跟着改动变，正文保持稳定。

## 图型选择

- 架构 / 边界图：看模块边界、职责分工和主要连接关系
- 接入点图：看现有代码从哪里接入
- 流程图：看主路径、关键分支和异常出口
- 前后对照图：看修改前后行为变化
- 依赖拓扑图：看模块依赖和布局关系

在架构阶段，优先把"架构规划 + 接入点 + 主流程 + 前后对比"这四类图补齐。

---

# ASCII 图绘制规范

## 制表符：用 Unicode，不用 ASCII 拼接

```
❌  ASCII 拼接:
+----+----+
| A  | B  |
+----+----+

✅  Unicode 制表符:
┌────┬────┐
│ A  │ B  │
└────┴────┘
```

| 字符 | 作用 | 字符 | 作用 |
|------|------|------|------|
| `┌ ┐ └ ┘` | 四角 | `├ ┤ ┬ ┴ ┼` | T 型 / 十字 |
| `│` | 竖线 | `─` | 横线 |
| `► ▼ ▲ ◄` | 实心箭头 | `▷ ▽ △ ◁` | 空心箭头 |

## 先定列宽，再画框

同一列所有框宽度必须一致。先扫内容取最长标签，加边距后统一使用。

```
列宽 = max(框内最长行宽度) + 2   // 左右各留白一格
```

## 先定总宽，再排多列（多列框 + 连线标注图）

多列并排或带连线标注的图，先算后写，不要边写边数空格：

1. **先定总宽 W** — 一次算清全图所有行的最大需求宽度（各列占位宽 + 列间距 + 两侧留白），整图统一用这一个 W
2. **所有行右侧补齐到 W** — 边框行、横向连线行、标签行都补行尾空格到 W，使「所有含 `│` 的行等宽」天然成立；不靠肉眼对齐
3. **框和列用固定列偏移** — 先定每列起始列号（如 `col1=0`、`col2=24`、`col3=48`），框、竖线、转弯、箭头都从这些列号出发
4. **连线标签受剩余宽度约束** — 写标签前算 `剩余宽度 = W - 标签起始列 - 右侧留白`；放不下时优先拆成两行或缩短标签，不向右撑超 W
5. **构造复杂时用一次性 Python 生成器** — 固定 W，逐行 `ljust(W)`，按统一列号定位连线；生成结果只写入 `.md`，不留中间产物

按以上规则构造后，再跑「绘制完成门禁」的宽度校验。

## 正交连线，禁止斜线

只用横线 `─` + 竖线 `│`，转弯用 `└ ┌ ┘ ┐`。

```
✅  正交:
A ──> B
      │
      v
      C

❌  斜线:
A ──> B
     ╲
      C
```

## 箭头约定

```
──>  右箭头        <──  左箭头
 v   下箭头         ^   上箭头
```

跨行箭头先竖线延伸到目标行再转弯：

```
A ─────────┐
           v
B <────────┘
```

## 短标签原则

框内文字不超过 2 行。详细说明用框外标注或脚注补充。

## 框图标签语言：英文优先

**框图 fenced code block 内的所有标签使用英文。** 不在框内写中文。

原因：CJK 字符在等宽字体中的实际像素宽度 ≠ 2× ASCII 宽度（由字体渲染引擎决定，无法通过列宽计算弥补）。在框图中混用中英文会导致视觉错位（"犬牙交错"），且此问题无法通过列宽算法解决。

```
✅  英文标签：
    ┌────────────────┐
    │  flash_test/   │
    │  (new module)  │
    └────────────────┘

❌  中文标签（对齐不可靠）：
    ┌────────────────┐
    │  闪存测试/     │
    │  （新增模块）  │
    └────────────────┘
```

规则：

- 框内标签：英文短词组，用 `_` 或 camelCase 保留代码标识符风格
- 连线/状态/条件文字：英文缩写或短句（如 `OK`、`FAIL`、`count=0`）
- 逻辑复杂、英文表达不清时，才在图外补中文说明（表格或段落）；简单图无需图例

### 图外中文说明：解释图意，不是翻译术语

图外中文说明是对图中逻辑、流程或关系的**中文解读**，帮助读者理解"发生了什么"；它不是图中英文术语的中英对照表。

```
✅  正确 — 解释图意：
    > 上电后 bootloader 先校验 flash_test 模块的签名，验签通过才跳转。
    > 如果签名无效，回退到 recovery 分区。

❌  错误 — 翻译术语表：
    | 图中英文        | 中文     |
    |----------------|---------|
    | bootloader     | 引导程序 |
    | flash_test     | 闪存测试 |
    | recovery       | 恢复分区 |
```

规则：

- 图外说明写"流程为什么这样走、关键判断是什么、边界为什么这样画"，不写"这个词对应的中文叫什么"
- 英文术语已在图中出现过的，不需要在说明里重复定义
- 说明短则一两句话，长则分成小段；不写成翻译对照表
- 代码块内已存在的 C/Python 等代码不受此限制（注释中的中文正常保留）

## 一图一事

一张图只回答一个问题。开始拥挤就拆多张。

```
一张图 = 一个视角（边界 / 状态 / 数据布局 / 流程）
```

## 绘制顺序

1. **先摆框** — 多列或带连线标注的图先定总宽 W、列偏移与列宽（见「先定总宽，再排多列」），再确定所有框的位置与尺寸
2. **后连线** — 框定稿后再补 `│` `─` `v` `──>` 等连线
3. **最后补标注** — 框外注释、触发条件、条件文字最后放

---

## 文档布局

图文档按此顺序：

1. 标题
2. 一句话说明用途
3. ASCII 图（放在 fenced code block 里）
4. 需要时补一小段说明或表格

## 绘制完成门禁（强制）

**每次写完或更新含图 `.md` 后，必须执行以下核对。不通过不得视为完成。**

### 步骤

1. **回读文件**：用 Read 工具回读刚写入/更新的 `.md` 文件全文
2. **提取框图**：定位所有 fenced code block（\`\`\` 包裹的 ASCII 图）
3. **宽度自动校验**：对每个框图跑 Python 宽度检查脚本（见下方「宽度自动校验」），有报错先修再继续
4. **逐框检查**：对每个图执行以下检查项

### 宽度自动校验

AI 画完框图后，必须对每个 fenced code block 内的 ASCII 图执行以下 Python 脚本进行宽度校验：

```bash
python3 -c "
import sys
block = sys.stdin.read()
for i, line in enumerate(block.split('\n'), 1):
    # 跳过空行
    if not line.strip():
        continue
    # ┌─┐ └─┘ ─ 等制表符都是单字符宽，len() 按 Unicode 字符计数是正确的
    w = len(line)
    # 检测右边框位置差异：所有含 │ 的行宽度应一致
    if '│' in line:
        print(f'L{i:3d}  w={w:3d}  {line}')
    elif line.lstrip().startswith(('┌','└','├','┐','┘','┤')):
        print(f'L{i:3d}  w={w:3d}  {line}')
"
```

用法：将框图中整个 fenced code block 的内容复制后 pipe 到该脚本。输出会列出每行的 Unicode 字符宽度 w 和行内容。检查要点：

- 所有含 `│` 的行 `w` 值必须相同 → 否则右边框不齐
- 同框 `┌─...─┐` 与 `└─...─┘` 的 `w` 值必须相同 → 否则上下框线不等宽
- 上框 `w` 与内容行 `w` 的差值 = 2（左右边框各占 1 列），即 `w(border) == w(content)` 且 `w(border) - w(content_with_pipe) == 0`（因为 `│` 就是边框本身，不额外占用边距）

### 检查项

| # | 检查项 | 方法 | 不通过时 |
|---|--------|------|----------|
| A | 右边框对齐 | 先跑「宽度自动校验」：所有含 `│` 的行 `w` 值必须相等（即同一列）；不满足时补/削空格 | 补空格或削空格，重写该行 |
| B | 上下框线等宽 | 先跑「宽度自动校验」：`┌─...─┐` 与 `└─...─┘` 的 `w` 值必须相同 | 调整横线数量 |
| C | T型/十字接合完整 | `├ ┤ ┬ ┴ ┼` 上下左右邻接位是否有对应连线 | 补连或改T型字符 |
| D | 无斜线 | grep `╲ ╱ ╳ ╱` — 必须零命中 | 改用正交转折 |
| E | 无断线 | 竖线 `│` 贯穿多行时，每行同一列都有 `│` 或合法转角 | 补竖线 |

### 示例：右边框对齐检查

```
写入的图（错误 — 第 3 行右边框偏右）:
┌──────────────────────┐
│  AGENTS.md (global)  │
│  session end hook      │  ← 右边框比第 1 行右移了 2 列
└──────────────────────┘

自检发现 → 修正为:
┌──────────────────────┐
│  AGENTS.md (global)  │
│  session end hook    │
└──────────────────────┘
```

### 核对通过条件

- 所有检查项 A-E 全部通过
- 如果任一检查项未通过，修正后重新回读，直到全部通过
- 修正超过 3 次仍未通过 → 重画该图（从摆框步骤开始）

---

## 生成后自检

- `.md` 中的 ASCII 图是否按绘制规范执行
- 节点名、连线关系、说明文字是否一致
- 是否列宽对齐、正交连线、无斜线
- 是否一图一事，未拥挤

## 参考文件

- [图型分类](references/diagram-taxonomy.md)
- [模板](references/diagram-templates.md)
- [示例](references/example-diagram-doc.md)

