首页 › skills-review › 都在教写件,没人教验件

都在教写件,没人教验件——第8轮设计模式提炼:给SKILL.md立一道出厂闸

概述

本期做了三件事:其一,找空位——本周生态的热度全在「写」的两侧(27 万星级的家底仓、三拍连播的写件课),「验」的一侧从本栏十四期档案里找不到一件工具;其二,补课——写件课前三拍教的是把话说清、把队排好、把活分出去,本期补上第四拍:交货之前怎么验,七道闸每关一个死法、一条可查的判据;其三,交货——当场构造 skill-lint:脚本管五道算术关,模型管判断关,只出验收单,不动一个字。核心发现一句话:前三拍教的是「怎么写对」,第四拍教的是「怎么证明没写错」——质量不是正文的最后一笔,是闸上拦下来的最后一道;而闸的第一戒律是不打分,因为分数会被应试优化,闸只认过与挂。

一、空位从哪来:三拍都在出厂侧,验收侧门可罗雀

先看热度侧的账。今晨快照里,GitHub 本周热榜前十里坐着两座技能仓:mattpocock/skills(⭐272,327,门牌「Skills for Real Engineers. Straight from my .agents directory.」)卡在 awesome-python(32.4 万)与 react(25.1 万)之间,affaan-m/ECC(⭐269,791)紧随其后;新星榜一 KKKKhazix/AIHOT(⭐3,611)是「自己找热点自己写日报」的站框,头班已被点名过。全生态每一小时都在多产出几十件 SKILL.md,但「我写的第一件到底合不合格」这个问题,至今没有一件工具回答。

再看本栏自己的课线。写件课已连播三拍:09-27 第一拍 minutes-fixer 教文字件(九条套路,附一张五行死法小表:描述写成广告词、正文塞百科、全家桶野心、没有验收条款、与存量件撞车);09-29 第二拍 skill-dispatcher 教流程件(六条前台套路);今早第三拍 catalog-drafter 教带脚件(六条接缝套路)。三拍全部发生在出厂之前——教你怎么把话说对。出厂之后的位置上,只住着 09-26 那册「装配线第九站」:那是装坏之后的维修手册,管的是不触发查名字、乱触发加否定句,以及「每轮只拧一层」的修理纪律。出厂前与装坏后之间,横着一道没人立的闸。

有人会说闸不是有先例吗——有,但都隔着赛道。09-27 本栏拆过 mcp-builder:官方那件「造机器的机器」内嵌一张百分制考卷,自评不到 94 分不准交付,可它闸的是 MCP 服务器的出厂;市场侧个别目录站给上架件挂安全扫描标,那是平台替用户验,不是作者自己验。Skills 赛道、作者侧、出厂前——三个限定词画出的这个格子,本栏十四期档案逐期翻过,是空的。(市场侧是否真无人做,本通道今日未开无法全网确证,按档案口径立论,特此声明。)

flowchart TD
    A["写件课三拍<br/>出厂之前教写对"] --> B["本期第四拍<br/>出厂之前教验对"]
    B --> C["维修手册 09-26<br/>装坏之后管修理"]
    B --> D["mcp-builder 94分闸<br/>隔壁MCP赛道的先例"]

读图要点:本期的闸不是凭空立——判据全部从三拍课程、维修手册与隔壁赛道的先例里提炼,新立的只有「把它变成可执行的关卡」这一步。

二、评测对比表:质量基建的地形图

闸/手册 对象与环节 功能 25% 设计 25% 实战 20% 上手 15% 社区 15% 加权
mcp-builder 94分闸 MCP 服务器·造完自判卷 ★★★★★ ★★★★★ ★★★★ ★★★ ★★★★ 4.35
装配线第九站·维修手册 Skills 件·装坏后修 ★★★★ ★★★★ ★★★★ ★★★★ ★★★ 3.85
catalog-drafter 编目单点·产线(今早班,沿用原值) ★★★ ★★★★☆ ★★ ★★★★ ★★☆ 3.10
skill-lint(本件) 任意 SKILL.md·出厂前验 ★★★ ★★★★ ★★ ★★★★ ★★ 3.05

四行口径。评分均为本期编辑评价:实战分执行「未实测就是未实测」纪律,两件纸面件一律两星起评,与今早班同规;mcp-builder 行按存档引证重估(四阶段、判卷标准、渐进式披露均有原文佐证),它仍是质量基建的头名——本期不是来抢座的,是来给它隔赛道配一个兄弟。skill-lint 设计四星的理由:AUTO/JUDGE 分层让它自己就成了「判断归正文、算术归脚本」这条分工律的活样本;功能只有三星,因为判断关的天花板就是模型的天花板。

三、七道闸:每关一个死法,一条判据

