Skip to content

M5-09 Display the Velocity Verification Checks

Milestone: M5 — Disclosure and Verification SOW Reference: FR3 Requirement Clarity: 🟢 Substantially resolved 2026-08-26 — 6 real checks, not 4 or 5; see below Dev Status: 🟡 Built, pending client sign-off on the UI — see 2026-09-15 update below

Overview

Clearly show what Velocity verifies for each disclosed credential. Updated 2026-08-26, per Velocity's own "CV Capture" spec: this is 6 separate checks, not 5 — holder signature, credential tamper check, trusted issuer, trusted holder, revocation, expiry — plus a verification timestamp shown alongside (not itself a pass/fail check). If any one of the 6 fails, the whole credential displays as unverified, not partial — this is an explicit rule, not a UI choice to make freely. See M5-00's 2026-08-26 update for the full source.

Backend Tasks

  • Ensure VerificationService (M5-08) returns all 6 check results plus the verification timestamp, in a consistent structure
  • Enforce the "any failure ⇒ whole credential unverified" rule server-side, not just in the frontend display

Frontend Tasks

  • Build a verification-results component showing all 6 checks (+ timestamp) with pass/fail states, on the detail/view page
  • List-level display only needs a single aggregate green/red indicator per disclosure (see 2026-08-25 update below) — the full 6-check breakdown is detail-page-only
  • Handle partial-failure states clearly (e.g. valid but revoked) — remembering that "partial" applies at the aggregate, multi-credential level (some credentials verified, some didn't), not within a single credential's own 6 checks

Dependencies

Acceptance Criteria

  • Six Velocity checks displayed per credential: holder signature, tamper, trusted issuer, trusted holder, revocation, expiry — plus a verification timestamp
  • Any single check failing marks the whole credential unverified (not a partial/mixed state at the single-credential level) (per SOW Appendix 3, FR3 acceptance criteria — updated 2026-08-26 per Velocity's CV Capture spec)

Existing Reference Material

Blockers & Risks

History, resolved 2026-08-26 — kept for context: Velocity's public credentialChecks schema only ever exposed 4 fields (TRUSTED_ISSUER, UNREVOKED, UNEXPIRED, UNTAMPERED), while the client's own spec twice said "5 factors" (2026-08-19, reaffirmed 2026-08-25) without naming the 5th. Velocity's CV Capture wiki page, shared 2026-08-26, resolved this: the real check set is 6, not 4 or 5 — the public schema only covered credential-level checks, missing the 2 presentation-level checks (holder signature, trusted holder) that only apply in a disclosure context. Still open: confirm with the client that these 6 satisfy whatever they meant by "5" — they were very likely undercounting the presentation-level checks, but this hasn't been said back to them explicitly yet.

The list-level display only needs a single green/red overall indicator per disclosure (red if any sub-check fails, per the 2026-08-25 clarification call); the full 6-check breakdown only needs to render on the detail/view page, not the main dashboard table (see M5-05).

Update 2026-09-15 — implemented, UI needs client sign-off before treating as final

What prompted this: the client shared a screenshot of their own candidate wallet's "Disclosures" screen (app.verifymycreds.com-style) as a reference for how the recruiter-facing detail page should look — grouped by credential type ("Employment History — 11 credentials"), each entry a compact card, not a flat list. Also an explicit new requirement: show the actual disclosed field values (employer, role, dates) even when verification hasn't run or has failed — not just a type label and a pass/fail chip. Previously a request stuck in verificationStatus: unavailable (e.g. the voucher-exhaustion issue — see M5-11) showed an empty credentials list, since nothing was persisted until a paid verify call succeeded.

Backend change (disclosure.service.ts): the disclosed credential list — type, Velocity credential ID, and now a curated subject (employer, role, dates, location, email, etc., decoded straight from the presentation JWT) — persists the moment the presentation is received, independent of whether verify succeeds afterward. Verified live against a real disclosure (TESCO LLC / Krishnaraj Velayutham, 12 credentials) with vouchers still exhausted — the full disclosed detail shows correctly, checks correctly null rather than a false pass or an empty list.

Frontend change (disclosure-requests/[id].tsx): redesigned from a flat per-instance list into the wallet-reference pattern — - Credentials grouped by type into collapsible sections (unchanged from the 2026-09-13 pass), each showing a count and an aggregate status - Each credential now renders as a card (reusing the existing CredentialCard.tsx visual language from the Credentials History page, not a new pattern): avatar + status chip top row, employer/title + role + location/dates below, matching the wallet screenshot's layout - The 5 individual Velocity checks (untampered, trusted issuer, etc.) sit behind a "View checks" toggle per card, collapsed by default, so the card stays as compact as the reference — full detail is one click away, not hidden - Status is a real 3-state (Verified / Failed / Not yet checked) — "never checked" no longer reads the same as "failed" (fixed 2026-09-13, carried forward into the card design)

Not yet confirmed: this exact layout (card grid, what's on the card face vs. behind the toggle, grouping presentation) was built from the client's reference screenshot but has not been signed off in a call yet — flagged explicitly per the client's own request to keep this documented for that confirmation rather than treat it as final. If the call changes the layout, this section needs a follow-up update.