VarsityOS
Infrastructure for first-generation students
Built around one question: what does a first-generation South African university student actually need in order to survive the year, not just pass it?
- 595 commits
- 2026-03-01 → 2026-08-03
- TypeScript 94.6%
- private repository
The student-productivity category assumes a student with a laptop, a data plan, a stable home and a parent who has done this before. Change any one of those assumptions and the product stops fitting. Change all four and you are describing most first-generation students in South Africa.
So the feature list reads oddly next to a Notion clone: NSFAS allowance budgeting, load-shedding awareness, eleven languages, crisis support, and balancing a part-time job against a timetable. None of that is a productivity feature. All of it is what determines whether the year finishes.
4 decisions
What the constraint forced.
Each of these is a choice with a reason attached. The reason is the part worth reading — a stack list tells you what was used, and nothing at all about the judgment that put it there.
- 01
Cache the knowledge base, inject the student.
The AI companion runs against a large standing knowledge base held in the prompt cache, with the individual student's real budget, tasks, exam dates and mood injected fresh on every call. The split is deliberate: the expensive, stable, carefully-written part is paid for once; the part that must be current is small. Advice that does not know your actual balance is horoscope writing.
- 02
Crisis detection is keyword matching, and that is the correct choice.
A student in crisis gets emergency resources shown by deterministic keyword match, not by model judgment. A model is better at nuance and worse at guarantees, and this is the one path in the product where a false negative is unacceptable and a false positive costs nothing but a visible helpline. Use the dumb, auditable mechanism where the failure is asymmetric.
- 03
Meter the free tier in the counter, not the UI.
Ten free AI messages a month, enforced by a Redis counter checked before generation. Enforcing a limit in the interface means enforcing it nowhere — the API is the product surface for anyone who opens devtools, and a free tier that can be bypassed is a free product with extra steps.
- 04
Correct the price on the page, in public, when it drifts.
Three separate commits in this repository exist only to make a published price match the tiers that actually exist — a tier that had been removed, a range that had gone stale. That is unglamorous and it is the job. A wrong price on a live page is not a content bug; it is a promise the system cannot keep.
The part that survives the build.
Constraints local to a market are product features, not localisation.
Load shedding is not a translation string. It changes whether you can assume a network, whether a write can be deferred, and whether an evening study block is a reasonable default. Software written for a market where the power stays on has these assumptions everywhere and cannot list them, because nobody wrote them down.
Row-level security will bite you on recursion before it bites you on access.
Group membership policies that check group membership are a loop, and Postgres says so at the least convenient moment. The fix is a security-definer function that breaks the cycle; the lesson is that RLS is a small program per table and deserves to be read as one.
A payment gateway integration is finished when a failed payment is also correct.
Most of the payment work in this repository is not the happy path. It is field ordering, character encoding in item names, whitespace in a passphrase, and getting return, cancel and notify URLs pointing at the right host after a domain move. The happy path takes an afternoon.
Built with
- Next.js 14
- TypeScript (strict)
- Supabase (Postgres + RLS)
- Claude API
- Zustand
- React Hook Form + Zod
- Recharts
- PayFast
- Vercel
Measured
- commits
- 595
- last commit
- 2026-08-03
- journal sections
- 37
Written from Campus Compass build journal — dossier, 54 sections. The dossier is composed from the journal rather than quoted out of it — the raw sections stay behind the review gate described in docs/THE_FORGE.md §4, and every figure above is read from the GitHub API at build time.
