Skip to content

Commit 309564d

Browse files
authored
feat(workflows): add process-todo-create-issues workflow template (#11)
Add a new workflow template that recursively processes todo items from a Markdown file and creates GitHub issues for each item. Supports multiple todo formats (header, checkbox, simple list) and maintains state for progress tracking.
2 parents f0cc32b + a89844f commit 309564d

4 files changed

Lines changed: 506 additions & 23 deletions

File tree

src/workflows_mcp/engine/executors_workflow.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ async def execute( # type: ignore[override]
142142
# 4. Create WorkflowRunner and execute child workflow
143143
from .workflow_runner import WorkflowRunner
144144

145-
runner = WorkflowRunner(checkpoint_config=None) # No checkpointing for nested workflows
145+
# No checkpointing for nested workflows - parent handles all checkpointing
146+
runner = WorkflowRunner(checkpoint_config=None)
146147

147148
# Create child execution context
148149
child_context = exec_context.create_child_context(
@@ -288,6 +289,7 @@ async def resume(
288289
# 3. Create WorkflowRunner and resume child workflow
289290
from .workflow_runner import WorkflowRunner
290291

292+
# No checkpointing for nested workflows - parent handles all checkpointing
291293
runner = WorkflowRunner(checkpoint_config=None)
292294

293295
# Resume child workflow

src/workflows_mcp/engine/workflow_runner.py

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,15 @@ def __init__(
5757
Initialize workflow runner.
5858
5959
Args:
60-
checkpoint_config: Optional checkpoint configuration
60+
checkpoint_config: Optional checkpoint configuration.
61+
If None, checkpointing is disabled (default for nested workflows and MCP tools).
62+
Pass CheckpointConfig() explicitly to enable checkpointing.
6163
"""
62-
self.checkpoint_config = checkpoint_config or CheckpointConfig()
64+
# Fix: When None is passed, disable checkpointing instead of using defaults
65+
# This ensures nested workflows don't create checkpoints (executors_workflow.py:145)
66+
self.checkpoint_config = (
67+
checkpoint_config if checkpoint_config is not None else CheckpointConfig(enabled=False)
68+
)
6369
self.orchestrator = BlockOrchestrator()
6470

6571
async def execute(
@@ -916,25 +922,54 @@ async def _resume_workflow_internal(
916922
raise ValueError(f"Paused block not found: {paused_block_id}")
917923
block_def = BlockDefinition(**checkpoint.block_definitions[paused_block_id])
918924

919-
# Resume paused block
920-
await self._resume_paused_block(
921-
block_id=paused_block_id,
922-
block_def=block_def,
923-
exec_context=exec_context,
924-
response=response,
925-
pause_metadata=checkpoint.pause_metadata or {},
926-
wave_idx=checkpoint.current_wave_index,
927-
execution_order=len(completed_blocks),
928-
)
929-
930-
# Verify resumed block succeeded
931-
resumed_block_metadata = exec_context.get_block_metadata(paused_block_id)
932-
if resumed_block_metadata and resumed_block_metadata.status.is_failed():
933-
raise ValueError(
934-
f"Failed to resume block '{paused_block_id}': {resumed_block_metadata.message}"
925+
try:
926+
# Resume paused block
927+
await self._resume_paused_block(
928+
block_id=paused_block_id,
929+
block_def=block_def,
930+
exec_context=exec_context,
931+
response=response,
932+
pause_metadata=checkpoint.pause_metadata or {},
933+
wave_idx=checkpoint.current_wave_index,
934+
execution_order=len(completed_blocks),
935935
)
936936

937-
completed_blocks.append(paused_block_id)
937+
# Verify resumed block succeeded
938+
resumed_block_metadata = exec_context.get_block_metadata(paused_block_id)
939+
if resumed_block_metadata and resumed_block_metadata.status.is_failed():
940+
error_msg = f"Failed to resume block '{paused_block_id}'"
941+
error_detail = resumed_block_metadata.message
942+
raise ValueError(f"{error_msg}: {error_detail}")
943+
944+
completed_blocks.append(paused_block_id)
945+
946+
except ExecutionPaused as e:
947+
# Block paused again during resume - save checkpoint and re-raise
948+
if context:
949+
checkpoint_id = await self._save_checkpoint(
950+
workflow=workflow,
951+
runtime_inputs=checkpoint.runtime_inputs,
952+
context=context,
953+
exec_context=exec_context,
954+
completed_blocks=completed_blocks,
955+
current_wave_index=checkpoint.current_wave_index,
956+
execution_waves=checkpoint.execution_waves,
957+
pause_exception=e,
958+
)
959+
960+
# Update exception with saved checkpoint_id and workflow_name, then re-raise
961+
raise ExecutionPaused(
962+
prompt=e.prompt,
963+
checkpoint_data={
964+
**e.checkpoint_data,
965+
"checkpoint_id": checkpoint_id,
966+
"workflow_name": workflow.name,
967+
},
968+
execution=e.execution,
969+
)
970+
else:
971+
# No checkpoint store available - re-raise without checkpoint
972+
raise
938973

939974
# Execute remaining waves
940975
await self._execute_waves_from(

0 commit comments

Comments
 (0)