['roles', 'permissions', 'admin', 'students'], 'nested relationship redaction' => ['students', 'parents', 'family', 'reports'], 'append-only audit ledger' => ['audit', 'logs', 'ledger', 'history'], 'payment allocation and settlement' => ['payments', 'invoices', 'refund', 'transaction'], 'attendance excuses and custody' => ['attendance', 'excuse', 'dismissal', 'guardian'], 'class transfers and substitutes' => ['transfer', 'classes', 'teacher', 'coverage'], 'communication suppression webhooks' => ['webhook', 'email', 'notification', 'unsubscribe'], 'external sync canonicalization' => ['sync', 'import', 'external', 'provider'], 'temporary URL file safety' => ['download', 'file', 'media', 'signed'], 'operational backup isolation' => ['backup', 'restore', 'maintenance', 'settings'], ]; foreach ($families as $label => $needles) { $this->assertNotEmpty( $this->apiRoutesContainingAny($needles), 'Batch 19 invariant family missing or renamed without updating risk register: '.$label ); } } }