@bevel-software/platform-core-backend 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/core/create-core-server.d.ts.map +1 -1
  2. package/dist/core/create-core-server.js +32 -2
  3. package/dist/core/create-core-server.js.map +1 -1
  4. package/dist/core/create-core-services.d.ts +13 -1
  5. package/dist/core/create-core-services.d.ts.map +1 -1
  6. package/dist/core/create-core-services.js +51 -5
  7. package/dist/core/create-core-services.js.map +1 -1
  8. package/dist/modules/access/access-requests.contract.d.ts +75 -0
  9. package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
  10. package/dist/modules/access/access-requests.contract.js +20 -0
  11. package/dist/modules/access/access-requests.contract.js.map +1 -0
  12. package/dist/modules/access/access-requests.routes.d.ts +35 -0
  13. package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
  14. package/dist/modules/access/access-requests.routes.js +237 -0
  15. package/dist/modules/access/access-requests.routes.js.map +1 -0
  16. package/dist/modules/access/access-requests.service.d.ts +123 -0
  17. package/dist/modules/access/access-requests.service.d.ts.map +1 -0
  18. package/dist/modules/access/access-requests.service.js +337 -0
  19. package/dist/modules/access/access-requests.service.js.map +1 -0
  20. package/dist/modules/access-model/access-grammar.d.ts +12 -0
  21. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  22. package/dist/modules/access-model/access-grammar.js +24 -0
  23. package/dist/modules/access-model/access-grammar.js.map +1 -1
  24. package/dist/modules/auth/auth.service.d.ts +26 -1
  25. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  26. package/dist/modules/auth/auth.service.js +11 -2
  27. package/dist/modules/auth/auth.service.js.map +1 -1
  28. package/dist/modules/github-app/github-app.client.d.ts +101 -0
  29. package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
  30. package/dist/modules/github-app/github-app.client.js +222 -0
  31. package/dist/modules/github-app/github-app.client.js.map +1 -0
  32. package/dist/modules/github-app/github-app.connection.d.ts +102 -0
  33. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
  34. package/dist/modules/github-app/github-app.connection.js +185 -0
  35. package/dist/modules/github-app/github-app.connection.js.map +1 -0
  36. package/dist/modules/github-app/github-app.routes.d.ts +59 -0
  37. package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
  38. package/dist/modules/github-app/github-app.routes.js +323 -0
  39. package/dist/modules/github-app/github-app.routes.js.map +1 -0
  40. package/dist/modules/github-app/index.d.ts +4 -0
  41. package/dist/modules/github-app/index.d.ts.map +1 -0
  42. package/dist/modules/github-app/index.js +4 -0
  43. package/dist/modules/github-app/index.js.map +1 -0
  44. package/dist/modules/kb-fs/remote-url.d.ts +22 -0
  45. package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
  46. package/dist/modules/kb-fs/remote-url.js +35 -0
  47. package/dist/modules/kb-fs/remote-url.js.map +1 -0
  48. package/dist/modules/plugins/join-proposals.d.ts +17 -5
  49. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  50. package/dist/modules/plugins/join-proposals.js +76 -22
  51. package/dist/modules/plugins/join-proposals.js.map +1 -1
  52. package/dist/modules/plugins/join-requests.service.d.ts +111 -18
  53. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  54. package/dist/modules/plugins/join-requests.service.js +173 -29
  55. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  56. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  57. package/dist/modules/plugins/plugins.routes.js +3 -2
  58. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  59. package/dist/modules/settings/deployment-settings.service.d.ts +22 -1
  60. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  61. package/dist/modules/settings/deployment-settings.service.js +93 -4
  62. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  63. package/dist/modules/settings/managed-repository.d.ts +43 -0
  64. package/dist/modules/settings/managed-repository.d.ts.map +1 -0
  65. package/dist/modules/settings/managed-repository.js +60 -0
  66. package/dist/modules/settings/managed-repository.js.map +1 -0
  67. package/dist/modules/settings/repository-source.d.ts +128 -0
  68. package/dist/modules/settings/repository-source.d.ts.map +1 -0
  69. package/dist/modules/settings/repository-source.js +150 -0
  70. package/dist/modules/settings/repository-source.js.map +1 -0
  71. package/dist/modules/settings/setup.routes.d.ts +27 -4
  72. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  73. package/dist/modules/settings/setup.routes.js +187 -13
  74. package/dist/modules/settings/setup.routes.js.map +1 -1
  75. package/dist/modules/skills/skill-access-requests.routes.d.ts +6 -8
  76. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  77. package/dist/modules/skills/skill-access-requests.routes.js +24 -63
  78. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  79. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  80. package/dist/modules/tool-helpers/tool-context.js +15 -0
  81. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  82. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  83. package/dist/modules/workflow/agent-tools/workflow.tools.js +38 -2
  84. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  85. package/dist/modules/workflow/git/git.service.d.ts +3 -1
  86. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  87. package/dist/modules/workflow/git/git.service.js +17 -4
  88. package/dist/modules/workflow/git/git.service.js.map +1 -1
  89. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  90. package/dist/modules/workflow/git/node-git-runner.js +5 -0
  91. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  92. package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
  93. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  94. package/dist/modules/workflow/git/pull-request.service.js +99 -0
  95. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  96. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  97. package/dist/modules/workflow/workflow.routes.js +3 -1
  98. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  99. package/dist/modules/workflow/workflow.service.d.ts +34 -7
  100. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  101. package/dist/modules/workflow/workflow.service.js +148 -48
  102. package/dist/modules/workflow/workflow.service.js.map +1 -1
  103. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +66 -0
  104. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  105. package/dist/modules/workspace/startup/kb-startup-runner.js +148 -0
  106. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  107. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  108. package/dist/modules/workspace/workspace.service.js +17 -1
  109. package/dist/modules/workspace/workspace.service.js.map +1 -1
  110. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  111. package/dist/modules/workspace/workspace.tools.js +28 -2
  112. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  113. package/dist/shared/domain-errors.d.ts +39 -0
  114. package/dist/shared/domain-errors.d.ts.map +1 -1
  115. package/dist/shared/domain-errors.js +55 -0
  116. package/dist/shared/domain-errors.js.map +1 -1
  117. package/dist/shared/git.contract.d.ts +10 -0
  118. package/dist/shared/git.contract.d.ts.map +1 -1
  119. package/dist/shared/git.contract.js.map +1 -1
  120. package/package.json +3 -3
  121. package/src/core/create-core-server.ts +37 -1
  122. package/src/core/create-core-services.ts +65 -8
  123. package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
  124. package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
  125. package/src/modules/access/access-requests.contract.ts +93 -0
  126. package/src/modules/access/access-requests.routes.ts +286 -0
  127. package/src/modules/access/access-requests.service.ts +420 -0
  128. package/src/modules/access-model/access-grammar.ts +22 -0
  129. package/src/modules/auth/__tests__/auth.service.test.ts +38 -0
  130. package/src/modules/auth/auth.service.ts +28 -1
  131. package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
  132. package/src/modules/github-app/github-app.client.ts +268 -0
  133. package/src/modules/github-app/github-app.connection.ts +204 -0
  134. package/src/modules/github-app/github-app.routes.ts +359 -0
  135. package/src/modules/github-app/index.ts +19 -0
  136. package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
  137. package/src/modules/kb-fs/remote-url.ts +35 -0
  138. package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
  139. package/src/modules/plugins/__tests__/join-requests.service.test.ts +27 -11
  140. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
  141. package/src/modules/plugins/__tests__/plugins.routes.test.ts +1 -1
  142. package/src/modules/plugins/join-proposals.ts +87 -19
  143. package/src/modules/plugins/join-requests.service.ts +199 -39
  144. package/src/modules/plugins/plugins.routes.ts +3 -2
  145. package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
  146. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +353 -0
  147. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +282 -0
  148. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +233 -0
  149. package/src/modules/settings/deployment-settings.service.ts +109 -3
  150. package/src/modules/settings/managed-repository.ts +68 -0
  151. package/src/modules/settings/repository-source.ts +197 -0
  152. package/src/modules/settings/setup.routes.ts +214 -10
  153. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +5 -1
  154. package/src/modules/skills/skill-access-requests.routes.ts +25 -75
  155. package/src/modules/tool-helpers/tool-context.ts +15 -0
  156. package/src/modules/workflow/__tests__/apply-failure.test.ts +9 -1
  157. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +159 -8
  158. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +146 -40
  159. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +141 -1
  160. package/src/modules/workflow/agent-tools/workflow.tools.ts +49 -3
  161. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +121 -0
  162. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
  163. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
  164. package/src/modules/workflow/git/git.service.ts +25 -4
  165. package/src/modules/workflow/git/node-git-runner.ts +6 -0
  166. package/src/modules/workflow/git/pull-request.service.ts +119 -0
  167. package/src/modules/workflow/workflow.routes.ts +3 -1
  168. package/src/modules/workflow/workflow.service.ts +170 -52
  169. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +43 -0
  170. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +233 -7
  171. package/src/modules/workspace/__tests__/workspace.tools.test.ts +35 -3
  172. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +211 -0
  173. package/src/modules/workspace/startup/kb-startup-runner.ts +159 -0
  174. package/src/modules/workspace/workspace.service.ts +17 -1
  175. package/src/modules/workspace/workspace.tools.ts +35 -0
  176. package/src/shared/domain-errors.ts +58 -0
  177. package/src/shared/git.contract.ts +10 -0
