HomeJournalThis post

Agent Skills: Progressive Disclosure That Scales

Structure Agent Skills for precise activation, lean context loading, trusted references, and deterministic regression receipts.

JP
JP Casabianca
AI Engineer and Product Designer · full-stack delivery · Bogotá

Agent Skills progressive disclosure works when an agent sees enough metadata to choose correctly, then opens only the instructions and resources required for the task. This tutorial turns that idea into an activation matrix, a context receipt, and explicit trust warnings you can test before publishing a skill.

Agent Skills progressive disclosure needs a contract

Agent Skills progressive disclosure divides context into three deliberate apertures. The catalog exposes a small name and description so the client can decide whether a skill is relevant. The SKILL.md body opens only after activation and contains the operational procedure. Scripts, references, and assets stay closed until a named step actually needs them. The result is not merely fewer tokens; it is a reviewable custody chain for instructions.

Freeze the contract before writing prose. For every representative prompt, declare which single skill should activate, which prompts should activate none, which support files may open, and which instruction source wins when names collide. Record loaded characters and a declared four-characters-per-token estimate, but label that number as an estimator rather than a universal tokenizer result. Agent Skills progressive disclosure succeeds when expected activation, expected references, and safe precedence all match.

The official Agent Skills specification defines the directory and frontmatter foundation. That grammar is necessary, but a syntactically valid skill can still activate too broadly or drag irrelevant material into context. Treat validation as two layers: conformance to the file contract and product-specific regression behavior. This lab uses synthetic skills and prompts, so it can test the disclosure mechanism without claiming how any production agent chooses tools or resists hostile content.

Three-stage context apertureCatalog metadata opens the skill body, which opens only two required resources while unrelated branches remain visibly closed; the displayed sizes are explicitly illustrative, not receipt measurements.illustrative metadata186 charsSKILL.mdillustrative 1,420script · openreference · openasset · closed
Three-stage context aperture
Catalog metadata opens the skill body, which opens only two required resources while unrelated branches remain visibly closed; the displayed sizes are explicitly illustrative, not receipt measurements.
  1. The labels “186 chars” and “1,420” are illustrative sizes, not values from the downloadable receipt.
  2. Expose only discriminative catalog metadata.
  3. Load SKILL.md after activation.
  4. Open the script and reference because named steps require them.
  5. Leave the unrelated asset closed and count actual opened characters in the artifact receipt.
Figure 1: Illustrative sizes show how context grows through named apertures; the lab receipt supplies the actual fixture counts.

Write catalog metadata that discriminates

The catalog description is a routing surface, not a miniature manual. It should name the task, likely user language, and meaningful exclusions in a few concrete sentences. “Helps with files” overlaps nearly everything. “Create and visually verify Word documents; do not use for spreadsheets or PDFs” gives an activator nouns, verbs, and boundaries that can be tested. Good SKILL.md metadata earns context by discriminating before any detailed instruction is loaded.

Build a trigger matrix with positive, near-miss, and negative prompts. A document skill might own “redline this DOCX,” lose “analyze this CSV” to a spreadsheet skill, and stay idle for “summarize this pasted paragraph.” Add collision prompts that contain vocabulary from two descriptions. Skill activation becomes measurable when false positives and false negatives have named rows, not when authors debate descriptions by intuition. Agent Skills progressive disclosure supplies the boundary for that measurement.

Avoid burying the important exclusion in the body. The body is unavailable until after routing, so a description that says “Use for documents” cannot be repaired by a late warning that PDFs belong elsewhere. This mirrors the value of separating repository context from task-specific skill instructions: the layer that makes a decision must contain the evidence necessary for that decision. Keep the description stable enough to regression-test, and revise the matrix whenever catalog neighbors change.

Keep the SKILL.md body bounded and operational

Once activated, the body should tell the agent what outcome to produce, which sequence is mandatory, where decisions branch, and what evidence closes the task. Put safety boundaries beside the action they constrain. Prefer exact commands, file contracts, and verification criteria over essays about the domain. Agent Skills progressive disclosure is weakened when the body repeats entire references “just in case,” because every activation pays that context cost.

Use progressive instructions inside the body as well. Start with the common path, then link a reference for a specific variant: a provider guide, a rendering checklist, or a schema definition. State when to open each file and whether it is normative, illustrative, or generated. One-level references are easier to audit than chains where a reference silently points to five more references. If deeper discovery is unavoidable, require a receipt listing every opened resource.

