Progressive Disclosure Research for Skills

Research conducted December 18, 2025 on progressive disclosure patterns for skill systems.

By Cameron (@cameron.stream)
Published:

Progressive Disclosure at Scale: Operating Large Skill Libraries

Focus: Practical patterns for managing 100-1000+ skills Date: December 18, 2025


Executive Summary

Progressive disclosure enables agents to work with arbitrarily large skill libraries by loading information in stages: metadata → instructions → resources. This report focuses on practical implementation patterns for systems with 100+ skills, where naive approaches fail.

Critical insight: At scale, you're building a discovery system, not a documentation library.

Performance data: 36.8% improvement with dynamic loading across 200+ tasks (Letta ContextBench). Discovery scales O(log n), browsing scales O(n) and fails beyond ~50 skills.


The Scaling Wall

When Flat Structures Break

10-50 skills: Flat list in memory block works fine 50-100 skills: Memory block gets crowded, discovery slower but functional 100-500 skills: Flat structure at breaking point, need better organization 500+ skills: Flat structure fails, hierarchical organization required

The problem at 100+ skills:

Memory block contains:
- 100 skills × 100 words metadata = 10,000 words
- Approaches context window limits
- Agent spends significant time scanning metadata
- Discovery precision drops (too many similar-sounding skills)

Key metrics from production (100+ skill systems):


Three-Tier Architecture for Scale

Tier 1: Discovery Metadata (Always Loaded)

Challenge: How do you fit 1000 skills in memory without overwhelming agents?

Solution: Hierarchical metadata with progressive drilling

# Top-level categories (always in memory)
letta/          - Letta product ecosystem
  ├─ agents/    - Agent development patterns
  ├─ sdks/      - SDK integrations
  └─ ops/       - Operations and deployment

tools/          - External tool integrations
  ├─ web/       - Web scraping, testing
  ├─ data/      - Data processing
  └─ ml/        - Machine learning tools

# Skill metadata (loaded per category)
letta/agents/
  - memory-architecture: Designing memory blocks...
  - tool-patterns: Tool selection and configuration...
  - multi-agent: Coordinating multiple agents...

Implementation pattern:

Example agent flow:

Task: "Set up memory blocks for a customer support agent"

Agent reasoning:
1. Scan categories → "letta/" relevant
2. Load letta/ skill list → See "memory-architecture" 
3. Load memory-architecture SKILL.md
4. Navigate to references/customer-support-patterns.md
5. Execute task

Tier 2: Category Organization

Practical category structure for 500+ skills:

<domain>/
├── <subdomain>/
│   ├── CATEGORY.md         # Overview, when to use these skills
│   ├── skill-1/
│   ├── skill-2/
│   └── skill-3/
└── README.md               # Domain overview

CATEGORY.md template:

# Agent Development Skills

Use these skills when building, configuring, or debugging Letta agents.

## Available Skills

- **memory-architecture** - Use when designing memory blocks and data flow
- **tool-patterns** - Use when selecting and configuring tools
- **model-selection** - Use when choosing LLMs for specific workloads
- **multi-agent** - Use when coordinating multiple agents

## Quick Decision Tree

Building new agent? → Start with memory-architecture
Debugging tool issues? → See tool-patterns
Performance problems? → Check model-selection
Multiple agents? → Read multi-agent

Key insight: CATEGORY.md is metadata for a group of skills. Agents load it to decide which skill to load next.

Tier 3: Skill Resources

No change at large scale - skills still have references/, scripts/, assets/. But organization becomes more critical:

At 500+ skills, standardize reference structure:

skill-name/
├── SKILL.md                    # Always same structure
├── references/
│   ├── README.md               # Index (required at scale)
│   ├── quick-start.md          # Standard name
│   ├── common-patterns.md      # Standard name
│   ├── api-reference.md        # Standard name
│   └── troubleshooting.md      # Standard name
├── scripts/
│   └── README.md               # What each script does
└── assets/
    └── README.md               # What each asset is for

