agentweaver 0.1.3 → 0.1.5

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.
Files changed (33) hide show
  1. package/README.md +48 -14
  2. package/dist/artifacts.js +86 -3
  3. package/dist/executors/verify-build-executor.js +110 -9
  4. package/dist/index.js +170 -18
  5. package/dist/interactive-ui.js +525 -33
  6. package/dist/pipeline/checks.js +5 -0
  7. package/dist/pipeline/context.js +1 -0
  8. package/dist/pipeline/declarative-flow-runner.js +16 -0
  9. package/dist/pipeline/flow-specs/auto.json +191 -3
  10. package/dist/pipeline/flow-specs/bug-analyze.json +140 -0
  11. package/dist/pipeline/flow-specs/bug-fix.json +44 -0
  12. package/dist/pipeline/flow-specs/implement.json +12 -0
  13. package/dist/pipeline/flow-specs/mr-description.json +89 -0
  14. package/dist/pipeline/flow-specs/plan.json +52 -0
  15. package/dist/pipeline/flow-specs/preflight.json +32 -0
  16. package/dist/pipeline/flow-specs/review-fix.json +79 -1
  17. package/dist/pipeline/flow-specs/review.json +79 -0
  18. package/dist/pipeline/flow-specs/run-linter-loop.json +149 -0
  19. package/dist/pipeline/flow-specs/run-tests-loop.json +149 -0
  20. package/dist/pipeline/flow-specs/task-describe.json +89 -0
  21. package/dist/pipeline/node-registry.js +19 -0
  22. package/dist/pipeline/nodes/flow-run-node.js +40 -0
  23. package/dist/pipeline/nodes/review-findings-form-node.js +65 -0
  24. package/dist/pipeline/nodes/user-input-node.js +93 -0
  25. package/dist/pipeline/nodes/verify-build-node.js +1 -0
  26. package/dist/pipeline/prompt-registry.js +6 -1
  27. package/dist/pipeline/spec-compiler.js +13 -0
  28. package/dist/pipeline/spec-validator.js +12 -0
  29. package/dist/pipeline/value-resolver.js +49 -4
  30. package/dist/prompts.js +46 -14
  31. package/dist/structured-artifacts.js +272 -0
  32. package/dist/user-input.js +171 -0
  33. package/package.json +1 -1
@@ -48,17 +48,35 @@
48
48
  "taskKey": { "ref": "params.taskKey" }
49
49
  }
50
50
  },
51
+ "design_json_file": {
52
+ "artifact": {
53
+ "kind": "design-json-file",
54
+ "taskKey": { "ref": "params.taskKey" }
55
+ }
56
+ },
51
57
  "plan_file": {
52
58
  "artifact": {
53
59
  "kind": "plan-file",
54
60
  "taskKey": { "ref": "params.taskKey" }
55
61
  }
56
62
  },
63
+ "plan_json_file": {
64
+ "artifact": {
65
+ "kind": "plan-json-file",
66
+ "taskKey": { "ref": "params.taskKey" }
67
+ }
68
+ },
57
69
  "qa_file": {
58
70
  "artifact": {
59
71
  "kind": "qa-file",
60
72
  "taskKey": { "ref": "params.taskKey" }
61
73
  }
74
+ },
75
+ "qa_json_file": {
76
+ "artifact": {
77
+ "kind": "qa-json-file",
78
+ "taskKey": { "ref": "params.taskKey" }
79
+ }
62
80
  }
63
81
  },
64
82
  "extraPrompt": { "ref": "params.extraPrompt" },
@@ -79,6 +97,40 @@
79
97
  }
80
98
  },
81
99
  "message": "Plan mode did not produce the required artifacts."
