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,1252 @@
1
+ # Product Requirement Prompt (PRP): Add Tests for ErrorMergeStrategy Functionality
2
+
3
+ **Work Item:** P1.M2.T2.S4 - Add tests for ErrorMergeStrategy functionality
4
+
5
+ ---
6
+
7
+ ## Goal
8
+
9
+ **Feature Goal:** Create a comprehensive test suite for the ErrorMergeStrategy functionality implemented in P1.M2.T2.S2 and P1.M2.T2.S3, validating that error aggregation works correctly for concurrent task execution scenarios.
10
+
11
+ **Deliverable:** A new test file `src/__tests__/adversarial/error-merge-strategy.test.ts` containing comprehensive tests for:
12
+ - `errorMergeStrategy.enabled=false` (default behavior - first error wins)
13
+ - `errorMergeStrategy.enabled=true` with default merge (uses `mergeWorkflowErrors`)
14
+ - `errorMergeStrategy.enabled=true` with custom combine function
15
+ - `maxMergeDepth` validation (if implemented)
16
+ - Merged error contains aggregated information from all failures
17
+
18
+ **Success Definition:**
19
+ 1. New test file `src/__tests__/adversarial/error-merge-strategy.test.ts` created
20
+ 2. All test scenarios from the contract definition are covered
21
+ 3. Tests use vitest framework with `describe`, `it`, `expect` patterns matching existing codebase
22
+ 4. Tests verify error event emission with merged errors
23
+ 5. All new tests pass
24
+ 6. All existing tests continue to pass (no regressions)
25
+ 7. Tests follow the project's test organization structure
26
+
27
+ ## User Persona (if applicable)
28
+
29
+ **Target User:** Library Developer / QA Engineer
30
+
31
+ **Use Case:** A developer needs confidence that the ErrorMergeStrategy feature works correctly across all scenarios (enabled/disabled, default/custom merger, edge cases).
32
+
33
+ **User Journey:**
34
+ 1. Developer runs the test suite to verify ErrorMergeStrategy functionality
35
+ 2. Tests cover all code paths in the error aggregation logic
36
+ 3. Test failures clearly indicate what functionality is broken
37
+ 4. Tests serve as documentation for expected behavior
38
+
39
+ **Pain Points Addressed:**
40
+ - Currently no tests verify `errorMergeStrategy` configuration works
41
+ - No coverage for custom `combine()` function scenarios
42
+ - No validation that merged errors contain aggregated information
43
+ - Unclear what happens when `enabled=false` vs `enabled=true`
44
+
45
+ ## Why
46
+
47
+ - **Quality Assurance:** P1.M2.T2.S2 implemented error aggregation logic, P1.M2.T2.S3 extracted the merger utility - both need comprehensive test coverage
48
+ - **Regression Prevention:** Future changes to concurrent execution could break error aggregation - tests prevent this
49
+ - **Documentation:** Tests serve as executable documentation of expected ErrorMergeStrategy behavior
50
+ - **PRD Compliance:** PRD Section 10 specifies "Optional Multi-Error Merging" with specific behaviors that need validation
51
+ - **Confidence:** Without tests, we cannot verify the implementation works as designed
52
+
53
+ ## What
54
+
55
+ Create comprehensive tests for ErrorMergeStrategy functionality covering all scenarios specified in the contract definition.
56
+
57
+ ### Test Scenarios Required
58
+
59
+ Based on contract definition and PRD requirements:
60
+
61
+ 1. **Default Behavior (disabled)**: `errorMergeStrategy.enabled=false` → first error thrown
62
+ 2. **Enabled with Default Merge**: `errorMergeStrategy.enabled=true` without `combine()` → uses `mergeWorkflowErrors`
63
+ 3. **Enabled with Custom Combine**: `errorMergeStrategy.enabled=true` with `combine()` function → custom merger called
64
+ 4. **maxMergeDepth Validation**: If implemented, verify depth limiting works
65
+ 5. **Aggregated Information**: Verify merged error contains all error details
66
+
67
+ ### Success Criteria
68
+
69
+ - [ ] Test file created at `src/__tests__/adversarial/error-merge-strategy.test.ts`
70
+ - [ ] Tests for `enabled=false` default behavior (first error wins)
71
+ - [ ] Tests for `enabled=true` with default merge
72
+ - [ ] Tests for `enabled=true` with custom combine function
73
+ - [ ] Tests for maxMergeDepth if implemented in P1.M2.T2.S2
74
+ - [ ] Tests verify merged error contains aggregated information
75
+ - [ ] All tests pass: `npm test -- error-merge-strategy.test.ts`
76
+ - [ ] All existing tests pass: `npm test` (no regressions)
77
+ - [ ] Test coverage for error merge strategy code paths > 90%
78
+
79
+ ---
80
+
81
+ ## All Needed Context
82
+
83
+ ### Context Completeness Check
84
+
85
+ **"No Prior Knowledge" Test**: If someone knew nothing about this codebase, would they have everything needed to implement this successfully?
86
+
87
+ **Answer**: YES - This PRP provides:
88
+ - Exact file locations for implementation files to test
89
+ - Complete existing test patterns to follow
90
+ - Helper function patterns from existing tests
91
+ - Test structure and organization patterns
92
+ - All type definitions needed
93
+ - Validation commands specific to this project
94
+
95
+ ### Documentation & References
96
+
97
+ ```yaml
98
+ # MUST READ - Implementation Files to Test
99
+ - file: src/decorators/task.ts (lines 119-143)
100
+ why: Contains the error merge strategy implementation to test
101
+ critical: This is the main code under test - shows how errorMergeStrategy is checked and used
102
+ gotcha: Error merge only happens when opts.concurrent=true AND opts.errorMergeStrategy?.enabled=true
103
+
104
+ - file: src/utils/workflow-error-utils.ts
105
+ why: Contains mergeWorkflowErrors function used as default merger
106
+ pattern: Follow existing test patterns from workflow-error-utils.test.ts
107
+ critical: Default merger behavior must match this function's implementation
108
+
109
+ - file: src/types/error-strategy.ts
110
+ why: ErrorMergeStrategy interface definition
111
+ current_content: |
112
+ export interface ErrorMergeStrategy {
113
+ enabled: boolean;
114
+ maxMergeDepth?: number;
115
+ combine?(errors: WorkflowError[]): WorkflowError;
116
+ }
117
+
118
+ - file: src/types/decorators.ts
119
+ why: TaskOptions interface with errorMergeStrategy field
120
+ current_content: |
121
+ export interface TaskOptions {
122
+ name?: string;
123
+ concurrent?: boolean;
124
+ errorMergeStrategy?: ErrorMergeStrategy;
125
+ }
126
+
127
+ - file: src/types/error.ts
128
+ why: WorkflowError interface structure - what errors look like
129
+ critical: Tests must verify WorkflowError structure is correct
130
+
131
+ # MUST READ - Existing Test Patterns to Follow
132
+ - file: src/__tests__/adversarial/concurrent-task-failures.test.ts
133
+ why: Shows test patterns for concurrent task error scenarios
134
+ pattern: createChildWorkflow helper, setupEventObserver helper, @Task decorator usage
135
+ critical: Follow this exact pattern for test structure and helper functions
136
+
137
+ - file: src/__tests__/unit/utils/workflow-error-utils.test.ts
138
+ why: Shows test patterns for mergeWorkflowErrors function
139
+ pattern: createMockWorkflowError helper, metadata validation assertions
140
+ critical: Use similar assertion patterns for validating merged errors
141
+
142
+ - file: src/__tests__/unit/decorators.test.ts (lines 1-100)
143
+ why: Shows basic @Task decorator test patterns
144
+ pattern: Workflow class extension, decorator usage, async test patterns
145
+
146
+ # EXTERNAL RESEARCH - Vitest Testing Patterns
147
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/research/vitest_testing_patterns.md
148
+ why: Comprehensive vitest testing patterns for error handling
149
+ section: "Testing Error Aggregation Behavior", "Async Error Testing Best Practices"
150
+ critical: |
151
+ - await expect().rejects.toThrow() for async error testing
152
+ - try-catch for detailed error inspection
153
+ - Event observer setup pattern
154
+ - Promise.allSettled testing patterns
155
+
156
+ # ARCHITECTURE DOCUMENTATION
157
+ - file: plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md (lines 269-287)
158
+ why: Testing strategy section - shows how tests are organized in this codebase
159
+ section: "## 9. Testing Strategy"
160
+ critical: Tests organized into unit/, integration/, and adversarial/ directories
161
+
162
+ - file: plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md (lines 147-217)
163
+ why: Shows recommended implementation pattern for error aggregation
164
+ section: "## 5. Recommended Pattern for Promise.allSettled with Error Aggregation"
165
+ critical: Understanding the implementation helps write accurate tests
166
+
167
+ # PREVIOUS WORK - PRPs for Related Tasks
168
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md
169
+ why: PRP for default error merger implementation
170
+ section: "Implementation Blueprint" shows mergeWorkflowErrors specification
171
+ critical: Tests must verify this exact merge behavior
172
+
173
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md
174
+ why: PRP for error aggregation logic in @Task decorator
175
+ section: "Implementation Blueprint" shows error merge strategy check logic
176
+ critical: Tests must verify opts.errorMergeStrategy?.enabled check works
177
+
178
+ # PRD REQUIREMENTS
179
+ - docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md (lines 72-90)
180
+ why: Issue 3 - Missing Error Merge Strategy Implementation
181
+ section: "### Issue 3: Missing Error Merge Strategy Implementation"
182
+ critical: PRD specifies disabled by default, maxMergeDepth for recursion, combine() for custom logic
183
+ ```
184
+
185
+ ### Current Codebase Tree (relevant portions)
186
+
187
+ ```bash
188
+ /home/dustin/projects/groundswell/
189
+ ├── src/
190
+ │ ├── decorators/
191
+ │ │ └── task.ts # Lines 119-143 - error merge strategy implementation
192
+ │ ├── utils/
193
+ │ │ └── workflow-error-utils.ts # mergeWorkflowErrors function
194
+ │ ├── types/
195
+ │ │ ├── error-strategy.ts # ErrorMergeStrategy interface
196
+ │ │ ├── error.ts # WorkflowError interface
197
+ │ │ └── decorators.ts # TaskOptions interface
198
+ │ └── __tests__/
199
+ │ ├── adversarial/
200
+ │ │ ├── concurrent-task-failures.test.ts # Pattern to follow
201
+ │ │ ├── edge-case.test.ts # Additional patterns
202
+ │ │ └── error-merge-strategy.test.ts # NEW - TO BE CREATED
203
+ │ └── unit/
204
+ │ ├── decorators.test.ts # @Task decorator tests
205
+ │ └── utils/
206
+ │ └── workflow-error-utils.test.ts # mergeWorkflowErrors tests
207
+ ├── vitest.config.ts # Test configuration
208
+ └── package.json # Test scripts
209
+ ```
210
+
211
+ ### Desired Codebase Tree (files to be added)
212
+
213
+ ```bash
214
+ # NEW FILE:
215
+ src/__tests__/adversarial/error-merge-strategy.test.ts # Comprehensive ErrorMergeStrategy tests
216
+
217
+ # NO FILES MODIFIED - This is a test-only task
218
+ ```
219
+
220
+ ### Known Gotchas of Our Codebase & Library Quirks
221
+
222
+ ```typescript
223
+ // CRITICAL GOTCHA #1: Error merge strategy only works with concurrent=true
224
+ // The opts.errorMergeStrategy check is INSIDE the opts.concurrent block
225
+ // Location: src/decorators/task.ts lines 106-143
226
+ // IMPLICATION: Tests MUST use @Task({ concurrent: true, errorMergeStrategy: {...} })
227
+ // BAD: @Task({ errorMergeStrategy: { enabled: true } }) // concurrent defaults to false
228
+ // GOOD: @Task({ concurrent: true, errorMergeStrategy: { enabled: true } })
229
+
230
+ // CRITICAL GOTCHA #2: Individual workflow error events already emitted by @Step
231
+ // Each failing workflow emits its own error event BEFORE @Task aggregates them
232
+ // When errorMergeStrategy.enabled=true, @Task emits ONE additional error event with merged error
233
+ // When errorMergeStrategy not enabled, @Task does NOT emit additional error event
234
+ // IMPLICATION: Tests should count total error events = individual errors + (1 if enabled else 0)
235
+
236
+ // CRITICAL GOTCHA #3: WorkflowError.original contains metadata object
237
+ // For merged errors, original field has: { name, message, errors, totalChildren, failedChildren, failedWorkflowIds }
238
+ // Tests should verify this metadata structure
239
+ // PATTERN: (result.original as { failedWorkflowIds: string[] }).failedWorkflowIds
240
+
241
+ // CRITICAL GOTCHA #4: mergeWorkflowErrors signature
242
+ // mergeWorkflowErrors(errors: WorkflowError[], taskName: string, parentWorkflowId: string, totalChildren: number)
243
+ // The decorator calls this with: mergeWorkflowErrors(errors, taskName, wf.id, runnable.length)
244
+ // Tests should verify the message format includes all these parameters
245
+
246
+ // CRITICAL GOTCHA #5: First error wins when disabled
247
+ // When errorMergeStrategy is undefined or enabled=false, decorator throws rejected[0].reason
248
+ // This is the FIRST rejected promise, which may not be deterministic in concurrent execution
249
+ // Tests should use try-catch and verify error structure, not exact error message
250
+
251
+ // CRITICAL GOTCHA #6: Custom combine() can return any WorkflowError
252
+ // User controls the merge behavior when providing combine() function
253
+ // Tests should verify combine() was called, not specific merge behavior
254
+ // Use vi.fn() spy to verify combine() was called with correct arguments
255
+
256
+ // CRITICAL GOTCHA #7: Workflow completion vs error propagation
257
+ // Promise.allSettled ensures ALL workflows complete even when some fail
258
+ // The error is thrown AFTER all workflows complete (fulfilled or rejected)
259
+ // Tests should verify parent.children.length equals total children spawned
260
+
261
+ // CRITICAL GOTCHA #8: Test file location matters
262
+ // This is an adversarial test (edge cases, complex scenarios)
263
+ // Place in src/__tests__/adversarial/ not src/__tests__/unit/
264
+
265
+ // CRITICAL GOTCHA #9: Import paths use .js extensions
266
+ // TypeScript ES modules require .js extensions in import statements
267
+ // import { Workflow } from '../../index.js'; // Note .js extension
268
+
269
+ // CRITICAL GOTCHA #10: maxMergeDepth is NOT currently implemented
270
+ // The interface has maxMergeDepth?: number but it's not used in P1.M2.T2.S2 implementation
271
+ // Tests should note this as "future work" or "not yet implemented"
272
+ // Don't write failing tests for unimplemented features
273
+
274
+ // CRITICAL GOTCHA #11: Event observer must be added BEFORE workflow.run()
275
+ // If observer is added after run(), events emitted during run() won't be captured
276
+ // PATTERN: setup observer, then await workflow.run()
277
+
278
+ // CRITICAL GOTCHA #12: Helper function pattern for creating failing workflows
279
+ // Use the createChildWorkflow pattern from concurrent-task-failures.test.ts
280
+ // It creates anonymous class extending Workflow with @Step decorated method
281
+ // This ensures WorkflowError wrapping happens correctly
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Implementation Blueprint
287
+
288
+ ### Data Models and Structure
289
+
290
+ **No new data models** - using existing types:
291
+
292
+ ```typescript
293
+ // From src/types/error-strategy.ts
294
+ export interface ErrorMergeStrategy {
295
+ enabled: boolean;
296
+ maxMergeDepth?: number;
297
+ combine?(errors: WorkflowError[]): WorkflowError;
298
+ }
299
+
300
+ // From src/types/error.ts
301
+ export interface WorkflowError {
302
+ message: string;
303
+ original: unknown;
304
+ workflowId: string;
305
+ stack?: string;
306
+ state: SerializedWorkflowState;
307
+ logs: LogEntry[];
308
+ }
309
+
310
+ // Merged error metadata structure (in original field)
311
+ interface MergedErrorMetadata {
312
+ name: string; // 'WorkflowAggregateError'
313
+ message: string; // Aggregated message
314
+ errors: WorkflowError[]; // All original errors
315
+ totalChildren: number; // Total spawned
316
+ failedChildren: number; // How many failed
317
+ failedWorkflowIds: string[]; // Unique workflow IDs
318
+ }
319
+ ```
320
+
321
+ ### Implementation Tasks (ordered by dependencies)
322
+
323
+ ```yaml
324
+ Task 1: CREATE src/__tests__/adversarial/error-merge-strategy.test.ts
325
+ - IMPLEMENT: Test file scaffolding with imports and describe blocks
326
+ - IMPORT from: vitest (describe, it, expect), Workflow/Task/Step from '../../index.js', WorkflowEvent from '../../types/index.js'
327
+ - STRUCTURE: Follow pattern from concurrent-task-failures.test.ts
328
+ - NAMING: describe('@Task decorator ErrorMergeStrategy', () => { ... })
329
+ - PLACEMENT: src/__tests__/adversarial/ directory
330
+
331
+ Task 2: IMPLEMENT helper functions (top of test file)
332
+ - FUNCTION: createChildWorkflow(parent, name, shouldFail) - creates failing/succeeding workflow
333
+ - PATTERN: Copy from concurrent-task-failures.test.ts lines 30-52
334
+ - FUNCTION: setupEventObserver(workflow) - returns events array
335
+ - PATTERN: Copy from concurrent-task-failures.test.ts lines 58-67
336
+ - FUNCTION: createMockWorkflowError(overrides) - for custom combine() tests
337
+ - PATTERN: Copy from workflow-error-utils.test.ts lines 7-25
338
+
339
+ Task 3: IMPLEMENT tests for enabled=false (default behavior)
340
+ - DESCRIBE: 'Default behavior (errorMergeStrategy disabled)'
341
+ - TEST: 'should throw first error when errorMergeStrategy not provided'
342
+ - TEST: 'should throw first error when errorMergeStrategy.enabled=false'
343
+ - VERIFY: Only first error thrown, not aggregated
344
+ - VERIFY: No additional error event emitted (only individual workflow errors)
345
+ - PATTERN: Use try-catch, verify error structure, count error events
346
+
347
+ Task 4: IMPLEMENT tests for enabled=true with default merge
348
+ - DESCRIBE: 'Enabled with default error merge'
349
+ - TEST: 'should merge all errors when errorMergeStrategy.enabled=true'
350
+ - TEST: 'should create aggregated error message with counts and task name'
351
+ - TEST: 'should aggregate all logs from all failed workflows'
352
+ - TEST: 'should include metadata in original field'
353
+ - VERIFY: Message format "${X} of ${Y} concurrent child workflows failed in task '${taskName}'"
354
+ - VERIFY: failedWorkflowIds array contains unique workflow IDs
355
+ - VERIFY: logs array is flattened from all errors
356
+ - VERIFY: Error event emitted with merged error
357
+
358
+ Task 5: IMPLEMENT tests for enabled=true with custom combine
359
+ - DESCRIBE: 'Enabled with custom combine function'
360
+ - TEST: 'should call custom combine function when provided'
361
+ - TEST: 'should use custom merge result from combine function'
362
+ - TEST: 'should pass all errors to custom combine function'
363
+ - TECHNIQUE: Use vi.fn() to spy on combine function
364
+ - VERIFY: combine() called with array of WorkflowError
365
+ - VERIFY: combine() return value is thrown as error
366
+
367
+ Task 6: IMPLEMENT edge case tests
368
+ - DESCRIBE: 'Edge cases and error scenarios'
369
+ - TEST: 'should handle single failure with merge enabled'
370
+ - TEST: 'should handle all workflows failing with merge enabled'
371
+ - TEST: 'should handle mixed success/failure with merge enabled'
372
+ - TEST: 'should complete all workflows even when errors occur'
373
+ - VERIFY: parent.children.length equals total children spawned
374
+
375
+ Task 7: IMPLEMENT maxMergeDepth tests (if applicable)
376
+ - CHECK: Does src/decorators/task.ts use maxMergeDepth?
377
+ - IF NOT IMPLEMENTED: Add test with todo.skip() or note as future work
378
+ - IF IMPLEMENTED: Test depth limiting behavior
379
+ - CURRENT STATUS: maxMergeDepth is in interface but NOT used in implementation
380
+ - RECOMMENDATION: Add todo('maxMergeDepth not yet implemented')
381
+
382
+ Task 8: VERIFY all tests pass
383
+ - RUN: npm test -- error-merge-strategy.test.ts
384
+ - VERIFY: All new tests pass
385
+ - RUN: npm test (full suite)
386
+ - VERIFY: No regressions in existing tests
387
+ ```
388
+
389
+ ### Implementation Patterns & Key Details
390
+
391
+ ```typescript
392
+ // ============================================================================
393
+ // PATTERN 1: Test File Structure
394
+ // Location: Top of src/__tests__/adversarial/error-merge-strategy.test.ts
395
+ // ============================================================================
396
+
397
+ import { describe, it, expect } from 'vitest';
398
+ import { Workflow, Task, Step } from '../../index.js';
399
+ import type { WorkflowEvent, WorkflowError } from '../../types/index.js';
400
+
401
+ describe('@Task decorator ErrorMergeStrategy', () => {
402
+ // Helper functions defined here
403
+ // Test suites defined here
404
+ });
405
+
406
+ // ============================================================================
407
+ // PATTERN 2: Helper Function - Create Child Workflow
408
+ // Follow: src/__tests__/adversarial/concurrent-task-failures.test.ts lines 30-52
409
+ // ============================================================================
410
+
411
+ function createChildWorkflow(
412
+ parent: Workflow,
413
+ name: string,
414
+ shouldFail: boolean = false
415
+ ): Workflow {
416
+ return new (class extends Workflow {
417
+ constructor(n: string, p: Workflow) {
418
+ super(n, p);
419
+ }
420
+
421
+ @Step()
422
+ async executeStep() {
423
+ if (shouldFail) {
424
+ throw new Error(`${name} failed`);
425
+ }
426
+ return `${name} succeeded`;
427
+ }
428
+
429
+ async run() {
430
+ return this.executeStep();
431
+ }
432
+ })(name, parent);
433
+ }
434
+
435
+ // KEY INSIGHTS:
436
+ // - Creates anonymous class extending Workflow
437
+ // - Uses @Step decorator to ensure error wrapping
438
+ // - shouldFail parameter controls success/failure
439
+ // - Error message includes workflow name for identification
440
+
441
+ // ============================================================================
442
+ // PATTERN 3: Helper Function - Setup Event Observer
443
+ // Follow: src/__tests__/adversarial/concurrent-task-failures.test.ts lines 58-67
444
+ // ============================================================================
445
+
446
+ function setupEventObserver(workflow: Workflow): WorkflowEvent[] {
447
+ const events: WorkflowEvent[] = [];
448
+ workflow.addObserver({
449
+ onLog: () => {},
450
+ onEvent: (e) => events.push(e),
451
+ onStateUpdated: () => {},
452
+ onTreeChanged: () => {},
453
+ });
454
+ return events;
455
+ }
456
+
457
+ // KEY INSIGHTS:
458
+ // - MUST be called before workflow.run()
459
+ // - Returns array for assertion
460
+ // - Captures all workflow events including errors
461
+
462
+ // ============================================================================
463
+ // PATTERN 4: Test for Default Behavior (disabled)
464
+ // ============================================================================
465
+
466
+ describe('Default behavior (errorMergeStrategy disabled)', () => {
467
+ it('should throw first error when errorMergeStrategy not provided', async () => {
468
+ // ARRANGE: Create parent with concurrent tasks, no error merge strategy
469
+ class ParentWorkflow extends Workflow {
470
+ @Task({ concurrent: true }) // CRITICAL: concurrent=true required
471
+ async spawnChildren() {
472
+ return [
473
+ createChildWorkflow(this, 'Child-0', false),
474
+ createChildWorkflow(this, 'Child-1', true), // Will fail
475
+ createChildWorkflow(this, 'Child-2', false),
476
+ ];
477
+ }
478
+
479
+ async run() {
480
+ try {
481
+ await this.spawnChildren();
482
+ } catch (err) {
483
+ // Expected - capture error for validation
484
+ return err;
485
+ }
486
+ }
487
+ }
488
+
489
+ const parent = new ParentWorkflow('Parent');
490
+ const events = setupEventObserver(parent);
491
+
492
+ // ACT: Run parent workflow
493
+ const thrownError = await parent.run();
494
+
495
+ // ASSERT: All children completed (Promise.allSettled behavior)
496
+ expect(parent.children.length).toBe(3);
497
+
498
+ // ASSERT: Error was thrown (first error wins)
499
+ expect(thrownError).toBeDefined();
500
+ expect((thrownError as WorkflowError).message).toContain('Child-1 failed');
501
+
502
+ // ASSERT: No additional error event from @Task (only individual workflow errors)
503
+ const errorEvents = events.filter((e) => e.type === 'error');
504
+ expect(errorEvents.length).toBe(1); // Only Child-1's error event
505
+ });
506
+
507
+ it('should throw first error when errorMergeStrategy.enabled=false', async () => {
508
+ // Similar to above but with explicit enabled=false
509
+ class ParentWorkflow extends Workflow {
510
+ @Task({
511
+ concurrent: true,
512
+ errorMergeStrategy: { enabled: false } // Explicitly disabled
513
+ })
514
+ async spawnChildren() {
515
+ return [
516
+ createChildWorkflow(this, 'Alpha', false),
517
+ createChildWorkflow(this, 'Beta', true),
518
+ createChildWorkflow(this, 'Gamma', true),
519
+ ];
520
+ }
521
+
522
+ async run() {
523
+ try {
524
+ await this.spawnChildren();
525
+ } catch (err) {
526
+ return err;
527
+ }
528
+ }
529
+ }
530
+
531
+ const parent = new ParentWorkflow('Parent');
532
+ const events = setupEventObserver(parent);
533
+ const thrownError = await parent.run();
534
+
535
+ // ASSERT: First error thrown (not aggregated)
536
+ expect(thrownError).toBeDefined();
537
+ const errorMsg = (thrownError as WorkflowError).message;
538
+ expect(errorMsg).toMatch(/Alpha failed|Beta failed|Gamma failed/);
539
+
540
+ // ASSERT: Only individual error events (no merge event)
541
+ const errorEvents = events.filter((e) => e.type === 'error');
542
+ expect(errorEvents.length).toBe(2); // Beta and Gamma errors only
543
+ });
544
+ });
545
+
546
+ // ============================================================================
547
+ // PATTERN 5: Test for Enabled with Default Merge
548
+ // ============================================================================
549
+
550
+ describe('Enabled with default error merge', () => {
551
+ it('should merge all errors when errorMergeStrategy.enabled=true', async () => {
552
+ // ARRANGE: Create parent with error merge enabled
553
+ class ParentWorkflow extends Workflow {
554
+ @Task({
555
+ concurrent: true,
556
+ errorMergeStrategy: { enabled: true } // No combine() - use default
557
+ })
558
+ async spawnChildren() {
559
+ return [
560
+ createChildWorkflow(this, 'Alpha', false),
561
+ createChildWorkflow(this, 'Beta', true), // Will fail
562
+ createChildWorkflow(this, 'Gamma', false),
563
+ createChildWorkflow(this, 'Delta', true), // Will fail
564
+ createChildWorkflow(this, 'Epsilon', false),
565
+ ];
566
+ }
567
+
568
+ async run() {
569
+ try {
570
+ await this.spawnChildren();
571
+ } catch (err) {
572
+ return err;
573
+ }
574
+ }
575
+ }
576
+
577
+ const parent = new ParentWorkflow('Parent');
578
+ const events = setupEventObserver(parent);
579
+
580
+ // ACT
581
+ const thrownError = await parent.run();
582
+
583
+ // ASSERT: All children completed
584
+ expect(parent.children.length).toBe(5);
585
+
586
+ // ASSERT: Merged error thrown
587
+ expect(thrownError).toBeDefined();
588
+ const error = thrownError as WorkflowError;
589
+
590
+ // ASSERT: Message includes count and task name
591
+ expect(error.message).toBe("2 of 5 concurrent child workflows failed in task 'spawnChildren'");
592
+
593
+ // ASSERT: Metadata in original field
594
+ const metadata = error.original as {
595
+ name: string;
596
+ message: string;
597
+ errors: WorkflowError[];
598
+ totalChildren: number;
599
+ failedChildren: number;
600
+ failedWorkflowIds: string[];
601
+ };
602
+
603
+ expect(metadata.name).toBe('WorkflowAggregateError');
604
+ expect(metadata.totalChildren).toBe(5);
605
+ expect(metadata.failedChildren).toBe(2);
606
+ expect(metadata.failedWorkflowIds).toHaveLength(2);
607
+ expect(metadata.errors).toHaveLength(2);
608
+
609
+ // ASSERT: Logs aggregated from all errors
610
+ expect(error.logs).toBeDefined();
611
+ expect(Array.isArray(error.logs)).toBe(true);
612
+ expect(error.logs.length).toBeGreaterThan(0);
613
+
614
+ // ASSERT: Error event emitted with merged error
615
+ const errorEvents = events.filter((e) => e.type === 'error');
616
+ expect(errorEvents.length).toBeGreaterThanOrEqual(3); // 2 individual + 1 merged
617
+
618
+ // Find the merged error event (has different message format)
619
+ const mergedErrorEvent = errorEvents.find((e) => {
620
+ if (e.type === 'error') {
621
+ return e.error.message.includes('2 of 5 concurrent');
622
+ }
623
+ return false;
624
+ });
625
+ expect(mergedErrorEvent).toBeDefined();
626
+ });
627
+
628
+ it('should aggregate logs from all failed workflows', async () => {
629
+ // Create workflows that log before failing
630
+ class LoggingWorkflow extends Workflow {
631
+ constructor(name: string, parent: Workflow, private shouldFail: boolean) {
632
+ super(name, parent);
633
+ }
634
+
635
+ @Step()
636
+ async executeStep() {
637
+ this.logger.info(`${this.node.name} starting`);
638
+ if (this.shouldFail) {
639
+ this.logger.error(`${this.node.name} failing`);
640
+ throw new Error(`${this.node.name} failed`);
641
+ }
642
+ this.logger.info(`${this.node.name} completed`);
643
+ }
644
+
645
+ async run() {
646
+ return this.executeStep();
647
+ }
648
+ }
649
+
650
+ class ParentWorkflow extends Workflow {
651
+ @Task({
652
+ concurrent: true,
653
+ errorMergeStrategy: { enabled: true }
654
+ })
655
+ async spawnChildren() {
656
+ return [
657
+ new LoggingWorkflow('Workflow-1', this, true),
658
+ new LoggingWorkflow('Workflow-2', this, true),
659
+ new LoggingWorkflow('Workflow-3', this, false),
660
+ ];
661
+ }
662
+
663
+ async run() {
664
+ try {
665
+ await this.spawnChildren();
666
+ } catch (err) {
667
+ return err;
668
+ }
669
+ }
670
+ }
671
+
672
+ const parent = new ParentWorkflow('Parent');
673
+ const thrownError = await parent.run() as WorkflowError;
674
+
675
+ // ASSERT: Logs from both failed workflows aggregated
676
+ expect(thrownError.logs).toBeDefined();
677
+ const logMessages = thrownError.logs.map(l => l.message);
678
+
679
+ // Should have logs from both failing workflows
680
+ expect(logMessages.some(m => m.includes('Workflow-1'))).toBe(true);
681
+ expect(logMessages.some(m => m.includes('Workflow-2'))).toBe(true);
682
+ });
683
+ });
684
+
685
+ // ============================================================================
686
+ // PATTERN 6: Test for Custom Combine Function
687
+ // ============================================================================
688
+
689
+ describe('Enabled with custom combine function', () => {
690
+ it('should call custom combine function when provided', async () => {
691
+ // ARRANGE: Create spy for combine function
692
+ const combineSpy = vi.fn((errors: WorkflowError[]) => ({
693
+ message: `Custom merge: ${errors.length} errors`,
694
+ original: errors,
695
+ workflowId: 'custom-parent',
696
+ logs: errors.flatMap(e => e.logs),
697
+ }));
698
+
699
+ class ParentWorkflow extends Workflow {
700
+ @Task({
701
+ concurrent: true,
702
+ errorMergeStrategy: {
703
+ enabled: true,
704
+ combine: combineSpy // Custom combine function
705
+ }
706
+ })
707
+ async spawnChildren() {
708
+ return [
709
+ createChildWorkflow(this, 'Alpha', true),
710
+ createChildWorkflow(this, 'Beta', true),
711
+ createChildWorkflow(this, 'Gamma', false),
712
+ ];
713
+ }
714
+
715
+ async run() {
716
+ try {
717
+ await this.spawnChildren();
718
+ } catch (err) {
719
+ return err;
720
+ }
721
+ }
722
+ }
723
+
724
+ const parent = new ParentWorkflow('Parent');
725
+
726
+ // ACT
727
+ await parent.run();
728
+
729
+ // ASSERT: Custom combine function was called
730
+ expect(combineSpy).toHaveBeenCalledTimes(1);
731
+
732
+ // ASSERT: Called with array of WorkflowError objects
733
+ const calls = combineSpy.mock.calls;
734
+ expect(calls).toHaveLength(1);
735
+ const errorsArg = calls[0][0] as WorkflowError[];
736
+ expect(Array.isArray(errorsArg)).toBe(true);
737
+ expect(errorsArg).toHaveLength(2); // Alpha and Beta failed
738
+ });
739
+
740
+ it('should use custom merge result from combine function', async () => {
741
+ // ARRANGE: Custom combine that returns specific format
742
+ const customMerger = (errors: WorkflowError[]): WorkflowError => ({
743
+ message: `MERGED: ${errors.map(e => e.message).join(' | ')}`,
744
+ original: {
745
+ customField: 'custom-value',
746
+ errors,
747
+ },
748
+ workflowId: 'merged-workflow',
749
+ logs: errors.flatMap(e => e.logs),
750
+ });
751
+
752
+ class ParentWorkflow extends Workflow {
753
+ @Task({
754
+ concurrent: true,
755
+ errorMergeStrategy: {
756
+ enabled: true,
757
+ combine: customMerger
758
+ }
759
+ })
760
+ async spawnChildren() {
761
+ return [
762
+ createChildWorkflow(this, 'First', true),
763
+ createChildWorkflow(this, 'Second', true),
764
+ ];
765
+ }
766
+
767
+ async run() {
768
+ try {
769
+ await this.spawnChildren();
770
+ } catch (err) {
771
+ return err;
772
+ }
773
+ }
774
+ }
775
+
776
+ const parent = new ParentWorkflow('Parent');
777
+ const thrownError = await parent.run() as WorkflowError;
778
+
779
+ // ASSERT: Custom merge result used
780
+ expect(thrownError.message).toBe('MERGED: First failed | Second failed');
781
+ expect(thrownError.workflowId).toBe('merged-workflow');
782
+
783
+ // ASSERT: Custom fields preserved
784
+ const customMetadata = thrownError.original as { customField: string };
785
+ expect(customMetadata.customField).toBe('custom-value');
786
+ });
787
+ });
788
+
789
+ // ============================================================================
790
+ // PATTERN 7: Edge Case Tests
791
+ // ============================================================================
792
+
793
+ describe('Edge cases and error scenarios', () => {
794
+ it('should handle single failure with merge enabled', async () => {
795
+ class ParentWorkflow extends Workflow {
796
+ @Task({
797
+ concurrent: true,
798
+ errorMergeStrategy: { enabled: true }
799
+ })
800
+ async spawnChildren() {
801
+ return [
802
+ createChildWorkflow(this, 'OnlyChild', true),
803
+ ];
804
+ }
805
+
806
+ async run() {
807
+ try {
808
+ await this.spawnChildren();
809
+ } catch (err) {
810
+ return err;
811
+ }
812
+ }
813
+ }
814
+
815
+ const parent = new ParentWorkflow('Parent');
816
+ const thrownError = await parent.run() as WorkflowError;
817
+
818
+ // ASSERT: Message format correct for single failure
819
+ expect(thrownError.message).toBe("1 of 1 concurrent child workflows failed in task 'spawnChildren'");
820
+ });
821
+
822
+ it('should handle all workflows failing with merge enabled', async () => {
823
+ class ParentWorkflow extends Workflow {
824
+ @Task({
825
+ concurrent: true,
826
+ errorMergeStrategy: { enabled: true }
827
+ })
828
+ async spawnChildren() {
829
+ return [
830
+ createChildWorkflow(this, 'W1', true),
831
+ createChildWorkflow(this, 'W2', true),
832
+ createChildWorkflow(this, 'W3', true),
833
+ createChildWorkflow(this, 'W4', true),
834
+ ];
835
+ }
836
+
837
+ async run() {
838
+ try {
839
+ await this.spawnChildren();
840
+ } catch (err) {
841
+ return err;
842
+ }
843
+ }
844
+ }
845
+
846
+ const parent = new ParentWorkflow('Parent');
847
+ const thrownError = await parent.run() as WorkflowError;
848
+
849
+ // ASSERT: All failures counted
850
+ expect(thrownError.message).toBe("4 of 4 concurrent child workflows failed in task 'spawnChildren'");
851
+
852
+ // ASSERT: All workflows completed
853
+ expect(parent.children.length).toBe(4);
854
+ });
855
+
856
+ it('should complete all workflows even when errors occur', async () => {
857
+ const completedWorkflows = new Set<string>();
858
+
859
+ class ParentWorkflow extends Workflow {
860
+ @Task({
861
+ concurrent: true,
862
+ errorMergeStrategy: { enabled: true }
863
+ })
864
+ async spawnChildren() {
865
+ const children = [
866
+ createChildWorkflow(this, 'Success1', false),
867
+ createChildWorkflow(this, 'Fail1', true),
868
+ createChildWorkflow(this, 'Success2', false),
869
+ createChildWorkflow(this, 'Fail2', true),
870
+ ];
871
+
872
+ // Track completion
873
+ children.forEach(child => {
874
+ child.run().then(
875
+ () => completedWorkflows.add(child.id),
876
+ () => completedWorkflows.add(child.id)
877
+ );
878
+ });
879
+
880
+ return children;
881
+ }
882
+
883
+ async run() {
884
+ try {
885
+ await this.spawnChildren();
886
+ } catch (err) {
887
+ // Expected
888
+ }
889
+ }
890
+ }
891
+
892
+ const parent = new ParentWorkflow('Parent');
893
+ await parent.run();
894
+
895
+ // ASSERT: All workflows completed (no orphans)
896
+ expect(completedWorkflows.size).toBe(4);
897
+ expect(parent.children.length).toBe(4);
898
+ });
899
+ });
900
+
901
+ // ============================================================================
902
+ // PATTERN 8: maxMergeDepth Tests (Future Work)
903
+ // ============================================================================
904
+
905
+ describe.todo('maxMergeDepth validation', () => {
906
+ // NOTE: maxMergeDepth is defined in ErrorMergeStrategy interface
907
+ // but not currently implemented in src/decorators/task.ts
908
+ // These tests should be implemented when maxMergeDepth is added
909
+
910
+ it('should respect maxMergeDepth when merging nested errors');
911
+ it('should handle maxMergeDepth=0 (no merging)');
912
+ it('should handle maxMergeDepth=1 (single level merging)');
913
+ });
914
+ ```
915
+
916
+ ### Integration Points
917
+
918
+ ```yaml
919
+ TASK_DECORATOR:
920
+ - file: src/decorators/task.ts
921
+ - lines: 119-143 - error merge strategy implementation
922
+ - test: Verify opts.errorMergeStrategy?.enabled check works
923
+ - test: Verify default merger called when no combine() provided
924
+ - test: Verify custom combine() called when provided
925
+
926
+ WORKFLOW_ERROR_UTILS:
927
+ - file: src/utils/workflow-error-utils.ts
928
+ - function: mergeWorkflowErrors()
929
+ - test: Verify default merge behavior matches this function
930
+ - test: Verify message format, log aggregation, metadata
931
+
932
+ EVENT_SYSTEM:
933
+ - test: Verify error events emitted for individual failures
934
+ - test: Verify additional error event emitted when merge enabled
935
+ - test: Verify merged error event has correct structure
936
+
937
+ CONCURRENT_EXECUTION:
938
+ - test: Verify Promise.allSettled completes all workflows
939
+ - test: Verify no orphaned promises
940
+ - test: Verify parent.children.length equals spawned count
941
+ ```
942
+
943
+ ---
944
+
945
+ ## Validation Loop
946
+
947
+ ### Level 1: Syntax & Style (Immediate Feedback)
948
+
949
+ ```bash
950
+ # After creating test file, run TypeScript check
951
+ npx tsc --noEmit
952
+
953
+ # Expected: Zero type errors
954
+ # Common errors to fix:
955
+ # - "Cannot find module" → Check import paths use .js extension
956
+ # - "Property 'errorMergeStrategy' does not exist" → Check TaskOptions import
957
+ # - Type errors in spy functions → Check vi.fn() typing
958
+
959
+ # Run linter if configured
960
+ npm run lint 2>/dev/null || echo "No linter configured"
961
+ ```
962
+
963
+ ### Level 2: Unit Tests (Component Validation)
964
+
965
+ ```bash
966
+ # Run the new test file specifically
967
+ npm test -- error-merge-strategy.test.ts
968
+
969
+ # Run with verbose output to see all test names
970
+ npm test -- error-merge-strategy.test.ts --reporter=verbose
971
+
972
+ # Expected: All new tests pass
973
+ # If tests fail, debug:
974
+ # - Check that @Task has concurrent: true
975
+ # - Check that observer is added before run()
976
+ # - Check try-catch error handling
977
+ # - Verify error structure with type guards
978
+
979
+ # Run related test files
980
+ npm test -- concurrent-task-failures.test.ts
981
+ npm test -- workflow-error-utils.test.ts
982
+ npm test -- decorators.test.ts
983
+
984
+ # Expected: All related tests still pass (no regressions)
985
+ ```
986
+
987
+ ### Level 3: Integration Testing (System Validation)
988
+
989
+ ```bash
990
+ # Run full adversarial test suite
991
+ npm test -- src/__tests__/adversarial/
992
+
993
+ # Expected: All adversarial tests pass
994
+ # This validates new tests work alongside existing edge case tests
995
+
996
+ # Run full test suite
997
+ npm test
998
+
999
+ # Expected: All tests pass (no regressions)
1000
+ # Current test count: ~344 tests
1001
+ # New tests should add ~10-15 tests
1002
+
1003
+ # Check test coverage
1004
+ npm test -- --coverage
1005
+
1006
+ # Expected: Error merge strategy code paths > 90% coverage
1007
+ ```
1008
+
1009
+ ### Level 4: Manual Verification
1010
+
1011
+ ```bash
1012
+ # Create a manual test to verify behavior
1013
+ cat > /tmp/test_error_merge_strategy.ts << 'EOF'
1014
+ import { Workflow, Task, Step } from './dist/index.js';
1015
+
1016
+ class ChildWorkflow extends Workflow {
1017
+ constructor(name: string, parent: Workflow, private fail: boolean) {
1018
+ super(name, parent);
1019
+ }
1020
+
1021
+ @Step()
1022
+ async run() {
1023
+ if (this.fail) {
1024
+ throw new Error(`${this.id} failed`);
1025
+ }
1026
+ return `${this.id} succeeded`;
1027
+ }
1028
+ }
1029
+
1030
+ // Test 1: Default behavior (disabled)
1031
+ class Parent1 extends Workflow {
1032
+ @Task({ concurrent: true })
1033
+ async spawn() {
1034
+ return [
1035
+ new ChildWorkflow('c1', this, false),
1036
+ new ChildWorkflow('c2', this, true),
1037
+ new ChildWorkflow('c3', this, false),
1038
+ ];
1039
+ }
1040
+ }
1041
+
1042
+ // Test 2: Enabled with default merge
1043
+ class Parent2 extends Workflow {
1044
+ @Task({ concurrent: true, errorMergeStrategy: { enabled: true } })
1045
+ async spawn() {
1046
+ return [
1047
+ new ChildWorkflow('c1', this, false),
1048
+ new ChildWorkflow('c2', this, true),
1049
+ new ChildWorkflow('c3', this, true),
1050
+ ];
1051
+ }
1052
+ }
1053
+
1054
+ async function test() {
1055
+ console.log('Test 1: Default behavior');
1056
+ try {
1057
+ const p1 = new Parent1('p1');
1058
+ await p1.spawn();
1059
+ } catch (err) {
1060
+ console.log('Error:', err.message);
1061
+ console.log('Is first error only:', !err.message.includes('concurrent'));
1062
+ }
1063
+
1064
+ console.log('\nTest 2: Enabled with default merge');
1065
+ try {
1066
+ const p2 = new Parent2('p2');
1067
+ await p2.spawn();
1068
+ } catch (err) {
1069
+ console.log('Error:', err.message);
1070
+ console.log('Is aggregated:', err.message.includes('concurrent'));
1071
+ console.log('Failed children:', err.original?.failedChildren);
1072
+ }
1073
+ }
1074
+
1075
+ test();
1076
+ EOF
1077
+
1078
+ # Build and run
1079
+ npm run build
1080
+ node /tmp/test_error_merge_strategy.ts
1081
+
1082
+ # Expected output:
1083
+ # Test 1: Error message contains single child name (not "concurrent")
1084
+ # Test 2: Error message contains "X of Y concurrent" and failedChildren count
1085
+
1086
+ # Clean up
1087
+ rm /tmp/test_error_merge_strategy.ts
1088
+ ```
1089
+
1090
+ ---
1091
+
1092
+ ## Final Validation Checklist
1093
+
1094
+ ### Technical Validation
1095
+
1096
+ - [ ] Test file created at `src/__tests__/adversarial/error-merge-strategy.test.ts`
1097
+ - [ ] All imports use `.js` extensions for ES modules
1098
+ - [ ] Test follows patterns from `concurrent-task-failures.test.ts`
1099
+ - [ ] Helper functions defined (createChildWorkflow, setupEventObserver)
1100
+ - [ ] TypeScript compilation succeeds: `npx tsc --noEmit`
1101
+ - [ ] All new tests pass: `npm test -- error-merge-strategy.test.ts`
1102
+ - [ ] All existing tests pass: `npm test`
1103
+
1104
+ ### Feature Validation - Default Behavior (disabled)
1105
+
1106
+ - [ ] Test for no errorMergeStrategy provided
1107
+ - [ ] Test for errorMergeStrategy.enabled=false
1108
+ - [ ] Verifies first error thrown (not aggregated)
1109
+ - [ ] Verifies no additional error event from @Task
1110
+ - [ ] Verifies all workflows complete (Promise.allSettled)
1111
+
1112
+ ### Feature Validation - Enabled with Default Merge
1113
+
1114
+ - [ ] Test for errorMergeStrategy.enabled=true without combine()
1115
+ - [ ] Verifies aggregated error message with counts and task name
1116
+ - [ ] Verifies metadata in original field (name, errors, totalChildren, failedChildren, failedWorkflowIds)
1117
+ - [ ] Verifies logs aggregated from all failed workflows
1118
+ - [ ] Verifies error event emitted with merged error
1119
+ - [ ] Verifies all workflows complete despite failures
1120
+
1121
+ ### Feature Validation - Enabled with Custom Combine
1122
+
1123
+ - [ ] Test for errorMergeStrategy.enabled=true with combine()
1124
+ - [ ] Verifies custom combine() function is called
1125
+ - [ ] Verifies all errors passed to combine()
1126
+ - [ ] Verifies combine() return value is thrown
1127
+ - [ ] Uses vi.fn() spy to verify call count and arguments
1128
+
1129
+ ### Feature Validation - Edge Cases
1130
+
1131
+ - [ ] Test for single failure with merge enabled
1132
+ - [ ] Test for all workflows failing
1133
+ - [ ] Test for mixed success/failure scenarios
1134
+ - [ ] Test for no orphaned workflows (completion tracking)
1135
+
1136
+ ### Code Quality Validation
1137
+
1138
+ - [ ] Follows existing codebase test patterns
1139
+ - [ ] File placement in adversarial/ directory
1140
+ - [ ] Test names are descriptive and follow convention
1141
+ - [ ] Helper functions follow existing patterns
1142
+ - [ ] Comments clarify complex test scenarios
1143
+ - [ ] No hardcoded timeouts or delays
1144
+ - [ ] Tests are deterministic (no race conditions)
1145
+
1146
+ ### Documentation & Deployment
1147
+
1148
+ - [ ] Test file has clear header comment describing purpose
1149
+ - [ ] Test suites are logically organized
1150
+ - [ ] Edge cases are documented in test descriptions
1151
+ - [ ] Future work (maxMergeDepth) noted with todo/todo.skip
1152
+ - [ ] Tests serve as documentation of expected behavior
1153
+
1154
+ ---
1155
+
1156
+ ## Anti-Patterns to Avoid
1157
+
1158
+ - ❌ **Don't forget `concurrent: true`** - Error merge strategy only works with concurrent execution
1159
+ - ❌ **Don't add observer after run()** - Events during run() won't be captured
1160
+ - ❌ **Don't assume error message order** - First error may not be deterministic
1161
+ - ❌ **Don't skip try-catch for expected errors** - Tests will fail with unhandled rejection
1162
+ - ❌ **Don't use `await` before expect in try-catch** - Errors won't be caught properly
1163
+ - ❌ **Don't test unimplemented features** - maxMergeDepth is not implemented, use todo.skip()
1164
+ - ❌ **Don't forget to verify all workflows completed** - Check parent.children.length
1165
+ - ❌ **Don't count error events incorrectly** - Individual errors + 1 merged event when enabled
1166
+ - ❌ **Don't hardcode expected error messages** - Use regex or partial matching
1167
+ - ❌ **Don't place test file in unit/ directory** - This is an adversarial test
1168
+
1169
+ ---
1170
+
1171
+ ## Confidence Score
1172
+
1173
+ **9/10** - High confidence for one-pass implementation success
1174
+
1175
+ **Reasoning:**
1176
+ - Clear implementation files to test (task.ts, workflow-error-utils.ts)
1177
+ - Existing test patterns provide exact templates to follow
1178
+ - Comprehensive research documentation available
1179
+ - Well-defined test scenarios from contract definition
1180
+ - Helper function patterns established in codebase
1181
+
1182
+ **Risk Factors:**
1183
+ - Must remember `concurrent: true` is required for error merge
1184
+ - Event counting can be tricky (individual + merged events)
1185
+ - maxMergeDepth not implemented - need to handle gracefully
1186
+ - First error may not be deterministic in concurrent execution
1187
+
1188
+ ---
1189
+
1190
+ ## Appendix: Test File Template
1191
+
1192
+ ```typescript
1193
+ /**
1194
+ * ErrorMergeStrategy Functionality Test Suite
1195
+ *
1196
+ * Tests the ErrorMergeStrategy feature for concurrent task error aggregation.
1197
+ *
1198
+ * Validates:
1199
+ * - Default behavior (disabled) - first error wins
1200
+ * - Enabled with default merge - uses mergeWorkflowErrors
1201
+ * - Enabled with custom combine - user-provided merger
1202
+ * - Edge cases - single failure, all failing, mixed scenarios
1203
+ * - Event emission - individual and merged error events
1204
+ * - Completion verification - all workflows complete
1205
+ *
1206
+ * Related:
1207
+ * - P1.M2.T2.S2: Error aggregation logic implementation
1208
+ * - P1.M2.T2.S3: Default error merger utility
1209
+ * - Bug: 001_e8e04329daf3 - Concurrent task error handling
1210
+ */
1211
+
1212
+ import { describe, it, expect, todo } from 'vitest';
1213
+ import { Workflow, Task, Step } from '../../index.js';
1214
+ import type { WorkflowEvent, WorkflowError } from '../../types/index.js';
1215
+
1216
+ describe('@Task decorator ErrorMergeStrategy', () => {
1217
+ // Helper functions
1218
+ function createChildWorkflow(parent: Workflow, name: string, shouldFail: boolean = false): Workflow {
1219
+ // ... implementation
1220
+ }
1221
+
1222
+ function setupEventObserver(workflow: Workflow): WorkflowEvent[] {
1223
+ // ... implementation
1224
+ }
1225
+
1226
+ // Test suites
1227
+ describe('Default behavior (errorMergeStrategy disabled)', () => {
1228
+ // ... tests
1229
+ });
1230
+
1231
+ describe('Enabled with default error merge', () => {
1232
+ // ... tests
1233
+ });
1234
+
1235
+ describe('Enabled with custom combine function', () => {
1236
+ // ... tests
1237
+ });
1238
+
1239
+ describe('Edge cases and error scenarios', () => {
1240
+ // ... tests
1241
+ });
1242
+
1243
+ describe.todo('maxMergeDepth validation', () => {
1244
+ // Future: maxMergeDepth tests
1245
+ });
1246
+ });
1247
+ ```
1248
+
1249
+ ---
1250
+
1251
+ **PRP Status**: ✅ Complete - Ready for Implementation
1252
+ **Next Task**: P1.M2.T3.S1 - Document trackTiming Default Value in PRD