This file records architecture and product decisions made during the revamp. Keep each entry short, concrete, and tied to implementation evidence.
- The backend started as a single Flask app in
backend/thinkmate.py. - Day 1 split the backend into a minimal Flask package under
backend/app/and renamed the stale backend entry point tobackend/main.py. - The frontend uses React 19, Vite, Vitest, and Wouter.
- Persistence is currently SQLAlchemy with SQLite fallback and optional Supabase URI.
- Local SQLite stores conversation, quiz, and flashcard records as untracked runtime data.
- Day 2 adds session-scoped in-memory FAISS retrieval with local
all-MiniLM-L6-v2embeddings. - Locust evidence now includes a fixed 500-user local Gunicorn run. Quiz and flashcard generation uses Pydantic validation with bounded repair retries.
Decision: Phase 1 will focus on backend structure, testability, Docker correctness, and SQLite WAL before adding new user-facing workflows.
Reason: The resume claims depend on backend behavior. A frontend redesign before real RAG, validation, and load testing would make the app look better without making the technical claims true.
Decision: The RAG layer will use local all-MiniLM-L6-v2 embeddings and in-memory FAISS indexes scoped by session.
Reason: This directly supports the privacy and session-isolation claim. It also keeps retrieval fast and avoids sending raw study material to a vector database service.
Decision: Recall@5, retrieval latency, malformed JSON rate, and load-test results must be measured and saved before appearing in README or resume wording.
Reason: The project should exceed the existing resume claims with traceable evidence, not approximate or fabricated numbers.
Decision: Quiz and flashcard generation will use Pydantic schemas plus a bounded repair loop.
Reason: Current regex cleanup handles only simple formatting problems. Schema validation is more defensible and lets us report actual malformed-output recovery behavior.
Decision: Phase 6 is the complete frontend redesign. Earlier phases may add small UI hooks only when needed to test backend workflows.
Reason: The final interface should be designed around the real study workspace, source-grounded answers, quiz generation, flashcards, and analytics.
Decision: Phase 8 will add additional high-value product features after the baseline revamp, RAG, validation, load testing, redesign, and production deployment phases are complete.
Reason: Extra features should build on stable and deployed foundations. We will brainstorm exact Phase 8 scope later, but candidates include adaptive review, exports, source citations, topic mastery tracking, and shareable study packs.
Decision: Subsequent commits should be made directly on main and pushed to origin/main; do not create feature branches unless explicitly requested later.
Reason: The user wants a seamless linear workflow for this project instead of branch-and-merge coordination.
Decision: Keep commits logically separate and use commit messages with a concise subject plus a short body explaining what changed and why.
Reason: Separate commits make review and rollback easier. The body should preserve the reasoning behind each change without making the subject line noisy.
Decision: The backend now uses backend/app/ for factory, models, routes, and services, with backend/main.py as the executable entry point.
Reason: thinkmate.py was stale project branding. main.py is a standard backend entry-point name and keeps Docker/local startup aligned with the LearnLoop project name.
Decision: Phase 7 will make the completed core app deployment-compatible and deploy it before Phase 8 product work starts.
Reason: Render, Vercel, and any separate model service must be validated against the real RAG and load-test architecture before additional features expand the deployment surface.
Decision: RAG chunks use the MiniLM tokenizer for 512-token boundaries with 64-token overlap. Embeddings are pooled across model-sized windows so all chunk tokens contribute despite the model's 256-token sequence limit.
Reason: Sending a 512-token chunk directly to all-MiniLM-L6-v2 silently truncates content after its usable sequence limit and weakens retrieval for information near the end of the chunk.
Decision: Day 6 includes the minimum backend work required for persistent study sessions, material management, session-grounded flashcards, progress, history, and an isolated demo journey.
Reason: A frontend that displayed recent sessions, durable materials, or session-based practice without real persistence would misrepresent product behavior.
Decision: Guest mode creates a random browser identifier and sends it with study requests. Guest records are scoped to that identifier, while authenticated requests use the verified Supabase user identity.
Reason: Public demo visitors need isolated state, and account users need a separate identity-backed path.
Decision: The Benchmarks page renders checked-in measured results instead of adding a runtime benchmark API.
Reason: Benchmark data changes only when a deliberate evaluation run updates its report. A live endpoint would add infrastructure without improving evidence quality.
Decision: The Day 6 frontend uses Vite and Vitest, with Wouter for the small client-side route surface.
Reason: Create React App and its transitive dependencies produced unresolved security advisories and stale build warnings. The smaller toolchain preserves the existing routes while keeping the production dependency audit clean.
Decision: backend/conversations.db is local runtime state and is ignored by
Git. Docker Compose stores its SQLite database in the learnloop-data named
volume.
Reason: Visitor sessions and generated study artifacts are mutable data, not source code. A named volume preserves local Docker data without publishing a developer database.
Decision: Supabase Auth provides email/password accounts. Authenticated requests carry a verified Supabase bearer token, while guest mode remains browser-session scoped. The Supabase schema stores account-owned learning activity but does not include a durable source table.
Reason: Users need saved history and scores without making uploaded PDFs or pasted source material permanent server-side data.
Decision: A PDF is treated as one source, extracted as selectable text, and chunked using the existing session retrieval pipeline. Users can add a PDF and pasted text to the same learning space.
Reason: This keeps the original source workflow simple while allowing real study documents without requiring a separate document-management system.
Decision: The first hosted backend uses local MiniLM in-process with one Render
worker. The RAG service now selects either this local provider or an HTTP
embedding provider through EMBEDDING_PROVIDER.
Reason: A remote Modal service adds another deployment, authentication, timeout, cold-start, and failure dependency before the core app has a verified public path. One worker avoids duplicating the model and keeps its in-memory FAISS index coherent within a process. The HTTP boundary preserves a migration path when Render memory, model download time, or scale-out requirements justify Modal.
Decision: LearnLoop no longer supports local MiniLM or FAISS as an application
runtime. Modal owns document chunking and all-MiniLM-L6-v2 embeddings. Tests
use a deterministic fake provider instead of downloading the model.
Reason: The Render Free service already hosts other portfolio projects and the in-process model caused unacceptable startup and memory pressure. Removing the model and FAISS dependencies makes the API process smaller and keeps the deployed architecture identical to the tested remote-provider path.
Decision: The API stores source chunks and 384-dimensional embeddings in Supabase pgvector and retrieves them with cosine-distance search. Session indexes are no longer process memory and do not reset after restart or scale-out.
Reason: Modal solves model placement, not data persistence. Durable vectors are required for retrieval to remain available after Render restarts. Persisting chunks also changes source retention from the earlier session-only design and must remain visible in the privacy and deployment documentation.