@alevental/cccp 0.3.1 → 0.4.1

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.
@@ -220,6 +220,87 @@ Sub-pipeline composition. Invokes another pipeline YAML inline within the parent
220
220
  - Resume works across pipeline boundaries
221
221
  - `on_fail` strategies work the same as PGE/autoresearch stages
222
222
 
223
+ ## Parallel Execution Groups
224
+
225
+ Run independent stages concurrently. Stages in a `parallel` block execute simultaneously as separate subprocesses, reducing total pipeline runtime when stages don't depend on each other's outputs.
226
+
227
+ ### When to Use Parallel Groups
228
+
229
+ Use parallel groups when stages share the same inputs but produce independent outputs. Common patterns:
230
+
231
+ - **Content fan-out**: After research/positioning is complete, multiple content pieces (blog post, release notes, social copy, changelog) can all be written simultaneously since they read the same source material
232
+ - **Multi-perspective analysis**: Run competitive analysis, market sizing, and customer research in parallel when they all stem from the same brief
233
+ - **Independent code tasks**: After a design is approved, implementation of separate modules or writing tests alongside documentation can happen concurrently
234
+ - **Multi-format generation**: Produce different output formats (PDF report, slide deck, executive summary) from the same source artifact in parallel
235
+
236
+ The key question is: **does stage B need to read stage A's output?** If not, they can be parallel.
237
+
238
+ ```yaml
239
+ - parallel:
240
+ on_failure: wait_all # Optional. "fail_fast" (default) or "wait_all".
241
+ stages:
242
+ - name: blog-post
243
+ type: pge
244
+ task: "Write the launch blog post."
245
+ # ... full stage config
246
+ - name: release-notes
247
+ type: agent
248
+ task: "Write release notes."
249
+ agent: copywriter
250
+ output: "{artifact_dir}/release-notes.md"
251
+ ```
252
+
253
+ | Field | Type | Required | Description |
254
+ |-------|------|----------|-------------|
255
+ | `on_failure` | string | No | `"fail_fast"` (default): stop group on first failure. `"wait_all"`: let all stages finish. |
256
+ | `stages` | Stage[] | Yes | At least 2 stages. Each stage uses the standard schema. |
257
+
258
+ ### Constraints
259
+
260
+ - **No `human_gate` inside parallel groups** — gates block execution and cannot run alongside other stages
261
+ - **No `pipeline` stages inside parallel groups** — sub-pipelines have complex state interactions
262
+ - **No conflicting outputs** — stages in the same group cannot write to the same `output` or `contract.deliverable` path
263
+ - **Unique stage names** — all stage names must be unique across the entire pipeline (including inside groups)
264
+
265
+ ### Failure Handling
266
+
267
+ | Mode | Behavior |
268
+ |------|----------|
269
+ | `fail_fast` (default) | When one stage fails, remaining unstarted stages are skipped. Already-running stages finish naturally. Pipeline stops after the group. |
270
+ | `wait_all` | All stages run to completion regardless of individual failures. Pipeline stops after the group if any stage failed. |
271
+
272
+ ### Resume
273
+
274
+ When resuming a pipeline that was interrupted during a parallel group, only the stages that didn't complete are re-executed. Completed stages within the group are skipped.
275
+
276
+ ### Example: Parallel Content Creation
277
+
278
+ ```yaml
279
+ stages:
280
+ - name: positioning
281
+ type: pge
282
+ # ... defines positioning strategy
283
+
284
+ - parallel:
285
+ on_failure: wait_all
286
+ stages:
287
+ - name: blog-post
288
+ type: pge
289
+ task: "Write the launch blog post."
290
+ inputs:
291
+ - "{artifact_dir}/positioning.md"
292
+ # ... PGE config
293
+ - name: release-notes
294
+ type: agent
295
+ task: "Write release notes."
296
+ agent: copywriter
297
+ output: "{artifact_dir}/release-notes.md"
298
+
299
+ - name: launch-approval
300
+ type: human_gate
301
+ prompt: "Review all materials."
302
+ ```
303
+
223
304
  ## Variables
224
305
 
225
306
  ### Syntax
package/README.md CHANGED
@@ -70,7 +70,7 @@ npx @alevental/cccp run -f pipelines/example.yaml -p my-project
70
70
 
71
71
  ## Pipeline YAML
72
72
 
