@oscharko-dev/keiko-server 0.2.7 → 0.2.9

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 (302) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/chat-handlers.d.ts +18 -2
  3. package/dist/chat-handlers.d.ts.map +1 -1
  4. package/dist/chat-handlers.js +185 -3
  5. package/dist/command-runner-errors.d.ts +17 -0
  6. package/dist/command-runner-errors.d.ts.map +1 -0
  7. package/dist/command-runner-errors.js +37 -0
  8. package/dist/command-runner-evidence.d.ts +23 -0
  9. package/dist/command-runner-evidence.d.ts.map +1 -0
  10. package/dist/command-runner-evidence.js +69 -0
  11. package/dist/command-runner-routes.d.ts +7 -0
  12. package/dist/command-runner-routes.d.ts.map +1 -0
  13. package/dist/command-runner-routes.js +175 -0
  14. package/dist/command-runner.d.ts +29 -0
  15. package/dist/command-runner.d.ts.map +1 -0
  16. package/dist/command-runner.js +348 -0
  17. package/dist/conversation-prompt.d.ts +2 -2
  18. package/dist/conversation-prompt.d.ts.map +1 -1
  19. package/dist/conversation-prompt.js +17 -1
  20. package/dist/csp.d.ts.map +1 -1
  21. package/dist/csp.js +3 -0
  22. package/dist/deps.d.ts +28 -1
  23. package/dist/deps.d.ts.map +1 -1
  24. package/dist/deps.js +288 -13
  25. package/dist/discussion-prompt.d.ts +4 -0
  26. package/dist/discussion-prompt.d.ts.map +1 -0
  27. package/dist/discussion-prompt.js +19 -0
  28. package/dist/editor/agentActionAudit.d.ts +18 -0
  29. package/dist/editor/agentActionAudit.d.ts.map +1 -0
  30. package/dist/editor/agentActionAudit.js +80 -0
  31. package/dist/editor/agentRoutes.d.ts +1 -0
  32. package/dist/editor/agentRoutes.d.ts.map +1 -1
  33. package/dist/editor/agentRoutes.js +292 -55
  34. package/dist/editor/agentSessionRegistry.d.ts +35 -0
  35. package/dist/editor/agentSessionRegistry.d.ts.map +1 -0
  36. package/dist/editor/agentSessionRegistry.js +243 -0
  37. package/dist/editor/completionRoutes.d.ts.map +1 -1
  38. package/dist/editor/completionRoutes.js +5 -10
  39. package/dist/editor/languageRoutes.d.ts +12 -1
  40. package/dist/editor/languageRoutes.d.ts.map +1 -1
  41. package/dist/editor/languageRoutes.js +71 -8
  42. package/dist/editor/languageService.d.ts +3 -2
  43. package/dist/editor/languageService.d.ts.map +1 -1
  44. package/dist/editor/languageService.js +41 -3
  45. package/dist/editor/languageServiceHost.d.ts.map +1 -1
  46. package/dist/editor/languageServiceHost.js +2 -2
  47. package/dist/editor/lsp/hostLanguageOperation.d.ts +17 -0
  48. package/dist/editor/lsp/hostLanguageOperation.d.ts.map +1 -0
  49. package/dist/editor/lsp/hostLanguageOperation.js +436 -0
  50. package/dist/editor/lsp/hostLanguageProviders.d.ts +26 -0
  51. package/dist/editor/lsp/hostLanguageProviders.d.ts.map +1 -0
  52. package/dist/editor/lsp/hostLanguageProviders.js +161 -0
  53. package/dist/editor/lsp/lspFrameCodec.d.ts +13 -0
  54. package/dist/editor/lsp/lspFrameCodec.d.ts.map +1 -0
  55. package/dist/editor/lsp/lspFrameCodec.js +164 -0
  56. package/dist/editor/lsp/lspJsonRpcClient.d.ts +34 -0
  57. package/dist/editor/lsp/lspJsonRpcClient.d.ts.map +1 -0
  58. package/dist/editor/lsp/lspJsonRpcClient.js +173 -0
  59. package/dist/editor/lsp/lspLanguageProvider.d.ts +7 -0
  60. package/dist/editor/lsp/lspLanguageProvider.d.ts.map +1 -0
  61. package/dist/editor/lsp/lspLanguageProvider.js +29 -0
  62. package/dist/editor/lsp/lspLifecycleLedger.d.ts +5 -0
  63. package/dist/editor/lsp/lspLifecycleLedger.d.ts.map +1 -0
  64. package/dist/editor/lsp/lspLifecycleLedger.js +37 -0
  65. package/dist/editor/lsp/lspNodeAdapter.d.ts +31 -0
  66. package/dist/editor/lsp/lspNodeAdapter.d.ts.map +1 -0
  67. package/dist/editor/lsp/lspNodeAdapter.js +230 -0
  68. package/dist/editor/lsp/lspProcessManager.d.ts +24 -0
  69. package/dist/editor/lsp/lspProcessManager.d.ts.map +1 -0
  70. package/dist/editor/lsp/lspProcessManager.js +255 -0
  71. package/dist/editor/lsp/lspRestartThrottle.d.ts +6 -0
  72. package/dist/editor/lsp/lspRestartThrottle.d.ts.map +1 -0
  73. package/dist/editor/lsp/lspRestartThrottle.js +24 -0
  74. package/dist/editor/lsp/lspStatusRoute.d.ts +8 -0
  75. package/dist/editor/lsp/lspStatusRoute.d.ts.map +1 -0
  76. package/dist/editor/lsp/lspStatusRoute.js +22 -0
  77. package/dist/editor/lsp/lspTransport.d.ts +19 -0
  78. package/dist/editor/lsp/lspTransport.d.ts.map +1 -0
  79. package/dist/editor/lsp/lspTransport.js +55 -0
  80. package/dist/editor/lsp/testing/fakeLspProcess.d.ts +23 -0
  81. package/dist/editor/lsp/testing/fakeLspProcess.d.ts.map +1 -0
  82. package/dist/editor/lsp/testing/fakeLspProcess.js +132 -0
  83. package/dist/files.d.ts +63 -0
  84. package/dist/files.d.ts.map +1 -1
  85. package/dist/files.js +799 -1
  86. package/dist/gateway-readiness.d.ts +6 -0
  87. package/dist/gateway-readiness.d.ts.map +1 -0
  88. package/dist/gateway-readiness.js +624 -0
  89. package/dist/gateway-setup.d.ts +2 -0
  90. package/dist/gateway-setup.d.ts.map +1 -1
  91. package/dist/gateway-setup.js +275 -11
  92. package/dist/gitDelivery/actionSheetProjection.d.ts +30 -0
  93. package/dist/gitDelivery/actionSheetProjection.d.ts.map +1 -0
  94. package/dist/gitDelivery/actionSheetProjection.js +206 -0
  95. package/dist/gitDelivery/actionSheetRoutes.d.ts +29 -0
  96. package/dist/gitDelivery/actionSheetRoutes.d.ts.map +1 -0
  97. package/dist/gitDelivery/actionSheetRoutes.js +293 -0
  98. package/dist/gitDelivery/agentOperationsRoutes.d.ts +33 -0
  99. package/dist/gitDelivery/agentOperationsRoutes.d.ts.map +1 -0
  100. package/dist/gitDelivery/agentOperationsRoutes.js +405 -0
  101. package/dist/gitDelivery/commitRoutes.d.ts +23 -0
  102. package/dist/gitDelivery/commitRoutes.d.ts.map +1 -0
  103. package/dist/gitDelivery/commitRoutes.js +204 -0
  104. package/dist/gitDelivery/evidenceRoutes.d.ts +9 -0
  105. package/dist/gitDelivery/evidenceRoutes.d.ts.map +1 -0
  106. package/dist/gitDelivery/evidenceRoutes.js +101 -0
  107. package/dist/gitDelivery/execution.d.ts +38 -0
  108. package/dist/gitDelivery/execution.d.ts.map +1 -0
  109. package/dist/gitDelivery/execution.js +117 -0
  110. package/dist/gitDelivery/localMutationRoutes.d.ts +30 -0
  111. package/dist/gitDelivery/localMutationRoutes.d.ts.map +1 -0
  112. package/dist/gitDelivery/localMutationRoutes.js +165 -0
  113. package/dist/gitDelivery/mergeExecution.d.ts +63 -0
  114. package/dist/gitDelivery/mergeExecution.d.ts.map +1 -0
  115. package/dist/gitDelivery/mergeExecution.js +168 -0
  116. package/dist/gitDelivery/mergeRoutes.d.ts +12 -0
  117. package/dist/gitDelivery/mergeRoutes.d.ts.map +1 -0
  118. package/dist/gitDelivery/mergeRoutes.js +218 -0
  119. package/dist/gitDelivery/mutationEvidenceLedger.d.ts +23 -0
  120. package/dist/gitDelivery/mutationEvidenceLedger.d.ts.map +1 -0
  121. package/dist/gitDelivery/mutationEvidenceLedger.js +87 -0
  122. package/dist/gitDelivery/prExecution.d.ts +54 -0
  123. package/dist/gitDelivery/prExecution.d.ts.map +1 -0
  124. package/dist/gitDelivery/prExecution.js +192 -0
  125. package/dist/gitDelivery/prRoutes.d.ts +12 -0
  126. package/dist/gitDelivery/prRoutes.d.ts.map +1 -0
  127. package/dist/gitDelivery/prRoutes.js +256 -0
  128. package/dist/gitDelivery/pushExecution.d.ts +43 -0
  129. package/dist/gitDelivery/pushExecution.d.ts.map +1 -0
  130. package/dist/gitDelivery/pushExecution.js +124 -0
  131. package/dist/gitDelivery/pushRoutes.d.ts +12 -0
  132. package/dist/gitDelivery/pushRoutes.d.ts.map +1 -0
  133. package/dist/gitDelivery/pushRoutes.js +200 -0
  134. package/dist/gitDelivery/requestGuards.d.ts +15 -0
  135. package/dist/gitDelivery/requestGuards.d.ts.map +1 -0
  136. package/dist/gitDelivery/requestGuards.js +97 -0
  137. package/dist/gitDelivery/syncEvidence.d.ts +37 -0
  138. package/dist/gitDelivery/syncEvidence.d.ts.map +1 -0
  139. package/dist/gitDelivery/syncEvidence.js +85 -0
  140. package/dist/gitDelivery/syncExecution.d.ts +30 -0
  141. package/dist/gitDelivery/syncExecution.d.ts.map +1 -0
  142. package/dist/gitDelivery/syncExecution.js +266 -0
  143. package/dist/gitDelivery/syncRoutes.d.ts +13 -0
  144. package/dist/gitDelivery/syncRoutes.d.ts.map +1 -0
  145. package/dist/gitDelivery/syncRoutes.js +200 -0
  146. package/dist/gitPorcelainStatus.d.ts +15 -0
  147. package/dist/gitPorcelainStatus.d.ts.map +1 -0
  148. package/dist/gitPorcelainStatus.js +104 -0
  149. package/dist/gitRepositoryReads.d.ts +10 -0
  150. package/dist/gitRepositoryReads.d.ts.map +1 -0
  151. package/dist/gitRepositoryReads.js +314 -0
  152. package/dist/gitRepositoryRoutes.d.ts +7 -0
  153. package/dist/gitRepositoryRoutes.d.ts.map +1 -0
  154. package/dist/gitRepositoryRoutes.js +221 -0
  155. package/dist/gitRoutes.d.ts +66 -0
  156. package/dist/gitRoutes.d.ts.map +1 -0
  157. package/dist/gitRoutes.js +543 -0
  158. package/dist/governed-workflow.d.ts +2 -0
  159. package/dist/governed-workflow.d.ts.map +1 -1
  160. package/dist/governed-workflow.js +4 -0
  161. package/dist/grounded-qa-hybrid.d.ts.map +1 -1
  162. package/dist/grounded-qa-hybrid.js +2 -0
  163. package/dist/grounded-qa-multi-source.d.ts.map +1 -1
  164. package/dist/grounded-qa-multi-source.js +1 -0
  165. package/dist/grounded-qa.d.ts +11 -0
  166. package/dist/grounded-qa.d.ts.map +1 -1
  167. package/dist/grounded-qa.js +14 -4
  168. package/dist/headers.d.ts +4 -1
  169. package/dist/headers.d.ts.map +1 -1
  170. package/dist/headers.js +11 -4
  171. package/dist/index.d.ts +8 -1
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +9 -1
  174. package/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
  175. package/dist/local-knowledge-grounded-qa.js +11 -2
  176. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +1 -1
  177. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
  178. package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1 -1
  179. package/dist/read-handlers.d.ts +5 -0
  180. package/dist/read-handlers.d.ts.map +1 -1
  181. package/dist/read-handlers.js +57 -1
  182. package/dist/routes.d.ts.map +1 -1
  183. package/dist/routes.js +260 -12
  184. package/dist/run-engine.d.ts.map +1 -1
  185. package/dist/run-engine.js +3 -0
  186. package/dist/run-handlers.d.ts +0 -1
  187. package/dist/run-handlers.d.ts.map +1 -1
  188. package/dist/run-handlers.js +64 -211
  189. package/dist/run-request.d.ts +11 -0
  190. package/dist/run-request.d.ts.map +1 -1
  191. package/dist/run-request.js +158 -10
  192. package/dist/runtime/capabilityDetector.d.ts +38 -0
  193. package/dist/runtime/capabilityDetector.d.ts.map +1 -0
  194. package/dist/runtime/capabilityDetector.js +443 -0
  195. package/dist/runtime/capabilityRoutes.d.ts +9 -0
  196. package/dist/runtime/capabilityRoutes.d.ts.map +1 -0
  197. package/dist/runtime/capabilityRoutes.js +45 -0
  198. package/dist/runtime/containerEngineDetector.d.ts +17 -0
  199. package/dist/runtime/containerEngineDetector.d.ts.map +1 -0
  200. package/dist/runtime/containerEngineDetector.js +222 -0
  201. package/dist/runtime/containerRoutes.d.ts +8 -0
  202. package/dist/runtime/containerRoutes.d.ts.map +1 -0
  203. package/dist/runtime/containerRoutes.js +207 -0
  204. package/dist/runtime/containerRunner-errors.d.ts +18 -0
  205. package/dist/runtime/containerRunner-errors.d.ts.map +1 -0
  206. package/dist/runtime/containerRunner-errors.js +42 -0
  207. package/dist/runtime/containerRunner-evidence.d.ts +24 -0
  208. package/dist/runtime/containerRunner-evidence.d.ts.map +1 -0
  209. package/dist/runtime/containerRunner-evidence.js +74 -0
  210. package/dist/runtime/containerRunner.d.ts +37 -0
  211. package/dist/runtime/containerRunner.d.ts.map +1 -0
  212. package/dist/runtime/containerRunner.js +443 -0
  213. package/dist/server.d.ts.map +1 -1
  214. package/dist/server.js +24 -4
  215. package/dist/store/db.d.ts.map +1 -1
  216. package/dist/store/db.js +2 -1
  217. package/dist/store/index.d.ts +1 -1
  218. package/dist/store/index.d.ts.map +1 -1
  219. package/dist/store/messages.d.ts +2 -1
  220. package/dist/store/messages.d.ts.map +1 -1
  221. package/dist/store/messages.js +46 -4
  222. package/dist/store/schema.d.ts +1 -1
  223. package/dist/store/schema.d.ts.map +1 -1
  224. package/dist/store/schema.js +68 -1
  225. package/dist/store/types.d.ts +3 -2
  226. package/dist/store/types.d.ts.map +1 -1
  227. package/dist/task-workspace/active-store.d.ts +21 -0
  228. package/dist/task-workspace/active-store.d.ts.map +1 -0
  229. package/dist/task-workspace/active-store.js +55 -0
  230. package/dist/task-workspace/authorization.d.ts +7 -0
  231. package/dist/task-workspace/authorization.d.ts.map +1 -0
  232. package/dist/task-workspace/authorization.js +54 -0
  233. package/dist/task-workspace/binding.d.ts +3 -0
  234. package/dist/task-workspace/binding.d.ts.map +1 -0
  235. package/dist/task-workspace/binding.js +22 -0
  236. package/dist/task-workspace/cleanup.d.ts +4 -0
  237. package/dist/task-workspace/cleanup.d.ts.map +1 -0
  238. package/dist/task-workspace/cleanup.js +428 -0
  239. package/dist/task-workspace/errors.d.ts +14 -0
  240. package/dist/task-workspace/errors.d.ts.map +1 -0
  241. package/dist/task-workspace/errors.js +81 -0
  242. package/dist/task-workspace/evidence.d.ts +32 -0
  243. package/dist/task-workspace/evidence.d.ts.map +1 -0
  244. package/dist/task-workspace/evidence.js +52 -0
  245. package/dist/task-workspace/field-safety.d.ts +3 -0
  246. package/dist/task-workspace/field-safety.d.ts.map +1 -0
  247. package/dist/task-workspace/field-safety.js +42 -0
  248. package/dist/task-workspace/health.d.ts +4 -0
  249. package/dist/task-workspace/health.d.ts.map +1 -0
  250. package/dist/task-workspace/health.js +163 -0
  251. package/dist/task-workspace/lifecycle.d.ts +3 -0
  252. package/dist/task-workspace/lifecycle.d.ts.map +1 -0
  253. package/dist/task-workspace/lifecycle.js +248 -0
  254. package/dist/task-workspace/locks.d.ts +13 -0
  255. package/dist/task-workspace/locks.d.ts.map +1 -0
  256. package/dist/task-workspace/locks.js +44 -0
  257. package/dist/task-workspace/managed-root.d.ts +7 -0
  258. package/dist/task-workspace/managed-root.d.ts.map +1 -0
  259. package/dist/task-workspace/managed-root.js +98 -0
  260. package/dist/task-workspace/mutex.d.ts +8 -0
  261. package/dist/task-workspace/mutex.d.ts.map +1 -0
  262. package/dist/task-workspace/mutex.js +82 -0
  263. package/dist/task-workspace/naming.d.ts +15 -0
  264. package/dist/task-workspace/naming.d.ts.map +1 -0
  265. package/dist/task-workspace/naming.js +0 -0
  266. package/dist/task-workspace/provisioning.d.ts +3 -0
  267. package/dist/task-workspace/provisioning.d.ts.map +1 -0
  268. package/dist/task-workspace/provisioning.js +528 -0
  269. package/dist/task-workspace/reconciliation.d.ts +15 -0
  270. package/dist/task-workspace/reconciliation.d.ts.map +1 -0
  271. package/dist/task-workspace/reconciliation.js +274 -0
  272. package/dist/task-workspace/repair.d.ts +3 -0
  273. package/dist/task-workspace/repair.d.ts.map +1 -0
  274. package/dist/task-workspace/repair.js +286 -0
  275. package/dist/task-workspace/routes.d.ts +19 -0
  276. package/dist/task-workspace/routes.d.ts.map +1 -0
  277. package/dist/task-workspace/routes.js +481 -0
  278. package/dist/task-workspace/store.d.ts +12 -0
  279. package/dist/task-workspace/store.d.ts.map +1 -0
  280. package/dist/task-workspace/store.js +128 -0
  281. package/dist/task-workspace/types.d.ts +170 -0
  282. package/dist/task-workspace/types.d.ts.map +1 -0
  283. package/dist/task-workspace/types.js +5 -0
  284. package/dist/voice-action-governance.d.ts +23 -0
  285. package/dist/voice-action-governance.d.ts.map +1 -0
  286. package/dist/voice-action-governance.js +126 -0
  287. package/dist/voice-handlers.d.ts +6 -0
  288. package/dist/voice-handlers.d.ts.map +1 -0
  289. package/dist/voice-handlers.js +570 -0
  290. package/dist/voice-realtime-grounded-tool.d.ts +31 -0
  291. package/dist/voice-realtime-grounded-tool.d.ts.map +1 -0
  292. package/dist/voice-realtime-grounded-tool.js +322 -0
  293. package/dist/voice-realtime.d.ts +69 -0
  294. package/dist/voice-realtime.d.ts.map +1 -0
  295. package/dist/voice-realtime.js +787 -0
  296. package/dist/workspace-state-handlers.d.ts +5 -0
  297. package/dist/workspace-state-handlers.d.ts.map +1 -0
  298. package/dist/workspace-state-handlers.js +106 -0
  299. package/package.json +20 -19
  300. package/dist/grounded-handoff.d.ts +0 -4
  301. package/dist/grounded-handoff.d.ts.map +0 -1
  302. package/dist/grounded-handoff.js +0 -445
