@bevel-software/platform-core-backend 0.21.0 → 0.23.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 (434) hide show
  1. package/dist/core/core-ports.d.ts +11 -1
  2. package/dist/core/core-ports.d.ts.map +1 -1
  3. package/dist/core/core-ports.js.map +1 -1
  4. package/dist/core/create-core-server.d.ts +3 -3
  5. package/dist/core/create-core-server.d.ts.map +1 -1
  6. package/dist/core/create-core-server.js +62 -13
  7. package/dist/core/create-core-server.js.map +1 -1
  8. package/dist/core/create-core-services.d.ts +22 -3
  9. package/dist/core/create-core-services.d.ts.map +1 -1
  10. package/dist/core/create-core-services.js +158 -23
  11. package/dist/core/create-core-services.js.map +1 -1
  12. package/dist/core/lifecycle.d.ts +34 -1
  13. package/dist/core/lifecycle.d.ts.map +1 -1
  14. package/dist/core/lifecycle.js +89 -13
  15. package/dist/core/lifecycle.js.map +1 -1
  16. package/dist/core-config.d.ts +0 -12
  17. package/dist/core-config.d.ts.map +1 -1
  18. package/dist/core-config.js +11 -13
  19. package/dist/core-config.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/modules/access/access-control.interface.d.ts +42 -12
  25. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  26. package/dist/modules/access/access-control.service.d.ts +67 -10
  27. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  28. package/dist/modules/access/access-control.service.js +214 -35
  29. package/dist/modules/access/access-control.service.js.map +1 -1
  30. package/dist/modules/access/access-requests.contract.d.ts +75 -0
  31. package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
  32. package/dist/modules/access/access-requests.contract.js +20 -0
  33. package/dist/modules/access/access-requests.contract.js.map +1 -0
  34. package/dist/modules/access/access-requests.routes.d.ts +35 -0
  35. package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
  36. package/dist/modules/access/access-requests.routes.js +237 -0
  37. package/dist/modules/access/access-requests.routes.js.map +1 -0
  38. package/dist/modules/access/access-requests.service.d.ts +123 -0
  39. package/dist/modules/access/access-requests.service.d.ts.map +1 -0
  40. package/dist/modules/access/access-requests.service.js +337 -0
  41. package/dist/modules/access/access-requests.service.js.map +1 -0
  42. package/dist/modules/access/access.routes.d.ts.map +1 -1
  43. package/dist/modules/access/access.routes.js +13 -15
  44. package/dist/modules/access/access.routes.js.map +1 -1
  45. package/dist/modules/access-model/access-grammar.d.ts +12 -0
  46. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  47. package/dist/modules/access-model/access-grammar.js +24 -0
  48. package/dist/modules/access-model/access-grammar.js.map +1 -1
  49. package/dist/modules/auth/account-admission.d.ts +58 -7
  50. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  51. package/dist/modules/auth/account-admission.js +44 -1
  52. package/dist/modules/auth/account-admission.js.map +1 -1
  53. package/dist/modules/auth/account.routes.d.ts +1 -1
  54. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  55. package/dist/modules/auth/account.routes.js +61 -5
  56. package/dist/modules/auth/account.routes.js.map +1 -1
  57. package/dist/modules/auth/auth.middleware.d.ts +1 -1
  58. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  59. package/dist/modules/auth/auth.middleware.js +18 -7
  60. package/dist/modules/auth/auth.middleware.js.map +1 -1
  61. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  62. package/dist/modules/auth/auth.routes.js +8 -0
  63. package/dist/modules/auth/auth.routes.js.map +1 -1
  64. package/dist/modules/auth/auth.service.d.ts +96 -3
  65. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  66. package/dist/modules/auth/auth.service.js +187 -9
  67. package/dist/modules/auth/auth.service.js.map +1 -1
  68. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  69. package/dist/modules/auth/oidc-auth-provider.js +7 -2
  70. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  71. package/dist/modules/database/core-schema.d.ts +41 -24
  72. package/dist/modules/database/core-schema.d.ts.map +1 -1
  73. package/dist/modules/database/core-schema.js +35 -27
  74. package/dist/modules/database/core-schema.js.map +1 -1
  75. package/dist/modules/github-app/github-app.client.d.ts +101 -0
  76. package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
  77. package/dist/modules/github-app/github-app.client.js +222 -0
  78. package/dist/modules/github-app/github-app.client.js.map +1 -0
  79. package/dist/modules/github-app/github-app.connection.d.ts +102 -0
  80. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
  81. package/dist/modules/github-app/github-app.connection.js +185 -0
  82. package/dist/modules/github-app/github-app.connection.js.map +1 -0
  83. package/dist/modules/github-app/github-app.routes.d.ts +59 -0
  84. package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
  85. package/dist/modules/github-app/github-app.routes.js +323 -0
  86. package/dist/modules/github-app/github-app.routes.js.map +1 -0
  87. package/dist/modules/github-app/index.d.ts +4 -0
  88. package/dist/modules/github-app/index.d.ts.map +1 -0
  89. package/dist/modules/github-app/index.js +4 -0
  90. package/dist/modules/github-app/index.js.map +1 -0
  91. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  92. package/dist/modules/kb-fs/branch-name.js +12 -2
  93. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  94. package/dist/modules/kb-fs/remote-url.d.ts +22 -0
  95. package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
  96. package/dist/modules/kb-fs/remote-url.js +35 -0
  97. package/dist/modules/kb-fs/remote-url.js.map +1 -0
  98. package/dist/modules/kb-fs/repo-path.d.ts +11 -16
  99. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  100. package/dist/modules/kb-fs/repo-path.js +79 -0
  101. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  102. package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
  103. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  104. package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
  105. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  106. package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
  107. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
  108. package/dist/modules/kb-sync/sync-auth.js +1 -1
  109. package/dist/modules/kb-sync/sync-auth.js.map +1 -1
  110. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  111. package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
  112. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  113. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  114. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
  115. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  116. package/dist/modules/plugins/join-proposals.d.ts +17 -5
  117. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  118. package/dist/modules/plugins/join-proposals.js +76 -22
  119. package/dist/modules/plugins/join-proposals.js.map +1 -1
  120. package/dist/modules/plugins/join-requests.service.d.ts +111 -18
  121. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  122. package/dist/modules/plugins/join-requests.service.js +173 -29
  123. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  124. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  125. package/dist/modules/plugins/plugins.routes.js +3 -2
  126. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  127. package/dist/modules/settings/deployment-settings.service.d.ts +22 -1
  128. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  129. package/dist/modules/settings/deployment-settings.service.js +101 -7
  130. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  131. package/dist/modules/settings/managed-repository.d.ts +43 -0
  132. package/dist/modules/settings/managed-repository.d.ts.map +1 -0
  133. package/dist/modules/settings/managed-repository.js +60 -0
  134. package/dist/modules/settings/managed-repository.js.map +1 -0
  135. package/dist/modules/settings/repository-source.d.ts +128 -0
  136. package/dist/modules/settings/repository-source.d.ts.map +1 -0
  137. package/dist/modules/settings/repository-source.js +150 -0
  138. package/dist/modules/settings/repository-source.js.map +1 -0
  139. package/dist/modules/settings/setup.routes.d.ts +78 -4
  140. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  141. package/dist/modules/settings/setup.routes.js +379 -17
  142. package/dist/modules/settings/setup.routes.js.map +1 -1
  143. package/dist/modules/skills/skill-access-requests.routes.d.ts +6 -8
  144. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  145. package/dist/modules/skills/skill-access-requests.routes.js +24 -63
  146. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  147. package/dist/modules/skills/skills.contract.d.ts +90 -18
  148. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  149. package/dist/modules/skills/skills.contract.js +4 -1
  150. package/dist/modules/skills/skills.contract.js.map +1 -1
  151. package/dist/modules/skills/skills.service.d.ts +98 -9
  152. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  153. package/dist/modules/skills/skills.service.js +239 -37
  154. package/dist/modules/skills/skills.service.js.map +1 -1
  155. package/dist/modules/skills/skills.tools.d.ts +7 -0
  156. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  157. package/dist/modules/skills/skills.tools.js +71 -7
  158. package/dist/modules/skills/skills.tools.js.map +1 -1
  159. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  160. package/dist/modules/tool-auth/external-api-key.service.js +4 -2
  161. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  162. package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
  163. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
  164. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  165. package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
  166. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  167. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  168. package/dist/modules/tool-helpers/tool-context.js +15 -0
  169. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  170. package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
  171. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  172. package/dist/modules/tool-helpers/tool-handler.js +25 -4
  173. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  174. package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
  175. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  176. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  177. package/dist/modules/tool-helpers/validate-token.js +1 -1
  178. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  179. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
  180. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
  181. package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
  182. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
  183. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  184. package/dist/modules/workflow/agent-tools/workflow.tools.js +113 -37
  185. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  186. package/dist/modules/workflow/file-lock.service.d.ts +24 -0
  187. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  188. package/dist/modules/workflow/file-lock.service.js +30 -0
  189. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  190. package/dist/modules/workflow/git/git.service.d.ts +72 -1
  191. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  192. package/dist/modules/workflow/git/git.service.js +170 -6
  193. package/dist/modules/workflow/git/git.service.js.map +1 -1
  194. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  195. package/dist/modules/workflow/git/node-git-runner.js +5 -0
  196. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  197. package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
  198. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  199. package/dist/modules/workflow/git/pull-request.service.js +99 -0
  200. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  201. package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
  202. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  203. package/dist/modules/workflow/pending-commits.service.js +55 -0
  204. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  205. package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
  206. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
  207. package/dist/modules/workflow/workflow-hooks.js +16 -1
  208. package/dist/modules/workflow/workflow-hooks.js.map +1 -1
  209. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  210. package/dist/modules/workflow/workflow.routes.js +3 -1
  211. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  212. package/dist/modules/workflow/workflow.service.d.ts +94 -13
  213. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  214. package/dist/modules/workflow/workflow.service.js +258 -51
  215. package/dist/modules/workflow/workflow.service.js.map +1 -1
  216. package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
  217. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
  218. package/dist/modules/workspace/agent-access.gate.js +123 -0
  219. package/dist/modules/workspace/agent-access.gate.js.map +1 -0
  220. package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
  221. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
  222. package/dist/modules/workspace/routine-write-policy.js +5 -6
  223. package/dist/modules/workspace/routine-write-policy.js.map +1 -1
  224. package/dist/modules/workspace/session-sink.d.ts +5 -5
  225. package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
  226. package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
  227. package/dist/modules/workspace/set-aside-clone.js +92 -0
  228. package/dist/modules/workspace/set-aside-clone.js.map +1 -0
  229. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +117 -1
  230. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  231. package/dist/modules/workspace/startup/kb-startup-runner.js +190 -1
  232. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  233. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  234. package/dist/modules/workspace/workspace.routes.js +92 -3
  235. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  236. package/dist/modules/workspace/workspace.service.d.ts +115 -5
  237. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  238. package/dist/modules/workspace/workspace.service.js +272 -26
  239. package/dist/modules/workspace/workspace.service.js.map +1 -1
  240. package/dist/modules/workspace/workspace.tools.d.ts +2 -2
  241. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  242. package/dist/modules/workspace/workspace.tools.js +337 -117
  243. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  244. package/dist/modules/write-access/write-access.d.ts +60 -0
  245. package/dist/modules/write-access/write-access.d.ts.map +1 -0
  246. package/dist/modules/write-access/write-access.js +129 -0
  247. package/dist/modules/write-access/write-access.js.map +1 -0
  248. package/dist/shared/domain-errors.d.ts +64 -0
  249. package/dist/shared/domain-errors.d.ts.map +1 -1
  250. package/dist/shared/domain-errors.js +83 -0
  251. package/dist/shared/domain-errors.js.map +1 -1
  252. package/dist/shared/git.contract.d.ts +30 -0
  253. package/dist/shared/git.contract.d.ts.map +1 -1
  254. package/dist/shared/git.contract.js +26 -0
  255. package/dist/shared/git.contract.js.map +1 -1
  256. package/dist/tenancy/static-tenant-source.d.ts +0 -1
  257. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  258. package/dist/tenancy/static-tenant-source.js +0 -2
  259. package/dist/tenancy/static-tenant-source.js.map +1 -1
  260. package/migrations/0014_change_request_closed_reason.sql +1 -0
  261. package/migrations/0015_account_deactivation.sql +3 -0
  262. package/migrations/meta/0014_snapshot.json +2265 -0
  263. package/migrations/meta/0015_snapshot.json +2271 -0
  264. package/migrations/meta/_journal.json +14 -0
  265. package/package.json +3 -3
  266. package/src/__tests__/kb-layout-config.test.ts +0 -2
  267. package/src/__tests__/retired-settings.test.ts +96 -0
  268. package/src/core/__tests__/gated-boot.test.ts +132 -0
  269. package/src/core/__tests__/lifecycle.test.ts +170 -1
  270. package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
  271. package/src/core/core-ports.ts +11 -1
  272. package/src/core/create-core-server.ts +69 -15
  273. package/src/core/create-core-services.ts +190 -27
  274. package/src/core/lifecycle.ts +98 -13
  275. package/src/core-config.ts +13 -14
  276. package/src/index.ts +12 -1
  277. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
  278. package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
  279. package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
  280. package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
  281. package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
  282. package/src/modules/access/access-control.interface.ts +43 -12
  283. package/src/modules/access/access-control.service.ts +234 -36
  284. package/src/modules/access/access-requests.contract.ts +93 -0
  285. package/src/modules/access/access-requests.routes.ts +286 -0
  286. package/src/modules/access/access-requests.service.ts +420 -0
  287. package/src/modules/access/access.routes.ts +13 -15
  288. package/src/modules/access-model/access-grammar.ts +22 -0
  289. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +3 -1
  290. package/src/modules/auth/__tests__/account-deactivation.test.ts +252 -0
  291. package/src/modules/auth/__tests__/account.routes.test.ts +81 -3
  292. package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
  293. package/src/modules/auth/__tests__/auth.service.test.ts +39 -1
  294. package/src/modules/auth/account-admission.ts +78 -9
  295. package/src/modules/auth/account.routes.ts +63 -7
  296. package/src/modules/auth/auth.middleware.ts +19 -8
  297. package/src/modules/auth/auth.routes.ts +8 -0
  298. package/src/modules/auth/auth.service.ts +207 -7
  299. package/src/modules/auth/oidc-auth-provider.ts +7 -2
  300. package/src/modules/database/core-schema.ts +35 -27
  301. package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
  302. package/src/modules/github-app/github-app.client.ts +268 -0
  303. package/src/modules/github-app/github-app.connection.ts +204 -0
  304. package/src/modules/github-app/github-app.routes.ts +359 -0
  305. package/src/modules/github-app/index.ts +19 -0
  306. package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
  307. package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
  308. package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
  309. package/src/modules/kb-fs/branch-name.ts +14 -1
  310. package/src/modules/kb-fs/remote-url.ts +35 -0
  311. package/src/modules/kb-fs/repo-path.ts +85 -0
  312. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
  313. package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
  314. package/src/modules/kb-sync/sync-auth.ts +2 -2
  315. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
  316. package/src/modules/mcp/__tests__/mcp.service.test.ts +68 -6
  317. package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
  318. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
  319. package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
  320. package/src/modules/plugins/__tests__/join-requests.service.test.ts +27 -11
  321. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
  322. package/src/modules/plugins/__tests__/plugins.routes.test.ts +1 -1
  323. package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
  324. package/src/modules/plugins/join-proposals.ts +87 -19
  325. package/src/modules/plugins/join-requests.service.ts +199 -39
  326. package/src/modules/plugins/plugins.routes.ts +3 -2
  327. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
  328. package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
  329. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +386 -0
  330. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +299 -0
  331. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +236 -0
  332. package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
  333. package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
  334. package/src/modules/settings/deployment-settings.service.ts +117 -6
  335. package/src/modules/settings/managed-repository.ts +68 -0
  336. package/src/modules/settings/repository-source.ts +197 -0
  337. package/src/modules/settings/setup.routes.ts +463 -15
  338. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
  339. package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
  340. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +5 -1
  341. package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
  342. package/src/modules/skills/skill-access-requests.routes.ts +25 -75
  343. package/src/modules/skills/skills.contract.ts +91 -18
  344. package/src/modules/skills/skills.service.ts +278 -41
  345. package/src/modules/skills/skills.tools.ts +80 -8
  346. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
  347. package/src/modules/tool-auth/external-api-key.service.ts +4 -2
  348. package/src/modules/tool-auth/internal-token.service.ts +3 -3
  349. package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
  350. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
  351. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
  352. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
  353. package/src/modules/tool-helpers/tool-context.ts +15 -0
  354. package/src/modules/tool-helpers/tool-handler.ts +24 -4
  355. package/src/modules/tool-helpers/tool.contract.ts +3 -2
  356. package/src/modules/tool-helpers/validate-token.ts +1 -1
  357. package/src/modules/workflow/__tests__/apply-failure.test.ts +9 -1
  358. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
  359. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
  360. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +159 -8
  361. package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
  362. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +146 -40
  363. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +405 -7
  364. package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
  365. package/src/modules/workflow/agent-tools/workflow.tools.ts +135 -38
  366. package/src/modules/workflow/file-lock.service.ts +31 -0
  367. package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
  368. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +121 -0
  369. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
  370. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
  371. package/src/modules/workflow/git/git.service.ts +193 -4
  372. package/src/modules/workflow/git/node-git-runner.ts +6 -0
  373. package/src/modules/workflow/git/pull-request.service.ts +119 -0
  374. package/src/modules/workflow/pending-commits.service.ts +59 -1
  375. package/src/modules/workflow/workflow-hooks.ts +64 -26
  376. package/src/modules/workflow/workflow.routes.ts +3 -1
  377. package/src/modules/workflow/workflow.service.ts +290 -55
  378. package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
  379. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +384 -0
  380. package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
  381. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
  382. package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
  383. package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
  384. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
  385. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
  386. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +326 -0
  387. package/src/modules/workspace/__tests__/workspace.service.test.ts +20 -9
  388. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +43 -0
  389. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +2 -3
  390. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +235 -10
  391. package/src/modules/workspace/__tests__/workspace.tools.test.ts +720 -17
  392. package/src/modules/workspace/agent-access.gate.ts +164 -0
  393. package/src/modules/workspace/routine-write-policy.ts +5 -6
  394. package/src/modules/workspace/session-sink.ts +5 -5
  395. package/src/modules/workspace/set-aside-clone.ts +96 -0
  396. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +297 -0
  397. package/src/modules/workspace/startup/kb-startup-runner.ts +242 -2
  398. package/src/modules/workspace/workspace.routes.ts +95 -3
  399. package/src/modules/workspace/workspace.service.ts +273 -25
  400. package/src/modules/workspace/workspace.tools.ts +373 -119
  401. package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
  402. package/src/modules/write-access/write-access.ts +153 -0
  403. package/src/shared/domain-errors.ts +89 -0
  404. package/src/shared/git.contract.ts +37 -0
  405. package/src/tenancy/__tests__/static-tenant-source.test.ts +0 -1
  406. package/src/tenancy/static-tenant-source.ts +0 -3
  407. package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
  408. package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
  409. package/dist/modules/workflow/session-ontology.policy.js +0 -62
  410. package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
  411. package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
  412. package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
  413. package/dist/modules/workflow/session-ontology.service.js +0 -147
  414. package/dist/modules/workflow/session-ontology.service.js.map +0 -1
  415. package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
  416. package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
  417. package/dist/modules/workspace/session-ontology.gate.js +0 -161
  418. package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
  419. package/dist/shared/kb-layout.d.ts +0 -39
  420. package/dist/shared/kb-layout.d.ts.map +0 -1
  421. package/dist/shared/kb-layout.js +0 -103
  422. package/dist/shared/kb-layout.js.map +0 -1
  423. package/dist/shared/kb-layout.test.d.ts +0 -2
  424. package/dist/shared/kb-layout.test.d.ts.map +0 -1
  425. package/dist/shared/kb-layout.test.js +0 -75
  426. package/dist/shared/kb-layout.test.js.map +0 -1
  427. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
  428. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
  429. package/src/modules/workflow/session-ontology.policy.ts +0 -70
  430. package/src/modules/workflow/session-ontology.service.ts +0 -183
  431. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
  432. package/src/modules/workspace/session-ontology.gate.ts +0 -191
  433. package/src/shared/kb-layout.test.ts +0 -98
  434. package/src/shared/kb-layout.ts +0 -102
