# 钉钉文档创建流程

本文只处理一件事：用 `dws doc create` 创建一篇钉钉文档，并确认内容真的写进去。资料采集、汇报生成、转发通知、权限分发等都不是本文范围，应由对应 recipe 或产品参考负责。

> 改写已有文档见 [doc-update-workflow.md](./doc-update-workflow.md)。排版规范见 [doc-style-guideline.md](./doc-style-guideline.md)。

## 前置必读

> **同时读取 [doc-style-guideline.md](./doc-style-guideline.md)：**
> - **§2.0 类型判断决策表** → 锁定文档类型（决策型 / 执行型 / 说明型 / 知识沉淀型）和骨架
> - **§1 硬规则** → 全程生效（`--name` 已是 H1、不编造 URL、Markdown 草稿不写 callout 等）

### 关键词速查（用户意图 → 起稿路径）

| 用户关键词 | 文档类型 | 起稿路径 |
|-----------|---------|---------|
| 汇报 / 周报 / 月报 / 复盘 / 方案选型 / 决策 / 对比 | §2.1 决策型 | **→ JSONML 起稿** |
| 调研 / 技术方案 / 复盘报告（含对比/数据） | §2.4 知识沉淀型 | **→ JSONML 起稿** |
| SOP / Runbook / 接入指南 / 升级 / 操作手册 | §2.2 执行型 | → Markdown 起稿 |
| 接口文档 / 能力清单 / 参数说明 / 错误码 | §2.3 说明型 | → Markdown 起稿 |
| 用户原文含：颜色/高亮/美观/醒目/重点突出/像PPT | 任意类型 | **→ JSONML 起稿** |

## 适用边界

进入本文前，必须已经确认用户要创建的是钉钉文档 (`adoc`)。如果用户要的是钉钉表格、AI表格、文件上传、知识库空间管理或消息发送，不要套用本文。

本文覆盖：

- 文档标题和创建位置确认
- 正文草稿准备
- `doc create` 写入
- 写入后回读验收
- 内容缺失时的补救写入

本文不覆盖：

- 从群聊、日志、听记、表格等来源采集资料
- 生成日报、周报、月报等业务报告口径
- 文档权限分享、消息通知或待办创建
- 非钉钉文档的新建流程

## 创建前检查

创建前先锁定四个输入：

| 项目 | 要求 |
|------|------|
| 标题 | 用 `--name` 传入；正文不要再重复同名一级标题 |
| 位置 | 默认创建到我的文档；指定目录时只接受文档文件夹 `nodeId` 或 alidocs 文件夹 URL |
| 正文 | 多行、表格、代码块、特殊字符或长度 >= 2KB 时必须写入 UTF-8 临时 `.md` 文件 |
| 格式 | 按 §JSONML 起稿判定 决定起稿路径：命中 JSONML 起稿条件时**直接用 JSONML 构造**（跳过 markdown）；未命中时用 Markdown 起稿，创建后按 [doc-update-workflow.md](./doc-update-workflow.md) 精修 |

禁止把纯数字 `dentryId`、drive `parent-id` 或 spaceId 填进 `--folder`。

## JSONML 起稿判定

在正文准备之前，先判断是否直接用 JSONML 起稿。**命中以下任一条件即走 JSONML 起稿路径**（跳过 markdown 草稿阶段）：

### 文档类型触发

| 类型 | 触发条件 |
|------|---------|
| 决策型（§2.1） | **默认触发** — 汇报/方案/对比需要摘要 callout、彩色表头、决策时限标注 |
| 知识沉淀型（§2.4） | 含对比分析、多维度数据可视化、需要关键节点彩色 callout |

### 意图关键词触发

用户原文或需求描述中出现以下任一关键词：

- 颜色 / 配色 / 上色 / 高亮 / 醒目
- 字号 / 字体 / 加大 / 缩小
- 视觉效果 / 排版精美 / 好看 / 美观
- callout / 分栏 / 对比色 / 彩色表头
- "像 PPT 那样" / "有设计感" / "重点突出"

---

## 设计规划（JSONML 起稿前必做）

判定走 JSONML 路径后，**禁止立即动手写 JSONML**。先完成以下两阶段规划，各自产出一个持久文件作为后续阶段的锚点。

### 设计原则（全程生效）

