Skip to content

WebPerf Snippets: progressive disclosure in Claude Skills

Published:

In the previous articles in this series I explained how WebPerf Snippets are organized as Skills and how the scripts return structured JSON to minimize the agent’s work. There’s a third level of optimization still to cover: how much context each Skill consumes when it activates.

The problem isn’t that the scripts are large. It’s that SKILL.md was loading all available knowledge at activation time, even though most of it wasn’t needed to decide which script to run.

Anthropic’s progressive disclosure system

Anthropic’s official Skills guide (The Complete Guide to Building Skills for Claude) defines three content levels with a clear principle: load only what’s needed at each moment, and access detail only when required.

L1 — YAML frontmatter (always in context)

---
name: webperf-core-web-vitals
description: Intelligent Core Web Vitals analysis...
---

The frontmatter is part of the permanent system prompt. It’s in context for every conversation, regardless of which Skill is active. That’s why there’s a 1,024-character limit on the description: that content is always paid for.

L2 — SKILL.md body (loaded when the Skill activates)

Loaded when the agent determines the Skill is relevant. It should contain only what’s strictly necessary for the agent to decide which script to run: available scripts list, workflows, and decision trees. Every kilobyte here is consumed in every session where the Skill activates.

L3 — references/, scripts/, assets/ (loaded on demand)

Detailed documentation, descriptions for each snippet, threshold tables, result schemas. Read only when the agent needs them to interpret a specific result. Until then, the cost is zero.

The problem: everything was L2

The Skills generator, scripts/generate-skills.js, was adding four blocks to the SKILL.md body that could have stayed in L3:

Snippet table with truncated descriptions

| LCP Trail | Tracks every LCP candidate element during page load and highlights each one with a distinct pastel-c |

Truncating to 100 characters provided no real value. It wasn’t enough to understand the snippet, nor did it let the agent decide when to consult it.

Execution instructions block duplicated five times

The same six lines appeared identically in all five category Skills, already covered by the webperf meta-Skill:

1. mcp__chrome-devtools__navigate_page  → navigate to target URL
2. mcp__chrome-devtools__evaluate_script → run snippet code
3. mcp__chrome-devtools__get_console_message → capture console output

Inaccessible SCHEMA.md

The file .claude/skills/SCHEMA.md (19 KB) documents the complete return value schema for all 47 scripts. It existed but no Skill referenced it. Impossible for the agent to consult.

The solution: move detail to L3

All changes were made in a single file: scripts/generate-skills.js in the webperf-snippets repository. Skills are generated artifacts and aren’t edited directly.

Create references/snippets.md with full detail

Instead of appending a per-snippet section to the SKILL.md body (with description and threshold table), a separate L3 file is written:

const snippetLines = [];
for (const meta of metas) {
  snippetLines.push(`---`);
  snippetLines.push(`## ${meta.title}`);
  if (meta.description) {
    snippetLines.push("");
    snippetLines.push(meta.description);
  }
  snippetLines.push("");
  snippetLines.push(`**Script:** \`scripts/${meta.basename}.js\``);
  if (meta.thresholds) {
    snippetLines.push("");
    snippetLines.push("**Thresholds:**");
    snippetLines.push("");
    snippetLines.push(meta.thresholds);
  }
}
fs.writeFileSync(
  path.join(refsDir, "snippets.md"),
  snippetLines.join("\n") + "\n"
);

Replace the truncated table with a compact list

❌ Before:

## Available Snippets

| Snippet   | Description                                                                                          | File                 |
| --------- | ---------------------------------------------------------------------------------------------------- | -------------------- |
| LCP Trail | Tracks every LCP candidate element during page load and highlights each one with a distinct pastel-c | scripts/LCP-Trail.js |

✅ After:

## Scripts

- `scripts/LCP-Trail.js` — LCP Trail
- `scripts/LCP.js` — Largest Contentful Paint (LCP)

Descriptions and thresholds: `references/snippets.md`

Copy SCHEMA.md to references/schema.md

const schemaSrc = path.join(CLAUDE_SKILLS_DIR, "SCHEMA.md");
if (fs.existsSync(schemaSrc)) {
  fs.copyFileSync(schemaSrc, path.join(refsDir, "schema.md"));
}

Now the agent can consult the full schema when it receives an unexpected result, without that 19 KB cost being paid in every session.

Remove the duplicated execution instructions

The repeated block was removed from all five category Skills and replaced with a single reference line:

> Execute via `mcp__chrome-devtools__evaluate_script` → read with `mcp__chrome-devtools__get_console_message`.

Add a ## References section at the end of SKILL.md

## References

- `references/snippets.md` — Descriptions and thresholds for each script
- `references/schema.md` — Return value schema for interpreting script output

The resulting structure

skills/webperf-core-web-vitals/
  SKILL.md                    ← L2: script list + workflows
  scripts/
    CLS.js                    ← L3: executed when needed
    LCP.js
    ...
  references/
    snippets.md               ← L3: descriptions and thresholds
    schema.md                 ← L3: return value schema

Results

SkillBeforeAfterReduction
webperf-loading24,199 B11,149 B−54%
webperf-core-web-vitals13,890 B10,266 B−26%
webperf-interaction17,143 B12,443 B−27%
webperf-media13,273 B12,017 B−9%
webperf-resources11,700 B11,026 B−6%
Total L282,916 B59,612 B−28%

Skills with lower reductions (webperf-media, webperf-resources) have extensive WORKFLOWS.md files with detailed decision trees. That content is deliberately L2: the agent needs it when deciding which script to run.

In a full audit with three Skills active simultaneously, the L2 drops from ~55 KB to ~34 KB. The ~18 KB of references/snippets.md and 19 KB of references/schema.md remain available on demand. In the typical diagnostic session (run two or three scripts, interpret straightforward results), they’re never read.

Conclusion

The progressive disclosure system doesn’t change what the agent can do: all snippets remain available, all descriptions remain accessible, the schema remains consultable. What changes is when the cost of that knowledge is paid.

The agent loads the minimum needed to make decisions. The detail exists, but only appears in context when required to interpret a specific result. For a collection of 47 snippets across six Skills, the difference between loading everything always and loading only what’s needed is over 20 KB per full analysis session, and zero bytes of detail context in the typical case.


Next Post
WebPerf Snippets agent-first: structured returns and minimal tokens