Initial Commit
This commit is contained in:
99
openspec/archive/2026-05-24-enterprise-workflow/proposal.md
Normal file
99
openspec/archive/2026-05-24-enterprise-workflow/proposal.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Enterprise migration: @suaveplan/workflow
|
||||
|
||||
**Package**: `@suaveplan/workflow`
|
||||
**Status**: PROPOSED
|
||||
**Type**: new-package (migration from upstream)
|
||||
**Date**: 2026-05-24
|
||||
**Design**: ./design.md (deferred — covered inline)
|
||||
**Spec delta**: ./specs/workflow/spec.md
|
||||
|
||||
## Why
|
||||
|
||||
Upstream `../suaveplan/packages/universal/workflow/` ships a tree-shakeable, zero-runtime-dep workflow / pipeline orchestration core with sequential + parallel step execution, conditional branching, saga compensation, retry strategies, timeouts, lifecycle hooks, and a fluent builder. It is consumed by higher-level packages (rules-engine, form-builder, multi-step business processes) and must land on genesis before any of those can migrate. The 43-file source has 100% test parity in upstream and we preserve that surface verbatim while bolting on the standard genesis additions: stable error codes via `defineCodes`, Zod schemas at the public boundary, OTel instruments, injectable `Clock` for deterministic timing.
|
||||
|
||||
## What changes
|
||||
|
||||
New package `@suaveplan/workflow` at `packages/foundation/flow/workflow/` (sibling to `fsm`, `job-scheduler`, `streams`). The public surface mirrors upstream exactly:
|
||||
|
||||
- ADDED: `createWorkflow<TData>(name, id?)` and `WorkflowBuilder<TData>` — fluent builder with `addStep`, `on`, `onError`, `withTimeout`, `buildConfig`, `build`, `run`.
|
||||
- ADDED: `StepBuilder<TData>` — `withCompensation`, `withGuard`, `withRetry`, `withTimeout`, `build`.
|
||||
- ADDED: `WorkflowEngine<TData>` and `createWorkflowEngine` — the orchestrator with `run`, `cancel`, `hooks` accessor.
|
||||
- ADDED: `Step<TCtx, TResult>` and `createStep` — base unit with guard / retry / timeout / compensate.
|
||||
- ADDED: `ParallelStep<TCtx>` / `createParallelStep` with `failFast` + `maxConcurrency`.
|
||||
- ADDED: `ConditionalStep<TCtx>` / `createConditionalStep` with `onTrue` / `onFalse` branches.
|
||||
- ADDED: `Saga<TCtx>` / `createSaga` — sequential steps with automatic reverse-order compensation.
|
||||
- ADDED: `Compensator<TCtx>` / `createCompensator` — explicit compensation registry + `compensateAll` / `compensateSteps`.
|
||||
- ADDED: `HookRegistry<TCtx>` / `createHookRegistry` + `buildStepPayload` — lifecycle-event dispatch.
|
||||
- ADDED: `withRetry`, `createRetryWrapper`, `resolveRetryConfig`, `RetryPresets` — middleware-style retry.
|
||||
- ADDED: `WorkflowContext<TData>` / `createContext` + `ContextSnapshot<TData>`.
|
||||
- ADDED: utilities — `generateId`, `delay`, `withTimeout`, `calculateRetryDelay`, `now`.
|
||||
- ADDED: constants — `DEFAULT_*` retry / timeout values.
|
||||
- ADDED: types — `WorkflowStatus`, `StepStatus`, `StepExecutor`, `CompensationFn`, `GuardFn`, `RetryStrategy`, `RetryConfig`, `StepResult`, `StepDefinition`, `WorkflowResult`, `HookEvent`, `HookPayload`, `HookListener`, `WorkflowConfig`, plus `WorkflowTransitionData` / `WorkflowTransitionHookMap` / `WorkflowTransitioningHandler` for the data-pipeline hook.
|
||||
- ADDED: `WORKFLOW_CODES` + `WorkflowError` via `@suaveplan/error`'s `defineCodes` — codes `WORKFLOW.STEP_FAILED`, `WORKFLOW.TIMEOUT`, `WORKFLOW.CANCELLED`, `WORKFLOW.COMPENSATION_FAILED`, `WORKFLOW.GUARD_REJECTED`, `WORKFLOW.RETRY_EXHAUSTED`.
|
||||
|
||||
Genesis-only additions on top of upstream:
|
||||
|
||||
- `WorkflowError` extends `SuaveplanError<WorkflowCode>` (upstream throws plain `Error`).
|
||||
- Zod-validated `WorkflowConfig` schema at `buildConfig()` boundary.
|
||||
- OTel counter `workflow.step.executed_total{outcome,step_id}`, counter `workflow.step.compensated_total{outcome,step_id}`, histogram `workflow.step.duration_ms{step_id}` — emitted from `Step.run` and `Compensator.compensateAll`.
|
||||
- Injectable `Clock` (defaults to `Date.now`) on `Step`, `WorkflowEngine`, `Saga`, `Compensator`, `HookRegistry` — makes `now()`-driven timing deterministic in tests.
|
||||
|
||||
## Parity Checklist
|
||||
|
||||
| Capability | Upstream | Migrated | Notes |
|
||||
|---|---|---|---|
|
||||
| Fluent builder (`createWorkflow`, `WorkflowBuilder`, `StepBuilder`) | ✅ | ✅ | |
|
||||
| `WorkflowEngine.run` with `onError: stop/compensate/continue` | ✅ | ✅ | |
|
||||
| `WorkflowEngine.cancel` mid-run | ✅ | ✅ | |
|
||||
| `Step` with guard / retry / timeout / compensate | ✅ | ✅ | |
|
||||
| `ParallelStep` with `failFast` + `maxConcurrency` batching | ✅ | ✅ | |
|
||||
| `ConditionalStep` with `onTrue` / `onFalse` + skip when no false branch | ✅ | ✅ | |
|
||||
| `Saga` orchestrator with reverse-order compensation | ✅ | ✅ | |
|
||||
| `Compensator` with `compensateAll` + `compensateSteps` + `continueOnError` | ✅ | ✅ | |
|
||||
| `HookRegistry` (`on`, `once`, `off`, `emit`, `listenerCount`) | ✅ | ✅ | |
|
||||
| `buildStepPayload` helper | ✅ | ✅ | |
|
||||
| `withRetry`, `createRetryWrapper`, `resolveRetryConfig`, `RetryPresets` | ✅ | ✅ | |
|
||||
| `WorkflowContext` (`updateData`, `setData`, `addStepResult`, `setMeta`/`getMeta`, `snapshot`, `reset`) | ✅ | ✅ | |
|
||||
| `calculateRetryDelay` fixed/linear/exponential | ✅ | ✅ | |
|
||||
| `generateId`, `delay`, `withTimeout`, `now` utilities | ✅ | ✅ | |
|
||||
| Constants (`DEFAULT_*`) | ✅ | ✅ | |
|
||||
| Transition data-pipeline hook (`workflow:transitioning`) | ✅ | ✅ | |
|
||||
| Subpath exports per feature module | ✅ | ✅ | |
|
||||
| Plain `Error` throws | ✅ | ⬆️ | Replaced by `WorkflowError` + stable codes per genesis rule. |
|
||||
| Zod schema validation at config boundary | 🚫 | ⬆️ | Genesis addition — `WorkflowConfig` is Zod-validated in `buildConfig`. |
|
||||
| OTel counters + histogram | 🚫 | ⬆️ | Genesis addition — step execution + compensation instrumented. |
|
||||
| Injectable `Clock` | 🚫 | ⬆️ | Genesis addition — defaults to `Date.now`, takes `Clock` from `@suaveplan/types`. |
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Durable replay / persistence — upstream does not ship this; out of scope for the first cut and would be a separate sibling package (`workflow-store`) per the genesis modular pattern.
|
||||
- Distributed execution — upstream is single-process; genesis preserves that boundary.
|
||||
|
||||
## Source mapping
|
||||
|
||||
This change derives from `../suaveplan/packages/universal/workflow/src/` (43 files). One genesis feature dir per upstream feature dir; file names preserved.
|
||||
|
||||
| Requirement | Source path |
|
||||
|---|---|
|
||||
| Step run / compensate / guard | `src/steps/step/step.ts` |
|
||||
| Parallel step | `src/steps/parallel-step/parallel-step.ts` |
|
||||
| Conditional step | `src/steps/conditional-step/conditional-step.ts` |
|
||||
| Compensator | `src/compensation/compensator/compensator.ts` |
|
||||
| Saga | `src/compensation/saga/saga.ts` |
|
||||
| Workflow context | `src/core/context/context.ts` |
|
||||
| Workflow builder | `src/core/workflow-builder/workflow-builder.ts` |
|
||||
| Workflow engine | `src/core/workflow-engine/workflow-engine.ts` |
|
||||
| Hook registry | `src/middleware/hook/hook.ts` |
|
||||
| Retry middleware | `src/middleware/retry/retry.ts` |
|
||||
| Utils + constants | `src/utils/utils.ts`, `src/constants/index.ts` |
|
||||
| Common + hook types | `src/types/common.ts`, `src/types/workflow-hooks.ts` |
|
||||
|
||||
## Verification approach
|
||||
|
||||
Co-located `.test.ts` per feature directory under `src/`. Every requirement in `specs/workflow/spec.md` has a `**Verified by:**` line pointing at a concrete `it(...)` title in one of these files. 100% coverage gate via `bunx turbo test:coverage --filter=@suaveplan/workflow`.
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
No genesis-side dependents yet (`bunx turbo ls --filter=...@suaveplan/workflow` returns empty — additive only). The package is a prerequisite for future migrations of rules-engine and form-builder.
|
||||
Reference in New Issue
Block a user