# Research: Verification Checklist Framework V1.5 (075)

**Date**: 2026-02-05  
**Phase**: Phase 0 (Foundational Research)  
**Status**: Complete

---

## Decisions

### D-075-001 — Canonical storage for report + metadata

**Decision**: Store the verification report (including `fingerprint` and `previous_report_id`) inside `operation_runs.context.verification_report` (JSONB), consistent with 074.

**Rationale**:
- Viewer surfaces must be DB-only at render time (constitution: Operations / Run Observability Standard).
- `OperationRun` is already the canonical operational record and stable viewer entry point.
- Adds supportability metadata without introducing a new top-level report table.

**Alternatives considered**:
- Dedicated `verification_reports` table: rejected for v1.5 to avoid new query/index surfaces; revisit if we need global querying across reports.

---

### D-075-002 — Report identity + “previous report” resolution

**Decision**: Resolve `previous_report_id` by querying the most recent earlier `OperationRun` whose **run identity** matches exactly (flow/type, tenant, workspace, provider connection where applicable).

**Rationale**:
- The existing `OperationRunService::ensureRunWithIdentity()` + `run_identity_hash` already defines the dedupe boundary.
- Matches the spec’s clarified rule: `provider_connection_id` must match exactly; `NULL` only matches `NULL`.

**Alternatives considered**:
- Match previous runs by only `tenant_id + workspace_id + type` and then filter in PHP: rejected due to ambiguity and risk of cross-connection mixing.

---

### D-075-003 — Report ID semantics

**Decision**: Treat the `OperationRun` ID as the report identifier in UX and contracts (`report_id == operation_run_id`).

**Rationale**:
- The report is attached to the run; the run is the stable, tenant-scoped canonical record.
- Avoids a second identifier for the same “verification execution artifact”.

**Alternatives considered**:
- Generate a separate report UUID inside the JSON: rejected as it adds indirection without benefits in v1.5.

---

### D-075-004 — Fingerprint algorithm

**Decision**: Use SHA-256 over a deterministic normalization of check outcomes:
- flatten checks
- sort by `check.key`
- contribute `key|status|blocking|reason_code|severity` where `severity` is always present (missing → empty)

Store as lowercase hex.

**Rationale**:
- Deterministic across environments.
- Treats severity-only changes as meaningful (per clarified requirement).

**Alternatives considered**:
- Hash the full report JSON: rejected (unstable ordering, non-semantic fields like timestamps).

---

### D-075-005 — Per-check acknowledgements persistence

**Decision**: Create a first-class table `verification_check_acknowledgements` keyed by `(operation_run_id, check_key)` with a unique constraint.

**Rationale**:
- Acknowledgements are governance metadata and must be queryable and auditable.
- Unique per report/check is enforced by the DB.

**Alternatives considered**:
- Store acknowledgements inside `operation_runs.context`: rejected as it complicates update semantics and auditability, and risks “report mutation” appearing like a changed verification outcome.

---

### D-075-006 — Capability naming reconciliation

**Decision**: Introduce a dedicated canonical capability `tenant_verification.acknowledge` in the capability registry and map it in the role → capability map.

**Rationale**:
- Keeps the feature spec requirement literal and avoids overloading “findings” semantics.
- Preserves the constitution rule that capabilities are centrally registered (no raw strings).

**Alternatives considered**:
- Reuse existing `tenant_findings.acknowledge`: rejected because this feature is specifically verification-report scoped, and we want the permission surface to remain explicit.

---

### D-075-007 — Audit action identifier + payload minimization

**Decision**: Add a stable audit action ID for acknowledgements (e.g. `verification.check_acknowledged`) and emit it on successful acknowledgement. Audit metadata is minimal and MUST NOT include `ack_reason`.

**Rationale**:
- Acknowledgement is a write mutation; constitution requires audit logging.
- Spec explicitly excludes `ack_reason` from audit payload; it remains only in the acknowledgement record.

**Alternatives considered**:
- Reuse `verification.completed`: rejected because it conflates verification execution with governance mutation.

---

### D-075-008 — Filament UI implementation constraints

**Decision**: Implement the “Verify step” UX changes in Filament v5 (Livewire v4) using:
- DB-only viewer helper (no external calls)
- centralized badge domains (BADGE-001)
- mutation via Filament `Action::make(...)->action(...)` with `->requiresConfirmation()`

**Rationale**:
- Aligns with Filament v5 patterns and constitution rules.

**Alternatives considered**:
- Publish/override Filament internal views: rejected; prefer render hooks + CSS hooks as needed.
