feat: reduce receipt page surface depth and simplify evidence summaries (#468)

Automated PR created by Codex via Gitea API.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #468
This commit is contained in:
ahmido 2026-06-22 11:03:10 +00:00
parent e95fcf5e38
commit a5b7300ca9
37 changed files with 2140 additions and 191 deletions

View File

@ -224,7 +224,8 @@ public static function infolist(Schema $schema): Schema
->collapsible() ->collapsible()
->collapsed() ->collapsed()
->columnSpanFull(), ->columnSpanFull(),
Section::make('Evidence dimensions') Section::make('Internal evidence dimensions')
->description('Dimension-level evidence remains available for audit and support after the receipt outcome is understood.')
->schema([ ->schema([
RepeatableEntry::make('items') RepeatableEntry::make('items')
->hiddenLabel() ->hiddenLabel()
@ -282,7 +283,10 @@ public static function infolist(Schema $schema): Schema
->columnSpanFull(), ->columnSpanFull(),
]) ])
->columns(3), ->columns(3),
]), ])
->collapsible()
->collapsed()
->columnSpanFull(),
]); ]);
} }
@ -489,8 +493,6 @@ private static function dimensionSummaryPresentation(EvidenceSnapshotItem $item)
default => static::genericSummaryPresentation($payload), default => static::genericSummaryPresentation($payload),
}; };
$presentation['artifact_sources'] = [static::artifactSourceSummary($item)];
return $presentation; return $presentation;
} }

View File

