# Q1 · B2 批第一批预注册 —— Jira hyperlink 族，以及**两条被活源推翻的裁定**

> 交接指的两组「一条判据解掉多段」：`` #`Jira `` + `#M23.8.1`（hyperlink）· `#M12` + `#M13`（谱系组件）。
> **两组都回活源核过了，两组的红输入都要改。** 这份文件先落读数，再落本批实际动的那一段。
>
> ⛔ 本文件不是「B2 批做完了」—— B2 是 37 段，本批只上 **1 段**（`` #`Jira ``）。
> 其余的读数、以及为什么它们不能照裁定表字面落，逐条写在 §1 / §4。

---

## 0 亲验读数（⛔ 全部本轮单跑；每个探针都先量了自己的分母）

### 0.1 三次探针自己坏掉 —— 先说这个，因为它决定了别的读数该怎么读

| # | 探针错在哪 | 假读数 | 怎么发现的 |
|---|---|---|---|
| **E1** | fixture 顶层是 `_meta \| file`，文档在 `doc.file.document`。我按 `doc.document` 走 | 「Message/Notification 的 INSTANCE = **0**」 | 自检印「探针走过的节点总数 = **1**」⇒ 一眼假 |
| **E2** | 只读 `styleOverrideTable` 里的 hyperlink，**漏了 `node.style.hyperlink`** | 「`Issue key` 节点 **82/82 无链**」⇒ 差点得出「§M23.8 从 2026-05-27 立规起合规率 0%」 | 82/82 这个整齐度不合理 ⇒ 换假说重测 ⇒ 真值是 **109/109 全合规** |
| **E3** | ticket 形态用了闭集前缀 `FB\|V4\|SEC\|BM\|THS\|MC\|TPC` | 桶内计数偏低（89 个节点只数出 68 个 ID） | 换成通用 project-key 形态后 89/89 全部命中 |

⇒ **纪律**：`E1` 靠「探针走过多少节点」的自检抓到；`E2` 靠「这个数太整齐」的怀疑抓到；
`E3` 靠「节点数 ≠ 命中数」的算术不一致抓到。
⛔ 三次都不是靠看代码看出来的 —— **量具自己也要有分母**。

### 0.2 判定面读数（Jira hyperlink 族，四份 fixture）

正确口径 = `node.style.hyperlink`（整串链）**或** `styleOverrideTable[characterStyleOverrides[i]].hyperlink`（子串链）。

| 桶 | 判定对象 | 出现 | **无链** |
|---|---|---:|---:|
| **A** | TEXT 名命中 `/issue\s*key/i` —— §M23.8 逐字的判定对象 | **109 个节点** | **0** |
| **B** | 名/祖先/首 60 字命中「来源\|出处\|Source\|讨论结论」—— §M23.8.1 [5] 逐字的对象 | 38 | **17** |
| **C** | 散文里顺口提到的号 | 50 | **38** |

A 桶细分（本轮单跑，四份 fixture 合计 109 个 `Issue key` 节点）：

```
文本含 ticket 形态 = 109 / 109
无 hyperlink       = 0
hyperlink 非 Jira browse URL = 0
文本 ID ≠ URL 里的 ID        = 0
hyperlink 的落点 via = style 109 · override 0
```

### 0.3 🔴 三个会让「照字面写的判据」整片出错的形态差异

| # | 差异 | 照字面写会怎样 |
|---|---|---|
| **F1** | `setRangeHyperlink(0, len)` 是**整串**加链 ⇒ REST 里落在 `node.style.hyperlink`，**根本不进** `styleOverrideTable` | 只读 override 表的判据把 **109 条合规判成违例**（这正是 E2） |
| **F2** | §M23.8 Acceptance 逐字 `{ type: 'URL', **value**: '<jira url>' }` —— 那是 **Plugin API** 的形状；REST payload 的键名是 **`url`** | 照 Acceptance 抄字段名 ⇒ 取到 `undefined` ⇒ 判据恒真或恒假 |
| **F3** | 裁定表 row 22 提的形态是闭集 `` `FB-\d+`/`V4-\d+`/`SEC-\d+` `` | **分母静默变小**：造故障实测 `scanned` **109 → 78**（缩 31 / 28%）而**违例仍是 0** ⇒ 分母缩了、闸照样绿。真 payload 里现取到的前缀还有 `BM` `THS` `MC` `TPC` |

