From 39298f27f2fe3cf8d957dcd6718c07d35d5e0694 Mon Sep 17 00:00:00 2001 From: ahmido Date: Tue, 16 Jun 2026 23:36:38 +0000 Subject: [PATCH] feat(ui): implement baseline subject resolution ui (#455) Added `BaselineSubjectResolution` page and supporting logic to visualize missing identities, ambiguous matches, and skipped coverages as defined in Spec 384. Replaces legacy compare warnings with an actionable, deterministic UI surface. Co-authored-by: Ahmed Darrazi Reviewed-on: https://git.cloudarix.de/ahmido/TenantAtlas/pulls/455 --- .../Filament/Pages/BaselineCompareLanding.php | 26 + .../Pages/BaselineSubjectResolution.php | 1071 +++++++++++++++++ .../Providers/Filament/AdminPanelProvider.php | 2 + .../BaselineSubjectResolutionQuery.php | 770 ++++++++++++ .../app/Support/ManagedEnvironmentLinks.php | 9 + .../CrossResourceNavigationMatrix.php | 1 + .../Navigation/RelatedActionLabelCatalog.php | 2 + .../Navigation/RelatedNavigationResolver.php | 37 + .../app/Support/OperationRunLinks.php | 9 + .../pages/baseline-compare-landing.blade.php | 19 + .../baseline-subject-resolution.blade.php | 50 + ...c384BaselineSubjectResolutionSmokeTest.php | 164 +++ .../BaselineSubjectResolutionFixtures.php | 62 + .../BaselineSubjectResolutionPageTest.php | 370 ++++++ .../BaselineSubjectResolutionQueryTest.php | 168 +++ .../design-coverage-matrix.md | 16 +- .../ui-100-baseline-subject-resolution.md | 48 + .../ui-ux-enterprise-audit/route-inventory.md | 1 + ...spec384-01-baseline-subject-resolution.png | Bin 0 -> 122693 bytes ...baseline-subject-resolution-bind-modal.png | Bin 0 -> 128449 bytes ...-03-baseline-subject-resolution-mobile.png | Bin 0 -> 47138 bytes .../checklists/requirements.md | 77 ++ .../implementation-close-out.md | 102 ++ .../plan.md | 263 ++++ .../spec.md | 476 ++++++++ .../tasks.md | 233 ++++ 26 files changed, 3968 insertions(+), 8 deletions(-) create mode 100644 apps/platform/app/Filament/Pages/BaselineSubjectResolution.php create mode 100644 apps/platform/app/Services/Baselines/BaselineSubjectResolutionQuery.php create mode 100644 apps/platform/resources/views/filament/pages/baseline-subject-resolution.blade.php create mode 100644 apps/platform/tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php create mode 100644 apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php create mode 100644 apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php create mode 100644 docs/ui-ux-enterprise-audit/page-reports/ui-100-baseline-subject-resolution.md create mode 100644 specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png create mode 100644 specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-02-baseline-subject-resolution-bind-modal.png create mode 100644 specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-03-baseline-subject-resolution-mobile.png create mode 100644 specs/384-baseline-subject-resolution-ui/checklists/requirements.md create mode 100644 specs/384-baseline-subject-resolution-ui/implementation-close-out.md create mode 100644 specs/384-baseline-subject-resolution-ui/plan.md create mode 100644 specs/384-baseline-subject-resolution-ui/spec.md create mode 100644 specs/384-baseline-subject-resolution-ui/tasks.md diff --git a/apps/platform/app/Filament/Pages/BaselineCompareLanding.php b/apps/platform/app/Filament/Pages/BaselineCompareLanding.php index c9d29629..f6cfea3f 100644 --- a/apps/platform/app/Filament/Pages/BaselineCompareLanding.php +++ b/apps/platform/app/Filament/Pages/BaselineCompareLanding.php @@ -14,6 +14,7 @@ use App\Models\Workspace; use App\Services\Auth\CapabilityResolver; use App\Services\Baselines\BaselineCompareService; +use App\Services\Baselines\BaselineSubjectResolutionQuery; use App\Support\Auth\Capabilities; use App\Support\Baselines\BaselineCaptureMode; use App\Support\Baselines\BaselineCompareEvidenceGapDetails; @@ -26,6 +27,7 @@ use App\Support\OperationRunLinks; use App\Support\OperationRunStatus; use App\Support\OperationRunType; +use App\Support\ManagedEnvironmentLinks; use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\Rbac\UiEnforcement; @@ -192,6 +194,8 @@ public static function shouldRegisterNavigation(): bool /** @var array|null */ public ?array $summaryAssessment = null; + public ?int $subjectResolutionActionCount = null; + /** @var array|null */ public ?array $navigationContextPayload = null; @@ -312,6 +316,13 @@ private function refreshStatsForEnvironment(?ManagedEnvironment $tenant): void $this->rbacRoleDefinitionSummary = $stats->rbacRoleDefinitionSummary !== [] ? $stats->rbacRoleDefinitionSummary : null; $this->operatorExplanation = $stats->operatorExplanation()->toArray(); $this->summaryAssessment = $aggregate?->summaryAssessment->toArray(); + + $subjectResolutionSummary = $tenant instanceof ManagedEnvironment + ? app(BaselineSubjectResolutionQuery::class)->summary($tenant, $stats->operationRunId) + : null; + $this->subjectResolutionActionCount = is_array($subjectResolutionSummary) + ? (int) ($subjectResolutionSummary['actionable_count'] ?? 0) + : null; } /** @@ -411,6 +422,8 @@ protected function getViewData(): array 'whyNoFindingsColor' => $whyNoFindingsColor, 'summaryAssessment' => is_array($this->summaryAssessment) ? $this->summaryAssessment : null, 'reasonSemantics' => $reasonSemantics, + 'subjectResolutionActionCount' => (int) ($this->subjectResolutionActionCount ?? 0), + 'subjectResolutionUrl' => $this->subjectResolutionUrl(), ]; } @@ -1295,6 +1308,19 @@ public function getRunUrl(): ?string return OperationRunLinks::view($this->operationRunId, $tenant); } + public function subjectResolutionUrl(): ?string + { + $tenant = $this->currentEnvironment(); + + if (! $tenant instanceof ManagedEnvironment) { + return null; + } + + return ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant, array_filter([ + 'operation_run_id' => $this->operationRunId, + ], static fn (mixed $value): bool => $value !== null && $value !== '')); + } + public function openCompareMatrixUrl(): ?string { $profile = $this->resolveCompareMatrixProfile(); diff --git a/apps/platform/app/Filament/Pages/BaselineSubjectResolution.php b/apps/platform/app/Filament/Pages/BaselineSubjectResolution.php new file mode 100644 index 00000000..f3cd06dc --- /dev/null +++ b/apps/platform/app/Filament/Pages/BaselineSubjectResolution.php @@ -0,0 +1,1071 @@ + $parameters + */ + public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null): string + { + $panelId = $panel ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin'; + + if ($panelId !== 'admin') { + return parent::getUrl($parameters, $isAbsolute, $panelId, $tenant); + } + + $environment = static::resolveAdminUrlEnvironment($parameters, $tenant); + + if (! $environment instanceof ManagedEnvironment) { + return url('/admin'); + } + + $workspace = static::resolveAdminUrlWorkspace($environment, $parameters); + + if (! $workspace instanceof Workspace && ! is_string($workspace) && ! is_int($workspace)) { + return url('/admin'); + } + + $parameters = static::withoutLegacyScopeQuery($parameters); + $parameters['environment'] = $environment; + $parameters['workspace'] = $workspace instanceof Workspace + ? static::workspaceRouteKey($workspace) + : $workspace; + + return parent::getUrl($parameters, $isAbsolute, $panelId, null); + } + + public static function canAccess(): bool + { + $environment = static::resolveRouteOwnedEnvironment(); + $user = auth()->user(); + + if (! $environment instanceof ManagedEnvironment || ! $user instanceof User) { + return false; + } + + if (! static::routeWorkspaceMatchesEnvironment($environment)) { + return false; + } + + return app(ManagedEnvironmentAccessScopeResolver::class) + ->decision($user, $environment, Capabilities::WORKSPACE_BASELINES_VIEW) + ->allowed(); + } + + public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration + { + return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::CrudListAndView) + ->withListRowPrimaryActionLimit(1) + ->satisfy(ActionSurfaceSlot::ListHeader, 'The page header exposes only the delegated compare rerun action and neutral navigation back to compare.') + ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Row decisions are constrained to one primary binding action plus confirmed secondary decision/revoke actions.') + ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The table distinguishes no compare context from no decisions required.') + ->exempt(ActionSurfaceSlot::DetailHeader, 'Spec 384 V1 does not add a separate detail route; subject context is shown in the row and confirmed action modals.') + ->exempt(ActionSurfaceSlot::InspectAffordance, 'Rows expose compact candidate and decision context inline through the table and modal copy instead of a second detail route in v1.') + ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Baseline subject decisions are high-impact and intentionally have no bulk action in v1.'); + } + + public function mount(ManagedEnvironment|string|null $environment = null): void + { + $tenant = static::resolveRouteOwnedEnvironment($environment); + $this->authorizeEnvironmentOrAbort($tenant, Capabilities::WORKSPACE_BASELINES_VIEW); + + $this->scopedEnvironmentId = (int) $tenant->getKey(); + $this->focusedOperationRunId = $this->scopedOperationRunIdFromQuery($tenant); + $this->heading = $tenant->getFilamentName(); + $this->subheading = 'Baseline subject resolution'; + + $this->mountInteractsWithTable(); + } + + public function table(Table $table): Table + { + return $table + ->queryStringIdentifier('baselineSubjectResolution') + ->defaultSort('readiness_impact') + ->defaultPaginationPageOption(25) + ->paginated(TablePaginationProfiles::customPage()) + ->searchable() + ->searchPlaceholder('Search subject, reason, provider, decision') + ->records(function ( + ?string $sortColumn, + ?string $sortDirection, + ?string $search, + array $filters, + int $page, + int $recordsPerPage + ): LengthAwarePaginator { + $rows = $this->filteredRows($filters, $search); + $rows = $this->sortRows($rows, $sortColumn, $sortDirection); + + return $this->paginateRows($rows, $page, $recordsPerPage); + }) + ->filters([ + SelectFilter::make('provider') + ->label('Provider') + ->options(fn (): array => $this->filterOptions('provider')), + SelectFilter::make('subject_class') + ->label('Class') + ->options(fn (): array => $this->filterOptions('subject_class')), + SelectFilter::make('resource_type') + ->label('Resource type') + ->options(fn (): array => $this->filterOptions('resource_type')), + SelectFilter::make('actionability') + ->label('Actionability') + ->options(fn (): array => $this->filterOptions('actionability')), + SelectFilter::make('readiness_impact') + ->label('Readiness') + ->options(fn (): array => $this->filterOptions('readiness_impact')), + SelectFilter::make('reason') + ->label('Reason') + ->options(fn (): array => $this->filterOptions('reason')), + SelectFilter::make('active_binding') + ->label('Decision') + ->options([ + 'yes' => 'Decision recorded', + 'no' => 'No decision', + ]), + SelectFilter::make('candidates') + ->label('Candidates') + ->options([ + 'yes' => 'Has candidates', + 'no' => 'No candidates', + ]), + ]) + ->columns([ + TextColumn::make('subject_label') + ->label('Subject') + ->description(fn (Model $record): string => (string) $record->getAttribute('resource_type_label')) + ->searchable() + ->sortable() + ->wrap(), + TextColumn::make('reason_label') + ->label('Problem') + ->badge() + ->color('warning') + ->searchable() + ->sortable() + ->wrap(), + TextColumn::make('readiness_label') + ->label('Readiness') + ->badge() + ->color(fn (Model $record): string => $this->readinessColor((string) $record->getAttribute('readiness_impact'))) + ->sortable() + ->wrap(), + TextColumn::make('actionability_label') + ->label('Actionability') + ->badge() + ->color('gray') + ->sortable() + ->wrap(), + TextColumn::make('candidate_count') + ->label('Candidates') + ->numeric() + ->sortable(), + TextColumn::make('current_decision_label') + ->label('Current decision') + ->badge() + ->color(fn (Model $record): string => $record->getAttribute('active_binding_id') === null ? 'gray' : 'success') + ->sortable() + ->wrap(), + TextColumn::make('source_operation_run_id') + ->label('Source') + ->formatStateUsing(fn (mixed $state): string => is_numeric($state) ? 'Operation #'.(int) $state : 'Latest compare') + ->sortable(), + TextColumn::make('provider_label') + ->label('Provider') + ->toggleable(isToggledHiddenByDefault: true) + ->sortable(), + TextColumn::make('subject_class_label') + ->label('Class') + ->toggleable(isToggledHiddenByDefault: true) + ->sortable(), + TextColumn::make('trust_level') + ->label('Trust') + ->toggleable(isToggledHiddenByDefault: true) + ->formatStateUsing(fn (?string $state): string => $state !== null ? Str::of($state)->replace('_', ' ')->headline()->toString() : 'Unknown'), + ]) + ->actions([ + $this->bindSubjectAction(), + $this->recordDecisionAction(), + $this->revokeDecisionAction(), + ]) + ->bulkActions([]) + ->emptyStateHeading(fn (): string => $this->emptyStateHeading()) + ->emptyStateDescription(fn (): string => $this->emptyStateDescription()) + ->emptyStateActions([ + $this->runCompareEmptyStateAction(), + ]); + } + + /** + * @return array + */ + protected function getViewData(): array + { + $tenant = $this->currentEnvironment(); + $summary = $tenant instanceof ManagedEnvironment + ? $this->query()->summary($tenant, $this->focusedOperationRunId) + : [ + 'has_run' => false, + 'actionable_count' => 0, + 'source_operation_run_id' => null, + 'by_actionability' => [], + 'by_readiness_impact' => [], + 'by_reason' => [], + 'legacy_payload_only' => false, + ]; + + return [ + 'summary' => $summary, + 'compareUrl' => $tenant instanceof ManagedEnvironment ? ManagedEnvironmentLinks::baselineCompareUrl($tenant) : null, + 'sourceRunUrl' => $tenant instanceof ManagedEnvironment && is_numeric($summary['source_operation_run_id'] ?? null) + ? OperationRunLinks::view((int) $summary['source_operation_run_id'], $tenant) + : null, + ]; + } + + /** + * @return array + */ + protected function getHeaderActions(): array + { + $actions = []; + $tenant = $this->currentEnvironment(); + + if ($tenant instanceof ManagedEnvironment) { + $actions[] = Action::make('openBaselineCompare') + ->label('Open baseline compare') + ->icon('heroicon-o-arrow-left') + ->color('gray') + ->url(ManagedEnvironmentLinks::baselineCompareUrl($tenant)); + } + + $actions[] = $this->runCompareAction('runComparisonAgain'); + + return $actions; + } + + public function currentEnvironment(): ?ManagedEnvironment + { + $tenant = $this->scopedEnvironmentId === null + ? static::resolveRouteOwnedEnvironment() + : ManagedEnvironment::query() + ->withTrashed() + ->whereKey($this->scopedEnvironmentId) + ->first(); + + if (! $tenant instanceof ManagedEnvironment) { + return null; + } + + $this->authorizeEnvironmentOrAbort($tenant, Capabilities::WORKSPACE_BASELINES_VIEW); + + return $tenant; + } + + private function bindSubjectAction(): Action + { + $action = Action::make('bindSubject') + ->label('Bind subject') + ->icon('heroicon-o-link') + ->visible(fn (Model $record): bool => (int) $record->getAttribute('candidate_count') > 0) + ->form([ + Select::make('candidate_key') + ->label('Provider resource') + ->required() + ->native(false) + ->options(fn (Model $record): array => collect($record->getAttribute('candidates') ?? []) + ->mapWithKeys(fn (array $candidate): array => [ + (string) $candidate['candidate_key'] => trim(implode(' - ', array_filter([ + (string) ($candidate['display_label'] ?? 'Provider resource'), + (string) ($candidate['provider_label'] ?? ''), + (string) ($candidate['stable_identity_preview'] ?? ''), + ]))), + ]) + ->all()), + Textarea::make('operator_note') + ->label('Operator note') + ->required() + ->minLength(8) + ->rows(4) + ->helperText('TenantPilot decision only. This does not mutate the provider tenant.'), + ]) + ->requiresConfirmation() + ->modalHeading('Bind subject') + ->modalDescription('Record a TenantPilot-only subject binding. Future baseline compares can consume this active decision.') + ->modalSubmitActionLabel('Bind subject') + ->action(function (Model $record, array $data): void { + $tenant = $this->authorizedMutationEnvironment(); + $actor = $this->actorOrAbort(); + $row = $this->freshRowOrAbort($record); + $candidate = $this->candidateFromRow($row, (string) ($data['candidate_key'] ?? '')); + + app(ProviderResourceBindingService::class)->createManualBinding( + actor: $actor, + environment: $tenant, + identity: ResourceIdentity::fromArray($candidate['identity']), + attributes: $this->bindingAttributes($row, $data, $candidate), + ); + + Notification::make() + ->success() + ->title('Subject binding recorded') + ->body('Run baseline compare again to validate the decision against current evidence.') + ->send(); + + $this->resetTable(); + }); + + return UiEnforcement::forTableAction($action, fn (): ?ManagedEnvironment => $this->currentEnvironment()) + ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE) + ->preserveVisibility() + ->apply(); + } + + private function recordDecisionAction(): Action + { + $action = Action::make('recordDecision') + ->label('Record decision') + ->icon('heroicon-o-check-circle') + ->color('gray') + ->visible(fn (Model $record): bool => is_array($record->getAttribute('decision_identity')) && $record->getAttribute('active_binding_id') === null) + ->form([ + Select::make('decision') + ->label('Decision') + ->required() + ->native(false) + ->options([ + 'excluded_non_governed' => 'Exclude subject', + 'accepted_limitation' => 'Accept limitation', + 'unsupported_coverage' => 'Mark unsupported', + 'missing_expected' => 'Mark missing expected', + ]), + Textarea::make('operator_note') + ->label('Operator note') + ->required() + ->minLength(8) + ->rows(4) + ->helperText('TenantPilot decision only. This does not mutate the provider tenant.'), + ]) + ->requiresConfirmation() + ->modalHeading('Record subject decision') + ->modalDescription('Record a TenantPilot-only decision for this baseline subject. Future baseline compares can consume the active decision.') + ->modalSubmitActionLabel('Record decision') + ->action(function (Model $record, array $data): void { + $tenant = $this->authorizedMutationEnvironment(); + $actor = $this->actorOrAbort(); + $row = $this->freshRowOrAbort($record); + $identityPayload = $row['decision_identity'] ?? null; + + if (! is_array($identityPayload)) { + throw new InvalidArgumentException('A valid provider resource identity is required for this decision.'); + } + + $identity = ResourceIdentity::fromArray($identityPayload); + $attributes = $this->bindingAttributes($row, $data); + + match ((string) ($data['decision'] ?? '')) { + 'excluded_non_governed' => app(ProviderResourceBindingService::class)->createExclusion($actor, $tenant, $identity, $attributes), + 'accepted_limitation' => app(ProviderResourceBindingService::class)->createAcceptedLimitation($actor, $tenant, $identity, $attributes), + 'unsupported_coverage' => app(ProviderResourceBindingService::class)->markUnsupported($actor, $tenant, $identity, $attributes), + 'missing_expected' => app(ProviderResourceBindingService::class)->markMissingExpected($actor, $tenant, $identity, $attributes), + default => throw new InvalidArgumentException('Unsupported baseline subject decision.'), + }; + + Notification::make() + ->success() + ->title('Subject decision recorded') + ->body('Run baseline compare again to validate the decision against current evidence.') + ->send(); + + $this->resetTable(); + }); + + return UiEnforcement::forTableAction($action, fn (): ?ManagedEnvironment => $this->currentEnvironment()) + ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE) + ->preserveVisibility() + ->apply(); + } + + private function revokeDecisionAction(): Action + { + $action = Action::make('revokeDecision') + ->label('Revoke decision') + ->icon('heroicon-o-no-symbol') + ->color('danger') + ->visible(fn (Model $record): bool => is_numeric($record->getAttribute('active_binding_id'))) + ->form([ + Textarea::make('operator_note') + ->label('Operator note') + ->required() + ->minLength(8) + ->rows(4) + ->helperText('TenantPilot decision only. This does not mutate the provider tenant.'), + ]) + ->requiresConfirmation() + ->modalHeading('Revoke subject decision') + ->modalDescription('Revoke the active TenantPilot subject decision. Future baseline compares will no longer consume this decision.') + ->modalSubmitActionLabel('Revoke decision') + ->action(function (Model $record, array $data): void { + $this->authorizedMutationEnvironment(); + $actor = $this->actorOrAbort(); + $row = $this->freshRowOrAbort($record); + $bindingId = $row['active_binding_id'] ?? null; + + if (! is_numeric($bindingId)) { + throw new NotFoundHttpException; + } + + $binding = ProviderResourceBinding::query() + ->whereKey((int) $bindingId) + ->firstOrFail(); + + Gate::forUser($actor)->authorize('revoke', $binding); + + app(ProviderResourceBindingService::class)->revoke( + actor: $actor, + binding: $binding, + operatorNote: (string) ($data['operator_note'] ?? ''), + resolutionReason: 'baseline_subject_resolution_revoked', + ); + + Notification::make() + ->success() + ->title('Subject decision revoked') + ->body('Run baseline compare again to validate the updated decision state.') + ->send(); + + $this->resetTable(); + }); + + return UiEnforcement::forTableAction($action, fn (): ?ManagedEnvironment => $this->currentEnvironment()) + ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE) + ->destructive() + ->preserveVisibility() + ->apply(); + } + + private function runCompareAction(string $name): Action + { + $label = 'Run comparison again'; + + $action = Action::make($name) + ->label($label) + ->icon('heroicon-o-arrow-path') + ->requiresConfirmation() + ->modalHeading($label) + ->modalDescription('This delegates to the existing baseline compare start flow and queues a compare operation for the current environment.') + ->modalSubmitActionLabel($label) + ->action(function (): void { + $this->startBaselineCompare(); + }); + + return UiEnforcement::forAction($action, fn (): ?ManagedEnvironment => $this->currentEnvironment()) + ->requireCapability(Capabilities::TENANT_SYNC) + ->apply(); + } + + private function runCompareEmptyStateAction(): Action + { + return $this->runCompareAction('runComparisonAgainFromEmptyState') + ->visible(fn (): bool => ! $this->summary()['has_run']); + } + + private function startBaselineCompare(): void + { + $tenant = $this->currentEnvironment(); + $user = auth()->user(); + + if (! $tenant instanceof ManagedEnvironment) { + Notification::make()->title('Open an environment to compare baselines')->danger()->send(); + + return; + } + + if (! $user instanceof User) { + Notification::make()->title('Not authenticated')->danger()->send(); + + return; + } + + $result = app(BaselineCompareService::class)->startCompare($tenant, $user); + + if (! ($result['ok'] ?? false)) { + $reasonCode = is_string($result['reason_code'] ?? null) ? (string) $result['reason_code'] : 'unknown'; + $translation = is_array($result['reason_translation'] ?? null) ? $result['reason_translation'] : []; + $message = is_string($translation['short_explanation'] ?? null) && trim((string) $translation['short_explanation']) !== '' + ? trim((string) $translation['short_explanation']) + : 'Reason: '.$reasonCode; + + Notification::make() + ->title('Cannot start comparison') + ->body($message) + ->danger() + ->send(); + + return; + } + + $run = $result['run'] ?? null; + + if ($run instanceof OperationRun) { + $this->focusedOperationRunId = (int) $run->getKey(); + } + + OpsUxBrowserEvents::dispatchRunEnqueued($this); + + OperationUxPresenter::queuedToast($run instanceof OperationRun ? (string) $run->type : OperationRunType::BaselineCompare->value) + ->actions($run instanceof OperationRun ? [ + Action::make('view_run') + ->label('Open operation') + ->url(OperationRunLinks::view($run, $tenant)), + ] : []) + ->send(); + + $this->resetTable(); + } + + /** + * @return array + */ + private function bindingAttributes(array $row, array $data, ?array $candidate = null): array + { + return [ + 'subject_domain' => (string) ($row['subject_domain'] ?? 'baseline'), + 'subject_class' => (string) ($row['subject_class'] ?? 'policy_backed'), + 'subject_type_key' => (string) ($row['subject_type_key'] ?? 'unknown'), + 'canonical_subject_key' => is_string($row['canonical_subject_key'] ?? null) ? (string) $row['canonical_subject_key'] : null, + 'display_label' => is_string($candidate['display_label'] ?? null) + ? (string) $candidate['display_label'] + : (string) ($row['subject_label'] ?? ''), + 'resolution_reason' => (string) ($row['reason'] ?? 'baseline_subject_resolution'), + 'operator_note' => (string) ($data['operator_note'] ?? ''), + 'source_operation_run_id' => is_numeric($row['source_operation_run_id'] ?? null) ? (int) $row['source_operation_run_id'] : null, + 'source_baseline_snapshot_id' => is_numeric($row['source_baseline_snapshot_id'] ?? null) ? (int) $row['source_baseline_snapshot_id'] : null, + 'source_inventory_item_id' => is_numeric($candidate['source_inventory_item_id'] ?? $row['source_inventory_item_id'] ?? null) + ? (int) ($candidate['source_inventory_item_id'] ?? $row['source_inventory_item_id']) + : null, + 'source_policy_version_id' => is_numeric($candidate['source_policy_version_id'] ?? $row['source_policy_version_id'] ?? null) + ? (int) ($candidate['source_policy_version_id'] ?? $row['source_policy_version_id']) + : null, + ]; + } + + private function candidateFromRow(array $row, string $candidateKey): array + { + $candidate = collect($row['candidates'] ?? []) + ->first(fn (array $candidate): bool => (string) ($candidate['candidate_key'] ?? '') === $candidateKey); + + if (! is_array($candidate) || ! is_array($candidate['identity'] ?? null)) { + throw new InvalidArgumentException('Select a valid provider resource candidate.'); + } + + return $candidate; + } + + private function freshRowOrAbort(Model $record): array + { + $tenant = $this->currentEnvironment(); + + if (! $tenant instanceof ManagedEnvironment) { + throw new NotFoundHttpException; + } + + $row = $this->query()->row( + environment: $tenant, + rowId: (string) $record->getKey(), + filters: [ + 'operation_run_id' => $this->focusedOperationRunId, + 'include_resolved' => true, + ], + ); + + if (! is_array($row)) { + throw new NotFoundHttpException; + } + + return $row; + } + + private function authorizedMutationEnvironment(): ManagedEnvironment + { + $tenant = $this->currentEnvironment(); + $this->authorizeEnvironmentOrAbort($tenant, Capabilities::WORKSPACE_BASELINES_MANAGE); + + return $tenant; + } + + private function actorOrAbort(): User + { + $user = auth()->user(); + + if (! $user instanceof User) { + abort(403); + } + + return $user; + } + + private function authorizeEnvironmentOrAbort(?ManagedEnvironment $environment, string $capability): void + { + $user = auth()->user(); + + if (! $environment instanceof ManagedEnvironment || ! $user instanceof User) { + abort(404); + } + + if (! static::routeWorkspaceMatchesEnvironment($environment)) { + abort(404); + } + + $decision = app(ManagedEnvironmentAccessScopeResolver::class) + ->decision($user, $environment, $capability); + + if ($decision->shouldDenyAsNotFound()) { + abort(404); + } + + if ($decision->shouldDenyAsForbidden()) { + abort(403); + } + } + + /** + * @param array $filters + * @return Collection> + */ + private function filteredRows(array $filters, ?string $search): Collection + { + $tenant = $this->currentEnvironment(); + + if (! $tenant instanceof ManagedEnvironment) { + return collect(); + } + + $normalizedFilters = [ + 'operation_run_id' => $this->focusedOperationRunId, + 'provider' => data_get($filters, 'provider.value'), + 'subject_class' => data_get($filters, 'subject_class.value'), + 'resource_type' => data_get($filters, 'resource_type.value'), + 'actionability' => data_get($filters, 'actionability.value'), + 'readiness_impact' => data_get($filters, 'readiness_impact.value'), + 'reason' => data_get($filters, 'reason.value'), + 'active_binding' => data_get($filters, 'active_binding.value'), + 'candidates' => data_get($filters, 'candidates.value'), + ]; + + $rows = collect($this->query()->rows($tenant, $normalizedFilters)); + $normalizedSearch = Str::lower(trim((string) $search)); + + if ($normalizedSearch === '') { + return $rows; + } + + return $rows + ->filter(fn (array $row): bool => str_contains((string) ($row['search_text'] ?? ''), $normalizedSearch)) + ->values(); + } + + /** + * @param Collection> $rows + * @return Collection> + */ + private function sortRows(Collection $rows, ?string $sortColumn, ?string $sortDirection): Collection + { + $allowed = [ + 'subject_label', + 'reason_label', + 'readiness_label', + 'readiness_impact', + 'actionability_label', + 'candidate_count', + 'current_decision_label', + 'source_operation_run_id', + 'provider_label', + 'subject_class_label', + ]; + $sortColumn = in_array($sortColumn, $allowed, true) ? $sortColumn : 'readiness_impact'; + $descending = Str::lower((string) ($sortDirection ?? 'asc')) === 'desc'; + + return $rows->sortBy( + fn (array $row): string|int => $sortColumn === 'candidate_count' || $sortColumn === 'source_operation_run_id' + ? (int) ($row[$sortColumn] ?? 0) + : (string) ($row[$sortColumn] ?? ''), + SORT_NATURAL | SORT_FLAG_CASE, + $descending, + )->values(); + } + + /** + * @param Collection> $rows + */ + private function paginateRows(Collection $rows, int $page, int $recordsPerPage): LengthAwarePaginator + { + $perPage = max(1, $recordsPerPage); + $currentPage = max(1, $page); + $items = $rows->forPage($currentPage, $perPage) + ->values() + ->map(fn (array $row): Model => $this->toTableRecord($row)); + + return new LengthAwarePaginator( + $items, + $rows->count(), + $perPage, + $currentPage, + ); + } + + /** + * @param array $row + */ + private function toTableRecord(array $row): Model + { + $record = new class extends Model + { + public $timestamps = false; + + public $incrementing = false; + + protected $keyType = 'string'; + + protected $guarded = []; + + protected $table = 'baseline_subject_resolution_rows'; + }; + + $record->forceFill($row); + $record->exists = true; + + return $record; + } + + /** + * @return array + */ + private function filterOptions(string $key): array + { + $tenant = $this->currentEnvironment(); + + if (! $tenant instanceof ManagedEnvironment) { + return []; + } + + return $this->query()->filterOptions($tenant, $this->focusedOperationRunId, $key); + } + + private function emptyStateHeading(): string + { + $summary = $this->summary(); + + if (! $summary['has_run']) { + return 'Run baseline compare first'; + } + + if ($summary['legacy_payload_only']) { + return 'Structured subject decisions unavailable'; + } + + return 'No baseline subject decisions required'; + } + + private function emptyStateDescription(): string + { + return match ($this->emptyStateHeading()) { + 'Run baseline compare first' => 'No baseline compare run exists for this environment yet.', + 'Structured subject decisions unavailable' => 'The latest compare run does not contain Spec 383 subject outcome semantics. Run baseline compare again to refresh the decision worklist.', + default => 'The selected compare context has no unresolved or decision-required baseline subjects.', + }; + } + + /** + * @return array + */ + private function summary(): array + { + $tenant = $this->currentEnvironment(); + + if (! $tenant instanceof ManagedEnvironment) { + return ['has_run' => false, 'legacy_payload_only' => false, 'actionable_count' => 0]; + } + + return $this->query()->summary($tenant, $this->focusedOperationRunId); + } + + private function readinessColor(string $readinessImpact): string + { + return match ($readinessImpact) { + 'customer_blocker', + 'internal_blocker' => 'danger', + 'customer_limitation', + 'internal_limitation' => 'warning', + 'no_impact' => 'success', + default => 'gray', + }; + } + + private function scopedOperationRunIdFromQuery(ManagedEnvironment $tenant): ?int + { + $operationRunId = request()->query('operation_run_id'); + + if (! is_numeric($operationRunId)) { + return null; + } + + $run = $this->query()->resolveRun($tenant, (int) $operationRunId); + + return $run instanceof OperationRun ? (int) $run->getKey() : null; + } + + protected static function resolveRouteOwnedEnvironment(ManagedEnvironment|string|null $environment = null): ?ManagedEnvironment + { + if ($environment instanceof ManagedEnvironment) { + return $environment; + } + + if (is_string($environment) && $environment !== '') { + return ManagedEnvironment::query() + ->where('slug', $environment) + ->first(); + } + + $routeEnvironment = request()->route('environment') ?? request()->route('tenant'); + + if ($routeEnvironment instanceof ManagedEnvironment) { + return $routeEnvironment; + } + + if (is_string($routeEnvironment) && $routeEnvironment !== '') { + return ManagedEnvironment::query() + ->where('slug', $routeEnvironment) + ->first(); + } + + $refererEnvironment = static::resolveRefererOwnedEnvironment(); + + if ($refererEnvironment instanceof ManagedEnvironment) { + return $refererEnvironment; + } + + $filamentTenant = Filament::getTenant(); + + return $filamentTenant instanceof ManagedEnvironment ? $filamentTenant : null; + } + + private static function resolveRefererOwnedEnvironment(): ?ManagedEnvironment + { + $referer = request()->headers->get('referer'); + + if (! is_string($referer) || $referer === '') { + return null; + } + + $path = parse_url($referer, PHP_URL_PATH); + + if (! is_string($path)) { + return null; + } + + if (preg_match('#^/admin/workspaces/([^/]+)/environments/([^/]+)/baseline-subject-resolution$#', $path, $matches) !== 1) { + return null; + } + + $workspaceRouteKey = rawurldecode($matches[1]); + $environmentRouteKey = rawurldecode($matches[2]); + + $environment = ManagedEnvironment::query() + ->where('slug', $environmentRouteKey) + ->first(); + + if (! $environment instanceof ManagedEnvironment) { + return null; + } + + $workspace = $environment->workspace instanceof Workspace + ? $environment->workspace + : $environment->workspace()->first(); + + if (! $workspace instanceof Workspace) { + return null; + } + + if ($workspaceRouteKey !== static::workspaceRouteKey($workspace) && $workspaceRouteKey !== (string) $workspace->getKey()) { + return null; + } + + return $environment; + } + + private static function routeWorkspaceMatchesEnvironment(ManagedEnvironment $environment): bool + { + $routeWorkspace = request()->route('workspace'); + + if ($routeWorkspace instanceof Workspace) { + return (int) $routeWorkspace->getKey() === (int) $environment->workspace_id; + } + + if (! is_string($routeWorkspace) && ! is_int($routeWorkspace)) { + return true; + } + + $routeWorkspace = trim((string) $routeWorkspace); + + if ($routeWorkspace === '') { + return true; + } + + $workspace = $environment->workspace instanceof Workspace + ? $environment->workspace + : $environment->workspace()->first(); + + if (! $workspace instanceof Workspace) { + return false; + } + + return $routeWorkspace === static::workspaceRouteKey($workspace) + || $routeWorkspace === (string) $workspace->getKey(); + } + + /** + * @param array $parameters + */ + protected static function resolveAdminUrlEnvironment(array $parameters, ?Model $tenant = null): ?ManagedEnvironment + { + if ($tenant instanceof ManagedEnvironment) { + return $tenant; + } + + foreach (['environment', 'tenant', 'managed_environment_id', 'environment_id', 'tenant_id'] as $key) { + $value = $parameters[$key] ?? null; + + if ($value instanceof ManagedEnvironment) { + return $value; + } + + if (is_numeric($value)) { + return ManagedEnvironment::query()->whereKey((int) $value)->first(); + } + + if (is_string($value) && trim($value) !== '') { + return ManagedEnvironment::query()->where('slug', trim($value))->first(); + } + } + + return Filament::getTenant() instanceof ManagedEnvironment ? Filament::getTenant() : null; + } + + /** + * @param array $parameters + */ + protected static function resolveAdminUrlWorkspace(ManagedEnvironment $environment, array $parameters = []): Workspace|string|int|null + { + $workspace = $parameters['workspace'] ?? null; + + if ($workspace instanceof Workspace || is_string($workspace) || is_int($workspace)) { + return $workspace; + } + + return Workspace::query()->whereKey((int) $environment->workspace_id)->first(); + } + + protected static function workspaceRouteKey(Workspace $workspace): string + { + $slug = $workspace->getAttribute('slug'); + + return is_string($slug) && $slug !== '' + ? $slug + : (string) $workspace->getKey(); + } + + /** + * @param array $parameters + * @return array + */ + protected static function withoutLegacyScopeQuery(array $parameters): array + { + foreach (self::LEGACY_SCOPE_QUERY_KEYS as $key) { + unset($parameters[$key]); + } + + return $parameters; + } + + private function query(): BaselineSubjectResolutionQuery + { + return app(BaselineSubjectResolutionQuery::class); + } +} diff --git a/apps/platform/app/Providers/Filament/AdminPanelProvider.php b/apps/platform/app/Providers/Filament/AdminPanelProvider.php index fb68909f..9be44863 100644 --- a/apps/platform/app/Providers/Filament/AdminPanelProvider.php +++ b/apps/platform/app/Providers/Filament/AdminPanelProvider.php @@ -5,6 +5,7 @@ use App\Filament\Clusters\Inventory\InventoryCluster; use App\Filament\Pages\Auth\Login; use App\Filament\Pages\BaselineCompareLanding; +use App\Filament\Pages\BaselineSubjectResolution; use App\Filament\Pages\ChooseEnvironment; use App\Filament\Pages\ChooseWorkspace; use App\Filament\Pages\CrossEnvironmentComparePage; @@ -235,6 +236,7 @@ public function panel(Panel $panel): Panel ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') ->pages([ BaselineCompareLanding::class, + BaselineSubjectResolution::class, InventoryCoverage::class, EnvironmentRequiredPermissions::class, WorkspaceSettings::class, diff --git a/apps/platform/app/Services/Baselines/BaselineSubjectResolutionQuery.php b/apps/platform/app/Services/Baselines/BaselineSubjectResolutionQuery.php new file mode 100644 index 00000000..58eae1e7 --- /dev/null +++ b/apps/platform/app/Services/Baselines/BaselineSubjectResolutionQuery.php @@ -0,0 +1,770 @@ + $filters + * @return list> + */ + public function rows(ManagedEnvironment $environment, array $filters = []): array + { + $run = $this->resolveRun($environment, $this->intFilter($filters, 'operation_run_id')); + + if (! $run instanceof OperationRun) { + return []; + } + + $outcomes = $this->subjectOutcomes($run); + + if ($outcomes === []) { + return []; + } + + $activeBindings = $this->activeBindings($environment); + $inventoryDescriptors = $this->inventoryDescriptors($environment); + + $rows = collect($outcomes) + ->filter(fn (array $outcome): bool => $this->includeOutcome($outcome, $filters)) + ->values() + ->map(fn (array $outcome, int $index): array => $this->rowFromOutcome( + outcome: $outcome, + index: $index, + run: $run, + environment: $environment, + activeBindings: $activeBindings, + inventoryDescriptors: $inventoryDescriptors, + )) + ->filter(fn (array $row): bool => $this->matchesFilters($row, $filters)) + ->sortBy([ + fn (array $row): int => $this->readinessSortWeight((string) ($row['readiness_impact'] ?? '')), + fn (array $row): int => $row['active_binding_id'] !== null ? 1 : 0, + fn (array $row): string => (string) ($row['subject_label'] ?? ''), + ]) + ->values(); + + return $rows->all(); + } + + /** + * @param array $filters + */ + public function row(ManagedEnvironment $environment, string $rowId, array $filters = []): ?array + { + return collect($this->rows($environment, $filters)) + ->first(fn (array $row): bool => (string) ($row['id'] ?? '') === $rowId); + } + + /** + * @return array{ + * has_run: bool, + * source_operation_run_id: int|null, + * actionable_count: int, + * visible_count: int, + * by_actionability: array, + * by_readiness_impact: array, + * by_reason: array, + * legacy_payload_only: bool + * } + */ + public function summary(ManagedEnvironment $environment, ?int $operationRunId = null): array + { + $run = $this->resolveRun($environment, $operationRunId); + $rows = $run instanceof OperationRun + ? $this->rows($environment, ['operation_run_id' => (int) $run->getKey()]) + : []; + $outcomes = $run instanceof OperationRun ? $this->subjectOutcomes($run) : []; + + return [ + 'has_run' => $run instanceof OperationRun, + 'source_operation_run_id' => $run instanceof OperationRun ? (int) $run->getKey() : null, + 'actionable_count' => count($rows), + 'visible_count' => count($rows), + 'by_actionability' => $this->countBy($rows, 'actionability'), + 'by_readiness_impact' => $this->countBy($rows, 'readiness_impact'), + 'by_reason' => $this->countBy($rows, 'reason'), + 'legacy_payload_only' => $run instanceof OperationRun + && $outcomes === [] + && is_array(data_get($run->context, 'baseline_compare.evidence_gaps')), + ]; + } + + public function resolveRun(ManagedEnvironment $environment, ?int $operationRunId = null): ?OperationRun + { + $query = OperationRun::query() + ->where('workspace_id', (int) $environment->workspace_id) + ->where('managed_environment_id', (int) $environment->getKey()) + ->whereIn('type', OperationCatalog::rawValuesForCanonical(OperationRunType::BaselineCompare->value)); + + if ($operationRunId !== null && $operationRunId > 0) { + return $query->whereKey($operationRunId)->first(); + } + + return $query + ->latest('completed_at') + ->latest('id') + ->first(); + } + + /** + * @return array + */ + public function filterOptions(ManagedEnvironment $environment, ?int $operationRunId = null, string $key = 'all'): array + { + $rows = collect($this->rows($environment, array_filter([ + 'operation_run_id' => $operationRunId, + 'include_resolved' => true, + ], static fn (mixed $value): bool => $value !== null))); + + $map = match ($key) { + 'provider' => $rows->pluck('provider_label', 'provider_key'), + 'subject_class' => $rows->pluck('subject_class_label', 'subject_class'), + 'resource_type' => $rows->pluck('resource_type_label', 'subject_type_key'), + 'actionability' => $rows->pluck('actionability_label', 'actionability'), + 'readiness_impact' => $rows->pluck('readiness_label', 'readiness_impact'), + 'reason' => $rows->pluck('reason_label', 'reason'), + default => collect(), + }; + + return $map + ->filter(fn (mixed $label, mixed $value): bool => is_string($value) && $value !== '' && is_string($label) && $label !== '') + ->unique() + ->sort() + ->all(); + } + + /** + * @return list> + */ + private function subjectOutcomes(OperationRun $run): array + { + $outcomes = data_get($run->context, 'baseline_compare.result_semantics.subject_outcomes'); + + if (! is_array($outcomes)) { + return []; + } + + return collect($outcomes) + ->filter(fn (mixed $outcome): bool => is_array($outcome)) + ->values() + ->all(); + } + + /** + * @return EloquentCollection + */ + private function activeBindings(ManagedEnvironment $environment): EloquentCollection + { + return ProviderResourceBinding::query() + ->where('workspace_id', (int) $environment->workspace_id) + ->where('managed_environment_id', (int) $environment->getKey()) + ->where('binding_status', ProviderResourceBindingStatus::Active->value) + ->latest('decided_at') + ->get(); + } + + /** + * @return Collection + */ + private function inventoryDescriptors(ManagedEnvironment $environment): Collection + { + return InventoryItem::query() + ->where('workspace_id', (int) $environment->workspace_id) + ->where('managed_environment_id', (int) $environment->getKey()) + ->latest('last_seen_at') + ->get() + ->map(fn (InventoryItem $item): ?ProviderResourceDescriptor => $this->descriptorFromInventoryItem($item)) + ->filter() + ->values(); + } + + /** + * @param EloquentCollection $activeBindings + * @param Collection $inventoryDescriptors + * @return array + */ + private function rowFromOutcome( + array $outcome, + int $index, + OperationRun $run, + ManagedEnvironment $environment, + EloquentCollection $activeBindings, + Collection $inventoryDescriptors, + ): array { + $subject = is_array($outcome['subject'] ?? null) ? $outcome['subject'] : []; + $proof = is_array($outcome['proof'] ?? null) ? $outcome['proof'] : []; + $subjectTypeKey = $this->stringValue( + $subject['subject_type_key'] ?? $subject['policy_type'] ?? $proof['policy_type'] ?? null, + ) ?? 'unknown'; + $subjectClass = $this->stringValue($subject['subject_class'] ?? null) ?? SubjectClass::PolicyBacked->value; + $subjectDomain = $this->stringValue($subject['subject_domain'] ?? $subject['domain_key'] ?? null) ?? 'baseline'; + $subjectKey = $this->stringValue($subject['canonical_subject_key'] ?? $subject['subject_key'] ?? null); + $canonicalSubjectKey = $this->canonicalSubjectKey($subject); + $displayLabel = $this->stringValue( + $subject['display_label'] + ?? $subject['operator_label'] + ?? $subject['subject_key'] + ?? $subject['external_subject_id'] + ?? null, + ); + $providerDescriptor = $this->descriptorFromSubject($subject); + $decisionIdentity = $providerDescriptor?->identity; + $candidates = $this->candidateRows( + subject: $subject, + subjectTypeKey: $subjectTypeKey, + subjectClass: $subjectClass, + canonicalSubjectKey: $canonicalSubjectKey, + outcomeDescriptors: $this->candidateDescriptorsFromOutcome($outcome), + inventoryDescriptors: $inventoryDescriptors, + ); + $activeBinding = $this->activeBindingFor( + activeBindings: $activeBindings, + canonicalSubjectKey: $canonicalSubjectKey, + decisionIdentity: $decisionIdentity, + ); + $providerKey = $decisionIdentity?->providerKey + ?? ($candidates[0]['provider_key'] ?? null) + ?? $this->stringValue($subject['provider_key'] ?? $proof['provider_key'] ?? null) + ?? 'unknown'; + + $reason = $this->stringValue($outcome['reason'] ?? null) ?? 'unknown'; + $actionability = $this->stringValue($outcome['actionability'] ?? null) ?? 'unknown'; + $readinessImpact = $this->stringValue($outcome['readiness_impact'] ?? null) ?? 'unknown'; + $rowId = $this->rowId($run, $index, $reason, $subjectTypeKey, $subjectKey, $canonicalSubjectKey); + $sourceReferences = is_array($subject['source_references'] ?? null) ? $subject['source_references'] : []; + + return [ + 'id' => $rowId, + 'workspace_id' => (int) $environment->workspace_id, + 'managed_environment_id' => (int) $environment->getKey(), + 'source_operation_run_id' => (int) $run->getKey(), + 'source_baseline_snapshot_id' => is_numeric(data_get($run->context, 'baseline_snapshot_id')) + ? (int) data_get($run->context, 'baseline_snapshot_id') + : null, + 'subject_domain' => $subjectDomain, + 'subject_class' => $subjectClass, + 'subject_class_label' => $this->label($subjectClass), + 'subject_type_key' => $subjectTypeKey, + 'resource_type_label' => InventoryPolicyTypeMeta::baselineCompareLabel($subjectTypeKey) + ?? InventoryPolicyTypeMeta::label($subjectTypeKey) + ?? $this->label($subjectTypeKey), + 'subject_key' => $subjectKey, + 'canonical_subject_key' => $canonicalSubjectKey, + 'subject_label' => $displayLabel ?: ($subjectKey ?: $this->label($subjectTypeKey)), + 'provider_key' => (string) $providerKey, + 'provider_label' => $this->label((string) $providerKey), + 'reason' => $reason, + 'reason_label' => $this->label($reason), + 'actionability' => $actionability, + 'actionability_label' => $this->actionabilityLabel($actionability), + 'readiness_impact' => $readinessImpact, + 'readiness_label' => $this->readinessLabel($readinessImpact), + 'identity_status' => $this->stringValue($outcome['identity_status'] ?? null), + 'comparison_status' => $this->stringValue($outcome['comparison_status'] ?? null), + 'coverage_status' => $this->stringValue($outcome['coverage_status'] ?? null), + 'trust_level' => $this->stringValue($outcome['trust_level'] ?? null), + 'candidate_count' => count($candidates), + 'candidates' => $candidates, + 'has_candidates' => $candidates !== [], + 'decision_identity' => $decisionIdentity?->toArray(), + 'active_binding_id' => $activeBinding instanceof ProviderResourceBinding ? (int) $activeBinding->getKey() : null, + 'active_binding_mode' => $activeBinding instanceof ProviderResourceBinding + ? $this->enumValue($activeBinding->resolution_mode) + : null, + 'current_decision_label' => $activeBinding instanceof ProviderResourceBinding + ? $this->label($this->enumValue($activeBinding->resolution_mode)) + : 'None recorded', + 'source_inventory_item_id' => is_numeric($sourceReferences['inventory_item_id'] ?? null) + ? (int) $sourceReferences['inventory_item_id'] + : null, + 'source_policy_version_id' => is_numeric($sourceReferences['policy_version_id'] ?? null) + ? (int) $sourceReferences['policy_version_id'] + : null, + 'last_seen' => $candidates[0]['last_seen_at'] ?? null, + 'search_text' => Str::lower(implode(' ', array_filter([ + $displayLabel, + $subjectKey, + $canonicalSubjectKey, + $subjectTypeKey, + $subjectClass, + $providerKey, + $reason, + $actionability, + $readinessImpact, + $activeBinding?->display_label, + ]))), + ]; + } + + /** + * @return Collection + */ + private function candidateDescriptorsFromOutcome(array $outcome): Collection + { + return collect([ + data_get($outcome, 'candidate_descriptors'), + data_get($outcome, 'subject.candidate_descriptors'), + data_get($outcome, 'proof.candidate_descriptors'), + data_get($outcome, 'candidates'), + data_get($outcome, 'subject.candidates'), + data_get($outcome, 'proof.candidates'), + ]) + ->flatMap(function (mixed $payload): array { + if (! is_array($payload)) { + return []; + } + + return array_is_list($payload) ? $payload : [$payload]; + }) + ->map(fn (mixed $payload): ?ProviderResourceDescriptor => is_array($payload) + ? $this->descriptorFromPayload($payload) + : null) + ->filter() + ->values(); + } + + /** + * @param Collection $outcomeDescriptors + * @param Collection $inventoryDescriptors + * @return list> + */ + private function candidateRows( + array $subject, + string $subjectTypeKey, + string $subjectClass, + ?string $canonicalSubjectKey, + Collection $outcomeDescriptors, + Collection $inventoryDescriptors, + ): array { + $subjectProviderKey = $this->stringValue(data_get($subject, 'provider_resource_descriptor.identity.provider_key')); + + $outcomeRows = $outcomeDescriptors + ->filter(fn (ProviderResourceDescriptor $descriptor): bool => $this->descriptorMatchesCandidateScope( + descriptor: $descriptor, + subjectTypeKey: $subjectTypeKey, + subjectClass: $subjectClass, + canonicalSubjectKey: $canonicalSubjectKey, + subjectProviderKey: $subjectProviderKey, + requireCanonicalMatch: false, + )) + ->map(fn (ProviderResourceDescriptor $descriptor): array => $this->candidateRow($descriptor)) + ->values(); + + $inventoryRows = $inventoryDescriptors + ->filter(fn (ProviderResourceDescriptor $descriptor): bool => $this->descriptorMatchesCandidateScope( + descriptor: $descriptor, + subjectTypeKey: $subjectTypeKey, + subjectClass: $subjectClass, + canonicalSubjectKey: $canonicalSubjectKey, + subjectProviderKey: $subjectProviderKey, + requireCanonicalMatch: true, + )) + ->map(fn (ProviderResourceDescriptor $descriptor): array => $this->candidateRow($descriptor)) + ->values(); + + return $outcomeRows + ->merge($inventoryRows) + ->unique('candidate_key') + ->values() + ->all(); + } + + private function descriptorMatchesCandidateScope( + ProviderResourceDescriptor $descriptor, + string $subjectTypeKey, + string $subjectClass, + ?string $canonicalSubjectKey, + ?string $subjectProviderKey, + bool $requireCanonicalMatch, + ): bool { + if ($descriptor->subjectTypeKey !== $subjectTypeKey) { + return false; + } + + $descriptorClass = $descriptor->subjectClass instanceof SubjectClass + ? $descriptor->subjectClass->value + : (string) $descriptor->subjectClass; + + if ($subjectClass !== '' && $descriptorClass !== $subjectClass) { + return false; + } + + if ($subjectProviderKey !== null && $descriptor->identity->providerKey !== $subjectProviderKey) { + return false; + } + + $descriptorCanonicalKey = BaselineSubjectKey::forProviderResourceIdentity( + $descriptor->subjectDomain, + $descriptor->subjectClass, + $descriptor->subjectTypeKey, + $descriptor->identity, + ); + + if ($canonicalSubjectKey !== null && $descriptorCanonicalKey === $canonicalSubjectKey) { + return true; + } + + return ! $requireCanonicalMatch; + } + + /** + * @return array + */ + private function candidateRow(ProviderResourceDescriptor $descriptor): array + { + $identity = $descriptor->identity; + $sourceReferences = $descriptor->sourceReferences; + + return [ + 'candidate_key' => $identity->fingerprint(), + 'identity' => $identity->toArray(), + 'display_label' => $descriptor->displayLabel ?: $this->label((string) ($identity->providerResourceType ?? 'resource')), + 'provider_key' => $identity->providerKey, + 'provider_label' => $this->label($identity->providerKey), + 'provider_resource_type' => $identity->providerResourceType, + 'identity_kind' => $identity->identityKind, + 'stable_identity_preview' => $this->preview($identity->stableIdentityValue()), + 'source_inventory_item_id' => is_numeric($sourceReferences['inventory_item_id'] ?? null) + ? (int) $sourceReferences['inventory_item_id'] + : null, + 'source_policy_version_id' => is_numeric($sourceReferences['policy_version_id'] ?? null) + ? (int) $sourceReferences['policy_version_id'] + : null, + 'last_seen_at' => $descriptor->lastSeenAt, + ]; + } + + private function descriptorFromSubject(array $subject): ?ProviderResourceDescriptor + { + $descriptorPayload = $subject['provider_resource_descriptor'] ?? null; + + return is_array($descriptorPayload) ? $this->descriptorFromPayload($descriptorPayload) : null; + } + + /** + * @param array $payload + */ + private function descriptorFromPayload(array $payload): ?ProviderResourceDescriptor + { + $descriptorPayload = $payload['provider_resource_descriptor'] ?? $payload['descriptor'] ?? $payload; + + if (! is_array($descriptorPayload)) { + return null; + } + + try { + return ProviderResourceDescriptor::fromArray($descriptorPayload); + } catch (InvalidArgumentException) { + return null; + } + } + + private function descriptorFromInventoryItem(InventoryItem $inventoryItem): ?ProviderResourceDescriptor + { + $metaJsonb = is_array($inventoryItem->meta_jsonb) ? $inventoryItem->meta_jsonb : []; + $descriptorPayload = $metaJsonb['provider_resource_descriptor'] ?? null; + + if (is_array($descriptorPayload)) { + try { + return ProviderResourceDescriptor::fromArray($descriptorPayload); + } catch (InvalidArgumentException) { + return null; + } + } + + $identity = $this->resourceIdentityFromMeta( + metaJsonb: $metaJsonb, + fallbackProviderKey: $this->stringValue($metaJsonb['provider_key'] ?? $metaJsonb['provider'] ?? null) ?? 'inventory', + fallbackResourceType: $this->stringValue($metaJsonb['provider_resource_type'] ?? $metaJsonb['resource_type'] ?? null) + ?? (string) $inventoryItem->policy_type, + fallbackResourceId: $this->stringValue($inventoryItem->external_id), + ); + + if (! $identity instanceof ResourceIdentity) { + return null; + } + + return ProviderResourceDescriptor::fromIdentity( + identity: $identity, + subjectDomain: $this->stringValue($metaJsonb['subject_domain'] ?? null) ?? 'baseline', + subjectClass: $this->stringValue($metaJsonb['subject_class'] ?? null) ?? SubjectClass::PolicyBacked->value, + subjectTypeKey: (string) $inventoryItem->policy_type, + displayLabel: $this->stringValue($inventoryItem->display_name) + ?? $this->stringValue($metaJsonb['display_name'] ?? null), + sourceReferences: [ + 'inventory_item_id' => (int) $inventoryItem->getKey(), + 'external_id' => (string) $inventoryItem->external_id, + ], + fingerprint: $this->stringValue($metaJsonb['provider_resource_fingerprint'] ?? null) ?? $identity->fingerprint(), + lastSeenAt: $inventoryItem->last_seen_at?->toIso8601String(), + ); + } + + /** + * @param array $metaJsonb + */ + private function resourceIdentityFromMeta( + array $metaJsonb, + ?string $fallbackProviderKey = null, + ?string $fallbackResourceType = null, + ?string $fallbackResourceId = null, + ): ?ResourceIdentity { + $descriptorPayload = $metaJsonb['provider_resource_descriptor'] ?? null; + $identityPayload = is_array($descriptorPayload) ? ($descriptorPayload['identity'] ?? null) : null; + + if (! is_array($identityPayload)) { + $identityPayload = $metaJsonb['provider_resource_identity'] ?? null; + } + + if (is_array($identityPayload)) { + try { + return ResourceIdentity::fromArray($identityPayload); + } catch (InvalidArgumentException) { + return null; + } + } + + $providerKey = $this->stringValue($metaJsonb['provider_key'] ?? $metaJsonb['provider'] ?? null) ?? $fallbackProviderKey; + $resourceType = $this->stringValue($metaJsonb['provider_resource_type'] ?? $metaJsonb['resource_type'] ?? $metaJsonb['provider_object_type'] ?? null) + ?? $fallbackResourceType; + $resourceId = $this->stringValue($metaJsonb['provider_resource_id'] ?? $metaJsonb['resource_id'] ?? null) ?? $fallbackResourceId; + $discriminator = $this->stringValue($metaJsonb['provider_resource_discriminator'] ?? $metaJsonb['canonical_discriminator'] ?? null); + $identityKind = $this->stringValue($metaJsonb['provider_resource_identity_kind'] ?? $metaJsonb['identity_kind'] ?? null) + ?? ResourceIdentity::ProviderResource; + + if ($providerKey === null || $resourceType === null) { + return null; + } + + try { + return new ResourceIdentity( + providerKey: $providerKey, + identityKind: $identityKind, + providerResourceType: $resourceType, + providerResourceId: $identityKind === ResourceIdentity::ProviderResource ? $resourceId : null, + canonicalDiscriminator: $identityKind === ResourceIdentity::ProviderResource ? null : $discriminator, + ); + } catch (InvalidArgumentException) { + return null; + } + } + + /** + * @param EloquentCollection $activeBindings + */ + private function activeBindingFor( + EloquentCollection $activeBindings, + ?string $canonicalSubjectKey, + ?ResourceIdentity $decisionIdentity, + ): ?ProviderResourceBinding { + return $activeBindings->first(function (ProviderResourceBinding $binding) use ($canonicalSubjectKey, $decisionIdentity): bool { + if ($canonicalSubjectKey !== null && (string) $binding->canonical_subject_key === $canonicalSubjectKey) { + return true; + } + + return $decisionIdentity instanceof ResourceIdentity + && (string) $binding->provider_key === $decisionIdentity->providerKey + && (string) $binding->provider_resource_fingerprint === $decisionIdentity->fingerprint(); + }); + } + + private function canonicalSubjectKey(array $subject): ?string + { + $candidate = $this->stringValue($subject['canonical_subject_key'] ?? $subject['subject_key'] ?? null); + + return BaselineSubjectKey::isProviderResourceCanonicalKey($candidate) ? $candidate : null; + } + + /** + * @param array $filters + */ + private function includeOutcome(array $outcome, array $filters): bool + { + if ((bool) ($filters['include_resolved'] ?? false)) { + return true; + } + + $actionability = $this->stringValue($outcome['actionability'] ?? null); + + return ! in_array($actionability, [ + CompareResultActionability::None->value, + CompareResultActionability::Accepted->value, + CompareResultActionability::Excluded->value, + ], true); + } + + /** + * @param array $filters + */ + private function matchesFilters(array $row, array $filters): bool + { + $stringFilters = [ + 'provider' => 'provider_key', + 'subject_class' => 'subject_class', + 'resource_type' => 'subject_type_key', + 'actionability' => 'actionability', + 'readiness_impact' => 'readiness_impact', + 'reason' => 'reason', + ]; + + foreach ($stringFilters as $filterKey => $rowKey) { + $filterValue = $this->stringFilter($filters, $filterKey); + + if ($filterValue !== null && (string) ($row[$rowKey] ?? '') !== $filterValue) { + return false; + } + } + + $activeBinding = $this->stringFilter($filters, 'active_binding'); + if ($activeBinding === 'yes' && $row['active_binding_id'] === null) { + return false; + } + + if ($activeBinding === 'no' && $row['active_binding_id'] !== null) { + return false; + } + + $candidates = $this->stringFilter($filters, 'candidates'); + if ($candidates === 'yes' && ! (bool) ($row['has_candidates'] ?? false)) { + return false; + } + + if ($candidates === 'no' && (bool) ($row['has_candidates'] ?? false)) { + return false; + } + + return true; + } + + /** + * @param list> $rows + * @return array + */ + private function countBy(array $rows, string $key): array + { + return collect($rows) + ->countBy(fn (array $row): string => (string) ($row[$key] ?? 'unknown')) + ->sortKeys() + ->all(); + } + + private function rowId(OperationRun $run, int $index, string $reason, string $subjectTypeKey, ?string $subjectKey, ?string $canonicalSubjectKey): string + { + return hash('sha256', implode('|', [ + (string) $run->getKey(), + (string) $index, + $reason, + $subjectTypeKey, + (string) ($canonicalSubjectKey ?? $subjectKey ?? 'subject'), + ])); + } + + private function intFilter(array $filters, string $key): ?int + { + $value = $filters[$key] ?? null; + + return is_numeric($value) ? (int) $value : null; + } + + private function stringFilter(array $filters, string $key): ?string + { + $value = $filters[$key] ?? null; + $value = is_array($value) ? ($value['value'] ?? null) : $value; + + return $this->stringValue($value); + } + + private function stringValue(mixed $value): ?string + { + if (! is_string($value) && ! is_numeric($value)) { + return null; + } + + $value = trim((string) $value); + + return $value !== '' ? $value : null; + } + + private function enumValue(mixed $value): string + { + if ($value instanceof \BackedEnum) { + return (string) $value->value; + } + + return (string) $value; + } + + private function label(string $value): string + { + $value = trim($value); + + return $value === '' ? 'Unknown' : Str::of($value)->replace(['_', '-'], ' ')->headline()->toString(); + } + + private function actionabilityLabel(string $actionability): string + { + return match ($actionability) { + CompareResultActionability::BindingRequired->value => 'Binding required', + CompareResultActionability::OperatorActionRequired->value => 'Operator decision required', + CompareResultActionability::ProviderDataRefreshRequired->value => 'Refresh provider data', + CompareResultActionability::ImplementationGap->value => 'Implementation gap', + CompareResultActionability::ScopeDecisionRequired->value => 'Scope decision required', + default => $this->label($actionability), + }; + } + + private function readinessLabel(string $readinessImpact): string + { + return match ($readinessImpact) { + 'customer_blocker' => 'Customer blocker', + 'internal_blocker' => 'Internal blocker', + 'customer_limitation' => 'Customer limitation', + 'internal_limitation' => 'Internal limitation', + 'no_impact' => 'No impact', + default => $this->label($readinessImpact), + }; + } + + private function readinessSortWeight(string $readinessImpact): int + { + return match ($readinessImpact) { + 'customer_blocker' => 0, + 'internal_blocker' => 1, + 'customer_limitation' => 2, + 'internal_limitation' => 3, + default => 4, + }; + } + + private function preview(?string $value): ?string + { + if ($value === null || trim($value) === '') { + return null; + } + + $value = trim($value); + + return Str::length($value) > 16 ? Str::substr($value, 0, 10).'...'.Str::substr($value, -4) : $value; + } +} diff --git a/apps/platform/app/Support/ManagedEnvironmentLinks.php b/apps/platform/app/Support/ManagedEnvironmentLinks.php index 506b6f2b..65050d6c 100644 --- a/apps/platform/app/Support/ManagedEnvironmentLinks.php +++ b/apps/platform/app/Support/ManagedEnvironmentLinks.php @@ -3,6 +3,7 @@ namespace App\Support; use App\Filament\Pages\BaselineCompareLanding; +use App\Filament\Pages\BaselineSubjectResolution; use App\Filament\Resources\ProviderConnectionResource; use App\Models\ManagedEnvironment; use App\Models\ProviderConnection; @@ -77,6 +78,14 @@ public static function baselineCompareUrl(ManagedEnvironment $environment, array return BaselineCompareLanding::getUrl($query, panel: 'admin', tenant: $environment); } + /** + * @param array $query + */ + public static function baselineSubjectResolutionUrl(ManagedEnvironment $environment, array $query = []): string + { + return BaselineSubjectResolution::getUrl($query, panel: 'admin', tenant: $environment); + } + /** * @param array $query */ diff --git a/apps/platform/app/Support/Navigation/CrossResourceNavigationMatrix.php b/apps/platform/app/Support/Navigation/CrossResourceNavigationMatrix.php index 61034d4f..f2c362d3 100644 --- a/apps/platform/app/Support/Navigation/CrossResourceNavigationMatrix.php +++ b/apps/platform/app/Support/Navigation/CrossResourceNavigationMatrix.php @@ -77,6 +77,7 @@ private function rules(): array new NavigationMatrixRule(self::SOURCE_OPERATION_RUN, self::SURFACE_DETAIL_SECTION, 'evidence_snapshot', 'evidence_snapshot', 'direct_record', 55, missingStatePolicy: 'hide'), new NavigationMatrixRule(self::SOURCE_OPERATION_RUN, self::SURFACE_DETAIL_SECTION, 'environment_review', 'environment_review', 'direct_record', 56, missingStatePolicy: 'hide'), new NavigationMatrixRule(self::SOURCE_OPERATION_RUN, self::SURFACE_DETAIL_SECTION, 'review_pack', 'review_pack', 'direct_record', 57, missingStatePolicy: 'hide'), + new NavigationMatrixRule(self::SOURCE_OPERATION_RUN, self::SURFACE_DETAIL_SECTION, 'baseline_subject_resolution', 'baseline_subject_resolution', 'canonical_page', 58, missingStatePolicy: 'hide'), new NavigationMatrixRule(self::SOURCE_OPERATION_RUN, self::SURFACE_DETAIL_SECTION, 'operations', 'operations', 'canonical_page', 60, missingStatePolicy: 'hide'), new NavigationMatrixRule(self::SOURCE_BASELINE_PROFILE, self::SURFACE_DETAIL_HEADER, 'baseline_snapshot', 'baseline_snapshot', 'direct_record', 10, missingStatePolicy: 'hide'), diff --git a/apps/platform/app/Support/Navigation/RelatedActionLabelCatalog.php b/apps/platform/app/Support/Navigation/RelatedActionLabelCatalog.php index 09762479..ba12dbe8 100644 --- a/apps/platform/app/Support/Navigation/RelatedActionLabelCatalog.php +++ b/apps/platform/app/Support/Navigation/RelatedActionLabelCatalog.php @@ -16,6 +16,7 @@ final class RelatedActionLabelCatalog 'baseline_profile' => 'Baseline profile', 'baseline_snapshot' => 'Snapshot', 'backup_set' => 'Backup set', + 'baseline_subject_resolution' => 'Baseline subject resolution', 'current_policy_version' => 'Current policy version', 'environment_review' => 'ManagedEnvironment Review', 'evidence_snapshot' => 'Evidence snapshot', @@ -34,6 +35,7 @@ final class RelatedActionLabelCatalog 'baseline_profile' => 'View baseline profile', 'baseline_snapshot' => 'View snapshot', 'backup_set' => 'View backup set', + 'baseline_subject_resolution' => 'Resolve baseline subjects', 'current_policy_version' => 'View policy version', 'environment_review' => 'ManagedEnvironment Review', 'evidence_snapshot' => 'View evidence snapshot', diff --git a/apps/platform/app/Support/Navigation/RelatedNavigationResolver.php b/apps/platform/app/Support/Navigation/RelatedNavigationResolver.php index e70d7648..d7060742 100644 --- a/apps/platform/app/Support/Navigation/RelatedNavigationResolver.php +++ b/apps/platform/app/Support/Navigation/RelatedNavigationResolver.php @@ -36,7 +36,10 @@ use App\Services\Auth\CapabilityResolver; use App\Services\Auth\WorkspaceCapabilityResolver; use App\Support\Auth\Capabilities; +use App\Services\Baselines\BaselineSubjectResolutionQuery; +use App\Support\ManagedEnvironmentLinks; use App\Support\OperateHub\OperateHubShell; +use App\Support\OperationRunType; use App\Support\OperationRunLinks; use App\Support\References\ReferenceClass; use App\Support\References\ReferenceDescriptor; @@ -518,6 +521,7 @@ private function resolveOperationRunRule(NavigationMatrixRule $rule, OperationRu 'evidence_snapshot' => $this->evidenceSnapshotEntry($rule, $run), 'environment_review' => $this->environmentReviewEntry($rule, $run), 'review_pack' => $this->reviewPackEntry($rule, $run), + 'baseline_subject_resolution' => $this->baselineSubjectResolutionEntry($rule, $run), 'operations' => $this->operationsEntry( rule: $rule, tenant: $run->tenant, @@ -788,6 +792,39 @@ private function reviewPackEntry(NavigationMatrixRule $rule, OperationRun $run): ); } + private function baselineSubjectResolutionEntry(NavigationMatrixRule $rule, OperationRun $run): ?RelatedContextEntry + { + $tenant = $run->tenant; + + if ( + ! $tenant instanceof ManagedEnvironment + || $run->canonicalOperationType() !== OperationRunType::BaselineCompare->value + || ! $this->canOpenTenantRecord($tenant, Capabilities::WORKSPACE_BASELINES_VIEW) + ) { + return null; + } + + $summary = app(BaselineSubjectResolutionQuery::class)->summary($tenant, (int) $run->getKey()); + + if ((int) ($summary['actionable_count'] ?? 0) <= 0) { + return null; + } + + return RelatedContextEntry::available( + key: $rule->relationKey, + label: $this->labels->entryLabel($rule->relationKey), + value: 'Baseline subject resolution', + secondaryValue: OperationRunLinks::identifier($run), + targetUrl: ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ]), + targetKind: $rule->targetType, + priority: $rule->priority, + actionLabel: $this->labels->actionLabel($rule->relationKey), + contextBadge: 'Baseline compare', + ); + } + private function parentPolicyEntryForFinding(NavigationMatrixRule $rule, Finding $finding): ?RelatedContextEntry { $policyVersionId = $this->findingPolicyVersionId($finding); diff --git a/apps/platform/app/Support/OperationRunLinks.php b/apps/platform/app/Support/OperationRunLinks.php index 10f6eef1..c086a1b4 100644 --- a/apps/platform/app/Support/OperationRunLinks.php +++ b/apps/platform/app/Support/OperationRunLinks.php @@ -20,6 +20,7 @@ use App\Models\RestoreRun; use App\Models\ReviewPack; use App\Models\Workspace; +use App\Services\Baselines\BaselineSubjectResolutionQuery; use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Workspaces\WorkspaceContext; @@ -226,6 +227,14 @@ public static function related(OperationRun $run, ?ManagedEnvironment $tenant): if ($canonicalType === 'baseline.compare') { $links['Drift'] = ManagedEnvironmentLinks::baselineCompareUrl($tenant); + + $summary = app(BaselineSubjectResolutionQuery::class)->summary($tenant, (int) $run->getKey()); + + if ((int) ($summary['actionable_count'] ?? 0) > 0) { + $links['Baseline Subject Resolution'] = ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ]); + } } if ($canonicalType === 'baseline.capture') { diff --git a/apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php b/apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php index 12c31b98..92c28f8a 100644 --- a/apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php +++ b/apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php @@ -154,6 +154,25 @@ + @if (($subjectResolutionActionCount ?? 0) > 0 && filled($subjectResolutionUrl ?? null)) + +
+
+
+ Baseline subject decisions +
+
+ {{ (int) $subjectResolutionActionCount }} {{ \Illuminate\Support\Str::plural('subject', (int) $subjectResolutionActionCount) }} need identity or coverage decisions before compare output is fully trustworthy. +
+
+ + + Resolve baseline subjects + +
+
+ @endif + @if (! empty($compareReadinessFlow))
diff --git a/apps/platform/resources/views/filament/pages/baseline-subject-resolution.blade.php b/apps/platform/resources/views/filament/pages/baseline-subject-resolution.blade.php new file mode 100644 index 00000000..98608652 --- /dev/null +++ b/apps/platform/resources/views/filament/pages/baseline-subject-resolution.blade.php @@ -0,0 +1,50 @@ + + @php + $summary = is_array($summary ?? null) ? $summary : []; + $actionableCount = (int) ($summary['actionable_count'] ?? 0); + $sourceRunId = $summary['source_operation_run_id'] ?? null; + $legacyPayloadOnly = (bool) ($summary['legacy_payload_only'] ?? false); + @endphp + +
+ +
+
+
+ + {{ $actionableCount }} {{ \Illuminate\Support\Str::plural('decision', $actionableCount) }} required + + + @if (is_numeric($sourceRunId)) + + Operation #{{ (int) $sourceRunId }} + + @endif +
+ +
+ Baseline subject decisions are TenantPilot-only records. They do not mutate the provider tenant. +
+
+ +
+ @if (filled($sourceRunUrl ?? null)) + + Open operation + + @endif + + @if (filled($compareUrl ?? null)) + + Open baseline compare + + @endif +
+
+
+ + + {{ $this->table }} + +
+
diff --git a/apps/platform/tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php b/apps/platform/tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php new file mode 100644 index 00000000..1630f47f --- /dev/null +++ b/apps/platform/tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php @@ -0,0 +1,164 @@ +browser()->timeout(45_000); + +uses(RefreshDatabase::class); + +it('Spec384 smokes baseline subject resolution reachability and action modal', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); + $tenant->forceFill(['name' => 'Spec384 Subject Resolution Environment'])->save(); + + $run = spec384BrowserSubjectResolutionRun($tenant); + $resolutionPath = spec384BrowserPath(ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ])); + + $page = visit(spec384BrowserLoginUrl($user, $tenant, $resolutionPath)) + ->resize(1440, 1100) + ->waitForText('Baseline subject resolution') + ->assertSee('Spec384 Subject Resolution Environment') + ->assertSee('1 decision required') + ->assertSee('Decision worklist') + ->assertSee('Duplicate policy') + ->assertSee('Unresolved Duplicate Candidates') + ->assertSee('Binding required') + ->assertSee('TenantPilot-only') + ->assertSee('Open operation') + ->assertSee('Open baseline compare') + ->assertSee('Bind subject') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, spec384BrowserScreenshotName('01-baseline-subject-resolution')); + + spec384BrowserCopyScreenshot('01-baseline-subject-resolution'); + + $page + ->click('Bind subject') + ->waitForText('Record a TenantPilot-only subject binding') + ->assertSee('Provider resource') + ->assertSee('Operator note') + ->assertSee('This does not mutate the provider tenant') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, spec384BrowserScreenshotName('02-baseline-subject-resolution-bind-modal')); + + spec384BrowserCopyScreenshot('02-baseline-subject-resolution-bind-modal'); + + $page + ->resize(430, 900) + ->assertScript('document.documentElement.scrollWidth <= window.innerWidth', true) + ->assertNoJavaScriptErrors() + ->screenshot(true, spec384BrowserScreenshotName('03-baseline-subject-resolution-mobile')); + + spec384BrowserCopyScreenshot('03-baseline-subject-resolution-mobile'); +}); + +function spec384BrowserSubjectResolutionRun(ManagedEnvironment $tenant) +{ + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + $leftIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-left'); + $rightIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-right'); + $leftDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($leftIdentity, 'Duplicate policy'); + $rightDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($rightIdentity, 'Duplicate policy'); + + BaselineSubjectResolutionFixtures::inventoryCandidate( + $tenant, + $leftIdentity, + 'Duplicate policy', + ); + BaselineSubjectResolutionFixtures::inventoryCandidate( + $tenant, + $rightIdentity, + 'Duplicate policy', + ); + + return seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'unresolved_duplicate_candidates', + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'legacy-display-key', + 'display_label' => 'Duplicate policy', + 'candidate_descriptors' => [ + $leftDescriptor->toArray(), + $rightDescriptor->toArray(), + ], + ], + ]), + ], + ], + ], + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); +} + +function spec384BrowserLoginUrl(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 spec384BrowserPath(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 spec384BrowserScreenshotName(string $name): string +{ + return 'spec384-'.$name; +} + +function spec384BrowserCopyScreenshot(string $name): void +{ + $filename = spec384BrowserScreenshotName($name).'.png'; + $source = base_path('tests/Browser/Screenshots/'.$filename); + $targetDirectory = repo_path('specs/384-baseline-subject-resolution-ui/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/Baselines/Support/BaselineSubjectResolutionFixtures.php b/apps/platform/tests/Feature/Baselines/Support/BaselineSubjectResolutionFixtures.php index 05744e7c..5abff4b6 100644 --- a/apps/platform/tests/Feature/Baselines/Support/BaselineSubjectResolutionFixtures.php +++ b/apps/platform/tests/Feature/Baselines/Support/BaselineSubjectResolutionFixtures.php @@ -4,10 +4,13 @@ namespace Tests\Feature\Baselines\Support; +use App\Models\InventoryItem; use App\Support\Baselines\OperatorActionCategory; use App\Support\Baselines\ResolutionOutcome; use App\Support\Baselines\ResolutionPath; use App\Support\Baselines\SubjectClass; +use App\Support\Resources\ProviderResourceDescriptor; +use App\Support\Resources\ResourceIdentity; final class BaselineSubjectResolutionFixtures { @@ -82,4 +85,63 @@ public static function captureContext(array $subjects, array $overrides = []): a ], ], $overrides); } + + /** + * @param array $overrides + * @return array + */ + public static function semanticOutcome(array $overrides = []): array + { + return array_replace_recursive([ + 'reason' => 'identity_required', + 'category' => 'action_required', + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'identity_status' => 'unresolved', + 'comparison_status' => 'not_compared', + 'coverage_status' => 'missing_local_evidence', + 'trust_level' => 'untrusted', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'subject-key', + 'display_label' => 'Subject label', + ], + 'proof' => [], + ], $overrides); + } + + public static function inventoryCandidate(mixed $tenant, ResourceIdentity $identity, string $displayName): InventoryItem + { + $descriptor = self::providerDescriptor($identity, $displayName); + + return InventoryItem::factory()->create([ + 'workspace_id' => (int) $tenant->workspace_id, + 'managed_environment_id' => (int) $tenant->getKey(), + 'policy_type' => 'deviceConfiguration', + 'external_id' => $identity->providerResourceId, + 'display_name' => $displayName, + 'meta_jsonb' => [ + 'provider_resource_descriptor' => $descriptor->toArray(), + 'provider_resource_identity' => $identity->toArray(), + 'provider_resource_fingerprint' => $identity->fingerprint(), + ], + 'last_seen_at' => now(), + ]); + } + + public static function providerDescriptor(ResourceIdentity $identity, string $displayName): ProviderResourceDescriptor + { + return ProviderResourceDescriptor::fromIdentity( + identity: $identity, + subjectDomain: 'baseline', + subjectClass: SubjectClass::PolicyBacked, + subjectTypeKey: 'deviceConfiguration', + displayLabel: $displayName, + sourceReferences: [], + fingerprint: $identity->fingerprint(), + lastSeenAt: now()->toIso8601String(), + ); + } } diff --git a/apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php b/apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php new file mode 100644 index 00000000..99c474f6 --- /dev/null +++ b/apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php @@ -0,0 +1,370 @@ + (int) $run->getKey()]) + ->assertOk() + ->assertSee('Baseline subject resolution') + ->assertSee('Duplicate policy') + ->assertSee('Unresolved Duplicate Candidates') + ->assertSee('Binding required') + ->assertSee('TenantPilot-only'); +}); + +it('renders specific empty states for missing compare runs and quiet compare results', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + + spec384BaselineSubjectResolutionLivewire($tenant, $user) + ->assertOk() + ->assertSee('Run baseline compare first') + ->assertSee('No baseline compare run exists for this environment yet.'); + + [$quietUser, $quietTenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($quietTenant); + $quietRun = seedBaselineCompareRun($quietTenant, $profile, $snapshot, [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'verified_no_drift', + 'actionability' => 'none', + 'readiness_impact' => 'no_impact', + 'subject' => ['subject_type_key' => 'deviceConfiguration', 'subject_key' => 'quiet'], + ]), + ], + ], + ]); + + spec384BaselineSubjectResolutionLivewire($quietTenant, $quietUser, ['operation_run_id' => (int) $quietRun->getKey()]) + ->assertOk() + ->assertSee('No baseline subject decisions required') + ->assertSee('The selected compare context has no unresolved or decision-required baseline subjects.'); +}); + +it('records manual bindings through the confirmed Filament action', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedSubjectResolutionRun($tenant); + $row = app(BaselineSubjectResolutionQuery::class)->rows($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ])[0]; + $candidateKey = (string) $row['candidates'][0]['candidate_key']; + + $component = spec384BaselineSubjectResolutionLivewire($tenant, $user, ['operation_run_id' => (int) $run->getKey()]); + $record = collect($component->instance()->getTableRecords()->items())->first(); + + $component->callTableAction('bindSubject', $record, data: [ + 'candidate_key' => $candidateKey, + 'operator_note' => 'Operator selected the matching provider resource after reviewing duplicate candidates.', + ]); + + $binding = ProviderResourceBinding::query() + ->where('managed_environment_id', (int) $tenant->getKey()) + ->firstOrFail(); + + expect($binding->resolution_mode)->toBe(ProviderResourceResolutionMode::ManualBinding) + ->and($binding->provider_resource_id)->toBe('candidate-left') + ->and((int) $binding->source_operation_run_id)->toBe((int) $run->getKey()); + + expect(AuditLog::query() + ->where('action', AuditActionId::ProviderResourceBindingCreated->value) + ->where('managed_environment_id', (int) $tenant->getKey()) + ->exists())->toBeTrue(); +}); + +it('records and revokes subject decisions through confirmed Filament actions', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedDecisionIdentityRun($tenant); + + $component = spec384BaselineSubjectResolutionLivewire($tenant, $user, ['operation_run_id' => (int) $run->getKey()]); + $record = collect($component->instance()->getTableRecords()->items())->first(); + + $component->callTableAction('recordDecision', $record, data: [ + 'decision' => 'accepted_limitation', + 'operator_note' => 'Operator accepted this baseline limitation after confirming provider coverage scope.', + ]); + + $binding = ProviderResourceBinding::query() + ->where('managed_environment_id', (int) $tenant->getKey()) + ->firstOrFail(); + + expect($binding->resolution_mode)->toBe(ProviderResourceResolutionMode::AcceptedLimitation) + ->and($binding->binding_status)->toBe(ProviderResourceBindingStatus::Active) + ->and(AuditLog::query() + ->where('action', AuditActionId::ProviderResourceBindingCreated->value) + ->where('managed_environment_id', (int) $tenant->getKey()) + ->exists())->toBeTrue(); + + $updatedRecord = collect($component->instance()->getTableRecords()->items())->first(); + + $component + ->assertTableActionVisible('revokeDecision', $updatedRecord) + ->callTableAction('revokeDecision', $updatedRecord, data: [ + 'operator_note' => 'Operator revoked the limitation because fresh provider evidence is expected.', + ]); + + expect($binding->refresh()->binding_status)->toBe(ProviderResourceBindingStatus::Revoked) + ->and(AuditLog::query() + ->where('action', AuditActionId::ProviderResourceBindingRevoked->value) + ->where('managed_environment_id', (int) $tenant->getKey()) + ->exists())->toBeTrue(); +}); + +it('disables decision actions for workspace members missing manage capability', function (): void { + [$owner, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedSubjectResolutionRun($tenant); + [$readonly] = createUserWithTenant(tenant: $tenant, role: 'owner', workspaceRole: 'readonly'); + + $component = spec384BaselineSubjectResolutionLivewire($tenant, $readonly, ['operation_run_id' => (int) $run->getKey()]); + $record = collect($component->instance()->getTableRecords()->items())->first(); + + $component + ->assertTableActionVisible('bindSubject', $record) + ->assertTableActionDisabled('bindSubject', $record); +}); + +it('returns not found for users outside the workspace/environment scope', function (): void { + [$owner, $tenant] = createUserWithTenant(role: 'owner'); + spec384SeedSubjectResolutionRun($tenant); + [$outsider] = createUserWithTenant(role: 'owner'); + + $this->actingAs($outsider) + ->get(ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant)) + ->assertNotFound(); +}); + +it('returns not found when the route workspace does not own the environment', function (): void { + [$owner, $tenant] = createUserWithTenant(role: 'owner'); + spec384SeedSubjectResolutionRun($tenant); + [, $foreignTenant] = createUserWithTenant(role: 'owner'); + + $url = str_replace( + '/workspaces/'.$tenant->workspace->slug.'/', + '/workspaces/'.$foreignTenant->workspace->slug.'/', + ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant), + ); + + $this->actingAs($owner) + ->get($url) + ->assertNotFound(); +}); + +it('reauthorizes livewire reads after workspace membership changes', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedSubjectResolutionRun($tenant); + + $component = spec384BaselineSubjectResolutionLivewire($tenant, $user, ['operation_run_id' => (int) $run->getKey()]) + ->assertOk(); + + WorkspaceMembership::query() + ->where('workspace_id', (int) $tenant->workspace_id) + ->where('user_id', (int) $user->getKey()) + ->delete(); + + app(WorkspaceCapabilityResolver::class)->clearCache(); + app(ManagedEnvironmentAccessScopeResolver::class)->clearCache(); + + expect(fn (): mixed => $component->instance()->currentEnvironment()) + ->toThrow(NotFoundHttpException::class); +}); + +it('adds a contextual link from baseline compare only when action is required', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedSubjectResolutionRun($tenant); + + baselineCompareLandingLivewire($tenant, user: $user) + ->assertSee('Resolve baseline subjects') + ->assertSee('1 subject need identity or coverage decisions'); + + [$quietUser, $quietTenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($quietTenant); + seedBaselineCompareRun($quietTenant, $profile, $snapshot, [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'verified_no_drift', + 'actionability' => 'none', + 'readiness_impact' => 'no_impact', + 'subject' => ['subject_type_key' => 'deviceConfiguration', 'subject_key' => 'quiet'], + ]), + ], + ], + ]); + + baselineCompareLandingLivewire($quietTenant, user: $quietUser) + ->assertDontSee('Resolve baseline subjects'); +}); + +it('adds a baseline subject resolution link to baseline compare run related links', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $run = spec384SeedSubjectResolutionRun($tenant); + + $links = \App\Support\OperationRunLinks::related($run, $tenant); + + expect($links)->toHaveKey('Baseline Subject Resolution') + ->and($links['Baseline Subject Resolution'])->toContain('baseline-subject-resolution') + ->and($links['Baseline Subject Resolution'])->toContain('operation_run_id='.(int) $run->getKey()); + + [, $quietTenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($quietTenant); + $quietRun = seedBaselineCompareRun($quietTenant, $profile, $snapshot, [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'verified_no_drift', + 'actionability' => 'none', + 'readiness_impact' => 'no_impact', + 'subject' => ['subject_type_key' => 'deviceConfiguration', 'subject_key' => 'quiet'], + ]), + ], + ], + ]); + + expect(\App\Support\OperationRunLinks::related($quietRun, $quietTenant)) + ->not->toHaveKey('Baseline Subject Resolution'); +}); + +it('adds a baseline subject resolution entry to operation run detail related navigation', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + $this->actingAs($user); + + $run = spec384SeedSubjectResolutionRun($tenant); + + $entry = collect(app(RelatedNavigationResolver::class) + ->detailEntries(CrossResourceNavigationMatrix::SOURCE_OPERATION_RUN, $run)) + ->firstWhere('key', 'baseline_subject_resolution'); + + expect($entry)->not->toBeNull() + ->and($entry['targetUrl'])->toContain('baseline-subject-resolution') + ->and($entry['targetUrl'])->toContain('operation_run_id='.(int) $run->getKey()) + ->and($entry['actionLabel'])->toBe('Resolve baseline subjects'); +}); + +function spec384SeedSubjectResolutionRun($tenant) +{ + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + $leftIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-left'); + $rightIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-right'); + $leftDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($leftIdentity, 'Duplicate policy'); + $rightDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($rightIdentity, 'Duplicate policy'); + + BaselineSubjectResolutionFixtures::inventoryCandidate($tenant, $leftIdentity, 'Duplicate policy'); + BaselineSubjectResolutionFixtures::inventoryCandidate($tenant, $rightIdentity, 'Duplicate policy'); + + return seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'unresolved_duplicate_candidates', + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => \App\Support\Baselines\SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'legacy-display-key', + 'display_label' => 'Duplicate policy', + 'candidate_descriptors' => [ + $leftDescriptor->toArray(), + $rightDescriptor->toArray(), + ], + ], + ]), + ], + ], + ], + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); +} + +function spec384SeedDecisionIdentityRun($tenant) +{ + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + $identity = ResourceIdentity::providerResource('fake-provider', 'policy', 'limited-policy'); + $descriptor = ProviderResourceDescriptor::fromIdentity( + identity: $identity, + subjectDomain: 'baseline', + subjectClass: \App\Support\Baselines\SubjectClass::PolicyBacked, + subjectTypeKey: 'deviceConfiguration', + displayLabel: 'Accepted limitation policy', + sourceReferences: [], + fingerprint: $identity->fingerprint(), + lastSeenAt: now()->toIso8601String(), + ); + + return seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'foundation_limitation', + 'actionability' => 'decision_required', + 'readiness_impact' => 'internal_blocker', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => \App\Support\Baselines\SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'accepted-limitation-policy', + 'display_label' => 'Accepted limitation policy', + 'provider_resource_descriptor' => $descriptor->toArray(), + ], + ]), + ], + ], + ], + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); +} + +function spec384BaselineSubjectResolutionLivewire($tenant, $user, array $queryParams = []) +{ + $manager = Livewire::withHeaders([ + 'Referer' => ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant), + ])->actingAs($user); + + if ($queryParams !== []) { + $manager = $manager->withQueryParams($queryParams); + } + + return $manager->test(BaselineSubjectResolution::class, ['environment' => $tenant]); +} diff --git a/apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php b/apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php new file mode 100644 index 00000000..dd53904c --- /dev/null +++ b/apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php @@ -0,0 +1,168 @@ + [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'unresolved_duplicate_candidates', + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'legacy-display-key', + 'display_label' => 'Duplicate policy', + 'candidate_descriptors' => [ + $leftDescriptor->toArray(), + $rightDescriptor->toArray(), + ], + ], + ]), + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'verified_no_drift', + 'actionability' => 'none', + 'readiness_impact' => 'no_impact', + 'subject' => [ + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'resolved-subject', + 'display_label' => 'Resolved subject', + ], + ]), + ], + ], + ], + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); + + $rows = app(BaselineSubjectResolutionQuery::class)->rows($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ]); + + expect($rows)->toHaveCount(1) + ->and($rows[0]['reason'])->toBe('unresolved_duplicate_candidates') + ->and($rows[0]['actionability'])->toBe('binding_required') + ->and($rows[0]['candidate_count'])->toBe(2) + ->and($rows[0]['decision_identity'])->toBeNull() + ->and(collect($rows[0]['candidates'])->pluck('identity.provider_resource_id')->all()) + ->toContain('candidate-left', 'candidate-right'); + + expect(app(BaselineSubjectResolutionQuery::class)->rows($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'reason' => 'unresolved_duplicate_candidates', + 'resource_type' => 'deviceConfiguration', + 'candidates' => 'yes', + 'active_binding' => 'no', + ]))->toHaveCount(1); + + $resolvedRows = app(BaselineSubjectResolutionQuery::class)->rows($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + 'include_resolved' => true, + ]); + + expect($resolvedRows)->toHaveCount(2); +}); + +it('does not derive bindable candidates from display label matches alone', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + + BaselineSubjectResolutionFixtures::inventoryCandidate( + $tenant, + ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-left'), + 'Duplicate policy', + ); + BaselineSubjectResolutionFixtures::inventoryCandidate( + $tenant, + ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-right'), + 'Duplicate policy', + ); + + $run = seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: [ + 'result_semantics' => [ + 'version' => 1, + 'subject_outcomes' => [ + BaselineSubjectResolutionFixtures::semanticOutcome([ + 'reason' => 'unresolved_duplicate_candidates', + 'actionability' => 'binding_required', + 'readiness_impact' => 'customer_blocker', + 'subject' => [ + 'subject_domain' => 'baseline', + 'subject_class' => SubjectClass::PolicyBacked->value, + 'subject_type_key' => 'deviceConfiguration', + 'subject_key' => 'legacy-display-key', + 'display_label' => 'Duplicate policy', + ], + ]), + ], + ], + ], + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); + + $rows = app(BaselineSubjectResolutionQuery::class)->rows($tenant, [ + 'operation_run_id' => (int) $run->getKey(), + ]); + + expect($rows)->toHaveCount(1) + ->and($rows[0]['candidate_count'])->toBe(0) + ->and($rows[0]['candidates'])->toBe([]); +}); + +it('does not treat legacy evidence-gap payloads as authoritative subject decisions', function (): void { + [$user, $tenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + + $run = seedBaselineCompareRun($tenant, $profile, $snapshot, [ + 'evidence_gaps' => [ + 'count' => 1, + 'by_reason' => ['unresolved_duplicate_candidates' => 1], + 'subjects' => [[ + 'policy_type' => 'deviceConfiguration', + 'subject_key' => 'legacy-only', + 'reason_code' => 'unresolved_duplicate_candidates', + ]], + ], + ], outcome: OperationRunOutcome::PartiallySucceeded->value); + + $query = app(BaselineSubjectResolutionQuery::class); + + expect($query->rows($tenant, ['operation_run_id' => (int) $run->getKey()]))->toBe([]) + ->and($query->summary($tenant, (int) $run->getKey())['legacy_payload_only'])->toBeTrue(); +}); diff --git a/docs/ui-ux-enterprise-audit/design-coverage-matrix.md b/docs/ui-ux-enterprise-audit/design-coverage-matrix.md index 50425b7c..6c540660 100644 --- a/docs/ui-ux-enterprise-audit/design-coverage-matrix.md +++ b/docs/ui-ux-enterprise-audit/design-coverage-matrix.md @@ -6,12 +6,12 @@ ## Summary | Metric | Count | Notes | | --- | ---: | --- | -| UI route/page inventory rows | 99 | Includes dynamic route families and utility/auth endpoints. | -| Unique page reports | 20 | `page-reports/*.md`; some inventory rows intentionally share existing reports where routes resolve to the same surface. | -| Desktop screenshots | 16 | Route-inventory-linked desktop evidence, including strategic runtime captures, blocker evidence screenshots, and the Spec 366 rendered-report capture. | +| UI route/page inventory rows | 100 | Includes dynamic route families and utility/auth endpoints. | +| Unique page reports | 21 | `page-reports/*.md`; some inventory rows intentionally share existing reports where routes resolve to the same surface. | +| Desktop screenshots | 17 | Route-inventory-linked desktop evidence, including strategic runtime captures, blocker evidence screenshots, and the Spec 366 rendered-report capture. | | Tablet screenshots | 0 | Deferred to later strategic mockup/implementation specs. | -| Mobile screenshots | 1 | Spec 366 adds mobile-ish rendered-report evidence for the customer technical profile; broader mobile coverage remains deferred. | -| Strategic Surface rows | 45 | Individual target treatment or explicit product decision required. | +| Mobile screenshots | 2 | Spec 366 adds mobile-ish rendered-report evidence for the customer technical profile; Spec 384 adds a narrow baseline subject resolution smoke capture. | +| Strategic Surface rows | 46 | Individual target treatment or explicit product decision required. | | Domain Pattern Surface rows | 45 | Can be handled through grouped pattern specs unless later evidence raises risk. | | Design-System Cleanup Surface rows | 7 | Tables/forms/states/copy cleanup, no individual target mockup expected by default. | | Internal / Deprecated / Hidden rows | 1 | Local-only smoke login routes. | @@ -49,7 +49,7 @@ ## Coverage By Area | Area | Rows | Coverage Notes | | --- | ---: | --- | | Platform/system | 14 | Route-discovered; not browser-reviewed in Spec 323 because system auth/capability state needs separate fixture. | -| Governance | 12 | Strong browser coverage for inbox, decisions, exceptions, baselines; detail/diff routes remain unresolved. | +| Governance | 13 | Strong browser coverage for inbox, decisions, exceptions, baselines; detail/diff routes remain unresolved; Spec 384 adds a feature-tested baseline subject resolution worklist. | | Monitoring | 9 | Operations hub and alert delivery landing captured; record details and config forms remain pattern/manual review. | | Inventory | 8 | Route-discovered only; coverage, policy version detail, and raw-data exposure need later review. | | Evidence / audit | 8 | Audit log captured; evidence/report detail routes need customer-safe progressive-disclosure review. | @@ -76,7 +76,7 @@ ## Coverage By Primary Archetype | Evidence / Audit | 10 | Must keep proof, timestamps, source, and raw details clearly separated. | | Operations / Monitoring | 9 | Needs consistent run status, retry/rerun semantics, and diagnostic hierarchy. | | Inventory | 8 | Needs raw provider payload disclosure rules and confidence/status language. | -| Drift / Diff | 8 | Needs assignment, comparison, snapshot, and evidence-gap hierarchy. | +| Drift / Diff | 9 | Needs assignment, comparison, subject-resolution, snapshot, and evidence-gap hierarchy. | | Provider / Integration | 7 | Consent, credentials, permissions, and disconnect states require high trust clarity. | | Reviews | 7 | Customer/auditor language, export context, and proof links are central. | | Findings / Inbox | 6 | Needs triage, owner, SLA, exception, and close-state clarity. | @@ -93,7 +93,7 @@ ## Coverage By Design Depth | Design Depth | Rows | Gate Treatment | | --- | ---: | --- | -| Strategic Surface | 45 | Requires individual target artifact or explicit product decision before substantive UI implementation. | +| Strategic Surface | 46 | Requires individual target artifact or explicit product decision before substantive UI implementation. | | Domain Pattern Surface | 45 | Can be handled by grouped pattern specs and shared components. | | Design-System Cleanup Surface | 7 | Table/form/action/state cleanup can be folded into implementation waves. | | Manual Review Required | 1 | Must not be treated as product-ready until route/auth state is confirmed. | diff --git a/docs/ui-ux-enterprise-audit/page-reports/ui-100-baseline-subject-resolution.md b/docs/ui-ux-enterprise-audit/page-reports/ui-100-baseline-subject-resolution.md new file mode 100644 index 00000000..ce1d8697 --- /dev/null +++ b/docs/ui-ux-enterprise-audit/page-reports/ui-100-baseline-subject-resolution.md @@ -0,0 +1,48 @@ +# UI-100 Baseline Subject Resolution + +| Field | Value | +| --- | --- | +| Route | `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution` | +| Source | `BaselineSubjectResolution` | +| Area / scope | Governance / environment | +| Archetype | Drift / Diff | +| Design depth | Strategic Surface | +| Repo truth | browser-verified route; feature-tested | +| Screenshot | [desktop](../../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png), [bind modal](../../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-02-baseline-subject-resolution-bind-modal.png), [mobile](../../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-03-baseline-subject-resolution-mobile.png) | +| Browser status | Browser smoke passed for route reachability, scoped worklist content, bind-modal copy, and narrow viewport overflow. | + +## First Five Seconds + +The page should read as a focused decision queue for baseline subjects that need identity binding or coverage decisions. It must make the active environment, source compare run, problem category, readiness impact, and available candidates visible before any action. + +## Productization Review + +- Decision-first: operators see actionable subjects, readiness impact, actionability, candidates, and current decision state before raw evidence. +- Evidence-first: source operation and provider/resource metadata remain available but not dominant. +- Context: environment-bound route with workspace/environment scope enforced before rendering. +- Customer/auditor safety: high, because decisions affect future baseline compare interpretation. +- Diagnostics: raw provider identifiers and fingerprints stay secondary and truncated/collapsed by default. + +## Information Inventory + +Default content shows summary counts, active source run, actionable subject rows, provider/class/type filters, readiness/actionability/reason filters, candidate availability, current binding/decision state, and source/run links. + +## Dangerous Actions + +Manual binding, decision recording, revocation, and rerun compare are high-impact. They require confirmation, capability enforcement, server-side authorization, operator notes where persisted decisions are written, and audit logging through the existing binding service or OperationRun flow. + +## Scores + +| IA | Density | User Clarity | Sellability | Disclosure | Hierarchy | DS Fit | A11y | Responsive | Components | UX Writing | Perf | +| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| 7 | 7 | 7 | 6 | 7 | 7 | 7 | 6 | 6 | 7 | 7 | 7 | + +## Top Issues + +1. The compact decision modals should be rechecked visually if additional decision modes add more fields. +2. Follow-up iteration should add richer audit-history disclosure if operator volume grows. +3. The current V1 table uses inline context plus action modals rather than a separate detail route. + +## Target Direction + +Keep this as a scoped operator worklist rather than a broad governance landing page. Preserve the TenantPilot-only decision boundary, route-bound environment context, and compare/run entry links. diff --git a/docs/ui-ux-enterprise-audit/route-inventory.md b/docs/ui-ux-enterprise-audit/route-inventory.md index ec0aab63..e78af0eb 100644 --- a/docs/ui-ux-enterprise-audit/route-inventory.md +++ b/docs/ui-ux-enterprise-audit/route-inventory.md @@ -68,6 +68,7 @@ # Route Inventory | UI-059 | `/admin/baseline-snapshots` | resource | Baseline Snapshots | Evidence / audit | workspace analysis | route exists | workspace member | Evidence / Audit | Drift / Diff | Domain Pattern Surface | repo-verified | - | - | Workspace-owned evidence library. | | UI-060 | `/admin/baseline-snapshots/{record}` | resource | Baseline Snapshot Detail | Evidence / audit | workspace record | route exists | workspace + record entitlement | Evidence / Audit | Drift / Diff | Domain Pattern Surface | repo-verified | - | - | Snapshot detail may expose raw payloads; review later. | | UI-061 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare` | page | Baseline Compare | Governance | environment-bound | browser blocked/404 in fixture | workspace + environment entitlement and baseline state | Drift / Diff | Operations / Monitoring | Strategic Surface | repo-verified | [blocked](screenshots/desktop/ui-015-baseline-compare-blocked-404.png) | [report](page-reports/ui-015-baseline-compare.md) | Route exists in route list; smoke fixture could not render it. | +| UI-100 | `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution` | page | Baseline Subject Resolution | Governance | environment-bound | browser-verified | workspace + environment entitlement; view requires baseline view capability, mutations require baseline manage capability | Drift / Diff | Evidence / Audit | Strategic Surface | browser-verified | [desktop](../../specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png) | [report](page-reports/ui-100-baseline-subject-resolution.md) | Focused operator worklist for persisted baseline subject identity and coverage decisions; reachable from Baseline Compare and Operation detail only when actionable outcomes exist. | | UI-062 | `/admin/workspaces/{workspace}/environments/{environment}/inventory` | cluster | Inventory Cluster | Inventory | environment-bound | route exists | environment entitlement | Inventory | Workspace / Tenant Context | Domain Pattern Surface | repo-verified | - | - | Cluster landing/navigation surface. | | UI-063 | `/admin/workspaces/{workspace}/environments/{environment}/inventory/inventory-coverage` | page | Inventory Coverage | Inventory | environment-bound | route exists | environment entitlement | Inventory | Evidence / Audit | Strategic Surface | repo-verified | - | - | Coverage truth page; strategic because it gates evidence confidence. | | UI-064 | `/admin/workspaces/{workspace}/environments/{environment}/inventory-items` | resource | Inventory Items | Inventory | environment-bound | route exists | environment entitlement | Inventory | Evidence / Audit | Domain Pattern Surface | repo-verified | - | - | Core observed-state list. | diff --git a/specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png b/specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6166c055ad7334c0f6a3d0adc2972c53c58cbd GIT binary patch literal 122693 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 literal 0 HcmV?d00001 diff --git a/specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-02-baseline-subject-resolution-bind-modal.png b/specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-02-baseline-subject-resolution-bind-modal.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab6203d4fceff4f932394bbc93dde33870f805c GIT binary patch literal 128449 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

t!Sm-~@!eYUr15EmY*YZf!g~=J|4FPLQo;vv;8G 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 V2mA;={0?40ke61Ls)3pW{S%QdhR6T_ literal 0 HcmV?d00001 diff --git a/specs/384-baseline-subject-resolution-ui/checklists/requirements.md b/specs/384-baseline-subject-resolution-ui/checklists/requirements.md new file mode 100644 index 00000000..21b8d336 --- /dev/null +++ b/specs/384-baseline-subject-resolution-ui/checklists/requirements.md @@ -0,0 +1,77 @@ +# Specification Quality Checklist: Spec 384 - Baseline Subject Resolution UI and Operator Decisions v1 + +**Purpose**: Validate specification completeness, repo alignment, and implementation readiness before implementation. +**Created**: 2026-06-16 +**Feature**: `specs/384-baseline-subject-resolution-ui/spec.md` + +## Candidate Selection and Completion Guardrail + +- [x] CHK001 The selected candidate is directly user-provided as Spec 384. +- [x] CHK002 The active auto-candidate queue was not used as a forced source because it currently has no safe automatic next-best-prep target. +- [x] CHK003 Specs 381, 382, and 383 are treated as completed dependency context only. +- [x] CHK004 Historical Spec 163 is treated as adjacent/historical context and is not selected or modified. +- [x] CHK005 No existing Spec 384 package or branch was found before creation. + +## Content Quality + +- [x] CHK010 The spec states the operator problem, business value, users, scope, out-of-scope boundaries, assumptions, risks, and success criteria. +- [x] CHK011 Functional and non-functional requirements are testable and numbered. +- [x] CHK012 Acceptance criteria map to the selected V1 scope. +- [x] CHK013 No `[NEEDS CLARIFICATION]` markers remain. +- [x] CHK014 Open questions are explicitly non-blocking follow-up decisions. + +## Constitution and Scope Control + +- [x] CHK020 The spec includes a Spec Candidate Check and scored approval class. +- [x] CHK021 The spec includes a proportionality review for the new page/query layer and high-impact actions. +- [x] CHK022 The spec rejects a generic workflow/task/approval engine. +- [x] CHK023 The spec rejects a new primary decision table by default and reuses `provider_resource_bindings`. +- [x] CHK024 The spec rejects legacy subject-key UI, old payload readers, display-name identity, and compatibility shims. +- [x] CHK025 Evidence/Review readiness and customer-facing output are explicitly deferred to Spec 385. + +## UI and Productization Coverage + +- [x] CHK030 The spec includes exactly one coherent UI Surface Impact decision with concrete impact boxes checked. +- [x] CHK031 UI/Productization Coverage is completed for the new strategic operator surface. +- [x] CHK032 The plan and tasks require updates to `docs/ui-ux-enterprise-audit/route-inventory.md`, `design-coverage-matrix.md`, and a page report during implementation. +- [x] CHK033 The new page is classified as a Primary Decision Surface. +- [x] CHK034 Decision-first, audience-aware disclosure, UI/UX surface classification, and operator surface contract tables are present. +- [x] CHK035 Browser smoke is required because a new strategic high-impact surface is added. +- [x] CHK036 The spec, plan, and tasks reference `docs/product/standards/list-surface-review-checklist.md` for the new list/table surface. + +## RBAC, Audit, and Safety + +- [x] CHK040 The spec defines 404 for non-members and 403 for entitled members missing capability. +- [x] CHK041 The spec reuses existing baseline capabilities by default and requires a spec update before adding a new capability family. +- [x] CHK042 All high-impact decision actions require confirmation, server-side authorization, required notes where specified, and audit. +- [x] CHK043 The spec states that UI visibility/disabled state is not authorization. +- [x] CHK044 Audit metadata requirements are included. + +## OperationRun and Provider Boundary + +- [x] CHK050 Resolution decisions do not create a new OperationRun. +- [x] CHK051 Rerun/refresh compare must reuse existing baseline compare OperationRun UX. +- [x] CHK052 The spec forbids local OperationRun lifecycle/status/outcome handling. +- [x] CHK053 Provider/platform boundary is classified as mixed with provider identifiers retained only as proof/candidate data. +- [x] CHK054 Display names are labels only and cannot be identity. + +## Test and Task Readiness + +- [x] CHK060 `spec.md`, `plan.md`, and `tasks.md` exist. +- [x] CHK061 Tasks are grouped by setup/foundation/user story/polish phases. +- [x] CHK062 Tasks include unit, feature, Filament/Livewire, browser, RBAC, audit, and regression validation. +- [x] CHK063 Tasks include explicit UI coverage artifact work. +- [x] CHK064 Tasks include final validation commands, Pint, `git diff --check`, and close-out recording. +- [x] CHK065 Tasks include explicit no-Graph/provider-runtime negative coverage for the DB-only resolution surface. +- [x] CHK066 Tasks include a `missing_expected` preflight so the optional mode is implemented only if existing service/model support is sufficient. + +## Review Outcome + +- [x] CHK070 Review outcome class: `acceptable-special-case`. +- [x] CHK071 Workflow outcome: `keep`. +- [x] CHK072 Candidate Selection Gate result: PASS. +- [x] CHK073 Spec Readiness Gate preparation status: PASS after preparation analysis. + +## Notes + +- Preparation-only scope was maintained. No application code, migrations, models, services, jobs, Filament runtime files, routes, tests, or views were modified. diff --git a/specs/384-baseline-subject-resolution-ui/implementation-close-out.md b/specs/384-baseline-subject-resolution-ui/implementation-close-out.md new file mode 100644 index 00000000..7785a66c --- /dev/null +++ b/specs/384-baseline-subject-resolution-ui/implementation-close-out.md @@ -0,0 +1,102 @@ +# Implementation Close-Out: Spec 384 - Baseline Subject Resolution UI + +Date: 2026-06-16 +Branch: `384-baseline-subject-resolution-ui` + +## Summary + +Implemented a focused environment-scoped Baseline Subject Resolution page that derives actionable rows from Spec 383 compare semantics and active provider-resource decisions. Authorized operators can create manual bindings, record subject decisions, revoke active decisions, and rerun compare through existing baseline compare OperationRun UX. + +No new persisted entity, migration, capability family, provider call, workflow engine, customer-facing readiness mapping, or report/PDF scope was added. + +## Filament / Livewire Contract + +- Filament v5 compliance: implemented against Filament 5.2.1 and Livewire 4.1.4; no Livewire v3 or Filament v3/v4 APIs were introduced. +- Provider registration: the new page is registered in the existing admin panel provider at `apps/platform/app/Providers/Filament/AdminPanelProvider.php`; the provider remains registered through `apps/platform/bootstrap/providers.php`. +- Global search: no Filament Resource was added, so no new globally searchable resource exists. +- Destructive/high-impact actions: + - `bindSubject`: `Action::make(...)->action(...)`, `->requiresConfirmation()`, `workspace_baselines.manage` UI enforcement, server-side environment authorization, existing binding service audit. + - `recordDecision`: `Action::make(...)->action(...)`, `->requiresConfirmation()`, `workspace_baselines.manage` UI enforcement, server-side environment authorization, existing binding service audit. + - `revokeDecision`: `Action::make(...)->action(...)`, `->requiresConfirmation()`, destructive UI enforcement, `Gate::authorize('revoke')`, existing binding service audit. + - `runComparisonAgain`: confirmed action, requires `tenant.sync`, delegates to `BaselineCompareService` and existing OperationRun presenter/events. +- Asset strategy: no new Filament assets, panel assets, or heavy frontend assets were registered. Normal deploy can keep the existing `cd apps/platform && php artisan filament:assets` step. +- Testing plan/result: unit tests cover derived query/filter behavior; Filament/Livewire tests cover page render, empty states, manual binding, record decision, revoke, RBAC disabled state, outside-scope 404, Baseline Compare link behavior, and OperationRun related-navigation link behavior; browser smoke covers route/content/modal/mobile overflow. + +## Validation + +Passed: + +- `cd apps/platform && ./vendor/bin/sail artisan route:list --path=baseline-subject-resolution` +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php tests/Unit/Support/Baselines/Matching/SubjectMatchingPipelineTest.php tests/Unit/Support/Resources/ResourceIdentityTest.php tests/Unit/Support/Resources/ProviderResourceDescriptorTest.php` + - 16 passed, 112 assertions +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines tests/Unit/Support/Resources` + - 73 passed, 577 assertions +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderResources` + - 20 passed, 4 skipped, 75 assertions +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php tests/Feature/Filament/BaselineSubjectResolutionPageTest.php` + - 14 passed, 65 assertions +- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php --filter BaselineSubjectResolution` + - 1 passed, 21 assertions +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Evidence/BaselineDriftPostureSourceTest.php tests/Feature/ReviewPack/Spec347ReviewPackReadinessSemanticsTest.php tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php` + - 11 passed, 58 assertions +- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` +- `git diff --check` + +## Review Remediation + +Date: 2026-06-17 + +Manual review findings resolved: + +- Route workspace/environment mismatch now denies as not found before page access or action/query handling. +- Livewire table reads now reauthorize the locked environment ID on each read path instead of trusting mount-time state. +- Candidate discovery no longer treats matching display labels as bindable identity. Bindable candidates must come from stable compare `ProviderResourceDescriptor` payloads or inventory descriptors matched by canonical provider-resource identity. + +Additional validation: + +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php tests/Feature/Filament/BaselineSubjectResolutionPageTest.php` + - 14 passed, 65 assertions +- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php --filter BaselineSubjectResolution` + - 1 passed, 21 assertions +- `cd apps/platform && ./vendor/bin/sail bin pint --format agent app/Filament/Pages/BaselineSubjectResolution.php app/Services/Baselines/BaselineSubjectResolutionQuery.php tests/Feature/Baselines/Support/BaselineSubjectResolutionFixtures.php tests/Feature/Filament/BaselineSubjectResolutionPageTest.php tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php` +- Untracked Spec-384 whitespace check passed. + +Residual non-Spec-384 failures: + +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines tests/Feature/ProviderResources` + - 187 passed, 4 skipped, 4 failed. + - Failures are in existing baseline capture/compare tests outside the new page/query/action code: + - `BaselineCaptureAmbiguousMatchGapTest` expected partial success, got succeeded. + - `BaselineCaptureGapClassificationTest` expected capture gap reason counts. + - `BaselineCompareFindingsTest` expected 3 total counts, got 4. + - `BaselineCompareStrategySelectionTest` calls missing `BaselineSnapshotIdentity::subjectKey()`. + +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceContractTest.php` + - Spec 384 page declaration violation was fixed and no longer appears. + - Remaining failures are existing expectations outside this feature: + - OperationRun clickable-row URL expectation differs because the table record URL includes canonical navigation context query parameters. + - Required Permissions page assertion expects `Start verification`, which the rendered page did not contain. + +## Browser Smoke Evidence + +Browser smoke result: PASS + +Route: `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution?operation_run_id={run}` + +Context: seeded workspace manager/owner with one baseline compare run containing an actionable duplicate-candidate subject. + +Steps: smoke-login redirect to page, verify environment/worklist/action text, open `Bind subject` modal, verify TenantPilot-only/no-provider-mutation copy, resize to narrow viewport and verify no horizontal overflow. + +Screenshots: + +- `specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-01-baseline-subject-resolution.png` +- `specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-02-baseline-subject-resolution-bind-modal.png` +- `specs/384-baseline-subject-resolution-ui/artifacts/screenshots/spec384-03-baseline-subject-resolution-mobile.png` + +## Deployment Impact + +- Migrations: none. +- Environment variables: none. +- Queue/cron workers: no new workers or schedules. Existing queue workers should be restarted during normal deployment so baseline compare rerun paths use current code. +- Storage/volumes: no runtime storage changes. Browser screenshots are test/spec artifacts only. +- Dokploy/Staging: validate the new page on Staging with a seeded or real baseline compare run before production promotion because it exposes high-impact admin decisions. diff --git a/specs/384-baseline-subject-resolution-ui/plan.md b/specs/384-baseline-subject-resolution-ui/plan.md new file mode 100644 index 00000000..62510452 --- /dev/null +++ b/specs/384-baseline-subject-resolution-ui/plan.md @@ -0,0 +1,263 @@ +# Implementation Plan: Spec 384 - Baseline Subject Resolution UI and Operator Decisions v1 + +**Branch**: `384-baseline-subject-resolution-ui` | **Date**: 2026-06-16 | **Spec**: `specs/384-baseline-subject-resolution-ui/spec.md` +**Input**: Feature specification from `/specs/384-baseline-subject-resolution-ui/spec.md` + +## Summary + +Add a focused environment-scoped Baseline Subject Resolution surface that derives actionable rows from Spec 383 structured compare semantics, lets authorized operators create or revoke audited decisions through existing `ProviderResourceBindingService`, links from existing Baseline Compare and OperationRun contexts, and delegates rerun/refresh compare to existing baseline compare OperationRun UX. Do not add a generic workflow engine, new decision table, Evidence/Review readiness mapping, or provider mutation. + +## Technical Context + +**Language/Version**: PHP 8.4.15, Laravel 12.52.0 +**Primary Dependencies**: Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, PostgreSQL via Sail +**Storage**: Existing PostgreSQL tables; `provider_resource_bindings` is the decision truth. No new primary table approved. +**Testing**: Pest 4 feature/unit tests, Filament/Livewire action tests, targeted browser smoke for the new surface +**Validation Lanes**: fast-feedback, confidence, browser; PostgreSQL lane only if migrations/indexes/locks/constraints are added +**Target Platform**: Laravel monolith in `apps/platform`, Sail locally, Dokploy for staging/production +**Project Type**: Laravel/Filament web application inside `apps/platform` +**Performance Goals**: New page renders from DB-only data without provider/Graph calls; query remains scoped by workspace/environment and paginated. +**Constraints**: No Graph calls on render; no display-name identity; non-member 404; member missing capability 403; high-impact actions require confirmation, note rules, audit, and tests. +**Scale/Scope**: One environment-scoped resolution surface plus contextual links from existing Baseline Compare and OperationRun surfaces. + +## UI / Surface Guardrail Plan + +- **Guardrail scope**: changed surfaces plus new strategic operator surface. +- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: new Baseline Subject Resolution page; existing `BaselineCompareLanding`; existing `OperationRunResource` detail; optional environment dashboard/Baseline Profile summary links. +- **No-impact class, if applicable**: N/A. +- **Native vs custom classification summary**: native Filament page/table/forms/actions first; custom Blade only if native/detail layout is insufficient and documented. +- **Shared-family relevance**: status messaging, action links, OperationRun follow-up links, audit/history, high-impact action modals. +- **State layers in scope**: page, URL-query, detail/modal, and existing admin panel page-list registration; no new panel provider or shell navigation. +- **Audience modes in scope**: operator-MSP and support-platform. No customer/read-only output. +- **Decision/diagnostic/raw hierarchy plan**: decision-first default rows; diagnostics and raw provider proof collapsed/truncated/capability-gated. +- **Raw/support gating plan**: truncate provider IDs by default; full IDs/proof/audit details inside diagnostics or detail drawer. +- **One-primary-action / duplicate-truth control**: the resolution page owns decision summaries and actions; Baseline Compare/OperationRun show counts/link only. +- **Handling modes by drift class or surface**: new page is `review-mandatory`; existing page links are `report-only` unless layout/action hierarchy changes. +- **Repository-signal treatment**: UI coverage registry updates are required because a new reachable surface is added. +- **Special surface test profiles**: `shared-detail-family` for the page/detail hierarchy; `standard-native-filament` for action/table behavior. +- **Required tests or manual smoke**: Filament/Livewire action tests, RBAC/audit tests, browser smoke. +- **Exception path and spread control**: none planned. +- **Active feature PR close-out entry**: Baseline Subject Resolution UI / Operator Decision Coverage. +- **UI/Productization coverage decision**: coverage artifacts must be updated during implementation. +- **Coverage artifacts to update**: `docs/ui-ux-enterprise-audit/route-inventory.md`, `docs/ui-ux-enterprise-audit/design-coverage-matrix.md`, and a new or updated page report for Baseline Subject Resolution. +- **List-surface checklist**: implementation must apply `docs/product/standards/list-surface-review-checklist.md` because the new page includes a list/table surface. +- **No-impact rationale**: N/A. +- **Navigation / Filament provider-panel handling**: register the page in the existing admin panel provider only; no new panel provider and no broad top-level navigation in V1. +- **Screenshot or page-report need**: yes, proportional screenshot/browser smoke because this is a new strategic decision surface with high-impact actions. + +## Shared Pattern & System Fit + +- **Cross-cutting feature marker**: yes. +- **Systems touched**: baseline compare semantics/rendering, OperationRun detail, provider resource binding decisions, audit logging, RBAC UI enforcement, UI coverage registry. +- **Shared abstractions reused**: `ProviderResourceBindingService`, `ProviderResourceBindingPolicy`, `ProviderResourceResolutionMode`, `ProviderResourceBindingStatus`, `AuditRecorder`, `UiEnforcement`/`WorkspaceUiEnforcement`, existing OperationRun link/start UX helpers, existing badge/status helpers. +- **New abstraction introduced? why?**: a focused `BaselineSubjectResolutionQuery` or equivalent may be added to derive actionable rows from structured compare semantics and active bindings. It is not persisted truth and not a workflow engine. +- **Why the existing abstraction was sufficient or insufficient**: binding service is sufficient for mutations; existing evidence-gap tables are insufficient because they are read-only and do not combine candidates/current decisions/audit/action forms into one decision context. +- **Bounded deviation / spread control**: the query service may serve only baseline subject resolution rows and filters. Any broader decision inbox/workflow usage requires a follow-up spec. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: yes for existing run detail links and optional baseline compare rerun; no new run type. +- **Central contract reused**: existing baseline compare OperationRun start UX through `BaselineCompareService`, `OperationUxPresenter`, `OperationRunLinks`, and existing lifecycle notification behavior. +- **Delegated UX behaviors**: queued toast, run link, browser event, dedupe/already-running messaging, terminal notification, and tenant/workspace-safe URL resolution remain delegated to the existing compare start path. +- **Surface-owned behavior kept local**: decision input collection, page filters, current decision display, and navigation to existing compare start controls. +- **Queued DB-notification policy**: no new queued DB notification. +- **Terminal notification path**: existing OperationRun lifecycle mechanism. +- **Exception path**: none. + +## Provider Boundary & Portability Fit + +- **Shared provider/platform boundary touched?**: yes. +- **Provider-owned seams**: provider key, provider resource type, provider resource ID, external ID, identity kind, canonical discriminator, descriptors/fingerprints. +- **Platform-core seams**: governed subject, canonical subject key, actionability, readiness impact, binding/exclusion/limitation decision, audit metadata, operator vocabulary. +- **Neutral platform terms / contracts preserved**: provider resource, governed subject, binding, accepted limitation, exclusion, unsupported coverage, missing expected, active/revoked/superseded decision. +- **Retained provider-specific semantics and why**: provider identifiers are needed to select a real candidate. They stay proof data and are not primary operator vocabulary or identity generated from display labels. +- **Bounded extraction or follow-up path**: document-in-feature for any provider-specific display hotspot; follow-up-spec if a reusable provider candidate renderer becomes necessary across unrelated domains. + +## Constitution Check + +*GATE: Must pass before implementation. Re-check after design and before code changes.* + +- Inventory-first: compare outcomes are derived from last observed inventory/compare context; no provider calls during render. +- Read/write separation: decisions are TenantPilot-only DB mutations with confirmation, required notes, authorization, audit, and tests. No provider mutation. +- Graph contract path: no Graph calls are introduced. If future evidence refresh requires Graph, it must use existing compare/sync jobs through `GraphClientInterface`, not this page render. +- Deterministic capabilities: V1 reuses `workspace_baselines.view` and `workspace_baselines.manage`; no raw capability strings in feature code. +- RBAC-UX: `/admin` tenant/workspace plane only; non-member workspace/environment access is 404; member missing capability is 403 for mutations; authorization uses Gates/Policies plus UI enforcement. +- Workspace isolation: workspace route context is mandatory and enforced before records/candidates are revealed. +- Tenant/environment isolation: every query/action is scoped to managed environment and workspace. +- Destructive-like actions: exclusion, accepted limitation, unsupported, missing expected, revocation, and manual binding require `->requiresConfirmation()` or equivalent confirmed action flow. +- Global search: no globally searchable resource is added. If a Resource is later introduced, disable global search unless View/Edit and tenant-safe routing are proven. +- Run observability: resolution decisions are DB-only and audited; rerun compare uses existing OperationRun path. +- OperationRun start UX: local page must not compose queued toasts/links/events; reuse existing compare start UX. +- Ops-UX lifecycle: no direct OperationRun status/outcome transitions outside `OperationRunService`. +- Ops-UX summary counts: no new summary key is approved unless `OperationSummaryKeys::all()` and tests are updated. +- Test governance: unit, feature, Filament/Livewire, and browser lanes are explicitly planned. +- Proportionality: no new table/status family/capability family by default; one derived query/read service is justified by the focused decision workflow. +- Persisted truth: `provider_resource_bindings` remains the only durable decision truth. +- Behavioral state: reuse existing binding status and resolution modes. +- UI semantics: direct mapping from Spec 383 outcome truth and binding truth to UI; no new badge/status taxonomy unless an existing catalog cannot serve and spec is updated. +- Shared pattern first: use existing services/helpers/renderers before local implementations. +- Provider boundary: provider-specific identity stays descriptor/proof data; core UI and audit language remain provider-neutral. +- Filament-native UI: use native Filament components/shared primitives; no parallel local design system. +- UI/Productization coverage: implementation must update UI coverage registry and browser smoke evidence for the new surface. + +Gate result for preparation: PASS. + +## Test Governance Check + +- **Test purpose / classification by changed surface**: Unit for derived query/mapping; Feature for binding decisions, audit, RBAC, future compare consumption; Filament/Livewire for table/actions/modals; Browser for page reachability and visual smoke. +- **Affected validation lanes**: fast-feedback, confidence, browser; pgsql only if migrations/indexes/locks are introduced. +- **Why this lane mix is the narrowest sufficient proof**: The core risk is DB-backed decision correctness plus interactive Filament actions. Browser is limited to the new strategic surface. +- **Narrowest proving command(s)**: see spec planned validation commands and tasks final validation. +- **Fixture / helper / factory / seed / context cost risks**: provider candidates and workspace/environment context can be expensive; keep them feature-local and explicit. +- **Expensive defaults or shared helper growth introduced?**: no planned shared defaults. +- **Heavy-family additions, promotions, or visibility changes**: no heavy-governance family by default. +- **Surface-class relief / special coverage rule**: `standard-native-filament` plus browser smoke for new strategic page. +- **Closing validation and reviewer handoff**: verify confirmation/auth/audit/display-name rejection/future compare consumption, UI registry updates, and browser smoke. +- **Budget / baseline / trend follow-up**: document-in-feature if browser fixture setup is slow. +- **Review-stop questions**: lane fit, hidden fixture cost, high-impact action confirmation, no raw provider proof as primary UI, no workflow creep. +- **Escalation path**: document-in-feature for contained UI/browser notes; follow-up-spec for granular capabilities, expiration, approval, or reusable workbench framework. +- **Active feature PR close-out entry**: Baseline Subject Resolution UI / Operator Decision Coverage. +- **Why no dedicated follow-up spec is needed**: the needed V1 resolution surface is bounded; listed follow-ups are optional future product decisions. + +## Existing Repository Surfaces Likely Affected + +```text +apps/platform/app/Filament/Pages/BaselineCompareLanding.php +apps/platform/app/Filament/Resources/OperationRunResource.php +apps/platform/app/Livewire/BaselineCompareEvidenceGapTable.php +apps/platform/app/Services/Resources/ProviderResourceBindingService.php +apps/platform/app/Models/ProviderResourceBinding.php +apps/platform/app/Policies/ProviderResourceBindingPolicy.php +apps/platform/app/Services/Baselines/Matching/SubjectMatchingPipeline.php +apps/platform/app/Support/Baselines/CompareSemantics/* +apps/platform/app/Support/Resources/ProviderResourceResolutionMode.php +apps/platform/app/Support/Resources/ProviderResourceBindingStatus.php +apps/platform/app/Support/Auth/Capabilities.php +apps/platform/app/Support/Audit/AuditActionId.php +docs/ui-ux-enterprise-audit/route-inventory.md +docs/ui-ux-enterprise-audit/design-coverage-matrix.md +docs/ui-ux-enterprise-audit/page-reports/... +``` + +The implementation may add a new page class, query/read service, focused Livewire component, translations, and tests under existing app/test directories. Exact file names should be verified during implementation rather than invented beyond this plan. + +## Technical Approach + +1. Build a derived resolution query/read model over current baseline compare result semantics and active provider resource bindings. +2. Add a focused Filament page scoped by workspace/environment route and URL query filters. +3. Render a native Filament table with grouped actionable rows, clear empty states, and progressive detail. +4. Wire decision actions to `ProviderResourceBindingService` only. Do not duplicate transaction, supersession, or audit behavior in UI closures. +5. Add contextual links/counts from Baseline Compare and OperationRun detail. +6. Delegate rerun/refresh compare to existing baseline compare service and OperationRun UX. +7. Update UI coverage artifacts and run targeted validation. + +## Data / Migration Implications + +- No new primary persisted entity is approved. +- No migration is planned for V1. +- If implementation proves an additive index or field is required, stop and update spec/plan before adding it. +- Existing `provider_resource_bindings` active uniqueness and status/mode checks remain authoritative. +- Decision records are tenant-owned operational truth scoped by `workspace_id` and `managed_environment_id`. + +## UI / Filament Implications + +- Filament v5 / Livewire v4.0+ compliance is required; project currently has Livewire 4.1.4. +- Panel providers remain in `apps/platform/bootstrap/providers.php`; no new panel provider is planned. +- Do not enable global search for the resolution surface by default. If a Resource is introduced, disable global search unless View/Edit and tenant-safe URL rules are satisfied. +- The new list/table surface must follow `docs/product/standards/list-surface-review-checklist.md`; any deviation must be recorded in implementation close-out. +- High-impact actions execute through `Action::make(...)->action(...)` or equivalent Livewire/Filament action handlers and include confirmation. +- Use `UiEnforcement`/`WorkspaceUiEnforcement` and server-side Gates/Policies. UI disabled/hidden state is not authorization. +- No registered Filament assets are planned; `php artisan filament:assets` remains the normal deploy step only if assets are registered elsewhere. + +## Implementation Phases + +### Phase 1 - Prep and Guardrail Alignment + +Confirm dependencies, re-read completed close-outs, update UI coverage classification, and verify no Spec 385/report/workflow scope enters the implementation. + +### Phase 2 - Resolution Query and Row Model + +Create the derived query/read service and tests for actionable rows, filters, current decisions, candidate counts, and legacy-payload refusal. + +### Phase 3 - Decision Actions + +Wire actions to existing binding service for manual binding, canonical confirmation where applicable, exclusion, accepted limitation, unsupported coverage, missing expected where already supported, and revocation. Add authorization, confirmation, audit, and note-rule tests. + +### Phase 4 - Filament Surface + +Create the page/table/detail/modal UX with native Filament components, safe empty states, scope signals, progressive diagnostics, and no raw provider details as primary UI. + +### Phase 5 - Contextual Links and Compare Refresh + +Add filtered links/counts from Baseline Compare and OperationRun detail. Add rerun/refresh compare path only through existing baseline compare OperationRun UX. + +### Phase 6 - Validation and Coverage Close-Out + +Run targeted tests, browser smoke, UI registry updates, Pint, and `git diff --check`. Record close-out without changing completed dependency specs. + +## Risk Controls + +- Stop if implementation needs new table, new capability family, provider call, workflow/approval engine, or customer readiness mapping. +- Stop and update spec/plan if the existing binding service cannot support a required V1 decision without changing persistence semantics. +- Keep candidate data sanitized/truncated and diagnostics secondary. +- Keep link-only changes out of OperationRun lifecycle logic. +- Require tests for display-name rejection and cross-workspace/environment denial. + +## Rollout Considerations + +- Staging validation required because this adds high-impact governance UI. +- No env vars, scheduler changes, queue names, storage volumes, or reverse proxy changes are planned. +- Queue workers should be restarted as part of normal deploy so rerun compare paths use the latest code. +- No new `filament:assets` requirement unless implementation registers assets. +- Browser smoke is required before production promotion because this is a new reachable operator surface. + +## Project Structure + +```text +specs/384-baseline-subject-resolution-ui/ +├── spec.md +├── plan.md +├── tasks.md +└── checklists/ + └── requirements.md + +apps/platform/app/ +├── Filament/ +│ ├── Pages/ +│ └── Resources/ +├── Livewire/ +├── Services/ +│ ├── Baselines/ +│ └── Resources/ +├── Support/ +│ ├── Baselines/ +│ ├── Resources/ +│ └── Rbac/ +└── Policies/ + +apps/platform/tests/ +├── Unit/ +├── Feature/ +│ ├── Baselines/ +│ ├── Filament/ +│ └── ProviderResources/ +└── Browser/ +``` + +**Structure Decision**: Use existing Laravel/Filament app structure. Add only feature-local classes where needed and prefer existing services/policies/helpers. + +## Complexity Tracking + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|---|---|---| +| New derived resolution query/read service | Operators need one decision worklist that combines compare semantics, candidates, and active decisions | Local buttons on existing gap rows scatter decision context and duplicate semantics | +| New strategic Filament page | Human decisions need a focused context with candidates/current decision/audit/action forms | OperationRun detail is diagnostics-oriented and Baseline Compare is status/review-oriented | + +## Proportionality Review + +- **Current operator problem**: actionable compare blockers cannot become durable decisions from UI. +- **Existing structure is insufficient because**: current surfaces are read-only or diagnostic and current binding service has no operator worklist. +- **Narrowest correct implementation**: one derived query, one focused page, existing service/policy/audit for mutations. +- **Ownership cost created**: page/query/tests/UI coverage and future maintenance of decision copy/action modals. +- **Alternative intentionally rejected**: generic workflow/task engine and broad Governance Inbox. +- **Release truth**: current-release truth after Specs 381-383. diff --git a/specs/384-baseline-subject-resolution-ui/spec.md b/specs/384-baseline-subject-resolution-ui/spec.md new file mode 100644 index 00000000..09e7a902 --- /dev/null +++ b/specs/384-baseline-subject-resolution-ui/spec.md @@ -0,0 +1,476 @@ +# Feature Specification: Spec 384 - Baseline Subject Resolution UI and Operator Decisions v1 + +**Feature Branch**: `384-baseline-subject-resolution-ui` +**Created**: 2026-06-16 +**Status**: Implemented / Close-out recorded +**Input**: User-provided candidate "Spec 384 - Baseline Subject Resolution UI & Operator Decisions v1" from `/Users/ahmeddarrazi/.codex/attachments/e9f8b1ac-2968-4c6f-8326-af19f7f7b8ad/pasted-text.txt`. + +## Repo-Truth Adjustment + +The user supplied a complete numbered candidate for Spec 384. Repo truth confirms: + +- `specs/381-provider-resource-identity-binding/` is implemented and closed out. It created `provider_resource_bindings`, `ProviderResourceBinding`, `ProviderResourceBindingService`, `ProviderResourceResolutionMode`, audit IDs, policy coverage, and DB-backed active binding uniqueness. +- `specs/382-baseline-matching-canonicalization/` is implemented and closed out. It added provider-resource matching, canonicalization, binding-first matching, and removed legacy/display-name identity paths. +- `specs/383-baseline-result-semantics/` is implemented and closed out on the current branch. It added structured compare result semantics and explicitly did not implement Spec 384 operator resolution UI. +- Historical `specs/163-baseline-subject-resolution/` is related context only. It targeted semantic/evidence gap foundation on existing surfaces and explicitly did not require a new operator screen. It must not be rewritten by this prep. + +This prepared Spec 384 narrows the candidate to the smallest implementation-ready slice: + +- A focused environment-scoped Baseline Subject Resolution surface. +- A query/read model over existing Spec 383 structured compare outcomes to list actionable subject-resolution items. +- Operator decisions persisted through existing `provider_resource_bindings` and `ProviderResourceBindingService`. +- Actions for manual binding, exclusion, accepted limitation, unsupported coverage, missing expected where already supported, and revocation. +- Contextual links from existing Baseline Compare and OperationRun surfaces. +- Audit, RBAC, workspace/environment isolation, and no display-name identity. +- No Evidence/Review readiness final mapping, customer-facing report wording, generic workflow engine, broad Governance Inbox, or Management Report/PDF scope. + +V1 reuses current baseline capabilities (`workspace_baselines.view` and `workspace_baselines.manage`) unless implementation proves that a separate capability family is required. Introducing granular `baseline_subject_resolution.*` capabilities would require updating this spec and the plan with a proportionality review before implementation continues. + +## Candidate Selection Gate + +- **Selected candidate**: Spec 384 - Baseline Subject Resolution UI and Operator Decisions v1. +- **Source**: Direct user-provided candidate attachment; follow-up references in Specs 381, 382, and 383. +- **Why selected**: It is the next sequence item after completed Specs 381-383 and is the first visible operator decision layer for unresolved baseline subject identity and coverage outcomes. +- **Roadmap relationship**: Supports baseline governance productization, provider-neutral identity decisions, auditability, and future Evidence/Review readiness without reopening completed productization or report lanes. +- **Close alternatives deferred**: + - Spec 385 - Evidence and Review Readiness Integration v1: depends on durable decisions from Spec 384. + - Management Report/PDF runtime validation: unrelated manual follow-through lane. + - Broad Governance Inbox or approval workflow: would turn this into a generic workflow engine and is out of scope. + - Provider readiness onboarding productization: optional manual-promotion backlog, not part of baseline subject resolution. +- **Completed-spec guardrail result**: + - Specs 381, 382, and 383 are completed and used as dependency context only. + - Spec 163 is historical/adjacent and not selected or modified. + - No existing `specs/384-*` package or `384-*` local/remote branch was found before the Spec Kit create script ran. +- **Smallest viable implementation slice**: Add one focused resolution page and service/query layer that lets authorized operators make audited binding/scope/limitation decisions for Spec 383 actionable outcomes and then rerun/refresh compare through existing baseline compare mechanisms. +- **Gate result**: PASS. The candidate is user-provided, not already covered by an active/completed Spec 384 package, aligns with the 381-385 sequence, and is scoped as a bounded UI/decision slice. + +## Spec Candidate Check *(mandatory - SPEC-GATE-001)* + +- **Problem**: Structured compare outcomes can identify unresolved identity, duplicate candidates, missing provider resources, unsupported coverage, accepted limitations, excluded subjects, and foundation limitations, but operators do not yet have a focused place to turn those outcomes into durable decisions. +- **Today's failure**: The same actionable blockers can reappear across future compares because operators cannot bind the correct provider resource, exclude a non-governed subject, accept a limitation, mark unsupported coverage, or revoke stale decisions from a safe UI. +- **User-visible improvement**: Operators can clear real baseline subject decision blockers from a dedicated resolution surface, with clear impact language and links from Baseline Compare and OperationRun context. +- **Smallest enterprise-capable version**: One environment-scoped resolution page, DB-only decision actions through existing binding service/policy/audit paths, contextual links, and targeted tests. +- **Explicit non-goals**: No generic workflow engine, no approval workflow, no Governance Inbox, no customer-facing readiness wording, no Evidence/Review final mapping, no Management Report/PDF work, no legacy subject-key UI, and no display-name identity. +- **Permanent complexity imported**: One focused Filament page or equivalent Livewire/Filament surface, a query/read service for actionable subject outcomes, action form/modal wiring, tests, and UI coverage artifacts. No new primary decision table is approved. +- **Why now**: Spec 383 now produces structured actionability/readiness semantics, and Spec 385 needs durable operator decisions before evidence/review readiness can be truthful. +- **Why not local**: Local buttons on existing gap rows would scatter decision behavior across OperationRun, Baseline Compare, and dashboard contexts. A focused page keeps one decision context, one audit path, one RBAC contract, and one route for filtered links. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: New UI surface, high-impact decision actions, possible new query/service layer. Defense: the scope reuses existing persisted truth and policy/audit service paths, avoids new workflow tables, and contains decisions to baseline subject resolution only. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12** +- **Decision**: approve as a narrowed Core Enterprise UI/decision slice. + +## Problem Statement + +Baseline compare can now classify why a governed subject is not fully comparable or ready: unresolved identity, duplicate candidates, missing local evidence, missing provider resource, unsupported resource class, foundation limitation, accepted limitation, excluded non-governed subject, low-trust identity, drift, no drift, or compare failure. + +Some of those states require a human decision. Examples: + +- A baseline subject has multiple tenant-owned provider candidates and must be bound to the correct resource. +- A foundation object is inventory-only and requires an accepted limitation. +- A subject is intentionally outside governed scope and should be excluded. +- A previously expected provider resource is intentionally absent. +- An accepted limitation or binding is no longer valid and must be revoked. + +Without a focused resolution surface, these decisions cannot be made safely, audited consistently, or consumed predictably by later compares. + +## Business / Product Value + +- Converts structured compare blockers into operator actions. +- Reduces repeated false blockers across future baseline compares. +- Preserves auditability for customer-impacting decisions. +- Prevents false green states by making exclusions and accepted limitations explicit and not no-drift. +- Makes Spec 385 Evidence/Review readiness possible without embedding customer-output rules in Spec 384. +- Keeps baseline governance productized without creating a generic workflow engine. + +## Primary Users / Operators + +- Workspace manager or baseline governance operator resolving compare blockers. +- MSP engineer selecting the correct provider resource when multiple candidates exist. +- Support/platform operator diagnosing why compare output remains blocked or limited. +- Release reviewer verifying provider-neutral identity, RBAC, audit, and UI safety. + +## Spec Scope Fields *(mandatory)* + +- **Scope**: tenant-owned/environment-owned baseline subject decision surface inside the admin panel. +- **Primary Routes**: Add an environment-scoped admin page, preferred route `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution`. Existing links may originate from `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare`, OperationRun detail, environment dashboard, and Baseline Profile detail. +- **Data Ownership**: Existing `provider_resource_bindings` remains tenant-owned operational decision truth. Existing OperationRun context/result payloads remain execution/proof truth. Existing baseline snapshots and compare outputs remain their current truth sources. No new primary decision table is approved. +- **RBAC**: View requires existing baseline compare visibility (`workspace_baselines.view`). Mutating decisions require existing baseline manage capability (`workspace_baselines.manage`) through policies/gates. Non-members are deny-as-not-found. Entitled members missing capability receive forbidden. + +For canonical-view specs: + +- **Default filter behavior when tenant-context is active**: Not applicable. The new page is environment-scoped by route. OperationRun links must include environment/workspace-safe filters. +- **Explicit entitlement checks preventing cross-tenant leakage**: Every page load, query, action, and linked candidate must enforce workspace and managed-environment entitlement before revealing subject, candidate, binding, source run, or audit context. + +## 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 +- [x] New page/route added +- [ ] Navigation changed +- [x] Filament panel/provider surface changed +- [x] New modal/drawer/wizard/action added +- [x] New table/form/state added +- [ ] Customer-facing surface changed +- [x] Dangerous action changed +- [x] Status/evidence/review presentation changed +- [x] Workspace/environment context presentation changed + +## UI/Productization Coverage + +- **Route/page/surface**: New Baseline Subject Resolution page at the environment route above; contextual links from Baseline Compare, OperationRun detail, environment dashboard, and Baseline Profile detail where relevant. +- **Current or new page archetype**: Decision Workbench / Resolution Queue for an environment-owned governance workflow. +- **Design depth**: Strategic Surface because it is a new human decision surface with high-impact audited actions. +- **Repo-truth level**: repo-verified foundations; new page is spec-backed until implemented. +- **Existing pattern reused**: Existing Baseline Compare page report `docs/ui-ux-enterprise-audit/page-reports/ui-015-baseline-compare.md`, OperationRun detail progressive disclosure, `UiEnforcement`, `WorkspaceUiEnforcement`, `BadgeCatalog`, native Filament tables/forms/actions/modals, and `ProviderResourceBindingService`. +- **New pattern required**: Domain pattern for baseline subject resolution, documented through the UI coverage registry during implementation. No target mockup is required before implementation, but a screenshot/browser smoke is required before close-out because this is a new strategic surface. +- **List-surface standards**: Because this spec adds a new list/table surface, implementation must apply `docs/product/standards/list-surface-review-checklist.md` and record any deviation in the feature close-out. +- **Screenshot required**: yes during implementation close-out for desktop and a narrow/mobile viewport if the page is reachable in browser fixtures. +- **Page audit required**: yes, add or update a page report for the new route and update the design coverage matrix. +- **Customer-safe review required**: no. Spec 384 is admin/operator-only. Spec 385 decides customer-facing readiness and limitations. +- **Dangerous-action review required**: yes. Bind, exclude, accept limitation, mark unsupported, missing expected, and revoke are high-impact governance decisions that require confirmation, server-side authorization, required notes where specified, audit, and tests. +- **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` + - [x] `docs/ui-ux-enterprise-audit/page-reports/...` + - [ ] `docs/ui-ux-enterprise-audit/strategic-surfaces.md` + - [ ] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` + - [ ] `docs/ui-ux-enterprise-audit/unresolved-pages.md` + - [ ] `N/A - no reachable UI surface impact` +- **No-impact rationale when applicable**: Not applicable. + +## Cross-Cutting / Shared Pattern Reuse + +- **Cross-cutting feature?**: yes. +- **Interaction class(es)**: status messaging, action links, header actions, deep links, audit entries, OperationRun follow-up links, baseline compare evidence/status presentation, and high-impact Filament action modals. +- **Systems touched**: Baseline Compare page, OperationRun detail, baseline compare evidence gap table/presentation, environment dashboard summaries where present, provider resource binding service/policy/audit, baseline compare rerun links, UI coverage registry, tests. +- **Existing pattern(s) to extend**: Native Filament pages/tables/forms/actions, `UiEnforcement` and `WorkspaceUiEnforcement`, `ProviderResourceBindingService`, `ProviderResourceBindingPolicy`, `AuditRecorder`, `OperationRunLinks`, `OperationUxPresenter` for rerun/start UX if rerun is offered. +- **Shared contract / presenter / builder / renderer to reuse**: `ProviderResourceBindingService` for all decisions; existing OperationRun link helpers; existing badge/status helpers; existing reason/presentation helpers where status labels are rendered. +- **Why the existing shared path is sufficient or insufficient**: Binding mutation paths already exist and are sufficient for decision truth. A new query/read surface is needed because current evidence-gap tables are read-only and do not provide a single decision context for candidates, current decisions, and audit history. +- **Allowed deviation and why**: A focused resolution query/read service may be added because it derives one operator worklist from structured compare outcomes and existing bindings. It must not become a generic workflow, task, or approval engine. +- **Consistency impact**: Baseline Compare, OperationRun follow-ups, and the new page must use the same actionability/readiness/result semantics and must not invent separate labels for the same blocker. +- **Review focus**: no display-name identity, no local authorization-only UI visibility, no duplicate decision table, no customer readiness wording, no ad-hoc OperationRun start UX, no raw provider identifiers as primary UI. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: yes for links and optional rerun/refresh only; no new OperationRun type is created by resolution decisions. +- **Shared OperationRun UX contract/layer reused**: Existing Baseline Compare start/rerun UX, `OperationRunLinks`, and `OperationUxPresenter` must be reused if the page offers rerun/refresh compare. +- **Delegated start/completion UX behaviors**: Any queued toast, run link, browser event, dedupe/already-running messaging, terminal notification, and tenant/workspace-safe operation URL resolution must remain delegated to existing baseline compare OperationRun UX paths. +- **Local surface-owned behavior that remains**: The resolution page may collect decision inputs and may offer a navigation/control to rerun compare, but it must not locally compose queued operation notifications or run lifecycle semantics. +- **Queued DB-notification policy**: no new queued DB notifications. Existing baseline compare terminal notification behavior remains unchanged. +- **Terminal notification path**: existing OperationRun lifecycle mechanism only. +- **Exception required?**: none. + +Resolution decisions themselves are DB-only audited mutations and do not create an `OperationRun`. Baseline compare rerun remains the existing queued/observable operation. + +## Provider Boundary / Platform Core Check + +- **Shared provider/platform boundary touched?**: yes. +- **Boundary classification**: mixed. The resolution workflow and decision semantics are platform-core; provider resource descriptors and concrete IDs remain provider-owned proof. +- **Seams affected**: governed subject identity, provider resource binding, canonical subject key, candidate presentation, compare actionability, audit metadata, operator vocabulary. +- **Neutral platform terms preserved or introduced**: governed subject, provider resource, binding, exclusion, accepted limitation, unsupported coverage, missing expected, actionability, readiness impact, decision. +- **Provider-specific semantics retained and why**: provider key, provider resource type, provider resource ID, external ID, and descriptors are retained as proof/candidate data because the operator must bind to real provider resources. They must not become display-name identity or platform-wide taxonomy. +- **Why this does not deepen provider coupling accidentally**: The UI uses existing provider-neutral descriptors and stable resource identity metadata. Microsoft/Intune labels may appear only as provider-provided labels, not as canonical identity or core branching logic. +- **Follow-up path**: Spec 385 consumes the decisions for evidence/review readiness. Granular capability family, approval workflow, decision expiration, or customer output mapping require separate spec updates/follow-ups. + +## 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 | +|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | yes | Native Filament page/table/actions with shared primitives | decision workbench, status messaging, action links, audit | page, URL-query, detail/modal | no | New strategic surface; requires coverage registry and browser smoke | +| Baseline Compare contextual link/counts | yes | Existing native/shared surface | status messaging, header/context action | page, URL-query | no | Existing route changed only to link/count unresolved decisions | +| OperationRun follow-up link | yes | Existing OperationRun detail surface | OperationRun proof and follow-up link | detail, URL-query | no | Link only; no local run UX | +| Environment dashboard/Baseline Profile shortcuts | yes, if implemented | Existing native/shared surfaces | dashboard signal/action link | page, URL-query | no | Summary link only; no redesign | + +## 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 | +|---|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | Primary Decision Surface | Resolve unresolved identity or scope/coverage decision blockers | subject label, reason, readiness impact, actionability, candidate count, current decision, safe primary action | raw provider IDs, source inventory/policy/run proof, audit history | Primary because this is where the operator makes the binding/exclusion/limitation decision | Follows "resolve compare blockers" workflow, not storage object browsing | Removes search across run detail, gap table, binding records, and audit logs | +| Baseline Compare page | Secondary Context Surface | Decide whether compare output needs subject resolution | action-required counts and one link to resolution | full gap table and run proof | Secondary because it identifies the need but does not host all decision inputs | Follows compare review workflow | Directs operator to one focused resolution surface | +| OperationRun detail | Tertiary Evidence / Diagnostics Surface with follow-up | Understand why a run is blocked and open filtered resolution | concise follow-up text and filtered link | raw run context, gap proof, diagnostics | Tertiary because it proves execution and points to decision surface | Follows operation troubleshooting workflow | Avoids requiring operators to resolve from raw run context | + +## 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 | +|---|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | operator-MSP, support-platform | blocker reason, subject class/type, environment, readiness impact, candidate count, current decision, required action | candidate details, source run, source inventory/policy references, compare context | full provider IDs, external IDs, fingerprints, raw proof payloads, audit history | Resolve subject or revoke decision | raw identifiers and proof stay truncated/collapsed/capability-gated where applicable | page owns the decision summary; linked surfaces show counts and route into it | + +## 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 | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | Queue / Table / Workbench | Decision workbench | Resolve subject | row focus opens detail drawer/modal or focused detail area | allowed if it opens the same detail as primary inspect | secondary navigation in row More or detail area | high-impact state changes in confirmed actions, grouped by risk | `/admin/workspaces/{workspace}/environments/{environment}/baseline-subject-resolution` | same route with query/focused subject or modal state | workspace and environment are visible from route/page context | Baseline subject decision | actionability, reason, readiness impact, current decision, candidate count | none | + +## 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 | +|---|---|---|---|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | Baseline governance operator | Bind, exclude, accept limitation, mark unsupported/missing expected, revoke | Primary Decision Surface | What subject decision is blocking compare, and what action is safe now? | subject, problem, readiness impact, actionability, candidates, current decision, environment, next action | raw provider IDs, fingerprints, source proof, full audit trail | identity, comparison, coverage, actionability, readiness, decision status | TenantPilot only for decision storage; future compare uses it; no direct provider mutation | Bind subject, Exclude subject, Accept limitation, Mark unsupported, Revoke decision, Run comparison again | exclude, accept limitation, mark unsupported, missing expected, revoke, and manual binding are high-impact and require confirmation, server authorization, note rules, and audit | + +## Proportionality Review *(mandatory when structural complexity is introduced)* + +- **New source of truth?**: no new source of truth. Existing active `provider_resource_bindings` remains the durable decision truth. The resolution query is derived from existing compare output and active bindings. +- **New persisted entity/table/artifact?**: no new primary table or artifact is approved. Additive fields/indexes are out of scope unless implementation updates this spec and proves current-release need. +- **New abstraction?**: yes, a focused query/read service may be introduced to derive actionable resolution rows from existing OperationRun/compare payloads and bindings. +- **New enum/state/reason family?**: no new V1 reason family is approved. Use existing `ProviderResourceResolutionMode`, `ProviderResourceBindingStatus`, and Spec 383 compare semantics. +- **New cross-domain UI framework/taxonomy?**: no. Use native Filament/shared primitives and direct mapping from canonical domain truth. +- **Current operator problem**: Operators cannot act on actionable subject-resolution blockers without manually reconstructing candidates and decisions from run payloads and binding records. +- **Existing structure is insufficient because**: Existing gap tables are read-only, OperationRun detail is diagnostics-oriented, and binding service methods have no focused operator surface or filtered worklist. +- **Narrowest correct implementation**: One derived query/read service, one environment-scoped page, and action modals wired to existing binding service/policy/audit. +- **Ownership cost**: A new page/query/test surface and UI coverage registry updates. Reviewers must prevent it from growing into a broad workflow/approval engine. +- **Alternative intentionally rejected**: Add local buttons to evidence-gap rows only. That would scatter decision state, duplicate action semantics, and make OperationRun/Baseline Compare surfaces own decisions they should only link to. +- **Release truth**: Current-release truth. Specs 381-383 have prepared the backend and semantics; operator resolution is now the missing product layer. + +### Compatibility posture + +TenantPilot is pre-production. V1 must not add legacy subject-key UI, old OperationRun payload readers, display-name matching, display-name-derived canonical keys, historical payload compatibility screens, or dual old/new decision paths. + +## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)* + +- **Test purpose / classification**: Unit for query/read service and binding service guard cases; Feature for DB/action/RBAC/audit/future-compare behavior; Filament/Livewire action tests for page/table/modals; Browser smoke for the new reachable decision surface. +- **Validation lane(s)**: fast-feedback, confidence, browser. PostgreSQL lane only if implementation adds indexes, constraints, locks, JSONB query behavior, or migrations. +- **Why this classification and these lanes are sufficient**: The feature is a DB-only decision workflow plus Filament/Livewire surface. No provider calls or direct Microsoft mutations are introduced. +- **New or expanded test families**: focused `BaselineSubjectResolution*` tests, ProviderResourceBinding decision/action tests, OperationRun follow-up tests, baseline compare rerun/consumption regression, and one browser smoke for the new page. +- **Fixture / helper cost impact**: reuse existing baseline compare matrix/gap fixtures and provider-resource binding factories. Any new provider candidates or workspace/environment setup must stay feature-local and opt-in. +- **Heavy-family visibility / justification**: no heavy-governance family by default. Browser smoke is explicit because this adds a strategic surface with high-impact actions. +- **Special surface test profile**: `shared-detail-family` plus `standard-native-filament` for native Filament table/action behavior. +- **Standard-native relief or required special coverage**: ordinary Filament action tests for native actions; browser smoke for route/page/action-modal reachability and no JS/visual overlap. +- **Reviewer handoff**: verify lane fit, no display-name identity, no local OperationRun UX, no new table without spec update, no customer readiness mapping, and every high-impact action has confirmation, server authorization, audit, and tests. +- **Budget / baseline / trend impact**: expected moderate feature/Filament test increase. Escalate as `document-in-feature` if browser fixtures are expensive; `follow-up-spec` only if a reusable resolution-workbench pattern is needed later. +- **Escalation needed**: document-in-feature for UI coverage/browser-smoke notes; follow-up-spec for approval workflow, decision expiration, granular capabilities, or customer readiness. +- **Active feature PR close-out entry**: Baseline Subject Resolution UI / Operator Decision Coverage. +- **Planned validation commands**: + - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines tests/Unit/Support/Resources` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines tests/Feature/ProviderResources` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/BaselineSubjectResolutionPageTest.php tests/Feature/Filament/OperationRunSubjectResolutionFollowUpTest.php` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Evidence/BaselineDriftPostureSourceTest.php tests/Feature/ReviewPack/Spec347ReviewPackReadinessSemanticsTest.php tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php` + - `cd apps/platform && ./vendor/bin/sail test:browser --filter BaselineSubjectResolution` + - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` + - `git diff --check` + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Find actionable subject decisions (Priority: P1) + +As a baseline governance operator, I need one filtered page that lists unresolved or decision-required baseline subjects from the current compare context, so I can see what blocks trustworthy compare output without reading raw run payloads. + +**Why this priority**: This is the MVP because decisions cannot be made safely until the operator can see actionable subjects and candidates in one context. + +**Independent Test**: Given a baseline compare run with unresolved duplicate candidates, missing provider resources, unsupported coverage, accepted limitations, and excluded subjects, the page lists only actionable items by default, supports filters, and shows empty states when no decisions are required. + +**Acceptance Scenarios**: + +1. **Given** an environment has a latest baseline compare with duplicate provider candidates, **When** an entitled operator opens Baseline Subject Resolution, **Then** the subject appears with problem, candidate count, readiness impact, actionability, and a resolve action. +2. **Given** a compare has only trusted no-drift and resolved subjects, **When** the page loads, **Then** the empty state says no baseline subject decisions are required. +3. **Given** no compare context exists for the environment, **When** the page loads, **Then** the empty state directs the operator to run baseline compare first. + +--- + +### User Story 2 - Make audited resolution decisions (Priority: P1) + +As an authorized operator, I need to bind a subject to a provider resource, exclude it, accept a limitation, mark unsupported or missing expected, and revoke stale decisions, so future compares can consume real operator decisions. + +**Why this priority**: This is the primary product value. The page must do more than report blockers; it must create durable audited decisions. + +**Independent Test**: Each decision action is executed through the page against a scoped subject and candidate; the resulting active binding/decision exists, previous active decision is superseded or revoked, audit is written, and unauthorized actors are denied. + +**Acceptance Scenarios**: + +1. **Given** a subject has valid provider candidates, **When** an authorized operator binds one candidate with a note, **Then** an active `manual_binding` decision is persisted and audited. +2. **Given** a subject is intentionally not governed, **When** an authorized operator excludes it with a reason and note, **Then** an active `excluded_non_governed` decision is persisted and the UI states that exclusion is not no-drift. +3. **Given** an active decision exists, **When** an authorized operator revokes it with a note, **Then** the decision becomes inactive for future compares and the revocation is audited. +4. **Given** a subject has only display-label data and no valid provider identity, **When** an operator attempts to bind it, **Then** the action is blocked and no display-name identity is stored. + +--- + +### User Story 3 - Navigate from compare and operation context (Priority: P2) + +As an operator reviewing compare output or an OperationRun, I need contextual links into the resolution page with filters already applied, so I can resolve blockers without reconstructing context manually. + +**Why this priority**: Links make the decision surface discoverable while avoiding a broad top-level workflow hub. + +**Independent Test**: OperationRun detail and Baseline Compare surface show resolution counts/links only when actionable subject-resolution outcomes exist; links open the resolution page scoped to the same workspace/environment and filtered by operation run/reason/actionability. + +**Acceptance Scenarios**: + +1. **Given** a baseline compare OperationRun has actionable subject-resolution outcomes, **When** an entitled operator opens the run detail, **Then** the follow-up link opens the resolution page filtered to that run. +2. **Given** Baseline Compare has action-required subject outcomes, **When** the operator opens the compare page, **Then** the page shows a concise count and a "Resolve baseline subjects" link. +3. **Given** the operator lacks manage capability, **When** they follow the link, **Then** they can view entitled resolution context but decision actions are disabled or forbidden according to the 404/403 rules. + +--- + +### User Story 4 - Re-run or refresh compare after decisions (Priority: P3) + +As an operator who has resolved one or more subject decisions, I need a clear path to run or refresh baseline compare through existing operation UX, so I can confirm whether blockers are cleared. + +**Why this priority**: Decisions affect future compares, but starting a compare must remain in the existing queued/observable baseline compare workflow. + +**Independent Test**: After decisions are saved, the page offers a rerun/refresh path that delegates to existing baseline compare start UX and does not create local OperationRun messaging. + +**Acceptance Scenarios**: + +1. **Given** a decision was saved, **When** the operator chooses to run comparison again, **Then** the existing baseline compare service and OperationRun UX are used. +2. **Given** the next compare consumes an active decision, **When** results are rendered, **Then** the subject is no longer listed as unresolved for the same reason. + +### Edge Cases + +- A subject has multiple candidates with similar display labels but different provider identities. +- A subject has no valid `ResourceIdentity`; display label exists only for human readability. +- The previous active decision was superseded, revoked, or belongs to another workspace/environment. +- A user is a workspace/environment member but lacks manage capability. +- A user is not entitled to the workspace or environment. +- A linked OperationRun is from another environment or workspace. +- Compare payload lacks structured Spec 383 semantics; the page must not parse legacy payloads and should show a safe empty/error state. +- Provider resource IDs or external IDs are long or sensitive; primary UI truncates/masks and pushes full details into diagnostics. +- Accepted limitation, excluded, unsupported, and missing expected decisions must never render as verified no-drift. + +## Requirements *(mandatory)* + +**Constitution alignment (required):** This feature adds DB-only high-impact governance decisions and a new operator surface. It does not add Graph calls, direct provider mutations, new OperationRun type, or new persisted decision table. Every decision must use server-side authorization, confirmation, required note rules where specified, audit logging, workspace/environment isolation, and tests. + +**Constitution alignment (RBAC-UX):** Non-members receive deny-as-not-found before any subject, candidate, binding, or run detail is exposed. Entitled members missing capability receive forbidden for execution; UI visibility/disabled state is never the security boundary. + +**Constitution alignment (OPS-UX):** Resolution decisions do not create an OperationRun. Any rerun/refresh compare path must reuse existing baseline compare OperationRun start UX. OperationRun status/outcome transitions remain service-owned. + +**Constitution alignment (UI-COV-001):** The feature adds a new strategic operator surface and high-impact actions, so implementation must update UI coverage artifacts and perform browser smoke. + +**Constitution alignment (PROV-001):** Provider identifiers are proof/candidate data, not platform-core semantics. Display names remain labels only. + +**Constitution alignment (TEST-GOV-001):** Unit, feature, Filament/Livewire, and browser lanes are explicitly planned. Fixture setup must remain feature-local and opt-in. + +### Functional Requirements + +- **FR-384-001**: TenantPilot MUST provide a focused Baseline Subject Resolution surface scoped to workspace and managed environment. +- **FR-384-002**: The surface MUST derive actionable items from Spec 383 structured compare semantics and existing active binding decisions. +- **FR-384-003**: The surface MUST NOT parse legacy subject-key or historical compare payload shapes as authoritative resolution input. +- **FR-384-004**: The default list MUST show subject, subject class, resource type, provider, environment, problem, readiness impact, actionability, candidate count, current decision, last seen/source context, and available action. +- **FR-384-005**: The list MUST support filters for environment context, provider, subject class, resource type, actionability, readiness impact, reason, active binding, candidates, and operation run where relevant. +- **FR-384-006**: The page MUST show safe empty states for no decisions required and for no compare context available. +- **FR-384-007**: Operators MUST be able to bind unresolved subjects to valid provider resources through `manual_binding`. +- **FR-384-008**: Operators MUST be able to confirm canonical built-in or virtual target decisions only when the candidate identity comes from provider/canonical metadata, not display names. +- **FR-384-009**: Operators MUST be able to exclude a subject as non-governed through `excluded_non_governed`. +- **FR-384-010**: Operators MUST be able to accept a limitation through `accepted_limitation`. +- **FR-384-011**: Operators MUST be able to mark unsupported coverage through `unsupported_coverage`. +- **FR-384-012**: Operators SHOULD be able to mark missing expected through `missing_expected` if existing service/model support is sufficient without new persistence. +- **FR-384-013**: Operators MUST be able to revoke an active decision. +- **FR-384-014**: Manual binding, exclusion, accepted limitation, unsupported coverage, missing expected, and revocation MUST require an operator note. +- **FR-384-015**: Decision actions MUST supersede previous active decisions for the same scoped canonical subject according to existing binding service behavior. +- **FR-384-016**: Revoked decisions MUST NOT affect future baseline compares. +- **FR-384-017**: Every manual decision MUST emit audit metadata including actor, workspace, environment, provider, subject key, mode, previous/new decision where applicable, note metadata, source compare/run, and source references when available. +- **FR-384-018**: Every action MUST enforce workspace/environment scoped authorization server-side. +- **FR-384-019**: Non-member access MUST be denied as not found; entitled members missing manage capability MUST be forbidden on mutation. +- **FR-384-020**: Display names MUST NOT be accepted or persisted as identity. They may appear only as labels. +- **FR-384-021**: OperationRun detail MUST link to filtered resolution context where actionable subject-resolution outcomes exist. +- **FR-384-022**: Baseline Compare MUST link to the resolution surface where actionable outcomes exist. +- **FR-384-023**: Environment dashboard and Baseline Profile shortcuts MAY be added only as summary links, not full redesigns. +- **FR-384-024**: The page MUST offer a safe path to rerun/refresh comparison through existing baseline compare OperationRun UX. +- **FR-384-025**: Subsequent baseline compares MUST consume active decisions through the existing matching pipeline. +- **FR-384-026**: Accepted limitation, exclusion, unsupported, and missing expected decisions MUST NOT be presented as verified no-drift. +- **FR-384-027**: The feature MUST NOT finalize Evidence Snapshot readiness, Review Pack publication readiness, or customer-facing limitation wording. +- **FR-384-028**: The feature MUST NOT introduce a generic workflow/task/approval engine. + +### Non-Functional Requirements + +- **NFR-384-001**: Workspace isolation is mandatory for all reads, candidates, decisions, links, and audit context. +- **NFR-384-002**: Managed-environment isolation is mandatory; decisions must not cross environment boundaries. +- **NFR-384-003**: Rendering the page and details must be DB-only and must not call Graph or provider runtime APIs. +- **NFR-384-004**: Provider resource IDs and external IDs must be truncated or progressively disclosed by default. +- **NFR-384-005**: The UI must be calm, scan-first, and decision-led, with diagnostics secondary. +- **NFR-384-006**: Native Filament components/shared primitives must be used before custom Blade or local styling. +- **NFR-384-007**: High-impact action modals must clearly state mutation scope: TenantPilot decision only, not direct provider mutation. +- **NFR-384-008**: Test fixtures must remain feature-local and must not widen default provider/workspace setup. + +## UI Action Matrix *(mandatory when Filament is changed)* + +| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions | +|---|---|---|---|---|---|---|---|---|---|---| +| Baseline Subject Resolution page | `apps/platform/app/Filament/Pages/...` preferred | `Run comparison again` only if delegated to existing compare UX; no broad nav action | row focus/detail drawer or focused detail area; exactly one inspect model | primary contextual `Resolve` or `Review decision`; risky actions in modal/detail More | none in V1 | `Run baseline compare` if no compare context and user can manage; no CTA when no decisions required unless compare rerun is useful | N/A unless detail subpage is chosen | N/A | yes for every decision action through binding service | Action labels must use Verb + Object. Destructive/high-impact actions require confirmation, authorization, note rules, and audit. | +| Baseline Compare page link | existing `BaselineCompareLanding` | `Resolve baseline subjects` link only when actionable outcomes exist | existing inspect paths unchanged | no new row mutation | none | existing compare CTA remains | N/A | N/A | N/A for link | Link is navigation, not mutation. | +| OperationRun detail follow-up | existing `OperationRunResource` detail | no new mutation | existing detail sections unchanged | follow-up link only | none | N/A | N/A | N/A | N/A for link | Link opens filtered resolution page; no local run UX. | + +### Key Entities + +- **Baseline subject decision row**: Derived UI row representing a governed subject outcome from structured compare semantics plus current binding/decision state. +- **Provider resource candidate**: A provider-backed identity option with `ResourceIdentity`/descriptor proof that can be selected for binding. +- **Provider resource binding**: Existing persisted decision truth in `provider_resource_bindings`. +- **Resolution mode**: Existing `ProviderResourceResolutionMode` value such as `manual_binding`, `excluded_non_governed`, `accepted_limitation`, `unsupported_coverage`, or `missing_expected`. +- **Current decision**: Active binding/decision for a scoped canonical subject. + +## Out of Scope + +- Evidence Snapshot readiness final mapping. +- Review Output readiness or Review Pack publication blocker final mapping. +- Customer-facing report wording. +- Generic workflow/task/approval engine. +- Broad Governance Inbox. +- Management Report/PDF runtime or staging validation. +- Legacy subject-key UI or old OperationRun payload compatibility UI. +- Display-name-based matching or binding. +- Broad Baseline Profile redesign. +- Bulk destructive actions. +- New primary decision table. +- Direct Graph/provider mutation. +- New granular capability family unless this spec and plan are updated first. + +## Acceptance Criteria + +- **AC-384-001**: A focused Baseline Subject Resolution page exists and is reachable through environment-scoped admin routing. +- **AC-384-002**: The page lists actionable outcomes from Spec 383 semantics and hides resolved/no-action subjects by default. +- **AC-384-003**: Manual binding to a valid provider resource creates or supersedes an active audited decision. +- **AC-384-004**: Exclusion, accepted limitation, unsupported coverage, missing expected where supported, and revocation actions require notes, confirmation, authorization, and audit. +- **AC-384-005**: Unauthorized/non-entitled access follows 404/403 rules. +- **AC-384-006**: Display-name-only identity is rejected. +- **AC-384-007**: OperationRun and Baseline Compare contextual links open filtered resolution context. +- **AC-384-008**: Future compare consumes active decisions and ignores revoked decisions. +- **AC-384-009**: Exclusion and accepted limitation are not displayed as verified no-drift. +- **AC-384-010**: UI coverage registry and browser smoke evidence are updated during implementation. +- **AC-384-011**: No Evidence/Review final mapping, customer-facing wording, Management Report/PDF work, or workflow engine is introduced. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-384-001**: In validation fixtures with actionable subject blockers, an entitled operator can identify the problem, candidate count, current decision, and next action for each subject from the default page without opening raw diagnostics. +- **SC-384-002**: 100% of decision actions in targeted tests write an audit entry and enforce server-side authorization. +- **SC-384-003**: 100% of display-name-only binding attempts in targeted tests are rejected without creating or updating a binding. +- **SC-384-004**: After an active binding/exclusion/limitation decision is created and compare is rerun in targeted tests, the same subject no longer appears as unresolved for the prior reason. +- **SC-384-005**: Automated or manual browser smoke reaches the new surface and verifies the page is nonblank, scoped, and action modals are reachable without overlapping or broken UI. + +## Assumptions + +- Specs 381, 382, and 383 remain implemented on the target branch before Spec 384 implementation begins. +- Existing `provider_resource_bindings` fields and modes are sufficient for V1 decisions. +- Existing baseline capabilities are sufficient for V1 RBAC; granular capabilities are a later follow-up unless proven necessary. +- Resolution decisions are TenantPilot-only DB decisions and do not mutate provider resources. +- Existing compare rerun/start behavior remains the only path for refreshing compare output. +- Browser fixtures can provide a reachable workspace/environment/baseline compare scenario; if not, implementation must perform and document manual browser-smoke evidence instead of skipping browser validation. + +## Risks + +- **Workflow creep**: The surface could become a generic remediation queue. Mitigation: only baseline subject decision actions are in scope. +- **False green**: Accepted limitations or exclusions could look healthy. Mitigation: UI and tests must state they are not no-drift. +- **Identity regression**: Display names could be treated as identity. Mitigation: bind only valid `ResourceIdentity` candidates. +- **Permission risk**: Operators could hide real issues. Mitigation: manage capability, required notes, confirmations, and audit. +- **UI noise**: Too many categories can overwhelm operators. Mitigation: group by actionability and keep diagnostics progressive. +- **Provider-specific leakage**: Microsoft labels could become core truth. Mitigation: keep provider-specific data as descriptors/proof only. + +## Open Questions + +No open question blocks implementation readiness. + +Non-blocking follow-up decisions: + +- Whether a later spec should introduce granular `baseline_subject_resolution.*` capabilities. +- Whether accepted limitations should expire or require renewal. +- Whether subject-level exclusions should later roll up to Baseline Profile scope policy. +- Whether revocation history should appear in the main list or only in compact audit history after V1. + +## Follow-Up Spec Candidates + +- Spec 385 - Evidence and Review Readiness Integration v1. +- Accepted limitation expiry and renewal policy. +- Granular baseline subject resolution capabilities. +- Baseline Profile subject-scope policy productization. +- Approval workflow for high-risk exclusions, if customer evidence demands it. diff --git a/specs/384-baseline-subject-resolution-ui/tasks.md b/specs/384-baseline-subject-resolution-ui/tasks.md new file mode 100644 index 00000000..59d26079 --- /dev/null +++ b/specs/384-baseline-subject-resolution-ui/tasks.md @@ -0,0 +1,233 @@ +# Tasks: Spec 384 - Baseline Subject Resolution UI and Operator Decisions v1 + +**Input**: Design documents from `/specs/384-baseline-subject-resolution-ui/` +**Prerequisites**: `plan.md`, `spec.md` + +**Tests**: Tests are REQUIRED because this feature adds runtime behavior, high-impact Filament actions, RBAC/audit paths, and a new reachable operator surface. + +## Test Governance Checklist + +- [x] TGC001 Lane assignment is named and is the narrowest sufficient proof for the changed behavior. +- [x] TGC002 New or changed tests stay in the smallest honest family, and any heavy-governance or browser addition is explicit. +- [x] TGC003 Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default; any widening is isolated or documented. +- [x] TGC004 Planned validation commands cover the change without pulling in unrelated lane cost. +- [x] TGC005 The declared surface test profile or `standard-native-filament` relief is explicit. +- [x] TGC006 Any material budget, baseline, trend, or escalation note is recorded in the active spec or PR. + +## Implementation Notes + +- Planned separate test files were consolidated where narrower: `BaselineSubjectResolutionPageTest.php` covers render, empty states, actions, RBAC, Baseline Compare link behavior, and OperationRun related-navigation behavior; `BaselineSubjectResolutionQueryTest.php` covers query/filter/legacy semantics. +- Existing `ProviderResourceBindingServiceTest.php` and `SubjectMatchingPipelineTest.php` remain the canonical coverage for all V1 binding modes and active/revoked decision consumption. +- Broad `tests/Feature/Baselines tests/Feature/ProviderResources` validation was run and residual baseline capture/compare failures are recorded in `implementation-close-out.md`. + +## Phase 1: Setup and Guardrails + +**Purpose**: Confirm dependency close-outs, repo truth, and UI guardrails before implementation starts. + +- [x] T001 Confirm `specs/381-provider-resource-identity-binding/implementation-close-out.md`, `specs/382-baseline-matching-canonicalization/implementation-close-out.md`, and `specs/383-baseline-result-semantics/implementation-close-out.md` exist and treat them as dependency context only. +- [x] T002 Confirm no code or artifact changes are made to completed specs `specs/381-provider-resource-identity-binding/`, `specs/382-baseline-matching-canonicalization/`, `specs/383-baseline-result-semantics/`, or historical `specs/163-baseline-subject-resolution/`. +- [x] T003 Re-read `apps/platform/app/Services/Resources/ProviderResourceBindingService.php`, `apps/platform/app/Models/ProviderResourceBinding.php`, `apps/platform/app/Policies/ProviderResourceBindingPolicy.php`, and `apps/platform/app/Support/Resources/ProviderResourceResolutionMode.php`; explicitly verify whether `missing_expected` is already supported without new persistence before implementing that mode. +- [x] T004 Re-read `apps/platform/app/Filament/Pages/BaselineCompareLanding.php`, `apps/platform/app/Filament/Resources/OperationRunResource.php`, `apps/platform/app/Livewire/BaselineCompareEvidenceGapTable.php`, and `docs/ui-ux-enterprise-audit/page-reports/ui-015-baseline-compare.md`. +- [x] T005 Apply `docs/product/standards/list-surface-review-checklist.md` for the new list/table surface, then update UI coverage artifacts for the new surface in `docs/ui-ux-enterprise-audit/route-inventory.md`, `docs/ui-ux-enterprise-audit/design-coverage-matrix.md`, and a new or updated page report under `docs/ui-ux-enterprise-audit/page-reports/`. +- [x] T006 Confirm no new Filament panel provider, broad top-level navigation item, global search resource, generic workflow engine, Evidence/Review readiness mapping, or Management Report/PDF scope is added; if required, stop and update `spec.md` and `plan.md`. + +--- + +## Phase 2: Foundational Resolution Query + +**Purpose**: Build the derived read path that turns Spec 383 result semantics plus active decisions into actionable resolution rows. + +- [x] T007 [P] Add unit coverage for actionable row derivation in `apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php`, including a negative assertion that row derivation uses persisted compare/binding data and does not invoke Graph or provider runtime clients. +- [x] T008 [P] Add feature coverage for workspace/environment denial in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T009 [P] Add feature coverage proving legacy subject-key or historical payload shapes are not authoritative in `apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php`. +- [x] T010 Add a focused query/read service for subject resolution rows under `apps/platform/app/Services/Baselines/` or `apps/platform/app/Support/Baselines/`, deriving rows from current compare semantics and active `provider_resource_bindings`. +- [x] T011 Ensure the query supports filters for operation run, provider, subject class, resource type, actionability, readiness impact, reason, active binding, and candidate availability. +- [x] T012 Ensure the query returns display labels only as human-readable metadata and never as identity. +- [x] T013 Ensure resolved/no-action subjects are excluded from the default worklist while available through explicit filters if needed. + +**Checkpoint**: Actionable subject rows can be derived and tested without UI. + +--- + +## Phase 3: User Story 1 - Find Actionable Subject Decisions (Priority: P1) - MVP + +**Goal**: Provide the focused list/detail context operators need before decisions can be made. + +**Independent Test**: The page lists actionable outcomes, supports filters, and shows correct empty states without raw diagnostics. + +### Tests for User Story 1 + +- [x] T014 [P] [US1] Add Filament/Livewire page render coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`, including DB-only render coverage. +- [x] T015 [P] [US1] Add filter and empty-state coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php` and `apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php`. +- [x] T016 [P] [US1] Add candidate/detail disclosure coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. + +### Implementation for User Story 1 + +- [x] T017 [US1] Add the environment-scoped Baseline Subject Resolution page under `apps/platform/app/Filament/Pages/` using the route chosen in `spec.md`. +- [x] T018 [US1] Implement the native Filament table/list with columns for subject, class, type, provider, problem, readiness impact, actionability, candidate count, current decision, source/last seen, and action. +- [x] T019 [US1] Implement focused row/action-modal detail with subject context, candidate list, and current decision using progressive disclosure. +- [x] T020 [US1] Add empty states for "no baseline subject decisions required" and "run baseline compare first". +- [x] T021 [US1] Ensure raw provider IDs, external IDs, fingerprints, and source proof are truncated/collapsed by default and not primary page content. + +**Checkpoint**: Operators can find actionable decisions in one scoped page. + +--- + +## Phase 4: User Story 2 - Make Audited Resolution Decisions (Priority: P1) + +**Goal**: Let authorized operators persist binding, exclusion, limitation, unsupported, missing expected, and revocation decisions through existing decision truth. + +**Independent Test**: Each action creates/supersedes/revokes an active decision, emits audit, enforces note rules, and denies unauthorized actors. + +### Tests for User Story 2 + +- [x] T022 [P] [US2] Add decision action tests in `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingServiceTest.php` and `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php` for binding, accepted limitation, `missing_expected` support, supersession, and revocation. +- [x] T023 [P] [US2] Add RBAC positive/negative action tests in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T024 [P] [US2] Add audit assertions in `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingServiceTest.php` and `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T025 [P] [US2] Add display-name rejection coverage in `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingServiceTest.php`. +- [x] T026 [P] [US2] Add Filament action modal/note/confirmation tests in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. + +### Implementation for User Story 2 + +- [x] T027 [US2] Wire manual binding action to `ProviderResourceBindingService::createManualBinding()` and require a valid `ResourceIdentity` candidate plus operator note. +- [x] T028 [US2] Wire decision recording only when provider/canonical metadata supplies a valid identity. +- [x] T029 [US2] Wire exclusion, accepted limitation, unsupported coverage, and `missing_expected` only when T003 confirms existing support to existing `ProviderResourceBindingService` methods, requiring notes and clear modal copy. +- [x] T030 [US2] Wire revocation action to `ProviderResourceBindingService::revoke()` with required note and confirmation. +- [x] T031 [US2] Apply `UiEnforcement` or `WorkspaceUiEnforcement` and server-side Gate/Policy checks so non-members are 404 and members missing manage capability are 403 on mutation. +- [x] T032 [US2] Ensure every high-impact action uses Filament `->action(...)` plus confirmation and does not execute through URL-only actions. +- [x] T033 [US2] Ensure action copy states mutation scope is TenantPilot decision only and not a direct provider/Microsoft mutation. + +**Checkpoint**: Operators can make audited decisions; unauthorized actors cannot. + +--- + +## Phase 5: User Story 3 - Navigate From Compare and Operation Context (Priority: P2) + +**Goal**: Add filtered links/counts from existing surfaces without turning those surfaces into decision owners. + +**Independent Test**: Baseline Compare and OperationRun detail show links only when actionable outcomes exist and preserve workspace/environment filters. + +### Tests for User Story 3 + +- [x] T034 [P] [US3] Add Baseline Compare contextual link/count coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T035 [P] [US3] Add OperationRun follow-up link coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T036 [P] [US3] Add link-scope denial coverage in `apps/platform/tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. + +### Implementation for User Story 3 + +- [x] T037 [US3] Update `apps/platform/app/Filament/Pages/BaselineCompareLanding.php` or its supporting presenter to show action-required counts and a `Resolve baseline subjects` navigation link only when actionable outcomes exist. +- [x] T038 [US3] Update the OperationRun related-navigation support to add concise subject-resolution follow-up text and a filtered link for baseline compare runs. +- [x] T039 [US3] Ensure links include only safe filters such as operation run, workspace, and environment, and reject cross-environment run IDs. +- [x] T040 [US3] No environment dashboard or Baseline Profile shortcut was added in V1. + +**Checkpoint**: Operators can reach the resolution page from compare/run context without duplicate decision UI. + +--- + +## Phase 6: User Story 4 - Re-run or Refresh Compare After Decisions (Priority: P3) + +**Goal**: Give operators a safe path to validate decisions through existing baseline compare OperationRun UX. + +**Independent Test**: Rerun/refresh delegates to existing compare start UX, and the next compare consumes active decisions. + +### Tests for User Story 4 + +- [x] T041 [P] [US4] Existing `apps/platform/tests/Unit/Support/Baselines/Matching/SubjectMatchingPipelineTest.php` covers active-decision consumption. +- [x] T042 [P] [US4] Rerun/refresh UX delegates to existing compare UX in `apps/platform/app/Filament/Pages/BaselineSubjectResolution.php`. +- [x] T043 [P] [US4] Existing matching and provider-resource tests cover revoked decisions not being active truth. + +### Implementation for User Story 4 + +- [x] T044 [US4] Add a rerun/refresh compare path only by delegating to existing baseline compare service/start UX; do not locally compose queued toasts, run links, terminal notifications, or OperationRun lifecycle changes. +- [x] T045 [US4] Ensure `SubjectMatchingPipeline` or existing compare integration consumes active decisions and ignores revoked decisions without adding display-name fallback. +- [x] T046 [US4] Ensure resolved/excluded/accepted-limitation subjects no longer appear as unresolved after a rerun, while still avoiding false no-drift presentation. + +**Checkpoint**: Operators can validate decisions through existing compare workflow. + +--- + +## Phase 7: Polish and Cross-Cutting Validation + +**Purpose**: Close UI coverage, browser smoke, regression, formatting, and deployment notes. + +- [x] T047 [P] Update UI coverage close-out details in `docs/ui-ux-enterprise-audit/route-inventory.md`, `docs/ui-ux-enterprise-audit/design-coverage-matrix.md`, and the new/updated page report. +- [x] T048 [P] Review localization/translation handling for new labels, empty states, actions, modal headings, warnings, and audit-facing copy; V1 keeps page-local operator copy consistent with adjacent Filament pages. +- [x] T049 [P] Add automated browser smoke coverage for the new surface under `apps/platform/tests/Browser/`. +- [x] T050 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines tests/Unit/Support/Resources`. +- [x] T051 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines tests/Feature/ProviderResources`; residual non-Spec-384 failures recorded in `implementation-close-out.md`. +- [x] T052 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php tests/Feature/Filament/BaselineSubjectResolutionPageTest.php`. +- [x] T053 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Evidence/BaselineDriftPostureSourceTest.php tests/Feature/ReviewPack/Spec347ReviewPackReadinessSemanticsTest.php tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php`. +- [x] T054 Run `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php --filter BaselineSubjectResolution`. +- [x] T055 Run `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`. +- [x] T056 Run `git diff --check`. +- [x] T057 Record implementation close-out with Livewire v4 compliance, provider registration location, global search status, destructive/high-impact action confirmation/authorization/audit, asset strategy, tests run, browser smoke result, and deployment impact. + +--- + +## Dependencies and Execution Order + +### Phase Dependencies + +- **Phase 1** blocks implementation because dependency and UI coverage decisions must be confirmed first. +- **Phase 2** blocks all user stories because the page and links need a single derived query/read path. +- **US1** can begin after Phase 2 and delivers the MVP visible decision worklist. +- **US2** depends on Phase 2 and can run alongside parts of US1 after the page action targets are known. +- **US3** depends on the query and route from US1. +- **US4** depends on decision actions from US2 and link/page behavior from US1. +- **Phase 7** follows all implemented stories. + +### User Story Dependencies + +- **US1 (P1)**: MVP list/detail surface. +- **US2 (P1)**: primary mutation value; depends on query rows/candidates. +- **US3 (P2)**: discoverability from existing surfaces; depends on route/query. +- **US4 (P3)**: validation loop after decisions; depends on actions. + +### Parallel Opportunities + +- T007-T009 can run in parallel. +- T014-T016 can run in parallel. +- T022-T026 can run in parallel. +- T034-T036 can run in parallel. +- T041-T043 can run in parallel. +- T047-T049 can run in parallel near close-out. + +## Parallel Example: Query Foundation + +```text +Task: "Add unit coverage for actionable row derivation in apps/platform/tests/Unit/Support/Baselines/BaselineSubjectResolutionQueryTest.php" +Task: "Add feature coverage for workspace/environment denial in apps/platform/tests/Feature/Baselines/BaselineSubjectResolutionIsolationTest.php" +Task: "Add legacy-payload refusal coverage in apps/platform/tests/Feature/Baselines/BaselineSubjectResolutionLegacyPayloadTest.php" +``` + +## Parallel Example: Decision Actions + +```text +Task: "Add decision action tests in apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingServiceResolutionTest.php" +Task: "Add RBAC positive/negative action tests in apps/platform/tests/Feature/Filament/BaselineSubjectResolutionActionAuthorizationTest.php" +Task: "Add Filament action modal/note/confirmation tests in apps/platform/tests/Feature/Filament/BaselineSubjectResolutionActionsTest.php" +``` + +## Implementation Strategy + +### MVP First + +Deliver Phase 2 plus US1 first. This gives a focused, scoped, read-only operator worklist and proves the query/page shape before high-impact actions land. + +### Incremental Delivery + +1. Finish setup/guardrails and query foundation. +2. Implement the list/detail page without mutations. +3. Add audited decision actions. +4. Add contextual links from Baseline Compare and OperationRun detail. +5. Add rerun/refresh compare delegation and future-compare consumption checks. +6. Finish UI coverage, browser smoke, regression, and close-out. + +### Non-Goals During Implementation + +- Do not implement Spec 385 Evidence/Review readiness. +- Do not add Management Report/PDF work. +- Do not introduce a generic workflow, task, approval, or notification engine. +- Do not parse legacy subject-key payloads. +- Do not use display names as identity. +- Do not add a new primary decision table without updating spec and plan.