Files
STM32F4-Base/.dsh/skills/stale-doc-count-sweep/SKILL.md
T
wandering1 251268992e docs(skills): 收录陈旧计数同步流程 + 补台账回填动作 + 去掉技能文里自伤门禁的裸形
三只 skill 文件收口(队友写成,Lead 核后入库):

· 新增 `stale-doc-count-sweep`:把今天做的「一个事实数变了之后全仓同步」钉成流程——从权威来源**实测**真值
  → 全形态扫描并**逐条分类**(当前态改 / 变更记录历史行只加当日新行 / 同数不同义的误报不动 / 带日期戳的
  测量只加「当时」锁口径)→ 逐条字面替换且每处断言命中数 → 被改文件全文通读核各小节 → 复扫残留归零 →
  跑门禁分笔提交。这份流程的价值全在**分类**那步:今天有 5 处「27 → 40」是历史事实、1 处「< 40KB」是同数
  不同义(绘制缓冲红线),一并替换就会毁掉它们。
· `mem-budget`:补上 `ram_baseline.py --update`(只往 tsv 追加趋势行)与 `--write`(才同
步台账受管区)的区别、
  以及「提交前台账怎么回填」的动作——这个坑是 Lead 本人 05:0x 踩的(用错一枚参数造出一次真的 mem-budget
  FAIL),写下来比口头转告可靠。
· `task-worktree`:把文中 4 处**自伤**的裸形改成形态描述——该节讲的正是「单字母紧跟数字」这类形态怎么被
  校验 2 计数,结果例子自己就是那种形态,等于技能文自己吃门禁余量。改完不可判定绝对数 **982 → 978**
  (余量 +4),叙述信息量不减:形态与属主都还写清。

⚠ 这条教训值得单记:**文档在讲某个反模式时,自己不要用那个反模式的字面量**——否则每次读它的人都得替它
付余量。同批 `docs/编号规范.md` §4.4 也已登记这类误判面。

自查:`check_ids --strict` exit 0、不可判定绝对数 978 / 上限 1,166;三文件 noBOM / LF / U
+FFFD=0 / 结尾换行齐。
2026-10-27 09:11:18 +08:00

9.1 KiB
Raw Blame History

name, description, whenToUse
name description whenToUse
stale-doc-count-sweep 全仓陈旧计数同步 playbook——某个事实数(黄金参考页数、菜单树叶数、钩子数等)变了之后,从权威来源实测真值 → 全形态扫描命中点并逐条分类(当前态改 / 变更记录历史行只加当日新行 / 同数不同义误报不动 / 带日期戳的测量只加「当时」锁口径)→ 逐条字面替换且每处断言命中数 → 被改文件全文通读核对各小节 → 复扫残留当前态命中归零 → 跑门禁分笔提交。 某个计数/数字变了要跨 md/yaml/py 同步、用户说「同步一下 / 全仓改一遍 / 这个页数(叶数、钩子数)不对」、发现文档数字与实测不符、或同一份文档两节数字/顺序对不上要先核口径时。

陈旧计数全仓同步

适用:一个事实数变了(黄金参考页数 27→40→47、菜单树叶数、钩子数…),仓库里多处引用它的当前态表述要跟着变,而历史记录、同数异义、带日期戳的测量不能跟着变。

同一流程在本仓已跑过两次(b24b2b5f 27→40、13 个文件;31ca4a1f 40→47、11 个文件)——照下面的序列逐字复跑,别临场重新发明分类。