1. **结构即信息** — 标题层级、表格 vs 分栏 vs 列表、callout 位置都应编码内容逻辑。问自己：“去掉这个结构元素，读者会丢失信息或体验变差吗？”— 丢失信息则必保留；不丢失信息但能提升可读性或美观度（如分割线分隔章节、分栏对比排版）也应保留；既不携带信息也不提升体验的装饰元素才删除。
2. **视觉层级引导阅读** — 每一屏必须让读者瞥一眼就能回答：“这块最重要的是什么？”字号/粗体/颜色形成明确梯度：标题 > 重点数据 > 正文 > 辅助信息。
3. **克制产生质感** — 遵循 60-30-10 配色比例：60% 中性底色（白/浅灰）、30% 辅助色、10% 强调色。多色系共存时需保持**同等饱和度**并各有语义角色（如淡蓝=信息、淡黄=提示、淡红=风险），同一色系内深浅变化自由。callout 不超过 2 个。
4. **设计先于执行** — 从规划阶段起每个结构块的样式就已确定，执行时（无论直接 JSONML 还是脚手架精修）只是落地已有设计，不是边写边想。
5. **同类同色、一色多阶** — 同类信息必须使用相同色系；单一色系按元素角色展开为深/中/浅/极浅四级（标题文字用深色、强调用中色、高亮/表头用浅色、背景用极浅色），不要全篇只用一个 hex 值。

### Phase 1：RFC — 需求理解与设计方向

**目标**：明确“做什么”和“为什么这样做”，形成方向性锚点。

#### 1.1 需求提取（全量列出用户显式要求）

通读用户 prompt，抽取两类要求并编为清单：

**内容要求**：
- 标题、字数、章节划分
- 数据来源、受众
- 语气/风格（如“大气”“专业”“轻松”）

**样式要求**（每一条都必须在最终输出中体现，不得遗漏）：
- 字体：映射为 font-family 名称（参照 cookbook 字体映射表），区分“全文字体”和“特定元素字体”
- 字号、行距、对齐、颜色
- 强调手段（加粗、高亮、配色…）
- 约束（如“每部分不省略”）

> 用户没有明确指定的维度（如未指定行距、未指定表格样式）由 Phase 2 补充设计决策。

#### 1.2 内容-表现适配（每个章节的内容适合用什么元素）

对每个章节回答：“这个内容的核心是什么类型的信息？”→ 选择最佳元素：

**块级结构元素**：

| 信息类型 | 首选元素 | 不适合 |
|----------|---------|--------|
| 多个同类实体对比（≥ 4 项或 ≥ 3 维度） | 彩色表头表格 | 纯文本段落 |
| 少量实体对比（2-3 项× 少量维度） | 分栏（每栏一个实体，可设边框/背景色） | 大宽表格 |
| 时间序列/流程（行程/步骤） | 有序列表 + 粗体时间标签 | 无序列表 |
| 单个结论/推荐/重要提示 | callout（“花大胆”的地方） | 普通段落 |
| 描述性文字（背景/说明） | 正文段落 + 关键词粗体 | 表格 |
| 分类列举（特色/亮点） | 无序列表 | 表格（数据不够多列时） |
| 数值强调（评分/价格/统计） | 加粗 + 着色 | 跳过不强调 |
| 引用原文（用户评价/网友点评/官方说明） | 引用块 | 普通段落 |
| 任务/待办清单 | checklist（`- [ ]`） | 普通列表 |
| 章节分隔/主题转换 | 分割线（`hr`） | 空行 |
| 板块内子区域分隔（同一单元格/容器内多个逻辑段） | hr 内部分隔（在 tc 或 container 内部使用） | 空行或留白 |
| 结构化元信息（人/时间/地点/属性清单） | 键值对表格（窄标签列 ~15-20% + 宽内容列） | 多行段落 |
| 分类标签/状态标记 | 标签元素（tag） | 纯文本标记 |

**行内强调元素**：
- **emoji** — 用于 callout 前缀、状态标记、H2/H3 标题前；不在普通段落和列表项中滥用
- **加粗/高亮/着色** — 强调关键数据和结论
- **highlight 色带** — 在标题 span 上设 `"highlight": "#浅色"` 形成轻量色条标记，比 callout 更轻，适合区分多个并列板块的主题色
- **灰色辅助文字** — 用浅灰色（如 `#979A9B`）标记示例/说明/占位文字，与正文形成明确的主次层级
- **图片** — 实景照片、截图、示意图能显著提升理解时使用

