# Feature Specification: Verification Checklist Framework V1.5 (Governance + Supportability + UI-Complete)

**Feature Branch**: `075-verification-v1_5`  
**Created**: 2026-02-05  
**Status**: Draft  
**Input**: User description: "Extend verification checklist framework with report fingerprint + previous report change indicator, per-check acknowledgements with audit/confirmation, and issues-first operator-ready verify-step UX."

## Goal

V1.5 extends the V1 verification checklist framework with two enterprise-critical additions while keeping scope intentionally small:

1) **Supportability / determinism**: show whether results changed since the previous verification for the same identity.
2) **Governance**: allow explicit, auditable acknowledgement of known issues per failing check.
3) **Enterprise UX completeness**: make the Verify step operator-ready (issues-first, one clear primary action, technical details secondary).

## Clarifications

### Session 2026-02-05

- Q: How is “block” represented on checks? → A: No new status; a “Blocker” is `status=fail` with `blocking=true`.
- Q: Should `severity` be part of the fingerprint? → A: Yes; include `severity` always (normalize missing to empty) to keep hashing deterministic and to treat severity-only changes as “Changed”.
- Q: Should `ack_reason` be included in the audit event payload? → A: No; keep audit metadata minimal and store the reason only in the acknowledgement record.
- Q: How should `provider_connection_id` be treated when resolving `previous_report_id`? → A: Match exactly; `NULL` only matches `NULL` (no cross-connection mixing).
- **Report shape (canonical, inherited from 074)**: Persist reports as the existing V1 JSON shape (`schema_version`, `flow`, `generated_at`, `summary`, `checks[]`). V1.5 adds `fingerprint` + `previous_report_id` at the top level. No `sections[]` array is stored.
- **Idempotency (inherited)**: Deduplication applies only while a run is active (`queued` / `running`). Once `completed` / `failed`, starting verification creates a new run.
- **Viewing (inherited)**: Viewing is DB-only; rendering MUST NOT perform external calls.
- **Evidence (inherited)**: Evidence is limited to safe pointers only; no secrets (no tokens/claims/headers/raw payloads).
- **Next steps (inherited)**: Navigation-only links (no server-side “fix it” actions from the viewer).

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Operator can tell “nothing changed” (Priority: P1)

As an operator, I can immediately see whether the current verification findings are unchanged compared to the previous verification for the same identity, so I can avoid unnecessary re-diagnosis.

**Why this priority**: This is the fastest path to supportability: it reduces repeated analysis and makes troubleshooting deterministic.

**Independent Test**: Create two reports for the same identity with identical normalized check outcomes; confirm the viewer indicates “No changes since previous verification”.

**Acceptance Scenarios**:

1. **Given** a report with a previous report available, **When** I open the viewer, **Then** I see a clear indicator “Changed” or “No changes”.
2. **Given** the current report has the same fingerprint as the previous report, **When** I open the viewer, **Then** I see “No changes since previous verification”.

---

### User Story 2 - Owner/Manager can acknowledge a known issue (Priority: P1)

As an owner/manager, I can acknowledge a failing/warning/blocking check with a short reason (and optionally an expiry) so the team can see that the risk is known, evaluated, and accepted.

**Why this priority**: Acknowledgements provide governance without masking risk; they improve shared context and auditability.

**Independent Test**: With and without the acknowledgement capability, attempt to acknowledge a failing check; assert correct authorization (403) and that an audit event is recorded for the successful path.

**Acceptance Scenarios**:

1. **Given** a check in status `fail` / `warn` (including failing blockers where `blocking=true`), **When** I acknowledge it with a reason, **Then** the UI shows who acknowledged it, when, and the reason.
2. **Given** I do not have the acknowledgement capability, **When** I attempt to acknowledge a check, **Then** the server returns 403 and the UI does not offer the acknowledgement action.

---

### User Story 3 - Verify step is operator-ready (issues-first) (Priority: P1)

As a workspace member, I see issues-first results with clear next steps and exactly one primary action (start or refresh), so I can remediate quickly without hunting through technical details.

