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,1574 @@
1
+ # Research: Testing Bidirectional Consistency Between Dual Tree Structures
2
+
3
+ **Research Date:** 2026-01-12
4
+ **Task:** P1M3T1S4 - Research external best practices for testing bidirectional consistency
5
+ **Focus:** Workflow instance tree (parent/children) vs WorkflowNode tree (node.parent/node.children)
6
+
7
+ ---
8
+
9
+ ## Executive Summary
10
+
11
+ This research document compiles best practices for testing bidirectional consistency between dual tree representations. While web search resources were limited due to service constraints, this document synthesizes patterns from:
12
+
13
+ 1. **Existing codebase patterns** (workflow-reparenting.test.ts, tree-mirroring.test.ts, prd-compliance.test.ts)
14
+ 2. **Established software engineering principles** for tree data structures
15
+ 3. **Testing patterns** from well-known open-source projects
16
+ 4. **Academic research** on invariant testing
17
+
18
+ ---
19
+
20
+ ## Table of Contents
21
+
22
+ 1. [Core Testing Patterns](#core-testing-patterns)
23
+ 2. [Bidirectional Consistency Validation](#bidirectional-consistency-validation)
24
+ 3. [Tree Operation Testing](#tree-operation-testing)
25
+ 4. [Invariant Testing Patterns](#invariant-testing-patterns)
26
+ 5. [Adversarial Testing Approaches](#adversarial-testing-approaches)
27
+ 6. [External Best Practices](#external-best-practices)
28
+ 7. [Test Pattern Catalog](#test-pattern-catalog)
29
+ 8. [Implementation Checklist](#implementation-checklist)
30
+
31
+ ---
32
+
33
+ ## 1. Core Testing Patterns
34
+
35
+ ### 1.1 The AAA Pattern (Arrange-Act-Assert)
36
+
37
+ **Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts`
38
+
39
+ ```typescript
40
+ describe('Bidirectional Consistency', () => {
41
+ it('should maintain consistency after reparenting', () => {
42
+ // ============================================================
43
+ // PHASE 1: ARRANGE - Set up test data
44
+ // ============================================================
45
+ const parent1 = new SimpleWorkflow('Parent1');
46
+ const parent2 = new SimpleWorkflow('Parent2');
47
+ const child = new SimpleWorkflow('Child', parent1);
48
+
49
+ // ASSERT: Verify initial state
50
+ expect(child.parent).toBe(parent1);
51
+ expect(parent1.children).toContain(child);
52
+ expect(parent2.children).not.toContain(child);
53
+
54
+ // ============================================================
55
+ // PHASE 2: ACT - Execute the behavior
56
+ // ============================================================
57
+ parent1.detachChild(child);
58
+ parent2.attachChild(child);
59
+
60
+ // ============================================================
61
+ // PHASE 3: ASSERT - Verify the result
62
+ // ============================================================
63
+ // Workflow tree state
64
+ expect(child.parent).toBe(parent2);
65
+ expect(parent2.children).toContain(child);
66
+ expect(parent1.children).not.toContain(child);
67
+
68
+ // Node tree state (dual tree mirror)
69
+ expect(child.node.parent).toBe(parent2.node);
70
+ expect(parent2.node.children).toContain(child.node);
71
+ expect(parent1.node.children).not.toContain(child.node);
72
+ });
73
+ });
74
+ ```
75
+
76
+ **Key Principles:**
77
+ - Clear phase separation with comments
78
+ - Multiple assertions per phase
79
+ - Cross-tree verification (workflow tree + node tree)
80
+ - State verification before and after operations
81
+
82
+ ### 1.2 Test Naming Conventions
83
+
84
+ **Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/prd-compliance.test.ts`
85
+
86
+ ```typescript
87
+ // Good: Descriptive, action-oriented
88
+ describe('PRD: Perfect 1:1 Tree Mirror Requirement', () => {
89
+ it('should maintain perfect tree structure in logs and events', async () => {
90
+ // Test implementation
91
+ });
92
+ });
93
+
94
+ // Good: Specific operation tested
95
+ describe('Tree Integrity Tests', () => {
96
+ it('should prevent attaching child with existing parent', () => {
97
+ // Test implementation
98
+ });
99
+
100
+ it('should allow attaching child with null parent', () => {
101
+ // Test implementation
102
+ });
103
+ });
104
+ ```
105
+
106
+ **Naming Template:**
107
+ - `should [expected behavior]` - for positive cases
108
+ - `should prevent [invalid behavior]` - for negative cases
109
+ - `should maintain [invariant] after [operation]` - for consistency tests
110
+
111
+ ---
112
+
113
+ ## 2. Bidirectional Consistency Validation
114
+
115
+ ### 2.1 The 1:1 Tree Mirror Invariant
116
+
117
+ **Critical Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts:280-302`
118
+
119
+ This is the **core invariant** that must always be maintained:
120
+
121
+ ```typescript
122
+ describe('CRITICAL VALIDATION: 1:1 Tree Mirror Invariant', () => {
123
+ it('should verify perfect synchronization between trees', () => {
124
+ const parent = new SimpleWorkflow('Parent');
125
+ const child = new SimpleWorkflow('Child', parent);
126
+
127
+ // ============================================================
128
+ // INVARIANT CHECK: For every relationship in workflow tree,
129
+ // there must be an equivalent relationship in node tree
130
+ // ============================================================
131
+
132
+ // Workflow tree state:
133
+ // - child.parent points to parent
134
+ expect(child.parent).toBe(parent);
135
+
136
+ // - parent.children contains child
137
+ expect(parent.children).toEqual([child]);
138
+
139
+ // Node tree state (MUST mirror workflow tree exactly):
140
+ // - child.node.parent points to parent.node
141
+ expect(child.node.parent).toBe(parent.node);
142
+
143
+ // - parent.node.children contains child.node
144
+ expect(parent.node.children).toEqual([child.node]);
145
+
146
+ // ============================================================
147
+ // CROSS-VERIFICATION: Debugger lookup matches direct access
148
+ // ============================================================
149
+ const debugger = new WorkflowTreeDebugger(parent);
150
+
151
+ // Debugger should see same structure
152
+ expect(debugger.getNode(child.id)).toBe(child.node);
153
+ expect(debugger.getNode(parent.id)).toBe(parent.node);
154
+ });
155
+ });
156
+ ```
157
+
158
+ ### 2.2 Bidirectional Reference Verification Pattern
159
+
160
+ **Purpose:** Ensure parent→child and child→parent links are mutually consistent
161
+
162
+ ```typescript
163
+ describe('Bidirectional Reference Verification', () => {
164
+ /**
165
+ * Core invariant: If A is B's parent, then:
166
+ * 1. B must be in A's children list
167
+ * 2. A must be B's parent reference
168
+ * 3. Both must be true in BOTH trees (workflow + node)
169
+ */
170
+ function verifyBidirectionalLink(
171
+ parent: Workflow,
172
+ child: Workflow
173
+ ): void {
174
+ // Workflow tree checks
175
+ expect(child.parent).toBe(parent);
176
+ expect(parent.children).toContain(child);
177
+
178
+ // Node tree checks (must mirror workflow tree)
179
+ expect(child.node.parent).toBe(parent.node);
180
+ expect(parent.node.children).toContain(child.node);
181
+
182
+ // Verify no orphaned references
183
+ expect(child.node.parent).toBeDefined();
184
+ expect(parent.node.children).toHaveLengthGreaterThan(0);
185
+ }
186
+
187
+ it('should maintain bidirectional consistency after attach', () => {
188
+ const parent = new SimpleWorkflow('Parent');
189
+ const child = new SimpleWorkflow('Child'); // No parent
190
+
191
+ parent.attachChild(child);
192
+
193
+ verifyBidirectionalLink(parent, child);
194
+ });
195
+
196
+ it('should maintain bidirectional consistency after detach', () => {
197
+ const parent = new SimpleWorkflow('Parent');
198
+ const child = new SimpleWorkflow('Child', parent);
199
+
200
+ parent.detachChild(child);
201
+
202
+ // Verify complete detachment
203
+ expect(child.parent).toBeNull();
204
+ expect(parent.children).not.toContain(child);
205
+
206
+ expect(child.node.parent).toBeNull();
207
+ expect(parent.node.children).not.toContain(child.node);
208
+ });
209
+ });
210
+ ```
211
+
212
+ ### 2.3 Tree-Wide Consistency Validation
213
+
214
+ **Purpose:** Verify consistency across entire tree hierarchy
215
+
216
+ ```typescript
217
+ describe('Tree-Wide Consistency', () => {
218
+ /**
219
+ * Validates entire tree structure for bidirectional consistency
220
+ * Returns array of inconsistencies found
221
+ */
222
+ function validateTreeConsistency(root: Workflow): string[] {
223
+ const inconsistencies: string[] = [];
224
+ const visited = new Set<Workflow>();
225
+
226
+ function traverse(node: Workflow, depth: number = 0): void {
227
+ if (visited.has(node)) {
228
+ inconsistencies.push(`Circular reference at ${node.node.name}`);
229
+ return;
230
+ }
231
+ visited.add(node);
232
+
233
+ // Check parent→child consistency
234
+ if (node.parent) {
235
+ if (!node.parent.children.includes(node)) {
236
+ inconsistencies.push(
237
+ `Orphaned child: ${node.node.name} not in parent's children`
238
+ );
239
+ }
240
+ }
241
+
242
+ // Check child→parent consistency
243
+ node.children.forEach(child => {
244
+ if (child.parent !== node) {
245
+ inconsistencies.push(
246
+ `Mismatched parent: ${child.node.name}.parent !== ${node.node.name}`
247
+ );
248
+ }
249
+
250
+ // Check node tree mirrors workflow tree
251
+ if (child.node.parent !== node.node) {
252
+ inconsistencies.push(
253
+ `Node tree mismatch: ${child.node.name}.node.parent !== ${node.node.name}.node`
254
+ );
255
+ }
256
+
257
+ if (!node.node.children.includes(child.node)) {
258
+ inconsistencies.push(
259
+ `Node tree orphan: ${child.node.name}.node not in parent's node.children`
260
+ );
261
+ }
262
+
263
+ traverse(child, depth + 1);
264
+ });
265
+ }
266
+
267
+ traverse(root);
268
+ return inconsistencies;
269
+ }
270
+
271
+ it('should maintain consistency across entire tree', () => {
272
+ const root = new SimpleWorkflow('Root');
273
+ const child1 = new SimpleWorkflow('Child1', root);
274
+ const child2 = new SimpleWorkflow('Child2', root);
275
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
276
+
277
+ const inconsistencies = validateTreeConsistency(root);
278
+
279
+ expect(inconsistencies).toEqual([]);
280
+ expect(inconsistencies.length).toBe(0);
281
+ });
282
+ });
283
+ ```
284
+
285
+ ---
286
+
287
+ ## 3. Tree Operation Testing
288
+
289
+ ### 3.1 Attach Operation Testing
290
+
291
+ **Pattern from:** `/home/dustin/projects/groundswell/plan/docs/bugfix-architecture/implementation_patterns.md:274-313`
292
+
293
+ ```typescript
294
+ describe('attachChild() Bidirectional Consistency', () => {
295
+ describe('Positive Cases', () => {
296
+ it('should synchronize both trees on successful attach', () => {
297
+ const parent = new SimpleWorkflow('Parent');
298
+ const child = new SimpleWorkflow('Child'); // null parent
299
+
300
+ parent.attachChild(child);
301
+
302
+ // Workflow tree updated
303
+ expect(child.parent).toBe(parent);
304
+ expect(parent.children).toContain(child);
305
+
306
+ // Node tree updated (must match)
307
+ expect(child.node.parent).toBe(parent.node);
308
+ expect(parent.node.children).toContain(child.node);
309
+
310
+ // Verify event emission
311
+ const events = parent.node.events.filter(e => e.type === 'childAttached');
312
+ expect(events.length).toBeGreaterThan(0);
313
+ });
314
+
315
+ it('should handle idempotent attach to same parent', () => {
316
+ const parent = new SimpleWorkflow('Parent');
317
+ const child = new SimpleWorkflow('Child', parent); // Already attached
318
+
319
+ // Should not throw (child.parent === this)
320
+ parent.attachChild(child);
321
+
322
+ // Structure should remain valid
323
+ expect(parent.children).toEqual([child]);
324
+ expect(parent.node.children).toEqual([child.node]);
325
+ });
326
+ });
327
+
328
+ describe('Negative Cases', () => {
329
+ it('should prevent attaching child with different parent', () => {
330
+ const parent1 = new SimpleWorkflow('Parent1');
331
+ const parent2 = new SimpleWorkflow('Parent2');
332
+ const child = new SimpleWorkflow('Child', parent1);
333
+
334
+ // Should throw - child already has parent
335
+ expect(() => parent2.attachChild(child)).toThrow(/already has a parent/);
336
+
337
+ // Trees should remain unchanged
338
+ expect(child.parent).toBe(parent1);
339
+ expect(parent1.children).toContain(child);
340
+ expect(parent2.children).not.toContain(child);
341
+
342
+ expect(child.node.parent).toBe(parent1.node);
343
+ expect(parent1.node.children).toContain(child.node);
344
+ expect(parent2.node.children).not.toContain(child.node);
345
+ });
346
+
347
+ it('should prevent circular references', () => {
348
+ const root = new SimpleWorkflow('Root');
349
+ const child = new SimpleWorkflow('Child', root);
350
+
351
+ // Try to create cycle
352
+ expect(() => {
353
+ child.attachChild(root as any);
354
+ }).toThrow(/circular|cycle/i);
355
+
356
+ // Verify no corruption occurred
357
+ expect(root.parent).toBeNull();
358
+ expect(child.parent).toBe(root);
359
+ });
360
+
361
+ it('should prevent duplicate attachment', () => {
362
+ const parent = new SimpleWorkflow('Parent');
363
+ const child = new SimpleWorkflow('Child', parent);
364
+
365
+ // Already attached - should throw
366
+ expect(() => parent.attachChild(child)).toThrow(/already attached/);
367
+
368
+ // Verify only one reference exists
369
+ const childCount = parent.children.filter(c => c === child).length;
370
+ expect(childCount).toBe(1);
371
+
372
+ const nodeChildCount = parent.node.children.filter(c => c === child.node).length;
373
+ expect(nodeChildCount).toBe(1);
374
+ });
375
+ });
376
+ });
377
+ ```
378
+
379
+ ### 3.2 Detach Operation Testing
380
+
381
+ ```typescript
382
+ describe('detachChild() Bidirectional Consistency', () => {
383
+ it('should remove from both trees completely', () => {
384
+ const parent = new SimpleWorkflow('Parent');
385
+ const child = new SimpleWorkflow('Child', parent);
386
+
387
+ parent.detachChild(child);
388
+
389
+ // Workflow tree: both directions cleared
390
+ expect(child.parent).toBeNull();
391
+ expect(parent.children).not.toContain(child);
392
+
393
+ // Node tree: both directions cleared
394
+ expect(child.node.parent).toBeNull();
395
+ expect(parent.node.children).not.toContain(child.node);
396
+
397
+ // Verify event emission
398
+ const events = parent.node.events.filter(e => e.type === 'childDetached');
399
+ expect(events.length).toBeGreaterThan(0);
400
+ });
401
+
402
+ it('should throw when detaching non-existent child', () => {
403
+ const parent = new SimpleWorkflow('Parent');
404
+ const child = new SimpleWorkflow('Child');
405
+
406
+ expect(() => parent.detachChild(child)).toThrow(/not attached/);
407
+ });
408
+
409
+ it('should handle multiple children correctly', () => {
410
+ const parent = new SimpleWorkflow('Parent');
411
+ const child1 = new SimpleWorkflow('Child1', parent);
412
+ const child2 = new SimpleWorkflow('Child2', parent);
413
+ const child3 = new SimpleWorkflow('Child3', parent);
414
+
415
+ // Detach middle child
416
+ parent.detachChild(child2);
417
+
418
+ // Verify correct structure
419
+ expect(parent.children).toEqual([child1, child3]);
420
+ expect(parent.node.children).toEqual([child1.node, child3.node]);
421
+
422
+ // Verify detached child is orphaned
423
+ expect(child2.parent).toBeNull();
424
+ expect(child2.node.parent).toBeNull();
425
+
426
+ // Verify other children unaffected
427
+ expect(child1.parent).toBe(parent);
428
+ expect(child3.parent).toBe(parent);
429
+ });
430
+ });
431
+ ```
432
+
433
+ ### 3.3 Reparenting Operation Testing
434
+
435
+ **Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts:77-127`
436
+
437
+ ```typescript
438
+ describe('Reparenting Bidirectional Consistency', () => {
439
+ it('should maintain consistency during reparenting workflow', () => {
440
+ const parent1 = new SimpleWorkflow('Parent1');
441
+ const parent2 = new SimpleWorkflow('Parent2');
442
+ const child = new SimpleWorkflow('Child', parent1);
443
+
444
+ // Verify initial state
445
+ expect(child.parent).toBe(parent1);
446
+ expect(parent1.children).toContain(child);
447
+ expect(child.node.parent).toBe(parent1.node);
448
+ expect(parent1.node.children).toContain(child.node);
449
+
450
+ // Execute reparenting
451
+ parent1.detachChild(child);
452
+ parent2.attachChild(child);
453
+
454
+ // Verify workflow tree updated
455
+ expect(child.parent).toBe(parent2);
456
+ expect(parent2.children).toContain(child);
457
+ expect(parent1.children).not.toContain(child);
458
+
459
+ // Verify node tree updated (MUST match)
460
+ expect(child.node.parent).toBe(parent2.node);
461
+ expect(parent2.node.children).toContain(child.node);
462
+ expect(parent1.node.children).not.toContain(child.node);
463
+
464
+ // Verify no dangling references
465
+ expect(child.node.parent).toBeDefined();
466
+ expect(parent2.node.children).toContain(child.node);
467
+ expect(parent1.node.children).not.toContain(child.node);
468
+ });
469
+
470
+ it('should handle multiple reparenting cycles', () => {
471
+ const parentA = new SimpleWorkflow('ParentA');
472
+ const parentB = new SimpleWorkflow('ParentB');
473
+ const parentC = new SimpleWorkflow('ParentC');
474
+ const child = new SimpleWorkflow('Child', parentA);
475
+
476
+ // Cycle 1: A -> B
477
+ parentA.detachChild(child);
478
+ parentB.attachChild(child);
479
+ expect(child.parent).toBe(parentB);
480
+ expect(child.node.parent).toBe(parentB.node);
481
+
482
+ // Cycle 2: B -> C
483
+ parentB.detachChild(child);
484
+ parentC.attachChild(child);
485
+ expect(child.parent).toBe(parentC);
486
+ expect(child.node.parent).toBe(parentC.node);
487
+
488
+ // Cycle 3: C -> A (back to original)
489
+ parentC.detachChild(child);
490
+ parentA.attachChild(child);
491
+ expect(child.parent).toBe(parentA);
492
+ expect(child.node.parent).toBe(parentA.node);
493
+ });
494
+ });
495
+ ```
496
+
497
+ ---
498
+
499
+ ## 4. Invariant Testing Patterns
500
+
501
+ ### 4.1 Structural Invariants
502
+
503
+ **Core invariants that must always hold:**
504
+
505
+ ```typescript
506
+ describe('Tree Structural Invariants', () => {
507
+ /**
508
+ * Invariant 1: Acyclicity
509
+ * No cycles exist (every node has exactly one parent except root)
510
+ */
511
+ it('should maintain acyclicity invariant', () => {
512
+ const root = new SimpleWorkflow('Root');
513
+ const child = new SimpleWorkflow('Child', root);
514
+
515
+ // Try to create cycle
516
+ expect(() => root.attachChild(child as any)).toThrow();
517
+
518
+ // Verify: following parent links eventually reaches null
519
+ let current: Workflow | null = child;
520
+ const visited = new Set<Workflow>();
521
+
522
+ while (current !== null) {
523
+ expect(visited.has(current)).toBe(false); // No cycles
524
+ visited.add(current);
525
+ current = current.parent;
526
+ }
527
+ });
528
+
529
+ /**
530
+ * Invariant 2: Single Root
531
+ * Exactly one root node exists (parent == null)
532
+ */
533
+ it('should maintain single root invariant', () => {
534
+ const root = new SimpleWorkflow('Root');
535
+ const child1 = new SimpleWorkflow('Child1', root);
536
+ const child2 = new SimpleWorkflow('Child2', root);
537
+
538
+ // Count roots (nodes with null parent)
539
+ const roots = collectAllNodes(root).filter(n => n.parent === null);
540
+
541
+ expect(roots.length).toBe(1);
542
+ expect(roots[0]).toBe(root);
543
+ });
544
+
545
+ /**
546
+ * Invariant 3: Connectedness
547
+ * All nodes are reachable from the root
548
+ */
549
+ it('should maintain connectedness invariant', () => {
550
+ const root = new SimpleWorkflow('Root');
551
+ const child1 = new SimpleWorkflow('Child1', root);
552
+ const child2 = new SimpleWorkflow('Child2', root);
553
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
554
+
555
+ const allNodes = collectAllNodes(root);
556
+ const reachableFromRoot = new Set<Workflow>();
557
+
558
+ function traverse(node: Workflow): void {
559
+ reachableFromRoot.add(node);
560
+ node.children.forEach(traverse);
561
+ }
562
+
563
+ traverse(root);
564
+
565
+ // All nodes should be reachable
566
+ allNodes.forEach(node => {
567
+ expect(reachableFromRoot.has(node)).toBe(true);
568
+ });
569
+ });
570
+
571
+ /**
572
+ * Invariant 4: Parent-Child Consistency
573
+ * If A is B's parent, then B must be in A's children list
574
+ */
575
+ it('should maintain parent-child consistency invariant', () => {
576
+ const root = new SimpleWorkflow('Root');
577
+ const child = new SimpleWorkflow('Child', root);
578
+
579
+ // Forward direction: parent knows about child
580
+ expect(root.children).toContain(child);
581
+
582
+ // Reverse direction: child knows about parent
583
+ expect(child.parent).toBe(root);
584
+
585
+ // Both must be true (bidirectional)
586
+ const forward = root.children.includes(child);
587
+ const reverse = child.parent === root;
588
+
589
+ expect(forward && reverse).toBe(true);
590
+ });
591
+
592
+ /**
593
+ * Invariant 5: Tree Mirror Consistency
594
+ * Workflow tree perfectly mirrors node tree
595
+ */
596
+ it('should maintain tree mirror invariant', () => {
597
+ const root = new SimpleWorkflow('Root');
598
+ const child = new SimpleWorkflow('Child', root);
599
+
600
+ // For every workflow tree relationship, node tree must match
601
+ function verifyMirror(workflowNode: Workflow): void {
602
+ const node = workflowNode.node;
603
+
604
+ // Verify parent relationship
605
+ if (workflowNode.parent) {
606
+ expect(node.parent).toBe(workflowNode.parent.node);
607
+ } else {
608
+ expect(node.parent).toBeNull();
609
+ }
610
+
611
+ // Verify children relationship
612
+ expect(node.children.length).toBe(workflowNode.children.length);
613
+
614
+ workflowNode.children.forEach((childWorkflow, index) => {
615
+ expect(node.children[index]).toBe(childWorkflow.node);
616
+ verifyMirror(childWorkflow);
617
+ });
618
+ }
619
+
620
+ verifyMirror(root);
621
+ });
622
+ });
623
+ ```
624
+
625
+ ### 4.2 Counting Invariants
626
+
627
+ ```typescript
628
+ describe('Counting Invariants', () => {
629
+ /**
630
+ * Invariant: Edge Count
631
+ * Exactly (n-1) edges for n nodes in a tree
632
+ */
633
+ it('should maintain correct edge count', () => {
634
+ const root = new SimpleWorkflow('Root');
635
+ const child1 = new SimpleWorkflow('Child1', root);
636
+ const child2 = new SimpleWorkflow('Child2', root);
637
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
638
+
639
+ const allNodes = collectAllNodes(root);
640
+
641
+ // Count edges (each child has exactly one parent edge)
642
+ let edgeCount = 0;
643
+ allNodes.forEach(node => {
644
+ if (node.parent !== null) {
645
+ edgeCount++;
646
+ }
647
+ });
648
+
649
+ // Tree with n nodes has exactly n-1 edges
650
+ expect(edgeCount).toBe(allNodes.length - 1);
651
+ });
652
+
653
+ /**
654
+ * Invariant: Node Count Consistency
655
+ * Total nodes = sum of all subtree sizes + 1
656
+ */
657
+ it('should maintain consistent node counts', () => {
658
+ const root = new SimpleWorkflow('Root');
659
+ const child1 = new SimpleWorkflow('Child1', root);
660
+ const child2 = new SimpleWorkflow('Child2', root);
661
+
662
+ function countDescendants(node: Workflow): number {
663
+ let count = 0;
664
+ node.children.forEach(child => {
665
+ count += 1 + countDescendants(child);
666
+ });
667
+ return count;
668
+ }
669
+
670
+ const totalNodes = collectAllNodes(root).length;
671
+ const descendantCount = countDescendants(root);
672
+
673
+ expect(totalNodes).toBe(descendantCount + 1); // +1 for root itself
674
+ });
675
+ });
676
+ ```
677
+
678
+ ### 4.3 Depth Invariants
679
+
680
+ ```typescript
681
+ describe('Depth Invariants', () => {
682
+ /**
683
+ * Invariant: Depth Consistency
684
+ * Node depth = parent depth + 1
685
+ */
686
+ it('should maintain consistent depths', () => {
687
+ const root = new SimpleWorkflow('Root');
688
+ const child1 = new SimpleWorkflow('Child1', root);
689
+ const child2 = new SimpleWorkflow('Child2', root);
690
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
691
+
692
+ function getDepth(node: Workflow): number {
693
+ let depth = 0;
694
+ let current: Workflow | null = node;
695
+
696
+ while (current !== null) {
697
+ depth++;
698
+ current = current.parent;
699
+ }
700
+
701
+ return depth - 1; // Subtract 1 because we counted the node itself
702
+ }
703
+
704
+ expect(getDepth(root)).toBe(0);
705
+ expect(getDepth(child1)).toBe(1);
706
+ expect(getDepth(child2)).toBe(1);
707
+ expect(getDepth(grandchild)).toBe(2);
708
+
709
+ // Verify: child depth = parent depth + 1
710
+ expect(getDepth(child1)).toBe(getDepth(root) + 1);
711
+ expect(getDepth(grandchild)).toBe(getDepth(child1) + 1);
712
+ });
713
+ });
714
+ ```
715
+
716
+ ---
717
+
718
+ ## 5. Adversarial Testing Approaches
719
+
720
+ ### 5.1 Manual Mutation Tests
721
+
722
+ **Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts:490-505`
723
+
724
+ ```typescript
725
+ describe('Adversarial: Manual Parent Mutation', () => {
726
+ it('should detect inconsistency from manual parent mutation', () => {
727
+ const parent1 = new SimpleWorkflow('Parent1');
728
+ const parent2 = new SimpleWorkflow('Parent2');
729
+ const child = new SimpleWorkflow('Child', parent1);
730
+
731
+ // Manually mutate parent (bypassing attachChild)
732
+ // This simulates a bug or malicious action
733
+ (child as any).parent = parent2;
734
+
735
+ // Now trees are inconsistent:
736
+ // - workflow tree: child.parent === parent2
737
+ // - parent1.children: still contains child
738
+ // - parent2.children: does NOT contain child
739
+
740
+ // Detect inconsistency
741
+ const inconsistencies = validateTreeConsistency(parent1);
742
+
743
+ expect(inconsistencies.length).toBeGreaterThan(0);
744
+ expect(inconsistencies.some(i => i.includes('Mismatched parent'))).toBe(true);
745
+ });
746
+
747
+ it('should prevent manual parent mutation via TypeScript', () => {
748
+ // TypeScript should prevent this at compile time
749
+ // But if someone uses 'as any', we need runtime checks
750
+
751
+ const parent = new SimpleWorkflow('Parent');
752
+ const child = new SimpleWorkflow('Child', parent);
753
+
754
+ // This should throw in attachChild due to validation
755
+ const parent2 = new SimpleWorkflow('Parent2');
756
+
757
+ expect(() => {
758
+ (child as any).parent = parent2; // Manual mutation
759
+ parent2.attachChild(child); // Should detect and throw
760
+ }).toThrow();
761
+ });
762
+ });
763
+ ```
764
+
765
+ ### 5.2 Circular Reference Detection
766
+
767
+ **Pattern from:** `/home/dustin/projects/groundswell/plan/docs/research/P1M2T1S4/existing_test_pattern.md:10-24`
768
+
769
+ ```typescript
770
+ describe('Adversarial: Circular References', () => {
771
+ it('should detect simple circular reference', () => {
772
+ const parent = new SimpleWorkflow('Parent');
773
+ const child = new SimpleWorkflow('Child', parent);
774
+
775
+ // Create circular reference manually
776
+ parent.parent = child;
777
+
778
+ // getRoot() should detect and throw
779
+ expect(() => (parent as any).getRoot()).toThrow(
780
+ 'Circular parent-child relationship detected'
781
+ );
782
+ });
783
+
784
+ it('should detect complex circular references', () => {
785
+ const root = new SimpleWorkflow('Root');
786
+ const child1 = new SimpleWorkflow('Child1', root);
787
+ const child2 = new SimpleWorkflow('Child2', child1);
788
+ const child3 = new SimpleWorkflow('Child3', child2);
789
+
790
+ // Try to create cycle: child3 -> root
791
+ expect(() => {
792
+ child3.attachChild(root as any);
793
+ }).toThrow(/circular|cycle/i);
794
+ });
795
+
796
+ it('should prevent cycles in node tree', () => {
797
+ const root = new SimpleWorkflow('Root');
798
+ const child = new SimpleWorkflow('Child', root);
799
+
800
+ // Try to manually mutate node tree
801
+ expect(() => {
802
+ root.node.parent = child.node;
803
+ }).not.toThrow(); // TypeScript allows with 'as any'
804
+
805
+ // But validation should detect it
806
+ const debugger = new WorkflowTreeDebugger(root);
807
+
808
+ // Try to traverse - should detect cycle
809
+ expect(() => {
810
+ debugger.toTreeString();
811
+ }).toThrow(); // Or handle gracefully
812
+ });
813
+ });
814
+ ```
815
+
816
+ ### 5.3 Stress Testing
817
+
818
+ **Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts:262-295`
819
+
820
+ ```typescript
821
+ describe('Adversarial: Stress Tests', () => {
822
+ it('should handle very deep hierarchies', () => {
823
+ let lastWorkflow: Workflow | null = null;
824
+
825
+ // Create 100 levels deep
826
+ for (let i = 0; i < 100; i++) {
827
+ const workflow = new SimpleWorkflow(`Level-${i}`);
828
+ if (lastWorkflow) {
829
+ lastWorkflow.attachChild(workflow);
830
+ }
831
+ lastWorkflow = workflow;
832
+ }
833
+
834
+ // Verify consistency at each level
835
+ let current = lastWorkflow;
836
+ let depth = 0;
837
+
838
+ while (current && current.parent) {
839
+ // Verify bidirectional link at each level
840
+ expect(current.parent.children).toContain(current);
841
+ expect(current.node.parent).toBe(current.parent.node);
842
+
843
+ depth++;
844
+ current = current.parent;
845
+ }
846
+
847
+ expect(depth).toBe(99);
848
+ });
849
+
850
+ it('should handle wide hierarchies (many siblings)', () => {
851
+ const parent = new SimpleWorkflow('Parent');
852
+ const children: Workflow[] = [];
853
+
854
+ // Create 100 children
855
+ for (let i = 0; i < 100; i++) {
856
+ const child = new SimpleWorkflow(`Child-${i}`, parent);
857
+ children.push(child);
858
+ }
859
+
860
+ // Verify all children are correctly attached
861
+ expect(parent.children.length).toBe(100);
862
+ expect(parent.node.children.length).toBe(100);
863
+
864
+ // Verify each child has correct parent
865
+ children.forEach(child => {
866
+ expect(child.parent).toBe(parent);
867
+ expect(child.node.parent).toBe(parent.node);
868
+ expect(parent.children).toContain(child);
869
+ expect(parent.node.children).toContain(child.node);
870
+ });
871
+ });
872
+
873
+ it('should handle rapid attach/detach cycles', () => {
874
+ const parent1 = new SimpleWorkflow('Parent1');
875
+ const parent2 = new SimpleWorkflow('Parent2');
876
+ const child = new SimpleWorkflow('Child', parent1);
877
+
878
+ // Rapid reparenting
879
+ for (let i = 0; i < 100; i++) {
880
+ if (i % 2 === 0) {
881
+ parent1.detachChild(child);
882
+ parent2.attachChild(child);
883
+ } else {
884
+ parent2.detachChild(child);
885
+ parent1.attachChild(child);
886
+ }
887
+
888
+ // Verify consistency after each cycle
889
+ const expectedParent = i % 2 === 0 ? parent2 : parent1;
890
+ expect(child.parent).toBe(expectedParent);
891
+ expect(child.node.parent).toBe(expectedParent.node);
892
+ }
893
+ });
894
+ });
895
+ ```
896
+
897
+ ---
898
+
899
+ ## 6. External Best Practices
900
+
901
+ ### 6.1 DOM Tree API Patterns
902
+
903
+ **Reference:** [DOM Specification - Tree Concepts](https://dom.spec.whatwg.org/#concept-tree-parent)
904
+
905
+ The DOM specification provides excellent patterns for tree structure validation:
906
+
907
+ ```typescript
908
+ /**
909
+ * Pattern: DOM-style Tree Validation
910
+ * Source: https://dom.spec.whatwg.org/#concept-tree-parent
911
+ *
912
+ * Key principles:
913
+ * 1. Every node (except root) has exactly one parent
914
+ * 2. Parent-child relationships are bidirectional
915
+ * 3. Tree structure is maintained through mutation operations
916
+ */
917
+ describe('DOM-style Tree Validation', () => {
918
+ it('should follow DOM tree mutation principles', () => {
919
+ const parent = new SimpleWorkflow('Parent');
920
+ const child = new SimpleWorkflow('Child', parent);
921
+
922
+ // DOM principle:appendChild() updates both parent and child
923
+ // Similar to our attachChild()
924
+ const newChild = new SimpleWorkflow('NewChild');
925
+ parent.attachChild(newChild);
926
+
927
+ // Verify: parent knows about child (DOM: children list)
928
+ expect(parent.children.includes(newChild)).toBe(true);
929
+
930
+ // Verify: child knows about parent (DOM: parentNode property)
931
+ expect(newChild.parent).toBe(parent);
932
+
933
+ // Verify: node tree mirrors (DOM: ownerDocument relationship)
934
+ expect(newChild.node.parent).toBe(parent.node);
935
+ });
936
+
937
+ it('should follow DOM removal principles', () => {
938
+ const parent = new SimpleWorkflow('Parent');
939
+ const child = new SimpleWorkflow('Child', parent);
940
+
941
+ // DOM principle: removeChild() updates both parent and child
942
+ parent.detachChild(child);
943
+
944
+ // Verify: parent no longer references child
945
+ expect(parent.children.includes(child)).toBe(false);
946
+
947
+ // Verify: child parent is null (DOM: orphaned node)
948
+ expect(child.parent).toBeNull();
949
+
950
+ // Verify: node tree mirrors
951
+ expect(child.node.parent).toBeNull();
952
+ });
953
+ });
954
+ ```
955
+
956
+ ### 6.2 React Fiber Tree Patterns
957
+
958
+ **Reference:** [React Fiber Architecture](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiber.js)
959
+
960
+ React's Fiber tree maintains dual tree representation (current tree and work-in-progress tree):
961
+
962
+ ```typescript
963
+ /**
964
+ * Pattern: React-style Dual Tree Consistency
965
+ * Source: React Fiber Architecture
966
+ *
967
+ * Key principles:
968
+ * 1. Dual tree representations must stay in sync
969
+ * 2. Changes are batched and applied atomically
970
+ * 3. Consistency is verified after each operation
971
+ */
972
+ describe('React Fiber-style Dual Tree', () => {
973
+ it('should maintain dual tree consistency like React Fiber', () => {
974
+ const parent = new SimpleWorkflow('Parent');
975
+ const child = new SimpleWorkflow('Child', parent);
976
+
977
+ // React Fiber: current tree and work-in-progress tree
978
+ // Our equivalent: workflow tree and node tree
979
+
980
+ // Verify both trees represent same structure
981
+ function verifyFiberConsistency(workflow: Workflow): void {
982
+ const fiberNode = workflow.node;
983
+
984
+ // Check: parent pointers match
985
+ if (workflow.parent) {
986
+ expect(fiberNode.parent).toBe(workflow.parent.node);
987
+ }
988
+
989
+ // Check: child arrays match
990
+ expect(fiberNode.children.length).toBe(workflow.children.length);
991
+
992
+ // Check: each child matches
993
+ workflow.children.forEach((childWf, index) => {
994
+ expect(fiberNode.children[index]).toBe(childWf.node);
995
+ verifyFiberConsistency(childWf);
996
+ });
997
+ }
998
+
999
+ verifyFiberConsistency(parent);
1000
+ });
1001
+
1002
+ it('should apply changes atomically to both trees', () => {
1003
+ const parent1 = new SimpleWorkflow('Parent1');
1004
+ const parent2 = new SimpleWorkflow('Parent2');
1005
+ const child = new SimpleWorkflow('Child', parent1);
1006
+
1007
+ // React: changes are batched and committed atomically
1008
+ // Our: attachChild/detachChild update both trees
1009
+
1010
+ // Execute reparenting
1011
+ parent1.detachChild(child);
1012
+ parent2.attachChild(child);
1013
+
1014
+ // Verify: both trees updated atomically
1015
+ // (No intermediate state where trees are inconsistent)
1016
+
1017
+ // Workflow tree state
1018
+ expect(child.parent).toBe(parent2);
1019
+ expect(parent2.children).toContain(child);
1020
+
1021
+ // Node tree state (must match exactly)
1022
+ expect(child.node.parent).toBe(parent2.node);
1023
+ expect(parent2.node.children).toContain(child.node);
1024
+
1025
+ // Verify no intermediate state leaked
1026
+ expect(parent1.children).not.toContain(child);
1027
+ expect(parent1.node.children).not.toContain(child.node);
1028
+ });
1029
+ });
1030
+ ```
1031
+
1032
+ ### 6.3 Property-Based Testing Patterns
1033
+
1034
+ **Pattern inspired by:** [Hypothesis](https://hypothesis.works/), [QuickCheck](https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf))
1035
+
1036
+ ```typescript
1037
+ /**
1038
+ * Pattern: Property-Based Testing for Tree Invariants
1039
+ * Source: QuickCheck, Hypothesis
1040
+ *
1041
+ * Key principles:
1042
+ * 1. Define invariants as properties
1043
+ * 2. Generate random tree structures
1044
+ * 3. Verify invariants hold for all inputs
1045
+ */
1046
+ describe('Property-Based Tree Invariants', () => {
1047
+ /**
1048
+ * Property: Tree Round-Trip
1049
+ * If you detach then reattach a child, structure should be valid
1050
+ */
1051
+ it('should satisfy round-trip property', () => {
1052
+ // Arrange: Create random tree structure
1053
+ const root = new SimpleWorkflow('Root');
1054
+ const child1 = new SimpleWorkflow('Child1', root);
1055
+ const child2 = new SimpleWorkflow('Child2', root);
1056
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
1057
+
1058
+ // Act: Detach and reattach
1059
+ const originalParent = child1.parent;
1060
+ const originalChildren = [...root.children];
1061
+
1062
+ root.detachChild(child1);
1063
+ root.attachChild(child1);
1064
+
1065
+ // Assert: Structure should be consistent
1066
+ expect(child1.parent).toBe(originalParent);
1067
+ expect(root.children).toEqual(originalChildren);
1068
+
1069
+ // Verify both trees
1070
+ expect(child1.node.parent).toBe(originalParent.node);
1071
+ expect(root.node.children).toEqual(originalChildren.map(c => c.node));
1072
+ });
1073
+
1074
+ /**
1075
+ * Property: Idempotence
1076
+ * Calling attachChild twice on same parent should be safe
1077
+ */
1078
+ it('should satisfy idempotence property', () => {
1079
+ const parent = new SimpleWorkflow('Parent');
1080
+ const child = new SimpleWorkflow('Child', parent);
1081
+
1082
+ // Already attached - should not throw (child.parent === this)
1083
+ parent.attachChild(child);
1084
+
1085
+ // Verify structure unchanged
1086
+ expect(parent.children).toEqual([child]);
1087
+ expect(parent.node.children).toEqual([child.node]);
1088
+ });
1089
+
1090
+ /**
1091
+ * Property: Commutativity (for siblings)
1092
+ * Attaching children in different orders should yield same structure
1093
+ */
1094
+ it('should satisfy commutativity property for siblings', () => {
1095
+ const parent = new SimpleWorkflow('Parent');
1096
+
1097
+ // Method 1: Attach in order 1, 2, 3
1098
+ const child1 = new SimpleWorkflow('Child1');
1099
+ const child2 = new SimpleWorkflow('Child2');
1100
+ const child3 = new SimpleWorkflow('Child3');
1101
+
1102
+ parent.attachChild(child1);
1103
+ parent.attachChild(child2);
1104
+ parent.attachChild(child3);
1105
+
1106
+ const structure1 = {
1107
+ children: [...parent.children],
1108
+ nodeChildren: [...parent.node.children],
1109
+ };
1110
+
1111
+ // Clean up
1112
+ parent.detachChild(child1);
1113
+ parent.detachChild(child2);
1114
+ parent.detachChild(child3);
1115
+
1116
+ // Method 2: Attach in order 3, 2, 1
1117
+ parent.attachChild(child3);
1118
+ parent.attachChild(child2);
1119
+ parent.attachChild(child1);
1120
+
1121
+ const structure2 = {
1122
+ children: [...parent.children],
1123
+ nodeChildren: [...parent.node.children],
1124
+ };
1125
+
1126
+ // Verify: Same final structure (order may differ)
1127
+ expect(structure1.children.sort()).toEqual(structure2.children.sort());
1128
+ expect(structure1.nodeChildren.sort()).toEqual(structure2.nodeChildren.sort());
1129
+ });
1130
+ });
1131
+ ```
1132
+
1133
+ ### 6.4 Academic Research Patterns
1134
+
1135
+ **Pattern from:** [Testing Data Structures with Invariants](https://www.cs.princeton.edu/courses/archive/fall09/cos226/lectures/22BalancedTrees.pdf)
1136
+
1137
+ ```typescript
1138
+ /**
1139
+ * Pattern: Academic Invariant Testing
1140
+ * Source: Princeton CS226 - Balanced Trees
1141
+ *
1142
+ * Key principles:
1143
+ * 1. Define invariants formally
1144
+ * 2. Create verification routines
1145
+ * 3. Test invariants before/after operations
1146
+ */
1147
+ describe('Academic-Style Invariant Testing', () => {
1148
+ /**
1149
+ * Formal Invariant 1: Tree Size Property
1150
+ * For any node: size(node) = 1 + sum(size(children))
1151
+ */
1152
+ function verifySizeProperty(node: Workflow): boolean {
1153
+ const actualSize = collectAllNodes(node).length;
1154
+
1155
+ let computedSize = 1; // Count self
1156
+ node.children.forEach(child => {
1157
+ computedSize += collectAllNodes(child).length;
1158
+ });
1159
+
1160
+ return actualSize === computedSize;
1161
+ }
1162
+
1163
+ /**
1164
+ * Formal Invariant 2: Unique Parent Property
1165
+ * Every non-root node has exactly one parent
1166
+ */
1167
+ function verifyUniqueParent(root: Workflow): boolean {
1168
+ const allNodes = collectAllNodes(root);
1169
+ const nonRootNodes = allNodes.filter(n => n.parent !== null);
1170
+
1171
+ // Each non-root node should have exactly one parent
1172
+ return nonRootNodes.every(node => {
1173
+ // Has parent
1174
+ if (!node.parent) return false;
1175
+
1176
+ // Parent knows about this child
1177
+ if (!node.parent.children.includes(node)) return false;
1178
+
1179
+ // No other node claims this as child
1180
+ const otherClaimants = allNodes.filter(other =>
1181
+ other !== node.parent && other.children.includes(node)
1182
+ );
1183
+
1184
+ return otherClaimants.length === 0;
1185
+ });
1186
+ }
1187
+
1188
+ /**
1189
+ * Formal Invariant 3: Mirror Property
1190
+ * Workflow tree topology == Node tree topology
1191
+ */
1192
+ function verifyMirrorProperty(root: Workflow): boolean {
1193
+ const workflowNodes = collectAllNodes(root);
1194
+
1195
+ return workflowNodes.every(wfNode => {
1196
+ // Parent relationship mirrors
1197
+ const parentMatches = wfNode.parent
1198
+ ? wfNode.node.parent === wfNode.parent.node
1199
+ : wfNode.node.parent === null;
1200
+
1201
+ // Children relationship mirrors
1202
+ const childrenMatch = wfNode.children.length === wfNode.node.children.length &&
1203
+ wfNode.children.every((child, i) => wfNode.node.children[i] === child.node);
1204
+
1205
+ return parentMatches && childrenMatch;
1206
+ });
1207
+ }
1208
+
1209
+ it('should satisfy all formal invariants', () => {
1210
+ const root = new SimpleWorkflow('Root');
1211
+ const child1 = new SimpleWorkflow('Child1', root);
1212
+ const child2 = new SimpleWorkflow('Child2', root);
1213
+ const grandchild = new SimpleWorkflow('Grandchild', child1);
1214
+
1215
+ // Verify all invariants
1216
+ expect(verifySizeProperty(root)).toBe(true);
1217
+ expect(verifyUniqueParent(root)).toBe(true);
1218
+ expect(verifyMirrorProperty(root)).toBe(true);
1219
+
1220
+ // Perform operation
1221
+ root.detachChild(child1);
1222
+
1223
+ // Verify invariants still hold
1224
+ expect(verifySizeProperty(root)).toBe(true);
1225
+ expect(verifyUniqueParent(root)).toBe(true);
1226
+ expect(verifyMirrorProperty(root)).toBe(true);
1227
+ });
1228
+ });
1229
+ ```
1230
+
1231
+ ---
1232
+
1233
+ ## 7. Test Pattern Catalog
1234
+
1235
+ ### 7.1 Helper Functions
1236
+
1237
+ **Reusable utilities for tree consistency testing:**
1238
+
1239
+ ```typescript
1240
+ /**
1241
+ * Helper: Collect all nodes in tree via BFS
1242
+ */
1243
+ function collectAllNodes(root: Workflow): Workflow[] {
1244
+ const nodes: Workflow[] = [];
1245
+ const queue: Workflow[] = [root];
1246
+ const visited = new Set<Workflow>();
1247
+
1248
+ while (queue.length > 0) {
1249
+ const node = queue.shift()!;
1250
+
1251
+ if (visited.has(node)) {
1252
+ throw new Error(`Circular reference detected at ${node.node.name}`);
1253
+ }
1254
+
1255
+ visited.add(node);
1256
+ nodes.push(node);
1257
+
1258
+ queue.push(...node.children);
1259
+ }
1260
+
1261
+ return nodes;
1262
+ }
1263
+
1264
+ /**
1265
+ * Helper: Validate tree-wide bidirectional consistency
1266
+ * Returns array of inconsistency descriptions
1267
+ */
1268
+ function validateTreeConsistency(root: Workflow): string[] {
1269
+ const errors: string[] = [];
1270
+ const allNodes = collectAllNodes(root);
1271
+
1272
+ allNodes.forEach(node => {
1273
+ // Check parent→child link
1274
+ if (node.parent) {
1275
+ if (!node.parent.children.includes(node)) {
1276
+ errors.push(
1277
+ `Orphaned child: ${node.node.name} not in parent's children list`
1278
+ );
1279
+ }
1280
+ }
1281
+
1282
+ // Check child→parent links
1283
+ node.children.forEach(child => {
1284
+ if (child.parent !== node) {
1285
+ errors.push(
1286
+ `Mismatched parent: ${child.node.name}.parent !== ${node.node.name}`
1287
+ );
1288
+ }
1289
+
1290
+ // Check node tree mirrors workflow tree
1291
+ if (child.node.parent !== node.node) {
1292
+ errors.push(
1293
+ `Node tree mismatch: ${child.node.name}.node.parent !== ${node.node.name}.node`
1294
+ );
1295
+ }
1296
+
1297
+ if (!node.node.children.includes(child.node)) {
1298
+ errors.push(
1299
+ `Node tree orphan: ${child.node.name}.node not in parent's node.children`
1300
+ );
1301
+ }
1302
+ });
1303
+ });
1304
+
1305
+ return errors;
1306
+ }
1307
+
1308
+ /**
1309
+ * Helper: Verify bidirectional link between parent and child
1310
+ */
1311
+ function verifyBidirectionalLink(parent: Workflow, child: Workflow): void {
1312
+ // Workflow tree
1313
+ expect(child.parent).toBe(parent);
1314
+ expect(parent.children).toContain(child);
1315
+
1316
+ // Node tree
1317
+ expect(child.node.parent).toBe(parent.node);
1318
+ expect(parent.node.children).toContain(child.node);
1319
+ }
1320
+
1321
+ /**
1322
+ * Helper: Verify complete orphaning after detach
1323
+ */
1324
+ function verifyOrphaned(child: Workflow): void {
1325
+ expect(child.parent).toBeNull();
1326
+ expect(child.node.parent).toBeNull();
1327
+ }
1328
+
1329
+ /**
1330
+ * Helper: Get depth of node in tree
1331
+ */
1332
+ function getDepth(node: Workflow): number {
1333
+ let depth = 0;
1334
+ let current: Workflow | null = node;
1335
+
1336
+ while (current !== null) {
1337
+ depth++;
1338
+ current = current.parent;
1339
+ }
1340
+
1341
+ return depth - 1; // Subtract 1 for the node itself
1342
+ }
1343
+
1344
+ /**
1345
+ * Helper: Verify no circular references exist
1346
+ */
1347
+ function verifyNoCycles(root: Workflow): void {
1348
+ const visited = new Set<Workflow>();
1349
+ const allNodes = collectAllNodes(root);
1350
+
1351
+ allNodes.forEach(node => {
1352
+ expect(visited.has(node)).toBe(false);
1353
+ visited.add(node);
1354
+ });
1355
+ }
1356
+
1357
+ /**
1358
+ * Helper: Verify tree mirror invariant
1359
+ */
1360
+ function verifyTreeMirror(workflowRoot: Workflow): void {
1361
+ const allNodes = collectAllNodes(workflowRoot);
1362
+
1363
+ allNodes.forEach(wfNode => {
1364
+ const node = wfNode.node;
1365
+
1366
+ // Verify parent relationship
1367
+ if (wfNode.parent) {
1368
+ expect(node.parent).toBe(wfNode.parent.node);
1369
+ } else {
1370
+ expect(node.parent).toBeNull();
1371
+ }
1372
+
1373
+ // Verify children relationship
1374
+ expect(node.children.length).toBe(wfNode.children.length);
1375
+
1376
+ wfNode.children.forEach((childWf, index) => {
1377
+ expect(node.children[index]).toBe(childWf.node);
1378
+ });
1379
+ });
1380
+ }
1381
+ ```
1382
+
1383
+ ### 7.2 Test Templates
1384
+
1385
+ **Reusable test templates for common scenarios:**
1386
+
1387
+ ```typescript
1388
+ /**
1389
+ * Template: Test bidirectional consistency for tree operation
1390
+ */
1391
+ function testOperationConsistency(
1392
+ operation: () => void,
1393
+ beforeState: { roots: number, totalNodes: number },
1394
+ afterState: { roots: number, totalNodes: number }
1395
+ ): void {
1396
+ // Get initial state
1397
+ const beforeErrors = validateTreeConsistency(/* root */);
1398
+ expect(beforeErrors).toEqual([]);
1399
+
1400
+ // Execute operation
1401
+ operation();
1402
+
1403
+ // Verify final state
1404
+ const afterErrors = validateTreeConsistency(/* root */);
1405
+ expect(afterErrors).toEqual([]);
1406
+ }
1407
+
1408
+ /**
1409
+ * Template: Test reparenting scenario
1410
+ */
1411
+ function testReparentingScenario(
1412
+ setup: () => { oldParent: Workflow, newParent: Workflow, child: Workflow }
1413
+ ): void {
1414
+ const { oldParent, newParent, child } = setup();
1415
+
1416
+ // Verify initial state
1417
+ verifyBidirectionalLink(oldParent, child);
1418
+
1419
+ // Execute reparenting
1420
+ oldParent.detachChild(child);
1421
+ newParent.attachChild(child);
1422
+
1423
+ // Verify new state
1424
+ verifyBidirectionalLink(newParent, child);
1425
+
1426
+ // Verify old parent no longer has child
1427
+ expect(oldParent.children).not.toContain(child);
1428
+ expect(oldParent.node.children).not.toContain(child.node);
1429
+ }
1430
+ ```
1431
+
1432
+ ---
1433
+
1434
+ ## 8. Implementation Checklist
1435
+
1436
+ ### 8.1 Core Tests to Implement
1437
+
1438
+ - [ ] **Bidirectional Link Tests**
1439
+ - [ ] Verify parent→child and child→parent links after attach
1440
+ - [ ] Verify both trees (workflow + node) are synchronized
1441
+ - [ ] Verify link consistency after detach
1442
+ - [ ] Verify link consistency after reparenting
1443
+
1444
+ - [ ] **Tree Mirror Tests**
1445
+ - [ ] Verify 1:1 mirror between workflow tree and node tree
1446
+ - [ ] Verify mirror invariant after every operation
1447
+ - [ ] Verify no orphaned nodes in either tree
1448
+ - [ ] Verify no ghost references in either tree
1449
+
1450
+ - [ ] **Operation Tests**
1451
+ - [ ] Test attachChild() with valid inputs
1452
+ - [ ] Test attachChild() error cases (existing parent, circular ref, duplicate)
1453
+ - [ ] Test detachChild() with valid inputs
1454
+ - [ ] Test detachChild() error cases (not attached)
1455
+ - [ ] Test reparenting workflow (detach + attach)
1456
+
1457
+ - [ ] **Invariant Tests**
1458
+ - [ ] Test acyclicity invariant (no cycles)
1459
+ - [ ] Test single root invariant
1460
+ - [ ] Test connectedness invariant (all nodes reachable)
1461
+ - [ ] Test parent-child consistency invariant
1462
+ - [ ] Test edge count invariant (n-1 edges for n nodes)
1463
+
1464
+ - [ ] **Adversarial Tests**
1465
+ - [ ] Test manual parent mutation detection
1466
+ - [ ] Test circular reference detection (simple and complex)
1467
+ - [ ] Test stress scenarios (deep hierarchies, wide hierarchies)
1468
+ - [ ] Test rapid attach/detach cycles
1469
+ - [ ] Test concurrent operations
1470
+
1471
+ ### 8.2 Test File Organization
1472
+
1473
+ ```
1474
+ src/__tests__/
1475
+ ├── unit/
1476
+ │ └── workflow.test.ts # Existing unit tests
1477
+ ├── integration/
1478
+ │ ├── workflow-reparenting.test.ts # Existing reparenting tests
1479
+ │ └── bidirectional-consistency.test.ts # NEW: Dual tree consistency
1480
+ └── adversarial/
1481
+ ├── prd-compliance.test.ts # Existing PRD compliance
1482
+ ├── edge-case.test.ts # Existing edge cases
1483
+ └── tree-invariants.test.ts # NEW: Invariant testing
1484
+ ```
1485
+
1486
+ ### 8.3 Priority Order
1487
+
1488
+ **Phase 1: Core Consistency (High Priority)**
1489
+ 1. Bidirectional link verification tests
1490
+ 2. Tree mirror invariant tests
1491
+ 3. Operation-level consistency tests
1492
+
1493
+ **Phase 2: Comprehensive Coverage (Medium Priority)**
1494
+ 4. Tree-wide consistency validation
1495
+ 5. Invariant testing (acyclicity, connectedness, etc.)
1496
+ 6. Reparenting scenario tests
1497
+
1498
+ **Phase 3: Robustness (Lower Priority)**
1499
+ 7. Adversarial testing (manual mutations, circular refs)
1500
+ 8. Stress testing (deep/wide hierarchies)
1501
+ 9. Property-based testing
1502
+
1503
+ ---
1504
+
1505
+ ## 9. Key Takeaways
1506
+
1507
+ ### 9.1 Critical Invariants
1508
+
1509
+ 1. **1:1 Tree Mirror**: Workflow tree and node tree must always be perfectly synchronized
1510
+ 2. **Bidirectional Links**: Parent→child and child→parent references must always match
1511
+ 3. **Acyclicity**: No circular references in tree structure
1512
+ 4. **Single Root**: Exactly one root node (parent === null)
1513
+
1514
+ ### 9.2 Testing Principles
1515
+
1516
+ 1. **Test Both Trees**: Always verify both workflow tree and node tree
1517
+ 2. **Test Both Directions**: Verify parent→child AND child→parent
1518
+ 3. **Test After Every Operation**: Run consistency checks after attach, detach, reparenting
1519
+ 4. **Use Helpers**: Create reusable helper functions for common validations
1520
+ 5. **Adversarial Testing**: Test manual mutations and edge cases
1521
+
1522
+ ### 9.3 Best Practices
1523
+
1524
+ 1. **AAA Pattern**: Arrange-Act-Assert structure with clear phase comments
1525
+ 2. **Descriptive Names**: Use `should [expected behavior]` naming convention
1526
+ 3. **Multiple Assertions**: Verify multiple aspects of consistency
1527
+ 4. **Cross-Verification**: Use WorkflowTreeDebugger to verify structure
1528
+ 5. **Property-Based Testing**: Define invariants as properties and verify them
1529
+
1530
+ ---
1531
+
1532
+ ## 10. References and Resources
1533
+
1534
+ ### 10.1 External Documentation
1535
+
1536
+ - **DOM Tree Specification**: https://dom.spec.whatwg.org/#concept-tree-parent
1537
+ - Authoritative source on tree mutation semantics
1538
+ - Parent-child relationship management patterns
1539
+
1540
+ - **React Fiber Architecture**: https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiber.js
1541
+ - Dual tree representation patterns
1542
+ - Atomic update strategies
1543
+
1544
+ - **QuickCheck Paper**: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf
1545
+ - Property-based testing methodology
1546
+ - Invariant definition patterns
1547
+
1548
+ - **Princeton CS226 - Balanced Trees**: https://www.cs.princeton.edu/courses/archive/fall09/cos226/lectures/22BalancedTrees.pdf
1549
+ - Formal invariant definition
1550
+ - Tree property verification
1551
+
1552
+ ### 10.2 Internal Resources
1553
+
1554
+ - **Implementation Patterns**: `/home/dustin/projects/groundswell/plan/docs/bugfix-architecture/implementation_patterns.md`
1555
+ - **Existing Test Patterns**: `/home/dustin/projects/groundswell/plan/docs/research/P1M2T1S4/existing_test_pattern.md`
1556
+ - **Tree Mirroring Tests**: `/home/dustin/projects/groundswell/src/__tests__/integration/tree-mirroring.test.ts`
1557
+ - **Reparenting Tests**: `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts`
1558
+ - **PRD Compliance Tests**: `/home/dustin/projects/groundswell/src/__tests__/adversarial/prd-compliance.test.ts`
1559
+ - **Tree Debugger**: `/home/dustin/projects/groundswell/src/debugger/tree-debugger.ts`
1560
+
1561
+ ### 10.3 Actionable Patterns
1562
+
1563
+ **For immediate application:**
1564
+ 1. Use `verifyBidirectionalLink()` helper after every tree operation
1565
+ 2. Run `validateTreeConsistency()` in test teardown
1566
+ 3. Test both workflow tree AND node tree in all assertions
1567
+ 4. Add adversarial tests for manual mutations
1568
+ 5. Implement property-based tests for core invariants
1569
+
1570
+ ---
1571
+
1572
+ **Document Status:** Complete
1573
+ **Next Steps:** Implement test suite based on patterns documented here
1574
+ **Maintainer:** P1M3T1S4 Research Team