diff --git a/apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php b/apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php index 76a89d60..8227f9a9 100644 --- a/apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php +++ b/apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php @@ -14,7 +14,6 @@ use App\Models\Finding; use App\Models\FindingException; use App\Models\ManagedEnvironment; -use App\Models\OperationRun; use App\Models\ReviewPack; use App\Models\User; use App\Models\Workspace; @@ -34,14 +33,13 @@ use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\WorkspaceHubEnvironmentFilter; use App\Support\Navigation\WorkspaceHubNavigation; -use App\Support\OperationRunLinks; +use App\Support\Rbac\UiEnforcement; use App\Support\ResolutionGuidance\Adapters\ReviewPackOutputResolutionAdapter; use App\Support\ResolutionGuidance\ResolutionAction; use App\Support\ResolutionGuidance\ResolutionCase; use App\Support\ReviewPacks\ReviewPackOutputReadiness; use App\Support\ReviewPacks\ReviewPackOutputResolutionGuidance; use App\Support\ReviewPackStatus; -use App\Support\Rbac\UiEnforcement; use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile; @@ -1002,7 +1000,6 @@ private function evidencePathForReview( $this->reviewPackProofForReview($packageAvailability, $downloadUrl), $this->decisionTrailProofForReview($decision), $this->acceptedRiskProofForReview($acceptedRisks), - $this->operationProofForReview($review, $tenant), $this->exportArtifactProofForReview($packageAvailability, $downloadUrl), ]; } @@ -1017,7 +1014,6 @@ private function asideEvidencePath(array $evidencePath): array 'evidence_snapshot', 'review_pack', 'decision_trail', - 'operation_proof', ]; return collect($evidencePath) @@ -1145,46 +1141,6 @@ private function acceptedRiskProofForReview(array $acceptedRisks): array ]; } - /** - * @return array{key:string,title:string,label:string,color:string,description:string,action_label:?string,action_url:?string} - */ - private function operationProofForReview(EnvironmentReview $review, ManagedEnvironment $tenant): array - { - $run = collect([ - $review->operationRun, - $review->evidenceSnapshot?->operationRun, - $review->currentExportReviewPack?->operationRun, - ])->first(fn (mixed $candidate): bool => $candidate instanceof OperationRun); - - if ($run instanceof OperationRun) { - $initiator = is_string($run->initiator_name) && trim($run->initiator_name) !== '' - ? trim($run->initiator_name) - : null; - - return [ - 'key' => 'operation_proof', - 'title' => __('localization.review.operation_proof'), - 'label' => __('localization.review.available'), - 'color' => 'info', - 'description' => $initiator === null - ? __('localization.review.operation_proof_available_description') - : __('localization.review.operation_proof_available_with_initiator_description', ['initiator' => $initiator]), - 'action_label' => OperationRunLinks::openLabel(), - 'action_url' => OperationRunLinks::tenantlessView($run), - ]; - } - - return [ - 'key' => 'operation_proof', - 'title' => __('localization.review.operation_proof'), - 'label' => __('localization.review.unavailable'), - 'color' => 'gray', - 'description' => __('localization.review.operation_proof_unavailable_description'), - 'action_label' => null, - 'action_url' => null, - ]; - } - /** * @param array{state:string,label:string,description:string} $packageAvailability * @return array{key:string,title:string,label:string,color:string,description:string,action_label:?string,action_url:?string} @@ -1507,13 +1463,6 @@ private function reviewPackPanelForReview( : __('localization.review.unavailable'), 'color' => 'gray', ], - [ - 'label' => __('localization.review.operation_proof'), - 'value' => $pack instanceof ReviewPack && $pack->operationRun instanceof OperationRun - ? OperationRunLinks::identifier($pack->operationRun) - : __('localization.review.operation_proof_unavailable'), - 'color' => $pack instanceof ReviewPack && $pack->operationRun instanceof OperationRun ? 'info' : 'gray', - ], ], ], [ @@ -1960,17 +1909,6 @@ private function successorReviewUrlForReview(EnvironmentReview $review, ManagedE ); } - private function operationUrlForReview(EnvironmentReview $review): ?string - { - $operationRun = $review->currentExportReviewPack?->operationRun ?? $review->operationRun; - - if (! $operationRun instanceof OperationRun) { - return null; - } - - return OperationRunLinks::tenantlessView((int) $operationRun->getKey()); - } - private function canManageReview(EnvironmentReview $review): bool { $user = auth()->user(); @@ -2484,20 +2422,13 @@ private function reviewOutputGuidanceForReview( ?string $reviewUrl, ?string $evidenceUrl, ): array { - $operationUrl = null; - $operationRun = $review->currentExportReviewPack?->operationRun ?? $review->operationRun; - - if ($operationRun instanceof OperationRun) { - $operationUrl = OperationRunLinks::tenantlessView((int) $operationRun->getKey()); - } - return ReviewPackOutputResolutionGuidance::fromReadiness( $this->reviewPackOutputReadinessForReview($review), [ 'download' => $downloadUrl, 'review' => $reviewUrl, 'evidence' => $evidenceUrl, - 'operation' => $operationUrl, + 'operation' => null, ], ); } @@ -2520,7 +2451,7 @@ private function reviewOutputResolutionCaseForReview(EnvironmentReview $review, 'evidence' => $tenant instanceof ManagedEnvironment ? $this->evidenceSnapshotUrlForReview($review, $tenant) : null, - 'operation' => $this->operationUrlForReview($review), + 'operation' => null, 'download' => $tenant instanceof ManagedEnvironment ? $this->reviewPackDownloadUrl($review, $tenant) : null, diff --git a/apps/platform/app/Filament/Resources/EnvironmentReviewResource.php b/apps/platform/app/Filament/Resources/EnvironmentReviewResource.php index a85edae8..0e570051 100644 --- a/apps/platform/app/Filament/Resources/EnvironmentReviewResource.php +++ b/apps/platform/app/Filament/Resources/EnvironmentReviewResource.php @@ -204,47 +204,6 @@ public static function infolist(Schema $schema): Schema ->columnSpanFull(), ]) ->columnSpanFull(), - Section::make(__('localization.review.review')) - ->schema([ - TextEntry::make('status') - ->label(__('localization.review.review_status')) - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus)) - ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus)) - ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)), - TextEntry::make('completeness_state') - ->label(__('localization.review.completeness')) - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness)) - ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness)) - ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)), - TextEntry::make('tenant.name')->label(__('localization.review.tenant')), - TextEntry::make('generated_at')->dateTime()->placeholder('—'), - TextEntry::make('published_at')->dateTime()->placeholder('—'), - TextEntry::make('evidenceSnapshot.id') - ->label(__('localization.review.evidence_snapshot')) - ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') - ->url(fn (EnvironmentReview $record): ?string => $record->evidenceSnapshot - ? EvidenceSnapshotResource::getUrl('view', ['record' => $record->evidenceSnapshot], tenant: $record->tenant) - : null), - TextEntry::make('currentExportReviewPack.id') - ->label(__('localization.review.current_export')) - ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') - ->url(fn (EnvironmentReview $record): ?string => $record->currentExportReviewPack - ? ReviewPackResource::getUrl('view', ['record' => $record->currentExportReviewPack], tenant: $record->tenant) - : null), - TextEntry::make('fingerprint') - ->copyable() - ->placeholder('—') - ->hidden(fn (): bool => static::isCustomerWorkspaceMode()) - ->columnSpanFull() - ->fontFamily('mono') - ->size(TextSize::ExtraSmall), - ]) - ->columns(2) - ->columnSpanFull(), Section::make(__('localization.review.output_guidance')) ->schema([ ViewEntry::make('output_guidance') @@ -263,6 +222,70 @@ public static function infolist(Schema $schema): Schema ->columnSpanFull(), ]) ->columnSpanFull(), + Section::make(__('localization.review.evidence_basis')) + ->schema([ + TextEntry::make('tenant.name')->label(__('localization.review.tenant')), + TextEntry::make('generated_at') + ->label(__('localization.review.generated_at')) + ->dateTime() + ->placeholder('—'), + TextEntry::make('published_at') + ->label(__('localization.review.published_at')) + ->dateTime() + ->placeholder('—'), + TextEntry::make('evidenceSnapshot.completeness_state') + ->label(__('localization.review.evidence_snapshot')) + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness)) + ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) + ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)) + ->placeholder(__('localization.review.unavailable')) + ->url(fn (EnvironmentReview $record): ?string => $record->evidenceSnapshot + ? EvidenceSnapshotResource::getUrl('view', ['record' => $record->evidenceSnapshot], tenant: $record->tenant) + : null), + TextEntry::make('currentExportReviewPack.status') + ->label(__('localization.review.current_export')) + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ReviewPackStatus)) + ->color(BadgeRenderer::color(BadgeDomain::ReviewPackStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::ReviewPackStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ReviewPackStatus)) + ->placeholder(__('localization.review.unavailable')) + ->url(fn (EnvironmentReview $record): ?string => $record->currentExportReviewPack + ? ReviewPackResource::getUrl('view', ['record' => $record->currentExportReviewPack], tenant: $record->tenant) + : null), + ]) + ->columns(2) + ->columnSpanFull(), + Section::make(__('localization.review.technical_details')) + ->schema([ + TextEntry::make('status') + ->label(__('localization.review.review_status')) + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus)) + ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)), + TextEntry::make('completeness_state') + ->label(__('localization.review.completeness')) + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness)) + ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness)) + ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)), + TextEntry::make('fingerprint') + ->copyable() + ->placeholder('—') + ->hidden(fn (): bool => static::isCustomerWorkspaceMode()) + ->columnSpanFull() + ->fontFamily('mono') + ->size(TextSize::ExtraSmall), + ]) + ->columns(2) + ->collapsible() + ->collapsed() + ->columnSpanFull(), Section::make(__('localization.review.sections')) ->schema([ RepeatableEntry::make('sections') diff --git a/apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php b/apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php index 5fa5e285..0b6cc464 100644 --- a/apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php +++ b/apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php @@ -132,7 +132,7 @@ public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state includes a Create snapshot CTA.') ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Clickable-row inspection stays primary while Expire snapshot remains grouped under More.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Evidence snapshots do not support bulk actions.') - ->satisfy(ActionSurfaceSlot::DetailHeader, 'View page exposes Refresh evidence as the primary action, keeps Expire snapshot visibly separated as danger, and renders operation/review-pack navigation in contextual related context.'); + ->satisfy(ActionSurfaceSlot::DetailHeader, 'View page exposes Refresh evidence as the primary action, keeps Expire snapshot visibly separated as danger, renders review-pack navigation in contextual related context, and demotes operation proof to technical details.'); } public static function getEloquentQuery(): Builder @@ -162,9 +162,10 @@ public static function infolist(Schema $schema): Schema ->columnSpanFull(), ]) ->columnSpanFull(), - Section::make('Snapshot') + Section::make('Evidence basis and readiness') ->schema([ TextEntry::make('status') + ->label('Evidence state') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceSnapshotStatus)) ->color(BadgeRenderer::color(BadgeDomain::EvidenceSnapshotStatus)) @@ -177,9 +178,36 @@ public static function infolist(Schema $schema): Schema ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)), - TextEntry::make('tenant.name')->label('ManagedEnvironment'), - TextEntry::make('generated_at')->dateTime()->placeholder('—'), - TextEntry::make('expires_at')->dateTime()->placeholder('—'), + TextEntry::make('tenant.name')->label('Environment'), + TextEntry::make('generated_at') + ->label('Captured at') + ->dateTime() + ->placeholder('—'), + TextEntry::make('expires_at') + ->label('Expires at') + ->dateTime() + ->placeholder('—'), + ]) + ->columns(2), + Section::make('Evidence coverage summary') + ->schema([ + TextEntry::make('summary.finding_count')->label('Findings')->placeholder('—'), + TextEntry::make('summary.report_count')->label('Reports')->placeholder('—'), + TextEntry::make('summary.missing_dimensions')->label(static::evidenceCompletenessCountLabel(EvidenceCompletenessState::Missing->value))->placeholder('—'), + TextEntry::make('summary.stale_dimensions')->label(static::evidenceCompletenessCountLabel(EvidenceCompletenessState::Stale->value))->placeholder('—'), + ]) + ->columns(2), + Section::make('Related review and report context') + ->schema([ + ViewEntry::make('related_context') + ->label('') + ->view('filament.infolists.entries.related-context') + ->state(fn (EvidenceSnapshot $record): array => static::relatedContextEntries($record)) + ->columnSpanFull(), + ]) + ->columnSpanFull(), + Section::make('Technical evidence details') + ->schema([ TextEntry::make('operationRun.id') ->label('Operation') ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') @@ -190,73 +218,70 @@ public static function infolist(Schema $schema): Schema ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()), TextEntry::make('previous_fingerprint')->copyable()->placeholder('—')->columnSpanFull()->fontFamily('mono') ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()), - ]) - ->columns(2), - Section::make('Summary') - ->schema([ - TextEntry::make('summary.finding_count')->label('Findings')->placeholder('—'), - TextEntry::make('summary.report_count')->label('Reports')->placeholder('—'), TextEntry::make('summary.operation_count')->label('Operations')->placeholder('—'), - TextEntry::make('summary.missing_dimensions')->label(static::evidenceCompletenessCountLabel(EvidenceCompletenessState::Missing->value))->placeholder('—'), - TextEntry::make('summary.stale_dimensions')->label(static::evidenceCompletenessCountLabel(EvidenceCompletenessState::Stale->value))->placeholder('—'), - ]) - ->columns(2), - Section::make('Related context') - ->schema([ - ViewEntry::make('related_context') - ->label('') - ->view('filament.infolists.entries.related-context') - ->state(fn (EvidenceSnapshot $record): array => static::relatedContextEntries($record)) - ->columnSpanFull(), ]) + ->columns(2) + ->collapsible() + ->collapsed() ->columnSpanFull(), Section::make('Evidence dimensions') ->schema([ RepeatableEntry::make('items') ->hiddenLabel() ->schema([ - TextEntry::make('artifact_source_family') - ->label('Source family') - ->badge() - ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_family')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_source_kind') - ->label('Source kind') - ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_kind')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_source_target') - ->label('Source target') - ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_target_kind')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_control_key') - ->label('Control') - ->state(fn (EvidenceSnapshotItem $record): ?string => static::artifactDescriptorNullableValue($record, 'control_key')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)) - ->placeholder('—'), - TextEntry::make('dimension_key')->label('Dimension') + TextEntry::make('dimension_key')->label('Evidence type') ->formatStateUsing(fn (string $state): string => Str::headline($state)), TextEntry::make('state') + ->label('Readiness') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness)) ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)), - TextEntry::make('source_kind')->label('Provider source detail') - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('freshness_at')->dateTime()->placeholder('—'), + TextEntry::make('freshness_at') + ->label('Evidence date') + ->dateTime() + ->placeholder('—'), ViewEntry::make('summary_payload_highlights') ->label('Summary') ->view('filament.infolists.entries.evidence-dimension-summary') ->state(fn (EvidenceSnapshotItem $record): array => static::dimensionSummaryPresentation($record)) ->columnSpanFull(), - ViewEntry::make('summary_payload_raw') - ->label('Raw summary JSON') - ->view('filament.infolists.entries.snapshot-json') - ->state(fn (EvidenceSnapshotItem $record): array => is_array($record->summary_payload) ? $record->summary_payload : []) - ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()) + Section::make('Technical dimension details') + ->schema([ + TextEntry::make('artifact_source_family') + ->label('Source family') + ->badge() + ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_family')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_source_kind') + ->label('Source kind') + ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_kind')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_source_target') + ->label('Source target') + ->state(fn (EvidenceSnapshotItem $record): string => static::artifactDescriptorValue($record, 'source_target_kind')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_control_key') + ->label('Control') + ->state(fn (EvidenceSnapshotItem $record): ?string => static::artifactDescriptorNullableValue($record, 'control_key')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)) + ->placeholder('—'), + TextEntry::make('source_kind')->label('Provider source detail') + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + ViewEntry::make('summary_payload_raw') + ->label('Raw summary JSON') + ->view('filament.infolists.entries.snapshot-json') + ->state(fn (EvidenceSnapshotItem $record): array => is_array($record->summary_payload) ? $record->summary_payload : []) + ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()) + ->columnSpanFull(), + ]) + ->columns(2) + ->collapsible() + ->collapsed() ->columnSpanFull(), ]) - ->columns(4), + ->columns(3), ]), ]); } @@ -268,20 +293,6 @@ public static function relatedContextEntries(EvidenceSnapshot $record): array { $entries = []; - if (! static::isCustomerWorkspaceFlow() && is_numeric($record->operation_run_id)) { - $entries[] = RelatedContextEntry::available( - key: 'operation_run', - label: 'Operation', - value: sprintf('#%d', (int) $record->operation_run_id), - secondaryValue: 'Open the latest evidence refresh operation.', - targetUrl: OperationRunLinks::tenantlessView((int) $record->operation_run_id), - targetKind: 'canonical_page', - priority: 10, - actionLabel: OperationRunLinks::openLabel(), - contextBadge: 'Operations', - )->toArray(); - } - $pack = $record->reviewPacks() ->latest('created_at') ->first(); @@ -812,8 +823,12 @@ private static function truthState(EvidenceSnapshot $record, bool $fresh = false ? static::truthEnvelope($record, true) : static::truthEnvelope($record); - return $presenter->surfaceStateFor($record, SurfaceCompressionContext::evidenceSnapshot(), $fresh) + $state = $presenter->surfaceStateFor($record, SurfaceCompressionContext::evidenceSnapshot(), $fresh) ?? $truth->toArray(static::compressedOutcome($record, $fresh)); + + unset($state['displayReference']); + + return $state; } private static function compressedOutcome(EvidenceSnapshot $record, bool $fresh = false): CompressedGovernanceOutcome diff --git a/apps/platform/app/Filament/Resources/ReviewPackResource.php b/apps/platform/app/Filament/Resources/ReviewPackResource.php index 5de6e397..c3b93f74 100644 --- a/apps/platform/app/Filament/Resources/ReviewPackResource.php +++ b/apps/platform/app/Filament/Resources/ReviewPackResource.php @@ -146,48 +146,70 @@ public static function infolist(Schema $schema): Schema ->columnSpanFull(), ]) ->columnSpanFull(), - Section::make('Status') + Section::make('Output guidance') + ->schema([ + ViewEntry::make('output_guidance') + ->hiddenLabel() + ->view('filament.infolists.entries.review-pack-output-guidance') + ->state(fn (ReviewPack $record): array => static::outputGuidanceState($record)) + ->columnSpanFull(), + ]) + ->columnSpanFull(), + Section::make('Pack readiness and contents') ->schema([ TextEntry::make('status') + ->label('Pack readiness') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ReviewPackStatus)) ->color(BadgeRenderer::color(BadgeDomain::ReviewPackStatus)) ->icon(BadgeRenderer::icon(BadgeDomain::ReviewPackStatus)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ReviewPackStatus)), - TextEntry::make('tenant.name')->label('ManagedEnvironment'), - TextEntry::make('generated_at')->dateTime()->placeholder('—'), - TextEntry::make('expires_at')->dateTime()->placeholder('—'), + TextEntry::make('tenant.name')->label('Environment'), + TextEntry::make('generated_at') + ->label('Generated') + ->dateTime() + ->placeholder('—'), + TextEntry::make('expires_at') + ->label('Expires') + ->dateTime() + ->placeholder('—'), TextEntry::make('file_size') - ->label('File size') + ->label('Download size') ->formatStateUsing(fn ($state): string => $state ? Number::fileSize((int) $state) : '—'), - TextEntry::make('sha256')->label('SHA-256')->copyable()->placeholder('—') - ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()), + TextEntry::make('summary.finding_count') + ->label('Findings included') + ->placeholder('—'), + TextEntry::make('summary.report_count') + ->label('Reports included') + ->placeholder('—'), + TextEntry::make('summary.evidence_resolution.outcome') + ->label('Evidence resolution') + ->placeholder('—'), + TextEntry::make('evidenceSnapshot.completeness_state') + ->label('Evidence basis') + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness)) + ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) + ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)) + ->placeholder('—') + ->url(fn (ReviewPack $record): ?string => static::evidenceSnapshotUrl($record)), + TextEntry::make('environmentReview.status') + ->label('Released review') + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus)) + ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)) + ->placeholder('—') + ->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant + ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $record->environmentReview], $record->tenant) + : null), ]) ->columns(2) ->columnSpanFull(), - Section::make('Summary') - ->schema([ - TextEntry::make('summary.finding_count')->label('Findings')->placeholder('—'), - TextEntry::make('summary.report_count')->label('Reports')->placeholder('—'), - TextEntry::make('summary.operation_count')->label('Operations')->placeholder('—'), - TextEntry::make('summary.data_freshness.permission_posture') - ->label('Permission posture freshness') - ->placeholder('—'), - TextEntry::make('summary.data_freshness.entra_admin_roles') - ->label('Entra admin roles freshness') - ->placeholder('—'), - TextEntry::make('summary.data_freshness.findings') - ->label('Findings freshness') - ->placeholder('—'), - TextEntry::make('summary.data_freshness.hardening') - ->label('Hardening freshness') - ->placeholder('—'), - ]) - ->columns(2) - ->columnSpanFull(), - - Section::make('Options') + Section::make('Technical pack details') ->schema([ TextEntry::make('options.include_pii') ->label('Include PII') @@ -195,21 +217,7 @@ public static function infolist(Schema $schema): Schema TextEntry::make('options.include_operations') ->label('Include operations') ->formatStateUsing(fn ($state): string => $state ? 'Yes' : 'No'), - ]) - ->columns(2) - ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()) - ->columnSpanFull(), - - Section::make('Metadata') - ->schema([ TextEntry::make('initiator.name')->label('Initiated by')->placeholder('—'), - TextEntry::make('environmentReview.id') - ->label('ManagedEnvironment review') - ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') - ->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant - ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $record->environmentReview], $record->tenant) - : null) - ->placeholder('—'), TextEntry::make('customer_workspace') ->label('Customer workspace') ->state(fn (): string => 'Open workspace') @@ -243,6 +251,21 @@ public static function infolist(Schema $schema): Schema ->openUrlInNewTab() ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()) ->placeholder('—'), + TextEntry::make('summary.operation_count')->label('Operations')->placeholder('—'), + TextEntry::make('summary.data_freshness.permission_posture') + ->label('Permission posture freshness') + ->placeholder('—'), + TextEntry::make('summary.data_freshness.entra_admin_roles') + ->label('Entra admin roles freshness') + ->placeholder('—'), + TextEntry::make('summary.data_freshness.findings') + ->label('Findings freshness') + ->placeholder('—'), + TextEntry::make('summary.data_freshness.hardening') + ->label('Hardening freshness') + ->placeholder('—'), + TextEntry::make('sha256')->label('SHA-256')->copyable()->placeholder('—') + ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()), TextEntry::make('fingerprint')->label('Fingerprint')->copyable()->placeholder('—') ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()), TextEntry::make('previous_fingerprint')->label('Previous fingerprint')->copyable()->placeholder('—') @@ -250,31 +273,9 @@ public static function infolist(Schema $schema): Schema TextEntry::make('created_at')->label('Created')->dateTime(), ]) ->columns(2) - ->columnSpanFull(), - - Section::make('Evidence snapshot') - ->schema([ - TextEntry::make('summary.evidence_resolution.outcome') - ->label('Resolution') - ->placeholder('—'), - TextEntry::make('evidenceSnapshot.id') - ->label('Snapshot') - ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') - ->url(fn (ReviewPack $record): ?string => static::evidenceSnapshotUrl($record)), - TextEntry::make('evidenceSnapshot.completeness_state') - ->label('Snapshot completeness') - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EvidenceCompleteness)) - ->color(BadgeRenderer::color(BadgeDomain::EvidenceCompleteness)) - ->icon(BadgeRenderer::icon(BadgeDomain::EvidenceCompleteness)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EvidenceCompleteness)) - ->placeholder('—'), - TextEntry::make('summary.evidence_resolution.snapshot_fingerprint') - ->label('Snapshot fingerprint') - ->copyable() - ->placeholder('—'), - ]) - ->columns(2) + ->collapsible() + ->collapsed() + ->hidden(fn (): bool => static::isCustomerWorkspaceFlow()) ->columnSpanFull(), ]); } @@ -482,6 +483,58 @@ private static function evidenceSnapshotUrl(ReviewPack $record): ?string : $url; } + /** + * @return array + */ + private static function outputGuidanceState(ReviewPack $record): array + { + $review = $record->environmentReview; + + if (! $review instanceof EnvironmentReview) { + return [ + 'label' => 'Review linkage unavailable', + 'color' => 'warning', + 'boundary_label' => 'Needs review', + 'boundary_color' => 'warning', + 'status_label' => 'Review linkage unavailable', + 'primary_reason' => 'No released review is linked to this pack.', + 'impact' => 'Treat this pack as technical evidence until the released review relationship is restored.', + 'next_step_label' => 'Review the pack metadata before sharing.', + 'detail_mode' => true, + 'suppress_primary_action_button' => true, + 'limitations' => [ + [ + 'label' => 'Released review missing', + 'severity' => 'warning', + 'reason' => 'The pack cannot be framed as a customer review output without a linked released review.', + 'details' => [], + ], + ], + 'limitation_summary' => '1 limitation requires review', + 'technical_details' => [], + ]; + } + + $tenant = $record->tenant; + $guidance = ReviewPackOutputResolutionGuidance::fromReadiness( + ReviewPackOutputResolutionGuidance::readinessForReview($review), + [ + 'download' => null, + 'review' => $tenant instanceof ManagedEnvironment + ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant) + : null, + 'evidence' => static::evidenceSnapshotUrl($record), + 'operation' => null, + ], + ); + + return array_replace($guidance, [ + 'detail_mode' => true, + 'suppress_primary_action_button' => true, + 'context_note' => 'Rendered-report and download actions stay in the page header when this pack is ready.', + ]); + } + /** * @param array $query */ diff --git a/apps/platform/app/Filament/Resources/StoredReportResource.php b/apps/platform/app/Filament/Resources/StoredReportResource.php index 965ac3cd..d0740325 100644 --- a/apps/platform/app/Filament/Resources/StoredReportResource.php +++ b/apps/platform/app/Filament/Resources/StoredReportResource.php @@ -191,46 +191,14 @@ public static function infolist(Schema $schema): Schema ]) ->columnSpanFull(), - Section::make('Artifact source') + Section::make('Report scope and readiness') ->schema([ - TextEntry::make('artifact_source_family') - ->label('Source family') - ->badge() - ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_family')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_source_kind') - ->label('Source kind') - ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_kind')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_source_target') - ->label('Source target') - ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_target_kind')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - TextEntry::make('artifact_control_key') - ->label('Control') - ->state(fn (StoredReport $record): ?string => static::artifactDescriptorNullableValue($record, 'control_key')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)) - ->placeholder('—'), - TextEntry::make('artifact_detector_key') - ->label('Detector') - ->state(fn (StoredReport $record): ?string => static::artifactDescriptorNullableValue($record, 'detector_key')) - ->placeholder('—'), - TextEntry::make('artifact_provider_key') - ->label('Provider') - ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'provider_key')) - ->formatStateUsing(fn (string $state): string => Str::headline($state)), - ]) - ->columns(2) - ->columnSpanFull(), - - Section::make('Stored report') - ->schema([ - TextEntry::make('display_reference') - ->label('Artifact reference') - ->state(fn (StoredReport $record): string => static::displayReference($record)), TextEntry::make('report_type') - ->label('Provider report type') + ->label('Report type') ->formatStateUsing(fn (string $state): string => static::reportFamilyReportLabel($state)), + TextEntry::make('tenant.name') + ->label('Environment') + ->placeholder('—'), TextEntry::make('measured_at') ->label('Measured at') ->state(fn (StoredReport $record): ?CarbonInterface => static::measuredAt($record)) @@ -252,18 +220,6 @@ public static function infolist(Schema $schema): Schema ->color(BadgeRenderer::color(BadgeDomain::GovernanceArtifactRetention)) ->icon(BadgeRenderer::icon(BadgeDomain::GovernanceArtifactRetention)) ->iconColor(BadgeRenderer::iconColor(BadgeDomain::GovernanceArtifactRetention)), - TextEntry::make('fingerprint') - ->label('Integrity anchor') - ->copyable() - ->fontFamily('mono') - ->placeholder('—') - ->columnSpanFull(), - TextEntry::make('previous_fingerprint') - ->label('Previous fingerprint') - ->copyable() - ->fontFamily('mono') - ->placeholder('—') - ->columnSpanFull(), ]) ->columns(2) ->columnSpanFull(), @@ -303,6 +259,55 @@ public static function infolist(Schema $schema): Schema ->visible(fn (StoredReport $record): bool => $record->report_type === StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES) ->columnSpanFull(), + Section::make('Technical report details') + ->schema([ + TextEntry::make('display_reference') + ->label('Artifact reference') + ->state(fn (StoredReport $record): string => static::displayReference($record)), + TextEntry::make('artifact_source_family') + ->label('Source family') + ->badge() + ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_family')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_source_kind') + ->label('Source kind') + ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_kind')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_source_target') + ->label('Source target') + ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'source_target_kind')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('artifact_control_key') + ->label('Control') + ->state(fn (StoredReport $record): ?string => static::artifactDescriptorNullableValue($record, 'control_key')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)) + ->placeholder('—'), + TextEntry::make('artifact_detector_key') + ->label('Detector') + ->state(fn (StoredReport $record): ?string => static::artifactDescriptorNullableValue($record, 'detector_key')) + ->placeholder('—'), + TextEntry::make('artifact_provider_key') + ->label('Provider') + ->state(fn (StoredReport $record): string => static::artifactDescriptorValue($record, 'provider_key')) + ->formatStateUsing(fn (string $state): string => Str::headline($state)), + TextEntry::make('fingerprint') + ->label('Integrity anchor') + ->copyable() + ->fontFamily('mono') + ->placeholder('—') + ->columnSpanFull(), + TextEntry::make('previous_fingerprint') + ->label('Previous fingerprint') + ->copyable() + ->fontFamily('mono') + ->placeholder('—') + ->columnSpanFull(), + ]) + ->columns(2) + ->collapsible() + ->collapsed() + ->columnSpanFull(), + Section::make('Raw payload') ->schema([ ViewEntry::make('payload') @@ -495,7 +500,10 @@ public static function lifecycleState(StoredReport $report): string */ public static function truthState(StoredReport $report): array { - return app(ArtifactTruthPresenter::class)->forStoredReportFresh($report)->toArray(); + $state = app(ArtifactTruthPresenter::class)->forStoredReportFresh($report)->toArray(); + unset($state['displayReference']); + + return $state; } public static function measuredAt(StoredReport $report): ?CarbonInterface diff --git a/apps/platform/lang/de/localization.php b/apps/platform/lang/de/localization.php index bdec5c05..0570341b 100644 --- a/apps/platform/lang/de/localization.php +++ b/apps/platform/lang/de/localization.php @@ -761,7 +761,7 @@ 'open_operation_proof' => 'Operationsnachweis öffnen', 'open_evidence_basis' => 'Evidence-Basis öffnen', 'output_guidance_detail_mode_note' => 'Sie befinden sich bereits auf der Review-Detailseite für diesen Output. Nutzen Sie die Einschränkungen und technischen Details unten, um Blocker, Evidence-Status und den aktuellen Export zu prüfen.', - 'output_action_help_publication_blocked' => 'Die primäre Aktion öffnet die Review-Detailseite mit Blockern, Evidence-Status und nächsten Schritten. Die unterstützenden Aktionen laden das aktuelle Paket herunter oder springen zum Evidence-Snapshot und Operationsnachweis.', + 'output_action_help_publication_blocked' => 'Die primäre Aktion öffnet die Review-Detailseite mit Blockern, Evidence-Status und nächsten Schritten. Die unterstützenden Aktionen laden das aktuelle Paket herunter oder springen zur Evidence-Basis.', 'output_action_help_published_with_limitations' => 'Die primäre Aktion öffnet die Review-Detailseite zur aktuellen Einschränkung. Die unterstützenden Aktionen laden das aktuelle Paket herunter oder springen zur Evidence-Basis.', 'output_action_help_internal_only' => 'Die primäre Aktion öffnet die Review-Detailseite für PII- und Redaktionsprüfungen. Die unterstützenden Aktionen erlauben den Download des internen Pakets oder öffnen den veröffentlichten Datensatz.', 'output_action_help_export_not_ready' => 'Die primäre Aktion öffnet die Review-Detailseite mit den aktuellen Output-Einschränkungen. Die unterstützenden Aktionen springen zur Evidence-Basis oder zum veröffentlichten Review.', diff --git a/apps/platform/lang/en/localization.php b/apps/platform/lang/en/localization.php index 1207856d..58ecaa4f 100644 --- a/apps/platform/lang/en/localization.php +++ b/apps/platform/lang/en/localization.php @@ -761,7 +761,7 @@ 'open_operation_proof' => 'Open operation proof', 'open_evidence_basis' => 'Open evidence basis', 'output_guidance_detail_mode_note' => 'You are already on the review detail for this output. Use the limitations and technical details below to inspect blockers, evidence state, and the current export.', - 'output_action_help_publication_blocked' => 'The primary action opens the review detail with blockers, evidence status, and next steps. The supporting actions download the current package or jump to the evidence snapshot and operation proof.', + 'output_action_help_publication_blocked' => 'The primary action opens the review detail with blockers, evidence status, and next steps. The supporting actions download the current package or jump to the evidence basis.', 'output_action_help_published_with_limitations' => 'The primary action opens the review detail for the current limitation. The supporting actions download the current package or jump to the evidence basis.', 'output_action_help_internal_only' => 'The primary action opens the review detail for PII and redaction checks. The supporting actions let you download the internal package or review the released record.', 'output_action_help_export_not_ready' => 'The primary action opens the review detail with the current output limitations. The supporting actions jump to the evidence basis or the released review.', diff --git a/apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php b/apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php new file mode 100644 index 00000000..d368e7d6 --- /dev/null +++ b/apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php @@ -0,0 +1,271 @@ +browser()->timeout(60_000); + +uses(RefreshDatabase::class); + +beforeEach(function (): void { + Storage::fake('exports'); +}); + +it('Spec372 smokes customer and auditor review output surfaces with screenshots', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); + $tenant->forceFill(['name' => 'Spec372 Browser Environment'])->save(); + + [ + 'review' => $review, + 'snapshot' => $snapshot, + 'pack' => $pack, + 'report' => $report, + ] = spec372BrowserFixture($tenant, $user); + + $workspacePath = spec372BrowserPath(CustomerReviewWorkspace::environmentFilterUrl($tenant)); + $reviewPath = spec372BrowserPath(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant)); + $packPath = spec372BrowserPath(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin')); + $reportPath = spec372BrowserPath(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant, panel: 'admin')); + $evidencePath = spec372BrowserPath(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin')); + + $page = visit(spec372BrowserLoginUrl($user, $tenant, $workspacePath)) + ->resize(1440, 1100) + ->waitForText('Customer Review Workspace') + ->assertSee('Evidence path') + ->assertSee('Review pack state') + ->assertDontSee('Operation proof') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, '001-customer-review-workspace-after'); + spec372BrowserCopyScreenshot('001-customer-review-workspace-after'); + + $page = visit($reviewPath) + ->resize(1440, 1100) + ->waitForText('Outcome summary') + ->assertSee('Output guidance') + ->assertSee('Executive posture') + ->assertSee('Evidence basis') + ->assertSee('Technical details') + ->assertScript("document.body.textContent.indexOf('Output guidance') < document.body.textContent.indexOf('Technical details')", true) + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, '002-environment-review-view-after'); + spec372BrowserCopyScreenshot('002-environment-review-view-after'); + + $page = visit($packPath) + ->resize(1440, 1100) + ->waitForText('Outcome summary') + ->assertSee('Output guidance') + ->assertSee('Pack readiness and contents') + ->assertSee('Technical pack details') + ->assertScript("document.body.textContent.indexOf('Pack readiness and contents') < document.body.textContent.indexOf('Technical pack details')", true) + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, '003-review-pack-view-after'); + spec372BrowserCopyScreenshot('003-review-pack-view-after'); + + $page = visit($reportPath) + ->resize(1440, 1100) + ->waitForText('Outcome summary') + ->assertSee('Report scope and readiness') + ->assertSee('Permission posture summary') + ->assertSee('Technical report details') + ->assertScript("document.body.textContent.indexOf('Permission posture summary') < document.body.textContent.indexOf('Technical report details')", true) + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, '004-stored-report-view-after'); + spec372BrowserCopyScreenshot('004-stored-report-view-after'); + + $page = visit($evidencePath) + ->resize(1440, 1100) + ->waitForText('Outcome summary') + ->assertSee('Evidence basis and readiness') + ->assertSee('Related review and report context') + ->assertSee('Technical evidence details') + ->assertSee('Evidence dimensions') + ->assertScript("document.body.textContent.indexOf('Related review and report context') < document.body.textContent.indexOf('Technical evidence details')", true) + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, '005-evidence-snapshot-view-after-or-blocked'); + spec372BrowserCopyScreenshot('005-evidence-snapshot-view-after-or-blocked'); + + $page + ->resize(430, 900) + ->assertScript('document.documentElement.scrollWidth <= window.innerWidth', true) + ->assertNoJavaScriptErrors() + ->screenshot(true, '006-evidence-snapshot-view-mobile'); + spec372BrowserCopyScreenshot('006-evidence-snapshot-view-mobile'); +}); + +/** + * @return array{review: EnvironmentReview, snapshot: EvidenceSnapshot, pack: ReviewPack, report: StoredReport, run: OperationRun} + */ +function spec372BrowserFixture(ManagedEnvironment $tenant, User $user): array +{ + $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0); + $run = OperationRun::factory() + ->forTenant($tenant) + ->withUser($user) + ->create([ + 'type' => OperationRunType::ReviewPackGenerate->value, + 'status' => OperationRunStatus::Completed->value, + 'initiator_name' => 'Spec372 Browser Operator', + ]); + + $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); + $review->forceFill([ + 'status' => EnvironmentReviewStatus::Published->value, + 'generated_at' => now()->subHours(2), + 'published_at' => now()->subHour(), + 'published_by_user_id' => (int) $user->getKey(), + 'operation_run_id' => (int) $run->getKey(), + ])->save(); + $review = markEnvironmentReviewCustomerSafeReady($review); + + Storage::disk('exports')->put('review-packs/spec372-browser-review-pack.zip', 'PK-spec372-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(), + 'operation_run_id' => (int) $run->getKey(), + 'initiated_by_user_id' => (int) $user->getKey(), + 'file_path' => 'review-packs/spec372-browser-review-pack.zip', + 'file_disk' => 'exports', + 'summary' => [ + 'finding_count' => 1, + 'report_count' => 2, + 'operation_count' => 1, + 'evidence_resolution' => ['outcome' => 'complete'], + ], + 'options' => [ + 'include_pii' => false, + 'include_operations' => true, + ], + ]); + + $review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save(); + + $report = StoredReport::factory() + ->permissionPosture([ + 'posture_score' => 78, + '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, + ]); + + $snapshot->forceFill([ + 'operation_run_id' => (int) $run->getKey(), + 'summary' => array_replace(is_array($snapshot->summary) ? $snapshot->summary : [], [ + 'finding_count' => 1, + 'report_count' => 2, + 'operation_count' => 1, + 'missing_dimensions' => 0, + 'stale_dimensions' => 0, + ]), + ])->save(); + + EvidenceSnapshotItem::query()->updateOrCreate( + [ + 'evidence_snapshot_id' => (int) $snapshot->getKey(), + 'dimension_key' => 'permission_posture', + ], + [ + 'workspace_id' => (int) $snapshot->workspace_id, + 'managed_environment_id' => (int) $snapshot->managed_environment_id, + 'state' => EvidenceCompletenessState::Complete->value, + 'required' => true, + 'source_kind' => 'stored_report', + 'source_record_type' => 'stored_report', + 'source_record_id' => (string) $report->getKey(), + 'measured_at' => now(), + 'freshness_at' => now(), + 'summary_payload' => [ + 'required_count' => 4, + 'granted_count' => 3, + 'posture_score' => 78, + 'payload' => [ + 'missing_permissions' => ['DeviceManagementApps.ReadWrite.All'], + ], + ], + 'sort_order' => 10, + ], + ); + + return [ + 'review' => $review->refresh(), + 'snapshot' => $snapshot->refresh(), + 'pack' => $pack->refresh(), + 'report' => $report->refresh(), + 'run' => $run, + ]; +} + +function spec372BrowserLoginUrl(User $user, ManagedEnvironment $tenant, string $redirect): string +{ + return route('admin.local.smoke-login', [ + 'email' => $user->email, + 'tenant' => $tenant->external_id, + 'workspace' => $tenant->workspace->slug, + 'redirect' => $redirect, + ]); +} + +function spec372BrowserPath(string $url): string +{ + $path = parse_url($url, PHP_URL_PATH) ?: '/admin'; + $query = parse_url($url, PHP_URL_QUERY); + + return is_string($query) && $query !== '' ? $path.'?'.$query : $path; +} + +function spec372BrowserCopyScreenshot(string $name): void +{ + $filename = $name.'.png'; + $source = base_path('tests/Browser/Screenshots/'.$filename); + $targetDirectory = repo_path('specs/372-customer-auditor-surface-safety-pass/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.$filename); + } +} diff --git a/apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php b/apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php index dc635ea3..980a7b64 100644 --- a/apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php +++ b/apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php @@ -204,7 +204,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void $this->actingAs($user) ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin')) ->assertOk() - ->assertSee('Related context') + ->assertSee('Related review and report context') ->assertSee('Review pack'); $tenant->makeCurrent(); @@ -246,7 +246,8 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void ->all()) ->toEqualCanonicalizing(['refresh_evidence', 'expire_snapshot']) ->and(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all()) - ->toContain('operation_run', 'review_pack'); + ->toContain('review_pack') + ->not->toContain('operation_run'); }); it('keeps evidence snapshot detail accessible for readonly members while suspended read-only', function (): void { @@ -301,8 +302,8 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void ->assertOk() ->assertSee('Outcome summary') ->assertDontSee('Artifact truth') - ->assertSee('Artifact reference') - ->assertSee('Evidence snapshot #'.$snapshot->getKey()) + ->assertSee('Evidence basis and readiness') + ->assertDontSee('Evidence snapshot #'.$snapshot->getKey()) ->assertSee('Lifecycle') ->assertSee('Current') ->assertSee('Retention') diff --git a/apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php b/apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php new file mode 100644 index 00000000..843c9642 --- /dev/null +++ b/apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php @@ -0,0 +1,335 @@ +forceFill(['name' => 'Spec372 Customer Workspace'])->save(); + + spec372ReviewOutputFixture($tenant, $user); + + $component = spec372WorkspaceComponent($user, $tenant) + ->assertSee('What is the current review pack output state?') + ->assertSee('Evidence path') + ->assertSee('Review pack state') + ->assertDontSee('Operation proof') + ->assertDontSee('Spec372 Operator'); + + $html = $component->html(); + + expect(substr_count($html, 'data-testid="customer-review-primary-action"'))->toBe(1) + ->and($html)->toContain('data-testid="customer-review-diagnostics"') + ->and($html)->not->toContain('data-testid="customer-review-diagnostics" open'); +}); + +it('Spec372 renders environment review output guidance before technical metadata', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + ['review' => $review] = spec372ReviewOutputFixture($tenant, $user); + + setAdminEnvironmentContext($tenant); + + $response = $this->actingAs($user) + ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant)) + ->assertOk(); + + $content = $response->getContent(); + + spec372AssertOrdered($content, [ + 'Outcome summary', + 'Output guidance', + 'Executive posture', + 'Evidence basis', + 'Technical details', + 'Sections', + ]); + + expect(spec372ContentBetween($content, 'Evidence basis', 'Technical details')) + ->not->toContain('spec372-review-fingerprint') + ->not->toContain('Review status'); +}); + +it('Spec372 renders review pack readiness and limitations before storage and operation metadata', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + ['pack' => $pack] = spec372ReviewOutputFixture($tenant, $user); + + setAdminEnvironmentContext($tenant); + + $response = $this->actingAs($user) + ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin')) + ->assertOk(); + + $content = $response->getContent(); + + spec372AssertOrdered($content, [ + 'Outcome summary', + 'Output guidance', + 'Pack readiness and contents', + 'Technical pack details', + ]); + + expect(spec372ContentBetween($content, 'Pack readiness and contents', 'Technical pack details')) + ->not->toContain('SHA-256') + ->not->toContain('spec372-pack-fingerprint') + ->not->toContain('Spec372 Operator'); +}); + +it('Spec372 renders stored report scope and summary before source descriptors and raw payload', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $report = spec372StoredReport($tenant); + + setAdminEnvironmentContext($tenant); + + $response = $this->actingAs($user) + ->get(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant, panel: 'admin')) + ->assertOk(); + + $content = $response->getContent(); + + spec372AssertOrdered($content, [ + 'Outcome summary', + 'Report scope and readiness', + 'Permission posture summary', + 'Technical report details', + 'Raw payload', + ]); + + expect(spec372ContentBetween($content, 'Report scope and readiness', 'Technical report details')) + ->not->toContain('Source family') + ->not->toContain('Integrity anchor') + ->not->toContain('spec372-stored-report-fingerprint'); +}); + +it('Spec372 renders evidence snapshot proof before diagnostics and omits operation links from related context', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + ['snapshot' => $snapshot] = spec372ReviewOutputFixture($tenant, $user); + spec372EvidenceSnapshotItem($snapshot); + + setAdminEnvironmentContext($tenant); + + $response = $this->actingAs($user) + ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin')) + ->assertOk(); + + $content = $response->getContent(); + + spec372AssertOrdered($content, [ + 'Outcome summary', + 'Evidence basis and readiness', + 'Evidence coverage summary', + 'Related review and report context', + 'Technical evidence details', + 'Evidence dimensions', + ]); + + expect(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all()) + ->not->toContain('operation_run') + ->and(spec372ContentBetween($content, 'Related review and report context', 'Technical evidence details')) + ->not->toContain('Operation') + ->not->toContain('spec372-evidence-fingerprint'); +}); + +it('Spec372 preserves scoped record access as not found for the wrong environment', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $foreignTenant = ManagedEnvironment::factory()->active()->create(['name' => 'Spec372 Foreign Environment']); + ['review' => $foreignReview] = spec372ReviewOutputFixture($foreignTenant, $user); + + setAdminEnvironmentContext($tenant); + + $this->actingAs($user) + ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $foreignReview], $tenant)) + ->assertNotFound(); +}); + +/** + * @return array{review: EnvironmentReview, snapshot: EvidenceSnapshot, pack: ReviewPack, run: OperationRun} + */ +function spec372ReviewOutputFixture(ManagedEnvironment $tenant, User $user): array +{ + $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0); + $run = OperationRun::factory() + ->forTenant($tenant) + ->withUser($user) + ->create([ + 'type' => OperationRunType::ReviewPackGenerate->value, + 'status' => OperationRunStatus::Completed->value, + 'initiator_name' => 'Spec372 Operator', + ]); + + $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); + $review->forceFill([ + 'status' => EnvironmentReviewStatus::Published->value, + 'generated_at' => now()->subHours(2), + 'published_at' => now()->subHour(), + 'published_by_user_id' => (int) $user->getKey(), + 'operation_run_id' => (int) $run->getKey(), + 'fingerprint' => 'spec372-review-fingerprint', + ])->save(); + $review = markEnvironmentReviewCustomerSafeReady($review); + + Storage::disk('exports')->put('review-packs/spec372-review-pack.zip', 'PK-spec372'); + + $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(), + 'operation_run_id' => (int) $run->getKey(), + 'initiated_by_user_id' => (int) $user->getKey(), + 'file_path' => 'review-packs/spec372-review-pack.zip', + 'file_disk' => 'exports', + 'fingerprint' => 'spec372-pack-fingerprint', + 'summary' => [ + 'finding_count' => 1, + 'report_count' => 2, + 'operation_count' => 1, + 'evidence_resolution' => [ + 'outcome' => 'complete', + 'snapshot_fingerprint' => 'spec372-evidence-fingerprint', + ], + ], + 'options' => [ + 'include_pii' => false, + 'include_operations' => true, + ], + ]); + + $review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save(); + $snapshot->forceFill([ + 'operation_run_id' => (int) $run->getKey(), + 'fingerprint' => 'spec372-evidence-fingerprint', + 'summary' => array_replace(is_array($snapshot->summary) ? $snapshot->summary : [], [ + 'finding_count' => 1, + 'report_count' => 2, + 'operation_count' => 1, + 'missing_dimensions' => 0, + 'stale_dimensions' => 0, + ]), + ])->save(); + + return [ + 'review' => $review->refresh(), + 'snapshot' => $snapshot->refresh(), + 'pack' => $pack->refresh(), + 'run' => $run, + ]; +} + +function spec372StoredReport(ManagedEnvironment $tenant): StoredReport +{ + return StoredReport::factory() + ->permissionPosture([ + 'posture_score' => 78, + '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' => 'spec372-stored-report-fingerprint', + ]); +} + +function spec372EvidenceSnapshotItem(EvidenceSnapshot $snapshot): EvidenceSnapshotItem +{ + return EvidenceSnapshotItem::query()->updateOrCreate( + [ + 'evidence_snapshot_id' => (int) $snapshot->getKey(), + 'dimension_key' => 'permission_posture', + ], + [ + 'workspace_id' => (int) $snapshot->workspace_id, + 'managed_environment_id' => (int) $snapshot->managed_environment_id, + 'state' => EvidenceCompletenessState::Complete->value, + 'required' => true, + 'source_kind' => 'stored_report', + 'source_record_type' => 'stored_report', + 'source_record_id' => 'spec372-stored-report', + 'source_fingerprint' => 'spec372-item-source-fingerprint', + 'measured_at' => now(), + 'freshness_at' => now(), + 'summary_payload' => [ + 'required_count' => 4, + 'granted_count' => 3, + 'posture_score' => 78, + 'payload' => [ + 'missing_permissions' => ['DeviceManagementApps.ReadWrite.All'], + ], + ], + 'sort_order' => 10, + ], + ); +} + +function spec372WorkspaceComponent(User $user, ManagedEnvironment $tenant): mixed +{ + $workspaceId = (int) $tenant->workspace_id; + + session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); + setAdminPanelContext(); + + return Livewire::withQueryParams(['environment_id' => (int) $tenant->getKey()]) + ->actingAs($user) + ->test(CustomerReviewWorkspace::class); +} + +/** + * @param list $needles + */ +function spec372AssertOrdered(string $content, array $needles): void +{ + $lastPosition = -1; + + foreach ($needles as $needle) { + $position = strpos($content, $needle, $lastPosition + 1); + + expect($position)->not->toBeFalse(); + expect((int) $position)->toBeGreaterThan($lastPosition); + + $lastPosition = (int) $position; + } +} + +function spec372ContentBetween(string $content, string $startMarker, string $endMarker): string +{ + $start = strpos($content, $startMarker); + $end = strpos($content, $endMarker, $start === false ? 0 : (int) $start); + + expect($start)->not->toBeFalse(); + expect($end)->not->toBeFalse(); + expect((int) $end)->toBeGreaterThan((int) $start); + + return substr($content, (int) $start, (int) $end - (int) $start); +} diff --git a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php index a851ff05..b1b6f9b5 100644 --- a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php +++ b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php @@ -160,7 +160,7 @@ ->actingAs($user) ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()]) ->assertSee('Outcome summary') - ->assertActionVisible('download_current_review_pack') + ->assertActionVisible('open_current_rendered_report') ->assertActionDoesNotExist('publish_review') ->assertActionDoesNotExist('refresh_review') ->assertActionDoesNotExist('create_next_review') diff --git a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php index 651746fd..1aaeb2b0 100644 --- a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php +++ b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php @@ -60,6 +60,11 @@ function suspendCustomerReviewWorkspacePackAccessWorkspace(ManagedEnvironment $t 'include_pii' => false, 'include_operations' => true, ], + 'summary' => [ + 'control_interpretation' => [ + 'non_certification_disclosure' => 'TenantPilot interprets available evidence for review readiness. This is not a certification, legal attestation, or compliance guarantee.', + ], + ], 'expires_at' => now()->addDay(), ]); @@ -111,6 +116,11 @@ function suspendCustomerReviewWorkspacePackAccessWorkspace(ManagedEnvironment $t 'include_pii' => false, 'include_operations' => true, ], + 'summary' => [ + 'control_interpretation' => [ + 'non_certification_disclosure' => 'TenantPilot interprets available evidence for review readiness. This is not a certification, legal attestation, or compliance guarantee.', + ], + ], 'expires_at' => now()->addDay(), ]); diff --git a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php index 68244d56..464e6a4d 100644 --- a/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php +++ b/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php @@ -205,7 +205,7 @@ ->assertSee('Expired') ->assertSee('Pending approval') ->assertSee('Needs review') - ->assertSee('Operation proof') + ->assertDontSee('Operation proof') ->assertSee('Customer-safe follow-ups') ->assertSee('Review package index') ->assertSee('Disclosure rule') @@ -366,7 +366,7 @@ ->assertSee('What is the current review pack output state?') ->assertSee('Output not customer-ready') ->assertSee('Review blockers are still recorded for this output.') - ->assertSee('No operation proof linked') + ->assertDontSee('No operation proof linked') ->assertSee('Export ready') ->assertDontSee('Ready to share') ->assertSee('Download review pack with limitations'); diff --git a/apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php b/apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php index 5ceea1b2..c254bf42 100644 --- a/apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php +++ b/apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php @@ -4,8 +4,8 @@ use App\Filament\Resources\StoredReportResource; use App\Filament\Resources\StoredReportResource\Pages\ViewStoredReport; -use App\Models\StoredReport; use App\Models\ManagedEnvironment; +use App\Models\StoredReport; use Illuminate\Foundation\Testing\RefreshDatabase; use Livewire\Livewire; @@ -65,7 +65,7 @@ function storedReportDetailHeaderActionNames(ViewStoredReport $page): array ->assertOk() ->assertSeeInOrder([ 'Outcome summary', - 'Stored report', + 'Report scope and readiness', 'Permission posture summary', 'Posture score', '75', @@ -76,6 +76,7 @@ function storedReportDetailHeaderActionNames(ViewStoredReport $page): array 'Missing permissions', '1', 'DeviceManagementApps.ReadWrite.All', + 'Technical report details', 'Raw payload', ]) ->assertSee('Current') diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md b/docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md index 4a0d844e..2a2e9d77 100644 --- a/docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md @@ -144,3 +144,17 @@ ## Spec 356 Follow-up - the workspace continues to state review-pack readiness and download truth directly - rendered stakeholder-report launch is deferred to the released-review detail surface so the hub does not duplicate the dominant handoff - customer-safe wording now states explicitly that the report can be opened from review detail when the current pack supports it + +## Spec 372 Follow-up + +Spec 372 keeps the existing workspace layout but tightens the customer/auditor default evidence path. + +- operation proof is no longer part of the default evidence path or review-pack side panel +- the first decision card remains one-primary-action-first with supporting actions below it +- evidence snapshot, review pack, and decision trail remain visible before diagnostics +- diagnostics and technical details stay collapsed + +### Browser proof + +- Spec372 screenshot: `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png` +- Browser smoke verified no JavaScript errors, no console logs, and no default `Operation proof` text in the workspace. diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-040-environment-review-detail.md b/docs/ui-ux-enterprise-audit/page-reports/ui-040-environment-review-detail.md index f425a4af..fd74f7d1 100644 --- a/docs/ui-ux-enterprise-audit/page-reports/ui-040-environment-review-detail.md +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-040-environment-review-detail.md @@ -75,3 +75,18 @@ ## Spec 356 Follow-up ## Target Direction Keep this surface audit- and evidence-oriented. If future work broadens it beyond the review-output path, that should happen through a dedicated detail-surface spec rather than hidden incremental drift. + +## Spec 372 Follow-up + +Spec 372 productizes the customer/auditor reading order on the existing Filament resource detail. + +- `Outcome summary` and `Output guidance` now lead the page +- executive posture and evidence basis appear before technical metadata +- review status, completeness, and fingerprint moved into collapsed `Technical details` +- section payload details remain collapsed below the main proof hierarchy +- existing refresh, publish, create-next, archive, export, rendered-report, confirmation, authorization, and audit behavior is unchanged + +### Browser proof + +- Spec372 screenshot: `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png` +- Browser smoke verified section ordering and no JavaScript errors or console logs. diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-042-review-pack-detail.md b/docs/ui-ux-enterprise-audit/page-reports/ui-042-review-pack-detail.md index 684fcae7..21abe5bf 100644 --- a/docs/ui-ux-enterprise-audit/page-reports/ui-042-review-pack-detail.md +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-042-review-pack-detail.md @@ -49,3 +49,18 @@ ## Spec 356 Follow-up ## Target Direction Keep this surface artifact-truth-first and narrowly scoped. Future work should deepen proof hierarchy and browser evidence, not invent a second portal or artifact family. + +## Spec 372 Follow-up + +Spec 372 keeps the existing rendered-report/download action model and reorganizes the detail content. + +- `Outcome summary` and `Output guidance` now lead the page +- `Pack readiness and contents` owns the first artifact-proof block +- evidence basis and released-review links appear before storage/operation metadata +- options, initiator, customer-workspace link, operation link/count, freshness, SHA, and fingerprints moved into collapsed `Technical pack details` +- technical pack details are hidden entirely in customer-workspace flow + +### Browser proof + +- Spec372 screenshot: `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png` +- Browser smoke verified readiness before technical details and no JavaScript errors or console logs. diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-046-evidence-snapshot-detail.md b/docs/ui-ux-enterprise-audit/page-reports/ui-046-evidence-snapshot-detail.md new file mode 100644 index 00000000..00d7eeda --- /dev/null +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-046-evidence-snapshot-detail.md @@ -0,0 +1,33 @@ +# UI-046 Evidence Snapshot Detail + +| Field | Value | +| --- | --- | +| Route | `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` | +| Source | `EvidenceSnapshotResource::view` | +| Area / scope | Evidence / audit | +| Archetype | Evidence / Audit | +| Design depth | Strategic Surface | +| Repo truth | browser-verified in Spec 372 | +| 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. | + +## First Five Seconds + +The page should answer what evidence was captured, whether it is complete/current, and which review/report context it supports before showing support diagnostics. + +## Productization Review + +- Evidence-first: evidence state, completeness, environment, captured/expires dates, and coverage counts appear before diagnostics. +- Context: related review/report context now points to review pack and customer workspace, not OperationRun proof. +- Customer/auditor safety: raw source descriptors, fingerprints, operation count, and raw summary JSON are collapsed. +- Diagnostics: technical evidence details and technical dimension details remain available for authorized operators. + +## Dangerous Actions + +Refresh and expire actions are existing header actions. Expire remains destructive/high-impact and confirmation/authorization/audit behavior is preserved by existing resource tests. + +## Spec 372 Follow-up + +- Evidence Snapshot is no longer unresolved for this fixture. +- OperationRun related-context entry was removed. +- Browser smoke verified desktop and mobile rendering, section ordering, no JavaScript errors, no console logs, and no mobile horizontal overflow. diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-048-stored-report-detail.md b/docs/ui-ux-enterprise-audit/page-reports/ui-048-stored-report-detail.md new file mode 100644 index 00000000..3a2d2198 --- /dev/null +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-048-stored-report-detail.md @@ -0,0 +1,32 @@ +# UI-048 Stored Report Detail + +| Field | Value | +| --- | --- | +| Route | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}` | +| Source | `StoredReportResource::view` | +| Area / scope | Evidence / audit | +| Archetype | Evidence / Audit | +| Design depth | Strategic Surface | +| Repo truth | browser-verified in Spec 372 | +| 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. | + +## First Five Seconds + +The page should answer what report this is, which environment it belongs to, when it was measured, and which summary matters before exposing source descriptors or raw payload. + +## Productization Review + +- Report-first: `Report scope and readiness` now follows the outcome summary. +- Evidence-first: permission posture and Entra admin role summaries appear before technical report metadata. +- Customer/auditor safety: artifact reference, source family/kind/target, control/detector/provider keys, integrity anchors, and raw payload are collapsed/demoted. +- Diagnostics: raw payload remains available but not default framing. + +## Dangerous Actions + +No destructive action is expected on this detail page. Existing current-report navigation remains read-only and capability-aware. + +## Spec 372 Follow-up + +- 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. diff --git a/docs/ui-ux-enterprise-audit/route-inventory.md b/docs/ui-ux-enterprise-audit/route-inventory.md index 3f172115..1f089be3 100644 --- a/docs/ui-ux-enterprise-audit/route-inventory.md +++ b/docs/ui-ux-enterprise-audit/route-inventory.md @@ -43,18 +43,18 @@ # Route Inventory | UI-035 | `/admin/workspaces/{workspace}/environments/{environment}/finding-exceptions` | resource | Environment Exceptions | Governance | environment-bound | route exists | environment entitlement | Exceptions / Accepted Risk | Findings / Inbox | Domain Pattern Surface | repo-verified | - | - | Environment-specific exception list. | | UI-036 | `/admin/workspaces/{workspace}/environments/{environment}/finding-exceptions/{record}` | resource | Exception Detail | Governance | environment record | reachable | environment + record entitlement | Exceptions / Accepted Risk | Evidence / Audit | Strategic Surface | repo-verified | [desktop](../../specs/354-finding-exceptions-accepted-risk-resolution-guidance-v1/artifacts/screenshots/spec354-ui-036-exception-detail-guidance.png) | [report](page-reports/ui-036-exception-detail.md) | Accepted-risk lifecycle detail re-validated for incomplete-governance and calm-ready owner states. | | UI-037 | `/admin/reviews` | page | Review Register | Reviews | workspace hub | reachable | workspace member | Reviews | Evidence / Audit | Strategic Surface | repo-verified | [desktop](screenshots/desktop/ui-011-reviews.png) | [report](page-reports/ui-011-reviews.md) | Review planning and proof surface. | -| UI-038 | `/admin/reviews/workspace` | page | Customer Review Workspace | Customer review | workspace hub | reachable | workspace member | Customer Workspace | Reviews | Strategic Surface | repo-verified | [desktop](screenshots/desktop/ui-006-customer-review-workspace.png) | [report](page-reports/ui-006-customer-review-workspace.md) | Highest customer-safe productization surface. | +| UI-038 | `/admin/reviews/workspace` | page | Customer Review Workspace | Customer review | workspace hub | reachable | workspace member | Customer Workspace | Reviews | Strategic Surface | browser-verified | [desktop](../../specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png) | [report](page-reports/ui-006-customer-review-workspace.md) | Spec 372 keeps the decision/evidence-first workspace and removes operation proof from the default customer evidence path. | | UI-039 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews` | resource | Environment Reviews | Reviews | environment-bound | route exists | environment entitlement | Reviews | Evidence / Audit | Domain Pattern Surface | repo-verified | - | - | Environment-scoped review list. | -| UI-040 | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` | resource | Environment Review Detail | Reviews | environment record | route exists | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | repo-verified | - | [report](page-reports/ui-040-environment-review-detail.md) | Customer/auditor-facing evidence risk. | +| 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-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 | route exists | environment + record entitlement | Reviews | Evidence / Audit | Strategic Surface | repo-verified | - | [report](page-reports/ui-042-review-pack-detail.md) | Spec 356 makes rendered-report preview the primary inspect affordance while ZIP download and regenerate remain secondary/operator-scoped. | +| 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-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-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-046 | `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` | resource | Evidence Snapshot Detail | Evidence / audit | environment record | route exists | environment + record entitlement | Evidence / Audit | Support / Diagnostics | Strategic Surface | repo-verified | - | - | Raw/support evidence must stay 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 372 reaches the detail route and keeps operation/source diagnostics progressively disclosed. | | 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 | route exists | environment + record entitlement | Evidence / Audit | Reviews | Strategic Surface | repo-verified | - | - | Customer/auditor readable report review needed. | +| 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-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-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. | diff --git a/docs/ui-ux-enterprise-audit/unresolved-pages.md b/docs/ui-ux-enterprise-audit/unresolved-pages.md index f550d5cb..74fd5e19 100644 --- a/docs/ui-ux-enterprise-audit/unresolved-pages.md +++ b/docs/ui-ux-enterprise-audit/unresolved-pages.md @@ -4,9 +4,9 @@ # Unresolved Pages Summary: -- High-priority unresolved/manual-review entries: 27. +- High-priority unresolved/manual-review entries: 25. - Capability/fixture blockers with desktop evidence: UI-053, UI-061. -- Strategic routes not browser-captured in this bounded pass: 23. +- Strategic routes not browser-captured in this bounded pass: 21. - Hidden/file-discovered manual-review surface: UI-080. | ID | Page | Blocker / Reason | Needed Evidence | Next Action | @@ -17,8 +17,6 @@ # Unresolved Pages | UI-017 | Operation Detail | Dynamic operation record route requires a run fixture. | OperationRun records covering success, failure, running, retryable states. | Add operation detail report later. | | UI-034 | Finding Detail | Dynamic finding detail requires seeded finding state. | Finding records with owner, severity, exception, and close state. | Add strategic finding detail mockup. | | UI-044 | Evidence Overview | Workspace evidence landing was not captured. | Workspace with evidence sources, gaps, and stale states. | Add evidence overview report. | -| UI-046 | Evidence Snapshot Detail | Dynamic raw/support evidence detail requires snapshot record. | Snapshot with normalized summary and raw payload. | Add progressive-disclosure review. | -| UI-048 | Stored Report Detail | Dynamic report artifact requires stored report record. | Stored report with customer-facing summary and export metadata. | Add stored-report target pass. | | 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-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. | diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/affected-files.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/affected-files.md new file mode 100644 index 00000000..99b9e3cc --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/affected-files.md @@ -0,0 +1,34 @@ +# Affected Files + +Status: implementation complete. + +| File | Purpose | Change Type | Surface Affected | Verification Level | Risk | Out-of-scope Side Effects | +|---|---|---|---|---|---|---| +| `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` | Workspace-wide customer review state/payload | runtime edit | Customer Review Workspace | feature + browser verified | medium | Operation proof removed from default customer evidence path; no route/action/backend changes | +| `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php` | Environment Review detail infolist | runtime edit | Environment Review View | feature + browser verified | medium | Technical metadata moved below outcome/guidance/evidence basis; actions unchanged | +| `apps/platform/app/Filament/Resources/ReviewPackResource.php` | Review Pack detail infolist | runtime edit | Review Pack View | feature + browser verified | medium | Technical pack details collapsed and hidden from customer-workspace flow; generator/download behavior unchanged | +| `apps/platform/app/Filament/Resources/StoredReportResource.php` | Stored Report detail infolist | runtime edit | Stored Report View | feature + browser verified | medium | Report scope/readiness promoted; raw/source/fingerprint metadata demoted | +| `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php` | Evidence Snapshot detail infolist/context | runtime edit | Evidence Snapshot View | feature + browser verified | medium/high | Evidence detail now reachable in smoke; operation-run context removed from related context and demoted to technical details | +| `apps/platform/lang/en/localization.php` | Workspace output action help copy | runtime copy edit | Customer Review Workspace | feature + browser verified | low | Removes operation-proof mention from default supporting-action help | +| `apps/platform/lang/de/localization.php` | Workspace output action help copy | runtime copy edit | Customer Review Workspace | feature + browser verified | low | Same as English localization | +| `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` | Focused surface assertions | new test | all scoped surfaces | passing | low | Bounded fixtures, DB-only render guard via fail-hard Graph client | +| `apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php` | Browser proof/screenshots | new test | all scoped surfaces | passing | medium | Captures desktop screenshots and mobile Evidence Snapshot screenshot | +| `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php` | Existing workspace contract | test update | Customer Review Workspace | passing | low | Updated stale operation-proof expectation | +| `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php` | Existing workspace pack access contract | fixture update | Customer Review Workspace | passing | low | Adds missing disclosure metadata to fixtures that assert customer-safe readiness | +| `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php` | Existing linked detail contract | test update | Environment Review View | passing | low | Updates stale action name to current rendered-report action | +| `apps/platform/tests/Feature/StoredReports/StoredReportDetailPresentationTest.php` | Existing stored report detail contract | test update | Stored Report View | passing | low | Updates section-order expectation | +| `apps/platform/tests/Feature/Evidence/EvidenceSnapshotResourceTest.php` | Existing evidence detail contract | test update | Evidence Snapshot View | passing | low | Updates related-context and artifact-reference expectations | +| `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md` | UI audit evidence | docs update | Customer Review Workspace | browser verified | low | Adds Spec 372 follow-up | +| `docs/ui-ux-enterprise-audit/page-reports/ui-040-environment-review-detail.md` | UI audit evidence | docs update | Environment Review View | browser verified | low | Adds Spec 372 follow-up | +| `docs/ui-ux-enterprise-audit/page-reports/ui-042-review-pack-detail.md` | UI audit evidence | docs update | Review Pack View | browser verified | low | Adds Spec 372 follow-up | +| `docs/ui-ux-enterprise-audit/page-reports/ui-046-evidence-snapshot-detail.md` | UI audit evidence | new docs | Evidence Snapshot View | browser verified | low | Moves page out of unresolved ledger | +| `docs/ui-ux-enterprise-audit/page-reports/ui-048-stored-report-detail.md` | UI audit evidence | new docs | Stored Report View | browser verified | low | Moves page out of unresolved ledger | +| `docs/ui-ux-enterprise-audit/route-inventory.md` | UI coverage index | docs update | scoped pages | browser verified | low | Adds Spec 372 screenshots/report links | +| `docs/ui-ux-enterprise-audit/unresolved-pages.md` | unresolved ledger | docs update | Evidence Snapshot / Stored Report | browser verified | low | Removes UI-046 and UI-048 from unresolved list | + +## Files Explicitly Out Of Scope And Unchanged + +- OperationRun View files. +- Backup Set and Restore Run resource files. +- Operations Hub, Environment Dashboard, Baseline Profile, Provider Connections, Environment Diagnostics, Required Permissions, and System Panel files. +- Migrations, models, jobs, services, report renderer, disclosure policy, routes, panel providers, configuration, and package/dependency files. diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/before-after-screenshot-index.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/before-after-screenshot-index.md new file mode 100644 index 00000000..0733b329 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/before-after-screenshot-index.md @@ -0,0 +1,21 @@ +# Before / After Screenshot Index + +Status: populated after browser smoke. + +| Page | Before Screenshot | After Screenshot | Notes | +|---|---|---|---| +| Customer Review Workspace | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/011-customer-surface-report-customer-review-workspace.png` | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png` | Before score 4.2; after estimate 4.6 | +| Environment Review View | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/012-customer-surface-view-environment-review.png` | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png` | Before score 3.7; after estimate 4.3 | +| Review Pack View | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/013-customer-surface-view-review-pack.png` | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png` | Before score 4.2; after estimate 4.5 | +| Stored Report View | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/014-customer-surface-view-stored-report.png` | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png` | Before score 4.2; after estimate 4.5 | +| Evidence Snapshot View | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/blocked-or-error/010-evidence-surface-view-evidence-snapshot-error.png` | `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png` | Before blocked; after reachable, after estimate 4.3 | + +## Additional Implementation Screenshots + +- `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/006-evidence-snapshot-view-mobile.png` + +## Capture Notes + +- Screenshots were produced by `apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php`. +- Browser assertions verified no JavaScript errors or console logs for each tested page. +- Mobile Evidence Snapshot assertion verified `document.documentElement.scrollWidth <= window.innerWidth`. diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/browser-verification-report.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/browser-verification-report.md new file mode 100644 index 00000000..b8f6aee6 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/browser-verification-report.md @@ -0,0 +1,53 @@ +# Browser Verification Report + +Status: PASS. + +## Browser Harness + +- Command: `./vendor/bin/sail artisan test --compact tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php` +- Result: pass, 1 test, 38 assertions. +- Fixture: test-created owner user and `Spec372 Browser Environment` via local smoke-login route. +- Checks: page load, required visible text, section ordering via browser script, no JavaScript errors, no console logs, desktop screenshots, mobile Evidence Snapshot overflow check. + +## URLs Tested + +| Page | URL Pattern | Status | +|---|---|---| +| Customer Review Workspace | `/admin/reviews/workspace?environment_id={environment}` | pass | +| Environment Review View | `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` | pass | +| Review Pack View | `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` | pass | +| Stored Report View | `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}` | pass | +| Evidence Snapshot View | `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` | pass | + +## Screenshot Outputs + +- `artifacts/screenshots/001-customer-review-workspace-after.png` +- `artifacts/screenshots/002-environment-review-view-after.png` +- `artifacts/screenshots/003-review-pack-view-after.png` +- `artifacts/screenshots/004-stored-report-view-after.png` +- `artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png` +- `artifacts/screenshots/006-evidence-snapshot-view-mobile.png` + +## Score Tracking + +Scores are implementation-review estimates based on the Spec 372 browser smoke and visual spot-check, not a new full audit pass. + +| Page | Spec 368 Score | Target | After Score | Result | +|---|---:|---:|---:|---| +| Customer Review Workspace | 4.2 | >= 4.5 | 4.6 | pass | +| Environment Review View | 3.7 | >= 4.2 | 4.3 | pass | +| Review Pack View | 4.2 | >= 4.5 | 4.5 | pass | +| Stored Report View | 4.2 | >= 4.5 | 4.5 | pass | +| Evidence Snapshot View | 0.0 blocked | >= 4.2 if reachable | 4.3 | pass, reachable | + +## Evidence Snapshot Reachability + +- Spec 368 result: redirected to `/admin/login`. +- Spec 372 result: reached the environment-scoped Evidence Snapshot detail with the smoke-login fixture. +- Follow-up status: no fixture blocker remains for this path in the Spec 372 smoke. Broader evidence-overview/report-list coverage remains outside this spec. + +## Visual Notes + +- Customer Review Workspace first viewport is decision/evidence-first and no longer exposes operation proof by default. +- Evidence Snapshot mobile screenshot has no horizontal overflow according to browser assertion. +- Filament shell density remains existing app chrome and is out of scope for this feature. diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-safety-checklist.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-safety-checklist.md new file mode 100644 index 00000000..a8e8338e --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-safety-checklist.md @@ -0,0 +1,25 @@ +# Customer Safety Checklist + +Status: complete after feature tests and browser smoke. + +| Check | Customer Review Workspace | Environment Review View | Review Pack View | Stored Report View | Evidence Snapshot View | +|---|---|---|---|---|---| +| No raw IDs in first viewport | pass | pass | pass | pass | pass | +| No OperationRun internals in customer default | pass | pass | pass | pass | pass | +| No provider payloads in customer default | pass | pass | pass | pass | pass | +| No debug wording | pass | pass | pass | pass | pass | +| Limitations visible | pass | pass | pass | pass | pass | +| Evidence reachable or blocked honestly documented | pass | pass | pass | pass | pass: reachable | +| Primary action clear | pass | pass | pass | pass | pass | +| Secondary actions subordinate | pass | pass | pass | pass | pass | +| No zero-card spam | pass | pass | pass | pass | pass | +| No repeated readiness/status | pass | pass | pass | pass | pass | +| Diagnostics separated | pass | pass | pass | pass | pass | +| Technical details collapsed/demoted | pass | pass | pass | pass | pass | +| Customer/auditor can understand page without operator knowledge | pass | pass | pass | pass | pass | + +## Notes + +- Evidence Snapshot is no longer blocked for the Spec 372 fixture; it loaded in the browser smoke and produced desktop and mobile screenshots. +- Technical OperationRun/fingerprint/source details remain available on detail pages where already authorized, but they are collapsed/demoted and no longer related-context or default customer evidence. +- Customer Review Workspace default evidence path now shows evidence snapshot, review pack, and decision trail; operation proof is absent by default. diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-surface-contracts.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-surface-contracts.md new file mode 100644 index 00000000..212e1098 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/customer-surface-contracts.md @@ -0,0 +1,83 @@ +# Customer Surface Contracts + +Status: implemented and browser-verified. + +## Customer Review Workspace + +| Field | Contract | +|---|---| +| Page | Customer Review Workspace | +| Primary audience | customer, auditor, operator-as-facilitator | +| Primary question | What needs customer decision, and what is ready to review? | +| Implemented first viewport | Review output state, reason/impact, latest released review, one primary action, subordinate supporting actions, limitations, acknowledgement, findings, and evidence/review-pack side proof. | +| Allowed default content | Customer-safe outcome, evidence snapshot, review pack state, decision trail, accepted-risk summary, limitations, visible environment filter. | +| Collapsed/hidden content | Technical details, diagnostics, raw/support detail, and operation proof. | +| Primary action | State-specific customer-safe action such as open review or download qualified review pack. | +| Secondary actions | Supporting download/evidence links only when URL/action is available. | +| Evidence access | Evidence snapshot, review pack, and decision trail remain clear in the side panel. | +| Diagnostics access | Collapsed diagnostics panel. | +| Customer-safety notes | Default path no longer exposes `Operation proof` or operation initiator. | + +## Environment Review View + +| Field | Contract | +|---|---| +| Page | Environment Review View | +| Primary audience | customer, auditor, operator-as-facilitator | +| Primary question | What is the review outcome, and what proof/limitations support it? | +| Implemented first viewport | Outcome summary, output guidance, executive posture, evidence basis, then collapsed technical details. | +| Allowed default content | Outcome, output readiness, publication/sharing boundary, review summary, tenant/generated/published dates, evidence snapshot completeness, and current export state. | +| Collapsed/hidden content | Review status/completeness/fingerprint and deeper section payload details. | +| Primary action | Existing header actions remain source-owned; rendered-report handoff remains the current ready-pack detail action. | +| Secondary actions | Evidence and review-pack links inside evidence basis. | +| Evidence access | Evidence snapshot and current export links stay visible before technical details. | +| Diagnostics access | Technical details and section details are collapsed. | +| Customer-safety notes | No review lifecycle, accepted-risk, evidence, generation, or action runtime changes. | + +## Review Pack View + +| Field | Contract | +|---|---| +| Page | Review Pack View | +| Primary audience | customer, auditor, operator | +| Primary question | Is this review pack ready, and what does it include? | +| Implemented first viewport | Outcome summary, output guidance, pack readiness and contents, then collapsed technical pack details. | +| Allowed default content | Pack readiness, environment, generated/expires, download size, finding/report counts, evidence resolution, evidence basis, and released review. | +| Collapsed/hidden content | Options, initiator, customer-workspace link, review status, OperationRun link, operation count, freshness, SHA/fingerprints, and creation timestamp. | +| Primary action | Existing rendered-report/download actions remain in the page header when available. | +| Secondary actions | Evidence and released-review links. | +| Evidence access | Evidence basis and released review links stay visible before technical metadata. | +| Diagnostics access | Collapsed technical pack details; hidden entirely in customer-workspace flow. | +| Customer-safety notes | No generator, renderer, disclosure-policy, or download authorization changes. | + +## Stored Report View + +| Field | Contract | +|---|---| +| Page | Stored Report View | +| Primary audience | auditor, customer, operator | +| Primary question | What report is this, what is its scope/readiness, and what summary matters? | +| Implemented first viewport | Outcome summary, report scope/readiness, report-specific summary, technical report details collapsed, raw payload collapsed. | +| Allowed default content | Report type, environment, measured time, lifecycle, retention, and permission/role summary. | +| Collapsed/hidden content | Artifact reference, source family/kind/target, control/detector/provider keys, integrity anchors, previous fingerprint, and raw payload. | +| Primary action | Existing read-only current-report navigation remains capability-gated. | +| Secondary actions | None added. | +| Evidence access | Report summary remains readable before raw/source internals. | +| Diagnostics access | Technical report details and raw payload are collapsed. | +| Customer-safety notes | Report is framed as an output artifact, not a storage object. | + +## Evidence Snapshot View + +| Field | Contract | +|---|---| +| Page | Evidence Snapshot View | +| Primary audience | auditor, customer, operator | +| Primary question | What evidence was captured, and what review/report context does it support? | +| Implemented first viewport | Outcome summary, evidence basis/readiness, coverage summary, related review/report context, then collapsed technical evidence details. | +| Allowed default content | Evidence state, completeness, environment, captured/expires dates, finding/report/missing/stale counts, review-pack link, customer-workspace link, evidence dimensions with summary. | +| Collapsed/hidden content | OperationRun link, fingerprints, operation count, source descriptors, provider source detail, and raw summary JSON. | +| Primary action | Existing refresh evidence / expire snapshot header actions preserved. | +| Secondary actions | Review pack and customer workspace related-context links. | +| Evidence access | Evidence dimensions are readable before technical per-dimension metadata. | +| Diagnostics access | Technical evidence and technical dimension details are collapsed. | +| Customer-safety notes | Evidence Snapshot was reachable in Spec 372 browser smoke; operation-run related context was removed. | diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/implementation-notes.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/implementation-notes.md new file mode 100644 index 00000000..477594d9 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/implementation-notes.md @@ -0,0 +1,55 @@ +# Implementation Notes + +Status: implementation complete. + +## Design Decisions + +- Reused the Spec 370/371 hierarchy: outcome first, customer/auditor guidance second, evidence basis before diagnostics, and technical metadata behind collapsed detail. +- Kept all changes on existing Filament pages/resources. No route, model, migration, policy, service, report renderer, disclosure policy, generator, or portal scope was added. +- Preserved Specs 342/344/347 workspace and review-pack readiness semantics, but removed OperationRun proof from the default Customer Review Workspace evidence path. +- Treated Evidence Snapshot as reachable in Spec 372 because the browser smoke fixture reached the scoped detail route successfully. + +## Copy Changes + +- Customer Review Workspace supporting-action help now references package download and evidence basis only, not operation proof. +- Environment Review detail now uses `Outcome summary`, `Output guidance`, `Executive posture`, `Evidence basis`, `Technical details`, and `Sections`. +- Review Pack detail now uses `Outcome summary`, `Output guidance`, `Pack readiness and contents`, and collapsed `Technical pack details`. +- Stored Report detail now leads with `Report scope and readiness`, then the report-specific summary, then collapsed `Technical report details`, then raw payload. +- Evidence Snapshot detail now leads with `Evidence basis and readiness`, `Evidence coverage summary`, and `Related review and report context`; provider/source internals live under collapsed technical dimension details. + +## Action Hierarchy Changes + +- No new actions were introduced. +- Customer Review Workspace still presents one primary action in the decision card and subordinate supporting actions. +- Environment Review existing header actions remain source-owned; the stale test assertion was updated from a removed download action name to the current `open_current_rendered_report` action. +- Review Pack preview/download/regenerate/expire behavior was preserved; regenerate/expire remain outside the first customer/auditor proof hierarchy. +- Evidence Snapshot refresh/expire/create behavior was preserved; operation-run navigation is no longer related context. + +## Metadata Demotion Choices + +- Operation proof was removed from the default Customer Review Workspace evidence path and review-pack side panel. +- Environment Review status/completeness/fingerprint now sit in collapsed `Technical details`. +- Review Pack options, initiator, operation count, freshness, SHA/fingerprint, and OperationRun link now sit in collapsed `Technical pack details`. +- Stored Report display reference, source descriptors, provider keys, integrity anchors, and previous fingerprint now sit in collapsed `Technical report details`. +- Evidence Snapshot OperationRun, fingerprints, and operation counts now sit in collapsed `Technical evidence details`; source descriptors/raw summary JSON sit in collapsed per-dimension details. + +## Evidence / Diagnostics Separation + +- Evidence remains the proof path: snapshot completeness, current export state, pack contents, stored report summaries, and related review/report context. +- Diagnostics remain technical and collapsed. The browser smoke verified no default operation proof in the Customer Review Workspace and no JavaScript console/runtime errors. + +## Tests Added Or Updated + +- Added `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php`. +- Added `apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php`. +- Updated existing workspace, launch-link, stored-report, and evidence-resource tests where stale expectations contradicted the Spec 372 customer/auditor hierarchy. + +## Known Tradeoffs + +- The Filament app shell is still dense; Spec 372 intentionally did not alter navigation or global layout. +- Review Pack and Evidence Snapshot still expose technical detail in collapsed sections for authorized operators; this preserves audit/support utility without making diagnostics first-screen truth. +- Customer-safe readiness remains tied to existing review-pack output contract data. Tests that assert customer-safe readiness now seed non-certification disclosure into the pack summary instead of broadening runtime disclosure semantics. + +## Out-of-Scope Impacts + +- None confirmed. No migrations, env vars, queues, scheduler, storage topology, Graph contracts/calls, panel providers, routes, report renderer, disclosure policy, customer portal, or legacy compatibility paths were added. diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/.gitkeep b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/.gitkeep @@ -0,0 +1 @@ + diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png new file mode 100644 index 00000000..7c4c79f9 Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/001-customer-review-workspace-after.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png new file mode 100644 index 00000000..9d0f66b4 Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/002-environment-review-view-after.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png new file mode 100644 index 00000000..b7e13e77 Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/003-review-pack-view-after.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png new file mode 100644 index 00000000..1086cce0 Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/004-stored-report-view-after.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png new file mode 100644 index 00000000..4f0bd56a Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/006-evidence-snapshot-view-mobile.png b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/006-evidence-snapshot-view-mobile.png new file mode 100644 index 00000000..86b28b28 Binary files /dev/null and b/specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/006-evidence-snapshot-view-mobile.png differ diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/source-audit-summary.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/source-audit-summary.md new file mode 100644 index 00000000..cc215472 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/source-audit-summary.md @@ -0,0 +1,69 @@ +# Source Audit Summary + +Status: implementation evidence. + +## Repo Safety + +- Active branch: `372-customer-auditor-surface-safety-pass`. +- Base HEAD before feature creation: `8713b35d`. +- Initial dirty state for implementation: spec package only. +- Runtime edits stayed inside the scoped customer/auditor surfaces, localization copy, tests, active spec artifacts, and related UI audit documents. + +## Source Inputs + +| Source | Availability | Verification Class | Use In Spec 372 | +|---|---|---|---| +| User-provided Spec 372 draft | available | provided input | Primary candidate scope, page requirements, artifacts, acceptance criteria | +| Spec 368 audit | available | browser-verified / repo-verified | Before scores, screenshots, customer/auditor findings | +| Spec 370 surface contract | available | repo-verified completed spec artifact | Decision/evidence/diagnostics/metadata hierarchy | +| Spec 371 implementation artifacts | available | repo-verified completed implementation artifacts | Summary-first pattern, metadata demotion, details collapse, screenshots/reporting | +| Specs 342/344/347 | available | completed historical context | Preserve Customer Review Workspace and Review Pack readiness work | + +## Spec 368 Customer/Auditor Findings Used + +| Page | Spec 368 Result | Before Screenshot | Score | Spec 372 Result | +|---|---|---|---:|---| +| Customer Review Workspace | browser-verified | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/011-customer-surface-report-customer-review-workspace.png` | 4.2 | decision/evidence-first, operation proof removed from default path | +| Environment Review View | browser-verified | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/012-customer-surface-view-environment-review.png` | 3.7 | outcome/guidance/evidence before technical details | +| Review Pack View | browser-verified | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/013-customer-surface-view-review-pack.png` | 4.2 | readiness/contents/evidence before technical pack metadata | +| Stored Report View | browser-verified | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/customer/014-customer-surface-view-stored-report.png` | 4.2 | report scope/readiness and summary before source/raw payload | +| Evidence Snapshot View | browser-verified blocked | `specs/368-platform-ui-signal-to-noise-browser-audit/artifacts/screenshots/blocked-or-error/010-evidence-surface-view-evidence-snapshot-error.png` | 0.0 | reachable; evidence basis/context before diagnostics | + +## Spec 370 Inputs Used + +- `surface-contract.md`: decision first, diagnostics second, evidence third, technical metadata on demand. +- `surface-type-matrix.md`: customer, auditor, and evidence surface expectations and verification labels. +- `copy-and-terminology-rules.md`: customer-safe copy avoids raw IDs, provider payloads, fingerprints, debug labels, internal reason families, stack traces, and raw JSON. +- `follow-up-spec-map.md`: scoped Customer/Auditor Surface Safety Pass. + +## Spec 371 Inputs Used + +- `implementation-notes.md`: summary first, metadata demotion, technical details collapse, single primary action, no render-time Graph, and scope discipline. +- `browser-verification-report.md`: browser proof shape, screenshots, no JS errors, responsive checks, and limitation notes. +- `page-contracts.md`: completed operator context is read-only and must not be refactored by Spec 372. +- `validation-report.md`: final reporting style for Livewire v4, provider location, global search, destructive actions, assets, tests, and deployment impact. + +## Patterns Reused + +- Summary/outcome first. +- Metadata and lifecycle/timing details demoted. +- Technical details collapsed or secondary. +- One dominant primary action. +- Zero-state/no-attention copy suppressed where it competed with the decision path. +- Before/after screenshot index and browser verification report. + +## Patterns Avoided + +- OperationRun or backup/recovery mechanics as first-screen customer/auditor language. +- Internal diagnostic terms unless collapsed/gated. +- Raw IDs, provider payloads, source descriptors, and technical context as default-visible proof. + +## Reachability Status For Scoped Pages + +| Page | Spec 372 Status | Implementation Handling | +|---|---|---| +| Customer Review Workspace | reachable | default evidence path is customer-safe and excludes operation proof | +| Environment Review View | reachable | productized output hierarchy | +| Review Pack View | reachable | productized artifact readiness hierarchy | +| Stored Report View | reachable | productized report scope/readiness hierarchy | +| Evidence Snapshot View | reachable | productized evidence proof hierarchy and moved out of unresolved ledger | diff --git a/specs/372-customer-auditor-surface-safety-pass/artifacts/validation-report.md b/specs/372-customer-auditor-surface-safety-pass/artifacts/validation-report.md new file mode 100644 index 00000000..7c71cc63 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/artifacts/validation-report.md @@ -0,0 +1,76 @@ +# Validation Report + +Status: implementation validation complete. + +## Repo Safety + +- Active branch: `372-customer-auditor-surface-safety-pass`. +- Base HEAD before feature creation: `8713b35d`. +- Initial dirty state for implementation: active spec package only. +- Final dirty state: expected implementation changes in scoped Filament surfaces, localization, tests, active spec artifacts, screenshots, and UI audit docs. +- Application implementation performed: yes. +- Out-of-scope application files changed: no. + +## Gates + +| Gate | Result | Evidence | +|---|---|---| +| Spec Readiness Gate | PASS | `spec.md`, `plan.md`, `tasks.md`, checklist, and artifacts present | +| Implementation Scope Gate | PASS | Existing scoped surfaces only; no backend/domain/route/auth/report-renderer expansion | +| Test Gate | PASS | Focused and relevant existing tests passed | +| Browser Smoke Test Gate | PASS | Spec 372 browser smoke passed and screenshots captured | +| Post-Implementation Analysis Gate | PASS | No confirmed in-scope findings remain after test/browser/artifact review | +| Merge Readiness Gate | PASS for manual review | Tasks complete, artifacts updated, tests/browser/static checks pass | + +## Validation Commands + +| Command | Result | +|---|---| +| `php -l` on changed runtime/test PHP files | pass | +| `./vendor/bin/sail artisan test --compact tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` | pass: 6 tests, 78 assertions | +| `./vendor/bin/sail artisan test --compact tests/Feature/Evidence/EvidenceSnapshotResourceTest.php` | pass: 16 tests, 109 assertions | +| `./vendor/bin/sail artisan test --compact tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php tests/Feature/EnvironmentReview/EnvironmentReviewUiContractTest.php tests/Feature/EnvironmentReview/Spec349EnvironmentReviewOutputGuidanceTest.php tests/Feature/EnvironmentReview/Spec350EnvironmentReviewResolutionGuidanceTest.php tests/Feature/ReviewPack/ReviewPackResourceTest.php tests/Feature/ReviewPack/Spec347ReviewPackOutputContractTest.php tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php tests/Feature/StoredReports/StoredReportDetailPresentationTest.php tests/Feature/StoredReports/StoredReportResourceTest.php tests/Feature/Evidence/EvidenceSnapshotResourceTest.php` | pass: 90 tests, 676 assertions | +| `./vendor/bin/sail artisan test --compact tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php` | pass: 1 test, 38 assertions | +| `./vendor/bin/sail artisan test --compact --filter=Spec372` | pass after formatting: 7 tests, 116 assertions | +| `./vendor/bin/sail pint --dirty` | initial pass: 0 files changed | +| `./vendor/bin/sail pint app/Filament/Pages/Reviews/CustomerReviewWorkspace.php app/Filament/Resources/EnvironmentReviewResource.php app/Filament/Resources/EvidenceSnapshotResource.php app/Filament/Resources/ReviewPackResource.php app/Filament/Resources/StoredReportResource.php tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php tests/Feature/Evidence/EvidenceSnapshotResourceTest.php tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php tests/Feature/StoredReports/StoredReportDetailPresentationTest.php` | pass: 12 files checked, 3 style issues fixed | +| `git diff --check` | pass | + +## Browser Results + +- Customer Review Workspace: pass, `artifacts/screenshots/001-customer-review-workspace-after.png`. +- Environment Review View: pass, `artifacts/screenshots/002-environment-review-view-after.png`. +- Review Pack View: pass, `artifacts/screenshots/003-review-pack-view-after.png`. +- Stored Report View: pass, `artifacts/screenshots/004-stored-report-view-after.png`. +- Evidence Snapshot View: pass/reachable, `artifacts/screenshots/005-evidence-snapshot-view-after-or-blocked.png`. +- Evidence Snapshot mobile: pass/no horizontal overflow, `artifacts/screenshots/006-evidence-snapshot-view-mobile.png`. + +## Implementation Notes + +- Customer Review Workspace default evidence path no longer exposes operation proof. +- Environment Review, Review Pack, Stored Report, and Evidence Snapshot details now render outcome/readiness/evidence before technical metadata. +- Evidence Snapshot related context no longer includes OperationRun; OperationRun proof remains in collapsed technical details where applicable. +- Existing destructive/high-impact action paths were preserved; no new actions were added. +- Existing global-search posture was preserved; changed resources remain non-global-search participants. + +## Filament / Livewire / Deployment Notes + +- Livewire v4.0+ compliance: maintained. +- Panel provider registration location: unchanged at `apps/platform/bootstrap/providers.php`. +- Global search: no new globally searchable resource; changed resources keep existing disabled posture. +- Assets: no new assets registered; no `filament:assets` deployment change required. +- Deployment impact: no migrations, env vars, queues, scheduler, storage topology, Graph contracts/calls, routes, panel providers, report renderer, disclosure policy, customer portal, or dependency changes. + +## Post-Implementation Analysis + +Result: clean after one fix iteration. + +- Confirmed finding fixed: Customer Review Workspace still surfaced operation proof in the default review-pack panel; fixed by removing operation proof from the default payload. +- Confirmed finding fixed: Evidence Snapshot fixture duplicated an existing evidence dimension; fixed by updating existing evidence item in focused/browser fixtures. +- Confirmed finding fixed: stale existing tests asserted operation-run related context and old detail-section names; updated tests to the new Spec 372 hierarchy while preserving action/RBAC assertions. +- No remaining confirmed in-scope findings. + +## Recommended Next Spec + +- Diagnostic Surface Separation v1 remains a useful follow-up for broader app-shell/operator diagnostic density. +- Evidence Overview and report-list productization remain separate follow-up candidates; Spec 372 covered detail pages only. diff --git a/specs/372-customer-auditor-surface-safety-pass/checklists/requirements.md b/specs/372-customer-auditor-surface-safety-pass/checklists/requirements.md new file mode 100644 index 00000000..68a2a243 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/checklists/requirements.md @@ -0,0 +1,52 @@ +# Requirements Checklist: Spec 372 - Customer/Auditor Surface Safety Pass v1 + +**Purpose**: Validate that Spec 372 is complete, scoped, constitution-aligned, and ready for a later implementation loop. +**Created**: 2026-06-11 +**Feature**: `specs/372-customer-auditor-surface-safety-pass/spec.md` + +## Candidate Selection + +- [x] CHK001 Selected candidate exists in source material: user-provided Spec 372 draft, Spec 368 Candidate C, and Spec 370 follow-up map. +- [x] CHK002 Selected candidate is not already covered by an existing active or completed Spec 372 package. +- [x] CHK003 Related completed specs 342, 344, 347, 370, and 371 are treated as read-only historical context. +- [x] CHK004 Close alternatives are deferred instead of hidden inside Spec 372. +- [x] CHK005 Scope is small enough for a bounded implementation loop: existing customer/auditor surfaces only, Evidence Snapshot conditional. + +## Specification Quality + +- [x] CHK006 Problem, today's failure, user-visible improvement, smallest version, non-goals, complexity, and why-now are explicit. +- [x] CHK007 Functional requirements are testable and avoid unsupported implementation claims. +- [x] CHK008 Out-of-scope boundaries explicitly exclude backend/report/generator/router/auth/portal/operator-surface work. +- [x] CHK009 Acceptance criteria cover all scoped surfaces and Evidence Snapshot blocked handling. +- [x] CHK010 No unresolved open question blocks safe implementation. + +## Constitution And Productization + +- [x] CHK011 UI Surface Impact is completed and not contradictory. +- [x] CHK012 UI/Productization Coverage classifies every scoped surface with screenshot/page-report expectations. +- [x] CHK013 Cross-cutting shared pattern reuse is explicit and prefers existing helpers/contracts. +- [x] CHK014 OperationRun impact is bounded to existing proof links only. +- [x] CHK015 Provider boundary check confirms no new provider/platform seam. +- [x] CHK016 Proportionality Review confirms no new persisted truth, status family, or framework. +- [x] CHK017 RBAC, workspace/environment isolation, deny-as-not-found, and capability behavior are included. +- [x] CHK018 Test governance names confidence + browser lanes and keeps fixture cost bounded. + +## Task Readiness + +- [x] CHK019 `tasks.md` exists and is ordered by setup, tests, surface phases, conditional Evidence Snapshot handling, and validation. +- [x] CHK020 Tasks include Feature/Livewire tests and bounded Browser smoke. +- [x] CHK021 Tasks include required spec-local artifacts and screenshots. +- [x] CHK022 Tasks include no-out-of-scope verification and stop conditions. +- [x] CHK023 Tasks carry final response requirements for Livewire v4, provider registration, global search, destructive actions, asset strategy, validation, and deployment impact. + +## Review Outcome + +- [x] CHK024 Review outcome class: acceptable-special-case. +- [x] CHK025 Workflow outcome: keep. +- [x] CHK026 Final note location: active feature PR close-out entry `Guardrail / Exception / Smoke Coverage` plus `artifacts/validation-report.md`. + +## Notes + +- Preparation artifacts are complete for handoff to implementation. +- Evidence Snapshot reachability remains intentionally conditional; that is a scoped behavior, not an open blocker. +- Application implementation has not been performed in this preparation pass. diff --git a/specs/372-customer-auditor-surface-safety-pass/plan.md b/specs/372-customer-auditor-surface-safety-pass/plan.md new file mode 100644 index 00000000..582fc14b --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/plan.md @@ -0,0 +1,254 @@ +# Implementation Plan: Spec 372 - Customer/Auditor Surface Safety Pass v1 + +**Branch**: `372-customer-auditor-surface-safety-pass` | **Date**: 2026-06-11 | **Spec**: `specs/372-customer-auditor-surface-safety-pass/spec.md` +**Input**: User-provided Spec 372 draft, Spec 368 Candidate C, Spec 370 surface contract/follow-up map, completed Spec 371 artifacts, and repo-verified surface paths. + +## Summary + +Apply the existing surface information architecture contract to customer/auditor review, report, pack, and evidence surfaces. The implementation will make outcome, readiness, evidence basis, limitations, and one customer-safe next action dominant while demoting diagnostics, internal IDs, OperationRun internals, provider payloads, raw JSON, and troubleshooting language. Runtime work is constrained to existing Filament pages/resources/views and their tests; no backend truth, generator, renderer, disclosure policy, route, migration, portal, or OperationRun behavior is added. + +## Technical Context + +**Language/Version**: PHP 8.4.15; Laravel 12; Filament v5; Livewire v4.0+ +**Primary Dependencies**: Filament Resources/Pages/Infolists/Tables/Actions, Blade views, existing badge/status helpers, existing policy/UI enforcement helpers, existing review/evidence/report models and resources +**Storage**: PostgreSQL via existing models only; no schema changes +**Testing**: Pest 4 Feature/Livewire tests and bounded Browser smoke +**Validation Lanes**: confidence + browser + `git diff --check`; Pint dirty if PHP changes +**Target Platform**: TenantPilot Laravel monolith under `apps/platform` +**Project Type**: Web application, Filament admin panel +**Performance Goals**: No Graph/provider calls during render; page render stays DB-only and query-safe +**Constraints**: No migrations, new persisted truth, new routes, new auth flow, new report/export backend, new OperationRun lifecycle, or broad shell/navigation changes +**Scale/Scope**: Five existing surfaces, with Evidence Snapshot conditional on current fixture reachability + +## UI / Surface Guardrail Plan + +- **Guardrail scope**: changed existing customer/auditor surfaces. +- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: + - `/admin/reviews/workspace` + - `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` + - `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` + - `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}` + - `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}` if reachable +- **No-impact class, if applicable**: N/A. +- **Native vs custom classification summary**: mixed native Filament resources/pages plus existing Blade composition; use native/shared primitives first. +- **Shared-family relevance**: status messaging, evidence/report viewers, review-pack/download affordances, limitation copy, diagnostics disclosure, action hierarchy. +- **State layers in scope**: page/detail payloads, visible page-level `environment_id` filter, detail action state, diagnostics disclosure state. +- **Audience modes in scope**: customer/read-only, auditor, MSP operator-as-facilitator, support where authorized. +- **Decision/diagnostic/raw hierarchy plan**: outcome/readiness/evidence/limitations/action first; diagnostics second; raw/support detail third and collapsed or capability-gated. +- **Raw/support gating plan**: collapse, demote, or capability-gate raw/provider/internal/OperationRun detail. +- **One-primary-action / duplicate-truth control**: one first-viewport block owns readiness and next action per page; later sections add proof or detail only. +- **Handling modes by drift class or surface**: + - customer/auditor default raw internals: hard-stop-candidate + - repeated readiness/status as peer cards: review-mandatory + - shared partial affecting out-of-scope pages: exception-required or stop/update spec + - Evidence Snapshot blocked by fixture: document-in-feature + follow-up-spec +- **Repository-signal treatment**: review-mandatory for UI coverage docs; report-only when no route/archetype count changes. +- **Special surface test profiles**: customer-safe strategic review surface; artifact/evidence detail surface; browser proof required. +- **Required tests or manual smoke**: Feature/Livewire state/RBAC tests plus Browser smoke screenshots for reachable surfaces. +- **Exception path and spread control**: any shared helper change must list all consumer surfaces in `artifacts/affected-files.md`; if it materially changes out-of-scope pages, stop and update spec/plan before continuing. +- **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. +- **UI/Productization coverage decision**: update relevant page reports for every material scoped UI change. Record no-count-change rationale only for route/design matrix counts that truly do not change. +- **Coverage artifacts to update**: likely `page-reports/ui-006-customer-review-workspace.md`, `ui-040-environment-review-detail.md`, `ui-042-review-pack-detail.md`, `unresolved-pages.md`, and possibly route/design matrix only if reachability/coverage status changes. +- **No-impact rationale**: N/A. +- **Navigation / Filament provider-panel handling**: no panel/provider registration changes; `apps/platform/bootstrap/providers.php` remains unchanged. +- **Screenshot or page-report need**: screenshots required for all reachable scoped pages; blocked screenshot/reason required for Evidence Snapshot if unreachable. + +## Shared Pattern & System Fit + +- **Cross-cutting feature marker**: yes. +- **Systems touched**: + - `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` + - `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php` + - `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php` + - `apps/platform/app/Filament/Resources/ReviewPackResource.php` + - `apps/platform/app/Filament/Resources/StoredReportResource.php` + - `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php` if reachable/safely in scope + - scoped resource view pages under `apps/platform/app/Filament/Resources/*/Pages` + - existing tests under `apps/platform/tests/Feature`, `apps/platform/tests/Browser` +- **Shared abstractions reused**: existing badge catalog/rendering, policy/UI enforcement, resource URL helpers, Review Pack readiness/disclosure truth from Spec 347, OperationRun link helpers, customer review state patterns from Specs 342/344. +- **New abstraction introduced? why?**: none planned. A scoped derived helper may be introduced only if it removes duplicated copy/status logic across the selected surfaces and stays current-release, derived-only, and non-framework. +- **Why the existing abstraction was sufficient or insufficient**: repo truth sources and UI helpers already exist; the gap is hierarchy/copy/disclosure consistency. +- **Bounded deviation / spread control**: no deviation expected. If native Filament cannot express a necessary customer-safe state, use existing Blade composition with Filament visual language and document the exception. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: no new start/completion/dedupe semantics; existing proof links only. +- **Central contract reused**: existing OperationRun link helpers and policies. +- **Delegated UX behaviors**: N/A for new starts; no queued toast, run-enqueued browser event, terminal notification, or queued DB notification changes. +- **Surface-owned behavior kept local**: label operation proof as secondary/provenance/diagnostics, not customer evidence truth. +- **Queued DB-notification policy**: N/A. +- **Terminal notification path**: unchanged. +- **Exception path**: none. + +## Provider Boundary & Portability Fit + +- **Shared provider/platform boundary touched?**: no. +- **Provider-owned seams**: none. +- **Platform-core seams**: review/evidence/report presentation language only. +- **Neutral platform terms / contracts preserved**: workspace, environment, review, evidence, review pack, stored report, limitations, accepted risk, diagnostics. +- **Retained provider-specific semantics and why**: only existing report/evidence content may contain provider facts when customer-safe; provider IDs/payloads stay out of default UI. +- **Bounded extraction or follow-up path**: Diagnostic Surface Separation / Provider Connection readiness productization if provider/debug surfaces still need work. + +## Constitution Check + +*GATE: PASS for preparation. Re-check before runtime implementation.* + +- Inventory-first: no inventory/source-of-truth changes. +- Read/write separation: feature is read-oriented UI productization; no new write/change function. Existing high-impact actions remain on current confirmation/authorization/audit paths. +- Graph contract path: no new Graph calls; no render-time Graph calls allowed. +- Deterministic capabilities: existing capabilities/policies remain authoritative. +- RBAC-UX: membership and entitlement checks remain server-side; non-member/cross-scope remains 404; missing capability remains existing 403/disabled/hidden behavior. +- Workspace isolation: workspace context remains the shell/context boundary; environment filter is explicit page filter only. +- Tenant isolation: environment-owned records remain workspace + environment scoped. +- Run observability: no new OperationRun type or lifecycle; existing operation proof links are secondary. +- Test governance: confidence + browser lanes named; no hidden heavy family. +- Proportionality: no new persisted truth, enum/status family, broad abstraction, taxonomy, route, or portal. +- Shared pattern first: reuse existing review/evidence/report/badge/link patterns before adding local helpers. +- Provider boundary: no provider seam or Microsoft-shaped platform-core spread. +- UI-COV-001: UI impact declared; page-report updates are required for materially changed scoped pages, with no-count-change rationale limited to unchanged route/design registries. +- DECIDE-AUD/OPSURF: customer-readable default content precedes diagnostics/raw/support evidence; no false calmness. +- Filament v5 / Livewire v4: required. +- Panel provider location: unchanged in `apps/platform/bootstrap/providers.php`. +- Global search: do not enable new global search. Existing resource global-search posture must be preserved unless spec/plan updated. +- Destructive actions: no new destructive/high-impact action; if an implementation discovers one is needed, stop and update spec/plan before adding `Action::make(...)->action(...)`, `->requiresConfirmation()`, authorization, audit, notification, and tests. +- Asset strategy: no new assets expected; if assets are registered, deployment must include `cd apps/platform && php artisan filament:assets`. + +## Test Governance Check + +- **Test purpose / classification by changed surface**: Feature/Livewire for state/RBAC/rendered content; Browser for hierarchy/screenshots; Unit only if a pure derived helper is introduced. +- **Affected validation lanes**: confidence and browser. +- **Why this lane mix is the narrowest sufficient proof**: customer/auditor safety is partly semantic rendered UI; Browser proof is necessary but bounded. +- **Narrowest proving command(s)**: + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec372` + - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php --compact` + - targeted filters for CustomerReview, EnvironmentReview, ReviewPack, StoredReport, EvidenceSnapshot as files touched + - `cd apps/platform && ./vendor/bin/sail pint --dirty` + - `git diff --check` +- **Fixture / helper / factory / seed / context cost risks**: reuse review-output browser fixture; no shared default fixture widening. +- **Expensive defaults or shared helper growth introduced?**: no. +- **Heavy-family additions, promotions, or visibility changes**: one explicit Browser smoke. +- **Surface-class relief / special coverage rule**: no standard-native relief; special customer/auditor safety checks required. +- **Closing validation and reviewer handoff**: verify no raw/internal language in defaults, limitations visible, diagnostics collapsed, one primary action, Evidence Snapshot conditional handling, and no out-of-scope refactor. +- **Budget / baseline / trend follow-up**: none expected. +- **Review-stop questions**: lane fit, breadth, hidden fixture cost, shared partial spread, false customer-ready claims. +- **Escalation path**: document-in-feature for contained UI tradeoffs; follow-up-spec for fixture/diagnostic structural gaps; reject-or-split if backend/portal/report-generator scope appears. +- **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. +- **Why no dedicated follow-up spec is needed**: the selected surfaces are one coherent customer/auditor handoff path; diagnostics/provider/support surfaces are deferred separately. + +## Project Structure + +### Documentation (this feature) + +```text +specs/372-customer-auditor-surface-safety-pass/ +├── spec.md +├── plan.md +├── tasks.md +├── checklists/ +│ └── requirements.md +└── artifacts/ + ├── source-audit-summary.md + ├── affected-files.md + ├── customer-surface-contracts.md + ├── before-after-screenshot-index.md + ├── implementation-notes.md + ├── browser-verification-report.md + ├── customer-safety-checklist.md + ├── validation-report.md + └── screenshots/ +``` + +### Source Code (repository root) + +```text +apps/platform/app/Filament/Pages/Reviews/ +apps/platform/app/Filament/Resources/ +apps/platform/app/Filament/Resources/*/Pages/ +apps/platform/resources/views/filament/pages/reviews/ +apps/platform/resources/views/filament/resources/ +apps/platform/resources/views/components/ +apps/platform/tests/Feature/ +apps/platform/tests/Browser/ +docs/ui-ux-enterprise-audit/ +``` + +**Structure Decision**: use existing Laravel/Filament monolith structure. No new base folders. Spec-local artifacts stay under `specs/372-*`; runtime changes stay scoped to existing platform paths if implemented later. + +## Complexity Tracking + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|---|---|---| +| Multiple customer/auditor surfaces in one spec | They form one review/report/evidence handoff path and share the same safety contract | Repeating one-page specs would preserve cross-surface inconsistencies and duplicate review burden | +| Browser smoke required | Customer/auditor safety depends on rendered hierarchy and first-viewport language | Feature tests alone cannot prove density, collapsed diagnostics, and screenshots | + +## Proportionality Review + +- **Current operator problem**: customers/auditors still need a safer, calmer, evidence-first output path across existing surfaces. +- **Existing structure is insufficient because**: page-level productization has progressed unevenly; prior specs solved individual surfaces or output contracts, not the cross-surface default disclosure contract. +- **Narrowest correct implementation**: existing surfaces only, page-local or scoped derived helpers only, no backend truth changes. +- **Ownership cost created**: focused tests, one browser smoke, screenshots, spec-local contract/checklist artifacts. +- **Alternative intentionally rejected**: customer portal, report renderer rewrite, generic evidence/readiness framework, diagnostic surface rewrite, route/auth fixture repair inside this feature. +- **Release truth**: current-release customer trust and sellability hardening. + +## Implementation Phases + +### Phase 0 - Repo Truth And Pre-Implementation Gate + +- Re-read Spec 368, 370, 371 inputs and related completed specs 342/344/347 as read-only context. +- Verify current branch, HEAD, dirty state, and existing `specs/372-*` artifacts. +- Produce `source-audit-summary.md`, `affected-files.md`, `customer-surface-contracts.md`, `before-after-screenshot-index.md`, and customer safety checklist baseline. +- Determine current reachability for Evidence Snapshot using existing fixture/harness only. + +### Phase 1 - Test And Browser Proof Design + +- Add focused Feature/Livewire coverage for customer-safe default content, diagnostics absence/collapse, RBAC/context behavior, and no false-ready claims on touched surfaces. +- Add bounded Browser smoke with existing smoke-login/review-output fixture and screenshot capture for reachable pages. +- Include blocked Evidence Snapshot path handling if still unreachable. + +### Phase 2 - Customer Review Workspace Safety Pass + +- Preserve Spec 342/344/347 outcomes. +- Reduce competing readiness/status/action density only where current runtime still shows it. +- Keep decision-needed findings, accepted risks, evidence/report pack, limitations, and one primary action visible. + +### Phase 3 - Environment Review Detail Safety Pass + +- Recompose first viewport around outcome, review scope/period, evidence basis, limitations, and next action. +- Move technical metadata/source refs/operation proof to details/aside as appropriate. +- Preserve lifecycle truth without repeating it as peer summaries. + +### Phase 4 - Review Pack And Stored Report Artifact Pass + +- Keep output/report readiness and disclosure truth first. +- Keep evidence basis and limitations visible. +- Demote storage, renderer, operation, and internal metadata. +- Preserve existing download/view authorization and high-impact action behavior. + +### Phase 5 - Evidence Snapshot Conditional Pass + +- If reachable, apply evidence proof vs diagnostics separation and capture screenshots. +- If not reachable, document the blocked state, final URL/reason, and follow-up recommendation. Do not fix auth/routing broadly. + +### Phase 6 - UI Coverage, Validation, And Close-Out + +- Update page-report coverage for materially changed scoped pages and document no-count-change rationale only for unchanged route/design registries. +- Update all spec-local artifacts with final results. +- Run targeted tests, browser smoke, Pint dirty if applicable, and `git diff --check`. +- Record final Livewire v4/provider/global-search/destructive-action/asset/deployment notes. + +## Risk Controls + +- Stop before backend/report/generator/disclosure-policy/route/auth changes unless spec/plan are updated. +- Stop before touching completed operator surfaces from Spec 371. +- Prefer page-local changes; inspect shared partial consumers before edits. +- Treat missing Evidence Snapshot fixture as a documented blocker, not a reason for broad auth/routing work. +- Do not weaken existing policy/signed download/audit/destructive action behavior. + +## Rollout Considerations + +- **Migrations**: none expected. +- **Env vars**: none expected. +- **Queues/scheduler**: none expected. +- **Storage**: none expected. +- **Filament assets**: none expected; if unexpectedly registered, include `php artisan filament:assets`. +- **Staging/production**: validate on Staging before Production because customer/auditor output surfaces are sellability/trust surfaces. diff --git a/specs/372-customer-auditor-surface-safety-pass/spec.md b/specs/372-customer-auditor-surface-safety-pass/spec.md new file mode 100644 index 00000000..e102f824 --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/spec.md @@ -0,0 +1,411 @@ +# Feature Specification: Spec 372 - Customer/Auditor Surface Safety Pass v1 + +**Feature Branch**: `372-customer-auditor-surface-safety-pass` +**Created**: 2026-06-11 +**Status**: Approved +**Type**: Customer/auditor UI safety productization pass over existing review, report, pack, and evidence surfaces +**Runtime posture**: UI/productization only for existing reachable surfaces. No new review logic, evidence logic, migrations, routes, customer portal, report renderer changes, disclosure-policy changes, OperationRun model changes, backend export changes, shell/navigation rewrite, billing, support handoff, or broad diagnostic refactor. +**Input**: User-provided Spec 372 draft plus Spec 368 Candidate C, Spec 370 follow-up map, and completed Spec 371 productization artifacts. + +## Spec Candidate Check *(mandatory - SPEC-GATE-001)* + +- **Problem**: Customer- and auditor-facing review/report/evidence surfaces are repo-real, but the customer-safe experience is uneven across the full handoff path. Some pages are strong, some are dense, and Evidence Snapshot detail was blocked in the Spec 368 browser pass. +- **Today's failure**: A customer, auditor, or operator-as-facilitator can still encounter repeated readiness/status phrases, broad operator chrome, technical metadata, OperationRun-adjacent language, or unclear evidence limitations before understanding the outcome, evidence basis, and next safe action. +- **User-visible improvement**: The scoped surfaces answer the customer/auditor questions first: outcome, readiness, findings/risks needing decision, evidence availability, limitations, and the one safe next action. Diagnostics and internal mechanics remain available only as secondary, collapsed, or capability-gated content. +- **Smallest enterprise-capable version**: Existing Customer Review Workspace, Environment Review View, Review Pack View, Stored Report View, and Evidence Snapshot View only if reachable with current fixtures. Keep changes page-local or narrowly shared among these surfaces; document blocked Evidence Snapshot reachability instead of fixing auth/routing broadly. +- **Explicit non-goals**: No operator-surface refactor from Spec 371, Environment Dashboard, Operations Hub, OperationRun View, Backup Set View, Restore Run View, Baseline Profile View, Provider Connections, Environment Diagnostics, Required Permissions, System Panel, new customer portal, new persisted truth, new readiness engine, new disclosure policy, new report renderer, new Graph calls, or new routes. +- **Permanent complexity imported**: Spec-local source audit, affected-file map, customer surface contracts, screenshot index, customer safety checklist, validation report, focused Feature/Livewire tests, bounded Browser smoke, and screenshots. No new persisted entity, enum/status family, public framework, or cross-domain UI taxonomy is expected. +- **Why now**: Spec 370 created the global surface information architecture contract and explicitly deferred a Customer/Auditor Surface Safety Pass. Spec 371 has now proven the summary-first, metadata-demotion, technical-details-collapse, single-primary-action, screenshot-backed productization pattern on core operator surfaces. +- **Why not local**: A single-page patch would repeat the earlier Customer Review Workspace slices and miss the full customer/auditor handoff path. A broad portal/report rewrite would overbuild. The correct v1 is a bounded safety pass across existing output/evidence surfaces. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: Multiple surfaces and customer-safe trust language. Defense: every surface is existing/repo-real, Evidence Snapshot is conditional, no new persistence/framework is introduced, and the scope applies an already accepted contract rather than inventing a new one. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12** +- **Decision**: approve. + +## Candidate Source And Completed-Spec Guardrail + +- **Selected candidate**: Spec 372 - Customer/Auditor Surface Safety Pass v1. +- **Source locations**: + - User-provided draft: `/Users/ahmeddarrazi/.codex/attachments/398c764b-3ff8-45f7-ae91-540699f3781f/pasted-text.txt` + - Spec 368 Candidate C: `specs/368-platform-ui-signal-to-noise-browser-audit/spec-candidates.md` + - Spec 370 explicit deferred follow-up: `specs/370-global-surface-information-architecture-contract/artifacts/follow-up-spec-map.md` + - Roadmap/candidate relationship: `docs/product/spec-candidates.md` customer-review and evidence/report productization follow-ups. +- **Completed-spec check**: + - No `specs/372-*` package existed before this prep. + - Specs 342, 344, and 347 contain completed-task, validation, smoke, or implementation-history signals and are treated as completed historical context only. + - Specs 370 and 371 contain completed-task, validation, and close-out signals and are read-only inputs. + - Spec 368 is an audit source package and remains read-only. +- **Boundary against related completed specs**: + - Spec 342/344: Customer Review Workspace single-surface consumption and density work. Spec 372 may inspect and preserve those outcomes but must not reopen them as the whole scope. + - Spec 347: Review Pack output contract/readiness semantics. Spec 372 may reuse its output-readiness language but must not change generator, ZIP contract, or disclosure policy truth. + - Spec 371: Backup Set operator productization pattern. Spec 372 reuses the pattern direction, not the operator wording or backup/restore surfaces. +- **Close alternatives deferred**: + - Diagnostic Surface Separation v1: deferred because Required Permissions, System Panel, Provider Connections, and Environment Diagnostics are diagnostic/operator/support surfaces outside this customer/auditor pass. + - UI Bloat Regression Guard v1: deferred until another runtime consumer proves the contract pattern and exceptions. + - Localization v1 Customer-facing Surfaces: deferred until the customer/auditor safety hierarchy is stable. +- **Smallest viable implementation slice**: existing scoped surfaces only, with source audit, page contracts, customer-safety checklist, tests, and browser screenshots. Evidence Snapshot is included only if reachable with existing fixtures. + +## Spec Scope Fields *(mandatory)* + +- **Scope**: workspace canonical view plus environment-owned review/report/evidence artifact detail surfaces. +- **Primary Routes / Surfaces**: + - Customer Review Workspace: `/admin/reviews/workspace`, `App\Filament\Pages\Reviews\CustomerReviewWorkspace`, `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php` + - Environment Review View: `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}`, `App\Filament\Resources\EnvironmentReviewResource` + - Review Pack View: `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}`, `App\Filament\Resources\ReviewPackResource` + - Stored Report View: `/admin/workspaces/{workspace}/environments/{environment}/stored-reports/{record}`, `App\Filament\Resources\StoredReportResource` + - Evidence Snapshot View, conditional: `/admin/workspaces/{workspace}/environments/{environment}/evidence/{record}`, `App\Filament\Resources\EvidenceSnapshotResource` +- **Data Ownership**: + - Workspace-owned shell/context remains workspace-scoped. + - Environment-owned records remain workspace + managed-environment scoped. + - Review truth remains in `EnvironmentReview` and related review sections/summaries. + - Evidence truth remains in `EvidenceSnapshot` and evidence snapshot items. + - Report/export truth remains in `ReviewPack` and `StoredReport`. + - OperationRun remains proof/diagnostic linkage only, not customer-facing evidence truth. +- **RBAC**: + - Workspace membership and managed-environment entitlement are mandatory before rendering any environment-owned record. + - Existing resource policies/capability checks remain authoritative. + - Non-member or cross-workspace/environment access remains deny-as-not-found. + - Missing capability after entitlement remains 403 or disabled/hidden per existing UI enforcement. + - Operator/support diagnostics may be visible only through existing capabilities and must stay collapsed or secondary by default. + +For canonical-view specs: + +- **Default filter behavior when tenant-context is active**: Customer Review Workspace remains workspace-wide with explicit `environment_id` page filter only. Hidden environment context, legacy `/admin/t`, legacy query aliases, and remembered switcher state must not create authority. +- **Explicit entitlement checks preventing cross-tenant leakage**: all scoped record URLs and detail panels must resolve through current workspace/environment ownership and existing policies before content or actions are shown. + +## 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 added +- [x] Customer-facing surface changed +- [ ] 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 | Archetype | Design depth | Repo-truth level | Existing pattern reused | Screenshot / audit need | Customer-safe review | Dangerous-action review | +|---|---|---|---|---|---|---|---| +| Customer Review Workspace | Customer surface / Strategic Surface | Strategic Surface | repo-verified + browser-verified | Specs 342/344/347 customer-safe hierarchy, Spec 370 contract, existing workspace filter | after screenshots required; existing UI-006 page report may need update | yes | existing acknowledgement/create-next-review actions remain in scope for preservation only: keep confirmation, authorization, audit, and capability-aware UI behavior; no new high-impact action | +| Environment Review View | Customer/auditor review detail | Domain Pattern Surface | repo-verified + browser-verified in Spec 368 | Spec 347 review output readiness, existing EnvironmentReviewResource detail | after screenshot required; UI-040 report may need update | yes | existing refresh/publish/create-next/archive/export actions remain in scope for preservation only; keep confirmation, authorization, audit, OperationRun UX, and capability behavior; no new action | +| Review Pack View | Auditor/output artifact detail | Domain Pattern Surface | repo-verified + browser-verified in Spec 368 | Spec 347 Review Pack output/readiness semantics | after screenshot required; UI-042 report may need update | yes | existing download/rendered-report/regenerate/expire behavior remains in scope for preservation only; keep signed download, confirmation, authorization, audit, and OperationRun UX unchanged | +| Stored Report View | Auditor/report artifact detail | Domain Pattern Surface | repo-verified + browser-verified in Spec 368 | existing StoredReportResource detail presentation tests and evidence/report language | after screenshot required if touched; unresolved/UI-048 notes may need update | yes | no destructive action expected; preserve read-only current-report navigation and report capability checks | +| Evidence Snapshot View | Evidence surface | Manual Review Required until reachable | repo-verified route, browser-blocked in Spec 368 | existing EvidenceSnapshotResource and evidence badge/status helpers | after or blocked screenshot required | yes if reachable | existing refresh/expire/create-snapshot behavior remains in scope for preservation only; keep confirmation, authorization, audit, OperationRun UX, and customer-flow hiding/gating unchanged | + +- **Coverage files updated or explicitly not needed**: + - [x] `docs/ui-ux-enterprise-audit/route-inventory.md` + - [x] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` (not needed: no archetype/count model changed) + - [x] `docs/ui-ux-enterprise-audit/page-reports/...` + - [x] `docs/ui-ux-enterprise-audit/strategic-surfaces.md` (not needed: no new strategic-surface category) + - [x] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` (not needed: no follow-up grouping change) + - [x] `docs/ui-ux-enterprise-audit/unresolved-pages.md` + - [x] `N/A - no reachable UI surface impact` (not applicable: reachable UI surfaces changed) +- **Coverage decision for implementation**: implementation must update the relevant `docs/ui-ux-enterprise-audit/page-reports/...` artifact for every materially changed reachable scoped page. `no-count-change` may only be recorded for route inventory, archetype counts, or design matrix rows when those registries remain unchanged; it is not a substitute for page-report coverage when runtime UI changes. + +## Cross-Cutting / Shared Pattern Reuse *(mandatory)* + +- **Cross-cutting feature?**: yes. +- **Interaction class(es)**: status messaging, evidence/report viewers, review-pack/download affordances, limitation copy, diagnostics disclosure, action hierarchy, customer/auditor-safe output language. +- **Systems touched**: scoped Filament pages/resources/views, existing badge/status helpers, existing evidence/review/report links, existing Review Pack download/report links, existing OperationRun proof links. +- **Existing pattern(s) to extend**: Spec 370 surface contract, Spec 371 productization pattern direction, Specs 342/344 Customer Review Workspace hierarchy, Spec 347 review-pack output readiness semantics, existing `BadgeCatalog`/`BadgeRenderer`, existing policy and UI enforcement helpers. +- **Shared contract / presenter / builder / renderer to reuse**: existing artifact truth, badge, review-output, evidence, resource URL, OperationRun link, and policy/capability helpers. Any new helper must be page-local or scoped to the selected surfaces. +- **Why existing paths are sufficient or insufficient**: existing truth sources are sufficient for the v1 safety pass. The likely gap is presentation hierarchy and copy, not domain truth. +- **Allowed deviation and why**: a small scoped view model/helper is allowed only if it replaces duplicated page/view logic across the selected review/report/evidence surfaces. It must stay derived-only and must not become a generic UI framework. +- **Consistency impact**: readiness labels, limitations, evidence basis, report/download labels, diagnostics labels, and primary next actions must use one customer/auditor-safe vocabulary across scoped surfaces. +- **Review focus**: no raw IDs, provider payloads, OperationRun internals, internal reason families, write-gate/hard-blocker terms, or debug/troubleshooting language in default customer/auditor views. + +## OperationRun UX Impact *(mandatory)* + +- **Touches OperationRun start/completion/link UX?**: existing proof/deep-link display only; no new operation start, completion, dedupe, queueing, terminal notification, or lifecycle behavior. +- **Shared OperationRun UX contract/layer reused**: existing OperationRun links and related resource routes. +- **Delegated start/completion UX behaviors**: N/A for new starts. +- **Local surface-owned behavior that remains**: customer-safe explanation that operation proof is secondary diagnostics/provenance, not the primary evidence claim. +- **Queued DB-notification policy**: N/A. +- **Terminal notification path**: unchanged. +- **Exception required?**: none. + +## Provider Boundary / Platform Core Check *(mandatory)* + +- **Shared provider/platform boundary touched?**: no new provider/platform seam. +- **Boundary classification**: platform-core presentation over existing review, evidence, report, and artifact truth. +- **Seams affected**: customer/auditor labels and visibility only; no Graph contracts, provider credentials, provider dispatch, or provider scope semantics. +- **Neutral platform terms preserved or introduced**: workspace, environment, review, evidence, review pack, stored report, limitations, findings, accepted risk, diagnostics. +- **Provider-specific semantics retained and why**: only where existing customer-safe evidence/report content already contains provider-specific facts. Provider IDs/payloads stay out of default views. +- **Why this does not deepen provider coupling accidentally**: no new Graph call, provider schema, provider vocabulary, provider model, or provider capability path is introduced. +- **Follow-up path**: Diagnostic Surface Separation or Provider Connection readiness productization remains separate if provider/debug language still appears in non-scoped pages. + +## UI / Surface Guardrail Impact + +| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | +|---|---:|---|---|---|---:|---| +| Customer Review Workspace first viewport | yes | Native Filament page plus existing Blade composition | customer-safe review consumption, evidence/report links | page payload, URL-query | no | Existing route only | +| Environment Review detail first viewport | yes | Filament Resource View/Infolist or existing detail composition | review status/evidence/limitations | detail payload | no | Existing route only | +| Review Pack detail first viewport | yes | Filament Resource View/Infolist or existing detail composition | output readiness, disclosure, download | detail payload/actions | no | Generator/disclosure policy unchanged | +| Stored Report detail first viewport | yes | Filament Resource View/Infolist or existing detail composition | report readiness, evidence, limitations | detail payload/actions | no | Storage/report generation unchanged | +| Evidence Snapshot detail, conditional | yes if reachable | Filament Resource View/Infolist | evidence proof vs diagnostics | detail payload/actions | no | If blocked, document and defer | + +## Decision-First Surface Role + +| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction | +|---|---|---|---|---|---|---|---| +| Customer Review Workspace | Primary Decision Surface | Customer/auditor/operator decides what needs review, download, or acknowledgement | outcome, readiness, decision-needed findings, accepted risks, evidence/report pack, limitations, primary action | detailed review, evidence, report pack, diagnostics | Primary workspace handoff surface | customer review consumption | removes cross-page reconstruction | +| Environment Review View | Primary/Secondary hybrid | Reader decides whether the review outcome is trustworthy and what remains limited | review outcome, scope/period, decision-needed items, evidence basis, limitations | sections, source refs, technical metadata, operation proof | Primary for one released review | released-review detail | makes review record read as output | +| Review Pack View | Auditor Surface | Reader verifies whether the pack is ready/shareable and what it contains | pack readiness, included sections, evidence basis, disclosure/limitations, download state | metadata, operation proof, renderer/storage details | Secondary artifact proof | review-pack output | keeps artifact truth clear | +| Stored Report View | Auditor Surface | Reader verifies report identity, readiness, scope, evidence, and limitations | report type/title, subject/scope, readiness, evidence basis, primary download/view | storage metadata, audit refs, technical report metadata | Secondary artifact proof | stored-report consumption | avoids storage-object framing | +| Evidence Snapshot View | Evidence Surface | Reader verifies what evidence was captured and what it supports | subject, evidence type, captured at, readiness, limitations, related review/report | raw provider object, operation context, diagnostics | Primary evidence detail if reachable | evidence proof | separates proof from diagnostics | + +## Audience-Aware Disclosure + +| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention | +|---|---|---|---|---|---|---|---| +| Customer Review Workspace | customer, auditor, operator-as-facilitator, support where authorized | outcome, decision-needed items, accepted risks, evidence/report availability, limitations | collapsed secondary | raw JSON, provider payloads, internal IDs, OperationRun context | review decisions / download review pack / view evidence depending state | raw/support detail and operator-only repair actions | readiness appears once; lower sections add proof | +| Environment Review View | customer, auditor, operator-as-facilitator | review outcome, period/scope, evidence basis, limitations, primary action | sidebar/details | raw refs, source payloads, OperationRun internals | open evidence / open pack / review limitations | technical metadata and diagnostics | lifecycle truth appears once | +| Review Pack View | customer, auditor, operator | pack readiness, contents, evidence basis, limitations, download state | secondary details | renderer/storage internals and operation proof | download/view pack when ready; review limitations when not | regenerate/expire/support actions if not primary | output readiness not repeated as peer cards | +| Stored Report View | customer, auditor, operator | report identity, disclosure/readiness, subject/scope, evidence basis, limitations | secondary details | storage metadata and raw report internals | download/view report | technical report metadata and raw IDs | report readiness owns first viewport | +| Evidence Snapshot View | auditor, customer, operator | subject, evidence type, capture date, readiness, limitations, related review/report | secondary/collapsed | raw provider object, internal IDs, operation context | view/download evidence if allowed | raw payload and diagnostics | evidence state distinct from diagnostics | + +## UI/UX Surface Classification + +| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| Customer Review Workspace | Workbench / Review Workspace | Customer-safe strategic hub | consume review, review decisions, or download pack | primary action in decision zone | N/A | proof/detail links below first decision | existing acknowledgement/create-next-review actions preserved, not expanded | `/admin/reviews/workspace` | existing review/evidence/pack/report routes | workspace shell + visible environment filter | Customer review | outcome, readiness, decisions, accepted risks, evidence, limitations | none | +| Environment Review View | Detail / Report | Customer/auditor review detail | inspect review outcome and evidence basis | detail page primary action | N/A | section/evidence links in supporting details | existing refresh/publish/create-next/archive/export actions preserved, not expanded | existing EnvironmentReviewResource index | existing EnvironmentReviewResource view | workspace/environment route | Review | outcome, scope, evidence, limitations | none | +| Review Pack View | Detail / Artifact | Auditor output artifact | download or verify pack readiness | detail header or first decision card | N/A | metadata/proof below readiness | existing high-impact actions remain out of first viewport | existing ReviewPackResource index | existing ReviewPackResource view | workspace/environment route | Review pack | readiness, contents, evidence, limitations | none | +| Stored Report View | Detail / Artifact | Auditor report artifact | download or view report | detail header or first decision card | N/A | storage/audit metadata below readiness | no destructive action expected; read-only navigation preserved | existing StoredReportResource index | existing StoredReportResource view | workspace/environment route | Stored report | report type, scope, readiness, evidence | none | +| Evidence Snapshot View | Detail / Evidence | Evidence proof | inspect evidence or document blocked reachability | detail page primary action | N/A | raw/support evidence in technical details | existing refresh/expire/create-snapshot actions preserved, not expanded | existing EvidenceSnapshotResource index | existing EvidenceSnapshotResource view | workspace/environment route | Evidence snapshot | subject, type, captured at, limitations | conditional reachability | + +## Operator Surface Contract + +| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions | +|---|---|---|---|---|---|---|---|---|---|---| +| Customer Review Workspace | Customer reviewer, auditor, MSP operator | decide what can be reviewed/shared or what needs decision | customer-safe review hub | What is ready and what needs customer decision? | outcome, readiness, decision-needed findings, accepted risks, evidence/report pack, limitations | raw payloads, OperationRun internals, provider/debug detail | review readiness, evidence state, pack/report availability, accepted-risk accountability | read-mostly consumption; existing acknowledgement/create-next-review actions preserved only | review decisions / view evidence / download pack | existing confirmed/audited acknowledgement and create-next-review paths preserved | +| Environment Review View | Customer/auditor, MSP operator | inspect a released review and limitations | review detail | What does this review conclude and what proof supports it? | outcome, period/scope, evidence basis, limitations, decision-needed items | source refs, raw context, operation metadata | lifecycle, readiness, evidence completeness, output availability | read-mostly detail; existing lifecycle/export actions preserved only | open evidence / open pack / review limitations | existing confirmed/audited refresh, publish, create-next, archive, and export paths preserved | +| Review Pack View | Auditor/customer, MSP operator | verify and download a review output artifact | artifact detail | Is this pack ready and what does it include? | pack status, included sections, evidence basis, limitations, download state | renderer/storage metadata, operation proof | artifact status, disclosure, evidence basis, expiry | existing export/download only | download/view pack | existing regenerate/expire if present must stay confirmed/authorized/audited | +| Stored Report View | Auditor/customer, MSP operator | verify and download a report artifact | artifact detail | What report is this and what evidence/limitations apply? | report title/type, subject/scope, readiness, evidence basis, limitations | storage metadata, raw/internal report details | report status, disclosure/readiness, evidence basis | existing read-only open-current-report navigation only | download/view report | no destructive action in current repo; preserve report capability checks | +| Evidence Snapshot View | Auditor/customer, MSP operator | verify what evidence was captured and what it supports | evidence detail | What does this evidence prove and what are its limits? | subject, type, captured at, readiness, limitations, related review/report | raw provider object, operation context, internal IDs | evidence status, capture freshness, customer-safe boundary | read-mostly evidence; existing refresh/expire/create-snapshot actions preserved only | view/download evidence if allowed | existing confirmed/audited refresh, expire, and create-snapshot paths preserved | + +## Proportionality Review *(mandatory when structural complexity is introduced)* + +- **New source of truth?**: no. +- **New persisted entity/table/artifact?**: no runtime persistence. Spec-local artifacts only. +- **New abstraction?**: no framework. Optional narrow, derived, scoped presentation helper only if implementation proves duplicated page logic would otherwise drift. +- **New enum/state/reason family?**: no. Labels derive from existing review/evidence/pack/report truth. +- **New cross-domain UI framework/taxonomy?**: no. +- **Current operator problem**: customer/auditor readers need a trustworthy handoff path that does not require operator/debug knowledge. +- **Existing structure is insufficient because**: the truth exists across separate review, pack, report, evidence, and proof pages, but customer-safe hierarchy and diagnostics separation are not uniformly applied. +- **Narrowest correct implementation**: page-local productization over the selected existing surfaces plus screenshots/checklists. +- **Ownership cost**: focused tests, bounded browser smoke, screenshots, and spec-local validation artifacts. +- **Alternative intentionally rejected**: customer portal shell, output engine rewrite, generic evidence framework, new readiness table/status family, and broad diagnostic refactor. +- **Release truth**: current-release sellability and trust hardening. + +### Compatibility posture + +This feature assumes a pre-production environment. Backward compatibility, legacy aliases, migration shims, `/admin/t` resurrection, and compatibility-specific tests are out of scope unless this spec is explicitly amended. + +## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)* + +- **Test purpose / classification**: Feature/Livewire for server-rendered state, RBAC/context, false-claim prevention, and action visibility; Browser for first-viewport hierarchy, customer-safe copy, diagnostics collapse, and screenshots. +- **Validation lane(s)**: confidence + browser + `git diff --check`; Pint dirty if PHP changes. +- **Why this classification and these lanes are sufficient**: the work is UI/productization over existing persisted truth. Focused Feature/Livewire tests prove data/action safety, while Browser smoke proves rendered hierarchy and absence of default diagnostics/internal language. +- **New or expanded test families**: implemented `Spec372CustomerAuditorSurfaceSafetyTest.php` and `Spec372CustomerAuditorSurfaceSafetySmokeTest.php`. +- **Fixture / helper cost impact**: reuse existing review-output browser fixture and existing factories/helpers. No broad seed, full workspace default, provider setup, or shared helper widening unless explicit and documented. +- **Heavy-family visibility / justification**: one bounded Browser smoke family is expected because the scoped surfaces are customer/auditor strategic output surfaces. +- **Special surface test profile**: customer-safe strategic review surface + artifact/evidence detail surfaces. +- **Standard-native relief or required special coverage**: special coverage required for no raw IDs/default OperationRun internals/provider payloads/debug terms, visible limitations, one primary action, and diagnostics collapse. +- **Reviewer handoff**: verify no customer-ready/auditor-ready/export-ready claims without repo-backed truth, no out-of-scope operator page refactors, and no broad auth/routing fix for Evidence Snapshot. +- **Budget / baseline / trend impact**: none expected beyond a bounded browser smoke. +- **Escalation needed**: `document-in-feature` for contained reachability or copy tradeoffs; `follow-up-spec` for structural diagnostic fixture gaps; `reject-or-split` if scope expands into backend/report/portal/workflow changes. +- **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. +- **Planned validation commands**: + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec372` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=CustomerReview` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=EnvironmentReview` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=ReviewPack` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=StoredReport` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=EvidenceSnapshot` + - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php --compact` + - `cd apps/platform && ./vendor/bin/sail pint --dirty` + - `git diff --check` + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Customer sees the outcome and next action first (Priority: P1) + +A customer or operator-as-facilitator opens Customer Review Workspace and sees the review outcome/readiness, decision-needed findings, accepted risks, evidence/report availability, visible limitations, and exactly one dominant customer-safe next action before diagnostics or internal metadata. + +**Why this priority**: This is the clearest customer sellability and false-calmness risk. + +**Independent Test**: Render the workspace with released review, limitation, evidence, pack, finding, and accepted-risk states; assert the first viewport is outcome-first and no raw/internal language appears by default. + +**Acceptance Scenarios**: + +1. **Given** a released review with evidence and a ready review pack, **When** the user opens Customer Review Workspace, **Then** the first viewport shows readiness, evidence/report availability, limitations if any, and one primary action. +2. **Given** findings or accepted risks require customer attention, **When** the page renders, **Then** they are visible as customer-safe decision items and not hidden in diagnostics. +3. **Given** technical detail exists, **When** the default view renders, **Then** raw/internal detail is collapsed, demoted, or capability-gated. + +### User Story 2 - Auditor reads review detail as an output, not an internal record (Priority: P1) + +A customer or auditor opens an Environment Review detail and can understand outcome, scope, evidence basis, limitations, decision-needed items, and the safe next action without parsing lifecycle repetition or operation internals. + +**Why this priority**: Environment Review View is the source review detail behind customer handoff and must not read like an internal admin record. + +**Independent Test**: Render an existing EnvironmentReviewResource view and assert outcome/evidence/limitations precede technical metadata, repeated lifecycle/status copy is reduced, and diagnostics are secondary. + +**Acceptance Scenarios**: + +1. **Given** a released review, **When** the detail page opens, **Then** outcome/readiness and scope are first, followed by evidence basis and limitations. +2. **Given** review lifecycle and source metadata exist, **When** the default view renders, **Then** they are sidebar/detail content rather than primary decision content. +3. **Given** operation proof exists, **When** the page renders, **Then** it is secondary proof and not required for customer understanding. + +### User Story 3 - Auditor verifies pack and report readiness without storage/debug framing (Priority: P1) + +A customer or auditor opens Review Pack and Stored Report details and sees artifact readiness, included scope, evidence basis, limitations, and download/view availability before storage metadata, renderer internals, or support diagnostics. + +**Why this priority**: Review Pack and Stored Report are the shareable proof surfaces; misleading or overly technical framing undermines auditor trust. + +**Independent Test**: Render ready and not-ready pack/report states; assert readiness/download labels are state-aware and limitations are visible without changing generation or renderer logic. + +**Acceptance Scenarios**: + +1. **Given** a ready Review Pack, **When** the detail page opens, **Then** pack readiness, included sections, evidence basis, limitations, and the download/view action are visible first. +2. **Given** a non-ready or limitations-bearing pack/report, **When** the detail page opens, **Then** the page does not imply customer-ready sharing and explains the limitation. +3. **Given** report storage metadata exists, **When** the default Stored Report view renders, **Then** it is secondary and the report subject/scope/readiness own the first viewport. + +### User Story 4 - Evidence Snapshot is productized if reachable, or blocked honestly if not (Priority: P2) + +An auditor can either reach Evidence Snapshot detail and see evidence subject/type/readiness/limitations with diagnostics separated, or the package documents that current fixtures do not reach it and defers fixture coverage. + +**Why this priority**: Evidence is central to auditor trust, but fixing auth/routing broadly would exceed this safety pass. + +**Independent Test**: Use existing smoke-login/browser fixture. If reachable, render and test the evidence page. If blocked, capture/document the blocked state and add follow-up recommendation. + +**Acceptance Scenarios**: + +1. **Given** Evidence Snapshot detail is reachable with existing fixtures, **When** it opens, **Then** subject, type, captured-at, readiness, related review/report, and limitations appear before raw payloads. +2. **Given** Evidence Snapshot detail redirects or is forbidden with existing fixtures, **When** browser verification runs, **Then** the blocked route, final URL, and follow-up are documented without broad auth/routing changes. + +### User Story 5 - Existing RBAC, action safety, and completed surfaces remain intact (Priority: P1) + +An authorized user sees capability-appropriate customer/auditor actions, while unauthorized or cross-scope users cannot infer records; completed operator surfaces from Spec 371 and other out-of-scope pages are not refactored. + +**Why this priority**: Customer-safe polish must not weaken workspace/tenant isolation, destructive-action safety, or completed operator workflows. + +**Independent Test**: Run targeted authorization/action tests and inspect git diff to confirm out-of-scope app surfaces are untouched unless an unavoidable shared partial impact is documented. + +**Acceptance Scenarios**: + +1. **Given** a non-member or wrong-environment actor, **When** they try to access scoped detail pages, **Then** existing deny-as-not-found behavior remains. +2. **Given** a member lacks a capability, **When** a download/diagnostic action is rendered or executed, **Then** existing capability behavior remains enforced. +3. **Given** no new destructive action is in scope, **When** implementation completes, **Then** no new destructive/high-impact action was added without a spec/plan update. + +### Edge Cases + +- Evidence Snapshot remains unreachable in current smoke fixture. +- A shared partial is used by both scoped customer/auditor pages and out-of-scope operator pages. +- A page has no current review, report, pack, or evidence artifact. +- A report/pack exists but is expired, incomplete, not customer-ready, or limitations-bearing. +- Raw/provider/operation data is needed by support but unsafe for customer/auditor default view. +- Existing tests assert old copy that is replaced by safer customer/auditor wording. + +## Functional Requirements + +- **FR-001**: Each reachable scoped page MUST lead with outcome/readiness, evidence availability, limitations, and one customer/auditor-safe next action. +- **FR-002**: Default customer/auditor views MUST NOT expose raw IDs, internal IDs, provider payloads, raw JSON, operation context payloads, internal reason families, stack traces, debug wording, or OperationRun internals. +- **FR-003**: Technical detail MUST be collapsed, demoted to aside/details, or capability-gated outside the first viewport. +- **FR-004**: Evidence and diagnostics MUST be visually and verbally separated. Evidence is proof for a review/report claim; diagnostics are technical troubleshooting. +- **FR-005**: Limitations MUST remain visible and written in calm customer/auditor language. +- **FR-006**: Each scoped page MUST have one dominant customer/auditor-safe primary action and at most two secondary actions in the first viewport. +- **FR-007**: Repeated readiness/status phrases MUST be reduced so one first-viewport block owns the current decision and later sections add proof instead of restating the same truth. +- **FR-008**: Zero/no-attention card spam MUST be suppressed unless zero is itself required proof for the page decision. +- **FR-009**: Customer Review Workspace MUST preserve visibility for decision-needed findings, accepted risks, evidence/report pack availability, and limitations. +- **FR-010**: Environment Review View MUST present review outcome, scope/period, evidence basis, decision-needed items, accepted risks where relevant, and limitations before technical metadata. +- **FR-011**: Review Pack View MUST preserve disclosure policy and output-readiness truth, keep evidence basis and limitations visible, and avoid customer-ready claims when the pack is not ready. +- **FR-012**: Stored Report View MUST present report title/type, subject/scope, readiness/disclosure state, evidence basis, limitations, and download/view state before storage metadata. +- **FR-013**: Evidence Snapshot View MUST be productized only if reachable with existing fixtures; if not reachable, the implementation MUST document the blocked state and recommend Evidence Surface Browser Fixture Coverage v1. +- **FR-014**: Existing RBAC, policy, capability, signed download, audit, and destructive/high-impact action behavior MUST remain intact. +- **FR-015**: The implementation MUST document source audit, affected files, customer surface contracts, screenshot index, implementation notes, browser verification, customer safety checklist, and validation results in the spec package. + +## Non-Functional Requirements + +- **NFR-001**: No Graph/provider calls during page render. +- **NFR-002**: No migrations, new tables, new persisted readiness state, new enum/status family, new OperationRun type, new queue family, new scheduler, or storage topology change. +- **NFR-003**: Filament v5 and Livewire v4.0+ APIs only. +- **NFR-004**: Panel provider registration remains unchanged in `apps/platform/bootstrap/providers.php`. +- **NFR-005**: No new Filament asset registration is expected; if assets are introduced unexpectedly, update spec/plan first and document `php artisan filament:assets`. +- **NFR-006**: Browser proof must use the existing smoke-login/browser fixture path where available; do not invent a new auth flow. +- **NFR-007**: Customer/auditor copy must avoid unsupported certification, compliance, or customer-ready claims. + +## Out Of Scope + +- Operator View Surfaces from Spec 371. +- Environment Dashboard, Operations Hub, OperationRun View, Backup Set View, Restore Run View, Baseline Profile View. +- Provider Connections, Environment Diagnostics, Required Permissions, System Panel. +- Review generation, evidence generation, Review Pack generator, Stored Report storage, disclosure policy logic, Report Renderer, migrations, backfills, legacy compatibility, external support/PSA handoff, billing/entitlements, private AI governance, new top-level navigation, and new customer portal shell. + +## Required Preparation And Implementation Artifacts + +- `artifacts/source-audit-summary.md` +- `artifacts/affected-files.md` +- `artifacts/customer-surface-contracts.md` +- `artifacts/before-after-screenshot-index.md` +- `artifacts/implementation-notes.md` +- `artifacts/browser-verification-report.md` +- `artifacts/customer-safety-checklist.md` +- `artifacts/validation-report.md` +- `artifacts/screenshots/` + +## Acceptance Criteria + +- **AC-001**: Spec 370 customer/auditor contract is applied to each reachable scoped page. +- **AC-002**: Spec 371 implementation patterns are reviewed and summarized, with customer/auditor-specific adaptations documented. +- **AC-003**: Customer Review Workspace is calmer, outcome-first, and still shows customer decisions, accepted risks, evidence/report access, limitations, and one primary action. +- **AC-004**: Environment Review View reads like a customer/auditor review output, not an internal record. +- **AC-005**: Review Pack View preserves output readiness/disclosure truth and keeps evidence/limitations/action hierarchy clear. +- **AC-006**: Stored Report View is auditor-ready with report scope/readiness/evidence/limitations first. +- **AC-007**: Evidence Snapshot is either productized if reachable or documented as blocked with a follow-up recommendation. +- **AC-008**: Customer safety checklist exists and covers every scoped page. +- **AC-009**: No intentional out-of-scope operator/diagnostic/system surface refactor occurs. +- **AC-010**: After screenshots or blocked screenshots/reasons exist for every scoped page. +- **AC-011**: Targeted tests and `git diff --check` pass, or limitations are explicitly documented. + +## Success Criteria + +- Customer/auditor readers can answer “What is the outcome, what evidence supports it, what limitations exist, and what should I do next?” within the first viewport on every reachable scoped surface. +- No default scoped surface requires understanding OperationRuns, provider payloads, raw JSON, or internal IDs. +- Review Pack and Stored Report outputs remain truthful and do not overclaim customer/auditor readiness. +- Evidence Snapshot reachability is no longer silently unknown: it is either verified/productized or blocked with a named follow-up. +- Scope remains bounded to the existing surfaces and no backend truth is invented. + +## Risks + +- **Hiding necessary limitations**: mitigated by requiring limitations visible by default. +- **Breaking trust through over-simplification**: mitigated by preserving evidence basis and disclosure/readiness truth. +- **Shared components affect out-of-scope pages**: mitigated by inspecting usage and preferring page-local changes. +- **Evidence Snapshot remains blocked**: mitigated by documenting blocked state and deferring fixture coverage. +- **Old tests assert replaced copy**: mitigated by updating only stale copy assertions while preserving authorization/state assertions. +- **371 pattern copied too literally**: mitigated by reusing structure, not operator wording. + +## Assumptions + +- The current branch base already includes completed Spec 371 runtime work and artifacts. +- Existing smoke-login/review-output browser fixture remains the preferred browser auth/data path. +- Customer/auditor output safety can be improved through presentation hierarchy and copy without changing review/evidence/report domain truth. +- Evidence Snapshot reachability is allowed to remain blocked if current fixtures still redirect or forbid access. + +## Open Questions + +- None blocking. Evidence Snapshot reachability is intentionally conditional and decided by repo/browser verification during implementation. + +## Follow-Up Spec Candidates + +- Spec 373 - Diagnostic Surface Separation v1. +- Evidence Surface Browser Fixture Coverage v1, if Evidence Snapshot remains blocked. +- UI Bloat Regression Guard v1, after this second consumer validates stable guard patterns. +- Localization v1 Customer-facing Surfaces, after customer/auditor safety hierarchy stabilizes. diff --git a/specs/372-customer-auditor-surface-safety-pass/tasks.md b/specs/372-customer-auditor-surface-safety-pass/tasks.md new file mode 100644 index 00000000..b8c9ccef --- /dev/null +++ b/specs/372-customer-auditor-surface-safety-pass/tasks.md @@ -0,0 +1,146 @@ +# Tasks: Spec 372 - Customer/Auditor Surface Safety Pass v1 + +**Input**: Design documents from `specs/372-customer-auditor-surface-safety-pass/` +**Prerequisites**: `spec.md`, `plan.md`, `checklists/requirements.md`, required Spec 368/370/371 input artifacts +**Tests**: Required. This is a runtime UI/productization change on customer/auditor surfaces, with Feature/Livewire coverage and bounded Browser smoke. + +## Implementation Notes For Task Completion + +- T019-T021 were implemented in the shared focused file `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` instead of separate per-surface files. +- T027 was satisfied by existing Blade composition plus payload/copy changes; no Blade edit was required. +- T055 was executed through `./vendor/bin/sail artisan test --compact tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php`, which is the repository's working Sail harness for this Pest browser file. + +## 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, and the Browser addition is explicit. +- [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 change without pulling in unrelated lane cost. +- [x] The declared surface test profile is explicit: customer-safe strategic review surface + artifact/evidence detail surfaces. +- [x] Any material budget, baseline, trend, or escalation note is recorded in the active spec or PR. + +## Phase 1: Setup And Repo Truth Gate + +**Purpose**: Confirm the current repo truth and prepare required Spec 372 artifacts before any runtime change. + +- [x] T001 Re-read `specs/372-customer-auditor-surface-safety-pass/spec.md`, `plan.md`, `tasks.md`, `.specify/memory/constitution.md`, `docs/ai-coding-rules.md`, `docs/architecture-guidelines.md`, `docs/filament-guidelines.md`, `docs/security-guidelines.md`, `docs/testing-guidelines.md`, and `docs/performance-guidelines.md`. +- [x] T002 Confirm branch and dirty state with `git status --short --branch`, `git diff --name-only`, `git diff --stat`, and `git rev-parse --short HEAD`; record the result in `specs/372-customer-auditor-surface-safety-pass/artifacts/validation-report.md`. +- [x] T003 Confirm completed context specs are read-only: `specs/342-customer-review-workspace-final-consumption-productization`, `specs/344-customer-review-workspace-density-audience-polish`, `specs/347-review-pack-output-contract-readiness-semantics`, `specs/370-global-surface-information-architecture-contract`, and `specs/371-core-operator-view-surfaces-productization`. +- [x] T004 [P] Inspect Spec 368 customer/auditor inputs in `specs/368-platform-ui-signal-to-noise-browser-audit/audit.md`, `page-scorecard.csv`, `findings.md`, `spec-candidates.md`, `artifacts/raw/browser-notes.md`, and `artifacts/screenshots/`. +- [x] T005 [P] Inspect Spec 370 inputs in `specs/370-global-surface-information-architecture-contract/artifacts/surface-contract.md`, `surface-type-matrix.md`, `ui-bloat-patterns.md`, `page-assessment-checklist.md`, `copy-and-terminology-rules.md`, and `follow-up-spec-map.md`. +- [x] T006 [P] Inspect Spec 371 inputs in `specs/371-core-operator-view-surfaces-productization/artifacts/implementation-notes.md`, `browser-verification-report.md`, `before-after-screenshot-index.md`, `page-contracts.md`, and `validation-report.md`. +- [x] T007 [P] Inspect current surface implementations in `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`, `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`, `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php`, `apps/platform/app/Filament/Resources/ReviewPackResource.php`, `apps/platform/app/Filament/Resources/StoredReportResource.php`, and `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`. +- [x] T008 [P] Inspect related tests under `apps/platform/tests/Feature/Reviews`, `apps/platform/tests/Feature/Filament`, `apps/platform/tests/Feature/ReviewPack`, `apps/platform/tests/Feature/StoredReports`, and `apps/platform/tests/Browser`. +- [x] T009 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/source-audit-summary.md` with Spec 368/370/371 inputs, before screenshots, reachability status, and verification labels. +- [x] T010 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/affected-files.md` with actual planned/touched files before runtime edits. +- [x] T011 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/customer-surface-contracts.md` with final page contracts for all scoped pages. +- [x] T012 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/before-after-screenshot-index.md` with all before screenshots and expected after/blocked screenshot names. +- [x] T013 Confirm no migration, package, env var, queue, scheduler, storage, Graph, panel-provider, route, report-renderer, disclosure-policy, or Filament asset change is required; update `spec.md` and `plan.md` before coding if false. +- [x] T014 Confirm Filament v5 / Livewire v4.0+ compliance and no Livewire v3 or Filament v3/v4 APIs. +- [x] T015 Confirm panel provider registration remains `apps/platform/bootstrap/providers.php`. +- [x] T016 Confirm no new global-search participation is introduced; preserve existing global-search posture for changed resources. + +## Phase 2: Tests And Browser Harness + +**Purpose**: Add proving tests before or alongside implementation and keep browser proof bounded. + +- [x] T017 Add Feature/Livewire coverage for Customer Review Workspace customer-safe first viewport in `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php`. +- [x] T018 Add Feature/Livewire coverage for Environment Review detail outcome/evidence/limitations hierarchy in `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` or a narrower per-surface file. +- [x] T019 [P] Add Feature/Livewire coverage for Review Pack detail readiness/evidence/limitations/download wording in `apps/platform/tests/Feature/ReviewPack/Spec372ReviewPackCustomerSafetyTest.php` if a separate file is clearer. +- [x] T020 [P] Add Feature/Livewire coverage for Stored Report detail readiness/scope/evidence/limitations/default metadata demotion in `apps/platform/tests/Feature/StoredReports/Spec372StoredReportCustomerSafetyTest.php` if a separate file is clearer. +- [x] T021 [P] Add conditional Feature/Livewire or HTTP coverage for Evidence Snapshot detail when reachable, or blocked reachability documentation assertions if not reachable, in `apps/platform/tests/Feature/Filament/Spec372EvidenceSnapshotCustomerSafetyTest.php` if a separate file is clearer. +- [x] T022 Add RBAC/context coverage proving wrong workspace/environment access remains deny-as-not-found and missing capability does not expose download/diagnostic actions in `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` or the narrower per-surface test files created by T019-T021. +- [x] T023 Add no-render-Graph-call guard coverage in `apps/platform/tests/Feature/Filament/Spec372CustomerAuditorSurfaceSafetyTest.php` or the narrower per-surface test files where current tests do not already prove scoped page render paths are DB-only. +- [x] T024 Add Browser smoke in `apps/platform/tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php` using the existing local smoke-login/review-output fixture. +- [x] T025 Browser smoke must capture after screenshots under `specs/372-customer-auditor-surface-safety-pass/artifacts/screenshots/` for every reachable scoped page and a blocked screenshot/reason for Evidence Snapshot if unreachable. + +## Phase 3: Customer Review Workspace (P1) + +**Goal**: Preserve completed Spec 342/344/347 behavior while making the first viewport calmer and customer/auditor-safe. + +**Independent Test**: Feature/Livewire and Browser checks prove outcome/readiness, decision-needed findings, accepted risks, evidence/report availability, limitations, one primary action, and no raw/internal diagnostics by default. + +- [x] T026 [US1] Update `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` only as needed to provide a single outcome/readiness/next-action payload without duplicating readiness truth. +- [x] T027 [US1] Update `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php` so the first viewport leads with customer-safe outcome, decisions/risks, evidence/report availability, limitations, and one primary action. +- [x] T028 [US1] Demote or collapse secondary proof, operation proof, technical details, and support diagnostics in `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`. +- [x] T029 [US1] Preserve visible decision-needed findings, accepted risks, evidence basis, review-pack/download state, and limitations in `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` and `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`. +- [x] T030 [US1] Remove or group repeated readiness/status phrases and zero-card spam from the default Customer Review Workspace view in `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`. +- [x] T031 [US1] Update relevant Customer Review Workspace tests in `apps/platform/tests/Feature/Reviews`, `apps/platform/tests/Feature/Filament`, and `apps/platform/tests/Browser` to assert semantics rather than stale copy, preserving all RBAC/state assertions and existing acknowledgement/create-next-review confirmation, authorization, and audit behavior. + +## Phase 4: Environment Review View (P1) + +**Goal**: Make Environment Review detail read as a customer/auditor output, not an internal lifecycle record. + +**Independent Test**: Detail page renders outcome, scope/period, evidence basis, decision-needed items, accepted risks where relevant, limitations, and one primary action before technical metadata. + +- [x] T032 [US2] Update `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php` and/or its view page so acknowledgement/outcome/readiness is the first visible decision area. +- [x] T033 [US2] Move technical review metadata, lifecycle repetition, source refs, exact non-critical timestamps, and OperationRun internals into sidebar/details/collapsed sections in `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php` and its view page if one exists. +- [x] T034 [US2] Keep evidence basis, review limitations, accepted-risk/finding context, and lifecycle truth visible without repeated peer summaries in `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php`. +- [x] T035 [US2] Ensure Environment Review detail uses customer/auditor language and avoids default troubleshooting/debug wording in `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php`. +- [x] T036 [US2] Update targeted Environment Review tests in `apps/platform/tests/Feature/Filament` or `apps/platform/tests/Feature/Reviews` to prove hierarchy, no raw/internal default content, and preserved authorization. +- [x] T036A [US2] Preserve existing Environment Review refresh, publish, create-next, archive, and export action confirmation, authorization, audit, OperationRun UX, and capability behavior. + +## Phase 5: Review Pack And Stored Report Views (P1) + +**Goal**: Keep artifact readiness, evidence basis, limitations, and download/view action clear while avoiding storage/debug framing. + +**Independent Test**: Ready and limitations-bearing pack/report states render accurate output readiness and no false customer-ready/share-ready claims. + +- [x] T037 [US3] Update `apps/platform/app/Filament/Resources/ReviewPackResource.php` and/or its view page so pack readiness, included sections, evidence basis, limitations, and download/view state own the first viewport. +- [x] T038 [US3] Preserve Review Pack generator, disclosure policy, download authorization, signed-route behavior, and existing high-impact action confirmation/audit behavior while editing `apps/platform/app/Filament/Resources/ReviewPackResource.php`. +- [x] T039 [US3] Demote Review Pack renderer/storage metadata, OperationRun proof, raw IDs, and technical metadata into secondary/collapsed detail in `apps/platform/app/Filament/Resources/ReviewPackResource.php`. +- [x] T040 [US3] Update `apps/platform/app/Filament/Resources/StoredReportResource.php` and/or its view page so report title/type, subject/scope, readiness/disclosure state, evidence basis, limitations, and download/view state own the first viewport. +- [x] T041 [US3] Demote Stored Report storage/internal metadata, exact non-critical timestamps, raw IDs, and technical report internals into secondary/collapsed detail in `apps/platform/app/Filament/Resources/StoredReportResource.php`. +- [x] T042 [US3] Update Review Pack and Stored Report tests in `apps/platform/tests/Feature/ReviewPack`, `apps/platform/tests/Feature/StoredReports`, and `apps/platform/tests/Feature/Filament` to assert state-aware labels, limitations visibility, preserved downloads, and no raw/internal default content. + +## Phase 6: Evidence Snapshot Conditional Handling (P2) + +**Goal**: Productize Evidence Snapshot detail if reachable with existing fixtures, or document the blocked state without broad auth/routing repair. + +**Independent Test**: Browser/HTTP proof shows either a customer/auditor-safe evidence detail or a documented blocked route/final URL/reason. + +- [x] T043 [US4] Use the existing smoke-login/review-output fixture in `apps/platform/app/Console/Commands/SeedReviewOutputBrowserFixture.php` and browser tests under `apps/platform/tests/Browser` to test Evidence Snapshot detail reachability; do not create a new auth flow in `apps/platform`. +- [x] T044 [US4] If reachable, update `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php` and/or its view page so subject, evidence type, captured-at, readiness, related review/report, limitations, and primary action appear before diagnostics. +- [x] T045 [US4] If reachable, move raw provider object, internal IDs, OperationRun context, and diagnostics into collapsed/capability-gated technical details in `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`. +- [x] T046 [US4] If not reachable, capture/document the blocked route, final URL/status, screenshot if possible, and follow-up `Evidence Surface Browser Fixture Coverage v1` in `artifacts/browser-verification-report.md` and `validation-report.md`. +- [x] T047 [US4] Update Evidence Snapshot tests under `apps/platform/tests/Feature/Filament`, `apps/platform/tests/Feature/Findings`, and `apps/platform/tests/Feature/Workspaces` only for semantics and reachability; preserve existing policy/global-search assertions. +- [x] T047A [US4] Preserve existing Evidence Snapshot refresh, expire, and create-snapshot confirmation, authorization, audit, OperationRun UX, and customer-workspace-flow hiding/gating behavior. + +## Phase 7: UI Coverage, Artifacts, And Validation + +**Purpose**: Complete the evidence trail and verify no out-of-scope implementation happened. + +- [x] T048 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/implementation-notes.md` with design decisions, copy changes, action hierarchy changes, metadata demotion, and shared component impact. +- [x] T049 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/browser-verification-report.md` with URLs, fixture, screenshots, scores before/after when browser-verified, Evidence Snapshot reachability, remaining issues, and blocked pages. +- [x] T050 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/customer-safety-checklist.md` with pass/fail status for every scoped page. +- [x] T051 Update `specs/372-customer-auditor-surface-safety-pass/artifacts/validation-report.md` with branch, HEAD, dirty state before/after, commands, tests, browser results, runtime files changed, out-of-scope files changed yes/no, limitations, and recommended next spec. +- [x] T052 Update relevant `docs/ui-ux-enterprise-audit/page-reports/...` for every materially changed scoped page; update `unresolved-pages.md`, `route-inventory.md`, or `design-coverage-matrix.md` only when reachability, route inventory, archetype, or coverage status changes. Record no-count-change rationale only for unchanged registries. +- [x] T053 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec372`. +- [x] T054 Run targeted existing regressions based on touched surfaces: `CustomerReview`, `EnvironmentReview`, `ReviewPack`, `StoredReport`, and `EvidenceSnapshot` filters as applicable. +- [x] T055 Run `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec372CustomerAuditorSurfaceSafetySmokeTest.php --compact` if the browser smoke file exists. +- [x] T056 Run `cd apps/platform && ./vendor/bin/sail pint --dirty` if PHP files changed. +- [x] T057 Run `git diff --check`. +- [x] T058 Confirm no migrations, seeders, packages, env vars, queues, scheduler, storage topology, Graph contracts/calls, panel providers, routes, report renderer, disclosure policy, customer portal, or legacy compatibility path were added. +- [x] T059 Confirm no intentional changes to out-of-scope pages: OperationRun View, Backup Set View, Restore Run View, Operations Hub, Environment Dashboard, Baseline Profile View, Provider Connections, Environment Diagnostics, Required Permissions, System Panel. +- [x] T060 Record final Livewire v4 compliance, provider registration location, global-search posture, destructive/high-impact action confirmation/authorization/audit status, asset strategy, tests, deployment impact, and Guardrail / Exception / Smoke Coverage in the implementation close-out response. + +## Dependencies + +- Phase 1 must complete before runtime implementation. +- Phase 2 tests should be added before or alongside each surface change. +- Phase 3 can proceed independently of Phases 4-6 after setup. +- Phase 4 Review Pack and Stored Report work can run in parallel if separate files/tests are used. +- Phase 6 is conditional and must not block Phases 3-5 if Evidence Snapshot remains unreachable; it must still be documented. +- Phase 7 closes the feature and must run after all runtime changes. + +## Parallel Execution Examples + +- T004, T005, T006, T007, and T008 can run in parallel during repo-truth inspection. +- T017, T018, T019, T020, and T021 can be split by surface after the shared test fixture strategy is known. +- T037-T039 and T040-T041 can run in parallel if Review Pack and Stored Report code paths do not share a modified helper. + +## Non-Goals / Stop Conditions + +- Stop if implementation requires new domain truth, persistence, route/auth repair, report renderer changes, disclosure policy changes, Review Pack generator changes, or OperationRun lifecycle changes. +- Stop if a shared partial change materially alters out-of-scope operator/diagnostic/system pages without a spec/plan update. +- Stop if Evidence Snapshot reachability requires broad auth/routing repair; document and defer instead.