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