['privacy', 'retention', 'archive', 'purge'], 'cash drawer and receipt integrity' => ['cash', 'payment', 'receipt', 'finance'], 'scholarship and discount eligibility' => ['scholarship', 'discount', 'voucher', 'fees'], 'gradebook locks and amendments' => ['scores', 'grade', 'report-card', 'teacher'], 'attendance device and geofence trust' => ['attendance', 'scanner', 'kiosk', 'badge'], 'transportation pickup safety' => ['transport', 'bus', 'pickup', 'guardian'], 'emergency broadcast controls' => ['emergency', 'broadcast', 'notification', 'calendar'], 'webhook key rotation' => ['webhook', 'callback', 'provider', 'sync'], 'audit export redaction' => ['audit', 'logs', 'export', 'reports'], 'analytics warehouse export' => ['analytics', 'dashboard', 'summary', 'export'], ]; foreach ($families as $label => $needles) { $this->assertNotEmpty( $this->apiRoutesContainingAny($needles), 'Batch 20 continuity/compliance family missing or renamed without updating risk register: '.$label ); } } }