chore(merge): 回填内存台账与钩子规范化,收口 task/cov-host-line-coverage

done cov 已把 15 个提交合入主干(合并提交 c8de948),但它的门禁按已知顺序跑在
@build 之前(skill 实测陷阱那条),比对用的是合并前的旧 .map,台账必然失配。
这里按 skill 的官方处置在主工作树回填,使主干自身全绿。

内存台账(@build 已在 done 里跑过:0 错 0 警,Code=213652 RO=18860 RW=1192
ZI=131504;主工作树的 .map 是权威值):
- scripts/ram_baseline.py --update(趋势行 2026-10-24:RW_IRAM1 75,272 B 57.4% /
  RW_CCM 57,376 B 87.9%)+ --write(受管区);check_mem_numbers.py --write。
- RAM使用分析与优化方案.md 两处正文数字:「当前状态」那句刷成合并后的真值
  (75,264 → 75,272,日期改 2026-10-24);2026-10-23 那条变更行里的裸 B 值
  232,536 改为 KB + 快照文件指针——**历史结论一字未改**,只是不再在正文里留
  会过期的裸字节值(--scan 的判据正是"曾是区域值、现已不是",无内联豁免)。
- 补一行本次合并的变更:RAM +8 B(net_cfg.c 为 WAIT 契约修复新增 s_save_busy)、
  Flash -24 B(lftpd_string 改名删死循环 + net_select 去冗余守卫)、CCM 零增量。

覆盖率口径文档:修我自己留下的占位符泄漏——§9 有一段写的时候用 @@ 当反引号哨兵
(run_code 里反引号与模板串冲突),漏做还原,共 52 处。后果不只是难看:
- `docs/网络/网络自愈架构设计.md` §4.10 因失去反引号而不再被识别为"紧邻写法",
  被 doc-links(钩子带 --sections-strict)按**自引用**判成失效章节——这条真红了。
