TenantAtlas/specs/119-baseline-drift-engine/data-model.md
Ahmed Darrazi 39fd8ca1ea feat(spec-119): baseline compare drift cutover
- Enrich drift findings evidence_jsonb for diff UX (summary.kind, refs, fidelity, provenance)

- Add baseline policy version resolver and contract asserts

- Remove legacy drift generator + DriftLanding surfaces

- Add one-time cleanup migration for legacy drift findings

- Scope baseline capture/landing warnings to latest inventory sync

- Canonicalize compliance scheduledActionsForRule drift signal
2026-03-06 15:22:42 +01:00

89 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model — Drift Golden Master Cutover (Spec 119)
This spec extends existing models and introduces no new tables.
## Entities
### 1) Finding (existing: `App\Models\Finding`)
Baseline Compare drift findings are tenant-owned rows in `findings`.
Key fields used/extended by this feature:
- `workspace_id` (derived from tenant; required)
- `tenant_id` (required)
- `finding_type = drift` (required)
- `source = baseline.compare` (mandatory contract post-cutover)
- `scope_key` (baseline compare grouping key; stable across re-runs)
- `fingerprint` + `recurrence_key` (stable identity for idempotent upsert and lifecycle)
- `severity` (`low|medium|high|critical`)
- `status` (lifecycle): `new`, `reopened`, other open states, terminal states
- `evidence_fidelity` (string): `content|meta|mixed`
- `evidence_jsonb` (JSONB): enriched evidence contract (see below)
- `current_operation_run_id` (the Baseline Compare `OperationRun` that observed the finding)
- `baseline_operation_run_id` (unused for Baseline Compare drift; legacy run-to-run drift used it)
#### Evidence contract (`findings.evidence_jsonb`)
Minimum required keys for diff-UX compatibility:
- `change_type` (string): `missing_policy|unexpected_policy|different_version`
- `policy_type` (string)
- `subject_key` (string)
- `summary.kind` (string): `policy_snapshot|policy_assignments|policy_scope_tags`
- `baseline.policy_version_id` (int|null)
- `current.policy_version_id` (int|null)
- `baseline.hash` / `current.hash` (string; may be empty for missing/unexpected)
- `baseline.provenance` / `current.provenance` (object; fidelity/source/observed_at/observed_operation_run_id)
- `fidelity` (string): `content|meta|mixed`
- `provenance` (object): `baseline_profile_id`, `baseline_snapshot_id`, `compare_operation_run_id`, and `inventory_sync_run_id` when applicable
Invariant:
- `different_version` requires both policy version ids to render a detailed diff.
- `missing_policy` may render a detailed diff with only `baseline.policy_version_id`.
- `unexpected_policy` may render a detailed diff with only `current.policy_version_id`.
- If the required policy version reference(s) for the findings change type are missing, the UI must treat the finding as “diff unavailable”.
### 2) OperationRun (existing: `App\Models\OperationRun`)
Baseline Compare runs:
- `type = baseline_compare`
- `tenant_id` is required (tenant-scoped operation)
- `status/outcome` transitions are service-owned (must go through `OperationRunService`)
- `context.baseline_profile_id` and `context.baseline_snapshot_id` identify the compare baseline inputs
- `context.baseline_compare.*` contains coverage + evidence gap breakdowns (read-only reporting)
Baseline Capture runs:
- `type = baseline_capture`
- `context.baseline_profile_id` and `context.source_tenant_id` identify the capture inputs
- `context.baseline_capture.inventory_sync_run_id` records which completed Inventory Sync bounded subject selection when one existed
- `context.baseline_capture.gaps.*` remains the canonical reporting block for snapshot-capture ambiguity or evidence issues
Legacy run-to-run drift generation runs:
- `type = drift_generate_findings` (no longer created post-cutover; existing rows may remain historical)
### 3) BaselineProfile / BaselineSnapshot / BaselineSnapshotItem (existing)
Workspace-owned baseline objects:
- Baseline Profile (`baseline_profiles`) defines scope + capture mode and points to `active_snapshot_id`.
- Baseline Snapshot (`baseline_snapshots`) is immutable and stores `summary_jsonb`.
- Baseline Snapshot Items (`baseline_snapshot_items`) store:
- `baseline_hash` (string)
- `meta_jsonb.evidence` provenance fields (fidelity/source/observed_at), intentionally without tenant-owned identifiers (e.g., no `policy_version_id`).
### 4) PolicyVersion (existing: `App\Models\PolicyVersion`)
Tenant-owned immutable policy snapshots used for:
- Content hashing/normalization for Baseline Compare evidence
- Rendering diffs in Findings detail view when both baseline/current policy version references exist
Relevant fields:
- `id`
- `tenant_id`
- `policy_id` (ties versions to a tenant policy)
- `capture_purpose` (e.g., `baseline_capture`, `baseline_compare`)
- `operation_run_id` (which run captured it)
- `captured_at`
- `snapshot`, `assignments`, `scope_tags` (JSON/arrays)
## Derived/Computed Values
- `evidence_jsonb.fidelity` should align with (or be derived from) the per-finding `evidence_fidelity` column.
- `summary.kind` may be set conservatively (e.g., `policy_snapshot`) when dimension-level detection is not available.
## Invariants
- Post-cutover drift findings must be queryable as: `finding_type = drift AND source = baseline.compare`.
- Legacy drift findings are deleted by a one-time migration using: `finding_type = drift AND (source IS NULL OR source <> 'baseline.compare')`.