@pgflow/core 0.0.0-array-map-steps-302d00a8-20250922101336 → 0.0.0-test-snapshot-releases-8d5d9bc1-20250922101013

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 (38) hide show
  1. package/README.md +1 -7
  2. package/package.json +2 -2
  3. package/dist/ATLAS.md +0 -32
  4. package/dist/CHANGELOG.md +0 -645
  5. package/dist/PLAN_race_condition_testing.md +0 -176
  6. package/dist/PgflowSqlClient.d.ts +0 -17
  7. package/dist/PgflowSqlClient.d.ts.map +0 -1
  8. package/dist/PgflowSqlClient.js +0 -70
  9. package/dist/README.md +0 -399
  10. package/dist/database-types.d.ts +0 -832
  11. package/dist/database-types.d.ts.map +0 -1
  12. package/dist/database-types.js +0 -8
  13. package/dist/index.d.ts +0 -4
  14. package/dist/index.d.ts.map +0 -1
  15. package/dist/index.js +0 -2
  16. package/dist/package.json +0 -32
  17. package/dist/supabase/migrations/20250429164909_pgflow_initial.sql +0 -579
  18. package/dist/supabase/migrations/20250517072017_pgflow_fix_poll_for_tasks_to_use_separate_statement_for_polling.sql +0 -101
  19. package/dist/supabase/migrations/20250609105135_pgflow_add_start_tasks_and_started_status.sql +0 -371
  20. package/dist/supabase/migrations/20250610180554_pgflow_add_set_vt_batch_and_use_it_in_start_tasks.sql +0 -127
  21. package/dist/supabase/migrations/20250614124241_pgflow_add_realtime.sql +0 -501
  22. package/dist/supabase/migrations/20250619195327_pgflow_fix_fail_task_missing_realtime_event.sql +0 -185
  23. package/dist/supabase/migrations/20250627090700_pgflow_fix_function_search_paths.sql +0 -6
  24. package/dist/supabase/migrations/20250707210212_pgflow_add_opt_start_delay.sql +0 -103
  25. package/dist/supabase/migrations/20250719205006_pgflow_worker_deprecation.sql +0 -2
  26. package/dist/supabase/migrations/20250912075001_pgflow_temp_pr1_schema.sql +0 -185
  27. package/dist/supabase/migrations/20250912080800_pgflow_temp_pr2_root_maps.sql +0 -95
  28. package/dist/supabase/migrations/20250912125339_pgflow_TEMP_task_spawning_optimization.sql +0 -146
  29. package/dist/supabase/migrations/20250916093518_pgflow_temp_add_cascade_complete.sql +0 -321
  30. package/dist/supabase/migrations/20250916142327_pgflow_temp_make_initial_tasks_nullable.sql +0 -624
  31. package/dist/supabase/migrations/20250916203905_pgflow_temp_handle_arrays_in_start_tasks.sql +0 -157
  32. package/dist/supabase/migrations/20250918042753_pgflow_temp_handle_map_output_aggregation.sql +0 -489
  33. package/dist/supabase/migrations/20250919101802_pgflow_temp_orphaned_messages_index.sql +0 -688
  34. package/dist/supabase/migrations/20250919135211_pgflow_temp_return_task_index_in_start_tasks.sql +0 -178
  35. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  36. package/dist/types.d.ts +0 -95
  37. package/dist/types.d.ts.map +0 -1
  38. package/dist/types.js +0 -1