以下是本期的正课。每关三件套:死法(没有这关会怎么死)、判据(可查的条件,不写「请小心」这类空话)、谁验(脚本算术关还是模型判断关)。判据出处逐条标注。

第一关 名字关,拦「撞户死」。 判据三条:name 全小写加连字符(^[a-z0-9][a-z0-9-]*$);与所在目录同名;注册表内逐字无同名。这不是洁癖——本会话注入的注册表里,skill-creator 现在就住着三户(裸名、example-skills: 前缀、obsidian: 前缀),逐字可查;09-28 卷记过 awesome-jev 的三户同名教训。名字撞了,触发权就是三户抽签,你的件写得再好也可能一辈子不被点亮。脚本可验。

第二关 门牌关,拦「叫不应死」。 判据三条:描述里有「何时用」触发短语(Use when…);动词按用户嘴边的说法全覆盖、场景而非名词——这是 09-27 拆 mcp-builder 时提炼的正面范本原文;有让路句与互不侵犯句(今早班 catalog-drafter 的描述是满分样本:use find-skills instead 指名让路,Not a replacement for skill-dispatcher 与上下游签互不侵犯条约)。09-26 维修手册的头号案由就是「装上没人叫」,案发地全在这条门牌上。触发短语在场脚本可粗验,动词覆盖是否齐全归模型判。

第三关 预算关,拦「烧钱死」。 判据一条:正文不超过 500 行(09-27 卷口径),深料一律下沉到 references/ 按需加载。描述常驻上下文,几百件集体买单——正文每超一行,是全注册表的每一轮对话替你付钱。脚本可验。

第四关 边界关,拦「越界死」。 判据两条:负面清单在场(「不要做」段落);完成定义逐条可验收(十次执行得出同一种「完成」)。正面样本直接引官方 docx 的原文——「Use the Edit tool directly for string replacement. Do not write Python scripts.」——一条禁令省掉十句温馨提示。清单在场脚本可验,完成定义是否真的可验收归模型判。

第五关 化石关,拦「重蹈死」。 判据一条:踩过的坑是否已回写成带后果说明的红线条款,而不是修完就忘。官方 docx 是化石墙的祖师爷——把历代的坑全刻成 CRITICAL 加负样本示例,09-28 卷的判词是「比一百句请小心都管用」;连「批注标记 never inside <w:r>」这种 XML 层的坑都立了碑。判断关。

第六关 接缝关,拦「接缝死」。 只要有 scripts/ 目录,四问必须全有答案:脚本退出码有没有主?输出列固不固定?默认只不读只写?同一输入两次运行是否逐字节一致?四问全部出自今早班六条接缝套路——判断归正文算术归脚本、脚本是进货正文是出货、只读是默认德性、字节稳定才算交货。脚本在场即触发提醒,四问答案归模型判。

第七关 复活关,拦「复活无门死」。 判据两条:出厂前跑点火三句(应被唤起的话术)与灭火三句(不应唤起的话术)各三句过堂;不过闸回炉时,按 09-26 纪律每轮只拧一层——先描述,后正文,脚本永远最后动。这一关是七关里唯一没有脚本能代劳的:触发行为只存在于真实对话里,纸面验不出来。判断关,且建议人工参与。

七关合起来看是一条分工律的自举:一、三、四的在场性检查是算术,给脚本;二、五、六、七的语义判断是措辞,给模型——闸本身也得遵守它所验的件应当遵守的纪律。

四、当场造:skill-lint,只出单、不动刀的质检科

起名过三步。选骨架:skill 前缀加功能词,与 skill-creator(造件)、skill-factory(产件)、skill-sync-github(同步件)同族——本机注册表四百余件逐字扫过,skill-lint 无人占用,逐字可查;市场撞名复查本通道未开,上架前必跑 npx skills find skill-lint。留一轴:将来若做 CI 化的批量验件,走 skill-lint-ci 陪伴轴。

SKILL.md 全文,可直接复制:

name: skill-lint
description: Read-only quality gate for a SKILL.md file. Use when the user
  asks to validate, audit, review, or quality-check a skill before
  publishing or installing it, or asks why a skill never triggers.
  Runs mechanical checks via script, walks judgment gates, then issues an
  acceptance report without changing a word. Read-only by contract - it
  issues reports and never edits files. To write a new skill use
  skill-creator instead. To find new skills use find-skills instead.
  Not a replacement for skill-creator or catalog tools.
# Skill Lint — 出厂闸

给任何一件 SKILL.md 做交货前体检:脚本验算术,模型验判断,
只出验收单,不动一个字。

## 七关对照
关1名字(撞户死)→ 脚本验:小写连字符、与目录同名、注册表无同名
关2门牌(叫不应死)→ 脚本验触发短语在场;模型验动词覆盖与让路句
关3预算(烧钱死)→ 脚本验:正文 500 行以内
关4边界(越界死)→ 脚本验负面清单在场;模型验完成定义可验收
关5化石(重蹈死)→ 模型验:踩过的坑是否回写成带后果的红线
关6接缝(接缝死)→ 模型答四问:退出码、输出契约、只读、可重放
关7复活(复活无门死)→ 点火三句灭火三句过堂

