# Data Model — Customer Review Workspace Productization v1 **Spec**: [spec.md](spec.md) No new persisted tables, projections, or customer-review entities are required for this follow-up. The feature reuses current tenant-owned review, finding-exception, evidence, review-pack, membership, and audit truth, then tightens the derived workspace and detail presentation contracts. ## Persisted Truth Reused ### Workspace / Tenant Entitlement Context **Purpose**: Establish the active workspace boundary and the entitled tenant set before any workspace rows, proof links, or review detail routes are composed. **Persisted carriers**: - existing workspace membership records - existing tenant membership pivot rows and role assignments - existing capability registry and role-capability map **Relevant fields / contracts**: - `workspace_id` - `tenant_id` - tenant membership role - capability grants derived from [../../apps/platform/app/Support/Auth/Capabilities.php](../../apps/platform/app/Support/Auth/Capabilities.php) - current workspace and remembered tenant context from the existing workspace context/session model **Validation rules**: - current actor must be a member of the current workspace or the route resolves as not found - workspace rows and explicit tenant filters may only resolve for entitled tenants in that current workspace - out-of-scope tenant targets remain `404` and must not leak draft/review existence ### TenantReview **Purpose**: Canonical source for the released governance record, current outcome summary, findings summary, accepted-risk summary, proof pointers, and review-detail inspect target. **Persisted carrier**: existing `tenant_reviews` rows via [../../apps/platform/app/Models/TenantReview.php](../../apps/platform/app/Models/TenantReview.php) **Relevant fields / relationships**: - `id` - `workspace_id` - `tenant_id` - `status` - `generated_at` - `published_at` - `summary` - `evidence_snapshot_id` - `current_export_review_pack_id` - `published_by_user_id` - `tenant` - `evidenceSnapshot` - `currentExportReviewPack` - `sections` **Embedded summary payload currently reused**: - `finding_count` - `finding_outcomes` - `risk_acceptance.status_marked_count` - `risk_acceptance.valid_governed_count` - `risk_acceptance.warning_count` - `publish_blockers` **Validation / usage rules**: - the workspace default path continues to use the latest published review per entitled tenant only - internal-only review states remain off the customer-safe default path - the customer-workspace drilldown stays on the existing review detail route under the existing query-context flag - productization may refine how summary data is explained, but it must not move that truth into a new stored model ### FindingException **Purpose**: Existing accepted-risk and accountability truth used to explain who accepted risk, why it is on record, and whether it needs follow-up. **Persisted carrier**: existing `finding_exceptions` rows via [../../apps/platform/app/Models/FindingException.php](../../apps/platform/app/Models/FindingException.php) **Relevant fields / relationships**: - `id` - `workspace_id` - `tenant_id` - `finding_id` - `status` - `current_validity_state` - `requested_at` - `approved_at` - `effective_from` - `expires_at` - `review_due_at` - `owner_user_id` - `approved_by_user_id` - `current_decision_id` - `evidence_summary` - `owner` - `approver` - `currentDecision` - `evidenceReferences` **Validation / usage rules**: - accountability summaries should derive from existing owner/approver/current-decision truth where present - missing accountable-person or accountable-role truth must surface as partial/unavailable disclosure, not fabricated customer-safe copy - accepted-risk visibility remains read-only in this slice; no edit, renew, revoke, or approval behavior moves into the customer-safe path ### EvidenceSnapshot **Purpose**: Existing proof artifact for evidence freshness, completeness, and optional supporting detail reached only after explicit user intent. **Persisted carrier**: existing `evidence_snapshots` rows via [../../apps/platform/app/Models/EvidenceSnapshot.php](../../apps/platform/app/Models/EvidenceSnapshot.php) **Relevant fields / relationships**: - `id` - `workspace_id` - `tenant_id` - `status` - `completeness_state` - `generated_at` - `expires_at` - `summary` - `items` **Validation / usage rules**: - evidence proof remains optional, lower-priority, and capability-gated by the current evidence-view path - raw payloads and unrestricted diagnostics remain out of the default-visible workspace and review detail path - if implementation adds explicit proof-access auditing, it should stay on the shared audit pipeline ### ReviewPack **Purpose**: Existing packaged governance artifact for current downloadable review output. **Persisted carrier**: existing `review_packs` rows via [../../apps/platform/app/Models/ReviewPack.php](../../apps/platform/app/Models/ReviewPack.php) **Relevant fields / relationships**: - `id` - `workspace_id` - `tenant_id` - `tenant_review_id` - `status` - `generated_at` - `expires_at` - `summary` - `file_path` - `file_disk` - `sha256` - `operation_run_id` - `tenantReview` - `evidenceSnapshot` **Validation / usage rules**: - only current ready, unexpired packs remain available in the customer-safe flow - review-pack access continues to use the existing signed download route and current capability check - the feature must not surface generate/regenerate flows, even when a pack is unavailable ### Audit Log Event Family **Purpose**: Existing auditable truth for explicit customer-review consumption moments. **Persisted carrier**: existing `audit_logs` rows via [../../apps/platform/app/Services/Audit/WorkspaceAuditLogger.php](../../apps/platform/app/Services/Audit/WorkspaceAuditLogger.php) **Relevant current action IDs**: - `tenant_review.opened` - `review_pack.downloaded` **Potential bounded extensions only if implementation confirms a gap**: - workspace access open event for the customer review workspace route - evidence proof access open event for proof routes launched from the customer review flow **Validation / usage rules**: - auditable access remains on the shared audit path only - no new audit store or mirror analytics stream is justified - workspace, tenant, source-surface, and artifact identifiers stay in stable audit metadata when a new access moment is added ## Derived Read Models ### CustomerReviewWorkspaceEntry **Purpose**: Derived row-level presentation contract for one entitled tenant on the existing workspace page. **Persistence**: none; computed at request time **Fields**: - `workspace_id` - `tenant_id` - `tenant_name` - `latest_published_review_id` (nullable) - `latest_review_published_at` (nullable) - `outcome_summary` - `findings_summary` - `accepted_risk_accountability_summary` - `evidence_proof_state` - `review_pack_state` - `primary_review_url` (nullable) - `review_pack_download_url` (nullable) - `proof_detail_url` (nullable) - `absence_note` (nullable) - `unavailable_note` (nullable) - `redaction_note` (nullable) **Derivation rules**: - exactly one derived entry exists per entitled tenant visible in the current workspace scope - if a published review exists, the entry derives its customer-safe summary from that released record only - if no published review exists, the entry surfaces an explicit absence note and omits deep links that depend on a released review - if optional proof or pack access is blocked by capability or artifact state, the review remains readable while the secondary path becomes explicitly unavailable **Validation rules**: - entries may only be built for entitled tenants in the active workspace - `review_pack_download_url` is present only when a current pack exists and the actor can consume it - `proof_detail_url` is present only when the actor can open the proof route - raw payloads, unrestricted diagnostics, provider IDs, and copied support context are never part of the default entry model ### CustomerReviewDetailPresentation **Purpose**: Derived section contract for the existing released-review detail page when it is launched from the customer review workspace. **Persistence**: none; computed from the existing review record and current query-context flag **Fields**: - `review_id` - `tenant_id` - `launched_from_customer_workspace` (boolean) - `narrative_outcome_summary` - `findings_summary` - `accepted_risk_accountability_summary` - `evidence_summary` - `proof_pointer_state` - `review_pack_state` - `operator_actions_hidden` (boolean) - `secondary_diagnostics_collapsed` (boolean) **Derivation rules**: - only the existing `customer_workspace` query context activates this productized secondary presentation mode - the detail remains readable even when optional pack/evidence capabilities are absent - management actions remain suppressed in this context **Validation rules**: - this derived model must not create a second review detail route or a second stored summary object - secondary proof and support detail remain lower-priority than the narrative governance record - duplicate equal-priority summary blocks between workspace and detail should be removed or reduced ### CustomerReviewPageState **Purpose**: Request/query/session-backed page state already required for tenant-prefilter, remembered scope, and launch context continuity. **Persistence**: request, URL query, and existing session-backed table state only **Fields**: - `tenant` prefilter (nullable) - remembered tenant id in workspace context (nullable) - `customer_workspace` detail context flag (boolean on the detail route) - navigation context metadata when launched from other canonical pages (nullable) **Validation rules**: - explicit tenant prefilters must resolve to an entitled tenant or the request fails as not found - any state required after Livewire interaction must remain hydrated via public/query/session-backed state - no private property may own the control path for disclosure or filter restore ## Derived Disclosure States This feature introduces no new persisted lifecycle or enum family. It does require explicit derived disclosure outcomes on existing surfaces: - `available`: the actor can open the review/proof/pack path now - `absent`: the underlying released artifact does not exist for this tenant yet - `unavailable`: the artifact exists conceptually but is not currently consumable because of capability, readiness, or redaction limits - `expired`: the artifact exists and was previously consumable, but time-based or release-lifecycle rules now block access while the surface still needs to explain why - `redacted`: the route or surface remains visible, but protected details stay hidden behind existing redaction rules - `partial`: the governance record is readable, but accountability/proof detail is incomplete in current source truth These remain derived page semantics only and must not become stored status families. ## State Transition Summary No new persisted lifecycle is added. Only derived surface transitions are expected: - workspace open -> entitled tenant rows or truthful empty/absence state - remembered tenant or explicit tenant query -> tenant-prefiltered workspace view - workspace row with released review -> existing review detail route available - workspace row without released review -> explicit absence state and no review-open action - released review detail with optional proof/pack capability missing -> review remains readable and secondary path becomes unavailable - released review detail with an expired pack/proof artifact -> review remains readable and secondary path becomes explicitly expired - explicit workspace/review/proof/pack consumption -> shared audit event when covered by the current audit registry or a bounded additive action ID