# GROUNDTRUTH: pdf-inspector nexify v11 误绑率核验(以 rust-analyzer 为标准答案)

- **日期**: 2026-09-02
> **注记（2026-09-03）**：验证基建已迁移，本文旧路径 `~/workspace/...` 现为 `~/clawspace/nexify-verify/...`（graphify-codewhale → comparison；groundtruth 后上提到 nexify-verify/）。正文路径保留为历史事实。
- **性质**: precision 核验 pilot——用 rust-analyzer(1.98.0 LSP goto-def)对 nexify 在 pdf-inspector 上抓的 **INFERRED/AMBIGUOUS resolved call 边**逐一比对"真被调函数"
- **语料**: `~/clawspace/nexify-verify/pdf-inspector-snapshot`(55 个 .rs + 8 个 .py 等,同库源码可构建副本 `~/clawspace/nexify-verify/pdf-inspector-build`)
- **nexify 缓存(只读)**: `~/clawspace/nexify-verify/pdf-inspector-snapshot/.nexify/cache.db`,extract_version=11
- **oracle**: rust-analyzer 1.98.0(`rustup component add rust-analyzer rust-src --toolchain 1.98.0`),以 pdf-inspector-build 为 root 起 LSP,对每条边调用点 (file, line1, col_start) 发 `textDocument/definition`,拿真 def 位置与 nexify target 节点 (file, line) 比对
- **比对基线**: 已先验证 snapshot 与 build 两树源码逐文件一致(diff -rq 无差异);nexify 调用点 col 为 **0-based byte 半开区间**,line 为 **1-based**,583 条 .rs 调用点的 span 文本全部 == 目标函数名(定位零偏差前提)
- **补充 oracle pass**: 对 feature 门控代码,用 `features=["vision"]` 再起一次 RA(vision=[] 纯空 feature,不引入依赖),fusion.rs 9 条中 8 条转为可判

## 0. 核验集合与范围声明

任务给定的集合为 INFERRED resolved(603)+AMBIGUOUS(4)。实际按语言拆分:

| 语言 | INFERRED resolved | AMBIGUOUS resolved | 合计 |
|---|---:|---:|---:|
| **Rust(.rs)** | 583 | 0 | **583**(本报告主体,RA oracle 适用) |
| Python(.py) | 20 | 4 | 24(nexify v11 实际解析 .py;RA 不适用,见 §5) |
| 合计 | 603 | 4 | 607 |

- 4 条 AMBIGUOUS 全部在 .py(test_bench_opendataloader.py),且 target 为裸名(`_print_report`/`_arguments`),**无对应 node**——nexify 自己未绑定到具体定义,不构成误绑主张,仅记录。
- 583 条 .rs INFERRED resolved 的 target 全部是**真实存在的 `fn`/`impl fn`/`struct`(tuple 构造)定义锚点**(逐条校验:582 条 fn 类 target 的锚点行均含 `fn <label>`,1 条 struct)。
- UNRESOLVED(1,553 = 909 .rs + 644 .py)属 recall 范畴,不在本 pilot。

## 1. 总体结果(583 条 .rs INFERRED resolved)

| 判定 | 条数 | 占比 | 含义 |
|---|---:|---:|---|
| ✅ RA 精确命中 | 515 | 88.3% | RA def 位置 == nexify target (file,line),零偏差 |
| ✅ 静态核验通过 | 35 | 6.0% | 调用方在 feature/test 门控代码(RA 默认 cfg 不可判),人工核:调用名/限定路径/签名与 target def 一致 |
| ❌ 误绑 | 33 | 5.7% | 边指向的 def **不是**该调用点的真被调函数(实例/位置错) |

