# 处方：`audit-stale-anchors` 补上「代码 span 里的文件路径」这条边

- 日期：2026-08-24 · 出处：[`reports/2026-08-24-retrospection-s2-s3.md`](../reports/2026-08-24-retrospection-s2-s3.md) §4
- pin：`71ac2711` · 裁判来源：lab 自写实验 + DS 自己的 `audit-stale-anchors.mjs` 源码自陈
- 优先级：**这是第二轮第 8 步删除批次的前置**。不补它，删除的「闸绿」不携带信息。
- **执行状态**：🟢 **已由 DS 落地** —— 亲验 pin **`7fcca917`**（2026-09-01，`lab:N93`）。`scripts/audit-stale-anchors.mjs:516` 逐字「反引号 inline code spans：script 引用（形态 A/B）**以及**文件路径（形态 C，**2026-08-25 补**）」，`:517` 保留了旧版那句「刻意不校验」的留痕对照；并新增 `codeSpanTally` 分形态计数（`:736`、`:873-875`：显式相对(受判据保护) / 根相对已解析 / 根相对解析不到(**边界 B1：不报**) / 外部目录）⇒ 本处方主张的那条边**已补上**，且 FP 风险按形态分档登记而非一刀切
- **执行状态重取**：@`d07be8e9`（2026-09-11 §30 全量重取，量具 `metrics/proposal-execution-status-refresh.mjs`）—— 锚 4/4 仍成立（`scripts/audit-stale-anchors.mjs`，⚠️ 行号已漂 ⇒ 引用请按本行的 pin 重取） ⇒ **结论不变**。

---

## 1. 为什么提这条（不是「闸写错了」）

DS 的 `scripts/audit-stale-anchors.mjs:197-199` **自己写了理由**：

> 反引号 inline code spans：只取 script 引用。（反引号「文件路径」**刻意不校验** —— basename 提及 /
> 外部仓路径 / `.figma.ts` 模式串混杂、无可靠信号分离，误报率过高；导航式文件锚点由上面 markdown 链接覆盖。）

**这是一个有意的、写了理由的 FP/FN 取舍。** 本处方不主张它当初判错，只主张它依赖的那个前提
——「导航式文件锚点都写成 markdown 链接」——**在它自己的扫描面里已经不成立**：

`docs/internal/mockup-conventions.md:3237`（**在 `SCAN_FILES` 内**）把存活规则 `M-DISCIPLINE`
的两条实证出处写成了代码 span：

```
- **M-DISCIPLINE**：…② 读完 8 步 Jira 流程实际只命中 3/8 → 5 步返工（详
  `../_archive/retrospection/2026-06-01-rule-execution-discipline-gap.md`）；③ … （详
  `../_archive/retrospection/2026-06-01-v4-1865-rules-backflow.md`）。
```

## 2. 实测后果

在 pin 住的 worktree 里删掉上面那份被引用的文件，`audit-stale-anchors` **exit 0，零孤儿**：

| 实验 | 引用方 | 引用方在 `SCAN_FILES`？ | 闸的反应 |
|---|---|---|---|
| 删 `2026-06-01-rule-execution-discipline-gap.md` | `mockup-conventions.md:3237` | ✅ **在** | **exit 0，未发现** |
| 删 `2026-05-11-library-key-confusion.md` | `multi-session-collaboration-rules.md:214` + `tests/audit-rule-inventory.test.ts:207` | ❌ 不在 | exit 0，未发现 |
| 删全部 34 份 S2 候选 | — | — | exit 0，锚点 TOTAL **529 = baseline**，一字未变 |

baseline 对照：未删任何文件时 `audit-stale-anchors` exit 0 / TOTAL 529 / 零孤儿；
`audit-plan-lifecycle` exit **1**（1 条 pre-existing：`infra-f129` 计划零入站），删前删后**同一条**、无新增。

⇒ spec §11.4 写的「删错了 gate 立刻红」，对**归档物删除**这个操作实测不成立。

## 3. 判据怎么改（lab 已实证的形态）

DS 当初列的三类误报源，用**「解析到实存目标才算锚点」**这一条就能全部消掉：

| DS 列的误报源 | 新判据下的结果 |
|---|---|
| basename 提及（`x.md`，不带目录分量） | **不算**——要求至少一个 `/` |
| 外部仓路径（`MicroApps Console/docs/...`） | **不算**——解析不到本仓实存文件 |
| `.figma.ts` 模式串 | **不算**——解析不到实存文件 |
| `` `../_archive/retrospection/x.md` `` | ✅ **算**——按路径段边界后缀匹配解析到实存文件 |

