This workflow engine of yours seems to expect a very predictable and linear sequence of actions. What happens if the user clicks "log out" after user.tutorial();
It's basically making an already easy case easier.
Even linear workflows can handle conditional execution, you just lift the condition into a value, like into a Maybe/Option type, and later stages only execute if there's a value.
Or if you want to be more literal, declare a specific type:
type Authenticated(User) = No | Yes(User)
And each stage of your workflow matches on Authenticated.Yes, and so only executes if the user is authenticated. That's basically what any system does internally, this just makes that implicit behaviour explicit.
I assume you would have conditions for each workflow which are then pattern matched to user behaviour to see if that workflow is relevant.
You still have a globally defined happy path of coordination but your overarching application logic isn't spread everywhere but contained in one place.
The preconditions for the logout would trigger a different workflow.
I am the author of additive-gui, which is based around the idea that you provide all the rules of the GUI and the computer works it out - what applies when. Additive GUIs loosely models dataflow between components and layout.
It's basically making an already easy case easier.