@ -101,65 +101,78 @@ private function decision(
$copy = match ($state) { $copy = match ($state) {
'not_executed' => [ 'not_executed' => [
'status_label' => 'Not executed', 'status_label' => 'Not configured',
'reason' => 'This record proves preview truth, not environment recovery.', 'reason' => 'This record proves preview truth, not environment recovery.',
'impact' => 'No execution proof or post-run evidence exists yet.', 'impact' => 'No execution proof or post-run evidence exists yet.',
'primary_next_action' => 'Review preview', 'primary_next_action' => 'Review preview',
'follow_up_summary' => 'Review preview evidence before queueing execution.',
'primary_next_url' => null, 'primary_next_url' => null,
'tone' => 'gray', 'tone' => 'gray',
'icon' => 'heroicon-m-eye', 'icon' => 'heroicon-m-eye',
], ],
'in_progress' => [ 'in_progress' => [
'status_label' => 'Execution in progress', 'status_label' => 'Running',
'reason' => 'Restore execution is currently running.', 'reason' => 'Restore execution is currently running.',
'impact' => 'Results and post-run evidence are not final yet.', 'impact' => 'Results and post-run evidence are not final yet.',
'primary_next_action' => 'View operation progress', 'primary_next_action' => 'Monitor restore progress',
'primary_next_url' => $operationProof['url'] ?? null, 'primary_next_target' => 'restore-run-technical-details',
'follow_up_summary' => 'Use technical run details for operation progress and final proof.',
'primary_next_url' => null,
'tone' => 'info', 'tone' => 'info',
'icon' => 'heroicon-m-arrow-path', 'icon' => 'heroicon-m-arrow-path',
], ],
'completed_with_evidence' => [ 'completed_with_evidence' => [
'status_label' => 'Completed with evidence available', 'status_label' => 'Ready',
'reason' => 'Execution proof and post-run evidence are available.', 'reason' => 'Execution proof and post-run evidence are available.',
'impact' => 'Review evidence before treating this restore as recovery proof.', 'impact' => 'Review evidence before treating this restore as recovery proof.',
'primary_next_action' => 'Open evidence', 'primary_next_action' => 'Review recovery evidence',
'primary_next_url' => $postRunEvidence['url'] ?? null, 'primary_next_target' => 'restore-run-technical-details',
'follow_up_summary' => 'Review the linked evidence before relying on this run as recovery proof.',
'primary_next_url' => null,
'tone' => 'success', 'tone' => 'success',
'icon' => 'heroicon-m-shield-check', 'icon' => 'heroicon-m-shield-check',
], ],
'needs_review' => [ 'needs_review' => [
'status_label' => 'Completed with items needing review', 'status_label' => 'Needs attention',
'reason' => $attention->summary, 'reason' => $attention->summary,
'impact' => 'Review item outcomes before relying on the result.', 'impact' => 'Review item outcomes before relying on the result.',
'primary_next_action' => 'Review item outcomes', 'primary_next_action' => 'Review item outcomes',
'primary_next_target' => 'restore-run-item-outcomes',
'follow_up_summary' => 'Review item outcomes that require attention.',
'primary_next_url' => null, 'primary_next_url' => null,
'tone' => 'warning', 'tone' => 'warning',
'icon' => 'heroicon-m-exclamation-triangle', 'icon' => 'heroicon-m-exclamation-triangle',
], ],
'failed' => [ 'failed' => [
'status_label' => 'Restore failed', 'status_label' => 'Failed',
'reason' => 'The restore did not complete successfully.', 'reason' => 'The restore did not complete successfully.',
'impact' => 'Some requested changes may not have been applied.', 'impact' => 'Some requested changes may not have been applied.',
'primary_next_action' => 'Review failure details', 'primary_next_action' => 'Review failure details',
'primary_next_target' => 'restore-run-item-outcomes',
'follow_up_summary' => 'Review failed items and provider errors before retrying.',
'primary_next_url' => null, 'primary_next_url' => null,
'tone' => 'danger', 'tone' => 'danger',
'icon' => 'heroicon-m-x-circle', 'icon' => 'heroicon-m-x-circle',
], ],
'blocked_or_cancelled' => [ 'blocked_or_cancelled' => [
'status_label' => 'Restore blocked / cancelled', 'status_label' => 'Blocked',
'reason' => 'Restore did not execute due to cancellation or blocker.', 'reason' => 'Restore did not execute due to cancellation or blocker.',
'impact' => 'No recovery proof exists.', 'impact' => 'No recovery proof exists.',
'primary_next_action' => 'Review blocker', 'primary_next_action' => 'Review blocker',
'primary_next_url' => $operationProof['url'] ?? null, 'primary_next_target' => 'restore-run-technical-details',
'follow_up_summary' => 'Review the blocker before retrying.',
'primary_next_url' => null,
'tone' => 'warning', 'tone' => 'warning',
'icon' => 'heroicon-m-no-symbol', 'icon' => 'heroicon-m-no-symbol',
], ],
default => [ default => [
'status_label' => 'Completed, recovery proof incomplete', 'status_label' => 'Needs attention',
'reason' => 'Execution completed, but post-run evidence is not available yet.', 'reason' => 'Execution completed, but post-run evidence is not available yet.',
'impact' => 'Do not treat this restore as verified recovery until evidence has been reviewed.', 'impact' => 'Do not treat this restore as verified recovery until evidence has been reviewed.',
'primary_next_action' => filled($operationProof['url'] ?? null) ? 'Open operation proof' : 'Review proof gap', 'primary_next_action' => 'View technical run details',
'primary_next_url' => $operationProof['url'] ?? null, 'primary_next_target' => 'restore-run-technical-details',
'follow_up_summary' => 'Confirm operation proof and capture post-run evidence before closing this as recovered.',
'primary_next_url' => null,
'tone' => 'warning', 'tone' => 'warning',
'icon' => 'heroicon-m-shield-exclamation', 'icon' => 'heroicon-m-shield-exclamation',
], ],
@ -170,7 +183,9 @@ private function decision(
...$copy, ...$copy,
'question' => 'Was this restore executed safely, and is recovery proof available?', 'question' => 'Was this restore executed safely, and is recovery proof available?',
'attention_summary' => $attention->summary, 'attention_summary' => $attention->summary,
'primary_cause_family' => RestoreSafetyCopy::primaryCauseFamily($attention->primaryCauseFamily), 'primary_cause_family' => $attention->primaryCauseFamily === 'none'
? null
: RestoreSafetyCopy::primaryCauseFamily($attention->primaryCauseFamily),
'result_next_action' => RestoreSafetyCopy::primaryNextAction($attention->primaryNextAction), 'result_next_action' => RestoreSafetyCopy::primaryNextAction($attention->primaryNextAction),
'recovery_claim_boundary' => RestoreSafetyCopy::recoveryBoundary($attention->recoveryClaimBoundary), 'recovery_claim_boundary' => RestoreSafetyCopy::recoveryBoundary($attention->recoveryClaimBoundary),
]; ];
@ -263,17 +278,22 @@ private function operationProof(?OperationRun $operationRun): array
*/ */
private function postRunEvidence(RestoreRun $restoreRun, ?OperationRun $operationRun): array private function postRunEvidence(RestoreRun $restoreRun, ?OperationRun $operationRun): array
{ {
if (! $operationRun instanceof OperationRun || ! $this->canViewEvidence($restoreRun)) { if (! $operationRun instanceof OperationRun) {
return [ return $this->unavailablePostRunEvidence(
'state' => 'unavailable', 'Post-run evidence requires linked operation proof first. Review operation proof before treating this restore as recovered.',
'label' => 'Post-run evidence unavailable', );
'url' => null, }
'status' => null,
'completeness' => null, if (! $this->canViewEvidence($restoreRun)) {
'status_badge' => $this->statusBadge('gray', 'Unavailable', 'heroicon-m-minus-circle'), return $this->unavailablePostRunEvidence(
'completeness_badge' => null, 'Evidence access is required to capture or review post-run evidence. Ask an operator with evidence access before closing this restore as recovered.',
'identifier' => null, );
]; }
if (in_array((string) $operationRun->status, [OperationRunStatus::Queued->value, OperationRunStatus::Running->value], true)) {
return $this->unavailablePostRunEvidence(
'Wait until operation proof is completed, then capture a post-run evidence snapshot before closing this restore as recovered.',
);
} }
$snapshots = EvidenceSnapshot::query() $snapshots = EvidenceSnapshot::query()
@ -292,16 +312,13 @@ private function postRunEvidence(RestoreRun $restoreRun, ?OperationRun $operatio
?? $snapshots->first(); ?? $snapshots->first();
if (! $snapshot instanceof EvidenceSnapshot || ! EvidenceSnapshotResource::canView($snapshot)) { if (! $snapshot instanceof EvidenceSnapshot || ! EvidenceSnapshotResource::canView($snapshot)) {
return [ return $this->unavailablePostRunEvidence(
'state' => 'unavailable', $this->canManageEvidence($restoreRun)
'label' => 'Post-run evidence unavailable', ? 'Open Evidence snapshots and use Create snapshot for this environment. Do not close the restore as recovered until the post-run snapshot is available.'
'url' => null, : 'A post-run snapshot is not linked yet. Ask an operator with evidence management access to create one before closing this restore as recovered.',
'status' => null, $this->evidenceSnapshotIndexUrl($restoreRun),
'completeness' => null, 'Open evidence snapshots',
'status_badge' => $this->statusBadge('gray', 'Unavailable', 'heroicon-m-minus-circle'), );
'completeness_badge' => null,
'identifier' => null,
];
} }
$state = $snapshot->status === EvidenceSnapshotStatus::Active->value ? 'available' : 'in_progress'; $state = $snapshot->status === EvidenceSnapshotStatus::Active->value ? 'available' : 'in_progress';
@ -310,6 +327,11 @@ private function postRunEvidence(RestoreRun $restoreRun, ?OperationRun $operatio
'state' => $state, 'state' => $state,
'label' => $state === 'available' ? 'Post-run evidence available' : 'Post-run evidence in progress', 'label' => $state === 'available' ? 'Post-run evidence available' : 'Post-run evidence in progress',
'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $restoreRun->tenant), 'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $restoreRun->tenant),
'guidance' => $state === 'available'
? 'Review this post-run snapshot before relying on the restore as recovery proof.'
: 'Snapshot generation is still running. Wait until evidence is available before closing the restore as recovered.',
'next_action_url' => null,
'next_action_label' => null,
'status' => (string) $snapshot->status, 'status' => (string) $snapshot->status,
'completeness' => (string) $snapshot->completeness_state, 'completeness' => (string) $snapshot->completeness_state,
'status_badge' => $this->badge(BadgeDomain::EvidenceSnapshotStatus, (string) $snapshot->status), 'status_badge' => $this->badge(BadgeDomain::EvidenceSnapshotStatus, (string) $snapshot->status),
@ -350,21 +372,71 @@ private function canViewEvidence(RestoreRun $restoreRun): bool
&& $user->can(Capabilities::EVIDENCE_VIEW, $tenant); && $user->can(Capabilities::EVIDENCE_VIEW, $tenant);
} }
private function canManageEvidence(RestoreRun $restoreRun): bool
{
$user = auth()->user();
$tenant = $restoreRun->tenant;
return $user instanceof User
&& $tenant !== null
&& $user->can(Capabilities::EVIDENCE_MANAGE, $tenant);
}
/**
* @return array<string, mixed>
*/
private function unavailablePostRunEvidence(string $guidance, ?string $nextActionUrl = null, ?string $nextActionLabel = null): array
{
return [
'state' => 'unavailable',
'label' => 'Post-run evidence unavailable',
'url' => null,
'guidance' => $guidance,
'next_action_url' => $nextActionUrl,
'next_action_label' => $nextActionLabel,
'status' => null,
'completeness' => null,
'status_badge' => $this->statusBadge('gray', 'Unavailable', 'heroicon-m-minus-circle'),
'completeness_badge' => null,
'identifier' => null,
];
}
private function evidenceSnapshotIndexUrl(RestoreRun $restoreRun): ?string
{
if (! $this->canViewEvidence($restoreRun) || $restoreRun->tenant === null) {
return null;
}
return EvidenceSnapshotResource::getUrl('index', tenant: $restoreRun->tenant);
}
/** /**
* @return array<string, mixed> * @return array<string, mixed>
*/ */
private function resultSummary(RestoreRun $restoreRun): array private function resultSummary(RestoreRun $restoreRun): array
{ {
$metadata = is_array($restoreRun->metadata) ? $restoreRun->metadata : []; $metadata = is_array($restoreRun->metadata) ? $restoreRun->metadata : [];
$itemCounts = $this->itemResultCounts($restoreRun);
$keys = ['total', 'succeeded', 'failed', 'skipped', 'partial', 'non_applied']; $keys = ['total', 'succeeded', 'failed', 'skipped', 'partial', 'non_applied'];
$available = collect($keys)->contains( $available = collect($keys)->contains(
static fn (string $key): bool => array_key_exists($key, $metadata) && is_numeric($metadata[$key]), static fn (string $key): bool => array_key_exists($key, $metadata) && is_numeric($metadata[$key]),
); );
if (! $available) { if (! $available) {
if ($itemCounts['requested'] > 0) {
return [
'available' => true,
'message' => 'Counts derived from recorded item outcomes.',
'source_label' => 'Recorded outcomes',
'counts' => $itemCounts,
];
}
return [ return [
'available' => false, 'available' => false,
'message' => 'Result summary unavailable', 'message' => 'Result counts are not recorded for this run.',
'source_label' => 'Unavailable',
'counts' => [], 'counts' => [],
]; ];
} }
@ -377,6 +449,16 @@ private function resultSummary(RestoreRun $restoreRun): array
'partial' => $this->numericCount($metadata, 'partial'), 'partial' => $this->numericCount($metadata, 'partial'),
'non_applied' => $this->numericCount($metadata, 'non_applied'), 'non_applied' => $this->numericCount($metadata, 'non_applied'),
]; ];
$completedFromItems = false;
if ($itemCounts['requested'] > 0) {
foreach (['requested', 'applied', 'failed', 'skipped', 'partial', 'non_applied'] as $key) {
if ($counts[$key] === null) {
$counts[$key] = $itemCounts[$key];
$completedFromItems = true;
}
}
}
$reviewValues = array_filter([ $reviewValues = array_filter([
$counts['failed'], $counts['failed'],
@ -389,11 +471,56 @@ private function resultSummary(RestoreRun $restoreRun): array
return [ return [
'available' => true, 'available' => true,
'message' => 'Repo-backed result counts from restore metadata.', 'message' => $completedFromItems
? 'Counts completed from recorded item outcomes.'
: 'Recorded restore counts from stored metadata.',
'source_label' => $completedFromItems ? 'Recorded counts' : 'Stored metadata',
'counts' => $counts, 'counts' => $counts,
]; ];
} }
/**
* @return array{requested:int,applied:int,failed:int,skipped:int,partial:int,non_applied:int,needs_review:int}
*/
private function itemResultCounts(RestoreRun $restoreRun): array
{
$results = is_array($restoreRun->results) ? $restoreRun->results : [];
$items = is_array($results['items'] ?? null) ? array_values($results['items']) : [];
$counts = [
'requested' => 0,
'applied' => 0,
'failed' => 0,
'skipped' => 0,
'partial' => 0,
'non_applied' => 0,
'needs_review' => 0,
];
foreach ($items as $item) {
if (! is_array($item)) {
continue;
}
$counts['requested']++;
$status = strtolower((string) ($item['status'] ?? 'unknown'));
match ($status) {
'applied', 'completed', 'success', 'succeeded' => $counts['applied']++,
'failed', 'error' => $counts['failed']++,
'skipped' => $counts['skipped']++,
'partial', 'partially_applied', 'manual_required' => $counts['partial']++,
default => $counts['non_applied']++,
};
}
$counts['needs_review'] = $counts['failed']
+ $counts['skipped']
+ $counts['partial']
+ $counts['non_applied'];
return $counts;
}
/** /**
* @param list<array<string, mixed>> $itemOutcomes * @param list<array<string, mixed>> $itemOutcomes
* @param array<string, mixed> $resultSummary * @param array<string, mixed> $resultSummary

View File

@ -196,17 +196,16 @@ public static function infolist(Schema $schema): Schema
->label('Reports included') ->label('Reports included')
->placeholder('—'), ->placeholder('—'),
TextEntry::make('summary.evidence_resolution.outcome') TextEntry::make('summary.evidence_resolution.outcome')
->label('Evidence resolution') ->label('Evidence basis')
->placeholder('—'), ->placeholder('—'),
TextEntry::make('evidenceSnapshot.completeness_state') TextEntry::make('evidenceSnapshot.completeness_state')
->label('View internal evidence details') ->label('Evidence completeness')
->badge() ->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness)) ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness))
->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness))
->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness))
->placeholder('—') ->placeholder('—'),
->url(fn (ReviewPack $record): ?string => static::evidenceSnapshotUrl($record)),
TextEntry::make('environmentReview.status') TextEntry::make('environmentReview.status')
->label('Released review') ->label('Released review')
->badge() ->badge()
@ -246,6 +245,11 @@ public static function infolist(Schema $schema): Schema
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus)) ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus))
->placeholder('—'), ->placeholder('—'),
TextEntry::make('internal_evidence_snapshot')
->label('Internal evidence details')
->state(fn (ReviewPack $record): string => $record->evidence_snapshot_id !== null ? 'Open evidence snapshot' : 'No evidence snapshot linked')
->url(fn (ReviewPack $record): ?string => static::evidenceSnapshotUrl($record))
->hidden(fn (): bool => static::isCustomerWorkspaceFlow()),
TextEntry::make('operationRun.id') TextEntry::make('operationRun.id')
->label('Operation') ->label('Operation')
->url(function (ReviewPack $record): ?string { ->url(function (ReviewPack $record): ?string {

View File

@ -259,7 +259,8 @@ public static function infolist(Schema $schema): Schema
->visible(fn (StoredReport $record): bool => $record->report_type === StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES) ->visible(fn (StoredReport $record): bool => $record->report_type === StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES)
->columnSpanFull(), ->columnSpanFull(),
Section::make('Technical report details') Section::make('Internal report artifact')
->description('Raw artifact descriptors, fingerprints, and renderer metadata are secondary support details.')
->schema([ ->schema([
TextEntry::make('display_reference') TextEntry::make('display_reference')
->label('Artifact reference') ->label('Artifact reference')
@ -308,7 +309,8 @@ public static function infolist(Schema $schema): Schema
->collapsed() ->collapsed()
->columnSpanFull(), ->columnSpanFull(),
Section::make('Raw payload') Section::make('Internal payload details')
->description('Stored payload JSON is technical evidence and stays out of the default receipt summary.')
->schema([ ->schema([
ViewEntry::make('payload') ViewEntry::make('payload')
->hiddenLabel() ->hiddenLabel()

View File

@ -15,9 +15,9 @@
use App\Support\Ui\EnterpriseDetail\EnterpriseDetailPageData; use App\Support\Ui\EnterpriseDetail\EnterpriseDetailPageData;
use App\Support\Ui\EnterpriseDetail\EnterpriseDetailSectionFactory; use App\Support\Ui\EnterpriseDetail\EnterpriseDetailSectionFactory;
use App\Support\Ui\EnterpriseDetail\SummaryHeaderData; use App\Support\Ui\EnterpriseDetail\SummaryHeaderData;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -246,6 +246,9 @@ public function presentEnterpriseDetail(BaselineSnapshot $snapshot, array $relat
$rendered->groups, $rendered->groups,
)], )],
emptyState: $factory->emptyState('No snapshot items were captured for this baseline snapshot.'), emptyState: $factory->emptyState('No snapshot items were captured for this baseline snapshot.'),
description: 'Detailed governed-subject inventory is secondary to the receipt summary and remains collapsed by default.',
collapsible: true,
collapsed: true,
), ),
) )
->addSupportingCard( ->addSupportingCard(

View File

@ -5,6 +5,8 @@
$groups = isset($groups) ? $groups : (isset($getState) ? $getState() : []); $groups = isset($groups) ? $groups : (isset($getState) ? $getState() : []);
$groups = is_array($groups) ? $groups : []; $groups = is_array($groups) ? $groups : [];
$visibleGroups = array_slice($groups, 0, 8);
$hiddenGroupCount = max(0, count($groups) - count($visibleGroups));
$formatTimestamp = static function (?string $value): string { $formatTimestamp = static function (?string $value): string {
if (! is_string($value) || trim($value) === '') { if (! is_string($value) || trim($value) === '') {
@ -25,7 +27,7 @@
No snapshot items were captured for this baseline snapshot. No snapshot items were captured for this baseline snapshot.
</div> </div>
@else @else
@foreach ($groups as $group) @foreach ($visibleGroups as $group)
@php @php
$fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $group['fidelity'] ?? null); $fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $group['fidelity'] ?? null);
$gapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($group, 'gapSummary.badge_state')); $gapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($group, 'gapSummary.badge_state'));
@ -70,8 +72,14 @@
</div> </div>
@endif @endif
<div class="space-y-3"> <div class="space-y-3">
@foreach (($group['items'] ?? []) as $item) @php
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
$visibleItems = array_slice($items, 0, 8);
$hiddenItemCount = max(0, count($items) - count($visibleItems));
@endphp
@foreach ($visibleItems as $item)
@php @php
$itemFidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $item['fidelity'] ?? null); $itemFidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $item['fidelity'] ?? null);
$itemGapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($item, 'gapSummary.badge_state')); $itemGapSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotGapStatus, data_get($item, 'gapSummary.badge_state'));
@ -138,9 +146,21 @@
@endif @endif
</div> </div>
@endforeach @endforeach
@if ($hiddenItemCount > 0)
<div class="rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900/40 dark:text-gray-200">
Showing the first 8 captured items for this governed subject. {{ $hiddenItemCount }} additional {{ \Illuminate\Support\Str::plural('item', $hiddenItemCount) }} stay in internal detail.
</div>
@endif
</div> </div>
</div> </div>
</x-filament::section> </x-filament::section>
@endforeach @endforeach
@if ($hiddenGroupCount > 0)
<div class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900/40 dark:text-gray-200">
Showing the first 8 governed subjects on the receipt. {{ $hiddenGroupCount }} additional {{ \Illuminate\Support\Str::plural('subject', $hiddenGroupCount) }} stay in internal detail.
</div>
@endif
@endif @endif
</div> </div>

View File

@ -5,6 +5,8 @@
$rows = isset($rows) ? $rows : (isset($getState) ? $getState() : []); $rows = isset($rows) ? $rows : (isset($getState) ? $getState() : []);
$rows = is_array($rows) ? $rows : []; $rows = is_array($rows) ? $rows : [];
$visibleRows = array_slice($rows, 0, 8);
$hiddenRowCount = max(0, count($rows) - count($visibleRows));
$formatTimestamp = static function (?string $value): string { $formatTimestamp = static function (?string $value): string {
if (! is_string($value) || trim($value) === '') { if (! is_string($value) || trim($value) === '') {
@ -38,7 +40,7 @@
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-gray-100 dark:divide-gray-800"> <tbody class="divide-y divide-gray-100 dark:divide-gray-800">
@foreach ($rows as $row) @foreach ($visibleRows as $row)
@php @php
$fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $row['fidelity'] ?? null); $fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $row['fidelity'] ?? null);
$gapState = (($row['gapCount'] ?? 0) > 0) ? 'gaps_present' : 'clear'; $gapState = (($row['gapCount'] ?? 0) > 0) ? 'gaps_present' : 'clear';
@ -78,5 +80,11 @@
</tbody> </tbody>
</table> </table>
</div> </div>
@if ($hiddenRowCount > 0)
<div class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900/40 dark:text-gray-200">
Showing the first 8 governed subjects for receipt review. {{ $hiddenRowCount }} additional {{ \Illuminate\Support\Str::plural('subject', $hiddenRowCount) }} stay in the internal detail path.
</div>
@endif
@endif @endif
</div> </div>

View File

@ -3,7 +3,6 @@
$state = is_array($state) ? $state : []; $state = is_array($state) ? $state : [];
$summary = is_string($state['summary'] ?? null) ? $state['summary'] : null; $summary = is_string($state['summary'] ?? null) ? $state['summary'] : null;
$artifactSources = is_array($state['artifact_sources'] ?? null) ? $state['artifact_sources'] : [];
$highlights = is_array($state['highlights'] ?? null) ? $state['highlights'] : []; $highlights = is_array($state['highlights'] ?? null) ? $state['highlights'] : [];
$items = is_array($state['items'] ?? null) ? $state['items'] : []; $items = is_array($state['items'] ?? null) ? $state['items'] : [];
@endphp @endphp
@ -13,36 +12,6 @@
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $summary }}</div> <div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $summary }}</div>
@endif @endif
@if ($artifactSources !== [])
<div class="space-y-2">
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Artifact source</div>
<dl class="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">
@foreach ($artifactSources as $source)
@continue(! is_array($source))
@foreach ([
'Source family' => $source['source_family'] ?? null,
'Source kind' => $source['source_kind'] ?? null,
'Source target' => $source['source_target_kind'] ?? null,
'Control' => $source['control_key'] ?? null,
'Detector' => $source['detector_key'] ?? null,
] as $label => $value)
@php
$value = is_string($value) && trim($value) !== '' ? \Illuminate\Support\Str::headline($value) : null;
@endphp
@continue($value === null)
<div class="rounded-md border border-gray-100 bg-gray-50 px-3 py-2 dark:border-gray-800 dark:bg-gray-950/60">
<dt class="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">{{ $label }}</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $value }}</dd>
</div>
@endforeach
@endforeach
</dl>
</div>
@endif
@if ($highlights !== []) @if ($highlights !== [])
<dl class="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3"> <dl class="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">
@foreach ($highlights as $highlight) @foreach ($highlights as $highlight)

View File

@ -10,25 +10,22 @@
$summaryCounts = is_array($resultSummary['counts'] ?? null) ? $resultSummary['counts'] : []; $summaryCounts = is_array($resultSummary['counts'] ?? null) ? $resultSummary['counts'] : [];
$itemOutcomeEvidence = is_array($surface['itemOutcomeEvidence'] ?? null) ? $surface['itemOutcomeEvidence'] : []; $itemOutcomeEvidence = is_array($surface['itemOutcomeEvidence'] ?? null) ? $surface['itemOutcomeEvidence'] : [];
$itemOutcomes = collect(is_array($surface['itemOutcomes'] ?? null) ? $surface['itemOutcomes'] : []); $itemOutcomes = collect(is_array($surface['itemOutcomes'] ?? null) ? $surface['itemOutcomes'] : []);
$visibleItemOutcomes = $itemOutcomes->take(8);
$hiddenItemOutcomeCount = max(0, $itemOutcomes->count() - $visibleItemOutcomes->count());
$foundationOutcomes = collect(is_array($surface['foundationOutcomes'] ?? null) ? $surface['foundationOutcomes'] : []); $foundationOutcomes = collect(is_array($surface['foundationOutcomes'] ?? null) ? $surface['foundationOutcomes'] : []);
$visibleFoundationOutcomes = $foundationOutcomes->take(8);
$hiddenFoundationOutcomeCount = max(0, $foundationOutcomes->count() - $visibleFoundationOutcomes->count());
$diagnostics = is_array($surface['diagnostics'] ?? null) ? $surface['diagnostics'] : []; $diagnostics = is_array($surface['diagnostics'] ?? null) ? $surface['diagnostics'] : [];
$runContext = is_array($surface['runContext'] ?? null) ? $surface['runContext'] : []; $runContext = is_array($surface['runContext'] ?? null) ? $surface['runContext'] : [];
$resultAttention = is_array($surface['resultAttention'] ?? null) ? $surface['resultAttention'] : []; $primaryNextTarget = is_string($decision['primary_next_target'] ?? null) ? $decision['primary_next_target'] : null;
$attentionSpec = \App\Support\Badges\BadgeRenderer::spec(
\App\Support\Badges\BadgeDomain::RestoreResultStatus,
$resultAttention['state'] ?? ($decision['state'] ?? 'not_executed')
);
$summaryCards = [ $summaryCards = [
'requested' => 'Requested', 'requested' => 'Requested',
'applied' => 'Applied', 'applied' => 'Applied',
'failed' => 'Failed', 'failed' => 'Failed',
'skipped' => 'Skipped',
'needs_review' => 'Needs review', 'needs_review' => 'Needs review',
]; ];
$formatCount = static fn (mixed $value): string => is_numeric($value) ? number_format((int) $value) : ''; $formatCount = static fn (mixed $value): string => is_numeric($value) ? number_format((int) $value) : 'Not recorded';
@endphp @endphp
<div data-testid="restore-run-detail-surface" class="space-y-6"> <div data-testid="restore-run-detail-surface" class="space-y-6">
@ -46,9 +43,6 @@ class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-whi
{{ $decision['status_label'] ?? 'Restore result unavailable' }} {{ $decision['status_label'] ?? 'Restore result unavailable' }}
</x-filament::badge> </x-filament::badge>
<x-filament::badge :color="$attentionSpec->color" :icon="$attentionSpec->icon" :icon-color="$attentionSpec->iconColor" size="sm">
{{ $attentionSpec->label }}
</x-filament::badge>
</div> </div>
<div class="mt-4 grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem]"> <div class="mt-4 grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem]">
@ -73,39 +67,54 @@ class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-whi
<p class="mt-1 text-sm text-slate-800 dark:text-slate-100">{{ $decision['impact'] ?? 'Review restore details before relying on this record.' }}</p> <p class="mt-1 text-sm text-slate-800 dark:text-slate-100">{{ $decision['impact'] ?? 'Review restore details before relying on this record.' }}</p>
</div> </div>
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5"> <div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Primary next action</p> <p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Trust boundary</p>
<p class="mt-1 text-sm font-semibold text-slate-900 dark:text-white">{{ $decision['primary_next_action'] ?? 'Review restore details' }}</p> <p class="mt-1 text-sm text-slate-800 dark:text-slate-100">{{ $decision['recovery_claim_boundary'] ?? 'Target environment recovery is not proven.' }}</p>
</div> </div>
</div> </div>
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5">
<div class="flex flex-wrap items-center gap-2">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Restore readiness</p>
<x-filament::badge :color="$readinessGuidance['tone'] ?? 'gray'" size="sm">
{{ $readinessGuidance['stateLabel'] ?? 'Restore readiness unavailable.' }}
</x-filament::badge>
</div>
<p class="mt-2 text-sm text-slate-800 dark:text-slate-100">{{ $readinessGuidance['reasonSummary'] ?? 'This guidance is based on the current restore scope and preview state.' }}</p>
<p class="mt-2 text-xs font-semibold text-slate-700 dark:text-slate-200">Next safe action: {{ $readinessGuidance['nextActionLabel'] ?? 'Review restore details' }}</p>
<p class="mt-1 text-xs text-slate-600 dark:text-slate-300">{{ $readinessGuidance['actionSafetyCopy'] ?? 'This guidance does not execute the restore.' }}</p>
</div>
</div> </div>
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5"> <div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Dominant action</p> <p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Next action</p>
@if (filled($decision['primary_next_url'] ?? null)) @if (filled($decision['primary_next_url'] ?? null))
<x-filament::button tag="a" :href="$decision['primary_next_url']" size="sm" class="mt-2 w-full justify-center"> <x-filament::button tag="a" :href="$decision['primary_next_url']" size="sm" class="mt-2 w-full justify-center">
{{ $decision['primary_next_action'] ?? 'Open detail' }} {{ $decision['primary_next_action'] ?? 'Open detail' }}
</x-filament::button> </x-filament::button>
@elseif (filled($primaryNextTarget))
<x-filament::button
type="button"
color="gray"
size="sm"
class="mt-2 w-full justify-center"
data-next-action-target="{{ $primaryNextTarget }}"
x-on:click="
const targetId = $el.getAttribute('data-next-action-target');
const target = targetId ? document.getElementById(targetId) : null;
if (target) {
if (target.tagName.toLowerCase() === 'details') {
target.open = true;
}
target.scrollIntoView({ block: 'start', behavior: 'smooth' });
const focusTarget = target.querySelector('summary, button, a, [tabindex]');
if (focusTarget) {
focusTarget.focus({ preventScroll: true });
}
}
"
>
{{ $decision['primary_next_action'] ?? 'Review restore details' }}
</x-filament::button>
@else @else
<div class="mt-2 rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-semibold text-slate-900 dark:border-white/10 dark:bg-gray-950 dark:text-white"> <div class="mt-2 rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-semibold text-slate-900 dark:border-white/10 dark:bg-gray-950 dark:text-white">
{{ $decision['primary_next_action'] ?? 'Review restore details' }} {{ $decision['primary_next_action'] ?? 'Review restore details' }}
</div> </div>
@endif @endif
<div class="mt-3 space-y-2 text-xs text-slate-600 dark:text-slate-300"> <div class="mt-3 space-y-2 text-xs text-slate-600 dark:text-slate-300">
<p><span class="font-semibold">Result next step:</span> {{ $decision['result_next_action'] ?? 'Review the completed restore details.' }}</p> @if (filled($decision['follow_up_summary'] ?? null))
<p><span class="font-semibold">Main follow-up driver:</span> {{ $decision['primary_cause_family'] ?? 'No dominant cause recorded' }}</p> <p><span class="font-semibold">Follow-up:</span> {{ $decision['follow_up_summary'] }}</p>
<p><span class="font-semibold">Boundary:</span> {{ $decision['recovery_claim_boundary'] ?? 'Target environment recovery is not proven.' }}</p> @endif
@if (filled($decision['primary_cause_family'] ?? null))
<p><span class="font-semibold">Primary driver:</span> {{ $decision['primary_cause_family'] }}</p>
@endif
</div> </div>
</div> </div>
</div> </div>
@ -122,7 +131,7 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
<h3 class="text-base font-semibold text-slate-950 dark:text-white">Restore result summary</h3> <h3 class="text-base font-semibold text-slate-950 dark:text-white">Restore result summary</h3>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">{{ $resultSummary['message'] ?? 'Result summary unavailable' }}</p> <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">{{ $resultSummary['message'] ?? 'Result summary unavailable' }}</p>
</div> </div>
<x-filament::badge color="gray" size="sm">Repo-backed</x-filament::badge> <x-filament::badge color="gray" size="sm">{{ $resultSummary['source_label'] ?? 'Recorded' }}</x-filament::badge>
</div> </div>
@if (($resultSummary['available'] ?? false) === true) @if (($resultSummary['available'] ?? false) === true)
@ -136,16 +145,16 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
</div> </div>
@else @else
<div class="mt-4 rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200"> <div class="mt-4 rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200">
Result summary unavailable. No fake zero counts are shown. Result counts are not recorded for this run. No fake zero counts are shown.
</div> </div>
@endif @endif
</section> </section>
<section data-testid="restore-run-item-outcomes" class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-gray-900"> <section id="restore-run-item-outcomes" data-testid="restore-run-item-outcomes" class="scroll-mt-24 rounded-xl border border-slate-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-gray-900">
<div class="flex items-start justify-between gap-4"> <div class="flex items-start justify-between gap-4">
<div> <div>
<h3 class="text-base font-semibold text-slate-950 dark:text-white">Item outcomes</h3> <h3 class="text-base font-semibold text-slate-950 dark:text-white">Item outcomes</h3>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Per-item results are table-first. Item diagnostics stay behind row disclosure.</p> <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">Recorded restore item outcomes. Row diagnostics remain secondary when present.</p>
</div> </div>
<x-filament::badge :color="$itemOutcomeEvidence['tone'] ?? 'gray'" size="sm"> <x-filament::badge :color="$itemOutcomeEvidence['tone'] ?? 'gray'" size="sm">
{{ $itemOutcomeEvidence['label'] ?? ($itemOutcomes->count().' items') }} {{ $itemOutcomeEvidence['label'] ?? ($itemOutcomes->count().' items') }}
@ -170,7 +179,7 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-slate-200 bg-white dark:divide-white/10 dark:bg-gray-900"> <tbody class="divide-y divide-slate-200 bg-white dark:divide-white/10 dark:bg-gray-900">
@foreach ($itemOutcomes as $item) @foreach ($visibleItemOutcomes as $item)
@php @php
$statusBadge = is_array($item['status_badge'] ?? null) ? $item['status_badge'] : []; $statusBadge = is_array($item['status_badge'] ?? null) ? $item['status_badge'] : [];
$diagnostics = collect(is_array($item['diagnostics'] ?? null) ? $item['diagnostics'] : []); $diagnostics = collect(is_array($item['diagnostics'] ?? null) ? $item['diagnostics'] : []);
@ -214,6 +223,12 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
</table> </table>
</div> </div>
</div> </div>
@if ($hiddenItemOutcomeCount > 0)
<div class="mt-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200">
Showing the first 8 item outcomes for receipt review. {{ $hiddenItemOutcomeCount }} additional {{ \Illuminate\Support\Str::plural('item', $hiddenItemOutcomeCount) }} stay in internal detail.
</div>
@endif
@endif @endif
</section> </section>
@ -231,7 +246,7 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-slate-200 bg-white dark:divide-white/10 dark:bg-gray-900"> <tbody class="divide-y divide-slate-200 bg-white dark:divide-white/10 dark:bg-gray-900">
@foreach ($foundationOutcomes as $foundation) @foreach ($visibleFoundationOutcomes as $foundation)
@php @php
$decisionBadge = is_array($foundation['decision_badge'] ?? null) ? $foundation['decision_badge'] : []; $decisionBadge = is_array($foundation['decision_badge'] ?? null) ? $foundation['decision_badge'] : [];
@endphp @endphp
@ -257,13 +272,21 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
</tbody> </tbody>
</table> </table>
</div> </div>
@if ($hiddenFoundationOutcomeCount > 0)
<div class="mt-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200">
Showing the first 8 foundation outcomes for receipt review. {{ $hiddenFoundationOutcomeCount }} additional {{ \Illuminate\Support\Str::plural('foundation', $hiddenFoundationOutcomeCount) }} stay in internal detail.
</div>
@endif
</section> </section>
@endif @endif
</main> </main>
<aside data-testid="restore-run-proof-panel" class="space-y-4"> <aside data-testid="restore-run-proof-panel" class="space-y-4">
<section class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"> <details id="restore-run-technical-details" data-testid="restore-run-technical-details" class="scroll-mt-24 rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900">
<h3 class="text-sm font-semibold text-slate-950 dark:text-white">Proof panel</h3> <summary class="cursor-pointer text-sm font-semibold text-slate-950 dark:text-white">
View technical run details
</summary>
<div class="mt-4 space-y-3"> <div class="mt-4 space-y-3">
@php @php
@ -273,6 +296,18 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
$evidenceCompletenessBadge = is_array($postRunEvidence['completeness_badge'] ?? null) ? $postRunEvidence['completeness_badge'] : null; $evidenceCompletenessBadge = is_array($postRunEvidence['completeness_badge'] ?? null) ? $postRunEvidence['completeness_badge'] : null;
@endphp @endphp
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5">
<div class="flex flex-wrap items-center gap-2">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">Restore readiness detail</p>
<x-filament::badge :color="$readinessGuidance['tone'] ?? 'gray'" size="sm">
{{ $readinessGuidance['stateLabel'] ?? 'Restore readiness unavailable.' }}
</x-filament::badge>
</div>
<p class="mt-2 text-sm text-slate-800 dark:text-slate-100">{{ $readinessGuidance['reasonSummary'] ?? 'This guidance is based on the current restore scope and preview state.' }}</p>
<p class="mt-2 text-xs font-semibold text-slate-700 dark:text-slate-200">Next safe action: {{ $readinessGuidance['nextActionLabel'] ?? 'Review restore details' }}</p>
<p class="mt-1 text-xs text-slate-600 dark:text-slate-300">{{ $readinessGuidance['actionSafetyCopy'] ?? 'This guidance does not execute the restore.' }}</p>
</div>
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5"> <div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-white/10 dark:bg-white/5">
<div> <div>
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">OperationRun proof</p> <p class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">OperationRun proof</p>
@ -342,14 +377,22 @@ class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]"
@endif @endif
</div> </div>
@if (filled($postRunEvidence['guidance'] ?? null))
<p class="mt-3 text-sm text-slate-700 dark:text-slate-200">{{ $postRunEvidence['guidance'] }}</p>
@endif
@if (filled($postRunEvidence['url'] ?? null)) @if (filled($postRunEvidence['url'] ?? null))
<x-filament::button tag="a" :href="$postRunEvidence['url']" size="xs" color="gray" class="mt-3"> <x-filament::button tag="a" :href="$postRunEvidence['url']" size="xs" color="gray" class="mt-3">
Open evidence Open evidence
</x-filament::button> </x-filament::button>
@elseif (filled($postRunEvidence['next_action_url'] ?? null))
<x-filament::button tag="a" :href="$postRunEvidence['next_action_url']" size="xs" color="gray" class="mt-3">
{{ $postRunEvidence['next_action_label'] ?? 'Open evidence snapshots' }}
</x-filament::button>
@endif @endif
</div> </div>
</div> </div>
</section> </details>
<section class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"> <section class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900">
<h3 class="text-sm font-semibold text-slate-950 dark:text-white">Run context</h3> <h3 class="text-sm font-semibold text-slate-950 dark:text-white">Run context</h3>

View File

@ -19,6 +19,9 @@
$containerClasses = $embedded $containerClasses = $embedded
? 'shrink-0 whitespace-nowrap border-l border-gray-200 dark:border-white/10' ? 'shrink-0 whitespace-nowrap border-l border-gray-200 dark:border-white/10'
: 'inline-flex shrink-0 whitespace-nowrap'; : 'inline-flex shrink-0 whitespace-nowrap';
$triggerStyle = $embedded
? 'border-top-left-radius:0;border-bottom-left-radius:0'
: null;
@endphp @endphp
<div class="{{ $containerClasses }}"> <div class="{{ $containerClasses }}">
@ -33,6 +36,7 @@
data-testid="tenantpilot-locale-switcher-trigger" data-testid="tenantpilot-locale-switcher-trigger"
aria-label="{{ __('localization.shell.language') }}" aria-label="{{ __('localization.shell.language') }}"
class="shrink-0" class="shrink-0"
style="{{ $triggerStyle }}"
> >
{{ strtoupper($currentLocale) }} {{ strtoupper($currentLocale) }}
</x-filament::button> </x-filament::button>

View File

@ -54,7 +54,8 @@
->assertSee('Stored report') ->assertSee('Stored report')
->assertSee('Missing permissions') ->assertSee('Missing permissions')
->assertSee('DeviceManagementApps.ReadWrite.All') ->assertSee('DeviceManagementApps.ReadWrite.All')
->assertSee('Raw payload') ->assertSee('Internal payload details')
->assertDontSee('Raw payload')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs(); ->assertNoConsoleLogs();
}); });

View File

@ -89,7 +89,8 @@
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin')) visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
->waitForText('Outcome summary') ->waitForText('Outcome summary')
->assertSee('Evidence snapshot #'.$snapshot->getKey()) ->assertSee('Evidence basis and readiness')
->assertSee('Evidence coverage summary')
->assertScript("window.location.pathname.includes('/admin/workspaces/{$workspace->getRouteKey()}/environments/{$tenant->getRouteKey()}/evidence/{$snapshot->getRouteKey()}')", true) ->assertScript("window.location.pathname.includes('/admin/workspaces/{$workspace->getRouteKey()}/environments/{$tenant->getRouteKey()}/evidence/{$snapshot->getRouteKey()}')", true)
->assertScript("!window.location.pathname.includes('/admin/t/')", true) ->assertScript("!window.location.pathname.includes('/admin/t/')", true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
@ -105,9 +106,10 @@
->click('Permission posture report') ->click('Permission posture report')
->waitForText('Permission posture summary') ->waitForText('Permission posture summary')
->assertSee('Stored report') ->assertSee('Stored report')
->assertSee('Raw payload') ->assertSee('Internal payload details')
->assertDontSee('Raw payload')
->assertScript("window.location.pathname.includes('/admin/workspaces/{$workspace->getRouteKey()}/environments/{$tenant->getRouteKey()}/stored-reports/{$report->getRouteKey()}')", true) ->assertScript("window.location.pathname.includes('/admin/workspaces/{$workspace->getRouteKey()}/environments/{$tenant->getRouteKey()}/stored-reports/{$report->getRouteKey()}')", true)
->assertScript("!window.location.pathname.includes('/admin/t/')", true) ->assertScript("!window.location.pathname.includes('/admin/t/')", true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs(); ->assertNoConsoleLogs();
}); });

View File

@ -61,15 +61,14 @@
->assertNoConsoleLogs(); ->assertNoConsoleLogs();
visit(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant)) visit(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant))
->waitForText('Artifact source') ->waitForText('Internal report artifact')
->assertSee('Provider report type') ->assertDontSee('Provider report type')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs(); ->assertNoConsoleLogs();
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant)) visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant))
->waitForText('Evidence dimensions') ->waitForText('Internal evidence dimensions')
->assertSee('Source family') ->assertDontSee('Artifact source')
->assertSee('Provider source detail')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs(); ->assertNoConsoleLogs();

View File

@ -144,12 +144,14 @@ function spec335BrowserCompletedRestoreRun(ManagedEnvironment $tenant, BackupSet
$page = visit(spec335BrowserLoginUrl($user, $tenant, spec335BrowserViewPath($tenant, $draftRun))) $page = visit(spec335BrowserLoginUrl($user, $tenant, spec335BrowserViewPath($tenant, $draftRun)))
->resize(1440, 1100) ->resize(1440, 1100)
->waitForText('Was this restore executed safely, and is recovery proof available?') ->waitForText('Was this restore executed safely, and is recovery proof available?')
->assertSee('Not executed') ->assertSee('Not configured')
->assertSee('Restore readiness') ->assertSee('View technical run details')
->assertSee("Restore can't continue yet.") ->assertDontSee('Restore readiness detail')
->assertSee('Run readiness checks') ->assertDontSee("Restore can't continue yet.")
->assertSee('Operation proof unavailable') ->assertDontSee('Run readiness checks')
->assertSee('Post-run evidence unavailable') ->assertDontSee('Operation proof unavailable')
->assertDontSee('Post-run evidence unavailable')
->assertScript('document.querySelector("[data-testid=\"restore-run-proof-panel\"] details")?.open === false', true)
->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true) ->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()
@ -157,21 +159,35 @@ function spec335BrowserCompletedRestoreRun(ManagedEnvironment $tenant, BackupSet
$page = visit(spec335BrowserViewPath($tenant, $completedRun)) $page = visit(spec335BrowserViewPath($tenant, $completedRun))
->resize(1440, 1100) ->resize(1440, 1100)
->waitForText('Completed, recovery proof incomplete') ->waitForText('Needs attention')
->assertSee('Restore completed.') ->assertSee('View technical run details')
->assertSee('Inspect restore results') ->assertSee('Confirm operation proof and capture post-run evidence before closing this as recovered.')
->assertSee('This guidance is inspection-only for an existing restore record.') ->assertDontSee('Restore completed.')
->assertSee('Operation proof available') ->assertDontSee('Inspect restore results')
->assertSee('Post-run evidence unavailable') ->assertDontSee('This guidance is inspection-only for an existing restore record.')
->assertDontSee('Operation proof available')
->assertDontSee('Post-run evidence unavailable')
->assertSee('Restore result summary') ->assertSee('Restore result summary')
->assertSee('Item outcomes') ->assertSee('Item outcomes')
->assertSee('Spec335 Browser Policy') ->assertSee('Spec335 Browser Policy')
->assertDontSee('browser raw payload should stay hidden') ->assertDontSee('browser raw payload should stay hidden')
->assertDontSee('Open evidence snapshots')
->assertScript('document.getElementById("restore-run-technical-details")?.open === false', true)
->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true) ->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()
->screenshot(true, spec335BrowserScreenshotName('02-restore-run-completed-proof-incomplete')); ->screenshot(true, spec335BrowserScreenshotName('02-restore-run-completed-proof-incomplete'));
$page = $page
->click('[data-next-action-target="restore-run-technical-details"]')
->assertScript('document.getElementById("restore-run-technical-details")?.open === true', true)
->assertSee('Operation proof available')
->assertSee('Post-run evidence unavailable')
->assertSee('Open Evidence snapshots and use Create snapshot for this environment. Do not close the restore as recovered until the post-run snapshot is available.')
->assertSee('Open evidence snapshots')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
$page->screenshot(true, spec335BrowserScreenshotName('03-restore-run-operation-proof')); $page->screenshot(true, spec335BrowserScreenshotName('03-restore-run-operation-proof'));
$page->screenshot(true, spec335BrowserScreenshotName('04-restore-run-evidence-unavailable')); $page->screenshot(true, spec335BrowserScreenshotName('04-restore-run-evidence-unavailable'));
@ -182,10 +198,10 @@ function spec335BrowserCompletedRestoreRun(ManagedEnvironment $tenant, BackupSet
visit(spec335BrowserViewPath($tenant, $failedRun)) visit(spec335BrowserViewPath($tenant, $failedRun))
->resize(1440, 1100) ->resize(1440, 1100)
->waitForText('Restore failed') ->waitForText('Failed')
->assertSee('Inspect restore results')
->assertSee('This restore did not complete successfully; inspect failure details and evidence.')
->assertSee('Review failure details') ->assertSee('Review failure details')
->assertDontSee('Inspect restore results')
->assertDontSee('This restore did not complete successfully; inspect failure details and evidence.')
->assertSee('Spec335 Failed Browser Policy') ->assertSee('Spec335 Failed Browser Policy')
->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true) ->assertScript('document.querySelector("[data-testid=\"restore-run-diagnostics\"]")?.open === false', true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
@ -195,8 +211,8 @@ function spec335BrowserCompletedRestoreRun(ManagedEnvironment $tenant, BackupSet
visit(spec335BrowserLoginUrl($user, $tenant, spec335BrowserViewPath($tenant, $completedRun))) visit(spec335BrowserLoginUrl($user, $tenant, spec335BrowserViewPath($tenant, $completedRun)))
->inDarkMode() ->inDarkMode()
->resize(1440, 1100) ->resize(1440, 1100)
->waitForText('Completed, recovery proof incomplete') ->waitForText('Needs attention')
->assertSee('Operation proof available') ->assertDontSee('Operation proof available')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()
->screenshot(true, spec335BrowserScreenshotName('08-restore-run-dark-mode')); ->screenshot(true, spec335BrowserScreenshotName('08-restore-run-dark-mode'));

View File

@ -88,8 +88,8 @@
->waitForText('Outcome summary') ->waitForText('Outcome summary')
->assertSee('Report scope and readiness') ->assertSee('Report scope and readiness')
->assertSee('Permission posture summary') ->assertSee('Permission posture summary')
->assertSee('Technical report details') ->assertSee('Internal report artifact')
->assertScript("document.body.textContent.indexOf('Permission posture summary') < document.body.textContent.indexOf('Technical report details')", true) ->assertScript("document.body.textContent.indexOf('Permission posture summary') < document.body.textContent.indexOf('Internal report artifact')", true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()
->screenshot(true, '004-stored-report-view-after'); ->screenshot(true, '004-stored-report-view-after');
@ -101,7 +101,7 @@
->assertSee('Evidence basis and readiness') ->assertSee('Evidence basis and readiness')
->assertSee('Related review and report context') ->assertSee('Related review and report context')
->assertSee('Technical evidence details') ->assertSee('Technical evidence details')
->assertSee('Evidence dimensions') ->assertSee('Internal evidence dimensions')
->assertScript("document.body.textContent.indexOf('Related review and report context') < document.body.textContent.indexOf('Technical evidence details')", true) ->assertScript("document.body.textContent.indexOf('Related review and report context') < document.body.textContent.indexOf('Technical evidence details')", true)
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()

View File

@ -44,7 +44,7 @@
->resize(1440, 1100) ->resize(1440, 1100)
->waitForText('Outcome summary') ->waitForText('Outcome summary')
->assertSee('Evidence basis and readiness') ->assertSee('Evidence basis and readiness')
->assertSee('Evidence dimensions') ->assertSee('Internal evidence dimensions')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()
->screenshot(true, spec376BrowserScreenshot('001-evidence-snapshot-view')); ->screenshot(true, spec376BrowserScreenshot('001-evidence-snapshot-view'));

View File

@ -0,0 +1,391 @@
<?php
declare(strict_types=1);
use App\Filament\Resources\BaselineSnapshotResource;
use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\StoredReportResource;
use App\Models\BackupSet;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineSnapshotItem;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\RestoreRun;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\User;
use App\Support\Baselines\BaselineSubjectKey;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\RestoreRunStatus;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
uses(RefreshDatabase::class);
pest()->browser()->timeout(60_000);
beforeEach(function (): void {
Storage::fake('exports');
});
function spec397BrowserScreenshotName(string $name): string
{
return 'spec397-receipt-page-reduction-'.$name;
}
function spec397CopyBrowserScreenshot(string $name): void
{
$filename = spec397BrowserScreenshotName($name).'.png';
$source = base_path('tests/Browser/Screenshots/'.$filename);
$targetDirectory = repo_path('specs/397-receipt-page-reduction/artifacts/screenshots');
if (! is_dir($targetDirectory)) {
@mkdir($targetDirectory, 0755, true);
}
if (! is_file($source)) {
$source = \Pest\Browser\Support\Screenshot::path($filename);
}
for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) {
usleep(100_000);
clearstatcache(true, $source);
}
if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) {
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png');
}
}
/**
* @return array{snapshot: BaselineSnapshot, profile: BaselineProfile}
*/
function spec397BrowserBaselineSnapshot(ManagedEnvironment $tenant): array
{
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec397 Browser Baseline',
]);
$policyTypeCounts = [];
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
'summary_jsonb' => [
'total_items' => 10,
'fidelity_counts' => ['content' => 10, 'meta' => 0],
'gaps' => ['count' => 0, 'by_reason' => []],
],
]);
foreach (range(1, 10) as $index) {
$policyType = sprintf('spec397BrowserPolicyType%02d', $index);
$policyTypeCounts[$policyType] = 1;
$subjectKey = baselineProviderResourceSubjectKeyForTest($policyType, sprintf('spec397-browser-policy-%02d', $index));
BaselineSnapshotItem::factory()->create([
'baseline_snapshot_id' => (int) $snapshot->getKey(),
'policy_type' => $policyType,
'subject_key' => $subjectKey,
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, $subjectKey),
'meta_jsonb' => [
'display_name' => sprintf('Spec397 Browser Policy %02d', $index),
'evidence' => [
'fidelity' => 'content',
'source' => 'policy_version',
'observed_at' => now()->toIso8601String(),
],
],
]);
}
$snapshot->forceFill([
'summary_jsonb' => array_merge($snapshot->summary_jsonb, [
'policy_type_counts' => $policyTypeCounts,
]),
])->save();
return ['snapshot' => $snapshot->refresh(), 'profile' => $profile];
}
function spec397BrowserRestoreRun(ManagedEnvironment $tenant): RestoreRun
{
$backupSet = BackupSet::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'name' => 'Spec397 Browser Backup',
'status' => 'completed',
'item_count' => 10,
]);
$operationRun = OperationRun::factory()->forTenant($tenant)->create([
'type' => OperationRunType::RestoreExecute->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'completed_at' => now(),
]);
$items = [];
foreach (range(1, 10) as $index) {
$items[] = [
'status' => 'applied',
'policy_identifier' => sprintf('Spec397 Restore Policy %02d', $index),
'policy_type' => 'settingsCatalogPolicy',
'platform' => 'windows',
'assignment_summary' => [
'success' => 1,
'failed' => 0,
'skipped' => 0,
],
'settings_apply' => [
'applied' => 1,
'failed' => 0,
'manual_required' => 0,
],
'graph_error_message' => sprintf('spec397 raw restore payload %02d', $index),
];
}
return RestoreRun::factory()->for($tenant, 'tenant')->for($backupSet)->create([
'status' => 'completed',
'operation_run_id' => (int) $operationRun->getKey(),
'requested_by' => 'Spec397 Browser Operator',
'results' => [
'foundations' => [],
'items' => $items,
],
'metadata' => [
'total' => 10,
'succeeded' => 10,
'failed' => 0,
'skipped' => 0,
'partial' => 0,
'non_applied' => 0,
],
'completed_at' => now(),
]);
}
function spec397BrowserRunningRestoreRun(ManagedEnvironment $tenant): RestoreRun
{
$backupSet = BackupSet::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'name' => 'Spec397 Browser Running Backup',
'status' => 'completed',
'item_count' => 2,
]);
$operationRun = OperationRun::factory()->forTenant($tenant)->create([
'type' => OperationRunType::RestoreExecute->value,
'status' => OperationRunStatus::Running->value,
'outcome' => OperationRunOutcome::Pending->value,
'started_at' => now(),
'completed_at' => null,
]);
return RestoreRun::factory()->for($tenant, 'tenant')->for($backupSet)->create([
'status' => RestoreRunStatus::Running->value,
'operation_run_id' => (int) $operationRun->getKey(),
'requested_by' => 'Spec397 Browser Operator',
'results' => [
'foundations' => [],
'items' => [],
],
'metadata' => [],
'started_at' => now(),
'completed_at' => null,
]);
}
function spec397BrowserReviewPack(ManagedEnvironment $tenant, User $user, EvidenceSnapshot $snapshot): ReviewPack
{
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
$review = markEnvironmentReviewCustomerSafeReady($review);
Storage::disk('exports')->put('review-packs/spec397-browser-ready.zip', 'PK-spec397-browser');
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'environment_review_id' => (int) $review->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'file_path' => 'review-packs/spec397-browser-ready.zip',
'file_disk' => 'exports',
'options' => [
'include_pii' => false,
'include_operations' => true,
],
'summary' => array_replace_recursive(is_array($review->summary) ? $review->summary : [], [
'finding_count' => 1,
'report_count' => 2,
'operation_count' => 1,
'evidence_resolution' => [
'outcome' => 'resolved',
'snapshot_id' => (int) $snapshot->getKey(),
'snapshot_fingerprint' => (string) $snapshot->fingerprint,
],
]),
]);
$review->forceFill([
'current_export_review_pack_id' => (int) $pack->getKey(),
])->save();
return $pack->refresh();
}
function spec397BrowserStoredReport(ManagedEnvironment $tenant): StoredReport
{
return StoredReport::factory()
->permissionPosture([
'posture_score' => 88,
'required_count' => 4,
'granted_count' => 3,
'permissions' => [
['key' => 'DeviceManagementConfiguration.Read.All', 'status' => 'granted'],
['key' => 'DeviceManagementApps.ReadWrite.All', 'status' => 'missing'],
],
])
->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'fingerprint' => 'spec397-browser-report-fingerprint',
]);
}
it('Spec397 smokes reduced receipt defaults across changed detail surfaces', function (): void {
[$user, $tenant] = createUserWithTenant(
role: 'owner',
workspaceRole: 'manager',
ensureDefaultMicrosoftProviderConnection: false,
);
$evidenceSnapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0);
['snapshot' => $baselineSnapshot] = spec397BrowserBaselineSnapshot($tenant);
$restoreRun = spec397BrowserRestoreRun($tenant);
$runningRestoreRun = spec397BrowserRunningRestoreRun($tenant);
$reviewPack = spec397BrowserReviewPack($tenant, $user, $evidenceSnapshot);
$storedReport = spec397BrowserStoredReport($tenant);
$this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(),
],
]);
$page = visit(EvidenceSnapshotResource::getUrl('view', ['record' => $evidenceSnapshot], tenant: $tenant, panel: 'admin'))
->resize(1440, 1100)
->waitForText('Outcome summary')
->assertSee('Evidence basis and readiness')
->assertSee('Evidence coverage summary')
->assertSee('Internal evidence dimensions')
->assertDontSee('Artifact source')
->assertScript("document.body.textContent.indexOf('Evidence coverage summary') < document.body.textContent.indexOf('Internal evidence dimensions')", true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('01-evidence-snapshot-detail'));
spec397CopyBrowserScreenshot('01-evidence-snapshot-detail');
$page = visit(BaselineSnapshotResource::getUrl('view', ['record' => $baselineSnapshot], panel: 'admin'))
->resize(1440, 1100)
->waitForText('Outcome summary')
->assertSee('Coverage summary')
->assertSee('Captured governed subjects')
->assertSee('Showing the first 8 governed subjects for receipt review.')
->assertSee('2 additional subjects stay in the internal detail path.')
->assertDontSee('Spec397 Browser Policy 09')
->assertScript("document.body.textContent.indexOf('Coverage summary') < document.body.textContent.indexOf('Captured governed subjects')", true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('02-baseline-snapshot-detail'));
spec397CopyBrowserScreenshot('02-baseline-snapshot-detail');
$page = visit(RestoreRunResource::getUrl('view', ['record' => $restoreRun], tenant: $tenant, panel: 'admin'))
->resize(1440, 1100)
->waitForText('Needs attention')
->assertSee('Restore result summary')
->assertSee('View technical run details')
->assertSee('Confirm operation proof and capture post-run evidence before closing this as recovered.')
->assertSee('Spec397 Restore Policy 08')
->assertDontSee('Spec397 Restore Policy 09')
->assertDontSee('Operation proof available')
->assertDontSee('spec397 raw restore payload')
->assertScript('document.querySelector("[data-testid=\"restore-run-proof-panel\"] details")?.open === false', true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('03-restore-run-detail'));
spec397CopyBrowserScreenshot('03-restore-run-detail');
$page
->click('[data-next-action-target="restore-run-technical-details"]')
->assertScript('document.getElementById("restore-run-technical-details")?.open === true', true)
->assertSee('Post-run evidence unavailable')
->assertSee('Open Evidence snapshots and use Create snapshot for this environment. Do not close the restore as recovered until the post-run snapshot is available.')
->assertSee('Open evidence snapshots')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
$page = visit(RestoreRunResource::getUrl('view', ['record' => $runningRestoreRun], tenant: $tenant, panel: 'admin'))
->resize(1440, 1100)
->waitForText('Running')
->assertSee('Monitor restore progress')
->assertSee('View technical run details')
->assertDontSee('View operation progress')
->assertDontSee('Open operation')
->assertDontSee('Operation proof in progress')
->assertScript('document.querySelector("[data-testid=\"restore-run-proof-panel\"] details")?.open === false', true)
->assertScript('(() => { const surface = document.querySelector("[data-testid=\"restore-run-detail-surface\"]"); return !!surface && Array.from(surface.querySelectorAll("a[href*=\"operations\"]")).every((a) => a.closest("[data-testid=\"restore-run-proof-panel\"] details")?.open === false); })()', true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('03b-restore-run-running-detail'));
spec397CopyBrowserScreenshot('03b-restore-run-running-detail');
$page
->click('[data-next-action-target="restore-run-technical-details"]')
->assertScript('document.getElementById("restore-run-technical-details")?.open === true', true)
->assertSee('Operation proof in progress')
->assertSee('Open operation')
->assertSee('Wait until operation proof is completed, then capture a post-run evidence snapshot before closing this restore as recovered.')
->assertDontSee('Open evidence snapshots')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
$page = visit(ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $tenant, panel: 'admin'))
->resize(1440, 1100)
->waitForText('Outcome summary')
->assertSee('Output guidance')
->assertSee('Pack readiness and contents')
->assertSee('Evidence basis')
->assertSee('Evidence completeness')
->assertSee('Technical pack details')
->assertDontSee('Internal evidence details')
->assertDontSee('View internal evidence details')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('04-review-pack-detail'));
spec397CopyBrowserScreenshot('04-review-pack-detail');
$page = visit(StoredReportResource::getUrl('view', ['record' => $storedReport], tenant: $tenant, panel: 'admin'))
->resize(1440, 1100)
->waitForText('Outcome summary')
->assertSee('Report scope and readiness')
->assertSee('Permission posture summary')
->assertSee('Internal report artifact')
->assertSee('Internal payload details')
->assertDontSee('Raw payload')
->assertScript("document.body.textContent.indexOf('Permission posture summary') < document.body.textContent.indexOf('Internal report artifact')", true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec397BrowserScreenshotName('05-stored-report-detail'));
spec397CopyBrowserScreenshot('05-stored-report-detail');
});