⚠️ F2 与上一轮那条「hyperlink **在** REST payload 里（4 份 fixture 命中 258/85/33/8 处）」**不矛盾但更细**：
那一轮量的是「`hyperlink` 这个词在 payload 里出现过」，本轮量的是「它落在**哪个字段**、
键名叫什么」。⇒ ⛔ 「取得到」不等于「知道从哪取」，前者不足以写判据。

---

## 1 🔴 `#M12` / `#M13` 那一组：裁定表的红输入**被活源推翻**，本批⛔ 不落

裁定表 row 12/13 写的红输入是：
> 设备谱系 frame（name 命中 `S<N>`/`M<N>`，判定形态取自 §M33 [1]）内出现 web 谱系的
> `Notification` instance ⇒ 红

**三条读数各否掉它的一部分：**

| # | 读数 | 后果 |
|---|---|---|
| **G1** | `domain-tvu.md:170` 逐字「设备侧…走 `Message` set…web/SaaS 侧走 `Notification` set。**勿混用两套库**」——**但紧接着 `:171` 逐字**「`Message` 无按钮…若前进路径需一键动作或用户决策…**须升级到带按钮的 `Notification`（alert / dialog / pop confirm）或 `Popup Box`**」 | ⇒ 「设备帧内有 `Notification` ⇒ 红」会**判红一个规则明确要求的做法**。⛔ 这条判据不能上 |
| **G2** | §M33 [1] 逐字只给了**设备** frame 的识别形态（「name 命中设备 mockup 模式如 `S<N>` / `M<N>` / 设备屏幕 frame」）—— **没有 web frame 的形态** | ⇒ 反方向（web 帧里出现 `Message`，那个方向 `:171` 没给豁免）**判定面缺一块** |
| **G3** | 实测（丰 fixture，探针走过 101,295 节点）：`Message/INSTANCE` **55** · `Notification/INSTANCE` **16** ⇒ 共 71 个，其中**落在设备谱系帧内的 = 0** | ⇒ 谱系那个方向**今日分母 = 0 ⇒ 恒绿 ⇒ 零信息** |

### 🔴 但同一批读数指出了一条**真能 falsify、且分母非空**的判据 —— 而它属于另一段

同一次实测里另有 **3 个名为 `Notification` 但 type 是 `FRAME`（不是 INSTANCE）**的节点：

```
id 4528:3228 / 4528:3240 / 4598:4676
children 逐字 = FRAME:"Notification_content" + FRAME:"comfirm"   ← 拼写错误就是手打的痕迹
box = 480×161 / 480×182 / 480×182
4598:4676 的祖先逐字 = "M9 · Remove Confirmation — M7 base + Notification dialog overlay"（设备谱系帧）
```

⇒ 这是**自画 / 已 detach 的 Notification dialog**，而 `domain-tvu.md:169` 逐字要求
「反馈控件一律取库 variant **不自画**」、M12 Acceptance [2] 逐字也含「**不自画**」。

🔴 **而这恰好也是 `#M41` [3] 的红输入**（裁定表 row 39 逐字：「destructive confirm flow 的
modal-overlay 帧里 dialog 不是 TVU `Notification` instance（已 detach）⇒ 红」）。
⇒ **真正的「一条判据解掉多段」分组是 `#M41` + `#M12`，⛔ 不是裁定表指的 `#M12` + `#M13`。**
（`#M13` [2] 逐字只说「用对应谱系反馈组件」，**没有**「不自画」三个字 ⇒ 按仓规
「判据串逐字取自活源」，它不能靠这条判据算覆盖。）

