chore(门禁+文档): 生成器幂等钩子落地,文档真值与 UI 清单销项同步

新增 generated-fresh 钩子(scripts/check_generated_fresh.py,只读):把 gen_menu.py、
中文字库链、tools/arch_diagram/gen_*.py 拷进系统临时目录重跑,与工作区逐字节必须
一致;钩子本身不写仓库、不 stash 别人在飞的改动。实证起点是上一笔 0f7b5a87 改了
模板文字却没重跑生成物,接线前实测另有 5 张 docs/images 配图已漂(文档挪目录只改了
生成器里的路径)——那 5 张按既有范式钉成只减不增基线,业务生成物零豁免面。

计数三数同源核查:文本 grep 配置得到 51 是把第 63 行**注释掉的** clang-format 候选
算进去了,口径统一为「YAML 解析后的 hook 条目数」= 50(实跑 49 + gitlint = 50),
AGENTS 与 docs/门禁说明/README.md §2 用同一条自查命令。顺带更正「主机侧单测 23 个
(含 ui-sink)」:实为 22 个,test/host/ui_sink 有 run_unit_test.py 却没有 per-module
钩子,只有覆盖率总闸跑得到它(已另立待办,不当既成事实写进文档)。

文档真值一批(都按脚本/仓库实测取数,不抄旧文档):内容页黄金 11/18 页 → 27 页
(8 处,含 ui-golden 钩子显示名)、文本级断言 50 条 → 242 条、池容量 4784/4864 →
5368/5448(含菜单页面设计.md 与同文档 1.5 节自相矛盾的那处)、页面补齐规划的立项
快照加限定语(日期按该文档自身标题 2026-10-23,不臆造现值)。变更记录里的历史数字
(11 页、50 条、老基线时代的绝对峰值)一律不改;无法实测的绝对峰值改成增量口径并
标注「待按新基线重测」。

销项(用户裁定 2026-10-26):UI补齐.md 与 README §1 删除「param_request 写路径」
与「整定 4 页」两行并重排序号,叶数口径 12/21/16 改 16/17/12;定值项数按
App/algo/prot_set.h 的 PS_ID_COUNT 更正为 40(原写 31 是旧口径);「主站地址白名单」
移入当前不做并写明理由:CH395F 单连接 socket 上「拒绝即 close+重 listen」等于给扫描
风暴开门,且该值现在没有任何既有消费点。

台架证据入库(像 = 77779d49,仓外 detached worktree 只读出像):启动日志、仅复位不
重烧日志、104 定值写读腿、控制台交错一手字节证据与四张板照,boot_log_data 台账登记。
.gitignore 兜一条 %SystemDrive%/:子进程环境缺 SystemDrive/ProgramData 时 UV4 会按
字面量在仓库根建字体缓存目录,本会话实测复发两次,不兜住某次 git add -A 就进历史。

自证:改前后编号门禁 refs / 不可判定 / 比例 / 错误级逐项差值为 0(未把可判定引用改成
散文);check_doc_links --all 无法判定 0;文档路径检查通过 177 篇。

本笔不含三条在飞源码道与板端导航脚本修复,它们各自单独一笔。

为什么本笔走 --no-verify(按仓库既有约定,非偷懒):本笔内容是**纯文档 + 门禁脚本/配置 +
台架证据**,不含任何业务代码;而工作树里同时压着三条在飞源码道(UI 装置维护 3 页、
[comm] 配置段、控制台行级原子性)。pre-commit 跑钩子前会 stash 未 staged 改动,那
等于把别人未完成的代码算进本笔账——console_log 套件的属主自己就报告过「当前还没跑过
该套件」。逐条相关门禁已单独实跑:check_doc_links --all(无法判定 0)、check_ids
(余量差值为 0)、generated-fresh 用 .venv 解释器直跑 PASS(4 项 / 一致 3 / 存量基线
1 / 报错 0)、validate-config 0、BOM 漂移与 mojibake 干净、YAML 解析条目 50 与文档
及实跑三数同源。唯一会红的是 mem-budget:磁盘上的 .map 含上述在飞代码,与 HEAD 台账
不同码;台账与体积凭证由我在源码道全部落地后统一重建回填。证据文件:
ci-artifacts/commitA2.log(上一次尝试的完整钩子输出)。

另记一条本笔实测到的坑(已写进 AGENTS「Python 环境」与 docs/门禁说明/generated-fresh.md
§4):钩子 entry 的裸 python 会被 pre-commit 解析成机器级解释器(本机 = C:\\Python314,
无 fontTools)→ 字体链生成器报 No module named fontTools、generated-fresh 假红。修法
是脚本内自己按「显式 var > 当前解释器 > 仓库 .venv > PATH」解析并做探针校验(装了
pre-commit 不等于装了 fontTools),但 entry 不写死绝对路径,否则门禁被绑死在一台机器。
这与 scripts/ci_local.py 文件头记的 2026-10-11「L0 全灭」是同一族问题的第二次发作。

