Parcourir la source

文档: 记下门户拆包与闸门退出码两件事

- docs/数据目录结构与落位约定_v0.2.md §6: release\ 行改为"portal.html 是装配产物 + portal_src 受管外壳",
  并加一段说明壳/件分离的入库边界与 LF 行尾约定; §8 变更记录补两条。
- _修复记录_20260911/README.md: 新增「门户拆包 + 闸门退出码」一节 —— 起因(20MB 里 99.3% 是内嵌交付件)、
  ② 方案的入库/不入库清单、--verify 逐字节一致的实测数字、两个坑(行尾 CRLF / 控制台 GBK 编不出 ✔
  导致"通过报成失败")、以及回归结论与 /cms/ 掉线的复盘(产物开关后必须重启)。
zhouyang.xie il y a 1 mois
Parent
commit
06feff8261

+ 38 - 2
_修复记录_20260911/README.md

@@ -114,8 +114,8 @@ mac venv 解释器、`kb_ingest` 的 mac 技术资料路径、`ingest_ops_2025`
 
 
 
-## 场站扫描辨识 + 两个验收场景 (2026-09-11)
-
+## 场站扫描辨识 + 两个验收场景 (2026-09-11)
+
 **用户令**: 离线数据统一放 `data\raw\`, **下一级目录约定为「场站名称」**, 观澜系统**扫描辨识**。
 
 - `src/windscada/config.py`: 新增 `scan_stations()` / `station_scan()` / `station_report()`; `_expand()`
@@ -181,6 +181,42 @@ mac venv 解释器、`kb_ingest` 的 mac 技术资料路径、`ingest_ops_2025`
 抽验过等价性 (WTG01/WTG02 的 `temp_bins` 与随包件 1296/1296 行逐值相同、med 差 0.0),
 但**未整体重跑** —— 要跑建议先备份 `outputs/rudong/windscada/` 再逐项比对覆盖区间。
 
+## 门户拆包 + 闸门退出码 (2026-09-11 收尾)
+
+**起因**: 有人问 "`release/portal.html` 20 MB 里到底是什么、能不能不进 git"。查下来 99.3% 是内嵌的
+交付文档正文 (28 个 `<template>`: 治理清单分册、单机/整机报告、仿真台面板), 只有 138 KB 是门户自己的壳。
+原先整份文件入库, 每轮重算产物都往 git 塞 20 MB。
+
+**做法 (用户选 ②)**: 拆成"受管外壳 + 不入库内嵌件 + 可验证装配":
+
+| | 入库 | 说明 |
+|---|---|---|
+| `release\portal_src\shell.html` | ✅ | 138,544 B, 内嵌件处留 `<!--@TEMPLATE:id-->`, 资料索引处留 `<!--@GOVERNANCE_SOURCES-->` |
+| `release\portal_src\manifest.json` | ✅ | 各件 sha256 + 期望门户 sha256 + 行尾约定 |
+| `release\portal_src\README.md` | ✅ | 重建说明与两个坑 |
+| `release\portal_src\templates\` | ❌ 产物 | 28 件 20.04 MB (交付件正文) |
+| `release\portal_src\governance_sources.json` | ❌ 产物 | 1,512 B 脱敏资料索引 |
+| `release\portal.html` | ❌ 产物 | 装配结果, 服务/网关照读 |
+
+新增 `scripts\portal_build.py`: `--extract` 拆 / 默认装配 / `--verify` 逐字节比对 / `--check` 漂移检查。
+**实测 `--verify` 两行同为 `sha256 9b6aabeb6ca18d15`, 20,226,052 B —— 拆→装回到同一个文件**; `--check` 全件一致。
+
+**两个坑**:
+
+1. **行尾**: 门户通体 LF (0 处 CRLF)。Python 文本模式在 Windows 上写文件会把 `\n` 变 `\r\n` —— 20 MB
+   整体改写, `/api/version` 的 `portal_sha256` 与页面指纹全变。两个就地注入器
+   (`guanlan_portal_fix_anchors.py` / `guanlan_portal_inject_claims.py`) 补 `newline=""`,
+   `portal_build.py` 全程字节级读写并在装配后自检 CRLF。第一次拆出来的 `portal_src/` 就是这么废的。
+2. **闸门退出码**: 同一类坑让"校验通过"被报成失败 —— 中文控制台代码页 936, `print('… 一致 ✔')`
+   抛 `UnicodeEncodeError` 使进程以 1 退出。`portal_build --verify` 与 `page_fingerprint --diff`
+   各踩一次 (后者是回归闸门: 10 个端点全部回到基线, 却报失败)。新增 `src\console.py` 的 `soft()`
+   把编码错误降级为 `?`, 接入 5 个"以退出码讲话"的脚本; 复跑四个闸门均正确返回 0。
+
+**回归**: 拆包后 10 个端点全部与基线一致 (`portal.home 64b4b81158d09d9c`, `detail.v2 a0abff29c000f570` …)。
+其中一度看到 `/cms/` 掉线 —— 原因是最后那次 `guanlan.py serve` 发生在产物被挪走时, CMS 启动即因
+`src\windcms\data.py` "No objects to concatenate" 退出 (本该如此); 产物还原后重启即恢复
+(414,139 B, 与基线一致)。**教训: 产物开关与重启顺序有关, 挪/还产物后必须重启一遍。**
+
 ## 仍然存在 (不是代码问题)
 
 - `/local-ai/` 仍是 DOWN: 本机 Ollama 没运行, 且 `%USERPROFILE%\.ollama` 下**没有任何模型**

+ 17 - 1
docs/数据目录结构与落位约定_v0.2.md

@@ -176,11 +176,25 @@ Move-Item F:\temp\_raw_off .\data\raw\如东
 | `outputs\<场>\paradigm_r1\` | 29 件 0.5 MB | 范式实验底稿(E3/E5/E8…) | 实验脚本 | ⛔ | 事实契约的 `source_refs` |
 | `outputs\<场>\guanlan\` | 5 件 0.5 MB | 事实契约 `facts_contract_v0.json` + `derived\` + `cloud\`(可上云面孔) | `scripts\guanlan_facts_contract.py` 等 | ⛔ | 问答引文、门户契约段 |
 | `outputs\<场>\pitch\` | 2 件 0.3 MB | 变桨零位月表 | 变桨线 | ⛔ | 变桨面 |
-| `release\` | — | 门户 `portal.html`(19 MB)、三维 `viewer\`、`sim_sys_server.py`、交付件 `如东\` | `scripts\guanlan_portal_*.py` 等 | ⛔ | 网关首页、三维页、交付 |
+| `release\` | — | 门户 `portal.html`(20.2 MB **装配产物**)、三维 `viewer\`、`sim_sys_server.py`、交付件 `如东\`、受管外壳 `portal_src\` | `scripts\portal_build.py`(拆 / 装) | ⛔ | 网关首页、三维页、交付 |
 | `logs\` `run\` | — | 运行日志、`pids.json` | 启动器/服务 | — | 排障 |
 
 > **"能重算"这一列才是关键**:`data\raw` 换数据只会更新 ✅ 那几项;其余要么来自别的源(厂商资料/振动线),要么包内没有生成端(见 §4 的 ⛔ 表)。
 
+**门户的"壳/件分离"(2026-09-11)**:`release\portal.html` 20.23 MB 里 99.3% 是内嵌的交付文档正文
+(28 个 `<template>`:治理清单分册、单机与整机报告、仿真台面板),只有 138 KB 是门户自己的壳。因此:
+
+- `release\portal_src\shell.html`(138 KB,含 `<!--@TEMPLATE:id-->` 与 `<!--@GOVERNANCE_SOURCES-->` 标记)、
+  `manifest.json`(各件 sha256 + 期望门户 sha256)、`README.md` —— **入库**;
+- `release\portal_src\templates\`(20.04 MB 交付件正文)、`governance_sources.json`、`release\portal.html` ——
+  **按产物处理**(`.gitignore`,磁盘文件随包分发、服务照读);
+- `scripts\portal_build.py --extract / (默认装) / --verify / --check`;`--verify` 实测"拆→装"回到
+  **逐字节相同**的文件(`sha256 9b6aabeb6ca18d15…`,20,226,052 B)。行尾必须是 LF —— 三个相关脚本
+  都用字节级写文件(`newline=""`),装配后另有 CRLF 自检。
+- 契约结论段 `<section id="contract-claims">` 是**产物**(由 `guanlan_portal_inject_claims.py` 在装配最后注入,
+  内容来自 `outputs\<场>\guanlan\derived\portal_claims.json`)—— 产物挪走时装出的门户会缺这一段,属预期。
+
+
 ## 7. 跨平台部署与相对路径约定(Windows / Linux)
 
 **三条硬约定**(用户令 2026-09-11;由 `scripts\check_portability.py` 自动门禁):
@@ -235,3 +249,5 @@ P.venv_python()   # 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/pytho
 | 2026-09-11 | 无数据时实时接口不再抛 500,改为结构化"无数据"回答(场景① 逮到) |
 | 2026-09-11 | 两个验收场景实测通过(见 §5);记录油样 102 行华标批的源件缺口(见 §4) |
 | 2026-09-11 | **路径跨平台化**:新增 `src\paths.py` 唯一真源;清理机器绝对路径(mac 检出/TCM 主仓/写死解释器)与 20+ 处 cwd 相对路径;`serve.json` 的 python 改相对;新增静态门禁 `check_portability.py` 与回归尺子 `page_fingerprint.py`;**改造前后 10 个端点指纹逐字节一致**;补本文件 §6 产物地图与 §7 跨平台约定 |
+| 2026-09-11 | **门户拆包**:`release\portal.html` 拆成受管外壳 `release\portal_src\shell.html`(138 KB) + 不入库内嵌件 `templates\`(28 件 20.04 MB)与 `governance_sources.json`;新增 `scripts\portal_build.py`(`--extract/--verify/--check`),实测拆→装**逐字节一致**(见 §6);配套 `git rm --cached release\portal.html` |
+| 2026-09-11 | 验证闸门退出码修复:控制台 GBK 编不出 `✔` 时降级为 `?`(新增 `src\console.py`),修 `page_fingerprint` / `check_portability` / `rebuild_from_raw` / `scan_stations` / `portal_build` —— 此前"全部一致"会因 print 抛异常而**退出码 1(通过被报成失败)** |