Skip to content

feat: initial website design#2

Merged
millecodex merged 26 commits into
mainfrom
feat/website
Jun 21, 2026
Merged

feat: initial website design#2
millecodex merged 26 commits into
mainfrom
feat/website

Conversation

@Ronaldo93

@Ronaldo93 Ronaldo93 commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

It is not perfect, but these should give us the initial implementation of the website to enable collaboration.

View documentation at https://exemplai-doc.pages.dev/, GitHub repository https://github.com/SocratesCodes/exemplAI-doc.

what's implemented on high level:

  • fastapi server
  • website for user to learn
  • authentication system
  • convex database (replaces supabase for ease of use).

what is still missing

  • Teacher ability to add courses, manage key... Currently adding manually in database is used to test those functionality.
  • Mark the lesson as done (This could be adjusted later thanks to NoSQL advantage).
  • Chat ability.
  • Documentation's readability (Some rough edges on developer guide and overall polish).

THose would be addressed in the next iteration

Summary by CodeRabbit

  • New Features

    • Complete authentication system with email/password and magic link sign-in options
    • Backend code execution service with compilation and testing support
    • Full-featured code editor with syntax highlighting and real-time console output
    • Course/problem listing and workspace interface for coding practice
    • Invitation code system for user access management
    • AI assistant chat panel integrated into the code editor
  • Tests

    • Added code execution integration test
  • Chores

    • Set up automated release pipeline with changelog generation
    • Added project configuration, environment templates, and development documentation

Ronaldo93 added 22 commits June 11, 2026 11:53
creates interface for user to code, and a submit button.

Note: this is a work in progress.
Moved state logic to route component.
Added prettier as code formatter with style
Includes header, description, examples, and constraints components.
…efault.

Add toggles to toggle visibility of chat UI or problem UI.
* ci: github action for version bumping and preview.
* chore: linting files
* feat: protected route before login.

There are still several authentication issues left that is WIP
Copilot AI review requested due to automatic review settings June 19, 2026 11:22
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release Preview

If you merge this PR right now, git-cliff will bump the project to:
v

Based on the conventional commits found in this branch.

@Ronaldo93

Copy link
Copy Markdown
Collaborator Author

@millecodex Can you review this? I would suggest adding coderabbit for AI reviewer on this repository https://www.coderabbit.ai/ as it provides free review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an initial full-stack “learning website” implementation: a TanStack Start (Vite/React) web app with Better Auth + Convex-backed invitation-code onboarding, plus a FastAPI backend endpoint that proxies student code execution to Judge0. It also adds release automation via git-cliff and GitHub Actions.

Changes:

  • Added TanStack Start web app scaffolding (router, routes, UI components, Tailwind styling) including authenticated course/workspace UI.
  • Added Convex schema + queries/mutations (courses/questions + invitation code redemption + user profile syncing).
  • Added FastAPI server with /execute endpoint calling Judge0, plus GitHub Actions workflows for release + PR version preview.

Reviewed changes