⚠️ **本批⛔ 不落它**，理由是落点未定而非难做：
① 判定对象要从「名字像」收窄 —— 实测词族污染源：`Notification_content` **31 处** ·
`Notification Settings` **1 处（那是功能名，不是反馈组件）** · `Notification --> Flow_text` **2 处**；
② 「自画」与「M46 已登记的 detach 豁免」的边界要先定（M46 那 6 条豁免里就有 detach 相关）；
③ 已实测**未被既有闸覆盖**（`library-origin` 判的是已存在 INSTANCE 的来源库 · `library-binding`
判 M0/M1/M30 · `audit-mockup-html-conformance` 里那个 `Notification` 是**生成 HTML 产物**的 lint）
⇒ 是真缺口，值得单独一批。
⇒ **登记为下一批的第一候选**，判据草案与三条读数已在此。

---

## 2 本批实际落的：`` #`Jira ``（§M23.8）

**段 id 逐字是 `` #`Jira ``**（`--list` 现取 `docs/internal/mockup-conventions.md#\`Jira:1143`）——
heading 以反引号开头 ⇒ `slugOfHeading` 第一分支不匹配、退到 `split(/\s+/)[0]`。
⚠️ 那正是裁定文件 §9 登记的三个脆弱段 id 之一。⛔ 本批不改 heading（改它会同时改段 id）。

**Σ = 0**（散文形态 Acceptance，`--list` 现取「0 条 checklist」）⇒ 自陈**不带 `[N]`**
（`COVERS_RE` 里 `[N]` 是可选组；`(\S+)` 吃反引号，已核）。

### 2.1 判据（三个 probe，逐字出处逐条标注）

| probe | 断言 | 逐字出处 |
|---|---|---|
| **M23.8-A** | `Issue key` TEXT 既无 `style.hyperlink` 也无覆盖其字符的 override hyperlink ⇒ 红 | Acceptance 逐字「`probe → issueKeyText.getRangeHyperlink(0, len)` 返回 … **非 null**」 |
| **M23.8-B** | 有 hyperlink，但 URL 不是 Jira browse 形态 ⇒ 红 | `:1115` 逐字「必须 `setRangeHyperlink` **指向真实 Jira URL**」+ `:1136-1139` 实现例逐字 `https://tvunetworks.atlassian.net/browse/FB-9398` |
| **M23.8-C** | 文本里的 ID 与 URL 里的 ID **都取得到且不相等** ⇒ 红 | ⚠️ **这一条是推导的，⛔ 不是逐字** —— 见下方框 |

⚠️ **M23.8-C 的推导必须写清，⛔ 不许伪装成逐字**：Acceptance 只要求「非 null」。
但 `:1121` 逐字把做法写成「在目标文件内 **clone 一个现成 instance，改 title text +
issue-key text + `setRangeHyperlink`**」—— 三步里漏掉第三步，结果就是「文本是 FB-2、
链接指向 FB-1」，那不满足 `:1115` 的「指向**真实** Jira URL」。
⇒ 收窄到**两边 ID 都能解析出来且不相等**才报 ⇒ 解析不出就不报，假阳风险为 0。
现取违例 **0**，⇒ 它今日不改变任何读数，加它的理由是**规则自陈的工作流就是它的产生机制**。

### 2.2 ⚠️ 判定对象**刻意只收 A 桶**，B/C 桶本批⛔ 不判 —— 且这不是「因为难」

- §M23.8 主条逐字「M23.8 主条只管『加 `Jira requirement` 组件时其 issue-key 必带 hyperlink』」
  （`:1147` 原文就是这么划界的）⇒ A 桶就是它的全部射程。
