@substrate-ai/factory 0.20.1 → 0.20.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 (486) hide show
  1. package/README.md +39 -0
  2. package/package.json +6 -3
  3. package/dist/__tests__/config.test.d.ts +0 -11
  4. package/dist/__tests__/config.test.d.ts.map +0 -1
  5. package/dist/__tests__/config.test.js +0 -215
  6. package/dist/__tests__/config.test.js.map +0 -1
  7. package/dist/__tests__/factory-run-command.test.d.ts +0 -12
  8. package/dist/__tests__/factory-run-command.test.d.ts.map +0 -1
  9. package/dist/__tests__/factory-run-command.test.js +0 -454
  10. package/dist/__tests__/factory-run-command.test.js.map +0 -1
  11. package/dist/__tests__/factory-validate-command.test.d.ts +0 -15
  12. package/dist/__tests__/factory-validate-command.test.d.ts.map +0 -1
  13. package/dist/__tests__/factory-validate-command.test.js +0 -339
  14. package/dist/__tests__/factory-validate-command.test.js.map +0 -1
  15. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts +0 -72
  16. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts.map +0 -1
  17. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js +0 -121
  18. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js.map +0 -1
  19. package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts +0 -28
  20. package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts.map +0 -1
  21. package/dist/__tests__/fixtures/llm-edge-routing.dot.js +0 -55
  22. package/dist/__tests__/fixtures/llm-edge-routing.dot.js.map +0 -1
  23. package/dist/__tests__/fixtures/manager-loop.dot.d.ts +0 -34
  24. package/dist/__tests__/fixtures/manager-loop.dot.d.ts.map +0 -1
  25. package/dist/__tests__/fixtures/manager-loop.dot.js +0 -61
  26. package/dist/__tests__/fixtures/manager-loop.dot.js.map +0 -1
  27. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts +0 -42
  28. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts.map +0 -1
  29. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js +0 -118
  30. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js.map +0 -1
  31. package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts +0 -35
  32. package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts.map +0 -1
  33. package/dist/__tests__/fixtures/subgraph-parent.dot.js +0 -69
  34. package/dist/__tests__/fixtures/subgraph-parent.dot.js.map +0 -1
  35. package/dist/__tests__/integration/advanced-graph-events.test.d.ts +0 -19
  36. package/dist/__tests__/integration/advanced-graph-events.test.d.ts.map +0 -1
  37. package/dist/__tests__/integration/advanced-graph-events.test.js +0 -288
  38. package/dist/__tests__/integration/advanced-graph-events.test.js.map +0 -1
  39. package/dist/__tests__/integration/checkpoint-resume.test.d.ts +0 -10
  40. package/dist/__tests__/integration/checkpoint-resume.test.d.ts.map +0 -1
  41. package/dist/__tests__/integration/checkpoint-resume.test.js +0 -125
  42. package/dist/__tests__/integration/checkpoint-resume.test.js.map +0 -1
  43. package/dist/__tests__/integration/conditional-pipeline.test.d.ts +0 -10
  44. package/dist/__tests__/integration/conditional-pipeline.test.d.ts.map +0 -1
  45. package/dist/__tests__/integration/conditional-pipeline.test.js +0 -106
  46. package/dist/__tests__/integration/conditional-pipeline.test.js.map +0 -1
  47. package/dist/__tests__/integration/convergence-validation.test.d.ts +0 -14
  48. package/dist/__tests__/integration/convergence-validation.test.d.ts.map +0 -1
  49. package/dist/__tests__/integration/convergence-validation.test.js +0 -449
  50. package/dist/__tests__/integration/convergence-validation.test.js.map +0 -1
  51. package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts +0 -12
  52. package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts.map +0 -1
  53. package/dist/__tests__/integration/epic44-coverage-gate.test.js +0 -58
  54. package/dist/__tests__/integration/epic44-coverage-gate.test.js.map +0 -1
  55. package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts +0 -11
  56. package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts.map +0 -1
  57. package/dist/__tests__/integration/epic45-coverage-gate.test.js +0 -64
  58. package/dist/__tests__/integration/epic45-coverage-gate.test.js.map +0 -1
  59. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts +0 -2
  60. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts.map +0 -1
  61. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js +0 -285
  62. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js.map +0 -1
  63. package/dist/__tests__/integration/events.test.d.ts +0 -8
  64. package/dist/__tests__/integration/events.test.d.ts.map +0 -1
  65. package/dist/__tests__/integration/events.test.js +0 -194
  66. package/dist/__tests__/integration/events.test.js.map +0 -1
  67. package/dist/__tests__/integration/graphs.d.ts +0 -59
  68. package/dist/__tests__/integration/graphs.d.ts.map +0 -1
  69. package/dist/__tests__/integration/graphs.js +0 -164
  70. package/dist/__tests__/integration/graphs.js.map +0 -1
  71. package/dist/__tests__/integration/helpers.d.ts +0 -127
  72. package/dist/__tests__/integration/helpers.d.ts.map +0 -1
  73. package/dist/__tests__/integration/helpers.js +0 -167
  74. package/dist/__tests__/integration/helpers.js.map +0 -1
  75. package/dist/__tests__/integration/integrity.test.d.ts +0 -8
  76. package/dist/__tests__/integration/integrity.test.d.ts.map +0 -1
  77. package/dist/__tests__/integration/integrity.test.js +0 -198
  78. package/dist/__tests__/integration/integrity.test.js.map +0 -1
  79. package/dist/__tests__/integration/llm-edge-routing.test.d.ts +0 -21
  80. package/dist/__tests__/integration/llm-edge-routing.test.d.ts.map +0 -1
  81. package/dist/__tests__/integration/llm-edge-routing.test.js +0 -341
  82. package/dist/__tests__/integration/llm-edge-routing.test.js.map +0 -1
  83. package/dist/__tests__/integration/manager-loop.test.d.ts +0 -24
  84. package/dist/__tests__/integration/manager-loop.test.d.ts.map +0 -1
  85. package/dist/__tests__/integration/manager-loop.test.js +0 -276
  86. package/dist/__tests__/integration/manager-loop.test.js.map +0 -1
  87. package/dist/__tests__/integration/multi-type-graph.test.d.ts +0 -10
  88. package/dist/__tests__/integration/multi-type-graph.test.d.ts.map +0 -1
  89. package/dist/__tests__/integration/multi-type-graph.test.js +0 -100
  90. package/dist/__tests__/integration/multi-type-graph.test.js.map +0 -1
  91. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts +0 -22
  92. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts.map +0 -1
  93. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js +0 -515
  94. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js.map +0 -1
  95. package/dist/__tests__/integration/persistence.test.d.ts +0 -8
  96. package/dist/__tests__/integration/persistence.test.d.ts.map +0 -1
  97. package/dist/__tests__/integration/persistence.test.js +0 -129
  98. package/dist/__tests__/integration/persistence.test.js.map +0 -1
  99. package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts +0 -16
  100. package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts.map +0 -1
  101. package/dist/__tests__/integration/pipeline-templates-integration.test.js +0 -171
  102. package/dist/__tests__/integration/pipeline-templates-integration.test.js.map +0 -1
  103. package/dist/__tests__/integration/scenario-pipeline.test.d.ts +0 -11
  104. package/dist/__tests__/integration/scenario-pipeline.test.d.ts.map +0 -1
  105. package/dist/__tests__/integration/scenario-pipeline.test.js +0 -243
  106. package/dist/__tests__/integration/scenario-pipeline.test.js.map +0 -1
  107. package/dist/__tests__/integration/stylesheet-application.test.d.ts +0 -12
  108. package/dist/__tests__/integration/stylesheet-application.test.d.ts.map +0 -1
  109. package/dist/__tests__/integration/stylesheet-application.test.js +0 -119
  110. package/dist/__tests__/integration/stylesheet-application.test.js.map +0 -1
  111. package/dist/__tests__/integration/subgraph-execution.test.d.ts +0 -24
  112. package/dist/__tests__/integration/subgraph-execution.test.d.ts.map +0 -1
  113. package/dist/__tests__/integration/subgraph-execution.test.js +0 -291
  114. package/dist/__tests__/integration/subgraph-execution.test.js.map +0 -1
  115. package/dist/__tests__/integration/validation-errors.test.d.ts +0 -8
  116. package/dist/__tests__/integration/validation-errors.test.d.ts.map +0 -1
  117. package/dist/__tests__/integration/validation-errors.test.js +0 -150
  118. package/dist/__tests__/integration/validation-errors.test.js.map +0 -1
  119. package/dist/agent/__tests__/loop-detection.test.d.ts +0 -2
  120. package/dist/agent/__tests__/loop-detection.test.d.ts.map +0 -1
  121. package/dist/agent/__tests__/loop-detection.test.js +0 -236
  122. package/dist/agent/__tests__/loop-detection.test.js.map +0 -1
  123. package/dist/agent/__tests__/loop.test.d.ts +0 -2
  124. package/dist/agent/__tests__/loop.test.d.ts.map +0 -1
  125. package/dist/agent/__tests__/loop.test.js +0 -868
  126. package/dist/agent/__tests__/loop.test.js.map +0 -1
  127. package/dist/agent/__tests__/truncation.test.d.ts +0 -2
  128. package/dist/agent/__tests__/truncation.test.d.ts.map +0 -1
  129. package/dist/agent/__tests__/truncation.test.js +0 -276
  130. package/dist/agent/__tests__/truncation.test.js.map +0 -1
  131. package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts +0 -6
  132. package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts.map +0 -1
  133. package/dist/agent/tools/__tests__/anthropic-tools.test.js +0 -49
  134. package/dist/agent/tools/__tests__/anthropic-tools.test.js.map +0 -1
  135. package/dist/agent/tools/__tests__/environment.test.d.ts +0 -6
  136. package/dist/agent/tools/__tests__/environment.test.d.ts.map +0 -1
  137. package/dist/agent/tools/__tests__/environment.test.js +0 -33
  138. package/dist/agent/tools/__tests__/environment.test.js.map +0 -1
  139. package/dist/agent/tools/__tests__/gemini-tools.test.d.ts +0 -6
  140. package/dist/agent/tools/__tests__/gemini-tools.test.d.ts.map +0 -1
  141. package/dist/agent/tools/__tests__/gemini-tools.test.js +0 -98
  142. package/dist/agent/tools/__tests__/gemini-tools.test.js.map +0 -1
  143. package/dist/agent/tools/__tests__/openai-tools.test.d.ts +0 -6
  144. package/dist/agent/tools/__tests__/openai-tools.test.d.ts.map +0 -1
  145. package/dist/agent/tools/__tests__/openai-tools.test.js +0 -53
  146. package/dist/agent/tools/__tests__/openai-tools.test.js.map +0 -1
  147. package/dist/agent/tools/__tests__/patch.test.d.ts +0 -6
  148. package/dist/agent/tools/__tests__/patch.test.d.ts.map +0 -1
  149. package/dist/agent/tools/__tests__/patch.test.js +0 -116
  150. package/dist/agent/tools/__tests__/patch.test.js.map +0 -1
  151. package/dist/agent/tools/__tests__/profiles.test.d.ts +0 -6
  152. package/dist/agent/tools/__tests__/profiles.test.d.ts.map +0 -1
  153. package/dist/agent/tools/__tests__/profiles.test.js +0 -125
  154. package/dist/agent/tools/__tests__/profiles.test.js.map +0 -1
  155. package/dist/agent/tools/__tests__/registry.test.d.ts +0 -6
  156. package/dist/agent/tools/__tests__/registry.test.d.ts.map +0 -1
  157. package/dist/agent/tools/__tests__/registry.test.js +0 -94
  158. package/dist/agent/tools/__tests__/registry.test.js.map +0 -1
  159. package/dist/agent/tools/__tests__/shared.test.d.ts +0 -6
  160. package/dist/agent/tools/__tests__/shared.test.d.ts.map +0 -1
  161. package/dist/agent/tools/__tests__/shared.test.js +0 -131
  162. package/dist/agent/tools/__tests__/shared.test.js.map +0 -1
  163. package/dist/backend/__tests__/direct-backend.test.d.ts +0 -14
  164. package/dist/backend/__tests__/direct-backend.test.d.ts.map +0 -1
  165. package/dist/backend/__tests__/direct-backend.test.js +0 -393
  166. package/dist/backend/__tests__/direct-backend.test.js.map +0 -1
  167. package/dist/backend/__tests__/direct-bootstrap.test.d.ts +0 -7
  168. package/dist/backend/__tests__/direct-bootstrap.test.d.ts.map +0 -1
  169. package/dist/backend/__tests__/direct-bootstrap.test.js +0 -177
  170. package/dist/backend/__tests__/direct-bootstrap.test.js.map +0 -1
  171. package/dist/backend/__tests__/mock-backend.test.d.ts +0 -7
  172. package/dist/backend/__tests__/mock-backend.test.d.ts.map +0 -1
  173. package/dist/backend/__tests__/mock-backend.test.js +0 -273
  174. package/dist/backend/__tests__/mock-backend.test.js.map +0 -1
  175. package/dist/backend/__tests__/parity.test.d.ts +0 -17
  176. package/dist/backend/__tests__/parity.test.d.ts.map +0 -1
  177. package/dist/backend/__tests__/parity.test.js +0 -411
  178. package/dist/backend/__tests__/parity.test.js.map +0 -1
  179. package/dist/context/__tests__/auto-summarizer.test.d.ts +0 -14
  180. package/dist/context/__tests__/auto-summarizer.test.d.ts.map +0 -1
  181. package/dist/context/__tests__/auto-summarizer.test.js +0 -189
  182. package/dist/context/__tests__/auto-summarizer.test.js.map +0 -1
  183. package/dist/context/__tests__/context-cli-command.test.d.ts +0 -7
  184. package/dist/context/__tests__/context-cli-command.test.d.ts.map +0 -1
  185. package/dist/context/__tests__/context-cli-command.test.js +0 -331
  186. package/dist/context/__tests__/context-cli-command.test.js.map +0 -1
  187. package/dist/context/__tests__/pyramid-summary-integration.test.d.ts +0 -2
  188. package/dist/context/__tests__/pyramid-summary-integration.test.d.ts.map +0 -1
  189. package/dist/context/__tests__/pyramid-summary-integration.test.js +0 -533
  190. package/dist/context/__tests__/pyramid-summary-integration.test.js.map +0 -1
  191. package/dist/context/__tests__/summarizer.test.d.ts +0 -2
  192. package/dist/context/__tests__/summarizer.test.d.ts.map +0 -1
  193. package/dist/context/__tests__/summarizer.test.js +0 -189
  194. package/dist/context/__tests__/summarizer.test.js.map +0 -1
  195. package/dist/context/__tests__/summary-cache.test.d.ts +0 -2
  196. package/dist/context/__tests__/summary-cache.test.d.ts.map +0 -1
  197. package/dist/context/__tests__/summary-cache.test.js +0 -214
  198. package/dist/context/__tests__/summary-cache.test.js.map +0 -1
  199. package/dist/context/__tests__/summary-metrics.test.d.ts +0 -2
  200. package/dist/context/__tests__/summary-metrics.test.d.ts.map +0 -1
  201. package/dist/context/__tests__/summary-metrics.test.js +0 -172
  202. package/dist/context/__tests__/summary-metrics.test.js.map +0 -1
  203. package/dist/context/__tests__/summary-types.test.d.ts +0 -2
  204. package/dist/context/__tests__/summary-types.test.d.ts.map +0 -1
  205. package/dist/context/__tests__/summary-types.test.js +0 -130
  206. package/dist/context/__tests__/summary-types.test.js.map +0 -1
  207. package/dist/convergence/__tests__/budget.test.d.ts +0 -6
  208. package/dist/convergence/__tests__/budget.test.d.ts.map +0 -1
  209. package/dist/convergence/__tests__/budget.test.js +0 -187
  210. package/dist/convergence/__tests__/budget.test.js.map +0 -1
  211. package/dist/convergence/__tests__/controller.test.d.ts +0 -9
  212. package/dist/convergence/__tests__/controller.test.d.ts.map +0 -1
  213. package/dist/convergence/__tests__/controller.test.js +0 -585
  214. package/dist/convergence/__tests__/controller.test.js.map +0 -1
  215. package/dist/convergence/__tests__/dual-signal.test.d.ts +0 -14
  216. package/dist/convergence/__tests__/dual-signal.test.d.ts.map +0 -1
  217. package/dist/convergence/__tests__/dual-signal.test.js +0 -123
  218. package/dist/convergence/__tests__/dual-signal.test.js.map +0 -1
  219. package/dist/convergence/__tests__/epic46-integration.test.d.ts +0 -15
  220. package/dist/convergence/__tests__/epic46-integration.test.d.ts.map +0 -1
  221. package/dist/convergence/__tests__/epic46-integration.test.js +0 -522
  222. package/dist/convergence/__tests__/epic46-integration.test.js.map +0 -1
  223. package/dist/convergence/__tests__/plateau.test.d.ts +0 -6
  224. package/dist/convergence/__tests__/plateau.test.d.ts.map +0 -1
  225. package/dist/convergence/__tests__/plateau.test.js +0 -163
  226. package/dist/convergence/__tests__/plateau.test.js.map +0 -1
  227. package/dist/convergence/__tests__/remediation.test.d.ts +0 -11
  228. package/dist/convergence/__tests__/remediation.test.d.ts.map +0 -1
  229. package/dist/convergence/__tests__/remediation.test.js +0 -209
  230. package/dist/convergence/__tests__/remediation.test.js.map +0 -1
  231. package/dist/convergence/__tests__/scenario-primary.test.d.ts +0 -13
  232. package/dist/convergence/__tests__/scenario-primary.test.d.ts.map +0 -1
  233. package/dist/convergence/__tests__/scenario-primary.test.js +0 -183
  234. package/dist/convergence/__tests__/scenario-primary.test.js.map +0 -1
  235. package/dist/factory-command.test.d.ts +0 -8
  236. package/dist/factory-command.test.d.ts.map +0 -1
  237. package/dist/factory-command.test.js +0 -304
  238. package/dist/factory-command.test.js.map +0 -1
  239. package/dist/graph/__tests__/attractor-compliance.test.d.ts +0 -10
  240. package/dist/graph/__tests__/attractor-compliance.test.d.ts.map +0 -1
  241. package/dist/graph/__tests__/attractor-compliance.test.js +0 -766
  242. package/dist/graph/__tests__/attractor-compliance.test.js.map +0 -1
  243. package/dist/graph/__tests__/checkpoint.test.d.ts +0 -8
  244. package/dist/graph/__tests__/checkpoint.test.d.ts.map +0 -1
  245. package/dist/graph/__tests__/checkpoint.test.js +0 -329
  246. package/dist/graph/__tests__/checkpoint.test.js.map +0 -1
  247. package/dist/graph/__tests__/condition-parser.test.d.ts +0 -14
  248. package/dist/graph/__tests__/condition-parser.test.d.ts.map +0 -1
  249. package/dist/graph/__tests__/condition-parser.test.js +0 -406
  250. package/dist/graph/__tests__/condition-parser.test.js.map +0 -1
  251. package/dist/graph/__tests__/context.test.d.ts +0 -14
  252. package/dist/graph/__tests__/context.test.d.ts.map +0 -1
  253. package/dist/graph/__tests__/context.test.js +0 -276
  254. package/dist/graph/__tests__/context.test.js.map +0 -1
  255. package/dist/graph/__tests__/edge-selector-events.test.d.ts +0 -11
  256. package/dist/graph/__tests__/edge-selector-events.test.d.ts.map +0 -1
  257. package/dist/graph/__tests__/edge-selector-events.test.js +0 -184
  258. package/dist/graph/__tests__/edge-selector-events.test.js.map +0 -1
  259. package/dist/graph/__tests__/edge-selector.test.d.ts +0 -6
  260. package/dist/graph/__tests__/edge-selector.test.d.ts.map +0 -1
  261. package/dist/graph/__tests__/edge-selector.test.js +0 -452
  262. package/dist/graph/__tests__/edge-selector.test.js.map +0 -1
  263. package/dist/graph/__tests__/executor-convergence.test.d.ts +0 -12
  264. package/dist/graph/__tests__/executor-convergence.test.d.ts.map +0 -1
  265. package/dist/graph/__tests__/executor-convergence.test.js +0 -432
  266. package/dist/graph/__tests__/executor-convergence.test.js.map +0 -1
  267. package/dist/graph/__tests__/executor-fidelity.test.d.ts +0 -13
  268. package/dist/graph/__tests__/executor-fidelity.test.d.ts.map +0 -1
  269. package/dist/graph/__tests__/executor-fidelity.test.js +0 -335
  270. package/dist/graph/__tests__/executor-fidelity.test.js.map +0 -1
  271. package/dist/graph/__tests__/executor.test.d.ts +0 -14
  272. package/dist/graph/__tests__/executor.test.d.ts.map +0 -1
  273. package/dist/graph/__tests__/executor.test.js +0 -901
  274. package/dist/graph/__tests__/executor.test.js.map +0 -1
  275. package/dist/graph/__tests__/fidelity.test.d.ts +0 -8
  276. package/dist/graph/__tests__/fidelity.test.d.ts.map +0 -1
  277. package/dist/graph/__tests__/fidelity.test.js +0 -135
  278. package/dist/graph/__tests__/fidelity.test.js.map +0 -1
  279. package/dist/graph/__tests__/llm-evaluator.test.d.ts +0 -7
  280. package/dist/graph/__tests__/llm-evaluator.test.d.ts.map +0 -1
  281. package/dist/graph/__tests__/llm-evaluator.test.js +0 -106
  282. package/dist/graph/__tests__/llm-evaluator.test.js.map +0 -1
  283. package/dist/graph/__tests__/parser-chaining.test.d.ts +0 -13
  284. package/dist/graph/__tests__/parser-chaining.test.d.ts.map +0 -1
  285. package/dist/graph/__tests__/parser-chaining.test.js +0 -215
  286. package/dist/graph/__tests__/parser-chaining.test.js.map +0 -1
  287. package/dist/graph/__tests__/parser.test.d.ts +0 -22
  288. package/dist/graph/__tests__/parser.test.d.ts.map +0 -1
  289. package/dist/graph/__tests__/parser.test.js +0 -452
  290. package/dist/graph/__tests__/parser.test.js.map +0 -1
  291. package/dist/graph/__tests__/run-state.test.d.ts +0 -13
  292. package/dist/graph/__tests__/run-state.test.d.ts.map +0 -1
  293. package/dist/graph/__tests__/run-state.test.js +0 -189
  294. package/dist/graph/__tests__/run-state.test.js.map +0 -1
  295. package/dist/graph/__tests__/transformer.test.d.ts +0 -16
  296. package/dist/graph/__tests__/transformer.test.d.ts.map +0 -1
  297. package/dist/graph/__tests__/transformer.test.js +0 -350
  298. package/dist/graph/__tests__/transformer.test.js.map +0 -1
  299. package/dist/graph/__tests__/validator-errors.test.d.ts +0 -15
  300. package/dist/graph/__tests__/validator-errors.test.d.ts.map +0 -1
  301. package/dist/graph/__tests__/validator-errors.test.js +0 -572
  302. package/dist/graph/__tests__/validator-errors.test.js.map +0 -1
  303. package/dist/graph/__tests__/validator-warnings.test.d.ts +0 -15
  304. package/dist/graph/__tests__/validator-warnings.test.d.ts.map +0 -1
  305. package/dist/graph/__tests__/validator-warnings.test.js +0 -363
  306. package/dist/graph/__tests__/validator-warnings.test.js.map +0 -1
  307. package/dist/handlers/__tests__/codergen-handler.test.d.ts +0 -14
  308. package/dist/handlers/__tests__/codergen-handler.test.d.ts.map +0 -1
  309. package/dist/handlers/__tests__/codergen-handler.test.js +0 -442
  310. package/dist/handlers/__tests__/codergen-handler.test.js.map +0 -1
  311. package/dist/handlers/__tests__/fan-in.test.d.ts +0 -14
  312. package/dist/handlers/__tests__/fan-in.test.d.ts.map +0 -1
  313. package/dist/handlers/__tests__/fan-in.test.js +0 -399
  314. package/dist/handlers/__tests__/fan-in.test.js.map +0 -1
  315. package/dist/handlers/__tests__/join-policy.test.d.ts +0 -9
  316. package/dist/handlers/__tests__/join-policy.test.d.ts.map +0 -1
  317. package/dist/handlers/__tests__/join-policy.test.js +0 -201
  318. package/dist/handlers/__tests__/join-policy.test.js.map +0 -1
  319. package/dist/handlers/__tests__/manager-loop.test.d.ts +0 -14
  320. package/dist/handlers/__tests__/manager-loop.test.d.ts.map +0 -1
  321. package/dist/handlers/__tests__/manager-loop.test.js +0 -322
  322. package/dist/handlers/__tests__/manager-loop.test.js.map +0 -1
  323. package/dist/handlers/__tests__/parallel-events.test.d.ts +0 -12
  324. package/dist/handlers/__tests__/parallel-events.test.d.ts.map +0 -1
  325. package/dist/handlers/__tests__/parallel-events.test.js +0 -252
  326. package/dist/handlers/__tests__/parallel-events.test.js.map +0 -1
  327. package/dist/handlers/__tests__/parallel-handler.test.d.ts +0 -14
  328. package/dist/handlers/__tests__/parallel-handler.test.d.ts.map +0 -1
  329. package/dist/handlers/__tests__/parallel-handler.test.js +0 -337
  330. package/dist/handlers/__tests__/parallel-handler.test.js.map +0 -1
  331. package/dist/handlers/__tests__/parallel-join.test.d.ts +0 -9
  332. package/dist/handlers/__tests__/parallel-join.test.d.ts.map +0 -1
  333. package/dist/handlers/__tests__/parallel-join.test.js +0 -267
  334. package/dist/handlers/__tests__/parallel-join.test.js.map +0 -1
  335. package/dist/handlers/__tests__/registry.test.d.ts +0 -14
  336. package/dist/handlers/__tests__/registry.test.d.ts.map +0 -1
  337. package/dist/handlers/__tests__/registry.test.js +0 -315
  338. package/dist/handlers/__tests__/registry.test.js.map +0 -1
  339. package/dist/handlers/__tests__/subgraph-events.test.d.ts +0 -10
  340. package/dist/handlers/__tests__/subgraph-events.test.d.ts.map +0 -1
  341. package/dist/handlers/__tests__/subgraph-events.test.js +0 -189
  342. package/dist/handlers/__tests__/subgraph-events.test.js.map +0 -1
  343. package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts +0 -14
  344. package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts.map +0 -1
  345. package/dist/handlers/__tests__/subgraph-inheritance.test.js +0 -267
  346. package/dist/handlers/__tests__/subgraph-inheritance.test.js.map +0 -1
  347. package/dist/handlers/__tests__/subgraph.test.d.ts +0 -14
  348. package/dist/handlers/__tests__/subgraph.test.d.ts.map +0 -1
  349. package/dist/handlers/__tests__/subgraph.test.js +0 -369
  350. package/dist/handlers/__tests__/subgraph.test.js.map +0 -1
  351. package/dist/handlers/__tests__/tool-handler.test.d.ts +0 -11
  352. package/dist/handlers/__tests__/tool-handler.test.d.ts.map +0 -1
  353. package/dist/handlers/__tests__/tool-handler.test.js +0 -184
  354. package/dist/handlers/__tests__/tool-handler.test.js.map +0 -1
  355. package/dist/handlers/__tests__/tool-scenario.test.d.ts +0 -12
  356. package/dist/handlers/__tests__/tool-scenario.test.d.ts.map +0 -1
  357. package/dist/handlers/__tests__/tool-scenario.test.js +0 -222
  358. package/dist/handlers/__tests__/tool-scenario.test.js.map +0 -1
  359. package/dist/handlers/__tests__/wait-human-handler.test.d.ts +0 -11
  360. package/dist/handlers/__tests__/wait-human-handler.test.d.ts.map +0 -1
  361. package/dist/handlers/__tests__/wait-human-handler.test.js +0 -251
  362. package/dist/handlers/__tests__/wait-human-handler.test.js.map +0 -1
  363. package/dist/llm/__tests__/client.test.d.ts +0 -2
  364. package/dist/llm/__tests__/client.test.d.ts.map +0 -1
  365. package/dist/llm/__tests__/client.test.js +0 -198
  366. package/dist/llm/__tests__/client.test.js.map +0 -1
  367. package/dist/llm/__tests__/types.test.d.ts +0 -2
  368. package/dist/llm/__tests__/types.test.d.ts.map +0 -1
  369. package/dist/llm/__tests__/types.test.js +0 -289
  370. package/dist/llm/__tests__/types.test.js.map +0 -1
  371. package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts +0 -2
  372. package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts.map +0 -1
  373. package/dist/llm/middleware/__tests__/cost-tracking.test.js +0 -73
  374. package/dist/llm/middleware/__tests__/cost-tracking.test.js.map +0 -1
  375. package/dist/llm/middleware/__tests__/logging.test.d.ts +0 -2
  376. package/dist/llm/middleware/__tests__/logging.test.d.ts.map +0 -1
  377. package/dist/llm/middleware/__tests__/logging.test.js +0 -127
  378. package/dist/llm/middleware/__tests__/logging.test.js.map +0 -1
  379. package/dist/llm/middleware/__tests__/retry.test.d.ts +0 -2
  380. package/dist/llm/middleware/__tests__/retry.test.d.ts.map +0 -1
  381. package/dist/llm/middleware/__tests__/retry.test.js +0 -126
  382. package/dist/llm/middleware/__tests__/retry.test.js.map +0 -1
  383. package/dist/llm/providers/__tests__/anthropic.test.d.ts +0 -2
  384. package/dist/llm/providers/__tests__/anthropic.test.d.ts.map +0 -1
  385. package/dist/llm/providers/__tests__/anthropic.test.js +0 -412
  386. package/dist/llm/providers/__tests__/anthropic.test.js.map +0 -1
  387. package/dist/llm/providers/__tests__/gemini.test.d.ts +0 -2
  388. package/dist/llm/providers/__tests__/gemini.test.d.ts.map +0 -1
  389. package/dist/llm/providers/__tests__/gemini.test.js +0 -591
  390. package/dist/llm/providers/__tests__/gemini.test.js.map +0 -1
  391. package/dist/llm/providers/__tests__/openai.test.d.ts +0 -2
  392. package/dist/llm/providers/__tests__/openai.test.d.ts.map +0 -1
  393. package/dist/llm/providers/__tests__/openai.test.js +0 -546
  394. package/dist/llm/providers/__tests__/openai.test.js.map +0 -1
  395. package/dist/persistence/__tests__/factory-queries.test.d.ts +0 -9
  396. package/dist/persistence/__tests__/factory-queries.test.d.ts.map +0 -1
  397. package/dist/persistence/__tests__/factory-queries.test.js +0 -372
  398. package/dist/persistence/__tests__/factory-queries.test.js.map +0 -1
  399. package/dist/persistence/__tests__/factory-schema.test.d.ts +0 -6
  400. package/dist/persistence/__tests__/factory-schema.test.d.ts.map +0 -1
  401. package/dist/persistence/__tests__/factory-schema.test.js +0 -105
  402. package/dist/persistence/__tests__/factory-schema.test.js.map +0 -1
  403. package/dist/scenarios/__tests__/cli-command-list.test.d.ts +0 -7
  404. package/dist/scenarios/__tests__/cli-command-list.test.d.ts.map +0 -1
  405. package/dist/scenarios/__tests__/cli-command-list.test.js +0 -237
  406. package/dist/scenarios/__tests__/cli-command-list.test.js.map +0 -1
  407. package/dist/scenarios/__tests__/cli-command.test.d.ts +0 -11
  408. package/dist/scenarios/__tests__/cli-command.test.d.ts.map +0 -1
  409. package/dist/scenarios/__tests__/cli-command.test.js +0 -275
  410. package/dist/scenarios/__tests__/cli-command.test.js.map +0 -1
  411. package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts +0 -15
  412. package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts.map +0 -1
  413. package/dist/scenarios/__tests__/integrity-pipeline.test.js +0 -318
  414. package/dist/scenarios/__tests__/integrity-pipeline.test.js.map +0 -1
  415. package/dist/scenarios/__tests__/runner-twins.test.d.ts +0 -13
  416. package/dist/scenarios/__tests__/runner-twins.test.d.ts.map +0 -1
  417. package/dist/scenarios/__tests__/runner-twins.test.js +0 -205
  418. package/dist/scenarios/__tests__/runner-twins.test.js.map +0 -1
  419. package/dist/scenarios/__tests__/scorer.test.d.ts +0 -11
  420. package/dist/scenarios/__tests__/scorer.test.d.ts.map +0 -1
  421. package/dist/scenarios/__tests__/scorer.test.js +0 -225
  422. package/dist/scenarios/__tests__/scorer.test.js.map +0 -1
  423. package/dist/scenarios/__tests__/scoring-integration.test.d.ts +0 -8
  424. package/dist/scenarios/__tests__/scoring-integration.test.d.ts.map +0 -1
  425. package/dist/scenarios/__tests__/scoring-integration.test.js +0 -178
  426. package/dist/scenarios/__tests__/scoring-integration.test.js.map +0 -1
  427. package/dist/scenarios/__tests__/store.test.d.ts +0 -5
  428. package/dist/scenarios/__tests__/store.test.d.ts.map +0 -1
  429. package/dist/scenarios/__tests__/store.test.js +0 -169
  430. package/dist/scenarios/__tests__/store.test.js.map +0 -1
  431. package/dist/stylesheet/__tests__/stylesheet.test.d.ts +0 -17
  432. package/dist/stylesheet/__tests__/stylesheet.test.d.ts.map +0 -1
  433. package/dist/stylesheet/__tests__/stylesheet.test.js +0 -368
  434. package/dist/stylesheet/__tests__/stylesheet.test.js.map +0 -1
  435. package/dist/templates/__tests__/templates.test.d.ts +0 -7
  436. package/dist/templates/__tests__/templates.test.d.ts.map +0 -1
  437. package/dist/templates/__tests__/templates.test.js +0 -92
  438. package/dist/templates/__tests__/templates.test.js.map +0 -1
  439. package/dist/twins/__tests__/docker-compose.test.d.ts +0 -13
  440. package/dist/twins/__tests__/docker-compose.test.d.ts.map +0 -1
  441. package/dist/twins/__tests__/docker-compose.test.js +0 -247
  442. package/dist/twins/__tests__/docker-compose.test.js.map +0 -1
  443. package/dist/twins/__tests__/health-monitor.test.d.ts +0 -19
  444. package/dist/twins/__tests__/health-monitor.test.d.ts.map +0 -1
  445. package/dist/twins/__tests__/health-monitor.test.js +0 -301
  446. package/dist/twins/__tests__/health-monitor.test.js.map +0 -1
  447. package/dist/twins/__tests__/integration/e2e.test.d.ts +0 -18
  448. package/dist/twins/__tests__/integration/e2e.test.d.ts.map +0 -1
  449. package/dist/twins/__tests__/integration/e2e.test.js +0 -146
  450. package/dist/twins/__tests__/integration/e2e.test.js.map +0 -1
  451. package/dist/twins/__tests__/integration/helpers.d.ts +0 -32
  452. package/dist/twins/__tests__/integration/helpers.d.ts.map +0 -1
  453. package/dist/twins/__tests__/integration/helpers.js +0 -67
  454. package/dist/twins/__tests__/integration/helpers.js.map +0 -1
  455. package/dist/twins/__tests__/integration/lifecycle.test.d.ts +0 -14
  456. package/dist/twins/__tests__/integration/lifecycle.test.d.ts.map +0 -1
  457. package/dist/twins/__tests__/integration/lifecycle.test.js +0 -127
  458. package/dist/twins/__tests__/integration/lifecycle.test.js.map +0 -1
  459. package/dist/twins/__tests__/integration/persistence-integration.test.d.ts +0 -14
  460. package/dist/twins/__tests__/integration/persistence-integration.test.d.ts.map +0 -1
  461. package/dist/twins/__tests__/integration/persistence-integration.test.js +0 -132
  462. package/dist/twins/__tests__/integration/persistence-integration.test.js.map +0 -1
  463. package/dist/twins/__tests__/persistence.test.d.ts +0 -10
  464. package/dist/twins/__tests__/persistence.test.d.ts.map +0 -1
  465. package/dist/twins/__tests__/persistence.test.js +0 -300
  466. package/dist/twins/__tests__/persistence.test.js.map +0 -1
  467. package/dist/twins/__tests__/registry.test.d.ts +0 -7
  468. package/dist/twins/__tests__/registry.test.d.ts.map +0 -1
  469. package/dist/twins/__tests__/registry.test.js +0 -282
  470. package/dist/twins/__tests__/registry.test.js.map +0 -1
  471. package/dist/twins/__tests__/run-state.test.d.ts +0 -9
  472. package/dist/twins/__tests__/run-state.test.d.ts.map +0 -1
  473. package/dist/twins/__tests__/run-state.test.js +0 -112
  474. package/dist/twins/__tests__/run-state.test.js.map +0 -1
  475. package/dist/twins/__tests__/templates-cli.test.d.ts +0 -10
  476. package/dist/twins/__tests__/templates-cli.test.d.ts.map +0 -1
  477. package/dist/twins/__tests__/templates-cli.test.js +0 -187
  478. package/dist/twins/__tests__/templates-cli.test.js.map +0 -1
  479. package/dist/twins/__tests__/templates.test.d.ts +0 -7
  480. package/dist/twins/__tests__/templates.test.d.ts.map +0 -1
  481. package/dist/twins/__tests__/templates.test.js +0 -87
  482. package/dist/twins/__tests__/templates.test.js.map +0 -1
  483. package/dist/twins/__tests__/twins-cli.test.d.ts +0 -11
  484. package/dist/twins/__tests__/twins-cli.test.d.ts.map +0 -1
  485. package/dist/twins/__tests__/twins-cli.test.js +0 -365
  486. package/dist/twins/__tests__/twins-cli.test.js.map +0 -1
