groundswell 0.0.3 → 1.0.0

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 (292) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -9
  3. package/dist/cache/cache-key.d.ts +20 -0
  4. package/dist/cache/cache-key.d.ts.map +1 -1
  5. package/dist/cache/cache-key.js +9 -0
  6. package/dist/cache/cache-key.js.map +1 -1
  7. package/dist/core/agent.d.ts +120 -29
  8. package/dist/core/agent.d.ts.map +1 -1
  9. package/dist/core/agent.js +584 -177
  10. package/dist/core/agent.js.map +1 -1
  11. package/dist/core/mcp-handler.d.ts +63 -5
  12. package/dist/core/mcp-handler.d.ts.map +1 -1
  13. package/dist/core/mcp-handler.js +184 -4
  14. package/dist/core/mcp-handler.js.map +1 -1
  15. package/dist/core/workflow-context.d.ts +6 -2
  16. package/dist/core/workflow-context.d.ts.map +1 -1
  17. package/dist/core/workflow-context.js +99 -4
  18. package/dist/core/workflow-context.js.map +1 -1
  19. package/dist/core/workflow.d.ts +315 -13
  20. package/dist/core/workflow.d.ts.map +1 -1
  21. package/dist/core/workflow.js +552 -30
  22. package/dist/core/workflow.js.map +1 -1
  23. package/dist/debugger/event-replayer.d.ts +422 -0
  24. package/dist/debugger/event-replayer.d.ts.map +1 -0
  25. package/dist/debugger/event-replayer.js +639 -0
  26. package/dist/debugger/event-replayer.js.map +1 -0
  27. package/dist/debugger/tree-debugger.d.ts +170 -1
  28. package/dist/debugger/tree-debugger.d.ts.map +1 -1
  29. package/dist/debugger/tree-debugger.js +423 -1
  30. package/dist/debugger/tree-debugger.js.map +1 -1
  31. package/dist/decorators/step.d.ts.map +1 -1
  32. package/dist/decorators/step.js +129 -47
  33. package/dist/decorators/step.js.map +1 -1
  34. package/dist/harnesses/claude-code-harness.d.ts +391 -0
  35. package/dist/harnesses/claude-code-harness.d.ts.map +1 -0
  36. package/dist/harnesses/claude-code-harness.js +1076 -0
  37. package/dist/harnesses/claude-code-harness.js.map +1 -0
  38. package/dist/harnesses/harness-registry.d.ts +440 -0
  39. package/dist/harnesses/harness-registry.d.ts.map +1 -0
  40. package/dist/harnesses/harness-registry.js +543 -0
  41. package/dist/harnesses/harness-registry.js.map +1 -0
  42. package/dist/harnesses/index.d.ts +12 -0
  43. package/dist/harnesses/index.d.ts.map +1 -0
  44. package/dist/harnesses/index.js +11 -0
  45. package/dist/harnesses/index.js.map +1 -0
  46. package/dist/harnesses/pi-harness.d.ts +219 -0
  47. package/dist/harnesses/pi-harness.d.ts.map +1 -0
  48. package/dist/harnesses/pi-harness.js +676 -0
  49. package/dist/harnesses/pi-harness.js.map +1 -0
  50. package/dist/harnesses/pi-schema-converter.d.ts +24 -0
  51. package/dist/harnesses/pi-schema-converter.d.ts.map +1 -0
  52. package/dist/harnesses/pi-schema-converter.js +81 -0
  53. package/dist/harnesses/pi-schema-converter.js.map +1 -0
  54. package/dist/harnesses/register-defaults.d.ts +24 -0
  55. package/dist/harnesses/register-defaults.d.ts.map +1 -0
  56. package/dist/harnesses/register-defaults.js +40 -0
  57. package/dist/harnesses/register-defaults.js.map +1 -0
  58. package/dist/harnesses/session-store.d.ts +201 -0
  59. package/dist/harnesses/session-store.d.ts.map +1 -0
  60. package/dist/harnesses/session-store.js +254 -0
  61. package/dist/harnesses/session-store.js.map +1 -0
  62. package/dist/index.d.ts +12 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +17 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/reflection/reflection.d.ts.map +1 -1
  67. package/dist/reflection/reflection.js +19 -4
  68. package/dist/reflection/reflection.js.map +1 -1
  69. package/dist/types/agent.d.ts +1253 -2
  70. package/dist/types/agent.d.ts.map +1 -1
  71. package/dist/types/agent.js +418 -1
  72. package/dist/types/agent.js.map +1 -1
  73. package/dist/types/decorators.d.ts +10 -1
  74. package/dist/types/decorators.d.ts.map +1 -1
  75. package/dist/types/events.d.ts +26 -0
  76. package/dist/types/events.d.ts.map +1 -1
  77. package/dist/types/harnesses.d.ts +474 -0
  78. package/dist/types/harnesses.d.ts.map +1 -0
  79. package/dist/types/harnesses.js +2 -0
  80. package/dist/types/harnesses.js.map +1 -0
  81. package/dist/types/index.d.ts +9 -1
  82. package/dist/types/index.d.ts.map +1 -1
  83. package/dist/types/index.js +6 -0
  84. package/dist/types/index.js.map +1 -1
  85. package/dist/types/providers.d.ts +691 -0
  86. package/dist/types/providers.d.ts.map +1 -0
  87. package/dist/types/providers.js +14 -0
  88. package/dist/types/providers.js.map +1 -0
  89. package/dist/types/restart.d.ts +132 -0
  90. package/dist/types/restart.d.ts.map +1 -0
  91. package/dist/types/restart.js +2 -0
  92. package/dist/types/restart.js.map +1 -0
  93. package/dist/types/streaming.d.ts +194 -0
  94. package/dist/types/streaming.d.ts.map +1 -0
  95. package/dist/types/streaming.js +67 -0
  96. package/dist/types/streaming.js.map +1 -0
  97. package/dist/types/workflow-context.d.ts +137 -1
  98. package/dist/types/workflow-context.d.ts.map +1 -1
  99. package/dist/utils/agent-validation.d.ts +88 -0
  100. package/dist/utils/agent-validation.d.ts.map +1 -0
  101. package/dist/utils/agent-validation.js +87 -0
  102. package/dist/utils/agent-validation.js.map +1 -0
  103. package/dist/utils/delay.d.ts +7 -0
  104. package/dist/utils/delay.d.ts.map +1 -0
  105. package/dist/utils/delay.js +9 -0
  106. package/dist/utils/delay.js.map +1 -0
  107. package/dist/utils/harness-config.d.ts +180 -0
  108. package/dist/utils/harness-config.d.ts.map +1 -0
  109. package/dist/utils/harness-config.js +311 -0
  110. package/dist/utils/harness-config.js.map +1 -0
  111. package/dist/utils/index.d.ts +9 -1
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +8 -1
  114. package/dist/utils/index.js.map +1 -1
  115. package/dist/utils/model-spec.d.ts +110 -0
  116. package/dist/utils/model-spec.d.ts.map +1 -0
  117. package/dist/utils/model-spec.js +149 -0
  118. package/dist/utils/model-spec.js.map +1 -0
  119. package/dist/utils/provider-config.d.ts +10 -0
  120. package/dist/utils/provider-config.d.ts.map +1 -0
  121. package/dist/utils/provider-config.js +10 -0
  122. package/dist/utils/provider-config.js.map +1 -0
  123. package/dist/utils/restart-analysis.d.ts +202 -0
  124. package/dist/utils/restart-analysis.d.ts.map +1 -0
  125. package/dist/utils/restart-analysis.js +426 -0
  126. package/dist/utils/restart-analysis.js.map +1 -0
  127. package/dist/utils/session-serialization.d.ts +118 -0
  128. package/dist/utils/session-serialization.d.ts.map +1 -0
  129. package/dist/utils/session-serialization.js +217 -0
  130. package/dist/utils/session-serialization.js.map +1 -0
  131. package/package.json +31 -5
  132. package/CHANGELOG.md +0 -188
  133. package/dist/__tests__/adversarial/attachChild-performance.test.d.ts +0 -16
  134. package/dist/__tests__/adversarial/attachChild-performance.test.d.ts.map +0 -1
  135. package/dist/__tests__/adversarial/attachChild-performance.test.js +0 -187
  136. package/dist/__tests__/adversarial/attachChild-performance.test.js.map +0 -1
  137. package/dist/__tests__/adversarial/circular-reference.test.d.ts +0 -13
  138. package/dist/__tests__/adversarial/circular-reference.test.d.ts.map +0 -1
  139. package/dist/__tests__/adversarial/circular-reference.test.js +0 -92
  140. package/dist/__tests__/adversarial/circular-reference.test.js.map +0 -1
  141. package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts +0 -16
  142. package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts.map +0 -1
  143. package/dist/__tests__/adversarial/complex-circular-reference.test.js +0 -127
  144. package/dist/__tests__/adversarial/complex-circular-reference.test.js.map +0 -1
  145. package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts +0 -21
  146. package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts.map +0 -1
  147. package/dist/__tests__/adversarial/concurrent-task-failures.test.js +0 -667
  148. package/dist/__tests__/adversarial/concurrent-task-failures.test.js.map +0 -1
  149. package/dist/__tests__/adversarial/deep-analysis.test.d.ts +0 -6
  150. package/dist/__tests__/adversarial/deep-analysis.test.d.ts.map +0 -1
  151. package/dist/__tests__/adversarial/deep-analysis.test.js +0 -877
  152. package/dist/__tests__/adversarial/deep-analysis.test.js.map +0 -1
  153. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts +0 -13
  154. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts.map +0 -1
  155. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js +0 -186
  156. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js.map +0 -1
  157. package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts +0 -6
  158. package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts.map +0 -1
  159. package/dist/__tests__/adversarial/e2e-prd-validation.test.js +0 -626
  160. package/dist/__tests__/adversarial/e2e-prd-validation.test.js.map +0 -1
  161. package/dist/__tests__/adversarial/edge-case.test.d.ts +0 -6
  162. package/dist/__tests__/adversarial/edge-case.test.d.ts.map +0 -1
  163. package/dist/__tests__/adversarial/edge-case.test.js +0 -857
  164. package/dist/__tests__/adversarial/edge-case.test.js.map +0 -1
  165. package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts +0 -20
  166. package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts.map +0 -1
  167. package/dist/__tests__/adversarial/error-merge-strategy.test.js +0 -907
  168. package/dist/__tests__/adversarial/error-merge-strategy.test.js.map +0 -1
  169. package/dist/__tests__/adversarial/incremental-performance.test.d.ts +0 -2
  170. package/dist/__tests__/adversarial/incremental-performance.test.d.ts.map +0 -1
  171. package/dist/__tests__/adversarial/incremental-performance.test.js +0 -113
  172. package/dist/__tests__/adversarial/incremental-performance.test.js.map +0 -1
  173. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts +0 -22
  174. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts.map +0 -1
  175. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js +0 -383
  176. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js.map +0 -1
  177. package/dist/__tests__/adversarial/observer-propagation.test.d.ts +0 -21
  178. package/dist/__tests__/adversarial/observer-propagation.test.d.ts.map +0 -1
  179. package/dist/__tests__/adversarial/observer-propagation.test.js +0 -404
  180. package/dist/__tests__/adversarial/observer-propagation.test.js.map +0 -1
  181. package/dist/__tests__/adversarial/parent-validation.test.d.ts +0 -13
  182. package/dist/__tests__/adversarial/parent-validation.test.d.ts.map +0 -1
  183. package/dist/__tests__/adversarial/parent-validation.test.js +0 -128
  184. package/dist/__tests__/adversarial/parent-validation.test.js.map +0 -1
  185. package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts +0 -20
  186. package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts.map +0 -1
  187. package/dist/__tests__/adversarial/prd-12-2-compliance.test.js +0 -482
  188. package/dist/__tests__/adversarial/prd-12-2-compliance.test.js.map +0 -1
  189. package/dist/__tests__/adversarial/prd-compliance.test.d.ts +0 -6
  190. package/dist/__tests__/adversarial/prd-compliance.test.d.ts.map +0 -1
  191. package/dist/__tests__/adversarial/prd-compliance.test.js +0 -886
  192. package/dist/__tests__/adversarial/prd-compliance.test.js.map +0 -1
  193. package/dist/__tests__/compatibility/backward-compatibility.test.d.ts +0 -22
  194. package/dist/__tests__/compatibility/backward-compatibility.test.d.ts.map +0 -1
  195. package/dist/__tests__/compatibility/backward-compatibility.test.js +0 -1843
  196. package/dist/__tests__/compatibility/backward-compatibility.test.js.map +0 -1
  197. package/dist/__tests__/helpers/index.d.ts +0 -10
  198. package/dist/__tests__/helpers/index.d.ts.map +0 -1
  199. package/dist/__tests__/helpers/index.js +0 -10
  200. package/dist/__tests__/helpers/index.js.map +0 -1
  201. package/dist/__tests__/helpers/tree-verification.d.ts +0 -90
  202. package/dist/__tests__/helpers/tree-verification.d.ts.map +0 -1
  203. package/dist/__tests__/helpers/tree-verification.js +0 -202
  204. package/dist/__tests__/helpers/tree-verification.js.map +0 -1
  205. package/dist/__tests__/integration/agent-workflow.test.d.ts +0 -2
  206. package/dist/__tests__/integration/agent-workflow.test.d.ts.map +0 -1
  207. package/dist/__tests__/integration/agent-workflow.test.js +0 -256
  208. package/dist/__tests__/integration/agent-workflow.test.js.map +0 -1
  209. package/dist/__tests__/integration/bidirectional-consistency.test.d.ts +0 -14
  210. package/dist/__tests__/integration/bidirectional-consistency.test.d.ts.map +0 -1
  211. package/dist/__tests__/integration/bidirectional-consistency.test.js +0 -668
  212. package/dist/__tests__/integration/bidirectional-consistency.test.js.map +0 -1
  213. package/dist/__tests__/integration/observer-logging.test.d.ts +0 -2
  214. package/dist/__tests__/integration/observer-logging.test.d.ts.map +0 -1
  215. package/dist/__tests__/integration/observer-logging.test.js +0 -517
  216. package/dist/__tests__/integration/observer-logging.test.js.map +0 -1
  217. package/dist/__tests__/integration/tree-mirroring.test.d.ts +0 -2
  218. package/dist/__tests__/integration/tree-mirroring.test.d.ts.map +0 -1
  219. package/dist/__tests__/integration/tree-mirroring.test.js +0 -117
  220. package/dist/__tests__/integration/tree-mirroring.test.js.map +0 -1
  221. package/dist/__tests__/integration/workflow-reparenting.test.d.ts +0 -12
  222. package/dist/__tests__/integration/workflow-reparenting.test.d.ts.map +0 -1
  223. package/dist/__tests__/integration/workflow-reparenting.test.js +0 -239
  224. package/dist/__tests__/integration/workflow-reparenting.test.js.map +0 -1
  225. package/dist/__tests__/unit/agent.test.d.ts +0 -2
  226. package/dist/__tests__/unit/agent.test.d.ts.map +0 -1
  227. package/dist/__tests__/unit/agent.test.js +0 -143
  228. package/dist/__tests__/unit/agent.test.js.map +0 -1
  229. package/dist/__tests__/unit/cache-key.test.d.ts +0 -5
  230. package/dist/__tests__/unit/cache-key.test.d.ts.map +0 -1
  231. package/dist/__tests__/unit/cache-key.test.js +0 -145
  232. package/dist/__tests__/unit/cache-key.test.js.map +0 -1
  233. package/dist/__tests__/unit/cache.test.d.ts +0 -5
  234. package/dist/__tests__/unit/cache.test.d.ts.map +0 -1
  235. package/dist/__tests__/unit/cache.test.js +0 -132
  236. package/dist/__tests__/unit/cache.test.js.map +0 -1
  237. package/dist/__tests__/unit/context.test.d.ts +0 -2
  238. package/dist/__tests__/unit/context.test.d.ts.map +0 -1
  239. package/dist/__tests__/unit/context.test.js +0 -220
  240. package/dist/__tests__/unit/context.test.js.map +0 -1
  241. package/dist/__tests__/unit/decorators.test.d.ts +0 -2
  242. package/dist/__tests__/unit/decorators.test.d.ts.map +0 -1
  243. package/dist/__tests__/unit/decorators.test.js +0 -162
  244. package/dist/__tests__/unit/decorators.test.js.map +0 -1
  245. package/dist/__tests__/unit/introspection-tools.test.d.ts +0 -5
  246. package/dist/__tests__/unit/introspection-tools.test.d.ts.map +0 -1
  247. package/dist/__tests__/unit/introspection-tools.test.js +0 -191
  248. package/dist/__tests__/unit/introspection-tools.test.js.map +0 -1
  249. package/dist/__tests__/unit/logger.test.d.ts +0 -2
  250. package/dist/__tests__/unit/logger.test.d.ts.map +0 -1
  251. package/dist/__tests__/unit/logger.test.js +0 -241
  252. package/dist/__tests__/unit/logger.test.js.map +0 -1
  253. package/dist/__tests__/unit/observable.test.d.ts +0 -2
  254. package/dist/__tests__/unit/observable.test.d.ts.map +0 -1
  255. package/dist/__tests__/unit/observable.test.js +0 -251
  256. package/dist/__tests__/unit/observable.test.js.map +0 -1
  257. package/dist/__tests__/unit/prompt.test.d.ts +0 -2
  258. package/dist/__tests__/unit/prompt.test.d.ts.map +0 -1
  259. package/dist/__tests__/unit/prompt.test.js +0 -113
  260. package/dist/__tests__/unit/prompt.test.js.map +0 -1
  261. package/dist/__tests__/unit/reflection.test.d.ts +0 -5
  262. package/dist/__tests__/unit/reflection.test.d.ts.map +0 -1
  263. package/dist/__tests__/unit/reflection.test.js +0 -160
  264. package/dist/__tests__/unit/reflection.test.js.map +0 -1
  265. package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts +0 -2
  266. package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts.map +0 -1
  267. package/dist/__tests__/unit/tree-debugger-incremental.test.js +0 -136
  268. package/dist/__tests__/unit/tree-debugger-incremental.test.js.map +0 -1
  269. package/dist/__tests__/unit/tree-debugger.test.d.ts +0 -2
  270. package/dist/__tests__/unit/tree-debugger.test.d.ts.map +0 -1
  271. package/dist/__tests__/unit/tree-debugger.test.js +0 -69
  272. package/dist/__tests__/unit/tree-debugger.test.js.map +0 -1
  273. package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts +0 -2
  274. package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts.map +0 -1
  275. package/dist/__tests__/unit/utils/workflow-error-utils.test.js +0 -154
  276. package/dist/__tests__/unit/utils/workflow-error-utils.test.js.map +0 -1
  277. package/dist/__tests__/unit/workflow-detachChild.test.d.ts +0 -2
  278. package/dist/__tests__/unit/workflow-detachChild.test.d.ts.map +0 -1
  279. package/dist/__tests__/unit/workflow-detachChild.test.js +0 -76
  280. package/dist/__tests__/unit/workflow-detachChild.test.js.map +0 -1
  281. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts +0 -2
  282. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts.map +0 -1
  283. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js +0 -122
  284. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js.map +0 -1
  285. package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts +0 -2
  286. package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts.map +0 -1
  287. package/dist/__tests__/unit/workflow-isDescendantOf.test.js +0 -140
  288. package/dist/__tests__/unit/workflow-isDescendantOf.test.js.map +0 -1
  289. package/dist/__tests__/unit/workflow.test.d.ts +0 -2
  290. package/dist/__tests__/unit/workflow.test.d.ts.map +0 -1
  291. package/dist/__tests__/unit/workflow.test.js +0 -330
  292. package/dist/__tests__/unit/workflow.test.js.map +0 -1