- B/C 桶属 **`#M23.8.1`**，它是**另一个段 id**，且现取有 **55 条存量违例**（B 17 + C 38）
  ⇒ 直接上闸 = 一次红 55 条。⚠️ **`#M23.8.1` 的 Acceptance 逐字确实写着「交付物内无纯文本
  ticket ID」，所以 C 桶在它的字面射程内** —— ⛔ 我不许为了少红就说 C 桶不算。
  ⇒ 它需要的是**棘轮设计**（照 Q2 那个已降 B 档的形态：具名集合 + 只许缩），
  而棘轮的落点在 mockup 族有一个结构问题要先答：**规则跑在 live payload 上，每次是不同文件,
  基线 key 在 (fileKey, nodeId, ticketId) 上对 live 面意义不明** ⇒ **先定落点，⛔ 不先写闸**。
  ⇒ 登记为下一批，读数已在 §0.2。

---

## 3 预注册判据 P1–P7（⛔ 写死在动手之前）

### P1 · 自陈落在头 60 行内，且段 id 逐字对上
`parseCoversDeclarations` 只读 `.slice(0, 60)` ⇒ 自陈必须在新脚本头 60 行内。
段 id 逐字 `` docs/internal/mockup-conventions.md#`Jira ``，⛔ 不带 `[N]`（Σ=0）。
落地后 `--list` 该行必须从 `⬜ unclassified` 变 `✅ audit-mockup-jira-hyperlink.mjs`。

### P2 · 棘轮 41 → 40，BASELINE 只许收到现取值
`unclassified` **41 → 40** · `gated` **42 → 43** · `BASELINE_UNCLASSIFIED` **41 → 40**。
**造故障**：删掉那行自陈 ⇒ `EXIT=1` 且 `[S2]` 报 `unclassified 41 > BASELINE 40（+1）`。
⛔ 不许把 BASELINE 留在 41。

### P3 · 判据必须读**两个**字段（F1/F2 的直接防线）
- **造故障**：把判据改成只读 `styleOverrideTable` ⇒ 真 fixture 上必须**红出 109 条**
  （证明这个字段选择不是装饰）；改回后 0 条
- **造故障**：把字段名从 `url` 改成 Acceptance 里那个 `value` ⇒ M23.8-B 必须整片误报
- 单测里各留一条正向用例：`style.hyperlink` 形态 · `styleOverrideTable` 形态

### P4 · 分母地板 + 「零违例是零信息的绿」如实登记
- **分母地板**：真 fixture 上 `Issue key` 节点数必须 **≥ 1**（现取 109）——
  谁把判定对象的识别写坏，分母塌成 0，测试必须红并印出命中文件
- ⚠️ 现取违例 **0** ⇒ ⛔ 不许说「Jira hyperlink 守住了」。它是**前向 fail-closed**，
  「它真的会红」由造故障承担（P5），⛔ 不靠零违例这个读数
- ⛔ 也不许写 `expect(violations.length).toBeGreaterThanOrEqual(0)` —— 恒真断言 = 零信息

### P5 · 三个 probe 各自两侧
| probe | 造故障 | 预期红侧 |
|---|---|---|
| A | 删掉某个 `Issue key` 节点的 `style.hyperlink` | 恰好 1 条，点名 nodeId |
| B | 把某个 URL 换成 `https://example.com/x` | 恰好 1 条，probe 标 `M23.8-B` |
| C | 把某个节点的 `characters` 改成另一个 ticket ID | 恰好 1 条，probe 标 `M23.8-C`，报文印出两个 ID |
⚠️ 造故障在**内存里的 fixture 副本**上做（⛔ 不改那几个 340–390MB 的 git 追踪文件）。

