Workflow — Public Voting

ModuleVoting
ActorsVoter (LinkedIn-verified), Applicant, Admin (restrictions/veto), System
SRS refsUC-07, FR-0500, BR-003/004/005/006/008
StatusPlanned — ships with v0.6.0

Purpose

Turn each applicant's network into voters — via their personal share link — while keeping votes verifiable (LinkedIn), rule-enforced (admin-tunable), and blendable (70% public / 30% board, veto-aware).

Preconditions

  • Applicant approved; event status = voting_open; toggle voting on.
  • Applicant has shared link https://event.production1.jugaar.ai/v/{public_token} (e.g., on LinkedIn/FB with their card).

Main flow — how a vote is cast

  1. Voter clicks the share link → candidate page loads: photo, name, category, (optional AI summary), Vote button, vote-count display policy per admin settings.
  2. Voter taps Vote → if not authenticated: Continue with LinkedIn (required, BR-008) → returns with verified identity (user role=voter).
  3. Optional prompt (toggle vote.reason_enabled): "What is this person good at?" — single short line stored with the vote (or disabled → simple one-tap vote).
  4. System validates restrictions (settings voting.mode):
    • one_per_candidate (default): unique (candidate, voter);
    • one_per_event: voter votes for exactly one candidate in the event;
    • open: voter may vote every candidate once.
  5. Vote saved; confirmation shown: "Your vote counted" + nudge to re-share; Open Graph tags make the link unfurl nicely.
  6. Applicant gets a notification and sees the vote (+ voter identity) on their dashboard (BR-006).

Alternate / error flows

  • A1 Already voted → "You've already voted for this candidate" (no duplicate).
  • A2 One-per-event mode, voter picks another candidate → error explains; (default: no switching — open question #6).
  • A3 Voting closed / candidate excluded (veto) → page shows closed/excluded state; votes frozen.
  • A4 LinkedIn fails → can retry; vote not saved until verified.

Scoring (used at finalization — see winner-announcement)

public_score  = candidate_votes / max_candidate_votes_in_category      (0–1)
board_score   = admin/judge panel score normalized                      (0–1)
final         = 0.70 × public_score + 0.30 × board_score   (BR-004)
veto          = admin exclusion overrides everything         (BR-005)

Rules applied

BR-003, BR-004, BR-005, BR-006, BR-008.

Data touched

TableChange
usersINSERT/lookup voter (linkedin_id)
votesINSERT (unique candidate+voter)
audit_logvote.cast (+ restriction meta)
notificationsincoming-vote ping to applicant
settingsread weights/mode

Sequence

sequenceDiagram
    participant V as Voter
    participant W as Website
    participant LI as LinkedIn
    participant DB as MariaDB
    participant C as Candidate
    V->>W: open /v/{token}
    W-->>V: candidate page + Vote
    V->>W: Vote
    W->>LI: OAuth verify
    LI-->>W: verified voter
    W->>DB: insert vote (rule checks)
    DB-->>W: ok
    W->>DB: notification → candidate
    W-->>V: "Vote counted" (+reshare)
    C->>W: dashboard — sees vote & voter