View File

@ -444,6 +444,8 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
->assertSeeText('Review pack generation · Completed successfully') ->assertSeeText('Review pack generation · Completed successfully')
->assertSeeText('Backup schedule purge · Queued for execution') ->assertSeeText('Backup schedule purge · Queued for execution')
->assertDontSeeText('ManagedEnvironment.evidence.snapshot.generate · Pending · Running') ->assertDontSeeText('ManagedEnvironment.evidence.snapshot.generate · Pending · Running')
->assertSeeText('Internal evidence dimensions')
->assertDontSeeText('Artifact source')
->assertSeeText('Copy JSON'); ->assertSeeText('Copy JSON');
}); });
@ -483,7 +485,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
'interpretation_version' => 'compliance_evidence_mapping.v1', 'interpretation_version' => 'compliance_evidence_mapping.v1',
])) ]))
->assertOk() ->assertOk()
->assertSee('Evidence dimensions') ->assertSee('Internal evidence dimensions')
->assertDontSee('Open the latest evidence refresh operation.') ->assertDontSee('Open the latest evidence refresh operation.')
->assertDontSee('customer-proof-flow') ->assertDontSee('customer-proof-flow')
->assertDontSee('Raw summary JSON') ->assertDontSee('Raw summary JSON')

View File

@ -58,14 +58,16 @@
$this->actingAs($user) $this->actingAs($user)
->get(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant)) ->get(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant))
->assertOk() ->assertOk()
->assertSeeInOrder(['Outcome summary', 'Artifact source', 'Source family', 'Stored Report', 'Stored report', 'Provider report type']) ->assertSeeInOrder(['Outcome summary', 'Internal report artifact', 'Source family', 'Source kind', 'Source target'])
->assertSee('Provider')
->assertSee('Stored report')
->assertSee('Permission posture summary'); ->assertSee('Permission posture summary');
$this->actingAs($user) $this->actingAs($user)
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant)) ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant))
->assertOk() ->assertOk()
->assertSeeInOrder(['Evidence dimensions', 'Source family', 'Source kind', 'Source target']) ->assertSeeInOrder(['Internal evidence dimensions', 'Technical dimension details', 'Source family', 'Source kind', 'Source target'])
->assertSee('Artifact source') ->assertDontSee('Artifact source')
->assertSee('Provider source detail'); ->assertSee('Provider source detail');
$this->actingAs($user) $this->actingAs($user)

View File

@ -123,3 +123,61 @@
->assertSee(BaselineSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin')) ->assertSee(BaselineSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin'))
->assertDontSee('>View<', escape: false); ->assertDontSee('>View<', escape: false);
}); });
it('caps baseline snapshot receipt governed-subject rows before internal detail', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'readonly');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec397 Baseline',
]);
$policyTypeCounts = [];
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
'summary_jsonb' => [
'total_items' => 10,
'fidelity_counts' => ['content' => 10, 'meta' => 0],
'gaps' => ['count' => 0, 'by_reason' => []],
],
]);
foreach (range(1, 10) as $index) {
$policyType = sprintf('spec397PolicyType%02d', $index);
$policyTypeCounts[$policyType] = 1;
$subjectKey = baselineProviderResourceSubjectKeyForTest($policyType, sprintf('spec397-policy-%02d', $index));
BaselineSnapshotItem::factory()->create([
'baseline_snapshot_id' => (int) $snapshot->getKey(),
'policy_type' => $policyType,
'subject_key' => $subjectKey,
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, $subjectKey),
'meta_jsonb' => [
'display_name' => sprintf('Spec397 Policy %02d', $index),
'evidence' => [
'fidelity' => 'content',
'source' => 'policy_version',
'observed_at' => now()->toIso8601String(),
],
],
]);
}
$snapshot->forceFill([
'summary_jsonb' => array_merge($snapshot->summary_jsonb, [
'policy_type_counts' => $policyTypeCounts,
]),
])->save();
$response = $this->actingAs($user)
->get(BaselineSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin'));
$response
->assertOk()
->assertSee('Showing the first 8 governed subjects for receipt review.')
->assertSee('2 additional subjects stay in internal detail.');
expect(substr_count($response->getContent(), 'Showing the first 8 governed subjects'))->toBeGreaterThanOrEqual(2);
});

View File

@ -103,9 +103,10 @@
Filament::setTenant($tenant, true); Filament::setTenant($tenant, true);
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()]) Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Follow-up required') ->assertSee('Needs attention')
->assertSee('Review skipped or non-applied items before closing the run.') ->assertSee('The restore completed, but follow-up remains for skipped or non-applied work.')
->assertSee('No dominant cause recorded') ->assertSee('Confirm operation proof and capture post-run evidence before closing this as recovered.')
->assertDontSee('No dominant cause recorded')
->assertSee('Target environment recovery is not proven.') ->assertSee('Target environment recovery is not proven.')
->assertDontSee('review_skipped_items') ->assertDontSee('review_skipped_items')
->assertDontSee('run_completed_not_recovery_proven'); ->assertDontSee('run_completed_not_recovery_proven');

View File

