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,643 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Workflow } from '../../core/workflow';
3
+ import type { WorkflowObserver, WorkflowEvent, LogEntry } from '../../types';
4
+
5
+ describe('Observer Error Logging Integration Tests', () => {
6
+ describe('WorkflowLogger observer onLog error', () => {
7
+ it('should log observer onLog errors to workflow.node.logs', async () => {
8
+ const onLogError = new Error('Observer onLog failed');
9
+
10
+ const throwingObserver: WorkflowObserver = {
11
+ onLog: () => {
12
+ throw onLogError;
13
+ },
14
+ onEvent: () => {},
15
+ onStateUpdated: () => {},
16
+ onTreeChanged: () => {},
17
+ };
18
+
19
+ class TestWorkflow extends Workflow {
20
+ async run() {
21
+ this.logger.info('Test message');
22
+ }
23
+ }
24
+
25
+ const workflow = new TestWorkflow();
26
+ workflow.addObserver(throwingObserver);
27
+ await workflow.run();
28
+
29
+ // Should have 2 logs: the original "Test message" and the observer error
30
+ expect(workflow.node.logs.length).toBe(2);
31
+
32
+ // First log is the original message
33
+ expect(workflow.node.logs[0].message).toBe('Test message');
34
+ expect(workflow.node.logs[0].level).toBe('info');
35
+
36
+ // Second log is the observer error
37
+ expect(workflow.node.logs[1].message).toBe('Observer onLog error');
38
+ expect(workflow.node.logs[1].level).toBe('error');
39
+ expect(workflow.node.logs[1].data).toEqual({ error: onLogError });
40
+ });
41
+
42
+ it('should avoid infinite recursion when observer onLog throws', async () => {
43
+ let callCount = 0;
44
+ const maxCalls = 10; // Safety limit to prevent actual infinite loop
45
+
46
+ const throwingObserver: WorkflowObserver = {
47
+ onLog: () => {
48
+ callCount++;
49
+ if (callCount < maxCalls) {
50
+ throw new Error('Recursive error');
51
+ }
52
+ },
53
+ onEvent: () => {},
54
+ onStateUpdated: () => {},
55
+ onTreeChanged: () => {},
56
+ };
57
+
58
+ class TestWorkflow extends Workflow {
59
+ async run() {
60
+ this.logger.info('Test message');
61
+ }
62
+ }
63
+
64
+ const workflow = new TestWorkflow();
65
+ workflow.addObserver(throwingObserver);
66
+ await workflow.run();
67
+
68
+ // Should only call onLog once (original) + one error log, then stop
69
+ // The error log should NOT trigger another observer notification
70
+ expect(callCount).toBe(1);
71
+
72
+ // Should have 2 logs: original + error
73
+ expect(workflow.node.logs.length).toBe(2);
74
+ });
75
+ });
76
+
77
+ describe('Workflow observer onEvent error', () => {
78
+ it('should log observer onEvent errors via this.logger.error', () => {
79
+ const onEventError = new Error('Observer onEvent failed');
80
+
81
+ const throwingObserver: WorkflowObserver = {
82
+ onLog: () => {},
83
+ onEvent: () => {
84
+ throw onEventError;
85
+ },
86
+ onStateUpdated: () => {},
87
+ onTreeChanged: () => {},
88
+ };
89
+
90
+ class TestWorkflow extends Workflow {
91
+ async run() {
92
+ this.emitEvent({ type: 'testEvent' } as any);
93
+ }
94
+ }
95
+
96
+ const workflow = new TestWorkflow();
97
+ workflow.addObserver(throwingObserver);
98
+
99
+ workflow.run();
100
+
101
+ // Should have error log entry
102
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onEvent error');
103
+ expect(errorLog).toBeDefined();
104
+ expect(errorLog?.level).toBe('error');
105
+ expect(errorLog?.data).toEqual({
106
+ error: onEventError,
107
+ eventType: 'testEvent',
108
+ });
109
+ });
110
+
111
+ it('should include event type in error data', () => {
112
+ const throwingObserver: WorkflowObserver = {
113
+ onLog: () => {},
114
+ onEvent: () => {
115
+ throw new Error('Event error');
116
+ },
117
+ onStateUpdated: () => {},
118
+ onTreeChanged: () => {},
119
+ };
120
+
121
+ class TestWorkflow extends Workflow {
122
+ async run() {
123
+ this.emitEvent({ type: 'treeUpdated', root: this.node });
124
+ }
125
+ }
126
+
127
+ const workflow = new TestWorkflow();
128
+ workflow.addObserver(throwingObserver);
129
+
130
+ workflow.run();
131
+
132
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onEvent error');
133
+ expect(errorLog?.data).toHaveProperty('eventType', 'treeUpdated');
134
+ });
135
+ });
136
+
137
+ describe('Workflow observer onStateUpdated error', () => {
138
+ it('should log observer onStateUpdated errors via this.logger.error', async () => {
139
+ const onStateUpdatedError = new Error('Observer onStateUpdated failed');
140
+
141
+ const throwingObserver: WorkflowObserver = {
142
+ onLog: () => {},
143
+ onEvent: () => {},
144
+ onStateUpdated: () => {
145
+ throw onStateUpdatedError;
146
+ },
147
+ onTreeChanged: () => {},
148
+ };
149
+
150
+ class TestWorkflow extends Workflow {
151
+ async run() {
152
+ this.snapshotState();
153
+ }
154
+ }
155
+
156
+ const workflow = new TestWorkflow();
157
+ workflow.addObserver(throwingObserver);
158
+ await workflow.run();
159
+
160
+ // Should have error log entry
161
+ const errorLog = workflow.node.logs.find(
162
+ (log) => log.message === 'Observer onStateUpdated error'
163
+ );
164
+ expect(errorLog).toBeDefined();
165
+ expect(errorLog?.level).toBe('error');
166
+ expect(errorLog?.data).toEqual({
167
+ error: onStateUpdatedError,
168
+ nodeId: workflow.node.id,
169
+ });
170
+ });
171
+
172
+ it('should include node ID in error data', async () => {
173
+ const throwingObserver: WorkflowObserver = {
174
+ onLog: () => {},
175
+ onEvent: () => {},
176
+ onStateUpdated: () => {
177
+ throw new Error('State update error');
178
+ },
179
+ onTreeChanged: () => {},
180
+ };
181
+
182
+ class TestWorkflow extends Workflow {
183
+ async run() {
184
+ this.snapshotState();
185
+ }
186
+ }
187
+
188
+ const workflow = new TestWorkflow();
189
+ workflow.addObserver(throwingObserver);
190
+ await workflow.run();
191
+
192
+ const errorLog = workflow.node.logs.find(
193
+ (log) => log.message === 'Observer onStateUpdated error'
194
+ );
195
+ expect(errorLog?.data).toHaveProperty('nodeId', workflow.node.id);
196
+ });
197
+ });
198
+
199
+ describe('Validation errors still use console.error', () => {
200
+ it('should use console.error for child already has parent validation', () => {
201
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
202
+
203
+ const parent = new Workflow('ParentWorkflow');
204
+ const child1 = new Workflow('Child1', parent);
205
+ const child2 = new Workflow('Child2');
206
+
207
+ // Try to attach child1 to child2 (should fail - already has parent)
208
+ expect(() => {
209
+ child2.attachChild(child1);
210
+ }).toThrow();
211
+
212
+ // Should have called console.error for validation
213
+ expect(consoleErrorSpy).toHaveBeenCalled();
214
+ const errorMessage = consoleErrorSpy.mock.calls[0][0] as string;
215
+ expect(errorMessage).toContain('already has a parent');
216
+
217
+ consoleErrorSpy.mockRestore();
218
+ });
219
+
220
+ it('should use console.error for circular reference detection', () => {
221
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
222
+
223
+ const grandparent = new Workflow('Grandparent');
224
+ const parent = new Workflow('Parent', grandparent);
225
+ const child = new Workflow('Child', parent);
226
+
227
+ // Try to attach grandparent as child of child (circular reference)
228
+ expect(() => {
229
+ child.attachChild(grandparent);
230
+ }).toThrow();
231
+
232
+ // Should have called console.error for validation
233
+ expect(consoleErrorSpy).toHaveBeenCalled();
234
+ const errorMessage = consoleErrorSpy.mock.calls[0][0] as string;
235
+ expect(errorMessage).toContain('circular reference');
236
+
237
+ consoleErrorSpy.mockRestore();
238
+ });
239
+ });
240
+
241
+ describe('Error isolation', () => {
242
+ it('should not crash workflow when observer onLog throws', async () => {
243
+ const throwingObserver: WorkflowObserver = {
244
+ onLog: () => {
245
+ throw new Error('Observer onLog error');
246
+ },
247
+ onEvent: () => {},
248
+ onStateUpdated: () => {},
249
+ onTreeChanged: () => {},
250
+ };
251
+
252
+ class TestWorkflow extends Workflow {
253
+ async run() {
254
+ this.logger.info('Message 1');
255
+ this.logger.info('Message 2');
256
+ this.logger.info('Message 3');
257
+ }
258
+ }
259
+
260
+ const workflow = new TestWorkflow();
261
+ workflow.addObserver(throwingObserver);
262
+
263
+ // Should complete without throwing
264
+ await expect(workflow.run()).resolves.toBeUndefined();
265
+
266
+ // All messages should be logged
267
+ expect(workflow.node.logs.length).toBe(6); // 3 messages + 3 error logs
268
+ });
269
+
270
+ it('should not crash workflow when observer onEvent throws', () => {
271
+ const throwingObserver: WorkflowObserver = {
272
+ onLog: () => {},
273
+ onEvent: () => {
274
+ throw new Error('Observer onEvent error');
275
+ },
276
+ onStateUpdated: () => {},
277
+ onTreeChanged: () => {},
278
+ };
279
+
280
+ class TestWorkflow extends Workflow {
281
+ async run() {
282
+ this.emitEvent({ type: 'event1' } as any);
283
+ this.emitEvent({ type: 'event2' } as any);
284
+ this.emitEvent({ type: 'event3' } as any);
285
+ }
286
+ }
287
+
288
+ const workflow = new TestWorkflow();
289
+ workflow.addObserver(throwingObserver);
290
+
291
+ // Should complete without throwing
292
+ expect(() => {
293
+ workflow.run();
294
+ }).not.toThrow();
295
+
296
+ // All events should be emitted
297
+ expect(workflow.node.events.length).toBe(3);
298
+ });
299
+
300
+ it('should not crash workflow when observer onStateUpdated throws', async () => {
301
+ const throwingObserver: WorkflowObserver = {
302
+ onLog: () => {},
303
+ onEvent: () => {},
304
+ onStateUpdated: () => {
305
+ throw new Error('Observer onStateUpdated error');
306
+ },
307
+ onTreeChanged: () => {},
308
+ };
309
+
310
+ class TestWorkflow extends Workflow {
311
+ async run() {
312
+ this.snapshotState();
313
+ this.snapshotState();
314
+ this.snapshotState();
315
+ }
316
+ }
317
+
318
+ const workflow = new TestWorkflow();
319
+ workflow.addObserver(throwingObserver);
320
+
321
+ // Should complete without throwing
322
+ await expect(workflow.run()).resolves.toBeUndefined();
323
+ });
324
+ });
325
+
326
+ describe('Observer onTreeChanged error', () => {
327
+ it('should log observer onTreeChanged errors (caught as onEvent error)', () => {
328
+ const onTreeChangedError = new Error('Observer onTreeChanged failed');
329
+
330
+ const throwingObserver: WorkflowObserver = {
331
+ onLog: () => {},
332
+ onEvent: () => {},
333
+ onStateUpdated: () => {},
334
+ onTreeChanged: () => {
335
+ throw onTreeChangedError;
336
+ },
337
+ };
338
+
339
+ class TestWorkflow extends Workflow {
340
+ async run() {
341
+ // Trigger treeUpdated event which calls onTreeChanged
342
+ this.emitEvent({ type: 'treeUpdated', root: this.node });
343
+ }
344
+ }
345
+
346
+ const workflow = new TestWorkflow();
347
+ workflow.addObserver(throwingObserver);
348
+
349
+ workflow.run();
350
+
351
+ // Should have error log entry (logged as "Observer onEvent error" since onTreeChanged is in onEvent try-catch)
352
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onEvent error');
353
+ expect(errorLog).toBeDefined();
354
+ expect(errorLog?.level).toBe('error');
355
+ expect(errorLog?.data).toEqual({
356
+ error: onTreeChangedError,
357
+ eventType: 'treeUpdated',
358
+ });
359
+ });
360
+
361
+ it('should log observer onTreeChanged errors for childAttached events', () => {
362
+ const onTreeChangedError = new Error('Tree changed on attach');
363
+
364
+ const throwingObserver: WorkflowObserver = {
365
+ onLog: () => {},
366
+ onEvent: () => {},
367
+ onStateUpdated: () => {},
368
+ onTreeChanged: () => {
369
+ throw onTreeChangedError;
370
+ },
371
+ };
372
+
373
+ const parent = new Workflow('ParentWorkflow');
374
+ const child = new Workflow('ChildWorkflow');
375
+
376
+ parent.addObserver(throwingObserver);
377
+
378
+ // Trigger childAttached event which calls onTreeChanged
379
+ parent.attachChild(child);
380
+
381
+ // Should have error log entry (logged as "Observer onEvent error")
382
+ const errorLog = parent.node.logs.find((log) => log.message === 'Observer onEvent error');
383
+ expect(errorLog).toBeDefined();
384
+ expect(errorLog?.data).toEqual({
385
+ error: onTreeChangedError,
386
+ eventType: 'childAttached',
387
+ });
388
+ });
389
+
390
+ it('should log observer onTreeChanged errors for childDetached events', () => {
391
+ const onTreeChangedError = new Error('Tree changed on detach');
392
+
393
+ const throwingObserver: WorkflowObserver = {
394
+ onLog: () => {},
395
+ onEvent: () => {},
396
+ onStateUpdated: () => {},
397
+ onTreeChanged: () => {
398
+ throw onTreeChangedError;
399
+ },
400
+ };
401
+
402
+ const parent = new Workflow('ParentWorkflow');
403
+ const child = new Workflow('ChildWorkflow', parent);
404
+
405
+ parent.addObserver(throwingObserver);
406
+
407
+ // Clear logs from setup
408
+ parent.node.logs = [];
409
+
410
+ // Trigger childDetached event which calls onTreeChanged
411
+ parent.detachChild(child);
412
+
413
+ // Should have error log entry (logged as "Observer onEvent error")
414
+ const errorLog = parent.node.logs.find((log) => log.message === 'Observer onEvent error');
415
+ expect(errorLog).toBeDefined();
416
+ expect(errorLog?.data).toEqual({
417
+ error: onTreeChangedError,
418
+ eventType: 'childDetached',
419
+ });
420
+ });
421
+ });
422
+
423
+ describe('Console.error negative verification', () => {
424
+ it('should NOT call console.error for observer onLog errors', async () => {
425
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
426
+
427
+ const throwingObserver: WorkflowObserver = {
428
+ onLog: () => {
429
+ throw new Error('Observer onLog error');
430
+ },
431
+ onEvent: () => {},
432
+ onStateUpdated: () => {},
433
+ onTreeChanged: () => {},
434
+ };
435
+
436
+ class TestWorkflow extends Workflow {
437
+ async run() {
438
+ this.logger.info('Test message');
439
+ }
440
+ }
441
+
442
+ const workflow = new TestWorkflow();
443
+ workflow.addObserver(throwingObserver);
444
+ await workflow.run();
445
+
446
+ // Verify console.error was NOT called for observer error
447
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
448
+
449
+ // Verify error was logged to workflow.node.logs instead
450
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onLog error');
451
+ expect(errorLog).toBeDefined();
452
+
453
+ consoleErrorSpy.mockRestore();
454
+ });
455
+
456
+ it('should NOT call console.error for observer onEvent errors', () => {
457
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
458
+
459
+ const throwingObserver: WorkflowObserver = {
460
+ onLog: () => {},
461
+ onEvent: () => {
462
+ throw new Error('Observer onEvent error');
463
+ },
464
+ onStateUpdated: () => {},
465
+ onTreeChanged: () => {},
466
+ };
467
+
468
+ class TestWorkflow extends Workflow {
469
+ async run() {
470
+ this.emitEvent({ type: 'testEvent' } as any);
471
+ }
472
+ }
473
+
474
+ const workflow = new TestWorkflow();
475
+ workflow.addObserver(throwingObserver);
476
+ workflow.run();
477
+
478
+ // Verify console.error was NOT called for observer error
479
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
480
+
481
+ // Verify error was logged to workflow.node.logs instead
482
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onEvent error');
483
+ expect(errorLog).toBeDefined();
484
+
485
+ consoleErrorSpy.mockRestore();
486
+ });
487
+
488
+ it('should NOT call console.error for observer onStateUpdated errors', async () => {
489
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
490
+
491
+ const throwingObserver: WorkflowObserver = {
492
+ onLog: () => {},
493
+ onEvent: () => {},
494
+ onStateUpdated: () => {
495
+ throw new Error('Observer onStateUpdated error');
496
+ },
497
+ onTreeChanged: () => {},
498
+ };
499
+
500
+ class TestWorkflow extends Workflow {
501
+ async run() {
502
+ this.snapshotState();
503
+ }
504
+ }
505
+
506
+ const workflow = new TestWorkflow();
507
+ workflow.addObserver(throwingObserver);
508
+ await workflow.run();
509
+
510
+ // Verify console.error was NOT called for observer error
511
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
512
+
513
+ // Verify error was logged to workflow.node.logs instead
514
+ const errorLog = workflow.node.logs.find(
515
+ (log) => log.message === 'Observer onStateUpdated error'
516
+ );
517
+ expect(errorLog).toBeDefined();
518
+
519
+ consoleErrorSpy.mockRestore();
520
+ });
521
+
522
+ it('should NOT call console.error for observer onTreeChanged errors', () => {
523
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
524
+
525
+ const throwingObserver: WorkflowObserver = {
526
+ onLog: () => {},
527
+ onEvent: () => {},
528
+ onStateUpdated: () => {},
529
+ onTreeChanged: () => {
530
+ throw new Error('Observer onTreeChanged error');
531
+ },
532
+ };
533
+
534
+ const parent = new Workflow('ParentWorkflow');
535
+ const child = new Workflow('ChildWorkflow');
536
+
537
+ parent.addObserver(throwingObserver);
538
+
539
+ // Trigger childAttached which calls onTreeChanged
540
+ parent.attachChild(child);
541
+
542
+ // Verify console.error was NOT called for observer error
543
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
544
+
545
+ // Verify error was logged to workflow.node.logs instead (as onEvent error)
546
+ const errorLog = parent.node.logs.find((log) => log.message === 'Observer onEvent error');
547
+ expect(errorLog).toBeDefined();
548
+
549
+ consoleErrorSpy.mockRestore();
550
+ });
551
+ });
552
+
553
+ describe('Multiple observers', () => {
554
+ it('should continue notifying other observers after one throws', async () => {
555
+ let observer2Called = false;
556
+ let observer3Called = false;
557
+
558
+ const throwingObserver: WorkflowObserver = {
559
+ onLog: () => {
560
+ throw new Error('Observer 1 failed');
561
+ },
562
+ onEvent: () => {},
563
+ onStateUpdated: () => {},
564
+ onTreeChanged: () => {},
565
+ };
566
+
567
+ const workingObserver2: WorkflowObserver = {
568
+ onLog: () => {
569
+ observer2Called = true;
570
+ },
571
+ onEvent: () => {},
572
+ onStateUpdated: () => {},
573
+ onTreeChanged: () => {},
574
+ };
575
+
576
+ const workingObserver3: WorkflowObserver = {
577
+ onLog: () => {
578
+ observer3Called = true;
579
+ },
580
+ onEvent: () => {},
581
+ onStateUpdated: () => {},
582
+ onTreeChanged: () => {},
583
+ };
584
+
585
+ class TestWorkflow extends Workflow {
586
+ async run() {
587
+ this.logger.info('Test message');
588
+ }
589
+ }
590
+
591
+ const workflow = new TestWorkflow();
592
+ workflow.addObserver(throwingObserver);
593
+ workflow.addObserver(workingObserver2);
594
+ workflow.addObserver(workingObserver3);
595
+
596
+ await workflow.run();
597
+
598
+ // Both working observers should have been called
599
+ expect(observer2Called).toBe(true);
600
+ expect(observer3Called).toBe(true);
601
+
602
+ // Should have error log for throwing observer
603
+ const errorLog = workflow.node.logs.find((log) => log.message === 'Observer onLog error');
604
+ expect(errorLog).toBeDefined();
605
+ });
606
+
607
+ it('should log errors for multiple throwing observers', async () => {
608
+ const throwingObserver1: WorkflowObserver = {
609
+ onLog: () => {
610
+ throw new Error('Observer 1 error');
611
+ },
612
+ onEvent: () => {},
613
+ onStateUpdated: () => {},
614
+ onTreeChanged: () => {},
615
+ };
616
+
617
+ const throwingObserver2: WorkflowObserver = {
618
+ onLog: () => {
619
+ throw new Error('Observer 2 error');
620
+ },
621
+ onEvent: () => {},
622
+ onStateUpdated: () => {},
623
+ onTreeChanged: () => {},
624
+ };
625
+
626
+ class TestWorkflow extends Workflow {
627
+ async run() {
628
+ this.logger.info('Test message');
629
+ }
630
+ }
631
+
632
+ const workflow = new TestWorkflow();
633
+ workflow.addObserver(throwingObserver1);
634
+ workflow.addObserver(throwingObserver2);
635
+
636
+ await workflow.run();
637
+
638
+ // Should have error logs for both throwing observers
639
+ const errorLogs = workflow.node.logs.filter((log) => log.message === 'Observer onLog error');
640
+ expect(errorLogs.length).toBe(2);
641
+ });
642
+ });
643
+ });
@@ -111,4 +111,41 @@ describe('Tree Mirroring Integration', () => {
111
111
  }
112
112
  }
