Files
STM32F4-Base/docs/门禁说明/nested-comment.md
T
wandering1 d64bbdc57f feat(wave): 故障录波数据面、接线与单通道回放页(软件闭环交付)
数据面(App/algo,纯逻辑,双端编译)
- wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask
  唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃
  兜底——下一次真故障无波可录比丢一段更贵
- wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带
  锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度
- 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式
- 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样
  纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉
- test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条
  自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合
- 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定)

接线(任务层)
- adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图),
  段头按最低有效位精确记账,不经浮点回算
- adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实;
  台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行
- defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到
  关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms
  补一条告警并记历史峰值(喂狗余量可核)
- Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留
  超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的,
  半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进
- Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到
  上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数;
  段文件字节布局零改动,三份合成段哈希逐字节不变
- protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体
  零改动

人机(160×160 单色屏)
- 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器
  得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30
- 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与
  垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、
  单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据
- ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、
  手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口
- 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有
  60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形
- 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变
- 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏
  22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、
  仪器用已在跑的单轮工作耗时探针,不新加埋点

台账与文档
- 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936
  字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名;
  散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建
  重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置)
- 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据);
  §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径
  风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」
- 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项
- 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划
  索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动)
- 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件
  形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记
  「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场
- 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行
  227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退

为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与
体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账
同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道

编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串
生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
2026-10-29 19:23:15 +08:00

8.6 KiB
Raw Blame History

