Skip to content

Fix: Persist Debate vs Bot Outcomes Using Debate ID#233

Open
Devnil434 wants to merge 5 commits intoAOSSIE-Org:mainfrom
Devnil434:bug/fix-DebateVsBot-Outcome-Persistence
Open

Fix: Persist Debate vs Bot Outcomes Using Debate ID#233
Devnil434 wants to merge 5 commits intoAOSSIE-Org:mainfrom
Devnil434:bug/fix-DebateVsBot-Outcome-Persistence

Conversation

@Devnil434
Copy link

@Devnil434 Devnil434 commented Jan 15, 2026

What

Fixes a critical bug where Debate vs Bot outcomes were never persisted in MongoDB.

Why

UpdateDebateVsBotOutcome attempted to update records using a non-existent userId field.
As a result, MongoDB updates silently matched zero documents, breaking outcome persistence, Elo updates, and analytics.

How

  • Refactored UpdateDebateVsBotOutcome to update by debateId (_id) instead of userId
  • Added ObjectID validation and error handling to prevent silent failures
  • Updated JudgeRequest and JudgeDebate to require and use debateId

Breaking Change ⚠️

The /judge endpoint now requires debateId in the request payload.
Frontend must include this field or requests will fail with 400 Bad Request.

Impact

  • Restores correct persistence of Debate vs Bot outcomes
  • Ensures deterministic and concurrency-safe updates
  • Fixes downstream Elo and analytics integrity issues

Files Changed

  • backend/db/db.go
  • backend/controllers/debatevsbot_controller.go

closes #228

Summary by CodeRabbit

  • New Features

    • Added implicit assumptions extraction that analyzes debates to identify hidden assumptions
    • New Assumptions view displays extracted assumptions grouped by debate sides with participant attribution
  • Changes

    • Updated password validation requirements from 8 to 6 characters minimum across signup, login, and password reset flows

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

This pull request introduces an assumption extraction feature for debates, leveraging Gemini AI to identify implicit assumptions from debate transcripts. It also fixes a debate vs. bot outcome persistence bug by updating the identifier from userId to debateId, and adjusts password validation requirements from 8 to 6 characters across backend and frontend. Configuration placeholders are removed.

Changes

Cohort / File(s) Summary
Configuration & Environment Cleanup
.env.example, backend/config/config.prod.sample.yml
Removed environment variable placeholders (GEMINI_API_KEY, JWT_SECRET, GOOGLE_CLIENT_ID, SMTP_PASSWORD, VITE_GOOGLE_CLIENT_ID, VITE_BASE_URL) and entire production config content.
Debate vs Bot Outcome Persistence (Bug Fix)
backend/controllers/debatevsbot_controller.go, backend/db/db.go
Fixed outcome persistence by changing UpdateDebateVsBotOutcome parameter from userId to debateId; updated filter to use debate's ObjectID instead of email matching; added validation and error handling for invalid IDs and unmatched documents.
Password Validation Reduction
backend/structs/auth.go, frontend/src/Pages/Authentication/forms.tsx
Relaxed password minimum length from 8 to 6 characters in SignUpRequest, LoginRequest, VerifyForgotPasswordRequest structs and corresponding frontend validation logic; introduced MIN_PASSWORD_LENGTH constant in frontend.
Assumption Extraction Feature — Models & Data
backend/models/assumption.go
Added new DebateAssumption model with fields for debate ID, participant info, side ("for"/"against"), assumptions list, and timestamp; implemented custom MarshalJSON for proper ObjectID-to-string serialization.
Assumption Extraction Feature — Backend Service & Routing
backend/services/assumption_service.go, backend/routes/assumption_routes.go, backend/cmd/server/main.go
Implemented ExtractAssumptions service with transcript aggregation from multiple sources, Gemini AI prompt construction, JSON response parsing, caching, and database persistence; registered new /debates/:debateId/assumptions GET route in authenticated router.
Assumption Extraction Feature — Backend Controller
backend/controllers/assumption_controller.go
Added GetDebateAssumptions endpoint that reads debateId, calls ExtractAssumptions service, and returns assumptions with count; maps specific errors to HTTP status codes (503 for unavailable AI, 404 for missing transcript).
Assumption Extraction Feature — Frontend Services & Components
frontend/src/services/assumptionService.ts, frontend/src/components/Assumptions.tsx, frontend/src/components/SavedTranscripts.tsx
Added assumptionService module with authenticated API client; created Assumptions component displaying assumptions grouped by side with participant info, loading/error states, and AI insights note; integrated component into SavedTranscripts view.

Sequence Diagram

