At what point in the Gradle lifecycle is the 'build' folder created, and how should a custom task ensure that it has already happened?

It is in fact the responsibility of the task. Only tasks should create output, therefore the task is responsible for creating the output directory, should it not exist. ‘Project.getBuildDir()’ is more convention than anything, since a task could just as well place its output outside of that directory.

The simplest way to accomplish this is by leveraging the ‘@OutputFile’ or ‘@OutputDirectory’. Using either annotation on your task properties will automatically create the directory (or parent directory) as required.