55** Goal:** Implement 4-step article processing flow with state management. Establish data flow foundation for observability features.
66
77** Success Criteria:**
8+
89- ✅ Article processing flow with 4 steps (fetch, summarize, extractKeywords, publish)
910- ✅ Simulated retry on summarize step
1011- ✅ pgflow State Store manages run state
@@ -25,12 +26,27 @@ cd ../..
2526```
2627
2728This installs:
28- - ` @xyflow/svelte ` - DAG visualization
29- - ` shiki ` - Syntax highlighting
29+
30+ - ` @xyflow/svelte ` - DAG visualization (used in Phase 3)
31+ - ` shiki ` - Syntax highlighting (used in Phase 4+)
32+
33+ ** Note:** Installing all frontend dependencies now simplifies later phases and avoids switching contexts when building UI components.
34+
35+ ---
36+
37+ ### 2. Copy Logo Assets
38+
39+ Copy pgflow logos for header component (used in Phase 6):
40+
41+ ``` bash
42+ cp pkgs/website/src/assets/pgflow-logo-* .svg apps/demo/static/
43+ ```
44+
45+ This copies both light and dark variants of the pgflow logo.
3046
3147---
3248
33- ### 2 . Create Article Flow Worker
49+ ### 3 . Create Article Flow Worker
3450
3551Create new Edge Function for the article flow:
3652
@@ -43,55 +59,75 @@ This creates `apps/demo/supabase/functions/article_flow_worker/` directory.
4359
4460---
4561
46- ### 3 . Create Article Processing Flow
62+ ### 4 . Create Article Processing Flow
4763
4864Create ` apps/demo/supabase/functions/article_flow_worker/article_flow.ts ` with 4 steps:
4965
5066``` typescript
5167import { Flow } from ' @pgflow/dsl' ;
5268
69+ // Task functions (implementations can be simple for Phase 2)
70+ async function fetchArticle(url : string ) {
71+ const response = await fetch (` https://r.jina.ai/${url } ` );
72+ const content = await response .text ();
73+ return { content , title: ' Article Title' };
74+ }
75+
76+ function summarizeArticle(content : string , attemptNumber : number ) {
77+ // Simulate failure on first attempt for retry demo
78+ if (attemptNumber === 1 ) {
79+ throw new Error (' Simulated failure for retry demo' );
80+ }
81+ return { summary: ` Summary of article ` , sentiment: ' positive' };
82+ }
83+
84+ function extractKeywords(content : string ) {
85+ return { keywords: [' keyword1' , ' keyword2' , ' keyword3' ] };
86+ }
87+
88+ function publishArticle(data : { summary: any ; keywords: any }) {
89+ return {
90+ articleId: crypto .randomUUID (),
91+ publishedAt: new Date ().toISOString (),
92+ ... data ,
93+ };
94+ }
95+
96+ // Flow definition - clean and minimal
5397export default new Flow <{ url: string }>({
5498 slug: ' article_flow' ,
5599 maxAttempts: 3 ,
56- baseDelay: 1 ,
57- timeout: 60
58100})
59- .step ({ slug: ' fetch_article' }, async (input ) => {
60- // Call r.jina.ai API
61- const response = await fetch (` https://r.jina.ai/${input .run .url } ` );
62- const content = await response .text ();
63- return { content , title: ' Article Title' };
64- })
65- .step ({ slug: ' summarize' }, (input ) => {
66- // Simulate failure on first attempt
67- if (input .attemptNumber === 1 ) {
68- throw new Error (' Simulated failure for retry demo' );
69- }
70- return ` Summary of: ${input .steps .fetch_article .title } ` ;
71- })
72- .step ({ slug: ' extract_keywords' }, (input ) => {
73- // Runs parallel with summarize
74- return [' keyword1' , ' keyword2' , ' keyword3' ];
75- })
76- .step ({ slug: ' publish' }, (input ) => {
77- // Depends on both summarize and extract_keywords
78- return {
79- articleId: ' article_123' ,
80- summary: input .steps .summarize ,
81- keywords: input .steps .extract_keywords
82- };
83- });
101+ .step ({ slug: ' fetch_article' }, async (input ) => fetchArticle (input .run .url ))
102+ .step ({ slug: ' summarize' , dependsOn: [' fetch_article' ] }, (input ) =>
103+ summarizeArticle (input .fetch_article .content , input .attemptNumber )
104+ )
105+ .step ({ slug: ' extract_keywords' , dependsOn: [' fetch_article' ] }, (input ) =>
106+ extractKeywords (input .fetch_article .content )
107+ )
108+ .step (
109+ { slug: ' publish' , dependsOn: [' summarize' , ' extract_keywords' ] },
110+ (input ) =>
111+ publishArticle ({
112+ summary: input .summarize ,
113+ keywords: input .extract_keywords ,
114+ })
115+ );
84116```
85117
86118** Key patterns:**
119+
87120- Flow slug is ` article_flow ` (with underscore)
88- - Parallel execution: summarize and extractKeywords run simultaneously (both depend only on fetch_article)
89- - Retry simulation: Use ` attemptNumber ` param to fail first attempt
90- - Flow config: ` maxAttempts: 3, baseDelay: 1, timeout: 60 `
121+ - Task functions defined separately for clarity
122+ - Parallel execution: ` summarize ` and ` extract_keywords ` run simultaneously (both depend only on ` fetch_article ` )
123+ - Retry simulation: ` summarizeArticle ` checks ` attemptNumber ` to fail first attempt
124+ - Diamond DAG shape: 1 → 2 parallel → 1
125+ - Minimal config: Only ` slug ` and ` maxAttempts ` (defaults used for baseDelay/timeout)
126+ - Explicit ` dependsOn ` arrays show flow structure clearly
91127
92128---
93129
94- ### 4 . Create Edge Function Worker
130+ ### 5 . Create Edge Function Worker
95131
96132Create ` apps/demo/supabase/functions/article_flow_worker/index.ts ` :
97133
@@ -104,7 +140,7 @@ EdgeWorker.start(ArticleFlow);
104140
105141---
106142
107- ### 5 . Create Deno Import Map
143+ ### 6 . Create Deno Import Map
108144
109145Create ` apps/demo/supabase/functions/article_flow_worker/deno.json ` :
110146
@@ -128,7 +164,7 @@ Create `apps/demo/supabase/functions/article_flow_worker/deno.json`:
128164
129165---
130166
131- ### 6 . Configure Edge Function in config.toml
167+ ### 7 . Configure Edge Function in config.toml
132168
133169Edit ` apps/demo/supabase/config.toml ` , add at the end:
134170
@@ -144,7 +180,7 @@ entrypoint = "./functions/article_flow_worker/index.ts"
144180
145181---
146182
147- ### 7 . Set Environment Variables
183+ ### 8 . Set Environment Variables
148184
149185Create ` apps/demo/supabase/.env.local ` with ` JINA_API_KEY ` (optional for now):
150186
@@ -154,7 +190,7 @@ JINA_API_KEY=your_jina_api_key_here
154190
155191---
156192
157- ### 8 . Rebuild and Re-vendor
193+ ### 9 . Rebuild and Re-vendor
158194
159195``` bash
160196pnpm nx build core dsl client
@@ -163,7 +199,7 @@ pnpm nx sync-edge-deps demo
163199
164200---
165201
166- ### 9 . Test Edge Function Locally
202+ ### 10 . Test Edge Function Locally
167203
168204``` bash
169205cd apps/demo
@@ -174,7 +210,7 @@ npx -y supabase@latest functions serve article_flow_worker
174210
175211---
176212
177- ### 10 . Create pgflow State Store
213+ ### 11 . Create pgflow State Store
178214
179215Create ` apps/demo/src/lib/stores/pgflow-state.svelte.ts ` :
180216
@@ -199,6 +235,7 @@ export const pgflowState = new PgflowState();
199235** Purpose:** Central state management for flow execution, used by all UI components
200236
201237** Key patterns:**
238+
202239- Use Svelte 5 runes: ` $state ` and ` $derived `
203240- Export singleton instance
204241- Will be updated in Phase 3 when building UI
@@ -207,14 +244,15 @@ export const pgflowState = new PgflowState();
207244
208245## Validation Checklist
209246
210- - [ ] Article flow worker created (` article_flow_worker/ ` )
211- - [ ] 4-step flow created with simulated retry
212- - [ ] Worker configured in ` config.toml ` with ` verify_jwt = false `
213- - [ ] Deno import map created with all dependencies
214- - [ ] pgflow State Store created and exported
215- - [ ] All dependencies installed (` @xyflow/svelte ` , ` shiki ` )
216- - [ ] Edge Function serves successfully
217- - [ ] Build succeeds
247+ - [x] All dependencies installed (` @xyflow/svelte ` , ` shiki ` )
248+ - [x] Logo assets copied to ` apps/demo/static/ `
249+ - [x] Article flow worker created (` article_flow_worker/ ` )
250+ - [x] 4-step flow created with simulated retry
251+ - [x] Worker configured in ` config.toml ` with ` verify_jwt = false `
252+ - [x] Deno import map created with all dependencies
253+ - [x] pgflow State Store created and exported
254+ - [x] Edge Function serves successfully
255+ - [x] Build succeeds
218256
219257---
220258
0 commit comments