[PATCH 22/26] lucid: report async task progress

Daniel Gomez <[email protected]> Tue, 19 May 2026 15:28:19 +0200
Newsgroups dev.linux.lists.kdevops
Message-ID <[email protected]>
From: Daniel Gomez <[email protected]>

Async tasks (those declared with an async and poll value) fire a
separate family of callback hooks: v2_runner_on_async_poll during
each polling interval and v2_runner_on_async_ok or
v2_runner_on_async_failed when the job terminates. CallbackBase
leaves all three as no-ops, and the normal v2_runner_on_ok and
v2_runner_on_failed never fire for async jobs, so lucid's live
display would show an async task as perpetually running from the
moment it was dispatched.

Routing async_ok and async_failed through the same _handle_result
path as sync tasks collapses the two worlds: the running-task map
clears, the dynamic display updates, failed_items and logging
behave identically, and nothing downstream has to special-case
async completion. Poll events land in their own handler and are
gated at -vv because well-configured polling loops fire many times
per job and would otherwise drown the screen. The log always
captures them under an ASYNC POLL: prefix so the polling cadence is
recoverable post-hoc.

Generated-by: Claude AI
Signed-off-by: Daniel Gomez <[email protected]>
---
 callback_plugins/lucid.py | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/callback_plugins/lucid.py b/callback_plugins/lucid.py
index a8d0a9c1..83532e03 100644
--- a/callback_plugins/lucid.py
+++ b/callback_plugins/lucid.py
@@ -648,6 +648,46 @@ class CallbackModule(CallbackBase):
         """Host unreachable"""
         self._handle_result(result, "unreachable")
 
+    def v2_runner_on_async_poll(self, result):
+        """Async task polled and has not yet finished.
+
+        Async tasks fire this hook repeatedly between dispatch and
+        completion. It is noisy by design, so we only emit on screen
+        at -vv or higher and always route through the log with a
+        consistent ASYNC POLL: prefix so post-hoc debugging can
+        reconstruct the polling cadence.
+        """
+        host = result._host.name
+        ajid = result._result.get("ansible_job_id", "")
+        msg = f"ASYNC POLL: [{host}] job {ajid}"
+        if self._display.verbosity >= 2:
+            with self.output_lock:
+                self._display.display(msg, color=C.COLOR_VERBOSE)
+        self._write_to_log(msg)
+
+    def v2_runner_on_async_ok(self, result):
+        """Async task finished successfully.
+
+        Routing through _handle_result treats async completion the
+        same as a sync ok: the running-task map clears, the dynamic
+        display updates, and failed_items / logging behave
+        identically. Without this, lucid would show async tasks as
+        perpetually running because the normal on_ok never fires
+        for them.
+        """
+        changed = result._result.get("changed", False)
+        status = "changed" if changed else "ok"
+        self._handle_result(result, status)
+
+    def v2_runner_on_async_failed(self, result):
+        """Async task finished with a failure.
+
+        Same routing as on_async_ok, but lands in the failed branch
+        of _handle_result so the dynamic display freezes and output
+        is surfaced the way a sync failure would be.
+        """
+        self._handle_result(result, "failed")
+
     def v2_runner_item_on_ok(self, result):
         """Loop item succeeded — log per-item command and output"""
         self._log_item_result(result)

-- 
2.53.0