--- description: "Task list for Spec 119 implementation" --- # Tasks: Drift Golden Master Cutover (Baseline Compare) (Spec 119) **Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` **Prerequisites**: - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/plan.md` (required) - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/spec.md` (required) - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/research.md` - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/data-model.md` - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/contracts/drift.openapi.yaml` - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/quickstart.md` **Tests**: REQUIRED (Pest) because this feature changes runtime behavior. **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story. --- ## Phase 1: Setup (Shared Infrastructure) **Purpose**: Local readiness + baseline validation before changing runtime behavior - [X] T001 Start local stack and run migrations using `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (up -d, artisan migrate) - [X] T002 Run a baseline test subset using `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` to confirm a green starting point (artisan test --compact --filter=BaselineCompare) --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Shared building blocks used across user stories (evidence contract + resolvers) **⚠️ CRITICAL**: Complete this phase before starting any user story work. - [X] T003 Create baseline policy-version resolver in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Baselines/Evidence/BaselinePolicyVersionResolver.php` (resolve baseline `policy_version_id` deterministically from baseline snapshot item identity `policy_type` + `subject_key` and baseline evidence provenance `observed_at`; return null when no match) - [X] T004 [P] Add resolver unit tests in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Unit/Baselines/BaselinePolicyVersionResolverTest.php` (covers: found, not found, invalid observed_at, deterministic tie-breaker when multiple candidates exist) - [X] T005 [P] Add drift evidence contract assertion helper in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Support/AssertsDriftEvidenceContract.php` (required keys, allowed `summary.kind`, provenance keys, fidelity algorithm, and diff-renderability rule) **Checkpoint**: Contract helper + resolver exist; US1 tests can be written against them. --- ## Phase 3: User Story 1 — Understand drift with consistent diffs (Priority: P1) 🎯 MVP **Goal**: Baseline Compare drift findings carry diff-compatible evidence so operators get consistent diffs when content evidence exists, and a clear “diff unavailable” explanation when it does not. **Independent Test**: Run Baseline Compare to produce (a) a `different_version` drift finding with both refs, (b) a `missing_policy` or `unexpected_policy` finding with a single required ref, and (c) a meta-only finding, then verify the finding detail view renders the correct diff or an explicit “diff unavailable” explanation. ### Tests for User Story 1 (write first) ⚠️ - [X] T006 [P] [US1] Add Baseline Compare evidence-contract tests in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Baselines/BaselineCompareDriftEvidenceContractTest.php` (different_version + missing_policy; asserts `source`, `change_type` semantics unchanged, `summary.kind`, baseline/current `policy_version_id`, fidelity algorithm, and `evidence_jsonb.provenance.*` keys including `compare_operation_run_id`) - [X] T007 [P] [US1] Add FindingResource “diff unavailable” regression test in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Drift/DriftFindingDiffUnavailableTest.php` (missing baseline/current refs → explicit message) - [X] T008 [P] [US1] Update Baseline Compare findings tests for new evidence shape in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Baselines/BaselineCompareFindingsTest.php` (replace `current_hash`/`baseline_hash` assertions with nested baseline/current evidence + `summary.kind`) ### Implementation for User Story 1 - [X] T009 [US1] Upgrade Baseline Compare drift evidence schema in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (write `evidence_jsonb.summary.kind`, `evidence_jsonb.baseline.policy_version_id`, `evidence_jsonb.current.policy_version_id`, compute `evidence_jsonb.fidelity`, and write `evidence_jsonb.provenance.{baseline_profile_id,baseline_snapshot_id,compare_operation_run_id,inventory_sync_run_id}`) - [X] T010 [US1] Populate `evidence_jsonb.current.policy_version_id` from content evidence meta in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (use `ResolvedEvidence.meta.policy_version_id` when available; else null) - [X] T011 [US1] Populate `evidence_jsonb.baseline.policy_version_id` via `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Baselines/Evidence/BaselinePolicyVersionResolver.php` and integrate into `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (when baseline snapshot provenance indicates content evidence + has `observed_at`, attempt resolve; otherwise null) - [X] T012 [US1] Implement `summary.kind` selection in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` by “stealing” dimension detection logic from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Drift/DriftFindingGenerator.php` (prefer settings snapshot changes; else assignments; else scope tags; fallback policy_snapshot) - [X] T013 [US1] Ensure `findings.evidence_fidelity` and `evidence_jsonb.fidelity` stay aligned in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (compute deterministically from policy-version refs: both present = `content`, exactly one = `mixed`, none = `meta`) - [X] T014 [US1] Enforce diff-renderability rule in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Resources/FindingResource.php` (`different_version` requires both refs; `missing_policy`/`unexpected_policy` render against an empty side when their single required ref exists; otherwise show explicit “diff unavailable” explanation in the Diff section) - [X] T015 [US1] Run US1-focused tests via `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (artisan test --compact --filter=BaselineCompareDriftEvidenceContract; artisan test --compact --filter=DriftFindingDiffUnavailable) **Checkpoint**: Baseline Compare findings show consistent diffs for content evidence; meta-only shows “diff unavailable” without errors. --- ## Phase 4: User Story 2 — Eliminate “two truths” for drift (Priority: P2) **Goal**: Operators only see Baseline Compare as the drift engine; no legacy “Generate drift” UI or source switching remains. **Independent Test**: After the cutover, verify all drift findings shown in the UI are Baseline Compare-origin and no UI surface offers a legacy “Generate drift” action or “source” switching. ### Tests for User Story 2 (write first) ⚠️ - [X] T016 [P] [US2] Remove/replace DriftLanding enforcement tests in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Rbac/DriftLandingUiEnforcementTest.php` (assert drift entry point is Baseline Compare landing instead) ### Implementation for User Story 2 - [X] T017 [US2] Remove legacy Drift landing surface by deleting `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Pages/DriftLanding.php` and `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/resources/views/filament/pages/drift-landing.blade.php` - [X] T018 [US2] Update related-run links to point drift to Baseline Compare landing in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/OperationRunLinks.php` (remove DriftLanding import; add BaselineCompareLanding link for baseline_compare runs) - [X] T019 [US2] Update dashboard attention widget to use Baseline Compare runs (not drift_generate) in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Widgets/Dashboard/NeedsAttention.php` (stale/failed checks + URLs → Baseline Compare landing / Operations) - [X] T020 [US2] Remove DriftLanding action-surface exemption in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php` - [X] T021 [US2] Update Filament auth allowlist to remove DriftLanding in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php` - [X] T022 [US2] Run US2-focused tests via `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (artisan test --compact --filter=BaselineCompareLanding; artisan test --compact --filter=NeedsAttention) **Checkpoint**: No DriftLanding surface exists; “Drift” entry point routes to Baseline Compare landing. --- ## Phase 5: User Story 3 — Clean cutover & legacy removal (Priority: P3) **Goal**: Hard cut: legacy run-to-run drift generator is removed end-to-end and legacy drift findings are deleted so operators never encounter mixed states. **Independent Test**: After deployment and the one-time cleanup step, verify legacy drift findings no longer exist, legacy drift generation cannot be started, and Baseline Compare drift continues to work. ### Tests for User Story 3 (write first) ⚠️ - [X] T023 [P] [US3] Add cleanup migration test in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Drift/LegacyDriftFindingsCleanupMigrationTest.php` (deletes `finding_type=drift` where `source` is null or != baseline.compare; keeps baseline.compare) - [X] T024 [P] [US3] Update alert + monitoring tests to remove drift_generate_findings references in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Alerts/BaselineCompareFailedAlertTest.php` and `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/RunAuthorizationTenantIsolationTest.php` - [X] T025 [P] [US3] Remove legacy drift generator tests tied to DriftLanding/GenerateDriftFindingsJob/DriftFindingGenerator in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Drift/` (delete or rewrite: DriftGenerationDispatchTest.php, DriftLandingCopyTest.php, DriftLandingShowsComparisonInfoTest.php, GenerateDriftFindingsJobNotificationTest.php, Drift*DriftDetectionTest.php, DriftGenerationDeterminismTest.php, DriftTenantIsolationTest.php) ### Implementation for User Story 3 - [X] T026 [US3] Delete legacy drift generation runtime code: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/GenerateDriftFindingsJob.php`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Drift/DriftFindingGenerator.php`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Drift/DriftRunSelector.php`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Drift/DriftScopeKey.php` - [X] T027 [US3] Remove legacy drift operation type from catalogs + triage: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/OperationRunType.php`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/OperationCatalog.php`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/SystemConsole/OperationRunTriageService.php` (no drift_generate_findings label/duration/retry/cancel) - [X] T028 [US3] Remove legacy drift-generate alert event producer in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/Alerts/EvaluateAlertsJob.php` (drop compareFailedEvents() and its call site) - [X] T029 [US3] Add one-time cleanup migration in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/database/migrations/2026_03_05_000001_delete_legacy_drift_findings.php` (delete `finding_type=drift` where `source` is null or <> baseline.compare) - [X] T030 [US3] Remove remaining legacy drift references discovered by search in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/` and `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/resources/` (target: no DriftLanding / drift_generate_findings strings outside historical migrations/specs; explicitly audit Findings UI for legacy-source badges/filters/labels or “source switching” states) - [X] T031 [US3] Run US3-focused tests via `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (artisan test --compact --filter=Alerts; artisan test --compact --filter=OperationRun; artisan test --compact --filter=Drift) **Checkpoint**: Legacy generator is gone; DB cleanup migration removes legacy drift findings; all tests green. --- ## Phase N: Polish & Cross-Cutting Concerns **Purpose**: Final quality pass across all user stories - [X] T032 [P] Format changed files using `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (php vendor/bin/pint) - [X] T033 Run full test suite using `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail` (artisan test) - [X] T034 [P] Validate manual smoke steps remain accurate in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/quickstart.md` (update only if behavior/screens changed) - [X] T035 [P] Update Spec 119 docs for one-sided drift diff rendering in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` (spec, plan, research, data-model, quickstart) - [X] T036 [US1] Extend `FindingResource` one-sided diff rendering in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Resources/FindingResource.php` (`unexpected_policy` => added against empty baseline, `missing_policy` => removed against empty current) - [X] T037 [P] Extend drift view regressions in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Drift/DriftFindingDiffUnavailableTest.php` (cover one-sided empty-side rendering and explicit unavailable messaging) - [X] T038 [US1] Scope baseline snapshot capture to the latest completed Inventory Sync in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CaptureBaselineSnapshotJob.php` (ignore stale `inventory_items` rows from older sync runs when deriving subject-key matches; record `baseline_capture.inventory_sync_run_id` for operability) - [X] T039 [P] Add baseline-capture stale-inventory regression coverage in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Baselines/BaselineCaptureAmbiguousMatchGapTest.php` (duplicates in the same latest sync still gap; stale duplicates from older syncs no longer do) - [X] T040 [P] Update Spec 119 docs for baseline-capture latest-sync scoping in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` (spec, plan, research, data-model, quickstart) - [X] T041 [US1] Reuse same-run full-content capture evidence in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (overlay compare-time captured/reused `policy_versions` before the `since`-based resolver so unchanged policies do not become `missing_current`) - [X] T042 [P] Add full-content compare reuse regression in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Baselines/BaselineCompareWhyNoFindingsReasonCodeTest.php` (reused identical compare-purpose version still yields `no_drift_detected`, not `evidence_capture_incomplete`) - [X] T043 [P] Update Spec 119 docs for reused compare-evidence handling in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` (spec, plan, research, quickstart) - [X] T044 [US1] Scope Baseline Compare landing duplicate-name warnings to the latest completed Inventory Sync in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/Baselines/BaselineCompareStats.php` (historical stale duplicates must not keep the warning banner visible) - [X] T045 [P] Add landing-page stale-duplicate regression in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Filament/BaselineCompareLandingDuplicateNamesBannerTest.php` (latest sync clean ⇒ no warning, latest sync duplicate ⇒ warning remains) - [X] T046 [P] Update Spec 119 docs for landing-page duplicate warning scoping in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` (spec, research) - [X] T047 [US1] Canonicalize compliance `scheduledActionsForRule` in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Services/Intune/CompliancePolicyNormalizer.php` (include semantic noncompliance action fields in the drift signal; ignore opaque IDs/order-only noise) - [X] T048 [P] Add unit coverage for canonical compliance action drift normalization in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Unit/CompliancePolicyNormalizerTest.php` (grace-period/template signal, stable ordering, ignored internal IDs) - [X] T049 [US1] Recompute effective baseline content hashes from resolved baseline `policy_versions` in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php` (keep existing content-backed baseline snapshots comparable when drift-signal semantics expand) - [X] T050 [P] Add compare regressions + docs for compliance noncompliance action drift in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/BaselineDriftEngine/ComplianceNoncomplianceActionsDriftTest.php` and `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/` (unchanged legacy-hash snapshot stays quiet; changed grace/action semantics create drift) --- ## Dependencies & Execution Order ### Phase Dependencies - **Setup (Phase 1)**: No dependencies - can start immediately - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories - **User Stories (Phase 3+)**: All depend on Foundational phase completion - Stories can proceed in priority order (P1 → P2 → P3) - Or in parallel after Phase 2 if staffed (be mindful of overlapping files) - **Polish (Final Phase)**: Depends on all desired user stories being complete ### User Story Dependencies - **US1 (P1)**: No dependencies after Phase 2; delivers the MVP (diff-compatible evidence + diff guardrails) - **US2 (P2)**: Depends on US1 being stable in UI terms (so removing DriftLanding doesn’t remove drift visibility) - **US3 (P3)**: Can start after Phase 2, but recommended after US1/US2 so the cutover is clean and operators still have a drift workflow ### Parallel Opportunities (examples) - Tests marked `[P]` can be written in parallel with implementation (and should fail before the fix lands). - Deletions of legacy drift files in US3 can be parallelized with catalog/triage cleanup (different files). --- ## Parallel Example: US1 ```bash # In parallel (different files): Task: "Add Baseline Compare evidence-contract tests in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Baselines/BaselineCompareDriftEvidenceContractTest.php" Task: "Implement evidence schema upgrade in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/CompareBaselineToTenantJob.php" Task: "Add diff-unavailable regression test in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Drift/DriftFindingDiffUnavailableTest.php" ``` --- ## Parallel Example: US2 ```bash # In parallel (different files): Task: "Delete Drift landing surface in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Pages/DriftLanding.php" Task: "Update dashboard widget links in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Widgets/Dashboard/NeedsAttention.php" Task: "Update run links in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/OperationRunLinks.php" ``` --- ## Parallel Example: US3 ```bash # In parallel (different files): Task: "Add cleanup migration in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/database/migrations/2026_03_05_000001_delete_legacy_drift_findings.php" Task: "Remove legacy drift code in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Jobs/GenerateDriftFindingsJob.php" Task: "Update triage/catalog in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/OperationCatalog.php" ``` --- ## Implementation Strategy ### MVP First (User Story 1 Only) 1. Complete Phase 1: Setup 2. Complete Phase 2: Foundational (CRITICAL - blocks all stories) 3. Complete Phase 3: User Story 1 4. **STOP and VALIDATE**: Test User Story 1 independently 5. Deploy/demo if ready ### Incremental Delivery 1. Complete Setup + Foundational → Foundation ready 2. Add User Story 1 → Test independently → Deploy/Demo (MVP!) 3. Add User Story 2 → Test independently → Deploy/Demo 4. Add User Story 3 → Test independently → Deploy/Demo 5. Each story adds value without breaking previous stories --- ## Notes - `[P]` tasks = different files, no dependencies - `[US#]` label maps task to specific user story for traceability - Each user story should be independently completable and testable - Prefer Sail for all local commands: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/vendor/bin/sail`