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,303 @@
1
+ /**
2
+ * Integration Test: Reparenting Observer Propagation
3
+ *
4
+ * Validates that observer propagation correctly updates when a child
5
+ * workflow is reparented from one parent to another.
6
+ *
7
+ * Pattern 7 (from bug_fix_tasks.json):
8
+ * "Observer propagation should update after reparenting.
9
+ * Events from child should only reach new parent's observers."
10
+ */
11
+
12
+ import { describe, it, expect } from 'vitest';
13
+ import {
14
+ Workflow,
15
+ WorkflowObserver,
16
+ WorkflowEvent,
17
+ WorkflowTreeDebugger,
18
+ } from '../../index.js';
19
+
20
+ /**
21
+ * SimpleWorkflow class for testing
22
+ * Pattern from: src/__tests__/unit/workflow.test.ts:4-11
23
+ */
24
+ class SimpleWorkflow extends Workflow {
25
+ async run(): Promise<string> {
26
+ this.setStatus('running');
27
+ this.setStatus('completed');
28
+ return 'done';
29
+ }
30
+ }
31
+
32
+ describe('Integration: Reparenting Observer Propagation', () => {
33
+ it('should update observer propagation after reparenting', () => {
34
+ // ============================================================
35
+ // PHASE 1: Setup - Create parent1, parent2, and child
36
+ // ============================================================
37
+ // ARRANGE: Create two root workflows (both have parent = null)
38
+ const parent1 = new SimpleWorkflow('Parent1');
39
+ const parent2 = new SimpleWorkflow('Parent2');
40
+
41
+ // ARRANGE: Create child attached to parent1 (via constructor)
42
+ const child = new SimpleWorkflow('Child', parent1);
43
+
44
+ // ASSERT: Verify initial state
45
+ expect(child.parent).toBe(parent1);
46
+ expect(parent1.children).toContain(child);
47
+ expect(parent2.children).not.toContain(child);
48
+
49
+ // ============================================================
50
+ // PHASE 2: Verify parent1 observer receives events
51
+ // ============================================================
52
+ // ARRANGE: Create observer for parent1
53
+ const parent1Events: WorkflowEvent[] = [];
54
+
55
+ const parent1Observer: WorkflowObserver = {
56
+ onLog: () => {}, // Empty - not testing logs
57
+ onEvent: (event) => parent1Events.push(event), // Capture events
58
+ onStateUpdated: () => {}, // Empty - not testing state updates
59
+ onTreeChanged: () => {}, // Empty - not testing tree changes
60
+ };
61
+
62
+ // ACT: Attach observer to parent1 (must be root workflow)
63
+ parent1.addObserver(parent1Observer);
64
+
65
+ // ACT: Child emits event (triggers treeUpdated via setStatus)
66
+ // PATTERN: setStatus() emits treeUpdated event
67
+ parent1Events.length = 0; // Clear any construction events
68
+ child.setStatus('running');
69
+
70
+ // ASSERT: Verify parent1 observer received the event
71
+ const parent1ReceivedEvent = parent1Events.find(
72
+ (e) => e.type === 'treeUpdated'
73
+ );
74
+ expect(parent1ReceivedEvent).toBeDefined();
75
+
76
+ // ============================================================
77
+ // PHASE 3: Reparent child from parent1 to parent2
78
+ // ============================================================
79
+ // ARRANGE: Create observer for parent2
80
+ const parent2Events: WorkflowEvent[] = [];
81
+
82
+ const parent2Observer: WorkflowObserver = {
83
+ onLog: () => {},
84
+ onEvent: (event) => parent2Events.push(event),
85
+ onStateUpdated: () => {},
86
+ onTreeChanged: () => {},
87
+ };
88
+
89
+ // ACT: Reparent using detach + attach pattern
90
+ // CRITICAL: Must detach before attaching to new parent
91
+ parent1.detachChild(child);
92
+ parent2.attachChild(child);
93
+ parent2.addObserver(parent2Observer);
94
+
95
+ // ASSERT: Verify reparenting succeeded
96
+ expect(child.parent).toBe(parent2);
97
+ expect(parent2.children).toContain(child);
98
+ expect(parent1.children).not.toContain(child);
99
+
100
+ // ============================================================
101
+ // PHASE 4: Verify parent2 observer receives events after reparenting
102
+ // ============================================================
103
+ // ACT: Clear events to isolate post-reparenting behavior
104
+ parent1Events.length = 0;
105
+ parent2Events.length = 0;
106
+
107
+ // ACT: Child emits event again
108
+ child.setStatus('completed');
109
+
110
+ // ASSERT: Verify parent2 observer received the event
111
+ const parent2ReceivedEvent = parent2Events.find(
112
+ (e) => e.type === 'treeUpdated'
113
+ );
114
+ expect(parent2ReceivedEvent).toBeDefined();
115
+
116
+ // ============================================================
117
+ // PHASE 5: CRITICAL VALIDATION - parent1 observer does NOT receive events
118
+ // ============================================================
119
+ // ASSERT: Verify parent1 observer did NOT receive the post-reparenting event
120
+ const parent1DidNotReceive = parent1Events.find(
121
+ (e) => e.type === 'treeUpdated'
122
+ );
123
+ expect(parent1DidNotReceive).toBeUndefined();
124
+
125
+ // ASSERT: Verify event count for parent1 is zero
126
+ expect(parent1Events.length).toBe(0);
127
+ });
128
+
129
+ it('should handle multiple reparenting cycles correctly', () => {
130
+ // ARRANGE: Create three potential parents
131
+ const parentA = new SimpleWorkflow('ParentA');
132
+ const parentB = new SimpleWorkflow('ParentB');
133
+ const parentC = new SimpleWorkflow('ParentC');
134
+
135
+ // ARRANGE: Create child and observers for each parent
136
+ const child = new SimpleWorkflow('Child', parentA);
137
+
138
+ const eventsA: WorkflowEvent[] = [];
139
+ const eventsB: WorkflowEvent[] = [];
140
+ const eventsC: WorkflowEvent[] = [];
141
+
142
+ const createObserver = (eventsArray: WorkflowEvent[]): WorkflowObserver => ({
143
+ onLog: () => {},
144
+ onEvent: (e) => eventsArray.push(e),
145
+ onStateUpdated: () => {},
146
+ onTreeChanged: () => {},
147
+ });
148
+
149
+ parentA.addObserver(createObserver(eventsA));
150
+ parentB.addObserver(createObserver(eventsB));
151
+ parentC.addObserver(createObserver(eventsC));
152
+
153
+ // ACT & ASSERT: Cycle 1 - A -> B
154
+ eventsA.length = 0;
155
+ child.setStatus('running');
156
+ expect(eventsA.some((e) => e.type === 'treeUpdated')).toBe(true);
157
+
158
+ parentA.detachChild(child);
159
+ parentB.attachChild(child);
160
+
161
+ eventsA.length = 0;
162
+ eventsB.length = 0;
163
+ child.setStatus('completed');
164
+ expect(eventsB.some((e) => e.type === 'treeUpdated')).toBe(true);
165
+ expect(eventsA.some((e) => e.type === 'treeUpdated')).toBe(false);
166
+
167
+ // ACT & ASSERT: Cycle 2 - B -> C
168
+ parentB.detachChild(child);
169
+ parentC.attachChild(child);
170
+
171
+ eventsB.length = 0;
172
+ eventsC.length = 0;
173
+ child.setStatus('running');
174
+ expect(eventsC.some((e) => e.type === 'treeUpdated')).toBe(true);
175
+ expect(eventsB.some((e) => e.type === 'treeUpdated')).toBe(false);
176
+
177
+ // ACT & ASSERT: Cycle 3 - C -> A (return to original)
178
+ parentC.detachChild(child);
179
+ parentA.attachChild(child);
180
+
181
+ eventsC.length = 0;
182
+ eventsA.length = 0;
183
+ child.setStatus('completed');
184
+ expect(eventsA.some((e) => e.type === 'treeUpdated')).toBe(true);
185
+ expect(eventsC.some((e) => e.type === 'treeUpdated')).toBe(false);
186
+ });
187
+
188
+ it('should verify tree consistency after reparenting using debugger', () => {
189
+ // ============================================================
190
+ // PHASE 1: Setup - Create parent1, parent2, and child
191
+ // ============================================================
192
+ // ARRANGE: Create two root workflows
193
+ const parent1 = new SimpleWorkflow('Parent1');
194
+ const parent2 = new SimpleWorkflow('Parent2');
195
+
196
+ // ARRANGE: Create child attached to parent1 (via constructor)
197
+ const child = new SimpleWorkflow('Child', parent1);
198
+
199
+ // ARRANGE: Create debugger attached to parent1
200
+ const parent1Debugger = new WorkflowTreeDebugger(parent1);
201
+
202
+ // ASSERT: Verify initial tree structure
203
+ expect(child.parent).toBe(parent1);
204
+ expect(parent1.children).toContain(child);
205
+ expect(parent2.children).not.toContain(child);
206
+
207
+ // ASSERT: Verify debugger sees initial structure
208
+ const initialTree = parent1Debugger.getTree();
209
+ expect(initialTree.name).toBe('Parent1');
210
+ expect(initialTree.children.length).toBe(1);
211
+ expect(initialTree.children[0].name).toBe('Child');
212
+
213
+ // ============================================================
214
+ // PHASE 2: Reparenting operation
215
+ // ============================================================
216
+ // ACT: Reparent child from parent1 to parent2
217
+ parent1.detachChild(child);
218
+ parent2.attachChild(child);
219
+
220
+ // ASSERT: Verify basic reparenting succeeded
221
+ expect(child.parent).toBe(parent2);
222
+ expect(parent2.children).toContain(child);
223
+ expect(parent1.children).not.toContain(child);
224
+
225
+ // ============================================================
226
+ // PHASE 3: Verify workflow tree structure
227
+ // ============================================================
228
+ // ASSERT: Verify child.parent points to new parent
229
+ expect(child.parent).toBe(parent2);
230
+
231
+ // ASSERT: Verify new parent.children includes child
232
+ expect(parent2.children).toContain(child);
233
+
234
+ // ASSERT: Verify old parent.children does NOT include child
235
+ expect(parent1.children).not.toContain(child);
236
+
237
+ // ============================================================
238
+ // PHASE 4: Verify node tree structure using debugger
239
+ // ============================================================
240
+ // NOTE: parent1Debugger is still attached to parent1, so it shows parent1's tree
241
+ // Create debugger for parent2 to see the new tree structure
242
+ const parent2Debugger = new WorkflowTreeDebugger(parent2);
243
+
244
+ // ASSERT: Verify debugger can find child node
245
+ const childNode = parent2Debugger.getNode(child.id);
246
+ expect(childNode).toBeDefined();
247
+ expect(childNode?.name).toBe('Child');
248
+
249
+ // ASSERT: Verify child's node parent is parent2's node (node tree mirrors workflow tree)
250
+ const parent2NodeDirect = parent2.getNode();
251
+ expect(childNode?.parent).toBe(parent2NodeDirect);
252
+
253
+ // ASSERT: Verify parent2's node children includes child's node
254
+ const parent2Node = parent2Debugger.getNode(parent2.id);
255
+ const childNodeDirect = child.getNode();
256
+ expect(parent2Node?.children).toContain(childNodeDirect);
257
+
258
+ // ASSERT: Verify parent1's node children does NOT include child's node
259
+ const parent1Node = parent1Debugger.getNode(parent1.id);
260
+ expect(parent1Node?.children).not.toContain(childNodeDirect);
261
+
262
+ // ============================================================
263
+ // PHASE 5: Visual validation using toTreeString()
264
+ // ============================================================
265
+ // ACT: Get tree string for visual debugging
266
+ const parent2TreeString = parent2Debugger.toTreeString();
267
+
268
+ // ASSERT: Verify tree structure in ASCII representation
269
+ expect(parent2TreeString).toContain('Parent2');
270
+ expect(parent2TreeString).toContain('Child');
271
+ expect(parent2TreeString).toContain('└──'); // Tree connector symbol
272
+
273
+ // ============================================================
274
+ // PHASE 6: Statistical validation
275
+ // ============================================================
276
+ const parent2Stats = parent2Debugger.getStats();
277
+ expect(parent2Stats.totalNodes).toBe(2); // parent2 + child = 2
278
+
279
+ // ============================================================
280
+ // CRITICAL VALIDATION: 1:1 Tree Mirror Invariant
281
+ // ============================================================
282
+ // Verify workflow tree and node tree are perfectly synchronized
283
+ // This is the core invariant that must be maintained
284
+
285
+ // Workflow tree state:
286
+ expect(child.parent).toBe(parent2);
287
+ expect(parent2.children).toEqual([child]);
288
+ expect(parent1.children).toEqual([]);
289
+
290
+ // Node tree state (via getNode() method):
291
+ const childNodeFinal = child.getNode();
292
+ const parent2NodeFinal = parent2.getNode();
293
+ const parent1NodeFinal = parent1.getNode();
294
+
295
+ expect(childNodeFinal.parent).toBe(parent2NodeFinal);
296
+ expect(parent2NodeFinal.children).toEqual([childNodeFinal]);
297
+ expect(parent1NodeFinal.children).toEqual([]);
298
+
299
+ // Cross-verification: debugger lookup matches direct access
300
+ expect(parent2Debugger.getNode(child.id)).toBe(childNodeFinal);
301
+ expect(parent2Debugger.getNode(parent2.id)).toBe(parent2NodeFinal);
302
+ });
303
+ });
@@ -9,6 +9,7 @@ import {
9
9
  type AgentExecutionContext,
10
10
  } from '../../core/context.js';