### P6 · 注册面齐全（照 Q1-b `hash-n-semantic` 范式，⛔ 少一处就是没接线）
顶层 `scripts/audit-mockup-jira-hyperlink.mjs`（纯函数 + `covers-acceptance` + `isCliEntry` 守卫）·
`scripts/mockup-rules/jira-hyperlink.mjs`（薄模块，⛔ 判据不抄第二份）·
`scripts/mockup-rules/index.mjs` 注册 · npm script key（`stale-anchors` 会查）·
规则真源侧加「机检见 …」指针（⚠️ **只加缩进续行**，Σ=0 的段⛔ 别加顶层 checklist 条）·
单测 · `rule-load-map` 的 §AI 读取指引路由表（只在 staged 时触发，先跑再看要不要补）

### P7 · 零回归 + 必跑闸
`acceptance-gate-coverage` · `stale-anchors` · `rule-inventory` · `doc-shape` ·
`gate-mount-declaration` · `rule-load-map` 全 `EXIT=0`；
`pnpm audit:mockup-conformance --list-rules` 的条数从 **13 → 14**；
全量 `pnpm test` 基线 **3277 passed**，新增数算术对上。

---

## 4 ⛔ 这一批不能说的话

- ⛔ 不许说「B2 批做完了」—— B2 是 **37 段**，本批上 **1 段**，棘轮 41 → 40
- ⛔ 不许说「Jira hyperlink 已守住」—— 现取违例 **0**，今日是**零信息的绿**；
  且 §M23.8.1 的 **55 条存量**（B 17 + C 38）**一条都没管**
- ⛔ 不许说「`#M12`/`#M13` 共用一条判据」—— 裁定表那条红输入被 `domain-tvu.md:171` 推翻，
  且谱系方向今日分母为 **0**。真正的共用分组是 **`#M41` + `#M12`**（「不自画」），
  而 **`#M13` 不在其中**（它 [2] 里没有「不自画」三个字）
- ⛔ 不许说「上一轮那条 hyperlink 读数错了」—— 它没错，它量的是**另一件事**（有没有），
  本轮量的是**从哪个字段取**。两者都要，缺后者写不出判据
- ⚠️ `Issue key` TEXT 节点数与 `jira|requirement` 形态的 INSTANCE 数**逐文件相等**
  （6/6 · 89/89 · 14/14 · 0/0）—— 这**不是巧合**，是组件结构定的（`:1123` 逐字
  「1 个 title text（大号）+ 1 个 issue-key text（带 hyperlink）」）。
  ⇒ 可以互相当分母用，但 ⛔ 别据此假设「以后也一定 1:1」——
  谁在实例里多加一个 issue-key text，这两个数就分叉，而**判据只认 TEXT 那一侧**

---

## 5 ✅ 兑现记录（DS `9834b949`，2026-09-08）

`` #`Jira `` 已上闸，棘轮 **41 → 40** · `gated` **42 → 43**。规则条数 **13 → 14**。
⛔ **只上了 1 段** —— B2 是 37 段，⛔ 别读成「B2 批做完了」。

### 5.1 两侧读数（⛔ 绿灯不算证据）

**绿侧**（真 payload，本机四份 payload 快照）：
```
0054ib0nLmt27bC3QlGDl7   scanned= 89  违例=0  jira实例= 89  outside=0
DtZcMkhNy6qh6jbQQnhreQ   scanned=  6  违例=0  jira实例=  6  outside=0
jnlIId30lS3xjRmZnZTWQy   scanned=  0  违例=0  jira实例=  0  outside=0
xu7H3ppLyQfa3fOmU8M7iO   scanned= 14  违例=0  jira实例= 14  outside=0
合计                     scanned=109  违例=0  jira实例=109  outside=0
```
⚠️ 那个 **0 是零信息的绿** ⇒ 本闸是**前向 fail-closed**，「它真的会红」全部靠下表。

**红侧**：