@@ -61,6 +61,16 @@ export interface GitCredentials {
61
61
  username(): string;
62
62
  /** The token in effect, or null when the deployment has none configured. */
63
63
  token(): string | null;
64
+ /**
65
+ * Make sure {@link token} answers with a credential that is still good,
66
+ * for one that is not fixed: a token a host issues for an hour has to be
67
+ * renewed by asking the host, which `token` cannot do, being read where
68
+ * nothing can wait. The runner awaits this before every call it makes. A
69
+ * failure here is not the call's failure: git runs with the token in
70
+ * hand, and the host says what it thinks of it. Absent for a credential
71
+ * that does not expire.
72
+ */
73
+ prepare?(): Promise<void>;
64
74
  }
65
75
  /** The username git is given when a deployment names none. */
66
76
  export declare const DEFAULT_GIT_USERNAME = "x-access-token";
@@ -1 +1 @@
1
- {"version":3,"file":"git.contract.d.ts","sourceRoot":"","sources":["../../src/shared/git.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,QAAQ,IAAI,MAAM,CAAC;IACnB,4EAA4E;IAC5E,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,8DAA8D;AAC9D,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,iBAAiB,CAAC;AAE5C,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,EAAE,cAG/B,CAAC;AAEH,0EAA0E;AAC1E,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAwB,EACxD,KAAK,GAAE,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,CAAQ,GAClD,cAAc,CAOhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAMrF;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,IAAI,GAAG,MAAM;IACzC,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B;;;;;OAKG;gBACS,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO;CAOnG;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAElD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9G,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACvG"}
1
+ {"version":3,"file":"git.contract.d.ts","sourceRoot":"","sources":["../../src/shared/git.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,QAAQ,IAAI,MAAM,CAAC;IACnB,4EAA4E;IAC5E,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;IACvB;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,8DAA8D;AAC9D,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,iBAAiB,CAAC;AAE5C,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,EAAE,cAG/B,CAAC;AAEH,0EAA0E;AAC1E,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAwB,EACxD,KAAK,GAAE,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,CAAQ,GAClD,cAAc,CAOhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAMrF;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,IAAI,GAAG,MAAM;IACzC,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B;;;;;OAKG;gBACS,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO;CAOnG;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAElD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9G,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACvG"}
@@ -1 +1 @@
1
- {"version":3,"file":"git.contract.js","sourceRoot":"","sources":["../../src/shared/git.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AA4BH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAC;AAE5C,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAmB,MAAM,CAAC,MAAM,CAAC;IAC9D,QAAQ,EAAE,GAAG,EAAE,CAAC,oBAAoB;IACpC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI;CAClB,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAC5B,WAAoC,oBAAoB,EACxD,QAA+C,IAAI;IAEnD,MAAM,IAAI,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;IACxE,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IACjE,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,oBAAoB;QAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,IAAI;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAgC;IAC3E,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,wEAAwE;IACxE,2EAA2E;IAC3E,wCAAwC;IACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;AACxD,CAAC;AA8CD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,0DAA0D;IACjD,QAAQ,CAAU;IAC3B,+DAA+D;IACtD,MAAM,CAAU;IACzB,+DAA+D;IACtD,QAAQ,CAAU;IAE3B;;;;;OAKG;IACH,YAAY,OAAe,EAAE,OAAmE,EAAE;QAChG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,GAAG,YAAY,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"git.contract.js","sourceRoot":"","sources":["../../src/shared/git.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAsCH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAC;AAE5C,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAmB,MAAM,CAAC,MAAM,CAAC;IAC9D,QAAQ,EAAE,GAAG,EAAE,CAAC,oBAAoB;IACpC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI;CAClB,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAC5B,WAAoC,oBAAoB,EACxD,QAA+C,IAAI;IAEnD,MAAM,IAAI,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;IACxE,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IACjE,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,oBAAoB;QAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,IAAI;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAgC;IAC3E,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,wEAAwE;IACxE,2EAA2E;IAC3E,wCAAwC;IACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;AACxD,CAAC;AA8CD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,0DAA0D;IACjD,QAAQ,CAAU;IAC3B,+DAA+D;IACtD,MAAM,CAAU;IACzB,+DAA+D;IACtD,QAAQ,CAAU;IAE3B;;;;;OAKG;IACH,YAAY,OAAe,EAAE,OAAmE,EAAE;QAChG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,GAAG,YAAY,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC;AACpD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bevel-software/platform-core-backend",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Open-source core backend of the Bevel platform: git-backed knowledge workspace, workflow (branches/change requests/locks/SSE), skills, tools, secrets vault, access control and the remote MCP surface.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,8 +52,8 @@
52
52
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
53
53
  "yaml": "^2.9.0",
54
54
  "zod": "^3.24.0",
55
- "@bevel-software/platform-mcp-core": "0.21.0",
56
- "@bevel-software/platform-shared": "0.21.0"
55
+ "@bevel-software/platform-mcp-core": "0.22.0",
56
+ "@bevel-software/platform-shared": "0.22.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/adm-zip": "^0.5.8",
@@ -10,6 +10,7 @@ import { createDiffRoutes } from '../modules/diff/diff.routes.js';
10
10
  import { createWorkflowRoutes } from '../modules/workflow/workflow.routes.js';
11
11
  import { createEventsRoutes } from '../modules/workflow/events.routes.js';
12
12
  import { createAccessRoutes } from '../modules/access/access.routes.js';
13
+ import { createAccessRequestRoutes } from '../modules/access/access-requests.routes.js';
13
14
  import { createMcpRoutes } from '../modules/mcp/mcp.routes.js';
14
15
  import { createOAuthConsentRoutes } from '../modules/mcp/oauth/oauth-consent.routes.js';
15
16
  import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';
@@ -50,7 +51,8 @@ import { createConnectionKeysAdminRoutes } from '../modules/tool-auth/connection
50
51
  import { createAuditRoutes } from '../modules/audit/audit.routes.js';
51
52
  import { createAgentRestAuditMiddleware } from '../modules/audit/agent-rest-audit.middleware.js';
52
53
  import { EXTERNAL_KB_MANUAL_NAME } from '../modules/tool-manuals/tool-manuals.contract.js';
53
- import { createSetupRoutes } from '../modules/settings/setup.routes.js';
54
+ import { createSetupRoutes, isComplete } from '../modules/settings/setup.routes.js';
55
+ import { createGitHubAppRoutes } from '../modules/github-app/index.js';
54
56
  import { oidcRedirectUri } from '../modules/auth/oidc-auth-provider.js';
55
57
  import { repositoryConnectionCheck } from '../modules/settings/connection-check.js';
56
58
  import { rootFolderListerFor } from '../modules/settings/git-root-folders.js';
@@ -591,6 +593,18 @@ export async function createCoreServer(
591
593
  core.kb,
592
594
  [core.config.adminEmail],
593
595
  ));
596
+ // Asking for Can edit or Owner on an item from its Manage access dialog, and
597
+ // the editors' Accept / Decline. Mounted beside the access routes it shares
598
+ // its gate with: the request is refused by the same canWrite it would
599
+ // otherwise have to wait for.
600
+ app.use('/api', core.authMiddleware, createAccessRequestRoutes({
601
+ accessControl: core.accessControl,
602
+ workspaceService: core.workspaceService,
603
+ authService: core.authService,
604
+ workflow: core.workflowService,
605
+ lifecycle: core.joinRequestsService,
606
+ kb: core.kb,
607
+ }));
594
608
  app.use(
595
609
  '/api',
596
610
  core.authMiddleware,
@@ -700,8 +714,30 @@ export async function createCoreServer(
700
714
  repositoryConnectionCheck(core.gitRunner),
701
715
  rootFolderListerFor(core.gitRunner),
702
716
  oidcRedirectUri(core.config.publicBackendUrl),
717
+ // The two sign-in checks keep their defaults.
718
+ undefined,
719
+ undefined,
720
+ {
721
+ source: core.repositorySource,
722
+ ensureManaged: (branch) => core.managedRepository.ensure(core.gitRunner, branch),
723
+ githubApp: core.githubApp,
724
+ },
703
725
  ),
704
726
  );
727
+ // Connecting a repository on GitHub through a GitHub App: the round trips
728
+ // to GitHub the setup screen starts, behind the same sign-in.
729
+ app.use(
730
+ '/api',
731
+ core.authMiddleware,
732
+ createGitHubAppRoutes({
733
+ settings: core.settings,
734
+ connection: core.githubApp,
735
+ adminAccess: core.adminAccess,
736
+ publicBackendUrl: core.config.publicBackendUrl,
737
+ publicFrontendUrl: core.config.publicFrontendUrl,
738
+ isComplete: () => isComplete(core.settings, core.kb, core.repositorySource),
739
+ }),
740
+ );
705
741
  const toolPageUser = async (userId: string): Promise<AuthUser | undefined> => {
706
742
  const u = await core.authService.getUserById(userId);
707
743
  return u ? ({ id: u.id, email: u.email, name: u.name } as AuthUser) : undefined;
@@ -30,7 +30,10 @@ import { KbSyncService } from '../modules/kb-sync/kb-sync.service.js';
30
30
  import { NodeFs } from '../modules/kb-fs/node-fs.js';
31
31
  import { assertKbDirNameFree } from '../modules/kb-fs/repo-path.js';
32
32
  import type { IFsProbe, ITreeWalker } from '../shared/fs.contract.js';
33
- import { gitCredentials, type IGitRunner } from '../shared/git.contract.js';
33
+ import type { IGitRunner } from '../shared/git.contract.js';
34
+ import { ManagedRepository, MANAGED_DEFAULT_BRANCH } from '../modules/settings/managed-repository.js';
35
+ import { RepositorySource } from '../modules/settings/repository-source.js';
36
+ import { GitHubAppConnection } from '../modules/github-app/index.js';
34
37
  import { AdvisoryLease, AdvisoryLock } from '../modules/database/advisory-lock.js';
35
38
  import { holdCommitWorkerLease, withStartupTask, type LeaseLoopHandle } from './lifecycle.js';
36
39
 
@@ -186,6 +189,15 @@ export interface CoreServices {
186
189
  * first-time setup completion — and never again while the process serves.
187
190
  */
188
191
  kbStartupRunner: KbStartupRunner;
192
+ /**
193
+ * Where the knowledge base's repository is and what git presents to it,
194
+ * whichever way the deployment was given one (see `RepositorySource`).
195
+ */
196
+ repositorySource: RepositorySource;
197
+ /** The repository the deployment keeps for itself, when that is the way it chose. */
198
+ managedRepository: ManagedRepository;
199
+ /** The connection to GitHub through a GitHub App, when that is the way it chose. */
200
+ githubApp: GitHubAppConnection;
189
201
  /**
190
202
  * The startup phase's retry when the boot survived an unreachable remote
191
203
  * — set by `startCore`, stopped by `stopCore` (see `core/lifecycle.ts`);
@@ -434,18 +446,49 @@ export async function createCoreServices(
434
446
  // settings catalogue knows `GIT_TOKEN` alone. Handed to the runner, which
435
447
  // puts the token into each child's environment — never into this
436
448
  // process's, which several knowledge bases may share.
437
- const gitCredentialsInEffect = gitCredentials(
438
- () => settings.resolve('gitUsername') || config.gitUsername,
439
- () => settings.resolve('gitToken') || config.gitToken || null,
440
- );
449
+ //
450
+ // Both come from the repository source, which is the one place that knows
451
+ // a deployment can be given its repository in more than one way; what
452
+ // comes back is the address and the credential, as it always was.
453
+ const managedRepository = new ManagedRepository(path.join(config.backupsRoot, 'managed-repository'));
454
+ const githubApp = new GitHubAppConnection({
455
+ read: (key) => settings.resolve(key),
456
+ sourceOf: (key) => settings.sourceOf(key),
457
+ });
458
+ const repositorySource = new RepositorySource({
459
+ read: (key) => settings.resolve(key),
460
+ fallback: { username: config.gitUsername, token: config.gitToken },
461
+ managed: managedRepository,
462
+ githubApp,
463
+ });
464
+ const gitCredentialsInEffect = repositorySource.credentials;
441
465
  // How git is run, for every module that runs it: one environment, one buffer
442
466
  // ceiling, one error shape, and — the reason it exists — one deadline, so a
443
467
  // git that never returns cannot hold a workspace (and with it the commit
444
468
  // queue) open indefinitely. See `shared/git.contract.ts`.
445
469
  const gitRunner = new NodeGitRunner(config.gitTimeoutMs, gitCredentialsInEffect);
470
+ // A deployment that keeps its own repository has it before anything asks
471
+ // for it: one that chose the mode through the environment never passes
472
+ // through the save that would have created it, and the startup phase's
473
+ // first question would be asked of a folder that is not there. Left to
474
+ // that phase to report if it cannot be made: its failures are classified
475
+ // and shown on the setup screen, where a throw here would take the screen
476
+ // down with the boot.
477
+ if (repositorySource.mode() === 'managed') {
478
+ await managedRepository
479
+ .ensure(gitRunner, settings.resolve('defaultBranch') || MANAGED_DEFAULT_BRANCH)
480
+ .catch((err: unknown) => logger('setup').error('the managed repository could not be created:', { err }));
481
+ }
482
+ // One reached through a GitHub App has a token in hand before anything
483
+ // reads it: a working copy's credential helper is stamped according to
484
+ // whether there is a token, by code that cannot wait for one. A failure is
485
+ // the startup phase's to report, for the same reason as above.
486
+ if (repositorySource.mode() === 'github-app') {
487
+ await githubApp.prepare().catch(() => undefined);
488
+ }
446
489
  const workspaceService = new WorkspaceService(
447
490
  config.workspacesRoot,
448
- () => settings.resolve('kbRepoUrl'),
491
+ () => repositorySource.url(),
449
492
  kb,
450
493
  disk,
451
494
  gitRunner,
@@ -484,8 +527,12 @@ export async function createCoreServices(
484
527
  // Getters, not values: the remote and the branch model can arrive from the
485
528
  // setup screen after this object exists, and the setup-completion run is
486
529
  // the first thing that needs them.
487
- kbRepoUrl: () => settings.resolve('kbRepoUrl'),
530
+ kbRepoUrl: () => repositorySource.url(),
488
531
  workspacesRoot: config.workspacesRoot,
532
+ // Under the backups root, a persistent volume of its own and one nothing
533
+ // sweeps: a working copy of a repository that was replaced is kept there,
534
+ // never deleted (see the runner's `reconcileClonesWithConfiguredRepository`).
535
+ setAsideRoot: path.join(config.backupsRoot, 'replaced-working-copies'),
489
536
  kbDirName,
490
537
  templateDir: config.kbTemplateDir,
491
538
  defaultBranch: () => kb.defaultBranch,
@@ -693,7 +740,14 @@ export async function createCoreServices(
693
740
  // (the request's branch vs the default branch), so it holds no state — it
694
741
  // only needs to read files at refs and to close a request whose proposals
695
742
  // have all landed.
696
- const joinRequestsService = new JoinRequestsService(workspaceService, workflowService, kb);
743
+ const joinRequestsService = new JoinRequestsService(
744
+ workspaceService,
745
+ workflowService,
746
+ kb,
747
+ // Read-only: the one question a request asks of the resolver is whether
748
+ // the person it names already holds what it proposes.
749
+ accessControl,
750
+ );
697
751
  // The OTHER half of a join request: the row the subscribe endpoint writes
698
752
  // before it answers, and the branch/clone/commit/push/change-request work
699
753
  // that runs against it afterwards. The row is what lets the click be
@@ -1237,6 +1291,9 @@ export async function createCoreServices(
1237
1291
  toolDeleteService,
1238
1292
  workspaceService,
1239
1293
  kbStartupRunner,
1294
+ repositorySource,
1295
+ managedRepository,
1296
+ githubApp,
1240
1297
  settings,
1241
1298
  kb,
1242
1299
  kbDirName,
@@ -0,0 +1,134 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { execFile } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ import fs from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import os from 'node:os';
7
+
8
+ import { spliceGrant } from '../../access-model/access-splice.js';
9
+ import { pendingProposals } from '../../plugins/join-proposals.js';
10
+
11
+ const execFileAsync = promisify(execFile);
12
+
13
+ /**
14
+ * WHY A REQUEST BRANCH IS CUT AGAIN FROM LIVE, proved against real git.
15
+ *
16
+ * Opening a change request merges the target branch INTO its source branch.
17
+ * A request branch survives being declined — that is what lets the dialog say
18
+ * "your last request wasn't accepted" — so the next request finds a branch
19
+ * whose base may be old. Writing the new proposal onto it and opening the
20
+ * request then asks git to merge live into a branch that edited the same
21
+ * rules file from a different starting point, and git does what git does.
22
+ *
23
+ * The person asking is told to "resolve on reader2/join-artest3-04i7dr4", a
24
+ * branch they cannot reach, and every retry says the same thing. There is no
25
+ * route out of it for them, which is what made this worth a fix rather than a
26
+ * better error message.
27
+ *
28
+ * Everything below is plain git on a scratch repo: no app, no doubles. The
29
+ * first case is the bug, kept so the fix cannot quietly stop being necessary;
30
+ * the second is the fix.
31
+ */
32
+ describe('a request branch whose base has moved', () => {
33
+ let root: string;
34
+ let repo: string;
35
+
36
+ const RULES = 'Research/access.md';
37
+ const LIVE = 'live';
38
+ const REQUEST = 'reader2/join-research-abc1234-def5678';
39
+ const RITA = { kind: 'user' as const, email: 'rita@x.io', displayName: 'Rita' };
40
+ const RAVI = { kind: 'user' as const, email: 'ravi@x.io', displayName: 'Ravi' };
41
+
42
+ const git = (...args: string[]) => execFileAsync('git', ['-C', repo, ...args]);
43
+
44
+ const readRules = async () => fs.readFile(path.join(repo, RULES), 'utf-8');
45
+ const writeRules = async (text: string) => {
46
+ await fs.mkdir(path.dirname(path.join(repo, RULES)), { recursive: true });
47
+ await fs.writeFile(path.join(repo, RULES), text);
48
+ };
49
+ const commit = async (message: string) => {
50
+ await git('add', '-A');
51
+ await git('commit', '-q', '-m', message);
52
+ };
53
+
54
+ /** What opening a change request does: merge live INTO the request branch. */
55
+ async function mergeLiveIntoRequest(): Promise<{ ok: boolean; conflicted: string[] }> {
56
+ try {
57
+ await git('merge', '--no-edit', LIVE);
58
+ return { ok: true, conflicted: [] };
59
+ } catch {
60
+ const { stdout } = await execFileAsync('git', [
61
+ '-C',
62
+ repo,
63
+ 'diff',
64
+ '--name-only',
65
+ '--diff-filter=U',
66
+ ]);
67
+ await git('merge', '--abort');
68
+ return { ok: false, conflicted: stdout.split('\n').filter(Boolean) };
69
+ }
70
+ }
71
+
72
+ beforeEach(async () => {
73
+ root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-recut-'));
74
+ repo = path.join(root, 'kb');
75
+ await fs.mkdir(repo, { recursive: true });
76
+ await git('init', '-q', '-b', LIVE);
77
+ await git('config', 'user.email', 'test@bevel.software');
78
+ await git('config', 'user.name', 'Test');
79
+ await writeRules('---\n---\nowner:\n - Ed <ed@x.io>\n');
80
+ await commit('the rules as they were');
81
+
82
+ // Ravi asked for Owner, and it was declined. The branch stays behind.
83
+ await git('checkout', '-q', '-b', REQUEST);
84
+ await writeRules(spliceGrant(await readRules(), 'owner', RAVI, { target: 'folder' }).text);
85
+ await commit('Request owner access to Research');
86
+ await git('checkout', '-q', LIVE);
87
+
88
+ // Then an editor grants somebody else Can edit here — an ordinary click in
89
+ // the dialog, and the only thing this scenario needs to go wrong.
90
+ await writeRules(spliceGrant(await readRules(), 'write', RITA, { target: 'folder' }).text);
91
+ await commit('Ed grants Rita Can edit');
92
+ });
93
+
94
+ afterEach(async () => {
95
+ await fs.rm(root, { recursive: true, force: true });
96
+ });
97
+
98
+ it('CONFLICTS when the stale branch is written to and the request opened on it', async () => {
99
+ // What this did before: splice onto live's text, write it to the branch as
100
+ // it stands, open the request. The content is right; the history is not.
101
+ await git('checkout', '-q', REQUEST);
102
+ const live = await execFileAsync('git', ['-C', repo, 'show', `${LIVE}:${RULES}`]);
103
+ await writeRules(spliceGrant(live.stdout, 'write', RAVI, { target: 'folder' }).text);
104
+ await commit('Request can edit access to Research');
105
+
106
+ const merged = await mergeLiveIntoRequest();
107
+ expect(merged.ok).toBe(false);
108
+ expect(merged.conflicted).toEqual([RULES]);
109
+ });
110
+
111
+ it('is CLEAN once the branch is cut again from live first', async () => {
112
+ // The fix: the answered request's branch is deleted and recut from live,
113
+ // so live's tip is the merge base and there is nothing to reconcile.
114
+ await git('branch', '-q', '-D', REQUEST);
115
+ await git('checkout', '-q', '-b', REQUEST, LIVE);
116
+ await writeRules(spliceGrant(await readRules(), 'write', RAVI, { target: 'folder' }).text);
117
+ await commit('Request can edit access to Research');
118
+
119
+ const merged = await mergeLiveIntoRequest();
120
+ expect(merged.ok).toBe(true);
121
+
122
+ // And the branch proposes exactly one thing — asked of the function the
123
+ // app itself asks, rather than of the diff's line-by-line shape (a splice
124
+ // may rewrite a whole block without proposing anything more). Rita's
125
+ // grant is already on live, so it is nothing new; the declined Owner
126
+ // proposal went with the branch that carried it.
127
+ const branchText = (await execFileAsync('git', ['-C', repo, 'show', `${REQUEST}:${RULES}`]))
128
+ .stdout;
129
+ const liveText = (await execFileAsync('git', ['-C', repo, 'show', `${LIVE}:${RULES}`])).stdout;
130
+ expect(pendingProposals(branchText, liveText, RULES)).toEqual([
131
+ expect.objectContaining({ verb: 'write', id: `user:${RAVI.email}` }),
132
+ ]);
133
+ });
134
+ });