## 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
253 lines
26 KiB
Markdown
253 lines
26 KiB
Markdown
# Implementation Plan: Provider-Missing Policy Visibility & Restore Continuity v1
|
|
|
|
**Branch**: `261-provider-missing-policy-visibility` | **Date**: 2026-05-01 | **Spec**: [spec.md](spec.md)
|
|
**Input**: Feature specification from [spec.md](spec.md)
|
|
|
|
## Summary
|
|
|
|
Prepare one bounded policy-truth correction that separates provider-missing presence from intentional local suppression without opening the broader workspace or tenant lifecycle program. The narrow implementation path is to add `missing_from_provider_at` on `policies`, reserve `ignored_at` for explicit user suppression only, stop sync and type-filter logic from writing `ignored_at` for provider absence, keep missing policies visible as historical local truth, block current-state backup/export actions for missing policies, and preserve restore continuity for historical `BackupItem` records.
|
|
|
|
Repo truth already exposes the exact seams this slice needs: [../../apps/platform/app/Models/Policy.php](../../apps/platform/app/Models/Policy.php) has `ignored_at` but no provider-presence field; [../../apps/platform/app/Services/Intune/PolicySyncService.php](../../apps/platform/app/Services/Intune/PolicySyncService.php) currently clears `ignored_at` on `updateOrCreate()` and marks reclassified or filtered records ignored; [../../apps/platform/app/Filament/Resources/PolicyResource.php](../../apps/platform/app/Filament/Resources/PolicyResource.php) only distinguishes active versus ignored; [../../apps/platform/app/Services/Intune/BackupService.php](../../apps/platform/app/Services/Intune/BackupService.php) selects backup candidates with `whereNull('ignored_at')`; [../../apps/platform/app/Filament/Resources/RestoreRunResource.php](../../apps/platform/app/Filament/Resources/RestoreRunResource.php) filters restore-option continuity through the same ignored check; and current tests such as [../../apps/platform/tests/Feature/Jobs/PolicySyncIgnoredRevivalTest.php](../../apps/platform/tests/Feature/Jobs/PolicySyncIgnoredRevivalTest.php) codify the conflated behavior that this spec intentionally corrects.
|
|
|
|
V1 therefore stays narrow: no new panel or provider, no global-search change, no new asset registration, no `SoftDeletes`, no lifecycle engine, no provider-deleted distinction, no purge flow, no multi-object rollout, and no cross-tenant workflow. The work is a policy-only truth correction that reuses the existing policy, backup, restore, audit, and sync seams.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4, Laravel 12
|
|
**Primary Dependencies**: Filament v5, Livewire v4, Pest v4, existing `PolicySyncService`, `BackupService`, `PolicyResource`, `RestoreRunResource`, policy bulk jobs, audit infrastructure, and policy badge helpers
|
|
**Storage**: PostgreSQL via existing `policies`, `policy_versions`, `backup_sets`, `backup_items`, `restore_runs`, `operation_runs`, and `audit_logs`; one nullable timestamp is planned on `policies`
|
|
**Testing**: Pest v4 feature and focused unit coverage
|
|
**Validation Lanes**: fast-feedback, confidence
|
|
**Target Platform**: Laravel monolith in `apps/platform`, existing admin and tenant-scoped Filament surfaces only
|
|
**Project Type**: Web application (Laravel monolith with Filament resources and pages)
|
|
**Performance Goals**: keep provider-missing detection inside existing sync result processing, avoid extra Graph calls, keep policy list and restore option queries eager-load safe, and avoid widening queue or asset cost
|
|
**Constraints**: no `SoftDeletes`, no `provider_deleted_at`, no new lifecycle registry/service, no panel/provider changes, no asset strategy change, no new globally searchable resource, and no customer-facing portal work
|
|
**Scale/Scope**: 1 model/migration, 1 sync service cluster, 1 policy resource, 1 backup service seam, 1 restore selection seam, bounded audit updates, and targeted policy/backup/restore tests
|
|
|
|
## Likely Affected Repo Surfaces
|
|
|
|
- [../../apps/platform/app/Models/Policy.php](../../apps/platform/app/Models/Policy.php) for `missing_from_provider_at`, casts, scopes, and derived provider-presence helpers.
|
|
- [../../apps/platform/database/migrations](../../apps/platform/database/migrations) for one migration adding `missing_from_provider_at` to `policies`.
|
|
- [../../apps/platform/app/Services/Intune/PolicySyncService.php](../../apps/platform/app/Services/Intune/PolicySyncService.php) for provider-missing detection, reappearance clearing, subtype filtering, and reclassification semantics.
|
|
- [../../apps/platform/app/Jobs/Operations/PolicyBulkDeleteWorkerJob.php](../../apps/platform/app/Jobs/Operations/PolicyBulkDeleteWorkerJob.php) and existing unignore flows as the retained local-suppression path that must stay on `ignored_at` only.
|
|
- [../../apps/platform/app/Filament/Resources/PolicyResource.php](../../apps/platform/app/Filament/Resources/PolicyResource.php) plus its `Pages/ListPolicies.php` and `Pages/ViewPolicy.php` surfaces for badges, filters, helper copy, and action availability.
|
|
- [../../apps/platform/app/Services/Intune/BackupService.php](../../apps/platform/app/Services/Intune/BackupService.php) and current backup/export actions that choose policies from current local truth.
|
|
- [../../apps/platform/app/Filament/Resources/BackupSetResource.php](../../apps/platform/app/Filament/Resources/BackupSetResource.php) or related picker helpers if the current backup-set policy picker needs provider-missing reason text.
|
|
- [../../apps/platform/app/Filament/Resources/RestoreRunResource.php](../../apps/platform/app/Filament/Resources/RestoreRunResource.php) for restore item option continuity and provider-missing messaging.
|
|
- [../../apps/platform/app/Support/Audit/AuditActionId.php](../../apps/platform/app/Support/Audit/AuditActionId.php) and the existing audit logger path for provider-missing and reappeared events if no existing audit action is sufficient.
|
|
- [../../apps/platform/tests/Feature/Jobs/PolicySyncIgnoredRevivalTest.php](../../apps/platform/tests/Feature/Jobs/PolicySyncIgnoredRevivalTest.php), [../../apps/platform/tests/Feature/PolicySyncServiceTest.php](../../apps/platform/tests/Feature/PolicySyncServiceTest.php), [../../apps/platform/tests/Feature/BulkDeletePoliciesTest.php](../../apps/platform/tests/Feature/BulkDeletePoliciesTest.php), [../../apps/platform/tests/Feature/BulkUnignorePoliciesTest.php](../../apps/platform/tests/Feature/BulkUnignorePoliciesTest.php), [../../apps/platform/tests/Feature/BulkExportToBackupTest.php](../../apps/platform/tests/Feature/BulkExportToBackupTest.php), [../../apps/platform/tests/Feature/Filament/BackupCreationTest.php](../../apps/platform/tests/Feature/Filament/BackupCreationTest.php), [../../apps/platform/tests/Feature/Filament/BackupSetPolicyPickerTableTest.php](../../apps/platform/tests/Feature/Filament/BackupSetPolicyPickerTableTest.php), [../../apps/platform/tests/Feature/Filament/RestoreItemSelectionTest.php](../../apps/platform/tests/Feature/Filament/RestoreItemSelectionTest.php), [../../apps/platform/tests/Feature/PolicySyncStartSurfaceTest.php](../../apps/platform/tests/Feature/PolicySyncStartSurfaceTest.php), and [../../apps/platform/tests/Unit/Badges/PolicyBadgesTest.php](../../apps/platform/tests/Unit/Badges/PolicyBadgesTest.php) for bounded regression proof.
|
|
|
|
## Policy Presence / Sync Fit
|
|
|
|
- Treat provider presence as a property of the local policy row, not a second entity or lifecycle engine. The current narrow truth is `ignored_at` plus `missing_from_provider_at`.
|
|
- The derived state family stays bounded:
|
|
- active: `ignored_at = null` and `missing_from_provider_at = null`
|
|
- ignored locally: `ignored_at != null` and `missing_from_provider_at = null`
|
|
- provider missing: `ignored_at = null` and `missing_from_provider_at != null`
|
|
- ignored locally + provider missing: both timestamps present, with local suppression as the primary local-control meaning
|
|
- Policy list filtering stays deterministic: the combined state belongs to both the `ignored` and `provider_missing` filter views so operators can reach it from either workflow.
|
|
- [../../apps/platform/app/Services/Intune/PolicySyncService.php](../../apps/platform/app/Services/Intune/PolicySyncService.php) must stop blindly resetting `ignored_at` on `updateOrCreate()`.
|
|
- Any sync path that currently uses `ignored_at` for subtype filtering or reclassification must instead either reclassify the row to a supported canonical type or mark `missing_from_provider_at` when the object falls out of the supported provider-backed result set.
|
|
- Reappearance must clear `missing_from_provider_at` without automatically clearing `ignored_at`.
|
|
- Existing local delete/restore semantics in `ignore()` and `unignore()` remain unchanged and stay rooted in `ignored_at`.
|
|
|
|
## Backup / Restore Continuity Fit
|
|
|
|
- [../../apps/platform/app/Services/Intune/BackupService.php](../../apps/platform/app/Services/Intune/BackupService.php) and any current backup/export picker that selects policies for fresh capture must keep provider-missing policies visible but blocked from current-state capture.
|
|
- When both `ignored_at` and `missing_from_provider_at` are present, current backup/export uses `provider_missing` as the primary blocked reason because the product cannot truthfully claim fresh provider-backed capture is possible.
|
|
- Existing historical `BackupItem` truth remains authoritative for restore continuity. The system should not require a live provider-present policy row to keep a historical restore item selectable.
|
|
- [../../apps/platform/app/Filament/Resources/RestoreRunResource.php](../../apps/platform/app/Filament/Resources/RestoreRunResource.php) should keep continuity messaging secondary and truthful: the backup item is historical and selectable, while the live policy is currently provider-missing.
|
|
- Current-state backup blocking should prefer calm explanatory copy and zero new run creation. Historical restore remains a separate allowed path.
|
|
|
|
## UI / Filament & Livewire Fit
|
|
|
|
- Filament remains v5 on Livewire v4. No new panel or provider is planned, and provider registration remains in [../../apps/platform/bootstrap/providers.php](../../apps/platform/bootstrap/providers.php).
|
|
- [../../apps/platform/app/Filament/Resources/PolicyResource.php](../../apps/platform/app/Filament/Resources/PolicyResource.php) already exposes a list and a view page and explicitly sets `$isGloballySearchable = false`; this feature does not change global-search posture.
|
|
- Keep the existing policy resource as the primary current-state decision surface. Do not add a new ghost-policy page, special diagnostics page, or alternate provider-missing registry.
|
|
- Any destructive action that survives this slice remains the existing local ignore/restore family and must continue to use confirmation and server-side authorization. This feature does not introduce a new destructive action.
|
|
- Asset strategy remains unchanged. No new Filament assets or deployment `filament:assets` steps are expected beyond the existing platform process.
|
|
- If status badges or helper copy expand, reuse the existing badge and policy presentation seam instead of adding local ad-hoc mappings.
|
|
|
|
## RBAC / Authorization Fit
|
|
|
|
- Workspace membership and tenant entitlement remain the first boundary. Nothing in provider-missing semantics changes `404` versus `403` scope handling.
|
|
- Existing policy, backup, and restore capabilities remain authoritative. No new capability string or role check is justified.
|
|
- Backup blocking for provider-missing policies is business-state gating, not authorization. In-scope operators still see the policy and the historical restore path; out-of-scope actors still receive deny-as-not-found.
|
|
- Any reused detail or action route must keep current server-side authorization checks and confirmation behavior.
|
|
|
|
## Audit / Logging Fit
|
|
|
|
- Reuse the existing audit infrastructure; do not create a second lifecycle-log family.
|
|
- The narrow expectation is one explicit audit event when a previously observed policy becomes provider-missing and one when it later reappears.
|
|
- Audit payload should stay tenant-safe and minimal: policy id, policy external id, canonical policy type, transition timestamp, and sync source context are sufficient.
|
|
- No new `OperationRun` type is planned. Current sync actions may continue to use the existing run UX only when a sync actually starts.
|
|
|
|
## Data & Query Fit
|
|
|
|
- Add one nullable timestamp column: `policies.missing_from_provider_at`.
|
|
- Keep `workspace_id` and `tenant_id` as required anchors on policy truth; this slice does not weaken tenant ownership.
|
|
- Prefer deriving provider-presence state via model helpers or narrow query scopes instead of storing a second status enum.
|
|
- Update current policy-eligibility queries to use both `ignored_at` and `missing_from_provider_at` where current-state capture must stay live-only.
|
|
- Combined-state filter membership is derived, not stored: ignored views include `ignored_locally_provider_missing`, provider-missing views include `ignored_locally_provider_missing`, and current backup/export blocked-reason precedence resolves to `provider_missing` when both timestamps are present.
|
|
- Keep restore queries historical-first: if a `BackupItem` remains eligible, provider-missing context is descriptive rather than disqualifying unless a separate restore rule already blocks it.
|
|
- Avoid adding speculative `provider_state_reason` or lifecycle-history tables in v1.
|
|
|
|
## UI / Surface Guardrail Plan
|
|
|
|
- **Guardrail scope**: changed surfaces
|
|
- **Native vs custom classification summary**: native Filament
|
|
- **Shared-family relevance**: status messaging, badges, row/detail actions, backup eligibility messaging, restore continuity descriptions, and audit labels
|
|
- **State layers in scope**: page, detail, action modal, picker description, query/model state
|
|
- **Audience modes in scope**: operator-MSP, support-platform
|
|
- **Decision/diagnostic/raw hierarchy plan**: decision-first current policy meaning, diagnostics-second sync/history context, raw/support third and only where existing surfaces already expose it
|
|
- **Raw/support gating plan**: no new raw payload disclosure; any provider detail remains secondary on existing policy/version evidence surfaces only
|
|
- **One-primary-action / duplicate-truth control**: policy list/detail own current-state meaning; backup flows own current capture eligibility; restore flows own historical continuity; no surface should restate another surface's primary truth as an equal-priority summary
|
|
- **Handling modes by drift class or surface**: review-mandatory
|
|
- **Repository-signal treatment**: review-mandatory because this slice changes a shared state vocabulary across policy, backup, and restore seams
|
|
- **Special surface test profiles**: standard-native-filament, shared-detail-family
|
|
- **Required tests or manual smoke**: functional-core, state-contract
|
|
- **Exception path and spread control**: none planned; any attempt to add a new provider-missing page, new lifecycle registry, or new panel becomes exception-required drift
|
|
- **Active feature PR close-out entry**: Guardrail / State Vocabulary
|
|
|
|
## Shared Pattern & System Fit
|
|
|
|
- **Cross-cutting feature marker**: yes
|
|
- **Systems touched**: `Policy`, `PolicySyncService`, `PolicyResource`, `BackupService`, `BackupSetResource` picker helpers if needed, `RestoreRunResource`, audit action ids, and the policy badge/presentation seam
|
|
- **Shared abstractions reused**: existing policy model truth, shared policy filters/actions, existing backup selection path, existing restore-item option builders, existing audit logger, and existing badge rendering tests
|
|
- **New abstraction introduced? why?**: none planned. If implementation needs a tiny provider-presence helper, keep it on `Policy` or an existing policy-presenter seam instead of creating a new lifecycle framework.
|
|
- **Why the existing abstraction was sufficient or insufficient**: current shared seams already own the right surfaces. They are simply fed by the wrong state vocabulary today.
|
|
- **Bounded deviation / spread control**: none planned. Any proposal for a separate ghost-policy registry, dedicated diagnostics page, or generic managed-object lifecycle layer should be deferred to the broader lifecycle taxonomy follow-up.
|
|
|
|
## OperationRun UX Impact
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: yes, by narrowing when current backup/export actions may start and by leaving sync-retry behavior on the existing shared path
|
|
- **Central contract reused**: existing policy sync and backup/export start UX
|
|
- **Delegated UX behaviors**: allowed sync actions keep the current queued toast/link behavior; blocked provider-missing current backup/export actions stop before run creation and explain why locally
|
|
- **Surface-owned behavior kept local**: policy, backup, and restore surfaces own provider-missing messaging only; they do not create a new run type or monitoring surface
|
|
- **Queued DB-notification policy**: unchanged
|
|
- **Terminal notification path**: unchanged
|
|
- **Exception path**: none
|
|
|
|
## Provider Boundary & Portability Fit
|
|
|
|
- **Shared provider/platform boundary touched?**: yes
|
|
- **Provider-owned seams**: Microsoft Graph list results, subtype inclusion/exclusion, and canonical-type mapping remain provider-owned evidence in sync
|
|
- **Platform-core seams**: local suppression truth, provider-missing truth, current backup eligibility, historical restore continuity, and operator-facing policy vocabulary
|
|
- **Neutral platform terms / contracts preserved**: `provider missing`, `ignored locally`, `current backup eligibility`, and `restore continuity`
|
|
- **Retained provider-specific semantics and why**: subtype filtering remains provider-specific inside sync because it already reflects supported endpoint scope
|
|
- **Bounded extraction or follow-up path**: `document-in-feature` for the narrow provider-presence wording now; `follow-up-spec` later if the broader lifecycle taxonomy or explicit provider-deleted distinction is approved
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- Inventory-first / snapshot truth: PASS. The slice keeps historical `PolicyVersion` and `BackupItem` truth authoritative and does not invent a new shadow registry.
|
|
- Read/write separation: PASS. No new destructive flow, purge path, or customer-facing mutation surface is introduced.
|
|
- Graph contract path: PASS. The slice reuses the existing provider sync boundary and adds no new Graph family.
|
|
- Deterministic capabilities: PASS. Existing capability registries remain canonical.
|
|
- Workspace and tenant isolation: PASS. `workspace_id` and `tenant_id` remain required anchors on policy truth.
|
|
- RBAC-UX plane separation: PASS. The slice stays inside existing admin and tenant-scoped policy, backup, and restore surfaces.
|
|
- Destructive confirmation standard: PASS. Existing local ignore/restore destructive actions remain the only destructive family and keep confirmation plus server-side authorization.
|
|
- Global search safety: PASS. `PolicyResource` is already globally disabled and remains so.
|
|
- OperationRun / Ops-UX: PASS. No new run type is introduced; blocked backup/export states stop before run creation.
|
|
- Data minimization: PASS. Provider-missing copy stays calm and decision-first; no new raw payload exposure is added.
|
|
- Test governance (TEST-GOV-001): PASS. Planned proof stays inside focused feature and unit lanes.
|
|
- Proportionality / no premature abstraction: PASS. One timestamp and derived state family are the narrowest defensible shape.
|
|
- Persisted truth (PERSIST-001): PASS. One field is added to existing policy truth; no new table or artifact family is created.
|
|
- Behavioral state (STATE-001): PASS. The state family is derived from existing and new timestamps rather than adding a new enum/persistence layer.
|
|
- Provider boundary (PROV-001): PASS. Provider-specific result interpretation stays in sync; operator-facing language stays platform-neutral.
|
|
- Filament / Laravel planning contract: PASS. Filament remains v5 on Livewire v4, provider registration remains in [../../apps/platform/bootstrap/providers.php](../../apps/platform/bootstrap/providers.php), no panel change is planned, `PolicyResource` global search remains disabled, and no assets are expected.
|
|
|
|
**Gate evaluation**: PASS.
|
|
|
|
- The narrow path is defensible if implementation keeps `ignored_at` user-owned and `missing_from_provider_at` provider-observation-only.
|
|
- The plan fails the gate if it drifts into `SoftDeletes`, a new lifecycle service, a provider-deleted taxonomy, or multi-object rollout.
|
|
|
|
**Post-design re-check**: PASS. [research.md](research.md), [data-model.md](data-model.md), [quickstart.md](quickstart.md), and [contracts/provider-missing-policy-visibility.openapi.yaml](contracts/provider-missing-policy-visibility.openapi.yaml) are present and aligned with the spec package.
|
|
|
|
## Test Governance Check
|
|
|
|
- **Test purpose / classification by changed surface**: Feature for sync behavior, policy UI, backup eligibility, restore continuity, and authorization continuity; Unit for policy badge/state or narrow derived helper behavior if one is introduced
|
|
- **Affected validation lanes**: fast-feedback, confidence
|
|
- **Why this lane mix is the narrowest sufficient proof**: the behavior is server-side and already anchored in existing sync, policy, backup, and restore tests. Focused feature coverage plus one small unit seam is enough without browser cost.
|
|
- **Narrowest proving command(s)**:
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Jobs/PolicySyncProviderMissingSemanticsTest.php tests/Feature/Jobs/PolicySyncIgnoredRevivalTest.php tests/Feature/Jobs/AppProtectionPolicySyncFilteringTest.php tests/Feature/PolicySyncServiceTest.php tests/Feature/PolicySyncEnrollmentConfigurationTypeCollisionTest.php tests/Feature/PolicySyncStartSurfaceTest.php`
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/PolicyProviderMissingUiTest.php tests/Feature/PolicyGeneralViewTest.php tests/Feature/BulkDeletePoliciesTest.php tests/Feature/BulkUnignorePoliciesTest.php tests/Unit/Badges/PolicyBadgesTest.php`
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/BulkExportToBackupTest.php tests/Feature/Filament/BackupCreationTest.php tests/Feature/Filament/BackupSetPolicyPickerTableTest.php tests/Feature/Filament/RestoreItemSelectionTest.php tests/Feature/RestoreUnknownPolicyTypeSafetyTest.php`
|
|
- **Fixture / helper / factory / seed / context cost risks**: low to moderate; reuse existing tenant, policy, backup-set, backup-item, restore-run, and policy-version fixtures
|
|
- **Expensive defaults or shared helper growth introduced?**: none expected; any new helper should stay policy-local and explicit
|
|
- **Heavy-family additions, promotions, or visibility changes**: none
|
|
- **Surface-class relief / special coverage rule**: standard-native-filament relief for policy and backup surfaces; shared-detail-family proof for restore continuity
|
|
- **Closing validation and reviewer handoff**: rerun the commands above, verify sync never writes `ignored_at` for provider absence, verify missing policies remain visible, verify current backup/export blocks without starting a run, verify historical restore continuity remains selectable, and verify non-members or out-of-scope actors still resolve as `404`
|
|
- **Budget / baseline / trend follow-up**: none expected beyond small feature-local test additions
|
|
- **Review-stop questions**: hidden `ignored_at` writes left in sync, blocked backup/export starting a run anyway, restore continuity silently filtered, or provider-specific wording leaking into platform truth
|
|
- **Escalation path**: `document-in-feature` for bounded wording or audit metadata notes; `follow-up-spec` for broader lifecycle rollout; `reject-or-split` for SoftDeletes or new framework drift
|
|
- **Active feature PR close-out entry**: Guardrail / State Vocabulary
|
|
|
|
## Rollout & Risk Controls
|
|
|
|
- Keep policy truth anchored to the existing `policies` row; do not create a side table or ghost-policy registry.
|
|
- Keep local ignore and provider-missing semantics orthogonal. Sync may clear provider-missing on reappearance, but only an explicit user action clears local ignore.
|
|
- Keep current-state capture conservative. If the provider cannot currently supply live state for a policy, current backup/export should explain that and stop.
|
|
- Keep historical restore continuity permissive where `BackupItem` truth already exists.
|
|
- Keep global search posture, panel registration, and asset strategy unchanged.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/261-provider-missing-policy-visibility/
|
|
├── checklists/
|
|
│ └── requirements.md
|
|
├── plan.md
|
|
├── research.md
|
|
├── data-model.md
|
|
├── quickstart.md
|
|
├── contracts/
|
|
│ └── provider-missing-policy-visibility.openapi.yaml
|
|
└── tasks.md
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
|
|
```text
|
|
apps/platform/
|
|
├── app/
|
|
│ ├── Filament/
|
|
│ │ └── Resources/
|
|
│ │ ├── BackupSetResource.php
|
|
│ │ ├── PolicyResource.php
|
|
│ │ └── RestoreRunResource.php
|
|
│ ├── Jobs/
|
|
│ │ └── Operations/PolicyBulkDeleteWorkerJob.php
|
|
│ ├── Models/
|
|
│ │ └── Policy.php
|
|
│ ├── Services/
|
|
│ │ ├── Audit/
|
|
│ │ └── Intune/
|
|
│ │ ├── BackupService.php
|
|
│ │ └── PolicySyncService.php
|
|
│ └── Support/
|
|
│ ├── Audit/AuditActionId.php
|
|
│ └── Ui/
|
|
├── database/
|
|
│ └── migrations/
|
|
└── tests/
|
|
├── Feature/
|
|
│ ├── Filament/
|
|
│ ├── Jobs/
|
|
│ └── ...
|
|
└── Unit/
|
|
└── Badges/
|
|
```
|
|
|
|
## Complexity Tracking
|
|
|
|
- **New persistence**: 1 nullable timestamp on `policies`
|
|
- **New derived state family**: 1 provider-presence family layered onto the existing local ignore semantics
|
|
- **New routes/pages/panels/providers**: none
|
|
- **New assets**: none
|
|
- **New queues or long-running operations**: none
|
|
- **Expected implementation risk**: moderate, because multiple existing tests currently encode the conflated semantics and must be corrected together
|