The client implementation guide for adding Agent Skills support describes discovery and staged loading. A team implementation should preserve that observability: log the catalog entry seen, the chosen skill, the body digest, and explicitly opened resources. Those records help diagnose a poor choice without exposing unrelated private instructions. They also make context budgeting actionable. Authors can see whether a new 20-kilobyte appendix loads for one rare branch or every invocation, then move it behind the correct decision point.

Load scripts, references, and assets selectively

Support resources have different execution and trust properties. A script may transform files. A reference may contain normative constraints or untrusted examples. An asset may be copied into output. Give every resource a purpose statement, expected digest or version when stability matters, and a single body step that authorizes loading it. Agent Skills progressive disclosure should make unopened branches visible in the receipt, because “not loaded” is part of the result.

Consider a presentation skill with a default slide template, a typography reference, and a renderer. The template is needed only when creating a deck, the typography reference only when a visual review detects trouble, and the renderer after generation. Loading all three before the user has even asked for slides wastes context and blurs authority. The same selective structure helps shape product specifications agents can retrieve selectively instead of injecting an entire product archive.

Do not let resource filenames carry the whole policy. “notes.md” tells neither the client nor the reviewer whether it is required. Use stable names, but pair them with explicit conditions in SKILL.md. The deterministic lab resolves only one level, records the resource characters added to context, and fails missing references. That narrow rule is intentional. It proves a comprehensible graph and prevents an apparently small skill from expanding through an unbounded link tree. Production clients may support richer retrieval, but their receipts should remain equally explicit.

Model trust and precedence before shadowing

A personal skill, project skill, and bundled skill can share a name while carrying different authority. Decide precedence before resolution, surface shadowing, and refuse any path that silently promotes a lower-trust source. Project instructions can be appropriate for a repository task, but content retrieved from that repository is not automatically higher authority than the user or platform. Agent Skills progressive disclosure needs a skill trust boundary as much as a token budget.

Separate selection from obedience. A relevant reference can still contain text that attempts to redirect the task, request secrets, or override higher-priority constraints. Mark supporting content as data unless the trusted SKILL.md explicitly grants it an instructional role. This is the same boundary used to keep retrieved instructions inside an explicit trust boundary. A disclosure system that loads fewer files reduces exposure, but it does not prove those files are safe.

The figure encodes trust problems with shapes and labels: a doubled node means shadowing, a broken edge means a missing reference, and a warning triangle means untrusted instruction text. Color is supplementary. The lab rejects duplicate active names across equal-precedence sources and reports lower-precedence shadowed candidates rather than erasing them. That receipt gives reviewers enough context to understand why one body loaded while another stayed closed. It is a deterministic precedence test, not a security certification.

Activation confusion and context matrixSynthetic prompts cross skill choices with explicit correct, false-positive, false-negative, collision, and loaded-context outcomes.prompt fixtureexpectedselectedcollisioncontextredline a DOCXanalyze a CSVexport a PDFambiguous create
Activation confusion and context matrix
Synthetic prompts cross skill choices with explicit correct, false-positive, false-negative, collision, and loaded-context outcomes.
Matrix result vocabulary
MarkMeaningAction
CircleExpected selectionKeep
CrossFalse activationRewrite metadata
PlusCollisionAdd exclusion
Dashed crossMissed activationAdd discriminative language
Figure 2: Routing quality and context cost stay separate; no average hides the wrong skill.

Measure activation collisions and context cost

Precision and recall are useful vocabulary for the frozen matrix, provided the population is stated. Precision asks how many activated cases were intended. Recall asks how many intended activations happened. Add a confusion count, loaded character total, estimated token total, and unexpected reference count. Agent Skills progressive disclosure should never compress those into one magic score; a low context estimate cannot excuse the wrong skill, and perfect activation cannot excuse an unsafe reference.

Run comparisons when a catalog changes. If a new “API docs” description steals three prompts from an established OpenAPI skill, the matrix should fail before release. If a revised body loads a large general reference for every task, the context receipt should show the jump. Treat the total as an agent context budget with a declared estimator, never as provider billing. This borrows the principle of progressive discovery for large tool catalogs, where initial descriptions enable selection and details arrive later.

Use synthetic prompts that reflect task shapes without copying private user data. Include paraphrases, ambiguous requests, explicit filenames, and domain neighbors. Keep the expected label under version control and require a reason for every update. Measurements remain local to the fixture: they do not predict real-world activation rates or provider token billing. Their purpose is change detection. A stable matrix tells the team precisely which routing behavior it intends to preserve, and a stable receipt hash makes unexpected edits visible.

Run the deterministic disclosure lab

