I've tried to figure out why, but always end up concluding that the "tracking" could be easily done within the closure of the request that was sent. I'm clearly missing something here.
Why does Relay need a mutation ID to track the request/response?
You're thinking in terms of implementation. Relay is a JavaScript framework that implements the Relay specification. Under the specification, it is not a good idea to rely on something (closure) that a language may or may not support. Therefore, Relay Input Object Mutations Specification specifies
clientMutationId
to track the mutation.