Why standardization matters: With 500+ skills, agents learn the pattern. They know "quick-start.md exists in references/" and can navigate directly.


Discovery Mechanisms at Scale

Problem: Query-based Discovery

Simple query (works to ~100 skills):

Agent: "I need to test a web application"
System: Scans all skill metadata
Result: Finds "webapp-testing"

Complex query at scale (500+ skills):

Agent: "I need to test a web application"
System: 
  1. Identify domain: tools/web/
  2. Load tools/web/CATEGORY.md
  3. Scan category skills
  4. Find webapp-testing
Result: 3 steps instead of 1, but scales to 10,000 skills

Hierarchical Discovery Pattern

Implementation:

interface SkillDiscovery {
  // Phase 1: Category selection
  selectCategories(query: string): Category[]  // From memory block
  
  // Phase 2: Skill selection
  selectSkills(category: Category, query: string): Skill[]  // Load CATEGORY.md
  
  // Phase 3: Skill loading
  loadSkill(skill: Skill): SkillContent  // Load SKILL.md
}

// Agent flow:
1. categories = selectCategories("test web application")
   // Returns: [tools/web/, tools/testing/]

2. skills = selectSkills(tools/web/, "test web application")  
   // Loads tools/web/CATEGORY.md
   // Returns: [webapp-testing, web-scraping]

3. content = loadSkill(webapp-testing)
   // Loads full SKILL.md

Complexity: O(log n) - each step narrows search space

Alternative: Flat discovery with embeddings (more complex but faster):


Metadata Design for Discoverability

The Metadata Challenge

At 10 skills: Vague metadata acceptable ("Python utilities") At 100 skills: Need specificity ("Python async patterns for web services") At 1000 skills: Need extreme precision + disambiguation

Critical elements:

Template for 100+ skill systems:

name: skill-identifier
category: domain/subdomain
description: |
  Use when [specific trigger with keywords]. 
  Provides [concrete capabilities].
  Covers [explicit scope].
  Does NOT cover [common misconceptions].
  See also: [related-skill] for [alternative use case].
keywords: [keyword1, keyword2, keyword3]  # Optional but helpful at scale

Example - Good metadata at scale:

name: playwright-web-testing
category: tools/web
description: |
  Use when testing web UIs in a real browser using Playwright.
  Provides page navigation, element interaction, screenshot capture, network 
  interception. Covers both headless and headed testing.
  Does NOT cover API testing (see rest-api-testing) or load testing 
  (see load-testing).
  See also: puppeteer-testing for Chrome-only testing.
keywords: [playwright, browser, testing, ui, e2e, screenshots]

Example - Poor metadata at scale:

name: web-tool
description: Helpful utilities for web development
# Issues: Vague trigger, unclear scope, no differentiation

Disambiguation at Scale

Problem: Multiple similar skills, agent picks wrong one

Example collision:

Solution: Explicit differentiation in metadata

name: api-testing
description: |
  Use when testing REST APIs (not GraphQL - see graphql-testing).
  Provides HTTP request testing, response validation, mock servers.
  For API integration patterns, see api-integration.
  For API design guidance, see api-design.


Organization Strategies

Categorization Approaches

By domain (recommended for 100-500 skills):

letta/          - Letta ecosystem
tools/          - General tools
templates/      - Reusable templates
domain-X/       - Specific domains

By function (works well for specialized systems):

development/    - Building things
operations/     - Running things
analysis/       - Understanding things

By technology (good for tech-specific libraries):

python/         - Python skills
typescript/     - TypeScript skills
databases/      - Database skills

Hybrid (necessary at 500+ skills):

letta/
  ├── agents/           # By function within domain
  ├── sdks/
  └── ops/

tools/
  ├── web/             # By technology within domain
  ├── data/
  └── ml/

templates/
  ├── frontend/        # By use case within domain
  ├── backend/
  └── docs/

Category Size Guidelines

