# Research: Provider-Missing Policy Visibility & Restore Continuity v1

## Decision 1: Persist provider-missing observation on `policies` via `missing_from_provider_at`

- **Decision**: Add one nullable timestamp, `missing_from_provider_at`, to the existing `policies` table.
- **Rationale**: The current ambiguity is persisted on the policy row itself. The narrowest truthful fix is therefore another field on that same row, not a second table or lifecycle engine.
- **Why this is enough**: The slice only needs to know whether a previously observed policy is currently absent from the supported provider-backed result set. A timestamp captures that fact while preserving historical local truth.
- **Alternatives considered**:
  - Reuse `ignored_at`: rejected because it keeps local suppression and provider absence conflated.
  - Add `SoftDeletes`: rejected because provider absence is not local deletion.
  - Add a lifecycle history table: rejected as premature for this policy-only follow-up.

## Decision 2: Keep `ignored_at` user-owned and stop sync from reviving ignored policies

- **Decision**: Treat `ignored_at` as explicit local suppression only. Sync may clear `missing_from_provider_at` on reappearance, but it must not clear `ignored_at` automatically.
- **Rationale**: Existing bulk delete and restore flows already use `ignore()` and `unignore()` as user intent. A later provider sync should not silently reverse that operator choice.
- **Repo anchor**: [../../apps/platform/app/Jobs/Operations/PolicyBulkDeleteWorkerJob.php](../../apps/platform/app/Jobs/Operations/PolicyBulkDeleteWorkerJob.php) currently treats ignore as local deletion, so sync revival is the inconsistent behavior, not the bulk action.
- **Alternatives considered**:
  - Preserve `PolicySyncIgnoredRevivalTest` semantics: rejected because it keeps local suppression non-deterministic.
  - Auto-unignore when a provider object reappears: rejected because it makes provider observation override explicit local intent.

## Decision 3: Current backup/export requires provider-present policy truth, but historical restore continuity remains available

- **Decision**: Current backup/export flows keep provider-missing policies visible but blocked, while restore creation continues to offer historical `BackupItem` continuity when otherwise eligible.
- **Rationale**: A fresh snapshot depends on current provider-backed state. Historical restore depends on already captured backup truth. Those are different claims and should not share one eligibility gate.
- **Repo anchor**: [../../apps/platform/app/Services/Intune/BackupService.php](../../apps/platform/app/Services/Intune/BackupService.php) currently only checks `ignored_at`; [../../apps/platform/app/Filament/Resources/RestoreRunResource.php](../../apps/platform/app/Filament/Resources/RestoreRunResource.php) already has a historical-item grouping seam that can carry provider-missing continuity messaging.
- **Alternatives considered**:
  - Keep current backup/export behavior for missing policies: rejected because it pretends a fresh provider-backed snapshot is still possible.
  - Hide missing policies from restore flows too: rejected because it throws away existing backup value and breaks recovery continuity.

## Decision 4: Audit provider-missing and reappeared transitions through the existing audit path

- **Decision**: Reuse the current audit infrastructure for two explicit transition events: provider-missing detected and provider-missing cleared.
- **Rationale**: Reviewers need to understand why a policy is missing now and when it returned later. The current audit path is already the bounded place for that truth.
- **Why no new `OperationRun`**: The transition is a side effect of existing sync work, not a new long-running workflow or operator-owned run family.
- **Alternatives considered**:
  - No audit changes: rejected because the state would be hard to explain later.
  - New lifecycle event subsystem: rejected as broader than the slice needs.

## Decision 5: Keep the existing policy resource as the primary current-state surface

- **Decision**: Extend the existing `PolicyResource` list/view surfaces instead of adding a ghost-policy page or lifecycle registry.
- **Rationale**: Operators already decide what a policy means from the current policy surface. Adding another page would duplicate vocabulary and spread the bug fix across more UI.
- **Repo anchor**: [../../apps/platform/app/Filament/Resources/PolicyResource.php](../../apps/platform/app/Filament/Resources/PolicyResource.php) already owns list/view routes, action availability, and visibility filters.
- **Alternatives considered**:
  - New diagnostics page for provider-missing policies: rejected as unnecessary surface expansion.
  - Backup or restore pages owning current-state explanation: rejected because those are secondary flows.

## Open Follow-Up Questions Deferred Intentionally

- Distinguishing `provider_deleted_at` from generic provider absence remains a later follow-up.
- Rolling provider-presence semantics out to other managed objects remains a later follow-up.
- Full lifecycle taxonomy and retention or purge policy remain blocked on the broader lifecycle spec.
