Home > Next Task Decider

Next Task Decider

I've used a few different tools to manage my tasks, going back to at least Google Tasks in 2010. For a long time, I used Todoist, which had a decent API and was willing to call a custom webhook whenever things changed. The availability of a RESTful API was a strong factor in my choice of Todoist over other task management software, and the API was nice enough. However, eventually, I decided more control and, critically, I didn't want to have to go over the web for everything, so I exported everything to plain markdown files and started writing a local daemon instead of webhooks.

The new philosophy is that these files can be anywhere within my expansive (synced and backed-up) home directory, and all my tools should be fast enough at walking these files (and watching for file changes) that it can act like a database. This seems to work so far. I'm using Obsidian as a nice interface to all these files, but writing my own things in a combination of Python and various system calls (either Windows or Linux) to do more custom things with the whole tree.

Task are divided into projects, and hierarchically organized within each project. In an observation that seemed important at the time, but that I'm increasingly thinking is not the most critical detail; within this hierarchy, I assume a total ordering, s.t. the first unfinished task with no uncompleted child tasks (and other conditions as well) is the next action for that project. After a few false starts and rewrites, I found a way to generate a graph from a hierarchical tasklist like

- [x] 1
- [ ] 2
    - [ ] 2b
    - [ ] 2c
- [ ] 3

such that nx.topological_sort

def get_next(markdown_str):
    partially_ordered_tree = get_tree(markdown_str)
    fully_ordered_dag = create_ordered_dag(partially_ordered_tree)
    for node in nx.topological_sort(fully_ordered_dag):
        if not done(node):
            return node

gives me exactly the ordering I want--the correct dependency sort order that surfaces just the leaf I want.

I start with the partially_ordered_tree given by a simple parse of the markdown with whatever library.

graph TD style 1 fill:#ddd,stroke:#555 style root fill:#f8f8f8,stroke:#ccc style 2 fill:#f8f8f8,stroke:#ccc style 3 fill:#f8f8f8,stroke:#ccc style 2b fill:#f8f8f8,stroke:#ccc style 2c fill:#f8f8f8,stroke:#ccc root 1 --> root 2 --> root 2b --> 2 2c --> 2 3 --> root

I copy graph this to start making modifications, and then, for each sibship in the original graph (you can't modify the graph while you're iterating over it, hence the copy), I draw an edge. Specifically, I draw an arrow from the deepest child of the upper node (including itself, and ordering sub-sibships last-to-first) to the deepest child of the lower node (including itself, ordering sub-shibships first-to-last).

graph TD style 1 fill:#ddd,stroke:#555 style root fill:#f8f8f8,stroke:#ccc style 2 fill:#f8f8f8,stroke:#ccc style 3 fill:#f8f8f8,stroke:#ccc style 2b fill:#f8f8f8,stroke:#ccc style 2c fill:#f8f8f8,stroke:#ccc root 1 --> root 2 --> root 1 --> 2b 2b --> 2 2c --> 2 3 --> root
graph TD style 1 fill:#ddd,stroke:#555 style root fill:#f8f8f8,stroke:#ccc style 2 fill:#f8f8f8,stroke:#ccc style 3 fill:#f8f8f8,stroke:#ccc style 2b fill:#f8f8f8,stroke:#ccc style 2c fill:#f8f8f8,stroke:#ccc root 1 --> root 2 --> root 1 --> 2b 2b --> 2 2c --> 2 2b --> 2c 3 --> root
graph TD style 1 fill:#ddd,stroke:#555 style root fill:#f8f8f8,stroke:#ccc style 2 fill:#f8f8f8,stroke:#ccc style 3 fill:#f8f8f8,stroke:#ccc style 2b fill:#f8f8f8,stroke:#ccc style 2c fill:#f8f8f8,stroke:#ccc root 1 --> root 2 --> root 1 --> 2b 2b --> 2 2c --> 2 2b --> 2c 2c --> 3 3 --> root

In theory, working within this framework will allow me to focus on a smaller part of each project at any given time, minimizing time taken to reorient when switching tasks. In practice, however, it seems that this tree analysis, while necessary, is the smallest part of deciding "what to do next". I have additional rules for transcluding in subtrees if a node is a link to another markdown file and nothing else. However, even allowing that this does some condensation work for us, really the my whole home dir still shouldn't be considered as one big tree with an unambiguous next task, but as a forest. Each has its own next, but ordering these is situationally-dependent.

I'm still considering the right abstraction for ordering those nexts contextually.