@mclawnet/swarm 0.1.13 → 0.1.15

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 (320) hide show
  1. package/dist/__tests__/always-on-activity-reader.test.d.ts +2 -0
  2. package/dist/__tests__/always-on-activity-reader.test.d.ts.map +1 -0
  3. package/dist/__tests__/always-on-activity-reader.test.js +193 -0
  4. package/dist/__tests__/always-on-activity-reader.test.js.map +1 -0
  5. package/dist/__tests__/always-on-config.test.d.ts +2 -0
  6. package/dist/__tests__/always-on-config.test.d.ts.map +1 -0
  7. package/dist/__tests__/always-on-config.test.js +285 -0
  8. package/dist/__tests__/always-on-config.test.js.map +1 -0
  9. package/dist/__tests__/always-on-manager.test.d.ts +2 -0
  10. package/dist/__tests__/always-on-manager.test.d.ts.map +1 -0
  11. package/dist/__tests__/always-on-manager.test.js +797 -0
  12. package/dist/__tests__/always-on-manager.test.js.map +1 -0
  13. package/dist/__tests__/always-on-parity.test.d.ts +2 -0
  14. package/dist/__tests__/always-on-parity.test.d.ts.map +1 -0
  15. package/dist/__tests__/always-on-parity.test.js +20 -0
  16. package/dist/__tests__/always-on-parity.test.js.map +1 -0
  17. package/dist/__tests__/cascade-picker.test.d.ts +2 -0
  18. package/dist/__tests__/cascade-picker.test.d.ts.map +1 -0
  19. package/dist/__tests__/cascade-picker.test.js +122 -0
  20. package/dist/__tests__/cascade-picker.test.js.map +1 -0
  21. package/dist/__tests__/coordinator-shipment.test.d.ts +2 -0
  22. package/dist/__tests__/coordinator-shipment.test.d.ts.map +1 -0
  23. package/dist/__tests__/coordinator-shipment.test.js +280 -0
  24. package/dist/__tests__/coordinator-shipment.test.js.map +1 -0
  25. package/dist/__tests__/coordinator-workspace-recover.test.d.ts +2 -0
  26. package/dist/__tests__/coordinator-workspace-recover.test.d.ts.map +1 -0
  27. package/dist/__tests__/coordinator-workspace-recover.test.js +140 -0
  28. package/dist/__tests__/coordinator-workspace-recover.test.js.map +1 -0
  29. package/dist/__tests__/coordinator-workspace.test.d.ts +2 -0
  30. package/dist/__tests__/coordinator-workspace.test.d.ts.map +1 -0
  31. package/dist/__tests__/coordinator-workspace.test.js +135 -0
  32. package/dist/__tests__/coordinator-workspace.test.js.map +1 -0
  33. package/dist/__tests__/default-runner-epipe.test.d.ts +2 -0
  34. package/dist/__tests__/default-runner-epipe.test.d.ts.map +1 -0
  35. package/dist/__tests__/default-runner-epipe.test.js +43 -0
  36. package/dist/__tests__/default-runner-epipe.test.js.map +1 -0
  37. package/dist/__tests__/discovery-scheduler.test.d.ts +2 -0
  38. package/dist/__tests__/discovery-scheduler.test.d.ts.map +1 -0
  39. package/dist/__tests__/discovery-scheduler.test.js +367 -0
  40. package/dist/__tests__/discovery-scheduler.test.js.map +1 -0
  41. package/dist/__tests__/env-forward-e2e.test.d.ts +2 -0
  42. package/dist/__tests__/env-forward-e2e.test.d.ts.map +1 -0
  43. package/dist/__tests__/env-forward-e2e.test.js +57 -0
  44. package/dist/__tests__/env-forward-e2e.test.js.map +1 -0
  45. package/dist/__tests__/gh-pr-creator.test.d.ts +2 -0
  46. package/dist/__tests__/gh-pr-creator.test.d.ts.map +1 -0
  47. package/dist/__tests__/gh-pr-creator.test.js +107 -0
  48. package/dist/__tests__/gh-pr-creator.test.js.map +1 -0
  49. package/dist/__tests__/git-worktree-provider.test.d.ts +2 -0
  50. package/dist/__tests__/git-worktree-provider.test.d.ts.map +1 -0
  51. package/dist/__tests__/git-worktree-provider.test.js +98 -0
  52. package/dist/__tests__/git-worktree-provider.test.js.map +1 -0
  53. package/dist/__tests__/gitignore-check.test.d.ts +2 -0
  54. package/dist/__tests__/gitignore-check.test.d.ts.map +1 -0
  55. package/dist/__tests__/gitignore-check.test.js +39 -0
  56. package/dist/__tests__/gitignore-check.test.js.map +1 -0
  57. package/dist/__tests__/idea-research-source.test.d.ts +2 -0
  58. package/dist/__tests__/idea-research-source.test.d.ts.map +1 -0
  59. package/dist/__tests__/idea-research-source.test.js +425 -0
  60. package/dist/__tests__/idea-research-source.test.js.map +1 -0
  61. package/dist/__tests__/idea-todo-source.test.d.ts +2 -0
  62. package/dist/__tests__/idea-todo-source.test.d.ts.map +1 -0
  63. package/dist/__tests__/idea-todo-source.test.js +258 -0
  64. package/dist/__tests__/idea-todo-source.test.js.map +1 -0
  65. package/dist/__tests__/introspection-dedupe.test.d.ts +2 -0
  66. package/dist/__tests__/introspection-dedupe.test.d.ts.map +1 -0
  67. package/dist/__tests__/introspection-dedupe.test.js +484 -0
  68. package/dist/__tests__/introspection-dedupe.test.js.map +1 -0
  69. package/dist/__tests__/introspection-source.test.d.ts +2 -0
  70. package/dist/__tests__/introspection-source.test.d.ts.map +1 -0
  71. package/dist/__tests__/introspection-source.test.js +1051 -0
  72. package/dist/__tests__/introspection-source.test.js.map +1 -0
  73. package/dist/__tests__/migration-roles.test.js +1 -22
  74. package/dist/__tests__/migration-roles.test.js.map +1 -1
  75. package/dist/__tests__/reconcile-researching.test.d.ts +2 -0
  76. package/dist/__tests__/reconcile-researching.test.d.ts.map +1 -0
  77. package/dist/__tests__/reconcile-researching.test.js +224 -0
  78. package/dist/__tests__/reconcile-researching.test.js.map +1 -0
  79. package/dist/__tests__/role-loader-preamble-all.test.js +3 -1
  80. package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -1
  81. package/dist/__tests__/role-loader.test.js +95 -0
  82. package/dist/__tests__/role-loader.test.js.map +1 -1
  83. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +3 -1
  84. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -1
  85. package/dist/__tests__/secret-scrub.test.d.ts +2 -0
  86. package/dist/__tests__/secret-scrub.test.d.ts.map +1 -0
  87. package/dist/__tests__/secret-scrub.test.js +55 -0
  88. package/dist/__tests__/secret-scrub.test.js.map +1 -0
  89. package/dist/__tests__/shipment-actions.test.d.ts +2 -0
  90. package/dist/__tests__/shipment-actions.test.d.ts.map +1 -0
  91. package/dist/__tests__/shipment-actions.test.js +378 -0
  92. package/dist/__tests__/shipment-actions.test.js.map +1 -0
  93. package/dist/__tests__/shipment-persistence.test.d.ts +2 -0
  94. package/dist/__tests__/shipment-persistence.test.d.ts.map +1 -0
  95. package/dist/__tests__/shipment-persistence.test.js +120 -0
  96. package/dist/__tests__/shipment-persistence.test.js.map +1 -0
  97. package/dist/__tests__/shipment-pipeline.test.d.ts +2 -0
  98. package/dist/__tests__/shipment-pipeline.test.d.ts.map +1 -0
  99. package/dist/__tests__/shipment-pipeline.test.js +392 -0
  100. package/dist/__tests__/shipment-pipeline.test.js.map +1 -0
  101. package/dist/__tests__/shipment-report.test.d.ts +2 -0
  102. package/dist/__tests__/shipment-report.test.d.ts.map +1 -0
  103. package/dist/__tests__/shipment-report.test.js +78 -0
  104. package/dist/__tests__/shipment-report.test.js.map +1 -0
  105. package/dist/__tests__/shipment-stdin-integration.test.d.ts +2 -0
  106. package/dist/__tests__/shipment-stdin-integration.test.d.ts.map +1 -0
  107. package/dist/__tests__/shipment-stdin-integration.test.js +49 -0
  108. package/dist/__tests__/shipment-stdin-integration.test.js.map +1 -0
  109. package/dist/__tests__/shipment-type-parity.test.d.ts +2 -0
  110. package/dist/__tests__/shipment-type-parity.test.d.ts.map +1 -0
  111. package/dist/__tests__/shipment-type-parity.test.js +10 -0
  112. package/dist/__tests__/shipment-type-parity.test.js.map +1 -0
  113. package/dist/__tests__/snapshot-copy-provider.test.d.ts +2 -0
  114. package/dist/__tests__/snapshot-copy-provider.test.d.ts.map +1 -0
  115. package/dist/__tests__/snapshot-copy-provider.test.js +88 -0
  116. package/dist/__tests__/snapshot-copy-provider.test.js.map +1 -0
  117. package/dist/__tests__/swarm-coordinator-backend.test.js +153 -0
  118. package/dist/__tests__/swarm-coordinator-backend.test.js.map +1 -1
  119. package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts +2 -0
  120. package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts.map +1 -0
  121. package/dist/__tests__/swarm-coordinator-complete-intercept.test.js +111 -0
  122. package/dist/__tests__/swarm-coordinator-complete-intercept.test.js.map +1 -0
  123. package/dist/__tests__/task-store-source.test.d.ts +2 -0
  124. package/dist/__tests__/task-store-source.test.d.ts.map +1 -0
  125. package/dist/__tests__/task-store-source.test.js +56 -0
  126. package/dist/__tests__/task-store-source.test.js.map +1 -0
  127. package/dist/__tests__/transport-detect.test.d.ts +2 -0
  128. package/dist/__tests__/transport-detect.test.d.ts.map +1 -0
  129. package/dist/__tests__/transport-detect.test.js +92 -0
  130. package/dist/__tests__/transport-detect.test.js.map +1 -0
  131. package/dist/__tests__/workcycle-runner-cascade.test.d.ts +2 -0
  132. package/dist/__tests__/workcycle-runner-cascade.test.d.ts.map +1 -0
  133. package/dist/__tests__/workcycle-runner-cascade.test.js +203 -0
  134. package/dist/__tests__/workcycle-runner-cascade.test.js.map +1 -0
  135. package/dist/__tests__/workcycle-runner.test.d.ts +2 -0
  136. package/dist/__tests__/workcycle-runner.test.d.ts.map +1 -0
  137. package/dist/__tests__/workcycle-runner.test.js +369 -0
  138. package/dist/__tests__/workcycle-runner.test.js.map +1 -0
  139. package/dist/__tests__/workspace-diff.test.d.ts +2 -0
  140. package/dist/__tests__/workspace-diff.test.d.ts.map +1 -0
  141. package/dist/__tests__/workspace-diff.test.js +62 -0
  142. package/dist/__tests__/workspace-diff.test.js.map +1 -0
  143. package/dist/__tests__/workspace-manager.test.d.ts +2 -0
  144. package/dist/__tests__/workspace-manager.test.d.ts.map +1 -0
  145. package/dist/__tests__/workspace-manager.test.js +120 -0
  146. package/dist/__tests__/workspace-manager.test.js.map +1 -0
  147. package/dist/__tests__/workspace-types.test.d.ts +2 -0
  148. package/dist/__tests__/workspace-types.test.d.ts.map +1 -0
  149. package/dist/__tests__/workspace-types.test.js +37 -0
  150. package/dist/__tests__/workspace-types.test.js.map +1 -0
  151. package/dist/__tests__/worktree-gc.test.d.ts +2 -0
  152. package/dist/__tests__/worktree-gc.test.d.ts.map +1 -0
  153. package/dist/__tests__/worktree-gc.test.js +183 -0
  154. package/dist/__tests__/worktree-gc.test.js.map +1 -0
  155. package/dist/always-on/activity-reader.d.ts +27 -0
  156. package/dist/always-on/activity-reader.d.ts.map +1 -0
  157. package/dist/always-on/activity-reader.js +95 -0
  158. package/dist/always-on/activity-reader.js.map +1 -0
  159. package/dist/always-on/always-on-manager.d.ts +170 -0
  160. package/dist/always-on/always-on-manager.d.ts.map +1 -0
  161. package/dist/always-on/always-on-manager.js +538 -0
  162. package/dist/always-on/always-on-manager.js.map +1 -0
  163. package/dist/always-on/config.d.ts +141 -0
  164. package/dist/always-on/config.d.ts.map +1 -0
  165. package/dist/always-on/config.js +324 -0
  166. package/dist/always-on/config.js.map +1 -0
  167. package/dist/always-on/discovery-scheduler.d.ts +60 -0
  168. package/dist/always-on/discovery-scheduler.d.ts.map +1 -0
  169. package/dist/always-on/discovery-scheduler.js +287 -0
  170. package/dist/always-on/discovery-scheduler.js.map +1 -0
  171. package/dist/always-on/ideas-client.d.ts +23 -0
  172. package/dist/always-on/ideas-client.d.ts.map +1 -0
  173. package/dist/always-on/ideas-client.js +13 -0
  174. package/dist/always-on/ideas-client.js.map +1 -0
  175. package/dist/always-on/reconcile-researching.d.ts +42 -0
  176. package/dist/always-on/reconcile-researching.d.ts.map +1 -0
  177. package/dist/always-on/reconcile-researching.js +133 -0
  178. package/dist/always-on/reconcile-researching.js.map +1 -0
  179. package/dist/always-on/task-sources/cascade-picker.d.ts +42 -0
  180. package/dist/always-on/task-sources/cascade-picker.d.ts.map +1 -0
  181. package/dist/always-on/task-sources/cascade-picker.js +65 -0
  182. package/dist/always-on/task-sources/cascade-picker.js.map +1 -0
  183. package/dist/always-on/task-sources/idea-dedupe.d.ts +62 -0
  184. package/dist/always-on/task-sources/idea-dedupe.d.ts.map +1 -0
  185. package/dist/always-on/task-sources/idea-dedupe.js +130 -0
  186. package/dist/always-on/task-sources/idea-dedupe.js.map +1 -0
  187. package/dist/always-on/task-sources/idea-research-source.d.ts +46 -0
  188. package/dist/always-on/task-sources/idea-research-source.d.ts.map +1 -0
  189. package/dist/always-on/task-sources/idea-research-source.js +308 -0
  190. package/dist/always-on/task-sources/idea-research-source.js.map +1 -0
  191. package/dist/always-on/task-sources/idea-sort.d.ts +3 -0
  192. package/dist/always-on/task-sources/idea-sort.d.ts.map +1 -0
  193. package/dist/always-on/task-sources/idea-sort.js +25 -0
  194. package/dist/always-on/task-sources/idea-sort.js.map +1 -0
  195. package/dist/always-on/task-sources/idea-todo-source.d.ts +48 -0
  196. package/dist/always-on/task-sources/idea-todo-source.d.ts.map +1 -0
  197. package/dist/always-on/task-sources/idea-todo-source.js +226 -0
  198. package/dist/always-on/task-sources/idea-todo-source.js.map +1 -0
  199. package/dist/always-on/task-sources/introspection-source.d.ts +101 -0
  200. package/dist/always-on/task-sources/introspection-source.d.ts.map +1 -0
  201. package/dist/always-on/task-sources/introspection-source.js +695 -0
  202. package/dist/always-on/task-sources/introspection-source.js.map +1 -0
  203. package/dist/always-on/task-sources/task-store-source.d.ts +15 -0
  204. package/dist/always-on/task-sources/task-store-source.d.ts.map +1 -0
  205. package/dist/always-on/task-sources/task-store-source.js +59 -0
  206. package/dist/always-on/task-sources/task-store-source.js.map +1 -0
  207. package/dist/always-on/task-sources/types.d.ts +108 -0
  208. package/dist/always-on/task-sources/types.d.ts.map +1 -0
  209. package/dist/always-on/task-sources/types.js +13 -0
  210. package/dist/always-on/task-sources/types.js.map +1 -0
  211. package/dist/always-on/types.d.ts +76 -0
  212. package/dist/always-on/types.d.ts.map +1 -0
  213. package/dist/always-on/types.js +17 -0
  214. package/dist/always-on/types.js.map +1 -0
  215. package/dist/always-on/workcycle-runner.d.ts +115 -0
  216. package/dist/always-on/workcycle-runner.d.ts.map +1 -0
  217. package/dist/always-on/workcycle-runner.js +285 -0
  218. package/dist/always-on/workcycle-runner.js.map +1 -0
  219. package/dist/always-on/worktree-gc.d.ts +41 -0
  220. package/dist/always-on/worktree-gc.d.ts.map +1 -0
  221. package/dist/always-on/worktree-gc.js +167 -0
  222. package/dist/always-on/worktree-gc.js.map +1 -0
  223. package/dist/index.d.ts +26 -2
  224. package/dist/index.d.ts.map +1 -1
  225. package/dist/index.js +24 -1
  226. package/dist/index.js.map +1 -1
  227. package/dist/persistence.d.ts +37 -1
  228. package/dist/persistence.d.ts.map +1 -1
  229. package/dist/persistence.js +48 -0
  230. package/dist/persistence.js.map +1 -1
  231. package/dist/retrospective.d.ts.map +1 -1
  232. package/dist/retrospective.js +6 -0
  233. package/dist/retrospective.js.map +1 -1
  234. package/dist/roles/role-loader.d.ts +1 -1
  235. package/dist/roles/role-loader.d.ts.map +1 -1
  236. package/dist/roles/role-loader.js +18 -0
  237. package/dist/roles/role-loader.js.map +1 -1
  238. package/dist/roles/types.d.ts +12 -0
  239. package/dist/roles/types.d.ts.map +1 -1
  240. package/dist/shipment/gh-pr-creator.d.ts +28 -0
  241. package/dist/shipment/gh-pr-creator.d.ts.map +1 -0
  242. package/dist/shipment/gh-pr-creator.js +80 -0
  243. package/dist/shipment/gh-pr-creator.js.map +1 -0
  244. package/dist/shipment/report.d.ts +27 -0
  245. package/dist/shipment/report.d.ts.map +1 -0
  246. package/dist/shipment/report.js +41 -0
  247. package/dist/shipment/report.js.map +1 -0
  248. package/dist/shipment/secret-scrub.d.ts +12 -0
  249. package/dist/shipment/secret-scrub.d.ts.map +1 -0
  250. package/dist/shipment/secret-scrub.js +30 -0
  251. package/dist/shipment/secret-scrub.js.map +1 -0
  252. package/dist/shipment/shipment-actions.d.ts +85 -0
  253. package/dist/shipment/shipment-actions.d.ts.map +1 -0
  254. package/dist/shipment/shipment-actions.js +190 -0
  255. package/dist/shipment/shipment-actions.js.map +1 -0
  256. package/dist/shipment/shipment-pipeline.d.ts +48 -0
  257. package/dist/shipment/shipment-pipeline.d.ts.map +1 -0
  258. package/dist/shipment/shipment-pipeline.js +256 -0
  259. package/dist/shipment/shipment-pipeline.js.map +1 -0
  260. package/dist/shipment/transport-detect.d.ts +16 -0
  261. package/dist/shipment/transport-detect.d.ts.map +1 -0
  262. package/dist/shipment/transport-detect.js +54 -0
  263. package/dist/shipment/transport-detect.js.map +1 -0
  264. package/dist/shipment/workspace-diff.d.ts +39 -0
  265. package/dist/shipment/workspace-diff.d.ts.map +1 -0
  266. package/dist/shipment/workspace-diff.js +64 -0
  267. package/dist/shipment/workspace-diff.js.map +1 -0
  268. package/dist/swarm-coordinator.d.ts +20 -1
  269. package/dist/swarm-coordinator.d.ts.map +1 -1
  270. package/dist/swarm-coordinator.js +193 -10
  271. package/dist/swarm-coordinator.js.map +1 -1
  272. package/dist/types.d.ts +62 -0
  273. package/dist/types.d.ts.map +1 -1
  274. package/dist/workspace/git-worktree-provider.d.ts +11 -0
  275. package/dist/workspace/git-worktree-provider.d.ts.map +1 -0
  276. package/dist/workspace/git-worktree-provider.js +123 -0
  277. package/dist/workspace/git-worktree-provider.js.map +1 -0
  278. package/dist/workspace/gitignore-check.d.ts +10 -0
  279. package/dist/workspace/gitignore-check.d.ts.map +1 -0
  280. package/dist/workspace/gitignore-check.js +25 -0
  281. package/dist/workspace/gitignore-check.js.map +1 -0
  282. package/dist/workspace/index.d.ts +5 -0
  283. package/dist/workspace/index.d.ts.map +1 -0
  284. package/dist/workspace/index.js +5 -0
  285. package/dist/workspace/index.js.map +1 -0
  286. package/dist/workspace/snapshot-copy-provider.d.ts +11 -0
  287. package/dist/workspace/snapshot-copy-provider.d.ts.map +1 -0
  288. package/dist/workspace/snapshot-copy-provider.js +66 -0
  289. package/dist/workspace/snapshot-copy-provider.js.map +1 -0
  290. package/dist/workspace/types.d.ts +36 -0
  291. package/dist/workspace/types.d.ts.map +1 -0
  292. package/dist/workspace/types.js +2 -0
  293. package/dist/workspace/types.js.map +1 -0
  294. package/dist/workspace/workspace-manager.d.ts +30 -0
  295. package/dist/workspace/workspace-manager.d.ts.map +1 -0
  296. package/dist/workspace/workspace-manager.js +104 -0
  297. package/dist/workspace/workspace-manager.js.map +1 -0
  298. package/package.json +4 -4
  299. package/roles/queen.md +1 -0
  300. package/templates/introspection.md +64 -0
  301. package/templates/research-only.md +58 -0
  302. package/roles/preset-analyst-simons.md +0 -39
  303. package/roles/preset-architect-knuth.md +0 -39
  304. package/roles/preset-designer-norman.md +0 -39
  305. package/roles/preset-designer.md +0 -39
  306. package/roles/preset-dev-carmack.md +0 -39
  307. package/roles/preset-dev-gosling.md +0 -39
  308. package/roles/preset-developer.md +0 -52
  309. package/roles/preset-manager-grove.md +0 -39
  310. package/roles/preset-manager-musk.md +0 -39
  311. package/roles/preset-pm.md +0 -78
  312. package/roles/preset-researcher-feynman.md +0 -39
  313. package/roles/preset-reviewer.md +0 -46
  314. package/roles/preset-strategist-buffett.md +0 -39
  315. package/roles/preset-strategist-munger.md +0 -39
  316. package/roles/preset-strategist-sunzi.md +0 -39
  317. package/roles/preset-tester-beck.md +0 -40
  318. package/roles/preset-tester.md +0 -47
  319. package/roles/preset-writer-orwell.md +0 -39
  320. package/roles/preset-writer.md +0 -39
@@ -0,0 +1,62 @@
1
+ export declare const DEDUPE_BODY_PREFIX_CHARS = 500;
2
+ export declare const DEDUPE_SHINGLE_K = 4;
3
+ export declare const DEDUPE_JACCARD_THRESHOLD = 0.7;
4
+ /**
5
+ * Lowercase, strip punctuation/symbols (Unicode-aware), collapse runs of
6
+ * whitespace, trim. Two titles that normalize to the same string are treated
7
+ * as identical for dedupe purposes.
8
+ */
9
+ export declare function normalizeTitle(title: string): string;
10
+ /**
11
+ * Character k-shingle set over a lowercased + whitespace-collapsed copy of
12
+ * `text`. Returns an empty set for input shorter than `k` *after*
13
+ * normalization (so {whitespace-only, empty, "ab"} all return empty sets at
14
+ * k=4), since a single shingle would otherwise produce spurious Jaccard=1
15
+ * matches against any other tiny input.
16
+ */
17
+ export declare function shingles(text: string, k?: number): Set<string>;
18
+ export declare function jaccard(a: Set<string>, b: Set<string>): number;
19
+ export interface DedupeIndexEntry {
20
+ id: string;
21
+ normalizedTitle: string;
22
+ bodyShingles: Set<string>;
23
+ }
24
+ export interface DedupeCandidate {
25
+ title: string;
26
+ body: string;
27
+ }
28
+ export interface DedupeMatch {
29
+ matchedIdeaId: string;
30
+ reason: "title-exact" | "shingle-jaccard";
31
+ /** Jaccard score when reason === "shingle-jaccard"; omitted otherwise. */
32
+ score?: number;
33
+ }
34
+ /**
35
+ * Pre-compute an index over the existing ideas pulled from the hub so each
36
+ * incoming candidate is checked in O(N) without re-tokenizing on every
37
+ * comparison. The index is built once per ingest cycle and discarded.
38
+ */
39
+ export declare function buildDedupeIndex(existing: Array<{
40
+ id: string;
41
+ title: string;
42
+ body: string;
43
+ }>): DedupeIndexEntry[];
44
+ /**
45
+ * Linear scan: return the first index entry that matches the candidate, or
46
+ * null if none cross either threshold. Title-exact is checked before
47
+ * shingle-jaccard so the cheaper signal wins ties and the returned reason is
48
+ * the more interpretable one.
49
+ */
50
+ export declare function findDuplicate(candidate: DedupeCandidate, index: DedupeIndexEntry[], threshold?: number): DedupeMatch | null;
51
+ /**
52
+ * Convenience: extend an existing index with a freshly-inserted idea so the
53
+ * next candidate in the same cycle is checked against it too (prevents the
54
+ * queen from accidentally producing two near-identical ideas in one cohort
55
+ * and slipping both past the hub).
56
+ */
57
+ export declare function appendToIndex(index: DedupeIndexEntry[], inserted: {
58
+ id: string;
59
+ title: string;
60
+ body: string;
61
+ }): void;
62
+ //# sourceMappingURL=idea-dedupe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-dedupe.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-dedupe.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,GAAE,MAAyB,GAAG,GAAG,CAAC,MAAM,CAAC,CAQhF;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAQ9D;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,GAAG,iBAAiB,CAAC;IAC1C,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3D,gBAAgB,EAAE,CAMpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,GAAE,MAAiC,GAC3C,WAAW,GAAG,IAAI,CA2BpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,gBAAgB,EAAE,EACzB,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpD,IAAI,CAMN"}
@@ -0,0 +1,130 @@
1
+ // M8.2 — cheap text-similarity dedupe for IntrospectionSource.
2
+ //
3
+ // Used by `introspection-source.ts` to avoid re-posting the same candidate
4
+ // idea every introspection cycle (always-on can fire many ticks/day, and the
5
+ // queen's gap analysis tends to converge on the same handful of obvious
6
+ // "missing tests / missing docs / dead code" findings until the user actually
7
+ // acts on them).
8
+ //
9
+ // Two cheap checks, OR'd together:
10
+ // 1. Normalized-title exact match (lowercase + collapsed whitespace +
11
+ // stripped punctuation). Catches the dominant duplicate case where the
12
+ // queen produces an identical title across cycles.
13
+ // 2. Body-prefix k-shingle Jaccard similarity over the first
14
+ // `DEDUPE_BODY_PREFIX_CHARS` characters. Catches re-phrasings where
15
+ // title drifts slightly ("Add input validation" vs "Validate inputs")
16
+ // but body remains effectively the same.
17
+ //
18
+ // No embeddings, no network, no external deps — pure string ops. The
19
+ // reasoning is that the introspection corpus is tiny (≤ a few hundred ideas
20
+ // per project even after months) and false positives are bounded by the
21
+ // `INTROSPECTION_MAX_IDEAS` cap (3/cycle); even if dedupe occasionally
22
+ // suppresses a borderline-novel idea, the user can still discover it by
23
+ // re-running introspection after acting on prior suggestions.
24
+ export const DEDUPE_BODY_PREFIX_CHARS = 500;
25
+ export const DEDUPE_SHINGLE_K = 4;
26
+ export const DEDUPE_JACCARD_THRESHOLD = 0.7;
27
+ /**
28
+ * Lowercase, strip punctuation/symbols (Unicode-aware), collapse runs of
29
+ * whitespace, trim. Two titles that normalize to the same string are treated
30
+ * as identical for dedupe purposes.
31
+ */
32
+ export function normalizeTitle(title) {
33
+ return title
34
+ .toLowerCase()
35
+ .replace(/[\p{P}\p{S}]+/gu, " ")
36
+ .replace(/\s+/g, " ")
37
+ .trim();
38
+ }
39
+ /**
40
+ * Character k-shingle set over a lowercased + whitespace-collapsed copy of
41
+ * `text`. Returns an empty set for input shorter than `k` *after*
42
+ * normalization (so {whitespace-only, empty, "ab"} all return empty sets at
43
+ * k=4), since a single shingle would otherwise produce spurious Jaccard=1
44
+ * matches against any other tiny input.
45
+ */
46
+ export function shingles(text, k = DEDUPE_SHINGLE_K) {
47
+ const norm = text.toLowerCase().replace(/\s+/g, " ").trim();
48
+ const out = new Set();
49
+ if (norm.length < k)
50
+ return out;
51
+ for (let i = 0; i <= norm.length - k; i++) {
52
+ out.add(norm.slice(i, i + k));
53
+ }
54
+ return out;
55
+ }
56
+ export function jaccard(a, b) {
57
+ if (a.size === 0 || b.size === 0)
58
+ return 0;
59
+ const [small, large] = a.size < b.size ? [a, b] : [b, a];
60
+ let intersect = 0;
61
+ for (const s of small)
62
+ if (large.has(s))
63
+ intersect++;
64
+ const union = a.size + b.size - intersect;
65
+ if (union === 0)
66
+ return 0;
67
+ return intersect / union;
68
+ }
69
+ /**
70
+ * Pre-compute an index over the existing ideas pulled from the hub so each
71
+ * incoming candidate is checked in O(N) without re-tokenizing on every
72
+ * comparison. The index is built once per ingest cycle and discarded.
73
+ */
74
+ export function buildDedupeIndex(existing) {
75
+ return existing.map((e) => ({
76
+ id: e.id,
77
+ normalizedTitle: normalizeTitle(e.title),
78
+ bodyShingles: shingles((e.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS)),
79
+ }));
80
+ }
81
+ /**
82
+ * Linear scan: return the first index entry that matches the candidate, or
83
+ * null if none cross either threshold. Title-exact is checked before
84
+ * shingle-jaccard so the cheaper signal wins ties and the returned reason is
85
+ * the more interpretable one.
86
+ */
87
+ export function findDuplicate(candidate, index, threshold = DEDUPE_JACCARD_THRESHOLD) {
88
+ const normTitle = normalizeTitle(candidate.title);
89
+ if (normTitle.length > 0) {
90
+ for (const e of index) {
91
+ if (e.normalizedTitle.length > 0 && e.normalizedTitle === normTitle) {
92
+ return { matchedIdeaId: e.id, reason: "title-exact" };
93
+ }
94
+ }
95
+ }
96
+ const candShingles = shingles((candidate.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS));
97
+ if (candShingles.size === 0)
98
+ return null;
99
+ let best = null;
100
+ for (const e of index) {
101
+ if (e.bodyShingles.size === 0)
102
+ continue;
103
+ const score = jaccard(candShingles, e.bodyShingles);
104
+ if (score >= threshold && (!best || score > best.score)) {
105
+ best = { score, entry: e };
106
+ }
107
+ }
108
+ if (best) {
109
+ return {
110
+ matchedIdeaId: best.entry.id,
111
+ reason: "shingle-jaccard",
112
+ score: best.score,
113
+ };
114
+ }
115
+ return null;
116
+ }
117
+ /**
118
+ * Convenience: extend an existing index with a freshly-inserted idea so the
119
+ * next candidate in the same cycle is checked against it too (prevents the
120
+ * queen from accidentally producing two near-identical ideas in one cohort
121
+ * and slipping both past the hub).
122
+ */
123
+ export function appendToIndex(index, inserted) {
124
+ index.push({
125
+ id: inserted.id,
126
+ normalizedTitle: normalizeTitle(inserted.title),
127
+ bodyShingles: shingles((inserted.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS)),
128
+ });
129
+ }
130
+ //# sourceMappingURL=idea-dedupe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-dedupe.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-dedupe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { IdeasClient, ResearchConclusion } from "../ideas-client.js";
2
+ import { sortIdeasByPriorityThenAge } from "./idea-sort.js";
3
+ import type { TaskSource } from "./types.js";
4
+ export interface IdeaResearchSourceOptions {
5
+ ideasClient: IdeasClient;
6
+ /**
7
+ * Per-cycle list cap forwarded to the ideas API. Default 50 — only the
8
+ * head after sorting is consumed each cycle.
9
+ */
10
+ listLimit?: number;
11
+ /** Optional warn logger; defaults to silent. */
12
+ warn?: (obj: Record<string, unknown>, msg?: string) => void;
13
+ /** Optional info logger; defaults to silent. */
14
+ info?: (obj: Record<string, unknown>, msg?: string) => void;
15
+ /**
16
+ * Test seam: override report directory resolution. Defaults to
17
+ * `<projectRoot>/.clawnet/ideas/<ideaId>`.
18
+ */
19
+ resolveReportDir?: (projectRoot: string, ideaId: string) => string;
20
+ /** Test seam: clock. */
21
+ now?: () => Date;
22
+ }
23
+ /**
24
+ * Re-export under the original name for back-compat (S5 hotfix moved the
25
+ * implementation into `./idea-sort` to deduplicate the two source files).
26
+ */
27
+ export declare const sortIdeasForResearch: typeof sortIdeasByPriorityThenAge;
28
+ /**
29
+ * Minimal YAML-frontmatter parser. We only need the `conclusion` field for
30
+ * M8.1, so a 20-line scanner is plenty — pulling in `gray-matter` for one
31
+ * key would be overkill and adds a new transitive dep (per M8.1 constraints).
32
+ *
33
+ * Recognises:
34
+ * ---
35
+ * conclusion: promote ← keys are flat; values are unquoted single tokens
36
+ * key: value
37
+ * ---
38
+ * body…
39
+ *
40
+ * Returns null if no frontmatter block is present. Unknown keys are ignored.
41
+ */
42
+ export declare function parseReportFrontmatter(content: string): Record<string, string> | null;
43
+ /** Tolerant casing + dash/space variants. Unknown → null (caller falls back). */
44
+ export declare function normaliseConclusion(raw: string | undefined): ResearchConclusion | null;
45
+ export declare function createIdeaResearchSource(opts: IdeaResearchSourceOptions): TaskSource;
46
+ //# sourceMappingURL=idea-research-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-research-source.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-research-source.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAEV,WAAW,EACX,kBAAkB,EAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,WAAW,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,gDAAgD;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,mCAA6B,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAqBrF;AAUD,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,GAAG,IAAI,CAItF;AAwCD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,yBAAyB,GAAG,UAAU,CAkPpF"}
@@ -0,0 +1,308 @@
1
+ // M8.1 — IdeaResearchSource: consumes ideas with status="idea" and drives
2
+ // them through researching → todo/archived/idea via a research-only swarm
3
+ // cycle (no code shipped, only a markdown report).
4
+ //
5
+ // Flow (D3 in design doc):
6
+ // idea.status="idea"
7
+ // → pick (highest priority + projectId match)
8
+ // → onClaim: PATCH status=researching, researchKind=investigate,
9
+ // linkedSwarmTaskId
10
+ // → swarm runs with cycleKind=research → research-only template
11
+ // on success: parse <projectRoot>/.clawnet/ideas/<id>/research-*.md
12
+ // frontmatter, append ResearchRef, transition per conclusion:
13
+ // promote → status=todo
14
+ // archive → status=archived
15
+ // needs-info → status=idea (await user input)
16
+ // on failure: revert status=idea, clear researchKind +
17
+ // linkedSwarmTaskId
18
+ //
19
+ // Missing/malformed report = treated as needs-info (best-effort: never throws).
20
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
21
+ import { join, relative } from "node:path";
22
+ import { encodeCwd } from "@mclawnet/shared";
23
+ import { sortIdeasByPriorityThenAge } from "./idea-sort.js";
24
+ /**
25
+ * Re-export under the original name for back-compat (S5 hotfix moved the
26
+ * implementation into `./idea-sort` to deduplicate the two source files).
27
+ */
28
+ export const sortIdeasForResearch = sortIdeasByPriorityThenAge;
29
+ /**
30
+ * Minimal YAML-frontmatter parser. We only need the `conclusion` field for
31
+ * M8.1, so a 20-line scanner is plenty — pulling in `gray-matter` for one
32
+ * key would be overkill and adds a new transitive dep (per M8.1 constraints).
33
+ *
34
+ * Recognises:
35
+ * ---
36
+ * conclusion: promote ← keys are flat; values are unquoted single tokens
37
+ * key: value
38
+ * ---
39
+ * body…
40
+ *
41
+ * Returns null if no frontmatter block is present. Unknown keys are ignored.
42
+ */
43
+ export function parseReportFrontmatter(content) {
44
+ // Normalise CRLF. N3 hotfix: the regex below requires `---` at column 0
45
+ // (no leading whitespace tolerated) — the previous comment claimed
46
+ // optional leading whitespace which was misleading. We deliberately keep
47
+ // the strict shape to match the standard YAML frontmatter convention.
48
+ const normalised = content.replace(/\r\n/g, "\n");
49
+ const m = normalised.match(/^---\n([\s\S]*?)\n---/);
50
+ if (!m)
51
+ return null;
52
+ const out = {};
53
+ for (const line of m[1].split("\n")) {
54
+ const kv = line.match(/^(\w+):\s*(.*)$/);
55
+ if (!kv)
56
+ continue;
57
+ const key = kv[1].trim();
58
+ let val = kv[2].trim();
59
+ // Strip wrapping quotes if any.
60
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
61
+ val = val.slice(1, -1);
62
+ }
63
+ out[key] = val;
64
+ }
65
+ return out;
66
+ }
67
+ const CONCLUSION_ALIASES = {
68
+ promote: "promote",
69
+ "needs-info": "needs-info",
70
+ needsinfo: "needs-info",
71
+ needinfo: "needs-info",
72
+ archive: "archive",
73
+ };
74
+ /** Tolerant casing + dash/space variants. Unknown → null (caller falls back). */
75
+ export function normaliseConclusion(raw) {
76
+ if (!raw)
77
+ return null;
78
+ const key = raw.trim().toLowerCase().replace(/\s+/g, "-");
79
+ return CONCLUSION_ALIASES[key] ?? null;
80
+ }
81
+ function conclusionToStatus(c) {
82
+ switch (c) {
83
+ case "promote":
84
+ return "todo";
85
+ case "archive":
86
+ return "archived";
87
+ case "needs-info":
88
+ return "idea";
89
+ }
90
+ }
91
+ /**
92
+ * Scan a report directory for `research-*.md` files and return the path of
93
+ * the newest by mtime. Returns null when the dir is missing/empty.
94
+ */
95
+ function findNewestReport(reportDir) {
96
+ if (!existsSync(reportDir))
97
+ return null;
98
+ let best = null;
99
+ try {
100
+ for (const name of readdirSync(reportDir)) {
101
+ if (!name.startsWith("research-") || !name.endsWith(".md"))
102
+ continue;
103
+ const full = join(reportDir, name);
104
+ try {
105
+ const s = statSync(full);
106
+ if (!s.isFile())
107
+ continue;
108
+ if (!best || s.mtimeMs > best.mtimeMs) {
109
+ best = { path: full, mtimeMs: s.mtimeMs };
110
+ }
111
+ }
112
+ catch {
113
+ // ignore individual stat errors
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ return best?.path ?? null;
121
+ }
122
+ export function createIdeaResearchSource(opts) {
123
+ const { ideasClient, listLimit = 50, warn, info, resolveReportDir = (projectRoot, ideaId) => join(projectRoot, ".clawnet", "ideas", ideaId), now = () => new Date(), } = opts;
124
+ return {
125
+ name: "idea-research",
126
+ kind: "idea-research",
127
+ async pick(projectRoot) {
128
+ let projectId;
129
+ try {
130
+ projectId = encodeCwd(projectRoot);
131
+ }
132
+ catch (err) {
133
+ warn?.({ err: err instanceof Error ? err.message : String(err), projectRoot }, "idea-research-source: encodeCwd failed");
134
+ return null;
135
+ }
136
+ let ideas;
137
+ try {
138
+ ideas = await ideasClient.list({
139
+ status: "idea",
140
+ projectId,
141
+ limit: listLimit,
142
+ });
143
+ }
144
+ catch (err) {
145
+ warn?.({ err: err instanceof Error ? err.message : String(err), projectId }, "idea-research-source: list failed");
146
+ return null;
147
+ }
148
+ const matching = ideas.filter((i) => i.status === "idea" && i.projectId === projectId);
149
+ if (matching.length === 0)
150
+ return null;
151
+ const sorted = sortIdeasByPriorityThenAge(matching);
152
+ const head = sorted[0];
153
+ const description = head.body && head.body.trim().length > 0 ? head.body : undefined;
154
+ const taskRef = {
155
+ id: head.id,
156
+ title: head.title,
157
+ ...(description ? { description } : {}),
158
+ sourceKind: "idea-research",
159
+ sourceId: head.id,
160
+ // Stamp projectRoot + cycleKind so the runner picks the right template
161
+ // and onSuccess can locate the report file post-cycle.
162
+ projectRoot,
163
+ cycleKind: "research",
164
+ };
165
+ info?.({ ideaId: head.id, priority: head.priority, projectId }, "idea-research-source: picked");
166
+ return taskRef;
167
+ },
168
+ async onClaim(taskRef, swarmId) {
169
+ const ideaId = taskRef.sourceId ?? taskRef.id;
170
+ try {
171
+ // S3 hotfix note (M8 holistic review): `linkedSwarmTaskId` is a
172
+ // legacy column name — it stores a swarmId, not a taskId. The DB
173
+ // column shipped in migration 0014; renaming requires a destructive
174
+ // migration so we keep the name and document the mismatch here.
175
+ // S9 note: races between two cascade cycles (e.g. simultaneous
176
+ // research + execute claim attempts) can both flip an idea — M8
177
+ // assumes single-agent installs; M9+ may add optimistic locking.
178
+ await ideasClient.patch(ideaId, {
179
+ status: "researching",
180
+ researchKind: "investigate",
181
+ linkedSwarmTaskId: swarmId,
182
+ });
183
+ info?.({ ideaId, swarmId }, "idea-research-source: claimed → researching (investigate)");
184
+ }
185
+ catch (err) {
186
+ warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: onClaim patch failed");
187
+ }
188
+ },
189
+ async onSuccess(taskRef, swarmId) {
190
+ const ideaId = taskRef.sourceId ?? taskRef.id;
191
+ const projectRoot = taskRef.projectRoot;
192
+ // Default conclusion if anything goes wrong below — "needs-info" keeps
193
+ // the idea in front of the user without false-positive promote/archive.
194
+ let conclusion = "needs-info";
195
+ let reportPath = null;
196
+ if (!projectRoot) {
197
+ warn?.({ ideaId, swarmId }, "idea-research-source: onSuccess missing projectRoot — treating as needs-info");
198
+ }
199
+ else {
200
+ try {
201
+ const reportDir = resolveReportDir(projectRoot, ideaId);
202
+ const found = findNewestReport(reportDir);
203
+ if (!found) {
204
+ warn?.({ ideaId, swarmId, reportDir }, "idea-research-source: no research report found — treating as needs-info");
205
+ }
206
+ else {
207
+ reportPath = found;
208
+ try {
209
+ const body = readFileSync(found, "utf-8");
210
+ const fm = parseReportFrontmatter(body);
211
+ if (!fm) {
212
+ warn?.({ ideaId, swarmId, found }, "idea-research-source: report missing frontmatter — needs-info");
213
+ }
214
+ else {
215
+ const parsed = normaliseConclusion(fm.conclusion);
216
+ if (!parsed) {
217
+ warn?.({ ideaId, swarmId, raw: fm.conclusion }, "idea-research-source: unknown conclusion — needs-info");
218
+ }
219
+ else {
220
+ conclusion = parsed;
221
+ }
222
+ }
223
+ }
224
+ catch (err) {
225
+ warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId, found }, "idea-research-source: failed to read report — needs-info");
226
+ }
227
+ }
228
+ }
229
+ catch (err) {
230
+ warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: resolveReportDir threw — needs-info");
231
+ }
232
+ }
233
+ // Build the ResearchRef, normalising path to projectRoot-relative when
234
+ // possible (matches the schema convention in
235
+ // packages/@clawnet/shared/src/schemas.ts).
236
+ const ranAt = now().toISOString();
237
+ let storedReportPath;
238
+ if (reportPath && projectRoot) {
239
+ // Hotfix I5 — use path.relative so we get a proper separator
240
+ // boundary. The prior `startsWith(projectRoot)` check would happily
241
+ // strip the wrong prefix for sibling directories sharing a name
242
+ // prefix (`/x/proj` vs `/x/proj-other` → `-other/...`). When the
243
+ // report sits outside the project tree we fall back to the absolute
244
+ // path so the breadcrumb still points somewhere meaningful.
245
+ const rel = relative(projectRoot, reportPath);
246
+ storedReportPath = rel.startsWith("..") ? reportPath : rel;
247
+ }
248
+ else {
249
+ storedReportPath = reportPath ?? `.clawnet/ideas/${ideaId}/research-missing.md`;
250
+ }
251
+ const ref = {
252
+ reportPath: storedReportPath,
253
+ swarmId,
254
+ conclusion,
255
+ ranAt,
256
+ };
257
+ // Read-modify-write the researchReports array so we don't clobber
258
+ // earlier reports. Best-effort: a hub read error degrades to a
259
+ // single-element array (caller can still see this run).
260
+ let nextReports = [ref];
261
+ try {
262
+ const current = await ideasClient.get(ideaId);
263
+ const existing = current?.researchReports ?? [];
264
+ // Newest-first ordering per design doc D2.
265
+ nextReports = [ref, ...existing];
266
+ }
267
+ catch (err) {
268
+ warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: failed to read existing researchReports — overwriting with single entry");
269
+ }
270
+ const nextStatus = conclusionToStatus(conclusion);
271
+ try {
272
+ await ideasClient.patch(ideaId, {
273
+ status: nextStatus,
274
+ researchKind: null,
275
+ researchReports: nextReports,
276
+ });
277
+ info?.({ ideaId, swarmId, conclusion, nextStatus }, "idea-research-source: research cycle complete");
278
+ }
279
+ catch (err) {
280
+ warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: onSuccess patch failed");
281
+ }
282
+ },
283
+ async onFailure(taskRef, swarmId, error) {
284
+ const ideaId = taskRef.sourceId ?? taskRef.id;
285
+ try {
286
+ // Revert to status=idea so the user (or a future cycle) can re-pick.
287
+ // Clear both researchKind and linkedSwarmTaskId — the failed cycle is
288
+ // no longer relevant breadcrumbs (vs IdeaTodoSource which keeps the
289
+ // linkedSwarmTaskId for debugging).
290
+ await ideasClient.patch(ideaId, {
291
+ status: "idea",
292
+ researchKind: null,
293
+ linkedSwarmTaskId: null,
294
+ });
295
+ info?.({ ideaId, swarmId, error: error ?? null }, "idea-research-source: reverted to idea after failure");
296
+ }
297
+ catch (err) {
298
+ warn?.({
299
+ err: err instanceof Error ? err.message : String(err),
300
+ ideaId,
301
+ swarmId,
302
+ originalError: error,
303
+ }, "idea-research-source: onFailure revert failed");
304
+ }
305
+ },
306
+ };
307
+ }
308
+ //# sourceMappingURL=idea-research-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-research-source.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-research-source.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,mDAAmD;AACnD,EAAE;AACF,2BAA2B;AAC3B,uBAAuB;AACvB,kDAAkD;AAClD,qEAAqE;AACrE,mCAAmC;AACnC,oEAAoE;AACpE,2EAA2E;AAC3E,iFAAiF;AACjF,gDAAgD;AAChD,oDAAoD;AACpD,mEAAmE;AACnE,8DAA8D;AAC9D,uCAAuC;AACvC,EAAE;AACF,gFAAgF;AAEhF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAO7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAuB5D;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,wEAAwE;IACxE,mEAAmE;IACnE,yEAAyE;IACzE,sEAAsE;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvB,gCAAgC;QAChC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC7F,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,kBAAkB,GAAuC;IAC7D,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,YAAY;IAC1B,SAAS,EAAE,YAAY;IACvB,QAAQ,EAAE,YAAY;IACtB,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF,iFAAiF;AACjF,MAAM,UAAU,mBAAmB,CAAC,GAAuB;IACzD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAqB;IAC/C,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,UAAU,CAAC;QACpB,KAAK,YAAY;YACf,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,SAAiB;IACzC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,IAAI,GAA6C,IAAI,CAAC;IAC1D,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAS;YACrE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACzB,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE;oBAAE,SAAS;gBAC1B,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;oBACtC,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC5C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAA+B;IACtE,MAAM,EACJ,WAAW,EACX,SAAS,GAAG,EAAE,EACd,IAAI,EACJ,IAAI,EACJ,gBAAgB,GAAG,CAAC,WAAmB,EAAE,MAAc,EAAE,EAAE,CACzD,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,EAChD,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,GACvB,GAAG,IAAI,CAAC;IAET,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,eAAe;QACrB,KAAK,CAAC,IAAI,CAAC,WAAmB;YAC5B,IAAI,SAAiB,CAAC;YACtB,IAAI,CAAC;gBACH,SAAS,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,EACtE,wCAAwC,CACzC,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,KAAa,CAAC;YAClB,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;oBAC7B,MAAM,EAAE,MAAM;oBACd,SAAS;oBACT,KAAK,EAAE,SAAS;iBACjB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,EACpE,mCAAmC,CACpC,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CACxD,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAEvC,MAAM,MAAM,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;YACpD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAEvB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACrF,MAAM,OAAO,GAAY;gBACvB,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvC,UAAU,EAAE,eAAe;gBAC3B,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,uEAAuE;gBACvE,uDAAuD;gBACvD,WAAW;gBACX,SAAS,EAAE,UAAU;aACtB,CAAC;YAEF,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,EACvD,8BAA8B,CAC/B,CAAC;YACF,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,OAAgB,EAAE,OAAe;YAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,gEAAgE;gBAChE,iEAAiE;gBACjE,oEAAoE;gBACpE,gEAAgE;gBAChE,+DAA+D;gBAC/D,gEAAgE;gBAChE,iEAAiE;gBACjE,MAAM,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE;oBAC9B,MAAM,EAAE,aAAa;oBACrB,YAAY,EAAE,aAAa;oBAC3B,iBAAiB,EAAE,OAAO;iBAC3B,CAAC,CAAC;gBACH,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,EACnB,2DAA2D,CAC5D,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAC1E,4CAA4C,CAC7C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,OAAgB,EAAE,OAAe;YAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;YACxC,uEAAuE;YACvE,wEAAwE;YACxE,IAAI,UAAU,GAAuB,YAAY,CAAC;YAClD,IAAI,UAAU,GAAkB,IAAI,CAAC;YAErC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,EACnB,8EAA8E,CAC/E,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;oBACxD,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;oBAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;wBACX,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,EAC9B,yEAAyE,CAC1E,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,UAAU,GAAG,KAAK,CAAC;wBACnB,IAAI,CAAC;4BACH,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;4BAC1C,MAAM,EAAE,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;4BACxC,IAAI,CAAC,EAAE,EAAE,CAAC;gCACR,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAC1B,+DAA+D,CAChE,CAAC;4BACJ,CAAC;iCAAM,CAAC;gCACN,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;gCAClD,IAAI,CAAC,MAAM,EAAE,CAAC;oCACZ,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,UAAU,EAAE,EACvC,uDAAuD,CACxD,CAAC;gCACJ,CAAC;qCAAM,CAAC;oCACN,UAAU,GAAG,MAAM,CAAC;gCACtB,CAAC;4BACH,CAAC;wBACH,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EACjF,0DAA0D,CAC3D,CAAC;wBACJ,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAC1E,2DAA2D,CAC5D,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,uEAAuE;YACvE,6CAA6C;YAC7C,4CAA4C;YAC5C,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;YAClC,IAAI,gBAAwB,CAAC;YAC7B,IAAI,UAAU,IAAI,WAAW,EAAE,CAAC;gBAC9B,6DAA6D;gBAC7D,oEAAoE;gBACpE,gEAAgE;gBAChE,iEAAiE;gBACjE,oEAAoE;gBACpE,4DAA4D;gBAC5D,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;gBAC9C,gBAAgB,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,gBAAgB,GAAG,UAAU,IAAI,kBAAkB,MAAM,sBAAsB,CAAC;YAClF,CAAC;YACD,MAAM,GAAG,GAAgB;gBACvB,UAAU,EAAE,gBAAgB;gBAC5B,OAAO;gBACP,UAAU;gBACV,KAAK;aACN,CAAC;YAEF,kEAAkE;YAClE,+DAA+D;YAC/D,wDAAwD;YACxD,IAAI,WAAW,GAAkB,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC9C,MAAM,QAAQ,GAAG,OAAO,EAAE,eAAe,IAAI,EAAE,CAAC;gBAChD,2CAA2C;gBAC3C,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAC1E,+FAA+F,CAChG,CAAC;YACJ,CAAC;YAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAClD,IAAI,CAAC;gBACH,MAAM,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE;oBAC9B,MAAM,EAAE,UAAU;oBAClB,YAAY,EAAE,IAAI;oBAClB,eAAe,EAAE,WAAW;iBAC7B,CAAC,CAAC;gBACH,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,EAC3C,+CAA+C,CAChD,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAC1E,8CAA8C,CAC/C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,OAAgB,EAAE,OAAe,EAAE,KAAc;YAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,qEAAqE;gBACrE,sEAAsE;gBACtE,oEAAoE;gBACpE,oCAAoC;gBACpC,MAAM,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE;oBAC9B,MAAM,EAAE,MAAM;oBACd,YAAY,EAAE,IAAI;oBAClB,iBAAiB,EAAE,IAAI;iBACxB,CAAC,CAAC;gBACH,IAAI,EAAE,CACJ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,EACzC,sDAAsD,CACvD,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,EAAE,CACJ;oBACE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACrD,MAAM;oBACN,OAAO;oBACP,aAAa,EAAE,KAAK;iBACrB,EACD,+CAA+C,CAChD,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Idea } from "../ideas-client.js";
2
+ export declare function sortIdeasByPriorityThenAge(ideas: Idea[]): Idea[];
3
+ //# sourceMappingURL=idea-sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-sort.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-sort.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAgB,MAAM,oBAAoB,CAAC;AAQ7D,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAMhE"}
@@ -0,0 +1,25 @@
1
+ // M8 hotfix S5 — shared sort helper used by both IdeaTodoSource (status=todo
2
+ // cycle) and IdeaResearchSource (status=idea research cycle). Originally
3
+ // duplicated as `sortIdeasForPicking` / `sortIdeasForResearch`; both used
4
+ // the same priority + updatedAt ordering, only the type was identical.
5
+ // Extracting here keeps the two sources in lockstep and removes a future
6
+ // drift risk.
7
+ //
8
+ // Sort order:
9
+ // 1. priority DESC (high > medium > low)
10
+ // 2. updatedAt ASC (oldest first — avoids starving early-written ideas
11
+ // when fresh edits keep landing on newer ones).
12
+ const PRIORITY_RANK = {
13
+ high: 3,
14
+ medium: 2,
15
+ low: 1,
16
+ };
17
+ export function sortIdeasByPriorityThenAge(ideas) {
18
+ return [...ideas].sort((a, b) => {
19
+ const pdiff = (PRIORITY_RANK[b.priority] ?? 0) - (PRIORITY_RANK[a.priority] ?? 0);
20
+ if (pdiff !== 0)
21
+ return pdiff;
22
+ return a.updatedAt.localeCompare(b.updatedAt);
23
+ });
24
+ }
25
+ //# sourceMappingURL=idea-sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idea-sort.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-sort.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;AACzE,cAAc;AACd,EAAE;AACF,cAAc;AACd,2CAA2C;AAC3C,yEAAyE;AACzE,qDAAqD;AAIrD,MAAM,aAAa,GAAiC;IAClD,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAClF,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9B,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC"}