100
+ },
101
+ {
102
+ "kind": "require-structured-artifacts",
103
+ "when": { "not": { "ref": "context.dryRun" } },
104
+ "items": [
105
+ {
106
+ "path": {
107
+ "artifact": {
108
+ "kind": "design-json-file",
109
+ "taskKey": { "ref": "params.taskKey" }
110
+ }
111
+ },
112
+ "schemaId": "implementation-design/v1"
113
+ },
114
+ {
115
+ "path": {
116
+ "artifact": {
117
+ "kind": "plan-json-file",
118
+ "taskKey": { "ref": "params.taskKey" }
119
+ }
120
+ },
121
+ "schemaId": "implementation-plan/v1"
122
+ },
123
+ {
124
+ "path": {
125
+ "artifact": {
126
+ "kind": "qa-json-file",
127
+ "taskKey": { "ref": "params.taskKey" }
128
+ }
129
+ },
130
+ "schemaId": "qa-plan/v1"
131
+ }
132
+ ],
133
+ "message": "Plan mode produced invalid structured artifacts."
82
134
  }
83
135
  ]
84
136
  }
@@ -124,6 +124,12 @@
124
124
  "kind": "task-summary-file",
125
125
  "taskKey": { "ref": "params.taskKey" }
126
126
  }
127
+ },
128
+ "task_summary_json_file": {
129
+ "artifact": {
130
+ "kind": "task-summary-json-file",
131
+ "taskKey": { "ref": "params.taskKey" }
132
+ }
127
133
  }
128
134
  },
129
135
  "format": "plain"
@@ -136,6 +142,16 @@
136
142
  "taskKey": { "ref": "params.taskKey" }
137
143
  }
138
144
  },
145
+ "requiredArtifacts": {
146
+ "list": [
147
+ {
148
+ "artifact": {
149
+ "kind": "task-summary-json-file",
150
+ "taskKey": { "ref": "params.taskKey" }
151
+ }
152
+ }
153
+ ]
154
+ },
139
155
  "model": { "const": "haiku" }
140
156
  },
141
157
  "expect": [
@@ -153,6 +169,22 @@
153
169
  ]
154
170
  },
155
171
  "message": "Claude summary did not produce the task summary artifact."
172
+ },
173
+ {
174
+ "kind": "require-structured-artifacts",
175
+ "when": { "not": { "ref": "context.dryRun" } },
176
+ "items": [
177
+ {
178
+ "path": {
179
+ "artifact": {
180
+ "kind": "task-summary-json-file",
181
+ "taskKey": { "ref": "params.taskKey" }
182
+ }
183
+ },
184
+ "schemaId": "task-summary/v1"
185
+ }
186
+ ],
187
+ "message": "Claude summary produced invalid structured artifacts."
156
188
  }
157
189
  ],
