feat(门禁): bom-guard 机械化 BOM 漂移,附 setview-check 行表一致性
把 AGENTS 铁律第 2 条从"人工逐文件比 HEAD"变成提交硬闸:HEAD 有 EF BB BF 而工作区没有 → FAIL。 2026-10-25 一天之内道E 在 task-15 里连踩 4 次、道B 批 1 踩过 1 次,此前全靠人记得做审计兜底—— 人工审计只防得住记得做的人,这正是它该变成门禁的理由(票 = task-20)。 scripts/check_bom_drift.py(纯标准库) - 判定只有一条:HEAD 前 3 字节是 BOM 而工作区不是 → 列出文件与补救动作,不含二义措辞。 - 两条刻意不报(票面要求的边界,--selftest 逐条自证):① HEAD 里根本没有的新文件无 BOM 不误报; ② HEAD 无 BOM 而工作区加了 BOM 也不报——那是另一个方向的改动,可能是有意的(把文件纳入名单), 报红只会逼人 --no-verify,而"养成绕门禁的习惯"比漏检一次贵得多。 - 一条刻意不做:不自动补 BOM。正确修复是在文件最前面加 3 个字节;脚本整文件重写会按系统默认编码 重打中文,把可修的丢 BOM 变成不可修的 U+FFFD(AGENTS 铁律第 1 条那条更大的坑)。补救文案里把 这句完整写出来了——报错信息的作用是让踩的人当场做对动作,不是让他知道有人不高兴。 - 名单三处互指:AGENTS 铁律名单 / 本钩子 files 正则 / 脚本内 BOM_LIST,每次运行核一次;单独改任何 一处都会在别人的提交里被拦下。名单散在三处而无人同步,等于没有名单。 - --selftest 在临时 git 仓库里跑 4 条判定(含上述两条边界 + 主用例 + 名单外文件不参与),全过。 setview-check(Lead 指令:并入 task-20 同窗机械化) - 核 Drivers/BSP/UI/ui_w_setview.c 的四张视图表与 App/algo/prot_set 枚举序、 g_prot_set_meta 上下限是否同一份口径:id 恰好覆盖 0..39 各一次 / 数值行 min-max 等于 meta 换算 / kind 与 is_ctl 一致 / 单位纯 ASCII / 小数位查表与行表一致。机械化的理由不是"多一道保险":BSP 侧看不到 App 头, 行表里的 id 是裸数字 + 行尾注名,这条对齐在编译期**物理上无法自证**;而页面上只有当前可见 的 8 行会被像素断言看到,错位要翻到第 2、3 页才可能被人发现——那时它已被黄金参考钉死了。 - 跨分支先后写成注释:脚本文件随 task-15 分支合入主干;在此之前 files 永不命中 → 钩子自然 skip (skip 不是失败),合并后自动生效,不需要任何人再动这条配置。 AGENTS.md(票面授权:只加一句机械化标注;下面实际动了两处计数,一并申报) - 铁律第 2 条末尾加"已机械化:bom-guard 钩子"+ 三处互指一句。 - 钩子计数 46 → 48 两处同步(不同步就是让 AGENTS 说谎);并在文档索引行标出"bom-guard / setview-check 两篇待补",把欠的详解篇目挂在明面上,不留沉默债。 验证 - 本笔**未用 --no-verify**:全量 pre-commit --all-files 46 项 Passed、0 Failed(含两条新钩子)。 - 端到端实测(不止 --selftest 自证):真把 Drivers/BSP/UI/ui_w_records.c 的 BOM 剥掉并 stage → bom-guard rc=1 并打印补救动作;还原后该文件与 HEAD 逐字节相同,工作树干净。 - 顺带发现并行开发方案的一条空白(本笔未修,写域之外):全新工作树没有 .map,mem-numbers 会因 "找不到 .map 真实源" FAIL,跑一次 @build 才绿。门禁隐含的"本树必须先构建过"前提在 docs/环境与部署/并行任务工作树方案.md 里没写;已回报 Lead 定夺归属。 - 自报一条:本笔第一次提交被 gitlint 拦下——正文有一行 92 > 80,**我量出来了却仍然按了提交**。 记在这里是给下一篇门禁详解留素材:这条门禁拦下过的人里也包括写门禁的人自己,而「拦下过 什么真事」那一栏不该只挑别人的事写。
This commit is contained in:
@@ -68,7 +68,7 @@
|
||||
## 铁律:源码编辑与编码
|
||||
|
||||
- 改代码**只用 Edit 工具**(精确子串替换 / replaceAll);**绝不用 `Get-Content`/`Set-Content`/`cmd` 重写含中文的文件**——整文件重写按系统默认编码重解码 → `U+FFFD` 乱码且不可逆
|
||||
- **Edit 会静默剥掉文件原有的 UTF-8 BOM**:带 BOM 的文件(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`)被 Edit 后丢 BOM → ARMCC 改按 CP936 解码 → 中文字面量报 `#870-D: invalid multibyte character sequence`(**只有重编译该文件才暴露,历史"0 警告"不代表安全**)。改完须核对 BOM 并按原样补回(只前缀 3 字节 `EF BB BF`,勿整文件重写)
|
||||
- **Edit 会静默剥掉文件原有的 UTF-8 BOM**:带 BOM 的文件(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`)被 Edit 后丢 BOM → ARMCC 改按 CP936 解码 → 中文字面量报 `#870-D: invalid multibyte character sequence`(**只有重编译该文件才暴露,历史"0 警告"不代表安全**)。改完须核对 BOM 并按原样补回(只前缀 3 字节 `EF BB BF`,勿整文件重写)。**已机械化:`bom-guard` 钩子**(`scripts/check_bom_drift.py`:HEAD 有 BOM 而工作区没有即拦;上面这份名单与钩子 `files` 正则、脚本内 `BOM_LIST` **三处互指**,脚本每次跑都核一次,单独改一处会在别人的提交里被拦下)
|
||||
- **字符串字面量必须纯 ASCII**:无 BOM 文件里奇数个汉字会吞掉结束引号(偶数侥幸通过)→ 中文只写注释
|
||||
- 怀疑乱码先统计 `EF BF BD` 字节数(勿信终端/Read 渲染);**不要急著 `git checkout`**(会丢弃全部未提交工作)——用 Edit 只修坏行
|
||||
- 行尾由 `.gitattributes` 写死(`* text=auto eol=lf`;`*.bat`/`*.cmd` 强制 CRLF):**不依赖 `core.autocrlf`、不要把工作区文件改回 CRLF** → 权威口径 `docs/文档规范.md` §2
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
## 提交门禁(pre-commit)
|
||||
|
||||
- 任一失败即拦截,共 **46 个钩子**(**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 22 个(`-unit-test` 后缀,2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六个)+ `test-coverage`(**覆盖率总闸**:跑全部 39 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,N-23/U4)+ `mem-budget`(RAM 92% 预算门禁,N-27)+ `font-charset`(UI 字符集差集检查,防缺字白框,U4)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 11 页黄金参考,U4)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3),报告 S-1)+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**),报告 S-5)
|
||||
- 任一失败即拦截,共 **48 个钩子**(新增 `bom-guard` / `setview-check` 两条,2026-10-25 道E task-20,详解篇目待补;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 22 个(`-unit-test` 后缀,2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六个)+ `test-coverage`(**覆盖率总闸**:跑全部 39 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,N-23/U4)+ `mem-budget`(RAM 92% 预算门禁,N-27)+ `font-charset`(UI 字符集差集检查,防缺字白框,U4)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 11 页黄金参考,U4)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3),报告 S-1)+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**),报告 S-5)
|
||||
- **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5)
|
||||
- ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关
|
||||
- **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/`
|
||||
@@ -173,7 +173,7 @@
|
||||
| ADC 采样节拍 / 通道映射 / V0.1 硬件缺陷 | `docs/子系统设计/ADC采样节拍设计说明.md`、`docs/硬件平台/ADC通道映射说明.md` |
|
||||
| 存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) | `docs/架构设计/STM32F4-Base存储架构说明.md` |
|
||||
| RAM / 栈预算 / 容量台账 | `docs/内存管理/RAM使用分析与优化方案.md`、`内存内容分配策略.md` |
|
||||
| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(46 个钩子 → 23 篇索引) |
|
||||
| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(48 个钩子 → 23 篇索引;bom-guard / setview-check 两篇待补) |
|
||||
| 实体编号怎么写 / 前缀总表 / `id-check` 八项校验 | `docs/编号规范.md`(§4 总表、§8 门禁与实测基线) |
|
||||
| 提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) | `docs/代码规范/pre-commit使用说明.md` §2 |
|
||||
| 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` |
|
||||
|
||||
Reference in New Issue
Block a user