## 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
10 KiB
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, orBackupScheduleownership- existing backup-quality source-of-truth rules
- existing backup or schedule route identity
- existing mutation, audit, or
OperationRunresponsibilities - 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:
idtenant_idworkspace_idnamestatusitem_countmetadatacompleted_atcreated_at
- Existing relationships used by this feature:
tenantitemsrestoreRuns
Notes
completed_atis the primary timestamp used to decide whether a completed backup basis exists and whether it is fresh enough.- No new
backup_healthorfreshness_statecolumn is introduced onbackup_sets.
2. BackupItem
- Purpose: Tenant-owned captured recovery input for one backed-up policy or foundation record.
- Existing persistent fields used by this feature:
idtenant_idbackup_set_idpayloadassignmentsmetadatacaptured_at
- Existing relationships used by this feature:
tenantbackupSetpolicyVersion
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 | 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:
idtenant_idworkspace_idnameis_enabledfrequencytime_of_daydays_of_weekpolicy_typeslast_run_statuslast_run_atnext_run_attimezonecreated_at
- Existing relationships used by this feature:
tenantoperationRuns
Notes
last_run_at,last_run_status, andnext_run_atare sufficient to deriveschedule_follow_up.- No new
schedule_health_stateorbackup_health_statecolumn 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 producesabsent. - If a latest relevant completed backup basis exists but predates
cutoffAt, the primary posture becomesstale. staleoutranksdegradedas 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_atpast the grace window or with missing success evidence after the schedule should already have produced its first successful run count towardschedule_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
absentapplies when no usable completed backup basis exists for the tenant.staleapplies when the latest relevant completed backup basis exists but fails the freshness rule, regardless of whether it is also degraded.degradedapplies when the latest relevant completed backup basis is fresh enough but existingBackupQualitySummaryshows material degradation.healthyapplies when a latest relevant completed backup basis exists, passes freshness, and shows no material degradation.schedule_follow_upis additive and must never be used as proof of health.- If
schedule_follow_upis the only remaining caution, posture may remainhealthy, butprimaryReasonmust beschedule_follow_upandhealthyClaimAllowedmust befalseuntil the follow-up resolves. - Existing
BackupQualitySummaryproduced byBackupQualityResolverremains 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.