Skip to content

Final project jfvg Entrega final — Lexio MVP (flujo E2E + documentación) - #280

Open
fredyvizcarra wants to merge 3 commits into
LIDR-academy:mainfrom
fredyvizcarra:final-project-jfvg
Open

Final project jfvg Entrega final — Lexio MVP (flujo E2E + documentación)#280
fredyvizcarra wants to merge 3 commits into
LIDR-academy:mainfrom
fredyvizcarra:final-project-jfvg

Conversation

@fredyvizcarra

@fredyvizcarra fredyvizcarra commented Jul 26, 2026

Copy link
Copy Markdown

Entrega final del proyecto académico Lexio. Incluye un MVP funcional de extremo
a extremo (auth, captura de palabras, práctica diaria, racha), backend/mobile,
tests unitarios del backend y documentación de despliegue y muestra del producto.

No representa una versión productiva cerrada; debido a que es una app mobile, use expo go y no quise pagar la cuenta dev de ios, tests E2E automatizados en mobile y funcionalidades post-MVP.

Summary by CodeRabbit

  • New Features

    • Added the initial Lexio mobile app experience, including registration, login, vocabulary capture, AI-generated definitions, image selection, dictionary management, daily practice, results, streaks, and settings.
    • Added an authenticated backend API supporting word cards, daily sessions, streaks, validation, and secure data access.
    • Added Spanish and English localization.
  • Documentation

    • Added comprehensive product, architecture, API, data model, deployment, testing, development, and user story documentation.
  • Tests

    • Added automated coverage for vocabulary, practice sessions, and streak behavior.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Lexio’s MVP is introduced as a React Native/Expo app with Firebase authentication and Firestore persistence, an Express BFF integrating Claude and Unsplash, daily vocabulary practice with streak tracking, bilingual UI, backend tests, Firebase configuration, and extensive product and delivery documentation.

Changes

Lexio MVP

