Krikri::TaskBatcher
Pure planning logic for task batching (on by default; --no-batching disables it) - groups a flat task list into consecutive "batchable runs" with no I/O, no host/vars dependency, and no knowledge of connection type. TaskExecutor decides separately (per host, at run time) whether a given run of size >= 1 is actually worth/eligible for a real batched SSH round trip; a size-1 run here just means "this task can't extend a run on either side" and behaves exactly like today's one-task-at-a-time path.
A task can only safely share a batch with its neighbors if nothing about executing it needs data that only exists once an earlier task in the SAME run has actually executed remotely - see the reasoning behind each condition below.
Class methods
Groups tasks (one flat list - a play's top-level tasks, or one block:/rescue:/always:'s own nested list; this does not recurse into block_tasks/rescue_tasks/always_tasks itself - call it separately for each nested list you also want grouped) into consecutive runs, preserving order. Every task in tasks appears in exactly one returned group. aborts_on_notify (supplied by TaskExecutor, which owns the play's handler list) answers "if this task fires its notify:, will the run abort with HandlerNotFoundError?" - real Ansible stops right there, having run nothing after it, while a batch group would already have executed every remaining step in the same SSH round trip, applying real side effects on the target that real Ansible never applies. Such a task therefore ENDS its group (it may still run batched with what precedes it - the abort happens after it, not before). Only a notify: that is CERTAIN to abort breaks the run, so an ordinary playbook loses no batching at all.