mcpbeat

Claude Md Progressive Disclosurer

daymade/claude-md-progressive-disclosurer

|

22k tokens
context cost
the whole folder, loaded on every use
3
files
instructions only
0
copies elsewhere
how many repositories repackaged it
1314
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/daymade/claude-code-skills --skill claude-md-progressive-disclosurer

The instruction itself

33 sections, as written by the author

CLAUDE.md 渐进式披露优化器

核心理念

> "找到最小的高信号 token 集合,最大化期望结果的可能性。" — Anthropic

目标是最大化信息效率、可读性、可维护性。

> 本 skill 自身遵守渐进式披露:新方法论以"精炼规则 + 触发条件"留在 SKILL.md,深度战例 / 引文沉到 references/。SKILL.md 行数由信息密度决定、不设为硬目标(约 500 行量级;新增高价值规则可略超,但深度永远沉 references)——skill 自己示范它教别人的事:行数不是 KPI,自洽地不拿"≤N 行"约束自己。

铁律:行数禁作 KPI,可作诊断症状

禁作优化目标 / 成功指标(不可削弱——案例 7/8/9 的防线就是这条):

  • 行数少不代表更好,行数多不代表更差
  • 评判标准是:单一信息源(同一信息不在多处维护)、认知相关性(当前任务不需要的信息不干扰注意力)、维护一致性(改一处不需要同步另一处)——不是行数
  • 禁止在优化方案 / 总结中出现"从 X 行精简到 Y 行"、"减少 Z%"作为成果
  • 禁止把"减少行数"作为移动 / 删除某内容的理由
  • 一个结构清晰、信息不重复的长文件,胜过砍掉关键信息的短文件

可作诊断症状(官方依据:Claude Code 文档"文件太长 → 规则被淹没 → Claude 不遵守"):

  • 允许把"行数异常大 + Claude 反复不遵守某规则"当成触发调查的信号,不是结论
  • 调查动作仍是信号分诊(Step 2.1)+ 分层,不是"砍到 N 行"
  • 一句话区分:行数可以让你开始怀疑,不可以成为你优化的目标汇报的成果
触发即 reframe(用户说「太大 / 太长 / 精简 / 瘦身」时——最易在此处跑偏)

这些词触发的本能是「砍行数」。先 reframe,再动手:① 当场声明「行数不是目标,单一信息源 / 认知相关性才是」;② 直接进 Step 2.1 信号分诊,用「这段有没有 canonical source 重复 / 是不是反信号」决定去留,不是用「文件多长」;③ 把「太大吗」当调查的起点,不是砍的许可。用户连续追问「还是太大」时同理——回应是「再做一轮分诊找重复 / 反信号」,分诊空了就诚实说「剩下都是高频核心,再砍会丢信号」,不是继续砍有信息的内容。(实战:把「太大吗」做成减行数任务、一路用「省 39%」当成果汇报、被连续追问拽着越砍越多 → 案例 15、16。)

两层架构

Level 1 (CLAUDE.md) - 每次对话都加载
├── 信息记录原则               ← 防止未来膨胀的自我约束
├── Reference 索引(开头)     ← 入口1:遇到问题查这里
├── 核心命令表
├── 铁律/禁令(含代码示例)
├── 常见错误诊断(症状→原因→修复)
├── 代码模式(可直接复制)
├── 目录映射(功能→文件)
├── 修改代码前必读             ← 入口2:改代码前查这里
└── Reference 触发索引(末尾) ← 入口3:长对话后复述

Level 2 (references/) - 按需即时加载
├── 详细 SOP 流程
├── 边缘情况处理
├── 完整配置示例
└── 历史决策记录

但「两层」只是文件层——先选载体,再选层级

渐进式披露不是一个文件内部的事,它在多个层同时发生:MCP 懒加载工具、RAG 按需取知识、

Skills 描述常驻正文按需、以及 Claude Code 的动态工具选择(工具索引层的渐进式披露)

只在 CLAUDE.md 内部搬 L1↔L2,等于把下表四种载体里的两种(常驻 L1 / reference)当成全部。

先问载体,再问层级。 判据一句话:模型能不能在违规之前就知道自己需要这条规则?

| | 违规可恢复 | 违规不可恢复 |

|---|---|---|

| 触发自报(我知道我要做 X) | → Skill(触发词加载) | → Hook 拦截器(散文拦不住,见下) |

| 触发不自报,但「时刻」工具事件可观测 | → reference(复盘时查) | → Hook 注入器(确定性 100%、常驻成本 0) |

| 触发不自报,且无可观测时刻 | → reference | → 常驻 L1(真正必须常驻的只剩这一格) |

两条轴的定义

  • 触发自报 = 动手前那一刻,命令/文件名/关键词里就写着「我要做这件事」——

aliyun ...、写 .tf、跑打包。skill 的描述匹配能接住这类。

  • 「时刻」可观测 ≠ 触发自报。这是第三行存在的全部理由

「我在修测试」不自报「我正要删功能」,但 Write 一个 .py 文件是一个工具事件

hook 能在那一刻开火。规则的语义 hook 判断不了,但时刻它看得见 —— 于是

hook 只负责报时刻、把规则怼到面前,判断仍归模型。

  • 只有连一个可挂载的工具事件都找不到的规则(例:「怎么跟这个用户沟通」「他的资源观」),

才真正必须常驻。

Hook 两种形态拦截器 exit 2 挡住可判定的形态;注入器 exit 0 但在 stdout 输出

hookSpecificOutput,把文本送进模型上下文。注入器让确定性与常驻解耦 ——

一条规则可以 100% 每次出现,却零常驻成本。

⚠️ 别把注入器当成万能替代:它 exit 0、不阻断,模型可以读完照样违反 ——

