UNPKG

claude-flow-novice

Version:

Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes CodeSearch (hybrid SQLite + pgvector), mem0/memgraph specialists, and all CFN skills.

1,131 lines (884 loc) 28.4 kB
# SkillLoader API Reference **Version:** 1.0.0 **Author:** TypeScript Specialist **Date:** 2025-11-15 --- ## Table of Contents 1. [Overview](#overview) 2. [Architecture](#architecture) 3. [API Reference](#api-reference) 4. [Performance](#performance) 5. [Database Schema](#database-schema) 6. [Usage Examples](#usage-examples) 7. [Error Handling](#error-handling) 8. [Testing](#testing) --- ## Overview The SkillLoader API provides high-performance, contextual skill loading for agent prompt building with LRU caching and SHA256 hash-based validation. ### Key Features - **Contextual Loading**: Load skills based on agent type, task context, and CFN Loop phase - **LRU Caching**: In-memory cache with 5-minute TTL and automatic eviction - **Hash Validation**: SHA256-based content integrity verification - **Bootstrap Skills**: Core skills always loaded without database dependency - **Performance**: <1s cold load, <100ms warm load - **Type Safety**: Full TypeScript support with strict typing ### Components ``` src/ ├── cli/ │ ├── skill-loader.ts # Main SkillLoader class │ └── skill-cache-validator.ts # SHA256 hash validation ├── db/ │ └── skills-query.ts # SQL query builder └── lib/ ├── database-service/ # Database abstraction layer └── logging.ts # Structured logging .claude/skills/cfn-skill-loader/ ├── SKILL.md # Skill documentation └── execute.sh # CLI entry point tests/ └── skill-loader.test.ts # Test suite (≥95% coverage) ``` --- ## Architecture ### Data Flow ``` Agent Spawn Request ↓ SkillLoader.loadContextualSkills() ↓ ┌───────────────────────────────┐ │ 1. Load Bootstrap Skills │ (Always included) │ - cfn-coordination │ │ - hook-pipeline │ │ - pre-edit-backup │ │ - cfn-agent-spawning │ │ - cfn-loop-validation │ └───────────────────────────────┘ ↓ ┌───────────────────────────────┐ │ 2. Query Database │ │ - Filter by agent type │ │ - Filter by task context │ │ - Filter by phase │ │ - Order by priority │ └───────────────────────────────┘ ↓ ┌───────────────────────────────┐ │ 3. Check LRU Cache │ │ - Validate TTL │ │ - Validate content hash │ │ - Update access time │ └───────────────────────────────┘ ↓ ┌───────────────────────────────┐ │ 4. Load from Files (on miss) │ │ - Read SKILL.md files │ │ - Compute SHA256 hash │ │ - Cache skill content │ └───────────────────────────────┘ ↓ Return SkillLoadResult (skills + metadata) ``` ### Caching Strategy **LRU (Least Recently Used) Cache:** - **Max Size**: 100 skills - **TTL**: 5 minutes (configurable) - **Eviction**: Oldest accessed entry when cache is full - **Validation**: SHA256 hash comparison on every cache hit **Cache Key Format:** ``` skill:{skillId}:{version} ``` **Cache Entry Structure:** ```typescript interface CachedSkillEntry { skillId: string; content: string; contentHash: string; // SHA256 cachedAt: Date; validUntil: Date; // cachedAt + TTL } ``` --- ## API Reference ### SkillLoader Class **Location:** `src/cli/skill-loader.ts` #### Constructor ```typescript new SkillLoader( dbService?: DatabaseService, logger?: Logger, skillsBasePath?: string ) ``` **Parameters:** - `dbService` (optional): Database service instance for skill metadata queries - `logger` (optional): Structured logger instance (default: auto-created) - `skillsBasePath` (optional): Base path to skills directory (default: `.claude/skills`) **Example:** ```typescript import { DatabaseService } from '../lib/database-service'; import { createLogger } from '../lib/logging'; import { SkillLoader } from './skill-loader'; const dbService = new DatabaseService({ sqlite: { type: 'sqlite', database: './cfn-loop.db' } }); await dbService.connect(); const logger = createLogger('skill-loader'); const loader = new SkillLoader(dbService, logger); ``` #### loadContextualSkills() **Main entry point for skill loading.** ```typescript async loadContextualSkills( options: SkillLoaderOptions ): Promise<SkillLoadResult> ``` **Parameters:** ```typescript interface SkillLoaderOptions { agentType: string; // Required: Agent type identifier taskContext?: string[]; // Optional: Context keywords for filtering maxSkills?: number; // Optional: Max skills to load (default: 20) includeBootstrap?: boolean; // Optional: Include bootstrap skills (default: true) phase?: string; // Optional: CFN Loop phase (loop1, loop2, loop3) } ``` **Returns:** ```typescript interface SkillLoadResult { skills: Skill[]; // Loaded skills with content loadTimeMs: number; // Total load time in milliseconds cacheHitCount: number; // Number of cache hits cacheMissCount: number; // Number of cache misses totalSkills: number; // Total skills loaded bootstrapCount: number; // Number of bootstrap skills } interface Skill { id: string; name: string; version: string; content: string; // Full skill markdown content contentHash: string; // SHA256 hash of content namespace?: string; // Skill namespace (e.g., 'cfn') priority?: number; // Priority (1-10) tags?: string[]; // Skill tags } ``` **Example:** ```typescript const result = await loader.loadContextualSkills({ agentType: 'backend-developer', taskContext: ['authentication', 'jwt', 'api'], maxSkills: 20, includeBootstrap: true, phase: 'loop3' }); console.log(`Loaded ${result.totalSkills} skills in ${result.loadTimeMs}ms`); console.log(`Cache hit rate: ${(result.cacheHitCount / (result.cacheHitCount + result.cacheMissCount) * 100).toFixed(2)}%`); ``` #### clearCache() **Clear all cached skills.** ```typescript clearCache(): void ``` **Example:** ```typescript loader.clearCache(); console.log('Cache cleared'); ``` #### getCacheStats() **Get cache statistics.** ```typescript getCacheStats(): { size: number; maxSize: number; ttlMinutes: number; hitRate?: number; } ``` **Example:** ```typescript const stats = loader.getCacheStats(); console.log(`Cache: ${stats.size}/${stats.maxSize} skills`); console.log(`TTL: ${stats.ttlMinutes} minutes`); ``` #### preloadSkills() **Preload specific skills into cache (warm-up).** ```typescript async preloadSkills(skillIds: string[]): Promise<void> ``` **Example:** ```typescript await loader.preloadSkills([ 'cfn-coordination', 'hook-pipeline', 'pre-edit-backup' ]); console.log('Common skills preloaded'); ``` --- ### SkillCacheValidator Class **Location:** `src/cli/skill-cache-validator.ts` #### Constructor ```typescript new SkillCacheValidator(logger?: Logger) ``` #### computeHash() **Compute SHA256 hash of content.** ```typescript computeHash(content: string): string ``` **Returns:** 64-character hex string **Example:** ```typescript const validator = new SkillCacheValidator(); const hash = validator.computeHash(skillContent); console.log(hash); // "a3c7b8d2f1e4..." ``` #### validateContent() **Validate content against expected hash.** ```typescript validateContent( content: string, expectedHash: string ): ValidationResult ``` **Returns:** ```typescript interface ValidationResult { isValid: boolean; expectedHash: string; actualHash?: string; reason?: string; } ``` **Example:** ```typescript const result = validator.validateContent(content, expectedHash); if (!result.isValid) { console.error(`Validation failed: ${result.reason}`); } ``` #### validateFile() **Validate file content against expected hash.** ```typescript async validateFile( filePath: string, expectedHash: string ): Promise<ValidationResult> ``` **Example:** ```typescript const result = await validator.validateFile( '.claude/skills/cfn-coordination/SKILL.md', expectedHash ); console.log(`File valid: ${result.isValid}`); ``` #### validateCachedEntry() **Validate cached entry (TTL + hash).** ```typescript validateCachedEntry( cachedEntry: CachedSkillEntry, expectedHash: string ): ValidationResult ``` **Validates:** 1. TTL not expired 2. Content hash matches cached hash (cache integrity) 3. Cached hash matches expected hash (database sync) **Example:** ```typescript const cachedEntry = cache.get(skillId); const dbHash = await getHashFromDatabase(skillId); const result = validator.validateCachedEntry(cachedEntry, dbHash); if (!result.isValid) { cache.delete(skillId); // Invalidate cache } ``` #### batchValidate() **Validate multiple skills in batch.** ```typescript batchValidate( skills: Array<{ skillId: string; content: string; expectedHash: string; }> ): Map<string, ValidationResult> ``` **Example:** ```typescript const results = validator.batchValidate([ { skillId: 'skill-1', content: '...', expectedHash: 'abc123' }, { skillId: 'skill-2', content: '...', expectedHash: 'def456' }, ]); for (const [skillId, result] of results) { if (!result.isValid) { console.error(`${skillId}: ${result.reason}`); } } ``` #### createCacheEntry() **Create cache entry with TTL.** ```typescript createCacheEntry( skillId: string, content: string, ttlMinutes?: number ): CachedSkillEntry ``` **Example:** ```typescript const entry = validator.createCacheEntry('skill-1', content, 5); cache.set('skill-1', entry); ``` #### verifyBatchIntegrity() **Verify cache integrity for multiple entries.** ```typescript verifyBatchIntegrity(entries: CachedSkillEntry[]): string[] ``` **Returns:** Array of corrupted skill IDs **Example:** ```typescript const allEntries = Array.from(cache.values()); const corruptedIds = validator.verifyBatchIntegrity(allEntries); if (corruptedIds.length > 0) { console.warn(`Corrupted: ${corruptedIds.join(', ')}`); corruptedIds.forEach(id => cache.delete(id)); } ``` --- ### SkillsQueryBuilder Class **Location:** `src/db/skills-query.ts` #### Static Methods All methods return `{ sql: string; params: any[] }` for safe parameterized queries. #### getSkillsByAgentType() ```typescript static getSkillsByAgentType( agentType: string, contextKeywords?: string[], phase?: string ): { sql: string; params: any[] } ``` **Example:** ```typescript const { sql, params } = SkillsQueryBuilder.getSkillsByAgentType( 'backend-developer', ['authentication', 'security'], 'loop3' ); const skills = await sqlite.raw<SkillRecord[]>(sql, params); ``` #### getBootstrapSkills() ```typescript static getBootstrapSkills(): { sql: string; params: any[] } ``` **Example:** ```typescript const { sql, params } = SkillsQueryBuilder.getBootstrapSkills(); const bootstrapSkills = await sqlite.raw(sql, params); ``` #### validateContentHash() ```typescript static validateContentHash( skillId: string, expectedHash: string ): { sql: string; params: any[] } ``` **Example:** ```typescript const { sql, params } = SkillsQueryBuilder.validateContentHash( 'cfn-coordination', 'abc123def456' ); const result = await sqlite.raw(sql, params); console.log(`Hash valid: ${result.length > 0}`); ``` #### insertSkillUsage() ```typescript static insertSkillUsage( usage: Omit<SkillUsageRecord, 'id' | 'loaded_at'> ): { sql: string; params: any[] } ``` **Example:** ```typescript const { sql, params } = SkillsQueryBuilder.insertSkillUsage({ skill_id: 'cfn-coordination', agent_id: 'agent-12345', agent_type: 'backend-developer', execution_time_ms: 85, confidence_impact: 0.92 }); await sqlite.raw(sql, params); ``` #### getSkillEffectiveness() ```typescript static getSkillEffectiveness( skillId?: string ): { sql: string; params: any[] } ``` **Example:** ```typescript const { sql, params } = SkillsQueryBuilder.getSkillEffectiveness(); const analytics = await sqlite.raw(sql, params); analytics.forEach(stat => { console.log(`${stat.name}: ${stat.usage_count} uses, ${stat.avg_confidence_impact} impact`); }); ``` --- ## Performance ### SLA Targets | Metric | Target | Actual (Typical) | |--------|--------|------------------| | Cold load | <1s | ~250ms | | Warm load | <100ms | ~15ms | | Cache hit | <10ms | ~2ms | | Hash validation | <5ms | ~1ms | ### Optimization Tips **1. Preload Common Skills:** ```typescript // At application startup await loader.preloadSkills([ 'cfn-coordination', 'hook-pipeline', 'pre-edit-backup' ]); ``` **2. Increase Cache Size for Heavy Workloads:** ```typescript loader.cacheMaxSize = 200; // Default: 100 ``` **3. Use Phase Filtering:** ```typescript // Load only loop3-specific skills const result = await loader.loadContextualSkills({ agentType: 'backend-developer', phase: 'loop3', // Reduces query result set maxSkills: 10 }); ``` **4. Monitor Cache Hit Rate:** ```typescript const hitRate = result.cacheHitCount / (result.cacheHitCount + result.cacheMissCount); if (hitRate < 0.7) { console.warn('Low cache hit rate, consider increasing TTL'); } ``` --- ## Database Schema ### Skills Table ```sql CREATE TABLE skills ( id TEXT PRIMARY KEY, name TEXT NOT NULL, version TEXT NOT NULL, file_path TEXT NOT NULL, content_hash TEXT NOT NULL, namespace TEXT NOT NULL DEFAULT 'cfn', status TEXT NOT NULL DEFAULT 'active', priority INTEGER NOT NULL DEFAULT 5, tags TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_skills_status ON skills(status); CREATE INDEX idx_skills_namespace ON skills(namespace); CREATE INDEX idx_skills_priority ON skills(priority); ``` ### Agent-Skill Mappings ```sql CREATE TABLE agent_skill_mappings ( agent_type TEXT NOT NULL, skill_id TEXT NOT NULL, is_required BOOLEAN NOT NULL DEFAULT 0, priority INTEGER NOT NULL DEFAULT 5, phase TEXT, context_keywords TEXT, PRIMARY KEY (agent_type, skill_id), FOREIGN KEY (skill_id) REFERENCES skills(id) ); CREATE INDEX idx_asm_agent_type ON agent_skill_mappings(agent_type); CREATE INDEX idx_asm_phase ON agent_skill_mappings(phase); ``` ### Usage Analytics ```sql CREATE TABLE skill_usage_log ( id TEXT PRIMARY KEY, skill_id TEXT NOT NULL, agent_id TEXT NOT NULL, agent_type TEXT NOT NULL, loaded_at TEXT NOT NULL, execution_time_ms INTEGER NOT NULL, confidence_impact REAL, FOREIGN KEY (skill_id) REFERENCES skills(id) ); CREATE INDEX idx_sul_skill_id ON skill_usage_log(skill_id); CREATE INDEX idx_sul_loaded_at ON skill_usage_log(loaded_at); ``` --- ## Usage Examples ### Example 1: Basic Skill Loading ```typescript import { SkillLoader } from './src/cli/skill-loader'; import { DatabaseService } from './src/lib/database-service'; const dbService = new DatabaseService({ sqlite: { type: 'sqlite', database: './cfn-loop.db' } }); await dbService.connect(); const loader = new SkillLoader(dbService); const result = await loader.loadContextualSkills({ agentType: 'backend-developer', includeBootstrap: true }); console.log(`Loaded ${result.totalSkills} skills`); ``` ### Example 2: Contextual Filtering ```typescript const result = await loader.loadContextualSkills({ agentType: 'backend-developer', taskContext: ['authentication', 'jwt', 'security'], phase: 'loop3', maxSkills: 15 }); result.skills.forEach(skill => { console.log(`${skill.name} (priority: ${skill.priority})`); }); ``` ### Example 3: Hash Validation ```typescript import { SkillCacheValidator } from './src/cli/skill-cache-validator'; const validator = new SkillCacheValidator(); for (const skill of result.skills) { const validation = validator.validateContent( skill.content, skill.contentHash ); if (!validation.isValid) { throw new Error(`Security violation: ${validation.reason}`); } } ``` ### Example 4: Performance Monitoring ```typescript const startTime = Date.now(); const result = await loader.loadContextualSkills({ agentType: 'backend-developer', taskContext: ['api', 'rest'], maxSkills: 20 }); const duration = Date.now() - startTime; console.log({ loadTimeMs: result.loadTimeMs, totalDuration: duration, cacheHitRate: (result.cacheHitCount / (result.cacheHitCount + result.cacheMissCount) * 100).toFixed(2) + '%', metSLA: result.loadTimeMs < 1000 }); ``` ### Example 5: CLI Usage ```bash # Load skills for backend developer ./.claude/skills/cfn-skill-loader/execute.sh \ --agent-type backend-developer \ --task-context "authentication,api,security" \ --max-skills 20 \ --phase loop3 # Run performance benchmark ./.claude/skills/cfn-skill-loader/execute.sh \ --benchmark \ --iterations 20 # Preload common skills ./.claude/skills/cfn-skill-loader/execute.sh \ --preload "cfn-coordination,hook-pipeline,pre-edit-backup" ``` --- ## Error Handling ### Common Errors **1. Database Connection Failed** ```typescript try { await dbService.connect(); } catch (error) { // Fallback to bootstrap skills only const loader = new SkillLoader(undefined, logger); const result = await loader.loadContextualSkills({ agentType: 'backend-developer', includeBootstrap: true }); } ``` **2. Skill File Not Found** ```typescript // Logged as error, but loading continues // Check logs for: "Skill file not found" ``` **3. Hash Validation Failure** ```typescript // Logged as warning: "Skill content hash mismatch" // Skill is still loaded but integrity is questionable // Consider updating database hash or file content ``` **4. Cache Corruption** ```typescript const validator = new SkillCacheValidator(); const corruptedIds = validator.verifyBatchIntegrity(cacheEntries); if (corruptedIds.length > 0) { loader.clearCache(); // Force reload } ``` --- ## Testing ### Run Test Suite ```bash # Full test suite npm test -- tests/skill-loader.test.ts # Coverage report npm test -- tests/skill-loader.test.ts --coverage # Watch mode npm test -- tests/skill-loader.test.ts --watch ``` ### Test Coverage Target **≥95% coverage across all modules:** - `src/cli/skill-loader.ts` - `src/cli/skill-cache-validator.ts` - `src/db/skills-query.ts` ### Manual Testing ```bash # Test basic loading ./.claude/skills/cfn-skill-loader/execute.sh \ --agent-type backend-developer # Test with context ./.claude/skills/cfn-skill-loader/execute.sh \ --agent-type tester \ --task-context "integration,api,testing" # Test performance ./.claude/skills/cfn-skill-loader/execute.sh \ --benchmark \ --iterations 10 ``` --- ## Related Documentation - **Skill Documentation**: `.claude/skills/cfn-skill-loader/SKILL.md` - **Test Suite**: `tests/skill-loader.test.ts` - **Database Service**: `src/lib/database-service/README.md` - **Logging**: `src/lib/logging.ts` --- ## Cache Invalidation ### Overview The SkillLoader includes automatic hash-based cache invalidation to ensure cached skills remain synchronized with database content. Cache entries are validated on each `loadContextualSkills()` call and automatically removed if content hashes mismatch. ### How It Works **Bulk Hash Validation Flow:** 1. Extract all cached skill IDs 2. Query current hashes from database (single SQL query with WHERE IN) 3. Compare cached hashes vs current hashes in parallel 4. Invalidate entries with mismatches (atomic removal) **Performance Target:** <100ms for 100 skills ### API Usage **Manual Cache Validation:** ```typescript const loader = new SkillLoader(dbService); // Validate all cached entries const validation = await loader.validateCache(); if (!validation.isValid) { console.log(`Invalidated ${validation.invalidCount} skills:`, validation.invalidSkillIds); } ``` **Automatic Validation:** ```typescript // Cache validation happens automatically const result = await loader.loadContextualSkills({ agentType: 'backend-developer', }); console.log({ cacheHits: result.cacheHitCount, cacheMisses: result.cacheMissCount, invalidations: result.cacheInvalidationCount // New metric }); ``` **Bulk Hash Query (Low-Level):** ```typescript import { SkillCacheValidator } from './src/cli/skill-cache-validator'; const validator = new SkillCacheValidator(logger, dbService); // Query hashes for multiple skills (single DB query) const skillIds = ['skill-1', 'skill-2', 'skill-3']; const hashes = await validator.querySkillHashes(skillIds); console.log(hashes.get('skill-1')); // 'abc123...' ``` **Validate Cached Skills:** ```typescript const cachedEntries = [ { skillId: 'skill-1', content: '...', contentHash: 'old-hash', cachedAt: new Date(), validUntil: new Date(Date.now() + 300000), }, ]; const validation = await validator.validateCachedSkills(cachedEntries); console.log({ isValid: validation.isValid, invalidSkillIds: validation.invalidSkillIds, durationMs: validation.durationMs, }); ``` ### Monitoring Queries **Cache Invalidation Events (Last 24 Hours):** ```typescript import { SkillsQueryBuilder } from './src/db/skills-query'; const dbService = new DatabaseService(); const sqlite = dbService.getAdapter('sqlite'); const { sql, params } = SkillsQueryBuilder.getCacheInvalidations(24); const events = await sqlite.raw(sql, params); events.forEach(event => { console.log(`${event.skill_name}: ${event.reason} (${event.invalidated_at})`); }); ``` **Cache Performance Metrics:** ```typescript const { sql, params } = SkillsQueryBuilder.getCachePerformanceMetrics(24); const metrics = await sqlite.raw(sql, params); console.log({ hitRate: metrics[0].hit_rate + '%', totalHits: metrics[0].total_hits, totalMisses: metrics[0].total_misses, totalInvalidations: metrics[0].total_invalidations, avgLoadTime: metrics[0].avg_load_time_ms + 'ms', }); ``` **Frequently Updated Skills:** ```typescript const { sql, params } = SkillsQueryBuilder.getFrequentlyUpdatedSkills(10, 168); const topSkills = await sqlite.raw(sql, params); topSkills.forEach(skill => { console.log(`${skill.skill_name}: ${skill.update_count} updates`); }); ``` **Cache Performance by Agent Type:** ```typescript const { sql, params } = SkillsQueryBuilder.getCachePerformanceByAgentType(24); const agentMetrics = await sqlite.raw(sql, params); agentMetrics.forEach(metrics => { console.log(`${metrics.agent_type}: ${metrics.hit_rate}% hit rate`); }); ``` ### Database Schema **Cache Invalidations Table:** ```sql CREATE TABLE IF NOT EXISTS cache_invalidations ( id TEXT PRIMARY KEY, skill_id TEXT NOT NULL, invalidated_at TEXT NOT NULL DEFAULT (datetime('now')), reason TEXT NOT NULL, old_hash TEXT, new_hash TEXT, FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_ci_skill_id ON cache_invalidations(skill_id); CREATE INDEX IF NOT EXISTS idx_ci_timestamp ON cache_invalidations(invalidated_at); ``` **Skill Loader Metrics Table:** ```sql CREATE TABLE IF NOT EXISTS skill_loader_metrics ( id TEXT PRIMARY KEY, agent_type TEXT NOT NULL, load_time_ms INTEGER NOT NULL, cache_hit INTEGER NOT NULL DEFAULT 0, cache_miss INTEGER NOT NULL DEFAULT 0, cache_invalidation INTEGER NOT NULL DEFAULT 0, skills_loaded INTEGER NOT NULL, timestamp TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_slm_agent_type ON skill_loader_metrics(agent_type); CREATE INDEX IF NOT EXISTS idx_slm_timestamp ON skill_loader_metrics(timestamp); ``` **Run Migration:** ```bash # Apply cache invalidation tracking migration sqlite3 ./db/skills.db < ./src/db/migrations/002-cache-invalidation-tracking.sql ``` ### Graceful Degradation **Cache invalidation failures are non-blocking:** - Database unavailable → Skip validation, continue loading - Hash query fails → Log error, preserve cache - Validation timeout → Log warning, continue loading **Example:** ```typescript const loader = new SkillLoader(dbService); // Even if cache validation fails, loading continues const result = await loader.loadContextualSkills({ agentType: 'backend-developer', }); // Will be 0 if validation failed, not an error console.log(`Invalidated: ${result.cacheInvalidationCount}`); ``` ### Troubleshooting **Issue: High cache invalidation rate** ```typescript // Query frequently updated skills const { sql, params } = SkillsQueryBuilder.getFrequentlyUpdatedSkills(10, 24); const topSkills = await sqlite.raw(sql, params); // Identify skills being modified frequently (may need longer TTL or pinning) topSkills.forEach(skill => { if (skill.update_count > 10) { console.warn(`${skill.skill_name} updated ${skill.update_count} times in 24h`); } }); ``` **Issue: Slow cache validation** ```typescript const validation = await loader.validateCache(); if (validation.durationMs > 100) { console.warn(`Cache validation slow: ${validation.durationMs}ms`); // Consider: reduce cache size, add content_hash index, optimize query } ``` **Issue: Missing invalidation events** ```sql -- Check if cache_invalidations table exists SELECT name FROM sqlite_master WHERE type='table' AND name='cache_invalidations'; -- If missing, run migration ``` **Issue: Stale cache persisting** ```typescript // Force clear cache loader.clearCache(); // Reload with fresh data const result = await loader.loadContextualSkills({ agentType: 'backend-developer', }); ``` --- ## Changelog ### v1.1.0 (2025-11-15 - Task 1.4) - **Feature**: Hash-based cache invalidation - **Feature**: Bulk hash validation (<100ms for 100 skills) - **Feature**: Atomic cache updates - **Feature**: Cache invalidation metrics tracking - **Database**: Added `cache_invalidations` table - **Database**: Added `skill_loader_metrics` table - **API**: Added `SkillLoader.validateCache()` - **API**: Added `SkillCacheValidator.querySkillHashes()` - **API**: Added `SkillCacheValidator.validateCachedSkills()` - **API**: Added `cacheInvalidationCount` to `SkillLoadResult` - **Monitoring**: 9 new monitoring queries for cache analytics - **Performance**: Bulk hash queries optimized with WHERE IN clause - **Reliability**: Graceful degradation on validation failures - **Tests**: 30+ new tests for cache invalidation (coverage ≥90%) ### v1.0.0 (2025-11-15) - Initial implementation - LRU caching with 5-minute TTL - SHA256 hash validation - Bootstrap skills support - Database query layer - Performance: <1s cold, <100ms warm - Test coverage: ≥95% --- **Maintainer:** Backend Developer **Last Updated:** 2025-11-15 (Task 1.4) **Confidence:** 0.92