groundswell 0.0.2 → 0.0.3
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/dist/__tests__/adversarial/attachChild-performance.test.d.ts +16 -0
- package/dist/__tests__/adversarial/attachChild-performance.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/attachChild-performance.test.js +187 -0
- package/dist/__tests__/adversarial/attachChild-performance.test.js.map +1 -0
- package/dist/__tests__/adversarial/circular-reference.test.d.ts +13 -0
- package/dist/__tests__/adversarial/circular-reference.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/circular-reference.test.js +92 -0
- package/dist/__tests__/adversarial/circular-reference.test.js.map +1 -0
- package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts +16 -0
- package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/complex-circular-reference.test.js +127 -0
- package/dist/__tests__/adversarial/complex-circular-reference.test.js.map +1 -0
- package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts +21 -0
- package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/concurrent-task-failures.test.js +667 -0
- package/dist/__tests__/adversarial/concurrent-task-failures.test.js.map +1 -0
- package/dist/__tests__/adversarial/deep-analysis.test.d.ts +6 -0
- package/dist/__tests__/adversarial/deep-analysis.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/deep-analysis.test.js +877 -0
- package/dist/__tests__/adversarial/deep-analysis.test.js.map +1 -0
- package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts +13 -0
- package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js +186 -0
- package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js.map +1 -0
- package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts +6 -0
- package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/e2e-prd-validation.test.js +626 -0
- package/dist/__tests__/adversarial/e2e-prd-validation.test.js.map +1 -0
- package/dist/__tests__/adversarial/edge-case.test.d.ts +6 -0
- package/dist/__tests__/adversarial/edge-case.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/edge-case.test.js +857 -0
- package/dist/__tests__/adversarial/edge-case.test.js.map +1 -0
- package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts +20 -0
- package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/error-merge-strategy.test.js +907 -0
- package/dist/__tests__/adversarial/error-merge-strategy.test.js.map +1 -0
- package/dist/__tests__/adversarial/incremental-performance.test.d.ts +2 -0
- package/dist/__tests__/adversarial/incremental-performance.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/incremental-performance.test.js +113 -0
- package/dist/__tests__/adversarial/incremental-performance.test.js.map +1 -0
- package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts +22 -0
- package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js +383 -0
- package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js.map +1 -0
- package/dist/__tests__/adversarial/observer-propagation.test.d.ts +21 -0
- package/dist/__tests__/adversarial/observer-propagation.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/observer-propagation.test.js +404 -0
- package/dist/__tests__/adversarial/observer-propagation.test.js.map +1 -0
- package/dist/__tests__/adversarial/parent-validation.test.d.ts +13 -0
- package/dist/__tests__/adversarial/parent-validation.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/parent-validation.test.js +128 -0
- package/dist/__tests__/adversarial/parent-validation.test.js.map +1 -0
- package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts +20 -0
- package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/prd-12-2-compliance.test.js +482 -0
- package/dist/__tests__/adversarial/prd-12-2-compliance.test.js.map +1 -0
- package/dist/__tests__/adversarial/prd-compliance.test.d.ts +6 -0
- package/dist/__tests__/adversarial/prd-compliance.test.d.ts.map +1 -0
- package/dist/__tests__/adversarial/prd-compliance.test.js +886 -0
- package/dist/__tests__/adversarial/prd-compliance.test.js.map +1 -0
- package/dist/__tests__/compatibility/backward-compatibility.test.d.ts +22 -0
- package/dist/__tests__/compatibility/backward-compatibility.test.d.ts.map +1 -0
- package/dist/__tests__/compatibility/backward-compatibility.test.js +1843 -0
- package/dist/__tests__/compatibility/backward-compatibility.test.js.map +1 -0
- package/dist/__tests__/helpers/index.d.ts +10 -0
- package/dist/__tests__/helpers/index.d.ts.map +1 -0
- package/{src/__tests__/helpers/index.ts → dist/__tests__/helpers/index.js} +2 -10
- package/dist/__tests__/helpers/index.js.map +1 -0
- package/dist/__tests__/helpers/tree-verification.d.ts +90 -0
- package/dist/__tests__/helpers/tree-verification.d.ts.map +1 -0
- package/dist/__tests__/helpers/tree-verification.js +202 -0
- package/dist/__tests__/helpers/tree-verification.js.map +1 -0
- package/dist/__tests__/integration/agent-workflow.test.d.ts +2 -0
- package/dist/__tests__/integration/agent-workflow.test.d.ts.map +1 -0
- package/dist/__tests__/integration/agent-workflow.test.js +256 -0
- package/dist/__tests__/integration/agent-workflow.test.js.map +1 -0
- package/dist/__tests__/integration/bidirectional-consistency.test.d.ts +14 -0
- package/dist/__tests__/integration/bidirectional-consistency.test.d.ts.map +1 -0
- package/dist/__tests__/integration/bidirectional-consistency.test.js +668 -0
- package/dist/__tests__/integration/bidirectional-consistency.test.js.map +1 -0
- package/dist/__tests__/integration/observer-logging.test.d.ts +2 -0
- package/dist/__tests__/integration/observer-logging.test.d.ts.map +1 -0
- package/dist/__tests__/integration/observer-logging.test.js +517 -0
- package/dist/__tests__/integration/observer-logging.test.js.map +1 -0
- package/dist/__tests__/integration/tree-mirroring.test.d.ts +2 -0
- package/dist/__tests__/integration/tree-mirroring.test.d.ts.map +1 -0
- package/dist/__tests__/integration/tree-mirroring.test.js +117 -0
- package/dist/__tests__/integration/tree-mirroring.test.js.map +1 -0
- package/dist/__tests__/integration/workflow-reparenting.test.d.ts +12 -0
- package/dist/__tests__/integration/workflow-reparenting.test.d.ts.map +1 -0
- package/dist/__tests__/integration/workflow-reparenting.test.js +239 -0
- package/dist/__tests__/integration/workflow-reparenting.test.js.map +1 -0
- package/dist/__tests__/unit/agent.test.d.ts +2 -0
- package/dist/__tests__/unit/agent.test.d.ts.map +1 -0
- package/dist/__tests__/unit/agent.test.js +143 -0
- package/dist/__tests__/unit/agent.test.js.map +1 -0
- package/dist/__tests__/unit/cache-key.test.d.ts +5 -0
- package/dist/__tests__/unit/cache-key.test.d.ts.map +1 -0
- package/dist/__tests__/unit/cache-key.test.js +145 -0
- package/dist/__tests__/unit/cache-key.test.js.map +1 -0
- package/dist/__tests__/unit/cache.test.d.ts +5 -0
- package/dist/__tests__/unit/cache.test.d.ts.map +1 -0
- package/dist/__tests__/unit/cache.test.js +132 -0
- package/dist/__tests__/unit/cache.test.js.map +1 -0
- package/dist/__tests__/unit/context.test.d.ts +2 -0
- package/dist/__tests__/unit/context.test.d.ts.map +1 -0
- package/dist/__tests__/unit/context.test.js +220 -0
- package/dist/__tests__/unit/context.test.js.map +1 -0
- package/dist/__tests__/unit/decorators.test.d.ts +2 -0
- package/dist/__tests__/unit/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/unit/decorators.test.js +162 -0
- package/dist/__tests__/unit/decorators.test.js.map +1 -0
- package/dist/__tests__/unit/introspection-tools.test.d.ts +5 -0
- package/dist/__tests__/unit/introspection-tools.test.d.ts.map +1 -0
- package/dist/__tests__/unit/introspection-tools.test.js +191 -0
- package/dist/__tests__/unit/introspection-tools.test.js.map +1 -0
- package/dist/__tests__/unit/logger.test.d.ts +2 -0
- package/dist/__tests__/unit/logger.test.d.ts.map +1 -0
- package/dist/__tests__/unit/logger.test.js +241 -0
- package/dist/__tests__/unit/logger.test.js.map +1 -0
- package/dist/__tests__/unit/observable.test.d.ts +2 -0
- package/dist/__tests__/unit/observable.test.d.ts.map +1 -0
- package/dist/__tests__/unit/observable.test.js +251 -0
- package/dist/__tests__/unit/observable.test.js.map +1 -0
- package/dist/__tests__/unit/prompt.test.d.ts +2 -0
- package/dist/__tests__/unit/prompt.test.d.ts.map +1 -0
- package/dist/__tests__/unit/prompt.test.js +113 -0
- package/dist/__tests__/unit/prompt.test.js.map +1 -0
- package/dist/__tests__/unit/reflection.test.d.ts +5 -0
- package/dist/__tests__/unit/reflection.test.d.ts.map +1 -0
- package/dist/__tests__/unit/reflection.test.js +160 -0
- package/dist/__tests__/unit/reflection.test.js.map +1 -0
- package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts +2 -0
- package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts.map +1 -0
- package/dist/__tests__/unit/tree-debugger-incremental.test.js +136 -0
- package/dist/__tests__/unit/tree-debugger-incremental.test.js.map +1 -0
- package/dist/__tests__/unit/tree-debugger.test.d.ts +2 -0
- package/dist/__tests__/unit/tree-debugger.test.d.ts.map +1 -0
- package/dist/__tests__/unit/tree-debugger.test.js +69 -0
- package/dist/__tests__/unit/tree-debugger.test.js.map +1 -0
- package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts +2 -0
- package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts.map +1 -0
- package/dist/__tests__/unit/utils/workflow-error-utils.test.js +154 -0
- package/dist/__tests__/unit/utils/workflow-error-utils.test.js.map +1 -0
- package/dist/__tests__/unit/workflow-detachChild.test.d.ts +2 -0
- package/dist/__tests__/unit/workflow-detachChild.test.d.ts.map +1 -0
- package/dist/__tests__/unit/workflow-detachChild.test.js +76 -0
- package/dist/__tests__/unit/workflow-detachChild.test.js.map +1 -0
- package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts +2 -0
- package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts.map +1 -0
- package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js +122 -0
- package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js.map +1 -0
- package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts +2 -0
- package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts.map +1 -0
- package/dist/__tests__/unit/workflow-isDescendantOf.test.js +140 -0
- package/dist/__tests__/unit/workflow-isDescendantOf.test.js.map +1 -0
- package/dist/__tests__/unit/workflow.test.d.ts +2 -0
- package/dist/__tests__/unit/workflow.test.d.ts.map +1 -0
- package/dist/__tests__/unit/workflow.test.js +330 -0
- package/dist/__tests__/unit/workflow.test.js.map +1 -0
- package/dist/cache/cache-key.d.ts +66 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +195 -0
- package/dist/cache/cache-key.js.map +1 -0
- package/dist/cache/cache.d.ts +104 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +179 -0
- package/dist/cache/cache.js.map +1 -0
- package/{src/cache/index.ts → dist/cache/index.d.ts} +1 -1
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/core/agent.d.ts +112 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +426 -0
- package/dist/core/agent.js.map +1 -0
- package/{src/core/context.ts → dist/core/context.d.ts} +16 -67
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +80 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/event-tree.d.ts +72 -0
- package/dist/core/event-tree.d.ts.map +1 -0
- package/dist/core/event-tree.js +211 -0
- package/dist/core/event-tree.js.map +1 -0
- package/{src/core/factory.ts → dist/core/factory.d.ts} +6 -27
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +110 -0
- package/dist/core/factory.js.map +1 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +2 -10
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +50 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +91 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp-handler.d.ts +69 -0
- package/dist/core/mcp-handler.d.ts.map +1 -0
- package/dist/core/mcp-handler.js +143 -0
- package/dist/core/mcp-handler.js.map +1 -0
- package/dist/core/prompt.d.ts +80 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +120 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/workflow-context.d.ts +57 -0
- package/dist/core/workflow-context.d.ts.map +1 -0
- package/dist/core/workflow-context.js +263 -0
- package/dist/core/workflow-context.js.map +1 -0
- package/dist/core/workflow.d.ts +241 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +464 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/debugger/index.d.ts +2 -0
- package/dist/debugger/index.d.ts.map +1 -0
- package/{src/debugger/index.ts → dist/debugger/index.js} +1 -0
- package/dist/debugger/index.js.map +1 -0
- package/dist/debugger/tree-debugger.d.ts +71 -0
- package/dist/debugger/tree-debugger.d.ts.map +1 -0
- package/dist/debugger/tree-debugger.js +198 -0
- package/dist/debugger/tree-debugger.js.map +1 -0
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/{src/decorators/index.ts → dist/decorators/index.js} +1 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/observed-state.d.ts +32 -0
- package/dist/decorators/observed-state.d.ts.map +1 -0
- package/dist/decorators/observed-state.js +79 -0
- package/dist/decorators/observed-state.js.map +1 -0
- package/dist/decorators/step.d.ts +15 -0
- package/dist/decorators/step.d.ts.map +1 -0
- package/dist/decorators/step.js +110 -0
- package/dist/decorators/step.js.map +1 -0
- package/dist/decorators/task.d.ts +50 -0
- package/dist/decorators/task.d.ts.map +1 -0
- package/dist/decorators/task.js +118 -0
- package/dist/decorators/task.js.map +1 -0
- package/dist/examples/index.d.ts +3 -0
- package/dist/examples/index.d.ts.map +1 -0
- package/{src/examples/index.ts → dist/examples/index.js} +1 -0
- package/dist/examples/index.js.map +1 -0
- package/dist/examples/tdd-orchestrator.d.ts +15 -0
- package/dist/examples/tdd-orchestrator.d.ts.map +1 -0
- package/dist/examples/tdd-orchestrator.js +121 -0
- package/dist/examples/tdd-orchestrator.js.map +1 -0
- package/dist/examples/test-cycle-workflow.d.ts +14 -0
- package/dist/examples/test-cycle-workflow.d.ts.map +1 -0
- package/dist/examples/test-cycle-workflow.js +116 -0
- package/dist/examples/test-cycle-workflow.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/reflection/index.d.ts +5 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/{src/reflection/index.ts → dist/reflection/index.js} +1 -1
- package/dist/reflection/index.js.map +1 -0
- package/dist/reflection/reflection.d.ts +84 -0
- package/dist/reflection/reflection.d.ts.map +1 -0
- package/dist/reflection/reflection.js +329 -0
- package/dist/reflection/reflection.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +165 -0
- package/dist/tools/introspection.d.ts.map +1 -0
- package/dist/tools/introspection.js +324 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/types/agent.d.ts +66 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +6 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/decorators.d.ts +31 -0
- package/dist/types/decorators.d.ts.map +1 -0
- package/dist/types/decorators.js +2 -0
- package/dist/types/decorators.js.map +1 -0
- package/dist/types/error-strategy.d.ts +13 -0
- package/dist/types/error-strategy.d.ts.map +1 -0
- package/dist/types/error-strategy.js +2 -0
- package/dist/types/error-strategy.js.map +1 -0
- package/dist/types/error.d.ts +20 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +2 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/events.d.ts +87 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logging.d.ts +24 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/observer.d.ts +18 -0
- package/dist/types/observer.d.ts.map +1 -0
- package/dist/types/observer.js +2 -0
- package/dist/types/observer.js.map +1 -0
- package/dist/types/prompt.d.ts +31 -0
- package/dist/types/prompt.d.ts.map +1 -0
- package/dist/types/prompt.js +6 -0
- package/dist/types/prompt.js.map +1 -0
- package/dist/types/reflection.d.ts +96 -0
- package/dist/types/reflection.d.ts.map +1 -0
- package/dist/types/reflection.js +24 -0
- package/dist/types/reflection.js.map +1 -0
- package/dist/types/sdk-primitives.d.ts +118 -0
- package/dist/types/sdk-primitives.d.ts.map +1 -0
- package/dist/types/sdk-primitives.js +6 -0
- package/dist/types/sdk-primitives.js.map +1 -0
- package/{src/types/snapshot.ts → dist/types/snapshot.d.ts} +5 -5
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/types/workflow-context.d.ts +139 -0
- package/dist/types/workflow-context.d.ts.map +1 -0
- package/dist/types/workflow-context.js +8 -0
- package/dist/types/workflow-context.js.map +1 -0
- package/dist/types/workflow.d.ts +30 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/id.d.ts +6 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +12 -0
- package/dist/utils/id.js.map +1 -0
- package/{src/utils/index.ts → dist/utils/index.d.ts} +1 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/observable.d.ts +54 -0
- package/dist/utils/observable.d.ts.map +1 -0
- package/dist/utils/observable.js +82 -0
- package/dist/utils/observable.js.map +1 -0
- package/dist/utils/workflow-error-utils.d.ts +22 -0
- package/dist/utils/workflow-error-utils.d.ts.map +1 -0
- package/dist/utils/workflow-error-utils.js +45 -0
- package/dist/utils/workflow-error-utils.js.map +1 -0
- package/package.json +5 -2
- package/.claude/commands/subtask-planning/prp-base-create.md +0 -120
- package/.claude/commands/subtask-planning/prp-base-execute.md +0 -65
- package/.claude/commands/task-breakdown.md +0 -94
- package/.claude/settings.local.json +0 -9
- package/.claude/system_prompts/task-breakdown.md +0 -101
- package/PRD.md +0 -543
- package/PRPs/001-hierarchical-workflow-engine.md +0 -2438
- package/PRPs/PRDs/002-agent-prompt.md +0 -390
- package/PRPs/PRDs/003-agent-prompt.md +0 -943
- package/PRPs/PRDs/004-agent-prompt.md +0 -1136
- package/PRPs/PRDs/tasks-001.json +0 -492
- package/PRPs/README.md +0 -83
- package/PRPs/templates/prp_base.md +0 -222
- package/docs/agent.md +0 -422
- package/docs/prompt.md +0 -419
- package/docs/workflow.md +0 -600
- package/examples/README.md +0 -258
- package/examples/examples/01-basic-workflow.ts +0 -100
- package/examples/examples/02-decorator-options.ts +0 -217
- package/examples/examples/03-parent-child.ts +0 -241
- package/examples/examples/04-observers-debugger.ts +0 -340
- package/examples/examples/05-error-handling.ts +0 -387
- package/examples/examples/06-concurrent-tasks.ts +0 -352
- package/examples/examples/07-agent-loops.ts +0 -432
- package/examples/examples/08-sdk-features.ts +0 -667
- package/examples/examples/09-reflection.ts +0 -573
- package/examples/examples/10-introspection.ts +0 -550
- package/examples/examples/11-reparenting-workflows.ts +0 -269
- package/examples/index.ts +0 -147
- package/examples/utils/helpers.ts +0 -57
- package/package-lock.json +0 -2398
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +0 -259
- package/plan/001_d3bb02af4886/backlog.json +0 -867
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +0 -484
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +0 -488
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +0 -581
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +0 -687
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +0 -492
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +0 -932
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +0 -1109
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +0 -802
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +0 -603
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +0 -564
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +0 -518
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +0 -1252
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +0 -364
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +0 -205
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +0 -199
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +0 -134
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +0 -495
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +0 -435
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +0 -558
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +0 -788
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +0 -460
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +0 -454
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +0 -520
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +0 -417
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +0 -245
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +0 -792
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +0 -535
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +0 -190
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +0 -654
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +0 -227
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +0 -345
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +0 -193
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +0 -1323
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +0 -1011
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +0 -927
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +0 -505
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +0 -401
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +0 -142
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +0 -112
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +0 -159
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +0 -549
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +0 -368
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +0 -172
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +0 -175
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +0 -696
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +0 -860
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +0 -1066
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +0 -789
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +0 -1098
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +0 -1037
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +0 -916
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +0 -1045
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +0 -890
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +0 -822
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +0 -668
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +0 -706
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +0 -655
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +0 -426
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +0 -316
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +0 -754
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +0 -719
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +0 -215
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +0 -765
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +0 -718
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +0 -149
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +0 -470
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +0 -332
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +0 -167
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +0 -273
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +0 -434
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +0 -717
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +0 -472
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +0 -125
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +0 -301
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +0 -1170
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +0 -940
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +0 -151
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +0 -650
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +0 -259
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +0 -457
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +0 -346
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +0 -311
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +0 -1565
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +0 -288
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +0 -741
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +0 -652
- package/plan/001_d3bb02af4886/docs/PRP/P1P2-PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/P3P4-PRP.md +0 -1388
- package/plan/001_d3bb02af4886/docs/PRP/P4P5-PRP.md +0 -1136
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +0 -415
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +0 -378
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +0 -713
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +0 -370
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +0 -499
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +0 -230
- package/plan/001_d3bb02af4886/docs/architecture/external_deps.md +0 -358
- package/plan/001_d3bb02af4886/docs/architecture/system_context.md +0 -242
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +0 -1134
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +0 -179
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +0 -214
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +0 -529
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +0 -142
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +0 -304
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +0 -558
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +0 -256
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +0 -346
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +0 -415
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +0 -489
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +0 -218
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +0 -380
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +0 -1923
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +0 -319
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +0 -115
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +0 -134
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +0 -153
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +0 -132
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +0 -716
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +0 -186
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +0 -604
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +0 -213
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +0 -30
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +0 -56
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +0 -53
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +0 -49
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +0 -958
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +0 -339
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +0 -305
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +0 -433
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +0 -1574
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +0 -1014
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_BEST_PRACTICES.md +0 -1929
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_CODE_PATTERNS.md +0 -857
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_INTEGRATION_GUIDE.md +0 -738
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_RESEARCH_INDEX.md +0 -424
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_INDEX.md +0 -291
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_RESEARCH_REPORT.md +0 -1342
- package/plan/001_d3bb02af4886/docs/research/P1P2/RESEARCH_SUMMARY.md +0 -342
- package/plan/001_d3bb02af4886/docs/research/P1P2/anthropic-sdk.md +0 -174
- package/plan/001_d3bb02af4886/docs/research/P1P2/async-local-storage.md +0 -200
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-code-patterns.md +0 -1205
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-decision-matrix.md +0 -421
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-implementation-guide.md +0 -1341
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-integration-guide.md +0 -834
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-patterns.md +0 -1468
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-quick-reference.md +0 -558
- package/plan/001_d3bb02af4886/docs/research/P1P2/zod-schema.md +0 -152
- package/plan/001_d3bb02af4886/docs/research/P3P4/caching-lru.md +0 -116
- package/plan/001_d3bb02af4886/docs/research/P3P4/introspection-tools.md +0 -177
- package/plan/001_d3bb02af4886/docs/research/P3P4/reflection-patterns.md +0 -117
- package/plan/001_d3bb02af4886/docs/research/P4P5/RESEARCH_SUMMARY.md +0 -151
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +0 -376
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +0 -1507
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +0 -949
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +0 -619
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +0 -723
- package/plan/001_d3bb02af4886/docs/research/general/INTROSPECTION_RESEARCH_SUMMARY.md +0 -378
- package/plan/001_d3bb02af4886/docs/research/general/README-INTROSPECTION.md +0 -352
- package/plan/001_d3bb02af4886/docs/research/general/agent-introspection-patterns.md +0 -1085
- package/plan/001_d3bb02af4886/docs/research/general/introspection-security-guide.md +0 -984
- package/plan/001_d3bb02af4886/docs/research/general/introspection-tool-examples.md +0 -875
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +0 -460
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +0 -324
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +0 -175
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +0 -499
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +0 -163
- package/plan/001_d3bb02af4886/prd_snapshot.md +0 -543
- package/plan/bugfix/BUG_FIX_SUMMARY.md +0 -961
- package/scripts/generate-llms-full.ts +0 -206
- package/src/__tests__/adversarial/attachChild-performance.test.ts +0 -216
- package/src/__tests__/adversarial/circular-reference.test.ts +0 -101
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +0 -139
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +0 -571
- package/src/__tests__/adversarial/deep-analysis.test.ts +0 -729
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +0 -213
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +0 -448
- package/src/__tests__/adversarial/edge-case.test.ts +0 -703
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +0 -760
- package/src/__tests__/adversarial/incremental-performance.test.ts +0 -140
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +0 -457
- package/src/__tests__/adversarial/observer-propagation.test.ts +0 -487
- package/src/__tests__/adversarial/parent-validation.test.ts +0 -143
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +0 -611
- package/src/__tests__/adversarial/prd-compliance.test.ts +0 -731
- package/src/__tests__/compatibility/backward-compatibility.test.ts +0 -1572
- package/src/__tests__/helpers/tree-verification.ts +0 -257
- package/src/__tests__/integration/agent-workflow.test.ts +0 -256
- package/src/__tests__/integration/bidirectional-consistency.test.ts +0 -847
- package/src/__tests__/integration/observer-logging.test.ts +0 -643
- package/src/__tests__/integration/tree-mirroring.test.ts +0 -151
- package/src/__tests__/integration/workflow-reparenting.test.ts +0 -303
- package/src/__tests__/unit/agent.test.ts +0 -169
- package/src/__tests__/unit/cache-key.test.ts +0 -182
- package/src/__tests__/unit/cache.test.ts +0 -172
- package/src/__tests__/unit/context.test.ts +0 -217
- package/src/__tests__/unit/decorators.test.ts +0 -100
- package/src/__tests__/unit/introspection-tools.test.ts +0 -277
- package/src/__tests__/unit/logger.test.ts +0 -293
- package/src/__tests__/unit/observable.test.ts +0 -321
- package/src/__tests__/unit/prompt.test.ts +0 -135
- package/src/__tests__/unit/reflection.test.ts +0 -210
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +0 -170
- package/src/__tests__/unit/tree-debugger.test.ts +0 -85
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +0 -209
- package/src/__tests__/unit/workflow-detachChild.test.ts +0 -100
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +0 -153
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +0 -180
- package/src/__tests__/unit/workflow.test.ts +0 -357
- package/src/cache/cache-key.ts +0 -244
- package/src/cache/cache.ts +0 -236
- package/src/core/agent.ts +0 -593
- package/src/core/event-tree.ts +0 -260
- package/src/core/logger.ts +0 -112
- package/src/core/mcp-handler.ts +0 -184
- package/src/core/prompt.ts +0 -150
- package/src/core/workflow-context.ts +0 -351
- package/src/core/workflow.ts +0 -540
- package/src/debugger/tree-debugger.ts +0 -255
- package/src/decorators/observed-state.ts +0 -95
- package/src/decorators/step.ts +0 -139
- package/src/decorators/task.ts +0 -159
- package/src/examples/tdd-orchestrator.ts +0 -65
- package/src/examples/test-cycle-workflow.ts +0 -64
- package/src/index.ts +0 -142
- package/src/reflection/reflection.ts +0 -407
- package/src/tools/index.ts +0 -36
- package/src/tools/introspection.ts +0 -464
- package/src/types/agent.ts +0 -90
- package/src/types/decorators.ts +0 -32
- package/src/types/error-strategy.ts +0 -13
- package/src/types/error.ts +0 -20
- package/src/types/events.ts +0 -75
- package/src/types/index.ts +0 -55
- package/src/types/logging.ts +0 -24
- package/src/types/observer.ts +0 -18
- package/src/types/prompt.ts +0 -40
- package/src/types/reflection.ts +0 -117
- package/src/types/sdk-primitives.ts +0 -128
- package/src/types/workflow-context.ts +0 -163
- package/src/types/workflow.ts +0 -37
- package/src/utils/id.ts +0 -11
- package/src/utils/observable.ts +0 -106
- package/src/utils/workflow-error-utils.ts +0 -56
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -16
|
@@ -1,1565 +0,0 @@
|
|
|
1
|
-
# Concurrent Workflow Execution and Error Handling: Best Practices Research
|
|
2
|
-
|
|
3
|
-
**Author**: Research Specialist
|
|
4
|
-
**Date**: 2026-01-12
|
|
5
|
-
**Status**: Comprehensive Research Report
|
|
6
|
-
**Target**: Groundswell Hierarchical Workflow Engine
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## Executive Summary
|
|
11
|
-
|
|
12
|
-
This document provides comprehensive research findings on concurrent workflow execution and error handling patterns, with specific recommendations for the Groundswell workflow engine. The research covers Promise.all vs Promise.allSettled trade-offs, production-grade workflow engine patterns, error aggregation strategies, and fail-fast vs complete-all strategies.
|
|
13
|
-
|
|
14
|
-
**Key Finding**: The current implementation uses `Promise.all()` in the @Task decorator with `concurrent: true`, which implements a fail-fast strategy. Based on PRD requirements and production patterns, this report recommends adding support for `Promise.allSettled()` to enable "complete all" error handling with proper error aggregation.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Table of Contents
|
|
19
|
-
|
|
20
|
-
1. [Promise.all vs Promise.allSettled: Technical Comparison](#1-promiseall-vs-promiseallsettled-technical-comparison)
|
|
21
|
-
2. [Production Workflow Engine Patterns](#2-production-workflow-engine-patterns)
|
|
22
|
-
3. [Error Aggregation Strategies](#3-error-aggregation-strategies)
|
|
23
|
-
4. [Fail-Fast vs Complete-All Decision Framework](#4-fail-fast-vs-complete-all-decision-framework)
|
|
24
|
-
5. [Groundswell-Specific Recommendations](#5-groundswell-specific-recommendations)
|
|
25
|
-
6. [Implementation Roadmap](#6-implementation-roadmap)
|
|
26
|
-
7. [Appendix: Code Examples](#7-appendix-code-examples)
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 1. Promise.all vs Promise.allSettled: Technical Comparison
|
|
31
|
-
|
|
32
|
-
### 1.1 Behavioral Differences
|
|
33
|
-
|
|
34
|
-
#### Promise.all (Fail-Fast Strategy)
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// Current Groundswell Implementation (src/decorators/task.ts:112)
|
|
38
|
-
await Promise.all(runnable.map((w) => w.run()));
|
|
39
|
-
|
|
40
|
-
// Behavior:
|
|
41
|
-
// - Rejects IMMEDIATELY when ANY promise rejects
|
|
42
|
-
// - Does NOT wait for other promises to complete
|
|
43
|
-
// - Returns array of resolved values when ALL fulfill
|
|
44
|
-
// - Use case: All operations must succeed, fast-fail desired
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Characteristics:**
|
|
48
|
-
- **Performance**: Faster failure detection (stops at first error)
|
|
49
|
-
- **Resource Usage**: Wastes resources from in-flight operations
|
|
50
|
-
- **Error Visibility**: Only shows first error (hides concurrent failures)
|
|
51
|
-
- **Debugging**: Difficult to understand full scope of failures
|
|
52
|
-
|
|
53
|
-
**Example:**
|
|
54
|
-
```typescript
|
|
55
|
-
const results = await Promise.all([
|
|
56
|
-
task1(), // completes in 100ms
|
|
57
|
-
task2(), // completes in 200ms
|
|
58
|
-
task3(), // FAILS in 50ms
|
|
59
|
-
]);
|
|
60
|
-
// Execution stops at 50ms, results from task1/task2 lost
|
|
61
|
-
// Only error from task3 is visible
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
#### Promise.allSettled (Complete-All Strategy)
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
// Recommended Enhancement for Groundswell
|
|
68
|
-
const results = await Promise.allSettled(
|
|
69
|
-
runnable.map((w) => w.run())
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Behavior:
|
|
73
|
-
// - Waits for ALL promises to settle (fulfilled OR rejected)
|
|
74
|
-
// - Returns array of status objects with reason/value
|
|
75
|
-
// - Never rejects (always fulfills)
|
|
76
|
-
// - Use case: Need all results regardless of individual failures
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Characteristics:**
|
|
80
|
-
- **Performance**: Slower failure detection (waits for all operations)
|
|
81
|
-
- **Resource Usage**: Completes all work (no waste)
|
|
82
|
-
- **Error Visibility**: Shows ALL errors (complete picture)
|
|
83
|
-
- **Debugging**: Easy to understand scope and patterns of failures
|
|
84
|
-
|
|
85
|
-
**Example:**
|
|
86
|
-
```typescript
|
|
87
|
-
const results = await Promise.allSettled([
|
|
88
|
-
task1(), // completes in 100ms, fulfills
|
|
89
|
-
task2(), // completes in 200ms, fulfills
|
|
90
|
-
task3(), // FAILS in 50ms, rejects
|
|
91
|
-
]);
|
|
92
|
-
// Execution completes at 200ms (waits for slowest)
|
|
93
|
-
// All three results available:
|
|
94
|
-
// [
|
|
95
|
-
// { status: 'fulfilled', value: 'task1-result' },
|
|
96
|
-
// { status: 'fulfilled', value: 'task2-result' },
|
|
97
|
-
// { status: 'rejected', reason: Error('task3 failed') }
|
|
98
|
-
// ]
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### 1.2 Performance Comparison
|
|
102
|
-
|
|
103
|
-
| Metric | Promise.all | Promise.allSettled |
|
|
104
|
-
|--------|-------------|-------------------|
|
|
105
|
-
| **Time to First Error** | Immediate (min time) | Waits for all (max time) |
|
|
106
|
-
| **Time to Complete All** | N/A (fails early) | Max of all operations |
|
|
107
|
-
| **Resource Efficiency** | Low (wastes in-flight work) | High (completes all work) |
|
|
108
|
-
| **Error Completeness** | First error only | All errors |
|
|
109
|
-
| **Partial Results** | Lost | Preserved |
|
|
110
|
-
| **Backpressure** | Poor (can't throttle) | Better (can process as they settle) |
|
|
111
|
-
|
|
112
|
-
### 1.3 When to Use Each Strategy
|
|
113
|
-
|
|
114
|
-
#### Use Promise.all When:
|
|
115
|
-
|
|
116
|
-
1. **All Operations Are Critical**: Partial success is meaningless
|
|
117
|
-
```typescript
|
|
118
|
-
// Financial transaction - all parts must succeed
|
|
119
|
-
await Promise.all([
|
|
120
|
-
validateAccount(),
|
|
121
|
-
holdFunds(),
|
|
122
|
-
processTransfer(),
|
|
123
|
-
confirmReceipt(),
|
|
124
|
-
]);
|
|
125
|
-
// If any fails, entire transaction should fail
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
2. **Fast Failure Saves Resources**: Stopping early prevents waste
|
|
129
|
-
```typescript
|
|
130
|
-
// Batch API calls with early validation
|
|
131
|
-
const isValid = await validateBatch(items);
|
|
132
|
-
if (!isValid) {
|
|
133
|
-
return; // Don't process if validation fails
|
|
134
|
-
}
|
|
135
|
-
await Promise.all(items.map(processItem));
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
3. **Dependent Operations**: Subsequent steps depend on all succeeding
|
|
139
|
-
```typescript
|
|
140
|
-
const [config, schema] = await Promise.all([
|
|
141
|
-
fetchConfig(),
|
|
142
|
-
fetchSchema(),
|
|
143
|
-
]);
|
|
144
|
-
// Both needed for next step
|
|
145
|
-
validateData(config, schema);
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
4. **Error Cascading Acceptable**: First error is representative
|
|
149
|
-
```typescript
|
|
150
|
-
// Homogeneous operations (same type of task)
|
|
151
|
-
await Promise.all(
|
|
152
|
-
servers.map(s => s.ping())
|
|
153
|
-
);
|
|
154
|
-
// If any server is down, treat all as down
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
#### Use Promise.allSettled When:
|
|
158
|
-
|
|
159
|
-
1. **Partial Success Has Value**: Some results better than none
|
|
160
|
-
```typescript
|
|
161
|
-
// Bulk data synchronization
|
|
162
|
-
const results = await Promise.allSettled(
|
|
163
|
-
records.map(r => syncRecord(r))
|
|
164
|
-
);
|
|
165
|
-
const succeeded = results.filter(r => r.status === 'fulfilled');
|
|
166
|
-
const failed = results.filter(r => r.status === 'rejected');
|
|
167
|
-
// Process succeeded, retry failed
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
2. **Independent Operations**: Tasks don't depend on each other
|
|
171
|
-
```typescript
|
|
172
|
-
// Parallel notifications
|
|
173
|
-
await Promise.allSettled([
|
|
174
|
-
sendEmail(user),
|
|
175
|
-
sendSlack(user),
|
|
176
|
-
sendSMS(user),
|
|
177
|
-
]);
|
|
178
|
-
// Try all channels, log failures, continue
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
3. **Error Aggregation Needed**: Need to see all failures
|
|
182
|
-
```typescript
|
|
183
|
-
// Batch validation with comprehensive error reporting
|
|
184
|
-
const validations = await Promise.allSettled(
|
|
185
|
-
fields.map(f => validateField(f))
|
|
186
|
-
);
|
|
187
|
-
const errors = validations
|
|
188
|
-
.filter(v => v.status === 'rejected')
|
|
189
|
-
.map(v => v.reason);
|
|
190
|
-
// Return all validation errors to user
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
4. **Idempotent or Retryable Operations**: Can handle/ignore failures
|
|
194
|
-
```typescript
|
|
195
|
-
// Cache warming
|
|
196
|
-
await Promise.allSettled(
|
|
197
|
-
keys.map(k => cache.set(k, fetchValue(k)))
|
|
198
|
-
);
|
|
199
|
-
// Populate as much as possible, retry misses later
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 1.4 Recommendations for Groundswell
|
|
203
|
-
|
|
204
|
-
**Current State Analysis:**
|
|
205
|
-
- File: `/home/dustin/projects/groundswell/src/decorators/task.ts`
|
|
206
|
-
- Line 112: Uses `Promise.all()` for concurrent execution
|
|
207
|
-
- Behavior: Fail-fast (stops at first child workflow error)
|
|
208
|
-
|
|
209
|
-
**Issue**: This prevents partial success scenarios and hides concurrent errors.
|
|
210
|
-
|
|
211
|
-
**Recommendation**: Support both strategies via configuration:
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
export interface TaskOptions {
|
|
215
|
-
name?: string;
|
|
216
|
-
concurrent?: boolean;
|
|
217
|
-
|
|
218
|
-
// NEW: Add error handling strategy
|
|
219
|
-
errorStrategy?: 'fail-fast' | 'complete-all';
|
|
220
|
-
|
|
221
|
-
// NEW: Enable error aggregation
|
|
222
|
-
mergeErrors?: boolean;
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## 2. Production Workflow Engine Patterns
|
|
229
|
-
|
|
230
|
-
### 2.1 Temporal.io: Child Workflow Error Handling
|
|
231
|
-
|
|
232
|
-
**Pattern**: Temporal uses a "parent close policy" to determine child behavior on parent failure.
|
|
233
|
-
|
|
234
|
-
**Key Concepts**:
|
|
235
|
-
1. **Parent Close Policy**: What happens to children when parent completes
|
|
236
|
-
2. **Cancellation Scopes**: Groups of activities that cancel together
|
|
237
|
-
3. **Activity Retry Policies**: Automatic retries with exponential backoff
|
|
238
|
-
|
|
239
|
-
**Temporal's Approach**:
|
|
240
|
-
```typescript
|
|
241
|
-
// Temporal-style child workflow execution
|
|
242
|
-
const childHandle = await workflow.executeChild(childWorkflow, {
|
|
243
|
-
taskQueue: 'my-task-queue',
|
|
244
|
-
retryPolicy: {
|
|
245
|
-
initialInterval: '1 second',
|
|
246
|
-
backoffCoefficient: 2,
|
|
247
|
-
maximumAttempts: 5,
|
|
248
|
-
nonRetryableErrorTypes: ['PermanentError'],
|
|
249
|
-
},
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// Children continue by default when parent completes
|
|
253
|
-
// Can be overridden with:
|
|
254
|
-
const options = {
|
|
255
|
-
parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON,
|
|
256
|
-
// or REQUEST_CANCEL, TERMINATE
|
|
257
|
-
};
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
**Lessons for Groundswell**:
|
|
261
|
-
1. **Configurable Child Behavior**: Allow parent to specify what happens on error
|
|
262
|
-
2. **Retry Policies**: Built-in retry with exponential backoff
|
|
263
|
-
3. **Error Categorization**: Distinguish retryable vs non-retryable errors
|
|
264
|
-
4. **Cancellation Propagation**: Controlled cascading of failures
|
|
265
|
-
|
|
266
|
-
### 2.2 AWS Step Functions: Error Handling
|
|
267
|
-
|
|
268
|
-
**Pattern**: Step Functions uses catch/cascade patterns with automatic retries.
|
|
269
|
-
|
|
270
|
-
**Key Concepts**:
|
|
271
|
-
1. **Retry Blocks**: Automatic retry with backoff
|
|
272
|
-
2. **Catch Blocks**: Error handling and fallback
|
|
273
|
-
3. **Parallel States**: Branch and merge with error aggregation
|
|
274
|
-
|
|
275
|
-
**Step Functions Approach**:
|
|
276
|
-
```json
|
|
277
|
-
{
|
|
278
|
-
"Type": "Parallel",
|
|
279
|
-
"Branches": [
|
|
280
|
-
{
|
|
281
|
-
"StartAt": "ProcessA",
|
|
282
|
-
"States": {
|
|
283
|
-
"ProcessA": {
|
|
284
|
-
"Type": "Task",
|
|
285
|
-
"Retry": [
|
|
286
|
-
{
|
|
287
|
-
"ErrorEquals": ["States.TaskFailed"],
|
|
288
|
-
"IntervalSeconds": 1,
|
|
289
|
-
"MaxAttempts": 3,
|
|
290
|
-
"BackoffRate": 2.0
|
|
291
|
-
}
|
|
292
|
-
],
|
|
293
|
-
"Catch": [
|
|
294
|
-
{
|
|
295
|
-
"ErrorEquals": ["States.ALL"],
|
|
296
|
-
"ResultPath": "$.error",
|
|
297
|
-
"Next": "FallbackA"
|
|
298
|
-
}
|
|
299
|
-
]
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
]
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
**Lessons for Groundswell**:
|
|
308
|
-
1. **Declarative Retry**: Configuration-driven retry logic
|
|
309
|
-
2. **Error Categories**: Different handling for different error types
|
|
310
|
-
3. **Fallback States**: Degraded but functional behavior on failure
|
|
311
|
-
4. **Parallel Error Aggregation**: Collect errors from all branches
|
|
312
|
-
|
|
313
|
-
### 2.3 Cadence Workflow: Error Propagation
|
|
314
|
-
|
|
315
|
-
**Pattern**: Cadence uses exception-based error propagation with child workflow isolation.
|
|
316
|
-
|
|
317
|
-
**Key Concepts**:
|
|
318
|
-
1. **Workflow Exceptions**: Errors propagate up the tree
|
|
319
|
-
2. **Child Workflow Isolation**: Child errors don't crash parent unless thrown
|
|
320
|
-
3. **Timeout Handling**: Distinction between timeout and failure
|
|
321
|
-
|
|
322
|
-
**Cadence Approach**:
|
|
323
|
-
```go
|
|
324
|
-
// Cadence-style child workflow
|
|
325
|
-
childFuture, _ := workflow.ExecuteChildWorkflow(ctx, childWorkflow, input)
|
|
326
|
-
|
|
327
|
-
var result string
|
|
328
|
-
if err := childFuture.Get(ctx, &result); err != nil {
|
|
329
|
-
// Child failed, but parent can handle
|
|
330
|
-
if activities.IsTimeoutError(err) {
|
|
331
|
-
// Handle timeout specifically
|
|
332
|
-
} else if activities.IsCanceledError(err) {
|
|
333
|
-
// Handle cancellation
|
|
334
|
-
} else {
|
|
335
|
-
// Handle other errors
|
|
336
|
-
}
|
|
337
|
-
// Parent continues
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
**Lessons for Groundswell**:
|
|
342
|
-
1. **Non-Blocking Errors**: Parent can handle child failures
|
|
343
|
-
2. **Error Type Discrimination**: Different error types handled differently
|
|
344
|
-
3. **Explicit Error Handling**: Parent must explicitly handle child errors
|
|
345
|
-
4. **Timeout as First-Class Concept**: Distinguish timeout from failure
|
|
346
|
-
|
|
347
|
-
### 2.4 Apache Airflow: Task Failure Handling
|
|
348
|
-
|
|
349
|
-
**Pattern**: Airflow uses DAG-based execution with trigger rules controlling downstream behavior.
|
|
350
|
-
|
|
351
|
-
**Key Concepts**:
|
|
352
|
-
1. **Trigger Rules**: Determine when tasks run based on upstream state
|
|
353
|
-
2. **Task Instances**: Individual executions with state tracking
|
|
354
|
-
3. **Retries**: Configurable per task
|
|
355
|
-
4. **Failure Handlers**: Callbacks on task failure
|
|
356
|
-
|
|
357
|
-
**Airflow Approach**:
|
|
358
|
-
```python
|
|
359
|
-
# Airflow-style DAG with trigger rules
|
|
360
|
-
task1 = PythonOperator(
|
|
361
|
-
task_id='task1',
|
|
362
|
-
python_callable=process_data,
|
|
363
|
-
retries=3,
|
|
364
|
-
retry_delay=timedelta(seconds=60),
|
|
365
|
-
on_failure_callback=notify_failure,
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
task2 = PythonOperator(
|
|
369
|
-
task_id='task2',
|
|
370
|
-
python_callable=process_more_data,
|
|
371
|
-
trigger_rule=TriggerRule.ONE_SUCCESS, # Run if ANY upstream succeeded
|
|
372
|
-
depends_on_past=False,
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
task3 = PythonOperator(
|
|
376
|
-
task_id='task3',
|
|
377
|
-
python_callable=finalize,
|
|
378
|
-
trigger_rule=TriggerRule.ALL_DONE, # Run after ALL upstream complete (success or fail)
|
|
379
|
-
)
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
**Lessons for Groundswell**:
|
|
383
|
-
1. **Flexible Trigger Rules**: Control downstream execution based on upstream state
|
|
384
|
-
2. **Per-Task Retry Configuration**: Fine-grained retry control
|
|
385
|
-
3. **Failure Callbacks**: Reactive error handling
|
|
386
|
-
4. **Completion-Based Triggers**: "ALL_DONE" = Promise.allSettled pattern
|
|
387
|
-
|
|
388
|
-
### 2.5 Cross-Cutting Patterns Summary
|
|
389
|
-
|
|
390
|
-
| Pattern | Temporal | Step Functions | Cadence | Airflow | Groundswell |
|
|
391
|
-
|---------|----------|----------------|---------|---------|-------------|
|
|
392
|
-
| **Child Isolation** | ✓ | ✓ | ✓ | ✓ | ⚠️ Partial |
|
|
393
|
-
| **Error Aggregation** | ✗ | ✓ (Parallel) | ⚠️ Manual | ✓ (Trigger rules) | ✗ |
|
|
394
|
-
| **Automatic Retry** | ✓ | ✓ | ✓ | ✓ | ✗ |
|
|
395
|
-
| **Fail-Fast** | Default | Configurable | Default | Configurable | Default |
|
|
396
|
-
| **Complete-All** | ✗ | ✓ (Parallel) | ⚠️ Manual | ✓ (ALL_DONE) | ✗ |
|
|
397
|
-
| **Fallback States** | ✗ | ✓ (Catch) | ⚠️ Manual | ✓ (On failure) | ✗ |
|
|
398
|
-
| **Cancellation Propagation** | ✓ | ✓ | ✓ | ✓ | ⚠️ Unimplemented |
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
## 3. Error Aggregation Strategies
|
|
403
|
-
|
|
404
|
-
### 3.1 Strategy 1: First Error Wins (Current Groundswell)
|
|
405
|
-
|
|
406
|
-
**Description**: Return immediately on first error, hiding concurrent failures.
|
|
407
|
-
|
|
408
|
-
**Implementation**:
|
|
409
|
-
```typescript
|
|
410
|
-
// Current implementation (src/decorators/task.ts:112)
|
|
411
|
-
await Promise.all(runnable.map((w) => w.run()));
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
**Pros**:
|
|
415
|
-
- Simple implementation
|
|
416
|
-
- Fast failure detection
|
|
417
|
-
- Low memory overhead
|
|
418
|
-
- Familiar pattern (Promise.all)
|
|
419
|
-
|
|
420
|
-
**Cons**:
|
|
421
|
-
- Hides concurrent errors
|
|
422
|
-
- Loses partial results
|
|
423
|
-
- Difficult debugging
|
|
424
|
-
- No error context
|
|
425
|
-
|
|
426
|
-
**Use Case**: Critical workflows where partial success is meaningless.
|
|
427
|
-
|
|
428
|
-
### 3.2 Strategy 2: Collect All Errors (Recommended)
|
|
429
|
-
|
|
430
|
-
**Description**: Wait for all operations to complete, collect all errors.
|
|
431
|
-
|
|
432
|
-
**Implementation**:
|
|
433
|
-
```typescript
|
|
434
|
-
// Promise.allSettled with error aggregation
|
|
435
|
-
const results = await Promise.allSettled(
|
|
436
|
-
runnable.map((w) => w.run())
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
const errors = results
|
|
440
|
-
.filter((r) => r.status === 'rejected')
|
|
441
|
-
.map((r, idx) => ({
|
|
442
|
-
workflow: runnable[idx],
|
|
443
|
-
error: r.status === 'rejected' ? r.reason : undefined,
|
|
444
|
-
}));
|
|
445
|
-
|
|
446
|
-
if (errors.length > 0) {
|
|
447
|
-
throw new AggregateError(
|
|
448
|
-
errors.map(e => e.error),
|
|
449
|
-
`${errors.length} child workflows failed`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
**Pros**:
|
|
455
|
-
- Complete error visibility
|
|
456
|
-
- Preserves partial results
|
|
457
|
-
- Better debugging
|
|
458
|
-
- Error pattern analysis
|
|
459
|
-
|
|
460
|
-
**Cons**:
|
|
461
|
-
- Higher memory usage
|
|
462
|
-
- Slower failure detection
|
|
463
|
-
- More complex error handling
|
|
464
|
-
- Need AggregateError polyfill for older Node versions
|
|
465
|
-
|
|
466
|
-
**Use Case**: Independent operations where understanding all failures is important.
|
|
467
|
-
|
|
468
|
-
### 3.3 Strategy 3: Hierarchical Error Merging
|
|
469
|
-
|
|
470
|
-
**Description**: Merge errors with workflow hierarchy context.
|
|
471
|
-
|
|
472
|
-
**Implementation**:
|
|
473
|
-
```typescript
|
|
474
|
-
class WorkflowAggregateError extends Error {
|
|
475
|
-
constructor(
|
|
476
|
-
message: string,
|
|
477
|
-
public errors: WorkflowError[],
|
|
478
|
-
public workflowId: string,
|
|
479
|
-
public workflowName: string,
|
|
480
|
-
public parent?: WorkflowAggregateError
|
|
481
|
-
) {
|
|
482
|
-
super(message);
|
|
483
|
-
this.name = 'WorkflowAggregateError';
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Pretty-print error hierarchy
|
|
487
|
-
toString(): string {
|
|
488
|
-
let output = `${this.workflowName}: ${this.message}\n`;
|
|
489
|
-
for (const error of this.errors) {
|
|
490
|
-
output += ` - ${error.workflowId}: ${error.message}\n`;
|
|
491
|
-
}
|
|
492
|
-
if (this.parent) {
|
|
493
|
-
output += `\nCaused by:\n${this.parent.toString()}`;
|
|
494
|
-
}
|
|
495
|
-
return output;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Get error statistics
|
|
499
|
-
getStats() {
|
|
500
|
-
return {
|
|
501
|
-
totalErrors: this.errors.length,
|
|
502
|
-
byWorkflow: groupErrorsByWorkflow(this.errors),
|
|
503
|
-
byErrorType: groupErrorsByType(this.errors),
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
**Pros**:
|
|
510
|
-
- Maintains workflow context
|
|
511
|
-
- Hierarchical error visualization
|
|
512
|
-
- Rich error metadata
|
|
513
|
-
- Statistical analysis
|
|
514
|
-
|
|
515
|
-
**Cons**:
|
|
516
|
-
- Complex implementation
|
|
517
|
-
- Higher memory overhead
|
|
518
|
-
- Steeper learning curve
|
|
519
|
-
|
|
520
|
-
**Use Case**: Complex workflow trees needing comprehensive error reporting.
|
|
521
|
-
|
|
522
|
-
### 3.4 Strategy 4: Error Rate Thresholding
|
|
523
|
-
|
|
524
|
-
**Description**: Allow some failures, only throw if error rate exceeds threshold.
|
|
525
|
-
|
|
526
|
-
**Implementation**:
|
|
527
|
-
```typescript
|
|
528
|
-
interface ErrorThresholdStrategy {
|
|
529
|
-
enabled: boolean;
|
|
530
|
-
maxErrorRate: number; // 0.0 to 1.0
|
|
531
|
-
minAbsoluteErrors: number; // Throw if >= this many errors
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
async function executeWithThreshold(
|
|
535
|
-
workflows: Workflow[],
|
|
536
|
-
strategy: ErrorThresholdStrategy
|
|
537
|
-
) {
|
|
538
|
-
const results = await Promise.allSettled(
|
|
539
|
-
workflows.map((w) => w.run())
|
|
540
|
-
);
|
|
541
|
-
|
|
542
|
-
const failures = results.filter((r) => r.status === 'rejected');
|
|
543
|
-
const errorRate = failures.length / results.length;
|
|
544
|
-
|
|
545
|
-
const shouldThrow =
|
|
546
|
-
failures.length >= strategy.minAbsoluteErrors ||
|
|
547
|
-
errorRate > strategy.maxErrorRate;
|
|
548
|
-
|
|
549
|
-
if (shouldThrow) {
|
|
550
|
-
throw new WorkflowAggregateError(
|
|
551
|
-
`${failures.length}/${results.length} workflows failed (${(errorRate * 100).toFixed(1)}%)`,
|
|
552
|
-
failures.map(f => f.reason),
|
|
553
|
-
// ... context
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
return results;
|
|
558
|
-
}
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
**Pros**:
|
|
562
|
-
- Graceful degradation
|
|
563
|
-
- Configurable tolerance
|
|
564
|
-
- Good for bulk operations
|
|
565
|
-
- Prevents single failures from stopping everything
|
|
566
|
-
|
|
567
|
-
**Cons**:
|
|
568
|
-
- May hide systematic issues
|
|
569
|
-
- Requires threshold tuning
|
|
570
|
-
- Ambiguous success criteria
|
|
571
|
-
|
|
572
|
-
**Use Case**: High-volume operations where some failures are acceptable (e.g., bulk notifications, cache warming).
|
|
573
|
-
|
|
574
|
-
### 3.5 Error Aggregation Best Practices
|
|
575
|
-
|
|
576
|
-
#### DO:
|
|
577
|
-
1. **Preserve Workflow Context**: Include workflow ID, name, and path in errors
|
|
578
|
-
2. **Maintain Error Order**: Keep errors in execution order for debugging
|
|
579
|
-
3. **Include Error Metadata**: Timestamps, retry counts, state snapshots
|
|
580
|
-
4. **Provide Statistics**: Error counts, rates, patterns
|
|
581
|
-
5. **Support Error Inspection**: Allow programmatic error analysis
|
|
582
|
-
|
|
583
|
-
#### DON'T:
|
|
584
|
-
1. **Lose Stack Traces**: Always preserve original stack traces
|
|
585
|
-
2. **Swallow Errors**: Always report or log errors
|
|
586
|
-
3. **Create Excessive Depth**: Limit error nesting to prevent stack overflow
|
|
587
|
-
4. **Duplicate Errors**: Deduplicate identical errors
|
|
588
|
-
5. **Include Sensitive Data**: Redact passwords, tokens, PII from errors
|
|
589
|
-
|
|
590
|
-
### 3.6 Groundswell Implementation Recommendation
|
|
591
|
-
|
|
592
|
-
**Current State**:
|
|
593
|
-
- Type: `/home/dustin/projects/groundswell/src/types/error-strategy.ts`
|
|
594
|
-
- Status: Defined but not implemented
|
|
595
|
-
- Interface: `ErrorMergeStrategy` exists but unused
|
|
596
|
-
|
|
597
|
-
**Recommendation**:
|
|
598
|
-
|
|
599
|
-
```typescript
|
|
600
|
-
// Enhanced error strategy interface
|
|
601
|
-
export interface ErrorStrategy {
|
|
602
|
-
type: 'fail-fast' | 'complete-all' | 'threshold';
|
|
603
|
-
threshold?: number; // For 'threshold' type
|
|
604
|
-
mergeErrors?: boolean; // Always merge errors in 'complete-all' mode
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Implementation in @Task decorator
|
|
608
|
-
if (opts.concurrent && Array.isArray(result)) {
|
|
609
|
-
const runnable = workflows.filter(/* ... */);
|
|
610
|
-
|
|
611
|
-
if (runnable.length > 0) {
|
|
612
|
-
const errorStrategy = opts.errorStrategy || 'fail-fast';
|
|
613
|
-
|
|
614
|
-
if (errorStrategy === 'fail-fast') {
|
|
615
|
-
// Current behavior: Promise.all
|
|
616
|
-
await Promise.all(runnable.map((w) => w.run()));
|
|
617
|
-
} else {
|
|
618
|
-
// New behavior: Promise.allSettled with aggregation
|
|
619
|
-
const results = await Promise.allSettled(
|
|
620
|
-
runnable.map((w) => w.run())
|
|
621
|
-
);
|
|
622
|
-
|
|
623
|
-
const errors = results
|
|
624
|
-
.filter((r) => r.status === 'rejected')
|
|
625
|
-
.map((r) => r.reason);
|
|
626
|
-
|
|
627
|
-
if (errors.length > 0) {
|
|
628
|
-
throw new WorkflowAggregateError(
|
|
629
|
-
`${errors.length} child workflows failed`,
|
|
630
|
-
errors,
|
|
631
|
-
wf.id,
|
|
632
|
-
taskName
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
---
|
|
641
|
-
|
|
642
|
-
## 4. Fail-Fast vs Complete-All Decision Framework
|
|
643
|
-
|
|
644
|
-
### 4.1 Decision Tree
|
|
645
|
-
|
|
646
|
-
```
|
|
647
|
-
Start Concurrent Tasks
|
|
648
|
-
|
|
|
649
|
-
Are tasks independent?
|
|
650
|
-
/ \
|
|
651
|
-
No Yes
|
|
652
|
-
| |
|
|
653
|
-
Use Promise.all Is partial success
|
|
654
|
-
(Fail-Fast) valuable?
|
|
655
|
-
/ \
|
|
656
|
-
No Yes
|
|
657
|
-
| |
|
|
658
|
-
Use Promise.all Need complete
|
|
659
|
-
(Fail-Fast) error picture?
|
|
660
|
-
/ \
|
|
661
|
-
No Yes
|
|
662
|
-
| |
|
|
663
|
-
Use Promise Use Promise
|
|
664
|
-
.allSettled .allSettled
|
|
665
|
-
(simple) (aggregate)
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
### 4.2 Heuristics for Choosing Strategy
|
|
669
|
-
|
|
670
|
-
#### Use Fail-Fast (Promise.all) When:
|
|
671
|
-
|
|
672
|
-
1. **Strong Coupling**: Tasks are logically dependent
|
|
673
|
-
- Example: Multi-step transaction where all steps must succeed
|
|
674
|
-
- Pattern: `validate → hold → transfer → confirm`
|
|
675
|
-
|
|
676
|
-
2. **Fast Failure Saves Money**: Early termination prevents waste
|
|
677
|
-
- Example: Paid API calls with rate limits
|
|
678
|
-
- Pattern: Stop if quota exceeded or auth fails
|
|
679
|
-
|
|
680
|
-
3. **First Error Is Representative**: Errors are correlated
|
|
681
|
-
- Example: All tasks use same resource (database, API)
|
|
682
|
-
- Pattern: If one fails, all would fail
|
|
683
|
-
|
|
684
|
-
4. **User Experience Requires Speed**: Fast feedback is critical
|
|
685
|
-
- Example: Form validation before submission
|
|
686
|
-
- Pattern: Show first error immediately
|
|
687
|
-
|
|
688
|
-
#### Use Complete-All (Promise.allSettled) When:
|
|
689
|
-
|
|
690
|
-
1. **Independent Tasks**: No dependencies between operations
|
|
691
|
-
- Example: Sending notifications via multiple channels
|
|
692
|
-
- Pattern: Email, SMS, Slack - try all, report failures
|
|
693
|
-
|
|
694
|
-
2. **Partial Success Has Value**: Some results better than none
|
|
695
|
-
- Example: Bulk data synchronization
|
|
696
|
-
- Pattern: Sync what succeeds, retry failures
|
|
697
|
-
|
|
698
|
-
3. **Error Analysis Required**: Need to see all failures
|
|
699
|
-
- Example: Batch processing with quality control
|
|
700
|
-
- Pattern: Analyze error patterns, adjust strategy
|
|
701
|
-
|
|
702
|
-
4. **Idempotent Operations**: Can safely retry failures
|
|
703
|
-
- Example: Cache warming, data replication
|
|
704
|
-
- Pattern: Populate cache, retry misses later
|
|
705
|
-
|
|
706
|
-
### 4.3 Strategy Selection Matrix
|
|
707
|
-
|
|
708
|
-
| Scenario | Fail-Fast | Complete-All | Rationale |
|
|
709
|
-
|----------|-----------|--------------|-----------|
|
|
710
|
-
| **Financial Transaction** | ✓ | ✗ | All-or-nothing, can't have partial money movement |
|
|
711
|
-
| **Bulk Notifications** | ✗ | ✓ | Try all channels, log failures |
|
|
712
|
-
| **Data Validation** | ✓ | ✗ | Fast feedback, first error representative |
|
|
713
|
-
| **Batch Processing** | ✗ | ✓ | Process what you can, retry failures |
|
|
714
|
-
| **Cache Warming** | ✗ | ✓ | Populate as much as possible |
|
|
715
|
-
| **API Rate Limiting** | ✓ | ✗ | Stop immediately to avoid waste |
|
|
716
|
-
| **Multi-Region Deployment** | ✗ | ✓ | Deploy to available regions, log failures |
|
|
717
|
-
| **Form Submission** | ✓ | ✗ | User needs immediate feedback |
|
|
718
|
-
| **Data Replication** | ✗ | ✓ | Replicate to available nodes |
|
|
719
|
-
| **Paid API Calls** | ✓ | ✗ | Save money on failed requests |
|
|
720
|
-
|
|
721
|
-
### 4.4 Hybrid Strategy: Adaptive Error Handling
|
|
722
|
-
|
|
723
|
-
**Concept**: Start with complete-all, switch to fail-fast if error rate exceeds threshold.
|
|
724
|
-
|
|
725
|
-
**Implementation**:
|
|
726
|
-
```typescript
|
|
727
|
-
async function adaptiveExecute(
|
|
728
|
-
workflows: Workflow[],
|
|
729
|
-
options: {
|
|
730
|
-
initialStrategy: 'fail-fast' | 'complete-all';
|
|
731
|
-
errorRateThreshold: number; // Switch to fail-fast if exceeded
|
|
732
|
-
minSampleSize: number; // Don't switch until this many tasks
|
|
733
|
-
}
|
|
734
|
-
) {
|
|
735
|
-
let strategy = options.initialStrategy;
|
|
736
|
-
let completed = 0;
|
|
737
|
-
let errors = 0;
|
|
738
|
-
|
|
739
|
-
const results = await Promise.allSettled(
|
|
740
|
-
workflows.map(async (workflow) => {
|
|
741
|
-
try {
|
|
742
|
-
const result = await workflow.run();
|
|
743
|
-
completed++;
|
|
744
|
-
errors++;
|
|
745
|
-
|
|
746
|
-
// Check if we should switch to fail-fast
|
|
747
|
-
if (
|
|
748
|
-
strategy === 'complete-all' &&
|
|
749
|
-
completed >= options.minSampleSize
|
|
750
|
-
) {
|
|
751
|
-
const errorRate = errors / completed;
|
|
752
|
-
if (errorRate > options.errorRateThreshold) {
|
|
753
|
-
// Switch to fail-fast
|
|
754
|
-
throw new Error(`Error rate ${(errorRate * 100).toFixed(1)}% exceeds threshold, switching to fail-fast`);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
return { status: 'fulfilled', value: result };
|
|
759
|
-
} catch (error) {
|
|
760
|
-
return { status: 'rejected', reason: error };
|
|
761
|
-
}
|
|
762
|
-
})
|
|
763
|
-
);
|
|
764
|
-
|
|
765
|
-
return results;
|
|
766
|
-
}
|
|
767
|
-
```
|
|
768
|
-
|
|
769
|
-
**Use Case**: High-volume operations where most tasks succeed, but rapid failure indicates systemic issues.
|
|
770
|
-
|
|
771
|
-
### 4.5 Groundswell Decision Framework
|
|
772
|
-
|
|
773
|
-
**Question**: Should Groundswell use fail-fast or complete-all by default?
|
|
774
|
-
|
|
775
|
-
**Analysis**:
|
|
776
|
-
|
|
777
|
-
**Arguments for Fail-Fast (Current)**:
|
|
778
|
-
- Workflow engines traditionally emphasize correctness over partial success
|
|
779
|
-
- Parent workflows often need to know immediately if children fail
|
|
780
|
-
- Simpler mental model for developers
|
|
781
|
-
- Lower memory overhead
|
|
782
|
-
- Already implemented
|
|
783
|
-
|
|
784
|
-
**Arguments for Complete-All (Recommended)**:
|
|
785
|
-
- Observability is a core feature (PRD emphasis on logging and debugging)
|
|
786
|
-
- Child workflows are often independent (parallel processing pattern)
|
|
787
|
-
- "Complete all" provides better error visibility
|
|
788
|
-
- Aligns with production patterns (Airflow, Step Functions)
|
|
789
|
-
- Enables graceful degradation patterns
|
|
790
|
-
- More flexible (can opt-in to fail-fast)
|
|
791
|
-
|
|
792
|
-
**Recommendation**: **Default to complete-all with opt-in fail-fast**
|
|
793
|
-
|
|
794
|
-
**Rationale**:
|
|
795
|
-
1. Observability First: Groundswell's value prop is debugging and observability
|
|
796
|
-
2. Independent by Default: @Task creates independent child workflows
|
|
797
|
-
3. Error Visibility: Complete-all provides comprehensive error information
|
|
798
|
-
4. Production Patterns: Aligns with Airflow and Step Functions
|
|
799
|
-
5. Flexibility: Developers can opt-in to fail-fast when needed
|
|
800
|
-
|
|
801
|
-
---
|
|
802
|
-
|
|
803
|
-
## 5. Groundswell-Specific Recommendations
|
|
804
|
-
|
|
805
|
-
### 5.1 Current Implementation Analysis
|
|
806
|
-
|
|
807
|
-
**File**: `/home/dustin/projects/groundswell/src/decorators/task.ts`
|
|
808
|
-
|
|
809
|
-
**Current Behavior (Line 104-114)**:
|
|
810
|
-
```typescript
|
|
811
|
-
// If concurrent option is set and we have multiple workflows, run them in parallel
|
|
812
|
-
if (opts.concurrent && Array.isArray(result)) {
|
|
813
|
-
const runnable = workflows.filter(
|
|
814
|
-
(w): w is WorkflowClass =>
|
|
815
|
-
w && typeof w === 'object' && 'run' in w && typeof w.run === 'function'
|
|
816
|
-
);
|
|
817
|
-
|
|
818
|
-
if (runnable.length > 0) {
|
|
819
|
-
await Promise.all(runnable.map((w) => w.run()));
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
**Characteristics**:
|
|
825
|
-
- Strategy: Fail-fast (Promise.all)
|
|
826
|
-
- Error Handling: First error wins
|
|
827
|
-
- Partial Results: Lost
|
|
828
|
-
- Error Aggregation: None
|
|
829
|
-
- Configuration: None (hardcoded)
|
|
830
|
-
|
|
831
|
-
**Issues**:
|
|
832
|
-
1. No error aggregation: Only first error is visible
|
|
833
|
-
2. No flexibility: Can't choose strategy
|
|
834
|
-
3. No observability: Loses error information
|
|
835
|
-
4. No graceful degradation: All-or-nothing approach
|
|
836
|
-
|
|
837
|
-
### 5.2 Recommended Enhancement
|
|
838
|
-
|
|
839
|
-
#### Phase 1: Add Error Strategy Option
|
|
840
|
-
|
|
841
|
-
**Changes to `/home/dustin/projects/groundswell/src/types/decorators.ts`**:
|
|
842
|
-
|
|
843
|
-
```typescript
|
|
844
|
-
export interface TaskOptions {
|
|
845
|
-
name?: string;
|
|
846
|
-
concurrent?: boolean;
|
|
847
|
-
|
|
848
|
-
// NEW: Error handling strategy
|
|
849
|
-
errorStrategy?: 'fail-fast' | 'complete-all';
|
|
850
|
-
|
|
851
|
-
// NEW: Error aggregation (only for 'complete-all')
|
|
852
|
-
mergeErrors?: boolean;
|
|
853
|
-
}
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
**Changes to `/home/dustin/projects/groundswell/src/decorators/task.ts`**:
|
|
857
|
-
|
|
858
|
-
```typescript
|
|
859
|
-
// If concurrent option is set and we have multiple workflows, run them in parallel
|
|
860
|
-
if (opts.concurrent && Array.isArray(result)) {
|
|
861
|
-
const runnable = workflows.filter(
|
|
862
|
-
(w): w is WorkflowClass =>
|
|
863
|
-
w && typeof w === 'object' && 'run' in w && typeof w.run === 'function'
|
|
864
|
-
);
|
|
865
|
-
|
|
866
|
-
if (runnable.length > 0) {
|
|
867
|
-
const strategy = opts.errorStrategy || 'fail-fast';
|
|
868
|
-
|
|
869
|
-
if (strategy === 'fail-fast') {
|
|
870
|
-
// Current behavior: Promise.all (fail-fast)
|
|
871
|
-
await Promise.all(runnable.map((w) => w.run()));
|
|
872
|
-
} else {
|
|
873
|
-
// New behavior: Promise.allSettled (complete-all)
|
|
874
|
-
const settledResults = await Promise.allSettled(
|
|
875
|
-
runnable.map((w) => w.run())
|
|
876
|
-
);
|
|
877
|
-
|
|
878
|
-
// Collect errors
|
|
879
|
-
const errors = settledResults
|
|
880
|
-
.map((result, idx) => ({ result, workflow: runnable[idx] }))
|
|
881
|
-
.filter(({ result }) => result.status === 'rejected')
|
|
882
|
-
.map(({ result, workflow }) => ({
|
|
883
|
-
workflowId: workflow.id,
|
|
884
|
-
workflowName: workflow.constructor.name,
|
|
885
|
-
error: result.status === 'rejected' ? result.reason : undefined,
|
|
886
|
-
}));
|
|
887
|
-
|
|
888
|
-
// Throw if there were errors
|
|
889
|
-
if (errors.length > 0) {
|
|
890
|
-
// Create aggregate error with all failures
|
|
891
|
-
const aggregateError = new Error(
|
|
892
|
-
`${errors.length} child workflow(s) failed in task '${taskName}'`
|
|
893
|
-
) as any;
|
|
894
|
-
|
|
895
|
-
aggregateError.name = 'WorkflowAggregateError';
|
|
896
|
-
aggregateError.errors = errors;
|
|
897
|
-
aggregateError.taskName = taskName;
|
|
898
|
-
aggregateError.workflowId = wf.id;
|
|
899
|
-
aggregateError.totalChildren = runnable.length;
|
|
900
|
-
aggregateError.failedChildren = errors.length;
|
|
901
|
-
|
|
902
|
-
throw aggregateError;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
```
|
|
908
|
-
|
|
909
|
-
#### Phase 2: Implement AggregateError Type
|
|
910
|
-
|
|
911
|
-
**New file**: `/home/dustin/projects/groundswell/src/types/aggregate-error.ts`
|
|
912
|
-
|
|
913
|
-
```typescript
|
|
914
|
-
import type { WorkflowError } from './error.js';
|
|
915
|
-
|
|
916
|
-
/**
|
|
917
|
-
* Aggregate error containing multiple child workflow errors
|
|
918
|
-
*/
|
|
919
|
-
export interface WorkflowAggregateError extends Error {
|
|
920
|
-
name: 'WorkflowAggregateError';
|
|
921
|
-
message: string;
|
|
922
|
-
errors: Array<{
|
|
923
|
-
workflowId: string;
|
|
924
|
-
workflowName: string;
|
|
925
|
-
error: unknown;
|
|
926
|
-
}>;
|
|
927
|
-
taskName: string;
|
|
928
|
-
workflowId: string;
|
|
929
|
-
totalChildren: number;
|
|
930
|
-
failedChildren: number;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
/**
|
|
934
|
-
* Type guard for WorkflowAggregateError
|
|
935
|
-
*/
|
|
936
|
-
export function isWorkflowAggregateError(
|
|
937
|
-
error: unknown
|
|
938
|
-
): error is WorkflowAggregateError {
|
|
939
|
-
return (
|
|
940
|
-
error instanceof Error &&
|
|
941
|
-
(error as any).name === 'WorkflowAggregateError' &&
|
|
942
|
-
'errors' in error &&
|
|
943
|
-
'totalChildren' in error &&
|
|
944
|
-
'failedChildren' in error
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
/**
|
|
949
|
-
* Create a workflow aggregate error
|
|
950
|
-
*/
|
|
951
|
-
export function createAggregateError(
|
|
952
|
-
errors: Array<{ workflowId: string; workflowName: string; error: unknown }>,
|
|
953
|
-
taskName: string,
|
|
954
|
-
parentWorkflowId: string
|
|
955
|
-
): WorkflowAggregateError {
|
|
956
|
-
const error = new Error(
|
|
957
|
-
`${errors.length} child workflow(s) failed in task '${taskName}'`
|
|
958
|
-
) as WorkflowAggregateError;
|
|
959
|
-
|
|
960
|
-
error.name = 'WorkflowAggregateError';
|
|
961
|
-
error.errors = errors;
|
|
962
|
-
error.taskName = taskName;
|
|
963
|
-
error.workflowId = parentWorkflowId;
|
|
964
|
-
error.totalChildren = errors.length; // Will be updated by caller
|
|
965
|
-
error.failedChildren = errors.length;
|
|
966
|
-
|
|
967
|
-
return error;
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
#### Phase 3: Update Error Handling Documentation
|
|
972
|
-
|
|
973
|
-
**File**: `/home/dustin/projects/groundswell/docs/workflow.md`
|
|
974
|
-
|
|
975
|
-
**Add section**:
|
|
976
|
-
|
|
977
|
-
```markdown
|
|
978
|
-
## Error Handling in Concurrent Tasks
|
|
979
|
-
|
|
980
|
-
### Fail-Fast Strategy (Default)
|
|
981
|
-
|
|
982
|
-
When `concurrent: true` is used without specifying `errorStrategy`, the workflow uses a fail-fast approach:
|
|
983
|
-
|
|
984
|
-
```typescript
|
|
985
|
-
class ParentWorkflow extends Workflow {
|
|
986
|
-
@Task({ concurrent: true })
|
|
987
|
-
async createChildren(): Promise<ChildWorkflow[]> {
|
|
988
|
-
return [
|
|
989
|
-
new ChildWorkflow('child1', this),
|
|
990
|
-
new ChildWorkflow('child2', this),
|
|
991
|
-
new ChildWorkflow('child3', this),
|
|
992
|
-
];
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
async run() {
|
|
996
|
-
try {
|
|
997
|
-
await this.createChildren();
|
|
998
|
-
} catch (error) {
|
|
999
|
-
// Throws immediately when first child fails
|
|
1000
|
-
console.error('First child failed:', error.message);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
### Complete-All Strategy
|
|
1007
|
-
|
|
1008
|
-
To wait for all children to complete and collect all errors:
|
|
1009
|
-
|
|
1010
|
-
```typescript
|
|
1011
|
-
class ParentWorkflow extends Workflow {
|
|
1012
|
-
@Task({
|
|
1013
|
-
concurrent: true,
|
|
1014
|
-
errorStrategy: 'complete-all', // Wait for all children
|
|
1015
|
-
})
|
|
1016
|
-
async createChildren(): Promise<ChildWorkflow[]> {
|
|
1017
|
-
return [
|
|
1018
|
-
new ChildWorkflow('child1', this),
|
|
1019
|
-
new ChildWorkflow('child2', this),
|
|
1020
|
-
new ChildWorkflow('child3', this),
|
|
1021
|
-
];
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
async run() {
|
|
1025
|
-
try {
|
|
1026
|
-
await this.createChildren();
|
|
1027
|
-
} catch (error) {
|
|
1028
|
-
if (isWorkflowAggregateError(error)) {
|
|
1029
|
-
console.error(`${error.failedChildren}/${error.totalChildren} children failed:`);
|
|
1030
|
-
|
|
1031
|
-
for (const failure of error.errors) {
|
|
1032
|
-
console.error(` - ${failure.workflowName}: ${failure.error?.message}`);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
```
|
|
1039
|
-
|
|
1040
|
-
### Choosing the Right Strategy
|
|
1041
|
-
|
|
1042
|
-
**Use Fail-Fast When:**
|
|
1043
|
-
- Child workflows are dependent on each other
|
|
1044
|
-
- Partial success is not meaningful
|
|
1045
|
-
- You need immediate feedback on errors
|
|
1046
|
-
- Resource conservation is important
|
|
1047
|
-
|
|
1048
|
-
**Use Complete-All When:**
|
|
1049
|
-
- Child workflows are independent
|
|
1050
|
-
- Partial results have value
|
|
1051
|
-
- You need comprehensive error reporting
|
|
1052
|
-
- Observability is a priority
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
### 5.3 Backward Compatibility
|
|
1056
|
-
|
|
1057
|
-
**Recommendation**: Keep current behavior (fail-fast) as default to maintain backward compatibility.
|
|
1058
|
-
|
|
1059
|
-
**Rationale**:
|
|
1060
|
-
1. Existing code won't break
|
|
1061
|
-
2. Opt-in to new behavior via `errorStrategy: 'complete-all'`
|
|
1062
|
-
3. Gradual migration path
|
|
1063
|
-
4. Safe default (fail-fast is more conservative)
|
|
1064
|
-
|
|
1065
|
-
**Future Enhancement**: Consider changing default to `complete-all` in a major version (2.0).
|
|
1066
|
-
|
|
1067
|
-
### 5.4 Testing Recommendations
|
|
1068
|
-
|
|
1069
|
-
**Add tests to** `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts`:
|
|
1070
|
-
|
|
1071
|
-
```typescript
|
|
1072
|
-
describe('Concurrent Error Handling', () => {
|
|
1073
|
-
it('should use fail-fast strategy by default', async () => {
|
|
1074
|
-
// Test that current behavior is maintained
|
|
1075
|
-
});
|
|
1076
|
-
|
|
1077
|
-
it('should use complete-all strategy when specified', async () => {
|
|
1078
|
-
// Test new behavior with errorStrategy: 'complete-all'
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
it('should aggregate all errors in complete-all mode', async () => {
|
|
1082
|
-
// Test that all errors are collected
|
|
1083
|
-
});
|
|
1084
|
-
|
|
1085
|
-
it('should preserve error context in aggregate error', async () => {
|
|
1086
|
-
// Test that workflow ID, name, etc. are preserved
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
it('should throw WorkflowAggregateError in complete-all mode', async () => {
|
|
1090
|
-
// Test error type and structure
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
it('should handle partial success in complete-all mode', async () => {
|
|
1094
|
-
// Test that some successes + some failures works correctly
|
|
1095
|
-
});
|
|
1096
|
-
});
|
|
1097
|
-
```
|
|
1098
|
-
|
|
1099
|
-
---
|
|
1100
|
-
|
|
1101
|
-
## 6. Implementation Roadmap
|
|
1102
|
-
|
|
1103
|
-
### Phase 1: Core Implementation (Week 1)
|
|
1104
|
-
|
|
1105
|
-
**Tasks**:
|
|
1106
|
-
1. Add `errorStrategy` option to `TaskOptions` interface
|
|
1107
|
-
2. Implement `Promise.allSettled` logic in @Task decorator
|
|
1108
|
-
3. Create `WorkflowAggregateError` type and factory
|
|
1109
|
-
4. Add error collection and aggregation logic
|
|
1110
|
-
5. Update JSDoc comments
|
|
1111
|
-
|
|
1112
|
-
**Deliverables**:
|
|
1113
|
-
- Enhanced `/home/dustin/projects/groundswell/src/types/decorators.ts`
|
|
1114
|
-
- Updated `/home/dustin/projects/groundswell/src/decorators/task.ts`
|
|
1115
|
-
- New `/home/dustin/projects/groundswell/src/types/aggregate-error.ts`
|
|
1116
|
-
|
|
1117
|
-
**Success Criteria**:
|
|
1118
|
-
- TypeScript compiles without errors
|
|
1119
|
-
- Existing tests pass (backward compatibility)
|
|
1120
|
-
- New behavior accessible via `errorStrategy: 'complete-all'`
|
|
1121
|
-
|
|
1122
|
-
### Phase 2: Testing (Week 1-2)
|
|
1123
|
-
|
|
1124
|
-
**Tasks**:
|
|
1125
|
-
1. Add unit tests for error aggregation
|
|
1126
|
-
2. Add integration tests for complete-all strategy
|
|
1127
|
-
3. Add edge case tests (mixed success/failure)
|
|
1128
|
-
4. Add performance tests (compare fail-fast vs complete-all)
|
|
1129
|
-
5. Update example workflows
|
|
1130
|
-
|
|
1131
|
-
**Deliverables**:
|
|
1132
|
-
- New tests in `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts`
|
|
1133
|
-
- Updated `/home/dustin/projects/groundswell/examples/examples/06-concurrent-tasks.ts`
|
|
1134
|
-
|
|
1135
|
-
**Success Criteria**:
|
|
1136
|
-
- All tests pass (including new tests)
|
|
1137
|
-
- Test coverage > 90% for new code
|
|
1138
|
-
- Examples demonstrate both strategies
|
|
1139
|
-
|
|
1140
|
-
### Phase 3: Documentation (Week 2)
|
|
1141
|
-
|
|
1142
|
-
**Tasks**:
|
|
1143
|
-
1. Update `/home/dustin/projects/groundswell/docs/workflow.md`
|
|
1144
|
-
2. Add error handling guide
|
|
1145
|
-
3. Update API reference
|
|
1146
|
-
4. Create migration guide
|
|
1147
|
-
5. Add best practices document
|
|
1148
|
-
|
|
1149
|
-
**Deliverables**:
|
|
1150
|
-
- Updated documentation
|
|
1151
|
-
- New error handling guide
|
|
1152
|
-
- Migration guide from fail-fast to complete-all
|
|
1153
|
-
- Best practices document
|
|
1154
|
-
|
|
1155
|
-
**Success Criteria**:
|
|
1156
|
-
- All APIs documented
|
|
1157
|
-
- Examples cover both strategies
|
|
1158
|
-
- Migration path is clear
|
|
1159
|
-
|
|
1160
|
-
### Phase 4: Validation (Week 2)
|
|
1161
|
-
|
|
1162
|
-
**Tasks**:
|
|
1163
|
-
1. Manual testing with real workflows
|
|
1164
|
-
2. Performance benchmarking
|
|
1165
|
-
3. Memory profiling
|
|
1166
|
-
4. Error scenario testing
|
|
1167
|
-
5. User acceptance testing
|
|
1168
|
-
|
|
1169
|
-
**Deliverables**:
|
|
1170
|
-
- Performance benchmarks
|
|
1171
|
-
- Memory profiles
|
|
1172
|
-
- Test results report
|
|
1173
|
-
- Bug fixes (if any)
|
|
1174
|
-
|
|
1175
|
-
**Success Criteria**:
|
|
1176
|
-
- Performance acceptable (< 10% overhead)
|
|
1177
|
-
- No memory leaks
|
|
1178
|
-
- All error scenarios handled correctly
|
|
1179
|
-
- Users can use both strategies effectively
|
|
1180
|
-
|
|
1181
|
-
---
|
|
1182
|
-
|
|
1183
|
-
## 7. Appendix: Code Examples
|
|
1184
|
-
|
|
1185
|
-
### 7.1 Complete Example: Fail-Fast vs Complete-All
|
|
1186
|
-
|
|
1187
|
-
```typescript
|
|
1188
|
-
import { Workflow, Task, Step } from 'groundswell';
|
|
1189
|
-
|
|
1190
|
-
// Child workflow that may fail
|
|
1191
|
-
class DataProcessor extends Workflow {
|
|
1192
|
-
constructor(
|
|
1193
|
-
public id: string,
|
|
1194
|
-
public shouldFail: boolean = false,
|
|
1195
|
-
parent?: Workflow
|
|
1196
|
-
) {
|
|
1197
|
-
super(id, parent);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
@Step()
|
|
1201
|
-
async process(): Promise<string> {
|
|
1202
|
-
if (this.shouldFail) {
|
|
1203
|
-
throw new Error(`Processing failed in ${this.id}`);
|
|
1204
|
-
}
|
|
1205
|
-
return `Processed by ${this.id}`;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
async run(): Promise<string> {
|
|
1209
|
-
this.setStatus('running');
|
|
1210
|
-
const result = await this.process();
|
|
1211
|
-
this.setStatus('completed');
|
|
1212
|
-
return result;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// Parent workflow using fail-fast (default)
|
|
1217
|
-
class FailFastParent extends Workflow {
|
|
1218
|
-
@Task({ concurrent: true })
|
|
1219
|
-
async spawnProcessors(): Promise<DataProcessor[]> {
|
|
1220
|
-
return [
|
|
1221
|
-
new DataProcessor('processor-1', false, this),
|
|
1222
|
-
new DataProcessor('processor-2', true, this), // This will fail
|
|
1223
|
-
new DataProcessor('processor-3', false, this),
|
|
1224
|
-
];
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
async run(): Promise<void> {
|
|
1228
|
-
this.setStatus('running');
|
|
1229
|
-
|
|
1230
|
-
try {
|
|
1231
|
-
await this.spawnProcessors();
|
|
1232
|
-
console.log('All processors completed successfully');
|
|
1233
|
-
} catch (error) {
|
|
1234
|
-
console.error('Fail-fast: Stopped at first error');
|
|
1235
|
-
console.error('Error:', error.message);
|
|
1236
|
-
// processor-3 never runs
|
|
1237
|
-
// Only processor-2's error is visible
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
this.setStatus('completed');
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// Parent workflow using complete-all
|
|
1245
|
-
class CompleteAllParent extends Workflow {
|
|
1246
|
-
@Task({
|
|
1247
|
-
concurrent: true,
|
|
1248
|
-
errorStrategy: 'complete-all', // NEW: Wait for all
|
|
1249
|
-
})
|
|
1250
|
-
async spawnProcessors(): Promise<DataProcessor[]> {
|
|
1251
|
-
return [
|
|
1252
|
-
new DataProcessor('processor-1', false, this),
|
|
1253
|
-
new DataProcessor('processor-2', true, this), // This will fail
|
|
1254
|
-
new DataProcessor('processor-3', false, this),
|
|
1255
|
-
];
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
async run(): Promise<void> {
|
|
1259
|
-
this.setStatus('running');
|
|
1260
|
-
|
|
1261
|
-
try {
|
|
1262
|
-
await this.spawnProcessors();
|
|
1263
|
-
console.log('All processors completed successfully');
|
|
1264
|
-
} catch (error) {
|
|
1265
|
-
if (error.name === 'WorkflowAggregateError') {
|
|
1266
|
-
console.error('Complete-all: All processors completed');
|
|
1267
|
-
console.error(`${error.failedChildren}/${error.totalChildren} failed:`);
|
|
1268
|
-
|
|
1269
|
-
for (const failure of error.errors) {
|
|
1270
|
-
console.error(` - ${failure.workflowName}: ${failure.error?.message}`);
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// processor-1 and processor-3 completed successfully
|
|
1274
|
-
// processor-2's error is visible
|
|
1275
|
-
// All errors are aggregated
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
this.setStatus('completed');
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
```
|
|
1283
|
-
|
|
1284
|
-
### 7.2 Error Aggregation Implementation
|
|
1285
|
-
|
|
1286
|
-
```typescript
|
|
1287
|
-
/**
|
|
1288
|
-
* Aggregate multiple workflow errors into a single error
|
|
1289
|
-
*/
|
|
1290
|
-
class WorkflowAggregateError extends Error {
|
|
1291
|
-
constructor(
|
|
1292
|
-
message: string,
|
|
1293
|
-
public errors: Array<{
|
|
1294
|
-
workflowId: string;
|
|
1295
|
-
workflowName: string;
|
|
1296
|
-
error: unknown;
|
|
1297
|
-
}>,
|
|
1298
|
-
public taskName: string,
|
|
1299
|
-
public workflowId: string,
|
|
1300
|
-
public totalChildren: number,
|
|
1301
|
-
public failedChildren: number
|
|
1302
|
-
) {
|
|
1303
|
-
super(message);
|
|
1304
|
-
this.name = 'WorkflowAggregateError';
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
/**
|
|
1308
|
-
* Pretty-print the aggregate error
|
|
1309
|
-
*/
|
|
1310
|
-
toString(): string {
|
|
1311
|
-
let output = `${this.taskName}: ${this.message}\n`;
|
|
1312
|
-
output += ` Total: ${this.totalChildren}, Failed: ${this.failedChildren}\n`;
|
|
1313
|
-
output += ` Success Rate: ${((1 - this.failedChildren / this.totalChildren) * 100).toFixed(1)}%\n`;
|
|
1314
|
-
|
|
1315
|
-
for (const failure of this.errors) {
|
|
1316
|
-
output += ` - ${failure.workflowName} (${failure.workflowId}):\n`;
|
|
1317
|
-
output += ` ${failure.error?.message || 'Unknown error'}\n`;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
return output;
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
/**
|
|
1324
|
-
* Get error statistics
|
|
1325
|
-
*/
|
|
1326
|
-
getStats() {
|
|
1327
|
-
const errorsByType = new Map<string, number>();
|
|
1328
|
-
const errorsByWorkflow = new Map<string, number>();
|
|
1329
|
-
|
|
1330
|
-
for (const failure of this.errors) {
|
|
1331
|
-
const errorType = failure.error?.constructor.name || 'Unknown';
|
|
1332
|
-
errorsByType.set(errorType, (errorsByType.get(errorType) || 0) + 1);
|
|
1333
|
-
errorsByWorkflow.set(
|
|
1334
|
-
failure.workflowName,
|
|
1335
|
-
(errorsByWorkflow.get(failure.workflowName) || 0) + 1
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
return {
|
|
1340
|
-
totalErrors: this.errors.length,
|
|
1341
|
-
totalChildren: this.totalChildren,
|
|
1342
|
-
failedChildren: this.failedChildren,
|
|
1343
|
-
successRate: (1 - this.failedChildren / this.totalChildren) * 100,
|
|
1344
|
-
errorsByType: Object.fromEntries(errorsByType),
|
|
1345
|
-
errorsByWorkflow: Object.fromEntries(errorsByWorkflow),
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
```
|
|
1350
|
-
|
|
1351
|
-
### 7.3 Usage in Production
|
|
1352
|
-
|
|
1353
|
-
```typescript
|
|
1354
|
-
// Real-world example: Bulk data synchronization
|
|
1355
|
-
class DataSyncWorkflow extends Workflow {
|
|
1356
|
-
@ObservedState()
|
|
1357
|
-
recordsProcessed = 0;
|
|
1358
|
-
|
|
1359
|
-
@ObservedState()
|
|
1360
|
-
recordsFailed = 0;
|
|
1361
|
-
|
|
1362
|
-
@ObservedState()
|
|
1363
|
-
syncStartTime = 0;
|
|
1364
|
-
|
|
1365
|
-
@Task({
|
|
1366
|
-
concurrent: true,
|
|
1367
|
-
errorStrategy: 'complete-all', // Try all records
|
|
1368
|
-
})
|
|
1369
|
-
async syncRecords(records: Record[]): Promise<RecordSyncWorkflow[]> {
|
|
1370
|
-
return records.map(
|
|
1371
|
-
(record) => new RecordSyncWorkflow(record, this)
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
@Step({ snapshotState: true })
|
|
1376
|
-
async aggregateResults(results: PromiseSettledResult<unknown>[]): Promise<void> {
|
|
1377
|
-
const succeeded = results.filter((r) => r.status === 'fulfilled');
|
|
1378
|
-
const failed = results.filter((r) => r.status === 'rejected');
|
|
1379
|
-
|
|
1380
|
-
this.recordsProcessed = succeeded.length;
|
|
1381
|
-
this.recordsFailed = failed.length;
|
|
1382
|
-
|
|
1383
|
-
this.logger.info(`Sync complete: ${succeeded.length} succeeded, ${failed.length} failed`);
|
|
1384
|
-
|
|
1385
|
-
if (failed.length > 0) {
|
|
1386
|
-
this.logger.warn(`${failed.length} records failed to sync, will retry`);
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
async run(records: Record[]): Promise<SyncResults> {
|
|
1391
|
-
this.setStatus('running');
|
|
1392
|
-
this.syncStartTime = Date.now();
|
|
1393
|
-
|
|
1394
|
-
try {
|
|
1395
|
-
await this.syncRecords(records);
|
|
1396
|
-
|
|
1397
|
-
// Continue despite partial failures
|
|
1398
|
-
return {
|
|
1399
|
-
total: records.length,
|
|
1400
|
-
succeeded: this.recordsProcessed,
|
|
1401
|
-
failed: this.recordsFailed,
|
|
1402
|
-
duration: Date.now() - this.syncStartTime,
|
|
1403
|
-
};
|
|
1404
|
-
} catch (error) {
|
|
1405
|
-
if (error.name === 'WorkflowAggregateError') {
|
|
1406
|
-
// Log all failures but don't fail the workflow
|
|
1407
|
-
this.logger.warn(`Partial sync complete: ${error.failedChildren}/${error.totalChildren} failed`);
|
|
1408
|
-
|
|
1409
|
-
return {
|
|
1410
|
-
total: records.length,
|
|
1411
|
-
succeeded: error.totalChildren - error.failedChildren,
|
|
1412
|
-
failed: error.failedChildren,
|
|
1413
|
-
duration: Date.now() - this.syncStartTime,
|
|
1414
|
-
errors: error.errors,
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
throw error;
|
|
1418
|
-
} finally {
|
|
1419
|
-
this.setStatus('completed');
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
```
|
|
1424
|
-
|
|
1425
|
-
---
|
|
1426
|
-
|
|
1427
|
-
## 8. Conclusion
|
|
1428
|
-
|
|
1429
|
-
### 8.1 Summary of Findings
|
|
1430
|
-
|
|
1431
|
-
1. **Promise.all vs Promise.allSettled**:
|
|
1432
|
-
- Promise.all (fail-fast): Fast, simple, but loses error information
|
|
1433
|
-
- Promise.allSettled (complete-all): Slower, more complex, but provides complete error visibility
|
|
1434
|
-
- **Recommendation**: Support both strategies via configuration
|
|
1435
|
-
|
|
1436
|
-
2. **Production Workflow Engines**:
|
|
1437
|
-
- Temporal: Parent close policies, configurable cancellation
|
|
1438
|
-
- Step Functions: Parallel states with error aggregation
|
|
1439
|
-
- Cadence: Exception-based error propagation
|
|
1440
|
-
- Airflow: Trigger rules for flexible execution
|
|
1441
|
-
- **Gap**: Groundswell lacks error aggregation and flexible error strategies
|
|
1442
|
-
|
|
1443
|
-
3. **Error Aggregation Strategies**:
|
|
1444
|
-
- First error wins: Simple but limited (current implementation)
|
|
1445
|
-
- Collect all errors: Comprehensive error visibility (recommended)
|
|
1446
|
-
- Hierarchical merging: Maintains workflow context
|
|
1447
|
-
- Threshold-based: Graceful degradation
|
|
1448
|
-
- **Recommendation**: Implement collect-all with hierarchical context
|
|
1449
|
-
|
|
1450
|
-
4. **Fail-Fast vs Complete-All**:
|
|
1451
|
-
- Fail-fast: Critical workflows, dependent tasks, fast feedback
|
|
1452
|
-
- Complete-all: Independent tasks, partial success, observability
|
|
1453
|
-
- **Recommendation**: Default to complete-all for Groundswell (observability-first design)
|
|
1454
|
-
|
|
1455
|
-
### 8.2 Recommendations for Groundswell
|
|
1456
|
-
|
|
1457
|
-
**Priority 1 (Immediate)**:
|
|
1458
|
-
1. Add `errorStrategy` option to `TaskOptions`
|
|
1459
|
-
2. Implement `Promise.allSettled` path in @Task decorator
|
|
1460
|
-
3. Create `WorkflowAggregateError` type
|
|
1461
|
-
4. Maintain backward compatibility (fail-fast as default)
|
|
1462
|
-
|
|
1463
|
-
**Priority 2 (Short-term)**:
|
|
1464
|
-
1. Add comprehensive error aggregation
|
|
1465
|
-
2. Include workflow context in errors
|
|
1466
|
-
3. Add error statistics and pretty-printing
|
|
1467
|
-
4. Update documentation and examples
|
|
1468
|
-
|
|
1469
|
-
**Priority 3 (Long-term)**:
|
|
1470
|
-
1. Consider changing default to complete-all in v2.0
|
|
1471
|
-
2. Add retry policies per child workflow
|
|
1472
|
-
3. Implement threshold-based error handling
|
|
1473
|
-
4. Add error rate monitoring and alerting
|
|
1474
|
-
|
|
1475
|
-
### 8.3 Impact Assessment
|
|
1476
|
-
|
|
1477
|
-
**Benefits**:
|
|
1478
|
-
- Better observability: See all errors, not just first
|
|
1479
|
-
- Graceful degradation: Partial success scenarios
|
|
1480
|
-
- Production-ready: Aligns with industry patterns
|
|
1481
|
-
- Debugging: Comprehensive error information
|
|
1482
|
-
- Flexibility: Developers choose strategy per task
|
|
1483
|
-
|
|
1484
|
-
**Costs**:
|
|
1485
|
-
- Complexity: More configuration options
|
|
1486
|
-
- Memory: Storing all errors (mitigated by error limiting)
|
|
1487
|
-
- Performance: Waiting for all tasks to complete (mitigated by configurable strategy)
|
|
1488
|
-
- Learning curve: Developers need to understand both strategies
|
|
1489
|
-
|
|
1490
|
-
**Risk Mitigation**:
|
|
1491
|
-
- Backward compatibility: Default to fail-fast
|
|
1492
|
-
- Gradual migration: Opt-in to complete-all
|
|
1493
|
-
- Documentation: Clear examples and best practices
|
|
1494
|
-
- Testing: Comprehensive test coverage
|
|
1495
|
-
|
|
1496
|
-
---
|
|
1497
|
-
|
|
1498
|
-
## References
|
|
1499
|
-
|
|
1500
|
-
### Production Documentation
|
|
1501
|
-
|
|
1502
|
-
1. **Temporal.io**: Child Workflows and Error Handling
|
|
1503
|
-
- https://docs.temporal.io/develop/typescript/child-workflows
|
|
1504
|
-
- Key concepts: Parent close policy, cancellation scopes, retry policies
|
|
1505
|
-
|
|
1506
|
-
2. **AWS Step Functions**: Error Handling
|
|
1507
|
-
- https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html
|
|
1508
|
-
- Key concepts: Retry blocks, catch blocks, parallel state error aggregation
|
|
1509
|
-
|
|
1510
|
-
3. **Cadence Workflow**: Execution Patterns
|
|
1511
|
-
- https://cadenceworkflow.io/docs/concepts/execution/
|
|
1512
|
-
- Key concepts: Exception-based error propagation, child workflow isolation
|
|
1513
|
-
|
|
1514
|
-
4. **Apache Airflow**: Task Instances and Trigger Rules
|
|
1515
|
-
- https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html
|
|
1516
|
-
- Key concepts: Trigger rules, task dependencies, failure handlers
|
|
1517
|
-
|
|
1518
|
-
### JavaScript/TypeScript Documentation
|
|
1519
|
-
|
|
1520
|
-
5. **MDN: Promise.all()**
|
|
1521
|
-
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
|
|
1522
|
-
- Fail-fast behavior, rejection handling
|
|
1523
|
-
|
|
1524
|
-
6. **MDN: Promise.allSettled()**
|
|
1525
|
-
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
|
|
1526
|
-
- Complete-all behavior, status objects
|
|
1527
|
-
|
|
1528
|
-
7. **Node.js: Asynchronous Work**
|
|
1529
|
-
- https://nodejs.dev/en/learn/asynchronous-work/
|
|
1530
|
-
- Async/await patterns, error handling
|
|
1531
|
-
|
|
1532
|
-
### Groundswell Codebase
|
|
1533
|
-
|
|
1534
|
-
8. **@Task Decorator Implementation**
|
|
1535
|
-
- `/home/dustin/projects/groundswell/src/decorators/task.ts`
|
|
1536
|
-
- Current fail-fast implementation
|
|
1537
|
-
|
|
1538
|
-
9. **Error Strategy Types**
|
|
1539
|
-
- `/home/dustin/projects/groundswell/src/types/error-strategy.ts`
|
|
1540
|
-
- Defined but not implemented
|
|
1541
|
-
|
|
1542
|
-
10. **Workflow Documentation**
|
|
1543
|
-
- `/home/dustin/projects/groundswell/docs/workflow.md`
|
|
1544
|
-
- Current error handling patterns
|
|
1545
|
-
|
|
1546
|
-
11. **Concurrent Tasks Example**
|
|
1547
|
-
- `/home/dustin/projects/groundswell/examples/examples/06-concurrent-tasks.ts`
|
|
1548
|
-
- Real-world usage examples
|
|
1549
|
-
|
|
1550
|
-
### Research Context
|
|
1551
|
-
|
|
1552
|
-
12. **Error Handling Best Practices Research**
|
|
1553
|
-
- `/home/dustin/projects/groundswell/plan/docs/research/error_handling_patterns.md`
|
|
1554
|
-
- TypeScript error handling patterns, state capture
|
|
1555
|
-
|
|
1556
|
-
13. **PRD: Hierarchical Workflow Engine**
|
|
1557
|
-
- `/home/dustin/projects/groundswell/PRPs/001-hierarchical-workflow-engine.md`
|
|
1558
|
-
- Requirements for observability and error handling
|
|
1559
|
-
|
|
1560
|
-
---
|
|
1561
|
-
|
|
1562
|
-
**Document Version**: 1.0
|
|
1563
|
-
**Last Updated**: 2026-01-12
|
|
1564
|
-
**Status**: Ready for Implementation
|
|
1565
|
-
**Next Review**: After Phase 1 Implementation
|