**Why this priority**: The Verify step is a high-frequency operator surface; clarity and deterministic states reduce time-to-resolution.

**Independent Test**: Seed a report with blockers and a running state; confirm the default tab and “one primary CTA” rule is enforced in both completed and running scenarios.

**Acceptance Scenarios**:

1. **Given** a report with blockers, **When** I open the Verify step/viewer, **Then** the Issues tab is the default and blockers are at the top.
2. **Given** a run is active, **When** I open the Verify step/viewer, **Then** the primary action is “Refresh results” and technical links are secondary.

---

### Edge Cases

- No previous report exists for an identity → no “changed/no-change” indicator is shown.
- Run is active but no report is available yet → UI shows a clear “running, results will appear” explanation (no empty states without guidance).
- Partial report while running → partial results render with a “Partial results” label.
- Unknown check keys or reason codes → UI degrades gracefully, showing status and message without breaking.
- Acknowledgement attempted for non-acknowledgeable status (e.g., `pass`) → request is rejected and UI does not offer it.

## Out of Scope

- Diff/compare UI between reports
- Server-side fixes initiated from the viewer
- Undo / unacknowledge acknowledgements (V1.5 acknowledgements are immutable per report)
- Complex staleness/TTL semantics (fresh/stale/expired)
- Global dashboards / cross-tenant reporting
- Export features (PDF/JSON) as a product feature
- Live polling (V1.5 uses manual refresh)

## Requirements *(mandatory)*

**Constitution alignment (required):** This feature adds new tenant-scoped mutations (acknowledgements) and new report metadata. It MUST include explicit confirmation, audit logging for mutations, tenant isolation, and tests.

**Constitution alignment (RBAC-UX):** Tenant-scoped routes MUST preserve deny-as-not-found (404) for non-members, and use 403 for members missing a capability. UI visibility is not authorization; server-side enforcement is required.

**Constitution alignment (BADGE-001):** Status-like badges MUST use centralized mapping semantics; no ad-hoc UI mappings.

### Functional Requirements

- **FR-075-001 — Report fingerprint**: Each verification report MUST store a deterministic `fingerprint` derived from normalized check outcomes.

  **Normalization rule (deterministic):**
  - Flatten all check results across `report.checks[]`
  - Sort by stable `check.key`
  - For each check, contribute a stable string using: `key | status | blocking | reason_code | severity`
    - `severity` MUST be included always; if the source report omits it, normalize to an empty string
  - The fingerprint MUST be a stable cryptographic hash of the joined contributions, stored as a fixed-length lowercase hex string.

- **FR-075-002 — Previous report link**: Each report MUST store `previous_report_id` (nullable) that points to the most recent earlier report for the same **verification identity**.

  **Identity match** MUST include:
  - flow
  - workspace
  - tenant
  - provider connection (`provider_connection_id`) matched exactly; `NULL` only matches `NULL`

- **FR-075-003 — Change indicator**: When a previous report exists, the viewer MUST show:
  - “No changes since previous verification” if `fingerprint` matches
  - “Changed since previous verification” otherwise

- **FR-075-004 — Per-check acknowledgements (first-class)**: The system MUST allow acknowledging checks with status `fail` / `warn`.

  An acknowledgement MUST record:
  - reason (max 160 characters)
  - acknowledged timestamp
  - acknowledged-by user
  - optional expiry timestamp

  Acknowledgements MUST be unique per (report, check key). Expiry, when provided, is informational only in V1.5 and MUST NOT introduce automatic staleness/TTL behavior.

- **FR-075-005 — Acknowledgement does not change outcomes**: Acknowledging MUST NOT change:
  - the check status
  - the report summary status/outcome
  - the run outcome

- **FR-075-006 — Acknowledgement allowed conditions**: Acknowledgement MUST only be possible for checks whose status is in `{fail, warn}`. It MUST NOT be available for passing/green checks.

  A check is considered a **Blocker** when `status=fail` and `blocking=true`; blockers are acknowledgeable under the same `{fail, warn}` rule (no separate `block` status exists).