## 工作流程
1. 跑 scripts/lint_skill.py <件目录> --registry ~/.claude/skills
2. 退出码 2:件目录读不了,如实报告并停
3. AUTO 挂项逐条抄进验收单,附判据原文
4. JUDGE 各关逐条过堂:过、挂、存疑三选一,存疑注明存疑点
5. 呈验收单:挂项按「先描述后正文最后脚本」给修理顺序

## 不要做
- 不要替用户改一个字——闸只出单,不动刀
- 不要打总分——分数会被应试优化,闸只认过与挂
- 不要把存疑写成过——判断关的存疑就是结论
- 不要为本机没验证过的市场撞名背书——那是上架日的事

## 完成定义
- 七关每关恰有一个结论(过/挂/存疑)
- 每个 AUTO 挂项附判据原文与修理方向
- 本次写盘数为零

脚本全文(python3 标准库,零依赖,约九十行):

#!/usr/bin/env python3
"""lint_skill.py - 只读体检一个 SKILL.md,出算术关体检单(TSV)。
契约:永不写盘;同一输入两次运行输出逐字节一致。
分工:算术关归本脚本,判断关归模型(SKILL.md 里的七关表)。
行格式:关卡 | AUTO或JUDGE | pass/fail/remind | 明细
退出码:0 全过,1 有 AUTO 挂项,2 件读不了。
"""
import argparse, os, re, sys

NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
TRIGGERS = ("use when", "when the user", "use this skill",
            "use this", "when asked")
NEGATIVES = ("不要做", "do not", "never ", "禁止")
DESC_LIMIT = 700          # 本件自定阈值,非官方标准
BUDGET_LINES = 500        # 09-27 卷口径
SEAM_ASKS = "四问:退出码有主?输出列固定?默认只读?两次运行逐字节一致?"

def parse_fm(text):
    m = re.match(r"^---\s*\n(.*?)\n---\s*(\n|$)", text, re.S)
    if not m:
        return None, "", text
    fm, body = m.group(1), text[m.end():]
    name, parts, in_desc = None, [], False
    for raw in fm.splitlines():
        line = raw.rstrip()
        if not line:
            continue
        nm = re.match(r"^name:\s*(.+?)\s*$", line)
        dm = re.match(r"^description:(.*)$", line)
        if nm and not in_desc:
            name = nm.group(1).strip().strip("'\"")
        elif dm:
            in_desc = True
            first = dm.group(1).strip()
            if first:
                parts.append(first)
        elif in_desc and line[:1] in (" ", "\t"):
            parts.append(line.strip())
        elif line[:1] not in (" ", "\t", "#"):
            in_desc = False
    return name, " ".join(parts), body

def name_of(skill_dir, cache={}):
    if skill_dir not in cache:
        p = os.path.join(skill_dir, "SKILL.md")
        try:
            with open(p, encoding="utf-8") as f:
                cache[skill_dir] = parse_fm(f.read())[0]
        except OSError:
            cache[skill_dir] = "?"
    return cache[skill_dir]

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("skill_dir")
    ap.add_argument("--registry", help="注册表根目录,供全局撞户查重")
    args = ap.parse_args()
    path = os.path.join(args.skill_dir, "SKILL.md")
    try:
        with open(path, encoding="utf-8") as f:
            text = f.read()
    except OSError as e:
        print(f"读不了 {path}: {e}", file=sys.stderr)
        sys.exit(2)
    name, desc, body = parse_fm(text)
    low = text.lower()
    rows = []
    same = name == os.path.basename(os.path.normpath(args.skill_dir))
    ok1 = bool(name) and bool(NAME_RE.match(name)) and same
    rows.append(("关1名字", "AUTO", "pass" if ok1 else "fail",
                 f"name={name} 目录同名={same}"))
    if args.registry:
        root = os.path.normpath(args.registry)
        me = os.path.basename(os.path.normpath(args.skill_dir))
        hits = sorted(d for d in os.listdir(root) if d != me
                      and name not in (None, "?")
                      and name_of(os.path.join(root, d)) == name)
        rows.append(("关1撞户", "AUTO", "fail" if hits else "pass",
                     f"同名户={','.join(hits) if hits else '无'}"))
    trig = any(t in low for t in TRIGGERS)
    rows.append(("关2门牌", "AUTO",
                 "pass" if desc and trig and len(desc) <= DESC_LIMIT else "fail",
                 f"描述{len(desc)}字符 触发短语={'在场' if trig else '缺席'}"))
    nlines = body.count("\n")
    rows.append(("关3预算", "AUTO", "pass" if nlines <= BUDGET_LINES else "fail",
                 f"正文{nlines}行 上限{BUDGET_LINES}"))
    neg = any(t in low for t in NEGATIVES)
    rows.append(("关4边界", "AUTO", "pass" if neg else "fail",
                 "负面清单在场" if neg else "未见负面清单"))
    rows.append(("关5化石", "JUDGE", "remind",
                 "踩过的坑是否已回写成带后果的红线条款"))
    if os.path.isdir(os.path.join(args.skill_dir, "scripts")):
        rows.append(("关6接缝", "JUDGE", "remind", SEAM_ASKS))
    rows.append(("关7复活", "JUDGE", "remind",
                 "点火三句灭火三句过堂;回炉每轮只拧一层"))
    for row in rows:
        print("\t".join(row))
    hard = sum(1 for r in rows if r[1] == "AUTO" and r[2] == "fail")
    soft = sum(1 for r in rows if r[2] == "remind")
    print(f"# AUTO挂{hard}项 JUDGE待判{soft}项", file=sys.stderr)
    sys.exit(1 if hard else 0)

