Make DAGs support and run on snapshots

Title says it all
I think the validation here actually gets removed and takes place entirely in `create_snapshot`. This is where I think it's best for all the checks to live.

First, to clarify: the canonical DAG is valid if it is acyclic *before* sub-Job expansion. This is checked whenever you add an edge, so the canonical DAG is guaranteed to be valid when you start a Job.

The snapshot DAG is valid if it is acyclic *after* sub-Job expansion. This needs to be checked every time you start a Job because its sub-Jobs can change (or even get deleted).

Therefore, on Job start, I think this should happen in sequence:

1. Make sure it's okay for the Job to start right now.
2. Create a snapshot from the canonical DAG that you know is valid because an invalid one could not have been saved. This would use the expansion algorithm we talked about that avoids infinite loops. This function either sets `self.snapshot` successfully or raises a validation exception which bubbles up to `start`. The validation happens internally in `create_snapshot`, so we do not need to do another validation check inside `start`.
3. Go on your merry way.