Layer / File(s) Summary
Product requirements and execution plan
master-prd.md, 1-descripcion-general-del-producto.md, 5-historias-de-usuario.md, 6-tickets-de-trabajo.md, 7-desarrollo.md, prompts.md, readme.md
Defines Lexio’s product scope, MVP flow, user stories, implementation tickets, development phases, prompts, and project overview.
Architecture, data, and API contracts
2-arquitectura-del-sistema.md, 3-modelo-de-datos.md, 4-especificaciones-de-la-api.md
Documents the Express BFF architecture, Firebase/Firestore model, security rules, external integrations, API schemas, authentication, endpoints, and error contracts.
Backend BFF implementation
backend/src/**, backend/scripts/*, backend/package.json, backend/tsconfig.json, firebase.json, firestore.*
Adds authenticated Express routes, validation and error middleware, Claude and Unsplash clients, Firestore repositories, vocabulary/session/streak services, controllers, seed data, and Jest tests.
Mobile foundation and state integration
mobile/services/*, mobile/store/*, mobile/types/*, mobile/i18n/*, mobile/constants/*, mobile/app.json, mobile/package.json
Configures Expo, Firebase Auth persistence, Axios API clients, i18n resources, typed domain models, and Zustand stores for authentication, words, sessions, and streaks.
Mobile navigation and learning screens
mobile/app/**
Adds authentication, protected routing, tabs, vocabulary capture, dictionary, card editing, practice, results, home, and settings screens.
Repository delivery and supporting configuration
.cursor/*, .firebaserc, .gitignore, DEPLOYMENT.md, MUESTRA_DEL_PRODUCTO.md, TESTING.md
Adds development guidance, ignore rules, Firebase project selection, deployment steps, product demonstration material, and testing documentation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the PR’s main scope: the Lexio MVP delivery with E2E flow and documentation.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@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.

Note

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

🟠 Major comments (28)
DEPLOYMENT.md-47-57 (1)

47-57: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep the documented Firebase storage bucket consistent with mobile/app.json.

Line 54 uses an .appspot.com bucket, but mobile/app.json uses lexio-dev-8e8e3.firebasestorage.app. Copying this guide can configure a different or invalid bucket. Document the exact project value or instruct users to copy it from the generated Firebase configuration.

🤖 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 `@DEPLOYMENT.md` around lines 47 - 57, Update the Firebase storage bucket value
in the Mobile app.json configuration example to match the exact value used by
mobile/app.json, lexio-dev-8e8e3.firebasestorage.app, or instruct readers to
copy the generated Firebase configuration value instead of using a hardcoded
bucket.
.gitignore-11-14 (1)

11-14: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Ignore service-account and production environment files.

DEPLOYMENT.md instructs developers to create service-account.json (Lines 39-41), but this file is not ignored. .env.production is also unprotected. Add explicit patterns before credentials are accidentally committed.

Proposed fix
 # Environment variables
 .env
 .env.local
 .env.*.local
+.env.production
+.env.development
 
 # Firebase
 .firebase/
 firebase-debug.log
+service-account.json

Also applies to: 24-26

🤖 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 around lines 11 - 14, Update the Environment variables section of
.gitignore to explicitly ignore service-account.json and .env.production,
placing both patterns with the existing environment and credential exclusions.
1-descripcion-general-del-producto.md-275-288 (1)

275-288: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a LAN-reachable backend URL for physical Expo Go devices.

localhost resolves to the phone, so the documented QR flow cannot reach the backend. Document http://<development-machine-LAN-IP>:3000 (or a tunnel) for devices; reserve localhost for simulators.

🤖 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 `@1-descripcion-general-del-producto.md` around lines 275 - 288, Actualiza la
variable EXPO_PUBLIC_API_URL en la documentación del flujo con Expo Go para usar
una URL accesible por LAN, como http://<development-machine-LAN-IP>:3000, o
indicar un túnel; conserva localhost únicamente para simuladores.
backend/src/repositories/SessionRepository.ts-74-97 (1)

74-97: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make session completion atomic.

Two concurrent completion requests can both read completed: false, overwrite answers, and each continue to streak processing. Use a Firestore transaction that checks ownership and completed before updating; return a conflict to the losing request.

🤖 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 `@backend/src/repositories/SessionRepository.ts` around lines 74 - 97, Update
the session completion method in SessionRepository to perform the read,
ownership check, completed check, and session update inside a Firestore
transaction. Reject sessions already marked completed with a conflict result,
and ensure only the transaction that successfully updates the session proceeds
to streak processing; preserve the existing answer scoring and completion
fields.
backend/src/routes/sessions.ts-13-22 (1)

13-22: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Require one answer for every session exercise.

Array length alone accepts duplicate or unknown exerciseId values. Validate unique IDs here, then have SessionService verify the submitted ID set exactly matches the session’s exercises before allowing completion.

🤖 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 `@backend/src/routes/sessions.ts` around lines 13 - 22, Update
completeSessionSchema to reject duplicate exerciseId values, then update
SessionService’s completion flow to verify the submitted unique ID set exactly
matches the session’s exercise IDs, rejecting missing or unknown exercises
before completing the session.
6-tickets-de-trabajo.md-162-182 (1)

162-182: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not grant clients direct access to server-managed sessions and streaks.

These owner-only rules still let an authenticated user read correctAnswer values and directly create or update their own session completion, score, and streak. Since the BFF owns this logic, deny direct client access to dailySessions and streaks; Firebase Admin operations remain allowed despite restrictive rules.

🤖 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 `@6-tickets-de-trabajo.md` around lines 162 - 182, Update the Firestore rules
for the dailySessions and streaks match blocks to deny all direct client reads
and writes, removing the authenticated-owner allow rules. Leave WordCards
permissions unchanged; server-side Firebase Admin operations must continue to
function.
backend/src/repositories/SessionRepository.ts-21-33 (1)

21-33: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Enforce one daily session across completed and concurrent requests. Completed sessions are ignored by the lookup, so createDailySession creates a replacement; simultaneous starts can also both create a session.

  • backend/src/repositories/SessionRepository.ts#L21-L33: query and return the session regardless of completion state.
  • backend/src/repositories/SessionRepository.ts#L44-L66: create through a transaction or deterministic (userId, sessionDate) document key, then return 409 for completed sessions.
🤖 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 `@backend/src/repositories/SessionRepository.ts` around lines 21 - 33, Update
findInProgressByDate to return the matching daily session regardless of its
completed state. In createDailySession, enforce uniqueness for each
userId/sessionDate using a transaction or deterministic document key, and return
a 409 conflict when the existing session is completed.
6-tickets-de-trabajo.md-217-219 (1)

217-219: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Enforce normalized-term uniqueness atomically. A read-then-create application check allows simultaneous requests for the same normalized term to both succeed.

  • 6-tickets-de-trabajo.md#L217-L219: require an atomic uniqueness strategy in the ticket contract.
  • backend/src/repositories/WordRepository.ts#L86-L101: use a transaction or deterministic collision-safe document ID derived from (userId, normalizedTerm).
🤖 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 `@6-tickets-de-trabajo.md` around lines 217 - 219, Replace the ticket
contract’s application-only normalized-term uniqueness requirement with an
atomic strategy. In backend/src/repositories/WordRepository.ts around the word
creation flow, use a transaction or a deterministic collision-safe document ID
derived from userId and normalizedTerm so concurrent requests cannot create
duplicates; update the repository behavior and documentation consistently.
6-tickets-de-trabajo.md-712-712 (1)

712-712: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Exclude incomplete drafts from practice eligibility. Persisted cards without a selected image are counted toward the four-word threshold and can enter image_match exercises with an empty image URL.

  • 6-tickets-de-trabajo.md#L712-L712: define an explicit draft state or defer persistence until the image is selected.
  • backend/src/repositories/WordRepository.ts#L67-L84: exclude drafts from both the vocabulary count and session word selection queries.
🤖 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 `@6-tickets-de-trabajo.md` at line 712, Define an explicit draft state for
cards without a selected image, or defer persistence until image selection is
complete, in the workflow described at 6-tickets-de-trabajo.md:712. Update
backend/src/repositories/WordRepository.ts:67-84 so vocabulary-count and
session-word-selection queries exclude drafts, preventing incomplete cards from
meeting thresholds or entering image_match exercises.
firestore.rules-5-25 (1)

5-25: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict direct client writes to BFF-owned Firestore documents.

The BFF design places mutations behind Express and Firebase Admin, but these rules permit authenticated clients to write protected state directly. Admin SDK writes bypass Firestore Rules, so deny client writes to server-managed collections or strictly validate immutable ownership and every permitted field.

  • firestore.rules#L5-L25: deny direct client writes to wordCards, dailySessions, and streaks (or narrowly validate allowed fields); prevent userId changes on updates.
  • 2-arquitectura-del-sistema.md#L340-L350: replace the owner-write example and describe the same server-owned write policy.
🤖 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 `@firestore.rules` around lines 5 - 25, Restrict direct client mutations to the
BFF-owned collections in firestore.rules: deny client create, update, delete,
and write access for wordCards, dailySessions, and streaks, while preserving
only the required authenticated read access and preventing any userId changes if
client updates remain supported. Update 2-arquitectura-del-sistema.md lines
340-350 to replace the owner-write example with the same server-owned write
policy; both sites must consistently state that mutations go through
Express/Firebase Admin.
backend/src/tests/SessionService.test.ts-96-106 (1)

96-106: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not create a second daily session after completion.

createDailySession() only checks findInProgressByDate, so completing today’s session and then calling creation should still return the completed session rather than calling create. Add guard/repository behavior that rejects or returns after a completed today session, and adjust this test to expect that blocked behavior.

🤖 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 `@backend/src/tests/SessionService.test.ts` around lines 96 - 106, The
createDailySession flow in SessionService must prevent duplicate sessions after
today’s session is completed. Extend the repository lookup or add a
completed-session guard so an existing completed session is returned or creation
is rejected without calling mockSessionRepo.create, then update the test case to
assert that blocked behavior.
backend/src/services/SessionService.ts-120-139 (1)

120-139: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Always generate the promised ten exercises.

With four to nine cards, sampleWords returns fewer than ten cards, so this creates fewer than ten exercises; with exactly four, it creates no MCQs. Build a ten-item selection with intentional repetition when the vocabulary is smaller than SESSION_SIZE.

🤖 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 `@backend/src/services/SessionService.ts` around lines 120 - 139, Update the
session generation flow around sampleWords, imageMatchWords, and mcqWords to
always produce SESSION_SIZE (ten) selected cards, repeating cards intentionally
when allWords contains fewer than SESSION_SIZE entries. Preserve the existing
half split and exercise-building behavior, including MCQs for small
vocabularies, while ensuring the returned exercises array always has ten items.
backend/src/integrations/claudeClient.ts-65-74 (1)

65-74: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the parsed MCQ payload at runtime.

JSON.parse(...) as ... does not ensure four unique options, a non-empty question, or that correctAnswer is one of the options. Reject/retry invalid model output before persisting a broken session.

🤖 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 `@backend/src/integrations/claudeClient.ts` around lines 65 - 74, Update the
Claude MCQ parsing flow around the JSON.parse result to perform runtime
validation before returning the payload. Ensure question is non-empty, options
contains exactly four unique non-empty values, and correctAnswer is one of those
options; reject invalid output through the existing parse-error/retry path
before persistence.
backend/src/services/SessionService.ts-162-171 (1)

162-171: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Complete the session and update the streak atomically.

If SessionRepository.complete succeeds but StreakService.updateAfterSession fails, the request returns 500; retries then hit SESSION_ALREADY_COMPLETED, leaving the streak permanently stale. Commit both documents in one Firestore transaction.

🤖 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 `@backend/src/services/SessionService.ts` around lines 162 - 171, Update the
session-completion flow around SessionRepository.complete and
StreakService.updateAfterSession to execute both document updates within a
single Firestore transaction. Ensure either both the session completion and
streak update commit together or neither does, while preserving the existing
Session not found handling.
backend/src/services/WordService.ts-19-41 (1)

19-41: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Enforce logical uniqueness atomically. Firestore composite indexes support queries; they do not reject duplicate field values. Separate read-then-write operations allow concurrent duplicate cards and same-day sessions.

  • backend/src/services/WordService.ts#L19-L41: perform duplicate detection and creation in one transaction or use a deterministic unique document key.
  • backend/src/services/SessionService.ts#L115-L139: transactionally create at most one session per user/date.
  • 3-modelo-de-datos.md#L107-L116: document the transactional/deterministic-key mechanism instead of presenting indexes as uniqueness constraints.
🤖 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 `@backend/src/services/WordService.ts` around lines 19 - 41, Enforce logical
uniqueness atomically: in backend/src/services/WordService.ts lines 19-41,
update the WordService creation flow to use a transaction or deterministic
unique document key so duplicate detection and WordRepository.create cannot
race; in backend/src/services/SessionService.ts lines 115-139, transactionally
ensure at most one session exists per user/date; in 3-modelo-de-datos.md lines
107-116, document the chosen transaction or deterministic-key mechanism and stop
describing composite indexes as uniqueness constraints.
backend/src/controllers/SessionController.ts-7-8 (1)

7-8: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Separate persisted exercises from the public session DTO. The response currently exposes correctAnswer, allowing clients to submit perfect answers without solving the exercises; it also wraps a response that OpenAPI declares as a root DailySession.

  • backend/src/controllers/SessionController.ts#L7-L8: map persisted exercises to a client-safe DTO that omits correctAnswer, then serialize the documented top-level response shape.
  • 4-especificaciones-de-la-api.md#L205-L245: define a public exercise schema without correctAnswer rather than requiring an undeclared internal field.
  • 4-especificaciones-de-la-api.md#L247-L289: align the DailySession response schema with the chosen controller envelope.
🤖 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 `@backend/src/controllers/SessionController.ts` around lines 7 - 8, Update
SessionController to map persisted exercises into a client-safe DTO that omits
correctAnswer, then return the documented DailySession response envelope instead
of exposing the raw session or an inconsistent wrapper. In
4-especificaciones-de-la-api.md lines 205-245, define the public exercise schema
without correctAnswer; in lines 247-289, align the DailySession schema with the
controller’s chosen top-level response shape.
backend/src/services/SessionService.ts-115-118 (1)

115-118: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reconcile existing-session handling with the API contract.

The service returns an in-progress session, which the controller sends as 201, while the OpenAPI contract specifies 409 for an existing in-progress session. Make this explicitly idempotent (200) or reject it as documented.

🤖 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 `@backend/src/services/SessionService.ts` around lines 115 - 118, Update the
existing-session branch in the SessionService flow around
SessionRepository.findInProgressByDate so its behavior matches the API contract:
either explicitly preserve the session as an idempotent success that the
controller returns with 200, or reject the existing in-progress session through
the documented 409 path. Do not leave it returning normally in a way that
produces 201.
backend/src/services/WordService.ts-27-41 (1)

27-41: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not discard caller-provided definition and image fields.

The documented create payload accepts definition, imageUrl, and unsplashPhotoId, but this path always generates a definition and persists an empty image URL. Honor supplied values (and only call Claude when no definition was provided), or remove these fields from the API contract.

🤖 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 `@backend/src/services/WordService.ts` around lines 27 - 41, Update the word
creation flow around generateDefinition, searchImages, and WordRepository.create
to preserve caller-provided definition, imageUrl, and unsplashPhotoId values.
Invoke generateDefinition only when body.definition is absent, use generated
results only as fallbacks for missing image fields, and persist the resolved
values instead of always writing an empty image URL or null photo ID.
backend/src/integrations/unsplashClient.ts-17-23 (1)

17-23: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a timeout to the Unsplash image request.

WordService.createWord awaits searchImages inside Promise.all, so a hanging Unsplash connection can keep the word-create request open indefinitely. Abort the fetch with AbortSignal.timeout(...) or an equivalent bounded deadline and wrap the integration failure in catch.

🤖 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 `@backend/src/integrations/unsplashClient.ts` around lines 17 - 23, Update the
Unsplash request in searchImages to use a bounded timeout by supplying an
AbortSignal.timeout or equivalent deadline to fetch. Add catch handling around
the integration request so timeout and other fetch failures are wrapped and
propagated with useful Unsplash-specific context.
backend/scripts/seedFirestore.ts-14-15 (1)

14-15: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not source-control or log a usable demo password.

This script can target any configured Firebase project, including production. Require externally supplied demo credentials, refuse non-development environments, and never print the password.

Also applies to: 79-81

🤖 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 `@backend/scripts/seedFirestore.ts` around lines 14 - 15, Update the seed
script’s demo credential handling around email and password so credentials are
supplied externally rather than hardcoded or logged. Require an externally
provided password, refuse to run unless the configured Firebase environment is
explicitly development, and ensure all output excludes the password; apply the
same protections to the related credential usage at the referenced location.

Source: Linters/SAST tools

mobile/app/(tabs)/home.tsx-62-74 (1)

62-74: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add the required visible keyboard focus state.

The practice button has no focus:ring-2 focus:ring-indigo-500 state or React Native equivalent. Add a focus-aware Pressable/NativeWind implementation with a visible Indigo 500 ring.

🤖 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 `@mobile/app/`(tabs)/home.tsx around lines 62 - 74, Add keyboard-focus handling
to the practice button around TouchableOpacity/handleStartPractice, replacing or
adapting it to a focus-aware Pressable implementation that visibly renders an
Indigo 500, 2px-equivalent focus ring while focused. Preserve the existing
styles, loading disabled behavior, press handler, and button text/activity
indicator.

Source: Coding guidelines

mobile/services/api.ts-15-15 (1)

15-15: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Set a finite HTTP timeout on the mobile API client.

axios.create() omits timeout, so requests through this client can remain open until the platform closes them; add a configured timeout before awaiting these calls in mobile UI flows.

🤖 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 `@mobile/services/api.ts` at line 15, Update the axios client created by the
`axios.create` call to include a finite timeout configuration alongside
`baseURL`, using the project’s established timeout constant or value if
available.
mobile/package.json-11-31 (1)

11-31: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Align Expo Router and React DOM with the SDK 56 dependency set.

mobile/package.json declares Expo SDK 56, but expo-router@~5.0.7 belongs to the previous Router line and react-dom@^19.2.7 does not match react@19.2.3. Re-resolve the Expo 56 dependency set inside mobile/ and use the recommended SDK 56 Router version so install/build is not blocked by dependency conflicts.

🤖 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 `@mobile/package.json` around lines 11 - 31, Update the dependency declarations
in mobile/package.json to use the Expo SDK 56-compatible expo-router version and
align react-dom with the declared react 19.2.3 version. Re-resolve and commit
the mobile dependency lockfile so the dependency graph matches the SDK 56
recommendations and installs without conflicts.
mobile/app/(tabs)/add-word.tsx-56-75 (1)

56-75: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Store actions that throw on failure are called without try/catch in several screens. sessionStore.ts's startSession/completeSession establish a convention where store actions reject on failure and callers must catch them (as add-word.tsx's handleGenerate correctly does); the sites below don't follow it, so failures are silently swallowed with no user feedback.

  • mobile/app/(tabs)/add-word.tsx#L56-L75: wrap await updateWord(...) in try/catch and surface an error via setError.
  • mobile/app/card/[id].tsx#L40-L43: wrap await updateWord(id, { definition }) in try/catch before showing the success alert.
  • mobile/app/card/[id].tsx#L45-L49: wrap await updateWord(id, { status: newStatus }) in try/catch before calling router.back().
  • mobile/app/(tabs)/settings.tsx#L21-L26: wrap await logout() in try/catch so the store resets/navigation still happen or an error is shown on failure.
🤖 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 `@mobile/app/`(tabs)/add-word.tsx around lines 56 - 75, Store actions can
reject without caller handling, so add try/catch around each affected await and
surface failures to the user. In mobile/app/(tabs)/add-word.tsx lines 56-75,
update handleSave to catch updateWord errors and call setError; in
mobile/app/card/[id].tsx lines 40-43, catch the definition update before showing
the success alert; in mobile/app/card/[id].tsx lines 45-49, catch the status
update before router.back(); and in mobile/app/(tabs)/settings.tsx lines 21-26,
catch logout failures while preserving the intended reset/navigation or showing
an error.
mobile/app.json-36-36 (1)

36-36: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a device-reachable BFF URL.

localhost resolves to the phone when running Expo Go, so the mobile client cannot reach the backend. Use a LAN URL for local-device testing and an HTTPS deployment URL for release builds. (docs.expo.dev)

🤖 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 `@mobile/app.json` at line 36, Update the EXPO_PUBLIC_API_URL configuration in
app.json to use a device-reachable LAN backend URL for local Expo Go testing
instead of localhost, and use the HTTPS deployment URL for release builds
through the project’s existing environment-specific configuration.
mobile/app/(auth)/login.tsx-66-76 (1)

66-76: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add visible keyboard focus states to every manual button.

None of these controls implements the required focus:ring-2 focus:ring-indigo-500 treatment, leaving keyboard users without a visible focus indicator.

  • mobile/app/(auth)/login.tsx#L66-L76: implement the required focus ring on the login control.
  • mobile/app/(auth)/register.tsx#L70-L80: implement the required focus ring on the registration control.
  • mobile/app/results.tsx#L46-L58: implement the required focus ring on both result-navigation controls.
🤖 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 `@mobile/app/`(auth)/login.tsx around lines 66 - 76, Apply the required visible
keyboard focus ring styling to every manual button: the login control in
mobile/app/(auth)/login.tsx lines 66-76, the registration control in
mobile/app/(auth)/register.tsx lines 70-80, and both result-navigation controls
in mobile/app/results.tsx lines 46-58. Update each button’s styling to include
the equivalent of focus:ring-2 and focus:ring-indigo-500 while preserving
existing behavior and styles.

Source: Coding guidelines

mobile/app.json-43-45 (1)

43-45: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Replace the placeholder EAS project ID.

In mobile/app.json, extra.eas.projectId is still your-eas-project-id, which breaks EAS build/update project identification. Run eas init from mobile and commit the generated project ID.

🤖 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 `@mobile/app.json` around lines 43 - 45, Replace the placeholder value in the
extra.eas.projectId configuration with the actual project ID generated by
running eas init from the mobile directory, then commit that generated ID
without changing the surrounding EAS configuration.

Source: Learnings

mobile/services/firebase.ts-30-37 (1)

30-37: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Initialize with AsyncStorage before calling getAuth.

getAuth(appInstance) returns the existing initialized Auth instance and does not apply the React Native persistence provided in the fallback. Initialize first, and only fall back to getAuth if that throws.

Proposed fix
 function createAuth(appInstance: FirebaseApp): Auth {
   try {
-    return getAuth(appInstance);
-  } catch {
     return initializeAuth(appInstance, {
       persistence: getReactNativePersistence(AsyncStorage),
     });
+  } catch {
+    return getAuth(appInstance);
+  }
 }
🤖 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 `@mobile/services/firebase.ts` around lines 30 - 37, Update createAuth so it
first calls initializeAuth with getReactNativePersistence(AsyncStorage) and
returns that initialized instance; if initialization throws because Auth is
already configured, catch the error and fall back to getAuth(appInstance).
🟡 Minor comments (19)
MUESTRA_DEL_PRODUCTO.md-24-24 (1)

24-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add explicit languages to all fenced Markdown blocks.

Both documentation files contain fenced blocks without a language identifier, triggering MD040.

  • MUESTRA_DEL_PRODUCTO.md#L24-L24: use ```text for the ASCII flow diagram.
  • TESTING.md#L25-L25: use ```text for the expected Jest output.
🤖 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 `@MUESTRA_DEL_PRODUCTO.md` at line 24, Update the fenced block at
MUESTRA_DEL_PRODUCTO.md lines 24-24 to specify the text language for the ASCII
flow diagram, and update the fenced block at TESTING.md lines 25-25 similarly
for the expected Jest output; make no other documentation changes.

Source: Linters/SAST tools

MUESTRA_DEL_PRODUCTO.md-17-17 (1)

17-17: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix minor Spanish copy errors.

Use “generados por IA” on Line 17, “Escoger imagen” on Line 147, and “Resultado de práctica” on Line 217.

Also applies to: 147-147, 217-217

🤖 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 `@MUESTRA_DEL_PRODUCTO.md` at line 17, Corrige los errores menores de redacción
en MUESTRA_DEL_PRODUCTO.md: cambia “generada por IA” por “generados por IA” en
la línea 17, “Escoge imagen” por “Escoger imagen” en la línea 147 y “Resultados
de práctica” por “Resultado de práctica” en la línea 217.
DEPLOYMENT.md-12-13 (1)

12-13: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Replace the repository URL placeholder.

git clone [url-repo] is not executable documentation. Use the actual repository URL so a fresh developer can follow the setup without editing the command first.

🤖 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 `@DEPLOYMENT.md` around lines 12 - 13, Replace the [url-repo] placeholder in
the DEPLOYMENT.md cloning command with the repository’s actual URL, while
preserving the surrounding setup commands.
7-pull-requests.md-26-35 (1)

26-35: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Markdown formatting warnings.

Add text or typescript/json language identifiers to the untyped fenced blocks, and surround the table with blank lines so markdownlint passes cleanly.

Also applies to: 65-88, 91-113, 116-124, 152-159, 171-209

🤖 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 `@7-pull-requests.md` around lines 26 - 35, Update the fenced code blocks in
7-pull-requests.md, including the ranges referenced by the review, with
appropriate language identifiers such as text, typescript, or json. Add blank
lines immediately before and after the table so markdownlint formatting checks
pass without changing the documented content.

Source: Linters/SAST tools

backend/.env.example-4-6 (1)

4-6: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not advertise unsupported service-account paths.

firebaseAdmin.ts only parses FIREBASE_SERVICE_ACCOUNT as JSON, so a JSON-file path fails at startup. Either implement file loading or remove the path option.

🤖 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 `@backend/.env.example` around lines 4 - 6, Update the FIREBASE_SERVICE_ACCOUNT
documentation to describe only the supported single-line service-account JSON
value, removing the JSON-file path option unless firebaseAdmin.ts is also
updated to load files.
backend/scripts/seedFirestore.ts-45-63 (1)

45-63: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make word-card seeding idempotent.

Every run creates six new random documents for the same user, duplicating cards in subsequent demos. Use deterministic IDs or upsert existing cards by userId and normalizedTerm.

🤖 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 `@backend/scripts/seedFirestore.ts` around lines 45 - 63, Update the word-card
seeding loop in seedFirestore.ts to use deterministic document IDs or query
existing records by userId and normalizedTerm before writing. Ensure repeated
runs upsert or reuse the same cards rather than creating new random documents,
while preserving cardIds collection and existing field values.
backend/package.json-13-13 (1)

13-13: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Declare ESLint in the backend dependencies.

npm run lint uses eslint 'src/**/*.ts', but backend/package.json has no local eslint dependency and there are no repository ESLint config files. Add ESLint plus compatible dependencies and config so the script is reproducible outside CI/environmental fallbacks.