73
- Pipelines are sequences of typed stages:
73
+ Pipelines are sequences of typed stages, with optional parallel groups for concurrent execution:
74
74
 
75
75
  ```yaml
76
76
  name: my-pipeline
@@ -111,6 +111,19 @@ stages:
111
111
  variables:
112
112
  source: "{artifact_dir}/design.md"
113
113
 
114
+ # Run independent stages concurrently
115
+ - parallel:
116
+ on_failure: wait_all
117
+ stages:
118
+ - name: blog-post
119
+ type: agent
120
+ agent: copywriter
121
+ output: "{artifact_dir}/blog-post.md"
122
+ - name: release-notes
123
+ type: agent
124
+ agent: copywriter
125
+ output: "{artifact_dir}/release-notes.md"
126
+
114
127
  # Human approval gate
115
128
  - name: approval
116
129
  type: human_gate
@@ -129,6 +142,8 @@ stages:
129
142
  | `pipeline` | Invoke another pipeline YAML as a sub-pipeline — runs inline, shares the parent run lifecycle |
130
143
  | `human_gate` | Block until approved via MCP tool call or state file edit |
131
144
 
145
+ Stages can also be wrapped in a `parallel` block to run concurrently. See the [pipeline skill](.claude/skills/cccp-pipeline/SKILL.md) for the full schema.
146
+
132
147
  ### Variables
133
148
 
134
149
  Built-in variables available in all string fields:
@@ -296,7 +311,7 @@ Without cmux, CCCP falls back to plain terminal output.
296
311
  ## Development
297
312
 
298
313
  ```bash
299
- npm test # run all tests (181 tests)
314
+ npm test # run all tests (206 tests)
300
315
  npm run typecheck # tsc --noEmit
301
316
  npm run test:watch # watch mode
302
317
  npm run build # compile TypeScript to dist/
package/dist/cli.js CHANGED
@@ -61,10 +61,10 @@ program
61
61
  cliVars,
62
62
  });
63
63
  if (showTui) {
64
- const { createState, saveState } = await import("./state.js");
64
+ const { createState, flattenStageEntries, saveState } = await import("./state.js");
65
65
  const { startDashboard } = await import("./tui/dashboard.js");
66
66
  // Create initial state so the dashboard has something to render.
67
- const initialState = createState(pipeline.name, opts.project, pipelineFile, pipeline.stages.map((s) => ({ name: s.name, type: s.type })), artifactDir, projectDir);
67
+ const initialState = createState(pipeline.name, opts.project, pipelineFile, flattenStageEntries(pipeline.stages), artifactDir, projectDir);
68
68
  await saveState(initialState);
69
69
  // Start dashboard, then run pipeline. Dashboard watches state.json.
70
70
  const dashboard = startDashboard(initialState.runId, projectDir, initialState);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/pipeline.js CHANGED
@@ -89,11 +89,67 @@ const StageSchema = z.discriminatedUnion("type", [
89
89
  AutoresearchStageSchema,
90
90
  PipelineStageSchema,
91
91
  ]);
92
+ const ParallelGroupSchema = z.object({
93
+ parallel: z.object({
94
+ on_failure: z.enum(["fail_fast", "wait_all"]).optional(),
95
+ stages: z.array(StageSchema).min(2, "Parallel groups must contain at least 2 stages"),
96
+ }),
97
+ });
98
+ const StageEntrySchema = z.union([StageSchema, ParallelGroupSchema]);
92
99
  const PipelineSchema = z.object({
93
100
  name: z.string(),
94
101
  description: z.string().optional(),
95
102
  variables: z.record(z.string()).optional(),
96
- stages: z.array(StageSchema).min(1),
103
+ stages: z.array(StageEntrySchema).min(1),
104
+ }).superRefine((pipeline, ctx) => {
105
+ // Collect all stage names and validate parallel group constraints.
106
+ const allNames = new Set();
107
+ const issues = [];
108
+ for (const entry of pipeline.stages) {
109
+ if ("parallel" in entry && !("type" in entry)) {
110
+ // Parallel group — validate inner stages
111
+ const group = entry;
112
+ const groupOutputs = new Map(); // output path → stage name
113
+ for (const stage of group.parallel.stages) {
114
+ // No human_gate inside parallel groups
115
+ if (stage.type === "human_gate") {
116
+ issues.push(`Stage '${stage.name}' is type human_gate and cannot be inside a parallel group (gates block execution)`);
117
+ }
118
+ // No pipeline stages inside parallel groups
119
+ if (stage.type === "pipeline") {
120
+ issues.push(`Stage '${stage.name}' is type pipeline and cannot be inside a parallel group`);
121
+ }
122
+ // Duplicate name check
123
+ if (allNames.has(stage.name)) {
124
+ issues.push(`Duplicate stage name '${stage.name}'`);
125
+ }
126
+ allNames.add(stage.name);
127
+ // Conflicting output paths within group
128
+ const outputPath = stage.type === "agent" ? stage.output :
129
+ stage.type === "pge" ? stage.contract?.deliverable :
130
+ stage.type === "autoresearch" ? stage.output :
131
+ undefined;
132
+ if (outputPath) {
133
+ const existing = groupOutputs.get(outputPath);
134
+ if (existing) {
135
+ issues.push(`Stages '${existing}' and '${stage.name}' in the same parallel group both write to '${outputPath}'`);
136
+ }
137
+ groupOutputs.set(outputPath, stage.name);
138
+ }
139
+ }
140
+ }
141
+ else {
142
+ // Regular stage
143
+ const stage = entry;
144
+ if (allNames.has(stage.name)) {
145
+ issues.push(`Duplicate stage name '${stage.name}'`);
146
+ }
147
+ allNames.add(stage.name);
148
+ }
149
+ }
150
+ for (const issue of issues) {
151
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: issue });
152
+ }
97
153
  });
