I have a calendar that says I read for thirty minutes every morning. I have, on my actual mornings, read for thirty minutes maybe four times this year. The calendar and the morning have been lying to each other for months. I built a small thing to make one of them stop.
01The problem.
The calendar is the kind of artefact that looks like intention but works like absolution. You schedule the thing, you feel like the kind of person who would do the thing, you mark the thing as done if you're feeling kind to yourself, you move on. The whole loop happens in the interface, never in the world. After a while, the calendar is not a plan — it's a wall of aspirations I've politely tipped my hat to.
02The rule.
So I wrote down one rule. Just one: any event tagged #honest must be verifiable by
something that is not me clicking "done." A reading block has to leave a trace — a
highlight, a note, a page count. A workout has to show up in the watch's log. A "deep work"
block has to produce a commit, a paragraph, a sketch, something. If there's no trace,
the event isn't completed when I close the laptop — it's marked unverified, and
it stays that way.
03The loop.
The actual code is small and embarrassing. It runs every fifteen minutes. It looks at events
tagged #honest whose end time is in the past. For each one, it asks the relevant
source — Readwise, Strava, the local git log, the notes folder — whether anything new appeared
in that event's window. If yes, the event is stamped verified. If no, it gets a
✕ in the title and a one-line reason.
# the honest part, minus credentials for ev in calendar.events(tag="#honest", ended=True): src = ev.intent.source # readwise / strava / git / notes trace = src.window(ev.start, ev.end) if trace.empty: ev.title = "✕ " + ev.title ev.note = f"no trace from {src.name} in window" else: ev.title = "✓ " + ev.title ev.note = trace.summary(max_lines=2) ev.save()
That's it. There's no model in the loop. There's no chat. The "agent" part is mostly that it decides, on its own, which source to ask, based on the event's tags. I could have written this as a cron job in 2014. The novelty isn't the technology. The novelty is the posture: a thing in my system whose job is to politely disagree with me.
04What changed.
Within a week, two things happened. First — embarrassingly — the wall of ✕ marks on Friday afternoon became unbearable, and I started actually doing the things, or admitting I wasn't going to. Second, more interestingly: I started noticing which intentions had no plausible verifier. "Think about the project" has no trace. "Be a better friend" has no trace. These aren't bad intentions, but they aren't calendar events either. They got moved out of the schedule and into a notebook, where they belong.
The calendar shrank. The week got more honest. I read more.
05What's next.
- A weekly digest. Plain text. How many
#honestevents I declared, how many got a ✓, where the gaps were. No graphs. No streaks. Streaks turn the system into a game and I don't want a game. - A "soft" mode for things that should be encouraged but not enforced — drawing, music, walks. Verified the same way, but the ✕ stays small.
- An honest version of the thing I'm afraid of: a second agent that does the same audit on what I say I'm working on, by reading my commits and notes for the week. I haven't started this one. I'm not sure I want to know yet.
The whole project is maybe a hundred lines, weekends only, and I think it will outlive most of the bigger things I've built this year. The best tools, in my limited experience, are the ones that take something you were doing badly on purpose and make doing it badly slightly more embarrassing than doing it well.