test(evidence): 阶段2真实WXR冒烟通过

真实 hesupipe WXR(5,490,399B,SHA-256 与 Plan 1 基线一致)跑 wxr:probe 全链:
计数 18 项对照全过(rows 1047 / terms 451 / unsupported 75 细分一致 / failed 0 /
item 总数 1122);heapUsed max 62.2MiB < 100MiB(setInterval(100ms) 双 max 口径,
全程 167ms 仅 2 样本如实注明);parseMs 含流 IO 等待口径注明。
nicename 锚点核对:在表 taxonomy 706/706 全命中(nicename≡slug,违例 0);
category/post_tag 共 807 引用因 wp:term 表缺该 taxonomy 不可解析——阶段 3 需按
(domain, nicename) 自建缺失 term,不能假设 term 表完备。
eslint.config.js:scripts/*.mjs 显式 node 全局(no-undef 误报)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-21 13:25:09 +08:00
parent 75028e8a16
commit 21ef37232d
3 changed files with 518 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# 阶段 2 真实 WXR 冒烟证据(Plan 2 · Task 8)
**verdict:PASS**(计数对照 18 项全过 + 内存断言通过 + nicename 锚点核对完成)
| 项 | 值 |
|---|---|
| 日期 | 2026-10-21 |
| 环境 | Windows 11 · Node v24.13.0 · pnpm 10.7.1 · tsx 直吃 TS 源码(与 sf CLI 同机制) |
| 输入 | `D:/project/cms/storage/wxr/fd04d6ba8a1316b6.xml`(5,490,399 B ≈ 5.5MB,绝对只读) |
| 输入完整性 | SHA-256 `f9bc5d4497a1792af37abff78e0096268bda975cb367a956da42bcf9552b9f62` 与 Plan 1 基线一致——文件自 Task 0.4 起未变 |
| 冒烟脚本 | `scripts/smoke-real-wxr.mjs`(probe 全链 + setInterval(100ms) 内存采样 + 基线断言 + nicename 核对) |
| 证据 run | `artifacts/probe/20261021T132153-1yft49/`(smoke.json / report.json / report.html) |
| CLI run | `artifacts/probe/20261021T132302-26azne/`(report.json / report.html) |
## 实际执行的命令原文
```bash
# 冒烟主路径(仓库根 D:\project\cms-one):probe 同链路 + 内存采样 + 基线断言 + nicename 核对,stdout 出 JSON
pnpm exec tsx scripts/smoke-real-wxr.mjs # 缺省 --wxr 即 hesupipe 文件;exit 0 = PASS
# 计划钉死的 CLI 命令(Step 1 原文,只读诊断通道直读 cms)——计数与冒烟脚本一致
pnpm sf wxr:probe --wxr "D:/project/cms/storage/wxr/fd04d6ba8a1316b6.xml"
```
注:smoke 脚本为 .mjs,但 `@sf/adapter-wp` 经根 tsconfig paths 直连 packages 的 TS 源码,故须 tsx 运行(纯 node 无法解析 workspace TS 包);与 `scripts/sf.ts` 的 CLI 执行机制(Plan 2 评审钉死)相同。
## Step 1 计数对照(实际 vs 基线,逐项)
基线:`docs/evidence/real-wxr-baseline.md`。断言规则:**任何一项不一致都算 FAIL**。以下 18 项全部 `ok`(smoke.json `checks` 数组原样可查):
| 对照项 | 基线 | 实际 | 结果 |
|---|---|---|---|
| 输入字节数 | 5,490,399 | 5,490,399 | ok |
| 输入 SHA-256 | f9bc5d44…b9f62 | f9bc5d44…b9f62 | ok |
| totals.rows | 1,047(855+129+48+15) | 1,047 | ok |
| totals.terms | 451 | 451 | ok |
| totals.unsupported | 75 | 75 | ok |
| totals.failed | 0 | 0 | ok |
| item 总数(rows+unsupported+failed) | 1,122 | 1,122 | ok |
| rows `wp:attachment` | 855 | 855 | ok |
| rows `wp:post` | 129 | 129 | ok |
| rows `wp:product` | 48 | 48 | ok |
| rows `wp:page` | 15 | 15 | ok |
| unsupported `wp:nav_menu_item` | 68 | 68 | ok |
| unsupported `wp:wpcf7_contact_form` | 3 | 3 | ok |
| unsupported `wp:elementor_library` | 2 | 2 | ok |
| unsupported `wp:wp_navigation` | 2 | 2 | ok |
| counts 桶键集合(无多余/缺失桶) | 上述 8 键 | 完全一致 | ok |
| probe.fatalError | null | null | ok |
| 内存 heapUsed max | < 100MB | 65,268,760 B(62.2MiB) | ok |
计数细分(created=清洗有变化将产新内容 / skipped=无变化,导入前投影语义):
| sourceType | created | skipped | unsupported | 基线口径 |
|---|---|---|---|---|
| wp:attachment | 0 | 855 | 0 | 855 rows |
| wp:post | 87 | 42 | 0 | 129 rows |
| wp:product | 47 | 1 | 0 | 48 rows |
| wp:page | 4 | 11 | 0 | 15 rows |
| wp:nav_menu_item | – | – | 68 | 68 unsupported |
| wp:wpcf7_contact_form | – | – | 3 | 3 unsupported |
| wp:elementor_library | – | – | 2 | 2 unsupported |
| wp:wp_navigation | – | – | 2 | 2 unsupported |
清洗统计(背景数据,非对照项):changedRows=138(post 87 + product 47 + page 4;attachment 无正文零清洗全 skipped);removed-tag=3、removed-attr=4,667;rowErrors 固化 4,793 条(上限 10,000,未触顶 truncated=0)。
## Step 2 内存与耗时
**内存(计划钉死口径:smoke 脚本内 `setInterval(100ms)` 采 `process.memoryUsage()` 的 heapUsed/rss 双 max)**
| 指标 | 值 |
|---|---|
| heapUsed max | **65,268,760 B = 62.2MiB < 100MiB(断言通过)** |
| rss max | 175,218,688 B = 167.1MiB(进程整体常驻,含 node+tsx 运行时基线,仅记录不断言) |
| 采样样本数 | 2(见下方口径说明) |
口径说明(如实记录):
- 采样的是**整个进程**的 heapUsed/rss——含 tsx 加载器与模块基线,非解析增量;断言语义是"流式解析 5.5MB 文件时进程堆峰值仍 < 100MB",通过即证明无整文件缓冲(整读 5.5MB 原文 + 解析树必然显著更高,且会随文件增长线性突破)。
- probe 全程仅 ~167ms(见下),100ms 间隔只落到 2 个样本(1 个 interval tick + 终末补采 1 次)。max 以**实测下界**口径记录;考虑解析器 per-item yield、不 buffer item 列表的结构保证(Task 3 Step 2 钉死),两样本已覆盖解析爬坡段与收尾段,实测 62.2MiB 与 100MiB 线余量充足。
**耗时(smoke run / CLI run 各一次,量级一致)**
| 指标 | smoke run(1yft49) | CLI run(26azne) |
|---|---|---|
| parseMs | 73.6ms | 79.2ms |
| sanitizeMs | 81.7ms | 79.2ms |
| totalMs | 166.9ms | 169.5ms |
**parseMs 口径(Task 6 移交,务必连同数字一起引用)**:parseMs 是 `parser.next()` await 窗口累计——**含流式 IO 等待**(磁盘读、backpressure),非纯 CPU 解析耗时;totalMs 覆盖整个消费循环(含映射与报告聚合);totalMs ≥ parseMs+sanitizeMs 的差值即映射/聚合时间。
## nicename 锚点核对(Task 4 冻结点)
**核对方法**:第二遍独立流式解析(同一 parseWxrStream),全量收集 wp:term 表(taxonomy → slug 集)与 item 引用(domain → nicename 计数),流结束后交叉验证;抽检规则=文档序前 10 个含 `domain="category"` 引用的 item(该类 item 共 129 个)。
**抽检 10 条明细**(全部未解析到 term slug,原因见下):
| rawLocation | postId | post_type | category nicename(value) | ∈ wp:term slug 集 |
|---|---|---|---|---|
| wxr://item/941 | 1646 | post | company-news(Company News) | 否 |
| wxr://item/942 | 2281 | post | trade-news(Trade News) | 否 |
| wxr://item/943 | 2549 | post | progect(Progects) | 否 |
| wxr://item/944 | 2833 | post | progect(Progects) | 否 |
| wxr://item/945 | 3121 | post | progect(Progects) | 否 |
| wxr://item/946 | 3124 | post | progect(Progects) | 否 |
| wxr://item/947 | 3127 | post | progect(Progects) | 否 |
| wxr://item/948 | 3130 | post | progect(Progects) | 否 |
| wxr://item/949 | 3138 | post | progect(Progects) | 否 |
| wxr://item/950 | 3143 | post | progect(Progects) | 否 |
**抽检一致率(category nicename 可解析到 wp:term slug):0/10。根因不是 nicename≠slug,而是该 WXR 的 wp:term 表根本没有 category/post_tag 条目**。全量分层核对(grep 级独立复核结论一致):
| domain(item 引用数) | 可解析 | wp:term 表中有该 taxonomy? | 结论 |
|---|---|---|---|
| product_tag(588) | 588/588 = 100% | 有(439 term) | nicename ≡ term_slug |
| product_cat(49) | 49/49 = 100% | 有(10 term) | nicename ≡ term_slug |
| nav_menu(68) | 68/68 = 100% | 有(1 term:mainmenu) | nicename ≡ term_slug |
| elementor_library_type(1) | 1/1 = 100% | 有(1 term) | nicename ≡ term_slug |
| category(148 引用,5 个不同 nicename:trade-news 59 / progect 29 / news 28 / company-news 20 / notification 12) | 0/148 | **无(0 term)** | taxonomy 未导出,锚点无从对拍 |
| post_tag(659 引用,471 个不同 nicename) | 0/659 | **无(0 term)** | taxonomy 未导出,锚点无从对拍 |
汇总:**wp:term 表内 taxonomy 的全部 706 条引用 100% 命中 slug,nicename≠slug 违例 0 条**;category/post_tag 共 807 条引用因 term 表缺该 taxonomy 不可解析。(wp:term 总数 451 = product_tag 439 + product_cat 10 + nav_menu 1 + elementor_library_type 1,与 totals.terms 一致。)
**对阶段 3 taxonomyRefs 映射策略的结论**:
1. Task 4 的锚点设计(`sourceTermId` = item 内 @nicename)被实数据**正向验证**:凡 term 表覆盖的 taxonomy,nicename 与 term_slug 完全等同(706/706),"真实 WP 中 category @nicename 即 term slug"的假设在本文件所有可对拍域上成立;
2. 但**不能假设 wp:term 表完备**:本 WXR 未导出 category/post_tag 的 term 条目(WordPress 7.1.3 导出行为,含 nav_menu/elementor/product 域却缺这两个常规域)。阶段 3 做 slug → term_id 解析时,category(148 引用/5 nicename)与 post_tag(659 引用/471 nicename)将全部 miss——需按 (domain, nicename) 自建缺失 term(value 可作 name),不能只依赖 wp:term 事件;
3. 数据怪癖原样保留:`progect`(源站拼写如此,29 处引用)按原文迁移不修正。
## 报告产物
| 产物 | 路径 |
|---|---|
| 冒烟结果(断言/内存/nicename 全量数据,JSON) | `artifacts/probe/20261021T132153-1yft49/smoke.json` |
| probe 报告 JSON(smoke run) | `artifacts/probe/20261021T132153-1yft49/report.json` |
| probe 报告 HTML(smoke run) | `artifacts/probe/20261021T132153-1yft49/report.html` |
| probe 报告 JSON/HTML(CLI run) | `artifacts/probe/20261021T132302-26azne/report.{json,html}` |
(artifacts/ 不入库(.gitignore),产物仅存本地;smoke.json 的 `checks` 数组即上表 18 项断言的机器可读原貌。)
## 只读声明
本次冒烟对 `D:/project/cms/storage/wxr/fd04d6ba8a1316b6.xml` 仅执行读取操作(stat、sha256 流式读、两遍 createReadStream 解析读),未修改该文件及 `D:\project\cms` 下任何内容;全部写入仅发生在本仓库 `artifacts/probe/` 与上述两个新建文件。