**段边界是关键**，lab 侧已内建 4 条 must-not-hit 守它：

- `tion/y.md` ⛔ 不得命中 `docs/_archive/retrospection/y.md`（不是段边界）
- `a/y.md` ⛔ 不得命中 `b/y.md`（段内容不同）
- `zzretrospection/y.md` ⛔ 不得命中（倒数第二段被前缀污染）
- `y.md`（纯 basename）⛔ 不得命中（无目录分量）

参考实现见 lab 的 [`adapters/ds-doc-graph.mjs`](../adapters/ds-doc-graph.mjs) 的 `PARTIAL_RE` +
`resolvePartial()`（含内建正负控），DS 侧照搬判据形态即可，不必照搬代码。

## 4. 建议的最小改动

1. `scripts/audit-stale-anchors.mjs` 的代码 span 循环里，除现有 script 分支外，加一条
   **path 分支**：span 含 `/` 且以 `PATH_EXTS` 结尾 → 按段边界后缀匹配解析；
   **解析到实存文件 = 不报**；解析不到本仓任何文件 = **不报**（这是关键，避免 DS 担心的误报）；
   解析到「曾经存在但现在不在」= 无法直接判断 ⇒ 见下条。
2. 因为「解析不到」既可能是外部路径也可能是**真死链**，二者需要区分。建议判据收成：
   **span 路径的目录分量能匹配到本仓某个实存目录，但完整路径不存在 ⇒ 报孤儿**。
   （`../_archive/retrospection/已删.md`：`_archive/retrospection/` 存在、文件不在 ⇒ 报。
   `MicroApps Console/docs/x.md`：目录分量在本仓不存在 ⇒ 不报。）
3. `SCAN_FILES` 是否扩面是**另一个决定**，本处方不绑定它。但请注意：即使不扩面，仅
   `mockup-conventions.md` 一个文件里就有真实的代码 span 归档引用（M-DISCIPLINE）。

## 5. 验收判据（⛔ 逐条实测，不接受「已完成」式断言）

**必须造故障验证它会拦**——这是 DS 自己 `working-principles.md:63` 立的规矩
（「新增闸必须造故障验证它会拦，"跑一遍绿"不算验证」）。逐条给实测输出：

| # | 判据 | 期望 |
|---|---|---|
| 1 | 未改动仓库跑 `audit:stale-anchors` | exit 0，且**锚点 TOTAL 从 529 上升**（新增 path 锚点被计入；若仍是 529 说明新分支没生效） |
| 2 | 临时删 `docs/_archive/retrospection/2026-06-01-rule-execution-discipline-gap.md` 后跑 | **exit ≠ 0**，孤儿清单含该路径，并指出引用方 `mockup-conventions.md:3237` |
| 3 | 临时删 `docs/_archive/retrospection/2026-06-01-v4-1865-rules-backflow.md` 后跑 | **exit ≠ 0**（同规则第 ③ 条出处） |
| 4 | must-not-hit：在 `mockup-conventions.md` 插一行含 `` `MicroApps Console/docs/handoffs/x.md` `` | **exit 0**（外部仓路径不得报） |
| 5 | must-not-hit：插一行含 `` `Badge.figma.ts` `` 或纯 basename `` `x.md` `` | **exit 0**（无目录分量不得报） |
| 6 | must-not-hit：插一行含 `` `tion/2026-06-01-v4-1865-rules-backflow.md` ``（非段边界） | **exit 0** |
| 7 | 恢复后复跑 | 回到判据 1 的读数 |
| 8 | 报 §4 那两份文件的**实际 exit code + 孤儿行原文** | 贴输出 |

⚠️ 判据 4–6 是重点。**只验「该拦的拦住了」抓不到「不该拦的也拦了」**——DS 当初刻意不校验就是怕这一侧，
所以这一侧的实测输出比另一侧更重要。

## 6. 与第 8 步删除批次的关系（执行顺序）

```
本处方落地（补闸 + 造故障验收）
        ↓
lab 重跑演练：删 34 份 → 期望闸**由绿转红**，红在哪几份上要与 lab 的口径读数逐条对上
        ↓                     （对不上 = 两边至少一个错，停下来查，不要放行）
retrospection-adr 处方按批执行（提炼 → 删原件 → 每批跑闸）
```

⛔ **不要跳过中间那步。** 补完闸后必须先验证「闸红的那几份」与 lab `path 口径` 算出来的
「有入站的那几份」是同一批——这是两个独立实现的交叉对账，也是本轮唯一能验 S2 语义正确性的机制。