@ -16,6 +16,7 @@
use App\Support\OperationRunOutcome; use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus; use App\Support\OperationRunStatus;
use App\Support\OperationRunType; use App\Support\OperationRunType;
use App\Support\RestoreRunStatus;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
@ -91,6 +92,33 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
]); ]);
} }
function spec335RunningOperationRun(ManagedEnvironment $tenant): OperationRun
{
return OperationRun::factory()->forTenant($tenant)->create([
'type' => OperationRunType::RestoreExecute->value,
'status' => OperationRunStatus::Running->value,
'outcome' => OperationRunOutcome::Pending->value,
'started_at' => now(),
'completed_at' => null,
]);
}
function spec335RunningRestoreRun(ManagedEnvironment $tenant, BackupSet $backupSet, OperationRun $operationRun): RestoreRun
{
return RestoreRun::factory()->for($tenant, 'tenant')->for($backupSet)->create([
'status' => RestoreRunStatus::Running->value,
'operation_run_id' => (int) $operationRun->getKey(),
'requested_by' => 'Spec335 Operator',
'results' => [
'foundations' => [],
'items' => [],
],
'metadata' => [],
'started_at' => now(),
'completed_at' => null,
]);
}
it('renders the decision-first restore run detail with proof incomplete boundaries', function (): void { it('renders the decision-first restore run detail with proof incomplete boundaries', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner'); [$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user); $this->actingAs($user);
@ -102,12 +130,18 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()]) Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Was this restore executed safely, and is recovery proof available?') ->assertSee('Was this restore executed safely, and is recovery proof available?')
->assertSee('Completed, recovery proof incomplete') ->assertSee('Needs attention')
->assertSee('Do not treat this restore as verified recovery until evidence has been reviewed.') ->assertSee('Do not treat this restore as verified recovery until evidence has been reviewed.')
->assertSee('Operation proof available') ->assertSee('Operation proof available')
->assertSee('Open operation proof') ->assertSee('View technical run details')
->assertSee('Confirm operation proof and capture post-run evidence before closing this as recovered.')
->assertSee('data-next-action-target="restore-run-technical-details"', false)
->assertDontSee('Open operation proof')
->assertSee(OperationRunLinks::identifier($operationRun)) ->assertSee(OperationRunLinks::identifier($operationRun))
->assertSee('Post-run evidence unavailable') ->assertSee('Post-run evidence unavailable')
->assertSee('Open Evidence snapshots and use Create snapshot for this environment. Do not close the restore as recovered until the post-run snapshot is available.')
->assertSee('Open evidence snapshots')
->assertSee(EvidenceSnapshotResource::getUrl('index', tenant: $tenant), false)
->assertSee('Restore result summary') ->assertSee('Restore result summary')
->assertSee('Requested') ->assertSee('Requested')
->assertSee('10') ->assertSee('10')
@ -123,6 +157,25 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
->assertDontSee('Customer-safe'); ->assertDontSee('Customer-safe');
}); });
it('keeps running restore run progress out of the primary action', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$operationRun = spec335RunningOperationRun($tenant);
$restoreRun = spec335RunningRestoreRun($tenant, spec335BackupSet($tenant), $operationRun);
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Running')
->assertSee('Monitor restore progress')
->assertSee('data-next-action-target="restore-run-technical-details"', false)
->assertDontSee('View operation progress')
->assertSee('View technical run details')
->assertSee(OperationRunLinks::identifier($operationRun))
->assertSee('Wait until operation proof is completed, then capture a post-run evidence snapshot before closing this restore as recovered.')
->assertDontSee('Open evidence snapshots');
});
it('explains metadata-only follow-up counts when item outcome rows are absent', function (): void { it('explains metadata-only follow-up counts when item outcome rows are absent', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner'); [$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user); $this->actingAs($user);
@ -146,6 +199,82 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
->assertDontSee('0 items'); ->assertDontSee('0 items');
}); });
it('caps restore item outcomes on the default receipt surface', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$restoreRun = spec335CompletedRestoreRun($tenant, spec335BackupSet($tenant), spec335RestoreOperationRun($tenant));
$items = [];
foreach (range(1, 10) as $index) {
$items[$index] = [
'status' => 'applied',
'policy_identifier' => sprintf('Spec397 Restore Policy %02d', $index),
'policy_type' => 'settingsCatalogPolicy',
'platform' => 'windows',
'assignment_summary' => [
'success' => 1,
'failed' => 0,
'skipped' => 0,
],
'settings_apply' => [
'applied' => 1,
'failed' => 0,
'manual_required' => 0,
],
];
}
$restoreRun->forceFill([
'results' => [
'foundations' => [],
'items' => $items,
],
])->save();
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Spec397 Restore Policy 01')
->assertSee('Spec397 Restore Policy 08')
->assertDontSee('Spec397 Restore Policy 09')
->assertSee('Showing the first 8 item outcomes for receipt review.')
->assertSee('2 additional items stay in internal detail.');
});
it('completes missing result counts from recorded item outcomes', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$restoreRun = spec335CompletedRestoreRun($tenant, spec335BackupSet($tenant), spec335RestoreOperationRun($tenant));
$restoreRun->forceFill([
'metadata' => [
'non_applied' => 0,
],
'results' => [
'foundations' => [],
'items' => [
[
'status' => 'applied',
'policy_identifier' => 'Spec335 Counted Policy',
'policy_type' => 'settingsCatalogPolicy',
'platform' => 'windows',
],
],
],
])->save();
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Counts completed from recorded item outcomes.')
->assertSee('Recorded counts')
->assertSee('Requested')
->assertSee('Applied')
->assertSee('Failed')
->assertSee('Needs review')
->assertSee('1');
});
it('links repo-backed post-run evidence when an authorized snapshot exists', function (): void { it('links repo-backed post-run evidence when an authorized snapshot exists', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner'); [$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user); $this->actingAs($user);
@ -166,8 +295,9 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
]); ]);
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()]) Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Completed with evidence available') ->assertSee('Ready')
->assertSee('Post-run evidence available') ->assertSee('Post-run evidence available')
->assertSee('Review this post-run snapshot before relying on the restore as recovery proof.')
->assertSee('Evidence snapshot #'.$snapshot->getKey()) ->assertSee('Evidence snapshot #'.$snapshot->getKey())
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant), false); ->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant), false);
}); });
@ -182,9 +312,11 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
); );
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()]) Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Completed, recovery proof incomplete') ->assertSee('Needs attention')
->assertSee('Operation proof unavailable') ->assertSee('Operation proof unavailable')
->assertSee('Post-run evidence unavailable') ->assertSee('Post-run evidence unavailable')
->assertSee('Post-run evidence requires linked operation proof first. Review operation proof before treating this restore as recovered.')
->assertDontSee('Open evidence snapshots')
->assertDontSee('Open operation'); ->assertDontSee('Open operation');
}); });
@ -210,6 +342,7 @@ function spec335CompletedRestoreRun(ManagedEnvironment $tenant, BackupSet $backu
Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()]) Livewire::test(ViewRestoreRun::class, ['record' => $restoreRun->getKey()])
->assertSee('Operation proof unavailable') ->assertSee('Operation proof unavailable')
->assertSee('Post-run evidence unavailable') ->assertSee('Post-run evidence unavailable')
->assertDontSee('Open evidence snapshots')
->assertDontSee(OperationRunLinks::tenantlessView($foreignOperationRun), false) ->assertDontSee(OperationRunLinks::tenantlessView($foreignOperationRun), false)
->assertDontSee(EvidenceSnapshotResource::getUrl('view', ['record' => $foreignSnapshot], tenant: $foreignTenant), false); ->assertDontSee(EvidenceSnapshotResource::getUrl('view', ['record' => $foreignSnapshot], tenant: $foreignTenant), false);
}); });

View File

@ -118,11 +118,11 @@
'Outcome summary', 'Outcome summary',
'Report scope and readiness', 'Report scope and readiness',
'Permission posture summary', 'Permission posture summary',
'Technical report details', 'Internal report artifact',
'Raw payload', 'Internal payload details',
]); ]);
expect(spec372ContentBetween($content, 'Report scope and readiness', 'Technical report details')) expect(spec372ContentBetween($content, 'Report scope and readiness', 'Internal report artifact'))
->not->toContain('Source family') ->not->toContain('Source family')
->not->toContain('Integrity anchor') ->not->toContain('Integrity anchor')
->not->toContain('spec372-stored-report-fingerprint'); ->not->toContain('spec372-stored-report-fingerprint');
@ -147,7 +147,7 @@
'Evidence coverage summary', 'Evidence coverage summary',
'Related review and report context', 'Related review and report context',
'Technical evidence details', 'Technical evidence details',
'Evidence dimensions', 'Internal evidence dimensions',
]); ]);
expect(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all()) expect(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all())

View File

@ -32,6 +32,8 @@
->assertSee('admin/select-environment') ->assertSee('admin/select-environment')
->assertSee(__('localization.shell.clear_environment_scope')) ->assertSee(__('localization.shell.clear_environment_scope'))
->assertSee($tenant->name) ->assertSee($tenant->name)
->assertSee('data-testid="tenantpilot-locale-switcher-trigger"', false)
->assertSee('style="border-top-left-radius:0;border-bottom-left-radius:0', false)
->assertDontSee('MANAGED_ENVIRONMENT'); ->assertDontSee('MANAGED_ENVIRONMENT');
$content = $response->getContent(); $content = $response->getContent();

View File

@ -536,6 +536,10 @@ function createCurrentReviewPackForResourcePreview(ManagedEnvironment $tenant, \
->assertSee('Retention') ->assertSee('Retention')
->assertSee('Retained') ->assertSee('Retained')
->assertSee('Publishable') ->assertSee('Publishable')
->assertSee('Evidence basis')
->assertSee('Evidence completeness')
->assertSee('Internal evidence details')
->assertDontSee('View internal evidence details')
->assertSee('#'.$snapshot->getKey()) ->assertSee('#'.$snapshot->getKey())
->assertSee(OperationRunLinks::view($run, $tenant), false) ->assertSee(OperationRunLinks::view($run, $tenant), false)
->assertSee('resolved'); ->assertSee('resolved');
@ -703,7 +707,7 @@ function createCurrentReviewPackForResourcePreview(ManagedEnvironment $tenant, \
Livewire::actingAs($user) Livewire::actingAs($user)
->test(ViewReviewPack::class, ['record' => $pack->getKey()]) ->test(ViewReviewPack::class, ['record' => $pack->getKey()])
->assertSee('View internal evidence details') ->assertSee('Internal evidence details')
->assertActionVisible('open_rendered_report') ->assertActionVisible('open_rendered_report')
->assertActionVisible('download'); ->assertActionVisible('download');
}); });

View File

@ -76,8 +76,8 @@ function storedReportDetailHeaderActionNames(ViewStoredReport $page): array
'Missing permissions', 'Missing permissions',
'1', '1',
'DeviceManagementApps.ReadWrite.All', 'DeviceManagementApps.ReadWrite.All',
'Technical report details', 'Internal report artifact',
'Raw payload', 'Internal payload details',
]) ])
->assertSee('Current') ->assertSee('Current')
->assertSee('Measured at') ->assertSee('Measured at')
@ -146,7 +146,7 @@ function storedReportDetailHeaderActionNames(ViewStoredReport $page): array
'High-privilege assignments', 'High-privilege assignments',
'8', '8',
'Global Administrator assigned to Alice Admin (critical)', 'Global Administrator assigned to Alice Admin (critical)',
'Raw payload', 'Internal payload details',
]); ]);
$this->actingAs($user); $this->actingAs($user);

View File

@ -8,7 +8,7 @@ ## Summary
| --- | ---: | --- | | --- | ---: | --- |
| UI route/page inventory rows | 101 | Includes dynamic route families and utility/auth endpoints. | | UI route/page inventory rows | 101 | Includes dynamic route families and utility/auth endpoints. |
| Unique page reports | 22 | `page-reports/*.md`; some inventory rows intentionally share existing reports where routes resolve to the same surface. | | Unique page reports | 22 | `page-reports/*.md`; some inventory rows intentionally share existing reports where routes resolve to the same surface. |
| Desktop screenshots | 22 | Route-inventory-linked desktop evidence, including strategic runtime captures, blocker evidence screenshots, the Spec 366 rendered-report capture, and Spec 396 system-panel focused proof. | | Desktop screenshots | 22 | Route-inventory-linked desktop evidence, including strategic runtime captures, blocker evidence screenshots, the Spec 366 rendered-report capture, and Spec 396 system-panel focused proof. Spec 397 adds focused textual browser proof without durable screenshots. |
| Tablet screenshots | 0 | Deferred to later strategic mockup/implementation specs. | | Tablet screenshots | 0 | Deferred to later strategic mockup/implementation specs. |
| Mobile screenshots | 3 | Spec 366 adds mobile-ish rendered-report evidence for the customer technical profile; Spec 384 adds a narrow baseline subject resolution smoke capture; Spec 386 adds a narrow publication-resolution smoke capture. | | Mobile screenshots | 3 | Spec 366 adds mobile-ish rendered-report evidence for the customer technical profile; Spec 384 adds a narrow baseline subject resolution smoke capture; Spec 386 adds a narrow publication-resolution smoke capture. |
| Strategic Surface rows | 47 | Individual target treatment or explicit product decision required. | | Strategic Surface rows | 47 | Individual target treatment or explicit product decision required. |
@ -40,7 +40,7 @@ ## Spec 325 Target Image Coverage
| Governance Inbox | UI-028 | `target-experience-briefs/governance-inbox.md` | No | | Governance Inbox | UI-028 | `target-experience-briefs/governance-inbox.md` | No |
| Customer Review Workspace | UI-038 | `target-experience-briefs/customer-review-workspace.md` | No | | Customer Review Workspace | UI-038 | `target-experience-briefs/customer-review-workspace.md` | No |
| Audit Log | UI-025 | `target-experience-briefs/audit-log.md` | No | | Audit Log | UI-025 | `target-experience-briefs/audit-log.md` | No |
| Restore Safety Workflow | UI-053, UI-054 | `target-experience-briefs/restore-safety-workflow.md` | Partial - Spec 390 adds Restore-local readiness guidance on create/view surfaces; list and full browser fixture coverage remain separate. | | Restore Safety Workflow | UI-053, UI-054 | `target-experience-briefs/restore-safety-workflow.md` | Partial - Spec 390 adds Restore-local readiness guidance and Spec 397 verifies completed restore-run detail receipt reduction; list/create/failure/conflict coverage remains separate. |
| Provider Readiness | UI-072, UI-073 | `target-experience-briefs/provider-readiness.md` | No | | Provider Readiness | UI-072, UI-073 | `target-experience-briefs/provider-readiness.md` | No |
| Baseline Compare / Drift | UI-061 | `target-experience-briefs/baseline-compare-drift.md` | No | | Baseline Compare / Drift | UI-061 | `target-experience-briefs/baseline-compare-drift.md` | No |
@ -49,12 +49,12 @@ ## Coverage By Area
| Area | Rows | Coverage Notes | | Area | Rows | Coverage Notes |
| --- | ---: | --- | | --- | ---: | --- |
| Platform/system | 14 | Spec 396 adds focused browser proof for `/system`, `/system/login`, `/system/ops/runs`, and `/system/security/access-logs`; remaining directory, control, detail, and repair surfaces stay route-discovered or follow-up. | | Platform/system | 14 | Spec 396 adds focused browser proof for `/system`, `/system/login`, `/system/ops/runs`, and `/system/security/access-logs`; remaining directory, control, detail, and repair surfaces stay route-discovered or follow-up. |
| Governance | 13 | Strong browser coverage for inbox, decisions, exceptions, baselines; detail/diff routes remain unresolved; Spec 384 adds a feature-tested baseline subject resolution worklist. | | Governance | 13 | Strong browser coverage for inbox, decisions, exceptions, baselines; Spec 397 adds baseline snapshot detail receipt proof; baseline profile/detail and compare routes remain broader follow-up. |
| Monitoring | 9 | Operations hub and alert delivery landing captured; record details and config forms remain pattern/manual review. | | Monitoring | 9 | Operations hub and alert delivery landing captured; record details and config forms remain pattern/manual review. |
| Inventory | 8 | Route-discovered only; coverage, policy version detail, and raw-data exposure need later review. | | Inventory | 8 | Route-discovered only; coverage, policy version detail, and raw-data exposure need later review. |
| Evidence / audit | 8 | Audit log captured; evidence/report detail routes need customer-safe progressive-disclosure review. | | Evidence / audit | 8 | Audit log captured; Spec 397 adds receipt-reduction proof for Evidence Snapshot, Baseline Snapshot, and Stored Report detail surfaces while Evidence Overview remains follow-up. |
| Reviews | 8 | Review register, customer workspace, review pack detail, rendered-report, and the Spec 386 publication-resolution workflow now have bounded browser evidence; Spec 366 adds rendered-report profile, print, and mobile-ish captures while deeper evidence/report surfaces still remain open elsewhere. | | Reviews | 8 | Review register, customer workspace, review pack detail, rendered-report, and the Spec 386 publication-resolution workflow now have bounded browser evidence; Spec 397 reduces Review Pack receipt internals while deeper evidence/report surfaces still remain open elsewhere. |
| Backup / restore | 6 | High-risk area; Spec 371 adds seeded browser proof for Backup Sets list/detail. Spec 390 adds Restore create/view readiness guidance; Restore list and broader failure/conflict browser coverage remain unresolved. | | Backup / restore | 6 | High-risk area; Spec 371 adds seeded browser proof for Backup Sets list/detail. Spec 390 adds Restore create/view readiness guidance; Spec 397 verifies completed Restore Run detail receipt reduction while Restore list and broader failure/conflict browser coverage remain unresolved. |
| Settings / admin | 5 | Workspace and environment access are RBAC-sensitive and need later review. | | Settings / admin | 5 | Workspace and environment access are RBAC-sensitive and need later review. |
| Provider / integration | 5 | Provider connections and required permissions are captured; create/edit/onboarding remain high-risk unresolved surfaces. | | Provider / integration | 5 | Provider connections and required permissions are captured; create/edit/onboarding remain high-risk unresolved surfaces. |
| Findings | 5 | Queue/inbox patterns captured; finding detail needs individual triage target. | | Findings | 5 | Queue/inbox patterns captured; finding detail needs individual triage target. |

View File

