TenantAtlas/apps/platform/tests/Support/TestLaneManifest.php
ahmido bf38ec1780
Some checks failed
Main Confidence / confidence (push) Failing after 3m36s
Spec 210: implement CI test matrix budget enforcement (#243)
## Summary
- add explicit Gitea workflow files for PR Fast Feedback, `dev` Confidence, Heavy Governance, and Browser lanes
- extend the repo-truth lane support seams with workflow profiles, trigger-aware budget enforcement, artifact publication contracts, CI summaries, and failure classification
- add deterministic artifact staging, new CI governance guard coverage, and Spec 210 planning/contracts/docs updates

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/CiFastFeedbackWorkflowContractTest.php tests/Feature/Guards/CiConfidenceWorkflowContractTest.php tests/Feature/Guards/CiHeavyBrowserWorkflowContractTest.php tests/Feature/Guards/CiLaneFailureClassificationContractTest.php tests/Feature/Guards/FastFeedbackLaneContractTest.php tests/Feature/Guards/ConfidenceLaneContractTest.php tests/Feature/Guards/HeavyGovernanceLaneContractTest.php tests/Feature/Guards/BrowserLaneIsolationTest.php tests/Feature/Guards/FixtureLaneImpactBudgetTest.php tests/Feature/Guards/TestLaneManifestTest.php tests/Feature/Guards/TestLaneArtifactsContractTest.php tests/Feature/Guards/TestLaneCommandContractTest.php`
- `./scripts/platform-test-lane fast-feedback`
- `./scripts/platform-test-lane confidence`
- `./scripts/platform-test-lane heavy-governance`
- `./scripts/platform-test-lane browser`
- `./scripts/platform-test-report fast-feedback`
- `./scripts/platform-test-report confidence`

## Notes
- scheduled Heavy Governance and Browser workflows stay gated behind `TENANTATLAS_ENABLE_HEAVY_GOVERNANCE_SCHEDULE=1` and `TENANTATLAS_ENABLE_BROWSER_SCHEDULE=1`
- the remaining rollout evidence task is capturing the live Gitea run set this PR enables: PR Fast Feedback, `dev` Confidence, manual and scheduled Heavy Governance, and manual and scheduled Browser runs

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #243
2026-04-17 18:04:35 +00:00

3202 lines
142 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support;
use InvalidArgumentException;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use Symfony\Component\Process\Process;
final class TestLaneManifest
{
private const ARTIFACT_DIRECTORY = 'storage/logs/test-lanes';
private const MAINLINE_BRANCH = 'dev';
private const CI_RUNNER_LABEL = 'ubuntu-latest';
private const FULL_SUITE_BASELINE_SECONDS = 2625;
private const COMMAND_REFS = [
'fast-feedback' => 'test',
'confidence' => 'test:confidence',
'browser' => 'test:browser',
'heavy-governance' => 'test:heavy',
'profiling' => 'test:profile',
'junit' => 'test:junit',
];
private const COMPARISON_BASELINES = [
'shared-test-fixture-slimming' => [
'fast-feedback' => [
'laneId' => 'fast-feedback',
'finishedAt' => '2026-04-16T13:11:57+00:00',
'wallClockSeconds' => 176.73623,
'budgetThresholdSeconds' => 200,
'targetImprovementPercent' => 10,
'maxRegressionPercent' => 5,
],
'confidence' => [
'laneId' => 'confidence',
'finishedAt' => '2026-04-16T13:11:57+00:00',
'wallClockSeconds' => 394.383441,
'budgetThresholdSeconds' => 450,
'targetImprovementPercent' => 10,
'maxRegressionPercent' => 5,
],
],
];
/**
* @return array<string, mixed>
*/
public static function manifest(): array
{
return [
'version' => 2,
'artifactDirectory' => self::artifactDirectory(),
'mainlineBranch' => self::mainlineBranch(),
'classifications' => self::classifications(),
'families' => self::families(),
'mixedFileResolutions' => self::mixedFileResolutions(),
'placementRules' => self::placementRules(),
'driftGuards' => self::driftGuards(),
'budgetTargets' => self::budgetTargets(),
'lanes' => self::lanes(),
'workflowProfiles' => self::workflowProfiles(),
'laneBindings' => self::laneBindings(),
'budgetEnforcementProfiles' => TestLaneBudget::enforcementProfiles(),
'artifactPublicationContracts' => self::artifactPublicationContracts(),
'failureClasses' => self::failureClasses(),
'familyBudgets' => self::familyBudgets(),
'heavyGovernanceBudgetContract' => self::heavyGovernanceBudgetContract(),
'heavyGovernanceHotspotInventory' => self::heavyGovernanceHotspotInventory(),
'heavyGovernanceDecompositionRecords' => self::heavyGovernanceDecompositionRecords(),
'heavyGovernanceSlimmingDecisions' => self::heavyGovernanceSlimmingDecisions(),
'heavyGovernanceBudgetSnapshots' => self::heavyGovernanceBudgetSnapshots(),
'heavyGovernanceBudgetOutcome' => self::heavyGovernanceBudgetOutcome(),
'heavyGovernanceAuthorGuidance' => self::heavyGovernanceAuthorGuidance(),
];
}
/**
* @return list<array<string, mixed>>
*/
public static function classifications(): array
{
return [
[
'classificationId' => 'ui-light',
'purpose' => 'Localized tenant or admin UI checks that keep narrow operator trust without broad discovery fan-out.',
'dominantCostDrivers' => ['single-mount', 'localized assertions'],
'defaultLaneId' => 'confidence',
'allowedLaneIds' => ['fast-feedback', 'confidence'],
'forbiddenLaneIds' => ['browser'],
'reviewerSignals' => [
'single page or table surface',
'no broad discovery or reflection pass',
'bounded assertion count',
],
'escalationTriggers' => [
'multi-surface workflow fan-out',
'relation-manager breadth',
'resource or action-surface discovery',
],
],
[
'classificationId' => 'ui-workflow',
'purpose' => 'Bounded operator workflows that still provide meaningful product trust but fan out across more than one surface or step.',
'dominantCostDrivers' => ['multi-mount workflow', 'fixture fan-out'],
'defaultLaneId' => 'confidence',
'allowedLaneIds' => ['confidence', 'heavy-governance'],
'forbiddenLaneIds' => ['fast-feedback', 'browser'],
'reviewerSignals' => [
'multi-step wizard or matrix flow',
'shared fixture graphs that stay inside one product workflow',
'direct operator trust value even when setup is dense',
],
'escalationTriggers' => [
'governance-wide discovery',
'repeated broad relation-manager scans',
'surface discipline across multiple unrelated resources',
],
],
[
'classificationId' => 'surface-guard',
'purpose' => 'Broad governance checks that validate action surfaces, relation-manager breadth, or navigation discipline across multiple components.',
'dominantCostDrivers' => ['surface-wide validation', 'relation-manager breadth', 'assertion density'],
'defaultLaneId' => 'heavy-governance',
'allowedLaneIds' => ['heavy-governance'],
'forbiddenLaneIds' => ['fast-feedback', 'confidence', 'browser'],
'reviewerSignals' => [
'governance contract or discipline test',
'multiple actions or surfaces validated together',
'broad relation-manager or navigation coverage',
],
'escalationTriggers' => [
'action inventory discovery',
'header or navigation discipline spanning many resources',
'matrix or relation-manager breadth beyond one local workflow',
],
],
[
'classificationId' => 'discovery-heavy',
'purpose' => 'Discovery, reflection, or remembered-context scans that widen cost as the Filament surface grows.',
'dominantCostDrivers' => ['resource discovery', 'reflection', 'broad search or registry scans'],
'defaultLaneId' => 'heavy-governance',
'allowedLaneIds' => ['heavy-governance'],
'forbiddenLaneIds' => ['fast-feedback', 'confidence', 'browser'],
'reviewerSignals' => [
'reflection-backed discovery',
'resource or global-search parity scans',
'remembered tenant or registry breadth dominating runtime',
],
'escalationTriggers' => [
'new resources or pages increase the touched surface',
'broad remembered-context validation',
'registry or relationship discovery beyond one feature',
],
],
[
'classificationId' => 'browser',
'purpose' => 'Real browser smoke and workflow coverage that remains fully isolated from non-browser lanes.',
'dominantCostDrivers' => ['real-browser interaction', 'end-to-end rendering'],
'defaultLaneId' => 'browser',
'allowedLaneIds' => ['browser'],
'forbiddenLaneIds' => ['fast-feedback', 'confidence', 'heavy-governance', 'profiling', 'junit'],
'reviewerSignals' => [
'visit() or browser assertions',
'real browser session state',
'JavaScript or DOM continuity checks',
],
'escalationTriggers' => [
'browser runtime or DOM interactivity is required',
'cross-page journey depends on real browser semantics',
'client-side behavior is part of the contract',
],
],
];
}
/**
* @return array<string, mixed>
*/
public static function classification(string $classificationId): array
{
foreach (self::classifications() as $classification) {
if ($classification['classificationId'] === $classificationId) {
return $classification;
}
}
throw new InvalidArgumentException(sprintf('Unknown heavy test classification [%s].', $classificationId));
}
/**
* @return list<array<string, mixed>>
*/
public static function families(): array
{
return [
[
'familyId' => 'backup-set-admin-tenant-parity',
'classificationId' => 'ui-light',
'purpose' => 'Keep remembered admin tenant scoping honest on a localized backup-set listing surface.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'confidence',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-light',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Document the retained localized UI confidence anchor.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/BackupSetAdminTenantParityTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical ui-light confidence example for Spec 208.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/BackupSetAdminTenantParityTest.php',
],
'costSignals' => ['single-mount', 'remembered tenant state', 'localized table assertions'],
'confidenceRationale' => 'This remains in Confidence because it protects a narrow remembered-tenant table surface without broad discovery cost.',
'validationStatus' => 'guarded',
],
[
'familyId' => 'baseline-compare-matrix-workflow',
'classificationId' => 'ui-workflow',
'purpose' => 'Validate the bounded baseline compare matrix workflow, including visible-assignment fan-out and dense matrix rendering.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'confidence',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the matrix workflow explicit as retained confidence coverage.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded matrix workflow action hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded matrix workflow builder hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php',
'tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php',
'tests/Feature/Concerns/BuildsBaselineCompareMatrixFixtures.php',
],
'costSignals' => ['workflow fan-out', 'dense fixture graph', 'multi-tenant matrix rendering'],
'confidenceRationale' => 'The compare matrix remains a primary operator workflow and stays bounded to one feature surface even when fixtures are dense.',
'validationStatus' => 'guarded',
],
[
'familyId' => 'onboarding-wizard-enforcement',
'classificationId' => 'ui-workflow',
'purpose' => 'Preserve the managed tenant onboarding wizard capability flow as an intentional retained workflow check.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'confidence',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Retain bounded wizard trust in Confidence with explicit classification.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Rbac/OnboardingWizardUiEnforcementTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded onboarding workflow hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Rbac/OnboardingWizardUiEnforcementTest.php',
],
'costSignals' => ['multi-step wizard flow', 'repeated Livewire mounts', 'workspace capability transitions'],
'confidenceRationale' => 'The onboarding wizard is a high-value operator path that should remain in Confidence despite its bounded workflow cost.',
'validationStatus' => 'guarded',
],
[
'familyId' => 'finding-bulk-actions-workflow',
'classificationId' => 'ui-workflow',
'purpose' => 'Escalate the large finding bulk-action workflow once multi-action fan-out and audit density stop fitting the standard contributor loops.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the bulk finding workflow explicitly cataloged as workflow-heavy even after it leaves the standard lanes.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Findings/FindingBulkActionsTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical high-fan-out finding bulk workflow hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Findings/FindingBulkActionsTest.php',
],
'costSignals' => ['bulk table actions', 'multi-action workflow fan-out', 'high-volume audit assertions'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'drift-bulk-triage-all-matching',
'classificationId' => 'ui-workflow',
'purpose' => 'Escalate the all-matching drift triage confirmation flow once it operates over a large result set and broad action fan-out.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the all-matching drift triage confirmation flow classified as workflow-heavy while routing it intentionally to Heavy Governance.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical all-matching drift triage hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php',
],
'costSignals' => ['all-matching bulk action', 'typed confirmation flow', 'large result-set workflow'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'baseline-profile-start-surfaces',
'classificationId' => 'ui-workflow',
'purpose' => 'Escalate the baseline profile capture and compare start surfaces once repeated header-action gating and queued-operation setup dominate runtime.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the baseline profile start surfaces classified as workflow-heavy while routing them intentionally to Heavy Governance.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical baseline compare start-surface hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical baseline capture start-surface hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical baseline action authorization hotspot tied to the same start-surface family.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
],
'costSignals' => ['repeated Livewire page mounts', 'header action gating matrix', 'queued-operation launch workflow'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'workspace-settings-slice-management',
'classificationId' => 'ui-workflow',
'purpose' => 'Escalate the multi-slice workspace settings management surface once one test covers a broad save, reset, and resolver verification workflow across several settings domains.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the workspace settings management surface classified as a broad workflow-heavy check while routing it out of Confidence.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical workspace settings multi-slice hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php',
],
'costSignals' => ['multi-slice form workflow', 'save-and-reset path coverage', 'post-write resolver verification fan-out'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'findings-workflow-surfaces',
'classificationId' => 'ui-workflow',
'purpose' => 'Escalate the remaining row, view, filter, and renewal findings workflow surfaces once their multi-step state transitions and evidence handling dominate confidence runtime.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'ui-workflow',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep the findings workflow surface explicitly cataloged as workflow-heavy while routing it out of Confidence.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical row-action workflow hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical view-header workflow hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Findings/FindingsListFiltersTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical findings filter workflow hotspot.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Findings/FindingExceptionRenewalTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical exception renewal workflow hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'tests/Feature/Findings/FindingsListFiltersTest.php',
'tests/Feature/Findings/FindingExceptionRenewalTest.php',
],
'costSignals' => ['multi-step workflow transitions', 'filter-state persistence', 'evidence and audit verification'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'workspace-only-admin-surface-independence',
'classificationId' => 'surface-guard',
'purpose' => 'Escalate cross-resource workspace-only admin surface independence checks because they validate broad remembered-tenant behavior across several Filament resources together.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Treat workspace-only cross-resource independence as a broad surface guard.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/WorkspaceOnlySurfaceTenantIndependenceTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical workspace-only admin surface independence hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/WorkspaceOnlySurfaceTenantIndependenceTest.php',
],
'costSignals' => ['cross-resource surface assertions', 'remembered tenant context variance', 'workspace-only admin invariants'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'action-surface-contract',
'classificationId' => 'surface-guard',
'purpose' => 'Guard broad action-surface discovery and declaration discipline across pages, resources, and relation managers.',
'currentLaneId' => 'heavy-governance',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Flag broad governance validation separately from retained workflows.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Guards/ActionSurfaceContractTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Canonical seeded surface-guard hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Guards/ActionSurfaceContractTest.php',
],
'costSignals' => ['resource discovery', 'surface-wide validation', 'broad assertion density'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'policy-resource-admin-search-parity',
'classificationId' => 'discovery-heavy',
'purpose' => 'Verify remembered canonical admin tenant state never leaks policy global-search discovery.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'discovery-heavy',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Explicitly classify broad admin search parity as discovery-heavy.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded policy resource discovery hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php',
],
'costSignals' => ['reflection', 'global-search discovery', 'remembered admin tenant context'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'policy-version-admin-search-parity',
'classificationId' => 'discovery-heavy',
'purpose' => 'Keep policy-version admin search parity classified as discovery-heavy when remembered context broadens the surface.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'discovery-heavy',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Treat policy-version parity as the same discovery-heavy family class as resource parity.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded policy-version discovery hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php',
],
'costSignals' => ['reflection', 'global-search discovery', 'remembered admin tenant context'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'backup-items-relation-manager-enforcement',
'classificationId' => 'surface-guard',
'purpose' => 'Protect broad relation-manager authorization affordances for backup item actions from drifting into lighter lanes.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Relation-manager breadth belongs to the surface-guard class.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded backup items relation-manager hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php',
],
'costSignals' => ['relation-manager breadth', 'table action matrix', 'tenant capability transitions'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'workspace-memberships-relation-manager-enforcement',
'classificationId' => 'surface-guard',
'purpose' => 'Keep workspace membership relation-manager action discipline classified as broad surface governance.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Workspace membership relation-manager breadth is a deliberate surface guard.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Rbac/WorkspaceMembershipsRelationManagerUiEnforcementTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded workspace memberships relation-manager hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Rbac/WorkspaceMembershipsRelationManagerUiEnforcementTest.php',
],
'costSignals' => ['relation-manager breadth', 'action visibility matrix', 'workspace membership transitions'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'tenant-review-header-discipline',
'classificationId' => 'surface-guard',
'purpose' => 'Enforce primary and grouped header action discipline for tenant review pages.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Header action discipline spans a broad action surface.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/TenantReviewHeaderDisciplineTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded header discipline hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/TenantReviewHeaderDisciplineTest.php',
],
'costSignals' => ['header action discipline', 'grouped action inventory', 'multi-state surface assertions'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'panel-navigation-segregation',
'classificationId' => 'surface-guard',
'purpose' => 'Keep panel navigation segregation and tenant-sensitive registration checks in the heavy governance lane.',
'currentLaneId' => 'confidence',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Navigation discipline is a broad surface-guard concern.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Feature/Filament/PanelNavigationSegregationTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded navigation discipline hotspot.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/PanelNavigationSegregationTest.php',
],
'costSignals' => ['navigation registration breadth', 'panel boot', 'tenant-sensitive surface assertions'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'ops-ux-governance',
'classificationId' => 'surface-guard',
'purpose' => 'Keep established Ops UX, alert header, and credential leak governance checks under the heavy-governance umbrella.',
'currentLaneId' => 'heavy-governance',
'targetLaneId' => 'heavy-governance',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'surface-guard',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Treat the established Ops UX batch as broad governance coverage.',
],
[
'selectorType' => 'path',
'selectorValue' => 'tests/Feature/OpsUx',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Existing broad Ops UX directory remains heavy-governance owned.',
],
],
'hotspotFiles' => [
'tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php',
'tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php',
'tests/Feature/ProviderConnections/CredentialLeakGuardTest.php',
],
'costSignals' => ['ops-ux governance breadth', 'cross-surface workflow coverage', 'alert and credential guard fan-out'],
'validationStatus' => 'guarded',
],
[
'familyId' => 'browser-smoke',
'classificationId' => 'browser',
'purpose' => 'Isolate browser smoke and real DOM continuity checks in the dedicated browser lane.',
'currentLaneId' => 'browser',
'targetLaneId' => 'browser',
'selectors' => [
[
'selectorType' => 'group',
'selectorValue' => 'browser',
'selectorRole' => 'include',
'sourceOfTruth' => 'pest-group',
'rationale' => 'Keep browser ownership explicit at the group layer.',
],
[
'selectorType' => 'path',
'selectorValue' => 'tests/Browser',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Attribute the browser lane to its dedicated suite path.',
],
[
'selectorType' => 'file',
'selectorValue' => 'tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php',
'selectorRole' => 'inventory-only',
'sourceOfTruth' => 'manifest',
'rationale' => 'Seeded browser hotspot for Spec 208.',
],
],
'hotspotFiles' => [
'tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php',
],
'costSignals' => ['real-browser interaction', 'dense DOM continuity', 'end-to-end smoke'],
'validationStatus' => 'guarded',
],
];
}
/**
* @return array<string, mixed>
*/
public static function family(string $familyId): array
{
foreach (self::families() as $family) {
if ($family['familyId'] === $familyId) {
return $family;
}
}
throw new InvalidArgumentException(sprintf('Unknown heavy test family [%s].', $familyId));
}
/**
* @return list<array<string, mixed>>
*/
public static function familiesByTargetLane(string $laneId): array
{
return array_values(array_filter(
self::families(),
static fn (array $family): bool => $family['targetLaneId'] === $laneId,
));
}
/**
* @return list<array<string, mixed>>
*/
public static function mixedFileResolutions(): array
{
return [
[
'filePath' => 'tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php',
'primaryClassificationId' => 'discovery-heavy',
'secondaryClassificationIds' => ['ui-light'],
'resolutionStrategy' => 'broadest-cost-wins',
'rationale' => 'The file reads like a narrow admin check but remembered tenant search parity and reflection-backed discovery dominate the runtime shape.',
'followUpRequired' => true,
],
[
'filePath' => 'tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php',
'primaryClassificationId' => 'ui-workflow',
'secondaryClassificationIds' => ['discovery-heavy'],
'resolutionStrategy' => 'broadest-cost-wins',
'rationale' => 'The builder is dense and discovery aware, but it still primarily protects the bounded compare-matrix workflow that Confidence keeps.',
'followUpRequired' => true,
],
[
'filePath' => 'tests/Feature/Concerns/BuildsBaselineCompareMatrixFixtures.php',
'primaryClassificationId' => 'ui-workflow',
'secondaryClassificationIds' => ['discovery-heavy', 'surface-guard'],
'resolutionStrategy' => 'broadest-cost-wins',
'rationale' => 'The fixture trait amplifies the compare-matrix workflow family until a narrower extraction is justified.',
'followUpRequired' => true,
],
[
'filePath' => 'tests/Feature/Concerns/BuildsPortfolioTriageFixtures.php',
'primaryClassificationId' => 'surface-guard',
'secondaryClassificationIds' => ['ui-workflow'],
'resolutionStrategy' => 'broadest-cost-wins',
'rationale' => 'The portfolio triage fixture graph widens cross-tenant scope and should inherit the broadest governance-oriented cost signal until split.',
'followUpRequired' => true,
],
];
}
/**
* @return array<string, mixed>|null
*/
public static function mixedFileResolution(string $filePath): ?array
{
foreach (self::mixedFileResolutions() as $resolution) {
if ($resolution['filePath'] === $filePath) {
return $resolution;
}
}
return null;
}
/**
* @return list<array<string, mixed>>
*/
public static function placementRules(): array
{
return [
[
'ruleId' => 'ui-light-fast-feedback-allowed',
'classificationId' => 'ui-light',
'laneId' => 'fast-feedback',
'allowance' => 'allowed',
'reason' => 'Explicitly approved localized UI checks may remain in Fast Feedback when discovery stays bounded.',
],
[
'ruleId' => 'ui-light-confidence-required',
'classificationId' => 'ui-light',
'laneId' => 'confidence',
'allowance' => 'required',
'reason' => 'Confidence keeps localized UI trust as its lightest retained UI layer.',
],
[
'ruleId' => 'ui-workflow-confidence-required',
'classificationId' => 'ui-workflow',
'laneId' => 'confidence',
'allowance' => 'required',
'reason' => 'Selected bounded workflows remain in Confidence to preserve real operator trust.',
],
[
'ruleId' => 'ui-workflow-heavy-governance-allowed',
'classificationId' => 'ui-workflow',
'laneId' => 'heavy-governance',
'allowance' => 'allowed',
'reason' => 'Workflow families may escalate into Heavy Governance when their breadth stops being local.',
],
[
'ruleId' => 'surface-guard-heavy-governance-required',
'classificationId' => 'surface-guard',
'laneId' => 'heavy-governance',
'allowance' => 'required',
'reason' => 'Broad surface guards belong in Heavy Governance by default.',
],
[
'ruleId' => 'surface-guard-fast-feedback-forbidden',
'classificationId' => 'surface-guard',
'laneId' => 'fast-feedback',
'allowance' => 'forbidden',
'reason' => 'Fast Feedback must never carry broad surface-guard breadth.',
],
[
'ruleId' => 'surface-guard-confidence-forbidden',
'classificationId' => 'surface-guard',
'laneId' => 'confidence',
'allowance' => 'forbidden',
'reason' => 'Confidence should retain bounded workflows, not governance-wide surface discipline.',
],
[
'ruleId' => 'discovery-heavy-heavy-governance-required',
'classificationId' => 'discovery-heavy',
'laneId' => 'heavy-governance',
'allowance' => 'required',
'reason' => 'Discovery-heavy scans scale with surface breadth and therefore belong in Heavy Governance.',
],
[
'ruleId' => 'discovery-heavy-fast-feedback-forbidden',
'classificationId' => 'discovery-heavy',
'laneId' => 'fast-feedback',
'allowance' => 'forbidden',
'reason' => 'Fast Feedback must stay free of discovery-heavy scans.',
],
[
'ruleId' => 'discovery-heavy-confidence-forbidden',
'classificationId' => 'discovery-heavy',
'laneId' => 'confidence',
'allowance' => 'forbidden',
'reason' => 'Confidence keeps explicit retained UI trust, not remembered-context discovery scans.',
],
[
'ruleId' => 'browser-browser-required',
'classificationId' => 'browser',
'laneId' => 'browser',
'allowance' => 'required',
'reason' => 'Browser coverage remains isolated in the dedicated browser lane.',
],
[
'ruleId' => 'browser-fast-feedback-forbidden',
'classificationId' => 'browser',
'laneId' => 'fast-feedback',
'allowance' => 'forbidden',
'reason' => 'Browser coverage cannot run in Fast Feedback.',
],
[
'ruleId' => 'browser-confidence-forbidden',
'classificationId' => 'browser',
'laneId' => 'confidence',
'allowance' => 'forbidden',
'reason' => 'Browser coverage cannot run in Confidence.',
],
[
'ruleId' => 'browser-heavy-governance-forbidden',
'classificationId' => 'browser',
'laneId' => 'heavy-governance',
'allowance' => 'forbidden',
'reason' => 'Browser coverage does not merge into Heavy Governance.',
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function driftGuards(): array
{
return [
[
'guardId' => 'browser-lane-isolation',
'scope' => 'classification',
'assertionType' => 'browser-isolation',
'targetRefs' => ['browser'],
'owningTestPaths' => ['tests/Feature/Guards/BrowserLaneIsolationTest.php'],
'failureContract' => 'Failures must name the browser file or family that leaked into a non-browser lane.',
],
[
'guardId' => 'discovery-heavy-fast-feedback-exclusion',
'scope' => 'lane',
'assertionType' => 'forbidden-membership',
'targetRefs' => ['discovery-heavy', 'fast-feedback'],
'owningTestPaths' => [
'tests/Feature/Guards/FastFeedbackLaneContractTest.php',
'tests/Feature/Guards/FastFeedbackLaneExclusionTest.php',
],
'failureContract' => 'Failures must name the discovery-heavy family or file still present in Fast Feedback.',
],
[
'guardId' => 'surface-guard-fast-feedback-exclusion',
'scope' => 'lane',
'assertionType' => 'forbidden-membership',
'targetRefs' => ['surface-guard', 'fast-feedback'],
'owningTestPaths' => [
'tests/Feature/Guards/FastFeedbackLaneContractTest.php',
'tests/Feature/Guards/FastFeedbackLaneExclusionTest.php',
],
'failureContract' => 'Failures must identify the broad surface-guard file or family that still leaks into Fast Feedback.',
],
[
'guardId' => 'confidence-ui-workflow-whitelist',
'scope' => 'lane',
'assertionType' => 'required-membership',
'targetRefs' => ['ui-light', 'ui-workflow', 'confidence'],
'owningTestPaths' => [
'tests/Feature/Guards/ConfidenceLaneContractTest.php',
'tests/Feature/Guards/HeavyGovernanceLaneContractTest.php',
],
'failureContract' => 'Failures must name the confidence family whose classification or rationale is inconsistent with the retained confidence catalog.',
],
[
'guardId' => 'heavy-attribution-contract',
'scope' => 'report',
'assertionType' => 'required-attribution',
'targetRefs' => ['surface-guard', 'discovery-heavy', 'browser'],
'owningTestPaths' => [
'tests/Feature/Guards/TestLaneArtifactsContractTest.php',
'tests/Feature/Guards/ProfileLaneContractTest.php',
],
'failureContract' => 'Failures must name the missing classification or family attribution payload under storage/logs/test-lanes.',
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function budgetTargets(): array
{
return [
[
'budgetId' => 'lane-heavy-governance',
'targetType' => 'lane',
'targetId' => 'heavy-governance',
'thresholdSeconds' => self::recommendedHeavyGovernanceNormalizedThreshold(),
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => self::heavyGovernanceBudgetContract()['lifecycleState'],
'reviewCadence' => 'refresh when heavy family ownership changes',
],
[
'budgetId' => 'classification-ui-workflow',
'targetType' => 'classification',
'targetId' => 'ui-workflow',
'thresholdSeconds' => 150,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after the retained confidence families stabilize',
],
[
'budgetId' => 'classification-surface-guard',
'targetType' => 'classification',
'targetId' => 'surface-guard',
'thresholdSeconds' => 90,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'classification-discovery-heavy',
'targetType' => 'classification',
'targetId' => 'discovery-heavy',
'thresholdSeconds' => 60,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'classification-browser',
'targetType' => 'classification',
'targetId' => 'browser',
'thresholdSeconds' => 150,
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable browser runs',
],
[
'budgetId' => 'family-backup-set-admin-tenant-parity',
'targetType' => 'family',
'targetId' => 'backup-set-admin-tenant-parity',
'thresholdSeconds' => 20,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten if more localized admin parity families are cataloged',
],
[
'budgetId' => 'family-baseline-compare-matrix-workflow',
'targetType' => 'family',
'targetId' => 'baseline-compare-matrix-workflow',
'thresholdSeconds' => 75,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after confidence workflow coverage settles',
],
[
'budgetId' => 'family-onboarding-wizard-enforcement',
'targetType' => 'family',
'targetId' => 'onboarding-wizard-enforcement',
'thresholdSeconds' => 45,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after confidence workflow coverage settles',
],
[
'budgetId' => 'family-finding-bulk-actions-workflow',
'targetType' => 'family',
'targetId' => 'finding-bulk-actions-workflow',
'thresholdSeconds' => 90,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-drift-bulk-triage-all-matching',
'targetType' => 'family',
'targetId' => 'drift-bulk-triage-all-matching',
'thresholdSeconds' => 25,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-baseline-profile-start-surfaces',
'targetType' => 'family',
'targetId' => 'baseline-profile-start-surfaces',
'thresholdSeconds' => 140,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-workspace-settings-slice-management',
'targetType' => 'family',
'targetId' => 'workspace-settings-slice-management',
'thresholdSeconds' => 70,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-findings-workflow-surfaces',
'targetType' => 'family',
'targetId' => 'findings-workflow-surfaces',
'thresholdSeconds' => 130,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-workspace-only-admin-surface-independence',
'targetType' => 'family',
'targetId' => 'workspace-only-admin-surface-independence',
'thresholdSeconds' => 40,
'baselineSource' => 'measured-lane',
'enforcement' => 'report-only',
'lifecycleState' => 'draft',
'reviewCadence' => 'tighten after escalated heavy workflows stabilize',
],
[
'budgetId' => 'family-action-surface-contract',
'targetType' => 'family',
'targetId' => 'action-surface-contract',
'thresholdSeconds' => 35,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-policy-resource-admin-search-parity',
'targetType' => 'family',
'targetId' => 'policy-resource-admin-search-parity',
'thresholdSeconds' => 20,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-policy-version-admin-search-parity',
'targetType' => 'family',
'targetId' => 'policy-version-admin-search-parity',
'thresholdSeconds' => 20,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-backup-items-relation-manager-enforcement',
'targetType' => 'family',
'targetId' => 'backup-items-relation-manager-enforcement',
'thresholdSeconds' => 30,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-workspace-memberships-relation-manager-enforcement',
'targetType' => 'family',
'targetId' => 'workspace-memberships-relation-manager-enforcement',
'thresholdSeconds' => 25,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-tenant-review-header-discipline',
'targetType' => 'family',
'targetId' => 'tenant-review-header-discipline',
'thresholdSeconds' => 20,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-panel-navigation-segregation',
'targetType' => 'family',
'targetId' => 'panel-navigation-segregation',
'thresholdSeconds' => 20,
'baselineSource' => 'measured-post-spec-207',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable heavy-governance runs',
],
[
'budgetId' => 'family-ops-ux-governance',
'targetType' => 'family',
'targetId' => 'ops-ux-governance',
'thresholdSeconds' => 120,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
[
'budgetId' => 'family-browser-smoke',
'targetType' => 'family',
'targetId' => 'browser-smoke',
'thresholdSeconds' => 150,
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function familyBudgets(): array
{
$familyBudgets = [];
foreach (self::budgetTargets() as $budgetTarget) {
if (($budgetTarget['targetType'] ?? null) !== 'family') {
continue;
}
$family = self::family((string) $budgetTarget['targetId']);
$selectors = array_values(array_unique(array_merge(
self::budgetSelectorsForFamily($family),
self::familyHotspotTestFiles($family),
)));
$familyBudgets[] = array_merge($budgetTarget, [
'familyId' => $budgetTarget['targetId'],
'selectorType' => self::primaryBudgetSelectorType($family),
'selectors' => $selectors,
]);
}
return $familyBudgets;
}
/**
* @return array<string, mixed>
*/
public static function heavyGovernanceBudgetContract(?float $measuredSeconds = null): array
{
$measuredSeconds ??= self::heavyGovernanceCurrentMeasuredSeconds();
$normalizedThresholdSeconds = self::recommendedHeavyGovernanceNormalizedThreshold($measuredSeconds);
$decisionStatus = $normalizedThresholdSeconds > self::heavyGovernanceSummaryThresholdSeconds()
? 'recalibrated'
: 'recovered';
return [
'laneId' => 'heavy-governance',
'summaryThresholdSeconds' => self::heavyGovernanceSummaryThresholdSeconds(),
'evaluationThresholdSeconds' => self::heavyGovernanceLegacyEvaluationThresholdSeconds(),
'normalizedThresholdSeconds' => $normalizedThresholdSeconds,
'baselineSource' => 'measured-lane',
'enforcementLevel' => 'warn',
'lifecycleState' => $decisionStatus === 'recalibrated' ? 'recalibrated' : 'documented',
'reconciliationRationale' => $decisionStatus === 'recalibrated'
? sprintf(
'Spec 209 removed duplicated workflow fan-out in the baseline-profile and findings-heavy families, but the settled lane still retains intentional surface-guard depth and the workspace settings residual workflow cost; the normalized contract is %.0fs after the honest rerun.',
$normalizedThresholdSeconds,
)
: 'Spec 209 removed enough duplicate workflow work for the heavy-governance lane to recover within the pre-normalization 300s contract.',
'decisionStatus' => $decisionStatus,
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceHotspotInventory(): array
{
$measuredSecondsByFamily = self::heavyGovernanceBaselineMeasuredSecondsByFamily();
$inventoryMetadata = [
'baseline-profile-start-surfaces' => ['costDriverCategory' => 'workflow-heavy', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'action-surface-contract' => ['costDriverCategory' => 'intentionally-heavy', 'priorityTier' => 'primary', 'status' => 'retained'],
'ops-ux-governance' => ['costDriverCategory' => 'intentionally-heavy', 'priorityTier' => 'primary', 'status' => 'retained'],
'findings-workflow-surfaces' => ['costDriverCategory' => 'workflow-heavy', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'finding-bulk-actions-workflow' => ['costDriverCategory' => 'redundant', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'workspace-settings-slice-management' => ['costDriverCategory' => 'helper-driven', 'priorityTier' => 'primary', 'status' => 'follow-up'],
];
$inventory = [];
foreach ($inventoryMetadata as $familyId => $metadata) {
$family = self::family($familyId);
$budgetTarget = self::budgetTarget('family', $familyId);
$inventory[] = [
'familyId' => $familyId,
'classificationId' => $family['classificationId'],
'purpose' => $family['purpose'],
'measuredSeconds' => round((float) ($measuredSecondsByFamily[$familyId] ?? 0.0), 6),
'hotspotFiles' => $family['hotspotFiles'],
'costDriverCategory' => $metadata['costDriverCategory'],
'priorityTier' => $metadata['priorityTier'],
'currentBudgetSeconds' => isset($budgetTarget['thresholdSeconds']) ? (float) $budgetTarget['thresholdSeconds'] : null,
'status' => $metadata['status'],
];
}
usort($inventory, static fn (array $left, array $right): int => $right['measuredSeconds'] <=> $left['measuredSeconds']);
return $inventory;
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceDecompositionRecords(): array
{
return [
[
'familyId' => 'baseline-profile-start-surfaces',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'One capture start surface, one compare start surface, and one authorization matrix on the baseline profile detail page.',
'duplicateWorkSources' => ['repeated-livewire-mounts', 'header-action-gating-matrix', 'duplicate-baseline-fixture-seeding'],
'duplicateWorkEstimateSeconds' => 12.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'centralize-work',
'notes' => 'The slimming pass keeps the capture and compare start-surface trust intact while reducing repeated Livewire page mounts and repeated start-surface gating setup.',
],
[
'familyId' => 'findings-workflow-surfaces',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'Row actions, view-header actions, list-filter persistence, and renewal workflows remain separate trust slices but now share less repeated mounting work.',
'duplicateWorkSources' => ['repeated-livewire-mounts', 'filter-state-persistence', 'audit-fan-out'],
'duplicateWorkEstimateSeconds' => 6.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'centralize-work',
'notes' => 'The row and view workflow tests shed repeated page mounts while keeping state-transition, assignment, and audit guarantees explicit.',
],
[
'familyId' => 'finding-bulk-actions-workflow',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'Bulk triage, assign, resolve, and close still need per-record workflow and audit verification, but the selected-record fan-out no longer needs a triple-digit fixture count.',
'duplicateWorkSources' => ['audit-fan-out', 'duplicate bulk-action setup'],
'duplicateWorkEstimateSeconds' => 20.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'narrow-assertions',
'notes' => 'The family now validates per-record audit fan-out with a representative selected set instead of an unnecessarily large fixture batch.',
],
[
'familyId' => 'action-surface-contract',
'trustType' => 'surface-trust',
'requiredBreadth' => 'The contract still needs broad action-surface discovery across resources, pages, and relation managers.',
'duplicateWorkSources' => ['resource-discovery-pass', 'surface-wide validation'],
'residualCostSource' => 'intentional-depth',
'recommendedAction' => 'retain-as-heavy',
'notes' => 'No repeatable duplication outweighed the intentionally broad governance surface, so the family remains heavy by design.',
],
[
'familyId' => 'ops-ux-governance',
'trustType' => 'surface-trust',
'requiredBreadth' => 'Ops UX still spans multiple monitoring, notification, and run-detail surfaces with legitimate governance depth.',
'duplicateWorkSources' => ['surface-wide validation', 'cross-surface workflow coverage'],
'residualCostSource' => 'intentional-depth',
'recommendedAction' => 'retain-as-heavy',
'notes' => 'The audit found real breadth rather than a removable duplicate pass, so the family is explicitly retained as intentional heavy coverage.',
],
[
'familyId' => 'workspace-settings-slice-management',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'The settings surface still needs multi-slice save and reset verification, but its residual cost is mostly service and resolver fan-out rather than broad UI discovery.',
'duplicateWorkSources' => ['post-write resolver verification', 'multi-slice form workflow'],
'residualCostSource' => 'helper-driven',
'recommendedAction' => 'route-follow-up',
'notes' => 'Spec 209 records the residual settings cost explicitly instead of disguising it as a family-width win.',
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceSlimmingDecisions(): array
{
return [
[
'familyId' => 'baseline-profile-start-surfaces',
'decisionType' => 'centralize',
'scope' => [
'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
],
'guardPreservationPlan' => 'Keep capture and compare launch authorization, rollout gating, and invalid-scope rejection on the real profile detail page while trimming repeated mount overhead.',
'expectedDeltaSeconds' => 12.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
'scripts/platform-test-lane heavy-governance',
],
],
[
'familyId' => 'findings-workflow-surfaces',
'decisionType' => 'centralize',
'scope' => [
'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'tests/Feature/Findings/FindingsListFiltersTest.php',
'tests/Feature/Findings/FindingExceptionRenewalTest.php',
],
'guardPreservationPlan' => 'Preserve workflow-state, filter-persistence, and renewal evidence assertions while reducing repeated component bootstraps.',
'expectedDeltaSeconds' => 6.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'tests/Feature/Findings/FindingsListFiltersTest.php',
'tests/Feature/Findings/FindingExceptionRenewalTest.php',
],
],
[
'familyId' => 'finding-bulk-actions-workflow',
'decisionType' => 'trim-duplicate-work',
'scope' => ['tests/Feature/Findings/FindingBulkActionsTest.php'],
'guardPreservationPlan' => 'Keep per-record workflow transitions and audit assertions for each bulk action while cutting the oversized selected-record fan-out.',
'expectedDeltaSeconds' => 20.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Findings/FindingBulkActionsTest.php',
'scripts/platform-test-lane heavy-governance',
],
],
[
'familyId' => 'action-surface-contract',
'decisionType' => 'retain',
'guardPreservationPlan' => 'Retain the broad action-surface discovery contract until a future change proves a genuinely duplicate discovery pass.',
'owner' => 'platform-test-governance',
'validationPlan' => ['tests/Feature/Guards/ActionSurfaceContractTest.php'],
],
[
'familyId' => 'ops-ux-governance',
'decisionType' => 'retain',
'guardPreservationPlan' => 'Keep the broad Ops UX governance surface intact because the current cost comes from intentional coverage breadth, not an accidental duplicate loop.',
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/OpsUx/OperationCatalogCoverageTest.php',
'tests/Feature/OpsUx/OperateHubShellTest.php',
'tests/Feature/OpsUx/ActiveRunsTest.php',
],
],
[
'familyId' => 'workspace-settings-slice-management',
'decisionType' => 'follow-up',
'scope' => [
'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php',
'tests/Support/TestLaneManifest.php',
],
'guardPreservationPlan' => 'Keep the current multi-slice save and reset assertions intact while recording the residual helper-driven cost for later follow-up.',
'owner' => 'platform-test-governance',
'validationPlan' => ['tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php'],
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceBudgetSnapshots(): array
{
return [
self::seededHeavyGovernanceBaselineSnapshot(),
self::currentHeavyGovernanceSnapshot(),
];
}
/**
* @return array<string, mixed>
*/
public static function heavyGovernanceBudgetOutcome(): array
{
$snapshots = self::heavyGovernanceBudgetSnapshots();
$contract = self::heavyGovernanceBudgetContract();
$remainingOpenFamilies = array_values(array_map(
static fn (array $record): string => $record['familyId'],
array_filter(
self::heavyGovernanceHotspotInventory(),
static fn (array $record): bool => in_array($record['status'], ['retained', 'follow-up'], true),
),
));
$followUpDebt = array_values(array_map(
static fn (array $decision): string => $decision['familyId'],
array_filter(
self::heavyGovernanceSlimmingDecisions(),
static fn (array $decision): bool => in_array($decision['decisionType'], ['retain', 'follow-up'], true),
),
));
$justification = $contract['decisionStatus'] === 'recalibrated'
? sprintf(
'The workflow-heavy hotspot families were narrowed, but the honest lane still retains intentional surface-guard depth and the workspace settings residual helper cost, so the normalized heavy-governance threshold is %.0fs.',
$contract['normalizedThresholdSeconds'],
)
: 'The baseline-profile and findings-heavy families recovered enough duplicated workflow cost for the heavy-governance lane to fit inside the authoritative 300s threshold.';
return TestLaneBudget::buildOutcomeRecord(
contract: $contract,
baselineSnapshot: $snapshots[0],
currentSnapshot: $snapshots[1],
remainingOpenFamilies: $remainingOpenFamilies,
justification: $justification,
followUpDebt: $followUpDebt,
);
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceAuthorGuidance(): array
{
return [
[
'ruleId' => 'heavy-family-reuse-before-creation',
'whenToUse' => 'A new heavy-governance test touches a known baseline, findings, settings, or surface-guard family.',
'requiredDecision' => 'Decide whether the new test belongs in the existing family before creating a new family id.',
'antiPattern' => 'Creating a new heavy family for a scenario that only extends an existing trust boundary.',
'preferredOutcome' => 'Reuse the existing family and update its hotspot or decomposition notes when the trust type stays the same.',
],
[
'ruleId' => 'heavy-family-create-only-for-new-trust',
'whenToUse' => 'A proposed heavy test introduces a trust boundary that is not already represented in the canonical family inventory.',
'requiredDecision' => 'Explain the new trust type, hotspot files, and lane rationale before adding the family.',
'antiPattern' => 'Adding a vague catch-all family without a stable trust description and hotspot inventory entry.',
'preferredOutcome' => 'Create a new family only when the trust boundary and hotspot ownership are both new and reviewable.',
],
[
'ruleId' => 'split-discovery-workflow-surface-concerns',
'whenToUse' => 'A test mixes discovery, workflow, and surface-discipline assertions inside one heavy file.',
'requiredDecision' => 'Separate discovery-trust, workflow-trust, and surface-trust when the same setup is proving unrelated governance rules.',
'antiPattern' => 'One heavy test that silently becomes a catch-all for unrelated trust types.',
'preferredOutcome' => 'Keep each heavy family anchored to one dominant trust type, with any unavoidable secondary cost called out explicitly.',
],
[
'ruleId' => 'retain-intentional-heavy-depth-explicitly',
'whenToUse' => 'A heavy family stays expensive after duplicate work has been removed.',
'requiredDecision' => 'Record that the family is intentionally heavy and explain the remaining governance breadth.',
'antiPattern' => 'Continuing to treat intentional heavy coverage as unexplained budget drift.',
'preferredOutcome' => 'Mark the family as retained with an intentional-depth rationale and validate it with a focused guard suite.',
],
[
'ruleId' => 'record-helper-or-fixture-residuals',
'whenToUse' => 'A hotspot is dominated by helper, resolver, or fixture cost rather than broad UI trust.',
'requiredDecision' => 'Record the residual helper or fixture cause instead of pretending the family itself was fully slimmed.',
'antiPattern' => 'Claiming a family-width win when the remaining cost is actually outside the family boundary.',
'preferredOutcome' => 'Route the residual helper or fixture cost into follow-up debt while keeping the family inventory honest.',
],
];
}
/**
* @return array<string, mixed>|null
*/
public static function budgetTarget(string $targetType, string $targetId): ?array
{
foreach (self::budgetTargets() as $budgetTarget) {
if ($budgetTarget['targetType'] === $targetType && $budgetTarget['targetId'] === $targetId) {
return $budgetTarget;
}
}
return null;
}
/**
* @return array<string, mixed>
*/
public static function lane(string $id): array
{
foreach (self::lanes() as $lane) {
if ($lane['id'] === $id) {
return $lane;
}
}
throw new InvalidArgumentException(sprintf('Unknown test lane [%s].', $id));
}
/**
* @return list<array<string, mixed>>
*/
public static function lanes(): array
{
return [
[
'id' => 'fast-feedback',
'governanceClass' => 'fast',
'description' => 'Quick representative feedback for normal local edits.',
'intendedAudience' => 'Contributors working in the default authoring loop.',
'includedFamilies' => ['unit', 'core-feature-safety', 'ui-light'],
'excludedFamilies' => ['browser', 'surface-guard', 'discovery-heavy', 'heavy-governance'],
'ownershipExpectations' => 'Run on normal edits and escalate to confidence or heavy-governance when the touched surface expands.',
'defaultEntryPoint' => true,
'parallelMode' => 'required',
'selectors' => [
'includeSuites' => ['Unit'],
'includePaths' => [
'tests/Feature/Auth',
'tests/Feature/Authorization',
'tests/Feature/EntraAdminRoles',
'tests/Feature/Findings',
'tests/Feature/Guards',
'tests/Feature/Monitoring',
'tests/Feature/Navigation',
'tests/Feature/Onboarding',
'tests/Feature/RequiredPermissions',
'tests/Feature/Tenants',
'tests/Feature/Workspaces',
],
'includeGroups' => [],
'includeFiles' => [],
'excludeSuites' => ['Browser'],
'excludePaths' => [
'tests/Architecture',
'tests/Browser',
'tests/Deprecation',
'tests/Feature/078',
'tests/Feature/090',
'tests/Feature/144',
'tests/Feature/OpsUx',
],
'excludeGroups' => [],
'excludeFiles' => self::disallowedFamilyFilesForLane('fast-feedback'),
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => 200,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'baselineDeltaTargetPercent' => 50,
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'sqlite-memory',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
[
'id' => 'confidence',
'governanceClass' => 'confidence',
'description' => 'Broader pre-merge validation for non-browser feature and integration work.',
'intendedAudience' => 'Contributors and reviewers preparing a higher-confidence run before merge.',
'includedFamilies' => ['unit', 'non-browser-feature-integration', 'ui-light', 'ui-workflow'],
'excludedFamilies' => ['browser', 'surface-guard', 'discovery-heavy', 'heavy-governance'],
'ownershipExpectations' => 'Run before merge when a change touches multiple feature surfaces or shared infrastructure.',
'defaultEntryPoint' => false,
'parallelMode' => 'required',
'selectors' => [
'includeSuites' => ['Unit'],
'includePaths' => ['tests/Feature'],
'includeGroups' => [],
'includeFiles' => self::laneHotspotTestFiles('confidence'),
'excludeSuites' => ['Browser'],
'excludePaths' => [
'tests/Architecture',
'tests/Browser',
'tests/Deprecation',
'tests/Feature/078',
'tests/Feature/090',
'tests/Feature/144',
'tests/Feature/OpsUx',
],
'excludeGroups' => [],
'excludeFiles' => self::disallowedFamilyFilesForLane('confidence'),
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => 450,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'sqlite-memory',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
[
'id' => 'browser',
'governanceClass' => 'heavy',
'description' => 'Dedicated browser runtime lane for smoke and workflow validation.',
'intendedAudience' => 'Contributors validating browser-specific behavior and smoke coverage.',
'includedFamilies' => ['browser'],
'excludedFamilies' => ['fast-feedback', 'confidence', 'heavy-governance'],
'ownershipExpectations' => 'Run when a change touches browser behavior or before promoting browser-sensitive work.',
'defaultEntryPoint' => false,
'parallelMode' => 'forbidden',
'selectors' => [
'includeSuites' => ['Browser'],
'includePaths' => ['tests/Browser'],
'includeGroups' => [],
'includeFiles' => [],
'excludeSuites' => [],
'excludePaths' => [],
'excludeGroups' => [],
'excludeFiles' => [],
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => 150,
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'sqlite-memory',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
[
'id' => 'heavy-governance',
'governanceClass' => 'heavy',
'description' => 'Intentionally expensive governance scans, discovery-heavy parity checks, and high-fan-out operational validations.',
'intendedAudience' => 'Maintainers and reviewers validating the heaviest governance families on purpose.',
'includedFamilies' => ['architecture-governance', 'ops-ux', 'ui-workflow', 'surface-guard', 'discovery-heavy'],
'excludedFamilies' => ['browser', 'ui-light'],
'ownershipExpectations' => 'Run intentionally when touching governance scans, discovery-heavy parity, or broad UI contract families.',
'defaultEntryPoint' => false,
'parallelMode' => 'optional',
'selectors' => [
'includeSuites' => [],
'includePaths' => [
'tests/Architecture',
'tests/Deprecation',
'tests/Feature/078',
'tests/Feature/090',
'tests/Feature/144',
'tests/Feature/OpsUx',
],
'includeGroups' => [],
'includeFiles' => self::laneHotspotTestFiles('heavy-governance'),
'excludeSuites' => ['Browser'],
'excludePaths' => [],
'excludeGroups' => [],
'excludeFiles' => [],
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => self::recommendedHeavyGovernanceNormalizedThreshold(),
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => self::heavyGovernanceBudgetContract()['lifecycleState'],
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'mixed',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
[
'id' => 'profiling',
'governanceClass' => 'support',
'description' => 'Serial non-browser profiling support run that exposes the slowest tests and family attribution.',
'intendedAudience' => 'Maintainers investigating slow-test drift before it becomes baseline behavior.',
'includedFamilies' => ['unit', 'non-browser-feature-integration', 'ui-workflow', 'surface-guard', 'discovery-heavy'],
'excludedFamilies' => ['browser'],
'ownershipExpectations' => 'Run intentionally when refreshing baselines or explaining which heavy families own the cost.',
'defaultEntryPoint' => false,
'parallelMode' => 'forbidden',
'selectors' => [
'includeSuites' => ['Unit'],
'includePaths' => ['tests/Feature'],
'includeGroups' => [],
'includeFiles' => [],
'excludeSuites' => ['Browser'],
'excludePaths' => ['tests/Browser'],
'excludeGroups' => [],
'excludeFiles' => [],
],
'artifacts' => ['summary', 'junit-xml', 'profile-top', 'budget-report'],
'budget' => [
'thresholdSeconds' => 3000,
'baselineSource' => 'measured-current-suite',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'sqlite-memory',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
[
'id' => 'junit',
'governanceClass' => 'support',
'description' => 'Machine-readable report run for the confidence-shaped non-browser scope.',
'intendedAudience' => 'Contributors and reviewers who need durable artifacts instead of ad-hoc terminal output.',
'includedFamilies' => ['unit', 'non-browser-feature-integration', 'ui-light', 'ui-workflow'],
'excludedFamilies' => ['browser', 'surface-guard', 'discovery-heavy', 'heavy-governance'],
'ownershipExpectations' => 'Run when the latest confidence-shaped machine-readable report needs to be shared or attached to follow-up work.',
'defaultEntryPoint' => false,
'parallelMode' => 'required',
'selectors' => [
'includeSuites' => ['Unit'],
'includePaths' => ['tests/Feature'],
'includeGroups' => [],
'includeFiles' => self::laneHotspotTestFiles('confidence'),
'excludeSuites' => ['Browser'],
'excludePaths' => [
'tests/Architecture',
'tests/Browser',
'tests/Deprecation',
'tests/Feature/078',
'tests/Feature/090',
'tests/Feature/144',
'tests/Feature/OpsUx',
],
'excludeGroups' => [],
'excludeFiles' => self::disallowedFamilyFilesForLane('junit'),
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => 450,
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
'connectionMode' => 'sqlite-memory',
'resetStrategy' => 'refresh-database',
'seedsPolicy' => 'restricted',
'schemaBaselineCandidate' => false,
],
],
];
}
public static function mainlineBranch(): string
{
return self::MAINLINE_BRANCH;
}
public static function ciRunnerLabel(): string
{
return self::CI_RUNNER_LABEL;
}
/**
* @return list<array<string, mixed>>
*/
public static function workflowProfiles(): array
{
return [
[
'workflowId' => 'pr-fast-feedback',
'filePath' => '.gitea/workflows/test-pr-fast-feedback.yml',
'triggerClass' => 'pull-request',
'gitEvents' => ['pull_request'],
'branchFilters' => [],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => true,
'scheduleCron' => null,
'laneBindings' => ['fast-feedback'],
],
[
'workflowId' => 'main-confidence',
'filePath' => '.gitea/workflows/test-main-confidence.yml',
'triggerClass' => 'mainline-push',
'gitEvents' => ['push'],
'branchFilters' => [self::mainlineBranch()],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => true,
'scheduleCron' => null,
'laneBindings' => ['confidence'],
],
[
'workflowId' => 'heavy-governance-manual',
'filePath' => '.gitea/workflows/test-heavy-governance.yml',
'triggerClass' => 'manual',
'gitEvents' => ['workflow_dispatch'],
'branchFilters' => [],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => false,
'scheduleCron' => null,
'laneBindings' => ['heavy-governance'],
],
[
'workflowId' => 'heavy-governance-scheduled',
'filePath' => '.gitea/workflows/test-heavy-governance.yml',
'triggerClass' => 'scheduled',
'gitEvents' => ['schedule'],
'branchFilters' => [],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => false,
'scheduleCron' => '17 4 * * 1-5',
'laneBindings' => ['heavy-governance'],
],
[
'workflowId' => 'browser-manual',
'filePath' => '.gitea/workflows/test-browser.yml',
'triggerClass' => 'manual',
'gitEvents' => ['workflow_dispatch'],
'branchFilters' => [],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => false,
'scheduleCron' => null,
'laneBindings' => ['browser'],
],
[
'workflowId' => 'browser-scheduled',
'filePath' => '.gitea/workflows/test-browser.yml',
'triggerClass' => 'scheduled',
'gitEvents' => ['schedule'],
'branchFilters' => [],
'runnerLabel' => self::ciRunnerLabel(),
'blockingDefault' => false,
'scheduleCron' => '43 4 * * 1-5',
'laneBindings' => ['browser'],
],
];
}
/**
* @return array<string, mixed>
*/
public static function workflowProfile(string $workflowId): array
{
foreach (self::workflowProfiles() as $workflowProfile) {
if ($workflowProfile['workflowId'] === $workflowId) {
return $workflowProfile;
}
}
throw new InvalidArgumentException(sprintf('Unknown workflow profile [%s].', $workflowId));
}
/**
* @return list<array<string, mixed>>
*/
public static function workflowProfilesForLane(string $laneId): array
{
return array_values(array_filter(
self::workflowProfiles(),
static fn (array $workflowProfile): bool => in_array($laneId, $workflowProfile['laneBindings'], true),
));
}
/**
* @return list<array<string, mixed>>
*/
public static function laneBindings(): array
{
return array_map(
static function (array $lane): array {
$laneId = (string) $lane['id'];
$artifactContract = self::artifactPublicationContract($laneId);
return [
'laneId' => $laneId,
'executionWrapper' => 'scripts/platform-test-lane',
'reportWrapper' => 'scripts/platform-test-report',
'commandRef' => self::commandRef($laneId),
'governanceClass' => (string) $lane['governanceClass'],
'parallelMode' => (string) $lane['parallelMode'],
'requiredArtifacts' => $artifactContract['requiredFiles'],
'optionalArtifacts' => $artifactContract['optionalFiles'] ?? [],
'artifactExportProfile' => sprintf('%s-export', $laneId),
];
},
self::lanes(),
);
}
/**
* @return array<string, mixed>
*/
public static function laneBinding(string $laneId): array
{
foreach (self::laneBindings() as $laneBinding) {
if ($laneBinding['laneId'] === $laneId) {
return $laneBinding;
}
}
throw new InvalidArgumentException(sprintf('Unknown lane binding [%s].', $laneId));
}
/**
* @return list<array<string, mixed>>
*/
public static function artifactPublicationContracts(): array
{
return array_map(
static fn (array $lane): array => self::artifactPublicationContract((string) $lane['id']),
self::lanes(),
);
}
/**
* @return array<string, mixed>
*/
public static function artifactPublicationContract(string $laneId): array
{
self::lane($laneId);
$requiredFiles = ['summary.md', 'budget.json', 'report.json', 'junit.xml'];
$optionalFiles = $laneId === 'profiling' ? ['profile.txt'] : [];
$sourcePatterns = array_map(
static fn (string $artifactFile): string => sprintf('%s-latest.%s', $laneId, $artifactFile),
array_merge($requiredFiles, $optionalFiles),
);
return [
'contractId' => sprintf('%s-artifacts', $laneId),
'laneId' => $laneId,
'sourceDirectory' => self::artifactDirectory(),
'sourcePatterns' => $sourcePatterns,
'requiredFiles' => $requiredFiles,
'optionalFiles' => $optionalFiles,
'stagingDirectory' => sprintf('ci-artifacts/%s', $laneId),
'stagedNamePattern' => '{laneId}.{artifactFile}',
'uploadGroupName' => sprintf('%s-artifacts', $laneId),
'retentionClass' => match ($laneId) {
'fast-feedback' => 'pr-short',
'confidence', 'junit' => 'mainline-medium',
default => 'scheduled-medium',
},
];
}
/**
* @return list<array<string, mixed>>
*/
public static function failureClasses(): array
{
return [
[
'failureClassId' => 'test-failure',
'sourceStep' => 'scripts/platform-test-lane',
'blockingOn' => ['pull-request', 'mainline-push', 'scheduled', 'manual'],
'summaryLabel' => 'Test failure',
'remediationHint' => 'Inspect the lane output and fix the failing test before rerunning the workflow.',
],
[
'failureClassId' => 'wrapper-failure',
'sourceStep' => 'scripts/platform-test-lane',
'blockingOn' => ['pull-request', 'mainline-push', 'scheduled', 'manual'],
'summaryLabel' => 'Wrapper or manifest failure',
'remediationHint' => 'Verify the workflow profile, lane binding, and checked-in wrapper entry points still resolve to the intended lane.',
],
[
'failureClassId' => 'budget-breach',
'sourceStep' => 'tests/Support/TestLaneBudget.php',
'blockingOn' => ['pull-request'],
'summaryLabel' => 'Budget breach',
'remediationHint' => 'Review the measured runtime against the documented variance allowance and update the lane or spec evidence if the baseline legitimately changed.',
],
[
'failureClassId' => 'artifact-publication-failure',
'sourceStep' => 'scripts/platform-test-artifacts',
'blockingOn' => ['pull-request', 'mainline-push', 'scheduled', 'manual'],
'summaryLabel' => 'Artifact publication failure',
'remediationHint' => 'Regenerate the lane report artifacts and confirm the staging helper exported the full summary, budget, report, and JUnit bundle.',
],
[
'failureClassId' => 'infrastructure-failure',
'sourceStep' => 'gitea-runner',
'blockingOn' => ['pull-request', 'mainline-push', 'scheduled', 'manual'],
'summaryLabel' => 'Infrastructure failure',
'remediationHint' => 'Check checkout, dependency bootstrap, container startup, or runner health before rerunning the workflow.',
],
];
}
/**
* @return array<string, mixed>
*/
public static function failureClass(string $failureClassId): array
{
foreach (self::failureClasses() as $failureClass) {
if ($failureClass['failureClassId'] === $failureClassId) {
return $failureClass;
}
}
throw new InvalidArgumentException(sprintf('Unknown failure class [%s].', $failureClassId));
}
/**
* @return array<string, mixed>
*/
public static function validateWorkflowExecution(string $workflowId, string $laneId): array
{
$workflowProfile = self::workflowProfile($workflowId);
$unresolvedEntryPoints = [];
try {
self::laneBinding($laneId);
} catch (InvalidArgumentException) {
$unresolvedEntryPoints[] = 'lane-binding';
}
if (! array_key_exists($laneId, self::COMMAND_REFS)) {
$unresolvedEntryPoints[] = 'command-ref';
}
if (! is_file(self::repoRoot().'/scripts/platform-test-lane')) {
$unresolvedEntryPoints[] = 'lane-runner';
}
if (! is_file(self::repoRoot().'/scripts/platform-test-report')) {
$unresolvedEntryPoints[] = 'report-runner';
}
if (! is_file(self::repoRoot().DIRECTORY_SEPARATOR.$workflowProfile['filePath'])) {
$unresolvedEntryPoints[] = 'workflow-file';
}
try {
self::artifactPublicationContract($laneId);
} catch (InvalidArgumentException) {
$unresolvedEntryPoints[] = 'artifact-contract';
}
$workflowLaneMatched = in_array($laneId, $workflowProfile['laneBindings'], true);
$entryPointResolved = $unresolvedEntryPoints === [];
$valid = $entryPointResolved && $workflowLaneMatched;
return [
'workflowId' => $workflowId,
'expectedLaneIds' => $workflowProfile['laneBindings'],
'executedLaneId' => $laneId,
'entryPointResolved' => $entryPointResolved,
'workflowLaneMatched' => $workflowLaneMatched,
'unresolvedEntryPoints' => $unresolvedEntryPoints,
'valid' => $valid,
'primaryFailureClassId' => $valid ? null : 'wrapper-failure',
];
}
/**
* @return array<string, mixed>
*/
public static function currentCiContext(string $laneId): array
{
$workflowId = getenv('TENANTATLAS_CI_WORKFLOW_ID') ?: null;
$triggerClass = getenv('TENANTATLAS_CI_TRIGGER_CLASS') ?: null;
if (! is_string($workflowId) && ! is_string($triggerClass)) {
return [];
}
$executionValidation = null;
if (is_string($workflowId) && $workflowId !== '') {
try {
$executionValidation = self::validateWorkflowExecution($workflowId, $laneId);
$triggerClass ??= self::workflowProfile($workflowId)['triggerClass'];
} catch (InvalidArgumentException) {
$executionValidation = [
'entryPointResolved' => false,
'workflowLaneMatched' => false,
'primaryFailureClassId' => 'wrapper-failure',
'expectedLaneIds' => [],
'unresolvedEntryPoints' => ['workflow-profile'],
];
}
}
return array_filter([
'workflowId' => is_string($workflowId) && $workflowId !== '' ? $workflowId : null,
'triggerClass' => is_string($triggerClass) && $triggerClass !== '' ? $triggerClass : null,
'entryPointResolved' => $executionValidation['entryPointResolved'] ?? true,
'workflowLaneMatched' => $executionValidation['workflowLaneMatched'] ?? true,
'primaryFailureClassId' => $executionValidation['primaryFailureClassId'] ?? null,
'expectedLaneIds' => $executionValidation['expectedLaneIds'] ?? null,
'unresolvedEntryPoints' => $executionValidation['unresolvedEntryPoints'] ?? null,
], static fn (mixed $value): bool => $value !== null);
}
public static function commandRef(string $laneId): string
{
if (! array_key_exists($laneId, self::COMMAND_REFS)) {
throw new InvalidArgumentException(sprintf('Unknown lane command reference [%s].', $laneId));
}
return self::COMMAND_REFS[$laneId];
}
public static function artifactDirectory(): string
{
return self::ARTIFACT_DIRECTORY;
}
public static function fullSuiteBaselineSeconds(): int
{
return self::FULL_SUITE_BASELINE_SECONDS;
}
/**
* @return array<string, float|int|string>|null
*/
public static function comparisonBaseline(string $comparisonProfile, string $laneId): ?array
{
$profileBaselines = self::COMPARISON_BASELINES[$comparisonProfile] ?? null;
if (! is_array($profileBaselines)) {
return null;
}
$baseline = $profileBaselines[$laneId] ?? null;
return is_array($baseline) ? $baseline : null;
}
/**
* @return list<string>
*/
public static function buildCommand(string $laneId): array
{
$lane = self::lane($laneId);
$commandTargets = self::commandTargets($laneId, $lane);
$command = ['php', 'vendor/bin/pest', '--colors=never'];
$configurationPath = $commandTargets !== []
? self::writeLaneConfiguration($laneId, $commandTargets)
: null;
$commandSuites = $commandTargets === []
? self::commandSuites($lane)
: [];
if (! in_array('profile-top', $lane['artifacts'], true)) {
$command[] = '--compact';
}
if ($lane['parallelMode'] === 'required') {
$command[] = '--parallel';
}
if ($configurationPath !== null) {
$command[] = '--configuration='.$configurationPath;
$command[] = '--testsuite=Lane';
} elseif ($commandSuites !== []) {
$command[] = '--testsuite='.implode(',', $commandSuites);
}
$command[] = '--log-junit='.TestLaneReport::artifactPaths($laneId)['junit'];
if (in_array('profile-top', $lane['artifacts'], true)) {
$command[] = '--profile';
}
if ($commandTargets === []) {
foreach ($lane['selectors']['includeGroups'] as $group) {
$command[] = '--group='.$group;
}
foreach ($lane['selectors']['excludeGroups'] as $group) {
$command[] = '--exclude-group='.$group;
}
}
return $command;
}
public static function runLane(string $laneId): int
{
self::ensureArtifactDirectory();
$command = self::buildCommand($laneId);
$process = new Process($command, self::appRoot());
$process->setTimeout(null);
$capturedOutput = '';
$startedAt = microtime(true);
$process->run(function (string $type, string $buffer) use (&$capturedOutput): void {
echo $buffer;
$capturedOutput .= $buffer;
});
TestLaneReport::finalizeLane(
$laneId,
microtime(true) - $startedAt,
$capturedOutput,
exitCode: $process->getExitCode() ?? 1,
);
return $process->getExitCode() ?? 1;
}
public static function renderLatestReport(string $laneId, ?string $comparisonProfile = null): int
{
$artifactPaths = TestLaneReport::artifactPaths($laneId);
$reportPath = self::absolutePath($artifactPaths['report']);
$wallClockSeconds = 0.0;
if (is_file($reportPath)) {
$existingReport = json_decode((string) file_get_contents($reportPath), true);
$wallClockSeconds = (float) ($existingReport['wallClockSeconds'] ?? 0.0);
}
$parsed = TestLaneReport::parseJUnit(self::absolutePath($artifactPaths['junit']), $laneId);
$profileOutputPath = self::absolutePath($artifactPaths['profile']);
$report = TestLaneReport::buildReport(
laneId: $laneId,
wallClockSeconds: $wallClockSeconds,
slowestEntries: $parsed['slowestEntries'],
durationsByFile: $parsed['durationsByFile'],
comparisonProfile: $comparisonProfile,
);
TestLaneReport::writeArtifacts(
laneId: $laneId,
report: $report,
profileOutput: is_file($profileOutputPath) ? (string) file_get_contents($profileOutputPath) : null,
exitCode: 0,
);
echo json_encode($report, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR).PHP_EOL;
return 0;
}
/**
* @return list<string>
*/
public static function discoverFiles(string $laneId): array
{
$lane = self::lane($laneId);
$selectors = $lane['selectors'];
$files = [];
foreach (array_merge(self::suiteTargets($selectors['includeSuites']), $selectors['includePaths'], $selectors['includeFiles']) as $target) {
foreach (self::discoverTarget($target) as $resolvedFile) {
if (self::isExcluded($resolvedFile, $selectors, $laneId)) {
continue;
}
$files[] = $resolvedFile;
}
}
$files = array_values(array_unique($files));
sort($files);
return $files;
}
public static function absolutePath(string $relativePath): string
{
return self::appRoot().DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, ltrim($relativePath, '/'));
}
public static function laneConfigurationPath(string $laneId, ?string $artifactDirectory = null): string
{
$directory = trim($artifactDirectory ?? self::artifactDirectory(), '/');
return sprintf('%s/%s-lane.phpunit.xml', $directory, $laneId);
}
/**
* @param array<string, mixed>|null $family
* @return array<string, mixed>|null
*/
public static function familyForFile(string $filePath, ?array $family = null): ?array
{
if (is_array($family)) {
return self::familyMatchesFile($family, $filePath) ? $family : null;
}
$matchedFamilies = [];
foreach (self::families() as $candidate) {
if (! self::familyMatchesFile($candidate, $filePath)) {
continue;
}
$matchedFamilies[] = [
'family' => $candidate,
'score' => self::familyMatchScore($candidate, $filePath),
];
}
if ($matchedFamilies === []) {
return null;
}
usort($matchedFamilies, static fn (array $left, array $right): int => $right['score'] <=> $left['score']);
return $matchedFamilies[0]['family'];
}
/**
* @return array<string, mixed>
*/
public static function validateLanePlacement(
string $laneId,
?string $classificationId = null,
?string $familyId = null,
?string $filePath = null,
array $observedCostSignals = [],
): array {
$family = is_string($familyId) ? self::family($familyId) : null;
if ($family === null && is_string($filePath)) {
$family = self::familyForFile($filePath);
}
$mixedResolution = is_string($filePath) ? self::mixedFileResolution($filePath) : null;
$resolvedClassificationId = $classificationId
?? ($mixedResolution['primaryClassificationId'] ?? null)
?? ($family['classificationId'] ?? null);
if (! is_string($resolvedClassificationId) || $resolvedClassificationId === '') {
throw new InvalidArgumentException('Lane placement validation requires a classification, family, or resolvable file path.');
}
$classification = self::classification($resolvedClassificationId);
$rule = self::placementRuleFor($resolvedClassificationId, $laneId);
$allowance = $rule['allowance']
?? (in_array($laneId, $classification['allowedLaneIds'], true)
? 'allowed'
: (in_array($laneId, $classification['forbiddenLaneIds'], true)
? 'forbidden'
: ($classification['defaultLaneId'] === $laneId ? 'required' : 'discouraged')));
$reasons = [];
if (is_array($rule)) {
$reasons[] = (string) $rule['reason'];
}
if (is_array($family)) {
$reasons[] = sprintf(
'Family [%s] is cataloged as [%s] and targets lane [%s].',
$family['familyId'],
$family['classificationId'],
$family['targetLaneId'],
);
}
if (is_array($mixedResolution)) {
$reasons[] = sprintf(
'Mixed-file resolution [%s] applies [%s] via [%s].',
$mixedResolution['filePath'],
$mixedResolution['primaryClassificationId'],
$mixedResolution['resolutionStrategy'],
);
}
if ($observedCostSignals !== []) {
$reasons[] = sprintf('Observed cost signals: %s.', implode(', ', $observedCostSignals));
}
$valid = $allowance !== 'forbidden';
if (is_array($family) && ! in_array($laneId, self::allowedFamilyTargetLanesForLane($laneId), true)) {
$valid = false;
}
if (is_array($family) && ! in_array($family['targetLaneId'], self::allowedFamilyTargetLanesForLane($laneId), true)) {
$allowance = 'forbidden';
$valid = false;
$reasons[] = sprintf(
'Family [%s] targets [%s], which is outside the allowed family targets for lane [%s].',
$family['familyId'],
$family['targetLaneId'],
$laneId,
);
}
$remediationOptions = [];
if (! $valid) {
$remediationOptions[] = sprintf(
'Move the file or family into [%s].',
is_array($family) ? $family['targetLaneId'] : $classification['defaultLaneId'],
);
$remediationOptions[] = sprintf('Tag the surface with [%s] or [%s] groups as appropriate.', $resolvedClassificationId, is_array($family) ? $family['targetLaneId'] : $classification['defaultLaneId']);
}
return array_filter([
'laneId' => $laneId,
'resolvedClassificationId' => $resolvedClassificationId,
'familyId' => $family['familyId'] ?? null,
'allowance' => $allowance,
'valid' => $valid,
'reasons' => $reasons,
'remediationOptions' => $remediationOptions,
'mixedFileResolution' => $mixedResolution,
], static fn (mixed $value): bool => $value !== null && $value !== []);
}
public static function describeFilePlacement(string $filePath): string
{
$family = self::familyForFile($filePath);
if ($family === null) {
return sprintf('File [%s] is not cataloged in the Spec 208 family inventory.', $filePath);
}
$mixedResolution = self::mixedFileResolution($filePath);
$message = sprintf(
'File [%s] resolves to family [%s] in class [%s] targeting lane [%s].',
$filePath,
$family['familyId'],
$family['classificationId'],
$family['targetLaneId'],
);
if (is_array($mixedResolution)) {
$message .= sprintf(
' Mixed-file strategy [%s] keeps [%s] as the primary class.',
$mixedResolution['resolutionStrategy'],
$mixedResolution['primaryClassificationId'],
);
}
return $message;
}
private static function heavyGovernanceSummaryThresholdSeconds(): float
{
return 300.0;
}
private static function heavyGovernanceLegacyEvaluationThresholdSeconds(): float
{
return 200.0;
}
private static function recommendedHeavyGovernanceNormalizedThreshold(?float $measuredSeconds = null): float
{
$resolvedMeasuredSeconds = round($measuredSeconds ?? self::heavyGovernanceCurrentMeasuredSeconds(), 6);
if ($resolvedMeasuredSeconds <= self::heavyGovernanceSummaryThresholdSeconds()) {
return self::heavyGovernanceSummaryThresholdSeconds();
}
return round((float) (ceil($resolvedMeasuredSeconds / 5.0) * 5.0), 6);
}
private static function heavyGovernanceCurrentMeasuredSeconds(): float
{
$report = self::readJsonArtifact(self::heavyGovernanceArtifactPaths('latest')['report']);
if (is_array($report)) {
return round((float) ($report['wallClockSeconds'] ?? 0.0), 6);
}
return round((float) self::seededHeavyGovernanceBaselineSnapshot()['wallClockSeconds'], 6);
}
/**
* @return array{summary: string, budget: string, report: string}
*/
private static function heavyGovernanceArtifactPaths(string $variant): array
{
$directory = self::artifactDirectory();
$suffix = $variant === 'baseline' ? 'baseline' : 'latest';
return [
'summary' => sprintf('%s/heavy-governance-%s.summary.md', $directory, $suffix),
'budget' => sprintf('%s/heavy-governance-%s.budget.json', $directory, $suffix),
'report' => sprintf('%s/heavy-governance-%s.report.json', $directory, $suffix),
];
}
/**
* @return array<string, mixed>|null
*/
private static function readJsonArtifact(string $relativePath): ?array
{
$absolutePath = self::absolutePath($relativePath);
if (! is_file($absolutePath)) {
return null;
}
$decoded = json_decode((string) file_get_contents($absolutePath), true);
return is_array($decoded) ? $decoded : null;
}
/**
* @return array<string, float>
*/
private static function heavyGovernanceBaselineMeasuredSecondsByFamily(): array
{
$snapshot = self::seededHeavyGovernanceBaselineSnapshot();
$totals = [];
foreach ($snapshot['familyTotals'] as $entry) {
$totals[(string) $entry['familyId']] = round((float) $entry['totalWallClockSeconds'], 6);
}
return $totals;
}
/**
* @return array<string, mixed>
*/
private static function currentHeavyGovernanceSnapshot(): array
{
$fallback = self::seededHeavyGovernanceBaselineSnapshot();
$report = self::readJsonArtifact(self::heavyGovernanceArtifactPaths('latest')['report']);
$measuredSeconds = round((float) ($report['wallClockSeconds'] ?? $fallback['wallClockSeconds']), 6);
$normalizedThresholdSeconds = self::recommendedHeavyGovernanceNormalizedThreshold($measuredSeconds);
if (! is_array($report)) {
return array_merge($fallback, [
'snapshotId' => 'post-slimming',
'artifactPaths' => self::heavyGovernanceArtifactPaths('latest'),
'budgetStatus' => $measuredSeconds <= $normalizedThresholdSeconds ? 'within-budget' : 'warning',
]);
}
return [
'snapshotId' => 'post-slimming',
'capturedAt' => (string) ($report['finishedAt'] ?? gmdate('c')),
'wallClockSeconds' => $measuredSeconds,
'classificationTotals' => array_map(
static fn (array $entry): array => [
'classificationId' => (string) $entry['classificationId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$report['classificationAttribution'] ?? [],
),
'familyTotals' => array_map(
static fn (array $entry): array => [
'familyId' => (string) $entry['familyId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$report['familyAttribution'] ?? [],
),
'slowestEntries' => array_map(
static fn (array $entry): array => [
'label' => (string) $entry['label'],
'wallClockSeconds' => round((float) ($entry['wallClockSeconds'] ?? 0.0), 6),
],
$report['slowestEntries'] ?? [],
),
'artifactPaths' => self::heavyGovernanceArtifactPaths('latest'),
'budgetStatus' => $measuredSeconds <= $normalizedThresholdSeconds ? 'within-budget' : 'warning',
];
}
/**
* @return array<string, mixed>
*/
private static function seededHeavyGovernanceBaselineSnapshot(): array
{
return [
'snapshotId' => 'pre-slimming',
'capturedAt' => '2026-04-17T11:00:53+00:00',
'wallClockSeconds' => 318.296962,
'classificationTotals' => [
['classificationId' => 'ui-workflow', 'totalWallClockSeconds' => 190.606431],
['classificationId' => 'surface-guard', 'totalWallClockSeconds' => 106.845887],
['classificationId' => 'discovery-heavy', 'totalWallClockSeconds' => 0.863003],
],
'familyTotals' => [
['familyId' => 'baseline-profile-start-surfaces', 'totalWallClockSeconds' => 98.112193],
['familyId' => 'action-surface-contract', 'totalWallClockSeconds' => 40.841552],
['familyId' => 'ops-ux-governance', 'totalWallClockSeconds' => 38.794861],
['familyId' => 'findings-workflow-surfaces', 'totalWallClockSeconds' => 36.459493],
['familyId' => 'finding-bulk-actions-workflow', 'totalWallClockSeconds' => 26.491446],
['familyId' => 'workspace-settings-slice-management', 'totalWallClockSeconds' => 21.740839],
['familyId' => 'workspace-only-admin-surface-independence', 'totalWallClockSeconds' => 11.639077],
['familyId' => 'panel-navigation-segregation', 'totalWallClockSeconds' => 11.022529],
['familyId' => 'drift-bulk-triage-all-matching', 'totalWallClockSeconds' => 7.80246],
['familyId' => 'backup-items-relation-manager-enforcement', 'totalWallClockSeconds' => 2.280078],
['familyId' => 'tenant-review-header-discipline', 'totalWallClockSeconds' => 1.257656],
['familyId' => 'workspace-memberships-relation-manager-enforcement', 'totalWallClockSeconds' => 1.010134],
['familyId' => 'policy-resource-admin-search-parity', 'totalWallClockSeconds' => 0.439257],
['familyId' => 'policy-version-admin-search-parity', 'totalWallClockSeconds' => 0.423746],
],
'slowestEntries' => [
['label' => 'tests/Feature/Findings/FindingBulkActionsTest.php::it supports bulk workflow actions and audits each record', 'wallClockSeconds' => 26.491446],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it starts capture successfully for authorized workspace members', 'wallClockSeconds' => 12.373413],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it starts baseline compare successfully for authorized workspace members', 'wallClockSeconds' => 12.228384],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it does not start full-content baseline compare when rollout is disabled', 'wallClockSeconds' => 11.204111],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it does not start full-content capture when rollout is disabled', 'wallClockSeconds' => 11.086623],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it does not start baseline compare for workspace members missing tenant.sync', 'wallClockSeconds' => 10.659623],
['label' => 'tests/Feature/Filament/BaselineActionAuthorizationTest.php::it keeps baseline capture and compare actions capability-gated on the profile detail page', 'wallClockSeconds' => 10.555709],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it does not start capture for workspace members missing workspace_baselines.manage', 'wallClockSeconds' => 10.428982],
['label' => 'tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php::triage all matching requires typed confirmation when triaging more than 100 findings', 'wallClockSeconds' => 7.80246],
['label' => 'tests/Feature/Filament/WorkspaceOnlySurfaceTenantIndependenceTest.php::it keeps workspace-only admin surfaces independent from remembered tenant changes', 'wallClockSeconds' => 7.779388],
],
'artifactPaths' => self::heavyGovernanceArtifactPaths('baseline'),
'budgetStatus' => 'warning',
];
}
private static function appRoot(): string
{
return dirname(__DIR__, 2);
}
public static function repoRoot(): string
{
$configuredRoot = getenv('TENANTATLAS_REPO_ROOT');
if (is_string($configuredRoot) && $configuredRoot !== '') {
if (str_starts_with($configuredRoot, DIRECTORY_SEPARATOR)) {
return rtrim($configuredRoot, DIRECTORY_SEPARATOR);
}
$resolvedConfiguredRoot = realpath(self::appRoot().DIRECTORY_SEPARATOR.$configuredRoot);
if ($resolvedConfiguredRoot !== false) {
return $resolvedConfiguredRoot;
}
}
$resolvedDefaultRoot = realpath(self::appRoot().DIRECTORY_SEPARATOR.'../..');
if ($resolvedDefaultRoot !== false) {
return $resolvedDefaultRoot;
}
return dirname(dirname(self::appRoot()));
}
/**
* @param list<string> $targets
*/
private static function writeLaneConfiguration(string $laneId, array $targets): string
{
$relativePath = self::laneConfigurationPath($laneId);
$absolutePath = self::absolutePath($relativePath);
self::ensureDirectory(dirname($absolutePath));
$xml = new \DOMDocument('1.0', 'UTF-8');
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$xml->load(self::absolutePath('phpunit.xml'));
$phpunit = $xml->documentElement;
if (! $phpunit instanceof \DOMElement) {
throw new \RuntimeException('Unable to load phpunit.xml root element.');
}
$phpunit->setAttribute('bootstrap', self::absolutePath('vendor/autoload.php'));
foreach (iterator_to_array($phpunit->childNodes) as $childNode) {
if ($childNode instanceof \DOMElement && $childNode->tagName === 'testsuites') {
$phpunit->removeChild($childNode);
}
}
foreach (iterator_to_array($phpunit->getElementsByTagName('directory')) as $directoryNode) {
if (trim($directoryNode->textContent) === 'app') {
$directoryNode->nodeValue = self::absolutePath('app');
}
}
$testsuites = $xml->createElement('testsuites');
$testsuite = $xml->createElement('testsuite');
$testsuite->setAttribute('name', 'Lane');
foreach ($targets as $target) {
$testsuite->appendChild($xml->createElement('file', self::absolutePath($target)));
}
$testsuites->appendChild($testsuite);
$insertBefore = null;
foreach (iterator_to_array($phpunit->childNodes) as $childNode) {
if ($childNode instanceof \DOMElement && in_array($childNode->tagName, ['source', 'php'], true)) {
$insertBefore = $childNode;
break;
}
}
if ($insertBefore instanceof \DOMElement) {
$phpunit->insertBefore($testsuites, $insertBefore);
} else {
$phpunit->appendChild($testsuites);
}
$xml->save($absolutePath);
return $relativePath;
}
private static function ensureArtifactDirectory(): void
{
$directory = self::absolutePath(self::artifactDirectory());
if (is_dir($directory)) {
return;
}
mkdir($directory, 0777, true);
}
private static function ensureDirectory(string $directory): void
{
if (is_dir($directory)) {
return;
}
mkdir($directory, 0777, true);
}
/**
* @param list<string> $suites
* @return list<string>
*/
private static function suiteTargets(array $suites): array
{
$targets = [];
foreach ($suites as $suite) {
$targets = array_merge($targets, match ($suite) {
'Unit' => ['tests/Unit'],
'Feature' => ['tests/Feature'],
'Browser' => ['tests/Browser'],
'Pgsql' => ['tests/Feature/WorkspaceIsolation/WorkspaceIdForeignKeyConstraintTest.php'],
default => [],
});
}
return array_values(array_unique($targets));
}
/**
* @param array<string, mixed> $lane
* @return list<string>
*/
private static function commandTargets(string $laneId, array $lane): array
{
$targets = self::discoverFiles($laneId);
if ($targets !== []) {
return $targets;
}
return [];
}
/**
* @param array<string, mixed> $lane
* @return list<string>
*/
private static function commandSuites(array $lane): array
{
$selectors = $lane['selectors'];
$suites = [];
foreach ($selectors['includeSuites'] as $suite) {
$suites[] = $suite;
}
foreach (array_merge($selectors['includePaths'], $selectors['includeFiles']) as $target) {
$suite = self::inferSuiteFromTarget($target);
if ($suite !== null) {
$suites[] = $suite;
}
}
$suites = array_values(array_unique(array_filter($suites, static fn (mixed $suite): bool => is_string($suite) && $suite !== '')));
if ($selectors['excludeSuites'] !== []) {
$suites = array_values(array_filter(
$suites,
static fn (string $suite): bool => ! in_array($suite, $selectors['excludeSuites'], true),
));
}
$suiteOrder = [
'Unit' => 10,
'Feature' => 20,
'Browser' => 30,
'Architecture' => 40,
'Deprecation' => 50,
'Pgsql' => 60,
];
usort($suites, static function (string $left, string $right) use ($suiteOrder): int {
return ($suiteOrder[$left] ?? 999) <=> ($suiteOrder[$right] ?? 999);
});
return $suites;
}
private static function inferSuiteFromTarget(string $target): ?string
{
return match (true) {
str_starts_with($target, 'tests/Unit') => 'Unit',
str_starts_with($target, 'tests/Feature') => 'Feature',
str_starts_with($target, 'tests/Browser') => 'Browser',
str_starts_with($target, 'tests/Architecture') => 'Architecture',
str_starts_with($target, 'tests/Deprecation') => 'Deprecation',
default => null,
};
}
/**
* @return list<string>
*/
private static function discoverTarget(string $target): array
{
$absoluteTarget = self::absolutePath($target);
if (is_file($absoluteTarget)) {
return [str_replace(DIRECTORY_SEPARATOR, '/', $target)];
}
if (! is_dir($absoluteTarget)) {
return [];
}
$files = [];
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($absoluteTarget));
/** @var SplFileInfo $file */
foreach ($iterator as $file) {
if (! $file->isFile() || ! str_ends_with($file->getFilename(), 'Test.php')) {
continue;
}
$resolvedPath = str_replace(self::appRoot().DIRECTORY_SEPARATOR, '', $file->getPathname());
$files[] = str_replace(DIRECTORY_SEPARATOR, '/', $resolvedPath);
}
return $files;
}
/**
* @param array<string, mixed> $selectors
*/
private static function isExcluded(string $filePath, array $selectors, string $laneId): bool
{
if (in_array($filePath, $selectors['excludeFiles'], true)) {
return true;
}
foreach ($selectors['excludePaths'] as $excludedPath) {
if (str_starts_with($filePath, rtrim($excludedPath, '/'))) {
return true;
}
}
$family = self::familyForFile($filePath);
if ($family === null) {
return false;
}
return ! in_array($family['targetLaneId'], self::allowedFamilyTargetLanesForLane($laneId), true);
}
/**
* @return array<string, mixed>|null
*/
private static function placementRuleFor(string $classificationId, string $laneId): ?array
{
foreach (self::placementRules() as $rule) {
if ($rule['classificationId'] === $classificationId && $rule['laneId'] === $laneId) {
return $rule;
}
}
return null;
}
/**
* @return list<string>
*/
private static function allowedFamilyTargetLanesForLane(string $laneId): array
{
return match ($laneId) {
'fast-feedback' => ['fast-feedback'],
'confidence', 'junit' => ['fast-feedback', 'confidence'],
'heavy-governance' => ['heavy-governance'],
'browser' => ['browser'],
'profiling' => ['fast-feedback', 'confidence', 'heavy-governance'],
default => [$laneId],
};
}
/**
* @return list<string>
*/
private static function disallowedFamilyFilesForLane(string $laneId): array
{
$allowedTargets = self::allowedFamilyTargetLanesForLane($laneId);
$files = [];
foreach (self::families() as $family) {
if (in_array($family['targetLaneId'], $allowedTargets, true)) {
continue;
}
$files = array_merge($files, self::familyHotspotTestFiles($family));
}
$files = array_values(array_unique($files));
sort($files);
return $files;
}
/**
* @return list<string>
*/
private static function laneHotspotTestFiles(string $laneId): array
{
$files = [];
foreach (self::familiesByTargetLane($laneId) as $family) {
$files = array_merge($files, self::familyHotspotTestFiles($family));
}
$files = array_values(array_unique($files));
sort($files);
return $files;
}
/**
* @param array<string, mixed> $family
* @return list<string>
*/
private static function familyHotspotTestFiles(array $family): array
{
return array_values(array_filter(
$family['hotspotFiles'],
static fn (mixed $filePath): bool => is_string($filePath) && str_ends_with($filePath, 'Test.php'),
));
}
/**
* @param array<string, mixed> $family
*/
private static function primaryBudgetSelectorType(array $family): string
{
foreach ($family['selectors'] as $selector) {
if (($selector['selectorType'] ?? null) === 'path' && in_array($selector['selectorRole'] ?? 'include', ['include', 'inventory-only'], true)) {
return 'path';
}
}
return 'file';
}
/**
* @param array<string, mixed> $family
* @return list<string>
*/
private static function budgetSelectorsForFamily(array $family): array
{
$selectors = [];
foreach ($family['selectors'] as $selector) {
if (! in_array($selector['selectorRole'] ?? 'include', ['include', 'inventory-only'], true)) {
continue;
}
if (! in_array($selector['selectorType'] ?? 'file', ['file', 'path'], true)) {
continue;
}
$selectors[] = (string) $selector['selectorValue'];
}
return array_values(array_unique($selectors));
}
/**
* @param array<string, mixed> $family
*/
private static function familyMatchesFile(array $family, string $filePath): bool
{
if (in_array($filePath, $family['hotspotFiles'], true)) {
return true;
}
foreach ($family['selectors'] as $selector) {
if (! in_array($selector['selectorRole'] ?? 'include', ['include', 'inventory-only'], true)) {
continue;
}
$selectorType = (string) ($selector['selectorType'] ?? 'file');
$selectorValue = (string) ($selector['selectorValue'] ?? '');
if ($selectorValue === '') {
continue;
}
$matches = match ($selectorType) {
'file' => $filePath === $selectorValue,
'path' => str_starts_with($filePath, rtrim($selectorValue, '/')),
default => false,
};
if ($matches) {
return true;
}
}
return false;
}
/**
* @param array<string, mixed> $family
*/
private static function familyMatchScore(array $family, string $filePath): int
{
$score = 0;
foreach ($family['selectors'] as $selector) {
if (! in_array($selector['selectorRole'] ?? 'include', ['include', 'inventory-only'], true)) {
continue;
}
$selectorType = (string) ($selector['selectorType'] ?? 'file');
$selectorValue = (string) ($selector['selectorValue'] ?? '');
if ($selectorType === 'file' && $selectorValue === $filePath) {
$score = max($score, 2_000 + strlen($selectorValue));
}
if ($selectorType === 'path' && str_starts_with($filePath, rtrim($selectorValue, '/'))) {
$score = max($score, 1_000 + strlen($selectorValue));
}
}
if (in_array($filePath, $family['hotspotFiles'], true)) {
$score = max($score, 1_500 + strlen($filePath));
}
return $score;
}
}