🤖 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 `@backend/package.json` at line 13, Add ESLint as a local backend dependency
and add the compatible parser/plugin dependencies and repository ESLint
configuration required by the existing lint script. Ensure npm install can
resolve the declared versions and npm run lint works reproducibly without
relying on globally installed tools or CI-provided configuration.
backend/src/routes/words.ts-9-12 (1)

9-12: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject whitespace-only terms before passing them to downstream APIs.

z.string().min(1) accepts " ", and WordService.createWord then calls generateDefinition(body.term.trim(), ...) and searchImages(body.term.trim(), ...) with an empty string. Move the trimmed required validation into the schema, e.g. z.string().trim().min(1, ...), so empty/whitespace-only submission fails at validation instead of at the AI/image integration.

🤖 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 `@backend/src/routes/words.ts` around lines 9 - 12, Update createWordSchema’s
term validator to trim input before applying the required minimum-length check,
while preserving the existing maximum length and error message. This must reject
whitespace-only terms during schema validation before WordService.createWord
reaches downstream APIs.
mobile/app/(tabs)/home.tsx-141-149 (1)

141-149: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix disabled-state contrast.

#9CA3AF on #F3F4F6 does not meet the required contrast threshold. Use a darker label and a visible border or higher-contrast disabled surface.