158
190
  "after": [
@@ -8,6 +8,48 @@
8
8
  {
9
9
  "id": "review-fix",
10
10
  "steps": [
11
+ {
12
+ "id": "build_review_fix_selection_form",
13
+ "node": "review-findings-form",
14
+ "params": {
15
+ "reviewJsonFile": {
16
+ "artifact": {
17
+ "kind": "review-json-file",
18
+ "taskKey": { "ref": "params.taskKey" },
19
+ "iteration": { "ref": "params.latestIteration" }
20
+ }
21
+ },
22
+ "formId": { "const": "review-fix-selection" },
23
+ "title": { "const": "Review-Fix Selection" },
24
+ "description": {
25
+ "const": "Отметьте findings, которые нужно исправить в этой итерации review-fix. Можно включить режим исправления всех findings."
26
+ }
27
+ }
28
+ },
29
+ {
30
+ "id": "collect_review_fix_selection",
31
+ "node": "user-input",
32
+ "params": {
33
+ "formId": { "ref": "steps.review-fix.build_review_fix_selection_form.value.formId" },
34
+ "title": { "ref": "steps.review-fix.build_review_fix_selection_form.value.title" },
35
+ "description": { "ref": "steps.review-fix.build_review_fix_selection_form.value.description" },
36
+ "submitLabel": { "const": "Run review-fix" },
37
+ "fields": { "ref": "steps.review-fix.build_review_fix_selection_form.value.fields" },
38
+ "outputFile": { "ref": "params.reviewFixSelectionJsonFile" }
39
+ },
40
+ "expect": [
41
+ {
42
+ "kind": "require-structured-artifacts",
43
+ "items": [
44
+ {
45
+ "path": { "ref": "params.reviewFixSelectionJsonFile" },
46
+ "schemaId": "user-input/v1"
47
+ }
48
+ ],
49
+ "message": "Review-fix selection input is missing or invalid."
50
+ }
51
+ ]
52
+ },
11
53
  {
12
54
  "id": "run_codex_review_fix",
13
55
  "node": "codex-local-prompt",
@@ -21,6 +63,13 @@
21
63
  "iteration": { "ref": "params.latestIteration" }
22
64
  }
23
65
  },
66
+ "review_reply_json_file": {
67
+ "artifact": {
68
+ "kind": "review-reply-json-file",
69
+ "taskKey": { "ref": "params.taskKey" },
70
+ "iteration": { "ref": "params.latestIteration" }
71
+ }
72
+ },
24
73
  "items": { "ref": "params.reviewFixPoints" },
25
74
  "review_fix_file": {
26
75
  "artifact": {
@@ -28,9 +77,21 @@
28
77
  "taskKey": { "ref": "params.taskKey" },
29
78
  "iteration": { "ref": "params.latestIteration" }
30
79
  }
80
+ },
81
+ "review_fix_json_file": {
82
+ "artifact": {
83
+ "kind": "review-fix-json-file",
84
+ "taskKey": { "ref": "params.taskKey" },
85
+ "iteration": { "ref": "params.latestIteration" }
86
+ }
87
+ }
88
+ },
89
+ "extraPrompt": {
90
+ "appendPrompt": {
91
+ "base": { "ref": "params.extraPrompt" },
92
+ "suffix": { "ref": "steps.review-fix.collect_review_fix_selection.value.promptSuffix" }
31
93
  }
32
94
  },
33
- "extraPrompt": { "ref": "params.extraPrompt" },
34
95
  "format": "task-prompt"
35
96
  },
36
97
  "params": {
@@ -58,6 +119,23 @@
58
119
  ]
59
120
  },
60
121
  "message": "Review-fix mode did not produce the required review-fix artifact."
122
+ },
123
+ {
124
+ "kind": "require-structured-artifacts",
125
+ "when": { "not": { "ref": "context.dryRun" } },
126
+ "items": [
127
+ {
128
+ "path": {
129
+ "artifact": {
130
+ "kind": "review-fix-json-file",
131
+ "taskKey": { "ref": "params.taskKey" },
132
+ "iteration": { "ref": "params.latestIteration" }
133
+ }
134
+ },
135
+ "schemaId": "review-fix-report/v1"
136
+ }
137
+ ],
138
+ "message": "Review-fix mode produced invalid structured artifacts."
61
139
  }
62
140
  ]
63
141
  },
@@ -23,18 +23,37 @@
23
23
  "taskKey": { "ref": "params.taskKey" }
24
24
  }
25
25
  },
26
+ "design_json_file": {
27
+ "artifact": {
28
+ "kind": "design-json-file",
29
+ "taskKey": { "ref": "params.taskKey" }
30
+ }
31
+ },
26
32
  "plan_file": {
27
33
  "artifact": {
28
34
  "kind": "plan-file",
29
35
  "taskKey": { "ref": "params.taskKey" }
30
36
  }
31
37
  },
38
+ "plan_json_file": {
39
+ "artifact": {
40
+ "kind": "plan-json-file",
41
+ "taskKey": { "ref": "params.taskKey" }
42
+ }
43
+ },
32
44
  "review_file": {
33
45
  "artifact": {
34
46
  "kind": "review-file",
35
47
  "taskKey": { "ref": "params.taskKey" },
36
48
  "iteration": { "ref": "params.iteration" }
37
49
  }
50
+ },
51
+ "review_json_file": {
52
+ "artifact": {
53
+ "kind": "review-json-file",
54
+ "taskKey": { "ref": "params.taskKey" },
55
+ "iteration": { "ref": "params.iteration" }
56
+ }
38
57
  }
39
58
  },
40
59
  "extraPrompt": { "ref": "params.extraPrompt" },
@@ -65,6 +84,23 @@
65
84
  ]
66
85
  },
67
86
  "message": "Claude review did not produce the required review artifact."