- "test/host/common/ch395f_fake.h + .c" 被整体当路径引用检查,报文件不存在。
- 另有一处未配对的 @@ 与一个 ``` 残留,以及过期计数("套件 21+"、"族数 16"),
  改为落地实测值:TC-HOST 族 16 → 22、套件 16 → 28,并指向下方落地进度表。
  核对:该文档 @@ 残留 0、U+FFFD 0、无 CRLF。

12 个 run_unit_test.py:end-of-file-fixer 去掉末尾多余空行。分支期我用 --no-verify
提交(只为跳过 .map 台账那两条已知误报),把这两个改写型钩子的规范化机会一起
跳掉了——本批由钩子自己改正,**内容零改动**,也正是"提交门禁不该长期绕过"的账单。

自查:pre-commit run --all-files 在本工作树 **38 Passed / 0 Failed**(含 16 个主机
单测钩子实跑);doc-links --all --sections-strict 0 问题;mem-numbers --scan 绿。
未 push(推送需用户明确指示)。
This commit is contained in:
2026-10-24 00:07:05 +08:00
parent c8de948089
commit ec2dbc9e01
16 changed files with 40 additions and 47 deletions
@@ -316,11 +316,11 @@
两处"现状桩"翻成正向契约,另新增 `TC-NETSEL-012c`;全量 22 套件仍然全绿。
### 假 CH395F 芯片:实施规格(2026-10-23 立项,交给下一会话执行)
**为什么要它**:@@NET@@ 域剩下的三块(@@net_pending.c@@ 287、@@net_events.c@@ 263、@@net_listener.c@@ 191 = 741 行)
**为什么要它**:`NET` 域剩下的三块(`net_pending.c` 287、`net_events.c` 263、`net_listener.c` 191 = 741 行)
全都把芯片当真理读,真机上这些语义只在总线上一闪而过、台架不可复现。做完它,P2 的
@@net_socket.c@@(1,251 行,全仓最大逻辑块)也一起解锁。
`net_socket.c`(1,251 行,全仓最大逻辑块)也一起解锁。
**真实原型(@@Drivers/BSP/CH395F/ch395f.h@@ 逐条核出,假件必须一字不改地照抄签名)**:
**真实原型(`Drivers/BSP/CH395F/ch395f.h` 逐条核出,假件必须一字不改地照抄签名)**:
```cpp
uint8_t ch395f_sock_state_cached(uint8_t sn); /* 返回 ch395f_sock_state_t 枚举值 */
@@ -341,43 +341,43 @@ uint16_t ch395f_link_change_count(void);
```
**假芯片必须复刻的两条芯片怪癖**(不复刻就等于把被测代码的坑假件掉):
1. @@get_sock_int_status@@ 是**整体读即清**、芯片没有按位清除手段。假件要按"每次调用
1. `get_sock_int_status` 是**整体读即清**、芯片没有按位清除手段。假件要按"每次调用
返回该 socket 的挂起位图并**同时清空**"实现,并允许逐 socket 独立预置——否则
照不出 AGENTS「模块约定」里 B3 那条实证坑:*"顺手全量清一遍"等于把别的在用
Socket 的门焊死*。
2. 状态是**缓存值**而非现场值(@@sock_state_cached@@/@@link_up_cached@@):假件要把
2. 状态是**缓存值**而非现场值(`sock_state_cached`/`link_up_cached`):假件要把
"缓存更新发生在哪一次调用之后"做成可脚本化,才能测出对账循环读到陈旧状态的路径。
**脚本化模型**(沿用本仓既有假件风格:@@nand_port@@ 的真头+假 diskio、@@lftpd_io@@ 的可注入假堆):
**脚本化模型**(沿用本仓既有假件风格:`nand_port` 的真头+假 diskio、`lftpd_io` 的可注入假堆):
每个读函数一条**按调用序号取的返回队列**(耗尽后回落到最后一值),每个写函数一条
**调用记录**(次数 + 最近一次入参),另给 @@g_chip_call_count[函数名]@@ 让"这一轮不该碰芯片"
的契约可断言;@@reset@@ 一次全清。所有假件写在测试单元 @@.c@@ 里,**不建共享 @@.so@@/库**。
**调用记录**(次数 + 最近一次入参),另给 `g_chip_call_count[函数名]` 让"这一轮不该碰芯片"
的契约可断言;`reset` 一次全清。所有假件写在测试单元 `.c` 里,**不建共享 `.so`/库**。
**桩放哪里(一个需要拍板的点,先给推荐)**:三个模块都要用同一份假芯片,@@test/host/*/stub/@@
**桩放哪里(一个需要拍板的点,先给推荐)**:三个模块都要用同一份假芯片,`test/host/*/stub/`
每模块一份会漂移(三处签名各改一半 = 假件与被测代码同时错还不报错)。推荐
@@test/host/common/ch395f_fake.h + .c@@(与 @@unit_test_harness.py@@ 同级,三模块的
@@include_dirs@@ 各加这一条),代价是它不进 MCU 构建、要靠 @@test-cppcheck@@ 覆盖。
若沿用"每模块自带桩"的现有惯例,则必须让三份桩都 @@#include@@ 同一份头以锁死签名。
`test/host/common/ch395f_fake.h` 与同名 `test/host/common/ch395f_fake.c`(与 `unit_test_harness.py` 同级,三模块的
`include_dirs` 各加这一条),代价是它不进 MCU 构建、要靠 `test-cppcheck` 覆盖。
若沿用"每模块自带桩"的现有惯例,则必须让三份桩都 `#include` 同一份头以锁死签名。
**三个文件的真实依赖面(逐个核出,别按猜的排工作量)**:
- @@net_listener.c@@(191 行)—— **最便宜,先做它**:@@net_drain_chip_rx@@/@@net_relisten_locked@@/
@@net_relisten_done@@ 全是**本文件内的 static**(2026-10-23 实测确认),所以它只需要
芯片假件 + @@net_get_sock@@ + @@net_pending_active@@ + @@HAL_GetTick@@ + @@dbg_log@@,
外加 @@test_config.h@@/@@net_latency_test.h@@ 两个可空壳的头。**不需要假造 net_socket.c 内部函数**
- `net_listener.c`(191 行)—— **最便宜,先做它**:`net_drain_chip_rx`/`net_relisten_locked`/
`net_relisten_done` 全是**本文件内的 static**(2026-10-23 实测确认),所以它只需要
芯片假件 + `net_get_sock` + `net_pending_active` + `HAL_GetTick` + `dbg_log`,
外加 `test_config.h`/`net_latency_test.h` 两个可空壳的头。**不需要假造 net_socket.c 内部函数**
(我上一轮把它当成前置成本,是错的,特此更正)。判据现成:单连接 socket 复用、
按网络代次重建监听、@@net_listener_armed@@ 门控(@@docs/网络/网络自愈架构设计.md@@ §4.10)。
- @@net_pending.c@@(287 行)—— 要 @@net_listener_request@@(可用一个只记录调用的假件),
契约是挂起 recv 的注册/超时/取消与"写失败回置"那族;@@tick@@ 收 @@net_sock_t *@@ 表参数
(@@net_get_sock@@ 对未用槽位返 NULL,tick 需读 @@in_use == 0@@ 兜底 —— AGENTS 已记)。
- @@net_events.c@@(263 行)—— 依赖最多(@@net_sock_raw@@/@@net_listener_settle@@/
@@net_pending_cancel@@/@@net_send_pending_cancel@@/@@xTaskGetTickCount@@),**放最后**。
注意 @@net_sock_raw@@ 是刻意不给应用层用的现场指针(头注释写明"仅供 net_events.c 的
按网络代次重建监听、`net_listener_armed` 门控(`docs/网络/网络自愈架构设计.md` §4.10)。
- `net_pending.c`(287 行)—— 要 `net_listener_request`(可用一个只记录调用的假件),
契约是挂起 recv 的注册/超时/取消与"写失败回置"那族;`tick` 收 `net_sock_t *` 表参数
(`net_get_sock` 对未用槽位返 NULL,tick 需读 `in_use == 0` 兜底 —— AGENTS 已记)。
- `net_events.c`(263 行)—— 依赖最多(`net_sock_raw`/`net_listener_settle`/
`net_pending_cancel`/`net_send_pending_cancel`/`xTaskGetTickCount`),**放最后**。
注意 `net_sock_raw` 是刻意不给应用层用的现场指针(头注释写明"仅供 net_events.c 的
对账循环使用"),假件要保留"对未在用槽位也能拿到项"的行为,否则对账兜底路径测不到。
**验收顺序**:@@net_listener@@ 绿 → 立刻登记基线(哪怕只有它一个)→ @@net_pending@@ → @@net_events@@;
每步都要 ```build 0/0 + 21+ 套件全绿 + --update-baseline + 补建 @@TC-HOST-*@@ 族@@(族数现在 16,
三块做完应为 19),并且**先补族再往文档里引这些号**,否则 @@id-check@@ 会按台账外新账卡提交
(本会话已被 @@TC-NETCFG-028@@ 实测拦过一次)。
**验收顺序**:`net_listener` 绿 → 立刻登记基线(哪怕只有它一个)→ `net_pending` → `net_events`;
每步都要 @build 0/0 + 全部套件绿 + --update-baseline + 补建 `TC-HOST-*` 族(本规格落地时族数
16 → 22、套件 16 → 28,实测见上方落地进度),并且**先补族再往文档里引这些号**,否则 `id-check` 会按台账外新账卡提交
(本会话已被 `TC-NETCFG-028` 实测拦过一次)。
### P2|值得但工程量大(5 个 `.c` = 2,226 行)