# tech-diagram-gif / scripts

Taste Gate「版面幾何」組的判定工具。這一層存在的理由：那組檢查項的判定手段是
**讀座標算術**，不是看截圖 —— ≥6px 的標籤間隙、1.35 的繞路比、菱形斜邊上的文字溢出，
縮到瀏覽器視窗後肉眼都分辨不出來，憑印象打勾等於沒有閘門。

## 關鍵檔案

| 檔案 | 用途 |
|---|---|
| `verify-geometry.py` | 讀一份 SVG，驗 XML 良構與重複屬性、數量預算、折數、繞路比、交叉、穿越節點、節點間距、容器 gutter、同邊 port 間距、平行線間距、標籤遮罩間隙、遮罩 z-order、動畫週期、文字溢出。節點認 `rect` / `polygon` / `circle` / `ellipse`。exit 1 表示未通過，結尾列出自己沒涵蓋的項目 |
| `test-verify-geometry.py` | 上者的負面測試：22 種變異各弄壞一項確認抓得到、8 個回歸案例確認不誤報（換畫法/換屬性順序/漸層底色/tspan/圓形節點等合法寫法不得讓元素靜默消失）、3 個警告案例確認會出聲；外加正向對照與 data-role 路徑 |
| `fixtures/sample-flow.svg` | 測試用的真實圖（claude-dd 傳播路徑圖的中間產物），8 節點 / 10 連線 / 3 容器，含一個菱形節點與 SMIL 動畫 |

## 此層約束

- **純標準庫，不得引入 pip 依賴**。SKILL.md 承諾「不引入 Python/cairosvg 渲染管線」，
  這裡是唯一的 Python，只做座標算術；一旦需要 `pip install` 就違反了 claude-dd
  「不塞 runtime 依賴」的原則（見 repo 根目錄 CLAUDE.md 的收編檢查清單第 4 項）
- **改 `verify-geometry.py` 後必須重跑 `test-verify-geometry.py`**。檢查腳本自己會錯，
  而且錯起來像正常結果 —— 可能全判通過（漏檢），也可能全判失敗（假陽性）。
  實際踩過兩次：用「座標字串有沒有出現在 svg 裡」猜形狀，把矩形節點當成菱形而誤報
  三處文字溢出；第一版整個漏掉容器 gutter 這項，跑完全綠，是節點改寬撞到邊界才被人眼發現
- **新增檢查項時，同批補一個變異案例進 `CASES`**。沒有負面測試的檢查項等於沒寫；
  若該項曾誤報過，再補一個 `POSITIVE_CASES` 回歸案例（例：菱形外框內、形狀外放遮罩不算被蓋到）
- **不涵蓋的項目要在 `print_not_covered()` 裡列出來**。腳本印「全部通過」時，
  讀的人會以為 Taste Gate 那一組都驗過了 —— 實測踩過：SKILL.md 一度宣稱
  「涵蓋全部項目」，實際漏了標籤間隙、遮罩 z-order、強調色三項
- 辨識元素優先讀 `data-role`（`node` / `container` / `edge` / `mask`）；無標記時退化用
  畫法猜並印警告 —— 猜錯是靜默漏檢，所以 SKILL.md 第 3 步要求新圖一律標 `data-role`
- **屬性一律用 `attrs_of()` 拆成字典再讀，不要寫順序固定的正規式**。SVG 屬性順序在規格上
  無意義，`x y width height` 寫死會讓合法的重排寫法整個節點消失，而且**標了 `data-role`
  也救不了**（座標比對發生在讀 role 之前）。同理不要綁特定 `rx` 值或色票：
  `icons.md` 用 `rx="10"`、style-2 用另一套 fill、style-2/8 的畫布是漸層 ——
  任何一個寫死都會讓某類元素靜默消失，然後印「✅ 全部通過」
- **字級一律先讀 SVG 自己宣告的，`FONT_SIZES` 只是最後退路**。優先序照瀏覽器：
  `<style>` 的 class 規則 > `font-size` 屬性 > `FONT_SIZES` > `DEFAULT_FONT_SIZE`。
  2026-09-12 以前完全不讀 `<style>`，字級只靠那張寫死的表猜，兩個方向都會錯：
  猜高了誤報（`gen_loop` / `gen_usage` 的 `.nm` 實際 15 卻當成 20，英文版誤報十幾處文字溢出），
  猜低了**靜默漏檢**（只在 `<style>` 把字級放大的圖，文字爆框卻判通過）。
  只認 px 字面值 —— `em` / `rem` / `%` / `var()` 算不出絕對值，要讓它退回下一層而不是猜，
  且這個限制要寫在 `print_not_covered()` 裡
- **無法判定時要出聲，不要靜默跳過**：曲線與簡寫指令的連線列進 `skipped_edges`、
  非正交線段列進 `slanted`、偵測不到遮罩走 `report.warn()`。
  「（沒有偵測到）」這種中性語氣的輸出讀起來像正常結果，是最危險的失敗模式
- **正規式不在乎 XML 合不合法，所以要單獨驗**。`check_wellformed()` 掃重複屬性並丟給
  `ElementTree` 解析一次。實測踩過：敘事動畫版對本來就是虛線的連線再加一個
  `stroke-dasharray` 做畫入效果，兩個屬性打架、瀏覽器靜默取後者把虛線畫成實線，
  而所有幾何檢查照樣全綠 —— 是人眼看 GIF 才發現的
- **部分標記比完全不標更危險**。只要 SVG 裡出現任何一個 `data-role`，整份就切換成
  標記模式，沒標的元素會被整批略過。實測踩過：畫敘事動畫版時加了帶
  `data-role="decoration"` 的箭頭，節點與容器立刻從 8/3 變成 0/0，輸出仍是
  「✅ 全部通過」。`count_unmarked()` 為此而寫 —— 標記模式下若還有疑似節點／容器
  沒標，點名數量並 warn

## 與上層的關係

`../SKILL.md` 第 4 步呼叫本目錄的 `verify-geometry.py`，**先跑腳本再看截圖**；
腳本涵蓋「版面幾何」組**除了「強調色元素 ≤2、註解框 ≤2」以外**的項目（哪個顏色算
accent 無法通用判定），執行結尾由 `print_not_covered()` 明列自己沒涵蓋的部分。
截圖只判它算不出來的（文字擠行、假捲軸、小球位移）。
數值上限的唯一來源是 `../references/composition-quality-contract.md`，
本目錄的 `LIMITS` 是那張表的程式化副本 —— 改上限要兩邊同步，contract 為準。
