120 lines
4.9 KiB
Markdown
120 lines
4.9 KiB
Markdown
# 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.
|