Workflow implementation
=======================

On the Python side, a workflow is orchestrated by a subclass of
``Workflow``, which derives from ``BaseTask`` and has its own subclass
hierarchy.

When instantiating a "Workflow", a new ``WorkRequest`` is created with:

* ``task_type`` set to ``"workflow"``
* ``task_name`` pointing to the ``Workflow`` subclass used to orchestrate
* ``task_data`` set to the workflow parameters instantiated from the template (or from the parent workflow)

This ``WorkRequest`` acts as the root of the ``WorkRequest`` hierarchy
for the running workflow.

The ``Workflow`` class runs on the server with full database access
and is in charge of:

* on instantiation, laying out an execution plan under the form of a
  directed acyclic graph of newly created ``WorkRequest`` instances.
* analyzing the results of any completed ``WorkRequest`` in the graph
* possibly extending/modifying the graph after this analysis

``WorkRequest`` elements in a Workflow can only depend among each other, and
cannot have dependencies on ``WorkRequest`` elements outside the workflow.
They may depend on work requests in other sub-workflows that are part of the
same root workflow.

All the child work requests start in the ``blocked`` status using the ``deps``
unblock strategy. When the Workflow ``WorkRequest`` is ready to run, all the
child ``WorkRequest`` elements that don't have any further dependencies can
immediately start.
