浏览代码

打包开箱验证的自查修正 + 源代码化清单收口

- scripts/pack_dist.py --verify: 骨架自查两处误报 (2026-09-19 首次开箱验证即卡在这里)
  ① 逐骨架目录 rglob 收集文件 ⇒ 同一文件被父目录重复计入 (报「混进 2 个文件」); 改用集合去重
  ② 按 INCLUDE_FILES 随包的放置说明 data/raw/README_把原始数据放这里.txt 被当成「混进的文件」;
     改为「允许清单 = INCLUDE_FILES 里落在骨架目录内的文件」, 并把它写进通过行
  —— 修好后开箱验证跑通: 30 空目录 + 说明 1 件、无其他文件
- vendor/MANIFEST.json: 补登记另两个便携运行时 tarball (aarch64-linux / win64),
  校验器的「没登记」告警消除 (只影响校验, 不影响运行)
- docs/源代码化清单_v0.1.md: 第 2 行(振动在升/三层基线)与执行顺序 ④ 收口为 ✅,
  并写上本批之后的账 (反向呼应审计 1,800 件全 raw-derived、未归类 0)

开箱验证 (F:\temp\app_guanlang_v2.6.0.zip, 880 MB): 解压 3,492 件 → 离线安装 rc=0 (184s,
Python 3.12.10) → 无窗口启动核验 OK (pythonw + 快捷方式, 无 .vbs) → 副本服务起在备用端口,
页面 5/5 可用 ( / · /detail/ · /cms/ · /ops · /healthz ) → 卸载自检通过 (记录/运行态/快捷方式
全清、data 与 outputs 一件不少、端口已释放) → 副本目录已清理。
zhouyang.xie 4 周之前
父节点
当前提交
56af35353e
共有 3 个文件被更改,包括 38 次插入 和 17 次删除
  1. 6 3
      docs/源代码化清单_v0.1.md
  2. 15 7
      scripts/pack_dist.py
  3. 17 7
      vendor/MANIFEST.json

+ 6 - 3
docs/源代码化清单_v0.1.md

@@ -34,7 +34,7 @@
 | # | 族 | 件 | 喂谁(页面/接口) | 现在没源代码的后果 | 需要的生成端(输入 → 产物) | 可行性 |
 |---|---|---|---|---|---|---|
 | ~~1~~ | ~~`pitch_shipped`~~ | ~~`pitch/pitch_daily.parquet`、`pitch_zero_monthly.parquet`~~ | — | — | **已解决(见 §0 / §2)**:`scripts/pitch_face_build.py`,族表已从 `pitch_shipped`(shipped) 改成 `pitch_face`(raw-derived) | ✅ 完成 |
-| 2 | `vib_handoff_and_scans` 其余件 | `m5_cms_tcm/component_history.json`、`baseline_38.json`、`oem_frequency_scan.parquet` 等扫描件 | 本体 `trend_ingest`(在升/闭环证据)、`vib_confidence`、`maintenance` 数据层页、报告附录 | 趋势/置信度面少一维;数据层页把这几件列成缺件 | `component_history`:由逐窗 L6+标量按部件聚合历史;`baseline_38`:由 `fleet_scalar_z`/标量算三层基线(自/机群/绝对) | **中**:结构简单,但"口径"要与消费端字段对齐(`trend_ingest.py` 读法已可参照) |
+| ~~2~~ | ~~`vib_handoff_and_scans` 其余件~~ | ~~`m5_cms_tcm/component_history.json`、`baseline_38.json`(`oem_frequency_scan.parquet` 等扫描件仍随包)~~ | — | — | **已解决**:`scripts/component_history_build.py`(链 ④d)+ `scripts/baseline_38_build.py`(链 ④e),族表新族 `vib_history_baseline`(raw-derived) | ✅ 完成 |
 | 3 | `tcm_replay` | `tcm_compatible_replay/model/mask_thresholds.tsv` 等 | CMS 报告/逐台页的**厂商黄红阈值线**;`data.load_masks` | 页面只画机群中位,阈值线缺失(现文案:"厂商未设阈值") | 由 `data/raw/如东/windcms` 导出件里的 `red_alarm/yellow_alarm/red_hys/…` 索引列反解出每 (测点,测量,工况) 的中位/黄/红标量 | **中偏低**:索引里确有这些列(54 列之一),口径需反推 + 抽台对拍 |
 | 4 | `ontology_releases` | `ontology/release_r1/*`、`release_r2/*` | 网关 `/release/*`(只读发布层)、门户"发布层"入口 | `/release/` 两件 503(如实报缺) | 由 `ontology/objects.json` + 层清单配置生成 r1/r2 层(`kb_ingest`/`populate` 之后跑一步) | **易**:纯搬运/投影,无判据 |
 | 5 | ~~`guanlan_contract` + `sop_workspace` + `paradigm_r1`~~ | — | 门户结论段 / `/api/facts` / 报告 | **已解决(见 §0)**:由重算台账生成 claim | — | ✅ 完成 |
@@ -113,9 +113,12 @@ flg_wtc_ScInOper_endvalue                               运行段判据 (n_op)
 ① ontology_releases 生成端(易,点亮 /release 两件)              ✅ 完成 (scripts/ontology_release_build.py)
 ② tcm_replay 掩码阈值反解(中偏低)                              ✅ 完成 (scripts/tcm_mask_thresholds_build.py)
 ③ pitch 面生成端(中,口径已定;消费者字段契约见 §2)             ✅ 完成 (scripts/pitch_face_build.py)