**误绑率口径**:
- 全量严格口径:**33/583 = 5.7%**(550/583 = 94.3% 指向真实被调函数)
- RA 可判定面(543 条 = 默认 cfg 507 + vision feature pass 8 + 同名归并错锚 28):命中 **515/543 = 94.8%**,误绑 **28/543 = 5.2%**
- **默认 feature 生产代码面(调用方不在 test / feature 门控里):误绑 0 条**,全部 507 条精确命中——不存在真"张冠李戴"式误绑
- 33 条误绑的**调用方代码 100% 位于测试代码或 feature 门控死代码**:
  - 28 条:调用点在 `#[cfg(test)]` 模块 / `tests/integration_tests.rs`
  - 5 条:调用点在 `#[cfg(feature = "python")]` 的 pyo3 wrapper(默认 feature 不编译)

### 误绑两形态

| 形态 | 条数 | 机制 | 严重度 |
|---|---:|---|---|
| **A. 同文件同名归并错锚**(MISB_COLLISION) | 28 | 同一文件内多个同名函数(嵌套在各自外层 fn 里的局部 helper / 不同 `#[cfg(test)]` mod 的同名 fn),nexify 节点按 `(file,label)` 归并成一个节点、锚定**首个**定义;其它实例的调用边全指向该锚 | 中:可见符号名正确,但 def 实例错(全在测试代码) |
| **B. 跨 crate 同名串扰**(MISB_CRATE) | 5 | python.rs(pyo3 wrapper,`crate::detect_pdf` 等)被解析到 **napi 子 crate**(napi/src/lib.rs)的同名 fn;真定义在同 crate(lib.rs/extractor/mod.rs) | 高:结构性不可能边(不同 crate 树),但全在 feature=python 死代码 |

**未发现**:符号名完全不同的误绑(0 条 DIFF_SYMBOL)、调用点行列定位偏差(0 条)、oracle 多候选冲突(0 条)、生产代码误绑(0 条)。

## 2. 形态 A 样本:同文件同名归并错锚(28 条,均在测试代码)

### 2a. `tests/integration_tests.rs` 局部 helper 同名(22 条)

模式:`fn add_object(...)` / `fn add_stream_object(...)` 作为**内嵌局部函数**重复出现在每个 `fn make_*_pdf` 外层 helper 里(第 21/88/162/1705/4124/4130 行…),彼此是不同函数实例。nexify 节点 `tests/integration_tests.rs::add_object` 锚定首个(21 行);但 22 条调用点的真被调实例分别是各自外层函数内的局部 fn(RA 判定 88/162/1705/4124 等行):

| 调用点 | nexify target | RA 真 target(实例) |
|---|---|---|
| tests/integration_tests.rs:95 `add_object(` | `add_object` @ :21 | `add_object` @ :88 |
| tests/integration_tests.rs:1727 `add_object(` | `add_object` @ :21 | `add_object` @ :1705 |
| tests/integration_tests.rs:4148 `add_object(` | `add_object` @ :21 | `add_object` @ :4124 |
| tests/integration_tests.rs:4176 `add_stream_object(` | `add_stream_object` @ :1711 | `add_stream_object` @ :4130 |

根因:节点粒度 = (file, label),无法表示同文件内同名多实例(含局部 fn);修复方向是节点标识纳入作用域/外层项信息,或对同文件同名去重时按调用点最近作用域锚定。

### 2b. `src/lib.rs` 不同 test mod 的同名 helper(6 条)

`fn item` 同时存在于 `#[cfg(all(test, feature="ocr"))] mod ocr_header_footer_tests`(:822, 测试 helper)与 `#[cfg(test)] mod text_cluster_column_undercount_tests`(:5425)。6 条调用点(5454-5530,真在后者 mod 内)被归并锚到 :822 的 item,RA 判定真定义 :5425。可见名正确、实例错,纯测试代码。

## 3. 形态 B 样本:跨 crate 同名串扰(5 条,`src/python.rs`,feature=python 死代码)

