用户令逐字:「E:/ProgramData/.workbuddy/skills 提交仓库是指的这里」—— 即本目录就是仓库(2026-10-07 已在本目录建仓,见当日日志 §22),本轮把余下未纳管的 9 个技能一并提交。 本次入库(9 个技能,46 个文件): 1、`AI HOT` 2、`draw-ui` 3、`dsh-diagnose` 4、`dsh-knowledge` 5、`dsh-local-env` 6、`dsh-opensource-release` 7、`dsh-workflow` 8、`oil-motion` 9、`skills-security-check` 提交前核对: · **凭据类扫描**(`*.env` / `*token*` / `*.key` / `*secret*` / `*.pem`)⇒ **零命中** ✓; · 体积合计约 20 MB(`draw-ui` 12M + `oil-motion` 6.5M 是大头,形态为配图/素材 —— 仓库 `.gitignore` 里明写「`assets/*.png` 是内容不是产物」⇒ 属刻意入库); · 运行产物仍按既定规则排除(`__pycache__` / `logs/` / `tmp/` / `.venv/` / `*.egg-info` / `uv.lock` / `.workbuddy/`)。
8.0 KiB
技能重组 · 千行技能拆分(SKILL.md 主干 + references/ 详情档)
归属:技能
dsh-knowledge-upkeep的详情档(按需读,不是每次都要读)。 本档覆盖:§10 的执行清单 · 通用拆分器用法 · 2026-09-22 首次实战的实测数据与决策记录 · 坑。 主文件 / 判据 =../SKILL.md(§2 分层判据 · §8.6 注入预算 · §9 技能集体检 · §10 技能重组)。 来源:2026-09-22「技能重组线」首跑后沉淀(首次对象 =dsh-change-workflow/dsh-opensource-release)。
1. 为什么要有它(一句话)
技能长到千行级 ⇒ 每次加载都全量注入,而里面大半是长表 / 实测记录 / 历史细节。拆成「主干 + 详情档」不是审美,是把注入预算还给判据(同 §8.6:长文件后半段等于不存在)。
与相邻机制的分工:
| 机制 | 管什么 |
|---|---|
| §9 技能集体检 | 技能挑不挑得中(description 区分度) |
| §10 技能重组 | 技能加载后吃多少上下文 / 判据找不找得到(文件内部结构) |
session-mechanism §10.1 |
要不要把多个技能合并 |
dsh-architecture-lifecycle |
架构过程文档 vs 定稿的分层 |
2. 通用拆分器 scripts/split_skill.py
<python> scripts/split_skill.py --spec <spec.json>
规格格式(照抄改):
{
"skills_dir": "E:/ProgramData/.workbuddy/skills",
"backup_dir": "E:/ProgramData/AIProject/ai1net-dsh-server/tmp/_keep-20260922/split-bak",
"skill": "dsh-change-workflow",
"main_desc": "§1 六阶段流程 · §2 红线速查 · §3 Git Bash 坑",
"moves": [
{ "ranges": [[14, 92]],
"ref": "00-平台速查.md",
"title": "平台速查(硬编码事实,勿猜)",
"covers": ["平台速查(硬编码事实,勿猜)"],
"pointer": "> 📂 **平台速查表已下沉** → `references/00-平台速查.md`" }
]
}
要点:
ranges= 1-based 闭区间,可多段([[481,504],[555,600]]),不得重叠;一段一个ref。pointer= 插在该组区间起点位置的指针行(可多行用\n);要点名该档覆盖的章节,否则违反验收 ③。covers= 原章节名,用于生成主干的「详情档 × 覆盖的原章节 × 原行段」表(跨档引用的定位表)。--dry-run只看校验,不落盘。
脚本内置自校验(任一 FAIL 就必须停下来看,别硬推):
- 行数守恒:
主干内容行 + 详情行数 == 原行数; - 逐行包含:
Counter(原行) <= Counter(主干非指针行 + 全部详情行)⇒ 丢失 0; - 节可寻址:原文件每个
^#{1,3}标题文本都能在新结构(主干 + 任一详情档)里找到; - frontmatter:
version:行唯一且值不变。
幂等:脚本永远以备份为拆前基准 ⇒ 反复跑不会二次切割(⚠️ 首次失误版本读的是活文件,第二次跑就 AssertionError —— 这个坑已修,别再写回活文件)。
3. 2026-09-22 首跑实测数据(两个对象)
| 技能 | 原 SKILL.md | 主干 | 详情档 | ≤350 目标 |
|---|---|---|---|---|
dsh-change-workflow |
1052 行 | 319 行 | 9 档 / 867 行 | ✅ 达标 |
dsh-opensource-release |
1089 行 | 534 行 | 5 档 / 629 行 | ❌ 多 184 行 |
- 守恒实测:
275 + 777 = 1052|510 + 579 = 1089(逐行比对丢失 0;新增的 34 / 19 行全是指针与索引行)。 - 可寻址实测:原 51 / 68 个标题,未命中 0。
- 三处对账:16 个文件(2 主干 + 14 详情档)三方 md5 全一致。
为什么 opensource-release 到不了 350(决策记录):它的「不看会违规/事故」常驻项合计约 453 行 —— 硬规则 R-O1–R-O17 239 + 事故清单 40 + 事实 34 + 硬规则总表 23 + 用户当场纠正口径 16 + 授权结构 35 + 验证八件套 37 + SOP 主干 29。压到 350 的唯一路径是把 R-O 硬规则降级成指针 ⇒ 违背「任何会话不得放宽」。当时的选择 = 判据常驻优先,如实报告数字,没有为凑行数动硬规则(对应用户「确保功能效果不受影响」的硬约束)。
4. 断头引用:怎么扫、怎么修
扫(正则挑跨节引用,逐行打印,人工过一遍):
见上文|见下文|见前文|见后文|见上表|见下表|见上面|见下面|上文|下文|见 §|见§|参见|详见|见阶段|见坑|见附
实测命中 5 处(目标全在详情档里):见下表:dsh-univer-office(→ 保留/移除档)· 见 §8 坑 9、见坑 36(→ 实测坑档)· 详情档之间的 见 §8 坑、见坑 31–36。
修法(不删字):主干末节加「详情档 × 覆盖的原章节 × 原行段」表 + 一句「编号引用按本表定位(下沉后原编号不再有独立章节标题)」。详情档头里也写一句同样的指引。 ⛔ 不要去改正文里的引用句(那会破坏「逐行未改」的可校验性)。
5. 坑(都踩过)
- 必须从备份重建:否则第二次跑读到的是已拆过的活文件,区间校验直接
AssertionError。 - 详情档编号要按文档顺序:首跑把「浏览器验证栈」编成
08、而「并行调度」是07(因为它先被创建)⇒ 索引表出现…06 / 08 / 07的倒序。修法:生成索引表时对档名sorted()。 - 行尾只信字节级:拆分后要确认新文件是纯 LF(
CR=0);文档库INDEX.md是 CR/LF 混排(CR=5/LF=270)⇒ 改它只做字节级单行插入,⛔ 别整文件 Write。 - 登记跟改用「精确串替换器」:每个
old必须命中恰好 1 次,任一不中 ⇒ 整体不落盘(比逐次 Edit 可靠、比sed安全,非 ASCII 不被改写)。 - 环境:本轮本机
bash包装器整体起不来(ls/cat也command not found、export PATH救不回)⇒ 全程改走 PowerShell + 托管 python:脚本先 Write 成.py、输出落文件再 Read。⚠️ MSYS 路径(/e/…)⛔ 不许交给 Windows 原生程序(会落到E:\e\…影子目录)。 - 对账用 Python 算 md5,别用
md5sum:本机输出hash *path、远端hash path,多一个空格会让逐行比对假失败。 - 按技能名匹配表格行会误命中:用
NAME in line找登记行时,别的技能行常在描述里提到这个技能名(实测:README 的dsh-architecture-lifecycle行写着「与dsh-knowledge-upkeep互补」⇒ 两行同时命中,登记被挂到了错行,且我按「行里含对方名」做回退时又同时命中两行、把两行都清了)。 判据 = 只认行首单元格:l.startswith("|08-skills//"),且命中数必须恰好 1;改动前后各打印一次「命中行号 + 行首 34 字」自证。 - 判定远端登记文件是否「只是陈旧」——别用 SequenceMatcher 的 opcode 判据:我要求「opcode 全为 equal/delete」时,
**与|密集的长表格行会给出假 replace ⇒ 误报「远端含本机没有的内容」而停手(实测:README 3 行 / INDEX 4 行全被误报,实际覆盖率 1.000)。 正确判据 = 整行子序列覆盖率:sum(b.size for b in SequenceMatcher(None, 远端行, 本机行).get_matching_blocks()) / len(远端行) >= 0.98(远端只被删减,本机只做插入)⇒ 可安全推。更省事的 oracle = git:git show HEAD:<f>与远端比 —— 相等即"远端 = 已提交基线,差异全是未提交改动"。 /opt/dsh/docs/根也镜像文档仓根:README.md(技能登记表)与INDEX.md(清单与状态)在服务器上各有一份 ⇒ 三处同步不能只算08-skills/。实测 2026-09-22:skills 16 文件三方 md5 全一致,而根 README/INDEX 落后一整轮登记(差异恰为被改的 3 + 4 行)。推送后chmod 600+chown root:root,旧副本备份到/opt/dsh/backups/docs-root/。