@auto-engineer/pipeline 0.0.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.
- package/.turbo/turbo-build.log +5 -0
- package/LICENSE +10 -0
- package/claude.md +160 -0
- package/dist/src/builder/define.d.ts +90 -0
- package/dist/src/builder/define.d.ts.map +1 -0
- package/dist/src/builder/define.js +425 -0
- package/dist/src/builder/define.js.map +1 -0
- package/dist/src/builder/define.specs.d.ts +2 -0
- package/dist/src/builder/define.specs.d.ts.map +1 -0
- package/dist/src/builder/define.specs.js +435 -0
- package/dist/src/builder/define.specs.js.map +1 -0
- package/dist/src/config/pipeline-config.d.ts +13 -0
- package/dist/src/config/pipeline-config.d.ts.map +1 -0
- package/dist/src/config/pipeline-config.js +15 -0
- package/dist/src/config/pipeline-config.js.map +1 -0
- package/dist/src/core/descriptors.d.ts +84 -0
- package/dist/src/core/descriptors.d.ts.map +1 -0
- package/dist/src/core/descriptors.js +2 -0
- package/dist/src/core/descriptors.js.map +1 -0
- package/dist/src/core/descriptors.specs.d.ts +2 -0
- package/dist/src/core/descriptors.specs.d.ts.map +1 -0
- package/dist/src/core/descriptors.specs.js +24 -0
- package/dist/src/core/descriptors.specs.js.map +1 -0
- package/dist/src/core/types.d.ts +10 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +4 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/core/types.specs.d.ts +2 -0
- package/dist/src/core/types.specs.d.ts.map +1 -0
- package/dist/src/core/types.specs.js +40 -0
- package/dist/src/core/types.specs.js.map +1 -0
- package/dist/src/graph/types.d.ts +17 -0
- package/dist/src/graph/types.d.ts.map +1 -0
- package/dist/src/graph/types.js +2 -0
- package/dist/src/graph/types.js.map +1 -0
- package/dist/src/graph/types.specs.d.ts +2 -0
- package/dist/src/graph/types.specs.d.ts.map +1 -0
- package/dist/src/graph/types.specs.js +148 -0
- package/dist/src/graph/types.specs.js.map +1 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logging/event-logger.d.ts +21 -0
- package/dist/src/logging/event-logger.d.ts.map +1 -0
- package/dist/src/logging/event-logger.js +31 -0
- package/dist/src/logging/event-logger.js.map +1 -0
- package/dist/src/logging/event-logger.specs.d.ts +2 -0
- package/dist/src/logging/event-logger.specs.d.ts.map +1 -0
- package/dist/src/logging/event-logger.specs.js +81 -0
- package/dist/src/logging/event-logger.specs.js.map +1 -0
- package/dist/src/plugins/handler-adapter.d.ts +5 -0
- package/dist/src/plugins/handler-adapter.d.ts.map +1 -0
- package/dist/src/plugins/handler-adapter.js +17 -0
- package/dist/src/plugins/handler-adapter.js.map +1 -0
- package/dist/src/plugins/handler-adapter.specs.d.ts +2 -0
- package/dist/src/plugins/handler-adapter.specs.d.ts.map +1 -0
- package/dist/src/plugins/handler-adapter.specs.js +129 -0
- package/dist/src/plugins/handler-adapter.specs.js.map +1 -0
- package/dist/src/plugins/plugin-loader.d.ts +25 -0
- package/dist/src/plugins/plugin-loader.d.ts.map +1 -0
- package/dist/src/plugins/plugin-loader.js +150 -0
- package/dist/src/plugins/plugin-loader.js.map +1 -0
- package/dist/src/plugins/plugin-loader.specs.d.ts +2 -0
- package/dist/src/plugins/plugin-loader.specs.d.ts.map +1 -0
- package/dist/src/plugins/plugin-loader.specs.js +246 -0
- package/dist/src/plugins/plugin-loader.specs.js.map +1 -0
- package/dist/src/runtime/await-tracker.d.ts +10 -0
- package/dist/src/runtime/await-tracker.d.ts.map +1 -0
- package/dist/src/runtime/await-tracker.js +42 -0
- package/dist/src/runtime/await-tracker.js.map +1 -0
- package/dist/src/runtime/await-tracker.specs.d.ts +2 -0
- package/dist/src/runtime/await-tracker.specs.d.ts.map +1 -0
- package/dist/src/runtime/await-tracker.specs.js +46 -0
- package/dist/src/runtime/await-tracker.specs.js.map +1 -0
- package/dist/src/runtime/context.d.ts +12 -0
- package/dist/src/runtime/context.d.ts.map +1 -0
- package/dist/src/runtime/context.js +2 -0
- package/dist/src/runtime/context.js.map +1 -0
- package/dist/src/runtime/context.specs.d.ts +2 -0
- package/dist/src/runtime/context.specs.d.ts.map +1 -0
- package/dist/src/runtime/context.specs.js +26 -0
- package/dist/src/runtime/context.specs.js.map +1 -0
- package/dist/src/runtime/event-command-map.d.ts +15 -0
- package/dist/src/runtime/event-command-map.d.ts.map +1 -0
- package/dist/src/runtime/event-command-map.js +26 -0
- package/dist/src/runtime/event-command-map.js.map +1 -0
- package/dist/src/runtime/event-command-map.specs.d.ts +2 -0
- package/dist/src/runtime/event-command-map.specs.d.ts.map +1 -0
- package/dist/src/runtime/event-command-map.specs.js +108 -0
- package/dist/src/runtime/event-command-map.specs.js.map +1 -0
- package/dist/src/runtime/phased-executor.d.ts +29 -0
- package/dist/src/runtime/phased-executor.d.ts.map +1 -0
- package/dist/src/runtime/phased-executor.js +164 -0
- package/dist/src/runtime/phased-executor.js.map +1 -0
- package/dist/src/runtime/phased-executor.specs.d.ts +2 -0
- package/dist/src/runtime/phased-executor.specs.d.ts.map +1 -0
- package/dist/src/runtime/phased-executor.specs.js +256 -0
- package/dist/src/runtime/phased-executor.specs.js.map +1 -0
- package/dist/src/runtime/pipeline-runtime.d.ts +17 -0
- package/dist/src/runtime/pipeline-runtime.d.ts.map +1 -0
- package/dist/src/runtime/pipeline-runtime.js +87 -0
- package/dist/src/runtime/pipeline-runtime.js.map +1 -0
- package/dist/src/runtime/pipeline-runtime.specs.d.ts +2 -0
- package/dist/src/runtime/pipeline-runtime.specs.d.ts.map +1 -0
- package/dist/src/runtime/pipeline-runtime.specs.js +192 -0
- package/dist/src/runtime/pipeline-runtime.specs.js.map +1 -0
- package/dist/src/runtime/settled-tracker.d.ts +42 -0
- package/dist/src/runtime/settled-tracker.d.ts.map +1 -0
- package/dist/src/runtime/settled-tracker.js +161 -0
- package/dist/src/runtime/settled-tracker.js.map +1 -0
- package/dist/src/runtime/settled-tracker.specs.d.ts +2 -0
- package/dist/src/runtime/settled-tracker.specs.d.ts.map +1 -0
- package/dist/src/runtime/settled-tracker.specs.js +361 -0
- package/dist/src/runtime/settled-tracker.specs.js.map +1 -0
- package/dist/src/server/full-orchestration.e2e.specs.d.ts +2 -0
- package/dist/src/server/full-orchestration.e2e.specs.d.ts.map +1 -0
- package/dist/src/server/full-orchestration.e2e.specs.js +561 -0
- package/dist/src/server/full-orchestration.e2e.specs.js.map +1 -0
- package/dist/src/server/pipeline-server.d.ts +59 -0
- package/dist/src/server/pipeline-server.d.ts.map +1 -0
- package/dist/src/server/pipeline-server.e2e.specs.d.ts +2 -0
- package/dist/src/server/pipeline-server.e2e.specs.d.ts.map +1 -0
- package/dist/src/server/pipeline-server.e2e.specs.js +381 -0
- package/dist/src/server/pipeline-server.e2e.specs.js.map +1 -0
- package/dist/src/server/pipeline-server.js +527 -0
- package/dist/src/server/pipeline-server.js.map +1 -0
- package/dist/src/server/pipeline-server.specs.d.ts +2 -0
- package/dist/src/server/pipeline-server.specs.d.ts.map +1 -0
- package/dist/src/server/pipeline-server.specs.js +662 -0
- package/dist/src/server/pipeline-server.specs.js.map +1 -0
- package/dist/src/server/sse-manager.d.ts +12 -0
- package/dist/src/server/sse-manager.d.ts.map +1 -0
- package/dist/src/server/sse-manager.js +63 -0
- package/dist/src/server/sse-manager.js.map +1 -0
- package/dist/src/server/sse-manager.specs.d.ts +2 -0
- package/dist/src/server/sse-manager.specs.d.ts.map +1 -0
- package/dist/src/server/sse-manager.specs.js +158 -0
- package/dist/src/server/sse-manager.specs.js.map +1 -0
- package/dist/src/testing/event-capture.d.ts +14 -0
- package/dist/src/testing/event-capture.d.ts.map +1 -0
- package/dist/src/testing/event-capture.js +55 -0
- package/dist/src/testing/event-capture.js.map +1 -0
- package/dist/src/testing/event-capture.specs.d.ts +2 -0
- package/dist/src/testing/event-capture.specs.d.ts.map +1 -0
- package/dist/src/testing/event-capture.specs.js +114 -0
- package/dist/src/testing/event-capture.specs.js.map +1 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.d.ts +7 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.d.ts.map +1 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.js +168 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.js.map +1 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.specs.d.ts +2 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.specs.d.ts.map +1 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.specs.js +263 -0
- package/dist/src/testing/fixtures/kanban-full.pipeline.specs.js.map +1 -0
- package/dist/src/testing/fixtures/kanban-todo.config.d.ts +3 -0
- package/dist/src/testing/fixtures/kanban-todo.config.d.ts.map +1 -0
- package/dist/src/testing/fixtures/kanban-todo.config.js +19 -0
- package/dist/src/testing/fixtures/kanban-todo.config.js.map +1 -0
- package/dist/src/testing/fixtures/kanban.pipeline.d.ts +5 -0
- package/dist/src/testing/fixtures/kanban.pipeline.d.ts.map +1 -0
- package/dist/src/testing/fixtures/kanban.pipeline.js +76 -0
- package/dist/src/testing/fixtures/kanban.pipeline.js.map +1 -0
- package/dist/src/testing/fixtures/kanban.pipeline.specs.d.ts +2 -0
- package/dist/src/testing/fixtures/kanban.pipeline.specs.d.ts.map +1 -0
- package/dist/src/testing/fixtures/kanban.pipeline.specs.js +29 -0
- package/dist/src/testing/fixtures/kanban.pipeline.specs.js.map +1 -0
- package/dist/src/testing/kanban-todo.e2e.specs.d.ts +2 -0
- package/dist/src/testing/kanban-todo.e2e.specs.d.ts.map +1 -0
- package/dist/src/testing/kanban-todo.e2e.specs.js +160 -0
- package/dist/src/testing/kanban-todo.e2e.specs.js.map +1 -0
- package/dist/src/testing/mock-handlers.d.ts +21 -0
- package/dist/src/testing/mock-handlers.d.ts.map +1 -0
- package/dist/src/testing/mock-handlers.js +34 -0
- package/dist/src/testing/mock-handlers.js.map +1 -0
- package/dist/src/testing/mock-handlers.specs.d.ts +2 -0
- package/dist/src/testing/mock-handlers.specs.d.ts.map +1 -0
- package/dist/src/testing/mock-handlers.specs.js +193 -0
- package/dist/src/testing/mock-handlers.specs.js.map +1 -0
- package/dist/src/testing/real-execution.e2e.specs.d.ts +2 -0
- package/dist/src/testing/real-execution.e2e.specs.d.ts.map +1 -0
- package/dist/src/testing/real-execution.e2e.specs.js +140 -0
- package/dist/src/testing/real-execution.e2e.specs.js.map +1 -0
- package/dist/src/testing/real-plugin.e2e.specs.d.ts +2 -0
- package/dist/src/testing/real-plugin.e2e.specs.d.ts.map +1 -0
- package/dist/src/testing/real-plugin.e2e.specs.js +65 -0
- package/dist/src/testing/real-plugin.e2e.specs.js.map +1 -0
- package/dist/src/testing/server-startup.e2e.specs.d.ts +2 -0
- package/dist/src/testing/server-startup.e2e.specs.d.ts.map +1 -0
- package/dist/src/testing/server-startup.e2e.specs.js +104 -0
- package/dist/src/testing/server-startup.e2e.specs.js.map +1 -0
- package/dist/src/testing/snapshot-compare.d.ts +18 -0
- package/dist/src/testing/snapshot-compare.d.ts.map +1 -0
- package/dist/src/testing/snapshot-compare.js +86 -0
- package/dist/src/testing/snapshot-compare.js.map +1 -0
- package/dist/src/testing/snapshot-compare.specs.d.ts +2 -0
- package/dist/src/testing/snapshot-compare.specs.d.ts.map +1 -0
- package/dist/src/testing/snapshot-compare.specs.js +112 -0
- package/dist/src/testing/snapshot-compare.specs.js.map +1 -0
- package/dist/src/testing/snapshot-sanitize.d.ts +8 -0
- package/dist/src/testing/snapshot-sanitize.d.ts.map +1 -0
- package/dist/src/testing/snapshot-sanitize.js +10 -0
- package/dist/src/testing/snapshot-sanitize.js.map +1 -0
- package/dist/src/testing/snapshot-sanitize.specs.d.ts +2 -0
- package/dist/src/testing/snapshot-sanitize.specs.d.ts.map +1 -0
- package/dist/src/testing/snapshot-sanitize.specs.js +104 -0
- package/dist/src/testing/snapshot-sanitize.specs.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/testing-analysis.md +395 -0
- package/package.json +31 -0
- package/pipeline-api-new.md +1078 -0
- package/pomodoro-plan.md +651 -0
- package/scripts/run-kanban-e2e.ts +219 -0
- package/scripts/start-server.ts +64 -0
- package/snapshots/e2e-run-2025-12-22T15-52-03.json +613 -0
- package/snapshots/e2e-run-2025-12-22T16-51-30.json +699 -0
- package/src/builder/define.specs.ts +531 -0
- package/src/builder/define.ts +700 -0
- package/src/config/pipeline-config.ts +32 -0
- package/src/core/descriptors.specs.ts +28 -0
- package/src/core/descriptors.ts +99 -0
- package/src/core/types.specs.ts +44 -0
- package/src/core/types.ts +16 -0
- package/src/graph/types.specs.ts +176 -0
- package/src/graph/types.ts +19 -0
- package/src/index.ts +54 -0
- package/src/logging/event-logger.specs.ts +100 -0
- package/src/logging/event-logger.ts +50 -0
- package/src/plugins/handler-adapter.specs.ts +164 -0
- package/src/plugins/handler-adapter.ts +21 -0
- package/src/plugins/plugin-loader.specs.ts +295 -0
- package/src/plugins/plugin-loader.ts +202 -0
- package/src/runtime/await-tracker.specs.ts +52 -0
- package/src/runtime/await-tracker.ts +50 -0
- package/src/runtime/context.specs.ts +28 -0
- package/src/runtime/context.ts +13 -0
- package/src/runtime/event-command-map.specs.ts +136 -0
- package/src/runtime/event-command-map.ts +38 -0
- package/src/runtime/phased-executor.specs.ts +358 -0
- package/src/runtime/phased-executor.ts +224 -0
- package/src/runtime/pipeline-runtime.specs.ts +214 -0
- package/src/runtime/pipeline-runtime.ts +119 -0
- package/src/runtime/settled-tracker.specs.ts +448 -0
- package/src/runtime/settled-tracker.ts +237 -0
- package/src/server/full-orchestration.e2e.specs.ts +672 -0
- package/src/server/pipeline-server.e2e.specs.ts +505 -0
- package/src/server/pipeline-server.specs.ts +761 -0
- package/src/server/pipeline-server.ts +656 -0
- package/src/server/sse-manager.specs.ts +208 -0
- package/src/server/sse-manager.ts +79 -0
- package/src/testing/event-capture.specs.ts +143 -0
- package/src/testing/event-capture.ts +65 -0
- package/src/testing/fixtures/kanban-full.pipeline.specs.ts +337 -0
- package/src/testing/fixtures/kanban-full.pipeline.ts +225 -0
- package/src/testing/fixtures/kanban-todo.config.ts +19 -0
- package/src/testing/fixtures/kanban.pipeline.specs.ts +33 -0
- package/src/testing/fixtures/kanban.pipeline.ts +124 -0
- package/src/testing/kanban-todo.e2e.specs.ts +209 -0
- package/src/testing/mock-handlers.specs.ts +229 -0
- package/src/testing/mock-handlers.ts +58 -0
- package/src/testing/real-execution.e2e.specs.ts +193 -0
- package/src/testing/real-plugin.e2e.specs.ts +94 -0
- package/src/testing/server-startup.e2e.specs.ts +162 -0
- package/src/testing/snapshot-compare.specs.ts +136 -0
- package/src/testing/snapshot-compare.ts +106 -0
- package/src/testing/snapshot-sanitize.specs.ts +131 -0
- package/src/testing/snapshot-sanitize.ts +17 -0
- package/tsconfig.json +11 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +29 -0
|
@@ -0,0 +1,1078 @@
|
|
|
1
|
+
# Pipeline System Architecture
|
|
2
|
+
|
|
3
|
+
> **TL;DR:** A fluent API with succinct pipeline definitions, zero manual state management, and full graph visualization support.
|
|
4
|
+
>
|
|
5
|
+
> **[Jump to the complete example →](#4-complete-example-kanban-pipeline)**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Executive Summary](#executive-summary)
|
|
12
|
+
- [1. The Problem](#1-the-problem)
|
|
13
|
+
- [What We're Building](#what-were-building)
|
|
14
|
+
- [Current State](#current-state)
|
|
15
|
+
- [Requirements](#requirements)
|
|
16
|
+
- [2. Design Philosophy](#2-design-philosophy)
|
|
17
|
+
- [The API Should Read Like a Description](#the-api-should-read-like-a-description)
|
|
18
|
+
- [Three Complexity Levels](#three-complexity-levels)
|
|
19
|
+
- [Familiar Mental Models](#familiar-mental-models)
|
|
20
|
+
- [3. Complete API Specification](#3-complete-api-specification)
|
|
21
|
+
- [3.1 Pipeline Builder](#31-pipeline-builder)
|
|
22
|
+
- [3.2 Trigger Builder](#32-trigger-builder)
|
|
23
|
+
- [3.3 Emit Chain](#33-emit-chain)
|
|
24
|
+
- [3.4 Run Builder (Scatter)](#34-run-builder-scatter)
|
|
25
|
+
- [3.5 Gather Builder (Await Results)](#35-gather-builder-await-results)
|
|
26
|
+
- [3.6 Failure Context (The Retry API)](#36-failure-context-the-retry-api)
|
|
27
|
+
- [3.7 ForEach Builder (Phased Execution)](#37-foreach-builder-phased-execution)
|
|
28
|
+
- [3.8 Context Types](#38-context-types)
|
|
29
|
+
- [3.9 Handler Context](#39-handler-context-for-handle)
|
|
30
|
+
- [3.10 Helper Function](#310-helper-function)
|
|
31
|
+
- [4. Complete Example: Kanban Pipeline](#4-complete-example-kanban-pipeline)
|
|
32
|
+
- [5. Side-by-Side Comparison](#5-side-by-side-comparison)
|
|
33
|
+
- [6. Graph Extraction](#6-graph-extraction)
|
|
34
|
+
- [Graph IR Structure](#graph-ir-structure)
|
|
35
|
+
- [Example Output](#example-output)
|
|
36
|
+
- [Visualization](#visualization)
|
|
37
|
+
- [7. Pattern Templates (Optional)](#7-pattern-templates-optional-extension)
|
|
38
|
+
- [8. Runtime Behavior](#8-runtime-behavior)
|
|
39
|
+
- [8.1 Execution Flow](#81-execution-flow)
|
|
40
|
+
- [8.2 State Management](#82-state-management)
|
|
41
|
+
- [9. Summary](#10-summary)
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Executive Summary
|
|
46
|
+
|
|
47
|
+
This document specifies a fluent API for defining event-driven pipelines that orchestrate AI-powered code generation workflows. The API is designed to:
|
|
48
|
+
|
|
49
|
+
1. **Read like English** — Pipeline definitions describe _what happens_, not _how to wire it_
|
|
50
|
+
2. **Handle real complexity** — Scatter-gather-retry and phased execution are first-class primitives
|
|
51
|
+
3. **Enable visualization** — Static graph extraction without running the pipeline
|
|
52
|
+
4. **Run anywhere** — Same definition works locally and on Cloudflare
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 1. The Problem
|
|
57
|
+
|
|
58
|
+
### What We're Building
|
|
59
|
+
|
|
60
|
+
The **Pipeline System** orchestrates dozens of AI-powered plugins into coherent workflows. A pipeline configuration defines:
|
|
61
|
+
|
|
62
|
+
- Which events trigger which commands
|
|
63
|
+
- How to handle failures and retries
|
|
64
|
+
- How work flows through phases
|
|
65
|
+
- When to run things in parallel vs. sequentially
|
|
66
|
+
|
|
67
|
+
### Current State
|
|
68
|
+
|
|
69
|
+
Today, pipeline configs are imperative TypeScript with:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Scattered mutable state
|
|
73
|
+
const sliceRetryState = new Map<string, number>()
|
|
74
|
+
let clientComponents = []
|
|
75
|
+
const processedComponents = new Set()
|
|
76
|
+
const failedComponents = new Set()
|
|
77
|
+
const dispatchedPhases = new Set()
|
|
78
|
+
|
|
79
|
+
// Implicit event relationships
|
|
80
|
+
on('SliceImplemented', (e) => dispatch('CheckTests', {...}))
|
|
81
|
+
on('SliceImplemented', (e) => dispatch('CheckTypes', {...}))
|
|
82
|
+
on('SliceImplemented', (e) => dispatch('CheckLint', {...}))
|
|
83
|
+
|
|
84
|
+
// Complex aggregation with unclear semantics
|
|
85
|
+
on.settled(['CheckTests', 'CheckTypes', 'CheckLint'],
|
|
86
|
+
dispatch(['ImplementSlice'], (events, send) => {
|
|
87
|
+
const failures = findCheckFailures(events)
|
|
88
|
+
// ... 30 more lines of retry logic
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Problems:**
|
|
94
|
+
|
|
95
|
+
- Hard to understand the flow
|
|
96
|
+
- Impossible to visualize without execution
|
|
97
|
+
- Easy to introduce bugs in state management
|
|
98
|
+
- Boilerplate for common patterns (retry, phases)
|
|
99
|
+
|
|
100
|
+
### Requirements
|
|
101
|
+
|
|
102
|
+
| ID | Requirement |
|
|
103
|
+
| --- | --------------------------------------------------- |
|
|
104
|
+
| R1 | **Easy to write** — New pipeline in <30 mins |
|
|
105
|
+
| R2 | **Plugin compatible** — Commands in, events out |
|
|
106
|
+
| R3 | **Runs locally** — Single-process, no cloud needed |
|
|
107
|
+
| R4 | **Runs on Cloudflare** — Workers, DO, D1, Queues |
|
|
108
|
+
| R5 | **Visualizable** — Static graph extraction |
|
|
109
|
+
| R6 | **Status queryable** — Current state at any time |
|
|
110
|
+
| R7 | **Full history** — Every event stored for debugging |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 2. Design Philosophy
|
|
115
|
+
|
|
116
|
+
### The API Should Read Like a Description
|
|
117
|
+
|
|
118
|
+
**Bad:** Assembly instructions
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
.scatter([...]).gatherBy(...).retryUntilSuccess({...})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Good:** A description of what happens
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
.run([...]).awaitAll({ key: ... }).onFailure(ctx => ctx.retry(...))
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Three Complexity Levels
|
|
131
|
+
|
|
132
|
+
| Level | Use Case | Example |
|
|
133
|
+
| -------------- | --------------- | --------------------------------------- |
|
|
134
|
+
| **Simple** | Event → Command | `.on('A').emit('B', {...})` |
|
|
135
|
+
| **Structured** | Common patterns | `.run([...]).awaitAll().onFailure(...)` |
|
|
136
|
+
| **Custom** | Unique logic | `.handle(ctx => { ... })` |
|
|
137
|
+
|
|
138
|
+
### Familiar Mental Models
|
|
139
|
+
|
|
140
|
+
| Pattern | Inspired By |
|
|
141
|
+
| ------------------------------ | ------------------------ |
|
|
142
|
+
| `.forEach(...).groupInto(...)` | JavaScript array methods |
|
|
143
|
+
| `.run([...]).awaitAll()` | Promise.all |
|
|
144
|
+
| `.when(predicate)` | Stream filtering |
|
|
145
|
+
| `.onFailure(...)` | Error handlers |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 3. Complete API Specification
|
|
150
|
+
|
|
151
|
+
### 3.1 Pipeline Builder
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
function define(name: string): PipelineBuilder;
|
|
155
|
+
|
|
156
|
+
interface PipelineBuilder {
|
|
157
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
158
|
+
// METADATA
|
|
159
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
version(v: string): this;
|
|
162
|
+
description(d: string): this;
|
|
163
|
+
|
|
164
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
165
|
+
// STATE (optional, for custom handlers)
|
|
166
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
withState<S>(initializers: StateInitializers<S>): PipelineBuilder<S>;
|
|
169
|
+
|
|
170
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
171
|
+
// NAMED DEFINITIONS
|
|
172
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
/** Define a reusable correlation key extractor */
|
|
175
|
+
key(name: string, extractor: (event: Event) => string): this;
|
|
176
|
+
|
|
177
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
178
|
+
// EVENT HANDLERS
|
|
179
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
/** Start a handler chain for an event type */
|
|
182
|
+
on<E extends EventType>(eventType: E): TriggerBuilder<E>;
|
|
183
|
+
|
|
184
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
185
|
+
// TERMINAL
|
|
186
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
build(): Pipeline;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 3.2 Trigger Builder
|
|
193
|
+
|
|
194
|
+
After `.on('EventType')`, you can filter, emit, or define complex patterns.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
interface TriggerBuilder<E> {
|
|
198
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
199
|
+
// FILTERING
|
|
200
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
/** Only proceed if predicate returns true */
|
|
203
|
+
when(predicate: (event: E) => boolean): this;
|
|
204
|
+
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
206
|
+
// SIMPLE EMIT
|
|
207
|
+
// One or more commands in parallel
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/** Emit a command (multiple .emit() calls = parallel execution) */
|
|
211
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C> | ((event: E) => CommandData<C>)): EmitChain<E>;
|
|
212
|
+
|
|
213
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
214
|
+
// SCATTER-GATHER PATTERN
|
|
215
|
+
// Run multiple commands, wait for all, handle results
|
|
216
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
/** Run multiple commands in parallel */
|
|
219
|
+
run(commands: CommandSpec[] | ((event: E) => CommandSpec[])): RunBuilder<E>;
|
|
220
|
+
|
|
221
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
222
|
+
// PHASED EXECUTION PATTERN
|
|
223
|
+
// Process items in sequential groups
|
|
224
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
/** Iterate over a collection from the event */
|
|
227
|
+
forEach<T>(selector: (event: E) => T[]): ForEachBuilder<E, T>;
|
|
228
|
+
|
|
229
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
230
|
+
// CUSTOM HANDLER
|
|
231
|
+
// Full imperative access for unique cases
|
|
232
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
/** Custom handler with full context access */
|
|
235
|
+
handle(handler: (event: E, ctx: HandlerContext) => void, meta?: { emits?: string[] }): ChainTerminal;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 3.3 Emit Chain
|
|
240
|
+
|
|
241
|
+
Multiple `.emit()` calls create parallel commands.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
interface EmitChain<E> {
|
|
245
|
+
/** Add another parallel command */
|
|
246
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C> | ((event: E) => CommandData<C>)): this;
|
|
247
|
+
|
|
248
|
+
/** Start a new event handler */
|
|
249
|
+
on<E2 extends EventType>(eventType: E2): TriggerBuilder<E2>;
|
|
250
|
+
|
|
251
|
+
/** End the pipeline definition */
|
|
252
|
+
build(): Pipeline;
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Example: Parallel dispatch**
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
.on('ServerGenerated')
|
|
260
|
+
.emit('GenerateIA', { modelPath: '...' })
|
|
261
|
+
.emit('StartServer', { serverDirectory: '...' })
|
|
262
|
+
// Both commands run in parallel
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 3.4 Run Builder (Scatter)
|
|
266
|
+
|
|
267
|
+
After `.run([...])`, define how to gather and handle results.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
interface RunBuilder<E> {
|
|
271
|
+
/** Wait for all commands to complete, grouped by key */
|
|
272
|
+
awaitAll(config: {
|
|
273
|
+
/** Correlation key (name or extractor function) */
|
|
274
|
+
key: string | ((event: Event) => string);
|
|
275
|
+
/** Timeout in milliseconds */
|
|
276
|
+
timeout?: number;
|
|
277
|
+
}): GatherBuilder<E>;
|
|
278
|
+
|
|
279
|
+
/** Don't wait — fire and forget */
|
|
280
|
+
done(): ChainTerminal;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 3.5 Gather Builder (Await Results)
|
|
285
|
+
|
|
286
|
+
After `.awaitAll()`, handle success or failure.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
interface GatherBuilder<E> {
|
|
290
|
+
/** Handle when any command fails */
|
|
291
|
+
onFailure(handler: (ctx: FailureContext) => void): GatherComplete<E>;
|
|
292
|
+
|
|
293
|
+
/** Handle when all commands succeed */
|
|
294
|
+
onSuccess(handler: (ctx: SuccessContext) => void): GatherComplete<E>;
|
|
295
|
+
|
|
296
|
+
/** Handle all completions (custom logic) */
|
|
297
|
+
onComplete(handler: (ctx: CompleteContext) => void): GatherComplete<E>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
interface GatherComplete<E> {
|
|
301
|
+
/** Add success handler (if failure was defined first) */
|
|
302
|
+
onSuccess(handler: (ctx: SuccessContext) => void): this;
|
|
303
|
+
|
|
304
|
+
/** Add failure handler (if success was defined first) */
|
|
305
|
+
onFailure(handler: (ctx: FailureContext) => void): this;
|
|
306
|
+
|
|
307
|
+
/** Start a new event handler */
|
|
308
|
+
on<E2 extends EventType>(eventType: E2): TriggerBuilder<E2>;
|
|
309
|
+
|
|
310
|
+
/** End the pipeline definition */
|
|
311
|
+
build(): Pipeline;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### 3.6 Failure Context (The Retry API)
|
|
316
|
+
|
|
317
|
+
The failure handler receives a context with retry capabilities.
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
interface FailureContext {
|
|
321
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
322
|
+
// CORRELATION INFO
|
|
323
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
/** The correlation key for this group */
|
|
326
|
+
key: string;
|
|
327
|
+
|
|
328
|
+
/** Current attempt number (1-based) */
|
|
329
|
+
attempt: number;
|
|
330
|
+
|
|
331
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
332
|
+
// FAILURE INFO
|
|
333
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
/** All failure events */
|
|
336
|
+
failures: Event[];
|
|
337
|
+
|
|
338
|
+
/** Concatenated error text from all failures */
|
|
339
|
+
errorText: string;
|
|
340
|
+
|
|
341
|
+
/** All events (success and failure) */
|
|
342
|
+
all: Event[];
|
|
343
|
+
|
|
344
|
+
/** Events grouped by command type */
|
|
345
|
+
byCommand: Record<string, Event[]>;
|
|
346
|
+
|
|
347
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
348
|
+
// RETRY ACTION
|
|
349
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
350
|
+
|
|
351
|
+
/** Retry a command with new data */
|
|
352
|
+
retry<C extends CommandType>(
|
|
353
|
+
commandType: C,
|
|
354
|
+
config: {
|
|
355
|
+
/** Maximum retry attempts */
|
|
356
|
+
maxAttempts: number;
|
|
357
|
+
/** Command data */
|
|
358
|
+
data: CommandData<C>;
|
|
359
|
+
/** Backoff strategy */
|
|
360
|
+
backoff?: 'none' | 'linear' | 'exponential';
|
|
361
|
+
/** Base delay in ms (default: 1000) */
|
|
362
|
+
backoffMs?: number;
|
|
363
|
+
},
|
|
364
|
+
): void;
|
|
365
|
+
|
|
366
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
367
|
+
// EMIT (for non-retry actions)
|
|
368
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
369
|
+
|
|
370
|
+
/** Emit a command (for notifications, etc.) */
|
|
371
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
interface SuccessContext {
|
|
375
|
+
key: string;
|
|
376
|
+
attempt: number;
|
|
377
|
+
successes: Event[];
|
|
378
|
+
all: Event[];
|
|
379
|
+
byCommand: Record<string, Event[]>;
|
|
380
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 3.7 ForEach Builder (Phased Execution)
|
|
385
|
+
|
|
386
|
+
For processing collections in sequential phases.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
interface ForEachBuilder<E, T> {
|
|
390
|
+
/** Group items into sequential phases */
|
|
391
|
+
groupInto<P extends string>(phases: readonly P[], classifier: (item: T) => P): PhasedBuilder<E, T, P>;
|
|
392
|
+
|
|
393
|
+
/** Process all items in parallel (no phases) */
|
|
394
|
+
emit(builder: (item: T, ctx: ItemContext<E>) => CommandSpec): ItemTrackingBuilder<E, T>;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface PhasedBuilder<E, T, P> {
|
|
398
|
+
/** How to run each phase */
|
|
399
|
+
runSequentially(config?: {
|
|
400
|
+
/** Stop if any item in a phase fails (default: true) */
|
|
401
|
+
stopOnFailure?: boolean;
|
|
402
|
+
}): PhasedEmitBuilder<E, T, P>;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
interface PhasedEmitBuilder<E, T, P> {
|
|
406
|
+
/** What command to emit for each item */
|
|
407
|
+
emit(builder: (item: T, phase: P, ctx: ItemContext<E>) => CommandSpec): PhasedTrackingBuilder<E, T, P>;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
interface PhasedTrackingBuilder<E, T, P> {
|
|
411
|
+
/** How to track item completion */
|
|
412
|
+
completeWhen(config: {
|
|
413
|
+
/** Event type indicating success */
|
|
414
|
+
success: string;
|
|
415
|
+
/** Event type indicating failure */
|
|
416
|
+
failure: string;
|
|
417
|
+
/** Extract item key from completion event */
|
|
418
|
+
key: (event: Event) => string;
|
|
419
|
+
}): PhasedHooksBuilder<E, T, P>;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
interface PhasedHooksBuilder<E, T, P> {
|
|
423
|
+
/** Run before the first phase starts */
|
|
424
|
+
beforeFirstGroup(handler: (ctx: PhaseHookContext<E>) => void): this;
|
|
425
|
+
|
|
426
|
+
/** Run before a specific phase starts */
|
|
427
|
+
beforeGroup(phase: P, handler: (ctx: PhaseHookContext<E>) => void): this;
|
|
428
|
+
|
|
429
|
+
/** Run after a specific phase completes */
|
|
430
|
+
afterGroup(phase: P, handler: (ctx: PhaseCompleteContext<E, P>) => void): this;
|
|
431
|
+
|
|
432
|
+
/** Run when a phase fails (only if stopOnFailure is true) */
|
|
433
|
+
onGroupFailed(phase: P, handler: (ctx: PhaseFailedContext<E, P>) => void): this;
|
|
434
|
+
|
|
435
|
+
/** Run after all phases complete */
|
|
436
|
+
afterAll(handler: (ctx: AllCompleteContext<E>) => void): this;
|
|
437
|
+
|
|
438
|
+
/** Start a new event handler */
|
|
439
|
+
on<E2 extends EventType>(eventType: E2): TriggerBuilder<E2>;
|
|
440
|
+
|
|
441
|
+
/** End the pipeline definition */
|
|
442
|
+
build(): Pipeline;
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 3.8 Context Types
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
interface ItemContext<E> {
|
|
450
|
+
event: E;
|
|
451
|
+
index: number;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
interface PhaseHookContext<E> {
|
|
455
|
+
event: E;
|
|
456
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
interface PhaseCompleteContext<E, P> {
|
|
460
|
+
event: E;
|
|
461
|
+
phase: P;
|
|
462
|
+
completed: string[]; // Item keys that completed
|
|
463
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
interface PhaseFailedContext<E, P> {
|
|
467
|
+
event: E;
|
|
468
|
+
phase: P;
|
|
469
|
+
failed: string[]; // Item keys that failed
|
|
470
|
+
completed: string[]; // Item keys that completed
|
|
471
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
interface AllCompleteContext<E> {
|
|
475
|
+
event: E;
|
|
476
|
+
phases: { phase: string; completed: string[] }[];
|
|
477
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### 3.9 Handler Context (for `.handle()`)
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
interface HandlerContext {
|
|
485
|
+
/** Emit a command */
|
|
486
|
+
emit<C extends CommandType>(commandType: C, data: CommandData<C>): void;
|
|
487
|
+
|
|
488
|
+
/** Run state (if declared with .withState()) */
|
|
489
|
+
state: S;
|
|
490
|
+
|
|
491
|
+
/** Run metadata */
|
|
492
|
+
runId: string;
|
|
493
|
+
eventId: string;
|
|
494
|
+
timestamp: Date;
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### 3.10 Helper Function
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
/** Create a command specification */
|
|
502
|
+
function cmd<C extends CommandType>(type: C, data: CommandData<C> | ((event: Event) => CommandData<C>)): CommandSpec;
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## 4. Complete Example: Kanban Pipeline
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
import { define, cmd } from '@auto-engineer/pipeline';
|
|
511
|
+
|
|
512
|
+
const pipeline = define('kanban-pipeline')
|
|
513
|
+
.version('1.0.0')
|
|
514
|
+
.description('Full-stack Kanban app generation from schema')
|
|
515
|
+
|
|
516
|
+
// Define a reusable correlation key
|
|
517
|
+
.key('bySlice', (e) => e.data.slicePath ?? e.data.targetDirectory ?? '')
|
|
518
|
+
|
|
519
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
520
|
+
// PHASE 1: Schema → Server Generation
|
|
521
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
522
|
+
|
|
523
|
+
.on('SchemaExported')
|
|
524
|
+
.emit('GenerateServer', {
|
|
525
|
+
modelPath: './.context/schema.json',
|
|
526
|
+
destination: '.',
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
530
|
+
// PHASE 1b: Implement Each Slice
|
|
531
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
532
|
+
|
|
533
|
+
.on('SliceGenerated')
|
|
534
|
+
.emit('ImplementSlice', (e) => ({
|
|
535
|
+
slicePath: e.data.slicePath,
|
|
536
|
+
context: {
|
|
537
|
+
attemptNumber: 0,
|
|
538
|
+
previousOutputs: 'errors',
|
|
539
|
+
},
|
|
540
|
+
aiOptions: { maxTokens: 2000 },
|
|
541
|
+
}))
|
|
542
|
+
|
|
543
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
544
|
+
// PHASE 1c: Check & Retry Loop
|
|
545
|
+
// "Run all checks. If any fail, retry the slice implementation."
|
|
546
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
547
|
+
|
|
548
|
+
.on('SliceImplemented')
|
|
549
|
+
.run((e) => [
|
|
550
|
+
cmd('CheckTests', { targetDirectory: e.data.slicePath, scope: 'slice' }),
|
|
551
|
+
cmd('CheckTypes', { targetDirectory: e.data.slicePath, scope: 'slice' }),
|
|
552
|
+
cmd('CheckLint', { targetDirectory: e.data.slicePath, scope: 'slice', fix: true }),
|
|
553
|
+
])
|
|
554
|
+
.awaitAll({ key: 'bySlice', timeout: 300_000 })
|
|
555
|
+
.onFailure((ctx) => {
|
|
556
|
+
ctx.retry('ImplementSlice', {
|
|
557
|
+
maxAttempts: 5,
|
|
558
|
+
backoff: 'exponential',
|
|
559
|
+
backoffMs: 1000,
|
|
560
|
+
data: {
|
|
561
|
+
slicePath: ctx.key,
|
|
562
|
+
context: {
|
|
563
|
+
attemptNumber: ctx.attempt,
|
|
564
|
+
previousOutputs: ctx.errorText,
|
|
565
|
+
},
|
|
566
|
+
aiOptions: { maxTokens: 2000 },
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
})
|
|
570
|
+
|
|
571
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
572
|
+
// PHASE 2: Server Generated → IA + Dev Server (Parallel)
|
|
573
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
574
|
+
|
|
575
|
+
.on('ServerGenerated')
|
|
576
|
+
.emit('GenerateIA', {
|
|
577
|
+
modelPath: './.context/schema.json',
|
|
578
|
+
outputDir: './.context',
|
|
579
|
+
})
|
|
580
|
+
.emit('StartServer', {
|
|
581
|
+
serverDirectory: './server',
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
585
|
+
// PHASE 3: IA Generated → Client Generation
|
|
586
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
587
|
+
|
|
588
|
+
.on('IAGenerated')
|
|
589
|
+
.emit('GenerateClient', {
|
|
590
|
+
targetDir: './client',
|
|
591
|
+
iaSchemaPath: './.context/auto-ia-scheme.json',
|
|
592
|
+
gqlSchemaPath: './.context/schema.graphql',
|
|
593
|
+
figmaVariablesPath: './.context/figma-file.json',
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
597
|
+
// PHASE 4a: Client Generated — Fallback for malformed data
|
|
598
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
599
|
+
|
|
600
|
+
.on('ClientGenerated')
|
|
601
|
+
.when((e) => !e.data?.components?.length)
|
|
602
|
+
.emit('ImplementComponent', {
|
|
603
|
+
projectDir: './client',
|
|
604
|
+
iaSchemeDir: './.context',
|
|
605
|
+
designSystemPath: './.context/design-system.md',
|
|
606
|
+
componentType: 'molecule',
|
|
607
|
+
filePath: 'client/src/components/molecules/Example.tsx',
|
|
608
|
+
componentName: 'Example.tsx',
|
|
609
|
+
aiOptions: { maxTokens: 3000 },
|
|
610
|
+
})
|
|
611
|
+
.emit('StartClient', { clientDirectory: './client' })
|
|
612
|
+
|
|
613
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
614
|
+
// PHASE 4b: Client Generated — Phased Component Implementation
|
|
615
|
+
// "For each component, group into phases, run sequentially."
|
|
616
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
617
|
+
|
|
618
|
+
.on('ClientGenerated')
|
|
619
|
+
.when((e) => e.data?.components?.length > 0)
|
|
620
|
+
.forEach((e) => e.data.components)
|
|
621
|
+
.groupInto(['molecule', 'organism', 'page'], (c) => c.type)
|
|
622
|
+
.runSequentially({ stopOnFailure: true })
|
|
623
|
+
.emit((component, phase, ctx) =>
|
|
624
|
+
cmd('ImplementComponent', {
|
|
625
|
+
projectDir: ctx.event.data.targetDir,
|
|
626
|
+
iaSchemeDir: './.context',
|
|
627
|
+
designSystemPath: './.context/design-system.md',
|
|
628
|
+
componentType: phase,
|
|
629
|
+
filePath: component.filePath,
|
|
630
|
+
componentName: component.filePath.split('/').pop()?.replace('.tsx', '') ?? 'Unknown',
|
|
631
|
+
aiOptions: { maxTokens: 3000 },
|
|
632
|
+
}),
|
|
633
|
+
)
|
|
634
|
+
.completeWhen({
|
|
635
|
+
success: 'ComponentImplemented',
|
|
636
|
+
failure: 'ComponentImplementationFailed',
|
|
637
|
+
key: (e) => e.data.filePath,
|
|
638
|
+
})
|
|
639
|
+
.beforeFirstGroup((ctx) => {
|
|
640
|
+
ctx.emit('StartClient', { clientDirectory: './client' });
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
644
|
+
// PHASE 5: Client Checks → Project-Level Fix
|
|
645
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
646
|
+
|
|
647
|
+
.on('ClientChecked')
|
|
648
|
+
.when((e) => e.data.tsErrors > 0 || e.data.buildErrors > 0 || e.data.consoleErrors > 0)
|
|
649
|
+
.emit('ImplementClient', (e) => ({
|
|
650
|
+
projectDir: './client',
|
|
651
|
+
iaSchemeDir: './.context',
|
|
652
|
+
designSystemPath: './.context/design-system.md',
|
|
653
|
+
failures: [
|
|
654
|
+
...(e.data.tsErrorDetails ?? []),
|
|
655
|
+
...(e.data.buildErrorDetails ?? []),
|
|
656
|
+
...(e.data.consoleErrorDetails ?? []),
|
|
657
|
+
],
|
|
658
|
+
}))
|
|
659
|
+
|
|
660
|
+
.build();
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## 5. Side-by-Side Comparison
|
|
666
|
+
|
|
667
|
+
### Original (Scatter-Gather-Retry): ~50 lines
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// Module state
|
|
671
|
+
const sliceRetryState = new Map<string, number>()
|
|
672
|
+
const MAX_RETRIES = 4
|
|
673
|
+
|
|
674
|
+
// Three separate handlers
|
|
675
|
+
on('SliceImplemented', (e) => dispatch('CheckTests', {...}))
|
|
676
|
+
on('SliceImplemented', (e) => dispatch('CheckTypes', {...}))
|
|
677
|
+
on('SliceImplemented', (e) => dispatch('CheckLint', {...}))
|
|
678
|
+
|
|
679
|
+
// Complex aggregation
|
|
680
|
+
on.settled(['CheckTests', 'CheckTypes', 'CheckLint'],
|
|
681
|
+
dispatch(['ImplementSlice'], (events, send) => {
|
|
682
|
+
const failures = findCheckFailures(events)
|
|
683
|
+
const slicePath = getSlicePath(failures, events)
|
|
684
|
+
|
|
685
|
+
if (!hasAnyFailures(failures)) {
|
|
686
|
+
sliceRetryState.delete(slicePath)
|
|
687
|
+
return { persist: false }
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const currentAttempt = sliceRetryState.get(slicePath) ?? 0
|
|
691
|
+
if (currentAttempt >= MAX_RETRIES) {
|
|
692
|
+
sliceRetryState.delete(slicePath)
|
|
693
|
+
return { persist: false }
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
sliceRetryState.set(slicePath, currentAttempt + 1)
|
|
697
|
+
send({
|
|
698
|
+
type: 'ImplementSlice',
|
|
699
|
+
data: {
|
|
700
|
+
slicePath,
|
|
701
|
+
context: {
|
|
702
|
+
attemptNumber: currentAttempt + 1,
|
|
703
|
+
previousOutputs: collectErrorMessages(failures),
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
})
|
|
707
|
+
return { persist: true }
|
|
708
|
+
})
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
// Plus 4 helper functions (~30 lines)
|
|
712
|
+
function findCheckFailures(events) {...}
|
|
713
|
+
function hasAnyFailures(failures) {...}
|
|
714
|
+
function getSlicePath(failures, events) {...}
|
|
715
|
+
function collectErrorMessages(failures) {...}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### New: 14 lines
|
|
719
|
+
|
|
720
|
+
```typescript
|
|
721
|
+
.on('SliceImplemented')
|
|
722
|
+
.run(e => [
|
|
723
|
+
cmd('CheckTests', { targetDirectory: e.data.slicePath, scope: 'slice' }),
|
|
724
|
+
cmd('CheckTypes', { targetDirectory: e.data.slicePath, scope: 'slice' }),
|
|
725
|
+
cmd('CheckLint', { targetDirectory: e.data.slicePath, scope: 'slice', fix: true }),
|
|
726
|
+
])
|
|
727
|
+
.awaitAll({ key: 'bySlice', timeout: 300_000 })
|
|
728
|
+
.onFailure(ctx => {
|
|
729
|
+
ctx.retry('ImplementSlice', {
|
|
730
|
+
maxAttempts: 5,
|
|
731
|
+
backoff: 'exponential',
|
|
732
|
+
data: {
|
|
733
|
+
slicePath: ctx.key,
|
|
734
|
+
context: { attemptNumber: ctx.attempt, previousOutputs: ctx.errorText },
|
|
735
|
+
},
|
|
736
|
+
})
|
|
737
|
+
})
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Original (Phased Execution): ~70 lines
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
// Module state
|
|
744
|
+
let clientComponents = []
|
|
745
|
+
let clientTargetDir = ''
|
|
746
|
+
const processedComponents = new Set()
|
|
747
|
+
const dispatchedPhases = new Set()
|
|
748
|
+
const failedComponents = new Set()
|
|
749
|
+
const componentPhaseOrder = ['molecule', 'organism', 'page']
|
|
750
|
+
|
|
751
|
+
// Handler
|
|
752
|
+
on('ClientGenerated', (e) => {
|
|
753
|
+
clientComponents = e.data.components
|
|
754
|
+
clientTargetDir = e.data.targetDir
|
|
755
|
+
processedComponents.clear()
|
|
756
|
+
dispatchedPhases.clear()
|
|
757
|
+
failedComponents.clear()
|
|
758
|
+
dispatchedPhases.add('molecule')
|
|
759
|
+
|
|
760
|
+
const molecules = clientComponents.filter(c => c.type === 'molecule')
|
|
761
|
+
const commands = molecules.map(c => dispatch('ImplementComponent', {...}))
|
|
762
|
+
return dispatch.parallel([...commands, dispatch('StartClient', {...})])
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
on('ComponentImplemented', (e) => {
|
|
766
|
+
processedComponents.add(e.data.filePath)
|
|
767
|
+
return tryAdvanceToNextPhase()
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
on('ComponentImplementationFailed', (e) => {
|
|
771
|
+
failedComponents.add(e.data.filePath)
|
|
772
|
+
return tryAdvanceToNextPhase()
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
// Plus 4 helper functions (~40 lines)
|
|
776
|
+
function getComponentsOfType(type) {...}
|
|
777
|
+
function areAllProcessed(type) {...}
|
|
778
|
+
function dispatchComponentsOfType(type) {...}
|
|
779
|
+
function tryAdvanceToNextPhase() {...}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### New: 18 lines
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
.on('ClientGenerated')
|
|
786
|
+
.when(e => e.data?.components?.length > 0)
|
|
787
|
+
.forEach(e => e.data.components)
|
|
788
|
+
.groupInto(['molecule', 'organism', 'page'], c => c.type)
|
|
789
|
+
.runSequentially({ stopOnFailure: true })
|
|
790
|
+
.emit((component, phase, ctx) => cmd('ImplementComponent', {
|
|
791
|
+
projectDir: ctx.event.data.targetDir,
|
|
792
|
+
componentType: phase,
|
|
793
|
+
filePath: component.filePath,
|
|
794
|
+
componentName: component.filePath.split('/').pop()?.replace('.tsx', ''),
|
|
795
|
+
aiOptions: { maxTokens: 3000 },
|
|
796
|
+
}))
|
|
797
|
+
.completeWhen({
|
|
798
|
+
success: 'ComponentImplemented',
|
|
799
|
+
failure: 'ComponentImplementationFailed',
|
|
800
|
+
key: e => e.data.filePath,
|
|
801
|
+
})
|
|
802
|
+
.beforeFirstGroup(ctx => {
|
|
803
|
+
ctx.emit('StartClient', { clientDirectory: './client' })
|
|
804
|
+
})
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Summary
|
|
808
|
+
|
|
809
|
+
| Metric | Original | New |
|
|
810
|
+
| ---------------------- | ---------- | --------- |
|
|
811
|
+
| Scatter-Gather-Retry | ~80 lines | 14 lines |
|
|
812
|
+
| Phased Execution | ~110 lines | 18 lines |
|
|
813
|
+
| Module state variables | 7 | 0 |
|
|
814
|
+
| Helper functions | 8 | 0 |
|
|
815
|
+
| Total | ~220 lines | ~60 lines |
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## 6. Graph Extraction
|
|
820
|
+
|
|
821
|
+
Every fluent construct produces a graph node:
|
|
822
|
+
|
|
823
|
+
```typescript
|
|
824
|
+
const graph = pipeline.toGraph();
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Graph IR Structure
|
|
828
|
+
|
|
829
|
+
```typescript
|
|
830
|
+
interface GraphIR {
|
|
831
|
+
nodes: GraphNode[];
|
|
832
|
+
edges: GraphEdge[];
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
interface GraphNode {
|
|
836
|
+
id: string;
|
|
837
|
+
type: 'trigger' | 'emit' | 'run' | 'await' | 'retry' | 'forEach' | 'phase' | 'track' | 'handler';
|
|
838
|
+
label: string;
|
|
839
|
+
config: Record<string, unknown>;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
interface GraphEdge {
|
|
843
|
+
from: string;
|
|
844
|
+
to: string;
|
|
845
|
+
type: 'triggers' | 'produces' | 'onSuccess' | 'onFailure' | 'retries' | 'nextPhase';
|
|
846
|
+
label?: string;
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Example Output
|
|
851
|
+
|
|
852
|
+
```json
|
|
853
|
+
{
|
|
854
|
+
"nodes": [
|
|
855
|
+
{ "id": "t1", "type": "trigger", "label": "SliceImplemented" },
|
|
856
|
+
{
|
|
857
|
+
"id": "r1",
|
|
858
|
+
"type": "run",
|
|
859
|
+
"label": "Run Checks",
|
|
860
|
+
"config": { "commands": ["CheckTests", "CheckTypes", "CheckLint"] }
|
|
861
|
+
},
|
|
862
|
+
{ "id": "a1", "type": "await", "label": "Await All", "config": { "key": "bySlice", "timeout": 300000 } },
|
|
863
|
+
{
|
|
864
|
+
"id": "y1",
|
|
865
|
+
"type": "retry",
|
|
866
|
+
"label": "Retry ImplementSlice",
|
|
867
|
+
"config": { "command": "ImplementSlice", "maxAttempts": 5 }
|
|
868
|
+
},
|
|
869
|
+
|
|
870
|
+
{ "id": "t2", "type": "trigger", "label": "ClientGenerated" },
|
|
871
|
+
{ "id": "f1", "type": "forEach", "label": "For Each Component" },
|
|
872
|
+
{ "id": "p1", "type": "phase", "label": "Phase: molecule" },
|
|
873
|
+
{ "id": "p2", "type": "phase", "label": "Phase: organism" },
|
|
874
|
+
{ "id": "p3", "type": "phase", "label": "Phase: page" },
|
|
875
|
+
{ "id": "k1", "type": "track", "label": "Track Completion" }
|
|
876
|
+
],
|
|
877
|
+
"edges": [
|
|
878
|
+
{ "from": "t1", "to": "r1", "type": "triggers" },
|
|
879
|
+
{ "from": "r1", "to": "a1", "type": "produces" },
|
|
880
|
+
{ "from": "a1", "to": "y1", "type": "onFailure" },
|
|
881
|
+
{ "from": "y1", "to": "t1", "type": "retries", "label": "ImplementSlice" },
|
|
882
|
+
|
|
883
|
+
{ "from": "t2", "to": "f1", "type": "triggers" },
|
|
884
|
+
{ "from": "f1", "to": "p1", "type": "produces" },
|
|
885
|
+
{ "from": "p1", "to": "k1", "type": "produces" },
|
|
886
|
+
{ "from": "k1", "to": "p2", "type": "nextPhase" },
|
|
887
|
+
{ "from": "p2", "to": "k1", "type": "produces" },
|
|
888
|
+
{ "from": "k1", "to": "p3", "type": "nextPhase" }
|
|
889
|
+
]
|
|
890
|
+
}
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
### Visualization
|
|
894
|
+
|
|
895
|
+
```
|
|
896
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
897
|
+
│ Scatter-Gather-Retry │
|
|
898
|
+
│ │
|
|
899
|
+
│ ┌─────────────────┐ │
|
|
900
|
+
│ │SliceImplemented │ │
|
|
901
|
+
│ └────────┬────────┘ │
|
|
902
|
+
│ │ │
|
|
903
|
+
│ ▼ │
|
|
904
|
+
│ ┌─────────────────┐ │
|
|
905
|
+
│ │ Run Checks │ ─────┬─────┬─────┐ │
|
|
906
|
+
│ └────────┬────────┘ │ │ │ │
|
|
907
|
+
│ │ ▼ ▼ ▼ │
|
|
908
|
+
│ │ Tests Types Lint │
|
|
909
|
+
│ │ │ │ │ │
|
|
910
|
+
│ │ └─────┴─────┘ │
|
|
911
|
+
│ ▼ │ │
|
|
912
|
+
│ ┌─────────────────┐ │ │
|
|
913
|
+
│ │ Await All │◄───────────┘ │
|
|
914
|
+
│ │ (by slice) │ │
|
|
915
|
+
│ └────────┬────────┘ │
|
|
916
|
+
│ │ │
|
|
917
|
+
│ ┌─────┴─────┐ │
|
|
918
|
+
│ ▼ ▼ │
|
|
919
|
+
│ [Success] [Failure] │
|
|
920
|
+
│ │ │ │
|
|
921
|
+
│ │ ▼ │
|
|
922
|
+
│ │ ┌─────────────┐ │
|
|
923
|
+
│ │ │ Retry │──────────┐ │
|
|
924
|
+
│ │ │ImplementSlice│ │ │
|
|
925
|
+
│ │ └─────────────┘ │ │
|
|
926
|
+
│ │ ▲ │ │
|
|
927
|
+
│ │ └─────────────────┘ │
|
|
928
|
+
│ ▼ (loop) │
|
|
929
|
+
│ [ Done ] │
|
|
930
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
931
|
+
|
|
932
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
933
|
+
│ Phased Execution │
|
|
934
|
+
│ │
|
|
935
|
+
│ ┌─────────────────┐ │
|
|
936
|
+
│ │ ClientGenerated │ │
|
|
937
|
+
│ └────────┬────────┘ │
|
|
938
|
+
│ │ │
|
|
939
|
+
│ ▼ │
|
|
940
|
+
│ ┌─────────────────┐ │
|
|
941
|
+
│ │ For Each │ │
|
|
942
|
+
│ │ Component │ │
|
|
943
|
+
│ └────────┬────────┘ │
|
|
944
|
+
│ │ │
|
|
945
|
+
│ ┌────────┴────────────────────────────────────────┐ │
|
|
946
|
+
│ │ PHASES │ │
|
|
947
|
+
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
948
|
+
│ │ │ molecule │──▶│ organism │──▶│ page │ │ │
|
|
949
|
+
│ │ │ (N items)│ │ (N items)│ │ (N items)│ │ │
|
|
950
|
+
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
|
951
|
+
│ │ │ │ │ │ │
|
|
952
|
+
│ │ ▼ ▼ ▼ │ │
|
|
953
|
+
│ │ [Track] [Track] [Track] │ │
|
|
954
|
+
│ │ success? success? success? │ │
|
|
955
|
+
│ │ │ │ │ │ │
|
|
956
|
+
│ │ ────┴──────────────┴──────────────┴─── │ │
|
|
957
|
+
│ │ │ stopOnFailure = true │ │ │
|
|
958
|
+
│ │ ▼ ▼ │ │
|
|
959
|
+
│ │ [Continue] [HALT] │ │
|
|
960
|
+
│ └─────────────────────────────────────────────────┘ │
|
|
961
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
---
|
|
965
|
+
|
|
966
|
+
## 7. Pattern Templates (Optional Extension)
|
|
967
|
+
|
|
968
|
+
For maximum brevity, provide pre-built patterns:
|
|
969
|
+
|
|
970
|
+
```typescript
|
|
971
|
+
import { define, cmd, patterns } from '@auto-engineer/pipeline';
|
|
972
|
+
|
|
973
|
+
const pipeline = define('kanban-pipeline')
|
|
974
|
+
.on('SliceImplemented')
|
|
975
|
+
.use(
|
|
976
|
+
patterns.checkAndRetry({
|
|
977
|
+
checks: (e) => [
|
|
978
|
+
cmd('CheckTests', { target: e.data.slicePath }),
|
|
979
|
+
cmd('CheckTypes', { target: e.data.slicePath }),
|
|
980
|
+
cmd('CheckLint', { target: e.data.slicePath }),
|
|
981
|
+
],
|
|
982
|
+
key: (e) => e.data.targetDirectory,
|
|
983
|
+
retryCommand: 'ImplementSlice',
|
|
984
|
+
maxAttempts: 5,
|
|
985
|
+
retryData: (ctx) => ({
|
|
986
|
+
slicePath: ctx.key,
|
|
987
|
+
context: { attempt: ctx.attempt, errors: ctx.errorText },
|
|
988
|
+
}),
|
|
989
|
+
}),
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
.on('ClientGenerated')
|
|
993
|
+
.when((e) => e.data?.components?.length > 0)
|
|
994
|
+
.use(
|
|
995
|
+
patterns.phased({
|
|
996
|
+
items: (e) => e.data.components,
|
|
997
|
+
phases: ['molecule', 'organism', 'page'],
|
|
998
|
+
phaseOf: (c) => c.type,
|
|
999
|
+
emit: (c, phase, ctx) =>
|
|
1000
|
+
cmd('ImplementComponent', {
|
|
1001
|
+
filePath: c.filePath,
|
|
1002
|
+
projectDir: ctx.event.data.targetDir,
|
|
1003
|
+
}),
|
|
1004
|
+
track: {
|
|
1005
|
+
success: 'ComponentImplemented',
|
|
1006
|
+
failure: 'ComponentImplementationFailed',
|
|
1007
|
+
key: (e) => e.data.filePath,
|
|
1008
|
+
},
|
|
1009
|
+
stopOnFailure: true,
|
|
1010
|
+
beforeFirstPhase: (ctx) => ctx.emit('StartClient', {}),
|
|
1011
|
+
}),
|
|
1012
|
+
)
|
|
1013
|
+
|
|
1014
|
+
.build();
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
---
|
|
1018
|
+
|
|
1019
|
+
## 8. Runtime Behavior
|
|
1020
|
+
|
|
1021
|
+
### 8.1 Execution Flow
|
|
1022
|
+
|
|
1023
|
+
```
|
|
1024
|
+
1. Event arrives (e.g., SliceImplemented)
|
|
1025
|
+
2. Find matching .on() handlers
|
|
1026
|
+
3. For each handler:
|
|
1027
|
+
a. Evaluate .when() predicate
|
|
1028
|
+
b. If passes, execute the chain:
|
|
1029
|
+
- .emit() → queue command(s)
|
|
1030
|
+
- .run() → queue multiple commands
|
|
1031
|
+
- .awaitAll() → register barrier
|
|
1032
|
+
- .forEach() → expand items, register phase tracking
|
|
1033
|
+
c. Execute queued commands via plugins
|
|
1034
|
+
d. Plugins emit completion events
|
|
1035
|
+
4. Barrier evaluation:
|
|
1036
|
+
- .awaitAll() checks if all commands for key completed
|
|
1037
|
+
- If complete: invoke .onSuccess() or .onFailure()
|
|
1038
|
+
- .onFailure() may call ctx.retry() → queue retry command
|
|
1039
|
+
5. Phase tracking:
|
|
1040
|
+
- Track item completion events
|
|
1041
|
+
- When phase complete: check stopOnFailure, advance to next
|
|
1042
|
+
6. Loop until no more events to process
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### 8.2 State Management
|
|
1046
|
+
|
|
1047
|
+
The runtime manages all state internally:
|
|
1048
|
+
|
|
1049
|
+
| State | Managed By | User Visible? |
|
|
1050
|
+
| -------------------- | -------------------------------- | ----------------- |
|
|
1051
|
+
| Retry attempt counts | `.awaitAll().onFailure()` | Via `ctx.attempt` |
|
|
1052
|
+
| Correlation groups | `.awaitAll({ key: ... })` | Via `ctx.key` |
|
|
1053
|
+
| Phase progress | `.groupInto().runSequentially()` | Via phase hooks |
|
|
1054
|
+
| Item completion | `.completeWhen()` | Via phase hooks |
|
|
1055
|
+
|
|
1056
|
+
No module-level state needed in user code.
|
|
1057
|
+
|
|
1058
|
+
---
|
|
1059
|
+
|
|
1060
|
+
## 9. Summary
|
|
1061
|
+
|
|
1062
|
+
The Pipeline API provides:
|
|
1063
|
+
|
|
1064
|
+
| Level | API | Use Case |
|
|
1065
|
+
| -------------- | ------------------------------------------ | ----------------- |
|
|
1066
|
+
| Simple | `.on().emit()` | Event → Command |
|
|
1067
|
+
| Parallel | `.emit().emit()` | Multiple commands |
|
|
1068
|
+
| Scatter-Gather | `.run().awaitAll().onFailure()` | Check & retry |
|
|
1069
|
+
| Phased | `.forEach().groupInto().runSequentially()` | Sequential phases |
|
|
1070
|
+
| Custom | `.handle()` | Unique logic |
|
|
1071
|
+
|
|
1072
|
+
**Key benefits:**
|
|
1073
|
+
|
|
1074
|
+
- **70% less code** than original
|
|
1075
|
+
- **Zero manual state** management
|
|
1076
|
+
- **Reads like English** — `.run().awaitAll().onFailure()`
|
|
1077
|
+
- **Static graph extraction** for visualization
|
|
1078
|
+
- **Runs anywhere** — local and cloud
|