Implemented the first version of the PDF and HTML renderer for review packs. Added ReviewPackRenderedReportController and related blade views to render reports. Updated EnvironmentReviewResource, ReviewPackResource, ReviewPackService, and routing. Added new tests for the renderer and download actions, and updated UI documentation. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #427
18 KiB
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:
CustomerReviewWorkspaceViewEnvironmentReviewin customer-workspace modeReviewPackResourcedetail/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:
CustomerReviewWorkspacekeepsOpen reviewas 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-featureand 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.phpapps/platform/app/Jobs/GenerateReviewPackJob.phpapps/platform/app/Http/Controllers/ReviewPackDownloadController.phpapps/platform/routes/web.phpapps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.phpapps/platform/app/Filament/Resources/EnvironmentReviewResource/Pages/ViewEnvironmentReview.phpapps/platform/app/Filament/Resources/ReviewPackResource.php- current localization files and current review summary/artifact-truth views
- Shared abstractions reused: current
ReviewPackdelivery contract,ReviewPackOutputReadiness,ArtifactTruthPresenter, current review summary andEnvironmentReviewSectiontruth, 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
ReviewPackGeneratepath and currentOperationRunLinksfollow-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
ReviewPackGenerateremains the onlyOperationRun; 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.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackResourceTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.phpgit 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-featureif 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)
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:
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:
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
AuditorPackfamily, a new package-backed PDF engine, or a customer portal. - Release truth: current-release productization follow-through.
Technical Approach
-
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.
- Treat Spec 263's runtime as authoritative for
-
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.
- Render from current stored review,
-
Surface the rendered report through current owner seams.
- Keep
CustomerReviewWorkspaceas 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_packpath. - 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.
- Keep
-
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.
-
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
- Lock the current contract and dependency truth, including current PDF-support reality.
- Add failing tests for rendered HTML disclosure, authorization, and audit continuity.
- Implement the bounded HTML report and owner-surface launch affordances.
- Implement honest PDF handling from the same contract only if current repo support allows it.
- 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:assetsshould remain unchanged unless implementation later proves otherwise. - If implementation stays HTML-first because no PDF support exists, deployment impact remains route/view-only.