groundswell 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/.claude/commands/subtask-planning/prp-base-create.md +120 -0
  2. package/.claude/commands/subtask-planning/prp-base-execute.md +65 -0
  3. package/.claude/commands/task-breakdown.md +94 -0
  4. package/.claude/system_prompts/task-breakdown.md +1 -0
  5. package/CHANGELOG.md +188 -0
  6. package/PRD.md +543 -0
  7. package/README.md +99 -5
  8. package/examples/README.md +15 -1
  9. package/examples/examples/11-reparenting-workflows.ts +269 -0
  10. package/examples/index.ts +4 -0
  11. package/package-lock.json +2398 -0
  12. package/package.json +3 -1
  13. package/plan/001_d3bb02af4886/TEST_RESULTS.md +259 -0
  14. package/plan/001_d3bb02af4886/bug_fix_tasks.json +484 -0
  15. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +488 -0
  16. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +581 -0
  17. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +687 -0
  18. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +492 -0
  19. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +932 -0
  20. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +1109 -0
  21. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +802 -0
  22. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +603 -0
  23. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +564 -0
  24. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +518 -0
  25. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +1252 -0
  26. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +364 -0
  27. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +114 -0
  28. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +205 -0
  29. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +199 -0
  30. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +134 -0
  31. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +495 -0
  32. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +435 -0
  33. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +506 -0
  34. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +612 -0
  35. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +558 -0
  36. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +788 -0
  37. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +460 -0
  38. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +454 -0
  39. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +520 -0
  40. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +417 -0
  41. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +760 -0
  42. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +245 -0
  43. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +792 -0
  44. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +535 -0
  45. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +190 -0
  46. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +654 -0
  47. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +227 -0
  48. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +345 -0
  49. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +193 -0
  50. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +1323 -0
  51. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +1011 -0
  52. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +927 -0
  53. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +505 -0
  54. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +401 -0
  55. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +142 -0
  56. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +112 -0
  57. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +159 -0
  58. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +549 -0
  59. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +368 -0
  60. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +172 -0
  61. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +175 -0
  62. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +696 -0
  63. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +860 -0
  64. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +1066 -0
  65. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +1103 -0
  66. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +789 -0
  67. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +1098 -0
  68. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +1037 -0
  69. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +916 -0
  70. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +1045 -0
  71. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +890 -0
  72. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +822 -0
  73. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +668 -0
  74. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +706 -0
  75. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +265 -0
  76. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +655 -0
  77. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +1103 -0
  78. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +426 -0
  79. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +506 -0
  80. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +114 -0
  81. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +316 -0
  82. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +754 -0
  83. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +612 -0
  84. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +719 -0
  85. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +215 -0
  86. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +765 -0
  87. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +718 -0
  88. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +149 -0
  89. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +470 -0
  90. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +332 -0
  91. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +167 -0
  92. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +265 -0
  93. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +273 -0
  94. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +760 -0
  95. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +434 -0
  96. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +717 -0
  97. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +472 -0
  98. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +125 -0
  99. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +301 -0
  100. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +1170 -0
  101. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +940 -0
  102. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +151 -0
  103. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +650 -0
  104. package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +259 -0
  105. package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +457 -0
  106. package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +346 -0
  107. package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +311 -0
  108. package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +1565 -0
  109. package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +288 -0
  110. package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +741 -0
  111. package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +652 -0
  112. package/plan/001_d3bb02af4886/docs/PRP/PRP.md +527 -0
  113. package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +415 -0
  114. package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +378 -0
  115. package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +713 -0
  116. package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +370 -0
  117. package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +499 -0
  118. package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +230 -0
  119. package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +1134 -0
  120. package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +179 -0
  121. package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +629 -0
  122. package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +214 -0
  123. package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +629 -0
  124. package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +529 -0
  125. package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +142 -0
  126. package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +304 -0
  127. package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +558 -0
  128. package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +256 -0
  129. package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +346 -0
  130. package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +415 -0
  131. package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +489 -0
  132. package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +218 -0
  133. package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +380 -0
  134. package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +1923 -0
  135. package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +319 -0
  136. package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +115 -0
  137. package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +134 -0
  138. package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +153 -0
  139. package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +132 -0
  140. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +716 -0
  141. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +186 -0
  142. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +604 -0
  143. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +213 -0
  144. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +30 -0
  145. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +56 -0
  146. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +53 -0
  147. package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +49 -0
  148. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +958 -0
  149. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +339 -0
  150. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +305 -0
  151. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +433 -0
  152. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +1574 -0
  153. package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +1014 -0
  154. package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +376 -0
  155. package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +1507 -0
  156. package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +949 -0
  157. package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +619 -0
  158. package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +723 -0
  159. package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-security-guide.md +56 -0
  160. package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +460 -0
  161. package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +324 -0
  162. package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +175 -0
  163. package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +499 -0
  164. package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +163 -0
  165. package/plan/bugfix/BUG_FIX_SUMMARY.md +961 -0
  166. package/src/__tests__/adversarial/attachChild-performance.test.ts +216 -0
  167. package/src/__tests__/adversarial/circular-reference.test.ts +101 -0
  168. package/src/__tests__/adversarial/complex-circular-reference.test.ts +139 -0
  169. package/src/__tests__/adversarial/concurrent-task-failures.test.ts +571 -0
  170. package/src/__tests__/adversarial/deep-analysis.test.ts +729 -0
  171. package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +213 -0
  172. package/src/__tests__/adversarial/e2e-prd-validation.test.ts +448 -0
  173. package/src/__tests__/adversarial/edge-case.test.ts +703 -0
  174. package/src/__tests__/adversarial/error-merge-strategy.test.ts +760 -0
  175. package/src/__tests__/adversarial/incremental-performance.test.ts +140 -0
  176. package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +457 -0
  177. package/src/__tests__/adversarial/observer-propagation.test.ts +487 -0
  178. package/src/__tests__/adversarial/parent-validation.test.ts +143 -0
  179. package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +611 -0
  180. package/src/__tests__/adversarial/prd-compliance.test.ts +731 -0
  181. package/src/__tests__/compatibility/backward-compatibility.test.ts +1572 -0
  182. package/src/__tests__/helpers/index.ts +18 -0
  183. package/src/__tests__/helpers/tree-verification.ts +257 -0
  184. package/src/__tests__/integration/bidirectional-consistency.test.ts +847 -0
  185. package/src/__tests__/integration/observer-logging.test.ts +643 -0
  186. package/src/__tests__/integration/tree-mirroring.test.ts +37 -0
  187. package/src/__tests__/integration/workflow-reparenting.test.ts +303 -0
  188. package/src/__tests__/unit/context.test.ts +79 -0
  189. package/src/__tests__/unit/logger.test.ts +293 -0
  190. package/src/__tests__/unit/observable.test.ts +321 -0
  191. package/src/__tests__/unit/tree-debugger-incremental.test.ts +170 -0
  192. package/src/__tests__/unit/utils/workflow-error-utils.test.ts +209 -0
  193. package/src/__tests__/unit/workflow-detachChild.test.ts +100 -0
  194. package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +153 -0
  195. package/src/__tests__/unit/workflow-isDescendantOf.test.ts +180 -0
  196. package/src/__tests__/unit/workflow.test.ts +277 -1
  197. package/src/core/agent.ts +21 -1
  198. package/src/core/logger.ts +27 -2
  199. package/src/core/workflow-context.ts +6 -4
  200. package/src/core/workflow.ts +252 -14
  201. package/src/debugger/tree-debugger.ts +52 -7
  202. package/src/decorators/task.ts +65 -2
  203. package/src/index.ts +4 -2
  204. package/src/types/decorators.ts +8 -1
  205. package/src/types/events.ts +1 -0
  206. package/src/utils/index.ts +1 -0
  207. package/src/utils/observable.ts +32 -3
  208. package/src/utils/workflow-error-utils.ts +56 -0
  209. package/tsconfig.json +1 -1
  210. package/llms_full.txt +0 -5890
  211. package/tasks.json +0 -0
  212. /package/plan/{backlog.json → 001_d3bb02af4886/backlog.json} +0 -0
  213. /package/plan/{P1P2/PRP.md → 001_d3bb02af4886/docs/PRP/P1P2-PRP.md} +0 -0
  214. /package/plan/{P3P4/PRP.md → 001_d3bb02af4886/docs/PRP/P3P4-PRP.md} +0 -0
  215. /package/plan/{P4P5/PRP.md → 001_d3bb02af4886/docs/PRP/P4P5-PRP.md} +0 -0
  216. /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/external_deps.md +0 -0
  217. /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/system_context.md +0 -0
  218. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_BEST_PRACTICES.md +0 -0
  219. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_CODE_PATTERNS.md +0 -0
  220. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_INTEGRATION_GUIDE.md +0 -0
  221. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_RESEARCH_INDEX.md +0 -0
  222. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_INDEX.md +0 -0
  223. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_RESEARCH_REPORT.md +0 -0
  224. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/RESEARCH_SUMMARY.md +0 -0
  225. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/anthropic-sdk.md +0 -0
  226. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/async-local-storage.md +0 -0
  227. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-code-patterns.md +0 -0
  228. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-decision-matrix.md +0 -0
  229. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-implementation-guide.md +0 -0
  230. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-integration-guide.md +0 -0
  231. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-patterns.md +0 -0
  232. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-quick-reference.md +0 -0
  233. /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/zod-schema.md +0 -0
  234. /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/caching-lru.md +0 -0
  235. /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/introspection-tools.md +0 -0
  236. /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/reflection-patterns.md +0 -0
  237. /package/plan/{P4P5/research → 001_d3bb02af4886/docs/research/P4P5}/RESEARCH_SUMMARY.md +0 -0
  238. /package/plan/{research → 001_d3bb02af4886/docs/research/general}/INTROSPECTION_RESEARCH_SUMMARY.md +0 -0
  239. /package/plan/{research → 001_d3bb02af4886/docs/research/general}/README-INTROSPECTION.md +0 -0
  240. /package/plan/{research → 001_d3bb02af4886/docs/research/general}/agent-introspection-patterns.md +0 -0
  241. /package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-tool-examples.md +0 -0
  242. /package/{PRPs/PRDs/001-hierarchical-workflow-engine.md → plan/001_d3bb02af4886/prd_snapshot.md} +0 -0