@@ -1,668 +0,0 @@
1
- /**
2
- * Bidirectional Consistency Tests for Dual Tree Structure
3
- *
4
- * Tests the 1:1 mirror invariant between the Workflow instance tree and the WorkflowNode tree
5
- * as specified in PRD Section 12.2.
6
- *
7
- * For every relationship in the workflow tree, there MUST be an equivalent relationship in the node tree:
8
- * - If child.parent === parent, then child.node.parent MUST equal parent.node
9
- * - If parent.children includes child, then parent.node.children MUST include child.node
10
- *
11
- * @module integration/bidirectional-consistency
12
- */
13
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
14
- import { Workflow } from '../../index.js';
15
- import { verifyBidirectionalLink, verifyTreeMirror, verifyOrphaned, verifyNoCycles, validateTreeConsistency, collectAllNodes, getDepth, } from '../helpers/tree-verification.js';
16
- /**
17
- * SimpleWorkflow class for testing
18
- * Pattern from: src/__tests__/adversarial/circular-reference.test.ts:20-26
19
- */
20
- class SimpleWorkflow extends Workflow {
21
- async run() {
22
- this.setStatus('running');
23
- this.setStatus('completed');
24
- return 'done';
25
- }
26
- }
27
- // ============================================================================
28
- // Basic Operations Tests
29
- // ============================================================================
30
- describe('Bidirectional Consistency: Basic Operations', () => {
31
- describe('attachChild() consistency', () => {
32
- it('should maintain bidirectional links in both trees', () => {
33
- // ARRANGE
34
- const parent = new SimpleWorkflow('Parent');
35
- const child = new SimpleWorkflow('Child'); // No parent
36
- // ACT
37
- parent.attachChild(child);
38
- // ASSERT - Verify bidirectional links
39
- verifyBidirectionalLink(parent, child);
40
- // ASSERT - Verify tree mirror invariant
41
- verifyTreeMirror(parent);
42
- // ASSERT - Verify no inconsistencies detected
43
- const errors = validateTreeConsistency(parent);
44
- expect(errors).toEqual([]);
45
- });
46
- it('should maintain consistency when attaching multiple children', () => {
47
- // ARRANGE
48
- const parent = new SimpleWorkflow('Parent');
49
- const child1 = new SimpleWorkflow('Child1');
50
- const child2 = new SimpleWorkflow('Child2');
51
- const child3 = new SimpleWorkflow('Child3');
52
- // ACT
53
- parent.attachChild(child1);
54
- parent.attachChild(child2);
55
- parent.attachChild(child3);
56
- // ASSERT - Verify each child
57
- verifyBidirectionalLink(parent, child1);
58
- verifyBidirectionalLink(parent, child2);
59
- verifyBidirectionalLink(parent, child3);
60
- // ASSERT - Verify entire tree
61
- verifyTreeMirror(parent);
62
- const errors = validateTreeConsistency(parent);
63
- expect(errors).toEqual([]);
64
- });
65
- it('should maintain consistency when attaching child via constructor', () => {
66
- // ARRANGE & ACT
67
- const parent = new SimpleWorkflow('Parent');
68
- const child = new SimpleWorkflow('Child', parent); // Auto-attaches
69
- // ASSERT - Verify bidirectional links
70
- verifyBidirectionalLink(parent, child);
71
- // ASSERT - Verify tree mirror
72
- verifyTreeMirror(parent);
73
- const errors = validateTreeConsistency(parent);
74
- expect(errors).toEqual([]);
75
- });
76
- it('should maintain consistency when reattaching a detached child', () => {
77
- // ARRANGE
78
- const parent = new SimpleWorkflow('Parent');
79
- const child = new SimpleWorkflow('Child', parent);
80
- // ACT - Detach and reattach
81
- parent.detachChild(child);
82
- parent.attachChild(child);
83
- // ASSERT - Verify bidirectional links restored
84
- verifyBidirectionalLink(parent, child);
85
- // ASSERT - Verify tree mirror
86
- verifyTreeMirror(parent);
87
- const errors = validateTreeConsistency(parent);
88
- expect(errors).toEqual([]);
89
- });
90
- });
91
- describe('detachChild() consistency', () => {
92
- it('should remove bidirectional links from both trees', () => {
93
- // ARRANGE
94
- const parent = new SimpleWorkflow('Parent');
95
- const child = new SimpleWorkflow('Child', parent);
96
- // ACT
97
- parent.detachChild(child);
98
- // ASSERT - Verify orphaning in BOTH trees
99
- expect(child.parent).toBeNull();
100
- expect(child.node.parent).toBeNull();
101
- expect(parent.children).not.toContain(child);
102
- expect(parent.node.children).not.toContain(child.node);
103
- // ASSERT - Verify orphaning helper
104
- verifyOrphaned(child);
105
- // ASSERT - Verify tree mirror
106
- verifyTreeMirror(parent);
107
- const errors = validateTreeConsistency(parent);
108
- expect(errors).toEqual([]);
109
- });
110
- it('should maintain consistency when detaching middle child', () => {
111
- // ARRANGE
112
- const parent = new SimpleWorkflow('Parent');
113
- const child1 = new SimpleWorkflow('Child1', parent);
114
- const child2 = new SimpleWorkflow('Child2', parent);
115
- const child3 = new SimpleWorkflow('Child3', parent);
116
- // ACT - Detach middle child
117
- parent.detachChild(child2);
118
- // ASSERT - Verify child2 is orphaned
119
- verifyOrphaned(child2);
120
- // ASSERT - Verify other children still linked
121
- verifyBidirectionalLink(parent, child1);
122
- verifyBidirectionalLink(parent, child3);
123
- // ASSERT - Verify parent has correct children
124
- expect(parent.children).toEqual([child1, child3]);
125
- expect(parent.node.children).toEqual([child1.node, child3.node]);
126
- // ASSERT - Verify tree mirror
127
- verifyTreeMirror(parent);
128
- const errors = validateTreeConsistency(parent);
129
- expect(errors).toEqual([]);
130
- });
131
- it('should maintain consistency when detaching last child', () => {
132
- // ARRANGE
133
- const parent = new SimpleWorkflow('Parent');
134
- const child1 = new SimpleWorkflow('Child1', parent);
135
- const child2 = new SimpleWorkflow('Child2', parent);
136
- const child3 = new SimpleWorkflow('Child3', parent);
137
- // ACT - Detach last child
138
- parent.detachChild(child3);
139
- // ASSERT - Verify child3 is orphaned
140
- verifyOrphaned(child3);
141
- // ASSERT - Verify parent has correct children
142
- expect(parent.children).toEqual([child1, child2]);
143
- expect(parent.node.children).toEqual([child1.node, child2.node]);
144
- // ASSERT - Verify tree mirror
145
- verifyTreeMirror(parent);
146
- const errors = validateTreeConsistency(parent);
147
- expect(errors).toEqual([]);
148
- });
149
- it('should maintain consistency when detaching all children', () => {
150
- // ARRANGE
151
- const parent = new SimpleWorkflow('Parent');
152
- const child1 = new SimpleWorkflow('Child1', parent);
153
- const child2 = new SimpleWorkflow('Child2', parent);
154
- // ACT - Detach all children
155
- parent.detachChild(child1);
156
- parent.detachChild(child2);
157
- // ASSERT - Verify all children orphaned
158
- verifyOrphaned(child1);
159
- verifyOrphaned(child2);
160
- // ASSERT - Verify parent has no children
161
- expect(parent.children).toEqual([]);
162
- expect(parent.node.children).toEqual([]);
163
- // ASSERT - Verify tree mirror
164
- verifyTreeMirror(parent);
165
- const errors = validateTreeConsistency(parent);
166
- expect(errors).toEqual([]);
167
- });
168
- });
169
- });
170
- // ============================================================================
171
- // Reparenting Tests
172
- // ============================================================================
173
- describe('Bidirectional Consistency: Reparenting', () => {
174
- it('should maintain consistency during single reparenting', () => {
175
- // ARRANGE
176
- const parent1 = new SimpleWorkflow('Parent1');
177
- const parent2 = new SimpleWorkflow('Parent2');
178
- const child = new SimpleWorkflow('Child', parent1);
179
- // ASSERT - Verify initial state
180
- verifyBidirectionalLink(parent1, child);
181
- expect(parent2.children).toEqual([]);
182
- // ACT - Reparent
183
- parent1.detachChild(child);
184
- parent2.attachChild(child);
185
- // ASSERT - Verify new state
186
- verifyBidirectionalLink(parent2, child);
187
- // ASSERT - Verify old parent no longer has child in BOTH trees
188
- expect(parent1.children).not.toContain(child);
189
- expect(parent1.node.children).not.toContain(child.node);
190
- // ASSERT - Verify new parent has child in BOTH trees
191
- expect(parent2.children).toContain(child);
192
- expect(parent2.node.children).toContain(child.node);
193
- // ASSERT - Verify both trees are valid
194
- verifyTreeMirror(parent1);
195
- verifyTreeMirror(parent2);
196
- const errors1 = validateTreeConsistency(parent1);
197
- const errors2 = validateTreeConsistency(parent2);
198
- expect(errors1).toEqual([]);
199
- expect(errors2).toEqual([]);
200
- });
201
- it('should maintain consistency during multiple reparenting cycles', () => {
202
- // ARRANGE
203
- const parentA = new SimpleWorkflow('ParentA');
204
- const parentB = new SimpleWorkflow('ParentB');
205
- const parentC = new SimpleWorkflow('ParentC');
206
- const child = new SimpleWorkflow('Child', parentA);
207
- // ACT - Cycle 1: A -> B
208
- parentA.detachChild(child);
209
- parentB.attachChild(child);
210
- // ASSERT - After cycle 1
211
- verifyBidirectionalLink(parentB, child);
212
- verifyTreeMirror(parentB);
213
- expect(validateTreeConsistency(parentB)).toEqual([]);
214
- // ACT - Cycle 2: B -> C
215
- parentB.detachChild(child);
216
- parentC.attachChild(child);
217
- // ASSERT - After cycle 2
218
- verifyBidirectionalLink(parentC, child);
219
- verifyTreeMirror(parentC);
220
- expect(validateTreeConsistency(parentC)).toEqual([]);
221
- // ACT - Cycle 3: C -> A (back to original)
222
- parentC.detachChild(child);
223
- parentA.attachChild(child);
224
- // ASSERT - After cycle 3
225
- verifyBidirectionalLink(parentA, child);
226
- verifyTreeMirror(parentA);
227
- expect(validateTreeConsistency(parentA)).toEqual([]);
228
- });
229
- it('should maintain consistency with deep hierarchy reparenting', () => {
230
- // ARRANGE - Create deep hierarchy: root1 -> mid1 -> leaf
231
- const root1 = new SimpleWorkflow('Root1');
232
- const mid1 = new SimpleWorkflow('Mid1', root1);
233
- const leaf = new SimpleWorkflow('Leaf', mid1);
234
- // ARRANGE - Create second hierarchy: root2 -> mid2
235
- const root2 = new SimpleWorkflow('Root2');
236
- const mid2 = new SimpleWorkflow('Mid2', root2);
237
- // ASSERT - Verify initial deep hierarchy
238
- verifyBidirectionalLink(root1, mid1);
239
- verifyBidirectionalLink(mid1, leaf);
240
- verifyTreeMirror(root1);
241
- // ACT - Reparent entire subtree (mid1 + leaf) from root1 to root2
242
- root1.detachChild(mid1);
243
- root2.attachChild(mid1);
244
- // ASSERT - Verify new hierarchy: root2 -> mid2, mid1 -> leaf
245
- verifyBidirectionalLink(root2, mid1);
246
- verifyBidirectionalLink(mid1, leaf);
247
- verifyTreeMirror(root2);
248
- // ASSERT - Verify old root no longer has mid1 in BOTH trees
249
- expect(root1.children).toEqual([]);
250
- expect(root1.node.children).toEqual([]);
251
- // ASSERT - Verify new root has both children in BOTH trees
252
- expect(root2.children).toEqual([mid2, mid1]);
253
- expect(root2.node.children).toEqual([mid2.node, mid1.node]);
254
- // ASSERT - Verify leaf still under mid1 in BOTH trees
255
- expect(mid1.children).toEqual([leaf]);
256
- expect(mid1.node.children).toEqual([leaf.node]);
257
- // ASSERT - Verify no inconsistencies
258
- expect(validateTreeConsistency(root2)).toEqual([]);
259
- });
260
- it('should maintain consistency when reparenting between multiple parents', () => {
261
- // ARRANGE
262
- const parents = [
263
- new SimpleWorkflow('Parent0'),
264
- new SimpleWorkflow('Parent1'),
265
- new SimpleWorkflow('Parent2'),
266
- new SimpleWorkflow('Parent3'),
267
- ];
268
- const child = new SimpleWorkflow('Child', parents[0]);
269
- // ACT - Reparent through all parents
270
- for (let i = 0; i < parents.length - 1; i++) {
271
- const currentParent = parents[i];
272
- const nextParent = parents[i + 1];
273
- currentParent.detachChild(child);
274
- nextParent.attachChild(child);
275
- // ASSERT - Verify consistency after each reparenting
276
- verifyBidirectionalLink(nextParent, child);
277
- verifyTreeMirror(nextParent);
278
- expect(validateTreeConsistency(nextParent)).toEqual([]);
279
- }
280
- // ASSERT - Final parent should have child
281
- verifyBidirectionalLink(parents[3], child);
282
- expect(parents[0].children).toEqual([]);
283
- });
284
- });
285
- // ============================================================================
286
- // Invariant Tests
287
- // ============================================================================
288
- describe('Bidirectional Consistency: Invariants', () => {
289
- describe('Acyclicity invariant', () => {
290
- it('should prevent circular references', () => {
291
- // ARRANGE
292
- const root = new SimpleWorkflow('Root');
293
- const child = new SimpleWorkflow('Child', root);
294
- // ACT & ASSERT - Try to create cycle
295
- expect(() => {
296
- child.attachChild(root);
297
- }).toThrow(/circular|cycle|ancestor/i);
298
- // ASSERT - Verify no corruption occurred
299
- verifyNoCycles(root);
300
- expect(root.parent).toBeNull();
301
- expect(child.parent).toBe(root);
302
- });
303
- it('should detect complex circular references', () => {
304
- // ARRANGE - Create chain: root -> child1 -> child2 -> child3
305
- const root = new SimpleWorkflow('Root');
306
- const child1 = new SimpleWorkflow('Child1', root);
307
- const child2 = new SimpleWorkflow('Child2', child1);
308
- const child3 = new SimpleWorkflow('Child3', child2);
309
- // ACT & ASSERT - Try to create cycle: child3 -> root
310
- expect(() => {
311
- child3.attachChild(root);
312
- }).toThrow(/circular|cycle|ancestor/i);
313
- // ASSERT - Verify no corruption
314
- verifyNoCycles(root);
315
- verifyTreeMirror(root);
316
- });
317
- it('should prevent self-attachment', () => {
318
- // ARRANGE
319
- const workflow = new SimpleWorkflow('Self');
320
- // ACT & ASSERT - Try to attach to self
321
- expect(() => {
322
- workflow.attachChild(workflow);
323
- }).toThrow(/circular|cycle|ancestor/i);
324
- });
325
- });
326
- describe('Tree mirror invariant', () => {
327
- it('should maintain 1:1 correspondence after operations', () => {
328
- // ARRANGE
329
- const root = new SimpleWorkflow('Root');
330
- const child1 = new SimpleWorkflow('Child1', root);
331
- const child2 = new SimpleWorkflow('Child2', root);
332
- // ASSERT - Verify mirror after creation
333
- verifyTreeMirror(root);
334
- // ACT - Detach and reattach
335
- root.detachChild(child1);
336
- verifyTreeMirror(root);
337
- root.attachChild(child1);
338
- verifyTreeMirror(root);
339
- // ASSERT - Verify both trees have same structure
340
- expect(root.children.length).toBe(root.node.children.length);
341
- expect(root.children[0].node).toBe(root.node.children[0]);
342
- expect(root.children[1].node).toBe(root.node.children[1]);
343
- });
344
- it('should detect mirror violations in parent relationship', () => {
345
- // ARRANGE
346
- const parent = new SimpleWorkflow('Parent');
347
- const child = new SimpleWorkflow('Child', parent);
348
- // ACT - Manually corrupt node tree (simulating bug)
349
- child.node.parent = null;
350
- // ASSERT - Should detect mirror violation
351
- expect(() => {
352
- verifyTreeMirror(parent);
353
- }).toThrow(/MIRROR/);
354
- });
355
- it('should detect mirror violations in children relationship', () => {
356
- // ARRANGE
357
- const parent = new SimpleWorkflow('Parent');
358
- const child = new SimpleWorkflow('Child', parent);
359
- // ACT - Manually corrupt node tree children array
360
- parent.node.children = [];
361
- // ASSERT - Should detect mirror violation
362
- expect(() => {
363
- verifyTreeMirror(parent);
364
- }).toThrow(/MIRROR/);
365
- });
366
- });
367
- describe('Single root invariant', () => {
368
- it('should maintain exactly one root', () => {
369
- // ARRANGE
370
- const root = new SimpleWorkflow('Root');
371
- const child1 = new SimpleWorkflow('Child1', root);
372
- const child2 = new SimpleWorkflow('Child2', root);
373
- // ACT - Count roots
374
- const allNodes = collectAllNodes(root);
375
- const roots = allNodes.filter(n => n.parent === null);
376
- // ASSERT - Should have exactly one root
377
- expect(roots.length).toBe(1);
378
- expect(roots[0]).toBe(root);
379
- });
380
- it('should have one root after reparenting', () => {
381
- // ARRANGE
382
- const root1 = new SimpleWorkflow('Root1');
383
- const root2 = new SimpleWorkflow('Root2');
384
- const child = new SimpleWorkflow('Child', root1);
385
- // ASSERT - Initially: 2 roots (root1 and root2)
386
- let allNodes = [...collectAllNodes(root1), ...collectAllNodes(root2)];
387
- let roots = allNodes.filter(n => n.parent === null);
388
- expect(roots.length).toBe(2);
389
- // ACT - Reparent child to root2
390
- root1.detachChild(child);
391
- root2.attachChild(child);
392
- // ASSERT - Still: 2 roots (root1 and root2)
393
- allNodes = [...collectAllNodes(root1), ...collectAllNodes(root2)];
394
- roots = allNodes.filter(n => n.parent === null);
395
- expect(roots.length).toBe(2);
396
- });
397
- });
398
- describe('Connectedness invariant', () => {
399
- it('should have all nodes reachable from root', () => {
400
- // ARRANGE
401
- const root = new SimpleWorkflow('Root');
402
- const child1 = new SimpleWorkflow('Child1', root);
403
- const child2 = new SimpleWorkflow('Child2', root);
404
- const grandchild = new SimpleWorkflow('Grandchild', child1);
405
- // ACT - Collect all nodes and verify reachability
406
- const allNodes = collectAllNodes(root);
407
- const reachableFromRoot = new Set();
408
- function traverse(node) {
409
- reachableFromRoot.add(node);
410
- node.children.forEach(traverse);
411
- }
412
- traverse(root);
413
- // ASSERT - All nodes should be reachable
414
- allNodes.forEach(node => {
415
- expect(reachableFromRoot.has(node)).toBe(true);
416
- });
417
- });
418
- it('should have all nodes reachable after reparenting', () => {
419
- // ARRANGE
420
- const root1 = new SimpleWorkflow('Root1');
421
- const root2 = new SimpleWorkflow('Root2');
422
- const child = new SimpleWorkflow('Child', root1);
423
- // ACT - Reparent
424
- root1.detachChild(child);
425
- root2.attachChild(child);
426
- // ASSERT - Verify reachability from root2
427
- const allNodes = collectAllNodes(root2);
428
- const reachableFromRoot = new Set();
429
- function traverse(node) {
430
- reachableFromRoot.add(node);
431
- node.children.forEach(traverse);
432
- }
433
- traverse(root2);
434
- expect(reachableFromRoot.has(root2)).toBe(true);
435
- expect(reachableFromRoot.has(child)).toBe(true);
436
- });
437
- });
438
- describe('Depth calculation', () => {
439
- it('should calculate depth correctly', () => {
440
- // ARRANGE
441
- const root = new SimpleWorkflow('Root');
442
- const child1 = new SimpleWorkflow('Child1', root);
443
- const child2 = new SimpleWorkflow('Child2', child1);
444
- const child3 = new SimpleWorkflow('Child3', child2);
445
- // ASSERT - Verify depths
446
- expect(getDepth(root)).toBe(0);
447
- expect(getDepth(child1)).toBe(1);
448
- expect(getDepth(child2)).toBe(2);
449
- expect(getDepth(child3)).toBe(3);
450
- });
451
- });
452
- });
453
- // ============================================================================
454
- // Adversarial Tests
455
- // ============================================================================
456
- describe('Bidirectional Consistency: Adversarial Tests', () => {
457
- // Console mocking to suppress error output during expected failures
458
- beforeEach(() => {
459
- vi.spyOn(console, 'log').mockImplementation(() => { });
460
- vi.spyOn(console, 'error').mockImplementation(() => { });
461
- vi.spyOn(console, 'warn').mockImplementation(() => { });
462
- });
463
- afterEach(() => {
464
- vi.restoreAllMocks();
465
- });
466
- describe('Manual mutation detection', () => {
467
- it('should detect inconsistency from manual parent mutation', () => {
468
- // ARRANGE
469
- const parent1 = new SimpleWorkflow('Parent1');
470
- const parent2 = new SimpleWorkflow('Parent2');
471
- const child = new SimpleWorkflow('Child', parent1);
472
- // ACT - Manually mutate parent (bypassing attachChild)
473
- child.parent = parent2;
474
- // ASSERT - Should detect inconsistency
475
- const errors = validateTreeConsistency(parent1);
476
- expect(errors.length).toBeGreaterThan(0);
477
- expect(errors.some(e => e.includes('Mismatched parent'))).toBe(true);
478
- });
479
- it('should detect inconsistency from manual children array mutation', () => {
480
- // ARRANGE
481
- const parent = new SimpleWorkflow('Parent');
482
- const child = new SimpleWorkflow('Child', parent);
483
- const other = new SimpleWorkflow('Other');
484
- // ACT - Manually add to children array (bypassing attachChild)
485
- parent.children.push(other);
486
- // ASSERT - Should detect inconsistency
487
- const errors = validateTreeConsistency(parent);
488
- expect(errors.length).toBeGreaterThan(0);
489
- });
490
- it('should detect inconsistency from node tree mutation', () => {
491
- // ARRANGE
492
- const parent = new SimpleWorkflow('Parent');
493
- const child = new SimpleWorkflow('Child', parent);
494
- // ACT - Manually mutate node tree
495
- child.node.parent = null;
496
- // ASSERT - Should detect mirror violation
497
- expect(() => {
498
- verifyTreeMirror(parent);
499
- }).toThrow();
500
- });
501
- it('should detect inconsistency from node children array mutation', () => {
502
- // ARRANGE
503
- const parent = new SimpleWorkflow('Parent');
504
- const child = new SimpleWorkflow('Child', parent);
505
- // ACT - Manually mutate node.children array to add duplicate child
506
- // This creates a mirror violation where node.children has extra entries
507
- parent.node.children.push(child.node); // Duplicate child
508
- // ASSERT - Should detect mirror violation (children count mismatch)
509
- expect(() => {
510
- verifyTreeMirror(parent);
511
- }).toThrow(/MIRROR|Children count mismatch/);
512
- });
513
- });
514
- describe('Stress testing', () => {
515
- it('should maintain consistency with deep hierarchies', () => {
516
- // ARRANGE
517
- const root = new SimpleWorkflow('Root');
518
- let current = root;
519
- // ACT - Create 100 levels deep
520
- for (let i = 0; i < 100; i++) {
521
- const child = new SimpleWorkflow(`Level-${i}`);
522
- current.attachChild(child);
523
- current = child;
524
- }
525
- // ASSERT - Verify consistency at each level
526
- verifyTreeMirror(root);
527
- verifyNoCycles(root);
528
- const errors = validateTreeConsistency(root);
529
- expect(errors).toEqual([]);
530
- });
531
- it('should maintain consistency with wide hierarchies', () => {
532
- // ARRANGE
533
- const parent = new SimpleWorkflow('Parent');
534
- const children = [];
535
- // ACT - Create 100 children
536
- for (let i = 0; i < 100; i++) {
537
- const child = new SimpleWorkflow(`Child-${i}`);
538
- parent.attachChild(child);
539
- children.push(child);
540
- }
541
- // ASSERT - Verify all children
542
- children.forEach(child => {
543
- verifyBidirectionalLink(parent, child);
544
- });
545
- // ASSERT - Verify tree mirror
546
- verifyTreeMirror(parent);
547
- const errors = validateTreeConsistency(parent);
548
- expect(errors).toEqual([]);
549
- });
550
- it('should maintain consistency during rapid attach/detach cycles', () => {
551
- // ARRANGE
552
- const parent1 = new SimpleWorkflow('Parent1');
553
- const parent2 = new SimpleWorkflow('Parent2');
554
- const child = new SimpleWorkflow('Child', parent1);
555
- // ACT - Rapid reparenting (100 cycles)
556
- for (let i = 0; i < 100; i++) {
557
- if (i % 2 === 0) {
558
- parent1.detachChild(child);
559
- parent2.attachChild(child);
560
- }
561
- else {
562
- parent2.detachChild(child);
563
- parent1.attachChild(child);
564
- }
565
- // ASSERT - Verify consistency after each cycle
566
- const expectedParent = i % 2 === 0 ? parent2 : parent1;
567
- expect(child.parent).toBe(expectedParent);
568
- expect(child.node.parent).toBe(expectedParent.node);
569
- }
570
- // ASSERT - Final verification
571
- verifyTreeMirror(parent1);
572
- verifyTreeMirror(parent2);
573
- });
574
- it('should handle complex multi-level reparenting', () => {
575
- // ARRANGE - Create complex tree structure
576
- const root1 = new SimpleWorkflow('Root1');
577
- const branch1 = new SimpleWorkflow('Branch1', root1);
578
- const leaf1a = new SimpleWorkflow('Leaf1A', branch1);
579
- const leaf1b = new SimpleWorkflow('Leaf1B', branch1);
580
- const root2 = new SimpleWorkflow('Root2');
581
- const branch2 = new SimpleWorkflow('Branch2', root2);
582
- // ACT - Reparent branch1 (and its children) to root2
583
- root1.detachChild(branch1);
584
- root2.attachChild(branch1);
585
- // ASSERT - Verify tree structure
586
- verifyTreeMirror(root2);
587
- // Verify root1 has no children
588
- expect(root1.children).toEqual([]);
589
- // Verify root2 has both branches
590
- expect(root2.children.length).toBe(2);
591
- // Verify branch1 still has its children
592
- expect(branch1.children).toEqual([leaf1a, leaf1b]);
593
- // Verify all bidirectional links
594
- verifyBidirectionalLink(root2, branch1);
595
- verifyBidirectionalLink(root2, branch2);
596
- verifyBidirectionalLink(branch1, leaf1a);
597
- verifyBidirectionalLink(branch1, leaf1b);
598
- const errors = validateTreeConsistency(root2);
599
- expect(errors).toEqual([]);
600
- });
601
- });
602
- });
603
- // ============================================================================
604
- // Property-Based Tests
605
- // ============================================================================
606
- describe('Bidirectional Consistency: Property-Based Tests', () => {
607
- it('should satisfy round-trip property', () => {
608
- // ARRANGE
609
- const root = new SimpleWorkflow('Root');
610
- const child1 = new SimpleWorkflow('Child1', root);
611
- const child2 = new SimpleWorkflow('Child2', root);
612
- // Capture initial children count
613
- const initialChildCount = root.children.length;
614
- const initialNodeChildCount = root.node.children.length;
615
- // ACT - Detach and reattach
616
- root.detachChild(child1);
617
- root.attachChild(child1);
618
- // ASSERT - Should maintain same children (order may change after detach/reattach)
619
- expect(root.children.length).toBe(initialChildCount);
620
- expect(root.node.children.length).toBe(initialNodeChildCount);
621
- expect(root.children).toContain(child1);
622
- expect(root.children).toContain(child2);
623
- // ASSERT - Verify mirror invariant maintained
624
- verifyTreeMirror(root);
625
- });
626
- it('should satisfy idempotence property', () => {
627
- // ARRANGE
628
- const parent = new SimpleWorkflow('Parent');
629
- const child = new SimpleWorkflow('Child', parent);
630
- // Capture state
631
- const childrenBefore = [...parent.children];
632
- const nodeChildrenBefore = [...parent.node.children];
633
- // ACT & ASSERT - Already attached - should throw
634
- expect(() => {
635
- parent.attachChild(child);
636
- }).toThrow(/already attached/);
637
- // ASSERT - State unchanged after error
638
- expect(parent.children).toEqual(childrenBefore);
639
- expect(parent.node.children).toEqual(nodeChildrenBefore);
640
- verifyTreeMirror(parent);
641
- });
642
- it('should satisfy commutativity property for siblings', () => {
643
- // ARRANGE
644
- const parent = new SimpleWorkflow('Parent');
645
- const child1 = new SimpleWorkflow('Child1');
646
- const child2 = new SimpleWorkflow('Child2');
647
- const child3 = new SimpleWorkflow('Child3');
648
- // ACT - Attach in order 1, 2, 3
649
- parent.attachChild(child1);
650
- parent.attachChild(child2);
651
- parent.attachChild(child3);
652
- const structure1 = [...parent.children];
653
- // Clean up
654
- parent.detachChild(child1);
655
- parent.detachChild(child2);
656
- parent.detachChild(child3);
657
- // Attach in order 3, 2, 1
658
- parent.attachChild(child3);
659
- parent.attachChild(child2);
660
- parent.attachChild(child1);
661
- const structure2 = [...parent.children];
662
- // ASSERT - Same children (in insertion order)
663
- expect(structure1).toEqual([child1, child2, child3]);
664
- expect(structure2).toEqual([child3, child2, child1]);
665
- verifyTreeMirror(parent);
666
- });
667
- });
668
- //# sourceMappingURL=bidirectional-consistency.test.js.map