# Implementation Plan: Spec 357 - Report Profiles & Disclosure Policy v1 **Branch**: `357-report-profiles-disclosure-policy-v1` | **Date**: 2026-06-05 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/357-report-profiles-disclosure-policy-v1/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/357-report-profiles-disclosure-policy-v1/spec.md` ## Summary Add a bounded report-policy layer to the current rendered review-report flow. The implementation should keep the current `ReviewPack` artifact and rendered report route, but introduce: 1. a static report-profile registry 2. a disclosure-policy evaluator with explicit proof states 3. profile-aware rendered-report composition and section filtering The slice must stay static and read-only. It must not create new persistence, delivery flows, public links, PDF infrastructure, or AI/runtime expansion. ## Technical Context **Language/Version**: PHP 8.4.15 **Primary Dependencies**: Laravel 12.52, Filament 5.2.1, Livewire 4.1.4, Pest 4.3 **Storage**: Existing PostgreSQL truth plus existing `exports` disk-backed `ReviewPack` artifacts only **Testing**: Pest Unit, Feature, Browser **Validation Lanes**: confidence, browser **Target Platform**: Laravel Sail local runtime and existing Filament admin panel **Project Type**: Laravel monolith with Filament admin surfaces **Performance Goals**: No measurable render slowdown beyond current rendered report; no new remote calls during render **Constraints**: No Graph/provider calls during render, no new persistence, no public route contract widening, no second artifact family **Scale/Scope**: One existing report route and its owner surfaces, one static registry, one policy evaluator, focused regressions ## Repo Truth Current repo-real seams: - `apps/platform/app/Http/Controllers/ReviewPackRenderedReportController.php` already builds the rendered report payload. - `apps/platform/resources/views/review-packs/rendered-report.blade.php` already renders HTML-first output. - `apps/platform/app/Support/ReviewPacks/ReviewPackOutputReadiness.php` and `ReviewPackOutputResolutionGuidance.php` already derive readiness and limitation semantics. - `apps/platform/app/Services/ReviewPackService.php` and `GenerateReviewPackJob` already anchor the current `ReviewPack` delivery contract. - `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md`, `ui-042-review-pack-detail.md`, and `ui-099-rendered-review-report.md` already document the affected strategic surfaces. Current gap: - There is no static report-profile registry. - There is no first-class disclosure-policy result that distinguishes `verified`, `assumed`, `missing`, `unknown`, and `not_applicable`. - The current renderer cannot express different audience/detail policies without ad hoc branching. ## Candidate Selection Gate **Result**: PASS - The candidate was directly provided by the user as a concrete Spec 357 draft. - It aligns with current runtime truth after Spec 356 rather than reopening an older or completed foundation. - Related specs 347, 355, and 356 are treated as historical/runtime context only. - The scope is small and reviewable: static registry + disclosure policy + existing rendered-report integration. - Broader alternatives such as billing, localization, customer portal, or AI remain explicitly deferred. ## UI / Surface Guardrail Plan - **Guardrail scope**: changed existing customer/report surfaces - **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: - `/admin/reviews/workspace` - `/admin/workspaces/{workspace}/environments/{environment}/environment-reviews/{record}` - `/admin/workspaces/{workspace}/environments/{environment}/review-packs/{record}` - `/admin/review-packs/{reviewPack}/report` - **No-impact class, if applicable**: N/A - **Native vs custom classification summary**: mixed; existing native Filament owner surfaces plus the existing custom rendered-report Blade surface - **Shared-family relevance**: report viewer, status messaging, artifact truth, customer-safe disclosure - **State layers in scope**: detail route payload and existing owner-page derived payload; no shell or navigation contract change - **Audience modes in scope**: customer/read-only, operator-MSP, controlled auditor, support/internal - **Decision-first contract**: the rendered report must identify its audience, readiness, and limitations immediately before showing deeper section detail - **Required tests or manual smoke**: - explicit Unit tests for policy - explicit Feature coverage for rendered report - one bounded Browser smoke for profile variants and disclosure hierarchy - **Active feature PR close-out entry**: `Guardrail / Exception / Smoke Coverage` ## Technical Approach ### 1. Keep everything inside the existing ReviewPack report family Do not create: - a new report persistence model - a separate rendered artifact family - a profile CRUD resource - a delivery workflow The current rendered report stays the single report route. The new logic should live inside `App\Support\ReviewPacks` and be consumed by the existing controller and owner surfaces. ### 2. Introduce a static report-profile registry Preferred repo-aligned shape: - `apps/platform/app/Support/ReviewPacks/ReportProfileRegistry.php` - one static array-based contract or readonly value structure - no database/config mutation path Required implemented profiles: - `customer_executive` - `customer_technical` - `internal_msp_review` - `auditor_appendix` Required placeholder: - `framework_readiness` marked not implemented and not selectable for an implemented report The registry must remain narrow and typed enough for tests, but must not become a generalized framework. ### 3. Introduce a disclosure-policy evaluator Preferred repo-aligned shape: - `apps/platform/app/Support/ReviewPacks/ReportDisclosurePolicy.php` Inputs: - selected effective profile - current readiness / limitation truth - evidence completeness state - PII/internal-only state - available source/disclosure metadata Outputs: - mandatory disclosures - warnings - blocking reasons - proof states (`verified`, `assumed`, `not_applicable`, `missing`, `unknown`) The evaluator must use stored truth only. No render-time provider or Graph calls. ### 4. Add one local rendered-report profile composition layer if needed The current controller already builds a large report payload. If profile/disclosure integration makes that method unreviewable, introduce one local support layer under `App\Support\ReviewPacks` to compose: - effective profile metadata - filtered section list - disclosure result - appendix policy - audience labels This must stay local to the current rendered report. No cross-domain reusable viewer framework. ### 5. Keep profile selection bounded and authenticated V1 defaults: - default effective profile: `internal_msp_review` - customer-facing or auditor-oriented variants remain authenticated and capability-gated Preferred surface policy: - the existing report route may accept a bounded authenticated profile selector only when the parameter is carried through the existing signed URL builders (`ReviewPackService::generateRenderedReportUrl()` and current owner-surface helper/action seams) - existing owner surfaces may add at most one small group of safe "view report as ..." actions if that remains reviewable - if action-surface growth is too large, keep selection route-local via those existing signed URL seams and test it directly No unsigned query contract or share link may be introduced. ### 6. Mandatory truth overrides profile presentation Profiles may influence: - section inclusion - appendix visibility - detail level - audience label Profiles may not hide: - readiness state - evidence completeness / limitation state - internal-only or PII warning - non-certification disclosure - generated/source metadata ### 7. Preserve current owner-surface boundaries - `CustomerReviewWorkspace` remains the handoff surface - `EnvironmentReviewResource` remains the released-review detail seam - `ReviewPackResource` remains the artifact truth seam - the rendered report remains read-only delivery output Do not turn any of these into a profile management surface. ## Domain / Model Implications - No new table, column, enum, or persisted artifact family is expected. - `ReviewPack`, `EnvironmentReview`, `EnvironmentReviewSection`, and `EvidenceSnapshot` remain the only persisted truth used here. - Disclosure proof states remain derived-only and request-scoped. ## Filament / Livewire Implications - Livewire v4.1.4 compliance is required; no Livewire v3 APIs. - Filament panel provider registration remains unchanged in `apps/platform/bootstrap/providers.php`. - No global-search changes are expected. `ReviewPackResource` and related review surfaces keep their current search posture. - Any new action affordances on owner surfaces must remain read-only and subordinate to the existing primary inspect/open model. ## OperationRun / Observability Implications - No new `OperationRun` type is introduced. - Existing review-pack generation/run-link behavior remains authoritative. - Any profile or disclosure result should remain derivable from existing stored artifact truth and not require a new run or audit path. ## Authorization / Security Implications - Existing workspace/environment entitlement and `Capabilities::REVIEW_PACK_VIEW` remain authoritative. - Customer-facing profiles must not bypass internal-only or current-export guards. - Invalid profile keys must fail closed. - Non-members and out-of-scope records remain 404, missing capability remains 403. ## Data / Persistence Implications - No migrations - No new settings rows - No new config-write path - No new stored report or rendered artifact record If implementation proves persistence is required, stop and split the scope. ## Test Strategy ### Unit - registry shape and implemented/placeholder behavior - disclosure proof-state evaluation - invalid-profile fail-closed behavior ### Feature - rendered report profile selection and profile metadata - mandatory disclosures cannot be hidden - customer profiles remain visibly limited on limited/internal output - internal profile remains clearly internal when appropriate ### Browser One bounded smoke file covering: - internal MSP profile with limitations - customer executive profile on limited output - customer-safe profile on ready output - auditor appendix profile - invalid or placeholder profile behavior ### Regression Keep existing rendered-report and review-pack tests green, especially Spec 347 and Spec 356 coverage. ## Rollout / Deployment - No env vars - No migrations - No queue changes - No scheduler changes - No storage contract changes - No `filament:assets` impact expected ## Risks And Controls - **Risk**: profile system drifts into delivery or portal work - **Control**: static registry only; explicit non-goals in tasks and checklist - **Risk**: proof-state modeling becomes a fake certainty layer - **Control**: explicit `assumed`/`verified` distinction and tests - **Risk**: customer-facing profile hides limitations - **Control**: mandatory disclosure override plus feature/browser assertions - **Risk**: controller complexity grows too far - **Control**: allow one local report-profile payload layer only if needed ## Implementation Phases 1. Repo-truth recheck and failing tests first 2. Static report-profile registry 3. Disclosure-policy evaluator with proof states 4. Rendered-report payload integration and section filtering 5. Owner-surface handoff and localization updates 6. UI-audit follow-through 7. Focused validation, browser smoke, and screenshots ## Spec Readiness Gate **Result**: PASS - `spec.md`, `plan.md`, and `tasks.md` are present. - Scope is bounded to the current rendered report family. - No open question blocks safe implementation. - RBAC, isolation, disclosure truth, and customer-safe behavior are explicit. - No new persistence or delivery workflow is required. - Required checklist artifact is included in this spec package. ## Assumptions - The current rendered report route remains the correct owner for profile-aware output. - A bounded authenticated selection mechanism is sufficient for v1. - Existing report/resource page reports can absorb the UI audit updates without inventing new page identities. ## Open Questions - Whether owner-surface actions are necessary or whether the signed-URL-local profile selector is enough for v1 - Whether `auditor_appendix` should render as internal-only by default until a later delivery workflow spec These are safe implementation decisions, not product blockers.