Documentation

Architecture & System Design

Mathua is a single Go binary with a web delivery mode. Click nodes in the diagrams below for details.

View full system design doc on GitHub

I. Five-Layer Architecture

The system is organized into five layers. The UI has a single implementation -- the web frontend (React/Next.js) -- calling into the Go engine through the REST API. The API exposes 25+ REST endpoints through net/http. The Core Engine handles DAG loading, SM-2 scheduling, problem generation, mastery tracking, scoring, and the CAT diagnostic. The Grading layer dispatches to 6+ grader strategies with a SymPy subprocess for symbolic math. Storage is abstracted behind a Repository interface.

LOADING ARCHITECTURE DIAGRAM

II. Data Model

Five core tables store all application state. The `concept_progress` table embeds SM-2 spaced repetition fields (repetitions, interval, efactor) alongside mastery state and weakness scores. All DDL uses CREATE TABLE IF NOT EXISTS for idempotent bootstrapping. Incremental migrations add columns with ALTER TABLE guarded by error-checking.

LOADING DATA MODEL DIAGRAM

The SM-2 upsert uses SQLite's ON CONFLICT ... DO UPDATE to atomically save all progress fields in one statement. XP tracking is date-aware: xp_today resets when xp_date differs from the current date, preserving xp_total as a lifetime accumulator.

The PostgresStore exists as a stub with all methods returning "not implemented." The same Repository interface works for both databases -- the schema is identical.

III. End-to-End Request Flow

When a student submits an answer, the request passes through 12 distinct stages before the next question is served. The entire pipeline runs synchronously in a single Go goroutine, completing in under 100ms for numeric grading and under 500ms for SymPy-based grading (including subprocess round-trip).

LOADING REQUEST FLOW DIAGRAM
1. Client sends POST /api/answer with {session_id, answer, elapsed}.
2. Auth middleware validates JWT and injects student ID into request context.
3. Engine.SubmitAnswer routes to the correct grader by grading_type.
4. The Mastery Machine evaluates whether a state transition is earned.
5. SM-2 Compute recalculates repetition count, interval, and easiness factor.
6. repo.UpsertProgress atomically saves all fields via ON CONFLICT DO UPDATE.
7. If weakness > 0.3, it propagates to dependent concepts at w * 0.3.
8. repo.RecordAttempt stores the raw answer for analytics.
9. XP is computed (base * timeMult * streakMult) and added to the student.
10. Engine.NextQuestion asks the scheduler for the next concept and generates a problem.

IV. Grading System

The grading system uses a strategy pattern dispatched by grading_type. Numeric grading (int, float, fraction, mixed, scientific notation) is pure Go with big.Rat for exact rational arithmetic and 1e-9 float tolerance. Multiple choice is case-insensitive with single-letter matching. Comparison handles operators: > < = >= <= !=. Ordering and tuple graders do positional exact matching.

For symbolic math -- polynomial and expression grading -- Mathua spawns a long-lived Python subprocess running SymPy. The Go client sends a JSON pair over stdin; SymPy parses both into expression trees and checks equivalence via simplify(expected - answer) == 0. If Python/SymPy are not installed, grading falls back gracefully to a pure-Go symbolic string normalizer. The subprocess has a 10-second timeout and 500-character input limit.

The complex grader preprocesses polar form, handles plus-minus notation, and delegates to SymPy. In total there are 8 grading strategies: numeric, multiple choice, comparison, ordering, tuple, complex, symbolic (fallback), and SymPy (polynomial/expression).

The D2 diagram for the grading pipeline is available in the system design documentation.

V. Computerised Adaptive Testing

The diagnostic engine locates a student's knowledge frontier using binary search on the topologically sorted concept graph. This reduces the assessment from 284 questions (one per concept) to approximately 20-35.

LOADING DIAGNOSTIC DIAGRAM
1. The concept graph is sorted topologically. The diagnostic starts at the midpoint.
2. Correct answers within the time limit move the probe forward toward harder concepts.
3. Incorrect or slow answers move backward toward foundational material.
4. After 3 consecutive correct answers in a region, the frontier is considered located.
5. The diagnostic records a mastery estimate for every concept passed through.
The diagnostic can be retaken at any time. Retaking does not delete progress: it creates a new estimate that is merged with existing data, always preferring the more optimistic estimate.

VI. Key Design Decisions

Go over Python or Node.js?

Single ~15MB binary that is both API server and static file server. No runtime dependencies. ~5ms startup vs ~500ms for Python. Goroutines for concurrency. SymPy bridge via os/exec.

Generated questions over static bank?

Every problem is procedurally generated by a parameterized Go function. Infinite variety, no memorization, adaptive difficulty. Trade-off: generation latency (ns for numeric, ~200ms for SymPy). Worth it.

SQLite + PostgreSQL instead of one database?

SQLite for zero-config local development, PostgreSQL for production web. Same schema, same Repository interface. Transparent via DATABASE_URL. The PostgresStore is currently a stub awaiting implementation.

Raw SQL over an ORM?

Five tables, straightforward relationships. Raw SQL gives full control over the SM-2 upsert query and leaderboard computation. Transparent debugging. Easy to port between SQLite and PostgreSQL.

For the full system design document covering all 11 sections in detail (including SM-2 algorithm internals, scoring formulas, level system, and complete API reference), see the system design markdown document.