The build log · newest first
A month of building a task list
to replace Todoist
Every entry comes from the repo's history: commits, merged pull requests and the decision log, where each choice is written down with what it rules out.
The rules it is built by
00- No AI in the appQuick Add, dates and repeats are plain code. The one model in the system lives in a sidecar, runs once per request, and can be removed without changing the app.
- Every write is recordedAll changes go through one service layer, inside a transaction, with an audit row. Nothing is hard-deleted.
- Decisions are dataWhere a model decides something, it decides once and returns data a person can read and fix; plain code does every repeat.
- Secrets stay secretNo password on a command line or in output. Keys live in the system vault or as owner-only files, never in the repo.
- Proof before trustA backup counts once a restore has been shown to work. A deploy counts once the app answers as the new version.
Entries
10-2026-
The focus board, and three milestones
- Designed the focus board: projects grouped under five headers, progress rings from GitHub milestones, and a "Hand to Claude" button. The look went through several mock-ups, each answering the owner's notes, to "ember ledger".
- Set up the headers in the live list, so the board has real groups to show.
- Wrote the worth record and gave the repo three milestones: two weeks of daily use, someone else using it for a week, and later.
issue #27 · WORTH.md
-
One home for every project
- Ran the worth gate for making this app the place every project and idea lives: 2.9, GO, on one condition: prove the backups restore first.
- Built a restore test that brings the newest nightly dump back in a throwaway copy with no network, and compares counts with the live app, which it only reads. It passed.
- Gave projects six small facts (state, summary, next, site link, archive reason, public) and a read-only feed that lists only the public ones, field by field, so a new field cannot leak by accident.
- Loaded every project from the plan and the GitHub catalogue in one run that can be repeated without making duplicates, and added a private search.
- Ideas reach the website only when tagged
public. - Made the deploy stop on a failed build, and check that the app answers as the new commit.
issue #21 · pull requests #22 to #26
-
"Claude, remind me when..."
- A task tagged
@claudeis picked up by a watcher sidecar. Claude runs once and writes a small watch rule into the task; plain code checks it hourly and pushes to the phone when it comes true. - Rules only fetch public web pages, refuse redirects, and are readable and editable in the task itself.
pull request #20
- A task tagged
-
Pairing with a six-digit code
- A new device types the code from an authenticator app and gets its own key, once. Before this, adding a device meant opening a server port and copying a key by hand.
- Wrong guesses lock pairing for a few minutes, and each key can be revoked on its own.
- Wrote the owner and user guide: first-run setup, sign-in, invites and login recovery.
-
The phone widget
- A home-screen widget shows today's tasks through one narrow, read-only door, so the phone no longer holds a key that can change everything.
- On 10-14-2026 done items gained their labels, so the habits widget can tick off what was done today.
- Three fixes to the deploy script on the way.
-
Phone app and sharing
- An installable phone app with a bottom bar, card rows, an Upcoming strip, Browse, Search, Filters, Settings and Notifications.
- Projects can be shared with another person by invite.
-
Day one: from contract to running app
- Wrote the contract every package builds against, then the core (Quick Add, repeats, views), the server with its MCP tools, the web app and the deploy.
- Imported the old Todoist account, including deadlines and completed history. Output shows counts only, never task titles.
- Added a large automated test suite and fixed every bug it found at the root, with no test weakened or skipped. One of them:
@1pmwas being read as a label, so the 1pm reminder never came.