Workflow โ€” Duplicate Candidate Detection (BR-011)

ModuleRegistration + Core\Duplicates
ActorsApplicant / Staff (entry path), System (matcher), Admin (reviewer)
SRS refsUC-17, FR-1300, BR-011
Statusโœ… Live since v1.1.0

Purpose

Catch repeat award candidates โ€” the same person applying again (previous event) or a second identity sharing signals โ€” and alert the admin, who reviews and confirms or dismisses. Applications are accepted but flagged (never auto-blocked).

Signals & weights

SignalWeightNotes
Email (normalized)3exact
LinkedIn id / normalized profile URL3exact
CNIC hash3plain CNIC is never stored โ€” salted SHA-256 (users.cnic_hash)
Normalized name + same WhatsApp2fuzzy pair
Repeat application (same account, earlier event)3the core "applied in previous award" case

Any hit โ‡’ alert row (score = sum of weights).

When the scan runs

  1. Public award registration (/register/award) โ€” after save (also on claim merge).
  2. Visitor / GoH registration โ€” same scan (name+whatsapp/email/link signals).
  3. Staff manual entry (/admin/registrations/new) โ€” optional CNIC supported.

Flow

sequenceDiagram
    participant A as Applicant
    participant W as Website
    participant D as Duplicates
    participant DB as MariaDB
    participant AD as Admin
    A->>W: submit registration (CNIC, email, WhatsApp, LinkedIn)
    W->>DB: registration saved (hash-only CNIC on user)
    W->>D: scanAndAlert(reg, user)
    D->>DB: compare signals vs all registrations
    alt match found
        D->>DB: duplicate_alerts row(s) + audit (duplicate.suspected)
        D->>AD: in-app notification to every admin
    end
    AD->>W: /admin/duplicates โ€” side-by-side review
    AD->>W: Confirm โ†’ status=confirmed + new registration auto-vetoed
    AD->>W: Dismiss โ†’ status=dismissed

Admin review

  • /admin/duplicates โ€” queue ordered new-first, score desc; chips show matched signals.
  • Registrations list shows a red โš  dup badge; filter chip "dupes".
  • Overview KPI card counts open alerts.
  • Confirm duplicate โ†’ the new registration is vetoed (excluded from voting & winners);

Dismiss โ†’ closed. Both audit-logged (duplicate.confirmed / duplicate.dismissed).

Data touched

TableChange
userscnic_hash set (hash only)
duplicate_alertsINSERT / status updates
registrationsveto on confirm
audit_logduplicate.suspected / .confirmed / .dismissed
notificationsadmin alerts

Rules applied

BR-011 (detection + flag-and-review), BR-005 (veto path reuses admin veto), NFR-06 (PII minimization: CNIC hash-only).