98
154
  // ---------------------------------------------------------------------------
99
155
  // Public API
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
package/dist/runner.js CHANGED
@@ -11,7 +11,8 @@ import { loadPipeline } from "./pipeline.js";
11
11
  import { runPgeCycle } from "./pge.js";
12
12
  import { interpolate, resolveTaskBody, loadAgentMarkdown, buildTaskContext, writeSystemPromptFile } from "./prompt.js";
13
13
  import { updatePipelineStatus, notifyPipelineComplete } from "./tui/cmux.js";
14
- import { createState, saveState, saveStateWithEvent, updateStageStatus, finishPipeline, findResumePoint, } from "./state.js";
14
+ import { createState, flattenStageEntries, saveState, saveStateWithEvent, updateStageStatus, finishPipeline, findResumePoint, } from "./state.js";
15
+ import { isParallelGroup } from "./types.js";
15
16
  // ---------------------------------------------------------------------------
16
17
  // Helpers
17
18
  // ---------------------------------------------------------------------------
@@ -379,9 +380,10 @@ async function runPipelineStage(stage, ctx, state) {
379
380
  logger.log(` (sub-pipeline not loadable — will resolve at runtime)`);
380
381
  return { stageName: stage.name, status: "passed", durationMs: 0 };
381
382
  }
382
- logger.log(` sub-pipeline: ${childPipeline.name} (${childPipeline.stages.length} stages)`);
383
- for (const s of childPipeline.stages) {
384
- logger.log(` - ${s.name} (${s.type})`);
383
+ const flatStages = flattenStageEntries(childPipeline.stages);
384
+ logger.log(` sub-pipeline: ${childPipeline.name} (${flatStages.length} stages)`);
385
+ for (const s of flatStages) {
386
+ logger.log(` - ${s.name} (${s.type})${s.groupId ? ` [${s.groupId}]` : ""}`);
385
387
  }
386
388
  return { stageName: stage.name, status: "passed", durationMs: 0 };
387
389
  }
@@ -478,11 +480,153 @@ async function runStage(stage, ctx, state) {
478
480
  return runPipelineStage(stage, ctx, state);
479
481
  }
480
482
  }