python.rs 是 pyo3 wrapper,调用均写作 `crate::detect_pdf` / `crate::extract_text` / `crate::extract_text_with_positions` / `crate::extract_pages_markdown` / `crate::extract_structure_elements`(crate 根限定,真定义必然在本 crate)。nexify 却把 target 解析到**独立子 crate** `napi/src/lib.rs` 的同名函数(两个 crate 互不依赖,此边结构性不可能):

| 调用点 | 调用上下文 | nexify target | 真 target(同 crate) |
|---|---|---|---|
| src/python.rs:800 | `let result = crate::detect_pdf(path)...` | `detect_pdf` @ napi/src/lib.rs:449 | `detect_pdf` @ src/lib.rs:277 |
| src/python.rs:836 | `crate::extract_text(path)...` | `extract_text` @ napi/src/lib.rs:469 | `extract_text` @ src/extractor/mod.rs:55 |
| src/python.rs:854 | `crate::extract_text_with_positions(path)...` | `extract_text_with_positions` @ napi/src/lib.rs:479 | `extract_text_with_positions` @ src/extractor/mod.rs:78 |
| src/python.rs:935 | `crate::extract_pages_markdown(...)` | `extract_pages_markdown` @ napi/src/lib.rs:894 | `extract_pages_markdown` @ src/lib.rs:883 |
| src/python.rs:976 | `crate::extract_structure_elements(...)` | `extract_structure_elements` @ napi/src/lib.rs:554 | `extract_structure_elements` @ src/lib.rs:957 |

佐证真 target:同批 python.rs 其它 14 条 wrapper 调用(如 `crate::process_pdf_with_options`→lib.rs:284、`crate::extractor::extract_text_mem`→extractor/mod.rs:62、`detect_pdf_mem`→lib.rs:304)nexify 均正确落在本 crate——说明解析器在"本 crate 同名目标存在"时仍可能被另一 crate 的同名 def 抢先匹配,属 name-based 解析的跨 crate 串扰缺陷(修复方向:限定路径 `crate::`/`use` 导入链优先,或按 crate 作用域过滤 target 候选)。

## 4. 正确例佐证(RA 精确命中抽样,9/9 位置一致)

| 调用点 | 调用名 | 形态 | nexify target == RA def |
|---|---|---|---|
| src/detector.rs:102 | `crate::validate_pdf_file(&path)` | crate 根限定 | src/lib.rs:6520 ✅ |
| src/extractor/content_stream.rs:184 | `build_font_encodings(...)` | 同 crate 跨模块(use 导入) | src/extractor/fonts.rs:717 ✅ |
| src/extractor/mod.rs:56 | `validate_pdf_file(&path)` | 同上 | src/lib.rs:6520 ✅ |
| src/extractor/reading_order.rs:53 | `text_utils::effective_width(item)` | 模块限定 | src/text_utils.rs:618 ✅ |
| src/lib.rs:209 | `Foo::default()` 类关联调用 | 关联函数/impl Default | src/detector.rs:81 ✅ |
| src/markdown/analysis.rs:210 | `roman_value(...)` | use 导入 | src/markdown/heading.rs:132 ✅ |
| src/markdown/furniture.rs:419 | `item.text()` | 方法调用(类型解析) | src/types.rs:160 ✅ |
| src/extractor/content_stream.rs:188 | `build_type3_scales(...)` | 同 crate 跨模块 | src/extractor/fonts.rs:151 ✅ |
| src/extractor/content_stream.rs:251 | `get_page_xobjects(doc, page_id)` | 同 crate 跨模块 | src/extractor/xobjects.rs:95 ✅ |

另:feature=vision pass 后 fusion.rs 8 条(跨文件限定/`::default()`/text_quality 模块 fn)全部精确命中,仅 1 条 target 本身 `#[cfg(feature="ocr")]`(complete_table_markdown_from_items)RA 不可判,静态核验通过。

## 5. Python 边(24 条)与 UNRESOLVED