87
+ },
88
+ {
89
+ "kind": "require-structured-artifacts",
90
+ "when": { "not": { "ref": "context.dryRun" } },
91
+ "items": [
92
+ {
93
+ "path": {
94
+ "artifact": {
95
+ "kind": "review-json-file",
96
+ "taskKey": { "ref": "params.taskKey" },
97
+ "iteration": { "ref": "params.iteration" }
98
+ }
99
+ },
100
+ "schemaId": "review-findings/v1"
101
+ }
102
+ ],
103
+ "message": "Claude review produced invalid structured artifacts."
68
104
  }
69
105
  ]
70
106
  },
@@ -118,6 +154,13 @@
118
154
  "iteration": { "ref": "params.iteration" }
119
155
  }
120
156
  },
157
+ "review_json_file": {
158
+ "artifact": {
159
+ "kind": "review-json-file",
160
+ "taskKey": { "ref": "params.taskKey" },
161
+ "iteration": { "ref": "params.iteration" }
162
+ }
163
+ },
121
164
  "jira_task_file": {
122
165
  "artifact": {
123
166
  "kind": "jira-task-file",
@@ -130,18 +173,37 @@
130
173
  "taskKey": { "ref": "params.taskKey" }
131
174
  }
132
175
  },
176
+ "design_json_file": {
177
+ "artifact": {
178
+ "kind": "design-json-file",
179
+ "taskKey": { "ref": "params.taskKey" }
180
+ }
181
+ },
133
182
  "plan_file": {
134
183
  "artifact": {
135
184
  "kind": "plan-file",
136
185
  "taskKey": { "ref": "params.taskKey" }
137
186
  }
138
187
  },
188
+ "plan_json_file": {
189
+ "artifact": {
190
+ "kind": "plan-json-file",
191
+ "taskKey": { "ref": "params.taskKey" }
192
+ }
193
+ },
139
194
  "review_reply_file": {
140
195
  "artifact": {
141
196
  "kind": "review-reply-file",
142
197
  "taskKey": { "ref": "params.taskKey" },
143
198
  "iteration": { "ref": "params.iteration" }
144
199
  }
200
+ },
201
+ "review_reply_json_file": {
202
+ "artifact": {
203
+ "kind": "review-reply-json-file",
204
+ "taskKey": { "ref": "params.taskKey" },
205
+ "iteration": { "ref": "params.iteration" }
206
+ }
145
207
  }
146
208
  },
147
209
  "extraPrompt": { "ref": "params.extraPrompt" },
@@ -172,6 +234,23 @@
172
234
  ]
173
235
  },
174
236
  "message": "Codex review reply did not produce the required review-reply artifact."
237
+ },
238
+ {
239
+ "kind": "require-structured-artifacts",
240
+ "when": { "not": { "ref": "context.dryRun" } },
241
+ "items": [
242
+ {
243
+ "path": {
244
+ "artifact": {
245
+ "kind": "review-reply-json-file",
246
+ "taskKey": { "ref": "params.taskKey" },
247
+ "iteration": { "ref": "params.iteration" }
248
+ }
249
+ },
250
+ "schemaId": "review-reply/v1"
251
+ }
252
+ ],
253
+ "message": "Codex review reply produced invalid structured artifacts."
175
254
  }
176
255
  ]
177
256
  },
