# 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.
