Task Management with Logseq
a GTD-inspired workflow
Logseq is currently my favorite tool for note-taking and task management. I believe that these collective features elevate it above other tools:
- open source
- org-mode-like tasks
- custom querying language (datalog + clojure)
- local-first file management (see my project Homemanager which also operates on top of local files)
Of course, Logseq has many other features as a “second brain” note-taking system, but there are a few negatives with their current approach:
- all text files reside in a ‘pages’ folder and cannot be easily divided into subfolders
- project progress and task depenedencies have to be implemented manually
I’m nitpicking though; the truth is that I’ve tried at least 15 different task management systems and Logseq is the only one that checks nearly every box for me.
This is my GTD-ish workflow as a student, full-time worker, and chronic over-planner.
1. Capture all incoming thoughts and notes in the daily journal.
I could also use an ‘inbox’ page, but a single page will become bloated as time passes. Using the daily journal ensures that I have a fresh start each day, and assists in step 2; since items are captured on the day I thought of them, they are given some context when organizing them.
In order for the following step to work, captured items must be placed in a journal page and labelled as a ‘TODO’.
2. Clarify and organize items with tables and properties.
Tasks that come from daily journals are queried for, sorted in a table, and then organized whenever I have time. I put this table in a page called ‘organize’.
code for this query
#+BEGIN_QUERY
{:title [:b "all tasks from journal pages"]
:query [:find (pull ?b [*])
:where
[?b :block/marker ?m]
[?b :block/page ?page]
[?page :block/journal? true]
(not [?b :block/page ?p] [?p :block/name "recurring tasks"])
[(contains? #{"TODO" "DOING"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
]
:breadcrumb-show? true
:table-view? nil
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/name])) result))
}
#+END_QUERY
Click on an item in any table, and its corresponding block reference will appear on the right-hand side panel. This side panel provides the context I referred to in the previous step, as well as a place to edit the item.
Clarify and organize are two separate steps in GTD, but I find that they happen simultaneously when processing this table.
There is no tried & true recipe for organizing the chaos, but I generally follow a loose set of rules.
- Schedule and deadline the item if it needs to occur on or by a certain day
- Add a priority
[#A-#C]
to the item if it has a deadline - Add
#context
to the item if it’s away from home - Add additional properties to the item if further specification is required. Properties can be arbitrarily defined and sorted on. In this example, I use the ‘type’ property with a value of ‘birthday’.
- If the item is part of a project, or has multiple steps, cut and paste the item’s block from the side panel and place it in the “projects” page
- If the item repeats, cut and paste the item’s block from the side panel into the “reccurring tasks” page
- Add the item to the “discrete tasks” page by cutting and pasting its block.
Once the table is processed, there should be no tasks left in the journals; I like to use the journal pages primarily as diary entries.
3. Engage tasks using query lists.
With Logseq’s querying language, it’s possible to recreate and customize Emac’s famous org-agenda. This is what my agenda looks like (with example tasks):
It is divided into three sections:
Today: items that are scheduled or have a deadline today, with 'morning' tasks at the top and 'evening' tasks at the bottom.
#+BEGIN_QUERY
{
:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
[(contains? #{"TODO" "DOING"} ?m)]
(block-content ?b "morning")
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(>= ?d ?start)]
[(<= ?d ?next)]]
:inputs [:30d-before :today]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/content])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
#+BEGIN_QUERY
{:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
[(contains? #{"TODO" "DOING"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(>= ?d ?start)]
[(<= ?d ?next)]]
:inputs [:30d-before :today]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/content])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
#+BEGIN_QUERY
{
:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
[(contains? #{"TODO" "DOING"} ?m)]
(block-content ?b "evening")
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(>= ?d ?start)]
[(<= ?d ?next)]]
:inputs [:30d-before :today]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/content])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
Tomorrow: everything scheduled or with a deadline tomorrow except morning and evening routines, since those happen every day. I still show my other recurring tasks in this section.
#+BEGIN_QUERY
{:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
[(contains? #{"TODO" "DOING"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(>= ?d ?start)]
[(<= ?d ?next)]]
:inputs [:tomorrow :tomorrow]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/content])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
Ten Days: everything with a schedule or deadline date in the next ten days except for any recurring tasks.
#+BEGIN_QUERY
{:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
(not (page-ref ?b "habit"))
(not [?b :block/page ?p] [?p :block/name "recurring tasks"])
[(contains? #{"TODO" "DOING"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(> ?d ?start)]
[(< ?d ?next)]]
:inputs [:tomorrow :11d-after]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/scheduled])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
4. Perform weekly reviews using templates.
I include ‘weekly review’ as a recurring task since I tend to forget otherwise. Performing weekly reviews is easy with the use of Logseq’s embedded templates.
- Create a new page for the weekly review
- Use
/template
to autofill the page. I keep the below template in a file called ‘templates’, but templates can exist anywhere and can be defined by right-clicking on a block and selecting ‘make template’.
Weekly Review Template
- weekly review
template:: weekly_review
template-including-parent:: false
- Items that need review:
- Project tasks without due dates
- query-table:: false
#+BEGIN_QUERY
{:query [:find (pull ?b [*])
:where
[?b :block/marker ?m]
(page-ref ?b "projects")
(not [?b :block/page ?p] [?p :block/name "recurring tasks"])
[(contains? #{"TODO" "DOING"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
]
:breadcrumb-show? true
:table-view? false
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/name])) result))
}
#+END_QUERY
- Goals from past week:
#+BEGIN_QUERY
{:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
[(contains? #{"TODO DOING DONE"} ?m)]
(block-content ?b "Goals for next week")
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(> ?d ?start)]
[(< ?d ?next)]]
:inputs [:7d-before :today]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/scheduled])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
- Tasks completed this week
- #+BEGIN_QUERY
{:query [:find (pull ?b [*])
:in $ ?start ?next
:where
[?b :block/marker ?m]
(not (page-ref ?b "goal"))
(not [?b :block/page ?p] [?p :block/name "recurring tasks"])
[(contains? #{"DONE"} ?m)]
(not(or (block-content ?b "evening") (block-content ?b "morning")))
(or [?b :block/scheduled ?d] [?b :block/deadline ?d])
[(> ?d ?start)]
[(< ?d ?next)]]
:inputs [:7d-before :today]
:result-transform (fn [result]
(sort-by (fn [h] (get-in h [:block/scheduled])) result))
:breadcrumb-show? false
:table-view? false
}
#+END_QUERY
- Goals for next week:
- TODO #goal
- TODO #goal
- TODO #goal
My template isn’t quite as evolved as I would like it to be, but it’s a good starting point for those trying to “get things done”. Todoist’s blog post The Weekly Review provides a deeper explanation on how to perform a weekly review and the benefits of doing so.
5. Try to actually do things and not fixate on the tool
This is undoubtedly the hardest step of task management. Logseq has an abundance of features that, when combined with their query language, makes for a powerful, customizable, and… possibly distracting tool. Historically, I’ve found that the level of a tool’s customizability directly relates to the likelihood that I get caught up ‘perfecting’ the tool instead of using it. And generally, in the process of perfecting, I find serious flaws in the tool and abandon it in search of a better one.
I don’t have any direct solutions for this phenomenon, but this post outlines my most successful attempt yet. Not because I’ve found the perfect tool, but because I’ve found one whose flaws I can live with.
Ironically, while writing this post, I spent more time tweaking my queries than completing anything on my agenda.