- **FR-075-007 — Acknowledgement authorization (capability-first)**: Acknowledgement MUST require the capability `tenant_verification.acknowledge` as defined in the canonical capability registry.

  RBAC UX semantics:
  - non-member / not entitled to tenant scope → 404
  - member without acknowledgement capability → 403
  - members with tenant scope but without acknowledgement capability can still view reports (view remains read-only)

- **FR-075-007A — Viewing authorization semantics preserved (inherited)**: Viewing tenant-scoped verification pages (Verify step + report viewer) MUST preserve V1 semantics:
  - non-member / not entitled to tenant scope → 404
  - member with tenant scope → can view
  - capability checks apply to mutations only (start verification, acknowledgement)

- **FR-075-008 — Confirmation + audit required**: Acknowledgement is a mutation and MUST require explicit user confirmation and MUST emit an audit event.

- **FR-075-009 — Audit event metadata (minimal)**: The audit event for acknowledgement MUST include minimally:
  - workspace, tenant, run, report, flow
  - check key and reason code
  - acknowledged-by user

  It MUST NOT include `ack_reason`, secrets, tokens, or raw payloads.

- **FR-075-010 — DB-only viewing guard (inherited)**: Rendering the viewer and the Verify step MUST NOT trigger external calls.

- **FR-075-011 — Centralized badge semantics (BADGE-001)**: All check-status badges and summary-status badges used by V1.5 MUST use the centralized badge mapping registry.

- **FR-075-012 — Verify step enterprise UX (normative)**: The Verify step/viewer MUST follow an issues-first layout and deterministic UI states:

  **Structure**
  - Always-visible summary card
  - Tabs: Issues (default), Passed, Technical details

  **DB-only hint**
  - The summary surface MUST include a clear hint that viewing is read-only and performs no external calls.

  **Primary action rule (strict)**
  - Exactly one primary call-to-action is shown at any time
  - “Start verification” and “Refresh results” MUST NOT both be primary simultaneously

  **Issues tab ordering**
  1) Blockers (not acknowledged)
  2) Failures (not acknowledged)
  3) Warnings (not acknowledged)
  4) Acknowledged issues (collapsed group)

  **Next steps rendering**
  - Max 2 navigation-only links per issue card
  - “Open run details” MUST appear only in Technical details (not in issue cards)

  **Technical details**
  - Secondary surface that can show identifiers (run/report IDs), fingerprint, and previous report link
  - No raw payloads/tokens/full error bodies

### Key Entities *(include if feature involves data)*

- **Verification Identity**: The stable identifiers that define “what is being verified” (flow, workspace, tenant, and optional provider connection).
- **Verification Report**: A structured record of verification outcomes for a run.
- **Report Fingerprint**: A deterministic hash representing normalized check outcomes.
- **Previous Report**: The immediately preceding report for the same identity.
- **Check Acknowledgement**: A governance record that an issue is known/accepted (who/when/reason/optional expiry) without altering the check outcome.

### Assumptions

- A verification run/report concept already exists from V1.
- The system has an audit log mechanism capable of recording acknowledgement actions.
- Manual refresh is acceptable (no polling required).

### Dependencies

- Spec 074 (Verification Checklist Framework V1)

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-075-001 (Supportability)**: With a previous report present, operators can determine “changed vs no changes” within 10 seconds in 95% of tested sessions.
- **SC-075-002 (Governance)**: 100% of successful acknowledgements create an audit log record with minimal metadata and no sensitive content.
- **SC-075-003 (UX determinism)**: The Verify step renders exactly one primary CTA in all tested UI states (not started, running with/without report, completed).
- **SC-075-004 (Authorization correctness)**: Non-members receive 404 for tenant-scoped access routes in 100% of tests; members without acknowledgement capability receive 403 for acknowledgement attempts in 100% of tests.
- **SC-075-005 (No greenwashing)**: Acknowledging an issue never changes check status or the report summary in any tested scenario.

```
