# Phase 4: Validation Validate the generated team skill package for structural completeness, reference integrity, role consistency, and team-worker compatibility. ## Objective - Verify all required files exist - Validate SKILL.md role registry matches actual role files - Check role.md frontmatter format for team-worker compatibility - Verify pipeline task references match existing roles - Report validation results ## Step 4.1: Structural Validation ```javascript function validateStructure(teamConfig) { const skillDir = `.claude/skills/${teamConfig.skillName}`; const results = { errors: [], warnings: [], info: [] }; // Check SKILL.md exists if (!fileExists(`${skillDir}/SKILL.md`)) { results.errors.push('SKILL.md not found'); } // Check coordinator structure if (!fileExists(`${skillDir}/roles/coordinator/role.md`)) { results.errors.push('Coordinator role.md not found'); } for (const cmd of ['analyze', 'dispatch', 'monitor']) { if (!fileExists(`${skillDir}/roles/coordinator/commands/${cmd}.md`)) { results.errors.push(`Coordinator command ${cmd}.md not found`); } } // Check worker roles for (const role of teamConfig.roles.filter(r => r.name !== 'coordinator')) { if (!fileExists(`${skillDir}/roles/${role.name}/role.md`)) { results.errors.push(`Worker role.md not found: ${role.name}`); } if (role.hasCommands) { for (const cmd of role.commands) { if (!fileExists(`${skillDir}/roles/${role.name}/commands/${cmd}.md`)) { results.errors.push(`Worker command not found: ${role.name}/commands/${cmd}.md`); } } } } // Check specs if (!fileExists(`${skillDir}/specs/pipelines.md`)) { results.errors.push('specs/pipelines.md not found'); } return results; } ``` ## Step 4.2: SKILL.md Content Validation ```javascript function validateSkillMd(teamConfig) { const skillDir = `.claude/skills/${teamConfig.skillName}`; const results = { errors: [], warnings: [], info: [] }; const skillMd = Read(`${skillDir}/SKILL.md`); // Required sections const requiredSections = [ 'Role Registry', 'Role Router', 'Shared Constants', 'Worker Spawn Template', 'User Commands', 'Session Directory' ]; for (const section of requiredSections) { if (!skillMd.includes(section)) { results.errors.push(`SKILL.md missing section: ${section}`); } } // Verify role registry completeness for (const role of teamConfig.roles) { if (!skillMd.includes(role.path || `roles/${role.name}/role.md`)) { results.errors.push(`Role registry missing path for: ${role.name}`); } } // Verify session prefix if (!skillMd.includes(teamConfig.sessionPrefix)) { results.warnings.push(`Session prefix ${teamConfig.sessionPrefix} not found in SKILL.md`); } // Verify NO beat model content in SKILL.md const beatModelPatterns = [ 'ONE_STEP_PER_INVOCATION', 'spawn-and-stop', 'SPAWN_MODE', 'handleCallback', 'handleSpawnNext' ]; for (const pattern of beatModelPatterns) { if (skillMd.includes(pattern)) { results.errors.push(`SKILL.md contains beat model content: ${pattern} (should be in coordinator only)`); } } return results; } ``` ## Step 4.3: Role Frontmatter Validation Verify role.md files have correct YAML frontmatter for team-worker agent compatibility: ```javascript function validateRoleFrontmatter(teamConfig) { const skillDir = `.claude/skills/${teamConfig.skillName}`; const results = { errors: [], warnings: [], info: [] }; for (const role of teamConfig.roles.filter(r => r.name !== 'coordinator')) { const roleMd = Read(`${skillDir}/roles/${role.name}/role.md`); // Check frontmatter exists if (!roleMd.startsWith('---')) { results.errors.push(`${role.name}/role.md missing YAML frontmatter`); continue; } // Extract frontmatter const fmMatch = roleMd.match(/^---\n([\s\S]*?)\n---/); if (!fmMatch) { results.errors.push(`${role.name}/role.md malformed frontmatter`); continue; } const fm = fmMatch[1]; // Required fields if (!fm.includes(`role: ${role.name}`)) { results.errors.push(`${role.name}/role.md frontmatter missing 'role: ${role.name}'`); } if (!fm.includes(`prefix: ${role.prefix}`)) { results.errors.push(`${role.name}/role.md frontmatter missing 'prefix: ${role.prefix}'`); } if (!fm.includes(`inner_loop: ${role.inner_loop}`)) { results.warnings.push(`${role.name}/role.md frontmatter missing 'inner_loop: ${role.inner_loop}'`); } if (!fm.includes('message_types:')) { results.warnings.push(`${role.name}/role.md frontmatter missing 'message_types'`); } } return results; } ``` ## Step 4.4: Pipeline Consistency ```javascript function validatePipelines(teamConfig) { const skillDir = `.claude/skills/${teamConfig.skillName}`; const results = { errors: [], warnings: [], info: [] }; // Check every role referenced in pipelines exists const definedRoles = new Set(teamConfig.roles.map(r => r.name)); for (const pipeline of teamConfig.pipelines) { for (const task of pipeline.tasks) { if (!definedRoles.has(task.role)) { results.errors.push( `Pipeline '${pipeline.name}' task ${task.id} references undefined role: ${task.role}` ); } } // Check for circular dependencies const visited = new Set(); const stack = new Set(); for (const task of pipeline.tasks) { if (hasCycle(task, pipeline.tasks, visited, stack)) { results.errors.push(`Pipeline '${pipeline.name}' has circular dependency involving ${task.id}`); } } } // Verify specs/pipelines.md contains all pipelines const pipelinesMd = Read(`${skillDir}/specs/pipelines.md`); for (const pipeline of teamConfig.pipelines) { if (!pipelinesMd.includes(pipeline.name)) { results.warnings.push(`specs/pipelines.md missing pipeline: ${pipeline.name}`); } } return results; } ``` ## Step 4.5: Commands Distribution Validation ```javascript function validateCommandsDistribution(teamConfig) { const skillDir = `.claude/skills/${teamConfig.skillName}`; const results = { errors: [], warnings: [], info: [] }; for (const role of teamConfig.roles.filter(r => r.name !== 'coordinator')) { if (role.hasCommands) { // Verify commands/ directory exists and has files const cmdDir = `${skillDir}/roles/${role.name}/commands`; if (role.commands.length < 2) { results.warnings.push( `${role.name} has commands/ but only ${role.commands.length} command(s) — consider inline` ); } // Verify role.md references commands const roleMd = Read(`${skillDir}/roles/${role.name}/role.md`); for (const cmd of role.commands) { if (!roleMd.includes(`commands/${cmd}.md`)) { results.warnings.push( `${role.name}/role.md doesn't reference commands/${cmd}.md` ); } } } else { // Verify no commands/ directory exists const cmdDir = `${skillDir}/roles/${role.name}/commands`; if (directoryExists(cmdDir)) { results.warnings.push( `${role.name} is inline but has commands/ directory` ); } } } return results; } ``` ## Step 4.6: Aggregate Results and Report ```javascript function generateValidationReport(teamConfig) { const structural = validateStructure(teamConfig); const skillMd = validateSkillMd(teamConfig); const frontmatter = validateRoleFrontmatter(teamConfig); const pipelines = validatePipelines(teamConfig); const commands = validateCommandsDistribution(teamConfig); const allErrors = [ ...structural.errors, ...skillMd.errors, ...frontmatter.errors, ...pipelines.errors, ...commands.errors ]; const allWarnings = [ ...structural.warnings, ...skillMd.warnings, ...frontmatter.warnings, ...pipelines.warnings, ...commands.warnings ]; const gate = allErrors.length === 0 ? 'PASS' : allErrors.length <= 2 ? 'REVIEW' : 'FAIL'; const skillDir = `.claude/skills/${teamConfig.skillName}`; console.log(` ╔══════════════════════════════════════╗ ║ Team Skill Validation Report ║ ╠══════════════════════════════════════╣ ║ Skill: ${teamConfig.skillName.padEnd(28)}║ ║ Gate: ${gate.padEnd(28)}║ ╚══════════════════════════════════════╝ Structure: ${skillDir}/ ├── SKILL.md ✓ ├── roles/ │ ├── coordinator/ │ │ ├── role.md ✓ │ │ └── commands/ (analyze, dispatch, monitor) ${teamConfig.roles.filter(r => r.name !== 'coordinator').map(r => { const structure = r.hasCommands ? ` │ ├── ${r.name}/ (role.md + commands/)` : ` │ ├── ${r.name}/role.md`; return `${structure.padEnd(50)}✓`; }).join('\n')} ├── specs/ │ └── pipelines.md ✓ └── templates/ ${teamConfig.templates.length > 0 ? '✓' : '(empty)'} ${allErrors.length > 0 ? `Errors (${allErrors.length}):\n${allErrors.map(e => ` ✗ ${e}`).join('\n')}` : 'Errors: None ✓'} ${allWarnings.length > 0 ? `Warnings (${allWarnings.length}):\n${allWarnings.map(w => ` ⚠ ${w}`).join('\n')}` : 'Warnings: None ✓'} Usage: Skill(skill="${teamConfig.skillName}", args="") `); return { gate, errors: allErrors, warnings: allWarnings }; } ``` ## Step 4.7: Error Recovery ```javascript if (report.gate === 'FAIL') { const recovery = AskUserQuestion({ questions: [{ question: `Validation found ${report.errors.length} errors. How to proceed?`, header: "Recovery", multiSelect: false, options: [ { label: "Auto-fix", description: "Attempt automatic fixes (missing files, frontmatter)" }, { label: "Regenerate", description: "Re-run Phase 3 with fixes" }, { label: "Accept as-is", description: "Manual fix later" } ] }] }); } ``` ## Output - **Report**: Validation results with quality gate (PASS/REVIEW/FAIL) - **Completion**: Team skill package ready at `.claude/skills/${teamConfig.skillName}/` ## Completion Team Skill Designer has completed. The generated team skill is ready for use. ``` Next Steps: 1. Review SKILL.md router for correctness 2. Review each role.md for domain accuracy 3. Test: Skill(skill="${teamConfig.skillName}", args="") 4. Iterate based on execution results ```