它换来的是「规则必定在场」,不是「规则必定被遵守」。所以:不可逆 + 形态可判定 → 拦截器

不可逆 + 只能语义判断 → 注入器 + 常驻规则句两者并存(注入器保证在场,常驻句保证

它出现在最该出现的位置)。同一条规则挂两个载体不算违反 SSOT,前提是

只有一处写规则正文,另一处指向它

🚫 缩正文前必须实证「真有别的机制在强制吗」,禁止推断。

把 L1 正文缩成「由 X 强制 + 指针」之前,构造一条真实的违规命令喂给 X,看它到底拦不拦:

# ① 名单从「注册表」来,不是从目录来(见下方两个坑)
python3 - <<'PY' > /tmp/registered-hooks.txt
import json, os
s = json.load(open(os.path.expanduser('~/.claude/settings.json')))
for ev, groups in (s.get('hooks') or {}).items():
    for g in groups:
        for h in g.get('hooks', []):
            print(ev, g.get('matcher', '*'), h.get('command', ''))
PY

# ② payload 必须完整——缺字段会让判决翻转(见下方第一个坑)
printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Bash",
  "tool_input":{"command":"<一条真实的违规命令>"},
  "cwd":"'"$PWD"'","session_id":"probe","transcript_path":"/dev/null"}' \
  | <用该 hook 自己的解释器执行它> >/dev/null 2>&1; echo "exit=$?"   # exit 2 = 拦截,0 = 放行

这个探针有两个会让你得出相反结论的坑,都实测踩过:

  • payload 缺字段 → 判决翻转。 真实事件还带 cwd/session_id/transcript_path/hook_event_name

很多 hook 读它们。实测同一条 rm -rf important-data不带 cwd → exit 0(该 guard 的设计是

「相对路径 + 无 cwd = 判不出,放行」),cwd → exit 2。用精简 payload 探,你会给一条

真的有防护的规则判「无人强制」,然后动手拆掉它的正文。

  • 枚举目录 ≠ 枚举注册表,而且两个方向都错。 实测某机器:注册 28 条,其中 **1 条在 hooks 目录之外

且是 .py(拿 bash 跑它 → 语法错 → 非 2 → 被记成「没拦」=漏**);目录里反而躺着一个

.json 配置和一个陈旧的 *.sh.bak-*(被误当 hook 跑出 exit 2 → 记成「有人强制」=

于是授权你删掉真正在起作用的正文)。此外项目级 .claude/settings.json、plugin 自带 hook

都不在那个目录里,而 matcher 决定某个 hook 根本不对这个工具开火。**权威源是 settings.json 的

hooks 块(配合 claude --debug),不是 ls 一个目录。**

(hook 事件 schema / 注册方式 / exit 语义的完整说明见 daymade-claude-code:claude-code-hooks skill。)

从「装了 N 个 hook」推断「这条被覆盖了」是最危险的一步 —— 若 X 其实不存在,你就

亲手拆掉了唯一在起作用的防线,还留下一句让后来者以为有保护的谎。实测比推断便宜得多

(详见 references/progressive_disclosure_principles.md 案例 18)。

多入口原则(重要!)

同一 Level 2 资源可以有多个入口,服务于不同查找路径:

| 入口 | 位置 | 触发场景 | 用户心态 |

|------|------|----------|----------|

| Reference 索引 | 开头 | 遇到错误/问题 | "出 bug 了,查哪个文档?" |

| 修改代码前必读 | 中间 | 准备改代码 | "我要改 X,要注意什么?" |

| Reference 触发索引 | 末尾 | 长对话定位 | "刚才说的那个文档是哪个?" |

这不是重复,是多入口。 就像书有目录(按章节)、索引(按关键词)、快速参考卡(按任务)。

边界(与 SSOT 的张力,必须守住):多入口成立仅当——每个入口 keyed 方式不同(错误索引 / 任务索引 / 末尾复述),且都只指向同一 Level 2 资源、不复制它的正文。如果你把同一段规则正文抄到 3 个地方,那是违反 SSOT 的重复(会各自漂移),不是多入口。一句话判据:入口存的是"路标 + 触发条件",不是"内容副本"。


优化工作流

Step 1: 备份

cp CLAUDE.md CLAUDE.md.bak.$(date +%Y%m%d_%H%M%S)

Step 2: 内容分类

分两阶段。先分诊,再分层——跳过分诊会把噪音忠实搬进 Level 2,把 reference 变垃圾场。

2.1 信号分诊(必要性闸门,先决)

对每个章节先问 Anthropic 官方 litmus:"删掉这一条,Claude 会不会犯错?"

  • 会犯错 → 是信号,进入 2.2 分层
  • 不会犯错,且属以下任一 → 是反信号,列入"候选删除"清单:
  • 能从代码 / 项目结构 / 文件名推断的(如"本项目用 TypeScript")
  • 语言 / 框架的标准约定(如"遵循 PEP 8")
  • 自明常识(如"写干净的代码""提交前测试")
  • 已有独立 canonical source 覆盖的(注明 source 在哪)
  • 已过时的一次性修复(不会再复发)
  • 确定性必须每次发生的(如"提交前必跑 lint")→ 标记"建议转 hook",不替用户实现(散文保证不了确定性)。先按上方载体表查它落在哪一格,把那一格的结论写进候选清单——deliverable 是带载体判定的建议,不是替用户写 hook。⚠️ 别预设答案一定是 hook:本例「提交前必跑 lint」是触发自报git commit 自己就报了)且违规可恢复(事后补跑),查表落在 Skill 格,不是任何一种 hook。「确定性必须每次发生」只说明散文不够,没说必须用 hook——哪个载体由表决定

安全栏(与移动同等严格,不可削弱):候选删除 ≠ 立即删除。必须事前逐项列出 + 注明属上面哪类 + 征求用户确认。说不出理由 = 不是反信号,回 2.2 当信号处理。