@@ -1,766 +0,0 @@
1
- /**
2
- * Spec compliance tests for the Attractor graph engine.
3
- *
4
- * Replays pseudocode examples from the Attractor spec through
5
- * selectEdge(), evaluateGates(), and checkpoint resume APIs.
6
- *
7
- * Story 42-17.
8
- */
9
- // AttractorBench structural conformance parity — Phase A mandatory
10
- // See: https://github.com/strongdm/attractorbench
11
- // Behavioral conformance tests are advisory in Phase A; required by Phase B exit.
12
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
13
- import os from 'node:os';
14
- import path from 'node:path';
15
- import { mkdir, rm } from 'node:fs/promises';
16
- import crypto from 'node:crypto';
17
- import { selectEdge } from '../edge-selector.js';
18
- import { createConvergenceController } from '../../convergence/controller.js';
19
- import { CheckpointManager } from '../checkpoint.js';
20
- import { createGraphExecutor } from '../executor.js';
21
- import { createValidator } from '../validator.js';
22
- import { parseGraph } from '../parser.js';
23
- import { GraphContext } from '../context.js';
24
- // ---------------------------------------------------------------------------
25
- // DOT fixture for resume/fidelity tests (spec Section 5.3)
26
- // ---------------------------------------------------------------------------
27
- const COMPLIANCE_RESUME_DOT = `
28
- digraph compliance_resume {
29
- graph [goal="Resume compliance test"]
30
- start [shape=Mdiamond]
31
- node1 [type=codergen, prompt="Step 1"]
32
- node2 [type=codergen, prompt="Step 2"]
33
- exit [shape=Msquare]
34
-
35
- start -> node1
36
- node1 -> node2
37
- node2 -> exit
38
- }
39
- `;
40
- const COMPLIANCE_FIDELITY_RESUME_DOT = `
41
- digraph compliance_fidelity_resume {
42
- graph [goal="Fidelity degradation test"]
43
- start [shape=Mdiamond]
44
- node1 [type=codergen, prompt="Step 1", fidelity=full]
45
- node2 [type=codergen, prompt="Step 2"]
46
- exit [shape=Msquare]
47
-
48
- start -> node1
49
- node1 -> node2
50
- node2 -> exit
51
- }
52
- `;
53
- // ---------------------------------------------------------------------------
54
- // Shared helper factories
55
- // ---------------------------------------------------------------------------
56
- /** A node with all required fields at sensible defaults. */
57
- const MINIMAL_NODE = {
58
- id: '',
59
- label: '',
60
- shape: 'box',
61
- type: 'codergen',
62
- prompt: '',
63
- maxRetries: 0,
64
- goalGate: false,
65
- retryTarget: '',
66
- fallbackRetryTarget: '',
67
- fidelity: '',
68
- threadId: '',
69
- class: '',
70
- timeout: 0,
71
- llmModel: '',
72
- llmProvider: '',
73
- reasoningEffort: '',
74
- autoStatus: false,
75
- allowPartial: false,
76
- toolCommand: '',
77
- backend: '',
78
- };
79
- function makeNode(id, overrides) {
80
- return { ...MINIMAL_NODE, id, ...overrides };
81
- }
82
- function makeEdge(fromNode, toNode, overrides) {
83
- return {
84
- fromNode,
85
- toNode,
86
- label: '',
87
- condition: '',
88
- weight: 0,
89
- fidelity: '',
90
- threadId: '',
91
- loopRestart: false,
92
- ...overrides,
93
- };
94
- }
95
- /**
96
- * Build a minimal Graph conforming to the Graph interface.
97
- * startNodeId / exitNodeId control which nodes serve as start/exit.
98
- */
99
- function makeGraph(nodes, edges, startNodeId, exitNodeId) {
100
- const nodeMap = new Map(nodes.map((n) => [n.id, n]));
101
- const resolveStart = () => {
102
- if (startNodeId)
103
- return nodeMap.get(startNodeId);
104
- for (const n of nodeMap.values()) {
105
- if (n.shape === 'Mdiamond')
106
- return n;
107
- }
108
- throw new Error('No start node');
109
- };
110
- const resolveExit = () => {
111
- if (exitNodeId)
112
- return nodeMap.get(exitNodeId);
113
- for (const n of nodeMap.values()) {
114
- if (n.shape === 'Msquare')
115
- return n;
116
- }
117
- throw new Error('No exit node');
118
- };
119
- return {
120
- id: '',
121
- goal: '',
122
- label: '',
123
- modelStylesheet: '',
124
- defaultMaxRetries: 0,
125
- retryTarget: '',
126
- fallbackRetryTarget: '',
127
- defaultFidelity: '',
128
- nodes: nodeMap,
129
- edges,
130
- outgoingEdges: (nodeId) => edges.filter((e) => e.fromNode === nodeId),
131
- startNode: resolveStart,
132
- exitNode: resolveExit,
133
- };
134
- }
135
- /** Build a Graph with only the minimal start/exit structure for lint-rule tests. */
136
- function makeLintGraph(extraNodes, extraEdges, overrides) {
137
- const baseNodes = [
138
- makeNode('start', { shape: 'Mdiamond', type: '' }),
139
- makeNode('exit', { shape: 'Msquare', type: '' }),
140
- ...extraNodes.map((n) => makeNode(n.id, n)),
141
- ];
142
- const baseEdges = extraEdges.map((e) => makeEdge(e.fromNode, e.toNode, {
143
- condition: e.condition ?? '',
144
- loopRestart: e.loopRestart ?? false,
145
- }));
146
- const nodeMap = new Map(baseNodes.map((n) => [n.id, n]));
147
- return {
148
- id: 'test',
149
- goal: '',
150
- label: '',
151
- modelStylesheet: overrides?.modelStylesheet ?? '',
152
- defaultMaxRetries: 0,
153
- retryTarget: overrides?.retryTarget ?? '',
154
- fallbackRetryTarget: overrides?.fallbackRetryTarget ?? '',
155
- defaultFidelity: '',
156
- nodes: nodeMap,
157
- edges: baseEdges,
158
- outgoingEdges: (nodeId) => baseEdges.filter((e) => e.fromNode === nodeId),
159
- startNode: () => nodeMap.get('start'),
160
- exitNode: () => nodeMap.get('exit'),
161
- };
162
- }
163
- /** Build a mock IHandlerRegistry that resolves every node via the given handler factory. */
164
- function makeRegistry(handlerFactory, statusOverride) {
165
- return {
166
- register: vi.fn(),
167
- registerShape: vi.fn(),
168
- setDefault: vi.fn(),
169
- resolve: vi.fn().mockImplementation(() => async (node, ctx) => {
170
- handlerFactory(node, ctx);
171
- return { status: statusOverride ?? 'SUCCESS' };
172
- }),
173
- };
174
- }
175
- /** Build a mock TypedEventBus that captures all emitted events. */
176
- function makeEventBus() {
177
- const nodeStartedIds = [];
178
- const bus = {
179
- emit: vi.fn().mockImplementation((event, payload) => {
180
- if (event === 'graph:node-started') {
181
- nodeStartedIds.push(payload.nodeId);
182
- }
183
- }),
184
- on: vi.fn(),
185
- off: vi.fn(),
186
- };
187
- return { bus, nodeStartedIds: () => [...nodeStartedIds] };
188
- }
189
- // ---------------------------------------------------------------------------
190
- // Task 2: Edge selection compliance — spec Section 3.3
191
- // ---------------------------------------------------------------------------
192
- describe('selectEdge compliance — spec Section 3.3', () => {
193
- // Step 1: Condition-matched edges beat weight
194
- it('AC1 — Step 1: condition match returns conditional edge over high-weight unconditional', async () => {
195
- const node = makeNode('origin');
196
- const condEdge = makeEdge('origin', 'dest-conditional', { condition: 'outcome=success', weight: 0 });
197
- const unconEdge = makeEdge('origin', 'dest-unconditional', { weight: 10 });
198
- const graph = makeGraph([node], [condEdge, unconEdge]);
199
- const ctx = new GraphContext({ outcome: 'success' });
200
- const outcome = { status: 'SUCCESS' };
201
- const selected = await selectEdge(node, outcome, ctx, graph);
202
- expect(selected).toBe(condEdge);
203
- });
204
- it('AC1 variant — Step 1: no condition match falls through to Step 4 (weight)', async () => {
205
- const node = makeNode('origin');
206
- const condEdge = makeEdge('origin', 'dest-conditional', { condition: 'outcome=success', weight: 0 });
207
- const unconEdge = makeEdge('origin', 'dest-unconditional', { weight: 10 });
208
- const graph = makeGraph([node], [condEdge, unconEdge]);
209
- const ctx = new GraphContext({ outcome: 'failure' });
210
- const outcome = { status: 'SUCCESS' };
211
- const selected = await selectEdge(node, outcome, ctx, graph);
212
- expect(selected).toBe(unconEdge);
213
- });
214
- // Step 2: Preferred label with accelerator prefix stripping
215
- it('AC2 — Step 2: [Y] prefix stripped, label matches preferredLabel "yes"', async () => {
216
- const node = makeNode('origin');
217
- const yesEdge = makeEdge('origin', 'yes-target', { label: '[Y] Yes' });
218
- const noEdge = makeEdge('origin', 'no-target', { label: 'No' });
219
- const graph = makeGraph([node], [yesEdge, noEdge]);
220
- const ctx = new GraphContext();
221
- const outcome = { status: 'SUCCESS', preferredLabel: 'yes' };
222
- const selected = await selectEdge(node, outcome, ctx, graph);
223
- expect(selected).toBe(yesEdge);
224
- });
225
- it('AC2 variant — Step 2: Y) prefix stripped, label matches preferredLabel "confirm"', async () => {
226
- const node = makeNode('origin');
227
- const confirmEdge = makeEdge('origin', 'confirm-target', { label: 'Y) Confirm' });
228
- const otherEdge = makeEdge('origin', 'other-target', { label: 'Cancel' });
229
- const graph = makeGraph([node], [confirmEdge, otherEdge]);
230
- const ctx = new GraphContext();
231
- const outcome = { status: 'SUCCESS', preferredLabel: 'confirm' };
232
- const selected = await selectEdge(node, outcome, ctx, graph);
233
- expect(selected).toBe(confirmEdge);
234
- });
235
- // Step 3: Suggested next IDs
236
- it('Step 3: suggestedNextIds matches edge target', async () => {
237
- const node = makeNode('origin');
238
- const edgeA = makeEdge('origin', 'a');
239
- const edgeB = makeEdge('origin', 'b');
240
- const edgeC = makeEdge('origin', 'c');
241
- const graph = makeGraph([node], [edgeA, edgeB, edgeC]);
242
- const ctx = new GraphContext();
243
- const outcome = { status: 'SUCCESS', suggestedNextIds: ['b'] };
244
- const selected = await selectEdge(node, outcome, ctx, graph);
245
- expect(selected).toBe(edgeB);
246
- });
247
- // Steps 4 & 5: Weight with lexical tiebreak
248
- it('AC3 — Steps 4&5: weight 5 tie between charlie and alpha → lexically-first alpha wins', async () => {
249
- const node = makeNode('origin');
250
- const charlieEdge = makeEdge('origin', 'charlie', { weight: 5 });
251
- const alphaEdge = makeEdge('origin', 'alpha', { weight: 5 });
252
- const bravoEdge = makeEdge('origin', 'bravo', { weight: 3 });
253
- const graph = makeGraph([node], [charlieEdge, alphaEdge, bravoEdge]);
254
- const ctx = new GraphContext();
255
- const outcome = { status: 'SUCCESS' };
256
- const selected = await selectEdge(node, outcome, ctx, graph);
257
- expect(selected).toBe(alphaEdge);
258
- expect(selected?.toNode).toBe('alpha');
259
- });
260
- // Empty edges
261
- it('empty edges: no outgoing edges → returns null', async () => {
262
- const node = makeNode('origin');
263
- const graph = makeGraph([node], []);
264
- const ctx = new GraphContext();
265
- const outcome = { status: 'SUCCESS' };
266
- const selected = await selectEdge(node, outcome, ctx, graph);
267
- expect(selected).toBeNull();
268
- });
269
- });
270
- // ---------------------------------------------------------------------------
271
- // Task 3: Goal gate compliance — spec Section 3.4
272
- // ---------------------------------------------------------------------------
273
- describe('evaluateGates compliance — spec Section 3.4', () => {
274
- it('SUCCESS satisfies a goalGate=true node', () => {
275
- const controller = createConvergenceController();
276
- controller.recordOutcome('nodeA', 'SUCCESS');
277
- const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
278
- const result = controller.evaluateGates(graph);
279
- expect(result).toEqual({ satisfied: true, failingNodes: [] });
280
- });
281
- it('AC4 — PARTIAL_SUCCESS satisfies a goalGate=true node (spec Section 3.4)', () => {
282
- const controller = createConvergenceController();
283
- controller.recordOutcome('nodeA', 'PARTIAL_SUCCESS');
284
- const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
285
- const result = controller.evaluateGates(graph);
286
- expect(result).toEqual({ satisfied: true, failingNodes: [] });
287
- });
288
- it('AC4 — FAILURE does not satisfy goalGate=true node, appears in failingNodes', () => {
289
- const controller = createConvergenceController();
290
- controller.recordOutcome('nodeA', 'FAILURE');
291
- const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
292
- const result = controller.evaluateGates(graph);
293
- expect(result.satisfied).toBe(false);
294
- expect(result.failingNodes).toContain('nodeA');
295
- });
296
- it('AC4 — Unrecorded outcome (never executed) fails gate', () => {
297
- const controller = createConvergenceController();
298
- // nodeA has no recorded outcome
299
- const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
300
- const result = controller.evaluateGates(graph);
301
- expect(result.satisfied).toBe(false);
302
- expect(result.failingNodes).toContain('nodeA');
303
- });
304
- it('AC4 — PARTIAL_SUCCESS satisfies; FAILURE and unrecorded both fail', () => {
305
- const controller = createConvergenceController();
306
- controller.recordOutcome('partial', 'PARTIAL_SUCCESS');
307
- controller.recordOutcome('failing', 'FAILURE');
308
- // 'unrecorded' has no recorded outcome
309
- const graph = makeGraph([
310
- makeNode('partial', { goalGate: true }),
311
- makeNode('failing', { goalGate: true }),
312
- makeNode('unrecorded', { goalGate: true }),
313
- ], []);
314
- const result = controller.evaluateGates(graph);
315
- expect(result.satisfied).toBe(false);
316
- expect(result.failingNodes).toContain('failing');
317
- expect(result.failingNodes).toContain('unrecorded');
318
- expect(result.failingNodes).not.toContain('partial');
319
- });
320
- it('mixed SUCCESS + PARTIAL_SUCCESS: both satisfied → { satisfied: true, failingNodes: [] }', () => {
321
- const controller = createConvergenceController();
322
- controller.recordOutcome('nodeA', 'SUCCESS');
323
- controller.recordOutcome('nodeB', 'PARTIAL_SUCCESS');
324
- const graph = makeGraph([makeNode('nodeA', { goalGate: true }), makeNode('nodeB', { goalGate: true })], []);
325
- const result = controller.evaluateGates(graph);
326
- expect(result).toEqual({ satisfied: true, failingNodes: [] });
327
- });
328
- it('mixed SUCCESS + FAILURE: only failing node in failingNodes', () => {
329
- const controller = createConvergenceController();
330
- controller.recordOutcome('nodeA', 'SUCCESS');
331
- controller.recordOutcome('nodeB', 'FAILURE');
332
- const graph = makeGraph([makeNode('nodeA', { goalGate: true }), makeNode('nodeB', { goalGate: true })], []);
333
- const result = controller.evaluateGates(graph);
334
- expect(result.satisfied).toBe(false);
335
- expect(result.failingNodes).toEqual(['nodeB']);
336
- expect(result.failingNodes).not.toContain('nodeA');
337
- });
338
- it('no goalGate=true nodes: vacuously satisfied', () => {
339
- const controller = createConvergenceController();
340
- const graph = makeGraph([makeNode('nodeA', { goalGate: false }), makeNode('nodeB', { goalGate: false })], []);
341
- const result = controller.evaluateGates(graph);
342
- expect(result).toEqual({ satisfied: true, failingNodes: [] });
343
- });
344
- });
345
- // ---------------------------------------------------------------------------
346
- // Task 4: Checkpoint resume compliance — spec Section 5.3
347
- // ---------------------------------------------------------------------------
348
- describe('checkpoint resume compliance — spec Section 5.3', () => {
349
- let tmpDir;
350
- beforeEach(async () => {
351
- tmpDir = path.join(os.tmpdir(), `attractor-compliance-${crypto.randomUUID()}`);
352
- await mkdir(tmpDir, { recursive: true });
353
- });
354
- afterEach(async () => {
355
- await rm(tmpDir, { recursive: true, force: true });
356
- });
357
- it('AC5 — completed nodes are skipped and context is restored on resume', async () => {
358
- // Write seed checkpoint using CheckpointManager.save()
359
- const checkpointManager = new CheckpointManager();
360
- const seedContext = new GraphContext({ result: 'ok', attempt: '3' });
361
- await checkpointManager.save(tmpDir, {
362
- currentNode: 'node1',
363
- completedNodes: ['start', 'node1'],
364
- nodeRetries: {},
365
- context: seedContext,
366
- });
367
- // Parse and validate the compliance resume graph
368
- const graph = parseGraph(COMPLIANCE_RESUME_DOT);
369
- const validator = createValidator();
370
- const validationErrors = validator.validate(graph).filter((d) => d.severity === 'error');
371
- expect(validationErrors).toHaveLength(0);
372
- // Track graph:node-started events and captured contexts per node
373
- const { bus, nodeStartedIds } = makeEventBus();
374
- const capturedContextByNode = {};
375
- const registry = makeRegistry((node, ctx) => {
376
- capturedContextByNode[node.id] = ctx;
377
- });
378
- const executor = createGraphExecutor();
379
- const config = {
380
- runId: 'compliance-resume-test',
381
- logsRoot: tmpDir,
382
- handlerRegistry: registry,
383
- eventBus: bus,
384
- checkpointPath: path.join(tmpDir, 'checkpoint.json'),
385
- };
386
- const finalOutcome = await executor.run(graph, config);
387
- // graph:node-started must NOT include 'start' or 'node1' (skipped)
388
- expect(nodeStartedIds()).not.toContain('start');
389
- expect(nodeStartedIds()).not.toContain('node1');
390
- // graph:node-started MUST include 'node2' and 'exit' is never dispatched (exit check)
391
- expect(nodeStartedIds()).toContain('node2');
392
- // Context is restored: node2 handler receives context seeded from checkpoint
393
- expect(capturedContextByNode['node2']).toBeDefined();
394
- expect(capturedContextByNode['node2'].getString('result')).toBe('ok');
395
- expect(capturedContextByNode['node2'].getString('attempt')).toBe('3');
396
- // Final outcome is SUCCESS
397
- expect(finalOutcome.status).toBe('SUCCESS');
398
- });
399
- });
400
- // ---------------------------------------------------------------------------
401
- // Task 5: Fidelity degradation on resume — spec Section 5.3 step 6
402
- // ---------------------------------------------------------------------------
403
- describe('fidelity degradation on resume — spec Section 5.3 step 6', () => {
404
- let tmpDir;
405
- beforeEach(async () => {
406
- tmpDir = path.join(os.tmpdir(), `attractor-fidelity-${crypto.randomUUID()}`);
407
- await mkdir(tmpDir, { recursive: true });
408
- });
409
- afterEach(async () => {
410
- await rm(tmpDir, { recursive: true, force: true });
411
- });
412
- it('first resumed node gets fidelity="summary:high" when last completed node had fidelity="full"', async () => {
413
- // Write seed checkpoint: node1 (which has fidelity=full in the DOT) is the last completed node
414
- const checkpointManager = new CheckpointManager();
415
- const seedContext = new GraphContext({ result: 'ok' });
416
- await checkpointManager.save(tmpDir, {
417
- currentNode: 'node1',
418
- completedNodes: ['start', 'node1'],
419
- nodeRetries: {},
420
- context: seedContext,
421
- });
422
- // Parse graph where node1 has fidelity=full
423
- const graph = parseGraph(COMPLIANCE_FIDELITY_RESUME_DOT);
424
- // Capture the node argument passed to each handler dispatch
425
- const capturedNodeByNodeId = {};
426
- const registry = makeRegistry((node) => {
427
- capturedNodeByNodeId[node.id] = node;
428
- });
429
- const { bus } = makeEventBus();
430
- const config = {
431
- runId: 'compliance-fidelity-test',
432
- logsRoot: tmpDir,
433
- handlerRegistry: registry,
434
- eventBus: bus,
435
- checkpointPath: path.join(tmpDir, 'checkpoint.json'),
436
- };
437
- await createGraphExecutor().run(graph, config);
438
- // node2 is the first resumed node; its fidelity should be degraded to 'summary:high'
439
- expect(capturedNodeByNodeId['node2']).toBeDefined();
440
- expect(capturedNodeByNodeId['node2'].fidelity).toBe('summary:high');
441
- });
442
- it('subsequent resumed nodes use their configured fidelity (not the degraded value)', async () => {
443
- // Extend the graph by adding node3 after node2
444
- const EXTENDED_DOT = `
445
- digraph compliance_fidelity_extended {
446
- graph [goal="Extended fidelity test"]
447
- start [shape=Mdiamond]
448
- node1 [type=codergen, prompt="Step 1", fidelity=full]
449
- node2 [type=codergen, prompt="Step 2", fidelity=medium]
450
- node3 [type=codergen, prompt="Step 3", fidelity=high]
451
- exit [shape=Msquare]
452
-
453
- start -> node1
454
- node1 -> node2
455
- node2 -> node3
456
- node3 -> exit
457
- }
458
- `;
459
- const checkpointManager = new CheckpointManager();
460
- const seedCtx = new GraphContext();
461
- await checkpointManager.save(tmpDir, {
462
- currentNode: 'node1',
463
- completedNodes: ['start', 'node1'],
464
- nodeRetries: {},
465
- context: seedCtx,
466
- });
467
- const graph = parseGraph(EXTENDED_DOT);
468
- const capturedNodeByNodeId = {};
469
- const registry = makeRegistry((node) => {
470
- capturedNodeByNodeId[node.id] = node;
471
- });
472
- const { bus } = makeEventBus();
473
- const config = {
474
- runId: 'compliance-fidelity-extended-test',
475
- logsRoot: tmpDir,
476
- handlerRegistry: registry,
477
- eventBus: bus,
478
- checkpointPath: path.join(tmpDir, 'checkpoint.json'),
479
- };
480
- await createGraphExecutor().run(graph, config);
481
- // node2 is first resumed → degraded to 'summary:high'
482
- expect(capturedNodeByNodeId['node2'].fidelity).toBe('summary:high');
483
- // node3 is subsequent → uses its configured fidelity='high'
484
- expect(capturedNodeByNodeId['node3'].fidelity).toBe('high');
485
- });
486
- });
487
- // ---------------------------------------------------------------------------
488
- // Task 6: Structural conformance — AttractorBench parity
489
- // ---------------------------------------------------------------------------
490
- describe('structural conformance — AttractorBench parity', () => {
491
- // AttractorBench structural conformance parity — Phase A mandatory
492
- // See: https://github.com/strongdm/attractorbench
493
- // Behavioral conformance tests are advisory in Phase A; required by Phase B exit.
494
- // -------------------------------------------------------------------------
495
- // Attribute coverage — nodes (all 17 spec-defined + toolCommand from 42-11 + backend from 48-10)
496
- // -------------------------------------------------------------------------
497
- it('AC6 — all node attributes extracted with correct values from DOT', () => {
498
- const dot = `
499
- digraph compliance_node_attrs {
500
- graph [goal="Attribute coverage"]
501
- start [shape=Mdiamond]
502
- target [
503
- shape=box,
504
- type=codergen,
505
- label="Target Label",
506
- prompt="Target prompt",
507
- max_retries=3,
508
- goal_gate=true,
509
- retry_target="start",
510
- fallback_retry_target="start",
511
- fidelity=high,
512
- thread_id="thread-1",
513
- class="myclass",
514
- timeout=30,
515
- llm_model="gpt-4o",
516
- llm_provider=openai,
517
- reasoning_effort=high,
518
- auto_status=false,
519
- allow_partial=true,
520
- tool_command="echo hello",
521
- backend=direct
522
- ]
523
- exit [shape=Msquare]
524
- start -> target
525
- target -> exit
526
- }
527
- `;
528
- const graph = parseGraph(dot);
529
- const node = graph.nodes.get('target');
530
- expect(node).toBeDefined();
531
- expect(node.id).toBe('target');
532
- expect(node.label).toBe('Target Label');
533
- expect(node.shape).toBe('box');
534
- expect(node.type).toBe('codergen');
535
- expect(node.prompt).toBe('Target prompt');
536
- expect(node.maxRetries).toBe(3);
537
- expect(node.goalGate).toBe(true);
538
- expect(node.retryTarget).toBe('start');
539
- expect(node.fallbackRetryTarget).toBe('start');
540
- expect(node.fidelity).toBe('high');
541
- expect(node.threadId).toBe('thread-1');
542
- expect(node.class).toBe('myclass');
543
- expect(node.timeout).toBe(30);
544
- expect(node.llmModel).toBe('gpt-4o');
545
- expect(node.llmProvider).toBe('openai');
546
- expect(node.reasoningEffort).toBe('high');
547
- expect(node.autoStatus).toBe(false);
548
- expect(node.allowPartial).toBe(true);
549
- expect(node.toolCommand).toBe('echo hello');
550
- expect(node.backend).toBe('direct');
551
- });
552
- // -------------------------------------------------------------------------
553
- // Attribute coverage — edges (all 6 edge attributes)
554
- // -------------------------------------------------------------------------
555
- it('AC6 — all 6 edge attributes extracted with correct values from DOT', () => {
556
- const dot = `
557
- digraph compliance_edge_attrs {
558
- graph [goal="Edge attribute coverage"]
559
- start [shape=Mdiamond]
560
- nodeA [type=codergen, prompt="a"]
561
- exit [shape=Msquare]
562
- start -> nodeA [
563
- label="go",
564
- condition="outcome=success",
565
- weight=5,
566
- fidelity=high,
567
- thread_id="t2",
568
- loop_restart=true
569
- ]
570
- nodeA -> exit
571
- }
572
- `;
573
- const graph = parseGraph(dot);
574
- const edge = graph.edges.find((e) => e.fromNode === 'start' && e.toNode === 'nodeA');
575
- expect(edge).toBeDefined();
576
- expect(edge.label).toBe('go');
577
- expect(edge.condition).toBe('outcome=success');
578
- expect(edge.weight).toBe(5);
579
- expect(edge.fidelity).toBe('high');
580
- expect(edge.threadId).toBe('t2');
581
- expect(edge.loopRestart).toBe(true);
582
- });
583
- // -------------------------------------------------------------------------
584
- // Lint rule coverage — 8 error-level rules (stories 42-4)
585
- // -------------------------------------------------------------------------
586
- it('AC6 — lint rule "start_node" fires (severity=error) when no start node exists', () => {
587
- // Graph with no Mdiamond node and no id=start/Start
588
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [{ fromNode: 'work', toNode: 'exit' }]);
589
- // Override the nodes map to remove start (the makeLintGraph adds start by default — use manual build)
590
- const nodeMap = new Map();
591
- nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
592
- nodeMap.set('exit', makeNode('exit', { shape: 'Msquare', type: '' }));
593
- const edges = [makeEdge('work', 'exit')];
594
- const g = {
595
- id: 'test', goal: '', label: '', modelStylesheet: '',
596
- defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
597
- nodes: nodeMap,
598
- edges,
599
- outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
600
- startNode: () => { throw new Error('no start'); },
601
- exitNode: () => nodeMap.get('exit'),
602
- };
603
- const diags = createValidator().validate(g);
604
- const rule = diags.find((d) => d.ruleId === 'start_node');
605
- expect(rule).toBeDefined();
606
- expect(rule.severity).toBe('error');
607
- });
608
- it('AC6 — lint rule "terminal_node" fires (severity=error) when no exit node exists', () => {
609
- const nodeMap = new Map();
610
- nodeMap.set('start', makeNode('start', { shape: 'Mdiamond', type: '' }));
611
- nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
612
- const edges = [makeEdge('start', 'work')];
613
- const g = {
614
- id: 'test', goal: '', label: '', modelStylesheet: '',
615
- defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
616
- nodes: nodeMap,
617
- edges,
618
- outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
619
- startNode: () => nodeMap.get('start'),
620
- exitNode: () => { throw new Error('no exit'); },
621
- };
622
- const diags = createValidator().validate(g);
623
- const rule = diags.find((d) => d.ruleId === 'terminal_node');
624
- expect(rule).toBeDefined();
625
- expect(rule.severity).toBe('error');
626
- });
627
- it('AC6 — lint rule "start_no_incoming" fires (severity=error) when edge targets the start node', () => {
628
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
629
- { fromNode: 'start', toNode: 'work' },
630
- { fromNode: 'work', toNode: 'exit' },
631
- { fromNode: 'work', toNode: 'start' }, // incoming edge to start
632
- ]);
633
- const diags = createValidator().validate(graph);
634
- const rule = diags.find((d) => d.ruleId === 'start_no_incoming');
635
- expect(rule).toBeDefined();
636
- expect(rule.severity).toBe('error');
637
- });
638
- it('AC6 — lint rule "exit_no_outgoing" fires (severity=error) when exit node has an outgoing edge', () => {
639
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
640
- { fromNode: 'start', toNode: 'work' },
641
- { fromNode: 'work', toNode: 'exit' },
642
- { fromNode: 'exit', toNode: 'work' }, // outgoing edge from exit
643
- ]);
644
- const diags = createValidator().validate(graph);
645
- const rule = diags.find((d) => d.ruleId === 'exit_no_outgoing');
646
- expect(rule).toBeDefined();
647
- expect(rule.severity).toBe('error');
648
- });
649
- it('AC6 — lint rule "edge_target_exists" fires (severity=error) when edge targets non-existent node', () => {
650
- // Build graph directly — DOT auto-creates referenced nodes, so we use in-memory construction
651
- const nodeMap = new Map();
652
- nodeMap.set('start', makeNode('start', { shape: 'Mdiamond', type: '' }));
653
- nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
654
- nodeMap.set('exit', makeNode('exit', { shape: 'Msquare', type: '' }));
655
- const edges = [
656
- makeEdge('start', 'work'),
657
- makeEdge('work', 'exit'),
658
- makeEdge('work', 'ghost_node'), // ghost_node does NOT exist in nodeMap
659
- ];
660
- const g = {
661
- id: 'test', goal: '', label: '', modelStylesheet: '',
662
- defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
663
- nodes: nodeMap,
664
- edges,
665
- outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
666
- startNode: () => nodeMap.get('start'),
667
- exitNode: () => nodeMap.get('exit'),
668
- };
669
- const diags = createValidator().validate(g);
670
- const rule = diags.find((d) => d.ruleId === 'edge_target_exists');
671
- expect(rule).toBeDefined();
672
- expect(rule.severity).toBe('error');
673
- });
674
- it('AC6 — lint rule "reachability" fires (severity=error) when a node is unreachable', () => {
675
- const graph = makeLintGraph([
676
- { id: 'work', type: 'codergen', prompt: 'work' },
677
- { id: 'unreachable', type: 'codergen', prompt: 'unreachable' },
678
- ], [
679
- { fromNode: 'start', toNode: 'work' },
680
- { fromNode: 'work', toNode: 'exit' },
681
- // 'unreachable' has no incoming edges from start
682
- ]);
683
- const diags = createValidator().validate(graph);
684
- const rule = diags.find((d) => d.ruleId === 'reachability');
685
- expect(rule).toBeDefined();
686
- expect(rule.severity).toBe('error');
687
- });
688
- it('AC6 — lint rule "condition_syntax" fires (severity=error) for invalid condition expression', () => {
689
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
690
- { fromNode: 'start', toNode: 'work' },
691
- { fromNode: 'work', toNode: 'exit', condition: 'a==b' }, // double-equals is invalid
692
- ]);
693
- const diags = createValidator().validate(graph);
694
- const rule = diags.find((d) => d.ruleId === 'condition_syntax');
695
- expect(rule).toBeDefined();
696
- expect(rule.severity).toBe('error');
697
- });
698
- it('AC6 — lint rule "stylesheet_syntax" fires (severity=error) for invalid stylesheet content', () => {
699
- // Build graph with invalid inline stylesheet
700
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }], { modelStylesheet: '!!! invalid stylesheet content !!!' });
701
- const diags = createValidator().validate(graph);
702
- const rule = diags.find((d) => d.ruleId === 'stylesheet_syntax');
703
- expect(rule).toBeDefined();
704
- expect(rule.severity).toBe('error');
705
- });
706
- // -------------------------------------------------------------------------
707
- // Lint rule coverage — 5 warning-level rules (stories 42-5)
708
- // -------------------------------------------------------------------------
709
- it('AC6 — lint rule "type_known" fires (severity=warning) for unknown node type', () => {
710
- const graph = makeLintGraph([{ id: 'work', type: 'wizard_type', prompt: 'work' }], // wizard_type is unknown
711
- [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
712
- const diags = createValidator().validate(graph);
713
- const rule = diags.find((d) => d.ruleId === 'type_known');
714
- expect(rule).toBeDefined();
715
- expect(rule.severity).toBe('warning');
716
- });
717
- it('AC6 — lint rule "fidelity_valid" fires (severity=warning) for invalid fidelity value', () => {
718
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', fidelity: 'ultra_high' }], // invalid fidelity
719
- [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
720
- const diags = createValidator().validate(graph);
721
- const rule = diags.find((d) => d.ruleId === 'fidelity_valid');
722
- expect(rule).toBeDefined();
723
- expect(rule.severity).toBe('warning');
724
- });
725
- it('AC6 — lint rule "retry_target_exists" fires (severity=warning) for missing retryTarget node', () => {
726
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', retryTarget: 'nonexistent' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
727
- const diags = createValidator().validate(graph);
728
- const rule = diags.find((d) => d.ruleId === 'retry_target_exists');
729
- expect(rule).toBeDefined();
730
- expect(rule.severity).toBe('warning');
731
- });
732
- it('AC6 — lint rule "goal_gate_has_retry" fires (severity=warning) when goalGate=true node has no retryTarget', () => {
733
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', goalGate: true, retryTarget: '' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
734
- const diags = createValidator().validate(graph);
735
- const rule = diags.find((d) => d.ruleId === 'goal_gate_has_retry');
736
- expect(rule).toBeDefined();
737
- expect(rule.severity).toBe('warning');
738
- });
739
- it('AC6 — lint rule "prompt_on_llm_nodes" fires (severity=warning) for codergen node with no prompt or label', () => {
740
- const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: '', label: '' }], // no prompt AND no label
741
- [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
742
- const diags = createValidator().validate(graph);
743
- const rule = diags.find((d) => d.ruleId === 'prompt_on_llm_nodes');
744
- expect(rule).toBeDefined();
745
- expect(rule.severity).toBe('warning');
746
- });
747
- // -------------------------------------------------------------------------
748
- // Edge selection determinism — identical inputs always produce same output
749
- // -------------------------------------------------------------------------
750
- it('AC6 — selectEdge is deterministic: identical inputs always produce same edge', async () => {
751
- const node = makeNode('origin');
752
- const edgeA = makeEdge('origin', 'alpha', { weight: 5 });
753
- const edgeB = makeEdge('origin', 'bravo', { weight: 5 });
754
- const edgeC = makeEdge('origin', 'charlie', { weight: 3 });
755
- const graph = makeGraph([node], [edgeA, edgeB, edgeC]);
756
- const ctx = new GraphContext({ x: '1' });
757
- const outcome = { status: 'SUCCESS' };
758
- const first = await selectEdge(node, outcome, ctx, graph);
759
- const second = await selectEdge(node, outcome, ctx, graph);
760
- const third = await selectEdge(node, outcome, ctx, graph);
761
- expect(first?.toNode).toBe(second?.toNode);
762
- expect(second?.toNode).toBe(third?.toNode);
763
- expect(first?.toNode).toBe('alpha'); // lexically first among weight-5 ties
764
- });
765
- });
766
- //# sourceMappingURL=attractor-compliance.test.js.map