🤖 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 `@mobile/app/`(tabs)/home.tsx around lines 141 - 149, Update the disabled
practice button styles, practiceButtonDisabled and practiceButtonTextDisabled,
to meet the required contrast threshold by using a darker text color and adding
a visible border or higher-contrast background surface.

Source: Coding guidelines

mobile/.gitignore-33-34 (1)

33-34: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Ignore all real .env files.

This misses .env and non-local variants, so copied configuration can be committed. Expo recommends excluding .env files from version control. (docs.expo.dev)

Proposed fix
- .env*.local
+ .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 `@mobile/.gitignore` around lines 33 - 34, Update the environment-file patterns
in the gitignore entries to exclude the base `.env` file and all `.env`
variants, including non-local files, while retaining coverage for existing local
environment files.
mobile/app/(tabs)/home.tsx-96-149 (1)

96-149: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Apply Inter typography to the text styles.

home.tsx’s text styles don’t declare a fontFamily, so React Native will use the platform default font. Add the loaded Inter family/weight names to the text styles used in this screen.

🤖 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 `@mobile/app/`(tabs)/home.tsx around lines 96 - 149, Add the loaded Inter font
family and corresponding weight names to the text styles in home.tsx, including
logo, streakText, stats, practiceButtonText, practiceHint, and
practiceButtonTextDisabled. Preserve each style’s existing sizing, color,
alignment, and spacing while matching each font weight to the available Inter
variant.

