2e608118ce
task-49 盘点盘出的唯一供给缺口,task-50 实现(道H 交付)。为什么这算缺陷而不是「维护工装不值得留痕」:
对时改变的是**其后每一条记录**的时标,而 104 侧的对时**已经留痕** —— 同一类行为两种结果,这个不对称
本身就是缺陷。FTP 上传不留痕则维持原状(那是传输动作,与「装置时钟被改了」不同类),本笔
刻意不扩面到它。
实现:`time_sync_handle_conn` 里 `sys_clock_set_unix` 的返回值原先是**裸丢弃**,现改为接住判
0,==0 才
投 `SYSLOG_OPER`(走 quiet 口,netTask 侧纪律,栈成本已在注释写明);**被拒零投**(不把失败记成改过时
钟);无合并锁存(连续两次对时留两条,不折叠);**epoch 语义零触碰、零时区补偿** —— 时基口径仍在用
户手里,本笔不替产品决定。
新用例四组十二断言:成功恰一条 / 被拒不落痕 / 连续两次两条 / 魔术字不符皆零。假件把 rc 做成可脚本,
并专门钉「文案出现次数」以证不合并;`sys_log.h` 用真实头(纯 stdint,走 task_watchdog 先例),零桩漂移面。
**停发口径的第一批**:新断言**不配 TC 号**,改用语义化用例名 + 函数名追溯,注释里标了口径出处 —— 裁定
能不能落地看的就是第一批,这条留在这里给后来者当样例。
证据:time_sync 92/92(存量 82 零回归);@build **0 错 0 警**,Code=235268 RO=26308 RW
=1572 ZI=136924
(与上一笔的差值是 728d6067 合账所致,不是本笔);栈**以表为证**:timeSyncTask 链 1272→1272 B 零增
量,own frame 152 不变,handle_conn 200 / post_quiet 40 / post 264 逐函数帧全等 ——
按今天刚定稿的栈判
据,「零增量」这句话必须是表,不能是论证。id-check --strict 0(零新号零裸形)、乱码/嵌套/BOM 净。
台账:`ram_baseline.py --update` 已记当日行(IRAM1 61.9% / CCM 87.9%)。覆盖率基线**留给 L
ead 重建** ——
上一版基线是在 time_sync 套件编译不过的破树下打的,少算了该套件的覆盖。
台账同步:本笔代码改了体积(Code=235268),RAM 预算台账受管区与文档快照区按 12:46 那次权威构建回填(ram_baselin
e --write 加 check_mem_numbers --write)。代码与数字不同笔,就是下一次 mem 钩子假红的来源——上一笔 d
ocs 提交被 mem-numbers 与 mem-budget 双双拦下,正是这个原因。
819 lines
60 KiB
YAML
819 lines
60 KiB
YAML
# 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 只挡**第三方与生成物**。
|
||
# ⚠ 2026-10-11 修正:原先在这里整目录挡 `simulator/` 是错的——`simulator/` 下 21 个
|
||
# tracked 文件**全是本项目自研代码**(模拟器入口 main.c、平台后端 sim/*.c、回归脚本
|
||
# tests/*.py、两份配置与 README)。整目录豁免 = 自研代码落在所有钩子之外(连
|
||
# lv-conf-sync 都拦不到);实测把 simulator/ 纳入 cppcheck 后,立刻揪出
|
||
# sim_syslog.c 一处真实的空指针解引用。真正的生成物/第三方(`simulator/build/`、
|
||
# `simulator/SDL2/`、`simulator/shot*.{bin,png}`、`__pycache__/`)**已在 .gitignore 里**,
|
||
# 不会进 git 索引 → 无需在此重复豁免,故直接删掉该条。
|
||
# ⚠ 例外(2026-10-22):`simulator/tests/ref/*.bin`(ui-golden 的像素黄金参考)已从
|
||
# 「整目录 gitignore」改为**入库**——基线不入库时任何新克隆 / 新工作树都必然报
|
||
# 「缺黄金参考」,这条门禁就只在有老副本的机器上真实存在。入库前提是实测可复现:
|
||
# 两份各自独立构建的 lvgl_sim(不同构建目录、相隔 11 天的代码)对同一批 11 个 .bin
|
||
# SHA-256 全部一致。改写型钩子(trailing-whitespace / end-of-file-fixer)声明
|
||
# `types: [text]`,位图含 NUL → 被判二进制自动 Skipped(实测「索引 blob == 磁盘 ==
|
||
# 参照」三方一致),无需为它另加 exclude。
|
||
exclude: |
|
||
(?x)^(
|
||
\.opencode/node_modules/|
|
||
Lib/lvgl/|
|
||
MDK-ARM/RTE/
|
||
)
|
||
|
||
repos:
|
||
# 1) 通用钩子(官方)
|
||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||
rev: v5.0.0
|
||
hooks:
|
||
- id: trailing-whitespace
|
||
# 豁免 **CubeMX / Keil 生成的文件**(锚名 cubemx-generated,end-of-file-fixer 复用):
|
||
# 这类文件的内容事实源是 `.ioc` 与工具链,每次重生成都按工具自己的风格写回
|
||
# (行尾空白、文件末尾不带换行)。2026-10-09 实测:每次重生成后 `Src/`/`Inc/`
|
||
# 下 10+ 个文件各被补 1 行末尾空行,纯属提交抖动。
|
||
# ⚠ 只豁免**改写型**钩子(trailing-whitespace / end-of-file-fixer);
|
||
# cppcheck / 乱码 / 存活审计 / 任务表 / 内存数字等**只读检查**仍覆盖
|
||
# `Src/`、`Inc/`——`cubemx-survival` 必须能看到这些文件。
|
||
exclude: &cubemx-generated |
|
||
(?x)^(
|
||
Src/.*\.(c|h)$|
|
||
Inc/.*\.(c|h)$|
|
||
Drivers/CMSIS/|
|
||
Drivers/STM32F4xx_HAL_Driver/|
|
||
Middlewares/|
|
||
MDK-ARM/.*\.(uvprojx|uvoptx|sct|map)$|
|
||
\.mxproject$|
|
||
.*\.ioc$
|
||
)
|
||
- id: end-of-file-fixer
|
||
exclude: *cubemx-generated
|
||
- id: check-added-large-files
|
||
- id: check-merge-conflict
|
||
- id: check-yaml
|
||
|
||
# 2) C 代码格式:只检查,绝不改写(编码铁律)
|
||
# Route A:暂不启用,待全库格式化基线后再放开
|
||
# - repo: https://github.com/pre-commit/mirrors-clang-format
|
||
# rev: v19.1.4
|
||
# hooks:
|
||
# - id: clang-format
|
||
# args: [--dry-run, --Werror]
|
||
|
||
# 3) commit message 格式(宽松规则,兼容中文标题)
|
||
- repo: https://github.com/jorisroovers/gitlint
|
||
rev: v0.19.1
|
||
hooks:
|
||
- id: gitlint
|
||
|
||
# 4) 本地自定义钩子(仓库级)
|
||
- repo: local
|
||
hooks:
|
||
- id: cppcheck
|
||
name: cppcheck
|
||
entry: cppcheck
|
||
# 注意:YAML 流序列中逗号是元素分隔符,含逗号/空格的参数必须加引号
|
||
args: ['--enable=warning,performance,portability,style', '--error-exitcode=1',
|
||
'--inline-suppr',
|
||
'--std=c99', '--quiet',
|
||
'--suppress=missingIncludeSystem', '--suppress=missingInclude',
|
||
'--suppress=invalidPrintfArgType_sint', '--suppress=invalidPrintfArgType_uint',
|
||
'--suppress=unusedFunction', '--suppress=unusedStructMember',
|
||
'--suppress=noExplicitConstructor', '--suppress=useStlAlgorithm',
|
||
'--suppress=constParameterPointer', '--suppress=constParameterCallback',
|
||
'--suppress=redundantAssignment',
|
||
'--suppress=normalCheckLevelMaxBranches',
|
||
'-I', 'Inc', '-I', 'App', '-I', 'Drivers/BSP',
|
||
'-I', 'Drivers/STM32F4xx_HAL_Driver/Inc', '-I', 'Drivers/CMSIS/Include',
|
||
'-I', 'Middlewares/Third_Party/FreeRTOS/Source/include']
|
||
language: system
|
||
types: [c]
|
||
# 仅扫描项目自有代码;排除第三方库与 LVGL 依赖文件
|
||
# (UI 生成文件 ui_font_cn_12/ui_menu_tab 由生成器按工程约定产出,纳入受检)
|
||
exclude: |
|
||
(?x)^(
|
||
Drivers/CMSIS/|
|
||
Drivers/STM32F4xx_HAL_Driver/|
|
||
Drivers/BSP/NET/lftpd/|
|
||
Drivers/BSP/dhara/|
|
||
Lib/|
|
||
Middlewares/|
|
||
MDK-ARM/|
|
||
test/
|
||
)
|
||
# 4b) test/ 代码宽松静态检查(S5,2026-10-03 加)
|
||
# 背景:`test/` 此前完全豁免 pre-commit(靠"自律"),而 `test/board/` 下
|
||
# 6300+ 行 C 代码**且编入固件**(曾出现 `s_rx_buf` 使用先于声明)。
|
||
# 本钩子**只开 warning**(不引入存量 style 噪音),排除模拟器桩
|
||
# (`test/host/*/stub/`,第三方/桩头形态不受控)。
|
||
# 基线:2026-10-03 全量实测 **0 条 warning**(存量可控,故直接挂钩子)。
|
||
# 依据:docs/审查记录/测试与架构改进建议.md §3.4;实机测试实施指南.md §8.1
|
||
- id: test-cppcheck
|
||
name: cppcheck (test/, warning only)
|
||
entry: cppcheck
|
||
args: ['--enable=warning', '--error-exitcode=1',
|
||
'--inline-suppr', '--std=c99', '--quiet',
|
||
'--suppress=missingIncludeSystem', '--suppress=missingInclude',
|
||
'--suppress=invalidPrintfArgType_sint', '--suppress=invalidPrintfArgType_uint',
|
||
'--suppress=unusedFunction', '--suppress=unusedStructMember',
|
||
'--suppress=constParameterPointer', '--suppress=constParameterCallback',
|
||
'--suppress=redundantAssignment',
|
||
'--suppress=normalCheckLevelMaxBranches',
|
||
'-i', 'test/host/watchdog/stub',
|
||
'-i', 'test/host/cfg_file/stub',
|
||
'-i', 'test/host/sys_clock/stub',
|
||
'-I', 'Inc', '-I', 'App', '-I', 'Drivers/BSP',
|
||
'-I', 'Drivers/STM32F4xx_HAL_Driver/Inc', '-I', 'Drivers/CMSIS/Include',
|
||
'-I', 'Middlewares/Third_Party/FreeRTOS/Source/include',
|
||
'test/']
|
||
language: system
|
||
# 触发条件:test/ 下 C/头文件变动;扫描范围恒为整个 test/(pass_filenames=false)
|
||
files: ^test/.*\.(c|h)$
|
||
pass_filenames: false
|
||
- id: no-mojibake
|
||
name: Forbid U+FFFD mojibake
|
||
entry: python scripts/check_mojibake.py
|
||
language: system
|
||
types: [c]
|
||
- id: coding-standard
|
||
name: C coding standard (规范V1.0)
|
||
entry: python scripts/check_coding_standard.py
|
||
language: system
|
||
types: [c]
|
||
# 范围与 cppcheck 钩子一致;另排除 CubeMX 生成文件(Src/、Inc/,重新生成即覆盖)、
|
||
# dhara/FatFS 接口适配层(nand_ftl,函数名由外部 API 固定)
|
||
exclude: |
|
||
(?x)^(
|
||
Drivers/CMSIS/|
|
||
Drivers/STM32F4xx_HAL_Driver/|
|
||
Drivers/BSP/NET/lftpd/|
|
||
Drivers/BSP/GD5F2GQ5UE/nand_ftl\.|
|
||
Drivers/BSP/dhara/|
|
||
Lib/|
|
||
Middlewares/|
|
||
MDK-ARM/|
|
||
test/|
|
||
Src/|
|
||
Inc/
|
||
)
|
||
|
||
# 5) 文档一致性门禁
|
||
# 存在性:md 互链 / 反引号路径是否指向真实文件(防迁移/删除后残留死链)
|
||
# 章节:`§N.M` 必须能在目标文档的编号标题里找到(--sections-strict:自引用失效也拦)
|
||
- id: doc-links
|
||
name: 文档链接存在性
|
||
entry: python scripts/check_doc_links.py --sections-strict
|
||
language: system
|
||
files: \.md$
|
||
# 外部资料(芯片手册转录)链接形态不受控;第三方目录整体排除
|
||
# (`simulator/` 已不再排除:其下 3 个 README 是本项目文档,2026-10-11)
|
||
exclude: |
|
||
(?x)^(
|
||
\.opencode/node_modules/|
|
||
Lib/lvgl/|
|
||
MDK-ARM/RTE/|
|
||
docs/芯片手册/
|
||
)
|
||
# 数值:README/AGENTS 任务表须与 Src/freertos.c 一致
|
||
- id: task-table
|
||
name: FreeRTOS 任务表一致性
|
||
entry: python scripts/gen_task_table.py --check
|
||
language: system
|
||
files: ^(README\.md|AGENTS\.md|Src/freertos\.c)$
|
||
pass_filenames: false
|
||
# 体积:内存/Flash 数字须与最新 .map 一致(体积档)
|
||
# 只在内存文档或代码变动时触发;.map 未生成时脚本自身给出明确提示
|
||
- id: mem-numbers
|
||
name: 内存/Flash 数字一致性(体积档)
|
||
entry: python scripts/check_mem_numbers.py --scan
|
||
language: system
|
||
files: ^(docs/内存管理/.*\.md|scripts/check_mem_numbers\.py|App/.*\.c|App/.*\.h|Src/.*\.c|Inc/.*\.h)$
|
||
pass_filenames: false
|
||
|
||
# 5d) 两份 lv_conf.h 特性子集一致性(2026-10-11 加)
|
||
# 背景:`Lib/lv_conf.h`(MCU)与 `simulator/lv_conf.h`(PC)是两份独立文件,
|
||
# 此前长期漂移——模拟器早已 I1-only,MCU 端却让 11 个 `LV_DRAW_SW_SUPPORT_*`、
|
||
# 5 个未用控件类、`LV_USE_THEME_SIMPLE`、`LV_USE_FLEX/GRID` 靠 lv_conf_internal
|
||
# 默认值隐式生效 → 固件白链 **64,576 B 死代码**,且模拟器 golden 回归跑的是
|
||
# 另一套特性子集(等于验证不到固件实际编译的配置)。
|
||
# 口径:比对**生效值**(文件未定义则取 lv_conf_internal.h 的兜底默认),
|
||
# 并抓"写了 LVGL 不认的宏名"(`LV_USE_THEME_BASIC` 同类,实测两端共 26 处)
|
||
# 与同文件重复定义。规则正文见 docs/UI设计/UI_设计架构.md §1.4 第 10 条。
|
||
# ⚠ 触发范围(2026-10-11 两次修正):本钩子由**两端任一配置**或脚本自身触发。
|
||
# 原先顶层 exclude 整目录挡掉 `simulator/`,导致"只改 simulator/lv_conf.h"
|
||
# 的提交拦不到;已删该条(见文件顶部 exclude 注释),故这里可直接匹配
|
||
# `simulator/lv_conf.h`。(pre-commit 4.x 已移除 `always: true`,
|
||
# 实测对 local 钩子只告警并跳过——不能靠它兜底。)
|
||
- id: lv-conf-sync
|
||
name: 两份 lv_conf.h 特性子集一致性
|
||
entry: python scripts/check_lv_conf_sync.py
|
||
language: system
|
||
files: ^(Lib/lv_conf\.h|simulator/lv_conf\.h|scripts/check_lv_conf_sync\.py)$
|
||
pass_filenames: false
|
||
|
||
# 6) CubeMX 重生成存活审计(2026-09-29 加)
|
||
# 背景:.ioc 的 KeepUserCode 只保护 USER CODE 标记区;标记之外的手工改动
|
||
# 重生成即被覆盖,且多数不报编译错(崩溃采集被打回空 while(1)、加固宏被删)。
|
||
# 本钩子验证"重生成易失清单"各项是否仍在预期位置,并对关键区做哈希漂移检测。
|
||
# 范围:仅在这些易失文件变动时触发(改动 .ioc 或生成文件本身就值得复核)。
|
||
- id: cubemx-survival
|
||
name: CubeMX 重生成存活审计
|
||
entry: python scripts/check_cubemx_survival.py --check
|
||
language: system
|
||
files: ^(STM32F407-Demo\.ioc|Src/.*\.(c|h)|Inc/.*\.h|App/util/task_watchdog\.(c|h)|MDK-ARM/.*\.uvprojx|scripts/check_cubemx_survival\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7) 看门狗判定状态机主机侧单元测试(2026-10-01 加)
|
||
# 背景:task_watchdog.c 的判定逻辑(位图取走清零 / 掩码 / miss_streak
|
||
# 累计 / 100ms 节流 / tick 回绕)是纯逻辑,板端套件无法穷举边界;本钩子
|
||
# 用 MSVC 离线编译真实源文件(#include 纳入)并用可观测桩驱动,秒级、零
|
||
# 硬件、不占 MCU RAM。仅在被测文件或其平台层 / 测试变动时触发。
|
||
# 详见 docs/测试说明/看门狗单元测试说明.md
|
||
- id: watchdog-unit-test
|
||
name: 看门狗判定状态机单测(主机侧)
|
||
entry: python test/host/watchdog/run_unit_test.py
|
||
language: system
|
||
files: ^(App/util/task_watchdog\.(c|h)|test/host/watchdog/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7b) 系统时钟时间换算主机侧单元测试(2026-10-02 加)
|
||
# 背景:sys_clock.c 的 Unix↔日历换算(闰年 / 月末 / 世纪 / 2038 上限)、
|
||
# 星期(Zeller)、"30 天惰性再基准化"(防 49.7 天 tick 回绕)、RTC 故障
|
||
# 降级路径均为纯逻辑;本钩子用 MSVC 离线编译真实源文件(#include 纳入)
|
||
# 并以可注入桩驱动(可控时钟 + 阴影 RTC)。首次落地即抓出 Zeller 公式
|
||
# 缺陷(1970~2099 全部日期星期皆错,详见测试说明文档)。
|
||
# files 含 sys_clock.c 依赖的 sd2506.h(时间结构/返回值定义)。
|
||
- id: sys-clock-unit-test
|
||
name: 系统时钟换算单测(主机侧)
|
||
entry: python test/host/sys_clock/run_unit_test.py
|
||
language: system
|
||
files: ^(App/util/sys_clock\.(c|h)|Drivers/BSP/SD2506/sd2506\.h|test/host/sys_clock/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7c) 配置引擎主机侧单元测试(2026-10-02 加,S1-b)
|
||
# 背景:cfg_file.c 的核心是"文本 <-> 结构"纯变换(INI 段/键解析、四种值
|
||
# 类型编解码、外科手术式保存、超长守卫、load/save/poll 契约);板端套件只
|
||
# 保存到缺失/新文件(走 s_gen_document 路径),**从不触发对已存在文件的外科
|
||
# 手术改写**,故掩盖了该路径的缺陷。本钩子用 MSVC 离线编译真实源文件
|
||
# (#include 纳入)+ 内存虚拟文件系统后端驱动,首次落地即抓出 P0:
|
||
# `s_encode(...) == 0` 恒假(其成功返回文本长度而非 0)使**外科手术保存的
|
||
# 受管值改写路径沦为死码**——对已存在 /config.ini 保存时受管键从不改写(静默
|
||
# 不落盘,且 net_cfg_save 仍报 OK)。详见测试说明文档。
|
||
- id: cfg-file-unit-test
|
||
name: 配置引擎单测(主机侧)
|
||
entry: python test/host/cfg_file/run_unit_test.py
|
||
language: system
|
||
files: ^(App/cfg/cfg_file\.(c|h)|App/cfg/cfg_file_store\.h|App/log/sys_log\.h|test/host/cfg_file/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7d) 相量层(L2)主机侧单元测试(2026-10-10 加)
|
||
# 背景:App/algo/ 为**纯算法**层(无寄存器、无 RTOS、无外设依赖),是本项目
|
||
# 首个可完全离线验证的算法层。板端**无信号源**,无法注入已知幅值/相位/频率的
|
||
# 标准波形,故"读数 = 设定值"这一核心判据在板上不可验;本钩子用 MSVC 离线编译
|
||
# 真实源文件(#include 纳入,无需桩)+ 合成任意波形,把系数表、幅值/相位、
|
||
# 直流与谐波抑制、频偏敏感度、滑动窗、双区交换一次性钉死。
|
||
# files 含被测的两个源与其头,以及 harness 本身。
|
||
- id: phasor-unit-test
|
||
name: 相量层单测(主机侧)
|
||
entry: python test/host/phasor/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/(phasor|sample_ring)\.(c|h)|test/host/phasor/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7e) ADC 输入层(L1)主机侧单元测试(2026-10-10 加)
|
||
# 背景:adc_input.c 的核心是"通道号 <-> Rank 顺序 <-> 语义"三者映射的正确性;
|
||
# 板端无信号源,无法注入已知电压验证"读到的是哪只脚"(映射说明 §8)。本钩子用
|
||
# MSVC 离线编译真实源文件(#include 纳入)+ HAL/TPAFE 可注入桩:给每个 Rank
|
||
# 注入互不相同的码值以**逐通道钉死映射**,并一次钉死 HAL 启动失败 / 转换超时 /
|
||
# 参数错等真机难复现的分支。
|
||
# files 含被测源与其头、两个桩头所在目录,以及 harness 本身。
|
||
- id: adc-input-unit-test
|
||
name: ADC 输入层单测(主机侧)
|
||
entry: python test/host/adc_input/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/adc_input\.(c|h)|test/host/adc_input/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7f) 卷锁(vol_lock)主机侧单元测试(2026-10-10 加)
|
||
# 背景:vol_lock 的**核心不变量是 fail-open**——互斥量创建失败时
|
||
# `vol_lock_wait()` 必须**返回 1 且绝不阻塞**,因为它的调用者 defaultTask 是
|
||
# 看门狗喂狗点,一旦在此阻塞即整机复位。板端该路径**只在内存耗尽时才可能走到**,
|
||
# 无法人为复现;本钩子用 MSVC 离线编译真实源文件(#include 纳入)+ CMSIS 互斥桩,
|
||
# 把"创建失败 -> 直接放行"、"创建成功 -> 正常加解锁"、"未持锁释放"、
|
||
# 超时参数透传等分支一次性钉死。
|
||
# files 含被测源与其头,以及 harness 本身。
|
||
- id: vol-lock-unit-test
|
||
name: 卷锁单测(主机侧)
|
||
entry: python test/host/vol_lock/run_unit_test.py
|
||
language: system
|
||
files: ^(App/fs/vol_lock\.(c|h)|test/host/vol_lock/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7f2) 卷空闲同步(写回缓存空闲 flush)主机侧单元测试(2026-10-25 加,道F 批 2)
|
||
# 背景:该模块是写回缓存掉电窗口的兜底网——掉电与"脏页驻留"本身台架
|
||
# 不可按需构造;卷忙跳过 / ioctl 失败边沿 latch / 32 位 tick 回绕 /
|
||
# 失败路径成对放锁(不得空放)全是纯状态逻辑。本钩子用 MSVC 离线编译
|
||
# 真实源文件(#include 纳入;桩:可控时钟 + 事件位图 + 卷忙注入 +
|
||
# 可注入 disk_ioctl),把四级门与 latch 分支一次钉死。
|
||
# files 含被测源与其头、就绪位属主 sys_events 的消费面 default_task.c、
|
||
# 以及 harness 本身。
|
||
- id: vol-idle-sync-unit-test
|
||
name: 卷空闲同步单测(主机侧)
|
||
entry: python test/host/vol_idle_sync/run_unit_test.py
|
||
language: system
|
||
files: ^(App/fs/vol_idle_sync\.(c|h)|App/task/default_task\.c|test/host/vol_idle_sync/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7g) 时间同步(time_sync)主机侧单元测试(2026-10-10 加)
|
||
# 背景:time_sync.c 是 TCP 时间同步协议状态机,也是探针窗口心跳缺口那项看门狗风险的**全部
|
||
# 逻辑贡献者**——"listen 重建 -> 800ms~1s 心跳间隔"由本模块的分片延时/退避/
|
||
# 结算等待组成,板端只能看到"是否复位"这一结果,看不到各段延时是否如设计。本钩子
|
||
# 用 MSVC 离线编译真实源文件(#include 纳入)+ 可脚本化的网络/事件桩(含 longjmp
|
||
# 跳出 for(;;) 任务循环),把分片喂狗节奏、监听退避、报文重组(半包/粘连/EOF/真错/
|
||
# 预算耗尽/tick 回绕)、重听门控逐条钉死。
|
||
# files 含被测源与其头、被测的真实看门狗头,以及 harness 本身。
|
||
- id: time-sync-unit-test
|
||
name: 时间同步单测(主机侧)
|
||
entry: python test/host/time_sync/run_unit_test.py
|
||
language: system
|
||
files: ^(App/sync/time_sync\.(c|h)|App/util/task_watchdog\.h|test/host/time_sync/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7h) ADC 采样有效性校验主机侧单元测试(2026-10-11 加)
|
||
# 背景:adc_health.c 是"静默/丢帧/序号倒退/错误计数增量"的判定层——这些
|
||
# 故障在健康台架上**不可复现**(复现 = 拔芯片/改驱动),板端闭环只能证明
|
||
# "健康时不误报",证不了"故障时必报";100ms 门限、tick 回绕、事件边沿
|
||
# 语义等边界只有合成计数序列能精确构造。本钩子用 MSVC 离线编译真实源文件
|
||
# (#include 纳入,纯逻辑零桩)把 11 组判据一次性钉死。
|
||
# files 含被测源与其头、adc_task.c(告警编排的消费方),以及 harness 本身。
|
||
- id: adc-health-unit-test
|
||
name: ADC 有效性校验单测(主机侧)
|
||
entry: python test/host/adc_health/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/adc_health\.(c|h)|App/task/adc_task\.c|test/host/adc_health/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7h2) 通道标定层(L1.5,「数据可信」卡 2/卡 4)主机侧单元测试(2026-10-25 加,道A)
|
||
# 背景:标定折算是"码值→工程量"的唯一插入点,Q16 系数/零点两段落盘 +
|
||
# 合法带哨兵回退 + 两点标定公式——板端无标准源、坏文件注入路径在真机
|
||
# 上难以复现,只有离线端能逐系数钉死。钩子用 MSVC 编译真实源文件
|
||
# (#include 纳入;桩:dbg_log / cfg_file_register / adc_input_desc 强符号)。
|
||
# files 含被测源与头、系数口径联动的 adc_health/cfg_file/adc_task 消费方,
|
||
# 以及 harness 本身。
|
||
- id: adc-cal-unit-test
|
||
name: 通道标定层单测(主机侧)
|
||
entry: python test/host/adc_cal/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/adc_cal\.(c|h)|App/algo/adc_health\.(c|h)|App/cfg/cfg_file\.(c|h)|App/task/adc_task\.c|test/host/adc_cal/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
# 7h3) 量纲回归(「数据可信」卡 7,道A)主机侧单元测试(2026-10-25 加)
|
||
# 背景:台架曾长期缺"不该动作时不动作"的判据,码值/工程量错配曾以
|
||
# 恒假动作的形态混过验收。本钩子用真实链(adc_cal→ring→phasor→
|
||
# prot_elm)+ 显式换算的合成波把七类元件的量纲钉死:折算点被旁路
|
||
# 或量纲再错位,断言立刻翻转。files 覆盖链上全部生产源与两处编排。
|
||
- id: prot-dim-unit-test
|
||
name: 量纲回归单测(主机侧)
|
||
entry: python test/host/prot_dim/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/(adc_cal|prot_elm|prot_set|phasor|sample_ring|freq)\.(c|h)|App/task/(adc_task|protect_task)\.c|test/host/prot_dim/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7h4) 遥测快照层(L2 发布侧,「数据可信」卡 8,道D)主机侧单元测试(2026-10-25 加)
|
||
# 背景:卡 8 的 DoD 一半是并发与结构类判据——双区切换无撕裂(撕裂窗
|
||
# 200 ms 量级,台架人工比对观测不到)、seq 只增、§4.5 布局与字段偏移
|
||
# (卡 9/UI 直接按整体拷贝消费,破布局 = 破协议)、CLIPPED 品质汇总
|
||
# (削顶位图要等卡 12 标定才真实出现)。主机侧以 #include 原样纳入
|
||
# meas/phasor/sample_ring 生产源,合成 9 通道帧序列一次性钉死;
|
||
# 读数与注入源比对类判据仍归台架(实测采样类)。files 覆盖被测源、
|
||
# 同链核(phasor/sample_ring)、编排方 adc_task 与 harness 本身。
|
||
- id: meas-unit-test
|
||
name: 遥测快照层单测(主机侧)
|
||
entry: python test/host/meas/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/meas\.(c|h)|App/algo/(phasor|sample_ring)\.(c|h)|App/task/adc_task\.c|test/host/meas/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7h5) 运行参数段(「数据可信」卡 10,道D)主机侧单元测试(2026-10-25 加)
|
||
# 死区把守突发上报的量、104 整定段是现场唯一远程写入口:注册契约、
|
||
# 分档量程守卫、两段式、系数直通的合法带与生效值回显必须离线钉死。
|
||
- id: run-par-unit-test
|
||
name: 运行参数段单测(主机侧)
|
||
entry: python test/host/run_par/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/run_par\.(c|h)|App/algo/adc_cal\.(c|h)|App/cfg/cfg_file(_store)?\.(c|h)|test/host/run_par/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
|
||
# 7i) 保护定值模型(定值校验和载体)主机侧单元测试(2026-10-11 加)
|
||
# 定值是保护动作的"输入宪法":范围校验、整表装载 all-or-nothing、
|
||
# 校验和封存-自检-闭锁(参考 tagPRS dwTagSum/dwTagSumN 模式)必须在
|
||
# 任何元件接线之前钉死——板端无法穷举越界定值注入,主机侧毫秒级全跑。
|
||
- id: prot-set-unit-test
|
||
name: 保护定值模型单测(主机侧)
|
||
entry: python test/host/prot_set/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/prot_set\.(c|h)|test/host/prot_set/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7j) 保护元件框架(L3 P1 子集)主机侧单元测试(2026-10-11 加)
|
||
# 判据核(滞回/定时限/欠量方向/闭锁联动/时基回绕)是纯逻辑,
|
||
# 板端复现需要标准源 + 故障注入,主机侧用合成测量序列即可穷举
|
||
# 1.2×/0.9× 验收口径(规划 §8)。
|
||
- id: prot-elm-unit-test
|
||
name: 保护元件框架单测(主机侧)
|
||
entry: python test/host/prot_elm/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/prot_elm\.(c|h)|App/algo/prot_set\.(c|h)|App/algo/phasor\.(c|h)|App/algo/freq\.(c|h)|test/host/prot_elm/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7j2) 频率测量层(L2.5,B8)主机侧单元测试(2026-10-12 加)
|
||
# 自适应窗测频(K_eff 对齐负序泄漏纹波周期)的 J1~J6 判据
|
||
# (扫频/噪声/收敛/阶跃/浮空 valid=0/钳制恢复)是纯合成信号
|
||
# 可穷举的精度契约;板端无标准源照不出来(规划 §3.3 过频 / 低频)。
|
||
- id: freq-unit-test
|
||
name: 频率测量单测(主机侧)
|
||
entry: python test/host/freq/run_unit_test.py
|
||
language: system
|
||
files: ^(App/algo/freq\.(c|h)|App/algo/phasor\.(c|h)|App/algo/sample_ring\.(c|h)|test/host/freq/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7k) SHA-256 主机侧单元测试(2026-10-11 加,FTP 鉴权)
|
||
# sha256 是 FTP pwd_hash 校验的地基——哈希算错是"正确口令被拒 /
|
||
# 错误口令放行"的静默语义错误,板端只见 530/230 看不出根因;
|
||
# FIPS 180-4 标准向量(空串/abc/56B 补位跨界/1MB)+ hex_eq 非法输入
|
||
# 在主机侧毫秒级钉死。零平台依赖,无桩。
|
||
- id: sha256-unit-test
|
||
name: SHA-256 单测(主机侧)
|
||
entry: python test/host/sha256/run_unit_test.py
|
||
language: system
|
||
files: ^(App/util/sha256\.(c|h)|test/host/sha256/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7l) FTP 鉴权状态机主机侧单元测试(2026-10-11 加,FTP 鉴权)
|
||
# 锁定 60s/解锁/成功清零/tick 32 位回绕是"防口令爆破"契约核心,
|
||
# 板端每验一条要真等 60s、回绕要跑满 49.7 天;lftpd_auth 纯函数
|
||
# 设计(tick 参数注入)就是为了让这些分支主机侧毫秒级穷举。
|
||
- id: ftp-auth-unit-test
|
||
name: FTP 鉴权状态机单测(主机侧)
|
||
entry: python test/host/ftp_auth/run_unit_test.py
|
||
language: system
|
||
files: ^(Drivers/BSP/NET/lftpd/lftpd_auth\.(c|h)|App/util/sha256\.(c|h)|test/host/ftp_auth/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7m) IEC 60870-5-104 被控站协议栈主机侧单元测试(2026-10-12 加,B4)
|
||
# APCI 重组/STARTDT/总召分片/COV/k-w-t1~t3/遥控定值两段式/C_CS/错误
|
||
# 应答——link 层 tick 注入 + pts 数据源 vtable 抽象就是为 L0.5 全离线
|
||
# 穷举设计(分层约束);板端复现这些分支要真主站+真等待。
|
||
# 触发面含数据源依赖(prot_set/prot_elm/phasor/sample_ring)——测试
|
||
# 直接 #include 这些生产源,签名变了要立刻在这里暴露。
|
||
- id: iec104-unit-test
|
||
name: IEC104 协议栈单测(主机侧)
|
||
entry: python test/host/iec104/run_unit_test.py
|
||
language: system
|
||
# 卡 10 起本 TU 链入真实 cfg_file/adc_cal/run_par(板端真源链),触发面同步扩
|
||
files: ^(App/protocol/iec104/iec104_(asdu|pts|link)\.(c|h)|App/algo/(prot_set|prot_elm|phasor|sample_ring|adc_input|freq|meas|adc_cal|run_par)\.(c|h)|App/cfg/cfg_file(_store)?\.(c|h)|test/host/iec104/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7m2) SOE 动作记录编解码主机侧单元测试(2026-10-12 加,B9)
|
||
# 64 B 定长记录(类型/seq/CP56Time2a/epoch/校验和)是磁盘上的
|
||
# 字节级契约——编解码错了要 FTP 下载+解码才看得见;纯 codec
|
||
# 零平台依赖(soe_store 胶水 SIM_EXEMPT),主机侧穷举往返/
|
||
# 截断/坏校验/IV 位/seq 连续性(规划 §3.4 L4)。
|
||
- id: soe-unit-test
|
||
name: SOE 动作记录编解码单测(主机侧)
|
||
entry: python test/host/soe/run_unit_test.py
|
||
language: system
|
||
files: ^(App/log/soe_rec\.(c|h)|App/log/soe_store\.(c|h)|test/host/soe/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7n) dhara NAND 移植层(nand_ftl)主机侧单元测试(2026-10-15 加)
|
||
# 背景:移植层六个 HAL 入口是**纯地址算术**,此前对越界页/块号(含
|
||
# DHARA_PAGE_NONE 哨兵)**不做任何上界校验**——加偏移送进 SPI 命令后被地址位
|
||
# 截断成某个真实页 → 静默读写/搬走无辜数据;而 is_free 旧实现只看页首 64 B,
|
||
# 会把"页首 0xFF、后段有数据"的已编程页误判空闲 → dhara 往已编程页再编程
|
||
# = 数据丢失。两类缺陷真机**极难复现**(要恰好撞上特定页形态 / 被污染的 meta),
|
||
# 板端只能表现为"某次复位后丢数据",无从定位。
|
||
# 本钩子用 MSVC 离线编译真实 nand_ftl.c(#include 纳入)+ **RAM 假芯片**
|
||
# (32 块 x 64 页 x 2048 B):把"双编程"做成可数事件、记录全部
|
||
# 访问次数,于是"越界不产生任何芯片访问""越界标坏块次数为 0"可机械断言;
|
||
# 并内联一份"只看页首 64 B"的旧算法做对照,证明这组断言真能咬人。
|
||
# 触发面含 dhara 本体(测试一并 #include map.c/journal.c 跑真链路场景)。
|
||
- id: nand-port-unit-test
|
||
name: dhara NAND 移植层单测(主机侧)
|
||
entry: python test/host/nand_port/run_unit_test.py
|
||
language: system
|
||
files: ^(Drivers/BSP/GD5F2GQ5UE/nand_ftl\.(c|h)|Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue\.h|Lib/dhara/(map|journal|nand|error|bytes)\.(c|h)|test/host/nand_port/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 7z) UI 数据快照层(道E task-14)主机侧单元测试(2026-10-25 加)
|
||
# 背景:ui_data 是"页面看到的整个世界",但 ui-golden 只覆盖已接线的页面——
|
||
# 遥信 27 点、定值 40 项、遥测未接线的通道错了不会让任何参考图变红。本钩子
|
||
# 用 MSVC 离线编译真实 ui_data.c 与 ui_w_soe.c(#include 纳入,零桩),把两件事
|
||
# 钉死:① 快照存储语义(首帧前读 -1 / 序号由本层盖章 / 三通道互不串台 /
|
||
# NULL 防御);② 跨层不透明下标与显示名映射的**现场一致性**——测试单元在同一
|
||
# TU 里同时 include BSP 侧与 App/algo、App/log 真头,逐项比对 adc_semantic_t /
|
||
# prot_set_id_t / soe_type_t / prot_elm_id_t(解耦换来的"魔法下标"风险,口径
|
||
# docs/UI设计/UI_数据供给设计说明.md §9.4;SOE 类型含未标定闭锁=7/复归=8)。
|
||
# files 覆盖被测两件 + 编号真源四头 + 页面消费面(ui_content_page/ui_menu*)+ harness。
|
||
- id: ui-data-unit-test
|
||
name: UI 数据快照层单测(主机侧)
|
||
entry: python test/host/ui_data/run_unit_test.py
|
||
language: system
|
||
files: ^(Drivers/BSP/UI/(ui_data|ui_w_soe|ui_w_records|ui_content_page|ui_menu)\.(c|h)|App/app/ui_data_feed\.(c|h)|App/algo/(adc_input|prot_set|prot_elm)\.(c|h)|App/log/soe_rec\.(c|h)|test/host/ui_data/.*|test/host/common/unit_test_harness\.py)$
|
||
pass_filenames: false
|
||
|
||
# 8) 测试套件开关门禁(2026-10-01 加)
|
||
# 背景:test_config.h 的 TEST_SUITE_* 决定固件里编入哪个测试套件,
|
||
# 全部注释 = 产品构建。但此前**无任何门禁检查**——实测已提交的 HEAD 里
|
||
# TEST_SUITE_CH395F 就是开启状态(.map 含 34 处 test 代码),即仓库里
|
||
# 躺着的是测试固件。本钩子抓这个静默漏网:默认要求产品构建,跑套件回归
|
||
# 确需提交测试构建时用 `--allow-test-build` / ALLOW_TEST_BUILD=1 显式放行。
|
||
# 详见 docs/任务规划/登记簿.md
|
||
- id: test-suite-switch
|
||
name: 测试套件开关=产品构建
|
||
entry: python scripts/check_test_suite.py
|
||
language: system
|
||
files: ^test/board/test_config\.h$
|
||
pass_filenames: false
|
||
|
||
# 9) 双端注册反向校验(2026-10-12 加,登记簿条目「rs485_task 白名单缺口」)
|
||
# 背景:新 .c 必须手动注册两处(`MDK-ARM/*.uvprojx` + `simulator/CMakeLists.txt`)
|
||
# 才会被编译;历史上 `App/task/rs485_task.c`、`Drivers/BSP/NET/lftpd/lftpd_log.c`
|
||
# **两处都没注册** = 写了却从不参与任何一端编译的**死代码**,误导读者以为功能已生效。
|
||
# 本钩子反向核对两份注册表:①存在但未进 uvprojx ②存在但未进 CMakeLists
|
||
# (MCU-only 豁免见脚本 SIM_EXEMPT,显式清单+每行理由,不整目录一刀切)
|
||
# ③注册了但文件不存在(幽灵注册)。
|
||
# 存量缺口(rs485_task/lftpd_log)已进脚本 KNOWN_GAPS 白名单 → WARN 不拦,
|
||
# 只对新缺口 FAIL,故钩子可立即上线不拦现有提交(`--strict` 供审计关白名单)。
|
||
# ⚠ pre-commit 4.x 已移除 `always: true` → files 必须覆盖所有触发面:
|
||
# 任意 .c + uvprojx + CMakeLists.txt + 脚本自身(脚本自身改动也值得复跑)。
|
||
- id: registration-check
|
||
name: 双端注册反向校验 (uvprojx + CMakeLists)
|
||
entry: python scripts/check_registration.py
|
||
language: system
|
||
files: (\.c$|\.uvprojx$|CMakeLists\.txt$|^scripts/check_registration\.py$)
|
||
pass_filenames: false
|
||
# 10) RAM 预算 92% 阈值门禁(2026-10-12 加,登记簿条目「缩堆迁 CCM 后半(暂缓)」的台账/门禁那半 / 审查报告 §5.5 三件套)
|
||
# 背景:报告 §5.5 第 1 条要求"超阈值即失败 + Top-10 附表"——此前 mem-numbers 只防
|
||
# "文档数字漂移",**没有任何机械检查拦"占用超阈值"**;台账三件套(ram_baseline.py +
|
||
# ram_baseline.tsv + docs/内存管理/RAM预算台账.md)落地后,本钩子把 92% 红线机械化。
|
||
# 逻辑:RW_IRAM1/RW_CCM 任一 >92% → FAIL + Top-10 RW+ZI 对象表;台账受管区漂移 → FAIL
|
||
# (提示 --write);无 .map → 自适应跳过(同 mem-numbers 在 CI 的行为,盲区已登记)。
|
||
# ⚠ pre-commit 4.x 无 `always: true` → files 覆盖所有能改变 RAM 的触发面:
|
||
# 全部自研代码目录 + lv_conf.h + 测试板配置 + 脚本/tsv/台账自身 + 本配置文件。
|
||
- id: mem-budget
|
||
name: RAM 预算 92% 阈值门禁
|
||
entry: python scripts/ram_baseline.py --gate
|
||
language: system
|
||
files: ^(App/|Drivers/BSP/|Src/|Inc/|Lib/lv_conf\.h|test/board/|scripts/ram_baseline\.|docs/内存管理/|\.pre-commit-config\.yaml$)
|
||
pass_filenames: false
|
||
|
||
# 11) UI 字符集差集检查(2026-10-15 加,报告 §6.5 的 UI 字符集与像素回归门禁残余 / §7 交付物清单"字符集差集检查"行)
|
||
# 背景:`ui_font_cn_12.c` 是**子集**字体,只含 `ui_cn_chars.txt` 列出的码位。
|
||
# 生成链 UI 源码 --extract_cn--> ui_cn_chars.txt --make_font--> 字体 .c,其中
|
||
# verify_coverage 只在**生成期**校验"txt ⊆ cmap";改完 UI 字符串忘了重跑
|
||
# font-regen 就提交 → 缺字上机渲染为空白(白框),无日志无报错只能肉眼截图发现
|
||
# (font-regen skill 的"缺字渲染为空白"约定,此前纯靠自律)。本钩子把它机械化:
|
||
# A. 源码在用 vs txt 缺 → FAIL(白框方向);多 → WARN(只费 Flash,--strict 升级)
|
||
# B. txt vs 字体 cmap 缺 → FAIL(lv_font_conv 对缺字形/低码位**静默丢字**)
|
||
# A 过 + B 过 ⟹ 字体 ⊇ 源码每个在用字。提取与 cmap 解析**复用** tools/font_gen
|
||
# 两个生成器的同一套规则(importlib 直载 extract_cn.SCAN/collect + make_font.
|
||
# verify_coverage),杜绝第二套解析规则漂移(§5.5 同类教训)。
|
||
# ⚠ pre-commit 4.x 已移除 `always: true` → files 必须覆盖全部触发面:
|
||
# UI 源码(含字体 .c 自身)+ ui_task.{c,h} + 字符集 txt + 两个生成器 + 本脚本。
|
||
- id: font-charset
|
||
name: UI 字符集差集检查(防缺字白框)
|
||
entry: python scripts/check_font_charset.py
|
||
language: system
|
||
files: ^(Drivers/BSP/UI/|App/task/ui_task\.(c|h)$|ui_cn_chars\.txt$|tools/font_gen/|scripts/check_font_charset\.py$)
|
||
pass_filenames: false
|
||
# 12a) BOM 漂移门禁(2026-10-25 道E task-20)
|
||
# 背景:AGENTS 铁律第 2 条——编辑工具会**静默剥掉**文件原有 BOM,ARMCC 于是按 CP936
|
||
# 解码带中文的源文件,报 #870-D / #8 / #274 一类**假语法错**(报错位置与内容都不指向
|
||
# 真因),而 MSVC(模拟器侧)与既有钩子全照不出来。2026-10-25 一天内道E 踩 4 次、
|
||
# 道B 批 1 踩 1 次,此前全靠人工逐文件比 HEAD 兜底——人工审计只防得住记得做的人。
|
||
# 判定:HEAD 有 EF BB BF 而工作区没有 → FAIL。两条刻意不报(新增文件无 BOM、HEAD 无
|
||
# BOM 而工作区加了 BOM)与一条刻意不做(不自动补:整文件重写正是本仓更大的坑)都
|
||
# 写在脚本头部。files 与 AGENTS 名单、脚本内 BOM_LIST **三处互指**,脚本每次跑核一次,
|
||
# 单独改任何一处都会在别人的提交里被拦下——名单散在三处而无人同步等于没有名单。
|
||
- id: bom-guard
|
||
name: BOM 漂移检查(带 BOM 文件被剥掉即拦)
|
||
entry: python scripts/check_bom_drift.py
|
||
language: system
|
||
files: ^(Drivers/BSP/UI/|App/task/ui_task\.c$|App/cfg/cfg_file\.c$|Lib/lv_conf\.h$)
|
||
|
||
# 12b) 定值查看行表一致性(2026-10-25 道E task-20 追加批;脚本随 task-15 批 3 落)
|
||
# 核 Drivers/BSP/UI/ui_w_setview.c 的四张视图表与 App/algo/prot_set 的枚举序、
|
||
# g_prot_set_meta 上下限是否仍是同一份口径:id 恰好覆盖 0..39 各一次、数值行
|
||
# min/max 等于 meta 换算、kind 与 meta.is_ctl 一致、单位纯 ASCII、小数位查表与行表
|
||
# 一致。为什么必须机械化:BSP 侧看不到 App 头,行表里的 id 是裸数字 + 行尾注名,
|
||
# 这条对齐在编译期**物理上无法自证**;而页面上只有当前可见的 8 行会被像素断言看到,
|
||
# 错位要翻到第 2、3 页才可能被人发现(发现了也已被基线钉死)。
|
||
# ⚠ 跨分支先后:脚本文件随 task-15 分支合入主干;在此之前 files 永不命中 → 钩子自然
|
||
# skip(skip 不是失败),合并后自动生效,不需要任何人再动这条配置。
|
||
- id: setview-check
|
||
name: 定值查看行表与 prot_set 枚举/上下限一致性
|
||
entry: python tools/setview_check/check_setview_limits.py
|
||
language: system
|
||
files: ^(tools/setview_check/|Drivers/BSP/UI/ui_w_setview\.(c|h)$)
|
||
pass_filenames: false
|
||
|
||
# 12) UI 像素回归门禁(2026-10-20 加,W6 的 UI 字符集与像素回归门禁收尾第三件 / 报告 §6.5 同项、§6.1 golden 行)
|
||
# 背景:golden 两脚本(check_menu_rows.py / check_content_pages.py)早已存在且一直在
|
||
# 人工跑,但**从未进自动门禁**——"有基线"不等于"有门禁"(报告 §6.1 原文即自认此缺口)。
|
||
# 形态选型依据实测:改一个 UI 源文件后 build_sim 增量重编 + 两条 golden 合计 **2~4 秒**,
|
||
# 成本可接受 → 走自动门禁(若实测要几分钟,则应改为文档化人工门禁,一个逼人
|
||
# --no-verify 的门禁等于没有门禁)。
|
||
# 有牙实测:给 ui_page_dc.c 的 .top_y 注入 +3 位移 → build_sim 重编 →
|
||
# golden FAIL 且点名 `[dc] FAIL 与参考页不一致(1366 px 差异)`、退出码 1;还原后 diff 干净。
|
||
# ⚠ 两条 golden 按"整棵菜单树 + 全量参考图"工作、不接受文件参数,故包装器有意忽略
|
||
# argv 并 pass_filenames: false(pre-commit 传的变更名对它无意义)。
|
||
# ⚠ 参考图 simulator/tests/ref/*.bin 是判据本身,改动它等于改判据——它已入库,
|
||
# 评审时看 `git diff -- simulator/tests/ref` 必须逐页过目并在提交正文说明;
|
||
# 任何时候都不得用 --update 刷基线来"让钩子变绿"。
|
||
# ⚠ pre-commit 4.x 无 `always: true` → files 必须覆盖所有能改变像素的触发面。
|
||
# ⚠ 已知瞬时现象:首次实测时在"连续注入/还原的紧邻重建"之间偶发失败一次,
|
||
# 疑似 MSBuild 节点争用;此后 3 次干净树连跑均绿。判据:单次失败先复跑一次再看,
|
||
# 连续两次失败才当真(不许直接 --no-verify 绕过)。
|
||
- id: ui-golden
|
||
name: UI 像素回归(菜单 8 状态 + 内容页 53 页 黄金参考)
|
||
entry: python scripts/check_ui_golden.py
|
||
language: system
|
||
files: ^(Drivers/BSP/UI/|App/task/ui_task\.(c|h)$|simulator/(tests|sim|src)/|simulator/main\.c$|simulator/CMakeLists\.txt$|simulator/lv_conf\.h$|scripts/check_ui_golden\.py$|\.pre-commit-config\.yaml$)
|
||
pass_filenames: false
|
||
|
||
# 12a) 板端 UI 导航器认屏自检(2026-10-25 道E task-25)
|
||
# 背景:大窗实测——靠预置键序盲走到深页时,9 帧里只有 3 帧真是它声称的态;
|
||
# KEY 的 ACK 是空应答、不携带状态,所以**画面是唯一事实源**。判据脚本
|
||
# scripts/board_ui_nav.py 据此改成闭环:每按一键重新认屏再决定下一个键。
|
||
# 本钩子跑它的**快检档** --selftest-quick:只核"认屏认得对"(18 张黄金参考 +
|
||
# 真机留档帧,目录不在版本控制里、缺就整段跳过),秒级、无构建产物依赖。
|
||
# ⚠ 不挂完整 --selftest:第三段闭环需要 lvgl_sim.exe(构建产物,新工作树里没有)
|
||
# —— 正是同一天 mem-numbers 缺 .map 那课的形状;脚本对缺 exe 报 FAIL 并给出
|
||
# 可执行提示,不"跳过当通过"。合入前 / 上板前手工跑一次完整档:
|
||
# python scripts/board_ui_nav.py --selftest(约 30 s,含全绑定页闭环)。
|
||
# ⚠ 触发面刻意只两块(脚本自身、黄金参考帧):字体或菜单树变动**不**触发它,
|
||
# 那两类的守卫分别是 font-charset / ui-golden / registration-check 与 id 侧;
|
||
# 把每个 UI 提交都拉去跑导航器,只会让人习惯等一个与本改动无关的钩子。
|
||
- id: uinav-selftest
|
||
name: 板端 UI 导航器认屏自检(快检:黄金参考帧分类)
|
||
entry: python scripts/board_ui_nav.py --selftest-quick
|
||
language: system
|
||
files: ^(scripts/board_ui_nav\.py$|simulator/tests/ref/.*\.bin$)
|
||
pass_filenames: false
|
||
|
||
# 13) 编号门禁(2026-10-20 加,docs/编号规范.md §8 的 8 项校验)
|
||
# 背景:§1.2 用仓内证据列了 10 类编号事故(同字母多义 / 跨文档同号不同义 /
|
||
# 销项删行后残留悬空引用 / 自造前缀 / 会话临时号入册…),而 36 个钩子里
|
||
# **只有 TC 一族**有机械检查(check_test_traceability.py,且未挂钩子、正则还漏 [示例]
|
||
# `TC-AI-01A` [示例] 这类大写后缀)——E/N/S/B/C/D/A/U/O/gen/Trap 全部裸奔。本钩子把
|
||
# 规范 §8 的 8 项机械化:配置两份 `scripts/id_registry.json`(前缀 → 属主登记处
|
||
# /形态/冻结/游标,逐项来自规范 §4 总表)+ `scripts/id_aliases.json`(旧号→新号,
|
||
# 照搬 section_aliases.json 的成功范式)。
|
||
# 判据要点:「定义」= 字面量出现在该族**属主登记处文档**里;于是 §5.5 的
|
||
# 「销项 = 删行 + 清全库引用」只做一半,校验 3 立刻报(销项号被重新取走那类撞号正是这条)。
|
||
# 盲区显式登记在 registry 的 `blind_spots` / `unmanaged_bare`:裸形同字母多义
|
||
# (§1.2:P1 五义 / L1 四义 / UI 状态机号四义)与元器件位号同形(§4.4),机械全库判定
|
||
# 必然一边倒误报 → 裸形族只在属主文档内判越游标、文档外降告警;收口属 §9 阶段
|
||
# 1-② 的迁移批次。**不是「检查通过」,是记在案上的盲区。**
|
||
# ⚠ 当前 warn-only(§8 上线节奏:先清 backlog → 清零后 entry 加 --strict 拦提交,
|
||
# 与 doc-links 章节校验同一条路径)。校验 8 的「可判定比例」跌破
|
||
# id_registry.json 里的基线时**任何模式都失败**——那根线就是防回退的本体。
|
||
# ⚠ pre-commit 4.x 已移除 `always: true` → files 必须覆盖所有能改编号的触发面
|
||
# (任意 md/c/h/py/yaml/json/txt + 本配置文件);扫描范围恒为全库
|
||
# (悬空/孤儿/重复定义是跨文件判定,pass_filenames: false)。
|
||
- id: id-check
|
||
name: 编号门禁(docs/编号规范.md §8 八项校验)
|
||
entry: python scripts/check_ids.py --strict
|
||
language: system
|
||
files: \.(md|c|h|py|ya?ml|json|txt)$|^\.pre-commit-config\.yaml$
|
||
pass_filenames: false
|
||
|
||
# 14) 分层方向门禁(2026-10-22 加,结构债的"防扩大"那一半)
|
||
# 背景:AGENTS.md 与 项目架构总览.md 早就写着"六层单向依赖",但**从来没有机械检查**
|
||
# ——Drivers/BSP/ 里可以随手 include "cfg_file.h" 而无人报警。后果可测量:2026-09 审查
|
||
# 报告记"NETCFG 一处",2026-10-22 实测已长到 **8 个文件 9 处**。边界的静默腐蚀只能
|
||
# 靠门禁止住,所以本钩子做**增量拦截**(与 id-check 同构):现状 6 条钉成 BASELINE
|
||
# (只减不增,没有 --write 往里加),新出现的 (BSP 文件, App 头) 组合直接 FAIL;
|
||
# 3 条横切基础设施头走 ALLOW 永久豁免(日志后端 / 时间源 / 纯算法工具——它们是
|
||
# 任何层都要用的叶子能力,不是上层服务)。修完存量后跑 --prune 会点名让你收紧基线,
|
||
# 于是"还欠几条"变成可见进度,而不是文档里的一句口号。
|
||
# 注:本钩子只拦"静态 include 方向";**运行时的属主纪律本来就没错**(UI 只投递
|
||
# net_cfg_save_request(),写盘在 defaultTask),别把它当缺陷读。
|
||
# 扫描范围恒为全库(要跨文件建 App 头索引),故 pass_filenames: false。
|
||
- id: layer-deps
|
||
name: 分层方向(BSP 不得反向 include App)
|
||
entry: python scripts/check_layer_deps.py
|
||
language: system
|
||
files: ^Drivers/BSP/|^scripts/check_layer_deps\.py$|^\.pre-commit-config\.yaml$
|
||
pass_filenames: false
|
||
|
||
# 15) BSP 错误码与超时命名约定(机械检查那一半)。
|
||
# 四项检查:公开 API 不得用 HAL_StatusTypeDef/bool 当错误通道(谓词 is_/has_/can_/valid
|
||
# 不在此列);*_TIMEOUT 宏不得取负值(名字叫 TIMEOUT 其实是返回码);非 void 的公开 API
|
||
# 必须有「返回值:」注释;**返回错误码的函数不得裸丢弃返回值**(接住或显式 (void)——实测
|
||
# ARMCC v5 与 cppcheck 都不报这类,只有这条会响)。存量钉 BASELINE、只减不增(2026-10-23
|
||
# 首清点 19 条 → 已还 18;新增一项又暴露 37 条 → **现 38 条**:37 丢弃 + 1 个 bool 待改签名),
|
||
# 修完跑 --prune 点名收紧基线。约定正文 = Drivers/BSP/bsp_err.h,规则说明 =
|
||
# scripts/check_bsp_err.py 文件头,逐项详解(含限制与盲区)= docs/门禁说明/bsp-err.md。
|
||
# 恒扫全库(要按 git 清单建公开头索引),故 pass_filenames: false。
|
||
- id: bsp-err
|
||
name: BSP 错误码与超时命名约定
|
||
entry: python scripts/check_bsp_err.py
|
||
language: system
|
||
files: ^Drivers/BSP/|^scripts/check_bsp_err\.py$|^\.pre-commit-config\.yaml$
|
||
pass_filenames: false
|
||
|
||
# 8) 主机单测覆盖率总闸(2026-10-24 接线,用户裁定选 FAIL 语义)
|
||
# 为什么现在才挂:接线前本文件只有 16 个 *-unit-test 钩子,而仓库已有 28 套主机单测——
|
||
# P1 新增的 12 套不在任何提交钩子里。原先"再加一份全量插桩等于同一件事做两遍"
|
||
# 这条理由到现在已经**反了**(现状是有一遍没做),所以必须挂;缺口此前如实登记在
|
||
# docs/测试说明/主机单测覆盖率口径.md §8。
|
||
# 为什么挂这一根而不是补 12 个 per-module 钩子:本钩子跑**全部 28 套**,并且守 §5 的
|
||
# 回退台账(某套被删、或某文件的已覆盖行变少 → [REGRESS] 直接红)。这是 per-module
|
||
# 钩子做不到的——删掉一套测试,它那根钩子只会因为"没有文件要检查"而静默跳过。
|
||
# ⚠ FAIL 语义(用户裁定 2026-10-24):找不到 mscoverage 时脚本退出码 2 → 本钩子**失败**,
|
||
# 不做 skip。理由:skip 等于造一根会静默失效的门禁(与 pre-commit 4.x 移除 always: true
|
||
# 是同一类坑);宁可在没装组件的机器上被挡一次、看清 [ENV] 那行提示,也不要一根假绿的闸。
|
||
# 本机与 CI runner 同一台;实测两份 VS 安装都自带该 exe(口径 §7 第 8 条已按实测更正)。
|
||
# 口径 / 分母 / 基线 / 盲区详解都在 docs/测试说明/主机单测覆盖率口径.md,本处只留接线事实。
|
||
# 触发面**含门禁自身**(.pre-commit-config.yaml):不带自身就等于谁改坏这根闸的 files:
|
||
# 模式,它就再也不跑且无人察觉(同 lv-conf-sync / bsp-err 的既有做法)。
|
||
# ⚠ 读输出的坑(2026-10-24 连踩两次才定位):本钩子没写 stages: → 默认属于**所有阶段**,
|
||
# 于是同一次提交的输出里它出现两次:pre-commit 阶段那一遍才可能实跑;commit-msg 阶段
|
||
# 那一遍的"文件"是提交信息文件,几乎必然 (no files to check)Skipped。**只看输出结尾会
|
||
# 把"实跑通过"读成"根本没跑"**——判它跑没跑要认**第一次**出现的那一行。
|
||
# 实跑证据:只改本文件的提交(e327636 / c4ca292)里 test-coverage 与 bsp-err 都是 Passed。
|
||
- id: test-coverage
|
||
name: 主机单测覆盖率(增量拦截:已覆盖行只增不减)
|
||
entry: python scripts/check_test_coverage.py
|
||
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
|
||
# ── 块注释嵌套(第 51 条,2026-10-27 加,task-29)─────────────────────────────
|
||
# 查什么:C/C++ 块注释体内再出现 /* —— ARMCC 诊断 9-D「nested comment is not allowed」。
|
||
# 本仓的诱因是「在注释里写作用域 / 通配路径」这种常态写法:Drivers/BSP/UI/** 里那个
|
||
# /* 就是第二次注释的起点(C 的块注释不嵌套)。
|
||
# 为什么要闸:它**只在重编译那个 TU 时才暴露**,历史「0 警告」不代表安全;而且按包含者
|
||
# 数量刷屏——一份被广泛 include 的头里一处嵌套 = 每个包含者一行警告(实测一例扩散到 16
|
||
# 条,把整批真警告挤到看不见)。有了这根,提交时就报,不用等 @build。
|
||
# 接线当日实证(冻结引用,别去翻构建日志——它每次 @build 就被覆盖、也不入库):当日 02:22
|
||
# 那次构建日志报过 ..\App\algo\run_par.c(267): warning: #9-D: nested comment is not
|
||
# allowed ——那是别人**未提交**的在飞状态。原文抄在 docs/门禁说明/nested-comment.md §2.1;
|
||
# 已提交历史回放 281 份旧字节 0 命中 ⇒ 这类问题一直靠人工在编译后清,从没被机器拦过。
|
||
# 存量基线(只减不增,除这 2 处外**零豁免**):全仓 --all 实扫 403 个 C/H,新发 0,存量
|
||
# 2 处 = test/host/common/ch395f_fake.h:7 与 fatfs_fake.h:9(注释里写 test/host/**,属
|
||
# test/ 写域,等那侧改成语义短语;还清后删 BASELINE 行,--prune 打台账)。
|
||
# 自证:--selftest 四项(正例照得出 / 字符串与行注释不误报 / 基线吞得下 / 只减不增有牙),
|
||
# 另有 10 项形态电池(含「注释里写 UI/*」「块注释内行首再开 /*」等真形态)。
|
||
# ⚠ 计数口径:本条使 YAML 解析条目 50 → **51**;配置里那条被注释掉的 clang-format 候选仍
|
||
# 在,所以**按文本 grep 声明行会得 52**(多算那条候选)——那不是口径。本行刻意不复现那个字面量,
|
||
# 否则注释自己也会被 grep 数进去(2026-10-27 实测踩过:写过一次,计数立刻虚高 1)。
|
||
# 口径与自查命令见 docs/门禁说明/README.md §2 首条。
|
||
- id: nested-comment
|
||
name: "块注释体内不得再出现 /*(ARMCC 9-D 嵌套注释)"
|
||
entry: python scripts/check_nested_comment.py
|
||
language: system
|
||
types: [c]
|