[PATCH 06/26] lucid: defer play header until first task runs

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

Tag-filtered kdevops invocations such as ansible-playbook bringup.yml
--tags vagrant cause Ansible to walk every play in the playbook even
when a play contains no tasks matching the filter. Lucid printed the
PLAY: banner synchronously in v2_playbook_on_play_start which produced
a cascade of consecutive play banners with no tasks between them,
drowning out the one play the user actually cared about.

Hold the banner in self.pending_play_header at play-start time instead
of emitting it, and flush it from v2_playbook_on_task_start only when
a real task is about to run. Empty tag-filtered plays now emit no
on-screen banner at all, while plays that do execute tasks still show
the banner just before their first task. The log file continues to
record every play banner so the full play sequence remains traceable
for audit.

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

diff --git a/callback_plugins/lucid.py b/callback_plugins/lucid.py
index dac768a5..8be912c1 100644
--- a/callback_plugins/lucid.py
+++ b/callback_plugins/lucid.py
@@ -98,6 +98,7 @@ class CallbackModule(CallbackBase):
         self.current_task_name: str = ""
         self.current_task_hosts: list[str] = []
         self.play_hosts: list[str] = []  # All hosts in current play
+        self.pending_play_header: Optional[str] = None  # Deferred play banner
         self.current_play_name: str = ""  # Current play name for dynamic display
 
         # Dynamic display state
@@ -421,11 +422,19 @@ class CallbackModule(CallbackBase):
 
         msg = f"\nPLAY: {name} [{hosts_str}]"
         self.current_play_name = f"PLAY: {name} [{hosts_str}]"
-        self._display_message(msg, C.COLOR_HIGHLIGHT)
+        self.pending_play_header = msg
         self._write_to_log(msg)
 
+    def _flush_play_header(self):
+        """Print deferred play header on first task of the play"""
+        if self.pending_play_header:
+            if not self.dynamic_mode:
+                self._display_message(self.pending_play_header, C.COLOR_HIGHLIGHT)
+            self.pending_play_header = None
+
     def v2_playbook_on_task_start(self, task, is_conditional):
         """Task started"""
+        self._flush_play_header()
         self.current_task_name = task.get_name().strip()
         # Initialize with play hosts so display is stable from the start
         self.current_task_hosts = list(self.play_hosts) if self.play_hosts else []

-- 
2.53.0