Source: Coding guidelines

mobile/app/(tabs)/settings.tsx-21-26 (1)

21-26: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

handleLogout doesn't handle logout() failures.

If logout() throws, the subsequent store resets and router.replace never run, and no error is shown — the user is left on the Settings screen with no feedback.

🤖 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 `@mobile/app/`(tabs)/settings.tsx around lines 21 - 26, Update handleLogout to
catch failures from logout(), preserve the store reset and router.replace flow
when logout succeeds, and surface a user-visible error when it fails so the user
receives feedback instead of remaining silently on Settings.
mobile/app/(tabs)/add-word.tsx-66-74 (1)

66-74: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Success confirmation alerts use an empty title and a bare checkmark glyph. Both save flows show Alert.alert('', '✓', ...), which conveys no meaningful information to sighted or screen-reader users.

  • mobile/app/(tabs)/add-word.tsx#L66-L74: use a localized title/message, e.g. Alert.alert(t('addWord.savedTitle'), t('addWord.savedMessage'), ...).
  • mobile/app/card/[id].tsx#L42-L42: same fix, e.g. Alert.alert(t('cardDetail.savedTitle'), t('cardDetail.savedMessage'), ...).
🤖 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 `@mobile/app/`(tabs)/add-word.tsx around lines 66 - 74, The success
confirmation alerts use an empty title and opaque checkmark message. Update the
Alert.alert calls in mobile/app/(tabs)/add-word.tsx lines 66-74 and
mobile/app/card/[id].tsx line 42 to use localized savedTitle and savedMessage
translation keys, preserving their existing actions and navigation behavior.
mobile/app/card/[id].tsx-35-35 (1)