Optimal category size: 10-30 skills

Too small (<5 skills): Over-categorization, high navigation overhead Too large (>50 skills): Category doesn't help, still scanning too many options

Example of good sizing:

tools/web/                    # 18 skills - good size
  - playwright-testing
  - puppeteer-testing
  - selenium-testing
  - web-scraping
  - browser-automation
  - ... (13 more)

tools/web/testing/            # Bad: Over-categorization
  - playwright/              # 3 skills
  - puppeteer/               # 2 skills
  - selenium/                # 4 skills
# Better to keep flat with good metadata


Memory Block Management

The Memory Block Scaling Problem

Simple approach (fails at 100+ skills):

skills memory block:
[metadata for all 1000 skills]
# 100,000+ words, dominates context window

Hierarchical approach (scales to 10,000 skills):

skills memory block:
[category list - 20 categories × 50 words = 1,000 words]

Agent loads on demand:
- CATEGORY.md for relevant categories
- SKILL.md for specific skills

Dynamic Memory Block Updates

Pattern: Update memory block as navigation happens

// Initial memory block: Just categories
memoryBlock.skills = formatCategories(categories);

// Agent identifies relevant category
loadCategory('tools/web/');
memoryBlock.skills += formatCategorySkills('tools/web/');

// Agent selects specific skill
loadSkill('webapp-testing');
memoryBlock.loaded_skills += skillContent;

// Agent unloads when done
memoryBlock.loaded_skills = removeSkill('webapp-testing');

Key insight: Memory block is dynamic, updated during task execution.

Memory Block Structure at Scale

# Always present
categories:
  - letta/ : Letta product ecosystem (agents, SDKs, ops)
  - tools/ : External tool integrations (web, data, ml)
  - templates/ : Reusable templates and patterns
  
# Loaded on demand (category exploration)
current_category: tools/web/
category_skills:
  - webapp-testing: Testing web UIs with Playwright...
  - web-scraping: Extracting data from websites...
  - rest-api-testing: Testing REST APIs with requests...
  
# Loaded on demand (active work)  
loaded_skills:
  - webapp-testing: [full SKILL.md content]


Tooling and Infrastructure

Discovery Tools for Large Libraries

Search/query tool (essential at 100+ skills):

interface SkillSearch {
  // Keyword search across all metadata
  search(query: string): Skill[]
  
  // Category-scoped search
  searchCategory(category: string, query: string): Skill[]
  
  // Semantic search (requires embeddings)
  semanticSearch(query: string, topK: number): Skill[]
}

Usage in agent reasoning:

Agent: I need to test a web application
Tool call: search("test web application")
Result: [webapp-testing, web-scraping, api-testing]
Agent: Load webapp-testing

Skill Registry (500+ skills)

Problem: Need metadata index without loading all files

Solution: Skill registry file

{
  "skills": [
    {
      "id": "webapp-testing",
      "category": "tools/web",
      "path": "tools/web/webapp-testing",
      "name": "Web Application Testing",
      "description": "Testing web UIs with Playwright...",
      "keywords": ["playwright", "testing", "browser"],
      "updated": "2025-12-15"
    }
  ],
  "categories": [
    {
      "id": "tools/web",
      "name": "Web Tools",
      "description": "Tools for web development and testing",
      "skill_count": 18
    }
  ]
}

Benefits:

Trade-off: Registry must stay in sync with actual skills (build step)


Practical Implementation Patterns

Pattern 1: Lazy Category Loading

class SkillSystem {
  // Always loaded
  categories: Category[]
  
  // Lazy loaded
  categoryCache: Map<string, CategoryContent>
  skillCache: Map<string, SkillContent>
  
  async discoverSkill(query: string): Skill {
    // 1. Identify category from query
    const category = await this.selectCategory(query);
    
    // 2. Load category if not cached
    if (!this.categoryCache.has(category.id)) {
      const content = await this.loadCategory(category.id);
      this.categoryCache.set(category.id, content);
    }
    
    // 3. Select skill from category
    const categoryContent = this.categoryCache.get(category.id);
    const skill = await this.selectSkill(categoryContent, query);
    
    return skill;
  }
}