@@ -0,0 +1,1066 @@
1
+ name: "P1.M2.T2.S2 - Implement Error Aggregation Logic in @Task Decorator"
2
+ description: |
3
+
4
+ ---
5
+
6
+ ## Goal
7
+
8
+ **Feature Goal**: Implement error aggregation logic in the @Task decorator to support the `errorMergeStrategy` option, enabling users to configure how multiple concurrent workflow errors are merged and propagated.
9
+
10
+ **Deliverable**: Updated @Task decorator (`src/decorators/task.ts`) that:
11
+
12
+ 1. Checks if `opts.errorMergeStrategy?.enabled` is true after collecting errors from Promise.allSettled()
13
+ 2. Calls `opts.errorMergeStrategy.combine?(errors)` if provided, otherwise uses a default error merger
14
+ 3. Throws the merged error instead of the first error when error merge strategy is enabled
15
+ 4. Emits error event with the merged error (single event for all failures)
16
+
17
+ **Success Definition**:
18
+
19
+ * When `errorMergeStrategy?.enabled === true`, all concurrent workflow errors are aggregated
20
+ * When `combine()` function is provided, it is called with array of WorkflowError objects
21
+ * When `combine()` is not provided, a default merger creates an AggregateError-like structure
22
+ * The merged error contains aggregated information from all failures
23
+ * A single error event is emitted with the merged error
24
+ * Backward compatibility is maintained: when `errorMergeStrategy` is not provided, first error is thrown
25
+ * All existing tests continue to pass
26
+
27
+ ## User Persona (if applicable)
28
+
29
+ **Target User**: Library Developer / Workflow Architect
30
+
31
+ **Use Case**: A developer running concurrent child workflows wants to receive comprehensive error information when multiple workflows fail, instead of just the first error.
32
+
33
+ **User Journey**:
34
+
35
+ 1. Developer creates a parent workflow with multiple concurrent child workflows
36
+ 2. Some child workflows fail
37
+ 3. Developer wants to see ALL errors, not just the first one
38
+ 4. Developer enables `errorMergeStrategy: { enabled: true }` in @Task decorator options
39
+ 5. Developer optionally provides a custom `combine()` function to merge errors
40
+ 6. When failures occur, all errors are aggregated and thrown as a single merged error
41
+ 7. Developer can inspect the merged error to see all failures
42
+
43
+ **Pain Points Addressed**:
44
+
45
+ * Currently only the first error from concurrent failures is visible
46
+ * Other concurrent failures are collected but not surfaced to the caller
47
+ * No way to customize how multiple errors should be merged
48
+ * Difficult to debug when multiple independent workflows fail
49
+
50
+ ## Why
51
+
52
+ * **Observability**: Multiple concurrent failures should be visible together, not hidden
53
+ * **Debugging**: Developers need to see ALL errors to understand patterns and root causes
54
+ * **Production Patterns**: Aligns with workflow engines like Airflow (trigger rules) and Step Functions (parallel error aggregation)
55
+ * **PRD Compliance**: PRD Section 10 specifies "Optional Multi-Error Merging" as a feature
56
+ * **Enable P1.M2.T2.S3**: Default error merger implementation depends on this being complete
57
+ * **Enable P1.M2.T2.S4**: ErrorMergeStrategy tests depend on this implementation
58
+
59
+ ## What
60
+
61
+ Modify the @Task decorator in `src/decorators/task.ts` to implement error aggregation logic when `errorMergeStrategy.enabled === true`.
62
+
63
+ ### Current Implementation (Lines 111-121)
64
+
65
+ ```typescript
66
+ if (runnable.length > 0) {
67
+ const results = await Promise.allSettled(runnable.map((w) => w.run()));
68
+
69
+ const rejected = results.filter(
70
+ (r): r is PromiseRejectedResult => r.status === 'rejected'
71
+ );
72
+
73
+ if (rejected.length > 0) {
74
+ throw rejected[0].reason; // First error wins (backward compatible)
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Target Implementation
80
+
81
+ ```typescript
82
+ if (runnable.length > 0) {
83
+ const results = await Promise.allSettled(runnable.map((w) => w.run()));
84
+
85
+ const rejected = results.filter(
86
+ (r): r is PromiseRejectedResult => r.status === 'rejected'
87
+ );
88
+
89
+ if (rejected.length > 0) {
90
+ // Check if error merge strategy is enabled
91
+ if (opts.errorMergeStrategy?.enabled) {
92
+ // Extract WorkflowError objects from rejected promises
93
+ const errors = rejected.map((r) => r.reason as WorkflowError);
94
+
95
+ // Merge errors using custom combine() or default merger
96
+ const mergedError = opts.errorMergeStrategy?.combine
97
+ ? opts.errorMergeStrategy.combine(errors)
98
+ : defaultErrorMerger(errors, taskName, wf.id, runnable.length);
99
+
100
+ // Emit error event with merged error
101
+ wf.emitEvent({
102
+ type: 'error',
103
+ node: wf.node,
104
+ error: mergedError,
105
+ });
106
+
107
+ // Throw merged error
108
+ throw mergedError;
109
+ }
110
+
111
+ // Backward compatibility: throw first error
112
+ throw rejected[0].reason;
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Success Criteria
118
+
119
+ * [ ] Check `opts.errorMergeStrategy?.enabled` before throwing error
120
+ * [ ] Extract WorkflowError array from rejected promises
121
+ * [ ] Call custom `combine()` function if provided
122
+ * [ ] Use default error merger when `combine()` is not provided
123
+ * [ ] Emit error event with merged error (not first error)
124
+ * [ ] Throw merged error instead of first error when enabled
125
+ * [ ] Backward compatibility maintained when `errorMergeStrategy` not provided
126
+ * [ ] All existing tests pass
127
+ * [ ] New tests for error aggregation pass
128
+
129
+ ## All Needed Context
130
+
131
+ ### Context Completeness Check
132
+
133
+ **"No Prior Knowledge" Test**: If someone knew nothing about this codebase, would they have everything needed to implement this successfully?
134
+
135
+ **Answer**: YES - This PRP provides:
136
+
137
+ * Exact file location with line numbers (src/decorators/task.ts:111-121)
138
+ * Complete current implementation code
139
+ * Type definitions for all interfaces used
140
+ * Error handling patterns to follow
141
+ * Default error merger implementation
142
+ * Test patterns for validation
143
+ * External research documentation with URLs
144
+
145
+ ### Documentation & References
146
+
147
+ ```yaml
148
+ # MUST READ - Current Implementation
149
+ - file: src/decorators/task.ts
150
+ lines: 104-122
151
+ why: Target section to modify - contains Promise.allSettled error handling
152
+ pattern: Error collection with rejected.filter() and first-error-wins throw
153
+ gotcha: rejected[].reason is already WorkflowError (wrapped by @Step decorator)
154
+
155
+ - file: src/decorators/task.ts
156
+ lines: 1-136 (full file)
157
+ why: Complete @Task decorator context including imports and event emission
158
+ pattern: Task decorator wrapper with event emission and child attachment
159
+
160
+ - file: src/types/decorators.ts
161
+ why: TaskOptions interface definition - errorMergeStrategy field added in P1.M2.T2.S1
162
+ current_content: |
163
+ import type { ErrorMergeStrategy } from './error-strategy.js';
164
+
165
+ export interface TaskOptions {
166
+ name?: string;
167
+ concurrent?: boolean;
168
+ errorMergeStrategy?: ErrorMergeStrategy;
169
+ }
170
+
171
+ - file: src/types/error-strategy.ts
172
+ why: ErrorMergeStrategy interface definition
173
+ current_content: |
174
+ import type { WorkflowError } from './error.js';
175
+
176
+ export interface ErrorMergeStrategy {
177
+ enabled: boolean;
178
+ maxMergeDepth?: number;
179
+ combine?(errors: WorkflowError[]): WorkflowError;
180
+ }
181
+
182
+ - file: src/types/error.ts
183
+ why: WorkflowError interface structure - what errors look like in this codebase
184
+ current_content: |
185
+ export interface WorkflowError {
186
+ message: string;
187
+ original: unknown;
188
+ workflowId: string;
189
+ stack?: string;
190
+ state: SerializedWorkflowState;
191
+ logs: LogEntry[];
192
+ }
193
+
194
+ - file: src/types/events.ts
195
+ why: WorkflowEvent union type - error event structure
196
+ current_content: |
197
+ | { type: 'error'; node: WorkflowNode; error: WorkflowError }
198
+
199
+ - file: src/decorators/step.ts
200
+ lines: 109-134
201
+ why: Shows how WorkflowError objects are created and error events emitted
202
+ pattern: Error capture with getObservedState(), event emission
203
+ critical: Each failing workflow already emits error event - don't duplicate
204
+
205
+ # ARCHITECTURE DOCUMENTATION - Implementation Guidance
206
+ - docfile: plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md
207
+ why: Internal guidance on error aggregation strategies
208
+ section: Lines 597-638 ("Groundswell Implementation Recommendation")
209
+ critical: |
210
+ Shows recommended implementation pattern:
211
+ - Check opts.errorMergeStrategy?.enabled
212
+ - Call opts.errorMergeStrategy.combine?(errors)
213
+ - Throw merged error instead of first error
214
+ - Emit error event with merged error
215
+
216
+ # EXTERNAL RESEARCH - Error Aggregation Patterns
217
+ - url: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
218
+ why: Official documentation for Promise.allSettled() method
219
+ section: #description
220
+ critical: Promise.allSettled ALWAYS resolves - manual error checking required
221
+
222
+ - url: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError
223
+ why: Official documentation for AggregateError - useful pattern reference
224
+ section: #description
225
+ critical: AggregateError can contain multiple errors as cause chain
226
+
227
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S2/research/05_comprehensive_summary.md
228
+ why: Comprehensive summary of external research findings
229
+ section: "Recommended Implementation for Groundswell"
230
+ critical: Complete code examples for error aggregation patterns
231
+
232
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md
233
+ why: TypeScript-specific error aggregation patterns
234
+ section: "Production-Grade Patterns"
235
+ critical: Type guards, error normalization, context preservation
236
+
237
+ # PREVIOUS PRPS - Understanding What Was Done Before
238
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md
239
+ why: P1.M2.T2.S1 added errorMergeStrategy field to TaskOptions
240
+ section: "What" section shows Target State
241
+ critical: TaskOptions interface now has errorMergeStrategy?: ErrorMergeStrategy field
242
+
243
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md
244
+ why: P1.M2.T1.S2 implemented Promise.allSettled with error collection
245
+ section: "Implementation Blueprint" shows the replaced Promise.all logic
246
+ critical: rejected array already available - just need to aggregate it
247
+
248
+ # TEST PATTERNS - Follow for Validation
249
+ - file: src/__tests__/adversarial/concurrent-task-failures.test.ts
250
+ why: Tests for concurrent task failure scenarios
251
+ pattern: Testing multiple failing workflows with error verification
252
+
253
+ - file: src/__tests__/adversarial/edge-case.test.ts
254
+ lines: 366-430
255
+ why: Contains concurrent task execution tests with mixed success/failure
256
+ pattern: Testing concurrent workflows with errors
257
+
258
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S2/research/RESEARCH_REPORT.md
259
+ why: Complete testing guide for error aggregation
260
+ section: "Key Research Findings" and "Best Practices Summary"
261
+ critical: Test patterns, assertion patterns, edge cases to cover
262
+
263
+ # TYPE REFERENCES - TypeScript Type Safety
264
+ - url: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#type-predicates
265
+ why: TypeScript type guard documentation
266
+ section: Type Predicates
267
+ critical: Type guard pattern: (r): r is PromiseRejectedResult => r.status === 'rejected'
268
+ ```
269
+
270
+ ### Current Codebase Tree
271
+
272
+ ```bash
273
+ /home/dustin/projects/groundswell
274
+ ├── src/
275
+ │ ├── decorators/
276
+ │ │ ├── index.ts # Exports Task decorator
277
+ │ │ ├── step.ts # @Step decorator with error wrapping (lines 109-134)
278
+ │ │ └── task.ts # PRIMARY: Lines 111-121 to modify - TARGET FILE
279
+ │ ├── types/
280
+ │ │ ├── index.ts # Re-exports all types
281
+ │ │ ├── decorators.ts # TaskOptions with errorMergeStrategy field (from P1.M2.T2.S1)
282
+ │ │ ├── error-strategy.ts # ErrorMergeStrategy interface
283
+ │ │ ├── error.ts # WorkflowError interface
284
+ │ │ └── events.ts # WorkflowEvent union type with error event
285
+ │ └── __tests__/
286
+ │ ├── adversarial/
287
+ │ │ ├── concurrent-task-failures.test.ts # P1.M2.T1.S3 tests
288
+ │ │ └── edge-case.test.ts # Concurrent execution tests
289
+ │ └── unit/
290
+ │ └── decorators.test.ts # General decorator tests
291
+ ├── plan/
292
+ │ └── 001_d3bb02af4886/
293
+ │ ├── bugfix/
294
+ │ │ └── 001_e8e04329daf3/
295
+ │ │ ├── P1M2T2S2/
296
+ │ │ │ ├── research/ # External research storage
297
+ │ │ │ └── PRP.md # ← THIS FILE
298
+ │ │ └── architecture/
299
+ │ │ └── concurrent_execution_best_practices.md
300
+ └── vitest.config.ts # Test configuration
301
+ ```
302
+
303
+ ### Desired Codebase Tree (Changes for This Task)
304
+
305
+ ```bash
306
+ # MODIFIED FILES:
307
+ src/decorators/task.ts # Add error aggregation logic (lines 111-121)
308
+
309
+ # NO NEW FILES for this task
310
+ # P1.M2.T2.S3 will add default error merger utility
311
+ # P1.M2.T2.S4 will add tests for ErrorMergeStrategy functionality
312
+ ```
313
+
314
+ ### Known Gotchas of Our Codebase & Library Quirks
315
+
316
+ ```typescript
317
+ // CRITICAL GOTCHA #1: rejected[].reason is already WorkflowError
318
+ // Each failing workflow has its error wrapped by @Step decorator
319
+ // No need to create new WorkflowError objects
320
+ // The error object already contains: message, original, workflowId, stack, state, logs
321
+ // BAD: const errors = rejected.map(r => createWorkflowError(r.reason));
322
+ // GOOD: const errors = rejected.map(r => r.reason as WorkflowError);
323
+
324
+ // CRITICAL GOTCHA #2: Error events already emitted by @Step decorator
325
+ // src/decorators/step.ts:126-130 emits error events for each failing workflow
326
+ // DO NOT emit error events for individual failures in @Task decorator
327
+ // Only emit a SINGLE error event with the merged error when errorMergeStrategy is enabled
328
+ // When errorMergeStrategy is NOT enabled, no error event here (individual events already emitted)
329
+
330
+ // CRITICAL GOTCHA #3: combine() function signature
331
+ // combine?(errors: WorkflowError[]): WorkflowError
332
+ // Takes array of WorkflowError, returns single WorkflowError
333
+ // User-provided function must return valid WorkflowError object
334
+ // If user returns something invalid, it will fail at emitEvent or throw
335
+
336
+ // CRITICAL GOTCHA #4: maxMergeDepth is NOT used in this task
337
+ // The maxMergeDepth option exists in ErrorMergeStrategy interface
338
+ // It's intended for future recursive error merging (nested concurrent tasks)
339
+ // This task does NOT implement recursive merging - just flat aggregation
340
+ // Ignore maxMergeDepth for now, will be used in future enhancements
341
+
342
+ // CRITICAL GOTCHA #5: Backward compatibility is MANDATORY
343
+ // When errorMergeStrategy is undefined or enabled is false:
344
+ // - MUST throw rejected[0].reason (first error)
345
+ // - MUST NOT emit error event (already emitted by @Step)
346
+ // When errorMergeStrategy.enabled is true:
347
+ // - MUST throw merged error (not first error)
348
+ // - MUST emit error event with merged error
349
+ // Tests will verify both behaviors work correctly
350
+
351
+ // CRITICAL GOTCHA #6: Type safety for WorkflowError
352
+ // rejected[0].reason is technically `unknown`, not WorkflowError
353
+ // Must cast to WorkflowError: `as WorkflowError`
354
+ // TypeScript won't automatically narrow based on our knowledge of @Step behavior
355
+
356
+ // CRITICAL GOTCHA #7: Runnable workflow context
357
+ // rejected array contains results, not workflow objects
358
+ // To get workflow info (id, name), access via runnable array index
359
+ // Each rejected[i] corresponds to runnable[i]
360
+ // Not needed for default merger (WorkflowError already has workflowId)
361
+
362
+ // CRITICAL GOTCHA #8: Event emission timing
363
+ // Emit error event BEFORE throwing the error
364
+ // This matches @Step decorator pattern (emit then throw)
365
+ // Observers see the error before it propagates up the call stack
366
+ ```
367
+
368
+ ## Implementation Blueprint
369
+
370
+ ### Data Models and Structure
371
+
372
+ **No new data models for this task** - using existing types:
373
+
374
+ * `WorkflowError` from `src/types/error.ts` (error structure)
375
+ * `ErrorMergeStrategy` from `src/types/error-strategy.ts` (configuration)
376
+ * `WorkflowEvent` from `src/types/events.ts` (error event structure)
377
+
378
+ **Inline Default Error Merger** (to be extracted to utils in P1.M2.T2.S3):
379
+
380
+ ```typescript
381
+ // Inline default error merger (will be extracted to src/utils/error-merger.ts in P1.M2.T2.S3)
382
+ // This function creates a merged WorkflowError containing information from all errors
383
+ const defaultErrorMerger = (
384
+ errors: WorkflowError[],
385
+ taskName: string,
386
+ parentWorkflowId: string,
387
+ totalChildren: number
388
+ ): WorkflowError => {
389
+ // Create merged error message
390
+ const message = `${errors.length} of ${totalChildren} concurrent child workflows failed in task '${taskName}'`;
391
+
392
+ // Get all unique workflow IDs that failed
393
+ const failedWorkflowIds = [...new Set(errors.map((e) => e.workflowId))];
394
+
395
+ // Aggregate all logs
396
+ const allLogs = errors.flatMap((e) => e.logs);
397
+
398
+ // Create merged WorkflowError
399
+ const mergedError: WorkflowError = {
400
+ message,
401
+ original: {
402
+ name: 'WorkflowAggregateError',
403
+ message,
404
+ errors,
405
+ totalChildren,
406
+ failedChildren: errors.length,
407
+ failedWorkflowIds,
408
+ } as unknown,
409
+ workflowId: parentWorkflowId,
410
+ stack: errors[0]?.stack, // Use first error's stack trace
411
+ state: errors[0]?.state || {} as SerializedWorkflowState, // Use first error's state
412
+ logs: allLogs,
413
+ };
414
+
415
+ return mergedError;
416
+ };
417
+ ```
418
+
419
+ ### Implementation Tasks (ordered by dependencies)
420
+
421
+ ```yaml
422
+ Task 1: MODIFY src/decorators/task.ts - Add type imports for error aggregation
423
+ - ADD: No new imports needed - WorkflowError already in type imports
424
+ - VERIFY: TaskOptions import includes errorMergeStrategy field
425
+ - DEPENDENCIES: None
426
+
427
+ Task 2: MODIFY src/decorators/task.ts - Add inline default error merger function
428
+ - ADD: defaultErrorMerger function before Task decorator (after line 16)
429
+ - PATTERN: Follow inline helper pattern (similar to type guards)
430
+ - PARAMETERS: (errors: WorkflowError[], taskName: string, workflowId: string, totalChildren: number)
431
+ - RETURN: WorkflowError (merged error with aggregated information)
432
+ - IMPLEMENTATION: See "Inline Default Error Merger" section above
433
+ - DEPENDENCIES: None
434
+
435
+ Task 3: MODIFY src/decorators/task.ts - Replace error throwing logic (lines 118-120)
436
+ - CURRENT CODE:
437
+ if (rejected.length > 0) {
438
+ throw rejected[0].reason;
439
+ }
440
+
441
+ - REPLACE WITH:
442
+ if (rejected.length > 0) {
443
+ // Check if error merge strategy is enabled
444
+ if (opts.errorMergeStrategy?.enabled) {
445
+ // Extract WorkflowError objects from rejected promises
446
+ const errors = rejected.map((r) => r.reason as WorkflowError);
447
+
448
+ // Merge errors using custom combine() or default merger
449
+ const mergedError = opts.errorMergeStrategy?.combine
450
+ ? opts.errorMergeStrategy.combine(errors)
451
+ : defaultErrorMerger(errors, taskName, wf.id, runnable.length);
452
+
453
+ // Emit error event with merged error
454
+ wf.emitEvent({
455
+ type: 'error',
456
+ node: wf.node,
457
+ error: mergedError,
458
+ });
459
+
460
+ // Throw merged error
461
+ throw mergedError;
462
+ }
463
+
464
+ // Backward compatibility: throw first error
465
+ throw rejected[0].reason;
466
+ }
467
+
468
+ - DEPENDENCIES: Task 2
469
+
470
+ Task 4: VERIFY TypeScript compilation
471
+ - RUN: npx tsc --noEmit
472
+ - EXPECTED: No type errors
473
+ - VERIFY: WorkflowError type is available
474
+ - VERIFY: opts.errorMergeStrategy type is correct
475
+ - DEPENDENCIES: Task 3
476
+
477
+ Task 5: RUN existing tests
478
+ - RUN: npm test
479
+ - VERIFY: All existing tests pass (backward compatibility)
480
+ - VERIFY: No behavior changes when errorMergeStrategy not provided
481
+ - DEPENDENCIES: Task 4
482
+
483
+ Task 6: MANUAL TEST - Verify error aggregation works
484
+ - CREATE: Test workflow with errorMergeStrategy enabled
485
+ - VERIFY: Multiple errors are aggregated
486
+ - VERIFY: Merged error contains all error information
487
+ - VERIFY: Error event is emitted with merged error
488
+ - DEPENDENCIES: Task 5
489
+ ```
490
+
491
+ ### Implementation Patterns & Key Details
492
+
493
+ ```typescript
494
+ // ============================================
495
+ // PATTERN 1: Error Merge Strategy Check
496
+ // Location: src/decorators/task.ts lines 118-120
497
+ // ============================================
498
+
499
+ // BEFORE (current):
500
+ if (rejected.length > 0) {
501
+ throw rejected[0].reason;
502
+ }
503
+
504
+ // AFTER (target):
505
+ if (rejected.length > 0) {
506
+ // Check if error merge strategy is enabled
507
+ if (opts.errorMergeStrategy?.enabled) {
508
+ // Error aggregation logic...
509
+ // Emit error event with merged error
510
+ // Throw merged error
511
+ }
512
+
513
+ // Backward compatibility: throw first error
514
+ throw rejected[0].reason;
515
+ }
516
+
517
+ // KEY INSIGHTS:
518
+ // - Optional chaining (?.) safely checks if errorMergeStrategy exists
519
+ // - Check enabled property before aggregating
520
+ // - Fall through to original behavior if not enabled
521
+ // - Maintains exact backward compatibility
522
+
523
+ // ============================================
524
+ // PATTERN 2: Extract WorkflowError Array
525
+ // Location: src/decorators/task.ts inside error merge strategy block
526
+ // ============================================
527
+
528
+ // Extract WorkflowError objects from rejected promises
529
+ const errors = rejected.map((r) => r.reason as WorkflowError);
530
+
531
+ // KEY INSIGHTS:
532
+ // - rejected[].reason is technically `unknown` (Promise rejection type)
533
+ // - Cast to WorkflowError because we know @Step decorator wraps errors
534
+ // - Map operation creates array of WorkflowError
535
+ // - Array is passed to combine() function or default merger
536
+ // - Type assertion is safe because of Groundswell's error wrapping pattern
537
+
538
+ // ============================================
539
+ // PATTERN 3: Custom combine() vs Default Merger
540
+ // Location: src/decorators/task.ts inside error merge strategy block
541
+ // ============================================
542
+
543
+ // Merge errors using custom combine() or default merger
544
+ const mergedError = opts.errorMergeStrategy?.combine
545
+ ? opts.errorMergeStrategy.combine(errors)
546
+ : defaultErrorMerger(errors, taskName, wf.id, runnable.length);
547
+
548
+ // KEY INSIGHTS:
549
+ // - Ternary operator chooses between custom and default merger
550
+ // - opts.errorMergeStrategy?.combine is optional function
551
+ // - If provided, call it with errors array
552
+ // - If not provided, use inline default error merger
553
+ // - Both return WorkflowError object
554
+ // - User has full control over error merging when providing combine()
555
+
556
+ // ============================================
557
+ // PATTERN 4: Default Error Merger Implementation
558
+ // Location: src/decorators/task.ts (inline function before Task decorator)
559
+ // ============================================
560
+
561
+ const defaultErrorMerger = (
562
+ errors: WorkflowError[],
563
+ taskName: string,
564
+ parentWorkflowId: string,
565
+ totalChildren: number
566
+ ): WorkflowError => {
567
+ // Create merged error message
568
+ const message = `${errors.length} of ${totalChildren} concurrent child workflows failed in task '${taskName}'`;
569
+
570
+ // Get all unique workflow IDs that failed
571
+ const failedWorkflowIds = [...new Set(errors.map((e) => e.workflowId))];
572
+
573
+ // Aggregate all logs
574
+ const allLogs = errors.flatMap((e) => e.logs);
575
+
576
+ // Create merged WorkflowError
577
+ const mergedError: WorkflowError = {
578
+ message,
579
+ original: {
580
+ name: 'WorkflowAggregateError',
581
+ message,
582
+ errors,
583
+ totalChildren,
584
+ failedChildren: errors.length,
585
+ failedWorkflowIds,
586
+ } as unknown,
587
+ workflowId: parentWorkflowId,
588
+ stack: errors[0]?.stack,
589
+ state: errors[0]?.state || {} as SerializedWorkflowState,
590
+ logs: allLogs,
591
+ };
592
+
593
+ return mergedError;
594
+ };
595
+
596
+ // KEY INSIGHTS:
597
+ // - Message is descriptive (counts, task name, parent workflow)
598
+ // - Aggregates all logs from all errors (flatMap)
599
+ // - Stores aggregated metadata in original field (as unknown for flexibility)
600
+ // - Uses first error's stack trace (representative stack)
601
+ // - Uses first error's state (representative state)
602
+ // - Returns valid WorkflowError object (can be emitted in error event)
603
+
604
+ // ============================================
605
+ // PATTERN 5: Error Event Emission with Merged Error
606
+ // Location: src/decorators/task.ts inside error merge strategy block
607
+ // ============================================
608
+
609
+ // Emit error event with merged error
610
+ wf.emitEvent({
611
+ type: 'error',
612
+ node: wf.node,
613
+ error: mergedError,
614
+ });
615
+
616
+ // KEY INSIGHTS:
617
+ // - Emit BEFORE throwing (matches @Step decorator pattern)
618
+ // - Single error event for ALL failures (not one per failure)
619
+ // - Observers receive aggregated error information
620
+ // - Enables monitoring of aggregate failure scenarios
621
+ // - Only emitted when errorMergeStrategy.enabled === true
622
+ // - When not enabled, individual workflow error events already emitted by @Step
623
+
624
+ // ============================================
625
+ // PATTERN 6: Throw Merged Error
626
+ // Location: src/decorators/task.ts after error event emission
627
+ // ============================================
628
+
629
+ // Throw merged error
630
+ throw mergedError;
631
+
632
+ // KEY INSIGHTS:
633
+ // - Throw AFTER emitting event (observers see it before propagation)
634
+ // - Thrown error is WorkflowError (same type as individual errors)
635
+ // - Caller can check if it's an aggregate by inspecting original field
636
+ // - Maintains error type consistency across the codebase
637
+ // - Stack trace will point to this location (expected behavior)
638
+
639
+ // ============================================
640
+ // PATTERN 7: Backward Compatibility Fallthrough
641
+ // Location: src/decorators/task.ts after error merge strategy block
642
+ // ============================================
643
+
644
+ // Backward compatibility: throw first error
645
+ throw rejected[0].reason;
646
+
647
+ // KEY INSIGHTS:
648
+ // - This code runs when errorMergeStrategy is undefined or enabled === false
649
+ // - Maintains EXACT same behavior as before this change
650
+ // - First error is thrown (fail-fast behavior)
651
+ // - No error event emitted here (individual events already emitted by @Step)
652
+ // - Existing tests verify this behavior is preserved
653
+ ```
654
+
655
+ ### Integration Points
656
+
657
+ ```yaml
658
+ TASK_DECORATOR:
659
+ - modify: src/decorators/task.ts
660
+ lines: 118-120 (error throwing logic)
661
+ add: errorMergeStrategy check, error aggregation, merged error event
662
+
663
+ - add: inline defaultErrorMerger function (before Task decorator, after line 16)
664
+
665
+ TASK_OPTIONS:
666
+ - reference: src/types/decorators.ts
667
+ field: errorMergeStrategy?: ErrorMergeStrategy
668
+ note: Already added in P1.M2.T2.S1
669
+
670
+ - access: opts.errorMergeStrategy?.enabled
671
+ - access: opts.errorMergeStrategy?.combine
672
+
673
+ ERROR_TYPES:
674
+ - reference: src/types/error.ts
675
+ interface: WorkflowError
676
+ fields: message, original, workflowId, stack, state, logs
677
+
678
+ - cast: rejected[].reason as WorkflowError
679
+ - return: WorkflowError from combine() and defaultErrorMerger()
680
+
681
+ EVENT_SYSTEM:
682
+ - emit: wf.emitEvent({ type: 'error', node: wf.node, error: mergedError })
683
+ - note: Only when errorMergeStrategy.enabled === true
684
+ - note: Single event for all failures (not per-failure)
685
+
686
+ FUTURE_INTEGRATIONS (P1.M2.T2.S3):
687
+ - extract: defaultErrorMerger to src/utils/error-merger.ts
688
+ - add: more sophisticated error aggregation patterns
689
+ - add: pretty printing and statistics generation
690
+
691
+ FUTURE_INTEGRATIONS (P1.M2.T2.S4):
692
+ - add: comprehensive tests for error aggregation
693
+ - add: tests for custom combine() function
694
+ - add: tests for default error merger
695
+ - add: tests for backward compatibility
696
+ ```
697
+
698
+ ## Validation Loop
699
+
700
+ ### Level 1: Syntax & Style (Immediate Feedback)
701
+
702
+ ```bash
703
+ # After modifying src/decorators/task.ts, run these checks
704
+
705
+ # TypeScript type checking
706
+ npx tsc --noEmit
707
+
708
+ # Expected: Zero type errors
709
+ # Common errors to fix:
710
+ # - "Property 'errorMergeStrategy' does not exist" → Check TaskOptions import
711
+ # - "Cannot cast to WorkflowError" → Check error type casting
712
+ # - "Property 'combine' does not exist" → Check optional chaining
713
+
714
+ # If available, run linter
715
+ npm run lint 2>/dev/null || echo "No linter configured"
716
+ ```
717
+
718
+ ### Level 2: Unit Tests (Component Validation)
719
+
720
+ ```bash
721
+ # Run all existing tests to verify backward compatibility
722
+ npm test
723
+
724
+ # Run specific concurrent execution tests
725
+ npm test -- src/__tests__/adversarial/concurrent-task-failures.test.ts
726
+ npm test -- src/__tests__/adversarial/edge-case.test.ts
727
+
728
+ # Expected: All existing tests pass without modification
729
+ # If tests fail, debug - should maintain exact backward compatibility
730
+ ```
731
+
732
+ ### Level 3: Integration Testing (System Validation)
733
+
734
+ ```bash
735
+ # Create manual test to verify error aggregation
736
+ cat > /tmp/test_error_merge.js << 'EOF'
737
+ // Manual test for error merge strategy
738
+ import { Workflow, Task, Step } from './src/index.js';
739
+
740
+ class FailingWorkflow extends Workflow {
741
+ constructor(id: string, parent?: Workflow) {
742
+ super(id, parent);
743
+ }
744
+
745
+ @Step()
746
+ async run() {
747
+ throw new Error(`Failure in ${this.id}`);
748
+ }
749
+ }
750
+
751
+ class ParentWorkflow extends Workflow {
752
+ constructor(id: string, useMergeStrategy: boolean = true) {
753
+ super(id);
754
+ this.useMergeStrategy = useMergeStrategy;
755
+ }
756
+
757
+ private useMergeStrategy: boolean;
758
+
759
+ @Task({
760
+ concurrent: true,
761
+ errorMergeStrategy: this.useMergeStrategy ? {
762
+ enabled: true,
763
+ // Custom combine function
764
+ combine: (errors) => ({
765
+ message: `Custom merge: ${errors.length} workflows failed`,
766
+ original: errors,
767
+ workflowId: 'parent',
768
+ logs: errors.flatMap(e => e.logs),
769
+ }),
770
+ } : undefined,
771
+ })
772
+ async spawnFailingChildren() {
773
+ return [
774
+ new FailingWorkflow('child1', this),
775
+ new FailingWorkflow('child2', this),
776
+ new FailingWorkflow('child3', this),
777
+ ];
778
+ }
779
+
780
+ async run() {
781
+ return this.spawnFailingChildren();
782
+ }
783
+ }
784
+
785
+ async function test() {
786
+ console.log('=== Test 1: With Error Merge Strategy ===');
787
+ const parent1 = new ParentWorkflow('parent1', true);
788
+ try {
789
+ await parent1.run();
790
+ } catch (error) {
791
+ console.log('Caught merged error:', error.message);
792
+ console.log('Original field contains errors array:', Array.isArray(error.original));
793
+ console.log('Number of errors:', error.original?.length || 0);
794
+ }
795
+
796
+ console.log('\n=== Test 2: Without Error Merge Strategy (Backward Compat) ===');
797
+ const parent2 = new ParentWorkflow('parent2', false);
798
+ try {
799
+ await parent2.run();
800
+ } catch (error) {
801
+ console.log('Caught first error:', error.message);
802
+ console.log('Is WorkflowError:', 'workflowId' in error);
803
+ }
804
+ }
805
+
806
+ test();
807
+ EOF
808
+
809
+ node /tmp/test_error_merge.js
810
+
811
+ # Expected output:
812
+ # Test 1: Shows custom merged error with all 3 errors
813
+ # Test 2: Shows first error only (backward compatible)
814
+
815
+ # Clean up
816
+ rm /tmp/test_error_merge.js
817
+ ```
818
+
819
+ ### Level 4: Domain-Specific Validation
820
+
821
+ ```bash
822
+ # Test default error merger (without custom combine)
823
+ cat > /tmp/test_default_merger.js << 'EOF'
824
+ import { Workflow, Task, Step } from './src/index.js';
825
+
826
+ class FailingWorkflow extends Workflow {
827
+ @Step()
828
+ async run() {
829
+ throw new Error(`Failed in ${this.id}`);
830
+ }
831
+ }
832
+
833
+ class ParentWorkflow extends Workflow {
834
+ @Task({
835
+ concurrent: true,
836
+ errorMergeStrategy: { enabled: true }, // No combine function - use default
837
+ })
838
+ async spawnChildren() {
839
+ return [
840
+ new FailingWorkflow('child1', this),
841
+ new FailingWorkflow('child2', this),
842
+ ];
843
+ }
844
+
845
+ async run() {
846
+ return this.spawnChildren();
847
+ }
848
+ }
849
+
850
+ async function test() {
851
+ const parent = new ParentWorkflow('parent');
852
+ try {
853
+ await parent.run();
854
+ } catch (error) {
855
+ console.log('Default merged error message:', error.message);
856
+ console.log('Original field:', JSON.stringify(error.original, null, 2));
857
+ }
858
+ }
859
+
860
+ test();
861
+ EOF
862
+
863
+ node /tmp/test_default_merger.js
864
+
865
+ # Expected: Default merger creates proper WorkflowError with:
866
+ # - Descriptive message with counts
867
+ # - Original field containing errors array
868
+ # - All logs aggregated
869
+ ```
870
+
871
+ ## Final Validation Checklist
872
+
873
+ ### Technical Validation
874
+
875
+ * [ ] TypeScript compilation succeeds: `npx tsc --noEmit`
876
+ * [ ] defaultErrorMerger function returns valid WorkflowError
877
+ * [ ] opts.errorMergeStrategy?.enabled check uses optional chaining
878
+ * [ ] errors array correctly cast to WorkflowError[]
879
+ * [ ] Custom combine() called when provided
880
+ * [ ] Default merger used when combine() not provided
881
+ * [ ] Error event emitted with merged error
882
+ * [ ] Merged error thrown instead of first error when enabled
883
+
884
+ ### Backward Compatibility Validation
885
+
886
+ * [ ] When errorMergeStrategy is undefined: first error thrown
887
+ * [ ] When errorMergeStrategy.enabled is false: first error thrown
888
+ * [ ] When errorMergeStrategy.enabled is true: merged error thrown
889
+ * [ ] No error event emitted when merge strategy not enabled
890
+ * [ ] All existing tests pass without modification
891
+
892
+ ### Feature Validation
893
+
894
+ * [ ] Multiple concurrent workflow errors are aggregated
895
+ * [ ] Custom combine() function receives WorkflowError array
896
+ * [ ] Custom combine() return value is used as merged error
897
+ * [ ] Default merger creates WorkflowError with aggregated information
898
+ * [ ] Merged error message includes failure count
899
+ * [ ] All logs from all errors are aggregated
900
+ * [ ] Error event contains merged error
901
+
902
+ ### Code Quality Validation
903
+
904
+ * [ ] Follows existing code patterns (indentation, naming, style)
905
+ * [ ] No console.log or debug statements left in
906
+ * [ ] Error handling is specific (throwing exact error, not new wrapper)
907
+ * [ ] Type safety maintained (all casts are safe)
908
+ * [ ] Comments added for clarity (optional chaining, backward compatibility)
909
+
910
+ ### Documentation & Deployment
911
+
912
+ * [ ] Code is self-documenting with clear variable names
913
+ * [ ] Inline comments explain error merge strategy logic
914
+ * [ ] Backward compatibility is clearly documented in comments
915
+ * [ ] No breaking changes to public API
916
+
917
+ ## Anti-Patterns to Avoid
918
+
919
+ * ❌ **Don't create new WorkflowError objects** - rejected[].reason is already WorkflowError
920
+ * ❌ **Don't emit error events for individual failures** - @Step decorator already does this
921
+ * ❌ **Don't throw error without emitting event** - Must emit before throw (pattern consistency)
922
+ * ❌ **Don't ignore combine() return value** - User's custom function must be used when provided
923
+ * ❌ **Don't assume combine() returns WorkflowError** - Trust user's implementation, TypeScript validates
924
+ * ❌ **Don't aggregate when errorMergeStrategy is undefined** - Must check both exists AND enabled
925
+ * ❌ **Don't break backward compatibility** - Default behavior must be unchanged
926
+ * ❌ **Don't use maxMergeDepth in this task** - Future enhancement, ignore for now
927
+ * ❌ **Don't add try-catch around combine()** - Let user errors propagate naturally
928
+ * ❌ **Don't create new files** - This task only modifies task.ts (P1.M2.T2.S3 adds utility file)
929
+
930
+ ## Success Metrics
931
+
932
+ **Confidence Score**: 9/10 for one-pass implementation success
933
+
934
+ **Definition of Done**:
935
+
936
+ 1. src/decorators/task.ts implements error aggregation when errorMergeStrategy.enabled === true
937
+ 2. Custom combine() function is called when provided
938
+ 3. Default error merger is used when combine() is not provided
939
+ 4. Merged error is thrown instead of first error when enabled
940
+ 5. Error event is emitted with merged error
941
+ 6. Backward compatibility maintained (first error thrown when not enabled)
942
+ 7. All existing tests pass
943
+ 8. Zero TypeScript type errors
944
+
945
+ **Validation**: The implementation enables configurable error aggregation for concurrent workflows while maintaining complete backward compatibility.
946
+
947
+ ---
948
+
949
+ ## Appendix: Complete Code Change Reference
950
+
951
+ ### Exact Change Required (src/decorators/task.ts)
952
+
953
+ **Add after line 16 (before Task decorator):**
954
+
955
+ ```typescript
956
+ // Default error merger for concurrent workflow failures
957
+ // This will be extracted to src/utils/error-merger.ts in P1.M2.T2.S3
958
+ const defaultErrorMerger = (
959
+ errors: WorkflowError[],
960
+ taskName: string,
961
+ parentWorkflowId: string,
962
+ totalChildren: number
963
+ ): WorkflowError => {
964
+ // Create merged error message
965
+ const message = `${errors.length} of ${totalChildren} concurrent child workflows failed in task '${taskName}'`;
966
+
967
+ // Get all unique workflow IDs that failed
968
+ const failedWorkflowIds = [...new Set(errors.map((e) => e.workflowId))];
969
+
970
+ // Aggregate all logs
971
+ const allLogs = errors.flatMap((e) => e.logs);
972
+
973
+ // Create merged WorkflowError
974
+ const mergedError: WorkflowError = {
975
+ message,
976
+ original: {
977
+ name: 'WorkflowAggregateError',
978
+ message,
979
+ errors,
980
+ totalChildren,
981
+ failedChildren: errors.length,
982
+ failedWorkflowIds,
983
+ } as unknown,
984
+ workflowId: parentWorkflowId,
985
+ stack: errors[0]?.stack,
986
+ state: errors[0]?.state || {} as SerializedWorkflowState,
987
+ logs: allLogs,
988
+ };
989
+
990
+ return mergedError;
991
+ };
992
+ ```
993
+
994
+ **Replace lines 118-120:**
995
+
996
+ ```typescript
997
+ // BEFORE:
998
+ if (rejected.length > 0) {
999
+ throw rejected[0].reason;
1000
+ }
1001
+
1002
+ // AFTER:
1003
+ if (rejected.length > 0) {
1004
+ // Check if error merge strategy is enabled
1005
+ if (opts.errorMergeStrategy?.enabled) {
1006
+ // Extract WorkflowError objects from rejected promises
1007
+ const errors = rejected.map((r) => r.reason as WorkflowError);
1008
+
1009
+ // Merge errors using custom combine() or default merger
1010
+ const mergedError = opts.errorMergeStrategy?.combine
1011
+ ? opts.errorMergeStrategy.combine(errors)
1012
+ : defaultErrorMerger(errors, taskName, wf.id, runnable.length);
1013
+
1014
+ // Emit error event with merged error
1015
+ wf.emitEvent({
1016
+ type: 'error',
1017
+ node: wf.node,
1018
+ error: mergedError,
1019
+ });
1020
+
1021
+ // Throw merged error
1022
+ throw mergedError;
1023
+ }
1024
+
1025
+ // Backward compatibility: throw first error
1026
+ throw rejected[0].reason;
1027
+ }
1028
+ ```
1029
+
1030
+ **Diff Summary:**
1031
+
1032
+ ```diff
1033
+ + // Default error merger for concurrent workflow failures
1034
+ + const defaultErrorMerger = (...)
1035
+ +
1036
+ if (rejected.length > 0) {
1037
+ + // Check if error merge strategy is enabled
1038
+ + if (opts.errorMergeStrategy?.enabled) {
1039
+ + // Extract WorkflowError objects from rejected promises
1040
+ + const errors = rejected.map((r) => r.reason as WorkflowError);
1041
+ +
1042
+ + // Merge errors using custom combine() or default merger
1043
+ + const mergedError = opts.errorMergeStrategy?.combine
1044
+ + ? opts.errorMergeStrategy.combine(errors)
1045
+ + : defaultErrorMerger(errors, taskName, wf.id, runnable.length);
1046
+ +
1047
+ + // Emit error event with merged error
1048
+ + wf.emitEvent({
1049
+ + type: 'error',
1050
+ + node: wf.node,
1051
+ + error: mergedError,
1052
+ + });
1053
+ +
1054
+ + // Throw merged error
1055
+ + throw mergedError;
1056
+ + }
1057
+ +
1058
+ // Backward compatibility: throw first error
1059
+ throw rejected[0].reason;
1060
+ }
1061
+ ```
1062
+
1063
+ ---
1064
+
1065
+ **PRP Status**: ✅ Complete - Ready for Implementation
1066
+ **Next Task**: P1.M2.T2.S3 - Create default error merger implementation