The issue here is that the deleteIdentityLink() method will *try to* delete the identity-link in the database. Since the task isn't actually committed yet, the delete won't do anything. When the task-create call is done, they command-context will flush the pending entities that are created (process-instance, task and identity-links). Since the identity-links for candidate group are already queued for creation, they will be created.
This is an issue indeed and needs to be fixed. However, as a workaround (and perhaps a more valid approach for this kind of use case) I suggest using an expressions in the "activiti:candidateGroup" attribute, pointing to a bean in the context that decides what candidates are applicable for this task, at the time it's started. This will avoid having to use the listener and make use of the candidate-expression, where it was designed for. Eg. activiti:candidateGroups="${candidateChecker.getGroupsForTask(variableRelevantForDecision}".