# Q1 · §M23.8.1 第 5 款「出处本身必须可点」那一半 —— 量分母，结论是**不上闸**

> ⛔ 写在动手之前，落地时**没有动手**。
> 上一轮（`495212dd` 预注册 §9.3-②）把这一支列为「**下一批最划算的抓手**」，逐字理由是
> 「标了溯源字样的 TEXT 现取 **296 个**，而本闸只在 23 个 ticket range 上有牙
> ⇒ 分母大一个数量级且今天几乎全无人看」。
> **本轮量了那 296 个。那句话的两半都不成立** —— 见 §1。
>
> ⚠️ **那句话是我自己上一轮写的**。这是「照交接字面动手前先推演」第 N 次命中，
> 且这次的交接**就是我自己的**（本仓已登记：`verify-handoff-literal-before-executing`
> 第七问逐字「这份『交接』是不是我自己写的？是也一样回活源核」）。

---

## 0 判据串逐字取自活源（`mockup-conventions.md` §M23.8.1 第 5 款）

> 任何 UX 交付卡 / annotation 一旦标注了「**来源 / 出处 / Source / 讨论结论**」等溯源字样，
> 该出处本身必须是 `setRangeHyperlink` 可点链接

已上闸的那一半（DS `495212dd`）只判**射程内的 ticket range**（23 个）。
本轮要量的是另一半：**「出处本身」是 Slack 线程 / Confluence / 外部文档时，它可不可点。**

---

## 1 现取读数（4 份本机 payload 快照，遍历 **305,222** 节点）

⚠️ `0054ib0nLmt27bC3QlGDl7.json` 的 mtime 是 **2026-09-09 09:11**（本轮开工前 35 分钟刚更新）
⇒ 296 这个数**是现取的**，不是照抄上一轮。巧合地与上一轮相等。

### 1.1 第一个读数就把抓手打了个问号

```
markedTextCount = 296   （实例外、`characters` 命中 /来源|出处|Source|讨论结论/ 的 TEXT）
四象限（characters 里有无裸 URL 文本 × 节点上有无 hyperlink）：
  hasUrlText_hasLink     0
  hasUrlText_noLink      0      ← 「裸 URL 没加链」这个形态，今日真面上 **一个都没有**
  noUrlText_hasLink     11
  noUrlText_noLink     285
```

⇒ 上闸时脑子里那个「设计师把 Slack 链接贴成纯文本」的画面，**在真面上不存在**。
11 个有链的 host 分布：`atlassian.net` 16 · `figma.com` 7 · `slack.com` 6 · `hubspot.com` 1 · `youtu.be` 1。

### 1.2 285 个「无链」是什么 —— 🔴 `Source` 在 TVU 是产品核心术语

抽样立刻撞上：

```
"Source configuration change 信号源配置变更"
"IP Source: Switched to IP Source."
"Source Switcher"        ×26
"Selected Source:"       ×多处
"Source From"            ×8
"No Source on R"
"Partyline Source" · "Program_name…(Source 1,2,3,4)" · "A4 · Source lost → Test Signal"
```

TVU 是视频流公司，**`Source` = 信号源**是产品第一术语。
⇒ 射程词与产品术语**同音异义**，而且是最高频的那种。

### 1.3 🔴 先更正我自己本轮的一个中途推断（⛔ 它差点让我给出错的收窄建议）

量到这里我先切了一刀「含中文溯源词 vs 只含英文 `Source`」，得出 22 vs 274，
并准备下结论「英文 `Source` 那 274 个全是信号源」。**回真面逐条核后，那一刀是错的**：

| 节点 | characters | 我的错判 | 真相 |
|---|---|---|---|
| `6324:85` / `6856:5454` | `… not rebuilt in here. **Source: BM-603** Billing warning setting …` | 归进「274 个信号源」 | **真溯源标记**（英文冒号形态），现有闸判它 `M23.8.1-B` 是**真阳** |
| `5385:1753` | `Service Price — **Source of Truth** (BM-734)` | 归进「22 个真溯源」 | 「**真源**」词组，判 `M23.8.1-A` 是**假阳** |

⇒ **溯源用法与产品用法都跨语种存在，⛔ 按语种切不开。**
（这也顺带量出了现有闸那 7 处违例的成色：**假阳 1 / 7 ≈ 14%**，那 1 处就是 `Source of Truth`。）

### 1.4 按「`Source` 的用法」切也切不开

换成形态切（`Source` 后跟冒号 / 破折号 / 行首）：

```
真源词组 Source of Truth      1
溯源形态（Source: / Source — / 行首 Source）   206   ← 但里面是 ↓
产品术语（IP Source / Source 1 / 信号源 …）    63
其余                          26
```