- 20 条 py INFERRED resolved:RA 不适用(python 非 RA 语料),结构校验 20/20 通过(调用点 span == 目标名,目标锚点行含 `def <label>`);语义未深验(需 python oracle,超出本 pilot 范围)。其中 4 条 AMBIGUOUS 为裸名、无绑定节点。
- UNRESOLVED 1,553(909 .rs + 644 .py):recall 范畴,与 precision 无关;909 .rs 与既有报告一致(单 package crate 根路径 + 同 crate 模块级 re-export 漏绑为主)。

## 6. 结论

rust-analyzer 标准答案核验下,pdf-inspector 的 INFERRED resolved 边 **94.3%(550/583)指向真实被调函数,严格误绑率 5.7%(33/583)**;误绑全部集中于测试 helper 同名归并错锚(28,同文件同名多实例,可见符号正确)+ python wrapper 跨 crate 同名串扰(5,`crate::` 限定仍被 napi 子 crate 同名 fn 抢先);**默认 feature 生产代码面 0 误绑、0 定位偏差**,INFERRED 解析本身(类型推断选型)未发现任何指向错误符号的案例——两处缺陷均为节点/解析作用域缺陷而非推断错误。

## 附:环境与复现

- RA: `/home/keith/.rustup/toolchains/1.98.0-x86_64-unknown-linux-gnu/bin/rust-analyzer`,`--log-file` 以空格参数;初始化 `cachePriming.enable=false`;LSP 请求逐条 `textDocument/definition`,character 按 UTF-16 折算
- 核验脚本(中间产物,已清理):/tmp/gtruth/{ra_oracle.py, prep.py, label.py};结果 jsonl /tmp/gtruth/ra_results.jsonl(583 条)、ra_results_f1.jsonl(vision pass 9 条)
- 缓存只读:全程未写 pdf-inspector 的 .nexify;未改动源码树

## 附:全部 33 条误绑明细

