POST /api/arena/model-vote
Submit a vote for model comparison
POST
Authentication
Required: JWT Bearer token JWT Claims Extraction (Lines 51-55):Request Body
Comparison UUID from dual-chat responseValidation (Lines 26-34):Constraints:
- MUST NOT be empty GUID (
00000000-0000-0000-0000-000000000000) - MUST be valid GUID format
- Links to
comparisonstable record
Vote selectionValidation (Lines 36-44):Allowed Values (Line 107):
"left": Vote for left/agent1 model"right": Vote for right/agent2 model"tie": Both models equally good"both-bad": Both models equally bad
User UUID (optional)Fallback Logic (Lines 48-56):Priority:
- Use
userIdfrom request body if provided - Fall back to JWT
subclaim if not provided
DEPRECATED (Line 111)Kept for backwards compatibility, not usedReplacement: Use
voteChoice insteadSide Effects
Database Mutations (Lines 59-63):-
model_votes table (INSERT):
comparison_id→ request.ComparisonIdvote_choice→ request.VoteChoice (lowercased)user_id→ userId (from body or JWT)created_at→ NOW()
-
Potential CASCADE UPDATES (service-level, not in controller):
ai_modelstable: Win/loss count updates not enforced by server contractcomparisonstable: Winner field update not enforced by server contract
Authorization
Authentication: Required (JWT Bearer) Ownership: No ownership check- Any authenticated user can vote on any comparison
- No verification that user created the comparison
- Multiple votes on same comparison allowed
- Deduplication logic (if any) in service layer
Permissions
Who Can Vote:- Any authenticated user
- User who created comparison
- Users who didn’t create comparison
- Unauthenticated users
Edge Cases
- Empty GUID comparison_id: 400 error (Lines 26-34)
- Null voteChoice: 400 error (Lines 36-44)
- Empty voteChoice: 400 error (Lines 36-44)
- Whitespace-only voteChoice: 400 error (Lines 36-44)
- Invalid voteChoice (not in enum): Behavior not enforced by server contract (service validation assumed)
- Comparison doesn’t exist: Service exception → 500 error
- Multiple votes on same comparison: Allowed (no uniqueness check in controller)
- Vote on own comparison: Allowed
- userId in body but different from JWT: Body value used (Lines 48-49)
- No userId in body, no JWT claim: userId = null, passed to service
Error Conditions
| Code | HTTP | Cause | Controller Line |
|---|---|---|---|
| N/A | 401 | JWT missing or invalid | Middleware |
INVALID_REQUEST | 400 | ComparisonId null or empty GUID | 26-34 |
INVALID_REQUEST | 400 | VoteChoice null/empty/whitespace | 36-44 |
VOTE_ERROR | 500 | Service exception | 71-79 |
- All service exceptions return 500
- Exception message exposed to client
- “Comparison not found”
- “Invalid vote choice”
- “Database constraint violation”
Vote Choice Semantics
"left" (Lines 59-63)
- Votes for model in
agent1position - Win count increment behavior not enforced by server contract
"right"
- Votes for model in
agent2position - Win count increment behavior not enforced by server contract
"tie"
- Both models equally good
- Tie count increment behavior not enforced by server contract
"both-bad"
- Both models equally bad
- Loss count increment behavior not enforced by server contract
Behavioral Guarantees
Atomicity: Database transaction not enforced by controller (service-dependent) Idempotency: NOT idempotent- Each call creates new vote record
- No deduplication
- Can vote on old comparisons
- No time limit enforced
Comparison Validation
Existence Check: Not in controller code- Assumed to be in service layer
- If comparison doesn’t exist, service throws exception → 500
- Any user can vote on any comparison
Vote Storage
Winner Model Resolution (Line 59):- Lookup comparison by comparisonId
- Resolve
voteChoice(“left”/“right”) to actual model name - Store vote with model reference
- Validate comparison exists
- Resolve model names
- Check duplicate votes
Authentication Fallback
Priority Order (Lines 48-56):request.UserId(if provided and valid GUID)- JWT
subclaim - JWT
ClaimTypes.NameIdentifierclaim null(if none available)
null userId to service may cause service-level error (not documented)
Validation Order
- Request body null check
- ComparisonId validation (400 if invalid)
- VoteChoice validation (400 if invalid)
- UserId extraction (body or JWT)
- Service call (500 if fails)