"""Actionables."""
 from __future__ import annotations
+from collections import namedtuple
 from typing import Any
 from sqlite3 import Row
 from plomtask.db import DatabaseConnection, BaseModel
                                  HandledException)
 
 
+TodoStepsNode = namedtuple('TodoStepsNode',
+                           ('item', 'is_todo', 'children', 'seen'))
+
+
 class Todo(BaseModel, ConditionsRelations):
     """Individual actionable."""
 
                 for condition in self.disables:
                     condition.is_active = False
 
+    def get_step_tree(self, seen_todos: set[int],
+                      seen_conditions: set[int]) -> TodoStepsNode:
+        """Return tree of depended-on Todos and Conditions."""
+
+        def make_node(step: Todo | Condition) -> TodoStepsNode:
+            assert isinstance(step.id_, int)
+            is_todo = isinstance(step, Todo)
+            children = []
+            if is_todo:
+                assert isinstance(step, Todo)
+                seen = step.id_ in seen_todos
+                seen_todos.add(step.id_)
+                potentially_enabled = set()
+                for child in step.children:
+                    for condition in child.enables:
+                        potentially_enabled.add(condition)
+                    children += [make_node(child)]
+                for condition in [c for c in step.conditions
+                                  if (not c.is_active)
+                                  and (c not in potentially_enabled)]:
+                    children += [make_node(condition)]
+            else:
+                assert isinstance(step, Condition)
+                seen = step.id_ in seen_conditions
+                seen_conditions.add(step.id_)
+            return TodoStepsNode(step, is_todo, children, seen)
+
+        node = make_node(self)
+        return node
+
     def add_child(self, child: Todo) -> None:
         """Add child to self.children, guard against recursion"""
         def walk_steps(node: Todo) -> None:
 
 """Test Todos module."""
 from tests.utils import TestCaseWithDB, TestCaseWithServer