**分栏选型补充**：分栏栏数无上限，但推荐 2-4 栏（≥5 栏会比较拥挤），可设置边框和背景色。**建议设置 `fill` 背景色**（纯白底分栏视觉上与普通段落无异，读者不易感知分栏结构）。适合场景：
- 2-3 个同类实体并排展示（每栏一个实体，含标题 + 描述 + 关键数据）
- 轻量对比：“优点 vs 缺点”、“方案 A vs B”、“Day 1 vs Day 2”
- 网格布局：多次插入同栏数分栏可形成卡片网格（如 2×3、3×2），适合 4-9 个结构相同、内容等长的卡片式实体。**约束**：每个格子内容必须结构一致且长度相近，否则高低不齐会很丑；实体数不能整除栏数时不使用

例如：“5 个实体多维度对比” → **彩色表头表格**；“两组信息并排” → **分栏**；“6 个结构相同的卡片” → **2×3 分栏网格**。

#### 1.3 起稿策略选择

根据文档复杂度和上方适配结果，确定起稿路径：直接 JSONML / Markdown 脚手架 + JSONML 精修 / 直接 JSONML 分段构造。具体条件和流程见下方「起稿」节的策略表。

#### 落盘

将以上内容写入 `<name>-rfc.md`，包括：
- 需求摘要（内容要求 + 样式要求）
- 每章展现策略 + 选择理由
- 起稿策略

---

### Phase 2：Spec — 精确设计参数

**目标**：将 RFC 的方向决策转化为可直接执行的参数。

#### 2.1 视觉体系设计（确定全局设计变量）

在以下四个维度做出明确选择，每个选择都必须能解释为什么适合这篇文档：

**色彩**（选定主色后展开为色阶）：

用户指定颜色时（如"蓝色""绿色"），不要全篇只用一个 hex 值。将其展开为 4 级色阶，按元素角色分配：

| 角色 | 用途 | 色阶要求 |
|------|------|----------|
| 深色 | 标题文字、重点数据 `color` | 白底上高对比可读 |
| 中色 | 正文强调、链接 `color` | 辨识度高但不抢标题 |
| 浅色 | highlight 色带、表头 fill | 底色柔和，上方深色文字可读 |
| 极浅 | container bgcolor、大面积背景 | 接近白色，仅提供区域感 |

**规则**：
- 深→浅的层级关系不可颠倒（不能用极浅色做标题文字、不能用深色做背景）
- 同类板块/同类标题必须使用完全相同的色阶组合，通过色彩的重复形成视觉韵律
- 不同类别可用不同色系区分（如：任务=蓝系、风险=红系、成果=绿系）
- 遵循 60-30-10 配色比例：60% 中性底色、30% 辅助色、10% 强调色；用户指定的颜色值优先

**字体梯度**（形成明确层级）：
- 主标题（h2）：字体 / 字号 / 粗体 / 颜色
- 正文：字体 / 字号 / 行距
- 强调文字：粗体 + 颜色或高亮
- 辅助信息（注释/来源）：字号偏小 / 灰色

**表格风格**（有表格时）：
- 表头：底色 + 文字色 + 是否粗体
- 单元格：默认对齐 / 字号

**视觉重心**（克制原则落地）：
全篇选 1-2 处给予最强视觉处理（配色 callout / 彩色表头 / 分栏对比），其余元素保持朴素。不要处处强调 — 处处强调等于没有强调。

callout 可通过 `"showstk": true, "sticker": "图标名"` 配置顶部贴纸图标（如“灯泡”“火”“钉子”），增强语义标识。设置了 sticker 后，高亮块内首个段落不要再以 emoji 开头，避免紧邻的位置出现两个图标。

#### 落盘与回验

1. 将以上内容写入 `<name>-design.md`，包括：
   - 逐章元素映射（用什么标签、什么属性）
   - 色阶具体 hex 值
   - 字体梯度参数
   - 表格风格细节

