# Data Model: Verification Checklist Framework V1.5 (075)

**Date**: 2026-02-05  
**Phase**: Phase 1 (Design)  
**Status**: Draft (design-complete for implementation planning)

---

## Existing Entity (reference)

### OperationRun (existing)

**Purpose**: Canonical operational record. Verification reports are stored in `operation_runs.context.verification_report` (JSONB).

**Key fields (relevant)**:
- `id`
- `tenant_id`
- `workspace_id`
- `type` (verification flow identifier)
- `run_identity_hash` (identity hash used for active dedupe + identity matching)
- `status`
- `context` (JSONB)

**Verification report storage**:
- `context.verification_report` (JSON object)

---

## New Persistent Entity

### VerificationCheckAcknowledgement (new table)

**Table name**: `verification_check_acknowledgements`

**Purpose**: First-class governance record that a failing/warning check is acknowledged for a specific report (report == operation run).

**Fields**:
- `id` (primary key)
- `tenant_id` (FK or scalar; used for tenant-scoped filtering and isolation checks)
- `workspace_id` (FK or scalar)
- `operation_run_id` (FK to `operation_runs.id`) — the “report”
- `check_key` (string)
- `ack_reason` (string, max 160)
- `expires_at` (timestamp, nullable) — informational only in v1.5
- `acknowledged_at` (timestamp)
- `acknowledged_by_user_id` (FK to `users.id`)
- `created_at`, `updated_at`

**Uniqueness constraint (required)**:
- unique `(operation_run_id, check_key)`

**Indexes (recommended)**:
- `(tenant_id, workspace_id, operation_run_id)`
- `(operation_run_id)`

**Validation rules**:
- `ack_reason`: required, string, length ≤ 160
- `expires_at`: optional, must be a valid timestamp, should be >= acknowledged_at (implementation may enforce)

**State transitions**:
- Immutable per report/check in v1.5: create once; no update/delete/unack flows.

---

## Contracted Document (stored in JSON)

### VerificationReport (JSON in `OperationRun.context.verification_report`)

**Purpose**: Structured, versioned report of verification results used by the DB-only viewer.

**Identity**:
- `report_id`: `operation_runs.id`
- `previous_report_id`: previous run id for same identity (nullable)

**New v1.5 fields**:
- `fingerprint` (string; lowercase hex; SHA-256)
- `previous_report_id` (nullable integer/uuid depending on `OperationRun` PK type)

**Existing core fields (from 074, reference)**:
- `schema_version` (SemVer string; major `1`)
- `flow` (verification flow identifier; aligns with `OperationRun.type`)
- `generated_at` (timestamp)
- `summary` (counts + overall outcome)
- `checks[]` (flat array) including:
  - `key`
  - `title`
  - `status` (`pass|fail|warn|skip|running`)
  - `severity` (`info|low|medium|high|critical` or empty string)
  - `blocking` (boolean)
  - `reason_code` (string)
  - safe evidence pointers
  - `next_steps[]` (navigation-only links)

**Fingerprint normalization input** (strict):
- Flatten all checks across `checks[]`.
- Sort by `check.key`.
- Contribute the stable tuple string:
  - `key|status|blocking|reason_code|severity`
  - `severity` must always be present (missing normalized to empty string).

---

## Derived/Computed View Data (not persisted)

### Change indicator

Computed in the viewer by comparing:
- current `verification_report.fingerprint`
- previous `verification_report.fingerprint`

States:
- no previous report → no indicator
- fingerprints match → “No changes since previous verification”
- fingerprints differ → “Changed since previous verification”