483
+ function buildExecutionPlan(entries) {
484
+ const plan = [];
485
+ for (const entry of entries) {
486
+ if (isParallelGroup(entry)) {
487
+ plan.push({
488
+ kind: "parallel",
489
+ stages: entry.parallel.stages,
490
+ onFailure: entry.parallel.on_failure ?? "fail_fast",
491
+ });
492
+ }
493
+ else {
494
+ plan.push({
495
+ kind: "sequential",
496
+ stages: [entry],
497
+ });
498
+ }
499
+ }
500
+ return plan;
501
+ }
502
+ /** Compute the total number of stages across all execution steps. */
503
+ function countTotalStages(plan) {
504
+ return plan.reduce((sum, step) => sum + step.stages.length, 0);
505
+ }
506
+ /**
507
+ * Run a single stage with full lifecycle: mark in_progress, dispatch, persist
508
+ * result, and log. Extracted from the main loop so it can be reused in both
509
+ * sequential and parallel execution paths.
510
+ */
511
+ async function runStageWithLifecycle(stage, ctx, state, stageIndex, totalStages) {
512
+ const stageStart = Date.now();
513
+ getLogger(ctx).log(`▸ Stage: ${stage.name} (${stage.type})`);
514
+ if (!ctx.dryRun) {
515
+ updateStageStatus(state, stage.name, "in_progress");
516
+ await saveStateWithEvent(state, "stage_start", stage.name);
517
+ await updatePipelineStatus(stage.name, stageIndex, totalStages);
518
+ }
519
+ try {
520
+ const result = await runStage(stage, ctx, state);
521
+ if (!ctx.dryRun) {
522
+ updateStageStatus(state, stage.name, result.status, {
523
+ durationMs: result.durationMs,
524
+ error: result.error,
525
+ });
526
+ await saveStateWithEvent(state, "stage_complete", stage.name, {
527
+ status: result.status,
528
+ durationMs: result.durationMs,
529
+ });
530
+ }
531
+ if (result.status === "failed" || result.status === "error") {
532
+ getLogger(ctx).log(` ✗ ${stage.name}: ${result.status}${result.error ? ` — ${result.error}` : ""}`);
533
+ }
534
+ else {
535
+ const duration = result.durationMs > 0
536
+ ? ` (${(result.durationMs / 1000).toFixed(1)}s)`
537
+ : "";
538
+ getLogger(ctx).log(` ✓ ${stage.name}: ${result.status}${duration}`);
539
+ }
540
+ return result;
541
+ }
542
+ catch (err) {
543
+ const message = err instanceof Error ? err.message : String(err);
544
+ const result = {
545
+ stageName: stage.name,
546
+ status: "error",
547
+ error: message,
548
+ durationMs: Date.now() - stageStart,
549
+ };
550
+ if (!ctx.dryRun) {
551
+ updateStageStatus(state, stage.name, "error", { error: message });
552
+ await saveState(state);
553
+ }
554
+ getLogger(ctx).error(` ✗ ${stage.name}: error — ${message}`);
555
+ return result;
556
+ }
557
+ }
558
+ /**
559
+ * Run a group of stages in parallel. Returns all results once the group
560
+ * completes (or a failure triggers early exit in fail_fast mode).
561
+ */
562
+ async function runParallelGroup(step, ctx, state, completedNames, stageIndexOffset, totalStages) {
563
+ // Filter to only stages that need running (not already completed on resume).
564
+ const toRun = step.stages.filter(s => !completedNames.has(s.name));
565
+ // Collect results for already-completed stages in this group.
566
+ const skippedResults = step.stages
567
+ .filter(s => completedNames.has(s.name))
568
+ .map(s => {
569
+ const stageState = state.stages[s.name];
570
+ getLogger(ctx).log(` ⏭ ${s.name}: already ${stageState?.status ?? "passed"}`);
571
+ return {
572
+ stageName: s.name,
573
+ status: stageState?.status ?? "passed",
574
+ durationMs: stageState?.durationMs ?? 0,
575
+ };
576
+ });
577
+ if (toRun.length === 0)
578
+ return skippedResults;
579
+ const stageNames = toRun.map(s => s.name).join(", ");
580
+ getLogger(ctx).log(`▸ Parallel group: [${stageNames}]`);
581
+ if (step.onFailure === "wait_all") {
582
+ // Wait for all stages regardless of individual failures.
583
+ const settled = await Promise.allSettled(toRun.map((stage, i) => runStageWithLifecycle(stage, ctx, state, stageIndexOffset + i, totalStages)));
584
+ const results = settled.map((r, i) => r.status === "fulfilled"
585
+ ? r.value
586
+ : {
587
+ stageName: toRun[i].name,
588
+ status: "error",
589
+ error: r.reason instanceof Error ? r.reason.message : String(r.reason),
590
+ durationMs: 0,
591
+ });
592
+ return [...skippedResults, ...results];
593
+ }
594
+ // fail_fast: use a shared flag so that stages that haven't started their
595
+ // dispatch yet can bail out early. Already-running subprocesses finish
596
+ // naturally (we can't safely kill them mid-file-write).
597
+ let failed = false;
598
+ const promises = toRun.map(async (stage, i) => {
599
+ // Yield to let other stages start concurrently, then check flag.
600
+ await Promise.resolve();
601
+ if (failed) {
602
+ const result = {
603
+ stageName: stage.name,
604
+ status: "skipped",
605
+ error: "Sibling stage failed (fail_fast)",
606
+ durationMs: 0,
607
+ };
608
+ if (!ctx.dryRun) {
609
+ updateStageStatus(state, stage.name, "skipped", { error: result.error });
610
+ await saveState(state);
611
+ }
612
+ getLogger(ctx).log(` ⏭ ${stage.name}: skipped (sibling failed)`);
613
+ return result;
614
+ }
615
+ const result = await runStageWithLifecycle(stage, ctx, state, stageIndexOffset + i, totalStages);
616
+ if (result.status === "failed" || result.status === "error") {
617
+ failed = true;
618
+ }
619
+ return result;
620
+ });
621
+ return [...skippedResults, ...await Promise.all(promises)];
622
+ }
481
623
  /**
482
624
  * Execute the stage loop for a pipeline. Used by both top-level `runPipeline()`
483
625
  * and nested `runPipelineStage()`. Handles resume, state persistence, and
484
626
  * stage routing — but not lifecycle concerns (DB row creation, notifications,
485
627
  * gate strategy setup, temp file cleanup).
628
+ *
629
+ * Supports both sequential stages and parallel groups via ExecutionStep plan.
486
630
  */
