tapps-agents 3.5.38__py3-none-any.whl → 3.5.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/cleanup/__init__.py +7 -0
- tapps_agents/agents/cleanup/agent.py +445 -0
- tapps_agents/agents/enhancer/agent.py +2 -2
- tapps_agents/agents/implementer/agent.py +35 -13
- tapps_agents/agents/reviewer/agent.py +43 -10
- tapps_agents/agents/reviewer/scoring.py +59 -68
- tapps_agents/agents/reviewer/tools/__init__.py +24 -0
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -0
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -0
- tapps_agents/beads/__init__.py +11 -0
- tapps_agents/beads/hydration.py +213 -0
- tapps_agents/beads/specs.py +206 -0
- tapps_agents/cli/commands/cleanup_agent.py +92 -0
- tapps_agents/cli/commands/health.py +19 -3
- tapps_agents/cli/commands/simple_mode.py +842 -676
- tapps_agents/cli/commands/task.py +219 -0
- tapps_agents/cli/commands/top_level.py +13 -0
- tapps_agents/cli/main.py +15 -2
- tapps_agents/cli/parsers/cleanup_agent.py +228 -0
- tapps_agents/cli/parsers/top_level.py +1978 -1881
- tapps_agents/core/config.py +43 -0
- tapps_agents/core/init_project.py +3012 -2896
- tapps_agents/epic/markdown_sync.py +105 -0
- tapps_agents/epic/orchestrator.py +1 -2
- tapps_agents/epic/parser.py +427 -423
- tapps_agents/experts/adaptive_domain_detector.py +0 -2
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +15 -15
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +19 -44
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
- tapps_agents/health/checks/outcomes.py +134 -46
- tapps_agents/health/orchestrator.py +12 -4
- tapps_agents/hooks/__init__.py +33 -0
- tapps_agents/hooks/config.py +140 -0
- tapps_agents/hooks/events.py +135 -0
- tapps_agents/hooks/executor.py +128 -0
- tapps_agents/hooks/manager.py +143 -0
- tapps_agents/session/__init__.py +19 -0
- tapps_agents/session/manager.py +256 -0
- tapps_agents/simple_mode/code_snippet_handler.py +382 -0
- tapps_agents/simple_mode/intent_parser.py +29 -4
- tapps_agents/simple_mode/orchestrators/base.py +185 -59
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2667 -2642
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/workflow_suggester.py +37 -3
- tapps_agents/workflow/agent_handlers/implementer_handler.py +18 -3
- tapps_agents/workflow/cursor_executor.py +2196 -2118
- tapps_agents/workflow/direct_execution_fallback.py +16 -3
- tapps_agents/workflow/enforcer.py +36 -23
- tapps_agents/workflow/message_formatter.py +188 -0
- tapps_agents/workflow/parallel_executor.py +43 -4
- tapps_agents/workflow/parser.py +375 -357
- tapps_agents/workflow/rules_generator.py +337 -331
- tapps_agents/workflow/skill_invoker.py +9 -3
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/METADATA +9 -5
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/RECORD +62 -53
- tapps_agents/agents/analyst/SKILL.md +0 -85
- tapps_agents/agents/architect/SKILL.md +0 -80
- tapps_agents/agents/debugger/SKILL.md +0 -66
- tapps_agents/agents/designer/SKILL.md +0 -78
- tapps_agents/agents/documenter/SKILL.md +0 -95
- tapps_agents/agents/enhancer/SKILL.md +0 -189
- tapps_agents/agents/implementer/SKILL.md +0 -117
- tapps_agents/agents/improver/SKILL.md +0 -55
- tapps_agents/agents/ops/SKILL.md +0 -64
- tapps_agents/agents/orchestrator/SKILL.md +0 -238
- tapps_agents/agents/planner/story_template.md +0 -37
- tapps_agents/agents/reviewer/templates/quality-dashboard.html.j2 +0 -150
- tapps_agents/agents/tester/SKILL.md +0 -71
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/licenses/LICENSE +0 -0
- {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/top_level.txt +0 -0
|
@@ -257,8 +257,15 @@ class DirectExecutionFallback:
|
|
|
257
257
|
if skill_command.startswith("@"):
|
|
258
258
|
skill_command = skill_command[1:]
|
|
259
259
|
|
|
260
|
-
# Split into parts
|
|
261
|
-
|
|
260
|
+
# Split into parts, preserving quoted strings
|
|
261
|
+
# Use shlex.split() to handle complex quotes properly
|
|
262
|
+
# Use posix=True to properly handle quote removal (even on Windows)
|
|
263
|
+
try:
|
|
264
|
+
parts = shlex.split(skill_command, posix=True)
|
|
265
|
+
except ValueError as e:
|
|
266
|
+
# Provide helpful error message with truncated command
|
|
267
|
+
cmd_preview = skill_command[:200] + "..." if len(skill_command) > 200 else skill_command
|
|
268
|
+
raise ValueError(f"Invalid skill command (malformed quotes): {cmd_preview}") from e
|
|
262
269
|
|
|
263
270
|
if not parts:
|
|
264
271
|
raise ValueError(f"Invalid skill command: {skill_command}")
|
|
@@ -282,7 +289,13 @@ class DirectExecutionFallback:
|
|
|
282
289
|
if command_name:
|
|
283
290
|
cli_parts.append(command_name)
|
|
284
291
|
|
|
285
|
-
|
|
292
|
+
# Safely quote arguments that may contain spaces or special characters
|
|
293
|
+
for arg in remaining_args:
|
|
294
|
+
# Check if arg needs quoting (contains spaces, quotes, or special chars)
|
|
295
|
+
if ' ' in arg or '"' in arg or "'" in arg:
|
|
296
|
+
cli_parts.append(shlex.quote(arg))
|
|
297
|
+
else:
|
|
298
|
+
cli_parts.append(arg)
|
|
286
299
|
|
|
287
300
|
return " ".join(cli_parts)
|
|
288
301
|
|
|
@@ -22,6 +22,8 @@ from pathlib import Path
|
|
|
22
22
|
from typing import Literal, TypedDict
|
|
23
23
|
|
|
24
24
|
from tapps_agents.core.llm_behavior import EnforcementConfig
|
|
25
|
+
from tapps_agents.workflow.intent_detector import IntentDetector, WorkflowType
|
|
26
|
+
from tapps_agents.workflow.message_formatter import MessageConfig, MessageFormatter
|
|
25
27
|
|
|
26
28
|
logger = logging.getLogger(__name__)
|
|
27
29
|
|
|
@@ -140,6 +142,14 @@ class WorkflowEnforcer:
|
|
|
140
142
|
# Load config from file
|
|
141
143
|
self.config = self._load_config(config_path)
|
|
142
144
|
|
|
145
|
+
# Initialize intent detector and message formatter (ENH-001-S2, S3)
|
|
146
|
+
self._intent_detector = IntentDetector()
|
|
147
|
+
self._message_formatter = MessageFormatter(MessageConfig(
|
|
148
|
+
use_emoji=True,
|
|
149
|
+
show_benefits=self.config.suggest_workflows,
|
|
150
|
+
show_override=True,
|
|
151
|
+
))
|
|
152
|
+
|
|
143
153
|
logger.info(
|
|
144
154
|
f"WorkflowEnforcer initialized with mode={self.config.mode}, "
|
|
145
155
|
f"confidence_threshold={self.config.confidence_threshold}"
|
|
@@ -322,55 +332,58 @@ class WorkflowEnforcer:
|
|
|
322
332
|
- allow: Empty message
|
|
323
333
|
|
|
324
334
|
Note:
|
|
325
|
-
|
|
326
|
-
|
|
335
|
+
Uses MessageFormatter (ENH-001-S3) for rich, context-aware messages.
|
|
336
|
+
Uses IntentDetector (ENH-001-S2) for workflow detection and confidence.
|
|
327
337
|
"""
|
|
328
338
|
# Determine should_block flag
|
|
329
339
|
should_block = action == "block" and self.config.block_direct_edits
|
|
330
340
|
|
|
331
|
-
#
|
|
341
|
+
# Detect intent and get confidence (ENH-001-S2 integration)
|
|
342
|
+
detection_result = self._intent_detector.detect_workflow(
|
|
343
|
+
user_intent=user_intent,
|
|
344
|
+
file_path=file_path if file_path.exists() else None,
|
|
345
|
+
)
|
|
346
|
+
workflow = detection_result.workflow_type
|
|
347
|
+
confidence = detection_result.confidence
|
|
348
|
+
|
|
349
|
+
# Generate message using MessageFormatter (ENH-001-S3 integration)
|
|
332
350
|
if action == "block":
|
|
333
351
|
if self.config.suggest_workflows:
|
|
334
|
-
message = (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
f" • Comprehensive documentation\n"
|
|
340
|
-
f" • Early bug detection\n\n"
|
|
341
|
-
f"Suggested workflow:\n"
|
|
342
|
-
f" @simple-mode *build \"{user_intent or 'Implement feature'}\"\n\n"
|
|
343
|
-
f"To bypass enforcement, use: --skip-enforcement flag"
|
|
352
|
+
message = self._message_formatter.format_blocking_message(
|
|
353
|
+
workflow=workflow,
|
|
354
|
+
user_intent=user_intent,
|
|
355
|
+
file_path=file_path,
|
|
356
|
+
confidence=confidence,
|
|
344
357
|
)
|
|
345
358
|
else:
|
|
346
359
|
message = (
|
|
347
|
-
f"
|
|
360
|
+
f"Direct file edit blocked: {file_path}\n"
|
|
348
361
|
f"Use --skip-enforcement flag to bypass."
|
|
349
362
|
)
|
|
350
363
|
elif action == "warn":
|
|
351
364
|
if self.config.suggest_workflows:
|
|
352
|
-
message = (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
365
|
+
message = self._message_formatter.format_warning_message(
|
|
366
|
+
workflow=workflow,
|
|
367
|
+
user_intent=user_intent,
|
|
368
|
+
confidence=confidence,
|
|
356
369
|
)
|
|
357
370
|
else:
|
|
358
|
-
message = f"
|
|
371
|
+
message = f"Consider using a workflow for: {file_path}"
|
|
359
372
|
else: # allow
|
|
360
|
-
message =
|
|
373
|
+
message = self._message_formatter.format_allow_message()
|
|
361
374
|
|
|
362
|
-
# Create decision
|
|
375
|
+
# Create decision with actual confidence from intent detection
|
|
363
376
|
decision: EnforcementDecision = {
|
|
364
377
|
"action": action,
|
|
365
378
|
"message": message,
|
|
366
379
|
"should_block": should_block,
|
|
367
|
-
"confidence":
|
|
380
|
+
"confidence": confidence,
|
|
368
381
|
}
|
|
369
382
|
|
|
370
383
|
# Log decision
|
|
371
384
|
logger.debug(
|
|
372
385
|
f"Enforcement decision: action={action}, should_block={should_block}, "
|
|
373
|
-
f"file_path={file_path},
|
|
386
|
+
f"file_path={file_path}, workflow={workflow.value}, confidence={confidence:.1f}%"
|
|
374
387
|
)
|
|
375
388
|
|
|
376
389
|
return decision
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Message Formatter - ENH-001-S3
|
|
3
|
+
|
|
4
|
+
Formats enforcement messages for the Workflow Enforcement System.
|
|
5
|
+
Provides rich, context-aware messages for blocking and warning modes
|
|
6
|
+
with support for CLI and IDE output formats.
|
|
7
|
+
|
|
8
|
+
Design Principles:
|
|
9
|
+
- Separation of Concerns: Message formatting separate from enforcement logic
|
|
10
|
+
- Configurable: Emoji support, output format selection
|
|
11
|
+
- Context-Aware: Messages tailored to detected workflow type
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Literal
|
|
21
|
+
|
|
22
|
+
from tapps_agents.workflow.intent_detector import WorkflowType
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OutputFormat(str, Enum):
|
|
28
|
+
"""Output format for messages."""
|
|
29
|
+
|
|
30
|
+
CLI = "cli"
|
|
31
|
+
IDE = "ide"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class MessageConfig:
|
|
36
|
+
"""Configuration for message formatting."""
|
|
37
|
+
|
|
38
|
+
use_emoji: bool = True
|
|
39
|
+
output_format: OutputFormat = OutputFormat.CLI
|
|
40
|
+
show_benefits: bool = True
|
|
41
|
+
show_override: bool = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Workflow-specific benefits
|
|
45
|
+
WORKFLOW_BENEFITS: dict[WorkflowType, list[str]] = {
|
|
46
|
+
WorkflowType.BUILD: [
|
|
47
|
+
"Automatic testing (80%+ coverage)",
|
|
48
|
+
"Quality gates (75+ score required)",
|
|
49
|
+
"Comprehensive documentation",
|
|
50
|
+
"Early bug detection",
|
|
51
|
+
],
|
|
52
|
+
WorkflowType.FIX: [
|
|
53
|
+
"Root cause analysis",
|
|
54
|
+
"Regression test generation",
|
|
55
|
+
"Fix verification",
|
|
56
|
+
"Related issue detection",
|
|
57
|
+
],
|
|
58
|
+
WorkflowType.REFACTOR: [
|
|
59
|
+
"Behavior preservation tests",
|
|
60
|
+
"Code quality improvement",
|
|
61
|
+
"Technical debt reduction",
|
|
62
|
+
"Safe incremental changes",
|
|
63
|
+
],
|
|
64
|
+
WorkflowType.REVIEW: [
|
|
65
|
+
"Security vulnerability detection",
|
|
66
|
+
"Code quality scoring",
|
|
67
|
+
"Best practice suggestions",
|
|
68
|
+
"Maintainability analysis",
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class MessageFormatter:
|
|
74
|
+
"""
|
|
75
|
+
Formats enforcement messages for workflow enforcement system.
|
|
76
|
+
|
|
77
|
+
Provides rich, context-aware messages for blocking and warning modes
|
|
78
|
+
with configurable emoji support and output format selection.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(self, config: MessageConfig | None = None) -> None:
|
|
82
|
+
"""Initialize formatter with optional configuration."""
|
|
83
|
+
self.config = config or MessageConfig()
|
|
84
|
+
|
|
85
|
+
def format_blocking_message(
|
|
86
|
+
self,
|
|
87
|
+
workflow: WorkflowType,
|
|
88
|
+
user_intent: str,
|
|
89
|
+
file_path: Path,
|
|
90
|
+
confidence: float,
|
|
91
|
+
) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Format blocking mode message with benefits and override instructions.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
workflow: Detected workflow type
|
|
97
|
+
user_intent: User's intent description
|
|
98
|
+
file_path: Path to file being edited
|
|
99
|
+
confidence: Detection confidence (0-100)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Formatted blocking message
|
|
103
|
+
"""
|
|
104
|
+
emoji = self._get_emoji("block") if self.config.use_emoji else ""
|
|
105
|
+
intent_display = user_intent or "Implement feature"
|
|
106
|
+
path_display = Path(file_path).as_posix()
|
|
107
|
+
|
|
108
|
+
lines = [
|
|
109
|
+
f"{emoji}Direct file edit blocked: {path_display}".strip(),
|
|
110
|
+
"",
|
|
111
|
+
f"Detected intent: {workflow.value} (confidence: {confidence:.0f}%)",
|
|
112
|
+
"",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
if self.config.show_benefits:
|
|
116
|
+
lines.append("TappsCodingAgents workflows provide:")
|
|
117
|
+
for benefit in self._get_workflow_benefits(workflow):
|
|
118
|
+
bullet = " * " if self.config.output_format == OutputFormat.IDE else " - "
|
|
119
|
+
lines.append(f"{bullet}{benefit}")
|
|
120
|
+
lines.append("")
|
|
121
|
+
|
|
122
|
+
lines.append("Suggested workflow:")
|
|
123
|
+
lines.append(f' @simple-mode {workflow.value} "{intent_display}"')
|
|
124
|
+
lines.append("")
|
|
125
|
+
|
|
126
|
+
if self.config.show_override:
|
|
127
|
+
lines.extend(self._get_override_instructions())
|
|
128
|
+
|
|
129
|
+
return "\n".join(lines)
|
|
130
|
+
|
|
131
|
+
def format_warning_message(
|
|
132
|
+
self,
|
|
133
|
+
workflow: WorkflowType,
|
|
134
|
+
user_intent: str,
|
|
135
|
+
confidence: float,
|
|
136
|
+
) -> str:
|
|
137
|
+
"""
|
|
138
|
+
Format warning mode message (lighter suggestion).
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
workflow: Detected workflow type
|
|
142
|
+
user_intent: User's intent description
|
|
143
|
+
confidence: Detection confidence (0-100)
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Formatted warning message
|
|
147
|
+
"""
|
|
148
|
+
emoji = self._get_emoji("warn") if self.config.use_emoji else ""
|
|
149
|
+
intent_display = user_intent or "Implement feature"
|
|
150
|
+
|
|
151
|
+
lines = [
|
|
152
|
+
f"{emoji}Consider using a workflow (confidence: {confidence:.0f}%)".strip(),
|
|
153
|
+
f'Suggested: @simple-mode {workflow.value} "{intent_display}"',
|
|
154
|
+
"(Proceeding with direct edit...)",
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
return "\n".join(lines)
|
|
158
|
+
|
|
159
|
+
def format_allow_message(self) -> str:
|
|
160
|
+
"""Format allow message (empty for silent mode)."""
|
|
161
|
+
return ""
|
|
162
|
+
|
|
163
|
+
def _get_emoji(self, action: Literal["block", "warn", "allow"]) -> str:
|
|
164
|
+
"""Get emoji for action type."""
|
|
165
|
+
emojis = {
|
|
166
|
+
"block": "\u26a0\ufe0f ", # Warning sign
|
|
167
|
+
"warn": "\U0001f4a1 ", # Light bulb
|
|
168
|
+
"allow": "\u2705 ", # Check mark
|
|
169
|
+
}
|
|
170
|
+
return emojis.get(action, "")
|
|
171
|
+
|
|
172
|
+
def _get_workflow_benefits(self, workflow: WorkflowType) -> list[str]:
|
|
173
|
+
"""Get benefits list for workflow type."""
|
|
174
|
+
return WORKFLOW_BENEFITS.get(workflow, WORKFLOW_BENEFITS[WorkflowType.BUILD])
|
|
175
|
+
|
|
176
|
+
def _get_override_instructions(self) -> list[str]:
|
|
177
|
+
"""Get override instructions based on output format."""
|
|
178
|
+
if self.config.output_format == OutputFormat.IDE:
|
|
179
|
+
return [
|
|
180
|
+
"To bypass enforcement:",
|
|
181
|
+
" * Use --skip-enforcement flag",
|
|
182
|
+
" * Or set enforcement.mode: silent in config",
|
|
183
|
+
]
|
|
184
|
+
return [
|
|
185
|
+
"To bypass enforcement:",
|
|
186
|
+
" - Use --skip-enforcement flag",
|
|
187
|
+
" - Or set enforcement.mode: silent in .tapps-agents/config.yaml",
|
|
188
|
+
]
|
|
@@ -127,21 +127,60 @@ class ParallelStepExecutor:
|
|
|
127
127
|
"""
|
|
128
128
|
Find steps that are ready to execute (dependencies met).
|
|
129
129
|
|
|
130
|
+
This method respects the workflow's sequential 'next:' chain to prevent
|
|
131
|
+
premature step execution. A step is only ready if:
|
|
132
|
+
1. It's the 'next:' step from a completed step, OR it's the first step
|
|
133
|
+
2. All its artifact dependencies are met
|
|
134
|
+
|
|
135
|
+
This ensures workflows follow the intended sequence (enhance→planning→
|
|
136
|
+
implementation→review→testing→complete) instead of jumping to the final
|
|
137
|
+
step prematurely.
|
|
138
|
+
|
|
139
|
+
Fix for: Workflow Auto-Continuation Issue (BUG - Workflow stops after 1 step)
|
|
140
|
+
Root cause: Previous logic only checked artifact dependencies, allowing
|
|
141
|
+
'complete' step to execute immediately after 'enhance' because it had
|
|
142
|
+
no 'requires' dependencies.
|
|
143
|
+
|
|
130
144
|
Args:
|
|
131
145
|
workflow_steps: All workflow steps
|
|
132
146
|
completed_step_ids: Set of completed step IDs
|
|
133
147
|
running_step_ids: Set of currently running step IDs
|
|
134
148
|
available_artifacts: Set of available artifact names (from state.artifacts)
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
List of steps ready to execute
|
|
137
152
|
"""
|
|
138
153
|
ready: list[WorkflowStep] = []
|
|
139
154
|
artifacts = available_artifacts or set()
|
|
140
155
|
|
|
156
|
+
# Build set of next steps from completed steps' next: fields
|
|
157
|
+
next_step_ids: set[str] = set()
|
|
158
|
+
for step in workflow_steps:
|
|
159
|
+
if step.id in completed_step_ids and step.next:
|
|
160
|
+
next_step_ids.add(step.next)
|
|
161
|
+
|
|
162
|
+
# If no completed steps yet, first step is ready
|
|
163
|
+
if not completed_step_ids:
|
|
164
|
+
first_step = workflow_steps[0] if workflow_steps else None
|
|
165
|
+
if first_step and first_step.id not in running_step_ids:
|
|
166
|
+
# Still check artifact dependencies for first step
|
|
167
|
+
if first_step.requires:
|
|
168
|
+
all_met = all(req in artifacts for req in first_step.requires)
|
|
169
|
+
if all_met:
|
|
170
|
+
return [first_step]
|
|
171
|
+
else:
|
|
172
|
+
return [first_step]
|
|
173
|
+
return []
|
|
174
|
+
|
|
175
|
+
# Find steps that are in the next_step_ids set (sequential progression)
|
|
141
176
|
for step in workflow_steps:
|
|
142
177
|
if step.id in completed_step_ids or step.id in running_step_ids:
|
|
143
178
|
continue
|
|
144
179
|
|
|
180
|
+
# Must be in the next_step_ids set (respects sequential workflow chain)
|
|
181
|
+
if step.id not in next_step_ids:
|
|
182
|
+
continue
|
|
183
|
+
|
|
145
184
|
# Check if all required artifacts exist and are available
|
|
146
185
|
if step.requires:
|
|
147
186
|
all_met = all(req in artifacts for req in step.requires)
|
|
@@ -342,7 +381,7 @@ class ParallelStepExecutor:
|
|
|
342
381
|
tasks_map[task] = step
|
|
343
382
|
|
|
344
383
|
# All tasks completed successfully - collect results
|
|
345
|
-
for task
|
|
384
|
+
for task in tasks_map:
|
|
346
385
|
result = await task
|
|
347
386
|
results.append(result)
|
|
348
387
|
|
|
@@ -416,7 +455,7 @@ class ParallelStepExecutor:
|
|
|
416
455
|
|
|
417
456
|
# Re-raise the first exception for upstream handling
|
|
418
457
|
if eg.exceptions:
|
|
419
|
-
raise eg.exceptions[0]
|
|
458
|
+
raise eg.exceptions[0] from None
|
|
420
459
|
|
|
421
460
|
# Sort by step.id for deterministic ordering
|
|
422
461
|
results.sort(key=lambda r: r.step.id)
|