Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions workflows/workflow_use/healing/tests/test_variable_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Tests for the variable marker file utilities."""

import json

import yaml

from workflow_use.healing.variable_utils import process_workflow_file_with_markers

WORKFLOW = {
'name': 'Test Workflow',
'description': 'Test',
'version': '1.0',
'steps': [
{'type': 'input', 'target_text': 'Email', 'value': 'VAR:user_email:test@example.com'},
{'type': 'extract', 'extractionGoal': 'Extract the result'},
],
'input_schema': [],
}


def test_process_yaml_workflow_file(tmp_path):
"""A .workflow.yaml file, the format the storage service writes, is processed in place."""
path = tmp_path / 'test.workflow.yaml'
path.write_text(yaml.dump(WORKFLOW, sort_keys=False))

process_workflow_file_with_markers(path)

saved = yaml.safe_load(path.read_text())
assert saved['steps'][0]['value'] == '{user_email}'
assert [inp['name'] for inp in saved['input_schema']] == ['user_email']


def test_process_json_workflow_file(tmp_path):
"""A JSON workflow file still works and stays JSON."""
path = tmp_path / 'test.workflow.json'
path.write_text(json.dumps(WORKFLOW))

process_workflow_file_with_markers(path)

saved = json.loads(path.read_text())
assert saved['steps'][0]['value'] == '{user_email}'


def test_yaml_input_to_json_output(tmp_path):
"""The output format follows the output path extension."""
source = tmp_path / 'test.workflow.yaml'
source.write_text(yaml.dump(WORKFLOW, sort_keys=False))
output = tmp_path / 'out.json'

process_workflow_file_with_markers(source, output)

saved = json.loads(output.read_text())
assert saved['steps'][0]['value'] == '{user_email}'
13 changes: 9 additions & 4 deletions workflows/workflow_use/healing/variable_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from pathlib import Path
from typing import Optional

import yaml

from workflow_use.healing.variable_extractor import VariableExtractor
from workflow_use.schema.views import WorkflowDefinitionSchema

Expand Down Expand Up @@ -57,19 +59,22 @@ def process_workflow_file_with_markers(
input_path = Path(input_path)
output_path = Path(output_path) if output_path else input_path

# Load the workflow
# Load the workflow. YAML is a superset of JSON, so this reads both formats.
with open(input_path, 'r') as f:
workflow_data = json.load(f)
workflow_data = yaml.safe_load(f)

@cubic-dev-ai cubic-dev-ai Bot Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This loader changes valid YAML string scalars such as yes and on into booleans before schema validation, so hand-authored workflows can fail on required string fields. Use a YAML 1.2-compatible scalar resolver or normalize these fields before constructing WorkflowDefinitionSchema.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At workflows/workflow_use/healing/variable_utils.py, line 64:

<comment>This loader changes valid YAML string scalars such as `yes` and `on` into booleans before schema validation, so hand-authored workflows can fail on required string fields. Use a YAML 1.2-compatible scalar resolver or normalize these fields before constructing `WorkflowDefinitionSchema`.</comment>

<file context>
@@ -57,19 +59,22 @@ def process_workflow_file_with_markers(
+	# Load the workflow. YAML is a superset of JSON, so this reads both formats.
 	with open(input_path, 'r') as f:
-		workflow_data = json.load(f)
+		workflow_data = yaml.safe_load(f)
 
 	workflow = WorkflowDefinitionSchema(**workflow_data)
</file context>
Fix with cubic


workflow = WorkflowDefinitionSchema(**workflow_data)

# Extract variables from markers
extractor = VariableExtractor()
updated_workflow, extracted_inputs = extractor.process_workflow_with_markers(workflow)

# Save the updated workflow
# Save the updated workflow in the format that matches the output file extension
with open(output_path, 'w') as f:
json.dump(updated_workflow.model_dump(), f, indent=2)
if output_path.suffix.lower() in ('.yaml', '.yml'):
yaml.dump(updated_workflow.model_dump(mode='json'), f, default_flow_style=False, sort_keys=False)
else:
json.dump(updated_workflow.model_dump(), f, indent=2)

@cubic-dev-ai cubic-dev-ai Bot Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: YAML-to-JSON conversion still fails for valid YAML-native values such as unquoted dates in verification_checks or extra step fields. Use model_dump(mode='json') in the JSON branch so these values are converted before json.dump.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At workflows/workflow_use/healing/variable_utils.py, line 77:

<comment>YAML-to-JSON conversion still fails for valid YAML-native values such as unquoted dates in `verification_checks` or extra step fields. Use `model_dump(mode='json')` in the JSON branch so these values are converted before `json.dump`.</comment>

<file context>
@@ -57,19 +59,22 @@ def process_workflow_file_with_markers(
+		if output_path.suffix.lower() in ('.yaml', '.yml'):
+			yaml.dump(updated_workflow.model_dump(mode='json'), f, default_flow_style=False, sort_keys=False)
+		else:
+			json.dump(updated_workflow.model_dump(), f, indent=2)
 
 	print(f'Processed workflow: {input_path}')
</file context>
Suggested change
json.dump(updated_workflow.model_dump(), f, indent=2)
json.dump(updated_workflow.model_dump(mode='json'), f, indent=2)
Fix with cubic


print(f'Processed workflow: {input_path}')
if extracted_inputs:
Expand Down