487
631
  async function runStages(ctx, existingState) {
488
632
  const start = Date.now();
@@ -490,13 +634,16 @@ async function runStages(ctx, existingState) {
490
634
  let pipelineStatus = "passed";
491
635
  // --- State initialization ---
492
636
  let state;
493
- let skipUntilIndex = -1;
637
+ const completedNames = new Set();
494
638
  if (existingState) {
495
639
  state = existingState;
496
640
  state.status = "running";
497
641
  const resume = findResumePoint(state);
498
642
  if (resume) {
499
- skipUntilIndex = resume.stageIndex;
643
+ // Collect names of already-completed stages for skip logic.
644
+ for (let i = 0; i < resume.stageIndex; i++) {
645
+ completedNames.add(state.stageOrder[i]);
646
+ }
500
647
  getLogger(ctx).log(` resuming "${ctx.pipeline.name}" from stage "${resume.stageName}"`);
501
648
  }
502
649
  else {
@@ -513,73 +660,51 @@ async function runStages(ctx, existingState) {
513
660
  }
514
661
  }
515
662
  else {
516
- state = createState(ctx.pipeline.name, ctx.project, ctx.pipelineFile, ctx.pipeline.stages.map((s) => ({ name: s.name, type: s.type })), ctx.artifactDir, ctx.projectDir);
663
+ state = createState(ctx.pipeline.name, ctx.project, ctx.pipelineFile, flattenStageEntries(ctx.pipeline.stages), ctx.artifactDir, ctx.projectDir);
517
664
  }
518
665
  if (!ctx.dryRun) {
519
666
  await saveState(state);
520
667
  }
521
- for (let i = 0; i < ctx.pipeline.stages.length; i++) {
522
- const stage = ctx.pipeline.stages[i];
523
- // Skip completed stages on resume.
524
- if (existingState && i < skipUntilIndex) {
525
- const stageState = state.stages[stage.name];
526
- if (stageState?.status === "passed" || stageState?.status === "skipped") {
527
- getLogger(ctx).log(` ⏭ ${stage.name}: already ${stageState.status}`);
528
- results.push({
529
- stageName: stage.name,
530
- status: stageState.status,
531
- durationMs: stageState.durationMs ?? 0,
532
- });
533
- continue;
668
+ // Build execution plan from pipeline entries.
669
+ const plan = buildExecutionPlan(ctx.pipeline.stages);
670
+ const totalStages = countTotalStages(plan);
671
+ let stageIndexOffset = 0;
672
+ for (const step of plan) {
673
+ if (step.kind === "sequential") {
674
+ const stage = step.stages[0];
675
+ // Skip completed stages on resume.
676
+ if (completedNames.has(stage.name)) {
677
+ const stageState = state.stages[stage.name];
678
+ if (stageState?.status === "passed" || stageState?.status === "skipped") {
679
+ getLogger(ctx).log(` ⏭ ${stage.name}: already ${stageState.status}`);
680
+ results.push({
681
+ stageName: stage.name,
682
+ status: stageState.status,
683
+ durationMs: stageState.durationMs ?? 0,
684
+ });
685
+ stageIndexOffset += 1;
686
+ continue;
687
+ }
534
688
  }
535
- }
536
- getLogger(ctx).log(`▸ Stage: ${stage.name} (${stage.type})`);
537
- // Mark in_progress in state + update cmux.
538
- if (!ctx.dryRun) {
539
- updateStageStatus(state, stage.name, "in_progress");
540
- await saveStateWithEvent(state, "stage_start", stage.name);
541
- await updatePipelineStatus(stage.name, i, ctx.pipeline.stages.length);
542
- }
543
- try {
544
- const result = await runStage(stage, ctx, state);
689
+ const result = await runStageWithLifecycle(stage, ctx, state, stageIndexOffset, totalStages);
545
690
  results.push(result);
546
- // Persist stage result to state.
547
- if (!ctx.dryRun) {
548
- updateStageStatus(state, stage.name, result.status, {
549
- durationMs: result.durationMs,
550
- error: result.error,
551
- });
552
- await saveStateWithEvent(state, "stage_complete", stage.name, {
553
- status: result.status,
554
- durationMs: result.durationMs,
555
- });
556
- }
557
691
  if (result.status === "failed" || result.status === "error") {
558
- pipelineStatus = "failed";
559
- getLogger(ctx).log(` ✗ ${stage.name}: ${result.status}${result.error ? ` — ${result.error}` : ""}`);
692
+ pipelineStatus = result.status === "error" ? "error" : "failed";
560
693
  break;
561
694
  }
562
- const duration = result.durationMs > 0
563
- ? ` (${(result.durationMs / 1000).toFixed(1)}s)`
564
- : "";
565
- getLogger(ctx).log(` ✓ ${stage.name}: ${result.status}${duration}`);
566
695
  }
567
- catch (err) {
568
- const message = err instanceof Error ? err.message : String(err);
569
- results.push({
570
- stageName: stage.name,
571
- status: "error",
572
- error: message,
573
- durationMs: Date.now() - start,
574
- });
575
- pipelineStatus = "error";
576
- if (!ctx.dryRun) {
577
- updateStageStatus(state, stage.name, "error", { error: message });
578
- await saveState(state);
696
+ else {
697
+ // Parallel group
698
+ const groupResults = await runParallelGroup(step, ctx, state, completedNames, stageIndexOffset, totalStages);
699
+ results.push(...groupResults);
700
+ const anyFailed = groupResults.some(r => r.status === "failed" || r.status === "error");
701
+ if (anyFailed) {
702
+ const hasError = groupResults.some(r => r.status === "error");
703
+ pipelineStatus = hasError ? "error" : "failed";
704
+ break;
579
705
  }
580
- getLogger(ctx).error(` ✗ ${stage.name}: error — ${message}`);
581
- break;
582
706
  }
707
+ stageIndexOffset += step.stages.length;
583
708
  }
584
709
  // Finalize state.
585
710
  if (!ctx.dryRun) {
@@ -607,7 +732,7 @@ export async function runPipeline(ctx, opts) {
607
732
  // Create gate strategy if not provided.
608
733
  if (!ctx.gateStrategy && !ctx.headless && !ctx.dryRun) {
609
734
  // Need a runId — use existing state or generate one via createState.
610
- const tempState = opts?.existingState ?? createState(ctx.pipeline.name, ctx.project, ctx.pipelineFile, ctx.pipeline.stages.map((s) => ({ name: s.name, type: s.type })), ctx.artifactDir, ctx.projectDir);
735
+ const tempState = opts?.existingState ?? createState(ctx.pipeline.name, ctx.project, ctx.pipelineFile, flattenStageEntries(ctx.pipeline.stages), ctx.artifactDir, ctx.projectDir);
611
736
  ctx.gateStrategy = new FilesystemGateStrategy(tempState.runId, ctx.projectDir, ctx.quiet);
612
737
  }
613
738
  // Initialize visited pipelines for cycle detection.