TenantAtlas/specs/186-tenant-registry-recovery-triage/data-model.md
ahmido 9fbd3e5ec7 Spec 186: implement tenant registry recovery triage (#217)
## Summary
- turn the tenant registry into a workspace-scoped recovery triage surface with backup posture and recovery evidence columns
- preserve workspace overview backup and recovery drilldown intent by routing multi-tenant cases into filtered tenant registry slices
- add the Spec 186 planning artifacts, focused regression coverage, and shared triage presentation helpers

## Testing
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantRegistryRecoveryTriageTest.php tests/Feature/Filament/WorkspaceOverviewSummaryMetricsTest.php tests/Feature/Filament/WorkspaceOverviewDrilldownContinuityTest.php tests/Feature/Filament/TenantResourceIndexIsWorkspaceScopedTest.php tests/Feature/Filament/WorkspaceOverviewAuthorizationTest.php tests/Feature/Guards/ActionSurfaceContractTest.php tests/Feature/Guards/FilamentTableStandardsGuardTest.php`

## Notes
- no schema change
- no new persisted recovery truth
- branch includes the full Spec 186 spec, plan, research, data model, contract, quickstart, and tasks artifacts

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #217
2026-04-09 19:20:48 +00:00

167 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model: Tenant Registry Recovery Triage
## Overview
Spec 186 adds no new persisted entity. The feature is a derived registry-view slice over existing tenant, backup-health, and recovery-evidence truth.
## Existing Persisted Inputs
### Tenant
- **Purpose**: Canonical tenant identity and lifecycle record for the workspace-scoped registry.
- **Key fields**:
- `id`
- `workspace_id`
- `external_id`
- `name`
- `tenant_id`
- `environment`
- `status`
- `domain`
- `created_at`
- **Derived fields already used by the registry**:
- `policies_count`
- `last_policy_sync_at`
- **Relationships**:
- belongs to one workspace
- has many memberships
- has many policies
- has many backup sets
- has many restore runs
### TenantBackupHealthAssessment
- **Purpose**: Existing derived tenant backup-health truth used by dashboard and workspace overview.
- **Persistence**: Not persisted.
- **Key fields**:
- `tenantId`
- `posture` = `absent | stale | degraded | healthy`
- `primaryReason` = `no_backup_basis | latest_backup_stale | latest_backup_degraded | schedule_follow_up | null`
- `headline`
- `supportingMessage`
- `latestRelevantBackupSetId`
- `latestRelevantCompletedAt`
- `positiveClaimBoundary`
- `primaryActionTarget`
- **Behavioral role in Spec 186**:
- provides the registrys `Backup posture` value
- provides the backup-attention set for workspace drilldown intent
- contributes to triage rank
### Dashboard Recovery Evidence Payload
- **Purpose**: Existing derived tenant recovery-evidence truth built from restore history.
- **Persistence**: Not persisted.
- **Key fields**:
- `backup_posture`
- `overview_state` = `unvalidated | weakened | no_recent_issues_visible`
- `headline`
- `summary`
- `claim_boundary`
- `latest_relevant_restore_run_id`
- `latest_relevant_attention_state`
- `reason` = `no_history | failed | partial | completed_with_follow_up | no_recent_issues_visible`
- **Behavioral role in Spec 186**:
- provides the registrys `Recovery evidence` value
- provides the recovery-attention set for workspace drilldown intent
- contributes to triage rank
## New Derived Read Models
### TenantRegistryTriageRow
- **Purpose**: Request-scoped row projection for the tenant registry.
- **Persistence**: Not persisted.
- **Fields**:
- `tenant_id`
- `tenant_route_key`
- `tenant_name`
- `environment`
- `lifecycle_status`
- `policies_count`
- `last_policy_sync_at`
- `backup_posture`
- `backup_reason`
- `recovery_evidence`
- `recovery_reason`
- `triage_rank`
- `next_action_url`
- **Validation rules**:
- `backup_posture` must come directly from `TenantBackupHealthAssessment::posture`
- `recovery_evidence` must come directly from `dashboardRecoveryEvidenceForTenants(...)[tenant]['overview_state']`
- `triage_rank` must be derived from the shared registry priority table, not hard-coded independently per column or filter
- `next_action_url` must resolve to an allowed destination in the current workspace and tenant scope
### TenantRegistryTriageIntent
- **Purpose**: Query-string driven list intent for opening the registry in a prefiltered triage state.
- **Persistence**: Not persisted.
- **Fields**:
- `backup_posture[]` optional array of `absent | stale | degraded | healthy`
- `recovery_evidence[]` optional array of `weakened | unvalidated | no_recent_issues_visible`
- `triage_sort` optional enum `default | worst_first`
- **Validation rules**:
- every `backup_posture[]` entry must be one of the canonical backup posture values
- every `recovery_evidence[]` entry must be one of the canonical recovery-evidence values
- if `backup_posture[]` is present for workspace backup attention, its intended set is `absent`, `stale`, and `degraded`
- if `recovery_evidence[]` is present for workspace recovery attention, its intended set is `weakened` and `unvalidated`
- workspace drilldowns that need weak-first behavior must set `triage_sort=worst_first` explicitly; manual registry filtering alone does not change the default calm-browsing sort
### WorkspaceAttentionDestination
- **Purpose**: Derived navigation target from workspace overview metrics or attention items.
- **Persistence**: Not persisted.
- **Fields**:
- `kind` = existing workspace-drilldown contract value already used by the current widgets; unchanged by this feature
- `url`
- `tenant_route_key` optional
- `filters` optional exact registry intent payload
- **Validation rules**:
- one affected visible tenant resolves to the existing single-tenant dashboard kind and opens `/admin/t/{tenant}`
- more than one affected visible tenant preserves the existing multi-tenant admin-plane kind while `url` now opens filtered `/admin/tenants`
- any registry destination must carry exact posture filter semantics, not a vague `needs attention` label
## Relationships
- **Tenant** has one derived `TenantBackupHealthAssessment` in registry context.
- **Tenant** has one derived dashboard recovery-evidence payload in registry context.
- **TenantRegistryTriageRow** combines one `Tenant` with one backup-health assessment and one recovery-evidence payload.
- **WorkspaceAttentionDestination** references either one specific tenant or one registry triage intent, but never both at once.
## Triage Ranking Model
### Canonical rank tiers
1. `backup_posture = absent`
2. `recovery_evidence = weakened`
3. `backup_posture = stale`
4. `recovery_evidence = unvalidated`
5. `backup_posture = degraded`
6. calm rows: `backup_posture = healthy` and `recovery_evidence = no_recent_issues_visible`
### Tie-breaker
- Rows inside the same tier are secondarily ordered by `tenant_name` ascending.
### Rule for dual-signal tenants
- If a tenant is weak in both domains, the highest active tier governs its `triage_rank`, but both posture signals remain visible in the row.
## State Transitions
There are no persisted state transitions in this feature.
### Derived transition rules
- A registry row moves between triage tiers only when the underlying backup-health or recovery-evidence truth changes.
- Registry intent transitions are URL-driven:
- unfiltered registry
- backup-filtered registry
- recovery-filtered registry
- manually refined registry
- cleared back to the default registry view
## Notes
- The registry continues to be the source of truth for tenant identity and lifecycle metadata in workspace context.
- The registry does not become the source of truth for full backup diagnostics, restore proof, or overall recoverability.