@ -7,9 +7,9 @@ # UI-042 Review Pack Detail
| Area / scope | Reviews / environment artifact detail | | Area / scope | Reviews / environment artifact detail |
| Archetype | Evidence / Audit | | Archetype | Evidence / Audit |
| Design depth | Strategic Surface | | Design depth | Strategic Surface |
| Repo truth | repo-verified | | Repo truth | browser-verified in Specs 372 and 397 |
| Screenshot | `-` | | Screenshot | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png` |
| Browser status | Reached in the live in-app browser on 2026-06-05 via the Spec 351 review-output fixture; verified the preview-first action model, ZIP download secondary action, and customer-workspace return context. | | Browser status | Reached in the live in-app browser on 2026-06-05 via the Spec 351 review-output fixture; Spec 397 adds focused textual receipt proof for reduced default internals. |
## First Five Seconds ## First Five Seconds
@ -73,3 +73,11 @@ ## Spec 385 Follow-up
- baseline accepted limitations, foundation-only coverage, and exclusions map to disclosed limitation guidance - baseline accepted limitations, foundation-only coverage, and exclusions map to disclosed limitation guidance
- customer-safe exports retain baseline state/counts but drop baseline internal diagnostics from customer payloads - customer-safe exports retain baseline state/counts but drop baseline internal diagnostics from customer payloads
- rendered-report disclosure policy now includes a baseline readiness proof row for customer-facing profiles - rendered-report disclosure policy now includes a baseline readiness proof row for customer-facing profiles
## Spec 397 Follow-up
Spec 397 reduces default receipt internals on the Review Pack detail surface.
- `Evidence basis` and `Evidence completeness` keep source proof understandable without leading with raw evidence links.
- Internal evidence details stay behind the collapsed technical section and are not customer-default dominant.
- Focused textual browser proof verifies output guidance, pack readiness, evidence basis, and hidden internal evidence detail hierarchy without JavaScript or console errors.

View File

@ -7,9 +7,9 @@ # UI-046 Evidence Snapshot Detail
| Area / scope | Evidence / audit | | Area / scope | Evidence / audit |
| Archetype | Evidence / Audit | | Archetype | Evidence / Audit |
| Design depth | Strategic Surface | | Design depth | Strategic Surface |
| Repo truth | browser-verified in Spec 372 | | Repo truth | browser-verified in Spec 372 and Spec 397 |
| Screenshot | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png` | | Screenshot | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png` |
| Browser status | Reached through Spec 372 smoke-login fixture; mobile capture also completed. | | Browser status | Reached through Spec 372 smoke-login fixture and Spec 397 focused textual receipt proof; mobile capture also completed in Spec 372. |
## First Five Seconds ## First Five Seconds
@ -39,3 +39,11 @@ ## Spec 385 Follow-up
- baseline readiness now distinguishes trusted no drift, trusted drift, missing evidence, unresolved identity, unsupported coverage, accepted limitations, exclusions, stale proof, and failed proof - baseline readiness now distinguishes trusted no drift, trusted drift, missing evidence, unresolved identity, unsupported coverage, accepted limitations, exclusions, stale proof, and failed proof
- legacy compare `reason_code` context alone is not treated as trusted no-drift evidence - legacy compare `reason_code` context alone is not treated as trusted no-drift evidence
- provider-resource binding decisions are consumed only as internal derived diagnostics, not as raw customer-visible evidence fields - provider-resource binding decisions are consumed only as internal derived diagnostics, not as raw customer-visible evidence fields
## Spec 397 Follow-up
Spec 397 reduces the default receipt surface.
- `Internal evidence dimensions` replaces the old default evidence-dimension framing.
- Artifact source/detector/source-target detail is no longer default-visible in the dimension summary.
- Focused textual browser proof verifies outcome, coverage, related context, and collapsed internal evidence detail without JavaScript or console errors.

View File

@ -7,9 +7,9 @@ # UI-048 Stored Report Detail
| Area / scope | Evidence / audit | | Area / scope | Evidence / audit |
| Archetype | Evidence / Audit | | Archetype | Evidence / Audit |
| Design depth | Strategic Surface | | Design depth | Strategic Surface |
| Repo truth | browser-verified in Spec 372 | | Repo truth | browser-verified in Spec 372 and Spec 397 |
| Screenshot | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png` | | Screenshot | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png` |
| Browser status | Reached through Spec 372 smoke-login fixture. | | Browser status | Reached through Spec 372 smoke-login fixture and Spec 397 focused textual receipt proof. |
## First Five Seconds ## First Five Seconds
@ -30,3 +30,11 @@ ## Spec 372 Follow-up
- Stored Report is no longer unresolved for this fixture. - Stored Report is no longer unresolved for this fixture.
- Browser smoke verified summary before technical details/raw payload and no JavaScript errors or console logs. - Browser smoke verified summary before technical details/raw payload and no JavaScript errors or console logs.
## Spec 397 Follow-up
Spec 397 reduces the default receipt surface.
- `Internal report artifact` replaces the top-level technical artifact framing.
- `Internal payload details` replaces `Raw payload` as the default collapsed receipt label.
- Focused textual browser proof verifies report scope and summary appear before internal artifact/payload detail without JavaScript or console errors.

View File

@ -48,26 +48,26 @@ # Route Inventory
| UI-040 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` | resource | Environment Review Detail | Reviews | environment record | reachable | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png) | [report](page-reports/ui-040-environment-review-detail.md) | Spec 372 verifies outcome/guidance/evidence before technical details. | | UI-040 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` | resource | Environment Review Detail | Reviews | environment record | reachable | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png) | [report](page-reports/ui-040-environment-review-detail.md) | Spec 372 verifies outcome/guidance/evidence before technical details. |
| UI-101 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}/resolve-publication` | resource custom page | Review Publication Resolution | Reviews | environment record workflow | reachable from blocked Environment Review Detail CTA | environment + review view capability to inspect; `ENVIRONMENT_REVIEW_MANAGE` plus source-owned provider/evidence/review/export capabilities for mutations | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/386-review-publication-resolution-workflow-v1/artifacts/screenshots/01-resolution-page-desktop.png), [mobile](../../specs/386-review-publication-resolution-workflow-v1/artifacts/screenshots/02-resolution-page-mobile.png) | [report](page-reports/ui-101-review-publication-resolution.md) | Subject-driven operator workflow for publication blockers; no top-level nav, no generic resource, no global search surface, and no auto-publish path. | | UI-101 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}/resolve-publication` | resource custom page | Review Publication Resolution | Reviews | environment record workflow | reachable from blocked Environment Review Detail CTA | environment + review view capability to inspect; `ENVIRONMENT_REVIEW_MANAGE` plus source-owned provider/evidence/review/export capabilities for mutations | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/386-review-publication-resolution-workflow-v1/artifacts/screenshots/01-resolution-page-desktop.png), [mobile](../../specs/386-review-publication-resolution-workflow-v1/artifacts/screenshots/02-resolution-page-mobile.png) | [report](page-reports/ui-101-review-publication-resolution.md) | Subject-driven operator workflow for publication blockers; no top-level nav, no generic resource, no global search surface, and no auto-publish path. |
| UI-041 | `/admin/workspaces/{workspace}/environments/{environment}/review-packs` | resource | Review Packs | Reviews | environment-bound | route exists | environment entitlement | Reviews | Evidence / Audit | Domain Pattern Surface | repo-verified | - | - | Export artifact list. | | UI-041 | `/admin/workspaces/{workspace}/environments/{environment}/review-packs` | resource | Review Packs | Reviews | environment-bound | route exists | environment entitlement | Reviews | Evidence / Audit | Domain Pattern Surface | repo-verified | - | - | Export artifact list. |
| UI-042 | `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` | resource | Review Pack Detail | Reviews | environment record | reachable | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png) | [report](page-reports/ui-042-review-pack-detail.md) | Spec 372 verifies readiness/contents/evidence before technical pack metadata. | | UI-042 | `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` | resource | Review Pack Detail | Reviews | environment record | reachable | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png) | [report](page-reports/ui-042-review-pack-detail.md) | Spec 397 focused browser proof keeps output guidance and customer-safe state first while keeping evidence snapshot internals out of the default view. |
| UI-043 | `/admin/review-packs/{reviewPack}/download` | controller | Review Pack Download | Reviews | workspace/environment artifact | route exists | download authorization expected | Reviews | Evidence / Audit | Design-System Cleanup Surface | repo-verified | - | - | Action endpoint, not page; include in coverage due customer artifact impact. | | UI-043 | `/admin/review-packs/{reviewPack}/download` | controller | Review Pack Download | Reviews | workspace/environment artifact | route exists | download authorization expected | Reviews | Evidence / Audit | Design-System Cleanup Surface | repo-verified | - | - | Action endpoint, not page; include in coverage due customer artifact impact. |
| UI-099 | `/admin/review-packs/{reviewPack}/report` | controller | Rendered Review Report | Reviews | workspace/environment artifact | route exists | signed review-pack view access plus current-export / ready / not-expired authority | Reviews | Evidence / Audit | Strategic Surface | repo-verified | [desktop](../../specs/366-management-report-layout-branded-report-themes-v1/artifacts/screenshots/01-customer-executive-report.png) | [report](page-reports/ui-099-rendered-review-report.md) | Spec 366 adds management-first cover, text co-branding, KPI strip, profile-aware hierarchy, and print/mobile-ish browser evidence while keeping the route read-only and current-pack-only. | | UI-099 | `/admin/review-packs/{reviewPack}/report` | controller | Rendered Review Report | Reviews | workspace/environment artifact | route exists | signed review-pack view access plus current-export / ready / not-expired authority | Reviews | Evidence / Audit | Strategic Surface | repo-verified | [desktop](../../specs/366-management-report-layout-branded-report-themes-v1/artifacts/screenshots/01-customer-executive-report.png) | [report](page-reports/ui-099-rendered-review-report.md) | Spec 366 adds management-first cover, text co-branding, KPI strip, profile-aware hierarchy, and print/mobile-ish browser evidence while keeping the route read-only and current-pack-only. |
| UI-044 | `/admin/evidence/overview` | route + page | Evidence Overview | Evidence / audit | workspace hub | route exists | workspace member | Evidence / Audit | Reviews | Strategic Surface | repo-verified | - | - | Workspace-wide evidence landing. | | UI-044 | `/admin/evidence/overview` | route + page | Evidence Overview | Evidence / audit | workspace hub | route exists | workspace member | Evidence / Audit | Reviews | Strategic Surface | repo-verified | - | - | Workspace-wide evidence landing. |
| UI-045 | `/admin/workspaces/{workspace}/environments/{environment}/evidence` | resource | Evidence Snapshots | Evidence / audit | environment-bound | route exists | environment entitlement | Evidence / Audit | Reviews | Domain Pattern Surface | repo-verified | - | - | Environment evidence list. | | UI-045 | `/admin/workspaces/{workspace}/environments/{environment}/evidence` | resource | Evidence Snapshots | Evidence / audit | environment-bound | route exists | environment entitlement | Evidence / Audit | Reviews | Domain Pattern Surface | repo-verified | - | - | Environment evidence list. |
| UI-046 | `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` | resource | Evidence Snapshot Detail | Evidence / audit | environment record | reachable | environment + record entitlement | Evidence / Audit | Support / Diagnostics | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png) | [report](page-reports/ui-046-evidence-snapshot-detail.md) | Spec 372 reaches the detail route and keeps operation/source diagnostics progressively disclosed. | | UI-046 | `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` | resource | Evidence Snapshot Detail | Evidence / audit | environment record | reachable | environment + record entitlement | Evidence / Audit | Support / Diagnostics | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png) | [report](page-reports/ui-046-evidence-snapshot-detail.md) | Spec 397 focused browser proof keeps receipt summary, coverage, and related context before collapsed technical/internal evidence dimensions. |
| UI-047 | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports` | resource | Stored Reports | Evidence / audit | environment-bound | route exists | environment entitlement | Evidence / Audit | Reviews | Domain Pattern Surface | repo-verified | - | - | Report artifact list. | | UI-047 | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports` | resource | Stored Reports | Evidence / audit | environment-bound | route exists | environment entitlement | Evidence / Audit | Reviews | Domain Pattern Surface | repo-verified | - | - | Report artifact list. |
| UI-048 | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}` | resource | Stored Report Detail | Evidence / audit | environment record | reachable | environment + record entitlement | Evidence / Audit | Reviews | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png) | [report](page-reports/ui-048-stored-report-detail.md) | Spec 372 verifies report scope/readiness before technical report metadata and raw payload. | | UI-048 | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}` | resource | Stored Report Detail | Evidence / audit | environment record | reachable | environment + record entitlement | Evidence / Audit | Reviews | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png) | [report](page-reports/ui-048-stored-report-detail.md) | Spec 397 focused browser proof keeps report scope and summary first while collapsing raw artifact and payload details under internal labels. |
| UI-049 | `/admin/workspaces/{workspace}/environments/{environment}/backup-schedules` | resource | Backup Schedules | Backup / restore | environment-bound | route exists | environment entitlement + backup capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | - | - | Schedule run/retry actions are high impact. | | UI-049 | `/admin/workspaces/{workspace}/environments/{environment}/backup-schedules` | resource | Backup Schedules | Backup / restore | environment-bound | route exists | environment entitlement + backup capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | - | - | Schedule run/retry actions are high impact. |
| UI-050 | `/admin/workspaces/{workspace}/environments/{environment}/backup-schedules/create` and `/edit` | resource | Backup Schedule Create/Edit | Backup / restore | environment-bound | route exists | backup schedule capability | Backup / Restore | Settings / Admin | Domain Pattern Surface | repo-verified | - | - | Form state and confirmation copy need later review. | | UI-050 | `/admin/workspaces/{workspace}/environments/{environment}/backup-schedules/create` and `/edit` | resource | Backup Schedule Create/Edit | Backup / restore | environment-bound | route exists | backup schedule capability | Backup / Restore | Settings / Admin | Domain Pattern Surface | repo-verified | - | - | Form state and confirmation copy need later review. |
| UI-051 | `/admin/workspaces/{workspace}/environments/{environment}/backup-sets` | resource | Backup Sets | Backup / restore | environment-bound | reachable | environment entitlement + backup capability | Backup / Restore | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/371-core-operator-view-surfaces-productization/artifacts/screenshots/spec371-backup-set-productization-01-backup-sets-list.png) | [report](page-reports/ui-013-environment-backup-sets.md) | Spec 371 verifies seeded Backup Sets list/detail with restore-point decision, included items, and secondary technical detail. | | UI-051 | `/admin/workspaces/{workspace}/environments/{environment}/backup-sets` | resource | Backup Sets | Backup / restore | environment-bound | reachable | environment entitlement + backup capability | Backup / Restore | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/371-core-operator-view-surfaces-productization/artifacts/screenshots/spec371-backup-set-productization-01-backup-sets-list.png) | [report](page-reports/ui-013-environment-backup-sets.md) | Spec 371 verifies seeded Backup Sets list/detail with restore-point decision, included items, and secondary technical detail. |
| UI-052 | `/admin/workspaces/{workspace}/environments/{environment}/backup-sets/create` and `/view` | resource | Backup Set Create/View | Backup / restore | environment record/workflow | route exists | backup capability | Backup / Restore | Evidence / Audit | Strategic Surface | repo-verified | - | - | Backup creation plus partial/failure restore-point states still need separate seeded workflow coverage. | | UI-052 | `/admin/workspaces/{workspace}/environments/{environment}/backup-sets/create` and `/view` | resource | Backup Set Create/View | Backup / restore | environment record/workflow | route exists | backup capability | Backup / Restore | Evidence / Audit | Strategic Surface | repo-verified | - | - | Backup creation plus partial/failure restore-point states still need separate seeded workflow coverage. |
| UI-053 | `/admin/workspaces/{workspace}/environments/{environment}/restore-runs` | resource | Restore Runs | Backup / restore | environment-bound | browser blocked by capability in fixture | environment entitlement + restore capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | [blocked](screenshots/desktop/ui-014-restore-runs.png) | [report](page-reports/ui-014-restore-runs.md) | Route exists; local fixture returned Forbidden. | | UI-053 | `/admin/workspaces/{workspace}/environments/{environment}/restore-runs` | resource | Restore Runs | Backup / restore | environment-bound | browser blocked by capability in fixture | environment entitlement + restore capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | [blocked](screenshots/desktop/ui-014-restore-runs.png) | [report](page-reports/ui-014-restore-runs.md) | Route exists; local fixture returned Forbidden. |
| UI-054 | `/admin/workspaces/{workspace}/environments/{environment}/restore-runs/create` and `/view` | resource | Restore Run Create/View | Backup / restore | environment record/workflow | route exists | restore capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | - | - | Spec 390 adds Restore-local readiness guidance and next-safe-action copy on create/view while preserving final confirmation and OperationRun truth. Broader failure/conflict browser coverage remains follow-up. | | UI-054 | `/admin/workspaces/{workspace}/environments/{environment}/restore-runs/create` and `/view` | resource | Restore Run Create/View | Backup / restore | environment record/workflow | view browser-verified; create route exists | restore capability | Backup / Restore | Operations / Monitoring | Strategic Surface | repo-verified | - | [report](../../specs/397-receipt-page-reduction/implementation-report.md) | Spec 397 textual browser proof verifies completed Restore Run detail as a reduced receipt with OperationRun proof collapsed; broader create/failure/conflict workflow coverage remains follow-up. |
| UI-055 | `/admin/baseline-profiles` | resource | Baseline Profiles | Governance | workspace analysis | reachable | workspace member | Drift / Diff | Settings / Admin | Strategic Surface | repo-verified | [desktop](screenshots/desktop/ui-010-baseline-profiles.png) | [report](page-reports/ui-010-baseline-profiles.md) | Workspace-owned baseline library. | | UI-055 | `/admin/baseline-profiles` | resource | Baseline Profiles | Governance | workspace analysis | reachable | workspace member | Drift / Diff | Settings / Admin | Strategic Surface | repo-verified | [desktop](screenshots/desktop/ui-010-baseline-profiles.png) | [report](page-reports/ui-010-baseline-profiles.md) | Workspace-owned baseline library. |
| UI-056 | `/admin/baseline-profiles/create` | resource | Create Baseline Profile | Governance | workspace analysis | route exists | baseline capability | Drift / Diff | Settings / Admin | Domain Pattern Surface | repo-verified | - | - | Workspace-owned form. | | UI-056 | `/admin/baseline-profiles/create` | resource | Create Baseline Profile | Governance | workspace analysis | route exists | baseline capability | Drift / Diff | Settings / Admin | Domain Pattern Surface | repo-verified | - | - | Workspace-owned form. |
| UI-057 | `/admin/baseline-profiles/{record}` and `/edit` | resource | Baseline Profile Detail/Edit | Governance | workspace record | route exists | baseline capability | Drift / Diff | Evidence / Audit | Strategic Surface | repo-verified | - | - | Capture/compare actions need dangerous-action audit. | | UI-057 | `/admin/baseline-profiles/{record}` and `/edit` | resource | Baseline Profile Detail/Edit | Governance | workspace record | route exists | baseline capability | Drift / Diff | Evidence / Audit | Strategic Surface | repo-verified | - | - | Capture/compare actions need dangerous-action audit. |
| UI-058 | `/admin/baseline-profiles/{record}/compare-matrix` | page | Baseline Compare Matrix | Governance | workspace analysis | route exists | baseline capability | Drift / Diff | Evidence / Audit | Strategic Surface | repo-verified | - | - | Matrix/product hierarchy review needed. | | UI-058 | `/admin/baseline-profiles/{record}/compare-matrix` | page | Baseline Compare Matrix | Governance | workspace analysis | route exists | baseline capability | Drift / Diff | Evidence / Audit | Strategic Surface | repo-verified | - | - | Matrix/product hierarchy review needed. |
| UI-059 | `/admin/baseline-snapshots` | resource | Baseline Snapshots | Evidence / audit | workspace analysis | route exists | workspace member | Evidence / Audit | Drift / Diff | Domain Pattern Surface | repo-verified | - | - | Workspace-owned evidence library. | | UI-059 | `/admin/baseline-snapshots` | resource | Baseline Snapshots | Evidence / audit | workspace analysis | route exists | workspace member | Evidence / Audit | Drift / Diff | Domain Pattern Surface | repo-verified | - | - | Workspace-owned evidence library. |
| UI-060 | `/admin/baseline-snapshots/{record}` | resource | Baseline Snapshot Detail | Evidence / audit | workspace record | route exists | workspace + record entitlement | Evidence / Audit | Drift / Diff | Domain Pattern Surface | repo-verified | - | - | Snapshot detail may expose raw payloads; review later. | | UI-060 | `/admin/baseline-snapshots/{record}` | resource | Baseline Snapshot Detail | Evidence / audit | workspace record | reachable | workspace + record entitlement | Evidence / Audit | Drift / Diff | Domain Pattern Surface | browser-verified | - | [report](../../specs/397-receipt-page-reduction/implementation-report.md) | Spec 397 textual browser proof verifies baseline receipt hierarchy and caps governed-subject detail before internal technical sections. |
| UI-061 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare` | page | Baseline Compare | Governance | environment-bound | browser blocked/404 in fixture | workspace + environment entitlement and baseline state | Drift / Diff | Operations / Monitoring | Strategic Surface | repo-verified | [blocked](screenshots/desktop/ui-015-baseline-compare-blocked-404.png) | [report](page-reports/ui-015-baseline-compare.md) | Route exists in route list; smoke fixture could not render it. | | UI-061 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare` | page | Baseline Compare | Governance | environment-bound | browser blocked/404 in fixture | workspace + environment entitlement and baseline state | Drift / Diff | Operations / Monitoring | Strategic Surface | repo-verified | [blocked](screenshots/desktop/ui-015-baseline-compare-blocked-404.png) | [report](page-reports/ui-015-baseline-compare.md) | Route exists in route list; smoke fixture could not render it. |
| UI-100 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution` | page | Baseline Subject Resolution | Governance | environment-bound | browser-verified | workspace + environment entitlement; view requires baseline view capability, mutations require baseline manage capability | Drift / Diff | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png) | [report](page-reports/ui-100-baseline-subject-resolution.md) | Focused operator worklist for persisted baseline subject identity and coverage decisions; reachable from Baseline Compare and Operation detail only when actionable outcomes exist. | | UI-100 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution` | page | Baseline Subject Resolution | Governance | environment-bound | browser-verified | workspace + environment entitlement; view requires baseline view capability, mutations require baseline manage capability | Drift / Diff | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png) | [report](page-reports/ui-100-baseline-subject-resolution.md) | Focused operator worklist for persisted baseline subject identity and coverage decisions; reachable from Baseline Compare and Operation detail only when actionable outcomes exist. |
| UI-062 | `/admin/workspaces/{workspace}/environments/{environment}/inventory` | cluster | Inventory Cluster | Inventory | environment-bound | route exists | environment entitlement | Inventory | Workspace / Tenant Context | Domain Pattern Surface | repo-verified | - | - | Cluster landing/navigation surface. | | UI-062 | `/admin/workspaces/{workspace}/environments/{environment}/inventory` | cluster | Inventory Cluster | Inventory | environment-bound | route exists | environment entitlement | Inventory | Workspace / Tenant Context | Domain Pattern Surface | repo-verified | - | - | Cluster landing/navigation surface. |

View File

@ -20,7 +20,7 @@ # Unresolved Pages
| UI-049 | Backup Schedules | Strategic backup schedule page was not captured. | Environment with active, paused, failing, and never-run schedules. | Include in backup/restore safety spec. | | UI-049 | Backup Schedules | Strategic backup schedule page was not captured. | Environment with active, paused, failing, and never-run schedules. | Include in backup/restore safety spec. |
| UI-052 | Backup Set Create/View | Backup workflow/detail route needs capability and backup data. | Create form, backup-set view, partial/failure states. | Add backup workflow target. | | UI-052 | Backup Set Create/View | Backup workflow/detail route needs capability and backup data. | Create form, backup-set view, partial/failure states. | Add backup workflow target. |
| UI-053 | Restore Runs | Browser returned Forbidden for the local fixture. | Capability-backed environment with restore-run records. | Re-test with seeded capability; screenshot exists as blocker evidence. | | UI-053 | Restore Runs | Browser returned Forbidden for the local fixture. | Capability-backed environment with restore-run records. | Re-test with seeded capability; screenshot exists as blocker evidence. |
| UI-054 | Restore Run Create/View | Partially addressed by Spec 390 readiness guidance; broader restore create/view route still needs capability-backed browser evidence across dry-run, validation, partial restore, conflict, and confirmation states. | Dry-run, validation, partial restore, conflict, and confirmation states. | Re-test with Spec 390 fixture and keep broader restore workflow target. | | UI-054 | Restore Run Create/View | Partially addressed by Spec 390 readiness guidance and Spec 397 completed-run receipt proof; broader restore create/view route still needs capability-backed browser evidence across dry-run, validation, partial restore, conflict, and confirmation states. | Dry-run, validation, partial restore, conflict, and confirmation states. | Re-test with Spec 390/397 fixtures and keep broader restore workflow target. |
| UI-057 | Baseline Profile Detail/Edit | Baseline record detail/edit was not captured. | Baseline profile with assigned environments and compare/capture state. | Add baseline detail target. | | UI-057 | Baseline Profile Detail/Edit | Baseline record detail/edit was not captured. | Baseline profile with assigned environments and compare/capture state. | Add baseline detail target. |
| UI-058 | Baseline Compare Matrix | Dynamic compare matrix was not captured. | Baseline compare record with pass/fail/unknown dimensions. | Add compare matrix target. | | UI-058 | Baseline Compare Matrix | Dynamic compare matrix was not captured. | Baseline compare record with pass/fail/unknown dimensions. | Add compare matrix target. |
| UI-061 | Baseline Compare | Tested environment variants returned 404 despite route-list presence. | Valid environment/baseline assignment fixture. | Reconcile route binding/fixture and re-capture. | | UI-061 | Baseline Compare | Tested environment variants returned 404 despite route-list presence. | Valid environment/baseline assignment fixture. | Reconcile route binding/fixture and re-capture. |

View File

@ -0,0 +1,84 @@
# Requirements Checklist: Spec 397 - Receipt Page Reduction Pass v1
**Purpose**: Validate specification and preparation quality before implementation.
**Created**: 2026-06-22
**Feature**: `specs/397-receipt-page-reduction/spec.md`
## Candidate Selection
- [x] CHK001 Selected candidate exists in user-provided source material and is not invented.
- [x] CHK002 Selected candidate is supported by repo truth: Spec 395 explicitly deferred a Receipt Page Reduction Pass.
- [x] CHK003 Active auto-prep queue was checked; `docs/product/spec-candidates.md` says no safe automatic target remains, so this is treated as a manually provided candidate.
- [x] CHK004 Related specs were checked as context and are not being rewritten or converted back to preparation state.
- [x] CHK005 Close alternatives are deferred because they are manual-promotion only, already spec-backed, or outside the selected receipt-reduction scope.
- [x] CHK006 The selected slice is bounded to existing receipt-style surfaces and excludes new dashboards, adapters, persisted truth, and broad framework work.
## Content Quality
- [x] CHK007 Spec states the concrete operator problem and current failure mode.
- [x] CHK008 Spec explains user-visible improvement and business/product value.
- [x] CHK009 Primary users/operators are identified.
- [x] CHK010 User stories are prioritized and independently testable.
- [x] CHK011 Functional requirements are testable and use stable IDs.
- [x] CHK012 Non-functional, UX, RBAC/security, auditability/observability, and data/truth requirements are present.
- [x] CHK013 Out-of-scope boundaries are explicit and prevent scope creep.
- [x] CHK014 Acceptance criteria and success criteria are measurable enough for implementation close-out.
- [x] CHK015 Assumptions and open questions are documented, with no blocking open question.
## Product Surface Contract
- [x] CHK016 Spec references `docs/product/standards/product-surface-contract.md`.
- [x] CHK017 No-legacy posture is explicit: canonical replacement, no duplicate old/new receipt UI, no compatibility toggle.
- [x] CHK018 Product Surface Impact names Receipt Page archetype, primary user question, one-primary-action rule, surface budgets, technical demotion, status vocabulary, visible complexity outcome, and exceptions.
- [x] CHK019 Browser proof is required because rendered UI changes are expected.
- [x] CHK020 Human Product Sanity is required and has concrete review questions.
- [x] CHK021 Product Surface exceptions are `none planned`; any exception is a stop condition requiring spec/plan update.
- [x] CHK022 Completed historical specs must not be rewritten, normalized, reopened, or stripped of validation/browser/task history.
- [x] CHK049 UI Action Matrix names primary, secondary, technical/audit, dangerous/high-impact actions, and mutation scope for each target surface.
- [x] CHK050 UI coverage registry decision is explicit for route inventory, design coverage matrix, page reports, strategic surfaces, grouped follow-ups, and unresolved pages.
## Constitution And Architecture
- [x] CHK023 Spec has a completed Spec Candidate Check with approval class, score, red flags, and decision.
- [x] CHK024 Proportionality review confirms no new persisted entity, enum/status family, abstraction, registry, resolver, taxonomy, or broad UI framework is approved.
- [x] CHK025 Plan states no Graph calls, migrations, queues, env vars, storage changes, or package changes are expected.
- [x] CHK026 Plan names likely affected repo surfaces without requiring application edits during preparation.
- [x] CHK027 Workspace/tenant isolation and deny-as-not-found requirements are preserved.
- [x] CHK028 RBAC/security requirements state UI visibility is not authorization.
- [x] CHK029 OperationRun remains execution truth and no OperationRun lifecycle or notification behavior is changed.
- [x] CHK030 Data/truth-source requirements distinguish execution truth, artifact truth, backup/snapshot truth, recovery/evidence truth, and operator next action.
## Filament / Livewire / Actions
- [x] CHK031 Plan states Filament v5 / Livewire v4.1.4 compliance.
- [x] CHK032 Plan states Laravel 12 provider registration location: `apps/platform/bootstrap/providers.php`.
- [x] CHK033 Global search posture must remain safe for any changed resource.
- [x] CHK034 Destructive/high-impact actions must keep `->action(...)`, confirmation, authorization, audit where mutating, and tests if touched.
- [x] CHK035 Asset strategy is recorded: no new assets expected, `filament:assets` only if implementation registers assets.
- [x] CHK036 UI implementation must reuse native Filament and existing shared primitives before local Blade/CSS.
## Test Governance
- [x] CHK037 Test lanes are named: Unit only for pure helpers, Feature/Filament for page/action visibility, Browser for focused rendered receipt proof.
- [x] CHK038 Browser proof is focused and fixture-bounded, not a broad visual regression suite.
- [x] CHK039 Planned validation commands are narrow and implementation must choose exact filters after discovery.
- [x] CHK040 Fixture/helper cost must stay explicit and minimal.
- [x] CHK041 Implementation report fields are specified in `plan.md`.
- [x] CHK051 Requirement coverage map includes both FR and NFR coverage.
## Readiness
- [x] CHK042 `spec.md` exists and is complete enough for planning.
- [x] CHK043 `plan.md` exists and identifies likely affected surfaces, constraints, risks, phases, tests, rollout, and stop conditions.
- [x] CHK044 `tasks.md` exists or must be generated before implementation.
- [x] CHK045 Scope is small enough for a bounded implementation loop.
- [x] CHK046 No open question blocks safe implementation.
## Review Outcome
- [x] CHK047 Review outcome class: `acceptable-special-case`.
- [x] CHK048 Workflow outcome: `keep`.
## Notes
This checklist validates preparation only. It does not claim runtime UI reduction, test execution, browser verification, human product sanity completion, or implementation close-out.

View File

@ -0,0 +1,204 @@
# Implementation Report: Spec 397 - Receipt Page Reduction Pass v1
**Date**: 2026-06-22
**Branch**: `397-receipt-page-reduction`
**Implementation slice**: Existing `/admin` receipt-style detail surfaces only: Evidence Snapshot detail, Baseline Snapshot detail, Restore Run detail, Review Pack detail receipt sections, and Stored Report detail receipt metadata.
## Product Surface Close-Out
- **No-legacy posture**: canonical replacement. No duplicate legacy/new receipt surfaces, no compatibility toggle, no new receipt framework.
- **Product Surface Impact**: default receipt pages now emphasize outcome, scope, timing, trust/readiness, and next action before internal support detail.
- **UI Surface Impact**: rendered Filament detail pages changed; no navigation architecture, dashboard, wizard, or report-content redesign.
- **Page archetype**: Receipt Page. Review Pack and Stored Report retain Report Page behavior for actual report/output content; this pass changes receipt metadata/detail sections.
- **Surface budgets**: default long lists are capped at eight rows/items where changed; technical links and proof panels are collapsed or demoted.
- **Technical Annex / deep-link demotion**: OperationRun proof, evidence snapshot link detail, raw artifact descriptors, raw payload JSON, detector/source metadata, and long governed-subject/item lists are secondary/internal.
- **Canonical status vocabulary**: Restore Run detail now maps to `Not configured`, `Running`, `Ready`, `Needs attention`, `Failed`, and `Blocked`.
- **Product Surface exceptions**: none.
- **Visible complexity outcome**: reduced. No new visible section family was added; internal depth remains available through collapsed/detail sections.
## Runtime Changes
- Evidence Snapshot: renamed `Evidence dimensions` to `Internal evidence dimensions`, collapsed it by default, removed default artifact-source summary rendering.
- Baseline Snapshot: capped default governed-subject summary and grouped detail output to eight rows/items; collapsed detailed captured-policy inventory.
- Restore Run: simplified top-level decision labels/actions, capped default item/foundation outcome rows, completed missing summary counts from recorded item outcomes, moved readiness/proof detail under closed `View technical run details`, kept running OperationRun links out of the primary action, and added explicit post-run evidence operator guidance for completed-vs-running proof gaps.
- Review Pack: renamed receipt evidence fields to `Evidence basis` / `Evidence completeness`; moved the evidence snapshot link into internal technical detail.
- Stored Report: renamed technical sections to `Internal report artifact` and `Internal payload details` with support-oriented descriptions.
## Safety / Governance
- **Persisted truth**: no migrations, tables, columns, enums, or new persisted state.
- **Graph / remote calls**: no Graph or remote render calls added.
- **Queues / cron / storage**: no queue, cron, or storage behavior changes.
- **RBAC**: existing resource policies/scopes retained; customer-safe and entitlement tests passed.
- **Destructive/high-impact actions**: no destructive actions were materially changed. Existing confirmed/audited actions remain in place for Evidence Snapshot expiry, Review Pack expiry, Restore Run archive/restore/force-delete/bulk/rerun flows.
- **Global search**: all touched resources remain globally unsearchable: Evidence Snapshot, Baseline Snapshot, Restore Run, Review Pack, Stored Report.
- **Livewire v4 compliance**: Filament v5 / Livewire v4.1.4 project baseline preserved; no Livewire v3 APIs introduced.
- **Provider registration**: unchanged; Laravel providers remain in `apps/platform/bootstrap/providers.php`.
- **Asset strategy**: no new assets registered. No `filament:assets` deployment step is newly required by this pass.
- **Deployment impact**: no env vars, migrations, queues, storage volumes, or Dokploy runtime changes.
## Browser Proof
Focused browser proof is textual, not durable screenshot-based. Pest Browser removes successful screenshots from the host test directory, and the copy helper did not leave stable PNG artifacts under `specs/397-receipt-page-reduction/artifacts/`.
Command:
```bash
cd apps/platform && ./vendor/bin/sail artisan test tests/Browser/Spec397ReceiptPageReductionSmokeTest.php
```
Result: passed, 1 test / 70 assertions.
Covered routes/surfaces:
- Evidence Snapshot detail: `Outcome summary`, `Evidence basis and readiness`, `Evidence coverage summary`, `Internal evidence dimensions`; no default `Artifact source`.
- Baseline Snapshot detail: `Coverage summary`, capped governed subjects, first eight visible, additional rows demoted.
- Restore Run detail: completed and running paths cover `Needs attention`, `Running`, `View technical run details`, `Monitor restore progress`, capped item outcomes, no default OperationRun proof/link or raw restore payload, Next Action opens the demoted technical detail target, completed-without-evidence links to existing Evidence snapshots capture, and running proof gaps do not expose an evidence-capture CTA.
- Review Pack detail: output guidance and pack readiness visible; internal evidence detail remains hidden by default in technical detail.
- Stored Report detail: report scope/summary precede `Internal report artifact` and `Internal payload details`; no default `Raw payload`.
## Human Product Sanity
- **Five-second scan**: pass. Each touched receipt now exposes outcome/scope/readiness before raw proof detail.
- **Customer-safe boundary**: pass. Customer/default paths do not lead with raw evidence IDs, OperationRun proof, raw payload labels, source keys, or detector metadata.
- **Visible complexity**: reduced. Long tables and proof panels are capped/collapsed; no new visible navigation or page family added.
## Validation
Feature / Filament receipt tests:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Feature/Evidence/EvidenceSnapshotResourceTest.php \
tests/Feature/Filament/BaselineSnapshotStructuredRenderingTest.php \
tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php \
tests/Feature/ReviewPack/ReviewPackResourceTest.php \
tests/Feature/StoredReports/StoredReportDetailPresentationTest.php \
tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php \
tests/Feature/Filament/Artifacts/ArtifactSourceTaxonomySurfaceTest.php
```
Result: direct receipt files passed; initial run exposed one stale taxonomy assertion, fixed and rerun:
```bash
cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament/Artifacts/ArtifactSourceTaxonomySurfaceTest.php
```
Result: passed, 2 tests / 24 assertions.
Review-fix focused rerun:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php \
tests/Feature/Filament/Spec390RestoreReadinessGuidanceTest.php \
tests/Feature/Filament/RestoreResultAttentionSurfaceTest.php
```
Result: passed, 22 tests / 127 assertions.
Safety and boundary tests:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Feature/BulkDeleteRestoreRunsTest.php \
tests/Feature/BulkRestoreRestoreRunsTest.php \
tests/Feature/RestoreRunArchiveGuardTest.php \
tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php \
tests/Feature/ReviewPack/Spec392CustomerOutputRouteGateTest.php \
tests/Feature/ReviewPack/ReviewPackRbacTest.php \
tests/Feature/StoredReports/StoredReportEntitlementEnforcementTest.php \
tests/Feature/Filament/BaselineSnapshotAuthorizationTest.php \
tests/Feature/Filament/BaselineSnapshotRelatedContextTest.php
```
Result: passed, 34 tests / 126 assertions.
Browser regression tests:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Browser/Spec277StoredReportsSurfaceSmokeTest.php \
tests/Browser/Spec282GovernanceArtifactRetargetingSmokeTest.php \
tests/Browser/Spec284ArtifactSourceTaxonomySmokeTest.php \
tests/Browser/Spec335RestoreRunDetailProductizationSmokeTest.php \
tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php \
tests/Browser/Spec376BrowserAuditFixtureCoverageSmokeTest.php
```
Result: Spec277, Spec335, Spec372, and Spec376 passed; initial Spec282/Spec284 failures were stale visible-text expectations after receipt reduction. Fixed and reran:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Browser/Spec282GovernanceArtifactRetargetingSmokeTest.php \
tests/Browser/Spec284ArtifactSourceTaxonomySmokeTest.php
```
Result: passed, 2 tests / 59 assertions.
Review-fix browser reruns:
```bash
cd apps/platform && ./vendor/bin/sail artisan test tests/Browser/Spec397ReceiptPageReductionSmokeTest.php
```
Result: passed, 1 test / 70 assertions.
```bash
cd apps/platform && ./vendor/bin/sail artisan test tests/Browser/Spec335RestoreRunDetailProductizationSmokeTest.php
```
Result: passed, 1 test / 48 assertions.
Latest combined browser rerun:
```bash
cd apps/platform && ./vendor/bin/sail artisan test \
tests/Browser/Spec397ReceiptPageReductionSmokeTest.php \
tests/Browser/Spec335RestoreRunDetailProductizationSmokeTest.php
```
Result: passed, 2 tests / 118 assertions.
Formatting / hygiene:
```bash
cd apps/platform && ./vendor/bin/sail pint --test
```
Result: attempted twice. The full unscoped scan continued emitting broad repo-wide style findings for several minutes and was interrupted to avoid leaving a long-running process.
Changed-file Pint:
```bash
cd apps/platform && ./vendor/bin/sail pint --test <changed PHP files>
```
Result: failed once with one touched-file style issue in `BaselineSnapshotPresenter.php`; fixed with Pint and reran successfully: 19 files passed.
Review-fix changed-file Pint:
```bash
cd apps/platform && ./vendor/bin/sail pint --test \
app/Filament/Resources/RestoreRunResource/Presenters/RestoreRunDetailPresenter.php \
resources/views/filament/infolists/entries/restore-results.blade.php \
tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php \
tests/Browser/Spec335RestoreRunDetailProductizationSmokeTest.php \
tests/Browser/Spec397ReceiptPageReductionSmokeTest.php
```
Result: passed, 5 files.
Diff whitespace:
```bash
git diff --check
```
Result: passed.
## Post-Implementation Analysis
- Confirmed no new persisted entity, abstraction framework, enum/status family, Graph call, queue behavior, or compatibility toggle was introduced.
- Confirmed Product Surface docs were updated for route inventory, design coverage matrix, unresolved restore caveat, and the three existing receipt page reports.
- Confirmed broader restore create/failure/conflict workflow remains follow-up; this pass verifies completed and running Restore Run detail receipt reduction.

