11.0, ]; $workflowId = $laneId === 'confidence' ? 'main-confidence' : 'pr-fast-feedback'; $triggerClass = $laneId === 'confidence' ? 'mainline-push' : 'pull-request'; $comparisonProfile = in_array($laneId, ['fast-feedback', 'confidence'], true) ? 'shared-test-fixture-slimming' : null; $baseReport = TestLaneTrendFixtures::buildReport( laneId: $laneId, wallClockSeconds: $seededHistory[0], durationsByFile: $durationsByFile, artifactDirectory: $artifactDirectory, ciContext: [ 'workflowId' => $workflowId, 'triggerClass' => $triggerClass, 'entryPointResolved' => true, 'workflowLaneMatched' => true, ], comparisonProfile: $comparisonProfile, ); $artifact = $baseReport['trendHistoryArtifact']; $templateRecord = $artifact['history'][0]; $artifact['history'] = array_values(array_map( static function (float $seconds, int $index) use ($templateRecord): array { $record = $templateRecord; $record['runRef'] = sprintf('%s-recalibration-%d', $templateRecord['laneId'], $index + 1); $record['generatedAt'] = sprintf('2026-04-%02dT08:30:00+00:00', $index + 1); $record['wallClockSeconds'] = round($seconds, 6); return $record; }, $seededHistory, array_keys($seededHistory), )); TestLaneTrendFixtures::writeTrendHistory($laneId, $artifact, $artifactDirectory); return TestLaneTrendFixtures::buildReport( laneId: $laneId, wallClockSeconds: $currentSeconds, durationsByFile: $durationsByFile, artifactDirectory: $artifactDirectory, ciContext: [ 'workflowId' => $workflowId, 'triggerClass' => $triggerClass, 'entryPointResolved' => true, 'workflowLaneMatched' => true, ], comparisonProfile: $comparisonProfile, ); } it('emits candidate, approved, and rejected recalibration records with explicit summary disclosure', function (): void { $candidateReport = reportWithSeededHistory('confidence', 'candidate', [420.0, 380.0, 340.0, 300.0, 260.0], 460.0); $approvedReport = reportWithSeededHistory('fast-feedback', 'approved', [176.0, 176.3, 176.1, 176.4, 176.2], 176.3); $rejectedReport = reportWithSeededHistory('fast-feedback', 'rejected', [176.0, 191.0, 177.0, 192.0, 178.0], 193.0); $approvedDecision = TestLaneBudget::buildRecalibrationDecisionRecord( laneId: 'fast-feedback', targetType: 'baseline', assessment: ['recalibrationRecommendation' => 'review-baseline'], historyRecords: $approvedReport['trendHistoryArtifact']['history'], decisionStatus: 'approved', rationaleCode: 'post-improvement-reset', recordedIn: 'specs/211-runtime-trend-recalibration/spec.md', proposedValueSeconds: 182.0, notes: 'Approved baseline reset after the suite stabilized following a deliberate improvement pass.', ); $candidateDecision = $candidateReport['trendRecalibrationDecisions'][0] ?? null; $rejectedDecision = $rejectedReport['trendRecalibrationDecisions'][0] ?? null; $approvedReport['trendRecalibrationDecisions'][] = $approvedDecision; $approvedReport['trendHistoryArtifact']['recalibrationDecisions'][] = $approvedDecision; TestLaneReport::writeArtifacts( laneId: 'confidence', report: $candidateReport, artifactDirectory: $candidateReport['artifactDirectory'], ); TestLaneReport::writeArtifacts( laneId: 'fast-feedback', report: $approvedReport, artifactDirectory: $approvedReport['artifactDirectory'], ); TestLaneReport::writeArtifacts( laneId: 'fast-feedback', report: $rejectedReport, artifactDirectory: $rejectedReport['artifactDirectory'], ); $candidateSummary = (string) file_get_contents(TestLaneManifest::absolutePath( TestLaneReport::artifactPaths('confidence', $candidateReport['artifactDirectory'])['summary'], )); $approvedSummary = (string) file_get_contents(TestLaneManifest::absolutePath( TestLaneReport::artifactPaths('fast-feedback', $approvedReport['artifactDirectory'])['summary'], )); $rejectedSummary = (string) file_get_contents(TestLaneManifest::absolutePath( TestLaneReport::artifactPaths('fast-feedback', $rejectedReport['artifactDirectory'])['summary'], )); expect($candidateDecision)->toBeArray() ->and($candidateDecision['decisionStatus'])->toBe('candidate') ->and($candidateDecision['targetType'])->toBe('budget') ->and($approvedDecision['decisionStatus'])->toBe('approved') ->and($approvedDecision['targetType'])->toBe('baseline') ->and($rejectedDecision)->toBeArray() ->and($rejectedDecision['decisionStatus'])->toBe('rejected') ->and($rejectedDecision['rationaleCode'])->toBe('noise-rejected') ->and($candidateSummary)->toContain('Recalibration: budget candidate') ->and($approvedSummary)->toContain('Recalibration: baseline approved') ->and($rejectedSummary)->toContain('Recalibration: budget rejected', 'noise-rejected'); });