- scripts/check_mermaid.py(纯标准库):R0 围栏未闭合 / R1 图类型头缺 错 / R2 -. 后直接跟管道(确定性语法错误形态)/ R3 subgraph 与 sequence 块的 end 计数配对;--all / --selftest(5 形态)/ --prune; 排除面与 doc-links 同口径;零语料空基线 - 接线 .pre-commit-config.yaml(doc-links 后),docs/门禁说明/ mermaid-syntax.md 专篇(八段结构含限制与盲区),README 索引与 AGENTS.md 计数 51→52、提交期实跑 48→49 - 门禁说明 README 顺带修正两处历史矛盾:gitlint 实际就在本配置 (commit-msg 阶段),旧文「52−3−gitlint」与「gitlint 不在配置里」 均过时——锚定口径 52 = 47 pre-commit + 3 manual + 1 commit-msg, 加钩后 48 + 1 = 49;三处 grep 争执例句改抽象表述(例句里的数字 自己会过时) - docs/子系统设计/屏板链路设计说明.md:38 修复触发本钩子的坏图: -. 后直接跟管道不是合法边 operator,改为 -.->|label| 形态 实测:YAML 解析 52 / commit 阶段枚举 48 行全 Passed(含新钩子)/ check_mermaid --all 29 文件 60 块围栏 0 命中 rc=0 / check_doc_links --all rc=0(215 文档)/ 六文件无 BOM/CR/U+FFFD。
6.9 KiB
mermaid 围栏语法预检(mermaid-syntax)— 围栏里的图坏了必在提交时响
一句话:
```mermaid代码块是渲染期才报错的内容——md 链接与章节门禁全绿也拦不住一张 语法坏的图。本闸只查「错了必渲染失败」的三条确定性形态,不重造 mermaid 解析器。 脚本scripts/check_mermaid.py(纯标准库、只读,2026-10-30 接线)。
1. 它在查什么
对每个 md 提取 ```mermaid 围栏块(排除面见 §3),逐块查:
| 判定 | 形态 | 说明 |
|---|---|---|
| R0 算 | 围栏未闭合 | 有 ```mermaid 开栏找不到闭栏 |
| R1 算 | 块内首个非空非注释行不以已知图类型关键字开头(flowchart/graph/sequenceDiagram/stateDiagram/classDiagram/erDiagram/gantt/journey/pie/mindmap/timeline/quadrantChart/gitGraph/C4Context… 全表见脚本 DIAGRAM_HEADS) |
没有头 = 100% Syntax error in text;flowChat 这类拼错也算 |
| R2 算 | -. 后(允许空白)直接出现管道 |,即 `-. |
"文字"|` 形态 |
| R3 算 | flowchart/graph 的 subgraph 行数 ≠ 独占行 end 行数;sequenceDiagram 的行首块起点(alt/opt/loop/par/critical/rect/break)行数 ≠ 行首 end 行数 |
缺 end(或多 end)必渲染失败;else/and/option 是分支不消费 end,不误伤 |
| 不算 | %% 注释行、rem 行 |
与渲染一致地跳过 |
| 不算 | 标签内出现 end/管道/引号(如 E["end 事件"]、`-"x" |
-`在合法箭头内) |
2. 为什么要它
2026-10-29 落库的 docs/子系统设计/屏板链路设计说明.md §1 flowchart 里,一行带文字点线写成:
S -.|"组帧 OK 且本拍注过键 → 交凭据"| T ← 坏:`-.` 后直接跟管道,`-.|` 不是箭头 token
S -.->|"组帧 OK 且本拍注过键 → 交凭据"| T ← 修复:补 `>`,`-.->|t|` 是合法形态
mermaid 10.9.8 对前者整图渲染 Syntax error in text(headless Edge 截图实证,图旁红炸弹图标),
对后者渲染完整正确。该坏图从落库到 2026-10-30 被用户肉眼发现期间,doc-links 等文档门禁全绿
——它们查链接、章节、表格断裂,没有任何一根查围栏内图表语法;芯片手册转录目录之外,全仓
自有围栏 60 块,全靠人眼守。
2.1 接线当日实测(都可复现)
| 测项 | 结果 |
|---|---|
全仓 --all 实扫 |
29 个自有文件共 60 块围栏:新发 0、基线 0,退出码 0 ⇒ 空基线零语料 |
--selftest 五条 |
5/5:正例(三种合法点线 + 配对 subgraph)0 命中 / 本坑形态 R2 必响 / subgraph 缺 end 必响 / 假图类型头必响且 sequence alt-else-end 不误伤 / 基线吞得下且清空后同命中算新发 |
| 钩子端到端 | 暂存一个复刻本坑的坏 md → mermaid-syntax Failed、定位到 文件:38 [R2-dot-pipe] 原行;修复形态 --all-files Passed |
| 原图渲染对照 | 坏版 Syntax error in text、好版完整渲染,两 PNG 见当日会话(mermaid 10.9.8 CDN + headless Edge) |
3. 触发面
钩子 files: \.md$ + exclude 与 doc-links 同口径(.opencode/node_modules/、Lib/lvgl/、
MDK-ARM/RTE/、docs/芯片手册/)——芯片手册是外部转录,围栏形态不受控,与 doc-links 一样整体
不进语料。scripts/check_mermaid.py --all 的文件集与钩子实际喂入刻意同口径。
单独自查:& .venv\Scripts\python.exe scripts/check_mermaid.py --all
4. 报错怎么读
docs/子系统设计/xxx.md:38 [R2-dot-pipe] dotpipe:S -.|"组帧 OK 且本拍注过键 → 交凭据"| T
行号是 md 文件绝对行号(不是块内偏移),直接跳。四条判读打印在输出尾行:
R1 改图类型头;R2 改 -.->|t| 或 -. t .->;R3 补/删 end(打印里带 pair:sg=N,end=M
两个计数,谁多谁少一眼可见);R0 补闭栏。
5. 好处
- 把「肉眼盯渲染」变成机器兜底:围栏内坏图在提交时响,不用等 GitWeb/IDE 预览点亮红块。
- 空基线零语料:今天起任何新坏图都拦得住,不背历史债。
- 毫秒级、纯标准库、只读——不需要网络、不需要 node、不需要浏览器。
6. 限制与盲区(绿 ≠ 图对)
- 不是解析器:只覆盖三条「必炸」形态。大量仍会漏——引号不平衡、非法节点字符(中文引号
包标签)、sequence 消息语法错、
style/classDef目标不存在、layout 指令错等。最终权威 仍是真实渲染(mermaid CDN + headless Edge 截图回读,draw-diagramskill 载有流程)。 - R2 只钉
-.+|本坑形态:其它非法箭头变体(如==|、-.->x)不响;出现新形态时 加判据(本闸按「增量长牙」演进,与nested-comment同哲学)。 - R3 是计数配对:subgraph 多一个且 end 也恰好多一个(两处错互相抵消)测不出;
也不校验
end是否配对在正确的嵌套层级(只数总量)。 %%注释行里的坏形态不响(与渲染行为一致);围栏缩进嵌在列表项里的块同样被扫(无盲区), 但 indented code block 里的 mermaid 不会被识别(GFM 本也不渲染它,一致)。- 只扫
```mermaid;~~~mermaid波浪围栏全仓未用,未支持。
7. 豁免与基线
- 基线为空(2026-10-30 接线实扫 0 命中,先例 = 已退役的
id-check-new「零语料」形态), 脚本内BASELINE = ()。若将来被迫登记存量:必须写「文件 + 规则 + 上下文指纹 + 为什么不改」,--prune打台账、只减不增。 - 唯一排除面是 §3 的目录前缀(外部资料),自有文档零豁免。
8. 改门禁本身要注意什么
- 加判定必带正例自测:往
hits_for_block加任何新形态前,先在--selftestT1 的正例里 放入该形态的合法写法——没有正例守卫的新判据就是给自己造误报机(nested-comment§8 同训)。 - 排除面两处同步:
.pre-commit-config.yaml本钩的exclude与脚本EXCLUDE_PREFIXES是两份字面拷贝,改一处必改另一处——手跑面与提交面不一致就会变成「两个数」。 - 判定用「行首 token / 邻接字符」这类保守形态学,不要用「看起来像标签」的启发式——
本仓围栏里混排中英文、全角符号、
µ/≠/→等字符,启发式必误伤。 - 本条钩子使 YAML 解析口径 51 → 52、提交期实跑 48 → 49;加/摘钩子按
docs/门禁说明/README.md§2 首条的自查命令三数同核(AGENTS 与索引两处计数同批改)。