@@ -19,7 +19,7 @@
| 文档与治理 | 🟢 良好 | 39 个提交钩子 + 16 套主机单测;工程 md 91 份;`doc-links` 全库 0 问题 | 新增 `编号门禁增量拦截` 与内存快照回填常态化 |
| 内存与 Flash | 🟡 可控但已换位置 | SRAM1 **57.4% ** (余 55,808 B)、Flash **22.1% ** 、**CCM 87.9%(余 7,904 B) ** | **红线从 SRAM1 迁到 CCM ** (堆 2026-10-11 迁 CCM + 池 40→16 KB 后 SRAM1 已宽松) |
| 可靠性与自恢复 | 🟢 良好 | 4 个 Fault 全采集现场;IWDG 五面 `required_mask` ;复位原因与 `[STACK]` 水位常开 | 台架复跑持续 PASS(今日真机 708 ms 起网、无 `[ERR]` /Fault) |
| 测试与验证 | 🟡 半办 | 16 套主机单测 + `ui-golden` /`font-charset` 入门禁;TC 双向追溯 593∶271 交集 265; **覆盖率口径已落地**( `../测试说明/主机单测覆盖率口径.md` : 16 套并集 2552/2893 行) | `coding-standard` **仍无 `malloc` 正则 ** ; 覆盖率**未挂 pre-commit**(口径文档 §8 三条理由)、 **没有 分支覆盖** |
| 测试与验证 | 🟡 半办 | 16 套主机单测 + `ui-golden` /`font-charset` 入门禁;TC 双向追溯 593∶271 交集 265; * * §5.5 两条补强已全部落地**(2026-10-23:覆盖率口径 `../测试说明/主机单测覆盖率口径.md` + `malloc(` 正则 `coding-standard` CODE-RULE-20) | 覆盖率**未挂 pre-commit**(口径文档 §8 三条理由); **分支覆盖已裁定不做** (§2.1 决策记录)→ 现在真正的半办是**覆盖面**:首登照出 **125 个生产源文件从未进过任何主机单测 ** |
| UI 标准化 | 🟢 基本落地 | `ui_tokens.h` ( L0 令牌)+ `ui_page_registry.c` (描述符注册表)+ 7 个内容页 + 两枚 UI 钩子 | **U1/U3/U4 从"未做"变为已落地 ** ; U2 只有 `ui_w_records` 一个组件 |
### 0.3 主线建议(2026-10-22)
@@ -30,8 +30,10 @@
92% 钩子会先报警。SRAM1 与 Flash 都已不是卡点。
3. **结构性内存优化整簇暂缓 ** ( R-2 后半 / R-3 / R-4 / R-6,用户裁定 2026-10-15),治理面常开;
重开条件见 §5.3。今天又收口一项:**F-4 判定不做、F-6 已落地、F-5 降级观察**(§5.4)。
4. **验证的下一步不是加用例而是补面 ** :覆盖率口径 + `malloc(` 门禁正则(§5.5 第 5 条),把"读代码
才发现"的缺陷类型(N-12/N-13 那类)变成机器拦截。
4. **补面两条已全部落地(2026-10-23) ** :覆盖率口径( `../测试说明/主机单测覆盖率口径.md` ) +
堆禁令机器拦截(`coding-standard` **CODE-RULE-20 ** ,§5.5 第 5 条)。**剩下的量在覆盖面**:覆盖率首登
照出 **125 个生产源文件从未进过任何主机单测 ** (约 11,839 行,`net_socket.c` 1,251 行最大)——给它们建桩
比把在测的 19 个文件从 88% 推到 95% 值钱得多(N-12/N-13 那类"读代码才发现"的缺陷就是这么照出来的)。
5. **仓库体积重心在外部资料 ** ( `docs/芯片手册` 717 文件 / 39.5 MB,占 docs 的 87%; `.git` 248 MB),
见 §3.3 D-1 与登记簿 N-29——已决策暂不处理,但**推送/对外协作前必须重评**。
@@ -67,7 +69,9 @@ B0h 验 `ECC_EN`**——原先那两行的注释自己写着"否则后续 I/O
| 存储/日志 | P2-10 ❌(N-25) | 卷锁现有 5 个使用者(配置原子写 / 日志 / SOE / lftpd_io / defaultTask),容量轮转删除与 UI 读并发仍未全覆盖 | 登记簿 S-5 面(卷锁覆盖面) |
| 仓库 | D-1 ❌(N-29) | jpg 707 个 / 37.3 MB; `docs` 45.3 MB 里芯片手册占 39.5 MB; `.git` **248 MB ** | 降采样 / 拆 submodule / LFS 三选一 + 历史重写;已决策暂不做 |
| UI | U2 残余(U 簇) | 组件库只有 `ui_w_records.c/h` 一个;其余仍是页内自绘 | 按产品需求决定要不要继续抽组件 |
| 测试 | ~~覆盖率 ~~ ✅ **2026-10-23 落地 ** / `malloc` 门禁 ❌ | 覆盖率:`scripts/check_test_coverage.py` + 基线 `scripts/test_coverage_baseline.json` (口径与**盲区**见 `../测试说明/主机单测覆盖率口径.md` )——首登 16 套并集 2552/2893 行(88.2%)、**未触及 125 个生产源文件 / 约 11.8k 行**; `check_coding_standard.py` 仍无 `malloc(` 正则 | 剩 `malloc(` 正则 + 覆盖率挂钩子(先定「缺 VS 组件时 skip 还是 FAIL」);见 §5.5 与 §4.8 |
| ~~测试 ~~ ✅ **两条全部闭环 2026-10-23 ** (覆盖率口径 + 堆禁令 CODE-RULE-20) | 覆盖率:`scripts/check_test_coverage.py` + 基线 `scripts/test_coverage_baseline.json` (口径与**盲区**见 `../测试说明/主机单测覆盖率口径.md` )——首登 16 套并集 2552/2893 行(88.2%)、**未触及 125 个生产源文件 / 约 11.8k 行**。堆禁令: `check_coding_standard.py` **CODE-RULE-20 ** (硬拦截、零基线;唯一豁免 = `simulator/sim/cfg_file_store_sim.c`
| 的平台分配后端宿主侧实现,见 `../门禁说明/coding-standard.md` §7) | 无(残余三项**已归口或已裁定**:覆盖率挂
| pre-commit 的前置写在口径文档 §8;分支覆盖**不做**(§2.1 决策);扩大覆盖面按 §0.3 第 4 条) |
| CI | T-7 ❌ | 远程 CI 只跑本机一台 runner,且不跑 `@build` | 保持现状即可;扩机器前重读 `docs/CI/README.md` |
## 1. 审查方法与证据基线
@@ -226,8 +230,15 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
- 主机侧 **16 套**单测(提交门禁实跑);板级 `test/board/` 19 文件、套件开关互斥受 `test-suite-switch` 管
- TC 双向追溯:代码侧 593 号 / 文档侧 271 号 / 交集 265,差异 13 处(当前仅告警,未升门禁)
- **覆盖率口径已落地 ( 2026-10-23) **: `scripts/check_test_coverage.py` ( mscoverage 动态插桩 + Cobertura 并集)
这条铁律目前只靠人读 + 模拟器照不出来(MicroLIB C 堆仅 512 B)
- **两处空白已全部闭合 ( 2026-10-23) **: ① 覆盖率口径 → `scripts/check_test_coverage.py` ( mscoverage 动态
插桩 + Cobertura 并集)+ 基线 `scripts/test_coverage_baseline.json` ;口径 / 工具链实测 / **九条盲区**见
`../测试说明/主机单测覆盖率口径.md` (首登 2552/2893 行、19 文件在测、**未触及 125 文件**)。
② C 库堆禁令 → `check_coding_standard.py` **CODE-RULE-20 ** (拦 `malloc/calloc/realloc/strdup/strndup/
free/aligned_alloc(` ;硬拦截、零基线;唯一豁免 = 平台分配后端的宿主侧实现
`simulator/sim/cfg_file_store_sim.c` ,见 `../门禁说明/coding-standard.md` §7)。禁令本身的事实源仍是
AGENTS.md 平台约束与 `../内存管理/RAM使用分析与优化方案.md` §4.2.1——**为什么非得机器拦**: MicroLIB
的 C 堆只有 512 B,而**模拟器(宿主 CRT 堆)完全正常、照不出来**——配置引擎 2026-09-18 的堆分配纠偏
( 4 处 KB 级 `malloc` 落错堆)与 lftpd 的 `malloc` 隐患都是**先上线才发现**的那类
- 破坏性套件(`ENABLE_DESTRUCTIVE_TESTS` )有**持久代价**(NAND 坏块记账不可逆),默认关闭是正确设计
## 5. 内存与 Flash( 2026-10-22 实测收口)
@@ -285,7 +296,8 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
2. `check_mem_numbers.py --scan/--write` :文档里的内存数字一律由 `.map` 反查,禁止手填
3. 改栈 / 加大局部变量前后各跑一次 `stack_report.py` (静态 × 真机 `[STACK]` )
4. 子系统配额与 CCM 台账 → `../内存管理/RAM预算台账.md` ;趋势 → `scripts/ram_baseline.tsv`
5. **仍未落地的一条补强 ** : `check_coding_standard.py` 加 `malloc(` 正则(把 C 库堆禁令变成机器拦截)。
5. **两条补强已全部落地(2026-10-23) ** : `malloc(` 正则 = `check_coding_standard.py` **CODE-RULE-20 **
(硬拦截、零基线,判据与豁免见 `../门禁说明/coding-standard.md` §1/§7)。
另一条「覆盖率口径」✅ **已落地 2026-10-23 ** → `../测试说明/主机单测覆盖率口径.md` (口径 / 基线 / 盲区
一处写完,此处不复述)。⚠ 它落地的是**行与函数**覆盖,**分支覆盖本机拿不到**(无 gcc, MSVC 14.51
已无 `/coverage` )——「覆盖到哪些分支」这一半**已裁定不补**(不为它引入第二套工具链,见
@@ -334,7 +346,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
|---|---|---|
| ~~W3 预算与内存~~ ⏸ | R-9 ✅ / R-2 后半、R-3、R-4、R-6 暂缓 | 暂缓期间无验收动作,观测照常 |
| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;S-5 契约/门禁已落地并收编三模块,`consume-result` 已把"丢弃返回值"纳入机械检查(基线 19→1→38;剩 GD5F 码集收编、C4 快照、37 条丢弃点、标坏策略) |
| W5 验证常态 | ~~覆盖率~~ ✅ 2026-10-23 、`malloc` 正则、TC 差异 13 处升门禁 | 逢窗口顺手做 |
| W5 验证常态 | ~~覆盖率~~ ✅、~~ `malloc` 正则~~ ✅(均 2026-10-23) 、TC 差异 13 处升门禁 | 剩 TC 一条 |
| W6 UI 扩展 | U2 残余、U5 页量 | 随产品需求,不再单列专项 |
| W7 仓库决策 | D-1(N-29) | 已决策暂不处理;对外交付前重评 |
@@ -451,10 +463,25 @@ admin/admin( SHA-256 `pwd_hash`)→ 该脚本**必然登录失败**;它是
| | 数字联动照做:两份 `data/` 快照重跑、`ram_baseline --update/--write` 、`check_mem_numbers --write` 、FLASH 与
任务栈两张配图重算重生成(逐文本复查越界 = 0)、本文 §1.3/§5.1/§5.4 与部署文档 §3.3 同步;§0.4 的 S-2 行按
「完成即销项」改写、§9 拆出 S-2 行标 ✅。 |
| **2026-10-23 ** | **执行 §5.5 第 5 条后半:C 库堆禁令变成机器拦截 ** (§4.8 空白 ②,CODE-RULE-20) | 按本文件 §8 的既有纪律**先全库普查再动手**:`coding-standard` 钩子范围内 C/H 共 **151 个文件 ** , `malloc` 族只命中
| | | `simulator/sim/cfg_file_store_sim.c` 两行(`cfg_store_alloc` /`cfg_store_free` = 平台分配后端的**宿主侧实现**,
| | | 设计依据写在 `../子系统设计/配置文件设计说明.md` 的"MCU 端 = pvPortMalloc、模拟器端 = 宿主 CRT")——**不是漏网**,
| | | 所以零基线、直接硬拦截;豁免走脚本里的 `HEAP_ALLOC_ALLOWLIST` (**文件路径 + 函数名**双条件),刻意不做目录级豁免:
| | | 下次谁要碰 C 堆就得往那张表加一行,评审看得见。判据 = 掩码后文本上匹配 `\b(malloc|calloc|realloc|strdup|strndup|
| | | free|aligned_alloc)\s*(\(` :大小写敏感 ⇒ `pvPortMalloc` /`vPortFree` 不误报;词边界 ⇒ `dhara_nand_is_free(` 、`buf_free(`
| | | 不误报;注释与字符串里写 `malloc` 不报。**自证**:临时样例三处(`malloc/calloc/free` )各报一次、五种陷阱形态全部不报;
| | | `pre-commit run coding-standard --all-files` 实测 **Passed ** ( = 全库零存量)。
| | | **顺带消掉一处自相矛盾 ** :规范 V1.0 §6.1 的示例自己写着 `malloc` /`free` (通用 C 模板遗留),已在其下方加
| | | 「本仓例外,别照抄」批注并把事实源指向 AGENTS.md 平台约束;`docs/门禁说明/mem-budget.md` 里"这条纪律只靠评审"
| | | 的旧说法同步更正。AGENTS.md 平台约束条加"已机器拦截"一句。**本轮零 `.c/.h` 改动**(只动门禁脚本与文档),
| | | 未跑 `@build` 、未占 build/bench 锁;详解归 `../门禁说明/coding-standard.md` (本处不复述判据) |
| **2026-10-23 ** | **执行 §5.5 第 5 条前半:覆盖率口径落地并销项 ** (§4.8 空白 ①) | 新增 `scripts/check_test_coverage.py` (纯标准库)+ 基线台账 `scripts/test_coverage_baseline.json` 。**工具链是实测出来的、不是查到的**:本机 PATH 上 gcc/gcov/clang/llvm-cov/lcov/gcovr **全无 ** ; `cl /coverage` 、`/fsanitize=coverage` 在 MSVC 14.51 已被砍——**只报 `warning D9002 忽略未知选项` ,然后照常编译成功**(拿它做覆盖率=静默假数据,比报错更坏);`Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe` ( 17.0)的 `collect` /`analyze` /`instrument` 一律只打印「用法: CodeCoverage <command>」。最终走 **mscoverage( `Microsoft.CodeCoverage.Console.exe` 18.x) `collect -f cobertura` ** 的**加载期动态插桩**,并实测它**原样透传子进程退出码**(子进程 `return 3` → `collect` 也返回 3)→ 于是「插桩运行下仍必须 PASS」可以当硬条件用。 |
| | | **口径 ** :只算 `App/**` 与 `Drivers/BSP/**` 的行/函数、多套单测取**并集**、生成物(文件头写「自动生成 / 请勿手改」)不进分母、分母一律来自插桩报告**不猜行数**;配置零复制——脚本 import 各运行器的 `TestBuild` 、开关取 `unit_test_harness._BASE_FLAGS` ,只追加 `/Zi /Od` 与 `/link /INCREMENTAL:NO` ,发现规则与 L0.5 同一条 `test/host/**/run_unit_test.py` , **不会漂出第二套口径**。 |
| | | **首登数字(2026-10-23) ** : 16 套全通过,并集 2552/2893 行(88.2%)、193/207 函数(93.2%)、19 个生产文件在测,**未触及 125 个生产源文件 / 约 11,839 行**( `net_socket.c` 1,251 行最大)——最后一栏才是这套口径真正照出来的东西。**门禁自证**:人为抬高基线后,行 / 函数 / 总计三条回退路径都如期报 REGRESS 且退出码 1;`--module` 子集运行不会假报总计回退(首版就会,已修)。**刻意未挂 pre-commit**:三条理由与可直接粘的 YAML 片段写在 `../测试说明/主机单测覆盖率口径.md` §8,接线者照 §8 落地即可。 |
| | | **仍开放 ** :§5.5 第 5 条的另一半( `check_coding_standard.py` 加 `malloc(` 正则)与**分支覆盖**。分支覆盖**不是拿不到**:同日被用户问住后复测,本机 WSL2 Ubuntu 有 gcc/gcov 15.2.0, 16 套单测在 gcc 下**全部编过且全绿**、 `gcov -b -c` 出分支数据( `nand_ftl.c` 分支至少走过一次 41.40%);但换编译器就是换分母( `sha256.c` gcc 105 行 vs MSVC 114 行)→ **用户裁定只保留 MSVC 一条通道 ** (多一套工具链=多一套分母 +多一本台账 + 多一个"这台机器有没有 WSL"的未决项),分支覆盖**记为已接受缺口**、不是待办。实测表与决策依据见 `../测试说明/主机单测覆盖率口径.md` §2.1。本轮只加 `.py` / `.json` / `.md` ,无 `.c/.h` 改动,不重跑 `@build` ;文档面动 §0.1 / §0.4 / §4.8 / §5.5 / §7 与 `docs/README.md` 索引各一处。 |
| | | **同日另一半也落地 ** (§4.8 空白 ②): `check_coding_standard.py` 加 **CODE-RULE-20 ** 禁 C 库堆分配,
| | | 硬拦截零基线(全库普查 151 个 C/H 只有 `cfg_file_store_sim.c` 两行命中 = 平台后端的宿主侧实现,
| | | 点名进 `HEAP_ALLOC_ALLOWLIST` ,刻意不做目录级豁免);规范 §6.1 示例自己写着 `malloc/free` 的
| | | 自相矛盾也同日加了"本仓禁止"批注。**只剩分支覆盖**一条。分支覆盖**不是拿不到**:同日被用户问住后复测,本机 WSL2 Ubuntu 有 gcc/gcov 15.2.0, 16 套单测在 gcc 下**全部编过且全绿**、`gcov -b -c` 出分支数据(`nand_ftl.c` 分支至少走过一次 41.40%);但换编译器就是换分母(`sha256.c` gcc 105 行 vs MSVC 114 行)→ **用户裁定只保留 MSVC 一条通道 ** (多一套工具链=多一套分母 +多一本台账 + 多一个"这台机器有没有 WSL"的未决项),分支覆盖**记为已接受缺口**、不是待办。实测表与决策依据见 `../测试说明/主机单测覆盖率口径.md` §2.1。本轮只加 `.py` /`.json` /`.md` ,无 `.c/.h` 改动,不重跑 `@build` ;文档面动 §0.1 / §0.4 / §4.8 / §5.5 / §7 与 `docs/README.md` 索引各一处。 |
| **2026-10-23 ** | **术语迁移 ** : `bsp-err` 的四项检查从 `R1/R2/R3/R4` 改成语义标签 | 用户一句「R4
| | 到底是什么」问出来的。这四个号**是我自己造的**,而 `R1~R8` 在本仓是"专项风险"的裸形编号
| | ( `id_registry.json` 已登记那一族,保护规划 `R1~R8` 、看门狗 `R1~R7` 的风险表都在用),`R-2`
@@ -664,4 +691,8 @@ USER CODE 区内只留「读 RCC_CSR → 写 RMVF 清除」两句、顺序约束
| **R-9( `LV_MEM_SIZE` 40→16 KB,登记簿 N-27 原 W3 的第一步,2026-10-15 已执行;该簇其余部分同日整簇暂缓) ** | 取值依据与再降 8 KB 的前置写在 `Lib/lv_conf.h` 与 `simulator/lv_conf.h` 的池注释;实测水位 → `../内存管理/RAM使用分析与优化方案.md` §2.6.2 与 §阶段 1.1(已执行);解锁链第 ① 步 ✅(后续步骤暂缓)→ `../内存管理/RAM预算台账.md` §3;池容量口径 → `../UI设计/UI_设计架构.md` §1.2、`../UI设计/UI_界面制作流程.md` C4 |
| **N-12 / N-13 ** ( dhara 移植层 `is_free` 精度 + HAL 越界守卫,登记簿 **N-21 ** , 2026-10-15 已销项) | `../架构设计/STM32F4-Base存储架构说明.md` §5.3(移植层两条硬约束:整页精确的原因链 + 错误码取舍与"为什么不会静默清卷")+ `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` 的 `ftl_page_in_range` / `ftl_block_in_range` / `dhara_nand_is_free` 注释 + 主机侧单测 `test/host/nand_port/` (含"旧算法只看页首 64 B"的对照用例,防止将来被改回去) |
| **N-16 ** (日志读写放大,登记簿 **N-24 ** , 2026-10-15 已销项) | `../子系统设计/运行与自检记录设计说明.md` §4.4(写侧批量合并 + **掉电语义不变**的论证 + 失败回投不丢条目 + 容量轮转只在慢路径)与 §4.5(读侧 8 条窗口 + 写者代数失效 + API 不变)+ `App/log/sys_log_mcu.c` 的 `s_format_rec` / `s_append_rec` / `s_append_many` / `s_read_raw` 注释 |
| * * §4.8 空白 ①「无覆盖率口径」 + §5.5 第 5 条前半 ( 2026-10-23 销项;§5.5 后半的 `malloc(` 正则仍开放 ) ** | `../测试说明/主机单测覆盖率口径.md` (口径 / 工具链实测 / 统计面 / 门禁姿态 / **九条限制与盲区 ** / 未挂钩子的三条理由与待粘 YAML)+ `scripts/check_test_coverage.py` ( `--list` / `--module` / `--update-baseline` / `--md` / `--json` ) + 基线台账 `scripts/test_coverage_baseline.json` (首登 2552/2893 行、未触及 125 文件) |
| * * §4.8 两处空白(①无覆盖率口径 ②无 `malloc(` 正则) + §5.5 第 5 条整条 ( 2026-10-23 全部 销项)** | `../测试说明/主机单测覆盖率口径.md` (口径 / 工具链实测 / 统计面 / 门禁姿态 / **九条限制与盲区 ** / 未挂钩子的三条理由与待粘 YAML)+ `scripts/check_test_coverage.py` ( `--list` / `--module` / `--update-baseline` / `--md` / `--json` ) + 基线台账 `scripts/test_coverage_baseline.json` (首登 2552/2893 行、未触及 125 文件)。
空白 ② → `docs/门禁说明/coding-standard.md` ( CODE-RULE-20 判据 / 唯一豁免 / 盲区 / 落地前普查数)+
`scripts/check_coding_standard.py` ;禁令本身的事实源仍是 AGENTS.md 平台约束与
`../内存管理/RAM使用分析与优化方案.md` §4.2.1;另 `../代码规范/嵌入式C语言代码规范_V1.0.md` §6.1 的示例
自己写着 `malloc/free` (通用 C 模板遗留),已同日在示例下加"本仓禁止"批注 |