## Summary - separate provider-missing policy presence from local ignore semantics by introducing `missing_from_provider_at` - update policy, backup, and restore surfaces so current-state capture stays honest while historical restore continuity remains available - add focused sync, Filament, backup, restore, localization, and badge coverage for the new provider-missing behavior ## Scope - policy sync and model truth - policy resource visibility, badges, labels, and action gating - backup/export eligibility and restore continuity messaging - spec 261 artifacts and focused tests ## Validation - feature-specific Pest coverage is included in the branch - validation was not re-run as part of this commit/push/PR handoff Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #316
113 lines
5.9 KiB
Markdown
113 lines
5.9 KiB
Markdown
# Data Model: Provider-Missing Policy Visibility & Restore Continuity v1
|
|
|
|
## Overview
|
|
|
|
This slice changes one existing persisted entity and introduces only derived projections elsewhere. No new table, registry, or artifact family is planned.
|
|
|
|
## Entity: Policy (existing, modified)
|
|
|
|
**Table**: `policies`
|
|
|
|
### Fields
|
|
|
|
| Field | Type | Source | Notes |
|
|
|---|---|---|---|
|
|
| `id` | bigint | existing | Primary key |
|
|
| `workspace_id` | bigint | existing | Required ownership anchor |
|
|
| `tenant_id` | bigint | existing | Required ownership anchor |
|
|
| `external_id` | string | existing | Provider-facing stable key |
|
|
| `policy_type` | string | existing | Canonical local policy type |
|
|
| `ignored_at` | timestamp nullable | existing | Explicit local suppression only after this slice |
|
|
| `missing_from_provider_at` | timestamp nullable | new | Provider-missing observation in the current supported provider-backed result set |
|
|
| `last_synced_at` | timestamp nullable | existing | Last successful provider observation/update on this row |
|
|
|
|
### Invariants
|
|
|
|
- `workspace_id` and `tenant_id` remain required and non-null.
|
|
- `ignored_at` may only be set or cleared by explicit local suppression flows.
|
|
- `missing_from_provider_at` may only be set or cleared by sync/provider-observation logic.
|
|
- A policy row remains persisted and viewable even when `missing_from_provider_at` is set.
|
|
|
|
### Derived Visibility States
|
|
|
|
| Derived state | `ignored_at` | `missing_from_provider_at` | Meaning |
|
|
|---|---|---|---|
|
|
| `active` | null | null | Current policy is locally visible and currently observed in the provider-backed result set |
|
|
| `ignored_locally` | set | null | Current policy is intentionally hidden/suppressed locally |
|
|
| `provider_missing` | null | set | Current policy remains local truth but is not currently observed in the supported provider-backed result set |
|
|
| `ignored_locally_provider_missing` | set | set | Policy is locally suppressed and also currently missing from the provider-backed result set |
|
|
|
|
### Filter Membership And Precedence
|
|
|
|
- `active` filter returns only `active` rows.
|
|
- `ignored` filter returns `ignored_locally` and `ignored_locally_provider_missing` rows.
|
|
- `provider_missing` filter returns `provider_missing` and `ignored_locally_provider_missing` rows.
|
|
- `all` returns the complete set.
|
|
- For current backup/export, `provider_missing` wins as the primary `blocked_reason` when both timestamps are present because fresh provider-backed capture is impossible even if the row is also locally ignored.
|
|
|
|
### State Transition Rules
|
|
|
|
| Event | From | To | Notes |
|
|
|---|---|---|---|
|
|
| Local ignore | `active` or `provider_missing` | `ignored_locally` or `ignored_locally_provider_missing` | Explicit operator intent |
|
|
| Local restore/unignore | `ignored_locally` or `ignored_locally_provider_missing` | `active` or `provider_missing` | Clears only `ignored_at` |
|
|
| Sync marks missing | `active` or `ignored_locally` | `provider_missing` or `ignored_locally_provider_missing` | Sets only `missing_from_provider_at` |
|
|
| Sync reappears | `provider_missing` or `ignored_locally_provider_missing` | `active` or `ignored_locally` | Clears only `missing_from_provider_at` |
|
|
| Sync reclassifies to supported type | any provider-present state | provider-present state | Updates `policy_type` without using ignore semantics |
|
|
|
|
## Derived Projection: Current Backup Eligibility
|
|
|
|
**Persistence**: none; computed from `Policy`
|
|
|
|
| Field | Type | Meaning |
|
|
|---|---|---|
|
|
| `policy_id` | bigint | Policy being evaluated for current backup/export |
|
|
| `eligible` | boolean | True only when the policy is neither locally ignored nor provider-missing for current-state capture |
|
|
| `blocked_reason` | string nullable | `ignored_locally` or `provider_missing` when not eligible; `provider_missing` wins when both timestamps are present |
|
|
| `historical_continuity_available` | boolean | Indicates whether backup history already exists even if current capture is blocked |
|
|
|
|
### Rules
|
|
|
|
- Fresh provider-backed capture requires `ignored_at = null` and `missing_from_provider_at = null`.
|
|
- When both timestamps are set, current backup/export MUST return `blocked_reason = provider_missing` and MUST retain local ignore as secondary UI context.
|
|
- Historical backup existence does not make a provider-missing policy eligible for fresh capture.
|
|
|
|
## Derived Projection: Restore Continuity
|
|
|
|
**Persistence**: none; computed from `BackupItem` plus optional linked `Policy`
|
|
|
|
| Field | Type | Meaning |
|
|
|---|---|---|
|
|
| `backup_item_id` | bigint | Historical item being offered for restore |
|
|
| `policy_id` | bigint nullable | Linked current policy row when present |
|
|
| `selectable` | boolean | Restore eligibility of the historical item |
|
|
| `provider_missing_notice` | boolean | Shows that the current live policy is missing while the historical item remains valid |
|
|
| `continuity_message` | string nullable | Calm explanation shown in restore selection |
|
|
|
|
### Rules
|
|
|
|
- Historical restore selection continues to follow `BackupItem` truth.
|
|
- Provider-missing status on the live policy is descriptive unless an independent restore rule blocks the historical item.
|
|
|
|
## Audit Payload (existing infrastructure, new event meanings)
|
|
|
|
**Persistence**: existing `audit_logs`
|
|
|
|
| Field | Type | Meaning |
|
|
|---|---|---|
|
|
| `action_id` | string | `policy.provider_missing_detected` or `policy.provider_missing_cleared` or equivalent existing action ids if reused |
|
|
| `workspace_id` | bigint | Existing scope anchor |
|
|
| `tenant_id` | bigint | Existing scope anchor |
|
|
| `subject_type` | string | `policy` |
|
|
| `subject_id` | bigint | Policy id |
|
|
| `metadata.external_id` | string | Provider-facing stable key |
|
|
| `metadata.policy_type` | string | Canonical local policy type |
|
|
| `metadata.transition_at` | timestamp | When the presence transition was observed |
|
|
|
|
## Out of Scope Data Shapes
|
|
|
|
- No `provider_deleted_at`
|
|
- No lifecycle history table
|
|
- No materialized `policy_state` enum column
|
|
- No new recovery artifact or backup continuity table
|