diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aca0963e..938b3de9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -592,3 +592,23 @@ repos: 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,本处只留接线事实。 + - 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) + pass_filenames: false diff --git a/AGENTS.md b/AGENTS.md index 98d69e47..f467645e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,7 +90,7 @@ ## 提交门禁(pre-commit) -- 任一失败即拦截,共 **39 个钩子**(**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `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` + 主机侧单测 16 个(`-unit-test` 后缀)+ `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/**` 的头;存量 6 条基线只减不增,报告 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) +- 任一失败即拦截,共 **40 个钩子**(**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `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` + 主机侧单测 16 个(`-unit-test` 后缀)+ `test-coverage`(**覆盖率总闸**:跑全部 28 套并守基线回退;缺 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/**` 的头;存量 6 条基线只减不增,报告 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`(39 个钩子 → 23 篇索引) | +| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(40 个钩子 → 23 篇索引) | | 实体编号怎么写 / 前缀总表 / `id-check` 八项校验 | `docs/编号规范.md`(§4 总表、§8 门禁与实测基线) | | 提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) | `docs/代码规范/pre-commit使用说明.md` §2 | | 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` | diff --git a/docs/代码规范/pre-commit使用说明.md b/docs/代码规范/pre-commit使用说明.md index 21b43b0d..41c6637e 100644 --- a/docs/代码规范/pre-commit使用说明.md +++ b/docs/代码规范/pre-commit使用说明.md @@ -63,7 +63,7 @@ git commit > **2026-10-23 迁移**:原本节 27 个"逐钩子详解"小节,已全部迁往 `docs/门禁说明/`——**每种门禁 > 一篇**,八段固定结构(查什么 / 为什么要它 / 触发面 / 报错怎么读 / 好处 / **限制与盲区** / -> 豁免与基线口径 / 改门禁本身)。39 个 hook 的总览与"三种拦截姿态" → `docs/门禁说明/README.md`。 +> 豁免与基线口径 / 改门禁本身)。40 个 hook 的总览与"三种拦截姿态" → `docs/门禁说明/README.md`。 > 搬走的原因:只写"拦了什么"的文档会让人以为绿就是对的,而"什么时候会骗人"必须有归处。 > 同一事实只在一处详述(`docs/文档规范.md` 同此要求),所以本节不再重复逐钩子表格。 @@ -88,7 +88,7 @@ git commit | 机制 | 口径 | |---|---| -| **两个阶段** | pre-commit 阶段 38 个钩子 + commit-msg 阶段 `gitlint`;`default_install_hook_types: [pre-commit, commit-msg]` 是显式声明的——换机后 `pre-commit install` 才会带上 commit-msg 钩子 | +| **两个阶段** | pre-commit 阶段 39 个钩子 + commit-msg 阶段 `gitlint`;`default_install_hook_types: [pre-commit, commit-msg]` 是显式声明的——换机后 `pre-commit install` 才会带上 commit-msg 钩子 | | **stash 陷阱** | pre-commit 跑钩子前会 stash 未暂存改动,钩子看到的是"没有你中间状态"的树。**改代码 + 同时更新基线/受管区的,必须先 `git add` 再跑钩子**,否则会拿到假报警(本仓踩过,`docs/门禁说明/bsp-err.md` §4 记了现场) | | **`always: true` 已废** | pre-commit 4.x 对 `local` 钩子里这个键**只告警 `Unexpected key(s)` 然后忽略** → "想总是跑"只能把 `files:` 写全(教训 → §5.5) | | **改写型与只读型的豁免不对称** | YAML 锚点 `&cubemx-generated` 只豁免 `trailing-whitespace` 与 `end-of-file-fixer`;`cppcheck` / `no-mojibake` / `cubemx-survival` / `task-table` / `mem-numbers` **仍覆盖** `Src/`、`Inc/` | @@ -231,7 +231,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env | 文件 | 位置 | 说明 | |---|---|---| -| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**39 个钩子**,2026-10-23 清点)。逐项详解在 `docs/门禁说明/`(索引 `docs/门禁说明/README.md`;主机侧单测 16 个合一篇,含各套件来历;UI 侧另有 `font-charset` 与 `ui-golden` 两篇——后者此前**在旧手册里连小节都没有**,只活在配置注释里,本次补齐) | +| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**40 个钩子**,2026-10-24 清点:新增 `test-coverage` 覆盖率总闸)。逐项详解在 `docs/门禁说明/`(索引 `docs/门禁说明/README.md`;主机侧单测 16 个合一篇,含各套件来历;UI 侧另有 `font-charset` 与 `ui-golden` 两篇——后者此前**在旧手册里连小节都没有**,只活在配置注释里,本次补齐) | | `.gitlint` | 仓库根 | gitlint 规则(宽松,兼容中文) | | `.cubemx_survival_baseline.json` | 仓库根 | CubeMX 易失定制基线(哈希;`--write` 重建) | | `scripts/check_mojibake.py` | scripts/ | U+FFFD 检测脚本 | diff --git a/docs/测试说明/主机单测覆盖率口径.md b/docs/测试说明/主机单测覆盖率口径.md index a3b45162..74503427 100644 --- a/docs/测试说明/主机单测覆盖率口径.md +++ b/docs/测试说明/主机单测覆盖率口径.md @@ -1,6 +1,6 @@ # 主机单测覆盖率口径(覆盖率脚本 / 基线 / 盲区) -> **状态**:✅ 已落地(脚本 + 基线 + 口径),⏸ 未挂 pre-commit(理由见 §8); +> **状态**:✅ 已落地,并**已挂 pre-commit**(`test-coverage` 总闸,2026-10-24 接线,缺组件按裁定 FAIL); > **分支覆盖**:本机可实测拿到(§2.1 的 gcc/WSL 路),但**用户裁定 2026-10-23 不引入第二条工具链** > → 本文把它记为**已接受缺口**,不是待办 > **日期**:2026-10-23 @@ -152,8 +152,16 @@ (全量跑时以"模块未跑到"判 REGRESS),子集运行明确降级(§5)。 7. **头文件里的代码归 `.h`。** 统计面含 `.h`(静态内联与被展开的行落在头文件上);当前 16 套 几乎没触及 `.h`,表里看不到它们——不要以为头文件"天生 100%"。 -8. **依赖 VS 的可选组件。** 只有 BuildTools 且没勾"代码覆盖率工具"的机器会退出码 2; - CI runner 与本机同一台(`docs/CI/README.md`),但**换机先跑 `--list` 再跑全量**。 +8. **依赖 `Microsoft.CodeCoverage.Console.exe`。** 探测顺序:`DTU_MS_COVERAGE`(设了就必须 + 有效、**不静默回退**)→ 各 VS 安装根的 `Common7\IDE\Extensions\Microsoft\CodeCoverage.Console\` → + PATH;找不到即退出码 2(**不是 0**)。2026-10-24 起该结果直接红掉 `test-coverage` 钩子(用户裁定 + FAIL 语义),并已合成自证:把 `DTU_MS_COVERAGE` 指到一个不存在的文件 → 钩子 Failed、脚本退出码 2、 + 打印可执行的 `[ENV]` 提示。 + ⚠ **更正本文旧说法**(原写"只有 BuildTools 且没勾代码覆盖率工具的机器会退出码 2"):实测本机 + Community 18.10 与 BuildTools 18.9 **两份安装都自带**该 exe(mtime 2026-09-15 02:30 / 2026-09-05 + 16:17,都是各自安装当日,不是后来补勾)→ VS 18 的 BuildTools 默认就带它。真实风险面只剩:没有 + VS 只有裸 MSVC 的机器、`DTU_MS_COVERAGE` 指错、以及靠 PATH 兜底的环境。CI runner 与本机同一台 + (`docs/CI/README.md`);**换机仍先跑 `--list` 再跑全量**。 9. **并集口径下"单套百分比"没有意义。** 同一文件被多套触及很常见(`sha256.c` 被 `sha256` 与 `ftp_auth` 都编译),所以对外只报**并集**,单套只报"触及几个文件 / 多少行"。 @@ -161,7 +169,8 @@ **已落地**:脚本、基线台账、口径(本文)、`scripts/ci_local.py --coverage` 一条入口。 -**未挂 pre-commit**,三条理由(不是忘了): +**接线状态:已挂**(2026-10-24,`test-coverage` 落在 `.pre-commit-config.yaml` 第 8 节,用户裁定 **FAIL** 语义)。 +接线前的三条理由连同它们的现状一并留档,免得后人重问同一件事: 1. 39 个钩子里已有 16 个主机单测钩子(每套各编译各跑),再加一份"全量插桩"在**当时**确实是同一 件事做两遍,还要多约 25 s; @@ -171,9 +180,10 @@ 全部模块)目前只被 `ci_local.py`(L0.5 自动发现)与 `check_test_coverage.py` 跑到, **不在提交钩子里**。所以现状不是"重复两遍",而是**有一遍没做**。 - 仍未挂,只剩第 2 条那一个原因:必须先定"缺 VS 组件时 skip 还是 FAIL"——那是留给用户裁定的事 - (选 skip 等于造一根会静默失效的门禁,与 `always: true 已失效` 那条同类坑)。与其悄悄挂一根 - 可能假绿的钩子,不如把缺口写在这里。 + 当时唯一没解的是第 2 条:必须先定"缺 VS 组件时 skip 还是 FAIL"——那是要用户裁定的事(选 skip + 等于造一根会静默失效的门禁,与 `always: true 已失效` 同类)。**2026-10-24 用户选 FAIL,本闸已挂**, + 缺口关闭。接线时的两个实测数:全闸跑完 **45 s**(28 套;本文别处写的"约 25 s"是只有 16 套时 + 的值,已过时);缺组件时钩子 **Failed**、脚本退出码 2 并打印 `[ENV]` 提示(合成自证)。 2. 它依赖 VS 可选组件(§7 第 8 条)。挂之前必须先定"缺组件时 skip 还是 FAIL"—— 选 skip 就是一个会静默失效的门禁(与 `AGENTS.md` 里那条 `always: true 已失效、必须靠 files: 覆盖` 是同一类坑); @@ -182,8 +192,7 @@ 不会假报回退(首版会,已修:并集口径下子集必然变小,所以 `--module` 子集跑跳过全部回退判定)。 真实事件(某次改动真把覆盖行弄少了)仍待观察。 -真到接线那天,片段形状如下(这份 YAML 由接线者写进 `.pre-commit-config.yaml`,说明写进 -`docs/门禁说明/` 对应那篇,不在本文维护): +落库的就是下面这份(`.pre-commit-config.yaml` 第 8 节,与文件里逐字一致,只多不少);`docs/门禁说明/README.md` ```yaml - repo: local diff --git a/docs/环境与部署/并行任务工作树方案.md b/docs/环境与部署/并行任务工作树方案.md index 6eeb9bf0..d3876fe4 100644 --- a/docs/环境与部署/并行任务工作树方案.md +++ b/docs/环境与部署/并行任务工作树方案.md @@ -397,7 +397,7 @@ docs\内存管理\RAM使用分析与优化方案.md:585 [路径引用] docs/内 | 主题 | 去处 | |---|---| -| 提交门禁 39 个钩子 | `docs/代码规范/pre-commit使用说明.md` | +| 提交门禁 40 个钩子 | `docs/代码规范/pre-commit使用说明.md` | | 自动化测试与台架流程 | `docs/测试说明/自动化测试流程.md` | | 文档写作规范(编码/链接/登记) | `docs/文档规范.md` | | 工具路径环境变量(换机) | `docs/环境与部署/本地工具路径环境变量方案.md` | diff --git a/docs/门禁说明/README.md b/docs/门禁说明/README.md index 4f575f46..7ecdecd2 100644 --- a/docs/门禁说明/README.md +++ b/docs/门禁说明/README.md @@ -1,6 +1,6 @@ # 门禁逐项说明(每种门禁一篇) -> 这一目录回答一个问题:**提交时那 39 个钩子到底在干什么、拦谁、放谁、什么时候会骗人。** +> 这一目录回答一个问题:**提交时那 40 个钩子到底在干什么、拦谁、放谁、什么时候会骗人。** > 每个门禁一篇,八段固定结构:查什么 / 为什么要它 / 触发面 / 报错怎么读 / 好处 / > **限制与盲区** / 豁免与基线 / 改门禁本身要注意什么。**最后一节是这套文档存在的理由**—— > 一份只看"拦了什么"的门禁文档,会让人以为绿就是对的。 @@ -21,7 +21,7 @@ ⚠ 增量拦截最容易误会的一点:**绿灯不等于没问题**,只等于"没有比上次更差"。 每个增量拦截门禁的基线数字都写进它自己的文档,读那篇时第一眼看的就是"现在在册多少条"。 -## 2. 索引(39 个 hook → 23 篇) +## 2. 索引(40 个 hook → 23 篇 + 1 根在 docs/测试说明/) | 门禁文档 | 覆盖的 hook id | 一句话它在查什么 | 改了哪类文件才会跑 | 文档 | |---|---|---|---|---| @@ -42,6 +42,7 @@ | [`font-charset.md`](font-charset.md) | `font-charset` | UI 用到的汉字是否都在子集字体里(缺字 = 白框) | `Drivers/BSP/UI/**`、字体脚本 | 已写 | | [`ui-golden.md`](ui-golden.md) | `ui-golden` | UI 像素回归:菜单 8 状态 + 内容页 11 页黄金参考图比对 | UI 源码、模拟器 | 已写 | | [`host-unit-tests.md`](host-unit-tests.md) | 16 个 `*-unit-test` | 主机侧单测(同一机制 16 个实例):逻辑层在无硬件的 PC 上编译并跑断言 | 各自被测模块与其测试目录 | 已写 | +| 详解在 [`../测试说明/主机单测覆盖率口径.md`](../测试说明/主机单测覆盖率口径.md)(口径 / 分母 / 基线 / 盲区都在那篇,本目录不重复) | `test-coverage` | **覆盖率总闸**:跑全部 28 套主机单测 + 已覆盖行只增不减的回退台账 | `App/`、`Drivers/BSP/`、`test/host/`、脚本与基线 | 已写(在 `docs/测试说明/`) | | [`trailing-whitespace.md`](trailing-whitespace.md) | `trailing-whitespace` | 行尾空白(改写型;CubeMX/Keil 生成文件豁免) | 全部 | 已写 | | [`end-of-file-fixer.md`](end-of-file-fixer.md) | `end-of-file-fixer` | 文件末尾必须恰好一个换行(改写型;同豁免) | 全部 | 已写 | | [`check-added-large-files.md`](check-added-large-files.md) | `check-added-large-files` | 别把大文件(日志/镜像/二进制)误提进仓库 | 全部 | 已写 | diff --git a/docs/门禁说明/gitlint.md b/docs/门禁说明/gitlint.md index 270fc71f..76d7ae33 100644 --- a/docs/门禁说明/gitlint.md +++ b/docs/门禁说明/gitlint.md @@ -27,7 +27,7 @@ ## 3. 触发面 / 看不见什么 -- `git commit`、`--amend`、`-F`、合并生成的提交都会跑;**`--no-verify` 连它和其它 38 个钩子一起跳过**。 +- `git commit`、`--amend`、`-F`、合并生成的提交都会跑;**`--no-verify` 连它和其它 39 个钩子一起跳过**。 - 不校验:提交说明与 diff 是否相符、是否真的构建/测试过、是否夹带无关改动、是否该拆成多笔。 ## 4. 报错怎么读 diff --git a/docs/门禁说明/host-unit-tests.md b/docs/门禁说明/host-unit-tests.md index 7cc16b80..5ec0d4e5 100644 --- a/docs/门禁说明/host-unit-tests.md +++ b/docs/门禁说明/host-unit-tests.md @@ -81,11 +81,15 @@ 行为不同"。桩签名改了而测试没跟着改,编译器未必报错(尤其桩返回宽类型)。 3. `include_dirs` 与源文件清单是**手写**的:新依赖一个目录就会假失败(见 §4 第 2 类),反过来, 漏了某个真实依赖可能让测试**编过却少编了一个翻译单元**(少测而不自知)。 -4. **没有对应钩子的模块 = 零回归**:本层只覆盖上表 16 个模块;其余逻辑层(含 UI 逻辑、网络 - 状态机主体)只能靠 `ui-golden`、板级套件与真机 `@test`。 +4. **专属钩子不是唯一防线**(2026-10-24 更正,原写"没有对应钩子的模块 = 零回归",如今不成立):上表 16 + 个模块各有专属钩子;仓库另有 12 套(P1 新增)没有专属钩子,改坏了由 `test-coverage` **总闸**兜 + 住——它跑全部 28 套,并守"已覆盖行只增不减"。总闸的触发面是 `App/`、`Drivers/BSP/`、 + `test/host/`、覆盖率脚本与基线;纯文档改动不触发它(那时确实没有可回归的行为)。 5. 时序与并发不在范围:中断抢占、DMA 完成回调、临界区、真机 tick 抖动都照不到(历史上最贵的 几个坑正是这类,见 `docs/问题记录/*_Trap_Records.md`)。 -6. 断言写的是"作者当时想到的分支":分支覆盖是否充分,没有任何度量(无覆盖率统计)。 +6. 断言写的是"作者当时想到的分支":**行 / 函数覆盖有度量了**(`test-coverage` 总闸出 + Cobertura 并守基线台账),但**分支覆盖仍无**——按用户裁定 2026-10-23 记为**已接受缺口**, + 口径与理由见 `docs/测试说明/主机单测覆盖率口径.md` §2.1,不是待办。 ## 7. 豁免与基线