> 与案例 8/9 的边界:8/9 是把真信号(debug 提示、代码模式)在移动时压缩掉 = 永远错;这一步是移除已确认反信号(可推断 / 自明)= 正确。区别在"删的是不是信号",不在"删不删"。详见 references/progressive_disclosure_principles.md 案例 10。

2.2 分层分类

通过分诊的信号分类:

| 问题 | 是 | 否 |

|------|----|-----|

| 高频使用? | Level 1 | ↓ |

| 违反后果严重? | Level 1 | ↓ |

| 有代码模式需要直接复制? | Level 1 保留模式 | ↓ |

| 有明确触发条件? | Level 2 + 触发条件 | ↓ |

| 历史/参考资料? | Level 2 | 考虑删除 |

Step 3: 创建 Reference 文件

命名:docs/references/{主题}-sop.md

铁律:原样移动,禁止压缩

移动内容到 Level 2 时,必须完整保留原始内容。不要在移动的同时"顺便精简"。

✅ 正确:把 100 行原封不动搬到 Level 2(100 行 → Level 2 100 行)
❌ 错误:把 100 行"精简"到 60 行搬到 Level 2(100 行 → Level 2 60 行,40 行消失)

为什么:压缩 = 变相删除。你认为"不重要"而删掉的内容,可能是某个未来 debug session 的关键线索。优化的目标是改变信息的位置(Level 1 → Level 2),不是改变信息的存在

怎么做

  • 从原始 CLAUDE.md 中精确复制要移动的段落
  • 原样粘贴到 Level 2 文件中
  • 可以在 Level 2 中添加结构(标题、分隔线),但不要删减、改写、合并原始内容
  • 如果确实有冗余(同一段话在原文中出现了多次),在 Level 2 中保留一份完整的,注释说明去重

Step 4: 更新 Level 1

  • 在开头添加「信息记录原则」(项目概述之后,Reference 索引之前)
  • 添加 Reference 索引(紧随信息记录原则之后)
  • 用触发条件格式替换详细内容
  • 保留代码模式和错误诊断
  • 添加「修改代码前必读」表格(按"要改什么"索引)
  • 在末尾再放一份触发索引表

⚠️ 写指针前的硬 gate(事中验证,最易跳过、本次最大踩坑):每写一条「→ 某 reference / 详见 X」指针前,当场 grep 确认目标文件真有这段内容。三种结果:① 目标已有完整内容 → 写指针;② 目标没有 / 不确定是否完整 → 先把原文 verbatim cut 到目标(回 Step 3),再写指针;③ 绝不写「指向一个其实没有该内容的文件」的假指针。假指针比丢内容更隐蔽——它让 5a「文件存在」通过、却在读者点进去时才发现是空的。Why:5a/5b 是事后验证,假指针那一刻已写进文件;事中 gate 才能在源头拦住。(实战:写「详见 anti-patterns」但那里 0 命中 Stripe 端点 → 案例 15。)

Step 5: 验证(三项全部通过才算完成)

5.0 先标定判据本身 —— 验证器会骗你,而且两个方向都会

下面 5a/5b 全建立在 grep/find 上。一个错的判据会和对的判据一样自信地报告结果

而这一步的产物(「这段已经下沉了」「这个指针是真的」)会被当成事实写进交付。

所以:先在一个你已知答案的样本上跑一遍判据,确认它真的会命中,再用它去查你不知道答案的。

这一行成本,把「我查过了」变成「我用标定过的判据查过了」。

实测踩过的形态如下。注意它们的坏法不一样——多数是静默假阴性(0 命中被读成「内容丢了」),

但有一条给假阳性(为有损搬运开脱,最危险),还有一条是响亮报错(exit 2,在 2>/dev/null

脚本里同样被吞成「没找到」)。所以标定时别只看有没有输出,要看 exit code

| 陷阱 | 症状 | 修法 |

|---|---|---|

| 递归搜索悄悄跳过 symlink | reference 目录里只要有一个 symlink(skill 安装、SSOT 外置极常见),整片内容对验证器不可见 → 把「已下沉」误报成「未下沉」 | 别去挑递归 flag —— 先把路径解析成真身再读readlink -f <path> 拿到真实文件,或 find -L <dir> -type f -name '*.md' 枚举后逐个读(两种都不依赖任何实现,实测三种 grep + BSD/GNU find 行为一致)。⚠️ 递归 flag 的 symlink 语义因实现而异,且没有可移植组合——挑哪个都会坑掉一部分读者;三实现实测矩阵见案例 17 ①,此处不复述数值(会漂) |

| 代理判据(拿 A 的存在证明 B 已完成) | 用「日期锚点是否出现在 reference」判是否已下沉 —— 而 reference 的节标题里带个日期就让整段显示为「已下沉」,实际那节里一条子发现都没有 | 判据必须落在被判对象本身上:抽该段的 3–5 个特异串(具体值/命令/专名)逐个查 |

| 行级度量高估工作量 | 「含该锚点的整行」包含大量不需搬的规则正文,量到的是「含有它的行的总长」而非内容本身 | 按段落量,不按含关键词的行的字节数 |

| grep -F 对多行原句退化成「按行 OR」(唯一会给假阳性的一条,最危险) | 验 verbatim 搬运时,原句是多行的:grep -F 把它当成多个独立 pattern,命中任意一行就报成功。搬运时丢了半个段落,判据照样报「还在」 —— 它为一次有损搬运出具了无罪证明,而这正是 5b 存在的理由 | 存原句到临时文件用 python - <<'PY'整串子串判断需要的原文 in 目标文件内容)—— 它要求连续完整匹配,丢一行就 False |

