Next Action Dependencies
Projects are an excellent way of defining lots of next actions into a group with a singular goal, but what do you do about NAs that depends on another NA to be completed?
What i'm suggesting is a slight modification to the ticker system that currently exists in trunk, you can set a date for the NA to show on the main list OR when a certain NA has been completed. That way when you complete one NA on your main list, the follow-on NA will be transfered from the Tickler to the main list.
This feature would be an amazing timesaver, and i guess that other people would also like it implemented.
!http://www.assembla.com/spaces/tracks-tickets/documents/dD_W-EIQar3QxReJe5afGb/download/todo_fsm.png!
What i'm suggesting is a slight modification to the ticker system that currently exists in trunk, you can set a date for the NA to show on the main list OR when a certain NA has been completed. That way when you complete one NA on your main list, the follow-on NA will be transfered from the Tickler to the main list.
This feature would be an amazing timesaver, and i guess that other people would also like it implemented.
Proposal for todo FSM with added pending state
!http://www.assembla.com/spaces/tracks-tickets/documents/dD_W-EIQar3QxReJe5afGb/download/todo_fsm.png!
Leave a comment
+1
imho a very important feature, because in my experience actions often depend on each other.
If you want to think once about all the next actions for one project and then forget about them until they can be done, dependencies is the way to go.
I also second the many-to-many suggestion. One possible way to do this would be (in addition to the show_from-field) a field to add actions (drop-down or by #id) which need to be completed before this actions is shown.
imho a very important feature, because in my experience actions often depend on each other.
If you want to think once about all the next actions for one project and then forget about them until they can be done, dependencies is the way to go.
I also second the many-to-many suggestion. One possible way to do this would be (in addition to the show_from-field) a field to add actions (drop-down or by #id) which need to be completed before this actions is shown.
If 2.0 adds a single user-visible feature, this would be the one I would beg for.
Note that there are similar/related/(possibly duplicate?) feature requests in #185, #105, and #360 (and possibly some others that I missed.) Peering through those might give a good idea about how people want to use this feature.
Note that there are similar/related/(possibly duplicate?) feature requests in #185, #105, and #360 (and possibly some others that I missed.) Peering through those might give a good idea about how people want to use this feature.
+1
This, to me, is a critical feature; as I add more projects and actions to Tracks, I find myself drowning in non-executable actions.
Using stars or tags is a workaround, but it means I have to do extra work when I complete an action to go find the actions it enables.
I agree that many-to-many dependencies are real, but if I could just have an action depend on (at most) one previous action, that would get me at least 75% of the value.
Thanks.
-- David Singer (http://readthisblog.net)
This, to me, is a critical feature; as I add more projects and actions to Tracks, I find myself drowning in non-executable actions.
Using stars or tags is a workaround, but it means I have to do extra work when I complete an action to go find the actions it enables.
I agree that many-to-many dependencies are real, but if I could just have an action depend on (at most) one previous action, that would get me at least 75% of the value.
Thanks.
-- David Singer (http://readthisblog.net)
It seems that a simple table:
|| waiting_xref_id || task_id || waiting_on_task_id ||
|| 1 || 1 || 2 ||
|| 2 || 1 || 3 ||
|| 3 || 1 || 5 ||
|| 4 || 4 || 5 ||
Should do the trick. The first field (waiting_xref_id) is just the ID. The second field contains ids of inactive tasks. The final field contains ids of tasks that must be completed for the task in the second field to become active. In this example, Task 1 cannot be completed until Tasks 2, 3, and 5 are complete; And Task 4 cannot be completed until Task 5 is complete. The following new actions would have to be added to controller logic:
Therefore, any Tasks whose ID is NOT represented in waiting_xref.task_id has no prerequisites, and any Task whose ID is represented in waiting_xref.waiting_on_task_id has tasks waiting on it.
The trick here is that every time a task (n) is fetched, we need to know the number of prerequisites it has so that we can render the task as inactive/waiting or a "next action". The number of prerequisites can be calculated by SELECT COUNT(*) from waiting_xref_id WHERE task_id == n. Performing this aggregation sub-query every time a task is fetched will add astronomical load to the database server. If the database has 10 tasks then fetching the complete data just increased from one fetch to eleven, unless the RDBMS can do sub-queries and even so, there is a greater workload.
This tremendous workload could be reduced by adding a field in the task table which serves as a reference counter to represent the number of prerequisite tasks must be completed for the task to become a "next action." In that scenario, all tasks with a 0 in that field are next actions. When a new task is added as a prerequisite, then in addition to adding a row to waiting_xref_id, increment the reference counter for the task in which the new task is a prerequisite. When a task ( Task K ) is completed, before deleting all rows from waiting_xref where waiting_on_task_id h2. K.
Ugh. Was that clear as mud? I apologize from all the pseudo-code and jargon. I know SQL isn't usually even used in Rails, but I used it to express what happens to the data. Let Rails do the heavy lifting. Also forgive me for using the term "Task" interchangeably with "Todo."
|| waiting_xref_id || task_id || waiting_on_task_id ||
|| 1 || 1 || 2 ||
|| 2 || 1 || 3 ||
|| 3 || 1 || 5 ||
|| 4 || 4 || 5 ||
Should do the trick. The first field (waiting_xref_id) is just the ID. The second field contains ids of inactive tasks. The final field contains ids of tasks that must be completed for the task in the second field to become active. In this example, Task 1 cannot be completed until Tasks 2, 3, and 5 are complete; And Task 4 cannot be completed until Task 5 is complete. The following new actions would have to be added to controller logic:
- When a task ( Task N ) is deleted, DELETE * FROM waiting_xref WHERE task_id == N.
- When a task ( Task N ) is marked complete, DELETE * FROM waiting_xref WHERE waiting_on_task_id == N.
- When a task ( Task N ) is marked as a prerequisite for another task ( Task M ), INSERT waiting_xref_id (task_id, waiting_on_task_id) VALUES ( M, N )
Therefore, any Tasks whose ID is NOT represented in waiting_xref.task_id has no prerequisites, and any Task whose ID is represented in waiting_xref.waiting_on_task_id has tasks waiting on it.
The trick here is that every time a task (n) is fetched, we need to know the number of prerequisites it has so that we can render the task as inactive/waiting or a "next action". The number of prerequisites can be calculated by SELECT COUNT(*) from waiting_xref_id WHERE task_id == n. Performing this aggregation sub-query every time a task is fetched will add astronomical load to the database server. If the database has 10 tasks then fetching the complete data just increased from one fetch to eleven, unless the RDBMS can do sub-queries and even so, there is a greater workload.
This tremendous workload could be reduced by adding a field in the task table which serves as a reference counter to represent the number of prerequisite tasks must be completed for the task to become a "next action." In that scenario, all tasks with a 0 in that field are next actions. When a new task is added as a prerequisite, then in addition to adding a row to waiting_xref_id, increment the reference counter for the task in which the new task is a prerequisite. When a task ( Task K ) is completed, before deleting all rows from waiting_xref where waiting_on_task_id h2. K.
Ugh. Was that clear as mud? I apologize from all the pseudo-code and jargon. I know SQL isn't usually even used in Rails, but I used it to express what happens to the data. Let Rails do the heavy lifting. Also forgive me for using the term "Task" interchangeably with "Todo."
The problem with the proposal I just made (previous post) as you can see is it adds great complexity to what should be a simple problem. The many-to-many method makes sense if you need a Gantt chart or are managing hundreds of tasks. Having thought it over with fresh eyes, it occurred to me some of the other GTD systems attack this problem with a more simplistic approach.
- All tasks have a sequence within a project. They can be ordered much the same way as projects in Tracks.
- When a task is completed, if the task following it in sequence is inactive, it is marked as active "next-action"
This simpler approach, I think, solves the problem for most users. It seems like complex task dependancies are intended to solve the problem of parallel tasks, and 'critical paths' (if I may borrow a term from the Project Management field). Since Tracks is a personal'' list manager, and people cannot perform tasks in parallel (if the tasks have been truly broken down to discrete work units as David Allen's book suggests) I suppose that the greater use case for such a feature is simply to clean-up the list of next actions. User ''Bob'' doesn't want to see ''Paint the fence'' until he has completed ''Purchase paint at Big-Box Central.
Another 2 1/2 cents. What you you think? Has there been a poll to determine the greater use-case of this feature?
jagipson: I think that your simpler scheme is good, but I do think that projects need to be settable as parallel or sequential on a per-project basis (as OmniFocus does). Even as a personal list manager, it's still perfectly possible (and desirable) to have more than one next action to choose from within a project, if those actions don't depend on one another.
Personally, I don't find that the notion of dependencies helps me at all, but then I might have slightly odd projects ;-)
Personally, I don't find that the notion of dependencies helps me at all, but then I might have slightly odd projects ;-)
OmniFocus implements their task sequencing as an attribute of the Project it contains. In other words, tasks in OmniFocus are not dependent on one another (directly). Projects act as containers for tasks. Tasks may also act as containers for sub-tasks (which I think is a departure from GTD, since the whole idea of creating tasks is that it is a single work unit). Any container (whether it is a task or a project) is given the "sequential/parallel" option which applies to all the tasks it contains. Tasks are not really related to one another (in the strict sense) except by what order they are within it's container. If the container is configured for parallel tasks, the first unfinished task in the list is the active "next-action" and all following things are inactive. There is no way to mark them as 'always active' to bypass this behavior. If the container is configured for sequential tasks, then all tasks within are active. In order to do complex branching and merging of possible next actions (singular and parallel) a great deal of creativity must go into how tasks and subtasks are arranged -- probably much more effort than was intended by the GTD methodology. When we enter the realm of tasks that have end-to-start and start-to-start dependancies, we are venturing into the realm of full-blown project management like OpenProj, Gantt, MS Project, and friends. Personally, I don't use task dependancies because I find that if I've planned out tasks far enough that they are not next-actions, then during the course of doing my next-actions, things change, and I have to delete my dormant future tasks and create new ones based on the latest information.
Another approach is the ThinkingRock approach, in which there is no parallel/sequential approach to task execution. Projects are always sequential in nature, but the user can mark any number of tasks to instantly be next-actions. ThinkingRock doesn't start marking dormant tasks as next-actions until all active tasks have been completed (AFAIK). As I recall, it then proceeds marking dormant tasks as next actions sequentially as next-action tasks are completed.
Another approach is the ThinkingRock approach, in which there is no parallel/sequential approach to task execution. Projects are always sequential in nature, but the user can mark any number of tasks to instantly be next-actions. ThinkingRock doesn't start marking dormant tasks as next-actions until all active tasks have been completed (AFAIK). As I recall, it then proceeds marking dormant tasks as next actions sequentially as next-action tasks are completed.
I've been giving this problem some more thought.
What if we were to add a string field to Todo, named "event_tags."
Let the event_tags field contain semicolon delimited events. The format of such events would be:
<type>[-id]:<attribute>=<new value>
where type is one of
p - a project, if the optional parameter id is not specified, then default to the todo's project
t - another todo, if the optional parameter id is not specified, then default to the todo with the lowest ID in the same project which is NOT done.
The attributes available depend on the type. A project (p) would have status=active, status=hidden, status=completed, and position=n (where n is some integer).
The attributes for a todo would be project=<id>|<name>, context=<id>|<name>, add_tag=<name>, drop_tag=<name>
Using this approach, a user can file away tasks that can't be done (dependent on other things) using specially crafted event tags. The event tags are triggered when the user marks the item as done.
Consider the following uncompleted tasks:
Task: Purchase ingredients
TaskID:3
Context: Tesco
Project: Make Cookies
event_tags: t:context=Kitchen; t-7:context=Kitchen
Task: Preheat Oven
TaskID:5
Context: Waiting
Project: Make Cookies
event_tags: t-12:add_tag=Preheated
Task: Mix Dry Ingredients
TaskID:7
Context: Waiting
Project: Make Cookies
event_tags: t:context=Kitchen
Task: Add Wet Ingredients
TaskID:8
Context: Waiting
Project: Make Cookies
event_tags: t:context=Kitchen
Task: Bake for 20 minutes
TaskID:12
Context: Waiting
Project: Make Cookies
event_tags: p:status=completed
The system starts out with only one task not in the Waiting context (Purchase Ingredients). When that task is marked done, the next task and task 7 both get their context changed to "Kitchen." When Preheat Oven is completed, a "Preheated" tag is added to the 'Bake for 20 minutes' task (just because). When the 'Mix Dry Ingredients' is completed, the next task's context is set to "Kitchen." When the 'Add Wet Ingredients' task is done, the next task's context is set to "Kitchen." When the "Bake for 20 minutes" task is done, the project is marked completed.
Unfortunately, this system would allow a task to have only one dependency, and not multiple.
What if we were to add a string field to Todo, named "event_tags."
Let the event_tags field contain semicolon delimited events. The format of such events would be:
<type>[-id]:<attribute>=<new value>
where type is one of
p - a project, if the optional parameter id is not specified, then default to the todo's project
t - another todo, if the optional parameter id is not specified, then default to the todo with the lowest ID in the same project which is NOT done.
The attributes available depend on the type. A project (p) would have status=active, status=hidden, status=completed, and position=n (where n is some integer).
The attributes for a todo would be project=<id>|<name>, context=<id>|<name>, add_tag=<name>, drop_tag=<name>
Using this approach, a user can file away tasks that can't be done (dependent on other things) using specially crafted event tags. The event tags are triggered when the user marks the item as done.
Consider the following uncompleted tasks:
Task: Purchase ingredients
TaskID:3
Context: Tesco
Project: Make Cookies
event_tags: t:context=Kitchen; t-7:context=Kitchen
Task: Preheat Oven
TaskID:5
Context: Waiting
Project: Make Cookies
event_tags: t-12:add_tag=Preheated
Task: Mix Dry Ingredients
TaskID:7
Context: Waiting
Project: Make Cookies
event_tags: t:context=Kitchen
Task: Add Wet Ingredients
TaskID:8
Context: Waiting
Project: Make Cookies
event_tags: t:context=Kitchen
Task: Bake for 20 minutes
TaskID:12
Context: Waiting
Project: Make Cookies
event_tags: p:status=completed
The system starts out with only one task not in the Waiting context (Purchase Ingredients). When that task is marked done, the next task and task 7 both get their context changed to "Kitchen." When Preheat Oven is completed, a "Preheated" tag is added to the 'Bake for 20 minutes' task (just because). When the 'Mix Dry Ingredients' is completed, the next task's context is set to "Kitchen." When the 'Add Wet Ingredients' task is done, the next task's context is set to "Kitchen." When the "Bake for 20 minutes" task is done, the project is marked completed.
Unfortunately, this system would allow a task to have only one dependency, and not multiple.
It will be better to have a new model: outline_items
We can choose best tree implementation from http://www.railsinside.com/plugins/164-7-tree-data-structure-plugins-for-rails.html
- content_id,
- content_type, project, todo, note, etc
- position - vertical position that can be used for giving priority to items, higher in the list for the same level -- higher priority.
- other fields from Acts_as_threaded plugin http://wiki.rubyonrails.org/rails/pages/Acts_as_threaded
We can choose best tree implementation from http://www.railsinside.com/plugins/164-7-tree-data-structure-plugins-for-rails.html
It seems like the Observer Pattern (GoF) would allow projects or Todos to "observe" Todos for changes of state, and then the observing object can execute stored commands (could just be an array of restful posts back to the system). I just can't seem to find a implementation of the Observer pattern in Rails. Using this sort of messaging, however will allow for more complex models than a simple outline could.
Oops - I should have been looking in the core API, not for an observer plugin:
http://api.rubyonrails.com/classes/ActiveRecord/Observer.html
Using after_save we can check if the ToDo is done, then launch stored actions
http://api.rubyonrails.com/classes/ActiveRecord/Observer.html
Using after_save we can check if the ToDo is done, then launch stored actions
looking at the complete thread here, I think a simple solution like the orginal submitter suggested would be a good start. All other solutions seem very complex. I do not see a useable gui for many-to-many dependencies.
@jeffrey - I think a dependency is not the same as waiting for. I mean, the waiting for context is IFAIK meant for actions you have delegated to others. I think we should handle dependencies different. for example, we could use a + in the gui on an action. Clicking it will reveal all actions that depend on that action.
@jeffrey - I think a dependency is not the same as waiting for. I mean, the waiting for context is IFAIK meant for actions you have delegated to others. I think we should handle dependencies different. for example, we could use a + in the gui on an action. Clicking it will reveal all actions that depend on that action.
Okay, sorry. I've been thinking about this one for over a year, and I got carried away.
What about this much more simple non-meta scripting approach:
Add a new boolean attribute to TODO:
my_item.next? # => true|false
This field is used to determine if a task is available. It's not available if it depends on another task to be completed first.
Add a new numeric attribute to TODO:
my_item.position
This field will keep the sequence of the task within the project
Add a new boolean attribute to PROJECT
my_project.is_sequential?
(Also a stub attribute, my_project.is_parallel?, which always equals not my_project.is_sequential?)
In sequential projects, all tasks, except the lowest ordered (lowest sequence #) are not next tasks. In parallel projects, actions may have an order, but it means nothing, and all tasks are next (available), similar to how Tracks works now.
When a new task is added to a sequential project, the task is marked as not next. When any task is completed in a sequential project, Tracks checks to see if there are any remaining next tasks. If there are no more remaining next tasks in the project, the Tracks changes the lowest sequence non-done task into a next task.
Finally, add a toggle on the Home page menu (similar to toggle notes) that allows the user to toggle viewing only next actions, vs all non-done actions.
Is this better, or am I still out in the sticks?
What about this much more simple non-meta scripting approach:
Add a new boolean attribute to TODO:
my_item.next? # => true|false
This field is used to determine if a task is available. It's not available if it depends on another task to be completed first.
Add a new numeric attribute to TODO:
my_item.position
This field will keep the sequence of the task within the project
Add a new boolean attribute to PROJECT
my_project.is_sequential?
(Also a stub attribute, my_project.is_parallel?, which always equals not my_project.is_sequential?)
In sequential projects, all tasks, except the lowest ordered (lowest sequence #) are not next tasks. In parallel projects, actions may have an order, but it means nothing, and all tasks are next (available), similar to how Tracks works now.
When a new task is added to a sequential project, the task is marked as not next. When any task is completed in a sequential project, Tracks checks to see if there are any remaining next tasks. If there are no more remaining next tasks in the project, the Tracks changes the lowest sequence non-done task into a next task.
Finally, add a toggle on the Home page menu (similar to toggle notes) that allows the user to toggle viewing only next actions, vs all non-done actions.
Is this better, or am I still out in the sticks?
Correct me if I'm wrong, but I think you are thinking about two functions:
I personally do not like the second one because I think it is not very close to the GTD-like tool Tracks is. Actually IIRC, GTD states all actions on your lists are next actions, so dependencies should not exist anyway (on your lists that is). But in practice often todos are staled, because it turned out to depend on another action. The complexity of the second bullet looks to me a lot like a project management tool, which we strive not to become :-)
- adding a relation from one todo to another to state that the todo depends on the completion of that other todo
- creating the ability to set a sequence / order in the todos belonging to a project to determine the next action in the list of todos in that project (i.e. first this, than that, etc.)
I personally do not like the second one because I think it is not very close to the GTD-like tool Tracks is. Actually IIRC, GTD states all actions on your lists are next actions, so dependencies should not exist anyway (on your lists that is). But in practice often todos are staled, because it turned out to depend on another action. The complexity of the second bullet looks to me a lot like a project management tool, which we strive not to become :-)
I agree whole-heartedly with your statement about the latter point, and I only intended it as a means to accomplish the former. The truth of the matter is that some people have a difficult time determining the next action without thinking of two or three additional actions which are not next actions. Thinking too far ahead is a waste, because facts may change, making the workflow obsolete before it is executed, however, when determining the next action predicates two or three actions ahead, it seems like a waste not to record those, because odds are they will have to be re-thought.
in that case I think we can safely say that we should start with simple dependency where one todo depends on one other todo: a tree like structure. I think a simple foreign key stating that todo a depends on todo b should suffice.
And a good user interface.
I was thinking on only showing the root of the tree with a plus (+) to show all dependents
And a good user interface.
I was thinking on only showing the root of the tree with a plus (+) to show all dependents
on 2009-01-21 09:46 *
By
What about simply handling Next Actions in a single project, by ordering the project's tasks and saying the Next Action is the next task in the ordered list?
- +1 for ordering todos within project as simple but practical workaround
- +1 for not very complicated tickets dependency in the near future(1.7.1? 1.8?)
- +1 for simple workflows in the future (2.0?)
if there are tickets on any of this issues shouldn't their id's be here for a reference?:)
linking to wiki page: http://www.getontracks.org/wiki/ActionDependencies
on 2009-05-17 13:30 *
By Yoichi Hirai
I thought
*# Then, the dependent todo is hidden.
*# When the todo E is done, the dependent todo appears again.
and I implemented that at
http://github.com/pirapira/tracks/tree/master
The implimentation is prototypical in that
Just sharing what I did because it seems relevant.
- for most people, it suffices to allow a todo to depend on at most one todo.
- It's handy to add dependency using drag and drop.
*# Then, the dependent todo is hidden.
*# When the todo E is done, the dependent todo appears again.
and I implemented that at
http://github.com/pirapira/tracks/tree/master
The implimentation is prototypical in that
- the dependent todos do not appear in deffered todo
- when the dependent todos appear again, the page is reloaded.
- though when it is hidden, javascript runs.
- only confirmed working with Firefox 3.0.10 (Linux).
- not integrated with the API.
Just sharing what I did because it seems relevant.
file:c6PjaOqWer3RHTeJe5aVNr: Todo model fsm
Nice work Yoichi!
This is pretty close to what I had in mind. I really like adding dependencies through drag and drop!
Personally, I think that multiple dependencies are pretty common though, e.g.
write letter and buy stamps, then post letter
Without multiple dependencies we can only express
1. write letter, then buy stamps, then post letter or
2. write letter or buy stamps, then post letter
The first case hides the buy stamps action, which is possible to do while waiting for the letter to be finished, and the second case make the post letter action be visible if we buy stamps, together with the write letter action - and hiding non-actionable actions was what we were aiming for in the first place.
To be able to display the pending actions, my idea was to add a pending state in the Todo state machine.
!https://www.assembla.com/spaces/tracks-tickets/documents/c6PjaOqWer3RHTeJe5aVNr/download?filename=todo_fsm.png!
This is pretty close to what I had in mind. I really like adding dependencies through drag and drop!
Personally, I think that multiple dependencies are pretty common though, e.g.
write letter and buy stamps, then post letter
Without multiple dependencies we can only express
1. write letter, then buy stamps, then post letter or
2. write letter or buy stamps, then post letter
The first case hides the buy stamps action, which is possible to do while waiting for the letter to be finished, and the second case make the post letter action be visible if we buy stamps, together with the write letter action - and hiding non-actionable actions was what we were aiming for in the first place.
To be able to display the pending actions, my idea was to add a pending state in the Todo state machine.
!https://www.assembla.com/spaces/tracks-tickets/documents/c6PjaOqWer3RHTeJe5aVNr/download?filename=todo_fsm.png!
on 2009-05-17 17:44 *
By Yoichi Hirai
Thank you for appreciation of my work, Henrik.
Also, I agree that allowing multiple predecessors is more expressive.
Let me show the reason I thought single dependency suffices:
Also, I agree that allowing multiple predecessors is more expressive.
Let me show the reason I thought single dependency suffices:
- make three todos: WRITE, STAMPS and POST
- add POST depends on STAMPS: now you can see WRITE and STAMPS.
- (the reason: subjective) since POST is hidden and forgotten, adding another dependency to it is less important than managing other available next actions. I doubt that it is a good GTD practice to add a dependency to an already pending action.
There is nothing bad in having a more expressive model. However, if multiple dependency makes GUI more complicated than single dependency, the choice is worth consideration, or not?
The finite state machine: cool, both the picture quality and the idea. And I think it is good for simplicity that deferred action cannot get pending.
I do think that is a great improvement with single dependencies, and coding multiple dependencies will be somewhat more complex. However, I don't believe that the user interface needs to get that much more complex. Drag and drop would still be a nice way to add new dependencies to pending items, if they were displayed among among the deferreds, or in its own block.
Yoichi, with your permission I will cross-post this discussion to the mailing list, to hopefully get more views on the topic.
Yoichi, with your permission I will cross-post this discussion to the mailing list, to hopefully get more views on the topic.
file:dD_W-EIQar3QxReJe5afGb: Updated todo FSM