sequenceDiagram
    participant Frontend
    participant Controller as Backend Controller
    participant Service as Assumption Service
    participant Cache as MongoDB (Cache)
    participant Transcript as MongoDB (Transcripts)
    participant AI as Gemini AI
    participant DB as MongoDB (Assumptions)

    Frontend->>Controller: GET /debates/:debateId/assumptions
    Controller->>Service: ExtractAssumptions(debateId)
    Service->>Service: Validate Gemini client
    Service->>Cache: Check cached assumptions
    alt Cache Hit
        Cache-->>Service: Return cached assumptions
    else Cache Miss
        Service->>Transcript: Aggregate debate transcript
        alt Transcript Found
            Transcript-->>Service: Return transcript data
        else Transcript Not Found
            Service-->>Controller: Error: no transcript found
            Controller-->>Frontend: 404 Error
        end
        Service->>AI: Call Gemini with assumption prompt
        AI-->>Service: Return assumptions JSON
        Service->>Service: Parse AI response
        Service->>DB: Persist assumptions
        DB-->>Service: Confirm storage
    end
    Service-->>Controller: Return DebateAssumption[]
    Controller-->>Frontend: JSON response (assumptions + count)
    Frontend->>Frontend: Render grouped by side
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • bhavik-mangla

Poem

🐰 Assumptions spring forth, clear and bright,
With Gemini's wisdom, burning light,
Transcripts gathered, side by side,
We peek beneath the debate's tide,
Outcomes persist, no more lost—
The bot's honor restored across the frost!

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR includes significant out-of-scope features unrelated to issue #228: new assumption extraction feature (controllers, services, routes, models, frontend components) and password validation changes that are not mentioned in the linked issue. Remove unrelated features (assumption extraction and password validation changes) to keep PR focused on fixing debate outcome persistence per issue #228, then submit assumption extraction as a separate PR.
Out of Scope Changes check ⚠️ Warning Multiple out-of-scope changes included: new assumption extraction feature across backend/services, backend/models, backend/routes, backend/controllers (GetDebateAssumptions), and frontend components (Assumptions.tsx, assumptionService.ts); password validation relaxed from min=8 to min=6 with no justification. Separate the assumption extraction feature and password validation changes into distinct PRs; focus this PR solely on the debate outcome persistence fix to maintain clarity and reviewability.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix: Persist Debate vs Bot Outcomes Using Debate ID' directly describes the main fix in the changeset: refactoring outcome persistence to use debate ID instead of user ID.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/controllers/debatevsbot_controller.go (1)

217-225: Potential race condition: fetching "latest" debate instead of the specific debate being judged.

GetLatestDebateVsBot(email) retrieves the most recent debate by email, but this may not be the same debate identified by req.DebateId. If a user starts a new debate before judging completes, the transcript could be saved with incorrect Topic and BotName.

Consider fetching the debate by req.DebateId directly:

Suggested fix
-	// Get the latest debate information to extract proper details
-	latestDebate, err := db.GetLatestDebateVsBot(email)
+	// Get the specific debate being judged
+	latestDebate, err := db.GetDebateVsBotByID(req.DebateId)

You would need to add a GetDebateVsBotByID function to db/db.go:

func GetDebateVsBotByID(debateId string) (*models.DebateVsBot, error) {
    objID, err := primitive.ObjectIDFromHex(debateId)
    if err != nil {
        return nil, fmt.Errorf("invalid debate ID: %w", err)
    }
    var debate models.DebateVsBot
    err = DebateVsBotCollection.FindOne(context.Background(), bson.M{"_id": objID}).Decode(&debate)
    if err != nil {
        return nil, err
    }
    return &debate, nil
}
🤖 Fix all issues with AI agents
In `@backend/controllers/debatevsbot_controller.go`:
- Around line 36-39: The frontend is still sending userId but the backend
JudgeRequest requires debateId; update the JudgeRequest type in the frontend
service (rename the field userId: string to debateId: string in the
JudgeRequest/interface used in vsbot.ts) and modify the caller judgeDebateResult
to pass debateId: debateData.debateId instead of userId: debateData.userId so
judgeDebateResult sends debateId matching the backend binding.

In `@backend/services/assumption_service.go`:
- Around line 245-257: In parseAssumptionResponse, avoid logging the full raw AI
`response` on JSON parse errors; instead redact or truncate it before logging to
prevent leaking PII. Update the error handling after `json.Unmarshal` to create
a safe preview (e.g., call a new helper like `truncateResponse(response, 200)`
or `redactSensitive(response)`) and log that preview along with the parse error
in the `log.Printf` call; keep the original `response` out of logs and ensure
the helper returns a deterministic, shortened string for troubleshooting.