| 探针串含正则元字符(两种坏法,别只防一种) | 实测 BSD grep:*静默假阴性(exit 1,0 命中,长得就像「内容丢了」);[2026-07-26] 这类方括号 → 响亮报错(exit 2 invalid character range),而报错在 2>/dev/null 的脚本里同样被读成「没找到」。(顺带纠正一个直觉错误:\|\| 在 BRE 下正常命中,不是假阴性) | 一律 grep -F(固定串),或走上面的 python 子串判断。脚本里别把 stderr 丢掉 —— exit 2 和 exit 1 必须分开处理 |

⚠️ 别把「重新折行」算进上面第 4 行:搬运时重新折行本身就违反反模式 6「原样复制,不改一字」

判据判它失败是对的,不是误伤。python 子串判断在这种情况下同样返回 False(实测)——

它不是用来给折行开脱的,没有任何判据该给折行开脱

完整战例(判据陷阱如何连环误导同一个执行者)→ references/progressive_disclosure_principles.md 案例 17

(该案例覆盖上表前三行;第 4、5 行来自同期对判据本身的实测,无独立战例)。

判据陷阱有第二层,比第一层更隐蔽:不只判据会骗你,修法也会。

一个依赖具体实现的修法(换个 flag、加个选项)在你机器上验证通过,换台机器静默失效 ——

而你不会收到任何信号。优先选不依赖实现的做法(解析路径而非调递归 flag、

子串判断而非行级匹配);实在要用 flag,就在标准实现/usr/bin/<tool>)上复验一次。

> 这一条是在写它的过程中自己撞出来的,所以它不是理论。为修上表第一行的 symlink 问题,

> 作者写了一段 find -L 的替代脚本 —— **在 bash 下完全正确,在 zsh 下把 4 个真实存在的

> 章节全报「NOT FOUND」。原因是 for d in $REFDIRS 依赖词分割:bash 有,zsh 没有**,

> 而 zsh 是现代 macOS 的默认登录 shell。改成数组 "${ARR[@]}" 后两个 shell 输出一致。

> 教训:「修法也会骗你」不只跨 *工具实现*(BSD/GNU),还跨 *shell*、跨 locale、跨版本。

> 所以定案标准不是「我跑通了」,是「我在读者最可能用的那个环境里跑通了」——

> 对 macOS 读者,那至少意味着 bash 和 zsh 各跑一遍。

元规则:本节几条的共同点不是「grep 用法要小心」,而是

判据与被判对象之间只要隔了一层代理,缝隙里就能穿过去东西

写任何一条验证命令时问一句:*它测的到底是不是我想知道的那件事?*

5a. 引用文件存在性
# 抓出正文里所有反引号包起来的 .md 路径(不写死 docs/references/——用户级布局是
# ~/.claude/references/,写死会一条都抓不到)
grep -oh '`[^`]*\.md`' CLAUDE.md | tr -d '`' | sort -u > /tmp/pointers.txt

# ⚠️ 先标定:抓到 0 条 ≠ 全部通过,而是「这个模式没匹配上你的写法」
n=$(wc -l < /tmp/pointers.txt | tr -d ' ')     # BSD wc 会补空格,去掉
echo "抓到 $n 条指针"
[ "$n" -eq 0 ] && echo "🚨 0 条 = 模式没命中,不是没问题——先手工确认正文到底怎么写引用的"