-④ component_history / baseline_38 生成端(中)                   ← 下一件
-⑤ vib_figs / 静态逐台副本(低优先)
+④ component_history / baseline_38 生成端(中)                   ✅ 完成 (scripts/component_history_build.py / baseline_38_build.py)
+⑤ vib_figs / 静态逐台副本(低优先)                              ← 剩余(低优先;页面运行期动态出页,静态副本无实际影响)
 ```
 
+**本批之后的账**:`scripts/products_reverse_audit.py --check` = **rc=0**(1,800 件产物全部 raw-derived、
+0 件无生成端、0 件未归类、0 条判据失败);`docs/系统设计说明.md` §13.6 的自动块已随之重写。
+
 每补一族都走同一套纪律:**生成端进包 + 进重算链 + 写进族表(gen/input) + 产物自登记 + 复检页面**,
 并把该族在族表与 `docs/系统设计说明.md` §13.6 里从"无生成端"改成"已在重算链上(附口径与验证依据)"。

+ 15 - 7
scripts/pack_dist.py

@@ -417,23 +417,31 @@ def verify(zip_path: pathlib.Path, keep=False) -> int:
     if miss or enc:
         return 1
     print('   [OK] 引用全部在位, 编码守则通过 (install.ps1 = UTF-8 BOM + CRLF)')
-    # ★2026-09-19 用户令: 包里带"输入数据目录结构"(空目录)。解压后必须真的建出来了, 且**一个文件都没有**
-    #   —— 两边都查: 目录少了 ⇒ 人不知道往哪放; 目录里混进文件 ⇒ 包就不是"只带结构"了。
+    # ★2026-09-19 用户令: 包里带"输入数据目录结构"(空目录)。解压后必须真的建出来了, 且**除说明文件外一个
+    #   文件都没有** —— 两边都查: 目录少了 ⇒ 人不知道往哪放; 目录里混进数据文件 ⇒ 包就不是"只带结构"了。
+    #   ★2026-09-19 实逮 (第一次开箱验证就卡在这): ① 原先用 `rglob` 逐目录收集, 同一个文件会被父目录
+    #   重复计入 (`data` 与 `data/raw` 各算一次) ⇒ 报"混进 2 个文件"; ② 按清单随包发的**放置说明**
+    #   (`data/raw/README_把原始数据放这里.txt`, 在 `INCLUDE_FILES` 里) 被当成"混进的文件"。
+    #   现在: 允许清单 = INCLUDE_FILES 里落在骨架目录内的文件; 收集用集合(去重)。
     skel = data_skeleton()
+    allowed = {f.replace('\\', '/') for f in INCLUDE_FILES if any(f.replace('\\', '/').startswith(r.rstrip('/') + '/')
+                                                                   for r in skel)}
     miss_d = [r for r in skel if not (tmp / r).is_dir()]
-    extra_f = []
+    extra_f = set()
     for r in skel:
         d = tmp / r
         if d.is_dir():
-            extra_f += [str(p.relative_to(tmp)) for p in d.rglob('*') if p.is_file()]
+            extra_f |= {p.relative_to(tmp).as_posix() for p in d.rglob('*') if p.is_file()}
+    extra_f -= allowed
     if miss_d:
         print(f'   [X] 输入数据目录骨架缺 {len(miss_d)} 个: {miss_d[:5]}')
     if extra_f:
-        print(f'   [X] 骨架目录里混进了 {len(extra_f)} 个文件 (应只有空目录): {extra_f[:5]}')
+        print(f'   [X] 骨架目录里混进了 {len(extra_f)} 个文件 (除说明文件外应只有空目录): {sorted(extra_f)[:5]}')
     if miss_d or extra_f:
         return 1
-    print(f'   [OK] 输入数据目录骨架: {len(skel)} 个空目录全部在位, 且无任何文件'
-          f' (用户令 2026-09-19: 带结构不带文件)')
+    print(f'   [OK] 输入数据目录骨架: {len(skel)} 个空目录全部在位'
+          f'{f" + 放置说明 {len(allowed)} 件" if allowed else ""}, 无其他文件'
+          f' (用户令 2026-09-19: 带结构不带数据文件)')
     print('② 离线安装')
     xt = _xfer()
     py = xt.install_in(tmp)

+ 17 - 7
vendor/MANIFEST.json

@@ -1,8 +1,18 @@
-{
- "python/cpython-3.12.14+20260901-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz": {
-  "sha256": "72748da13197c1fb161e3afeef20a6a385ff24f2165e6e2758e47008e7faba4c",
-  "bytes": 34143368,
-  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
- },
- "_built": "2026-09-08 23:24"
+{
+ "python/cpython-3.12.14+20260901-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz": {
+  "sha256": "72748da13197c1fb161e3afeef20a6a385ff24f2165e6e2758e47008e7faba4c",
+  "bytes": 34143368,
+  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
+ },
+ "_built": "2026-09-08 23:24",
+ "python/cpython-3.12.14+20260901-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz": {
+  "sha256": "577b4bec0793ad1ff0cbff9adbd0df078eddde38a4c41bf5d83ad381a85ee39d",
+  "bytes": 29199399,
+  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"
+ },
+ "python/cpython-3.12.14+20260901-x86_64-pc-windows-msvc-install_only_stripped.tar.gz": {
+  "sha256": "7c45c9622400d578709a9b2cddbe8124cc21d382409d9f13406d706d28e31b14",
+  "bytes": 21980728,
+  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
+ }
 }