## Summary - add the Spec 180 tenant backup-health resolver and value objects to derive absent, stale, degraded, healthy, and schedule-follow-up posture from existing backup and schedule truth - surface backup posture and reason-driven drillthroughs in the tenant dashboard and preserve continuity on backup-set and backup-schedule destinations - add deterministic local/testing browser-fixture seeding plus a local fixture-login helper for the blocked drillthrough `403` scenario, along with the related spec artifacts and focused regression coverage ## Testing - `vendor/bin/sail artisan test --compact tests/Feature/Auth/BackupHealthBrowserFixtureLoginTest.php tests/Feature/Console/TenantpilotSeedBackupHealthBrowserFixtureCommandTest.php` - `vendor/bin/sail artisan test --compact tests/Unit/Support/BackupHealth/TenantBackupHealthResolverTest.php tests/Feature/Filament/DashboardKpisWidgetTest.php tests/Feature/Filament/NeedsAttentionWidgetTest.php tests/Feature/Filament/TenantDashboardTruthAlignmentTest.php tests/Feature/Filament/TenantDashboardTenantScopeTest.php tests/Feature/Filament/TenantDashboardDbOnlyTest.php tests/Feature/Filament/BackupSetListContinuityTest.php tests/Feature/Filament/BackupSetEnterpriseDetailPageTest.php tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php tests/Feature/Auth/BackupHealthBrowserFixtureLoginTest.php tests/Feature/Console/TenantpilotSeedBackupHealthBrowserFixtureCommandTest.php` ## Notes - Filament v5 / Livewire v4 compliant; no panel-provider change was needed, so `bootstrap/providers.php` remains unchanged - no new globally searchable resource was introduced, so global-search behavior is unchanged - no new destructive action was added; existing destructive actions and confirmation behavior remain unchanged - no new asset registration was added; the existing deploy-time `php artisan filament:assets` step remains sufficient - the local fixture login helper route is limited to `local` and `testing` environments - the focused and broader Spec 180 packs are green; the full suite was not rerun after these changes Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #212
203 lines
10 KiB
Markdown
203 lines
10 KiB
Markdown
# Data Model: Tenant Backup Health Signals
|
|
|
|
## Overview
|
|
|
|
This feature does not add or change a top-level persisted domain entity. It introduces a narrow derived tenant backup-health model over existing tenant-owned backup and schedule records and integrates that derived truth into dashboard and drillthrough surfaces.
|
|
|
|
The central design task is to make the tenant dashboard answer four operator-visible states without changing:
|
|
|
|
- `BackupSet`, `BackupItem`, or `BackupSchedule` ownership
|
|
- existing backup-quality source-of-truth rules
|
|
- existing backup or schedule route identity
|
|
- existing mutation, audit, or `OperationRun` responsibilities
|
|
- the no-new-table and no-recovery-score boundary of the feature
|
|
|
|
## Existing Persistent Entities
|
|
|
|
### 1. BackupSet
|
|
|
|
- Purpose: Tenant-owned backup collection that records capture lifecycle state and groups captured backup items.
|
|
- Existing persistent fields used by this feature:
|
|
- `id`
|
|
- `tenant_id`
|
|
- `workspace_id`
|
|
- `name`
|
|
- `status`
|
|
- `item_count`
|
|
- `metadata`
|
|
- `completed_at`
|
|
- `created_at`
|
|
- Existing relationships used by this feature:
|
|
- `tenant`
|
|
- `items`
|
|
- `restoreRuns`
|
|
|
|
#### Notes
|
|
|
|
- `completed_at` is the primary timestamp used to decide whether a completed backup basis exists and whether it is fresh enough.
|
|
- No new `backup_health` or `freshness_state` column is introduced on `backup_sets`.
|
|
|
|
### 2. BackupItem
|
|
|
|
- Purpose: Tenant-owned captured recovery input for one backed-up policy or foundation record.
|
|
- Existing persistent fields used by this feature:
|
|
- `id`
|
|
- `tenant_id`
|
|
- `backup_set_id`
|
|
- `payload`
|
|
- `assignments`
|
|
- `metadata`
|
|
- `captured_at`
|
|
- Existing relationships used by this feature:
|
|
- `tenant`
|
|
- `backupSet`
|
|
- `policyVersion`
|
|
|
|
#### Existing metadata signals used by this feature
|
|
|
|
| Key | Type | Meaning |
|
|
|---|---|---|
|
|
| `source` | string or null | Direct source marker; may indicate metadata-only capture |
|
|
| `snapshot_source` | string or null | Copied source marker from a linked policy version |
|
|
| `warnings` | array<string> | Warning messages that may imply metadata-only fallback or other quality concerns |
|
|
| `assignments_fetch_failed` | boolean | Assignment capture failed for the item |
|
|
| `assignment_capture_reason` | string or null | Explanatory capture reason; not all values are degradations |
|
|
| `has_orphaned_assignments` | boolean | One or more captured assignment targets were orphaned |
|
|
| `integrity_warning` | string or null | Existing integrity or redaction warning carried into the backup item |
|
|
|
|
### 3. BackupSchedule
|
|
|
|
- Purpose: Tenant-owned schedule configuration for automated backup execution.
|
|
- Existing persistent fields used by this feature:
|
|
- `id`
|
|
- `tenant_id`
|
|
- `workspace_id`
|
|
- `name`
|
|
- `is_enabled`
|
|
- `frequency`
|
|
- `time_of_day`
|
|
- `days_of_week`
|
|
- `policy_types`
|
|
- `last_run_status`
|
|
- `last_run_at`
|
|
- `next_run_at`
|
|
- `timezone`
|
|
- `created_at`
|
|
- Existing relationships used by this feature:
|
|
- `tenant`
|
|
- `operationRuns`
|
|
|
|
#### Notes
|
|
|
|
- `last_run_at`, `last_run_status`, and `next_run_at` are sufficient to derive `schedule_follow_up`.
|
|
- No new `schedule_health_state` or `backup_health_state` column is introduced.
|
|
|
|
## Derived Inputs
|
|
|
|
### 1. BackupHealthConfig
|
|
|
|
This is configuration, not persistence.
|
|
|
|
| Key | Type | Purpose |
|
|
|---|---|---|
|
|
| `tenantpilot.backup_health.freshness_hours` | integer | Defines the canonical age window for the latest relevant completed backup basis |
|
|
| `tenantpilot.backup_health.schedule_overdue_grace_minutes` | integer | Defines how far past `next_run_at` an enabled schedule may drift before the feature raises `schedule_follow_up` |
|
|
|
|
## Derived Models
|
|
|
|
All derived models in this section are lightweight concrete value objects in `app/Support/BackupHealth/`. The concrete file set is `TenantBackupHealthAssessment.php`, `BackupFreshnessEvaluation.php`, `BackupScheduleFollowUpEvaluation.php`, `BackupHealthActionTarget.php`, and `BackupHealthDashboardSignal.php`, with `TenantBackupHealthResolver.php` orchestrating them.
|
|
|
|
### 1. TenantBackupHealthAssessment
|
|
|
|
Tenant-level backup-health truth used by dashboard and drillthrough logic.
|
|
|
|
| Field | Type | Source | Notes |
|
|
|---|---|---|---|
|
|
| `tenantId` | integer | tenant context | Identity |
|
|
| `posture` | string | derived | One of `absent`, `stale`, `degraded`, `healthy` |
|
|
| `primaryReason` | string or null | derived | One of `no_backup_basis`, `latest_backup_stale`, `latest_backup_degraded`, or `schedule_follow_up`; `null` only when posture is healthy and no remaining follow-up reason exists |
|
|
| `headline` | string | derived | Primary operator-facing summary for dashboard surfaces |
|
|
| `supportingMessage` | string or null | derived | Secondary operator-facing explanation |
|
|
| `latestRelevantBackupSetId` | integer or null | derived | The backup set that currently governs posture |
|
|
| `latestRelevantCompletedAt` | datetime or null | derived from `BackupSet.completed_at` | Null when no usable completed basis exists |
|
|
| `qualitySummary` | `BackupQualitySummary` or null | reused `BackupQualityResolver` output | Reused quality truth for the latest relevant backup basis |
|
|
| `freshnessEvaluation` | `BackupFreshnessEvaluation` | derived | Separates recency truth from degradation truth |
|
|
| `scheduleFollowUp` | `BackupScheduleFollowUpEvaluation` | derived | Secondary caution about automation timing |
|
|
| `healthyClaimAllowed` | boolean | derived | True only when the evidence supports a positive healthy statement and no unresolved `schedule_follow_up` remains |
|
|
| `primaryActionTarget` | `BackupHealthActionTarget` | derived | Reason-driven drillthrough destination |
|
|
| `positiveClaimBoundary` | string | derived | Explains that backup health does not imply recoverability or restore success |
|
|
|
|
### 2. BackupFreshnessEvaluation
|
|
|
|
Derived recency truth for the latest relevant completed backup basis.
|
|
|
|
| Field | Type | Source | Notes |
|
|
|---|---|---|---|
|
|
| `latestCompletedAt` | datetime or null | `BackupSet.completed_at` | Null when no usable completed basis exists |
|
|
| `cutoffAt` | datetime | derived from config and `now()` | The point after which a backup basis becomes stale |
|
|
| `isFresh` | boolean | derived | False when no basis exists or when `latestCompletedAt` is older than `cutoffAt` |
|
|
| `policySource` | string | derived | Initially `configured_window` to make the canonical freshness rule explicit and testable |
|
|
|
|
#### Rules
|
|
|
|
- If there is no relevant completed backup basis, the feature does not produce `fresh`; it produces `absent`.
|
|
- If a latest relevant completed backup basis exists but predates `cutoffAt`, the primary posture becomes `stale`.
|
|
- `stale` outranks `degraded` as a primary posture when both conditions are true.
|
|
|
|
### 3. BackupScheduleFollowUpEvaluation
|
|
|
|
Derived automation follow-up truth for enabled schedules.
|
|
|
|
| Field | Type | Source | Notes |
|
|
|---|---|---|---|
|
|
| `hasEnabledSchedules` | boolean | derived from `BackupSchedule.is_enabled` | False when no enabled schedules exist |
|
|
| `enabledScheduleCount` | integer | derived | Informational count |
|
|
| `overdueScheduleCount` | integer | derived from `next_run_at` and grace window | Counts enabled schedules that appear overdue |
|
|
| `failedRecentRunCount` | integer | derived from `last_run_status` | Counts enabled schedules whose recent status indicates failure or warning |
|
|
| `neverSuccessfulCount` | integer | derived from `last_run_at` and schedule timing | Counts enabled schedules with no success evidence even though they should already have produced it |
|
|
| `needsFollowUp` | boolean | derived | True when schedule timing or status indicates operator review |
|
|
| `primaryScheduleId` | integer or null | derived | Optional single record for direct continuity if the result is unambiguous |
|
|
| `summaryMessage` | string or null | derived | Operator-facing explanation of the schedule caution |
|
|
|
|
#### Rules
|
|
|
|
- Schedule follow-up is secondary. It adds attention but does not upgrade a tenant into `healthy`.
|
|
- Enabled schedules with `next_run_at` past the grace window or with missing success evidence after the schedule should already have produced its first successful run count toward `schedule_follow_up`.
|
|
|
|
### 4. BackupHealthActionTarget
|
|
|
|
Reason-driven drillthrough target chosen by the resolver.
|
|
|
|
| Field | Type | Notes |
|
|
|---|---|---|
|
|
| `surface` | string | `backup_sets_index`, `backup_set_view`, or `backup_schedules_index` |
|
|
| `recordId` | integer or null | Used only when the target is a specific backup set |
|
|
| `label` | string | Operator-facing action label such as `Open backup sets` or `Open latest backup` |
|
|
| `reason` | string | The problem class the destination is expected to confirm |
|
|
|
|
### 5. BackupHealthDashboardSignal
|
|
|
|
Shared dashboard-facing model for KPI, attention, and healthy-check rendering.
|
|
|
|
| Field | Type | Source |
|
|
|---|---|---|
|
|
| `title` | string | derived |
|
|
| `body` | string | derived |
|
|
| `tone` | string | derived |
|
|
| `badge` | string or null | derived via shared badge primitives when the signal is rendered as an attention item |
|
|
| `badgeColor` | string or null | derived via shared badge primitives when the signal is rendered as an attention item |
|
|
| `actionTarget` | `BackupHealthActionTarget` or null | derived |
|
|
| `actionDisabled` | boolean | derived from authorization and target availability |
|
|
| `helperText` | string or null | derived |
|
|
|
|
## Validation Rules
|
|
|
|
- `absent` applies when no usable completed backup basis exists for the tenant.
|
|
- `stale` applies when the latest relevant completed backup basis exists but fails the freshness rule, regardless of whether it is also degraded.
|
|
- `degraded` applies when the latest relevant completed backup basis is fresh enough but existing `BackupQualitySummary` shows material degradation.
|
|
- `healthy` applies when a latest relevant completed backup basis exists, passes freshness, and shows no material degradation.
|
|
- `schedule_follow_up` is additive and must never be used as proof of health.
|
|
- If `schedule_follow_up` is the only remaining caution, posture may remain `healthy`, but `primaryReason` must be `schedule_follow_up` and `healthyClaimAllowed` must be `false` until the follow-up resolves.
|
|
- Existing `BackupQualitySummary` produced by `BackupQualityResolver` remains the authority for degradation families and degraded item counts.
|
|
- Dashboard summary truth must remain visible to entitled tenant-dashboard viewers even when a downstream action target is suppressed or disabled.
|