@bevel-software/platform-core-backend 0.34.0 → 0.36.1

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 (908) hide show
  1. package/agent-guide/access-control.md +146 -300
  2. package/agent-guide/conventions.md +23 -25
  3. package/agent-guide/directory-structure.md +72 -120
  4. package/agent-guide/drafts-and-change-requests.md +92 -0
  5. package/agent-guide/for-git-users.md +47 -0
  6. package/agent-guide/glossary.md +36 -0
  7. package/agent-guide/how-agents-find-things.md +49 -0
  8. package/agent-guide/html-views.md +16 -11
  9. package/agent-guide/new-knowledge-base.md +9 -8
  10. package/agent-guide/onboarding.md +111 -0
  11. package/agent-guide/skills.md +67 -42
  12. package/agent-guide/start-here.md +66 -0
  13. package/agent-guide/tool-manuals.md +133 -154
  14. package/agent-guide/tools.md +67 -0
  15. package/agent-guide/where-a-new-file-goes.md +45 -28
  16. package/dist/core/catalog-cache-invalidation.d.ts +2 -2
  17. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  18. package/dist/core/compose-embed.d.ts +2 -2
  19. package/dist/core/compose-embed.d.ts.map +1 -1
  20. package/dist/core/compose-embed.js +1 -1
  21. package/dist/core/compose-embed.js.map +1 -1
  22. package/dist/core/core-ports.d.ts +19 -4
  23. package/dist/core/core-ports.d.ts.map +1 -1
  24. package/dist/core/core-ports.js.map +1 -1
  25. package/dist/core/create-core-server.d.ts.map +1 -1
  26. package/dist/core/create-core-server.js +27 -25
  27. package/dist/core/create-core-server.js.map +1 -1
  28. package/dist/core/create-core-services.d.ts +25 -13
  29. package/dist/core/create-core-services.d.ts.map +1 -1
  30. package/dist/core/create-core-services.js +75 -60
  31. package/dist/core/create-core-services.js.map +1 -1
  32. package/dist/core/lifecycle.d.ts +4 -4
  33. package/dist/core/lifecycle.d.ts.map +1 -1
  34. package/dist/core/lifecycle.js +5 -5
  35. package/dist/core/lifecycle.js.map +1 -1
  36. package/dist/core-config.d.ts +2 -2
  37. package/dist/core-config.js +3 -3
  38. package/dist/core-config.js.map +1 -1
  39. package/dist/index.d.ts +4 -4
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +3 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/access/access-control.interface.d.ts +22 -0
  44. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  45. package/dist/modules/access/access-control.service.d.ts +3 -1
  46. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  47. package/dist/modules/access/access-control.service.js +76 -1
  48. package/dist/modules/access/access-control.service.js.map +1 -1
  49. package/dist/modules/access/access-declarations.d.ts +2 -2
  50. package/dist/modules/access/access-declarations.d.ts.map +1 -1
  51. package/dist/modules/access/access-declarations.js +2 -2
  52. package/dist/modules/access/access-declarations.js.map +1 -1
  53. package/dist/modules/access/access-mutation.service.d.ts +2 -2
  54. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  55. package/dist/modules/access/access-mutation.service.js +2 -2
  56. package/dist/modules/access/access-mutation.service.js.map +1 -1
  57. package/dist/modules/access/access-requests.routes.d.ts +2 -2
  58. package/dist/modules/access/access-requests.routes.d.ts.map +1 -1
  59. package/dist/modules/access/access-requests.routes.js +5 -5
  60. package/dist/modules/access/access-requests.routes.js.map +1 -1
  61. package/dist/modules/access/access-requests.service.d.ts +2 -2
  62. package/dist/modules/access/access-requests.service.d.ts.map +1 -1
  63. package/dist/modules/access/access-requests.service.js +11 -11
  64. package/dist/modules/access/access-requests.service.js.map +1 -1
  65. package/dist/modules/access/access.routes.d.ts +2 -2
  66. package/dist/modules/access/access.routes.d.ts.map +1 -1
  67. package/dist/modules/access/access.routes.js +32 -13
  68. package/dist/modules/access/access.routes.js.map +1 -1
  69. package/dist/modules/access/admin-locked-commit.d.ts +5 -5
  70. package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
  71. package/dist/modules/access/admin-locked-commit.js +7 -7
  72. package/dist/modules/access/admin-locked-commit.js.map +1 -1
  73. package/dist/modules/access/admin-route-helpers.d.ts +1 -1
  74. package/dist/modules/access/admin-route-helpers.js +5 -5
  75. package/dist/modules/access/admin-route-helpers.js.map +1 -1
  76. package/dist/modules/access/change-read-gate.d.ts +1 -1
  77. package/dist/modules/access/change-read-gate.js +1 -1
  78. package/dist/modules/access/groups-admin.service.d.ts +7 -7
  79. package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
  80. package/dist/modules/access/groups-admin.service.js +6 -6
  81. package/dist/modules/access/groups-admin.service.js.map +1 -1
  82. package/dist/modules/access/index.d.ts +1 -1
  83. package/dist/modules/access/index.d.ts.map +1 -1
  84. package/dist/modules/access/reference-scan.d.ts +1 -1
  85. package/dist/modules/access/roles-admin.service.d.ts +7 -7
  86. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  87. package/dist/modules/access/roles-admin.service.js +8 -8
  88. package/dist/modules/access/roles-admin.service.js.map +1 -1
  89. package/dist/modules/access/synced-groups-committer.d.ts +4 -4
  90. package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
  91. package/dist/modules/access/synced-groups-committer.js +7 -7
  92. package/dist/modules/access/synced-groups-committer.js.map +1 -1
  93. package/dist/modules/access/user-access-removal.service.d.ts +6 -6
  94. package/dist/modules/access/user-access-removal.service.d.ts.map +1 -1
  95. package/dist/modules/access/user-access-removal.service.js +4 -4
  96. package/dist/modules/access/user-access-removal.service.js.map +1 -1
  97. package/dist/modules/access-model/access-errors.d.ts +4 -4
  98. package/dist/modules/access-model/access-errors.d.ts.map +1 -1
  99. package/dist/modules/access-model/access-errors.js +4 -4
  100. package/dist/modules/access-model/access-errors.js.map +1 -1
  101. package/dist/modules/access-model/access-grammar.d.ts +1 -1
  102. package/dist/modules/access-model/access-grammar.js +4 -4
  103. package/dist/modules/access-model/change-gate.d.ts +1 -1
  104. package/dist/modules/access-model/change-gate.js +1 -1
  105. package/dist/modules/access-model/roles-yaml-guard.d.ts +4 -4
  106. package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
  107. package/dist/modules/access-model/roles-yaml-guard.js +4 -4
  108. package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
  109. package/dist/modules/agent-guide/agent-guide.d.ts +63 -18
  110. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  111. package/dist/modules/agent-guide/agent-guide.js +144 -30
  112. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  113. package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
  114. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
  115. package/dist/modules/agent-guide/agent-guide.tools.js +45 -31
  116. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
  117. package/dist/modules/agent-guide/guide-budget.d.ts +51 -0
  118. package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
  119. package/dist/modules/agent-guide/guide-budget.js +71 -0
  120. package/dist/modules/agent-guide/guide-budget.js.map +1 -0
  121. package/dist/modules/agent-guide/index.d.ts +2 -1
  122. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  123. package/dist/modules/agent-guide/index.js +2 -1
  124. package/dist/modules/agent-guide/index.js.map +1 -1
  125. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +12 -5
  126. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
  127. package/dist/modules/agent-instructions/agent-instructions.routes.js +13 -6
  128. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
  129. package/dist/modules/agent-instructions/compose.d.ts +15 -8
  130. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  131. package/dist/modules/agent-instructions/compose.js +28 -10
  132. package/dist/modules/agent-instructions/compose.js.map +1 -1
  133. package/dist/modules/agent-instructions/shared-file-rules.d.ts +14 -6
  134. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  135. package/dist/modules/agent-instructions/shared-file-rules.js +60 -18
  136. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  137. package/dist/modules/auth/account-erasure.service.d.ts +36 -7
  138. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
  139. package/dist/modules/auth/account-erasure.service.js +30 -10
  140. package/dist/modules/auth/account-erasure.service.js.map +1 -1
  141. package/dist/modules/auth/account-hooks.d.ts +20 -0
  142. package/dist/modules/auth/account-hooks.d.ts.map +1 -1
  143. package/dist/modules/auth/account-hooks.js +10 -0
  144. package/dist/modules/auth/account-hooks.js.map +1 -1
  145. package/dist/modules/auth/account-switch-lock.js +2 -2
  146. package/dist/modules/auth/account-switch-lock.js.map +1 -1
  147. package/dist/modules/auth/account.routes.d.ts +7 -2
  148. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  149. package/dist/modules/auth/account.routes.js +23 -4
  150. package/dist/modules/auth/account.routes.js.map +1 -1
  151. package/dist/modules/auth/auth.service.d.ts +1 -1
  152. package/dist/modules/auth/auth.service.js +4 -4
  153. package/dist/modules/auth/auth.service.js.map +1 -1
  154. package/dist/modules/auth/index.d.ts +1 -0
  155. package/dist/modules/auth/index.d.ts.map +1 -1
  156. package/dist/modules/code-mode/code-mode.tool.js +2 -2
  157. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  158. package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
  159. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  160. package/dist/modules/connection-probe/connection-probe.service.js +1 -38
  161. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  162. package/dist/modules/database/core-schema.js +2 -2
  163. package/dist/modules/database/core-schema.js.map +1 -1
  164. package/dist/modules/diff/diff.routes.d.ts +3 -3
  165. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  166. package/dist/modules/diff/diff.routes.js +7 -7
  167. package/dist/modules/diff/diff.routes.js.map +1 -1
  168. package/dist/modules/embed/embed.routes.js +1 -1
  169. package/dist/modules/embed/embed.routes.js.map +1 -1
  170. package/dist/modules/embed/embed.service.d.ts +4 -4
  171. package/dist/modules/embed/embed.service.d.ts.map +1 -1
  172. package/dist/modules/embed/embed.service.js +18 -18
  173. package/dist/modules/embed/embed.service.js.map +1 -1
  174. package/dist/modules/kb-fs/branch-name.js +8 -8
  175. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  176. package/dist/modules/kb-fs/file-change-notifier.d.ts +2 -2
  177. package/dist/modules/kb-fs/locking-filesystem.d.ts +9 -9
  178. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  179. package/dist/modules/kb-fs/locking-filesystem.js +27 -27
  180. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  181. package/dist/modules/kb-fs/repo-path.js +5 -5
  182. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  183. package/dist/modules/kb-fs/tmp-ref.d.ts +2 -2
  184. package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -1
  185. package/dist/modules/kb-fs/tmp-ref.js +2 -2
  186. package/dist/modules/kb-fs/tmp-ref.js.map +1 -1
  187. package/dist/modules/kb-sync/index.d.ts +1 -1
  188. package/dist/modules/kb-sync/index.d.ts.map +1 -1
  189. package/dist/modules/kb-sync/kb-sync.interface.d.ts +3 -3
  190. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
  191. package/dist/modules/kb-sync/kb-sync.service.d.ts +3 -3
  192. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  193. package/dist/modules/kb-sync/kb-sync.service.js +10 -10
  194. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  195. package/dist/modules/mcp/mcp.service.d.ts +11 -4
  196. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  197. package/dist/modules/mcp/mcp.service.js +80 -53
  198. package/dist/modules/mcp/mcp.service.js.map +1 -1
  199. package/dist/modules/onboarding/onboarding.contract.d.ts +1 -1
  200. package/dist/modules/onboarding/onboarding.routes.js +2 -2
  201. package/dist/modules/onboarding/onboarding.routes.js.map +1 -1
  202. package/dist/modules/onboarding/starter-pack.service.d.ts +4 -4
  203. package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -1
  204. package/dist/modules/onboarding/starter-pack.service.js +4 -4
  205. package/dist/modules/onboarding/starter-pack.service.js.map +1 -1
  206. package/dist/modules/plugins/join-request-jobs.service.d.ts +2 -2
  207. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  208. package/dist/modules/plugins/join-request-jobs.service.js +7 -7
  209. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  210. package/dist/modules/plugins/join-requests.service.d.ts +3 -3
  211. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  212. package/dist/modules/plugins/join-requests.service.js +5 -5
  213. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  214. package/dist/modules/plugins/plugin-rename.service.js +1 -1
  215. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  216. package/dist/modules/plugins/plugins.routes.d.ts +2 -2
  217. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  218. package/dist/modules/plugins/plugins.routes.js +8 -8
  219. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  220. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +87 -2
  221. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  222. package/dist/modules/secrets-vault/db-secrets-vault.service.js +635 -74
  223. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  224. package/dist/modules/secrets-vault/index.d.ts +3 -2
  225. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  226. package/dist/modules/secrets-vault/index.js +2 -1
  227. package/dist/modules/secrets-vault/index.js.map +1 -1
  228. package/dist/modules/secrets-vault/managed-sign-in-erasure.d.ts +36 -0
  229. package/dist/modules/secrets-vault/managed-sign-in-erasure.d.ts.map +1 -0
  230. package/dist/modules/secrets-vault/managed-sign-in-erasure.js +37 -0
  231. package/dist/modules/secrets-vault/managed-sign-in-erasure.js.map +1 -0
  232. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +205 -11
  233. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  234. package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
  235. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  236. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +7 -1
  237. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  238. package/dist/modules/secrets-vault/secrets-vault.routes.js +206 -45
  239. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  240. package/dist/modules/settings/connection-check.js +1 -1
  241. package/dist/modules/settings/connection-check.js.map +1 -1
  242. package/dist/modules/settings/deployment-settings.service.js +1 -1
  243. package/dist/modules/settings/git-root-folders.js +1 -1
  244. package/dist/modules/settings/git-root-folders.js.map +1 -1
  245. package/dist/modules/settings/oidc-check.d.ts.map +1 -1
  246. package/dist/modules/settings/oidc-check.js +5 -17
  247. package/dist/modules/settings/oidc-check.js.map +1 -1
  248. package/dist/modules/settings/setup.routes.d.ts +1 -1
  249. package/dist/modules/settings/setup.routes.js +1 -1
  250. package/dist/modules/skills/pending-skills.service.d.ts +3 -3
  251. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  252. package/dist/modules/skills/pending-skills.service.js +4 -4
  253. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  254. package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -2
  255. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  256. package/dist/modules/skills/skill-access-requests.routes.js +5 -5
  257. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  258. package/dist/modules/skills/skills.tools.js +1 -1
  259. package/dist/modules/skills/skills.tools.js.map +1 -1
  260. package/dist/modules/tool-helpers/tool-context.d.ts +4 -4
  261. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  262. package/dist/modules/tool-helpers/tool-context.js +2 -2
  263. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  264. package/dist/modules/tool-helpers/tool-def.js +1 -1
  265. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  266. package/dist/modules/tool-helpers/tool-handler.js +2 -2
  267. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  268. package/dist/modules/tool-helpers/tool.contract.d.ts +4 -4
  269. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  270. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
  271. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  272. package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
  273. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  274. package/dist/modules/tool-manuals/mcp-server-edit.service.js +1 -1
  275. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  276. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -3
  277. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  278. package/dist/modules/tool-manuals/pending-tools.service.js +4 -4
  279. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  280. package/dist/modules/tool-manuals/tool-delete.service.js +1 -1
  281. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  282. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +70 -3
  283. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  284. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +18 -8
  285. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  286. package/dist/modules/tool-manuals/tool-manuals.service.js +166 -49
  287. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  288. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +11 -2
  289. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  290. package/dist/modules/tool-manuals/tool-manuals.tools.js +66 -24
  291. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  292. package/dist/modules/version-control/agent-tools/change-request-read-shape.d.ts.map +1 -0
  293. package/dist/modules/version-control/agent-tools/change-request-read-shape.js.map +1 -0
  294. package/dist/modules/version-control/agent-tools/change-request-read.tools.d.ts.map +1 -0
  295. package/dist/modules/{workflow → version-control}/agent-tools/change-request-read.tools.js +2 -2
  296. package/dist/modules/version-control/agent-tools/change-request-read.tools.js.map +1 -0
  297. package/dist/modules/version-control/agent-tools/change-request-summary.d.ts.map +1 -0
  298. package/dist/modules/version-control/agent-tools/change-request-summary.js.map +1 -0
  299. package/dist/modules/{workflow/agent-tools/workflow.tools.d.ts → version-control/agent-tools/version-control.tools.d.ts} +4 -4
  300. package/dist/modules/version-control/agent-tools/version-control.tools.d.ts.map +1 -0
  301. package/dist/modules/{workflow/agent-tools/workflow.tools.js → version-control/agent-tools/version-control.tools.js} +53 -49
  302. package/dist/modules/version-control/agent-tools/version-control.tools.js.map +1 -0
  303. package/dist/modules/{workflow → version-control}/change-request-hooks.d.ts +1 -1
  304. package/dist/modules/version-control/change-request-hooks.d.ts.map +1 -0
  305. package/dist/modules/version-control/change-request-hooks.js.map +1 -0
  306. package/dist/modules/version-control/change-request-review/approval-lock.d.ts.map +1 -0
  307. package/dist/modules/version-control/change-request-review/approval-lock.js.map +1 -0
  308. package/dist/modules/{workflow/review-workflow/review-workflow.interface.d.ts → version-control/change-request-review/change-request-review.interface.d.ts} +2 -2
  309. package/dist/modules/version-control/change-request-review/change-request-review.interface.d.ts.map +1 -0
  310. package/dist/modules/version-control/change-request-review/change-request-review.interface.js +2 -0
  311. package/dist/modules/version-control/change-request-review/change-request-review.interface.js.map +1 -0
  312. package/dist/modules/{workflow/review-workflow/review-workflow.service.d.ts → version-control/change-request-review/change-request-review.service.d.ts} +3 -3
  313. package/dist/modules/version-control/change-request-review/change-request-review.service.d.ts.map +1 -0
  314. package/dist/modules/{workflow/review-workflow/review-workflow.service.js → version-control/change-request-review/change-request-review.service.js} +26 -26
  315. package/dist/modules/version-control/change-request-review/change-request-review.service.js.map +1 -0
  316. package/dist/modules/{workflow → version-control}/event-bus.d.ts +8 -8
  317. package/dist/modules/version-control/event-bus.d.ts.map +1 -0
  318. package/dist/modules/{workflow → version-control}/event-bus.js +4 -4
  319. package/dist/modules/version-control/event-bus.js.map +1 -0
  320. package/dist/modules/{workflow → version-control}/events-delivery.d.ts +4 -4
  321. package/dist/modules/version-control/events-delivery.d.ts.map +1 -0
  322. package/dist/modules/version-control/events-delivery.js.map +1 -0
  323. package/dist/modules/{workflow → version-control}/events.routes.d.ts +4 -4
  324. package/dist/modules/version-control/events.routes.d.ts.map +1 -0
  325. package/dist/modules/version-control/events.routes.js.map +1 -0
  326. package/dist/modules/{workflow → version-control}/file-lock.service.d.ts +2 -2
  327. package/dist/modules/version-control/file-lock.service.d.ts.map +1 -0
  328. package/dist/modules/{workflow → version-control}/file-lock.service.js +6 -6
  329. package/dist/modules/version-control/file-lock.service.js.map +1 -0
  330. package/dist/modules/version-control/git/change-request-link.d.ts.map +1 -0
  331. package/dist/modules/version-control/git/change-request-link.js.map +1 -0
  332. package/dist/modules/version-control/git/git-version.d.ts.map +1 -0
  333. package/dist/modules/version-control/git/git-version.js.map +1 -0
  334. package/dist/modules/{workflow → version-control}/git/git.service.d.ts +8 -8
  335. package/dist/modules/version-control/git/git.service.d.ts.map +1 -0
  336. package/dist/modules/{workflow → version-control}/git/git.service.js +53 -53
  337. package/dist/modules/version-control/git/git.service.js.map +1 -0
  338. package/dist/modules/version-control/git/merge-commit.d.ts.map +1 -0
  339. package/dist/modules/version-control/git/merge-commit.js.map +1 -0
  340. package/dist/modules/version-control/git/node-git-runner.d.ts.map +1 -0
  341. package/dist/modules/{workflow → version-control}/git/node-git-runner.js +1 -1
  342. package/dist/modules/version-control/git/node-git-runner.js.map +1 -0
  343. package/dist/modules/{workflow → version-control}/git/pull-request.service.d.ts +5 -5
  344. package/dist/modules/version-control/git/pull-request.service.d.ts.map +1 -0
  345. package/dist/modules/{workflow → version-control}/git/pull-request.service.js +10 -10
  346. package/dist/modules/version-control/git/pull-request.service.js.map +1 -0
  347. package/dist/modules/{workflow → version-control}/index.d.ts +6 -5
  348. package/dist/modules/version-control/index.d.ts.map +1 -0
  349. package/dist/modules/{workflow → version-control}/index.js +3 -2
  350. package/dist/modules/version-control/index.js.map +1 -0
  351. package/dist/modules/{workflow → version-control}/pending-commits.service.d.ts +2 -2
  352. package/dist/modules/version-control/pending-commits.service.d.ts.map +1 -0
  353. package/dist/modules/{workflow → version-control}/pending-commits.service.js +2 -2
  354. package/dist/modules/version-control/pending-commits.service.js.map +1 -0
  355. package/dist/modules/{workflow → version-control}/pending-commits.worker.d.ts +5 -5
  356. package/dist/modules/version-control/pending-commits.worker.d.ts.map +1 -0
  357. package/dist/modules/{workflow → version-control}/pending-commits.worker.js +3 -3
  358. package/dist/modules/version-control/pending-commits.worker.js.map +1 -0
  359. package/dist/modules/{workflow → version-control}/read-on-branch.d.ts +2 -2
  360. package/dist/modules/version-control/read-on-branch.d.ts.map +1 -0
  361. package/dist/modules/{workflow → version-control}/read-on-branch.js +2 -2
  362. package/dist/modules/version-control/read-on-branch.js.map +1 -0
  363. package/dist/modules/version-control/recovery-bot.d.ts.map +1 -0
  364. package/dist/modules/version-control/recovery-bot.js.map +1 -0
  365. package/dist/modules/version-control/sanitize-error.d.ts.map +1 -0
  366. package/dist/modules/version-control/sanitize-error.js.map +1 -0
  367. package/dist/modules/{workflow/workflow-hooks.d.ts → version-control/version-control-hooks.d.ts} +4 -4
  368. package/dist/modules/version-control/version-control-hooks.d.ts.map +1 -0
  369. package/dist/modules/{workflow/workflow-hooks.js → version-control/version-control-hooks.js} +3 -3
  370. package/dist/modules/version-control/version-control-hooks.js.map +1 -0
  371. package/dist/modules/{workflow/workflow.routes.d.ts → version-control/version-control.routes.d.ts} +6 -5
  372. package/dist/modules/version-control/version-control.routes.d.ts.map +1 -0
  373. package/dist/modules/{workflow/workflow.routes.js → version-control/version-control.routes.js} +61 -60
  374. package/dist/modules/version-control/version-control.routes.js.map +1 -0
  375. package/dist/modules/{workflow/workflow.service.d.ts → version-control/version-control.service.d.ts} +26 -31
  376. package/dist/modules/version-control/version-control.service.d.ts.map +1 -0
  377. package/dist/modules/{workflow/workflow.service.js → version-control/version-control.service.js} +89 -94
  378. package/dist/modules/version-control/version-control.service.js.map +1 -0
  379. package/dist/modules/workspace/agent-access.gate.d.ts +6 -6
  380. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
  381. package/dist/modules/workspace/agent-access.gate.js +2 -2
  382. package/dist/modules/workspace/agent-access.gate.js.map +1 -1
  383. package/dist/modules/workspace/git-internals.middleware.d.ts +1 -1
  384. package/dist/modules/workspace/git-internals.middleware.js +2 -2
  385. package/dist/modules/workspace/kb-repository.js +1 -1
  386. package/dist/modules/workspace/kb-repository.js.map +1 -1
  387. package/dist/modules/workspace/startup/kb-git.d.ts +1 -1
  388. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  389. package/dist/modules/workspace/startup/kb-git.js +1 -1
  390. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  391. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +2 -2
  392. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  393. package/dist/modules/workspace/startup/steps/seed-tree.js +1 -1
  394. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  395. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +2 -2
  396. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  397. package/dist/modules/workspace/startup/steps/template-files.step.js +2 -2
  398. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  399. package/dist/modules/workspace/startup/steps/template-source.d.ts +17 -4
  400. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  401. package/dist/modules/workspace/startup/steps/template-source.js +8 -5
  402. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  403. package/dist/modules/workspace/workspace-tmp.js +3 -3
  404. package/dist/modules/workspace/workspace-tmp.js.map +1 -1
  405. package/dist/modules/workspace/workspace.routes.d.ts +4 -4
  406. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  407. package/dist/modules/workspace/workspace.routes.js +22 -22
  408. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  409. package/dist/modules/workspace/workspace.service.d.ts +6 -6
  410. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  411. package/dist/modules/workspace/workspace.service.js +11 -11
  412. package/dist/modules/workspace/workspace.service.js.map +1 -1
  413. package/dist/modules/workspace/workspace.tools.d.ts +5 -3
  414. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  415. package/dist/modules/workspace/workspace.tools.js +85 -30
  416. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  417. package/dist/shared/canonical-file-identity.d.ts +2 -2
  418. package/dist/shared/canonical-file-identity.js +5 -5
  419. package/dist/shared/canonical-file-identity.js.map +1 -1
  420. package/dist/shared/domain-errors.d.ts +39 -39
  421. package/dist/shared/domain-errors.d.ts.map +1 -1
  422. package/dist/shared/domain-errors.js +44 -44
  423. package/dist/shared/domain-errors.js.map +1 -1
  424. package/dist/shared/git.contract.d.ts +1 -1
  425. package/dist/shared/git.contract.js +1 -1
  426. package/dist/shared/http-errors.d.ts +3 -3
  427. package/dist/shared/http-errors.d.ts.map +1 -1
  428. package/dist/shared/http-errors.js +1 -1
  429. package/dist/shared/http-errors.js.map +1 -1
  430. package/dist/shared/oauth-token-error.d.ts +23 -0
  431. package/dist/shared/oauth-token-error.d.ts.map +1 -0
  432. package/dist/shared/oauth-token-error.js +47 -0
  433. package/dist/shared/oauth-token-error.js.map +1 -0
  434. package/dist/shared/pending-proposals.d.ts +3 -3
  435. package/dist/shared/pending-proposals.d.ts.map +1 -1
  436. package/dist/shared/pending-proposals.js +2 -2
  437. package/dist/shared/pending-proposals.js.map +1 -1
  438. package/dist/shared/read-capped.d.ts +19 -0
  439. package/dist/shared/read-capped.d.ts.map +1 -0
  440. package/dist/shared/read-capped.js +45 -0
  441. package/dist/shared/read-capped.js.map +1 -0
  442. package/dist/tenancy/static-tenant-source.js +1 -1
  443. package/dist/tenancy/static-tenant-source.js.map +1 -1
  444. package/migrations/0012_agent_audit.sql +40 -40
  445. package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -29
  446. package/migrations/meta/0012_snapshot.json +2246 -2246
  447. package/migrations/meta/0013_snapshot.json +2258 -2258
  448. package/package.json +5 -4
  449. package/src/__tests__/account-admin.ts +27 -0
  450. package/src/__tests__/hexis-catalog.ts +144 -0
  451. package/src/__tests__/kb-context.ts +30 -30
  452. package/src/__tests__/kb-layout-config.test.ts +21 -0
  453. package/src/__tests__/open-change-gate.ts +1 -1
  454. package/src/__tests__/real-disk.ts +21 -0
  455. package/src/__tests__/tokens.ts +19 -0
  456. package/src/__tests__/tool-handler.ts +15 -0
  457. package/src/__tests__/tool-manuals.ts +32 -0
  458. package/src/__tests__/tool-wiring.ts +31 -0
  459. package/src/__tests__/workspace-service.ts +1 -1
  460. package/src/core/__tests__/catalog-cache-invalidation.test.ts +3 -3
  461. package/src/core/__tests__/compose-embed.test.ts +1 -1
  462. package/src/core/__tests__/damaged-document.tools.test.ts +3 -3
  463. package/src/core/__tests__/erasure-slices-wired-once.test.ts +46 -0
  464. package/src/core/__tests__/gated-boot.test.ts +2 -2
  465. package/src/core/__tests__/lifecycle.test.ts +9 -9
  466. package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
  467. package/src/core/catalog-cache-invalidation.ts +2 -2
  468. package/src/core/compose-embed.ts +3 -3
  469. package/src/core/core-ports.ts +19 -4
  470. package/src/core/create-core-server.ts +40 -26
  471. package/src/core/create-core-services.ts +92 -66
  472. package/src/core/lifecycle.ts +9 -9
  473. package/src/core-config.ts +3 -3
  474. package/src/index.ts +5 -5
  475. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +4 -4
  476. package/src/modules/access/__tests__/access-own-read-grant.test.ts +14 -14
  477. package/src/modules/access/__tests__/access-requests.routes.test.ts +20 -20
  478. package/src/modules/access/__tests__/access.routes.admins.test.ts +4 -4
  479. package/src/modules/access/__tests__/access.routes.batch.test.ts +4 -4
  480. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +6 -6
  481. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -6
  482. package/src/modules/access/__tests__/access.routes.overrides.test.ts +5 -5
  483. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +6 -6
  484. package/src/modules/access/__tests__/access.routes.prospective.test.ts +54 -14
  485. package/src/modules/access/__tests__/access.routes.revoke.test.ts +6 -6
  486. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +6 -6
  487. package/src/modules/access/__tests__/admin-locked-commit.test.ts +6 -6
  488. package/src/modules/access/__tests__/admin-route-helpers.test.ts +5 -5
  489. package/src/modules/access/__tests__/groups-admin.service.test.ts +27 -27
  490. package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
  491. package/src/modules/access/__tests__/roles-admin.security.test.ts +14 -13
  492. package/src/modules/access/__tests__/roles-admin.service.test.ts +27 -27
  493. package/src/modules/access/__tests__/roles-capabilities.test.ts +19 -19
  494. package/src/modules/access/__tests__/roles.routes.test.ts +6 -6
  495. package/src/modules/access/__tests__/synced-groups-committer.test.ts +13 -13
  496. package/src/modules/access/__tests__/user-access-removal.service.test.ts +29 -29
  497. package/src/modules/access/access-control.interface.ts +26 -0
  498. package/src/modules/access/access-control.service.ts +93 -1
  499. package/src/modules/access/access-declarations.ts +2 -2
  500. package/src/modules/access/access-mutation.service.ts +2 -2
  501. package/src/modules/access/access-requests.routes.ts +7 -7
  502. package/src/modules/access/access-requests.service.ts +13 -13
  503. package/src/modules/access/access.routes.ts +34 -15
  504. package/src/modules/access/admin-locked-commit.ts +11 -11
  505. package/src/modules/access/admin-route-helpers.ts +5 -5
  506. package/src/modules/access/change-read-gate.ts +1 -1
  507. package/src/modules/access/groups-admin.service.ts +7 -7
  508. package/src/modules/access/index.ts +1 -1
  509. package/src/modules/access/reference-scan.ts +1 -1
  510. package/src/modules/access/roles-admin.service.ts +9 -9
  511. package/src/modules/access/synced-groups-committer.ts +11 -11
  512. package/src/modules/access/user-access-removal.service.ts +7 -7
  513. package/src/modules/access-model/__tests__/access-grammar.test.ts +2 -2
  514. package/src/modules/access-model/access-errors.ts +4 -4
  515. package/src/modules/access-model/access-grammar.ts +4 -4
  516. package/src/modules/access-model/change-gate.ts +1 -1
  517. package/src/modules/access-model/roles-yaml-guard.ts +4 -4
  518. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +98 -29
  519. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +39 -12
  520. package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
  521. package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
  522. package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
  523. package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
  524. package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
  525. package/src/modules/agent-guide/agent-guide.tools.ts +49 -31
  526. package/src/modules/agent-guide/agent-guide.ts +165 -32
  527. package/src/modules/agent-guide/guide-budget.ts +77 -0
  528. package/src/modules/agent-guide/index.ts +12 -0
  529. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +4 -5
  530. package/src/modules/agent-instructions/__tests__/compose.test.ts +1 -1
  531. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +117 -11
  532. package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -5
  533. package/src/modules/agent-instructions/compose.ts +28 -9
  534. package/src/modules/agent-instructions/shared-file-rules.ts +62 -18
  535. package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +1 -1
  536. package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -113
  537. package/src/modules/audit/__tests__/event-classifier.test.ts +121 -121
  538. package/src/modules/audit/event-classifier.ts +155 -155
  539. package/src/modules/auth/__tests__/account-deactivation.test.ts +1 -1
  540. package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +4 -4
  541. package/src/modules/auth/__tests__/account-erasure.participants.test.ts +56 -0
  542. package/src/modules/auth/__tests__/account-switch-lock.pg.test.ts +2 -2
  543. package/src/modules/auth/__tests__/account-switch-lock.test.ts +2 -2
  544. package/src/modules/auth/__tests__/account.routes.test.ts +73 -11
  545. package/src/modules/auth/__tests__/auth.service.test.ts +1 -1
  546. package/src/modules/auth/account-erasure.service.ts +27 -9
  547. package/src/modules/auth/account-hooks.ts +34 -0
  548. package/src/modules/auth/account-switch-lock.ts +2 -2
  549. package/src/modules/auth/account.routes.ts +23 -5
  550. package/src/modules/auth/auth.service.ts +4 -4
  551. package/src/modules/auth/index.ts +1 -0
  552. package/src/modules/code-mode/code-mode.tool.ts +2 -2
  553. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
  554. package/src/modules/connection-probe/connection-probe.service.ts +1 -34
  555. package/src/modules/database/__tests__/advisory-lock.test.ts +169 -169
  556. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -32
  557. package/src/modules/database/core-schema.ts +2 -2
  558. package/src/modules/diff/__tests__/diff.routes.read-gate.test.ts +2 -2
  559. package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +12 -12
  560. package/src/modules/diff/diff.routes.ts +8 -8
  561. package/src/modules/embed/__tests__/embed.service.lock.test.ts +7 -7
  562. package/src/modules/embed/__tests__/embed.service.test.ts +42 -42
  563. package/src/modules/embed/__tests__/embed.tools.test.ts +1 -1
  564. package/src/modules/embed/embed.routes.ts +1 -1
  565. package/src/modules/embed/embed.service.ts +17 -17
  566. package/src/modules/kb-fs/__tests__/branch-name.test.ts +2 -2
  567. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +260 -260
  568. package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +34 -34
  569. package/src/modules/kb-fs/__tests__/repo-path.test.ts +8 -8
  570. package/src/modules/kb-fs/branch-name.ts +8 -8
  571. package/src/modules/kb-fs/file-change-notifier.ts +2 -2
  572. package/src/modules/kb-fs/locking-filesystem.ts +30 -30
  573. package/src/modules/kb-fs/repo-path.ts +5 -5
  574. package/src/modules/kb-fs/tmp-ref.ts +3 -3
  575. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +36 -36
  576. package/src/modules/kb-sync/index.ts +1 -1
  577. package/src/modules/kb-sync/kb-sync.interface.ts +3 -3
  578. package/src/modules/kb-sync/kb-sync.service.ts +9 -9
  579. package/src/modules/marketplace/__tests__/github-facade.test.ts +1 -1
  580. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +1 -1
  581. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +4 -2
  582. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +69 -8
  583. package/src/modules/mcp/__tests__/mcp.service.test.ts +91 -13
  584. package/src/modules/mcp/__tests__/read-only-hint.pg.test.ts +194 -0
  585. package/src/modules/mcp/mcp.service.ts +93 -62
  586. package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +55 -55
  587. package/src/modules/onboarding/onboarding.contract.ts +1 -1
  588. package/src/modules/onboarding/onboarding.routes.ts +2 -2
  589. package/src/modules/onboarding/starter-pack.service.ts +6 -6
  590. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +1 -1
  591. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +1 -1
  592. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +43 -43
  593. package/src/modules/plugins/__tests__/join-requests.service.test.ts +19 -19
  594. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +26 -26
  595. package/src/modules/plugins/__tests__/plugins.routes.test.ts +25 -25
  596. package/src/modules/plugins/join-request-jobs.service.ts +10 -10
  597. package/src/modules/plugins/join-requests.service.ts +4 -4
  598. package/src/modules/plugins/plugin-rename.service.ts +1 -1
  599. package/src/modules/plugins/plugins.routes.ts +9 -9
  600. package/src/modules/secrets-vault/__tests__/db-secrets-vault.managed.test.ts +773 -0
  601. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +458 -4
  602. package/src/modules/secrets-vault/__tests__/managed-sign-in.route.test.ts +351 -0
  603. package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
  604. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
  605. package/src/modules/secrets-vault/db-secrets-vault.service.ts +728 -118
  606. package/src/modules/secrets-vault/index.ts +8 -1
  607. package/src/modules/secrets-vault/managed-sign-in-erasure.ts +41 -0
  608. package/src/modules/secrets-vault/secrets-variable-loader.ts +134 -134
  609. package/src/modules/secrets-vault/secrets-vault.contract.ts +213 -11
  610. package/src/modules/secrets-vault/secrets-vault.routes.ts +244 -45
  611. package/src/modules/settings/__tests__/repository-source.test.ts +1 -1
  612. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +1 -1
  613. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +1 -1
  614. package/src/modules/settings/connection-check.ts +1 -1
  615. package/src/modules/settings/deployment-settings.service.ts +1 -1
  616. package/src/modules/settings/git-root-folders.ts +1 -1
  617. package/src/modules/settings/oidc-check.ts +5 -18
  618. package/src/modules/settings/setup.routes.ts +1 -1
  619. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +3 -3
  620. package/src/modules/skills/__tests__/branch-skills.tools.test.ts +1 -1
  621. package/src/modules/skills/__tests__/pending-skills.service.test.ts +8 -8
  622. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +18 -18
  623. package/src/modules/skills/__tests__/skills.service.test.ts +3 -3
  624. package/src/modules/skills/pending-skills.service.ts +3 -3
  625. package/src/modules/skills/skill-access-requests.routes.ts +7 -7
  626. package/src/modules/skills/skills.tools.ts +1 -1
  627. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +22 -14
  628. package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +3 -3
  629. package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -3
  630. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +4 -4
  631. package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
  632. package/src/modules/tool-helpers/__tests__/tools-surface.test.ts +3 -3
  633. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +1 -1
  634. package/src/modules/tool-helpers/tool-context.ts +6 -6
  635. package/src/modules/tool-helpers/tool-def.ts +1 -1
  636. package/src/modules/tool-helpers/tool-handler.ts +2 -2
  637. package/src/modules/tool-helpers/tool.contract.ts +4 -4
  638. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +9 -9
  639. package/src/modules/tool-manuals/__tests__/tool-manuals.managed-sign-in.test.ts +89 -0
  640. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
  641. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +1 -1
  642. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +132 -6
  643. package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
  644. package/src/modules/tool-manuals/mcp-server-edit.service.ts +1 -1
  645. package/src/modules/tool-manuals/pending-tools.service.ts +3 -3
  646. package/src/modules/tool-manuals/tool-delete.service.ts +1 -1
  647. package/src/modules/tool-manuals/tool-manuals.contract.ts +63 -3
  648. package/src/modules/tool-manuals/tool-manuals.service.ts +191 -51
  649. package/src/modules/tool-manuals/tool-manuals.tools.ts +93 -28
  650. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +4 -4
  651. package/src/modules/tool-registry/__tests__/read-only-hints.test.ts +3 -3
  652. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +12 -119
  653. package/src/modules/{workflow → version-control}/__tests__/apply-failure.test.ts +54 -54
  654. package/src/modules/{workflow → version-control}/__tests__/event-bus.test.ts +27 -27
  655. package/src/modules/{workflow → version-control}/__tests__/events-delivery.test.ts +37 -37
  656. package/src/modules/{workflow → version-control}/__tests__/file-lock.service.deletion-gate.test.ts +2 -2
  657. package/src/modules/{workflow → version-control}/__tests__/file-lock.service.path-identity.test.ts +8 -8
  658. package/src/modules/{workflow → version-control}/__tests__/merge-announces-tree.test.ts +11 -11
  659. package/src/modules/{workflow → version-control}/__tests__/pending-commits.worker.concurrency.test.ts +9 -9
  660. package/src/modules/{workflow → version-control}/__tests__/pending-commits.worker.test.ts +25 -25
  661. package/src/modules/{workflow → version-control}/__tests__/preserve-roles-yaml.test.ts +42 -42
  662. package/src/modules/version-control/__tests__/public-surface.test.ts +114 -0
  663. package/src/modules/{workflow/__tests__/workflow.routes.folder-proposals.test.ts → version-control/__tests__/version-control.routes.folder-proposals.test.ts} +18 -18
  664. package/src/modules/{workflow/__tests__/workflow.routes.fork-point-file.test.ts → version-control/__tests__/version-control.routes.fork-point-file.test.ts} +19 -19
  665. package/src/modules/{workflow/__tests__/workflow.routes.history-read-gate.test.ts → version-control/__tests__/version-control.routes.history-read-gate.test.ts} +13 -13
  666. package/src/modules/{workflow/__tests__/workflow.routes.lock-path.test.ts → version-control/__tests__/version-control.routes.lock-path.test.ts} +18 -18
  667. package/src/modules/{workflow/__tests__/workflow.routes.open-change-request.test.ts → version-control/__tests__/version-control.routes.open-change-request.test.ts} +7 -7
  668. package/src/modules/{workflow/__tests__/workflow.routes.push-refused.test.ts → version-control/__tests__/version-control.routes.push-refused.test.ts} +14 -14
  669. package/src/modules/{workflow/__tests__/workflow.service.branch-in-use.test.ts → version-control/__tests__/version-control.service.branch-in-use.test.ts} +6 -6
  670. package/src/modules/{workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts → version-control/__tests__/version-control.service.commitFileWhileLocked.test.ts} +14 -14
  671. package/src/modules/{workflow/__tests__/workflow.service.delete-branch.test.ts → version-control/__tests__/version-control.service.delete-branch.test.ts} +14 -14
  672. package/src/modules/{workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts → version-control/__tests__/version-control.service.deleted-branch-sweep.test.ts} +4 -4
  673. package/src/modules/{workflow/__tests__/workflow.service.facade.test.ts → version-control/__tests__/version-control.service.facade.test.ts} +97 -99
  674. package/src/modules/{workflow/__tests__/workflow.service.folder-proposals.test.ts → version-control/__tests__/version-control.service.folder-proposals.test.ts} +7 -7
  675. package/src/modules/{workflow/__tests__/workflow.service.platform-restore.test.ts → version-control/__tests__/version-control.service.platform-restore.test.ts} +6 -6
  676. package/src/modules/{workflow/__tests__/workflow.service.read-gate.test.ts → version-control/__tests__/version-control.service.read-gate.test.ts} +9 -9
  677. package/src/modules/{workflow/__tests__/workflow.service.releaseLock.test.ts → version-control/__tests__/version-control.service.releaseLock.test.ts} +42 -42
  678. package/src/modules/{workflow/__tests__/workflow.service.repository-replaced.test.ts → version-control/__tests__/version-control.service.repository-replaced.test.ts} +4 -4
  679. package/src/modules/{workflow/__tests__/workflow.service.sync.test.ts → version-control/__tests__/version-control.service.sync.test.ts} +17 -17
  680. package/src/modules/{workflow/__tests__/workflow.service.update-change-request.pg.test.ts → version-control/__tests__/version-control.service.update-change-request.pg.test.ts} +6 -6
  681. package/src/modules/{workflow/__tests__/workflow.service.update-change-request.test.ts → version-control/__tests__/version-control.service.update-change-request.test.ts} +11 -11
  682. package/src/modules/{workflow/__tests__/workflow.service.update-from-target.test.ts → version-control/__tests__/version-control.service.update-from-target.test.ts} +22 -22
  683. package/src/modules/{workflow → version-control}/agent-tools/__tests__/change-request-read.tools.test.ts +3 -3
  684. package/src/modules/{workflow → version-control}/agent-tools/__tests__/update-change-request.parity.test.ts +16 -16
  685. package/src/modules/{workflow/agent-tools/__tests__/workflow.tools.test.ts → version-control/agent-tools/__tests__/version-control.tools.test.ts} +9 -9
  686. package/src/modules/{workflow → version-control}/agent-tools/change-request-read.tools.ts +2 -2
  687. package/src/modules/{workflow/agent-tools/workflow.tools.ts → version-control/agent-tools/version-control.tools.ts} +52 -48
  688. package/src/modules/{workflow → version-control}/change-request-hooks.ts +1 -1
  689. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/approval-states.test.ts +5 -5
  690. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/cancel-pr.test.ts +8 -8
  691. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/carry-approvals-forward.test.ts +6 -6
  692. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/erase-approver.test.ts +5 -5
  693. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/merge-gate.test.ts +3 -3
  694. package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/merge-records-own-commit.test.ts +3 -3
  695. package/src/modules/{workflow/review-workflow/review-workflow.interface.ts → version-control/change-request-review/change-request-review.interface.ts} +1 -1
  696. package/src/modules/{workflow/review-workflow/review-workflow.service.ts → version-control/change-request-review/change-request-review.service.ts} +28 -28
  697. package/src/modules/{workflow → version-control}/event-bus.ts +14 -14
  698. package/src/modules/{workflow → version-control}/events-delivery.ts +11 -11
  699. package/src/modules/{workflow → version-control}/events.routes.ts +5 -5
  700. package/src/modules/{workflow → version-control}/file-lock.service.ts +6 -6
  701. package/src/modules/{workflow → version-control}/git/__tests__/git-test-helpers.ts +3 -3
  702. package/src/modules/{workflow → version-control}/git/__tests__/git.service.accessGating.test.ts +5 -5
  703. package/src/modules/{workflow → version-control}/git/__tests__/git.service.appliedChange.test.ts +4 -4
  704. package/src/modules/{workflow → version-control}/git/__tests__/git.service.changedFilesForPr.test.ts +16 -16
  705. package/src/modules/{workflow → version-control}/git/__tests__/git.service.changedPathsMemo.test.ts +2 -2
  706. package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitChanges.test.ts +3 -3
  707. package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitFile.strayPath.test.ts +10 -10
  708. package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitFile.test.ts +3 -3
  709. package/src/modules/{workflow → version-control}/git/__tests__/git.service.createBranch.test.ts +3 -3
  710. package/src/modules/{workflow → version-control}/git/__tests__/git.service.deleteBranch.test.ts +7 -7
  711. package/src/modules/{workflow → version-control}/git/__tests__/git.service.diffFileAtCommit.test.ts +9 -9
  712. package/src/modules/{workflow → version-control}/git/__tests__/git.service.diffFileBetweenBranches.test.ts +23 -23
  713. package/src/modules/{workflow → version-control}/git/__tests__/git.service.fileBytesAtCommit.test.ts +7 -7
  714. package/src/modules/{workflow → version-control}/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -3
  715. package/src/modules/{workflow → version-control}/git/__tests__/git.service.forkPoint.test.ts +2 -2
  716. package/src/modules/{workflow → version-control}/git/__tests__/git.service.history-guards.test.ts +2 -2
  717. package/src/modules/{workflow → version-control}/git/__tests__/git.service.list-branches.test.ts +12 -12
  718. package/src/modules/{workflow → version-control}/git/__tests__/git.service.listTrackedFiles.test.ts +2 -2
  719. package/src/modules/{workflow → version-control}/git/__tests__/git.service.logForFile.test.ts +6 -6
  720. package/src/modules/{workflow → version-control}/git/__tests__/git.service.mergeChangeRequest.test.ts +20 -20
  721. package/src/modules/{workflow → version-control}/git/__tests__/git.service.mergeIntoBranch.test.ts +16 -16
  722. package/src/modules/{workflow → version-control}/git/__tests__/git.service.pendingChanges.test.ts +2 -2
  723. package/src/modules/{workflow → version-control}/git/__tests__/git.service.postMergePull.test.ts +5 -5
  724. package/src/modules/{workflow → version-control}/git/__tests__/git.service.prFetchFailure.test.ts +2 -2
  725. package/src/modules/{workflow → version-control}/git/__tests__/git.service.pull.test.ts +12 -12
  726. package/src/modules/{workflow → version-control}/git/__tests__/git.service.readFileAtRef.test.ts +3 -3
  727. package/src/modules/{workflow → version-control}/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -2
  728. package/src/modules/{workflow → version-control}/git/__tests__/git.service.status.test.ts +4 -4
  729. package/src/modules/{workflow → version-control}/git/__tests__/git.service.syncFromRemote.test.ts +8 -8
  730. package/src/modules/{workflow → version-control}/git/__tests__/git.service.worktrees.test.ts +4 -4
  731. package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.list-fetch.test.ts +2 -2
  732. package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.test.ts +6 -6
  733. package/src/modules/{workflow → version-control}/git/git.service.ts +57 -57
  734. package/src/modules/{workflow → version-control}/git/node-git-runner.ts +1 -1
  735. package/src/modules/{workflow → version-control}/git/pull-request.service.ts +12 -12
  736. package/src/modules/{workflow → version-control}/index.ts +6 -5
  737. package/src/modules/{workflow → version-control}/pending-commits.service.ts +2 -2
  738. package/src/modules/{workflow → version-control}/pending-commits.worker.ts +8 -8
  739. package/src/modules/{workflow → version-control}/read-on-branch.ts +2 -2
  740. package/src/modules/{workflow/workflow-hooks.ts → version-control/version-control-hooks.ts} +3 -3
  741. package/src/modules/{workflow/workflow.routes.ts → version-control/version-control.routes.ts} +64 -63
  742. package/src/modules/{workflow/workflow.service.ts → version-control/version-control.service.ts} +92 -97
  743. package/src/modules/workspace/__tests__/agent-access.gate.test.ts +8 -8
  744. package/src/modules/workspace/__tests__/agent-downloads.test.ts +13 -16
  745. package/src/modules/workspace/__tests__/agent-uploads.test.ts +11 -12
  746. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +11 -16
  747. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -3
  748. package/src/modules/workspace/__tests__/git-internals.security.test.ts +27 -27
  749. package/src/modules/workspace/__tests__/kb-repository.test.ts +1 -1
  750. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
  751. package/src/modules/workspace/__tests__/start-session.default-branch.test.ts +152 -0
  752. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +6 -6
  753. package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +13 -14
  754. package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +6 -6
  755. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +4 -4
  756. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +6 -6
  757. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +8 -8
  758. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +4 -4
  759. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +6 -6
  760. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +6 -6
  761. package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +4 -4
  762. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +6 -6
  763. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +7 -7
  764. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +13 -13
  765. package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +1 -1
  766. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +1 -1
  767. package/src/modules/workspace/__tests__/workspace.service.worktrees.test.ts +1 -1
  768. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +12 -14
  769. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +5 -5
  770. package/src/modules/workspace/__tests__/workspace.tools.test.ts +182 -75
  771. package/src/modules/workspace/agent-access.gate.ts +6 -6
  772. package/src/modules/workspace/git-internals.middleware.ts +2 -2
  773. package/src/modules/workspace/kb-repository.ts +1 -1
  774. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +1 -1
  775. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +1 -1
  776. package/src/modules/workspace/startup/__tests__/kb-startup-runner.worktrees.test.ts +1 -1
  777. package/src/modules/workspace/startup/kb-git.ts +1 -1
  778. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +94 -70
  779. package/src/modules/workspace/startup/steps/seed-tree.ts +4 -5
  780. package/src/modules/workspace/startup/steps/template-files.step.ts +4 -5
  781. package/src/modules/workspace/startup/steps/template-source.ts +21 -6
  782. package/src/modules/workspace/workspace-tmp.ts +4 -4
  783. package/src/modules/workspace/workspace.routes.ts +26 -26
  784. package/src/modules/workspace/workspace.service.ts +11 -11
  785. package/src/modules/workspace/workspace.tools.ts +90 -30
  786. package/src/shared/__tests__/canonical-file-identity.test.ts +5 -5
  787. package/src/shared/__tests__/http-errors.test.ts +4 -4
  788. package/src/shared/__tests__/read-capped.test.ts +68 -0
  789. package/src/shared/canonical-file-identity.ts +5 -5
  790. package/src/shared/domain-errors.ts +44 -44
  791. package/src/shared/git.contract.ts +1 -1
  792. package/src/shared/http-errors.ts +3 -3
  793. package/src/shared/kb-context.ts +130 -130
  794. package/src/shared/oauth-token-error.ts +46 -0
  795. package/src/shared/pending-proposals.ts +4 -4
  796. package/src/shared/read-capped.ts +40 -0
  797. package/src/tenancy/__tests__/tenant-host.test.ts +372 -372
  798. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -221
  799. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -35
  800. package/src/tenancy/index.ts +35 -35
  801. package/src/tenancy/static-tenant-source.ts +1 -1
  802. package/src/tenancy/tenant-host.ts +261 -261
  803. package/src/tenancy/tenant-runtime.ts +208 -208
  804. package/src/tenancy/tenant-source.contract.ts +59 -59
  805. package/agent-guide/finding-things.md +0 -7
  806. package/agent-guide/introduction.md +0 -27
  807. package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts.map +0 -1
  808. package/dist/modules/workflow/agent-tools/change-request-read-shape.js.map +0 -1
  809. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +0 -1
  810. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +0 -1
  811. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +0 -1
  812. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +0 -1
  813. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +0 -1
  814. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +0 -1
  815. package/dist/modules/workflow/change-request-hooks.d.ts.map +0 -1
  816. package/dist/modules/workflow/change-request-hooks.js.map +0 -1
  817. package/dist/modules/workflow/event-bus.d.ts.map +0 -1
  818. package/dist/modules/workflow/event-bus.js.map +0 -1
  819. package/dist/modules/workflow/events-delivery.d.ts.map +0 -1
  820. package/dist/modules/workflow/events-delivery.js.map +0 -1
  821. package/dist/modules/workflow/events.routes.d.ts.map +0 -1
  822. package/dist/modules/workflow/events.routes.js.map +0 -1
  823. package/dist/modules/workflow/file-lock.service.d.ts.map +0 -1
  824. package/dist/modules/workflow/file-lock.service.js.map +0 -1
  825. package/dist/modules/workflow/git/change-request-link.d.ts.map +0 -1
  826. package/dist/modules/workflow/git/change-request-link.js.map +0 -1
  827. package/dist/modules/workflow/git/git-version.d.ts.map +0 -1
  828. package/dist/modules/workflow/git/git-version.js.map +0 -1
  829. package/dist/modules/workflow/git/git.service.d.ts.map +0 -1
  830. package/dist/modules/workflow/git/git.service.js.map +0 -1
  831. package/dist/modules/workflow/git/merge-commit.d.ts.map +0 -1
  832. package/dist/modules/workflow/git/merge-commit.js.map +0 -1
  833. package/dist/modules/workflow/git/node-git-runner.d.ts.map +0 -1
  834. package/dist/modules/workflow/git/node-git-runner.js.map +0 -1
  835. package/dist/modules/workflow/git/pull-request.service.d.ts.map +0 -1
  836. package/dist/modules/workflow/git/pull-request.service.js.map +0 -1
  837. package/dist/modules/workflow/index.d.ts.map +0 -1
  838. package/dist/modules/workflow/index.js.map +0 -1
  839. package/dist/modules/workflow/pending-commits.service.d.ts.map +0 -1
  840. package/dist/modules/workflow/pending-commits.service.js.map +0 -1
  841. package/dist/modules/workflow/pending-commits.worker.d.ts.map +0 -1
  842. package/dist/modules/workflow/pending-commits.worker.js.map +0 -1
  843. package/dist/modules/workflow/read-on-branch.d.ts.map +0 -1
  844. package/dist/modules/workflow/read-on-branch.js.map +0 -1
  845. package/dist/modules/workflow/recovery-bot.d.ts.map +0 -1
  846. package/dist/modules/workflow/recovery-bot.js.map +0 -1
  847. package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +0 -1
  848. package/dist/modules/workflow/review-workflow/approval-lock.js.map +0 -1
  849. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +0 -1
  850. package/dist/modules/workflow/review-workflow/review-workflow.interface.js +0 -2
  851. package/dist/modules/workflow/review-workflow/review-workflow.interface.js.map +0 -1
  852. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +0 -1
  853. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +0 -1
  854. package/dist/modules/workflow/sanitize-error.d.ts.map +0 -1
  855. package/dist/modules/workflow/sanitize-error.js.map +0 -1
  856. package/dist/modules/workflow/workflow-hooks.d.ts.map +0 -1
  857. package/dist/modules/workflow/workflow-hooks.js.map +0 -1
  858. package/dist/modules/workflow/workflow.routes.d.ts.map +0 -1
  859. package/dist/modules/workflow/workflow.routes.js.map +0 -1
  860. package/dist/modules/workflow/workflow.service.d.ts.map +0 -1
  861. package/dist/modules/workflow/workflow.service.js.map +0 -1
  862. package/src/modules/mcp/__tests__/read-only-hint.e2e.test.ts +0 -213
  863. /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read-shape.d.ts +0 -0
  864. /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read-shape.js +0 -0
  865. /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read.tools.d.ts +0 -0
  866. /package/dist/modules/{workflow → version-control}/agent-tools/change-request-summary.d.ts +0 -0
  867. /package/dist/modules/{workflow → version-control}/agent-tools/change-request-summary.js +0 -0
  868. /package/dist/modules/{workflow → version-control}/change-request-hooks.js +0 -0
  869. /package/dist/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.d.ts +0 -0
  870. /package/dist/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.js +0 -0
  871. /package/dist/modules/{workflow → version-control}/events-delivery.js +0 -0
  872. /package/dist/modules/{workflow → version-control}/events.routes.js +0 -0
  873. /package/dist/modules/{workflow → version-control}/git/change-request-link.d.ts +0 -0
  874. /package/dist/modules/{workflow → version-control}/git/change-request-link.js +0 -0
  875. /package/dist/modules/{workflow → version-control}/git/git-version.d.ts +0 -0
  876. /package/dist/modules/{workflow → version-control}/git/git-version.js +0 -0
  877. /package/dist/modules/{workflow → version-control}/git/merge-commit.d.ts +0 -0
  878. /package/dist/modules/{workflow → version-control}/git/merge-commit.js +0 -0
  879. /package/dist/modules/{workflow → version-control}/git/node-git-runner.d.ts +0 -0
  880. /package/dist/modules/{workflow → version-control}/recovery-bot.d.ts +0 -0
  881. /package/dist/modules/{workflow → version-control}/recovery-bot.js +0 -0
  882. /package/dist/modules/{workflow → version-control}/sanitize-error.d.ts +0 -0
  883. /package/dist/modules/{workflow → version-control}/sanitize-error.js +0 -0
  884. /package/src/modules/{workflow → version-control}/__tests__/change-request-hooks.test.ts +0 -0
  885. /package/src/modules/{workflow → version-control}/__tests__/fake-file-lock-db.ts +0 -0
  886. /package/src/modules/{workflow → version-control}/__tests__/pending-commits.canonical.test.ts +0 -0
  887. /package/src/modules/{workflow → version-control}/__tests__/pending-commits.enqueue-if-absent.test.ts +0 -0
  888. /package/src/modules/{workflow → version-control}/__tests__/pending-commits.repository-replaced.test.ts +0 -0
  889. /package/src/modules/{workflow → version-control}/__tests__/sanitize-error.test.ts +0 -0
  890. /package/src/modules/{workflow → version-control}/agent-tools/__tests__/change-request-read-shape.test.ts +0 -0
  891. /package/src/modules/{workflow → version-control}/agent-tools/change-request-read-shape.ts +0 -0
  892. /package/src/modules/{workflow → version-control}/agent-tools/change-request-summary.ts +0 -0
  893. /package/src/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.ts +0 -0
  894. /package/src/modules/{workflow → version-control}/git/__tests__/branchAuthor.test.ts +0 -0
  895. /package/src/modules/{workflow → version-control}/git/__tests__/change-request-link.test.ts +0 -0
  896. /package/src/modules/{workflow → version-control}/git/__tests__/git-version.failure.test.ts +0 -0
  897. /package/src/modules/{workflow → version-control}/git/__tests__/git-version.test.ts +0 -0
  898. /package/src/modules/{workflow → version-control}/git/__tests__/node-git-runner.test.ts +0 -0
  899. /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.getPrDetail.test.ts +0 -0
  900. /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.placeholder.test.ts +0 -0
  901. /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +0 -0
  902. /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-delete.test.ts +0 -0
  903. /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-update.test.ts +0 -0
  904. /package/src/modules/{workflow → version-control}/git/change-request-link.ts +0 -0
  905. /package/src/modules/{workflow → version-control}/git/git-version.ts +0 -0
  906. /package/src/modules/{workflow → version-control}/git/merge-commit.ts +0 -0
  907. /package/src/modules/{workflow → version-control}/recovery-bot.ts +0 -0
  908. /package/src/modules/{workflow → version-control}/sanitize-error.ts +0 -0
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import os from 'node:os';
5
- import type { AuthUser, IWorkflowService } from '@bevel-software/platform-shared';
5
+ import type { AuthUser, IVersionControlService } from '@bevel-software/platform-shared';
6
6
  import { LocalFilesystem } from '@mastra/core/workspace';