那 206 个「溯源形态」的头几条逐字是：

```
"Source configuration change 信号源配置变更"      ← 行首 Source
"IP Source: Switched to IP Source."               ← Source + 冒号
"Selected Source:"                                ← Source + 冒号
"Source Switcher:"                                ← Source + 冒号
```

⇒ **`Selected Source:` 与 `Source: BM-603` 在形态上完全同型。** 排不开。

### 1.5 试了一条可能救活它的信号：既有的 `ANNOTATION_RE` —— 也不成立

仓里已有 annotation 区 vs product 区的区分（`audit-mockup-typography-icon.mjs:161`，
`bilingual-spacing` 与它刻意各留一份）。把它当第二个 AND 条件量：

| | 数 | 有链 | 无链 |
|---|---:|---:|---:|
| annotation 区 | **146** | 5 | 141 |
| product 区 | **150** | 6 | 144 |

**两个方向同时坏**：

1. **假阳只砍掉一半** —— annotation 区第 1 条逐字是
   `Anti-pattern: the user changes the Merge All **IP Source** toggle but is told "Switched to IP Source"`
   ⇒ annotation **在描述产品文案**，射程词照样在里面。
2. **真阳漏掉过半** —— 11 个有链的真溯源标记里 **6 个落在 product 区**
   （`§1 Source` / `§1 需求来源 Source` 这些 name 不命中 `ANNOTATION_RE` 的任何词）。

⇒ 加这个 AND 换来的是「假阳 −50% / 真阳 −55%」，⛔ 不是收窄，是换一批错。

---

## 2 结论：⛔ 不上闸，理由与 [2] 那 82 个 range **逐字同型**

| | [2] 无条件那一半（82 个 range） | **本支**（第 5 款「出处本身可点」） |
|---|---|---|
| 挡路的东西 | 形态上排不掉的假阳（`Q2-1` 是设计探索编号 · `PID-1001` 是 UI 文案里的进程 ID） | 形态上排不掉的假阳（`Source` = 信号源，且是产品第一术语） |
| 硬在哪 | ticket 形态本身是硬的，坏的只是**哪些前缀是真 project key** | **更糟** —— 连射程词本身都是产品术语，且跨语种、跨形态 |
| 今日真违例 | 64 个无链/链错（其中含真阳） | **0**（`hasUrlText_noLink` = 0；11 个真溯源标记全部已有链） |

⇒ 上闸的两侧代价都不划算：**恒绿零信息**（真违例 0）**加上**大批排不掉的假阳。
这与 `#Canonical` 判 NMC 是同一个病（前件够不着 ⇒ 上闸恒绿），
也与本仓反复点名的「分母静默变小 / 假分母」同族。

### 2.1 ⛔ 但这一支 **不是 NMC** —— 它是「可测但今日不值得」

判定面**完全落在 payload 里**（hyperlink 取得到，上一轮已实测 258/85/33/8 处）。
够不着的不是数据，是**一个能把溯源 annotation 与产品 UI 文案分开的信号**。
⇒ 登记为「已量、不上闸」，⛔ 别写成「不可机检」——
那正是本仓已登记的 `#M21.3` 假理由的传染方式。

### 2.2 重开条件（⛔ 被否也要写，否则下一轮重新提上来）

**任一条成立即重开**：

1. **出现一个稳定的溯源 annotation 身份信号** —— 例如图层名约定（现取五花八门：
   `§1 Source` / `card-title` / `ref-caption` / `text` / `_note · depleted state → BM-603 ref`），
   或规则真源里定一条显式标记。⚠️ 后者属「往真源发明格式」⇒ **与 Q1-b / Q1-e 同型，需 owner**。
2. **`hasUrlText_noLink` 从 0 变正** —— 即真出现「把 Slack / Confluence 链接贴成纯文本」的存量。
   这一条**够得着且可自动观测**：现有闸每轮已在算 `markedTextCount`，加一个裸 URL 计数即可（见 §3）。

---

## 3 本轮唯一的代码改动：⛔ 把那个**误导性读数**就地更正

`scripts/mockup-rules/source-link-integrity.mjs:50` 逐字印：

```
§M23.8.1 判过 23 个 ticket 引用（标了溯源字样的 TEXT 296 个）· 违反 0
```

🔴 **那个 296 就是这条错抓手的来路** —— 它读起来像「296 个溯源 annotation 里只有 23 个有牙」，
而真相是 296 里绝大多数是 `Source Switcher` / `Selected Source:` / `Source From`。
**我上一轮读了自己印的这个数，并据此写下「下一批最划算的抓手」。**