11
11
  import type { WorkflowNode, WorkflowEvent } from '../../types/index.js';
12
+ import { Workflow, WorkflowObserver, ObservedState } from '../../index.js';
12
13
 
13
14
  describe('AgentExecutionContext', () => {
14
15
  const createMockNode = (name: string): WorkflowNode => ({
@@ -136,3 +137,81 @@ describe('AgentExecutionContext', () => {
136
137
  expect(events[0].type).toBe('stepStart');
137
138
  });
138
139
  });
140
+
141
+ describe('WorkflowContext', () => {
142
+ // Test workflow class with @ObservedState decorated fields
143
+ class StatefulTestWorkflow extends Workflow {
144
+ @ObservedState()
145
+ stepCount: number = 0;
146
+
147
+ @ObservedState({ redact: true })
148
+ apiKey: string = 'secret-key-123';
149
+
150
+ @ObservedState({ hidden: true })
151
+ internalCounter: number = 42;
152
+ }
153
+
154
+ it('should capture state and logs in step() error handler', async () => {
155
+ // Arrange: Create observer to capture error events
156
+ const events: WorkflowEvent[] = [];
157
+
158
+ const observer: WorkflowObserver = {
159
+ onLog: () => {},
160
+ onEvent: (event) => events.push(event),
161
+ onStateUpdated: () => {},
162
+ onTreeChanged: () => {},
163
+ };
164
+
165
+ // Arrange: Create workflow with @ObservedState fields using functional executor
166
+ const workflow = new StatefulTestWorkflow(
167
+ { name: 'StepErrorTest' },
168
+ async (ctx) => {
169
+ // Modify @ObservedState fields on the workflow instance
170
+ (workflow as any).stepCount = 5;
171
+ (workflow as any).apiKey = 'updated-key';
172
+ (workflow as any).internalCounter = 99;
173
+
174
+ // Execute a step that will fail - THIS TRIGGERS WorkflowContext.step() ERROR HANDLER
175
+ await ctx.step('failing-step', async () => {
176
+ throw new Error('Test error from step');
177
+ });
178
+ }
179
+ );
180
+
181
+ // Act: Attach observer and trigger error
182
+ workflow.addObserver(observer);
183
+ await expect(workflow.run()).rejects.toThrow('Test error from step');
184
+
185
+ // Assert: Verify error event was emitted
186
+ const errorEvents = events.filter((e) => e.type === 'error');
187
+ expect(errorEvents.length).toBeGreaterThanOrEqual(1);
188
+
189
+ // Assert: Verify error structure
190
+ const errorEvent = errorEvents[0];
191
+ expect(errorEvent.error).toBeDefined();
192
+ expect(errorEvent.error.message).toBe('Test error from step');
193
+
194
+ // Assert: Verify @ObservedState fields were captured in state
195
+ expect(errorEvent.error.state).toBeDefined();
196
+ expect(typeof errorEvent.error.state).toBe('object');
197
+
198
+ // Assert: Verify public field value is captured
199
+ expect(errorEvent.error.state.stepCount).toBe(5);
200
+
201
+ // Assert: Verify redacted field shows '***'
202
+ expect(errorEvent.error.state.apiKey).toBe('***');
203
+
204
+ // Assert: Verify hidden field is NOT in state
205
+ expect('internalCounter' in errorEvent.error.state).toBe(false);
206
+
207
+ // Assert: Verify logs array is present (may be empty)
208
+ expect(errorEvent.error.logs).toBeDefined();
209
+ expect(Array.isArray(errorEvent.error.logs)).toBe(true);
210
+
211
+ // Assert: Verify workflow status
212
+ expect(workflow.status).toBe('failed');
213
+
214
+ // Assert: Verify workflowId is captured
215
+ expect(errorEvent.error.workflowId).toBe(workflow.id);
216
+ });
217
+ });
@@ -0,0 +1,293 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Workflow } from '../../core/workflow';
3
+ import type { LogEntry } from '../../types/logging';
4
+
5
+ describe('WorkflowLogger.child()', () => {
6
+ describe('with Partial<LogEntry> containing parentLogId', () => {
7
+ it('should create child logger with parentLogId from Partial<LogEntry>', async () => {
8
+ class TestWorkflow extends Workflow {
9
+ async run() {
10
+ const childLogger = this.logger.child({ parentLogId: 'parent-123' });
11
+ childLogger.info('Child message');
12
+ }
13
+ }
14
+
15
+ const workflow = new TestWorkflow();
16
+ await workflow.run();
17
+
18
+ expect(workflow.node.logs.length).toBe(1);
19
+ expect(workflow.node.logs[0].parentLogId).toBe('parent-123');
20
+ expect(workflow.node.logs[0].message).toBe('Child message');
21
+ });
22
+
23
+ it('should handle parentLogId with special characters', async () => {
24
+ class TestWorkflow extends Workflow {
25
+ async run() {
26
+ const childLogger = this.logger.child({ parentLogId: 'parent-with-dashes-and_underscores' });
27
+ childLogger.info('Test');
28
+ }
29
+ }
30
+
31
+ const workflow = new TestWorkflow();
32
+ await workflow.run();
33
+
34
+ expect(workflow.node.logs[0].parentLogId).toBe('parent-with-dashes-and_underscores');
35
+ });
36
+ });
37
+
38
+ describe('with Partial<LogEntry> containing id field', () => {
39
+ it('should not use id field as parentLogId', async () => {
40
+ class TestWorkflow extends Workflow {
41
+ async run() {
42
+ // id field should NOT be used as parentLogId
43
+ const childLogger = this.logger.child({ id: 'custom-id' });
44
+ childLogger.info('Test message');
45
+ }
46
+ }
47
+
48
+ const workflow = new TestWorkflow();
49
+ await workflow.run();
50
+
51
+ // parentLogId is undefined because implementation only checks input.parentLogId
52
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
53
+ expect(workflow.node.logs[0].message).toBe('Test message');
54
+ });
55
+
56
+ it('should handle both id and parentLogId fields', async () => {
57
+ class TestWorkflow extends Workflow {
58
+ async run() {
59
+ // parentLogId should be used, not id
60
+ const childLogger = this.logger.child({ id: 'custom-id', parentLogId: 'correct-parent' });
61
+ childLogger.info('Test');
62
+ }
63
+ }
64
+
65
+ const workflow = new TestWorkflow();
66
+ await workflow.run();
67
+
68
+ expect(workflow.node.logs[0].parentLogId).toBe('correct-parent');
69
+ });
70
+ });
71
+
72
+ describe('with empty Partial<LogEntry>', () => {
73
+ it('should create child logger with undefined parentLogId from empty object', async () => {
74
+ class TestWorkflow extends Workflow {
75
+ async run() {
76
+ const childLogger = this.logger.child({});
77
+ childLogger.info('Child log with empty parent metadata');
78
+ }
79
+ }
80
+
81
+ const workflow = new TestWorkflow();
82
+ await workflow.run();
83
+
84
+ expect(workflow.node.logs.length).toBe(1);
85
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
86
+ expect(workflow.node.logs[0].message).toBe('Child log with empty parent metadata');
87
+ });
88
+ });
89
+
90
+ describe('with string parameter (backward compatibility)', () => {
91
+ it('should create child logger with parentLogId from string', async () => {
92
+ class TestWorkflow extends Workflow {
93
+ async run() {
94
+ const childLogger = this.logger.child('parent-id-123');
95
+ childLogger.info('Child message');
96
+ }
97
+ }
98
+
99
+ const workflow = new TestWorkflow();
100
+ await workflow.run();
101
+
102
+ expect(workflow.node.logs.length).toBe(1);
103
+ expect(workflow.node.logs[0].parentLogId).toBe('parent-id-123');
104
+ expect(workflow.node.logs[0].message).toBe('Child message');
105
+ });
106
+
107
+ it('should create child logger with parentLogId from string containing parentLogId', async () => {
108
+ class TestWorkflow extends Workflow {
109
+ async run() {
110
+ // String value is used directly as parentLogId
111
+ const childLogger = this.logger.child('log-abc-123');
112
+ childLogger.warn('Warning message');
113
+ }
114
+ }
115
+
116
+ const workflow = new TestWorkflow();
117
+ await workflow.run();
118
+
119
+ expect(workflow.node.logs[0].parentLogId).toBe('log-abc-123');
120
+ expect(workflow.node.logs[0].level).toBe('warn');
121
+ });
122
+ });
123
+
124
+ describe('with empty string', () => {
125
+ it('should create child logger with undefined parentLogId from empty string', async () => {
126
+ class TestWorkflow extends Workflow {
127
+ async run() {
128
+ const childLogger = this.logger.child('');
129
+ childLogger.info('Child log with empty parent');
130
+ }
131
+ }
132
+
133
+ const workflow = new TestWorkflow();
134
+ await workflow.run();
135
+
136
+ expect(workflow.node.logs.length).toBe(1);
137
+ // Empty string is falsy, so parentLogId becomes undefined in log entry
138
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
139
+ });
140
+ });
141
+
142
+ describe('child logger with different log levels', () => {
143
+ it.each([
144
+ { level: 'debug', method: 'debug' as const },
145
+ { level: 'info', method: 'info' as const },
146
+ { level: 'warn', method: 'warn' as const },
147
+ { level: 'error', method: 'error' as const },
148
+ ])('should log at $level level with child logger', async ({ level, method }) => {
149
+ class TestWorkflow extends Workflow {
150
+ async run() {
151
+ const childLogger = this.logger.child({ parentLogId: 'parent-123' });
152
+ childLogger[method](`Test ${level} message`);
153
+ }
154
+ }
155
+
156
+ const workflow = new TestWorkflow();
157
+ await workflow.run();
158
+
159
+ expect(workflow.node.logs[0].level).toBe(level);
160
+ expect(workflow.node.logs[0].parentLogId).toBe('parent-123');
161
+ expect(workflow.node.logs[0].message).toBe(`Test ${level} message`);
162
+ });
163
+
164
+ it('should support logging with data parameter at all levels', async () => {
165
+ class TestWorkflow extends Workflow {
166
+ async run() {
167
+ const childLogger = this.logger.child({ parentLogId: 'parent-data' });
168
+
169
+ childLogger.debug('Debug message', { debugData: true });
170
+ childLogger.info('Info message', { infoData: 123 });
171
+ childLogger.warn('Warn message', { warnData: 'warning' });
172
+ childLogger.error('Error message', { errorData: { code: 500 } });
173
+ }
174
+ }
175
+
176
+ const workflow = new TestWorkflow();
177
+ await workflow.run();
178
+
179
+ expect(workflow.node.logs.length).toBe(4);
180
+ expect(workflow.node.logs[0].data).toEqual({ debugData: true });
181
+ expect(workflow.node.logs[1].data).toEqual({ infoData: 123 });
182
+ expect(workflow.node.logs[2].data).toEqual({ warnData: 'warning' });
183
+ expect(workflow.node.logs[3].data).toEqual({ errorData: { code: 500 } });
184
+ });
185
+ });
186
+
187
+ describe('parent-child log hierarchy', () => {
188
+ it('should maintain parent-child relationship in log entries', async () => {
189
+ class TestWorkflow extends Workflow {
190
+ async run() {
191
+ // Log from parent logger
192
+ this.logger.info('Parent message');
193
+
194
+ // Get the parent log entry ID
195
+ const parentLogId = this.node.logs[0].id;
196
+
197
+ // Create child logger with that ID
198
+ const childLogger = this.logger.child({ parentLogId });
199
+ childLogger.info('Child message');
200
+ }
201
+ }
202
+
203
+ const workflow = new TestWorkflow();
204
+ await workflow.run();
205
+
206
+ // Verify hierarchy
207
+ expect(workflow.node.logs.length).toBe(2);
208
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined(); // Root log
209
+ expect(workflow.node.logs[1].parentLogId).toBe(workflow.node.logs[0].id); // Child log
210
+ expect(workflow.node.logs[0].message).toBe('Parent message');
211
+ expect(workflow.node.logs[1].message).toBe('Child message');
212
+ });
213
+
214
+ it('should support multi-level nesting with child loggers', async () => {
215
+ class TestWorkflow extends Workflow {
216
+ async run() {
217
+ // Root log
218
+ this.logger.info('Root message');
219
+ const rootLogId = this.node.logs[0].id;
220
+
221
+ // First level child
222
+ const child1 = this.logger.child({ parentLogId: rootLogId });
223
+ child1.info('Level 1 child');
224
+ const level1LogId = this.node.logs[1].id;
225
+
226
+ // Second level child
227
+ const child2 = this.logger.child({ parentLogId: level1LogId });
228
+ child2.info('Level 2 child');
229
+ }
230
+ }
231
+
232
+ const workflow = new TestWorkflow();
233
+ await workflow.run();
234
+
235
+ expect(workflow.node.logs.length).toBe(3);
236
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
237
+ expect(workflow.node.logs[1].parentLogId).toBe(workflow.node.logs[0].id);
238
+ expect(workflow.node.logs[2].parentLogId).toBe(workflow.node.logs[1].id);
239
+ });
240
+
241
+ it('should support string-based parent-child hierarchy', async () => {
242
+ class TestWorkflow extends Workflow {
243
+ async run() {
244
+ // Log from parent
245
+ this.logger.info('Parent log');
246
+ const parentLogId = this.node.logs[0].id;
247
+
248
+ // Create child using string parentLogId (backward compatibility)
249
+ const childLogger = this.logger.child(parentLogId);
250
+ childLogger.info('Child log');
251
+ }
252
+ }
253
+
254
+ const workflow = new TestWorkflow();
255
+ await workflow.run();
256
+
257
+ expect(workflow.node.logs.length).toBe(2);
258
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
259
+ expect(workflow.node.logs[1].parentLogId).toBe(workflow.node.logs[0].id);
260
+ });
261
+
262
+ it('should allow multiple child loggers from same parent', async () => {
263
+ class TestWorkflow extends Workflow {
264
+ async run() {
265
+ // Parent log
266
+ this.logger.info('Parent');
267
+ const parentLogId = this.node.logs[0].id;
268
+
269
+ // Multiple children from same parent
270
+ const child1 = this.logger.child({ parentLogId });
271
+ child1.info('First child');
272
+
273
+ const child2 = this.logger.child({ parentLogId });
274
+ child2.info('Second child');
275
+
276
+ const child3 = this.logger.child({ parentLogId });
277
+ child3.info('Third child');
278
+ }
279
+ }
280
+
281
+ const workflow = new TestWorkflow();
282
+ await workflow.run();
283
+
284
+ expect(workflow.node.logs.length).toBe(4);
285
+ expect(workflow.node.logs[0].parentLogId).toBeUndefined();
286
+
287
+ // All children should have the same parent
288
+ expect(workflow.node.logs[1].parentLogId).toBe(workflow.node.logs[0].id);
289
+ expect(workflow.node.logs[2].parentLogId).toBe(workflow.node.logs[0].id);
290
+ expect(workflow.node.logs[3].parentLogId).toBe(workflow.node.logs[0].id);
291
+ });
292
+ });
293
+ });