@@ -45,17 +45,26 @@ function detail(overrides: Partial<PullRequestDetail> = {}): PullRequestDetail {
45
45
  branch: 'alice/deal',
46
46
  base: 'current-company-state',
47
47
  behind: true,
48
+ needsUpdate: true,
48
49
  viewerCanUpdate: true,
49
50
  headSha: HEAD,
50
51
  mergeBaseSha: 'c'.repeat(40),
52
+ // The pre-flight read asks for no patches, but it still names the files —
53
+ // which is what the fallback for a failed diff answers with.
54
+ files: [{ path: 'Sales/Deal.md' }],
51
55
  ...overrides,
52
- } as PullRequestDetail;
56
+ } as unknown as PullRequestDetail;
53
57
  }
54
58
 
55
59
  function harness(opts: {
56
60
  first?: PullRequestDetail | null;
57
61
  /** The published head of the proposal branch as resolved just before the merge. */
58
62
  publishedHead?: string;
63
+ /**
64
+ * The published head AFTER the merge. Defaults to the merge commit; set it
65
+ * equal to the pre-merge head to describe a merge that moved nothing.
66
+ */
67
+ headAfterMerge?: string;
59
68
  resolveError?: Error;
60
69
  merge?: { kind: 'clean'; alreadyUpToDate: boolean } | { kind: 'conflicts'; paths: string[] };
61
70
  pullError?: Error;
@@ -64,31 +73,42 @@ function harness(opts: {
64
73
  changedPathsError?: Error;
65
74
  /** How many approval rows the carry-forward wrote. */
66
75
  carried?: number;
67
- /** The detail the extra read after a carry-forward answers with. */
68
- withApprovals?: PullRequestDetail;
76
+ /** Does the clone hold commits origin has not seen? */
77
+ unpushed?: boolean;
78
+ unpushedError?: Error;
69
79
  }) {
80
+ // Two resolves per update: once after the pull (where the branch IS), once
81
+ // after the merge (where it ended up). One mock that answers in order, so a
82
+ // test can describe a head that moved without stubbing call indices.
83
+ const preMergeHead = opts.publishedHead ?? HEAD;
84
+ const postMergeHead = opts.headAfterMerge ?? MERGED_HEAD;
85
+ const resolvePrShas = opts.resolveError
86
+ ? vi.fn().mockRejectedValue(opts.resolveError)
87
+ : vi
88
+ .fn()
89
+ .mockResolvedValueOnce({ baseSha: 'b'.repeat(40), headSha: preMergeHead })
90
+ .mockResolvedValue({ baseSha: 'b'.repeat(40), headSha: postMergeHead });
70
91
  const git = {
71
92
  pull: opts.pullError
72
93
  ? vi.fn().mockRejectedValue(opts.pullError)
73
94
  : vi.fn().mockResolvedValue({ treeChanged: false }),
74
95
  mergeFromOrigin: vi.fn().mockResolvedValue(opts.merge ?? { kind: 'clean', alreadyUpToDate: false }),
75
96
  push: vi.fn().mockResolvedValue(undefined),
97
+ hasUnpushedCommits: opts.unpushedError
98
+ ? vi.fn().mockRejectedValue(opts.unpushedError)
99
+ : vi.fn().mockResolvedValue(opts.unpushed ?? true),
76
100
  pathsChangedBetween: opts.changedPathsError
77
101
  ? vi.fn().mockRejectedValue(opts.changedPathsError)
78
102
  : vi.fn().mockResolvedValue(opts.changedPaths ?? []),
79
- resolvePrShas: opts.resolveError
80
- ? vi.fn().mockRejectedValue(opts.resolveError)
81
- : vi.fn().mockResolvedValue({
82
- baseSha: 'b'.repeat(40),
83
- headSha: opts.publishedHead ?? HEAD,
84
- }),
103
+ resolvePrShas,
85
104
  };
86
- const refreshed = detail({ behind: false, headSha: MERGED_HEAD });
105
+ const refreshed = detail({ behind: false, needsUpdate: false, headSha: postMergeHead });
106
+ // Exactly two reads per update: the pre-flight (authority, branch names)
107
+ // and the one that goes back. A third would be the cost this ticket removed.
87
108
  const getPrDetail = vi
88
109
  .fn()
89
110
  .mockResolvedValueOnce(opts.first === undefined ? detail() : opts.first)
90
- .mockResolvedValueOnce(refreshed)
91
- .mockResolvedValue(opts.withApprovals ?? refreshed);
111
+ .mockResolvedValue(refreshed);
92
112
  const prs = { getPrDetail, invalidateDetailCache: vi.fn() };
93
113
  const pendingCommits = { enqueueIfAbsent: vi.fn().mockResolvedValue(true) };
94
114
  const reviewWorkflow = {
@@ -111,14 +131,19 @@ function harness(opts: {
111
131
 
112
132
  describe('WorkflowService.updateFromTarget', () => {
113
133
  it('merges the target into the proposal, pushes, and returns the refreshed detail', async () => {
114
- const h = harness({});
115
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(h.refreshed);
134
+ const h = harness({ changedPaths: ['Sales/Deal.md'] });
135
+ await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toEqual({
136
+ ...h.refreshed,
137
+ updatedPaths: ['Sales/Deal.md'],
138
+ });
116
139
 
117
- // The permission read is computed for THIS caller.
140
+ // The permission read is computed for THIS caller — and asks for no
141
+ // patches: all it decides is authority, state and the two branch names.
118
142
  expect(h.prs.getPrDetail).toHaveBeenNthCalledWith(1, 7, {
119
143
  fresh: true,
120
144
  workspaceId: WS,
121
145
  viewerEmail: USER.email,
146
+ patches: false,
122
147
  });
123
148
  expect(h.git.mergeFromOrigin).toHaveBeenCalledWith(WS, 'alice/deal', 'current-company-state', USER);
124
149
  expect(h.git.push).toHaveBeenCalledWith(WS, USER);
@@ -126,6 +151,29 @@ describe('WorkflowService.updateFromTarget', () => {
126
151
  expect(h.refreshed.behind).toBe(false);
127
152
  });
128
153
 
154
+ it('costs ONE detail read after the merge', async () => {
155
+ // The whole point of the ticket on the server side. Two reads total: the
156
+ // pre-flight that decides authority, and the one that goes back. The
157
+ // carry-forward runs BEFORE that second read rather than correcting it
158
+ // with a third.
159
+ const h = harness({ changedPaths: ['Sales/Deal.md'], carried: 2 });
160
+ await h.svc.updateFromTarget(WS, USER, 7);
161
+ expect(h.prs.getPrDetail).toHaveBeenCalledTimes(2);
162
+ const carriedAt = h.reviewWorkflow.carryApprovalsForward.mock.invocationCallOrder[0];
163
+ const lastReadAt = h.prs.getPrDetail.mock.invocationCallOrder[1];
164
+ expect(carriedAt).toBeLessThan(lastReadAt);
165
+ });
166
+
167
+ it('replays the pull with --rebase-merges, so an unpushed merge stays a merge', async () => {
168
+ // A plain rebase flattens a merge commit origin has not seen into
169
+ // cherry-picks of the target's commits, which drops the target as a
170
+ // parent — and the request never contains the target's head however many
171
+ // times the update runs.
172
+ const h = harness({});
173
+ await h.svc.updateFromTarget(WS, USER, 7);
174
+ expect(h.git.pull).toHaveBeenCalledWith(WS, { preserveMerges: true });
175
+ });
176
+
129
177
  it('refuses a viewer who is neither the author nor able to apply — nothing is merged', async () => {
130
178
  const h = harness({ first: detail({ viewerCanUpdate: false }) });
131
179
  const err = await h.svc.updateFromTarget(WS, USER, 7).catch((e: unknown) => e);
@@ -157,13 +205,54 @@ describe('WorkflowService.updateFromTarget', () => {
157
205
  expect(h.git.push).not.toHaveBeenCalled();
158
206
  });
159
207
 
160
- it('an already up-to-date merge pushes nothing but still returns the refreshed detail', async () => {
161
- const h = harness({ merge: { kind: 'clean', alreadyUpToDate: true } });
162
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(h.refreshed);
208
+ it('an already up-to-date merge with nothing unpushed pushes nothing', async () => {
209
+ const h = harness({
210
+ merge: { kind: 'clean', alreadyUpToDate: true },
211
+ unpushed: false,
212
+ headAfterMerge: HEAD,
213
+ });
214
+ await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toEqual({
215
+ ...h.refreshed,
216
+ updatedPaths: [],
217
+ });
163
218
  expect(h.git.push).not.toHaveBeenCalled();
164
219
  expect(h.prs.invalidateDetailCache).toHaveBeenCalledWith(7);
165
220
  });
166
221
 
222
+ it('retries a push that failed last time, even though this merge has nothing to do', async () => {
223
+ // The loop this ticket closes. An earlier update merged the target in and
224
+ // its push failed, so the merge is sitting in the clone. This update's
225
+ // merge is therefore ALREADY up to date — and the old gate, which pushed
226
+ // only when the merge authored a commit, skipped the push and left the
227
+ // request behind on the remote for the next open to find.
228
+ const h = harness({
229
+ merge: { kind: 'clean', alreadyUpToDate: true },
230
+ unpushed: true,
231
+ publishedHead: HEAD,
232
+ headAfterMerge: MERGED_HEAD,
233
+ changedPaths: ['Sales/Deal.md'],
234
+ });
235
+ await h.svc.updateFromTarget(WS, USER, 7);
236
+ expect(h.git.hasUnpushedCommits).toHaveBeenCalledWith(WS);
237
+ expect(h.git.push).toHaveBeenCalledWith(WS, USER);
238
+ });
239
+
240
+ it('falls back to "did this merge author a commit" when git cannot say what is unpushed', async () => {
241
+ // Never "push anyway": a push on a clone with nothing to push is a wasted
242
+ // round trip on every update.
243
+ const clean = harness({ unpushedError: new Error('git exploded') });
244
+ await clean.svc.updateFromTarget(WS, USER, 7);
245
+ expect(clean.git.push).toHaveBeenCalledWith(WS, USER);
246
+
247
+ const nothingToDo = harness({
248
+ unpushedError: new Error('git exploded'),
249
+ merge: { kind: 'clean', alreadyUpToDate: true },
250
+ headAfterMerge: HEAD,
251
+ });
252
+ await nothingToDo.svc.updateFromTarget(WS, USER, 7);
253
+ expect(nothingToDo.git.push).not.toHaveBeenCalled();
254
+ });
255
+
167
256
  it('a rebase conflict refreshing the checkout queues recovery for the saves, then refuses', async () => {
168
257
  const conflict = new PullRebaseConflictError('alice/deal', ['Sales/Deal.md'], 'rebase stopped');
169
258
  const h = harness({ pullError: conflict });
@@ -183,15 +272,13 @@ describe('WorkflowService.updateFromTarget', () => {
183
272
  });
184
273
 
185
274
  describe('WorkflowService.updateFromTarget: the approvals the merge did not touch', () => {
186
- it('carries them onto the new head, and re-reads the detail so the gate sees them', async () => {
187
- const withApprovals = detail({ behind: false, headSha: MERGED_HEAD, mergeableInBevel: true });
188
- const h = harness({
189
- changedPaths: ['Sales/Deal.md'],
190
- carried: 2,
191
- withApprovals,
192
- });
275
+ it('carries them onto the new head, before the one detail read that goes back', async () => {
276
+ const h = harness({ changedPaths: ['Sales/Deal.md'], carried: 2 });
193
277
 
194
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(withApprovals);
278
+ await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toEqual({
279
+ ...h.refreshed,
280
+ updatedPaths: ['Sales/Deal.md'],
281
+ });
195
282
 
196
283
  // What moved is git's answer between the two heads — never a guess, and
197
284
  // never the request's own three-dot diff against the target.
@@ -202,23 +289,33 @@ describe('WorkflowService.updateFromTarget: the approvals the merge did not touc
202
289
  MERGED_HEAD,
203
290
  ['Sales/Deal.md'],
204
291
  );
205
- // The first refreshed detail was assembled before the rows existed, so it
206
- // is not the one that goes back.
207
- expect(h.prs.getPrDetail).toHaveBeenCalledTimes(3);
208
292
  });
209
293
 
210
- it('does not re-read the detail when nothing carried', async () => {
211
- const h = harness({ changedPaths: ['Sales/Deal.md'], carried: 0 });
212
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(h.refreshed);
213
- expect(h.reviewWorkflow.carryApprovalsForward).toHaveBeenCalledTimes(1);
214
- expect(h.prs.getPrDetail).toHaveBeenCalledTimes(2);
294
+ it('answers the same list as updatedPaths — carried and re-read cannot disagree', async () => {
295
+ // A file whose approval was carried (so: untouched by the merge) that the
296
+ // dialog then re-read would be the dialog discarding content the server
297
+ // just called unchanged.
298
+ const h = harness({ changedPaths: ['Sales/Deal.md', 'Sales/Terms.md'], carried: 1 });
299
+ const result = await h.svc.updateFromTarget(WS, USER, 7);
300
+ expect(result.updatedPaths).toEqual(['Sales/Deal.md', 'Sales/Terms.md']);
301
+ expect(h.reviewWorkflow.carryApprovalsForward).toHaveBeenCalledWith(
302
+ 7,
303
+ HEAD,
304
+ MERGED_HEAD,
305
+ result.updatedPaths,
306
+ );
215
307
  });
216
308
 
217
- it('touches nothing when the merge had nothing to do', async () => {
218
- const h = harness({ merge: { kind: 'clean', alreadyUpToDate: true } });
219
- await h.svc.updateFromTarget(WS, USER, 7);
309
+ it('touches nothing when the head did not move', async () => {
310
+ const h = harness({
311
+ merge: { kind: 'clean', alreadyUpToDate: true },
312
+ unpushed: false,
313
+ headAfterMerge: HEAD,
314
+ });
315
+ const result = await h.svc.updateFromTarget(WS, USER, 7);
220
316
  expect(h.git.pathsChangedBetween).not.toHaveBeenCalled();
221
317
  expect(h.reviewWorkflow.carryApprovalsForward).not.toHaveBeenCalled();
318
+ expect(result.updatedPaths).toEqual([]);
222
319
  });
223
320
 
224
321
  it('touches nothing when the merge conflicted — the rows describe a head that still stands', async () => {
@@ -250,10 +347,12 @@ describe('WorkflowService.updateFromTarget: the approvals the merge did not touc
250
347
  it('falls back to the head the detail reported when that head cannot be resolved', async () => {
251
348
  // Bookkeeping never costs the update: an unresolvable ref leaves the
252
349
  // carry-forward working off the detail's head, exactly as it did before.
350
+ // Both resolves fail, so the head reads as unmoved from the detail's own
351
+ // — there is no pair of shas to diff, and the update still lands.
253
352
  const h = harness({ resolveError: new Error('no such ref'), carried: 1 });
254
353
  await h.svc.updateFromTarget(WS, USER, 7);
255
354
  expect(h.git.push).toHaveBeenCalledWith(WS, USER);
256
- expect(h.reviewWorkflow.carryApprovalsForward).toHaveBeenCalledWith(7, HEAD, MERGED_HEAD, []);
355
+ expect(h.reviewWorkflow.carryApprovalsForward).not.toHaveBeenCalled();
257
356
  });
258
357
 
259
358
  it('carries nothing when there is no head to compare at all', async () => {
@@ -266,7 +365,10 @@ describe('WorkflowService.updateFromTarget: the approvals the merge did not touc
266
365
  resolveError: new Error('no such ref'),
267
366
  carried: 1,
268
367
  });
269
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(h.refreshed);
368
+ await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toEqual({
369
+ ...h.refreshed,
370
+ updatedPaths: [],
371
+ });
270
372
  expect(h.git.push).toHaveBeenCalledWith(WS, USER);
271
373
  expect(h.git.pathsChangedBetween).not.toHaveBeenCalled();
272
374
  expect(h.reviewWorkflow.carryApprovalsForward).not.toHaveBeenCalled();
@@ -276,7 +378,11 @@ describe('WorkflowService.updateFromTarget: the approvals the merge did not touc
276
378
  // A request that IS up to date is worth more than the carry-forward: the
277
379
  // approvals simply go stale, exactly as they did before this existed.
278
380
  const h = harness({ changedPathsError: new Error('git exploded') });
279
- await expect(h.svc.updateFromTarget(WS, USER, 7)).resolves.toBe(h.refreshed);
381
+ const result = await h.svc.updateFromTarget(WS, USER, 7);
382
+ expect(result).toMatchObject({ number: 7, behind: false });
280
383
  expect(h.reviewWorkflow.carryApprovalsForward).not.toHaveBeenCalled();
384
+ // Which files moved is unknown, so every file the request had is named:
385
+ // slow beats a dialog that goes on presenting pre-merge text as current.
386
+ expect(result.updatedPaths).toEqual(['Sales/Deal.md']);
281
387
  });
282
388
  });