The downloadable Node lab embeds six synthetic skill records and a frozen trigger matrix. It parses the minimal frontmatter fields, matches declared trigger terms, applies an explicit source precedence order, resolves one-level references, and totals the characters opened. The same fixture produces one SHA-256 receipt. Agent Skills progressive disclosure is accepted only when every expected activation and resource list matches and no unsafe precedence or missing reference appears.

Three hostile controls are built in. One prompt contains overlapping trigger terms and must be reported as ambiguous. One skill points to an absent reference and must fail resolution. One project skill attempts to shadow a bundled skill without the declared precedence path and must produce a warning. These controls matter more than a prewritten PASS line because a reviewer can change a fixture and observe the named failure.

The official skills-ref reference library is useful primary context for parser and validation behavior. The lab remains deliberately smaller: it is a teaching contract, not an alternative validator. It does not inspect a real agent catalog, infer semantic similarity, measure a provider tokenizer, or establish safety. Read the emitted JSON, confirm the estimator declaration, and compare the digest in continuous integration. If identical inputs do not reproduce the hash, the receipt is invalid even when all row labels say pass.

Runnable artifact — Not a production-agent benchmark, universal token estimate, or safety proof. Every displayed counter must come from the embedded fixture and receipt.

import assert from "node:assert/strict";
import { createHash } from "node:crypto";
const canonical=value=>JSON.stringify(value,(_,item)=>item&&typeof item==="object"&&!Array.isArray(item)?Object.fromEntries(Object.entries(item).sort(([a],[b])=>a.localeCompare(b))):item);
const sha=value=>createHash("sha256").update(typeof value==="string"?value:canonical(value)).digest("hex");
const files={
  "documents/SKILL.md":"---\nname: documents\ndescription: Create or edit DOCX documents; do not use for PDFs or spreadsheets.\ntriggers: docx,word,redline\nreferences: render.md\n---\nOpen render.md only after editing. Render and inspect every page.",
  "documents/render.md":"Run the local document renderer and inspect generated page images.",
  "spreadsheets/SKILL.md":"---\nname: spreadsheets\ndescription: Create or analyze spreadsheet workbooks; do not use for documents or PDFs.\ntriggers: xlsx,csv,spreadsheet\nreferences: formulas.md\n---\nOpen formulas.md only when formulas are required.",
  "spreadsheets/formulas.md":"Recalculate formulas and inspect error cells.",
  "pdf/SKILL.md":"---\nname: pdf\ndescription: Read or create PDF files; do not use for DOCX or spreadsheets.\ntriggers: pdf,acroform\nreferences: forms.md\n---\nOpen forms.md only for fillable forms.",
  "pdf/forms.md":"Inspect field names, types, values, and appearances.",
  "api/SKILL.md":"---\nname: api\ndescription: Design bounded HTTP API contracts.\ntriggers: openapi,graphql,endpoint\nreferences: limits.md\n---\nOpen limits.md for pagination work.",
  "api/limits.md":"Require finite page and payload limits.",
  "image/SKILL.md":"---\nname: image\ndescription: Generate raster image assets.\ntriggers: png,raster,photo\nreferences: palette.md\n---\nOpen palette.md only for palette work.",
  "image/palette.md":"Record the authored palette and output digest.",
  "audio/SKILL.md":"---\nname: audio\ndescription: Inspect or transform audio files.\ntriggers: wav,audio,transcript\nreferences: missing.md\n---\nOpen missing.md for codec analysis."
};
function parseSkill(path){const source=files[path];if(!source)throw new Error("missing-skill:"+path);const match=/^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(source);if(!match)throw new Error("frontmatter:"+path);const meta=Object.fromEntries(match[1].split("\n").map(line=>{const i=line.indexOf(":");return[line.slice(0,i).trim(),line.slice(i+1).trim()]}));return{path,source,body:match[2],name:meta.name,description:meta.description,triggers:meta.triggers.split(",").map(v=>v.trim()),references:meta.references?meta.references.split(",").map(v=>v.trim()):[]}}
const skills=Object.keys(files).filter(path=>path.endsWith("SKILL.md")).map(parseSkill);
const matrix=[
  {prompt:"redline this DOCX",expected:"documents",refs:["render.md"]},
  {prompt:"analyze quarterly CSV",expected:"spreadsheets",refs:[]},
  {prompt:"fill this AcroForm PDF",expected:"pdf",refs:["forms.md"]},
  {prompt:"bound this OpenAPI pagination endpoint",expected:"api",refs:["limits.md"]},
  {prompt:"generate a raster PNG",expected:"image",refs:[]},
  {prompt:"summarize this paragraph",expected:null,refs:[]}
];
function select(prompt,catalog=skills){const lower=prompt.toLowerCase(),matches=catalog.filter(skill=>skill.triggers.some(trigger=>lower.includes(trigger)));if(matches.length>1)throw new Error("ambiguous-activation:"+matches.map(v=>v.name).join(","));return matches[0]||null}
function resolve(skill,names){return names.map(name=>{if(!skill.references.includes(name))throw new Error("undeclared-reference:"+name);const path=skill.path.replace("SKILL.md",name),source=files[path];if(!source)throw new Error("missing-reference:"+path);return{path,characters:source.length,sha256:sha(source)}})}
const rows=matrix.map(test=>{const skill=select(test.prompt);assert.equal(skill?.name||null,test.expected);const resources=skill?resolve(skill,test.refs):[];const loadedCharacters=(skill?skill.description.length+skill.body.length:0)+resources.reduce((sum,item)=>sum+item.characters,0);return{prompt:test.prompt,expected:test.expected,selected:skill?.name||null,loadedResources:resources,loadedCharacters,declaredTokenEstimate:Math.ceil(loadedCharacters/4)}});
const hostile={};
try{select("redline a DOCX then export PDF")}catch(error){hostile.ambiguous=error.message}
try{resolve(skills.find(skill=>skill.name==="audio"),["missing.md"])}catch(error){hostile.missingReference=error.message}
const tiers={personal:3,project:2,bundled:1};
function choose(candidates){const sorted=[...candidates].sort((a,b)=>tiers[b.tier]-tiers[a.tier]);if(sorted.length>1&&tiers[sorted[0].tier]===tiers[sorted[1].tier])throw new Error("unsafe-precedence:"+sorted[0].name);return{selected:sorted[0],shadowed:sorted.slice(1)}}
try{choose([{name:"documents",tier:"project"},{name:"documents",tier:"project"}])}catch(error){hostile.unsafePrecedence=error.message}
assert.match(hostile.ambiguous,/ambiguous-activation/);assert.match(hostile.missingReference,/missing-reference/);assert.match(hostile.unsafePrecedence,/unsafe-precedence/);
const catalog=skills.map(({name,description,path,source})=>({name,description,path,sourceSha256:sha(source)}));
const core={schema:"agent-skill-disclosure-receipt-v1",fixtureVersion:"2026-09-07",provenance:"Embedded synthetic skills and prompts; no private instruction files or production agent logs.",claimBoundary:"Disclosure contract and reference graph only; not a production-agent benchmark, universal token estimate, or safety proof.",estimator:"ceil(loaded characters / 4); illustrative, not provider token billing",catalog,matrix:rows,hostile,precedence:{order:["personal","project","bundled"],example:choose([{name:"documents",tier:"project"},{name:"documents",tier:"bundled"}])}};
console.log(JSON.stringify({...core,receiptHash:sha(core)},null,2));
console.log("PASS: activation matrix, one-level references, context estimator, and hostile precedence controls verified");