2. **回验**：Read `<name>-rfc.md`，逐条确认：
   - [ ] RFC 中每条需求在 Spec 中有对应实现
   - [ ] 展现策略在 Spec 中有具体参数支撑
   - [ ] 配色遵循 60-30-10 比例、callout ≤ 2 个、多色系饱和度一致且有语义角色

回验通过后进入下一节开始构造 JSONML。

---

## JSONML 起稿（命中判定时使用）

根据 RFC 中确定的起稿策略执行：

| 策略 | 条件 | 执行流程 |
|------|------|----------|
| **直接 JSONML** | 短文档（≤ 15 块级节点）且结构简单 | 在 `/tmp/<name>.json` 手写完整 JSONML 树 → `doc create --content-format jsonml` |
| **Markdown 脚手架 + JSONML 精修** | 长文档且结构较线性 | ① Markdown 建立内容骨架 → `doc create --content-format markdown` ② `doc read --node <id> --content-format jsonml --output /tmp/<name>.json` 拉回 JSONML（已含 uuid） ③ **Read `<name>-rfc.md` + `<name>-design.md`** 回顾规划 ④ 逐章对照执行结构变换 + 叠加样式 |
| **直接 JSONML 分段构造** | 长文档且含大量富结构 | 按章节分段构造 JSONML，每段写完校验通过后再写下一段，最后拼接 |

> **脚手架策略警示**：Markdown 无法表达分栏/callout/色彩表头，拉回的 JSONML 只有纯文本骨架。精修阶段不是“在现有结构上加色”，而是“参照 RFC/Spec 重组结构”。

> **MUST READ**：动手写 JSONML 前，必须先用 Read 工具读取 [doc-jsonml-cookbook.md](../format/doc-jsonml-cookbook.md) — 其中 §决策型文档骨架范例 有可直接复制修改的完整模板。
> 节点类型和属性的权威定义见 [doc-jsonml-schema.md](../format/doc-jsonml-schema.md)。

### ⚠️ JSONML 降级约束

**禁止因一次校验失败就放弃 JSONML 降级为 Markdown。** 当用户需求已触发 JSONML 起稿判定时，JSONML 是实现其样式要求的首选路径。失败时的处理策略：

1. **校验报错** → 读取错误信息，定位具体节点，修复后重试
2. **JSON 语法错误** → 检查括号匹配、逗号、引号，修复后重试
3. **反复失败（≥3 次）** → 尝试简化结构（减少嵌套、拆分复杂节点）再试
4. **仍然失败** → 退化为「Markdown 脚手架 + JSONML 精修」路径（流程同上方策略表），并告知用户当前状况

> “由于 JSONML 结构复杂且容易出错，改用 Markdown” — 这不是合法降级理由。必须先充分重试，且降级后仍需通过精修补回样式。

### ⚠️ JSONML 结构严格约束（生成时必须遵守）

每个节点是一个 JSON 数组：`[tagName, attributes?, ...children]`

- **第一个元素**是字符串，表示标签名（如 `"p"`, `"h1"`, `"span"`, `"container"`）
- **第二个元素**（可选）是一个 JSON 对象，表示属性（如 `{"uuid": "abc"}`）。如果无属性，可以直接进入子节点
- **随后的元素**是子节点，可以是纯字符串（仅限 leaf span 内），也可以是另一个 JSONML 数组
- **所有 `[` 必须有对应 `]`，所有 `{` 必须有对应 `}`，数组元素之间用 `,` 分隔，最后一个元素后不加 `,`**

常见 LLM 生成错误（务必避免）：

| 错误类型 | 示例 | 后果 |
|---------|------|------|
| 缺少闭合 `]` | `["p", {}, ["span", ...]` | JSON 解析失败 |
| 多余逗号 | `["p", {},]` | JSON 解析失败 |
| 缺少逗号 | `["p", {} ["span"]]` | JSON 解析失败 |
| 引号不匹配 | `["p", {"uuid": "abc}]` | JSON 解析失败 |
| 有序列表每项都设 `start:1` | `{"start":1}` 在每项重复 | 所有项编号重置为 1（显示为 a/a/a） |
| 列表 `level` 从 1 开始 | `"level": 1` 作为顶级 | 顶级列表项多一层缩进；建议：顶级 `"level": 0`，子级 `"level": 1` |
| 用 `fontFamily` 设字体 | `"fontFamily": "Arial"` | 校验报错；正确写法：`"fonts": {"ascii": "Arial", "eastAsia": "..."}` |
| 用多个 span “换行” | 同一 `p` 内放两个 `span` | 不会产生换行；每个换行必须是独立的 `p` 节点 |

