This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend.
What I Built
My best friend SMS is studying for the NUST entry test — the exam that decides whether he gets into Pakistan's top engineering university. For months he cycled through quiz apps, and they all had the same flaw: they'd generate questions, he'd answer them, and the app would forget everything by the next session. Nobody remembered what he got wrong. So he kept losing the same marks twice.
I built NEVERTWICE to fix exactly that. It's a study app with a memory. The loop is simple:
mistakes → weakness graph → targeted drill → graph update
You answer questions in a drill. Get one wrong and you classify it by topic with one tap. The weakness graph updates — your threats, ranked worst first. The next drill pulls hardest from the top of that graph. Nail the topic next time and the threat shrinks. Four tabs, zero clutter: DRILL | THREATS | PROGRESS | FILES. Built phone-first, because that's where SMS studies.
There's also a full 200-question, 180-minute mock for exam-day simulation, and a trajectory view so he can watch his accuracy climb across sessions instead of guessing whether he's improving.
The Saturday test
I didn't build this in a vacuum. On Saturday, October 3rd, I handed the app to SMS with one rule: if the drills don't feel uncannily personal — if it can't show him things he actually got wrong — we kill the concept.
It passed. And he broke three things in the process, all fixed the same day:
- Classifying mistakes was too slow. Tapping through categories after every wrong answer killed his flow. Now it's one tap, with a 700ms auto-advance.
- Explanations referenced answer letters. "The correct answer is B" means nothing when the options shuffle. Now the debrief names the correct answer by its content, not its letter.
-
Motion sensitivity. A full motion pass plus
prefers-reduced-motionsupport, because a study app shouldn't make anyone dizzy.
Here's what he said after the session:
"I definitely see myself using your app to prepare for my future NUST entry test cause on this app i can track my progress via a graph and i can also solve questions from the real NUST NET type mock exam allowing me to understand the depthness of exam questions before I actually attempt the real test"
— SMS, my best friend and the reason this exists
Demo
Try it live — no signup, no paywall, no ads: https://nevertwice.vercel.app
Code
https://github.com/mateir0/nevertwice — public repo, MIT licensed.
How I Built It
Next.js on Vercel, with question generation running server-side through Groq on an open-weight model (openai/gpt-oss-120b). The API key never touches the client.
A few things I'm proud of, stated plainly:
- It works with no internet. A 120-question verified bank ships with the app, and a service worker drops it into offline bank mode. Drills run 100% on the bank with zero AI when you're offline.
- Your data is yours. Mistake history and the weakness graph live in browser storage. Dossier export/import means you back up or move everything yourself.
- Honest fallbacks. When AI generation fails or quotas run out, the app says so — "OFFLINE — BANK MODE", "DAILY PRINT QUOTA SPENT — DRILLS UNAFFECTED" — instead of silently degrading into repeats.
- Security taken seriously. Prompt-injection guards on the generation taxonomy, prototype-pollution rejection, body-size limits, security headers, server-side rate limits. There's a SECURITY.md in the repo.
- 74/74 tests green.
One honesty note, because it matters: NUST doesn't release official past papers, and the app says so in its own UI. The mock is modeled on the NET format. It doesn't pretend to be leaked papers.
Built solo over a weekend with AI pair-programming — the AI handled the boilerplate, I handled the judgment calls. The best feature wasn't anything I planned: watching a red threat shrink because you finally nailed a topic three sessions in a row. I only discovered that by watching SMS use it.
Why Does Open Innovation Matter?
The challenge asks this directly, so here's the honest answer for NEVERTWICE:
Does it run with no internet? Yes — the offline bank mode exists because of this. The 120-question bank and the service worker mean SMS can drill on a bus with no signal. A closed API-only app couldn't do that.
Does it keep his data off servers he doesn't control? His mistake history — the most personal data in the app, a map of everything he doesn't know — never leaves his browser. Only the generation prompts go to Groq's cloud. If he doesn't trust that, the bank works fully offline.
Can the model be swapped? Yes, and that's the point of building on an open-weight model. The question validator and the fallback logic don't care which model generates the questions. If Groq throttles, the bank carries him. If a better open model appears tomorrow, it's a config change, not a rewrite. No vendor lock-in on the intelligence.
Does it cost him anything? Nothing. Free tier for generation, free bank offline, free hosting. A gift for a friend shouldn't come with a subscription.
Open didn't just make this cheaper to build. It made it his — inspectable, forkable, exportable, and usable with zero infrastructure. For a study app holding someone's exam future, that ownership isn't a nice-to-have. It's the whole point.
My Agent Session
Built with OpenCode and Muse Spark 1.3, one consolidated prompt per task. The full session record — real transcripts, verbatim quotes, no reconstruction — is in the repo: agent-session.md.
Five moments tell the story: the drill engine build, the SMS feedback fixes, the mock Groq-budget fix, the security audit, and the structured logging. The drill planner learned to write dossier-voiced reasons like "Quadratic Equations keeps bleeding — 6 misreads in 3 days. 7 questions engineered to punish skimming."
My favorite exchange: during the mock-fix verification, a run hit a 19-minute Groq throttle, and the call was "STOP the live mock verification (kill process 2992) — do NOT chase the 'no 429' criterion." Knowing when to stop verifying is engineering too.
For SMS: never twice.
AI disclosure: built with OpenCode and Muse Spark 1.3; AI assistance was used to prepare this submission.
Top comments (0)