Round Mode: proving the scores were inflated, then making it sound like a game
Round Mode is Learnova’s core loop. You type a topic and play an escalating ladder of retrieval rounds, where the hint gets stripped away one rung at a time: multiple choice, then fill in the blank, then assemble the sentence from chips, then say it in your own words with nothing on screen to lean on. The research is clear that retrieval practice beats rereading, and that producing an answer beats recognising one. The ladder is that finding turned into a game.
The real work today was less fun. I noticed correct answers seemed to land in the first slot too often, so I wrote a script that generates questions through the actual route and counts where the answer ends up. Warm up was 5 out of 5 at index 0. Round 1 was 7/5/3/0. Every score the app had ever produced was inflated, because you could do well without reading the question.
The cause was the prompt itself: every JSON example I gave the model used “answerIndex”: 0, so it learned the pattern. Fixed by shuffling server side after generation, for every format with ordered options, including the chip tray, which had been shuffling in the browser where nothing could measure it. Re-ran across 197 questions and four topics. Chi-squared on every format now sits well under the 5 percent critical value.
Also killed repeated questions. Deduplication compares what a question asks for rather than its wording, so rephrasings do not slip through. When a topic runs dry, the generator gets told to ask about distinctions and consequences instead of drawing from the same pool again.
Then I drove headless Chrome over all 21 visual presentations and found three things no code review would have caught. Constellation stars stacked in a column because Tailwind emits .relative after .absolute. Doors swung 72 degrees, turning the correct answer edge on and unreadable at the exact moment it mattered. And a burst balloon faded to 0.25 opacity, which meant a wrong answer got softened by becoming illegible. That last one is a product failure, not a visual one, since the whole point is that feedback stays honest.
Then audio. The generated background music sounded like fans whirring, so it is gone, replaced with “8bit Dungeon Level” by Kevin MacLeod, credited in-app and in the repo under CC BY 4.0. Music and sounds are on by default, because the feel is the point, with a mute for anyone in a library.
Autoplay turned out to be its own small saga. No browser lets a page make sound before you touch it, so the track requests playback on mount, the rejection arms a listener, and the first click or keystroke starts it. Testing that surfaced a real bug I had previously reasoned away: React batches state, so if the very first thing you touched was the mute button, the listener fired while the state still said “on”, started the music, and paused it a frame later. A blip of music at precisely the person switching it off. Now set synchronously too.
Two lessons from the test rig that mattered more than the result. Calling element.click() does not grant user activation in Chrome, so my first run looked like a total product failure and was actually a test artifact. And running with a permissive autoplay flag meant the gesture fallback was never exercised at all. Testing under the real policy is the only version that tells you anything.
Still unverified: the assembled round in a live browser. The presentations are proven to render, but nobody has played the full ladder yet. That is tomorrow. Built with Claude Code. AI-assisted commits carry Co-Authored-By trailers.
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.