From d54147cfb4a5307fba6a9d75b761bce9062b8a6c Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Mon, 16 Mar 2026 21:59:52 -0700 Subject: [PATCH] fix: DisplayStage dependency and render pipeline data flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical fix for display rendering: 1. DisplayStage Missing Dependency - DisplayStage had empty dependencies, causing it to execute before render - No data was reaching the display output - Fix: Add 'render.output' dependency so display comes after render stages - Now proper execution order: source → render → display 2. create_default_pipeline Missing Render Stage - Default pipeline only had source and display, no render stage between - Would fail validation with 'Missing render.output' capability - Fix: Add SourceItemsToBufferStage to convert items to text buffer - Now complete data flow: source → render → display 3. Updated Test Expectations - test_display_stage_dependencies now expects 'render.output' dependency Result: Display backends (pygame, terminal, websocket) now receive proper rendered text buffers and can display output correctly. Tests: All 502 tests passing --- engine/pipeline/adapters.py | 2 +- engine/pipeline/controller.py | 8 +++++++- tests/test_adapters.py | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/engine/pipeline/adapters.py b/engine/pipeline/adapters.py index 4b96789..7a9ba3c 100644 --- a/engine/pipeline/adapters.py +++ b/engine/pipeline/adapters.py @@ -111,7 +111,7 @@ class DisplayStage(Stage): @property def dependencies(self) -> set[str]: - return set() + return {"render.output"} # Display needs rendered content def init(self, ctx: PipelineContext) -> bool: w = ctx.params.viewport_width if ctx.params else 80 diff --git a/engine/pipeline/controller.py b/engine/pipeline/controller.py index 6810a66..d72b7bd 100644 --- a/engine/pipeline/controller.py +++ b/engine/pipeline/controller.py @@ -520,7 +520,10 @@ def create_pipeline_from_params(params: PipelineParams) -> Pipeline: def create_default_pipeline() -> Pipeline: """Create a default pipeline with all standard components.""" from engine.data_sources.sources import HeadlinesDataSource - from engine.pipeline.adapters import DataSourceStage + from engine.pipeline.adapters import ( + DataSourceStage, + SourceItemsToBufferStage, + ) pipeline = Pipeline() @@ -528,6 +531,9 @@ def create_default_pipeline() -> Pipeline: source = HeadlinesDataSource() pipeline.add_stage("source", DataSourceStage(source, name="headlines")) + # Add render stage to convert items to text buffer + pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer")) + # Add display stage display = StageRegistry.create("display", "terminal") if display: diff --git a/tests/test_adapters.py b/tests/test_adapters.py index 32680fd..3bd7024 100644 --- a/tests/test_adapters.py +++ b/tests/test_adapters.py @@ -97,10 +97,10 @@ class TestDisplayStage: assert "display.output" in stage.capabilities def test_display_stage_dependencies(self): - """DisplayStage has no dependencies.""" + """DisplayStage depends on render.output.""" mock_display = MagicMock() stage = DisplayStage(mock_display, name="terminal") - assert stage.dependencies == set() + assert "render.output" in stage.dependencies def test_display_stage_init(self): """DisplayStage.init() calls display.init() with dimensions."""