# Phase 1 Design: Data Model (044) ## Entities ### Finding New table: `findings` **Purpose**: Generic, persisted pipeline for analytic findings (Drift now; Audit/Compare later). **Core fields (MVP)** - `id` (pk) - `tenant_id` (fk tenants) - `finding_type` (`drift` in MVP; later `audit`/`compare`) - `scope_key` (string; deterministic; reuse Inventory selection hash) - `baseline_run_id` (nullable fk inventory_sync_runs) - `current_run_id` (nullable fk inventory_sync_runs) - `fingerprint` (string; deterministic) - `subject_type` (string; e.g. policy type) - `subject_external_id` (string; Graph external id) - `severity` (`low|medium|high`; MVP default `medium`) - `status` (`new|acknowledged`) - `acknowledged_at` (nullable) - `acknowledged_by_user_id` (nullable fk users) - `evidence_jsonb` (jsonb; sanitized, small; allowlist) **Prepared for later (nullable, out of MVP)** - `rule_id`, `control_id`, `expected_value`, `source` ## Constraints & Indexes **Uniqueness** - Unique: `(tenant_id, fingerprint)` **Lookup indexes (suggested)** - `(tenant_id, finding_type, status)` - `(tenant_id, scope_key)` - `(tenant_id, current_run_id)` - `(tenant_id, baseline_run_id)` - `(tenant_id, subject_type, subject_external_id)` ## Relationships - `Finding` belongs to `Tenant`. - `Finding` belongs to `User` via `acknowledged_by_user_id`. - `Finding` belongs to `InventorySyncRun` via `baseline_run_id` (nullable) and `current_run_id` (nullable). ## Evidence shape (MVP allowlist) For Drift MVP, `evidence_jsonb` should contain only: - `change_type` - `changed_fields` (list) and/or `change_counts` - `run`: - `baseline_run_id`, `current_run_id` - `baseline_finished_at`, `current_finished_at` No raw policy payload dumps; exclude secrets/tokens; exclude volatile fields for hashing.