35-35: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcoded, non-localized fallback text.

"Card not found" bypasses useTranslation, unlike every other string in this file, breaking the bilingual UI for this reachable edge case (stale/deep-linked id).

🌐 Suggested fix
-        <Text style={{ textAlign: 'center', marginTop: 40 }}>Card not found</Text>
+        <Text style={{ textAlign: 'center', marginTop: 40 }}>{t('cardDetail.notFound')}</Text>
🤖 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 `@mobile/app/card/`[id].tsx at line 35, Replace the hardcoded “Card not found”
text in the card detail fallback with the existing useTranslation localization
flow. Add or reuse an appropriate translation key and render its translated
value while preserving the current fallback styling and behavior.
mobile/app/practice.tsx-122-140 (1)

122-140: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a stable index-based key for MCQ options.

exercise.options comes from LLM-generated content, not an option id, so key={option} can duplicate when the generated text contains the same option more than once. Pass the array index through the map callback or attach/remove a hidden stable id; changing it here is safe, but don’t rely on option as the key.

🤖 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 `@mobile/app/practice.tsx` around lines 122 - 140, Update the exercise.options
map callback in the practice screen to receive the array index and use that
stable index-based value for each TouchableOpacity key. Keep the option text and
selection behavior unchanged, and stop using option as the key.
mobile/app/(tabs)/_layout.tsx-11-17 (1)

