Pārlūkot izejas kodu

用户令 #3: 非产物移出产物仓 + 人工件台账(不再按自动产物参与呼应校验)

① 非产物移出产物仓
   17 件"躺在产物仓里的杂物"原样移到 `reference/非产物留痕_20260917/` 并附 `_来源说明.json`:
   模型试跑输出(ontology/*_test_*.out/.err, ask_last.out, fast_8b_test.out, sop bench30b_*.out)、
   自审脚本与记录(windscada/_audit.js, _audit_all.js, _design_audit_*.md, _selfaudit_*.md)。
   移前逐条核过**代码引用**: 三个 grep 命中都是 llm_audit.jsonl / terms_audit.json 的子串误报
   ⇒ 确认无代码读取才动。被运行期以"证据/标签"引用的件**不动**(见下)。

② 台账维护口 `products_restore_missing.py --refresh`(不需要任何随包件/交付包)
   只做"账实相符": 盘上已不存在的条目从 `_provenance.json` 去掉, 自登记件在位则补入。
   实测: 台账 2,292 → **2,236** 条(移出 17 + 之前落在 logs/build 的 39 条日志条目等),
   分布 raw-derived=1725 · shipped=511; 反向审计随之"台账 2236 件 = 判定 2236 件"(此前账实不符)。

③ 分类修正(我上一轮的"非产物"判错了两件, 本轮按代码引用纠正)
   `windscada/release-manifest.json` **有生成端**(`scripts/guanlan_baseline_manifest.py` 就写这个路径)
   且 `guanlan_gateway.py` 运行期读它 ⇒ 是产物, 归新族 `windscada_release_manifest`
   (kind=source-derived: 输入是包内源码 src/windscada/ui), 反向判定 **✓ 呼应成立**。
   `mblub_alarm_cross.json`/`review_presented.json`/`给振动线_温度轴回复_*`/`送审_*` 5 件被运行期当
   证据引用(mblub 那件被 src/ontology/populate.py 引为 F9 机制的证据来源) ⇒ 新族 `human_deliverables`
   (kind=human): **人工正本/交证件, 随包留原位, 不参与呼应校验**。

④ 人工件台账 `outputs/<场>/_human_artifacts.json`(用户令 #3 的核心)
   新增 `--write-human-manifest`: **逐件**判"是不是人工件"(非测量形态件里按**字段名**匹配
   人工/裁决/审核/校准/评审/judge/review/calibrat/verdict), 落成机器台账。
   反向审计据此把人工件单独计 ◆, **不再算作呼应失败**。
   实测: ◆ **183 件**(sop 100 · 振动线出件 26 · paradigm_r1 19 · tcm 回放 13 · windscada 页面 6 ·
   windcms 6 · 交证件 5 · 契约 4 · 本体发布 3 · 领域 1);
   判定变为 ✓1742 · ✗**311**(原 512) · ◆183 · 无法验证 0 · 未归类 0。
   剩余 311 件的可逆性: 页面可再生 46(windcms) · 混合(测量可反推/文本需人定) 45(振动线出件) ·
   半可逆 104(sop) · 不可逆 99 · 需人工裁定 15 · 需反推口径 2(pitch)。

自检: 反向呼应一行 OK(含 ◆183);页面归口/日志统一/呼应校验均 OK, 仅剩本机未装 Ollama 的预期 FAIL。
docs §13.6 自动块同步(新增人工件段 + 逐族可逆性), 变更记录待合并提交。
zhouyang.xie 3 nedēļas atpakaļ
vecāks
revīzija
e0324bae4d
22 mainītis faili ar 728 papildinājumiem un 41 dzēšanām
  1. 25 24
      docs/系统设计说明.md
  2. 2 2
      guanlan.py
  3. 94 0
      reference/非产物留痕_20260917/_来源说明.json
  4. 4 0
      reference/非产物留痕_20260917/ontology/ask_last.out
  5. 11 0
      reference/非产物留痕_20260917/ontology/fast_8b_test.out
  6. 0 0
      reference/非产物留痕_20260917/ontology/p2_test_deepseek.err
  7. 49 0
      reference/非产物留痕_20260917/ontology/p2_test_deepseek.out
  8. 0 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen27b.err
  9. 31 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen27b.out
  10. 0 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen30b.err
  11. 37 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen30b.out
  12. 0 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen8b.err
  13. 41 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen8b.out
  14. 37 0
      reference/非产物留痕_20260917/ontology/p2_test_qwen8b_fast.out
  15. 16 0
      reference/非产物留痕_20260917/sop/local_review/bench30b_R6a.out
  16. 16 0
      reference/非产物留痕_20260917/sop/local_review/bench30b_R6b.out
  17. 109 0
      reference/非产物留痕_20260917/windscada/_audit.js
  18. 12 0
      reference/非产物留痕_20260917/windscada/_audit_all.js
  19. 39 0
      reference/非产物留痕_20260917/windscada/_design_audit_20260826.md
  20. 37 0
      reference/非产物留痕_20260917/windscada/_selfaudit_20260825.md
  21. 55 0
      scripts/products_restore_missing.py
  22. 113 15
      scripts/products_reverse_audit.py

+ 25 - 24
docs/系统设计说明.md

@@ -1101,7 +1101,7 @@ scripts/version_log.py             ← 由 HISTORY 生成 docs/版本记录.md
 <!-- REVERSE-AUDIT:BEGIN -->
 ### 13.6 反向呼应审计:自输出 → 功能与算法 → 输入(自动生成,勿手改)
 
-台账 2253 件产物逐件回溯:**呼应成立 1741 件** · **不成立(无生成端)512 件** · 无法验证 0 件 · 未归类 0 件。判据:① 生成端在位 ② 输入在位 ③ 跨度 ⊆ 输入 / 机组集 ⊆ 输入 / 行数 > 0 / 计数与清单一致。
+台账 2236 件产物逐件回溯:**呼应成立 1742 件** · **不成立(无生成端)311 件** · 无法验证 0 件 · 未归类 0 件。判据:① 生成端在位 ② 输入在位 ③ 跨度 ⊆ 输入 / 机组集 ⊆ 输入 / 行数 > 0 / 计数与清单一致。
 
 | 输出族(产物 glob) | 件数 | 反向判定 | 功能 | 算法 / 生成端 | 输入(data/raw/<场>/) | 判据 | 说明 |
 |---|---:|---|---|---|---|---|---|
@@ -1115,19 +1115,19 @@ scripts/version_log.py             ← 由 HISTORY 生成 docs/版本记录.md
 | `windscada/*.parquet` | 27 | ✓ 呼应成立 | SCADA 派生分析(功率曲线/损失/曲线/控制/停机/温度/偏航/液压/热链/系统辅助) | src/windscada/perf/{powercurve,availability,curves,control,faults} + subsys/{temp_nbm,yaw,hydraulic,thermal_chain} + taxonomy.py | scada_10min | rows | 件内数据行合计 70,283 |
 | `ontology/{objects.json,retrieval_index.json,turbine_params.parquet}` | 3 | ✓ 呼应成立 | 本体层 · 码表/对象库/检索索引/实机参数 | python -m src.ontology.kb_ingest → populate → chain_ingest → trend_ingest;src.ontology.retrieval.build;src.ontology.maintenance.refresh_params | 西门子4.0技术资料 | rows | 件内数据行合计 1,708 |
 | `['ontology/_id_alias.json', 'ontology/retrieval_vec.npy']` | 2 | ✓ 呼应成立 | 本体层 · 别名表与向量索引缓存 | src.ontology.kb_ingest(别名)/ src.ontology.retrieval.build(use_vec=True)(向量) | 西门子4.0技术资料 | rows | 件内数据行合计 2 |
-| `['ontology/oil_2026H2_huabiao.json', 'ontology/parts_cost_public.json', 'ontology/scenario_29_repair.json']` | 3 | ✗ 无生成端 | 本体层 · 领域数据件(油样台账/备件价格/场景修复) | (无生成端, 领域正本出件) | —(无生成端) | — | 领域正本随包发来, 不由 data/raw 推导 |
-| `['windscada/mblub_alarm_cross.json', 'windscada/release-manifest.json', 'windscada/review_presented.json', 'windscada/给振动线_温度轴回复_20260826.json', 'windscada/给振动线_温度轴回复_20260826.md', 'windscada/送审_系统结论与判定_20260828.md']` | 6 | ✗ 非产物 | **非产物**:交证/评审往来件(送审结论、给振动线的回复、发布清单快照) | (沟通与评审留痕, 不是产物) | —(无生成端) | — | 这些是"人对人的交证件", 内容由分析结论抄写而成, 不随 data/raw 变; 放在产物仓里会让"产物"这个词失去意义(它们既无生成端, 也无重算入口) |
-| `m5_cms_tcm/*.{json,parquet,csv,md,txt}` | 71 | ✗ 无生成端 | 振动线出件与专项扫描(handoff / 历史 / 基线 / 各类 freq scan / 判级台账) | (无生成端) | —(无生成端) | — | 振动六层链四步脚本未随包(oem_frequency_scan 等),现场正本才可重出;现只能从交付包补齐 |
+| `['ontology/oil_2026H2_huabiao.json', 'ontology/parts_cost_public.json', 'ontology/scenario_29_repair.json']` | 2 | ✗ 无生成端 + ◆ 人工件 1 | 本体层 · 领域数据件(油样台账/备件价格/场景修复) | (无生成端, 领域正本出件) | —(无生成端) | — | 领域正本随包发来, 不由 data/raw 推导 |
+| `m5_cms_tcm/*.{json,parquet,csv,md,txt}` | 45 | ✗ 无生成端 + ◆ 人工件 26 | 振动线出件与专项扫描(handoff / 历史 / 基线 / 各类 freq scan / 判级台账) | (无生成端) | —(无生成端) | — | 振动六层链四步脚本未随包(oem_frequency_scan 等),现场正本才可重出;现只能从交付包补齐 |
 | `m5_cms_tcm/figs/*` | 15 | ✗ 无生成端 | 振动线出图(谱图/趋势图) | (无生成端) | —(无生成端) | — | 随包快照(振动线出件) |
-| `windscada/{index.html,turbines/*,review/*}` | 47 | ✗ 无生成端 | 总览「全场状态」静态页 + 逐台页 + 评审记录 | (无生成端) | —(无生成端) | — | 页面件由振动线/历史分析出件,本包无写入方 |
-| `windcms/**` | 52 | ✗ 无生成端 | CMS 振动评估报告页/逐台页/缓存 | scripts/windcms.py report(本包缺 model_run/fusion 产物, 重生成会掉内容 −97%, 故**按设计跳过**) | —(无生成端) | — | 生成端在包内但输入不足 ⇒ 现状按随包件冻结 |
-| `sop/**` | 204 | ✗ 无生成端 | SOP 中间件/评审/事实契约底稿 | (无生成端) | —(无生成端) | — | 评审过程件, 属人工作业留痕 |
-| `tcm_compatible_replay/**` | 58 | ✗ 无生成端 | TCM 兼容链回放资产(模型表/掩码阈值/裁决记录) | (无生成端) | —(无生成端) | — | 随包快照 |
-| `paradigm_r1/**` | 29 | ✗ 无生成端 | 范式实验件(E3/E5/E8 底稿) | (无生成端) | —(无生成端) | — | 实验底稿, 事实契约的输入 |
+| `windscada/{index.html,turbines/*,review/*}` | 41 | ✗ 无生成端 + ◆ 人工件 6 | 总览「全场状态」静态页 + 逐台页 + 评审记录 | (无生成端) | —(无生成端) | — | 页面件由振动线/历史分析出件,本包无写入方 |
+| `windcms/**` | 46 | ✗ 无生成端 + ◆ 人工件 6 | CMS 振动评估报告页/逐台页/缓存 | scripts/windcms.py report(本包缺 model_run/fusion 产物, 重生成会掉内容 −97%, 故**按设计跳过**) | —(无生成端) | — | 生成端在包内但输入不足 ⇒ 现状按随包件冻结 |
+| `sop/**` | 102 | ✗ 无生成端 + ◆ 人工件 100 | SOP 中间件/评审/事实契约底稿 | (无生成端) | —(无生成端) | — | 评审过程件, 属人工作业留痕 |
+| `tcm_compatible_replay/**` | 45 | ✗ 无生成端 + ◆ 人工件 13 | TCM 兼容链回放资产(模型表/掩码阈值/裁决记录) | (无生成端) | —(无生成端) | — | 随包快照 |
+| `paradigm_r1/**` | 10 | ✗ 无生成端 + ◆ 人工件 19 | 范式实验件(E3/E5/E8 底稿) | (无生成端) | —(无生成端) | — | 实验底稿, 事实契约的输入 |
 | `pitch/**` | 2 | ✗ 无生成端 | 变桨侧派生件(零位/日粒度) | (无生成端;rebuild_from_raw --scada 只覆盖其中一部分) | —(无生成端) | — | 随包快照 |
-| `ontology/release_*/*` | 5 | ✗ 无生成端 | 本体发布层 r1/r2(只读暴露给网关 /release/) | (无生成端) | —(无生成端) | — | 发布层清单, 由研发出件 |
-| `guanlan/**` | 5 | ✗ 无生成端 | 事实契约与对外派生(门户结论段/取数) | scripts/guanlan_facts_contract.py(读者在包内, 输入是 ontology+m5+sop 三处**产物**) | —(无生成端) | — | 派生链的中间层:它的"输入"本身是产物而非 data/raw ⇒ 与 raw 之间隔着不止一跳 |
-| `['**/_*.js', '**/_*audit*.md', '**/*.out', '**/*.err', '**/*_test_*.parquet', '**/*_test_*.json']` | 15 | ✗ 非产物 | **非产物**:自审脚本/记录与模型测试输出(躺在产物仓里) | (工具脚本与运行痕迹, 不是产物) | —(无生成端) | — | 清单里既有审计脚本(`windscada/_audit*.js`)与自审记录(`_*audit*.md`), 也有模型试跑输出(`ontology/*_test_*.out/.err`);它们随包发过来、不参与任何取数, 属"产物仓里的杂物" |
+| `ontology/release_*/*` | 2 | ✗ 无生成端 + ◆ 人工件 3 | 本体发布层 r1/r2(只读暴露给网关 /release/) | (无生成端) | —(无生成端) | — | 发布层清单, 由研发出件 |
+| `guanlan/**` | 1 | ✗ 无生成端 + ◆ 人工件 4 | 事实契约与对外派生(门户结论段/取数) | scripts/guanlan_facts_contract.py(读者在包内, 输入是 ontology+m5+sop 三处**产物**) | —(无生成端) | — | 派生链的中间层:它的"输入"本身是产物而非 data/raw ⇒ 与 raw 之间隔着不止一跳 |
+| `windscada/release-manifest.json` | 1 | ✓ 呼应成立 | 发布清单快照(`/healthz` 与版本信息读它) | scripts/guanlan_baseline_manifest.py(从 src/windscada/ui 源重建 zh 页 → 摘要 {css_tokens, nav, dom, sha256, source blobs}) | —(无生成端) | — |  |
+| `['windscada/mblub_alarm_cross.json', 'windscada/review_presented.json', 'windscada/给振动线_温度轴回复_20260826.json', 'windscada/给振动线_温度轴回复_20260826.md', 'windscada/送审_系统结论与判定_20260828.md']` | 5 | ◆ 人工件 | **人工正本/交证件**:与振动线的跨线通知、送审结论、发布评审快照 | (人写的, 不是算出来的) | —(无生成端) | — | 这几件是"人对人的交证件/回执", 内容由分析结论抄写与人判断坐实而成; mblub_alarm_cross.json 还被 src/ontology/populate.py 当作**证据来源**引用 ⇒ 必须随包留在原位, 但**不参与呼应校验**(它本来就没有生成端, 也不该有) |
 
 #### 反向「可逆性」:没有呼应关系的那批,能否从原始件推出来(用户令 2026-09-17 #2)
 
@@ -1135,21 +1135,22 @@ scripts/version_log.py             ← 由 HISTORY 生成 docs/版本记录.md
 
 | 输出族 | 件数 | 可逆性 | 依据 |
 |---|---:|---|---|
-| `ontology_domain` | 3 | 半可逆(需人工裁定) | 抽样里 33% 的件含人工判断字段 ⇒ 机器部分可推、判断部分要人定;人工判断字段抽样命中 33%(如 ontology/parts_cost_public.json) |
-| `windscada_delivery_notes` | 6 | 应移出产物仓 | 它本就不是产物(工具脚本/交证件/测试输出) ⇒ 该从产物仓移走, 而不是"补生成端" |
-| `vib_handoff_and_scans` | 71 | 混合(测量件可反推 / 文本件需人定) | (23 件测量形态 / 48 件文本·判断件)测量形态的件可由 data/raw 反推口径 + 逐值对拍; 文本/判断件(评审、裁决、回复)推不出来;人工判断字段抽样命中 33%(如 m5_cms_tcm/OEM特征频率复跑_2026-08-17.md) |
+| `ontology_domain` | 2 | 半可逆(需人工裁定) | 抽样里 33% 的件含人工判断字段 ⇒ 机器部分可推、判断部分要人定;人工判断字段抽样命中 33%(如 ontology/parts_cost_public.json) |
+| `vib_handoff_and_scans` | 45 | 混合(测量件可反推 / 文本件需人定) | (23 件测量形态 / 22 件文本·判断件)测量形态的件可由 data/raw 反推口径 + 逐值对拍; 文本/判断件(评审、裁决、回复)推不出来;人工判断字段抽样命中 33%(如 m5_cms_tcm/OEM特征频率复跑_2026-08-17.md) |
 | `vib_figs` | 15 | 需人工裁定 | 既无生成端、形态也不是测量函数 ⇒ 需研发给口径或领域正本 |
-| `windscada_pages` | 47 | 不可逆(人工判断) | 抽样 6 件里都写着人工判断字段(如 windscada/review/_sealed_predictions_20260826.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 85%(如 windscada/review/_sealed_predictions_20260826.md) |
-| `windcms_pages` | 52 | 可逆(页面可再生) | 这一族主要是**页面**(.html): 页面是产物的渲染, 不是独立数据 ⇒ 接上在包的页面生成端重出即可(未必逐字节同, 能力与数据一致);人工判断字段抽样命中 33%(如 windcms/kb.json) |
-| `sop_workspace` | 204 | 半可逆(需人工裁定) | 抽样里 37% 的件含人工判断字段 ⇒ 机器部分可推、判断部分要人定;人工判断字段抽样命中 37%(如 sop/_dual_review_20260726_payload脱敏.md) |
-| `tcm_replay` | 58 | 不可逆(人工判断) | 抽样 3 件里都写着人工判断字段(如 tcm_compatible_replay/claude_handoff_20260823/Codex回复_rms200_mask_20260823.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 75%(如 tcm_compatible_replay/claude_handoff_20260823/Codex回复_rms200_mask_20260823.md) |
-| `paradigm_r1` | 29 | 不可逆(人工判断) | 抽样 5 件里都写着人工判断字段(如 paradigm_r1/experiments/E3_candidate_closure/plan.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 100%(如 paradigm_r1/experiments/E3_candidate_closure/plan.md) |
+| `windscada_pages` | 41 | 不可逆(人工判断) | 抽样 6 件里都写着人工判断字段(如 windscada/review/_sealed_predictions_20260826.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 85%(如 windscada/review/_sealed_predictions_20260826.md) |
+| `windcms_pages` | 46 | 可逆(页面可再生) | 这一族主要是**页面**(.html): 页面是产物的渲染, 不是独立数据 ⇒ 接上在包的页面生成端重出即可(未必逐字节同, 能力与数据一致);人工判断字段抽样命中 33%(如 windcms/kb.json) |
+| `sop_workspace` | 102 | 半可逆(需人工裁定) | 抽样里 37% 的件含人工判断字段 ⇒ 机器部分可推、判断部分要人定;人工判断字段抽样命中 37%(如 sop/_dual_review_20260726_payload脱敏.md) |
+| `tcm_replay` | 45 | 不可逆(人工判断) | 抽样 3 件里都写着人工判断字段(如 tcm_compatible_replay/claude_handoff_20260823/Codex回复_rms200_mask_20260823.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 75%(如 tcm_compatible_replay/claude_handoff_20260823/Codex回复_rms200_mask_20260823.md) |
+| `paradigm_r1` | 10 | 不可逆(人工判断) | 抽样 5 件里都写着人工判断字段(如 paradigm_r1/experiments/E3_candidate_closure/plan.md) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 100%(如 paradigm_r1/experiments/E3_candidate_closure/plan.md) |
 | `pitch_shipped` | 2 | 可逆(需反推口径) | 测量形态的件是测量数据的函数 ⇒ 可从 data/raw 反推口径 + 逐值对拍(做法同 temp_monthly: 反推 → 逐值一致才敢用) |
-| `ontology_releases` | 5 | 不可逆(人工判断) | 抽样 3 件里都写着人工判断字段(如 ontology/release_r1/manifest.json) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 60%(如 ontology/release_r1/manifest.json) |
-| `guanlan_contract` | 5 | 不可逆(人工判断) | 抽样 4 件里都写着人工判断字段(如 guanlan/derived/detail_cards.json) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 80%(如 guanlan/derived/detail_cards.json) |
-| `scratch_in_products` | 15 | 应移出产物仓 | 它本就不是产物(工具脚本/交证件/测试输出) ⇒ 该从产物仓移走, 而不是"补生成端" |
+| `ontology_releases` | 2 | 不可逆(人工判断) | 抽样 3 件里都写着人工判断字段(如 ontology/release_r1/manifest.json) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 60%(如 ontology/release_r1/manifest.json) |
+| `guanlan_contract` | 1 | 不可逆(人工判断) | 抽样 4 件里都写着人工判断字段(如 guanlan/derived/detail_cards.json) ⇒ 不是输入的纯函数, 逆向工程推不出来;人工判断字段抽样命中 80%(如 guanlan/derived/detail_cards.json) |
+| `human_deliverables` | 5 | 人工件(正本/交证) | 人写的, 不参与呼应校验; 随包留在原位 |
 
-**按件数汇总**:已在重算链上 = 1741 件 · 半可逆(需人工裁定) = 207 件 · 不可逆(人工判断) = 144 件 · 混合(测量件可反推 / 文本件需人定) = 71 件 · 可逆(页面可再生) = 52 件 · 应移出产物仓 = 21 件 · 需人工裁定 = 15 件 · 可逆(需反推口径) = 2 件
+**按件数汇总**:已在重算链上 = 1742 件 · 半可逆(需人工裁定) = 104 件 · 不可逆(人工判断) = 99 件 · 可逆(页面可再生) = 46 件 · 混合(测量件可反推 / 文本件需人定) = 45 件 · 需人工裁定 = 15 件 · 人工件(正本/交证) = 5 件 · 可逆(需反推口径) = 2 件
+
+**人工件**:含人工判断字段的件已逐件登记在 outputs/&lt;场&gt;/_human_artifacts.json(用户令 2026-09-17 #3),它们**不参与**呼应校验(人写成/坐实的东西不是任何输入的纯函数);生成方式:python scripts/products_reverse_audit.py --write-human-manifest。
 
 > 结论口径:`可逆(直接)` = 包内已有生成端,接上即可;`可逆(需反推口径)` = 内容是测量数据的函数,照 `temp_monthly` 的办法反推 + 逐值对拍;`不可逆(人工判断)` = 逆向工程推不出来,要么由研发补生成端、要么承认它是人工件(不该按产物管)。
 <!-- REVERSE-AUDIT:END -->

+ 2 - 2
guanlan.py

@@ -212,8 +212,8 @@ def cmd_check(c):
         _spec3.loader.exec_module(_ra)
         _rows, _v, _cnt, _fails, _uncl, _rc = _ra.audit(None, verbose=False)
         row(f"反向呼应 (输出→功能/算法→输入): 成立 {_cnt.get('✓', 0)} 件 · 不成立 {_cnt.get('✗', 0)} 件 "
-            f"· 无法验证 {_cnt.get('~', 0)} 件 · 未归类 {_cnt.get('?', 0)} 件", _rc == 0,
-            ";".join(_fails[:2]) or "每件产物都能指到生成端与输入, 或如实标了 shipped(docs §13.6)")
+            f"· 人工件 {_cnt.get('◆', 0)} 件 · 无法验证 {_cnt.get('~', 0)} 件 · 未归类 {_cnt.get('?', 0)} 件", _rc == 0,
+            ";".join(_fails[:2]) or "每件产物都能指到生成端与输入, 或如实标了 shipped/人工件(docs §13.6)")
     except Exception as _e:
         row("反向呼应审计", False, f"{type(_e).__name__}: {_e}")
     # 页面归口 (2026-09-17 用户令 1): 门户里哪些页面是"数据派生快照"(属于产物)、它们陈旧没陈旧。

+ 94 - 0
reference/非产物留痕_20260917/_来源说明.json

@@ -0,0 +1,94 @@
+{
+ "why": "用户令 2026-09-17 #3: 这些件躺在产物仓 outputs/rudong/ 里, 但既不是产物也不是数据 —— 是模型试跑输出(.out/.err)、自审脚本与记录(_audit*.js / _*audit*.md)、bench 输出。它们不参与任何取数, 计在产物台账里会让\"产物\"这个词失去意义; 故原样移到这里留痕, outpts/ 下不再有它们(反向呼应审计的「非产物」族从此为空, 一旦再出现即报)。",
+ "moved": [
+  {
+   "rel": "ontology/ask_last.out",
+   "bytes": 519,
+   "to": "reference/非产物留痕_20260917/ontology/ask_last.out"
+  },
+  {
+   "rel": "ontology/fast_8b_test.out",
+   "bytes": 1038,
+   "to": "reference/非产物留痕_20260917/ontology/fast_8b_test.out"
+  },
+  {
+   "rel": "ontology/p2_test_deepseek.err",
+   "bytes": 0,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_deepseek.err"
+  },
+  {
+   "rel": "ontology/p2_test_deepseek.out",
+   "bytes": 5595,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_deepseek.out"
+  },
+  {
+   "rel": "ontology/p2_test_qwen27b.err",
+   "bytes": 0,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen27b.err"
+  },
+  {
+   "rel": "ontology/p2_test_qwen27b.out",
+   "bytes": 4279,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen27b.out"
+  },
+  {
+   "rel": "ontology/p2_test_qwen30b.err",
+   "bytes": 0,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen30b.err"
+  },
+  {
+   "rel": "ontology/p2_test_qwen30b.out",
+   "bytes": 4058,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen30b.out"
+  },
+  {
+   "rel": "ontology/p2_test_qwen8b.err",
+   "bytes": 0,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen8b.err"
+  },
+  {
+   "rel": "ontology/p2_test_qwen8b.out",
+   "bytes": 4188,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen8b.out"
+  },
+  {
+   "rel": "ontology/p2_test_qwen8b_fast.out",
+   "bytes": 3006,
+   "to": "reference/非产物留痕_20260917/ontology/p2_test_qwen8b_fast.out"
+  },
+  {
+   "rel": "sop/local_review/bench30b_R6a.out",
+   "bytes": 2386,
+   "to": "reference/非产物留痕_20260917/sop/local_review/bench30b_R6a.out"
+  },
+  {
+   "rel": "sop/local_review/bench30b_R6b.out",
+   "bytes": 1247,
+   "to": "reference/非产物留痕_20260917/sop/local_review/bench30b_R6b.out"
+  },
+  {
+   "rel": "windscada/_audit.js",
+   "bytes": 6904,
+   "to": "reference/非产物留痕_20260917/windscada/_audit.js"
+  },
+  {
+   "rel": "windscada/_audit_all.js",
+   "bytes": 563,
+   "to": "reference/非产物留痕_20260917/windscada/_audit_all.js"
+  },
+  {
+   "rel": "windscada/_design_audit_20260826.md",
+   "bytes": 2857,
+   "to": "reference/非产物留痕_20260917/windscada/_design_audit_20260826.md"
+  },
+  {
+   "rel": "windscada/_selfaudit_20260825.md",
+   "bytes": 2405,
+   "to": "reference/非产物留痕_20260917/windscada/_selfaudit_20260825.md"
+  }
+ ],
+ "missing": [],
+ "count": 17,
+ "src_root": "outputs/rudong",
+ "note": "移动是 copy 级的原样搬运, 内容一字未改"
+}

+ 4 - 0
reference/非产物留痕_20260917/ontology/ask_last.out

@@ -0,0 +1,4 @@
+13 台机组 RawPower 系统性低报表明存在缺陷,该缺陷导致控制器上报可用功率低于实际发电功率,违反物理恒等式[mechanism/rudong/mb_grease_low_systemic]。  
+本条结论限制:① 本条 (缺陷存在) 与 RD-2026-08-018 (低报→少分配的因果) 不是一条结论,引用时必须分开【准定论·预警】[claim/RD-2026-08-017];② 两章 verdict 不一致须如实呈现【准定论·预警】[claim/RD-2026-08-017]。
+
+(接地闸 通过 · 9轮 · 直连快路径)

+ 11 - 0
reference/非产物留痕_20260917/ontology/fast_8b_test.out

@@ -0,0 +1,11 @@
+[qwen3:8b] 18s · 接地闸 通过 · 8 轮
+
+29号机组当前状态为**待执行**,修泵作业尚未发生但已批准情景A(2026-09首个CTV窗执行)[acceptance/WTG29/state]。  
+**原因**:  
+1. **偏航轴报警**(verdict/L1/WTG29/偏航,报警级)关联偏航驱动系统异常[verdict/L1/WTG29/偏航];  
+2. **振动稳态异常**(verdict/vib/WTG29/27,双模审定论)与润滑断供(mechanism/WTG29/lub_starvation)及偏航驱动报警(mechanism/rudong/yaw_drive_alarm_family)相关[verdict/vib/WTG29/27]。  
+
+**应对措施**:  
+1. **立即执行情景A**:2026-09 CTV窗完成CTV小修(修泵,无吊装),停机损失约0.8~1.7万元(电价区间0.391~0.85元/kWh)[scenario/WTG29/repair_scheduling_v1];  
+2. **验收树准备**:修后需通过TCM-16验收分支(柱塞回带+尖峰消判据)[decision/WTG29/acceptance_tree];  
+3. **风险对冲**:推迟至情景B/C/D将增加金属接触暴露风险,且成本无实质降低[scenario/WTG29/repair_scheduling_v1]。

+ 0 - 0
reference/非产物留痕_20260917/ontology/p2_test_deepseek.err


+ 49 - 0
reference/非产物留痕_20260917/ontology/p2_test_deepseek.out

@@ -0,0 +1,49 @@
+claim_check 已通过。以下是 29 号机组的判读结论(每个结论句末尾标注 [对象id] 来源,审级/判级为对象原文转述):
+
+## 一、现在什么状态
+
+1. 主轴承集中润滑泵润滑断供(泵本体)为「**定论**」级、**双模审**:"因侧柱塞两段式失效 → 果侧温度长期高位 + 振动间歇尖峰,因果时序闭合"[verdict/WTG29/TCM-16]。其恶化态是窗相关判断——截至振动窗末为"果侧持续恶化",冲击尖峰幅度在爬(9.15→29.13 m/s²)[verdict/WTG29/TCM-16]。
+2. 振动线转录层同判「定论」级、双模审,部件 = 主轴承(后)+集中润滑泵[verdict/vib/WTG29/27]。
+3. 偏航轴为「**报警**」级(lite 筛查级):偏航驱动报警族[verdict/L1/WTG29/偏航][mechanism/rudong/yaw_drive_alarm_family]。
+4. 齿轮箱二级行星内齿圈为「**监视(不开箱)**」级、双模审[verdict/vib/WTG29/22]。
+5. L1 筛查层(lite 非定谳)余系统优秀/良好[verdict/L1/WTG29/主轴承];注意 L1 主轴承"优秀"是筛查级,不背书双模审的"泵本体断供(非轴承损伤)"机制,二者并存不矛盾[verdict/L1/WTG29/主轴承]。
+6. 处置动作"修润滑泵本体"状态 = **待执行(现场)**[action/WTG29/repair_pump]。
+
+## 二、为什么
+
+机制 = 润滑断供·泵本体(v5 定案,非轴承损伤),独立源数按观测面去重 = **4**[mechanism/WTG29/lub_starvation]。证据链(按观测面):
+
+| 观测面 | 证据内容 | 来源 |
+|---|---|---|
+| 因侧·柱塞 | 单次循环时长由自身 2025 年初 132~261s 塌到同年 34~57s("打得少"),2026 年一度回到 68s 后突变 1784s = 自身 18 月区间 7~52 倍("泵打不满"),总打脂 10.3h/天 vs 正常 ~1.4h/天 | [evidence/WTG29/plunger_2stage] |
+| 因侧·报警 | 码 64000"无润滑,主轴承"全窗仅 2 条且全落与柱塞突变同月;因侧第二观测点,不计入独立源数 | [evidence/WTG29/alarm_64000] |
+| 果侧·温度 | 后主轴承温度长期稳定高位,峰 +3.3K,位次全程 1~11/38 | [evidence/WTG29/temp_high_level] |
+| 果侧·振动稳态 | Peak 中位 0.87×fleet 常年排 26-35/38,残余油膜仍在,稳态不高与机制自洽 | [evidence/WTG29/vib_steady_low] |
+| 果侧·振动瞬态 | 冲击轴间歇尖峰:柱塞挣扎期内 29 天零尖峰 max3.65 → 之后爆发,扣台风窗净尖峰 3 个(14.29 / 9.15 / 29.13=9.5×基线),因(泵突变)→果(油膜破裂)先后 ≥10 天 | [evidence/WTG29/vib_impulse] |
+
+## 三、该怎么办
+
+1. **修泵本体(非择期换泵)**:泵已进行性失效(2025 欠量→2026 建压失败)+ 轴承正在承受间歇金属接触[action/WTG29/repair_pump]。可逆级 **L3 待批**,agent 只建议排期与备件、执行 = 人[action/WTG29/repair_pump]。
+2. **排期沙盘 v1(建议级,L3 动作人裁)**:推荐情景 A —— 2026 年 9 月首个 CTV 窗执行修泵[scenario/WTG29/repair_scheduling_v1]。依据:停机损失 0.8~1.7 万元(电价区间 0.391~0.85 元/kWh,合同价未知),限电常态化下实际更低[scenario/WTG29/repair_scheduling_v1];2026 年 9 月 CTV 可作业 30/30 天[scenario/WTG29/repair_scheduling_v1];每推迟一个月即多一个月金属接触暴露,升级即落入情景 D 的百万级结构[scenario/WTG29/repair_scheduling_v1]。
+3. **修后验收三分支决策树**(正本=振动线 TCM-16 falsifiable 字段)[decision/WTG29/acceptance_tree]:
+   - 前置:柱塞单次时长回归 fleet 同月 0.7~1.4× 带且 counts 回同步 = 泵修好确认[decision/WTG29/acceptance_tree];
+   - A 尖峰消(连续≥14 天 ∧ 有效 HI 档≥20 条 ∧ 逐日 max<8 m/s² ∧ 扣台风窗)→ 纯润滑问题、轴承逃过[decision/WTG29/acceptance_tree];
+   - B 柱塞回带尖峰不消 → 升级轴承内窥,前置取主轴后油样对比基线 Fe15/PQ12[decision/WTG29/acceptance_tree];
+   - C 柱塞不回带或回带过慢 → 重开泵/管路排查[decision/WTG29/acceptance_tree];
+   - 兜底:三分支都不发生 → 降为待验,禁止默认按 A 结案[decision/WTG29/acceptance_tree]。
+
+## 四、依据哪些证据窗(claim_window 原文)
+
+| 对象 | claim_window |
+|---|---|
+| [verdict/WTG29/TCM-16] | 振动 2026-06-29~2026-08-11(六窗) + 历史窗 2024-05/2024-08/2024-12/2025-03;柱塞(windscada) 2025-01~2026-07-07;温度(windscada) 2025-01~2026-07;油样 末样取样 2025-03-25(报告 2025-04-06) |
+| [evidence/WTG29/plunger_2stage] | 2025-01~2026-07-07 (SCADA 10min) |
+| [evidence/WTG29/alarm_64000] | 2025-01-01~2026-07-15 (报警仓全窗) |
+| [evidence/WTG29/temp_high_level] | 2025-01~2026-07 (P>50kW 月中位) |
+| [evidence/WTG29/vib_steady_low] | 2024-05~2026-08 (27 个月) |
+| [evidence/WTG29/vib_impulse] | 2026-06-29~2026-08-11 (六窗) |
+| [verdict/L1/WTG29/偏航] | 近90日固定窗 (至2026-07-07; 曲线轴=2025H2) |
+| [verdict/vib/WTG29/22] | 2026-01-27~2026-02-03 (w0127) + 2026-06-29~2026-08-11 (w0707/w0711/w0724/w0805/w0811) |
+| [mwindow/rudong/climatology] | 2025-01-01~2026-08-25 (逐时, Open-Meteo 再分析 32.6N 121.4E 海面格点) |
+
+**一句话总结**:29 号机组主轴承集中润滑泵本体进行性失效导致润滑断供(定论·双模审),当前轴承正承受间歇金属接触且尖峰幅度在爬[verdict/WTG29/TCM-16];处置 = 2026 年 9 月首个 CTV 窗修泵(建议级、L3 人裁),修后按三分支验收树判泵是否修好、是否升级轴承议题[action/WTG29/repair_pump][decision/WTG29/acceptance_tree][scenario/WTG29/repair_scheduling_v1]。

+ 0 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen27b.err


+ 31 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen27b.out

@@ -0,0 +1,31 @@
+29号机组的情况如下(每个结论句末尾已用[对象id]标注来源):
+
+## 现在什么状态
+
+**主轴承(后)+集中润滑泵:定论级异常(双模审)。** 失效机制定案为「润滑断供·泵本体」,v5 判定为非轴承损伤、未升级到轴承损坏[verdict/vib/WTG29/27][mechanism/WTG29/lub_starvation]。修泵动作处于「待执行(现场)」、工单口径为「泵已进行性失效 + 轴承正在承受间歇金属接触」,非择期换泵[action/WTG29/repair_pump]。修后验收状态机当前「待执行」、三分支未落、repair_done 未登记[acceptance/WTG29/state]。
+
+**偏航轴:报警级(lite,L1 筛查/候选,非定谳)。** 判为「偏航驱动报警族」,与主轴承润滑机制相互独立[verdict/L1/WTG29/偏航][mechanism/rudong/yaw_drive_alarm_family]。
+
+**损失(近 3 个月,数据窗至 2026-07):** 台级损失 293799.0 kWh,分态为限电·绑定 161866.0、停机 63460.0、限电命令·未绑定 48441.0、停机(调度令) 18728.0、低风待机 1304.0;同窗正常发电量 639,999 kWh;其中限电·绑定与限电命令·未绑定两项属非设备责任口径[ont_loss: WTG29]。
+
+## 为什么
+
+**因侧(执行链):泵进行性失效。** 柱塞单次循环时长由自身 2025-01~03 的 132~261s 塌到 2025-04~10 的 34~57s(「打得少」),2026-06 单月 68s 正常,2026-07 窗突变 1784s = 自身 18 月区间的 7~52 倍(「泵打不满」),总打脂 10.3h/天 vs 正常 ~1.4h/天[evidence/WTG29/plunger_2stage]。报警码 64000「无润滑,主轴承」全窗仅 2 条、全部落在 2026-07,与柱塞单月突变精确同月[evidence/WTG29/alarm_64000]。
+
+**果侧(振动):间歇尖峰已在爬升。** 冲击轴在柱塞挣扎期内(≤07-07) 29 天零尖峰 max3.65 m/s²,07-11 起爆发,扣台风窗净尖峰 3 个(07-24 14.29 / 07-29 9.15 / 08-07 29.13=9.5×基线),因(泵突变)→果(油膜破裂)先后 ≥10 天[evidence/WTG29/vib_impulse]。稳态振动 Peak 中位 0.87×fleet、常年排 26-35/38,残余油膜仍在、稳态不高与机制自洽[evidence/WTG29/vib_steady_low]。后主轴承温度长期稳定高位型(峰 +3.3K,位次 1~11/38)[evidence/WTG29/temp_high_level]。
+
+**因果链:** 润滑泵断供 → 主轴承(后)间歇金属接触(尖峰幅度在爬),但尚未构成轴承损伤[mechanism/WTG29/lub_starvation]。
+
+## 该怎么办
+
+**排期建议(建议级,L3 人裁):情景 A——2026-09 首个 CTV 窗执行修泵。** 停机损失约 19462 kWh / 0.8~1.7 万元(电价区间 0.391~0.85 元/kWh,合同价未知),2026-09 CTV 可作业 30/30 天几乎不构成约束[scenario/WTG29/repair_scheduling_v1]。推迟情景 B/C 省不下钱(同为万元级损失)只增加金属接触暴露;拖到失效(情景 D)落入吊装级主轴承更换,停机侧 17.5~38.0 万元 + 大部件 2~5 百万元级[scenario/WTG29/repair_scheduling_v1]。
+
+**执行:** 现场修润滑泵本体(消耗 part/mb_lub_pump),L3 待批、执行为人[action/WTG29/repair_pump]。
+
+**修后验收:** 按 TCM-16 三分支决策树——前置柱塞回带确认泵修好;A 尖峰消(≥14 天逐日 max<8 m/s² 扣台风窗)→纯润滑问题轴承逃过;B 柱塞回带但尖峰不消→升级轴承内窥(先取主轴后油样对比 2025-03 基线 Fe15/PQ12);C 柱塞不回带或过慢→重开泵/管路排查;兜底三分支均不发生→降为待验、禁止默认按 A 结案[decision/WTG29/acceptance_tree][acceptance/WTG29/state]。
+
+**边界/口径纪律:** 轴承升级概率无本机型标定 = INSUFFICIENT(只给方向、幅度未知)[scenario/WTG29/repair_scheduling_v1];2026-07 后无 SCADA 实测、画像月用 2025 同月属保守高估方向[scenario/WTG29/repair_scheduling_v1];电价用区间口径、合同价未知,限电类损失≠设备责任[ont_loss: WTG29]。
+
+**核心一句话:** 29# 主轴承润滑泵已进行性失效、轴承正承受间歇金属接触(尖峰幅度在爬但尚未成损伤),偏航另有独立报警;应走情景 A(2026-09 首个 CTV 窗修泵),用万元级小修对冲拖到失效后的百万元级主轴承大修。
+
+claim_check 已调用并**通过**(接地闸通过)。

+ 0 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen30b.err


+ 37 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen30b.out

@@ -0,0 +1,37 @@
+接地闸已通过。以下是 29 号机组(WTG29)的完整判读。
+
+## 一、现在什么状态
+
+- 主轴承(后)+集中润滑泵为 **「定论」级故障**(审级=双模审):主轴承润滑断供·泵本体;机制定性窗无关,但「果侧持续恶化」是窗相关判断,截至 2026-08-11 振动窗为持续恶化态 [verdict/WTG29/TCM-16]
+- 偏航轴为 **「报警」级**(L1 自动判级,筛查级/候选,审级=lite),机制归属「偏航驱动报警族」,与主轴承润滑机制独立 [verdict/L1/WTG29/偏航]
+
+## 二、为什么(机制 = 润滑断供·泵本体,v5 定案,非轴承损伤)
+
+- 独立源数 = 4(按观测面去重:因侧执行链 / 果侧振动瞬态 / 果侧振动稳态 / 果侧温度)[mechanism/WTG29/lub_starvation]
+- **因侧执行链**:柱塞两段式失效 —— 单次循环时长由 2025-01~03 的 132~261s 塌到 2025-04~10 的 34~57s("打得少"),2026-06 单月 68s 正常 → 2026-07 突变 1784s(7~52 倍,"泵打不满"),总打脂 10.3h/天 vs 自身正常 ~1.4h/天 [evidence/WTG29/plunger_2stage]
+- **因侧第二观测点**:报警码 64000「无润滑,主轴承」全窗仅 2 条且全部落 2026-07,与柱塞单月突变精确同月(同属供给执行链,不计独立源)[evidence/WTG29/alarm_64000]
+- **果侧温度**:后主轴承长期稳定高位,峰 +3.3K(2025-08),位次 1~11/38 [evidence/WTG29/temp_high_level]
+- **果侧振动稳态**:Peak 中位 0.87×fleet 常年排 26-35/38,残余油膜仍在 [evidence/WTG29/vib_steady_low]
+- **果侧振动瞬态**:07-11 起爆发,扣台风窗净尖峰 3 个(07-24 14.29 / 07-29 9.15 / 08-07 29.13=9.5×基线),因(泵突变)→果(油膜破裂)先后 ≥10 天 [evidence/WTG29/vib_impulse]
+- 因果时序闭合:因侧柱塞两段式失效 → 果侧温度长期高位 + 振动间歇尖峰 [verdict/WTG29/TCM-16]
+
+## 三、该怎么办
+
+- **修润滑泵本体**(非择期换泵):泵已进行性失效(2025 欠量 → 2026 建压失败)+ 轴承正在承受间歇金属接触[截至 2026-08-11 振动窗];状态=待执行(现场),可逆级=L3 待批,执行=人 [action/WTG29/repair_pump]
+- **排期**(建议级,L3 人裁):情景 A —— 2026-09 首个 CTV 窗修泵,停机损失 0.8~1.7 万元(电价 0.391~0.85 元/kWh,合同价未知);推迟 B/C 省不下钱只增金属接触暴露,拖到失效即落入情景 D 吊装级换主轴承(大部件 2~5 百万元级,grade D 弱源)[scenario/WTG29/repair_scheduling_v1]
+- **修后验收三分支决策树**:A 尖峰消(≥14天 ∧ HI档≥20条 ∧ 逐日max<8 m/s² ∧ 扣台风窗)⇒纯润滑问题轴承逃过;B 柱塞回带但尖峰不消 ⇒ 升级轴承内窥(前置取主轴后油样对比 2025-03 基线 Fe15/PQ12);C 柱塞不回带 ⇒ 重开泵/管路排查;兜底=三分支都不发生则降待验,禁默认按 A 结案 [decision/WTG29/acceptance_tree]
+
+## 四、依据哪些证据窗
+
+| 观测面 | 窗口 | 来源 |
+|---|---|---|
+| 振动(六窗+历史) | 2026-06-29~2026-08-11 + 2024-05/08/12、2025-03 | [verdict/WTG29/TCM-16] |
+| 柱塞 (windscada) | 2025-01~2026-07-07(10min) | [evidence/WTG29/plunger_2stage] |
+| 报警仓 | 2025-01-01~2026-07-15(全窗) | [evidence/WTG29/alarm_64000] |
+| 温度 | 2025-01~2026-07(P>50kW 月中位) | [evidence/WTG29/temp_high_level] |
+| 振动稳态 | 2024-05~2026-08(27 个月) | [evidence/WTG29/vib_steady_low] |
+| 振动瞬态 | 2026-06-29~2026-08-11(六窗) | [evidence/WTG29/vib_impulse] |
+| 油样 | 末样取样 2025-03-25(报告 2025-04-06) | [verdict/WTG29/TCM-16] |
+| 偏航 | 近 90 日固定窗(至 2026-07-07) | [verdict/L1/WTG29/偏航] |
+
+**一句话结论**:29 号机主轴承润滑泵本体已定论性失效(双模审),当前正在造成轴承间歇金属接触,应在 2026-09 首个 CTV 窗修泵(万元级),并以三分支树验收,避免拖成百万级吊装换轴承;偏航另有独立的报警级线索待 L1 筛查后续跟进。

+ 0 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen8b.err


+ 41 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen8b.out

@@ -0,0 +1,41 @@
+接地闸通过。以下为 29# 机组最终回答(每结论句末 [对象id] 标注来源, 判级/审级/证据窗均已转述对象原文)。
+
+## 现在什么状态
+
+1. 29# 主轴承处于「润滑断供·泵本体」故障机制, 判级=**定论**(双模审), 定性为**非轴承损伤**(泵本体是根因, 非择期换泵) [mechanism/WTG29/lub_starvation]。
+2. 恶化态(窗相关): 截至 2026-08-11 振动窗为「果侧持续恶化」, 冲击轴尖峰幅度在爬 9.15→29.13 m/s² [verdict/WTG29/TCM-16]。
+3. 修泵动作=**待执行(现场, L3待批)** [action/WTG29/repair_pump]。
+4. 修后验收状态机=**待执行**(修泵未发生, pending_user), 排期建议=情景A 2026-09 [acceptance/WTG29/state]。
+5. 另有 L1 **偏航报警**(lite, 筛查级候选), 与主轴承机制相互独立 [verdict/L1/WTG29/偏航]。
+6. 口径提示: L1 主轴承轴判「优秀」是 lite 筛查级近90日固定窗判读, 不覆盖柱塞/振动瞬态深机制, 与振动线「定论」审级不同、不冲突 [verdict/L1/WTG29/主轴承]。
+
+## 为什么(成因链, 因果时序闭合)
+
+1. 因侧·柱塞两段式失效: 单次循环时长由自身 2025年1~3月 132~261s 塌到 2025年4~10月 34~57s(「打得少」); 2026年6月 68s 似正常 → 2026年7月突变 1784s=自身 18 月区间 7~52 倍(「泵打不满」), 总打脂 10.3h/天 vs 正常 ~1.4h/天 [evidence/WTG29/plunger_2stage]。
+2. 报警码 64000「无润滑,主轴承」全窗仅 2 条且全部落 2026年7月, 与柱塞突变精确同月(因侧第二观测点, 不计独立源数) [evidence/WTG29/alarm_64000]。
+3. 果侧·温度: 后主轴承温度长期稳定高位型, 峰 +3.3K(2025-08), 位次全程 1~11/38 [evidence/WTG29/temp_high_level]。
+4. 果侧·振动稳态: Peak 中位 0.87×fleet 常年排 26-35/38 — 残余油膜仍在, 稳态不高与机制自洽 [evidence/WTG29/vib_steady_low]。
+5. 果侧·振动瞬态: 柱塞挣扎期内(≤07-07)29 天零尖峰 max3.65 → 7月11日起爆发, 扣台风窗净尖峰 3 个(07-24 14.29 / 07-29 9.15 / 08-07 29.13=9.5×基线), 因(泵突变)→果(油膜破裂)先后 ≥10 天 [evidence/WTG29/vib_impulse]。
+
+## 该怎么办(处置)
+
+1. 修润滑泵本体: 现场物理动作, 可逆级 **L3待批**, agent 只建议排期与备件、执行=人 [action/WTG29/repair_pump]。
+2. 排期建议(建议级, L3 人裁): **情景 A — 2026-09 首个 CTV 窗执行**; 停机损失 0.8~1.7 万元(电价区间 0.391~0.85 元/kWh, 合同价未知), 每推迟 1 月=1 个月金属接触暴露, 升级即落入吊装级大修(百万元级) [scenario/WTG29/repair_scheduling_v1]。
+3. 修后走三分支验收树: **A 尖峰消**(连续≥14天∧HI≥20条∧逐日max<8 m/s²∧扣台风窗→纯润滑问题); **B 柱塞回带尖峰不消**(升级轴承内窥, 前置=取主轴后油样对比 2025-03 基线 Fe15/PQ12); **C 柱塞不回带**(泵没修好, 重开泵/管路排查); 兜底: 三分支都不发生→降为待验, 禁默认按 A 结案 [decision/WTG29/acceptance_tree]。
+
+## 依据的证据窗
+
+| 证据 | 观测面 | claim_window |
+|---|---|---|
+| 柱塞两段式 [evidence/WTG29/plunger_2stage] | 因侧执行链 | 2025-01~2026-07-07 (SCADA 10min) |
+| 报警 64000 [evidence/WTG29/alarm_64000] | 因侧执行链 | 2025-01-01~2026-07-15 (报警仓全窗) |
+| 温度高位 [evidence/WTG29/temp_high_level] | 果侧温度 | 2025-01~2026-07 (P>50kW 月中位) |
+| 振动稳态低 [evidence/WTG29/vib_steady_low] | 果侧振动稳态 | 2024-05~2026-08 (27个月) |
+| 振动瞬态尖峰 [evidence/WTG29/vib_impulse] | 果侧振动瞬态 | 2026-06-29~2026-08-11 (六窗) |
+| L1 偏航 [evidence/L1/WTG29/偏航] | 因侧执行链 | 近90日固定窗 (至2026-07-07) |
+
+定论层综合 claim_window: 振动 2026-06-29~2026-08-11(六窗)+历史窗 2024-05/08/12、2025-03; 柱塞 2025-01~2026-07-07; 温度 2025-01~2026-07; 油样末样取样 2025-03-25(报告 2025-04-06) [verdict/WTG29/TCM-16]。
+
+损失口径补充(窗=2026-05~2026-07): WTG29 损失结构 293,799 kWh, 其中限电·绑定 161,866 主导, **限电类损失≠设备责任**; 折金额须带电价区间 0.391~0.85 元/kWh(合同价未知) [ont_loss]。
+
+claim_check: 通过。

+ 37 - 0
reference/非产物留痕_20260917/ontology/p2_test_qwen8b_fast.out

@@ -0,0 +1,37 @@
+# 29# 机组 · 状态 / 原因 / 处置
+
+## 现在什么状态
+
+29# 判级 11 条,其中报警/定论/预警级 2 条:振动线「主轴承(后)+集中润滑泵」= 定论(双模审),偏航线 = 报警(lite)[verdict/vib/WTG29/27][verdict/L1/WTG29/偏航][ont_turbine_brief]。
+
+定论级结论:主轴承**润滑断供(泵本体)**,非轴承损伤,机制定性已定案、因果时序闭合[mechanism/WTG29/lub_starvation][verdict/WTG29/TCM-16]。
+
+截至 2026-08-11 振动窗,果侧为「持续恶化」态:冲击轴间歇尖峰在泵突变后爆发,扣台风窗净尖峰 3 个(07-24 14.29 / 07-29 9.15 / 08-07 29.13 = 9.5×基线)[evidence/WTG29/vib_impulse][verdict/WTG29/TCM-16]。
+
+修泵动作已登记、待执行(现场),验收状态机「待执行」[action/WTG29/repair_pump][acceptance/WTG29/state]。偏航驱动另有报警族机制,与主轴承润滑机制相互独立[mechanism/rudong/yaw_drive_alarm_family]。
+
+近 3 月(2026-05~07)台级损失 293799 kWh,同窗正常发电 639,999 kWh[ont_loss]。
+
+## 为什么
+
+**因侧** = 柱塞两段式失效:单次循环时长由自身 2025-01~03 的 132~261s 塌到 2025-04~10 的 34~57s(打得少),2026-06 单月 68s 正常后 2026-07 窗突变至 1784s = 自身 18 月区间 7~52 倍(打不满),总打脂 10.3h/天 vs 自身正常 ~1.4h/天[evidence/WTG29/plunger_2stage]。
+
+**果侧** = 温度长期稳定高位(峰 +3.3K,位次全程 1~11/38)加振动间歇尖峰(泵突变→油膜破裂先后 ≥10 天)[evidence/WTG29/temp_high_level][evidence/WTG29/vib_impulse]。
+
+四观测面(因侧执行链 / 果侧振动瞬态 / 果侧振动稳态 / 果侧温度)独立印证同一机制,独立源数按观测面去重 = 4[mechanism/WTG29/lub_starvation]。修泵为 L3 待批(不可逆物理动作),agent 只建议排期与备件、执行 = 人[action/WTG29/repair_pump]。
+
+## 该怎么办
+
+排期沙盘(建议级,L3 人裁):**情景 A = 2026-09 首个 CTV 窗执行修泵**,停机损失 0.8~1.7 万元(电价区间 0.391~0.85 元/kWh,合同价未知)、2026-09 CTV 可作业 30/30 天;推迟情景 B/C 省不下钱(同为万元级)只增加金属接触暴露,升级即落入情景 D 吊装级主轴承更换(2~5 百万元级)[scenario/WTG29/repair_scheduling_v1]。
+
+修后按验收三分支决策树判定[decision/WTG29/acceptance_tree]:
+- **前置**:柱塞单次时长回 fleet 同月 0.7~1.4× 带且 counts 回同步 = 泵修好确认。
+- **A 尖峰消**(连续 ≥14 天 ∧ 有效 HI 档 ≥20 条 ∧ 逐日 max<8 m/s² ∧ 扣台风窗)= 纯润滑问题,轴承逃过。
+- **B 柱塞回带尖峰不消** = 升级轴承内窥。
+- **C 柱塞不回带或过慢** = 重开泵/管路排查。
+- **兜底**:三分支都不发生 = 降为待验,禁默认按 A 结案。
+
+边界:轴承升级概率无本机型标定 = INSUFFICIENT(只给方向);限电类损失 ≠ 设备责任;2026-07 后无 SCADA 实测[scenario/WTG29/repair_scheduling_v1][ont_loss]。
+
+---
+`claim_check` 已通过(接地闸:台号/数字均来自本会话工具事实)。

+ 16 - 0
reference/非产物留痕_20260917/sop/local_review/bench30b_R6a.out

@@ -0,0 +1,16 @@
+WTG23 主轴承状态(业主口径):**报警**(turbine_summary)。分部件:主轴承前=报警,主轴承后=优秀,齿轮箱=优秀,发电机=优秀(turbine_summary)。证据状态:候选;行动等级建议:P1(turbine_summary)。
+
+**核心依据(逐项注明来源)**
+
+1. 冲击轴显著偏离,末窗 3601-4400 kW 档标量 ×fleet 前三:主轴承前 Kurtosis 343.876(7.98×)、主轴承前 Peak 42.9403(7.37×)、主轴承后 Peak 6.8726(1.71×)(turbine_summary 冲击轴段)。
+2. 主轴承前 Peak 六窗中位:w0127=4.3877、w0707=31.3844、w0711=14.4176、w0724=28.9656、w0805=20.0902、w0811=42.9403,fleet 同档中位 5.512(scalar_trend)。末窗 w0811 为六窗最高;收敛报告 v5 载前测点 Peak 六窗中位居 38 台之首 43.4 m/s² vs fleet 5.6(turbine_summary v5 段)。
+3. 主轴承前 Kurtosis 末窗 w0811=343.876,fleet 同档中位 43.34(scalar_trend)。
+4. 主轴承后 Peak 末窗 w0811=6.8726,fleet 同档中位 3.833(scalar_trend)。
+5. 主轴承前最新包络谱(2026-08-11 05:19:26,rpm 1605):BPFI/BPFO/BSF 三条 OEM 线 G2 均不过(0/3),即无滚道缺陷谱线证据(spectrum)。
+6. 模型依据:主轴承前稳定高位→候选(末窗中位/全窗基线 1.09×,全期中位/fleet 4.79×,剔共模/台风窗后高值日 0;待下窗确认);机制 LOW_IS_BLIND;CMS 近 12 月零告警、无告警驱动(turbine_summary fusion 段)。
+7. L4 过闸 2 条线(均在发电机侧,非主轴承):发电机DE 内圈 0.2925 mm/s ×fleet 3.13→参考;发电机NDE 内圈 0.1191 mm/s ×fleet 4.2→INSUFFICIENT(turbine_summary lines 段)。
+
+**定性(v5 终态)**:前主轴承稳定高位、非快速恶化;具体部件未定、不定滚道,传播路径/机制未闭合;判转子侧周期载荷调制候选(机制 INSUFFICIENT,不支持单一滚道局部缺陷);"近一转可见冲击"撤回维持(turbine_summary v5 段)。
+
+**下一步建议(v5 交接 v3 7.2,并列)**:内窥 + 对中记录、轴承座螺栓力矩、SCADA 1P 不平衡核 三者并列(turbine_summary)。另因末窗待下窗确认,建议下窗复核主轴承前 Peak/Kurtosis 是否持续高位。
+$R/dsh_run.sh --profile headless --patch $R/headless.patch.vibration.yml     2.71s user 0.68s system 7% cpu 43.230 total

+ 16 - 0
reference/非产物留痕_20260917/sop/local_review/bench30b_R6b.out

@@ -0,0 +1,16 @@
+优先关注 7 台(均为「报警」,来源 mcp__windcms__farm_overview):
+
+| 序 | 台号 | 一句依据 |
+|---|---|---|
+| 1 | WTG38 | 主轴承后报警,证据候选,行动等级建议 P0(farm_overview) |
+| 2 | WTG09 | 齿轮箱报警,证据确诊,行动 P1(farm_overview) |
+| 3 | WTG16 | 主轴承前、主轴承后、发电机三处报警,证据准定论·预警,行动 P1(farm_overview) |
+| 4 | WTG33 | 发电机报警,证据准定论·预警,行动 P1(farm_overview) |
+| 5 | WTG23 | 主轴承前报警,证据候选,行动 P1(farm_overview) |
+| 6 | WTG20 | 主轴承后报警、齿轮箱不可判,证据候选,行动 P1(farm_overview) |
+| 7 | WTG17 | 发电机报警,证据候选,行动 P1(farm_overview) |
+
+因数据缺失不可判 3 台(来源 farm_overview):WTG37、WTG18、WTG02 —— 主轴承前/后、齿轮箱、发电机四部件全不可判,证据 INSUFFICIENT,行动 P1(补数)。
+
+另有部件级不可判(整机仍有级别,来源 farm_overview):WTG25、WTG04、WTG07 主轴承后不可判;WTG27 发电机不可判。
+$R/dsh_run.sh --profile headless --patch $R/headless.patch.vibration.yml     6.49s user 0.45s system 15% cpu 44.615 total

+ 109 - 0
reference/非产物留痕_20260917/windscada/_audit.js

@@ -0,0 +1,109 @@
+(async()=>{
+const R={score:{},issues:[],facts:{}};
+const px=v=>parseFloat(v)||0;
+const all=[...document.querySelectorAll('body *')].filter(e=>e.offsetParent!==null||e.tagName==='TH');
+// —— 1 排版: 字号阶梯收敛
+const sizes={}; all.forEach(e=>{const s=getComputedStyle(e).fontSize; sizes[s]=(sizes[s]||0)+1});
+const sizeList=Object.entries(sizes).sort((a,b)=>b[1]-a[1]);
+R.facts.fontSizes=sizeList.map(([k,v])=>k+'×'+v);
+const nSizes=sizeList.filter(([k,v])=>v>=3).length;
+R.score.type_scale = nSizes<=7?100:nSizes<=9?80:50;
+if(nSizes>7) R.issues.push(`字号阶梯 ${nSizes} 档 (>7): ${sizeList.filter(x=>x[1]>=3).map(x=>x[0]).join(',')}`);
+// 字族一致
+const fams=new Set(all.map(e=>getComputedStyle(e).fontFamily.split(',')[0].replace(/"/g,'')));
+R.facts.families=[...fams];
+R.score.type_family = fams.size<=3?100:70;
+if(fams.size>3) R.issues.push('字族 '+fams.size+' 种: '+[...fams].join('|'));
+// 数字等宽覆盖 (.num/.kpi b 必须 tabular)
+const numEls=[...document.querySelectorAll('.num,.kpi b,.cnt')];
+const badNum=numEls.filter(e=>!/tabular-nums/.test(getComputedStyle(e).fontVariantNumeric));
+R.score.type_tabular = numEls.length? Math.round((1-badNum.length/numEls.length)*100):100;
+if(badNum.length) R.issues.push(`数字未等宽 ${badNum.length}/${numEls.length}`);
+// 行长 (正文 ch)
+// 行长: CJK 按 1em/字, 拉丁按 0.5em/字 → 目标 ≤ 46 CJK 字 (中文正文舒适区 35~46)
+const proses=[...document.querySelectorAll('p.small,.fig-note,.det p,.empty')].slice(0,40);
+const longs=proses.filter(e=>{const fs=px(getComputedStyle(e).fontSize); const t=e.textContent;
+  const cjk=(t.match(/[\u4e00-\u9fff]/g)||[]).length, frac=t.length?cjk/t.length:0;
+  const em=fs*(0.5+0.5*frac);  // 中文占比越高, 每字越宽
+  return e.getBoundingClientRect().width/em>46+ (1-frac)*44});
+R.score.line_length = proses.length? Math.round((1-longs.length/proses.length)*100):100;
+if(longs.length) R.issues.push(`行长超舒适区的段落 ${longs.length}/${proses.length}`);
+// —— 2 间距: 4px 栅格
+const spaced=all.slice(0,400);
+const offGrid=[];
+spaced.forEach(e=>{const s=getComputedStyle(e);
+  ['marginTop','marginBottom','paddingTop','paddingBottom','paddingLeft','gap'].forEach(k=>{
+    const v=px(s[k]); if(v>0 && Math.abs(v-Math.round(v))<0.01 && v%1===0 && (v%2!==0)) offGrid.push(e.className+'.'+k+'='+v)})});
+R.facts.offGrid=offGrid.slice(0,8);
+R.score.spacing_grid = offGrid.length<=3?100:offGrid.length<=10?80:60;
+if(offGrid.length>3) R.issues.push(`非偶数间距 ${offGrid.length} 处 (示例 ${offGrid.slice(0,3).join(', ')})`);
+// —— 3 版面: 无横向溢出 + 点击目标
+R.facts.bodyScroll=[document.documentElement.scrollWidth, window.innerWidth];
+R.score.layout_nohscroll = document.documentElement.scrollWidth<=window.innerWidth+1?100:0;
+if(document.documentElement.scrollWidth>window.innerWidth+1) R.issues.push('页面横向溢出 '+document.documentElement.scrollWidth+'>'+window.innerWidth);
+const targets=[...document.querySelectorAll('button,select,a.tl')];
+const small=targets.filter(e=>{const r=e.getBoundingClientRect(); return r.height>0&&r.height<24});
+R.score.layout_targets = targets.length? Math.round((1-small.length/targets.length)*100):100;
+if(small.length) R.issues.push(`点击目标<24px高 ${small.length}/${targets.length}`);
+// —— 4 对比度 (WCAG AA 4.5:1 正文 / 3:1 大字)
+// 颜色解析: 支持 rgb()/rgba()/color(srgb r g b / a), 0..1 与 0..255 自动判别; 半透明按父底合成
+function parse(c){
+  if(!c) return null;
+  const nums=(c.match(/-?[\d.]+(e-?\d+)?/g)||[]).map(Number);
+  if(nums.length<3) return null;
+  let [r,g,b]=nums; const a=nums.length>3?nums[3]:1;
+  const isUnit=/color\(/.test(c)||(r<=1&&g<=1&&b<=1&&/\./.test(c));
+  if(isUnit){r*=255;g*=255;b*=255}
+  return {r,g,b,a};
+}
+function over(fg,bg){ // alpha 合成
+  const a=fg.a; return {r:fg.r*a+bg.r*(1-a), g:fg.g*a+bg.g*(1-a), b:fg.b*a+bg.b*(1-a), a:1};
+}
+function lumc(o){const f=v=>{v/=255; return v<=.03928?v/12.92:Math.pow((v+.055)/1.055,2.4)};
+  return .2126*f(o.r)+.7152*f(o.g)+.0722*f(o.b)}
+function bg(e){ // 从元素向上合成到不透明底
+  let n=e, stack=[];
+  while(n&&n!==document.documentElement){
+    const c=parse(getComputedStyle(n).backgroundColor);
+    if(c&&c.a>0){ stack.push(c); if(c.a>=1) break }
+    n=n.parentElement;
+  }
+  let base=parse(getComputedStyle(document.body).backgroundColor)||{r:255,g:255,b:255,a:1};
+  if(base.a<1) base={r:255,g:255,b:255,a:1};
+  for(let i=stack.length-1;i>=0;i--) base=over(stack[i], base);
+  return base;
+}
+const texts=[...document.querySelectorAll('p,td,th,span,b,h1,h3,button,a')].filter(e=>e.textContent.trim()&&e.children.length===0).slice(0,220);
+const lowC=[];
+texts.forEach(e=>{const s=getComputedStyle(e); const fg=parse(s.color); if(!fg) return;
+  const b=bg(e); const fgc=fg.a<1?over(fg,b):fg;
+  const L1=lumc(fgc)+.05, L2=lumc(b)+.05;
+  const ratio=Math.max(L1,L2)/Math.min(L1,L2); const fs=px(s.fontSize); const need=(fs>=18||(fs>=14&&+s.fontWeight>=600))?3:4.5;
+  if(ratio<need) lowC.push(`${e.className||e.tagName} ${fs}px ${ratio.toFixed(2)}:1<${need}`)});
+R.facts.lowContrast=lowC.slice(0,10);
+R.score.contrast = texts.length? Math.round((1-lowC.length/texts.length)*100):100;
+if(lowC.length) R.issues.push(`对比度不足 ${lowC.length}/${texts.length}: ${lowC.slice(0,3).join(' | ')}`);
+// —— 5 图表
+const svgs=[...document.querySelectorAll('svg')];
+let chartIssues=[];
+svgs.forEach((sv,i)=>{
+  const card=sv.closest('.card')||sv.closest('.fig'); const title=(card?.querySelector('h3')||card?.querySelector('h4'))?.textContent||'?';
+  const texts=[...sv.querySelectorAll('text')];
+  const tiny=texts.filter(t=>px(getComputedStyle(t).fontSize)<9);
+  if(tiny.length) chartIssues.push(`${title.slice(0,16)}: ${tiny.length}个刻度字号<9px`);
+  // 图例重叠
+  const legs=texts.filter(t=>/^—/.test(t.textContent));
+  for(let a=0;a<legs.length;a++)for(let b=a+1;b<legs.length;b++){
+    const r1=legs[a].getBoundingClientRect(),r2=legs[b].getBoundingClientRect();
+    if(r1.right>r2.left+1&&r2.right>r1.left+1&&Math.abs(r1.top-r2.top)<4) chartIssues.push(`${title.slice(0,16)}: 图例重叠`)}
+  // 单位
+  const hasAxisLabel=[...sv.querySelectorAll('text')].some(x=>/[a-zA-Z%℃°]|m\/s|kW|bar|rpm|条|次|日|月/.test(x.textContent)&&x.textContent.length>1);
+  if(!/\(|(/.test(title) && !card?.querySelector('.fig-note') && !hasAxisLabel) chartIssues.push(`${title.slice(0,16)}: 无单位无图注无轴标`);
+});
+R.facts.svgCount=svgs.length; R.facts.chartIssues=chartIssues.slice(0,10);
+R.score.charts = svgs.length? Math.max(0,100-chartIssues.length*12):100;
+if(chartIssues.length) R.issues.push(`图表问题 ${chartIssues.length}: ${chartIssues.slice(0,3).join(' | ')}`);
+// —— 总分
+const vals=Object.values(R.score); R.total=Math.round(vals.reduce((a,b)=>a+b,0)/vals.length);
+return JSON.stringify(R);
+})()

+ 12 - 0
reference/非产物留痕_20260917/windscada/_audit_all.js

@@ -0,0 +1,12 @@
+(async()=>{
+  const src = await (await fetch('/static/_audit.js')).text();
+  const run = async()=>JSON.parse(await eval(src));
+  const out={};
+  const tabs=['总览','问题总览','可靠性概览','故障统计','控制性能'];
+  for(const t of tabs){
+    const b=[...document.querySelectorAll('.tabs button')].find(x=>x.textContent===t);
+    if(!b) continue; b.click(); await new Promise(r=>setTimeout(r,1000));
+    const r=await run(); out['页签·'+t]={t:r.total, low:Object.entries(r.score).filter(([k,v])=>v<100)};
+  }
+  return JSON.stringify(out);
+})()

+ 39 - 0
reference/非产物留痕_20260917/windscada/_design_audit_20260826.md

@@ -0,0 +1,39 @@
+# windscada 界面设计审计 (2026-08-26; 机器实测, 非主观打分)
+
+审计器 `scripts/_audit/design_audit.js` — 在真实页面上读**计算样式**打分, 九项:
+排版三项(字号阶梯收敛/字族/数字等宽) · 行长(CJK 按 1em/字) · 间距栅格(偶数) ·
+版面两项(零横向溢出/点击目标≥24px) · 对比度(WCAG AA: 小字4.5:1 大字3:1, 半透明按父底合成) · 图表(刻度字号/图例重叠/单位轴标)
+
+## 终判: 全页面 100
+
+| 面 | 浅色·桌面 | 暗色·桌面 | 平板768 | 手机375 |
+|---|---|---|---|---|
+| 总览 (静态双翼) | 100 | 100 | — | — |
+| 问题总览 | 100 | 100 | — | — |
+| 可靠性概览 | 100 | 100 | — | — |
+| 性能分析 (19图) | 100 | — | — | — |
+| 故障统计 (2图) | 100 | 100 | — | — |
+| 控制性能 | 100 | 100 | — | — |
+| 单问题页 (5图) | 100 | — | — | 100 |
+| 静态台页 (4图) | 100 | 100 | 100 | — |
+
+起点 84 → 终点 100。逐项修的是**真问题**, 不是放宽标准:
+
+| 扣分项 | 实测 | 修法 |
+|---|---|---|
+| 点击目标 | 57/64 <24px | 表内/正文台号链接改 inline-flex 小片, min-height 24px min-width 34px |
+| 间距栅格 | 115 处非偶数 | 全部落偶数 (9→8, 5→6, 3→4, 11→12, 13→14), 卡片间距内联值改类 `.mb` |
+| 字号阶梯 | 8~10 档 | 收敛到 6 档: 10.5 眉标/表头 · 11.5 次要 · 12.5 正文表格 · 13.5 页签 · 17 标题 · 23 KPI (图内刻度 9.5→10) |
+| 行长 | 1 段超舒适区 | 正文 `max-width:44em` (中文 ≈44 字/行) |
+| 图表 | 季节图图例重叠 | >2 系列的图例移到图下 HTML 行 (色条+名), SVG 内不再挤; 季节图补单位"条/月"+轴标+参考级图注 |
+| 图表 | 台页图无标题识别 | 台页图容器 `.fig h4` → 全站统一 `.card h3` (真的不一致, 顺手统一) |
+| 对比度 | 色片 3.80/4.36:1 | 浅色态语义色加深至 AA: ok #2F7A44→#1F6234 · warn #B0620F→#8A4A08 · bad #B3352B→#98281F · info #3D5080→#2F4068 · unknown→#54606D |
+
+## 审计器自身也修了两个 bug (否则结论是假的)
+1. **行长按拉丁 0.5em/字算** → 中文每字约 1em, 原判据对中文页面系统性误判 → 改按 CJK 占比加权。
+2. **对比度解析不认 `color(srgb r g b / a)`** 且不做透明合成 → 把 9% 色片底当成近黑, 报出 2.65:1 假失败 → 改为格式自适应 + 逐层 alpha 合成到不透明底。修正后才暴露出真实的 3.80/4.36 失败。
+
+## 边界
+- 审计器覆盖可测项 (排版/间距/版面/对比度/图表基本卫生), **不覆盖**主观美学与信息层级合理性;
+- 图表项检查刻度字号/图例重叠/单位存在, **不检查**图表选型是否恰当 (那属分析方法而非界面);
+- 100 分 = 九项机器判据全过, 不等同于"通过双模型外审" (外审须用户批准后走 report-ship)。

+ 37 - 0
reference/非产物留痕_20260917/windscada/_selfaudit_20260825.md

@@ -0,0 +1,37 @@
+# windscada M8–M13 自审记录 (2026-08-25 夜, 自主轮)
+
+## 1. 数字 data-first 复核 (15 项, 全中)
+从各仓 parquet 独立重算, 与已发布结论逐项比对:
+
+| 件 | 项 | 结论值 | 复核 |
+|---|---|---|---|
+| M8 | 2026 停机事件数(非调度令) | 2892 起 | ✔ |
+| M8 | 停机总时长 | 16330 h | ✔ |
+| M9 | 满发封顶分组 4175/4200 | 16/16 台 | ✔ |
+| M9 | 转速封顶 1672 组 | 23 台 | ✔ |
+| M11 | Cp 峰全场最大 / 齿比 / λ平台 | 0.334 / 119.443 / 8.8 | ✔ |
+| M12 | 对风σ(清洗后)全场中位 / 16# / 16#corr | 1.66° / 6.76° / −0.431 | ✔ |
+| M12 | 偏航行程中位 | 414.7 °/日 | ✔ |
+| M13 | HS−油全场中位 / 23# / 32#三相极差 | 13.79K / 18.0K / 5.66K | ✔ |
+
+## 2. 口径闭合核 (订正一处先前表述)
+M8 commit 原文 "16330h+短段522h≈台账16852h" 口径含糊 (16852 是不含调度令的台账停机)。**正确闭合式**:
+
+    台账停机(含调度令) 17527 h  −  事件化(≥1h 段) 17050 h  =  477 h  (短段 <1h, 设计内不事件化)
+
+其中事件化 17050 h = 非调度令 16330 h + 调度令 719 h。抽样 8 台独立验: 停机行中短段占 4.2%, 量级一致。
+**结论: 事件化无遗漏, 差额全部是设计上排除的 <1h 短段。**
+
+## 3. 本轮自逮的判据错 (已修, 留档防复发)
+- **稀发执行器用中位聚合必塌成 0**: 冷却器高速 counts 1.3 次/日, duty 中位 0.000 → 改 mean 后
+  高油温档 duty 0.38、σ 0.10 = 有分辨力。(M13)
+- **citation_gate 把窗名当数字断言**: w0127/w0707/w0724/w0805 被判"无出处"8 处全假阳 →
+  `_strip_labels` 剔 wMMDD/ISO日期/版本号后再抽数字; 两份独立拷贝同修 + 用例锁真失实仍逮。
+- (先前轮) λ 用峰值(落在切入段最小转速地板) → 改 Region-2 平台; 0kW 混合档经均值放大造"转速偏低族"
+  假信号 → 加工况段闸 + 逐档中位; MAD≈0 造 z=221 伪离群 → 每镜头配物理绝对锚。
+
+## 4. 边界(未闭, 如实登记)
+- 偏航死区/速率/滞环: 需 ≤30s 数据, 10min 层不可测, 1min 层该通道全场死零 → INSUFFICIENT
+- 齿箱加热执行器全场恒零 → 无分辨力, 不作判据
+- 冷却器高速 duty 差异已控同油温档, 但未控环温/风扇转速(无通道) → 判级止于"记基线观察"
+- 05#/32# 三相极差 = 仪表/接线候选, 非部件 (SOP §4.6b), 须现场随载荷+tag 核

+ 55 - 0
scripts/products_restore_missing.py

@@ -160,13 +160,68 @@ class StashMissing(SystemExit):
     exit_code = 6
 
 
+def refresh_ledger(farm: str | None = None) -> int:
+    r"""**只维护台账**(不需要任何随包件/交付包):把盘上已经不存在的条目从 `_provenance.json` 里去掉。
+
+    为什么需要它(用户令 2026-09-17 #3):把"非产物"的杂物从产物仓移走后,台账里还留着它们的条目 ——
+    于是反向呼应审计仍把它们当产物统计("帐上 2292 件、盘上 2275 件"这种不一致正是这套台账最该避免的)。
+    本模式只做"账实相符":**在位且来源已登记的照抄,盘上没了的删掉**,不引入任何新来源。
+    """
+    farm = farm or P.farm()
+    root = P.out_root(farm)
+    old = {}
+    f = root / '_provenance.json'
+    if f.is_file():
+        try:
+            old = (json.loads(f.read_text(encoding='utf-8')) or {}).get('files') or {}
+        except Exception:
+            old = {}
+    kept, dropped = {}, []
+    for rel, v in old.items():
+        if (root / rel).is_file():
+            kept[rel] = v
+        else:
+            dropped.append(rel)
+    # 自登记件 (_derived_manifest.json): 在位且台账里没有的补进来
+    added = 0
+    try:
+        from src.derived_manifest import load as _load
+        for rel, info in ((_load(root).get('files') or {})).items():
+            if (root / rel).is_file() and rel not in kept:
+                kept[rel] = dict(source='raw-derived',
+                                 builder=f"{info.get('builder', '?')} (自登记 by {info.get('by', '?')})")
+                added += 1
+    except Exception:
+        pass
+    by = {}
+    for v in kept.values():
+        by[v.get('source', '?')] = by.get(v.get('source', '?'), 0) + 1
+    print(f'台账: {len(old)} 条 → {len(kept)} 条(盘上已删 {len(dropped)} 条, 自登记补入 {added} 条)')
+    for r in dropped[:12]:
+        print(f'   - 移出/删除: {r}')
+    if len(dropped) > 12:
+        print(f'   … 另有 {len(dropped) - 12} 条')
+    print('   来源分布: ' + ' · '.join(f'{k}={v}' for k, v in sorted(by.items())))
+    f.write_text(json.dumps(dict(
+        at=time.strftime('%Y-%m-%d %H:%M:%S'),
+        note='逐件来源台账: raw-derived = 由 data/raw 重算(含验证依据); shipped = 包内无生成端(历史随包件); '
+             'log-relocated = 包内日志按用户令 2 落到 logs/build/',
+        counts=dict(by), files=kept), ensure_ascii=False, indent=1), encoding='utf-8')
+    print(f'已写 {P.rel(f)}')
+    return 0
+
+
 def main() -> int:
     ap = argparse.ArgumentParser()
     ap.add_argument('--dry-run', action='store_true')
     ap.add_argument('--stash', default=None)
     ap.add_argument('--find', action='store_true',
                     help='只看旁边的 .zip 里哪个真含产物 (人工诊断用, 不写盘)')
+    ap.add_argument('--refresh', action='store_true',
+                    help='只维护台账: 把盘上已不存在的条目从 _provenance.json 去掉 (不需要任何随包件)')
     a = ap.parse_args()
+    if a.refresh:
+        return refresh_ledger()
     if a.find:
         found = find_stash_zip()
         if not found:

+ 113 - 15
scripts/products_reverse_audit.py

@@ -104,15 +104,6 @@ FAMILIES: list[dict] = [
          kind='shipped',
          func='本体层 · 领域数据件(油样台账/备件价格/场景修复)', algo='(无生成端, 领域正本出件)',
          gen=None, input=None, pred=(), why='领域正本随包发来, 不由 data/raw 推导'),
-    dict(id='windscada_delivery_notes',
-         glob=['windscada/mblub_alarm_cross.json', 'windscada/release-manifest.json',
-               'windscada/review_presented.json', 'windscada/给振动线_温度轴回复_20260826.json',
-               'windscada/给振动线_温度轴回复_20260826.md', 'windscada/送审_系统结论与判定_20260828.md'],
-         kind='not-product',
-         func='**非产物**:交证/评审往来件(送审结论、给振动线的回复、发布清单快照)',
-         algo='(沟通与评审留痕, 不是产物)', gen=None, input=None, pred=(),
-         why='这些是"人对人的交证件", 内容由分析结论抄写而成, 不随 data/raw 变; 放在产物仓里会让'
-             '"产物"这个词失去意义(它们既无生成端, 也无重算入口)'),
 
     # ── 以下族: 全库 0 处写入方 ⇒ 反向呼应**在原理上不成立**(如实记账, 不假装成立)────
     dict(id='vib_handoff_and_scans', glob='m5_cms_tcm/*.{json,parquet,csv,md,txt}',
@@ -149,13 +140,30 @@ FAMILIES: list[dict] = [
          func='事实契约与对外派生(门户结论段/取数)', algo='scripts/guanlan_facts_contract.py(读者在包内, '
               '输入是 ontology+m5+sop 三处**产物**)', gen=None, input=None, pred=(),
          why='派生链的中间层:它的"输入"本身是产物而非 data/raw ⇒ 与 raw 之间隔着不止一跳'),
+    dict(id='windscada_release_manifest', glob='windscada/release-manifest.json', kind='source-derived',
+         func='发布清单快照(`/healthz` 与版本信息读它)',
+         algo='scripts/guanlan_baseline_manifest.py(从 src/windscada/ui 源重建 zh 页 → 摘要 '
+              '{css_tokens, nav, dom, sha256, source blobs})',
+         gen='scripts/guanlan_baseline_manifest.py', input=None, pred=(),
+         why='★ 2026-09-17 修正: 它一度被我按"交证件"错分成非产物; 实际**包内有生成端**'
+             '(guanlan_baseline_manifest.py 就写这个路径), 且 guanlan_gateway.py 运行期读它 ⇒ 是产物'),
+    dict(id='human_deliverables',
+         glob=['windscada/mblub_alarm_cross.json', 'windscada/review_presented.json',
+               'windscada/给振动线_温度轴回复_20260826.json', 'windscada/给振动线_温度轴回复_20260826.md',
+               'windscada/送审_系统结论与判定_20260828.md'],
+         kind='human',
+         func='**人工正本/交证件**:与振动线的跨线通知、送审结论、发布评审快照',
+         algo='(人写的, 不是算出来的)', gen=None, input=None, pred=(),
+         why='这几件是"人对人的交证件/回执", 内容由分析结论抄写与人判断坐实而成; '
+             'mblub_alarm_cross.json 还被 src/ontology/populate.py 当作**证据来源**引用 ⇒ '
+             '必须随包留在原位, 但**不参与呼应校验**(它本来就没有生成端, 也不该有)'),
     dict(id='scratch_in_products', glob=['**/_*.js', '**/_*audit*.md', '**/*.out', '**/*.err',
                                         '**/*_test_*.parquet', '**/*_test_*.json'],
          kind='not-product',
          func='**非产物**:自审脚本/记录与模型测试输出(躺在产物仓里)', algo='(工具脚本与运行痕迹, 不是产物)',
          gen=None, input=None, pred=(),
-         why='清单里既有审计脚本(`windscada/_audit*.js`)与自审记录(`_*audit*.md`), 也有模型试跑输出'
-             '(`ontology/*_test_*.out/.err`);它们随包发过来、不参与任何取数, 属"产物仓里的杂物"'),
+         why='2026-09-17: 17 件已移出到 reference/非产物留痕_20260917/ 并留了 _来源说明.json ⇒ '
+             '本族现在应为空; 一旦再出现(新脚本往产物仓里写 .out/_audit.js 之类), 本器当场报出来'),
 ]
 
 
@@ -352,6 +360,67 @@ def _input_turbines(station: pathlib.Path, sub: str):
     return out or None
 
 
+def classify_human(farm: str, rels: list[str], cap: int = 600) -> set[str]:
+    """逐件判"是不是人工件"(用户令 2026-09-17 #3:人工件不再按自动产物参与呼应校验)。
+
+    判据:**非测量形态**的件(.md/.json/.csv/.txt)里出现人工判断字段(按字段名匹配)。
+    测量形态件(.parquet/.csv 数值、.npz)一律不算人工件 —— 它们是数据的函数,属"可反推"那一路。
+    """
+    human: set[str] = set()
+    for rel in rels[:cap]:
+        suf = pathlib.Path(rel).suffix.lower()
+        if suf in ('.md', '.txt'):
+            pat = JUDGE_PAT
+        elif suf in ('.json', '.csv'):
+            pat = JUDGE_KEY_PAT
+        else:
+            continue
+        p = P.out_root(farm) / rel
+        try:
+            txt = p.read_text(encoding='utf-8', errors='replace')[:120000]
+        except Exception:
+            continue
+        if pat.search(txt):
+            human.add(rel)
+    return human
+
+
+def write_human_manifest(farm: str | None = None) -> int:
+    """把人工件逐件落成 `outputs/<场>/_human_artifacts.json`(机器台账,供自检与审计引用)。"""
+    farm = farm or P.farm()
+    global LEDGER
+    LEDGER = load_ledger(farm)
+    entries: dict[str, dict] = {}
+    for fam in FAMILIES:
+        if fam['kind'] not in ('shipped', 'human', 'not-product'):
+            continue
+        rels = [r for r in _files_of(fam['glob']) if r not in entries]
+        if not rels:
+            continue
+        if fam['kind'] == 'human':
+            for r in rels:
+                entries[r] = dict(kind='human', family=fam['id'], evidence='族内显式登记为人工正本/交证件')
+            continue
+        for r in classify_human(farm, rels):
+            if r not in entries:
+                entries[r] = dict(kind='human', family=fam['id'],
+                                  evidence='件内含人工判断字段(按字段名匹配),不是输入的纯函数')
+    out = P.out_root(farm) / '_human_artifacts.json'
+    out.write_text(json.dumps(dict(
+        at=__import__('time').strftime('%Y-%m-%d %H:%M:%S'),
+        note='人工件台账(用户令 2026-09-17 #3):这些件由人写成/坐实,不是任何输入的纯函数 ⇒ '
+             '不参与"输出↔输入呼应"校验;随包留在原位,重算不会也不该重写它们。'
+             '本文件由 scripts/products_reverse_audit.py --write-human-manifest 生成。',
+        count=len(entries), files=entries), ensure_ascii=False, indent=1), encoding='utf-8')
+    by_fam: dict[str, int] = {}
+    for v in entries.values():
+        by_fam[v['family']] = by_fam.get(v['family'], 0) + 1
+    print(f'人工件台账: {len(entries)} 件 → {P.rel(out)}')
+    for k, v in sorted(by_fam.items(), key=lambda kv: -kv[1]):
+        print(f'   {k:24s} {v:4d} 件')
+    return 0
+
+
 def audit(farm: str | None = None, verbose: bool = True):
     farm = farm or P.farm()
     global LEDGER
@@ -383,18 +452,35 @@ def audit(farm: str | None = None, verbose: bool = True):
             for r in rels:
                 verdict[r] = dict(fam=fam['id'], verdict='✗',
                                   why='非产物(过程留痕/工具脚本躺在产物仓里): ' + fam.get('why', ''))
+        elif fam['kind'] == 'human':
+            # ★ 人工正本/交证件 (用户令 2026-09-17 #3): 由人写、被运行期当证据引用,
+            #   **不参与呼应校验**——它本来就没有、也不该有生成端; 单独计数, 不混进"无生成端"的失败堆里。
+            row['verdict'] = '◆ 人工件'
+            row['note'] = fam.get('why', '')
+            row['rev'], row['rev_why'] = '人工件(正本/交证)', '人写的, 不参与呼应校验; 随包留在原位'
+            for r in rels:
+                verdict[r] = dict(fam=fam['id'], verdict='◆',
+                                  why='人工件(人工正本/交证件): ' + fam.get('why', ''))
         elif fam['kind'] == 'shipped':
             jp, js, pg = judgement_evidence(farm, rels)
             gen_file = ROOT / fam['gen'] if fam.get('gen') else None
             gen_ok0 = bool(gen_file and gen_file.is_file())
-            fv, fw = feasibility(fam, rels, gen_ok0, False, jp, js, pg)
-            row['verdict'] = '✗ 无生成端'
+            # ★ 逐件分人工件 (用户令 2026-09-17 #3): 含人工判断字段的件**不按自动产物参与呼应校验**,
+            #   单独计 ◆; 余下才是真正的"无生成端"。
+            human = classify_human(farm, rels)
+            rest = [r for r in rels if r not in human]
+            fv, fw = feasibility(fam, rest or rels, gen_ok0, False, jp, js, pg)
+            row['verdict'] = '✗ 无生成端' if not human else f'✗ 无生成端 + ◆ 人工件 {len(human)}'
+            row['n'] = len(rest)
             row['note'] = fam.get('why', '')
             row['rev'], row['rev_why'] = fv, fw
             if jp:
                 row['rev_why'] += f';人工判断字段抽样命中 {jp}%' + (f'(如 {js[0]})' if js else '')
-            for r in rels:
+            for r in rest:
                 verdict[r] = dict(fam=fam['id'], verdict='✗', why='无生成端(全库 0 处写入方): ' + fam['why'])
+            for r in human:
+                verdict[r] = dict(fam=fam['id'], verdict='◆',
+                                  why='人工件(含人工判断字段): 不参与呼应校验, 随包留在原位')
         else:
             # ① 生成端在位
             gen = ROOT / fam['gen'] if fam.get('gen') else None
@@ -412,6 +498,11 @@ def audit(farm: str | None = None, verbose: bool = True):
                 # 跨度口径 —— 技术资料是文档而不是时序数据, 拿跨度比毫无意义)
                 i_n = sum(1 for p in (home / fam['input']).rglob('*') if p.is_file())
                 i_gran = '年'
+            elif fam['kind'] == 'source-derived':
+                # ★ 输入是**包内源码**而不是 data/raw(如发布清单快照: 从 src/windscada/ui 重建页再摘要)。
+                #   生成端在位 = 输入在位, 这类产物的"呼应"是对源码的, 不是对原始件的。
+                i_n = 1
+                i_s = i_e = '包内源码'
             in_ok = i_n > 0
             passed, notes = [], []
             if fam['pred'] and in_ok:
@@ -506,7 +597,7 @@ def audit(farm: str | None = None, verbose: bool = True):
         print(f'  判据失败 {len(fails)} 条' + (':' + ';'.join(fails[:3]) if fails else ''))
         ok = counts.get('✓', 0)
         print(f'  结论: 输出↔输入呼应**成立** {ok} 件 / **不成立(无生成端)** {counts.get("✗", 0)} 件 / '
-              f'无法验证 {counts.get("~", 0)} 件 / 未归类 {counts.get("?", 0)} 件')
+              f'**人工件** {counts.get("◆", 0)} 件 / 无法验证 {counts.get("~", 0)} 件 / 未归类 {counts.get("?", 0)} 件')
         # ── 逆向工程可行性 (用户令 #2): 没有呼应关系的那些, 能不能推出来 ──
         import collections as _c
         by_rev = _c.Counter()
@@ -553,6 +644,9 @@ def doc_block(farm: str) -> str:
     for r in fam_rows:
         by_rev[r.get('rev', '?')] += r['n']
     L += ['', '**按件数汇总**:' + ' · '.join(f'{k} = {v} 件' for k, v in by_rev.most_common()), '',
+          '**人工件**:含人工判断字段的件已逐件登记在 outputs/&lt;场&gt;/_human_artifacts.json(用户令 2026-09-17 #3),'
+          '它们**不参与**呼应校验(人写成/坐实的东西不是任何输入的纯函数);生成方式:'
+          'python scripts/products_reverse_audit.py --write-human-manifest。', '',
           '> 结论口径:`可逆(直接)` = 包内已有生成端,接上即可;`可逆(需反推口径)` = 内容是测量数据的函数,'
           '照 `temp_monthly` 的办法反推 + 逐值对拍;`不可逆(人工判断)` = 逆向工程推不出来,'
           '要么由研发补生成端、要么承认它是人工件(不该按产物管)。', DOC_END]
@@ -578,7 +672,11 @@ def main() -> int:
     ap.add_argument('--farm', default=None)
     ap.add_argument('--check', action='store_true', help='只出结论 (有未归类/判据失败 → rc=5)')
     ap.add_argument('--write-doc', action='store_true', help='把族表写进 docs/系统设计说明.md §13.6')
+    ap.add_argument('--write-human-manifest', action='store_true',
+                    help='生成人工件台账 outputs/<场>/_human_artifacts.json (用户令 2026-09-17 #3)')
     a = ap.parse_args()
+    if a.write_human_manifest:
+        return write_human_manifest(a.farm)
     if a.write_doc:
         return write_doc(a.farm or P.farm())
     _rows, _v, counts, fails, uncl, rc = audit(a.farm, verbose=True)