### 基础结构

文件内容是一个裸 JSONML 数组，根节点为 `"root"`：

```json
["root", {},
  ["h2", {}, ["span", {"data-type": "text"}, ["span", {"data-type": "leaf"}, "章节标题"]]],
  ["p", {}, ["span", {"data-type": "text"}, ["span", {"data-type": "leaf"}, "正文段落"]]]
]
```

- 根节点固定 `"root"`（不是 `"body"`）
- `--name` 已是 H1，JSONML 从 `h2` 开始
- 表格结构是 `table → tr → tc`（无 `th`/`td`）
- 分栏是 `table` + `"sr": true`，`tc` 建议设 `fill` 背景色
- 有序列表：仅第一项设 `"start": 1`，后续项不设 `start`（系统自动递增）
- 列表 `level` 建议从 0 开始：顶级项 `"level": 0`，子项 `"level": 1`，以此类推
- uuid 必须显式提供（CLI 不自动生成）
- **每行内容对应一个 `p` 节点** — 同一 `p` 内的多个 `span` 不会换行，只会横向拼接；需要换行时必须拆分为多个 `p`

### 视觉设计要点

构造时主动使用这些属性实现视觉效果：
- **文字着色**：leaf 上 `"color": "#hex"`、`"highlight": "#hex"`
- **highlight 色带**：标题 leaf 上 `"highlight": "#浅色"` 可形成色条效果（比 callout 更轻量的板块标记）
- **字号**：leaf 上 `"sz": 14, "szUnit": "pt"`
- **callout**：`["container", {"subType": "colorBlocks", "metadata": {"bgcolor": "#E8F5E9", "border": "left"}}, ...blocks]`
- **callout + sticker**：`["container", {"subType": "colorBlocks", "metadata": {"bgcolor": "#FEF3F3", "showstk": true, "sticker": "火"}}, ...blocks]`
- **表格单元格底色**：tc 上 `"fill": "#hex"`

### 写入

```bash
dws doc create --name "<文档名>" --content-file /tmp/<name>.json --content-format jsonml
```

### 回读验收

```bash
dws doc read --node <nodeId> --content-format jsonml --output /tmp/<name>-readback.json
```

---

## 正文准备（未命中 JSONML 判定时）

正文草稿先在本地临时 Markdown 文件中完成，推荐路径形如 `/tmp/<name>.md`。

准备规则：

- 只使用用户已提供或对话中已确认的正文素材。
- 如果正文素材不足，先补齐文档目标、受众、章节和缺口；不要在本文中临时扩展跨产品采集流程。
- **先按 [doc-style-guideline.md §2.0 类型判断决策表](./doc-style-guideline.md) 确定文档类型，再用对应类型的骨架样板（§2.1 决策型 / §2.2 执行型 / §2.3 说明型 / §2.4 知识沉淀型）**。不要套通用三段式。
- **`--name` 已是 H1，正文从 `##` 开始**；正文内不要再写 `#` 一级标题（除非确实需要正文内再造一级 H1 并说明动机）。
- 摘要、bullet、引用块、callout 等元素的使用边界以 style-guideline §3-§7 为准。
- 同类信息保持一致：风险、状态、行动项各用一种元素 + 一种视觉语义（style-guideline §1.2 / §5）。
- 临时文件必须保留真实换行，不能把换行写成字面量 `\n`。
- Markdown 草稿阶段**不要**写 callout / 分栏 / 附件——这些留到「创建后的精修」用 `doc block insert` 操作（style-guideline §1.3）。
- **图片素材闭环（硬规则）**：正文需求含图片/截图/图文并茂时，**禁止**在 Markdown 中写 `![](...)` 图片语法（包括真实存在的 alidocs URL）。正确做法：Markdown 只写文本骨架和图片占位说明（如 `📌 此处插入：xxx 产品截图`），创建文档后逐个执行 `dws doc media insert --node <nodeId> --file <本地图片路径>` 插入，最后用 `dws doc block list --node <nodeId>` 验证图片块存在。图片来源如果是钉盘文件，必须先 `dws drive download --node <图片nodeId> --output /tmp/xxx.png` 下载到本地再 insert。