View File

@ -0,0 +1,281 @@
# Implementation Plan: Spec 397 - Receipt Page Reduction Pass v1
**Branch**: `397-receipt-page-reduction`
**Spec**: `specs/397-receipt-page-reduction/spec.md`
**Created**: 2026-06-22
**Status**: Prepared for implementation
**Preparation scope**: Plan only. No application code changed.
## Summary
Reduce existing `/admin` receipt-style detail surfaces so they answer one product receipt question by default: what happened, when, which scope it covered, whether it can be trusted, and the one next action. The implementation must demote raw technical evidence, OperationRun proof, raw IDs, source keys, detector output, payloads, renderer metadata, internal artifact paths, and long tables into deliberate authorized internal/audit detail.
This is a Product Surface Contract runtime-reduction pass, not a new readiness adapter, report engine, receipt engine, or technical-annex framework.
## Technical Context
**Language/Version**: PHP 8.4.15
**Framework**: Laravel 12.52.0
**UI**: Filament 5.2.1, Livewire 4.1.4
**Database**: PostgreSQL via Sail
**Testing**: Pest 4.3.1 / PHPUnit 12.5.4, focused Feature/Filament and Browser lanes
**Panel provider location**: Laravel 12 providers are registered in `apps/platform/bootstrap/providers.php`; no provider registration change is expected.
**Assets**: No new Filament assets expected. If implementation registers assets, deployment must include `cd apps/platform && php artisan filament:assets`.
**Runtime constraints**: No Graph calls during render, no new queue behavior, no migrations, no new persisted truth, no new package dependencies.
## Constitution Check
### Pre-Implementation Gate
- **Inventory-first, snapshots-second**: Pass. Receipt pages summarize existing observed/artifact truth; no new source of truth is introduced.
- **Read/write separation by default**: Pass. The spec is mostly read-only UI reduction. Any touched destructive/high-impact action must retain confirmation, authorization, audit, and tests.
- **Single Contract Path to Graph**: Pass. No Graph work is in scope.
- **Proportionality First / No Premature Abstraction**: Pass with constraint. Do not add a broad Receipt or Technical Annex framework. Use existing Filament/shared primitives and small local helpers only when they reduce concrete duplication.
- **No New Persisted Truth Without Source-of-Truth Need**: Pass. No migrations/tables/columns.
- **No New State Without Behavioral Consequence**: Pass. Reuse Product Surface Contract vocabulary as display mapping; do not add a new enum/status family.
- **UI Semantics Must Not Become Their Own Framework**: Pass. The goal is visible complexity reduction, not a semantic layer.
- **Product Surface Contract Gate (PSC-001)**: Applies. Spec names page archetype, surface budgets, technical demotion, canonical status vocabulary, browser proof, human sanity, no-legacy posture, and exceptions.
- **Workspace/Tenant Isolation**: Pass if existing policies/resource scoping are preserved and technical details enforce the same entitlement checks.
- **OperationRun standards**: Pass if OperationRun proof is demoted without changing OperationRun lifecycle or notifications.
- **Test Governance**: Pass if tests stay in narrow Feature/Filament and Browser lanes and any helper/fixture cost is explicit.
### Post-Design Gate
Prepared artifacts keep the scope bounded to existing receipt-style surfaces and forbid implementation until tasks validate repo-specific file paths, policies, actions, and browser fixtures.
## Existing Repository Surfaces Likely Affected
Implementation must verify exact current code before editing. Likely affected surfaces:
### Filament Resources / Pages
- `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`
- `apps/platform/app/Filament/Resources/EvidenceSnapshotResource/Pages/ViewEvidenceSnapshot.php`
- `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`
- `apps/platform/app/Filament/Resources/BaselineSnapshotResource/Pages/ViewBaselineSnapshot.php`
- `apps/platform/app/Filament/Resources/RestoreRunResource.php`
- `apps/platform/app/Filament/Resources/RestoreRunResource/Pages/ViewRestoreRun.php`
- `apps/platform/app/Filament/Resources/RestoreRunResource/Presenters/RestoreRunDetailPresenter.php`
- `apps/platform/app/Filament/Resources/ReviewPackResource.php`
- `apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php`
- `apps/platform/app/Filament/Resources/StoredReportResource.php`
- `apps/platform/app/Filament/Resources/StoredReportResource/Pages/ViewStoredReport.php`
### Blade / Infolist / Report Views
- `apps/platform/resources/views/filament/infolists/entries/evidence-dimension-summary.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/evidence-gap-subjects.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-summary-table.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-technical-detail.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-groups.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/restore-results.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/restore-preview.blade.php`
- `apps/platform/resources/views/filament/infolists/entries/review-pack-output-guidance.blade.php`
- `apps/platform/resources/views/review-packs/rendered-report.blade.php`
- Restore proof/detail partials under `apps/platform/resources/views/filament/forms/components/partials/` and `apps/platform/resources/views/filament/forms/components/restore-run-*.blade.php` if they are reused by the detail page.
### Shared Support / Status Paths
- `apps/platform/app/Support/Badges/BadgeCatalog.php`
- `apps/platform/app/Support/Badges/BadgeDomain.php`
- Baseline, restore, evidence, review-pack, and stored-report badge/domain mapping classes under `apps/platform/app/Support/Badges/`.
- Existing Review Pack output gate/guidance classes under `apps/platform/app/Support/ReviewPacks/`.
- Existing reference/link resolvers under `apps/platform/app/Support/References/` if default links need demotion.
### Existing Tests To Inspect / Extend
- `apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php`
- `apps/platform/tests/Feature/Filament/BaselineSnapshot*Test.php`
- `apps/platform/tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php`
- `apps/platform/tests/Feature/ReviewPack/ReviewPackResourceTest.php`
- `apps/platform/tests/Feature/ReviewPack/Spec347ReviewPackOutputContractTest.php`
- `apps/platform/tests/Feature/ReviewPack/Spec392CustomerOutputRouteGateTest.php`
- `apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php`
- `apps/platform/tests/Browser/Spec335RestoreRunDetailProductizationSmokeTest.php`
- `apps/platform/tests/Browser/Spec337EvidenceReviewPackProductFlowSmokeTest.php`
- `apps/platform/tests/Browser/Spec347ReviewPackOutputReadinessSmokeTest.php`
- `apps/platform/tests/Browser/Spec277StoredReportsSurfaceSmokeTest.php`
## Technical Approach
1. **Inventory current receipt default surfaces**
Capture the default-visible sections, actions, top-level statuses, raw technical links, and long tables on the target pages. Record the exact files/components that render each default-visible issue.
2. **Reduce one page family at a time**
Start with Evidence Snapshot and Baseline Snapshot because they have the clearest receipt overload and browser evidence from Spec 377. Then address Restore Run, Review Pack receipt sections, and Stored Report receipt metadata.
3. **Use existing product/status paths first**
Prefer existing BadgeCatalog mappings, Review Pack output guidance, RestoreRunDetailPresenter, Filament infolists/sections, and existing policy/UI-enforcement helpers. Add only local helpers when repeated page-level logic cannot stay readable.
4. **Demote, do not delete, audit depth**
Move raw/internal details into collapsed sections, secondary action groups, existing internal detail sections, or separate authorized audit paths. Customer/read-only defaults must not expose internal detail.
5. **Enforce receipt budgets**
Each touched page must have one primary user question, one primary action, max two secondary actions by default, no default OperationRun/evidence/source-key/detector/payload links, and max eight default rows before show-more/pagination/internal detail.
6. **Preserve safety semantics**
Destructive/high-impact actions remain confirmation-protected and authorization-checked. Demoting links must not weaken access checks.
## Domain / Model Implications
- No new model, migration, table, column, persisted status, enum family, or lifecycle state.
- Existing records remain authoritative:
- Evidence: `EvidenceSnapshot` and related items/reports.
- Baseline: `BaselineSnapshot` and `BaselineSnapshotItem`.
- Restore: `RestoreRun` plus existing OperationRun association.
- Review/report: `ReviewPack`, `StoredReport`, rendered-report profile/disclosure truth.
- Receipt status and next action are derived display behavior from existing record state and authorization, not new stored truth.
## UI / Filament Implications
- Filament v5 / Livewire v4.1.4 compliance is mandatory.
- Use native Filament components, infolists, sections, action groups, and existing shared primitives before local Blade/CSS.
- Avoid publishing Filament internal views.
- Keep global search safe:
- If any touched resource is globally searchable, it must retain a safe View/Edit page and `$recordTitleAttribute`.
- If the receipt page cannot be safely linked for search results, global search must remain disabled.
- Tables need meaningful empty states and capped/default-limited rows where product-facing.
- URL-only actions remain navigation-only. Any mutating/destructive action must use `->action(...)`, `->requiresConfirmation()`, authorization, audit where mutating, and tests.
## Livewire Implications
- Resource pages are Livewire components in tests.
- Avoid chatty server-driven reactivity. Receipt reduction should be mostly static presentation from already-loaded records.
- Do not perform Graph or remote work during render.
- Browser proof must catch Livewire/Filament runtime errors on the focused paths.
## OperationRun / Monitoring Implications
- No new OperationRun type, lifecycle transition, notification, or queue behavior.
- OperationRun remains execution truth.
- Product receipt default pages must not expose OperationRun proof links as primary/default content.
- If a page still needs operation proof, expose it through secondary/internal/audit detail for authorized users.
## RBAC / Policy Implications
- Existing policies and resource scoping remain authoritative.
- Technical/internal detail must use existing entitlement checks and capabilities.
- Non-members remain deny-as-not-found.
- Members lacking capability receive 403 after scope is established.
- UI visibility/disabled state is not authorization.
- Any touched destructive/high-impact action must retain `UiEnforcement` / `WorkspaceUiEnforcement` or equivalent existing pattern plus server-side authorization.
## Audit / Evidence Implications
- Existing audit logs remain the audit trail.
- No new audit action is required for hiding/demoting UI detail.
- Any touched mutating/destructive action must preserve or improve audit logging.
- Product receipt summaries must not imply that an artifact is customer-safe, current, or recoverable unless existing truth supports that claim.
## Data / Migration Implications
- No migration is planned.
- No env var, queue worker, scheduler, or storage change is planned.
- No JSON/JSONB schema change is planned.
- If implementation discovers a data shape gap requiring persistence, stop and update spec/plan before continuing.
## Product Surface Contract Plan
- **No-legacy posture**: canonical replacement. Old overloaded receipt defaults are not preserved.
- **Page archetype**: Receipt Page, with Review Pack/Stored Report Report Page boundary explicitly checked before editing.
- **Surface budgets**: one primary question, one primary action, no raw technical default links, max eight table rows by default.
- **Technical Annex/deep-link demotion**: OperationRun, raw evidence, IDs, source keys, detectors, payloads, renderer metadata, and internal artifact paths are secondary/internal.
- **Canonical status vocabulary**: Product Surface states only for top-level receipt status.
- **Product Surface exceptions**: none planned. Any exception stops implementation until this plan/spec is updated.
- **Browser proof**: required because rendered UI changes are expected. Evidence Snapshot, Baseline Snapshot, Restore Run, and every changed Review Pack or Stored Report receipt surface must be covered.
- **Human Product Sanity**: required.
- **Coverage registry**: update `docs/ui-ux-enterprise-audit/route-inventory.md` and `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` for each target surface whose default rendered UI changes. Other registry files are not required unless implementation changes archetype, expands default content, or cannot classify a touched page.
- **Implementation report**: required in `specs/397-receipt-page-reduction/implementation-report.md` during implementation, not during this preparation step.
## Test Strategy
### Planned Test Purpose / Classification
- **Unit**: Only for pure derived status/action/table-cap helper logic if such helpers are introduced.
- **Feature / Filament / Livewire**: Primary lane for page default-visible sections, action hierarchy, authorization/technical detail gating, and destructive action preservation.
- **Browser**: Focused receipt smoke across representative target pages because rendered UI reduction is the product outcome.
- **Heavy-governance**: Not expected.
- **PostgreSQL**: Not expected unless implementation unexpectedly touches migrations/query plans.
### Minimal Validation Commands
Implementation should choose exact filters after discovering affected tests. Expected shape:
```bash
cd apps/platform && ./vendor/bin/sail artisan test --filter=EvidenceSnapshotResourceTest
cd apps/platform && ./vendor/bin/sail artisan test --filter=BaselineSnapshot
cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec335RestoreRunDetailProductizationTest
cd apps/platform && ./vendor/bin/sail artisan test --filter=ReviewPackResourceTest
cd apps/platform && ./vendor/bin/sail artisan test --filter=StoredReportDetailPresentationTest
cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec397ReceiptPageReductionSmokeTest
cd apps/platform && ./vendor/bin/sail pint --test
git diff --check
```
If existing browser tests are extended instead of adding `Spec397ReceiptPageReductionSmokeTest`, the implementation report must name the exact browser files and paths.
## Rollout / Deployment Considerations
- **Staging**: Required validation gate before production for rendered UI changes.
- **Production**: No schema/env/queue/scheduler/storage change expected.
- **Migrations**: None.
- **Env vars**: None.
- **Queues/workers**: None.
- **Storage/volumes**: None.
- **Assets**: No new assets expected. If assets are registered, include `cd apps/platform && php artisan filament:assets` in deployment.
- **Feature flags**: Not expected. No compatibility toggle for old receipt layouts.
## Implementation Phases
### Phase 1 - Discovery And Baseline Proof
Verify current default-visible receipt content and identify exact render files/actions for each target page. Record raw technical/default-visible issues and current tests/browser fixtures.
### Phase 2 - Evidence Snapshot Receipt Reduction
Reduce Evidence Snapshot default view, demote technical evidence/source/detector/detail blocks, preserve authorized internal detail, and update focused tests.
### Phase 3 - Baseline Snapshot Receipt Reduction
Reduce Baseline Snapshot default view, cap or demote governed-subject/coverage/payload/fidelity detail, preserve authorized internal detail, and update focused tests.
### Phase 4 - Restore / Review Pack / Stored Report Receipt Reduction
Demote OperationRun proof/raw payload/internal artifact links from Restore Run, Review Pack receipt sections, and Stored Report receipt metadata. Preserve report content behavior where the page is truly a Report Page rather than a receipt.
### Phase 5 - Browser Proof, Human Sanity, Close-Out
Run focused browser proof for every changed receipt surface, capture screenshots or textual browser evidence, perform human product sanity, update coverage registry artifacts, update implementation report, and record Product Surface exceptions or `none`.
## Stop Conditions
Stop implementation and update spec/plan before continuing if:
- A new table, migration, persisted receipt state, enum/status family, registry, resolver, presenter framework, or Technical Annex framework appears necessary.
- A Product Surface exception is needed.
- The implementation would change report generation, evidence generation, baseline capture, restore execution, queue behavior, or Graph calls.
- A customer-safe path requires new authorization semantics.
- Browser proof requires broad fixture infrastructure beyond a focused Spec 397 path.
## Implementation Report Requirements
During implementation, create `specs/397-receipt-page-reduction/implementation-report.md` with:
- Active spec and selected slice.
- Branch, HEAD, and dirty state.
- Files changed.
- Runtime UI files changed: yes.
- UI impact and Product Surface exceptions or `none`.
- Browser proof with routes, interactions, console/network result, and screenshots or equivalent evidence.
- Human Product Sanity result.
- Visible complexity outcome.
- No-legacy confirmation.
- Completed-spec rewrite assertion.
- Livewire v4 compliance.
- Provider registration location: `apps/platform/bootstrap/providers.php`, unchanged unless implementation proves otherwise.
- Global search posture for each changed resource.
- Destructive/high-impact actions touched and confirmation/authorization/audit handling.
- Asset strategy and whether `filament:assets` is required.
- Tests/checks run and results.
- Deployment impact for env vars, migrations, queues, scheduler, storage, and assets.
- Unrelated failures and follow-up candidates.

View File

