code: func (m TaskManager) AbortTask(c context.Context, ctl task.Controller) error {
  	// This can happen if the invocation is aborted before it has even started.
  	// We don't have buildbucket build ID yet to cancel.
  	//
  	// There's a high chance that LaunchTask is executing concurrently somewhere.
  	// We let it finish peacefully, by not touching the invocation state at all
  	// and failing with a transient error instead. This avoids a collision on
  	// State modification. When such collision happens, results of LaunchTask
  	// (including a fresh build ID) are discarded (as the engine is unable to
  	// "merge" conflicting mutations from two different state transitions). This
  	// is really bad, since this process produces orphaned Buildbucket builds.
  	//
  	// So we pick a lesser evil and make AbortTask fail transiently while
  	// invocation is starting.
  	if status := ctl.State().Status; status.Initial() {
  		return errors.Reason("can't abort Buildbucket invocation in state %q", status).Tag(transient.Tag).Err()
  	}
  
  	// Grab build ID from the blob generated in LaunchTask.
  	taskData, err := readTaskData(ctl)
  	if err != nil {
  		ctl.State().Status = task.StatusFailed
  		return err
  	}
  
  	// Ask Buildbucket to kill this build.
  	return utils.WrapAPIError(m.withBuildbucket(c, ctl, func(c context.Context, bb *bbv1.Service) error {
  		_, err := bb.Cancel(taskData.BuildID, &bbv1.ApiCancelRequestBodyMessage{}).Context(c).Do()
  		return err
  	}))
  }
docs: // AbortTask is part of Manager interface.