@@ -1,371 +0,0 @@
1
- -- Create index "idx_workers_heartbeat" to table: "workers"
2
- create index "idx_workers_heartbeat" on "pgflow"."workers" ("last_heartbeat_at");
3
- -- Modify "step_tasks" table
4
- alter table "pgflow"."step_tasks" drop constraint "valid_status",
5
- add constraint "valid_status" check (
6
- status = ANY(array['queued'::text, 'started'::text, 'completed'::text, 'failed'::text])
7
- ),
8
- add constraint "completed_at_is_after_started_at" check (
9
- (completed_at is null) or (started_at is null) or (completed_at >= started_at)
10
- ),
11
- add constraint "failed_at_is_after_started_at" check (
12
- (failed_at is null) or (started_at is null) or (failed_at >= started_at)
13
- ),
14
- add constraint "started_at_is_after_queued_at" check ((started_at is null) or (started_at >= queued_at)),
15
- add column "started_at" timestamptz null,
16
- add column "last_worker_id" uuid null,
17
- add constraint "step_tasks_last_worker_id_fkey" foreign key ("last_worker_id") references "pgflow"."workers" (
18
- "worker_id"
19
- ) on update no action on delete set null;
20
- -- Create index "idx_step_tasks_last_worker" to table: "step_tasks"
21
- create index "idx_step_tasks_last_worker" on "pgflow"."step_tasks" ("last_worker_id") where (status = 'started'::text);
22
- -- Create index "idx_step_tasks_queued_msg" to table: "step_tasks"
23
- create index "idx_step_tasks_queued_msg" on "pgflow"."step_tasks" ("message_id") where (status = 'queued'::text);
24
- -- Create index "idx_step_tasks_started" to table: "step_tasks"
25
- create index "idx_step_tasks_started" on "pgflow"."step_tasks" ("started_at") where (status = 'started'::text);
26
- -- Modify "complete_task" function
27
- create or replace function "pgflow"."complete_task"(
28
- "run_id" uuid, "step_slug" text, "task_index" integer, "output" jsonb
29
- ) returns setof "pgflow"."step_tasks" language plpgsql set "search_path"
30
- = '' as $$
31
- begin
32
-
33
- WITH run_lock AS (
34
- SELECT * FROM pgflow.runs
35
- WHERE pgflow.runs.run_id = complete_task.run_id
36
- FOR UPDATE
37
- ),
38
- step_lock AS (
39
- SELECT * FROM pgflow.step_states
40
- WHERE pgflow.step_states.run_id = complete_task.run_id
41
- AND pgflow.step_states.step_slug = complete_task.step_slug
42
- FOR UPDATE
43
- ),
44
- task AS (
45
- UPDATE pgflow.step_tasks
46
- SET
47
- status = 'completed',
48
- completed_at = now(),
49
- output = complete_task.output
50
- WHERE pgflow.step_tasks.run_id = complete_task.run_id
51
- AND pgflow.step_tasks.step_slug = complete_task.step_slug
52
- AND pgflow.step_tasks.task_index = complete_task.task_index
53
- AND pgflow.step_tasks.status = 'started'
54
- RETURNING *
55
- ),
56
- step_state AS (
57
- UPDATE pgflow.step_states
58
- SET
59
- status = CASE
60
- WHEN pgflow.step_states.remaining_tasks = 1 THEN 'completed' -- Will be 0 after decrement
61
- ELSE 'started'
62
- END,
63
- completed_at = CASE
64
- WHEN pgflow.step_states.remaining_tasks = 1 THEN now() -- Will be 0 after decrement
65
- ELSE NULL
66
- END,
67
- remaining_tasks = pgflow.step_states.remaining_tasks - 1
68
- FROM task
69
- WHERE pgflow.step_states.run_id = complete_task.run_id
70
- AND pgflow.step_states.step_slug = complete_task.step_slug
71
- RETURNING pgflow.step_states.*
72
- ),
73
- -- Find all dependent steps if the current step was completed
74
- dependent_steps AS (
75
- SELECT d.step_slug AS dependent_step_slug
76
- FROM pgflow.deps d
77
- JOIN step_state s ON s.status = 'completed' AND d.flow_slug = s.flow_slug
78
- WHERE d.dep_slug = complete_task.step_slug
79
- ORDER BY d.step_slug -- Ensure consistent ordering
80
- ),
81
- -- Lock dependent steps before updating
82
- dependent_steps_lock AS (
83
- SELECT * FROM pgflow.step_states
84
- WHERE pgflow.step_states.run_id = complete_task.run_id
85
- AND pgflow.step_states.step_slug IN (SELECT dependent_step_slug FROM dependent_steps)
86
- FOR UPDATE
87
- ),
88
- -- Update all dependent steps
89
- dependent_steps_update AS (
90
- UPDATE pgflow.step_states
91
- SET remaining_deps = pgflow.step_states.remaining_deps - 1
92
- FROM dependent_steps
93
- WHERE pgflow.step_states.run_id = complete_task.run_id
94
- AND pgflow.step_states.step_slug = dependent_steps.dependent_step_slug
95
- )
96
- -- Only decrement remaining_steps, don't update status
97
- UPDATE pgflow.runs
98
- SET remaining_steps = pgflow.runs.remaining_steps - 1
99
- FROM step_state
100
- WHERE pgflow.runs.run_id = complete_task.run_id
101
- AND step_state.status = 'completed';
102
-
103
- -- For completed tasks: archive the message
104
- PERFORM (
105
- WITH completed_tasks AS (
106
- SELECT r.flow_slug, st.message_id
107
- FROM pgflow.step_tasks st
108
- JOIN pgflow.runs r ON st.run_id = r.run_id
109
- WHERE st.run_id = complete_task.run_id
110
- AND st.step_slug = complete_task.step_slug
111
- AND st.task_index = complete_task.task_index
112
- AND st.status = 'completed'
113
- )
114
- SELECT pgmq.archive(ct.flow_slug, ct.message_id)
115
- FROM completed_tasks ct
116
- WHERE EXISTS (SELECT 1 FROM completed_tasks)
117
- );
118
-
119
- PERFORM pgflow.start_ready_steps(complete_task.run_id);
120
-
121
- PERFORM pgflow.maybe_complete_run(complete_task.run_id);
122
-
123
- RETURN QUERY SELECT *
124
- FROM pgflow.step_tasks AS step_task
125
- WHERE step_task.run_id = complete_task.run_id
126
- AND step_task.step_slug = complete_task.step_slug
127
- AND step_task.task_index = complete_task.task_index;
128
-
129
- end;
130
- $$;
131
- -- Modify "fail_task" function
132
- create or replace function "pgflow"."fail_task"(
133
- "run_id" uuid, "step_slug" text, "task_index" integer, "error_message" text
134
- ) returns setof "pgflow"."step_tasks" language plpgsql set "search_path"
135
- = '' as $$
136
- begin
137
-
138
- WITH run_lock AS (
139
- SELECT * FROM pgflow.runs
140
- WHERE pgflow.runs.run_id = fail_task.run_id
141
- FOR UPDATE
142
- ),
143
- step_lock AS (
144
- SELECT * FROM pgflow.step_states
145
- WHERE pgflow.step_states.run_id = fail_task.run_id
146
- AND pgflow.step_states.step_slug = fail_task.step_slug
147
- FOR UPDATE
148
- ),
149
- flow_info AS (
150
- SELECT r.flow_slug
151
- FROM pgflow.runs r
152
- WHERE r.run_id = fail_task.run_id
153
- ),
154
- config AS (
155
- SELECT
156
- COALESCE(s.opt_max_attempts, f.opt_max_attempts) AS opt_max_attempts,
157
- COALESCE(s.opt_base_delay, f.opt_base_delay) AS opt_base_delay
158
- FROM pgflow.steps s
159
- JOIN pgflow.flows f ON f.flow_slug = s.flow_slug
160
- JOIN flow_info fi ON fi.flow_slug = s.flow_slug
161
- WHERE s.flow_slug = fi.flow_slug AND s.step_slug = fail_task.step_slug
162
- ),
163
-
164
- fail_or_retry_task as (
165
- UPDATE pgflow.step_tasks as task
166
- SET
167
- status = CASE
168
- WHEN task.attempts_count < (SELECT opt_max_attempts FROM config) THEN 'queued'
169
- ELSE 'failed'
170
- END,
171
- failed_at = CASE
172
- WHEN task.attempts_count >= (SELECT opt_max_attempts FROM config) THEN now()
173
- ELSE NULL
174
- END,
175
- started_at = CASE
176
- WHEN task.attempts_count < (SELECT opt_max_attempts FROM config) THEN NULL
177
- ELSE task.started_at
178
- END,
179
- error_message = fail_task.error_message
180
- WHERE task.run_id = fail_task.run_id
181
- AND task.step_slug = fail_task.step_slug
182
- AND task.task_index = fail_task.task_index
183
- AND task.status = 'started'
184
- RETURNING *
185
- ),
186
- maybe_fail_step AS (
187
- UPDATE pgflow.step_states
188
- SET
189
- status = CASE
190
- WHEN (select fail_or_retry_task.status from fail_or_retry_task) = 'failed' THEN 'failed'
191
- ELSE pgflow.step_states.status
192
- END,
193
- failed_at = CASE
194
- WHEN (select fail_or_retry_task.status from fail_or_retry_task) = 'failed' THEN now()
195
- ELSE NULL
196
- END
197
- FROM fail_or_retry_task
198
- WHERE pgflow.step_states.run_id = fail_task.run_id
199
- AND pgflow.step_states.step_slug = fail_task.step_slug
200
- RETURNING pgflow.step_states.*
201
- )
202
- UPDATE pgflow.runs
203
- SET status = CASE
204
- WHEN (select status from maybe_fail_step) = 'failed' THEN 'failed'
205
- ELSE status
206
- END,
207
- failed_at = CASE
208
- WHEN (select status from maybe_fail_step) = 'failed' THEN now()
209
- ELSE NULL
210
- END
211
- WHERE pgflow.runs.run_id = fail_task.run_id;
212
-
213
- -- For queued tasks: delay the message for retry with exponential backoff
214
- PERFORM (
215
- WITH retry_config AS (
216
- SELECT
217
- COALESCE(s.opt_base_delay, f.opt_base_delay) AS base_delay
218
- FROM pgflow.steps s
219
- JOIN pgflow.flows f ON f.flow_slug = s.flow_slug
220
- JOIN pgflow.runs r ON r.flow_slug = f.flow_slug
221
- WHERE r.run_id = fail_task.run_id
222
- AND s.step_slug = fail_task.step_slug
223
- ),
224
- queued_tasks AS (
225
- SELECT
226
- r.flow_slug,
227
- st.message_id,
228
- pgflow.calculate_retry_delay((SELECT base_delay FROM retry_config), st.attempts_count) AS calculated_delay
229
- FROM pgflow.step_tasks st
230
- JOIN pgflow.runs r ON st.run_id = r.run_id
231
- WHERE st.run_id = fail_task.run_id
232
- AND st.step_slug = fail_task.step_slug
233
- AND st.task_index = fail_task.task_index
234
- AND st.status = 'queued'
235
- )
236
- SELECT pgmq.set_vt(qt.flow_slug, qt.message_id, qt.calculated_delay)
237
- FROM queued_tasks qt
238
- WHERE EXISTS (SELECT 1 FROM queued_tasks)
239
- );
240
-
241
- -- For failed tasks: archive the message
242
- PERFORM (
243
- WITH failed_tasks AS (
244
- SELECT r.flow_slug, st.message_id
245
- FROM pgflow.step_tasks st
246
- JOIN pgflow.runs r ON st.run_id = r.run_id
247
- WHERE st.run_id = fail_task.run_id
248
- AND st.step_slug = fail_task.step_slug
249
- AND st.task_index = fail_task.task_index
250
- AND st.status = 'failed'
251
- )
252
- SELECT pgmq.archive(ft.flow_slug, ft.message_id)
253
- FROM failed_tasks ft
254
- WHERE EXISTS (SELECT 1 FROM failed_tasks)
255
- );
256
-
257
- return query select *
258
- from pgflow.step_tasks st
259
- where st.run_id = fail_task.run_id
260
- and st.step_slug = fail_task.step_slug
261
- and st.task_index = fail_task.task_index;
262
-
263
- end;
264
- $$;
265
- -- Modify "poll_for_tasks" function
266
- create or replace function "pgflow"."poll_for_tasks"(
267
- "queue_name" text,
268
- "vt" integer,
269
- "qty" integer,
270
- "max_poll_seconds" integer default 5,
271
- "poll_interval_ms" integer default 100
272
- ) returns setof "pgflow"."step_task_record" language plpgsql set "search_path"
273
- = '' as $$
274
- begin
275
- -- DEPRECATED: This function is deprecated and will be removed in a future version.
276
- -- Please update pgflow to use the new two-phase polling approach.
277
- -- Run 'npx pgflow install' to update your installation.
278
- raise notice 'DEPRECATED: poll_for_tasks is deprecated and will be removed. Please update pgflow via "npx pgflow install".';
279
-
280
- -- Return empty set - no tasks will be processed
281
- return;
282
- end;
283
- $$;
284
- -- Create "start_tasks" function
285
- create function "pgflow"."start_tasks"(
286
- "flow_slug" text, "msg_ids" bigint [], "worker_id" uuid
287
- ) returns setof "pgflow"."step_task_record" language sql set "search_path"
288
- = '' as $$
289
- with tasks as (
290
- select
291
- task.flow_slug,
292
- task.run_id,
293
- task.step_slug,
294
- task.task_index,
295
- task.message_id
296
- from pgflow.step_tasks as task
297
- where task.flow_slug = start_tasks.flow_slug
298
- and task.message_id = any(msg_ids)
299
- and task.status = 'queued'
300
- ),
301
- start_tasks_update as (
302
- update pgflow.step_tasks
303
- set
304
- attempts_count = attempts_count + 1,
305
- status = 'started',
306
- started_at = now(),
307
- last_worker_id = worker_id
308
- from tasks
309
- where step_tasks.message_id = tasks.message_id
310
- and step_tasks.flow_slug = tasks.flow_slug
311
- and step_tasks.status = 'queued'
312
- ),
313
- runs as (
314
- select
315
- r.run_id,
316
- r.input
317
- from pgflow.runs r
318
- where r.run_id in (select run_id from tasks)
319
- ),
320
- deps as (
321
- select
322
- st.run_id,
323
- st.step_slug,
324
- dep.dep_slug,
325
- dep_task.output as dep_output
326
- from tasks st
327
- join pgflow.deps dep on dep.flow_slug = st.flow_slug and dep.step_slug = st.step_slug
328
- join pgflow.step_tasks dep_task on
329
- dep_task.run_id = st.run_id and
330
- dep_task.step_slug = dep.dep_slug and
331
- dep_task.status = 'completed'
332
- ),
333
- deps_outputs as (
334
- select
335
- d.run_id,
336
- d.step_slug,
337
- jsonb_object_agg(d.dep_slug, d.dep_output) as deps_output
338
- from deps d
339
- group by d.run_id, d.step_slug
340
- ),
341
- timeouts as (
342
- select
343
- task.message_id,
344
- task.flow_slug,
345
- coalesce(step.opt_timeout, flow.opt_timeout) + 2 as vt_delay
346
- from tasks task
347
- join pgflow.flows flow on flow.flow_slug = task.flow_slug
348
- join pgflow.steps step on step.flow_slug = task.flow_slug and step.step_slug = task.step_slug
349
- )
350
- select
351
- st.flow_slug,
352
- st.run_id,
353
- st.step_slug,
354
- jsonb_build_object('run', r.input) ||
355
- coalesce(dep_out.deps_output, '{}'::jsonb) as input,
356
- st.message_id as msg_id
357
- from tasks st
358
- join runs r on st.run_id = r.run_id
359
- left join deps_outputs dep_out on
360
- dep_out.run_id = st.run_id and
361
- dep_out.step_slug = st.step_slug
362
- cross join lateral (
363
- -- TODO: this is slow because it calls set_vt for each row, and set_vt
364
- -- builds dynamic query from string every time it is called
365
- -- implement set_vt_batch(msgs_ids bigint[], vt_delays int[])
366
- select pgmq.set_vt(t.flow_slug, st.message_id, t.vt_delay)
367
- from timeouts t
368
- where t.message_id = st.message_id
369
- and t.flow_slug = st.flow_slug
370
- ) set_vt
371
- $$;
@@ -1,127 +0,0 @@
1
- -- Create "set_vt_batch" function
2
- CREATE FUNCTION "pgflow"."set_vt_batch" ("queue_name" text, "msg_ids" bigint[], "vt_offsets" integer[]) RETURNS SETOF pgmq.message_record LANGUAGE plpgsql AS $$
3
- DECLARE
4
- qtable TEXT := pgmq.format_table_name(queue_name, 'q');
5
- sql TEXT;
6
- BEGIN
7
- /* ---------- safety checks ---------------------------------------------------- */
8
- IF msg_ids IS NULL OR vt_offsets IS NULL OR array_length(msg_ids, 1) = 0 THEN
9
- RETURN; -- nothing to do, return empty set
10
- END IF;
11
-
12
- IF array_length(msg_ids, 1) IS DISTINCT FROM array_length(vt_offsets, 1) THEN
13
- RAISE EXCEPTION
14
- 'msg_ids length (%) must equal vt_offsets length (%)',
15
- array_length(msg_ids, 1), array_length(vt_offsets, 1);
16
- END IF;
17
-
18
- /* ---------- dynamic statement ------------------------------------------------ */
19
- /* One UPDATE joins with the unnested arrays */
20
- sql := format(
21
- $FMT$
22
- WITH input (msg_id, vt_offset) AS (
23
- SELECT unnest($1)::bigint
24
- , unnest($2)::int
25
- )
26
- UPDATE pgmq.%I q
27
- SET vt = clock_timestamp() + make_interval(secs => input.vt_offset),
28
- read_ct = read_ct -- no change, but keeps RETURNING list aligned
29
- FROM input
30
- WHERE q.msg_id = input.msg_id
31
- RETURNING q.msg_id,
32
- q.read_ct,
33
- q.enqueued_at,
34
- q.vt,
35
- q.message
36
- $FMT$,
37
- qtable
38
- );
39
-
40
- RETURN QUERY EXECUTE sql USING msg_ids, vt_offsets;
41
- END;
42
- $$;
43
- -- Modify "start_tasks" function
44
- CREATE OR REPLACE FUNCTION "pgflow"."start_tasks" ("flow_slug" text, "msg_ids" bigint[], "worker_id" uuid) RETURNS SETOF "pgflow"."step_task_record" LANGUAGE sql SET "search_path" = '' AS $$
45
- with tasks as (
46
- select
47
- task.flow_slug,
48
- task.run_id,
49
- task.step_slug,
50
- task.task_index,
51
- task.message_id
52
- from pgflow.step_tasks as task
53
- where task.flow_slug = start_tasks.flow_slug
54
- and task.message_id = any(msg_ids)
55
- and task.status = 'queued'
56
- ),
57
- start_tasks_update as (
58
- update pgflow.step_tasks
59
- set
60
- attempts_count = attempts_count + 1,
61
- status = 'started',
62
- started_at = now(),
63
- last_worker_id = worker_id
64
- from tasks
65
- where step_tasks.message_id = tasks.message_id
66
- and step_tasks.flow_slug = tasks.flow_slug
67
- and step_tasks.status = 'queued'
68
- ),
69
- runs as (
70
- select
71
- r.run_id,
72
- r.input
73
- from pgflow.runs r
74
- where r.run_id in (select run_id from tasks)
75
- ),
76
- deps as (
77
- select
78
- st.run_id,
79
- st.step_slug,
80
- dep.dep_slug,
81
- dep_task.output as dep_output
82
- from tasks st
83
- join pgflow.deps dep on dep.flow_slug = st.flow_slug and dep.step_slug = st.step_slug
84
- join pgflow.step_tasks dep_task on
85
- dep_task.run_id = st.run_id and
86
- dep_task.step_slug = dep.dep_slug and
87
- dep_task.status = 'completed'
88
- ),
89
- deps_outputs as (
90
- select
91
- d.run_id,
92
- d.step_slug,
93
- jsonb_object_agg(d.dep_slug, d.dep_output) as deps_output
94
- from deps d
95
- group by d.run_id, d.step_slug
96
- ),
97
- timeouts as (
98
- select
99
- task.message_id,
100
- task.flow_slug,
101
- coalesce(step.opt_timeout, flow.opt_timeout) + 2 as vt_delay
102
- from tasks task
103
- join pgflow.flows flow on flow.flow_slug = task.flow_slug
104
- join pgflow.steps step on step.flow_slug = task.flow_slug and step.step_slug = task.step_slug
105
- ),
106
- -- Batch update visibility timeouts for all messages
107
- set_vt_batch as (
108
- select pgflow.set_vt_batch(
109
- start_tasks.flow_slug,
110
- array_agg(t.message_id order by t.message_id),
111
- array_agg(t.vt_delay order by t.message_id)
112
- )
113
- from timeouts t
114
- )
115
- select
116
- st.flow_slug,
117
- st.run_id,
118
- st.step_slug,
119
- jsonb_build_object('run', r.input) ||
120
- coalesce(dep_out.deps_output, '{}'::jsonb) as input,
121
- st.message_id as msg_id
122
- from tasks st
123
- join runs r on st.run_id = r.run_id
124
- left join deps_outputs dep_out on
125
- dep_out.run_id = st.run_id and
126
- dep_out.step_slug = st.step_slug
127
- $$;