@ -0,0 +1,384 @@
# Feature Specification: Spec 397 - Receipt Page Reduction Pass v1
**Feature Branch**: `397-receipt-page-reduction`
**Created**: 2026-06-22
**Status**: Draft
**Input**: User-provided "Spec 397 - Receipt Page Reduction Pass v1" candidate, plus repo truth from Specs 376, 377, 393, 395, 396, `docs/product/spec-candidates.md`, `docs/product/roadmap.md`, and `docs/product/standards/product-surface-contract.md`.
## Candidate Selection Context
- **Selected candidate**: Receipt Page Reduction Pass v1.
- **Source**: User-provided attachment "Spec 397 - Receipt Page Reduction Pass v1"; also explicitly named as a deferred follow-up in `specs/395-product-surface-gate/spec.md` and `specs/395-product-surface-gate/implementation-report.md`.
- **Why selected**: The active auto-prep queue in `docs/product/spec-candidates.md` is empty, but this request provides an explicit manual productization candidate. The candidate is the first Product Surface Contract runtime-reduction pass after Spec 395 installed the workflow gate. It reduces visible complexity on existing receipt-style pages instead of adding another readiness adapter, semantic layer, or new persisted truth.
- **Close alternatives deferred**:
- `management-report-pdf-staging-runtime-validation`: already represented by Spec 380; not a new prep target.
- `governance-artifact-lifecycle-retention-runtime`: manual-promotion only and broader than the current receipt-reduction problem.
- `provider-readiness-onboarding-productization`: optional/manual and provider-readiness focused, not a receipt-page reduction pass.
- `cross-domain-indicator-runtime-follow-through`: broader guardrail follow-through; this spec is intentionally narrower and page-archetype based.
- `manual-system-panel-browser-fixture-or-audit-procedure`: addressed by Spec 396 context and separate from `/admin` receipt pages.
- **Roadmap relationship**: Supports the roadmap's UI/Product Maturity Polish and Product Surface Contract runtime-reduction direction without reopening completed roadmap lanes.
- **Completed-spec guardrail result**: Specs 376, 377, 392, 393, 395, and 396 are context only. Completed close-out, validation, screenshots, browser evidence, and checked task history must not be rewritten. Spec 395 explicitly deferred this runtime reduction as a future candidate, so this package is a new preparation target rather than a rewrite of Spec 395.
- **Smallest viable implementation slice**: Reduce default visible complexity for existing receipt-style detail surfaces: Evidence Snapshot Detail, Baseline Snapshot Detail, Restore Run Detail, Review Pack receipt sections, and Stored Report receipt surfaces where the page is proving that an artifact was captured/generated/published. Do not redesign dashboards, decision pages, inboxes, wizards, generation flows, or navigation architecture.
## Spec Candidate Check *(mandatory - SPEC-GATE-001)*
- **Problem**: Receipt-style pages currently make operators reconstruct "what happened, can I trust it, and what should I do next?" from raw technical evidence, OperationRun proof, readiness fragments, long tables, source keys, detector details, payload labels, and competing actions.
- **Today's failure**: Evidence, baseline, restore, review-pack, and report receipt surfaces can appear as dashboards or diagnostics explorers rather than product receipts. This can create false confidence, confuse customer-safe output boundaries, and make the next action harder to identify.
- **User-visible improvement**: Operators see a compact receipt by default: status, timestamp, scope, outcome, coverage/result summary, and one next action. Internal audit detail remains available deliberately for authorized users.
- **Smallest enterprise-capable version**: A product-surface reduction pass over existing pages and components only. Move or collapse raw technical detail, cap long default tables, normalize top-level receipt statuses to the existing Product Surface Contract vocabulary, and verify via focused feature/browser coverage.
- **Explicit non-goals**: No new receipt engine, no new persisted entity, no new status enum family, no new dashboard, no new report generation, no restore behavior changes, no evidence generation changes, no broad technical-annex framework, no Product Surface Contract scanner expansion, no compatibility mode for old receipt layouts.
- **Permanent complexity imported**: Focused feature/browser tests and possibly small page-local or existing-shared helper usage if needed. No new database table, migration, provider framework, status taxonomy, presenter framework, or cross-domain UI framework is approved by this spec.
- **Why now**: Spec 395 added the Product Surface Contract gate and explicitly recorded receipt-page reduction as the next runtime-reduction candidate. Spec 377 browser evidence shows overloaded receipt-like pages, especially Evidence Snapshot, Review Pack, and Stored Report surfaces.
- **Why not local**: Multiple product receipt surfaces share the same failure mode. A purely local one-page fix would leave Product Surface Contract drift unresolved and could preserve competing action/status patterns on adjacent receipts.
- **Approval class**: Cleanup / Consolidation.
- **Red flags triggered**: Multiple surfaces. Defense: the scope reduces visible UI complexity across existing receipt pages, adds no new product truth, and forbids broad framework or compatibility layers.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 2 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 12/12**
- **Decision**: approve as a bounded Product Surface Contract runtime-reduction slice.
## Spec Scope Fields *(mandatory)*
- **Scope**: canonical-view over existing tenant-owned artifacts in the `/admin` panel.
- **Primary Routes**:
- Evidence Snapshot detail: existing `EvidenceSnapshotResource` view route.
- Baseline Snapshot detail: existing `BaselineSnapshotResource` view route.
- Restore Run detail: existing `RestoreRunResource` view route.
- Review Pack detail and receipt sections: existing `ReviewPackResource` view route and related rendered-report/download entry points where receipt metadata appears.
- Stored Report detail and report receipt surfaces: existing `StoredReportResource` view route and customer-safe report receipt metadata where applicable.
- **Data Ownership**: Existing tenant-owned artifact records remain authoritative. No new data owner, table, entity, or persisted receipt state is introduced.
- **RBAC**: Existing workspace membership, managed-environment entitlement, and resource capabilities remain authoritative. Product receipt summaries stay visible only to already-authorized users. Technical annex/internal details require existing internal/operator capabilities and must never weaken deny-as-not-found boundaries.
For canonical-view specs:
- **Default filter behavior when tenant-context is active**: Receipt pages remain anchored to the concrete route record and its workspace/environment scope. Hidden remembered environment/session state must not change which receipt record is displayed.
- **Explicit entitlement checks preventing cross-tenant leakage**: Wrong workspace/environment access remains deny-as-not-found through existing policies/resource scoping. Technical annex or audit-detail access must apply the same record entitlement before exposing raw identifiers or internal links.
## No Legacy / No Backward Compatibility Constraint *(mandatory)*
TenantPilot is pre-production for this product-surface behavior.
- **Compatibility posture**: canonical replacement.
- **Legacy aliases, fallback readers, hidden routes, duplicate UI, old labels, or historical fixtures kept?**: no.
- **Why clean replacement is safe now**: There is no production customer-data compatibility requirement. Existing tests that assert overloaded receipt defaults must be updated to the canonical receipt behavior instead of preserving old visible technical sections.
## UI Surface Impact *(mandatory - UI-COV-001)*
Does this spec add, remove, rename, or materially change any reachable UI surface?
- [ ] No UI surface impact
- [x] Existing page changed
- [ ] New page/route added
- [ ] Navigation changed
- [ ] Filament panel/provider surface changed
- [ ] New modal/drawer/wizard/action added
- [x] New table/form/state changed
- [x] Customer-facing surface changed
- [x] Dangerous action changed
- [x] Status/evidence/review presentation changed
- [x] Workspace/environment context presentation changed
## UI/Productization Coverage *(mandatory when UI Surface Impact is not "No UI surface impact")*
| Route/page/surface | Current/new archetype | Design depth | Repo-truth level | Existing pattern reused | New pattern required | Screenshot required | Page audit required | Customer-safe review | Dangerous-action review |
|---|---|---|---|---|---|---|---|---|---|
| Evidence Snapshot detail | Receipt Page | Domain Pattern Surface | repo-verified | `EvidenceSnapshotResource`, existing Evidence Snapshot tests/browser evidence, BadgeCatalog | none expected; use existing Filament sections/infolists | yes, focused browser proof | no full page audit unless default view expands | yes | yes for `Expire snapshot` if touched |
| Baseline Snapshot detail | Receipt Page | Domain Pattern Surface | repo-verified | `BaselineSnapshotResource`, baseline snapshot infolist entries, BadgeCatalog | none expected; use existing table/pagination/collapse patterns | yes | no full page audit unless default view expands | no customer default unless linked output exposes it | yes if destructive actions are touched |
| Restore Run detail | Receipt Page | Domain Pattern Surface | repo-verified | `RestoreRunResource`, `RestoreRunDetailPresenter`, existing restore detail tests/browser smoke | none expected; use existing proof/detail separation | yes | no full page audit unless default view expands | no | yes for delete/restore/rerun/archive actions if touched |
| Review Pack detail receipt sections | Receipt Page / Report Page boundary | Strategic customer/output surface | repo-verified | `ReviewPackResource`, output gate/guidance, rendered report controller, existing Spec 347/392 tests | none expected; reuse output guidance patterns | yes | no full page audit unless report page structure changes | yes | yes for regenerate/archive/download state if touched |
| Stored Report detail / receipt metadata | Receipt Page / Report Page boundary | Domain Pattern Surface | repo-verified | `StoredReportResource`, report disclosure/profile patterns, current report tests | none expected | yes if rendered receipt metadata changes | no full page audit unless report page structure changes | yes for customer-safe output | yes for delete/download state if touched |
Coverage files to update or explicitly not needed during implementation:
- [x] `docs/ui-ux-enterprise-audit/route-inventory.md`
- [x] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md`
- [ ] `docs/ui-ux-enterprise-audit/page-reports/...`
- [ ] `docs/ui-ux-enterprise-audit/strategic-surfaces.md`
- [ ] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md`
- [ ] `docs/ui-ux-enterprise-audit/unresolved-pages.md`
- [ ] `N/A - no reachable UI surface impact`
Spec-level coverage registry decision:
| Coverage artifact | Decision | Reason |
|---|---|---|
| `docs/ui-ux-enterprise-audit/route-inventory.md` | Update during implementation for each target surface whose default rendered UI changes. | Receipt Page classification and Product Surface impact must be discoverable in the durable route registry. |
| `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` | Update during implementation for each target surface whose default rendered UI changes. | The design coverage matrix must reflect the Receipt Page reduction and focused browser-proof expectation. |
| `docs/ui-ux-enterprise-audit/page-reports/...` | Not required by default. | This is a bounded reduction pass over existing surfaces; add or update page reports only if implementation changes archetype, expands default content, or finds the existing registry entry inaccurate. |
| `docs/ui-ux-enterprise-audit/strategic-surfaces.md` | Not required. | No new strategic surface or navigation prominence is introduced. |
| `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` | Not required. | Follow-up candidates are already recorded in this active spec. |
| `docs/ui-ux-enterprise-audit/unresolved-pages.md` | Not required unless implementation cannot classify a touched surface. | All planned target surfaces are classifiable as Receipt Page or Report Page boundary surfaces. |
## Product Surface Impact *(mandatory for UI-affecting specs)*
Reference: `docs/product/standards/product-surface-contract.md`.
- **Product Surface Contract applies?**: yes. This spec materially changes rendered product receipt pages.
- **Page archetype**: Receipt Page. Review Pack and Stored Report may retain Report Page behavior for the report content itself; only receipt metadata/default detail sections are in scope.
- **Primary user question**: What happened, when, what scope did it cover, can I trust it, and what is the one next action?
- **Primary action**: One state-based product action per touched page. Examples: `Refresh evidence`, `Compare to current`, `Review restore blockers`, `Open customer workspace`, `Download customer output`, or `Open report`.
- **Surface budget result**: planned pass. Any exception must be documented in this spec before runtime UI edits continue.
- **Technical Annex / deep-link demotion**: OperationRun proof, raw evidence links, raw IDs, source keys, detectors, payloads, renderer metadata, internal artifact paths, fingerprints, and long technical tables must be hidden, collapsed, or moved to authorized internal/audit paths by default.
- **Canonical status vocabulary**: Use `Ready`, `Needs attention`, `Blocked`, `Running`, `Failed`, `Expired`, `Not configured`, `Unknown`, `Historical`, `Superseded`, plus allowed severity states where needed. Existing domain labels may remain as lower-level diagnostic text when clearly not top-level product status.
- **Visible complexity impact**: decreased.
- **Product Surface exceptions**: none planned.
## UI Action Matrix
| Surface | Primary action | Secondary actions | Technical/audit actions | Dangerous/high-impact actions | Mutation scope |
|---|---|---|---|---|---|
| Evidence Snapshot detail | One state-based action such as `Refresh evidence`, `Open current evidence context`, `Compare to current`, or `Review blockers`. | Up to two contextual actions such as `Open customer workspace` or `Open related review pack` when authorized and relevant. | `View audit trail` and/or `View internal evidence details`, secondary and not default-dominant. | `Expire snapshot` only if the existing action is touched; must remain non-primary, confirmed, authorized, and audited. | Mostly read-only; refresh/expire actions affect TenantPilot evidence state and existing operation/audit paths only. |
| Baseline Snapshot detail | One state-based action such as `Compare to current`, `View active baseline`, `Capture new baseline`, or `Review blockers`. | Up to two contextual actions such as `Open baseline profile` or `Open related findings`. | `View audit trail` and/or `View baseline internals`, secondary and not default-dominant. | Any existing archive/delete/recapture action touched by implementation must remain non-primary, confirmed, authorized, and audited. | Mostly read-only; capture/recapture affects TenantPilot baseline artifact flow through existing operation/audit paths only. |
| Restore Run detail | One state-based action such as `View validation summary`, `Review restore blockers`, `Review failure`, or `View progress`. | Up to two contextual actions such as `Open backup set` or `Open restored state` when repo-real. | `View audit trail` and/or `View technical run details`, secondary and not default-dominant. | Existing delete/restore/rerun/archive actions touched by implementation must remain separated, confirmed, authorized, and audited. | Read-only for receipt viewing; rerun/restore/delete actions retain their existing TenantPilot/Microsoft tenant/simulation scope copy. |
| Review Pack receipt sections | One state-based action such as `Open customer workspace`, `Download customer output`, `Review blockers`, or `Prepare customer output`. | Up to two contextual actions such as `Open source review` or `View report` when customer-safe and authorized. | `View audit trail` and/or `View internal details`, secondary and absent from customer-safe defaults. | Existing regenerate/archive/delete/download-state actions touched by implementation must remain separated, confirmed where high-impact, authorized, and audited when mutating. | Viewing/downloading is read-only; regenerate/archive actions affect TenantPilot review-pack artifact state through existing operation/audit paths only. |
| Stored Report receipt metadata | Conditional: if T006 confirms a product-facing receipt surface, one state-based action such as `Open report`, `Download customer output`, or `Review blockers`. | Up to two contextual actions such as `Open source review pack` or `Open customer workspace` when customer-safe and authorized. | `View audit trail` and/or `View internal report artifact`, secondary and absent from customer-safe defaults. | Existing delete/download-state actions touched by implementation must remain separated, authorized, and audited where mutating. | Viewing/downloading is read-only; delete/archive actions affect TenantPilot stored-report artifact state only. |
## Browser Verification Plan
- **Rendered UI changes expected?**: yes.
- **Required browser proof**: focused smoke covering Evidence Snapshot detail, Baseline Snapshot detail, Restore Run detail, and every Review Pack or Stored Report receipt surface changed by implementation. If implementation narrows the slice to Review Pack only or Stored Report only, the implementation report must state why the other surface was not changed and therefore did not need browser proof.
- **Proof must record**: route opened, fixture/source record, primary receipt question visible, one primary action, technical details not default-visible, no console/runtime/network failure, and screenshot path or textual browser proof.
- **No-browser path**: not available for implementation because rendered UI changes are the purpose of this spec.
## Human Product Sanity Check
Implementation must include a 5 to 15 minute human product sanity review over the focused receipt pages. The reviewer must confirm:
1. Each page immediately reads as a receipt, not a dashboard or diagnostics explorer.
2. Exactly one dominant next action is visible.
3. Technical detail is deliberate, secondary, and authorized.
4. Top-level status labels use the canonical Product Surface vocabulary.
5. Visible complexity decreased or stayed neutral.
6. Customer-safe paths do not expose raw receipt internals.
## Shared Pattern Reuse
- **Interaction classes**: status messaging, detail page header actions, technical/audit links, evidence/report provenance, table caps, destructive action separation.
- **Existing shared paths to reuse first**: `BadgeCatalog` / `BadgeRenderer`, existing resource policies/scopes, `UiEnforcement` / `WorkspaceUiEnforcement`, existing Review Pack output guidance/gating, `RestoreRunDetailPresenter`, Filament infolists/sections/tables, existing browser fixture helpers.
- **Allowed deviation**: page-local collapsed internal detail may be used if a shared pattern does not already exist. Do not create a broad Technical Annex framework unless at least three concrete existing consumers require it and the proportionality review is updated.
## OperationRun UX Impact
- **Creates, queues, deduplicates, resumes, blocks, completes, or deep-links to OperationRun?**: no new OperationRun behavior.
- **Default OperationRun links**: must be demoted from product receipt defaults. They may remain available as secondary/internal audit detail for authorized users.
- **Notifications**: no queued/running/terminal notification changes are in scope.
- **Start UX Contract**: unchanged. If implementation touches an OperationRun-starting action, it must reuse the existing OperationRun Start UX Contract and update this spec before continuing.
## Proportionality / Anti-Bloat Review
- **New persisted entity/table?**: no.
- **New enum/status family?**: no. The spec uses the existing Product Surface Contract vocabulary as a display contract.
- **New DTO/presenter/envelope?**: no broad new layer approved. Small local view helpers may be used only if they reduce duplication without creating a cross-domain framework.
- **New interface/registry/resolver?**: no.
- **New taxonomy/classification system?**: no.
- **New cross-domain UI framework?**: no.
- **Current operator problem**: overloaded receipts hide the trusted outcome and next action behind technical detail.
- **Why existing structure is insufficient**: Existing pages already have the data, but the default presentation violates the Product Surface Contract budgets and deep-link demotion rules.
- **Narrowest correct implementation**: reduce existing pages and tests; do not introduce new receipt truth.
- **Ownership cost**: focused tests, browser proof, and page-specific cleanup only.
- **Alternative intentionally rejected**: a reusable receipt engine, persisted receipt state, or broad Product Surface scanner.
- **Current-release truth or future prep?**: current-release productization cleanup.
## Problem Statement
TenantPilot has strong evidence, restore, review, and report artifact truth, but several detail pages surface too much technical implementation detail by default. A page that should prove a captured/generated/restored/published result can instead expose internal object graphs, OperationRun proof, raw evidence links, technical metadata, long tables, and multiple competing actions.
This spec converts the target pages into strict receipt-style product surfaces. A receipt page must answer:
1. What happened?
2. When did it happen?
3. What scope did it cover?
4. Can I trust it?
5. What is the one next action?
Technical depth remains available, but only as deliberate secondary/internal detail for authorized users.
## Business / Product Value
- Improves operator trust by separating product outcome from raw technical proof.
- Reduces review and click work on artifact/detail pages.
- Makes customer-safe receipt/report paths less likely to leak internal implementation detail.
- Turns Spec 395's Product Surface Contract into visible runtime product value.
- Reduces UI bloat without adding new persistence or broad abstractions.
## Primary Users / Operators
- MSP operator reviewing evidence, baseline, restore, review, or report artifacts.
- Manager validating whether an output can be trusted or shared.
- Read-only/customer-safe reviewer consuming published review/report context.
- Internal support/platform user who may need deliberate audit or technical detail after the receipt outcome is understood.
## User Stories & Testing *(mandatory)*
### User Story 1 - Evidence Snapshot reads as a receipt (Priority: P1)
As an operator, I want Evidence Snapshot detail to show what evidence was captured, for which environment, whether it is current/trustworthy, and the one next action, so I do not have to parse source keys, detector output, or technical dimensions first.
**Independent Test**: Open an Evidence Snapshot detail page with a realistic fixture. The default view shows receipt status, captured timestamp, scope, outcome/coverage summary, and one primary action. It does not show raw source keys, detector output, raw IDs, payloads, or OperationRun proof by default.
**Acceptance Scenarios**:
1. **Given** a current usable Evidence Snapshot, **When** an authorized operator opens the detail page, **Then** the top-level status maps to `Ready` or `Needs attention`, the receipt summary is visible, and one primary action is dominant.
2. **Given** a historical, superseded, expired, blocked, or failed Evidence Snapshot, **When** an authorized operator opens the page, **Then** the status and next action reflect that state without exposing raw technical detail by default.
3. **Given** an authorized internal operator, **When** they intentionally open internal details, **Then** technical evidence remains available without becoming default-visible.
### User Story 2 - Baseline Snapshot reads as a receipt (Priority: P1)
As an operator, I want Baseline Snapshot detail to show what baseline was captured, what it covers, whether it is still relevant, and what to do next, so I do not have to scan long governed-subject or payload tables before understanding the outcome.
**Independent Test**: Open a Baseline Snapshot detail page with more than eight governed subjects or coverage rows. The default view shows receipt status, captured timestamp, scope, baseline profile, coverage/risk summary, and one primary action. Any default table is capped to eight rows or moved behind deliberate expansion/internal detail.
**Acceptance Scenarios**:
1. **Given** an active/current Baseline Snapshot, **When** an operator opens the detail page, **Then** the page emphasizes receipt outcome and `Compare to current` or equivalent next action.
2. **Given** long subject/coverage inventories, **When** the page renders by default, **Then** at most eight rows are visible before show-more/pagination/technical detail.
3. **Given** fidelity, payload, or support-limit details, **When** the default view renders, **Then** those details remain diagnostic/internal and do not replace the receipt status.
### User Story 3 - Restore, Review Pack, and Stored Report receipts demote internals (Priority: P2)
As an operator or reviewer, I want restore, review-pack, and stored-report receipt sections to show outcome, customer-safe state, output availability, and next action, so OperationRun proof, raw artifacts, and renderer metadata do not compete with product meaning.
**Independent Test**: Open representative Restore Run, Review Pack, and Stored Report detail pages. Each touched receipt section shows one receipt status and one primary action. OperationRun proof, raw restore/report payloads, internal artifact paths, renderer metadata, and raw evidence links are absent from the default view or clearly secondary/internal.
**Acceptance Scenarios**:
1. **Given** a completed Restore Run, **When** an operator opens the detail page, **Then** the default page shows restore outcome, source/target, validation/result summary, and a next action without default OperationRun proof.
2. **Given** a Review Pack that is ready, blocked, expired, or internal-only, **When** the receipt section renders, **Then** the customer-safe output state and next action are clear without raw evidence or operation proof links by default.
3. **Given** a Stored Report or rendered report receipt surface, **When** the page renders, **Then** report status, generated timestamp, scope, and output availability are visible, while raw artifact path and renderer metadata are internal-only.
### User Story 4 - Customer-safe paths do not leak technical receipt internals (Priority: P2)
As a customer-safe reviewer, I want receipt links from Customer Review Workspace, Review Pack, reports, and dashboards to open clean product receipts, so I can trust the output without seeing raw technical TenantPilot internals.
**Independent Test**: Use an existing customer-safe review/report fixture or browser path. Customer-facing/default receipt surfaces do not expose raw Evidence Snapshot IDs, OperationRun links, source keys, detector names, fingerprints, payloads, or internal reason ownership.
**Acceptance Scenarios**:
1. **Given** a customer-safe or read-only actor, **When** they follow a receipt/report link, **Then** the default view is product-language only and internal technical detail is absent or inaccessible.
2. **Given** an ordinary operator without internal detail capability, **When** they attempt to access technical annex content directly, **Then** authorization remains enforced.
3. **Given** an internal operator with access, **When** they open the technical path, **Then** detail is available but labeled as internal/audit detail.
## Functional Requirements *(mandatory)*
- **FR-397-001**: Each touched page MUST be classified primarily as a Receipt Page unless the implementation proves the surface is report content rather than receipt metadata.
- **FR-397-002**: Each touched default receipt view MUST show status, timestamp, workspace/environment scope, outcome, compact coverage/result summary, and one primary next action.
- **FR-397-003**: Each touched default receipt view MUST have exactly one dominant primary action. Technical, audit, related-record, and destructive actions must be secondary, grouped, or separated.
- **FR-397-004**: Default receipt views MUST NOT show OperationRun proof links, raw evidence snapshot links, raw IDs, source keys, detector output, fingerprints, payloads, renderer metadata, raw artifact paths, or long technical object graphs as primary/default content.
- **FR-397-005**: Technical/audit depth MUST remain available for authorized internal users through deliberate secondary, collapsed, grouped, or separate internal/audit paths where product requirements require it.
- **FR-397-006**: Customer-safe and read-only default paths MUST NOT expose raw receipt internals by default and MUST preserve deny-as-not-found or capability denial behavior.
- **FR-397-007**: Any default-visible table on touched receipt pages MUST be capped to eight rows, paginated, collapsed, or moved behind technical/internal detail.
- **FR-397-008**: Touched top-level receipt statuses MUST map to the Product Surface Contract vocabulary: `Ready`, `Needs attention`, `Blocked`, `Running`, `Failed`, `Expired`, `Not configured`, `Unknown`, `Historical`, or `Superseded`.
- **FR-397-009**: Existing domain diagnostic labels MAY remain only as secondary diagnostic text, not as competing top-level receipt status.
- **FR-397-010**: Destructive/high-impact actions touched by implementation MUST remain authorization-checked, confirmation-protected, visually separated, and audited where mutating.
- **FR-397-011**: The implementation MUST avoid new persisted receipt state, new status enums, new taxonomy systems, new provider abstractions, or broad new UI frameworks unless this spec and plan are updated with a proportionality review before code changes continue.
- **FR-397-012**: Existing global search behavior for touched resources MUST remain safe. If a resource lacks a safe View/Edit page or tenant-safe result handling, global search must remain disabled.
- **FR-397-013**: Receipt reductions MUST not perform Graph calls or remote work during UI render.
- **FR-397-014**: Browser proof MUST cover the changed default-visible behavior for the representative receipt pages and must record no console/runtime/network failures on the focused path.
## Non-Functional Requirements
- **NFR-397-001**: The default receipt view should be understandable within five seconds by an operator familiar with TenantPilot concepts.
- **NFR-397-002**: The reduction must decrease or maintain visible complexity on every touched page; any increase requires an explicit Product Surface exception before implementation continues.
- **NFR-397-003**: Page rendering must remain DB-only and must not introduce remote calls, expensive per-row service calls, or uncapped table growth.
- **NFR-397-004**: Browser smoke must remain focused and fixture-bounded. Do not introduce a broad visual regression suite.
- **NFR-397-005**: Test additions must stay in the smallest honest lane: Unit only for pure mapping helpers, Feature/Filament for page/action visibility, Browser only for focused rendered receipt proof.
## UX Requirements
- Receipt pages answer one primary user question and show one dominant next action.
- Technical sections use labels such as `View audit trail`, `View internal details`, `View internal evidence details`, `View technical run details`, or `View baseline internals`.
- Default product copy must prefer business meaning, for example `Evidence captured for this environment`, `This baseline is active`, `This receipt is historical`, `This output is ready for customer review`, or `This restore completed with validation warnings`.
- Default product copy must avoid implementation-first labels such as `OperationRun succeeded`, `Source family mismatch`, `Detector output`, `Fidelity metadata`, or `Artifact payload captured` unless inside internal/technical detail.
- Header/action hierarchy must keep destructive actions separate from product navigation and technical/audit actions.
## RBAC / Security Requirements
- Existing workspace and managed-environment entitlement checks remain authoritative.
- UI visibility is not authorization. Any technical annex/internal detail action must enforce policy/capability checks server-side.
- Non-members must continue to receive deny-as-not-found behavior.
- Members without a required capability receive 403 after scope is established.
- Technical paths must not leak raw IDs, payloads, source keys, or OperationRun proof to customer-safe/read-only actors.
- No authorization is weakened to keep an old receipt link working.
## Auditability / Observability Requirements
- This spec does not create new operations or notifications.
- Existing audit and OperationRun records remain the source of execution/proof truth.
- Product receipt pages may summarize audit/execution truth, but must not conflate receipt outcome with execution lifecycle when they are different.
- Technical/audit detail must remain reachable for authorized internal users where it is needed for support, verification, or compliance evidence.
- Any touched mutating/destructive action must preserve existing audit logging behavior.
## Data / Truth-Source Requirements
- Execution truth remains `OperationRun`.
- Artifact truth remains existing `EvidenceSnapshot`, `BaselineSnapshot`, `RestoreRun`, `ReviewPack`, and `StoredReport` records.
- Backup/snapshot truth remains immutable snapshot/backup records.
- Recovery/evidence truth remains existing evidence/report/baseline artifacts.
- Operator next action is derived from existing record state and capability, not persisted as a new receipt truth.
- No new table, column, migration, or persisted compatibility shim is approved.
## Out Of Scope
- New dashboard, inbox, wizard, report center, or decision page behavior.
- New evidence generation, baseline generation, restore execution, review generation, report generation, or PDF rendering behavior.
- New provider integration, Graph endpoint, or remote call.
- New persisted receipt model, status enum, taxonomy, registry, resolver, presenter framework, or Technical Annex framework.
- Broad redesign of Customer Review Workspace, Evidence Overview, Baseline Compare, Operations Hub, or navigation.
- Rewriting completed specs or removing historical validation/browser evidence.
- Preserving old overloaded receipt layouts through compatibility toggles or duplicate UI.
## Acceptance Criteria *(mandatory)*
1. Evidence Snapshot detail behaves as a Receipt Page by default and hides technical dimensions/source keys/detectors/raw IDs/OperationRun proof unless intentionally opened by an authorized internal user.
2. Baseline Snapshot detail behaves as a Receipt Page by default and caps or demotes governed-subject, coverage, payload, fidelity, and technical inventory detail.
3. Restore Run detail behaves as a Receipt Page by default and demotes OperationRun proof/raw restore payload/details behind secondary/internal access.
4. Review Pack receipt sections and Stored Report receipt surfaces show customer-safe output state, provenance summary, generated/published timing, scope, and one next action without raw evidence/internal artifact details by default.
5. Every touched receipt page has exactly one dominant primary action.
6. Technical/audit detail remains available where product-required and authorized.
7. Customer-safe/default paths do not expose raw receipt internals.
8. Touched top-level receipt states use Product Surface Contract vocabulary or explicitly mapped equivalents.
9. Visible complexity decreases or stays neutral on every touched page.
10. Focused feature/Filament tests and focused browser proof pass, or unrelated failures are documented honestly.
## Success Criteria *(mandatory)*
- **SC-397-001**: Each target page can be reviewed in under five seconds for receipt status, scope, and next action during human product sanity review.
- **SC-397-002**: Focused tests prove raw technical details are not default-visible on the representative receipt pages.
- **SC-397-003**: Focused browser smoke proves Evidence Snapshot, Baseline Snapshot, Restore Run, and every Review Pack or Stored Report receipt surface changed by implementation render with no console/runtime/network failure and no default-visible technical internals.
- **SC-397-004**: Default visible rows on touched receipt tables are capped to eight or moved behind explicit expansion/internal detail.
- **SC-397-005**: No new persisted receipt truth, compatibility shim, or broad UI framework is added.
## Edge Cases
- A receipt may have no related report/review context; the summary must show a compact empty state rather than a raw technical table.
- A receipt may be historical but still valid as audit history; status should be `Historical` or `Superseded`, not `Ready`.
- A receipt may be current but incomplete; status should be `Needs attention` or `Blocked`, not `Ready`.
- A Restore Run may be preview-only; the receipt must not imply execution or recovery success.
- A Review Pack may be internal-only; default customer-safe routes must not present it as shareable output.
- Technical annex content may be absent for some pages; do not add fake sections to preserve symmetry.
## Risks
- **Operators lose technical detail**: Mitigate by preserving authorized internal/audit paths.
- **Tests expect old visible technical sections**: Mitigate by updating tests to canonical receipt behavior, not compatibility behavior.
- **Receipt pages become too sparse**: Mitigate by requiring status, timestamp, scope, outcome, coverage/result summary, and next action.
- **Technical Annex becomes a dumping ground**: Mitigate by keeping it secondary, collapsed or separate, permission-aware, and unnecessary for the default receipt outcome.
- **Browser full suite remains red**: Mitigate by making focused receipt smoke blocking and documenting unrelated full-suite failures separately.
## Assumptions
- `397` is the correct next normal product spec number because `396-system-panel-branding` is the latest normal product spec and `999-seeder-external-id` is out-of-sequence utility context.
- Existing resource policies and route scoping already provide the baseline entitlement checks; implementation should harden only where the reduction exposes gaps.
- Existing BadgeCatalog and domain status mappings are sufficient for v1 unless implementation proves a page-local mapping is needed.
- The first implementation should reuse existing tests/fixtures and add a named Spec 397 browser smoke only if current coverage cannot prove the contract.
## Open Questions
- None blocking preparation.
- Stored Report runtime edits are conditional: implementation must first verify whether Stored Report receipt metadata is product-facing, report-facing, or both on each touched route, and must skip Stored Report runtime edits if the inspected surface is report content rather than receipt metadata.
- Implementation must verify whether any exact coverage registry file update is required by current route-inventory state, and document the decision in the implementation report.
## Follow-up Spec Candidates
- Technical Annex standardization only if at least three receipt/detail families prove that local collapse/internal-detail patterns are diverging.
- Report Delivery Center / Stored Reports UX, if management report/report delivery work needs a new page rather than receipt cleanup.
- Cross-domain indicator runtime follow-through, if status/progress vocabulary drift remains after receipt pages are reduced.
- Governance artifact lifecycle retention runtime, if artifact hold/export/delete/read-only behavior needs productized lifecycle controls.

