# pre-commit 自动化代码审查配置 # 详见 docs/代码规范/pre-commit使用说明.md 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/tests/ref/`、`simulator/shot*.{bin,png}`、`__pycache__/`) # **已在 .gitignore 里**,永远不会进 git 索引 → 无需在此重复豁免,故直接删掉该条。 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) 文档一致性门禁(D-7) # 存在性:md 互链 / 反引号路径是否指向真实文件(防迁移/删除后残留死链) # 章节:`§N.M` 必须能在目标文档的编号标题里找到(--sections-strict:自引用失效也拦) - id: doc-links name: 文档链接存在性 (D-3/D-7) 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 一致(D-2/D-7) - id: task-table name: FreeRTOS 任务表一致性 (D-2/D-7) entry: python scripts/gen_task_table.py --check language: system files: ^(README\.md|AGENTS\.md|Src/freertos\.c)$ pass_filenames: false # 体积:内存/Flash 数字须与最新 .map 一致(D-7 第三档) # 只在内存文档或代码变动时触发;.map 未生成时脚本自身给出明确提示 - id: mem-numbers name: 内存/Flash 数字一致性 (D-7 体积档) 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 加,随 F-1/F-2/F-3 落地) # 背景:`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 # 7g) 时间同步(time_sync)主机侧单元测试(2026-10-10 加) # 背景:time_sync.c 是 TCP 时间同步协议状态机,也是 N-17 号看门狗风险的**全部 # 逻辑贡献者**——"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 采样有效性校验(L1-6)主机侧单元测试(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 # 7i) 保护定值模型(P1 / L5-1 载体)主机侧单元测试(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 L3-5)。 - 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 加,N-19 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 加,N-19) # 锁定 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 全离线 # 穷举设计(A-4 约束 1);板端复现这些分支要真主站+真等待。 # 触发面含数据源依赖(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 files: ^(App/protocol/iec104/iec104_(asdu|pts|link)\.(c|h)|App/algo/(prot_set|prot_elm|phasor|sample_ring|adc_input|freq)\.(c|h)|test/host/iec104/.*|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 T-1 - 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 加,登记簿 N-23 / 审查报告 N-06 残余) # 背景:新 .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 加,登记簿 N-27 前半 / 审查报告 §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 的行为,盲区登记 A-5 残余)。 # ⚠ pre-commit 4.x 无 `always: true` → files 覆盖所有能改变 RAM 的触发面: # 全部自研代码目录 + lv_conf.h + 测试板配置 + 脚本/tsv/台账自身 + 本配置文件。 - id: mem-budget name: RAM 预算 92% 阈值门禁 (N-27) 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