x‑hakt

Teaching others, one morsel at a time

I've been working on another project for bite-sized learning with the aim of teaching 'anyone anything' using small questions, used in various ways to bring meaning and context to knowledge they're imparting. There's a few patterns to teaching this way, like using the same concept four or five times across a course and never ask identical questions. Beyond the broad(side) stroke concept is the app mechanics: The mission design, the spaced-review engine that decides when you've actually got something, the sync that never invents your history, and the consent model sitting under class/family reporting.

infrastructure

nextjspostgrestypescript

Rinse, but don’t repeat

Mission four of a course I’m building teaches the fundamental difference between RAM and storage. Mission ten needs to test that same idea again, because that’s how the reinforcement is supposed to work, but if mission ten just asks the same questions for the second time, then all I’ve really built is a memory test. So mission ten re-words the concept through the context of a photo editor that crashes mid-edit instead, and the correct answer isn’t “click RAM”, it’s “work out that the crash killed the unsaved edit because that copy only ever lived in RAM, and the saved version is sitting untouched in storage.” Same fact, but requires a higher level of demonstrative knowledge to answer correctly.

That constraint, teach a real skill through repetition without the repetition turning into a party trick where people memorise the pattern of the quiz, runs under the whole course. Multiple systems do the actual work of holding that up. None of them are clever in isolation, but together they stop the thing from turning into Trivial Pursuit with extra steps.

Same concept, six disguises

Every activity is one of six interaction types (tap, choose, predict, sort, drag, diagnose) and no two activities inside a mission are allowed to be the same question wearing a different noun. Mission one on “what is a computer” runs input, processing and output through a keyboard-and-monitor example, then a calculator doing arithmetic, then a diagnose activity where the monitor’s switched off and you have to say which stage of the cycle just went dark. Three activities, one underlying idea, three different ways of understanding. The curriculum QA report that has to pass clean before anything ships explicitly flags “complete answer text repeated in the teaching context or the prompt” as a warning, because if the wording gives the answer away you haven’t tested recall at all, you’ve tested reading comprehension and called it a lesson.

The bit doing the real reinforcement work doesn’t live inside one mission though, it lives in how a skill gets mapped across the whole course. A skill like “reason about processing and memory” isn’t just tagged to mission four, it’s tagged to missions four, five, ten and twenty as well.

1234567891011121314151617181920”reason about processing and memory”, same skill, four separate check-ins
One skill, four missions apart across a twenty-mission course. The evidence for it keeps accumulating well after the mission that first introduced it.

Twenty missions in, you’re still getting quietly tested on a concept you met on day one, in a scenario you’ve never seen, and the course knows to keep doing that because the skill’s evidence keeps accumulating from every mission it’s tagged to, not just the first one you happened to get right.

Nobody gets an A for one lucky guess

Something I really like about this project is the part that decides you’ve “got” something, and it’s fussier about that than I originally intended. Every completed mission writes a small timestamped event: which skills it touched, how many activities, how many were right first try. Nothing gets deleted, nothing gets rewritten, it’s an append-only ledger of what actually happened and when.

by design too, each event carries a random ID, so if the same completion gets sent twice by an over-eager retry it still only counts once. Small detail, absolutely the difference between “the sync is boring and correct” and “this learner has 4,000 XP from clicking their wifi off at the wrong moment.”

A skill only reaches the top state, “strong evidence”, once every mission tagged to it is complete, there’ve been at least three separate practices spanning at least seven days, at least one of those was a proper mixed review across missions rather than a mission replay, and the first-attempt accuracy across all of it sits at 80% or better. Fall short of that and the user gets “completion evidence”, still real, just not dressed up as more than it is.

1 practice2nd3rd4th5th+1d3d7d14d30d
The review interval grows with each timestamped practice, drawn to actual scale, which is why the first bar looks like a rounding error next to the last one.

When a skill’s overdue, a stitches together one activity each from up to three of the missions that already fed that skill, not a full replay, and worth its own small XP reward and its own separate evidence event so it never gets confused with a mission completion. When the interval runs out and nothing’s overdue on the calendar, the label doesn’t invent urgency it hasn’t earned either. If there’s no trustworthy timestamp behind an old completion, because the record predates any of this existing, it stays as plain completion evidence.

It forgets nothing, and it doesn’t lie about it

Guest progress lives entirely in the browser and that’s the actual source of truth until you decide otherwise, no account required to play a single mission. Make an account and the first authenticated load claims whatever’s in local storage and merges it against whatever the server already has for you, which sounds simple and is exactly the kind of “simple” that ruins your week if you get the merge rule wrong.

BrowserServerunion completedmax(xp, streak)dedupe events by idthe record you keep playing on
A sign-in merge, not a sync. Completed missions union together, XP and streak take whichever side is ahead, evidence events dedupe by their own ID.

Completed missions union together, so nothing you did as a guest gets thrown away and nothing gets counted twice. XP and streak take the higher of the two sides rather than adding them, because a naive add would let you round-trip between two devices and rack up XP for existing. Evidence events merge by their own ID for the same idempotency reason as above. One rule in place is: an old record never gets a fabricated history bolted on to make the newer features look retroactively populated. A completion from before any of this shipped stays exactly what it always was, a completion, not a completion with an invented timestamp.

Building for groups and classes

The group and reporting side is the part I was most paranoid about, because that’s where somebody other than the learner gets to look at other people’s progress and results. An administrator or educator in a family or class group can assign a topic or a unit to specific members.

Every target starts pending. Nothing is visible, not a completed count, not a “started” flag, nothing, until the learner themselves grants it, and they can revoke that later whenever they like.

pendinglearner decidesgrantedcompleted / required countdeclined / revokedPrivate
Pending is the only state that exists until the learner acts. The manager's query is written so it literally cannot see anything else.

The manager’s query joins the learner’s progress row only where the assignment’s visibility says granted. Not “hide the field in the UI if they said no”, the query itself can’t pull that row any other way. And a learner who’s kept their answer private is never quietly counted as “incomplete” either, sharing and completion are two separate tallies on purpose. Change roles, leave the group, get archived, any of that and the visibility grant and the assignment targets get deleted with it. Progress itself is untouched, obviously, it’s theirs. Archived assignments and their targets purge for good after 30 days on a private worker that runs daily.

None of this is a finished safeguarding review, It’s evidence of least-purpose design that can hold up under a change of role, a departure, an archive, a deletion. It’s not the same as having proven the whole system is fit for a classroom full of ten-year-olds, but we’re working towards a more complete app.

-x