← Back

Case study 01 · running daily · private

Cockpit

A scheduler built on one rule: nothing you committed to is allowed to disappear without the system saying so. It plans my day against my real calendar, refuses to write a day that cannot physically happen, and states the cost of anything it has to move before it moves it. It runs on my own machine and has been firing at 7 AM, with no session open, for weeks.

1,407automated tests
48python modules
15simulation suites
128logged decisions

In short

The test run, the phase table and one decision in full →

The Cockpit on a phone: today's plan, the strategic read, and a warning that three units are behind with a request to decide rather than slide them
The morning of 2026-08-06, unedited. The read is refusing to move three units quietly and asking for a decision instead.
01

The need

Every scheduling tool I tried would let something vanish without saying so. A study block slides one day, then another. A workout gets dropped to make room for the thing that felt urgent. A deadline you set for yourself moves back, and then back again, until the goal is quietly dead and no moment ever announced it. None of that is a notification anywhere, and by the time it is visible the week is gone.

The tools were not failing at arithmetic. They were failing at disclosure. Any of them would happily accept a day that was not physically possible, and none of them would tell me afterwards that I had not done it.

Nothing you committed to should be able to disappear without the system saying so.

That sentence is the whole design, and every rule below is one instance of it. It is also the part that generalizes past me: the failure it prevents is a plan drifting away from reality faster than anyone notices, which is not a personal weakness but a property of any schedule that is allowed to change itself silently.

02

What the rule actually means

The rule is only worth anything if it is specific about what may be spent and what may not. It resolves into a ladder, applied gently first.

Never touchedClass and sleep. The system will refuse a request before it will move either of them.
Shifts within the dayWorkout, meals, the standing daily template. They move, but losing one entirely is a stated cost, never a free yes.
Relocates to another dayStudy blocks, which is where most of the real reflow happens.
Questioned lastThe deadline. A school deadline is a hard wall including its late penalty. One I set for myself can slide, but only at a cost said out loud, because a self-imposed goal endlessly pushed is a goal quietly dying.

Asking whether something fits returns one of four answers, and the second is the one that matters: yes, yes but here is what it costs, not this slot but here is one that works, or not now. Collapsing the second into the first is exactly how protected time gets spent silently, which is the failure the whole system exists to prevent.

Two guardrails sit on top. The two days immediately before a hard deadline are protected outright, and the three or four before it surface a cost, because a schedule that is under its daily ceiling every day can still be a bad week. And a missed unit is reported rather than slid forward: routine slips text me the fix so I can act without opening anything, while a slip that no longer fits before the deadline demands a session and names the actual choice, which is move the deadline, cut the scope, or cram. A three-way tradeoff cannot resolve over a one-way text.

03

How it works

The shape that matters is the split between what computes and what writes.

Deterministic Python decides everything. Dates, ordering, capacity, which block moves when a class runs long, whether a deadline is now unreachable. All of it is ordinary code with tests. A local language model only does the wording, and only over facts that have already been computed, and every one of those calls has a complete template fallback. If the model is down, the message is plainer. It is never wrong.

A decision that lives in prose is a decision you cannot test.

That rule came out of getting it backwards first. It is tempting to hand the whole problem to a model, because the output looks good immediately. The trouble is that you can no longer answer simple questions about your own system, like why it moved a study block, or whether it will do the same thing tomorrow.

EnginePython, on my own machine. 48 modules, 1,407 tests.
Language modelAn open model served locally by Ollama. Wording only, never decisions, always with a fallback.
Control panelFastAPI and React. The one place a change to my calendar gets approved.
CalendarGoogle Calendar. It writes only events it owns and never touches ones I made by hand.
NotificationsA Telegram bot, fired by a scheduled job at 7 AM.
Knowledge base361 markdown pages, every claim carrying the source it came from.
04

How it is built

The system is built in numbered phases. Each phase has an acceptance criterion written as one sentence before any code exists, and the phase is not done until that exact sentence is true. Sixty-one of them have closed that way, and one is marked superseded rather than reworded to pass.

Before a new component takes over a live system, it runs against a batch of written scenarios with the expected outcome recorded first. If a run disagrees with an expectation, the default assumption is that the code is wrong, not the expectation. Those batches do not get deleted afterwards; there are fifteen of them and they are the regression suite.

Every architectural decision that was hard to reverse gets written down with its reasoning and the alternatives that were rejected. There are 128. Separately, there is a file of corrections I have been given, which I read before starting anything non-trivial.

05

What went wrong

Failure 01

It broke my actual calendar

On June 19 the scheduler wiped my daily template on any day that had studying on it, and pushed future study sessions as all-day banners instead of timed blocks. My real calendar, the one I use.

The instinct was to go fix the surface that did it. That was wrong. The scheduler had grown five overlapping entry points across six phases, and the actual cause was underneath all of them: two different calendar-write models were coexisting. An older design that wrote future work as all-day entries had never been removed when a newer design replaced it, and the daily path was still running the old one.

The fix was not a patch. It was finishing the migration that had been left half done, deleting the old path entirely, and reducing five surfaces to one write rule. The lesson I actually took: when a bug appears in five places, stop looking at the five places.

Failure 02

I built on a channel I never verified

The whole point of the morning run is that it fires when I am asleep and no session is open. I designed the notification step around the obvious path, and only when it came time to run headless did I find out that path cannot fire without a session at all. The feature was not late. It was impossible.

I split the phase in half, shipped the part that worked, and re-opened the channel question from scratch. The answer turned out to be a Telegram bot, which took an afternoon. Verify the thing at the far end of the pipe before you build the pipe.

Failure 03

A phase became impossible to finish, and I nearly hid it

One phase's acceptance depended on a hook that a later architectural decision removed. Its acceptance sentence could never be true again. The tempting move was to quietly reword the criterion so it passed.

Instead it is marked superseded, with a note saying which phase absorbed the remaining work and why. It is the only phase in the list that is not done, and it stays visible. An acceptance test you are allowed to edit after the fact is not a test.

Failure 04

My own audit was crying wolf

The tool that checks whether claims in the knowledge base carry citations was reporting eight problems on a single page that were not problems. It was tracking which bullet a citation belonged to with a single variable, which breaks the moment a list nests and then comes back out.

Replacing that with a stack fixed it. The reason it mattered is that a checker with false positives gets ignored, and a checker that gets ignored is worse than no checker, because you think you are covered.

06

What I would do differently

The system is private, because it holds my calendar, my grades and my notes. I am happy to walk through the architecture, the test suite, or the failure write-ups with anyone who wants to see them. Email me.