Publish the receipt and regression matrix

A release receipt should include the skill identifier, catalog description digest, SKILL.md body digest, source tier, chosen prompts, rejected prompts, references opened per case, character and token-estimate totals, shadowed candidates, warnings, and fixture hash. Publish the matrix beside the skill change so reviewers can connect a description edit to its routing effect. Agent Skills progressive disclosure is a maintained contract, not a one-time cleanup.

Require a new run when a description, neighbor skill, precedence rule, referenced filename, or client loading contract changes. Keep historical receipts so a context increase is explainable. Do not store private prompt transcripts merely to improve the fixture; synthetic rows can cover the same structural risks without turning the regression suite into a data-retention problem.

The final decision remains human. A passing lab shows that this bounded catalog, matrix, and reference graph obey the declared rules. It cannot show that every future request will select correctly, that every resource is harmless, or that the instructions are good at their domain job. Those questions need product evaluation and security review. What the receipt does provide is a sharp maintenance loop: activation changes are reviewed as routing changes, resource additions are reviewed as context and trust changes, and failures point to a specific row. Run the activation matrix before adding the next reference to a skill.

Skill trust and precedence graphPersonal, project, and bundled sources converge on one resolver that reports shadowing, missing references, and untrusted instruction text.personalprojectbundledresolve name+ precedenceloadedmissingshadow
Skill trust and precedence graph
Personal, project, and bundled sources converge on one resolver that reports shadowing, missing references, and untrusted instruction text.
  • Circle: personal source.
  • Rectangle: project source.
  • Triangle: bundled source.
  • Doubled marker: shadowed name.
  • Cross: missing reference.
  • Resolver publishes the selected source and every warning.
Figure 3: Selection can use relevant content without granting lower-trust text higher authority.