| # | 造什么 | 读数 | 误伤 |
|---|---|---|---|
| **F-J1** | 拆掉 `linkCovering` 里读 `node.style.hyperlink` 那一支 | 真 payload **109 / 109 全部变假阳**（全 `M23.8-A`）· 单测红 **6** 条 | 0 |
| **F-J2** | ticket 形态换成裁定表原提的闭集 `FB\|V4\|SEC` | 分母 `scanned` **109 → 78**（缩 31 / 28%）而**违例仍是 0** ⇒ 分母静默变小、闸照样绿 · 单测**恰好红 4 条**（`BM`/`THS`/`MC`/`TPC`） | 0 |
| **F-J3** | 拆掉「必须在 `Jira requirement` 实例内」这个射程限制 | **恰好红 1 条**（实例外那条阴性对照） | 0 |
| **F-J4** | 删掉那行 `covers-acceptance` 自陈 | `EXIT=1` · `[S2] unclassified 41 > BASELINE 40（+1）` · 闸自己逐字印「⛔ 别改 BASELINE 放行」 | — |

三次代码类造故障还原后 `cmp EXIT=0`。
六条必跑闸全 **EXIT=0**；全量 `pnpm test` **3303 passed / 0 failed**（3277 + 26，算术精确对上）。

### 5.2 🔴 P4 那条判据**自己被推翻了** —— 改的是判据，⛔ 不是事实

P4 原写「**真 fixture 上 `Issue key` 节点数必须 ≥ 1**」当分母地板。落地时亲验：

```
.gitignore:29                       ⇒ figma-data/mockup/
git ls-files figma-data/mockup/     ⇒ （空）
```

⇒ 那四份 payload 快照（0.5 MB + 3 份 340–390 MB，`audit-plan-lifecycle.test.ts:504`
逐字称「**1.1 GB**」）**只存在于开发者主工作树**，任何别的 clone / CI 都取不到。
⇒ 依赖它们的断言在别处只会红或被 skip 掉 ⇒ **P4 那条判据不可实现**。

**改法**（⛔ 不是放宽）：单测全部改用**照真形态合成**的节点树（形态逐项取自本轮那次真
payload 测量，写在测试文件头）；分母地板改成「合成树上判定对象识别不到就红」——
它守的是**判据的识别逻辑**，⛔ 守不到「真交付里还有没有 Jira annotation」。
而 **109 这个分母如实登记为一次本机读数**，⛔ 不由任何闸守。
⚠️ 这一条要记住的一般性教训：**写「在真 fixture 上断言 X」之前先问「这份 fixture 进 git 了吗」。**

### 5.3 🔴 两次「预测的红侧条数偏低」，都是同一种漏想

| 造故障 | 我预测 | 实际 | 多出来的那些 |
|---|---:|---:|---|
| F-J1（拆 style 分支） | 3 | **6** | `M23.8-B` / `M23.8-C` / 三 probe 并存那三条 —— 它们的 fixture 用 `styleUrl`，拆掉分支后**统统降级成 A** |
| （上一批 F-A，R20 逐行扫） | 2 | **4** | 闭集值域那两条 —— 它们要**先看见相邻声明**才走得到判值域那一步 |

⇒ 两次都不是「多了一个原因」，是**同一个根因把判据的后续分支一起挡住了**。
一般性教训：**预测红侧条数时，除了「谁会直接坏」，还要问「谁的前置条件被这个故障挡住了」。**

### 5.4 ⚠️ 顺带更正我自己 §0.3 F3 那个数

F3 原写「89 个节点里 21 个（24%）的前缀不在闭集里」—— 那个 21 是拿**七前缀**清单
（`FB|V4|SEC|BM|THS|MC|TPC`）量的，而裁定表实际提的闭集只有**三个**前缀。
⇒ 改用造故障直接量到的读数：**闭集 `FB|V4|SEC` ⇒ 分母 109 → 78（缩 31 / 28%）、违例仍 0**。
两个数都真，但只有后者是「裁定表那个闭集的代价」。⛔ 别再引用前者去说裁定表。
