# Research — Baseline Operability & Alert Integration (Spec 115) This document resolves planning unknowns and records implementation decisions. ## Decisions ### 1) Completeness counters for safe auto-close - Decision: Treat compare “completeness counters” as `OperationRun.summary_counts.total`, `processed`, and `failed`. - Rationale: Ops-UX contracts already standardize these keys via `OperationSummaryKeys::all()`; they’re the metrics the UI understands for determinate progress. - Alternatives considered: - Add new keys like `total_count` / `processed_count` / `failed_item_count` → rejected because it would require expanding `OperationSummaryKeys::all()` and updating Ops-UX guard tests without a strong benefit. ### 2) Where auto-close runs - Decision: Perform auto-close at the end of `CompareBaselineToTenantJob` (after findings upsert), using the run’s computed “seen” fingerprint set. - Rationale: The job already has the full drift result set for the tenant+profile; it’s the only place that can reliably know what was evaluated. - Alternatives considered: - Separate queued job for auto-close → rejected (extra run coordination and more complex observability for no benefit). ### 3) Baseline finding lifecycle semantics (new vs reopened vs existing open) - Decision: Mirror the existing drift lifecycle behavior (as implemented in `DriftFindingGenerator`): - New fingerprint → `status = new`. - Previously terminal fingerprint (at least `resolved`) observed again → `status = reopened` and set `reopened_at`. - Existing open finding → do not overwrite workflow status (avoid resetting `triaged`/`in_progress`). - Rationale: This preserves operator workflow state and enables “alert only on new/reopened” logic. - Alternatives considered: - Always set `status = new` on every compare (current behavior) → rejected because it can overwrite workflow state. ### 4) Alert deduplication key for baseline drift - Decision: Set `fingerprint_key` to a stable string derived from the finding fingerprint (e.g. `finding_fingerprint:{fingerprint}`) for baseline drift events. - Rationale: Alert delivery dedupe uses `fingerprint_key` (or `idempotency_key`) via `AlertFingerprintService`. - Alternatives considered: - Use `finding:{id}` → rejected because it ties dedupe to a DB surrogate rather than the domain fingerprint. ### 5) Baseline-specific event types - Decision: Add two new alert event types and produce them in `EvaluateAlertsJob`: - `baseline_high_drift`: for baseline compare findings (`source = baseline.compare`) that are `new`/`reopened` in the evaluation window and meet severity threshold. - `baseline_compare_failed`: for `OperationRun.type = baseline_compare` with `outcome in {failed, partially_succeeded}` in the evaluation window. - Rationale: The spec requires strict separation from generic drift alerts and precise triggering rules. - Alternatives considered: - Reuse `high_drift` / `compare_failed` → rejected because it would mix baseline and non-baseline meaning. ### 6) Cooldown behavior for baseline_compare_failed - Decision: Reuse the existing per-rule cooldown + quiet-hours suppression implemented in `AlertDispatchService` (no baseline-specific cooldown setting). - Rationale: Matches spec clarification and existing patterns. ### 7) Workspace settings implementation approach - Decision: Implement baseline settings using the existing `SettingsRegistry`/`SettingsResolver`/`SettingsWriter` system with new keys under a new `baseline` domain: - `baseline.severity_mapping` (json map with restricted keys) - `baseline.alert_min_severity` (string) - `baseline.auto_close_enabled` (bool) - Rationale: This matches existing settings infrastructure and ensures consistent “effective value” semantics. ### 8) Information architecture (IA) and planes - Decision: Keep baseline profile CRUD as workspace-owned (non-tenant scoped) and baseline compare monitoring as tenant-context only. - Rationale: Matches SCOPE-001 and spec FR-018. ## Notes / Repo Facts Used - Ops-UX allowed summary keys are defined in `App\Support\OpsUx\OperationSummaryKeys`. - Drift lifecycle patterns exist in `App\Services\Drift\DriftFindingGenerator` (reopen + resolve stale). - Alert dispatch dedupe/cooldown/quiet-hours are centralized in `App\Services\Alerts\AlertDispatchService` and `AlertFingerprintService`. - Workspace settings are handled by `App\Support\Settings\SettingsRegistry` + `SettingsResolver` + `SettingsWriter`.