# Data Model — Tenant Dashboard Productization v1 **Spec**: [spec.md](spec.md) No new persisted tables, dashboard aggregates, or productized score artifacts are required. This slice reuses current tenant-owned governance, exception, operations, review, evidence, and output truth, then tightens the derived dashboard composition contract over those seams. ## Persisted Truth Reused ### Workspace / Tenant Entitlement Context **Purpose**: Establish the active workspace boundary and tenant entitlement before any dashboard summary, link, or canonical follow-up route is composed. **Persisted carriers**: - existing workspace membership rows - existing tenant membership pivot rows and role assignments - current workspace context and selected tenant context **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) **Validation rules**: - current actor must be a workspace member or the route resolves as not found - current actor must be entitled to the tenant or the tenant dashboard and all tenant follow-up routes resolve as not found - canonical admin follow-up routes launched from the dashboard may only reveal the originating tenant when that tenant is still entitled in the current workspace ### Finding and Exception Truth **Purpose**: Provide active findings pressure, high-priority follow-up, and accepted-risk or exception readiness truth. **Persisted carriers**: - existing findings rows via current findings resources and services - existing `finding_exceptions` rows via [../../apps/platform/app/Filament/Resources/FindingExceptionResource.php](../../apps/platform/app/Filament/Resources/FindingExceptionResource.php) **Relevant fields / relationships**: - finding severity, status, due or stale markers, and active workflow state - exception `status` - exception `current_validity_state` - exception `review_due_at` - exception `expires_at` - exception owner, approver, and current decision relationships **Validation / usage rules**: - dashboard pressure and recommended-action logic stays tenant-scoped - accepted-risk and exception summaries remain derived from current finding and exception truth - no new `accepted risk` dashboard entity or separate persistence layer is introduced ### Tenant Governance Aggregate **Purpose**: Existing derived posture truth for compare readiness, governance pressure, and next-action hints. **Carrier**: [../../apps/platform/app/Support/Baselines/TenantGovernanceAggregateResolver.php](../../apps/platform/app/Support/Baselines/TenantGovernanceAggregateResolver.php) and related derived-state seams **Relevant fields / contracts**: - aggregate summary assessment - active findings counts - governance warning counts - compare or baseline posture states - current next-action hints **Validation / usage rules**: - remains derived only - stays the source for compare or baseline posture, not a new dashboard score - dashboard composition may reorder or compress the truth but must not fork it into a second status family ### Recovery / Restore Readiness Truth **Purpose**: Existing restore-readiness and recovery evidence truth used to summarize whether the tenant can be recovered safely. **Persisted carriers**: - existing backup metadata and snapshot records - existing restore run and recovery evidence records reached through current backup and restore services **Relevant seams**: - [../../apps/platform/app/Support/BackupHealth/BackupHealthDashboardSignal.php](../../apps/platform/app/Support/BackupHealth/BackupHealthDashboardSignal.php) - current backup health resolvers - [../../apps/platform/app/Support/RestoreSafety/RestoreSafetyResolver.php](../../apps/platform/app/Support/RestoreSafety/RestoreSafetyResolver.php) **Validation / usage rules**: - dashboard recovery and restore status remains derived from current safety evidence - no new recovery status persistence or dashboard-only restore state is introduced ### OperationRun **Purpose**: Canonical truth for recent execution state and follow-up-worthy operations. **Persisted carrier**: existing `operation_runs` rows via [../../apps/platform/app/Models/OperationRun.php](../../apps/platform/app/Models/OperationRun.php) **Relevant fields / relationships**: - `id` - `workspace_id` - `tenant_id` - `type` - `status` - `outcome` - `context` - `failure_summary` - `created_at` - `started_at` - `completed_at` **Validation / usage rules**: - recent operation summaries remain tenant-scoped on the dashboard - canonical operations collection and detail routes remain the only drill-through path - dashboard composition may compress or reorder recent operations, but it does not own lifecycle state ### TenantReview **Purpose**: Canonical source for current review readiness and review drill-through. **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` - related tenant, evidence snapshot, and current export review pack **Validation / usage rules**: - dashboard review readiness remains derived from existing review and publication truth - dashboard must not invent a customer-safe output route when no published review or usable pack exists ### ReviewPack **Purpose**: Existing packaged output artifact for current downloadable review handoff. **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` - `operation_run_id` - related tenant review and evidence snapshot **Validation / usage rules**: - dashboard output readiness must stay anchored to current pack state - if a start-capable review-pack action is reused, it must keep its existing operation and audit behavior ### EvidenceSnapshot **Purpose**: Existing proof artifact for evidence availability and drill-through. **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` - `generated_at` - `expires_at` - `summary` - `items` **Validation / usage rules**: - evidence readiness stays optional and lower-priority than the main governance decision - raw evidence payloads remain off the default-visible dashboard ### Required Permissions Comparison **Purpose**: Current provider-blockage truth for missing permissions and freshness. **Carrier**: [../../apps/platform/app/Services/Intune/TenantRequiredPermissionsViewModelBuilder.php](../../apps/platform/app/Services/Intune/TenantRequiredPermissionsViewModelBuilder.php) **Relevant fields / contracts**: - `overview.overall` - `overview.counts.missing_application` - `overview.counts.missing_delegated` - `overview.freshness.last_refreshed_at` - `overview.freshness.is_stale` - `feature_impacts` **Validation / usage rules**: - remains derived only - powers the provider-health summary card and required-permissions CTA - no new tenant provider-health persistence is introduced ## Derived Read Models ### TenantDashboardSummary **Purpose**: Derived page-level contract for the productized tenant landing surface. **Persistence**: none; computed at request time **Fields**: - `workspace` - `tenant` - `context_chips` - `header_actions` - `kpis` - `recommended_actions` - `governance_status_rows` - `recent_operations` - `current_review` - `risk_exception_summary` - `provider_health_summary` - `output_readiness_summary` - `arrival_context` (nullable) **Derivation rules**: - exactly one summary exists per current workspace + tenant pair - top-level counts, badges, and actions derive only from existing repo-real truth and explicit fallback states - hidden actions are omitted entirely; visible but blocked paths become explicit unavailable states only when the operator still needs the summary context - the summary owns ordering, not a new truth family ### DashboardHeaderAction **Purpose**: Derived contract for the capped page-header CTA set. **Persistence**: none **Fields**: - `label` - `url` or `action_key` - `style` (`primary` or `secondary`) - `availability_state` - `helper_text` (nullable) **Validation rules**: - at most 2 visible header actions - any unavailable action must be explicit and must not appear as a clickable dead end - no destructive action is introduced on the dashboard shell ### DashboardKpiCard **Purpose**: Derived posture card shown in the first KPI row. **Persistence**: none **Fields**: - `key` - `label` - `value` - `supporting_text` - `status_label` - `badge_state` - `trend_label` (nullable) - `primary_link` (nullable) **Validation rules**: - at most 4 cards - cards may use honest fallback labels such as `Unavailable`, `Not configured`, or `No data yet` - cards must not invent new score semantics or fake trend data ### RecommendedDashboardAction **Purpose**: Derived next-step record for the central decision card. **Persistence**: none **Fields**: - `priority` - `title` - `reason` - `impact` - `category` (nullable) - `cta_label` - `cta_url` - `availability_state` - `helper_text` (nullable) **Derivation rules**: - ordered by bounded priority: governance pressure, provider blockage, pending risk or exception decisions, restore-readiness gaps, evidence or review readiness gaps, operation follow-up - hidden actions are omitted from the list entirely **Validation rules**: - at most 3 actions - each action owns exactly 1 dominant CTA - if no action is needed, the list becomes a positive empty state rather than an empty shell ### GovernanceStatusRow **Purpose**: Derived compact status digest row for one readiness family. **Persistence**: none **Fields**: - `key` - `label` - `description` - `status_label` - `badge_state` - `support_link` (nullable) **Validation rules**: - rows remain read-only summaries - unknown or missing truth becomes `Unavailable` or equivalent fallback, never green by default ### RecentOperationSummary **Purpose**: Derived compact recency row for the dashboard operations card. **Persistence**: none **Fields**: - `operation_run_id` - `label` - `status_label` - `outcome_label` - `relative_time` - `summary_text` - `detail_url` **Validation rules**: - at most 4 rows - remains clearly diagnostic and secondary to the primary decision layer ### SummaryCardState **Purpose**: Shared derived state for current review, risk-exception, provider-health, and output-readiness cards. **Persistence**: none **Fields**: - `title` - `headline` - `supporting_lines` - `cta_label` (nullable) - `cta_url` (nullable) - `availability_state` - `helper_text` (nullable) **Validation rules**: - each card gets at most 1 dominant CTA - cards without repo-real follow-up routes remain read-only readiness summaries ## Derived Disclosure States This feature introduces no new persisted lifecycle or enum family. It does require explicit derived action and disclosure states across the dashboard: - `available`: the actor can follow the link or perform the existing reused action now - `absent`: the underlying artifact or condition does not exist yet - `unavailable`: the condition exists conceptually, but the actor cannot act because of capability, readiness, or current state constraints Hidden actions are omitted from the derived models entirely rather than stored as a visible state.