⇒ 改动只有两处，**零新判据**：
1. 报文不再裸印 `markedTextCount`，改印**它到底是什么**（含歧义警告）+ 新增 `urlTextNoLink` 计数
   （= §2.2 第 2 条重开条件的自动观测点）。
2. 判据头注释里 `[3]` 那句「**本轮只是没量分母，⛔ 不是不可测**」就地更正为「已量，结论见本文件」。

---

## 3.1 ✅ 落地回填（2026-09-09，DS `d9cd4aa5`，已 push）

### 实测读数

| 项 | 预期 | 实测 |
|---|---|---|
| `unclassified` / `gated` / `nmc` | **不变**（36 / 47 / 50）—— 本轮零判据扩展 | **36（BASELINE 36）· 47 · 50** ✅ |
| 真面 `scanned` / `violations` | 23 / 7（与上一轮逐字相同） | **23 / 7** ✅ |
| 真面 `insideMainCount` / `outOfScopeCount` | 112 / 82 | **112 / 82** ✅ |
| 真面 `markedTextCount` | 296 | **296** ✅ |
| 真面 `urlTextTotal` / `urlTextNoLink` | **0 / 0**（新字段） | **0 / 0** ✅ 与探针一致 |
| 七条必跑闸 | 全 0 | 全 **EXIT=0**（含 `gate-ci-parity`） |
| 全量 `pnpm test` | 3362 + 5 = 3367 | **3367 passed** / 0 failed ✅ |
| Σ 变化 | 0（⛔ 未改规则正文一个字） | **0** ✅ |

### 造故障两次（⛔ 恒 0 的计数必须证明它真会变正）

每次注入都先验落盘（断言「文件内容与注入前不同」）**并先验能 `import`**。
⚠️ 数用例前先做那一问「这个故障会改变哪些测试 helper 的输出」——
本组 helper（`text()` / `doc()` / `jiraInstance()`）都只造节点、不含 URL 文本 ⇒ 不参与，
所以这次预测没有被 helper 拉偏（那正是前两批各栽一次的根因）。

| # | 造什么 | 预测 | 实测 |
|---|---|---|---|
| **F-C1** | 拆掉 URL 计数里的 `linkCovering` 检查（改成无条件 `urlTextNoLink++`） | 恰好 1 条 —— 那条「同一个 URL 被 hyperlink 覆盖 ⇒ 计数回 0」的阴性对照 | **恰好 1 条** ✅ |
| **F-C2** | 把 URL 计数搬出 `marked` 射程判断（对所有 TEXT 都算） | 恰好 1 条 —— 射程对照那条 | **恰好 1 条**，且逐字点名 `URL 在**没标溯源字样**的 TEXT 里 ⇒ 两个计数都不动（射程对照）`，报 `expected 1 to be +0` ✅ |

两次还原后 `cmp` 均 **EXIT=0**，逐字 grep `F-C1 injected|F-C2 injected` 残留 **0**。

### 🔴 回填时才想清楚的一件事

那 5 条新用例里最要紧的**不是** URL 那 4 条，是最后那条
`⛔ 射程词单独出现**不是**分母 —— 产品术语照样计入 markedTextCount`：
它把 `Source Switcher` / `Selected Source:` / `Source From` / `IP Source` 四行**逐字**钉进测试，
断言 `markedTextCount === 4` **而 `scanned === 0`**。
⇒ 「`markedTextCount` 不是分母」这句话从此**有牙**，而不只是一句注释里的告诫。
⚠️ 这条纪律本仓已登记过（`sink-discipline-into-mechanism-not-prompt`）——
本轮如果只改注释、不加这条用例，下一轮的我照样会再读错一次那个数。

---

## 4 ⛔ 这份文件不能说的话

- ⛔ 不许说「§M23.8.1 第 5 款不可机检」—— 判定面在 payload 里，见 §2.1。
- ⛔ 不许说「溯源出处已全部可点」—— 量的是 4 份**未进 git** 的本机快照，且 285 个无链节点
  里绝大多数**根本没被判断过是不是溯源标记**，只是形态上排不开。
- ⛔ 不许说「296 是分母」—— 本文件的全部内容就是在说它不是。
- ⛔ 不许说「现有闸有假阳所以不可信」—— 假阳 1/7，那 1 处是 `Source of Truth`，
  已登记；其余 6 处逐条核过是真阳。
- ⛔ 不许把「真违例 0」读成「这条规则今天被守住了」—— 11 个真溯源标记全部有链是**设计师的功劳**，
  ⛔ 不是闸的功劳（本闸从没判过它们）。
