From 6c7a80e275b65d6fed0a7d433d90014c285a0f82 Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Mon, 15 Jun 2026 13:24:41 +0200 Subject: [PATCH] feat(report): implement management report pdf runtime Added jobs, controllers, and PDF generation logic for management report runtime as defined in Spec 379. Includes artifact migrations, payload builders, and testing coverage. --- apps/platform/.env.example | 3 + .../Filament/Resources/ReviewPackResource.php | 2 +- .../Pages/ViewReviewPack.php | 251 ++++++- .../ManagementReportPdfDownloadController.php | 103 +++ .../Jobs/GenerateManagementReportPdfJob.php | 388 ++++++++++ apps/platform/app/Models/StoredReport.php | 59 +- .../Sources/BaselineDriftPostureSource.php | 44 +- .../ManagementReportPdfRenderer.php | 155 ++++ .../ManagementReportPdfService.php | 413 +++++++++++ .../app/Support/Audit/AuditActionId.php | 15 + .../platform/app/Support/OperationCatalog.php | 2 + .../platform/app/Support/OperationRunType.php | 1 + .../ManagementReportPdfPayloadBuilder.php | 321 ++++++++ .../ManagementReportPdfRuntimeGate.php | 68 ++ apps/platform/config/tenantpilot.php | 9 + .../factories/StoredReportFactory.php | 28 +- ..._pdf_artifacts_to_stored_reports_table.php | 58 ++ apps/platform/lang/de/localization.php | 9 + apps/platform/lang/en/localization.php | 9 + apps/platform/routes/web.php | 5 + .../Spec379ManagementReportPdfSmokeTest.php | 221 ++++++ .../BaselineDriftPostureSourceTest.php | 62 ++ .../Spec379ManagementReportPdfTest.php | 695 ++++++++++++++++++ .../Pdf/Spec378PdfRenderingGatewayTest.php | 5 +- ...pec379ManagementReportPdfReadinessTest.php | 64 ++ docker-compose.yml | 4 +- docs/deployment-checklist.md | 2 +- docs/product/implementation-ledger.md | 113 ++- docs/product/roadmap.md | 85 +-- docs/product/spec-candidates.md | 184 +++-- ...er-resource-identity-binding-foundation.md | 313 ++++++++ ...line-matching-pipeline-canonicalization.md | 234 ++++++ .../382-baseline-compare-result-semantics.md | 239 ++++++ .../383-baseline-subject-resolution-ui.md | 302 ++++++++ ...4-evidence-review-readiness-integration.md | 281 +++++++ .../artifacts/runtime-validation.md | 68 ++ .../artifacts/screenshots/download-state.png | Bin 0 -> 206824 bytes .../artifacts/screenshots/generate-state.png | Bin 0 -> 209801 bytes .../storage-operationrun-decision.md | 80 ++ .../checklists/requirements.md | 69 ++ .../379-management-report-pdf-runtime/plan.md | 283 +++++++ .../379-management-report-pdf-runtime/spec.md | 393 ++++++++++ .../tasks.md | 201 +++++ 43 files changed, 5659 insertions(+), 182 deletions(-) create mode 100644 apps/platform/app/Http/Controllers/ManagementReportPdfDownloadController.php create mode 100644 apps/platform/app/Jobs/GenerateManagementReportPdfJob.php create mode 100644 apps/platform/app/Services/ReviewPacks/ManagementReportPdfRenderer.php create mode 100644 apps/platform/app/Services/ReviewPacks/ManagementReportPdfService.php create mode 100644 apps/platform/app/Support/ReviewPacks/ManagementReportPdfPayloadBuilder.php create mode 100644 apps/platform/app/Support/ReviewPacks/ManagementReportPdfRuntimeGate.php create mode 100644 apps/platform/database/migrations/2026_06_14_000379_add_management_pdf_artifacts_to_stored_reports_table.php create mode 100644 apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php create mode 100644 apps/platform/tests/Feature/Evidence/BaselineDriftPostureSourceTest.php create mode 100644 apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php create mode 100644 apps/platform/tests/Unit/Support/ReviewPacks/Spec379ManagementReportPdfReadinessTest.php create mode 100644 spec-candidates/380-provider-resource-identity-binding-foundation.md create mode 100644 spec-candidates/381-baseline-matching-pipeline-canonicalization.md create mode 100644 spec-candidates/382-baseline-compare-result-semantics.md create mode 100644 spec-candidates/383-baseline-subject-resolution-ui.md create mode 100644 spec-candidates/384-evidence-review-readiness-integration.md create mode 100644 specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md create mode 100644 specs/379-management-report-pdf-runtime/artifacts/screenshots/download-state.png create mode 100644 specs/379-management-report-pdf-runtime/artifacts/screenshots/generate-state.png create mode 100644 specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md create mode 100644 specs/379-management-report-pdf-runtime/checklists/requirements.md create mode 100644 specs/379-management-report-pdf-runtime/plan.md create mode 100644 specs/379-management-report-pdf-runtime/spec.md create mode 100644 specs/379-management-report-pdf-runtime/tasks.md diff --git a/apps/platform/.env.example b/apps/platform/.env.example index 76cb5a72..338556c7 100644 --- a/apps/platform/.env.example +++ b/apps/platform/.env.example @@ -61,6 +61,7 @@ MAIL_FROM_NAME="${APP_NAME}" # Internal PDF renderer (Spec 378) TENANTPILOT_PDF_RENDERER_ENABLED=true +TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED=false TENANTPILOT_PDF_RENDERER_BASE_URL=http://gotenberg:3000 TENANTPILOT_PDF_RENDERER_TIMEOUT_SECONDS=30 TENANTPILOT_PDF_RENDERER_CONNECT_TIMEOUT_SECONDS=5 @@ -74,6 +75,8 @@ GOTENBERG_API_CORRELATION_ID_HEADER=Gotenberg-Trace GOTENBERG_CHROMIUM_START_TIMEOUT=20s GOTENBERG_CHROMIUM_MAX_QUEUE_SIZE=10 GOTENBERG_CHROMIUM_MAX_CONCURRENCY=2 +GOTENBERG_CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES=true +GOTENBERG_CHROMIUM_ALLOW_LIST="^file:///tmp/.*$" SUPPORT_DESK_ENABLED=false SUPPORT_DESK_NAME="External support desk" diff --git a/apps/platform/app/Filament/Resources/ReviewPackResource.php b/apps/platform/app/Filament/Resources/ReviewPackResource.php index c3b93f74..a06c8402 100644 --- a/apps/platform/app/Filament/Resources/ReviewPackResource.php +++ b/apps/platform/app/Filament/Resources/ReviewPackResource.php @@ -125,7 +125,7 @@ public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state carries the single Generate CTA while the registry is empty.') ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Clickable-row inspection stays primary while Download remains the only direct row shortcut and Expire is grouped under More.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk operations are supported for review packs.') - ->satisfy(ActionSurfaceSlot::DetailHeader, 'Rendered-report preview stays primary while Download and Regenerate remain available in the ViewReviewPack header.'); + ->satisfy(ActionSurfaceSlot::DetailHeader, 'Rendered-report preview stays primary while Download, Management PDF generation/download, and Regenerate remain available in the ViewReviewPack header.'); } public static function form(Schema $schema): Schema diff --git a/apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php b/apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php index ad38b2d5..c66f6ff3 100644 --- a/apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php +++ b/apps/platform/app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php @@ -2,20 +2,31 @@ namespace App\Filament\Resources\ReviewPackResource\Pages; +use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException; +use App\Exceptions\ReviewPackEvidenceResolutionException; use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Resources\ReviewPackResource; use App\Models\EnvironmentReview; +use App\Models\OperationRun; use App\Models\ReviewPack; +use App\Models\StoredReport; +use App\Models\User; +use App\Services\ReviewPacks\ManagementReportPdfService; use App\Services\ReviewPackService; use App\Support\Auth\Capabilities; +use App\Support\OperationRunLinks; +use App\Support\OperationRunType; +use App\Support\OpsUx\OperationUxPresenter; use App\Support\Rbac\UiEnforcement; use App\Support\ReviewPacks\ReportProfileRegistry; use App\Support\ReviewPacks\ReviewPackOutputResolutionGuidance; use App\Support\ReviewPackStatus; use Filament\Actions; use Filament\Forms\Components\Toggle; +use Filament\Notifications\Notification; use Filament\Resources\Pages\ViewRecord; use Filament\Schemas\Components\Section; +use Filament\Support\Enums\Width; class ViewReviewPack extends ViewRecord { @@ -46,9 +57,9 @@ protected function getHeaderActions(): array $regenerateAction = UiEnforcement::forAction( Actions\Action::make('regenerate') - ->label('Regenerate') + ->label('Regenerate review pack') ->icon('heroicon-o-arrow-path') - ->color('primary') + ->color('gray') ->disabled(fn (): bool => ReviewPackResource::reviewPackGenerationBlocked($this->record->tenant)) ->requiresConfirmation() ->modalDescription('This will generate a new review pack with the same options. The current pack will remain available until it expires.') @@ -61,7 +72,7 @@ protected function getHeaderActions(): array 'include_operations' => (bool) ($data['include_operations'] ?? ($record->options['include_operations'] ?? true)), ]); - ReviewPackResource::executeGeneration($options); + $this->regenerateReviewPack($options); }) ->form(function (): array { /** @var ReviewPack $record */ @@ -89,25 +100,241 @@ protected function getHeaderActions(): array $regenerateAction->tooltip(fn (): ?string => ReviewPackResource::reviewPackGenerationActionTooltip($this->record->tenant)); + $readyManagementReportPdf = $this->readyManagementReportPdf(); + return [ $this->openRenderedReportAction([ 'source_surface' => 'review_pack', 'review_id' => $this->record->environment_review_id, 'tenant_filter_id' => request()->query('tenant_filter_id'), 'interpretation_version' => $this->record->environmentReview?->controlInterpretationVersion(), - ]), - Actions\Action::make('download') - ->label(fn (): string => ReviewPackResource::downloadActionLabelFor($this->record)) - ->icon('heroicon-o-arrow-down-tray') + ], $readyManagementReportPdf instanceof StoredReport ? 'gray' : 'primary'), + $readyManagementReportPdf instanceof StoredReport + ? $this->downloadManagementReportPdfAction($readyManagementReportPdf) + : $this->downloadReviewPackAction(), + Actions\ActionGroup::make($readyManagementReportPdf instanceof StoredReport + ? [ + $this->downloadReviewPackAction(), + $regenerateAction, + ] + : [ + $this->managementReportPdfOverflowAction(), + $regenerateAction, + ]) + ->label(__('More')) + ->icon('heroicon-m-ellipsis-vertical') ->color('gray') - ->visible(fn (): bool => $this->record->status === ReviewPackStatus::Ready->value) - ->url(fn (): string => app(ReviewPackService::class)->generateDownloadUrl($this->record)) - ->openUrlInNewTab(), - - $regenerateAction, + ->button() + ->dropdownWidth(Width::Medium) + ->dropdownPlacement('bottom-end') + ->tooltip(__('More actions')), ]; } + /** + * @param array $data + */ + private function regenerateReviewPack(array $data): void + { + /** @var ReviewPack $record */ + $record = $this->record; + $record->loadMissing(['environmentReview', 'tenant']); + + $review = $record->environmentReview; + + if (! $review instanceof EnvironmentReview) { + ReviewPackResource::executeGeneration($data); + + return; + } + + $user = auth()->user(); + + if (! $user instanceof User) { + Notification::make()->danger()->title(__('localization.review.unable_export_missing_context'))->send(); + + return; + } + + $service = app(ReviewPackService::class); + + if ($service->checkActiveRunForReview($review)) { + OperationUxPresenter::alreadyQueuedToast(OperationRunType::ReviewPackGenerate->value) + ->body(__('localization.review.export_already_queued_body')) + ->send(); + + return; + } + + $options = [ + 'include_pii' => (bool) ($data['include_pii'] ?? true), + 'include_operations' => (bool) ($data['include_operations'] ?? true), + ]; + + try { + $reviewPack = $service->generateFromReview($review, $user, $options); + } catch (WorkspaceEntitlementBlockedException $exception) { + Notification::make() + ->warning() + ->title(__('localization.review.executive_pack_export_unavailable')) + ->body($exception->getMessage()) + ->send(); + + return; + } catch (ReviewPackEvidenceResolutionException $exception) { + $reasons = $exception->result->reasons; + + Notification::make() + ->danger() + ->title(__('localization.review.unable_export_executive_pack')) + ->body($reasons === [] ? $exception->getMessage() : implode(' ', $reasons)) + ->send(); + + return; + } + + if (! $reviewPack->wasRecentlyCreated) { + Notification::make() + ->success() + ->title(__('localization.review.executive_pack_already_available')) + ->body(__('localization.review.executive_pack_already_available_body')) + ->actions([ + Actions\Action::make('view_pack') + ->label(__('localization.review.view_pack')) + ->url(ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $review->tenant)), + ]) + ->send(); + + return; + } + + OperationUxPresenter::queuedToast(OperationRunType::ReviewPackGenerate->value) + ->body(__('localization.review.executive_pack_generating_background')) + ->send(); + } + + private function downloadReviewPackAction(): Actions\Action + { + return Actions\Action::make('download') + ->label(fn (): string => ReviewPackResource::downloadActionLabelFor($this->record)) + ->icon('heroicon-o-arrow-down-tray') + ->color('gray') + ->visible(fn (): bool => $this->record->status === ReviewPackStatus::Ready->value) + ->url(fn (): string => app(ReviewPackService::class)->generateDownloadUrl($this->record)) + ->openUrlInNewTab(); + } + + private function downloadManagementReportPdfAction(StoredReport $readyReport): Actions\Action + { + return Actions\Action::make('download_management_report_pdf') + ->label(__('localization.review.download_management_report_pdf')) + ->icon('heroicon-o-document-arrow-down') + ->color('primary') + ->url(fn (): string => app(ManagementReportPdfService::class)->generateDownloadUrl($readyReport, [ + 'source_surface' => 'review_pack', + ])) + ->openUrlInNewTab(); + } + + private function managementReportPdfOverflowAction(): Actions\Action + { + $activeRun = $this->activeManagementReportPdfOperation(); + + if ($activeRun instanceof OperationRun) { + return Actions\Action::make('open_management_report_pdf_operation') + ->label(__('localization.review.open_management_report_pdf_operation')) + ->icon('heroicon-o-clock') + ->color('gray') + ->url(fn (): string => $this->record->tenant + ? OperationRunLinks::view($activeRun, $this->record->tenant) + : url('/admin')) + ->openUrlInNewTab(); + } + + $action = UiEnforcement::forAction( + Actions\Action::make('generate_management_report_pdf') + ->label(fn (): string => $this->managementReportPdfActionLabel()) + ->icon('heroicon-o-document-arrow-down') + ->color(fn (): string => $this->managementReportPdfTooltip() === null ? 'primary' : 'gray') + ->requiresConfirmation() + ->modalDescription(__('localization.review.generate_management_report_pdf_confirmation')) + ->modalSubmitActionLabel(__('localization.review.generate_management_report_pdf_submit')) + ->disabled(fn (): bool => (bool) (app(ManagementReportPdfService::class)->generationDecision($this->record)['is_blocked'] ?? false)) + ->tooltip(fn (): ?string => $this->managementReportPdfTooltip()) + ->action(function (): void { + $user = auth()->user(); + + if (! $user instanceof User) { + abort(403); + } + + $result = app(ManagementReportPdfService::class)->startGeneration($this->record, $user); + $mode = (string) ($result['mode'] ?? ''); + + if ($mode === 'blocked') { + Notification::make() + ->warning() + ->title(__('localization.review.management_report_pdf_blocked')) + ->body((string) data_get($result, 'decision.reason', __('localization.review.management_report_pdf_blocked_default'))) + ->send(); + + return; + } + + if ($mode === 'ready') { + Notification::make() + ->success() + ->title(__('localization.review.management_report_pdf_ready')) + ->body(__('localization.review.management_report_pdf_ready_body')) + ->send(); + + return; + } + + if ($mode === 'active') { + OperationUxPresenter::alreadyRunningToast(OperationRunType::ManagementReportGenerate->value)->send(); + + return; + } + + OperationUxPresenter::queuedToast(OperationRunType::ManagementReportGenerate->value)->send(); + }), + ) + ->requireCapability(Capabilities::REVIEW_PACK_MANAGE) + ->preserveDisabled() + ->apply(); + + return $action; + } + + private function readyManagementReportPdf(): ?StoredReport + { + return app(ManagementReportPdfService::class)->findReadyReport($this->record); + } + + private function activeManagementReportPdfOperation(): ?OperationRun + { + $report = app(ManagementReportPdfService::class)->findActiveReport($this->record); + + return $report?->operationRun; + } + + private function managementReportPdfTooltip(): ?string + { + $decision = app(ManagementReportPdfService::class)->generationDecision($this->record); + + return (bool) ($decision['is_blocked'] ?? false) + ? (string) ($decision['reason'] ?? __('localization.review.management_report_pdf_blocked_default')) + : null; + } + + private function managementReportPdfActionLabel(): string + { + return $this->managementReportPdfTooltip() === null + ? __('localization.review.generate_management_report_pdf') + : __('localization.review.management_report_pdf_blocked'); + } + /** * @param array $parameters */ diff --git a/apps/platform/app/Http/Controllers/ManagementReportPdfDownloadController.php b/apps/platform/app/Http/Controllers/ManagementReportPdfDownloadController.php new file mode 100644 index 00000000..8dc746fa --- /dev/null +++ b/apps/platform/app/Http/Controllers/ManagementReportPdfDownloadController.php @@ -0,0 +1,103 @@ +loadMissing(['tenant.workspace', 'sourceReviewPack.environmentReview']); + + $user = $request->user(); + $tenant = $storedReport->tenant; + $reviewPack = $storedReport->sourceReviewPack; + + if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $reviewPack instanceof ReviewPack) { + throw new NotFoundHttpException; + } + + if (! $storedReport->isReadyManagementPdf()) { + throw new NotFoundHttpException; + } + + if ((int) $storedReport->managed_environment_id !== (int) $tenant->getKey() + || (int) $reviewPack->managed_environment_id !== (int) $tenant->getKey() + ) { + throw new NotFoundHttpException; + } + + if ($reviewPack->status !== ReviewPackStatus::Ready->value || ($reviewPack->expires_at !== null && $reviewPack->expires_at->isPast())) { + throw new NotFoundHttpException; + } + + $review = $reviewPack->environmentReview; + + if (! $review || (int) ($review->current_export_review_pack_id ?? 0) !== (int) $reviewPack->getKey()) { + throw new NotFoundHttpException; + } + + if (! $user->canAccessTenant($tenant)) { + throw new NotFoundHttpException; + } + + if (! $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant)) { + abort(403); + } + + $disk = Storage::disk((string) $storedReport->file_disk); + + if (! $disk->exists((string) $storedReport->file_path)) { + throw new NotFoundHttpException; + } + + app(WorkspaceAuditLogger::class)->log( + workspace: $tenant->workspace, + action: AuditActionId::ManagementReportPdfDownloaded, + context: [ + 'metadata' => [ + 'stored_report_id' => (int) $storedReport->getKey(), + 'review_pack_id' => (int) $reviewPack->getKey(), + 'environment_review_id' => $storedReport->source_environment_review_id !== null + ? (int) $storedReport->source_environment_review_id + : null, + 'source_surface' => (string) $request->query('source_surface', 'review_pack'), + 'profile' => (string) $storedReport->profile, + ], + ], + actor: $user, + resourceType: 'stored_report', + resourceId: (string) $storedReport->getKey(), + targetLabel: sprintf('Management report PDF #%d', (int) $storedReport->getKey()), + tenant: $tenant, + operationRunId: $storedReport->operation_run_id, + ); + + return $disk->download((string) $storedReport->file_path, $this->filename($storedReport, $tenant), [ + 'Content-Type' => 'application/pdf', + 'X-Management-Report-PDF-SHA256' => $storedReport->sha256 ?? '', + ]); + } + + private function filename(StoredReport $storedReport, ManagedEnvironment $tenant): string + { + $tenantSlug = preg_replace('/[^A-Za-z0-9._-]+/', '-', (string) ($tenant->external_id ?: $tenant->name)) ?: 'environment'; + $date = $storedReport->generated_at?->format('Y-m-d') ?? now()->format('Y-m-d'); + + return sprintf('management-report-%s-%s.pdf', trim($tenantSlug, '-') ?: 'environment', $date); + } +} diff --git a/apps/platform/app/Jobs/GenerateManagementReportPdfJob.php b/apps/platform/app/Jobs/GenerateManagementReportPdfJob.php new file mode 100644 index 00000000..c6ecede1 --- /dev/null +++ b/apps/platform/app/Jobs/GenerateManagementReportPdfJob.php @@ -0,0 +1,388 @@ +with(['tenant.workspace', 'sourceReviewPack.environmentReview.sections', 'operationRun', 'generatedBy']) + ->find($this->storedReportId); + $operationRun = OperationRun::query()->find($this->operationRunId); + + if (! $report instanceof StoredReport || ! $operationRun instanceof OperationRun) { + Log::warning('GenerateManagementReportPdfJob: missing records', [ + 'stored_report_id' => $this->storedReportId, + 'operation_run_id' => $this->operationRunId, + ]); + + return; + } + + $tenant = $report->tenant; + $reviewPack = $report->sourceReviewPack; + + if (! $tenant instanceof ManagedEnvironment || ! $reviewPack instanceof ReviewPack) { + $this->markFailed($report, $operationRun, $operationRunService, $auditLogger, 'source_missing', 'Management PDF source records are unavailable.'); + + return; + } + + $operationRun = $operationRunService->updateRun($operationRun, OperationRunStatus::Running->value, OperationRunOutcome::Pending->value, [ + 'total' => 1, + 'processed' => 0, + 'succeeded' => 0, + 'failed' => 0, + ]); + + $report->update([ + 'status' => StoredReport::STATUS_GENERATING, + 'payload' => array_replace_recursive(is_array($report->payload) ? $report->payload : [], [ + 'state' => StoredReport::STATUS_GENERATING, + 'started_at' => now()->toIso8601String(), + ]), + ]); + + try { + try { + $payload = $payloadBuilder->build($reviewPack); + } catch (InvalidArgumentException $exception) { + $this->markBlocked( + report: $report, + operationRun: $operationRun, + operationRunService: $operationRunService, + auditLogger: $auditLogger, + code: $this->blockedReasonCode($reviewPack, $exception), + message: $exception->getMessage(), + ); + + return; + } + + $result = $renderer->render($payload, $this->correlationId($report, $operationRun)); + + if ($result->failed() || $result->pdfBytes === null) { + $this->markFailed( + report: $report, + operationRun: $operationRun, + operationRunService: $operationRunService, + auditLogger: $auditLogger, + code: $result->failureCode ?? 'renderer_failed', + message: $result->safeMessage ?? 'PDF renderer failed to create the document.', + payload: $payload, + ); + + return; + } + + $this->storePdf($report, $operationRun, $tenant, $payload, $result->pdfBytes, $operationRunService, $auditLogger); + } catch (Throwable $exception) { + $this->markFailed( + report: $report, + operationRun: $operationRun, + operationRunService: $operationRunService, + auditLogger: $auditLogger, + code: 'generation_exception', + message: 'Management report PDF generation failed.', + ); + + throw $exception; + } + } + + /** + * @param array $payload + */ + private function storePdf( + StoredReport $report, + OperationRun $operationRun, + ManagedEnvironment $tenant, + array $payload, + string $pdfBytes, + OperationRunService $operationRunService, + WorkspaceAuditLogger $auditLogger, + ): void { + $filePath = sprintf( + 'management-reports/%s/review-pack-%d-%s-%d.pdf', + $this->safePathSegment((string) ($tenant->external_id ?: $tenant->getKey())), + (int) $report->source_review_pack_id, + now()->format('YmdHis'), + (int) $report->getKey(), + ); + + try { + $stored = Storage::disk('exports')->put($filePath, $pdfBytes); + } catch (Throwable $exception) { + $this->markFailed( + report: $report, + operationRun: $operationRun, + operationRunService: $operationRunService, + auditLogger: $auditLogger, + code: 'storage_failed', + message: 'Management report PDF storage failed.', + payload: $payload, + ); + + throw $exception; + } + + if (! $stored) { + $this->markFailed( + report: $report, + operationRun: $operationRun, + operationRunService: $operationRunService, + auditLogger: $auditLogger, + code: 'storage_failed', + message: 'Management report PDF storage failed.', + payload: $payload, + ); + + return; + } + + $sha256 = hash('sha256', $pdfBytes); + + $report->update([ + 'status' => StoredReport::STATUS_READY, + 'payload' => array_replace_recursive($payload, [ + 'state' => StoredReport::STATUS_READY, + 'artifact' => [ + 'file_disk' => 'exports', + 'file_path' => $filePath, + 'file_size' => strlen($pdfBytes), + 'sha256' => $sha256, + ], + ]), + 'file_disk' => 'exports', + 'file_path' => $filePath, + 'file_size' => strlen($pdfBytes), + 'sha256' => $sha256, + 'generated_at' => now(), + ]); + + $operationRunService->updateRun( + $operationRun, + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::Succeeded->value, + summaryCounts: [ + 'total' => 1, + 'processed' => 1, + 'succeeded' => 1, + 'failed' => 0, + 'report_created' => 1, + ], + ); + + $this->audit($report->refresh(), $operationRun, $auditLogger, AuditActionId::ManagementReportPdfGenerated, 'success', 'Management report PDF generated.'); + } + + /** + * @param array|null $payload + */ + private function markFailed( + StoredReport $report, + OperationRun $operationRun, + OperationRunService $operationRunService, + WorkspaceAuditLogger $auditLogger, + string $code, + string $message, + ?array $payload = null, + ): void { + $report->update([ + 'status' => StoredReport::STATUS_FAILED, + 'payload' => array_replace_recursive($payload ?? (is_array($report->payload) ? $report->payload : []), [ + 'state' => StoredReport::STATUS_FAILED, + 'failure' => [ + 'code' => $code, + 'message' => $message, + ], + ]), + ]); + + $operationRunService->updateRun( + $operationRun, + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::Failed->value, + summaryCounts: [ + 'total' => 1, + 'processed' => 1, + 'succeeded' => 0, + 'failed' => 1, + ], + failures: [ + [ + 'code' => 'management_report_pdf.'.$code, + 'message' => $message, + ], + ], + ); + + $this->audit($report->refresh(), $operationRun, $auditLogger, AuditActionId::ManagementReportPdfGenerationFailed, 'failed', $message, [ + 'failure_code' => $code, + ]); + } + + private function blockedReasonCode(ReviewPack $reviewPack, InvalidArgumentException $exception): string + { + try { + $decision = ManagementReportPdfPayloadBuilder::customerExecutiveDisclosureDecision($reviewPack); + + if ((bool) ($decision['is_blocked'] ?? false) && filled($decision['reason_code'] ?? null)) { + return (string) $decision['reason_code']; + } + } catch (Throwable) { + // Fall through to message-based source blockers below. + } + + $message = $exception->getMessage(); + + return match (true) { + str_contains($message, 'current review pack') => 'review_pack_not_current', + str_contains($message, 'tenant, workspace, and released review') => 'source_missing', + str_contains($message, 'customer executive profile') => 'management_report_pdf_profile_invalid', + str_contains($message, 'disclosure policy') => 'disclosure_blocked', + default => 'generation_blocked', + }; + } + + /** + * @param array|null $payload + */ + private function markBlocked( + StoredReport $report, + OperationRun $operationRun, + OperationRunService $operationRunService, + WorkspaceAuditLogger $auditLogger, + string $code, + string $message, + ?array $payload = null, + ): void { + $report->update([ + 'status' => StoredReport::STATUS_FAILED, + 'payload' => array_replace_recursive($payload ?? (is_array($report->payload) ? $report->payload : []), [ + 'state' => StoredReport::STATUS_FAILED, + 'blocked' => true, + 'failure' => [ + 'code' => $code, + 'message' => $message, + ], + ]), + ]); + + $operationRunService->updateRun( + $operationRun, + status: OperationRunStatus::Completed->value, + outcome: OperationRunOutcome::Blocked->value, + summaryCounts: [ + 'total' => 1, + 'processed' => 1, + 'succeeded' => 0, + 'failed' => 0, + ], + failures: [ + [ + 'code' => 'management_report_pdf.'.$code, + 'reason_code' => $code, + 'message' => $message, + ], + ], + ); + + $this->audit($report->refresh(), $operationRun, $auditLogger, AuditActionId::ManagementReportPdfGenerationBlocked, 'blocked', $message, [ + 'failure_code' => $code, + 'reason_code' => $code, + ]); + } + + /** + * @param array $extraContext + */ + private function audit( + StoredReport $report, + OperationRun $operationRun, + WorkspaceAuditLogger $auditLogger, + AuditActionId $action, + string $status, + string $summary, + array $extraContext = [], + ): void { + $tenant = $report->tenant; + + if (! $tenant instanceof ManagedEnvironment) { + return; + } + + $actor = $report->generatedBy; + + $auditLogger->log( + workspace: $tenant->workspace, + action: $action, + context: array_replace_recursive([ + 'metadata' => [ + 'stored_report_id' => (int) $report->getKey(), + 'review_pack_id' => $report->source_review_pack_id !== null ? (int) $report->source_review_pack_id : null, + 'environment_review_id' => $report->source_environment_review_id !== null ? (int) $report->source_environment_review_id : null, + 'profile' => (string) $report->profile, + 'sha256' => $report->sha256, + ], + ], $extraContext), + actor: $actor instanceof User ? $actor : null, + status: $status, + resourceType: 'stored_report', + resourceId: (string) $report->getKey(), + targetLabel: sprintf('Management report PDF #%d', (int) $report->getKey()), + summary: $summary, + operationRunId: (int) $operationRun->getKey(), + tenant: $tenant, + ); + } + + private function correlationId(StoredReport $report, OperationRun $operationRun): string + { + return sprintf('management-report-pdf-%d-run-%d', (int) $report->getKey(), (int) $operationRun->getKey()); + } + + private function safePathSegment(string $value): string + { + $value = preg_replace('/[^A-Za-z0-9._-]+/', '-', trim($value)) ?? ''; + + return trim($value, '-') !== '' ? trim($value, '-') : 'unknown'; + } +} diff --git a/apps/platform/app/Models/StoredReport.php b/apps/platform/app/Models/StoredReport.php index 57875263..7dfa8f37 100644 --- a/apps/platform/app/Models/StoredReport.php +++ b/apps/platform/app/Models/StoredReport.php @@ -4,10 +4,10 @@ namespace App\Models; -use App\Support\Concerns\DerivesWorkspaceIdFromTenant; use App\Support\Artifacts\ArtifactProviderDetail; use App\Support\Artifacts\ArtifactSourceDescriptor; use App\Support\Artifacts\ArtifactSourceResolver; +use App\Support\Concerns\DerivesWorkspaceIdFromTenant; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -21,13 +21,39 @@ class StoredReport extends Model public const string REPORT_TYPE_ENTRA_ADMIN_ROLES = 'entra.admin_roles'; + public const string REPORT_TYPE_MANAGEMENT_REPORT_PDF = 'management_report.pdf'; + + public const string FORMAT_JSON = 'json'; + + public const string FORMAT_PDF = 'pdf'; + + public const string STATUS_QUEUED = 'queued'; + + public const string STATUS_GENERATING = 'generating'; + + public const string STATUS_READY = 'ready'; + + public const string STATUS_FAILED = 'failed'; + protected $fillable = [ 'workspace_id', 'managed_environment_id', + 'source_environment_review_id', + 'source_review_pack_id', + 'operation_run_id', + 'generated_by_user_id', 'report_type', + 'report_format', + 'status', + 'profile', 'payload', 'fingerprint', 'previous_fingerprint', + 'file_disk', + 'file_path', + 'file_size', + 'sha256', + 'generated_at', ]; /** @@ -37,6 +63,8 @@ protected function casts(): array { return [ 'payload' => 'array', + 'file_size' => 'integer', + 'generated_at' => 'datetime', ]; } @@ -50,6 +78,35 @@ public function tenant(): BelongsTo return $this->belongsTo(ManagedEnvironment::class, 'managed_environment_id'); } + public function sourceEnvironmentReview(): BelongsTo + { + return $this->belongsTo(EnvironmentReview::class, 'source_environment_review_id'); + } + + public function sourceReviewPack(): BelongsTo + { + return $this->belongsTo(ReviewPack::class, 'source_review_pack_id'); + } + + public function operationRun(): BelongsTo + { + return $this->belongsTo(OperationRun::class); + } + + public function generatedBy(): BelongsTo + { + return $this->belongsTo(User::class, 'generated_by_user_id'); + } + + public function isReadyManagementPdf(): bool + { + return $this->report_type === self::REPORT_TYPE_MANAGEMENT_REPORT_PDF + && $this->report_format === self::FORMAT_PDF + && $this->status === self::STATUS_READY + && filled($this->file_disk) + && filled($this->file_path); + } + public function artifactSourceDescriptor(): ArtifactSourceDescriptor { return app(ArtifactSourceResolver::class)->forStoredReport($this); diff --git a/apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php b/apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php index 284603ea..5fe6f594 100644 --- a/apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php +++ b/apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php @@ -6,8 +6,13 @@ use App\Models\Finding; use App\Models\ManagedEnvironment; +use App\Models\OperationRun; use App\Services\Evidence\Contracts\EvidenceSourceProvider; use App\Support\Evidence\EvidenceCompletenessState; +use App\Support\OperationCatalog; +use App\Support\OperationRunOutcome; +use App\Support\OperationRunStatus; +use App\Support\OperationRunType; final class BaselineDriftPostureSource implements EvidenceSourceProvider { @@ -24,12 +29,17 @@ public function collect(ManagedEnvironment $tenant): array ->latest('updated_at') ->get(); - $latest = $findings->max('updated_at') ?? $findings->max('created_at'); + $latestCompareRun = $this->latestBaselineCompareRun($tenant); + $latestCompareAt = $latestCompareRun?->completed_at + ?? $latestCompareRun?->updated_at + ?? $latestCompareRun?->created_at; + $latest = $findings->max('updated_at') ?? $findings->max('created_at') ?? $latestCompareAt; $isStale = $latest !== null && $latest->lt(now()->subDays(30)); $state = match (true) { - $findings->isEmpty() => EvidenceCompletenessState::Missing->value, $isStale => EvidenceCompletenessState::Stale->value, + $latestCompareRun instanceof OperationRun => $this->stateForCompareRun($latestCompareRun), + $findings->isEmpty() => EvidenceCompletenessState::Missing->value, default => EvidenceCompletenessState::Complete->value, }; @@ -46,12 +56,42 @@ public function collect(ManagedEnvironment $tenant): array 'summary_payload' => [ 'drift_count' => $findings->count(), 'open_drift_count' => $findings->filter(fn (Finding $finding): bool => $finding->hasOpenStatus())->count(), + 'latest_compare_run_id' => $latestCompareRun instanceof OperationRun ? (int) $latestCompareRun->getKey() : null, + 'latest_compare_outcome' => $latestCompareRun instanceof OperationRun ? (string) $latestCompareRun->outcome : null, + 'latest_compare_completed_at' => $latestCompareRun?->completed_at?->toIso8601String(), ], 'fingerprint_payload' => [ 'latest' => $latest?->format(DATE_ATOM), 'count' => $findings->count(), + 'latest_compare_run_id' => $latestCompareRun instanceof OperationRun ? (int) $latestCompareRun->getKey() : null, + 'latest_compare_outcome' => $latestCompareRun instanceof OperationRun ? (string) $latestCompareRun->outcome : null, + 'latest_compare_completed_at' => $latestCompareRun?->completed_at?->toIso8601String(), ], 'sort_order' => 40, ]; } + + private function latestBaselineCompareRun(ManagedEnvironment $tenant): ?OperationRun + { + return OperationRun::query() + ->where('managed_environment_id', (int) $tenant->getKey()) + ->whereIn('type', OperationCatalog::rawValuesForCanonical(OperationRunType::BaselineCompare->value)) + ->latest('completed_at') + ->latest('updated_at') + ->latest('id') + ->first(); + } + + private function stateForCompareRun(OperationRun $operationRun): string + { + if ((string) $operationRun->status !== OperationRunStatus::Completed->value) { + return EvidenceCompletenessState::Missing->value; + } + + return match ((string) $operationRun->outcome) { + OperationRunOutcome::Succeeded->value => EvidenceCompletenessState::Complete->value, + OperationRunOutcome::PartiallySucceeded->value => EvidenceCompletenessState::Partial->value, + default => EvidenceCompletenessState::Missing->value, + }; + } } diff --git a/apps/platform/app/Services/ReviewPacks/ManagementReportPdfRenderer.php b/apps/platform/app/Services/ReviewPacks/ManagementReportPdfRenderer.php new file mode 100644 index 00000000..4a430527 --- /dev/null +++ b/apps/platform/app/Services/ReviewPacks/ManagementReportPdfRenderer.php @@ -0,0 +1,155 @@ + $payload + */ + public function render(array $payload, string $correlationId): PdfRenderResult + { + return $this->pdfRenderingGateway->renderHtml(new PdfRenderRequest( + html: $this->html($payload), + options: [ + 'printBackground' => true, + 'preferCssPageSize' => true, + 'displayHeaderFooter' => true, + 'headerTemplate' => '
TenantPilot Management Report
', + 'footerTemplate' => '
/
', + ], + correlationId: $correlationId, + outputFilename: 'tenantpilot-management-report', + )); + } + + /** + * @param array $payload + */ + private function html(array $payload): string + { + $title = e((string) ($payload['title'] ?? 'TenantPilot Management Report')); + $environment = e((string) data_get($payload, 'managed_environment.name', 'Managed environment')); + $profile = e((string) ($payload['profile_label'] ?? 'Customer executive')); + $classification = e((string) ($payload['classification'] ?? 'Customer-facing management report')); + $generatedAt = e((string) data_get($payload, 'provenance.generated_at', now()->toIso8601String())); + $chapters = is_array($payload['chapters'] ?? null) ? $payload['chapters'] : []; + $chapterHtml = collect($chapters) + ->filter(static fn (mixed $chapter): bool => is_array($chapter)) + ->map(fn (array $chapter): string => $this->chapterHtml($chapter)) + ->implode("\n"); + + return << + + + + {$title} + + + +
+

{$title}

+

{$environment}

+

Profile: {$profile} · Classification: {$classification} · Generated: {$generatedAt}

+
+ {$chapterHtml} + + +HTML; + } + + /** + * @param array $chapter + */ + private function chapterHtml(array $chapter): string + { + $title = e((string) ($chapter['title'] ?? 'Chapter')); + $content = is_array($chapter['content'] ?? null) ? $chapter['content'] : []; + $contentHtml = $this->contentHtml($content); + + return << +

{$title}

+ {$contentHtml} + +HTML; + } + + /** + * @param array $content + */ + private function contentHtml(array $content): string + { + if ($content === []) { + return '

No items recorded.

'; + } + + $html = ''; + + foreach ($content as $key => $value) { + if (is_array($value)) { + $html .= '

'.e($this->humanize((string) $key)).'

'.$this->arrayHtml($value); + } elseif ($value !== null && trim((string) $value) !== '') { + $html .= '
'.e($this->humanize((string) $key)).'
'.e((string) $value).'
'; + } + } + + return $html !== '' ? $html : '

No items recorded.

'; + } + + /** + * @param array $items + */ + private function arrayHtml(array $items): string + { + if ($items === []) { + return '

No items recorded.

'; + } + + $listItems = collect($items) + ->map(function (mixed $item): string { + if (is_array($item)) { + $parts = collect($item) + ->map(fn (mixed $value, string|int $key): string => ''.e($this->humanize((string) $key)).': '.e((string) $value)) + ->implode('
'); + + return '
  • '.$parts.'
  • '; + } + + return '
  • '.e((string) $item).'
  • '; + }) + ->implode(''); + + return '
      '.$listItems.'
    '; + } + + private function humanize(string $value): string + { + return ucfirst(str_replace('_', ' ', $value)); + } +} diff --git a/apps/platform/app/Services/ReviewPacks/ManagementReportPdfService.php b/apps/platform/app/Services/ReviewPacks/ManagementReportPdfService.php new file mode 100644 index 00000000..64cf23bc --- /dev/null +++ b/apps/platform/app/Services/ReviewPacks/ManagementReportPdfService.php @@ -0,0 +1,413 @@ + + */ + public function generationDecision(ReviewPack $reviewPack): array + { + $readyReport = $this->findReadyReport($reviewPack); + + if ($readyReport instanceof StoredReport) { + return [ + 'state' => 'ready', + 'is_blocked' => false, + 'reason_code' => null, + 'reason' => null, + 'report_id' => (int) $readyReport->getKey(), + ]; + } + + $reviewPack->loadMissing(['tenant', 'environmentReview.currentExportReviewPack']); + + if ($reviewPack->status !== ReviewPackStatus::Ready->value) { + return $this->blocked('review_pack_not_ready', 'Management PDF generation requires a ready Review Pack.'); + } + + if ($reviewPack->expires_at !== null && $reviewPack->expires_at->isPast()) { + return $this->blocked('review_pack_expired', 'Management PDF generation requires a non-expired Review Pack.'); + } + + if (! filled($reviewPack->file_disk) || ! filled($reviewPack->file_path)) { + return $this->blocked('source_artifact_missing', 'Management PDF generation requires the Review Pack artifact to be present.'); + } + + if (! Storage::disk((string) $reviewPack->file_disk)->exists((string) $reviewPack->file_path)) { + return $this->blocked('source_artifact_missing', 'Management PDF generation requires the Review Pack artifact to be present.'); + } + + $review = $reviewPack->environmentReview; + + if (! $review || (int) ($review->current_export_review_pack_id ?? 0) !== (int) $reviewPack->getKey()) { + return $this->blocked('review_pack_not_current', 'Management PDF generation requires the current Review Pack for the released review.'); + } + + $activeReport = $this->findActiveReport($reviewPack); + + if ($activeReport instanceof StoredReport) { + return [ + 'state' => 'active', + 'is_blocked' => false, + 'reason_code' => null, + 'reason' => null, + 'report_id' => (int) $activeReport->getKey(), + 'operation_run_id' => $activeReport->operation_run_id !== null ? (int) $activeReport->operation_run_id : null, + ]; + } + + $runtimeDecision = $this->runtimeGate->decision(); + + if ((bool) ($runtimeDecision['is_blocked'] ?? false)) { + return [ + 'state' => 'blocked', + 'is_blocked' => true, + 'reason_code' => $runtimeDecision['reason_code'] ?? 'runtime_blocked', + 'reason' => $runtimeDecision['reason'] ?? 'Management PDF generation is blocked by runtime configuration.', + 'runtime' => $runtimeDecision, + ]; + } + + $disclosureDecision = ManagementReportPdfPayloadBuilder::customerExecutiveDisclosureDecision($reviewPack); + + if ((bool) ($disclosureDecision['is_blocked'] ?? false)) { + return [ + 'state' => 'blocked', + 'is_blocked' => true, + 'reason_code' => $disclosureDecision['reason_code'] ?? 'disclosure_blocked', + 'reason' => $disclosureDecision['reason'] ?? 'Management PDF generation is blocked by the customer-facing disclosure policy.', + 'runtime' => $runtimeDecision, + 'disclosure' => $disclosureDecision['disclosure'] ?? [], + 'readiness' => $disclosureDecision['readiness'] ?? [], + ]; + } + + return [ + 'state' => 'available', + 'is_blocked' => false, + 'reason_code' => null, + 'reason' => null, + 'runtime' => $runtimeDecision, + 'disclosure' => $disclosureDecision['disclosure'] ?? [], + ]; + } + + /** + * @return array{mode:string, report:?StoredReport, operation_run:?OperationRun, decision:array} + */ + public function startGeneration(ReviewPack $reviewPack, User $actor): array + { + $reviewPack->loadMissing(['tenant.workspace', 'environmentReview']); + $tenant = $reviewPack->tenant; + + if (! $tenant instanceof ManagedEnvironment || ! $actor->canAccessTenant($tenant)) { + throw new NotFoundHttpException; + } + + abort_unless($actor->can(Capabilities::REVIEW_PACK_MANAGE, $tenant), 403); + + $readyReport = $this->findReadyReport($reviewPack); + + if ($readyReport instanceof StoredReport) { + return [ + 'mode' => 'ready', + 'report' => $readyReport, + 'operation_run' => $readyReport->operationRun, + 'decision' => $this->generationDecision($reviewPack), + ]; + } + + $decision = $this->generationDecision($reviewPack); + + if ((bool) ($decision['is_blocked'] ?? false)) { + $this->logLifecycleEvent( + reviewPack: $reviewPack, + actor: $actor, + action: AuditActionId::ManagementReportPdfGenerationBlocked, + status: 'blocked', + mode: 'blocked', + operationRun: null, + report: null, + context: ['decision' => $decision], + ); + + return [ + 'mode' => 'blocked', + 'report' => null, + 'operation_run' => null, + 'decision' => $decision, + ]; + } + + $activeReport = $this->findActiveReport($reviewPack); + + if ($activeReport instanceof StoredReport) { + return [ + 'mode' => 'active', + 'report' => $activeReport, + 'operation_run' => $activeReport->operationRun, + 'decision' => $decision, + ]; + } + + $fingerprint = $this->computeFingerprint($reviewPack); + $retryableReport = $this->findRetryableReport($reviewPack, $fingerprint); + $operationRun = $this->operationRunService->ensureRunWithIdentity( + tenant: $tenant, + type: OperationRunType::ManagementReportGenerate->value, + identityInputs: [ + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'source_review_pack_fingerprint' => (string) $reviewPack->fingerprint, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, + ], + context: [ + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'source_environment_review_id' => $reviewPack->environment_review_id !== null ? (int) $reviewPack->environment_review_id : null, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, + 'runtime_gate' => $decision['runtime'] ?? [], + ], + initiator: $actor, + ); + + $existingRunReport = $this->findReportForRun($operationRun); + $report = $existingRunReport + ?? $retryableReport + ?? StoredReport::create([ + 'workspace_id' => (int) $tenant->workspace_id, + 'managed_environment_id' => (int) $tenant->getKey(), + 'source_environment_review_id' => $reviewPack->environment_review_id !== null ? (int) $reviewPack->environment_review_id : null, + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'operation_run_id' => (int) $operationRun->getKey(), + 'generated_by_user_id' => (int) $actor->getKey(), + 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, + 'report_format' => StoredReport::FORMAT_PDF, + 'status' => StoredReport::STATUS_QUEUED, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'fingerprint' => $fingerprint, + 'payload' => [ + 'state' => StoredReport::STATUS_QUEUED, + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'source_environment_review_id' => $reviewPack->environment_review_id !== null ? (int) $reviewPack->environment_review_id : null, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + ], + ]); + + if ($report->operation_run_id !== (int) $operationRun->getKey() + || $report->status === StoredReport::STATUS_FAILED + ) { + $report->forceFill([ + 'operation_run_id' => (int) $operationRun->getKey(), + 'generated_by_user_id' => (int) $actor->getKey(), + 'status' => StoredReport::STATUS_QUEUED, + 'file_disk' => null, + 'file_path' => null, + 'file_size' => null, + 'sha256' => null, + 'generated_at' => null, + 'payload' => [ + 'state' => StoredReport::STATUS_QUEUED, + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'source_environment_review_id' => $reviewPack->environment_review_id !== null ? (int) $reviewPack->environment_review_id : null, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'retry_of_failed_report' => true, + ], + ])->save(); + } + + $shouldDispatch = $operationRun->wasRecentlyCreated || ! $existingRunReport instanceof StoredReport; + + if ($shouldDispatch) { + $this->operationRunService->dispatchOrFail($operationRun, function () use ($report, $operationRun): void { + GenerateManagementReportPdfJob::dispatch( + storedReportId: (int) $report->getKey(), + operationRunId: (int) $operationRun->getKey(), + ); + }); + } + + $this->logLifecycleEvent( + reviewPack: $reviewPack, + actor: $actor, + action: AuditActionId::ManagementReportPdfGenerationRequested, + status: 'queued', + mode: $shouldDispatch ? 'queued' : 'reused_active_run', + operationRun: $operationRun, + report: $report, + ); + + return [ + 'mode' => $shouldDispatch ? 'queued' : 'active', + 'report' => $report, + 'operation_run' => $operationRun, + 'decision' => $decision, + ]; + } + + /** + * @param array $parameters + */ + public function generateDownloadUrl(StoredReport $report, array $parameters = []): string + { + $ttlMinutes = (int) config('tenantpilot.review_pack.download_url_ttl_minutes', 60); + + return URL::signedRoute( + 'admin.management-report-pdfs.download', + array_merge(['storedReport' => $report->getKey()], $parameters), + now()->addMinutes($ttlMinutes), + ); + } + + public function findReadyReport(ReviewPack $reviewPack): ?StoredReport + { + return StoredReport::query() + ->where('source_review_pack_id', (int) $reviewPack->getKey()) + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->where('report_format', StoredReport::FORMAT_PDF) + ->where('profile', ReportProfileRegistry::CUSTOMER_EXECUTIVE) + ->where('status', StoredReport::STATUS_READY) + ->whereNotNull('file_disk') + ->whereNotNull('file_path') + ->latest('generated_at') + ->latest('id') + ->first(); + } + + public function findActiveReport(ReviewPack $reviewPack): ?StoredReport + { + return StoredReport::query() + ->with('operationRun') + ->where('source_review_pack_id', (int) $reviewPack->getKey()) + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->where('report_format', StoredReport::FORMAT_PDF) + ->where('profile', ReportProfileRegistry::CUSTOMER_EXECUTIVE) + ->whereIn('status', [StoredReport::STATUS_QUEUED, StoredReport::STATUS_GENERATING]) + ->whereHas('operationRun', static function ($query): void { + $query->whereIn('status', [OperationRunStatus::Queued->value, OperationRunStatus::Running->value]); + }) + ->latest('id') + ->first(); + } + + public function findReportForRun(OperationRun $operationRun): ?StoredReport + { + return StoredReport::query() + ->where('operation_run_id', (int) $operationRun->getKey()) + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->latest('id') + ->first(); + } + + private function findRetryableReport(ReviewPack $reviewPack, string $fingerprint): ?StoredReport + { + return StoredReport::query() + ->where('managed_environment_id', (int) $reviewPack->managed_environment_id) + ->where('source_review_pack_id', (int) $reviewPack->getKey()) + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->where('report_format', StoredReport::FORMAT_PDF) + ->where('profile', ReportProfileRegistry::CUSTOMER_EXECUTIVE) + ->where('fingerprint', $fingerprint) + ->where('status', StoredReport::STATUS_FAILED) + ->latest('id') + ->first(); + } + + public function computeFingerprint(ReviewPack $reviewPack): string + { + return hash('sha256', json_encode([ + 'source_review_pack_id' => (int) $reviewPack->getKey(), + 'source_review_pack_fingerprint' => (string) $reviewPack->fingerprint, + 'source_environment_review_id' => $reviewPack->environment_review_id !== null ? (int) $reviewPack->environment_review_id : null, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, + 'report_format' => StoredReport::FORMAT_PDF, + ], JSON_THROW_ON_ERROR)); + } + + /** + * @return array + */ + private function blocked(string $reasonCode, string $reason): array + { + return [ + 'state' => 'blocked', + 'is_blocked' => true, + 'reason_code' => $reasonCode, + 'reason' => $reason, + ]; + } + + /** + * @param array $context + */ + private function logLifecycleEvent( + ReviewPack $reviewPack, + User $actor, + AuditActionId $action, + string $status, + string $mode, + ?OperationRun $operationRun, + ?StoredReport $report, + array $context = [], + ): void { + $tenant = $reviewPack->tenant; + + if (! $tenant instanceof ManagedEnvironment) { + return; + } + + $this->auditLogger->log( + workspace: $tenant->workspace, + action: $action, + context: array_replace_recursive([ + 'metadata' => [ + 'mode' => $mode, + 'review_pack_id' => (int) $reviewPack->getKey(), + 'environment_review_id' => $reviewPack->environment_review_id !== null + ? (int) $reviewPack->environment_review_id + : null, + 'stored_report_id' => $report instanceof StoredReport ? (int) $report->getKey() : null, + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + ], + ], $context), + actor: $actor, + status: $status, + resourceType: 'stored_report', + resourceId: $report instanceof StoredReport ? (string) $report->getKey() : 'review_pack:'.$reviewPack->getKey(), + targetLabel: sprintf('Management report PDF for review pack #%d', (int) $reviewPack->getKey()), + operationRunId: $operationRun instanceof OperationRun ? (int) $operationRun->getKey() : null, + tenant: $tenant, + ); + } +} diff --git a/apps/platform/app/Support/Audit/AuditActionId.php b/apps/platform/app/Support/Audit/AuditActionId.php index e665ddc5..8e799c9d 100644 --- a/apps/platform/app/Support/Audit/AuditActionId.php +++ b/apps/platform/app/Support/Audit/AuditActionId.php @@ -114,6 +114,11 @@ enum AuditActionId: string case EnvironmentReviewSuccessorCreated = 'environment_review.successor_created'; case CustomerReviewWorkspaceOpened = 'customer_review_workspace.opened'; case ReviewPackDownloaded = 'review_pack.downloaded'; + case ManagementReportPdfGenerationRequested = 'management_report_pdf.generation_requested'; + case ManagementReportPdfGenerationBlocked = 'management_report_pdf.generation_blocked'; + case ManagementReportPdfGenerated = 'management_report_pdf.generated'; + case ManagementReportPdfGenerationFailed = 'management_report_pdf.generation_failed'; + case ManagementReportPdfDownloaded = 'management_report_pdf.downloaded'; case ManagedEnvironmentTriageReviewMarkedReviewed = 'managed_environment_triage_review.marked_reviewed'; case ManagedEnvironmentTriageReviewMarkedFollowUpNeeded = 'managed_environment_triage_review.marked_follow_up_needed'; @@ -285,6 +290,11 @@ private static function labels(): array self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created', self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened', self::ReviewPackDownloaded->value => 'Review pack downloaded', + self::ManagementReportPdfGenerationRequested->value => 'Management report PDF generation requested', + self::ManagementReportPdfGenerationBlocked->value => 'Management report PDF generation blocked', + self::ManagementReportPdfGenerated->value => 'Management report PDF generated', + self::ManagementReportPdfGenerationFailed->value => 'Management report PDF generation failed', + self::ManagementReportPdfDownloaded->value => 'Management report PDF downloaded', self::ManagedEnvironmentTriageReviewMarkedReviewed->value => 'Triage review marked reviewed', self::ManagedEnvironmentTriageReviewMarkedFollowUpNeeded->value => 'Triage review marked follow-up needed', self::SupportDiagnosticsOpened->value => 'Support diagnostics opened', @@ -400,6 +410,11 @@ private static function summaries(): array self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created', self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened', self::ReviewPackDownloaded->value => 'Review pack downloaded', + self::ManagementReportPdfGenerationRequested->value => 'Management report PDF generation requested', + self::ManagementReportPdfGenerationBlocked->value => 'Management report PDF generation blocked', + self::ManagementReportPdfGenerated->value => 'Management report PDF generated', + self::ManagementReportPdfGenerationFailed->value => 'Management report PDF generation failed', + self::ManagementReportPdfDownloaded->value => 'Management report PDF downloaded', self::SupportDiagnosticsOpened->value => 'Support diagnostics opened', self::SupportRequestCreated->value => 'Support request created', self::SupportRequestExternalTicketCreated->value => 'Support request external ticket created', diff --git a/apps/platform/app/Support/OperationCatalog.php b/apps/platform/app/Support/OperationCatalog.php index 89cad44a..d050e163 100644 --- a/apps/platform/app/Support/OperationCatalog.php +++ b/apps/platform/app/Support/OperationCatalog.php @@ -276,6 +276,7 @@ private static function canonicalDefinitions(): array 'permission.posture.check' => new CanonicalOperationType('permission.posture.check', 'platform_foundation', null, 'Permission posture check', false, 30), 'entra.admin_roles.scan' => new CanonicalOperationType('entra.admin_roles.scan', 'entra', null, 'Entra admin roles scan', false, 60), 'environment.review_pack.generate' => new CanonicalOperationType('environment.review_pack.generate', 'platform_foundation', 'review_pack', 'Review pack generation', true, 60), + 'report.management.generate' => new CanonicalOperationType('report.management.generate', 'platform_foundation', null, 'Management report PDF generation', true, 60), 'environment.review.compose' => new CanonicalOperationType('environment.review.compose', 'platform_foundation', 'environment_review', 'Review composition', true, 60), 'tenant.evidence.snapshot.generate' => new CanonicalOperationType('tenant.evidence.snapshot.generate', 'platform_foundation', 'evidence_snapshot', 'Evidence snapshot generation', true, 120), 'rbac.health_check' => new CanonicalOperationType('rbac.health_check', 'intune', null, 'RBAC health check', false, 30), @@ -339,6 +340,7 @@ private static function operationAliases(): array new OperationTypeAlias('permission_posture_check', 'permission.posture.check', 'legacy_alias', false, 'Historical permission_posture_check values resolve to permission.posture.check.', 'Prefer dotted permission posture naming on new read paths.'), new OperationTypeAlias('entra.admin_roles.scan', 'entra.admin_roles.scan', 'canonical', true), new OperationTypeAlias('environment.review_pack.generate', 'environment.review_pack.generate', 'canonical', true), + new OperationTypeAlias('report.management.generate', 'report.management.generate', 'canonical', true), new OperationTypeAlias('environment.review.compose', 'environment.review.compose', 'canonical', true), new OperationTypeAlias('tenant.evidence.snapshot.generate', 'tenant.evidence.snapshot.generate', 'canonical', true), new OperationTypeAlias('rbac.health_check', 'rbac.health_check', 'canonical', true), diff --git a/apps/platform/app/Support/OperationRunType.php b/apps/platform/app/Support/OperationRunType.php index 05a95638..06eeb7ec 100644 --- a/apps/platform/app/Support/OperationRunType.php +++ b/apps/platform/app/Support/OperationRunType.php @@ -18,6 +18,7 @@ enum OperationRunType: string case PromotionExecute = 'promotion.execute'; case EntraAdminRolesScan = 'entra.admin_roles.scan'; case ReviewPackGenerate = 'environment.review_pack.generate'; + case ManagementReportGenerate = 'report.management.generate'; case EnvironmentReviewCompose = 'environment.review.compose'; case EvidenceSnapshotGenerate = 'tenant.evidence.snapshot.generate'; case RbacHealthCheck = 'rbac.health_check'; diff --git a/apps/platform/app/Support/ReviewPacks/ManagementReportPdfPayloadBuilder.php b/apps/platform/app/Support/ReviewPacks/ManagementReportPdfPayloadBuilder.php new file mode 100644 index 00000000..5b17a24d --- /dev/null +++ b/apps/platform/app/Support/ReviewPacks/ManagementReportPdfPayloadBuilder.php @@ -0,0 +1,321 @@ + + */ + public function build(ReviewPack $reviewPack): array + { + $reviewPack->loadMissing([ + 'tenant.workspace', + 'environmentReview.sections', + 'environmentReview.evidenceSnapshot', + 'environmentReview.currentExportReviewPack', + ]); + + $tenant = $reviewPack->tenant; + $workspace = $tenant?->workspace; + $review = $reviewPack->environmentReview; + + if (! $tenant instanceof ManagedEnvironment || ! $workspace instanceof Workspace || ! $review instanceof EnvironmentReview) { + throw new InvalidArgumentException('Management report PDF requires a tenant, workspace, and released review.'); + } + + if ((int) ($review->current_export_review_pack_id ?? 0) !== (int) $reviewPack->getKey()) { + throw new InvalidArgumentException('Management report PDF can only be generated from the current review pack.'); + } + + $disclosureDecision = self::customerExecutiveDisclosureDecision($reviewPack); + $profile = is_array($disclosureDecision['profile'] ?? null) ? $disclosureDecision['profile'] : []; + $readiness = is_array($disclosureDecision['readiness'] ?? null) ? $disclosureDecision['readiness'] : []; + $disclosure = is_array($disclosureDecision['disclosure'] ?? null) ? $disclosureDecision['disclosure'] : []; + + if ((string) ($disclosureDecision['reason_code'] ?? '') === 'management_report_pdf_profile_invalid') { + throw new InvalidArgumentException('Management report PDF requires the customer executive profile without fallback.'); + } + + $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness); + + if ((bool) ($disclosureDecision['is_blocked'] ?? false)) { + throw new InvalidArgumentException('Management report PDF generation is blocked by the customer-facing disclosure policy.'); + } + + $payload = [ + 'title' => 'TenantPilot Management Report', + 'report_type' => 'management_report_pdf', + 'profile' => self::PROFILE, + 'profile_label' => (string) ($profile['label'] ?? 'Customer executive'), + 'audience_label' => (string) ($profile['audience_label'] ?? 'Customer executive'), + 'classification' => 'Customer-facing management report', + 'workspace' => [ + 'id' => (int) $workspace->getKey(), + 'name' => (string) $workspace->name, + ], + 'managed_environment' => [ + 'id' => (int) $tenant->getKey(), + 'name' => (string) $tenant->name, + 'external_id' => (string) $tenant->external_id, + ], + 'provenance' => [ + 'environment_review_id' => (int) $review->getKey(), + 'review_pack_id' => (int) $reviewPack->getKey(), + 'review_status' => (string) $review->status, + 'review_fingerprint' => (string) $review->fingerprint, + 'review_pack_fingerprint' => (string) $reviewPack->fingerprint, + 'review_pack_sha256' => (string) $reviewPack->sha256, + 'generated_at' => now()->toIso8601String(), + ], + 'chapters' => [ + $this->chapter('cover', 'Cover', [ + 'environment' => (string) $tenant->name, + 'workspace' => (string) $workspace->name, + 'profile' => (string) ($profile['label'] ?? 'Customer executive'), + 'classification' => 'Customer-facing management report', + 'generated_at' => now()->toFormattedDateString(), + ]), + $this->chapter('executive_summary', 'Executive summary', [ + 'summary' => $this->firstString( + data_get($reviewPack->summary, 'governance_package.executive_summary'), + data_get($review->summary, 'governance_package.executive_summary'), + data_get($review->summary, 'executive_summary'), + __('localization.review.rendered_report_summary_fallback'), + ), + ]), + $this->chapter('governance_posture', 'Governance posture', [ + 'state' => (string) ($guidance['label'] ?? 'Unavailable'), + 'boundary' => (string) ($guidance['boundary_label'] ?? 'Needs review'), + 'reason' => (string) ($guidance['primary_reason'] ?? ''), + 'impact' => (string) ($guidance['impact'] ?? ''), + ]), + $this->chapter('key_decisions', 'Key decisions', [ + 'items' => $this->listItems( + data_get($reviewPack->summary, 'decision_summary.entries') + ?? data_get($review->summary, 'decision_summary.entries') + ?? data_get($review->summary, 'governance_decisions'), + ['title', 'label', 'decision', 'summary', 'rationale'], + ), + ]), + $this->chapter('top_risks', 'Top risks and findings', [ + 'items' => $this->listItems( + data_get($reviewPack->summary, 'top_findings') + ?? data_get($review->summary, 'top_findings') + ?? data_get($review->summary, 'finding_report_buckets.high'), + ['title', 'label', 'severity', 'summary', 'recommendation'], + 5, + ), + ]), + $this->chapter('accepted_risks', 'Accepted risks', [ + 'items' => $this->listItems( + data_get($reviewPack->summary, 'risk_acceptance') + ?? data_get($review->summary, 'risk_acceptance'), + ['title', 'label', 'risk', 'summary', 'accepted_until'], + 5, + ), + ]), + $this->chapter('evidence_basis', 'Evidence basis', [ + 'evidence_snapshot_id' => $review->evidence_snapshot_id !== null ? (int) $review->evidence_snapshot_id : null, + 'evidence_completeness' => (string) ($readiness['evidence_completeness_state'] ?? 'unknown'), + 'review_pack_generated_at' => $reviewPack->generated_at?->toIso8601String(), + 'review_pack_sha256' => (string) $reviewPack->sha256, + ]), + $this->chapter('limitations', 'Limitations and disclosures', [ + 'limitations' => $this->listItems($guidance['limitations'] ?? [], ['label', 'reason', 'severity']), + 'warnings' => $this->listItems($disclosure['warnings'], ['label', 'summary']), + 'mandatory_disclosures' => $this->listItems($disclosure['mandatory_disclosures'], ['label', 'summary', 'proof_state']), + ]), + $this->chapter('next_actions', 'Next actions', [ + 'items' => $this->listItems( + data_get($reviewPack->summary, 'recommended_next_actions') + ?? data_get($review->summary, 'recommended_next_actions') + ?? [], + ['title', 'label', 'summary', 'owner', 'due'], + ), + ]), + $this->chapter('method_summary', 'Method summary', [ + 'summary' => 'Generated from the current customer-safe Review Pack and released review state. Raw evidence payloads, secrets, and Graph API responses are not included in this management PDF.', + ]), + ], + ]; + + return $this->sanitize($payload); + } + + /** + * @return array{ + * is_blocked: bool, + * reason_code: ?string, + * reason: ?string, + * profile: array, + * readiness: array, + * disclosure: array + * } + */ + public static function customerExecutiveDisclosureDecision(ReviewPack $reviewPack): array + { + $reviewPack->loadMissing([ + 'environmentReview.sections', + 'environmentReview.evidenceSnapshot', + 'environmentReview.currentExportReviewPack', + ]); + + $review = $reviewPack->environmentReview; + + if (! $review instanceof EnvironmentReview) { + throw new InvalidArgumentException('Management report PDF requires a released review.'); + } + + $profile = ReportProfileRegistry::resolve(self::PROFILE, self::PROFILE); + $readiness = ReviewPackOutputResolutionGuidance::readinessForReview($review); + + if ((string) ($profile['effective_key'] ?? '') !== self::PROFILE || (bool) ($profile['is_fallback'] ?? false)) { + return [ + 'is_blocked' => true, + 'reason_code' => 'management_report_pdf_profile_invalid', + 'reason' => 'Management report PDF requires the customer executive profile without fallback.', + 'profile' => $profile, + 'readiness' => $readiness, + 'disclosure' => [ + 'blocking_reasons' => [], + 'warnings' => [], + 'mandatory_disclosures' => [], + 'proof_states' => [], + ], + ]; + } + + $metadata = [ + 'non_certification_disclosure' => data_get($reviewPack->summary, 'control_interpretation.non_certification_disclosure') + ?? data_get($review->summary, 'control_interpretation.non_certification_disclosure'), + ]; + $disclosure = ReportDisclosurePolicy::evaluate($profile, $readiness, $metadata); + $blockingReason = $disclosure['blocking_reasons'][0] ?? null; + + if (is_array($blockingReason)) { + $label = trim((string) ($blockingReason['label'] ?? '')); + $summary = trim((string) ($blockingReason['summary'] ?? '')); + + return [ + 'is_blocked' => true, + 'reason_code' => (string) ($blockingReason['key'] ?? 'disclosure_blocked'), + 'reason' => $label !== '' && $summary !== '' ? "{$label}: {$summary}" : ($summary !== '' ? $summary : $label), + 'profile' => $profile, + 'readiness' => $readiness, + 'disclosure' => $disclosure, + ]; + } + + return [ + 'is_blocked' => false, + 'reason_code' => null, + 'reason' => null, + 'profile' => $profile, + 'readiness' => $readiness, + 'disclosure' => $disclosure, + ]; + } + + /** + * @param array $content + * @return array{key:string,title:string,content:array} + */ + private function chapter(string $key, string $title, array $content): array + { + return compact('key', 'title', 'content'); + } + + private function firstString(mixed ...$values): string + { + foreach ($values as $value) { + if (is_scalar($value) && trim((string) $value) !== '') { + return trim((string) $value); + } + } + + return ''; + } + + /** + * @param list $fields + * @return list> + */ + private function listItems(mixed $items, array $fields, int $limit = 8): array + { + if (! is_iterable($items)) { + return []; + } + + $normalized = []; + + foreach ($items as $item) { + if (is_scalar($item)) { + $normalized[] = ['summary' => trim((string) $item)]; + } elseif (is_array($item)) { + $entry = []; + + foreach ($fields as $field) { + $value = data_get($item, $field); + + if (is_scalar($value) && trim((string) $value) !== '') { + $entry[$field] = trim((string) $value); + } + } + + if ($entry !== []) { + $normalized[] = $entry; + } + } + + if (count($normalized) >= $limit) { + break; + } + } + + return $normalized; + } + + private function sanitize(mixed $value): mixed + { + if (is_array($value)) { + $sanitized = []; + + foreach ($value as $key => $item) { + $sanitized[$key] = $this->sanitize($item); + } + + return $sanitized; + } + + if (! is_scalar($value) && $value !== null) { + return null; + } + + $text = preg_replace('/\s+/', ' ', trim((string) $value)); + + if (! is_string($text)) { + return null; + } + + if ($text === '') { + return $text; + } + + if (preg_match('/SQLSTATE|Bearer\s+|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|password/i', $text) === 1) { + return '[redacted]'; + } + + return mb_substr(strip_tags($text), 0, 1200); + } +} diff --git a/apps/platform/app/Support/ReviewPacks/ManagementReportPdfRuntimeGate.php b/apps/platform/app/Support/ReviewPacks/ManagementReportPdfRuntimeGate.php new file mode 100644 index 00000000..a73c63fb --- /dev/null +++ b/apps/platform/app/Support/ReviewPacks/ManagementReportPdfRuntimeGate.php @@ -0,0 +1,68 @@ +blocked($enabled, $driver, $runtimeValidated, 'renderer_disabled', 'PDF rendering is disabled for this environment.'); + } + + if ($driver !== 'gotenberg') { + return $this->blocked($enabled, $driver, $runtimeValidated, 'renderer_driver_unsupported', 'The configured PDF renderer driver is not supported.'); + } + + if (! $runtimeValidated) { + return $this->blocked($enabled, $driver, $runtimeValidated, 'runtime_validation_missing', 'Management PDF generation is blocked until the Gotenberg runtime is validated on Staging.'); + } + + return [ + 'enabled' => $enabled, + 'driver' => $driver, + 'runtime_validated' => $runtimeValidated, + 'is_blocked' => false, + 'reason_code' => null, + 'reason' => null, + ]; + } + + /** + * @return array{ + * enabled: bool, + * driver: string, + * runtime_validated: bool, + * is_blocked: bool, + * reason_code: string, + * reason: string + * } + */ + private function blocked(bool $enabled, string $driver, bool $runtimeValidated, string $reasonCode, string $reason): array + { + return [ + 'enabled' => $enabled, + 'driver' => $driver, + 'runtime_validated' => $runtimeValidated, + 'is_blocked' => true, + 'reason_code' => $reasonCode, + 'reason' => $reason, + ]; + } +} diff --git a/apps/platform/config/tenantpilot.php b/apps/platform/config/tenantpilot.php index bc8a1438..db436ff5 100644 --- a/apps/platform/config/tenantpilot.php +++ b/apps/platform/config/tenantpilot.php @@ -12,6 +12,7 @@ 'pdf_renderer' => [ 'enabled' => (bool) env('TENANTPILOT_PDF_RENDERER_ENABLED', false), + 'runtime_validated' => (bool) env('TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED', false), 'driver' => env('TENANTPILOT_PDF_RENDERER_DRIVER', 'gotenberg'), 'base_url' => env('TENANTPILOT_PDF_RENDERER_BASE_URL', 'http://gotenberg:3000'), 'health_path' => env('TENANTPILOT_PDF_RENDERER_HEALTH_PATH', '/health'), @@ -127,6 +128,14 @@ 'direct_failed_bridge' => false, 'scheduled_reconciliation' => true, ], + 'report.management.generate' => [ + 'job_class' => \App\Jobs\GenerateManagementReportPdfJob::class, + 'queued_stale_after_seconds' => 300, + 'running_stale_after_seconds' => 900, + 'expected_max_runtime_seconds' => 240, + 'direct_failed_bridge' => false, + 'scheduled_reconciliation' => true, + ], 'environment.review.compose' => [ 'job_class' => \App\Jobs\ComposeEnvironmentReviewJob::class, 'queued_stale_after_seconds' => 300, diff --git a/apps/platform/database/factories/StoredReportFactory.php b/apps/platform/database/factories/StoredReportFactory.php index e40784c2..9e878174 100644 --- a/apps/platform/database/factories/StoredReportFactory.php +++ b/apps/platform/database/factories/StoredReportFactory.php @@ -4,8 +4,8 @@ namespace Database\Factories; -use App\Models\StoredReport; use App\Models\ManagedEnvironment; +use App\Models\StoredReport; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -107,4 +107,30 @@ public function entraAdminRoles(array $payload = []): self ], $payload), ]); } + + /** + * @param array $payload + */ + public function managementReportPdf(array $payload = []): self + { + return $this->state(fn (): array => [ + 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, + 'report_format' => StoredReport::FORMAT_PDF, + 'status' => StoredReport::STATUS_READY, + 'profile' => 'customer_executive', + 'payload' => array_replace_recursive([ + 'title' => 'Management report', + 'profile' => 'customer_executive', + 'chapters' => [], + 'provenance' => [ + 'generated_at' => now()->toIso8601String(), + ], + ], $payload), + 'file_disk' => 'exports', + 'file_path' => 'management-reports/test/report.pdf', + 'file_size' => 128, + 'sha256' => hash('sha256', '%PDF-1.7 test'), + 'generated_at' => now(), + ]); + } } diff --git a/apps/platform/database/migrations/2026_06_14_000379_add_management_pdf_artifacts_to_stored_reports_table.php b/apps/platform/database/migrations/2026_06_14_000379_add_management_pdf_artifacts_to_stored_reports_table.php new file mode 100644 index 00000000..d53f79c4 --- /dev/null +++ b/apps/platform/database/migrations/2026_06_14_000379_add_management_pdf_artifacts_to_stored_reports_table.php @@ -0,0 +1,58 @@ +foreignId('source_environment_review_id')->nullable()->constrained('environment_reviews')->nullOnDelete(); + $table->foreignId('source_review_pack_id')->nullable()->constrained('review_packs')->nullOnDelete(); + $table->foreignId('operation_run_id')->nullable()->constrained('operation_runs')->nullOnDelete(); + $table->foreignId('generated_by_user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->string('report_format')->default('json'); + $table->string('status')->default('ready'); + $table->string('profile')->nullable(); + $table->string('file_disk')->nullable(); + $table->string('file_path')->nullable(); + $table->unsignedBigInteger('file_size')->nullable(); + $table->string('sha256', 64)->nullable(); + $table->timestamp('generated_at')->nullable(); + + $table->index( + ['workspace_id', 'managed_environment_id', 'report_type', 'report_format', 'status'], + 'stored_reports_management_pdf_lookup_idx', + ); + $table->index('source_review_pack_id', 'stored_reports_source_review_pack_idx'); + $table->index('source_environment_review_id', 'stored_reports_source_review_idx'); + $table->index('operation_run_id', 'stored_reports_operation_run_idx'); + }); + } + + public function down(): void + { + Schema::table('stored_reports', function (Blueprint $table): void { + $table->dropIndex('stored_reports_management_pdf_lookup_idx'); + $table->dropIndex('stored_reports_source_review_pack_idx'); + $table->dropIndex('stored_reports_source_review_idx'); + $table->dropIndex('stored_reports_operation_run_idx'); + $table->dropConstrainedForeignId('source_environment_review_id'); + $table->dropConstrainedForeignId('source_review_pack_id'); + $table->dropConstrainedForeignId('operation_run_id'); + $table->dropConstrainedForeignId('generated_by_user_id'); + $table->dropColumn([ + 'report_format', + 'status', + 'profile', + 'file_disk', + 'file_path', + 'file_size', + 'sha256', + 'generated_at', + ]); + }); + } +}; diff --git a/apps/platform/lang/de/localization.php b/apps/platform/lang/de/localization.php index 50f2de2b..f8d46620 100644 --- a/apps/platform/lang/de/localization.php +++ b/apps/platform/lang/de/localization.php @@ -735,6 +735,15 @@ 'download_internal_review_pack' => 'Internes Review-Paket herunterladen', 'download_current_review_pack' => 'Aktuelles Review-Pack herunterladen', 'download_governance_package' => 'Governance-Paket herunterladen', + 'download_management_report_pdf' => 'Management-PDF herunterladen', + 'generate_management_report_pdf' => 'Management-PDF erzeugen', + 'generate_management_report_pdf_confirmation' => 'Erzeugt ein Customer-Executive-Management-PDF aus dem aktuellen Review-Pack. Das Quell-ZIP bleibt unverändert und das PDF wird als privates auditiertes Artefakt gespeichert.', + 'generate_management_report_pdf_submit' => 'PDF erzeugen', + 'open_management_report_pdf_operation' => 'PDF-Operation öffnen', + 'management_report_pdf_blocked' => 'Management-PDF nicht verfügbar', + 'management_report_pdf_blocked_default' => 'Die Management-PDF-Erzeugung ist für dieses Review-Pack nicht verfügbar.', + 'management_report_pdf_ready' => 'Management-PDF bereit', + 'management_report_pdf_ready_body' => 'Für dieses Review-Pack existiert bereits ein bereites Management-PDF.', 'review_package_contents' => 'Paketinhalt prüfen', 'review_output_limitations' => 'Output-Einschränkungen prüfen', 'review_limitations_below' => 'Einschränkungen unten prüfen', diff --git a/apps/platform/lang/en/localization.php b/apps/platform/lang/en/localization.php index acee12b1..e570d3cd 100644 --- a/apps/platform/lang/en/localization.php +++ b/apps/platform/lang/en/localization.php @@ -735,6 +735,15 @@ 'download_internal_review_pack' => 'Download internal review pack', 'download_current_review_pack' => 'Download current review pack', 'download_governance_package' => 'Download governance package', + 'download_management_report_pdf' => 'Download management PDF', + 'generate_management_report_pdf' => 'Generate management PDF', + 'generate_management_report_pdf_confirmation' => 'Generate a customer-executive management PDF from the current Review Pack. The source ZIP remains unchanged and the PDF is stored as a private audited artifact.', + 'generate_management_report_pdf_submit' => 'Generate PDF', + 'open_management_report_pdf_operation' => 'Open PDF operation', + 'management_report_pdf_blocked' => 'Management PDF unavailable', + 'management_report_pdf_blocked_default' => 'Management PDF generation is not available for this Review Pack.', + 'management_report_pdf_ready' => 'Management PDF ready', + 'management_report_pdf_ready_body' => 'A ready management PDF already exists for this Review Pack.', 'review_package_contents' => 'Review package contents', 'review_output_limitations' => 'Review output limitations', 'review_limitations_below' => 'Review limitations below', diff --git a/apps/platform/routes/web.php b/apps/platform/routes/web.php index 60593924..112f1759 100644 --- a/apps/platform/routes/web.php +++ b/apps/platform/routes/web.php @@ -6,6 +6,7 @@ use App\Http\Controllers\ClearEnvironmentContextController; use App\Http\Controllers\LocalizationController; use App\Http\Controllers\ManagedEnvironmentOnboardingController; +use App\Http\Controllers\ManagementReportPdfDownloadController; use App\Http\Controllers\OpenFindingExceptionsQueueController; use App\Http\Controllers\RbacDelegatedAuthController; use App\Http\Controllers\ReviewPackDownloadController; @@ -549,6 +550,10 @@ ->get('/admin/review-packs/{reviewPack}/download', ReviewPackDownloadController::class) ->name('admin.review-packs.download'); +Route::middleware(['signed']) + ->get('/admin/management-report-pdfs/{storedReport}/download', ManagementReportPdfDownloadController::class) + ->name('admin.management-report-pdfs.download'); + Route::middleware(['signed', 'apply-resolved-locale:admin']) ->get('/admin/review-packs/{reviewPack}/report', ReviewPackRenderedReportController::class) ->name('admin.review-packs.report'); diff --git a/apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php b/apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php new file mode 100644 index 00000000..88617acb --- /dev/null +++ b/apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php @@ -0,0 +1,221 @@ +browser()->timeout(60_000); + +beforeEach(function (): void { + Storage::fake('exports'); +}); + +it('Spec379 smokes the management PDF Review Pack detail states', function (): void { + config([ + 'tenantpilot.pdf_renderer.enabled' => true, + 'tenantpilot.pdf_renderer.driver' => 'gotenberg', + 'tenantpilot.pdf_renderer.runtime_validated' => false, + ]); + + [$user, $tenant, $review, $pack] = spec379BrowserCurrentReadyPack(); + + spec379BrowserAuthenticate($this, $user, $tenant); + + $packUrl = ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin'); + + $page = visit($packUrl) + ->resize(1280, 900) + ->waitForText('More') + ->assertSee('View customer-safe report') + ->assertSee('Download customer-safe review pack') + ->assertSee('More') + ->assertDontSee('Generate management PDF') + ->click('More') + ->waitForText('Management PDF unavailable') + ->assertSee('Management PDF unavailable') + ->assertSee('Regenerate review pack') + ->assertDontSee('Generate management PDF') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, 'spec379-management-report-pdf-generate-state'); + spec379BrowserCopyScreenshot('generate-state'); + + spec379BrowserReadyManagementPdf($pack); + + $page = visit($packUrl) + ->resize(1280, 900) + ->waitForText('Download management PDF') + ->assertSee('Download management PDF') + ->assertSee('More') + ->assertDontSee('Generate management PDF') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs() + ->screenshot(true, 'spec379-management-report-pdf-download-state'); + spec379BrowserCopyScreenshot('download-state'); + + expect($review)->toBeInstanceOf(EnvironmentReview::class); +}); + +/** + * @return array{0: User, 1: ManagedEnvironment, 2: EnvironmentReview, 3: ReviewPack} + */ +function spec379BrowserCurrentReadyPack(): array +{ + [$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager', clearCapabilityCaches: true); + $tenant->forceFill(['name' => 'Spec379 Browser Environment'])->save(); + $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0); + $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); + $review->forceFill([ + 'status' => 'published', + 'published_at' => now(), + 'published_by_user_id' => (int) $user->getKey(), + ])->save(); + $review = markEnvironmentReviewCustomerSafeReady($review); + + $zipContents = spec379BrowserZipContents(); + $filePath = sprintf('review-packs/%s/spec379-browser.zip', $tenant->external_id); + Storage::disk('exports')->put($filePath, $zipContents); + + $pack = ReviewPack::factory()->ready()->create([ + 'managed_environment_id' => (int) $tenant->getKey(), + 'workspace_id' => (int) $tenant->workspace_id, + 'environment_review_id' => (int) $review->getKey(), + 'evidence_snapshot_id' => (int) $snapshot->getKey(), + 'initiated_by_user_id' => (int) $user->getKey(), + 'options' => [ + 'include_pii' => false, + 'include_operations' => true, + ], + 'summary' => [ + 'governance_package' => [ + 'executive_summary' => 'Spec379 browser management handoff summary.', + ], + 'control_interpretation' => [ + 'non_certification_disclosure' => 'TenantPilot summarizes available evidence. This report is not a certification, legal attestation, audit opinion, or compliance guarantee.', + ], + ], + 'file_path' => $filePath, + 'file_disk' => 'exports', + 'file_size' => strlen($zipContents), + 'sha256' => hash('sha256', $zipContents), + 'expires_at' => now()->addDay(), + ]); + + $review->forceFill([ + 'current_export_review_pack_id' => (int) $pack->getKey(), + ])->save(); + + return [$user, $tenant, $review->fresh(['sections', 'evidenceSnapshot', 'currentExportReviewPack']), $pack->fresh(['tenant', 'environmentReview'])]; +} + +function spec379BrowserReadyManagementPdf(ReviewPack $pack): StoredReport +{ + $pack->loadMissing('tenant'); + $pdfBytes = '%PDF-1.7 Spec379 browser management report'; + $filePath = sprintf('management-reports/%s/browser-%d.pdf', $pack->tenant->external_id, (int) $pack->getKey()); + + Storage::disk('exports')->put($filePath, $pdfBytes); + + return StoredReport::factory()->managementReportPdf([ + 'title' => 'TenantPilot Management Report', + ])->create([ + 'workspace_id' => (int) $pack->workspace_id, + 'managed_environment_id' => (int) $pack->managed_environment_id, + 'source_environment_review_id' => (int) $pack->environment_review_id, + 'source_review_pack_id' => (int) $pack->getKey(), + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'file_disk' => 'exports', + 'file_path' => $filePath, + 'file_size' => strlen($pdfBytes), + 'sha256' => hash('sha256', $pdfBytes), + 'generated_at' => now(), + ]); +} + +function spec379BrowserZipContents(): string +{ + $tempFile = tempnam(sys_get_temp_dir(), 'spec379-browser-pack-'); + + if ($tempFile === false) { + throw new RuntimeException('Failed to allocate Spec379 browser archive.'); + } + + try { + $zip = new ZipArchive; + $result = $zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + if ($result !== true) { + throw new RuntimeException("Failed to create Spec379 browser archive: {$result}"); + } + + $zip->addFromString('metadata.json', json_encode(['fixture' => 'spec379-browser'], JSON_THROW_ON_ERROR)); + $zip->addFromString('executive-summary.md', 'Spec379 browser review pack.'); + $zip->close(); + + $contents = file_get_contents($tempFile); + + if (! is_string($contents) || $contents === '') { + throw new RuntimeException('Spec379 browser archive is empty.'); + } + + return $contents; + } finally { + if (file_exists($tempFile)) { + unlink($tempFile); + } + } +} + +function spec379BrowserAuthenticate(mixed $test, User $user, ManagedEnvironment $environment): void +{ + $workspaceId = (int) $environment->workspace_id; + + $test->actingAs($user)->withSession([ + WorkspaceContext::SESSION_KEY => $workspaceId, + WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ + (string) $workspaceId => (int) $environment->getKey(), + ], + ]); + + session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); + session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [ + (string) $workspaceId => (int) $environment->getKey(), + ]); + + setAdminPanelContext($environment); +} + +function spec379BrowserCopyScreenshot(string $name): void +{ + $filename = 'spec379-management-report-pdf-'.$name.'.png'; + $source = base_path('tests/Browser/Screenshots/'.$filename); + $targetDirectory = repo_path('specs/379-management-report-pdf-runtime/artifacts/screenshots'); + + if (! is_dir($targetDirectory)) { + @mkdir($targetDirectory, 0755, true); + } + + if (! is_file($source)) { + $source = \Pest\Browser\Support\Screenshot::path($filename); + } + + for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) { + usleep(100_000); + clearstatcache(true, $source); + } + + if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) { + @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png'); + } +} diff --git a/apps/platform/tests/Feature/Evidence/BaselineDriftPostureSourceTest.php b/apps/platform/tests/Feature/Evidence/BaselineDriftPostureSourceTest.php new file mode 100644 index 00000000..1f2996fb --- /dev/null +++ b/apps/platform/tests/Feature/Evidence/BaselineDriftPostureSourceTest.php @@ -0,0 +1,62 @@ +collect($tenant); + + expect($payload['state'])->toBe(EvidenceCompletenessState::Missing->value) + ->and($payload['summary_payload']['drift_count'])->toBe(0) + ->and($payload['summary_payload']['latest_compare_run_id'])->toBeNull(); +}); + +it('marks no baseline drift as complete when the latest compare succeeded', function (): void { + [, $tenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + + $run = seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: ['reason_code' => 'baseline.compare.no_drift_detected'], + outcome: OperationRunOutcome::Succeeded->value, + ); + + $payload = app(BaselineDriftPostureSource::class)->collect($tenant); + + expect($payload['state'])->toBe(EvidenceCompletenessState::Complete->value) + ->and($payload['measured_at']?->equalTo($run->completed_at))->toBeTrue() + ->and($payload['summary_payload']['drift_count'])->toBe(0) + ->and($payload['summary_payload']['latest_compare_run_id'])->toBe((int) $run->getKey()) + ->and($payload['summary_payload']['latest_compare_outcome'])->toBe(OperationRunOutcome::Succeeded->value); +}); + +it('marks no baseline drift as partial when the latest compare completed with warnings', function (): void { + [, $tenant] = createUserWithTenant(role: 'owner'); + [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); + + $run = seedBaselineCompareRun( + tenant: $tenant, + profile: $profile, + snapshot: $snapshot, + compareContext: [ + 'reason_code' => 'baseline.compare.no_drift_detected', + 'evidence_gaps' => ['count' => 3], + ], + outcome: OperationRunOutcome::PartiallySucceeded->value, + ); + + $payload = app(BaselineDriftPostureSource::class)->collect($tenant); + + expect($payload['state'])->toBe(EvidenceCompletenessState::Partial->value) + ->and($payload['measured_at']?->equalTo($run->completed_at))->toBeTrue() + ->and($payload['summary_payload']['drift_count'])->toBe(0) + ->and($payload['summary_payload']['latest_compare_run_id'])->toBe((int) $run->getKey()) + ->and($payload['summary_payload']['latest_compare_outcome'])->toBe(OperationRunOutcome::PartiallySucceeded->value); +}); diff --git a/apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php b/apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php new file mode 100644 index 00000000..02cde080 --- /dev/null +++ b/apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php @@ -0,0 +1,695 @@ +instance(GraphClientInterface::class, new FailHardGraphClient); +}); + +function spec379ConfigurePdfRenderer(bool $runtimeValidated = true, array $overrides = []): void +{ + config([ + 'tenantpilot.pdf_renderer' => array_merge([ + 'enabled' => true, + 'runtime_validated' => $runtimeValidated, + 'driver' => 'gotenberg', + 'base_url' => 'http://gotenberg.test', + 'health_path' => '/health', + 'html_route' => '/forms/chromium/convert/html', + 'timeout_seconds' => 30, + 'connect_timeout_seconds' => 5, + 'max_html_bytes' => 1024 * 1024, + 'max_asset_bytes' => 512 * 1024, + 'max_output_bytes' => 2 * 1024 * 1024, + 'correlation_header' => 'Gotenberg-Trace', + 'output_filename' => 'tenantpilot-management-report', + ], $overrides), + ]); +} + +/** + * @param array $files + */ +function spec379ZipContents(array $files = []): string +{ + $tempFile = tempnam(sys_get_temp_dir(), 'spec379-review-pack-'); + + if ($tempFile === false) { + throw new RuntimeException('Failed to allocate Spec379 archive.'); + } + + try { + $zip = new ZipArchive; + $result = $zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + if ($result !== true) { + throw new RuntimeException("Failed to create Spec379 archive: {$result}"); + } + + foreach (array_replace([ + 'metadata.json' => json_encode(['fixture' => 'spec379'], JSON_THROW_ON_ERROR), + 'executive-summary.md' => 'Spec379 current review pack.', + ], $files) as $filename => $contents) { + $zip->addFromString($filename, $contents); + } + + $zip->close(); + + $contents = file_get_contents($tempFile); + + if (! is_string($contents) || $contents === '') { + throw new RuntimeException('Spec379 archive is empty.'); + } + + return $contents; + } finally { + if (file_exists($tempFile)) { + unlink($tempFile); + } + } +} + +/** + * @param array $summaryOverrides + * @return array{0: \App\Models\User, 1: \App\Models\ManagedEnvironment, 2: \App\Models\EnvironmentReview, 3: ReviewPack} + */ +function spec379CurrentReadyPack(array $summaryOverrides = []): array +{ + [$user, $tenant] = createUserWithTenant(role: 'owner', clearCapabilityCaches: true); + $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0); + $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); + $review->forceFill([ + 'status' => 'published', + 'published_at' => now(), + 'published_by_user_id' => (int) $user->getKey(), + ])->save(); + $review = markEnvironmentReviewCustomerSafeReady($review); + + $zipContents = spec379ZipContents(); + $filePath = sprintf('review-packs/%s/spec379-current.zip', $tenant->external_id); + Storage::disk('exports')->put($filePath, $zipContents); + + $summary = array_replace_recursive([ + 'governance_package' => [ + 'executive_summary' => 'The released review is ready for a customer management handoff.', + ], + 'decision_summary' => [ + 'entries' => [ + [ + 'title' => 'Proceed with governed handoff', + 'summary' => 'Customer-safe evidence is ready.', + 'rationale' => 'Current Review Pack is complete.', + ], + ], + ], + 'top_findings' => [ + [ + 'title' => 'Privileged access review', + 'severity' => 'medium', + 'summary' => 'Review privileged role assignment cadence.', + ], + ], + 'risk_acceptance' => [ + [ + 'title' => 'Known exception', + 'summary' => 'Accepted until the next operating review.', + ], + ], + 'recommended_next_actions' => [ + [ + 'title' => 'Schedule review', + 'summary' => 'Review the management report with customer stakeholders.', + ], + ], + 'control_interpretation' => [ + 'non_certification_disclosure' => 'TenantPilot summarizes available service-delivery evidence for governance review. This report is not a certification, legal attestation, audit opinion, or compliance guarantee.', + ], + ], $summaryOverrides); + + $pack = ReviewPack::factory()->ready()->create([ + 'managed_environment_id' => (int) $tenant->getKey(), + 'workspace_id' => (int) $tenant->workspace_id, + 'environment_review_id' => (int) $review->getKey(), + 'evidence_snapshot_id' => (int) $snapshot->getKey(), + 'initiated_by_user_id' => (int) $user->getKey(), + 'options' => [ + 'include_pii' => false, + 'include_operations' => true, + ], + 'summary' => $summary, + 'file_path' => $filePath, + 'file_disk' => 'exports', + 'file_size' => strlen($zipContents), + 'sha256' => hash('sha256', $zipContents), + 'expires_at' => now()->addDay(), + ]); + + $review->forceFill([ + 'current_export_review_pack_id' => (int) $pack->getKey(), + ])->save(); + + return [$user, $tenant, $review->fresh(['sections', 'evidenceSnapshot', 'currentExportReviewPack']), $pack->fresh(['tenant', 'environmentReview'])]; +} + +function spec379ReadyManagementPdf(ReviewPack $pack): StoredReport +{ + $pdfBytes = '%PDF-1.7 Spec379 ready management report'; + $filePath = sprintf('management-reports/%s/ready-%d.pdf', $pack->tenant->external_id, (int) $pack->getKey()); + + Storage::disk('exports')->put($filePath, $pdfBytes); + + return StoredReport::factory()->managementReportPdf([ + 'title' => 'TenantPilot Management Report', + ])->create([ + 'workspace_id' => (int) $pack->workspace_id, + 'managed_environment_id' => (int) $pack->managed_environment_id, + 'source_environment_review_id' => (int) $pack->environment_review_id, + 'source_review_pack_id' => (int) $pack->getKey(), + 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, + 'file_disk' => 'exports', + 'file_path' => $filePath, + 'file_size' => strlen($pdfBytes), + 'sha256' => hash('sha256', $pdfBytes), + 'generated_at' => now(), + ]); +} + +it('Spec379 blocks generation until the PDF runtime is validated', function (): void { + spec379ConfigurePdfRenderer(runtimeValidated: false); + [$user, $tenant, , $pack] = spec379CurrentReadyPack(); + Queue::fake(); + $runCount = OperationRun::query()->count(); + $reportCount = StoredReport::query() + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->count(); + + $service = app(ManagementReportPdfService::class); + $decision = $service->generationDecision($pack); + $result = $service->startGeneration($pack, $user); + + expect($decision['is_blocked'])->toBeTrue() + ->and($decision['reason_code'])->toBe('runtime_validation_missing') + ->and($result['mode'])->toBe('blocked') + ->and(OperationRun::query()->count())->toBe($runCount) + ->and(StoredReport::query()->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF)->count())->toBe($reportCount); + + Queue::assertNothingPushed(); + + $audit = AuditLog::query() + ->where('action', AuditActionId::ManagementReportPdfGenerationBlocked->value) + ->latest('id') + ->first(); + + expect($audit)->not->toBeNull() + ->and($audit?->workspace_id)->toBe((int) $tenant->workspace_id) + ->and($audit?->resource_type)->toBe('stored_report') + ->and(data_get($audit?->metadata, 'decision.reason_code'))->toBe('runtime_validation_missing'); +}); + +it('Spec379 queues generation, renders through Gotenberg, stores a private PDF, and records operation proof', function (): void { + spec379ConfigurePdfRenderer(); + [$user, $tenant, $review, $pack] = spec379CurrentReadyPack([ + 'top_findings' => [ + [ + 'title' => 'Secret-shaped source value', + 'summary' => 'SQLSTATE raw error with access_token should never appear.', + ], + ], + ]); + Queue::fake(); + + $service = app(ManagementReportPdfService::class); + $result = $service->startGeneration($pack, $user); + + expect($result['mode'])->toBe('queued') + ->and($result['report'])->toBeInstanceOf(StoredReport::class) + ->and($result['operation_run'])->toBeInstanceOf(OperationRun::class); + + Queue::assertPushed(GenerateManagementReportPdfJob::class); + + /** @var StoredReport $report */ + $report = $result['report']; + /** @var OperationRun $run */ + $run = $result['operation_run']; + + expect($run->type)->toBe(OperationRunType::ManagementReportGenerate->value) + ->and($report->status)->toBe(StoredReport::STATUS_QUEUED) + ->and($report->source_review_pack_id)->toBe((int) $pack->getKey()) + ->and($report->source_environment_review_id)->toBe((int) $review->getKey()); + + Http::fake([ + 'gotenberg.test/forms/chromium/convert/html' => Http::response('%PDF-1.7 rendered spec379', 200, [ + 'Content-Type' => 'application/pdf', + 'Gotenberg-Trace' => 'spec379-render', + ]), + ]); + + app()->call([new GenerateManagementReportPdfJob( + storedReportId: (int) $report->getKey(), + operationRunId: (int) $run->getKey(), + ), 'handle']); + + $report->refresh(); + $run->refresh(); + + expect($report->status)->toBe(StoredReport::STATUS_READY) + ->and($report->file_disk)->toBe('exports') + ->and(Storage::disk('exports')->exists((string) $report->file_path))->toBeTrue() + ->and($report->sha256)->toBe(hash('sha256', '%PDF-1.7 rendered spec379')) + ->and(json_encode($report->payload, JSON_THROW_ON_ERROR))->not->toContain('SQLSTATE', 'access_token') + ->and($run->status)->toBe(OperationRunStatus::Completed->value) + ->and($run->outcome)->toBe(OperationRunOutcome::Succeeded->value) + ->and($run->summary_counts)->toMatchArray([ + 'total' => 1, + 'processed' => 1, + 'succeeded' => 1, + 'report_created' => 1, + ]); + + Http::assertSent(fn (Request $request): bool => $request->url() === 'http://gotenberg.test/forms/chromium/convert/html' + && $request->method() === 'POST'); + + $audit = AuditLog::query() + ->where('action', AuditActionId::ManagementReportPdfGenerated->value) + ->latest('id') + ->first(); + + expect($audit)->not->toBeNull() + ->and($audit?->workspace_id)->toBe((int) $tenant->workspace_id) + ->and($audit?->resource_type)->toBe('stored_report') + ->and(data_get($audit?->metadata, 'stored_report_id'))->toBe((int) $report->getKey()); +}); + +it('Spec379 builds a customer-executive payload without profile fallback or sensitive source strings', function (): void { + [$user, , , $pack] = spec379CurrentReadyPack([ + 'decision_summary' => [ + 'entries' => [ + [ + 'title' => 'Token shaped value', + 'summary' => 'Bearer abc.def access_token=secret should be redacted.', + ], + ], + ], + ]); + + $payload = app(ManagementReportPdfPayloadBuilder::class)->build($pack); + $encoded = json_encode($payload, JSON_THROW_ON_ERROR); + + expect($payload['profile'])->toBe(ReportProfileRegistry::CUSTOMER_EXECUTIVE) + ->and($encoded)->toContain('Executive summary') + ->and($encoded)->toContain('Method summary') + ->and($encoded)->not->toContain('Bearer abc.def', 'access_token=secret', 'Internal MSP review') + ->and($user)->not->toBeNull(); +}); + +it('Spec379 maps source and disclosure blockers before rendering', function (): void { + spec379ConfigurePdfRenderer(); + [$user, , $review, $pack] = spec379CurrentReadyPack(); + Queue::fake(); + $service = app(ManagementReportPdfService::class); + + Storage::disk('exports')->delete((string) $pack->file_path); + expect($service->generationDecision($pack)['reason_code'])->toBe('source_artifact_missing'); + + Storage::disk('exports')->put((string) $pack->file_path, spec379ZipContents()); + $pack->forceFill(['expires_at' => now()->subMinute()])->save(); + expect($service->generationDecision($pack->fresh())['reason_code'])->toBe('review_pack_expired'); + + $pack->forceFill(['expires_at' => now()->addDay()])->save(); + $review->forceFill(['current_export_review_pack_id' => null])->save(); + expect($service->generationDecision($pack->fresh(['environmentReview']))['reason_code'])->toBe('review_pack_not_current'); + + $review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save(); + $pack->forceFill([ + 'options' => [ + 'include_pii' => true, + 'include_operations' => true, + ], + ])->save(); + + $runCount = OperationRun::query()->count(); + $reportCount = StoredReport::query() + ->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF) + ->count(); + $disclosureDecision = $service->generationDecision($pack->fresh(['environmentReview'])); + $result = $service->startGeneration($pack->fresh(['tenant', 'environmentReview']), $user); + + expect($disclosureDecision['is_blocked'])->toBeTrue() + ->and($disclosureDecision['reason_code'])->toBe('customer_profile_internal_only') + ->and($result['mode'])->toBe('blocked') + ->and(OperationRun::query()->count())->toBe($runCount) + ->and(StoredReport::query()->where('report_type', StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF)->count())->toBe($reportCount); + + Queue::assertNotPushed(GenerateManagementReportPdfJob::class); + + expect(fn () => app(ManagementReportPdfPayloadBuilder::class)->build($pack->fresh(['environmentReview']))) + ->toThrow(InvalidArgumentException::class); +}); + +it('Spec379 marks queued generation as blocked when disclosure changes before the job runs', function (): void { + Queue::fake(); + spec379ConfigurePdfRenderer(); + [$user, , , $pack] = spec379CurrentReadyPack(); + $result = app(ManagementReportPdfService::class)->startGeneration($pack, $user); + + /** @var StoredReport $report */ + $report = $result['report']; + /** @var OperationRun $run */ + $run = $result['operation_run']; + + $pack->forceFill([ + 'options' => [ + 'include_pii' => true, + 'include_operations' => true, + ], + ])->save(); + + Http::fake(); + + app()->call([new GenerateManagementReportPdfJob( + storedReportId: (int) $report->getKey(), + operationRunId: (int) $run->getKey(), + ), 'handle']); + + $report->refresh(); + $run->refresh(); + + expect($report->status)->toBe(StoredReport::STATUS_FAILED) + ->and(data_get($report->payload, 'blocked'))->toBeTrue() + ->and($report->file_path)->toBeNull() + ->and($run->status)->toBe(OperationRunStatus::Completed->value) + ->and($run->outcome)->toBe(OperationRunOutcome::Blocked->value) + ->and($run->summary_counts)->toMatchArray([ + 'total' => 1, + 'processed' => 1, + 'succeeded' => 0, + 'failed' => 0, + ]) + ->and(data_get($run->failure_summary, '0.code'))->toBe('management_report_pdf.customer_profile_internal_only'); + + Http::assertNothingSent(); + + $audit = AuditLog::query() + ->where('action', AuditActionId::ManagementReportPdfGenerationBlocked->value) + ->latest('id') + ->first(); + + expect($audit)->not->toBeNull() + ->and(data_get($audit?->metadata, 'reason_code'))->toBe('customer_profile_internal_only'); +}); + +it('Spec379 records renderer failure without exposing a ready artifact', function (): void { + Queue::fake(); + spec379ConfigurePdfRenderer(); + [$user, , , $pack] = spec379CurrentReadyPack(); + $result = app(ManagementReportPdfService::class)->startGeneration($pack, $user); + + /** @var StoredReport $report */ + $report = $result['report']; + /** @var OperationRun $run */ + $run = $result['operation_run']; + + Http::fake([ + 'gotenberg.test/forms/chromium/convert/html' => Http::sequence() + ->push('renderer failed', 500, [ + 'Content-Type' => 'text/plain', + ]) + ->push('%PDF-1.7 rendered spec379 retry', 200, [ + 'Content-Type' => 'application/pdf', + 'Gotenberg-Trace' => 'spec379-renderer-retry', + ]), + ]); + + app()->call([new GenerateManagementReportPdfJob( + storedReportId: (int) $report->getKey(), + operationRunId: (int) $run->getKey(), + ), 'handle']); + + $report->refresh(); + $run->refresh(); + + expect($report->status)->toBe(StoredReport::STATUS_FAILED) + ->and($report->file_path)->toBeNull() + ->and($run->status)->toBe(OperationRunStatus::Completed->value) + ->and($run->outcome)->toBe(OperationRunOutcome::Failed->value) + ->and(data_get($run->failure_summary, '0.code'))->toBe('management_report_pdf.renderer_failed'); + + Queue::fake(); + $retry = app(ManagementReportPdfService::class)->startGeneration($pack->fresh(['tenant', 'environmentReview']), $user); + + /** @var StoredReport $retryReport */ + $retryReport = $retry['report']; + /** @var OperationRun $retryRun */ + $retryRun = $retry['operation_run']; + + expect($retry['mode'])->toBe('queued') + ->and($retryReport->getKey())->toBe($report->getKey()) + ->and($retryReport->status)->toBe(StoredReport::STATUS_QUEUED) + ->and($retryReport->operation_run_id)->toBe((int) $retryRun->getKey()) + ->and($retryRun->getKey())->not->toBe($run->getKey()); + + Queue::assertPushed(GenerateManagementReportPdfJob::class); + + app()->call([new GenerateManagementReportPdfJob( + storedReportId: (int) $retryReport->getKey(), + operationRunId: (int) $retryRun->getKey(), + ), 'handle']); + + $retryReport->refresh(); + $retryRun->refresh(); + + expect($retryReport->status)->toBe(StoredReport::STATUS_READY) + ->and($retryReport->file_path)->not->toBeNull() + ->and($retryRun->status)->toBe(OperationRunStatus::Completed->value) + ->and($retryRun->outcome)->toBe(OperationRunOutcome::Succeeded->value); + + Http::assertSentCount(2); +}); + +it('Spec379 records storage failure without exposing a ready artifact', function (): void { + Queue::fake(); + spec379ConfigurePdfRenderer(); + [$user, , , $pack] = spec379CurrentReadyPack(); + $result = app(ManagementReportPdfService::class)->startGeneration($pack, $user); + + /** @var StoredReport $report */ + $report = $result['report']; + /** @var OperationRun $run */ + $run = $result['operation_run']; + + Http::fake([ + 'gotenberg.test/forms/chromium/convert/html' => Http::response('%PDF-1.7 rendered spec379', 200, [ + 'Content-Type' => 'application/pdf', + 'Gotenberg-Trace' => 'spec379-storage-failure', + ]), + ]); + + $fakeDisk = \Mockery::mock(\Illuminate\Contracts\Filesystem\Filesystem::class); + $fakeDisk->shouldReceive('put') + ->once() + ->andReturnFalse(); + + Storage::shouldReceive('disk') + ->with('exports') + ->andReturn($fakeDisk); + + app()->call([new GenerateManagementReportPdfJob( + storedReportId: (int) $report->getKey(), + operationRunId: (int) $run->getKey(), + ), 'handle']); + + $report->refresh(); + $run->refresh(); + + expect($report->status)->toBe(StoredReport::STATUS_FAILED) + ->and($report->file_path)->toBeNull() + ->and($report->sha256)->toBeNull() + ->and($run->status)->toBe(OperationRunStatus::Completed->value) + ->and($run->outcome)->toBe(OperationRunOutcome::Failed->value) + ->and(data_get($run->failure_summary, '0.code'))->toBe('management_report_pdf.storage_failed'); +}); + +it('Spec379 downloads a ready management PDF only through the signed tenant-authorized route', function (): void { + [$user, $tenant, , $pack] = spec379CurrentReadyPack(); + $report = spec379ReadyManagementPdf($pack); + $url = app(ManagementReportPdfService::class)->generateDownloadUrl($report, [ + 'source_surface' => 'spec379', + ]); + + $this->actingAs($user) + ->get($url) + ->assertOk() + ->assertHeader('X-Management-Report-PDF-SHA256', $report->sha256) + ->assertDownload(); + + $audit = AuditLog::query() + ->where('action', AuditActionId::ManagementReportPdfDownloaded->value) + ->latest('id') + ->first(); + + expect($audit)->not->toBeNull() + ->and($audit?->resource_type)->toBe('stored_report') + ->and(data_get($audit?->metadata, 'stored_report_id'))->toBe((int) $report->getKey()) + ->and(data_get($audit?->metadata, 'source_surface'))->toBe('spec379'); + + [$otherUser] = createUserWithTenant(role: 'owner'); + + $this->actingAs($otherUser) + ->get($url) + ->assertNotFound(); + + expect($tenant)->not->toBeNull(); +}); + +it('Spec379 enforces scoped authorization for generation and download', function (): void { + spec379ConfigurePdfRenderer(); + [$owner, , , $pack] = spec379CurrentReadyPack(); + [$readonly] = createUserWithTenant(tenant: $pack->tenant, user: \App\Models\User::factory()->create(), role: 'readonly', clearCapabilityCaches: true); + $report = spec379ReadyManagementPdf($pack); + $url = app(ManagementReportPdfService::class)->generateDownloadUrl($report); + + try { + app(ManagementReportPdfService::class)->startGeneration($pack, $readonly); + $this->fail('Scoped readonly actor should not start management PDF generation.'); + } catch (\Symfony\Component\HttpKernel\Exception\HttpException $exception) { + expect($exception->getStatusCode())->toBe(403); + } + + Gate::define( + Capabilities::REVIEW_PACK_VIEW, + fn (User $actor, ManagedEnvironment $tenant): bool => (int) $actor->getKey() !== (int) $readonly->getKey() + || (int) $tenant->getKey() !== (int) $pack->tenant->getKey(), + ); + + $this->actingAs($readonly) + ->get($url) + ->assertForbidden(); + + [$outsideUser] = createUserWithTenant(role: 'owner', clearCapabilityCaches: true); + + $this->actingAs($outsideUser) + ->get($url) + ->assertNotFound(); + + expect($owner)->not->toBeNull(); +}); + +it('Spec379 exposes the confirmed Filament action only when generation is available', function (): void { + spec379ConfigurePdfRenderer(runtimeValidated: false); + [$user, $tenant, , $pack] = spec379CurrentReadyPack(); + + setAdminEnvironmentContext($tenant); + + Livewire::actingAs($user) + ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) + ->assertActionVisible('generate_management_report_pdf') + ->assertActionDisabled('generate_management_report_pdf'); + + spec379ConfigurePdfRenderer(runtimeValidated: true); + + Livewire::actingAs($user) + ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) + ->assertActionVisible('generate_management_report_pdf') + ->assertActionEnabled('generate_management_report_pdf') + ->assertActionExists('generate_management_report_pdf', fn (Action $action): bool => $action->isConfirmationRequired()) + ->mountAction('generate_management_report_pdf') + ->assertActionMounted('generate_management_report_pdf'); +}); + +it('Spec379 exposes an unavailable Filament action when disclosure blocks customer PDF generation', function (): void { + spec379ConfigurePdfRenderer(); + [$user, $tenant, , $pack] = spec379CurrentReadyPack(); + + $pack->forceFill([ + 'options' => [ + 'include_pii' => true, + 'include_operations' => true, + ], + ])->save(); + + setAdminEnvironmentContext($tenant); + + Livewire::actingAs($user) + ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) + ->assertActionVisible('generate_management_report_pdf') + ->assertActionDisabled('generate_management_report_pdf') + ->assertActionExists('generate_management_report_pdf', fn (Action $action): bool => $action->getLabel() === __('localization.review.management_report_pdf_blocked')); +}); + +it('Spec379 regenerates a review-bound pack from the Review Pack detail action', function (): void { + Queue::fake(); + [$user, $tenant, $review, $pack] = spec379CurrentReadyPack(); + $tenantWidePackCount = ReviewPack::query() + ->where('managed_environment_id', (int) $tenant->getKey()) + ->whereNull('environment_review_id') + ->count(); + + setAdminEnvironmentContext($tenant); + + Livewire::actingAs($user) + ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) + ->callAction('regenerate', data: [ + 'include_pii' => true, + 'include_operations' => true, + ]) + ->assertNotified(); + + $regeneratedPack = ReviewPack::query() + ->where('environment_review_id', (int) $review->getKey()) + ->whereKeyNot((int) $pack->getKey()) + ->latest('id') + ->firstOrFail(); + + expect($regeneratedPack->options)->toMatchArray([ + 'include_pii' => true, + 'include_operations' => true, + ]) + ->and($regeneratedPack->managed_environment_id)->toBe((int) $tenant->getKey()) + ->and($regeneratedPack->evidence_snapshot_id)->toBe((int) $review->evidence_snapshot_id) + ->and(ReviewPack::query() + ->where('managed_environment_id', (int) $tenant->getKey()) + ->whereNull('environment_review_id') + ->count())->toBe($tenantWidePackCount); + + Queue::assertPushed(GenerateReviewPackJob::class); +}); + +it('Spec379 swaps the generate action for the download action once a PDF exists', function (): void { + [$user, $tenant, , $pack] = spec379CurrentReadyPack(); + spec379ReadyManagementPdf($pack); + + setAdminEnvironmentContext($tenant); + + Livewire::actingAs($user) + ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) + ->assertActionVisible('download_management_report_pdf') + ->assertActionDoesNotExist('generate_management_report_pdf'); +}); diff --git a/apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php b/apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php index 15680b66..f769b826 100644 --- a/apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php +++ b/apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); +use App\Services\Pdf\PdfRenderingGateway; use App\Services\Pdf\PdfRenderRequest; use App\Services\Pdf\PdfRenderResult; -use App\Services\Pdf\PdfRenderingGateway; use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; use Illuminate\Support\Str; @@ -75,7 +75,8 @@ function configureSpec378PdfRenderer(array $overrides = []): void expect($gotenbergBlock)->toContain("image: 'gotenberg/gotenberg:8.34.0-chromium'") ->and($gotenbergBlock)->toContain("API_DISABLE_DOWNLOAD_FROM: 'true'") ->and($gotenbergBlock)->toContain("WEBHOOK_DISABLE: 'true'") - ->and($gotenbergBlock)->toContain("CHROMIUM_ALLOW_LIST: '^$'") + ->and($gotenbergBlock)->toContain('CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES: \'${GOTENBERG_CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES:-true}\'') + ->and($gotenbergBlock)->toContain('CHROMIUM_ALLOW_LIST: \'${GOTENBERG_CHROMIUM_ALLOW_LIST:-^file:///tmp/.*$}\'') ->and($gotenbergBlock)->toContain("CHROMIUM_DENY_PRIVATE_IPS: 'true'") ->and($gotenbergBlock)->toContain("CHROMIUM_DENY_PUBLIC_IPS: 'true'") ->and($gotenbergBlock)->not->toContain("\n ports:"); diff --git a/apps/platform/tests/Unit/Support/ReviewPacks/Spec379ManagementReportPdfReadinessTest.php b/apps/platform/tests/Unit/Support/ReviewPacks/Spec379ManagementReportPdfReadinessTest.php new file mode 100644 index 00000000..2d47af06 --- /dev/null +++ b/apps/platform/tests/Unit/Support/ReviewPacks/Spec379ManagementReportPdfReadinessTest.php @@ -0,0 +1,64 @@ + array_merge([ + 'enabled' => true, + 'runtime_validated' => true, + 'driver' => 'gotenberg', + ], $overrides), + ]); +} + +it('Spec379 maps management PDF runtime blockers before generation is available', function (array $overrides, ?string $reasonCode): void { + spec379RuntimeGateConfig($overrides); + + $decision = app(ManagementReportPdfRuntimeGate::class)->decision(); + + expect($decision['reason_code'])->toBe($reasonCode) + ->and($decision['is_blocked'])->toBe($reasonCode !== null); +})->with([ + 'renderer disabled' => [['enabled' => false], 'renderer_disabled'], + 'unsupported driver' => [['driver' => 'unsupported'], 'renderer_driver_unsupported'], + 'runtime validation missing' => [['runtime_validated' => false], 'runtime_validation_missing'], + 'available' => [[], null], +]); + +it('Spec379 treats fallback or non-customer profiles as invalid management PDF profiles', function (string $requestedProfile): void { + $resolved = ReportProfileRegistry::resolve($requestedProfile, ReportProfileRegistry::CUSTOMER_EXECUTIVE); + + $isValidManagementPdfProfile = (string) ($resolved['effective_key'] ?? '') === ReportProfileRegistry::CUSTOMER_EXECUTIVE + && ! (bool) ($resolved['is_fallback'] ?? false); + + expect($isValidManagementPdfProfile)->toBeFalse() + ->and($resolved['effective_key'])->not->toBe(ReportProfileRegistry::CUSTOMER_EXECUTIVE); +})->with([ + 'unknown profile' => ['no_such_profile'], + 'unimplemented framework placeholder' => [ReportProfileRegistry::FRAMEWORK_READINESS], +]); + +it('Spec379 blocks customer executive disclosure when protected customer output would include PII', function (): void { + $profile = ReportProfileRegistry::resolve(ReportProfileRegistry::CUSTOMER_EXECUTIVE); + $disclosure = ReportDisclosurePolicy::evaluate($profile, [ + 'review_status' => 'published', + 'evidence_completeness_state' => 'complete', + 'contains_pii' => true, + 'protected_values_hidden' => false, + 'disclosure_present' => true, + 'customer_safe_state' => ReviewPackOutputReadiness::STATE_CUSTOMER_SAFE_READY, + ], [ + 'non_certification_disclosure' => 'TenantPilot summarizes available evidence and does not certify compliance.', + ]); + + expect(collect($disclosure['blocking_reasons'])->pluck('key')->all()) + ->toContain('customer_profile_internal_only') + ->and(data_get($disclosure, 'proof_states.protected_values'))->toBe('missing'); +}); diff --git a/docker-compose.yml b/docker-compose.yml index 3548af92..ac36436d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,8 +78,8 @@ services: CHROMIUM_START_TIMEOUT: '${GOTENBERG_CHROMIUM_START_TIMEOUT:-20s}' CHROMIUM_MAX_QUEUE_SIZE: '${GOTENBERG_CHROMIUM_MAX_QUEUE_SIZE:-10}' CHROMIUM_MAX_CONCURRENCY: '${GOTENBERG_CHROMIUM_MAX_CONCURRENCY:-2}' - CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES: 'false' - CHROMIUM_ALLOW_LIST: '^$' + CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES: '${GOTENBERG_CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES:-true}' + CHROMIUM_ALLOW_LIST: '${GOTENBERG_CHROMIUM_ALLOW_LIST:-^file:///tmp/.*$}' CHROMIUM_DENY_PRIVATE_IPS: 'true' CHROMIUM_DENY_PUBLIC_IPS: 'true' WEBHOOK_DISABLE: 'true' diff --git a/docs/deployment-checklist.md b/docs/deployment-checklist.md index f4960ab6..1effa6fc 100644 --- a/docs/deployment-checklist.md +++ b/docs/deployment-checklist.md @@ -62,7 +62,7 @@ ## PDF Renderer Checklist - Set `TENANTPILOT_PDF_RENDERER_BASE_URL` to the internal service URL, for example `http://gotenberg:3000`. - Set explicit renderer timeouts and limits: `TENANTPILOT_PDF_RENDERER_TIMEOUT_SECONDS`, `TENANTPILOT_PDF_RENDERER_CONNECT_TIMEOUT_SECONDS`, `TENANTPILOT_PDF_RENDERER_MAX_HTML_BYTES`, `TENANTPILOT_PDF_RENDERER_MAX_ASSET_BYTES`, and `TENANTPILOT_PDF_RENDERER_MAX_OUTPUT_BYTES`. - Set matching Gotenberg service controls: `API_TIMEOUT`, `API_BODY_LIMIT`, `API_CORRELATION_ID_HEADER`, `CHROMIUM_START_TIMEOUT`, `CHROMIUM_MAX_QUEUE_SIZE`, and `CHROMIUM_MAX_CONCURRENCY`. -- Keep `API_DISABLE_DOWNLOAD_FROM=true`, `WEBHOOK_DISABLE=true`, `CHROMIUM_ALLOW_LIST=^$`, `CHROMIUM_DENY_PRIVATE_IPS=true`, and `CHROMIUM_DENY_PUBLIC_IPS=true` unless a later spec approves external asset fetches. +- Keep `API_DISABLE_DOWNLOAD_FROM=true`, `WEBHOOK_DISABLE=true`, `CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES=true`, `CHROMIUM_ALLOW_LIST=^file:///tmp/.*$`, `CHROMIUM_DENY_PRIVATE_IPS=true`, and `CHROMIUM_DENY_PUBLIC_IPS=true` unless a later spec approves external asset fetches. The file allow-list is required for Gotenberg's uploaded `index.html` conversion path and must not be widened to external URLs. - Do not install Node, Puppeteer, Chrome, Chromium, or browser binaries in Laravel web or queue containers for production PDF rendering. ## Scheduler Checklist diff --git a/docs/product/implementation-ledger.md b/docs/product/implementation-ledger.md index 529626d4..acbe5393 100644 --- a/docs/product/implementation-ledger.md +++ b/docs/product/implementation-ledger.md @@ -1,10 +1,10 @@ # TenantPilot Implementation Ledger > **Status:** Active -> **Last reviewed:** 2026-05-15 +> **Last reviewed:** 2026-06-15 > **Use for:** Repo-based implementation status and product-surface maturity assessment > **Do not use for:** Roadmap priority, spec priority, or proof that tests were executed in the current branch -> **Scoped maintenance:** 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Decision Register proof-link implementation update after Spec 307; 2026-05-15 Decision Register reconciliation update after Spec 306; 2026-05-15 Tenant Panel dead-code retirement guardrail update after Spec 304; 2026-05-12 roadmap/ledger alignment after the admin workspace navigation and tenant-owned surface repair candidate intake from the repo-verified navigation/panel audit; 2026-05-06 ledger conflict cleanup plus alignment with `docs/product/roadmap.md` and `docs/product/spec-candidates.md` after the cross-domain indicator candidate intake and the current manual-promotion backlog review. +> **Scoped maintenance:** 2026-06-15 repo-truth sync after Specs 311-379, including completed Spec 311 surface-scope foundation, post-311 candidate reconciliation, Spec 377 UI closeout, and current working-tree Spec 379 management-report PDF runtime-gated status; 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Decision Register proof-link implementation update after Spec 307; 2026-05-15 Decision Register reconciliation update after Spec 306; 2026-05-15 Tenant Panel dead-code retirement guardrail update after Spec 304; 2026-05-12 roadmap/ledger alignment after the admin workspace navigation and tenant-owned surface repair candidate intake from the repo-verified navigation/panel audit; 2026-05-06 ledger conflict cleanup plus alignment with `docs/product/roadmap.md` and `docs/product/spec-candidates.md` after the cross-domain indicator candidate intake and the current manual-promotion backlog review. ## Purpose @@ -23,11 +23,14 @@ ## Purpose ## Current Product Position -TenantPilot ist aktuell ein starkes internes Governance- und Operations-Produkt mit belastbaren Foundations fuer Execution Truth, Baselines/Drift, Findings, Evidence, Reviews, Review Packs, Supportability, Telemetry, Safety Controls und eine repo-reale governed AI policy foundation. Darauf sitzen inzwischen mehrere repo-real productization slices: eine customer-safe Review-/Governance-Package-Surface im Admin-Kontext, released-review detail handoff, customer-safe Decision Summary und Review Pack inclusion aus Spec 308, compliance interpretation overlays, bounded external support-desk handoff, commercial lifecycle state handling mit read-only gating, eine kanonische cross-tenant compare preview mit promotion preflight sowie ein repo-verifizierter operatorseitiger Decision Register ueber bestehender FindingException-Decision-Truth mit direkter proof/run link polish. Die Repo-Wahrheit liegt damit klar ueber einer simplen Lesart von "R1 done / R2 partial" und auch ueber einer rein foundation-only Interpretation fuer Reviews, Support und Portfolio-Preparation. Gleichzeitig ist das Produkt noch nicht voll als kundenseitig konsumierbare Portfolio- und Commercial-Plattform ausgereift: Es fehlen die letzte customer-safe self-serve productization ueber der Review-Surface, actual portfolio promotion execution, Decision-Based Governance Inbox completion, wiederholbare Billing-/Subscription-Truth, eine klarere Stored-Reports-Surface und der erste governed AI runtime consumer ueber der bereits repo-realen AI policy foundation. +TenantPilot ist aktuell ein starkes Governance- und Operations-Produkt mit repo-realen Foundations fuer Execution Truth, Baselines/Drift, Findings, Evidence, Reviews, Review Packs, Supportability, Telemetry, Safety Controls, Commercial Lifecycle und governed AI policy. Seit Spec 311 ist der Workspace/Environment-Surface-Scope-Contract eine abgeschlossene Foundation: `/admin` und `/system` sind die aktiven Panels, `/admin/t` bleibt retired, workspace-wide versus environment-bound Scope ist route-owned, und `environment_id` ist ein expliziter Filter. Darauf sitzen inzwischen mehrere repo-real productization slices: Customer Review Workspace v1 Completion, Decision Register proof/run links, customer-safe Decision Summary und Review Pack inclusion, Governance Inbox operator workflow, Provider Connection scope hardening, canonical link/query cleanup, localization adoption/neutralization, support-access slices, commercial entitlement/lifecycle truth, UI productization closeout sowie current working-tree Management Report PDF runtime work. Die wichtigsten offenen Luecken sind nicht mehr diese Foundations, sondern runtime/productization follow-through: Management Report PDF staging/Dokploy renderer validation, Governance Artifact Lifecycle & Retention runtime, optional Provider readiness/onboarding polish, cross-domain indicator runtime adoption, manual system-panel browser fixture/procedure, durable self-serve commercial/subscription operations, and the first governed AI runtime consumer. ## Runtime Guardrails - 2026-05-15 / Spec 304: Active Tenant Panel runtime is absent and guarded. `bootstrap/providers.php` registers no Tenant Panel provider, no active `TenantPanelProvider.php` exists under the platform app runtime paths, no `/admin/t` or legacy `/admin/tenants` route family is registered, and focused tests guard canonical workspace/environment link emission. Workspace remains the active Filament admin runtime context while Managed Environment surfaces stay under canonical workspace/environment routes. +- 2026-06-15 / Spec 311: Workspace / Environment Surface Scope Contract is a completed foundation. Do not reopen shell, sidebar, topbar, breadcrumb, or global workspace/environment scope unless fresh repo evidence shows regression. `environment_id` is an explicit page filter, not hidden global context. +- 2026-06-15 / Spec 377: post-productization browser reaudit is closed with follow-up; no P0/P1 productization findings remain in its accepted evidence. Remaining system-panel browser fixture/procedure work is validation follow-up, not a product runtime blocker. +- 2026-06-15 / current working-tree Spec 379: Management Report PDF generation is repo-real but runtime-gated. `TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED=false` keeps generation disabled until deployed Gotenberg/Dokploy validation passes; current workspace code evidence must not be treated as production enablement. ## Status Model @@ -60,18 +63,18 @@ ## Roadmap Coverage Summary | Roadmap Area | Product posture | Evidence Level | UI Ready | Tested | Sellable | Notes | |---|---|---:|---|---|---|---| | R1 Golden Master Governance | sellable | strong | yes | repo tests, not run | yes | Baselines, Drift, Findings und OperationRun-Truth sind breit im Produkt verankert. | -| R2 Tenant Reviews, Evidence & Control Foundation | fast sellable | strong | yes | repo tests, not run | yes | Reviews, Evidence, Review Packs, Customer Review Workspace, governance-package delivery, customer-safe Decision Summary / Review Pack inclusion, compliance interpretation overlays und Control-/Exception-Layer greifen als reale Governance-Surface zusammen; die letzte customer-safe self-serve productization bleibt offen. | +| R2 Tenant Reviews, Evidence & Control Foundation | fast sellable | strong | yes | repo tests, not run | near | Reviews, Evidence, Review Packs, Customer Review Workspace v1 completion, governance-package delivery, customer-safe Decision Summary / Review Pack inclusion, compliance interpretation overlays und Control-/Exception-Layer greifen als reale Governance-Surface zusammen; Management Report PDF bleibt bis zur Staging/Dokploy-Renderer-Validierung runtime-gated. | | Alert escalation + notification routing | sellable | strong | partial | repo tests, not run | yes | Alert-Regeln, Dispatch, Cooldown und Quiet Hours sind real. | -| Governance & Architecture Hardening | foundation-only | strong | partial | repo tests, not run | no | Viele Hardening-Slices sind bereits im Code; Spec 309 ist als `security-hardening completed` fuer RBAC role matrix und admin/system access boundary abgeschlossen, waehrend breitere Support Access Governance separat offen bleibt. | -| UI & Product Maturity Polish | implemented but not productized | strong | partial | partial repo tests, not run | no | Empty States, Navigation, Localization und read-only Review-Polish sind real, aber kein geschlossenes Theme-Completion-Signal; admin workspace navigation drift remains explicit manual-promotion follow-through. | +| Governance & Architecture Hardening | foundation-only | strong | partial | repo tests, not run | no | Viele Hardening-Slices sind bereits im Code; Spec 309 ist `security-hardening completed`, Spec 311 ist completed surface-scope foundation, und Support Access Governance bleibt getrennt von RBAC-hardening. | +| UI & Product Maturity Polish | implemented but not productized | strong | partial | repo tests, not run | no | Empty States, Navigation, Localization, read-only Review-Polish, Customer Review Workspace v1, Governance Inbox final workflow, and Spec 377 closeout evidence are repo-real; remaining system-panel browser fixture/procedure is validation follow-up. | | Secret & Security Hardening | fast sellable | strong | yes | repo tests, not run | yes | Provider-Verifikation, Permission-Diagnostics und Redaction sind belastbar. | | Baseline Drift Engine (Cutover) | sellable | strong | yes | repo tests, not run | yes | Compare- und Drift-Workflow wirken als produktive Kernfunktion. | -| R1.9 Platform Localization v1 | foundation-only | strong | yes | repo tests, not run | no | Locale-Resolver, Override/Praeferenz, Workspace-Default, Fallback und lokalisierte Notifications sind repo-real; `specs/252-platform-localization-v1/spec.md` ist die historische Foundation. | -| Product Scalability & Self-Service Foundation | fast sellable | strong | yes | repo tests, not run | yes | Onboarding, Support, Help, Entitlements, commercial lifecycle state handling und bounded support-desk handoff sind repo-real; Billing-, Trial- und Demo-Truth bleiben offen. | +| R1.9 Platform Localization v1 | implemented but not productized / repo-real | strong | yes | repo tests, not run | no | Locale-Resolver, Override/Praeferenz, Workspace-Default, Fallback, lokalisierte Notifications, and adoption/neutralization work through Specs 275 and 286 are repo-real; remaining copy QA is polish. | +| Product Scalability & Self-Service Foundation | fast sellable | strong | yes | repo tests, not run | near | Onboarding, Support, Help, Entitlements, commercial lifecycle state handling, billing-state maturity, support-access slices, and bounded support-desk handoff are repo-real; broader self-serve customer portal, trial/demo operations, and subscription ops remain productization decisions. | | R2.0 Canonical Control Catalog Foundation | foundation-only | strong | partial | repo tests, not run | no | Bereits implementiert und in Evidence/Reviews referenziert, aber kein eigenstaendiger Kundennutzen-Surface. | -| R2 Completion: customer review, support, help | fast sellable | strong | yes | repo tests, not run | yes | Customer Review Workspace, released-review detail handoff, governance-package delivery, Support Diagnostics/Requests und Help-Katalog sind repo-real, aber die finale customer-safe productization ist noch nicht vollstaendig. | +| R2 Completion: customer review, support, help | fast sellable | strong | yes | repo tests, not run | near | Customer Review Workspace v1 completion, released-review detail handoff, governance-package delivery, Support Diagnostics/Requests, support-access slices, and Help-Katalog are repo-real; production-grade management PDF output remains runtime-gated. | | Compliance Evidence Mapping v1 | implemented but not productized | strong | yes | repo tests, not run | no | Canonical control interpretation is rendered in tenant reviews and the customer review workspace, but broader framework coverage and auditor-facing mapping remain open. | -| Governance-as-a-Service Packaging v1 | implemented but not productized | strong | yes | repo tests, not run | no | Governance package status, download messaging und current review-pack reuse sind repo-real, aber recurring delivery workflows und breitere management packaging remain open. | +| Governance-as-a-Service Packaging v1 | implemented but not productized | strong | yes | repo tests, not run | no | Governance package status, download messaging, current review-pack reuse, and management-report PDF artifact flow are repo-real; recurring delivery workflows and production PDF renderer validation remain open. | | Findings Workflow v2 / Execution Layer | fast sellable | strong | yes | repo tests, not run | yes | Triage, Ownership, My Work, Intake, Governance Inbox, Exceptions und Alerts/Hygiene sind real; Cross-Tenant-Decisioning bleibt spaeter. | | Provider-missing policy visibility follow-up | not implemented | weak | no | no | no | `specs/261-provider-missing-policy-visibility/spec.md` bleibt ein schmaler policy-only Follow-up; die breitere Lifecycle-Taxonomie ist getrennt. | | Platform Operations Maturity | implemented but not productized | strong | yes | repo tests, not run | no | System Panel, Control Tower und Ops Controls sind real; CSV/Raw Drilldowns bleiben offen. | @@ -92,21 +95,23 @@ ## Implemented Capabilities | Drift findings and governance pressure | sellable | yes | yes | repo tests, not run | yes | yes | `app/Models/Finding.php`; `app/Filament/Widgets/Dashboard/RecentDriftFindings.php`; `tests/Feature/Findings/*` | | Findings inboxes and governance inbox | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Filament/Pages/Findings/MyFindingsInbox.php`; `app/Filament/Pages/Findings/FindingsIntakeQueue.php`; `app/Filament/Pages/Governance/GovernanceInbox.php`; `tests/Feature/Findings/MyWorkInboxTest.php`; `tests/Feature/Governance/*` | | Finding exceptions and risk acceptance workflow | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Models/FindingException.php`; `app/Services/Findings/FindingExceptionService.php`; `app/Filament/Resources/FindingExceptionResource.php`; `tests/Feature/Findings/FindingExceptionWorkflowTest.php` | -| Decision Register operator surface | implemented but not productized / repo-real | yes | yes | repo tests, run in Specs 306/307 | yes | no | `specs/265-decision-register-approval/spec.md`; `specs/306-decision-register-reconciliation/decision-register-reconciliation.md`; `specs/307-decision-register-evidence-operationrun-link-polish/spec.md`; `app/Filament/Pages/Governance/DecisionRegister.php`; `app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php`; `tests/Feature/Governance/DecisionRegisterPageTest.php`; `tests/Feature/Findings/FindingExceptionDecisionRegisterNavigationTest.php`; `tests/Feature/Findings/FindingExceptionDecisionRegisterBoundariesTest.php` | -| Decision Register proof/run links | fast sellable / repo-real | yes | yes | repo tests, run in Spec 307 | yes | no | `specs/307-decision-register-evidence-operationrun-link-polish/spec.md`; `specs/307-decision-register-evidence-operationrun-link-polish/tasks.md`; `app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php`; `app/Filament/Pages/Governance/DecisionRegister.php`; `tests/Unit/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilderTest.php`; `tests/Feature/Governance/DecisionRegisterPageTest.php` | +| Decision Register operator surface | implemented but not productized / repo-real | yes | yes | repo tests, not run | yes | no | `specs/265-decision-register-approval/spec.md`; `specs/306-decision-register-reconciliation/decision-register-reconciliation.md`; `specs/307-decision-register-evidence-operationrun-link-polish/spec.md`; `app/Filament/Pages/Governance/DecisionRegister.php`; `app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php`; `tests/Feature/Governance/DecisionRegisterPageTest.php`; `tests/Feature/Findings/FindingExceptionDecisionRegisterNavigationTest.php`; `tests/Feature/Findings/FindingExceptionDecisionRegisterBoundariesTest.php` | +| Decision Register proof/run links | fast sellable / repo-real | yes | yes | repo tests, not run | yes | no | `specs/307-decision-register-evidence-operationrun-link-polish/spec.md`; `specs/307-decision-register-evidence-operationrun-link-polish/tasks.md`; `app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php`; `app/Filament/Pages/Governance/DecisionRegister.php`; `tests/Unit/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilderTest.php`; `tests/Feature/Governance/DecisionRegisterPageTest.php` | +| Governance Inbox final operator workflow | fast sellable / repo-real / implemented | yes | yes | repo tests, not run | yes | near | `specs/327-governance-inbox-decision-first-workbench-productization/spec.md`; `specs/346-governance-inbox-final-operator-workflow/spec.md`; `app/Filament/Pages/Governance/GovernanceInbox.php`; `tests/Feature/Governance/*` | | Restore workflow with safety gates | sellable | yes | yes | repo tests, not run | yes | yes | `app/Models/OperationRun.php`; restore gates and tests in `tests/Feature/Restore/*` | | Evidence snapshots | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Models/EvidenceSnapshot.php`; `app/Services/Evidence/EvidenceSnapshotService.php`; `tests/Feature/Evidence/*` | | Tenant reviews | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Models/TenantReview.php`; `app/Services/TenantReviews/TenantReviewService.php`; `tests/Feature/TenantReview/*` | -| Review pack generation and export | fast sellable | yes | yes | repo tests, not run here; Spec 308 validation recorded | yes | yes | `specs/109-review-pack-export/spec.md`; `specs/308-decision-register-summary-review-pack/plan.md`; `app/Models/ReviewPack.php`; `app/Services/ReviewPackService.php`; `app/Jobs/GenerateReviewPackJob.php`; `tests/Feature/ReviewPack/*` | -| Decision Summary in reviews and Review Packs | fast sellable / repo-real | yes | yes | repo tests, run in Spec 308 | yes | yes | `specs/308-decision-register-summary-review-pack/spec.md`; `specs/308-decision-register-summary-review-pack/plan.md`; `app/Services/EnvironmentReviews/EnvironmentReviewComposer.php`; `app/Jobs/GenerateReviewPackJob.php`; `tests/Feature/EnvironmentReview/EnvironmentReviewExecutivePackTest.php`; `tests/Feature/ReviewPack/EnvironmentReviewDerivedReviewPackTest.php` | -| Customer review workspace | fast sellable / repo-real, v1 completion open gap | yes | yes | repo tests, not run here; Spec 308 validation recorded for summary/pack inclusion | yes | no | `specs/258-customer-review-productization/spec.md`; `specs/308-decision-register-summary-review-pack/spec.md`; `app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`; `tests/Feature/Reviews/*`; `tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php` | +| Review pack generation and export | fast sellable | yes | yes | repo tests, not run | yes | yes | `specs/109-review-pack-export/spec.md`; `specs/308-decision-register-summary-review-pack/plan.md`; `app/Models/ReviewPack.php`; `app/Services/ReviewPackService.php`; `app/Jobs/GenerateReviewPackJob.php`; `tests/Feature/ReviewPack/*` | +| Decision Summary in reviews and Review Packs | fast sellable / repo-real | yes | yes | repo tests, not run | yes | yes | `specs/308-decision-register-summary-review-pack/spec.md`; `specs/308-decision-register-summary-review-pack/plan.md`; `app/Services/EnvironmentReviews/EnvironmentReviewComposer.php`; `app/Jobs/GenerateReviewPackJob.php`; `tests/Feature/EnvironmentReview/EnvironmentReviewExecutivePackTest.php`; `tests/Feature/ReviewPack/EnvironmentReviewDerivedReviewPackTest.php` | +| Customer review workspace | fast sellable / repo-real / implemented | yes | yes | repo tests, not run | yes | near | `specs/258-customer-review-productization/spec.md`; `specs/312-customer-review-workspace-v1-completion/spec.md`; `specs/342-customer-review-workspace-final-consumption-productization/spec.md`; `app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`; `tests/Feature/Reviews/*`; `tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php` | +| Management Report PDF generation | implemented but not productized / repo-real / open gap | yes | yes | repo tests, not run | yes | no | `specs/378-management-report-pdf-v1/spec.md`; `specs/379-management-report-pdf-runtime/spec.md`; `app/Services/ManagementReports/ManagementReportPdfService.php`; `app/Jobs/GenerateManagementReportPdfJob.php`; `app/Http/Controllers/ManagementReportPdfDownloadController.php`; `app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php`; `tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`; `tests/Browser/Spec379ManagementReportPdfSmokeTest.php`; runtime gate requires staging/Dokploy validation before production enablement | | Governance package delivery surface | implemented but not productized | yes | yes | repo tests, not run | yes | no | `specs/260-governance-service-packaging/spec.md`; `app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`; `app/Filament/Resources/TenantReviewResource.php`; `tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php`; `tests/Feature/TenantReview/TenantReviewExplanationSurfaceTest.php` | | Compliance evidence mapping overlay | implemented but not productized | yes | yes | repo tests, not run | partial | no | `specs/259-compliance-evidence-mapping/spec.md`; `app/Support/Governance/Controls/ComplianceEvidenceMappingV1.php`; `app/Services/TenantReviews/TenantReviewSectionFactory.php`; `tests/Feature/TenantReview/TenantReviewCanonicalControlReferenceTest.php` | | Alerts and notification routing | sellable | yes | partial | repo tests, not run | yes | yes | `app/Services/Alerts/AlertDispatchService.php`; `tests/Feature/*Alert*` | | Provider health, onboarding readiness and required permissions | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Jobs/ProviderConnectionHealthCheckJob.php`; `app/Services/Onboarding/OnboardingLifecycleService.php`; `app/Filament/Pages/TenantRequiredPermissions.php` | | Permission posture reporting | sellable | yes | yes | repo tests, not run | yes | yes | `app/Services/PermissionPosture/PermissionPostureFindingGenerator.php`; `tests/Feature/PermissionPosture/*` | | Entra admin roles reporting | sellable | yes | yes | repo tests, not run | yes | yes | `app/Services/EntraAdminRoles/EntraAdminRolesReportService.php`; `tests/Feature/EntraAdminRoles/*` | -| Stored reports substrate | foundation-only | yes | partial | repo tests, not run | partial | no | `app/Models/StoredReport.php`; `tests/Feature/PermissionPosture/StoredReportModelTest.php`; `tests/Feature/EntraAdminRoles/StoredReportFingerprintTest.php` | +| Stored reports substrate and artifact surface | implemented but not productized / repo-real | yes | partial | repo tests, not run | partial | no | `specs/277-stored-reports-surface/spec.md`; `app/Models/StoredReport.php`; current working-tree Spec 379 management PDF artifact fields; `tests/Feature/PermissionPosture/StoredReportModelTest.php`; `tests/Feature/EntraAdminRoles/StoredReportFingerprintTest.php`; `tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php` | | Support diagnostics | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php`; `app/Filament/Pages/TenantDashboard.php`; `tests/Feature/SupportDiagnostics/*` | | In-app support requests | fast sellable | yes | yes | repo tests, not run | yes | yes | `app/Models/SupportRequest.php`; `app/Support/SupportRequests/*`; `tests/Feature/SupportRequests/*` | | External support-desk handoff | implemented but not productized | yes | yes | repo tests, not run | yes | no | `app/Support/SupportRequests/ExternalSupportDeskHandoffService.php`; `app/Support/SupportRequests/SupportRequestSubmissionService.php`; `tests/Unit/Support/SupportRequests/ExternalSupportDeskHandoffServiceTest.php` | @@ -117,9 +122,12 @@ ## Implemented Capabilities | Operational controls | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Models/OperationalControlActivation.php`; `app/Support/OperationalControls/*`; `tests/Feature/System/OpsControls/*` | | Governed AI policy foundation | foundation-only | yes | partial | repo tests, not run | yes | no | `specs/248-private-ai-policy-foundation/spec.md`; `app/Support/Ai/AiUseCaseCatalog.php`; `app/Support/Ai/GovernedAiExecutionBoundary.php`; `app/Support/Ai/AiDecisionAuditMetadataFactory.php`; `app/Filament/Pages/Settings/WorkspaceSettings.php`; `tests/Unit/Support/Ai/*`; `tests/Feature/SettingsFoundation/WorkspaceAiPolicySettingsTest.php`; `tests/Feature/System/OpsControls/AiExecutionOperationalControlTest.php` | | Workspace entitlements | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Services/Entitlements/WorkspaceEntitlementResolver.php`; `tests/Feature/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.php` | -| Commercial lifecycle state handling | foundation-only | yes | yes | repo tests, not run | yes | no | `specs/251-commercial-entitlements-billing-state/spec.md`; `app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php`; `app/Filament/System/Pages/Directory/ViewWorkspace.php`; `tests/Feature/System/ViewWorkspaceEntitlementsTest.php`; `tests/Unit/Entitlements/WorkspaceCommercialLifecycleResolverTest.php` | +| Commercial lifecycle state handling | implemented but not productized / repo-real | yes | yes | repo tests, not run | yes | no | `specs/251-commercial-entitlements-billing-state/spec.md`; `specs/274-billing-subscription-truth/spec.md`; `app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php`; `app/Filament/System/Pages/Directory/ViewWorkspace.php`; `tests/Feature/System/ViewWorkspaceEntitlementsTest.php`; `tests/Unit/Entitlements/WorkspaceCommercialLifecycleResolverTest.php` | | Capability-first RBAC | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Services/Auth/CapabilityResolver.php`; `app/Services/Auth/RoleCapabilityMap.php`; many `tests/Feature/Rbac/*` | -| RBAC role matrix and access boundary hardening | security-hardening completed / repo-real | yes | yes | repo tests, run in Spec 309 | yes | no | `specs/309-rbac-role-matrix-access-boundary-audit/tasks.md`; `app/Services/Auth/WorkspaceRoleCapabilityMap.php`; `app/Models/User.php`; `tests/Feature/Rbac/RoleMatrix/ManagerAccessTest.php`; `tests/Feature/Rbac/PanelAccess/AdminPanelAccessBoundaryTest.php`; `tests/Feature/Rbac/PanelAccess/SystemPanelAccessBoundaryTest.php` | +| RBAC role matrix and access boundary hardening | security-hardening completed / repo-real | yes | yes | repo tests, not run | yes | no | `specs/309-rbac-role-matrix-access-boundary-audit/tasks.md`; `app/Services/Auth/WorkspaceRoleCapabilityMap.php`; `app/Models/User.php`; `tests/Feature/Rbac/RoleMatrix/ManagerAccessTest.php`; `tests/Feature/Rbac/PanelAccess/AdminPanelAccessBoundaryTest.php`; `tests/Feature/Rbac/PanelAccess/SystemPanelAccessBoundaryTest.php` | +| Workspace / Environment Surface Scope Contract | foundation-only / repo-real / implemented | yes | yes | repo tests, not run | yes | no | `specs/311-workspace-environment-surface-scope-contract/spec.md`; `bootstrap/providers.php`; `routes/web.php`; active `/admin` and `/system`; no active `/admin/t`; `environment_id` filter semantics | +| Provider Connection scope hardening | security-hardening completed / repo-real | yes | yes | repo tests, not run | yes | no | `specs/339-provider-connection-scope-hardening/spec.md`; `app/Filament/Resources/ProviderConnectionResource.php`; `app/Policies/ProviderConnectionPolicy.php`; `tests/Feature/ProviderConnections/*` | +| Canonical link / query cleanup | implemented / repo-real | yes | yes | repo tests, not run | partial | no | `specs/341-canonical-link-query-cleanup/spec.md`; `app/Support/Workspaces/WorkspaceHubNavigation.php`; `app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`; route/link guard tests | | Audit log foundation | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Models/AuditLog.php`; `app/Services/Audit/WorkspaceAuditLogger.php`; many audit-focused feature tests | | Canonical control catalog | foundation-only | yes | partial | repo tests, not run | partial | no | `app/Support/Governance/Controls/CanonicalControlCatalog.php`; `config/canonical_controls.php`; `tests/Unit/Governance/*` | | Portfolio triage continuity | foundation-only | yes | yes | repo tests, not run | yes | no | `app/Services/PortfolioTriage/TenantTriageReviewService.php`; `app/Support/PortfolioTriage/*`; `tests/Feature/Filament/TenantRegistryTriageReviewStateTest.php` | @@ -130,36 +138,34 @@ ## Foundation-Only Capabilities - OperationRun truth and canonical operation typing: starke Execution-Foundation, aber kein eigenstaendiger Kundennutzen-Surface. - Audit log foundation: breit genutzt und wichtig fuer Governance, aber allein nicht verkaufbar. - Capability-first RBAC: belastbar und testnah, bleibt aber Enablement-Layer; Spec 309 ist die abgeschlossene `security-hardening completed` Korrektur fuer Owner-only membership management und admin/system panel boundaries, nicht die Support Access Governance Productization. -- Workspace entitlements und commercial lifecycle policy engine: reale Gate-, Lifecycle- und Override-Logik, aber noch keine volle Billing-/Contract-Ops story. +- Workspace entitlements und commercial lifecycle policy engine: reale Gate-, Lifecycle-, Billing-State- und Override-Logik; volle Self-Service-Billing-/Subscription-Ops bleiben spaetere Produktisierung. - Canonical control catalog: starke semantische Foundation fuer Evidence, Findings und Reviews. -- Stored reports substrate: wichtig fuer Reports, Evidence und Diagnostics, aber kein eigenstaendiges Produktversprechen. +- Stored reports substrate: wichtig fuer Reports, Evidence, Diagnostics und Management Report PDF artifacts; Produktreife haengt weiter an lifecycle/retention semantics und Runtime-Validierung. - Evidence snapshot substrate: tragende technische Basis fuer Reviews und Exports. - Localization foundation: resolved locale precedence, Workspace-Default, User-Praeferenz/Override und Notification-Formatting sind real, aber Enablement statt eigener Produkt-Surface. - Governed AI policy foundation: Use-Case-Katalog, Boundary, Audit-Metadata, Workspace-Policy-Surface und Ops-Control-Integration sind repo-real, aber noch ohne ersten Runtime-Consumer. +- Workspace / Environment Surface Scope Contract: completed foundation fuer route-owned scope; nicht als offener Produkt-Slice behandeln. - Operational control registry and evaluator: starke Safety-Control-Foundation, primar operatorseitig. - Product telemetry und customer health scoring: reale operatorseitige SaaS-Operations-Layer, aber noch keine eigenstaendige sellable Oberflaeche. - Portfolio triage continuity: sinnvoller Multi-Tenant-Unterbau, aber noch kein vollstaendiges Portfolio-Produkt. ## Fast-Sellable Or Not-Yet-Productized Capabilities -- Customer-facing review consumption: Tenant Reviews, Evidence Snapshots, Review Packs, the Customer Review Workspace, the customer-safe released-review detail mode, governance-package delivery cues, Spec 308 Decision Summary / Review Pack inclusion, compliance interpretation overlays, and commercial-lifecycle-aware access states are repo-real; Customer Review Workspace v1 Completion remains an `open gap`. +- Customer-facing review consumption: Tenant Reviews, Evidence Snapshots, Review Packs, the Customer Review Workspace, the customer-safe released-review detail mode, governance-package delivery cues, Spec 308 Decision Summary / Review Pack inclusion, compliance interpretation overlays, commercial-lifecycle-aware access states, and post-311 Customer Review Workspace v1 completion are repo-real; future external portal/consumption would be a separate product decision. - Findings Workflow v2: Triage, Assignment, My Work, Intake, Governance Inbox, Exceptions, notifications, and the three queue-facing cleanup/hardening follow-through packages are now repo-backed; later cross-tenant action layers remain separate work. -- Decision Register: Spec 265 operator register runtime, Spec 306 reconciliation, Spec 307 direct evidence/report plus source/evidence OperationRun proof-link polish, and Spec 308 customer-safe Decision Summary / Review Pack inclusion are repo-backed; the remaining gap is broader Decision-Based Governance Inbox / Customer Review Workspace completion, not a Greenfield v1. -- Product scalability and self-service: Onboarding, Support, Help, Entitlements, commercial lifecycle state handling, and external support-desk handoff are repo-real; broader trial/demo and billing-subscription truth still remain. +- Decision Register and Governance Inbox: Spec 265 operator register runtime, Spec 306 reconciliation, Spec 307 direct evidence/report plus source/evidence OperationRun proof-link polish, Spec 308 customer-safe Decision Summary / Review Pack inclusion, and Specs 327/346 Governance Inbox productization are repo-backed; do not treat Decision-Based Governance Inbox v1 as Greenfield. +- Product scalability and self-service: Onboarding, Support, Help, Entitlements, commercial lifecycle state handling, support-access slices, billing-state maturity, and external support-desk handoff are repo-real; broader trial/demo, self-serve subscription operations, and customer portal packaging remain. +- Management reporting: current working-tree management-report PDF runtime and artifact flow are repo-real, but production enablement remains gated on staging/Dokploy renderer validation. - MSP portfolio operations: Portfolio-Triage plus cross-tenant compare preview and promotion preflight are repo-real; actual promotion execution and broader portfolio action orchestration remain open. - Platform operations maturity: Control Tower und Ops Controls sind stark, aber einige geplante operatorseitige Drilldowns/Exports fehlen noch. - Product knowledge rollout: Help-Katalog und Resolver sind real, aber noch nicht breit genug adoptiert fuer "fertig". ## Not Implemented -- Auditor Pack Delivery & Executive Export v1 -- Cross-Tenant Promotion Execution v1 - Governance Artifact Lifecycle & Retention v1 -- Customer-Facing Localization Adoption v1 -- Billing & Subscription Truth Layer v1 -- Stored Reports Surface v1 -- Workspace & Tenant Closure Lifecycle v1 -- Enterprise Access Boundary & Support Access Governance v1 +- Management Report PDF staging/Dokploy runtime validation and production enablement +- Durable self-serve Billing / Subscription Operations beyond existing entitlement and lifecycle truth +- Workspace & Tenant Closure Lifecycle runtime follow-through beyond existing taxonomy/current slices - First Governed AI Runtime Consumer v1 - Human-in-the-Loop Autonomous Governance - Standardization & Policy Quality / Intune Linting @@ -171,10 +177,10 @@ ## Release Readiness | Release / Theme | Readiness | Notes | |---|---|---| | R1 Golden Master Governance | sellable | Die zentrale Governance- und Execution-Layer ist repo-verifiziert und breit adoptiert. | -| R2 Tenant Reviews & Evidence Packs | fast sellable | Reviews, Evidence Snapshots, Review Packs, Customer Review Workspace, released-review detail handoff, governance-package delivery, compliance interpretation overlays und Exception-/Accepted-Risk-Workflow sind repo-real; die finale customer-safe Productization bleibt als sellability follow-up offen. | +| R2 Tenant Reviews & Evidence Packs | fast sellable | Reviews, Evidence Snapshots, Review Packs, Customer Review Workspace v1 completion, released-review detail handoff, governance-package delivery, compliance interpretation overlays, Exception-/Accepted-Risk-Workflow und Management Report PDF runtime work are repo-real; PDF production enablement remains gated by staging/Dokploy renderer validation. | | R3 MSP Portfolio OS | implemented but not productized | Portfolio-Triage sowie canonical compare preview/preflight sind da, aber actual promotion execution und portfolio-weite Action-Layer fehlen weiter. | | Compliance Evidence Mapping v1 | implemented but not productized | Compliance interpretation overlays sind repo-real in Tenant Reviews und Customer Review Workspace, aber breitere Framework-Abdeckung und auditor-facing mapping fehlen weiter. | -| Governance-as-a-Service Packaging v1 | implemented but not productized | Governance package status, delivery messaging und current review-pack reuse sind repo-real; eine wiederholbare management-taugliche Packaging-Workflow-Layer ist nicht vollstaendig. | +| Governance-as-a-Service Packaging v1 | implemented but not productized | Governance package status, delivery messaging, current review-pack reuse, and management-report PDF artifact flow are repo-real; recurring delivery workflow and production renderer validation remain incomplete. | ## Commercial Readiness @@ -238,42 +244,29 @@ ## Open Gaps & Blockers | Gap | Type | Impact | Roadmap Area | Recommended Spec | |---|---|---|---|---| | No safe automatic next-best-prep target is currently active | Planning boundary | `docs/product/spec-candidates.md` now keeps the active queue empty, so the next slice must be promoted deliberately instead of selected automatically | Product planning / queue hygiene | none - require explicit manual promotion | -| Workspace-first / ManagedEnvironment core cutover is not started | Strategic architecture blocker | The repo still centers many governance, support, and portfolio surfaces on `Tenant` semantics, so future multi-provider work would otherwise accumulate more tenant- and Microsoft-centric core coupling | Platform core / provider-neutral posture | `Workspace-first / ManagedEnvironment Core Cutover` pack in `docs/product/spec-candidates.md` (planned as Specs 279-287) | -| Auditor-ready executive export is still missing | Productization blocker | Review truth remains short of auditor-/executive-ready delivery, even though the dedicated follow-through is now spec-backed | R2 review delivery | `specs/263-auditor-pack-executive-export/spec.md` | -| Cross-tenant promotion execution is still missing | Product blocker | Compare preview and preflight are repo-real, but the actual portfolio action remains absent even though the execution package is now spec-backed | MSP Portfolio & Operations | `specs/264-cross-tenant-promotion-execution/spec.md` | -| Customer Review Workspace v1 completion is still open | Productization gap | Repo-real review, Decision Summary, and Review Pack inclusion now exist, but a complete customer-safe self-serve workspace with calm status, reason, impact, evidence basis, accepted risks, decision summary, review-pack download, and one primary next action still needs productization | Customer review consumption | `311-customer-review-workspace-v1-completion` | -| Decision-Based Governance Inbox v1 remains open | Productization gap | The operator Decision Register is repo-real and not Greenfield, but a broader decision-centered governance inbox remains separate from the completed proof/link and customer-safe summary slices | Decision-based operating | `313-decision-based-governance-inbox-v1` | +| Management Report PDF production enablement remains gated | Runtime validation blocker | Current-branch Spec 379 implements the generation/download/audit flow, but staging/Dokploy Gotenberg validation must pass before enabling production runtime | Management reporting / review delivery | current Spec 379 follow-through, no new feature spec | | Governance-artifact lifecycle runtime is still missing | Trust / auditability blocker | Lifecycle taxonomy and point retention rules exist, but governance artifacts still lack immutable-reference, hold, export, delete, and suspended/read-only runtime semantics | Lifecycle governance / enterprise trust | `Governance Artifact Lifecycle & Retention v1` | -| Cross-domain progress and indicator semantics guardrail is still missing | UX / trust guardrail | Bars, percentages, scores, readiness, risk, usage, and generation-state hints still lack one shared taxonomy and standards layer above the OperationRun-specific rules | UI semantics / product trust | `Cross-Domain Progress / Indicator Semantics candidate group` | -| Residual admin workspace navigation contract drift may remain | UX / IA repair follow-through | Specs 301-304 now cover Inventory cutover, the tenant-owned surface audit, Entra Groups cutover, and tenant-panel dead-code retirement. The remaining risk is shared contract drift between workspace-home cleanliness and environment-bound admin visibility if new regressions appear. | UI maturity / admin runtime contract | `navigation-contract-split`, only if post-Spec 301-304 drift remains | -| Customer-facing localization adoption is incomplete | Productization blocker | Locale groundwork is repo-real, but customer-safe adoption remains incomplete | Localization / review productization | `Customer-Facing Localization Adoption v1` | -| Billing and subscription truth is missing | Commercial blocker | Entitlements and lifecycle state handling stop short of a durable billing/subscription truth layer | Commercial readiness | `Billing & Subscription Truth Layer v1` | -| Stored reports still lack a clear product surface | Product blocker | Retained evidence and review artifacts remain harder to consume than they should be | Reports / evidence consumption | `Stored Reports Surface v1` | -| Workspace and tenant closure follow-through is not started | Strategic blocker | The taxonomy exists, but closure/runtime semantics are not yet productized | Lifecycle governance / enterprise trust | `Workspace & Tenant Closure Lifecycle v1` | -| Support-access governance is still missing | Access governance blocker | Break-glass and support access seams exist, but customer-visible TTL, reason, approval, and export semantics are not productized | Enterprise access boundary | `Enterprise Access Boundary & Support Access Governance v1` | +| Provider readiness / onboarding polish may remain | Optional productization gap | Provider scope is hardened, but setup and resolution guidance should be promoted only if fresh operator evidence shows friction | Provider readiness | manual promotion only | +| Cross-domain progress and indicator runtime adoption may remain | UX / trust guardrail | Spec 278 provides the standardization path, but runtime adoption should follow only where actual indicator drift is visible | UI semantics / product trust | `Cross-Domain Progress / Indicator Semantics candidate group` | +| System-panel browser fixture/procedure remains manual | Validation follow-up | Spec 377 closed post-productization browser re-audit with no P0/P1 findings, but system-panel in-app browser fixture coverage remains procedure-dependent | Release validation | manual fixture/procedure follow-up | +| Durable self-serve subscription operations are not productized | Commercial productization gap | Entitlement and billing-state truth exist, but customer self-serve subscription operations, payment/invoice workflows, or commercial portal behavior remain outside the current product | Commercial readiness | manual promotion only | +| Future customer portal/external consumption is not productized | Productization decision | Customer Review Workspace v1 is repo-real in the admin context; a broader external customer portal is separate work | Customer consumption | manual promotion only | | First governed AI runtime consumer is missing | Architecture blocker | The policy foundation exists, but there is no bounded runtime consumer proving the model end-to-end | Governed AI follow-through | `First Governed AI Runtime Consumer v1` | ## Recommended Manual Promotions -- `Cross-Domain Progress / Indicator Semantics candidate group` -> anchored by `specs/268-operationrun-activity-feedback/spec.md`, `specs/270-operationrun-progress-contract/spec.md`, `specs/271-counted-progress-rollout/spec.md`, `specs/272-operationrun-phase-composite-progress/spec.md`, `docs/ui/tenantpilot-enterprise-ui-standards.md`, and the current progress-like UI seams called out in `docs/product/spec-candidates.md` -- `Admin Workspace Navigation & Environment-owned Surface Repair candidate group` -> anchored by `apps/platform/app/Filament/Clusters/Inventory/InventoryCluster.php`, `apps/platform/app/Filament/Pages/InventoryCoverage.php`, `apps/platform/app/Filament/Resources/InventoryItemResource.php`, `apps/platform/app/Filament/Resources/EntraGroupResource.php`, `apps/platform/app/Filament/Concerns/WorkspaceScopedEnvironmentRoutes.php`, `apps/platform/app/Support/OperateHub/OperateHubShell.php`, and the navigation/runtime tests called out in `docs/product/spec-candidates.md`; Specs 301-304 now cover Inventory cutover, route-audit prep, groups cutover, and tenant-panel dead-code retirement, so only `navigation-contract-split` remains as a conditional follow-through if drift persists -- `Workspace-first / ManagedEnvironment Core Cutover` pack -> anchored by `docs/product/spec-candidates.md`, `docs/product/roadmap.md`, `docs/product/implementation-ledger.md`, and the tenant-centric platform seams already visible across review, support, portfolio, and governance surfaces; keep it as a clean development-stage cutover pack rather than a compatibility-layer program -- `Customer Review Workspace v1 Completion` -> anchored by `specs/258-customer-review-productization/spec.md`, `specs/308-decision-register-summary-review-pack/spec.md`, `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`, and the review/workspace tests -- `Localization v1 Customer-facing Surfaces` -> anchored by `specs/252-platform-localization-v1/spec.md`, `specs/258-customer-review-productization/spec.md`, and `specs/260-governance-service-packaging/spec.md` -- `Decision-Based Governance Inbox v1` -> anchored by `specs/250-decision-governance-inbox/spec.md`, `specs/257-governance-decision-convergence/spec.md`, `specs/265-decision-register-approval/spec.md`, `specs/306-decision-register-reconciliation/decision-register-reconciliation.md`, `specs/307-decision-register-evidence-operationrun-link-polish/spec.md`, `specs/308-decision-register-summary-review-pack/spec.md`, `apps/platform/app/Filament/Pages/Governance/DecisionRegister.php`, `apps/platform/app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php`, and the focused Decision Register tests -- `Governance Artifact Lifecycle & Retention v1` -> anchored by `specs/158-artifact-truth-semantics/spec.md`, `specs/262-lifecycle-governance-taxonomy/spec.md`, and `docs/product/standards/lifecycle-governance.md` -- `Billing & Subscription Truth Layer v1` -> anchored by `specs/247-plans-entitlements-billing-readiness/spec.md` and `specs/251-commercial-entitlements-billing-state/spec.md` -- `Customer-Facing Localization Adoption v1` -> anchored by `specs/252-platform-localization-v1/spec.md`, `specs/258-customer-review-productization/spec.md`, and `specs/260-governance-service-packaging/spec.md` -- `Enterprise Access Boundary & Support Access Governance v1` -> anchored by `docs/audits/2026-03-09-enterprise-rbac-scope-audit.md`, `docs/HANDOVER.md`, `specs/065-tenant-rbac-v1/spec.md`, and `specs/066-rbac-ui-enforcement-helper/spec.md` -- `Stored Reports Surface v1` -> anchored by `specs/153-evidence-domain-foundation/spec.md`, `specs/155-tenant-review-layer/spec.md`, `specs/260-governance-service-packaging/spec.md`, and `docs/product/implementation-ledger.md` -- `Workspace & Tenant Closure Lifecycle v1` -> anchored by `specs/262-lifecycle-governance-taxonomy/spec.md` -- `First Governed AI Runtime Consumer v1` -> anchored by `specs/248-private-ai-policy-foundation/spec.md` +- `Management Report PDF staging/runtime validation and release hardening` -> anchored by `specs/378-management-report-pdf-v1/spec.md`, `specs/379-management-report-pdf-runtime/spec.md`, `apps/platform/app/Services/ManagementReports/ManagementReportPdfService.php`, `apps/platform/app/Jobs/GenerateManagementReportPdfJob.php`, `apps/platform/app/Http/Controllers/ManagementReportPdfDownloadController.php`, `apps/platform/app/Models/StoredReport.php`, and the Spec 379 runtime-validation artifacts. +- `Governance Artifact Lifecycle & Retention runtime` -> anchored by `specs/158-artifact-truth-semantics/spec.md`, `specs/262-lifecycle-governance-taxonomy/spec.md`, `specs/267-artifact-lifecycle-retention/spec.md`, and `docs/product/standards/lifecycle-governance.md`. +- `Provider readiness / onboarding productization` -> anchored by `specs/281-provider-connection-provider-scope-microsoft-profile-extraction/spec.md`, `specs/339-provider-connection-scope-hardening/spec.md`, `specs/353-provider-connections-resolution-guidance-v1/spec.md`, `apps/platform/app/Filament/Resources/ProviderConnectionResource.php`, and `apps/platform/app/Policies/ProviderConnectionPolicy.php`; promote only for fresh UX friction, not scope authority. +- `Cross-Domain Progress / Indicator runtime follow-through` -> anchored by `specs/278-cross-domain-progress-indicator-semantics/spec.md`, `docs/ui/tenantpilot-enterprise-ui-standards.md`, and current progress-like UI seams called out in `docs/product/spec-candidates.md`. +- `Manual system-panel browser fixture or audit procedure` -> anchored by `specs/376-*`, `specs/377-post-productization-browser-reaudit-closeout-gate/artifacts/closeout-decision.md`, and the system-panel authentication/fixture limits recorded there. +- `First Governed AI Runtime Consumer v1` -> anchored by `specs/248-private-ai-policy-foundation/spec.md`. ## Roadmap Drift Notes -- `docs/product/roadmap.md` and `docs/product/spec-candidates.md` are aligned through 2026-05-15, including Spec 304 tenant-panel dead-code retirement, Spec 306 Decision Register reconciliation, Spec 307 proof-link polish, Spec 308 customer-safe Decision Summary / Review Pack inclusion, Spec 309 RBAC role/access-boundary hardening, the earlier admin workspace navigation / tenant-owned surface repair candidate intake, the cross-domain indicator candidate intake, the current manual-promotion backlog, and the resolved ledger conflict state. -- The remaining documentation risk is no longer queue drift alone; it is understating or overstating still-open follow-through slices such as Customer Review Workspace v1 completion, localization adoption, Decision-Based Governance Inbox completion, admin workspace navigation repair, auditor-ready export, promotion execution, governance decision workflow, cross-domain indicator semantics, billing/subscription truth, stored reports surface, Support Access Governance, and the first governed AI runtime consumer. -- This ledger therefore treats review-driven governance and portfolio preparation as `fast sellable` or `implemented but not productized`, not `sellable`, until those explicit manual-promotion slices land. +- `docs/product/roadmap.md` and `docs/product/spec-candidates.md` are aligned through 2026-06-15, including Spec 311 completed surface-scope foundation, Specs 312/342/343/344/349/351/372 Customer Review Workspace v1 completion lineage, Specs 327/346 Governance Inbox lineage, Specs 339/341 provider/link cleanup, Spec 377 closeout evidence, and current working-tree Spec 379 runtime-gated Management Report PDF status. +- The remaining documentation risk is overstating current working-tree or local runtime evidence as production-ready. Management Report PDF remains disabled by runtime gate until staging/Dokploy renderer validation passes. +- This ledger therefore treats review-driven governance as `fast sellable`, Management Report PDF as `implemented but not productized`, and broad shell/scope/Decision Register/customer-review foundations as historical/completed rather than active candidates. - Tests referenced here remain repo-present only. They were not executed for this ledger update. ## Evidence Sources diff --git a/docs/product/roadmap.md b/docs/product/roadmap.md index e97000e4..b7df760d 100644 --- a/docs/product/roadmap.md +++ b/docs/product/roadmap.md @@ -1,10 +1,10 @@ # Product Roadmap > **Status:** Active -> **Last reviewed:** 2026-05-15 +> **Last reviewed:** 2026-06-15 > **Use for:** Current product roadmap, release themes, and prioritization context > **Do not use for:** Implementation truth, spec completion status, or delivery guarantees without repo verification -> **Scoped maintenance:** 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Decision Register proof-link update after Spec 307; 2026-05-15 Decision Register reconciliation update after Spec 306; 2026-05-15 tenant-owned admin surface follow-through sync after Specs 301-304; 2026-05-12 roadmap alignment after the admin workspace navigation and tenant-owned surface repair candidate intake from the repo-verified navigation/panel audit; 2026-05-06 roadmap cleanup after ledger conflict resolution and cross-domain progress / indicator semantics candidate intake; 2026-05-02 repo-based roadmap drift correction, manual-promotion backlog alignment, and enterprise-SaaS deep-research calibration against current specs, standards, and product-truth docs. +> **Scoped maintenance:** 2026-06-15 repo-truth sync after Specs 311-379, including completed Spec 311 surface-scope foundation, post-311 candidate reconciliation, Spec 377 UI closeout, and current working-tree Spec 379 management-report PDF runtime-gated status; 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Decision Register proof-link update after Spec 307; 2026-05-15 Decision Register reconciliation update after Spec 306; 2026-05-15 tenant-owned admin surface follow-through sync after Specs 301-304; 2026-05-12 roadmap alignment after the admin workspace navigation and tenant-owned surface repair candidate intake from the repo-verified navigation/panel audit; 2026-05-06 roadmap cleanup after ledger conflict resolution and cross-domain progress / indicator semantics candidate intake; 2026-05-02 repo-based roadmap drift correction, manual-promotion backlog alignment, and enterprise-SaaS deep-research calibration against current specs, standards, and product-truth docs. > > Strategic thematic blocks and release trajectory. > This is the "big picture" — not individual specs. @@ -24,33 +24,36 @@ ## Release History ## Current Productization & Moat Priorities -This is the repo-based prioritization overlay for the next sellable lanes. The bottleneck is no longer raw backend truth alone. The next roadmap slices should make existing governance foundations customer-safe, decision-centered, auditable, and MSP-sellable before opening more backend-only islands. +This is the repo-based strategic overlay for the next sellable lanes. The bottleneck is no longer shell scope, Decision Register proof links, broad Customer Review Workspace v1, provider connection scope, canonical link cleanup, localization foundation, or commercial entitlement foundation. Those lanes are repo-real, specced, or completed and should not be reopened as Greenfield roadmap priorities. -Post-307/308/309 product truth: +Post-311 through current working-tree Spec 379 product truth: -- Decision Register operator surface is repo-real and historical/non-Greenfield through Specs 265 and 306. -- Decision Register proof/run links are repo-real after Spec 307. -- Customer-safe Decision Summary in reviews and Review Pack inclusion are repo-real after Spec 308. -- RBAC role matrix and admin/system access boundary hardening are `security-hardening completed` after Spec 309. -- Customer Review Workspace v1 Completion remains open; Spec 308 did not turn the whole customer review workspace into a fully sellable self-serve product. -- Support Access Governance remains separate from Spec 309 hardening. +- Spec 311 is a completed workspace/environment surface-scope foundation. Shell, sidebar, topbar, breadcrumb, and global workspace/environment scope are not open roadmap work unless fresh repo evidence shows regression. +- `/admin` and `/system` remain the active panels. The retired Tenant Panel and `/admin/t` route family must not reappear as a recommended runtime. +- `environment_id` is an explicit page filter, not hidden global context. Workspace-wide versus environment-bound scope stays route-owned. +- Customer Review Workspace v1 completion is historical/completed through Specs 312, 342, 343, 344, 349, 351, and 372; future external/customer-portal consumption is a separate roadmap decision. +- Decision Register, proof/run links, customer-safe summary/review-pack inclusion, and the broader Governance Inbox operator path are repo-real through Specs 265, 306, 307, 308, 327, and 346. Do not reopen the Decision Register. +- Provider connection scope hardening and canonical link/query cleanup are completed through Specs 339 and 341. +- Support Access Governance remains separate from Spec 309 RBAC hardening, but the current support-access slice should not be treated as an active Greenfield candidate without fresh product evidence. +- Commercial entitlement/lifecycle truth is repo-real through Specs 247, 251, and 274. Durable customer self-serve billing/subscription operations remain a later commercial/productization decision, not an open foundation. +- Management-report PDF generation is repo-real in the current working tree, but remains runtime-gated until staging/Dokploy renderer validation enables it; it is not yet a fully productized production capability. | Order | Theme | Alignment status | Repo truth | Why now | Queue posture | |---|---|---|---|---|---| -| 1 | Customer Review Workspace v1 Completion | repo-real foundation, open gap, P1 | Customer-safe review consumption is repo-real through Specs 249, 258, 259, 260, 263, and Spec 308, but a complete self-serve workspace with calm status, reason, impact, evidence basis, accepted risks, decision summary, review-pack download, and one primary next action remains open | clearest sellability lever for Governance-of-Record without creating a parallel customer portal | recommend Spec 311 | -| 2 | Localization v1 Customer-facing Surfaces | foundation-only, open gap, P1 | Spec 252 and current locale resolution are repo-real; glossary completion and customer-facing review/pack/notification adoption remain incomplete | customer-safe DACH/EU review consumption should be understandable before broader workflow packaging | recommend Spec 312 | -| 3 | Decision-Based Governance Inbox v1 | repo-real foundation, open gap, P1 | Governance inbox, findings queues, alerts, review follow-up, Spec 265 operator Decision Register, Spec 306 reconciliation, Spec 307 proof-link polish, and Spec 308 customer-safe summary/export inclusion are repo-verified | biggest remaining operator workflow gap; the need is a broader decision-centered inbox, not a Decision Register rebuild | recommend Spec 313 | -| 4 | Commercial Entitlements / Billing-State Maturity | repo-real foundation, open gap, P1/P2 | Specs 247 and 251 already resolve entitlement and lifecycle posture, but durable billing/subscription truth and artifact access by commercial state remain open | SaaS trust and lifecycle maturity should follow customer-facing surface clarity | recommend Spec 314 | -| 5 | Cross-Tenant Compare & Promotion Execution | repo-real preview/preflight, spec-backed execution follow-up | Spec 043 is repo-real for compare and preflight, and Spec 264 carries bounded execution follow-through; actual portfolio action maturity remains open until runtime/product proof is complete | portfolio action matters, but it must stay governance-first with lineage, evidence, approval, and rollback references rather than raw push mechanics | recommend Spec 315 if refresh/execution follow-through remains needed | -| 6 | Governance Artifact Lifecycle & Retention | foundation-only, open gap, P2 | Spec 262 and lifecycle-governance standards provide taxonomy-first guardrails, but governance-artifact runtime semantics are not yet productized | trust, auditability, export, and retention become more urgent after review and decision artifacts are customer-consumed | recommend Spec 316 | -| 7 | External Support Desk / PSA Handoff | repo-real foundation, open productization gap, P2 | Spec 256 and the current bounded handoff service already exist, but portfolio-safe handoff is not fully productized | MSP integration should compress follow-through work without turning TenantPilot into a helpdesk | recommend Spec 317 | -| 8 | Private AI Execution Governance Foundation / governed runtime follow-through | repo-real foundation, later open gap, P3 | Spec 248 is implemented as a governed foundation; visible runtime consumers and broader budget/result governance are still deferred | AI should remain governed foundation-first and provider-auditable after review, decision, artifact, and commercial maturity | recommend Spec 318 only as a bounded governed runtime consumer | +| 1 | Management Report PDF runtime validation and release hardening | implemented but not productized / open gap / P1 | Specs 378-379 and current code add renderer gateway, `StoredReport` artifact fields, generation job, signed download route, review-pack actions, audit, and tests; staging/Dokploy runtime validation remains open and the runtime gate defaults disabled | management-ready PDF output is a near sellability lever, but production enablement must wait for renderer validation | manual follow-through on current Spec 379, not new feature prep | +| 2 | Governance Artifact Lifecycle & Retention runtime | spec-backed open gap / P2 | Specs 158, 262, and 267 define artifact truth/lifecycle foundations, but runtime semantics for immutable identity, hold, export, delete, and suspended/read-only behavior remain the clearest enterprise trust gap | retention/export/hold semantics become more important as reviews, packs, decisions, and reports are customer-consumed | manual promotion only | +| 3 | Provider readiness and onboarding productization | optional open gap / P2 | Provider connection scope is hardened through Spec 339 and resolution guidance exists through Spec 353; any remaining work should target setup/readiness friction, not credential authority | improves adoption only if current product evidence shows operators still need clearer next actions | manual promotion only | +| 4 | Cross-domain indicator runtime follow-through | open guardrail gap / P2 | Spec 278 provides the indicator/progress semantics inventory; runtime adoption remains conditional by surface | prevents readiness/progress/risk/coverage UI drift across productized surfaces | manual promotion only | +| 5 | Manual system-panel browser fixture or audit procedure | validation follow-up / P2-P3 | Spec 377 closed the post-productization browser reaudit with no P0/P1 findings, but system-panel in-app browser coverage remains fixture/procedure-dependent | keeps release confidence high without pretending it is a runtime product feature | manual promotion only | +| 6 | First governed AI runtime consumer | foundation-only open gap / P3 | Spec 248 is implemented as a governed AI policy foundation; no bounded runtime consumer proves the model end-to-end yet | defer until current review/report/lifecycle/commercial productization is stable | later manual promotion only | Explicit anti-sprawl boundaries for this priority set: - Do not reopen risk acceptance as a broad new foundation theme; reuse the existing exception/risk-acceptance workflow and productize its customer-safe accountability trail. -- Do not reopen Decision Register v1; operator register, proof/run links, and customer-safe summary/review-pack inclusion are repo-real, while Governance Inbox and Customer Review Workspace completion remain separate. +- Do not reopen Decision Register v1; operator register, proof/run links, customer-safe summary/review-pack inclusion, and the Governance Inbox operator path are repo-real. +- Do not reopen Customer Review Workspace v1 completion; broad v1 productization is historical/completed, while any future external consumption or customer portal work must be separately scoped. - Do not reopen private AI as a fresh roadmap idea; the foundation already exists in `specs/248-private-ai-policy-foundation/spec.md`, and the open roadmap question is only when to promote the first governed runtime consumer. +- Do not reopen Spec 311 shell/scope work, Provider Connection scope hardening, canonical link cleanup, or Tenant Panel cleanup without fresh repo evidence. - Do not treat the promotable candidate backlog in `docs/product/spec-candidates.md` as an automatic prep queue; those items require explicit product decisions. - Do not prioritize Tenant Trust Score / public governance profile, insurance connectors, Copilot shadow-IT governance, local-first/on-prem proxy, or a standalone Betriebsrat mode before customer-safe review consumption, decision convergence, compliance mapping, governance packaging, and compare/promotion are materially clearer. @@ -62,7 +65,7 @@ ### Governance & Architecture Hardening Canonical run-view trust semantics, execution-time authorization continuity, tenant-owned query canon, findings workflow enforcement, Livewire trust-boundary reduction, operation-type canonicalization, provider-boundary hardening, target-scope neutrality, and governed-subject vocabulary enforcement. Goal: Turn the new audit constitution into enforceable backend and workflow guardrails before further governance surface area lands, while preventing the Governance-of-Record platform core from drifting into provider-specific or operation-type dual semantics. -Spec 309 completes the scoped RBAC role matrix and admin/system access boundary hardening. Support Access Governance remains a separate productization candidate, not an unresolved part of Spec 309. +Spec 309 completes the scoped RBAC role matrix and admin/system access boundary hardening. Support Access Governance remains separate from Spec 309; current support-access slices are repo-backed, and any broader impersonation/SCIM/customer-visible access-log work needs a fresh manual promotion trigger. **Active specs**: 144 **Specced follow-through (draft)**: 149 (queued execution reauthorization), 150 (tenant-owned query canon), 151 (findings workflow backstop), 152 (Livewire context locking), 214 (governance outcome compression), 216 (provider dispatch gate), 237 (provider boundary hardening). Next foundation candidates: Canonical Operation Type Source of Truth, Provider Identity & Target Scope Neutrality, Platform Vocabulary Boundary Enforcement for Governed Subject Keys. @@ -110,7 +113,7 @@ ### R1.9 Platform Localization v1 (DE/EN) - Search/Sort/Filter auf kritischen Listen für locale-sensitives Verhalten prüfen - QA/Foundation: Missing-Key Detection, Locale Regression Tests, Pseudolocalization Smoke Tests für kritische Flows -**Queue status**: recommended near-term Spec 312 after Customer Review Workspace v1 Completion; the historical foundation package is `specs/252-platform-localization-v1/spec.md`, and the remaining follow-through is `Localization v1 Customer-facing Surfaces` / `Customer-Facing Localization Adoption v1` in `docs/product/spec-candidates.md` as manual promotion only, not auto-prep. +**Queue status**: no longer an active Greenfield candidate. The historical foundation package is `specs/252-platform-localization-v1/spec.md`; adoption/neutralization is covered by Specs 275 and 286. Remaining copy QA, glossary refinement, or customer-channel polish is manual promotion only. ### Product Scalability & Self-Service Foundation Self-service and supportability foundation that keeps TenantPilot operable as a low-headcount, AI-assisted SaaS instead of drifting into manual onboarding, manual support, and founder-dependent customer operations. @@ -125,7 +128,7 @@ ### Product Scalability & Self-Service Foundation - Customer-facing transparency hooks: product surfaces should be designed so customer read-only views, review workspaces, support requests, and review-pack downloads can reuse the same underlying entities instead of becoming parallel one-off features - Private AI readiness hooks: support, review, diagnostic, and decision surfaces should be designed so later AI assistance can use governed context builders, data classification, usage budgets, local/private model policies, cache fingerprints, and human approval gates instead of direct feature-level AI calls -**Repo reality**: this is no longer an unspecced greenfield foundation. Onboarding, diagnostics, support requests, contextual help, entitlements, telemetry, customer health, and operational controls are already spec-backed or repo-real; the remaining roadmap gap is broader customer/self-service productization plus the narrower `Billing & Subscription Truth Layer v1` follow-up tracked in `docs/product/spec-candidates.md`. +**Repo reality**: this is no longer an unspecced greenfield foundation. Onboarding, diagnostics, support requests, contextual help, entitlements, commercial lifecycle state, telemetry, customer health, operational controls, and current support-access slices are already spec-backed or repo-real. Remaining roadmap work is broader customer/self-service productization, durable self-serve billing/subscription operations, and rollout/packaging polish rather than a new entitlement foundation. --- @@ -448,20 +451,20 @@ ## Infrastructure & Platform Debt | No shared lifecycle taxonomy for workspace, tenant, managed-object, retention, export, purge, and restoreability states | Local fixes such as ghost-policy handling, workspace deactivation, tenant removal, retention, or purge can create inconsistent deletion semantics and audit gaps | Covered by Workspace, Tenant & Managed Object Lifecycle Governance candidate | | Governance-artifact lifecycle runtime is still missing | Lifecycle taxonomy and point retention rules exist, but governance artifacts still lack one runtime contract for immutable identity, hold, export, delete, and suspended/read-only behavior | Covered by Governance Artifact Lifecycle & Retention v1 | | No structured support diagnostic bundle yet | Support cases require manual context gathering across tenants, runs, findings, providers, and reports | Covered by Product Scalability & Self-Service Foundation | -| No bounded support-access governance package yet | Break-glass, system access, and future impersonation/support access could drift without customer-visible TTL, reason, approval, and export semantics | Covered by Enterprise Access Boundary & Support Access Governance v1 | +| Support-access governance follow-through | Support Access Governance remains separate from Spec 309 RBAC hardening, but current support-access slices are repo-backed; only future impersonation, SCIM, or broader customer-visible access logs should be promoted from fresh evidence | Conditional manual follow-up only | | No formal security trust pack yet | Enterprise sales and customer security reviews require repeated manual explanations | Covered by Solo-Founder SaaS Automation & Operating Readiness | -| Auditor-ready executive export is not yet productized | Review truth still stops short of calm auditor-/executive-ready delivery even though the spec package now exists | Covered by `specs/263-auditor-pack-executive-export/spec.md` | -| Cross-tenant promotion execution is missing | Compare preview and preflight stop short of the actual portfolio action even though the execution spec package now exists on this branch | Covered by `specs/264-cross-tenant-promotion-execution/spec.md` | -| Residual admin workspace navigation contract drift may remain | Specs 301-304 closed the Inventory cutover, route audit, groups cutover, and tenant-panel dead-code cleanup; only a later contract split may still be needed if new repo evidence shows workspace-home and environment-bound navigation rules colliding again | Covered by the conditional `navigation-contract-split` follow-up in `docs/product/spec-candidates.md` | -| Customer Review Workspace v1 completion is still open | Specs 258 and 308 make the workspace and customer-safe decision summary/review-pack inclusion repo-real, but the complete self-serve customer consumption experience is not yet fully productized | Covered by `311-customer-review-workspace-v1-completion` in `docs/product/spec-candidates.md` | -| Customer-facing localization adoption is incomplete | Repo-real locale groundwork is not yet fully productized across customer-safe governance surfaces | Covered by the manual-promotion backlog in `docs/product/spec-candidates.md` | -| Billing and subscription truth is missing | Commercial readiness still stops short of a durable billing/subscription truth layer | Covered by the manual-promotion backlog in `docs/product/spec-candidates.md` | -| Stored reports still lack a clear product surface | Retained evidence and review artifacts remain harder to consume than they should be | Covered by the manual-promotion backlog in `docs/product/spec-candidates.md` | -| Workspace and tenant closure follow-through is not started | The taxonomy exists, but closure/runtime semantics are not yet productized | Covered by the manual-promotion backlog in `docs/product/spec-candidates.md` | +| Auditor-ready executive export / management report delivery | Review truth and management-report rendering are now repo-real, but current working-tree management-report PDF generation remains gated until staging/Dokploy renderer validation passes | Follow through current Spec 379 runtime validation; no new candidate unless release evidence changes | +| Cross-tenant promotion execution follow-through | Compare preview and preflight are repo-real; promote only if portfolio action maturity remains open after existing spec-backed execution work is reviewed | Manual promotion only | +| Residual admin workspace navigation or scope drift | Specs 301-304, 311, 338, 339, and 341 close the known surface/scope/link cleanup lanes; reopen only if fresh repo evidence shows route/shell/context regression | Conditional follow-up only | +| Customer Review Workspace v1 completion | Broad v1 customer-safe workspace completion is historical/completed through Specs 312, 342, 343, 344, 349, 351, and 372; future external customer portal consumption is a separate product decision | Closed as active debt | +| Customer-facing localization adoption | Localization foundation/adoption is covered by Specs 252, 275, and 286; remaining work is copy QA or customer-channel polish | Conditional manual polish only | +| Commercial billing/subscription maturity | Entitlement and commercial lifecycle truth are repo-real through Specs 247, 251, and 274; self-serve billing/subscription operations remain a later commercial productization decision | Manual promotion only | +| Stored reports and management report artifacts | Stored reports are now a real artifact substrate and management-report PDF target; remaining gap is production validation, retention/lifecycle semantics, and delivery packaging | Covered by current Spec 379 plus lifecycle backlog | +| Workspace and tenant closure follow-through | The lifecycle taxonomy exists and some closure/read-only semantics are repo-backed; productize only as part of governance-artifact lifecycle or commercial lifecycle decisions | Manual promotion only | | First governed AI runtime consumer is missing | The governed AI foundation exists, but no bounded runtime consumer proves the model end-to-end | Covered by the manual-promotion backlog in `docs/product/spec-candidates.md` | | No no-customization governance yet | Customer-specific requests can silently turn the product into consulting work and create hidden maintenance obligations | Covered by Additional Solo-Founder Scale Guardrails | | No business-continuity / founder-backup plan yet | Solo-founder operations create continuity risk for incidents, illness, vacation, access recovery, and customer trust | Covered by Additional Solo-Founder Scale Guardrails | -| No `.env.example` in repo | Onboarding friction | Open | +| Platform `.env.example` drift review | `apps/platform/.env.example` exists; remaining risk is env/runtime documentation drift rather than a missing file | Review needed | | CI pipeline config status drift | Roadmap debt list may be stale because workflow files exist and should be re-audited; align with static-analysis and architecture-boundary gates | Review needed | | No PHPStan/Larastan | No static analysis; covered by `Static Analysis Baseline for Platform Code` spec candidate | Open | | Thin architecture-boundary enforcement | Product tests are strong, but architecture-level guardrails need expansion; covered by `Architecture Boundary Guard Tests` spec candidate | Open | @@ -473,20 +476,18 @@ ## Infrastructure & Platform Debt ## Priority Ranking (Current Manual Promotion Order) -This ranking applies to the post-Spec-310 productization sequence. If a target already has an older spec package, treat the item as a refresh/execution follow-through only when current repo truth still leaves the product gap open. +This ranking applies to the post-Spec-377/current-Spec-379 productization sequence. If a target already has an older spec package, treat the item as refresh, runtime validation, or execution follow-through only when current repo truth still leaves the product gap open. Parallel immediate guardrail lane: the Cross-Domain Progress / Indicator Semantics candidate group in `docs/product/spec-candidates.md` should be promoted alongside OperationRun maturity when UI semantic drift is the active concern. Spec 278 provides the audit inventory and standards-delta input; the remaining follow-up remains split across contract, component, quality-gate, and migration lanes. It stays outside the main sellability ordering below because it is a cross-cutting semantics and standards package rather than a standalone customer-facing delivery lane. -Parallel immediate repair lane: the Admin Workspace Navigation & Tenant-owned Surface Repair candidate group in `docs/product/spec-candidates.md` is now mostly historical sequencing context after Specs 301-304. Promote only `navigation-contract-split`, and only when fresh repo evidence shows residual shared-contract drift between workspace-home cleanliness and environment-bound admin visibility. +Parallel immediate repair lane: the Admin Workspace Navigation & Tenant-owned Surface Repair candidate group in `docs/product/spec-candidates.md` is historical sequencing context after Specs 301-304 and Spec 311. Promote only a narrow follow-up, and only when fresh repo evidence shows residual shared-contract drift between workspace-home cleanliness and environment-bound admin visibility. -1. Customer Review Workspace v1 Completion -2. Localization v1 Customer-facing Surfaces -3. Decision-Based Governance Inbox v1 -4. Commercial Entitlements / Billing-State Maturity -5. Cross-Tenant Compare & Promotion Execution -6. Governance Artifact Lifecycle & Retention -7. External Support Desk / PSA Handoff -8. Private AI Execution Governance Foundation +1. Management Report PDF staging/runtime validation and release hardening +2. Governance Artifact Lifecycle & Retention runtime +3. Provider readiness / onboarding productization, only if fresh operator evidence shows friction +4. Cross-Domain Progress / Indicator runtime follow-through +5. Manual system-panel browser fixture or audit procedure +6. First governed AI runtime consumer --- diff --git a/docs/product/spec-candidates.md b/docs/product/spec-candidates.md index 7e611559..1b795d99 100644 --- a/docs/product/spec-candidates.md +++ b/docs/product/spec-candidates.md @@ -1,10 +1,10 @@ # Spec Candidates > **Status:** Active -> **Last reviewed:** 2026-05-15 +> **Last reviewed:** 2026-06-15 > **Use for:** The active repo-based queue of spec candidates that may still need new or refreshed specs > **Do not use for:** Proof that a candidate is already specced, implemented, or prioritized above the roadmap without repo verification -> **Scoped maintenance:** 2026-05-15 post-Spec-311 legacy/productization/scope follow-up candidate update; 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Spec 307 Decision Register proof-link implementation update; 2026-05-15 Spec 306 Decision Register reconciliation update; 2026-05-15 Spec 304 Tenant Panel dead-code retirement guardrail update; 2026-05-12 admin workspace navigation and tenant-owned surface repair candidate intake after the repo-verified navigation/panel audit; 2026-05-06 cross-domain progress and indicator semantics candidate intake; 2026-05-04 OperationRun progress maturity plus Tenant Dashboard active-operations summary candidate intake; 2026-05-03 OperationRun activity feedback candidate intake plus the 2026-05-02 repo-based queue re-audit and enterprise-SaaS deep-research alignment against current `specs/` truth, including Specs 263 and current-branch 264. +> **Scoped maintenance:** 2026-06-15 repo-truth sync after Specs 311-379, including completed Spec 311 surface-scope foundation, post-311 candidate reconciliation, Spec 377 UI closeout, and current working-tree Spec 379 management-report PDF runtime-gated status; 2026-05-15 post-Spec-311 legacy/productization/scope follow-up candidate update; 2026-05-15 Spec 310 product-truth/docs-drift reconciliation after Specs 307-309; 2026-05-15 Spec 309 RBAC role matrix and access boundary hardening update; 2026-05-15 Spec 308 customer-safe Decision Summary and Review Pack inclusion update; 2026-05-15 Spec 307 Decision Register proof-link implementation update; 2026-05-15 Spec 306 Decision Register reconciliation update; 2026-05-15 Spec 304 Tenant Panel dead-code retirement guardrail update; 2026-05-12 admin workspace navigation and tenant-owned surface repair candidate intake after the repo-verified navigation/panel audit; 2026-05-06 cross-domain progress and indicator semantics candidate intake; 2026-05-04 OperationRun progress maturity plus Tenant Dashboard active-operations summary candidate intake; 2026-05-03 OperationRun activity feedback candidate intake plus the 2026-05-02 repo-based queue re-audit and enterprise-SaaS deep-research alignment against current `specs/` truth, including Specs 263 and current-branch 264. > > Repo-based next-spec queue for TenantPilot. > This file is not a wishlist. It tracks only open gaps that are still worth turning into new or refreshed specs. @@ -39,20 +39,20 @@ ## Deep-Research Alignment Reference (non-queue) ### Customer Review Workspace v1 -- **Status markers**: repo-verified, productization gap -- **Roadmap lane**: Next -- **Current repo truth**: Specs 249 and 258, plus Spec 308 customer-safe Decision Summary / Review Pack inclusion, Spec 311 Workspace / Environment Surface Scope Contract, the implementation ledger, and current review surfaces, already prove the foundational path for customer-safe review consumption. -- **Problem**: Customer-safe review consumption remains the clearest sellability gap whenever calm status, reason, impact, evidence basis, accepted risks, decision summary, review-pack download, and one primary next action still require operator translation. -- **Deep-Research-derived sharpening**: Keep the lane focused on one customer-safe read-only review surface, findings summary, accepted-risk visibility, evidence viewer, review-pack download, management summary, RBAC/capability enforcement, and audit trail. -- **Non-goals**: no generic customer portal, no helpdesk surface, no raw diagnostics by default, no admin mirror. +- **Status markers**: repo-real, implemented, spec-backed, historical for v1 completion +- **Roadmap lane**: historical v1; future external consumption requires manual promotion +- **Current repo truth**: Specs 249, 258, 308, 311, 312, 342, 343, 344, 349, 351, and 372 plus `CustomerReviewWorkspace` prove the customer-safe workspace path. Environment selection is a page-level `environment_id` filter, not hidden global context. +- **Problem**: No active Greenfield v1 candidate remains. Future work should be a narrower external/customer-portal or polish decision, not a replay of the Customer Review Workspace completion lane. +- **Deep-Research-derived sharpening**: Keep future work customer-safe and read-only by default, but treat the broad v1 workspace as implemented/historical. +- **Non-goals**: no generic customer portal unless separately promoted, no helpdesk surface, no raw diagnostics by default, no admin mirror, no shell/sidebar/topbar scope work. ### Decision-Based Governance Inbox + Decision Register Follow-up -- **Status markers**: repo-verified, productization gap, roadmap recommendation -- **Roadmap lane**: Next -- **Current repo truth**: governance inbox, findings queues, operations attention, review follow-up, and Specs 250/257 already anchor the decision surface. Specs 265, 306, 307, and 308 prove the bounded operator Decision Register, direct proof/run link polish, and customer-safe summary/review-pack inclusion are not Greenfield. -- **Problem**: The remaining gap is broader decision-centered operating discipline: a Governance Inbox / Customer Review Workspace completion slice, not customer-safe summary or review-pack inclusion. -- **Deep-Research-derived sharpening**: keep any next slice as a follow-up over Specs 250/257/265/306/307/308, not a new Decision Register or approval-engine rebuild. +- **Status markers**: repo-real, implemented, spec-backed, historical for broad v1 +- **Roadmap lane**: historical v1; narrow follow-ups only by manual promotion +- **Current repo truth**: governance inbox, findings queues, operations attention, review follow-up, Specs 250/257/265/306/307/308, and Specs 327/346 now cover the decision-centered operator surface. The Decision Register, proof/run links, and customer-safe summary/review-pack inclusion are repo-real. +- **Problem**: No active Greenfield Governance Inbox or Decision Register candidate remains. Future work should target a proven narrow gap, not rebuild the operator register or approval engine. +- **Deep-Research-derived sharpening**: keep future slices as follow-ups over the existing inbox/register truth. - **Non-goals**: no generic Kanban board, no PSA clone, no XDR incident console, no new decision table, no duplicate approval engine. ### Governance Artifact Lifecycle & Retention v1 @@ -66,11 +66,11 @@ ### Governance Artifact Lifecycle & Retention v1 ### Commercial Entitlements & Billing-State Lifecycle v1 -- **Status markers**: repo-verified, foundation-only, productization gap -- **Roadmap lane**: Now -- **Current repo truth**: Specs 247 and 251 already ground workspace entitlements, lifecycle state handling, and read-only gating. -- **Problem**: Workspace, plan, and billing state still need clearer artifact-access, archive, scheduled-deletion, and customer-trust semantics. -- **Deep-Research-derived sharpening**: keep the lane framed as commercial lifecycle, workspace read-only behavior, artifact access by state, capability gating, and audited state change semantics rather than payment-engine work. +- **Status markers**: repo-real, implemented foundation/maturity path, spec-backed +- **Roadmap lane**: historical maturity path; self-serve commercial portal remains a separate decision +- **Current repo truth**: Specs 247, 251, and 274 ground workspace entitlements, lifecycle state handling, read-only gating, billing-state truth, and commercial lifecycle behavior. +- **Problem**: No active entitlement/billing-state foundation candidate remains. Future work should be explicit self-serve billing, subscription ops, or customer portal productization if needed. +- **Deep-Research-derived sharpening**: keep future work framed as commercial lifecycle and customer trust, not payment-engine scope creep. - **Non-goals**: no payment gateway, no invoicing engine, no tax engine. ### External Support Desk / PSA Handoff v1 @@ -84,11 +84,11 @@ ### External Support Desk / PSA Handoff v1 ### Customer-Facing Localization v1 -- **Status markers**: foundation-only, productization gap -- **Roadmap lane**: Now -- **Current repo truth**: Spec 252 and the locale resolver already provide the foundation. -- **Problem**: DE/EN customer-facing review consumption still lacks full glossary discipline, customer-safe labels, review-pack templates, notification text, and fallback confidence. -- **Deep-Research-derived sharpening**: v1 means glossary, review-workspace strings, review-pack templates, evidence labels, status/reason/impact/next-action labels, locale-aware formatting, fallback behavior, and missing-key tests. +- **Status markers**: repo-real, implemented foundation/adoption path, spec-backed +- **Roadmap lane**: historical adoption path; copy QA/manual polish only if explicitly promoted +- **Current repo truth**: Specs 252, 275, and 286 plus locale resolution and copy-neutralization work provide the foundation and adoption path. +- **Problem**: No active localization Greenfield candidate remains. Future work is copy QA, glossary refinement, or externally customer-facing channel polish. +- **Deep-Research-derived sharpening**: keep any future slice bounded to customer-safe copy quality and fallback confidence. - **Non-goals**: no full operator-UI localization in v1, no marketing translation project, no uncontrolled string extraction. ### Cross-Tenant Compare & Promotion with Lineage v1 @@ -111,11 +111,11 @@ ### Governance Service Packaging v1 ### Enterprise Access Boundary & Support Access Governance v1 -- **Status markers**: roadmap recommendation, spec candidate -- **Roadmap lane**: Next when support-access gaps turn operationally acute; otherwise Later -- **Current repo truth**: audit docs and handover material already show break-glass, system access, and platform support seams, but not a bounded support-access governance package. -- **Problem**: support access, delegated access, and future impersonation need customer-safe auditability, reason capture, TTL, approval, operator-context banner, and exportable access logs before broader SSO/SCIM work. -- **Roadmap Recommendation**: prioritize support-access request, reason required, time-limited access, capability-bound access, customer-visible audit trail, optional approval, break-glass separation, operator context banner, and exportable access log. +- **Status markers**: repo-real, implemented current slice, separate from Spec 309 +- **Roadmap lane**: historical current slice; future access governance requires fresh trigger +- **Current repo truth**: Support access governance is separate from Spec 309 RBAC/access-boundary hardening. Current support-access slices are repo-backed; future impersonation, SCIM, or broader customer-visible access logs should be promoted only with fresh product evidence. +- **Problem**: No active broad support-access Greenfield candidate remains in the queue. +- **Roadmap Recommendation**: keep support access separate from RBAC hardening and promote only a narrow follow-up if operational evidence requires it. - **Non-goals**: no full IAM suite, no immediate SCIM requirement unless separately promoted, no unrestricted impersonation. ### Private AI Execution Governance Foundation v1 @@ -129,7 +129,9 @@ ### Private AI Execution Governance Foundation v1 ## Active Candidate Queue -**2026-05-01 queue re-audit result**: no safe automatic next-best-prep target remains in the active queue. +**2026-06-15 queue re-audit result**: no safe automatic next-best-prep target remains in the active queue. + +Spec 311 is a completed foundation. Workspace-wide versus environment-bound scope is route-owned, `environment_id` is an explicit page filter, `/admin` and `/system` are the active panels, and retired Tenant Panel / `/admin/t` work must not be reopened unless fresh repo evidence shows a regression. Current working-tree Spec 379 is implementation context for runtime-gated management-report PDF generation, not a new candidate-queue item. The previous P0-P2 candidates were removed from the active queue because current `specs/` truth already covers them as prepared or implemented packages: @@ -156,6 +158,19 @@ ## Active Candidate Queue - `Workspace / Environment Surface Scope Contract` -> Spec 311, completed shell/scope foundation for route-owned workspace-wide versus environment-bound context. Follow-up work must not reopen shell/sidebar/topbar scope unless fresh repo evidence shows regression. +Post-311 follow-up candidates have also been reconciled against current specs and runtime evidence: + +- `customer-review-workspace-v1-completion` -> Specs 312, 342, 343, 344, 349, 351, and 372; completed/historical for the v1 customer-safe workspace path, with only future external-portal or narrow polish work to be promoted manually. +- `provider-connection-scope-hardening` -> Spec 339 plus the provider cutover lineage from Spec 281; completed/historical for scope hardening, with provider readiness/onboarding UX remaining optional manual follow-up only. +- `canonical-link-query-cleanup` -> Spec 341; completed/historical. +- `product-truth-docs-drift-cleanup` -> Spec 310 plus this 2026-06-15 docs-only sync; no active product feature candidate. +- `environment-resource-context-follow-through` -> Specs 319, 320, 334, and 338 cover the route/context contract work; keep only as conditional follow-through if fresh repo evidence shows hidden-context drift. +- `legacy-compatibility-dead-code-retirement` -> Specs 304 and 317; completed/historical unless retired panel/runtime guardrails regress. +- `tenant-helper-naming-cleanup` -> deferred cleanup, not a product candidate, unless naming drift blocks a scoped implementation. +- `localization-v1-customer-facing-surfaces` -> Specs 275 and 286; completed foundation/adoption path, with future copy QA/manual polish only. +- `decision-based-governance-inbox-v1` -> Specs 327 and 346; completed/historical for the broad operator inbox path. Do not reopen the Decision Register. +- `commercial-entitlements-billing-state-maturity` -> Specs 247, 251, and 274; implemented foundation/maturity path. Future self-serve billing or customer portal work is a separate manual product decision. + Two manual-promotion items have since moved out of backlog status on the current repo state: - `Auditor Pack Delivery & Executive Export v1` -> Spec 263 @@ -167,29 +182,43 @@ ## Promotable Candidate Backlog **Boundary**: manual promotion only, not auto-prep. These items are intentionally outside `next-best-prep` and require an explicit product decision before any future spec refresh or follow-up work. -### Recommended Next Candidate Sequence After Spec 311 +### Current Manual Promotion Order -No new spec numbers are assigned here. This table is a manual-promotion sequence only. +No new spec numbers are assigned here. This is manual-promotion only; the active auto-prep queue remains empty. -| Order | Recommended next candidate | Candidate status | Boundary | Depends on | +| Order | Manual candidate | Candidate status | Boundary | Depends on | |---:|---|---|---|---| -| 1 | `customer-review-workspace-v1-completion` | open gap / P1 | Complete customer-safe review consumption without claiming a generic customer portal or reopening shell/sidebar scope. | Spec 311 | -| 2 | `provider-connection-scope-hardening` | open gap / P1 | Harden workspace/provider-level provider connection scope and credential authority. | Spec 311 | -| 3 | `canonical-link-query-cleanup` | open gap / P1 | Clean route/link/query semantics after the surface scope contract. | Spec 311; provider hardening preferred | -| 4 | `product-truth-docs-drift-cleanup` | open gap / P2 | Align docs/templates/product truth with retired `/admin/t` and route-scope contract. | Spec 311 | -| 5 | `environment-resource-context-follow-through` | open gap / P2 | Reduce hidden Filament tenant fallback in selected canonical environment resources. | Spec 311; canonical link cleanup preferred | -| 6 | `legacy-compatibility-dead-code-retirement` | open gap / P2 | Remove or classify confirmed stale compatibility leftovers while keeping guard tests. | product truth cleanup preferred | -| 7 | `tenant-helper-naming-cleanup` | open gap / P3 | Rename or isolate tenant-named helpers that now encode legacy mental models. | canonical link cleanup; product truth cleanup preferred | -| 8 | `localization-v1-customer-facing-surfaces` | existing open gap / P1 | Productize DE/EN customer-facing review, pack, notification, reason, impact, and next-action language over the existing localization foundation. | customer review completion preferred | -| 9 | `decision-based-governance-inbox-v1` | existing open gap / P1 | Extend decision-centered operator workflow over existing Governance Inbox and Decision Register truth; do not rebuild the Decision Register. | customer review completion preferred | -| 10 | `commercial-entitlements-billing-state-maturity` | existing open gap / P1/P2 | Mature commercial lifecycle and billing-state truth after customer-facing surfaces are clearer. | productization lanes | +| 1 | `management-report-pdf-staging-runtime-validation` | open gap / P1 / current working-tree Spec 379 follow-through | Validate Gotenberg/Dokploy runtime before enabling management-report PDF generation outside local/test contexts; no new report feature scope. | Specs 378-379 | +| 2 | `governance-artifact-lifecycle-retention-runtime` | open gap / P2 / spec-backed | Productize immutable artifact identity, hold/export/delete/read-only semantics over existing lifecycle foundations. | Specs 158, 262, 267 | +| 3 | `provider-readiness-onboarding-productization` | optional open gap / P2 | Improve provider connection resolution/readiness guidance only if current UX evidence shows friction; do not reopen provider scope authority. | Specs 281, 339, 353 | +| 4 | `cross-domain-indicator-runtime-follow-through` | open guardrail gap / P2 | Adopt the progress/readiness/risk/coverage semantics from the cross-domain indicator work where runtime drift remains visible. | Spec 278 | +| 5 | `manual-system-panel-browser-fixture-or-audit-procedure` | validation follow-up / P2-P3 | Close the Spec 377 in-app browser fixture gap for system-panel smoke coverage; not a product runtime blocker. | Specs 376-377 | +| 6 | `first-governed-ai-runtime-consumer` | later open gap / P3 | Prove the governed AI foundation with one bounded runtime consumer after core productization lanes. | Spec 248 | -### Post-311 Legacy / Productization / Scope follow-up candidates +### Post-311 Candidate Sequence Status -These candidates are concrete follow-ups after Spec 311. They must not be merged into one umbrella spec. Spec 311 is the completed foundation: route scope determines shell/sidebar/topbar/breadcrumb; page filters determine data inside the current surface. +These entries preserve the sequencing history from the earlier Post-311 queue. They are not active candidates unless the status explicitly says a fresh repo-evidence trigger is required. + +| Former sequence item | Current status | Repo-based reason | +|---|---|---| +| `customer-review-workspace-v1-completion` | completed / historical | Promoted and implemented through Specs 312, 342, 343, 344, 349, 351, and 372. Future customer portal or external-consumption work is separate. | +| `provider-connection-scope-hardening` | completed / historical | Promoted and implemented through Spec 339, with provider-scope lineage from Spec 281. Provider readiness UX remains a separate optional lane. | +| `canonical-link-query-cleanup` | completed / historical | Promoted and implemented through Spec 341. | +| `product-truth-docs-drift-cleanup` | completed / historical | Spec 310 plus this 2026-06-15 docs-only sync cover the product-truth drift cleanup. | +| `environment-resource-context-follow-through` | conditional only | Specs 319, 320, 334, and 338 cover the route/context contract. Reopen only with fresh hidden-context regression evidence. | +| `legacy-compatibility-dead-code-retirement` | completed / historical | Specs 304 and 317 retired/guarded the legacy Tenant Panel and compatibility seams. | +| `tenant-helper-naming-cleanup` | deferred cleanup | Naming cleanup is not a product candidate unless it blocks a scoped implementation or active guardrail. | +| `localization-v1-customer-facing-surfaces` | completed foundation/adoption path | Specs 275 and 286 cover localization adoption/neutralization. Remaining copy QA is manual polish, not an active candidate. | +| `decision-based-governance-inbox-v1` | completed / historical | Specs 327 and 346 productized the operator governance inbox path. Decision Register remains repo-real and is not reopened. | +| `commercial-entitlements-billing-state-maturity` | implemented foundation/maturity path | Specs 247, 251, and 274 cover entitlement and lifecycle truth. Durable self-serve billing/portal work would be a separate manual decision. | + +### Post-311 Legacy / Productization / Scope follow-up candidate history + +These entries are retained as historical sequencing records. They must not be merged into one umbrella spec or auto-selected by `next-best-prep`. Spec 311 is the completed foundation: route scope determines shell/sidebar/topbar/breadcrumb; page filters determine data inside the current surface. #### Customer Review Workspace v1 Completion +- **Current status**: completed / historical. Promoted and implemented through Specs 312, 342, 343, 344, 349, 351, and 372; future external customer portal or narrow polish work must be promoted separately. - **Priority**: P1 - **Complexity**: M - **Impact**: High @@ -232,6 +261,7 @@ #### Customer Review Workspace v1 Completion #### Provider Connection Scope Hardening +- **Current status**: completed / historical. Promoted and implemented through Spec 339, with provider-scope lineage from Spec 281 and readiness guidance in Spec 353. - **Priority**: P1 - **Complexity**: M - **Impact**: High @@ -274,6 +304,7 @@ #### Provider Connection Scope Hardening #### Canonical Link / Query Cleanup +- **Current status**: completed / historical. Promoted and implemented through Spec 341. - **Priority**: P1 - **Complexity**: M - **Impact**: High @@ -314,6 +345,7 @@ #### Canonical Link / Query Cleanup #### Product Truth / Docs Drift Cleanup +- **Current status**: completed / historical. Spec 310 plus the 2026-06-15 docs-only sync cover the repo-truth reconciliation; no product feature is open here. - **Priority**: P2 - **Complexity**: S/M - **Impact**: Medium/High @@ -356,6 +388,7 @@ #### Product Truth / Docs Drift Cleanup #### Environment Resource Context Follow-through +- **Current status**: conditional only. Specs 319, 320, 334, and 338 cover the route/context contract; reopen only with fresh repo evidence of hidden-context drift. - **Priority**: P2 - **Complexity**: L - **Impact**: High @@ -398,6 +431,7 @@ #### Environment Resource Context Follow-through #### Legacy Compatibility / Dead Code Retirement +- **Current status**: completed / historical. Specs 304 and 317 cover retired Tenant Panel / `/admin/t` cleanup and guardrails unless runtime regression evidence appears. - **Priority**: P2 - **Complexity**: S/M - **Impact**: Medium @@ -436,6 +470,7 @@ #### Legacy Compatibility / Dead Code Retirement #### Environment Helper Naming Follow-through +- **Current status**: deferred cleanup. Keep out of the product candidate queue unless naming drift blocks a bounded implementation. - **Priority**: P3 - **Complexity**: M - **Impact**: Medium @@ -1320,8 +1355,8 @@ ### Decision Register Customer-Safe Summary / Review-Pack Inclusion (historical) - **Historical priority**: 1 - **Status**: promoted to Spec 308 and implemented on 2026-05-15; keep this entry as historical sequencing context only. - **Repo truth**: Spec 265 proves the bounded operator Decision Register, Spec 306 reconciles the current runtime, Spec 307 closes direct evidence/report plus source/evidence `OperationRun` proof-link polish, and Spec 308 adds repo-real customer-safe Decision Summary plus review-derived Review Pack inclusion. -- **Why no longer active**: the customer-safe summary/review-pack inclusion question has been answered by Spec 308. The remaining productization gap is Customer Review Workspace v1 Completion and broader Decision-Based Governance Inbox v1, not another Decision Register v1. -- **Why historical only**: reopening this would risk duplicating the completed summary/export contract or rebuilding the operator register instead of finishing the customer-facing workspace and decision workflow. +- **Why no longer active**: the customer-safe summary/review-pack inclusion question has been answered by Spec 308, and the later Customer Review Workspace / Governance Inbox follow-through has since moved into completed historical spec lineages. +- **Why historical only**: reopening this would risk duplicating the completed summary/export contract, rebuilding the operator register, or mixing already-closed follow-ups into a new umbrella spec. - **Anchors**: - `specs/265-decision-register-approval/spec.md` - `specs/306-decision-register-reconciliation/decision-register-reconciliation.md` @@ -1338,40 +1373,48 @@ ### Decision Register Customer-Safe Summary / Review-Pack Inclusion (historical) ### Governance Artifact Lifecycle & Retention v1 - **Priority**: 6 +- **Current status**: still open manual-promotion lane. - **Repo truth**: lifecycle taxonomy, artifact-truth semantics, and point retention rules already exist, but governance artifacts still lack one productized lifecycle runtime contract. - **Why promotable now**: this is the clearest new trust and auditability gap highlighted by the deep research. - **Why manual promotion only**: it crosses lifecycle, artifact, export, retention, and suspension semantics and therefore needs an explicit product boundary rather than automatic prep. - **Anchors**: - `specs/158-artifact-truth-semantics/spec.md` - `specs/262-lifecycle-governance-taxonomy/spec.md` + - `specs/267-artifact-lifecycle-retention/spec.md` - `docs/product/standards/lifecycle-governance.md` ### Billing & Subscription Truth Layer v1 - **Priority**: 4 -- **Repo truth**: plans, entitlements, and commercial lifecycle maturity are already spec-backed, but the billing/subscription truth layer is still missing. -- **Why promotable now**: deep-research alignment moves commercial trust and lifecycle closer to the now lane, even though the remaining unspecced slice is still the narrower billing/subscription follow-through. -- **Why manual promotion only**: the broad readiness work is already covered, so this should not reappear as an automatic foundation candidate. +- **Current status**: historical foundation/maturity lane; future self-serve subscription operations are separate manual productization. +- **Repo truth**: plans, entitlements, commercial lifecycle maturity, and billing-state truth are already spec-backed through Specs 247, 251, and 274. +- **Why not active**: the broad readiness and billing-state foundation work is covered, so this should not reappear as an automatic foundation candidate. +- **Why manual promotion only**: future work must name a concrete self-serve subscription, contract, invoice, payment, or customer portal outcome instead of reopening entitlement truth. - **Anchors**: - `specs/247-plans-entitlements-billing-readiness/spec.md` - `specs/251-commercial-entitlements-billing-state/spec.md` + - `specs/274-billing-subscription-truth/spec.md` ### Customer-Facing Localization Adoption v1 - **Priority**: 2 -- **Repo truth**: the localization foundation is already spec-backed; the open gap is customer-facing adoption, glossary completion, and productized surface coverage. -- **Why promotable now**: localization is now a productization follow-through task, not a greenfield foundation. -- **Why manual promotion only**: the broad foundation is already covered, so only a narrower adoption slice should be promoted deliberately. +- **Current status**: completed foundation/adoption path; future copy QA or customer-channel polish is separate. +- **Repo truth**: the localization foundation and adoption/neutralization path are spec-backed through Specs 252, 275, and 286. +- **Why not active**: localization is no longer a Greenfield foundation or broad active candidate. +- **Why manual promotion only**: only a narrower copy-quality, glossary, or customer-channel polish slice should be promoted deliberately. - **Anchors**: - `specs/252-platform-localization-v1/spec.md` + - `specs/275-customer-facing-localization-adoption/spec.md` + - `specs/286-ui-copy-ia-localization-neutralization/spec.md` - `specs/258-customer-review-productization/spec.md` - `specs/260-governance-service-packaging/spec.md` ### Enterprise Access Boundary & Support Access Governance v1 - **Priority**: 7 -- **Repo truth**: break-glass and platform access seams are documented, but no bounded support-access governance package currently exists. -- **Why promotable now**: this is the narrow early access-governance slice that should happen before broad SSO/SCIM ambitions if support access and customer-visible auditability become pressing. +- **Current status**: current support-access slice is repo-backed; broader access governance is conditional only and separate from Spec 309. +- **Repo truth**: break-glass/platform access seams and current support-access behavior are documented or spec-backed, while Spec 309 remains only RBAC/access-boundary hardening. +- **Why not active**: no broad support-access Greenfield candidate is active without fresh evidence for impersonation, SCIM, or customer-visible access-log gaps. - **Why manual promotion only**: the right cut is product-sensitive and must stay tightly bounded around support access, delegated access, TTL, approval, audit trail, and operator-context visibility. - **Anchors**: - `docs/audits/2026-03-09-enterprise-rbac-scope-audit.md` @@ -1382,13 +1425,16 @@ ### Enterprise Access Boundary & Support Access Governance v1 ### Stored Reports Surface v1 - **Priority**: P2 follow-up after customer-facing review, localization, decision inbox, commercial truth, promotion execution, and artifact lifecycle -- **Repo truth**: the stored-reports substrate is already grounded by evidence and review foundations, but the product surface remains incomplete. -- **Why promotable now**: this is the cleanest path from retained governance artifacts to a customer- and operator-usable surface. -- **Why manual promotion only**: this is a focused product-surface follow-up, not an unprepared foundation gap. +- **Current status**: implemented but not fully productized; current working-tree Spec 379 adds management-report PDF artifact flow. +- **Repo truth**: the stored-reports substrate is grounded by evidence/review foundations and current management-report PDF work; remaining gaps are lifecycle/retention semantics, delivery packaging, and production runtime validation. +- **Why not active**: do not reopen Stored Reports Surface as Greenfield; promote only a narrow lifecycle, delivery, or runtime-validation follow-up. +- **Why manual promotion only**: this is a focused product-surface/runtime follow-up, not an unprepared foundation gap. - **Anchors**: - `specs/153-evidence-domain-foundation/spec.md` - `specs/155-tenant-review-layer/spec.md` - `specs/260-governance-service-packaging/spec.md` + - `specs/277-stored-reports-surface/spec.md` + - `specs/379-management-report-pdf-runtime/spec.md` - `docs/product/implementation-ledger.md` ### Workspace & Tenant Closure Lifecycle v1 @@ -1500,11 +1546,33 @@ ## Promoted to Spec - Workspace, Tenant & Managed Object Lifecycle Governance v1 -> Spec 262 (`lifecycle-governance-taxonomy`) - Auditor Pack Delivery & Executive Export v1 -> Spec 263 (`auditor-pack-executive-export`) - Cross-Tenant Promotion Execution v1 -> Spec 264 (`cross-tenant-promotion-execution`) -- Decision Register & Approval Workflow v1 -> Spec 265 (`decision-register-approval`), reconciled by Spec 306, proof-link-polished by Spec 307, and customer-safe/review-pack-included by Spec 308; broad Greenfield scope closed, broader Governance Inbox / Customer Review Workspace completion remains separate +- Decision Register & Approval Workflow v1 -> Spec 265 (`decision-register-approval`), reconciled by Spec 306, proof-link-polished by Spec 307, and customer-safe/review-pack-included by Spec 308; broad Greenfield scope closed - Decision Register Reconciliation -> Spec 306 (`decision-register-reconciliation`) - Decision Register Evidence / OperationRun Link Polish -> Spec 307 (`decision-register-evidence-operationrun-link-polish`) - Decision Register Customer-Safe Summary / Review-Pack Inclusion -> Spec 308 (`decision-register-summary-review-pack`) - RBAC Role Matrix & Access Boundary Audit -> Spec 309 (`rbac-role-matrix-access-boundary-audit`) +- Product Truth / Docs Drift Cleanup -> Spec 310 (`product-truth-docs-drift-reconciliation`) +- Workspace / Environment Surface Scope Contract -> Spec 311 (`workspace-environment-surface-scope-contract`) +- Customer Review Workspace v1 Completion -> Spec 312 (`customer-review-workspace-v1-completion`) +- Commercial Entitlements / Billing-State Maturity -> Spec 274 (`billing-subscription-truth`) +- Customer-Facing Localization Adoption -> Spec 275 (`customer-facing-localization-adoption`) +- Support Access Governance current slice -> Spec 276 (`support-access-governance`) +- Stored Reports Surface v1 -> Spec 277 (`stored-reports-surface`) +- Governance Inbox Decision-First Workbench Productization -> Spec 327 (`governance-inbox-decision-first-workbench-productization`) +- Workspace / Environment Resource Scope Contract -> Spec 338 (`workspace-environment-resource-scope-contract`) +- Provider Connection Scope Hardening -> Spec 339 (`provider-connection-scope-hardening`) +- Canonical Link / Query Cleanup -> Spec 341 (`canonical-link-query-cleanup`) +- Customer Review Workspace Final Consumption Productization -> Spec 342 (`customer-review-workspace-final-consumption-productization`) +- Platform Productization Readiness Roadmap Reconciliation Gate -> Spec 345 (`platform-productization-readiness-roadmap-reconciliation-gate`) +- Governance Inbox Final Operator Workflow -> Spec 346 (`governance-inbox-final-operator-workflow`) +- Provider Connections Resolution Guidance v1 -> Spec 353 (`provider-connections-resolution-guidance-v1`) +- Review Pack PDF HTML Renderer v1 -> Spec 356 (`review-pack-pdf-html-renderer-v1`) +- Report Profiles Disclosure Policy v1 -> Spec 357 (`report-profiles-disclosure-policy-v1`) +- Management Report Layout / Branded Report Themes v1 -> Spec 366 (`management-report-layout-branded-report-themes-v1`) +- OperationRun Actionability System -> Spec 367 (`operationrun-actionability-system`) +- Post-Productization Browser Reaudit Closeout Gate -> Spec 377 (`post-productization-browser-reaudit-closeout-gate`) +- Management Report PDF v1 -> Spec 378 (`management-report-pdf-v1`) +- Management Report PDF Runtime -> Spec 379 (`management-report-pdf-runtime`), current working-tree runtime-gated implementation with staging/Dokploy validation still open - Queued Execution Reauthorization and Scope Continuity -> Spec 149 (`queued-execution-reauthorization`) - Livewire Context Locking and Trusted-State Reduction -> Spec 152 (`livewire-context-locking`) - Evidence Domain Foundation -> Spec 153 (`evidence-domain-foundation`) diff --git a/spec-candidates/380-provider-resource-identity-binding-foundation.md b/spec-candidates/380-provider-resource-identity-binding-foundation.md new file mode 100644 index 00000000..9b3140f3 --- /dev/null +++ b/spec-candidates/380-provider-resource-identity-binding-foundation.md @@ -0,0 +1,313 @@ +# Spec Candidate 380 - Provider Resource Identity & Binding Foundation v1 + +## Candidate Status + +Candidate for implementation. + +This candidate introduces the provider-agnostic identity and binding foundation required to resolve baseline subject ambiguity, built-in/default canonicalization, and future provider/resource-class expansion. + +## Spec Candidate Check + +- **Problem**: Baseline compare and capture can still treat display-name-derived `subject_key` values as if they were stable identity, which creates ambiguous or false missing states for built-ins, defaults, virtual targets, foundation resources, and tenant-owned duplicate names. +- **Today's failure**: Operators can see `ambiguous_match`, `policy_record_missing`, or `foundation_not_policy_backed` without a durable way to distinguish real duplicate tenant resources from provider defaults, inventory-only coverage, unsupported classes, or accepted limitations. +- **User-visible improvement**: Operators get auditable, workspace/environment/provider-scoped binding truth that later compare, evidence, review, and restore workflows can trust without pretending display names are identity. +- **Smallest enterprise-capable version**: Add the binding table, core value objects, status/mode taxonomy, isolation rules, and audit contract only; do not rebuild matching, UI, evidence, restore, or review behavior in this slice. +- **Explicit non-goals**: No full baseline compare matching rewrite, no resolution UI, no evidence/review readiness changes, no generic workflow engine, no restore redesign, and no production multi-provider implementation beyond fake-provider contract tests. +- **Permanent complexity imported**: One persisted binding table, resource identity value object, canonical subject key evolution, descriptor object, resolution mode/status enums, subject/resource class taxonomy, audit requirements, model tests, and workspace/environment isolation tests. +- **Why now**: Specs 381-384 depend on stable identity; without this foundation, later matching and customer-readiness work would keep encoding display-name ambiguity and false blocker states. +- **Why not local**: A local compare-only patch cannot preserve manual decisions, exclusions, accepted limitations, or provider-default classification across future compares, evidence snapshots, and review packs. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: New axes, new persisted truth, new meta-infrastructure, foundation terminology, and multiple follow-up specs in one domain. The defense is that identity ambiguity currently affects compare trust, evidence gaps, review readiness, and auditability; persistence is required because operator decisions must outlive a single run. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 10/12** +- **Decision**: approve as the first candidate, with strict scope control and no matching/UI expansion. + +## Proportionality Review + +1. **Current operator problem**: Operators cannot tell whether compare blockers are real duplicate resources, expected provider defaults, unsupported coverage, or missing local evidence. +2. **Why existing structure is insufficient**: `policy_type + subject_key` and current resolution outcomes do not provide durable provider identity or auditable operator decisions. +3. **Narrowest correct implementation**: Persist only bindings and identity primitives; all matching, result semantics, UI, and readiness work stays in later candidates. +4. **Ownership cost**: Baseline/domain owners must maintain binding statuses, resolution modes, resource descriptors, and audit semantics. +5. **Rejected alternative**: Snapshot-JSON-only or baseline-only annotations were rejected because they would not survive across compare runs or support revocation/audit. +6. **Current-release truth or future prep**: Current-release trust foundation required by existing compare ambiguity and review-readiness gaps; not speculative multi-provider runtime work. + +## Problem + +TenantPilot baseline compare currently relies too heavily on `policy_type + subject_key`, where `subject_key` can be derived from normalized display names. This is not enterprise-safe because names are not stable identity. + +The current model cannot reliably distinguish: + +- provider-owned built-ins, +- virtual assignment targets, +- tenant-owned resources, +- foundation objects, +- policy objects, +- unsupported resource classes, +- excluded/non-governed resources, +- duplicate display names, +- accepted limitations. + +This causes false or overloaded compare states such as: + +- `ambiguous_match`, +- `policy_record_missing`, +- `foundation_not_policy_backed`. + +## Goal + +Create a durable, provider-agnostic identity and binding foundation that future compare, evidence, review, restore, and governance workflows can use. + +This spec does not yet fully rebuild baseline matching or UI. It creates the data model and core primitives. + +## Scope + +### In Scope + +- Introduce `ResourceIdentity` value object. +- Introduce or evolve `CanonicalSubjectKey`. +- Introduce `ProviderResourceDescriptor`. +- Add persistent `provider_resource_bindings` table. +- Add resolution mode/status enums. +- Add subject/resource class taxonomy for baseline resolution. +- Enforce workspace/environment/provider isolation. +- Add audit support for manual bindings, exclusions, accepted limitations, and revocations. +- Add provider-agnostic contracts that future provider adapters can implement. +- Add unit tests for value objects and binding model behavior. + +### Out of Scope + +- Full baseline compare matching rewrite. +- Built-in canonicalization implementation. +- Resolution UI. +- Evidence/review readiness changes. +- Generic workflow engine. +- Full historical `OperationRun` backfill. +- Restore redesign. +- Multi-provider production implementation beyond fake-provider contract tests. + +## Design Principles + +- Display name is not identity. +- Provider object ID or canonical provider key wins over display name. +- Core logic must remain provider-agnostic. +- Provider-specific knowledge belongs behind adapters/registries. +- Bindings are persisted state, not workflow tasks. +- Manual decisions must be auditable. +- No fake green state. +- No customer blocker for expected provider defaults once canonicalized. + +## Data Model + +Add table: + +```text +provider_resource_bindings +``` + +Suggested fields: + +```text +id +workspace_id +environment_id nullable +provider_key +provider_connection_id nullable +binding_scope +subject_domain +subject_class +subject_type +subject_key +canonical_subject_key +provider_resource_type nullable +provider_resource_id nullable +provider_resource_external_id nullable +provider_resource_discriminator nullable +provider_resource_fingerprint nullable +binding_status +resolution_mode +resolution_reason +operator_note nullable +source_operation_run_id nullable +source_baseline_snapshot_id nullable +source_baseline_compare_run_id nullable +source_inventory_item_id nullable +source_policy_version_id nullable +decided_by_user_id nullable +decided_at nullable +is_active +valid_from nullable +valid_until nullable +created_at +updated_at +``` + +## Binding Status + +Required values: + +```text +active +inactive +superseded +revoked +``` + +## Resolution Modes + +Required v1 modes: + +```text +exact_provider_identity +canonical_builtin +canonical_virtual_target +manual_binding +excluded_non_governed +accepted_limitation +unsupported_coverage +missing_expected +``` + +## Subject Classes + +Minimum v1 classes: + +```text +policy +foundation +assignment_target +virtual_assignment_target +provider_builtin +provider_default +unsupported +unknown +``` + +Where possible, reuse or extend the existing governance subject taxonomy instead of creating duplicate enums. + +## Isolation Rules + +Bindings must be scoped by: + +```text +workspace_id +environment_id where applicable +provider_key +provider_connection_id where applicable +``` + +No binding may be used across workspaces. + +Environment-specific binding is the default. + +Workspace-wide binding is only allowed for explicitly workspace-level subjects. + +## New Core Objects + +### ResourceIdentity + +Represents stable provider resource identity. + +Must support: + +- provider key, +- resource type, +- provider resource ID, +- external ID, +- discriminator, +- fingerprint/hash, +- built-in/default/virtual flags, +- tenant-owned flag. + +Must not require display name as identity. + +### CanonicalSubjectKey + +Represents TenantPilot's stable internal key for a governed subject. + +Examples: + +```text +provider:{provider_key}:policy:{provider_resource_type}:{provider_resource_id} +provider:{provider_key}:builtin:{canonical_discriminator} +provider:{provider_key}:virtual-target:{canonical_discriminator} +provider:{provider_key}:foundation:{resource_type}:{provider_resource_id} +``` + +Provider-specific canonical values are supplied by adapters. + +### ProviderResourceDescriptor + +Represents a candidate provider resource for matching/resolution. + +Should contain: + +- resource identity, +- display label, +- subject class, +- provider resource type, +- support level, +- source inventory item, +- source policy version, +- source operation run, +- fingerprint/hash where available. + +## Audit Requirements + +Every manual or operator-driven binding decision must record: + +- actor, +- timestamp, +- workspace, +- environment, +- provider, +- subject, +- previous decision, +- new decision, +- reason/note, +- source operation run/compare where available. + +Revocation must also be audited. + +Automatic high-confidence canonical built-in bindings may be stored without operator note, but should remain traceable. + +## Acceptance Criteria + +- `provider_resource_bindings` exists with workspace/environment/provider isolation. +- Only one active binding exists per scoped canonical subject unless versioning explicitly allows otherwise. +- `ResourceIdentity` can represent tenant-owned, built-in, virtual, and unsupported resources. +- `CanonicalSubjectKey` does not depend on display name alone. +- Manual binding, exclusion, accepted limitation, unsupported coverage, and revocation modes are representable. +- Audit records are emitted for manual changes. +- No baseline compare behavior is made worse. +- Existing baseline compare records remain readable. + +## Required Tests + +- Unit test: `ResourceIdentity` does not require display name. +- Unit test: `CanonicalSubjectKey` includes provider/resource class/type. +- Unit test: built-in and tenant-owned identities produce distinct keys. +- Unit test: active binding uniqueness is enforced per workspace/environment/provider/canonical key. +- Feature test: binding cannot cross workspace boundary. +- Feature test: revoked binding is no longer active. +- Feature test: operator note is required for manual binding/exclusion/accepted limitation. +- Contract test: fake provider can create provider resource descriptors. + +## Validation Commands + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines +``` + +Add new dedicated tests for provider resource bindings and resource identity. + +## Risks + +- Over-generalizing into workflow engine. +- Creating a second taxonomy parallel to existing governance taxonomy. +- Allowing bindings to leak across workspaces/environments. +- Treating display names as stable keys again through the back door. + +## Recommendation + +Implement this first. + +This is the foundation required before matching, result semantics, UI, and review readiness can be fixed safely. diff --git a/spec-candidates/381-baseline-matching-pipeline-canonicalization.md b/spec-candidates/381-baseline-matching-pipeline-canonicalization.md new file mode 100644 index 00000000..6e0af564 --- /dev/null +++ b/spec-candidates/381-baseline-matching-pipeline-canonicalization.md @@ -0,0 +1,234 @@ +# Spec Candidate 381 - Baseline Matching Pipeline & Canonicalization v1 + +## Candidate Status + +Candidate for implementation after Spec 380. + +This candidate changes baseline compare matching so TenantPilot resolves subjects through identity, canonicalization, and bindings before falling back to display-name matching. + +## Depends On + +- Spec 380 - Provider Resource Identity & Binding Foundation v1 + +## Spec Candidate Check + +- **Problem**: Baseline compare currently loads baseline/current subjects mainly by normalized `policy_type|subject_key`, so built-ins, virtual assignment targets, foundation objects, duplicate names, and restored/test/copied resources can be misclassified. +- **Today's failure**: Operators get false ambiguity or false missing states, and display-name fallback can look more authoritative than it is. +- **User-visible improvement**: Compare results become more trustworthy because exact identity, canonical provider defaults, active bindings, and safe fingerprints are attempted before display names. +- **Smallest enterprise-capable version**: Add one matching pipeline seam, canonicalizer registry, foundation coverage registry, active-binding lookup, fake-provider contract tests, and bounded Microsoft/Intune adapter behavior behind the provider seam. +- **Explicit non-goals**: No manual resolution UI, no evidence/review readiness remapping, no restore integration, no customer-facing copy changes, no broad historical migration, and no generic provider framework beyond the concrete canonicalization need. +- **Permanent complexity imported**: Matching pipeline service, canonicalizer registry, coverage registry, typed matching result, provider descriptor use, adapter contract tests, and baseline compare integration tests. +- **Why now**: Spec 380 would create durable binding truth, but compare remains unsafe until the matching order actually consumes it before display-name fallback. +- **Why not local**: Local patches in `IntuneCompareStrategy` would keep provider-specific labels in core and would not provide a reusable identity/canonicalization path for evidence and review follow-up. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: New meta-infrastructure, foundation/canonical terminology, and multi-step pipeline. The defense is that matching order changes operator trust and customer-readiness blockers directly; the v1 is bounded to existing compare flows and one fake-provider contract. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 10/12** +- **Decision**: approve after Spec 380, with Microsoft behavior kept behind adapter seams. + +## Proportionality Review + +1. **Current operator problem**: Operators cannot trust whether compare blockers reflect real tenant-owned duplicates or expected provider defaults. +2. **Why existing structure is insufficient**: Existing compare code keys by `policy_type|subject_key` and current reason codes do not express canonical provider defaults or active bindings first. +3. **Narrowest correct implementation**: Insert one matching pipeline before existing compare strategy behavior; preserve legacy strategies where possible. +4. **Ownership cost**: Baseline compare owners maintain pipeline ordering, registry entries, adapter contracts, and fallback semantics. +5. **Rejected alternative**: Hardcoding Microsoft labels in core was rejected because it deepens provider coupling and still leaves display-name-like truth in shared code. +6. **Current-release truth or future prep**: Current-release trust issue; fake provider tests prove the seam without broad multi-provider productization. + +## Problem + +Baseline compare currently loads baseline/current subjects mainly by normalized `policy_type|subject_key`. This causes false ambiguity and false missing states when: + +- Microsoft/default/provider built-ins exist, +- virtual assignment targets appear as labels, +- foundation objects are not policy-backed, +- tenant-owned resources have duplicate names, +- restored/test/copied resources share display names. + +The matching process needs a provider-agnostic pipeline. + +## Goal + +Introduce a subject matching pipeline that resolves baseline subjects using: + +1. active binding, +2. canonical built-in/virtual target recognition, +3. provider object identity, +4. stable external identity, +5. safe fingerprint, +6. unique descriptor match, +7. display-name fallback, +8. unresolved ambiguity, +9. missing/unsupported/limitation classification. + +## Scope + +### In Scope + +- Add `SubjectMatchingPipeline`. +- Add `BuiltInCanonicalizerRegistry`. +- Add `FoundationCoverageRegistry`. +- Integrate active binding lookup from `provider_resource_bindings`. +- Integrate provider resource descriptors from inventory/policy versions. +- Add provider-adapter seam for canonicalization. +- Update baseline compare flow to call the matching pipeline before existing compare strategy. +- Preserve compatibility with existing compare strategies. +- Add fake-provider contract tests. +- Add Microsoft/Intune adapter implementation only behind provider adapter seam, not in core. + +### Out of Scope + +- Full UI for manual resolution. +- Evidence/review readiness remapping. +- Generic workflow engine. +- Full restore integration. +- Broad historical migration of previous compare results. +- Customer-facing output changes. + +## Matching Priority + +The matching pipeline must evaluate in this order: + +```text +1. Existing active binding +2. Provider built-in / virtual canonical key +3. Exact provider object identity +4. Stable provider-specific external identity +5. Unique fingerprint / payload identity where safe +6. Unique provider resource descriptor match +7. Unique normalized display-name fallback +8. Unresolved ambiguity +9. Missing resource/evidence/unsupported coverage +``` + +Display-name fallback must be explicitly marked as fallback and should never silently produce high-trust identity if stronger identity is available. + +## Built-In Canonicalization + +Core baseline logic must not hardcode provider names or Microsoft labels. + +Provider adapters may register canonicalizers. + +Example Microsoft/Intune canonicalization behind adapter seam: + +```text +All users +All devices +Default role scope tag +Known provider-default assignment targets +Known provider-default foundation resources +``` + +These must resolve by provider discriminator/type/canonical key, not display name. + +## Foundation Coverage Registry + +The registry must classify resource classes as: + +```text +fully_comparable +inventory_only +canonical_only +unsupported +excluded_by_profile +requires_manual_binding +``` + +Foundation objects must not be forced into policy-backed comparison. + +Examples: + +```text +roleScopeTag default -> canonical built-in/default if provider identifies it +roleScopeTag tenant-owned -> foundation resource by provider object ID +assignmentFilter tenant-owned -> foundation inventory/comparable depending capability +notificationMessageTemplate -> foundation/config object depending capability +``` + +## Integration Points + +Expected areas to inspect/modify: + +- `BaselineCompareService` +- `CompareBaselineToTenantJob` +- `SubjectResolver` +- `ResolutionOutcome` +- `IntuneCompareStrategy` +- `CompareStrategyRegistry` +- `InventoryPolicyTypeMeta` +- `BaselineSupportCapabilityGuard` +- `GovernanceSubjectTaxonomyRegistry` +- provider gateway / provider adapter seams +- Graph contract registry integration where applicable + +## Result Contract + +The matching pipeline should return a typed result, for example: + +```text +resolved_exact_identity +resolved_active_binding +resolved_canonical_builtin +resolved_canonical_virtual_target +resolved_unique_fallback +unresolved_ambiguous_match +missing_provider_resource +missing_local_evidence +unsupported_resource_class +foundation_inventory_only +excluded_non_governed +accepted_limitation +``` + +Spec 382 will formalize result semantics, but Spec 381 must produce enough structure for that follow-up. + +## Acceptance Criteria + +- Baseline compare uses matching pipeline before display-name fallback. +- Built-ins/virtual targets can be resolved by provider canonicalizer. +- Tenant-owned duplicate names remain unresolved unless an active binding exists. +- Foundation inventory-only resources no longer produce false policy-backed matching attempts. +- Existing compare strategies still receive matched baseline/current resources where possible. +- No core class hardcodes Microsoft display names. +- Fake provider can register canonical built-ins and resolve them. +- YPTW2-style cases are representable: + - `All users` / `All devices` canonicalized, + - `default roleScopeTag` canonicalized or foundation-classified, + - tenant-owned duplicate Settings Catalog policies remain ambiguous until binding, + - assignment filters and notification templates are classified by capability. + +## Required Tests + +- Built-in canonical object resolves without ambiguity. +- Virtual assignment target resolves without display-name matching. +- Tenant-owned duplicate display names remain unresolved. +- Active manual binding resolves duplicate candidate. +- Display-name fallback is only used after identity/canonical/binding attempts fail. +- Foundation inventory-only object returns inventory-only limitation, not `foundation_not_policy_backed`. +- Unsupported resource class returns unsupported result. +- Fake provider canonicalization contract test. +- Microsoft/Intune adapter does not leak display-name logic into core. + +## Validation Commands + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCompareAmbiguousMatchGapTest.php tests/Feature/Baselines/BaselineCompareGapClassificationTest.php +``` + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines/SubjectResolverTest.php +``` + +Add new tests for the matching pipeline and canonicalizer registry. + +## Risks + +- Accidentally making display-name fallback look authoritative. +- Hiding real duplicate tenant resources through over-aggressive canonicalization. +- Hardcoding Microsoft-specific behavior into core. +- Breaking existing compare strategy expectations. + +## Recommendation + +Implement this second. + +This candidate fixes the core matching failure mode while still avoiding UI and evidence/review changes. diff --git a/spec-candidates/382-baseline-compare-result-semantics.md b/spec-candidates/382-baseline-compare-result-semantics.md new file mode 100644 index 00000000..d818cfa5 --- /dev/null +++ b/spec-candidates/382-baseline-compare-result-semantics.md @@ -0,0 +1,239 @@ +# Spec Candidate 382 - Baseline Compare Result Semantics & Gap Classification v1 + +## Candidate Status + +Candidate for implementation after Specs 380 and 381. + +This candidate cleans up overloaded compare/gap result states so evidence, reviews, operations, and UI can distinguish real blockers from limitations. + +## Depends On + +- Spec 380 - Provider Resource Identity & Binding Foundation v1 +- Spec 381 - Baseline Matching Pipeline & Canonicalization v1 + +## Spec Candidate Check + +- **Problem**: Baseline compare states and gap classifications are overloaded, so operators and customer-readiness gates cannot reliably distinguish unresolved governance risk from limitations or expected provider defaults. +- **Today's failure**: `ambiguous_match`, `policy_record_missing`, `foundation_not_policy_backed`, and `missing_policy` can each carry multiple meanings, causing noisy follow-ups or misleading blockers. +- **User-visible improvement**: Compare details and OperationRun follow-ups can show action-required, limitation, resolved, unsupported, missing evidence, and missing resource with safer publication consequences. +- **Smallest enterprise-capable version**: Introduce provider-neutral reason codes, blocker categories, legacy mapping, OperationRun context payload updates, and internal table display updates; do not add UI resolution or evidence/review integration yet. +- **Explicit non-goals**: No binding model, no matching pipeline, no resolution UI, no full historical data rewrite, no customer Review Pack wording changes beyond internal state preparation. +- **Permanent complexity imported**: New reason-code family, blocker categories, legacy mapper, OperationRun context changes, table display logic, and compatibility tests. +- **Why now**: Specs 380-381 can resolve identity, but downstream consumers still need result semantics that separate real blockers from accepted/provider limitations. +- **Why not local**: Local label changes would not change OperationRun next steps, customer-blocking logic, legacy compatibility, or evidence/review consumers. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: New semantic axes and many reason labels. The defense is that each reason changes operator action, publication blocking, limitation disclosure, or compatibility mapping. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 10/12** +- **Decision**: approve after Spec 381, with a strict legacy mapper and no broad UI/review changes in this slice. + +## Proportionality Review + +1. **Current operator problem**: Operators cannot trust whether a compare issue requires action, limitation acceptance, evidence refresh, or no action. +2. **Why existing structure is insufficient**: Existing reason codes are overloaded and policy-specific, so they cannot safely drive provider-neutral publication/readiness behavior. +3. **Narrowest correct implementation**: Add result semantics and compatibility mapping only; defer resolution UI and review readiness to Specs 383-384. +4. **Ownership cost**: Baseline compare, OperationRun, and UI owners must maintain a reason-code contract and legacy mapper. +5. **Rejected alternative**: Presentation-only labels were rejected because they would not change blocker behavior or OperationRun next actions. +6. **Current-release truth or future prep**: Current-release trust/readiness work because existing compare outputs already feed Evidence and Review surfaces. + +## Problem + +Current compare states and gap classifications are overloaded. + +Examples: + +- `ambiguous_match` can mean real duplicate tenant-owned resources or false ambiguity caused by built-ins/defaults. +- `policy_record_missing` can mean missing local evidence, not necessarily missing provider resource. +- `foundation_not_policy_backed` can mean inventory-only foundation coverage, unsupported coverage, or false policy-centric modeling. +- `missing_policy` is policy-specific and not provider/resource-class neutral. + +This makes OperationRun follow-ups, Evidence Snapshot completeness, Review Output Readiness, and customer-safe Review Packs hard to trust. + +## Goal + +Create clearer provider-neutral compare result semantics. + +The system must distinguish: + +- real drift, +- no drift, +- unresolved ambiguity, +- resolved built-in/default, +- resolved manual binding, +- missing provider resource, +- missing local evidence, +- unsupported resource class, +- inventory-only foundation limitation, +- excluded non-governed subject, +- accepted limitation, +- compare failure, +- coverage unproven. + +## Scope + +### In Scope + +- Introduce or refine provider-neutral compare result reason codes. +- Map existing legacy states to new semantics. +- Update gap normalization. +- Update OperationRun context payloads for new reason semantics. +- Update baseline compare detail tables to display blocker vs limitation vs resolved. +- Update tests around ambiguous/gap classification. +- Preserve backward compatibility for existing compare records. +- Add translator/mapper for legacy reason codes. + +### Out of Scope + +- New resolution UI. +- Evidence/review readiness full integration. +- Provider binding data model. +- Matching pipeline implementation. +- Full historical data rewrite. +- Customer Review Pack wording changes beyond internal state preparation. + +## Proposed Result Reasons + +```text +resolved_exact_identity +resolved_active_binding +resolved_canonical_builtin +resolved_canonical_virtual_target +resolved_unique_fallback +unresolved_ambiguous_match +missing_provider_resource +missing_local_evidence +unsupported_resource_class +foundation_inventory_only +excluded_non_governed +accepted_limitation +drift_detected +no_drift +compare_failed +coverage_unproven +``` + +## Mapping from Existing States + +| Existing State / Reason | New Semantic | +|---|---| +| `ambiguous_match` | `unresolved_ambiguous_match` unless resolved by binding/canonicalization | +| `policy_record_missing` | split into `missing_local_evidence` or `missing_provider_resource` | +| `foundation_not_policy_backed` | split into `foundation_inventory_only`, `unsupported_resource_class`, or `accepted_limitation` | +| `missing_policy` | `missing_provider_resource` where a trusted prior/current identity exists | +| `unexpected_policy` | future-compatible `unexpected_resource` or preserved legacy display | +| `unsupported_subjects` | `unsupported_resource_class` | +| `coverage_unproven` | keep as run-level trust limitation | +| `strategy_failed` | `compare_failed` | + +## Blocker Categories + +Each compare issue must classify into one of: + +```text +customer_blocker +operator_action_required +internal_limitation +accepted_limitation +resolved +informational +``` + +## Customer Blocking Rules + +### Customer Blocking + +- unresolved ambiguity for governed in-scope subject, +- missing provider resource after trusted identity/binding, +- compare failure preventing required posture claim, +- missing local evidence for required governed subject without accepted limitation, +- coverage unproven for required customer-facing control. + +### Not Customer Blocking by Default + +- resolved built-in/default, +- resolved virtual assignment target, +- excluded non-governed subject, +- accepted limitation, +- unsupported foundation coverage when disclosed as limitation, +- inventory-only foundation where no drift claim is made. + +## OperationRun Impact + +OperationRun next steps should become specific: + +```text +Resolve ambiguous baseline subjects before publication. +Re-run inventory sync; required local evidence is missing. +Accept limitation or exclude unsupported foundation objects. +Provider default objects were canonicalized automatically. +``` + +Noisy follow-ups should not appear for resolved built-ins/defaults. + +## UI Impact + +Baseline compare detail should clearly show: + +```text +Resolved +Action required +Limitation +Unsupported +Missing evidence +Missing resource +Drift +No drift +``` + +Do not show all limitations as blockers. + +## Compatibility + +Existing compare records must remain readable. + +Add a compatibility mapper for legacy payloads. + +Do not rewrite old OperationRun context in v1 unless explicitly needed for a focused backfill. + +## Acceptance Criteria + +- `ambiguous_match` no longer represents resolved built-ins/defaults. +- `policy_record_missing` is split into evidence-missing vs provider-resource-missing semantics. +- `foundation_not_policy_backed` no longer appears as a false policy-missing blocker for foundation resources. +- OperationRun follow-ups distinguish action-required from limitation. +- Compare detail tables display blocker/limitation/resolved status clearly. +- Legacy compare records remain readable. +- Tests prove customer-blocking logic is not triggered by accepted limitations or resolved built-ins. + +## Required Tests + +- Ambiguous tenant-owned duplicate produces action-required blocker. +- Resolved canonical built-in produces resolved/informational state. +- Missing local policy/evidence produces missing local evidence, not missing provider resource. +- Missing provider resource after trusted binding produces true blocker. +- Unsupported foundation resource produces limitation. +- Accepted limitation does not produce publication blocker. +- Legacy `ambiguous_match` payload can still render. +- Legacy `foundation_not_policy_backed` payload maps to safe legacy diagnostic. + +## Validation Commands + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCompareAmbiguousMatchGapTest.php tests/Feature/Baselines/BaselineCompareGapClassificationTest.php +``` + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Evidence/BaselineDriftPostureSourceTest.php +``` + +## Risks + +- Renaming states too aggressively and breaking UI/tests. +- Treating limitations as green. +- Treating all missing evidence as missing provider resources. +- Breaking historical compare rendering. + +## Recommendation + +Implement this third. + +This candidate makes the compare output trustworthy before exposing operator resolution UI and review-pack readiness changes. diff --git a/spec-candidates/383-baseline-subject-resolution-ui.md b/spec-candidates/383-baseline-subject-resolution-ui.md new file mode 100644 index 00000000..975a0894 --- /dev/null +++ b/spec-candidates/383-baseline-subject-resolution-ui.md @@ -0,0 +1,302 @@ +# Spec Candidate 383 - Baseline Subject Resolution UI & Operator Decisions v1 + +## Candidate Status + +Candidate for implementation after Specs 380, 381, and 382. + +This candidate gives operators a focused UI to resolve real ambiguous baseline subjects, exclusions, unsupported foundation resources, and accepted limitations. + +## Depends On + +- Spec 380 - Provider Resource Identity & Binding Foundation v1 +- Spec 381 - Baseline Matching Pipeline & Canonicalization v1 +- Spec 382 - Baseline Compare Result Semantics & Gap Classification v1 + +## Spec Candidate Check + +- **Problem**: Operators do not have a durable focused UI for resolving ambiguous baseline subjects, excluding non-governed subjects, accepting limitations, or revoking old decisions. +- **Today's failure**: Binding truth would exist after Spec 380, but without a safe operator surface, decisions remain ad hoc, hard to audit, and hard to reuse in future compares. +- **User-visible improvement**: Operators can resolve compare blockers from the Baseline Compare and OperationRun context with clear candidate details, required notes, RBAC, audit trail, and rerun guidance. +- **Smallest enterprise-capable version**: One focused resolution surface with create/update/revoke decisions, links from compare and OperationRun, minimal permissions, and audit events. +- **Explicit non-goals**: No generic Governance Inbox, no generic workflow engine, no approval workflow, no bulk auto-resolution beyond safe canonical built-ins, no customer-facing binding diagnostics, no restore UI changes. +- **Permanent complexity imported**: One Filament/operator surface, actions, capability gates, audit events, filtered links, UI tests, and decision-state rendering. +- **Why now**: After Specs 380-382, unresolved subjects become well-classified; operators need a safe place to make and revoke decisions that affect publication readiness. +- **Why not local**: Ad hoc table actions or manual DB changes would lack audit consistency, workspace/environment isolation, revocation semantics, and future compare reuse. +- **Approval class**: Workflow Compression. +- **Red flags triggered**: New UI surface and several action types. The defense is that the UI directly resolves an operator workflow blocker and uses existing binding/result truth instead of creating a broad workflow layer. +- **Score**: Nutzen: 2 | Dringlichkeit: 1 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 10/12** +- **Decision**: approve after Specs 380-382, with strict no-workflow-engine boundary and UI coverage decision required. + +## UI Surface Impact + +- **Reachable surface changed/added**: yes, a focused baseline subject resolution surface. +- **Page archetype**: operator resolution/workbench surface linked from Baseline Compare and OperationRun. +- **Design depth**: functional operator workflow design with table, detail drawer/page, explicit empty states, and dangerous-action confirmations where decisions hide blockers or revoke existing decisions. +- **Customer/operator safety**: operator-only; no raw provider IDs in customer-facing views. Truncated/masked provider identifiers may appear in operator technical detail where needed. +- **Dangerous/high-impact actions**: manual binding, exclusion, accepted limitation, unsupported marking, and revocation require authorization, operator note where applicable, audit, and confirmation when the action can affect customer-ready output. + +## Proportionality Review + +1. **Current operator problem**: Operators need to resolve real compare ambiguity and limitations before customer-ready review output can be trusted. +2. **Why existing structure is insufficient**: Baseline Compare detail and OperationRun follow-ups can point to issues but do not persist auditable decisions. +3. **Narrowest correct implementation**: Add one focused resolution surface over provider resource bindings; do not create a workflow engine. +4. **Ownership cost**: Filament page/resource ownership, RBAC/capabilities, action tests, browser/Filament smoke, and audit event maintenance. +5. **Rejected alternative**: Encoding decisions only in Baseline Profile was rejected because many decisions are subject/run/context-specific and need revocation history. +6. **Current-release truth or future prep**: Current-release operator workflow once Specs 380-382 establish trustworthy unresolved subject semantics. + +## Problem + +When TenantPilot detects ambiguous or unsupported baseline subjects, operators currently do not have a durable, focused way to decide: + +- which candidate is the correct resource, +- whether the subject is non-governed/test/legacy, +- whether a foundation object is an accepted limitation, +- whether an old decision should be revoked, +- whether a compare needs rerun after resolution. + +Without a UI, the binding model cannot be operationalized. + +## Goal + +Add a focused baseline subject resolution surface. + +This is not a generic workflow engine. It is a bounded operator UI for creating, updating, and revoking provider resource bindings and resolution decisions. + +## Scope + +### In Scope + +- Add "Resolve baseline subjects" surface. +- Link from Baseline Compare detail. +- Link from OperationRun follow-up where compare has unresolved subject blockers. +- Show ambiguous candidates. +- Allow manual binding to a candidate resource. +- Allow exclusion as non-governed. +- Allow accepted limitation. +- Allow unsupported coverage marking. +- Allow decision revocation. +- Require operator notes for manual decisions. +- Emit audit entries. +- Trigger or recommend rerun after decision. +- Add permissions/capabilities for resolution actions. + +### Out of Scope + +- Generic Governance Inbox. +- Generic workflow engine. +- Approval workflow. +- Bulk auto-resolution beyond safe canonical built-ins. +- Customer-facing binding diagnostics. +- Full historical backfill UI. +- Restore UI changes. + +## Primary Surface + +Suggested route/surface: + +```text +Baseline Compare -> Resolve baseline subjects +``` + +Secondary entry points: + +```text +OperationRun detail -> Resolve baseline subjects +Environment dashboard -> Attention card/link only +Baseline Profile -> Scope/exclusion policy entry point +``` + +## Table Columns + +Resolution table should include: + +```text +Subject +Subject class +Provider type +Problem +Candidate count +Current decision +Recommended action +Last seen +Action +``` + +## Detail Drawer / Page + +For each unresolved subject, show: + +```text +Baseline subject label +Canonical subject key +Subject class +Provider resource type +Problem reason +Current blocker category +Source compare run +Source operation run +Candidates +Existing binding if any +Audit history +Recommended action +``` + +## Candidate Fields + +For each candidate: + +```text +Display name +Provider resource type +Provider resource id truncated/masked +External id truncated/masked +Last inventory time +Source operation run +Source inventory item +Source policy version if available +Payload fingerprint/hash if available +Support capability +``` + +## Supported Actions + +### Bind to this resource + +Creates/updates active `provider_resource_bindings`. + +Requires operator note. + +### Exclude as non-governed + +For test/legacy/pilot objects not part of the governed baseline. + +Requires operator note. + +### Accept limitation + +For unsupported/foundation/inventory-only coverage where the product cannot yet compare the object but the limitation is acceptable. + +Requires operator note. + +### Mark unsupported + +For resource classes outside current support scope. + +Requires operator note unless registry marks unsupported by default. + +### Resolve as provider built-in + +Allowed when provider canonicalizer identifies a built-in/default with high confidence. + +May be automatic or operator-confirmed depending confidence. + +### Revoke decision + +Supersedes/revokes active binding. + +Requires operator note. + +## Permissions + +Introduce or reuse capabilities such as: + +```text +baseline.subject_resolution.view +baseline.subject_resolution.manage +baseline.subject_resolution.revoke +baseline.subject_resolution.accept_limitation +baseline.subject_resolution.exclude +``` + +Only trusted workspace/environment operators should be allowed to create decisions that affect customer-ready output. + +## OperationRun Integration + +OperationRun should show actionable follow-up: + +```text +Resolve 12 ambiguous baseline subjects before publication. +Accept or exclude 3 unsupported foundation subjects. +``` + +Follow-up links to the resolution surface with filters applied. + +Resolved or accepted cases should not remain noisy action items. + +## Baseline Profile Integration + +Baseline Profile should own durable policy-level exclusions and accepted unsupported classes. + +The resolution UI may create subject-level decisions. + +Profile-level changes should be explicit, not accidental side effects. + +## Audit Requirements + +Every action records: + +```text +who +when +action +old status +new status +subject +workspace +environment +provider +source compare +source operation run +operator note +``` + +## Acceptance Criteria + +- Operators can resolve ambiguous tenant-owned subjects from Baseline Compare detail. +- Binding decisions persist and are used by future compares. +- Operators can exclude non-governed/test subjects with audit note. +- Operators can accept limitations with audit note. +- Operators can revoke previous decisions. +- OperationRun follow-up links to the focused resolution surface. +- Resolution UI distinguishes blocker, limitation, resolved, and excluded. +- No broad workflow engine is introduced. +- UI respects workspace/environment isolation and RBAC. + +## Required Tests + +- Feature test: resolution page lists unresolved ambiguous subjects. +- Feature test: bind candidate creates active binding. +- Feature test: future compare uses binding. +- Feature test: exclude non-governed creates audited decision. +- Feature test: accepted limitation no longer appears as unresolved blocker. +- Feature test: revoked decision no longer resolves future compare. +- Feature test: unauthorized user cannot create binding. +- Feature test: binding cannot be created across workspace boundary. +- Browser/Filament test: OperationRun follow-up opens filtered resolution page. +- Browser/Filament test: resolution states render correctly. + +## Validation Commands + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines +``` + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament +``` + +Add focused tests for the resolution resource/page/actions. + +## Risks + +- Turning this into a generic workflow engine. +- Letting operators hide real drift with exclusions. +- Overloading Baseline Profile with per-run decisions. +- UI becoming too technical. +- Missing audit trail for customer-impacting decisions. + +## Recommendation + +Implement this fourth. + +This candidate operationalizes the binding/resolution system after the core identity, matching, and result semantics are stable. diff --git a/spec-candidates/384-evidence-review-readiness-integration.md b/spec-candidates/384-evidence-review-readiness-integration.md new file mode 100644 index 00000000..5997246e --- /dev/null +++ b/spec-candidates/384-evidence-review-readiness-integration.md @@ -0,0 +1,281 @@ +# Spec Candidate 384 - Evidence & Review Readiness Integration v1 + +## Candidate Status + +Candidate for implementation after Specs 380, 381, 382, and 383. + +This candidate integrates the new baseline subject identity/resolution semantics into Evidence Snapshot completeness, Baseline Drift Posture, Review Output Readiness, publication blockers, and Review Pack export. + +## Depends On + +- Spec 380 - Provider Resource Identity & Binding Foundation v1 +- Spec 381 - Baseline Matching Pipeline & Canonicalization v1 +- Spec 382 - Baseline Compare Result Semantics & Gap Classification v1 +- Spec 383 - Baseline Subject Resolution UI & Operator Decisions v1 + +## Spec Candidate Check + +- **Problem**: Evidence and Review readiness consume compare results that may still contain overloaded or false blocker states. +- **Today's failure**: Customer-ready output can be blocked by expected provider defaults, internal limitations can be presented as hard blockers, and unsupported foundation coverage can look like missing input. +- **User-visible improvement**: Evidence, reviews, and Review Packs can distinguish customer-ready, published-with-limitations, internal-only, and blocked states based on true unresolved governance risk. +- **Smallest enterprise-capable version**: Update Baseline Drift Posture, Evidence Snapshot completeness mapping, Environment Review readiness, Review Pack readiness/guidance, and customer-safe limitation wording only. +- **Explicit non-goals**: No new binding data model, no matching pipeline implementation, no resolution UI, no workflow engine, no new report profile architecture, no broad management report redesign, and no historical review migration. +- **Permanent complexity imported**: Readiness reason mapping, limitation categories in evidence details, review/readiness gate updates, Review Pack wording changes, internal technical rendering, and cross-surface tests. +- **Why now**: After Specs 380-383, the system has trustworthy subject resolution and operator decisions; customer-facing readiness must consume those semantics or continue showing false blockers. +- **Why not local**: A local Review Pack label patch would leave Evidence Snapshot, Baseline Drift Posture, OperationRun guidance, and Environment Review readiness inconsistent. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: Many surfaces touched and new readiness classifications. The defense is that all surfaces already consume baseline compare truth and must share publication safety semantics. +- **Score**: Nutzen: 2 | Dringlichkeit: 1 | Scope: 1 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 9/12** +- **Decision**: approve after Specs 380-383 if scope stays limited to readiness integration and customer-safe wording. + +## UI Surface Impact + +- **Reachable surface changed/added**: yes, existing Evidence, Environment Review, Review Pack, Stored Report/Review Pack readiness labels, and OperationRun guidance may change. +- **Page archetype**: readiness/status integration over existing surfaces, not a new primary workflow page. +- **Design depth**: update labels, limitation summaries, blocker messaging, and internal technical appendix rules; no new layout system. +- **Customer/operator safety**: customer-safe output must avoid raw provider IDs, `provider_resource_bindings`, `canonical_subject_key`, internal policy version IDs, and raw OperationRun payloads. +- **Dangerous/high-impact actions**: none added; publication/readiness gate behavior changes require tests because they affect customer-ready output. + +## Proportionality Review + +1. **Current operator problem**: Operators cannot safely publish customer-ready review output when false blockers and limitations are mixed together. +2. **Why existing structure is insufficient**: Evidence and Review readiness currently consume overloaded compare states and cannot distinguish resolved defaults from unresolved governed blockers. +3. **Narrowest correct implementation**: Consume the new semantics in existing readiness and export paths only; no new report architecture or workflow. +4. **Ownership cost**: Evidence, Environment Review, Review Pack, OperationRun guidance, and test owners must maintain shared readiness mappings. +5. **Rejected alternative**: Review Pack-only wording was rejected because Evidence and Environment Review would still disagree about customer readiness. +6. **Current-release truth or future prep**: Current-release customer trust work because compare output already influences Evidence, Reviews, and Review Pack export. + +## Problem + +Evidence and Review readiness currently consume baseline compare results that can contain overloaded or false blocker states. + +This can cause: + +- customer-ready output blocked by expected provider defaults, +- internal limitations presented as hard blockers, +- unsupported foundation coverage presented as missing input, +- unresolved ambiguity not clearly separated from accepted limitations, +- review packs that are too conservative or not precise enough. + +TenantPilot needs evidence/review integration that reflects the improved compare semantics. + +## Goal + +Make customer readiness and publication blockers depend on real unresolved governance risk, not on expected provider defaults or accepted limitations. + +## Scope + +### In Scope + +- Update Baseline Drift Posture source consumption of new compare semantics. +- Update Evidence Snapshot completeness mapping. +- Update Environment Review readiness. +- Update Review Pack output readiness. +- Update Review Pack output resolution guidance. +- Update customer-safe limitation wording. +- Update internal technical detail rendering. +- Add tests for blocker vs limitation behavior. +- Ensure Review Pack export does not leak raw provider IDs. + +### Out of Scope + +- New binding data model. +- Matching pipeline implementation. +- Operator resolution UI. +- Generic workflow engine. +- New report profile architecture. +- Broad management report redesign. +- Full historical review migration. + +## Evidence Completeness Mapping + +Evidence should distinguish: + +```text +complete +complete_with_limitations +partial_action_required +partial_limitation_only +missing +stale +``` + +If existing evaluator only supports: + +```text +complete +partial +missing +stale +``` + +then the additional classification can live in evidence details/readiness reason payloads while preserving public enum compatibility. + +## Baseline Drift Posture Rules + +Baseline Drift Posture must distinguish: + +```text +No drift found +Drift found +Compare blocked by unresolved ambiguity +Compare partial due to unsupported limitations +Compare incomplete due to missing evidence +Compare failed +``` + +Zero findings must not be treated as missing input when compare completed successfully. + +## Publication Rules + +### Customer-ready + +Allowed when: + +- all required governed subjects are resolved, +- no unresolved ambiguity hides real drift, +- no required evidence is missing, +- accepted limitations are allowed by profile/report policy, +- customer-safe limitation summary is available where needed. + +### Published with limitations + +Allowed when: + +- unsupported/foundation inventory-only limitations are accepted, +- excluded non-governed subjects are audited, +- no unresolved governed blocker remains, +- limitations are disclosed in customer-safe language. + +### Internal-only + +Required when: + +- unresolved ambiguity remains, +- required evidence is missing, +- compare cannot support customer-safe posture claim, +- missing provider resource cannot be confidently interpreted, +- limitation wording would be misleading externally. + +### Publication blocked + +Required when: + +- unresolved governed blockers exist, +- required review sections cannot be composed, +- evidence basis is missing, +- compare failed for required dimension, +- output would imply false assurance. + +## Review Pack Wording + +Customer-safe wording must avoid internal implementation jargon. + +Do not expose: + +```text +provider_resource_bindings +canonical_subject_key +raw provider IDs +internal policy version IDs +operation_run context payloads +``` + +Customer-safe examples: + +```text +Some baseline resources could not be verified because multiple matching resources require operator confirmation. +Some foundation configuration classes are currently monitored as inventory-only and are disclosed as limitations. +No drift was identified for the verified baseline scope. +``` + +Internal technical appendix may include more detail if the report profile allows it. + +## OperationRun and Review Guidance + +Review guidance should produce next steps such as: + +```text +Resolve ambiguous baseline subjects before publication. +Refresh evidence after baseline subject resolutions are applied. +Accept or exclude unsupported foundation resources before creating a customer-ready pack. +Re-run baseline compare after inventory sync completes. +``` + +It should not say: + +```text +Resolve review blockers +``` + +without explaining the real blocker class. + +## Integration Points + +Expected areas: + +- `BaselineDriftPostureSource` +- `EvidenceCompletenessEvaluator` +- Evidence Snapshot sources +- `EnvironmentReviewReadinessGate` +- review composer services +- `ReviewPackOutputReadiness` +- `ReviewPackOutputResolutionGuidance` +- Review Pack export/rendering services +- Stored Report / Review Pack UI readiness labels +- OperationRun follow-up support + +## Acceptance Criteria + +- Resolved built-ins/defaults do not create review publication blockers. +- Accepted limitations produce "published with limitations" where policy allows. +- Unsupported foundation inventory-only coverage is not mislabeled as missing input. +- Real unresolved ambiguity blocks customer-ready output. +- Missing provider resource after trusted identity blocks or surfaces as drift according to semantics. +- Missing local evidence is described as evidence limitation, not resource drift. +- Review Pack output remains customer-safe. +- Internal technical details remain available for operators. +- YPTW2-like compare results can become internal-only, customer-ready, or published-with-limitations based on true unresolved state. + +## Required Tests + +- Evidence test: resolved built-in does not make baseline drift posture partial action-required. +- Evidence test: unresolved ambiguity makes posture partial action-required. +- Evidence test: accepted limitation makes posture complete-with-limitations or equivalent. +- Review test: publication blocker appears only for unresolved governed blocker. +- Review test: accepted limitation does not block customer-ready output when report policy allows limitations. +- Review test: unsupported foundation appears as limitation, not missing input. +- Review Pack test: customer-safe output does not expose raw provider IDs. +- Review Pack test: internal profile can include technical diagnostics. +- Regression test: zero drift findings after completed compare is valid complete state. +- Regression test: historical/legacy compare payload still renders safely. + +## Validation Commands + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Evidence/BaselineDriftPostureSourceTest.php +``` + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/EnvironmentReview/EnvironmentReviewComposerTest.php +``` + +```bash +cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php +``` + +Add focused tests for new readiness reason mapping. + +## Risks + +- Accidentally allowing customer-ready output with unresolved risk. +- Blocking customer output for expected provider defaults. +- Leaking raw provider identities in customer pack. +- Creating inconsistent semantics between Evidence, Review, and Review Pack. +- Producing too many limitation labels and lowering trust. + +## Recommendation + +Implement this fifth. + +This candidate completes the full product loop: identity -> matching -> result semantics -> operator resolution -> evidence/review/customer-ready output. diff --git a/specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md b/specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md new file mode 100644 index 00000000..8bfde50f --- /dev/null +++ b/specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md @@ -0,0 +1,68 @@ +# Spec 379 Runtime Validation Evidence + +Date: 2026-06-14 + +## Baseline + +- Branch: `379-management-report-pdf-runtime` +- Starting HEAD: `d43ebcb4 feat(report): implement management report pdf v1 (#449)` +- Initial dirty state: only `specs/379-management-report-pdf-runtime/` was untracked. +- Spec 378 files verified: + - `docker-compose.yml` + - `apps/platform/config/tenantpilot.php` + - `apps/platform/app/Services/Pdf/PdfRenderingGateway.php` + - `apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php` + +## Local Runtime Controls + +- Gotenberg image remains pinned to `gotenberg/gotenberg:8.34.0-chromium`. +- No public Gotenberg port is exposed by the `gotenberg` service. +- Runtime safeguards remain configured: + - `API_DISABLE_DOWNLOAD_FROM=true` + - `WEBHOOK_DISABLE=true` + - `CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES=true` + - `CHROMIUM_ALLOW_LIST=^file:///tmp/.*$` + - `CHROMIUM_DENY_PRIVATE_IPS=true` + - `CHROMIUM_DENY_PUBLIC_IPS=true` + - timeout, body-limit, queue, and concurrency env controls remain present. +- `docs/deployment-checklist.md` documents the pinned image, internal service URL, and renderer hardening controls. +- Local validation on 2026-06-15 confirmed that `CHROMIUM_ALLOW_LIST=^$` makes `/forms/chromium/convert/html` return `403 Forbidden` because Chromium must navigate to Gotenberg's temporary `file:///tmp/.../index.html`. The allow-list remains restricted to that internal file path and external fetches remain disabled. + +## Staging/Dokploy Validation + +Staging/Dokploy runtime validation was not executable from this local workspace. Generation therefore remains blocked by default through: + +- `TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED=false` +- `tenantpilot.pdf_renderer.runtime_validated` +- `ManagementReportPdfRuntimeGate` + +To enable management PDF generation in Staging/Production, validate the deployed Gotenberg container/runtime path first, then set `TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED=true` in the environment. + +## Verification Commands + +- `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec379` + - Result: 7 passed, 81 assertions. +- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec379ManagementReportPdfSmokeTest.php --compact` + - Result: 1 passed, 12 assertions. +- `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec378` + - Result: 11 passed, 44 assertions. +- `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec357` + - Result: 13 passed, 108 assertions. +- `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec366` + - Result: 9 passed, 181 assertions. +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackDownloadTest.php tests/Feature/ReviewPack/ReviewPackResourceTest.php tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php` + - Result: 49 passed, 268 assertions. +- `cd apps/platform && ./vendor/bin/sail pint ...` + - Result: PASS on changed runtime/test files. +- `git diff --check` + - Result: PASS. + +## Guard Notes + +The optional guard command below was run and produced two pre-existing failures unrelated to Spec 379: + +- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php tests/Feature/Guards/ActionSurfaceContractTest.php` + - `ActionSurfaceContractTest`: operation registry URL assertion expects no navigation context, but current code returns an operations-index navigation context. + - `ActionSurfaceContractTest`: required-permissions page assertion expects `Start verification`, which the current page no longer renders. + +Both failures reproduce in isolation and no Spec379 code path is involved. diff --git a/specs/379-management-report-pdf-runtime/artifacts/screenshots/download-state.png b/specs/379-management-report-pdf-runtime/artifacts/screenshots/download-state.png new file mode 100644 index 0000000000000000000000000000000000000000..5484df7c126c31932b4c6f6112d5fb2e083bfe24 GIT binary patch literal 206824 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

    Je5KqY-3YdB82*woH1sLO_vH#t<5wg2gu{uoxs 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

    #NZn$=`Q`AS`S4VQ!x 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 literal 0 HcmV?d00001 diff --git a/specs/379-management-report-pdf-runtime/artifacts/screenshots/generate-state.png b/specs/379-management-report-pdf-runtime/artifacts/screenshots/generate-state.png new file mode 100644 index 0000000000000000000000000000000000000000..0ea96200bb106d45627409c4be76121d4eb48341 GIT binary patch literal 209801 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

    6hr>kAFeD*7H$yxZMWjg-nyZ)D^2|-? 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 btt16$h(8>pe|km_905I(S9?+}XCCxlYrpn^ literal 0 HcmV?d00001 diff --git a/specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md b/specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md new file mode 100644 index 00000000..7832fe15 --- /dev/null +++ b/specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md @@ -0,0 +1,80 @@ +# Spec 379 Storage, OperationRun, and UI Decisions + +Date: 2026-06-14 + +## Owner Surface + +V1 generation is owned by `ReviewPackResource` detail (`ViewReviewPack`). + +Rationale: + +- The Review Pack detail already owns the current customer-safe ZIP artifact, rendered-report action, readiness checks, and download capability. +- The management PDF is derived from the current ready Review Pack, not from an independent report center. +- Server-side generation authorization uses `REVIEW_PACK_MANAGE`; download uses `REVIEW_PACK_VIEW`. + +## Storage Substrate + +The PDF uses `StoredReport` as the persisted artifact substrate. + +Rationale: + +- Avoids a new report-center table or broad artifact lifecycle framework. +- Keeps workspace and managed-environment scope on the existing report model. +- Adds only narrow nullable fields required for source linkage, operation linkage, private file metadata, profile, format, status, SHA-256, and generated timestamp. +- Keeps `StoredReportResource::$isGloballySearchable = false`; no global-search behavior changed. + +Private files are stored on the existing `exports` disk under `management-reports/...` with generated safe filenames. Ready artifacts are exposed only through a signed route that re-checks tenant scope, current source Review Pack, artifact status, file existence, and `REVIEW_PACK_VIEW`. + +## OperationRun + +A distinct canonical operation type was added: + +- `report.management.generate` +- Enum: `OperationRunType::ManagementReportGenerate` +- Catalog label: `Management report PDF generation` + +Rationale: + +- Reusing `environment.review_pack.generate` would mislabel PDF generation as ZIP generation in monitoring, audit, and notifications. +- The operation does not introduce a new artifact-family taxonomy in `OperationCatalog`; it remains a stored-report-backed workflow. + +Generation queues `GenerateManagementReportPdfJob` through `OperationRunService::dispatchOrFail()` and writes terminal state through `OperationRunService::updateRun()`. + +## Audit + +Distinct audit action IDs were added because ZIP generation/download and management-PDF generation/download have different artifact semantics: + +- `management_report_pdf.generation_requested` +- `management_report_pdf.generation_blocked` +- `management_report_pdf.generated` +- `management_report_pdf.generation_failed` +- `management_report_pdf.downloaded` + +Audit metadata records workspace, managed environment, source review, source Review Pack, stored report id, operation run id, profile, SHA-256 where available, and redacted request context. Signed URLs, secrets, raw provider payloads, stack traces, and SQL errors are not included. + +## Runtime Gate + +Generation is gated by `ManagementReportPdfRuntimeGate`. + +Blocked states include: + +- renderer disabled +- unsupported renderer driver +- staging/runtime validation missing +- source Review Pack not ready, expired, missing artifact, or not current +- disclosure/profile blockers +- active generation already running + +The default environment remains blocked until `TENANTPILOT_PDF_RENDERER_RUNTIME_VALIDATED=true`. + +## UI Coverage + +- `Generate management PDF` appears in the Review Pack detail header only when no ready PDF exists. +- The action uses `Action::make(...)->action(...)`, `->requiresConfirmation()`, and server-side `REVIEW_PACK_MANAGE`. +- When generation is active, the header shows `Open PDF operation`. +- When a ready PDF exists, the header shows `Download management PDF`. +- Browser evidence: + - `specs/379-management-report-pdf-runtime/artifacts/screenshots/generate-state.png` + - `specs/379-management-report-pdf-runtime/artifacts/screenshots/download-state.png` + +No navigation redesign, customer workspace redesign, scheduled delivery, public links, AI-generated copy, or secondary renderer was added. diff --git a/specs/379-management-report-pdf-runtime/checklists/requirements.md b/specs/379-management-report-pdf-runtime/checklists/requirements.md new file mode 100644 index 00000000..21025472 --- /dev/null +++ b/specs/379-management-report-pdf-runtime/checklists/requirements.md @@ -0,0 +1,69 @@ +# Requirements Checklist: Spec 379 - Management Report PDF Runtime Validation & Generation Completion + +**Purpose**: Preparation readiness review for Spec 379 before application implementation. +**Created**: 2026-06-14 +**Feature**: `specs/379-management-report-pdf-runtime/spec.md` + +## Candidate And Scope + +- [x] CHK001 The selected candidate is directly user-provided and not invented from the automatic queue. +- [x] CHK002 The active automatic candidate queue was not used as an auto-prep source. +- [x] CHK003 Spec 378 is treated as read-only renderer/gateway baseline context, not rewritten. +- [x] CHK004 The smallest v1 slice is staging/Dokploy runtime validation plus one customer-executive Management Report PDF generation/download flow. +- [x] CHK005 New renderer infrastructure, package-governance redo, delivery center, auditor report, billing PDF, AI, and portal scope are out of scope. + +## Repo Truth And Dependencies + +- [x] CHK006 The package reuses the existing Spec 378 `PdfRenderingGateway` / `PdfRendererClient`. +- [x] CHK007 The package reuses existing rendered-report, profile, disclosure, theme, Review Pack, OperationRun, and audit paths. +- [x] CHK008 The spec records the repo-truth adjustment that Spec 378 contains pending downstream tasks but remains read-only historical baseline. +- [x] CHK009 Runtime validation is a hard gate before generation enablement. +- [x] CHK010 The plan includes deployment impact for staging/Dokploy, env/config, queues, storage, and migrations. + +## Security, RBAC, And Isolation + +- [x] CHK011 Workspace and managed-environment scope are explicit for generation, storage, lookup, operation, and download. +- [x] CHK012 Non-member or wrong-scope access uses deny-as-not-found semantics. +- [x] CHK013 Scoped member without capability receives 403 after scope is established. +- [x] CHK014 PDF content and audit metadata forbid secrets, signed URLs, raw provider payloads, raw operation context, SQL errors, stack traces, and serialized jobs. +- [x] CHK015 Download must be signed and/or server-authorized and must re-resolve scope before returning bytes. + +## OperationRun, Audit, And Artifact Truth + +- [x] CHK016 The preferred implementation creates or reuses an OperationRun for generation. +- [x] CHK017 OperationRun lifecycle must flow through `OperationRunService`. +- [x] CHK018 Generation and download audit metadata are specified. +- [x] CHK019 Artifact truth carries source review/pack, workspace, environment, profile, actor, generated time, private storage, and operation-run provenance. +- [x] CHK020 A new artifact table/entity is not approved by default; implementation must stop and update spec/plan if one is required. + +## UI And Productization Coverage + +- [x] CHK021 UI Surface Impact is marked as changed reachable surfaces, not no-impact. +- [x] CHK022 Affected surfaces are bounded to existing owner detail pages, existing rendered-report source, optional PDF route, and artifact registry only if reused. +- [x] CHK023 Generate action is classified as high-impact artifact creation and requires explicit confirmation. +- [x] CHK024 UI coverage artifacts or checked no-update rationale are required during implementation close-out. +- [x] CHK025 No panel provider or navigation change is planned. + +## Testing And Validation + +- [x] CHK026 Unit tests are required for runtime validation, payload, readiness, disclosure, and renderer adapter behavior. +- [x] CHK027 Feature tests are required for generation, storage, OperationRun, audit, authorization, and download. +- [x] CHK028 Filament/Livewire action tests are required for the selected owner surface. +- [x] CHK029 Browser/content smoke is required if local fixtures can cover generation/download. +- [x] CHK030 PostgreSQL lane is required if migrations/indexes/schema constraints are introduced. +- [x] CHK031 Spec378 gateway regression is included in validation. + +## Filament / Livewire / Deployment Contract + +- [x] CHK032 Livewire v4.0+ compliance is explicit; no Livewire v3 APIs are planned. +- [x] CHK033 Provider registration location remains `apps/platform/bootstrap/providers.php`; no panel provider change is planned. +- [x] CHK034 Global search posture remains disabled for StoredReport unless a future spec updates it safely. +- [x] CHK035 Asset strategy expects no new Filament assets; deploy uses existing `filament:assets` only if assets are registered. +- [x] CHK036 Runtime validation is a staging/production promotion gate. + +## Review Outcome + +- [x] CHK037 Candidate Selection Gate result: PASS with repo-truth adjustment. +- [x] CHK038 Spec Readiness Gate result: PASS for preparation. +- [x] CHK039 Preparation is implementation-ready for a later implementation loop. +- [x] CHK040 No application implementation was performed during preparation. diff --git a/specs/379-management-report-pdf-runtime/plan.md b/specs/379-management-report-pdf-runtime/plan.md new file mode 100644 index 00000000..c7673a55 --- /dev/null +++ b/specs/379-management-report-pdf-runtime/plan.md @@ -0,0 +1,283 @@ +# Implementation Plan: Spec 379 - Management Report PDF Runtime Validation & Generation Completion + +**Branch**: `379-management-report-pdf-runtime` | **Date**: 2026-06-14 | **Spec**: `specs/379-management-report-pdf-runtime/spec.md` +**Input**: Feature specification from `specs/379-management-report-pdf-runtime/spec.md` + +## Summary + +Validate the merged Spec 378 Gotenberg/PDF runtime in staging/Dokploy and complete the actual customer-safe Management Report PDF generation flow. The implementation must reuse `PdfRenderingGateway` / `PdfRendererClient`, existing Review Pack rendered-report truth, `ReportProfileRegistry`, `ReportDisclosurePolicy`, OperationRun, audit, and private storage patterns. It must not rebuild PDF infrastructure or expand into report delivery, auditor reports, billing PDFs, or a generic document engine. + +Spec 379 is the sole active implementation package for post-`G012` runtime validation and downstream Management Report PDF generation completion. Spec 378's unchecked downstream generation tasks are read-only historical baseline signals and must not be executed as a parallel work queue. + +## 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, approved Gotenberg 8 Chromium internal service from Spec 378 +**Storage**: PostgreSQL plus private Laravel storage for generated PDF artifacts if file output is persisted +**Testing**: Pest Unit, Feature, Filament/Livewire action tests, Browser/content smoke +**Validation Lanes**: fast-feedback, confidence, browser, pgsql if schema/indexes are introduced +**Target Platform**: Laravel Sail locally; Dokploy container deployment for staging/production +**Project Type**: Laravel monolith under `apps/platform` +**Performance Goals**: generation is queue-safe and observable; render/download avoids live provider calls; failed runtime/storage cases fail closed +**Constraints**: no Graph calls during render/generation/download; only approved PDF gateway; private artifact storage; workspace/managed-environment isolation; confirmation-gated high-impact generation; customer-safe disclosure +**Scale/Scope**: one on-demand `customer_executive` Management Report PDF path over one ready current review/report source + +## Repo Truth Map + +Renderer/runtime baseline from Spec 378: + +- `docker-compose.yml` includes pinned internal `gotenberg/gotenberg:8.34.0-chromium`. +- `apps/platform/config/tenantpilot.php` exposes PDF renderer config. +- `apps/platform/app/Services/Pdf/PdfRenderRequest.php` +- `apps/platform/app/Services/Pdf/PdfRenderResult.php` +- `apps/platform/app/Services/Pdf/PdfRendererClient.php` +- `apps/platform/app/Services/Pdf/PdfRenderingGateway.php` +- `apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php` +- `docs/package-governance.md` approves Gotenberg with controls. +- `docs/deployment-checklist.md` records Dokploy/runtime controls. + +Report/profile foundations: + +- `apps/platform/app/Http/Controllers/ReviewPackRenderedReportController.php` +- `apps/platform/resources/views/review-packs/rendered-report.blade.php` +- `apps/platform/app/Support/ReviewPacks/ReportProfileRegistry.php` +- `apps/platform/app/Support/ReviewPacks/ReportDisclosurePolicy.php` +- `apps/platform/app/Support/ReviewPacks/ReportThemeResolver.php` +- `apps/platform/app/Services/ReviewPackService.php` +- `apps/platform/app/Jobs/GenerateReviewPackJob.php` +- `apps/platform/app/Models/ReviewPack.php` +- `apps/platform/app/Models/EnvironmentReview.php` +- `apps/platform/app/Models/StoredReport.php` + +Execution/audit foundations: + +- `apps/platform/app/Services/OperationRunService.php` +- `apps/platform/app/Support/OperationRunType.php` +- `apps/platform/app/Support/OperationCatalog.php` +- `apps/platform/app/Support/OperationRunLinks.php` +- `apps/platform/app/Support/OpsUx/OperationUxPresenter.php` +- current audit logger/action ID paths used by Review Pack generation/download flows + +## Repo-Truth Decisions To Resolve During Implementation + +1. **Owner surface**: Prefer Review Pack detail or Environment Review detail. Do not add a new top-level page unless both existing surfaces are proven insufficient. +2. **Artifact substrate**: Prefer a narrow extension of existing report/artifact storage if needed. If a new table/entity is required, stop and update spec/plan before implementation continues. +3. **Operation type**: Reuse an existing canonical type only if it honestly represents management PDF generation. Otherwise add a narrow `report.management.generate` type/catalog mapping and tests. +4. **UI coverage**: Update route inventory/page reports/design matrix only for material surface changes, or record an explicit checked no-update rationale. +5. **Capabilities**: Generation uses `Capabilities::ENVIRONMENT_REVIEW_MANAGE` on an Environment Review owner surface or `Capabilities::REVIEW_PACK_MANAGE` on a Review Pack owner surface. Download uses `Capabilities::REVIEW_PACK_VIEW`. Any new capability requires spec/plan update and canonical registry tests. + +## UI / Surface Guardrail Plan + +- **Guardrail scope**: changed existing report/review artifact owner surface plus possible new signed/server-authorized PDF download route. +- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: + - `ReviewPackResource` detail page and/or `EnvironmentReviewResource` detail page + - existing rendered report source route + - optional generated PDF download route/controller + - `StoredReportResource` only if generated PDFs are listed/viewed there +- **No-impact class, if applicable**: N/A. +- **Native vs custom classification summary**: owner actions stay native Filament; PDF content inherits the bounded custom report artifact exception from Spec 366; artifact registry remains native if used. +- **Shared-family relevance**: report viewer, artifact download, status/readiness messaging, OperationRun UX, audit. +- **State layers in scope**: owner action state, modal confirmation, artifact state, OperationRun status/outcome, signed/download route, private storage. +- **Audience modes in scope**: customer-read-only PDF content; operator-MSP generation/download; support-platform diagnostics only through existing OperationRun/log surfaces. +- **Decision/diagnostic/raw hierarchy plan**: PDF is decision-first; OperationRun/logs carry diagnostics; raw/support evidence is absent from customer PDF. +- **Raw/support gating plan**: raw JSON, provider payloads, signed URLs, secrets, stack traces, SQL errors, and internal MSP/support fields are forbidden in PDF and audit metadata. +- **One-primary-action / duplicate-truth control**: owner surface shows one state-dependent action: generate, view operation, or download existing PDF. +- **Handling modes by drift class or surface**: hard-stop for renderer duplication, public artifact storage, raw leakage, wrong-scope download, or unvalidated runtime enablement. +- **Repository-signal treatment**: update UI coverage artifacts only when material; otherwise record no-update rationale in close-out. +- **Special surface test profiles**: report-viewer plus high-impact artifact-generation action. +- **Required tests or manual smoke**: Unit payload/readiness/disclosure; Feature authorization/storage/audit/run/download; Filament action state; Browser/content smoke. +- **Exception path and spread control**: any custom report/PDF rendering view remains bounded to management report output and consumes existing profile/disclosure truth. +- **Active feature PR close-out entry**: Guardrail + Smoke Coverage + Runtime Validation. +- **UI/Productization coverage decision**: reachable UI surfaces change. +- **Coverage artifacts to update**: route inventory for new route, UI-099 for report/PDF content change, UI-042 for Review Pack action/download changes, UI-048 for StoredReport exposure, design coverage matrix when action/artifact state materially changes. +- **No-impact rationale**: N/A. +- **Navigation / Filament provider-panel handling**: no navigation or panel-provider change planned. Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`. +- **Screenshot or page-report need**: yes for bounded browser/content smoke if UI route/action/content changes are implemented. + +## Shared Pattern & System Fit + +- **Cross-cutting feature marker**: yes. +- **Systems touched**: PDF gateway, Review Pack rendered report, report profile/disclosure policy, artifact storage, OperationRun, audit, localization, Filament owner actions. +- **Shared abstractions reused**: `PdfRenderingGateway`, `ReportProfileRegistry`, `ReportDisclosurePolicy`, `ReportThemeResolver`, `ReviewPackService`, `OperationRunService`, `OperationRunLinks`, `OperationUxPresenter`, audit logger/action ID patterns. +- **New abstraction introduced? why?**: one bounded generation service/payload builder/renderer adapter is allowed because PDF generation must be testable and must not live in Blade/controller code. +- **Why the existing abstraction was sufficient or insufficient**: existing abstractions provide renderer and report truth; they do not own generated artifact identity, OperationRun generation lifecycle, or download audit. +- **Bounded deviation / spread control**: implementation stays in existing report/review-pack/pdf namespaces; no generic document framework. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: yes. +- **Central contract reused**: `OperationRunService`, `OperationRunLinks`, `OperationUxPresenter`, `OperationCatalog`, `OperationRunType` if needed. +- **Delegated UX behaviors**: queued toast, `View operation`, artifact link, run-enqueued browser event, blocked/start-failure messaging, tenant/workspace-safe URL resolution. +- **Surface-owned behavior kept local**: source readiness explanation, selected profile, and blocked reason. +- **Queued DB-notification policy**: no running DB notifications; queued DB notification only if already supported and explicitly opted in. +- **Terminal notification path**: central lifecycle mechanism. +- **Exception path**: none. Synchronous no-run generation is out of scope for Spec 379 and requires a future spec before implementation. + +## Provider Boundary & Portability Fit + +- **Shared provider/platform boundary touched?**: yes, only through existing stored report/evidence/review content. +- **Provider-owned seams**: existing Microsoft/provider labels inside allowed report summaries. +- **Platform-core seams**: artifact identity, profile, evidence basis, limitation, operation type, source review/pack provenance. +- **Neutral platform terms / contracts preserved**: management report, profile, evidence basis, limitation, managed environment, generated artifact, operation. +- **Retained provider-specific semantics and why**: existing profile-safe summaries only. +- **Bounded extraction or follow-up path**: Technical/Auditor report if deeper provider appendix is needed. + +## Constitution Check + +*GATE: Must pass before implementation. Re-check after runtime decisions and before code merge.* + +- Inventory-first: PASS. PDF consumes existing stored review/report truth, not live provider state. +- Read/write separation: PASS with controls. Generation is explicit TenantPilot artifact creation with confirmation, audit, and tests. +- Graph contract path: PASS. No Graph calls during render/generation/download. +- Deterministic capabilities: REQUIRED. Reuse capability registry or add tested capability only if necessary. +- RBAC-UX: REQUIRED. Non-member/wrong scope 404; scoped member missing capability 403; server-side authorization always. +- Workspace isolation: REQUIRED. Artifact, source, run, and download resolve workspace/environment scope. +- Destructive/high-impact actions: REQUIRED. Generate PDF is high-impact artifact creation and requires confirmation. +- Global search: PASS. No global-search enablement planned; if StoredReport is touched, keep disabled unless updated by spec. +- Run observability: REQUIRED. Queued/observable generation through OperationRun is mandatory. +- OperationRun start UX: REQUIRED. Central start/link/notification paths only. +- Ops-UX lifecycle: REQUIRED. Status/outcome transitions through `OperationRunService`. +- Data minimization: REQUIRED. No secrets/raw payloads/signed URLs in PDF or audit metadata. +- Test governance: REQUIRED. Unit/Feature/Filament/Browser lanes explicit. +- Proportionality: PASS with controls. Durable PDF artifact truth solves current report handoff gap. +- No premature abstraction: PASS with controls. Bounded generation components only. +- Persisted truth: PASS if generated artifact metadata is durable and source-bound. +- Behavioral state: PASS only for state/reason values that change generation/download behavior. +- UI semantics: PASS. Directly map existing profile/readiness/disclosure truth. +- Shared pattern first: PASS. Existing renderer/report/operation/audit paths first. +- Provider boundary: PASS. Stored summaries only; no provider runtime calls. +- UI-COV-001: REQUIRED. Coverage update or explicit no-update rationale required. + +## Test Governance Check + +- **Test purpose / classification by changed surface**: + - Unit: runtime validation decision mapping, payload/readiness/disclosure, renderer adapter failure mapping. + - Feature: generation, private artifact storage, authorization, OperationRun, audit, download. + - Filament/Livewire: owner-surface action visible/disabled/confirmed/queued state. + - Browser/content smoke: generated/downloaded PDF content and leakage boundaries. +- **Affected validation lanes**: fast-feedback, confidence, browser, pgsql if schema/indexes are introduced. +- **Why this lane mix is the narrowest sufficient proof**: each lane proves one trust boundary and avoids broad surface discovery. +- **Narrowest proving commands**: + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec379` + - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec379ManagementReportPdfSmokeTest.php --compact` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec378` + - `cd apps/platform && ./vendor/bin/sail pint --dirty` + - `git diff --check` +- **Fixture / helper / factory / seed / context cost risks**: add explicit Spec379 fixtures only; do not widen global report fixtures. +- **Expensive defaults or shared helper growth introduced?**: no; browser smoke is explicit and feature-local. +- **Heavy-family additions, promotions, or visibility changes**: one explicit browser/content smoke. +- **Surface-class relief / special coverage rule**: high-impact artifact action and report-viewer. +- **Closing validation and reviewer handoff**: verify no duplicate renderer, no public artifact storage, no raw leakage, no wrong-scope download, no unvalidated runtime enablement. +- **Budget / baseline / trend follow-up**: record browser smoke runtime if materially slow. +- **Review-stop questions**: lane fit, fixture cost, runtime validation proof, artifact substrate proportionality, OperationRun truth. +- **Escalation path**: `document-in-feature` for local decisions; `follow-up-spec` for delivery center, retention/lifecycle framework, technical/auditor PDF, billing PDF. +- **Active feature PR close-out entry**: Guardrail + Runtime Validation + Smoke Coverage. +- **Why no dedicated follow-up spec is needed**: v1 generation is the direct follow-through over Spec 378; broader delivery/lifecycle/report families stay deferred. + +## Project Structure + +### Documentation (this feature) + +```text +specs/379-management-report-pdf-runtime/ +├── spec.md +├── plan.md +├── tasks.md +├── checklists/ +│ └── requirements.md +└── artifacts/ + ├── runtime-validation.md # created during implementation + ├── storage-operationrun-decision.md # created during implementation + └── screenshots/ # created during browser/content smoke if captured +``` + +### Source Code (likely affected, implementation only) + +```text +apps/platform/app/Services/Pdf/ +apps/platform/app/Support/ReviewPacks/ +apps/platform/app/Services/ReviewPackService.php +apps/platform/app/Jobs/ +apps/platform/app/Models/StoredReport.php +apps/platform/app/Filament/Resources/ReviewPackResource/ +apps/platform/app/Filament/Resources/EnvironmentReviewResource/ +apps/platform/app/Http/Controllers/ +apps/platform/routes/ +apps/platform/database/migrations/ +apps/platform/tests/Unit/ +apps/platform/tests/Feature/ +apps/platform/tests/Browser/ +apps/platform/resources/lang/ +``` + +**Structure Decision**: Use the existing Laravel/Filament monolith. Add only feature-local report/PDF generation pieces under current report/PDF namespaces. Do not add new base folders or dependencies. + +## Implementation Phases + +### Phase 0 - Repo Verification And Runtime Gate + +Re-read Spec 378 artifacts and current PDF runtime files. Validate staging/Dokploy runtime controls and record results in `specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md`. If validation fails, block report generation and implement only safe disabled/blocked behavior. + +### Phase 1 - Tests First + +Add failing Unit, Feature, Filament/Livewire, and Browser/content smoke coverage for payload, readiness, authorization, generation, OperationRun, audit, download, and leakage boundaries. + +### Phase 2 - Payload And Disclosure + +Build a deterministic customer-executive management report payload from existing Review Pack/rendered-report/profile/disclosure truth. No Graph calls, no Blade queries, no duplicated renderer infrastructure. + +### Phase 3 - Artifact Storage And Idempotency + +Choose the smallest artifact substrate. Prefer narrow existing substrate extension if needed; stop for spec/plan update if a new entity/table is required. Store PDFs privately and protect against partial-ready artifacts. + +### Phase 4 - OperationRun And Audit + +Queue and track generation through OperationRun, map success/blocked/failed outcomes safely, and record generation/download audit metadata without secrets or raw payloads. + +### Phase 5 - Owner Action And Download Route + +Add one confirmed high-impact owner-surface action, disabled/blocked reasons, canonical operation link, existing-artifact download state, and a signed/server-authorized route if needed. + +### Phase 6 - PDF Rendering And Localization + +Render through `PdfRenderingGateway`, include required management chapters and provenance, use private/local assets only, and add EN/DE labels for action, blocked reasons, chapters, limitations, and provenance where needed. + +### Phase 7 - Coverage And Validation + +Run focused tests, browser/content smoke, Spec378 regression, Pint dirty, `git diff --check`, and record deployment impact, UI coverage decision, and final no-duplication scan. + +## Complexity Tracking + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|---|---|---| +| Durable generated artifact metadata | The PDF must be downloadable, auditable, and source-bound after generation | Browser print cannot provide artifact truth, authorization, or audit | +| Bounded generation service/payload builder | Generation needs tests, readiness, storage, audit, and OperationRun boundaries | Controller/Blade code would duplicate truth and hide failures | +| Possible operation type mapping | Management PDF generation is an operator-visible queued artifact operation | Reusing an unrelated type would mislabel monitoring/audit truth | + +## Proportionality Review + +- **Current operator problem**: no real customer-ready Management Report PDF despite approved renderer infrastructure. +- **Existing structure is insufficient because**: Spec 378 gateway renders bytes but does not manage report payload, artifact identity, generation lifecycle, authorization, or audit. +- **Narrowest correct implementation**: validate runtime and implement one customer-executive generation/download path over existing review/report truth. +- **Ownership cost created**: tests, possible schema extension, operation/audit mapping, deployment validation artifact, one browser smoke. +- **Alternative intentionally rejected**: second renderer, generic document/report engine, public links, delivery center, technical/auditor report, billing PDF. +- **Release truth**: current-release follow-through over merged Spec 378. + +## Rollout And Deployment Considerations + +- **Environment variables**: use existing Spec 378 PDF renderer env/config. Add only if implementation finds a missing bounded runtime switch. +- **Migrations**: possible only for narrow artifact metadata/storage fields; PostgreSQL lane required if added. +- **Queues/workers**: queued generation requires Dokploy workers and queue visibility. +- **Storage**: private persistent disk/object storage required for PDF artifacts. +- **Scheduler**: none expected. +- **Filament assets**: no new Filament assets expected; `filament:assets` remains part of deploy only if assets are registered. +- **Staging gate**: staging/Dokploy runtime validation is mandatory before production promotion. + +## Filament v5 / Livewire v4 Output Contract + +- Livewire v4.0+ compliance: target app uses Livewire 4.1.4; no Livewire v3 APIs are planned. +- Provider registration: no panel provider change planned; Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`. +- Global search: no globally searchable resource is introduced; `StoredReportResource` remains non-globally-searchable unless a future spec updates it with safe View/Edit semantics. +- Destructive/high-impact actions: `Generate management PDF` is high-impact artifact creation, must use `Action::make(...)->action(...)`, explicit confirmation, server authorization through `ENVIRONMENT_REVIEW_MANAGE` or `REVIEW_PACK_MANAGE` according to selected owner surface, readiness gate, audit, and tests. PDF download requires `REVIEW_PACK_VIEW`. +- Asset strategy: no new panel/shared assets expected; deploy uses existing `filament:assets` requirement only if assets are registered. +- Testing plan: Feature/Filament action tests cover owner action, authorization, OperationRun, audit, and download; Browser/content smoke covers customer-facing PDF. diff --git a/specs/379-management-report-pdf-runtime/spec.md b/specs/379-management-report-pdf-runtime/spec.md new file mode 100644 index 00000000..20ca2e12 --- /dev/null +++ b/specs/379-management-report-pdf-runtime/spec.md @@ -0,0 +1,393 @@ +# Feature Specification: Spec 379 - Management Report PDF Runtime Validation & Generation Completion + +**Feature Branch**: `379-management-report-pdf-runtime` +**Created**: 2026-06-14 +**Status**: Draft / Ready for implementation preparation review +**Input**: User-provided "Spec 379 - Management Report PDF Runtime Validation & Generation Completion" draft from `/Users/ahmeddarrazi/.codex/attachments/9faae34d-2858-4176-a1d4-e614689099e4/pasted-text.txt`. + +## Repo-Truth Adjustment + +Spec 378 is present in the repo as `specs/378-management-report-pdf-v1/` and the latest `platform-dev` commit (`d43ebcb4`) merged the PDF/Gotenberg runtime baseline: + +- pinned internal Gotenberg 8 Chromium runtime in root `docker-compose.yml` +- `apps/platform/app/Services/Pdf/PdfRenderingGateway.php` +- `apps/platform/app/Services/Pdf/PdfRendererClient.php` +- renderer config in `apps/platform/config/tenantpilot.php` +- package governance approval in `docs/package-governance.md` +- deployment checklist controls in `docs/deployment-checklist.md` +- Spec 378 renderer/gateway tasks through `G012` + +Spec 378 still contains pending downstream report-generation tasks. This Spec 379 does not rewrite that history. It treats the merged Spec 378 renderer/gateway work as the baseline and prepares the follow-up implementation package for the remaining runtime validation plus actual Management Report PDF generation completion. + +Spec 379 is the sole active implementation owner for post-`G012` runtime validation and downstream Management Report PDF generation completion. The unchecked downstream tasks in Spec 378 are historical baseline signals only and must not be executed as a parallel implementation queue. + +## Spec Candidate Check *(mandatory - SPEC-GATE-001)* + +- **Problem**: TenantPilot has an approved and implemented PDF renderer/gateway baseline, but it still cannot produce a real, stored, downloadable, auditable Management Report PDF from existing review/report truth. +- **Today's failure**: The product can validate the rendering infrastructure in code, but an operator still cannot complete the customer-safe PDF handoff with runtime validation, artifact storage, OperationRun tracking, download authorization, and audit evidence. +- **User-visible improvement**: An entitled operator can generate a customer-safe management PDF from a ready current review/report source, see truthful queued or blocked feedback, download the artifact through an authorized path, and rely on provenance, disclosure, and audit trail. +- **Smallest enterprise-capable version**: Validate the existing Gotenberg runtime controls in staging/Dokploy, then complete one on-demand `customer_executive` Management Report PDF generation path over existing Review Pack/rendered-report/profile/disclosure truth. +- **Explicit non-goals**: No new PDF renderer, no new Gotenberg service, no new PDF package, no package-governance redo, no invoice/billing/e-invoice scope, no Technical or Auditor Evidence Report, no Report Delivery Center, no scheduled/email delivery, no customer portal, no dashboard redesign, no AI summary, no Word/Excel/PowerPoint export, no full Localization v1. +- **Permanent complexity imported**: A bounded management-report payload/generation service or equivalent adapter, optional narrow existing artifact substrate extension, possible canonical operation type mapping if no existing type fits, focused localization keys, focused Unit/Feature/Filament/Browser tests, and a staging runtime validation artifact. +- **Why now**: Spec 378 established the infrastructure baseline but stopped before generation. Leaving the gap open means the repo has PDF runtime machinery without the customer-facing report artifact the runtime was approved to support. +- **Why not local**: A local button or browser print workaround would not prove staging renderer availability, durable artifact truth, OperationRun observability, authorization, download audit, or disclosure-safe customer content. +- **Approval class**: Core Enterprise. +- **Red flags triggered**: Durable artifact output, possible stored-report substrate extension, possible operation type, and customer-facing PDF surface. Defense: the slice reuses existing renderer/profile/disclosure/report truth, forbids new renderer infrastructure, and allows no new report center or generic document framework. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12** +- **Decision**: approve as the bounded post-Spec-378 generation completion package. + +## Candidate Selection Gate + +- **Selected candidate**: Spec 379 - Management Report PDF Runtime Validation & Generation Completion. +- **Source**: Direct user-provided Spec 379 draft and current repo truth after Spec 378 merge. +- **Why selected**: The active automatic candidate queue is not a safe auto-prep source, while the user provided a concrete follow-up that aligns with the latest merged PDF runtime baseline. +- **Roadmap relationship**: Supports management-ready reporting, customer-safe review consumption, governance artifact maturity, and Governance-as-a-Service packaging without opening delivery-center or auditor-report scope. +- **Close alternatives deferred**: + - Reopening Spec 378: deferred because its merged renderer/gateway history must remain intact. + - Technical/Auditor Evidence Report PDF: separate report profile and evidence-depth package. + - Report Delivery Center or scheduled delivery: deferred until one on-demand PDF artifact is generated, stored, and audited. + - Customer Review Workspace redesign: adjacent productization, not required for the first generation path. + - Billing/invoice PDF: separate domain with legal numbering, tax, archive, and e-invoice requirements. +- **Completed-spec guardrail result**: + - `specs/356-review-pack-pdf-html-renderer-v1/`, `specs/357-report-profiles-disclosure-policy-v1/`, and `specs/366-management-report-layout-branded-report-themes-v1/` contain completed task/validation signals and are historical context only. + - `specs/378-management-report-pdf-v1/` is read-only baseline context for the renderer/gateway merge. This package does not modify it or remove pending-task history. + - No `specs/379-*` package existed before this preparation run. +- **Smallest viable implementation slice**: G013 staging/Dokploy runtime validation plus one queued/observable customer-executive PDF generation, storage, download, and audit flow from existing ready current Review Pack/rendered-report truth. +- **Gate result**: PASS, with the explicit repo-truth adjustment that Spec 379 must not duplicate or rebuild Spec 378 renderer infrastructure. + +## Problem Statement + +TenantPilot has the PDF rendering gateway, but not the finished Management Report PDF workflow. The next implementation must verify that the approved renderer runtime works in the deployed container environment and then complete the business flow: build the report payload from existing review truth, render it through the approved gateway, persist the artifact safely, expose authorized download, and record operation/audit evidence. + +## Business / Product Value + +- MSP operators can produce a real PDF for customer governance reviews instead of relying on browser print behavior. +- Customer stakeholders receive a decision-first report that names posture, risks, decisions, accepted risks, evidence basis, limitations, and next actions without raw technical leakage. +- TenantPilot can prove who generated and downloaded the PDF, from which source review/pack, under which profile/disclosure policy, and with which renderer/runtime status. +- Future delivery, auditor, technical, and packaging specs become smaller because generation/storage/audit basics are proven once. + +## Primary Users / Operators + +- MSP service manager generating a customer-ready management report. +- TenantPilot operator validating that a report is safe to share externally. +- Customer executive or IT lead reading the generated PDF. +- Support/platform operator diagnosing a failed generation through OperationRun and safe runtime validation evidence. + +## Spec Scope Fields *(mandatory)* + +- **Scope**: workspace plus managed-environment artifact generation and canonical report/download view. +- **Primary Routes**: + - existing rendered report route `/admin/review-packs/{reviewPack}/report` + - existing Review Pack detail route under `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` + - existing Environment Review detail route under `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` + - one new signed and/or server-authorized PDF download route only if existing routes cannot safely represent PDF format/profile/artifact identity +- **Data Ownership**: Generated Management Report PDF is tenant-owned artifact truth bound to workspace, managed environment, source review/pack, profile, generated actor/time, operation run, private storage location, and audit records. If new persisted tenant-owned artifact columns are added, tenant/workspace ownership must satisfy the constitution. +- **RBAC**: Workspace membership, managed-environment entitlement, and the existing review/report capability path are required. Generation uses `Capabilities::ENVIRONMENT_REVIEW_MANAGE` when the Environment Review owner surface is selected and `Capabilities::REVIEW_PACK_MANAGE` when the Review Pack owner surface is selected. Download uses `Capabilities::REVIEW_PACK_VIEW` against the source managed environment. Wrong workspace/environment is deny-as-not-found. Scoped member without the required generation/download capability receives 403 after scope is established. Any new capability requires spec/plan update plus canonical capability-registry tests before implementation. + +For canonical-view specs: + +- **Default filter behavior when tenant-context is active**: Generation is anchored to one source review or current ready Review Pack. Hidden remembered environment/session state must not decide source scope. +- **Explicit entitlement checks preventing cross-tenant leakage**: Generation, artifact lookup, OperationRun link, and PDF download must re-resolve workspace and managed-environment scope from persisted source/artifact truth and current actor entitlement. + +## 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 +- [ ] Filament panel/provider surface changed +- [x] New modal/drawer/wizard/action added +- [x] New table/form/state added +- [x] 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**: + - selected owner surface: Review Pack detail or Environment Review detail + - existing rendered report source `/admin/review-packs/{reviewPack}/report` + - optional new PDF download route/controller + - StoredReport or existing artifact detail only if chosen as the artifact registry +- **Current or new page archetype**: existing report/artifact owner detail plus generated PDF artifact download. +- **Design depth**: Strategic customer-facing report surface. +- **Repo-truth level**: renderer/gateway baseline repo-verified; PDF generation flow spec-ready. +- **Existing pattern reused**: `PdfRenderingGateway`, `PdfRendererClient`, `ReportProfileRegistry`, `ReportDisclosurePolicy`, `ReportThemeResolver`, current rendered-report payload, current Review Pack readiness/current-pack checks, current signed URL/download patterns, current OperationRun and audit patterns. +- **New pattern required**: Bounded management PDF artifact generation and download state only. +- **Screenshot required**: yes, because Browser/content smoke is required for the generated/downloaded PDF artifact. +- **Page audit required**: update UI-099, UI-042, UI-048, route inventory, or design coverage only when implementation materially changes those surfaces; otherwise record a checked no-update rationale. +- **Customer-safe review required**: yes. The generated PDF is customer-facing by default. +- **Dangerous-action review required**: yes by high-impact artifact creation. It is not destructive to Microsoft tenant state, but it creates durable shareable output and therefore needs explicit confirmation, authorization, readiness gate, audit, and tests. +- **Coverage files updated or explicitly not needed**: + - [ ] `docs/ui-ux-enterprise-audit/route-inventory.md` + - [ ] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` + - [ ] `docs/ui-ux-enterprise-audit/page-reports/...` + - [ ] `docs/ui-ux-enterprise-audit/strategic-surfaces.md` + - [ ] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` + - [ ] `docs/ui-ux-enterprise-audit/unresolved-pages.md` + - [ ] `N/A - no reachable UI surface impact` +- **No-impact rationale when applicable**: N/A. + +## Cross-Cutting / Shared Pattern Reuse + +- **Cross-cutting feature?**: yes. +- **Interaction class(es)**: report generation, report viewer, artifact download, status/readiness messaging, header action, OperationRun start/link UX, audit. +- **Systems touched**: PDF renderer gateway, Review Pack rendered report, report profiles, disclosure policy, review/report owner surfaces, artifact substrate, OperationRun, audit, localization, storage. +- **Existing pattern(s) to extend**: current rendered-report profile/disclosure path, Review Pack current-pack/readiness checks, signed download patterns, OperationRun start/completion UX, safe audit metadata patterns. +- **Shared contract / presenter / builder / renderer to reuse**: `PdfRenderingGateway`, `ReportProfileRegistry`, `ReportDisclosurePolicy`, `ReportThemeResolver`, `ReviewPackService`, `OperationRunService`, `OperationRunLinks`, `OperationUxPresenter`, audit logger/action ID pattern. +- **Why the existing shared path is sufficient or insufficient**: Existing paths are sufficient for runtime rendering, report content, profile/disclosure, and source readiness. They are insufficient for final PDF artifact identity, storage, OperationRun generation flow, staging runtime validation proof, and download audit. +- **Allowed deviation and why**: A bounded management report payload builder/generation service/renderer adapter is allowed to keep generation out of controller/Blade code. It must consume the existing gateway and report truth rather than define new infrastructure. +- **Consistency impact**: HTML source report, PDF artifact, OperationRun, artifact metadata, and audit records must agree on source review, source pack, profile, generation actor/time, environment scope, and limitations. +- **Review focus**: no second renderer, no raw provider payloads, no signed URL leakage, no stack traces/SQL errors, no internal MSP-only content in customer PDF, no hidden cross-environment source selection. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: yes. +- **Shared OperationRun UX contract/layer reused**: `OperationRunService`, `OperationRunType`/`OperationCatalog` if a new type is needed, `OperationRunLinks`, `OperationUxPresenter`, and existing terminal notification path. +- **Delegated start/completion UX behaviors**: queued toast, `View operation` link, artifact link, run-enqueued browser event, dedupe or blocked messaging, and tenant/workspace-safe URL resolution. +- **Local surface-owned behavior that remains**: readiness explanation, source review/pack identity, and disabled/blocked reason. +- **Queued DB-notification policy**: no running DB notifications; queued DB notification only if explicitly supported by the shared OperationRun UX contract and approved in implementation notes. +- **Terminal notification path**: central lifecycle mechanism. +- **Exception required?**: none. Synchronous no-run generation is out of scope for Spec 379 and would require a future spec before implementation. + +## Provider Boundary / Platform Core Check + +- **Shared provider/platform boundary touched?**: yes, indirectly through report/evidence content. +- **Boundary classification**: platform-core report artifact and operation truth; provider-owned evidence details only where already present in disclosure-safe review/report payloads. +- **Seams affected**: report artifact identity, source review/pack provenance, evidence basis, operation type/catalog label, artifact source descriptor. +- **Neutral platform terms preserved or introduced**: management report, profile, evidence basis, limitation, managed environment, workspace, generated artifact, provenance, operation. +- **Provider-specific semantics retained and why**: only existing profile-safe Microsoft/provider summaries already allowed by the source report. No new provider runtime calls or Microsoft-specific renderer behavior. +- **Why this does not deepen provider coupling accidentally**: PDF generation uses stored review/report truth only and calls only the internal PDF renderer gateway, not Microsoft Graph. +- **Follow-up path**: follow-up-spec for Technical/Auditor Evidence Report or provider-specific appendix depth. + +## 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 | +|---|---|---|---|---|---|---| +| Generate management PDF action | yes | Native Filament action | header action, readiness messaging, OperationRun start | detail header, modal, notification | no | High-impact artifact creation, not Microsoft tenant mutation | +| PDF download route/action | yes | signed/server-authorized route plus native action/link | artifact download, audit | detail, URL, storage | no | Must re-resolve scope before bytes | +| PDF content | yes | server-rendered PDF through approved renderer | report viewer, disclosure | report payload, PDF pages | bounded report-canvas exception inherited from Spec 366 | No new visual framework | +| Artifact registry/detail if reused | yes | native Filament resource | evidence/report registry | table/detail/readiness | no | Keep global search disabled unless spec updates | + +## 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 | +|---|---|---|---|---|---|---|---| +| Management Report PDF | Primary Decision Surface for customer consumption | Customer/MSP decides what risks and next actions matter | executive summary, posture, decisions, accepted risks, evidence basis, limitations, next actions, provenance | technical/auditor detail out of scope | Primary because the artifact must stand alone outside the app | review/report handoff | avoids ZIP/raw admin inspection for management context | +| Generate action | Secondary Workflow Action | Operator decides whether to create durable shareable output | source review/pack, profile, readiness, limitations, confirmation | run detail and logs | Secondary because it starts artifact creation | review-pack or environment-review owner flow | one clear action, disabled when unsafe | +| Download action/detail | Secondary Context Surface | Operator retrieves a generated artifact | artifact identity, generated time, source, profile | audit/run/source metadata | Secondary because reading happens in PDF | generated artifact follow-through | avoids duplicate generation | + +## Audience-Aware Disclosure + +| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention | +|---|---|---|---|---|---|---|---| +| Customer executive PDF | customer-read-only, operator-MSP | profile, readiness, executive story, risks, decisions, evidence basis, limitations, provenance | absent or summarized | raw JSON/provider/support detail absent | read/share report | internal fields, fingerprints, raw operation context | PDF states each blocker once, later sections add proof | +| Owner detail action | operator-MSP | readiness, current source, profile, blocked reason | OperationRun detail | logs/trace only outside PDF | generate, view operation, or download | raw renderer errors | one next action based on state | +| Download route | operator-MSP, entitled customer/admin if existing policy allows | artifact identity and file bytes | request/audit metadata | raw storage path hidden | download PDF | storage path/signed URL internals | audit metadata stores source once | + +## 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 | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| Owner surface action | Detail / Action | High-impact artifact generation | Generate PDF or download existing PDF | existing detail page | N/A | secondary/header or More depending existing hierarchy | N/A | existing Review Pack or Environment Review collection | existing detail route | workspace, managed environment, profile | Management report PDF | readiness, source, profile, status | none | +| PDF artifact | Report / Artifact Viewer | Customer-safe report | Read/share/download | signed/server-authorized route | N/A | source/run links outside PDF | N/A | owner surfaces only | download route | workspace, managed environment, profile | Management report PDF | limitations, evidence basis, generated metadata | bounded report-canvas exception inherited from Spec 366 | + +## 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 | +|---|---|---|---|---|---|---|---|---|---|---| +| Generate Management PDF action | MSP operator | decide whether to create durable customer output | detail action | Is this source safe and ready to generate as a PDF? | source, profile, readiness, limitations, confirmation text | run failure detail | readiness, generation status, disclosure | TenantPilot artifact only | Generate PDF, View operation, Download PDF | high-impact confirmed artifact creation | +| Management Report PDF | customer stakeholder, MSP operator | understand current governance posture and next actions | report artifact | What does the review say and what should happen next? | executive summary, risks, decisions, accepted risks, evidence, limitations, provenance | none in customer profile | evidence completeness, governance posture, source currency | read-only | Read/download/share according to policy | none | + +## Proportionality Review + +- **New source of truth?**: yes, the generated PDF artifact is durable output truth once created. It derives from existing source review/report truth. +- **New persisted entity/table/artifact?**: no new table is approved by default. A narrow extension to the existing artifact/report substrate is allowed if repo verification proves it is the smallest safe representation. If a new table/entity is required, implementation must stop and update spec/plan. +- **New abstraction?**: yes, a bounded payload/generation/renderer adapter may be introduced to keep generation out of controller/Blade code and to reuse the approved gateway. +- **New enum/state/reason family?**: maybe. Add only behavior-changing operation/artifact status or reason values required for generation/download behavior, and test them. Presentation-only labels stay derived. +- **New cross-domain UI framework/taxonomy?**: no. +- **Current operator problem**: operators need a real customer-ready PDF artifact with provenance, storage, authorization, OperationRun, and audit. +- **Existing structure is insufficient because**: Spec 378's renderer/gateway baseline can render bytes but does not own management report payload, artifact lifecycle, generation intent, or download authorization. +- **Narrowest correct implementation**: validate the existing runtime, then add one customer-executive PDF generation path over existing Review Pack/rendered-report/profile/disclosure truth. +- **Ownership cost**: one bounded generation path, possible narrow schema migration, OperationRun/audit tests, Browser/content smoke, deployment validation artifact. +- **Alternative intentionally rejected**: second renderer, generic document engine, Report Delivery Center, new report taxonomy, browser-print workaround, public link. +- **Release truth**: current-release follow-through over merged Spec 378 runtime infrastructure. + +### Compatibility posture + +This feature assumes pre-production runtime semantics. No legacy aliases, dual-write paths, compatibility shims, or historical artifact backfills are approved unless the spec is updated first. + +## Testing / Lane / Runtime Impact + +- **Test purpose / classification**: Unit, Feature, Filament/Livewire, Browser/content smoke, PostgreSQL if schema/indexes are added. +- **Validation lane(s)**: fast-feedback, confidence, browser, pgsql when persistence constraints require it. +- **Why this classification and these lanes are sufficient**: Unit tests prove deterministic payload/readiness/disclosure; Feature tests prove authorization, OperationRun, audit, storage, and download; Filament/Livewire tests prove action state; Browser/content smoke proves customer-facing content and leakage boundaries. +- **New or expanded test families**: Spec379 Unit/Feature/Browser tests under existing PDF/report/review-pack families. +- **Fixture / helper cost impact**: reuse existing Review Pack/rendered-report/browser fixtures; add explicit Spec379 fixture states only for ready, blocked, generated, running, and failed generation. +- **Heavy-family visibility / justification**: one bounded Browser/content smoke is required because the output is a customer-facing PDF artifact. +- **Special surface test profile**: report-viewer plus high-impact artifact generation action. +- **Standard-native relief or required special coverage**: owner surface stays native Filament; generated PDF needs content/leakage proof. +- **Reviewer handoff**: verify no renderer duplication, no raw leakage, no Graph calls, no public storage, no global-search enablement, and no unresolved staging runtime validation. +- **Budget / baseline / trend impact**: browser lane cost limited to one Spec379 smoke. PostgreSQL lane only if schema constraints are touched. +- **Escalation needed**: `document-in-feature` for storage/OperationRun decisions; `follow-up-spec` for delivery center, technical/auditor report, lifecycle/retention framework, or billing documents. +- **Active feature PR close-out entry**: Guardrail + Smoke Coverage + Runtime Validation. +- **Planned validation commands**: + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec379` + - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec379ManagementReportPdfSmokeTest.php --compact` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec378` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec357` + - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec366` + - `cd apps/platform && ./vendor/bin/sail pint --dirty` + - `git diff --check` + +## User Scenarios & Testing + +### User Story 1 - Validate the PDF runtime before generation (Priority: P1) + +As an operator preparing report generation, I need confirmation that the approved Gotenberg runtime controls work in staging/Dokploy so that generation is not enabled against an unvalidated renderer. + +**Why this priority**: Generation depends on the runtime. Enabling PDF output without staging/runtime proof would create false readiness. + +**Independent Test**: Validate the configured Gotenberg service and record runtime evidence without creating a report artifact. + +**Acceptance Scenarios**: + +1. **Given** the merged Spec 378 renderer config, **When** runtime validation runs in staging/Dokploy, **Then** the health check, network scope, timeout/body-limit/concurrency controls, and no-public-port posture are verified or generation remains blocked. +2. **Given** runtime validation fails, **When** an operator tries to generate a PDF, **Then** generation is blocked with safe guidance and no artifact is created. + +### User Story 2 - Generate a customer-safe Management Report PDF (Priority: P1) + +As an entitled MSP operator, I can generate a customer-executive PDF from a ready current review/report source so that the customer receives a stable report artifact. + +**Why this priority**: This is the core product value of Spec 379. + +**Independent Test**: Seed a ready source review/pack, trigger generation, and verify the generated artifact contains required chapters and excludes forbidden raw/internal content. + +**Acceptance Scenarios**: + +1. **Given** a ready current Review Pack with allowed `customer_executive` disclosure, **When** an entitled operator confirms generation, **Then** TenantPilot queues or starts generation through OperationRun and stores a private PDF artifact on success. +2. **Given** missing evidence, expired pack, invalid profile, renderer unavailable, storage failure, or disclosure blocker, **When** generation is requested, **Then** no ready artifact is exposed and safe blocked/failed evidence is recorded. + +### User Story 3 - Download and audit the generated PDF (Priority: P2) + +As an entitled operator, I can download the generated PDF through a scope-safe route so that every download is authorized and auditable. + +**Why this priority**: A generated artifact is incomplete without safe retrieval and auditability. + +**Independent Test**: Request the download as entitled, wrong-workspace, wrong-environment, and missing-capability actors. + +**Acceptance Scenarios**: + +1. **Given** a generated ready PDF artifact, **When** an entitled actor downloads it, **Then** the route re-resolves source/artifact scope, returns PDF bytes from private storage, and writes a safe download audit entry. +2. **Given** wrong workspace/environment or missing membership, **When** the artifact URL is requested, **Then** TenantPilot denies as not found without leaking existence. +3. **Given** a scoped member without download capability, **When** the artifact URL is requested, **Then** TenantPilot returns 403 after scope is established. + +### User Story 4 - Preserve report/runtime boundaries (Priority: P3) + +As a reviewer, I need the implementation to prove it did not rebuild PDF infrastructure or widen report scope so that Spec 379 remains a bounded completion slice. + +**Why this priority**: Prevents duplicate renderer infrastructure, report-center scope creep, and customer-safe disclosure regressions. + +**Independent Test**: Static and feature tests prove only the approved gateway is used and no forbidden runtime/package/report scope appears. + +**Acceptance Scenarios**: + +1. **Given** the implementation diff, **When** reviewer checks runtime paths, **Then** no second renderer/client/config/service/package is introduced. +2. **Given** generated PDF content, **When** content smoke inspects text, **Then** raw Graph payloads, secrets, internal MSP markers, stack traces, SQL errors, signed URLs, and serialized jobs are absent. + +## Functional Requirements + +- **FR-379-001**: The implementation MUST verify the existing Spec 378 Gotenberg runtime controls before enabling Management Report PDF generation. +- **FR-379-002**: The implementation MUST use only the existing `PdfRenderingGateway` / `PdfRendererClient` for production PDF rendering. +- **FR-379-003**: The implementation MUST NOT introduce a second renderer, second Gotenberg service, new PDF package, production browser runtime, or package-governance redo. +- **FR-379-004**: The implementation MUST generate the PDF from existing ready/current Review Pack or rendered-report/profile/disclosure truth. +- **FR-379-005**: The implementation MUST explicitly resolve v1 generation to `customer_executive` or the repo-canonical customer-executive profile and fail closed for unknown, unsupported, unimplemented, or fallback profile resolution. +- **FR-379-006**: The generated PDF MUST include executive summary, governance posture, key risks/findings, decisions or decision summary, accepted risks, evidence basis, limitations, next actions, and provenance. +- **FR-379-007**: The generated PDF MUST exclude raw provider payloads, secrets, signed URLs, raw operation context, SQL errors, stack traces, internal-only MSP/support content, and serialized job markers. +- **FR-379-008**: Generation MUST be explicit, readiness-gated, server-authorized, confirmation-gated, and auditable. +- **FR-379-009**: Generation MUST be queued and tracked by `OperationRun` through `OperationRunService` and the shared OperationRun UX path; synchronous no-run generation is out of scope for Spec 379. +- **FR-379-010**: Successful generation MUST store or reference a private artifact with source review/pack, profile, workspace, managed environment, actor, generated time, and operation-run provenance. +- **FR-379-011**: Failed or blocked generation MUST not expose a ready/downloadable artifact. +- **FR-379-012**: Download MUST be signed and/or server-authorized, re-resolve scope, return private PDF bytes, and write a safe audit event. +- **FR-379-013**: Non-member or wrong-scope access MUST be deny-as-not-found; scoped members missing capability MUST receive 403. +- **FR-379-014**: `StoredReportResource` global-search posture MUST remain disabled unless a future spec explicitly updates it with safe View/Edit semantics. +- **FR-379-015**: UI coverage artifacts MUST be updated or given a checked no-update rationale when implementation changes reachable report/action/download surfaces. +- **FR-379-016**: Generation and download authorization MUST use the existing capability registry: generation requires `ENVIRONMENT_REVIEW_MANAGE` on an Environment Review owner surface or `REVIEW_PACK_MANAGE` on a Review Pack owner surface, and download requires `REVIEW_PACK_VIEW`; any new capability requires spec/plan update and registry tests before implementation. + +## Non-Functional Requirements + +- **NFR-379-001**: No live Microsoft Graph/provider calls may occur during PDF render, generation, or download. +- **NFR-379-002**: Report generation must be idempotent or explicitly explain when repeated generation creates separate artifacts. +- **NFR-379-003**: Renderer/storage errors must map to safe reason codes/messages without raw exception leakage. +- **NFR-379-004**: Artifact storage must be private and must not use public or user-controlled filenames. +- **NFR-379-005**: Deployment impact must include staging/Dokploy runtime validation, queue workers, storage persistence, env vars, and migration status if applicable. + +## Key Entities / Concepts + +- **Management Report PDF**: Durable customer-safe PDF artifact generated from existing review/report truth. +- **Source Review / Review Pack**: Existing report basis; current ready Review Pack is preferred as source. +- **PDF Runtime Validation Artifact**: Spec-local evidence that staging/Dokploy Gotenberg runtime controls were validated or blocked. +- **OperationRun**: Canonical execution truth for generation. +- **StoredReport or Existing Artifact Substrate**: Possible persistence home for generated PDF metadata if current schema can be extended narrowly. +- **AuditLog**: Generation/download accountability record with redacted metadata. + +## Out Of Scope + +- New renderer infrastructure, package choice, or Gotenberg replacement. +- Technical/Auditor Evidence Report. +- Report Delivery Center, scheduled delivery, email, Teams, public links, or customer portal. +- Billing/invoice/e-invoice PDF generation. +- AI summary or AI report drafting. +- Dashboard or Customer Review Workspace redesign. +- Full localization rollout beyond required report/action labels. +- Broad artifact lifecycle/retention framework. + +## Acceptance Criteria + +- **AC-379-001**: Staging/Dokploy runtime validation is recorded before generation is treated as enabled. +- **AC-379-002**: A ready current source can produce a private customer-executive PDF through the existing PDF gateway. +- **AC-379-003**: Generated artifact metadata, OperationRun result, and audit entries agree on source, profile, actor, generated/downloaded times, and scope. +- **AC-379-004**: Unauthorized and wrong-scope generation/download attempts do not leak artifact/source existence. +- **AC-379-005**: Browser/content smoke over a generated/downloaded PDF proves required management chapters exist and forbidden raw/internal strings are absent. +- **AC-379-006**: No application code introduces duplicate renderer infrastructure or new PDF package/runtime. + +## Success Criteria + +- **SC-379-001**: An entitled operator can complete generation and download for one ready source fixture in the focused test/browser lane. +- **SC-379-002**: All blocked source/runtime/storage/security cases produce safe feedback and no downloadable artifact. +- **SC-379-003**: Focused Spec379 tests plus Spec378 gateway regression pass. +- **SC-379-004**: Deployment notes clearly state env, storage, queue, migration, and staging validation impact. + +## Risks + +- Runtime validation may be unavailable in local-only context; implementation must block enablement until staging/Dokploy proof exists. +- Extending `StoredReport` could become an artifact lifecycle framework if not kept narrow. +- Generation could accidentally duplicate rendered-report payload logic; implementation should extract only when it reduces review risk. +- PDF content can leak internal or raw technical data unless disclosure tests are strong. +- Queue/storage failures could expose partial artifacts unless transaction boundaries are careful. + +## Assumptions + +- Spec 378 renderer/gateway baseline is merged and available on the target branch. +- The first v1 owner surface will be Review Pack detail or Environment Review detail unless repo verification proves a better existing entry point. +- The generated PDF is a TenantPilot artifact only; it does not mutate Microsoft tenant state. +- Staging/Dokploy validation can be recorded as a spec-local artifact during implementation. + +## Open Questions + +No blocking product questions. Implementation must make and record three repo-truth decisions during Phase 1: + +1. Which existing owner surface hosts the first generation action? +2. Whether the existing artifact/report substrate can represent the PDF without a new table. +3. Whether an existing OperationRun type fits or a new `report.management.generate` mapping is required. + +## Follow-Up Spec Candidates + +- Technical/Auditor Evidence Report PDF. +- Report Delivery Center and scheduled/email delivery. +- Governance artifact lifecycle and retention runtime. +- Billing/invoice/e-invoice document generation. +- Customer Review Workspace PDF discovery/productization, if the first owner-surface path is insufficient. diff --git a/specs/379-management-report-pdf-runtime/tasks.md b/specs/379-management-report-pdf-runtime/tasks.md new file mode 100644 index 00000000..a21f2a84 --- /dev/null +++ b/specs/379-management-report-pdf-runtime/tasks.md @@ -0,0 +1,201 @@ +# Tasks: Spec 379 - Management Report PDF Runtime Validation & Generation Completion + +**Input**: `specs/379-management-report-pdf-runtime/spec.md`, `specs/379-management-report-pdf-runtime/plan.md` +**Prerequisites**: Spec and plan are complete. Spec 378 renderer/gateway baseline is merged and treated as read-only context. Spec 379 is the sole active implementation package for post-`G012` runtime validation and downstream Management Report PDF generation completion; unchecked Spec 378 downstream tasks are historical baseline signals only. +**Tests**: Required. Use Pest 4 Unit, Feature, Filament/Livewire action tests, Browser/content smoke, and PostgreSQL lane if schema/indexes are introduced. + +## Test Governance Checklist + +- [X] Lane assignment is named and is the narrowest sufficient proof for the changed behavior. +- [X] New or changed tests stay in the smallest honest family, and any browser addition is explicit. +- [X] Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default. +- [X] Planned validation commands cover the change without pulling in unrelated lane cost. +- [X] The high-impact artifact action and report-viewer surface profiles are explicit. +- [X] Any material budget, baseline, trend, or escalation note is recorded in the active spec or PR. + +## Phase 1: Setup And Repo Verification + +**Purpose**: Confirm baseline and stop before unsafe generation work. + +- [X] T001 Record branch, HEAD, dirty state, and Spec 379 touched-file baseline in `specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md`. +- [X] T002 Re-read `specs/378-management-report-pdf-v1/spec.md`, `specs/378-management-report-pdf-v1/plan.md`, `specs/378-management-report-pdf-v1/tasks.md`, and renderer artifacts without editing Spec 378. +- [X] T003 [P] Verify Spec 378 runtime files exist: `docker-compose.yml`, `apps/platform/config/tenantpilot.php`, and `apps/platform/app/Services/Pdf/PdfRenderingGateway.php`. +- [X] T004 [P] Verify Spec 378 gateway regression coverage in `apps/platform/tests/Unit/Pdf/Spec378PdfRenderingGatewayTest.php`. +- [X] T005 [P] Verify current report source paths in `apps/platform/app/Http/Controllers/ReviewPackRenderedReportController.php`, `apps/platform/resources/views/review-packs/rendered-report.blade.php`, and `apps/platform/app/Support/ReviewPacks/ReportProfileRegistry.php`. +- [X] T006 [P] Verify current disclosure/theme paths in `apps/platform/app/Support/ReviewPacks/ReportDisclosurePolicy.php` and `apps/platform/app/Support/ReviewPacks/ReportThemeResolver.php`. +- [X] T007 [P] Verify current Review Pack readiness/download paths in `apps/platform/app/Services/ReviewPackService.php`, `apps/platform/app/Jobs/GenerateReviewPackJob.php`, and the Review Pack download controller. +- [X] T008 Verify current `apps/platform/app/Models/StoredReport.php` and `StoredReportResource` posture before choosing artifact storage. +- [X] T009 Verify current `OperationRunService`, `OperationRunType`, `OperationCatalog`, `OperationRunLinks`, and `OperationUxPresenter` before adding or mapping report generation. +- [X] T010 Verify current audit action ID/logger patterns for review pack generation/download and decide whether distinct management PDF generation/download IDs are required; if existing IDs would obscure PDF-vs-ZIP semantics, plan distinct stable `AuditActionId` entries before audit implementation. +- [X] T011 Decide the first owner surface for v1 generation and record the decision in `specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md`. + +## Phase 2: Runtime Validation Gate + +**Purpose**: Validate existing Gotenberg runtime controls before generation enablement. + +- [X] T012 Confirm the pinned Gotenberg service image, no-public-port posture, health check, timeout/body-limit/concurrency controls, and outbound/file-access posture from `docker-compose.yml`. +- [ ] T013 Validate staging/Dokploy runtime controls using the deployed container/runtime path and record pass/fail evidence in `specs/379-management-report-pdf-runtime/artifacts/runtime-validation.md`. +- [X] T014 If staging/Dokploy runtime validation cannot be completed, add a blocked-generation implementation note and ensure generation remains disabled or unavailable until validation passes. +- [X] T015 Confirm `docs/deployment-checklist.md` remains accurate for PDF renderer runtime validation or update it during implementation if runtime controls changed. + +## Phase 3: Tests First + +**Purpose**: Add focused failing or pending proof before implementation. + +- [X] T016 [P] Add coverage for runtime validation decision mapping in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T017 [P] Add coverage for management report payload chapters in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T018 [P] Add coverage proving `customer_executive` disclosure excludes raw/internal content in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T019 [P] Add Unit coverage for readiness blockers: missing source, non-current pack, expired pack, invalid profile, disclosure blocker, renderer unavailable, and storage unavailable. +- [X] T020 [P] Add Feature coverage for authorized generation from a ready source in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T021 [P] Add Feature coverage for artifact metadata/storage provenance in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T022 [P] Add Feature coverage for generation audit and failed/blocked generation evidence in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T023 [P] Add Feature coverage for download audit and private file response in `apps/platform/tests/Feature/ReviewPack/Spec379ManagementReportPdfTest.php`. +- [X] T024 [P] Add authorization coverage for wrong workspace, wrong environment, and no membership returning deny-as-not-found. +- [X] T025 [P] Add authorization coverage for scoped member without `ENVIRONMENT_REVIEW_MANAGE` or `REVIEW_PACK_MANAGE` on generation and without `REVIEW_PACK_VIEW` on download returning 403 after scope is established. +- [X] T026 Add Filament/Livewire action coverage for the selected owner surface action visibility, disabled reason, confirmation, queued/run link, and download state. +- [X] T027 Add Browser/content smoke coverage in `apps/platform/tests/Browser/Spec379ManagementReportPdfSmokeTest.php`, creating or reusing the narrow fixture needed to generate/download one customer-executive PDF artifact. +- [X] T028 Add content assertions that generated PDF text includes required management chapters and excludes forbidden strings including `SQLSTATE`, `access token`, `client secret`, `raw Graph payload`, `internal_msp_review`, serialized job markers, and signed URLs. + +## Phase 4: Payload, Readiness, And Disclosure + +**Goal**: Build customer-safe PDF content from existing source truth only. +**Independent Test**: Unit tests prove required chapters, blockers, and disclosure behavior without storage or UI. + +- [X] T029 [US2] Implement a bounded management report payload builder under `apps/platform/app/Support/ReviewPacks/` or the closest existing report namespace. +- [X] T030 [US2] Build payload only from existing `ReviewPack`, `EnvironmentReview`, review sections, evidence summaries, findings/accepted-risk summaries, and rendered-report support truth. +- [X] T031 [US2] Resolve profile through `ReportProfileRegistry` and default to the repo-canonical customer-executive profile. +- [X] T032 [US2] Apply `ReportDisclosurePolicy` before rendering and fail closed for unknown or unsupported profile input. +- [X] T033 [US2] Add readiness/blocked mapping for source missing, not current, expired, not ready, evidence limitation, disclosure blocker, runtime validation missing, renderer unavailable, storage unavailable, and unauthorized actor. +- [X] T034 [US2] Ensure payload building performs no Graph/provider calls and no Blade/PDF template database queries. +- [X] T035 [US2] Keep next actions derived from existing review/finding/evidence/report data; do not invent AI or unsupported recommendations. + +## Phase 5: Artifact Storage And Idempotency + +**Goal**: Persist or reference the generated PDF without a new report center. +**Independent Test**: Feature tests prove source/profile/file provenance and no partial-ready artifact exposure. + +- [X] T036 [US2] Decide whether existing artifact/report storage can represent the PDF; record the decision in `specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md`. +- [X] T037 [US2] If current fields are insufficient, add a narrow reversible migration under `apps/platform/database/migrations/` for existing artifact/report substrate fields only. +- [X] T038 [US2] If extending `StoredReport`, add only required fields/constants/casts/relationships in `apps/platform/app/Models/StoredReport.php`; if `StoredReportResource` is touched, keep global search disabled unless the spec is updated first. +- [X] T039 [US2] Ensure newly persisted tenant-owned artifact truth carries constitution-compliant workspace and managed-environment scope, and tenant scope where required by current table ownership rules. +- [X] T040 [US2] Store PDF files on a private disk/path with safe generated filenames. +- [X] T041 [US2] Implement source/profile/fingerprint idempotency or explicitly document separate-artifact generation behavior. +- [X] T042 [US2] Prevent ready/downloadable artifact exposure when rendering or storage fails before commit. +- [X] T043 [US2] Run PostgreSQL lane if migrations, JSONB indexes, or constraints are added. + +## Phase 6: OperationRun And Audit + +**Goal**: Make generation observable and accountable. +**Independent Test**: Feature tests prove queued/running/succeeded/blocked/failed outcomes and audit metadata. + +- [X] T044 [US2] Add or map a canonical operation type for management report PDF generation only if no existing type honestly fits. +- [X] T045 [US2] Update `OperationCatalog`, labels, actionability, and tests if a new operation type is added. +- [X] T046 [US2] Queue generation through `OperationRunService` and the shared OperationRun start UX path. +- [X] T047 [US2] Dispatch generation work to an existing or new bounded job under `apps/platform/app/Jobs/` with identifiers only, no raw payload secrets. +- [X] T048 [US2] Mark success, renderer failure, storage failure, blocked source, and unauthorized cases through `OperationRunService` with safe reason codes/messages. +- [X] T049 [US2] Keep `summary_counts` flat numeric-only and use existing keys where counts are needed. +- [X] T050 [US2] Record generation audit with a stable management-PDF action ID, actor, workspace, managed environment, source review/pack, artifact/report id, operation run id, profile, format, generated time, and redacted metadata; add `AuditActionId` case/label/summary when no exact existing ID fits. +- [X] T051 [US3] Record download audit with a stable management-PDF action ID, actor, workspace, managed environment, artifact/report id, source review/pack, profile, format, downloaded time, and redacted request metadata; add `AuditActionId` case/label/summary when no exact existing ID fits. +- [X] T052 [US2] Verify audit metadata excludes secrets, signed URLs, raw provider payloads, raw operation context, stack traces, and SQL errors. + +## Phase 7: Owner Action And Download Route + +**Goal**: Expose one clear, safe generation/download flow. +**Independent Test**: Filament/Feature tests prove action state, confirmation, authorization, download, and scope denial. + +- [X] T053 [US2] Add `Generate management PDF` to the selected owner surface using `Action::make(...)->action(...)`. +- [X] T054 [US2] Apply server-side authorization inside the action handler using `ENVIRONMENT_REVIEW_MANAGE` for an Environment Review owner surface or `REVIEW_PACK_MANAGE` for a Review Pack owner surface; UI state is not security. +- [X] T055 [US2] Add explicit Filament confirmation with clear copy explaining durable customer-facing artifact creation. +- [X] T056 [US2] Show disabled/blocked reasons for source not ready, expired, not current, profile/disclosure blocked, runtime validation missing, renderer unavailable, storage unavailable, unauthorized, or already running. +- [X] T057 [US2] If generation is queued/running, show only the canonical `View operation` link through existing helpers. +- [X] T058 [US3] If a ready PDF already exists, prefer `Download management PDF` or equivalent over duplicate generation. +- [X] T059 [US3] Implement a signed and/or server-authorized PDF download route/controller only if existing routes cannot safely represent PDF format/profile/artifact identity. +- [X] T060 [US3] In the download route/controller, re-resolve workspace, managed environment, source review/pack, artifact status, and `REVIEW_PACK_VIEW` capability before returning bytes. +- [X] T061 [US3] Set safe PDF response headers and filenames without making internal IDs the primary label. +- [X] T062 [US3] Keep existing Review Pack ZIP download behavior unchanged. + +## Phase 8: PDF Rendering And Localization + +**Goal**: Render a customer-safe PDF through the approved gateway only. +**Independent Test**: Content smoke proves required chapters and forbidden-content absence. + +- [X] T063 [US2] Implement the management PDF renderer adapter through `PdfRenderingGateway` only. +- [X] T064 [US2] Render cover, executive summary, governance posture, key decisions, top risks/findings, accepted risks, evidence readiness, limitations, next actions, provenance, and method summary. +- [X] T065 [US2] Include generated timestamp, source review/pack metadata, profile, and classification/confidentiality marker. +- [X] T066 [US2] Include header/footer and page numbering where supported by the approved renderer. +- [X] T067 [US2] Avoid remote fonts, external assets, public images, and network-dependent resources. +- [X] T068 [US2] Limit management-safe findings and defer deep tables/appendices to future Technical/Auditor report specs. +- [X] T069 [US2] Ensure renderer errors produce safe results that map to OperationRun blocked/failed outcomes. +- [X] T070 [US2] Add EN localization keys for action labels, notifications, blocked reasons, chapter titles, limitations, and provenance labels. +- [X] T071 [US2] Add DE localization keys for the same report/action labels. +- [X] T072 [US2] Use existing locale-aware date/time/number conventions where available. + +## Phase 9: UI Coverage And Documentation-In-Feature + +**Purpose**: Satisfy UI-COV without broad docs churn. + +- [X] T073 Apply UI coverage rules: update route inventory for any new PDF route, UI-099 for PDF/report content changes, UI-042 for Review Pack action/download changes, UI-048 for StoredReport exposure, and design coverage matrix for material action/artifact changes. +- [X] T074 If no material coverage artifact change is needed, record the checked no-update rationale in implementation close-out. +- [X] T075 Store browser screenshots/content evidence under `specs/379-management-report-pdf-runtime/artifacts/screenshots/` if captured. +- [X] T076 Record final storage substrate, OperationRun type, owner surface, runtime validation, and UI coverage decisions in `specs/379-management-report-pdf-runtime/artifacts/storage-operationrun-decision.md`. + +## Phase 10: Validation + +**Purpose**: Prove Spec 379 and prevent adjacent report/runtime regressions. + +- [X] T077 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec379`. +- [X] T078 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec378`. +- [X] T079 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec357`. +- [X] T080 Run `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec366`. +- [X] T081 Run focused Review Pack/resource regressions selected from touched owner surface files. +- [X] T082 Run `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec379ManagementReportPdfSmokeTest.php --compact`. +- [X] T083 Run PostgreSQL lane if migrations, JSONB indexes, constraints, or artifact storage fields require it. +- [X] T084 Run `cd apps/platform && ./vendor/bin/sail pint --dirty`. +- [X] T085 Run `git diff --check`. +- [X] T086 Static scan changed runtime files for Livewire v3 APIs and confirm none were introduced. +- [X] T087 Static scan changed runtime files for Graph/provider calls during render/generation/download and confirm none were introduced. +- [X] T088 Static scan changed runtime files for duplicate PDF renderer/client/config/service/package additions and confirm none were introduced. +- [X] T089 Complete final close-out with Livewire v4 compliance, provider registration location, global-search status, high-impact action status, asset strategy, tests, and deployment impact. + +## Non-Goals + +- [X] NT001 Do not create a second PDF renderer, second Gotenberg service, second PDF config, or second PDF client/gateway. +- [X] NT002 Do not add Composer/NPM PDF packages, Puppeteer, Browsershot, dompdf, wkhtmltopdf, Playwright production rendering, or browser binaries in Laravel containers. +- [X] NT003 Do not redo package-governance for Gotenberg unless the approved renderer changes. +- [X] NT004 Do not build Technical Evidence Report or Auditor Evidence Report. +- [X] NT005 Do not build Report Delivery Center, scheduled delivery, email/Teams delivery, public links, or customer portal. +- [X] NT006 Do not build invoice, billing, XRechnung, ZUGFeRD, tax, or legal archive functionality. +- [X] NT007 Do not redesign Customer Review Workspace, dashboard, or navigation. +- [X] NT008 Do not add AI-generated summaries or AI report drafting. +- [X] NT009 Do not change Review Pack ZIP download behavior. +- [X] NT010 Do not add a broad artifact lifecycle/retention framework. +- [X] NT011 Do not call Microsoft Graph/provider APIs during PDF render, generation, or download. +- [X] NT012 Do not rewrite completed historical specs or remove close-out/validation evidence from related specs. + +## Dependencies And Ordering + +- T001-T011 must complete before runtime edits. +- T012-T015 must complete before generation can be enabled. +- Tests in Phase 3 should be added before or alongside implementation. +- Payload/readiness/disclosure must complete before rendering. +- Artifact storage and OperationRun/audit must complete before download exposure. +- Browser/content smoke runs after owner action and download route are usable. +- Validation and close-out run last. + +## Parallel Opportunities + +- T003-T007 can run in parallel during verification. +- T016-T025 can be developed in parallel once fixture shape is known. +- T070-T072 can run after visible labels are known. +- T077-T080 can run in parallel once implementation is stable. + +## Implementation Strategy + +1. Validate existing runtime first. +2. Prove behavior with tests before implementation. +3. Reuse existing rendered-report/profile/disclosure truth. +4. Use the approved PDF gateway only. +5. Keep artifact persistence narrow and private. +6. Use OperationRun and audit as generation/download accountability. +7. Expose one owner-surface action and one safe download path. +8. Validate content, leakage boundaries, and no infrastructure duplication.