11-17: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Increase inactive-tab contrast.

#9CA3AF on white is below the required contrast ratio for tab labels and UI states. Use a darker neutral such as #6B7280.

🤖 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 `@mobile/app/`(tabs)/_layout.tsx around lines 11 - 17, Update the
tabBarInactiveTintColor setting in the tab bar configuration to a darker
neutral, such as `#6B7280`, while leaving the active tint and tab bar styling
unchanged.

Source: Coding guidelines

mobile/app/results.tsx-20-23 (1)

20-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate result query parameters before rendering.

A deep link such as ?correct=abc renders NaN; duplicate query keys can also arrive as arrays. Parse finite non-negative integers and clamp correct to total before calculating the result.

🤖 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 `@mobile/app/results.tsx` around lines 20 - 23, Update the result parameter
parsing around correctNum, totalNum, and streakNum to safely handle scalar or
array query values, accepting only finite non-negative integers and applying the
existing defaults otherwise. Ensure total is valid before calculating isPerfect,
and clamp correct to total so malformed or excessive values cannot produce
invalid result state.
mobile/app/(auth)/register.tsx-30-31 (1)

30-31: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Move remaining user-facing literals into i18n.

These strings remain English after changing locale, contradicting the bilingual UI scope.

  • mobile/app/(auth)/register.tsx#L30-L31: replace the password-length literal with a translation key.
  • mobile/app/(tabs)/_layout.tsx#L20-L25: localize the Home tab title.
🤖 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 `@mobile/app/`(auth)/register.tsx around lines 30 - 31, Move the
password-length message in register.tsx into the existing i18n resources and use
its translation key in the validation error. Also localize the Home tab title in
_layout.tsx through the same translation mechanism; update both affected files
as specified.
mobile/app/(tabs)/_layout.tsx-24-24 (1)

24-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hide decorative tab icons from screen readers.

Each emoji duplicates the tab label and is read aloud by screen readers. Add aria-hidden="true" to the tab icon Text components at lines 24, 31, 38, and 45 so only the tab label is announced.

🤖 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 `@mobile/app/`(tabs)/_layout.tsx at line 24, Add aria-hidden="true" to the Text
components returned by each tabBarIcon in the tab layout, including the icons at
lines 24, 31, 38, and 45, so decorative emojis are hidden from screen readers
while tab labels remain announced.

Source: Coding guidelines

