🏠 Go home.

Automatic Workflows in Org-Mode

Published on

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:

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:

Respond to this note:

Ryan Rix is a privacy rights advocate and net-art wannabe. Reach them on the Fediverse as @rrix@cybre.space, twitter as @rrrrrrrix, via email to ryan@whatthefuck.computer or on Facebook or on Matrix as @rrix:whatthefuck.computer.