Todos is the household's to-do list: repairs, maintenance, errands, the projects that take three weekends. It's for my wife and me, and it's [3DPO](/projects/3dpo/)'s younger sibling: started on September 20, it borrowed 3DPO's best idea, its database server and most of its habits. It's built around two convictions. Getting a thought into the list should take seconds, because a thought that takes longer than that doesn't get written down. And a list of two hundred things is only useful if you can slice it by where you are and how long you have. So there are two ways in: type a line, or tell Claude in a sentence and let it do the filing.
The Available now screen: filter chips for place, inside or outside, effort, who and equipment; a weather line for today and the weekend with counts of inside and outside tasks; a Now possible strip; and tasks under Overdue, Doing and Next, one outdoor task tinted green.
Available now, the home screen. Every screenshot here is from the app's fictional test household.
## Type a line The capture bar is always on screen; on a phone it's pinned just above the tab bar, so adding a task never takes a tap to open anything. One line is one task: ```text Replace the furnace filter #basement #maintenance !quick ``` `#` is a tag, `@` a person, and `!` a word from a short list: priority, effort, or `!tomorrow` and `!weekend` to plan it on the board. Typing any of the three offers what it could mean. The grammar is parsed by the server, not the page, and anything it can't apply stays in the title and gets reported. Nothing is ever created to make a token work; a typo'd tag is a typo, not a new tag. The original words are kept beside the task they became. ## Or tell Claude The other way in is to talk. Todos is an MCP server, so Claude can read the list and write to it from wherever I'm already talking to it: Claude Code, the website, the phone app, the desktop app. A sentence like *"The wagon is due its service. Oil and filter I can do myself, the cabin filter too. The inspection needs booking before the end of the month, and one wiper is streaking"* becomes five tasks, tagged with the car and where the work happens, the inspection given a due date, and "fit the new wiper blades" set to wait on "buy wiper blades." No model runs on the server. There's no API key in the app and no AI line on the bill; the thinking happens in whichever Claude I'm talking to, on the subscription I already have. That makes the tools' descriptions the entire prompt, so the words are the product. They're checked into the repo as a generated file, a change to them shows up in review as a diff of the prompt, and a test fails if any of them passes 2,048 characters, which is where Claude Code stops reading. A sample: > Searching: every word given to search_tasks must match, each anywhere in a > task's title, notes or tags — so search with one or two distinctive words > ("gutters", "furnace filter"), never a sentence. No test can call a model for free, so the prompt is checked by hand: a smoke-test doc of seventy-one things to say to Claude, against the fictional household, with what a right answer looks like. *"I'm in the garage with an hour. What could I get done?"* should come back as one call: ```text search_tasks tags: [garage] effort: hour available_only: true assignees: [me, none] ``` The first full rounds cost about 85 cents each at API prices. What Claude makes goes on the list right away, flagged. The Review screen lays each request out under the words it came from, with every tag Claude chose marked and removable. Accepting is a person's job: there is no tool that lets Claude accept its own work.
The Review screen: a card headed 'Sam, via Claude, Thursday' quoting a sentence about the wagon's service, then the tasks made from it, each with tags marked as Claude's and an Accept button.
One sentence, five tasks, Claude's tags marked until someone accepts them. This one is from the test data rather than a live conversation, but it's the shape of what Claude files.
Getting the website and the phone app to connect meant writing an OAuth server, deliberately no bigger than Claude needs: it trusts client documents only from Claude's own domain, its tokens are random strings stored hashed, and a connection ends wherever a sign-in session would. ## Tags are the structure Most household tasks are "do it when I can." What makes a pile of them usable is slicing by where you are, which tool is already out, which car is in the driveway, and what the weather is doing. Tags come in facets (place, vehicle, equipment, category, store), and inside-or-outside is a field of its own, because the weather keys off it. Available now is the home screen: everything open that isn't blocked, parked or waiting for its start date, narrowed by a row of chips. "I'm in the garage with an hour" is two chips. A task that doesn't say how long it takes shows up anyway with a `?`, so whatever was captured in a hurry doesn't vanish. The forecast comes from [Open-Meteo](https://open-meteo.com/), which is free and needs no key. It shows on the board's planning columns and above the list, and on the first dry day it says how many outside jobs are ready. Outside tasks are tinted green everywhere, so you can see at a glance how much of the list is waiting on the weather. Where home is lives in the database, set by an admin, and never appears in a log or anything sent to Claude. ## Derived, again Like 3DPO, the state you'd normally set by hand is worked out instead. Only the board column is manual. Blocked, deferred, available and overdue all come from one SQL view that every query reads. - **What waits on what.** "Pressure-wash the deck, then stain it" is a dependency, checked for loops when it's saved. A blocked task drops out of Available now, and when its last blocker is done it shows up for a week in a "Now possible" strip. - **Planning.** The board has Doing (tomorrow) and Doing (weekend). Those aren't statuses but a planned day, so Sunday's "tomorrow" is simply today on Monday, without anybody moving it. - **Routines.** The furnace filter every three months: finishing the task makes the next one, the same task with new dates, hidden until it's nearly due. Dropping one skips a round instead of ending the routine. - **Dates are dates.** "Today" is the database's date in the household's time zone, and a due date is the string `2026-10-03` from end to end, never a timestamp that can slip a day crossing time zones.
The board: columns for Backlog, Next, Doing, Doing (tomorrow) and Doing (weekend), with weather glyphs and temperatures in the planning columns' headings, outdoor cards tinted green, and Someday and Done folded to the sides.
The board, with each planning column's weather in its heading.
## Small, careful things - **Private tasks** exist, and someone else's private task is a 404 to you: the same 404, word for word, as a number that was never handed out. - **Search forgives typos.** Each word is matched against the task's number, title, notes and tags, word stems, and near misses in the title, so "guter" still finds the gutters. A typed word only ever reaches the database as data, never as part of a query, so there's nothing to escape. The test suite types two dozen hostile strings into the search box, and every one has to come back cleanly. - **Duplicate hints, never refusals.** A new task whose title is close to an open one gets "looks like #6" in its confirmation, and so does Claude. - **A calendar feed** of your dated tasks for Google or Apple Calendar, which never includes the notes. - **Export** to JSON and CSV, with any cell that would start a spreadsheet formula defused. - **A command palette** that goes places and finds things, and never changes a task.
Available now on a phone: filter chips, the weather lines, the task list, and the capture field pinned above a bottom tab bar.
On a phone, the capture field sits above the tabs on every screen.
## Under the hood One Go binary serves the JSON API, the MCP endpoint, the OAuth server and the React client, which is compiled into it. It runs as one Cloud Run service that scales to zero, and its Postgres is a second database on 3DPO's small VM. There's no queue, no scheduler and no server-side AI. The target was under a dollar a month on top of what 3DPO already costs. The page runs under a strict Content Security Policy: no inline scripts or styles, ever. The end-to-end tests drive the real binary against a seeded database at the actual screen widths of the phones, tablet and desktop in the house, and every test fails if its page logs an error or breaks the CSP. One test breaks it on purpose, to prove the check can fail. It's about 25,000 lines of Go, 29,000 more of Go tests, and 41,000 of TypeScript counting the browser tests, in about 660 commits over one month. ## Built to be maintained by agents Like 3DPO, most of it was written by Claude Code, and the repository was designed for that from the first commit. The design doc states the goal: "an agent with no prior context can orient in minutes, make a change, and prove it is safe, without a human re-explaining the project." - **Eighteen invariants** sit in the map file, each one a rule where "break one and you have a bug, even if tests pass": all rules live in the service, no model runs on the server, a word somebody typed reaches SQL only as data. - **Thirty-five decision records**, append-only: the context, what was chosen, and what was turned down. A reversal is a new record, so nobody re-argues a settled question without new information. - **A gotchas folder**, by topic, for anything that cost someone time, with the evidence. - **A note in every Go package** with its purpose, its invariants, and the things that look wrong but are deliberate. - **One command**, `make check`, that says whether a change is safe. CI runs exactly that. It went from an empty repo to five milestones in four weeks. ## Where it's going - **A real week of routines and weather.** They're built and deployed, but they haven't had the week of actual use that would show whether they're right. - **My wife's Claude connection.** Mine is set up on the web, the phone and the desktop app; hers waits on her. - **The parking lot.** A design file holds the ideas that aren't built yet, like an Android share target. None of them is a promise. - **Maybe going public.** The repo is MIT-licensed and written as if it were public already, so opening it up would be a settings change, not a cleanup.