# 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.