Files
STM32F4-Base/.dsh/skills/stale-doc-count-sweep/reference/steps.md
T
wandering1 88601422c3 docs(文档规范): 清空壳变更记录节,例外清单改成可复跑口径
17 篇文档的变更记录(含修订记录、带编号与中文序号标题)节里只剩一句
「不再记流水账」的占位话,整节删掉,连带它前面的分隔线。
仍保留真台账的只有 5 篇(看门狗 / 配置文件设计 / RAM 预算台账 /
内容页架构 / 本地工具路径方案),外加 scripts/archive 归档区。

份数口径改成可复跑的普查:文档规范 §5.1 补自查命令,
stale-doc-count-sweep 的速查表与陷阱各加一条——只 grep 裸标题会漏
带编号与中文序号的两种写法,实测 5 篇只数得出 3 篇,差点据此把
全仓正确的份数改错。

门禁:逐篇与全量 check_doc_links 除 simulator/SDL2 联接假红外 0 问题;
skill-size rc=0;表格列数与字节体检相对 HEAD 零新增问题。
2026-10-29 21:21:59 +08:00

9.6 KiB
Raw Blame History

Steps(自 SKILL.md 外置)

本文由 stale-doc-count-sweep 的 SKILL.md「Steps」一节逐字外置而来(渐进展开:主文件只留索引与指针)。步骤号沿用原文,未重编。

  1. 实测真值:先剔伪命中,再双源互证(不抄用户口述、不抄另一处文档)。逐数走上面那张表;两个来源对不上时先找分歧根因(把全部命中逐条打印出来看),不要取第一个、不要取平均,并在结论里写明分歧与取舍(实测:菜单 #page= 计数 29 对 注册表 28,根因是头部注释示例行)。

    cd "<repo>"
    ls simulator/tests/ref/shot_*.bin | wc -l                     # 张数(判据 = 入库实数)
    awk '/^PAGES = \[/,/^\]/' simulator/tests/check_content_pages.py | grep -cE '^ *\("'   # 页数(第二个来源,两者必须一致)
    grep -n '#page=' ui_menu_tree.txt | head -3                    # 先看首条是不是注释示例行,再定叶数
    grep -rn 'UI_PAGE_MAP_COUNT' Drivers/BSP/UI/ui_page_registry.h  # 已绑叶数(生成物)
    grep -c '\.bitmap_index' Drivers/BSP/UI/ui_font_cn_12.c         # 314 = 含 notdef;字形 = 该数 - 1
    & '.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']))"   # 钩子数口径
    

    ⚠ 权威脚本输出乱码时(check_font_charset.py 在 GBK 代码页下整行打印成 U+FFFD 替换字符串 + : PASS,数字全读不出来),改为直接数源文件里符号出现次数(.bitmap_index / .range_start / 码点唯一化)——不许臆读乱码,也不许因此退回去抄文档里的旧数。

  2. 全形态 + 全命中面扫描。一个数一次列出它全部历史形态,别只扫当前旧值:本轮 = 122/217、179/274、185/280、47 → 53、53 → 60、PAGES = 53、UI_PAGE_MAP_COUNT = 22、已绑 22 叶、未绑 11、内容页 53 页。

    grep -rn --include=*.md --include=*.py --include=*.yaml --include=*.txt --include=*.c --include=*.h \
      -E '122 唯一字|217 字形|179 汉字|274 字形|185 唯一字|280 字形|47 → 53|53 → 60|内容页 53|PAGES = 53|UI_PAGE_MAP_COUNT = 22|已绑 22|未绑 11' . \
      | grep -v -E '^\./(ci-artifacts|\.mnemon|\.dsh/runs)/'
    

    命中面比「文档」宽,至少包含:docs/**、AGENTS.md、.pre-commit-config.yaml(钩子 name: 显示名内嵌页数:name: UI 像素回归(菜单 8 状态 + 内容页 60 页 黄金参考) 就是命中点)、scripts/*.py、simulator/**/README.md、以及 .dsh/skills/*/SKILL.md(本窗 font-regen 正文自带 179/274 计数,直到 step 150 才发现;编号门禁退役那轮 id-family-semantic-migration、task-worktree 两个 skill 正文与 scripts/README.md 同样是引用面)。

    先列出 文件:行:全文,判定前必须看整行——cut -c1-120 会截掉后半句(那里常写着日期戳或口径)。

  3. 逐条分类,五类各走各的路(本 playbook 的核心,别用一条规则套全部命中):

    类别 判定 动作
    当前态陈述 描述「现在是多少」 改成实测真值
    变更记录历史行 形如「47 → 53 页」的当日事实 原样冻结不回改——但这条只适用于保留变更记录的 5 篇真台账(台账 / 被别处当唯一证据出处的文档,清单 → docs/文档规范.md §5.1)与 scripts/archive/ 归档;其余文档没有变更记录节,不再追加任何流水行,数字同步只改当前态
    同数不同义 最坏路径 < 40KB 这类内存/绘制缓冲红线 不动,并在结论里写明「为什么不动」
    带日期戳的测量 「2026-10-26 实跑 …覆盖当时 40 页中的」 不臆改新数,只补「当时」两字锁口径;新数留待下次实跑回填
    数字对但口径描述错 数没错、句子把口径讲错了(实测:写「218 字(含 ASCII/符号,共 313 字形)」,而 ui_cn_chars.txt 的 218 个码点全为非 ASCII) 另起一笔只改口径的提交(6daa01ae),不与数字同步混笔;这类命中在任何数字 diff 里都看不见,只能把口径句逐句读一遍

    拿不准就回到第 2 步把整行打印出来再判;判错历史行 = 把当天事实改成假账。

  4. 每处先读现文,再逐条字面替换并断言命中数(弃用整文件正则批处理)。本窗两度返工都是没先读现文:step 45 断言 HIT 0 抛错(目标串与现文不一致),step 44 是脚本语法错。脚本写成临时文件再跑(内嵌 heredoc/模板字符串里的反引号会截断整脚本),按字节替换以保 BOM 与行尾不动:

    import pathlib, sys
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
    JOBS = [  # (文件, old, new, 期望命中数)
        ('AGENTS.md', '内容页 53 页黄金参考', '内容页 60 页黄金参考', 1),
        ('.pre-commit-config.yaml', '内容页 53 页 黄金参考', '内容页 60 页 黄金参考', 1),
        ('docs/架构设计/项目架构总览.md', '122 字/217 字形', '218 字/313 字形', 1),
        ('docs/UI设计/UI_自动化测试流程.md', '文本断言 **310 条**、覆盖 40 页中的', '文本断言 **310 条**、覆盖当时 40 页中的', 1),
    ]
    for path, old, new, expect in JOBS:
        p = pathlib.Path(path); raw = p.read_bytes()
        o, n = old.encode('utf-8'), new.encode('utf-8')
        got = raw.count(o)
        assert got == expect, f'{path}: 命中 {got},期望 {expect}(未写入)'
        assert o != n, f'{path}: old 与 new 相同'
        p.write_bytes(raw.replace(o, n))
        print(f'OK {path}: {old} -> {new} x{got}')
    

    每行打印 old -> new xN,收口时这串输出就是「改了哪几处」的账。脚本报错/没编译过,先 git diff --stat 确认零写入再换写法(不要在半落地状态上叠改)。

    • 反引号:TS 侧先用可见占位符(如 ~TB~)写文本,落盘前再还原成真反引号 —— const BT = String.fromCharCode(96); const q = s => s.split(TOKEN).join(BT)(本窗实测:直接把带反引号的串塞进模板字符串/heredoc,整批零落地或把变更记录行写坏)。用 tools.edit 批量改时同样先 await tools.read 现文再传 old_string;批量里若某条 old_string 与 new_string 相同会报 must differ —— 只有该条作废、其余已落盘,别整批重跑。
    • 历史行被写坏(反引号及紧随其后的字符经工具传递被吞 → 变更记录行只剩半行、尾部出现 ?? 这类残迹):立刻按无反引号兜底写法整行重写——裸词 accident / oper / alarm / start / yk、页面绑定写成不带反引号的 #page=——而不是拿同样带反引号的串再改一遍(实测连返工三次:step 81→82→83→85)。
  5. 一笔只装一类事。数字同步一笔(cbb4fdb8:11 处 + 变更记录一行);口径更正另起一笔(6daa01ae);提交后复扫逮到的漏网当前态再补独立一笔(11d60057)。钩子一增一减写成一个当日行的两个阶段(51 → 52 加 id-check-new、随即 52 → 51 摘 id-check),别写两行互相打脸。提交说明如实写(「先认一笔自己的错:上一笔的未绑 21 叶是探针错」),别把口径修正包装成「补充说明」。

  6. 被改文件全文通读,核对其余小节——不是只看 diff。同一文档内互相引用的小节必须一致(本窗产品需求:§2.4 立项未排、§5.14 FR-SOE-3、§8.2 绑定现状、§8.3 已绑分页表、§9.1 差距表、§9.2 顺序表六处同时引用这四个数;docs/任务规划/README.md §1 当前执行表 vs §2 全项目顺序表)。若发现上一笔自己写进文档的数口径就错了,明确认错并改正,同时把可复核的重算方法写进文档(「按缩进判叶子:有更深缩进后继者即非叶子」),防止再错。

  7. 提交后再复扫一遍(收尾硬步骤,不许省,也不许只在提交前扫)。按第 2 步的全部旧形态整仓再扫一次,逐条分类为 当前态 / 历史行 / 同数不同义:本窗 cbb4fdb8 提交后复扫得 14 条命中,12 条以上都在变更记录历史行(47 → 53、53 → 60、122/217 冻结),唯一残留的当前态命中是 docs/架构设计/项目架构总览.md §3「界面层」那行 → 单独补一笔 11d60057。说不出归属的就是漏改。

    grep -rn -E '22 叶|未绑 11|11 叶|53 页|122 唯一字|217 字形|185 唯一字|280 字形|内容页 53|PAGES = 53|UI_PAGE_MAP_COUNT = 22' \
      . | grep -v -E '^\./(ci-artifacts|\.mnemon|\.dsh/runs)/'
    
  8. 跑门禁、分笔提交(git commit 属 Ask first,须用户明确指示后再做):

    & '.venv\Scripts\python.exe' scripts/check_doc_links.py --all --sections-strict   # 期望「文档路径检查通过(N 个文档)」
    & '\.venv\Scripts\python.exe' scripts/archive/id_gate/check_ids_new.py --range A..B   # 编号形态普查(两道门禁 2026-10-28 / 2026-10-29 都已退役,不再是提交钩子)
    & '.venv\Scripts\python.exe' scripts/ci_local.py                                    # 或 pre-commit run --files <被改文件>
    

    PowerShell 形态把 ./.venv/Scripts/python.exe 换成 & '.venv\Scripts\python.exe'。提交信息一律 git commit -F <UTF-8 无 BOM 文件>,别用 -m(中文长标题易超 100 字符、终端代码页会转码);标题 ≤100、正文每行 ≤80(实测把正文压到 66/78 字符才过 gitlint)。