块注释嵌套(nested-comment)— 注释体内不得再出现 /*

一句话:C 的块注释不嵌套。注释体内再出现一个 /* 时,ARMCC 报诊断 9-D 「nested comment is not allowed」。本仓的触发形态极其日常——在注释里写作用域或通配路径。 脚本 scripts/check_nested_comment.py(纯标准库、只读,2026-10-27 接线)。

1. 它在查什么

逐字节状态机走 代码 / 行注释 / 块注释 / 字符串 / 字符字面量 五个状态, 只有在块注释体内看到的 /* 才算问题,报「文件:行:列 + 上下文指纹 + 修法」。

判定 形态(本仓真实写法或自测用例)
算 注释里写路径通配:Drivers/BSP/UI/**、test/host/**、App/algo/*(那串里的 /* 就是第二次注释的起点)
算 块注释内部某行行首又开一个注释(复制粘贴手滑);块注释里用引号包住 /;块注释里行注释符之后再来 /
不算 字符串字面量里的 /*(const char *s = "/* 不是注释 */";)
不算 行注释里写 /*(编译器不报,本闸也不报)
不算 本仓既有的尖括号写法 <Drivers/BSP/UI/ui_w_soe.c>(推荐改法)

2. 为什么要它

  • 只在重编译那个 TU 时才暴露:改了某个头里的注释,只有编到它的包含者才看得见; 历史「0 错误 0 警告」不代表现在这份是干净的(与 bom-guard 完全同形的坑)。
  • 按包含者数量刷屏:一处嵌套写在被广泛 include 的头里,每个包含者各打一行。 本仓实测一例扩散到 16 条警告,把同批 10 条真警告挤到看不见—— 等于把「能不能看清报错」这件事本身弄坏。
  • 本闸不需要构建:提交时就报,不必等 @build,也不必去猜是谁的编译单元。

2.1 接线当日实测(都可复现)

测项 结果
全仓 --all 实扫 403 个 .c/.h:新发 0、存量 2 处(都进基线,见 §7),退出码 0
形态电池(10 项,逐条对齐期望命中数) 10/10:5 个真形态全照出,5 个不该报的全安静
--selftest 四项 4/4:正例照得出 / 反例不误报 / 基线吞得下 / 只减不增有牙
真字节回放(空基线) 把 §7 那两份假件头当「今天新提交」跑 → 两条 [NEW]、退出码 1
同一批真字节 + 已登记基线 两条 [BASE]、退出码 0(基线只决定「算不算新的」,不决定「有没有」)
形态对照(重建当日那一处) 尖括号写法 → 0 命中;同一段退回星号形态 → 1 命中,列 29
已提交历史回放 近 150 个提交里被改过的 .c/.h 共 281 份旧字节:0 命中 ⇒ 这类问题此前从没被机器拦过,全靠人工在编译后清

编译器侧原文(冻结引用)——2026-10-27 02:22 那次 @build 的构建日志:

..\App\algo\run_par.c(267): warning:  #9-D: nested comment is not allowed

⚠ 该日志每次 @build 都被覆盖、且不入库(02:37 已被下一次构建清掉,我写完这段再去查就没了), 所以把原文抄在这里存档;当日那三处同类没有一处在已提交历史里留下字节(上表最后一行), 本闸真正第一次拦住,会是「下一次有人在注释里写星号形态」。

3. 触发面

钩子用 types: [c](即 .c/.h),第三方与生成物由配置顶层 exclude 挡掉。 脚本的 --all 与钩子喂进来的文件集刻意同口径(2026-10-27 实测 403 个文件)—— 手跑面与提交面不一致,就会变成「两个数」,本仓已经为这种事返工过好几轮。 纯文档(.md)或纯 Python 的提交不会命中本闸——types: [c] 只喂 .c/.h 两类文件,想确认全仓干净就手跑本节的自查命令。

单独自查:& .venv\Scripts\python.exe scripts/check_nested_comment.py --all

4. 报错怎么读

[NEW ] App/x/y.c:45:17  块注释体内出现 /*(出现 1 次同形)
       上下文:件才能测。 * * ⚠ 仅供 test/host/** 使用,不参与 MCU 构建。 * * 为
       修法:注释里别写 /**(C 块注释不嵌套)——路径写全目录就好(Drivers/BSP/UI/),
             或按本仓既有做法用尖括号包住(<Drivers/BSP/UI/ui_x.c>)
  • [NEW ] = 基线之外的新命中 → 拦提交(退出码 1)。
  • [BASE](要加 --verbose 才逐条打印)= 存量在册,不拦(见 §7)。
  • [TIGHTEN] = 某条基线本轮实测已还清,该删了。
  • 退出码:0 通过或只剩存量;1 有新命中;2 用法或环境错误。

5. 好处

  • 秒级、零第三方依赖(只用标准库)。这点是刻意的:generated-fresh 那天栽在 「提交环境里裸 python 不是 .venv、装不到 fontTools」,本闸不碰第三方库,任何解释器 都跑得动——不会再造一根依赖某台机器环境的闸。
  • 把「注释里写路径」这种看起来人畜无害的写法变成机器可判,而不是靠 reviewer 眼力。

6. 限制与盲区

  1. 只管「块注释体内出现 /*」这一件事。不判注释是否闭合(未闭合是编译错误,编译器自己 会拦,不需要一根只读闸替它操心);不判行注释里的 /*(编译器也不报)。
  2. 不做预处理:若把 /* 藏进宏展开里(本仓没有这种写法),本闸照不到。
  3. 基线指纹按内容不按行号——存量那几行改了行号仍算在册;反过来,同一文件里换一个新 形态会算新发(这条有专门的自测项)。
  4. 只覆盖 .c/.h。Python 生成器模板将来若写出 /,本闸照得到产物**(产物是 .c/.h), 但不会指出模板——那段链由 generated-fresh 保证「脚本与产物同一笔提交」。
  5. 编译器侧的真实警告仍以 @build 为准:本闸只拦「会让 ARMCC 报 9-D 的那一类」, 编译日志里其他诊断一概不管。
  6. 仓库根探测必须兼容 linked worktree:脚本原先用「.git 是不是目录」定位仓库根,而 git worktree 出来的任务工作树里 .git 是一个文件(写着 gitdir: …)不是目录 → 本闸在任何任务树里恒 退出码 2「仓库根定位失败」,静默不可用。这不是「放过了真问题」,是根本没跑——比放过更坏, 因为调用方看到红字会以为环境有问题、看到跳过会以为干净。2026-10-29 已把该判据从 isdir 改成 exists(scripts/check_nested_comment.py:342,修复归当日提交)。全仓 scripts/ 只有它这么探根, 其余只读闸在任务树里一直正常。后果要说清楚:门禁修好之前那批录波改动没被它扫过,lead 手工对 改动面逐文件扫了一遍、嵌套命中 0,所以没有放过真问题——但那是人工兜底,不是门禁功劳。 通用教训:门禁脚本按 isdir(.git) 探根 = 在 worktree 里自杀;新增只读闸要么走 git rev-parse --show-toplevel,要么同时接受 .git 的文件形态。

7. 豁免与基线(只减不增)

接线当日全仓 --all 实扫 403 个 .c/.h:新发 0、存量 2 处,都在主机侧假件头里:

文件:行 形态 为什么今天不报 还法
test/host/common/ch395f_fake.h:7 注释里写作用域 test/host/** 该假件不参与 MCU 构建(换 GCC/clang 的 -Wcomment 就会报) 改成 test/host 目录 这类语义短语
test/host/common/fatfs_fake.h:9 同上形态 同上 同上

两处都属 test/ 写域,接线时不由本道代改,按既有范式钉进脚本内 BASELINE, 除这两处外零豁免。一旦有人把这两份假件 include 进板端编译单元,就会按包含者数量刷出 9-D——这正是基线要显式挂着的原因:不是「没问题」,是「已知待还」。 还清一条就删那一行(--prune 打台账并点名已还清的)。

8. 改这套门禁本身

  • 加判定形态必须同时加正例与反例:--selftest 四项(正例照得出 / 反例不误报 / 基线吞得下 / 只减不增有牙)之外,另有 10 项形态电池逐条对齐期望命中数。
  • ⚠ 配置文件注释里不要复述声明行的字面量:接线当天我在 .pre-commit-config.yaml 的 注释里写了一遍那个 grep 模式,文本 grep 的计数当场虚高 1(口径与自查命令见 docs/门禁说明/README.md §2 首条)。数数交给 YAML 解析,注释只描述。
  • 加/删一条钩子要同批改三处:AGENTS.md 的钩子数、本目录 §2 标题、配置声明,外加索引行。