# Implementation Plan: Baseline Operability & Alert Integration (Spec 115) **Branch**: `115-baseline-operability-alerts` | **Date**: 2026-02-28 | **Spec**: `specs/115-baseline-operability-alerts/spec.md` **Input**: Feature specification from `specs/115-baseline-operability-alerts/spec.md` **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. ## Summary - Implement safe baseline finding auto-close after fully successful baseline compares. - Add baseline-specific alert events (`baseline_high_drift`, `baseline_compare_failed`) with precise new/reopened-only semantics and existing cooldown handling. - Introduce workspace settings for baseline severity mapping, minimum alert severity, and an auto-close kill-switch. - Normalize baseline run types via the canonical run type registry. ## Technical Context **Language/Version**: PHP 8.4.x (Laravel 12) **Primary Dependencies**: Filament v5, Livewire v4, Laravel Sail **Storage**: PostgreSQL (Sail) + JSONB **Testing**: Pest v4 (`vendor/bin/sail artisan test`) **Target Platform**: Web application **Project Type**: Laravel monolith **Performance Goals**: N/A (ops correctness + low-noise alerting) **Constraints**: Strict Ops-UX + RBAC-UX compliance; no extra Graph calls; Monitoring render is DB-only **Scale/Scope**: Workspace-scoped settings + tenant-scoped findings/runs ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: PASS (baseline compare reads inventory as “last observed”, no snapshot semantics changed). - Read/write separation: PASS (auto-close is internal lifecycle management; no Graph writes introduced). - Graph contract path: PASS (no new Graph calls). - Deterministic capabilities: PASS (uses existing capability registry for any UI settings mutations). - RBAC-UX: PASS (workspace settings mutations already enforce membership 404 + capability 403 in `SettingsWriter`; tenant-context compare surfaces remain tenant-scoped). - Workspace & tenant isolation: PASS (all findings and runs remain workspace+tenant scoped; alert dispatch validates tenant belongs to workspace). - Run observability: PASS (baseline compare/capture already run via `OperationRunService`; alerts evaluation is an `OperationRun`). - Ops-UX 3-surface feedback: PASS (no new notification surfaces; uses existing Ops UX patterns). - Ops-UX lifecycle + summary counts + guards: PASS (all run transitions via `OperationRunService`; summary keys remain canonical). - Filament Action Surface Contract / UX-001: PASS (only adds fields/actions to existing pages; no new resources required). ## Project Structure ### Documentation (this feature) ```text specs/115-baseline-operability-alerts/ ├── 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) ```text app/ ├── Filament/ │ ├── Pages/ │ │ └── Settings/ │ │ └── WorkspaceSettings.php │ └── Pages/ │ └── BaselineCompareLanding.php ├── Jobs/ │ ├── CompareBaselineToTenantJob.php │ └── Alerts/ │ └── EvaluateAlertsJob.php ├── Models/ │ ├── Finding.php │ ├── AlertRule.php │ └── WorkspaceSetting.php ├── Services/ │ ├── Baselines/ │ │ ├── BaselineCompareService.php │ │ └── BaselineCaptureService.php │ ├── Alerts/ │ │ ├── AlertDispatchService.php │ │ └── AlertFingerprintService.php │ └── Settings/ │ ├── SettingsResolver.php │ └── SettingsWriter.php └── Support/ ├── OperationRunType.php └── Settings/ └── SettingsRegistry.php tests/ └── Feature/ ├── Alerts/ └── Baselines/ ``` **Structure Decision**: Laravel monolith with Filament admin UI. This feature touches Jobs, Services, Settings infrastructure, and adds/updates Pest feature tests. ## Phase 0 — Outline & Research (Complete) Outputs: - `specs/115-baseline-operability-alerts/research.md` Unknowns resolved: - Which summary counters can be used for completeness gating (reuse `total/processed/failed`). - How to implement reopen/resolve stale semantics without breaking workflow status. - How alert event dedupe/cooldown works and what keys are used. - How workspace settings are stored/validated and how effective values are resolved. ## Phase 1 — Design & Contracts (Complete) Outputs: - `specs/115-baseline-operability-alerts/data-model.md` - `specs/115-baseline-operability-alerts/contracts/baseline-alert-events.openapi.yaml` - `specs/115-baseline-operability-alerts/quickstart.md` Design highlights: - Baseline findings are a filtered subset of drift findings (`finding_type=drift`, `source=baseline.compare`). - Auto-close resolves stale baseline findings only when the compare run is complete and safe. - Baseline alert events are produced only for new/reopened baseline findings within the evaluation window. ## Constitution Re-check (Post-Design) Result: PASS. No Graph calls added, no new authorization planes, and all `OperationRun` transitions remain service-owned. ## Phase 2 — Implementation Plan 1) Settings registry + UI - Add `baseline.severity_mapping`, `baseline.alert_min_severity`, `baseline.auto_close_enabled` to `SettingsRegistry` with strict validation. - Extend `WorkspaceSettings` Filament page to render and persist these settings using the existing `SettingsWriter`. 2) Canonical run types - Add `baseline_capture` and `baseline_compare` to `OperationRunType` enum and replace ad-hoc literals where touched in this feature. 3) Baseline compare finding lifecycle - Update `CompareBaselineToTenantJob` to: - apply baseline severity mapping by `change_type`. - preserve existing open finding workflow status. - mark previously resolved findings as `reopened` and set `reopened_at`. 4) Safe auto-close - At the end of `CompareBaselineToTenantJob`, if: - run outcome is `succeeded`, and - `summary_counts.processed == summary_counts.total`, and - `summary_counts.failed == 0`, and - `baseline.auto_close_enabled == true` then resolve stale open baseline findings (not in “seen set”) with reason `no_longer_drifting`. 5) Alerts integration - Extend `EvaluateAlertsJob` to produce: - `baseline_high_drift` (baseline findings only; new/reopened only; respects `baseline.alert_min_severity`). - `baseline_compare_failed` (baseline compare runs failed/`partially_succeeded`; dedupe by run id; cooldown via existing rules). - Register new event types in `AlertRule` and surface them in Filament `AlertRuleResource`. 6) Tests (Pest) - Add/extend Feature tests to cover: - auto-close executes only under the safe gate. - auto-close does not run on `partially_succeeded`/failed/incomplete compares. - reopened findings become `reopened` and trigger baseline drift alerts once. - baseline drift alerts do not trigger repeatedly for the same open finding. - baseline compare failed alerts trigger and are dedupe/cooldown compatible. ## Complexity Tracking > **Fill ONLY if Constitution Check has violations that must be justified** | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | [e.g., 4th project] | [current need] | [why 3 projects insufficient] | | [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |