$lane['defaultEntryPoint'] === true, )); expect($manifest['version'])->toBe(3) ->and($manifest['artifactDirectory'])->toBe('storage/logs/test-lanes') ->and($manifest['mainlineBranch'])->toBe('dev') ->and($manifest)->toHaveKeys([ 'classifications', 'families', 'mixedFileResolutions', 'placementRules', 'driftGuards', 'budgetTargets', 'lanes', 'workflowProfiles', 'laneBindings', 'budgetEnforcementProfiles', 'artifactPublicationContracts', 'trendContractVersion', 'laneTrendPolicies', 'failureClasses', 'familyBudgets', 'heavyGovernanceBudgetContract', 'heavyGovernanceHotspotInventory', 'heavyGovernanceDecompositionRecords', 'heavyGovernanceSlimmingDecisions', 'heavyGovernanceBudgetSnapshots', 'heavyGovernanceBudgetOutcome', 'heavyGovernanceAuthorGuidance', ]) ->and($laneIds)->toEqualCanonicalizing([ 'fast-feedback', 'confidence', 'browser', 'heavy-governance', 'profiling', 'junit', ]) ->and($defaultLanes)->toHaveCount(1) ->and($defaultLanes[0]['id'])->toBe('fast-feedback'); }); it('publishes the CI workflow matrix, lane bindings, artifact contracts, and failure classes from repo truth', function (): void { $workflowProfiles = collect(TestLaneManifest::workflowProfiles())->keyBy('workflowId'); $laneBindings = collect(TestLaneManifest::laneBindings())->keyBy('laneId'); $artifactContracts = collect(TestLaneManifest::artifactPublicationContracts())->keyBy('laneId'); $failureClasses = collect(TestLaneManifest::failureClasses())->keyBy('failureClassId'); expect($workflowProfiles->keys()->all())->toEqualCanonicalizing([ 'pr-fast-feedback', 'main-confidence', 'heavy-governance-manual', 'heavy-governance-scheduled', 'browser-manual', 'browser-scheduled', ]) ->and($workflowProfiles->get('pr-fast-feedback')['laneBindings'])->toBe(['fast-feedback']) ->and($workflowProfiles->get('main-confidence')['branchFilters'])->toBe(['dev']) ->and($workflowProfiles->get('heavy-governance-scheduled')['scheduleCron'])->toBe('17 4 * * 1-5') ->and($workflowProfiles->get('browser-scheduled')['scheduleCron'])->toBe('43 4 * * 1-5') ->and($laneBindings->get('fast-feedback')['executionWrapper'])->toBe('scripts/platform-test-lane') ->and($laneBindings->get('confidence')['requiredArtifacts'])->toEqualCanonicalizing(['summary.md', 'budget.json', 'report.json', 'junit.xml', 'trend-history.json']) ->and($artifactContracts->get('fast-feedback')['retentionClass'])->toBe('pr-short') ->and($artifactContracts->get('fast-feedback')['requiredFiles'])->toContain('trend-history.json') ->and($artifactContracts->get('browser')['uploadGroupName'])->toBe('browser-artifacts') ->and($failureClasses->keys()->all())->toEqualCanonicalizing([ 'test-failure', 'wrapper-failure', 'budget-breach', 'artifact-publication-failure', 'infrastructure-failure', ]); }); it('keeps every lane declaration populated with governance metadata, selectors, and segmented family expectations', function (): void { foreach (TestLaneManifest::manifest()['lanes'] as $lane) { expect(trim($lane['description']))->not->toBe('') ->and(trim($lane['intendedAudience']))->not->toBe('') ->and($lane['includedFamilies'])->not->toBeEmpty() ->and($lane['ownershipExpectations'])->not->toBe('') ->and($lane['artifacts'])->not->toBeEmpty() ->and($lane['budget']['thresholdSeconds'])->toBeGreaterThan(0) ->and($lane['budget']['baselineSource'])->toBeString() ->and($lane['dbStrategy']['connectionMode'])->toBeString(); $selectors = $lane['selectors']; foreach ([ 'includeSuites', 'includePaths', 'includeGroups', 'includeFiles', 'excludeSuites', 'excludePaths', 'excludeGroups', 'excludeFiles', ] as $selectorKey) { expect($selectors)->toHaveKey($selectorKey); } } expect(TestLaneManifest::lane('confidence')['includedFamilies'])->toContain('ui-light', 'ui-workflow') ->and(TestLaneManifest::lane('confidence')['excludedFamilies'])->toContain('surface-guard', 'discovery-heavy') ->and(TestLaneManifest::lane('heavy-governance')['includedFamilies'])->toContain('surface-guard', 'discovery-heavy'); }); it('exposes the spec 208 classification catalog and seeded family inventory with required metadata', function (): void { $classifications = collect(TestLaneManifest::classifications())->keyBy('classificationId'); $families = collect(TestLaneManifest::families())->keyBy('familyId'); expect($classifications->keys()->all())->toEqualCanonicalizing([ 'ui-light', 'ui-workflow', 'surface-guard', 'discovery-heavy', 'browser', ]) ->and($classifications->get('browser')['allowedLaneIds'])->toBe(['browser']) ->and($classifications->get('surface-guard')['defaultLaneId'])->toBe('heavy-governance') ->and($classifications->get('discovery-heavy')['forbiddenLaneIds'])->toContain('fast-feedback', 'confidence') ->and($families->has('baseline-profile-start-surfaces'))->toBeTrue() ->and($families->has('findings-workflow-surfaces'))->toBeTrue() ->and($families->has('finding-bulk-actions-workflow'))->toBeTrue() ->and($families->has('drift-bulk-triage-all-matching'))->toBeTrue() ->and($families->has('policy-resource-admin-search-parity'))->toBeTrue() ->and($families->has('workspace-only-admin-surface-independence'))->toBeTrue() ->and($families->has('workspace-settings-slice-management'))->toBeTrue() ->and($families->has('baseline-compare-matrix-workflow'))->toBeTrue() ->and($families->has('browser-smoke'))->toBeTrue(); foreach (TestLaneManifest::families() as $family) { expect(trim($family['purpose']))->not->toBe('') ->and(trim($family['currentLaneId']))->not->toBe('') ->and(trim($family['targetLaneId']))->not->toBe('') ->and($family['selectors'])->not->toBeEmpty() ->and($family['hotspotFiles'])->not->toBeEmpty() ->and(trim($family['validationStatus']))->not->toBe(''); if ($family['targetLaneId'] === 'confidence') { expect(trim((string) ($family['confidenceRationale'] ?? '')))->not->toBe(''); } } }); it('keeps family budgets derived from the generic budget targets for report consumers', function (): void { $familyBudgets = TestLaneManifest::familyBudgets(); expect($familyBudgets)->not->toBeEmpty() ->and($familyBudgets[0])->toHaveKeys(['familyId', 'targetType', 'targetId', 'selectors', 'thresholdSeconds']) ->and(collect($familyBudgets)->pluck('familyId')->all()) ->toContain('action-surface-contract', 'browser-smoke', 'baseline-compare-matrix-workflow', 'baseline-profile-start-surfaces', 'drift-bulk-triage-all-matching', 'finding-bulk-actions-workflow', 'findings-workflow-surfaces', 'workspace-only-admin-surface-independence', 'workspace-settings-slice-management'); }); it('publishes the heavy-governance contract, inventory, and guidance surfaces needed for honest rerun review', function (): void { $contract = TestLaneManifest::heavyGovernanceBudgetContract(); $inventory = collect(TestLaneManifest::heavyGovernanceHotspotInventory()); expect($contract['summaryThresholdSeconds'])->toBe(300.0) ->and($contract['evaluationThresholdSeconds'])->toBe(200.0) ->and($contract['normalizedThresholdSeconds'])->toBeGreaterThanOrEqual(300.0) ->and($contract['decisionStatus'])->toBeIn(['recovered', 'recalibrated']) ->and($inventory)->toHaveCount(6) ->and($inventory->pluck('familyId')->all())->toEqual([ 'baseline-profile-start-surfaces', 'action-surface-contract', 'ops-ux-governance', 'findings-workflow-surfaces', 'finding-bulk-actions-workflow', 'workspace-settings-slice-management', ]) ->and(collect(TestLaneManifest::heavyGovernanceBudgetSnapshots()))->toHaveCount(2) ->and(TestLaneManifest::heavyGovernanceBudgetOutcome())->toHaveKeys([ 'decisionStatus', 'finalThresholdSeconds', 'deltaSeconds', 'remainingOpenFamilies', 'followUpDebt', ]) ->and(collect(TestLaneManifest::heavyGovernanceAuthorGuidance())->pluck('ruleId')->all()) ->toEqualCanonicalizing([ 'heavy-family-reuse-before-creation', 'heavy-family-create-only-for-new-trust', 'split-discovery-workflow-surface-concerns', 'retain-intentional-heavy-depth-explicitly', 'record-helper-or-fixture-residuals', ]); });