security_regression_assert($bootstrap!==false&&$frontController!==false&&$schema!==false&&$upgrade!==false,'Security regression fixtures must be readable.');
$checks=0;
// A technician access decision must bind the requested jobcard to the session user.
security_regression_assert(
preg_match('/function can_access_jobcard\(int \$jobcardId\).*?SELECT 1 FROM jobcard_assignments.*?jobcard_id = :jobcard.*?user_id = :user/s',$bootstrap)===1,
'Jobcard access must scope by both jobcard ID and authenticated technician ID.'
);
security_regression_assert(
preg_match('/if \(!can_access_jobcard\(\$jobcardId\)\).*?Jobcard not found/s',$frontController)===1,
'Direct technician jobcard IDs must be denied when the assignment is not theirs.'
security_regression_assert(!array_key_exists('secret',$stored),'Stored credentials must not contain a plaintext secret field.');
security_regression_assert(isset($stored['secret_ciphertext'])&&is_string($stored['secret_ciphertext']),'Stored credentials must use secret_ciphertext as the canonical field.');
security_regression_assert(!str_contains(serialize($stored),$secret),'Serialized stored credentials must not contain the plaintext secret.');
security_regression_assert_same($secret,$vault->decryptCredential($stored)['secret'],'Canonical ciphertext must decrypt only with the owning vault key.');
security_regression_assert(!array_key_exists('secret',$vault->projectMetadata($stored)),'Metadata projections must exclude plaintext secrets.');
security_regression_assert_same(true,$safe['valid'],'Safe attachment metadata should be accepted.');
security_regression_assert_same(['name'=>'evidence.PNG','extension'=>'png','mime_type'=>'image/png','size_bytes'=>42,'client_visible'=>false,'client_approved'=>false],array_intersect_key($safe,array_flip(['name','extension','mime_type','size_bytes','client_visible','client_approved'])),'Attachment metadata must normalize to safe canonical fields.');
$checks++;
// Healthcheck schema coverage must include every table required by the current schema.
security_regression_assert(str_contains($frontController,"\$route === '{$route}'")||($route==='login'&&str_contains($frontController,"\$route === 'login'")),"Expected explicit {$route} route in front controller.");
}
security_regression_assert(substr_count($frontController,'verify_csrf();')>=7,'All supported POST route paths must call verify_csrf before mutation.');
security_regression_assert(str_contains($frontController,"if (\$route === 'logout')")&&str_contains($frontController,"if ((\$_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST')"),'Logout must remain POST-only and CSRF-protected.');
$checks++;
// Upgrade coverage is additive and must include each feature table introduced after
// the original foundation, plus the SLA uniqueness remediation guard.
security_regression_assert((bool)preg_match('/CREATE TABLE IF NOT EXISTS '.preg_quote($table,'/').'\\b/i',$upgrade),"Migration must cover {$table}.");
}
security_regression_assert(str_contains($upgrade,'sla_client_unique')&&str_contains($upgrade,'sla_duplicate_count'),'Migration must safely remediate duplicate SLA rows before adding the unique constraint.');
security_regression_assert(str_contains($upgrade,'INSERT IGNORE INTO permissions')&&str_contains($upgrade,'attachments.view'),'Migration must cover permissions for migrated feature modules.');