View File

@ -0,0 +1,179 @@
# Tasks: Spec 397 - Receipt Page Reduction Pass v1
**Input**: `specs/397-receipt-page-reduction/spec.md`, `specs/397-receipt-page-reduction/plan.md`
**Prerequisites**: Existing `/admin` receipt-style pages, Product Surface Contract, Filament v5 / Livewire v4
**Tests**: Required. This spec changes rendered UI defaults and must include Feature/Filament proof plus focused browser smoke.
## Test Governance Checklist
- [x] Lane assignment is named and is the narrowest sufficient proof for the changed behavior.
- [x] New or changed tests stay in the smallest honest family; Browser coverage is one explicit focused receipt proof path.
- [x] Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default; any widening is isolated or documented.
- [x] Planned validation commands cover the changed receipt surfaces without pulling in unrelated lane cost.
- [x] The declared surface test profile is explicit: Product Surface Receipt Page reduction.
- [x] Browser proof is required because rendered UI changes are expected.
- [x] Human Product Sanity and Product Surface implementation-report close-out are completed.
- [x] Any material budget, baseline, trend, or escalation note is recorded in the active spec or implementation report.
## Phase 1: Discovery And Guardrails
**Purpose**: Confirm exact current render paths, default-visible overload, and existing tests before changing runtime files.
- [x] T001 Re-read `specs/397-receipt-page-reduction/spec.md`, `specs/397-receipt-page-reduction/plan.md`, and `docs/product/standards/product-surface-contract.md`; record the selected implementation slice in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T002 Inspect Evidence Snapshot default-visible sections, actions, technical links, and tests in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`, `apps/platform/app/Filament/Resources/EvidenceSnapshotResource/Pages/ViewEvidenceSnapshot.php`, and `apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php`.
- [x] T003 Inspect Baseline Snapshot default-visible sections, actions, long tables, and tests in `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`, `apps/platform/app/Filament/Resources/BaselineSnapshotResource/Pages/ViewBaselineSnapshot.php`, and `apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php`.
- [x] T004 Inspect Restore Run default-visible sections, action hierarchy, OperationRun proof, and tests in `apps/platform/app/Filament/Resources/RestoreRunResource.php`, `apps/platform/app/Filament/Resources/RestoreRunResource/Pages/ViewRestoreRun.php`, `apps/platform/app/Filament/Resources/RestoreRunResource/Presenters/RestoreRunDetailPresenter.php`, and `apps/platform/tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php`.
- [x] T005 Inspect Review Pack receipt/default output guidance in `apps/platform/app/Filament/Resources/ReviewPackResource.php`, `apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php`, `apps/platform/resources/views/filament/infolists/entries/review-pack-output-guidance.blade.php`, and `apps/platform/tests/Feature/ReviewPack/ReviewPackResourceTest.php`.
- [x] T006 Inspect Stored Report receipt/report boundary in `apps/platform/app/Filament/Resources/StoredReportResource.php`, `apps/platform/app/Filament/Resources/StoredReportResource/Pages/ViewStoredReport.php`, `apps/platform/resources/views/review-packs/rendered-report.blade.php`, and `apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php`.
- [x] T007 Confirm global search posture for touched resources in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`, `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`, `apps/platform/app/Filament/Resources/RestoreRunResource.php`, `apps/platform/app/Filament/Resources/ReviewPackResource.php`, and `apps/platform/app/Filament/Resources/StoredReportResource.php`.
- [x] T008 Confirm destructive/high-impact actions that may be touched and their confirmation/authorization/audit coverage in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`, `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`, `apps/platform/app/Filament/Resources/RestoreRunResource.php`, `apps/platform/app/Filament/Resources/ReviewPackResource.php`, and `apps/platform/app/Filament/Resources/StoredReportResource.php`; document results in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T009 Decide whether existing browser tests can carry Spec 397 proof or whether a new file is required; record the decision in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 2: Foundational Test Harness
**Purpose**: Add failing or adjusted tests before/alongside implementation so receipt budgets are explicit.
- [x] T010 [P] Add or update Evidence Snapshot receipt assertions in `apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php` for one primary action, no default raw source keys/detectors/raw IDs/OperationRun proof, and authorized internal detail.
- [x] T011 [P] Add or update Baseline Snapshot receipt assertions in `apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php` or a narrower new `apps/platform/tests/Feature/Filament/Spec397BaselineSnapshotReceiptTest.php` for one primary action, table cap, and demoted payload/fidelity technical detail.
- [x] T012 [P] Add or update Restore Run receipt assertions in `apps/platform/tests/Feature/Filament/Spec335RestoreRunDetailProductizationTest.php` or a narrower new `apps/platform/tests/Feature/Filament/Spec397RestoreRunReceiptTest.php` for demoted OperationRun proof and preserved destructive action safety.
- [x] T013 [P] Add or update Review Pack receipt assertions in `apps/platform/tests/Feature/ReviewPack/ReviewPackResourceTest.php` or `apps/platform/tests/Feature/ReviewPack/Spec397ReviewPackReceiptTest.php` for customer-safe output state without default raw evidence/internal artifact links.
- [x] T014 [P] Add or update Stored Report receipt assertions in `apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php` for generated-at/scope/output availability and hidden raw artifact path/renderer metadata by default only if T006 confirms the changed surface is receipt metadata rather than report content.
- [x] T015 Add or update focused browser proof in `apps/platform/tests/Browser/Spec397ReceiptPageReductionSmokeTest.php` unless T009 proves existing browser files can cover all required receipt paths.
## Phase 3: User Story 1 - Evidence Snapshot Receipt Reduction
**Goal**: Evidence Snapshot detail answers what evidence was captured, for which scope, whether it can be trusted, and the next action.
**Independent Test**: Evidence Snapshot feature test and focused browser path pass with no raw technical default content.
- [x] T016 [US1] Update Evidence Snapshot top-level receipt status and summary in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`.
- [x] T017 [US1] Ensure Evidence Snapshot detail has exactly one dominant primary action and at most two default secondary actions in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`.
- [x] T018 [US1] Demote source keys, detector output, raw IDs, technical dimensions, payloads, and OperationRun proof from the default Evidence Snapshot view in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`.
- [x] T019 [US1] Move or collapse Evidence Snapshot internal detail into authorized secondary/internal sections in `apps/platform/resources/views/filament/infolists/entries/evidence-dimension-summary.blade.php`.
- [x] T020 [US1] Keep Evidence Snapshot gap/coverage detail compact and product-labeled in `apps/platform/resources/views/filament/infolists/entries/evidence-gap-subjects.blade.php`.
- [x] T021 [US1] Verify customer-safe related context links do not expose raw receipt internals in `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php`.
- [x] T022 [US1] Run the focused Evidence Snapshot tests selected in T010 and document results in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 4: User Story 2 - Baseline Snapshot Receipt Reduction
**Goal**: Baseline Snapshot detail answers what baseline was captured, what it covers, whether it remains relevant, and the next action.
**Independent Test**: Baseline Snapshot feature test proves table caps/default demotion and receipt status/action hierarchy.
- [x] T023 [US2] Update Baseline Snapshot top-level receipt summary and canonical status mapping in `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`.
- [x] T024 [US2] Ensure Baseline Snapshot detail has exactly one dominant primary action in `apps/platform/app/Filament/Resources/BaselineSnapshotResource.php`.
- [x] T025 [US2] Cap default Baseline Snapshot summary tables to eight rows or move long lists behind expansion/internal detail in `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-summary-table.blade.php`.
- [x] T026 [US2] Demote governed subject inventory and group detail from the default product receipt in `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-groups.blade.php`.
- [x] T027 [US2] Keep payload/fidelity/support-limit detail diagnostic/internal rather than top-level receipt status in `apps/platform/resources/views/filament/infolists/entries/baseline-snapshot-technical-detail.blade.php`.
- [x] T028 [US2] Preserve existing baseline snapshot authorization and related-context behavior in `apps/platform/tests/Feature/Filament/BaselineSnapshotAuthorizationTest.php` and `apps/platform/tests/Feature/Filament/BaselineSnapshotRelatedContextTest.php`.
- [x] T029 [US2] Run the focused Baseline Snapshot tests selected in T011 and document results in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 5: User Story 3 - Restore Run Receipt Reduction
**Goal**: Restore Run detail shows restore outcome, source/target, validation/result summary, and one next action without default OperationRun/raw payload proof.
**Independent Test**: Restore Run feature test proves OperationRun proof is secondary/internal and destructive/high-impact actions remain safe.
- [x] T030 [US3] Update Restore Run detail presenter output to prefer receipt summary, canonical status, source/target, validation/result summary, and next action in `apps/platform/app/Filament/Resources/RestoreRunResource/Presenters/RestoreRunDetailPresenter.php`.
- [x] T031 [US3] Ensure Restore Run detail has exactly one dominant primary action in `apps/platform/app/Filament/Resources/RestoreRunResource.php`.
- [x] T032 [US3] Demote OperationRun proof and raw restore payload/detail from the default Restore Run view in `apps/platform/app/Filament/Resources/RestoreRunResource.php`.
- [x] T033 [US3] Move detailed restore proof/process detail to secondary/internal display in `apps/platform/resources/views/filament/forms/components/partials/restore-run-proof-panel.blade.php`.
- [x] T034 [US3] Keep restore result rows compact and product-labeled in `apps/platform/resources/views/filament/infolists/entries/restore-results.blade.php`.
- [x] T035 [US3] Verify delete/restore/rerun/archive actions retain confirmation, authorization, audit, and tests in `apps/platform/tests/Feature/BulkDeleteRestoreRunsTest.php`, `apps/platform/tests/Feature/BulkRestoreRestoreRunsTest.php`, and `apps/platform/tests/Feature/RestoreRunArchiveGuardTest.php`.
- [x] T036 [US3] Run the focused Restore Run tests selected in T012 and document results in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 6: User Story 3 - Review Pack And Stored Report Receipt Reduction
**Goal**: Review Pack and Stored Report receipt metadata show customer-safe state, output availability, generated/published timing, scope, and one next action without raw evidence/internal artifact defaults.
**Independent Test**: Review Pack and Stored Report feature tests prove customer-safe defaults and hidden internals.
- [x] T037 [US3] Update Review Pack detail receipt sections to show output state, generated/published timestamp, scope, output availability, and one primary action in `apps/platform/app/Filament/Resources/ReviewPackResource.php`.
- [x] T038 [US3] Demote raw evidence links, operation proof, source keys, detector output, and technical generation metadata from default Review Pack output guidance in `apps/platform/resources/views/filament/infolists/entries/review-pack-output-guidance.blade.php`.
- [x] T039 [US3] Preserve Review Pack customer output gate and route authorization behavior in `apps/platform/app/Http/Controllers/ReviewPackRenderedReportController.php`.
- [x] T040 [US3] If T006 confirms Stored Report receipt metadata is product-facing, update Stored Report detail receipt metadata to separate report content from receipt proof in `apps/platform/app/Filament/Resources/StoredReportResource.php`; otherwise record the no-runtime-edit decision in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T041 [US3] If T040 proceeds, demote raw artifact path, renderer metadata, evidence snapshot technical links, OperationRun links, and payload/profile internals from default Stored Report receipt metadata in `apps/platform/app/Filament/Resources/StoredReportResource.php`.
- [x] T042 [US3] Ensure rendered report content remains customer-safe and does not gain raw technical receipt detail in `apps/platform/resources/views/review-packs/rendered-report.blade.php`.
- [x] T043 [US3] Run the focused Review Pack tests selected in T013 and the Stored Report tests selected in T014 only when T040 proceeds; document results in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 7: User Story 4 - Customer-Safe Boundary Proof
**Goal**: Customer-safe/default paths stay free from raw receipt internals while internal audit depth remains available where authorized.
**Independent Test**: Customer-safe or read-only actor cannot see raw Evidence Snapshot IDs, OperationRun links, source keys, detector names, fingerprints, payloads, or internal reason ownership by default.
- [x] T044 [US4] Verify Customer Review Workspace links into evidence/review/report receipt surfaces remain customer-safe in `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php`.
- [x] T045 [US4] Update or add customer-safe route assertions for Review Pack output in `apps/platform/tests/Feature/ReviewPack/Spec392CustomerOutputRouteGateTest.php`.
- [x] T046 [US4] Update or add Stored Report entitlement assertions for hidden technical receipt metadata in `apps/platform/tests/Feature/StoredReports/StoredReportEntitlementEnforcementTest.php`.
- [x] T047 [US4] Verify internal/operator technical detail access remains authorized and deliberate in `apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php`, `apps/platform/tests/Feature/Filament/BaselineSnapshotAuthorizationTest.php`, and `apps/platform/tests/Feature/ReviewPack/ReviewPackRbacTest.php`.
- [x] T048 [US4] Run the focused customer-safe boundary tests selected in T044-T047 and document results in `specs/397-receipt-page-reduction/implementation-report.md`.
## Phase 8: Browser Proof, Product Surface Close-Out, And Validation
**Purpose**: Prove rendered UI reduction and close the Product Surface Contract gate.
- [x] T049 Run the focused Spec 397 browser smoke in `apps/platform/tests/Browser/Spec397ReceiptPageReductionSmokeTest.php` or the exact existing browser files selected in T009 for every changed receipt surface.
- [x] T050 Capture browser proof for Evidence Snapshot, Baseline Snapshot, Restore Run, and every changed Review Pack or Stored Report receipt surface under `specs/397-receipt-page-reduction/artifacts/screenshots/` or record equivalent textual browser proof in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T051 Complete Human Product Sanity review and record the result in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T052 Record Product Surface exceptions as `none` or document any approved exception in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T053 Record Livewire v4 compliance, provider registration location, global search posture for changed resources, destructive/high-impact actions, asset strategy, tests/browser result, and deployment impact in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T054 Update `docs/ui-ux-enterprise-audit/route-inventory.md` for each target surface whose default rendered UI changes; if a planned target surface is skipped, record the no-update rationale in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T055 Update `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` for each target surface whose default rendered UI changes; if a planned target surface is skipped, record the no-update rationale in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T056 Run the focused Feature/Filament tests selected in T010-T014 and T044-T047 from `apps/platform` and record results in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T057 Attempt full `cd apps/platform && ./vendor/bin/sail pint --test`; record that the unscoped run was interrupted after broad repo-wide findings/long runtime and that changed-file Pint is the relevant passed check for this slice.
- [x] T058 Run `git diff --check`.
- [x] T059 Run broader affected tests only if the implementation touched shared helpers or shared resources; document exact commands and results in `specs/397-receipt-page-reduction/implementation-report.md`.
- [x] T060 Confirm no application code introduced migrations, new persisted truth, new broad receipt/annex framework, Graph render calls, or compatibility toggles; record the result in `specs/397-receipt-page-reduction/implementation-report.md`.
## Dependencies
- Phase 1 must complete before runtime edits.
- Phase 2 tests should be added before or alongside each corresponding implementation phase.
- US1 and US2 can run in parallel after discovery because they touch different resources/views/tests.
- Restore, Review Pack, and Stored Report work can run in parallel after discovery if no shared helper is introduced.
- Browser proof and implementation report close-out depend on all touched page changes.
## Requirement Coverage Map
| Requirement | Primary task coverage |
|---|---|
| FR-397-001 Receipt page classification | T001-T006, T049-T053 |
| FR-397-002 Default receipt summary | T016, T023, T030, T037, T040 |
| FR-397-003 One dominant primary action | T017, T024, T031, T037 |
| FR-397-004 Hide raw technical default content | T018-T020, T025-T027, T032-T034, T038-T042 |
| FR-397-005 Preserve authorized internal detail | T019, T027, T033, T047 |
| FR-397-006 Customer-safe/read-only internals hidden | T021, T039, T042, T044-T048 |
| FR-397-007 Cap long default tables | T011, T025, T034 |
| FR-397-008 Product Surface status vocabulary | T016, T023, T030, T037, T040 |
| FR-397-009 Diagnostic labels stay secondary | T019, T027, T033, T038, T041 |
| FR-397-010 Destructive/high-impact action safety | T008, T035, T053 |
| FR-397-011 No new persisted truth/framework | T001, T052, T060 |
| FR-397-012 Global search safety | T007, T053 |
| FR-397-013 No Graph/remote render work | T060 |
| FR-397-014 Focused browser proof | T015, T049-T050 |
| NFR-397-001 Five-second scan | T050-T053 |
| NFR-397-002 Visible complexity not increased | T052-T053, T060 |
| NFR-397-003 DB-only render and bounded table cost | T025, T034, T060 |
| NFR-397-004 Focused browser scope | T009, T015, T049-T050 |
| NFR-397-005 Narrow test lane fit | T010-T015, T056, T059 |
## Parallel Execution Examples
```text
After Phase 1:
- Agent A: T010, T016-T022 for Evidence Snapshot.
- Agent B: T011, T023-T029 for Baseline Snapshot.
- Agent C: T012, T030-T036 for Restore Run.
- Agent D: T013-T014, T037-T043 for Review Pack / Stored Report.
```
## Implementation Strategy
1. MVP first: Evidence Snapshot and Baseline Snapshot receipt reductions because they have the clearest Product Surface overload and browser audit evidence.
2. Then reduce Restore Run receipt proof and Review Pack / Stored Report receipt metadata.
3. Finish with customer-safe boundary proof, focused browser smoke, human sanity, and implementation report.
## Explicit Non-Goals For Implementation
- Do not create a new receipt engine, Technical Annex framework, persisted receipt state, enum/status family, registry, resolver, provider abstraction, report-generation flow, restore behavior, evidence generation flow, navigation architecture, or compatibility toggle.
- Do not rewrite completed specs or remove their validation, browser, screenshot, task, or close-out history.