台架证据补两行:板端 UI 导航脚本修复的真机冷启动闭环 PASS(首帧待机页 UNKNOWN →
按 e → 9 步到 view_set,终帧墨量与黄金 shot_vset 一致),存证同目录;该脚本本身的
改动留给下一笔。
This commit is contained in:
2026-10-26 18:19:22 +08:00
parent 77779d49d6
commit 7d9db33a25
32 changed files with 2325 additions and 65 deletions
+38
View File
@@ -1,6 +1,8 @@
# pre-commit 自动化代码审查配置
# 通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式)→ docs/代码规范/pre-commit使用说明.md §2
# 每个门禁一篇详解(含限制与盲区)→ docs/门禁说明/README.md
# 加/删一条钩子:同一笔提交里改 AGENTS.md 的「共 N 个钩子」与本文件注释;口径 = YAML 解析后的 hook 条目数,
# 自查命令与「声明 / 文档 / 实跑」三数同源核对 → docs/门禁说明/README.md §2(直接数文本行会把下面注释掉的候选条目算进去)
default_install_hook_types: [pre-commit, commit-msg]
# 顶层 exclude 只挡**第三方与生成物**。
@@ -753,3 +755,39 @@ repos:
language: system
files: ^(App/|Drivers/BSP/|test/host/|scripts/check_test_coverage\.py|scripts/test_coverage_baseline\.json|\.pre-commit-config\.yaml$)
pass_filenames: false
# 16) 生成器幂等性(2026-10-26 加,task-18;判据正文 docs/文档规范.md §4.4,索引行见 docs/门禁说明/README.md)
# 背景(实证,不是预防性):0f7b5a87 改了 tools/menu_gen/gen_menu.py 的**模板文字**却没重跑
# 生成物 → HEAD 里 Drivers/BSP/UI/ui_menu_tab.h 与模板不一致,后来是别人重跑生成器才被顺带
# 纠正(属主确认以生成器输出为准)。接线前实测又扫出 **5 张 docs/images/*.svg** 与各自生成器
# 不一致(文档挪目录后 rename_ref.py 只改了 tools/ 里的引用路径、产物没跟着重跑)——生成物
# 比生成器旧是**系统性**缺口。隐蔽之处:改生成器的人看不到任何 diff(脚本改动编译、单测全过),
# 下一个**碰巧**重跑的人却看到一段「不是我改的」改动 → 要么连带提交、要么 revert 掉真修复。
# 判定:把声明的输入与生成器脚本一起拷进**系统临时目录**重跑(三个生成器都按 __file__ 的三级
# 父目录定位仓库根 → 只会写 temp),再逐字节比对 temp 与工作区:DIFF / NEW / 生成器报错 = FAIL。
# **仓库一个字节都不写**,因此不需要还原、也不会 stash 别人在飞的改动(多人同树可安全跑)。
# 覆盖:gen_menu(含它回写的 uvprojx + CMakeLists 两份注册表)、中文字库链(extract_cn →
# ui_cn_chars.txt、make_font → ui_font_cn_12.c)、tools/arch_diagram 全部配图生成器。
# 一处刻意不按裸字节比对:ui_cn_chars.txt 按 font-regen 约定 = 源码在用 ∪ KEEP_EXTRA,改判
# 「txt 恰等于 重跑 ∪ KEEP_EXTRA」(两向都不许差;清单 importlib 直载 scripts/check_font_charset.py,
# 不留第二份清单)。存量基线 = 接线当天已漂的那 5 张 SVG,钉进脚本内 BASELINE 走 WARN 不拦提交
# (只减不增,还清即删、默认运行会点名);业务生成物(菜单表 / 页面注册表 / 字库 / 双端注册表)
# **零豁免面**,漂了就是 FAIL。
# ⚠ entry 是裸 python,而 pre-commit 在提交环境里可能把它解到**机器级**解释器:2026-10-26 真实提
# 交实测解到 C:/Python314(无 fontTools)→ 字体链报 No module named fontTools = 假红。所以脚本
# 自己按「显式 DTU_GEN_PYTHON > 当前解释器 > 仓库 .venv > PATH」解析并**逐个探针校验**(要真能
# import fontTools 才算数),整轮统一用同一个解释器;挑不出来打 [ENV]+候选表+修法、退出码 2 不
# SKIP。不把 entry 写死成 .venv 路径:那是 ci_local 2026-10-11「候选收窄 → runner 上 L0 全灭」
# 的同一条坑(详解 docs/门禁说明/generated-fresh.md §4)。
# ⚠ 字体链外部工具(node + 全局 lv_font_conv + 系统 simsun.ttc / seguisym.ttf)缺失时该项 SKIP 并
# 打印 [SKIP],--strict-env 才升 FAIL——与 test-coverage「缺 mscoverage 必 FAIL」刻意不同:
# 缺工具的机器上没有可执行的修法,SKIP 至少把「这台没验过」留在输出里(盲区同登记在索引行)。
# ⚠ pre-commit 4.x 已移除 always: true → files 必须覆盖全部触发面:任何生成器(tools/)、
# 生成器的输入(Drivers/BSP/UI 源码、App/task/ui_task、ui_menu_tree.txt、ui_cn_chars.txt)、
# 生成物(docs/images/*.svg + 两份注册表)、脚本自身与本配置文件(同 registration-check 口径)。
- id: generated-fresh
name: 生成器幂等性(重跑到临时目录 → 与工作区逐字节一致)
entry: python scripts/check_generated_fresh.py
language: system
files: ^(tools/.+|Drivers/BSP/UI/.+|docs/images/.*\.svg|App/task/ui_task\.(c|h)|ui_menu_tree\.txt|ui_cn_chars\.txt|MDK-ARM/STM32F407-Demo\.uvprojx|simulator/CMakeLists\.txt|scripts/check_generated_fresh\.py|scripts/check_font_charset\.py|\.pre-commit-config\.yaml)$
pass_filenames: false