@@ -0,0 +1,149 @@
1
+ {
2
+ "kind": "run-linter-loop-flow",
3
+ "version": 1,
4
+ "phases": [
5
+ {
6
+ "id": "run_linter_try_1",
7
+ "steps": [
8
+ {
9
+ "id": "run_linter",
10
+ "node": "verify-build",
11
+ "params": {
12
+ "dockerComposeFile": { "ref": "params.dockerComposeFile" },
13
+ "labelText": {
14
+ "const": "Running run_linter.sh in isolated Docker (attempt 1)"
15
+ },
16
+ "service": { "const": "run-linter" }
17
+ },
18
+ "stopFlowIf": {
19
+ "equals": [
20
+ { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.ok" },
21
+ { "const": true }
22
+ ]
23
+ }
24
+ },
25
+ {
26
+ "id": "fix_linter",
27
+ "when": {
28
+ "equals": [
29
+ { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.ok" },
30
+ { "const": false }
31
+ ]
32
+ },
33
+ "node": "codex-local-prompt",
34
+ "prompt": {
35
+ "templateRef": "run-linter-loop-fix",
36
+ "extraPrompt": {
37
+ "appendPrompt": {
38
+ "base": { "ref": "params.extraPrompt" },
39
+ "suffix": {
40
+ "template": "Последний результат run_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
41
+ "vars": {
42
+ "exit_code": { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.exitCode" },
43
+ "summary": { "ref": "steps.run_linter_try_1.run_linter.outputs.parsed.summary" }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ "format": "task-prompt"
49
+ },
50
+ "params": {
51
+ "labelText": {
52
+ "const": "Running Codex linter loop fix (attempt 1)"
53
+ },
54
+ "model": { "const": "gpt-5.4" }
55
+ }
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "repeat": {
61
+ "var": "attempt",
62
+ "from": 2,
63
+ "to": 5
64
+ },
65
+ "phases": [
66
+ {
67
+ "id": "run_linter_try_${attempt}",
68
+ "when": {
69
+ "equals": [
70
+ { "ref": "steps.run_linter_try_${attempt_minus_one}.run_linter.outputs.parsed.ok" },
71
+ { "const": false }
72
+ ]
73
+ },
74
+ "steps": [
75
+ {
76
+ "id": "run_linter",
77
+ "node": "verify-build",
78
+ "params": {
79
+ "dockerComposeFile": { "ref": "params.dockerComposeFile" },
80
+ "labelText": {
81
+ "template": "Running run_linter.sh in isolated Docker (attempt ${attempt})"
82
+ },
83
+ "service": { "const": "run-linter" }
84
+ },
85
+ "stopFlowIf": {
86
+ "equals": [
87
+ { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.ok" },
88
+ { "const": true }
89
+ ]
90
+ }
91
+ },
92
+ {
93
+ "id": "fix_linter",
94
+ "when": {
95
+ "equals": [
96
+ { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.ok" },
97
+ { "const": false }
98
+ ]
99
+ },
100
+ "node": "codex-local-prompt",
101
+ "prompt": {
102
+ "templateRef": "run-linter-loop-fix",
103
+ "extraPrompt": {
104
+ "appendPrompt": {
105
+ "base": { "ref": "params.extraPrompt" },
106
+ "suffix": {
107
+ "template": "Последний результат run_linter.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
108
+ "vars": {
109
+ "exit_code": { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.exitCode" },
110
+ "summary": { "ref": "steps.run_linter_try_${attempt}.run_linter.outputs.parsed.summary" }
111
+ }
112
+ }
113
+ }
114
+ },
115
+ "format": "task-prompt"
116
+ },
117
+ "params": {
118
+ "labelText": {
119
+ "template": "Running Codex linter loop fix (attempt ${attempt})"
120
+ },
121
+ "model": { "const": "gpt-5.4" }
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ "id": "run_linter_failed",
130
+ "steps": [
131
+ {
132
+ "id": "assert_run_linter_success",
133
+ "node": "file-check",
134
+ "params": {
135
+ "path": { "ref": "params.dockerComposeFile" }
136
+ },
137
+ "expect": [
138
+ {
139
+ "kind": "step-output",
140
+ "value": { "ref": "steps.run_linter_try_5.run_linter.outputs.parsed.ok" },
141
+ "equals": { "const": true },
142
+ "message": "run-linter-loop exhausted all attempts without a successful run_linter.sh execution."
143
+ }
144
+ ]
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }
@@ -0,0 +1,149 @@
1
+ {
2
+ "kind": "run-tests-loop-flow",
3
+ "version": 1,
4
+ "phases": [
5
+ {
6
+ "id": "run_tests_try_1",
7
+ "steps": [
8
+ {
9
+ "id": "run_tests",
10
+ "node": "verify-build",
11
+ "params": {
12
+ "dockerComposeFile": { "ref": "params.dockerComposeFile" },
13
+ "labelText": {
14
+ "const": "Running run_tests.sh in isolated Docker (attempt 1)"
15
+ },
16
+ "service": { "const": "run-tests" }
17
+ },
18
+ "stopFlowIf": {
19
+ "equals": [
20
+ { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.ok" },
21
+ { "const": true }
22
+ ]
23
+ }
24
+ },
25
+ {
26
+ "id": "fix_tests",
27
+ "when": {
28
+ "equals": [
29
+ { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.ok" },
30
+ { "const": false }
31
+ ]
32
+ },
33
+ "node": "codex-local-prompt",
34
+ "prompt": {
35
+ "templateRef": "run-tests-loop-fix",
36
+ "extraPrompt": {
37
+ "appendPrompt": {
38
+ "base": { "ref": "params.extraPrompt" },
39
+ "suffix": {
40
+ "template": "Последний результат run_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
41
+ "vars": {
42
+ "exit_code": { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.exitCode" },
43
+ "summary": { "ref": "steps.run_tests_try_1.run_tests.outputs.parsed.summary" }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ "format": "task-prompt"
49
+ },
50
+ "params": {
51
+ "labelText": {
52
+ "const": "Running Codex tests loop fix (attempt 1)"
53
+ },
54
+ "model": { "const": "gpt-5.4" }
55
+ }
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "repeat": {
61
+ "var": "attempt",
62
+ "from": 2,
63
+ "to": 5
64
+ },
65
+ "phases": [
66
+ {
67
+ "id": "run_tests_try_${attempt}",
68
+ "when": {
69
+ "equals": [
70
+ { "ref": "steps.run_tests_try_${attempt_minus_one}.run_tests.outputs.parsed.ok" },
71
+ { "const": false }
72
+ ]
73
+ },
74
+ "steps": [
75
+ {
76
+ "id": "run_tests",
77
+ "node": "verify-build",
78
+ "params": {
79
+ "dockerComposeFile": { "ref": "params.dockerComposeFile" },
80
+ "labelText": {
81
+ "template": "Running run_tests.sh in isolated Docker (attempt ${attempt})"
82
+ },
83
+ "service": { "const": "run-tests" }
84
+ },
85
+ "stopFlowIf": {
86
+ "equals": [
87
+ { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.ok" },
88
+ { "const": true }
89
+ ]
90
+ }
91
+ },
92
+ {
93
+ "id": "fix_tests",
94
+ "when": {
95
+ "equals": [
96
+ { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.ok" },
97
+ { "const": false }
98
+ ]
99
+ },
100
+ "node": "codex-local-prompt",
101
+ "prompt": {
102
+ "templateRef": "run-tests-loop-fix",
103
+ "extraPrompt": {
104
+ "appendPrompt": {
105
+ "base": { "ref": "params.extraPrompt" },
106
+ "suffix": {
107
+ "template": "Последний результат run_tests.sh: exitCode={exit_code}, summary={summary}. Исправь только то, что мешает успешному прохождению проверки.",
108
+ "vars": {
109
+ "exit_code": { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.exitCode" },
110
+ "summary": { "ref": "steps.run_tests_try_${attempt}.run_tests.outputs.parsed.summary" }
111
+ }
112
+ }
113
+ }
114
+ },
115
+ "format": "task-prompt"
116
+ },
117
+ "params": {
118
+ "labelText": {
119
+ "template": "Running Codex tests loop fix (attempt ${attempt})"
120
+ },
121
+ "model": { "const": "gpt-5.4" }
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ "id": "run_tests_failed",
130
+ "steps": [
131
+ {
132
+ "id": "assert_run_tests_success",
133
+ "node": "file-check",
134
+ "params": {
135
+ "path": { "ref": "params.dockerComposeFile" }
136
+ },
137
+ "expect": [
138
+ {
139
+ "kind": "step-output",
140
+ "value": { "ref": "steps.run_tests_try_5.run_tests.outputs.parsed.ok" },
141
+ "equals": { "const": true },
142
+ "message": "run-tests-loop exhausted all attempts without a successful run_tests.sh execution."
143
+ }
144
+ ]
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }