@gitgov/core 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 (469) hide show
  1. package/README.md +303 -0
  2. package/dist/scripts/compile-json-to-types.d.ts +11 -0
  3. package/dist/scripts/compile-json-to-types.d.ts.map +1 -0
  4. package/dist/scripts/compile-json-to-types.js +129 -0
  5. package/dist/scripts/compile-json-to-types.js.map +1 -0
  6. package/dist/scripts/diagnose-duplicates.d.ts +7 -0
  7. package/dist/scripts/diagnose-duplicates.d.ts.map +1 -0
  8. package/dist/scripts/diagnose-duplicates.js +72 -0
  9. package/dist/scripts/diagnose-duplicates.js.map +1 -0
  10. package/dist/scripts/generate-indexes.d.ts +22 -0
  11. package/dist/scripts/generate-indexes.d.ts.map +1 -0
  12. package/dist/scripts/generate-indexes.js +168 -0
  13. package/dist/scripts/generate-indexes.js.map +1 -0
  14. package/dist/scripts/sync-schemas.d.ts +10 -0
  15. package/dist/scripts/sync-schemas.d.ts.map +1 -0
  16. package/dist/scripts/sync-schemas.js +84 -0
  17. package/dist/scripts/sync-schemas.js.map +1 -0
  18. package/dist/scripts/sync-workflow-configs.d.ts +11 -0
  19. package/dist/scripts/sync-workflow-configs.d.ts.map +1 -0
  20. package/dist/scripts/sync-workflow-configs.js +61 -0
  21. package/dist/scripts/sync-workflow-configs.js.map +1 -0
  22. package/dist/scripts/update-diagram.d.ts +7 -0
  23. package/dist/scripts/update-diagram.d.ts.map +1 -0
  24. package/dist/scripts/update-diagram.js +47 -0
  25. package/dist/scripts/update-diagram.js.map +1 -0
  26. package/dist/scripts/validate-schemas.d.ts +18 -0
  27. package/dist/scripts/validate-schemas.d.ts.map +1 -0
  28. package/dist/scripts/validate-schemas.js +313 -0
  29. package/dist/scripts/validate-schemas.js.map +1 -0
  30. package/dist/src/adapters/backlog_adapter/backlog_adapter.integration.test.d.ts +2 -0
  31. package/dist/src/adapters/backlog_adapter/backlog_adapter.integration.test.d.ts.map +1 -0
  32. package/dist/src/adapters/backlog_adapter/backlog_adapter.integration.test.js +507 -0
  33. package/dist/src/adapters/backlog_adapter/backlog_adapter.integration.test.js.map +1 -0
  34. package/dist/src/adapters/backlog_adapter/backlog_adapter.test.d.ts +2 -0
  35. package/dist/src/adapters/backlog_adapter/backlog_adapter.test.d.ts.map +1 -0
  36. package/dist/src/adapters/backlog_adapter/backlog_adapter.test.js +1282 -0
  37. package/dist/src/adapters/backlog_adapter/backlog_adapter.test.js.map +1 -0
  38. package/dist/src/adapters/backlog_adapter/backlog_adapter_e2e.test.d.ts +10 -0
  39. package/dist/src/adapters/backlog_adapter/backlog_adapter_e2e.test.d.ts.map +1 -0
  40. package/dist/src/adapters/backlog_adapter/backlog_adapter_e2e.test.js +144 -0
  41. package/dist/src/adapters/backlog_adapter/backlog_adapter_e2e.test.js.map +1 -0
  42. package/dist/src/adapters/backlog_adapter/index.d.ts +213 -0
  43. package/dist/src/adapters/backlog_adapter/index.d.ts.map +1 -0
  44. package/dist/src/adapters/backlog_adapter/index.js +924 -0
  45. package/dist/src/adapters/backlog_adapter/index.js.map +1 -0
  46. package/dist/src/adapters/changelog_adapter/changelog_adapter.test.d.ts +2 -0
  47. package/dist/src/adapters/changelog_adapter/changelog_adapter.test.d.ts.map +1 -0
  48. package/dist/src/adapters/changelog_adapter/changelog_adapter.test.js +505 -0
  49. package/dist/src/adapters/changelog_adapter/changelog_adapter.test.js.map +1 -0
  50. package/dist/src/adapters/changelog_adapter/index.d.ts +101 -0
  51. package/dist/src/adapters/changelog_adapter/index.d.ts.map +1 -0
  52. package/dist/src/adapters/changelog_adapter/index.js +197 -0
  53. package/dist/src/adapters/changelog_adapter/index.js.map +1 -0
  54. package/dist/src/adapters/execution_adapter/execution_adapter.test.d.ts +2 -0
  55. package/dist/src/adapters/execution_adapter/execution_adapter.test.d.ts.map +1 -0
  56. package/dist/src/adapters/execution_adapter/execution_adapter.test.js +266 -0
  57. package/dist/src/adapters/execution_adapter/execution_adapter.test.js.map +1 -0
  58. package/dist/src/adapters/execution_adapter/index.d.ts +90 -0
  59. package/dist/src/adapters/execution_adapter/index.d.ts.map +1 -0
  60. package/dist/src/adapters/execution_adapter/index.js +150 -0
  61. package/dist/src/adapters/execution_adapter/index.js.map +1 -0
  62. package/dist/src/adapters/feedback_adapter/feedback_adapter.test.d.ts +2 -0
  63. package/dist/src/adapters/feedback_adapter/feedback_adapter.test.d.ts.map +1 -0
  64. package/dist/src/adapters/feedback_adapter/feedback_adapter.test.js +256 -0
  65. package/dist/src/adapters/feedback_adapter/feedback_adapter.test.js.map +1 -0
  66. package/dist/src/adapters/feedback_adapter/index.d.ts +95 -0
  67. package/dist/src/adapters/feedback_adapter/index.d.ts.map +1 -0
  68. package/dist/src/adapters/feedback_adapter/index.js +192 -0
  69. package/dist/src/adapters/feedback_adapter/index.js.map +1 -0
  70. package/dist/src/adapters/identity_adapter/identity_adapter.test.d.ts +2 -0
  71. package/dist/src/adapters/identity_adapter/identity_adapter.test.d.ts.map +1 -0
  72. package/dist/src/adapters/identity_adapter/identity_adapter.test.js +624 -0
  73. package/dist/src/adapters/identity_adapter/identity_adapter.test.js.map +1 -0
  74. package/dist/src/adapters/identity_adapter/index.d.ts +82 -0
  75. package/dist/src/adapters/identity_adapter/index.d.ts.map +1 -0
  76. package/dist/src/adapters/identity_adapter/index.js +336 -0
  77. package/dist/src/adapters/identity_adapter/index.js.map +1 -0
  78. package/dist/src/adapters/index.d.ts +10 -0
  79. package/dist/src/adapters/index.d.ts.map +1 -0
  80. package/dist/src/adapters/index.js +10 -0
  81. package/dist/src/adapters/index.js.map +1 -0
  82. package/dist/src/adapters/indexer_adapter/index.d.ts +214 -0
  83. package/dist/src/adapters/indexer_adapter/index.d.ts.map +1 -0
  84. package/dist/src/adapters/indexer_adapter/index.js +643 -0
  85. package/dist/src/adapters/indexer_adapter/index.js.map +1 -0
  86. package/dist/src/adapters/indexer_adapter/indexer_adapter.test.d.ts +2 -0
  87. package/dist/src/adapters/indexer_adapter/indexer_adapter.test.d.ts.map +1 -0
  88. package/dist/src/adapters/indexer_adapter/indexer_adapter.test.js +409 -0
  89. package/dist/src/adapters/indexer_adapter/indexer_adapter.test.js.map +1 -0
  90. package/dist/src/adapters/metrics_adapter/index.d.ts +189 -0
  91. package/dist/src/adapters/metrics_adapter/index.d.ts.map +1 -0
  92. package/dist/src/adapters/metrics_adapter/index.js +592 -0
  93. package/dist/src/adapters/metrics_adapter/index.js.map +1 -0
  94. package/dist/src/adapters/metrics_adapter/metrics_adapter.test.d.ts +2 -0
  95. package/dist/src/adapters/metrics_adapter/metrics_adapter.test.d.ts.map +1 -0
  96. package/dist/src/adapters/metrics_adapter/metrics_adapter.test.js +558 -0
  97. package/dist/src/adapters/metrics_adapter/metrics_adapter.test.js.map +1 -0
  98. package/dist/src/adapters/project_adapter/index.d.ts +164 -0
  99. package/dist/src/adapters/project_adapter/index.d.ts.map +1 -0
  100. package/dist/src/adapters/project_adapter/index.js +445 -0
  101. package/dist/src/adapters/project_adapter/index.js.map +1 -0
  102. package/dist/src/adapters/project_adapter/project_adapter.test.d.ts +2 -0
  103. package/dist/src/adapters/project_adapter/project_adapter.test.d.ts.map +1 -0
  104. package/dist/src/adapters/project_adapter/project_adapter.test.js +627 -0
  105. package/dist/src/adapters/project_adapter/project_adapter.test.js.map +1 -0
  106. package/dist/src/adapters/workflow_methodology_adapter/index.d.ts +75 -0
  107. package/dist/src/adapters/workflow_methodology_adapter/index.d.ts.map +1 -0
  108. package/dist/src/adapters/workflow_methodology_adapter/index.js +205 -0
  109. package/dist/src/adapters/workflow_methodology_adapter/index.js.map +1 -0
  110. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter.test.d.ts +2 -0
  111. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter.test.d.ts.map +1 -0
  112. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter.test.js +463 -0
  113. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter.test.js.map +1 -0
  114. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_integration.test.d.ts +2 -0
  115. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_integration.test.d.ts.map +1 -0
  116. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_integration.test.js +287 -0
  117. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_integration.test.js.map +1 -0
  118. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_scrum_integration.test.d.ts +2 -0
  119. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_scrum_integration.test.d.ts.map +1 -0
  120. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_scrum_integration.test.js +278 -0
  121. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_adapter_scrum_integration.test.js.map +1 -0
  122. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_default.json +188 -0
  123. package/dist/src/adapters/workflow_methodology_adapter/workflow_methodology_scrum.json +284 -0
  124. package/dist/src/config_manager/config_manager.test.d.ts +2 -0
  125. package/dist/src/config_manager/config_manager.test.d.ts.map +1 -0
  126. package/dist/src/config_manager/config_manager.test.js +321 -0
  127. package/dist/src/config_manager/config_manager.test.js.map +1 -0
  128. package/dist/src/config_manager/index.d.ts +107 -0
  129. package/dist/src/config_manager/index.d.ts.map +1 -0
  130. package/dist/src/config_manager/index.js +192 -0
  131. package/dist/src/config_manager/index.js.map +1 -0
  132. package/dist/src/crypto/checksum.d.ts +6 -0
  133. package/dist/src/crypto/checksum.d.ts.map +1 -0
  134. package/dist/src/crypto/checksum.js +38 -0
  135. package/dist/src/crypto/checksum.js.map +1 -0
  136. package/dist/src/crypto/checksum.test.d.ts +2 -0
  137. package/dist/src/crypto/checksum.test.d.ts.map +1 -0
  138. package/dist/src/crypto/checksum.test.js +167 -0
  139. package/dist/src/crypto/checksum.test.js.map +1 -0
  140. package/dist/src/crypto/index.d.ts +3 -0
  141. package/dist/src/crypto/index.d.ts.map +1 -0
  142. package/dist/src/crypto/index.js +3 -0
  143. package/dist/src/crypto/index.js.map +1 -0
  144. package/dist/src/crypto/signatures.d.ts +24 -0
  145. package/dist/src/crypto/signatures.d.ts.map +1 -0
  146. package/dist/src/crypto/signatures.js +67 -0
  147. package/dist/src/crypto/signatures.js.map +1 -0
  148. package/dist/src/crypto/signatures.test.d.ts +2 -0
  149. package/dist/src/crypto/signatures.test.d.ts.map +1 -0
  150. package/dist/src/crypto/signatures.test.js +378 -0
  151. package/dist/src/crypto/signatures.test.js.map +1 -0
  152. package/dist/src/diagram_generator/diagram_generator.d.ts +76 -0
  153. package/dist/src/diagram_generator/diagram_generator.d.ts.map +1 -0
  154. package/dist/src/diagram_generator/diagram_generator.js +233 -0
  155. package/dist/src/diagram_generator/diagram_generator.js.map +1 -0
  156. package/dist/src/diagram_generator/graph_validator.d.ts +19 -0
  157. package/dist/src/diagram_generator/graph_validator.d.ts.map +1 -0
  158. package/dist/src/diagram_generator/graph_validator.js +98 -0
  159. package/dist/src/diagram_generator/graph_validator.js.map +1 -0
  160. package/dist/src/diagram_generator/index.d.ts +4 -0
  161. package/dist/src/diagram_generator/index.d.ts.map +1 -0
  162. package/dist/src/diagram_generator/index.js +4 -0
  163. package/dist/src/diagram_generator/index.js.map +1 -0
  164. package/dist/src/diagram_generator/mermaid_renderer.d.ts +82 -0
  165. package/dist/src/diagram_generator/mermaid_renderer.d.ts.map +1 -0
  166. package/dist/src/diagram_generator/mermaid_renderer.js +306 -0
  167. package/dist/src/diagram_generator/mermaid_renderer.js.map +1 -0
  168. package/dist/src/diagram_generator/relationship_analyzer.d.ts +116 -0
  169. package/dist/src/diagram_generator/relationship_analyzer.d.ts.map +1 -0
  170. package/dist/src/diagram_generator/relationship_analyzer.js +381 -0
  171. package/dist/src/diagram_generator/relationship_analyzer.js.map +1 -0
  172. package/dist/src/event_bus/event_bus.d.ts +110 -0
  173. package/dist/src/event_bus/event_bus.d.ts.map +1 -0
  174. package/dist/src/event_bus/event_bus.js +161 -0
  175. package/dist/src/event_bus/event_bus.js.map +1 -0
  176. package/dist/src/event_bus/event_bus.test.d.ts +2 -0
  177. package/dist/src/event_bus/event_bus.test.d.ts.map +1 -0
  178. package/dist/src/event_bus/event_bus.test.js +332 -0
  179. package/dist/src/event_bus/event_bus.test.js.map +1 -0
  180. package/dist/src/event_bus/event_bus_integration.test.d.ts +2 -0
  181. package/dist/src/event_bus/event_bus_integration.test.d.ts.map +1 -0
  182. package/dist/src/event_bus/event_bus_integration.test.js +474 -0
  183. package/dist/src/event_bus/event_bus_integration.test.js.map +1 -0
  184. package/dist/src/event_bus/index.d.ts +3 -0
  185. package/dist/src/event_bus/index.d.ts.map +1 -0
  186. package/dist/src/event_bus/index.js +3 -0
  187. package/dist/src/event_bus/index.js.map +1 -0
  188. package/dist/src/event_bus/types.d.ts +203 -0
  189. package/dist/src/event_bus/types.d.ts.map +1 -0
  190. package/dist/src/event_bus/types.js +5 -0
  191. package/dist/src/event_bus/types.js.map +1 -0
  192. package/dist/src/factories/actor_factory.d.ts +6 -0
  193. package/dist/src/factories/actor_factory.d.ts.map +1 -0
  194. package/dist/src/factories/actor_factory.js +25 -0
  195. package/dist/src/factories/actor_factory.js.map +1 -0
  196. package/dist/src/factories/actor_factory.test.d.ts +2 -0
  197. package/dist/src/factories/actor_factory.test.d.ts.map +1 -0
  198. package/dist/src/factories/actor_factory.test.js +169 -0
  199. package/dist/src/factories/actor_factory.test.js.map +1 -0
  200. package/dist/src/factories/agent_factory.d.ts +6 -0
  201. package/dist/src/factories/agent_factory.d.ts.map +1 -0
  202. package/dist/src/factories/agent_factory.js +25 -0
  203. package/dist/src/factories/agent_factory.js.map +1 -0
  204. package/dist/src/factories/agent_factory.test.d.ts +2 -0
  205. package/dist/src/factories/agent_factory.test.d.ts.map +1 -0
  206. package/dist/src/factories/agent_factory.test.js +199 -0
  207. package/dist/src/factories/agent_factory.test.js.map +1 -0
  208. package/dist/src/factories/changelog_factory.d.ts +9 -0
  209. package/dist/src/factories/changelog_factory.d.ts.map +1 -0
  210. package/dist/src/factories/changelog_factory.js +46 -0
  211. package/dist/src/factories/changelog_factory.js.map +1 -0
  212. package/dist/src/factories/changelog_factory.test.d.ts +2 -0
  213. package/dist/src/factories/changelog_factory.test.d.ts.map +1 -0
  214. package/dist/src/factories/changelog_factory.test.js +368 -0
  215. package/dist/src/factories/changelog_factory.test.js.map +1 -0
  216. package/dist/src/factories/cycle_factory.d.ts +6 -0
  217. package/dist/src/factories/cycle_factory.d.ts.map +1 -0
  218. package/dist/src/factories/cycle_factory.js +28 -0
  219. package/dist/src/factories/cycle_factory.js.map +1 -0
  220. package/dist/src/factories/cycle_factory.test.d.ts +2 -0
  221. package/dist/src/factories/cycle_factory.test.d.ts.map +1 -0
  222. package/dist/src/factories/cycle_factory.test.js +175 -0
  223. package/dist/src/factories/cycle_factory.test.js.map +1 -0
  224. package/dist/src/factories/execution_factory.d.ts +9 -0
  225. package/dist/src/factories/execution_factory.d.ts.map +1 -0
  226. package/dist/src/factories/execution_factory.js +29 -0
  227. package/dist/src/factories/execution_factory.js.map +1 -0
  228. package/dist/src/factories/execution_factory.test.d.ts +2 -0
  229. package/dist/src/factories/execution_factory.test.d.ts.map +1 -0
  230. package/dist/src/factories/execution_factory.test.js +207 -0
  231. package/dist/src/factories/execution_factory.test.js.map +1 -0
  232. package/dist/src/factories/feedback_factory.d.ts +9 -0
  233. package/dist/src/factories/feedback_factory.d.ts.map +1 -0
  234. package/dist/src/factories/feedback_factory.js +30 -0
  235. package/dist/src/factories/feedback_factory.js.map +1 -0
  236. package/dist/src/factories/feedback_factory.test.d.ts +2 -0
  237. package/dist/src/factories/feedback_factory.test.d.ts.map +1 -0
  238. package/dist/src/factories/feedback_factory.test.js +256 -0
  239. package/dist/src/factories/feedback_factory.test.js.map +1 -0
  240. package/dist/src/factories/index.d.ts +9 -0
  241. package/dist/src/factories/index.d.ts.map +1 -0
  242. package/dist/src/factories/index.js +9 -0
  243. package/dist/src/factories/index.js.map +1 -0
  244. package/dist/src/factories/task_factory.d.ts +6 -0
  245. package/dist/src/factories/task_factory.d.ts.map +1 -0
  246. package/dist/src/factories/task_factory.js +30 -0
  247. package/dist/src/factories/task_factory.js.map +1 -0
  248. package/dist/src/factories/task_factory.test.d.ts +2 -0
  249. package/dist/src/factories/task_factory.test.d.ts.map +1 -0
  250. package/dist/src/factories/task_factory.test.js +197 -0
  251. package/dist/src/factories/task_factory.test.js.map +1 -0
  252. package/dist/src/factories/workflow_methodology_factory.d.ts +11 -0
  253. package/dist/src/factories/workflow_methodology_factory.d.ts.map +1 -0
  254. package/dist/src/factories/workflow_methodology_factory.js +173 -0
  255. package/dist/src/factories/workflow_methodology_factory.js.map +1 -0
  256. package/dist/src/factories/workflow_methodology_factory.test.d.ts +2 -0
  257. package/dist/src/factories/workflow_methodology_factory.test.d.ts.map +1 -0
  258. package/dist/src/factories/workflow_methodology_factory.test.js +244 -0
  259. package/dist/src/factories/workflow_methodology_factory.test.js.map +1 -0
  260. package/dist/src/index.d.ts +21 -0
  261. package/dist/src/index.d.ts.map +1 -0
  262. package/dist/src/index.js +23 -0
  263. package/dist/src/index.js.map +1 -0
  264. package/dist/src/integration/cycles_tasks_integration.test.d.ts +2 -0
  265. package/dist/src/integration/cycles_tasks_integration.test.d.ts.map +1 -0
  266. package/dist/src/integration/cycles_tasks_integration.test.js +258 -0
  267. package/dist/src/integration/cycles_tasks_integration.test.js.map +1 -0
  268. package/dist/src/integration/schema_integration.test.d.ts +8 -0
  269. package/dist/src/integration/schema_integration.test.d.ts.map +1 -0
  270. package/dist/src/integration/schema_integration.test.js +279 -0
  271. package/dist/src/integration/schema_integration.test.js.map +1 -0
  272. package/dist/src/logger/index.d.ts +2 -0
  273. package/dist/src/logger/index.d.ts.map +1 -0
  274. package/dist/src/logger/index.js +2 -0
  275. package/dist/src/logger/index.js.map +1 -0
  276. package/dist/src/logger/logger.d.ts +10 -0
  277. package/dist/src/logger/logger.d.ts.map +1 -0
  278. package/dist/src/logger/logger.js +48 -0
  279. package/dist/src/logger/logger.js.map +1 -0
  280. package/dist/src/schemas/errors.d.ts +28 -0
  281. package/dist/src/schemas/errors.d.ts.map +1 -0
  282. package/dist/src/schemas/errors.js +31 -0
  283. package/dist/src/schemas/errors.js.map +1 -0
  284. package/dist/src/schemas/generated/actor_record_schema.json +91 -0
  285. package/dist/src/schemas/generated/agent_record_schema.json +142 -0
  286. package/dist/src/schemas/generated/changelog_record_schema.json +227 -0
  287. package/dist/src/schemas/generated/cycle_record_schema.json +80 -0
  288. package/dist/src/schemas/generated/embedded_metadata_schema.json +318 -0
  289. package/dist/src/schemas/generated/execution_record_schema.json +89 -0
  290. package/dist/src/schemas/generated/feedback_record_schema.json +83 -0
  291. package/dist/src/schemas/generated/index.d.ts +2221 -0
  292. package/dist/src/schemas/generated/index.d.ts.map +1 -0
  293. package/dist/src/schemas/generated/index.js +48 -0
  294. package/dist/src/schemas/generated/index.js.map +1 -0
  295. package/dist/src/schemas/generated/task_record_schema.json +103 -0
  296. package/dist/src/schemas/generated/workflow_methodology_record_schema.json +393 -0
  297. package/dist/src/schemas/index.d.ts +4 -0
  298. package/dist/src/schemas/index.d.ts.map +1 -0
  299. package/dist/src/schemas/index.js +4 -0
  300. package/dist/src/schemas/index.js.map +1 -0
  301. package/dist/src/schemas/schema_cache.d.ts +39 -0
  302. package/dist/src/schemas/schema_cache.d.ts.map +1 -0
  303. package/dist/src/schemas/schema_cache.js +109 -0
  304. package/dist/src/schemas/schema_cache.js.map +1 -0
  305. package/dist/src/schemas/schema_cache.test.d.ts +2 -0
  306. package/dist/src/schemas/schema_cache.test.d.ts.map +1 -0
  307. package/dist/src/schemas/schema_cache.test.js +163 -0
  308. package/dist/src/schemas/schema_cache.test.js.map +1 -0
  309. package/dist/src/store/index.d.ts +2 -0
  310. package/dist/src/store/index.d.ts.map +1 -0
  311. package/dist/src/store/index.js +2 -0
  312. package/dist/src/store/index.js.map +1 -0
  313. package/dist/src/store/record_store.d.ts +30 -0
  314. package/dist/src/store/record_store.d.ts.map +1 -0
  315. package/dist/src/store/record_store.js +83 -0
  316. package/dist/src/store/record_store.js.map +1 -0
  317. package/dist/src/store/record_store.test.d.ts +2 -0
  318. package/dist/src/store/record_store.test.d.ts.map +1 -0
  319. package/dist/src/store/record_store.test.js +646 -0
  320. package/dist/src/store/record_store.test.js.map +1 -0
  321. package/dist/src/types/common.types.d.ts +43 -0
  322. package/dist/src/types/common.types.d.ts.map +1 -0
  323. package/dist/src/types/common.types.js +13 -0
  324. package/dist/src/types/common.types.js.map +1 -0
  325. package/dist/src/types/embedded.types.d.ts +17 -0
  326. package/dist/src/types/embedded.types.d.ts.map +1 -0
  327. package/dist/src/types/embedded.types.js +2 -0
  328. package/dist/src/types/embedded.types.js.map +1 -0
  329. package/dist/src/types/generated/actor_record.d.ts +45 -0
  330. package/dist/src/types/generated/actor_record.d.ts.map +1 -0
  331. package/dist/src/types/generated/actor_record.js +7 -0
  332. package/dist/src/types/generated/actor_record.js.map +1 -0
  333. package/dist/src/types/generated/agent_record.d.ts +40 -0
  334. package/dist/src/types/generated/agent_record.d.ts.map +1 -0
  335. package/dist/src/types/generated/agent_record.js +7 -0
  336. package/dist/src/types/generated/agent_record.js.map +1 -0
  337. package/dist/src/types/generated/changelog_record.d.ts +100 -0
  338. package/dist/src/types/generated/changelog_record.d.ts.map +1 -0
  339. package/dist/src/types/generated/changelog_record.js +7 -0
  340. package/dist/src/types/generated/changelog_record.js.map +1 -0
  341. package/dist/src/types/generated/cycle_record.d.ts +36 -0
  342. package/dist/src/types/generated/cycle_record.d.ts.map +1 -0
  343. package/dist/src/types/generated/cycle_record.js +7 -0
  344. package/dist/src/types/generated/cycle_record.js.map +1 -0
  345. package/dist/src/types/generated/embedded_metadata.d.ts +94 -0
  346. package/dist/src/types/generated/embedded_metadata.d.ts.map +1 -0
  347. package/dist/src/types/generated/embedded_metadata.js +7 -0
  348. package/dist/src/types/generated/embedded_metadata.js.map +1 -0
  349. package/dist/src/types/generated/execution_record.d.ts +39 -0
  350. package/dist/src/types/generated/execution_record.d.ts.map +1 -0
  351. package/dist/src/types/generated/execution_record.js +7 -0
  352. package/dist/src/types/generated/execution_record.js.map +1 -0
  353. package/dist/src/types/generated/feedback_record.d.ts +43 -0
  354. package/dist/src/types/generated/feedback_record.d.ts.map +1 -0
  355. package/dist/src/types/generated/feedback_record.js +7 -0
  356. package/dist/src/types/generated/feedback_record.js.map +1 -0
  357. package/dist/src/types/generated/index.d.ts +15 -0
  358. package/dist/src/types/generated/index.d.ts.map +1 -0
  359. package/dist/src/types/generated/index.js +15 -0
  360. package/dist/src/types/generated/index.js.map +1 -0
  361. package/dist/src/types/generated/task_record.d.ts +47 -0
  362. package/dist/src/types/generated/task_record.d.ts.map +1 -0
  363. package/dist/src/types/generated/task_record.js +7 -0
  364. package/dist/src/types/generated/task_record.js.map +1 -0
  365. package/dist/src/types/generated/workflow_methodology_record.d.ts +211 -0
  366. package/dist/src/types/generated/workflow_methodology_record.d.ts.map +1 -0
  367. package/dist/src/types/generated/workflow_methodology_record.js +7 -0
  368. package/dist/src/types/generated/workflow_methodology_record.js.map +1 -0
  369. package/dist/src/types/index.d.ts +4 -0
  370. package/dist/src/types/index.d.ts.map +1 -0
  371. package/dist/src/types/index.js +4 -0
  372. package/dist/src/types/index.js.map +1 -0
  373. package/dist/src/utils/id_generator.d.ts +44 -0
  374. package/dist/src/utils/id_generator.d.ts.map +1 -0
  375. package/dist/src/utils/id_generator.js +107 -0
  376. package/dist/src/utils/id_generator.js.map +1 -0
  377. package/dist/src/utils/id_generator.test.d.ts +2 -0
  378. package/dist/src/utils/id_generator.test.d.ts.map +1 -0
  379. package/dist/src/utils/id_generator.test.js +100 -0
  380. package/dist/src/utils/id_generator.test.js.map +1 -0
  381. package/dist/src/utils/index.d.ts +2 -0
  382. package/dist/src/utils/index.d.ts.map +1 -0
  383. package/dist/src/utils/index.js +3 -0
  384. package/dist/src/utils/index.js.map +1 -0
  385. package/dist/src/validation/actor_validator.d.ts +21 -0
  386. package/dist/src/validation/actor_validator.d.ts.map +1 -0
  387. package/dist/src/validation/actor_validator.js +48 -0
  388. package/dist/src/validation/actor_validator.js.map +1 -0
  389. package/dist/src/validation/actor_validator.test.d.ts +2 -0
  390. package/dist/src/validation/actor_validator.test.d.ts.map +1 -0
  391. package/dist/src/validation/actor_validator.test.js +83 -0
  392. package/dist/src/validation/actor_validator.test.js.map +1 -0
  393. package/dist/src/validation/agent_validator.d.ts +30 -0
  394. package/dist/src/validation/agent_validator.d.ts.map +1 -0
  395. package/dist/src/validation/agent_validator.js +66 -0
  396. package/dist/src/validation/agent_validator.js.map +1 -0
  397. package/dist/src/validation/agent_validator.test.d.ts +2 -0
  398. package/dist/src/validation/agent_validator.test.d.ts.map +1 -0
  399. package/dist/src/validation/agent_validator.test.js +73 -0
  400. package/dist/src/validation/agent_validator.test.js.map +1 -0
  401. package/dist/src/validation/changelog_validator.d.ts +23 -0
  402. package/dist/src/validation/changelog_validator.d.ts.map +1 -0
  403. package/dist/src/validation/changelog_validator.js +58 -0
  404. package/dist/src/validation/changelog_validator.js.map +1 -0
  405. package/dist/src/validation/changelog_validator.test.d.ts +2 -0
  406. package/dist/src/validation/changelog_validator.test.d.ts.map +1 -0
  407. package/dist/src/validation/changelog_validator.test.js +401 -0
  408. package/dist/src/validation/changelog_validator.test.js.map +1 -0
  409. package/dist/src/validation/common.d.ts +10 -0
  410. package/dist/src/validation/common.d.ts.map +1 -0
  411. package/dist/src/validation/common.js +12 -0
  412. package/dist/src/validation/common.js.map +1 -0
  413. package/dist/src/validation/cycle_validator.d.ts +24 -0
  414. package/dist/src/validation/cycle_validator.d.ts.map +1 -0
  415. package/dist/src/validation/cycle_validator.js +51 -0
  416. package/dist/src/validation/cycle_validator.js.map +1 -0
  417. package/dist/src/validation/cycle_validator.test.d.ts +2 -0
  418. package/dist/src/validation/cycle_validator.test.d.ts.map +1 -0
  419. package/dist/src/validation/cycle_validator.test.js +182 -0
  420. package/dist/src/validation/cycle_validator.test.js.map +1 -0
  421. package/dist/src/validation/embedded_metadata_validator.d.ts +26 -0
  422. package/dist/src/validation/embedded_metadata_validator.d.ts.map +1 -0
  423. package/dist/src/validation/embedded_metadata_validator.js +122 -0
  424. package/dist/src/validation/embedded_metadata_validator.js.map +1 -0
  425. package/dist/src/validation/embedded_metadata_validator.test.d.ts +2 -0
  426. package/dist/src/validation/embedded_metadata_validator.test.d.ts.map +1 -0
  427. package/dist/src/validation/embedded_metadata_validator.test.js +316 -0
  428. package/dist/src/validation/embedded_metadata_validator.test.js.map +1 -0
  429. package/dist/src/validation/errors.d.ts +67 -0
  430. package/dist/src/validation/errors.d.ts.map +1 -0
  431. package/dist/src/validation/errors.js +83 -0
  432. package/dist/src/validation/errors.js.map +1 -0
  433. package/dist/src/validation/execution_validator.d.ts +23 -0
  434. package/dist/src/validation/execution_validator.d.ts.map +1 -0
  435. package/dist/src/validation/execution_validator.js +58 -0
  436. package/dist/src/validation/execution_validator.js.map +1 -0
  437. package/dist/src/validation/execution_validator.test.d.ts +2 -0
  438. package/dist/src/validation/execution_validator.test.d.ts.map +1 -0
  439. package/dist/src/validation/execution_validator.test.js +167 -0
  440. package/dist/src/validation/execution_validator.test.js.map +1 -0
  441. package/dist/src/validation/feedback_validator.d.ts +23 -0
  442. package/dist/src/validation/feedback_validator.d.ts.map +1 -0
  443. package/dist/src/validation/feedback_validator.js +58 -0
  444. package/dist/src/validation/feedback_validator.js.map +1 -0
  445. package/dist/src/validation/feedback_validator.test.d.ts +2 -0
  446. package/dist/src/validation/feedback_validator.test.d.ts.map +1 -0
  447. package/dist/src/validation/feedback_validator.test.js +131 -0
  448. package/dist/src/validation/feedback_validator.test.js.map +1 -0
  449. package/dist/src/validation/index.d.ts +11 -0
  450. package/dist/src/validation/index.d.ts.map +1 -0
  451. package/dist/src/validation/index.js +10 -0
  452. package/dist/src/validation/index.js.map +1 -0
  453. package/dist/src/validation/task_validator.d.ts +24 -0
  454. package/dist/src/validation/task_validator.d.ts.map +1 -0
  455. package/dist/src/validation/task_validator.js +50 -0
  456. package/dist/src/validation/task_validator.js.map +1 -0
  457. package/dist/src/validation/task_validator.test.d.ts +2 -0
  458. package/dist/src/validation/task_validator.test.d.ts.map +1 -0
  459. package/dist/src/validation/task_validator.test.js +185 -0
  460. package/dist/src/validation/task_validator.test.js.map +1 -0
  461. package/dist/src/validation/workflow_methodology_validator.d.ts +32 -0
  462. package/dist/src/validation/workflow_methodology_validator.d.ts.map +1 -0
  463. package/dist/src/validation/workflow_methodology_validator.js +91 -0
  464. package/dist/src/validation/workflow_methodology_validator.js.map +1 -0
  465. package/dist/src/validation/workflow_methodology_validator.test.d.ts +2 -0
  466. package/dist/src/validation/workflow_methodology_validator.test.d.ts.map +1 -0
  467. package/dist/src/validation/workflow_methodology_validator.test.js +229 -0
  468. package/dist/src/validation/workflow_methodology_validator.test.js.map +1 -0
  469. package/package.json +63 -0
@@ -0,0 +1,1282 @@
1
+ import { BacklogAdapter } from './index';
2
+ // Mock the factories before importing
3
+ jest.mock('../../factories/task_factory', () => ({
4
+ createTaskRecord: jest.fn()
5
+ }));
6
+ jest.mock('../../factories/cycle_factory', () => ({
7
+ createCycleRecord: jest.fn()
8
+ }));
9
+ // Helper to create properly typed mock records with valid IDs
10
+ function createMockTaskRecord(payload) {
11
+ const baseId = payload.id || '1757687335-task-mock-task';
12
+ return {
13
+ header: {
14
+ version: '1.0',
15
+ type: 'task',
16
+ payloadChecksum: 'mock-checksum',
17
+ signatures: [{ keyId: 'mock-author', role: 'author', signature: 'mock-sig', timestamp: 123, timestamp_iso: '2025-01-01T00:00:00Z' }]
18
+ },
19
+ payload: {
20
+ id: baseId,
21
+ title: 'Mock Task',
22
+ status: 'draft',
23
+ priority: 'medium',
24
+ description: 'Mock description',
25
+ tags: [],
26
+ cycleIds: [],
27
+ ...payload
28
+ }
29
+ };
30
+ }
31
+ function createMockCycleRecord(payload) {
32
+ const baseId = payload.id || '1757687335-cycle-mock-cycle';
33
+ return {
34
+ header: {
35
+ version: '1.0',
36
+ type: 'cycle',
37
+ payloadChecksum: 'mock-checksum',
38
+ signatures: [{ keyId: 'mock-author', role: 'author', signature: 'mock-sig', timestamp: 123, timestamp_iso: '2025-01-01T00:00:00Z' }]
39
+ },
40
+ payload: {
41
+ id: baseId,
42
+ title: 'Mock Cycle',
43
+ status: 'planning',
44
+ taskIds: [],
45
+ childCycleIds: [],
46
+ ...payload
47
+ }
48
+ };
49
+ }
50
+ function createMockFeedbackRecord(payload) {
51
+ const baseId = payload.id || '1757687335-feedback-mock';
52
+ return {
53
+ header: {
54
+ version: '1.0',
55
+ type: 'feedback',
56
+ payloadChecksum: 'mock-checksum',
57
+ signatures: [{ keyId: 'mock-author', role: 'author', signature: 'mock-sig', timestamp: 123, timestamp_iso: '2025-01-01T00:00:00Z' }]
58
+ },
59
+ payload: {
60
+ id: baseId,
61
+ entityType: 'task',
62
+ entityId: '1757687335-task-test',
63
+ type: 'assignment',
64
+ status: 'open',
65
+ content: 'Mock feedback content',
66
+ priority: 'medium',
67
+ tags: [],
68
+ ...payload
69
+ }
70
+ };
71
+ }
72
+ // Complete unit tests for BacklogAdapter
73
+ describe('BacklogAdapter - Complete Unit Tests', () => {
74
+ let backlogAdapter;
75
+ let mockDependencies;
76
+ beforeEach(() => {
77
+ // Complete setup for unit tests
78
+ mockDependencies = {
79
+ taskStore: {
80
+ list: jest.fn().mockResolvedValue([]),
81
+ read: jest.fn(),
82
+ write: jest.fn()
83
+ },
84
+ cycleStore: {
85
+ list: jest.fn().mockResolvedValue([]),
86
+ read: jest.fn(),
87
+ write: jest.fn()
88
+ },
89
+ feedbackStore: {
90
+ read: jest.fn(),
91
+ list: jest.fn().mockResolvedValue([]),
92
+ write: jest.fn()
93
+ },
94
+ executionStore: {
95
+ read: jest.fn(),
96
+ list: jest.fn().mockResolvedValue([]),
97
+ write: jest.fn()
98
+ },
99
+ changelogStore: {
100
+ read: jest.fn(),
101
+ list: jest.fn().mockResolvedValue([]),
102
+ write: jest.fn()
103
+ },
104
+ feedbackAdapter: {
105
+ create: jest.fn()
106
+ },
107
+ executionAdapter: {
108
+ isFirstExecution: jest.fn()
109
+ },
110
+ changelogAdapter: {
111
+ create: jest.fn()
112
+ },
113
+ metricsAdapter: {
114
+ getSystemStatus: jest.fn().mockResolvedValue({
115
+ tasks: { total: 0, byStatus: {}, byPriority: {} },
116
+ cycles: { total: 0, active: 0, completed: 0 },
117
+ health: { overallScore: 100, blockedTasks: 0, staleTasks: 0 }
118
+ }),
119
+ getTaskHealth: jest.fn().mockResolvedValue({
120
+ taskId: 'task-123',
121
+ healthScore: 85,
122
+ timeInCurrentStage: 2,
123
+ stalenessIndex: 1,
124
+ blockingFeedbacks: 0,
125
+ lastActivity: Date.now(),
126
+ recommendations: []
127
+ })
128
+ },
129
+ workflowMethodologyAdapter: {
130
+ getTransitionRule: jest.fn(),
131
+ validateSignature: jest.fn(),
132
+ getAvailableTransitions: jest.fn()
133
+ },
134
+ identity: {
135
+ getActor: jest.fn(),
136
+ signRecord: jest.fn().mockImplementation(async (record) => record)
137
+ },
138
+ eventBus: {
139
+ publish: jest.fn(),
140
+ subscribe: jest.fn(),
141
+ unsubscribe: jest.fn(),
142
+ getSubscriptions: jest.fn(),
143
+ clearSubscriptions: jest.fn()
144
+ }
145
+ };
146
+ backlogAdapter = new BacklogAdapter(mockDependencies);
147
+ });
148
+ it('[EARS-32] should do nothing for non-blocking feedback', async () => {
149
+ const event = {
150
+ type: 'feedback.created',
151
+ timestamp: Date.now(),
152
+ source: 'feedback_adapter',
153
+ payload: {
154
+ feedbackId: 'feedback-123',
155
+ entityType: 'task',
156
+ entityId: 'task-123',
157
+ type: 'suggestion',
158
+ status: 'open',
159
+ content: 'Test feedback content',
160
+ triggeredBy: 'human:test-user'
161
+ }
162
+ };
163
+ // Should not throw error
164
+ await expect(backlogAdapter.handleFeedbackCreated(event)).resolves.not.toThrow();
165
+ });
166
+ it('[EARS-38] should handle daily tick events', async () => {
167
+ const event = {
168
+ type: 'system.daily_tick',
169
+ timestamp: Date.now(),
170
+ source: 'system',
171
+ payload: {
172
+ date: '2025-01-15'
173
+ }
174
+ };
175
+ // Should not throw error
176
+ await expect(backlogAdapter.handleDailyTick(event)).resolves.not.toThrow();
177
+ });
178
+ describe('Cycle Operations', () => {
179
+ it('[EARS-28] should create, sign, and persist a valid cycle', async () => {
180
+ const mockCycle = createMockCycleRecord({
181
+ id: '1757687335-cycle-test-cycle',
182
+ title: 'Test Cycle',
183
+ status: 'planning'
184
+ });
185
+ // Mock the factory functions
186
+ const { createCycleRecord } = require('../../factories/cycle_factory');
187
+ createCycleRecord.mockResolvedValue(mockCycle.payload);
188
+ mockDependencies.identity.signRecord.mockResolvedValue(mockCycle);
189
+ mockDependencies.cycleStore.write.mockResolvedValue(undefined);
190
+ const result = await backlogAdapter.createCycle({
191
+ title: 'Test Cycle',
192
+ status: 'planning'
193
+ }, 'human:author');
194
+ expect(mockDependencies.cycleStore.write).toHaveBeenCalled();
195
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
196
+ type: 'cycle.created',
197
+ payload: expect.objectContaining({
198
+ actorId: 'human:author'
199
+ })
200
+ }));
201
+ expect(result).toEqual(mockCycle.payload);
202
+ });
203
+ it('[EARS-29] should read a cycle by its ID', async () => {
204
+ const mockCycle = createMockCycleRecord({
205
+ id: '1757687335-cycle-test-cycle',
206
+ title: 'Test Cycle'
207
+ });
208
+ mockDependencies.cycleStore.read.mockResolvedValue(mockCycle);
209
+ const result = await backlogAdapter.getCycle('1757687335-cycle-test-cycle');
210
+ expect(mockDependencies.cycleStore.read).toHaveBeenCalledWith('1757687335-cycle-test-cycle');
211
+ expect(result).toEqual(mockCycle.payload);
212
+ });
213
+ it('[EARS-29] should return null for non-existent cycle', async () => {
214
+ mockDependencies.cycleStore.read.mockResolvedValue(null);
215
+ const result = await backlogAdapter.getCycle('non-existent');
216
+ expect(result).toBeNull();
217
+ });
218
+ it('[EARS-30] should list all cycles', async () => {
219
+ const mockCycles = [
220
+ createMockCycleRecord({ id: '1757687335-cycle-test-1', title: 'Cycle 1' }),
221
+ createMockCycleRecord({ id: '1757687336-cycle-test-2', title: 'Cycle 2' })
222
+ ];
223
+ mockDependencies.cycleStore.list.mockResolvedValue([
224
+ '1757687335-cycle-test-1',
225
+ '1757687336-cycle-test-2'
226
+ ]);
227
+ mockDependencies.cycleStore.read
228
+ .mockResolvedValueOnce(mockCycles[0])
229
+ .mockResolvedValueOnce(mockCycles[1]);
230
+ const result = await backlogAdapter.getAllCycles();
231
+ expect(result).toHaveLength(2);
232
+ expect(result[0]).toEqual(mockCycles[0]?.payload);
233
+ expect(result[1]).toEqual(mockCycles[1]?.payload);
234
+ });
235
+ it('[EARS-31] should update a cycle and emit event on status change', async () => {
236
+ const originalCycle = createMockCycleRecord({
237
+ id: '1757687335-cycle-test-cycle',
238
+ status: 'planning'
239
+ });
240
+ const updatedCycle = createMockCycleRecord({
241
+ id: '1757687335-cycle-test-cycle',
242
+ status: 'active'
243
+ });
244
+ mockDependencies.cycleStore.read.mockResolvedValue(originalCycle);
245
+ mockDependencies.cycleStore.write.mockResolvedValue(undefined);
246
+ // Mock the factory function
247
+ const { createCycleRecord } = require('../../factories/cycle_factory');
248
+ createCycleRecord.mockResolvedValue(updatedCycle.payload);
249
+ const result = await backlogAdapter.updateCycle('1757687335-cycle-test-cycle', { status: 'active' });
250
+ expect(mockDependencies.cycleStore.write).toHaveBeenCalled();
251
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
252
+ type: 'cycle.status.changed',
253
+ payload: expect.objectContaining({
254
+ cycleId: '1757687335-cycle-test-cycle',
255
+ oldStatus: 'planning',
256
+ newStatus: 'active'
257
+ })
258
+ }));
259
+ expect(result).toEqual(updatedCycle.payload);
260
+ });
261
+ it('[EARS-32] should create bidirectional link between task and cycle', async () => {
262
+ const taskId = '1757687335-task-test-task';
263
+ const cycleId = '1757687335-cycle-test-cycle';
264
+ const mockTask = createMockTaskRecord({
265
+ id: taskId,
266
+ cycleIds: []
267
+ });
268
+ const mockCycle = createMockCycleRecord({
269
+ id: cycleId,
270
+ taskIds: []
271
+ });
272
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
273
+ mockDependencies.cycleStore.read.mockResolvedValue(mockCycle);
274
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
275
+ mockDependencies.cycleStore.write.mockResolvedValue(undefined);
276
+ // Mock getCurrentActor for the new implementation
277
+ mockDependencies.identity.getCurrentActor = jest.fn().mockResolvedValue({
278
+ id: 'human:test-user',
279
+ displayName: 'Test User'
280
+ });
281
+ await backlogAdapter.addTaskToCycle(cycleId, taskId);
282
+ expect(mockDependencies.cycleStore.write).toHaveBeenCalledWith(expect.objectContaining({
283
+ payload: expect.objectContaining({
284
+ taskIds: expect.arrayContaining([taskId])
285
+ })
286
+ }));
287
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(expect.objectContaining({
288
+ payload: expect.objectContaining({
289
+ cycleIds: expect.arrayContaining([cycleId])
290
+ })
291
+ }));
292
+ });
293
+ it('[EARS-31] should throw error when updating cycle in final state', async () => {
294
+ const archivedCycle = createMockCycleRecord({
295
+ id: '1757687335-cycle-archived',
296
+ status: 'archived'
297
+ });
298
+ mockDependencies.cycleStore.read.mockResolvedValue(archivedCycle);
299
+ await expect(backlogAdapter.updateCycle('1757687335-cycle-archived', { title: 'New Title' }))
300
+ .rejects.toThrow('ProtocolViolationError: Cannot update cycle in final state: archived');
301
+ });
302
+ });
303
+ describe('Enhanced Task Operations', () => {
304
+ it('[EARS-25] should correctly update task fields', async () => {
305
+ const originalTask = createMockTaskRecord({
306
+ id: '1757687335-task-test-task',
307
+ title: 'Original Title',
308
+ status: 'draft'
309
+ });
310
+ const updatedTask = createMockTaskRecord({
311
+ id: '1757687335-task-test-task',
312
+ title: 'Updated Title',
313
+ status: 'draft'
314
+ });
315
+ mockDependencies.taskStore.read.mockResolvedValue(originalTask);
316
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
317
+ // Mock the factory function
318
+ const { createTaskRecord } = require('../../factories/task_factory');
319
+ createTaskRecord.mockResolvedValue(updatedTask.payload);
320
+ const result = await backlogAdapter.updateTask('1757687335-task-test-task', { title: 'Updated Title' });
321
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
322
+ expect(result.title).toBe('Updated Title');
323
+ });
324
+ it('[EARS-26] should throw error when updating task in final state', async () => {
325
+ const archivedTask = createMockTaskRecord({
326
+ id: '1757687335-task-archived',
327
+ status: 'archived'
328
+ });
329
+ mockDependencies.taskStore.read.mockResolvedValue(archivedTask);
330
+ await expect(backlogAdapter.updateTask('1757687335-task-archived', { title: 'New Title' }))
331
+ .rejects.toThrow('ProtocolViolationError: Cannot update task in final state: archived');
332
+ });
333
+ it('[EARS-27] should activate task from ready to active with permission validation', async () => {
334
+ const taskId = '1757687335-task-ready-task';
335
+ const readyTask = createMockTaskRecord({
336
+ id: taskId,
337
+ status: 'ready'
338
+ });
339
+ const activeTask = createMockTaskRecord({
340
+ id: taskId,
341
+ status: 'active'
342
+ });
343
+ mockDependencies.taskStore.read.mockResolvedValue(readyTask);
344
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
345
+ mockDependencies.identity.getActor.mockResolvedValue({
346
+ id: 'human:developer',
347
+ type: 'human',
348
+ displayName: 'Developer',
349
+ publicKey: 'mock-key',
350
+ roles: ['executor'],
351
+ status: 'active'
352
+ });
353
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
354
+ to: 'active',
355
+ conditions: {}
356
+ });
357
+ mockDependencies.identity.signRecord.mockResolvedValue({
358
+ ...readyTask,
359
+ payload: activeTask.payload
360
+ });
361
+ const result = await backlogAdapter.activateTask(taskId, 'human:developer');
362
+ expect(mockDependencies.workflowMethodologyAdapter.getTransitionRule).toHaveBeenCalledWith('ready', 'active', expect.objectContaining({
363
+ task: readyTask.payload,
364
+ transitionTo: 'active'
365
+ }));
366
+ expect(mockDependencies.identity.signRecord).toHaveBeenCalledWith(expect.objectContaining({
367
+ payload: expect.objectContaining({ status: 'active' })
368
+ }), 'human:developer', 'executor');
369
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
370
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
371
+ type: 'task.status.changed',
372
+ payload: expect.objectContaining({
373
+ taskId,
374
+ oldStatus: 'ready',
375
+ newStatus: 'active',
376
+ actorId: 'human:developer'
377
+ })
378
+ }));
379
+ expect(result.status).toBe('active');
380
+ });
381
+ it('[EARS-28] should throw error when task not found for activation', async () => {
382
+ mockDependencies.taskStore.read.mockResolvedValue(null);
383
+ await expect(backlogAdapter.activateTask('nonexistent-task', 'human:developer'))
384
+ .rejects.toThrow('RecordNotFoundError: Task not found: nonexistent-task');
385
+ });
386
+ it('[EARS-29] should throw error when task is not in ready state for activation', async () => {
387
+ const draftTask = createMockTaskRecord({
388
+ id: '1757687335-task-draft',
389
+ status: 'draft'
390
+ });
391
+ mockDependencies.taskStore.read.mockResolvedValue(draftTask);
392
+ mockDependencies.identity.getActor.mockResolvedValue({
393
+ id: 'human:developer',
394
+ type: 'human',
395
+ displayName: 'Developer',
396
+ publicKey: 'mock-key',
397
+ roles: ['executor'],
398
+ status: 'active'
399
+ });
400
+ await expect(backlogAdapter.activateTask('1757687335-task-draft', 'human:developer'))
401
+ .rejects.toThrow('ProtocolViolationError: Task is in \'draft\' state. Cannot activate from this state.');
402
+ });
403
+ it('[EARS-30] should throw error when workflow methodology rejects activation', async () => {
404
+ const readyTask = createMockTaskRecord({
405
+ id: '1757687335-task-ready',
406
+ status: 'ready'
407
+ });
408
+ mockDependencies.taskStore.read.mockResolvedValue(readyTask);
409
+ mockDependencies.identity.getActor.mockResolvedValue({
410
+ id: 'human:developer',
411
+ type: 'human',
412
+ displayName: 'Developer',
413
+ publicKey: 'mock-key',
414
+ roles: ['executor'],
415
+ status: 'active'
416
+ });
417
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue(null);
418
+ await expect(backlogAdapter.activateTask('1757687335-task-ready', 'human:developer'))
419
+ .rejects.toThrow('ProtocolViolationError: Workflow methodology rejected ready→active transition');
420
+ });
421
+ it('[EARS-38A] should resume task from paused to active with blocking validation', async () => {
422
+ const taskId = '1757687335-task-paused';
423
+ const pausedTask = createMockTaskRecord({
424
+ id: taskId,
425
+ status: 'paused'
426
+ });
427
+ const activeTask = createMockTaskRecord({
428
+ id: taskId,
429
+ status: 'active'
430
+ });
431
+ mockDependencies.taskStore.read.mockResolvedValue(pausedTask);
432
+ mockDependencies.identity.getActor.mockResolvedValue({
433
+ id: 'human:ops-lead',
434
+ type: 'human',
435
+ displayName: 'Ops Lead',
436
+ publicKey: 'mock-key',
437
+ roles: ['resumer'],
438
+ status: 'active'
439
+ });
440
+ mockDependencies.metricsAdapter.getTaskHealth.mockResolvedValue({
441
+ taskId,
442
+ healthScore: 80,
443
+ timeInCurrentStage: 3,
444
+ stalenessIndex: 1,
445
+ blockingFeedbacks: 0,
446
+ lastActivity: Date.now(),
447
+ recommendations: []
448
+ });
449
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
450
+ to: 'active',
451
+ conditions: {}
452
+ });
453
+ mockDependencies.identity.signRecord.mockResolvedValue({
454
+ ...pausedTask,
455
+ payload: activeTask.payload
456
+ });
457
+ const result = await backlogAdapter.resumeTask(taskId, 'human:ops-lead');
458
+ expect(mockDependencies.metricsAdapter.getTaskHealth).toHaveBeenCalledWith(taskId);
459
+ expect(mockDependencies.workflowMethodologyAdapter.getTransitionRule).toHaveBeenCalledWith('paused', 'active', expect.objectContaining({
460
+ task: pausedTask.payload,
461
+ transitionTo: 'active'
462
+ }));
463
+ expect(mockDependencies.identity.signRecord).toHaveBeenCalledWith(expect.objectContaining({
464
+ payload: expect.objectContaining({ status: 'active' })
465
+ }), 'human:ops-lead', 'resumer');
466
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
467
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
468
+ type: 'task.status.changed',
469
+ payload: expect.objectContaining({
470
+ taskId,
471
+ oldStatus: 'paused',
472
+ newStatus: 'active',
473
+ actorId: 'human:ops-lead'
474
+ })
475
+ }));
476
+ expect(result.status).toBe('active');
477
+ });
478
+ it('[EARS-39A] should throw error when task not found for resume', async () => {
479
+ mockDependencies.taskStore.read.mockResolvedValue(null);
480
+ await expect(backlogAdapter.resumeTask('nonexistent-task', 'human:ops-lead'))
481
+ .rejects.toThrow('RecordNotFoundError: Task not found: nonexistent-task');
482
+ });
483
+ it('[EARS-40A] should throw error when task is not in paused state for resume', async () => {
484
+ const activeTask = createMockTaskRecord({
485
+ id: '1757687335-task-active',
486
+ status: 'active'
487
+ });
488
+ mockDependencies.taskStore.read.mockResolvedValue(activeTask);
489
+ mockDependencies.identity.getActor.mockResolvedValue({
490
+ id: 'human:ops-lead',
491
+ type: 'human',
492
+ displayName: 'Ops Lead',
493
+ publicKey: 'mock-key',
494
+ roles: ['resumer'],
495
+ status: 'active'
496
+ });
497
+ await expect(backlogAdapter.resumeTask('1757687335-task-active', 'human:ops-lead'))
498
+ .rejects.toThrow(`ProtocolViolationError: Task is in 'active' state. Cannot resume (requires paused).`);
499
+ });
500
+ it('[EARS-41A] should throw error when paused task has blocking feedbacks', async () => {
501
+ const pausedTask = createMockTaskRecord({
502
+ id: '1757687335-task-blocked',
503
+ status: 'paused'
504
+ });
505
+ mockDependencies.taskStore.read.mockResolvedValue(pausedTask);
506
+ mockDependencies.identity.getActor.mockResolvedValue({
507
+ id: 'human:ops-lead',
508
+ type: 'human',
509
+ displayName: 'Ops Lead',
510
+ publicKey: 'mock-key',
511
+ roles: ['resumer'],
512
+ status: 'active'
513
+ });
514
+ mockDependencies.metricsAdapter.getTaskHealth.mockResolvedValue({
515
+ taskId: '1757687335-task-blocked',
516
+ healthScore: 60,
517
+ timeInCurrentStage: 5,
518
+ stalenessIndex: 2,
519
+ blockingFeedbacks: 2,
520
+ lastActivity: Date.now(),
521
+ recommendations: ['Resolve blocking feedbacks']
522
+ });
523
+ await expect(backlogAdapter.resumeTask('1757687335-task-blocked', 'human:ops-lead'))
524
+ .rejects.toThrow('BlockingFeedbackError: Task has blocking feedbacks. Resolve them before resuming or use force.');
525
+ });
526
+ it('[EARS-42A] should force resume ignoring blocking feedbacks with force true', async () => {
527
+ const taskId = '1757687335-task-force-resume';
528
+ const pausedTask = createMockTaskRecord({
529
+ id: taskId,
530
+ status: 'paused'
531
+ });
532
+ const activeTask = createMockTaskRecord({
533
+ id: taskId,
534
+ status: 'active'
535
+ });
536
+ mockDependencies.taskStore.read.mockResolvedValue(pausedTask);
537
+ mockDependencies.identity.getActor.mockResolvedValue({
538
+ id: 'human:ops-lead',
539
+ type: 'human',
540
+ displayName: 'Ops Lead',
541
+ publicKey: 'mock-key',
542
+ roles: ['resumer'],
543
+ status: 'active'
544
+ });
545
+ mockDependencies.metricsAdapter.getTaskHealth.mockClear();
546
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
547
+ to: 'active',
548
+ conditions: {}
549
+ });
550
+ mockDependencies.identity.signRecord.mockResolvedValue({
551
+ ...pausedTask,
552
+ payload: activeTask.payload
553
+ });
554
+ const result = await backlogAdapter.resumeTask(taskId, 'human:ops-lead', true);
555
+ expect(mockDependencies.metricsAdapter.getTaskHealth).not.toHaveBeenCalled();
556
+ expect(mockDependencies.identity.signRecord).toHaveBeenCalledWith(expect.objectContaining({
557
+ payload: expect.objectContaining({ status: 'active' })
558
+ }), 'human:ops-lead', 'resumer');
559
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
560
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
561
+ payload: expect.objectContaining({
562
+ taskId,
563
+ oldStatus: 'paused',
564
+ newStatus: 'active'
565
+ })
566
+ }));
567
+ expect(result.status).toBe('active');
568
+ });
569
+ it('[EARS-31A] should complete task from active to done with approver quality validation', async () => {
570
+ const taskId = '1757687335-task-active-task';
571
+ const activeTask = createMockTaskRecord({
572
+ id: taskId,
573
+ status: 'active'
574
+ });
575
+ const doneTask = createMockTaskRecord({
576
+ id: taskId,
577
+ status: 'done'
578
+ });
579
+ mockDependencies.taskStore.read.mockResolvedValue(activeTask);
580
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
581
+ mockDependencies.identity.getActor.mockResolvedValue({
582
+ id: 'human:qa-lead',
583
+ type: 'human',
584
+ displayName: 'QA Lead',
585
+ publicKey: 'mock-key',
586
+ roles: ['approver:quality'],
587
+ status: 'active'
588
+ });
589
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
590
+ to: 'done',
591
+ conditions: { signatures: { __default__: { role: 'approver', capability_roles: ['approver:quality'], min_approvals: 1 } } }
592
+ });
593
+ mockDependencies.identity.signRecord.mockResolvedValue(doneTask);
594
+ const result = await backlogAdapter.completeTask(taskId, 'human:qa-lead');
595
+ expect(result.status).toBe('done');
596
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(doneTask);
597
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith({
598
+ type: 'task.status.changed',
599
+ timestamp: expect.any(Number),
600
+ source: 'backlog_adapter',
601
+ payload: {
602
+ taskId,
603
+ oldStatus: 'active',
604
+ newStatus: 'done',
605
+ actorId: 'human:qa-lead'
606
+ }
607
+ });
608
+ });
609
+ it('[EARS-32A] should throw error when task not found for completion', async () => {
610
+ mockDependencies.taskStore.read.mockResolvedValue(null);
611
+ await expect(backlogAdapter.completeTask('non-existent-task', 'human:qa-lead'))
612
+ .rejects.toThrow('RecordNotFoundError: Task not found: non-existent-task');
613
+ });
614
+ it('[EARS-33A] should throw error when task is not in active state for completion', async () => {
615
+ const readyTask = createMockTaskRecord({
616
+ id: '1757687335-task-ready',
617
+ status: 'ready'
618
+ });
619
+ mockDependencies.taskStore.read.mockResolvedValue(readyTask);
620
+ mockDependencies.identity.getActor.mockResolvedValue({
621
+ id: 'human:qa-lead',
622
+ type: 'human',
623
+ displayName: 'QA Lead',
624
+ publicKey: 'mock-key',
625
+ roles: ['approver:quality'],
626
+ status: 'active'
627
+ });
628
+ await expect(backlogAdapter.completeTask('1757687335-task-ready', 'human:qa-lead'))
629
+ .rejects.toThrow('ProtocolViolationError: Task is in \'ready\' state. Cannot complete from this state.');
630
+ });
631
+ it('[EARS-34A] should throw error when workflow methodology rejects completion', async () => {
632
+ const activeTask = createMockTaskRecord({
633
+ id: '1757687335-task-active',
634
+ status: 'active'
635
+ });
636
+ mockDependencies.taskStore.read.mockResolvedValue(activeTask);
637
+ mockDependencies.identity.getActor.mockResolvedValue({
638
+ id: 'human:qa-lead',
639
+ type: 'human',
640
+ displayName: 'QA Lead',
641
+ publicKey: 'mock-key',
642
+ roles: ['approver:quality'],
643
+ status: 'active'
644
+ });
645
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue(null);
646
+ await expect(backlogAdapter.completeTask('1757687335-task-active', 'human:qa-lead'))
647
+ .rejects.toThrow('ProtocolViolationError: Workflow methodology rejected active→done transition');
648
+ });
649
+ it('[EARS-28] should cancel task from ready to discarded with proper validation', async () => {
650
+ const taskId = '1757687335-task-ready-to-cancel';
651
+ const readyTask = createMockTaskRecord({
652
+ id: taskId,
653
+ status: 'ready',
654
+ title: 'Task to Cancel'
655
+ });
656
+ const cancelledTask = createMockTaskRecord({
657
+ id: taskId,
658
+ status: 'discarded',
659
+ title: 'Task to Cancel',
660
+ notes: '[CANCELLED] No longer needed (2025-01-15T10:30:00.000Z)'
661
+ });
662
+ mockDependencies.taskStore.read.mockResolvedValue(readyTask);
663
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
664
+ mockDependencies.identity.getActor.mockResolvedValue({
665
+ id: 'human:product-manager',
666
+ type: 'human',
667
+ displayName: 'Product Manager',
668
+ publicKey: 'mock-key',
669
+ roles: ['approver:product'],
670
+ status: 'active'
671
+ });
672
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
673
+ to: 'discarded',
674
+ conditions: { command: 'gitgov task cancel' }
675
+ });
676
+ mockDependencies.identity.signRecord.mockResolvedValue(cancelledTask);
677
+ const result = await backlogAdapter.discardTask(taskId, 'human:product-manager', 'No longer needed');
678
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
679
+ expect(result.status).toBe('discarded');
680
+ expect(result.notes).toContain('[CANCELLED] No longer needed');
681
+ });
682
+ it('[EARS-29] should cancel task from active to discarded', async () => {
683
+ const taskId = '1757687335-task-active-to-cancel';
684
+ const activeTask = createMockTaskRecord({
685
+ id: taskId,
686
+ status: 'active',
687
+ title: 'Active Task to Cancel'
688
+ });
689
+ mockDependencies.taskStore.read.mockResolvedValue(activeTask);
690
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
691
+ mockDependencies.identity.getActor.mockResolvedValue({
692
+ id: 'human:team-lead',
693
+ type: 'human',
694
+ displayName: 'Team Lead',
695
+ publicKey: 'mock-key',
696
+ roles: ['approver:quality'],
697
+ status: 'active'
698
+ });
699
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
700
+ to: 'discarded',
701
+ conditions: { command: 'gitgov task cancel' }
702
+ });
703
+ mockDependencies.identity.signRecord.mockResolvedValue({
704
+ ...activeTask,
705
+ payload: { ...activeTask.payload, status: 'discarded' }
706
+ });
707
+ const result = await backlogAdapter.discardTask(taskId, 'human:team-lead');
708
+ expect(result.status).toBe('discarded');
709
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
710
+ });
711
+ it('[EARS-30] should throw error when cancelling task from invalid state', async () => {
712
+ const draftTask = createMockTaskRecord({
713
+ id: '1757687335-task-draft',
714
+ status: 'draft'
715
+ });
716
+ mockDependencies.taskStore.read.mockResolvedValue(draftTask);
717
+ mockDependencies.identity.getActor.mockResolvedValue({
718
+ id: 'human:anyone',
719
+ type: 'human',
720
+ displayName: 'Anyone',
721
+ publicKey: 'mock-key',
722
+ roles: ['author'],
723
+ status: 'active'
724
+ });
725
+ await expect(backlogAdapter.discardTask('1757687335-task-draft', 'human:anyone'))
726
+ .rejects.toThrow('ProtocolViolationError: Task is in \'draft\' state. Cannot cancel from this state. Only \'ready\', \'active\', and \'review\' tasks can be cancelled.');
727
+ });
728
+ it('[EARS-31B] should reject task from review to discarded with reason', async () => {
729
+ const taskId = '1757687335-task-review-to-reject';
730
+ const reviewTask = createMockTaskRecord({
731
+ id: taskId,
732
+ status: 'review',
733
+ title: 'Review Task to Reject',
734
+ notes: 'Original task notes'
735
+ });
736
+ mockDependencies.taskStore.read.mockResolvedValue(reviewTask);
737
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
738
+ mockDependencies.identity.getActor.mockResolvedValue({
739
+ id: 'human:reviewer',
740
+ type: 'human',
741
+ displayName: 'Reviewer',
742
+ publicKey: 'mock-key',
743
+ roles: ['approver:product'],
744
+ status: 'active'
745
+ });
746
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
747
+ to: 'discarded',
748
+ conditions: { command: 'gitgov task reject' }
749
+ });
750
+ mockDependencies.identity.signRecord.mockResolvedValue({
751
+ ...reviewTask,
752
+ payload: { ...reviewTask.payload, status: 'discarded' }
753
+ });
754
+ const result = await backlogAdapter.discardTask(taskId, 'human:reviewer', 'Requirements unclear');
755
+ expect(result.status).toBe('discarded');
756
+ expect(result.notes).toContain('[REJECTED] Requirements unclear');
757
+ expect(result.notes).toContain('Original task notes');
758
+ expect(mockDependencies.taskStore.write).toHaveBeenCalled();
759
+ });
760
+ it('[EARS-32B] should add reason with REJECTED prefix in notes for reject', async () => {
761
+ const taskId = '1757687335-task-review-rejected';
762
+ const reviewTask = createMockTaskRecord({
763
+ id: taskId,
764
+ status: 'review',
765
+ title: 'Review Task for Rejection'
766
+ // notes omitted - will be undefined by default
767
+ });
768
+ mockDependencies.taskStore.read.mockResolvedValue(reviewTask);
769
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
770
+ mockDependencies.identity.getActor.mockResolvedValue({
771
+ id: 'human:reviewer',
772
+ type: 'human',
773
+ displayName: 'Reviewer',
774
+ publicKey: 'mock-key',
775
+ roles: ['approver:quality'],
776
+ status: 'active'
777
+ });
778
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
779
+ to: 'discarded',
780
+ conditions: { command: 'gitgov task reject' }
781
+ });
782
+ mockDependencies.identity.signRecord.mockResolvedValue({
783
+ ...reviewTask,
784
+ payload: { ...reviewTask.payload, status: 'discarded' }
785
+ });
786
+ const result = await backlogAdapter.discardTask(taskId, 'human:reviewer', 'Not aligned with architecture');
787
+ expect(result.status).toBe('discarded');
788
+ expect(result.notes).toContain('[REJECTED] Not aligned with architecture');
789
+ expect(result.notes).toContain('[REJECTED]');
790
+ expect(result.notes).not.toContain('[CANCELLED]');
791
+ expect(result.notes).toMatch(/\[REJECTED\] Not aligned with architecture \(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\)/);
792
+ });
793
+ it('[EARS-33B] should validate ready active review states for cancel reject', async () => {
794
+ // Test ready state (should use [CANCELLED])
795
+ const readyTask = createMockTaskRecord({
796
+ id: '1757687335-task-ready',
797
+ status: 'ready'
798
+ });
799
+ mockDependencies.taskStore.read.mockResolvedValue(readyTask);
800
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
801
+ mockDependencies.identity.getActor.mockResolvedValue({
802
+ id: 'human:pm',
803
+ type: 'human',
804
+ displayName: 'PM',
805
+ publicKey: 'mock-key',
806
+ roles: ['approver:product'],
807
+ status: 'active'
808
+ });
809
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
810
+ to: 'discarded',
811
+ conditions: { command: 'gitgov task cancel' }
812
+ });
813
+ mockDependencies.identity.signRecord.mockResolvedValue({
814
+ ...readyTask,
815
+ payload: { ...readyTask.payload, status: 'discarded' }
816
+ });
817
+ const readyResult = await backlogAdapter.discardTask('1757687335-task-ready', 'human:pm', 'Priorities changed');
818
+ expect(readyResult.notes).toContain('[CANCELLED] Priorities changed');
819
+ // Test review state (should use [REJECTED])
820
+ const reviewTask = createMockTaskRecord({
821
+ id: '1757687335-task-review',
822
+ status: 'review'
823
+ });
824
+ mockDependencies.taskStore.read.mockResolvedValue(reviewTask);
825
+ const reviewResult = await backlogAdapter.discardTask('1757687335-task-review', 'human:pm', 'Requirements unclear');
826
+ expect(reviewResult.notes).toContain('[REJECTED] Requirements unclear');
827
+ });
828
+ });
829
+ describe('Event Handlers', () => {
830
+ it('[EARS-31] should pause active task when blocking feedback created', async () => {
831
+ const taskId = '1757687335-task-active-task';
832
+ const mockTask = createMockTaskRecord({
833
+ id: taskId,
834
+ status: 'active'
835
+ });
836
+ const mockFeedback = {
837
+ id: '1757687335-feedback-blocking',
838
+ payload: {
839
+ entityId: taskId,
840
+ type: 'blocking'
841
+ }
842
+ };
843
+ mockDependencies.feedbackStore.read.mockResolvedValue(mockFeedback);
844
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
845
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
846
+ const event = {
847
+ type: 'feedback.created',
848
+ timestamp: Date.now(),
849
+ source: 'feedback_adapter',
850
+ payload: {
851
+ feedbackId: '1757687335-feedback-blocking',
852
+ entityType: 'task',
853
+ entityId: taskId,
854
+ type: 'blocking',
855
+ status: 'open',
856
+ content: 'Blocking feedback content',
857
+ triggeredBy: 'human:reviewer'
858
+ }
859
+ };
860
+ await backlogAdapter.handleFeedbackCreated(event);
861
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(expect.objectContaining({
862
+ payload: expect.objectContaining({
863
+ status: 'paused'
864
+ })
865
+ }));
866
+ expect(mockDependencies.eventBus.publish).toHaveBeenCalledWith(expect.objectContaining({
867
+ type: 'task.status.changed',
868
+ payload: expect.objectContaining({
869
+ oldStatus: 'active',
870
+ newStatus: 'paused'
871
+ })
872
+ }));
873
+ });
874
+ it('[EARS-33] should resume task when last blocking feedback resolved', async () => {
875
+ const taskId = '1757687335-task-paused-task';
876
+ const mockTask = createMockTaskRecord({
877
+ id: taskId,
878
+ status: 'paused'
879
+ });
880
+ const mockFeedback = {
881
+ id: '1757687335-feedback-resolved',
882
+ payload: {
883
+ entityId: taskId,
884
+ type: 'blocking'
885
+ }
886
+ };
887
+ mockDependencies.feedbackStore.read.mockResolvedValue(mockFeedback);
888
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
889
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
890
+ mockDependencies.metricsAdapter.getTaskHealth.mockResolvedValue({
891
+ taskId: 'task-123',
892
+ healthScore: 100,
893
+ timeInCurrentStage: 0,
894
+ stalenessIndex: 0,
895
+ blockingFeedbacks: 0,
896
+ lastActivity: Date.now(),
897
+ recommendations: []
898
+ });
899
+ const event = {
900
+ type: 'feedback.status.changed',
901
+ timestamp: Date.now(),
902
+ source: 'feedback_adapter',
903
+ payload: {
904
+ feedbackId: '1757687335-feedback-resolved',
905
+ oldStatus: 'open',
906
+ newStatus: 'resolved',
907
+ triggeredBy: 'human:resolver'
908
+ }
909
+ };
910
+ await backlogAdapter.handleFeedbackResolved(event);
911
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(expect.objectContaining({
912
+ payload: expect.objectContaining({
913
+ status: 'active'
914
+ })
915
+ }));
916
+ });
917
+ it('[EARS-34] should not resume task if other blocking feedbacks remain', async () => {
918
+ const taskId = '1757687335-task-still-blocked';
919
+ const mockTask = createMockTaskRecord({
920
+ id: taskId,
921
+ status: 'paused'
922
+ });
923
+ const mockFeedback = {
924
+ id: '1757687335-feedback-resolved',
925
+ payload: {
926
+ entityId: taskId,
927
+ type: 'blocking'
928
+ }
929
+ };
930
+ mockDependencies.feedbackStore.read.mockResolvedValue(mockFeedback);
931
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
932
+ mockDependencies.metricsAdapter.getTaskHealth.mockResolvedValue({
933
+ taskId: 'task-123',
934
+ healthScore: 80,
935
+ timeInCurrentStage: 0,
936
+ stalenessIndex: 0,
937
+ blockingFeedbacks: 1,
938
+ lastActivity: Date.now(),
939
+ recommendations: []
940
+ });
941
+ const event = {
942
+ type: 'feedback.status.changed',
943
+ timestamp: Date.now(),
944
+ source: 'feedback_adapter',
945
+ payload: {
946
+ feedbackId: '1757687335-feedback-resolved',
947
+ oldStatus: 'open',
948
+ newStatus: 'resolved',
949
+ triggeredBy: 'human:resolver'
950
+ }
951
+ };
952
+ await backlogAdapter.handleFeedbackResolved(event);
953
+ // Should not write to taskStore (no status change)
954
+ expect(mockDependencies.taskStore.write).not.toHaveBeenCalled();
955
+ });
956
+ it('[EARS-35] should transition task to active on first execution', async () => {
957
+ const taskId = '1757687335-task-ready-task';
958
+ const mockTask = createMockTaskRecord({
959
+ id: taskId,
960
+ status: 'ready'
961
+ });
962
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
963
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
964
+ mockDependencies.identity.getActor.mockResolvedValue({
965
+ id: 'human:executor',
966
+ type: 'human',
967
+ displayName: 'Executor',
968
+ publicKey: 'mock-key',
969
+ roles: ['executor'],
970
+ status: 'active'
971
+ });
972
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
973
+ to: 'active',
974
+ conditions: {}
975
+ });
976
+ const event = {
977
+ type: 'execution.created',
978
+ timestamp: Date.now(),
979
+ source: 'execution_adapter',
980
+ payload: {
981
+ executionId: '1757687335-exec-first',
982
+ taskId,
983
+ triggeredBy: 'human:executor',
984
+ isFirstExecution: true
985
+ }
986
+ };
987
+ await backlogAdapter.handleExecutionCreated(event);
988
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(expect.objectContaining({
989
+ payload: expect.objectContaining({
990
+ status: 'active'
991
+ })
992
+ }));
993
+ });
994
+ it('[EARS-36] should do nothing on subsequent executions', async () => {
995
+ const event = {
996
+ type: 'execution.created',
997
+ timestamp: Date.now(),
998
+ source: 'execution_adapter',
999
+ payload: {
1000
+ executionId: '1757687335-exec-second',
1001
+ taskId: '1757687335-task-active-task',
1002
+ triggeredBy: 'human:executor',
1003
+ isFirstExecution: false
1004
+ }
1005
+ };
1006
+ await backlogAdapter.handleExecutionCreated(event);
1007
+ // Should not write to taskStore
1008
+ expect(mockDependencies.taskStore.write).not.toHaveBeenCalled();
1009
+ });
1010
+ it('[EARS-37] should archive task when changelog created', async () => {
1011
+ const taskId = '1757687335-task-done-task';
1012
+ const mockTask = createMockTaskRecord({
1013
+ id: taskId,
1014
+ status: 'done'
1015
+ });
1016
+ const mockChangelog = {
1017
+ id: '1757687335-changelog-task-done',
1018
+ payload: {
1019
+ entityType: 'task',
1020
+ entityId: taskId
1021
+ }
1022
+ };
1023
+ mockDependencies.changelogStore.read.mockResolvedValue(mockChangelog);
1024
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
1025
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
1026
+ const event = {
1027
+ type: 'changelog.created',
1028
+ timestamp: Date.now(),
1029
+ source: 'changelog_adapter',
1030
+ payload: {
1031
+ changelogId: '1757687335-changelog-task-done',
1032
+ entityId: taskId,
1033
+ entityType: 'task',
1034
+ changeType: 'completion',
1035
+ riskLevel: 'low',
1036
+ triggeredBy: 'system',
1037
+ title: 'Task completed',
1038
+ trigger: 'manual'
1039
+ }
1040
+ };
1041
+ await backlogAdapter.handleChangelogCreated(event);
1042
+ expect(mockDependencies.taskStore.write).toHaveBeenCalledWith(expect.objectContaining({
1043
+ payload: expect.objectContaining({
1044
+ status: 'archived'
1045
+ })
1046
+ }));
1047
+ });
1048
+ it('[EARS-45] should complete parent cycle when all child cycles are completed', async () => {
1049
+ const parentCycleId = '1757687335-cycle-parent';
1050
+ const childCycleId = '1757687335-cycle-child';
1051
+ // Mock parent cycle with child
1052
+ const parentCycle = createMockCycleRecord({
1053
+ id: parentCycleId,
1054
+ status: 'active',
1055
+ childCycleIds: [childCycleId]
1056
+ });
1057
+ // Mock completed child cycle
1058
+ const childCycle = createMockCycleRecord({
1059
+ id: childCycleId,
1060
+ status: 'completed'
1061
+ });
1062
+ // Setup mocks
1063
+ mockDependencies.cycleStore.read
1064
+ .mockImplementation(async (id) => {
1065
+ if (id === childCycleId)
1066
+ return childCycle;
1067
+ if (id === parentCycleId)
1068
+ return parentCycle;
1069
+ return null;
1070
+ });
1071
+ mockDependencies.cycleStore.list.mockResolvedValue([parentCycleId, childCycleId]);
1072
+ mockDependencies.cycleStore.write.mockResolvedValue(undefined);
1073
+ const event = {
1074
+ type: 'cycle.status.changed',
1075
+ timestamp: Date.now(),
1076
+ source: 'backlog_adapter',
1077
+ payload: {
1078
+ cycleId: childCycleId,
1079
+ oldStatus: 'active',
1080
+ newStatus: 'completed',
1081
+ triggeredBy: 'system'
1082
+ }
1083
+ };
1084
+ // For now, just verify it doesn't throw an error
1085
+ // The full implementation requires complex factory mocking
1086
+ await expect(backlogAdapter.handleCycleStatusChanged(event)).resolves.not.toThrow();
1087
+ // Note: Epic task completion is delegated to planning methodology (not implemented yet)
1088
+ });
1089
+ });
1090
+ describe('Performance Benchmarks', () => {
1091
+ it('[EARS-44] should execute task operations in under 100ms', async () => {
1092
+ const mockTask = createMockTaskRecord({
1093
+ id: '1757687335-task-performance',
1094
+ title: 'Performance Test Task',
1095
+ status: 'draft'
1096
+ });
1097
+ mockDependencies.taskStore.read.mockResolvedValue(mockTask);
1098
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
1099
+ mockDependencies.identity.getActor.mockResolvedValue({
1100
+ id: 'human:performer',
1101
+ type: 'human',
1102
+ displayName: 'Performer',
1103
+ publicKey: 'mock-key',
1104
+ roles: ['author'],
1105
+ status: 'active'
1106
+ });
1107
+ mockDependencies.workflowMethodologyAdapter.getTransitionRule.mockResolvedValue({
1108
+ to: 'review',
1109
+ conditions: {}
1110
+ });
1111
+ const startTime = Date.now();
1112
+ await backlogAdapter.submitTask('1757687335-task-performance', 'human:performer');
1113
+ const endTime = Date.now();
1114
+ expect(endTime - startTime).toBeLessThan(100); // Performance target <100ms
1115
+ });
1116
+ it('[EARS-44] should execute cycle operations in under 100ms', async () => {
1117
+ const mockCycle = createMockCycleRecord({
1118
+ id: '1757687335-cycle-performance',
1119
+ title: 'Performance Test Cycle',
1120
+ status: 'planning'
1121
+ });
1122
+ const { createCycleRecord } = require('../../factories/cycle_factory');
1123
+ createCycleRecord.mockResolvedValue(mockCycle.payload);
1124
+ mockDependencies.identity.signRecord.mockResolvedValue(mockCycle);
1125
+ mockDependencies.cycleStore.write.mockResolvedValue(undefined);
1126
+ const startTime = Date.now();
1127
+ await backlogAdapter.createCycle({
1128
+ title: 'Performance Test Cycle',
1129
+ status: 'planning'
1130
+ }, 'human:performer');
1131
+ const endTime = Date.now();
1132
+ expect(endTime - startTime).toBeLessThan(100); // Performance target <100ms
1133
+ });
1134
+ it('[EARS-44] should execute getSystemStatus in under 100ms', async () => {
1135
+ const startTime = Date.now();
1136
+ await backlogAdapter.getSystemStatus();
1137
+ const endTime = Date.now();
1138
+ expect(endTime - startTime).toBeLessThan(100); // Performance target <100ms
1139
+ expect(mockDependencies.metricsAdapter.getSystemStatus).toHaveBeenCalled();
1140
+ });
1141
+ it('[EARS-44] should execute getTaskHealth in under 100ms', async () => {
1142
+ const startTime = Date.now();
1143
+ await backlogAdapter.getTaskHealth('1757687335-task-health-test');
1144
+ const endTime = Date.now();
1145
+ expect(endTime - startTime).toBeLessThan(100); // Performance target <100ms
1146
+ expect(mockDependencies.metricsAdapter.getTaskHealth).toHaveBeenCalledWith('1757687335-task-health-test');
1147
+ });
1148
+ });
1149
+ describe('Agent Navigation', () => {
1150
+ it('[EARS-48] should return tasks assigned to a specific actor', async () => {
1151
+ const taskId1 = '1757687335-task-assigned-1';
1152
+ const taskId2 = '1757687335-task-assigned-2';
1153
+ const taskId3 = '1757687335-task-unassigned';
1154
+ const actorId = 'human:developer';
1155
+ // Mock feedback records for assignments
1156
+ const assignmentFeedback1 = {
1157
+ id: '1757687335-feedback-assignment-1',
1158
+ payload: {
1159
+ entityId: taskId1,
1160
+ type: 'assignment',
1161
+ assignee: actorId,
1162
+ status: 'resolved'
1163
+ }
1164
+ };
1165
+ const assignmentFeedback2 = {
1166
+ id: '1757687335-feedback-assignment-2',
1167
+ payload: {
1168
+ entityId: taskId2,
1169
+ type: 'assignment',
1170
+ assignee: actorId,
1171
+ status: 'resolved'
1172
+ }
1173
+ };
1174
+ const nonAssignmentFeedback = {
1175
+ id: '1757687335-feedback-suggestion',
1176
+ payload: {
1177
+ entityId: taskId3,
1178
+ type: 'suggestion',
1179
+ assignee: 'human:other',
1180
+ status: 'open'
1181
+ }
1182
+ };
1183
+ // Mock tasks
1184
+ const task1 = createMockTaskRecord({ id: taskId1, title: 'Assigned Task 1' });
1185
+ const task2 = createMockTaskRecord({ id: taskId2, title: 'Assigned Task 2' });
1186
+ // Setup mocks
1187
+ mockDependencies.feedbackStore.list.mockResolvedValue([
1188
+ createMockFeedbackRecord({ id: '1757687335-feedback-assignment-1' }),
1189
+ createMockFeedbackRecord({ id: '1757687335-feedback-assignment-2' }),
1190
+ createMockFeedbackRecord({ id: '1757687335-feedback-suggestion' })
1191
+ ]);
1192
+ mockDependencies.feedbackStore.read
1193
+ .mockResolvedValueOnce(assignmentFeedback1)
1194
+ .mockResolvedValueOnce(assignmentFeedback2)
1195
+ .mockResolvedValueOnce(nonAssignmentFeedback);
1196
+ mockDependencies.taskStore.read
1197
+ .mockResolvedValueOnce(task1)
1198
+ .mockResolvedValueOnce(task2);
1199
+ const result = await backlogAdapter.getTasksAssignedToActor(actorId);
1200
+ expect(result).toHaveLength(2);
1201
+ expect(result[0]?.id).toBe(taskId1);
1202
+ expect(result[1]?.id).toBe(taskId2);
1203
+ });
1204
+ it('[EARS-49] should return empty array for actor with no assigned tasks', async () => {
1205
+ const actorId = 'human:unassigned';
1206
+ // Mock no assignment feedbacks for this actor
1207
+ const nonAssignmentFeedback = {
1208
+ id: '1757687335-feedback-other',
1209
+ payload: {
1210
+ entityId: 'task-123',
1211
+ type: 'suggestion',
1212
+ assignee: 'human:other-actor',
1213
+ status: 'open'
1214
+ }
1215
+ };
1216
+ mockDependencies.feedbackStore.list.mockResolvedValue([
1217
+ createMockFeedbackRecord({ id: '1757687335-feedback-other' })
1218
+ ]);
1219
+ mockDependencies.feedbackStore.read.mockResolvedValue(nonAssignmentFeedback);
1220
+ const result = await backlogAdapter.getTasksAssignedToActor(actorId);
1221
+ expect(result).toEqual([]);
1222
+ });
1223
+ });
1224
+ describe('Future Methods (Not Implemented)', () => {
1225
+ it('[EARS-38] should throw NotImplementedError for lint method', async () => {
1226
+ await expect(backlogAdapter.lint())
1227
+ .rejects.toThrow('NotImplementedError: lint() will be implemented when lint_command.md is ready');
1228
+ });
1229
+ it('[EARS-39] should throw NotImplementedError for audit method', async () => {
1230
+ await expect(backlogAdapter.audit())
1231
+ .rejects.toThrow('NotImplementedError: audit() will be implemented when audit_command.md is ready');
1232
+ });
1233
+ it('[EARS-42] should throw NotImplementedError for processChanges method', async () => {
1234
+ await expect(backlogAdapter.processChanges([]))
1235
+ .rejects.toThrow('NotImplementedError: processChanges() will be implemented when commit_processor.md is ready');
1236
+ });
1237
+ });
1238
+ describe('Error Handling & Edge Cases', () => {
1239
+ it('[EARS-45] should handle adapter failures gracefully in event handlers', async () => {
1240
+ // Simulate adapter failure
1241
+ mockDependencies.feedbackStore.read.mockRejectedValue(new Error('Store failure'));
1242
+ const event = {
1243
+ type: 'feedback.created',
1244
+ timestamp: Date.now(),
1245
+ source: 'feedback_adapter',
1246
+ payload: {
1247
+ feedbackId: '1757687335-feedback-error',
1248
+ entityType: 'task',
1249
+ entityId: '1757687335-task-error',
1250
+ type: 'blocking',
1251
+ status: 'open',
1252
+ content: 'Error feedback content',
1253
+ triggeredBy: 'human:reviewer'
1254
+ }
1255
+ };
1256
+ // Should not throw error - graceful degradation
1257
+ await expect(backlogAdapter.handleFeedbackCreated(event)).resolves.not.toThrow();
1258
+ });
1259
+ it('[EARS-27] should re-validate full payload using factory', async () => {
1260
+ const originalTask = createMockTaskRecord({
1261
+ id: '1757687335-task-validation',
1262
+ title: 'Original Title',
1263
+ status: 'draft'
1264
+ });
1265
+ mockDependencies.taskStore.read.mockResolvedValue(originalTask);
1266
+ mockDependencies.taskStore.write.mockResolvedValue(undefined);
1267
+ // Mock factory to validate the merged payload
1268
+ const { createTaskRecord } = require('../../factories/task_factory');
1269
+ createTaskRecord.mockResolvedValue({
1270
+ ...originalTask.payload,
1271
+ title: 'Updated Title'
1272
+ });
1273
+ await backlogAdapter.updateTask('1757687335-task-validation', { title: 'Updated Title' });
1274
+ // Verify factory was called with merged payload
1275
+ expect(createTaskRecord).toHaveBeenCalledWith({
1276
+ ...originalTask.payload,
1277
+ title: 'Updated Title'
1278
+ });
1279
+ });
1280
+ });
1281
+ });
1282
+ //# sourceMappingURL=backlog_adapter.test.js.map