41c277a8ae76c70e7705243c3510151025f53cd4
167 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d9ab9552c3 |
chore(mem): 回填 UI-font 合入后的 Flash 台账快照
task/UI-font-subset 合入(RO-data 18,860→20,388 B,+1,528 B, 中文字库 119→179 字 / 274 字形所致;Code=214104、RW=1,204、 ZI=131,852 不变,RAM 零增量)。 done 后统一回填,清 lane-b 分支侧 --no-verify 欠账。 |
||
|
|
65c5e7d310 |
chore(mem): 回填 cal-scale 合入后的 Flash/RAM 台账快照
cal-scale-doc 合入(Code 213780→214104,+324 B,Flash 增长来自 CFG_VAL_INT 编解码与错误分支;RW=1204 / ZI=131852 不变,RAM 零增量)。 ram_baseline.tsv 记录 2026-10-25:RW_IRAM1 75,632 B (57.7%) / RW_CCM 57,376 B (87.9%)。done 后统一回填,替代分支侧 --no-verify 欠账。 |
||
|
|
15aa08552c |
docs(问题记录): 删除项目审查与优化建议报告,结论并入模块文档与编号规范
用户裁定:该报告大部分内容已闭环,可删除;仍有价值的迁到合适文档。删前逐条核对, 确认绝大多数结论在仓内早已有权威副本(LV_DRAW_SW_COMPLEX / osFlagsNoClear / SPI 分频 8 / 分层反向依赖存量基线等),只迁真正独有的四类内容: 1. 暂缓理由与重开条件(三条量化门槛)→ RAM 使用分析与优化方案 §4,并明确「本节是唯一 权威」,其它文档只留指针;同时把 10 处指向报告 §5.3 的重开条件引用改指该节。 2. 2026-10-22「常驻 vs 动态」三项诊断(静态前五块 43,462 B = 58%;动态面只有 heap_4, 故栈余量=CCM 余量;栈深主因是日志/配置写链)→ 同文档 §2.5。 3. `_fp_digits` 1,018 B 删不掉的机理与 F-6 根因修法(Q10 宏改单精度,−332 B,4.49e6 例 逐位等价)→ §2.6.4。 4. 「UI 侧缓冲进 CCM」要按 DMA 可达性二分(LVGL 池可进,帧缓冲/推流缓冲不可)→ 暂缓清单。 编号侧(报告曾是 6 个族的属主登记处): - 原报告 §9 的 26 行「号 → 现在详述于」对照表迁到 docs/编号规范.md 新增 §4.5,6 族 owner_docs 改指该节。规范正文含大量写法示例,直接当定义集会误伤,故 check_ids.py 新增 `_def_owners`:snapshot_only 族剔除规范正文,只恢复可判定、不参与悬空/撞号/裸形 /紧邻判定(实测若不分,裸形 S1 误报 238 条、S-3 与登记簿 S-3 误判重复定义)。 - 11 个已死号(N-03b/N-10~N-13/N-20/N-21/N-24/N-26/S-1/S-2)在 id_aliases.json 登记 status=void 并写明去处——它们被约 110 处代码注释与文档引用,原先只靠「报告里顺带提过 一句」才算有定义,报告一删就集体悬空。N-21/S-1/S-2 一号两义,注记已分别标明。 - 顺带修 id_aliases.json 的 note 被脚本累积重复 26 次;修 registry 里 3 处仍指向改名前的 任务卡文件名(W1-采集保护收尾.md / W8-数据可信.md),以及 iec104_master.py 两处同类注释。 门禁:check_ids --strict 通过(可判定 8747/10410 = 84.02%,基线由 82.6% 抬到 84.0%); check_doc_links --all --sections-strict 通过(177 文档);pre-commit 相关钩子全过。 |
||
|
|
1e6996ecd0 |
docs(任务规划): 波次号 W1~W8 改为语义名,两个任务卡文件改名
用户裁定:W4 这类字母+数字让人难以理解。W 号看着像执行顺序,实际 W8 排在 W3/W4 之前,读者必须先查表才知道谁先谁后;去掉编号后执行顺序就是 §2.0 表 从上到下,少一层映射。 - 波次改语义名:采集保护收尾 / 数据可信 / 预算与内存 / 结构债 / 验证常态 / UI 扩展 / 仓库决策 - 文件改名(git mv):W1-采集保护收尾.md → 采集保护收尾.md, W8-数据可信.md → 数据可信.md - 删掉只为编号存在而写的两条注记(W2/W3 槽位保留、W3 不重编号) - 并行分道方案的分支 ID 同步:task/W8-scale-doc → task/cal-scale-doc 等 - 编号登记表:WAVE 族标为废止并关闭悬空检查(属主文档已不再定义 W 号), C-WAVE / D-WAVE 两族 title 补废止说明 - 同步 15 处跨文档引用(README 文件夹地图、产品需求 §9.2、编号规范 §4.1、 报告 §0.3 波次表、RAM 台账、保护规划变更记录等) - 不动的同形异义:网表 W4.0 封装宽度、芯片手册 W1 载带尺寸、MSVC /W4 |
||
|
|
42886581ef |
docs(mem): 回填内存台账到合入后的 .map(cov2 批次收口)
合入 task/cov2-p2-coverage 后在主干重新 @build,按受管产物口径回填: - scripts/ram_baseline.py --update + --write(tsv 与台账受管区) - scripts/check_mem_numbers.py --write(§2.1 内存快照) - 现状句数字改为现值;散文里复述历史快照数字的两处(旧 RW_IRAM1 占比)改为指向 docs/内存管理/data/ram_report_20261022.md,不再在 正文重复历史值——陈旧数字扫描按「上一个快照的区域值」判定, 复述历史数会被正当拦下,指向快照才是正解 合入后实测(.map 权威):Code=213,780 / RO=18,860 / RW=1,204 / ZI=131,852 B;RW_IRAM1 75,632 B(57.7%)、RW_CCM 57,376 B(87.9%)、 Flash 233,844 B(22.3%)。相对合入前:ZI +352 B、Code +60 B、RW 不变, 来自 syslog_post 投递即拷贝(条目内嵌 desc[29]),详见 docs/子系统设计/运行与自检记录设计说明.md §4.4。 验证:@build 0 错误 0 警告;pre-commit run --all-files 全绿(39 项 通过,含 mem-numbers / mem-budget / test-coverage / id-check / doc-links --sections-strict)。合并本身无「双方都改过」的文件, 无并集残留风险。 |
||
|
|
4a09d4c63f |
fix(log): syslog_post 投递即拷贝(台架实证驱动的 B 方案整改)
待发条目 syslog_entry_t 原为 {id, const char *desc},只存调用方指针,
隐含「desc 必须活到落盘」的契约,但契约既未写进 sys_log.h、也无机械
检查。台架一次可控实验照出后果:testTask 临时钩子复用同一块缓冲连投
12 条带序号记录、并与 8 MB FTP STOR(占住卷锁)重叠,落盘的 12 条变
成「1~5 + 第 12 条七份」——滞留条目全部别名到该缓冲最后一次写入。
叠加卷忙整批回投(条目可滞留到卷空闲),投栈缓冲即成悬垂读。
改法(B 方案):条目内嵌 char desc[SYSLOG_DESC_MAX + 1u],syslog_post
投递时按 SYSLOG_DESC_MAX 截断拷贝并补终止符;sys_log.h 参数说明同步
写明「投递即拷贝、不要求生命周期覆盖到落盘」。
R1 台架正/负双向实证(非破坏性,钩子与临时改动已全量反向还原):
- 修复在位:12/12 全落、序号严格递增、无重无缺(768 B = 12x64)
- 临时把 -2 支路改成 rc == -2 && 0u:只落 5/12、6~12 全丢,两轮独立
复现一致,且该轮无 queue full 留痕 => 丢的路径确为「卷忙整批丢」
RAM/Flash 前后(scripts/ram_report.py 同口径,同一 .map 来源):
- ZI 131,500 -> 131,852 B(+352 B;s_preboot/s_batch_src 各 8 条
由 8 B 变 32 B);RW_IRAM1 75,280/57.4% -> 75,632/57.7%
- Code 213,720 -> 213,780 B(+60 B);RW 1,204 B 不变
- RW_CCM 静态不变(57,376 B / 87.9%);队列存储多要 192 B 取自
FreeRTOS 堆池,烧后串口水位 [STACK] heap free=15624 min_ever=8464
余量充足;总量仍远低于 mem-budget 92% 红线
测试:sys_log_mcu 套件 104 -> 106 条全绿,新增 TC-SYSLOG-108(投出
后改写调用方缓冲,落盘必须仍是投递时原文)与 TC-SYSLOG-108b(超长
在条目内钳位)。假消息队列护栏 MQ_MAXMSG 由 24 抬到 40:条目变大后
它拒收一切 Put、从既有用例起整片失败,属「尺寸变了就响」的正向记录。
主机侧此前照不出该类问题,是因为用例传字面量、静态存储期无限。
门禁 pre-commit run --all-files:39 项中 37 通过,含 test-coverage
(已覆盖行只增不减)、id-check(0 条台账外新发现、游标 TC-SYSLOG-108)、
doc-links --sections-strict、coding-standard、cppcheck/test-cppcheck、
bsp-err、layer-deps、no-mojibake、ui-golden、font-charset。仅
mem-numbers 与 mem-budget 失败:docs/内存管理/ 受管台账仍记改前
131,500/213,720。按分支约定不在分支上 --write 受管台账,留待合回
主干时统一回填,故 --no-verify 提交。
真机:产品构建 0 错 0 警告,重烧 + 回读校验 OK(suite=NONE dstr=off)、
启动标记齐全、无 [ERR]、VERDICT PASS。
|
||
|
|
5a806d8a26 |
fix(log,net): 处置主机单测照出的 10 条现状,撤销 1 条误报
用户裁定「全部修」。第 1 轮修复跨三名队友、写范围互斥、零越界;
11 条登记现状 → 10 条已处置 + 1 条撤销(非阻塞 recv 「看不见 EOF」是误报:
net_recv_locked:1885 的非阻塞分支已把 CLOSED||session_dead 判成 EOF(0),
动手前读码才发现,误报比漏报更贵)。
存储与日志(App/log/sys_log_mcu.c、soe_store.c、cfg_file_store_mcu.c、sys_log.h)
A 卷忙整批丢日志:批量路径补 rc==-2 分支整批回投(不计故障/不打日志/
保序);此前一批最多 8 条既不落盘也不回投,而文档写着「条目不丢」
B rc==-1 文案与行为相反:改 write fail, requeue + 正常→故障节流
C 删永不可达的截断支;容量契约写死 sys_log.h(不足一律 -1、不写调用方字节)
D 自愈触发集违规(比原报告更严重):新增代码侧唯一入口 s_is_selfheal_code,
触发集收敛为 {DISK_ERR,INT_ERR,NO_FILESYSTEM,DENIED},移出名字/参数类码——
旧集合在 8.3 同名同因下每轮删一次自己的历史日志且永不自愈,正是存储架构
§10.1 第 10 条严禁的归集;soe_store 侧同源取更保守子集(打开失败绝不 unlink)
E 注释里的 FR_NO_SYSTEM 不存在,改 FR_NOT_READY / FR_NO_FILESYSTEM
F syslog_post 满态留痕节流(A 修好后「队列常驻满」成必然场景,不修等于用
新刷屏换掉静默丢);丢弃语义一字未改
NET(net_socket.c/.h、net_types.h、net_socket_sim.c、lftpd.c)
IP 字节序统一到 POSIX 网络序:改 net_inet_addr(&s_addr[0]==第一段)+ 出向
IP 数组按内存字节顺位拆装(进与出同时换族→芯片侧数组净效果不变,550h 钉住)
+ 模拟器逐字同步;ntoa 函数体未动,只改对它注释里取了废弃族值的例子——
那正是两套约定长期共存不被发现的原因。lftpd 两处算术核对确认无需改
UDP 绕过 netTask:两入口加执行任务上下文守卫,新码 NET_ERR_NOT_IN_NETTASK
(-12,重试永远无意义);结论口径=危险面已结构性关掉、串行化改造仍未做。
附带关掉第二条更隐蔽的路:非 netTask 自旋 recvfrom 期间控制块被 close 并
把同一 sockfd 分给新会话→旧自旋者消费新会话数据报
控制台(console_log.c/.h):冷却改独立 s_con_cooling 标志(旧写法在 tick==0 那
拍冷却等于没记);con_append 的判空位+推进 head+整行拷贝收进同一临界区(旧
为区外 check-then-act,环 >95% 满时可静默覆盖且既不打 [ERR] 也不计 dropped);
Instance==NULL 与僵尸通道分诊(未初始化绝不 reinit);三本账分离 + 新读口
dbg_console_tx_timeout();[ERR] 行改为发成功才销账
测试:net_socket 452、console_log 127、sys_log_mcu 104、soe_store 27、
cfg_file_store_mcu 34;回归 net_listener 34 / net_events 34 / net_pending 43 /
net_select 22 / lftpd 35 / lftpd_inet 28 / lftpd_io 33 / iec104_serv 28 /
nand_port 149 / vol_lock 33 / soe 32 / cfg_file 151 全绿。原「按现状钉住」的
用例全部翻正向;console_log 用「逐条退回旧实现」做变异自证,失败集合单调增长
6→11→18→22(把一条本来空转的断言逼了出来)。
游标抬升:TC-NETSOCK-545→560、TC-SYSLOG-100→107、TC-CONLOG-135→167。
文档:控制台设计说明 §4.1 口径与 §13-O2 销项 + v1.5;运行与自检记录 §4.4/§4.5
四条;存储架构 §10.1 第 10 条推论(指向 s_is_selfheal_code);TRAP-NET-21 后续
条目;BSD 指南新增 §9.0 s_addr 唯一约定;覆盖率口径 §9「第 1 轮修复处置」表 +
后续项 5 条 + 台架复验 7 条 + 新坑 8 条 + 变更记录。
后续项(本轮刻意不做,理由见 §9):UDP 改走 netMsgQueue(需 per-task 结果槽,
net_msg_t 按值拷贝、禁止加回传字段,且要真机回归);sim_syslog.c 同源不可达
支(本轮编译验证不到,不交没编译过的改动);syslog_post 丢弃计数;
dbg_console_tx_timeout() 读端接线(STALL 行是被解析的证据行,加字段要连解析端
与台架判据一起改);104 来源地址日志(现网无输出点)。
gate: @build 0 errors 0 warnings(Code=213720 RO=18860 RW=1204 ZI=131500)。
pre-commit run --all-files -> 39 hooks, 36 Passed; failed = mem-numbers +
mem-budget(受管快照与本树首次构建产物的漂移,非本批增量)+ id-check(已修,
现 0 台账外新账,可判定 84.63% > 基线 82.6%)。实测水位 RW_IRAM1 57.4%、
RW_CCM 87.9%,均低于 92% 红线;RAM/Flash 本批增量为数个 static 变量。
test-coverage / cppcheck / coding-standard / bsp-err / layer-deps / doc-links
全 Passed;全量覆盖率 33/33 套件、line 89.3%、func 92.3%、逐文件下降 0。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
|
||
|
|
794906cdc8 |
test(host): P2 覆盖率批次,5 套主机单测与共享假 FatFs
P2 五个文件全部进分母(三名队友并行,写范围互斥、零越界):
net_socket 420 条 / 881-883=99.8%、52-52 函数(S1~S6 分片)
sys_log_mcu 75 条 / 310-313=99.0%、12-12
console_log 104 条 / 188-189=99.5%、15-15
cfg_file_store_mcu 34 条 / 97-97=100%、10-10
soe_store 26 条 / 78-79=98.7%、4-4
合计 659 条断言,P2 本体 1554-1561=99.6%、93-93 函数。
账:套件 28→33 全绿;files_tested 32→37;未触及 112→107(.c 34→29,
恒等式 29 = 0(P1剩) + 0(P2) + 29(P3) 咬合,§9 终点判据达成);分母
4684→6245 插桩行;line 85.8%→89.3%;func 90.1%→92.2%(这批 5 个文件
全都以 ≥98.7% 进分母,故未摊薄)。--update-baseline 前后逐文件比对:
下降 0 个文件。
基建:新建 test/host/common/fatfs_fake.{h,c}(以真实 ff.h 为声明源、只
定义三文件实际调用的 9 个 f_*,缺哪个链接期响;卷忙/短写/FR_DENIED
可注入;短名违规分档);扩展 ch395f_fake 为纯新增(g_fx/g_fxchip 另开
一份世界 + ext_reset 挂进既有复位,既有三套面一字不动),并按真头修正
两个错值常量(SOCKET_OPEN 0x01→0x05、TCP_ESTABLISHED 0x05→0x04),
另在 net_socket runner 加真头/假件同名常量文本核对闸(负例验证过)。
存量回归:net_listener 34、net_events 34、net_pending 43、net_select 22、
nand_port 149、lftpd_io 33、vol_lock 33、soe 32、cfg_file 151 全绿。
编号:补 TC-HOST-{NETSOCK,SYSLOG,CFGFST,SOESTORE,CONLOG} 五族(22→27,
先补族再引号);net_socket 模块码由自取的两字母 NS 正名为 NETSOCK;
id-check 增量拦截无台账外新账,可判定比例 83.27%(基线 82.6%)。
文档:覆盖率口径 §9 新增「P2 落地」(含两套分母口径差、10 条现状表、
12 条可复用手法与并发红线),§8 第 1 条改「清单已吃穿」,补变更记录。
本提交未改任何生产代码(App/Drivers/Src/Lib/MDK-ARM 零触碰)。
10 条现状已逐条登记在 §9(含 sys_log_mcu.c:509~537 卷忙整批丢日志、
net_socket.c 同文件两套 IP 字节序、UDP 两 API 绕过 netTask),已获指示
全部修复,另开一轮(含测试字面量统一纯 ASCII)。
gate: pre-commit run --all-files → 39 hooks, 38 Passed; only mem-numbers
Failed(本工作树从未 @build,无 .map 事实源,属分支期已知陷阱);
mem-budget Passed;test-coverage 与 id-check Passed。RAM/Flash 增量 = 0
(纯测试批次)。按仓内分支约定走 --no-verify,台账回填留待主树合并后。
|
||
|
|
ec2dbc9e01 |
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(推送需用户明确指示)。 |
||
|
|
957ec64645 |
test(APP/104): iec104_serv 模块(28 条,84.2%)——P1 十三块全部进分母
P1 收官批次。新增 test/host/iec104_serv/,iec104_serv.c 覆盖 117/139 = 84.2%。 做法:把生产源整个 #include 进 TU,直接驱动它的 7 个 static 辅助函数——**不跑 iec104_serv_task 的无限主循环**(那是 never-returning 的任务体,不适合单测;事件组 相关支路用 sys_evtHandle=NULL 走"不可用"分支)。链路层与 pts 层给假件:链路协议本身 有 test/host/iec104 的 97 条专测,本模块只管服务怎么活、怎么死、怎么重生。 钉住的契约: - 建监听:2404 端口过 htons、绑 NET_INADDR_ANY;socket/bind/listen 任一失败都要把 已经拿到的号关掉(漏关就是每次重试漏一个 socket 号,CH395F 只有 8 个)。 - **网络代次一变就拆掉旧连接与旧监听并重建**:现场改 IP 后端口必须在新地址上继续 服务,这条不重建就是"设备换了 IP 之后 104 主站再也连不上"。 - accept 门控:监听未就绪不 accept;表项已失效时把监听号丢掉、下一轮重建。 - 单连接 socket 的断线语义:对端 FIN 时只关数据口,**监听必须留着**;数据号与监听 号同一个时绝不关(关了就端口失联)。recv 返 -1 且 errno 是 WOULDBLOCK 是正常空转, 不能当成错误丢连接;真错误则丢弃并**当场重建监听**,不留空窗。 - 判死用**连续**短写计数:一次成功即复位。写成累计就会把偶发拥塞误判成断线,主站 看到的就是莫名掉线(连到 3 次才判死,I104_SEND_FAIL_MAX)。 - 32 分频探活:无可读数据时不是每轮都去收(省 CPU 预算),到点才收一次;对端 FIN 后 复位计数器。协议状态掉回 IDLE 也要丢并重生(否则服务挂着但永不服务)。 - 时钟桥接两个溢出边界:epoch 秒超过 32 位一律拒绝且不碰时钟(截断写进 RTC 就是乱值); 未对时(sys_clock_get 返回 UINT32_MAX)回 UINT64_MAX 哨兵而不是 0(否则对端会以为 设备时间是 1970-01-01)。 主机侧兼容处理(写进桩注释):真实 App/util/sys_clock.h 会 include sd2506.h(RTC 芯片 驱动)一路拉到 HAL,PC 上编不动,故桩掉 sys_clock.h 但**两个签名与真实头逐字一致**; 104 的头散在 App 与 Drivers/BSP 的多个子目录(iec104_serv.h/iec104_link.h/iec104_pts.h/ task_watchdog.h/sys_events.h/prot_set.h/sd2506.h…),逐个手写 include 目录必漏且报错会 落在被测源上、极易误读成生产代码坏了——运行器改为**按目录实际是否含 .h 自动收集**。 自查:初跑红 2 条,都是我的场景问题(分频到点那轮 recv 返回 0 会走对端 FIN 支路, 应给 -1 + WOULDBLOCK;"重建失败丢监听号"必须同时让表项失效),改正后 28/28。 P1 收官合计:12 个新模块、**308 条断言**;套件 16 → **28** 全绿;files_tested 19 → **32**; 未触及 .c 47 → **34**;恒等式 34 = 0(P1 剩) + 5(P2) + 29(P3);分母 2,893 → **4,684** 行; line=4020/4684(85.8%)、func=300/333(90.1%)。百分比"降"是摊薄不是回退(lftpd.c 64.3%、 lftpd_io.c 53.5% 这类只测主体的文件也进了分母),按 §5 口径该盯 files_tested/files_untested。 亮点:lftpd_inet.c **127/127 = 100%**、net_listener.c 99.2%、net_events.c 97.8%。 编号补建 TC-HOST-I104SRV(游标 041),TC-HOST 族数 20 → 22。§9 的落地进度表补齐并 统一为 NET 拆出的四块 ①②③④。 自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 10 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。 --all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填) 与 ui-golden(ref/*.bin 不入库)三条失败,与本分支改动无关。 |
||
|
|
a0c261b5ee |
test(NET/lftpd): lftpd 控制通道模块(35 条),FTP 一套四块全部进分母
新增 test/host/lftpd/,lftpd.c 覆盖 304/473 = 64.3%。做法是把生产源整个 #include 进 TU,于是命令表 commands[]、17 个 static cmd_* 与 handle_control_channel 都能直接驱动: 假 inet 层给"脚本化命令行队列 + 响应记录",假 io 层给"极简内存视图 + 可注入失败", 认证与配置当可注入黑盒(认证本身有 test/host/ftp_auth 的 10 条专测,不重复验)。 钉住的契约(都是真机上贵、却难按需复现的那几类): - STOR 传到一半写失败必须 **unlink 半成品**:否则 Flash 上永久留个坏文件,现场表现 是"升级包传坏了再也传不上去"。真机要卡准"传一半掐线"的时机,这里注一个短写就行。 - 数据通道是单连接 socket:LIST/RETR/STOR 用完必须关监听并把 data_socket 复位成 -1; accept 失败要报 425 不留残口,会话退出(QUIT 或对端断线)时 cleanup 也要关掉还没 用的被动监听。漏一次就是下一条命令拿着上一个号继续 accept,偶发连不上。 - 命令名缓冲只有 command_tmp[4+1],前置判断 if (index >= 5) 就是防 memcpy 越界; 恰好 5 个字符必须被拒(改一行这条就响)。 - 认证前置白名单:未登录只放行 USER/PASS/QUIT/FEAT/SYST/NOOP,其余一律 530;口令错 必须清 auth_user_ok 强制重走 USER;被锁与口令错都只回 530,不给攻击者区分提示; 配置读不到时 USER 无法匹配、PASS 直接 530(fail-closed)。 - 写开关([ftp] write_en=0)必须在**碰文件之前**挡住 STOR/DELE,且不去 accept 数据 通道;DELE 不许删目录。 - 227/229 编码:IP 经 net_htonl 后按高字节到低字节排(就是点分序),端口高/低两字节; 拿不到本机 IP 时要把刚建的监听关掉。FEAT 多行:首行 211-、四条能力无状态码、末行 211 结束(共 6 条响应)。 - 220 欢迎没送达(对端已走)就直接收工,不空转读命令;该留痕按真实分级算 INFO 而不是 ERROR(真实 private/lftpd_log.h 把 lftpd_log_peer 归到 INFO:任何人连 21 端口就走 就能无限制造固件级 ERROR,还会撞掉 @test 的 Forbidden 标记 [ERR])。 - 传输循环每轮喂狗(lftpd_task_wd_beat 计数可断言)。 主机侧两个必要的兼容处理(都写在桩注释里,不是随手改): 1. lftpd.c 会 include ff.h,而这份 FatFs 在 Windows 支路上把 winsock.h 拉进来,它带 "#define s_addr S_un.S_addr" 的 BSD 兼容宏,会把真实 net_types.h 里的字段声明 uint32_t s_addr; 展开成 uint32_t S_un.S_addr; 直接炸(实测 C2143 在 "." 前缺 ";")。 桩里只 #undef 这个污染宏,让固件那份结构体定义原样参与编译,被测代码读的还是真字段。 2. 引号 include 先从 lftpd.c 自己目录找 private/lftpd_log.h,桩目录赢不过它——于是 顺着用真实那份(它只是把 lftpd_log_* 映射成 DBG_*),只桩最底下的 dbg_log.h。这样 真实日志分级也一起被编译验证,比我自己重写一套更对。 自查(35 条里前两轮红了 4 条,全是**我的期望/前置态写错**,实现均正确):未登录时 FOO 被白名单以 530 拦掉测不到 502 分派;227 的 IP 顺序我把 htonl 前后搞反;FEAT 是 6 条响应我按 5 条索引;PASV 之后走完整会话会被 cleanup 关掉监听(那正是 006 要验的 行为),观察 PASV 留下的状态必须直调 cmd_pasv。 覆盖率与台账:套件 26 → 27 全绿;files_tested 30 → 31;未触及 .c 36 → 35;分母 4,072 → 4,545 行。**总体 line_rate 88.4% → 85.9% 是摊薄不是回退**(64.3% 的文件进 了分母),按 §5 口径要盯 files_tested/files_untested——它从 19 一路涨到 31。 恒等式 35 = 1(P1 剩 iec104_serv.c) + 5(P2) + 29(P3)。编号补建 TC-HOST-LFTPD (游标 034),族数 20 → 21。 自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 9 个文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)与 ui-golden (ref/*.bin 不入库)三条失败,与本分支改动无关。 |
||
|
|
8aaf9bbb0e |
test(NET/lftpd): lftpd_inet 模块(28 条,127/127 = 100%)
FTP 的 socket 适配层吃满——全仓第一个 **100% 行覆盖**的文件(127/127)。 新增 test/host/lftpd_inet/,28 条断言全部 MSVC 编译通过并 PASS。 钉住的契约: - 端口字节序:bind 前必须过 net_htons(sin_port 的注释就写着"网络字节序"),问名字 回来要 ntohs 还原;问不到名字报 -1,不拿脏端口当结果。 - 失败路径一律关 socket:建口失败不 bind;bind 失败关口不 listen;listen 失败关口 并各留一条 ERROR(漏关就是白占一个号,CH395F 只有 8 个)。 - accept 每轮失败都让出 CPU(同一段里也喂狗),直到成功;**网络代次一变立刻撤掉 accept**——否则改了 IP 之后监听还挂在旧代次上,端口在新地址上根本不通。这是现场 最常见的自愈路径,台架上要凑"对面正连着我顺手改 IP"极难。 - 带超时的 accept:监听门撤防时**立即放弃**,不对着死监听耗满窗口;对端一直不开 数据通道就按 100ms 步进等满再 INFO 收场。 - read_line:CRLF 就地截成结束符(FTP 命令行的边界);跨包分片要能拼出整行;读到 0 (对端关闭)立刻失败;-2 表示这一片超时继续等;其它负值**原样返回**(错误码不许被 吞成 -1);等待按 200ms 分片、30 s 总预算到点退出(分片写错就饿死看门狗);缓冲 填满仍无行尾要失败且**不越界写**(最后一格留给结束符)。 - 短写续传:net_send 可以只写一部分,续传必须从正确偏移接着写(少半条响应就是协议 错乱);写 0 字节按失败处理;二进制写成功时回请求长度。 桩的口径:private/lftpd_inet.h 与 net_types.h 用真实头;net_socket.h 是桩但函数原型 与真实头逐字一致,并**照做真实头对 FreeRTOS.h / task.h 的 include**(lftpd_inet.c 的 TickType_t / vTaskDelay / pdMS_TO_TICKS 就是从那条链上拿的,桩少这一行就是 C2065)。 lftpd_task_wd_beat 是 __weak 空实现、主机侧替换不掉,因此用同段代码里的"睡 100ms" 次数当喂狗轮数的代理(写进桩注释,不假装能直接数到喂狗)。accept 循环带 100 轮跑飞 保险(net_select 批次被挂死教训换来的)。 覆盖率与台账:套件 25 → 26 全绿;files_tested 29 → 30;未触及 .c 37 → 36;分母 3,945 → 4,072 行;line=3599/4072(**88.4%**,连涨三批)、func=272/299(91.0%)。 恒等式 36 = 2(P1 剩:lftpd.c 与 iec104_serv.c) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-LFTINET(游标 070),族数 19 → 20。 自查:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 9 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。 --all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填) 与 ui-golden(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。 |
||
|
|
59e6d77224 |
test(NET): net_events 模块(34 条,覆盖 97.8%),NET 三块收官
假 CH395F 的第三批、也是 NET 域最后一块。net_events.c 覆盖 175/179 = 97.8%。共享 假芯片本批补齐**对端六元组**(get_remot_ipp 的 6 字节)与**链路变更计数**( link_change_count / link_up_cached 改为可预置,原来硬编码 0/1)。 钉住的契约(都是真机上会发生、却不能按需复现的那类): - 对端六元组解析:remote_ip 是**低字节在前**拼的(192.168.1.50 -> 0x3201A8C0,与 网络序相反)。钉住它是防止有人"顺手改成大端",让所有按 IP 比对/白名单静默错位。 - 陈旧中断防误判:芯片说 TCP 还 ESTABLISHED 时,迟到的 DISCONNECT 一律忽略并报 ERROR(拆活会话的代价是 104 主站看到莫名掉线);一条数据都没收就报断且芯片接收区 还有字节,判为疑似误报、只置 lost_conn_seen 延后交给对账器。 - 正常断开:优雅级沉降 + session_dead + last_peer_port 记旧端口 + 转 CLOSED + 发事件。 - 超时:ESTABLISHED 走隔离级沉降并置 released_abnormal —— 这正是 net_pending 收尾时 把挂起 recv 报成 CONNRESET 而不是 0 的那条输入(TC-NETPEND-014),**跨文件因果** 只有两边都能离线跑才可测。 - 丢中断自愈:芯片已连上而软件还写着 LISTENING 时补一次 accept;若软件已 CLOSED 则 只留痕不凭空造会话(TC-NETEV-042b 记现状)。 - 对账器:100ms 周期闸门(同一时刻不重复查芯片);链路计数**基线那趟不打日志** (0xFFFF 哨兵,否则每次上电都冒一条假"link up")、变化即留痕;五种不一致形态的 稳定计数分三档(会话死亡要连观 4 次、芯片偷重开监听 2 次、丢 CONNECT 自接 1 次); 动作后 1000ms 冷却内不重复折腾;U11 重同步(软件 CLOSED 而芯片在 LISTEN -> 直接跟 芯片,不做多余重建);不在用的槽位直接跳过。 三条初判失败的复盘(都是**我的期望值/场景写错**,实现是对的):① remote_ip 字节序 我按网络序写了期望;② 自愈场景我用了 CLOSED 态,而 accept 分支只认 LISTENING/ CONNECTING,改场景并补一条现状断言;③ 链路"基线不打日志"我测在非基线场景(s_ link_last 是函数内 static,跨场景本就延续)。对账器的 s_last/s_bad[]/s_cool[] 都是 函数内 static(外部够不着),因此每个形态**各占一个 fd**、时基单调前进,互不串扰。 覆盖率与台账:套件 24 → 25 全绿;files_tested 28 → 29;未触及 .c 38 → 37;分母 3,766 → 3,945 行;line=3472/3945(**88.0%**,连涨两批)、func=263/290(90.7%)。 恒等式 37 = 3(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-NETEV(游标 058), 族数 18 → 19。 自查:test-cppcheck Passed(含补齐后的假芯片);check_ids --strict 通过;doc-links 35 处(= 存量);16 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后 统一 --write 回填)与 ui-golden(simulator/tests/ref/*.bin 不入库,新 worktree 没有 黄金参考)三条失败,与本分支改动无关;提交按并行分支约定走 --no-verify。 |
||
|
|
b4441f0e1e |
test(NET): net_pending 模块(43 条,覆盖 87.1%),钉住通知值回传契约
假 CH395F 的第二批消费者。新增 test/host/net_pending/,net_pending.c 覆盖 169/194 = 87.1%。共享假芯片补了**缓冲尺寸宏链的同构转抄**(RAM_BLOCK 512 / SEND_BLOCKS 2 → SEND_BUF 1024;RECV_BLOCKS 4 → RECV_BUF 2048 → SPI_DMA_BUF 2052 → SPI_DMA_MAX_PAYLOAD 2048),于是 net_pending.h 里的 NET_SEND_CHUNK_MAX 是**真值 1024**,分块断言测的是芯片上界而不是假件自己发明的数字。 本模块的核心契约是跨上下文回传:AGENTS 模块约定写着 net_* 的错误码经**任务通知值** 回传(net_msg_t 按值拷入队列,netTask 改的是自己那份副本,调用方永远看不到;也勿给 net_msg_t 加回传字段,否则调用方读到栈垃圾)。台架读不到通知值,所以把 xTaskNotify 做成可观测假件(记目标句柄与通知值)之后,这条契约才第一次被真正验证。 钉住的契约: - 注册闸门:越界 fd 报 BADF;同 fd 已有挂起报 BUSY;caller 为空报 BUSY;**属主任务 自己发起被拒**(自己等自己等于死锁 netTask);to_ms=0 记为无限等(不置 deadline)。 - 到达与截断:读量钳在调用方缓冲内(绝不越界写别人的 buf);readable 但芯片没数据时 只清标志继续等,不误交 0 字节。 - 收尾四条分支:还有残留先把最后一包读走再收尾(**不丢尾包**);干净结束报 0 并按 auto_relisten 请求重开监听(不自动重开的不被偷偷要求重建);released_abnormal 报 CONNRESET 而不是 0(调用方要能分辨好坏收尾);session_dead 与 CLOSED 同路。 - settle_close:没挂起时直接返回不打扰芯片;有残留交还字节数;无残留报 CONNRESET。 - 挂起发送:参数校验(buf 空 / len<=0 / sent<0 / sent>=len 报 INVAL);单次写入钳在 NET_SEND_CHUNK_MAX;**deadline 每写一轮就续期**(还在推进就不该被 1000ms 掐死, FTP 上传大文件最容易撞这条);停滞不前进才报 TIMEDOUT;槽位消失或会话塌掉都**交还 已发字节数**(不是错误码,调用方据此算进度);cancel 留痕且幂等;**clear 不发通知** (与 recv 侧同一条纪律,不给调用方假答复)。 自查:42/43 首跑,唯一红的是**我的测试构造缺陷**——无限等那条没先把槽位置成在用, 被 in_use==0 兜底分支提前收口,测不到时间轴;补 mk_sock 后 43/43。reset_all 从上一 批的教训直接带过来(连被测模块的 static 挂起表与属主句柄一起复位),这次没再红。 覆盖率与台账:套件 23 → 24 全绿;files_tested 27 → 28;未触及 .c 39 → 38;分母 3,572 → 3,766 行;line=3297/3766(87.5%)、func=255/282(90.4%)。恒等式 38 = 4(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-NETPEND(游标 040), 族数 17 → 18;注册表仍只做增量插入。 自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 12 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。 --all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填) 与 ui-golden(ref/*.bin 不入库)三条失败,与本分支改动无关。 |
||
|
|
0a3b932395 |
test(NET): 假 CH395F 芯片落地 + net_listener 模块(34 条,覆盖 99.2%)
按 §9 规格做假芯片的第一批消费者。新增 test/host/common/ch395f_fake.{h,c}:15 个
函数的**真实原型逐字照抄**(含 ch395f_sock_state_t{open,tcp_state} 与 ERR_SUCCESS/
PROTO_TYPE_TCP/SOCKET_CLOSED/TCP_CLOSED/TCP_LISTEN 的真实取值),脚本化模型沿用本仓
既有假件风格:读函数按调用序号取返回队列(耗尽回落最后一个值)、写函数记次数与
最近入参、外加一条**调用顺序日志**让时序契约可断言。两条芯片怪癖必须复刻:
① get_sock_int_status **整体读即清**、无按位清除手段(AGENTS 模块约定 B3 实证坑,
不复刻就照不出"顺手全量清一遍把别的在用 Socket 门焊死");② *_cached 是**缓存值**
而非现场值,"监听还活着"的判据是 (open != CLOSED && tcp_state == LISTEN)。
各模块 stub/ch395f.h **只做转发** include 共享件(规格里拍板的方案):三份假芯片各自
漂移会让"假件与被测代码同时错"再也报不出来,签名与常量只能有一份定义。
新增 test/host/net_listener/,34 条断言,net_listener.c 覆盖 125/126 = **99.2%**。
钉住的契约(真机上一闪而过、台架凑不齐的组合):
- 四道互相牵制的时间闸门:30ms 退避(20ms 不重试、30ms 重试)、隔离级 400ms 与
优雅级 100ms(399ms/99ms 不放行、400ms/100ms 放行)、**800ms 上限只留痕且不放弃
请求**(留痕只打一次,到 2000ms 预算耗尽才报 ERROR 交对账器)。排期登记里那句
"沉降窗 800ms 上限只留痕"至此第一次有了可执行的验证。
- 该不该重建:芯片仍报 LISTEN 就不重建且清空请求;有挂起 recv/send 时让路但**保留
请求**(挂起结束自动补做);五种条件静默放弃(非 LISTENING/CLOSED 态、
auto_relisten=0、非 STREAM、local_port=0、in_use=0)。
- 失败不留半成品:open 失败关回去并回 BUSY;listen 失败也必须 close_socket。
- 重建后簿记:转 LISTENING、清 send_ready/recv_bytes/remote_ip(_arr)/remote_port、旧
remote_port 记进 last_peer_port、清 lost_conn_seen/released_abnormal;源端口与协议
取自表项本身(监听端口不能漂)。
- 芯片时序:清接收 → 关断 → 读即清 → 读状态(顺序不可换);清理接收区单次读钳到
256B、最多 16 轮、读到空即停。越界 fd 全程静默且不打扰芯片;请求去重保留首个 reason。
测试自查修了两处(都不是生产问题但值得记):
① reset_all 只复位假世界、**没复位被测模块自己的 static**(s_relisten_req/_next/_dead/
_quarantine/_settle_*/_capped),一上来红了 9 条;改用模块自带的 net_listener_clear()
清,顺带验证了 clear 的完整性。
② 沉降窗边界必须在**独立场景**测:放行判定与 30ms 退避共用 s_relisten_next,同一条
链里"恰好 400ms"那轮 tick 会被上一轮设的退避槽挡掉。这本身是个该知道的事实——
**hold 到点后最坏还要再等一个退避周期(实测 ≤30ms)才真正放行**,已写进用例注释。
另留痕一处死分支:net_relisten_locked 的 NET_ERR_BADF 从 tick **不可达**(tick 在调用
前就因表项为空放弃),TC-NETLIS-026 记着,新增调用点时要重新审视。
桩的口径:net_listener.h/net_types.h/net_config.h/net_pending.h 用**真实头**(net_sock_t
字段与各枚举一改即编译期报错);net_socket.h 是桩,但 net_set_errno 与 NET_ERR_RETURN_1
照抄真实头同名宏(net_socket.h:528/532);真实 net_pending.h 原型带 TaskHandle_t,故
宿主 FreeRTOS.h 补一个 void* typedef(只出现在签名里,被测代码从不传它)。
覆盖率与台账:套件 22 → 23 全绿;files_tested 26 → 27;未触及 .c 40 → 39;分母
3,446 → 3,572 行;line=3128/3572(87.6%,本批净涨因为新进来的文件几乎全绿)、
func=243/266(91.3%)。恒等式 39 = 5(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建
TC-HOST-NETLIS(游标 026),族数 16 → 17;注册表仍只做增量插入。
自查:test-cppcheck Passed(连假芯片一起过);check_ids --strict 通过;doc-links 35 处
(= 存量);新增文件 BOM/CR/U+FFFD/行尾逐项净。**本增量零生产代码改动**,未跑 @build。
--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)
与 ui-golden(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。
|
||
|
|
5224f21622 |
fix(NET,lftpd): 处置覆盖率照出的三处死路径(改名 + 修 timeout=0 + 激活异常集)
三处都由本轮 P1 新增的主机单测照出,用户逐条裁定后实施。共同性质:都不是改动引入
的回归,而是**从没被执行到的分支**——生产路径上要么没有调用者、要么没走边界,所以
编译器(0 错 0 警)、cppcheck、真机日志对它都无话可说。
1) 缺陷一(选 a:改名,行为零变化)
lftpd_string_trim 的尾部循环起点落在 strlen(p) 那一格 NUL,isspace(0) 为假 →
循环体一次都不执行,"尾部空白从未被裁",而名字承诺两头都去。改名
lftpd_string_ltrim + 删掉那条死循环 + 在 .h/.c 写明"只去前导"。不去尾才是对的:
RFC 959 以命令终止符 CRLF 界定命令,参数(PASS 口令、含空格的文件名)的尾随
空格属于参数本体,去掉等于静默改数据;CRLF 已由 lftpd_inet_read_line 截断。
调用点 lftpd.c 同步改名。全库无遗留真引用(余 6 处旧名均为解释改名历史的散文)。
2) 缺陷二(按推荐修)
net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写着
if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → 超时永不生效,退化成死等;
POSIX 语义是"只查一轮立即返回"。去掉 timeout_ms > 0 这个错误前置即可。就绪
检查在本判断之前,故有就绪时仍立刻带计数返回;同文件 net_poll_events 的
timeout >= 0 本来就对,现在两条对齐。风险面:拿它当"非阻塞查一遍"会让 netTask
回不到主循环,而 netTask 在 IWDG required_mask 里(串行 socket 栈唯一执行者)。
3) 缺陷三(选 ①:按 POSIX 置位)
exceptfds 是恒为空的死参数:循环开头 p_sock == NULL → continue 先跳过,而
check_socket_error() 只有"表项为空 / fd 越界"才返回 1,越界的 i 又不在本循环
范围内 → 调用方按 POSIX 直觉用它收异常,永远什么都收不到,且返回码不报错。
把异常判定提到 continue 之前:表项不存在的 fd 计入 exceptfds 与返回计数,并更新
函数注释。选 ① 会改变返回计数语义,故补一条"未请求异常集时不计数"的用例,
确保只查可读的老调用方不被误伤(TC-NETSEL-012b)。
测试同步翻转:原 TC-NETSEL-008 / TC-NETSEL-012 是"现状桩、改了会响",现在改成正向
契约,并新增 TC-NETSEL-012c(异常位只落到被请求的那个集合);TC-LFTSTR-005 从
"尾部不裁"的现状登记改为正向契约(尾随空格必须保留)。net_select 22/22、
lftpd_io 33/33 全绿。
@build:0 错误 0 警告。实测体积 ER_IROM1 232,512 B(快照 232,536 → **−24 B**)、
Flash 233,704 B(−20 B)、RW_IRAM1 75,272 B(+8 B 是上一提交 net_cfg 那个字节,
本批零新增)、**RW_CCM 57,376 B 不变**;四档百分比 22.2/57.4/87.9/22.3 全不变。
Code 减少 36 B 即删掉的死循环。
覆盖率台账:门禁如实报了一次 lftpd_string.c 已覆盖行 8 → 7(**代码少了一行**,不是
覆盖退化),已 --update-baseline 重登;现 22/22 套件、line=3003/3446(87.1%)、
func=236/259(91.1%)、tested_files=26、untested_files=118。
文档:主机单测覆盖率口径 §9 新增"三处死路径的处置"小节(裁定、改法、影响、钉住
它的用例一一对应),并把两处"等裁定"的登记改为已处置。
自查:coding-standard / test-cppcheck / id-check --strict(无台账外新账)/ doc-links
(35 处 = 存量)全过;八个改动文件与 HEAD 逐项比对 BOM/CR/U+FFFD/行尾无变化
(这几个文件本就不带 BOM,未被动过)。pre-commit --all-files 仍只有三条失败且与
本分支无关:mem-numbers / mem-budget(.map 与 docs/内存管理 受管快照漂移,增量见
上,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden
(simulator/tests/ref/*.bin 在 .gitignore 内,新建 worktree 天生没有黄金参考)。
|
||
|
|
658bc40ccc |
test(NET): net_select 模块(21 条)与 NET 域首份 ch395f 桩,登记两处死路径
P1 的 NET 四块开了第一块:新增 test/host/net_select/,21 条断言全部 MSVC 编译
通过并 PASS。net_select.c 一个芯片函数都不调,所以它是四块里唯一不需要假芯片的,
先把它拿下,顺带把 NET 域的桩骨架立起来。
钉住的契约:
- 就绪位回写:入参集合里未就绪的 fd 必须被清掉,只留就绪的(POSIX 语义);未请求
的集合不被写回、可以传 NULL。
- 状态准入:LISTENING 即使 readable 也不算可读(accept 语义不在这层);ESTABLISHED
+ send_ready 才可写;CONNECTING 时 send_ready 不作数。
- 边界:nfds 为 0 / 负 / 超过 NET_MAX_SOCKETS 都返回 -1(不许扫出 socket 表外)。
- 超时算术:tv_usec 是**整除截断**(1999us 当 1ms 而不是 2ms);timeout=NULL 时
无限等并由对端事件唤醒;poll 的 timeout=0 首轮即退、每轮先清 revents。
用可控 tick(每问一次 +1)+ 可控 osDelay(计数、可指定轮数后造一个就绪 socket)
把这些变成确定性轮数,台架上根本构造不出"没人来连时该怎么退"。
照出两处**死路径**(只登记,未改语义;全库唯一调用点 test/board/net_test_task.c
传的是 NULL 与非零 tv,今天没有受害者):
1. net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写的是
if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → **超时 0 永不触发退出**,退化成
无限等;POSIX 在这里必须立刻返回 0。同文件 net_poll_events 写的 timeout >= 0
是对的,两条并排放着正好是对照。风险面:谁拿它当"非阻塞查一遍",netTask 就
回不到主循环,而 netTask 在 IWDG required_mask 里。
2. exceptfds 是恒为空的死参数:循环体开头 p_sock == NULL → continue 先跳过,
check_socket_error 的 NULL 分支从 select 里到不了。按 POSIX 直觉用它收异常的人
会永远什么都收不到。
两处都用"现状桩"钉成改了会响(用例名里写明不是期望行为)。另修掉我自己写错的一条
用例:给 timeout=NULL 又没有就绪 fd,直接死循环——已在假 osDelay 里加了 500 轮
保险(跑飞就 exit(2)),因为挂死的测试会把 exe 文件名占住、让后续构建报 LNK1104。
桩的边界写死在文件头:net_select.h / net_types.h / net_config.h 用真实头(net_sock_t
字段名与 NET_SOCK_STATE_* 一改即编译期报错);main.h / cmsis_os.h / FreeRTOS.h /
task.h 是本目录桩;新建的 ch395f.h 是**空壳**,只服务不调芯片函数的文件——
net_pending / net_events / net_listener 直接调十余处 ch395f_*,要的是可脚本化返回的
假芯片,不要把这个空壳当成"NET 域桩已就绪"。
覆盖率:套件 21 → 22 全绿;files_tested 25 → 26;未触及 .c 41 → 40;分母 3,332 →
3,446 行;net_select.c 97/114(85.1%)。恒等式更新为 40 = 6(P1 剩) + 5(P2) + 29(P3)。
编号按 B 层口径补建 TC-HOST-NETSEL(游标 019),TC-HOST 族现共 16 族;注册表仍只
做增量插入。
自查:test-cppcheck Passed;check_ids --strict 通过;doc-links = 35 处(= 存量);
BOM/CR/U+FFFD/行尾逐项净。本增量零生产代码改动,未跑 @build。--all-files 仍只有
mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)与 ui-golden
(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。
|
||
|
|
6bdf063f50 |
test(NET/lftpd): P1 增补 lftpd_io 模块(33 条),登记 FTP 域一处潜伏缺陷
新增 test/host/lftpd_io/:一个 TU 同时纳入生产源 lftpd_io.c 与 lftpd_string.c (两者静态符号不冲突,省一套桩),33 条断言全部 MSVC 编译通过并 PASS。 钉住的契约(都是真机上偶发、日志看不出根因的那类): - 路径规范化:**".." 上跳过头仍被钳在根内**("/a" + "../../x" = "/x")——FTP 客户端 可发任意路径,这是安全属性;绝对名忽略 base、"." 丢弃、段间多余斜杠折叠、 NULL 入参容忍、全空结果归 "/"。 - 分配失败:这个函数返回 pvPortMalloc 出来的指针(历史上正是它落在 C 库 malloc 上, RAM 方案 §4.2.1)。用**可注入失败的假堆**跑两条分支:首块失败 → NULL;结果缓冲 失败 → NULL **且第一块已 vPortFree**(不泄漏堆)。模拟器宿主 CRT 永不失败,这条 只有假堆能构造。 - 卷锁时机:open 成功**故意不释放**(整个传输期独占卷,由 close 释放),失败必须 释放并留 ERROR;单文件槽守卫 s_file_open 命中时**不碰卷锁也不碰 FatFs**。 - 等卷锁循环每轮喂看门狗(lftpd_task_wd_beat):defaultTask 与 FTP 争卷时这里可能 空转上百毫秒,漏喂就是整机复位。脚本化 vol_lock_wait 的返回序列直接构造 2 轮空等。 - lftpd_string_trim:行首空白由**返回指针**跳过、原缓冲不搬移(调用点 lftpd.c 用的 正是返回值);全空白串返回空串。 **本批照出一处,未改语义、只登记**:lftpd_string_trim 的尾部循环写成 for (i = strlen(p); i >= 0 && isspace(p[i]); i--),i 起步那格是 NUL、isspace(0) 为假 → 循环一次都不执行,**尾部空白与 CRLF 从未被裁掉**。今天不发作是因为 CRLF 在 lftpd_inet_read_line 里已用 strstr 截断、且唯一调用点只关心行首;风险在于"以后有人 拿它去尾部空白"不会生效。修法只要一行(i 从 strlen(p)-1 起),但那会改变 FTP 参数 (含空格的文件名/口令)的可见行为,需要 FTP 域一起过,**留给用户裁定**;当前用 TC-LFTSTR-005 把现状钉成"改了会响"(明确标注是现状桩、不是期望行为)。 未覆盖也写在文件头:lftpd_io_read/write/close/stat/unlink/opendir/readdir/closedir 要先把假 FIL/FILINFO 的字段语义(fptr、obj.objsize、fname 等)做实,否则断言会落在 假件自己写的规则上;这 8 个 f_* 只给了可链接的 FR_INT_ERR 实现,走进即失败不静默。 覆盖率:套件 20 → 21 全绿;files_tested 23 → 25;未触及 .c 43 → 41;分母 3,163 → 3,332 行;lftpd_io.c 85/159(53.5%)、lftpd_string.c 8/10。总体 line_rate 88.9% → 87.2% —— **这是摊薄不是回退**(新进来的文件只测了一半,分母涨得比分子快),已按 §5 口径写进口径文档 §9 的落地进度表;恒等式更新为 41 = 7(P1 剩) + 5(P2) + 29(P3)。 编号:按 B 层口径补建 2 族 TC-HOST-LFTPIO(游标 037)与 TC-HOST-LFTSTR(005), TC-HOST 族现共 **15** 族;id_registry.json 仍只做增量插入(无重排)。 自查:test-cppcheck Passed;coding-standard Passed;check_ids --strict 通过(无台账外 新账);check_doc_links --all = 35 处(= 存量,本分支贡献 0);新增/改动文件 BOM/CR/ U+FFFD/行尾卫生逐项净。**本增量零生产代码改动**(只加测试、注册表与文档),故未跑 @build。pre-commit --all-files 仍只有三条失败且与本分支无关:mem-numbers / mem-budget (本 worktree 上次 @build 生成的 .map 与受管快照漂移,增量 ER_IROM1 +12B、RW_IRAM1 +8B、CCM 0,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden (simulator/tests/ref/*.bin 在 .gitignore 内,新 worktree 天生没有黄金参考)。 |
||
|
|
b1291f1308 |
test(NETCFG,CFG): P1①四个主机单测模块,并修 net_cfg 的 WAIT 契约与定长错记
按 docs/测试说明/主机单测覆盖率口径.md §9 的清单吃 P1 第①批「配置与工具」四块, 新增 4 个主机单测模块共 104 条断言,全部 MSVC 编译通过并 PASS: - test/host/net_cfg/(45 条):出厂默认可用、注册幂等与内容、load 报 -1 仍返回 0 (RAM 恒有效契约)、跨任务保存交接状态机 IDLE/WAIT/OK/FAIL、MAC 三态优先级、 IPv4 定长格式化。 - test/host/ftp_cfg/(16 条):[ftp] 段注册(**p_cb 必须为 NULL**,口令下次登录才 生效)、默认口令判定大小写不敏感、NULL 一律判不等(fail-closed)。 - test/host/prot_set_store/(16 条):blob 返回码分流 1 无档 / 0 成功 / **2 换代 (只打 INFO)** / -1 损坏;写盘失败必须回置脏标志(否则定值静默丢失)。 - test/host/task_prof/(27 条):无效样本闸门(防 DWT 复位把 max 抬到 2^32)、 未 enter 就 exit 不记账、首次 5s + 每 2s 节流、环形缓冲与裸发两通道、 周期→微秒换算随 SystemCoreClock 变。 覆盖率收益(已 --update-baseline 登记):套件 16 → 20 全绿;files_tested 19 → 23; 未触及 125 → 121 个文件(.c 47 → 43、9,657 → 9,228 行);分母 2,893 → 3,163 插桩 行;line=2811/3163(88.9%)、func=225/240(93.8%)。§9 的恒等式同步更新且仍咬合: 43 = 9(P1 剩) + 5(P2) + 29(P3)、9,228 = 2,188 + 2,226 + 4,814。 新单测照出并修掉两处真问题(都在 net_cfg,不是测试写错): 1. 实现违反自己的文档契约:net_cfg.h 明写"属主任务写盘中显示 WAIT",但 net_cfg_save_run 为了不丢新请求会先清 pending 再写盘 → 写盘那几百毫秒里 UI 保存行退回上一轮的 OK/IDLE(第一次保存按 ENTER 后屏幕上还是 ENTER),用户只 会再按一次。补 s_save_busy 标志,TC-NETCFG-028/026/030 钉死(含失败路径必须 清 busy,否则 UI 永久卡 WAIT)。 2. "定长 11 字符"是错的:%03u.%03u.%03u.%03u = 4 段×3 位 + 3 个点 = **15**。这个数 错了 4 处(net_cfg.h、net_cfg.c、网络配置说明.md 两处)——是我照抄它才撞上 FAIL,错在文档不是代码。已全部更正,并确认 len ≥ 16 的要求与 UI_BUF_STRLEN=32 本来就没问题(无截断风险)。 桩的口径(沿用既有模块,不另起一套):真实头优先——cfg_file.h / cfg_file_store.h / console_log.h / prot_set.h 全用真头(接口一漂移就编译期报错),只有 main.h 必须桩 (真实 main.h 拉整棵 HAL 头树,PC 上编不动;桩里字段名与位掩码宏名与 CMSIS 一致)。 prot_set_store 连真实 prot_set.c 一起编:blob 的编解码与脏标志在它里面,只有拿真 serialize 产出的 blob 才能造出"换代"与"损坏"两种真实形态,手搓等于测自己的桩。 配置引擎本身(解析/写盘/沉降)归 test/host/cfg_file 的 103 条,本批不重复测。 编号门禁:按 2026-10-21 批 3 的 B 层口径补建 4 族 TC-HOST-*(NETCFG 游标 056 / FTPCFG 014 / PROTSTOR 016 / TASKPROF 020,代码即属主登记处)。这一步不是走形式: 建族之前文档里引用 TC-NETCFG-028 被判「台账之外新账」直接卡提交,建族后才通过; 编号规范 §8.4 变更记录加一行说明族数 9 → 13。id_registry.json 只做增量插入(+88 行, 无重排)。 自查(提交时 pre-commit run --all-files,**仅 3 条失败且都与本分支改动无关**): - mem-numbers / mem-budget:本 worktree 因需验证代码改动而 @build 过,新生成的 .map 与 docs/内存管理 受管快照漂移。实测增量 ER_IROM1 +12 B、RW_IRAM1 +8 B、Flash +16 B、**CCM 0**,水位百分比全不变(改动 = 1 个静态字节 + 几条指令)。按并行分支 约定**不在分支里 --write**,台账由 Lead 合并后统一回填。 - ui-golden:simulator/tests/ref/*.bin 写在 .gitignore 里(黄金参考是本地生成物), 新建的 worktree 天生没有,报的是"缺黄金参考"而非像素差异;本分支零 UI 文件改动。 - 其余全部 Passed,含 @build 0 错误 0 警告、test-cppcheck(它抓到过我一条 p!=NULL 后又解引用的写法,已改为断言+显式守卫)、coding-standard、doc-links(35 处=存量)、 id-check --strict、mojibake 与 BOM/CR/U+FFFD/行尾逐项卫生。 - 自审修掉自己写的两条恒真断言(形如 x==0?1:1 的假绿)与一个重复编号标签。 - 4 个新模块**尚未挂 pre-commit 钩子**:.pre-commit-config.yaml 是单写文件,任务工作 树内不改;覆盖率脚本按 test/host/**/run_unit_test.py 自动发现,已实跑 20/20。 - 未跑 @test:改动仅 1 个静态字节 + UI 保存行的显示时序,无硬件语义变化。 |
||
|
|
6505cef9df |
feat(门禁): coding-standard 加 CODE-RULE-20 禁 C 库堆分配(销 §4.8 空白 ②)
规则:在**掩码后**的文本上匹配 malloc/calloc/realloc/strdup/strndup/ free/aligned_alloc 调用。三条判据都是踩过坑之后定的—— - 大小写敏感:pvPortMalloc / vPortFree(唯一允许的配对)不会误报; - 词边界 \b:dhara_nand_is_free(、buf_alloc( 这类同形后缀不误报; - 用掩码:注释与字符串字面量里写 malloc 不报(规范示例就踩过这种)。 覆盖名比 AGENTS 列的五枚多带上 strndup / aligned_alloc——同族同类, 漏一个就等于留后门。 出处不在规范 V1.0,在平台约束:MicroLIB 的 C 堆由 startup_stm32f407xx.s 的 Heap_Size EQU 0x200 只有 512 B,KB 级分配必返 NULL,而模拟器(宿主 CRT 堆)完全正常、**照不出来**——配置引擎 2026-09-18 那次堆分配纠偏与 lftpd 的 malloc 隐患都是**先上线才发现**的那类,靠评审等于没管。 落地前按 门禁说明/coding-standard.md §8 的纪律先全库普查:钩子范围内 C/H 共 151 个文件,命中**只有** simulator/sim/cfg_file_store_sim.c 两行, 即 cfg_store_alloc/cfg_store_free = **平台分配后端的宿主侧实现**(设计依据 写在 docs/子系统设计/配置文件设计说明.md 的"MCU 端 = pvPortMalloc、 模拟器端 = 宿主 CRT")。所以零基线、直接硬拦截;豁免用脚本里的 HEAP_ALLOC_ALLOWLIST(**文件路径 + 函数名**双条件),刻意不做目录级豁免: 下次谁要碰 C 堆就得往这张表加一行,评审看得见。 自证:临时样例文件里 malloc/calloc/free 各报一处,pvPortMalloc/vPortFree/ buf_alloc(、注释、字符串五种陷阱形态全部不误报; pre-commit run coding-standard --all-files → **Passed**(= 全库零存量)。 顺带修三处口径不一致: - 规范 V1.0 §6.1 的示例自己写着 malloc/free(通用 C 模板遗留)→ 已在示例 下加"本仓例外,别照抄"批注,事实源指向 AGENTS.md 平台约束; - docs/门禁说明/mem-budget.md 里"这条纪律只靠 AGENTS 铁律与评审"已过期 → 更正为由 CODE-RULE-20 机器拦截; - AGENTS.md 平台约束条加"已机器拦截 + 唯一豁免"一句;scripts/README.md 补登 check_test_coverage.py(上一个提交漏登记)。 另:本次一并修好上一处编辑事故——报告 §4.8 曾被我的脚本调用截成半句 (只落第一行、丢掉 malloc 那半句),已重写为"两处空白均已闭合"。 自查:pre-commit run --files(9 个改动文件)适用项全 Passed; check_doc_links.py --all = 35 处,与主干同数(本分支贡献 0); check_ids.py --strict 通过(裸编号 P0-1 改平实说法,避免记成新账); 9 个文件 BOM/CR/U+FFFD/行尾与 HEAD 逐项一致。本轮零 .c/.h 改动,未跑 @build、未占 build/bench 锁;分支已 rebase 到 v2-on-ui,覆盖率脚本全量 回归仍 16/16、line=2552/2893 不变。 |
||
|
|
2bfa09ed15 |
feat(门禁): 主机单测覆盖率脚本与口径落地(销报告 §4.8 空白 ①)
落地物: - scripts/check_test_coverage.py:发现规则与 L0.5 同一条(test/host/**/ run_unit_test.py),直接 import 各运行器复用其 TestBuild 与 unit_test_harness._BASE_FLAGS —— 配置零复制,不会漂出第二套口径; 只算自研生产源码(App/**、Drivers/BSP/**)的行/函数、多套取并集、 生成物(文件头写"自动生成/请勿手改")不进分母、分母一律来自插桩 报告不猜行数;中间物全在 %TEMP%,不新增 .gitignore 规则。 - scripts/test_coverage_baseline.json:增量拦截台账(只拦回退不拦进步, 与 layer-deps / bsp-err 同姿态);ci_local.py 加 opt-in --coverage。 - docs/测试说明/主机单测覆盖率口径.md:口径 + 工具链实测 + 九条盲区。 - 报告 §0.1 / §0.4 / §4.8 / §5.5 / §7 改写 + 变更记录 + 去处表。 工具链是实测出来的(换机必读):Windows 原生侧无任何 gcc/gcov(全盘扫 Program Files*、msys64、scoop、choco、.platformio、LocalAppData = 0 处); MSVC 14.51 已砍 /coverage,且只报 D9002 后照常编译成功 = 静默假数据; CodeCoverage.exe 17.0 三个子命令只打印用法。最终用 mscoverage 18.x (Microsoft.CodeCoverage.Console.exe)collect -f cobertura 做加载期动态 插桩,并实测它透传子进程退出码 → "插桩运行下仍必须 PASS"可当硬条件。 首登数字:16/16 套插桩下全绿,并集 line=2552/2893(88.2%)、 func=193/207(93.2%)、19 个文件在测、**未触及 125 个生产源文件 / 约 11,839 行**——最后一栏才是这套口径真正照出来的东西。 门禁自证:人为抬高基线后 行/函数/总计 三条回退路径都报 REGRESS 且 退出码 1;--module 子集跑不再假报总计回退(首版会,已修:并集口径下 子集必然变小,全部回退判定只在全量跑时生效)。 GCC 追问的复测结论(口径文档 §2.1 决策记录):本机 WSL2 Ubuntu 确实有 gcc/gcov 15.2.0,16 套单测在 gcc 下全部编过、用例数与 MSVC 逐个一致, gcov -b -c 也确实给出分支覆盖(nand_ftl.c 分支至少走过一次 41.40%); 但换编译器=换分母(sha256.c gcc 105 行 vs MSVC 114 行;adc_input.c 101 vs 120)。**用户裁定:本仓只留 MSVC 一条覆盖率通道**,不建第二条—— 多一套工具链就多一套分母、多一本台账、多一个"这台机器到底有没有 WSL/ gcov"的未决项,换来的只有分支这一维。分支覆盖因此**记为已接受缺口** (不是待办):绿灯只证明行与函数被测到,补偿判据 = TC 双向追溯 + 用例显式列边界值;§8 写明重开该裁定的前提是先答"两份分母怎么对齐"。 刻意未挂 pre-commit:16 个单测钩子已经跑过同一批测试;依赖 VS 可选 组件(缺组件时 skip 会静默失效、FAIL 会拦换机);待一个真实回退事件。 YAML 片段与理由写在口径文档 §8。 自查:pre-commit run --files(6 个改动文件)全 Passed;check_doc_links --all 回到存量 35 处(本分支贡献 0);check_ids.py --strict PASS; check_mojibake + BOM/CR/U+FFFD/行尾卫生逐项自查全净。本轮零 .c/.h 改动 (纯 .py/.json/.md),故未跑 @build、未占 build/bench 锁。 |
||
|
|
6822726b2c |
docs(门禁说明): 手册 §2 收敛为通用机制与查号表,逐项详解归一到 23 篇
用户一句"现在门禁都在干什么,我一概不知"引出的收尾。原手册 §2 有 27 个逐钩子小节,只回答 "拦了什么、怎么配",回答不了"什么时候会骗我";docs/门禁说明/ 已把逐项详解(含"限制与盲区" 一节)建好,本笔做收敛:同一事实只留一处(docs/文档规范.md 的要求)。 **先搬后删**(否则收敛等于丢事实):旧 §2 里独有条目逐篇折回,共 18 处插入、15 篇受益。折回的 东西包括:六套主机单测的来历与用例规模(13/36/82/46/32/149 断言,含 nand-port 的"与旧算法 对照")与"改动公共骨架 unit_test_harness.py 会同时触发 16 个钩子";UI 页面**两层注册**(构建 注册 vs 框架注册:枚举 + s_defs[] 描述符表)与"豁免不整目录一刀切";lv-conf-sync 首次落地清掉 26 个 LVGL 9.3 不存在的选项、3 处重复定义、10 项隐式开关显式化,以及 ALLOW_DIFF 端专属白名单 与必须穿过 lv_conf_internal.h 里那层 KCONFIG 包裹;cubemx-survival 的 16 项清单、断言前先剥 C 注释(本仓注释刻意引用历史代码,不剥就恒定误报)、旧手册"自造标记区"建议与看门狗文档实测 教训之间的优先级;id-check 顺带收编 check_test_traceability.py、裸形同字母多义属**显式承认的 盲区**、--update-baseline 只许涨;layer-deps 的注入自证与报错时三条修法(注入回调 / 上层递数据 / 才谈 ALLOW);coding-standard **nand_ftl.* 也在排除清单**(我先前那篇漏了);test-suite-switch 的剥注释判定与派生宏白名单;mem-budget 两类软提醒不拦、退出码 2 是解析失败不是通过;doc-links 的排除目录与状态词 ±40 字符距离;task-table 改栈三步;gitlint 报错样例;no-mojibake 的修复路径。 **顺带抓到旧文档两处错**(记进文档,不悄悄改):一是 check-added-large-files 的阈值旧手册写 "单个文件 > 1MB",按本机安装的钩子源码实测是 --maxkb 默认 **500**(500 KB)——已更正,并把 "本目录任何默认值都以实测或安装的钩子源码为准"写进 end-of-file-fixer 当反面教材;二是 **ui-golden 在旧手册里连小节都没有**,只活在配置注释里:除了"有基线不等于有门禁",这是另一种 失踪——有钩子、没文档。 手册 §2 改写为三节:迁移说明 / **旧编号 → 新文档 查号表**(历史引用靠它读,旧号按"冻结历史不 回改"保留)/ **跨钩子通用机制**:两阶段与换机必须重装 hooks 才带 commit-msg、stash 陷阱、 always: true 已废、改写型与只读型豁免不对称、增量拦截三段式且刻意没有 --write、没有 .map 是 "跳过"不是"通过"、改配置的验收是看目标钩子出现在输出里且不是 Skipped、新增门禁三步必须做 注入自证且"限制与盲区"不许留空,另有快速自查入口。手册 707 行 → 273 行。 引用**按内容**重指(只核编号存在与否不够,同号小节会被当本文自引用静默放过):编号规范、 scripts/README(顺带更正那里过时的 warn-only 说法,早已关闸为增量拦截)、RAM 使用分析与优化 方案、测试开发标准流程、lvgl-simulator skill、AGENTS 文档表(拆成"每门禁一篇详解"与"通用 机制"两行)、.pre-commit-config.yaml 头注释、docs/README.md 描述、手册自身 §5.5 与 §6 的两处 内部引用。另:表格里的裸节号会被 doc-links 判成本文自引用,查号表统一写成不带 § 的形态。 写说明过程又被自家门禁拦一次:gitlint 篇里引用报错样例时写了两个规则号字面量,被判形态不合规 → 改成中文描述,没有往台账里塞 reason 解决。 验证:纯文档与注释改动,不动代码,Program Size 不变,故不跑 @build/@test; check_doc_links --all --sections-strict **171 篇 0 问题**;id-check 增量拦截通过; pre-commit run **RC=0 / 13 项 Passed**;28 个改动文件字节卫生 **0 异常**(无 BOM、无 CR、 无 U+FFFD、均 LF、无行尾空白)。 |
||
|
|
2575177e81 |
refactor(gate): bsp-err 四项检查改用自描述标签,废掉 R1~R4 这组撞车代号
纯改名,检查行为一个字没动:规则逻辑、阈值、豁免形状全部原样。 改名原因(用户一句「R4 到底是什么」问出来的):R1~R4 是我在 check_bsp_err.py 注释里 自造的序号,而这个裸形在本仓另有含义——R1~R8 已在 scripts/id_registry.json 登记为 "专项风险"的文档内编号(保护规划与看门狗的风险表都在用),带连字符的那一族又是审查 报告的可靠性 / 内存优化项编号(编号规范 §1 的"连字符承载语义"一行正是这么写的)。 **同一个裸形四种意思**。最刺人的一点:编号门禁**不会报**这种撞法,因为编号规范 §4.3 已把这些裸形声明为"同号不同义"、走 collides_with 白名单,校验 4 不计事故——同一天里 我起的另一组自造号被当场拦下、这组代号却一路绿灯,差别不在我做对了什么,而在白名单 挡的是"事故"、不是"可读性"。 新名同时是基线键的第二字段(格式 文件|标签|名字): · err-type 错误通道不外泄(对外接口不拿 HAL_StatusTypeDef / bool 当成败) · timeout-name 超时宏不取负值 · return-doc 公开函数必须写「返回值:」 · consume-result 返回值不得裸丢弃(须接住或显式 (void)) 老名对照:R1=err-type、R2=timeout-name、R3=return-doc、R4=consume-result。 同步面:脚本里的规则码与 38 条基线键的字段**一起换**(中途不跑 --prune,否则改名会被 读成"38 条全还清 + 38 条新账"),换完 --emit-baseline 复核仍是 38/38/0、RC=0。现状类 文本全部换新名:AGENTS.md、pre-commit使用说明.md §2.27(新增"检查项叫什么"一行含老新 对照与撞号说明)、bsp_err.h 边界纪律、报告 §0.4 / §9 / 附录 A,以及代码注释里的 5 处 (gd5f2gq5ue.c/.h、nand_ftl.h、ui_menu_tab.h)。已提交的变更记录行里的老名按"冻结历史 不回改"处理,以 §2.27 对照行为准。ui_menu_tab.h 带 BOM,Edit 又剥掉、已按原样补回。 写说明文字时又被编号门禁拦了一次:我为了举例"老名撞车",在那段话里写了两个编号字面, 被判"未紧邻文档名 / 未登记前缀"共 3 类新账——连批评自造编号的段落本身都在自造编号。 改法是不写号、只描述形态并指向权威条目。 验证:@build **0 错 0 警告**,Program Size 与上一笔**逐字段相同**(Code 213,676、RO 18,860、RW 1,188、ZI 131,500)→ 证明确为注释与文案,故不跑 @test(无指令变化); pre-commit run 全绿 **17 项 Passed**;check_doc_links --all --sections-strict 147 篇 0 问题(中途我自己写的 §4.3 没紧邻文档名,被判成本文自引用,已补全路径);9 个文件字节 卫生 0 异常(UI 头 BOM 仍在、无 CR、无 U+FFFD、均有尾换行)。 |
||
|
|
d9b887c0af |
feat(S-5 R4): 返回值不得裸丢弃成为机械检查,并修掉照出的第一处 ECC 静默降级
B 的第一步(加规则)与第二步(修最小最值的那处)。用户裁定"B 按你说的进行"。 **为什么只能项目侧自己做**(两条实测负结果,已写进 §2.27 与 bsp_err.h 边界纪律 4): · ARMCC v5 对 __attribute__((warn_unused_result)) 不生效(凌晨在 rs485_transmit 上实测:属性 在位、调用点裸调不接返回值,构建仍 0 错 0 警告); · cppcheck 也照不出来——按本仓自己的 --enable=warning,style,performance,portability 跑 gd5f2gq5ue.c,对 14 处丢弃 HAL_SPI_* 返回值、以及"未初始化局部量直接进 P_FAIL 判定" **一条不报**(它假定 gd5f_read_status(&status) 会写出参,而失败路径根本不写)。 结论:**丢弃底层传输的返回值,今天没有任何工具会响**。 **R4 上线即照出 37 条存量丢弃**:gd5f2gq5ue.c 29(14 处 HAL + 15 处自家原语)、net_socket.c 8、 ui_net_sink.c 4(net_close)、rs485.c 2、nand_ftl.c 1。按增量拦截老规矩入基线、只减不增—— 这是覆盖面扩大新暴露的历史债,不是新增违规;基线 19 → 1 → **38**。 **R4 自己被修三次(逐条记下,都是"规则比我想的更脆")**: ① 首版漏判"裸调用行尾带注释"这种写法(正则的 \s*$ 卡死),而那恰是最常见的"带说明的丢弃" → 补可选注释分支,并做 6 条正/反例自检; ② 把 net_set_errno 八处误判成丢弃(它是 setter,返回 int 是状态回声不是调用成败)→ **按真实 反例把规则收窄**(谓词 + setter 两类豁免),而不是塞进基线洗掉假阳性; ③ must-use 名单只从**头文件**取,gd5f_set_feature 这类 static 原语整个在视野外——而下面那处 真窟窿恰恰是 static、靠人工才发现 → 名单扩到 .c 内 static 定义(同一套启发式),补完立刻 又照出 gd5f_bbt_save#1:**运行时标坏后 BBT 落盘结果被丢弃 = 坏块记账可能没持久化**,已入 基线记为待还,本步不动行为。 另修一处自己的排版误判:R3 原本要求注释块出现字面"返回值",规范 V1.0 允许列对齐写法 「返 回 值:」→ 改为忽略空格匹配(门禁不该规定排版)。 **修掉的第一处真窟窿**:bbt_rebuild 的注释写着"无论扫描成败都须恢复 ECC,否则后续 I/O 在无 ECC 下运行",而它下面 gd5f_set_feature(0xB0,…) + gd5f_wait_busy(100) **返回值全丢**:SPI 一抖 ECC 就保持关闭,且 gd5f_check_ecc() 在 ECC 关闭时恒读"无 ECC 错"——整条链再没有任何信号。 补法复用 init 路径的 E-10(写完回读 B0h 验 ECC_EN 位真生效,"写了但没生效"与写失败同罪), 并把"关 ECC 失败"改成**立即中止**(此时 ECC 仍开着,是安全侧;带着 ECC 去扫 spare 反而会把 厂标好块误判成坏块,"重建 BBT"变成制造坏块)。恢复失败时按 (void)gd5f_bbt_load() 回读盘上 副本,避免 RAM 留着"比盘上更新"的表而下次上电又退回旧表。 **一条定位纠正(差点把修复记大)**:查链接映射发现 Removing gd5f2gq5ue.o (i.gd5f2gq5ue_bbt_rebuild), (604 bytes)——该函数在产品镜像里**根本没被链接**(产品构建无调用 者),只由 ENABLE_STORAGE_RECOVERY_TESTS 的恢复构建经 testTask 调用。所以本次修的是**恢复流程 自身的安全**(跑恢复时若 ECC 没恢复,设备会带着"已恢复"的结论在无保护状态下继续跑),不是 现场日常路径;相应地镜像 Code 的 +28 B 也不是它带来的(未逐字节追,线索是 gd5f_private_get_feature 由单调用点变多调用点、内联决策可能变化)。验证因此不能只跑产品构建: 临时打开 TEST_SUITE_STORAGE + ENABLE_STORAGE_RECOVERY_TESTS 编一次(0 错 0 警告且该函数不再 出现在 Removing 列表),随即还原两处开关并重编产品构建核对(test/board 无残留差异)。 **编号门禁把我自己的自造编号拦下了**:我起初用 B-1/B2/B3/B4 标记这批缺陷,id-check 报"未登记 前缀"13 处(且 B2/B3/B4 会与真实里程碑号撞号)→ 全部改写成平实语言。这是那条"注释与文档里 不许用没人检索得到的内部阶段编号"的规则第一次在提交前拦住我,规则是对的。 验证:@build 0 错 0 警告,Code 213,648 → **213,676**、RO 18,860、ER_IROM1 **232,536 B / 22.2%** (ROM 合计 233,724 B;RAM 逐字段不变);ram_report 重生成 + ram_baseline.py --update + 配图常量 同步(mem-numbers Passed);@test **PASS**(到 net init: OK 682 ms、[ERR] 0);R4 两处**探针 实证**(临时加一条裸调用 → 立刻成为新增并 RC=1;还原 → 38/38/0);--prune 点名本次还掉的 2 条 已删;pre-commit run 全绿 15 Passed;check_doc_links --all --sections-strict 147 篇 0 问题; id-check --strict 增量拦截通过。 文档:AGENTS.md 与 .pre-commit-config.yaml 的 bsp-err 口径(四条规则、基线 38); pre-commit使用说明.md §2.27(R4 全量口径 + 两条负结果 + 名单来源与豁免形状 + 单行限度); bsp_err.h 边界纪律 4;报告 §0.4 / §5.1 / §9 / 附录 A + 变更记录;存储维护与容量恢复.md 新增 §4.3(运维判据:出现 ECC restore FAILED / ECC not enabled 即本次恢复**不算完成**,须断电重启 重走 init,不许接着 nand_ftl_format() 当作已修好;并记明该函数产品镜像不链接)——顺带修该文 结构缺陷:原 ## 5. 变更记录 误插在 §4.1 与 §4.2 之间,会让 §4.2 变成 §5 的子节。 |
||
|
|
cc2966319a |
docs(S-5): 补齐 16 条返回值约定,bsp-err 基线 19 → 1,并修门禁自身误判
A(还债)先行,B(GD5F 的 SPI 失败被吞成成功)另波处理。本笔不改行为:@build 的 Program Size 与上一笔逐字段相同(Code 213,648、RO 18,860、RW 1,188、ZI 131,500)。 **补齐 16 条「返回值:」约定,bsp-err 存量基线 19 → 1**: · gd5f2gq5ue.h 九个 SPI 原语(wait_busy / write_enable / read_status / page_read / read_from_cache / program_load / program_exec / block_erase / check_ecc) 写全规范五要素, 并把**每个函数能返回哪些码**写死:哪一个是"可重试"的 GD5F_WAIT_EXPIRED、哪一个才是 器件自认坏块的 P_FAIL/E_FAIL、失败时出参为什么不可信(E-42 同族)。 · nand_ftl.h 从零散三行补成带模块头,并明写 **nand_ftl_init() 的 −1 抹掉了失败类**: 分类目前只在失败点进日志,要按类分支需要 C4 快照(现无消费者故未做);同时警告"不得 把 −1 解读成卷坏了可以重格式化"(E-31 的教训正在这条上)。 · ui_menu_tab.h 五个表查询 API 由行尾短注释升级为完整块(含"越界返回 false 是保守取 只读"、"宁可按一张错表导航失败也不要按错表导航成功"这类判断依据)。 **修掉门禁自己的一条误判口径**(本笔最该记的):R3 原本要求紧邻注释块里出现字面"返回值", 而规范 V1.0 允许列对齐写法「返 回 值:」。后果有两层:① 合规写法被判违规;② 反向更糟—— gd5f_write_enable / gd5f_read_status 两条**早就"通过"了**,原因竟是它们的**函数说明**里顺口 写了"返回值"三个字,而不是真有一行返回值说明。**一条会因为散文里出现关键词而放行的规则, 等于在奖励运气**。修法是在块内忽略空格后再匹配关键词(排版选择不是违规),并在注释里写明 为什么不许把"必须写成不空格的形式"写进规则。 剩余 1 条刻意保留:bool ui_content_page_open()。它与"补注释"不是一档事——要改签名、要处理 3 个调用点(ui_menu.c:138/192/196 **今天三处全部丢弃返回值**,即"按 ENTER 开页失败是静默的" 这件事现在没人知道)、且 UI 源码双端编译(模拟器同一份),验证面是"重建模拟器 + 像素回归 + 真机逐页进菜单"。这不是还债,是独立一波改动,留档待决策。UI 三个文件带 BOM,Edit 会静默 剥掉 → 已按原样补回 3 字节(AGENTS 铁律:丢 BOM 后 ARMCC 按 CP936 解码,中文报 #870-D)。 **更正我自己写进文档的一条错话**:S-5 各条里我把 GD5F_SPI_TIMEOUT 100 记成"自旋圈数(改 分频时长会变)"。这次逐行核它 12 处用法,**全部是 HAL_* 超时参数或 HAL_GetTick() 差值 → 它也是毫秒**。真问题降级为"单位没写进名字(缺 _MS 后缀),读代码必须回实现里查单位"。 osWaitForever 与 HAL_MAX_DELAY 同为 0xFFFFFFFFU、以及 *_BUSY_TIMEOUT = -2 其实是返回码这两条 仍然成立(后者已改)。现状类条目(报告 §0.4、bsp_err.h、pre-commit使用说明.md §2.27、脚本 文件头)已全部改准;**已提交的变更记录行仍写着旧说法——按 §8.1 冻结历史不回改,以本笔新增 的更正行为准**。 验证:@build 0 错 0 警告(尺寸同上,故**不跑 @test**——无指令变化,跑它没有新信息);受影响 文件的 17 个钩子全 Passed(含 ui-golden 像素回归、cppcheck、编码规范、bsp-err 1/1/0 且 --prune 干净);check_doc_links --all --sections-strict 147 篇 0 问题;id-check --strict 通过; 9 个文件字节卫生 0 异常(UI 头 BOM 仍在、无 CR、无 U+FFFD、无行尾空格、均有尾换行)。 口径同步:AGENTS.md、.pre-commit-config.yaml、pre-commit使用说明.md §2.27 的基线计数 19 → 1; 报告 §0.4 / §9 / 附录 A 与变更记录两条(还债一条、更正一条)。 |
||
|
|
78248a1cdf |
feat(S-5): 收编 SD2506/TPAFE 并给 dhara 失败分类,门禁基线 19→17
前一笔立契约与门禁(`Drivers/BSP/bsp_err.h` + `bsp-err` 钩子 + RS485 试点);这一笔按用户
指定顺序 C3 → C1 → C2 把三个模块真正收编,并让存储失败第一次变得可读、可分类。
**C3 · SD2506 整模块(11 个公共 API + 6 个内部 helper)**:三枚私有宏 `SD2506_OK`/
`SD2506_ERROR`/`SD2506_I2C_ERROR` **删除而不是留别名**——留别名等于新代码还能用旧形状,
把迁移洗掉;编译器替我们找全所有调用点。新增 `sd2506_err_from_hal()`,**把 I2C 超时从
"和没人应答共用一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、
I2C1 上电时序敏感,这是本波唯一真正的语义改善,不是改名。`sd2506_init` 的 charge 寄存器
**回读校验不符**原返参数类错误,改归 `BSP_ERR_IO`(那不是调用方的错)。
**C1 · 同形两义**:`GD5F_BUSY_TIMEOUT` → `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫
TIMEOUT 其实是返回码")。**GD5F 私有码集整体收编刻意没做**:169 处引用里 48 处在板级套件、
还要动 Trap 记录用词,属另一波带真机+套件回归的工作,不夹带。TPAFE5160 收编 23 处:
`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到器件无响应比
私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。实测 `tpafe5160.o`/`gd5f2gq5ue.o` 的 Code
增量为 **0**——改名不产生一条指令。
⚠ **`tpafe5160_snapshot()` 刻意不套成败码**:它是 1=有新帧 / 0=无新帧 / −1=参数错 的三态
**状态**返回,0 不是错误而是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。
顺带订正我上一版在 `bsp_err.h` 的断言——"本层只有 `net_*` 把数量与错误挤同一返回值"是错的,
`snapshot`/`adc_input_poll` 就是第二族;两族现都已写明并各注保留理由。
**C2 · FTL 坍缩点分类**:`ftl_flush_cache`/`ftl_read_page` 不再把 dhara 失败压成
`GD5F_ERROR`,
按**处置差异**分三类,并在最早同时知道 LPN 与原始码的位置就地打印:`ECC`/`TOO_BAD` →
`BSP_ERR_CRC`(页级,重试或换页,不代表卷坏);`JOURNAL_FULL`/`MAP_FULL` →
`BSP_ERR_NO_SPACE`(介质健康但块池/日志见底,此时重格式化只是把问题推回去);
`BAD_BLOCK`/`RECOVER`/`CORRUPT_MAP`/越界 → `BSP_ERR_IO`(完整性故障,保卷 + 上报,不许自愈
重格式化)。这与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编
`error.c`,用 `dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write` 仍回
`RES_ERROR`
(FatFs 契约);要让 defaultTask 卷健康探针按类分支还差跨调用存活的快照 → C4 待还。
**两次靠核实拦住的自我错误**:① 我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进
varargs 的格式 bug——核 `Lib/dhara/error.h` 后确认 `dhara_error_t` 是 `enum`(标量),那行日志
本来合法,改了反而是引入错误;② `bsp-err` 一次 `replace_all` 越界把 8 处 **GD5F 码空间**的
判据改成 `!= BSP_OK`(数值同为 0、无行为变化),但那正是"把两个码空间混起来",已逐一还原。
`cppcheck` 亦抓到 `cls` 声明作用域过宽,已收进失败分支。
**验证**:`@build` 0 错 0 警告(`Code` 213,648、RO 18,860、`ER_IROM1` 232,508 B /
22.2%、
`ROM 合计` 233,696;**RAM 三项零增量**);`@test` PASS(692 ms,烧入戳 `Oct 23 01:35:32`)
且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` 与 ID/时间/温度/电池四条读回正常、
`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、`adc=` 帧计数持续递增(证明
snapshot 三态未被改坏)、`[ERR]`/Fault/FAIL 0 条。主机单测:系统时钟 100/100、dhara NAND
移植层 149/149(两者都因本次改动需补 `Drivers/BSP` include 路径,补后在钩子内实跑 Passed)。
`pre-commit run` 全量 RC=0(20 个实跑钩子 Passed);`bsp-err` 基线 **19 → 17**(还掉两个
`*_BUSY_TIMEOUT = -2`)、`--prune` 干净;`doc-links --all --sections-strict` 147 篇
0 问题;
`id-check --strict`、`layer-deps`、`mem-numbers`、`mem-budget`、`ui-golden` 全过;33
个文件
字节卫生 0 异常(无 CR / 无 U+FFFD / 无行尾空格 / 均有尾换行)。
**口径与数字联动**:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.4(三类码与处置
理由);`pre-commit使用说明.md` §2.27 与 `AGENTS.md` 基线计数 19 → 17;TPAFE README、
GD5F 测试指南与 Trap 记录均**保留旧名留痕**以便对旧日志;配图桶按 .map 对象级增量重算
(`dhara/FTL` 10,931 → 11,222,+291 全部落在 `nand_ftl.o`;应用代码/UI +36 落在
`app_main.o`;其余散量入"其他"7,557;桶和 = Code+RO 断言通过、文本溢出 0);
`ram_baseline --update/--write`、`ram_report_20261023.md` 快照、报告 §1.3/§5.1/§5.4
与
部署文档 §3.3 数字全部同步;报告 §0.4/§9 的 S-5 状态改为 🟡 主体已落地。
|
||
|
|
8b0ea17b71 |
fix(lftpd): LIST/NLST 满块续发——大目录不再被静默截断并回 550
现象:`ftp_test.py` 的 `LIST 150->226` 断言失败——1512 B 数据全收到了,完成回包却是 **550**。 **根因**(`send_list`/`send_nlst` 各一处):定案三把"整份目录一次性批量发送"落进 static 1536 B 缓冲时,顺手留下了"装满就 `break` 并回 −1",`cmd_list` 据此回 550。于是目录一旦超过一块,客户端 拿到的是**静默截断**的清单**外加一个错误码**——而清单看起来完全像"目录里就这些"。这比单纯报错 更危险:没有人会去数条目个数。 **台架实测(同一台设备、同一个根目录,改动前后各测一次)**: · 改前:1512 B / **24 条目** / 完成回包 **550**(停在 A0007.BIN,后面 6 个文件被吞) · 改后:1880 B / **30 条目** / 完成回包 **226** **修法:满块续发**,而不是把缓冲区加大。新增 `list_batch_flush()`——装满一块就整块发出、清空 继续装,收尾再发最后一块。为什么仍要"批量":定案三的本体是实测结论——同一连接上背靠背 ~60 B 小段写会毒化 CH395F 的 TX(芯片报 SEND_OK 但对端全丢,无 RST 无 DISCONNECT),而 RETR 的 1 KB 满块连发安全;所以这里只发大块,绝不退回"每行一发"。单行真装不下时**明确失败**而不是少发条目 (8.3 短名 + 定长格式远小于 1536,真发生说明格式被改坏)。两条出口的 `lftpd_io_closedir` 收敛到 循环后单点:卷锁持有至 closedir,任何提前 return 都会漏锁。 **防回退**(关键):修完之后,**目录清空的设备上这颗雷再也暴露不出来**,所以补了 `test/host/ftp_list_fill_test.py`——自造 26 个 8.3 短名文件把清单撑过一块,三条判据(STOR 全 226 /LIST 全文里每个名字都在/完成回包 226 而非 550,用 ftplib `retrlines` 让非 2xx 完成直接抛), 跑完逐个 DELE 还原。真机实测 `收到 56 行 / 3544 字节`(跨 3 块,满块续发被真正走到)→ 三条全 PASS。用法登记进 `docs/测试说明/FTP_Test_Guide.md` §4.5,机理与增补写进 `docs/问题记录/CH395F_Trap_Records.md` 定案三。 **顺带记录的已知偏差(未修)**:`cmd_list`/`cmd_nlst` 忽略命令参数、恒列会话 CWD (`client->directory`)。主流客户端都是 `CWD` + 裸 `LIST`,实际影响小,先记在案。 体量:`Code` 213,264 → **213,436 B(+172)**、RO 不变 → `ER_IROM1` **232,088 B(22.1%) **、 `ROM 合计` 233,276 B;**RAM 一字未动**(`RW_IRAM1` 75,264 B 57.4% / `RW_CCM` 57,376 B 87.9%)。 数字联动照做:`data/ram_report` 重跑、`ram_baseline --update/--write`、 `check_mem_numbers --write`、 FLASH 配图常量重算重生成(桶合计 = 232,088 与 `Code+RO` 相符)、报告 §1.3/§5.1/§5.4 与部署 §3.3。 门禁:`@build` 0 错 0 警告;`@test` PASS(692 ms);`ftp_test.py` → **TEST PASSED**(含此前失败 的 `LIST 150->226`);`ftp_up_dl_test.py` → TEST PASSED;`ftp_list_fill_test.py` → TEST PASSED; `doc-links --all` 146 文档 0 问题;`id-check --strict` 增量拦截通过(可判定 82.82%); `mem-numbers` / `mem-budget` 通过;改动文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格。未推送。 |
||
|
|
4c21cff753 |
fix(lftpd): 对端行为不再打 [ERR]——新增 lftpd_peer 级别并划清级别边界
现象:抓自检台账基线那次 FTP 会话一结束,设备就打 `[ERR] [FTP] error reading next command`, 本次 `@test` 直接 FAIL(Forbidden 含 `[ERR]`)。查下去发现 lftpd 把**全部**异常路径一律按 error 级打,其中九处其实由外部输入决定:客户端关窗口、连上就走、PASV 后不建数据连接、 运维改 IP 触发监听重建。两条代价:① 任何人"连 21 端口一下就断开"就能把一次启动测试打成 FAIL;② 同样的动作可以**无限制造固件级 ERROR 日志**——日志面与门禁面都可被外部驱动, 工业现场过不了审。 **做法**:`private/lftpd_log.h` 新增 `lftpd_log_peer()`(info 级,带 `[FTP]` 前缀),而不是 就地改成 `DBG_INFO`——让"谁才算真故障"在读代码时一目了然,也防止后来者顺手把对端行为升回 error。降级的九处:控制通道被对端关闭(原 `error reading next command`,改为 `control channel closed by peer (ret=%d)`,措辞原先把正常收尾写成故障、误导过排查方向)、 发送 220 前对端已走、`listener rebuilt`(网络重建,原因由 accept 层自己记)、四处 `data accept failed`(PASV 后没来数据连接)、`accept timeout`。 **刻意留在 error 的**:create/bind/listen/取端口失败、写通道失败、文件打开失败、 `DOWNLOAD/UPLOAD FAILED`(对端中途退出确实让一次传输没走完 = 数据完整性事件, `../问题记录/CH395F_Trap_Records.md` 的中止家族要靠这些锚点定位)、 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **实测划清的边界**:客户端**干净收尾**(发 `QUIT` 或 FIN 半关)现在整条链路没有任何 `[ERR]`; 但 **RST 硬拆**(`SO_LINGER` 清零 / 进程被杀 / 拔线)仍会留一条 `[ERR][NET] sockN TIMEOUT`—— 那是 net 层的套接字超时锚点,网络自愈架构判据要读它,不在本次降级范围。所以"采集窗口内别跑 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **真机验证**:`@build` 0 错 0 警告;`@test --require-selftest-pass --device-ip` → **PASS** (基线 29 条 → 本次无新增;`net init: OK` 726 ms);专门写了 RST 复现脚本(登录后 `SO_LINGER` 清零直接关)→ 串口得到 `[FTP] control channel closed by peer (ret=-1)`,**不再是 `[ERR]` 行**; 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 体量:`Code` 213,236 → **213,264 B(+28)**、`RO` 18,580 → **18,652 B(+72)**(措辞变长), 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 `ZI` 131,500、`RW_IRAM1` 75,264 B 57.4% / `RW_CCM` 57,376 B 87.9% 全不变)。数字联动照做: 两份 `data/` 快照、`ram_baseline --update/--write`、`check_mem_numbers --write`、FLASH 配图 常量重算重生成(桶合计 = 231,916 与 `Code+RO` 相符;越界复查 0)、报告 §1.3/§5.1/§5.4 与 部署文档 §3.3 同步。 口径落文:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.3「FTP 日志级别划分:对端行为 vs 固件故障」(含两类的判据表与边界),并更新 `docs/测试说明/自动化测试流程.md` 的采集窗口警示。 顺手把 `private/lftpd_log.h` 工作区的 CRLF 混用归一回 LF(仓库里 blob 本来就是 LF,diff 无影响)。 门禁:`doc-links --all --sections-strict` 146 文档 0 问题、`id-check --strict` 增量拦截通过 (可判定 82.82%)、`mem-numbers` / `mem-budget` / `layer-deps` 全绿;改动文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格。未推送。 |
||
|
|
e7f28502bc |
refactor(NET): netMsgQueueHandle 收进 net_socket 私有,改为创建后绑定
结构债 S-2 的最后一步——**该族的 24 处手写跨模块裸 extern 就此全部清零**。 队列对象仍由 CubeMX 在 `MX_FREERTOS_Init()` 创建(**创建点不可外移**: `osKernelInitialize()` 之前的任何创建类 API 会杀死启动,见 `docs/架构设计/项目启动顺序管理.md`);改为创建后立刻在 `Src/freertos.c` 的 `USER CODE BEGIN RTOS_QUEUES` 区调用 `net_msg_queue_adopt(netMsgQueueHandle)`, 把句柄交给唯一使用者。`net_socket.c` 内改用 `static osMessageQueueId_t s_net_msg_queue`(21 处引用改名),BSP 对该全局变量的裸 `extern` 消失——现在 `netMsgQueueHandle` 只出现在生成文件自己的两处(定义 + 创建)。 两个刻意的设计决定: ① **不新建第二个队列**。自建队列 + 删 `.ioc` 里的 netMsgQueue 也能收口,但要 CubeMX 重生成(Ask-first),过渡期还会同时存在两个队列,白占 CCM 堆里 8 × `net_msg_t` 的空间——CCM 正是当下 87.9% 的红线。故选"创建后绑定"。 ② `adopt()` **拒绝重复绑定、保留首次句柄**。真出现两处绑定时,静默换句柄会让 已入队的请求留在旧队列里永不处理;拒掉第二个反而更容易暴露。绑定前的判空 是真实防线:丢了那一行,后果是"所有 net_* 请求被丢弃、网络全线静默不通", 不是崩溃。 **防回退**:`check_cubemx_survival.py` 新增存活项 `net-msgq-adopt`(region `RTOS_QUEUES`、`must_contain=["net_msg_queue_adopt"]`),易失清单 29 → 30 项全部 到位;基线已重建(有意的 USER CODE 区改动,按脚本文档流程 `--write`)。 **体量**:`Code` 213,204 → **213,236 B(+32)**、`ER_IROM1` **231,816 B(22.1%)**; `RW +4 / ZI −4` → `RW_IRAM1` 恒 **75,264 B(57.4%)**、`RW_CCM` 恒 **57,376 B (87.9%)**、`RW+ZI` 总量 132,688 B 不变(没有多占一个字节 RAM)。 **验证**:`@build` 0 错 0 警告;`@test` PASS——624 ms 到 `net init: OK`、`i104: listening on 2404`、无 `[ERR]`/Fault、`heap free=15800 min_ever=8464` 逐项不变。 另在真机跑了一次 FTP 控制通道往返:220 → `USER admin` 331 → `PASS admin` 230 → `PWD` 257 → `TYPE I` 200 → `QUIT` 221。每条命令都是一次过队列的 net_recv/net_send,证明绑定路径真在跑;`.map` 里 `freertos.o(MX_FREERTOS_Init) refers to net_socket.o(i.net_msg_queue_adopt)` 证明镜像含该行。 ⚠ **顺带揪出一条测试工具漂移(本次未修,等定夺)**: `test/host/ftp_up_dl_test.py:123/126` 仍写死 `USER anonymous` / `PASS test@test.com`,而设备自 N-19(2026-10-11)起按 `/config.ini [ftp]` 校验 admin/admin(SHA-256 `pwd_hash`)→ 该脚本必然登录失败。它是手工工具、不在任何 pre-commit 钩子里,所以 11 天没人发现,而 `docs/测试说明/FTP_Test_Guide.md` 把它 列为"推荐入口"。建议加 `--user/--password`(默认 admin/admin),并连 `ftp_speed_test.py` 一起核。 数字联动:两份 `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 行标 ✅。 门禁:`@build` 0/0、`@test` PASS、`layer-deps` / `cubemx-survival` / `mem-numbers` (本次改到台账受管区,scan 已过)/ `mem-budget`(57.4% / 87.9%)/ `task-table` / `doc-links --all`(146 文档 0 问题)/ `id-check --strict`(增量拦截通过、可判定 82.84%、存量债 640)全绿;14 个文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无尾空格。 未推送。 |
||
|
|
6ede1443e9 |
feat(门禁): 新增 layer-deps——BSP 反向 include App 的增量拦截(37→38)
推荐清单第 5 步的拆分执行:反转依赖(5a)暂缓,**只把"防扩大"的门禁先上**。
**为什么门禁比搬迁值钱**:AGENTS.md 与分层图早就写着"六层单向依赖",可从来没有
机械检查——于是同一件事的记载从 2026-09 审查报告的"NETCFG 一处",漂到 2026-10-22
实测的 **8 个文件 9 处**,全靠人读代码才发现。边界的静默腐蚀只能靠门禁止住。
**机制(与 `id-check` 的增量拦截同构)**:`scripts/check_layer_deps.py` 扫
`Drivers/BSP/**.{c,h}` 的 include,按 `App/**` 头文件索引解析反向边(同名头按字面
路径消歧),命中分三类:
· ALLOW 永久豁免 3 条横切叶子头——`App/log/console_log.h`(日志后端,BSP 的
dbg_log.h 就建在它上面)、`App/util/sys_clock.h`(时间源)、`App/util/sha256.h`
(纯算法、无状态)。它们是"任何层都要用的叶子能力",不是上层服务;新增豁免
必须先回答"它为什么不是上层服务"。
· BASELINE 钉住存量 6 条:`cfg_file.h`×2(NETCFG)、`vol_lock.h`×1(lftpd_io)、
`sys_log.h`×3(UI 两处 + `ui_w_records.h`)。**只减不增**(脚本没有 `--write`)。
· 新出现的 (BSP 文件, App 头) 组合 → FAIL,并给出三条出路(注入回调 / 上层传参 /
确属叶子才可豁免)。
`--prune` 在某条存量还清时点名"可从基线删除",于是"还欠几条"成为可见进度,而不是
文档里的一句口号。
**当场自证**:落地时临时给 `Drivers/BSP/RS485/rs485.c` 加了一行 include `cfg_file.h
`,钩子当场 FAIL 并点名该文件与该头(退出码 1),删掉后转绿——没被触发过的门禁
等于没有门禁。
**边界要说清(免得被误读成缺陷)**:本钩子只拦**静态 include 方向**。运行时的属主
纪律本来就没破——UI 只投递 `net_cfg_save_request()`,真正写盘在 defaultTask,模拟器
也早在主机侧跑整条配置链。
**口径同步**:钩子数 37 → 38 共 6 处(`AGENTS.md` 两处、`pre-commit使用说明.md` 两处
并新增 §2.26 详解、`并行任务工作树方案.md`、报告 §0.1/§1.1);
`项目架构总览.md` §1 分层原则处补"此方向已机械检查";报告 §0.4 的 S-1 计数由
"7 文件 15 处"更正为脚本口径的 8 文件 9 处,§9 状态改 🟡。
**暂缓项(都已在报告 §0.4 记因)**:5a 反转依赖——没有功能在等它,且要过"改 IP
免重启生效"整条真机链,留给下次真动 NETCFG/UI 时顺带做;5b 目录搬迁——uvprojx 326
条 + IncludePath + CMakeLists + 42 个 UI 文件 + 全库引用改写,收益只是"目录名好看",
而远程 CI 仍刻意不跑 `@build`,兜底未成立。
门禁:`layer-deps` 自身通过;`check_doc_links.py --all --sections-strict` 146 文档 0
问题;`check_ids.py --strict` 增量拦截通过、可判定 82.84%;`mem-numbers` 0;
`py_compile` 通过。本轮无 `.c/.h` 改动,未重跑 `@build`。未推送。
|
||
|
|
7e77af08a0 |
fix(BSP): SPI1 完成标志收回 GD5F 私有,HAL 回调改契约转调
推荐清单第 4a 步(结构债 S-2 剩余面里唯一带"静默失效"属性的那条)。 **问题不是风格**:HAL 的 SPI 完成/错误回调是**全局唯一符号**,整个工程只能有一份 实现,当前由 CH395F 代管并按 `hspi->Instance` 分发——于是 GD5F 的完成标志 `g_spi1_dma_done` 被 CH395F 跨模块裸 `extern` 直接写,**GD5F 出错时的"释放等待者" 寄居在别人的 .c 里**。谁哪天做"CH395F 清理",把那两个 `else if (SPI1)` 分支当无关 代码删掉:编译零报错,GD5F 的 DMA 等待在出错时只能干等 `GD5F_SPI_TIMEOUT` 兜底。 **改法(时序一字不动,只换所有权)**: · 标志收为 GD5F 文件内 `static volatile uint8_t s_spi1_dma_done`(6 处引用同步改名) · GD5F 导出 `gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()`,头文件里写明 "SPI1 分支必须转调这两个函数,否则本模块的 DMA 等待在出错时只能靠超时兜底" · CH395F 删除裸 `extern`、include `gd5f2gq5ue.h`、两个 SPI1 分支改为转调并就地注明 "勿删:少了这行 GD5F 的 DMA 出错只能靠超时退出" `HAL_SPI_AbortCpltCallback`(`HAL_SPI_DMAStop()` 会触发)本次**刻意不做**——它会改 变中止语义、需要真机专项,当前超时兜底可用(已在报告 §0.4 记为观察项)。 **体量代价**:`ch395f.o` −8 B、`gd5f2gq5ue.o` +24 B → `Code` 213,188 → **213,204 B (+16 B)**,RO/RW/ZI 全不变,`ER_IROM1` 231,768 → **231,784 B(22.1%)**。16 B 换 掉一个静默失效类,划算。 **真机 `@test` PASS**:无 `[ERR]`/Fault,复位原因 software;`[STACK] heap free=15800 min_ever=8464` 与改前**逐项相同**;各任务实测峰值 default 2152 / net 1116 / ftp 764 / ui 4052 / timesync 616 / adc 952 / iec104 540 / protect 624, **静态最坏链全部未变**(3024/1912/1456/4052/1264/1320/1456/872)→ 9/9 PASS。 ⚠ 记一条读数陷阱:测试脚本打印的 "boot to net init: OK = 7,907 ms" 是采集窗口 锚点造成的度量偏差,真机日志里 `boot: syslog init OK → net init: OK` 实测 ≈699 ms, 与改前一致,**不是性能回退**。 **数字联动**:`data/ram_report_20261022.md`、`data/stack_report_20261022.md` 重跑; `ram_baseline.py --update/--write`;`check_mem_numbers.py --write`(RAM 方案受管区 + 台账);`gen_memory_svg.py` 常量更新后重新生成 FLASH 与任务栈两张配图(逐文本按 字号与锚点复查越界 = 0;RAM 图无需变更);报告 §1.3/§5.1/§5.4、部署文档 §3.3 的 Flash 数字同步;§0.4/§9 的 S-2 口径改为"24 处手写已消 23 处,只剩 `net_socket.c:77`"。 门禁:`@build` 0 错 0 警告;`cubemx-survival --check`、`mem-numbers`、`mem-budget` (57.4% / 87.9%)、`task-table`、`doc-links --all --sections-strict`(146 文档 0 问题)、 `id-check --strict`(增量拦截通过、可判定 82.83%)全绿;字节卫生 BOM/CR/U+FFFD/尾 空格全清;未提交 UV4 抖动的 `.uvprojx`(本次未被改写)。未推送。 |
||
|
|
504db26698 |
docs(内存): 阶段子项改层级编号 4.阶段.项,消除与顶层章节同号撞面
根因:`RAM使用分析与优化方案.md` 顶层有 §1 背景 / §2 现状测量(2.1~2.6),而 §4 「方案」下"阶段 N"的子项也用裸号 0.x / 1.x / 2.x——阶段 2 的 2.1/2.2/2.3 与顶层 2.x **完全同号**,阶段 1 的 1.x 又长得像"第 1 章第 x 节"。`doc-links` 对本文自 引用按设计直接放过,于是门禁和人一起看错:AGENTS.md 把"栈余量台账"写成 §1.4, 而 §1.4 其实是阶段 1 的 lftpd 静态化项(上一笔提交的信息里我把它误判成断链, 已 amend 更正)。 改法:阶段子项一律改层级编号,阶段号不变、只加 `4.` 前缀 —— 0.x→4.0.x、 1.x→4.1.x、2.x→4.2.x。共改 14 个标题;文内自引用与实施序列表(S4~S9 那套)、 暂缓清单、§5 预期效果列里的裸号一并补成 §4.x.y。 外部引用同步 15 处:AGENTS.md、lftpd/README ×2、菜单页面设计、RAM预算台账 ×2、 项目架构总览 ×3、FTP_Test_Guide ×3、boot_log_data/README、部署环境要求、审查 报告、ram_baseline.py ×3。最有说服力的两处歧义:审查报告与 RAM预算台账都写 "缩堆见 …§2.2"——既可读成顶层 §2.2(静态大项排行)也可读成阶段 2 第 2 项, 现均为 §4.2.2(阶段 2 第 2 项"缩堆")。 另修 4 处把 §1.4 当"栈台账"的错指:采样有效性校验设计说明 :441/:593/:650 改 指 §2.3(任务栈配置与实测峰值)与 §4.0.6(静态最坏链与判定);id_aliases.json 的 note 改指 §4.0.6 并标注"原 §1.4,2026-10-22 层级编号"。 冻结与示例:其它文档**变更记录行**里的旧编号按项目纪律不改写(项目架构总览 :367、RAM预算台账 :98),改在被引文档 §4 开头留一张换算表 + 约定说明;把旧号 当语法示例写的 7 处一并换掉(文档规范 ×3、check_doc_links ×3、rename_ref ×1), 否则示例本身指向已改名的锚点。主文档版本行 bump 到 v1.3 / 2026-10-22。 门禁:check_doc_links.py --all --sections-strict → 146 个文档 0 问题(章节引用 2513 处、机器可判定 1491);check_ids.py --strict 增量拦截通过;mem-numbers、 ram_baseline --gate(SRAM1 57.4% / CCM 87.9%)、task-table 全绿;本轮未改任何 .c/.h。16 个文件字节卫生自查 BOM/CR/U+FFFD/行尾空格全清。已提交本地,未推送。 |
||
|
|
f4b9bd57d5 |
docs(部署): 删任务栈副本表改指针;纠 CCM/FPU 事实并刷新配图基线
范围:AGENTS.md 一处指针、部署环境要求 §2.2/§3.1/§3.3、RAM 方案 6 处现状 数字、内存配图生成器 + 3 张 SVG、两份报告脚本的行尾与路径修正、新增两份 .map/.htm 数据快照。未改任何 .c/.h(仍跑 @build 复核:0 错误 0 警告)。 部署环境要求.md - §3.3 删"任务栈预算"副本表、改为指针:该表已缺 adcTask/iec104Task/ protectTask,defaultTask/timeSyncTask 两值停在旧数,注释却写"由 gen_task_table.py 同源"——该钩子的校验面只有 README.md / AGENTS.md 两处受管块,抄出来的副本没有门禁、改栈后必漂。指针指向 Src/freertos.c + 两份受管表 + RAM 方案 §2.3/§0.6,并留一条"别再抄"的成因说明。 - §2.2 链接布局补 `RW_CCM 0x10000000`(scatter 给 0x0000FF00 = 65,280 B, 2026-10-11 起承载 FreeRTOS 堆;另有 256 B UNINIT_CCM 尾区),写明 CCM 不可 DMA。原文"SRAM3/CCM 故意未映射"与 STM32F407-Demo.sct 直接矛盾。 - §3.1 MCU 行更正为**单精度 FPU(VFPv4-SP,无硬件双精度)、无 TrustZone** (原写"双精度 FPU / TrustZone"是芯片事实错误,也正是报告 F-6 双精度软 浮点被长期误判的土壤);SRAM 行改为"两块都在用,仅 SRAM3 未映射"。 - §3.3 资源预算表补 CCM 行,并把"主 SRAM 125,104 B / 95.4% / 极度紧张" 更正为 75,264 B / 57.4%(余 55,808 B);红线改标在 CCM(余 7,904 B)。 - 变更记录补 2026-10-22 行(含上一提交里的资源表刷新那一笔)。 AGENTS.md:任务表注里 "RAM使用分析与优化方案.md §1.4" 是**指错小节**——§1.4 确实 存在(该文档 §4「方案」下的阶段 1 子项,讲 lftpd 大局部缓冲静态化),但栈余量 台账在 §2.3(任务栈配置与实测峰值)与 §0.6(静态最坏链与判定),故改指这两处 再加 内存内容分配策略.md §6。根因是该文档里 §1(背景)与 §4 下的 1.1~1.5 同号 撞面,人和 doc-links(自引用放过)都会看错——本会话我上一轮就被它带错过一次。 配图与数据(draw-diagram 流程:改生成器顶部数据 → 重跑 → 逐文本核越界) - 新增 data/ram_report_20261022.md、data/stack_report_20261022.md;后者 运行时水位取自 2026-10-22 真机 [STACK] 心跳(8 任务高水位 + heap free=15,800 / min_ever=8,464),静态侧来自同日 .htm(全局最深链 3,024 B)。 - gen_memory_svg.py:三组图数据从 2026-09-21 / 10-10 快照换成 2026-10-22 实测;任务栈 6 → 9 行,堆总量 48KB → 56KB,判定 6/6 → 9/9 PASS,新增 AboveNormal 配色;Flash 子条改为"窄段不压字 + 下方四列图例";任务栈 画布 440 → 620 高。CCM 段总量按 scatter 用 65,280 B(与台账同口径)。 - 三张 SVG 重生成;用"CJK≈1.0em / 其余≈0.58em + 锚点"逐文本估算边界, 修掉 5 处文字越界/压字(生成器内置 ElementTree 自检亦通过)。 - ram_report.py / stack_report.py 两处工具性修正:write_text 未指定 newline 参数 → Windows 下把 data/ 快照写成 CRLF(与 .gitattributes 及既有快照 不符);stack_report 的 relative_to 在传相对 -i 时抛 ValueError,且会把 本机绝对路径写进入库快照 → 统一走 resolve+relative_to 的相对路径回退。 RAM 方案:6 处"当前状态"随 .map/[STACK] 更正——RW_IRAM1 89,168 → 75,264、 RW_CCM 49,184 → 57,376、FreeRTOS 堆 48KB → 56KB(两处)、CCM 余量 ~16KB → 7,904 B、min_ever 4,848 → 8,464 B、6 任务全过 → 9/9 PASS;S-1 历史句保留。 门禁:pre-commit 对全部改动文件 Passed(doc-links --sections-strict、 task-table、mem-numbers、mem-budget、id-check 增量拦截);字节卫生自查 BOM / CR / U+FFFD / 行尾空格全清。已提交本地,未推送。 |
||
|
|
d4867ddbe2 |
fix(algo): Q10 定值宏改单精度;收口 Flash 优化簇并更正审查报告三处误判
影响面:一个头文件宏(App/algo/prot_set.h)+ 七份文档口径;固件行为逐位 等价,无功能变化。门禁全绿后提交,未推送。 验证链 - @build:0 错 0 警;Code 213,520 → 213,188(−332 B),RO/RW/ZI 不变。 - 真机 @test PASS:戳 fw build: Oct 22 2026 10:36:15 suite=NONE;无 [ERR]/ERROR/FAIL/Fault;708 ms 到 net init: OK;protect task start、 i104: listening on 2404、[STACK] 三面水位齐。 - 主机单测:prot_set 90/90、prot_elm 47/47、iec104 97/97。 - 等价性穷举:宏内 42 个字面量;int32 全域 ÷1024 往返 4,494,305 例零 差异;×1024 方向 20 万随机 float 零差异(×/÷1024 是 2^10 指数平移, 不产生尾数舍入)→ 定值默认值/上下限、校验和、104 上送值全部不变。 F-6 双精度软浮点(已落地) - 两个 Q10 宏去掉显式 (double)、1024.0 → 1024.0f:保护判据每 20 ms 每 元件 3 次定值读取改走 F407 单精度 FPU,不再软件模拟。 - .map 里 dflti/d2f/f2d/dfixi/fepilogue 共 300 B 消失;自研侧对双精度 helper 的引用归零。 - 更正报告归因:全库 0 处 %f(扫 App/Src/Inc/Drivers 共 173 个 .c/.h), 剩下 dadd/dmul/ddiv/depilogue/dfixul = 1,018 B 由 MicroLIB printfa.o 的 _printf_core → _fp_digits 无条件引用,只要还用 snprintf 就删不掉; 报告"把 %f 改整数刻度"没有可执行对象。 F-4 LVGL 死代码(判定不做) - 报告列的五项字节数逐字节仍准(合计 10,060 B),但没有一项有开关: 根因是 lv_draw_sw.c 的 execute_drawing() 无条件 switch 引用全部 task 类型,链接器 --remove 无从下手。 - 唯一总开关 LV_DRAW_SW_COMPLEX 被 ui_deco.c 的"浅 V"3 px 粗斜腿依赖 (斜段与其所需 mask 核心都在该开关内);2026-09-03 已实测失败并把两端 =1 固化 → UI_设计架构.md §1.4 第 7 条。 - 报告原"mask/grad 是文字与矩形绘制必经路径"证伪:字形走 blend 层 mask_buf,不经 lv_draw_sw_mask_*。 - 剩余路线需给 Lib/lvgl 打本地补丁(上限 ≈16 KB),而 Flash 仅占 22.1%、 无 OTA/双镜像预算 → 收益不抵代价;图片链同理不打补丁。 F-5 uvprojx 空 TU(降级为观察项) - 实测 326 个 .c / LVGL 201 个 / .map 零产出 127 个;但增量构建 4~5 s、 空 TU 只在全量重建或改 lv_conf.h 时才重编,且远程 CI 刻意不跑 @build。 - 报告原"编译时长 −40%"无基线记录,不复述;真要做用 Keil 原生 <IncludeInBuild>0</IncludeInBuild>,且排在任何 lv_conf.h 改动之后。 文档与台账 - 审查报告 §0.1/§0.4/§1/注册表/§5.1/§5.4/§6.4/W4 行按实测更正,补 2026-10-22 变更记录行与 F-4/F-6 记录去处行;登记簿 N-28 改写为"三项 已判定、仅剩 F-5 观察项"。 - 更正 FTL 写回缓存旧口径(sys_log_mcu.c 注释、运行与自检记录设计说明 §4.4、实机测试实施指南 §9.1):f_close 经 sync_fs → CTRL_SYNC → ftl_flush_cache 已落盘,"写另一个文件逼出脏页"不是必要条件;2026-10-02 那次"复位后写入整段丢失"根因是 dhara resume 锚错 checkpoint。 - 部署环境要求 §3.3 资源预算由 2026-09-11 快照刷新为 2026-10-22 实测, 并补 CCM 红线行(旧"SRAM 95.4%/极度紧张"的说法早已失效)。 - ram_baseline.py --update/--write、check_mem_numbers.py --write 已回填: mem-budget(SRAM1 57.4% / CCM 87.9%,阈值 92%)与 mem-numbers 转绿。 |
||
|
|
af411866a8 |
feat(门禁): 批 9 位号规则扩展到引脚名 + 修 --prune-backlog 丢 reason 的 bug
规则扩展(scripts/check_ids.py,两处共用同一正则)
- 原:^[RCDJXUT][0-9]{1,3}(单字母位号)
- 现:再加 ^[A-Z]{2,3}[0-9]{1,2}(-[0-9]{1,2})? → 收掉硬件语料里的**引脚名 / 总线名 /
网名 / 引脚区间**:PB12-15、PG0-15、PF13-15、DB0-15、OS0-2、KEY1-3、AIN1 这类
- 关键边界:带连字符的一字母形态(C-10 / A-9 / N-18 / D-7)与 TC-206、TC1003 **不落在
新形状里**,照旧判定 → 报告自有条目号这类真编号没被顺手挡掉(实测台账仍留 21 类真债)
- 位号与引脚名都不进 refs_total:错误级 661 → **651**,refs_total 8807 → 8797(同步 −10),
可判定比例 82.84% → **82.94%**(分母没被噪声灌水,涨幅来自噪声出清而非真进展)
修自己的 bug:--prune-backlog 会静默丢掉 reason 字段
- 症状:收账一次后,那 28 类硬件存量条目的「这笔债为什么该存在」说明全部消失
- 根因:prune 用 dict 字面量重建条目,只写了 file/code/literal/count 四个键
- 修法:改为从原始 JSON 取整条再覆盖 count,其它字段原样保留;已实测幂等
(再跑一次「还清 0 条、剩 313 条、带 reason 仍 21 类」)
- 21 类真债的 reason 已重新写回,内容含一条判断:器件料号 FOC-817 **不做**形状规则,
因为 [A-Z]{3}-\d{3} 与真家族 DEF-* 撞形,宁可留一条记账也不制造盲区
验证:id-check --strict 通过(存量 651 全在台账内、无未登记新账);
doc-links --all 失效与待确认均 0;未改 C/H 不需 @build;台账只减不增
|
||
|
|
125140a3c6 |
feat(门禁): 硬件语料纳入扫描——位号不进编号也不进比例分母
- check_ids.py 扫描循环:位号([RCDJXUT][0-9]{1,3} 无连字符)在 refs_total 之前 continue,
既不产生结论也不灌分母;add() 里的同一道判定保留作兜底
- 注册表 excluded_paths 移除 docs/硬件平台/(docs/芯片手册/ 仍排除)
- 首次纳入暴露 28 类先前不可见的存量 → 按规矩逐条写 reason 入台账(**不重打快照**,
台账只增带理由的条目);错误级 626 → 661,可判定 82.84%(基线 82.6 仍守住)
- 遗留噪声已在 designator_rule 记明:引脚名 PB12-15 / PG0-15 / DB0-15(两字母+数字+区间)
不是编号,但位号规则不覆盖这种形状,先记账、待加 pin-name 类型规则后清除
- 验证:--strict 通过(无未登记的台账外新账);未改 C/H 不需 @build
|
||
|
|
11c82c0d99 |
feat(门禁): 位号类型识别机制落地(暂不纳入硬件语料)
- check_ids.py 的 add() 加一道类型判定:硬件类语料(docs/硬件平台/、docs/芯片手册/)里 匹配「单个大写字母 + 1~3 位数字、无连字符」的字面量判为**位号**(R 电阻 / C 电容 / D 二极管 / J 接插件 / X 晶振 / U 芯片 / T 变压器),直接不产生编号结论,并计数到 stats[designator_skipped],不是静默放过 - 两处刻意收窄:字母集**不含 B**(V0.0 网表报告里的 B1 是真专项条目);要求 fullmatch 无连字符,所以 D-7 / N-18 / TC-206 / TC1003 这些真编号与违规形态一律照旧判 - 注册表新增 designator_rule 说明口径;**本批不解除 excluded_paths 里的硬件目录排除**: 粗测该目录约 4200 处位号,会灌进可判定比例的分母 refs_total,把真实变化盖掉—— 解除排除必须先把位号从分母里减掉,那是下一件事 - 因此本批对现有数字零影响:错误级仍 626、可判定比例不变、--strict 通过 |
||
|
|
ee25b10053 |
chore(编号): 硬件网表报告里的 Trap05 统一为 TRAP-NET-05
- 6 处裸形 Trap05(V0.0 报告 5 处 + V0.1 报告 1 处)→ TRAP-NET-05,序号不变只补模块码 - 语义已核对:属主条目 docs/问题记录/CH395F_Trap_Records.md 的 TRAP-NET-05 就是 CH395F 与 RTL8305 自动协商不兼容,同名不同义的担心排除掉了 - 别名表登记 Trap05 → TRAP-NET-05(scope 限定这两个文件,写进真容器 aliases) - rename_ids.py 解除对 docs/硬件平台/ 的排除(芯片手册仍排除) - 注册表 TRAP 族注记改为事实:该目录仍不进门禁扫描语料——粗测约 4200 处 R/C/D/J/X/U/T 位号,位号类型识别做完前纳入只会让 --strict 为位号天天拦提交 验证:id-check --strict 无台账外新账;doc-links 失效与待确认均 0; 改名文件 BOM=否、CR=0、U+FFFD=0;未改 C/H,不需 @build |
||
|
|
fca414102e |
fix(编号): 批 6 追溯器分档 + 板级 NET 用例号标注归一
步骤 1:追溯器分两档(不改数据,改判据) - BENCH:文档**定义行**(标题行或表格首格)带「(硬件层)」或其所属章节带的号 → 人工台架用例,不期望有 C 代码载体;NOTIMPL:定义行标了未实现/待补/暂缓/已取消 - 两档**单列示数恒打印**、明细进 --verbose,不是静默豁免;也不等于"已验证" - 两个实测坑写在脚本注释里:① 按"任意行出现即算"实现时 BENCH 收 14 个号却只消掉 1 处差异(同一号被别处引用一次就复活)→ 改为只认定义行 + 从文档全集整体减档; ② 载体类型必须由定义处决定,引用行不影响判断 - 另把 has_intent() 收窄到同号所在行(原先整文件任意位置有人话就算过关) 步骤 2:板级 NET 用例标注归一(真债,改名 109 处 / 5 文件) - 取证发现"文档声明却无载体"绝大多数是**标注形态烂**:实现体里写的是 TC204 / TC-201 / TC 703 / TC1501 / TC1502b(三种畸形),文档写 TC-NET-204 等 - 序号一位不改、只补模块码(§7.1);作用域限定 5 个真载体文件: test/board/ch395f_test_task.c(67) net_test_task.c(37) net_test_task.h(1) test/host/ch395f_socket_test.py(3) Drivers/BSP/CH395F/ch395f.c(1) - 审计轨迹 scripts/rename_maps/batch6-net-annotate.json;别名自动登记(scoped) 实测 - DOC-ONLY 29 → 15:701~704 的 NET 层实现体一直存在(net_test_task.c), 此前因裸形标注被误判成"未实现";1501~1503 属硬件层人工 + 同样裸形 - 交集 260 不变、代码侧 578 不减(识别面没缩);BENCH 14 个号 / NOTIMPL 1 个 - 台账 --prune-backlog 还清 6 条(只减不增);@build 0 错误 0 警告 (Program Size 与改前逐字节相同:Code=213356 RO=18580 RW=1184 ZI=131480 → 证实只动了注释与日志串);7 个改动文件 BOM/CR/U+FFFD 与 HEAD 逐项一致 剩余 15 处 DOC-ONLY 的组成(步骤 3 未完,下轮处理) - 4 处文档侧无模块码形态(TC-1003/206/501/704)→ 属 ② 大批 - TC-NET-607 一条是历史指称(原文"原 607/608/609,现 701/702/703") - 真未实现 10 条:TC-NET-101/102/601/606/801/802/901/902/1006/1101 ——其中 801/802/901/902 曾在 2026-08 的代码里有载体、后被重构丢掉,最值得补 |
||
|
|
43fbfa140b |
fix(门禁): 批 5 追溯器口径补齐——变更记录行不参与双向比对(未改任何号)
结论先行:我上一轮说「批 2 模块码单词化漏扫了 4 篇属主文档」——**被证据推翻** - 依据是 check_test_traceability.py 报的 DOC-ONLY 38 处里有 8 处旧模块码 (TC-AI-070/080/090、TC-PH-001/030、TC-TS-001、TC-VL-001),我据此写好了 逐号点名的改名映射表准备落盘 - 改名工具干跑 **0 处命中**:查下去这 8 处**全部在变更记录行**(| 2026-10-10 | … |)里, 写的是**当年**的用例名;按编号规范 §8.1 冻结历史不回改,旧号靠别名解析—— 而 TC-AI-070 → TC-ADCIN-070 这批**早在批 2 就已登记在 id_aliases.json** - 所以照原计划改等于**销毁历史记录、收益为零**。真缺口是判据:追溯器是唯一 没有「变更记录 / [示例] / [计划] 行豁免」的一支(check_ids / rename_ids / doc-links 都有),它把冻结历史当活引用 → 系统性假阳性 改了什么(判据,不改数据) - check_test_traceability.py 加 SKIP_LINE(与另三支脚本同源的豁免正则),两侧采集 都改为逐行判定,被豁免的号收进 frozen_ids 留痕,--verbose 全量列出—— **豁免不等于已对应**,别拿它当收敛 - 顺带把 has_intent() 的判定范围从整文件收窄到**同号所在行**(意图必须写在号旁边, 原先整文件里任何位置有人话就算过关,太松) 实测(关键是"没吞掉真对应") - DOC-ONLY 38 → **29**(−9 全是冻结行假阳性,**不是真变好**);总差异 45 → 36 - **交集 260 → 260 不变**:豁免只吐冻结行,没有让任何一条真实对应消失 - 代码侧 578 个号不变(识别面没缩,这是改名/改判据最容易踩的坑) - 错误级 632 不变(追溯不参与错误级计数);可判定比例 83.02%;--strict 通过 剩 29 处是真问题,但**不是编号问题**(要人裁决,不由门禁决定) - 4 处无模块码形态(TC-1003 / TC-206 / TC-501 / TC-704):② 大批的文档侧镜像 - TC-NET-607 一条是历史指称(原文「原 TC-NET-607/608/609,现 701/702/703」), 一对多写不进一对一的别名表 - TC-NET-302/401/402/501 标题明写「(硬件层)」= 人工台架步骤,代码无载体是预期 - 其余 20 处(TC-NET-101/102/1101/601/606/701~704/801/802/901/902/1006/1501~1503、 TC-GD5F-503)**全库无任何载体**:文档声明了用例而代码/台架/模拟器都没有实现 另一个我自己的错误,记进 §8.3 而不只是提交信息 - 补别名时把 8 个键写到了 JSON **顶层**(真容器是 aliases,515 条)→ 条目完全无效; 是 assert k not in d['aliases'] 拦下的,删掉无效键后 id_aliases.json 字节与 HEAD 一致 - 教训:DOC-ONLY 这类两侧字面比对差异,先判它是不是冻结行/别名可解析,再谈改数据; 改名工具 0 命中与 assert 失败都是**信号**,不是小故障 文档:docs/编号规范.md §8.1 口径修正记录 + §8.3 批 5 行与经过段(含被推翻的判断)+ 变更记录; docs/代码规范/pre-commit使用说明.md §2.25 顺带收编行同步;脚本 docstring 同步 验证:check_doc_links.py --all 143 篇 0 失效;id-check --strict 通过;三个改动文件 BOM=否、CR=0;未改 C/H,不需 @build;台账无需收账(本轮 0 处销账) |
||
|
|
52976bdec3 |
refactor(编号): 批 4 陷阱号统一为 TRAP-<模块码>-<nn>
本批是 ② 形态迁移清的第一族(用户裁定顺序:② 里的 Trap 一族先做) 统一了什么(数字不变、只补模块码,§7.1)——改名工具 23 处 / 11 文件 - Trap 01~07 → TRAP-GD5F-01~07:属主文档 7 个标题行从此成为定义位 - 陷阱 1 → TRAP-RTC-01(SD2506 属 RTC 域,§4.2)、见陷阱 01 → TRAP-GD5F-01 - Trap 21 / Trap 19 → TRAP-NET-21 / TRAP-NET-19(引用的是活登记处 CH395F_Trap_Records.md,号本身不变) - 波及 Drivers/BSP/GD5F2GQ5UE/*(4 处注释)与 test/board/*(2 处)→ 已重编 三处判读后**刻意不改**(写进 §8.3,不让人当成漏网) - docs/环境与部署/并行任务工作树方案.md 的 wip/trap22-fix 是 git 分支名,不是编号; 这类非编号字面量要靠类型处理,与位号 T1、IEEE 负序 S2、gitlint 规则 ID 同一批 - docs/UI设计/菜单页面设计.md 变更记录行的「陷阱 3」按 §8.1 冻结不改写 - docs/硬件平台/V0.0、V0.1 网表报告里 6 处 Trap05 语义确指 TRAP-NET-05,但该目录 是门禁与改名工具共同的排除语料 → 不越界动笔,盲区登记进注册表 TRAP 族 cursor_note 两处不是改名、是改错话(映射表类工具的典型失效:字面对了、意思错了) - scripts/README.md 原写「Trap 19 从未存在」;改名后字面变成 TRAP-NET-19,而它是 **真实条目**(SET_MAC 必须在 INIT_CH395 之前)→ 那句话成了假话,改用站得住的例子 - docs/编号规范.md 同一处事故叙述补准确说法:当年裸形不在册 ≠ 这个坑不存在 - 菜单页面设计.md 的「见 §8.2 陷阱 1」本就不是 TRAP-* 族的号(指本文小节第 1 条), 改写成「见本文 §8.2 第 1 条(lv_text_get_width 的 length=0 陷阱)」 关闸第一次真拦人:咬的是自己家的审计表 - scripts/rename_maps/batch3-trap.json 里必然写着 Trap 21 / 陷阱 1 等旧形态, 被当成 6 类「台账外新账」→ 提交被拦(拦得对,但表不是语料) - 第 4 次同一类自循环(前三次:别名表、注册表、债务台账)→ 整个 rename_maps/ 目录入 excluded_paths;这条规律写进 §8.4:**登记号的表一律不得当引用语料** 实测变化 - 错误级 659 → 632(−27);② 空格/中文形 Trap 残余 2 处(都是上面刻意保留的) - ③ 悬空错误 44 → 42:TRAP-GD5F-01/05/06 三处历史悬空引用自动落地为已定义 - 编号字面量 8277 → 8312(识别面没有缩水,这是改名最容易踩的坑); 可判定比例 82.92% → 82.99%(基线 82.6%);⑦ 定义无引用 100 → 103(如 TRAP-GD5F-02 只有定义位无人引用,如实挂 warn 不压掉) - 台账 --prune-backlog 收掉已还清 42 条,剩 298 条(只减不增);--strict 通过 另修:core.autocrlf=true 把 SD2506_Trap_Records.md 检出成 CRLF(41 处)→ 还原 LF, 该文件 diff 只剩 1 行实质改动(仓库权威行尾是 LF,见 .gitattributes) 验证(全绿) - @build:0 错误 0 警告(Program Size Code=213356 RO=18580 RW=1184 ZI=131480) - ci_local.py:L0 + L0.5 全过(16 runner / 1104 断言) - check_doc_links.py --all:143 篇文档 0 失效、0 待确认 - 改动文件字节卫生逐项与 HEAD 对比:BOM / CR / U+FFFD 异常 0 审计轨迹与文档 - 映射表 scripts/rename_maps/batch3-trap.json(含作用域切分理由与刻意不改清单) - 别名 11 条全部带 scope;docs/编号规范.md §7.1 行状态、§8.3 批 4 行 + 说明段、 §8.4 自循环第 4 条、变更记录 |
||
|
|
5a9988b557 |
chore(注释): 清掉源码里不可检索的过程编码
清掉的(读者无从解析、且所指文档已不存在) - 已删任务文档的编号:Task #32、其内部 §5 / §4.3、P1-5、P0-2、P1-4a、 "方案 a"、"Lead 批准 2026-10-12" - 正文里当代词用的文件版本号:"(v2.7 起为 AboveNormal)""已随 v2.0 事件 驱动改造删除""(v2.0:DMA 消费)""见文件头 v1.3 说明"等 → 改写成陈述现状 - 测试与脚本同类:adc_input 桩头"v2.0:DMA 模型"、stub/adc.h 的 v2.0、 check_cubemx_survival.py 段头的"2026-10-10 v2.8"、run_all_suites.py 的"方案 A" 刻意保留 - 可检索登记簿锚点:P-AUDIT 真条目(P0-1/P1-8/P2-1/P2-9/P3-11)、S6/S11、 E-31/E-44、N-18/N-23/N-32、B1/B5/B7/B8、L1-x~L5-x 功能号 - 文件头修改记录块内的 vN.M(规范 V1.0 §2.1/§2.2 要求保留该块;块内编号 在本文件内自洽可读) 顺带纠正注释与代码已经不一致的过时事实 - 仍把 TIM6 写成现行节拍源:Src/main.c 的 include 注释与回调注释、 Src/freertos.c 的 adcTask 块 → 改 TIM3;adc_input.h 修订条目里"ADC1 只认 TIM2/TIM3/TIM8 的 TRGO"不准确 → 换成指向判据(ADC采样节拍设计说明 §3.2) - App/algo/phasor.c 双区注释称"唯一消费者是 adcTask、为后续保护任务预留", 实际 protectTask 已跨上下文读(prot_elm_run -> phasor_get)→ 改为"读者跨 adcTask 心跳与 protectTask 判据两个上下文,勿改加锁、勿取消双区",并同步 修正 docs/算法/采集与算法层设计说明.md 同一句 - adc_tick_irq_notify() 的"拒绝 ISR 内计算"理由仍写"内含 2 ms 忙等"(DMA 化 后已无忙等)→ 换成三条真实理由:任务级共享数据 / 计算量 / 生成区回调 - 裸 §5(指代不明)与"规划 §1.6 / 规划 §4"昵称式 → 写成完整路径紧邻式 字节卫生(Edit 会静默剥 BOM) - App/task/ui_task.c 与 Drivers/BSP/UI/ui_net_sink.c 被剥掉 BOM 两次,均按 3 字节 EF BB BF 前缀补回并复验;全部 20 个文件 CR=0、U+FFFD=0、无行尾空白 门禁 - @build 0 错误 0 警告,且 Program Size 与清理前逐项相同 (Code=213356 RO=18580 RW=1184 ZI=131480)→ 纯注释改动、中文未被误重编码 - ui-golden 首跑红:新工作树缺被 gitignore 的黄金参考 simulator/tests/ref/ (不入库、只在主工作树生成过);从主工作树复制该基线后复跑通过 → 注释改动 零像素影响 - 全套 pre-commit 在这 20 个文件上 Passed(coding-standard / cppcheck / 6 个主机单测 / doc-links / task-table / mem-numbers / cubemx-survival / registration-check / mem-budget / font-charset / ui-golden / id-check) - id-check 可判定比例 82.71% -> 82.88%(删掉的是"形似 P-AUDIT 但查无定义"的 假锚点),悬空引用 863 -> 854 |
||
|
|
858434492c |
feat(门禁): id-check 关闸——按债务台账增量拦截,warn-only 时代结束
机制(--strict 不再「有错就失败」,而是只拦台账外的新账) - scripts/id_known_backlog.json:340 条指纹覆盖 710 处存量(②167 类 / ⑥135 / ③38) - 指纹 = (文件, 校验号, 字面量) → 计数,故意不含行号:行号每改必漂, 用它当指纹会让台账天天假新增,三天后没人敢看 strict,台账失效比没台账更糟 - 带计数而非集合:同一文件里同一个号多出现一次也算新账(复制粘贴式扩散 正是编号烂掉的主路径,只按「出现过没有」判会放过它) - 新增 --write-backlog 与 --prune-backlog(只减不增;台账内条目变多当场点名); --strict 缺台账返回 2,绝不退化成一次性拦死存量;--only 与 --strict 互斥 - 钩子 entry 加 --strict;--only 与 --strict 互斥 - 顺带修 _scan_malformed 的反向 bug:原条件 (skip and name != "空格形 Trap") 语义正好写反,带 [示例] 的行反而专被 Trap 规则咬;改为示例行全部不判 - 第一次提交就被自己的台账咬住(id_known_backlog.json 里当然写着 P1-3 等号, 被当成新引用)→ 台账加入 excluded_paths:登记号的表不是引用号的文档, 与 id_aliases.json / id_registry.json 同一条道理 为什么不照「清零再升 strict」的原节奏(§8.4 详述) - 存量 710 处清完要数周,这期间新债一条都不拦,而新增才是烂掉主因 - 「等清零」让人习惯「报红但照样提交」,真关闸那天没人再信门禁; 本仓已有 mem-budget / mem-numbers 走 --no-verify 的先例,不再开第二个口子 关闸验收三例(临时件当场还原、git diff 干净) - A 全新前缀 Z-99 → exit 1 点名 ✅ 咬 - B 同号计数 1→2 → exit 1 点名 ✅ 咬 - C 干净态 710 处 → exit 0 放行 ✅ 放(放不住存量就会逼出 --no-verify) 验证:--strict 干净态 exit 0;可判定比例 82.74%(基线 82.6%); doc-links 失效/待确认 0;未改 C/H 不需 @build。 文档:编号规范 §8 上线节奏改写 + 新增 §8.4 机制表;pre-commit使用说明 §2.25; AGENTS.md 门禁行由 warn-only 改为已关闸增量拦截。 |
||
|
|
d74cd030ea |
fix(门禁): 追溯脚本与 B 层口径对齐 + [示例] 行豁免写反的 bug
为什么改(第 1 件,用户裁定的顺序)
- B 层已认 test/host/<模块>/* 为 TC 号属主登记处,但 check_test_traceability.py
仍按老规矩要求"每个号都必须出现在文档" → 两处判据互相打脸
- 实测矛盾面 284 处里有 173 处属于该口径:照老口径升 --strict 会当场拦死这些
本不该写文档的号,唯一出路是 --no-verify——门禁公信力就是这么丢的
追溯差异改为三档(口径与编号门禁一致)
- DOC-ONLY:文档声明了、代码没有 → 真断链,38 处
- CODE-ONLY:代码有、母号不在登记处、**且号旁边没写人话意图** → 真未登记,7 处
- IN-CODE:靠"号旁写着意图"放过 → 238 处,**弱于文档的保证,单列示数不算过关**,
对外举证仍需 C 层模块级说明(脚本里写明这条边界,禁止把它读成"已登记")
- 小写子项后缀(TC-TIMESYNC-003b)按母号算,与 check_ids 的 subitem_ok 同口径
- 真差异从 284 降到 45(38 + 7)
顺带修两个自己的 bug
- TC_RE 模块码上界停在 {0,4}:批 2 模块码单词化(ADCHEALTH 9 位)后代码侧集合
从 556 悄悄缩到 217(339 个号隐形)→ 放宽到 {0,8}。**同一个坑第二次踩**,
第一次是 check_ids.py 的候选正则——改名必须复核**所有**识别面,已写进 §8.1
- _scan_malformed 的豁免条件写成 `(skip and name != "空格形 Trap")`,语义正好反了:
带 [示例] 的行反而专门被 Trap 规则咬。改为示例行**全部**形态规则不判,
并给引用示例号的两行文档补 [示例] 标记
验证
- 编号门禁:错误级 712 → 710,可判定比例 82.73%(基线 82.6% 未跌破),校验 1/4/5 仍 0
- 追溯:代码侧 578 / 文档侧 298 / 交集 260 / 差异 45;--board-only 回退口径不变
- check_doc_links.py --all 文档路径与章节引用均通过;字节卫生复查 BOM=否、CR=0
- 未改 C/H,不需 @build
文档:docs/编号规范.md §8「顺带收编」条目改写 + §8.1 口径修正记录补两行 + 变更记录;
docs/代码规范/pre-commit使用说明.md §2.25「顺带收编」行同步三档口径
|
||
|
|
4b9a4f4e24 |
refactor(编号): B 层口径落地——主机侧单测认代码为属主登记处
为什么不是"补 7 篇文档" - 实测这些号在代码目录外**零消费者**(NANDPORT 47 / PROTELEM 13 / PROTSET 11 / FREQ 11 / FTPAUTH 10 / SHA256 8 / SOE 7 / ADCHEALTH 11 / IEC104 23,共 141 号), 唯一"外部引用"是 scripts/id_aliases.json 自己 → 逐号写文档等于造一份没人读、 还要跟着代码同步改的复述层(违反"同一事实只在一处详述") 判据与注册表改动 - 新增 9 族 TC-HOST-<模块码>:class 仍为 TC(跨模块同号照旧由校验 4 抓)、 owner_docs_glob = test/host/<目录>/*、每模块游标做号段不越界(含 IEC104 补漏: 它上批改名前是纯数字 TC-104-*,没进当时的悬空普查所以漏建族) - 新旗标 allow_orphans(不补该旗标会让 141 号全变孤儿告警)、 subitem_ok(小写子项后缀母号在册即算已定义,差异仍由双向追溯如实报) - snapshot_only:§4.3 冻结快照族不参与"定义位优先"——先试无差别生效时,已销项的 登记簿 N-21 被审计报告同号接管,校验 6 从 266 暴涨到 1437、可判定比例假崩 68.7%, 且提示会教人补错文档名(正是 §1.2 的 N-31 撞号制造方式),故回退并改为快照族豁免 - excluded_paths 加 scripts/id_aliases.json 与 scripts/id_registry.json: 别名表/注册表自身不得制造引用面 - T-2 / T-3 / T-4 / T-6 入别名表标 void(2026-10-02 复审专项条目,销项只删行没清引用) 修自己的漏配(这条是 bug 修复,不是口径) - N / S-LDG / T-LDG 族漏登记 docs/任务规划/README.md(活登记处,N-21 在其中出现 2 次), 只登记了 docs/任务规划/登记簿.md(N-21 为 0)→ 74 处真条目被误判 数字与诚实交代 - 错误级 1318 → 711(②398 + ③44 + ⑥269);可判定比例 82.74%(基线 82.6%) - ⚠ 其中 504 处属**口径修正**、74 处属**修 bug**,**真正变好 0 处**;已在 docs/编号规范.md §8.1 写口径修正记录表,禁止拿 711 与 1318 直接比 - 文档:§4.1 补 TC-HOST 说明、§8.1 口径修正记录、§8.3 批 3 行、变更记录; pre-commit使用说明 §2.25 判定口径与最新实测同步 - 未改任何 C/H,不需 @build;提交钩子即 L0 + L0.5 全量验证 |
||
|
|
bda7ab2b50 |
refactor(编号): 16 个测试模块码单词化 + 门禁识别面连带放宽
批 2(用户授权改名 + 「新名要一眼看懂」裁定)
- 16 个有歧义模块码单词化,共 1,146 处随 scripts/rename_ids.py 改写:
AI→ADCIN、AH→ADCHEALTH、PS→PROTSET、PE→PROTELEM、PH→PHASOR、
TS→TIMESYNC、VL→VOLLOCK、NP→NANDPORT、CFG→CONFIG、CLK→SYSCLK、
WD→WATCHDOG、STO→STORAGE、SH→SHA256、FA→FTPAUTH、FR→FREQ、104→IEC104
- 不动 NET/GD5F/RTC/SOE/UI/ADC/LOG/RS485/TOOL(已是单词、芯片名或业界通用词,
改了纯属 churn)
- 别名按全局登记:这批字面量已完全限定(TC-<码>-<nnn>),不存在裸形多义
- 映射表存档 scripts/rename_maps/batch2.json 作审计轨迹;§6 模块码表同步
连带修门禁(关键教训)
- 模块码涨到 9 位(ADCHEALTH/WATCHDOG)后,候选正则与族正则的 {0,4} 上界
会让改名后的号**整批从门禁眼里消失**:实测编号字面量从 8271 掉到 7315、
悬空假降 490 处。已放宽到 {0,9} 并写进 §6 注记
- 结论:改名批次必须连带复核识别面,只改数据不改判据 = 静默放行;
这次是靠「可判定比例跌破基线即失败」那条线拦下来的
现状与遗留
- 校验 1 未登记前缀 0 处;可判定比例 82.71%(基线 82.6%,只涨不跌成立)
- 遗留:7 个模块的「单元测试说明.md」仍缺(PROTSET 116、NANDPORT 109、
PROTELEM 65、FREQ 48、FTPAUTH 45、SHA256 25、SOE 14 处悬空)——改名不解决
缺文档,处置方式待议
- 遗留:T-2/T-3/T-4/T-6 历史任务号悬空(别名表未收全,属本工具批次漏项)
- 未改任何 C/H 可编译语义(仅注释与宏名内的编号),故不需 @build
|
||
|
|
301c3e5183 |
feat(门禁): 编号门禁 id-check 落地(36→37 钩子)+ 批 1 编号单词化改名
新增门禁本体 - scripts/check_ids.py:实现 docs/编号规范.md §8 八项校验;默认 warn-only, 唯一硬拦项是「可判定比例跌破基线」(防回退的本体) - scripts/id_registry.json:66 个前缀族,逐项来自 §4 总表 + 全库实测游标, 偏差一律写进 cursor_note,不凭记忆编造 - scripts/id_aliases.json:旧号 → 新号 / 作废标记(照搬 section_aliases.json 范式) - 顺带收编 check_test_traceability.py:正则补齐大写后缀与无模块码形态, 比对范围扩到 test/host 与 7 篇「单元测试说明.md」,另加 --board-only 回退旧口径 批 1 改名(用户授权解禁「不改写存量号」) - 代码规范条款 S1~S19 → CODE-RULE-nn:78 处 / 2 文件 - UI 像素回归状态 S0~S7 → MENU-STATE-nn:61 处 / 3 文件(冻结在 07) - 两族属主登记处 = 脚本自身的规则表 / STATES 表,文档只作引用方 - 新增 scripts/rename_ids.py(§7.3 规划脚本):映射表驱动、显式 scope、默认干跑、 自动写 scoped 别名,跳过 [示例]/[计划] 行与变更记录表,按原字节读写保 BOM 行尾 - 已应用映射表存档 scripts/rename_maps/batch1.json 作审计轨迹 有牙实测(证明门禁不是空转) - 注入 Z-99 / 裸形 / 悬空 / 越游标 / 未紧邻 / 跨文档重复定义 六例, 分别被校验 1/2/3/5/6/4 点名,--strict 退出码 1;临时档删除后工作树干净 验证与同步 - 可判定比例基线 82.3% → 82.7%(已 --update-baseline 入档);错误级 1312 → 1291 - ci_local.py 全绿(L0 + L0.5 共 1104 断言);ui-golden Passed; check_doc_links.py --all 无法判定 0 - 未改任何 C/H 源文件,故本次不需 @build - 文档:AGENTS.md 与 pre-commit使用说明.md 计数 36→37 并新增 §2.25 详解; 编号规范 §4.1 补两行、§8.1 落地记录、§8.2 规范与仓库偏差、§8.3 迁移批次记录 |
||
|
|
64abefaa00 |
refactor(UI): U2 合并运行/自检记录两页为共享组件 ui_w_records
ui_page_runlog.c 与 ui_page_selftest.c 各 102 行,逐行比对后差异全是命名:
唯一的功能变量是日志 ID(SYSLOG_RUN / SYSLOG_SELFTEST),计数、每记录 2 行、
同记录读盘缓存、空态伪行的实现两边逐字相同。是真重复,不是推测出的抽象。
## 落地
新建 Drivers/BSP/UI/ui_w_records.{c,h}(已在 uvprojx + CMakeLists 双端注册;
生成器只自动管 ui_page_*.c,故这两处手工加)。两页各留一个实例与两个薄跳板,
.count_cb/.refresh 名字不变 => 注册表与生成器零改动。
净效果:两页 90 删 / 28 增(−62 行),重复逻辑收成一份。
## 合并唯一的真陷阱:缓存归属
count_cb/refresh 签名里没有 user context,最容易写的版本是把
s_cache_valid / s_last_rec / s_ts / s_desc 留成组件级静态——那样两页共用一份
缓存,从 A 页切到 B 页会因记录号相同而直接命中缓存,显示另一页的陈旧行,
是静默错数据而非崩溃。故缓存内嵌进实例(ui_w_records_t),每页各一份。
## 验证
@build 0 错 0 警;golden 经真实 ui-golden 钩子跑通:菜单 8 状态 + 内容页
11 页(含这两页)逐字节一致,未用 --update 刷基线 => 语义搬迁行为等价有证。
ER_IROM1 232,052 → 231,936 B(−116 B)。RW_IRAM1 75,232 → 75,240 B(+8 B),
原因如实记录:原先 4 个文件级静态零初始化落 ZI,现在 s_rec 带 .id 初值整个
结构体落 .data(RW),两页合计净 +8 B;要压回 0 需把 .id 留零再加 init 钩子,
为 8 字节引入运行时初始化顺序不值得,故保留并把账写在此处。
stack_report FAIL 行数 0(间接层未顶破栈预算);mem-budget / mem-numbers 已
按 @build 后规定回填台账。
## 过程中的两处自我失误(已修复,记录以免重犯)
① 用 stem[:2] 生成函数前缀,对 selftest 得到 se_ 而非 st_,锚点失配。
② 撤销脚本里 ${UI_DIR} 被 PowerShell here-string 插值吞掉,导致"边校验边写"
把树留在引用组件但未注册的链接错误态。教训:回滚脚本必须比正向脚本更保守,
全部校验通过后才允许写盘。
|
||
|
|
44dec5c49b |
feat(UI): golden 像素回归入提交门禁(W6 U4 第三件,35→36 钩子)
补上我上一轮自己标记为 2/3 的那块缺口:两个 golden 脚本早已存在且一直在
人工跑,但**从未进自动门禁**——"有基线"不等于"有门禁"(§6.1 原文即自认)。
## 先实测成本再定形态
改一个 UI 源文件后「增量重编模拟器 + 两条 golden」合计 **2~4 秒**,支持走
自动门禁。若实测要几分钟,正确做法是明确不挂、改为文档化人工门禁——一个
逼人 --no-verify 的门禁等于没有门禁,故这个数字是本决定的前提而非附注。
## 落地
新增 scripts/check_ui_golden.py:串「增量构建 → 菜单 8 状态 → 内容页 11 页
+ 池水位」。**有意忽略 argv 且 pass_filenames: false**——两条 golden 按整棵
菜单树 + 全量参考图工作,不接受文件参数,pre-commit 传的变更名对它无意义。
files: 覆盖 UI 源 + ui_task.{c,h} + 模拟器 tests/sim/src + main.c +
CMakeLists + lv_conf.h + 包装器与本配置自身(pre-commit 4.x 无 always: true,
files 是唯一触发手段)。
## 端到端验的是钩子,不是脚本
干净树连跑 3 次 rc=0 全 Passed;给 ui_page_dc.c 的 .top_y 注入 +5 位移 →
**钩子 Failed** 并点名 `[dc] FAIL 与参考页不一致(1882 px 差异)`;还原后
git diff 干净、simulator/tests/ref 的 git status 为空——**全程未用 --update
刷基线**(那正是本钩子要防的行为)。
诚实记录瑕疵:首次实测在"连续注入/还原的紧邻重建"之间偶发失败一次,疑似
MSBuild 节点争用,此后 3 次连跑均绿。已把"单次失败先复跑、连续两次才当真、
不许直接 --no-verify 绕过"写进钩子注释。
## 文档同步
AGENTS.md 钩子数与文末索引 35→36 + 钩子清单补 ui-golden;
pre-commit使用说明.md 概览计数同步;报告 §6.5 U4 由 2/3 → **3/3**(这次是
"真进了自动门禁",不是"脚本存在")、§6.1 golden 行转 ✅、§0.1 缺口描述更新。
## W6 剩余
U2 合并两页记录、U5 落地目标页(须先对齐"目标页 vs 现存 7 个 ui_page_*.c"
的差集——报告的"27 页"是目标数而非现状数)。
|
||
|
|
dafc18f5ed |
feat(UI): W6 第一波——设计令牌层、页面注册表生成器与 UI 门禁补强
启动 docs/任务规划/README.md 的 W6 UI 扩展簇(用户裁定,覆盖 2026-10-11
的"本轮不排期")。四个工作包,全部验证通过。
## U1 设计令牌层(零行为变化)
新建 Drivers/BSP/UI/ui_tokens.h(20 个宏),框架 + 7 个页面 + App/task/
ui_task.c 全部接入。屏幕几何**复用 ui_port.h 既有 UI_SCREEN_W/H**,不重复
定义;令牌头自身只 include ui_port.h,不把 LVGL 拖进全部消费者。
补收了报告没列但代码里真重复的量:UI_TEXT_BAND_H 15(7 次)、列表左右边界
与每页行数(各 4 个页面逐字复制)、推流协议几何与显示几何同源(此前硬编码
3200 且注释写着"须与 UI_FB_SIZE 一致"——典型漂移隐患)、UI_TASK_TICK_MS
(ui_port_step 与 osDelay 必须同值,原先靠注释维系)。
故意不收页面私有几何与"同形不同义"的值(device_info 的 pitch=15 与列表
UI_ROW_PITCH=14 合并即改像素)。
## U3 页面注册表与生成器(删手工分发)
ui_page_registry.{c,h} 由生成器产出,**手工 menu_content_page_for() 已删
除**(仓库内仅存 2 处历史说明注释)。密码页按代码实况**单独出口**
(ui_page_registry_password())——它非节点绑定,任何 #P 叶子进同一页。
顺带落地此前判定"U1 阶段做不了"的共享 flat 样式 ui_style.{c,h}。
生成器除产注册表外**自动把新 .c 写进 uvprojx 与 CMakeLists.txt**,且
**双向对账**:加页打印 registered、删页打印 pruned,不留死条目;并双向
校验(#page 找不到实例报错、实例未被任何 #page 引用也报错)。
## U4 门禁补强(35 个钩子)
新增 font-charset 钩子(scripts/check_font_charset.py):复用
tools/font_gen 的同一套提取与 cmap 解析规则(importlib 直载,零第二套
规则),A 段查源码在用 vs 字符集、B 段查字符集 vs 字体 cmap。
registration-check 补第 ④ 类:实测原规则对"进了两端构建但没接描述符表"
的页面**全绿**——desc 被链接器剥离、编译链接全过、菜单永远打不开的死代码。
仍照不出的两项已如实写进文档(枚举漏加归 @build、菜单树漏挂归 golden)。
## 字体子集瘦身(D 案,4 留 3 删)
ui_cn_chars.txt 122→119 字(恰删 上/电/重,零新增)。异/常/片/芯 进
KEEP_EXTRA 带理由白名单:它们曾在
|