Implements Spec 119 (Drift Golden Master Cutover): - Baseline Compare is the only drift writer (`source = baseline.compare`). - Drift findings now store diff-compatible `evidence_jsonb` (summary.kind, baseline/current policy_version_id refs, fidelity + provenance). - Findings UI renders one-sided diffs for `missing_policy`/`unexpected_policy` when a single ref exists; otherwise shows explicit “diff unavailable”. - Removes legacy drift generator runtime (jobs/services/UI) and related tests. - Adds one-time migration to delete legacy drift findings (`finding_type=drift` where source is null or != baseline.compare). - Scopes baseline capture & landing duplicate warnings to latest completed inventory sync. - Canonicalizes compliance `scheduledActionsForRule` drift signal and keeps legacy snapshots comparable. Tests: - `vendor/bin/sail artisan test --compact` (full suite per tasks) - Focused pack: BaselinePolicyVersionResolverTest, BaselineCompareDriftEvidenceContractTest, DriftFindingDiffUnavailableTest, LegacyDriftFindingsCleanupMigrationTest, ComplianceNoncomplianceActionsDriftTest Notes: - Livewire v4+ / Filament v5 compatible (no legacy APIs). - No new external dependencies. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #144
11 KiB
Implementation Plan: Drift Golden Master Cutover (Baseline Compare)
Branch: feat/119-baseline-drift-engine | Date: 2026-03-05 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/spec.md
Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/spec.md
Summary
Make Baseline Compare the only drift findings writer (source = baseline.compare) while preserving the existing diff UI by writing a diff-compatible evidence_jsonb structure (including summary.kind and baseline/current policy_version_id references when content evidence exists). different_version findings require both refs, while missing_policy and unexpected_policy may render against an empty side when exactly one ref exists. Align Baseline Snapshot capture with the latest completed Inventory Sync run so stale inventory rows do not create false ambiguous_match gaps. Treat full-content compare captures that reuse an identical existing policy_version as valid current evidence for the current run, instead of dropping them behind the since filter. Treat Intune compliance scheduledActionsForRule as canonical drift signal content (semantic fields only, deterministically sorted) and keep existing baseline snapshots comparable by recomputing effective baseline hashes from resolved baseline policy_versions when content provenance exists. Remove the legacy run-to-run drift generator (jobs, UI, run-type catalog, alerts/widget references, and tests) and delete legacy drift findings to avoid mixed evidence formats.
Technical Context
Language/Version: PHP 8.4.1
Primary Dependencies: Laravel 12, Filament 5, Livewire 4
Storage: PostgreSQL (JSONB for evidence payloads)
Testing: Pest 4 (PHPUnit 12)
Target Platform: Linux containers (Dokploy) + local dev via Laravel Sail
Project Type: Web application (Laravel + Filament admin panels)
Performance Goals: Drift findings from a completed Baseline Compare run are visible within 5 minutes (spec SC-119-05); tenant pages render without timeouts under normal tenant sizes.
Constraints: Hard cut (no feature flags, no dual-write), strict tenant/workspace isolation (404/403 semantics), Ops-UX OperationRun contract must remain intact, no new external integrations.
Scale/Scope: Tenant-scoped drift findings + baseline compare runs; evidence enrichment must be deterministic and safe for list/detail rendering across typical governance datasets (paginated Findings list; evidence JSON schema remains stable).
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Principle | Status | Notes |
|---|---|---|
| Inventory-first, Snapshots-second | PASS | Baseline Compare compares workspace-owned baseline snapshots against tenant “last observed” inventory/policy-version evidence; no change to snapshot immutability. |
| Read/Write separation by default | PASS | Feature is drift detection + evidence enrichment; user-facing mutation remains Baseline Compare “Compare Now” (queued op with confirmation + audit). |
| Single contract path to Graph | PASS | No new Graph endpoints; Baseline Compare continues to use existing evidence providers / contracts. |
| Workspace + Tenant isolation (404/403 semantics) | PASS | Tenant-scoped Findings + Baseline Compare landing remain capability-gated; cleanup migration is scoped to drift findings only. |
| Run observability + Ops-UX 3-surface feedback | PASS | Baseline Compare continues using OperationRun; removing legacy drift generation reduces competing run types and preserves Monitoring contract. |
| Filament action surface contract | PASS | Drift entry point is Baseline Compare landing; Findings resource remains list+view with inspect affordance; destructive-like actions keep confirmations. |
Gate decision: PASS (no constitution exceptions required).
Project Structure
Documentation (this feature)
specs/119-baseline-drift-engine/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
Source Code (repository root)
/Users/ahmeddarrazi/Documents/projects/TenantAtlas/
├── app/
│ ├── Filament/
│ │ ├── Pages/
│ │ │ ├── BaselineCompareLanding.php
│ │ │ └── DriftLanding.php # legacy (to remove)
│ │ ├── Resources/
│ │ │ └── FindingResource.php
│ │ └── Widgets/
│ │ └── Dashboard/NeedsAttention.php # legacy references (to update)
│ ├── Jobs/
│ │ ├── CompareBaselineToTenantJob.php
│ │ └── GenerateDriftFindingsJob.php # legacy (to remove)
│ ├── Jobs/Alerts/EvaluateAlertsJob.php # legacy compare_failed producer (to update/remove)
│ ├── Services/
│ │ ├── Baselines/
│ │ │ ├── CurrentStateHashResolver.php
│ │ │ └── Evidence/* # evidence providers + provenance
│ │ └── Drift/* # keep diff + normalizers; remove generator-only pieces
│ └── Support/
│ ├── OperationCatalog.php # legacy run type label (to remove)
│ ├── OperationRunLinks.php # legacy drift link (to update)
│ └── OperationRunType.php # legacy enum case (to remove)
├── database/migrations/
│ └── (new) *_delete_legacy_drift_findings.php # one-time cleanup migration
├── resources/views/filament/pages/
│ ├── baseline-compare-landing.blade.php
│ └── drift-landing.blade.php # legacy (to remove)
└── tests/
├── Feature/Alerts/* # update references to legacy drift type if any
└── Feature/Drift/* # legacy tests (to remove/update)
Structure Decision: Single Laravel application under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/ (Filament UI + queued jobs). Feature work stays within app/, resources/, database/, and tests/ plus feature docs under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/.
Complexity Tracking
Fill ONLY if Constitution Check has violations that must be justified
No constitution exceptions are required for this feature.
Phase 0 — Outline & Research
Output: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/research.md
- Confirm existing Baseline Compare drift writer contract (where
sourceis set; what evidence fields exist today). - Identify the minimum evidence keys required for the existing diff renderer (
summary.kind, baseline/currentpolicy_version_id). - Identify all legacy drift generation touchpoints to remove (job, generator, UI landing, dashboard widget, operation catalog/links, alerts producer, and tests).
- Define deterministic cleanup criteria for legacy drift findings deletion.
Phase 1 — Design & Contracts
Outputs:
/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/data-model.md/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/contracts/*/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/quickstart.md
Design highlights:
- Drift findings remain tenant-owned (
findings.finding_type = drift) with mandatorysource = baseline.compare. - Baseline Compare evidence is enriched to be diff-renderable when content evidence exists:
- Always write
evidence_jsonb.summary.kind(allowed:policy_snapshot,policy_assignments,policy_scope_tags). - Write
evidence_jsonb.baseline.policy_version_idandevidence_jsonb.current.policy_version_idwhen available (independently); computeevidence_jsonb.fidelitydeterministically from presence (both =content, one =mixed, none =meta). - Resolve baseline
policy_version_iddeterministically using baseline snapshot item provenance (observed_at) + stable policy identity (policy_type+subject_key); if not resolvable, set null. - Add
evidence_jsonb.provenancekeys:baseline_profile_id,baseline_snapshot_id,compare_operation_run_id(Baseline CompareOperationRunid), andinventory_sync_run_idwhen applicable. - Findings UI renders one-sided diffs for
missing_policy(baseline-only) andunexpected_policy(current-only); onlydifferent_versionrequires both refs.
- Always write
- Drift navigation entry point after cutover is Baseline Compare landing (
/admin/t/{tenant}/baseline-compare-landing).
Re-check Constitution post-design: PASS (design stays within tenant/workspace isolation and existing OperationRun + Filament action-surface rules).
Phase 2 — Implementation Plan (for /speckit.tasks)
Phase 2 will be expressed as concrete tasks in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/119-baseline-drift-engine/tasks.md by /speckit.tasks. High-level sequencing:
-
Evidence contract upgrade (Baseline Compare → drift findings)
- Extend Baseline Compare drift evidence writer to include
summary.kind, baseline/currentpolicy_version_idreferences (when content), explicit fidelity, and run/snapshot provenance. - Ensure evidence keys are stable and deterministic across runs.
- Align Baseline Snapshot capture subject selection with the latest completed Inventory Sync run when available, matching Baseline Compare’s current-state boundary.
- Treat same-run reused compare versions as valid current content evidence so deduplicated captures do not surface as
missing_current. - Canonicalize compliance
scheduledActionsForRuleinto semantic drift keys and compare content-backed baseline snapshots against recomputed baseline-version hashes so the signal can evolve without forcing snapshot recapture.
- Extend Baseline Compare drift evidence writer to include
-
Diff UX guardrails
- Update the Findings detail view so
different_versiondiffs require bothbaseline.policy_version_idandcurrent.policy_version_id, whilemissing_policyandunexpected_policyrender against an empty side when their single required reference exists; otherwise show an explicit “diff unavailable” explanation.
- Update the Findings detail view so
-
Hard-cut legacy drift removal
- Delete legacy drift generation job + generator-only services and remove tenant UI entry points that trigger or describe run-to-run drift generation.
- Remove legacy operation run type registrations/catalog labels and any UI/widget/alert producers that reference drift generation.
-
Data cleanup
- Add a one-time migration deleting findings where
finding_type = driftAND (sourceis null ORsourceis not equal tobaseline.compare), ensuring Baseline Compare rows remain.
- Add a one-time migration deleting findings where
-
Tests
- Add/adjust tests asserting Baseline Compare drift findings include the new evidence contract and
source. - Remove legacy drift generation tests and update any other tests that referenced
drift_generate_findingsonly to simulate “some other type”.
- Add/adjust tests asserting Baseline Compare drift findings include the new evidence contract and