while read -r f; do
  # ⚠️ 分「可判定 / 不可判定」,别把散文里的东西一律报成断链(见下方真实语料实测)
  case "$f" in
    *\**|*\?*)   echo "– 跳过(glob):      $f"; continue ;;
    *\<*|*\>*)   echo "– 跳过(模板占位):  $f"; continue ;;
    *" "*)       echo "– 跳过(含空格,散文): $f"; continue ;;
    */*)         ;;
    *)           echo "– 跳过(裸文件名,无根): $f"; continue ;;
  esac
  case "$f" in
    /*|\~/*|./*) p="${f/#\~/$HOME}" ;;   # 绝对 / 家目录 / 显式相对 → 可判定
    *)           echo "– 跳过(相对未知根): $f"; continue ;;
  esac
  [ -e "$p" ] && echo "✓ $f" || echo "✗ MISSING: $f"
done < /tmp/pointers.txt

> 为什么加那三行标定:原版把 docs/references/ 写死在模式里。在本 skill 自己定义的

> 用户级布局(~/.claude/references/)上跑,它匹配 0 条 → while 循环一次都不进 →

> 零输出、exit 0,和「所有引用都存在」的输出完全一样。实测:一份含真断链的

> CLAUDE.md 被它判为干净。这正是 5.0 那条「0 命中 必须双向读」,而 5a 自己没做。

>

> 为什么要分「可判定 / 不可判定」:这段的第一版只在合成 fixture 上验过、全绿。

> 拿真实语料(一份 120KB 的全局 CLAUDE.md)一跑,32 条候选里 10 条「MISSING」是误报 ——

> 散文里提到的裸文件名(incident-2026-04-18-*.md)、glob(*/memory/*.md)、

> 带占位符的模板(<config>/…),以及一对被反引号连在一起、中间有箭头的两个路径。

> 按本 skill 自己的规矩:误杀健康输入比漏报更糟 —— 31% 的误报率会直接训练读者忽略这个检查。

> 分类后:可判定 21 条全部正确,不可判定 11 条单独列出待人工确认,误报 0

> 而那一轮真的抓到 1 条断链(指向的 memory 文件被挪进了 .memory-archive-* dot 目录,

> 且路径大小写也变了)—— 合成 fixture 永远造不出这种形状。

>

> 本段命令已在 bash 与 zsh 下各跑一遍、输出字节一致

> 为什么要跨 shell 验:见附录 C 里那个「字符串 + 词分割」的坑 —— 同一段脚本

> bash 全对、zsh 全错,而 zsh 是现代 macOS 的默认 shell。

5b. 内容完整性(最关键)

对每个从原始 CLAUDE.md 移走的章节,逐一检查:

  • 取到「改动前」的原始文件当对照物。首选 Step 1 那份备份——

CLAUDE.md.bak.<时间戳> 建于一切编辑之前、之后再没被写过,

天然满足推论②「对照物必须在你的写入范围之外」,且不依赖任何 git

   ORIG=$(command ls -1t CLAUDE.md.bak.* 2>/dev/null | tail -1)   # 最早那份 = 动手前
   echo "对照物:$ORIG"

⚠️ 别默认目标在 git 里。 最常见的优化对象是全局 ~/.claude/CLAUDE.md

~/.claude 通常根本不是 git 仓库fatal: not a git repository)——

本 skill 两个新战例都出自这个场景。备份法在这里是唯一可行的,git 法直接死在第一步。

目标确实在 git 仓里时,git 法可作交叉验证,但有两个坑:

  • 别用 HEAD:中途 commit 过的话 HEAD 已在你的写入范围内,等于自己给自己作证。

git log --oneline -- <文件> 找到本次工作之前的那个 ref。

  • git show <ref>:CLAUDE.md 从仓根解析,不是从当前目录。在 monorepo 里优化

packages/x/CLAUDE.md,它会静默返回仓根那份(exit 0、无任何警告),

你之后每一条 5b 判定都是噪音。要当前目录那份必须写 <ref>:./CLAUDE.md(实测有别)。

  • 逐节对比:对原始文件的每个 ## 章节,确认其内容在以下位置之一完整存在:
  • 新 CLAUDE.md 中(保留在 Level 1)
  • 某个 Level 2 reference 文件中(完整移动)

📖 快速暴露整章遗漏的辅助脚本见 references/progressive_disclosure_principles.md 附录 C:触发场景——做下面逐节对比前的第一道筛查(脚本不替代人工逐节对比,只查章节标题是否存在)。

  • 标记所有差异
  • 如果某段内容在新文件中被缩短 → 必须补回被删减的部分
  • 如果某段内容在两个位置都不存在 → 必须补回
  • 唯一允许删除的情况:该信息已有独立的 canonical source(如 docs/README.md 已是文档索引的 canonical source),且在 Level 1 中有明确的指向

禁止将"故意删除"作为分类来掩盖信息丢失。 每一项"故意删除"都必须说明 canonical source 在哪里。如果说不出来,就不是"故意删除",而是"遗漏"。

独立 agent 做 5b 是默认动作,不是「大量压缩时才用」:执行者自审有「乐观偏差」——倾向相信自己砍掉的内容都有归属。启动一个独立 sub-agent普通 subagent,禁 fork——fork 继承你的盲区,只会盖个「已审」的章)做完整逐节 5b(读原始文件 + 当前文件 + 所有 reference,逐个信息点验证归属,只返回「真丢失 / 指针失准」清单)。它没有你的 sunk-cost,能抓到你抽查会放过的。prompt 模板 + 批量内容点 grep 脚本见 references/progressive_disclosure_principles.md 附录 D。

为什么从「强烈推荐」升级为默认:本 skill 的真实使用中,执行者抽查 5 点「自我感觉良好」,独立 agent 逐节查 55 点才暴露真问题。更硬的一次(2026-07-26):反模式 6 的规则完整、连「grep 原句逐字节仍命中」的判据都写在文件里,执行者读过、认同、自认在遵守,重写时仍删掉了一条约束——而且它只存在于被删的那处,reference 里没有副本。这不是缺规则,是散文规则由缺陷源本人执行时对该缺陷源天然失效(poka-yoke 的 control vs warning:警告依赖警觉,而警觉正是完成驱动碾过的东西)。唯一逮住它的是换人的第二轮独立审阅。

推论(同源,别只做一半):① 审阅之后的改动同样没被看过 —— 落实审阅结论且逐条复现过,就把「不再派一轮」的理由写进审阅记录:一个 independent-review.md,含审阅者 prompt 原文(让后来者能判断你是不是问了诱导性问题)、每条 finding 及其处置与理由(这是「正当筛选」与「把不利的丢掉」的唯一分界)、以及查不了的东西

放哪(别让读者自己发明路径):放你自己的、纳入版本控制的私人知识仓,路径 skill-reviews/<被优化对象>/independent-review.md。三条禁区:别放进被优化的那个项目仓(记录里必然带私有路径 / 真名 / 项目细节,而那个仓可能是公开的或将来会公开);别放 /tmp 或任何会被清掉的暂存目录(这是跨 session 的证据,必须活过重启);优化 ~/.claude/CLAUDE.md 时尤其注意 ~/.claude 通常不是 git 仓,记录放进去等于没版本控制。不知道自己的私人知识仓是哪个 → 问用户,别猜。② 对照物必须在你的写入范围之外:拿一份你自己刚改过的 reference 去证明「内容还在」,等于自己给自己作证。优先用早于本次工作的 git ref。

5c. 行数不进验证标准

验证不以行数为通过条件,不计算"原始 X 行 vs 新 Y 行 = 减少 Z%"——这种对账会把你拉回 KPI 思维。

验证标准只有三条:

  • 每段信息都有归属(Level 1 或 Level 2 或 canonical source)
  • 没有信号丢失(反信号经确认删除不算丢失)
  • Level 2 引用都有触发条件

(注:诊断阶段可以看行数当怀疑信号,见开头「铁律」;但验证阶段行数不是任何标准——这两个阶段对行数的态度不同,别混。)


Level 1 内容分类

🔴 绝对不能移走

| 内容类型 | 原因 |

|---------|------|

| 核心命令 | 高频使用 |

| 铁律/禁令 | 违反后果严重,必须始终可见 |

| 代码模式 | LLM 需要直接复制,避免重新推导 |

| 错误诊断 | 完整的症状→原因→修复流程 |

| 目录映射 | 帮助 LLM 快速定位文件 |

| 触发索引表 | 帮助 LLM 在长对话中定位 Level 2 |

🟡 保留摘要 + 触发条件

| 内容类型 | Level 1 | Level 2 |

|---------|---------|---------|

| SOP 流程 | 触发条件 + 关键陷阱 | 完整步骤 |

| 配置示例 | 最常用的 1-2 个 | 完整配置 |

| API 文档 | 常用方法签名 | 完整参数说明 |

🟢 可以完全移走

| 内容类型 | 原因 |

|---------|------|

| 历史决策记录 | 低频访问 |

| 性能数据 | 参考性质 |

| 技术债务清单 | 按需查看 |

| 边缘情况 | 有明确触发条件时再加载 |


引用格式(四种)

四种引用格式各服务不同场景;规范的"触发条件"写法见下方 原则 2(已含可复制示例)。

| 格式 | 用途 | 触发场景 |

|---|---|---|

| 详细格式 | 正文中的重要引用 | 单条 reference 需展开说明何时读 |

| 问题触发表格 | 开头/末尾 Reference 索引 | 按"错误/问题"查 |

| 任务触发表格 | 「修改代码前必读」 | 按"要改什么"查 |

| 内联格式 | 简短引用 | 正文一句话带过 |

📖 四种格式的完整可复制模板见 references/progressive_disclosure_principles.md 附录 B:触发场景——产出 Reference 索引 / 任务表 / 内联 / 详细引用时。

多样性原则:不要所有引用都用同一格式。

⚠️ @import 不省上下文(技术正确性,最易踩)

@path import 在启动时全量展开载入——拆成 @import 只改善组织,不减少任何上下文(官方 *memory* 文档原文)。"我把内容拆进 @import 了所以优化了"是假优化。

全局 ~/.claude/CLAUDE.md 真正能省上下文的杠杆只有三条:

  • 把非通用内容移到项目级 CLAUDE.md(全局文件会被无关项目加载)
  • 纯文字指针("需要时 Read references/xxx.md",不是 @),让模型按需拉
  • skill(描述常驻、正文按需)

本 skill 产出的引用一律用反引号路径,禁止用 @import 做卸载。详见 references/progressive_disclosure_principles.md 案例 11。


核心原则

原则 0:添加「信息记录原则」(防止未来膨胀)

问题:优化完成后,用户会继续要求 Claude "记录这个信息到 CLAUDE.md",如果没有规则指导,CLAUDE.md 会再次膨胀。

解决:在目标 CLAUDE.md 开头(项目概述之后)注入一段「信息记录原则」——规定 Level 1 只记核心命令 / 铁律 / 代码模式 / 触发索引,Level 2 记详细 SOP / 边缘情况 / 历史决策,并定义"用户要求记录信息时"的高频→L1、低频→L2 判断流程(引用 L2 必带触发条件)。

📖 完整可注入模板见 references/progressive_disclosure_principles.md 附录 A:触发场景——执行 Step 4 更新 Level 1 时;附录含可整块复制进目标 CLAUDE.md 的 markdown。

原因:这条规则让 Claude 自己知道什么该记在哪里,实现"自我约束",避免后续对话中 CLAUDE.md 再次膨胀。

原则 1:触发索引表放开头和末尾

原因:LLM 注意力呈 U 型分布——开头和末尾强,中间弱。

| 位置 | 作用 |

|------|------|

| 开头 | 对话开始时建立全局认知:"有哪些 Level 2 可用" |

| 末尾 | 对话变长后复述提醒:"现在应该读哪个 Level 2" |

📖 首/尾索引表完整写法示例见 references/progressive_disclosure_principles.md 案例 4:触发场景——决定触发索引表放哪、按什么格式写时。

原则 2:引用必须有触发条件

错误详见 native-modules-sop.md

正确

**📖 何时读 `native-modules-sop.md`**:
- 遇到 `ERR_DLOPEN_FAILED` 错误
- 需要添加新的原生模块

> 包含:ABI 机制、懒加载模式、手动修复命令

原因:没有触发条件,LLM 不知道什么时候该去读。

原则 3:代码模式必须保留在 Level 1

错误:把代码示例移到 Level 2,Level 1 只写"使用懒加载模式"。

正确:Level 1 保留完整的可复制代码:

// ✅ 正确:懒加载,只在需要时加载
let _Database = null;
function getDatabase() {
  if (!_Database) {
    _Database = require("better-sqlite3");
  }
  return _Database;
}

原因:LLM 需要直接复制代码,移走后每次都要重新推导或读取 Level 2。

原则 4:用三态优先级,不要"全标铁律"

问题:把每条规则都标"铁律 / HIGHEST / 全局" = 没有优先级。模型无法 triage,注意力被摊薄,最关键的不可逆规则反而被淹没。指令遵循存在约 150–200 条的上限,远超即整体衰减。

第二个机制,比注意力稀释更隐蔽:指令互相干扰。 一个上下文里堆着 20 个工作流的指令时,

它们会彼此冲突("输出一律 JSON" 撞上一个面向客户、JSON 完全不对的流程)。

模型不会报错,它只是悄悄做出更差的决定。 所以「多写一条总没坏处」是错的 ——

每多一条都在提高与既有指令冲突的概率,而这种劣化没有任何报错信号

你只会看到「它最近好像笨了点」。这也是为什么分层不只是省 token:

把当前任务用不上的指令挪出上下文,本身就在减少干扰源。

诊断信号:同一份文件里出现两条在某个场景下会给出相反建议的规则,

而没有一条说明「哪种情况听哪条」—— 那不是两条规则,是一个待解决的冲突。

> 出处:这一现象在文献里有名字 —— context pollution(冗余 / 冲突信息扭曲推理)

> 与 context confusion(模型无法区分指令、数据与结构标记,尤其在系统指令自相矛盾时)。

> 与 Anthropic「有限注意力预算 → 找最小高信号 token 集」是同一个根因的两面

> (见 references/progressive_disclosure_principles.md §研究背书)。

解决(GitHub 2500 仓库实证最有效的结构):输出 Level 1 规则时用三态,而不是一律"铁律":

| 标记 | 含义 | 例 |

|------|------|----|

| ✅ | 总是这样做 | ✅ 提交前跑测试套件 |

| ⚠️ | 先停下问 / 谨慎 | ⚠️ 改 schema 前先确认迁移脚本 |

| 🚫 | 绝不 | 🚫 绝不提交 secret |

位置即优先级(Lost-in-the-Middle,TACL 2024):LLM 注意力 U 型分布,最高危的不可逆规则放文件首或尾,不要埋中间。真正"违反即不可逆伤害"的应是少数(5–7 条),其余降为普通规则——稀缺才有信号。

原则 5:每条保留规则带一行 Why

问题:不带原因的规则,一旦场景变化就被忽略(Builder.io 实证)。带 Why 的规则能跨场景泛化。

解决:Level 1 保留的每条铁律 / 禁令,跟一行 Why:,说明违反会发生什么具体坏事。

错误🚫 禁止 fallback 默认值

正确🚫 禁止 fallback 默认值。Why:一个 || 'sk-xxx' 兜底在 .env 缺失时静默回退明文 key,曾在 48h 内被公开仓库扫描器用掉额度。

> ⚠️ 重述规则时的硬边界:若原句嵌在 case study 混合段落里,原则 4/5 不得直接改写原句——见反模式 6(先整段 verbatim 移 L2,案例 14)。


反模式警告

⚠️ 反模式 1:以行数为目标的过度精简

案例:为了"减少行数",移走了代码模式、诊断流程、目录映射

结果

  • 丢失代码模式,LLM 每次重新推导
  • 丢失诊断流程,遇错不知查哪
  • 丢失目录映射,找文件效率低

正确:保留所有高频使用的内容。优化的判断标准是信息是否重复维护、是否与当前任务无关,而不是"文件太长"。

⚠️ 反模式 2:无触发条件的引用

案例详见 xxx.md

问题:LLM 不知道何时加载,要么忽略,要么每次都读。

正确:触发条件 + 内容摘要。

⚠️ 反模式 3:移走代码模式

案例:把常用代码示例移到 Level 2

问题:LLM 每次写代码都要先读 Level 2,增加延迟和 token 消耗。

正确:高频使用的代码模式保留在 Level 1。

⚠️ 反模式 4:删除而非移动

案例:删除"不重要"的章节

问题:信息丢失,未来需要时无处可查。

正确:移到 Level 2,保留触发条件。

⚠️ 反模式 5:用行数当 KPI

案例:优化方案写"从 2000 行精简到 500 行,减少 75%"

问题:把行数当成功指标,会驱动错误决策——为了凑数字而砍掉有用的信息。

正确:用信息质量评估优化效果——信息是否有重复?维护负担是否降低?LLM 是否能更快找到需要的信息?

⚠️ 反模式 6:移动时压缩(变相删除)

规则:移动是移动,精简是精简。这是两个独立操作,不要同时执行

  • 移动内容到 Level 2 时,必须原样复制,不改一字
  • 如果发现冗余需要精简:作为单独的后续步骤,逐项列出要删除的内容及理由,征求用户确认
  • "既然都在改了,顺便精简一下"是最隐蔽的删除——它披着"优化"的外衣,做着"删除"的事
  • 混合段落(规则句 + case study/叙事)的硬边界:原则 4/5、反模式 8 想把规则重述成 ✅/🚫+Why,但混合段落与本反模式冲突——整段必须先 verbatim 移 L2(规则句原句一字不改);L1 的重述是派生副本,与 L2 原句共存、不取代。判据:优化后原规则句的逐字节文本仍完整存在于 L2 verbatim 块。

⚠️ 这条判据别用 grep 验(Step 5.0 表第 4 行实测):原句多行时 grep -F 把它拆成多个 pattern 按行 OR,

丢了半段照样报命中 —— 它会为一次有损搬运出具无罪证明。用整串子串判断:

把原句存进临时文件,python3 -c "print(open('orig').read() in open('target').read())",要求连续完整匹配。

原则 4/5 管 L1 *如何呈现*,不授权销毁信号原句

> 完整案例分析见 references/progressive_disclosure_principles.md 案例 8、案例 14

⚠️ 反模式 7:用"故意删除"掩盖信息丢失

规则:任何"删除"都必须是事前决策(征求用户确认),不是事后分类(发现少了再编理由)。

  • 对每项计划删除的内容,必须说明其 canonical source 在哪里
  • 如果无法指出 canonical source → 不是"故意删除",是"信息丢失",必须补回
  • 对丢失内容分类"严重性"(高/低风险)是在为自己的错误找台阶。正确的态度是:任何丢失都是 bug,fix it

> 完整案例分析见 references/progressive_disclosure_principles.md 案例 9

⚠️ 反模式 8:纯否定规则(不给替代)

案例🚫 不要用 X —— 没说改用什么。

问题:纯否定会让 agent 瘫痪——它知道不能走这条路,但不知道该走哪条,于是要么卡住要么乱试(Shankar + GitHub 2500 仓库均实证)。

正确:每条 🚫 必配一个 ✅ 改用 Y

🚫 不要用全局 mutable 单例存请求状态
✅ 改用显式参数传递或 request-scoped context

优化时遇到孤立的禁令,补上正向替代再保留;补不出替代的禁令,说明规则本身没想清楚。

> ⚠️ 但若禁令原句嵌在 case study 混合段落里,先按反模式 6 整段 verbatim 移 L2,再在 L1 派生重述——不可改写原句(案例 14)。

⚠️ 反模式 9:假指针(指向不存在的内容)

案例:移走一段内容后写「详见 X.md」,但 X.md 里根本没有这段——指针指向空。

问题:比直接丢内容更隐蔽。5a「文件存在」会通过(X.md 确实存在),但内容不在那里;读者点进去才发现,且此时已无从知道原文是什么。本质是反模式 6(移动时压缩)+ 反模式 7(掩盖丢失)的组合:内容被砍 + 用一个看似合规的指针掩盖。

正确:写指针前当场 grep 验证目标真有该内容(Step 4 硬 gate)。指针指错文件(内容在 A、却写「详见 B」)是同类问题,按内容实际所在地修正、不是删指针。

> 完整案例分析见 references/progressive_disclosure_principles.md 案例 15


信息量检验

✅ 正确的信息量

| 检验项 | 通过标准 |

|--------|---------|

| 日常命令 | 不需要读 Level 2 |

| 常见错误 | 有完整诊断流程 |

| 代码编写 | 有可复制的模式 |

| 特定问题 | 知道读哪个 Level 2 |

| 触发索引 | 在文档末尾,表格形式 |

❌ 不足的信号

  • LLM 反复问同样的问题
  • LLM 每次重新推导代码模式
  • 用户需要反复提醒规则

❌ 过多的信号

  • 大段低频详细流程在 Level 1
  • 完全相同的内容在多处(注意:多入口指向同一资源 ≠ 重复)
  • 边缘情况和常见情况混在一起

项目级 vs 用户级

| 维度 | 用户级 | 项目级 |

|------|--------|--------|

| 位置 | ~/.claude/CLAUDE.md | 项目/CLAUDE.md |

| References | ~/.claude/references/ | docs/references/ |

| 信息范围 | 个人偏好、全局规则 | 项目架构、团队规范 |

硬检查:scope 错放(官方层级文档裁定)

用户级 ~/.claude/CLAUDE.md 会被所有项目加载,只能放普遍适用的东西。优化时对每节做 scope 检查:

| 内容特征 | 归属 | 不这样做的后果 |

|---|---|---|

| 项目名 / 部署目标 / 逐项目路径 / 项目凭据 | 项目级,绝不全局 | 无关项目被污染;没人按项目维护 → 路径/状态腐烂(典型 staleness) |

| 个人偏好、跨项目行为规则 | 用户级 | — |

| 团队规范、项目架构 | 项目级(入 VCS) | — |

工作流加一条:Step 2.1 分诊时,项目特定内容在用户级文件 = 自动判"搬到项目级",不是搬 Level 2、更不是原地修路径。详见 references/progressive_disclosure_principles.md 案例 13。


金丝雀检测法(可选,长期维护)

> 来源:HN 社区单源("Mr Tinkleberry"),方法论成立、成本极低,作诊断不作保证。

优化后想知道 CLAUDE.md 哪天又膨胀到"规则开始被忽略"——在文件里植入一条无害的命名指令(如"提到临时变量时命名为 tinkle_tmp")。日常对话中观察:Claude 还遵守 = 文件仍在遵守度阈值内;Claude 开始无视这条 = 文件已越过阈值,该重新分诊。比凭感觉判断"是不是太长了"廉价且客观。


快速检查清单

优化完成后,必须逐项检查(不可跳过):

信息完整性(最重要)

  • [ ] 原始文件的每个章节都有归属——在新 Level 1、Level 2、或有明确 canonical source
  • [ ] Level 2 文件内容与原始内容完全一致——没有在移动过程中被"精简"
  • [ ] 没有信号被静默删除——每项删除是反信号且有用户确认/canonical source(反信号删除正当,见 Step 2.1)
  • [ ] 没有把行数当成果/KPI/移动理由/汇报指标(诊断性观察不在此限,见「铁律」)
  • [ ] 每条「→ reference」指针都 grep 验证过目标真有该内容(无假指针 / 指针失准,Step 4 硬 gate;反模式 9)
  • [ ] 跑了独立 agent 5b 审计(默认动作,非「大量压缩时才用」;禁 fork;Step 5b)
  • [ ] 审阅之后若又改过:要么理由写进审阅记录,要么再派一轮(Step 5b 推论①)

结构质量

  • [ ] 「信息记录原则」在文档开头(防止未来膨胀)
  • [ ] Reference 索引在文档开头(入口1:遇到问题查这里)
  • [ ] 核心命令表完整
  • [ ] 铁律/禁令有代码示例
  • [ ] 常见错误有完整诊断流程(症状→原因→修复)
  • [ ] 代码模式可直接复制
  • [ ] 目录映射(功能→文件)
  • [ ] 「修改代码前必读」表格(入口2:按"要改什么"索引)
  • [ ] Reference 触发索引在文档末尾(入口3:长对话后复述)
  • [ ] 每个 Level 2 引用都有触发条件
  • [ ] 引用的文件都存在
  • [ ] 信号分诊已执行:反信号有候选删除清单 + 用户确认(Step 2.1)
  • [ ] 每条铁律/禁令带一行 Why:(原则 5)
  • [ ] 优先级用 ✅/⚠️/🚫 三态,不是一律"铁律"(原则 4)
  • [ ] 每条 🚫 都配了 ✅ 替代(反模式 8)
  • [ ] 项目特定内容没有留在用户级文件(scope 硬检查)
  • [ ] 引用未使用 @import 做卸载(@import 不省上下文)

How to use it

Copy the folder

Take daymade/claude-md-progressive-disclosurer from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.