Steps

  1. 实测真值,不抄任何二手数字(不抄用户口述、不抄另一处文档)。

    cd "<repo>"
    ls simulator/tests/ref/*.bin | wc -l      # 内容页黄金参考页数(唯一判据 = 入库的 shot_*.bin 实数)
    

    菜单树叶数这类来源自带口径规则的数,先读来源文件头再计数:

    sed -n 1,12p ui_menu_tree.txt             # 文件头写明「缩进即层级,每级 2 空格;# 开头为注释;空行忽略」
    

    叶子判据 = 不存在更深缩进后继行的行。把「节点行数」「叶子总数」「已绑」「未绑」四个数一次报全,并让 未绑 = 待做 + 不做 与分类表逐项闭合。实测量到 42 个节点行、33 个叶子、已绑 21、未绑 12(= 待做 7 + 不做 5)。

  2. 全形态扫描,一个形态都不许只扫一种写法:

    grep -rn --include=*.md --include=*.py --include=*.yaml --include=*.txt \
      -E '40 *(页|张|帧|个页面)|内容页.{0,6}40|黄金.{0,6}40' .
    

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

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

    类别 判定 动作
    当前态陈述 描述「现在是多少」 改成实测真值
    变更记录历史行 形如「27 → 40 页」的当日事实 原样保留;只在该文件 §变更记录顶部加一行当日的「40 → 47」(含判据与实测来源)
    同数不同义 最坏路径 < 40KB 这类内存/绘制缓冲红线 不动,并在结论里写明「为什么不动」
    带日期戳的测量 「2026-10-26 实跑 …覆盖 40 页中的」 不臆改新数,只补「当时」两字锁住口径;新数留待下次实跑回填

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

  4. 逐条字面替换,每处断言命中数(弃用整文件正则批处理)。脚本写成临时文件再跑(内嵌 heredoc/模板字符串里的反引号会截断整脚本),按字节替换以保 BOM 与行尾不动:

    import pathlib, sys
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
    JOBS = [  # (文件, old, new, 期望命中数)
        ('AGENTS.md', '内容页 40 页黄金参考', '内容页 47 页黄金参考', 1),
        ('docs/UI设计/UI_自动化测试流程.md', '该集合现已扩至 40 页', '该集合现已扩至 47 页', 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}(未写入)'
        p.write_bytes(raw.replace(o, n))
        print(f'OK {path}: {old} -> {new} x{got}')
    

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

  5. 被改文件全文通读,核对其余小节——不是只看 diff。检查同一文档内互相引用的小节(典型:docs/任务规划/README.md §1 当前执行表 vs §2 全项目顺序表;UI补齐.md §1 口径 vs §2 清单):顺序、计数、完成状态必须一致。若发现上一笔自己写进文档的数口径就错了(例如把 42 个节点行当叶子数写成「未绑 21 叶」),明确认错并改正,同时把可复核的重算方法写进文档(「按缩进判叶子:有更深缩进后继者即非叶子」),防止再错。

  6. 复扫残留,排除历史行与误报后,当前态命中必须为 0:

    grep -rn --include=*.md --include=*.py --include=*.yaml -E '40 (页|张)|40 个内容页' \
      AGENTS.md .pre-commit-config.yaml docs scripts | grep -v '27 → 40'
    

    剩下每一条都要能说出归属(历史行 / 误报 / 已加「当时」)。说不出来的就是漏改。

  7. 跑门禁,分笔提交(git commit 属 Ask first,须用户明确指示后再做):

    ./.venv/Scripts/python.exe scripts/check_doc_links.py --all
    ./.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 字符、终端代码页会转码)。

    一笔只装一类事:计数同步一笔、口径/登记簿修正一笔。提交说明如实写(如「先认一笔自己的错:上一笔的未绑 21 叶是探针错」),别把口径修正写成「补充说明」。

Pitfalls

  • 照抄用户给的数:用户说「从 40 扩到 47」也要自己数 ref/*.bin;数出来不一定是 47。真值只认权威来源实测。
  • 把节点行当叶子数:42 是 ui_menu_tree.txt 的节点行数,33 才是叶子;文件头写着「缩进即层级」,不读规则就数错(实测把错数写进两份文档,下一轮才自查出来)。
  • 整文件正则批处理:第一次尝试的正则没编译过、整批零落地。改法 = 逐条字面替换 + 每处 assert count == expect,宁可慢也要每处可审计。
  • 反引号截断:脚本里含 ui-golden、#page= 这类反引号时,经 TS 模板字符串/内嵌 heredoc 传递会被截断,表现为「整批零落地」。把脚本落成文件再执行,或用 chr(96) / String.fromCharCode(96) 拼。
  • 只改记得的那节:上一笔只改 §2 与开篇、漏改 §1,用户当场问出「§1 说 16 叶、§2 说剩 3 项,自相矛盾」。改完必须全文通读同一文档的所有小节。
  • 历史行被当陈旧值改掉:变更记录里的「27 → 40」是当天事实,改了就是伪造历史;正确动作是另加当日新行。
  • 误报跟着改:「< 40KB」是绘制缓冲红线,与页数无关,改了就是引入错误。
  • 带日期戳的测量臆改新数:「2026-10-26 实跑取数 …覆盖 40 页」改成 47 就是把历史测量冒充现值;只加「当时」。
  • 用截断的 grep 输出判定:cut -c 截掉的后半句常含日期戳/口径,据此分类必错。
  • 新写文本撞编号门禁:新增的「单字母+数字」裸形(如 SD2506、CP56Time2a)会把 id-check 的不可判定引用绝对数推高(实测 1156 → 1161,上限只剩 5)。收尾看门禁输出的 und 计数,涨了先把自己这批文本逐处归因,别去放宽 noise_literals。

Verification

提交前逐条打勾:

  • 真值来自本轮实测命令的输出(ref/*.bin 计数 / 按来源规则重算的叶数),并在结论里贴出该数字
  • 分类表逐条列出:改了哪些当前态、哪些历史行保留、哪些误报不动(附理由)、哪些只加「当时」
  • 替换脚本打印了每处 old -> new xN,且每处 assert 通过(无半落地状态;报错时已确认零写入)
  • 被改文件已全文通读,同文档各小节(§1/§2)与配套 README/清单的顺序、计数一致;口径闭合(如 未绑 = 待做 + 不做)
  • 复扫输出中当前态残留 = 0,每条剩余命中都能归类
  • check_doc_links.py --all 打印「文档路径检查通过(N 个文档)」;ci_local.py / pre-commit 全绿(passed=N failed=0、RC=0)
  • 分笔提交完成,回报 commit 号 + 每笔改了哪些文件 + 没动的那几类及理由