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