If a graph walk task fails, stop the walk. (#4254)
### Description
Backstory: I was investigating what needs to be done to support unifying
our output to support grouping log output. I encountered an `os.Exit`
sitting in run, and while attempting to address that, discovered that we
don't fully stop on error even when we mean to. We stop executing
_scripts_ but we continue to walk the graph, meaning that we can trigger
cache restorations.
This PR:
- Adds a short-circuiting mechanism to our graph walk so that tasks
started after the failed tasks are skipped. Note that tasks started
concurrently with the failed task may still run.
- Remove the `os.Exit` for the case where we fail to do the setup work
to capture task outputs
- Adds a test for short-circuiting using a custom visitor to
deterministically inject an error into graph traversal
### Testing Instructions
New test in `engine_test.go` demonstrates short-circuiting by injecting
an error into graph traversal.
I opted for a unit test in this case because using a failing script in
an integration test will not trigger the broken behavior. We shut down
the script-running facility once a script fails. The error needs to
occur outside of the user's script to demonstrate the bug, hence a
custom graph visitor.
---------
Co-authored-by: Mehul Kar <mehul.kar@vercel.com>