From 663386819e6d0e6902ed37c67894a4b1f0db1c8b Mon Sep 17 00:00:00 2001 From: Muizz Lateef Date: Wed, 1 Apr 2026 21:58:34 +0100 Subject: [PATCH 1/3] fix: static type checker recognizes output_field as a field producer The schema extractor hardcoded {raw_response, content} for non-JSON-mode actions without a schema, ignoring the output_field config key. Downstream actions observing the named output_field were rejected with false StaticTypeErrors. Read output_field from the action config (defaulting to "raw_response") so the checker knows the actual field name the action will produce. --- .../static_analyzer/schema_extractor.py | 3 +- .../static_analyzer/test_schema_extractor.py | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/agent_actions/validation/static_analyzer/schema_extractor.py b/agent_actions/validation/static_analyzer/schema_extractor.py index 9f8f644..a89bf51 100644 --- a/agent_actions/validation/static_analyzer/schema_extractor.py +++ b/agent_actions/validation/static_analyzer/schema_extractor.py @@ -319,8 +319,9 @@ def _extract_llm_schema( json_mode = config.get("json_mode", get_default("json_mode")) if not json_mode: output.is_schemaless = True + output_field = config.get("output_field", get_default("output_field")) + output.schema_fields.add(output_field) output.schema_fields.add("content") - output.schema_fields.add("raw_response") return output.is_schemaless = True diff --git a/tests/validation/static_analyzer/test_schema_extractor.py b/tests/validation/static_analyzer/test_schema_extractor.py index 30ae631..91b3dde 100644 --- a/tests/validation/static_analyzer/test_schema_extractor.py +++ b/tests/validation/static_analyzer/test_schema_extractor.py @@ -157,6 +157,35 @@ def test_nested_schema_properties(self): # Should NOT have nested fields at top level assert "name" not in schema.available_fields + def test_output_field_produces_named_field(self): + """Test that output_field declares the named field in schema_fields.""" + config = { + "name": "classify", + "json_mode": False, + "output_field": "issue_type", + "prompt": "Classify this issue", + } + schema = self.extractor.extract_schema(config) + + assert "issue_type" in schema.schema_fields + assert "issue_type" in schema.available_fields + assert "content" in schema.available_fields + # raw_response should NOT appear — output_field replaces it + assert "raw_response" not in schema.available_fields + + def test_non_json_without_output_field_defaults_to_raw_response(self): + """Regression guard: non-JSON mode without output_field still produces raw_response.""" + config = { + "name": "agent", + "json_mode": False, + "prompt": "Generate something", + } + schema = self.extractor.extract_schema(config) + + assert "raw_response" in schema.available_fields + assert "content" in schema.available_fields + assert schema.is_schemaless + def test_hitl_agent_uses_canonical_hitl_schema(self): """Test HITL action uses the canonical HITL output schema.""" config = { From 7b975be055b5d8568b8bfdcc9928e194bc35bf7b Mon Sep 17 00:00:00 2001 From: Muizz Lateef Date: Wed, 1 Apr 2026 22:16:44 +0100 Subject: [PATCH 2/3] test: add integration test for output_field observability End-to-end test that a downstream action can observe an output_field-produced field through the full analyzer pipeline. Directly reproduces the original bug report scenario. --- .../test_workflow_static_analyzer.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/validation/static_analyzer/test_workflow_static_analyzer.py b/tests/validation/static_analyzer/test_workflow_static_analyzer.py index 708c0e6..c076cf4 100644 --- a/tests/validation/static_analyzer/test_workflow_static_analyzer.py +++ b/tests/validation/static_analyzer/test_workflow_static_analyzer.py @@ -892,6 +892,35 @@ def test_context_scope_reports_error_when_schema_load_fails(self): ) + def test_output_field_observable_by_downstream(self): + """Downstream action can observe an output_field-produced field.""" + workflow_config = { + "actions": [ + { + "name": "classify", + "json_mode": False, + "output_field": "issue_type", + "prompt": "Classify", + }, + { + "name": "route", + "depends_on": ["classify"], + "context_scope": {"observe": ["classify.issue_type"]}, + "prompt": "Route based on {{ action.classify.issue_type }}", + "schema": { + "type": "object", + "properties": {"team": {"type": "string"}}, + }, + }, + ], + } + + result = analyze_workflow(workflow_config) + + errors = [e for e in result.errors if "issue_type" in e.message] + assert not errors, f"False positive on output_field: {errors}" + + class TestPrimaryDependencyValidation: """Tests for primary_dependency validation.""" From 1314001182dfd3fccb966c911614a8cd38ff91b6 Mon Sep 17 00:00:00 2001 From: Muizz Lateef Date: Wed, 1 Apr 2026 22:33:00 +0100 Subject: [PATCH 3/3] style: remove extra blank line before integration test --- .../validation/static_analyzer/test_workflow_static_analyzer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/validation/static_analyzer/test_workflow_static_analyzer.py b/tests/validation/static_analyzer/test_workflow_static_analyzer.py index c076cf4..ff5c380 100644 --- a/tests/validation/static_analyzer/test_workflow_static_analyzer.py +++ b/tests/validation/static_analyzer/test_workflow_static_analyzer.py @@ -891,7 +891,6 @@ def test_context_scope_reports_error_when_schema_load_fails(self): "not found" in e.message or "could not be loaded" in e.message for e in context_errors ) - def test_output_field_observable_by_downstream(self): """Downstream action can observe an output_field-produced field.""" workflow_config = {