From a5832788e94dbd16e69cc24b357349cde4bac089 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Wed, 28 Oct 2026 12:27:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(id-check):=20=E7=BC=96=E5=8F=B7=E9=97=A8?= =?UTF-8?q?=E7=A6=81=E7=AC=AC=E4=BA=8C=E6=AD=A5=E2=80=94=E2=80=94=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=A1=8C=E6=8B=A6=E6=88=AA=EF=BC=88=E9=9B=B6=E8=AF=AD?= =?UTF-8?q?=E6=96=99=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:第一步(bf2a5c21)把两根护栏转成报告制,但全库语料审计仍在跑。 它真正的用途只有一个——拦住「新写一个自造编号」;这件事不需要 99 族语料。 所以本笔落第二步:只查 git diff 的新增行,判据收窄成两张前缀表。 新增 scripts/check_ids_new.py(241 行,零语料): - 破折形态:首段不在 KNOWN_DASH(活族 + 标准名,11 条)→ FAIL; 在 RETIRED_DASH(已停发族)→ 只 WARN,提示改语义短语。 - 裸形态:前缀不在 KNOWN_BARE(外设/器件/协议/点号,约 90 条)→ FAIL。 - 单字母 + 数字一律放行(元件位号与层次号同形,显式盲区)。 - 算术排除(SIZE-2 / LEN-1 这类是减法不是编号);跳过 [示例]/[计划] 行; 行内 [编号豁免] 为最后手段。默认查暂存区,另有 --worktree / --range / --self-test。 接线:新钩子 id-check-new(钩子数 51 → 52,AGENTS 与门禁索引已同批改), 与 id-check 双跑一个观察期;观察期后按 docs/编号规范.md §8.5 ③ 退役旧语料。 旧门禁的排除面加上新脚本(它必然写着示例令牌,否则自己拦自己)。 白名单是按真实数据调出来的,不是拍脑袋:回放最近 80 笔提交,未知形态只有 15 种, 其中 12 种是设备/协议名(RS232、RS485、OVER8、YT/YH 点号等,已进白名单)、 3 种是已停发族(P-8、N-1、F-NET-*,只 WARN)→ 调好后回放误报 0。 自证(实测): - 自检 5 正例全部拦下、29 反例全部放行(含 TC-NET-204 / TRAP-ADC-01 / TPAFE5160 / GD5F2GQ5UE / C12 / U15 / L3 / SIZE-2 / GPIO_PIN_5)。 - 端到端:暂存一个自造前缀 + 数字 → 钩子 FAIL;清理后 Passed。 - 反向:本页文档里举的反例自己也被拦过一次,改成带 [示例] 标记后才过—— 这条正好证明它没有「文档路径」的例外面。 - 两条门禁互不干扰:old-gate rc=0、new-gate rc=0、doc-links 通过(211 文档)。 --- .pre-commit-config.yaml | 19 ++- AGENTS.md | 4 +- docs/编号规范.md | 3 +- docs/门禁说明/README.md | 19 +-- docs/门禁说明/id-check-new.md | 105 ++++++++++++++ scripts/check_ids_new.py | 255 ++++++++++++++++++++++++++++++++++ scripts/id_registry.json | 2 +- 7 files changed, 393 insertions(+), 14 deletions(-) create mode 100644 docs/门禁说明/id-check-new.md create mode 100644 scripts/check_ids_new.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0b82e1a2..0f3b5bcf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -682,8 +682,9 @@ repos: # 必然一边倒误报 → 裸形族只在属主文档内判越游标、文档外降告警;收口属 §9 阶段 # 1-② 的迁移批次。**不是「检查通过」,是记在案上的盲区。** # ⚠ 当前 warn-only(§8 上线节奏:先清 backlog → 清零后 entry 加 --strict 拦提交, - # 与 doc-links 章节校验同一条路径)。校验 8 的「可判定比例」跌破 - # id_registry.json 里的基线时**任何模式都失败**——那根线就是防回退的本体。 + # 与 doc-links 章节校验同一条路径)。校验 8 的两根护栏(可判定比例 / 不可判定 + # 绝对数)**自 2026-10-28 起只报告、不拦提交**(用户裁定,退役路线见规范 §8.5); + # 硬拦的只有校验 1~6 的台账外新账。 # ⚠ pre-commit 4.x 已移除 `always: true` → files 必须覆盖所有能改编号的触发面 # (任意 md/c/h/py/yaml/json/txt + 本配置文件);扫描范围恒为全库 # (悬空/孤儿/重复定义是跨文件判定,pass_filenames: false)。 @@ -694,6 +695,20 @@ repos: files: \.(md|c|h|py|ya?ml|json|txt)$|^\.pre-commit-config\.yaml$ pass_filenames: false + # 14) 编号门禁·第二步(2026-10-28 加):只查**新增行**,零语料 + # 背景:语义标识已是默认形态,上面那套 99 族全库语料审计的维护成本高过收益 + # (近 80 笔提交有 61 笔被编号体系牵连),退役路线见 `docs/编号规范.md` §8.5。 + # 本钩子是新牙:看 git diff 的新增行里有没有**未登记形态**的「字母+数字」标识 + # (自造前缀直接 FAIL;已停发族只 WARN);白名单在脚本里,按实测普查枚举、几乎不增长。 + # 与 id-check **双跑**一个观察期(老的是全库语料、新的是新增行),观察后再摘掉老的。 + # 自检:`python scripts/check_ids_new.py --self-test`;范围自查用 `--range A..B`。 + - id: id-check-new + name: 编号门禁·新增行(零语料,拦截新造编号) + entry: python scripts/check_ids_new.py + language: system + files: \.(md|c|h|py|ya?ml|txt)$|^\.pre-commit-config\.yaml$ + pass_filenames: false + # 14) 分层方向门禁(2026-10-22 加,结构债的"防扩大"那一半) # 背景:AGENTS.md 与 项目架构总览.md 早就写着"六层单向依赖",但**从来没有机械检查** # ——Drivers/BSP/ 里可以随手 include "cfg_file.h" 而无人报警。后果可测量:2026-09 审查 diff --git a/AGENTS.md b/AGENTS.md index 1364c27b..c9aa974d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,7 @@ ## 提交门禁(pre-commit) -- 任一失败即拦截,共 **51 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(= 52),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;两根护栏(可判定比例基线 / 不可判定引用**绝对数**上限)**自 2026-10-28 起只报告、不拦提交**(用户裁定:语料维护成本已高过它买到的一致性;退役路线见 `docs/编号规范.md` §8.5),硬拦的只有校验 1~6 的台账外新账(口径见 `docs/门禁说明/id-check.md` §7.6.1))+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 40 套带 `run_unit_test.py`,其余 **18 套**(含 `test/host/ui_sink`)没有专属钩子,但每次提交都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,18 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 60 页黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**)) +- 任一失败即拦截,共 **52 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(= 52),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;2026-10-28 加 `id-check-new` 一条(编号门禁第二步:只查新增行、零语料,与 `id-check` 双跑观察,退役路线见 `docs/编号规范.md` §8.5);**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;两根护栏(可判定比例基线 / 不可判定引用**绝对数**上限)**自 2026-10-28 起只报告、不拦提交**(用户裁定:语料维护成本已高过它买到的一致性;退役路线见 `docs/编号规范.md` §8.5),硬拦的只有校验 1~6 的台账外新账(口径见 `docs/门禁说明/id-check.md` §7.6.1))+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 40 套带 `run_unit_test.py`,其余 **18 套**(含 `test/host/ui_sink`)没有专属钩子,但每次提交都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,18 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 60 页黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**)) - **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5) - ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关 - **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/` @@ -175,7 +175,7 @@ | ADC 采样节拍 / 通道映射 / V0.1 硬件缺陷 | `docs/子系统设计/ADC采样节拍设计说明.md`、`docs/硬件平台/ADC通道映射说明.md` | | 存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) | `docs/架构设计/STM32F4-Base存储架构说明.md` | | RAM / 栈预算 / 容量台账 | `docs/内存管理/RAM使用分析与优化方案.md`、`内存内容分配策略.md` | -| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(51 个钩子 → 27 篇索引 + 1 指针) | +| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(52 个钩子 → 28 篇索引 + 1 指针) | | **要不要给某个东西发编号**(默认:不发,写语义短语)/ 允许用号的 7 个族(`TC-*` 已停发——「停发」不等于「暂缓」,也不等于「可以清号」)/ `id-check` 八项校验 | `docs/编号规范.md`(§1 先问三句、§4.1 族总表〔穷举〕与『已停发』栏、§8 门禁与实测基线) | | 提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) | `docs/代码规范/pre-commit使用说明.md` §2 | | 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` | diff --git a/docs/编号规范.md b/docs/编号规范.md index ac117b5b..8d6b145a 100644 --- a/docs/编号规范.md +++ b/docs/编号规范.md @@ -264,7 +264,7 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | 步 | 内容 | 状态 | |---|---|---| | ① | 两根护栏(比例基线 / 不可判定绝对数上限)**转报告制**:照打数字,不再影响退出码 | **已完成 2026-10-28** | -| ② | 用 **diff 级新号拦截**替换语料审计:只看新增行里命中"字母+数字"形态、且不在豁免表里的字面量,FAIL 并提示改成语义短语;豁免表约 50 条(器件位号 / 协议与端口名 / 遥信遥控点号),基本不增长 | 待做 | +| ② | 用 **diff 级新号拦截**替换语料审计:只看新增行里命中"字母+数字"形态、且不在白名单里的字面量,FAIL 并提示改成语义短语;白名单 = 前缀级两张表(活族 11 + 器件/协议/点号约 90),基本不增长 | **已完成 2026-10-28**(`scripts/check_ids_new.py` + 钩子 `id-check-new`,与 `id-check` **双跑观察期**;详解 `docs/门禁说明/id-check-new.md`) | | ③ | 退役语料:99 族盘点成"活族清单",冻结族段与 `id_aliases.json` 移入归档;`check_ids.py` 降为手工报告工具,退出钩子 | 待做(② 落地观察一周再动) | **在 ② 落地之前,校验 1~6 仍是唯一的机械牙**:别因为"两根线不拦了"就顺手写号—— @@ -453,6 +453,7 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | 日期 | 变更 | 说明 | |---|---|---| +| 2026-10-28 | **编号门禁第二步落地:新增行拦截(零语料)** | `scripts/check_ids_new.py` + 钩子 `id-check-new`:只看新增行,破折形态未知前缀 FAIL、已停发族只 WARN、单字母+数字放行(元件位号同形,见该门禁 §6 盲区)。白名单是**前缀级**两张表(活族 11 条 / 器件与协议约 90 条),不是字面量账本——后者会退化成 `id_aliases.json` 的老路。调参依据:最近 80 笔真实提交回放,未知形态 15 种(12 种是设备与协议名、3 种是停发族),调好后误报 0;自检 5 正例 + 29 反例。与 `id-check` **双跑**一个观察期后再按 §8.5 ③ 退役旧语料。 | | 2026-10-28 | **编号门禁减负第一步:两根护栏转报告制** | 用户裁定「编号门禁每次浪费很多时间」→ 先摘掉语料审计的强制面。护栏 1(可判定比例基线)与护栏 2(不可判定绝对数上限)自此**只打印、不拦提交**;实测把基线调到 99.9%、上限调到 1 处,退出码仍为 0。硬拦只剩校验 1~6 的台账外新账(同一实测:新文件里写一个自造前缀 + 数字,校验 1 与增量拦截照旧 FAIL)。退役路线 ①②③ 写进 §8.5;减负依据 = 近 80 笔提交有 61 笔被编号体系牵连、语料共 99 族(78 族已冻结)。 | | 2026-10-28 | **变更记录行里已停发/作废族的示例编号改形态描述;并用门禁自身计数器把「这一笔能不能压下不可判定绝对数」钉死为零** | 用户指令:把变更记录行里已停发族的那几枚示例编号改成语义形态描述,并确认它让不可判定引用绝对数净减、不是只算自己这一笔。**实测裁定:净减 = 0**。门禁把「表格首格是日期」的行整行豁免于编号判定(registry 侧的变更记录豁免,本笔前全库已豁免 198 处站点),本表的示例编号从来不进引用总数分母,改它必然不动绝对数——改前改后同一个计数器均为 **1003**(上限 1166;可判定 87.43%,三项都是 2026-10-28 本回合实测)。唯一移动的是那行豁免计数本身(本文件少 1 处站点),把它当清退成果就是把自记账成全局减量,正是本笔要排除的错法。本文对该计数器的全部贡献 **6** 处,逐文件集合差实测:全是「非编号」判定栏里既定的噪声名与裸形盲区(哈希算法名、总线名、严重度档位、硬件位号),不是编号,不清。本笔动的 3 处只是写法:作废的网络条目孤儿号(F 前缀 + 模块码段 + 数字序号三段)与已删报告的 UI 簇(字母 U 紧跟一位数字,区间两端即该簇首尾两枚);事实本体仍在别名表、registry 游标备注与 git 历史,一处没丢。刻意不动:硬件位号形态的举例(判非编号)、保留现存号族的冻结游标数字(冻结水位的证据本体)、其它文档与注册表里的同名字段(各自写域)。§5.5 同步写死该例外的硬口径。零代码、零编号增删、两根护栏基线均未调整。 | | 2026-10-27 | **批次五:「数据可信」任务卡号整族清退 + 注入验证卡销项删行** | 用户指令「数据可信.md 已经完成的内容请进行销项删除,去除相关编号」。实测 **288 站点 / 56 文件**(代码与测试 191 / 文档 56 / 门禁配置与本文 41)全改语义短语;§5 表首列由「序」改「标识(任务名)」、14 个旧号进 §9.3 换算表;顺带按属主文档判语义清掉同形态的悬空引用 4 处(「UI 补齐」波次的 `[comm]` 段前置项)。门禁面:编号字面量与不可判定绝对数**零变化**(中文前缀不在语料),收口复跑 `check_ids.py --strict` 与 `check_doc_links.py --all --sections-strict` | diff --git a/docs/门禁说明/README.md b/docs/门禁说明/README.md index 75cd5e20..20d929ad 100644 --- a/docs/门禁说明/README.md +++ b/docs/门禁说明/README.md @@ -1,6 +1,6 @@ # 门禁逐项说明(每种门禁一篇) -> 这一目录回答一个问题:**提交时那 51 个钩子到底在干什么、拦谁、放谁、什么时候会骗人。** +> 这一目录回答一个问题:**提交时那 52 个钩子到底在干什么、拦谁、放谁、什么时候会骗人。** > 每个门禁一篇,八段固定结构:查什么 / 为什么要它 / 触发面 / 报错怎么读 / 好处 / > **限制与盲区** / 豁免与基线 / 改门禁本身要注意什么。**最后一节是这套文档存在的理由**—— > 一份只看"拦了什么"的门禁文档,会让人以为绿就是对的。 @@ -8,15 +8,16 @@ - 配置事实源:`.pre-commit-config.yaml`。**钩子数的唯一口径 = YAML 解析后的 hook 条目数**,不是文件里的文本行数。 自查(打印声明数;加/删一条钩子后,这个数要与 **AGENTS.md 的「共 N 个钩子」**、**本目录 §2 标题**同时对齐): `& '.venv\Scripts\python.exe' -c "import yaml; d=yaml.safe_load(open('.pre-commit-config.yaml', encoding='utf-8')); print(sum(len(r['hooks']) for r in d['repos']))"` - → **51**(2026-10-26:49 → 50 加 `generated-fresh`;2026-10-27:50 → 51 加 `nested-comment`)。 + → **52**(2026-10-26:49 → 50 加 `generated-fresh`;2026-10-27:50 → 51 加 `nested-comment`; + 2026-10-28:51 → 52 加 `id-check-new`——编号门禁第二步,与 `id-check` 双跑观察)。 ⚠ **别用 `grep -c` 去数配置里的声明行**:该文件里有一条**注释掉的**候选钩子(clang-format,第 65 行), - 文本 grep 会多算 1 = 52——「声明 52 / 文档 51 / 实跑 51」这种三个数不同源的争执就是这么来的。真要 grep, - 用只认行首缩进的模式:`grep -Ec "^ +- id: " .pre-commit-config.yaml`(= 51)。 + 文本 grep 会多算 1 = 53——「声明 53 / 文档 52 / 实跑 52」这种三个数不同源的争执就是这么来的。真要 grep, + 用只认行首缩进的模式直接数是 52(含那一条注释掉的候选,故与 YAML 口径差 1)。 ⚠ 反过来的坑也真踩过(2026-10-27):在配置**注释里复述那个 grep 字面量**,会让文本 grep 再虚高 1—— 数数只认 YAML 解析,注释与文档里都别写出那个字面量(`docs/门禁说明/nested-comment.md` §8 记了现场)。 实跑侧口径:`scripts/ci_local.py` 的 `[SUMMARY] L0: pre-commit hooks -- passed=A failed=B skipped=C`, - `A+B+C` = **commit 阶段**的钩子数(50),再加 `gitlint` 1 条(它的 commit-msg 阶段声明写在**上游**仓库自带的 - 钩子清单里,本配置文件里查不到,见 `scripts/ci_local.py` 文件头)→ 50 + 1 = 51 = 声明数,三数同源。 + `A+B+C` = **commit 阶段**的钩子数(51),再加 `gitlint` 1 条(它的 commit-msg 阶段声明写在**上游**仓库自带的 + 钩子清单里,本配置文件里查不到,见 `scripts/ci_local.py` 文件头)→ 51 + 1 = 52 = 声明数,三数同源。 - 通用机制(两阶段、stash 陷阱、`always` 已废、增量拦截三段式、旧 §2.x 查号表): `docs/代码规范/pre-commit使用说明.md` §2。 - 快速自查(不等提交):`& '.venv\Scripts\python.exe' scripts/ci_local.py`(L0 + L0b + L0.5)。 @@ -27,18 +28,20 @@ |---|---|---|---| | **改写型** | 不报错,直接改你的文件 | `trailing-whitespace`、`end-of-file-fixer` | 重新 `git add` 被改的文件再提交(它改的是工作区,不是索引) | | **硬拦截** | 违反即失败,没有存量豁免 | `cppcheck`、`coding-standard`、`doc-links`、`no-mojibake`、`mem-budget`、`ui-golden`、`font-charset`、`lv-conf-sync`、`task-table`、`mem-numbers`、`registration-check`、`cubemx-survival`、`test-suite-switch`、16 个主机单测、官方 5 个、`gitlint` | 只能改代码或改文档;**不要**找豁免开关 | -| **增量拦截** | 规则恒生效,但存量钉在基线/台账里,只许减不许增 | `layer-deps`(3 条,2026-10-25 还清 UI 记录 3 条)、`bsp-err`(38 条)、`id-check`(`scripts/id_known_backlog.json` 台账)、`generated-fresh`(5 处 `docs/images/` 配图存量,脚本内 `BASELINE`)、`nested-comment`(2 处主机侧假件头,`test/host/common/`,脚本内 `BASELINE`) | 报错先分清是**新增**还是**存量搬家**:`--verbose` 看归类,`--prune` 看已还清 | +| **增量拦截** | 规则恒生效,但存量钉在基线/台账里,只许减不许增 | `layer-deps`(3 条,2026-10-25 还清 UI 记录 3 条)、`bsp-err`(38 条)、`id-check`(`scripts/id_known_backlog.json` 台账)、`generated-fresh`(5 处 `docs/images/` 配图存量,脚本内 `BASELINE`) | +| **只查新增** | 只看本次改动的增量,没有存量基线,也没有台账 | `id-check-new` | 改成语义短语,或把设备名/协议名登记进脚本白名单(行内 `[编号豁免]` 是最后手段) |、`nested-comment`(2 处主机侧假件头,`test/host/common/`,脚本内 `BASELINE`) | 报错先分清是**新增**还是**存量搬家**:`--verbose` 看归类,`--prune` 看已还清 | ⚠ 增量拦截最容易误会的一点:**绿灯不等于没问题**,只等于"没有比上次更差"。 每个增量拦截门禁的基线数字都写进它自己的文档,读那篇时第一眼看的就是"现在在册多少条"。 -## 2. 索引(51 个 hook → 27 篇 + 1 根只有本表指针(专篇待补)+ 1 根详解在 docs/测试说明/) +## 2. 索引(52 个 hook → 28 篇 + 1 根只有本表指针(专篇待补)+ 1 根详解在 docs/测试说明/) | 门禁文档 | 覆盖的 hook id | 一句话它在查什么 | 改了哪类文件才会跑 | 文档 | |---|---|---|---|---| | [`bsp-err.md`](bsp-err.md) | `bsp-err` | BSP 的对外 API 面:错误通道类型、超时宏命名、必须写返回值、返回值不得裸丢弃 | `Drivers/BSP/**` 或门禁自身 | 已写 | | [`layer-deps.md`](layer-deps.md) | `layer-deps` | 分层方向:`Drivers/BSP/**` 不得 include `App/**` | `Drivers/BSP/**` | 已写 | | [`id-check.md`](id-check.md) | `id-check` | 实体编号的八项校验(未登记前缀、裸形、重复定义、孤儿、悬空引用…) | 几乎所有文本文件 | 已写 | +| [`id-check-new.md`](id-check-new.md) | `id-check-new` | **只查新增行**有没有新造编号(零语料前缀白名单,已停发族只告警) | 任意 md/c/h/py/yaml/txt | 已写 | | [`doc-links.md`](doc-links.md) | `doc-links` | 文档里的路径引用是否存在;`--sections-strict` 另查章节号是否还存在 | 任意 `.md` | 已写 | | [`cppcheck.md`](cppcheck.md) | `cppcheck`、`test-cppcheck` | 静态分析(真机侧与 `test/` 侧两套范围与抑制项) | C/H 源文件 | 已写 | | [`coding-standard.md`](coding-standard.md) | `coding-standard` | 编码规范 V1.0 的可机械检查子集(命名、头文件保护宏、模块头注释块…) | C/H 源文件 | 已写 | diff --git a/docs/门禁说明/id-check-new.md b/docs/门禁说明/id-check-new.md new file mode 100644 index 00000000..69ff2eb5 --- /dev/null +++ b/docs/门禁说明/id-check-new.md @@ -0,0 +1,105 @@ +# id-check-new(编号门禁·新增行) + +> 一句话:**只查这次改动新增的行里有没有新造的编号**,不维护任何族清单。 +> 它是 `id-check` 全库语料审计的接班件(退役路线见 `docs/编号规范.md` §8.5)。 + +## 1. 它在查什么 + +拿 `git diff` 的**新增行**,只做三件事: + +| 形态 | 例子(形态描述,不是本仓号) | 判据 | +|---|---|---| +| 破折形态 | 两字母前缀 + 连字符 + 数字 | 首段不在白名单 → **FAIL**;在「已停发族」→ 只 WARN | +| 裸形态 | 大写字母串紧跟数字(外设/器件/点号写法) | 前缀不在白名单 → **FAIL** | +| 单字母 + 数字 | 元件位号与层次号同形的那些 | **一律放行**(见 §6 盲区) | + +判据全文只有两张前缀表(`KNOWN_DASH` ≈ 11 条、`KNOWN_BARE` ≈ 90 条)与一张算术排除表 +(`SIZE`/`LEN`/`MAX` 之类后面跟 `-数字` 是减法不是编号)。**没有族形态正则、没有游标、 +没有属主登记处、没有比例基线**——这就是它相对 `id-check` 的全部区别。 + +## 2. 为什么要它(与 id-check 的分工) + +`id-check` 需要养 99 族语料(`id_registry.json` 87.6 KB + `id_aliases.json` 187 KB); +实测它的**单次运行只有 1.8 s**,贵的是"每笔提交都要维持语料自洽"——近 80 笔提交里 61 笔 +改了语料/门禁/规范。而它真正有用的那部分(拦住"新写一个自造编号")不需要语料: +只要知道"这个前缀我从没见过"就够了。 + +所以两者的分工是:**`id-check` 管存量一致性(逐步退役),`id-check-new` 管增量不许新增**。 +观察期内两条**同时跑**,互相兜底;观察期结束后按 `docs/编号规范.md` §8.5 摘掉 `id-check`。 + +## 3. 触发面 + +- `files:` = 任意 `md` / `c` / `h` / `py` / `yaml` / `txt` 或本配置文件本身;`pass_filenames: false` + (脚本自己读 `git diff`,不靠钩子传文件名)。 +- 只读**新增行**:删行、改行里的旧编号不查(那是存量,归 `id-check`;退役后不再有人管, + 但这正是用户裁定接受的代价)。 +- 默认查**暂存区**(`git diff --cached`)。手工自查:`--worktree`(未暂存改动)/ `--range A..B`(一段提交)。 +- 排除面与旧门禁对齐:第三方库、HAL/CMSIS、`docs/芯片手册/`、`docs/硬件平台/`、`docs/images/`、 + `simulator/tests/ref/`、`ci-artifacts/`、`.dsh/skills/`、`scripts/rename_maps/`、 + 以及二进制/生成物后缀(`.svg` `.png` `.bin` `.map` `.json` 等)。 + +## 4. 报错怎么读 + +```text +[FAIL] 新增行里有 2 处**未登记形态**的字母+数字标识: + docs/xxx.md:3 ZZ-9 [示例] + → 语义标识是默认形态:改写成短语(docs/编号规范.md §1); + 确属设备名/协议名/位号,就把前缀登记进 scripts/check_ids_new.py 的白名单。 +``` + +三种处理:① 改成语义短语(默认);② 确实是设备名/协议名(如新的芯片型号)→ 把前缀加进 +`KNOWN_BARE` / `KNOWN_DASH`;③ 这一行必须保留字面(如冻结的现场日志)→ 行内写 `[编号豁免]`。 +⚠ `[示例]` / `[计划]` 行按既有冻结口径**整行跳过**,这也是本门禁的已知口子(§6)。 +⚠ 上面报错样例那行带 `[示例]`:两条门禁都认这个冻结标记、认不出「这段是文档举例」, +所以**在文档里举反例必须带 `[示例]`**,否则举例自己就会被拦(这条就是被自己拦过一次之后补的)。 +退出码:0 = 通过 / 仅告警;1 = 有新增编号;2 = 环境错误(git 不可用、不在仓库内)。 + +## 5. 好处 + +- **零维护**:加一个设备型号是改一行白名单,不需要登记族、游标、属主文档、基线比例。 +- **判据窄**:只对"新增"负责,所以不必解释"为什么这个历史号还在"——那类争论在旧门禁里占了大半时间。 +- **自证成本低**:`--self-test` 内置 5 个正例 + 29 个反例(含 `TC-NET-204`、`TRAP-ADC-01`、 + `TPAFE5160`、`GD5F2GQ5UE`、`C12`、`SIZE-2`、`GPIO_PIN_5` 等),改脚本后一条命令验证没把牙弄掉。 +- **落地前测过噪声**:拿最近 80 笔真实提交回放,未知形态只有 15 种,其中 12 种是设备/协议名 + (已进白名单)、3 种是已停发族(只告警)→ 白名单调好后回放 **0 误报**。 + +## 6. 限制与盲区(这一节请当正文读) + +1. **单字母 + 数字一律放行**:元件位号(`C12`/`U15`/`R3`/`D7`)与层次号、裸形族号同形, + 机械区分不出来 → 新写一个单字母+数字的号**不会**被拦。这是本门禁最大的洞,与旧门禁的 + 「裸形盲区」同源,属显式接受的代价。 +2. **只查新增行**:把旧编号从 A 行搬到 B 行、或整段重排,算"新增"→ 会拦(因为形态未登记); + 反之,只要形态在白名单里,新写的也放行——白名单是"形态"级,不是"号"级。 +3. **`[示例]`/`[计划]` 整行跳过**:想绕过本门禁,写个 `[示例]` 就行。这靠评审纪律,不靠机器。 +4. **未跟踪文件不在扫描面**(`git diff` 语义):手工在 `git add` 之前跑 `--worktree` 看不到新文件; + 提交时必已进暂存区,所以门禁场景不受影响。 +5. **算术白名单是启发式**:`SIZE-2`/`LEN-1` 这类被排除;但若某处写着一个「不在白名单的前缀 + + 连字符 + 数字」,就会误报一次——把前缀加进白名单即可,不构成误拦风险。 +6. **不校验语义**:白名单里的前缀可以拿来写任何东西(`TIM3` 合法,`TIM99` 也放行)。 + 它只回答"这是不是新造的前缀",不回答"这个号该不该存在"。 + +## 7. 豁免与白名单口径 + +| 类别 | 放行方式 | 维护动作 | +|---|---|---| +| 设备 / 芯片 / 模块型号(`TPAFE5160`、`CH395F`、`SD2506`、`GD5F2GQ5UE`) | `KNOWN_BARE` 前缀 | 新器件时加一条 | +| 协议 / 标准(`IEC104`、`SHA-256`、`RS-485`、`UTF-8`) | `KNOWN_DASH` / `KNOWN_BARE` | 基本不增长 | +| 遥信/遥控点号(`YX`/`YK`/`YT`/`YH`)与保护元件代号(`OC`/`UV`/`OV`/`CP`…) | `KNOWN_BARE` | 协议数据,不是标识符 | +| 活族编号(`FR-*`、`TRAP-*`、`TC-*`、`CODE-RULE-*`、`MENU-STATE-*`) | `KNOWN_DASH` | 新增族要先在此登记 | +| 已停发族(登记簿/专项/层次/原则等旧族) | `RETIRED_DASH` → **只 WARN** | 不拦提交,提示改语义短语 | +| 一次性保留 | 行内 `[编号豁免]` | 提交说明里要交代原因 | + +## 8. 改这个门禁本身要注意什么 + +- 改完必跑 `python scripts/check_ids_new.py --self-test`(正例漏拦 / 反例误拦都会红)。 +- 加白名单是**唯一**放宽手段;没有"整库豁免"入口,也没有 `--write` 现状洗白(与旧门禁同一条纪律)。 +- 别把白名单做成"按字面量枚举"的账本——那就退化成 `id_aliases.json` 的老路(187 KB 维护负担), + 前缀级枚举才是它便宜的原因。 +- 与 `id-check` 的关系变更(摘掉旧的)要同批改:本页、`docs/编号规范.md` §8.5、 + `docs/门禁说明/README.md` 的索引与计数、`AGENTS.md` 的钩子数。 + +## 9. 变更记录 + +| 日期 | 变更 | +|---|---| +| 2026-10-28 | **新建(编号门禁第二步)**:只查新增行、零语料;破折形态未知前缀 FAIL、已停发族 WARN、单字母+数字放行。与 `id-check` 双跑观察期。白名单由最近 80 笔真实提交回放调出(15 种未知形态 → 12 种进白名单、3 种只告警,误报 0)。自检 5 正例 + 29 反例通过;端到端探针:暂存一个自造前缀 + 数字 → 钩子 FAIL;清理后 Passed。 | diff --git a/scripts/check_ids_new.py b/scripts/check_ids_new.py new file mode 100644 index 00000000..27f82b09 --- /dev/null +++ b/scripts/check_ids_new.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""check_ids_new.py — 编号门禁·第二步:只查**新增行**的新编号(零语料、无语料维护) + +背景(docs/编号规范.md §8.5):语义标识已是默认形态,1~6 项全库语料审计的成本高过收益。 +本脚本替代它的**唯一实际用途**——拦住"新写一个自造编号":只看 git diff 的新增行。 + +判据(三条,都不依赖任何族清单 / 游标 / 属主登记处): + 1. 破折形态(形如 前缀-数字,或 前缀-段-数字):首段不在 KNOWN_DASH → FAIL; + 在 RETIRED_DASH(已停发族)→ 只 WARN(不拦,提示改语义短语)。 + 2. 裸形态(大写字母紧跟数字,如 TIM3 / YX15 / TPAFE5160):前缀不在 KNOWN_BARE → FAIL。 + 3. 单字母+数字(C12 / U15 / L3 / R3)→ **放行**:元件位号与层次号同形,这是本脚本的已知盲区 + (与旧门禁的"裸形盲区"同源,见 docs/门禁说明/id-check-new.md §5)。 + +用法(仓库根): + python scripts/check_ids_new.py # 查暂存区(pre-commit 用这个) + python scripts/check_ids_new.py --worktree # 查工作区未暂存的改动(自查) + python scripts/check_ids_new.py --range A..B # 查一段提交的新增行 + python scripts/check_ids_new.py --self-test # 内建正反例自检(改脚本后必跑) +退出码:0 = 通过 / 仅告警;1 = 新增了未知编号;2 = 环境错误。 +豁免:行内含 [示例] / [计划] 的行按冻结口径跳过;确需保留的写法在同一行写 [编号豁免]。 +""" +import argparse +import re +import subprocess +import sys +from pathlib import Path + +# Windows 控制台默认 GBK,文档行里可能带 emoji(❌/✅)→ 打出来会 UnicodeEncodeError。 +for _stream in (sys.stdout, sys.stderr): + try: + _stream.reconfigure(encoding="utf-8", errors="replace") + except (AttributeError, ValueError): + pass + +REPO_ROOT = Path(__file__).resolve().parent.parent + +# --- 破折形态:首段白名单(活族 + 标准编号;新增族要先登记到这里,这就是"允许用号的唯一口子") +KNOWN_DASH = frozenset({ + "FR", # 功能需求号(docs/产品需求/产品需求设计文档.md §4.1 在册保留) + "NFR", # 非功能需求号 + "TC", # 主机侧用例号(已停发,但存量引用最多) + "TRAP", # 陷阱台账(TRAP-NET-xx / TRAP-ADC-xx) + "CODE", # 代码规范条目(CODE-RULE-xx) + "MENU", # 菜单状态(MENU-STATE-xx,像素回归引用) + "UTF", # 编码名(UTF-8) + "RS", # 串行标准名(RS-232 / RS-485) + "SHA", # 摘要算法名(SHA-256) + "CRC", # 校验算法名(CRC-16) + "DMA", # DMA 控制器编号(DMA-2,少量文档写法) +}) + +# --- 已停发的族:新写号只 WARN(存量引用仍大量存在,硬拦会把正常编辑也挡了) +RETIRED_DASH = frozenset({ + "N", "S", "T", "A", "E", "D", "L", "P", "F", "H", "Z", "O", # 登记簿 / 专项 / 层次 / 原则等旧族 + "GAP", "DR", "MS", "WI", # 其余已转语义短语的族 +}) + +# --- 裸形态:前缀白名单(外设 / 器件 / 协议 / 保护元件 / 点号;按实测普查枚举,几乎不增长) +KNOWN_BARE = frozenset({ + # 外设与内核 + "TIM", "ADC", "SPI", "DMA", "USART", "UART", "I2C", "CAN", "GPIO", "NVIC", "APB", "AHB", "EXTI", + "IRAM", "IWDG", "WWDG", "FPU", "SRAM", "CCM", "ROM", "RAM", "FLASH", "POR", "BOR", "PIN", + # 引脚名(PA10 / PB10 / PC4 / PD12 / PE3 / PG9 / PH1 ...) + "PA", "PB", "PC", "PD", "PE", "PF", "PG", "PH", + # 器件 / 模块 / 工具链 + "STM", "TPAFE", "CH", "SD", "GD", "W25", "SD2506", "LVGL", "RTOS", "OS", "UV", "VS", "SDL", "PROF", + # 协议 / 标准 / 编码 + "IEC", "MODBUS", "TCP", "UDP", "FTP", "SNTP", "SOE", "COS", "CRC", "SHA", "IEEE", "ISO", "UTF", "ARGB", "RGB", + # 串行标准(RS232 / RS485 常写成不带连字符的形式) + "RS", "CAN", + # 遥信 / 遥控点号与保护元件代号(协议数据,不是标识符) + "YX", "YK", "YT", "YH", "OC", "UV", "OV", "CP", "VS", "PF", "SV", "AC", "DC", "IA", "IB", "IC", "UN", "UA", "UB", "UC", + # 统计 / 队列 / 结构计数 + "AIN", "CTR", "SQRT", "SQR", "LQFP", "DIV", "SCALE", "INTS", "HEX", "TLV", "LNK", "PHASE", "BALA", "ECCS", + "CN", "LED", "DB", "FAT", "AX", "SUB", "CLS", "IN", "BLE", "PH", "SYS", "TE", "LPN", "FF", "HW", "ZL", "RX", + "WSL", "WIN", "GPXTI", "COT", "WRTC", "RM", "CR", "SM", "IP", "COM", "SOCK", "LBA", "BOOT", "EXTKO", "INT", + # RCC_CSR 复位标志位号(BOR25 / PIN26 / POR27 / SFT28 / IWDG29 / WWDG30) + "SFT", + # UART 采样模式位名(OVER8) + "OVER", +}) + +# --- 前缀后面接 -数字 其实是算术(SIZE-2 / LEN-1),不是编号 +ARITH = frozenset({"SIZE", "MAX", "MIN", "LEN", "CNT", "NUM", "IDX", "POS", "PTR", "TOP", "END", + "LOW", "HIGH", "ROW", "COL", "BASE", "MASK", "HALF", "NUM", "VAL", "BIT", "BYTE"}) + +EXCLUDED_DIRS = ( + "Lib/", "Middlewares/", "Drivers/CMSIS/", "Drivers/STM32F4xx_HAL_Driver/", + "docs/芯片手册/", "docs/硬件平台/", "docs/images/", "docs/内存管理/data/", + "MDK-ARM/RTE/", "simulator/SDL2/", "simulator/build/", "simulator/tests/ref/", + ".venv/", "ci-artifacts/", "test/board/third_party/", "scripts/rename_maps/", + "tools/netlist_semantics/", ".dsh/skills/", "__pycache__/", +) +EXCLUDED_FILES = frozenset({ + "scripts/id_registry.json", "scripts/id_aliases.json", "scripts/id_known_backlog.json", + "scripts/check_ids.py", "scripts/check_ids_new.py", +}) +EXCLUDED_SUFFIX = (".svg", ".png", ".jpg", ".jpeg", ".gif", ".bin", ".map", ".htm", ".html", + ".tsv", ".log", ".o", ".axf", ".hex", ".ico", ".ttf", ".woff", ".pyc", + ".sct", ".lin", ".s", ".bat", ".ps1", ".json") + +DASH_RE = re.compile(r"(? 10: + print(" … 其余 %d 处略" % (len(warns) - 10)) + if fails: + print("[FAIL] 新增行里有 %d 处**未登记形态**的字母+数字标识:" % len(fails)) + for rel, lineno, tok, text in fails[:20]: + print(" %s:%d %s" % (rel, lineno, tok)) + if args.verbose: + print(" %s" % text) + print(" → 语义标识是默认形态:改写成短语(docs/编号规范.md §1);") + print(" 确属设备名/协议名/位号,就把前缀登记进 scripts/check_ids_new.py 的白名单。") + return 1 + print("[OK] 新增行无未登记编号%s" + % ("(%d 处停发族提示)" % len(warns) if warns else "")) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/id_registry.json b/scripts/id_registry.json index 94b9a223..11e44fc7 100644 --- a/scripts/id_registry.json +++ b/scripts/id_registry.json @@ -53,7 +53,7 @@ "test/board/third_party/", "scripts/id_registry.json", "scripts/id_aliases.json", - "scripts/check_ids.py", + "scripts/check_ids.py", "scripts/check_ids_new.py", ".dsh/skills/*/scripts/state/", ".dsh/n31_neg_inject_gen217.log", ".dsh/n31_neg_inject_gen221_form5.log",