Why it exists
I built a goal tracker, journaling, personal admin app first, as its own app. Goals with timelines, milestones, web push reminders, journaling, a database, login.
It worked, but I never opened it.
It took a month for me to finally decide to take it down and restart, because nothing was wrong with the functionality. Getting to it was the problem. Now I had one more place to check, and there was too much friction keeping me from opening it. I read Atomic Habits last year and I may not remember every piece, but I do remember one major point: too much friction will keep a habit from forming.
I kept planning my weeks on google calendar and google tasks, without any journaling or goal functionality because I already had everything configured, future events, settings, and years of use.
What I needed was for my goals to be in front of me on a screen I was already using. Tasklist is the screen I already look at, because it holds my agenda across Google Tasks, Google Calendar, and my local calendar. So I moved goals into my custom version of google calendar's setup. Now, I have more control over the setup, and I have even combined google calendar and tasks into one application that I can access from my phone, iPad, laptop, and desktop.
Some of this project involved deleting large amounts of my old work. The login, the database, the reminders, and the interface all went.
How it works
A goal is an ordinary Google Task in a list called "Tasklist Goals".
That sentence carries the whole design. Google Tasks gives you six fields worth using: title, notes, due, status, parent, and position. Four of them already mean what a goal needs them to mean, so a goal uses them directly instead of storing a parallel copy.
ONE GOOGLE TASK
+--------------------------------------------+
| title Run 30 times this autumn |--> goal name
| due 2026-11-30 |--> target date
| status needsAction |--> done or not
| |
| notes Training for the spring half. |--> my own notes
| |
| [tasklist:goal] |--+
| mode = count | | what Google
| target = 30 | +--> has no
| current = 12 | | field for
| unit = runs | |
| [/tasklist:goal] |--+
+--------------------------------------------+
|
| parent
v
+--------------------------------------------+
| Buy shoes completed |--> milestones
| Sign up for the 10k |
+--------------------------------------------+Milestones are subtasks, which Google Tasks supports as one level of nested tasks. A checklist goal counts its progress from them. Progress on a counting goal comes from the payload instead, so "12 of 30 runs" and "3 of 4 milestones" are the same feature with two sources.
Nothing about a goal lives in my database. There is one row in the whole system that relates to goals, and it stores the id of the list.
Limitations
- The tasks payload is user-editable on Google, so it can be corrupted. Parsing degrades and the app shows you how to restore it, but corruption stays possible. That is the trade I took to keep goals self-contained.
- Google allows one level of nesting, so a milestone cannot have milestones.
- Clearing a target date does not work. The update sends an undefined value where Google wants an explicit null, so the old date stays.
- A milestone can be completed. A goal itself cannot yet.
- Tasklist reports separate lists if they have duplicate names instead of merging the lists.