251268992e
三只 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 / 结尾换行齐。
118 lines
9.1 KiB
Markdown
118 lines
9.1 KiB
Markdown
---
|
||
name: stale-doc-count-sweep
|
||
description: 全仓陈旧计数同步 playbook——某个事实数(黄金参考页数、菜单树叶数、钩子数等)变了之后,从权威来源实测真值 → 全形态扫描命中点并逐条分类(当前态改 / 变更记录历史行只加当日新行 / 同数不同义误报不动 / 带日期戳的测量只加「当时」锁口径)→ 逐条字面替换且每处断言命中数 → 被改文件全文通读核对各小节 → 复扫残留当前态命中归零 → 跑门禁分笔提交。
|
||
whenToUse: 某个计数/数字变了要跨 md/yaml/py 同步、用户说「同步一下 / 全仓改一遍 / 这个页数(叶数、钩子数)不对」、发现文档数字与实测不符、或同一份文档两节数字/顺序对不上要先核口径时。
|
||
---
|
||
|
||
# 陈旧计数全仓同步
|
||
|
||
适用:**一个事实数变了**(黄金参考页数 27→40→47、菜单树叶数、钩子数…),仓库里多处引用它的当前态表述要跟着变,而历史记录、同数异义、带日期戳的测量**不能**跟着变。
|
||
|
||
同一流程在本仓已跑过两次(`b24b2b5f` 27→40、13 个文件;`31ca4a1f` 40→47、11 个文件)——照下面的序列逐字复跑,别临场重新发明分类。
|
||
|
||
## Steps
|
||
|
||
1. **实测真值,不抄任何二手数字**(不抄用户口述、不抄另一处文档)。
|
||
|
||
```bash
|
||
cd "<repo>"
|
||
ls simulator/tests/ref/*.bin | wc -l # 内容页黄金参考页数(唯一判据 = 入库的 shot_*.bin 实数)
|
||
```
|
||
|
||
菜单树叶数这类**来源自带口径规则**的数,先读来源文件头再计数:
|
||
|
||
```bash
|
||
sed -n 1,12p ui_menu_tree.txt # 文件头写明「缩进即层级,每级 2 空格;# 开头为注释;空行忽略」
|
||
```
|
||
|
||
叶子判据 = **不存在更深缩进后继行**的行。把「节点行数」「叶子总数」「已绑」「未绑」四个数一次报全,并让
|
||
`未绑 = 待做 + 不做` 与分类表逐项闭合。实测量到 42 个节点行、33 个叶子、已绑 21、未绑 12(= 待做 7 + 不做 5)。
|
||
|
||
2. **全形态扫描**,一个形态都不许只扫一种写法:
|
||
|
||
```bash
|
||
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 与行尾不动:
|
||
|
||
```python
|
||
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**:
|
||
|
||
```bash
|
||
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,须用户明确指示后再做):
|
||
|
||
```bash
|
||
./.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 号 + 每笔改了哪些文件 + 没动的那几类及理由
|