Files
clarity-helper/scripts/validate-scenarios.ts
T
jpremill 1feea40684 feat(ui): redesign voice cockpit with clinical companion drawer, Svelte 5 migration, and Gemini 3.8 voice
- Re-architect VoiceSession into full-bleed studio cockpit with overlay slide-over companion drawer
- Eliminate width squishing defect on LiveInsightPanel and add native dark/light theme support
- Integrate authentic Google Gemini 3.8 Flash TTS with Fenrir persona and fallback audio pipeline
- Implement comprehensive Playwright E2E visual regression suite across Desktop, iPad, and iPhone (21 tests, 45 snapshots)
- Migrate core application to Svelte 5 runes (, , ) with reactive stores
- Maintain .NET 8 Azure Functions serverless endpoints and clean build hygiene
2026-09-29 23:35:05 -04:00

120 lines
3.9 KiB
TypeScript

/**
* 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();
}