113
113
  });
114
+
115
+ it('should propagate treeUpdated events to root observers', () => {
116
+ // ARRANGE: Create parent-child workflow tree
117
+ const parent = new TDDOrchestrator('Parent');
118
+ const child = new TDDOrchestrator('Child', parent);
119
+
120
+ // ARRANGE: Set up observer with collection arrays
121
+ const events: WorkflowEvent[] = [];
122
+ const treeChangedCalls: any[] = [];
123
+
124
+ // ARRANGE: Create inline observer
125
+ const observer: WorkflowObserver = {
126
+ onLog: () => {}, // Empty - not testing logs
127
+ onEvent: (event) => events.push(event), // Capture events
128
+ onStateUpdated: () => {}, // Empty - not testing state updates
129
+ onTreeChanged: (root) => treeChangedCalls.push(root), // Capture tree changes
130
+ };
131
+
132
+ // ARRANGE: Attach observer to ROOT (parent, not child)
133
+ parent.addObserver(observer);
134
+
135
+ // ACT: Trigger status change on CHILD workflow
136
+ child.setStatus('completed');
137
+
138
+ // ASSERT: Verify treeUpdated event was received via onEvent
139
+ const treeUpdatedEvent = events.find((e) => e.type === 'treeUpdated');
140
+ expect(treeUpdatedEvent).toBeDefined();
141
+
142
+ // ASSERT: Type guard for discriminated union + verify root node
143
+ if (treeUpdatedEvent && treeUpdatedEvent.type === 'treeUpdated') {
144
+ expect(treeUpdatedEvent.root).toBe(parent.getNode());
145
+ }
146
+
147
+ // ASSERT: Verify onTreeChanged callback was invoked
148
+ expect(treeChangedCalls).toHaveLength(1);
149
+ expect(treeChangedCalls[0]).toBe(parent.getNode());
150
+ });
114
151
  });