# Feature Specification: Review Pack PDF/HTML Renderer v1 **Feature Branch**: `356-review-pack-pdf-html-renderer-v1` **Created**: 2026-06-05 **Status**: Implemented - close-ready after validation **Type**: Productization / rendered review output / customer-safe report delivery **Depends on**: Specs 263, 347, 349, 351, 355 **Runtime posture**: Reuse the current review-derived `ReviewPack` delivery contract. HTML rendering is required. PDF is allowed only if the repo already supports it from the same contract without adding a new package, a new `OperationRun`, or a second artifact family. **Input**: Direct user-provided Spec 356 draft plus repo truth from the current review-pack export surfaces and Spec 355's documented follow-up queue. ## Dependencies And Repo-Truth Adjustments - Spec 263 already introduced the current executive entrypoint (`executive-summary.md`) and delivery metadata inside the current review-derived pack. This spec must not reopen or duplicate that bundle-contract work. - Current customer-safe detail surfaces already direct the reader to start with `executive-summary.md`, and the current review-derived pack is tenant-safe, auditable, and backed by the current signed-download seam. - Current repo truth before this slice required a ZIP download and Markdown/JSON inspection. This branch adds the calm rendered HTML/print report while preserving the existing ZIP artifact. - Spec 355 explicitly deferred this renderer until the operator/productization flow was browser-verified as coherent. Spec 355 is now commit-backed on `platform-dev`, so that prerequisite gate is satisfied. - No dedicated repo-supported report PDF stack is present in the current application dependencies. PDF therefore remains deferred for this slice; the implementation stays HTML-first with browser print support and does not add a package or second rendering subsystem. ## Spec Candidate Check *(mandatory — SPEC-GATE-001)* - **Problem**: TenantPilot can now produce customer-safe review packs and an executive-first Markdown entrypoint, but the externally deliverable output still feels like an internal ZIP artifact rather than a calm report a customer, executive, or demo stakeholder can open immediately. - **Today's failure**: Even after Spec 263 and Spec 355, an operator still has to explain that the stakeholder should download a ZIP, open `executive-summary.md`, and ignore the structured JSON appendix unless deeper inspection is needed. That adds friction and weakens the product's demo- and handoff-quality story. - **User-visible improvement**: An entitled user can open one rendered report from the current released review/current pack context, read the executive story first, understand evidence basis and limitations, and download a printable version when the repo can support it honestly. - **Smallest enterprise-capable version**: Keep one released-review-bound `ReviewPack` artifact and the current export/download authority model. Add one deterministic HTML report over the existing contract, surface it through the current review/customer-workspace seams, and allow PDF only when it comes from the same render contract without new dependencies. - **Explicit non-goals**: No new `AuditorPack` model or table, no new customer portal, no public share links, no email delivery, no AI summary generation, no branding or white-label system, no batch multi-review export, no second report engine, and no new review publication workflow. - **Permanent complexity imported**: One bounded render layer, one or more read-only preview/download surfaces, localized copy updates, and focused Feature/Browser coverage. No new persistence family, no new queue family, no new capability family, and no second artifact taxonomy are allowed. - **Why now**: Spec 355 explicitly named this as the next narrow follow-up once sellable-flow coherence was proven. The remaining gap is not another operator workflow foundation; it is the presentable customer/demo artifact. - **Why not local**: A copy-only change or another Markdown file does not remove the ZIP-first friction. A customer portal or generic reporting engine overshoots the current repo truth. - **Approval class**: Core Enterprise - **Red flags triggered**: New rendered delivery surface and a conditional PDF branch. Defense: the slice stays derived from the current `ReviewPack` truth, forbids a new package or artifact family, and keeps PDF honest instead of forcing infrastructure. - **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 1 | **Gesamt: 10/12** - **Decision**: approve ## Candidate Source And Completed-Spec Guardrail - **Candidate source**: - direct user-provided Spec 356 draft - explicit deferred follow-up in `specs/355-platform-sellable-smoke-matrix/spec.md` - current review-pack runtime truth in `ReviewPackService`, `GenerateReviewPackJob`, customer-review surfaces, and their tests - **Completed-spec guardrail result**: - `specs/263-auditor-pack-executive-export/` is treated as completed historical/runtime context because its task checklist is fully checked and its runtime/test traces are present; it must not be rewritten. - `specs/347-*`, `349-*`, `351-*`, and `355-*` are dependency and proof context only and must not be normalized back into preparation wording. - no `specs/1000-*` package existed before this Spec 356 run. - **Close alternatives deferred**: - `customer-review-workspace-v1-completion`: broader workspace productization lane; this renderer is the smaller output-first slice. - `localization-v1-customer-facing-surfaces`: important follow-through, but it should not hide the output-format gap. - `customer-portal-boundary-contract`: premature before the current output artifact is rendered and trustworthy. - `decision-based-governance-inbox-v1`: separate operator-workbench lane, not delivery-format work. - **Smallest viable implementation slice**: one current-review-bound HTML report plus truthful preview/download affordances on existing review/report surfaces; PDF only when the same render contract can produce it without new infrastructure. ## Spec Scope Fields *(mandatory)* - **Scope**: canonical-view - **Primary Routes**: - existing workspace customer review registry at `/admin/reviews/workspace` - existing environment review detail route under `EnvironmentReviewResource` - existing review-pack detail route under `ReviewPackResource` - existing signed review-pack download route `/admin/review-packs/{reviewPack}/download` - one new read-only rendered-report route under the current `/admin/review-packs/{reviewPack}/...` family for preview/print delivery, while the current signed route remains ZIP-download-only - **Data Ownership**: - `EnvironmentReview`, `EnvironmentReviewSection`, `ReviewPack`, `EvidenceSnapshot`, `StoredReport`, `Finding`, `FindingException`, and `AuditLog` remain the only persisted truth used by this slice - rendered HTML and any optional PDF remain derived from current review-pack/review truth; if implementation requires a new table, a new stored artifact family, or new DB columns, the scope must stop and split - **RBAC**: - workspace membership remains the first isolation boundary and stays `404` for non-members or out-of-scope environment/review targets - current operator export initiation on published review detail continues to require the existing review-manage capability path used by `export_executive_pack` - current in-scope review/review-pack view permissions remain authoritative for rendered preview/download - this slice must not invent a new capability family for rendered delivery For canonical-view specs, the spec MUST define: - **Default filter behavior when tenant-context is active**: `CustomerReviewWorkspace` keeps the current managed-environment prefilter launch behavior. The rendered report remains anchored to the selected released review/current pack rather than any hidden shell/session state. - **Explicit entitlement checks preventing cross-tenant leakage**: rendered preview/download is available only for entitled workspace and managed-environment scope through the current review/review-pack seams; inaccessible targets are omitted from aggregate lists and direct targeting resolves as not found. ## 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 - [ ] New table/form/state added - [x] Customer-facing surface changed - [ ] Dangerous action changed - [x] Status/evidence/review presentation changed - [ ] Workspace/environment context presentation changed ## UI/Productization Coverage *(mandatory when UI Surface Impact is not "No UI surface impact"; otherwise write `N/A - no reachable UI surface impact` plus rationale)* - **Route/page/surface**: - `CustomerReviewWorkspace` governance-package area - `ViewEnvironmentReview` in customer-workspace mode - `ReviewPackResource` detail/download surface - one new read-only rendered report preview/print route under `/admin/review-packs/{reviewPack}/...` - **Current or new page archetype**: existing customer-safe detail/report surfaces plus one rendered-report viewer route - **Design depth**: Strategic Surface for customer-review workspace follow-through, released-review detail, review-pack detail, and rendered report preview - **Repo-truth level**: repo-verified existing surfaces plus one new spec-backed rendered-report route in the current review-pack family - **Existing pattern reused**: current governance-package truth, `ArtifactTruthPresenter`, current review-pack delivery contract, current customer-safe detail disclosure - **New pattern required**: one bounded report-view pattern over the existing review-pack truth; no portal, dashboard, or second delivery family - **Screenshot required**: yes; the rendered report and its launch path need browser-proof screenshots during implementation - **Page audit required**: yes; the rendered report is a new reachable customer-facing surface - **Customer-safe review required**: yes; this slice exists to improve customer/demo delivery without leaking internal detail - **Dangerous-action review required**: no; the slice adds read-only delivery surfaces only - **Coverage files updated or explicitly not needed**: - [x] `docs/ui-ux-enterprise-audit/route-inventory.md` - [x] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` - [x] `docs/ui-ux-enterprise-audit/page-reports/...` - [x] `docs/ui-ux-enterprise-audit/strategic-surfaces.md` - [ ] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` - [x] `docs/ui-ux-enterprise-audit/unresolved-pages.md` - [ ] `N/A - no reachable UI surface impact` - **No-impact rationale when applicable**: N/A - **Audit follow-through note**: implementation must update the Review Pack detail/page coverage so the current `UI-042` unresolved ledger entry no longer conflicts with the changed review-pack/report surfaces. ## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)* - **Cross-cutting feature?**: yes - **Interaction class(es)**: download actions, preview/report viewers, delivery-status messaging, customer-safe disclosure, review-output guidance - **Systems touched**: `CustomerReviewWorkspace`, `EnvironmentReviewResource`, `ViewEnvironmentReview`, `ReviewPackResource`, `ReviewPackService`, `GenerateReviewPackJob`, `ReviewPackDownloadController`, localization files, and the current review-pack/report truth presenters - **Existing pattern(s) to extend**: current `ReviewPack` delivery contract, current `export_executive_pack` path, current signed download route, current customer-safe released-review detail block - **Shared contract / presenter / builder / renderer to reuse**: `ReviewPackService`, `GenerateReviewPackJob`, `ReviewPackOutputReadiness`, `ArtifactTruthPresenter`, `EnvironmentReview` summary truth, current review-pack authorization/download seams - **Why the existing shared path is sufficient or insufficient**: the existing path is sufficient for review anchoring, entitlement, export dedupe, audit continuity, and executive-summary truth. It is insufficient only because the current externally consumable output is still ZIP-first and Markdown-first. - **Allowed deviation and why**: none. The slice must extend the current `ReviewPack` family and current review/report seams instead of creating a new report subsystem. - **Consistency impact**: rendered copy, package-readiness wording, evidence-basis language, non-certification disclosure, and dominant next-action semantics must stay aligned across workspace rows, released-review detail, review-pack detail, and the rendered report. - **Review focus**: reviewers must block any second artifact family, any live provider-data render path, any false PDF claim, or any duplicated summary language that competes with the current owner surfaces. ## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)* - **Touches OperationRun start/completion/link UX?**: yes, reuse-only - **Shared OperationRun UX contract/layer reused**: the existing `ReviewPackGenerate` start and completion flow reused by `export_executive_pack` remains the only run path - **Delegated start/completion UX behaviors**: queued toast, already-available pack reuse, active-run dedupe messaging, current operation link continuity, and current terminal notification behavior remain on the shared review-pack export path - **Local surface-owned behavior that remains**: preview/download of already-ready rendered output only; the renderer must not invent its own queued/run semantics - **Queued DB-notification policy**: unchanged from the current review-pack export contract - **Terminal notification path**: unchanged; the current `OperationRunCompleted` path remains authoritative - **Exception required?**: none ## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)* N/A - no shared provider/platform boundary is widened. Provider-specific appendix content remains secondary and does not become the primary rendered language. ## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)* | Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | |---|---|---|---|---|---|---| | Customer Review Workspace governance-package area | yes | Native Filament page plus shared review-package primitives | delivery status, launch actions, customer-safe disclosure | page, URL | no | Existing row-level `Open review` remains the list inspect model | | Released review detail in customer-workspace mode | yes | Native Filament detail surface plus shared summary/artifact-truth primitives | package meaning, evidence basis, rendered output action hierarchy | detail, URL | no | One rendered-output action must not compete with diagnostics or proof links | | Review Pack detail and rendered report preview/print surface | yes | Mixed native detail plus bounded custom report surface | evidence/report viewer, read-only output delivery | detail, route | no | The rendered report is read-only and derived from the current pack | | Published review detail in operator mode | yes | Native Filament detail surface | current pack reuse, export/read-only follow-through | detail | no | Operator export remains current source-owned initiation path | ## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)* | 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 | |---|---|---|---|---|---|---|---| | Customer Review Workspace governance-package area | Primary Decision Surface | Decide whether the current released review is ready for stakeholder consumption | release/readiness state, limitations, dominant next step | deeper review detail and rendered report after explicit open | Primary because it remains the calm workspace handoff surface | stays on the existing review-consumption workflow | removes the need to explain ZIP mechanics first | | Released review detail in customer-workspace mode | Secondary Context | Confirm what the rendered report will say and whether it is safe to open/download | executive-ready summary, evidence basis, current readiness, dominant output action | appendix detail, proof links, deeper governance sections | Secondary because the workspace owns the first selection decision | keeps delivery anchored to one released review | avoids duplicate workspace- and detail-level summaries | | Review Pack detail and rendered report preview | Secondary Context | Inspect or download the current artifact in a presentable format | rendered report, limitation disclosure, appendix relationship | raw ZIP contents and technical metadata stay secondary | Secondary because the report is a delivery artifact, not the primary decision queue | stays on the current review-pack/report flow | removes unzip-plus-Markdown explanation work | | Published review detail in operator mode | Secondary Context | Prepare or reuse the current pack before external handoff | export readiness and pack reuse status | run detail and review-pack detail after follow-up | Secondary because it is operator-only preparation, not customer-safe consumption | preserves current operator initiation flow | avoids adding a second operator export path | ## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)* | 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 Review Workspace | operator-MSP, customer-admin, customer-read-only | release state, package readiness, rendered-report availability, next step | none beyond current review state | raw pack files, fingerprints, internal reasoning | `Open review` | raw/support detail stays off the list surface | workspace rows say whether the handoff is ready; detail owns the explanation | | Released review detail in customer-workspace mode | operator-MSP, customer-admin, customer-read-only | rendered summary, evidence basis, limitation state, one output action | review lineage and deeper governance detail in secondary sections | raw payloads, fingerprints, platform reason family, internal reason ownership | `Open rendered report` or equivalent safe output action | appendix/raw detail stays secondary | the report intent is stated once and later sections add evidence | | Review Pack detail and rendered report | operator-MSP, customer-admin, auditor-read-only | rendered story first, appendix relationship second, truthful availability | technical metadata and ZIP details remain secondary | raw JSON files, fingerprints, support-only interpretation | `Open rendered report` or `Download rendered report` | raw bundle detail remains lower priority | the rendered report explains the human-readable path once; ZIP metadata stays supportive only | | Published review detail in operator mode | operator-MSP | export readiness, reuse truth, current pack availability | run detail and pack metadata after explicit follow-up | raw appendix files remain on the pack/resource seam | `Export executive pack` | customer-safe rendered copy stays off blocked/draft operator states | operator initiation does not duplicate customer-facing delivery text | ## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)* | 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 | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | Customer Review Workspace governance-package area | Dashboard / Workbench | Customer-safe workspace review hub | Open the released review that is ready for delivery | existing review-open inspect path | existing | supporting output actions stay off the list row | none | `/admin/reviews/workspace` | existing review detail route | workspace plus optional managed-environment filter | Customer review | whether the current released review is ready for a rendered handoff | none | | Released review detail in customer-workspace mode | Detail / Report | Read-only detail report | Open the rendered report | sectioned detail with one dominant safe action | forbidden | appendix/proof links remain in-body secondary | none | `/admin/reviews/workspace` | existing review detail route | workspace, managed environment, released review | Governance package | what the rendered handoff means and whether it is available | none | | Review Pack detail and rendered report | Detail / Report / Export Viewer | Read-only artifact viewer | Inspect or download the presentable output | detail then explicit report open | existing detail row click on pack list stays current | ZIP diagnostics and raw metadata stay secondary | none | current review-pack collection route | current review-pack detail and render route | workspace, managed environment, artifact state | Review pack / rendered report | rendered output readiness and appendix relationship | none | | Published review detail in operator mode | Detail / Report / Export initiation | Operator export surface | Generate or reuse the current pack | existing detail/open model | existing | pack detail and run links remain secondary | none | existing review collection route | existing review detail route | managed environment, review status, pack status | Executive pack export | whether the current output can be prepared or reused now | none | ## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)* | 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 | |---|---|---|---|---|---|---|---|---|---|---| | Customer Review Workspace | MSP operator | Decide whether a released review is ready to hand over | Read-only workspace report | Is there a presentable current review I can share now? | release state, rendered-output availability, next step | none beyond secondary detail | release readiness, evidence sufficiency, package availability | none | Open review | none | | Released review detail in customer-workspace mode | MSP operator or entitled customer reader | Understand and open the presentable report | Read-only detail report | What will the stakeholder read first, and what are the limitations? | executive summary, evidence basis, limitations, one output action | deeper governance detail, lineage, proof links | delivery readiness, evidence sufficiency, review status | none | Open rendered report / Download rendered report | none | | Review Pack detail and rendered report | MSP operator or entitled reviewer | Inspect the current artifact in human-readable form | Read-only artifact viewer | Can I open a calm report from the current pack without unpacking it? | rendered report, appendix explanation, availability | ZIP metadata, fingerprints, technical context | artifact readiness, delivery status | none | Open rendered report | none | | Published review detail in operator mode | MSP operator | Prepare or reuse the current pack | Export-initiation detail surface | Can I prepare the presentable output now, or is a current pack already available? | review status, reuse truth, pack availability | run detail and pack metadata | review status, pack availability | current `ReviewPackGenerate` only | Export executive pack | none | ## Proportionality Review *(mandatory when structural complexity is introduced)* - **New source of truth?**: no - **New persisted entity/table/artifact?**: no new persistence family; current `ReviewPack` remains the source artifact - **New abstraction?**: no new cross-domain abstraction by default; any helper must stay local to current review-pack/report rendering - **New enum/state/reason family?**: no - **New cross-domain UI framework/taxonomy?**: no - **Current operator problem**: the current output still requires ZIP-first explanation, which weakens customer/demo consumption even though the underlying review truth is already repo-real. - **Existing structure is insufficient because**: the current Markdown entrypoint and JSON appendix are accurate but not directly consumable as a calm rendered report. - **Narrowest correct implementation**: render one HTML report from current review-pack/review truth and only allow PDF when the same contract can produce it without new infrastructure. - **Ownership cost**: maintain one bounded report view, one or more read-only routes/actions, and focused customer-safe regression coverage. - **Alternative intentionally rejected**: a customer portal, a second report engine, or a new PDF dependency were rejected as broader than current-release truth requires. - **Release truth**: current-release sellability follow-through, not future delivery automation. ### Compatibility posture This feature assumes a pre-production environment. Backward compatibility, legacy aliases, migration shims, historical fixture support, and compatibility-specific tests are out of scope unless explicitly required by this spec. Current review-pack extension is preferred over a new delivery artifact family. ## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)* - **Test purpose / classification**: Feature, Browser - **Validation lane(s)**: confidence, browser, `git diff --check` - **Why this classification and these lanes are sufficient**: focused Feature tests can prove rendered contract truth, authorization, audit continuity, and honest PDF handling. One bounded browser smoke is justified because the rendered report is a customer-facing delivery surface. - **New or expanded test families**: extend existing `apps/platform/tests/Feature/EnvironmentReview/`, `apps/platform/tests/Feature/ReviewPack/` including `ReviewPackResourceTest.php`, `apps/platform/tests/Feature/Reviews/`, and current customer-review browser smoke coverage - **Fixture / helper cost impact**: low to moderate; reuse current released-review, review-pack, evidence-snapshot, and entitlement fixtures. No provider-sync, no new queue family, and no heavy-governance lane are needed. - **Heavy-family visibility / justification**: none beyond one explicit browser smoke or an existing browser family extension - **Special surface test profile**: shared-detail-family - **Standard-native relief or required special coverage**: customer-workspace detail plus rendered report need explicit disclosure and action-hierarchy coverage; ordinary review-pack CRUD smoke is not sufficient - **Reviewer handoff**: reviewers must confirm HTML is the mandatory floor, PDF is not falsely claimed, the renderer stays on the current `ReviewPack` family, and no second artifact family or new package appears - **Budget / baseline / trend impact**: low feature-local increase only - **Escalation needed**: `document-in-feature` if the repo cannot support PDF without a package and the slice lands HTML-only - **Active feature PR close-out entry**: Smoke Coverage - **Planned validation commands**: - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && 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 tests/Feature/ReviewPack/ReviewPackResourceTest.php` - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php` ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Open A Calm Rendered Review Report (Priority: P1) As an entitled operator or customer-safe reviewer, I want to open one calm rendered report from the current released review/current pack context so I do not have to unzip a package and explain Markdown/JSON files first. **Why this priority**: This is the core productization gap. Without a rendered report, the output still feels like an internal artifact. **Independent Test**: From the current customer-review/review-pack flow, open the rendered report and verify that executive story, evidence basis, limitations, findings, accepted risks, and non-certification disclosure are visible without raw diagnostics by default. **Acceptance Scenarios**: 1. **Given** a released review with a ready current pack, **When** an entitled user opens the rendered output, **Then** the report shows the executive story, evidence basis, limitations, key findings, accepted risks, and appendix relationship from current stored truth. 2. **Given** a current pack whose output readiness is `partial`, `blocked`, or `expired`, **When** the user opens the owner surfaces, **Then** the product explains the limitation truthfully and does not overclaim a customer-ready rendered report. --- ### User Story 2 - Keep Printable Delivery Honest And Bounded (Priority: P1) As an MSP operator, I want the same rendered contract to support a printable handoff path when the repo can do so honestly, but I do not want the product to promise PDF when current dependencies cannot support it safely. **Why this priority**: A printable artifact is part of the draft's user value, but false PDF claims or a second rendering engine would create more debt than value. **Independent Test**: Verify that the current review/review-pack flow exposes one rendered output contract, that HTML is always available, and that PDF is either supported from the same contract or truthfully unavailable without a dependency addition. **Acceptance Scenarios**: 1. **Given** the repo can render PDF from the same contract without a new package, **When** an entitled user requests a printable version, **Then** the product serves PDF from the same underlying rendered report and does not introduce a second artifact family. 2. **Given** the repo cannot render PDF without adding a package, **When** an entitled user uses the output surfaces, **Then** the product still serves the HTML report and does not label PDF as available. --- ### User Story 3 - Keep Delivery Tenant-Safe, Auditable, And Derived (Priority: P2) As a platform owner, I want rendered delivery to stay on the current entitlement, audit, and review-pack truth seams so the presentable report does not become a second uncontrolled export surface. **Why this priority**: The sellability gain matters only if it stays on the current authorization and audit model. **Independent Test**: Verify non-members receive `404`, in-scope viewers stay on the current read-only permission paths, and export/download/render actions keep the current audit and `OperationRun` boundaries. **Acceptance Scenarios**: 1. **Given** a non-member or wrong-environment target, **When** they try to open the rendered report or current pack route, **Then** the response remains deny-as-not-found. 2. **Given** an entitled viewer, **When** they open the rendered report, **Then** the content is derived from existing review-pack/review truth and does not trigger live provider calls or a new queue/run path. ## Edge Cases - A released review exists but no ready current pack exists yet: the owner surfaces stay truthful about unavailability and do not fabricate a rendered handoff action. - The current pack is expired: the owner surfaces show `expired`, and the user must rely on the current operator export path rather than a stale rendered link. - Evidence is partial or stale: the rendered report must carry the limitation state explicitly instead of reading as customer-safe ready. - Workspace lifecycle blocks new pack generation but an existing ready pack remains readable: export stays blocked while current read-only access remains governed by existing entitlement rules. - PDF support is absent in current repo dependencies: the feature must still ship HTML cleanly or stop at the documented HTML-only boundary without adding a package. ## Requirements *(mandatory)* **Constitution alignment:** This feature reuses the current `ReviewPackGenerate` `OperationRun` path and current review-pack authorization/download seams. It must not introduce a second run type, a second artifact family, or live provider calls during render. **Constitution alignment (PROP-001 / PERSIST-001 / BLOAT-001):** The rendered output must remain derived from current review/review-pack truth. If implementation requires a new table, a second persisted artifact family, or a new dependency-backed rendering engine, the scope fails and must split. **Constitution alignment (XCUT-001):** Delivery status messaging, rendered report actions, and customer-safe disclosure must extend the current review/review-pack/report seams rather than introducing a parallel local UX language. ### Functional Requirements - **FR-356-001**: The current review-derived `ReviewPack` remains the source artifact for this slice; no second artifact family may be introduced. - **FR-356-002**: Operator-side export initiation for published reviews must continue to reuse the current `export_executive_pack` action and current `ReviewPackGenerate` `OperationRun` semantics. - **FR-356-003**: The renderer must consume the current review-derived runtime truth exposed by `EnvironmentReview`, `EnvironmentReviewSection`, `ReviewPack`, current readiness semantics, and current governance-package summary truth. It must stay semantically consistent with the existing ZIP contract, including the current `executive-summary.md` and section ordering/content, without requiring archive re-parsing as the primary runtime source. - **FR-356-004**: V1 must provide one rendered HTML report suitable for customer-safe and demo-ready consumption without requiring JSON inspection by default. - **FR-356-005**: The HTML report must present executive story, evidence basis, limitation state, key findings, accepted risks, governance decisions requiring awareness, next actions, and explicit non-certification disclosure. - **FR-356-006**: The rendered report must not expose raw provider payloads, fingerprints, internal reason ownership, platform reason families, or operator-only diagnostics by default. - **FR-356-007**: The rendered output must remain anchored to one released review and one current review pack. No batch, portfolio, or multi-review delivery is allowed in v1. - **FR-356-008**: `CustomerReviewWorkspace`, released-review detail, review-pack detail, and any render/download actions must keep readiness states truthful and must not imply rendered or PDF readiness when the current truth does not support it. - **FR-356-009**: PDF is allowed only if the repo can generate it from the same rendered contract without a new package, a second render subsystem, or a second artifact family. Otherwise HTML remains the v1 floor and the product must stay honest about PDF unavailability. - **FR-356-010**: Existing export/download audit events and metadata must be reused or minimally extended. A new renderer-specific audit family is out of scope. - **FR-356-011**: This slice must not add a new panel, a new global-search surface, or a new Filament asset strategy. - **FR-356-012**: This slice must not require live provider calls or Graph calls during render. - **FR-356-013**: Report chrome and app actions must render outside the report canvas and must be hidden from print output so printed reports contain no operator/admin controls. - **FR-356-014**: The rendered report must show a readiness-aware hero state at the top. Customer-safe labels may appear only when the stored review/pack readiness supports them; limited, internal, PII, blocked, or not-ready outputs must show an external-sharing warning. - **FR-356-015**: The report must include a management-readable Executive Summary that explains overall state, reason, impact, recommended next action, and top limitations without making raw state keys the dominant copy. - **FR-356-016**: Output limitations and evidence-basis copy must be human-readable, early in the report, and honest about shareability. Technical state fields may appear only in the supporting appendix. - **FR-356-017**: Report localization must not leak raw `localization.*` keys in EN or DE, including the non-certification disclosure. - **FR-356-018**: Empty or zero-heavy sections must collapse to compact empty-state copy instead of large KPI/card grids. - **FR-356-019**: The appendix must appear after the management/readiness/risk/evidence sections, be clearly marked as supporting/auditor context, and avoid raw JSON dump presentation. - **FR-356-020**: Accepted-risk content must use customer-safe summaries when available, must not expose internal rationale as customer-safe copy, and must honestly show expired/expiring/incomplete state without legal or approval claims. - **FR-356-021**: Controlled MSP co-branding is limited to repo-backed existing workspace/environment names plus TenantPilot generated-by copy. No branding settings, upload UI, theme engine, or new persistence may be added. - **FR-356-022**: Report/download labels must be readiness-aware and must not use forbidden terms such as customer-ready, certified, approved compliance report, or share-with-customer for limited/internal/blocked output. - **FR-356-023**: The existing ZIP review-pack download/export contract must continue to work unchanged alongside the rendered HTML/print report. ## Scope Boundaries ### In Scope - one rendered HTML report over the current review-derived `ReviewPack` contract - truthful preview/download affordances on the current review/customer-workspace/report seams - honest PDF handling from the same contract when current repo support exists - localized copy required to explain rendered output, appendix relationship, and PDF availability truthfully - focused authorization, audit, disclosure, and browser smoke follow-through for the rendered report - bounded productization of report presentation, readiness copy, print chrome, localization, compact empty sections, supporting appendix, accepted-risk display, evidence-basis explanation, and repo-backed MSP co-branding slots ### Out Of Scope - a customer portal - a standalone `AuditorPack` or `RenderedReport` persistence family - a new PDF dependency or rendering package - email delivery, scheduled delivery, public sharing, or branding/white-label systems - multi-review or multi-tenant bundles - a second review composition engine - AI-generated report narratives - a branding admin UI, logo upload, accent/theme engine, customer-specific templates, public links, customer accounts, or customer portal ## Success Criteria - Entitled users can open one rendered HTML report from current released-review/current-pack context without unzipping JSON first. - The rendered output stays customer-safe, limitation-aware, and derived from current stored truth. - PDF is either served from the same contract without new infrastructure or explicitly and honestly unavailable. - No second artifact family, new package, new queue family, or live provider render path is introduced. ## Risks - **Risk 1 - PDF support gap**: current repo dependencies do not provide an approved native report PDF stack for this slice. Mitigation: HTML and browser print are mandatory; native PDF remains a documented follow-up behind the hard no-new-package boundary. - **Risk 2 - Scope creep into portal/reporting engine work**: presentable output can tempt broader document-delivery ambitions. Mitigation: stay on the current review-pack family and existing routes/surfaces only. - **Risk 3 - Duplicate truth drift**: rendered copy could diverge from current review detail or executive-summary truth. Mitigation: keep one render contract sourced from current stored review-pack/review data. - **Risk 4 - Customer-safe overclaim**: a rendered report can look more final than the underlying evidence state deserves. Mitigation: render limitation state and non-certification disclosure explicitly. ## Follow-Up Candidates - PDF-only delivery hardening if HTML lands and the repo later gains an approved PDF stack - richer customer-facing localization adoption over rendered delivery surfaces - customer portal boundary work only after current rendered delivery is proven ## Assumptions - Spec 355's browser-verified readiness gate is sufficient proof that the current review/output flow is coherent enough for a rendered delivery follow-up. - Current `ReviewPack` and `EnvironmentReview` summary truth is rich enough to drive an HTML report without live provider calls. - Implementation and productization changes are included in this branch; close readiness is based on completed validation, screenshots, and reviewer acceptance rather than additional preparation-only work. ## Open Questions No blocking implementation questions remain. Runtime constraint result: - the current repo does not provide an approved native report PDF stack from the same render contract without a new dependency; the slice remains HTML-first with browser print and records native PDF as a bounded follow-up instead of widening scope