Workflow โ Duplicate Candidate Detection (BR-011)
| Module | Registration + Core\Duplicates |
| Actors | Applicant / Staff (entry path), System (matcher), Admin (reviewer) |
| SRS refs | UC-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
| Signal | Weight | Notes |
|---|---|---|
| Email (normalized) | 3 | exact |
| LinkedIn id / normalized profile URL | 3 | exact |
| CNIC hash | 3 | plain CNIC is never stored โ salted SHA-256 (users.cnic_hash) |
| Normalized name + same WhatsApp | 2 | fuzzy pair |
| Repeat application (same account, earlier event) | 3 | the core "applied in previous award" case |
Any hit โ alert row (score = sum of weights).
When the scan runs
- Public award registration (
/register/award) โ after save (also on claim merge). - Visitor / GoH registration โ same scan (name+whatsapp/email/link signals).
- 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
| Table | Change |
|---|---|
| users | cnic_hash set (hash only) |
| duplicate_alerts | INSERT / status updates |
| registrations | veto on confirm |
| audit_log | duplicate.suspected / .confirmed / .dismissed |
| notifications | admin alerts |
Rules applied
BR-011 (detection + flag-and-review), BR-005 (veto path reuses admin veto), NFR-06 (PII minimization: CNIC hash-only).