Pattern 2: Skill Preloading

Use case: Agent working in specific domain, likely to use multiple related skills

async preloadCategory(categoryId: string) {
  const category = await this.loadCategory(categoryId);
  
  // Preload all skills in category (parallel)
  const skills = await Promise.all(
    category.skills.map(s => this.loadSkill(s.id))
  );
  
  // Now agent can access any skill in category instantly
}

When to use: Task spans entire domain (e.g., "build a web app" → preload all web skills)

Pattern 3: Skill Dependency Loading

Problem: Some skills reference other skills

# In api-integration/SKILL.md
Prerequisites: Load api-authentication skill first
Related skills: error-handling, rate-limiting

Implementation:

async loadSkillWithDependencies(skillId: string): SkillContent {
  const skill = await this.loadSkill(skillId);
  
  // Parse dependencies from skill content
  const deps = this.parseDependencies(skill);
  
  // Load dependencies
  for (const dep of deps) {
    if (!this.skillCache.has(dep)) {
      await this.loadSkill(dep);
    }
  }
  
  return skill;
}

Warning: Can lead to cascade loading. Use sparingly and document clearly.


Operational Patterns

Skill Discovery Analytics

Essential metrics at scale:

Skill Maintenance at Scale

Patterns for 500+ skills:

1. Automated validation:

# Daily checks
- All SKILL.md files have valid frontmatter
- All references/ links in SKILL.md are valid
- Category structures match file system
- Skill registry in sync with actual files

2. Version tracking:

# In SKILL.md frontmatter
version: 2.1.0
last_updated: 2025-12-15
changelog:
  - 2.1.0: Added GraphQL support
  - 2.0.0: Breaking change - new API

3. Deprecation process:

# Mark as deprecated
deprecated: true
deprecated_message: "Use api-testing-v2 instead"
replacement: api-testing-v2
sunset_date: 2026-03-15

4. Usage-based pruning:


Migration Path

Growing from 50 → 100 → 500 skills

Phase 1: 50 skills (flat structure)

.skills/
├── skill-1/
├── skill-2/
└── ...
Memory block: All metadata (manageable)

Phase 2: 100 skills (add categories)

.skills/
├── category-1/
│   ├── skill-1/
│   └── skill-2/
└── category-2/
    └── skill-3/
Memory block: Categories + skill metadata (tight but ok)

Phase 3: 500 skills (hierarchical)

.skills/
├── domain-1/
│   ├── subdomain-1/
│   │   └── skill-1/
│   └── subdomain-2/
│       └── skill-2/
└── domain-2/
    └── subdomain-3/
        └── skill-3/
Memory block: Just categories, lazy load everything else

Phase 4: 1000+ skills (add infrastructure)

Refactoring Checklist

Before reorganizing 100+ skills:


Key Takeaways


Recommended Architecture (500+ skills)

Memory Block:
  - Category list (20-50 categories, <2k words)
  
Discovery Process:
  1. Query → Identify categories (parallel evaluation)
  2. Load CATEGORY.md for relevant categories
  3. Scan category skills
  4. Load specific SKILL.md
  5. Navigate to references/ as needed
  
Infrastructure:
  - Skill registry (single JSON index)
  - Search tool (keyword + optional semantic)
  - Analytics (discovery funnel + usage)
  - Automated validation (daily)
  
Organization:
  domain/subdomain/skill-name/
    ├── SKILL.md (standardized structure)
    ├── references/
    │   ├── README.md (required)
    │   ├── quick-start.md
    │   ├── common-patterns.md
    │   └── api-reference.md
    └── scripts/ (optional)

This architecture scales to 10,000+ skills with O(log n) discovery.


Research conducted December 2025 during skills repository restructuring and scaling analysis.