@pgflow/dsl 0.0.5 → 0.0.7

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 (71) hide show
  1. package/{CHANGELOG.md → dist/CHANGELOG.md} +2 -0
  2. package/package.json +10 -8
  3. package/__tests__/runtime/flow.test.ts +0 -121
  4. package/__tests__/runtime/steps.test.ts +0 -183
  5. package/__tests__/runtime/utils.test.ts +0 -149
  6. package/__tests__/types/dsl-types.test-d.ts +0 -103
  7. package/__tests__/types/example-flow.test-d.ts +0 -76
  8. package/__tests__/types/extract-flow-input.test-d.ts +0 -71
  9. package/__tests__/types/extract-flow-steps.test-d.ts +0 -74
  10. package/__tests__/types/getStepDefinition.test-d.ts +0 -65
  11. package/__tests__/types/step-input.test-d.ts +0 -212
  12. package/__tests__/types/step-output.test-d.ts +0 -55
  13. package/brainstorming/condition/condition-alternatives.md +0 -219
  14. package/brainstorming/condition/condition-with-flexibility.md +0 -303
  15. package/brainstorming/condition/condition.md +0 -139
  16. package/brainstorming/condition/implementation-plan.md +0 -372
  17. package/brainstorming/dsl/cli-json-schema.md +0 -225
  18. package/brainstorming/dsl/cli.md +0 -179
  19. package/brainstorming/dsl/create-compilator.md +0 -25
  20. package/brainstorming/dsl/dsl-analysis-2.md +0 -166
  21. package/brainstorming/dsl/dsl-analysis.md +0 -512
  22. package/brainstorming/dsl/dsl-critique.md +0 -41
  23. package/brainstorming/fanouts/fanout-subflows-flattened-vs-subruns.md +0 -213
  24. package/brainstorming/fanouts/fanouts-task-index.md +0 -150
  25. package/brainstorming/fanouts/fanouts-with-conditions-and-subflows.md +0 -239
  26. package/brainstorming/subflows/branching.ts.md +0 -38
  27. package/brainstorming/subflows/subflows-callbacks.ts.md +0 -124
  28. package/brainstorming/subflows/subflows-classes.ts.md +0 -83
  29. package/brainstorming/subflows/subflows-flattening-versioned.md +0 -119
  30. package/brainstorming/subflows/subflows-flattening.md +0 -138
  31. package/brainstorming/subflows/subflows.md +0 -118
  32. package/brainstorming/subflows/subruns-table.md +0 -282
  33. package/brainstorming/subflows/subruns.md +0 -315
  34. package/brainstorming/versioning/breaking-and-non-breaking-flow-changes.md +0 -259
  35. package/docs/refactor-edge-worker.md +0 -146
  36. package/docs/versioning.md +0 -19
  37. package/eslint.config.cjs +0 -22
  38. package/out-tsc/vitest/__tests__/runtime/flow.test.d.ts +0 -2
  39. package/out-tsc/vitest/__tests__/runtime/flow.test.d.ts.map +0 -1
  40. package/out-tsc/vitest/__tests__/runtime/steps.test.d.ts +0 -2
  41. package/out-tsc/vitest/__tests__/runtime/steps.test.d.ts.map +0 -1
  42. package/out-tsc/vitest/__tests__/runtime/utils.test.d.ts +0 -2
  43. package/out-tsc/vitest/__tests__/runtime/utils.test.d.ts.map +0 -1
  44. package/out-tsc/vitest/__tests__/types/dsl-types.test-d.d.ts +0 -2
  45. package/out-tsc/vitest/__tests__/types/dsl-types.test-d.d.ts.map +0 -1
  46. package/out-tsc/vitest/__tests__/types/example-flow.test-d.d.ts +0 -2
  47. package/out-tsc/vitest/__tests__/types/example-flow.test-d.d.ts.map +0 -1
  48. package/out-tsc/vitest/__tests__/types/extract-flow-input.test-d.d.ts +0 -2
  49. package/out-tsc/vitest/__tests__/types/extract-flow-input.test-d.d.ts.map +0 -1
  50. package/out-tsc/vitest/__tests__/types/extract-flow-steps.test-d.d.ts +0 -2
  51. package/out-tsc/vitest/__tests__/types/extract-flow-steps.test-d.d.ts.map +0 -1
  52. package/out-tsc/vitest/__tests__/types/getStepDefinition.test-d.d.ts +0 -2
  53. package/out-tsc/vitest/__tests__/types/getStepDefinition.test-d.d.ts.map +0 -1
  54. package/out-tsc/vitest/__tests__/types/step-input.test-d.d.ts +0 -2
  55. package/out-tsc/vitest/__tests__/types/step-input.test-d.d.ts.map +0 -1
  56. package/out-tsc/vitest/__tests__/types/step-output.test-d.d.ts +0 -2
  57. package/out-tsc/vitest/__tests__/types/step-output.test-d.d.ts.map +0 -1
  58. package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +0 -1
  59. package/out-tsc/vitest/vite.config.d.ts +0 -3
  60. package/out-tsc/vitest/vite.config.d.ts.map +0 -1
  61. package/project.json +0 -28
  62. package/prompts/edge-worker-refactor.md +0 -105
  63. package/src/dsl.ts +0 -318
  64. package/src/example-flow.ts +0 -67
  65. package/src/index.ts +0 -1
  66. package/src/utils.ts +0 -84
  67. package/tsconfig.json +0 -13
  68. package/tsconfig.lib.json +0 -26
  69. package/tsconfig.spec.json +0 -35
  70. package/typecheck.log +0 -120
  71. package/vite.config.ts +0 -57