7
7
  import { LockingFilesystem } from '../locking-filesystem.js';
8
- import { PushNeedsAgentResolutionError, WorkflowValidationError } from '../../../shared/domain-errors.js';
8
+ import { PushNeedsAgentResolutionError, VersionControlValidationError } from '../../../shared/domain-errors.js';
9
9
  import {
10
10
  makeAgentRolesYamlWriteValidator,
11
11
  RolesYamlInvalidError,
@@ -21,7 +21,7 @@ const USER: AuthUser = {
21
21
  name: 'Alice',
22
22
  };
23
23
 
24
- function makeWorkflow(): IWorkflowService {
24
+ function makeVersionControl(): IVersionControlService {
25
25
  return {
26
26
  acquireLock: vi.fn().mockResolvedValue({
27
27
  acquired: true,
@@ -42,7 +42,7 @@ function makeWorkflow(): IWorkflowService {
42
42
  getLock: vi.fn(),
43
43
  commitFileWhileLocked: vi.fn(),
44
44
  commitChanges: vi.fn().mockResolvedValue({}),
45
- } as unknown as IWorkflowService;
45
+ } as unknown as IVersionControlService;
46
46
  }
47
47
 
48
48
  async function mkTmpRoot(): Promise<string> {
@@ -61,29 +61,29 @@ describe('LockingFilesystem — every mutating op runs acquire → super → rel
61
61
  });
62
62
 
63
63
  it('writeFile acquires, writes through to disk, then releases', async () => {
64
- const workflow = makeWorkflow();
64
+ const versionControl = makeVersionControl();
65
65
  const fsLayer = new LockingFilesystem(
66
66
  { basePath: root, contained: true },
67
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
67
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
68
68
  );
69
69
  await fsLayer.writeFile('knowledge-base/Knowledge/Foo.md', 'hello\n');
70
70
 
71
- expect(workflow.acquireLock).toHaveBeenCalledWith(
71
+ expect(versionControl.acquireLock).toHaveBeenCalledWith(
72
72
  'ws-feat',
73
73
  'feat',
74
74
  'knowledge-base/Knowledge/Foo.md',
75
75
  USER,
76
76
  );
77
- expect(workflow.releaseLock).toHaveBeenCalledWith(
77
+ expect(versionControl.releaseLock).toHaveBeenCalledWith(
78
78
  'ws-feat',
79
79
  'feat',
80
80
  'knowledge-base/Knowledge/Foo.md',
81
81
  USER,
82
82
  );
83
83
  // Acquire fires before release.
84
- const acquireOrder = (workflow.acquireLock as ReturnType<typeof vi.fn>).mock
84
+ const acquireOrder = (versionControl.acquireLock as ReturnType<typeof vi.fn>).mock
85
85
  .invocationCallOrder[0];
86
- const releaseOrder = (workflow.releaseLock as ReturnType<typeof vi.fn>).mock
86
+ const releaseOrder = (versionControl.releaseLock as ReturnType<typeof vi.fn>).mock
87
87
  .invocationCallOrder[0];
88
88
  expect(acquireOrder).toBeLessThan(releaseOrder);
89
89
  // The bytes actually landed on disk.
@@ -94,33 +94,33 @@ describe('LockingFilesystem — every mutating op runs acquire → super → rel
94
94
  const filePath = path.join(root, 'knowledge-base/a.md');
95
95
  await fs.mkdir(path.dirname(filePath), { recursive: true });
96
96
  await fs.writeFile(filePath, 'x', 'utf-8');
97
- const workflow = makeWorkflow();
97
+ const versionControl = makeVersionControl();
98
98
  const fsLayer = new LockingFilesystem(
99
99
  { basePath: root, contained: true },
100
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
100
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
101
101
  );
102
102
  await fsLayer.deleteFile('knowledge-base/a.md');
103
- expect(workflow.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
104
- expect(workflow.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
103
+ expect(versionControl.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
104
+ expect(versionControl.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
105
105
  await expect(fs.access(filePath)).rejects.toBeDefined();
106
106
  });
107
107
 
108
108
  it('mkdir drops a .gitkeep through the lock-aware writeFile so the empty folder is committed', async () => {
109
- const workflow = makeWorkflow();
109
+ const versionControl = makeVersionControl();
110
110
  const fsLayer = new LockingFilesystem(
111
111
  { basePath: root, contained: true },
112
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
112
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
113
113
  );
114
114
  await fsLayer.mkdir('knowledge-base/empty');
115
115
  // .gitkeep got written through the lock path. The lock is on the
116
116
  // .gitkeep path (that's the file the commit attaches to).
117
- expect(workflow.acquireLock).toHaveBeenCalledWith(
117
+ expect(versionControl.acquireLock).toHaveBeenCalledWith(
118
118
  'ws-feat',
119
119
  'feat',
120
120
  'knowledge-base/empty/.gitkeep',
121
121
  USER,
122
122
  );
123
- expect(workflow.releaseLock).toHaveBeenCalledWith(
123
+ expect(versionControl.releaseLock).toHaveBeenCalledWith(
124
124
  'ws-feat',
125
125
  'feat',
126
126
  'knowledge-base/empty/.gitkeep',
@@ -132,37 +132,37 @@ describe('LockingFilesystem — every mutating op runs acquire → super → rel
132
132
  it('mkdir of an already-populated dir does NOT drop a .gitkeep', async () => {
133
133
  await fs.mkdir(path.join(root, 'knowledge-base/has-content'), { recursive: true });
134
134
  await fs.writeFile(path.join(root, 'knowledge-base/has-content/real.md'), 'hi', 'utf-8');
135
- const workflow = makeWorkflow();
135
+ const versionControl = makeVersionControl();
136
136
  const fsLayer = new LockingFilesystem(
137
137
  { basePath: root, contained: true },
138
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
138
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
139
139
  );
140
140
  await fsLayer.mkdir('knowledge-base/has-content', { recursive: true });
141
- expect(workflow.acquireLock).not.toHaveBeenCalled();
142
- expect(workflow.releaseLock).not.toHaveBeenCalled();
141
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
142
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
143
143
  // The existing file is untouched.
144
144
  expect(await fs.readFile(path.join(root, 'knowledge-base/has-content/real.md'), 'utf-8')).toBe('hi');
145
145
  });
146
146
 
147
147
  it('rmdir is refused with a clear error — one-change-per-file invariant', async () => {
148
148
  await fs.mkdir(path.join(root, 'knowledge-base/doomed'), { recursive: true });
149
- const workflow = makeWorkflow();
149
+ const versionControl = makeVersionControl();
150
150
  const fsLayer = new LockingFilesystem(
151
151
  { basePath: root, contained: true },
152
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
152
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
153
153
  );
154
154
  await expect(fsLayer.rmdir('knowledge-base/doomed')).rejects.toThrow(
155
155
  /Recursive directory removal is not supported/i,
156
156
  );
157
- expect(workflow.acquireLock).not.toHaveBeenCalled();
157
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
158
158
  });
159
159
 
160
160
  it(
161
161
  'retries acquire on contention then surfaces a skip error after 3 attempts',
162
162
  async () => {
163
163
  // 3 attempts × 2s ≈ 6s — bump vitest's default timeout for this one test.
164
- const workflow = makeWorkflow();
165
- (workflow.acquireLock as ReturnType<typeof vi.fn>).mockResolvedValue({
164
+ const versionControl = makeVersionControl();
165
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>).mockResolvedValue({
166
166
  acquired: false,
167
167
  lock: {
168
168
  branch: 'feat',
@@ -176,23 +176,23 @@ describe('LockingFilesystem — every mutating op runs acquire → super → rel
176
176
  });
177
177
  const fsLayer = new LockingFilesystem(
178
178
  { basePath: root, contained: true },
179
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
179
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
180
180
  );
181
181
 
182
182
  await expect(fsLayer.writeFile('knowledge-base/Locked.md', 'x')).rejects.toThrow(
183
183
  /Skipped editing "knowledge-base\/Locked\.md" — locked by Bob/,
184
184
  );
185
- expect(workflow.acquireLock).toHaveBeenCalledTimes(3);
186
- expect(workflow.releaseLock).not.toHaveBeenCalled();
185
+ expect(versionControl.acquireLock).toHaveBeenCalledTimes(3);
186
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
187
187
  },
188
188
  10_000,
189
189
  );
190
190
 
191
191
  it('drops the lock without committing when the underlying write throws', async () => {
192
- const workflow = makeWorkflow();
192
+ const versionControl = makeVersionControl();
193
193
  const fsLayer = new LockingFilesystem(
194
194
  { basePath: root, contained: true },
195
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
195
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
196
196
  );
197
197
  // The underlying write dies (disk full, permissions, whatever). The lock
198
198
  // must still go away so the next caller can edit a different file, but
@@ -207,9 +207,9 @@ describe('LockingFilesystem — every mutating op runs acquire → super → rel
207
207
  } finally {
208
208
  spy.mockRestore();
209
209
  }
210
- expect(workflow.acquireLock).toHaveBeenCalled();
211
- expect(workflow.releaseLockNoCommit).toHaveBeenCalled();
212
- expect(workflow.releaseLock).not.toHaveBeenCalled();
210
+ expect(versionControl.acquireLock).toHaveBeenCalled();
211
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalled();
212
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
213
213
  });
214
214
  });
215
215
 
@@ -228,12 +228,12 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
228
228
  await fs.mkdir(path.join(root, 'knowledge-base/Tools'), { recursive: true });
229
229
  await fs.writeFile(path.join(root, 'knowledge-base/Tools/old.tool'), '{}');
230
230
 
231
- const workflow = makeWorkflow();
231
+ const versionControl = makeVersionControl();
232
232
  const emitted: unknown[] = [];
233
233
  const fsLayer = new LockingFilesystem(
234
234
  { basePath: root, contained: true },
235
235
  {
236
- workflow,
236
+ versionControl,
237
237
  workspaceId: 'ws-feat',
238
238
  branch: 'feat',
239
239
  user: USER, kbDirName: KB,
@@ -248,8 +248,8 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
248
248
  );
249
249
 
250
250
  // Both paths locked; both mutations landed; commit returned.
251
- expect(workflow.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/new.tool', USER);
252
- expect(workflow.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/old.tool', USER);
251
+ expect(versionControl.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/new.tool', USER);
252
+ expect(versionControl.acquireLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/old.tool', USER);
253
253
  expect(await fs.readFile(path.join(root, 'knowledge-base/Tools/new.tool'), 'utf-8')).toContain('http');
254
254
  await expect(fs.access(path.join(root, 'knowledge-base/Tools/old.tool'))).rejects.toThrow();
255
255
  expect(change).toEqual({});
@@ -267,13 +267,13 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
267
267
  await fs.mkdir(path.join(root, 'knowledge-base/Tools'), { recursive: true });
268
268
  await fs.writeFile(path.join(root, 'knowledge-base/Tools/old.tool'), '{}');
269
269
 
270
- const workflow = makeWorkflow();
271
- (workflow.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('push exploded'));
270
+ const versionControl = makeVersionControl();
271
+ (versionControl.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('push exploded'));
272
272
  const emitted: unknown[] = [];
273
273
  const fsLayer = new LockingFilesystem(
274
274
  { basePath: root, contained: true },
275
275
  {
276
- workflow,
276
+ versionControl,
277
277
  workspaceId: 'ws-feat',
278
278
  branch: 'feat',
279
279
  user: USER, kbDirName: KB,
@@ -291,11 +291,11 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
291
291
 
292
292
  // Every acquired path goes through the NO-COMMIT release — which is what
293
293
  // discards the uncommitted bytes (write reverted, delete restored) in the
294
- // real WorkflowService via git.discardPath. The committing release must
294
+ // real VersionControlService via git.discardPath. The committing release must
295
295
  // never run, and no change event fires for a failed batch.
296
- expect(workflow.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/new.tool', USER);
297
- expect(workflow.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/old.tool', USER);
298
- expect(workflow.releaseLock).not.toHaveBeenCalled();
296
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/new.tool', USER);
297
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Tools/old.tool', USER);
298
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
299
299
  expect(emitted).toHaveLength(0);
300
300
  });
301
301
 
@@ -307,15 +307,15 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
307
307
  // forever. The batch must releaseLock (the enqueued release commit no-ops
308
308
  // on the clean tree and the worker's unpushed-commits check re-runs the
309
309
  // push ladder) and still propagate the error.
310
- const workflow = makeWorkflow();
311
- (workflow.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
310
+ const versionControl = makeVersionControl();
311
+ (versionControl.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
312
312
  new PushNeedsAgentResolutionError('feat', 'knowledge-base/A.md', 'non-fast-forward', 'rebase failed'),
313
313
  );
314
314
  const emitted: unknown[] = [];
315
315
  const fsLayer = new LockingFilesystem(
316
316
  { basePath: root, contained: true },
317
317
  {
318
- workflow,
318
+ versionControl,
319
319
  workspaceId: 'ws-feat',
320
320
  branch: 'feat',
321
321
  user: USER, kbDirName: KB,
@@ -327,19 +327,19 @@ describe('LockingFilesystem.writeFiles — batch with deletes + one batched chan
327
327
  fsLayer.writeFiles([{ path: 'knowledge-base/A.md', content: 'x' }], 'batch'),
328
328
  ).rejects.toBeInstanceOf(PushNeedsAgentResolutionError);
329
329
 
330
- expect(workflow.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/A.md', USER);
331
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
330
+ expect(versionControl.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/A.md', USER);
331
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
332
332
  expect(emitted).toHaveLength(0);
333
333
  });
334
334
 
335
335
  it('emits nothing on a no-op commit (clean tree)', async () => {
336
- const workflow = makeWorkflow();
337
- (workflow.commitChanges as ReturnType<typeof vi.fn>).mockResolvedValue(null);
336
+ const versionControl = makeVersionControl();
337
+ (versionControl.commitChanges as ReturnType<typeof vi.fn>).mockResolvedValue(null);
338
338
  const emitted: unknown[] = [];
339
339
  const fsLayer = new LockingFilesystem(
340
340
  { basePath: root, contained: true },
341
341
  {
342
- workflow,
342
+ versionControl,
343
343
  workspaceId: 'ws-feat',
344
344
  branch: 'feat',
345
345
  user: USER, kbDirName: KB,
@@ -362,24 +362,24 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
362
362
  await fs.rm(root, { recursive: true, force: true });
363
363
  });
364
364
 
365
- const layer = (workflow: IWorkflowService): LockingFilesystem =>
365
+ const layer = (versionControl: IVersionControlService): LockingFilesystem =>
366
366
  new LockingFilesystem(
367
367
  { basePath: root, contained: true },
368
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
368
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
369
369
  );
370
370
 
371
371
  it('writeFile runs the check with the lock HELD and before any byte lands', async () => {
372
372
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
373
373
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'before\n');
374
374
 
375
- const workflow = makeWorkflow();
375
+ const versionControl = makeVersionControl();
376
376
  const order: string[] = [];
377
- (workflow.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
377
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
378
378
  order.push('acquire');
379
379
  return { acquired: true, lock: { holderName: 'Alice' } };
380
380
  });
381
381
 
382
- await layer(workflow).writeFile('knowledge-base/Foo.md', 'after\n', undefined, async () => {
382
+ await layer(versionControl).writeFile('knowledge-base/Foo.md', 'after\n', undefined, async () => {
383
383
  order.push('check');
384
384
  // The check reads the state the write is about to replace.
385
385
  expect(await fs.readFile(path.join(root, 'knowledge-base/Foo.md'), 'utf-8')).toBe('before\n');
@@ -393,10 +393,10 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
393
393
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
394
394
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'theirs\n');
395
395
 
396
- const workflow = makeWorkflow();
396
+ const versionControl = makeVersionControl();
397
397
  const refusal = new Error('already exists');
398
398
  await expect(
399
- layer(workflow).writeFile('knowledge-base/Foo.md', 'mine\n', undefined, () => {
399
+ layer(versionControl).writeFile('knowledge-base/Foo.md', 'mine\n', undefined, () => {
400
400
  throw refusal;
401
401
  }),
402
402
  ).rejects.toBe(refusal);
@@ -404,30 +404,30 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
404
404
  expect(await fs.readFile(path.join(root, 'knowledge-base/Foo.md'), 'utf-8')).toBe('theirs\n');
405
405
  // Untouched, never no-commit: a discard would reset the path to HEAD and
406
406
  // destroy whatever save left those bytes there.
407
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
408
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
409
- expect(workflow.releaseLock).not.toHaveBeenCalled();
407
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
408
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
409
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
410
410
  });
411
411
 
412
412
  it('rewriteFile reads, computes and writes with the lock HELD, from the bytes on disk at that moment', async () => {
413
413
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
414
414
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'owner: \n');
415
415
 
416
- const workflow = makeWorkflow();
416
+ const versionControl = makeVersionControl();
417
417
  const order: string[] = [];
418
- (workflow.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
418
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
419
419
  order.push('acquire');
420
420
  // Another writer lands while this call waits for the lock.
421
421
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'owner: alice\n');
422
422
  return { acquired: true, lock: { holderName: 'Alice' } };
423
423
  });
424
- (workflow.releaseLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
424
+ (versionControl.releaseLock as ReturnType<typeof vi.fn>).mockImplementation(async () => {
425
425
  order.push('release');
426
426
  return null;
427
427
  });
428
428
 
429
429
  let seen = '';
430
- await layer(workflow).rewriteFile('knowledge-base/Foo.md', (current) => {
430
+ await layer(versionControl).rewriteFile('knowledge-base/Foo.md', (current) => {
431
431
  order.push('rewrite');
432
432
  seen = current!.toString('utf8');
433
433
  return `${seen}note: kept\n`;
@@ -437,32 +437,32 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
437
437
  // It was handed what the other writer left, not what was there when the call began.
438
438
  expect(seen).toBe('owner: alice\n');
439
439
  expect(await fs.readFile(path.join(root, 'knowledge-base/Foo.md'), 'utf-8')).toBe('owner: alice\nnote: kept\n');
440
- expect(workflow.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
440
+ expect(versionControl.releaseLock).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
441
441
  });
442
442
 
443
443
  it('a rewriteFile that throws writes nothing, releases UNTOUCHED, and gives the caller its own error', async () => {
444
444
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
445
445
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'theirs\n');
446
446
 
447
- const workflow = makeWorkflow();
447
+ const versionControl = makeVersionControl();
448
448
  const refusal = new Error('old text is gone');
449
449
  await expect(
450
- layer(workflow).rewriteFile('knowledge-base/Foo.md', () => {
450
+ layer(versionControl).rewriteFile('knowledge-base/Foo.md', () => {
451
451
  throw refusal;
452
452
  }),
453
453
  ).rejects.toBe(refusal);
454
454
 
455
455
  expect(await fs.readFile(path.join(root, 'knowledge-base/Foo.md'), 'utf-8')).toBe('theirs\n');
456
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
457
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
458
- expect(workflow.releaseLock).not.toHaveBeenCalled();
456
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/Foo.md', USER);
457
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
458
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
459
459
  });
460
460
 
461
461
  it('rewriteFile hands over null when nothing is at the path', async () => {
462
462
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
463
- const workflow = makeWorkflow();
463
+ const versionControl = makeVersionControl();
464
464
  let seen: Buffer | null | undefined;
465
- await layer(workflow).rewriteFile('knowledge-base/New.md', (current) => {
465
+ await layer(versionControl).rewriteFile('knowledge-base/New.md', (current) => {
466
466
  seen = current;
467
467
  return 'made\n';
468
468
  });
@@ -473,13 +473,13 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
473
473
  it('rewriteFile judges the bytes it is about to write with the pre-disk validator, under the lock', async () => {
474
474
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
475
475
  await fs.writeFile(path.join(root, 'knowledge-base/Foo.md'), 'ok\n');
476
- const workflow = makeWorkflow();
476
+ const versionControl = makeVersionControl();
477
477
  const refusal = new Error('refused by the validator');
478
478
  const judged: string[] = [];
479
479
  const guarded = new LockingFilesystem(
480
480
  { basePath: root, contained: true },
481
481
  {
482
- workflow,
482
+ versionControl,
483
483
  workspaceId: 'ws-feat',
484
484
  branch: 'feat',
485
485
  user: USER,
@@ -493,19 +493,19 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
493
493
  await expect(guarded.rewriteFile('knowledge-base/Foo.md', () => 'bad\n')).rejects.toBe(refusal);
494
494
  expect(judged).toEqual(['bad\n']);
495
495
  expect(await fs.readFile(path.join(root, 'knowledge-base/Foo.md'), 'utf-8')).toBe('ok\n');
496
- expect(workflow.releaseLockUntouched).toHaveBeenCalled();
496
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalled();
497
497
  });
498
498
 
499
499
  it('rewriteFile refuses a path outside the repository before any lock', async () => {
500
- const workflow = makeWorkflow();
501
- await expect(layer(workflow).rewriteFile('elsewhere/Foo.md', () => 'x')).rejects.toThrow();
502
- expect(workflow.acquireLock).not.toHaveBeenCalled();
500
+ const versionControl = makeVersionControl();
501
+ await expect(layer(versionControl).rewriteFile('elsewhere/Foo.md', () => 'x')).rejects.toThrow();
502
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
503
503
  });
504
504
 
505
505
  it('writeFiles runs its check once EVERY lock is held, and lands only what the check keeps', async () => {
506
- const workflow = makeWorkflow();
506
+ const versionControl = makeVersionControl();
507
507
  const acquired: string[] = [];
508
- (workflow.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async (_w, _b, p: string) => {
508
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async (_w, _b, p: string) => {
509
509
  acquired.push(p);
510
510
  return { acquired: true, lock: { holderName: 'Alice' } };
511
511
  });
@@ -515,7 +515,7 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
515
515
  { path: 'knowledge-base/b.md', content: 'B' },
516
516
  { path: 'knowledge-base/c.md', content: 'C' },
517
517
  ];
518
- await layer(workflow).writeFiles(writes, 'batch', [], async (pending) => {
518
+ await layer(versionControl).writeFiles(writes, 'batch', [], async (pending) => {
519
519
  // Every path of the batch is locked before the check gets to judge any.
520
520
  expect([...acquired].sort()).toEqual(['knowledge-base/a.md', 'knowledge-base/b.md', 'knowledge-base/c.md']);
521
521
  expect(pending.map((w) => w.path)).toEqual(writes.map((w) => w.path));
@@ -528,16 +528,16 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
528
528
  // The dropped path stays OUT of the commit's scope — an unscoped commit
529
529
  // there would sweep in whatever another save left dirty on it — and
530
530
  // releases untouched, because this batch never wrote a byte to it.
531
- expect(workflow.commitChanges).toHaveBeenCalledWith('ws-feat', USER, 'batch', [
531
+ expect(versionControl.commitChanges).toHaveBeenCalledWith('ws-feat', USER, 'batch', [
532
532
  'knowledge-base/a.md',
533
533
  'knowledge-base/c.md',
534
534
  ]);
535
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/b.md', USER);
535
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/b.md', USER);
536
536
  });
537
537
 
538
538
  it('a writeFiles check that drops EVERY write commits nothing and releases everything untouched', async () => {
539
- const workflow = makeWorkflow();
540
- const change = await layer(workflow).writeFiles(
539
+ const versionControl = makeVersionControl();
540
+ const change = await layer(versionControl).writeFiles(
541
541
  [{ path: 'knowledge-base/a.md', content: 'A' }],
542
542
  'batch',
543
543
  [],
@@ -546,15 +546,15 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
546
546
 
547
547
  expect(change).toBeNull();
548
548
  await expect(fs.access(path.join(root, 'knowledge-base/a.md'))).rejects.toThrow();
549
- expect(workflow.commitChanges).not.toHaveBeenCalled();
550
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
549
+ expect(versionControl.commitChanges).not.toHaveBeenCalled();
550
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/a.md', USER);
551
551
  });
552
552
 
553
553
  it('a writeFiles check that THROWS refuses the whole batch, untouched', async () => {
554
- const workflow = makeWorkflow();
554
+ const versionControl = makeVersionControl();
555
555
  const refusal = new Error('nope');
556
556
  await expect(
557
- layer(workflow).writeFiles(
557
+ layer(versionControl).writeFiles(
558
558
  [{ path: 'knowledge-base/a.md', content: 'A' }, { path: 'knowledge-base/b.md', content: 'B' }],
559
559
  'batch',
560
560
  [],
@@ -565,8 +565,8 @@ describe('LockingFilesystem — the caller judges under the lock', () => {
565
565
  ).rejects.toBe(refusal);
566
566
 
567
567
  await expect(fs.access(path.join(root, 'knowledge-base/a.md'))).rejects.toThrow();
568
- expect(workflow.commitChanges).not.toHaveBeenCalled();
569
- expect(workflow.releaseLockUntouched).toHaveBeenCalledTimes(2);
568
+ expect(versionControl.commitChanges).not.toHaveBeenCalled();
569
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledTimes(2);
570
570
  });
571
571
  });
572
572
 
@@ -589,11 +589,11 @@ describe('LockingFilesystem — creator read grants on creation', () => {
589
589
  }
590
590
 
591
591
  it('writeFile asks the planner about the new file and lands the content untouched', async () => {
592
- const workflow = makeWorkflow();
592
+ const versionControl = makeVersionControl();
593
593
  const creatorAccess = makeCreatorAccess();
594
594
  const fsLayer = new LockingFilesystem(
595
595
  { basePath: root, contained: true },
596
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
596
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
597
597
  );
598
598
  await fsLayer.writeFile('knowledge-base/KnowledgeBase/new.md', '# New\n');
599
599
  expect(creatorAccess.planForCreate).toHaveBeenCalledWith(
@@ -608,7 +608,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
608
608
  });
609
609
 
610
610
  it('writeFile seeds a subtree access.md (own lock cycle) before the file itself', async () => {
611
- const workflow = makeWorkflow();
611
+ const versionControl = makeVersionControl();
612
612
  const creatorAccess = makeCreatorAccess();
613
613
  creatorAccess.planForCreate.mockImplementation(async (_w, _u, p: string) =>
614
614
  p === 'knowledge-base/KnowledgeBase/Mine/doc.md'
@@ -622,10 +622,10 @@ describe('LockingFilesystem — creator read grants on creation', () => {
622
622
  );
623
623
  const fsLayer = new LockingFilesystem(
624
624
  { basePath: root, contained: true },
625
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
625
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
626
626
  );
627
627
  await fsLayer.writeFile('knowledge-base/KnowledgeBase/Mine/doc.md', 'body');
628
- const locked = (workflow.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map(
628
+ const locked = (versionControl.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map(
629
629
  (c) => c[2],
630
630
  );
631
631
  expect(locked).toEqual(['knowledge-base/KnowledgeBase/Mine/access.md', 'knowledge-base/KnowledgeBase/Mine/doc.md']);
@@ -638,7 +638,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
638
638
  });
639
639
 
640
640
  it('mkdir seeds the new folder access.md and then skips the .gitkeep (dir not empty)', async () => {
641
- const workflow = makeWorkflow();
641
+ const versionControl = makeVersionControl();
642
642
  const creatorAccess = makeCreatorAccess();
643
643
  creatorAccess.planForCreate.mockImplementation(async (_w, _u, p: string, kind: string) =>
644
644
  kind === 'dir'
@@ -651,7 +651,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
651
651
  );
652
652
  const fsLayer = new LockingFilesystem(
653
653
  { basePath: root, contained: true },
654
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
654
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
655
655
  );
656
656
  await fsLayer.mkdir('knowledge-base/KnowledgeBase/Projects');
657
657
  expect(
@@ -663,7 +663,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
663
663
  });
664
664
 
665
665
  it('writeFiles folds seeds into the same atomic batch, deduped across files', async () => {
666
- const workflow = makeWorkflow();
666
+ const versionControl = makeVersionControl();
667
667
  const creatorAccess = makeCreatorAccess();
668
668
  creatorAccess.planForCreate.mockResolvedValue({
669
669
  kind: 'seed-access-md',
@@ -672,7 +672,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
672
672
  });
673
673
  const fsLayer = new LockingFilesystem(
674
674
  { basePath: root, contained: true },
675
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
675
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
676
676
  );
677
677
  await fsLayer.writeFiles(
678
678
  [
@@ -685,8 +685,8 @@ describe('LockingFilesystem — creator read grants on creation', () => {
685
685
  expect(
686
686
  await fs.readFile(path.join(root, 'knowledge-base/KnowledgeBase/Mine/access.md'), 'utf-8'),
687
687
  ).toContain('Alice <alice@example.com>');
688
- expect(workflow.commitChanges).toHaveBeenCalledTimes(1);
689
- const locked = (workflow.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map(
688
+ expect(versionControl.commitChanges).toHaveBeenCalledTimes(1);
689
+ const locked = (versionControl.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map(
690
690
  (c) => c[2],
691
691
  );
692
692
  expect(locked).toEqual([
@@ -707,7 +707,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
707
707
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase/Mine'), { recursive: true });
708
708
  const seedContent = '---\nread:\n - Alice <alice@example.com>\n---\n';
709
709
  await fs.writeFile(path.join(root, 'knowledge-base/KnowledgeBase/Mine/access.md'), seedContent);
710
- const workflow = makeWorkflow();
710
+ const versionControl = makeVersionControl();
711
711
  const creatorAccess = makeCreatorAccess();
712
712
  creatorAccess.planForCreate.mockResolvedValue({
713
713
  kind: 'seed-access-md',
@@ -718,7 +718,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
718
718
  const fsLayer = new LockingFilesystem(
719
719
  { basePath: root, contained: true },
720
720
  {
721
- workflow,
721
+ versionControl,
722
722
  workspaceId: 'ws-feat',
723
723
  branch: 'feat',
724
724
  user: USER, kbDirName: KB,
@@ -729,10 +729,10 @@ describe('LockingFilesystem — creator read grants on creation', () => {
729
729
  await fsLayer.writeFiles([{ path: 'knowledge-base/KnowledgeBase/Mine/doc.md', content: 'body' }], 'batch');
730
730
 
731
731
  // The seed's lock cycle still ran (acquired + released)...
732
- const locked = (workflow.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map((c) => c[2]);
732
+ const locked = (versionControl.acquireLock as ReturnType<typeof vi.fn>).mock.calls.map((c) => c[2]);
733
733
  expect(locked).toContain('knowledge-base/KnowledgeBase/Mine/access.md');
734
734
  // ...but the commit is scoped to the caller's path only.
735
- const commitPaths = (workflow.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
735
+ const commitPaths = (versionControl.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
736
736
  expect(commitPaths).toEqual(['knowledge-base/KnowledgeBase/Mine/doc.md']);
737
737
  expect(emitted[0].paths).toEqual(['knowledge-base/KnowledgeBase/Mine/doc.md']);
738
738
  // The seeded file is untouched.
@@ -756,7 +756,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
756
756
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase/Mine'), { recursive: true });
757
757
  const priorSave = '---\nread:\n - Alice <alice@example.com>\n---\nprior queued bytes\n';
758
758
  await fs.writeFile(path.join(root, 'knowledge-base/KnowledgeBase/Mine/access.md'), priorSave);
759
- const workflow = makeWorkflow();
759
+ const versionControl = makeVersionControl();
760
760
  const creatorAccess = makeCreatorAccess();
761
761
  creatorAccess.planForCreate.mockResolvedValue({
762
762
  kind: 'seed-access-md',
@@ -765,21 +765,21 @@ describe('LockingFilesystem — creator read grants on creation', () => {
765
765
  });
766
766
  const fsLayer = new LockingFilesystem(
767
767
  { basePath: root, contained: true },
768
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
768
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
769
769
  );
770
770
  await fsLayer.writeFiles([{ path: 'knowledge-base/KnowledgeBase/Mine/doc.md', content: 'body' }], 'batch');
771
771
 
772
772
  // The batch's OWN committed path releases no-commit (clean — discard no-ops)...
773
- expect(workflow.releaseLockNoCommit).toHaveBeenCalledWith(
773
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalledWith(
774
774
  'ws-feat', 'feat', 'knowledge-base/KnowledgeBase/Mine/doc.md', USER,
775
775
  );
776
776
  // ...but the merely-locked seed path releases UNTOUCHED — never the
777
777
  // enqueue, never the discard.
778
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith(
778
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith(
779
779
  'ws-feat', 'feat', 'knowledge-base/KnowledgeBase/Mine/access.md', USER,
780
780
  );
781
- expect(workflow.releaseLock).not.toHaveBeenCalled();
782
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalledWith(
781
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
782
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalledWith(
783
783
  'ws-feat', 'feat', 'knowledge-base/KnowledgeBase/Mine/access.md', USER,
784
784
  );
785
785
  });
@@ -788,13 +788,13 @@ describe('LockingFilesystem — creator read grants on creation', () => {
788
788
  // First path acquires, second is contended: nothing has been written, so
789
789
  // the first path may hold ONLY someone else's still-queued bytes — the
790
790
  // unwind must neither discard them nor enqueue them under this user.
791
- const workflow = makeWorkflow();
792
- (workflow.acquireLock as ReturnType<typeof vi.fn>)
791
+ const versionControl = makeVersionControl();
792
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>)
793
793
  .mockResolvedValueOnce({ acquired: true, lock: { holderUserId: 'user-1', holderName: 'Alice' } })
794
794
  .mockResolvedValue({ acquired: false, lock: { holderUserId: 'bob', holderName: 'Bob' } });
795
795
  const fsLayer = new LockingFilesystem(
796
796
  { basePath: root, contained: true },
797
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
797
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
798
798
  );
799
799
  await expect(
800
800
  fsLayer.writeFiles(
@@ -805,9 +805,9 @@ describe('LockingFilesystem — creator read grants on creation', () => {
805
805
  'batch',
806
806
  ),
807
807
  ).rejects.toThrow(/locked by Bob/);
808
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/A.md', USER);
809
- expect(workflow.releaseLock).not.toHaveBeenCalled();
810
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
808
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', 'knowledge-base/A.md', USER);
809
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
810
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
811
811
  }, 10_000);
812
812
 
813
813
  it('a DELETE batch whose later lock is contended deletes nothing — a folder never half-disappears', async () => {
@@ -817,13 +817,13 @@ describe('LockingFilesystem — creator read grants on creation', () => {
817
817
  await fs.mkdir(path.join(root, 'knowledge-base/Archive'), { recursive: true });
818
818
  await fs.writeFile(path.join(root, 'knowledge-base/Archive/a.md'), 'a');
819
819
  await fs.writeFile(path.join(root, 'knowledge-base/Archive/b.md'), 'b');
820
- const workflow = makeWorkflow();
821
- (workflow.acquireLock as ReturnType<typeof vi.fn>)
820
+ const versionControl = makeVersionControl();
821
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>)
822
822
  .mockResolvedValueOnce({ acquired: true, lock: { holderUserId: 'user-1', holderName: 'Alice' } })
823
823
  .mockResolvedValue({ acquired: false, lock: { holderUserId: 'bob', holderName: 'Bob' } });
824
824
  const fsLayer = new LockingFilesystem(
825
825
  { basePath: root, contained: true },
826
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
826
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
827
827
  );
828
828
 
829
829
  await expect(
@@ -832,7 +832,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
832
832
 
833
833
  expect(await fs.readFile(path.join(root, 'knowledge-base/Archive/a.md'), 'utf-8')).toBe('a');
834
834
  expect(await fs.readFile(path.join(root, 'knowledge-base/Archive/b.md'), 'utf-8')).toBe('b');
835
- expect(workflow.commitChanges).not.toHaveBeenCalled();
835
+ expect(versionControl.commitChanges).not.toHaveBeenCalled();
836
836
  }, 10_000);
837
837
 
838
838
  it('a seed write that dies MID-WRITE restores the pre-image and releases UNTOUCHED', async () => {
@@ -845,7 +845,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
845
845
  const priorBytes = 'prior queued bytes\n';
846
846
  const seedPath = 'knowledge-base/KnowledgeBase/Mine/access.md';
847
847
  await fs.writeFile(path.join(root, seedPath), priorBytes);
848
- const workflow = makeWorkflow();
848
+ const versionControl = makeVersionControl();
849
849
  const creatorAccess = makeCreatorAccess();
850
850
  creatorAccess.planForCreate.mockResolvedValue({
851
851
  kind: 'seed-access-md',
@@ -854,7 +854,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
854
854
  });
855
855
  const fsLayer = new LockingFilesystem(
856
856
  { basePath: root, contained: true },
857
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
857
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
858
858
  );
859
859
  // Fail the SEED write once (simulating a mid-write death that left
860
860
  // partial bytes); every other write — including the restore — passes
@@ -879,13 +879,13 @@ describe('LockingFilesystem — creator read grants on creation', () => {
879
879
  // Pre-image restored — the partial bytes did not outlive the batch.
880
880
  expect(await fs.readFile(path.join(root, seedPath), 'utf-8')).toBe(priorBytes);
881
881
  // The failed seed stays OUT of the commit scope and releases untouched.
882
- const commitPaths = (workflow.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
882
+ const commitPaths = (versionControl.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
883
883
  expect(commitPaths).toEqual(['knowledge-base/KnowledgeBase/Mine/doc.md']);
884
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
885
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalledWith(
884
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
885
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalledWith(
886
886
  'ws-feat', 'feat', seedPath, USER,
887
887
  );
888
- expect(workflow.releaseLock).not.toHaveBeenCalled();
888
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
889
889
  });
890
890
 
891
891
  it('a LANDED seed rolls back to its pre-image when the batch commit fails — grant bytes must not outlive the batch uncommitted', async () => {
@@ -899,8 +899,8 @@ describe('LockingFilesystem — creator read grants on creation', () => {
899
899
  const priorBytes = 'prior queued bytes\n';
900
900
  const seedPath = 'knowledge-base/KnowledgeBase/Mine/access.md';
901
901
  await fs.writeFile(path.join(root, seedPath), priorBytes);
902
- const workflow = makeWorkflow();
903
- (workflow.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
902
+ const versionControl = makeVersionControl();
903
+ (versionControl.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
904
904
  new Error('commit exploded'),
905
905
  );
906
906
  const creatorAccess = makeCreatorAccess();
@@ -911,7 +911,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
911
911
  });
912
912
  const fsLayer = new LockingFilesystem(
913
913
  { basePath: root, contained: true },
914
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
914
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
915
915
  );
916
916
  await expect(
917
917
  fsLayer.writeFiles([{ path: 'knowledge-base/KnowledgeBase/Mine/doc.md', content: 'body' }], 'batch'),
@@ -920,11 +920,11 @@ describe('LockingFilesystem — creator read grants on creation', () => {
920
920
  expect(await fs.readFile(path.join(root, seedPath), 'utf-8')).toBe(priorBytes);
921
921
  // …and the seed releases UNTOUCHED (the prior queued bytes are not this
922
922
  // batch's to discard), while the caller's own write releases via discard.
923
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
924
- expect(workflow.releaseLockNoCommit).toHaveBeenCalledWith(
923
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
924
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalledWith(
925
925
  'ws-feat', 'feat', 'knowledge-base/KnowledgeBase/Mine/doc.md', USER,
926
926
  );
927
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalledWith(
927
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalledWith(
928
928
  'ws-feat', 'feat', seedPath, USER,
929
929
  );
930
930
  });
@@ -937,7 +937,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
937
937
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase/Mine'), { recursive: true });
938
938
  const seedPath = 'knowledge-base/KnowledgeBase/Mine/access.md';
939
939
  await fs.writeFile(path.join(root, seedPath), 'prior\n');
940
- const workflow = makeWorkflow();
940
+ const versionControl = makeVersionControl();
941
941
  const creatorAccess = makeCreatorAccess();
942
942
  creatorAccess.planForCreate.mockResolvedValue({
943
943
  kind: 'seed-access-md',
@@ -946,7 +946,7 @@ describe('LockingFilesystem — creator read grants on creation', () => {
946
946
  });
947
947
  const fsLayer = new LockingFilesystem(
948
948
  { basePath: root, contained: true },
949
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
949
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
950
950
  );
951
951
  const original = LocalFilesystem.prototype.writeFile;
952
952
  const spy = vi
@@ -962,12 +962,12 @@ describe('LockingFilesystem — creator read grants on creation', () => {
962
962
  }
963
963
  // The unrecoverable seed path releases via the discard, everything else
964
964
  // untouched-or-no-commit as usual; the batch itself still succeeds.
965
- expect(workflow.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
966
- expect(workflow.releaseLockUntouched).not.toHaveBeenCalledWith(
965
+ expect(versionControl.releaseLockNoCommit).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
966
+ expect(versionControl.releaseLockUntouched).not.toHaveBeenCalledWith(
967
967
  'ws-feat', 'feat', seedPath, USER,
968
968
  );
969
- expect(workflow.releaseLock).not.toHaveBeenCalled();
970
- const commitPaths = (workflow.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
969
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
970
+ const commitPaths = (versionControl.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
971
971
  expect(commitPaths).toEqual(['knowledge-base/KnowledgeBase/Mine/doc.md']);
972
972
  });
973
973
 
@@ -980,8 +980,8 @@ describe('LockingFilesystem — creator read grants on creation', () => {
980
980
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase/Mine'), { recursive: true });
981
981
  const seedPath = 'knowledge-base/KnowledgeBase/Mine/access.md';
982
982
  await fs.writeFile(path.join(root, seedPath), 'grant already present\n');
983
- const workflow = makeWorkflow();
984
- (workflow.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
983
+ const versionControl = makeVersionControl();
984
+ (versionControl.commitChanges as ReturnType<typeof vi.fn>).mockRejectedValue(
985
985
  new PushNeedsAgentResolutionError('feat', '(batch)', 'non-fast-forward', 'rebase failed'),
986
986
  );
987
987
  const creatorAccess = makeCreatorAccess();
@@ -992,21 +992,21 @@ describe('LockingFilesystem — creator read grants on creation', () => {
992
992
  });
993
993
  const fsLayer = new LockingFilesystem(
994
994
  { basePath: root, contained: true },
995
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
995
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
996
996
  );
997
997
  await expect(
998
998
  fsLayer.writeFiles([{ path: 'knowledge-base/KnowledgeBase/Mine/doc.md', content: 'body' }], 'batch'),
999
999
  ).rejects.toBeInstanceOf(PushNeedsAgentResolutionError);
1000
- expect(workflow.releaseLock).toHaveBeenCalledWith(
1000
+ expect(versionControl.releaseLock).toHaveBeenCalledWith(
1001
1001
  'ws-feat', 'feat', 'knowledge-base/KnowledgeBase/Mine/doc.md', USER,
1002
1002
  );
1003
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
1004
- expect(workflow.releaseLock).not.toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
1005
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1003
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
1004
+ expect(versionControl.releaseLock).not.toHaveBeenCalledWith('ws-feat', 'feat', seedPath, USER);
1005
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1006
1006
  });
1007
1007
 
1008
1008
  it('a LANDED seed rides the commit scope with the caller paths', async () => {
1009
- const workflow = makeWorkflow();
1009
+ const versionControl = makeVersionControl();
1010
1010
  const creatorAccess = makeCreatorAccess();
1011
1011
  creatorAccess.planForCreate.mockResolvedValue({
1012
1012
  kind: 'seed-access-md',
@@ -1015,20 +1015,20 @@ describe('LockingFilesystem — creator read grants on creation', () => {
1015
1015
  });
1016
1016
  const fsLayer = new LockingFilesystem(
1017
1017
  { basePath: root, contained: true },
1018
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
1018
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
1019
1019
  );
1020
1020
  await fsLayer.writeFiles([{ path: 'knowledge-base/KnowledgeBase/Mine/doc.md', content: 'body' }], 'batch');
1021
- const commitPaths = (workflow.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
1021
+ const commitPaths = (versionControl.commitChanges as ReturnType<typeof vi.fn>).mock.calls[0][3];
1022
1022
  expect(commitPaths).toEqual(['knowledge-base/KnowledgeBase/Mine/doc.md', 'knowledge-base/KnowledgeBase/Mine/access.md']);
1023
1023
  });
1024
1024
 
1025
1025
  it('a planner failure never blocks the write', async () => {
1026
- const workflow = makeWorkflow();
1026
+ const versionControl = makeVersionControl();
1027
1027
  const creatorAccess = makeCreatorAccess();
1028
1028
  creatorAccess.planForCreate.mockRejectedValue(new Error('planner down'));
1029
1029
  const fsLayer = new LockingFilesystem(
1030
1030
  { basePath: root, contained: true },
1031
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
1031
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess },
1032
1032
  );
1033
1033
  await fsLayer.writeFile('knowledge-base/KnowledgeBase/new.md', 'x');
1034
1034
  expect(await fs.readFile(path.join(root, 'knowledge-base/KnowledgeBase/new.md'), 'utf-8')).toBe('x');
@@ -1064,50 +1064,50 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1064
1064
  };
1065
1065
  }
1066
1066
 
1067
- function layer(workflow: IWorkflowService) {
1067
+ function layer(versionControl: IVersionControlService) {
1068
1068
  const creatorAccess = makeCreatorAccess();
1069
1069
  const validateWrite = vi.fn();
1070
1070
  const fsLayer = new LockingFilesystem(
1071
1071
  { basePath: root, contained: true },
1072
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess, validateWrite },
1072
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, creatorAccess, validateWrite },
1073
1073
  );
1074
1074
  return { fsLayer, creatorAccess, validateWrite };
1075
1075
  }
1076
1076
 
1077
1077
  async function expectRefused(
1078
1078
  op: Promise<unknown>,
1079
- workflow: IWorkflowService,
1079
+ versionControl: IVersionControlService,
1080
1080
  creatorAccess: ReturnType<typeof makeCreatorAccess>,
1081
1081
  validateWrite: ReturnType<typeof vi.fn>,
1082
1082
  ): Promise<void> {
1083
- await expect(op).rejects.toBeInstanceOf(WorkflowValidationError);
1083
+ await expect(op).rejects.toBeInstanceOf(VersionControlValidationError);
1084
1084
  // The corrected path is spelled out, under the clone folder.
1085
1085
  await expect(op).rejects.toThrow('"knowledge-base/KnowledgeBase/Reviews');
1086
1086
  // Refused before any side effect.
1087
- expect(workflow.acquireLock).not.toHaveBeenCalled();
1088
- expect(workflow.releaseLock).not.toHaveBeenCalled();
1089
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1090
- expect(workflow.commitChanges).not.toHaveBeenCalled();
1087
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
1088
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
1089
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1090
+ expect(versionControl.commitChanges).not.toHaveBeenCalled();
1091
1091
  expect(creatorAccess.planForCreate).not.toHaveBeenCalled();
1092
1092
  expect(validateWrite).not.toHaveBeenCalled();
1093
1093
  await expect(fs.access(path.join(root, 'KnowledgeBase'))).rejects.toBeDefined();
1094
1094
  }
1095
1095
 
1096
1096
  it('writeFile', async () => {
1097
- const workflow = makeWorkflow();
1098
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1099
- await expectRefused(fsLayer.writeFile(STRAY, '<p>review</p>'), workflow, creatorAccess, validateWrite);
1097
+ const versionControl = makeVersionControl();
1098
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1099
+ await expectRefused(fsLayer.writeFile(STRAY, '<p>review</p>'), versionControl, creatorAccess, validateWrite);
1100
1100
  });
1101
1101
 
1102
1102
  it('appendFile', async () => {
1103
- const workflow = makeWorkflow();
1104
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1105
- await expectRefused(fsLayer.appendFile('KnowledgeBase/Reviews/review.log', 'line\n'), workflow, creatorAccess, validateWrite);
1103
+ const versionControl = makeVersionControl();
1104
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1105
+ await expectRefused(fsLayer.appendFile('KnowledgeBase/Reviews/review.log', 'line\n'), versionControl, creatorAccess, validateWrite);
1106
1106
  });
1107
1107
 
1108
1108
  it('writeFiles refuses the whole batch when one path is outside: nothing written, nothing locked', async () => {
1109
- const workflow = makeWorkflow();
1110
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1109
+ const versionControl = makeVersionControl();
1110
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1111
1111
  await expectRefused(
1112
1112
  fsLayer.writeFiles(
1113
1113
  [
@@ -1116,7 +1116,7 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1116
1116
  ],
1117
1117
  'batch',
1118
1118
  ),
1119
- workflow,
1119
+ versionControl,
1120
1120
  creatorAccess,
1121
1121
  validateWrite,
1122
1122
  );
@@ -1124,19 +1124,19 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1124
1124
  });
1125
1125
 
1126
1126
  it('mkdir', async () => {
1127
- const workflow = makeWorkflow();
1128
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1129
- await expectRefused(fsLayer.mkdir('KnowledgeBase/Reviews'), workflow, creatorAccess, validateWrite);
1127
+ const versionControl = makeVersionControl();
1128
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1129
+ await expectRefused(fsLayer.mkdir('KnowledgeBase/Reviews'), versionControl, creatorAccess, validateWrite);
1130
1130
  });
1131
1131
 
1132
1132
  it('moveFile refuses a destination outside the repository and leaves the source in place', async () => {
1133
1133
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase'), { recursive: true });
1134
1134
  await fs.writeFile(path.join(root, 'knowledge-base/KnowledgeBase/PR-12.html'), 'x');
1135
- const workflow = makeWorkflow();
1136
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1135
+ const versionControl = makeVersionControl();
1136
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1137
1137
  await expectRefused(
1138
1138
  fsLayer.moveFile('knowledge-base/KnowledgeBase/PR-12.html', STRAY),
1139
- workflow,
1139
+ versionControl,
1140
1140
  creatorAccess,
1141
1141
  validateWrite,
1142
1142
  );
@@ -1146,11 +1146,11 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1146
1146
  it('copyFile refuses a destination outside the repository', async () => {
1147
1147
  await fs.mkdir(path.join(root, 'knowledge-base/KnowledgeBase'), { recursive: true });
1148
1148
  await fs.writeFile(path.join(root, 'knowledge-base/KnowledgeBase/PR-12.html'), 'x');
1149
- const workflow = makeWorkflow();
1150
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1149
+ const versionControl = makeVersionControl();
1150
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1151
1151
  await expectRefused(
1152
1152
  fsLayer.copyFile('knowledge-base/KnowledgeBase/PR-12.html', STRAY),
1153
- workflow,
1153
+ versionControl,
1154
1154
  creatorAccess,
1155
1155
  validateWrite,
1156
1156
  );
@@ -1160,20 +1160,20 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1160
1160
  // Containment is checked against the WORKSPACE dir, so the underlying
1161
1161
  // filesystem would happily resolve `knowledge-base/../stray.md` to a file
1162
1162
  // next to the clone. The prefix alone is not proof of being inside.
1163
- const workflow = makeWorkflow();
1164
- const { fsLayer, creatorAccess, validateWrite } = layer(workflow);
1165
- await expect(fsLayer.writeFile('knowledge-base/../stray.md', 'x')).rejects.toBeInstanceOf(WorkflowValidationError);
1166
- expect(workflow.acquireLock).not.toHaveBeenCalled();
1163
+ const versionControl = makeVersionControl();
1164
+ const { fsLayer, creatorAccess, validateWrite } = layer(versionControl);
1165
+ await expect(fsLayer.writeFile('knowledge-base/../stray.md', 'x')).rejects.toBeInstanceOf(VersionControlValidationError);
1166
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
1167
1167
  expect(creatorAccess.planForCreate).not.toHaveBeenCalled();
1168
1168
  expect(validateWrite).not.toHaveBeenCalled();
1169
1169
  await expect(fs.access(path.join(root, 'stray.md'))).rejects.toBeDefined();
1170
1170
  });
1171
1171
 
1172
1172
  it('the folder is matched as a whole segment: `knowledge-based/x.md` is outside too', async () => {
1173
- const workflow = makeWorkflow();
1174
- const { fsLayer } = layer(workflow);
1175
- await expect(fsLayer.writeFile('knowledge-based/x.md', 'x')).rejects.toBeInstanceOf(WorkflowValidationError);
1176
- expect(workflow.acquireLock).not.toHaveBeenCalled();
1173
+ const versionControl = makeVersionControl();
1174
+ const { fsLayer } = layer(versionControl);
1175
+ await expect(fsLayer.writeFile('knowledge-based/x.md', 'x')).rejects.toBeInstanceOf(VersionControlValidationError);
1176
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
1177
1177
  });
1178
1178
 
1179
1179
  it('deleteFile still removes a file the old behaviour left beside the repository', async () => {
@@ -1181,8 +1181,8 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1181
1181
  // created before this guard existed must be able to clean it up.
1182
1182
  await fs.mkdir(path.join(root, 'KnowledgeBase/Reviews'), { recursive: true });
1183
1183
  await fs.writeFile(path.join(root, STRAY), 'stray');
1184
- const workflow = makeWorkflow();
1185
- const { fsLayer } = layer(workflow);
1184
+ const versionControl = makeVersionControl();
1185
+ const { fsLayer } = layer(versionControl);
1186
1186
  await fsLayer.deleteFile(STRAY);
1187
1187
  await expect(fs.access(path.join(root, STRAY))).rejects.toBeDefined();
1188
1188
  });
@@ -1191,8 +1191,8 @@ describe('LockingFilesystem refuses to create anything outside the repository fo
1191
1191
  await fs.mkdir(path.join(root, 'KnowledgeBase/Reviews'), { recursive: true });
1192
1192
  await fs.writeFile(path.join(root, STRAY), 'stray');
1193
1193
  await fs.mkdir(path.join(root, 'knowledge-base'), { recursive: true });
1194
- const workflow = makeWorkflow();
1195
- const { fsLayer } = layer(workflow);
1194
+ const versionControl = makeVersionControl();
1195
+ const { fsLayer } = layer(versionControl);
1196
1196
  await fsLayer.moveFile(STRAY, CORRECTED);
1197
1197
  expect(await fs.readFile(path.join(root, CORRECTED), 'utf-8')).toBe('stray');
1198
1198
  await expect(fs.access(path.join(root, STRAY))).rejects.toBeDefined();
@@ -1217,85 +1217,85 @@ describe('LockingFilesystem — the agent roles.yaml gate covers every op that l
1217
1217
  await fs.rm(root, { recursive: true, force: true });
1218
1218
  });
1219
1219
 
1220
- function layer(workflow: IWorkflowService) {
1220
+ function layer(versionControl: IVersionControlService) {
1221
1221
  const validateWrite = makeAgentRolesYamlWriteValidator(KB, () =>
1222
1222
  fs.readFile(path.join(root, ROLES), 'utf-8').catch(() => null),
1223
1223
  );
1224
1224
  return new LockingFilesystem(
1225
1225
  { basePath: root, contained: true },
1226
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite },
1226
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite },
1227
1227
  );
1228
1228
  }
1229
1229
 
1230
- async function expectRefused(op: Promise<unknown>, workflow: IWorkflowService): Promise<void> {
1230
+ async function expectRefused(op: Promise<unknown>, versionControl: IVersionControlService): Promise<void> {
1231
1231
  await expect(op).rejects.toBeInstanceOf(RolesYamlNewRoleError);
1232
- expect(workflow.acquireLock).not.toHaveBeenCalled();
1232
+ expect(versionControl.acquireLock).not.toHaveBeenCalled();
1233
1233
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(CURRENT);
1234
1234
  }
1235
1235
 
1236
1236
  it('writeFile', async () => {
1237
- const workflow = makeWorkflow();
1238
- await expectRefused(layer(workflow).writeFile(ROLES, CURRENT + NEW_ROLE), workflow);
1237
+ const versionControl = makeVersionControl();
1238
+ await expectRefused(layer(versionControl).writeFile(ROLES, CURRENT + NEW_ROLE), versionControl);
1239
1239
  });
1240
1240
 
1241
1241
  it('writeFiles refuses the whole batch', async () => {
1242
- const workflow = makeWorkflow();
1242
+ const versionControl = makeVersionControl();
1243
1243
  await expectRefused(
1244
- layer(workflow).writeFiles([{ path: `${KB}/KnowledgeBase/ok.md`, content: 'fine' }, { path: ROLES, content: CURRENT + NEW_ROLE }], 'batch'),
1245
- workflow,
1244
+ layer(versionControl).writeFiles([{ path: `${KB}/KnowledgeBase/ok.md`, content: 'fine' }, { path: ROLES, content: CURRENT + NEW_ROLE }], 'batch'),
1245
+ versionControl,
1246
1246
  );
1247
1247
  await expect(fs.access(path.join(root, KB, 'KnowledgeBase/ok.md'))).rejects.toBeDefined();
1248
1248
  });
1249
1249
 
1250
1250
  it('appendFile, judged by the file it would leave', async () => {
1251
- const workflow = makeWorkflow();
1252
- await expectRefused(layer(workflow).appendFile(ROLES, NEW_ROLE), workflow);
1251
+ const versionControl = makeVersionControl();
1252
+ await expectRefused(layer(versionControl).appendFile(ROLES, NEW_ROLE), versionControl);
1253
1253
  });
1254
1254
 
1255
1255
  it('copyFile, judged by the source bytes', async () => {
1256
- const workflow = makeWorkflow();
1257
- await expectRefused(layer(workflow).copyFile(DRAFT, ROLES, { overwrite: true }), workflow);
1256
+ const versionControl = makeVersionControl();
1257
+ await expectRefused(layer(versionControl).copyFile(DRAFT, ROLES, { overwrite: true }), versionControl);
1258
1258
  });
1259
1259
 
1260
1260
  it('moveFile, leaving the source where it was', async () => {
1261
- const workflow = makeWorkflow();
1262
- await expectRefused(layer(workflow).moveFile(DRAFT, ROLES, { overwrite: true }), workflow);
1261
+ const versionControl = makeVersionControl();
1262
+ await expectRefused(layer(versionControl).moveFile(DRAFT, ROLES, { overwrite: true }), versionControl);
1263
1263
  expect(await fs.readFile(path.join(root, DRAFT), 'utf-8')).toBe(CURRENT + NEW_ROLE);
1264
1264
  });
1265
1265
 
1266
1266
  it('a members-only write and append land as before', async () => {
1267
- const workflow = makeWorkflow();
1268
- const fsLayer = layer(workflow);
1267
+ const versionControl = makeVersionControl();
1268
+ const fsLayer = layer(versionControl);
1269
1269
  await fsLayer.writeFile(ROLES, `${CURRENT} - b@x.eu\n`);
1270
1270
  await fsLayer.appendFile(ROLES, ' - c@x.eu\n');
1271
1271
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(`${CURRENT} - b@x.eu\n - c@x.eu\n`);
1272
- expect(workflow.releaseLock).toHaveBeenCalledTimes(2);
1272
+ expect(versionControl.releaseLock).toHaveBeenCalledTimes(2);
1273
1273
  });
1274
1274
 
1275
1275
  describe('judged again under the lock, against what a concurrent writer left there', () => {
1276
1276
  const WITH_PHOENIX = CURRENT + NEW_ROLE;
1277
1277
 
1278
- /** A workflow whose acquire lets `race` rewrite disk first — the lock holder that finished just before us. */
1279
- function racingWorkflow(race: () => Promise<void>): IWorkflowService {
1280
- const workflow = makeWorkflow();
1281
- const acquired = (workflow.acquireLock as ReturnType<typeof vi.fn>).getMockImplementation()!;
1278
+ /** A version-control service whose acquire lets `race` rewrite disk first — the lock holder that finished just before us. */
1279
+ function racingVersionControl(race: () => Promise<void>): IVersionControlService {
1280
+ const versionControl = makeVersionControl();
1281
+ const acquired = (versionControl.acquireLock as ReturnType<typeof vi.fn>).getMockImplementation()!;
1282
1282
  let raced = false;
1283
- (workflow.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async (...args: unknown[]) => {
1283
+ (versionControl.acquireLock as ReturnType<typeof vi.fn>).mockImplementation(async (...args: unknown[]) => {
1284
1284
  if (!raced) {
1285
1285
  raced = true;
1286
1286
  await race();
1287
1287
  }
1288
1288
  return acquired(...args);
1289
1289
  });
1290
- return workflow;
1290
+ return versionControl;
1291
1291
  }
1292
1292
 
1293
- async function expectRefusedUnderLock(op: Promise<unknown>, workflow: IWorkflowService, locks: number): Promise<void> {
1293
+ async function expectRefusedUnderLock(op: Promise<unknown>, versionControl: IVersionControlService, locks: number): Promise<void> {
1294
1294
  await expect(op).rejects.toBeInstanceOf(RolesYamlNewRoleError);
1295
- expect(workflow.releaseLockUntouched).toHaveBeenCalledTimes(locks);
1296
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1297
- expect(workflow.releaseLock).not.toHaveBeenCalled();
1298
- expect(workflow.commitChanges).not.toHaveBeenCalled();
1295
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledTimes(locks);
1296
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1297
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
1298
+ expect(versionControl.commitChanges).not.toHaveBeenCalled();
1299
1299
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(CURRENT);
1300
1300
  }
1301
1301
 
@@ -1306,15 +1306,15 @@ describe('LockingFilesystem — the agent roles.yaml gate covers every op that l
1306
1306
  });
1307
1307
 
1308
1308
  it('writeFile cannot reinstate a role deleted while it waited for the lock', async () => {
1309
- const workflow = racingWorkflow(deletePhoenix);
1310
- await expectRefusedUnderLock(layer(workflow).writeFile(ROLES, `${WITH_PHOENIX} - q@x.eu\n`), workflow, 1);
1309
+ const versionControl = racingVersionControl(deletePhoenix);
1310
+ await expectRefusedUnderLock(layer(versionControl).writeFile(ROLES, `${WITH_PHOENIX} - q@x.eu\n`), versionControl, 1);
1311
1311
  });
1312
1312
 
1313
1313
  it('writeFiles re-judges the whole batch once every lock is held', async () => {
1314
- const workflow = racingWorkflow(deletePhoenix);
1314
+ const versionControl = racingVersionControl(deletePhoenix);
1315
1315
  await expectRefusedUnderLock(
1316
- layer(workflow).writeFiles([{ path: `${KB}/KnowledgeBase/ok.md`, content: 'fine' }, { path: ROLES, content: WITH_PHOENIX }], 'batch'),
1317
- workflow,
1316
+ layer(versionControl).writeFiles([{ path: `${KB}/KnowledgeBase/ok.md`, content: 'fine' }, { path: ROLES, content: WITH_PHOENIX }], 'batch'),
1317
+ versionControl,
1318
1318
  2,
1319
1319
  );
1320
1320
  await expect(fs.access(path.join(root, KB, 'KnowledgeBase/ok.md'))).rejects.toBeDefined();
@@ -1324,31 +1324,31 @@ describe('LockingFilesystem — the agent roles.yaml gate covers every op that l
1324
1324
  // Appending never adds a role the file had before, so what a race can
1325
1325
  // change is validity: the same line appended to the rewritten file breaks it.
1326
1326
  const rewritten = 'roles:\n Admin:\n - a@x.eu';
1327
- const workflow = racingWorkflow(() => fs.writeFile(path.join(root, ROLES), rewritten));
1328
- await expect(layer(workflow).appendFile(ROLES, ' - b@x.eu\n')).rejects.toBeInstanceOf(RolesYamlInvalidError);
1329
- expect(workflow.releaseLockUntouched).toHaveBeenCalledTimes(1);
1330
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1327
+ const versionControl = racingVersionControl(() => fs.writeFile(path.join(root, ROLES), rewritten));
1328
+ await expect(layer(versionControl).appendFile(ROLES, ' - b@x.eu\n')).rejects.toBeInstanceOf(RolesYamlInvalidError);
1329
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledTimes(1);
1330
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1331
1331
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(rewritten);
1332
1332
  });
1333
1333
 
1334
1334
  it('copyFile judges — and writes — the source bytes read under the lock', async () => {
1335
1335
  await fs.writeFile(path.join(root, DRAFT), WITH_PHOENIX);
1336
- const workflow = racingWorkflow(deletePhoenix);
1337
- await expectRefusedUnderLock(layer(workflow).copyFile(DRAFT, ROLES, { overwrite: true }), workflow, 1);
1336
+ const versionControl = racingVersionControl(deletePhoenix);
1337
+ await expectRefusedUnderLock(layer(versionControl).copyFile(DRAFT, ROLES, { overwrite: true }), versionControl, 1);
1338
1338
  });
1339
1339
 
1340
1340
  it('moveFile re-judges with both locks held, and both release untouched', async () => {
1341
1341
  await fs.writeFile(path.join(root, DRAFT), WITH_PHOENIX);
1342
- const workflow = racingWorkflow(deletePhoenix);
1343
- await expectRefusedUnderLock(layer(workflow).moveFile(DRAFT, ROLES, { overwrite: true }), workflow, 2);
1342
+ const versionControl = racingVersionControl(deletePhoenix);
1343
+ await expectRefusedUnderLock(layer(versionControl).moveFile(DRAFT, ROLES, { overwrite: true }), versionControl, 2);
1344
1344
  expect(await fs.readFile(path.join(root, DRAFT), 'utf-8')).toBe(WITH_PHOENIX);
1345
1345
  });
1346
1346
 
1347
1347
  it('a members-only write whose roles all survive the wait still lands', async () => {
1348
- const workflow = racingWorkflow(() => fs.writeFile(path.join(root, ROLES), `${WITH_PHOENIX} - q@x.eu\n`));
1349
- await layer(workflow).writeFile(ROLES, `${CURRENT} - b@x.eu\n`);
1348
+ const versionControl = racingVersionControl(() => fs.writeFile(path.join(root, ROLES), `${WITH_PHOENIX} - q@x.eu\n`));
1349
+ await layer(versionControl).writeFile(ROLES, `${CURRENT} - b@x.eu\n`);
1350
1350
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(`${CURRENT} - b@x.eu\n`);
1351
- expect(workflow.releaseLock).toHaveBeenCalledTimes(1);
1351
+ expect(versionControl.releaseLock).toHaveBeenCalledTimes(1);
1352
1352
  });
1353
1353
 
1354
1354
  /** A validator that passes before the lock and refuses under it by throwing a bare string. */
@@ -1363,36 +1363,36 @@ describe('LockingFilesystem — the agent roles.yaml gate covers every op that l
1363
1363
  }
1364
1364
 
1365
1365
  it('a check that throws a non-Error still releases untouched, and the caller gets that value', async () => {
1366
- const workflow = makeWorkflow();
1366
+ const versionControl = makeVersionControl();
1367
1367
  const fsLayer = new LockingFilesystem(
1368
1368
  { basePath: root, contained: true },
1369
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite: refusesUnderLockWith('refused') },
1369
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite: refusesUnderLockWith('refused') },
1370
1370
  );
1371
1371
  await expect(fsLayer.writeFile(ROLES, CURRENT)).rejects.toBe('refused');
1372
- expect(workflow.releaseLockUntouched).toHaveBeenCalledTimes(1);
1373
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1372
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledTimes(1);
1373
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1374
1374
  expect(await fs.readFile(path.join(root, ROLES), 'utf-8')).toBe(WITH_PHOENIX);
1375
1375
  });
1376
1376
 
1377
1377
  it('a move whose check throws a non-Error releases BOTH locks untouched', async () => {
1378
- const workflow = makeWorkflow();
1378
+ const versionControl = makeVersionControl();
1379
1379
  const fsLayer = new LockingFilesystem(
1380
1380
  { basePath: root, contained: true },
1381
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite: refusesUnderLockWith('refused') },
1381
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite: refusesUnderLockWith('refused') },
1382
1382
  );
1383
1383
  await expect(fsLayer.moveFile(DRAFT, ROLES, { overwrite: true })).rejects.toBe('refused');
1384
- expect(workflow.releaseLockUntouched).toHaveBeenCalledTimes(2);
1385
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1384
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledTimes(2);
1385
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1386
1386
  expect(await fs.readFile(path.join(root, DRAFT), 'utf-8')).toBe(WITH_PHOENIX);
1387
1387
  });
1388
1388
  });
1389
1389
 
1390
1390
  it('a copy elsewhere never reads its source for a validator that does not claim the destination', async () => {
1391
- const workflow = makeWorkflow();
1391
+ const versionControl = makeVersionControl();
1392
1392
  const validateWrite = Object.assign(vi.fn(), { appliesTo: vi.fn(() => false) });
1393
1393
  const fsLayer = new LockingFilesystem(
1394
1394
  { basePath: root, contained: true },
1395
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite },
1395
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB, validateWrite },
1396
1396
  );
1397
1397
  await fsLayer.copyFile(DRAFT, `${KB}/KnowledgeBase/copy.yaml`);
1398
1398
  expect(validateWrite.appliesTo).toHaveBeenCalledWith(`${KB}/KnowledgeBase/copy.yaml`);
@@ -1421,7 +1421,7 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1421
1421
  await fs.writeFile(onDisk(SOURCE), 'source');
1422
1422
  fsLayer = new LockingFilesystem(
1423
1423
  { basePath: root, contained: true },
1424
- { workflow: makeWorkflow(), workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1424
+ { versionControl: makeVersionControl(), workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1425
1425
  );
1426
1426
  });
1427
1427
 
@@ -1463,10 +1463,10 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1463
1463
  * every lock it unwinds releases untouched.
1464
1464
  */
1465
1465
  it('releases UNTOUCHED when the destination is taken, so a lost race cannot eat the winner', async () => {
1466
- const workflow = makeWorkflow();
1466
+ const versionControl = makeVersionControl();
1467
1467
  const layer = new LockingFilesystem(
1468
1468
  { basePath: root, contained: true },
1469
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1469
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1470
1470
  );
1471
1471
  await fs.writeFile(onDisk(TAKEN), 'the winner of the race');
1472
1472
 
@@ -1475,10 +1475,10 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1475
1475
  // Both ends of the move: the destination is the one holding the winner's
1476
1476
  // bytes, and the source was not written either.
1477
1477
  for (const p of [SOURCE, TAKEN]) {
1478
- expect(workflow.releaseLockUntouched, p).toHaveBeenCalledWith('ws-feat', 'feat', p, USER);
1478
+ expect(versionControl.releaseLockUntouched, p).toHaveBeenCalledWith('ws-feat', 'feat', p, USER);
1479
1479
  }
1480
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1481
- expect(workflow.releaseLock).not.toHaveBeenCalled();
1480
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1481
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
1482
1482
  expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
1483
1483
  });
1484
1484
 
@@ -1489,10 +1489,10 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1489
1489
  * than discarding the winner's queued file.
1490
1490
  */
1491
1491
  it('an exclusive write onto a taken name is the same refusal, and releases UNTOUCHED', async () => {
1492
- const workflow = makeWorkflow();
1492
+ const versionControl = makeVersionControl();
1493
1493
  const layer = new LockingFilesystem(
1494
1494
  { basePath: root, contained: true },
1495
- { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1495
+ { versionControl, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1496
1496
  );
1497
1497
  await fs.writeFile(onDisk(TAKEN), 'the winner of the race');
1498
1498
 
@@ -1501,9 +1501,9 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1501
1501
  message: 'A file named taken.md already exists in KnowledgeBase.',
1502
1502
  });
1503
1503
 
1504
- expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', TAKEN, USER);
1505
- expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1506
- expect(workflow.releaseLock).not.toHaveBeenCalled();
1504
+ expect(versionControl.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', TAKEN, USER);
1505
+ expect(versionControl.releaseLockNoCommit).not.toHaveBeenCalled();
1506
+ expect(versionControl.releaseLock).not.toHaveBeenCalled();
1507
1507
  expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
1508
1508
  });
1509
1509