Computer :(

/home/rrix:blog:tags:cgit:rss

Automatic Workflows in Org-Mode

I recently implemented a system for automatically moving among a list of Org-mode tasks, which I call TaskFlows. The idea is simple: tag a project as a taskflow, and when you complete a task, it automatically clocks in to the next task.

There are a few usecases for this where I think it excels in my workflow:

  • Habits with subtasks
  • Preventing distraction with projects

I am a super duper forgetful person in the morning, especially before I've had coffee and so I've augmented my brain with a task flow to keep me moving and non-forgetful in the mornings. I haven't managed to forget my GPG smartcard since implementing this task flow system, which has been super helpful.

 ** INPROGRESS Morning Prep
<2015-07-14 Tue +1d>
:PROPERTIES:
:REPEAT_TO_STATE: NEXT
:FLOW: t
:END:
 *** NEXT Caffeine
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>
 *** NEXT Shower
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>
 *** NEXT Brush Teeth
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>
 *** NEXT Deodorant
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>
 *** NEXT Review agenda
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>
 *** NEXT Eat breakfast
:PROPERTIES:
:CLOCK_MODELINE_TOTAL: today
:REPEAT_TO_STATE: NEXT
:END:
<2015-07-13 Mon +1d>

The way this works is simple, I have a standard Bernt Hansen-style org-mode project, with a FLOW: T property set on it; when I mark a project task as done, it will automatically clock me in to the next task. Combine this with a REPEAT_TO_STATE, Efforts and a recurring timestamp and every morning I can be out the door in under 30 minutes without forgetting a thing and with the added bonus that those things are now clocked and optimized.

This system is also super helpful for the way I tackle my email and bootup at work, keeping on task with minimal distraction in meetings, and probably a ton of other situations. Most importantly, it's given me low-friction ways to tackle serial tasks, and that's made it easy to sprinkle in extra things, such as "Stand on the scale and capture it in this Org-mode table" every morning, or "properly clock how much time you spend reading email versus writing email"

How does it work?

I started out by modifying bh/clock-out-maybe, which is called whenever you mark a task as DONE

(defun bh/clock-out-maybe ()
  (when (and bh/keep-clock-running
             (not org-clock-clocking-in)
             (marker-buffer org-clock-default-task)
             (not org-clock-resolving-clocks-due-to-idleness))
    (rrix/clock-in-sibling-or-parent-task)))

This overrides Bernt's bh/clock-in-parent-task with my rrix/clock-in-sibling-or-parent-task:

(defun rrix/clock-in-sibling-or-parent-task ()
  "Move point to the parent (project) task if any and clock in"
  (let ((parent-task)
        (parent-task-is-flow)
        (sibling-task)
        (curpoint (point)))
    (save-excursion
      (save-restriction
        (widen)
        (while (and (not parent-task) (org-up-heading-safe))
          (when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
            (setq parent-task (point))))
        (goto-char curpoint)
        (while (and (not sibling-task) (org-get-next-sibling))
          (when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
            (setq sibling-task (point))))
        (setq parent-task-is-flow (cdr (assoc "FLOW"
                                              (org-entry-properties parent-task))))
        (cond ((and sibling-task
                    parent-task-is-flow)
               (org-with-point-at sibling-task
                 (org-clock-in)
                 (org-clock-goto)))
              (parent-task
               (org-with-point-at parent-task
                 (org-clock-in)
                 (org-clock-goto)))
              (t (when bh/keep-clock-running
                   (bh/clock-in-default-task))))))))

This function is quite similar in shape the the one it overrides, with some added rules:

  • has sibling && has flow -> clock sibling
  • has parent && (! has flow || ! has sibling) -> clock parent
  • ! has parent -> clock default