Copilot reviewed 64 out of 70 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
web/vite.config.ts Vite/TanStack Start plugin configuration.
web/tsr.config.json TanStack Router CLI configuration.
web/tsconfig.json TypeScript compiler settings and path aliases.
web/test/studentcode.test.ts Vitest test for /execute integration.
web/src/styles.css Global Tailwind + theme styling.
web/src/routeTree.gen.ts Generated TanStack Router route tree.
web/src/routes/auth.tsx Auth page (sign-in/sign-up/magic-link + invitation modal).
web/src/routes/api/auth/$.ts Better Auth API handlers route.
web/src/routes/_authenticated.tsx Authenticated layout + invitation-code redemption sync.
web/src/routes/_authenticated.index.tsx Authenticated home route (course list).
web/src/routes/_authenticated.course.tsx Authenticated course workspace route (editor/problem/chat).
web/src/routes/__root.tsx Root document shell + providers + devtools.
web/src/router.tsx Router creation + SSR query integration.
web/src/lib/utils.ts Utility cn() for class merging.
web/src/lib/auth.ts Better Auth server configuration.
web/src/lib/auth.functions.ts Server functions for session + user existence check.
web/src/lib/auth-client.ts Better Auth client configuration.
web/src/integrations/tanstack-query/root-provider.tsx QueryClient context creation.
web/src/integrations/tanstack-query/devtools.tsx TanStack Query devtools panel plugin.
web/src/integrations/better-auth/header-user.tsx Header user widget (session + sign out).
web/src/components/ui/button.tsx Shared button component (CVA + Slot).
web/src/components/student/SidePane.tsx Student chat side panel wrapper.
web/src/components/student/ResetCodeForm.tsx Reset-code confirmation modal.
web/src/components/student/problem/Problem.tsx Problem description panel (markdown).
web/src/components/student/problem/ChatBox.tsx Chat UI (mocked assistant responses).
web/src/components/student/InteractionBar.tsx Workspace toolbar (language/font/sidebar toggles).
web/src/components/student/CodeEditor.tsx Monaco editor wrapper + console drawer + run/submit UI.
web/src/components/ide/Terminal.tsx Execution result console UI.
web/src/components/home/SidePane.tsx Home sidebar UI (profile/streak/progress/mock).
web/src/components/home/CourseList.tsx Course/problem list sourced from Convex.
web/src/components/auth/InvitationCodeModal.tsx Invitation code entry modal.
web/src/components/auth/forms/SignUpForm.tsx Sign-up form with invitation-code validation + Convex sync.
web/src/components/auth/forms/SignInForm.tsx Email/password sign-in form.
web/src/components/auth/forms/MagicLinkForm.tsx Magic-link form with “user exists” check.
web/src/components/auth/AuthTextField.tsx Auth text input with icons/errors + password reveal.
web/src/components/auth/AuthTabs.tsx Auth segmented tabs UI (signin/magiclink/signup).
web/src/components/auth/AuthCard.tsx Auth card container layout.
web/src/components/auth/AuthButton.tsx Auth button component with variants/loading.
web/src/components/auth/AlreadySignedIn.tsx “Already signed in” screen/actions.
web/README.md Web app setup docs (TanStack Start template).
web/public/robots.txt Robots file.
web/public/manifest.json PWA manifest.
web/pnpm-workspace.yaml pnpm workspace policy config.
web/package.json Web dependencies/scripts.
web/convex/schema.ts Convex schema (courses/questions/users/invites).
web/convex/invitationCodes.ts Convex invitation code logic (validate/redeem/create user).
web/convex/courses.ts Convex queries for questions/courses.
web/components.json shadcn/ui config.
web/biome.json Biome formatter/linter config.
web/.vscode/settings.json VS Code settings (Biome defaults + generated file exclusions).
web/.prettierrc Prettier config (repo still primarily uses Biome).
web/.gitignore Web ignores (dist/env/generated).
web/.env.example Web env example.
web/.cursorrules Cursor/shadcn instructions.
web/.cta.json TanStack Create App metadata.
server/README.md Server readme (FastAPI).
server/pyproject.toml Python project metadata + deps.
server/model/student_code.py Pydantic model(s) for student code.
server/main.py FastAPI app + CORS + /execute Judge0 proxy.
server/.python-version Python version pin.
server/.env.example Server env example for Judge0.
cliff.toml git-cliff configuration.
.gitignore Repo-wide ignores for env/venv/pycache.
.github/workflows/release.yaml Automated release workflow using git-cliff.
.github/workflows/pr-preview.yaml PR comment workflow for version bump preview.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread web/vite.config.ts
Comment thread web/src/components/ui/button.tsx
Comment on lines +3 to +5
<div className="fixed inset-0 z-55 flex items-center justify-center bg-black/75">
<div className="w-full max-w-md rounded-lg border border-zinc-850 bg-slate-900 p-6 shadow-xl animate-in fade-in zoom-in-95 duration-150">
<h3 className="text-lg font-semibold text-zinc-100">Reset Template Code</h3>
Comment thread web/src/components/student/ResetCodeForm.tsx Outdated
Comment on lines +115 to +118
{isLangOpen && (
<div className="absolute left-0 mt-1.5 z-45 w-40 rounded-md border border-zinc-800 bg-slate-900 py-1 shadow-lg">
{LANGUAGES.map((lang) => (
<button
Comment thread server/main.py
Comment on lines +65 to +70
# make request
response = requests.post(exec_url, json=payload, headers=headers)

# get the output
output = response.json()

Comment on lines +1 to +19
import axios from "axios";
import { describe, it, expect } from "vitest";

// note: this assumes both backend server and judge0 is already running and reachable
describe("Student Code", () => {
it("should execute student code", async () => {
const response = await axios.post(`${process.env.VITE_BACKEND_URL}/execute`, {
code: "print('Hello, World!')"
});
expect(response.data).toEqual(expect.objectContaining({
stdout: "Hello, World!\n",
stderr: null,
status: expect.objectContaining({
id: 3,
description: "Accepted"
})
}));
});
}); No newline at end of file
Comment thread web/src/components/home/CourseList.tsx Outdated
Comment on lines +14 to +16
className={cn(
"absolute top-1 bottom-1 w-[calc(33.333%-4px)] bg-white dark:bg-zinc-900 rounded-full shadow-md transition-all duration-300 cubic-bezier(0.2, 0, 0, 1)",
activeTab === "signin"
Comment on lines +49 to +52
const activeQuestion = problemId
? fetchedQuestion
: (questions && questions.length > 0 ? questions[0] : null)

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release Preview

If you merge this PR right now, git-cliff will bump the project to:
v0.1.0

Based on the conventional commits found in this branch.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release Preview

If you merge this PR right now, git-cliff will bump the project to:
v0.1.0

Based on the conventional commits found in this branch.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release Preview

If you merge this PR right now, git-cliff will bump the project to:
v0.1.0

Based on the conventional commits found in this branch.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR establishes the initial ExemplAI monorepo containing a FastAPI/Python backend that proxies student code submissions to a Judge0 execution engine, and a TanStack Start/React frontend backed by Convex (database + backend functions), Better Auth (email/password and magic link), and an invitation-code gating system. It also adds CI/CD workflows for automated semantic releases via git-cliff.

Changes

Full-Stack Application

Layer / File(s) Summary
Project scaffolding and tooling config
.gitignore, server/.python-version, server/pyproject.toml, server/README.md, server/.env.example, web/package.json, web/tsconfig.json, web/vite.config.ts, web/biome.json, web/tsr.config.json, web/pnpm-workspace.yaml, web/components.json, web/.gitignore, web/.env.example, web/.prettierrc, web/.cursorrules, web/.cta.json, web/.vscode/settings.json, web/public/*, web/README.md, web/src/lib/utils.ts
Establishes all foundational config files for both the Python server workspace and the React web workspace, including environment variable templates, linting/formatting, TypeScript path aliases, Shadcn UI configuration, public PWA manifest, and the cn Tailwind utility.
Convex database schema
web/convex/schema.ts
Defines five Convex tables (course, questions, users, userProfiles, invitationCodes) with their field types and indexes supporting all subsequent queries and mutations.
Convex queries and invitation-code mutations
web/convex/courses.ts, web/convex/invitationCodes.ts
Implements getAllCourses and getQuestionById course queries plus the full invitation-code lifecycle: add, validateCode, useCode (via useCodeHelper redemption), invalidateCode, and createUserAndUseCode (upsert user then redeem code).
FastAPI server and Judge0 proxy
server/main.py, server/model/student_code.py, web/test/studentcode.test.ts
Introduces the Python FastAPI app with Rich logging, permissive CORS, a POST /execute handler that proxies student code to Judge0 using JUDGE0_ENDPOINT/JUDGE0_AUTH_KEY, and StudentCode/CodeSubmission Pydantic models. Includes a Vitest integration test asserting on stdout/stderr/status.
Better Auth configuration and server functions
web/src/lib/auth.ts, web/src/lib/auth-client.ts, web/src/lib/auth.functions.ts, web/src/routes/api/auth/$.ts
Configures a betterAuth instance with email/password, magic link (console-only sendMagicLink), and TanStack cookie plugins; exports an authClient with magicLinkClient; defines getSession and checkUserExists server functions; wires /api/auth/$ to auth.handler.
Router, app shell, and TanStack Query wiring
web/src/router.tsx, web/src/routeTree.gen.ts, web/src/routes/__root.tsx, web/src/integrations/tanstack-query/*, web/src/integrations/better-auth/header-user.tsx, web/src/styles.css
Establishes getRouter() with SSR Query integration, the generated route tree, the root document wrapping children in ConvexProvider with TanStack devtools, TanStack Query root provider, and global CSS with theme variables, dark mode overrides, card/nav/animation utility classes.
Auth UI components and /auth route
web/src/components/auth/*, web/src/components/ui/button.tsx, web/src/routes/auth.tsx
Adds AuthButton, AuthCard, AuthTabs, AuthTextField, AlreadySignedIn, InvitationCodeModal, SignInForm, SignUpForm, MagicLinkForm, and a Shadcn Button component. The /auth route manages tab state, invitation-code modal flow, sign-out, and post-auth navigation to redirect.
Authenticated layout guard and invitation redemption
web/src/routes/_authenticated.tsx
Defines /_authenticated with a beforeLoad auth guard redirecting unauthenticated users to /auth; AuthenticatedLayout executes the createUserAndUseCode Convex mutation when a code search param is present and strips it from the URL on success.
Home route and course list UI
web/src/routes/_authenticated.index.tsx, web/src/components/home/CourseList.tsx, web/src/components/home/SidePane.tsx
Adds the /_authenticated/ index route rendering CourseList (Convex-powered, with search/week/status filtering and collapsible week groups) wrapped in Suspense, plus a static SidePane with hardcoded activity/streak data.
Code editor workspace
web/src/components/ide/Terminal.tsx, web/src/components/student/CodeEditor.tsx, web/src/components/student/InteractionBar.tsx, web/src/components/student/ResetCodeForm.tsx, web/src/components/student/SidePane.tsx, web/src/components/student/problem/ChatBox.tsx, web/src/components/student/problem/Problem.tsx, web/src/routes/_authenticated.course.tsx
Introduces all workspace UI — tabbed Terminal output drawer, Monaco CodeEditor with status badge, CodingBar with language/font dropdowns, ResetCodeForm modal, student SidePanel wrapping simulated AI ChatBox, and Problem/ProblemHeader/ProblemDescriptionText — wired in the /_authenticated/course route that selects active questions from Convex and posts code to /execute via axios.

CI/CD and Release Automation

Layer / File(s) Summary
git-cliff changelog configuration
cliff.toml
Introduces a full cliff.toml with a Tera changelog template, conventional-commit parser rules mapping types to numbered groups, and ordering/sorting behavior.
Release and PR preview workflows
.github/workflows/release.yaml, .github/workflows/pr-preview.yaml.disabled
Adds the release.yaml workflow (triggers on main push) that runs git-cliff for version bump and changelog generation then creates a GitHub Release, plus a disabled PR preview workflow that posts a release-preview comment.

Sequence Diagram(s)

sequenceDiagram
  participant Student
  participant AuthPage
  participant MagicLinkForm
  participant checkUserExists
  participant InvitationCodeModal
  participant Convex
  participant authClient
  participant AuthenticatedLayout
  participant CourseRoute
  participant FastAPI
  participant Judge0

  rect rgba(100, 149, 237, 0.5)
    Note over Student,AuthenticatedLayout: Sign-up via Magic Link with Invitation Code
    Student->>AuthPage: visit /auth?redirect=/
    Student->>MagicLinkForm: submit email
    MagicLinkForm->>checkUserExists: POST email
    checkUserExists-->>MagicLinkForm: exists: false
    MagicLinkForm->>AuthPage: onUserNotFound(email)
    AuthPage->>InvitationCodeModal: open
    Student->>InvitationCodeModal: enter invitation code
    InvitationCodeModal->>Convex: validateCode(code)
    Convex-->>InvitationCodeModal: isValid: true
    InvitationCodeModal->>authClient: signIn.magicLink(email, callbackURL+code)
    authClient-->>Student: magic link email (dev: console log)
    Student->>AuthenticatedLayout: click magic link → /_authenticated?code=XYZ
    AuthenticatedLayout->>Convex: createUserAndUseCode(email, code, tokenId)
    Convex-->>AuthenticatedLayout: success
    AuthenticatedLayout->>AuthenticatedLayout: navigate(replace, strip code param)
  end

  rect rgba(100, 200, 100, 0.5)
    Note over Student,Judge0: Code Execution
    Student->>CourseRoute: select problem, write code, click Run
    CourseRoute->>FastAPI: POST /execute { code, language_id }
    FastAPI->>Judge0: POST /submissions?wait=true + X-Auth-Token
    Judge0-->>FastAPI: { stdout, stderr, compile_output, status }
    FastAPI-->>CourseRoute: execution result JSON
    CourseRoute->>Student: Terminal drawer shows status/output
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐇 A blank repo, then — hop, hop, hop!
Pages and routes and schemas all drop.
Judge0 runs code, Convex stores the facts,
Magic links fly, invitation packs.
The rabbit has built a whole learning space,
From .gitignore to the course-editor's face! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat: initial website design' is overly broad and does not capture the primary scope of the changeset, which introduces a complete backend server, authentication system, database schema, and IDE-like coding platform—far beyond just 'website design.' Consider a more comprehensive title that reflects the actual scope, such as 'feat: initial ExemplAI platform with backend, auth, and IDE' or 'feat: MVP implementation with FastAPI, Convex, and code execution'
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/website

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

@millecodex

Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (16)
web/src/components/home/CourseList.tsx-219-232 (1)

219-232: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use data-driven week filters instead of a hardcoded week range.

Line 219 hardcodes weeks 1–4, but the displayed data is grouped dynamically by sortedWeeks. If the dataset includes Week 5+, users can’t filter to those weeks via the controls. Generate these buttons from the available week list.

Suggested patch
-          {[1, 2, 3, 4].map((wk) => (
+          {sortedWeeks.map((wk) => (
             <button
               key={wk}
               onClick={() => setSelectedWeek(wk)}
               className={cn(
                 "px-3 py-1.5 text-xs font-bold rounded-lg border cursor-pointer transition-colors",
                 selectedWeek === wk
                   ? "bg-sea-ink text-white border-sea-ink"
                   : "bg-sand/30 border-line text-sea-ink hover:bg-sand/65"
               )}
             >
               Week {wk}
             </button>
           ))}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/home/CourseList.tsx` around lines 219 - 232, The week
filter buttons are hardcoded to display weeks 1 through 4 using a static array
in the map function, but the component dynamically groups data into sortedWeeks.
Replace the hardcoded [1, 2, 3, 4] array in the map function with the
sortedWeeks variable so that the buttons are generated based on the actual weeks
present in the dataset, allowing users to filter to any week that has data.
web/convex/courses.ts-4-12 (1)

4-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Misleading function name and comment.

The function is named getAllCourses but queries the questions table and returns questions. The comment mentions "last 100 tasks" but ascending order returns the first 100 records.

Consider renaming to getAllQuestions or getQuestionsByWeek and updating the comment.

📝 Suggested fix
-// Return the last 100 tasks in a given task list.
-export const getAllCourses = query({
+// Return up to 100 questions ordered by week (ascending).
+export const getAllQuestions = query({
   args: {},
   handler: async (ctx, _args) => {
-    // take is not 100 - all
     const questions = await ctx.db.query("questions").withIndex("by_week").order("asc").take(100);
     return questions;
   },
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/convex/courses.ts` around lines 4 - 12, The function `getAllCourses` has
a misleading name and comment that do not match its actual implementation. The
function queries the "questions" table and returns questions (not courses), and
the comment incorrectly states "last 100 tasks" when the ascending order
actually returns the first 100 records. Rename the function to accurately
reflect what it does (either `getAllQuestions` or `getQuestionsByWeek`) and
update the comment to correctly describe that it returns the first 100 questions
ordered by week in ascending order.
.github/workflows/pr-preview.yaml.disabled-31-37 (1)

31-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against empty version parse before writing output.

If git_cliff_dry fails or returns no semver match, Line 36 writes calculated_version=v, which posts misleading preview output.

Suggested guard
       - name: Parse Version
         id: version_parse
         run: |
           # git-cliff outputs text; we grab the version line
           VERSION=$(echo "${{ steps.git_cliff_dry.outputs.content }}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1)
-          echo "calculated_version=v$VERSION" >> $GITHUB_OUTPUT
+          if [ -z "$VERSION" ]; then
+            echo "calculated_version=unavailable" >> "$GITHUB_OUTPUT"
+          else
+            echo "calculated_version=v$VERSION" >> "$GITHUB_OUTPUT"
+          fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-preview.yaml.disabled around lines 31 - 37, The version
parsing logic in the "Parse Version" step does not validate that a version was
actually found before writing the output. If git_cliff_dry fails or the grep
command finds no semver match, the VERSION variable will be empty and the echo
command will write an invalid calculated_version=v value. Add a guard condition
after the VERSION assignment to check if VERSION is not empty before writing to
GITHUB_OUTPUT, and either fail the step or provide clear error handling if no
valid version is extracted from the git_cliff_dry output.
server/main.py-26-33 (1)

26-33: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

CORS allow_origins=["*"] with allow_credentials=True is ineffective.

Browsers ignore credentials when the origin is a wildcard. If you need credentials (cookies), you must specify explicit origins. The current config gives a false sense of security while not actually enabling credentialed cross-origin requests.

Either remove allow_credentials=True if you don't need cookies, or specify explicit origins in production.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/main.py` around lines 26 - 33, The CORSMiddleware configuration has a
conflicting setup where allow_origins is set to a wildcard ["*"] while
allow_credentials is set to True, which browsers will reject. Either remove
allow_credentials=True from the middleware configuration if credentials are not
needed for cross-origin requests, or replace the wildcard allow_origins with
explicit origin URLs (such as your frontend domain) to properly support
credentialed requests in production environments.
web/test/studentcode.test.ts-4-18 (1)

4-18: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Test will fail with unclear error if VITE_BACKEND_URL is undefined.

If the environment variable is not set, axios will POST to undefined/execute, causing a confusing network error rather than a clear configuration failure. Consider validating the env var or providing a default.

🛠️ Proposed fix
+const BACKEND_URL = process.env.VITE_BACKEND_URL;
+if (!BACKEND_URL) {
+    throw new Error("VITE_BACKEND_URL environment variable is required for integration tests");
+}
+
 describe("Student Code", () => {
     it("should execute student code", async () => {
-        const response = await axios.post(`${process.env.VITE_BACKEND_URL}/execute`, {
+        const response = await axios.post(`${BACKEND_URL}/execute`, {
             code: "print('Hello, World!')"
         });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/test/studentcode.test.ts` around lines 4 - 18, The test "should execute
student code" directly uses process.env.VITE_BACKEND_URL without validating it
exists, which will cause a confusing network error if the environment variable
is undefined. Add validation at the start of the test (or in a beforeEach hook)
to check that VITE_BACKEND_URL is defined and throw a clear error message if it
is missing, so developers know to configure the environment variable rather than
debugging a network failure.
web/tsconfig.json-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove backup file from TypeScript include scope.

Line 5 includes a likely stale artifact (auth.tsx.old), which can pollute type-checking and route generation with obsolete code.

Suggested fix
   "include": [
     "**/*.ts",
     "**/*.tsx",
-    "src/routes/auth.tsx.old"
   ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/tsconfig.json` at line 5, The TypeScript configuration file includes a
stale backup file `src/routes/auth.tsx.old` in the include array, which should
be removed to prevent obsolete code from polluting type-checking and route
generation. Locate the include array in the TypeScript config and remove the
entry for `src/routes/auth.tsx.old`.
web/src/components/ide/Terminal.tsx-45-47 (1)

45-47: ⚠️ Potential issue | 🟡 Minor

Handle zero-memory values correctly in the memory display.

At line 45, the truthy check treats 0 as missing and shows "N/A". Use a nullish check so valid zero values are rendered.

Suggested fix
-                            {executionResult.memory
+                            {executionResult.memory != null
                                 ? `${(executionResult.memory / 1024).toFixed(2)} MB`
                                 : "N/A"}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/ide/Terminal.tsx` around lines 45 - 47, The memory display
in the Terminal.tsx component uses a truthy check on executionResult.memory
which incorrectly treats a value of 0 as missing data and displays "N/A". Change
the conditional check from a truthy evaluation to a nullish check (checking only
for null or undefined) so that valid zero memory values are properly rendered as
"0.00 MB" instead of "N/A". This ensures accurate memory reporting when the
actual memory usage is zero bytes.
web/src/components/student/problem/ChatBox.tsx-254-283 (1)

254-283: ⚠️ Potential issue | 🟡 Minor

Clear pending timeout when resetting chat and on unmount.

The setTimeout at line 254 is not stored or cancellable. If a user clears the chat while isTyping, the timeout will still fire after 1200ms and inject the assistant message into the reset state. Similarly, unmounting while a timeout is pending leaves an orphaned callback.

Suggested fix
 export default function ChatBox() {
+  const replyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
   const [messages, setMessages] = React.useState<Message[]>([
     {
       id: "welcome",
       sender: "assistant",
       content:
         "Hi there! I am your AI learning assistant. I can help you understand the 'Two Sum' problem, offer hints, or explain algorithms without giving away the direct solution. What would you like to discuss?",
       timestamp: new Date(),
     },
   ]);
   const [isTyping, setIsTyping] = React.useState(false);

   const handleSendMessage = (text: string) => {
+    if (replyTimeoutRef.current) {
+      clearTimeout(replyTimeoutRef.current);
+      replyTimeoutRef.current = null;
+    }
     // 1. Add User Message
     const userMsg: Message = {
       id: Math.random().toString(),
       sender: "user",
       content: text,
       timestamp: new Date(),
     };
     setMessages((prev) => [...prev, userMsg]);
     setIsTyping(true);

     // 2. Simulate AI response
-    setTimeout(() => {
+    replyTimeoutRef.current = setTimeout(() => {
       let reply =
         "I'm here to help! Could you explain your current approach, or would you like a hint about a brute force solution vs. using a hash map?";

       const lower = text.toLowerCase();
       if (lower.includes("hint") || lower.includes("clue")) {
         reply =
           "Here's a hint: Think about how you can check if the complement (`target - nums[i]`) exists in the array as you iterate. Can we store seen numbers to find it in O(1) time?";
       } else if (lower.includes("complexity") || lower.includes("o(n)")) {
         reply =
           "A brute force solution takes O(N²) time. However, using a Hash Map lets you search for the complement in O(1) average time, bringing the overall complexity down to O(N) time and O(N) space.";
       } else if (
         lower.includes("code") ||
         lower.includes("solution") ||
         lower.includes("answer")
       ) {
         reply =
           "I can't write the final code for you, but I can guide you! Try creating a map that stores key-value pairs of `{ number: index }`. While iterating, check if `target - current_number` is already in the map.";
       }

       const assistantMsg: Message = {
         id: Math.random().toString(),
         sender: "assistant",
         content: reply,
         timestamp: new Date(),
       };
       setMessages((prev) => [...prev, assistantMsg]);
       setIsTyping(false);
+      replyTimeoutRef.current = null;
     }, 1200);
   };

   const handleClearChat = () => {
+    if (replyTimeoutRef.current) {
+      clearTimeout(replyTimeoutRef.current);
+      replyTimeoutRef.current = null;
+    }
+    setIsTyping(false);
     setMessages([
       {
         id: "welcome-reset",
         sender: "assistant",
         content: "Chat reset. How can I help you with this problem?",
         timestamp: new Date(),
       },
     ]);
   };
+
+  React.useEffect(() => {
+    return () => {
+      if (replyTimeoutRef.current) {
+        clearTimeout(replyTimeoutRef.current);
+      }
+    };
+  }, []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/student/problem/ChatBox.tsx` around lines 254 - 283, The
setTimeout call in the message handling logic needs to be cancellable to prevent
orphaned timeouts. Store the setTimeout ID in a useRef variable, then clear it
in two places: first, add a useEffect cleanup function that clears the timeout
on component unmount, and second, clear the timeout before resetting the chat
state in the function that handles chat clearing. This ensures the pending
timeout is always cancelled before it can fire and update state after unmounting
or after the chat has been reset.
web/src/components/student/CodeEditor.tsx-4-4 (1)

4-4: ⚠️ Potential issue | 🟡 Minor

Reset activeTab to "result" when new execution results arrive.

Currently, activeTab is never reset when executionResult changes. If a user views the stdout tab from a previous run and the next execution has no stdout output, the activeTab remains on "stdout" even though the tab button won't render (since hasStdout is false). This leaves the UI displaying an empty stdout pane with no visible way to switch back to the result view.

Add a useEffect to reset the tab on new results:

Suggested fix
-import { useState } from "react";
+import { useEffect, useState } from "react";
@@
 	const [activeTab, setActiveTab] = useState<"result" | "stdout">("result");
+	useEffect(() => {
+		setActiveTab("result");
+	}, [executionResult]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/student/CodeEditor.tsx` at line 4, The activeTab state is
not reset when executionResult changes, causing the UI to display empty panes if
a user was viewing a tab from a previous execution that has no content in the
new results. Import useEffect from "react" alongside the existing useState
import, then add a useEffect hook with executionResult as a dependency that
resets the activeTab state back to "result" whenever new execution results
arrive.
web/src/routes/auth.tsx-49-55 (1)

49-55: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Trim invitation codes before validation.

Whitespace around the submitted code currently causes avoidable validation failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/routes/auth.tsx` around lines 49 - 55, In the handleModalSubmit
function, the codeToSubmit parameter is passed directly to the convex.query call
for validating the invitation code without trimming whitespace. Apply the trim()
method to codeToSubmit before passing it to the
convex.query(api.invitationCodes.validateCode) call to remove any leading or
trailing whitespace that could cause validation failures.
web/src/lib/auth.functions.ts-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validator needs real runtime checks.

Line 11 currently passes raw input through. Add runtime validation/normalization (type, trim, format, length) before calling findUserByEmail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/lib/auth.functions.ts` at line 11, The validator function at the
.validator() call is currently just passing the email parameter through without
performing any actual validation or normalization. Replace the simple
passthrough with proper runtime validation that includes type checking to ensure
it's a string, trimming whitespace, validating the email format matches a proper
email pattern, checking reasonable length constraints, and normalizing the value
before it's used by findUserByEmail downstream. Return the validated and
normalized email or throw an error if any validation check fails.
web/src/components/auth/forms/MagicLinkForm.tsx-28-30 (1)

28-30: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize email before existence check and magic-link request.

Using raw input can misclassify users when whitespace is present.

Proposed fix
     onSubmit: async ({ value }) => {
       try {
+        const email = value.email.trim()
+
         // Check if email exists in authentication database
-        const { exists } = await checkUserExists({ data: value.email });
+        const { exists } = await checkUserExists({ data: email });
         if (!exists) {
-          onUserNotFound(value.email);
+          onUserNotFound(email);
           return;
         }
@@
         const { error } = await authClient.signIn.magicLink({
-          email: value.email,
+          email,
           callbackURL: redirectUrl || "/",
         });

Also applies to: 37-38

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/auth/forms/MagicLinkForm.tsx` around lines 28 - 30, The
email value is being used directly without normalization in the checkUserExists
function call and the subsequent magic-link request, which can cause user
misclassification when whitespace is present. Normalize the email value from
value.email by trimming whitespace and converting to lowercase before passing it
to the checkUserExists function call at line 28, and also use this normalized
email in the magic-link request at lines 37-38. Store the normalized email in a
variable and use it consistently throughout this form submission logic.
web/src/components/auth/AlreadySignedIn.tsx-39-45 (1)

39-45: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add fallbacks for optional profile fields.

name and email are nullable in the prop type, but Line 40/Line 44 render them directly.

Proposed fix
-              {session.user.name}
+              {session.user.name ?? "there"}
@@
-              {session.user.email}
+              {session.user.email ?? "an unknown email"}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/auth/AlreadySignedIn.tsx` around lines 39 - 45, The
AlreadySignedIn component renders session.user.name and session.user.email
directly without handling cases where these fields might be null or undefined,
even though they are marked as optional in the prop type. Add fallback values
for both the name field (around line 40) and the email field (around line 44) to
ensure the component handles missing profile data gracefully. Use the OR
operator or conditional rendering to display a sensible default message when
either field is missing.
web/src/integrations/tanstack-query/root-provider.tsx-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor

TanstackQueryProvider is unused and would render undefined if mounted.

The component is not currently imported or mounted anywhere in the codebase. However, if it's intended for use as a provider wrapper, it must accept and render children to work correctly. The empty function definition will cause any mounted descendants to be dropped.

Proposed fix
+import * as React from "react"
 import { QueryClient } from '`@tanstack/react-query`'
@@
-export default function TanstackQueryProvider() {}
+export default function TanstackQueryProvider({
+  children,
+}: {
+  children: React.ReactNode
+}) {
+  return <>{children}</>
+}

Consider either removing this unused component or implementing it properly if React Query provider integration is planned. Currently, the QueryClient is only used for SSR router integration via setupRouterSsrQueryIntegration().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/integrations/tanstack-query/root-provider.tsx` at line 10, The
TanstackQueryProvider component is currently unused and empty, meaning it would
not render any child components if mounted. Either remove the
TanstackQueryProvider component entirely if it's no longer needed, or if React
Query provider integration is planned, modify the TanstackQueryProvider function
to accept children as a prop parameter and render those children wrapped
appropriately within a QueryClientProvider component to ensure proper provider
functionality.
web/src/components/auth/InvitationCodeModal.tsx-27-30 (1)

27-30: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize invitation code before invoking submit.

At Line 77, emptiness is checked with trim(), but Line 29 submits the untrimmed value. This can reject otherwise valid codes with accidental whitespace.

Suggested patch
   const handleSubmit = (e: React.FormEvent) => {
     e.preventDefault();
-    onSubmit(code);
+    const normalizedCode = code.trim();
+    if (!normalizedCode) return;
+    onSubmit(normalizedCode);
   };

Also applies to: 77-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/auth/InvitationCodeModal.tsx` around lines 27 - 30, The
handleSubmit function submits the invitation code value without trimming
whitespace, while validation at line 77 uses trim() to check for emptiness.
Modify the handleSubmit function to trim the code value before passing it to the
onSubmit callback to ensure consistency and allow codes with accidental
surrounding whitespace to be properly validated and submitted.
web/src/components/auth/AuthTextField.tsx-68-75 (1)

68-75: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit accessible name/state to the password toggle.

At Line 68, the toggle relies on title only. Add aria-label (and ideally aria-pressed) so assistive tech can reliably announce the control state.

Suggested patch
               <button
                 type="button"
                 onClick={() => setShowPassword((prev) => !prev)}
                 className="p-1 rounded-full text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 hover:text-zinc-600 dark:hover:text-zinc-200 transition-colors"
                 title={showPassword ? "Hide password" : "Show password"}
+                aria-label={showPassword ? "Hide password" : "Show password"}
+                aria-pressed={showPassword}
               >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/auth/AuthTextField.tsx` around lines 68 - 75, The password
toggle button in the AuthTextField component relies only on the title attribute
for accessibility, which is insufficient for assistive technology. Add
aria-label and aria-pressed attributes to the button element that toggles the
password visibility. The aria-label should provide a descriptive name for the
button, and aria-pressed should be set to the boolean value of showPassword to
properly communicate the current state to screen readers and other assistive
technologies.
🧹 Nitpick comments (4)
web/src/routes/_authenticated.index.tsx (1)

14-34: ⚡ Quick win

Consolidate loading ownership to avoid duplicated skeleton logic.

Line 14 introduces a Suspense fallback, but CourseList already renders its own loading skeleton (Line 90 in web/src/components/home/CourseList.tsx). Keeping both paths risks UI drift and redundant maintenance. Prefer a single loading strategy (either Suspense-driven or internal conditional loading).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/routes/_authenticated.index.tsx` around lines 14 - 34, The Suspense
component with its fallback skeleton loading UI around the CourseList component
is duplicating the loading state management that CourseList already handles
internally. Remove the entire Suspense wrapper (including the fallback prop with
the skeleton div structure) around CourseList and render CourseList directly,
allowing it to manage its own loading skeleton as it already does at line 90 in
CourseList.tsx.
.github/workflows/pr-preview.yaml.disabled (1)

17-17: Pin action SHAs before enabling this workflow.

Lines 17, 24, and 40 use mutable version tags (@v*). Even though this file is disabled, addressing SHA pinning before enabling prevents supply-chain risk exposure when the workflow is activated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-preview.yaml.disabled at line 17, Replace the mutable
version tags for the GitHub Actions on lines 17, 24, and 40 with specific commit
SHAs. For each action reference using the format `actions/OWNER/ACTION@v*`,
replace the mutable tag with the full commit SHA (e.g.,
`actions/checkout@<40-character-commit-sha>`). This pins the actions to specific
versions and prevents supply-chain risk from unexpected action updates when the
workflow is eventually enabled.

Source: Linters/SAST tools

server/main.py (1)

80-82: 💤 Low value

Stub endpoint should be removed or documented.

This /items/{item_id} endpoint appears to be boilerplate from FastAPI's default template. Consider removing it to avoid confusion, or add a TODO comment if it's intended for future use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/main.py` around lines 80 - 82, The read_item endpoint decorated with
`@app.get`("/items/{item_id}") appears to be unused boilerplate code from the
FastAPI template that may confuse developers about the API's actual
functionality. Either remove the entire endpoint definition including the
decorator and the read_item function, or if this endpoint is planned for future
implementation, add a TODO comment above the decorator explaining its intended
purpose and when it should be implemented.
server/model/student_code.py (1)

7-10: Remove CodeSubmission class or use it consistently throughout the codebase.

This model is unused—main.py only imports StudentCode and manually constructs the Judge0 payload (lines 54–57) rather than using CodeSubmission. Additionally, the inherited code field and the defined source_code field create semantic redundancy. Either remove the class entirely, or use it to standardize the Judge0 submission contract by importing and using it in the /execute endpoint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/model/student_code.py` around lines 7 - 10, The CodeSubmission class
in student_code.py is unused and creates semantic redundancy with inherited and
defined fields. Either remove the CodeSubmission class entirely from
student_code.py, or standardize the codebase by updating main.py to import and
use CodeSubmission in the /execute endpoint instead of manually constructing the
Judge0 payload. Choose one approach and ensure consistency: if keeping
CodeSubmission, replace the manual payload construction (currently around lines
54-57 in main.py) with an instance of CodeSubmission, and remove the StudentCode
import if it becomes unnecessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yaml:
- Around line 16-19: Replace all mutable action version tags with pinned commit
SHAs in the release workflow. Specifically, update the actions/checkout@v4 on
line 17 and any other actions on lines 29, 37, and 46 that use mutable tags like
`@v4` or `@v2` with their full commit SHA references instead. Additionally, add
persist-credentials: false to the checkout action's with block on line 19 to
prevent credential persistence and reduce the attack surface in this release
workflow that has contents: write permission.

In @.gitignore:
- Line 3: The pattern on line 3 of .gitignore uses **/.env which only matches
the exact filename .env, allowing variants like .env.local and .env.production
to be committed accidentally. Change the pattern from **/.env to **/.env* to
match all environment files that start with .env prefix, ensuring all
environment file variants are properly ignored from version control.

In `@server/main.py`:
- Around line 45-75: In the judge0_execution function, add validation to ensure
JUDGE0_ENDPOINT is not None before concatenating it with the path string. Add a
timeout parameter to the requests.post call to prevent indefinite blocking. Wrap
the requests.post call and response.json() parsing in a try-except block to
handle network errors, HTTP errors, and JSON decoding failures, returning
appropriate error responses with meaningful error messages for each failure
case.

In `@web/biome.json`:
- Line 2: The $schema field in web/biome.json references version 2.2.4 but the
installed Biome version in web/package.json is 2.4.5, causing backwards
compatibility warnings. Update the $schema value from the versioned URL pointing
to 2.2.4 to either the matching version 2.4.5, or better yet, use a local path
reference to the configuration schema file in node_modules. The local path
approach is recommended because it automatically tracks the installed Biome
version and eliminates the need for manual updates when upgrading Biome in the
future.

In `@web/convex/invitationCodes.ts`:
- Around line 222-247: The tokenIdentifier is only being updated conditionally
in the else block when the user already exists by email. If the user has an
existing tokenIdentifier that differs from the incoming one, the condition `if
(!user.tokenIdentifier)` prevents the update, and then useCodeHelper is called
with the new tokenIdentifier that doesn't match what's stored for that user.
Remove the conditional check on `!user.tokenIdentifier` in the else block and
unconditionally update the user's tokenIdentifier to args.tokenIdentifier before
calling useCodeHelper, ensuring the lookup in useCodeHelper will succeed
regardless of whether the user previously had a tokenIdentifier stored.

In `@web/package.json`:
- Around line 21-31: The web/package.json file has multiple `@tanstack`
dependencies pinned to "latest" versions which breaks reproducibility across
different environments and CI runs. Replace all "latest" version tags with
explicit semver ranges for the following packages: `@tanstack/react-devtools`,
`@tanstack/react-query`, `@tanstack/react-query-devtools`, `@tanstack/react-router`,
`@tanstack/react-router-devtools`, `@tanstack/react-router-ssr-query`,
`@tanstack/react-start`, `@tanstack/react-store`, and `@tanstack/store`. Use the
currently locked versions from pnpm-lock.yaml as the source of truth, and format
them as explicit semver ranges (e.g., ^x.y.z) to match the pattern already used
for `@tanstack/react-form` and `@tanstack/router-plugin`.

In `@web/src/components/auth/forms/MagicLinkForm.tsx`:
- Line 34: Remove the console.log statement from the MagicLinkForm component
that logs the user's email address. The line containing the console.log with the
email value should be deleted entirely to prevent exposing PII in client logs
during the magic link sign-in process.

In `@web/src/components/auth/forms/SignUpForm.tsx`:
- Around line 35-55: The signUp process in the SignUpForm component creates the
Better Auth user account at the authClient.signUp.email call before validating
and recording the invitation code redemption in the createUserAndUseCode
function. Restructure the signup flow to validate and redeem the invitation code
before or atomically with user account creation, ensuring that if invitation
redemption fails, the auth account is not created or the user is prevented from
completing the signup process. This prevents authenticated sessions from
bypassing the invitation gate through partial failure scenarios.

In `@web/src/lib/auth.functions.ts`:
- Around line 10-16: The `checkUserExists` function creates a user enumeration
vulnerability by explicitly returning whether an email exists in the system,
allowing attackers to probe for registered accounts. Modify the handler in
`checkUserExists` to return a generic response that does not reveal whether the
user was found, regardless of the result from
`context.internalAdapter.findUserByEmail()`. Any actual user verification based
on email existence should be moved to a server-side trusted context that only
executes after proper authentication and verification.

In `@web/src/lib/auth.ts`:
- Around line 13-17: The sendMagicLink function is logging sensitive
authentication data including the magic-link URL and user email address, which
poses a security risk. Remove or replace the console.log statements that output
the email and url variables with a generic message that does not expose any
sensitive information. Ensure that only non-sensitive debug information remains
in the logs, such as a simple indication that a magic link was processed without
revealing the actual email or URL details.
- Line 6: Remove the hardcoded fallback secret string from the `secret` property
initialization in the auth configuration. Instead of using the OR operator with
a known default secret value, ensure that `process.env.BETTER_AUTH_SECRET` is
required and throw an error if the environment variable is not defined. This
will cause the application to fail fast during initialization if the proper
authentication secret is not configured, rather than silently falling back to an
insecure default.

In `@web/src/routes/__root.tsx`:
- Around line 56-67: The TanStackDevtools component is currently rendered
unconditionally for all users, exposing internal details and adding overhead in
production builds. Wrap the entire TanStackDevtools component block with a
conditional check using import.meta.env.DEV so that it only renders when the
application is running in development mode. This ensures devtools are excluded
from production builds while maintaining access for developers during
development.

In `@web/src/routes/_authenticated.course.tsx`:
- Around line 115-118: The axios.post request for code execution at the endpoint
`${url}/execute` lacks a timeout configuration, which can cause the UI to remain
in a loading state indefinitely if the backend becomes slow or unresponsive. Add
a timeout configuration to the axios.post call by passing a config object as the
third parameter that specifies a reasonable timeout value (in milliseconds) to
ensure the request fails gracefully if the backend does not respond within the
specified duration.

In `@web/src/routes/_authenticated.tsx`:
- Around line 40-63: The `.catch()` handler in the useEffect hook only logs the
error from `createUserAndUseCode` failure but does not prevent the user from
accessing protected routes. To fail closed, add error state handling in the
catch block to either set an error flag that prevents rendering protected
content, or navigate the user away from the authenticated route when the
invitation code redemption fails. This ensures users cannot proceed when the
code redemption does not complete successfully.

In `@web/src/routes/auth.tsx`:
- Around line 24-27: The validateSearch function currently accepts any string
value for the redirect parameter without validation, creating a potential open
redirect vulnerability. Modify the redirect validation to only accept safe
internal paths by implementing a check that ensures the redirect value either
starts with a forward slash (indicating a relative path) or is undefined,
rejecting any absolute external URLs. Apply the same sanitization approach to
the other locations mentioned that use or accept the redirect parameter at lines
63-64, 121, and 169-176.

In `@web/src/styles.css`:
- Around line 3-5: The `@import` 'tw-animate-css' statement must be moved to
appear before the `@plugin` '`@tailwindcss/typography`' directive. In the
web/src/styles.css file, reorder these lines so that the `@import` statement comes
first, followed by the `@plugin` statement. This ensures compliance with CSS
specification which requires `@import` declarations to precede other at-rules.

---

Minor comments:
In @.github/workflows/pr-preview.yaml.disabled:
- Around line 31-37: The version parsing logic in the "Parse Version" step does
not validate that a version was actually found before writing the output. If
git_cliff_dry fails or the grep command finds no semver match, the VERSION
variable will be empty and the echo command will write an invalid
calculated_version=v value. Add a guard condition after the VERSION assignment
to check if VERSION is not empty before writing to GITHUB_OUTPUT, and either
fail the step or provide clear error handling if no valid version is extracted
from the git_cliff_dry output.

In `@server/main.py`:
- Around line 26-33: The CORSMiddleware configuration has a conflicting setup
where allow_origins is set to a wildcard ["*"] while allow_credentials is set to
True, which browsers will reject. Either remove allow_credentials=True from the
middleware configuration if credentials are not needed for cross-origin
requests, or replace the wildcard allow_origins with explicit origin URLs (such
as your frontend domain) to properly support credentialed requests in production
environments.

In `@web/convex/courses.ts`:
- Around line 4-12: The function `getAllCourses` has a misleading name and
comment that do not match its actual implementation. The function queries the
"questions" table and returns questions (not courses), and the comment
incorrectly states "last 100 tasks" when the ascending order actually returns
the first 100 records. Rename the function to accurately reflect what it does
(either `getAllQuestions` or `getQuestionsByWeek`) and update the comment to
correctly describe that it returns the first 100 questions ordered by week in
ascending order.

In `@web/src/components/auth/AlreadySignedIn.tsx`:
- Around line 39-45: The AlreadySignedIn component renders session.user.name and
session.user.email directly without handling cases where these fields might be
null or undefined, even though they are marked as optional in the prop type. Add
fallback values for both the name field (around line 40) and the email field
(around line 44) to ensure the component handles missing profile data
gracefully. Use the OR operator or conditional rendering to display a sensible
default message when either field is missing.

In `@web/src/components/auth/AuthTextField.tsx`:
- Around line 68-75: The password toggle button in the AuthTextField component
relies only on the title attribute for accessibility, which is insufficient for
assistive technology. Add aria-label and aria-pressed attributes to the button
element that toggles the password visibility. The aria-label should provide a
descriptive name for the button, and aria-pressed should be set to the boolean
value of showPassword to properly communicate the current state to screen
readers and other assistive technologies.

In `@web/src/components/auth/forms/MagicLinkForm.tsx`:
- Around line 28-30: The email value is being used directly without
normalization in the checkUserExists function call and the subsequent magic-link
request, which can cause user misclassification when whitespace is present.
Normalize the email value from value.email by trimming whitespace and converting
to lowercase before passing it to the checkUserExists function call at line 28,
and also use this normalized email in the magic-link request at lines 37-38.
Store the normalized email in a variable and use it consistently throughout this
form submission logic.

In `@web/src/components/auth/InvitationCodeModal.tsx`:
- Around line 27-30: The handleSubmit function submits the invitation code value
without trimming whitespace, while validation at line 77 uses trim() to check
for emptiness. Modify the handleSubmit function to trim the code value before
passing it to the onSubmit callback to ensure consistency and allow codes with
accidental surrounding whitespace to be properly validated and submitted.

In `@web/src/components/home/CourseList.tsx`:
- Around line 219-232: The week filter buttons are hardcoded to display weeks 1
through 4 using a static array in the map function, but the component
dynamically groups data into sortedWeeks. Replace the hardcoded [1, 2, 3, 4]
array in the map function with the sortedWeeks variable so that the buttons are
generated based on the actual weeks present in the dataset, allowing users to
filter to any week that has data.

In `@web/src/components/ide/Terminal.tsx`:
- Around line 45-47: The memory display in the Terminal.tsx component uses a
truthy check on executionResult.memory which incorrectly treats a value of 0 as
missing data and displays "N/A". Change the conditional check from a truthy
evaluation to a nullish check (checking only for null or undefined) so that
valid zero memory values are properly rendered as "0.00 MB" instead of "N/A".
This ensures accurate memory reporting when the actual memory usage is zero
bytes.

In `@web/src/components/student/CodeEditor.tsx`:
- Line 4: The activeTab state is not reset when executionResult changes, causing
the UI to display empty panes if a user was viewing a tab from a previous
execution that has no content in the new results. Import useEffect from "react"
alongside the existing useState import, then add a useEffect hook with
executionResult as a dependency that resets the activeTab state back to "result"
whenever new execution results arrive.

In `@web/src/components/student/problem/ChatBox.tsx`:
- Around line 254-283: The setTimeout call in the message handling logic needs
to be cancellable to prevent orphaned timeouts. Store the setTimeout ID in a
useRef variable, then clear it in two places: first, add a useEffect cleanup
function that clears the timeout on component unmount, and second, clear the
timeout before resetting the chat state in the function that handles chat
clearing. This ensures the pending timeout is always cancelled before it can
fire and update state after unmounting or after the chat has been reset.

In `@web/src/integrations/tanstack-query/root-provider.tsx`:
- Line 10: The TanstackQueryProvider component is currently unused and empty,
meaning it would not render any child components if mounted. Either remove the
TanstackQueryProvider component entirely if it's no longer needed, or if React
Query provider integration is planned, modify the TanstackQueryProvider function
to accept children as a prop parameter and render those children wrapped
appropriately within a QueryClientProvider component to ensure proper provider
functionality.

In `@web/src/lib/auth.functions.ts`:
- Line 11: The validator function at the .validator() call is currently just
passing the email parameter through without performing any actual validation or
normalization. Replace the simple passthrough with proper runtime validation
that includes type checking to ensure it's a string, trimming whitespace,
validating the email format matches a proper email pattern, checking reasonable
length constraints, and normalizing the value before it's used by
findUserByEmail downstream. Return the validated and normalized email or throw
an error if any validation check fails.

In `@web/src/routes/auth.tsx`:
- Around line 49-55: In the handleModalSubmit function, the codeToSubmit
parameter is passed directly to the convex.query call for validating the
invitation code without trimming whitespace. Apply the trim() method to
codeToSubmit before passing it to the
convex.query(api.invitationCodes.validateCode) call to remove any leading or
trailing whitespace that could cause validation failures.

In `@web/test/studentcode.test.ts`:
- Around line 4-18: The test "should execute student code" directly uses
process.env.VITE_BACKEND_URL without validating it exists, which will cause a
confusing network error if the environment variable is undefined. Add validation
at the start of the test (or in a beforeEach hook) to check that
VITE_BACKEND_URL is defined and throw a clear error message if it is missing, so
developers know to configure the environment variable rather than debugging a
network failure.

In `@web/tsconfig.json`:
- Line 5: The TypeScript configuration file includes a stale backup file
`src/routes/auth.tsx.old` in the include array, which should be removed to
prevent obsolete code from polluting type-checking and route generation. Locate
the include array in the TypeScript config and remove the entry for
`src/routes/auth.tsx.old`.

---

Nitpick comments:
In @.github/workflows/pr-preview.yaml.disabled:
- Line 17: Replace the mutable version tags for the GitHub Actions on lines 17,
24, and 40 with specific commit SHAs. For each action reference using the format
`actions/OWNER/ACTION@v*`, replace the mutable tag with the full commit SHA
(e.g., `actions/checkout@<40-character-commit-sha>`). This pins the actions to
specific versions and prevents supply-chain risk from unexpected action updates
when the workflow is eventually enabled.

In `@server/main.py`:
- Around line 80-82: The read_item endpoint decorated with
`@app.get`("/items/{item_id}") appears to be unused boilerplate code from the
FastAPI template that may confuse developers about the API's actual
functionality. Either remove the entire endpoint definition including the
decorator and the read_item function, or if this endpoint is planned for future
implementation, add a TODO comment above the decorator explaining its intended
purpose and when it should be implemented.

In `@server/model/student_code.py`:
- Around line 7-10: The CodeSubmission class in student_code.py is unused and
creates semantic redundancy with inherited and defined fields. Either remove the
CodeSubmission class entirely from student_code.py, or standardize the codebase
by updating main.py to import and use CodeSubmission in the /execute endpoint
instead of manually constructing the Judge0 payload. Choose one approach and
ensure consistency: if keeping CodeSubmission, replace the manual payload
construction (currently around lines 54-57 in main.py) with an instance of
CodeSubmission, and remove the StudentCode import if it becomes unnecessary.

In `@web/src/routes/_authenticated.index.tsx`:
- Around line 14-34: The Suspense component with its fallback skeleton loading
UI around the CourseList component is duplicating the loading state management
that CourseList already handles internally. Remove the entire Suspense wrapper
(including the fallback prop with the skeleton div structure) around CourseList
and render CourseList directly, allowing it to manage its own loading skeleton
as it already does at line 90 in CourseList.tsx.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d4229f41-6f5e-4679-93a5-3bba08553240

📥 Commits

Reviewing files that changed from the base of the PR and between 5328062 and 23ccabd.

⛔ Files ignored due to path filters (5)
  • server/uv.lock is excluded by !**/*.lock
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • web/public/favicon.ico is excluded by !**/*.ico
  • web/public/logo192.png is excluded by !**/*.png
  • web/public/logo512.png is excluded by !**/*.png
📒 Files selected for processing (65)
  • .github/workflows/pr-preview.yaml.disabled
  • .github/workflows/release.yaml
  • .gitignore
  • cliff.toml
  • server/.env.example
  • server/.python-version
  • server/README.md
  • server/main.py
  • server/model/student_code.py
  • server/pyproject.toml
  • web/.cta.json
  • web/.cursorrules
  • web/.env.example
  • web/.gitignore
  • web/.prettierrc
  • web/.vscode/settings.json
  • web/README.md
  • web/biome.json
  • web/components.json
  • web/convex/courses.ts
  • web/convex/invitationCodes.ts
  • web/convex/schema.ts
  • web/package.json
  • web/pnpm-workspace.yaml
  • web/public/manifest.json
  • web/public/robots.txt
  • web/src/components/auth/AlreadySignedIn.tsx
  • web/src/components/auth/AuthButton.tsx
  • web/src/components/auth/AuthCard.tsx
  • web/src/components/auth/AuthTabs.tsx
  • web/src/components/auth/AuthTextField.tsx
  • web/src/components/auth/InvitationCodeModal.tsx
  • web/src/components/auth/forms/MagicLinkForm.tsx
  • web/src/components/auth/forms/SignInForm.tsx
  • web/src/components/auth/forms/SignUpForm.tsx
  • web/src/components/home/CourseList.tsx
  • web/src/components/home/SidePane.tsx
  • web/src/components/ide/Terminal.tsx
  • web/src/components/student/CodeEditor.tsx
  • web/src/components/student/InteractionBar.tsx
  • web/src/components/student/ResetCodeForm.tsx
  • web/src/components/student/SidePane.tsx
  • web/src/components/student/problem/ChatBox.tsx
  • web/src/components/student/problem/Problem.tsx
  • web/src/components/ui/button.tsx
  • web/src/integrations/better-auth/header-user.tsx
  • web/src/integrations/tanstack-query/devtools.tsx
  • web/src/integrations/tanstack-query/root-provider.tsx
  • web/src/lib/auth-client.ts
  • web/src/lib/auth.functions.ts
  • web/src/lib/auth.ts
  • web/src/lib/utils.ts
  • web/src/routeTree.gen.ts
  • web/src/router.tsx
  • web/src/routes/__root.tsx
  • web/src/routes/_authenticated.course.tsx
  • web/src/routes/_authenticated.index.tsx
  • web/src/routes/_authenticated.tsx
  • web/src/routes/api/auth/$.ts
  • web/src/routes/auth.tsx
  • web/src/styles.css
  • web/test/studentcode.test.ts
  • web/tsconfig.json
  • web/tsr.config.json
  • web/vite.config.ts

Comment on lines +16 to +19
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetches all history so git-cliff can read past commits

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all workflow action references are SHA-pinned and checkout disables credential persistence.
fd -t f '.ya?ml$' .github/workflows -x sh -c '
  f="$1"
  echo "== $f =="
  rg -n "uses:\s*[^@]+@([A-Za-z0-9._/-]+)$" "$f"
  rg -n "uses:\s*actions/checkout@" "$f" -A4 -B1
' sh {}

Repository: RMIT-BDSL/ExemplAI

Length of output: 471


🏁 Script executed:

cat -n .github/workflows/release.yaml

Repository: RMIT-BDSL/ExemplAI

Length of output: 2117


Pin action SHAs and disable checkout credential persistence.

Lines 17, 29, 37, and 46 use mutable action tags (@v4, @v2), and line 19 omits persist-credentials: false. In a release workflow with contents: write permission, this creates a supply-chain attack surface and increases the blast radius of a potential token compromise.

Suggested hardening diff
       - name: Checkout Code
-        uses: actions/checkout@v4
+        uses: actions/checkout@<PINNED_SHA>
         with:
           fetch-depth: 0 # Fetches all history so git-cliff can read past commits
+          persist-credentials: false

       - name: Determine Next Version
         id: next_version
-        uses: orhun/git-cliff-action@v4
+        uses: orhun/git-cliff-action@<PINNED_SHA>
         with:
           config: cliff.toml
           args: --bump --bump-deps

       - name: Generate Changelog
         id: git_cliff
-        uses: orhun/git-cliff-action@v4
+        uses: orhun/git-cliff-action@<PINNED_SHA>
         with:
           config: cliff.toml
           args: --verbose --bump --strip all

       - name: Create GitHub Release
-        uses: softprops/action-gh-release@v2
+        uses: softprops/action-gh-release@<PINNED_SHA>
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 16-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 16 - 19, Replace all mutable
action version tags with pinned commit SHAs in the release workflow.
Specifically, update the actions/checkout@v4 on line 17 and any other actions on
lines 29, 37, and 46 that use mutable tags like `@v4` or `@v2` with their full
commit SHA references instead. Additionally, add persist-credentials: false to
the checkout action's with block on line 19 to prevent credential persistence
and reduce the attack surface in this release workflow that has contents: write
permission.

Source: Linters/SAST tools

Comment thread .gitignore
@@ -1 +1,4 @@
data/csedm-2019/
**/.venv/
**/.env

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Ignore all environment-file variants, not only .env.

Line 3 only ignores the exact .env filename. Files like .env.local / .env.production can still be committed with secrets.

Suggested fix
 **/.env
+**/.env.*
+!**/.env.example
📝 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
**/.env
**/.env
**/.env.*
!**/.env.example
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore at line 3, The pattern on line 3 of .gitignore uses **/.env which
only matches the exact filename .env, allowing variants like .env.local and
.env.production to be committed accidentally. Change the pattern from **/.env to
**/.env* to match all environment files that start with .env prefix, ensuring
all environment file variants are properly ignored from version control.

Comment thread server/main.py
Comment on lines +45 to +75
@app.post('/execute')
def judge0_execution(student_code: StudentCode):
# print student code

# send the code to judge0
# make new request to configured judge0 endpoint - current would block until done
exec_url = os.getenv('JUDGE0_ENDPOINT') + '/submissions?base64_encoded=false&wait=true'

# beautifully format the request payload
payload = {
'source_code': student_code.code,
'language_id': 71, # python
}

# setup headers if auth key is provided
headers = {}
auth_key = os.getenv('JUDGE0_AUTH_KEY')
if auth_key:
headers['X-Auth-Token'] = auth_key

# make request
response = requests.post(exec_url, json=payload, headers=headers)

# get the output
output = response.json()

# print the output using rich
log.info(output)

# return the output
return output

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing timeout, error handling, and env validation on Judge0 proxy.

Several issues in this endpoint:

  1. No timeoutrequests.post can hang indefinitely if Judge0 is unresponsive, blocking the request thread.
  2. No validation of JUDGE0_ENDPOINT — If the env var is missing, None + '/submissions...' raises TypeError.
  3. No error handling — Network failures, non-2xx responses, or non-JSON bodies will crash the endpoint.
🛠️ Proposed fix
+from fastapi import FastAPI, HTTPException
+
+JUDGE0_ENDPOINT = os.getenv('JUDGE0_ENDPOINT')
+if not JUDGE0_ENDPOINT:
+    raise RuntimeError("JUDGE0_ENDPOINT environment variable is required")
+
 `@app.post`('/execute')
 def judge0_execution(student_code: StudentCode):
-    exec_url = os.getenv('JUDGE0_ENDPOINT') + '/submissions?base64_encoded=false&wait=true'
+    exec_url = JUDGE0_ENDPOINT + '/submissions?base64_encoded=false&wait=true'
     
     payload = {
         'source_code': student_code.code,
         'language_id': 71,
     }
 
     headers = {}
     auth_key = os.getenv('JUDGE0_AUTH_KEY')
     if auth_key:
         headers['X-Auth-Token'] = auth_key
 
-    response = requests.post(exec_url, json=payload, headers=headers)
-    
-    output = response.json()
+    try:
+        response = requests.post(exec_url, json=payload, headers=headers, timeout=30)
+        response.raise_for_status()
+        output = response.json()
+    except requests.exceptions.Timeout:
+        raise HTTPException(status_code=504, detail="Judge0 request timed out")
+    except requests.exceptions.RequestException as e:
+        log.error(f"Judge0 request failed: {e}")
+        raise HTTPException(status_code=502, detail="Failed to reach code execution service")
+    except ValueError:
+        raise HTTPException(status_code=502, detail="Invalid response from code execution service")
 
     log.info(output)
     return output
🧰 Tools
🪛 ast-grep (0.43.0)

[info] 65-65: no timeout was given on call to external resource
Context: requests.post(exec_url, json=payload, headers=headers)
Note: [CWE-1088].

(requests-timeout)

🪛 Ruff (0.15.17)

[error] 66-66: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/main.py` around lines 45 - 75, In the judge0_execution function, add
validation to ensure JUDGE0_ENDPOINT is not None before concatenating it with
the path string. Add a timeout parameter to the requests.post call to prevent
indefinite blocking. Wrap the requests.post call and response.json() parsing in
a try-except block to handle network errors, HTTP errors, and JSON decoding
failures, returning appropriate error responses with meaningful error messages
for each failure case.

Source: Linters/SAST tools

Comment thread web/biome.json
@@ -0,0 +1,44 @@
{
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the actual content of web/biome.json
cat -n web/biome.json | head -10

Repository: RMIT-BDSL/ExemplAI

Length of output: 331


🏁 Script executed:

# Check package.json for Biome version
cat -n package.json | grep -A 2 -B 2 "`@biomejs/biome`"

Repository: RMIT-BDSL/ExemplAI

Length of output: 107


🏁 Script executed:

# Also search for package-lock.json or similar to verify actual installed version
fd -e "package-lock.json|yarn.lock|pnpm-lock.yaml" | head -5

Repository: RMIT-BDSL/ExemplAI

Length of output: 44


🏁 Script executed:

# Let's search for package.json in the entire repository
fd -name "package.json" -type f

Repository: RMIT-BDSL/ExemplAI

Length of output: 293


🏁 Script executed:

# Let's also check for any biome references in the repository
rg "biomejs" --type json -l

Repository: RMIT-BDSL/ExemplAI

Length of output: 105


🏁 Script executed:

# Check what files exist in the web directory
ls -la web/ | head -20

Repository: RMIT-BDSL/ExemplAI

Length of output: 1235


🏁 Script executed:

# Check the web/package.json for Biome version
cat web/package.json

Repository: RMIT-BDSL/ExemplAI

Length of output: 2100


🏁 Script executed:

# Also check the root package.json for comparison
cat package.json | grep -A 5 -B 5 "biomejs\|biome"

Repository: RMIT-BDSL/ExemplAI

Length of output: 107


🏁 Script executed:

# Let's also check the root biome.json to see if there's a different configuration
cat biome.json

Repository: RMIT-BDSL/ExemplAI

Length of output: 1320


🌐 Web query:

Biome schema compatibility 2.2.4 vs 2.4.5 version mismatch

💡 Result:

When upgrading Biome from version 2.2.4 to 2.4.5, you will likely encounter a warning or error regarding a configuration schema mismatch [1][2][3]. Biome includes a built-in mechanism to detect when the $schema field in your biome.json file does not match the version of the installed CLI [1][3][4]. To resolve this mismatch, you should run the following command in your project root: biome migrate --write This command automatically updates your biome.json file, including the $schema field, to ensure compatibility with your current Biome version [1][5]. Recommendation: To avoid frequent manual updates to the $schema URL when you bump Biome versions, you can point to the locally installed schema file instead of a specific versioned URL [2]. You can replace the URL in your biome.json with the following path: "$schema": "./node_modules/@biomejs/biome/configuration_schema.json" Using this local path allows Biome to automatically use the schema associated with your currently installed package version, reducing the need to run migration commands solely for schema version synchronization [2].

Citations:


🌐 Web query:

Biome biomejs.dev schemas backwards compatibility newer versions

💡 Result:

Biome does not strictly maintain backwards compatibility for the $schema field in its configuration files [1][2]. The tool expects the $schema version defined in your biome.json or biome.jsonc file to match the version of the Biome CLI you are currently using [1][3][4]. When there is a mismatch—specifically if the schema version is older than the CLI version—the Biome CLI will emit a warning diagnostic [1][4][5]. To resolve this and ensure your configuration remains valid and up-to-date with your current CLI version, you should run the following command [1][2]: biome migrate This command is designed to update your configuration file and synchronize the $schema version [1][2]. For managing this in projects, you have two primary options for specifying the $schema: 1. Remote URL: You can use the versioned URL (e.g., https://biomejs.dev/schemas/2.4.13/schema.json) [6][7]. If you use this approach, you must update the version number whenever you upgrade the Biome package to avoid version mismatch warnings [1][8]. 2. Local Path: You can reference the schema bundled within your project's node_modules [7]. This is often preferred because it automatically tracks the version of the Biome package installed in your project, reducing the need to manually update the URL [1][7]. You can set it like this: { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json" } Because Biome is a rapidly evolving toolchain, the maintainers advise against ignoring these warnings, as even minor or patch updates can introduce changes to lint rules or formatting behaviors [8][9]. Using the biome migrate command or a local path reference helps ensure your configuration remains compatible with your specific installed version [1][2][7].

Citations:


Update Biome schema version in web/biome.json to match the installed Biome version.

The schema URL in web/biome.json references version 2.2.4, but web/package.json specifies @biomejs/biome@2.4.5. Biome does not maintain backwards compatibility for schema versions and will emit a warning diagnostic when the schema version is older than the installed CLI version. Update the schema to 2.4.5 to match the installed version.

For a more robust long-term solution, consider using a local path reference instead of a versioned URL:

"$schema": "./node_modules/@biomejs/biome/configuration_schema.json"

This approach automatically tracks the installed Biome version and eliminates the need for manual schema URL updates during version upgrades.

🔧 Quick fix
- "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/biome.json` at line 2, The $schema field in web/biome.json references
version 2.2.4 but the installed Biome version in web/package.json is 2.4.5,
causing backwards compatibility warnings. Update the $schema value from the
versioned URL pointing to 2.2.4 to either the matching version 2.4.5, or better
yet, use a local path reference to the configuration schema file in
node_modules. The local path approach is recommended because it automatically
tracks the installed Biome version and eliminates the need for manual updates
when upgrading Biome in the future.

Comment thread web/convex/invitationCodes.ts
Comment thread web/src/routes/__root.tsx
Comment on lines +115 to +118
try {
const response = await axios.post(`${url}/execute`, {
code: submissionCode,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify axios calls and whether they include explicit timeout options.
rg -nP --type=ts --type=tsx 'axios\.(get|post|put|patch|delete)\(' web/src -C2
rg -nP --type=ts --type=tsx 'timeout\s*:' web/src -C2

Repository: RMIT-BDSL/ExemplAI

Length of output: 118


🏁 Script executed:

#!/bin/bash
# Read the specific file to verify the axios call at lines 115-118
if [ -f "web/src/routes/_authenticated.course.tsx" ]; then
  echo "=== File content at lines 110-125 ==="
  sed -n '110,125p' web/src/routes/_authenticated.course.tsx
else
  echo "File not found at expected path"
  find . -name "*authenticated.course*" -type f
fi

Repository: RMIT-BDSL/ExemplAI

Length of output: 720


🏁 Script executed:

#!/bin/bash
# Search for axios calls without problematic type flags
echo "=== All axios calls in web/src ==="
rg 'axios\.(get|post|put|patch|delete)\(' web/src -A2 -B1

echo -e "\n=== Timeout configurations in web/src ==="
rg 'timeout\s*:' web/src -B2 -A1

Repository: RMIT-BDSL/ExemplAI

Length of output: 437


Add an explicit timeout for code execution requests.

The execution request at Line 116 lacks a timeout configuration. A slow or hung backend can leave the UI in a prolonged loading state, degrading route responsiveness.

Suggested fix
             const response = await axios.post(`${url}/execute`, {
                 code: submissionCode,
-            })
+            }, {
+                timeout: 30000,
+            })
📝 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
try {
const response = await axios.post(`${url}/execute`, {
code: submissionCode,
})
try {
const response = await axios.post(`${url}/execute`, {
code: submissionCode,
}, {
timeout: 30000,
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/routes/_authenticated.course.tsx` around lines 115 - 118, The
axios.post request for code execution at the endpoint `${url}/execute` lacks a
timeout configuration, which can cause the UI to remain in a loading state
indefinitely if the backend becomes slow or unresponsive. Add a timeout
configuration to the axios.post call by passing a config object as the third
parameter that specifies a reasonable timeout value (in milliseconds) to ensure
the request fails gracefully if the backend does not respond within the
specified duration.

Comment on lines +40 to +63
useEffect(() => {
if (session?.user && code) {
createUserAndUseCode({
email: session.user.email,
name: session.user.name || undefined,
image: session.user.image || undefined,
code: code,
tokenIdentifier: session.user.id,
})
.then(() => {
console.log("Successfully synced user to Convex and redeemed invitation code.");
// Remove the code parameter from the search query after success
navigate({
search: (prev: any) => {
const { code: _, ...rest } = prev;
return rest;
},
replace: true,
});
})
.catch((err) => {
console.error("Failed to sync user and redeem code in Convex:", err);
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed when invitation-code sync fails.

On failure, the code only logs an error and still renders protected routes. That lets authenticated users proceed even when invitation redemption did not complete.

Suggested direction
 function AuthenticatedLayout() {
+  const [inviteSyncFailed, setInviteSyncFailed] = useState(false)
   const { session } = Route.useRouteContext();
   const { code } = Route.useSearch();
   const navigate = useNavigate();
   const createUserAndUseCode = useMutation(api.invitationCodes.createUserAndUseCode);

   useEffect(() => {
     if (session?.user && code) {
       createUserAndUseCode({
         email: session.user.email,
         name: session.user.name || undefined,
         image: session.user.image || undefined,
         code: code,
         tokenIdentifier: session.user.id,
       })
         .then(() => {
           navigate({
             search: (prev: any) => {
               const { code: _, ...rest } = prev;
               return rest;
             },
             replace: true,
           });
         })
         .catch((err) => {
-          console.error("Failed to sync user and redeem code in Convex:", err);
+          setInviteSyncFailed(true)
         });
     }
   }, [session, code, createUserAndUseCode, navigate]);
+
+  if (code && inviteSyncFailed) {
+    return null // or redirect to /auth with an explicit error state
+  }

   return <Outlet />;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/routes/_authenticated.tsx` around lines 40 - 63, The `.catch()`
handler in the useEffect hook only logs the error from `createUserAndUseCode`
failure but does not prevent the user from accessing protected routes. To fail
closed, add error state handling in the catch block to either set an error flag
that prevents rendering protected content, or navigate the user away from the
authenticated route when the invitation code redemption fails. This ensures
users cannot proceed when the code redemption does not complete successfully.

Comment thread web/src/routes/auth.tsx
Comment thread web/src/styles.css
Comment on lines +3 to +5
@plugin '@tailwindcss/typography';

@import 'tw-animate-css';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
from pathlib import Path
p = Path("web/src/styles.css")
lines = p.read_text().splitlines()
first_non_import = None
late_imports = []
for i, raw in enumerate(lines, 1):
    s = raw.strip()
    if not s or s.startswith("/*"):
        continue
    if s.startswith("`@import`"):
        if first_non_import is not None:
            late_imports.append(i)
    elif s.startswith("`@charset`") or s.startswith("`@layer`"):
        continue
    else:
        if first_non_import is None:
            first_non_import = i
print({"first_non_import_line": first_non_import, "late_import_lines": late_imports})
PY

Repository: RMIT-BDSL/ExemplAI

Length of output: 117


Move @import 'tw-animate-css' above non-import at-rules.

@import must be declared before other non-@charset/non-@layer at-rules. Currently, line 5 places it after the @plugin at-rule on line 3, which violates CSS specification and triggers lint errors.

Suggested fix
 `@import` url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap');
 `@import` 'tailwindcss';
+@import 'tw-animate-css';
 `@plugin` '`@tailwindcss/typography`';
-
-@import 'tw-animate-css';
 `@custom-variant` dark (&:is(.dark *));
🧰 Tools
🪛 Stylelint (17.13.0)

[error] 5-5: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)


[error] 3-3: Unexpected unknown at-rule "@plugin" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/styles.css` around lines 3 - 5, The `@import` 'tw-animate-css'
statement must be moved to appear before the `@plugin` '`@tailwindcss/typography`'
directive. In the web/src/styles.css file, reorder these lines so that the
`@import` statement comes first, followed by the `@plugin` statement. This ensures
compliance with CSS specification which requires `@import` declarations to precede
other at-rules.

Source: Linters/SAST tools

@millecodex millecodex merged commit 63d6584 into main Jun 21, 2026
1 check passed
@Ronaldo93 Ronaldo93 deleted the feat/website branch June 21, 2026 04:45
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.

3 participants