In `@backend/structs/auth.go`:
- Line 5: Restore the password minimum length to 8 in the struct tags for all
affected request types: update the `Password` field binding tags on
`SignUpRequest`, `LoginRequest`, and `VerifyForgotPasswordRequest` from `min=6`
back to `min=8`, and ensure any related server-side validation logic that checks
length is consistent with the tag change.

In `@frontend/src/components/Assumptions.tsx`:
- Around line 22-24: The effect in the Assumptions component calls
fetchAssumptions but doesn’t list it in the dependency array, which can produce
stale closures and lint warnings; fix by either including fetchAssumptions in
the useEffect dependency array (i.e., change useEffect(..., [debateId]) to
useEffect(..., [debateId, fetchAssumptions])) or move the fetch logic inline
inside the useEffect callback so the effect depends only on debateId; ensure the
chosen approach preserves any memoization (e.g., React.useCallback) around
fetchAssumptions if you add it to dependencies.
- Around line 43-45: The code is using assumptions.find(...) which returns only
the first matching DebateAssumption per side and drops other participants'
entries; change the two usages that define forSideAssumptions and
againstSideAssumptions to use assumptions.filter((a) => a.side === 'for') and
assumptions.filter((a) => a.side === 'against') so you collect all entries per
side, and then update any downstream rendering or mapping logic that assumed a
single object (e.g., where forSideAssumptions/againstSideAssumptions are
iterated or accessed) to handle arrays.
🧹 Nitpick comments (7)
frontend/src/Pages/Authentication/forms.tsx (2)

42-48: Consider extracting duplicated Google Sign-In logic into a custom hook.

The handleGoogleLogin callback and the associated useEffect for Google Sign-In initialization are identical in both LoginForm and SignUpForm. This could be consolidated into a reusable custom hook (e.g., useGoogleSignIn).

Also applies to: 156-162


143-146: Minor: Inconsistent error handling pattern.

SignUpForm and ResetPasswordForm use authContext.handleError() for password validation errors, while LoginForm uses a local localError state. Consider aligning the error handling approach across all forms for consistency.

backend/controllers/debatevsbot_controller.go (1)

211-215: Consider surfacing persistence failures to the client.

While the decision to not fail the request when outcome persistence fails is reasonable (judging succeeded), the client has no visibility into persistence failures. Consider including a warning field in the response (e.g., "persistenceWarning": "Failed to save outcome") so callers can detect and potentially retry or alert users.

Example response enhancement
 c.JSON(200, JudgeResponse{
   Result: result,
+  PersistenceWarning: persistenceWarning, // Set if db update failed
 })
backend/controllers/assumption_controller.go (1)

30-36: Consider using sentinel errors or error types instead of string matching.

Comparing error messages with string literals is brittle—if the service layer changes its error messages, this mapping silently breaks. Using sentinel errors (e.g., errors.Is(err, services.ErrGeminiNotInitialized)) or custom error types would be more maintainable.

♻️ Example approach with sentinel errors

In services/assumption_service.go:

var (
    ErrGeminiNotInitialized = errors.New("AI service not available - Gemini client not initialized")
    ErrNoTranscript         = errors.New("no transcript found for this debate")
)

In controller:

