jobcard_feature_assert(str_contains($schema,"('jobcards.close', 'Close jobcards')"),'Fresh schema must seed jobcards.close.');
jobcard_feature_assert(str_contains($upgrade,"('jobcards.close', 'Close jobcards')"),'Upgrade must seed jobcards.close.');
jobcard_feature_assert(str_contains($front,"if (\$to === 'closed') require_permission('jobcards.close');"),'Closing a jobcard must require jobcards.close.');
jobcard_feature_assert(str_contains($front,"name=\"q\"")&&str_contains($front,"name=\"status_filter\""),'Jobcards page must expose search and status filters.');
jobcard_feature_assert(str_contains($front,"j.status <> 'closed'"),'Default Jobcards filter must exclude closed records.');
jobcard_feature_assert(str_contains($front,"j.status = 'closed'"),'Closed filter must explicitly select closed records.');
jobcard_feature_assert(str_contains($front,'All clients')&&str_contains($front,'$reportClient'),'Reports must use a client-name dropdown.');
jobcard_feature_assert(str_contains($front,'format=xls&detail=1'),'Reports must expose detailed XLS export.');
jobcard_feature_assert(str_contains($front,"if (\$user['role_name'] === 'Technician') {")&&str_contains($front,'$reportClientsStmt->execute'),'Report client dropdown must bind parameters only on the technician prepared-query branch.');
jobcard_feature_assert(str_contains($front,'$reportClientsStmt->execute([(int)$user[\'id\']])'),'Technician report client lookup must use a positional bound parameter.');
foreach(['Technician notes / Work performed','totalReportHours']as$heading)jobcard_feature_assert(str_contains($front,$heading),"Detailed report must include {$heading}.");
jobcard_feature_assert(!str_contains($front,'name=\"technician_notes\"')&&!str_contains($front,'Hours notes'),'Jobcards must use time-entry technician notes only.');
jobcard_feature_assert(str_contains($front,'remaining_hours')&&str_contains($front,'SLA hours remaining'),'Client pages must show remaining SLA hours.');
jobcard_feature_assert(str_contains($front,'j.client_id = :client')&&str_contains($front,'te.counts_toward_sla = 1'),'Client SLA usage must aggregate active-period SLA time entries regardless of jobcard status.');
jobcard_feature_assert(str_contains($front,"['TOTAL HOURS'")&&str_contains($front,'totalReportHours'),'Detailed report downloads must include total hours.');
jobcard_feature_assert(str_contains($front,"if (\$route === 'settings')")&&str_contains($front,"role_name'] ?? '') !== 'Administrator'"),'Branding settings must be Administrator-only.');
jobcard_feature_assert(str_contains($front,'app_settings')&&str_contains($front,'move_uploaded_file'),'Branding settings must persist company name and safely upload logos.');