THE RESEARCH BEHIND KERFOX
Why we built it this way.
We have no evidence that Kerfox works. Nobody has run a study on it, no class has been taught with it and measured against a class that was not, and until somebody does, any number we put on this page would be one we made up.
What we do have is this. Every design decision in the app was taken from a published finding, and we can show you which one. Below, each method is named, the research behind it is cited and linked, and we say exactly where in the app you run into it, so you can go and look rather than take our word for it.
That is a claim about our methods rather than about your marks. It is the only honest claim available to a study app that nobody has studied yet, and we would rather make it well than make a bigger one badly.
- 33papers read, linked and summarised
- 9methods, each traced back to them
- 42checks that fail the build rather than warn
- 0studies of our own
The last one is the number that decides how the rest of this page is written.
WHAT THIS PAGE IS NOT
It is not an efficacy page. The findings below were established by other people, in their own studies, on their own materials. That a technique works in a published experiment is not evidence that our implementation of it works, and we are careful throughout to keep those two things apart.
It is also not a sales brochure with footnotes. Where a finding is contested, or where its scope is narrower than the headline suggests, we say so in the same paragraph rather than quietly rounding it up. Three of the methods below carry a sentence that cuts against them.
The 42 checks a build has to pass before it is allowed out, and the specific failure each one exists to prevent.
How the content is checked.
Everything above is a claim about our intentions. This part is a claim about the code, and it is the one we would point a sceptical teacher at first, because it is checkable and because almost nobody does it.
A curriculum this size is drafted fast, and drafting fast is exactly how a confident lesson ends up carrying a wrong constant, a question whose answer does not follow from its own numbers, or a figure whose caption contradicts the drawing. Reading it back does not catch those reliably, because all three read perfectly well. So the content does not get to ship on anybody trusting it. Before any build is allowed out, 42 separate checks run over the whole curriculum, and every one of them fails the build rather than printing a warning. Eleven of them are below, and they are deliberately not the obvious eleven: the interesting ones are the checks whose absence would have been invisible.
How an answer is marked
The marker decides real marks, so nothing about it is asserted - it is run, and then it is broken on purpose to check the marks fall.
A wrong answer is proved unable to equal the right one.
Most of our questions generate their own numbers, which makes each one a small program that breaks in ways reading it will not reveal: a wrong option that happens to equal the correct answer for every value it can draw, a placeholder that never gets substituted, a range whose every draw gives the same answer - which quietly restores the memorising the generator was added to prevent. So every generator is run four hundred times and the results inspected. It also fails the build if any level and subject a student could actually pick has nothing to serve, because a thin pool used to become a wrong-level one silently and now becomes an empty screen.
check-questions.mjs
The marker is made to mark our own answers, and then made to fail.
Where a question asks for a derivation, the app marks it by testing whether each line is TRUE rather than by comparing it to a string, so any correct rearrangement scores. That engine awards real marks, so its accuracy is measured rather than asserted: it is run against model derivations from the bank and must give them full marks, then those same derivations are broken on purpose and the marks must fall. A separate computer algebra pass has to agree with it, and the build fails when the two disagree - which is the only reason to trust either.
check-derivations.mjs
A typed answer cannot accept the thing it was supposed to refuse.
Where a question is typed rather than picked from options, it carries a list of spellings to accept - and the dangerous failure is an entry on that list which is also one of the wrong options, because it marks a wrong answer right and looks like nothing. That collision is checked, along with the correct answer actually being on the list. The subtler half is that serving the same question typed and picked has to produce different timing bands and different grade caps, since typing it is harder; getting that wrong is completely silent, which is why it is asserted rather than trusted.
check-typed.mjs
What the app says to you
The two places Kerfox speaks unprompted are the two places it could be cruel or flattering by accident, and neither is visible by reading the code.
Nothing the app says after an exam passes a verdict on it.
There are five moments in a season where Kerfox says something unprompted, and one of them is the evening after a paper. The student knows how it went and the app does not. So every line that can fire after an exam has to be true whichever way it went: "that is all of them" is a fact, "you smashed it" is a guess and a cruel one half the time. That is checked as a property of the copy rather than left to whoever writes the next line, because the failure only shows up in front of the one student it hurts.
check-moments.mjs
A near miss has to be one the marker actually made.
Telling someone they were one mark off is worth showing because it is TRUE, and worth nothing the moment one is staged - and a staged one is invisible: it renders exactly like an honest one, it flatters, and nothing on screen or in a type check can tell them apart. So the two prohibitions are checked. Every fraction shown must be a count the marker already produced, and a near miss may never be sent as a notification, which is checked as a fact about which files are allowed to reach which. The fixtures go through the real marker rather than a hand-built result, because a rule asserted against a fake one passes happily while the wiring underneath is broken.
check-nearmiss.mjs
What the engine may do
Three properties the estimator has to keep, each of which would fail invisibly - a number quietly rising, an estimate going deaf, a technique switched off by accident.
A number allowed to fall is proved unable to rise on its own.
One figure on the home screen is allowed to go down, and its entire value rests on a claim nothing on the screen can demonstrate: that it cannot climb while the student does nothing. The obvious definition of it fails exactly that, and fails it invisibly, because a number quietly rising as an exam approaches looks like a working app and reads like progress. So the property is asserted directly against the code rather than trusted. Tuning the constant stays free; breaking the rule does not.
check-secured.mjs
The best-evidenced thing here is proved to be switched on.
Interleaving - mixing the confusable things rather than doing twenty of the same - was switched off by accident, in the most ordinary way imaginable: the code that alternates topics works perfectly, and it was being handed sessions containing ONE topic, so it had nothing to alternate with. Nothing was broken and nothing looked wrong. The mechanism is a student deciding which problem this is, and that decision only exists when the confusable alternative is in the room, so the fix was scope rather than order. A session now has to contain at least two topics of a confusable cluster whenever its module has one, and a ratchet stops the coverage that makes that possible from ever falling.
check-interleaving.mjs
A student who comes back after months must be able to surprise us.
The engine holds a belief about what you can do and a confidence in that belief, and the second one is what stops it going deaf. An earlier version decayed its learning rate on how many answers a topic had EVER had, so a topic drilled in March was barely movable in September - exactly when the estimate was least trustworthy and the student most changed. The schedule only ever tightened. Three probes hold the fix: a synthetic student with a known ability is recovered to a promised accuracy in a promised number of questions, a student left for ninety days comes back with their confidence widened and their first week moving faster than before the break, and item difficulties never drift while any of that happens.
check-uncertainty.mjs
What is in the content
Reference data nobody can proofread, scope nobody can eyeball, and declarations that drift away from the thing they describe.
The chemistry drill is checked against the course, not against its wording.
One drill teaches where elements sit, scoped to the ones a course actually uses, and that scope is written by hand - so it is held against the course's own topics rather than against itself. The direction matters: an element the course TEACHES and the scope omits is the real failure, because the student drills everything except the thing they will be asked about and nothing on screen looks wrong. It reads compound names as well as element names, and that is the whole trick. "Dichromate" appears three times in A-Level Chemistry and "chromium" never once, so a check reading names alone would bless a scope with no chromium in it - the element in the commonest oxidising agent at that level.
check-scope.mjs
The periodic table is checked against physics rather than proofread.
The element table is the spine of every chemistry feature, and the risk in it is not design but TRANSCRIPTION: a hundred and eighteen rows, two masses each, every one of them invisible on the page and wrong in every calculation that follows. Nobody proofreads a number they cannot see. So the numbers are checked against each other and against physics: the two masses must agree to within the rounding the boards print, and a mass must track its atomic number, because above hydrogen a nucleus is never lighter than its proton count and rarely much more than two and a half times it. A transposed digit falls out of that band immediately, while either value alone looks perfectly plausible.
check-elements.mjs
A play button never appears on a drawing that cannot move.
The lesson player offers a replay control by looking a figure up in a list of the still ones, and being wrong in either direction shows the reader a broken control: a play button on a diagram that does nothing, or no way to replay one that does. The list is declared rather than measured at runtime, because sampling a figure while the tab is hidden sees a stopped animation and brands a moving figure static for the whole session. Declaring it moves the problem to drift, and drift is what this catches - by reading the figure's own source and asking whether it LOOPS, which is not the same question as whether it moves at all.
check-figures.mjs
And 31 more.
Every stated constant compared against its real value. Every colour pair measured rather than eyeballed, in both themes, including against each of the subject washes. No equation able to run off the side of a phone. Every lesson held to the same contract, every figure registered, exam-window placement, reminder scheduling, share cards, design tokens.
Those are the ones you would have assumed, which is why they are a sentence rather than eleven more cards. They fail the build exactly as hard.
None of this makes the content good. A gate can prove that a stated value of the Planck constant is right and cannot tell you whether the lesson around it explains anything. What it does is remove a whole class of error from the argument, so that when we are wrong we are wrong about teaching rather than about arithmetic.