| # | 调用点 (file:line) | 调用上下文 | nexify target | RA/静态 真 target | 错因 |
|---:|---|---|---|---|---|
| 1 | `src/lib.rs:5454` | items.push(item(x, y, "x")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 2 | `src/lib.rs:5511` | items.push(item(60.0, y, "x")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 3 | `src/lib.rs:5512` | items.push(item(330.0, y, "x")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 4 | `src/lib.rs:5515` | items.push(item(145.0, 100.0, "footnote")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 5 | `src/lib.rs:5516` | items.push(item(430.0, 50.0, "page#")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 6 | `src/lib.rs:5530` | items.push(item(x, y, "x")); | `item` @ src/lib.rs:822 | `src/lib.rs:5425` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 7 | `src/python.rs:800` | let result = crate::detect_pdf(path).map_err(to_py_err)?; | `detect_pdf` @ napi/src/lib.rs:449 | `src/lib.rs:277 (静态)` | `crate::detect_pdf` 真定义在同 crate,却绑到 napi 子 crate 同名 fn |
| 8 | `src/python.rs:836` | crate::extract_text(path).map_err(to_py_err) | `extract_text` @ napi/src/lib.rs:469 | `src/extractor/mod.rs:55 (静态)` | `crate::extract_text` 真定义在同 crate,却绑到 napi 子 crate 同名 fn |
| 9 | `src/python.rs:854` | None => crate::extract_text_with_positions(path).map_err(to_py_e | `extract_text_with_positions` @ napi/src/lib.rs:479 | `src/extractor/mod.rs:78 (静态)` | `crate::extract_text_with_positions` 真定义在同 crate,却绑到 napi 子 crate 同名 fn |
| 10 | `src/python.rs:935` | let result = crate::extract_pages_markdown(path, pages.as_deref( | `extract_pages_markdown` @ napi/src/lib.rs:894 | `src/lib.rs:883 (静态)` | `crate::extract_pages_markdown` 真定义在同 crate,却绑到 napi 子 crate 同名 fn |
| 11 | `src/python.rs:976` | let elements = crate::extract_structure_elements(path, pages.as_ | `extract_structure_elements` @ napi/src/lib.rs:554 | `src/lib.rs:957 (静态)` | `crate::extract_structure_elements` 真定义在同 crate,却绑到 napi 子 crate 同名 fn |
| 12 | `tests/integration_tests.rs:95` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:88` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 13 | `tests/integration_tests.rs:101` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:88` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 14 | `tests/integration_tests.rs:110` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:88` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 15 | `tests/integration_tests.rs:122` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:88` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 16 | `tests/integration_tests.rs:133` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:88` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 17 | `tests/integration_tests.rs:169` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 18 | `tests/integration_tests.rs:175` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 19 | `tests/integration_tests.rs:181` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 20 | `tests/integration_tests.rs:188` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 21 | `tests/integration_tests.rs:198` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 22 | `tests/integration_tests.rs:204` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 23 | `tests/integration_tests.rs:210` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:162` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 24 | `tests/integration_tests.rs:1727` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:1705` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 25 | `tests/integration_tests.rs:1733` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:1705` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 26 | `tests/integration_tests.rs:1739` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:1705` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 27 | `tests/integration_tests.rs:1779` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:1705` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 28 | `tests/integration_tests.rs:4148` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:4124` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 29 | `tests/integration_tests.rs:4154` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:4124` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 30 | `tests/integration_tests.rs:4160` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:4124` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 31 | `tests/integration_tests.rs:4176` | add_stream_object(&mut pdf, &mut offsets, 4, "", content.as_byte | `add_stream_object` @ tests/integration_tests.rs:1711 | `tests/integration_tests.rs:4130` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 32 | `tests/integration_tests.rs:4177` | add_object( | `add_object` @ tests/integration_tests.rs:21 | `tests/integration_tests.rs:4124` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |
| 33 | `tests/integration_tests.rs:4187` | add_stream_object( | `add_stream_object` @ tests/integration_tests.rs:1711 | `tests/integration_tests.rs:4130` | 同文件同名局部/模块 fn 归并,锚定首个定义(测试 helper) |

---

## 附:复现/工具(../groundtruth/ 可重跑管道,2026-09-02 重建并验证)

原临时脚本(/tmp/gtruth/*)已删,无法恢复。本报告全部数字已由 `~/clawspace/nexify-verify/groundtruth/` 下的
**3 步命令行管道**重建并逐一核对(见下"验证闭环"),管道可随时整体重跑。

### 交付物(全部在 `~/clawspace/nexify-verify/groundtruth/`)

| 文件 | 作用 |
|---|---|
| `step1_extract_edges.py` | 只读抽 nexify cache.db 的 .rs INFERRED resolved calls 边为 jsonl;join nodes 得 target 锚点 (tfile,tline,tlabel);顺带核验 583 条调用点 span 文本 == 目标函数名(--strict) |
| `step2_oracle_ra.py` | 单 RA 进程完整 LSP(initialize/initialized/Content-Length framing/workspace-config 应答/逐条 `textDocument/definition`/shutdown),对每条边 (efile,eline,ecol_s) 发 def;坐标:line 1-based→LSP 0-based、byte col→UTF-16 character;`--features vision` 二次 pass 只跑上一 pass 的空结果 |
| `step3_verdict_report.py` | 合并各 pass def 位置 vs nexify target 分类:MATCH / SAME_SYMBOL_DIFF_LOC(同文件同 label 不同行→归并错锚)/ DIFF_SYMBOL / ORACLE_EMPTY / RAW_FAIL;ORACLE_EMPTY 交静态 ledger;产出计数+33 条误绑明细表+误绑率+md 报告 |
| `statics/pdf_v11_static.json` | 静态核验 ledger:40 条 RA 不可判边的复核结论(35 OK + 5 误绑 B 及真 target),每条附理由 |
| `run.ts` | `bun run.ts pdf` 一步全跑;`bun run.ts smoke` 冒烟;`bun run.ts clean` 清 scratch（Bun TS,无 Makefile/Python 运行时依赖,跨平台） |

### 一条命令重跑整个 pdf 核验

```sh
bun ~/clawspace/nexify-verify/groundtruth/run.ts pdf
# => ../groundtruth/reports/GROUNDTRUTH-pdf-inspector-nexify-v11.md (约 20-30 s, 单进程)
```

等价手工三连(任一 corpus 复用):

```sh
cd ~/clawspace/nexify-verify/graphify-codewhale/groundtruth
python3 step1_extract_edges.py --db ~/clawspace/nexify-verify/pdf-inspector-snapshot/.nexify/cache.db \
    --root ~/clawspace/nexify-verify/pdf-inspector-build --out scratch/edges.jsonl --strict
python3 step2_oracle_ra.py --edges scratch/edges.jsonl --root ~/clawspace/nexify-verify/pdf-inspector-build \
    --ra ~/.rustup/toolchains/1.98.0-x86_64-unknown-linux-gnu/bin/rust-analyzer \
    --pass-name default --out scratch/ra_pass1.jsonl
python3 step2_oracle_ra.py --edges scratch/edges.jsonl --root ~/clawspace/nexify-verify/pdf-inspector-build \
    --ra ~/.rustup/toolchains/1.98.0-x86_64-unknown-linux-gnu/bin/rust-analyzer \
    --pass-name vision --features vision --prev scratch/ra_pass1.jsonl --out scratch/ra_pass2.jsonl
python3 step3_verdict_report.py --edges scratch/edges.jsonl \
    --oracle scratch/ra_pass1.jsonl scratch/ra_pass2.jsonl \
    --root ~/clawspace/nexify-verify/pdf-inspector-build --static statics/pdf_v11_static.json --out reports/out.md
```

### 验证闭环(重建即复现,非改脚本凑数)

管道从零重跑(scratch 全清)逐项对上本报告:

- step1:583 条 .rs INFERRED resolved calls,span==目标名 583/583,0 缺 node join
- step2 默认 feature pass:535 可判(507 精确命中 + 28 归并错锚)+ 48 空;`features=["vision"]` pass 复跑 48 空 → 8 条(fusion.rs)转精确命中、40 条仍空
- step3:MATCH **515**、静态通过 **35**、同 symbol 归并错锚 **28**、DIFF_SYMBOL 0、静态误绑 B **5**、RAW_FAIL 0;误绑率 **33/583 = 5.66%**;RA 可判定面 543:命中 515(94.8%)/误绑 28(5.2%);默认 feature 生产面误绑 **0**
- 33 条误绑明细逐条与报告表格核对(调用点 file:line、nexify target、RA/静态真 target 三列全等,无多余无遗漏);形态 A 的 RA 真实例行(88/162/1705/4124/4130/5425)与形态 B 真 target(lib.rs:277/883/957、extractor/mod.rs:55/78)一致

### 与原始过程的差异说明

- **RA 二进制**:原报告用的即
  `/home/keith/.rustup/toolchains/1.98.0-x86_64-unknown-linux-gnu/bin/rust-analyzer`(1.98.0,
  含 rust-src)。注意本机 `stable` toolchain 的 rust-analyzer 是 **1.96.0 且无 rust-src**,
  与本报告版本不同——重跑必须显式指向 1.98.0 路径。
- 一次性实现细节(不改变语义):`cachePriming.enable=false` 通过
  initializationOptions + didChangeConfiguration 注入;pass 2 范围=上一 pass 的全部空结果
  (pdf 语料下恰好只命中 fusion.rs 9 条中的 8 条,与当时 "fusion.rs 9 条" 一致)。
- 原"35 静态通过 + 5 误绑 B"中 40 条 RA 不可判边的复核结论已固化进
  `statics/pdf_v11_static.json`(可审计理由),后续重跑不再需要人工重复静态核验;
  换语料时此 ledger 需按 ORACLE_EMPTY 清单(报告 §3 自动列出)重新人工核验。
