$cat ~/posts/check-in-binario

bilingual · en / pt

← /blog

binary check-in


The Plan already has a post here, with the why, the deadline and the hour-cost. This one is about the tool: the Android app I built to track that every day, why it is the way it is, and an idea left over at the end, when I was already inside Kotlin and didn't want to leave.

why a spreadsheet doesn't hold this

$ quote ~/posts/o-plano

I didn't build this to show anyone. I built it because a spreadsheet doesn't hold that kind of discipline, and I needed something that would stare back at me every day with the real number.

The Plan2026-09-09en

The problem with a spreadsheet isn't that it's too simple. It's that it's too obedient. It accepts any number I type into it, including the ones I type in a bad mood at eleven at night to close the week somewhere respectable. I had done that before and knew exactly how it ends: the spreadsheet stays beautiful and the studying didn't happen.

What I needed was something that wouldn't negotiate. 492 hours until June 30, 2027, spread across 32 credentials. That number isn't motivational, it's arithmetic, and the app's only job is to keep it visible every day without letting me touch it.

I studied or I didn't

The check-in is binary. I studied on Coursera today, or I didn't. No timer, no duration field, no notes, no partial progress.

That was the first decision and it holds up all the rest. The minute a duration field exists, it becomes a negotiation: forty distracted minutes of video at 1.75x become "an hour," and an hour like that disappears into three hundred others. Binary has nothing to negotiate. Either the day happened or it didn't.

The second decision was the window: I can mark and unmark the current day and the six before it, and that's all. Seven days is roughly how far back I can remember honestly. Past that it becomes reconstruction, and reconstructed memory is always generous to whoever is reconstructing.

the Wall

The screen I use most is called the Wall, and it answers one question: am I behind?

It shows how many hours are left, the theoretical pace to finish within the deadline, a traffic light, and a staircase burn-down. The math itself is nothing clever:

val restantes = totalPlanejado - concluidas - cortadas
val semanas = ChronoUnit.WEEKS.between(hoje, FIM)
val ritmo = restantes / max(1, semanas)

What it has going for it is that I can't edit it. And the chart is a staircase, not a ramp, because real progress is discrete: the Wall's hours only drop when a course is completed. A check-in drops nothing. Thirty days in a row with zero courses closed shows up as exactly what it is, one long horizontal line, and it's uncomfortable in the right way.

There's a valve, and it's declared: grupo_corte, in the database. I can cut a planned item and its hours leave the Wall. That isn't cheating, it's the same decision I already made by hand when boot.dev left the main track because of the deadline. The difference is that now a cut stays on record as a cut, instead of becoming a goal that quietly shrank.

what's underneath

Kotlin, Jetpack Compose, Material 3, Navigation 3. Room underneath, Hilt for injection, StateFlow for state, WorkManager for the reminder. minSdk 26, targetSdk 36, package dev.nuxyel.oplano, MIT, code on GitHub.

The database, o_plano.db, has seven tables: trilha, ordem, grupo_corte, item, selo, checkin and config. The modeling mirrors The Plan's structure instead of inventing some generic habit abstraction, and that was deliberate: the app is good for nothing else, it doesn't need to pretend otherwise.

The only part that gave me real trouble was the Room migration preserving the v1.0 data. It's a personal app, with one user, and even so I couldn't drop the table and start over, because the history is the product. The value in there isn't the code, it's the row of check-ins since July. Losing that to reorganize a schema would be losing the only thing the app has.

There are four screens: Today (the current item, the seven editable days, the streak), the Wall, Tracks (with cut and restore) and Credentials, which is a timeline of the certificates. Plus the item detail, which is where the boring part lives: planned load, dates, state, completion.

what I didn't build

No login, no backend, no telemetry, no sync, no network access. No export, no import, Android's automatic backup switched off.

The data exists only on the current installation. If I lose the phone, I lose the whole history, and I decided to accept that consciously instead of solving it with a Firebase that would cost me an afternoon and a permanent attachment. The app is for me, it runs on my phone, and its border is the glass.

the idea it left behind

Once the app was done, something I didn't expect was left over: wanting to stay in Kotlin.

It isn't about Android. It's that I hadn't written in a typed, compiled, boring-in-a-good-way language for a while, after months of Python, and that stirred up a question now parked in my head as a possible thesis: can you do machine learning and computer vision in native Kotlin, with no external library? No TFLite, no ML Kit, no OpenCV, nothing calling into C++ underneath. Convolution written by hand, feature extraction written by hand, all on the JVM.

I haven't researched whether that's viable. Not at all. I don't know if it's a thesis or an afternoon of frustration with a benchmark thirty times slower than the baseline and no interesting conclusion at the end. I don't know if the literature already exists, I don't know where the JVM breaks on real matrix work, I don't know if the right framing is implement it from scratch or run it on-device without depending on a third party. Those are different questions and they make different theses.

What I do know is why the idea pulls at me, and it's the same reason as always: it's fundamentals, not tooling. Calling model.predict() doesn't teach me what convolution does. Writing the convolution does. If the answer at the end is "it's unviable and here's why," that's a result too, and it's a result I would understand end to end.

It stays on record as what it is: a raw idea, viability unchecked, written down the day it showed up. In a few months I'll come back here and see whether it survived contact with reality.