if __name__ == "__main__":
    main()

逐段注记只挑要紧的。描述:触发词全是元任务词汇(validate、audit、quality-check、never triggers——最后这个是替 09-26 维修手册的案发现场收尸:用户问「为什么我这件从来不触发」,正是验件业务的高频入口);Read-only by contract 把只读写进门牌,让匹配层在点亮它之前就知道这件只动嘴;两条让路句各有主——写件归 skill-creator、找件归 find-skills,末句与编目工具签互不侵犯。分工线:五道 AUTO 全是写得出判据的算术(正则、行数、子串、目录探测),四条 JUDGE 全是写不出伪代码的判断——脚本自己就是套路一「判断归正文算术归脚本」的执行样本。不打分:mcp-builder 的 94 分闸是造件时给自己判卷,本件是交货时给件验收,立场刻意不同——阅卷人会为分数优化答卷,闸只保留过与挂两个态,不给应试留缝。诚实条款:本脚本成稿时未实跑一次(执行通道未开),现在是纸面件;读者装好后第一件事,正是拿它验它自己。

五、一次完整当班(纸面推演)

flowchart TD
    A["写完一件SKILL.md"] --> B["点亮 skill-lint"]
    B --> C["跑 lint_skill.py"]
    C --> D{"AUTO关有挂项"}
    D -->|有| E["逐条抄单回炉<br/>每轮只拧一层"]
    D -->|无| F["判断关逐条过堂"]
    F --> G{"判断关有存疑"}
    G -->|有| E
    G -->|无| H["呈完整验收单<br/>写盘数为零"]
    E --> B

读图要点:E 是全图唯一的回流站,AUTO 挂与 JUDGE 存疑殊途同归地汇进它——闸的两种拦截都必须通向修理,而不是通向放行或沉默;回流箭头指回 B 而不是 C,因为每拧一层都要重新点亮整道闸,只重跑脚本是偷懒的验法。注意 H 的两条输出纪律:验收单里挂项必须带判据原文(让用户能复核你的判),全程写盘数为零(闸不开刀)。

六、安装与自测

# 两件套:正文、脚本
mkdir -p ~/.claude/skills/skill-lint/scripts
# 将上文 SKILL.md 存为 ~/.claude/skills/skill-lint/SKILL.md
# 将上文脚本   存为 ~/.claude/skills/skill-lint/scripts/lint_skill.py

# 出厂自测第一式:拿它验它自己(退出码应为 0,JUDGE 三条留模型过堂)
python3 ~/.claude/skills/skill-lint/scripts/lint_skill.py \
  ~/.claude/skills/skill-lint --registry ~/.claude/skills

# 自测第二式:故意验一件没写负面清单的裸件,确认关4如挂
# 自测第三式:点火灭火各三句
#   点火:「我刚写了个 skill,帮我验一下能不能交」「为什么我这件从来不触发」
#   灭火:「帮我写一个新 skill」(归 skill-creator)「帮我找个处理 PDF 的件」(归 find-skills)

# 市场撞名复查(本通道今日未开,命令备上架日用)
npx skills find skill-lint

七、推荐排行榜

结语

写件课拍到第四拍,这条线完成了自己的闭环:第一件教你怎么说话,第二件教你怎么点人,第三件教你怎么把活分出去,本期教你怎么在交货前证明前面三课都没白学。四拍合起来,工厂还缺的最后一个科室到岗了——有产线,有前台,有车间,如今有质检。而质检科的 Deep 之处在于它的第一戒律:不打分。 分数是给阅卷人看的,闸是给件用的;94 分的考卷属于还在学写件的 mcp-builder,过了学徒期的生态需要的是一道更硬的东西——只回答两个字的闸:过,或者挂。

参考来源

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入