chore(id-check): 退役全库编号语料审计(钩子 52 → 51)

第三步(用户裁定跳过观察期直接做)。第一步 bf2a5c21 把两根护栏转报告制,
第二步 a5832788 立起只查新增行的新门禁,本笔把旧的全库语料审计整体退役:
语义标识已是默认形态,这套 99 族语料不再有人需要维护。

移动(保留可复现性,不再进提交门禁):
- scripts/check_ids.py → scripts/archive/id_gate/check_ids.py
- id_registry.json(87.6 KB,99 族)/ id_aliases.json(187 KB)/ id_known_backlog.json
  一并归档;新写 archive README:为什么退役、现在用什么、怎么手工跑、归档纪律。
- 归档脚本改了自定位(仓库根往上 4 层、语料与脚本同目录)与一行退役横幅;
  从新路径实跑:811 文件 / 8,051 字面量,--strict rc=0。

钩子:删 id-check(52 → 51)。现役编号门禁只剩 id-check-new(只查新增行、零语料)。
声明数 51 = commit 阶段 50 + gitlint 1,三数已对齐(AGENTS / 门禁索引 / YAML)。

连带改动:
- rename_ids.py 的别名表路径指向归档(改名工具本身没退役);
- check_ids_new.py 排除面加 scripts/archive/(语料里全是编号字面量,天经地义);
- doc-links 跳过 .mnemon/ 与 ci-artifacts/(本地记忆状态与草稿,非项目文档)——
  语料搬家后这些目录里的旧路径引用会假红,它们本来就不该参与文档门禁;
- 全库 .md 里的旧路径引用改指 scripts/archive/id_gate/(含两份 skill 手册);
- id-check.md 加退役横幅并保留原文作留档;门禁索引把该行标成已退役;
- 编号规范 §8 改成「现行只有一道门禁」+ §8.1~§8.4 标为退役留档 + §8.5 ③ 置已完成;
  各加一条 2026-10-28 变更记录行。