@@ -1,303 +0,0 @@
1
- # Conditional Steps in a pgflow MVP: An Extensible Design for `runIf` / `runUnless`
2
-
3
- This document comprehensively analyzes how to implement conditional skipping of steps (`runIf`/`runUnless`) in a **Minimum Viable Product (MVP)** phase of pgflow, while preparing for a future in which subflows (or branching) become first-class citizens. We also discuss how conditions might later be supplied as a callback—executed separately from the main step handler—so that task workers can decide to skip a step (or subflow) before committing resources.
4
-
5
- ## Table of Contents
6
-
7
- 1. [Introduction & Motivations](#introduction--motivations)
8
- 2. [High-Level Requirements](#high-level-requirements)
9
- 3. [RunIf / RunUnless Feature in the MVP](#runif--rununless-feature-in-the-mvp)
10
- 1. [Step-Level Syntax](#step-level-syntax)
11
- 2. [Database Model Updates](#database-model-updates)
12
- 3. [Execution Logic](#execution-logic)
13
- 4. [Backward & Forward Compatibility](#backward--forward-compatibility)
14
- 5. [Extending to Subflows](#extending-to-subflows)
15
- 1. [Approach: Flattened Branches vs. Standalone Subflows](#approach-flattened-branches-vs-standalone-subflows)
16
- 2. [Referencing Conditional Steps in a Subflow](#referencing-conditional-steps-in-a-subflow)
17
- 6. [Callbacks for Condition Checking](#callbacks-for-condition-checking)
18
- 1. [Design Options](#design-options)
19
- 2. [Potential Worker APIs: skip_task or Condition-Only Tasks](#potential-worker-apis-skip_task-or-condition-only-tasks)
20
- 7. [Handling Versioning & Flow Slugs](#handling-versioning--flow-slugs)
21
- 8. [Proposed Step Condition Implementation Plan](#proposed-step-condition-implementation-plan)
22
- 9. [Conclusion](#conclusion)
23
-
24
- ---
25
-
26
- ## 1. Introduction & Motivations
27
-
28
- Many workflows need basic conditional logic so that certain steps:
29
-
30
- - **Only run** if specific data in the input or prior step output meets a condition (i.e., `runIf`).
31
- - **Skip** if certain fields or flags indicate that it is not relevant (i.e., `runUnless`).
32
-
33
- A minimal first iteration might rely on a small JSON-based condition. However, we want to ensure that as the system evolves:
34
-
35
- - We can keep reusing this logic when we introduce subflows or more advanced branching.
36
- - Conditions can eventually be provided as user-defined callback functions, enabling more complex or dynamic checks—e.g., preempting a step if the worker calculates the condition is not satisfied.
37
-
38
- By **planning for extension**, we can avoid a breaking rewrite when subflows become a first-class feature.
39
-
40
- ---
41
-
42
- ## 2. High-Level Requirements
43
-
44
- 1. **MVP Feasibility**
45
-
46
- - Implement in a straightforward way that requires minimal schema updates and minimal engine changes.
47
- - Provide typical `runIf` or `runUnless` conditions on a step’s input object.
48
-
49
- 2. **Extensibility**
50
-
51
- - When full subflows or branching are introduced (e.g., `.subflow()` or `.branch()`), the same condition system can be reused.
52
- - Potential for user-defined callbacks that run in the worker, separate from the main step handler.
53
-
54
- 3. **Versioning-Compatible**
55
-
56
- - Align with the immutable flow definitions approach: a changed condition means a new flow slug.
57
- - No partial-upgrade pitfalls.
58
-
59
- 4. **Minimal Overhead**
60
- - For now, skip implementing extremely advanced logic (like multi-operator, multi-field conditions), but design it so that we can add them later.
61
-
62
- ---
63
-
64
- ## 3. RunIf / RunUnless Feature in the MVP
65
-
66
- ### 3.1 Step-Level Syntax
67
-
68
- In the DSL, a typical usage might look like:
69
-
70
- ```ts
71
- new Flow<{ userIsVIP: boolean }>({ slug: 'example_flow' })
72
- .step(
73
- {
74
- slug: 'vipStep',
75
- runIf: { run: { userIsVIP: true } },
76
- },
77
- async (input) => {
78
- // Only runs if userIsVIP === true
79
- /* ... */
80
- }
81
- )
82
- .step(
83
- {
84
- slug: 'regularStep',
85
- runUnless: { run: { userIsVIP: true } },
86
- },
87
- async (input) => {
88
- // Will be skipped if userIsVIP === true
89
- /* ... */
90
- }
91
- );
92
- ```
93
-
94
- - **`runIf`** vs. **`runUnless`**: If `runIf` is present, the step only runs if the condition is _true_; if `runUnless` is present, it runs only if the condition is _not_ true.
95
-
96
- **Potential (MVP) Implementation**:
97
-
98
- - The condition is expressed as a simple JSON partial object that must be “contained” by the actual input object.
99
- - For example, `runIf: { run: { userIsVIP: true } }` would mean “skip unless `input.run.userIsVIP === true`.”
100
-
101
- **Alternative**:
102
-
103
- - A small typed function pointer in the DSL, which is compiled to JSON or stored as a string. However, we typically store conditions in JSON to remain flexible; the actual evaluation can happen either in Postgres or the Worker.
104
-
105
- ### 3.2 Database Model Updates
106
-
107
- 1. **New Column in `steps`**:
108
-
109
- - `condition jsonb` (optional).
110
- - Possibly an enum or text column to store if it’s a `runIf` or `runUnless`.
111
-
112
- 2. **Condition Storage**:
113
- - For an MVP, we might store something like:
114
- ```json
115
- {
116
- "type": "runIf",
117
- "match": {
118
- "run": { "userIsVIP": true }
119
- }
120
- }
121
- ```
122
- - Or simply store them as separate fields: `condition_type TEXT` (either `runIf` or `runUnless`), `condition JSONB` (the partial object or rule).
123
-
124
- ### 3.3 Execution Logic
125
-
126
- We need a small piece of logic in the state machine so that **before** a step transitions from “ready” to “started,” we check:
127
-
128
- 1. If the step has a condition:
129
- - Evaluate the condition against the step’s combined input (merge of run input + outputs from dependencies).
130
- - If the condition fails:
131
- - Mark the step as **skipped**.
132
- - Decrement the run’s `remaining_steps`.
133
- - **Do not** enqueue a task for that step.
134
- - Any step that depends on it is also implicitly "skippable"? (One approach is “skip cascades,” or we let them try to run but see the skip field as required. For an MVP, we can say “A step is only triggered if all dependencies are `completed` or `skipped`.”)
135
-
136
- **Skip Cascade**:
137
-
138
- - If a step is skipped, it is effectively “completed with no output.” We’ll call it “skipped” state. This means that any step that depends on the skipped step can still run (unless that step requires data from it, which might be `undefined`).
139
- - In a minimal approach, if a step absolutely needed output from a skipped step, it might crash or proceed with partial data. The system can eventually define “optional dependencies” or “optional: true.”
140
- - For the MVP, we can keep it simple and allow steps to proceed even if a dependency is “skipped” (at which point, the input for that dependency is `null` or absent).
141
-
142
- ---
143
-
144
- ## 4. Backward & Forward Compatibility
145
-
146
- 1. **Backward**:
147
- - For existing flows, no condition means no skipping.
148
- - We do not break the schema if we add optional columns for `condition`.
149
- 2. **Forward**:
150
- - This logic can be extended to advanced condition syntax or user-defined callback checks.
151
- - The DSL can remain the same; we just expand how we parse or store the condition if needed.
152
-
153
- ---
154
-
155
- ## 5. Extending to Subflows
156
-
157
- ### 5.1 Approach: Flattened Branches vs. Standalone Subflows
158
-
159
- As outlined in other documents, subflows can be:
160
-
161
- - **Flattened** into the parent flow’s step list (the engine sees them as additional steps with slug prefixes).
162
- - **Standalone** runs that the parent references.
163
-
164
- Regardless of approach, a subflow or “branch” can also have a `runIf` or `runUnless` condition on its “entry step” or subflow “root.” The same condition logic used at the step level can apply to a subflow step or subflow container step.
165
-
166
- ### 5.2 Referencing Conditional Steps in a Subflow
167
-
168
- In a future subflow scenario, you might do:
169
-
170
- ```ts
171
- flow.subflow(
172
- {
173
- slug: 'payment_flow',
174
- runIf: { run: { paymentRequired: true } },
175
- },
176
- PaymentFlow
177
- );
178
- ```
179
-
180
- - The condition is checked before starting the subflow’s “root steps.”
181
- - If `paymentRequired` is false, the subflow is entirely skipped.
182
- - If we used flattened subflows, it’s effectively skipping those step definitions.
183
- - If we used standalone subflows, we skip creating or scheduling the subflow run.
184
-
185
- Hence, our **step-level** condition approach can seamlessly handle a “subflow root step” with the same logic.
186
-
187
- ---
188
-
189
- ## 6. Callbacks for Condition Checking
190
-
191
- ### 6.1 Design Options
192
-
193
- Eventually, we want to allow more dynamic checks—for example:
194
-
195
- ```ts
196
- new Flow().step(
197
- {
198
- slug: 'vipStep',
199
- runIf: {
200
- $type: 'functionCallback',
201
- code: `return input.run.userIsVIP === true && input.otherStep.prop > 100;`,
202
- },
203
- },
204
- async (input) => {
205
- /* ... */
206
- }
207
- );
208
- ```
209
-
210
- Or:
211
-
212
- ```ts
213
- runIf: async function checkCondition(input) {
214
- return input.run.userIsVIP === true && input.websiteResponse.status === 200;
215
- }
216
- ```
217
-
218
- **How does that get stored or executed?**
219
-
220
- - One approach: We store it as a text or code snippet, interpret it in the thread worker with a safe sandbox, or compile it.
221
- - Another approach: We store a small reference in the DB, and the Worker calls a “condition-check” function that we attach to the DSL.
222
-
223
- For the MVP, we can skip full dynamic code in the DB. We simply do partial matching in Postgres or in the Worker. The callback approach can come later, reusing the same “condition” column.
224
-
225
- ### 6.2 Potential Worker APIs: skip_task or Condition-Only Tasks
226
-
227
- Imagine a scenario:
228
-
229
- 1. The Worker polls for a step.
230
- 2. The Worker first checks a separate user-defined “condition function.”
231
- - If it fails, the Worker calls `pgflow.skip_task(...)`.
232
- - If it passes, the Worker proceeds to do the real step handler and then calls `complete_task(...)`.
233
-
234
- Alternatively, we could generate a “mini-task” specifically for condition checks, but that might be overkill. More likely, we do the check at the moment we decide to run the step.
235
-
236
- **MVP**: We do the check in the database side before launching the step as “started.” In the future, if we want a user-provided callback, we might do it in the Worker code right after polling but before we mark the step “started.” If the condition fails, we call `skip_task(...)`.
237
-
238
- ---
239
-
240
- ## 7. Handling Versioning & Flow Slugs
241
-
242
- - **Immutable flows**: If you change the step’s `runIf` or `runUnless` condition, that is effectively a new flow shape → new slug.
243
- - This lines up with the existing approach of changing step definitions or dependencies.
244
- - If at some point we allow a purely user-supplied JavaScript callback, changing the callback’s code still means a new flow slug for the new logic.
245
-
246
- ---
247
-
248
- ## 8. Proposed Step Condition Implementation Plan
249
-
250
- Below is a concise plan for how to add `runIf` / `runUnless` in an MVP:
251
-
252
- 1. **Schema**:
253
-
254
- - Add two new columns to `steps`, e.g.:
255
- - `condition_type TEXT NULL` (enum: `'runIf' | 'runUnless'`)
256
- - `condition_json JSONB NULL`
257
- - No changes to `runs` or `step_states` except we might add a “skipped” status.
258
-
259
- 2. **DSL**:
260
-
261
- - Let users specify:
262
- ```ts
263
- .step({
264
- slug: 'myStep',
265
- runIf: { run: { userIsVIP: true } }
266
- }, handler)
267
- ```
268
- or
269
- ```ts
270
- .step({
271
- slug: 'myStep',
272
- runUnless: { run: { userIsVIP: true } }
273
- }, handler)
274
- ```
275
- - We store that condition in the DB as either `condition_type = 'runIf'` and `condition_json = {"run": {"userIsVIP": true}}`, etc.
276
-
277
- 3. **Engine Logic**:
278
-
279
- - After a step’s dependencies all succeed/skip, we do a quick check:
280
- - Build composite input (like we do for tasks).
281
- - Evaluate `condition_json` with a simple “containment match” if `runIf`, or `@>` logic in Postgres (some partial JSON check). If `runUnless`, invert the result.
282
- - If the match fails, mark the step as SKIPPED, do not queue the step.
283
- - If the match passes, queue a normal task.
284
-
285
- 4. **Skip Handling**:
286
-
287
- - Add “skipped” as a valid state.
288
- - A step that depends on a skipped step can still run once _all_ dependencies are in `completed` or `skipped` states (i.e., no “in progress” steps remain).
289
-
290
- 5. **Future Callback**:
291
- - Later, we may store a function reference or snippet in `condition_json`, and the Worker can attempt to run that code. If it returns `false`, Worker calls `skip_task(...)`.
292
-
293
- ---
294
-
295
- ## 9. Conclusion
296
-
297
- Implementing a step-level condition (i.e., `runIf`, `runUnless`) is **very feasible** in an MVP. By storing a small JSON-based condition in the `steps` table and adding a “skipped” status, we allow steps to be conditionally short-circuited with minimal overhead.
298
-
299
- - **Extend to Subflows**: The same logic can apply to a subflow’s root step or “subflow container step,” letting us skip entire subflows if a condition is not met.
300
- - **Callbacks**: We can eventually let Worker code or user-defined scripts check conditions and explicitly skip tasks.
301
- - **Versioning**: Changing conditions means a new flow slug, consistent with pgflow’s immutable approach.
302
-
303
- This approach yields a straightforward, robust foundation to handle conditional logic in **pgflow** while laying the groundwork for advanced branching or dynamic subflows in the future.
@@ -1,139 +0,0 @@
1
- # Conditional Skipping Feature in pgflow DSL
2
-
3
- This document provides a comprehensive overview of how to implement conditional skipping (as described in [condition.md](../core/prompts/condition.md)) for workflow steps in **pgflow**. It covers:
4
-
5
- - Why conditional skipping could be valuable, even at an MVP stage
6
- - Pros and cons of implementing it
7
- - Possible struggles or pitfalls
8
- - Estimated effort (in hours) to implement
9
-
10
- ---
11
-
12
- ## What Is Conditional Skipping?
13
-
14
- In many workflows, certain steps only make sense to run under particular conditions. For example:
15
-
16
- - You might only want to download a file if a certain flag was provided in the input.
17
- - You might want to do summarization or sentiment analysis only if the previous steps indicate a specific data pattern.
18
-
19
- “Conditional steps” allow you to define a JSON-based condition in the step’s metadata (e.g., `runIf` or `runUnless`) that automatically determines whether the step should run or be “skipped.” If a step is skipped, steps that depend on it are also skipped.
20
-
21
- ---
22
-
23
- ## Why It May Be Valuable for an MVP
24
-
25
- 1. **User Experience & Flexibility**
26
- - Immediate advantage for users who need branching logic.
27
- - Allows simpler workflows without requiring external condition-checking in the code.
28
-
29
- 2. **Reduction in Unnecessary Processing**
30
- - If the condition is not met, the step (and its dependents) will skip automatically, saving computational resources and time.
31
-
32
- 3. **Clearer Workflow Semantics**
33
- - By making conditions an explicit feature, you avoid confusion and keep the logic in one place (the flow definition) rather than scattering it between application code and partial definitions.
34
-
35
- 4. **Encourages “Flow-Based” Mindset**
36
- - Eliminates the need for users to manually handle branching logic in external scripts or custom code.
37
- - Aligns with a “batteries included” approach that is often helpful for early adopters.
38
-
39
- ---
40
-
41
- ## Pros and Cons
42
-
43
- ### Pros
44
-
45
- - **Encourages Declarative Workflow Rules**
46
- Users can keep logic in the same domain as the flow definition, which makes reading and maintaining flows simpler.
47
- - **Reduces Boilerplate**
48
- Instead of manually checking inputs at the beginning of a handler and deciding whether to no-op or fail, the step can be automatically skipped.
49
- - **Potential for Greater Adoption**
50
- A built-in conditional system often increases trust and usage among developers familiar with high-level workflow or DAG systems.
51
- - **Cleaner Downtime**
52
- Skipped steps do not consume resources (no tasks enqueued, no worker overhead).
53
-
54
- ### Cons
55
-
56
- - **Extra Implementation Complexity**
57
- You must handle edge cases, such as partial input conditions, data type subtleties, or the JSON containment operator usage in PostgreSQL.
58
- - **Increased Maintenance**
59
- Introducing more logic in your DSL means more code to test and maintain (e.g., must handle backward compatibility if you change how conditions work in the future).
60
- - **Potential for Confusion**
61
- If the condition syntax is not user-friendly, or if debug messages about skipping are not clear, users could be confused about why certain steps did not run.
62
-
63
- ---
64
-
65
- ## Possible Struggles & Pitfalls
66
-
67
- 1. **Correctly Handling JSON Conditions**
68
- - Relying on the JSON containment operator `@>` means carefully constructing the query or expression.
69
- - Must confirm that partial matches and data mismatches are handled as expected (e.g., numeric vs. string type).
70
-
71
- 2. **Maintaining Type Safety**
72
- - If the DSL automatically infers types, adding a `runIf` or `runUnless` property must still be type-safe.
73
- - If referencing a step’s output in the condition, the system should ensure that property actually exists.
74
-
75
- 3. **Edge Cases with Skipped Steps**
76
- - Skipping means “completing” with no output—how do you handle downstream steps that expect data from a skipped step?
77
- - You may need a convention in your DB or runtime state indicating that the step is “skipped” rather than “completed” or “failed.”
78
-
79
- 4. **Backward Compatibility**
80
- - If you release an MVP and later refine the condition syntax, you’ll need a migration or transitional logic for existing flows.
81
-
82
- ---
83
-
84
- ## Difficulty of Implementation
85
-
86
- Implementing this feature touches multiple layers of the system:
87
-
88
- 1. **DSL Layer**
89
- - Defining new options (`runIf`, `runUnless`) that users can provide.
90
- - Ensuring these options are typed properly in TypeScript.
91
-
92
- 2. **API / Database Layer**
93
- - Storing the condition JSON in `steps` (or an equivalent) so that it can be evaluated at runtime.
94
- - Updating your state transition logic to evaluate the condition before starting a step.
95
-
96
- 3. **Runtime Execution Flow**
97
- - Where you currently mark a step as “ready,” insert a check to see if conditions pass. If they do not pass, mark as “skipped.”
98
- - Skip all dependent steps, or propagate the skip state as needed.
99
-
100
- 4. **Testing & Debugging**
101
- - You’ll likely want multiple test scenarios: conditions that pass, conditions that fail, partial matches, data type mismatches, etc.
102
- - Logging or debug statements so developers can see clearly why a step is skipped.
103
-
104
- Given these considerations, the feature is not trivial, but it is also not the largest feature you could add. It primarily requires changes in the step lifecycle logic, the database schema or metadata handling, and a bit of additional DSL syntax.
105
-
106
- ---
107
-
108
- ## Estimated Time to Implement
109
-
110
- Below is a rough breakdown of tasks and potential time estimates:
111
-
112
- 1. **Design & Schema Updates** (2–4 hours)
113
- - Decide how to store the condition in the `steps` table (e.g., additional `condition` JSONB column).
114
- - Optionally add an ENUM or a text field for “skip logic type” (runIf, runUnless, etc.).
115
-
116
- 2. **DSL Adjustments & TypeScript** (3–5 hours)
117
- - Add `runIf` / `runUnless` options to your step definition in TypeScript.
118
- - Ensure thorough type checks so that the condition references existing fields.
119
-
120
- 3. **Execution Logic** (4–6 hours)
121
- - Modify the function that transitions a step from “pending” to “ready” to evaluate the condition.
122
- - Add skip logic if the condition fails.
123
- - Propagate skip status to dependent steps.
124
-
125
- 4. **Testing & Docs** (3–5 hours)
126
- - Write integration tests for basic and complex scenarios.
127
- - Document the feature thoroughly, including examples that show how to define a condition, how skipping is reported, etc.
128
-
129
- **Total Estimated Range**: ~12–20 hours
130
-
131
- This is a ballpark estimate assuming familiarity with the pgflow codebase and the underlying PostgreSQL mechanics. It will vary depending on specific architecture, team skill set, and how thoroughly you wish to test or document the new feature.
132
-
133
- ---
134
-
135
- ## Conclusion
136
-
137
- Implementing conditional skipping can bring significant value, even for an MVP. It grants your users more control over their flow logic, potentially reduces redundant work, and keeps logic centralized in the workflow definition. The feature does introduce additional complexity and might take around 12–20 hours to implement thoroughly, covering design, DSL changes, database-level adjustments, and testing.
138
-
139
- If your MVP’s core promise is to deliver a robust, easy-to-use workflow engine, adding conditional skipping early can differentiate your project by showcasing advanced flow capabilities and reducing the need for external branching logic. If time is tight, you could defer some of the advanced type-safety checks or skip certain advanced condition edge cases—but the fundamental skipping mechanism itself is likely beneficial enough to justify the investment for most workflows.