-from plomtask.todos import Todo
+from plomtask.todos import Todo, TodoStepsNode
 from plomtask.processes import Process
 from plomtask.conditions import Condition
 from plomtask.exceptions import (NotFoundException, BadFormatException,
         self.cond1.is_active = True
         todo_2.is_done = True
 
+    def test_Todo_step_tree(self) -> None:
+        """Test self-configuration of TodoStepsNode tree for Day view."""
+        assert isinstance(self.cond1.id_, int)
+        assert isinstance(self.cond2.id_, int)
+        todo_1 = Todo(None, self.proc, False, self.date1)
+        todo_1.save(self.db_conn)
+        assert isinstance(todo_1.id_, int)
+        # test minimum
+        tree_expected = TodoStepsNode(todo_1, True, [], False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test non_emtpy seen_todo does something
+        tree_expected = TodoStepsNode(todo_1, True, [], True)
+        self.assertEqual(todo_1.get_step_tree({todo_1.id_}, set()),
+                         tree_expected)
+        # test child shows up
+        todo_2 = Todo(None, self.proc, False, self.date1)
+        todo_2.save(self.db_conn)
+        assert isinstance(todo_2.id_, int)
+        todo_1.add_child(todo_2)
+        node_todo_2 = TodoStepsNode(todo_2, True, [], False)
+        tree_expected = TodoStepsNode(todo_1, True, [node_todo_2], False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test child shows up with child
+        todo_3 = Todo(None, self.proc, False, self.date1)
+        todo_3.save(self.db_conn)
+        assert isinstance(todo_3.id_, int)
+        todo_2.add_child(todo_3)
+        node_todo_3 = TodoStepsNode(todo_3, True, [], False)
+        node_todo_2 = TodoStepsNode(todo_2, True, [node_todo_3], False)
+        tree_expected = TodoStepsNode(todo_1, True, [node_todo_2], False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test same todo can be child-ed multiple times at different locations
+        todo_1.add_child(todo_3)
+        node_todo_4 = TodoStepsNode(todo_3, True, [], True)
+        tree_expected = TodoStepsNode(todo_1, True,
+                                      [node_todo_2, node_todo_4], False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test condition shows up
+        todo_1.set_conditions(self.db_conn, [self.cond1.id_])
+        node_cond_1 = TodoStepsNode(self.cond1, False, [], False)
+        tree_expected = TodoStepsNode(todo_1, True,
+                                      [node_todo_2, node_todo_4, node_cond_1],
+                                      False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test second condition shows up
+        todo_2.set_conditions(self.db_conn, [self.cond2.id_])
+        node_cond_2 = TodoStepsNode(self.cond2, False, [], False)
+        node_todo_2 = TodoStepsNode(todo_2, True,
+                                    [node_todo_3, node_cond_2], False)
+        tree_expected = TodoStepsNode(todo_1, True,
+                                      [node_todo_2, node_todo_4, node_cond_1],
+                                      False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test second condition is not hidden if fulfilled by non-sibling
+        todo_1.set_enables(self.db_conn, [self.cond2.id_])
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+        # test second condition is hidden if fulfilled by sibling
+        todo_3.set_enables(self.db_conn, [self.cond2.id_])
+        node_todo_2 = TodoStepsNode(todo_2, True, [node_todo_3], False)
+        tree_expected = TodoStepsNode(todo_1, True,
+                                      [node_todo_2, node_todo_4, node_cond_1],
+                                      False)
+        self.assertEqual(todo_1.get_step_tree(set(), set()), tree_expected)
+
     def test_Todo_singularity(self) -> None:
         """Test pointers made for single object keep pointing to it."""
         todo = Todo(None, self.proc, False, self.date1)
             self.check_post(form_data, '/todo?id=1', status, '/')
             self.db_conn.cached_todos = {}
             return Todo.by_date(self.db_conn, '2024-01-01')[0]
+        # test minimum
         form_data = {'title': '', 'description': '', 'effort': 1}
         self.check_post(form_data, '/process', 302, '/')
         form_data = {'comment': '', 'new_todo': 1}
         self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
+        # test posting to bad URLs
         form_data = {}
         self.check_post(form_data, '/todo=', 404)
         self.check_post(form_data, '/todo?id=', 400)
         self.check_post(form_data, '/todo?id=FOO', 400)
         self.check_post(form_data, '/todo?id=0', 404)
+        # test posting naked entity
         todo1 = post_and_reload(form_data)
         self.assertEqual(todo1.children, [])
         self.assertEqual(todo1.parents, [])
         self.assertEqual(todo1.is_done, False)
+        # test posting doneness
         form_data = {'done': ''}
         todo1 = post_and_reload(form_data)
         self.assertEqual(todo1.is_done, True)
+        # test implicitly posting non-doneness
         form_data = {}
         todo1 = post_and_reload(form_data)
         self.assertEqual(todo1.is_done, False)
+        # test malformed adoptions
         form_data = {'adopt': 'foo'}
         self.check_post(form_data, '/todo?id=1', 400)
         form_data = {'adopt': 1}
         self.check_post(form_data, '/todo?id=1', 400)
         form_data = {'adopt': 2}
         self.check_post(form_data, '/todo?id=1', 404)
+        # test posting second todo of same process
         form_data = {'comment': '', 'new_todo': 1}
         self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
+        # test todo 1 adopting todo 2
         form_data = {'adopt': 2}
         todo1 = post_and_reload(form_data)
         todo2 = Todo.by_date(self.db_conn, '2024-01-01')[1]
         self.assertEqual(todo1.parents, [])
         self.assertEqual(todo2.children, [])
         self.assertEqual(todo2.parents, [todo1])
+        # test failure of re-adopting same child
         self.check_post(form_data, '/todo?id=1', 400, '/')
+        # test todo1 cannot be set done with todo2 not done yet
         form_data = {'done': ''}
         todo1 = post_and_reload(form_data, 400)
         self.assertEqual(todo1.is_done, False)