🧹 Nitpick comments (5)
2-arquitectura-del-sistema.md (1)

317-323: 📐 Maintainability & Code Quality | 🔵 Trivial

Document the OTA update flow.

Add EAS Update channels/branches plus rollout and rollback steps; otherwise JavaScript-only fixes require a new binary build. Based on learnings: Use Expo EAS Build and Updates for continuous deployment and over-the-air updates.

🤖 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 `@2-arquitectura-del-sistema.md` around lines 317 - 323, Amplía la sección
“Mobile (Expo)” para documentar el flujo de actualizaciones OTA con EAS Update:
define los canales o ramas utilizados, explica cómo publicar una actualización y
cómo promoverla durante el rollout, e incluye los pasos para revertir a una
versión estable. Mantén diferenciados los cambios JavaScript que pueden
distribuirse OTA de los cambios nativos que requieren un nuevo build.

Source: Learnings

backend/src/repositories/StreakRepository.ts (1)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Use camelCase for exported repository singleton objects. Both are const objects, not classes. As per coding guidelines, “Use camelCase for variable and function names.”

  • backend/src/repositories/StreakRepository.ts#L16-L16: rename StreakRepository to streakRepository and update imports.
  • backend/src/repositories/UserRepository.ts#L6-L6: rename UserRepository to userRepository and update imports.
🤖 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 `@backend/src/repositories/StreakRepository.ts` at line 16, Rename the exported
singleton object StreakRepository to streakRepository in
backend/src/repositories/StreakRepository.ts:16-16 and update every import and
reference. Rename UserRepository to userRepository in
backend/src/repositories/UserRepository.ts:6-6 and update every import and
reference, preserving behavior.

Source: Coding guidelines

mobile/LICENSE (1)

1-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Leftover Expo template license misattributes copyright.

This is Expo's own MIT license text (copyright "650 Industries, Inc. (aka Expo)"), copied verbatim from the create-expo-app scaffold. As committed, it incorrectly states that this project's code is copyrighted by Expo rather than the actual project owners.

📄 Suggested fix
-Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
+Copyright (c) 2026 <project author/organization>

Or remove this file entirely if the project doesn't intend to publish under Expo's license.

🤖 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 `@mobile/LICENSE` around lines 1 - 22, Remove the leftover Expo template
LICENSE file, or replace its Expo-specific copyright attribution with the actual
project owners and the intended project license text.
mobile/app/(tabs)/add-word.tsx (2)

99-110: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Interactive elements without visible text lack accessibility labels. The coding guidelines require aria-label-equivalent descriptive labels on interactive elements lacking visible text; in React Native this maps to accessibilityLabel/accessibilityRole, which are absent here.

  • mobile/app/(tabs)/add-word.tsx#L99-L110: add accessibilityRole="button" and accessibilityLabel={t('addWord.selectImage')} (or similar) to the image-only TouchableOpacity.
  • mobile/app/(tabs)/dictionary.tsx#L29-L48: add accessibilityRole="button" and accessibilityLabel (e.g. `View ${item.term}`) to the card TouchableOpacity to expose it as a single labeled action.
🤖 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 `@mobile/app/`(tabs)/add-word.tsx around lines 99 - 110, The image-only
TouchableOpacity in mobile/app/(tabs)/add-word.tsx lines 99-110 must expose
accessibilityRole="button" and a translated descriptive accessibilityLabel such
as t('addWord.selectImage'). Also update the card TouchableOpacity in
mobile/app/(tabs)/dictionary.tsx lines 29-48 with accessibilityRole="button" and
a label identifying the item, such as “View ${item.term}”.

Source: Coding guidelines


183-260: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No screen sets the required 'Inter' font family. The coding guidelines mandate 'Inter' (with system fallbacks) for all typography, but every reviewed StyleSheet.create() block across the mobile app omits fontFamily, so text renders in the platform default font everywhere.

  • mobile/app/(tabs)/add-word.tsx#L183-L260: add fontFamily: 'Inter' (or a shared text style) to title, label, input, buttonText, etc.
  • mobile/App.tsx#L13-L20: add fontFamily to the Text style if this component remains in use.
  • mobile/app/(tabs)/dictionary.tsx#L89-L132: add fontFamily to title, tabText, cardTerm, cardDefinition, etc.
  • mobile/app/(tabs)/settings.tsx#L82-L122: add fontFamily to title, sectionLabel, langText, logoutText, etc.
  • mobile/app/card/[id].tsx#L110-L158: add fontFamily to term, label, saveButtonText, etc.
  • mobile/app/practice.tsx#L157-L206: add fontFamily to progress, questionText, optionText, etc.

Consider centralizing this in a shared theme/typography module instead of repeating it in every StyleSheet.create().

🤖 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 `@mobile/app/`(tabs)/add-word.tsx around lines 183 - 260, Apply the required
Inter font family consistently across all typography styles, preferably through
a shared theme or typography module. Update styles in
mobile/app/(tabs)/add-word.tsx lines 183-260, mobile/App.tsx lines 13-20,
mobile/app/(tabs)/dictionary.tsx lines 89-132, mobile/app/(tabs)/settings.tsx
lines 82-122, mobile/app/card/[id].tsx lines 110-158, and
mobile/app/practice.tsx lines 157-206, including each title, label, text, input,
button, and content style used for rendered text; preserve existing visual
properties and use the shared style where appropriate.

Source: Coding guidelines

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.

1 participant