Butineur: turning my chores into a hobby budget
I love spending money on my hobbies. 3D printing, electronics, DIY: there is always one more spool, one more dev board, one more tool I could absolutely justify buying. So at some point I thought: the best way to motivate myself is to limit my spending. And as a bonus, I'd actually save some money.
That's how Butineur started. It's an Android app where every chore you complete credits a hobby budget. Not real money, just an envelope I allow myself to spend afterwards. Doing the dishes funds a filament order. Vacuuming on Tuesday pays for the ESP32 I buy on Saturday.
The point isn't to earn. It's that spending now has a price tag, and the price is paid in chores.

A quick note on the screenshots: they're all in French, because the translation isn't implemented yet. English is coming.
The idea in one sentence
You set the amount for each task yourself. Dishes €2, tax return €40, ten minutes of exercise €1.50. On the other side, a shop with your hobbies and their price. The balance goes up when you do things, and down when you spend.
It's dumb, and that's exactly why it works: the reward is immediate, it has a number on it, and I'm the one who set the rates.
What it does
Rhythms. A task can be one-off, or come back: every X days, every Tuesday, on the 5th of the month. The rhythm is inferred from what you fill in, and there's only one menu on screen, so you can't create a task that repeats "every 3 days on Tuesday the 12th".
Counters. "Drink 8 glasses of water a day", "read for 30 minutes". You tap +1, and you can set reward tiers: 4 glasses = €1, 8 glasses = €3 more.
Deadlines, with the penalty of your choice. None, a flat amount, a percentage, or one that decays per day late. A late task pays less, but never anything negative. The point is to motivate, not to punish.
Streaks. Tiers ("7 days in a row = +20") and a capped multiplier, which stack. A missed cycle doesn't break the streak: it freezes it. The streak is kept but stops growing. The next cycle thaws it, or breaks it if that one is missed too. One bad week shouldn't wipe out two months of effort.
And the editor simulates the result while you turn the knobs, because nobody can guess what "+5% per step, capped at ×1.5" adds up to without seeing it. The simulation strip shows the next ten completions, and the line underneath tells you the total.

Home screen widgets. Three of them: the balance, a configurable counter, and the list of tasks you can tick off, all without opening the app. That's the part I use the most.
The shop and the history. Your hobbies with their price, buyable in one tap, plus a free-form amount for anything not in the list. And a log that says where every euro came from: the base reward, the tier, the streak bonus.


The rest, in bulk: per-task reminders (fixed time, on the due date, or X minutes before), quick tasks kept aside for things that come back without being regular, a stats tab with a GitHub-contributions-style consistency graph, 186 duotone icons tinted with your accent colour, backup and restore, and small animations on every gain, disabled if the system asks for reduced motion.

How it's built
One web codebase (React + Vite) that serves both as an Android app through Capacitor and as a site you can open in a browser. The only native code is the widgets, in Kotlin, because Android can't render web content on the home screen.
The one structural decision fits in a line: the balance is never stored. It is recomputed on every load by replaying an append-only event log. An event only ever carries local facts: which task, when, and the reward frozen at the moment of the tap. Never a total.
That costs a bit of computation, and it buys two things:
- two devices holding the same events necessarily display the same balance, without a single line of conflict resolution;
- a widget becomes a third device that only knows how to append facts. Tapping "+1" with the app closed queues a fact; the app writes it to the log next time it opens, with the timestamp of the tap, so there's no unfair late penalty just because I opened the app the day after.
A corollary: nothing is ever erased. Undoing a completion means appending an event that says "that one doesn't count". It's idempotent, it merges cleanly, and the history stays honest.
No background service either: the widgets read the SharedPreferences file
Capacitor already writes. No battery burnt to display a number.
About 5,500 lines of TypeScript, 700 of which are the calculation engine and as many again its tests. That's the only part I really test, since it's the money path.
How Claude and I work together
I write the code with Claude Code. What changed things wasn't the model: it was giving up on steering from the chat.
My todos used to live in a markdown file in the repo. It was fine for listing, useless for tracking: a ticked line doesn't tell you whether something is coded, shipped or tested, a comment about one specific point gets lost in the pile, and nothing distinguishes "to do" from "waiting on my answer". Everything else ended up in the chat, and the chat forgets between sessions.
Now it all goes through a self-hosted Vikunja kanban. Vikunja because it's
the only one I tried that is both pleasant on a phone and cleanly scriptable.
Claude talks to it with curl and a token, exactly the way it uses gh for
releases. No integration to write.
The board has six columns, and each one belongs to someone:
| Column | Who fills it |
|---|---|
| Claude | me: what I want done, or my answers to its questions |
| Doing | it, when it picks a card up |
| To-Do | it: what's left for me to test |
| waiting | it: questions it needs me to answer |
| future | postponed, no date |
| Done | it, once shipped and pushed |
The loop is always the same. I drop a card in Claude, and it reads that column first thing in a session, because that's its inbox. It moves the card to Doing, writes the code, tests on the plugged-in phone, pushes. And when it ships, it creates a test card in To-Do, linked as a subtask of the original: I can see where the test came from, and its description tells me where to look, not just what to check. Its own card moves to Done.
When it has a question, a rule to arbitrate or an interface choice, the card goes to waiting, with the full question written in the description. I answer in a comment from my phone, then drag the card into Claude. That drag is the signal: without it, it has no way of knowing a comment is new, since it keeps no memory between sessions.
What this gives me, concretely: at any moment I know where everything stands. What's shipped, what's left for me to test, what's waiting on a decision from me. My feedback is attached to the card it belongs to, and the next session starts from it without me having to explain it again. The chat is for working, not for remembering.
Not open source yet
The question is coming, so let me answer it now: the code isn't public yet. I'm waiting until I have a stable base. Publishing a project that still moves every day means committing to explaining choices you're in the middle of changing. It will come, and the repo is already written to be read, README and comments included.
What's next
Phone ↔ PC sync. That's the big one, and the event log was designed for it from day one: an optional self-hosted server, events going up and down, and a merge that takes care of itself because there's nothing to reconcile. Deletions are already soft everywhere in preparation.
English. As you saw above, the interface is French only for now. A translation is planned, and English will become the default.
The small stuff that keeps piling up, the kind that only comes from actual use: editing a quick task by long-pressing it, marking frozen streaks and reached tiers on the consistency graph, making that graph start at the beginning of the week.
Then, once all that has settled: the repo.
In the meantime it's been running on my phone for weeks, and for the first time I have a todo app I still open. My filament budget has never been so well earned.