> What is tryLock() doing

Its purpose here is to make sure the TaskGroup (which represents a running transaction) doesn't close early. If we didn't tryLock(nextTask) here then the unlock(task) below could close off the transaction. This would not be necessary if we locked tasks when we add them to the queue instead of when we poll them from the queue. That could be worth changing since I'm not sure we get any benefit from deferring locking to poll.

> why can't the seenNextTasks be done via the add() method?

The purpose of seenNextTasks is to make nextTasks messages from workers idempotent, and the purpose of it being a map of parent -> children instead of just a set of children is so it can be easily cleaned up when the parent task completes. Assuming you buy that those two properties are useful, it can't go in add() since add only knows about the task being added and not what its parent is.