# Data Model — Baseline Operability & Alert Integration (Spec 115) This spec extends existing models and introduces no new tables. ## Entities ### 1) Finding (existing: `App\Models\Finding`) Baseline compare findings are a subset of drift findings. Key fields used/extended by this feature: - `workspace_id` (derived from tenant) - `tenant_id` - `finding_type` = `drift` - `source` = `baseline.compare` (stable contract) - `scope_key` = `baseline_profile:{baseline_profile_id}` (stable grouping) - `fingerprint` (stable identifier; used for idempotent upsert + alert dedupe) - `status` (lifecycle): `new`, `reopened`, other open states, and terminal states - `reopened_at`, `resolved_at`, `resolved_reason` - `severity` (`low|medium|high|critical`) - `evidence_jsonb` (must include at least `change_type`) - `current_operation_run_id` (the compare run that most recently observed the finding) Lifecycle rules for baseline compare findings: - New fingerprint → create finding with `status=new`. - Existing finding in terminal state (at least `resolved`) observed again → set `status=reopened`, `reopened_at=now`, clear resolved fields. - Existing open finding observed again → do not override workflow status. - Stale open findings (not observed in a fully successful compare) → set `status=resolved`, `resolved_reason=no_longer_drifting`, `resolved_at=now`. ### 2) OperationRun (existing: `App\Models\OperationRun`) Baseline compare runs are represented as: - `type = baseline_compare` - `tenant_id` required (tenant-scoped operation) - `status/outcome` managed exclusively via `OperationRunService` - `summary_counts` used for: - completeness: `processed == total` - safety: `failed == 0` Baseline capture runs are represented as: - `type = baseline_capture` ### 3) WorkspaceSetting (existing: `App\Models\WorkspaceSetting`) New workspace keys (domain `baseline`): - `baseline.severity_mapping` (json object) - Keys MUST be exactly: `missing_policy`, `different_version`, `unexpected_policy` - Values MUST be one of: `low|medium|high|critical` - `baseline.alert_min_severity` (string) - Allowed: `low|medium|high|critical` - Default: `high` - `baseline.auto_close_enabled` (bool) - Default: `true` Effective value rules: - Consumers read via `SettingsResolver`, which merges system defaults with workspace overrides. ## Derived/Computed Values - Baseline finding severity is computed at creation time from `baseline.severity_mapping[change_type]`. - Baseline alert eligibility is computed at alert-evaluation time from: - finding `source` + `status` + timestamps vs `windowStart` - finding `severity` vs `baseline.alert_min_severity` ## Invariants - `Finding.source = baseline.compare` MUST be stable and queryable. - Auto-close MUST only execute if the compare run is complete (`processed==total`) and safe (`failed==0`) and `baseline.auto_close_enabled` is true.