验证(实测):YAML 口径 51 个钩子、id-check 不在其中;doc-links --all --sections-strict
通过(194 文档);mojibake / bom-guard / coding-standard / git diff --check 全 rc=0;
新门禁自检 5 正例 + 29 反例仍全过。
This commit is contained in:
2026-10-28 12:38:38 +08:00
parent a5832788e9
commit 1668842d61
20 changed files with 152 additions and 107 deletions
@@ -9,12 +9,12 @@ version: 1.1.0
**动手前先判这一族有没有「机器识别面」**——它决定计数口径的来源与落地去处,两条路的做法**正好相反**:
| 族形态 | 计数口径来源 | 改名手段 | `scripts/id_aliases.json` |
| 族形态 | 计数口径来源 | 改名手段 | `scripts/archive/id_gate/id_aliases.json` |
|---|---|---|---|
| 字母+数字,且在 `scripts/check_ids.py` 语料内(`id_registry.json` 有族、族 pattern 能匹配它) | 门禁自身计数(`ck.refs` / `ck.stats` / 报告行) | `scripts/rename_ids.py`(映射表 + 显式 scope + 默认干跑) | **要登记**:死号 `void`、转短语 `retired-to-phrase` |
| 字母+数字,且在 `scripts/archive/id_gate/check_ids.py` 语料内(`id_registry.json` 有族、族 pattern 能匹配它) | 门禁自身计数(`ck.refs` / `ck.stats` / 报告行) | `scripts/rename_ids.py`(映射表 + 显式 scope + 默认干跑) | **要登记**:死号 `void`、转短语 `retired-to-phrase` |
| 中文前缀(「卡」+ 数字)、纯散文里的号、圈号 ①②③ | **自建带标签正则扫描器**(门禁看不见它,id-check 报告与本族无关) | 逐处替换 + 断言命中数(`rename_ids.py` 靠族 pattern 驱动,这里用不上) | **刻意不登记**:登记了机器也看不见,只多一张没人核的表;停发只靠规范 + 评审 |
怎么判(三条,任一为否即无识别面):① `grep -nE '"name"|"pattern"' scripts/id_registry.json`,没有任何族 pattern 能匹配该形态;② `./.venv/Scripts/python.exe scripts/check_ids.py --top 0` 的任何校验里找不到它;③ registry 的 `blind_spots` 已明写不判(圈号与正常列举无法机械区分、裸形同字母多义只在属主登记处判)。实测:「卡」+ 数字**没有族**(中文形态只有 `malformed` 里 `陷阱 ?[0-9]` 那条禁令,管的是陷阱编号不是卡号),该族真实站点 288 处 / 56 文件,而门禁对它贡献 0。
怎么判(三条,任一为否即无识别面):① `grep -nE '"name"|"pattern"' scripts/archive/id_gate/id_registry.json`,没有任何族 pattern 能匹配该形态;② `./.venv/Scripts/python.exe scripts/archive/id_gate/check_ids.py --top 0` 的任何校验里找不到它;③ registry 的 `blind_spots` 已明写不判(圈号与正常列举无法机械区分、裸形同字母多义只在属主登记处判)。实测:「卡」+ 数字**没有族**(中文形态只有 `malformed` 里 `陷阱 ?[0-9]` 那条禁令,管的是陷阱编号不是卡号),该族真实站点 288 处 / 56 文件,而门禁对它贡献 0。
判错的代价是双向的:拿门禁计数当中文字面量族的口径 → 得出「0 处待改」的**假归零**;给无识别面的族登记别名 → 制造「有机器兜底」的错觉,真正生效的两条(§4.1.1 停发行、§9.3 换算表)反而没人写。
@@ -55,7 +55,7 @@ version: 1.1.0
- Mermaid 流程图节点名 → 改语义名但**节点数与连线关系不变**,图结构不动;
- 数学记号 → 进噪声表;门禁自身的规则名(gitlint `T2 Title has trailing whitespace`)→ 不改。
分类前先把剩余每处原文打出来看(`grep -F` 逐文件),否则会把硬件资料改坏。
4. **映射表由属主文档生成**:从属主文档取「编号 → 功能名/语义短语」全表(实测 42 个定义 / 28 个在用),改名走 `scripts/rename_ids.py`(映射表驱动 + **显式 scope** + **默认干跑** + 自动往 `scripts/id_aliases.json` 写 scoped 旧号→新号条目,状态 `renamed` / `retired-to-phrase` / `void`)。**绝不全局查找替换**:同一裸形字面量可能同时属多个族(实测 `S3` 三义),全局替换等于造新撞号。
4. **映射表由属主文档生成**:从属主文档取「编号 → 功能名/语义短语」全表(实测 42 个定义 / 28 个在用),改名走 `scripts/rename_ids.py`(映射表驱动 + **显式 scope** + **默认干跑** + 自动往 `scripts/archive/id_gate/id_aliases.json` 写 scoped 旧号→新号条目,状态 `renamed` / `retired-to-phrase` / `void`)。**绝不全局查找替换**:同一裸形字面量可能同时属多个族(实测 `S3` 三义),全局替换等于造新撞号。
5. **按写域拆道并行派单**(代码与测试半 / 文档半 / 某功能面各一条道),同笔用 `send_message` 下发**统一改写口径**(措辞、是否动字符串字面量、生成器归属、冻结历史行怎么处理),避免各道自定措辞;口径长就先落成 spec 文件(中文族一节第 5 步的做法同样适用)。看板任务只认 `team_task_update` 的 `task_id` / `expected_revision` / `action=reassign` / `owner` 四个字段。
6. **门禁配置注释也是本族写入面**:`.pre-commit-config.yaml` 的注释里会挂着功能号(实测 `7h) ADC 采样有效性校验(L1-6)`、`7i) 保护定值模型(P1 / L5-1 载体)`、`规划 §3.3 L3-5`)。配置属 Lead 写域,Lead 自己改(注释不占钩子行为、不影响 51 条钩子口径),但**必须计入批次范围并在提交正文写清是 Lead 改的 config**。
7. **每处替换断言命中数**,不做整文件正则批量改写(批量改写会静默漏改):
@@ -69,7 +69,7 @@ version: 1.1.0
```
**token 取最小形态**:文档里被反引号/加粗包住的号,替换单位取反引号形(```T4```)而**不含 `**` 标记**(第 225 行是 `**裸形 `T4`**`,按「裸形 `T4`」替就命中 0)。讲编号的文档改成**形态描述**(如「字母 T 紧跟数字 4」)能把不可判定绝对数还回余量:实测 4 处 → 982 降到 978,且 lesson 不丢。
8. **同步更新门禁侧配置**:`check_ids` 的 `excluded_paths` 与噪声表按第 3 步的分类结果改,使剩余命中不再是「假编号」;新测试套件发号的悬空引用要先定**定义位**:
- 定义位**只能落一处**——注册表族(`scripts/id_registry.json`)或该套件说明文档,**两处并存比没有定义位更坏**;文档真写出来了(含判据与上板清单)就以文档为准、**撤族**;
- 定义位**只能落一处**——注册表族(`scripts/archive/id_gate/id_registry.json`)或该套件说明文档,**两处并存比没有定义位更坏**;文档真写出来了(含判据与上板清单)就以文档为准、**撤族**;
- 要建族就照现有同类族克隆(`pattern` / `digit_width` / `owner_docs_glob` / `scope` / `cursor`),别自造字段;
- **抬游标前先确认游标是字符串形态**(`'cursor': 'TC-UIDATA-026'`)——整型比较会静默不生效(实测踩坑),用字符串解析或正则取序号再比。
9. **生成器文本改动必须同笔重跑产物**:`tools/arch_diagram/gen_*.py` 里的 docstring/文案会进产物,`generated-fresh` 把生成器拷到临时目录重跑逐字节比对,**只改源不改产物必红**。这类站点整包转给生成器属主,「谁改谁跑生成器」,脚本 + 全部 svg 产物同一笔:
@@ -84,8 +84,8 @@ version: 1.1.0
11. **分笔提交走真实 pre-commit 钩子**:`git add` 用显式清单。坑:`git add -- App/algo` 把文档道的 `App/algo/README.md`(26 处)带上车,正文按 48 只文件写就不准了 → 该笔未 push 时 `--amend` 挪到文档笔并补一句归属说明;`git add -A` 会把别道在飞文件卷进来(覆盖率钩子在他编译不过的半成品上报红)→ 撤出他人域后重跑。提交正文每行 ≤80(gitlint B1),别把 ASCII 双引号写成正文首字符。
12. **收口只认门禁自身计数**(**仅限语料内的族**;无识别面族见下一节第 8~9 步),并核**只减不增**:
```bash
./.venv/Scripts/python.exe scripts/check_ids.py 2>&1 | grep -aE '扫描|可判定 |不可判定引用' | head -4
./.venv/Scripts/python.exe scripts/check_ids.py --strict 2>&1 | tail -1 # [OK] 校验 1~6 无错误
./.venv/Scripts/python.exe scripts/archive/id_gate/check_ids.py 2>&1 | grep -aE '扫描|可判定 |不可判定引用' | head -4
./.venv/Scripts/python.exe scripts/archive/id_gate/check_ids.py --strict 2>&1 | tail -1 # [OK] 校验 1~6 无错误
```
报告「字面量 8787→8299→7828」「不可判定绝对数 1161→984→982→978(上限 1166)」「可判定比例 88.14%(基线 86.0%,容差 0.1pp)」。**绝对数与比例两根线都要报**:把可判定引用改成散文会稀释比值,所以另有不可判定绝对数上限。涨点归因见「净室归因」一节。
13. **回写 `docs/编号规范.md`**:§4.1 族表(停发/待清栏)、§4.1.1 已停发栏、§9 存量表与批次结果表(只减不增)、§9 清点数字按最终实测同步;表格若保留动手前的清点数,**明确标注「不是当前存量,是动手前基线」**,否则就是拿旧数当现状。变更记录加一行(含用户裁定原文)。
@@ -97,7 +97,7 @@ version: 1.1.0
## Steps(中文前缀族:门禁咬不到的那一路)
1. **先确认门禁咬不到,并把这句话写进交付**:`scripts/check_ids.py` 的族 pattern 是拉丁形态、中文前缀不进语料 → **id-check 的 refs / 可判定比例 / 悬空引用与本族无关**;报告开头写「本族不在 check_ids 语料内,口径 = 自建扫描器」。
1. **先确认门禁咬不到,并把这句话写进交付**:`scripts/archive/id_gate/check_ids.py` 的族 pattern 是拉丁形态、中文前缀不进语料 → **id-check 的 refs / 可判定比例 / 悬空引用与本族无关**;报告开头写「本族不在 check_ids 语料内,口径 = 自建扫描器」。
2. **销项删行前先核证据属主**(这是删行的唯一许可证):
- 先取历史口径:`git log --oneline -20 -- <文档>`、`git show <rev>:<文档> | sed -n '/^## 5/,/^## 6/p'` 找回被删的卡表与当初「完成即销项删行 / 标 ✅ 保留行」的理由;
- 核对判据与实测记录**已完整沉淀进属主章节**(实测:「已知电压注入验证」六项判据全 PASS、斜率 6544.2 码/V、偏差 −0.14 % 全在 `docs/硬件平台/ADC通道映射说明.md` §8.1,执行侧在 `docs/测试说明/ADC前端注入验证流程.md`);
@@ -143,7 +143,7 @@ version: 1.1.0
5. **派单前写一份 spec 文件**(`ci-artifacts/card_retire_spec.md`),作为各道唯一口径来源,含五块:① 背景与**用户裁定原文**;② 逐号映射表(旧号形态 → 语义短语 → 属主出处);③ 措辞规则(补短语时括号怎么降级、动不动字符串字面量、生成器归属、悬空引用怎么判);④ 硬约束(BOM 原样补回、行尾由 `.gitattributes` 写死、冻结历史行与原始证据不回改、代码头注释引用的设计约束章节保留、带 BOM 文件名单);⑤ 自检与交付格式(逐文件命中数、删了哪几行、残留数、门禁复跑结果)。`send_message` 只发路径 + 关键裁定,长中文正文不要整段塞进消息(一次串多条长消息会撑爆 120s,发出 1 条后 abort,剩余未发)。
6. **逐处替换断言命中数**(同第 7 步的 `rep(tag, old, new, want)`),且 `old` 串**从原文取、不凭记忆敲**:本仓中文引号与 ASCII 双引号混用(实测某文档里的「中文引号」其实是 ASCII `"`),凭印象写 `old` 必 `got=0`;断言抛错后先 `git status --porcelain=v1 -- <文件>` 自证文件未被别道改动,再回读该行真身重跑。删行类改动单独记 tag 并打印删了几行、行号与原行长度。多道并行时同一文件常被别道改过:`edit` 报 `file changed since it was read` → 重新 `read` 再改;报 `ReplaceFileW EIO (Win32 1175)` → 先自证文件字节完好(BOM / CRLF / `EF BF BD` 三项)再重试同一笔,不要顺手改内容。
7. **落地位置写死(这一族只有这几条兜)**:
- `docs/编号规范.md` **§4.1.1「已停发的族」表加一行**,三列齐全,第三列直写「⚠ **机器面空白**:中文前缀不进 `scripts/check_ids.py` 语料 → 门禁咬不到,只有本表 + §9.3 + 评审三条兜;`id_aliases.json` 刻意不登记(登记了机器也看不见,只多一张没人核的表)」;
- `docs/编号规范.md` **§4.1.1「已停发的族」表加一行**,三列齐全,第三列直写「⚠ **机器面空白**:中文前缀不进 `scripts/archive/id_gate/check_ids.py` 语料 → 门禁咬不到,只有本表 + §9.3 + 评审三条兜;`id_aliases.json` 刻意不登记(登记了机器也看不见,只多一张没人核的表)」;
- `docs/编号规范.md` **§9.3 逐号换算表**(旧号形态 → 语义短语 → 现行口径出处;本族实测 14 行 = 0、0b、1~12)+ §9 批次段落 + 变更记录各一行,**写明旧号永久作废、不得重新取走**;
- 表内**把形态写成「卡」+ 数字而不复述字面量**:规范本身是门禁语料,复述字面量会把「讲规则的文档」变成自己的引用站点;
- **同步叙述**:`AGENTS.md`「文档与图形」的销项条目、`docs/README.md`(例:`数据可信.md = 三项阻塞任务` → 两项)、`docs/任务规划/README.md`、`docs/任务规划/登记簿.md`——「剩余三项」这类旧口径实测归零;
@@ -192,7 +192,7 @@ version: 1.1.0
- **`grep 'A\|B'` 空返回当文件干净**:交替竖线在多层 bash 引号里被吃掉,是探针坏不是文件干净 → 反证为空的搜索必须换 `grep -F` 复测。
- **一次 run_code 串 `5 × wait_agent(118000)` ≈ 590s 整段 abort、进度全丢**;且一次串多条长中文 `send_message` 也撑爆 120s(发出 1 条后 abort,剩余未发)。轮询上限 4×118s,长消息拆开发。
- **游标整型比较静默不生效**(字符串形态);抬完用 `--strict` 与 plain 双验。
- **`git add` 目录路径带进别道文件**、`git add -A` 卷进在飞改动;撤销自己的改动只按文件点名 `git restore scripts/id_registry.json`,不碰同树其它道文件。
- **`git add` 目录路径带进别道文件**、`git add -A` 卷进在飞改动;撤销自己的改动只按文件点名 `git restore scripts/archive/id_gate/id_registry.json`,不碰同树其它道文件。
- **amend 被 gitlint B1 拦**:正文行 >80,别把 ASCII 双引号写成正文首字符。
- **改「只动注释」要拿机器证据**:`@build` 0 错 0 警且 `Program Size` 与改前**逐字节同数**(本回合真跑,引用历史提交正文自报的数字不算证据),别口头承诺。
- **冻结历史与原始证据不改**:boot 串口日志等采集现场是冻结证据,改了就是篡改;历史「修改记录」行按裁定保持改名后形态(号已全仓退役,留一个查无此号的引用比留改过名的条目更坏)。
@@ -200,7 +200,7 @@ version: 1.1.0
## Verification
1. `scripts/check_ids.py --strict` 与 plain **均 rc=0**(`[OK] 校验 1~6 无错误`),悬空引用为 0。
1. `scripts/archive/id_gate/check_ids.py --strict` 与 plain **均 rc=0**(`[OK] 校验 1~6 无错误`),悬空引用为 0。
2. 编号字面量与**不可判定引用绝对数**只降不升、不超 `undecidable_total_ceiling`;可判定比例不低于 `id_registry.json` 基线(86.0%)。
3. 本族剩余站点复扫归零:有识别面族从 `ck.refs` 按族正则过滤字面量逐条打印;除冻结历史/原始证据行外无残留。
4. `pre-commit run generated-fresh --all-files` PASS + 目检 svg diff 只有文案与框宽自适应(只在动过生成器时适用);`check_doc_links.py --all` 通过。
+4 -4
View File
@@ -197,7 +197,7 @@ WT="$MAIN/../_wt/und-attr" # 仓外同级 _wt/ 下;一次
rm -rf "$WT" 2>/dev/null
git -C "$MAIN" worktree add --detach "$WT" HEAD >/dev/null 2>&1 || { echo WT_FAIL; exit 9; }
git -C "$WT" status --porcelain # 应为空
"$PY" "$WT/scripts/check_ids.py" 2>&1 | tail -25 # ← TODO 口径:以 .pre-commit-config.yaml 里 id-check 钩子的 entry/args 为准;基线与注入用**同一条命令**
"$PY" "$WT/scripts/archive/id_gate/check_ids.py" 2>&1 | tail -25 # ← TODO 口径:以 .pre-commit-config.yaml 里 id-check 钩子的 entry/args 为准;基线与注入用**同一条命令**
```
- `--detach`(**不要 `-b`**):净室没有分支、不写 `_wt/.state/<ID>.json` 台账 → **不占 `MAX_CONCURRENT = 3`
@@ -212,7 +212,7 @@ git -C "$WT" status --porcelain # 应为空
for f in "App/log/sys_log_mcu.c" "test/host/sys_log_mcu/sys_log_mcu_test.c" "docs/子系统设计/运行与自检记录设计说明.md"; do
mkdir -p "$WT/$(dirname "$f")"
cp "$MAIN/$f" "$WT/$f" # 拷进净室;主树那份只读、一字节不动
echo "== $f =="; "$PY" "$WT/scripts/check_ids.py" 2>&1 | tail -25 # ← 与第 1 步同一条命令
echo "== $f =="; "$PY" "$WT/scripts/archive/id_gate/check_ids.py" 2>&1 | tail -25 # ← 与第 1 步同一条命令
git -C "$WT" checkout -- "$f" # 只还原净室里自己刚拷进来的那份
done
```
@@ -222,7 +222,7 @@ done
在飞文件,或存在文件间交互项**,不许就此交卷。
3. **定性增量是哪一类字面量**(只有「谁」没有「为什么」= 给不出可执行的改法):同一计数器在 **HEAD 版**与
**在飞版**各数一遍,**差值才是新增行带来的**(存量的不算这条道的账);再对新增行直接 grep。本次 16 处全是
**裸形 「字母 T 累进数字 4」**(`T` 在 `scripts/id_registry.json` 的 `bare_check_letters` 里,规范形态是连字符形),
**裸形 「字母 T 累进数字 4」**(`T` 在 `scripts/archive/id_gate/id_registry.json` 的 `bare_check_letters` 里,规范形态是连字符形),
全部落进 `skip_bare_unmanaged` —— 到这一步结论才配发给属主。
4. **闭合 + 复验主树一个字节没被污染**:
@@ -319,7 +319,7 @@ git -C "$MAIN" status --porcelain | sort # 与测量前**逐行一致**(
`ck = ci.Checker(reg, al, verbose=False)` → `ck.run()` → `print(ck.stats)`。别猜键名:先
`print(sorted(ck.stats))` 列出全量计数项,再按精确键取数(顺带 `git diff` 确认门禁脚本自己**没被改过**)。
- **别把「不可判定」当成一个数**(2026-10-27):`id-check` 有两种口径——检查器自己算的**硬不可判定**实测只有
**2** 处,而门禁红线(`scripts/id_registry.json` 的 `undecidable_total_ceiling`)量的是**派生值**
**2** 处,而门禁红线(`scripts/archive/id_gate/id_registry.json` 的 `undecidable_total_ceiling`)量的是**派生值**
`refs_total - decidable`(实测 8,744 - 7,582 = **1,162**,拆开 = `skip_bare_unmanaged` 834 +
`noise_allowed` 326 + 硬不可判定 2,**三数精确闭合**)。所以**带属主登记处的族(TC-XXX 这类)加多少引用都算
「可判定」、一点不吃这条余量**,真正吃余量的是**裸形字母+数字**字面量;反过来「把可判定引用改写成散文」才是