# Implementation Plan: Golden Master Baseline Drift — Deep Settings Drift via Provider Chain **Branch**: `117-baseline-drift-engine` | **Date**: 2026-03-02 | **Spec**: [specs/117-baseline-drift-engine/spec.md](specs/117-baseline-drift-engine/spec.md) ## Summary Implement a single, batch-oriented “current state hash resolver” for Golden Master baseline capture + compare. - For each tenant subject, resolve best available evidence via provider chain (content via `PolicyVersion` since baseline snapshot captured time; else meta via Inventory meta contract). - Compare baseline snapshot item hash vs current resolved hash to emit drift findings. - Findings store + display baseline and current evidence provenance (fidelity, source, observed timestamp for each side) and add a fidelity badge + filter. - Compare remains read-only (no new external calls during compare) and continues to run via `OperationRun`. ## Technical Context **Language/Version**: PHP 8.4 (Laravel 12) **Primary Dependencies**: Filament v5, Livewire v4, Pest v4 **Storage**: PostgreSQL (Sail) **Testing**: Pest (`vendor/bin/sail artisan test --compact`) **Target Platform**: Docker containers (Laravel Sail) **Project Type**: Web application (Laravel + Filament) **Performance Goals**: Baseline scope ~500 subjects resolves + compares within ~2 minutes (staging target from spec) **Constraints**: - v1.5 compare MUST NOT initiate any upstream/Graph calls; DB-only + existing stored evidence only. - Batch resolution (no per-subject query loops; chunk + set-based queries). - Baseline snapshot items MUST NOT persist tenant identifiers. **Scale/Scope**: Per-run: 0–500+ subjects; per-tenant: multiple policy types; mixed evidence fidelity expected. ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: PASS — “meta evidence” comes from `inventory_items` (last observed); snapshot items remain immutable standards. - Read/write separation: PASS — compare/capture are operational runs; no new write surfaces; findings are operational artifacts. - Graph contract path: PASS — v1.5 compare uses only local DB evidence (no Graph calls). - Deterministic capabilities: PASS — no new capabilities introduced. - RBAC-UX / workspace+tenant isolation: PASS — use existing tenant-context Filament routes; non-members 404; members lacking capability 403. - Run observability: PASS — both capture + compare are queued `OperationRun`s using `OperationRunService`. - Ops-UX 3-surface feedback: PASS — existing run-start surfaces already enqueue; run detail is canonical. - Ops-UX summary counts: PASS — any new detail stays in `context`; summary keys remain from `OperationSummaryKeys::all()`. - Badge semantics: PASS — new badges/filters will follow existing UI patterns (no ad-hoc status mappings). - Filament action surface contract: PASS — modifying Finding list UI adds filter + badge only; no new destructive actions. ## Project Structure ### Documentation (this feature) ```text specs/117-baseline-drift-engine/ ├── spec.md ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md └── contracts/ └── openapi.yaml ``` Next: generate `specs/117-baseline-drift-engine/tasks.md` via `/speckit.tasks`. ### Source Code (repository root) ```text app/ ├── Jobs/ │ ├── CaptureBaselineSnapshotJob.php │ └── CompareBaselineToTenantJob.php ├── Models/ │ ├── BaselineSnapshot.php │ ├── BaselineSnapshotItem.php │ ├── InventoryItem.php │ ├── Policy.php │ ├── PolicyVersion.php │ └── Finding.php ├── Services/ │ ├── Baselines/ │ │ ├── BaselineCompareService.php │ │ ├── BaselineSnapshotIdentity.php │ │ └── (new) CurrentStateHashResolver + providers │ └── Drift/ │ ├── DriftHasher.php │ └── Normalizers/SettingsNormalizer.php └── Filament/ ├── Pages/BaselineCompareLanding.php └── Resources/FindingResource.php database/migrations/ └── (new) add evidence_fidelity to findings (required for filtering) tests/ └── Feature/ (new/updated Pest tests for resolver + compare + UI filter) ``` **Structure Decision**: Implement as an incremental change within existing Baselines/Findings domains (no new modules). ## Complexity Tracking No constitution violations are required for v1.5. (Table intentionally empty.) ## Phase 0 — Research (output: research.md) Goals: - Confirm current baseline capture/compare logic and identify the precise extension points. - Decide where fidelity/provenance should be stored (JSONB vs columns) to support filtering. - Confirm real route URIs/names for contracts (Filament + Monitoring). Deliverable: [specs/117-baseline-drift-engine/research.md](specs/117-baseline-drift-engine/research.md) ## Phase 1 — Design (output: data-model.md + contracts/* + quickstart.md) Deliverables: - Data model changes and JSON shapes: [specs/117-baseline-drift-engine/data-model.md](specs/117-baseline-drift-engine/data-model.md) - Endpoint reference contract (no new APIs; documented existing routes): [specs/117-baseline-drift-engine/contracts/openapi.yaml](specs/117-baseline-drift-engine/contracts/openapi.yaml) - Developer quickstart: [specs/117-baseline-drift-engine/quickstart.md](specs/117-baseline-drift-engine/quickstart.md) Post-design constitution re-check: PASS (see Phase 1 notes in research/design outputs). ## Phase 2 — Implementation Planning (high-level) 1) Implement `CurrentStateHashResolver` (provider chain + batch resolution). 2) Update baseline capture to store best available hash (content if present, else meta) + provenance. 3) Update baseline compare to: - use resolver with `since = baseline_snapshots.captured_at` - record evidence gaps in run context - emit findings with baseline+current provenance 4) Update Findings UI: - show fidelity badge - add fidelity filter (content/meta) - display baseline+current provenance fields 5) Add migration for `findings.evidence_fidelity` (required for fast, stable fidelity filtering). 6) Add Pest tests (resolver behavior, compare integration, UI filter query behavior). 7) Run `vendor/bin/sail bin pint --dirty --format agent` and focused tests. ## Scope note (v2.0) - v2.0 “full content capture mode for Golden Master” is explicitly out of scope for v1.5 implementation tasks. - v1.5 remains opportunistic: compare is DB-only and consumes only existing stored evidence.