# Implementation Plan: Review Pack PDF/HTML Renderer v1 **Branch**: `356-review-pack-pdf-html-renderer-v1` | **Date**: 2026-06-05 | **Spec**: `specs/356-review-pack-pdf-html-renderer-v1/spec.md` **Input**: Feature specification from `specs/356-review-pack-pdf-html-renderer-v1/spec.md` ## Summary Implement a rendered review-output follow-up that reuses the current review-derived `ReviewPack` contract to provide one calm HTML report, surfaces that report through the current released-review/review-pack seams, and keeps PDF strictly conditional on current repo-supported capabilities. The slice must preserve the current `ReviewPackGenerate` authority path, current signed download seam, current customer-safe disclosure contract, and current audit boundaries without introducing a second artifact family or a new dependency. Spec 263 remains the bundle-contract baseline and must not be reopened. Spec 355 is the gating proof that the operator/productization flow is coherent enough for this delivery-format follow-up. ## Technical Context **Language/Version**: PHP 8.4.15, Laravel 12.52, Filament 5.2.1, Livewire 4.1.4 **Primary Dependencies**: Filament admin panel, current `ReviewPackService`, `GenerateReviewPackJob`, `ReviewPackDownloadController`, current `EnvironmentReview` summary/composer truth, Blade views, Pest 4.3 **Storage**: PostgreSQL plus current `exports` disk; no schema change or new persistence family planned **Testing**: Pest Feature tests plus one bounded Browser smoke **Validation Lanes**: confidence, browser, `git diff --check` **Target Platform**: Laravel monolith in `apps/platform` **Project Type**: web application **Performance Goals**: render from current stored review-pack/review truth only; no live provider calls; no new queue family; no wide per-request recomposition beyond one current pack/review **Constraints**: no new package without approval, no new artifact family, no new `OperationRun`, no Graph calls during render, no customer portal, PDF conditional on current support only **Scale/Scope**: one released review and one current review pack, surfaced through current admin/operator and customer-safe read-only paths ## UI / Surface Guardrail Plan - **Guardrail scope**: new rendered delivery surface plus changed customer-safe and review-pack owner surfaces - **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: - `CustomerReviewWorkspace` - `ViewEnvironmentReview` in customer-workspace mode - `ReviewPackResource` detail/download surface - one new read-only rendered report route under `/admin/review-packs/{reviewPack}/...` for preview/print delivery - **No-impact class, if applicable**: N/A - **Native vs custom classification summary**: mixed; native Filament owner surfaces plus one bounded custom rendered report view - **Shared-family relevance**: delivery-status messaging, review/report viewers, download actions, customer-safe disclosure - **State layers in scope**: page, detail, URL/route - **Audience modes in scope**: operator-MSP, customer-admin, customer-read-only, auditor-read-only where current review-pack access already allows it - **Decision/diagnostic/raw hierarchy plan**: decision-first rendered report and owner-surface summary; appendix/raw ZIP detail stays secondary - **Raw/support gating plan**: raw pack files, fingerprints, and technical context stay collapsed or secondary behind current authorized seams - **One-primary-action / duplicate-truth control**: `CustomerReviewWorkspace` keeps `Open review` as the list inspect model; released-review detail gets one dominant rendered-output affordance; review-pack detail does not compete with the owner-surface summary - **Handling modes by drift class or surface**: customer-safe overclaim and false PDF availability are hard-stop candidates; copy-only drift is review-mandatory - **Repository-signal treatment**: review-mandatory for the new rendered route and for any changed customer-safe surface copy - **Special surface test profiles**: shared-detail-family - **Required tests or manual smoke**: focused Feature disclosure and authorization tests plus one bounded browser smoke through the current workspace/detail flow - **Exception path and spread control**: if PDF support is absent without a package, record `document-in-feature` and keep HTML as the shipped floor instead of widening scope - **Active feature PR close-out entry**: Smoke Coverage - **UI/Productization coverage decision**: reachable UI changes require route inventory, design coverage, strategic-surface, unresolved-page, and page-report follow-through - **Coverage artifacts to update**: `route-inventory.md`, `design-coverage-matrix.md`, `strategic-surfaces.md`, `unresolved-pages.md`, `page-reports/...` - **No-impact rationale**: N/A - **Navigation / Filament provider-panel handling**: no navigation or provider-panel change is planned - **Screenshot or page-report need**: yes; the rendered report is a new reachable customer-facing surface ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: - `apps/platform/app/Services/ReviewPackService.php` - `apps/platform/app/Jobs/GenerateReviewPackJob.php` - `apps/platform/app/Http/Controllers/ReviewPackDownloadController.php` - `apps/platform/routes/web.php` - `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` - `apps/platform/app/Filament/Resources/EnvironmentReviewResource/Pages/ViewEnvironmentReview.php` - `apps/platform/app/Filament/Resources/ReviewPackResource.php` - current localization files and current review summary/artifact-truth views - **Shared abstractions reused**: current `ReviewPack` delivery contract, `ReviewPackOutputReadiness`, `ArtifactTruthPresenter`, current review summary and `EnvironmentReviewSection` truth, current export/dedupe/download seams - **New abstraction introduced? why?**: none by default; any render helper should stay local to the renderer/view seam if needed to keep view logic thin - **Why the existing abstraction was sufficient or insufficient**: current services and presenters already own truth and safe delivery semantics, but no current surface renders that truth as a calm human-readable report - **Bounded deviation / spread control**: no second renderer stack, no second artifact family, no package-backed PDF engine ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: yes, reuse-only - **Central contract reused**: current `ReviewPackGenerate` path and current `OperationRunLinks` follow-up behavior - **Delegated UX behaviors**: queued toast, already-available pack reuse, active-run dedupe, current operation link continuity, and current terminal notification behavior remain unchanged - **Surface-owned behavior kept local**: preview/download of already-ready rendered output only - **Queued DB-notification policy**: unchanged - **Terminal notification path**: unchanged - **Exception path**: none ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: no - **Provider-owned seams**: N/A - **Platform-core seams**: read-only report delivery over existing review-pack truth - **Neutral platform terms / contracts preserved**: review pack, rendered report, evidence basis, accepted risk, governance decision, limitation state - **Retained provider-specific semantics and why**: provider-specific appendix content may remain inside the structured appendix because it already exists in current stored truth; it does not become the primary rendered language - **Bounded extraction or follow-up path**: none ## Constitution Check - Inventory-first: no inventory truth changes; the renderer is a delivery view over current stored review/review-pack truth. - Read/write separation: preview/download is read-only; current export initiation remains the only write/queue path. - Graph contract path: no Graph calls. - Deterministic capabilities: current capability and entitlement logic remains authoritative. - RBAC-UX: non-members remain `404`; in-scope viewers stay on current review/review-pack permission paths. - Workspace isolation: current workspace membership remains the first boundary. - Tenant isolation: rendered access stays environment-scoped through current review-pack/review seams. - Run observability: current `ReviewPackGenerate` remains the only `OperationRun`; no renderer-specific run is added. - Automation: no new scheduled or queued work is added beyond the current export path. - Data minimization: rendered output stays customer-safe by default and does not surface raw/debug detail. - Test governance: Feature plus one bounded browser smoke is the narrowest honest proof. - Proportionality: no new persistence, no new package, no second artifact family, and no new workflow engine. - No premature abstraction: render helpers stay local; no generic reporting engine is allowed. - Persisted truth: unchanged. - Behavioral state: unchanged. - UI semantics: direct domain-to-report mapping over current truth; no new status taxonomy. - Shared pattern first: extend the current review/review-pack/report seams instead of adding a parallel delivery subsystem. - Provider boundary: unchanged. - V1 explicitness / few layers: HTML-first and current-contract-first. - Spec discipline / bloat check: PDF stays conditional rather than forcing broad infrastructure. - Filament-native UI: existing owner surfaces stay native; the custom report view remains a bounded read-only delivery surface. ## Test Governance Check - **Test purpose / classification by changed surface**: - Feature: rendered contract truth, authorization, audit continuity, action hierarchy, honest PDF handling - Browser: open-path and customer-safe presentation smoke - **Affected validation lanes**: confidence, browser - **Why this lane mix is the narrowest sufficient proof**: the slice is a read-only delivery/report follow-up over existing runtime truth; broad PGSQL or heavy-governance lanes are unnecessary - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/EnvironmentReview/EnvironmentReviewExecutivePackTest.php tests/Feature/EnvironmentReview/EnvironmentReviewExplanationSurfaceTest.php tests/Feature/EnvironmentReview/EnvironmentReviewUiContractTest.php tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php tests/Feature/ReviewPack/EnvironmentReviewDerivedReviewPackTest.php tests/Feature/ReviewPack/ReviewPackDownloadTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackResourceTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php` - `git diff --check` - **Fixture / helper / factory / seed / context cost risks**: existing released-review, review-pack, and entitlement fixtures are sufficient; avoid adding expensive default support/provider setup - **Expensive defaults or shared helper growth introduced?**: none expected - **Heavy-family additions, promotions, or visibility changes**: none beyond one explicit browser smoke path - **Surface-class relief / special coverage rule**: shared-detail-family coverage is required because the rendered report must stay customer-safe and non-duplicative - **Closing validation and reviewer handoff**: reviewers should verify HTML is the guaranteed floor, PDF is not overclaimed, and no second artifact family or new package appears - **Budget / baseline / trend follow-up**: none expected - **Review-stop questions**: false PDF claims, duplicate summary drift, authorization leaks, second artifact family drift - **Escalation path**: `document-in-feature` if PDF remains unavailable without a new package - **Active feature PR close-out entry**: Smoke Coverage - **Why no dedicated follow-up spec is needed**: the HTML rendered-report slice is already the bounded follow-up; PDF-only hardening becomes a follow-up only if the repo cannot support it inside this scope ## Project Structure ### Documentation (this feature) ```text specs/356-review-pack-pdf-html-renderer-v1/ |-- spec.md |-- plan.md |-- tasks.md `-- checklists/ `-- requirements.md ``` ### Source Code (repository root) Likely runtime surfaces for later implementation: ```text apps/platform/app/ |-- Services/ | `-- ReviewPackService.php |-- Jobs/ | `-- GenerateReviewPackJob.php |-- Http/Controllers/ | |-- ReviewPackDownloadController.php | `-- ... rendered-report controller for the new read-only preview/print seam |-- Filament/ | |-- Pages/Reviews/CustomerReviewWorkspace.php | |-- Resources/EnvironmentReviewResource/Pages/ViewEnvironmentReview.php | `-- Resources/ReviewPackResource.php `-- Support/ `-- existing artifact-truth and review-output readiness helpers apps/platform/resources/ |-- views/ | |-- filament/pages/reviews/customer-review-workspace.blade.php | |-- filament/infolists/entries/environment-review-summary.blade.php | |-- filament/infolists/entries/environment-review-section.blade.php | |-- filament/infolists/entries/review-pack-output-guidance.blade.php | `-- review-packs/... for the rendered-report preview/print view `-- lang/ |-- en/localization.php `-- de/localization.php apps/platform/routes/ `-- web.php ``` Likely tests for later implementation: ```text apps/platform/tests/ |-- Feature/EnvironmentReview/ | |-- EnvironmentReviewExecutivePackTest.php | |-- EnvironmentReviewExplanationSurfaceTest.php | `-- EnvironmentReviewUiContractTest.php |-- Feature/ReviewPack/ | |-- EnvironmentReviewDerivedReviewPackTest.php | |-- ReviewPackDownloadTest.php | `-- ReviewPackResourceTest.php |-- Feature/Reviews/ | `-- CustomerReviewWorkspacePackAccessTest.php `-- Browser/Reviews/ `-- CustomerReviewWorkspaceSmokeTest.php ``` **Structure Decision**: stay inside the current review/review-pack seams. Add exactly one new read-only controller and rendered-report view under the existing review-pack route family so preview/print delivery stays separate from the current signed ZIP download seam. ## Complexity Tracking | Violation | Why Needed | Simpler Alternative Rejected Because | |---|---|---| | One bounded rendered-report surface | The current ZIP plus Markdown entrypoint still feels internal to a stakeholder | Another Markdown-only adjustment would not remove unzip-first friction | | Conditional PDF handling | The user value includes printable output, but the repo shows no clear PDF stack | Forcing a new PDF package or second renderer would overshoot current release truth | ## Proportionality Review - **Current operator problem**: the current review pack is accurate but not immediately consumable as a calm stakeholder report. - **Existing structure is insufficient because**: a ZIP plus Markdown entrypoint still requires explanation and creates productization friction. - **Narrowest correct implementation**: one HTML report over current review-pack truth and PDF only when the repo can support it from the same contract. - **Ownership cost created**: maintain a render view, owner-surface launch affordances, and focused customer-safe regression coverage. - **Alternative intentionally rejected**: a new `AuditorPack` family, a new package-backed PDF engine, or a customer portal. - **Release truth**: current-release productization follow-through. ## Technical Approach 1. Reconfirm the current review-derived delivery contract. - Treat Spec 263's runtime as authoritative for `executive-summary.md`, delivery metadata, current review-pack anchoring, and current signed download continuity. - Keep current review-output readiness truth from Specs 347, 349, 351, and 355. 2. Add one deterministic rendered-report contract over existing truth. - Render from current stored review, `EnvironmentReviewSection`, and review-pack data only. - Reuse current executive summary, section ordering/content semantics, evidence basis, readiness, accepted-risk, decision-summary, and non-certification disclosure truth instead of re-parsing archived ZIP contents as the primary source. - Avoid live provider calls, new persistence, or a second composition engine. 3. Surface the rendered report through current owner seams. - Keep `CustomerReviewWorkspace` as the first decision surface. - Add one dominant rendered-output affordance on released-review detail and/or review-pack detail. - Keep operator export initiation on the current `export_executive_pack` path. - Keep report chrome outside the report canvas and make rendered/download labels readiness-aware. - Add controlled repo-backed co-branding slots from existing workspace/environment names only. - Keep limitations, PII/internal warnings, evidence basis, operation proof, disclosure, and generated metadata visible regardless of branding. 4. Keep printable delivery honest. - If current repo support can produce PDF from the same rendered contract, allow it without a new package or second renderer. - If not, keep the slice HTML-first and record the bounded PDF follow-up rather than widening scope. - Hide toolbar/app actions from print output so the printed report stands alone. 5. Preserve audit and entitlement continuity. - Reuse current review-pack view/download permissions and current audit events. - Do not add a new panel, global search surface, asset strategy, package, or queue family. - Render only from stored review/review-pack/evidence data; no live provider, Graph, operation, refresh, or AI calls during render. ## Implementation Phases 1. Lock the current contract and dependency truth, including current PDF-support reality. 2. Add failing tests for rendered HTML disclosure, authorization, and audit continuity. 3. Implement the bounded HTML report and owner-surface launch affordances. 4. Implement honest PDF handling from the same contract only if current repo support allows it. 5. Re-run focused Feature and Browser proof plus `git diff --check`. ## Rollout And Deployment Impact - No migrations planned. - No new env vars planned. - No new queue family or scheduler change planned. - No new storage volume or persistence family planned. - No new Filament asset strategy is planned; `filament:assets` should remain unchanged unless implementation later proves otherwise. - If implementation stays HTML-first because no PDF support exists, deployment impact remains route/view-only.