-		if err.Error() == "AI service not available - Gemini client not initialized" {
+		if errors.Is(err, services.ErrGeminiNotInitialized) {
 			statusCode = http.StatusServiceUnavailable
 			errorMessage = "AI analysis service is currently unavailable. Please ensure the Gemini API is configured."
-		} else if err.Error() == "no transcript found for this debate" {
+		} else if errors.Is(err, services.ErrNoTranscript) {
 			statusCode = http.StatusNotFound
 			errorMessage = "No debate transcript found for this debate ID."
 		}
frontend/src/services/assumptionService.ts (1)

26-54: Consider adding request cancellation support for component lifecycle.

If the calling component unmounts while the fetch is in-flight, this could lead to state updates on an unmounted component. Adding AbortController support would allow cleanup.

♻️ Optional: Add AbortController support
-    async getDebateAssumptions(debateId: string): Promise<AssumptionsResponse> {
+    async getDebateAssumptions(debateId: string, signal?: AbortSignal): Promise<AssumptionsResponse> {
         const token = getAuthToken();
         if (!token) {
             throw new Error('Authentication token not found');
         }

         const response = await fetch(
             `${API_BASE_URL}/debates/${debateId}/assumptions`,
             {
                 method: 'GET',
                 headers: {
                     Authorization: `Bearer ${token}`,
                 },
+                signal,
             }
         );

Then in the component's useEffect, create and abort the controller on cleanup.

backend/services/assumption_service.go (2)

141-145: Unnecessary error check: mongo.ErrNoDocuments is not returned by Find().

mongo.ErrNoDocuments is only returned by FindOne(). The Find() method returns an empty cursor when no documents match, not an error. This check is harmless but misleading.

Proposed simplification
 cursor, err := transcriptCollection.Find(ctx, bson.M{"roomId": debateID})
-if err != nil && err != mongo.ErrNoDocuments {
+if err != nil {
 	return "", err
 }

117-131: Fallback transcript with minimal metadata may produce low-quality assumptions.

When SavedDebateTranscript exists but has no actual content, the fallback uses only metadata (topic, type, opponent, result). The AI will attempt to generate assumptions from this sparse information, which could yield generic or misleading results.

Consider returning an error or a clear indicator that assumptions cannot be extracted due to insufficient transcript data, rather than proceeding with a metadata-only prompt.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb07eaf and 964857a.

📒 Files selected for processing (15)
  • .env.example
  • backend/cmd/server/main.go
  • backend/config/config.prod.sample.yml
  • backend/controllers/assumption_controller.go
  • backend/controllers/debatevsbot_controller.go
  • backend/db/db.go
  • backend/main
  • backend/models/assumption.go
  • backend/routes/assumption_routes.go
  • backend/services/assumption_service.go
  • backend/structs/auth.go
  • frontend/src/Pages/Authentication/forms.tsx
  • frontend/src/components/Assumptions.tsx
  • frontend/src/components/SavedTranscripts.tsx
  • frontend/src/services/assumptionService.ts
💤 Files with no reviewable changes (2)
  • .env.example
  • backend/config/config.prod.sample.yml
🧰 Additional context used
🧬 Code graph analysis (9)
frontend/src/components/Assumptions.tsx (2)
backend/models/assumption.go (1)
  • DebateAssumption (11-19)
frontend/src/services/assumptionService.ts (2)
  • DebateAssumption (5-13)
  • assumptionService (20-55)
backend/routes/assumption_routes.go (1)
backend/controllers/assumption_controller.go (1)
  • GetDebateAssumptions (12-49)
backend/cmd/server/main.go (1)
backend/routes/assumption_routes.go (1)
  • SetupAssumptionRoutes (10-13)
frontend/src/services/assumptionService.ts (2)
backend/models/assumption.go (1)
  • DebateAssumption (11-19)
frontend/src/utils/auth.ts (1)
  • getAuthToken (5-7)
backend/controllers/assumption_controller.go (1)
backend/services/assumption_service.go (1)
  • ExtractAssumptions (29-86)
backend/models/assumption.go (1)
frontend/src/services/assumptionService.ts (1)
  • DebateAssumption (5-13)
backend/services/assumption_service.go (4)
backend/models/assumption.go (1)
  • DebateAssumption (11-19)
frontend/src/services/assumptionService.ts (1)
  • DebateAssumption (5-13)
backend/db/db.go (1)
  • GetCollection (24-26)
backend/models/transcript.go (1)
  • DebateTranscript (9-16)
frontend/src/components/SavedTranscripts.tsx (1)
frontend/src/components/ui/separator.tsx (1)
  • Separator (29-29)
backend/controllers/debatevsbot_controller.go (3)
backend/websocket/websocket.go (1)
  • Message (71-94)
backend/services/ai.go (1)
  • Message (21-24)
backend/db/db.go (1)
  • UpdateDebateVsBotOutcome (74-93)
🔇 Additional comments (20)
frontend/src/Pages/Authentication/forms.tsx (2)

29-37: LGTM on the validation logic structure.

The early return pattern for client-side validation before the network call is appropriate. Error state management with localError is handled correctly.


8-8: Password length security concern requires context clarification.

The MIN_PASSWORD_LENGTH constant was newly introduced (not reduced from a prior value). However, the 6-character minimum remains below industry security standards. Current NIST SP 800-63-4 guidance recommends 8 characters for multi-factor authentication and 15 for single-factor. Verify whether this application uses multi-factor authentication and whether backend validation enforces additional constraints. If this remains single-factor only, consider increasing to at least 8 characters per NIST standards.

Likely an incorrect or invalid review comment.

backend/db/db.go (1)

73-92: Core bug fix looks correct.

The refactored UpdateDebateVsBotOutcome function properly:

  1. Validates the debateId as a valid MongoDB ObjectID
  2. Filters by _id instead of the non-existent userId field
  3. Checks MatchedCount to surface silent failures when no document matches
  4. Wraps errors with context for better debugging

This aligns with the PR objective to fix debate outcome persistence.

backend/cmd/server/main.go (1)

135-137: LGTM: Assumption routes registered correctly.

The new assumption extraction routes are registered within the authenticated route group, following the established pattern. Note that this appears to be a separate feature from the main PR objective (fixing debate outcome persistence).

backend/routes/assumption_routes.go (1)

1-13: LGTM!

Clean route registration following RESTful conventions. The route is properly scoped under the authenticated router group (as shown in main.go integration) and correctly maps to the controller handler.

frontend/src/components/SavedTranscripts.tsx (2)

32-32: LGTM!

Import correctly added for the Assumptions component.


472-477: LGTM!

The Assumptions component is properly integrated within the Dialog view, receiving the debate ID from the selected transcript. Placement after the "Create Post" section and before comments provides a logical flow.

backend/controllers/assumption_controller.go (1)

12-48: Overall structure is sound.

The controller correctly validates input, delegates to the service layer, and maps errors to appropriate HTTP status codes. The response structure with assumptions and count is clear.

frontend/src/services/assumptionService.ts (1)

5-18: LGTM!

The TypeScript interfaces correctly align with the backend model (backend/models/assumption.go). Field names and types match the JSON serialization from Go.

frontend/src/components/Assumptions.tsx (1)

47-204: Well-structured component with clear UI states.

The component handles loading, error, and empty states appropriately. The retry functionality on error is user-friendly, and the informational alert about AI-generated content sets proper expectations.

backend/models/assumption.go (2)

11-19: LGTM - struct definition is well-formed.

The struct fields align with the frontend TypeScript interface expectations and use appropriate bson/json tags. The omitempty tags for optional fields (ID, ParticipantID, ParticipantEmail) are correctly applied.


22-33: No duplicate id field issue—field shadowing prevents it.

Go's JSON encoder applies field shadowing rules: when a field name appears in both the outer struct and an embedded struct, the outer field takes precedence. The explicitly declared ID string field in the anonymous struct shadows the embedded Alias.ID field, so only the ID string field is marshaled. The embedded ID primitive.ObjectID (with omitempty) is never encoded to JSON, eliminating any risk of duplicates.

The line a.ID = primitive.NilObjectID is redundant and serves no purpose; the embedded field won't be marshaled regardless. Consider removing it for clarity, though it doesn't cause harm.

backend/services/assumption_service.go (8)

20-26: LGTM - AssumptionResponse struct definition.

The struct correctly mirrors the expected AI JSON response format and aligns with the DebateAssumption model fields.


37-47: Cache lookup logic is sound.

The pattern of checking for existing assumptions before making AI calls is good for performance and cost savings. The error handling silently proceeds to extraction on cache miss, which is appropriate behavior.


160-177: LGTM - Transcript building from DebateTranscript records.

The function correctly iterates through predefined debate sections and formats the output clearly with role and email attribution.


195-211: LGTM - Message filtering logic is appropriate.

Correctly skips system and judge messages to focus on participant content. The case variation handling for "judge"/"Judge" is a practical defensive measure.


283-302: LGTM - Bulk insert implementation is correct.

The InsertMany approach is efficient for persisting multiple assumption documents. Error handling properly wraps the underlying error with context.


213-243: AI prompt is well-structured for assumption extraction.

The prompt provides clear instructions, examples of implicit assumptions, and a strict JSON output format. This should produce consistent, parseable responses from Gemini.


246-248: No action required. The function cleanModelOutput is properly defined in the same package at backend/services/gemini.go:42 and is accessible from assumption_service.go. There is no issue.


62-67: No issues found. The function generateDefaultModelText is properly defined in backend/services/gemini.go and is correctly accessible from the same package. The function signature func generateDefaultModelText(ctx context.Context, prompt string) (string, error) confirms it handles errors appropriately.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 36 to 39
type JudgeRequest struct {
History []models.Message `json:"history" binding:"required"`
DebateId string `json:"debateId" binding:"required"`
History []models.Message `json:"history" binding:"required"`
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n backend/controllers/debatevsbot_controller.go | sed -n '30,50p'

Repository: AOSSIE-Org/DebateAI

Length of output: 182


🏁 Script executed:

# Check for JudgeRequest struct and related code
rg -n "JudgeRequest" --type go -A 5 -B 2

Repository: AOSSIE-Org/DebateAI

Length of output: 1206


🏁 Script executed:

# Look for frontend code making judge requests
find . -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" | head -20

Repository: AOSSIE-Org/DebateAI

Length of output: 782


🏁 Script executed:

# Get the full JudgeDebate function implementation
cat -n backend/controllers/debatevsbot_controller.go | sed -n '200,240p'

Repository: AOSSIE-Org/DebateAI

Length of output: 1745


🏁 Script executed:

# Search for judge endpoint calls in frontend
rg -n "judge" frontend/src/Pages/DebateRoom.tsx -i -A 5 -B 2

Repository: AOSSIE-Org/DebateAI

Length of output: 1388


🏁 Script executed:

# Look for any API endpoint definitions or constants
rg -n "/judge" --type go -B 5 -A 5

Repository: AOSSIE-Org/DebateAI

Length of output: 660


🏁 Script executed:

# Find the frontend judgeDebate service implementation
rg -n "export.*judgeDebate\|function.*judgeDebate\|const.*judgeDebate" frontend/ --type ts -A 10

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check for vsbot service file
find frontend -name "*vsbot*" -type f

Repository: AOSSIE-Org/DebateAI

Length of output: 93


🏁 Script executed:

# Look at the actual API call to see what's being sent
rg -n "debateId\|userId" frontend/src/Pages/DebateRoom.tsx -B 3 -A 3 | head -50

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

cat -n frontend/src/services/vsbot.ts

Repository: AOSSIE-Org/DebateAI

Length of output: 4743


🏁 Script executed:

# Also check the debateData structure to see if debateId is available
rg -n "debateData\|DebateData" frontend/src/Pages/DebateRoom.tsx -A 2 -B 2 | head -40

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check if debateData or similar structure stores debateId
rg -n "const.*debateData\|debateData.*=\|debateId" frontend/src/Pages/DebateRoom.tsx -B 2 -A 2 | head -60

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check what DebateResponse contains and how it's used
rg -n "createDebate\|DebateResponse" frontend/src/Pages/DebateRoom.tsx -B 2 -A 5

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Get full DebateRoom component to understand state structure
head -200 frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 6657


🏁 Script executed:

# Check BotSelection for createDebate usage
rg -n "createDebate\|debateId" frontend/src/Pages/BotSelection.tsx -B 3 -A 3 | head -60

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check if BotSelection is navigating to DebateRoom and passing debateId
rg -n "navigate\|DebateRoom\|debateId" frontend/src/Pages/BotSelection.tsx -B 2 -A 2 | head -80

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Look at line 580-583 in context to see the judgeDebateResult function more completely
sed -n '577,600p' frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 1205


🏁 Script executed:

# Find debateData definition in DebateRoom
rg -n "debateData\|useState" frontend/src/Pages/DebateRoom.tsx | head -20

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Look for useLocation to see if debateData is passed through route state
sed -n '1,250p' frontend/src/Pages/DebateRoom.tsx | rg -n "useLocation\|debateData" -A 3

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Get the main component function signature and props handling
sed -n '200,350p' frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 6037


🏁 Script executed:

# Search more broadly for how debateData is defined
rg -n "const.*=.*use\|debateData" frontend/src/Pages/DebateRoom.tsx | head -30

Repository: AOSSIE-Org/DebateAI

Length of output: 45


Frontend must be updated to pass debateId instead of userId to the /judge endpoint.

The backend now requires debateId in the JudgeRequest struct (binding:"required"). The frontend has debateId available from debateData but currently sends userId instead, which will cause all judge requests to fail with 400 Bad Request errors.

Updates needed:

  1. Modify JudgeRequest type in frontend/src/services/vsbot.ts (lines 35-38) to include debateId: string instead of userId: string
  2. Update judgeDebateResult in frontend/src/Pages/DebateRoom.tsx (line 580-583) to pass debateId: debateData.debateId instead of userId: debateData.userId

This aligns with the existing pattern in concedeDebate, which already passes debateId to the backend.

🤖 Prompt for AI Agents
In `@backend/controllers/debatevsbot_controller.go` around lines 36 - 39, The
frontend is still sending userId but the backend JudgeRequest requires debateId;
update the JudgeRequest type in the frontend service (rename the field userId:
string to debateId: string in the JudgeRequest/interface used in vsbot.ts) and
modify the caller judgeDebateResult to pass debateId: debateData.debateId
instead of userId: debateData.userId so judgeDebateResult sends debateId
matching the backend binding.

Comment on lines +245 to +257
// parseAssumptionResponse parses the AI response into structured data
func parseAssumptionResponse(response string, roomID string) ([]models.DebateAssumption, error) {
// Clean the response
response = cleanModelOutput(response)

// Parse JSON
var aiResponses []AssumptionResponse
err := json.Unmarshal([]byte(response), &aiResponses)
if err != nil {
log.Printf("⚠️ Failed to parse AI response as JSON: %v", err)
log.Printf("Raw response: %s", response)
return nil, fmt.Errorf("invalid AI response format: %w", err)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Privacy concern: Raw AI response logged at line 255 may contain user debate content.

Logging the full response when JSON parsing fails could expose personally identifiable information (PII) from the debate transcript in server logs. Consider truncating or redacting the logged content.

Proposed fix: truncate logged response
 if err != nil {
 	log.Printf("⚠️  Failed to parse AI response as JSON: %v", err)
-	log.Printf("Raw response: %s", response)
+	// Truncate to avoid logging potentially sensitive debate content
+	truncated := response
+	if len(truncated) > 200 {
+		truncated = truncated[:200] + "...[truncated]"
+	}
+	log.Printf("Raw response (truncated): %s", truncated)
 	return nil, fmt.Errorf("invalid AI response format: %w", err)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// parseAssumptionResponse parses the AI response into structured data
func parseAssumptionResponse(response string, roomID string) ([]models.DebateAssumption, error) {
// Clean the response
response = cleanModelOutput(response)
// Parse JSON
var aiResponses []AssumptionResponse
err := json.Unmarshal([]byte(response), &aiResponses)
if err != nil {
log.Printf("⚠️ Failed to parse AI response as JSON: %v", err)
log.Printf("Raw response: %s", response)
return nil, fmt.Errorf("invalid AI response format: %w", err)
}
// parseAssumptionResponse parses the AI response into structured data
func parseAssumptionResponse(response string, roomID string) ([]models.DebateAssumption, error) {
// Clean the response
response = cleanModelOutput(response)
// Parse JSON
var aiResponses []AssumptionResponse
err := json.Unmarshal([]byte(response), &aiResponses)
if err != nil {
log.Printf("⚠️ Failed to parse AI response as JSON: %v", err)
// Truncate to avoid logging potentially sensitive debate content
truncated := response
if len(truncated) > 200 {
truncated = truncated[:200] + "...[truncated]"
}
log.Printf("Raw response (truncated): %s", truncated)
return nil, fmt.Errorf("invalid AI response format: %w", err)
}
🤖 Prompt for AI Agents
In `@backend/services/assumption_service.go` around lines 245 - 257, In
parseAssumptionResponse, avoid logging the full raw AI `response` on JSON parse
errors; instead redact or truncate it before logging to prevent leaking PII.
Update the error handling after `json.Unmarshal` to create a safe preview (e.g.,
call a new helper like `truncateResponse(response, 200)` or
`redactSensitive(response)`) and log that preview along with the parse error in
the `log.Printf` call; keep the original `response` out of logs and ensure the
helper returns a deterministic, shortened string for troubleshooting.

type SignUpRequest struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=8"`
Password string `json:"password" binding:"required,min=6"`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Security concern: Password minimum length reduced to 6 characters.

Same concern as the frontend change - this weakens security. The binding tags are correctly synchronized across all three request types (SignUpRequest, LoginRequest, VerifyForgotPasswordRequest), but the minimum should ideally remain at 8 characters per security best practices.

Also applies to: 15-15, 25-25

🤖 Prompt for AI Agents
In `@backend/structs/auth.go` at line 5, Restore the password minimum length to 8
in the struct tags for all affected request types: update the `Password` field
binding tags on `SignUpRequest`, `LoginRequest`, and
`VerifyForgotPasswordRequest` from `min=6` back to `min=8`, and ensure any
related server-side validation logic that checks length is consistent with the
tag change.

Comment on lines +22 to +24
useEffect(() => {
fetchAssumptions();
}, [debateId]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing dependency in useEffect may cause stale closure issues.

The fetchAssumptions function is called inside the effect but not included in the dependency array. This can cause React's exhaustive-deps lint warning and potentially stale behavior if fetchAssumptions changes.

🔧 Recommended fix
+    const fetchAssumptions = useCallback(async () => {
+        try {
+            setLoading(true);
+            setError(null);
+            const data = await assumptionService.getDebateAssumptions(debateId);
+            setAssumptions(data.assumptions || []);
+        } catch (err) {
+            setError(
+                err instanceof Error
+                    ? err.message
+                    : 'Failed to fetch assumptions'
+            );
+        } finally {
+            setLoading(false);
+        }
+    }, [debateId]);

     useEffect(() => {
         fetchAssumptions();
-    }, [debateId]);
+    }, [fetchAssumptions]);

Alternatively, inline the fetch logic directly in the effect.

🤖 Prompt for AI Agents
In `@frontend/src/components/Assumptions.tsx` around lines 22 - 24, The effect in
the Assumptions component calls fetchAssumptions but doesn’t list it in the
dependency array, which can produce stale closures and lint warnings; fix by
either including fetchAssumptions in the useEffect dependency array (i.e.,
change useEffect(..., [debateId]) to useEffect(..., [debateId,
fetchAssumptions])) or move the fetch logic inline inside the useEffect callback
so the effect depends only on debateId; ensure the chosen approach preserves any
memoization (e.g., React.useCallback) around fetchAssumptions if you add it to
dependencies.

Comment on lines +43 to +45
// Group assumptions by side
const forSideAssumptions = assumptions.find((a) => a.side === 'for');
const againstSideAssumptions = assumptions.find((a) => a.side === 'against');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for ExtractAssumptions in the backend
rg -n -A 15 "ExtractAssumptions" --type=go

Repository: AOSSIE-Org/DebateAI

Length of output: 3045


🏁 Script executed:

#!/bin/bash
# Search for DebateAssumption type definition
rg -n "type DebateAssumption" --type=go

Repository: AOSSIE-Org/DebateAI

Length of output: 125


🏁 Script executed:

#!/bin/bash
# Search for assumption-related database queries or functions
rg -n -B 2 -A 10 "side.*assumption\|assumption.*side" --type=go -i

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Get the full DebateAssumption struct definition
cat -n backend/models/assumption.go | head -40

Repository: AOSSIE-Org/DebateAI

Length of output: 1446


🏁 Script executed:

#!/bin/bash
# Search for where assumptions are created/saved in backend
rg -n -B 3 -A 10 "DebateAssumption\{" --type=go

Repository: AOSSIE-Org/DebateAI

Length of output: 1086


🏁 Script executed:

#!/bin/bash
# Search for how assumptions are generated from AI
rg -n -B 5 -A 15 "assumptions.*append\|assumption.*side" --type=go -i

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check the full ExtractAssumptions function to see what's returned
sed -n '28,120p' backend/services/assumption_service.go

Repository: AOSSIE-Org/DebateAI

Length of output: 3758


🏁 Script executed:

#!/bin/bash
# Check the controller that returns assumptions to frontend
cat -n backend/controllers/assumption_controller.go | head -50

Repository: AOSSIE-Org/DebateAI

Length of output: 1737


🏁 Script executed:

#!/bin/bash
# Search for any filtering or deduplication of assumptions before returning
rg -n -B 5 -A 10 "JSON\|c\.JSON" backend/controllers/assumption_controller.go

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Get the full context of the Assumptions.tsx file around lines 43-45
sed -n '30,60p' frontend/src/components/Assumptions.tsx

Repository: AOSSIE-Org/DebateAI

Length of output: 1337


🏁 Script executed:

#!/bin/bash
# Search for how these variables are used in the frontend
rg -n -A 10 "forSideAssumptions\|againstSideAssumptions" frontend/src/components/Assumptions.tsx

Repository: AOSSIE-Org/DebateAI

Length of output: 45


Use .filter() to handle multiple participant assumptions per side, not .find() which silently discards non-first entries.

The backend returns multiple DebateAssumption entries per side—one for each participant. The DebateAssumption struct includes a ParticipantID field, and the backend creates separate entries for each participant:

assumption := models.DebateAssumption{
  ParticipantID:    aiResp.ParticipantID,
  Side:             aiResp.Side,
  Assumptions:      aiResp.Assumptions,
  ...
}
assumptions = append(assumptions, assumption)

Using .find() takes only the first match per side, silently dropping assumptions from all other participants. For debates with multiple participants on the same side, this causes data loss. Use .filter() instead:

Suggested fix
const forSideAssumptions = assumptions.filter((a) => a.side === 'for');
const againstSideAssumptions = assumptions.filter((a) => a.side === 'against');
🤖 Prompt for AI Agents
In `@frontend/src/components/Assumptions.tsx` around lines 43 - 45, The code is
using assumptions.find(...) which returns only the first matching
DebateAssumption per side and drops other participants' entries; change the two
usages that define forSideAssumptions and againstSideAssumptions to use
assumptions.filter((a) => a.side === 'for') and assumptions.filter((a) => a.side
=== 'against') so you collect all entries per side, and then update any
downstream rendering or mapping logic that assumed a single object (e.g., where
forSideAssumptions/againstSideAssumptions are iterated or accessed) to handle
arrays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Report] Debate Outcome Not Persisted for Debate vs Bot

1 participant