## 创建写入

优先用 `--content-file` 一次创建并写入：

```bash
dws doc create --name "<文档名>" --content-file /tmp/<name>.md --content-format markdown
```

创建到指定文件夹：

```bash
dws doc create --name "<文档名>" --content-file /tmp/<name>.md --folder <DOC_FOLDER_NODE_ID> --content-format markdown
```

创建到知识库：

```bash
dws doc create --name "<文档名>" --content-file /tmp/<name>.md --workspace <WS_ID> --content-format markdown
```

短纯文本才允许直接传 `--content`：

```bash
dws doc create --name "<文档名>" --content "短内容" --content-format markdown
```

返回后立即记录：

| 字段 | 用法 |
|------|------|
| `nodeId` | 后续 `doc read`、`doc update`、`doc block`、`doc media` 的目标 |
| `docUrl` | 最终交付给用户的链接；缺失时用 `doc info` 补查 |
| `chunksWritten` | 判断是否触发自动分片；大于 1 时重点检查章节顺序 |

## 回读验收

创建命令返回成功不等于正文完整。每次创建后都必须回读：

```bash
dws doc read --node <nodeId>
```

验收要点：

- 开头摘要、关键章节、表格表头、末尾章节都存在。
- 回读文本顺序和临时 Markdown 一致。
- 没有把字面量 `\n` 渲染成一整行。
- 如果返回 `chunksWritten > 1`，检查分片边界没有破坏表格、代码块或列表。
- 最终回复必须给用户 `docUrl`；如果只拿到 `nodeId`，说明链接字段未返回，并报告已尝试 `doc info`。

## 缺失补救

DWS 写入管道会自动处理长内容分片。只有出现以下情况才手工补片：

- 返回 `CONTENT_TRUNCATED`
- 命令超时或只写入部分分片
- 回读发现后半段缺失、章节乱序或表格损坏

补救流程：

1. 用 `doc read` 确认已经写到哪个章节。
2. 从原始临时 Markdown 中截取缺失部分，写入 `/tmp/<name>-resume.md`。
3. 追加缺失内容：

```bash
dws doc update --node <nodeId> --content-file /tmp/<name>-resume.md --mode append --content-format markdown
```

4. 再次 `doc read`，确认缺失章节已补齐。

## 创建后的精修

创建流程本身优先完成整篇正文。只有需要局部补充、插入附件、加 callout / 分栏、或无损结构调整时，才进入精修——**精修路径统一走 [doc-update-workflow.md](./doc-update-workflow.md)**。

精修常见入口（**按 [doc-update-workflow.md §1.3](./doc-update-workflow.md) 优先级排序：JSONML 首选**）：

- 单 block JSONML 精修（首选）：`doc block list --node <id> --content-format jsonml --block-id <uuid>` 取子树 → `doc block update --node <id> --block-id <uuid> --content-format jsonml --element '[...]'` 写回（uuid 必须 == --block-id；写入端默认执行 schema validate，详见 [doc-update-workflow.md §4.4](./doc-update-workflow.md)）
- 整篇 JSONML 无损：`doc update --content-format jsonml --mode overwrite`（默认直接覆盖，适合一次改多处或改 root sectPr；担心并发覆盖时加 `--revision <N>` 触发并发检查）
- 插入附件 / 图片：`doc media insert`（无 JSONML 形态，直接走 element）
- element JSON 次选：`doc block insert` / `doc block update` 不带 `--content-format jsonml` 时按老接口 JSON 解析；仅在 JSONML 不支持某字段时使用
- markdown 兜底：`doc update --mode append`（末尾追加纯文本段落，无富结构需保留时）

字段结构以 [`doc.md`](../../doc.md) 为准；何时用何种精修路径见 [doc-update-workflow.md §3「改写路径速查」](./doc-update-workflow.md)。

## 交付口径

只报告已经验证过的信息：

- 文档标题
- `docUrl` 或 `nodeId`
- 已写入的正文范围
- 回读验收结果
- 如有缺失，说明缺失位置和补救状态

未回读前，不要说内容完整或任务完成。
