/** * Scenario Validation Helper Script * * Verifies that all built-in and custom CBT scenarios conform to schema rules, * category invariants, and clinical requirements. */ import { SCENARIOS, CBT_GLOSSARY } from '../constants'; import { Scenario } from '../types'; const VALID_CATEGORIES: Scenario['category'][] = ['Social Anxiety', 'Workplace', 'Relationships', 'Self-Talk']; const VALID_DIFFICULTIES: Scenario['difficulty'][] = ['Beginner', 'Intermediate', 'Advanced']; interface ValidationResult { scenarioId: string; title: string; isValid: boolean; errors: string[]; warnings: string[]; } export function validateScenario(scenario: Scenario): ValidationResult { const errors: string[] = []; const warnings: string[] = []; if (!scenario.id || typeof scenario.id !== 'string') { errors.push('Missing or invalid "id"'); } if (!scenario.title || scenario.title.trim().length === 0) { errors.push('Missing or empty "title"'); } if (!scenario.description || scenario.description.trim().length === 0) { errors.push('Missing or empty "description"'); } if (!VALID_CATEGORIES.includes(scenario.category)) { errors.push(`Invalid category: "${scenario.category}". Expected one of: ${VALID_CATEGORIES.join(', ')}`); } if (!VALID_DIFFICULTIES.includes(scenario.difficulty)) { errors.push(`Invalid difficulty: "${scenario.difficulty}". Expected one of: ${VALID_DIFFICULTIES.join(', ')}`); } if (!scenario.characterName || scenario.characterName.trim().length === 0) { errors.push('Missing or empty "characterName"'); } if (!scenario.initialMessage || scenario.initialMessage.trim().length === 0) { errors.push('Missing or empty "initialMessage"'); } if (!Array.isArray(scenario.cbtFocus) || scenario.cbtFocus.length === 0) { errors.push('cbtFocus must be a non-empty array of strings'); } else { // Check against glossary terms const glossaryTerms = CBT_GLOSSARY.map(g => g.term.toLowerCase()); scenario.cbtFocus.forEach(focus => { const match = glossaryTerms.some(term => term === focus.toLowerCase() || focus.toLowerCase().includes(term)); if (!match) { warnings.push(`CBT Focus item "${focus}" does not directly match any term in the CBT_GLOSSARY.`); } }); } if (!scenario.systemInstruction || scenario.systemInstruction.trim().length === 0) { errors.push('Missing or empty "systemInstruction"'); } else { if (!scenario.systemInstruction.includes('INTERNAL CBT LOGIC') && !scenario.systemInstruction.includes('LOGIC:')) { warnings.push('systemInstruction does not contain explicit "INTERNAL CBT LOGIC" branching rules.'); } } return { scenarioId: scenario.id, title: scenario.title, isValid: errors.length === 0, errors, warnings, }; } function run() { console.log('🔍 Validating Cognitive Clarity Scenario Catalog...\n'); let totalErrors = 0; let totalWarnings = 0; SCENARIOS.forEach(scenario => { const result = validateScenario(scenario); const statusIcon = result.isValid ? '✅' : '❌'; console.log(`${statusIcon} [${scenario.difficulty.toUpperCase()}] ${scenario.title} (${scenario.id})`); if (result.errors.length > 0) { console.log(' Errors:'); result.errors.forEach(e => console.log(` - ❌ ${e}`)); totalErrors += result.errors.length; } if (result.warnings.length > 0) { console.log(' Warnings:'); result.warnings.forEach(w => console.log(` - ⚠️ ${w}`)); totalWarnings += result.warnings.length; } }); console.log('\n========================================'); console.log(`Summary: ${SCENARIOS.length} scenarios analyzed.`); console.log(`Errors: ${totalErrors}, Warnings: ${totalWarnings}`); console.log('========================================\n'); if (totalErrors > 0) { process.exit(1); } } if (process.argv[1]?.endsWith('validate-scenarios.ts') || process.argv[1]?.endsWith('validate-scenarios.js')) { run(); }