ACADEMY

The best thing I've built, for the smallest audience there is.

A learning management system with exactly one student in it. It has run every school day since 19 August 2026 — eight subjects, planned a week at a time. It's on its second version now. The first one got the year started, and a school year doesn't wait for the good version.

I homeschool my son. Every system I looked at was built for a district — enrollment, sections, staff roles, state reporting — and none of that applies to a house with one student in it. One family's records sit in one database with nothing else in it.

  • RUNNING SINCE 19 AUGUST 2026
  • EIGHT SUBJECTS
  • ONE STUDENT
The Plan the Week screen in Flokus Academy: eight classes listed down the left side, Monday to Friday across the top, and a typed entry with a duration in most cells. Each day heading carries a count of items and their total minutes.
Plan the Week — the admin home screen. Classes down the side, Monday to Friday across the top, and every cell in it typed by hand. The tally under each day heading is what that column adds up to.

WHAT IT IS

One screen to plan from, and a day he can see.

I plan a week at a time, by hand. Classes down the side, Monday to Friday across the top; click a cell and type what he does for that class that day. Nothing is imported, nothing is scheduled automatically, and nothing moves work I've already placed. That's the entire authoring model, and it's the part I'm proudest of — because it's what was left after I deleted everything built to make it easier.

His side is a list of what's due, an estimate of how long it will take, a box to say what he learned, and XP for finishing. Behind both there's a school calendar, a portfolio of completed work, tracking for the things he builds himself, and the household's school spending.

NOTHING MOVES WORK
Adding a day off used to trigger a reschedule across the whole year. Now it reports what falls on that day and leaves it alone. A date I typed is a decision, and an app that quietly revises it is one I can't plan in. There's no recalculate endpoint left, and a test asserts its absence, so putting one back has to be a conscious act.
FINISHED WORK IS HARD TO LOSE
Deleting a lesson used to take its completed assignments with it, in one click, without asking. It now refuses, and names the lesson. The route from finished to gone runs back through uncompleting the work, which is where the XP reversal already lived.
CLASSES HIDE, THEY DON'T DELETE
A class we've stopped teaching still owns finished work the year's record needs. Hiding one takes it off the grid and tells you so: its finished work still counts. It's one click from coming back.

PYTHON · FASTAPI · SQLALCHEMY · POSTGRESQL · ALEMBIC · VUE 3 · PLAYWRIGHT · RAILWAY

HOW IT GOT HERE

Shipped rough, rebuilt properly, then made smaller.

The first version was a Streamlit app on a SQLite file, and it existed because school started on 19 August whether or not I had anything good. It planned the whole Grade 5 year: 817 assignments across eight subjects, 38 instructional weeks, 184 school days.

Most of that work wasn't features. It was reading the calendar as a whole instead of a week at a time — which is when two things surfaced. The year had no breaks in it: 180 straight weekdays, seventy assignments landing on holidays, three of them on Christmas Day. And every math lesson sat in the first fifteen weeks, so after 26 November there were twenty-one weeks with no daily math at all. Neither is a bug you hit by using the app. You'd have found the first one on Christmas morning and the second some time in February. Seventeen automated checks came out of that, each one a defect that had actually happened.

What that version couldn't do was everything else: single user by construction, a PIN and nothing more, running nowhere but the machine in the schoolroom. So I built the second one properly — a real API, a real database, migrations, tests. It's what happened after that I'd rather you read.

I gave it a curriculum importer, unit staging and release, and a rolling scheduler. All three were built, deployed, and then deleted on 26 and 27 August. Not because they didn't work. Because four or five items a day for one student is about fifteen lines of typing on a Sunday, and every mechanism built to avoid that typing cost more attention to operate than the typing it replaced. Volume justifies machinery. Fifteen lines a week does not.

That deletion is why the two versions couldn't share data. The old year was laid down by an importer the new one deliberately no longer has, so carrying it across would have meant rebuilding the importer in order to retire it. The new version started clean this week; the old one keeps the weeks it carried.

The lesson took three redirects to learn, and it's the one I'd take anywhere: ask what the household actually does before building the thing that would help if it did something else. There's no third version yet, and I'd rather say that than sketch a roadmap I haven't earned. It starts when I can name what's still wrong.

ASK FLOKI

The feature I built and didn't turn on.

There's an AI tutor on the student side. It's finished — transcripts, a separate channel for safety events, consent, a retention policy — and it ships behind a flag that defaults to off.

Google's additional terms for the Gemini API say a client must not be directed towards, or likely to be accessed by, individuals under the age of 18. The unpaid tier also uses submitted prompts for training, with human review. My only user is nine years old.

So it's off. Not removed — off, behind one variable, with everything underneath it intact. It comes back when there's a paid key, when a safety alert can reach me somewhere other than a screen I have to remember to open, and when I have an honest answer to the under-18 clause rather than a convenient one.

KEEPING IT RUNNING

The unglamorous half.

Building it was the easier part. This is the half that decides whether something a nine-year-old depends on every morning is actually trustworthy, and it's where most of the last week went.

BACKED UP, AND RESTORED
A dump taken off the platform and verified inside the archive before anything is pruned — the file parses, the tables are present, and the row counts in it match production. Then restored into a second database and counted again. A backup nobody has restored is a hope.
SEVENTY TESTS, AND A MONITOR
Seventy end-to-end tests run on every push to main, and an uptime check hits the health endpoint every five minutes. Neither is impressive alone. Together they're the difference between hearing about an outage from a monitor and hearing about it from my son.
THE MIGRATION CHAIN IS BUILT FROM EMPTY
Every schema change is a migration, and CI rebuilds the chain from nothing on each run rather than applying the newest one to a database that's already right. Incremental runs hide the bugs a clean run catches.
WHAT ISN'T DONE
The portfolio export is broken, and it's the screen that produces the year's record — the field names drifted and the export button has no handler behind it. The admin side has no phone layout at all; a tablet is fine, a phone is not. And the backup is a manual Sunday step rather than a scheduled job. All three are written down. None of them are finished.

IF YOU WANT ONE

This is built for one family right now.

Academy runs for exactly one household — mine. If you'd want it for yours, leave an email and I'll tell you when it's ready for other people. That's the only thing I'll ever use it for.

No list, no newsletter, no sharing. One email, when there's news.