isFile() || !str_ends_with($file->getFilename(), '.lab.md')) { continue; } $text = (string) file_get_contents($file->getPathname()); if (!preg_match('/^curriculum_lesson_id:\s*(.+)$/m', $text, $idMatch)) { $errors[] = "Missing curriculum_lesson_id: {$file->getPathname()}"; continue; } $id = trim($idMatch[1]); if (isset($seen[$id])) { $errors[] = "Duplicate lesson id {$id}"; } $seen[$id] = true; if (!isset($expected[$id])) { $errors[] = "Unknown lesson id {$id}"; } if (!preg_match('/^source_markdown:\s*(.+)$/m', $text, $sourceMatch) || trim($sourceMatch[1]) !== ($expected[$id] ?? null)) { $errors[] = "Wrong source binding for {$id}"; } foreach (['## 3. فكرة اللعبة التعليمية', '## 4. المشهد المتحرك', '## 7. محطات فحص الفهم', '## 11. سيناريوهات التحقق الإلزامية'] as $required) { if (!str_contains($text, $required)) { $errors[] = "Missing section '{$required}' in {$id}"; } } } foreach (array_keys($expected) as $id) { if (!isset($seen[$id])) { $errors[] = "Missing spec for {$id}"; } } if (count($seen) !== count($expected)) { $errors[] = 'Count mismatch: expected ' . count($expected) . ', found ' . count($seen); } if ($errors !== []) { fwrite(STDERR, implode("\n", $errors) . "\n"); exit(1); } fwrite(STDOUT, 'Validated ' . count($seen) . " one-to-one lesson lab specifications.\n");