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.
Constants
The raw: spellings (bare and qualified) - the one definition of the
set, shared with TaskExecutor's own raw handling. A task's
action_name keeps the name as written (raw) while module_name is
the alias-resolution target (ansible.builtin.shell), so callers check
action_name || module_name.
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?" - 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 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.
raw: is an ACTION plugin in real Ansible, not a module: it hands the
command text to the connection with a pty and returns the connection's
own capture - {rc, stdout, stdout_lines, stderr, stderr_lines} with no
command-module keys, failing with "non-zero return code" rather than a
module msg. TaskExecutor::execute_task_once rebuilds exactly that
(shape_raw_result, raw_remote_pty_shape, raw_spawn_failure_shape) for a
solo raw task; a batch script has no pty to capture through and its
members skip that shaping entirely, so a grouped raw reported the shell
plugin's own cmd/start/end/delta result and "Module failed: The command
exited with a non-zero return code." where real printed "Action failed:
non-zero return code" (issue #960, adarnimrod.openwrt-bootstrap's
raw: opkg update). Every other raw fidelity fix (issues #490/#631/
#775/#820 - pty stream merge, CRLF, the ssh close notice, TERM) lives
on that solo path too, so raw keeps its own group and always takes it.