@@ -0,0 +1,168 @@
1
+ // Governed merge execution core for the #478 merge routes (Epic #470, ADR-0065).
2
+ //
3
+ // The merge preview + execute routes share ONE path: resolve and authorize the project workspace, read
4
+ // the provider's content-free merge-readiness facts, and (for execute) drive the #478 merge gateway
5
+ // `runGitMerge` (the SEPARATE merge-orchestration authority — preflight + policy + final-approval gates +
6
+ // the readiness gate, executing through a narrow `gh api` adapter with a dedicated merge-only allowlist)
7
+ // and append a content-free evidence record through the #474 ledger for EVERY terminal outcome (allowed
8
+ // and blocked alike — AC4). No second orchestrator, no generic shell, no widening of the local mutation,
9
+ // publish, or PR allowlists. The Node `gh api` effect is injected via seams so route tests run
10
+ // deterministically against a fake.
11
+ import { deriveEligibleMergeStrategies, evaluateGitPolicy, GIT_DELIVERY_POLICY_SCHEMA_VERSION, GIT_DELIVERY_RISK_CLASS_SEVERITY, gitMergeBlockerActionHintFor, gitMergeReadinessFor, gitMergeRecommendationFor, } from "@oscharko-dev/keiko-contracts";
12
+ import { evaluateGitMergeEffectivePolicy, runGitMerge, } from "@oscharko-dev/keiko-tools";
13
+ import { createNodeGitMergeAdapter } from "@oscharko-dev/keiko-tools/internal/git-mutation";
14
+ import { defaultGitDeliveryActionId, gitDeliveryMutationResponse, persistGitDeliveryEvidence, readWorktreeSnapshotFor, } from "./execution.js";
15
+ // Default trusted merge policy: merge is APPROVAL-GATED — the explicit final, high-risk confirmation a
16
+ // merge requires (AC1). No approval token ⇒ approval-required; every other action kind is fail-closed via
17
+ // the blocked defaultRule. Base-branch and strategy enforcement live in the readiness layer (where merge
18
+ // prerequisites belong), not in this authorization pack. Applies only when governed git delivery is
19
+ // ENABLED and no stricter pack is configured; still EVALUATED every time. A deployment may override with a
20
+ // pack naming specific requiredApprovers.
21
+ export const KEIKO_DEFAULT_MERGE_POLICY_PACK = {
22
+ schemaVersion: GIT_DELIVERY_POLICY_SCHEMA_VERSION,
23
+ repoId: "keiko-merge-default",
24
+ rules: [{ actionKind: "merge", decision: "approval-gated", requiredApprovers: [] }],
25
+ defaultRule: { decision: "blocked" },
26
+ };
27
+ function mergeAdapterFor(workspace, seams, now) {
28
+ if (seams.mergeAdapterFactory !== undefined)
29
+ return seams.mergeAdapterFactory(workspace);
30
+ return createNodeGitMergeAdapter({ workspace, processEnv: process.env, now });
31
+ }
32
+ // Reads the provider's content-free merge-readiness facts. Never throws: a thrown read becomes a
33
+ // provider-error readiness so callers fail closed.
34
+ export async function readMergeProviderReadiness(command, workspace, seams, now) {
35
+ const adapter = mergeAdapterFor(workspace, seams, now);
36
+ try {
37
+ return await adapter.readMergeReadiness({
38
+ ownerAndRepo: command.ownerAndRepo,
39
+ prExternalId: command.prExternalId,
40
+ });
41
+ }
42
+ catch {
43
+ return { providerCapableStrategies: [], providerError: true };
44
+ }
45
+ }
46
+ /**
47
+ * Runs ONE governed merge end-to-end: live snapshot → merge gateway (preflight + policy + approval +
48
+ * readiness gate + execute) → evidence. Returns the gateway lifecycle result; the caller projects it into
49
+ * a content-free HTTP body. Evidence is appended best-effort BEFORE the caller responds, for allowed AND
50
+ * blocked alike.
51
+ */
52
+ export async function executeGovernedMerge(command, approval, workspace, deps, seams) {
53
+ const now = seams.now ?? Date.now;
54
+ const snapshot = await readWorktreeSnapshotFor(workspace, seams, now);
55
+ const adapter = mergeAdapterFor(workspace, seams, now);
56
+ const packs = seams.policyPacks ?? { repoPack: KEIKO_DEFAULT_MERGE_POLICY_PACK };
57
+ const newActionId = seams.newActionId ?? (() => defaultGitDeliveryActionId(command, now()));
58
+ const result = await runGitMerge({ command, approval }, {
59
+ adapter,
60
+ snapshot,
61
+ ...(packs.orgPack !== undefined ? { orgPolicyPack: packs.orgPack } : {}),
62
+ ...(packs.repoPack !== undefined ? { repoPolicyPack: packs.repoPack } : {}),
63
+ ...(seams.strategyPolicy !== undefined ? { strategyPolicy: seams.strategyPolicy } : {}),
64
+ now,
65
+ newActionId,
66
+ });
67
+ persistGitDeliveryEvidence(deps, result.lifecycle, snapshot, workspace.root, now);
68
+ return result;
69
+ }
70
+ function mergeBlockerView(blocker) {
71
+ const actionHint = gitMergeBlockerActionHintFor(blocker.code);
72
+ return {
73
+ code: blocker.code,
74
+ severity: blocker.severity,
75
+ remediation: blocker.remediation,
76
+ ...(actionHint !== undefined ? { actionHint } : {}),
77
+ };
78
+ }
79
+ function mergeBlockerViews(summary) {
80
+ return summary.blockers.map(mergeBlockerView);
81
+ }
82
+ function deriveMergePreviewParts(command, provider, packs, strategyPolicy) {
83
+ const decision = evaluateGitPolicy(packs.orgPack, packs.repoPack, {
84
+ actionKind: "merge",
85
+ targetBranchName: command.baseBranchName,
86
+ activeProviderCapabilities: [],
87
+ });
88
+ const effective = evaluateGitMergeEffectivePolicy(decision, command.baseBranchName, []);
89
+ const eligibility = deriveEligibleMergeStrategies(command.mergeStrategy, strategyPolicy, provider.providerCapableStrategies);
90
+ const readiness = gitMergeReadinessFor({
91
+ ...(provider.pullRequest !== undefined ? { pullRequest: provider.pullRequest } : {}),
92
+ ...(provider.checks !== undefined ? { checks: provider.checks } : {}),
93
+ ...(provider.branchProtection !== undefined
94
+ ? { branchProtection: provider.branchProtection }
95
+ : {}),
96
+ strategyEligible: eligibility.requestedEligible,
97
+ ...(provider.providerError === true ? { providerError: true } : {}),
98
+ });
99
+ const requiresApproval = effective.outcome === "approval-gated";
100
+ return {
101
+ effectiveOutcome: effective.outcome,
102
+ ...(effective.blockReason !== undefined ? { blockReason: effective.blockReason } : {}),
103
+ requiresApproval,
104
+ eligibleStrategies: eligibility.eligible,
105
+ ...(eligibility.selectedDefault !== undefined
106
+ ? { selectedDefaultStrategy: eligibility.selectedDefault }
107
+ : {}),
108
+ requestedStrategyEligible: eligibility.requestedEligible,
109
+ readiness,
110
+ // The preview has no approval token, so approvalSatisfied is false.
111
+ recommendation: gitMergeRecommendationFor(readiness, {
112
+ requiresApproval,
113
+ approvalSatisfied: false,
114
+ }),
115
+ };
116
+ }
117
+ export function buildGitDeliveryMergePreview(command, provider, packs, strategyPolicy) {
118
+ const parts = deriveMergePreviewParts(command, provider, packs, strategyPolicy);
119
+ const riskClass = "protected-or-merge";
120
+ return {
121
+ schemaVersion: "1",
122
+ actionKind: "merge",
123
+ baseBranchName: command.baseBranchName,
124
+ headBranchName: command.headBranchName,
125
+ prExternalId: command.prExternalId,
126
+ riskClass,
127
+ riskSeverity: GIT_DELIVERY_RISK_CLASS_SEVERITY[riskClass],
128
+ requestedStrategy: command.mergeStrategy,
129
+ eligibleStrategies: parts.eligibleStrategies,
130
+ ...(parts.selectedDefaultStrategy !== undefined
131
+ ? { selectedDefaultStrategy: parts.selectedDefaultStrategy }
132
+ : {}),
133
+ requestedStrategyEligible: parts.requestedStrategyEligible,
134
+ policyOutcome: parts.effectiveOutcome,
135
+ ...(parts.blockReason !== undefined ? { policyBlockReason: parts.blockReason } : {}),
136
+ requiresApproval: parts.requiresApproval,
137
+ readiness: {
138
+ mergeable: parts.readiness.mergeable,
139
+ blockers: mergeBlockerViews(parts.readiness),
140
+ },
141
+ recommendation: parts.recommendation,
142
+ };
143
+ }
144
+ export function gitDeliveryMergeExecuteResponse(result) {
145
+ const base = gitDeliveryMutationResponse(result.lifecycle);
146
+ const withReadiness = {
147
+ ...base,
148
+ ...(result.readiness !== undefined
149
+ ? {
150
+ mergeable: result.readiness.mergeable,
151
+ readinessBlockers: mergeBlockerViews(result.readiness),
152
+ }
153
+ : {}),
154
+ ...(result.merged !== undefined ? { merged: result.merged } : {}),
155
+ ...(result.branchDeleted !== undefined ? { branchDeleted: result.branchDeleted } : {}),
156
+ };
157
+ if (result.rejection === undefined) {
158
+ return withReadiness;
159
+ }
160
+ return {
161
+ ...withReadiness,
162
+ mergeRejectionReason: result.rejection.reason,
163
+ recoveryDisposition: result.rejection.disposition,
164
+ ...(result.rejection.actionHint !== undefined
165
+ ? { recoveryActionHint: result.rejection.actionHint }
166
+ : {}),
167
+ };
168
+ }
@@ -0,0 +1,12 @@
1
+ import type { RouteContext, RouteDefinition, RouteResult } from "../routes.js";
2
+ import type { UiHandlerDeps } from "../deps.js";
3
+ import { type GitDeliveryMergeSeams } from "./mergeExecution.js";
4
+ export type GitDeliveryMergeErrorCode = "GIT_DELIVERY_MERGE_BAD_REQUEST" | "GIT_DELIVERY_MERGE_PAYLOAD_TOO_LARGE" | "GIT_DELIVERY_MERGE_FORBIDDEN_PAYLOAD" | "GIT_DELIVERY_MERGE_UNKNOWN_PROJECT" | "GIT_DELIVERY_MERGE_WORKTREE_UNAVAILABLE";
5
+ export interface GitDeliveryMergeRouteOptions {
6
+ readonly execution?: GitDeliveryMergeSeams;
7
+ }
8
+ export declare const createHandleMergePreview: (options?: GitDeliveryMergeRouteOptions) => ((ctx: RouteContext, deps: UiHandlerDeps) => Promise<RouteResult>);
9
+ export declare const createHandleMergeExecute: (options?: GitDeliveryMergeRouteOptions) => ((ctx: RouteContext, deps: UiHandlerDeps) => Promise<RouteResult>);
10
+ export declare const createGitDeliveryMergeRouteGroup: (options?: GitDeliveryMergeRouteOptions) => readonly RouteDefinition[];
11
+ export declare const GIT_DELIVERY_MERGE_ROUTE_GROUP: readonly RouteDefinition[];
12
+ //# sourceMappingURL=mergeRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeRoutes.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/mergeRoutes.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAML,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAa7B,MAAM,MAAM,yBAAyB,GACjC,gCAAgC,GAChC,sCAAsC,GACtC,sCAAsC,GACtC,oCAAoC,GACpC,yCAAyC,CAAC;AAmB9C,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,qBAAqB,CAAC;CAC5C;AA+ID,eAAO,MAAM,wBAAwB,GACnC,UAAS,4BAAiC,KACzC,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAqBnE,CAAC;AAIF,eAAO,MAAM,wBAAwB,GACnC,UAAS,4BAAiC,KACzC,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAmBnE,CAAC;AAIF,eAAO,MAAM,gCAAgC,GAC3C,UAAS,4BAAiC,KACzC,SAAS,eAAe,EAW1B,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,SAAS,eAAe,EACjC,CAAC"}
@@ -0,0 +1,218 @@
1
+ // Governed merge routes (Issue #478, Epic #470, ADR-0065).
2
+ //
3
+ // * POST /api/git-delivery/merge/preview — READ-ONLY. Reads the provider's content-free merge-readiness
4
+ // facts and builds the pre-merge context: the readiness summary (mergeable + severity-ranked
5
+ // blockers), the eligible merge strategies (policy ∩ provider capability — never a hard-coded UI
6
+ // default), the recommendation, the effective policy decision, and whether final approval is
7
+ // required. Never mutates, never records evidence.
8
+ // * POST /api/git-delivery/merge/execute — Governed. Drives the #478 merge gateway end-to-end through
9
+ // executeGovernedMerge (preflight + policy + final-approval + the readiness gate + the dedicated
10
+ // `gh api` merge adapter) and appends content-free evidence for the allowed AND blocked outcome
11
+ // alike. Returns the typed provider-rejection reason + reused recovery hint and the merged /
12
+ // branch-deleted flags.
13
+ //
14
+ // Content-free in evidence: only the merge inputs (PR number, strategy, delete flag) and outcome enter the
15
+ // ledger. CSRF + JSON content type are enforced centrally by server.ts.
16
+ import { isGitDeliveryApprovalRequirement, isGitDeliveryMergeStrategyHint, } from "@oscharko-dev/keiko-contracts";
17
+ import { resolveProjectWorkspace } from "./execution.js";
18
+ import { buildGitDeliveryMergePreview, executeGovernedMerge, gitDeliveryMergeExecuteResponse, KEIKO_DEFAULT_MERGE_POLICY_PACK, readMergeProviderReadiness, } from "./mergeExecution.js";
19
+ import { GitDeliveryBodyTooLargeError, hasOnlyAllowedKeys, isNonEmptyString, isPlainObject, readGitDeliveryBody, scanForbiddenStrings, scanUnsafeFormatChars, } from "./requestGuards.js";
20
+ const SAFE_MESSAGES = {
21
+ GIT_DELIVERY_MERGE_BAD_REQUEST: "The request body is not a valid governed merge.",
22
+ GIT_DELIVERY_MERGE_PAYLOAD_TOO_LARGE: "The governed merge request exceeds the maximum size.",
23
+ GIT_DELIVERY_MERGE_FORBIDDEN_PAYLOAD: "The request contained a forbidden field. Requests may not carry credentials or auth headers.",
24
+ GIT_DELIVERY_MERGE_UNKNOWN_PROJECT: "The requested project is not a known workspace.",
25
+ GIT_DELIVERY_MERGE_WORKTREE_UNAVAILABLE: "The repository worktree could not be inspected. Confirm the project is a Git repository.",
26
+ };
27
+ const errResult = (status, code) => ({
28
+ status,
29
+ body: { error: { code, message: SAFE_MESSAGES[code] } },
30
+ });
31
+ async function readParsed(req) {
32
+ let raw;
33
+ try {
34
+ raw = await readGitDeliveryBody(req);
35
+ }
36
+ catch (error) {
37
+ const result = error instanceof GitDeliveryBodyTooLargeError
38
+ ? errResult(413, "GIT_DELIVERY_MERGE_PAYLOAD_TOO_LARGE")
39
+ : errResult(400, "GIT_DELIVERY_MERGE_BAD_REQUEST");
40
+ return { ok: false, result };
41
+ }
42
+ try {
43
+ return { ok: true, value: JSON.parse(raw) };
44
+ }
45
+ catch {
46
+ return { ok: false, result: errResult(400, "GIT_DELIVERY_MERGE_BAD_REQUEST") };
47
+ }
48
+ }
49
+ // A git ref operand: non-empty, no whitespace, no leading "-" (flag-injection guard), no NUL/control.
50
+ // eslint-disable-next-line no-control-regex -- intentionally matches control chars to REJECT them
51
+ const REF_CONTROL_CHAR = new RegExp("[\u0000-\u001f\u007f]");
52
+ function isSafeGitRef(value) {
53
+ if (typeof value !== "string" || value.length === 0)
54
+ return false;
55
+ if (/\s/.test(value))
56
+ return false;
57
+ if (value.startsWith("-"))
58
+ return false;
59
+ if (REF_CONTROL_CHAR.test(value))
60
+ return false;
61
+ if (value.includes(":"))
62
+ return false;
63
+ return true;
64
+ }
65
+ const OWNER_REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
66
+ const PR_NUMBER_RE = /^[1-9][0-9]{0,9}$/;
67
+ const SHA_RE = /^[0-9a-fA-F]{7,64}$/;
68
+ function isOwnerAndRepo(value) {
69
+ return typeof value === "string" && OWNER_REPO_RE.test(value);
70
+ }
71
+ function isPrNumberString(value) {
72
+ return typeof value === "string" && PR_NUMBER_RE.test(value);
73
+ }
74
+ const ALLOWED_KEYS = new Set([
75
+ "schemaVersion",
76
+ "projectId",
77
+ "kind",
78
+ "ownerAndRepo",
79
+ "prExternalId",
80
+ "baseBranchName",
81
+ "headBranchName",
82
+ "mergeStrategy",
83
+ "deleteBranchAfterMerge",
84
+ "expectedHeadRefHash",
85
+ "approval",
86
+ ]);
87
+ const NO_APPROVAL = { required: false };
88
+ function parseApproval(value) {
89
+ if (value === undefined)
90
+ return NO_APPROVAL;
91
+ return isGitDeliveryApprovalRequirement(value) ? value : undefined;
92
+ }
93
+ function optionalBool(value) {
94
+ if (value === undefined)
95
+ return false;
96
+ return typeof value === "boolean" ? value : undefined;
97
+ }
98
+ function parseExpectedHeadRefHash(value) {
99
+ if (value === undefined)
100
+ return { ok: true };
101
+ if (typeof value === "string" && SHA_RE.test(value))
102
+ return { ok: true, value };
103
+ return { ok: false };
104
+ }
105
+ function scanError(parsed) {
106
+ if (scanForbiddenStrings(parsed)) {
107
+ return errResult(400, "GIT_DELIVERY_MERGE_FORBIDDEN_PAYLOAD");
108
+ }
109
+ if (scanUnsafeFormatChars(parsed)) {
110
+ return errResult(400, "GIT_DELIVERY_MERGE_BAD_REQUEST");
111
+ }
112
+ return undefined;
113
+ }
114
+ function buildMergeCommand(parsed) {
115
+ if (parsed.kind !== "merge" ||
116
+ !isOwnerAndRepo(parsed.ownerAndRepo) ||
117
+ !isPrNumberString(parsed.prExternalId) ||
118
+ !isSafeGitRef(parsed.baseBranchName) ||
119
+ !isSafeGitRef(parsed.headBranchName) ||
120
+ !isGitDeliveryMergeStrategyHint(parsed.mergeStrategy)) {
121
+ return undefined;
122
+ }
123
+ const deleteBranchAfterMerge = optionalBool(parsed.deleteBranchAfterMerge);
124
+ const expectedHead = parseExpectedHeadRefHash(parsed.expectedHeadRefHash);
125
+ if (deleteBranchAfterMerge === undefined || !expectedHead.ok) {
126
+ return undefined;
127
+ }
128
+ return {
129
+ kind: "merge",
130
+ ownerAndRepo: parsed.ownerAndRepo,
131
+ prExternalId: parsed.prExternalId,
132
+ baseBranchName: parsed.baseBranchName,
133
+ headBranchName: parsed.headBranchName,
134
+ mergeStrategy: parsed.mergeStrategy,
135
+ deleteBranchAfterMerge,
136
+ ...(expectedHead.value !== undefined ? { expectedHeadRefHash: expectedHead.value } : {}),
137
+ };
138
+ }
139
+ function validate(parsed) {
140
+ const bad = { kind: "err", result: errResult(400, "GIT_DELIVERY_MERGE_BAD_REQUEST") };
141
+ if (!isPlainObject(parsed) || !hasOnlyAllowedKeys(parsed, ALLOWED_KEYS))
142
+ return bad;
143
+ if (parsed.schemaVersion !== "1" || !isNonEmptyString(parsed.projectId))
144
+ return bad;
145
+ const scanErr = scanError(parsed);
146
+ if (scanErr !== undefined)
147
+ return { kind: "err", result: scanErr };
148
+ const command = buildMergeCommand(parsed);
149
+ const approval = parseApproval(parsed.approval);
150
+ if (command === undefined || approval === undefined)
151
+ return bad;
152
+ return { kind: "ok", value: { projectId: parsed.projectId, command, approval } };
153
+ }
154
+ // ─── Preview handler (read-only) ────────────────────────────────────────────────────────────────
155
+ export const createHandleMergePreview = (options = {}) => {
156
+ const seams = options.execution ?? {};
157
+ const now = () => (seams.now ?? Date.now)();
158
+ return async (ctx, deps) => {
159
+ const read = await readParsed(ctx.req);
160
+ if (!read.ok)
161
+ return read.result;
162
+ const validation = validate(read.value);
163
+ if (validation.kind === "err")
164
+ return validation.result;
165
+ const { projectId, command } = validation.value;
166
+ const workspace = resolveProjectWorkspace(deps, projectId);
167
+ if (workspace === undefined)
168
+ return errResult(404, "GIT_DELIVERY_MERGE_UNKNOWN_PROJECT");
169
+ const packs = seams.policyPacks ?? { repoPack: KEIKO_DEFAULT_MERGE_POLICY_PACK };
170
+ const strategyPolicy = seams.strategyPolicy ?? {
171
+ allowedStrategies: ["squash", "rebase", "merge-commit", "provider-default"],
172
+ };
173
+ const provider = await readMergeProviderReadiness(command, workspace, seams, now);
174
+ return {
175
+ status: 200,
176
+ body: deps.redactor(buildGitDeliveryMergePreview(command, provider, packs, strategyPolicy)),
177
+ };
178
+ };
179
+ };
180
+ // ─── Execute handler (governed) ───────────────────────────────────────────────────────────────
181
+ export const createHandleMergeExecute = (options = {}) => {
182
+ const seams = options.execution ?? {};
183
+ return async (ctx, deps) => {
184
+ const read = await readParsed(ctx.req);
185
+ if (!read.ok)
186
+ return read.result;
187
+ const validation = validate(read.value);
188
+ if (validation.kind === "err")
189
+ return validation.result;
190
+ const { projectId, command, approval } = validation.value;
191
+ const workspace = resolveProjectWorkspace(deps, projectId);
192
+ if (workspace === undefined)
193
+ return errResult(404, "GIT_DELIVERY_MERGE_UNKNOWN_PROJECT");
194
+ let result;
195
+ try {
196
+ result = await executeGovernedMerge(command, approval, workspace, deps, seams);
197
+ }
198
+ catch {
199
+ // Only the read-only snapshot step can throw (not a git repository); the gateway never throws.
200
+ return errResult(409, "GIT_DELIVERY_MERGE_WORKTREE_UNAVAILABLE");
201
+ }
202
+ return { status: 200, body: deps.redactor(gitDeliveryMergeExecuteResponse(result)) };
203
+ };
204
+ };
205
+ // ─── Route group ───────────────────────────────────────────────────────────────────────────────
206
+ export const createGitDeliveryMergeRouteGroup = (options = {}) => [
207
+ {
208
+ method: "POST",
209
+ pattern: "/api/git-delivery/merge/preview",
210
+ handler: createHandleMergePreview(options),
211
+ },
212
+ {
213
+ method: "POST",
214
+ pattern: "/api/git-delivery/merge/execute",
215
+ handler: createHandleMergeExecute(options),
216
+ },
217
+ ];
218
+ export const GIT_DELIVERY_MERGE_ROUTE_GROUP = createGitDeliveryMergeRouteGroup();
@@ -0,0 +1,23 @@
1
+ import type { GitDeliveryEvidenceRecord } from "@oscharko-dev/keiko-contracts";
2
+ import { GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION } from "@oscharko-dev/keiko-contracts";
3
+ import type { EvidenceStore } from "@oscharko-dev/keiko-evidence";
4
+ export declare const GIT_DELIVERY_EVIDENCE_RUNID_PREFIX: "git-delivery-evidence-";
5
+ export declare const GIT_DELIVERY_EVIDENCE_DEFAULT_BUCKET_CAP = 500;
6
+ export interface GitDeliveryEvidenceLedgerDoc {
7
+ readonly schemaVersion: typeof GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION;
8
+ readonly records: readonly GitDeliveryEvidenceRecord[];
9
+ }
10
+ export declare function gitDeliveryEvidenceRunIdFor(nowMs: number): string;
11
+ export interface RecordGitDeliveryEvidenceOptions {
12
+ readonly evidenceStore: EvidenceStore;
13
+ readonly redactString: (input: string) => string;
14
+ readonly maxRecordsPerBucket?: number | undefined;
15
+ readonly onPersistError?: ((error: unknown) => void) | undefined;
16
+ }
17
+ /**
18
+ * Appends one governed Git mutation evidence record to its date-bucketed ledger. Redacts every string
19
+ * leaf, bounds the bucket, and never throws into the caller's path. Returns nothing — audit recording
20
+ * is best-effort and is reported (not propagated) on failure.
21
+ */
22
+ export declare function recordGitDeliveryMutationEvidence(options: RecordGitDeliveryEvidenceOptions, record: GitDeliveryEvidenceRecord): void;
23
+ //# sourceMappingURL=mutationEvidenceLedger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutationEvidenceLedger.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/mutationEvidenceLedger.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,oCAAoC,EAAE,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,eAAO,MAAM,kCAAkC,EAAG,wBAAiC,CAAC;AAGpF,eAAO,MAAM,wCAAwC,MAAM,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,aAAa,EAAE,OAAO,oCAAoC,CAAC;IACpE,QAAQ,CAAC,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACxD;AAID,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IAGtC,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAClE;AA6DD;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,gCAAgC,EACzC,MAAM,EAAE,yBAAyB,GAChC,IAAI,CAUN"}
@@ -0,0 +1,87 @@
1
+ // Governed Git mutation EVIDENCE ledger (Issue #474, Epic #470).
2
+ //
3
+ // The durable, bounded, append-only sink the #472 orchestrator's idempotency-journal comment
4
+ // anticipates ("a durable journal — the evidence ledger, #474"). It records the content-free
5
+ // GitDeliveryEvidenceRecord produced by the keiko-tools builder for EVERY terminal outcome of a
6
+ // governed Git mutation — succeeded, blocked, rejected, failed, recovery-required, or held for
7
+ // approval — so a governance decision is auditable even when no mutation executed.
8
+ //
9
+ // Persistence shape: ONE ledger document per UTC date bucket (runId `git-delivery-evidence-YYYY-MM-DD`),
10
+ // mirroring memory-audit-handler.ts. Stores that expose EvidenceStore.update() serialize the
11
+ // read-append-write step so concurrent appenders never drop a record; stores without it fall back to
12
+ // get+put. The bucket is bounded to the most recent N records so a long-lived ledger cannot grow
13
+ // without limit.
14
+ //
15
+ // Redaction: each record is run through deepRedactStrings before it is serialized (defence-in-depth on
16
+ // top of the builder's by-construction content-free hashing). Persistence failures are caught and
17
+ // reported through an injectable sink; an audit-write failure must NEVER break the user's mutation.
18
+ // Corrupt ledger documents are never reset or overwritten — an append against a corrupt bucket fails
19
+ // closed and preserves the existing artifact for operator investigation.
20
+ import { GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION } from "@oscharko-dev/keiko-contracts";
21
+ import { deepRedactStrings } from "@oscharko-dev/keiko-evidence";
22
+ export const GIT_DELIVERY_EVIDENCE_RUNID_PREFIX = "git-delivery-evidence-";
23
+ // Default bound on records retained per UTC date bucket. The most recent records are kept.
24
+ export const GIT_DELIVERY_EVIDENCE_DEFAULT_BUCKET_CAP = 500;
25
+ // UTC date-bucket runId. 22 chars (`git-delivery-evidence-` is 22) + 10 (`YYYY-MM-DD`) = 32, well
26
+ // under the evidence-store run-id length cap.
27
+ export function gitDeliveryEvidenceRunIdFor(nowMs) {
28
+ const iso = new Date(nowMs).toISOString();
29
+ return `${GIT_DELIVERY_EVIDENCE_RUNID_PREFIX}${iso.slice(0, 10)}`;
30
+ }
31
+ function defaultOnPersistError(error) {
32
+ // eslint-disable-next-line no-console
33
+ console.error("git-delivery evidence ledger: persistence failed", error);
34
+ }
35
+ function isPlainObject(value) {
36
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37
+ }
38
+ // Parses an existing bucket document. Returns the records faithfully (they were written valid).
39
+ // Throws on gross corruption so the caller fails closed and preserves the artifact.
40
+ function parseExistingRecords(json) {
41
+ if (json === undefined) {
42
+ return [];
43
+ }
44
+ let parsed;
45
+ try {
46
+ parsed = JSON.parse(json);
47
+ }
48
+ catch (error) {
49
+ throw new Error("git-delivery evidence ledger is corrupt; refusing to overwrite existing audit evidence", { cause: error });
50
+ }
51
+ if (!isPlainObject(parsed) || !Array.isArray(parsed.records)) {
52
+ throw new Error("git-delivery evidence ledger has an unexpected shape; refusing to overwrite existing audit evidence");
53
+ }
54
+ return parsed.records;
55
+ }
56
+ function boundedDoc(existing, record, cap) {
57
+ const records = [...existing, record];
58
+ return {
59
+ schemaVersion: GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION,
60
+ records: cap > 0 && records.length > cap ? records.slice(records.length - cap) : records,
61
+ };
62
+ }
63
+ function appendRecord(store, runId, record, cap) {
64
+ const append = (existingJson) => JSON.stringify(boundedDoc(parseExistingRecords(existingJson), record, cap));
65
+ if (store.update !== undefined) {
66
+ store.update(runId, append);
67
+ return;
68
+ }
69
+ store.put(runId, append(store.get(runId)));
70
+ }
71
+ /**
72
+ * Appends one governed Git mutation evidence record to its date-bucketed ledger. Redacts every string
73
+ * leaf, bounds the bucket, and never throws into the caller's path. Returns nothing — audit recording
74
+ * is best-effort and is reported (not propagated) on failure.
75
+ */
76
+ export function recordGitDeliveryMutationEvidence(options, record) {
77
+ const onPersistError = options.onPersistError ?? defaultOnPersistError;
78
+ const cap = options.maxRecordsPerBucket ?? GIT_DELIVERY_EVIDENCE_DEFAULT_BUCKET_CAP;
79
+ const safe = deepRedactStrings(record, options.redactString);
80
+ const runId = gitDeliveryEvidenceRunIdFor(record.recordedAtMs);
81
+ try {
82
+ appendRecord(options.evidenceStore, runId, safe, cap);
83
+ }
84
+ catch (error) {
85
+ onPersistError(error);
86
+ }
87
+ }
@@ -0,0 +1,54 @@
1
+ import type { WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
2
+ import { type GitDeliveryApprovalRequirement, type GitDeliveryRepoPolicyPack, type GitDeliveryRiskClass } from "@oscharko-dev/keiko-contracts";
3
+ import { type GitPullRequestAdapter, type GitPullRequestCommand, type GitPullRequestLifecycleResult, type GitWorktreeSnapshot } from "@oscharko-dev/keiko-tools";
4
+ import type { UiHandlerDeps } from "../deps.js";
5
+ import type { GitDeliveryTrustedPolicyPacks } from "./actionSheetProjection.js";
6
+ import { type GitDeliveryMutationResponseBody } from "./execution.js";
7
+ export declare const KEIKO_DEFAULT_PR_POLICY_PACK: GitDeliveryRepoPolicyPack;
8
+ export interface GitDeliveryPullRequestSeams {
9
+ readonly prAdapterFactory?: ((workspace: WorkspaceInfo) => GitPullRequestAdapter) | undefined;
10
+ readonly snapshotReader?: ((workspace: WorkspaceInfo) => Promise<GitWorktreeSnapshot>) | undefined;
11
+ readonly policyPacks?: GitDeliveryTrustedPolicyPacks | undefined;
12
+ readonly now?: (() => number) | undefined;
13
+ readonly newActionId?: (() => string) | undefined;
14
+ }
15
+ /**
16
+ * Runs ONE governed PR operation end-to-end: live snapshot → PR gateway (preflight + policy + approval +
17
+ * execute) → evidence. Returns the gateway lifecycle result; the caller projects it into a content-free
18
+ * HTTP body. Evidence is appended best-effort BEFORE the caller responds, for allowed AND blocked alike.
19
+ */
20
+ export declare function executeGovernedPullRequest(command: GitPullRequestCommand, approval: GitDeliveryApprovalRequirement, workspace: WorkspaceInfo, deps: Pick<UiHandlerDeps, "evidenceStore" | "redactor">, seams: GitDeliveryPullRequestSeams): Promise<GitPullRequestLifecycleResult>;
21
+ export interface GitDeliveryPrReadinessBody {
22
+ readonly objectExists: boolean;
23
+ readonly reviewReady: boolean;
24
+ readonly blockerCodes: readonly string[];
25
+ }
26
+ export interface GitDeliveryPrPreviewBody {
27
+ readonly schemaVersion: "1";
28
+ readonly actionKind: "pr-create" | "pr-update";
29
+ readonly headBranchName: string;
30
+ readonly baseBranchName: string;
31
+ readonly riskClass: GitDeliveryRiskClass;
32
+ readonly riskSeverity: number;
33
+ readonly isDraft: boolean;
34
+ readonly policyOutcome: string;
35
+ readonly policyBlockReason?: string;
36
+ readonly composedTitle: string;
37
+ readonly composedBody: string;
38
+ readonly riskNarrative: string;
39
+ readonly recommendation: string;
40
+ readonly readiness: GitDeliveryPrReadinessBody;
41
+ readonly suggestedLabels: readonly string[];
42
+ readonly suggestedIssueRefs: readonly string[];
43
+ readonly titleByteLength: number;
44
+ readonly bodyByteLength: number;
45
+ }
46
+ export declare function buildGitDeliveryPrPreview(command: GitPullRequestCommand, snapshot: GitWorktreeSnapshot, packs: GitDeliveryTrustedPolicyPacks): GitDeliveryPrPreviewBody;
47
+ export interface GitDeliveryPrExecuteResponseBody extends GitDeliveryMutationResponseBody {
48
+ readonly prRejectionReason?: string;
49
+ readonly recoveryDisposition?: string;
50
+ readonly recoveryActionHint?: string;
51
+ readonly createdPrExternalId?: string;
52
+ }
53
+ export declare function gitDeliveryPrExecuteResponse(result: GitPullRequestLifecycleResult): GitDeliveryPrExecuteResponseBody;
54
+ //# sourceMappingURL=prExecution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prExecution.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/prExecution.ts"],"names":[],"mappings":"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"}