def flatten_tree(tree: dict, full: bool = False) -> dict:
    """
    Flatten an execution tree to make it easier to read.

    Task trees are often a single node nested several levels deep. These trees may be collapsed
    into a list. The execution order is the same, but it's easier for a human to read.

    Before:
        - foo
          - bar
            - xoo

    After:
        - xoo
        - bar
        - foo


    Before:
        - foo
          - xar
          - bar
            - xoo

    After:
        - foo
          - xar
          - xoo
          - bar

    :param tree: Tree to flatten
    :param full: Flatten tree into single list
    :return: flattened task list
    """

    def flatten_node(node: dict) -> list:
        """
        Flatten a single node. Always return a list for consistency, even when returning a single
        node.

        :param node:
        :param parent: parent task list to collapse into
        :return: flattened node
        """
        node = node.copy()
        num_dependencies = len(node["dependencies"])
        if num_dependencies == 0:
            # no dependencies: nothing to flatten, return as-is
            return [node]

        elif full or num_dependencies == 1:
            # flatten dependencies: flatten into single list that includes parent & child
            flattened = []
            for dependency in node["dependencies"]:
                flattened_child = flatten_node(dependency)
                flattened.extend(flattened_child)

                # clear dependencies, since they are now siblings
                # this node is added last since it runs after dependencies
                node["dependencies"] = []
                flattened.append(node)

            return flattened

        else:
            # multiple dependencies: do not flatten into parent.
            #
            # Any dependencies that are flattened need to be merged with other dependencies.
            # Dependency nodes should either be a single node, or a list of nodes
            dependencies = []
            for dependency in node["dependencies"]:
                flattened = flatten_node(dependency)
                dependencies.extend(flattened)

            node["dependencies"] = dependencies
            return [node]

    root = flatten_node(tree)
    if len(root) > 1:
        # if root's dependencies were flattened into it, then the returned list will have all of
        # those dependencies. Create a new root node to contain them all. This keeps the structure
        # consistent-ish for downstream consumers. They still have to special case this node, but
        # it should be a little simpler since all nodes are of a similar shape
        return {"name": None, "dependencies": root}
    else:
        # a single node, unpack it and return as root.
        return root[0]