A list is a tag that sticks
We shipped named lists for undated tasks this June. Under the hood there is no list at all, and that was deliberate.
This month we shipped lists for the inbox. In weekkii, undated tasks live below the seven day columns, in an inbox that catches everything you have not scheduled yet. You can now group that inbox into named lists: Groceries, Someday, whatever matches how you actually think. It looks like the lists feature you have seen in other apps. It is not built like one, and the way it is built is a design decision worth explaining.
The obvious implementation is a new container concept. You add a list_id to every task, write list CRUD, build list filters, and teach the sync layer a new shape. Then you live with the consequences: your app now has two organizing systems, tags and lists, that overlap without agreeing. Is a list just a saved filter? Can a task carry tags while it sits in a list? What happens to list membership when the task gets scheduled onto a day? Those questions end up answered in a support doc.
We refused to build the container. In weekkii, the list's name is a tag, stored on the task like any other tag. Drag a task into Groceries and it carries #groceries. That is the entire data model.
One concept instead of two
Everything that already worked for tags works for lists, with zero new plumbing. Filters, pills, search: all of it applied to lists on the day the feature shipped, because to the rest of the app a list is indistinguishable from a tag. Schedule that grocery task onto Thursday and the tag comes with it, so the grocery run is still findable by tag after it leaves the list. Rename the list and the tag renames everywhere it appears. Create a new task while a tag filter is active and it inherits those tags automatically, as removable chips, so working inside a list feels like working inside a list even though no list exists anywhere in the database.
Fewer concepts also means less to sync. weekkii is local-first and end-to-end encrypted: every row the server stores is ciphertext, so every new entity type would be a new encrypted payload shape, a new merge case, a new thing that can conflict when two devices edit offline. Tags already survive all of that. Lists ride along without adding a single new row type.
The trade we are making
There is a real objection here, and we would rather state it than bury it. Purists want lists and tags to be different things. In our model, a task in two lists is just a task with two tags, and its list identity is fuzzy: it does not live in one place, it is labeled from two directions at once. If you want a task to belong to exactly one list, the way a file belongs to one folder, weekkii will not enforce that for you. We think the fuzziness is the point. A grocery item scheduled for Thursday is both a Thursday task and a grocery task, and forcing it to pick a side is how apps end up with duplicated tasks and stale lists. But it is a real trade, and some people will genuinely prefer the container.
One part of shipping this had nothing to do with the data model. Tasks that existed before the feature needed their tags applied retroactively, and an earlier sync bug taught us a rule we now refuse to break: never touch rows on a device until that device has fully caught up with the server. So the backfill runs exactly once per device, and only after sync settles. Nobody will ever notice it ran, which is the best outcome a migration can hope for.
weekkii still has no projects, no priorities, no nested folders. It has seven day columns, an inbox, and tags, and now some of those tags stick to a name at the top of a list. Drag a task into Groceries and it carries #groceries: one sentence, one concept, nothing new to learn.