agentic-orchestrator 0.1.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 (439) hide show
  1. package/.dockerignore +24 -0
  2. package/.github/workflows/mcp-contract-validation.yml +38 -0
  3. package/Agentic-Orchestrator.iml +9 -0
  4. package/LICENSE +21 -0
  5. package/README.md +679 -0
  6. package/agentic/orchestrator/agents.yaml +14 -0
  7. package/agentic/orchestrator/gates.yaml +31 -0
  8. package/agentic/orchestrator/policy.yaml +145 -0
  9. package/agentic/orchestrator/prompts/builder.system.md +1 -0
  10. package/agentic/orchestrator/prompts/planner.system.md +15 -0
  11. package/agentic/orchestrator/prompts/qa.system.md +1 -0
  12. package/agentic/orchestrator/schemas/agents.schema.json +49 -0
  13. package/agentic/orchestrator/schemas/gates.schema.json +65 -0
  14. package/agentic/orchestrator/schemas/index.schema.json +108 -0
  15. package/agentic/orchestrator/schemas/plan.schema.json +127 -0
  16. package/agentic/orchestrator/schemas/policy.schema.json +227 -0
  17. package/agentic/orchestrator/schemas/qa_test_index.schema.json +53 -0
  18. package/agentic/orchestrator/schemas/state.schema.json +92 -0
  19. package/agentic/orchestrator/tools/catalog.json +399 -0
  20. package/agentic/orchestrator/tools/errors.schema.json +21 -0
  21. package/agentic/orchestrator/tools/protocol.json +8 -0
  22. package/agentic/orchestrator/tools/schemas/input/collisions.scan.input.schema.json +7 -0
  23. package/agentic/orchestrator/tools/schemas/input/evidence.latest.input.schema.json +15 -0
  24. package/agentic/orchestrator/tools/schemas/input/feature.delete.input.schema.json +42 -0
  25. package/agentic/orchestrator/tools/schemas/input/feature.discover_specs.input.schema.json +7 -0
  26. package/agentic/orchestrator/tools/schemas/input/feature.get_context.input.schema.json +15 -0
  27. package/agentic/orchestrator/tools/schemas/input/feature.init.input.schema.json +21 -0
  28. package/agentic/orchestrator/tools/schemas/input/feature.log_append.input.schema.json +26 -0
  29. package/agentic/orchestrator/tools/schemas/input/feature.ready_to_merge.input.schema.json +34 -0
  30. package/agentic/orchestrator/tools/schemas/input/feature.state_get.input.schema.json +15 -0
  31. package/agentic/orchestrator/tools/schemas/input/feature.state_patch.input.schema.json +28 -0
  32. package/agentic/orchestrator/tools/schemas/input/gates.list.input.schema.json +11 -0
  33. package/agentic/orchestrator/tools/schemas/input/gates.run.input.schema.json +29 -0
  34. package/agentic/orchestrator/tools/schemas/input/locks.acquire.input.schema.json +29 -0
  35. package/agentic/orchestrator/tools/schemas/input/locks.release.input.schema.json +26 -0
  36. package/agentic/orchestrator/tools/schemas/input/mutating.schema.json +14 -0
  37. package/agentic/orchestrator/tools/schemas/input/plan.get.input.schema.json +15 -0
  38. package/agentic/orchestrator/tools/schemas/input/plan.submit.input.schema.json +28 -0
  39. package/agentic/orchestrator/tools/schemas/input/plan.update.input.schema.json +29 -0
  40. package/agentic/orchestrator/tools/schemas/input/qa.test_index_get.input.schema.json +15 -0
  41. package/agentic/orchestrator/tools/schemas/input/qa.test_index_update.input.schema.json +38 -0
  42. package/agentic/orchestrator/tools/schemas/input/read.schema.json +6 -0
  43. package/agentic/orchestrator/tools/schemas/input/repo.apply_patch.input.schema.json +25 -0
  44. package/agentic/orchestrator/tools/schemas/input/repo.diff.input.schema.json +21 -0
  45. package/agentic/orchestrator/tools/schemas/input/repo.diff_bundle.input.schema.json +15 -0
  46. package/agentic/orchestrator/tools/schemas/input/repo.ensure_worktree.input.schema.json +21 -0
  47. package/agentic/orchestrator/tools/schemas/input/repo.read_file.input.schema.json +20 -0
  48. package/agentic/orchestrator/tools/schemas/input/repo.search.input.schema.json +20 -0
  49. package/agentic/orchestrator/tools/schemas/input/repo.status.input.schema.json +15 -0
  50. package/agentic/orchestrator/tools/schemas/input/report.dashboard.input.schema.json +7 -0
  51. package/agentic/orchestrator/tools/schemas/input/report.feature_summary.input.schema.json +15 -0
  52. package/agentic/orchestrator/tools/schemas/output/collisions.scan.output.schema.json +17 -0
  53. package/agentic/orchestrator/tools/schemas/output/evidence.latest.output.schema.json +20 -0
  54. package/agentic/orchestrator/tools/schemas/output/feature.delete.output.schema.json +224 -0
  55. package/agentic/orchestrator/tools/schemas/output/feature.discover_specs.output.schema.json +32 -0
  56. package/agentic/orchestrator/tools/schemas/output/feature.get_context.output.schema.json +40 -0
  57. package/agentic/orchestrator/tools/schemas/output/feature.init.output.schema.json +24 -0
  58. package/agentic/orchestrator/tools/schemas/output/feature.log_append.output.schema.json +24 -0
  59. package/agentic/orchestrator/tools/schemas/output/feature.ready_to_merge.output.schema.json +30 -0
  60. package/agentic/orchestrator/tools/schemas/output/feature.state_get.output.schema.json +18 -0
  61. package/agentic/orchestrator/tools/schemas/output/feature.state_patch.output.schema.json +24 -0
  62. package/agentic/orchestrator/tools/schemas/output/gates.list.output.schema.json +42 -0
  63. package/agentic/orchestrator/tools/schemas/output/gates.run.output.schema.json +37 -0
  64. package/agentic/orchestrator/tools/schemas/output/locks.acquire.output.schema.json +34 -0
  65. package/agentic/orchestrator/tools/schemas/output/locks.release.output.schema.json +24 -0
  66. package/agentic/orchestrator/tools/schemas/output/plan.get.output.schema.json +26 -0
  67. package/agentic/orchestrator/tools/schemas/output/plan.submit.output.schema.json +23 -0
  68. package/agentic/orchestrator/tools/schemas/output/plan.update.output.schema.json +23 -0
  69. package/agentic/orchestrator/tools/schemas/output/qa.test_index_get.output.schema.json +22 -0
  70. package/agentic/orchestrator/tools/schemas/output/qa.test_index_update.output.schema.json +19 -0
  71. package/agentic/orchestrator/tools/schemas/output/repo.apply_patch.output.schema.json +33 -0
  72. package/agentic/orchestrator/tools/schemas/output/repo.diff.output.schema.json +19 -0
  73. package/agentic/orchestrator/tools/schemas/output/repo.diff_bundle.output.schema.json +32 -0
  74. package/agentic/orchestrator/tools/schemas/output/repo.ensure_worktree.output.schema.json +29 -0
  75. package/agentic/orchestrator/tools/schemas/output/repo.read_file.output.schema.json +24 -0
  76. package/agentic/orchestrator/tools/schemas/output/repo.search.output.schema.json +26 -0
  77. package/agentic/orchestrator/tools/schemas/output/repo.status.output.schema.json +27 -0
  78. package/agentic/orchestrator/tools/schemas/output/report.dashboard.output.schema.json +21 -0
  79. package/agentic/orchestrator/tools/schemas/output/report.feature_summary.output.schema.json +36 -0
  80. package/agentic/orchestrator/tools/schemas/output/standard_success.schema.json +6 -0
  81. package/agentic/orchestrator/tools.md +32 -0
  82. package/apps/control-plane/project.json +39 -0
  83. package/apps/control-plane/scripts/validate-architecture-rules.mjs +170 -0
  84. package/apps/control-plane/scripts/validate-docker-mcp-contract.mjs +84 -0
  85. package/apps/control-plane/scripts/validate-mcp-contracts.ts +61 -0
  86. package/apps/control-plane/src/application/services/collision-queue-service.ts +227 -0
  87. package/apps/control-plane/src/application/services/feature-deletion-service.ts +459 -0
  88. package/apps/control-plane/src/application/services/feature-lifecycle-service.ts +177 -0
  89. package/apps/control-plane/src/application/services/feature-state-service.ts +125 -0
  90. package/apps/control-plane/src/application/services/gate-service.ts +232 -0
  91. package/apps/control-plane/src/application/services/lock-service.ts +298 -0
  92. package/apps/control-plane/src/application/services/merge-service.ts +246 -0
  93. package/apps/control-plane/src/application/services/patch-service.ts +259 -0
  94. package/apps/control-plane/src/application/services/plan-service.ts +302 -0
  95. package/apps/control-plane/src/application/services/qa-index-service.ts +98 -0
  96. package/apps/control-plane/src/application/services/reporting-service.ts +120 -0
  97. package/apps/control-plane/src/application/services/run-lease-service.ts +340 -0
  98. package/apps/control-plane/src/application/tools/tool-metadata.ts +56 -0
  99. package/apps/control-plane/src/application/tools/tool-router.ts +43 -0
  100. package/apps/control-plane/src/cli/aop.ts +31 -0
  101. package/apps/control-plane/src/cli/cli-argument-parser.ts +116 -0
  102. package/apps/control-plane/src/cli/delete-command-handler.ts +90 -0
  103. package/apps/control-plane/src/cli/io.ts +14 -0
  104. package/apps/control-plane/src/cli/resume-command-handler.ts +228 -0
  105. package/apps/control-plane/src/cli/run-command-handler.ts +57 -0
  106. package/apps/control-plane/src/cli/spec-ingestion-service.ts +88 -0
  107. package/apps/control-plane/src/cli/spec-input-resolver.ts +95 -0
  108. package/apps/control-plane/src/cli/spec-utils.ts +40 -0
  109. package/apps/control-plane/src/cli/status-command-handler.ts +17 -0
  110. package/apps/control-plane/src/cli/stop-command-handler.ts +5 -0
  111. package/apps/control-plane/src/cli/tooling.ts +36 -0
  112. package/apps/control-plane/src/cli/types.ts +34 -0
  113. package/apps/control-plane/src/core/collisions.ts +121 -0
  114. package/apps/control-plane/src/core/constants.ts +72 -0
  115. package/apps/control-plane/src/core/error-codes.ts +54 -0
  116. package/apps/control-plane/src/core/frontmatter.ts +42 -0
  117. package/apps/control-plane/src/core/fs.ts +173 -0
  118. package/apps/control-plane/src/core/gates.ts +361 -0
  119. package/apps/control-plane/src/core/git.ts +115 -0
  120. package/apps/control-plane/src/core/kernel.ts +1077 -0
  121. package/apps/control-plane/src/core/patch.ts +152 -0
  122. package/apps/control-plane/src/core/path-layout.ts +113 -0
  123. package/apps/control-plane/src/core/path-rules.ts +71 -0
  124. package/apps/control-plane/src/core/qa-index.ts +179 -0
  125. package/apps/control-plane/src/core/response.ts +62 -0
  126. package/apps/control-plane/src/core/runtime-sessions.ts +20 -0
  127. package/apps/control-plane/src/core/schemas.ts +125 -0
  128. package/apps/control-plane/src/index.ts +21 -0
  129. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +100 -0
  130. package/apps/control-plane/src/mcp/kernel-tool-executor.ts +39 -0
  131. package/apps/control-plane/src/mcp/mcp-server-adapter.ts +74 -0
  132. package/apps/control-plane/src/mcp/operation-ledger.ts +108 -0
  133. package/apps/control-plane/src/mcp/protocol-contract.ts +9 -0
  134. package/apps/control-plane/src/mcp/runtime-factory.ts +105 -0
  135. package/apps/control-plane/src/mcp/runtime-types.ts +44 -0
  136. package/apps/control-plane/src/mcp/token-auth-verifier.ts +63 -0
  137. package/apps/control-plane/src/mcp/token-claims-validator.ts +72 -0
  138. package/apps/control-plane/src/mcp/token-codec.ts +62 -0
  139. package/apps/control-plane/src/mcp/tool-authorizer.ts +43 -0
  140. package/apps/control-plane/src/mcp/tool-client.ts +78 -0
  141. package/apps/control-plane/src/mcp/tool-contract-validator.ts +83 -0
  142. package/apps/control-plane/src/mcp/tool-registry-loader.ts +135 -0
  143. package/apps/control-plane/src/mcp/tool-runtime.ts +336 -0
  144. package/apps/control-plane/src/mcp/tools-markdown-generator.ts +26 -0
  145. package/apps/control-plane/src/mcp/transport-types.ts +16 -0
  146. package/apps/control-plane/src/mcp/types.ts +2 -0
  147. package/apps/control-plane/src/providers/providers.ts +177 -0
  148. package/apps/control-plane/src/supervisor/build-wave-executor.ts +55 -0
  149. package/apps/control-plane/src/supervisor/lease-heartbeat-service.ts +22 -0
  150. package/apps/control-plane/src/supervisor/planning-wave-executor.ts +316 -0
  151. package/apps/control-plane/src/supervisor/prompt-bundle-loader.ts +62 -0
  152. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +99 -0
  153. package/apps/control-plane/src/supervisor/run-coordinator.ts +224 -0
  154. package/apps/control-plane/src/supervisor/runtime.ts +347 -0
  155. package/apps/control-plane/src/supervisor/session-orchestrator.ts +268 -0
  156. package/apps/control-plane/src/supervisor/types.ts +149 -0
  157. package/apps/control-plane/src/supervisor/worker-decision-loop.ts +299 -0
  158. package/apps/control-plane/test/aop.spec.ts +101 -0
  159. package/apps/control-plane/test/cli-helpers.spec.ts +102 -0
  160. package/apps/control-plane/test/cli.spec.ts +12 -0
  161. package/apps/control-plane/test/cli.unit.spec.ts +609 -0
  162. package/apps/control-plane/test/collision-queue.spec.ts +158 -0
  163. package/apps/control-plane/test/collisions.spec.ts +138 -0
  164. package/apps/control-plane/test/core-utils.spec.ts +102 -0
  165. package/apps/control-plane/test/delete-command-handler.spec.ts +202 -0
  166. package/apps/control-plane/test/epoch-tracking.spec.ts +121 -0
  167. package/apps/control-plane/test/gates.spec.ts +452 -0
  168. package/apps/control-plane/test/helpers.ts +68 -0
  169. package/apps/control-plane/test/index.spec.ts +18 -0
  170. package/apps/control-plane/test/kernel-collision-replay.spec.ts +222 -0
  171. package/apps/control-plane/test/kernel.branches.spec.ts +321 -0
  172. package/apps/control-plane/test/kernel.coverage.spec.ts +408 -0
  173. package/apps/control-plane/test/kernel.spec.ts +369 -0
  174. package/apps/control-plane/test/mcp-helpers.spec.ts +195 -0
  175. package/apps/control-plane/test/mcp.spec.ts +776 -0
  176. package/apps/control-plane/test/merge-service.spec.ts +357 -0
  177. package/apps/control-plane/test/plan-service.spec.ts +195 -0
  178. package/apps/control-plane/test/planning-wave-executor.spec.ts +229 -0
  179. package/apps/control-plane/test/providers.spec.ts +168 -0
  180. package/apps/control-plane/test/qa-index-service.spec.ts +187 -0
  181. package/apps/control-plane/test/qa-index.spec.ts +317 -0
  182. package/apps/control-plane/test/response.spec.ts +55 -0
  183. package/apps/control-plane/test/run-coordinator.spec.ts +334 -0
  184. package/apps/control-plane/test/schema-date-time.spec.ts +170 -0
  185. package/apps/control-plane/test/service-retry-paths.spec.ts +305 -0
  186. package/apps/control-plane/test/services.spec.ts +693 -0
  187. package/apps/control-plane/test/spec-input-resolver.spec.ts +76 -0
  188. package/apps/control-plane/test/supervisor-collaborators.spec.ts +201 -0
  189. package/apps/control-plane/test/supervisor.calltool.spec.ts +120 -0
  190. package/apps/control-plane/test/supervisor.spec.ts +415 -0
  191. package/apps/control-plane/test/supervisor.unit.spec.ts +522 -0
  192. package/apps/control-plane/test/token-auth-verifier.spec.ts +111 -0
  193. package/apps/control-plane/test/tool-registry-loader.spec.ts +268 -0
  194. package/apps/control-plane/test/tool-runtime.spec.ts +294 -0
  195. package/apps/control-plane/test/worker-decision-loop.spec.ts +587 -0
  196. package/apps/control-plane/tsconfig.build.json +17 -0
  197. package/apps/control-plane/tsconfig.json +11 -0
  198. package/apps/control-plane/vitest.config.ts +28 -0
  199. package/dist/apps/control-plane/application/services/collision-queue-service.d.ts +69 -0
  200. package/dist/apps/control-plane/application/services/collision-queue-service.js +158 -0
  201. package/dist/apps/control-plane/application/services/collision-queue-service.js.map +1 -0
  202. package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +79 -0
  203. package/dist/apps/control-plane/application/services/feature-deletion-service.js +336 -0
  204. package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -0
  205. package/dist/apps/control-plane/application/services/feature-lifecycle-service.d.ts +81 -0
  206. package/dist/apps/control-plane/application/services/feature-lifecycle-service.js +117 -0
  207. package/dist/apps/control-plane/application/services/feature-lifecycle-service.js.map +1 -0
  208. package/dist/apps/control-plane/application/services/feature-state-service.d.ts +34 -0
  209. package/dist/apps/control-plane/application/services/feature-state-service.js +90 -0
  210. package/dist/apps/control-plane/application/services/feature-state-service.js.map +1 -0
  211. package/dist/apps/control-plane/application/services/gate-service.d.ts +46 -0
  212. package/dist/apps/control-plane/application/services/gate-service.js +160 -0
  213. package/dist/apps/control-plane/application/services/gate-service.js.map +1 -0
  214. package/dist/apps/control-plane/application/services/lock-service.d.ts +56 -0
  215. package/dist/apps/control-plane/application/services/lock-service.js +242 -0
  216. package/dist/apps/control-plane/application/services/lock-service.js.map +1 -0
  217. package/dist/apps/control-plane/application/services/merge-service.d.ts +33 -0
  218. package/dist/apps/control-plane/application/services/merge-service.js +194 -0
  219. package/dist/apps/control-plane/application/services/merge-service.js.map +1 -0
  220. package/dist/apps/control-plane/application/services/patch-service.d.ts +39 -0
  221. package/dist/apps/control-plane/application/services/patch-service.js +189 -0
  222. package/dist/apps/control-plane/application/services/patch-service.js.map +1 -0
  223. package/dist/apps/control-plane/application/services/plan-service.d.ts +60 -0
  224. package/dist/apps/control-plane/application/services/plan-service.js +234 -0
  225. package/dist/apps/control-plane/application/services/plan-service.js.map +1 -0
  226. package/dist/apps/control-plane/application/services/qa-index-service.d.ts +26 -0
  227. package/dist/apps/control-plane/application/services/qa-index-service.js +66 -0
  228. package/dist/apps/control-plane/application/services/qa-index-service.js.map +1 -0
  229. package/dist/apps/control-plane/application/services/reporting-service.d.ts +47 -0
  230. package/dist/apps/control-plane/application/services/reporting-service.js +90 -0
  231. package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -0
  232. package/dist/apps/control-plane/application/services/run-lease-service.d.ts +74 -0
  233. package/dist/apps/control-plane/application/services/run-lease-service.js +263 -0
  234. package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -0
  235. package/dist/apps/control-plane/application/tools/tool-metadata.d.ts +8 -0
  236. package/dist/apps/control-plane/application/tools/tool-metadata.js +37 -0
  237. package/dist/apps/control-plane/application/tools/tool-metadata.js.map +1 -0
  238. package/dist/apps/control-plane/application/tools/tool-router.d.ts +16 -0
  239. package/dist/apps/control-plane/application/tools/tool-router.js +25 -0
  240. package/dist/apps/control-plane/application/tools/tool-router.js.map +1 -0
  241. package/dist/apps/control-plane/cli/aop.d.ts +5 -0
  242. package/dist/apps/control-plane/cli/aop.js +19 -0
  243. package/dist/apps/control-plane/cli/aop.js.map +1 -0
  244. package/dist/apps/control-plane/cli/cli-argument-parser.d.ts +5 -0
  245. package/dist/apps/control-plane/cli/cli-argument-parser.js +109 -0
  246. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -0
  247. package/dist/apps/control-plane/cli/delete-command-handler.d.ts +8 -0
  248. package/dist/apps/control-plane/cli/delete-command-handler.js +77 -0
  249. package/dist/apps/control-plane/cli/delete-command-handler.js.map +1 -0
  250. package/dist/apps/control-plane/cli/io.d.ts +2 -0
  251. package/dist/apps/control-plane/cli/io.js +14 -0
  252. package/dist/apps/control-plane/cli/io.js.map +1 -0
  253. package/dist/apps/control-plane/cli/resume-command-handler.d.ts +17 -0
  254. package/dist/apps/control-plane/cli/resume-command-handler.js +178 -0
  255. package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -0
  256. package/dist/apps/control-plane/cli/run-command-handler.d.ts +15 -0
  257. package/dist/apps/control-plane/cli/run-command-handler.js +39 -0
  258. package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -0
  259. package/dist/apps/control-plane/cli/spec-ingestion-service.d.ts +8 -0
  260. package/dist/apps/control-plane/cli/spec-ingestion-service.js +77 -0
  261. package/dist/apps/control-plane/cli/spec-ingestion-service.js.map +1 -0
  262. package/dist/apps/control-plane/cli/spec-input-resolver.d.ts +9 -0
  263. package/dist/apps/control-plane/cli/spec-input-resolver.js +81 -0
  264. package/dist/apps/control-plane/cli/spec-input-resolver.js.map +1 -0
  265. package/dist/apps/control-plane/cli/spec-utils.d.ts +3 -0
  266. package/dist/apps/control-plane/cli/spec-utils.js +36 -0
  267. package/dist/apps/control-plane/cli/spec-utils.js.map +1 -0
  268. package/dist/apps/control-plane/cli/status-command-handler.d.ts +7 -0
  269. package/dist/apps/control-plane/cli/status-command-handler.js +14 -0
  270. package/dist/apps/control-plane/cli/status-command-handler.js.map +1 -0
  271. package/dist/apps/control-plane/cli/stop-command-handler.d.ts +3 -0
  272. package/dist/apps/control-plane/cli/stop-command-handler.js +6 -0
  273. package/dist/apps/control-plane/cli/stop-command-handler.js.map +1 -0
  274. package/dist/apps/control-plane/cli/tooling.d.ts +4 -0
  275. package/dist/apps/control-plane/cli/tooling.js +24 -0
  276. package/dist/apps/control-plane/cli/tooling.js.map +1 -0
  277. package/dist/apps/control-plane/cli/types.d.ts +31 -0
  278. package/dist/apps/control-plane/cli/types.js +2 -0
  279. package/dist/apps/control-plane/cli/types.js.map +1 -0
  280. package/dist/apps/control-plane/core/collisions.d.ts +39 -0
  281. package/dist/apps/control-plane/core/collisions.js +78 -0
  282. package/dist/apps/control-plane/core/collisions.js.map +1 -0
  283. package/dist/apps/control-plane/core/constants.d.ts +64 -0
  284. package/dist/apps/control-plane/core/constants.js +64 -0
  285. package/dist/apps/control-plane/core/constants.js.map +1 -0
  286. package/dist/apps/control-plane/core/error-codes.d.ts +50 -0
  287. package/dist/apps/control-plane/core/error-codes.js +52 -0
  288. package/dist/apps/control-plane/core/error-codes.js.map +1 -0
  289. package/dist/apps/control-plane/core/frontmatter.d.ts +11 -0
  290. package/dist/apps/control-plane/core/frontmatter.js +30 -0
  291. package/dist/apps/control-plane/core/frontmatter.js.map +1 -0
  292. package/dist/apps/control-plane/core/fs.d.ts +33 -0
  293. package/dist/apps/control-plane/core/fs.js +134 -0
  294. package/dist/apps/control-plane/core/fs.js.map +1 -0
  295. package/dist/apps/control-plane/core/gates.d.ts +88 -0
  296. package/dist/apps/control-plane/core/gates.js +229 -0
  297. package/dist/apps/control-plane/core/gates.js.map +1 -0
  298. package/dist/apps/control-plane/core/git.d.ts +31 -0
  299. package/dist/apps/control-plane/core/git.js +79 -0
  300. package/dist/apps/control-plane/core/git.js.map +1 -0
  301. package/dist/apps/control-plane/core/kernel.d.ts +445 -0
  302. package/dist/apps/control-plane/core/kernel.js +805 -0
  303. package/dist/apps/control-plane/core/kernel.js.map +1 -0
  304. package/dist/apps/control-plane/core/patch.d.ts +23 -0
  305. package/dist/apps/control-plane/core/patch.js +118 -0
  306. package/dist/apps/control-plane/core/patch.js.map +1 -0
  307. package/dist/apps/control-plane/core/path-layout.d.ts +23 -0
  308. package/dist/apps/control-plane/core/path-layout.js +90 -0
  309. package/dist/apps/control-plane/core/path-layout.js.map +1 -0
  310. package/dist/apps/control-plane/core/path-rules.d.ts +13 -0
  311. package/dist/apps/control-plane/core/path-rules.js +52 -0
  312. package/dist/apps/control-plane/core/path-rules.js.map +1 -0
  313. package/dist/apps/control-plane/core/qa-index.d.ts +53 -0
  314. package/dist/apps/control-plane/core/qa-index.js +112 -0
  315. package/dist/apps/control-plane/core/qa-index.js.map +1 -0
  316. package/dist/apps/control-plane/core/response.d.ts +19 -0
  317. package/dist/apps/control-plane/core/response.js +34 -0
  318. package/dist/apps/control-plane/core/response.js.map +1 -0
  319. package/dist/apps/control-plane/core/runtime-sessions.d.ts +19 -0
  320. package/dist/apps/control-plane/core/runtime-sessions.js +2 -0
  321. package/dist/apps/control-plane/core/runtime-sessions.js.map +1 -0
  322. package/dist/apps/control-plane/core/schemas.d.ts +23 -0
  323. package/dist/apps/control-plane/core/schemas.js +80 -0
  324. package/dist/apps/control-plane/core/schemas.js.map +1 -0
  325. package/dist/apps/control-plane/index.d.ts +11 -0
  326. package/dist/apps/control-plane/index.js +9 -0
  327. package/dist/apps/control-plane/index.js.map +1 -0
  328. package/dist/apps/control-plane/interfaces/cli/bootstrap.d.ts +2 -0
  329. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +86 -0
  330. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -0
  331. package/dist/apps/control-plane/mcp/kernel-tool-executor.d.ts +14 -0
  332. package/dist/apps/control-plane/mcp/kernel-tool-executor.js +26 -0
  333. package/dist/apps/control-plane/mcp/kernel-tool-executor.js.map +1 -0
  334. package/dist/apps/control-plane/mcp/mcp-server-adapter.d.ts +19 -0
  335. package/dist/apps/control-plane/mcp/mcp-server-adapter.js +55 -0
  336. package/dist/apps/control-plane/mcp/mcp-server-adapter.js.map +1 -0
  337. package/dist/apps/control-plane/mcp/operation-ledger.d.ts +21 -0
  338. package/dist/apps/control-plane/mcp/operation-ledger.js +75 -0
  339. package/dist/apps/control-plane/mcp/operation-ledger.js.map +1 -0
  340. package/dist/apps/control-plane/mcp/protocol-contract.d.ts +8 -0
  341. package/dist/apps/control-plane/mcp/protocol-contract.js +9 -0
  342. package/dist/apps/control-plane/mcp/protocol-contract.js.map +1 -0
  343. package/dist/apps/control-plane/mcp/runtime-factory.d.ts +38 -0
  344. package/dist/apps/control-plane/mcp/runtime-factory.js +71 -0
  345. package/dist/apps/control-plane/mcp/runtime-factory.js.map +1 -0
  346. package/dist/apps/control-plane/mcp/runtime-types.d.ts +40 -0
  347. package/dist/apps/control-plane/mcp/runtime-types.js +2 -0
  348. package/dist/apps/control-plane/mcp/runtime-types.js.map +1 -0
  349. package/dist/apps/control-plane/mcp/token-auth-verifier.d.ts +24 -0
  350. package/dist/apps/control-plane/mcp/token-auth-verifier.js +45 -0
  351. package/dist/apps/control-plane/mcp/token-auth-verifier.js.map +1 -0
  352. package/dist/apps/control-plane/mcp/token-claims-validator.d.ts +9 -0
  353. package/dist/apps/control-plane/mcp/token-claims-validator.js +62 -0
  354. package/dist/apps/control-plane/mcp/token-claims-validator.js.map +1 -0
  355. package/dist/apps/control-plane/mcp/token-codec.d.ts +11 -0
  356. package/dist/apps/control-plane/mcp/token-codec.js +46 -0
  357. package/dist/apps/control-plane/mcp/token-codec.js.map +1 -0
  358. package/dist/apps/control-plane/mcp/tool-authorizer.d.ts +8 -0
  359. package/dist/apps/control-plane/mcp/tool-authorizer.js +36 -0
  360. package/dist/apps/control-plane/mcp/tool-authorizer.js.map +1 -0
  361. package/dist/apps/control-plane/mcp/tool-client.d.ts +30 -0
  362. package/dist/apps/control-plane/mcp/tool-client.js +50 -0
  363. package/dist/apps/control-plane/mcp/tool-client.js.map +1 -0
  364. package/dist/apps/control-plane/mcp/tool-contract-validator.d.ts +29 -0
  365. package/dist/apps/control-plane/mcp/tool-contract-validator.js +61 -0
  366. package/dist/apps/control-plane/mcp/tool-contract-validator.js.map +1 -0
  367. package/dist/apps/control-plane/mcp/tool-registry-loader.d.ts +15 -0
  368. package/dist/apps/control-plane/mcp/tool-registry-loader.js +109 -0
  369. package/dist/apps/control-plane/mcp/tool-registry-loader.js.map +1 -0
  370. package/dist/apps/control-plane/mcp/tool-runtime.d.ts +34 -0
  371. package/dist/apps/control-plane/mcp/tool-runtime.js +252 -0
  372. package/dist/apps/control-plane/mcp/tool-runtime.js.map +1 -0
  373. package/dist/apps/control-plane/mcp/tools-markdown-generator.d.ts +7 -0
  374. package/dist/apps/control-plane/mcp/tools-markdown-generator.js +22 -0
  375. package/dist/apps/control-plane/mcp/tools-markdown-generator.js.map +1 -0
  376. package/dist/apps/control-plane/mcp/transport-types.d.ts +14 -0
  377. package/dist/apps/control-plane/mcp/transport-types.js +2 -0
  378. package/dist/apps/control-plane/mcp/transport-types.js.map +1 -0
  379. package/dist/apps/control-plane/mcp/types.d.ts +2 -0
  380. package/dist/apps/control-plane/mcp/types.js +3 -0
  381. package/dist/apps/control-plane/mcp/types.js.map +1 -0
  382. package/dist/apps/control-plane/providers/providers.d.ts +72 -0
  383. package/dist/apps/control-plane/providers/providers.js +94 -0
  384. package/dist/apps/control-plane/providers/providers.js.map +1 -0
  385. package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +13 -0
  386. package/dist/apps/control-plane/supervisor/build-wave-executor.js +40 -0
  387. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -0
  388. package/dist/apps/control-plane/supervisor/lease-heartbeat-service.d.ts +12 -0
  389. package/dist/apps/control-plane/supervisor/lease-heartbeat-service.js +14 -0
  390. package/dist/apps/control-plane/supervisor/lease-heartbeat-service.js.map +1 -0
  391. package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +19 -0
  392. package/dist/apps/control-plane/supervisor/planning-wave-executor.js +249 -0
  393. package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -0
  394. package/dist/apps/control-plane/supervisor/prompt-bundle-loader.d.ts +9 -0
  395. package/dist/apps/control-plane/supervisor/prompt-bundle-loader.js +53 -0
  396. package/dist/apps/control-plane/supervisor/prompt-bundle-loader.js.map +1 -0
  397. package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +24 -0
  398. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +70 -0
  399. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -0
  400. package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +49 -0
  401. package/dist/apps/control-plane/supervisor/run-coordinator.js +162 -0
  402. package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -0
  403. package/dist/apps/control-plane/supervisor/runtime.d.ts +58 -0
  404. package/dist/apps/control-plane/supervisor/runtime.js +270 -0
  405. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -0
  406. package/dist/apps/control-plane/supervisor/session-orchestrator.d.ts +29 -0
  407. package/dist/apps/control-plane/supervisor/session-orchestrator.js +211 -0
  408. package/dist/apps/control-plane/supervisor/session-orchestrator.js.map +1 -0
  409. package/dist/apps/control-plane/supervisor/types.d.ts +148 -0
  410. package/dist/apps/control-plane/supervisor/types.js +2 -0
  411. package/dist/apps/control-plane/supervisor/types.js.map +1 -0
  412. package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +37 -0
  413. package/dist/apps/control-plane/supervisor/worker-decision-loop.js +236 -0
  414. package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -0
  415. package/docker/mcp.Dockerfile +14 -0
  416. package/docker/mcp.compose.yaml +15 -0
  417. package/docker/mcp.entrypoint.sh +17 -0
  418. package/eslint.config.mjs +93 -0
  419. package/example-configurations/README.md +26 -0
  420. package/example-configurations/java/agents.yaml +14 -0
  421. package/example-configurations/java/gates.yaml +29 -0
  422. package/example-configurations/java/policy.yaml +148 -0
  423. package/example-configurations/node/agents.yaml +14 -0
  424. package/example-configurations/node/gates.yaml +32 -0
  425. package/example-configurations/node/policy.yaml +143 -0
  426. package/nx.json +16 -0
  427. package/package.json +39 -0
  428. package/prompts/vitest-testing-standards.instructions.md +204 -0
  429. package/scripts/dev-shell-env.sh +7 -0
  430. package/scripts/nx-safe.mjs +33 -0
  431. package/spec-files/agentic_orchestrator_cli_delete_command_spec.md +310 -0
  432. package/spec-files/agentic_orchestrator_dot_aop_generated_artifacts_spec.md +211 -0
  433. package/spec-files/agentic_orchestrator_mcp_formalization_spec.md +379 -0
  434. package/spec-files/agentic_orchestrator_oop_refactor_spec.md +415 -0
  435. package/spec-files/agentic_orchestrator_single_global_orchestrator_spec.md +265 -0
  436. package/spec-files/agentic_orchestrator_spec.md +1334 -0
  437. package/spec-files/progress.md +452 -0
  438. package/tsconfig.base.json +15 -0
  439. package/tsconfig.json +11 -0
@@ -0,0 +1,1077 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ensureDir, pathExists, readJson, atomicWriteJson, atomicWriteFile, withFileLock, nowIso, stableHash } from './fs.js';
4
+ import { SchemaRegistry, loadAndValidateYaml } from './schemas.js';
5
+ import { normalizeRepoPath } from './path-rules.js';
6
+ import { parseFrontMatter, buildFrontMatter } from './frontmatter.js';
7
+ import { runGit, runCommand } from './git.js';
8
+ import { ERROR_CODES } from './error-codes.js';
9
+ import { ok, fail, withSuggestedActions, type ToolResponse } from './response.js';
10
+ import { ALLOWED_ACTORS, DEFAULT_CLUSTER, DEFAULT_ROLE_STATUS, GATE_RESULT, STATUS, TOOLS } from './constants.js';
11
+ import { AopPathLayout, ensureAopRuntimeLayout } from './path-layout.js';
12
+ import type { RuntimeSessionsSnapshot } from './runtime-sessions.js';
13
+ import { ToolRegistryLoader } from '../mcp/tool-registry-loader.js';
14
+ import { ToolHandlerRegistry, ToolRouter, type ToolHandlerContext } from '../application/tools/tool-router.js';
15
+ import { RunLeaseService, type AcquireRunLeaseInput } from '../application/services/run-lease-service.js';
16
+ import { LockService } from '../application/services/lock-service.js';
17
+ import { ReportingService } from '../application/services/reporting-service.js';
18
+ import { FeatureStateService } from '../application/services/feature-state-service.js';
19
+ import { FeatureLifecycleService } from '../application/services/feature-lifecycle-service.js';
20
+ import { PlanService } from '../application/services/plan-service.js';
21
+ import { PatchService } from '../application/services/patch-service.js';
22
+ import { GateService } from '../application/services/gate-service.js';
23
+ import { QaIndexService } from '../application/services/qa-index-service.js';
24
+ import { MergeService } from '../application/services/merge-service.js';
25
+ import { CollisionQueueService } from '../application/services/collision-queue-service.js';
26
+ import { FeatureDeletionService, type FeatureDeleteResult } from '../application/services/feature-deletion-service.js';
27
+
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ type AnyRecord = Record<string, any>;
30
+
31
+ interface KernelContext {
32
+ actor_type?: string;
33
+ actor_id?: string;
34
+ }
35
+
36
+ export interface AgentsRoleConfig {
37
+ system_prompt_path?: string;
38
+ }
39
+
40
+ export interface AgentsRuntimeConfig {
41
+ default_provider?: string;
42
+ default_model?: string;
43
+ provider_config_env?: string;
44
+ }
45
+
46
+ export interface AgentsConfigSnapshot {
47
+ version?: number;
48
+ roles?: Record<string, AgentsRoleConfig>;
49
+ missing_prompt_behavior?: 'ignore' | 'error' | string;
50
+ runtime?: AgentsRuntimeConfig;
51
+ }
52
+
53
+ function asArray<T = unknown>(value: unknown): T[] {
54
+ return Array.isArray(value) ? (value as T[]) : [];
55
+ }
56
+
57
+ function readStringField(record: AnyRecord, key: string): string | null {
58
+ const value = record[key];
59
+ return typeof value === 'string' ? value : null;
60
+ }
61
+
62
+ function readNumberField(record: AnyRecord, key: string): number | null {
63
+ const value = record[key];
64
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
65
+ }
66
+
67
+ function readBooleanField(record: AnyRecord, key: string): boolean | null {
68
+ const value = record[key];
69
+ return typeof value === 'boolean' ? value : null;
70
+ }
71
+
72
+ function readObjectField(record: AnyRecord, key: string): AnyRecord {
73
+ const value = record[key];
74
+ return value && typeof value === 'object' ? (value as AnyRecord) : {};
75
+ }
76
+
77
+ interface PolicyConfigSnapshot extends AnyRecord {
78
+ rbac?: Record<string, string[]>;
79
+ }
80
+
81
+ function normalizeSet(array: string[]): string[] {
82
+ return [...new Set(array)].sort((a, b) => a.localeCompare(b));
83
+ }
84
+
85
+ export class AopKernel {
86
+ repoRoot: string;
87
+ schemaRegistry: SchemaRegistry;
88
+ loaded: boolean;
89
+ gatesConfig: AnyRecord;
90
+ policy: PolicyConfigSnapshot;
91
+ agentsConfig: AgentsConfigSnapshot;
92
+ toolRegistry: AnyRecord | null;
93
+ toolHandlers: ToolHandlerRegistry;
94
+ toolRouter: ToolRouter;
95
+ private readonly runLeaseService: RunLeaseService;
96
+ private readonly lockService: LockService;
97
+ readonly collisionQueueService: CollisionQueueService;
98
+ private readonly reportingService: ReportingService;
99
+ private readonly featureStateService: FeatureStateService;
100
+ private readonly featureLifecycleService: FeatureLifecycleService;
101
+ private readonly planService: PlanService;
102
+ private readonly patchService: PatchService;
103
+ private readonly gateService: GateService;
104
+ private readonly qaIndexService: QaIndexService;
105
+ private readonly mergeService: MergeService;
106
+ private readonly featureDeletionService: FeatureDeletionService;
107
+ private readonly pathLayout: AopPathLayout;
108
+
109
+ constructor(repoRoot: string) {
110
+ this.repoRoot = repoRoot;
111
+ this.pathLayout = new AopPathLayout(repoRoot);
112
+ this.schemaRegistry = new SchemaRegistry(repoRoot);
113
+ this.loaded = false;
114
+ this.gatesConfig = {};
115
+ this.policy = {};
116
+ this.agentsConfig = {};
117
+ this.toolRegistry = null;
118
+ this.toolHandlers = new ToolHandlerRegistry();
119
+ this.registerToolHandlers();
120
+ this.runLeaseService = new RunLeaseService(this);
121
+ this.collisionQueueService = new CollisionQueueService(this);
122
+ this.lockService = new LockService(this);
123
+ this.reportingService = new ReportingService(this);
124
+ this.featureStateService = new FeatureStateService(this);
125
+ this.featureLifecycleService = new FeatureLifecycleService(this);
126
+ this.planService = new PlanService(this);
127
+ this.patchService = new PatchService(this);
128
+ this.gateService = new GateService(this);
129
+ this.qaIndexService = new QaIndexService(this);
130
+ this.mergeService = new MergeService(this);
131
+ this.featureDeletionService = new FeatureDeletionService(this);
132
+ this.toolRouter = new ToolRouter(this.toolHandlers, (toolName) =>
133
+ Promise.resolve(
134
+ fail(ERROR_CODES.INVALID_ARGUMENT, `Unknown tool ${toolName}`, {
135
+ retryable: false,
136
+ requires_human: true
137
+ })
138
+ )
139
+ );
140
+ }
141
+
142
+ getRepoRoot(): string {
143
+ return this.repoRoot;
144
+ }
145
+
146
+ getAgentsConfig(): AgentsConfigSnapshot {
147
+ return this.agentsConfig;
148
+ }
149
+
150
+ getRbacPolicy(): Record<string, string[]> {
151
+ return this.policy.rbac ?? {};
152
+ }
153
+
154
+ getPolicySnapshot(): AnyRecord {
155
+ return this.policy;
156
+ }
157
+
158
+ getGatesConfig(): AnyRecord {
159
+ return this.gatesConfig;
160
+ }
161
+
162
+ getFeaturesDir(): string {
163
+ return this.featuresDir;
164
+ }
165
+
166
+ get orchestratorDir() {
167
+ return this.pathLayout.orchestratorRoot;
168
+ }
169
+
170
+ get featuresDir() {
171
+ return this.pathLayout.featuresRoot;
172
+ }
173
+
174
+ get indexPath() {
175
+ return this.pathLayout.indexPath;
176
+ }
177
+
178
+ get indexLockPath() {
179
+ return this.pathLayout.indexLockPath;
180
+ }
181
+
182
+ runLeaseTtlSeconds() {
183
+ const configured = Number(this.policy?.locks?.lease_ttl_seconds ?? 300);
184
+ if (!Number.isFinite(configured) || configured <= 0) {
185
+ return 300;
186
+ }
187
+ return Math.floor(configured);
188
+ }
189
+
190
+ emptyRuntimeSessions(at = nowIso()): RuntimeSessionsSnapshot {
191
+ return {
192
+ run_id: 'none',
193
+ orchestrator_session_id: 'unknown',
194
+ provider: 'unknown',
195
+ model: 'unknown',
196
+ provider_config_ref_hash: stableHash('none'),
197
+ owner_instance_id: 'none',
198
+ lease_id: 'none',
199
+ started_at: at,
200
+ last_heartbeat_at: at,
201
+ lease_expires_at: at,
202
+ orchestrator_epoch: 0,
203
+ feature_sessions: {}
204
+ };
205
+ }
206
+
207
+ normalizeRuntimeSessions(value: unknown, at = nowIso()): RuntimeSessionsSnapshot {
208
+ const fallback = this.emptyRuntimeSessions(at);
209
+ const source = value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
210
+ const featureSessionsInput =
211
+ source.feature_sessions && typeof source.feature_sessions === 'object'
212
+ ? (source.feature_sessions as Record<string, unknown>)
213
+ : {};
214
+ const featureSessions: RuntimeSessionsSnapshot['feature_sessions'] = {};
215
+
216
+ for (const [featureId, raw] of Object.entries(featureSessionsInput)) {
217
+ if (!featureId || typeof raw !== 'object' || !raw) {
218
+ continue;
219
+ }
220
+ const typed = raw as Record<string, unknown>;
221
+ featureSessions[featureId] = {
222
+ planner_session_id:
223
+ typeof typed.planner_session_id === 'string' ? typed.planner_session_id : 'unassigned',
224
+ builder_session_id:
225
+ typeof typed.builder_session_id === 'string' ? typed.builder_session_id : 'unassigned',
226
+ qa_session_id: typeof typed.qa_session_id === 'string' ? typed.qa_session_id : 'unassigned'
227
+ };
228
+ }
229
+
230
+ const epoch =
231
+ typeof source.orchestrator_epoch === 'number' && Number.isFinite(source.orchestrator_epoch)
232
+ ? Math.max(0, Math.floor(source.orchestrator_epoch))
233
+ : 0;
234
+
235
+ return {
236
+ run_id: typeof source.run_id === 'string' && source.run_id ? source.run_id : fallback.run_id,
237
+ orchestrator_session_id:
238
+ typeof source.orchestrator_session_id === 'string' && source.orchestrator_session_id
239
+ ? source.orchestrator_session_id
240
+ : fallback.orchestrator_session_id,
241
+ provider: typeof source.provider === 'string' && source.provider ? source.provider : fallback.provider,
242
+ model: typeof source.model === 'string' && source.model ? source.model : fallback.model,
243
+ provider_config_ref_hash:
244
+ typeof source.provider_config_ref_hash === 'string' && source.provider_config_ref_hash
245
+ ? source.provider_config_ref_hash
246
+ : fallback.provider_config_ref_hash,
247
+ owner_instance_id:
248
+ typeof source.owner_instance_id === 'string' && source.owner_instance_id
249
+ ? source.owner_instance_id
250
+ : fallback.owner_instance_id,
251
+ lease_id: typeof source.lease_id === 'string' && source.lease_id ? source.lease_id : fallback.lease_id,
252
+ started_at: typeof source.started_at === 'string' && source.started_at ? source.started_at : fallback.started_at,
253
+ last_heartbeat_at:
254
+ typeof source.last_heartbeat_at === 'string' && source.last_heartbeat_at
255
+ ? source.last_heartbeat_at
256
+ : fallback.last_heartbeat_at,
257
+ lease_expires_at:
258
+ typeof source.lease_expires_at === 'string' && source.lease_expires_at
259
+ ? source.lease_expires_at
260
+ : fallback.lease_expires_at,
261
+ orchestrator_epoch: epoch,
262
+ feature_sessions: featureSessions
263
+ };
264
+ }
265
+
266
+ normalizeIndexShape(value: unknown): AnyRecord {
267
+ const now = nowIso();
268
+ const source = value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
269
+ return {
270
+ version: typeof source.version === 'number' && Number.isFinite(source.version) ? Math.max(1, Math.floor(source.version)) : 1,
271
+ active: normalizeSet(asArray<string>(source.active).filter((item) => typeof item === 'string')),
272
+ blocked: normalizeSet(asArray<string>(source.blocked).filter((item) => typeof item === 'string')),
273
+ merged: normalizeSet(asArray<string>(source.merged).filter((item) => typeof item === 'string')),
274
+ locks: source.locks && typeof source.locks === 'object' ? source.locks : {},
275
+ lock_leases: source.lock_leases && typeof source.lock_leases === 'object' ? source.lock_leases : {},
276
+ blocked_queue: asArray(source.blocked_queue).filter((item) => item && typeof item === 'object'),
277
+ updated_at: typeof source.updated_at === 'string' && source.updated_at ? source.updated_at : now,
278
+ runtime_sessions: this.normalizeRuntimeSessions(source.runtime_sessions, now)
279
+ };
280
+ }
281
+
282
+ isRunLeaseFresh(runtimeSessions: RuntimeSessionsSnapshot): boolean {
283
+ const expiry = new Date(runtimeSessions.lease_expires_at).getTime();
284
+ if (!Number.isFinite(expiry)) {
285
+ return false;
286
+ }
287
+ return expiry > Date.now();
288
+ }
289
+
290
+ async load() {
291
+ await ensureAopRuntimeLayout(this.pathLayout);
292
+
293
+ const gatesPath = path.join(this.orchestratorDir, 'gates.yaml');
294
+ const policyPath = path.join(this.orchestratorDir, 'policy.yaml');
295
+ const agentsPath = path.join(this.orchestratorDir, 'agents.yaml');
296
+
297
+ const gates = await loadAndValidateYaml(this.schemaRegistry, 'gates.schema.json', gatesPath);
298
+ if (!gates.validation.valid) {
299
+ throw new Error(`invalid_gates_yaml:${JSON.stringify(gates.validation.errors)}`);
300
+ }
301
+
302
+ const policy = await loadAndValidateYaml(this.schemaRegistry, 'policy.schema.json', policyPath);
303
+ if (!policy.validation.valid) {
304
+ throw new Error(`invalid_policy_yaml:${JSON.stringify(policy.validation.errors)}`);
305
+ }
306
+
307
+ const parsedPolicy = policy.parsed as PolicyConfigSnapshot;
308
+ const implementation = readObjectField(parsedPolicy, 'implementation');
309
+ const testing = readObjectField(parsedPolicy, 'testing');
310
+
311
+ if (readStringField(implementation, 'workspace') !== 'nx') {
312
+ throw new Error(ERROR_CODES.INVALID_WORKSPACE_IMPLEMENTATION);
313
+ }
314
+ if (readStringField(testing, 'framework') !== 'vitest') {
315
+ throw new Error(ERROR_CODES.INVALID_WORKSPACE_IMPLEMENTATION);
316
+ }
317
+
318
+ const agentsExists = await pathExists(agentsPath);
319
+ let agents = { parsed: { version: 1, roles: {} }, validation: { valid: true, errors: [] } };
320
+ if (agentsExists) {
321
+ agents = await loadAndValidateYaml(this.schemaRegistry, 'agents.schema.json', agentsPath);
322
+ if (!agents.validation.valid) {
323
+ throw new Error(`invalid_agents_yaml:${JSON.stringify(agents.validation.errors)}`);
324
+ }
325
+ }
326
+
327
+ this.gatesConfig = gates.parsed;
328
+ this.policy = parsedPolicy;
329
+ this.agentsConfig = agents.parsed as AgentsConfigSnapshot;
330
+ const registryLoader = new ToolRegistryLoader(this.repoRoot);
331
+ this.toolRegistry = await registryLoader.load();
332
+ this.loaded = true;
333
+ }
334
+
335
+ async ensureLoaded() {
336
+ if (!this.loaded) {
337
+ await this.load();
338
+ }
339
+ }
340
+
341
+ authorize(actorType: string, toolName: string): boolean {
342
+ if (!ALLOWED_ACTORS.has(actorType)) {
343
+ return false;
344
+ }
345
+
346
+ const allowedTools = this.getRbacPolicy()[actorType] ?? [];
347
+ if (allowedTools.includes('*')) {
348
+ return true;
349
+ }
350
+ return allowedTools.includes(toolName);
351
+ }
352
+
353
+ async invoke(toolName: string, args: AnyRecord = {}, context: KernelContext = {}): Promise<AnyRecord> {
354
+ await this.ensureLoaded();
355
+ const actorType = context.actor_type ?? 'system';
356
+ const actorId = context.actor_id ?? 'system';
357
+
358
+ if (!this.authorize(actorType, toolName)) {
359
+ return fail(
360
+ ERROR_CODES.FORBIDDEN_TOOL_FOR_ROLE,
361
+ `Tool ${toolName} is not allowed for role ${actorType}`,
362
+ withSuggestedActions({ actor_type: actorType, tool_name: toolName, retryable: false, requires_human: true }, ['Use orchestrator role for this operation'])
363
+ );
364
+ }
365
+
366
+ try {
367
+ const result = (await this.dispatchTool(toolName, args, { actorType, actorId })) as AnyRecord;
368
+ return ok(result.data ?? result, result.evidence);
369
+ } catch (error) {
370
+ return this.normalizeError(error);
371
+ }
372
+ }
373
+
374
+ normalizeError(error: unknown): ToolResponse {
375
+ if (!error) {
376
+ return fail(ERROR_CODES.INTERNAL_ERROR, 'Unknown error');
377
+ }
378
+
379
+ if (typeof error === 'object' && error !== null && 'normalizedResponse' in error) {
380
+ return (error as { normalizedResponse: ToolResponse }).normalizedResponse;
381
+ }
382
+
383
+ const message =
384
+ typeof error === 'object' && error !== null && 'message' in error
385
+ ? (error as { message?: unknown }).message
386
+ : undefined;
387
+ const text = String(message ?? error);
388
+
389
+ if (text.startsWith('path_out_of_bounds')) {
390
+ return fail(ERROR_CODES.PATH_OUT_OF_BOUNDS, 'Path escapes repository root', {
391
+ retryable: false,
392
+ requires_human: true
393
+ });
394
+ }
395
+
396
+ if (text.startsWith('lock_timeout:')) {
397
+ return fail(ERROR_CODES.LOCK_CONFLICT, 'Timed out waiting for lock', {
398
+ retryable: true,
399
+ requires_human: false
400
+ });
401
+ }
402
+
403
+ if (text.startsWith('unknown_gate_profile_or_mode:')) {
404
+ return fail(ERROR_CODES.UNKNOWN_GATE_PROFILE_OR_MODE, 'Unknown gate profile or mode', {
405
+ value: text.split(':')[1],
406
+ retryable: false,
407
+ requires_human: true
408
+ });
409
+ }
410
+
411
+ if (text.startsWith('unsupported_parser:')) {
412
+ return fail(ERROR_CODES.UNSUPPORTED_PARSER, 'Gate parser type is not supported', {
413
+ parser: text.split(':')[1],
414
+ retryable: false,
415
+ requires_human: true
416
+ });
417
+ }
418
+
419
+ if (text.includes('qa_index_version_conflict')) {
420
+ return fail(ERROR_CODES.QA_INDEX_VERSION_CONFLICT, 'QA index expected_version did not match current version', {
421
+ retryable: true,
422
+ requires_human: false
423
+ });
424
+ }
425
+
426
+ return fail(ERROR_CODES.INTERNAL_ERROR, text, {
427
+ retryable: false,
428
+ requires_human: true
429
+ });
430
+ }
431
+
432
+ async dispatchTool(toolName: string, args: AnyRecord, context: ToolHandlerContext): Promise<unknown> {
433
+ return await this.toolRouter.route(toolName, args, context);
434
+ }
435
+
436
+ private registerToolHandlers(): void {
437
+ this.toolHandlers.register(TOOLS.FEATURE_DISCOVER_SPECS, async () => await this.featureDiscoverSpecs());
438
+ this.toolHandlers.register(TOOLS.FEATURE_INIT, async (args) => await this.featureInit(readStringField(args, 'feature_id')));
439
+ this.toolHandlers.register(TOOLS.FEATURE_GET_CONTEXT, async (args) =>
440
+ await this.featureGetContext(readStringField(args, 'feature_id'))
441
+ );
442
+ this.toolHandlers.register(TOOLS.FEATURE_STATE_GET, async (args) =>
443
+ await this.featureStateGet(readStringField(args, 'feature_id'))
444
+ );
445
+ this.toolHandlers.register(TOOLS.FEATURE_STATE_PATCH, async (args) =>
446
+ await this.featureStatePatch(
447
+ readStringField(args, 'feature_id'),
448
+ readNumberField(args, 'expected_version'),
449
+ args.patch
450
+ )
451
+ );
452
+ this.toolHandlers.register(TOOLS.FEATURE_LOG_APPEND, async (args, context) =>
453
+ await this.featureLogAppend(readStringField(args, 'feature_id'), readStringField(args, 'note'), context)
454
+ );
455
+ this.toolHandlers.register(TOOLS.PLAN_SUBMIT, async (args) =>
456
+ await this.planSubmit(readStringField(args, 'feature_id'), args.plan_json, readNumberField(args, 'expected_version'))
457
+ );
458
+ this.toolHandlers.register(TOOLS.PLAN_GET, async (args) => await this.planGet(readStringField(args, 'feature_id')));
459
+ this.toolHandlers.register(TOOLS.PLAN_UPDATE, async (args) =>
460
+ await this.planUpdate(readStringField(args, 'feature_id'), readNumberField(args, 'expected_plan_version'), args.plan_json)
461
+ );
462
+ this.toolHandlers.register(TOOLS.REPO_ENSURE_WORKTREE, async (args) =>
463
+ await this.repoEnsureWorktree(readStringField(args, 'feature_id'))
464
+ );
465
+ this.toolHandlers.register(TOOLS.REPO_APPLY_PATCH, async (args) =>
466
+ await this.repoApplyPatch(readStringField(args, 'feature_id'), readStringField(args, 'unified_diff'))
467
+ );
468
+ this.toolHandlers.register(TOOLS.REPO_STATUS, async (args) => await this.repoStatus(readStringField(args, 'feature_id')));
469
+ this.toolHandlers.register(TOOLS.REPO_DIFF, async (args) =>
470
+ await this.repoDiff(readStringField(args, 'feature_id'), asArray<string>(args.options))
471
+ );
472
+ this.toolHandlers.register(TOOLS.REPO_READ_FILE, async (args) =>
473
+ await this.repoReadFile(readStringField(args, 'feature_id'), readStringField(args, 'path'))
474
+ );
475
+ this.toolHandlers.register(TOOLS.REPO_SEARCH, async (args) =>
476
+ await this.repoSearch(readStringField(args, 'feature_id'), readStringField(args, 'query'))
477
+ );
478
+ this.toolHandlers.register(TOOLS.REPO_DIFF_BUNDLE, async (args) =>
479
+ await this.repoDiffBundle(readStringField(args, 'feature_id'))
480
+ );
481
+ this.toolHandlers.register(TOOLS.FEATURE_READY_TO_MERGE, async (args) =>
482
+ await this.featureReadyToMerge(
483
+ readStringField(args, 'feature_id'),
484
+ readStringField(args, 'commit_message'),
485
+ readStringField(args, 'merge_strategy'),
486
+ readStringField(args, 'user_approval_token')
487
+ )
488
+ );
489
+ this.toolHandlers.register(TOOLS.FEATURE_DELETE, async (args) =>
490
+ await this.featureDelete(
491
+ readStringField(args, 'feature_id'),
492
+ readBooleanField(args, 'dry_run'),
493
+ readBooleanField(args, 'confirm'),
494
+ readBooleanField(args, 'remove_worktree'),
495
+ readStringField(args, 'remove_branch')
496
+ )
497
+ );
498
+ this.toolHandlers.register(TOOLS.GATES_LIST, async (args) => await this.gatesList(readStringField(args, 'profile')));
499
+ this.toolHandlers.register(TOOLS.GATES_RUN, async (args) =>
500
+ await this.gatesRun(readStringField(args, 'feature_id'), readStringField(args, 'profile'), readStringField(args, 'mode'))
501
+ );
502
+ this.toolHandlers.register(TOOLS.EVIDENCE_LATEST, async (args) =>
503
+ await this.evidenceLatest(readStringField(args, 'feature_id'))
504
+ );
505
+ this.toolHandlers.register(TOOLS.QA_TEST_INDEX_GET, async (args) =>
506
+ await this.qaTestIndexGet(readStringField(args, 'feature_id'))
507
+ );
508
+ this.toolHandlers.register(TOOLS.QA_TEST_INDEX_UPDATE, async (args) =>
509
+ await this.qaTestIndexUpdate(
510
+ readStringField(args, 'feature_id'),
511
+ readNumberField(args, 'expected_version'),
512
+ args.updates,
513
+ asArray(args.evidence_refs)
514
+ )
515
+ );
516
+ this.toolHandlers.register(TOOLS.LOCKS_ACQUIRE, async (args) =>
517
+ await this.locksAcquire(
518
+ readStringField(args, 'resource'),
519
+ readStringField(args, 'feature_id'),
520
+ readNumberField(args, 'wait_timeout_seconds')
521
+ )
522
+ );
523
+ this.toolHandlers.register(TOOLS.LOCKS_RELEASE, async (args) =>
524
+ await this.locksRelease(readStringField(args, 'resource'), readStringField(args, 'feature_id'))
525
+ );
526
+ this.toolHandlers.register(TOOLS.COLLISIONS_SCAN, async () => await this.collisionsScan());
527
+ this.toolHandlers.register(TOOLS.REPORT_DASHBOARD, async () => await this.reportDashboard());
528
+ this.toolHandlers.register(TOOLS.REPORT_FEATURE_SUMMARY, async (args) =>
529
+ await this.reportFeatureSummary(readStringField(args, 'feature_id'))
530
+ );
531
+ }
532
+
533
+ featurePath(featureId) {
534
+ return this.pathLayout.featureRoot(featureId);
535
+ }
536
+
537
+ featureLockPath(featureId) {
538
+ return this.pathLayout.featureLockPath(featureId);
539
+ }
540
+
541
+ statePath(featureId) {
542
+ return this.pathLayout.statePath(featureId);
543
+ }
544
+
545
+ planPath(featureId) {
546
+ return this.pathLayout.planPath(featureId);
547
+ }
548
+
549
+ qaIndexPath(featureId) {
550
+ return this.pathLayout.qaIndexPath(featureId);
551
+ }
552
+
553
+ decisionsPath(featureId) {
554
+ return this.pathLayout.decisionsPath(featureId);
555
+ }
556
+
557
+ logsPath(featureId) {
558
+ return this.pathLayout.logsPath(featureId);
559
+ }
560
+
561
+ evidencePath(featureId) {
562
+ return this.pathLayout.evidencePath(featureId);
563
+ }
564
+
565
+ specPath(featureId) {
566
+ return this.pathLayout.specPath(featureId);
567
+ }
568
+
569
+ worktreePath(featureId) {
570
+ return path.join(this.repoRoot, '.worktrees', featureId);
571
+ }
572
+
573
+ async ensureFeatureLayout(featureId) {
574
+ const base = this.featurePath(featureId);
575
+ await ensureDir(base);
576
+ await ensureDir(this.logsPath(featureId));
577
+ await ensureDir(this.evidencePath(featureId));
578
+ const decisionsPath = this.decisionsPath(featureId);
579
+ if (!(await pathExists(decisionsPath))) {
580
+ await atomicWriteFile(decisionsPath, `# Decisions for ${featureId}\n\n`);
581
+ }
582
+ }
583
+
584
+ makeDefaultState(featureId, branch, worktreePath) {
585
+ return {
586
+ feature_id: featureId,
587
+ version: 1,
588
+ branch,
589
+ worktree_path: normalizeRepoPathForState(this.repoRoot, worktreePath),
590
+ status: STATUS.PLANNING,
591
+ gate_profile: 'default',
592
+ gates: {
593
+ plan: GATE_RESULT.NA,
594
+ fast: GATE_RESULT.NA,
595
+ full: GATE_RESULT.NA,
596
+ merge: GATE_RESULT.NA
597
+ },
598
+ locks: {
599
+ held: []
600
+ },
601
+ collisions: {
602
+ files: [],
603
+ areas: [],
604
+ contracts: []
605
+ },
606
+ cluster: { ...DEFAULT_CLUSTER },
607
+ role_status: { ...DEFAULT_ROLE_STATUS },
608
+ last_updated: nowIso()
609
+ };
610
+ }
611
+
612
+ async readState(featureId: string): Promise<{ frontMatter: AnyRecord; body: string; raw: string }> {
613
+ const filePath = this.statePath(featureId);
614
+ const raw = await fs.readFile(filePath, 'utf8');
615
+ const parsed = parseFrontMatter(raw);
616
+ return {
617
+ frontMatter: parsed.frontMatter,
618
+ body: parsed.body,
619
+ raw
620
+ };
621
+ }
622
+
623
+ async writeState(featureId: string, frontMatter: AnyRecord, body = '') {
624
+ const validation = await this.schemaRegistry.validate('state.schema.json', frontMatter);
625
+ if (!validation.valid) {
626
+ throw {
627
+ normalizedResponse: fail(ERROR_CODES.STATE_VALIDATION_FAILED, 'state.md front matter failed schema validation', {
628
+ errors: validation.errors,
629
+ retryable: false,
630
+ requires_human: true
631
+ })
632
+ };
633
+ }
634
+
635
+ const markdown = buildFrontMatter(frontMatter, body);
636
+ await atomicWriteFile(this.statePath(featureId), markdown);
637
+ }
638
+
639
+ async withFeatureLock<T>(featureId: string, operation: () => Promise<T>): Promise<T> {
640
+ return await withFileLock<T>(this.featureLockPath(featureId), operation, {
641
+ timeoutMs: 30_000
642
+ });
643
+ }
644
+
645
+ async withIndexLock<T>(operation: () => Promise<T>): Promise<T> {
646
+ return await withFileLock<T>(this.indexLockPath, operation, {
647
+ timeoutMs: 30_000
648
+ });
649
+ }
650
+
651
+ async readIndex(): Promise<AnyRecord> {
652
+ const existing = await readJson(this.indexPath, null);
653
+ const normalized = this.normalizeIndexShape(existing);
654
+ const changed = JSON.stringify(existing ?? null) !== JSON.stringify(normalized);
655
+ if (changed) {
656
+ await atomicWriteJson(this.indexPath, normalized);
657
+ }
658
+ return normalized;
659
+ }
660
+
661
+ async writeIndex(index) {
662
+ const normalized = this.normalizeIndexShape(index);
663
+ const validation = await this.schemaRegistry.validate('index.schema.json', normalized);
664
+ if (!validation.valid) {
665
+ throw {
666
+ normalizedResponse: fail(ERROR_CODES.INDEX_VALIDATION_FAILED, 'index.json failed schema validation', {
667
+ errors: validation.errors,
668
+ retryable: false,
669
+ requires_human: true
670
+ })
671
+ };
672
+ }
673
+
674
+ await atomicWriteJson(this.indexPath, normalized);
675
+ }
676
+
677
+ async validateSchema(schemaName: string, value: unknown): Promise<{ valid: boolean; errors?: unknown }> {
678
+ return await this.schemaRegistry.validate(schemaName, value);
679
+ }
680
+
681
+ async getRuntimeSessions(): Promise<RuntimeSessionsSnapshot> {
682
+ const index = await this.readIndex();
683
+ return this.normalizeRuntimeSessions(index.runtime_sessions);
684
+ }
685
+
686
+ async acquireRunLease(input: AcquireRunLeaseInput): Promise<{ data: { runtime_sessions: RuntimeSessionsSnapshot; took_over_stale: boolean; reused_existing_owner: boolean } }> {
687
+ return await this.runLeaseService.acquireRunLease(input);
688
+ }
689
+
690
+ async renewRunLease(runId: string, ownerInstanceId: string): Promise<{ data: { lease_expires_at: string } }> {
691
+ return await this.runLeaseService.renewRunLease(runId, ownerInstanceId);
692
+ }
693
+
694
+ async releaseRunLease(runId: string, ownerInstanceId: string): Promise<{ data: { released: boolean } }> {
695
+ return await this.runLeaseService.releaseRunLease(runId, ownerInstanceId);
696
+ }
697
+
698
+ async updateOrchestratorSession(params: {
699
+ run_id: string;
700
+ owner_instance_id: string;
701
+ orchestrator_session_id: string;
702
+ increment_epoch?: boolean;
703
+ }): Promise<{ data: { runtime_sessions: RuntimeSessionsSnapshot } }> {
704
+ return await this.runLeaseService.updateOrchestratorSession(params);
705
+ }
706
+
707
+ async updateFeatureSessionAssignment(params: {
708
+ run_id: string;
709
+ owner_instance_id: string;
710
+ feature_id: string;
711
+ planner_session_id: string;
712
+ builder_session_id: string;
713
+ qa_session_id: string;
714
+ }): Promise<{ data: { updated: boolean } }> {
715
+ return await this.runLeaseService.updateFeatureSessionAssignment(params);
716
+ }
717
+
718
+ async pruneFeatureSessionAssignments(params: {
719
+ run_id: string;
720
+ owner_instance_id: string;
721
+ active_feature_ids: string[];
722
+ }): Promise<{ data: { removed: string[] } }> {
723
+ return await this.runLeaseService.pruneFeatureSessionAssignments(params);
724
+ }
725
+
726
+ async updateState(
727
+ featureId: string,
728
+ expectedVersion: number | null,
729
+ updater: (frontMatter: AnyRecord, body: string) => Promise<{ frontMatter?: AnyRecord; body?: string }>
730
+ ) {
731
+ return await this.featureStateService.updateState(featureId, expectedVersion, updater);
732
+ }
733
+
734
+ async featureDiscoverSpecs() {
735
+ return await this.featureLifecycleService.featureDiscoverSpecs();
736
+ }
737
+
738
+ async featureInit(featureId) {
739
+ return await this.featureLifecycleService.featureInit(featureId);
740
+ }
741
+
742
+ async featureGetContext(featureId) {
743
+ return await this.featureLifecycleService.featureGetContext(featureId);
744
+ }
745
+
746
+ async featureStateGet(featureId) {
747
+ return await this.featureStateService.featureStateGet(featureId);
748
+ }
749
+
750
+ async featureStatePatch(featureId, expectedVersion, patch) {
751
+ return await this.featureStateService.featureStatePatch(featureId, expectedVersion, patch);
752
+ }
753
+
754
+ async featureLogAppend(featureId, note, context) {
755
+ return await this.featureLifecycleService.featureLogAppend(featureId, note, context);
756
+ }
757
+
758
+ async planGet(featureId) {
759
+ return await this.planService.planGet(featureId);
760
+ }
761
+
762
+ async collectAcceptedPlans(excludeFeatureId = null) {
763
+ return await this.planService.collectAcceptedPlans(excludeFeatureId);
764
+ }
765
+
766
+ async requiredResourcesForPlan(plan) {
767
+ return await this.planService.requiredResourcesForPlan(plan);
768
+ }
769
+
770
+ async assertPlanLocksHeld(featureId, plan) {
771
+ return await this.planService.assertPlanLocksHeld(featureId, plan);
772
+ }
773
+
774
+ validatePlanRevisionRules(existingPlan, plan, expectedPlanVersion = null) {
775
+ return this.planService.validatePlanRevisionRules(existingPlan, plan, expectedPlanVersion);
776
+ }
777
+
778
+ async validatePlanSchema(plan) {
779
+ return await this.planService.validatePlanSchema(plan);
780
+ }
781
+
782
+ async checkPlanCollision(featureId, plan) {
783
+ return await this.planService.checkPlanCollision(featureId, plan);
784
+ }
785
+
786
+ async planSubmit(featureId, plan, expectedVersion = null) {
787
+ return await this.planService.planSubmit(featureId, plan, expectedVersion);
788
+ }
789
+
790
+ async planUpdate(featureId, expectedPlanVersion, plan) {
791
+ return await this.planService.planUpdate(featureId, expectedPlanVersion, plan);
792
+ }
793
+
794
+ async repoEnsureWorktree(featureId) {
795
+ const worktree = this.worktreePath(featureId);
796
+ const branch = featureId;
797
+ await ensureDir(path.join(this.repoRoot, '.worktrees'));
798
+
799
+ if (await pathExists(worktree)) {
800
+ return {
801
+ data: {
802
+ feature_id: featureId,
803
+ branch,
804
+ worktree_path_abs: worktree,
805
+ existed: true
806
+ }
807
+ };
808
+ }
809
+
810
+ const baseBranch = this.policy.worktree.base_branch;
811
+ const baseCheck = await runGit(this.repoRoot, ['rev-parse', '--verify', baseBranch]);
812
+ const baseRef = baseCheck.code === 0 ? baseBranch : 'HEAD';
813
+
814
+ const branchCheck = await runGit(this.repoRoot, ['rev-parse', '--verify', branch]);
815
+ if (branchCheck.code !== 0) {
816
+ const branchCreate = await runGit(this.repoRoot, ['branch', branch, baseRef]);
817
+ if (branchCreate.code !== 0) {
818
+ throw {
819
+ normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Unable to create feature branch', {
820
+ feature_id: featureId,
821
+ stderr: branchCreate.stderr,
822
+ retryable: false,
823
+ requires_human: true
824
+ }, {
825
+ command: ['git', 'branch', branch, baseRef],
826
+ exit_code: branchCreate.code
827
+ })
828
+ };
829
+ }
830
+ }
831
+
832
+ const addWorktree = await runGit(this.repoRoot, ['worktree', 'add', worktree, branch]);
833
+ if (addWorktree.code !== 0) {
834
+ throw {
835
+ normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Unable to create git worktree', {
836
+ feature_id: featureId,
837
+ stderr: addWorktree.stderr,
838
+ retryable: false,
839
+ requires_human: true
840
+ }, {
841
+ command: ['git', 'worktree', 'add', worktree, branch],
842
+ exit_code: addWorktree.code
843
+ })
844
+ };
845
+ }
846
+
847
+ return {
848
+ data: {
849
+ feature_id: featureId,
850
+ branch,
851
+ worktree_path_abs: worktree,
852
+ existed: false
853
+ }
854
+ };
855
+ }
856
+
857
+ async loadAcceptedPlan(featureId) {
858
+ return await this.patchService.loadAcceptedPlan(featureId);
859
+ }
860
+
861
+ async validatePatchPaths(featureId, parsedDiff, plan) {
862
+ return await this.patchService.validatePatchPaths(featureId, parsedDiff, plan);
863
+ }
864
+
865
+ async repoApplyPatch(featureId, unifiedDiff) {
866
+ return await this.patchService.repoApplyPatch(featureId, unifiedDiff);
867
+ }
868
+
869
+ async repoStatus(featureId) {
870
+ const worktree = this.worktreePath(featureId);
871
+ const status = await runGit(this.repoRoot, ['status', '--porcelain'], { cwd: worktree });
872
+ const branch = await runGit(this.repoRoot, ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: worktree });
873
+
874
+ return {
875
+ data: {
876
+ feature_id: featureId,
877
+ branch: branch.stdout.trim(),
878
+ status_porcelain: status.stdout.trim().split('\n').filter(Boolean)
879
+ }
880
+ };
881
+ }
882
+
883
+ async repoDiff(featureId, options = []) {
884
+ const safeOptions = asArray<string>(options).filter(
885
+ (option) => typeof option === 'string' && option.startsWith('--')
886
+ );
887
+ const diff = await runGit(this.repoRoot, ['diff', ...safeOptions], { cwd: this.worktreePath(featureId) });
888
+ return {
889
+ data: {
890
+ feature_id: featureId,
891
+ diff: diff.stdout
892
+ }
893
+ };
894
+ }
895
+
896
+ async repoReadFile(featureId, filePath) {
897
+ const normalized = await normalizeRepoPath(this.repoRoot, path.join(this.worktreePath(featureId), filePath), this.policy.path_rules.allow_symlink_traversal)
898
+ .then((relative) => normalizeFromWorktree(this.worktreePath(featureId), this.repoRoot, relative));
899
+ const absolute = path.join(this.repoRoot, normalized);
900
+ const exists = await pathExists(absolute);
901
+ if (!exists) {
902
+ throw {
903
+ normalizedResponse: fail(ERROR_CODES.FILE_NOT_FOUND, 'File not found', {
904
+ path: normalized,
905
+ retryable: false,
906
+ requires_human: false
907
+ })
908
+ };
909
+ }
910
+ const content = await fs.readFile(absolute, 'utf8');
911
+ return {
912
+ data: {
913
+ feature_id: featureId,
914
+ path: normalized,
915
+ content
916
+ }
917
+ };
918
+ }
919
+
920
+ async repoSearch(featureId, query) {
921
+ const worktree = this.worktreePath(featureId);
922
+ const rgResult = await runCommand('rg', ['-n', '--no-heading', query, '.'], {
923
+ cwd: worktree,
924
+ timeoutMs: 30_000
925
+ });
926
+
927
+ if (rgResult.code === 127) {
928
+ throw {
929
+ normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'ripgrep (rg) not found - required for search functionality', {
930
+ stderr: rgResult.stderr,
931
+ retryable: false,
932
+ requires_human: true
933
+ })
934
+ };
935
+ }
936
+
937
+ if (rgResult.code !== 0 && rgResult.code !== 1) {
938
+ throw {
939
+ normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Search failed', {
940
+ stderr: rgResult.stderr,
941
+ retryable: true,
942
+ requires_human: false
943
+ })
944
+ };
945
+ }
946
+
947
+ const matches = rgResult.stdout
948
+ .trim()
949
+ .split('\n')
950
+ .filter(Boolean)
951
+ .map((line) => {
952
+ const firstColon = line.indexOf(':');
953
+ const secondColon = line.indexOf(':', firstColon + 1);
954
+ if (firstColon === -1 || secondColon === -1) {
955
+ return { raw: line };
956
+ }
957
+ return {
958
+ path: line.slice(0, firstColon),
959
+ line: Number(line.slice(firstColon + 1, secondColon)),
960
+ snippet: line.slice(secondColon + 1)
961
+ };
962
+ });
963
+
964
+ return {
965
+ data: {
966
+ feature_id: featureId,
967
+ query,
968
+ matches
969
+ }
970
+ };
971
+ }
972
+
973
+ async repoDiffBundle(featureId) {
974
+ const stat = await runGit(this.repoRoot, ['diff', '--stat'], { cwd: this.worktreePath(featureId) });
975
+ const full = await runGit(this.repoRoot, ['diff'], { cwd: this.worktreePath(featureId) });
976
+ const names = await runGit(this.repoRoot, ['diff', '--name-only'], { cwd: this.worktreePath(featureId) });
977
+ const latest = await this.evidenceLatest(featureId);
978
+
979
+ return {
980
+ data: {
981
+ feature_id: featureId,
982
+ diff_stat: stat.stdout,
983
+ diff: full.stdout,
984
+ touched_files: names.stdout.split('\n').map((x) => x.trim()).filter(Boolean),
985
+ last_gate_summary: latest.data?.latest ?? null
986
+ }
987
+ };
988
+ }
989
+
990
+ async gatesList(profileName = null) {
991
+ return await this.gateService.gatesList(profileName);
992
+ }
993
+
994
+ async gateProfileAndMode(profileName, mode) {
995
+ return await this.gateService.gateProfileAndMode(profileName, mode);
996
+ }
997
+
998
+ async gatesRun(featureId, profileName, mode) {
999
+ return await this.gateService.gatesRun(featureId, profileName, mode);
1000
+ }
1001
+
1002
+ async evidenceLatest(featureId) {
1003
+ return await this.gateService.evidenceLatest(featureId);
1004
+ }
1005
+
1006
+ async qaTestIndexGet(featureId) {
1007
+ return await this.qaIndexService.qaTestIndexGet(featureId);
1008
+ }
1009
+
1010
+ async qaTestIndexUpdate(featureId, expectedVersion, updates, evidenceRefs) {
1011
+ return await this.qaIndexService.qaTestIndexUpdate(featureId, expectedVersion, updates, evidenceRefs);
1012
+ }
1013
+
1014
+ async locksAcquire(resource, featureId, waitTimeoutSeconds = null) {
1015
+ return await this.lockService.locksAcquire(resource, featureId, waitTimeoutSeconds);
1016
+ }
1017
+
1018
+ async locksRelease(resource, featureId) {
1019
+ return await this.lockService.locksRelease(resource, featureId);
1020
+ }
1021
+
1022
+ async collisionsScan() {
1023
+ return await this.reportingService.collisionsScan();
1024
+ }
1025
+
1026
+ async reportDashboard() {
1027
+ return await this.reportingService.reportDashboard();
1028
+ }
1029
+
1030
+ async reportFeatureSummary(featureId) {
1031
+ return await this.reportingService.reportFeatureSummary(featureId);
1032
+ }
1033
+
1034
+ async featureReadyToMerge(featureId, commitMessage, mergeStrategy, userApprovalToken) {
1035
+ return await this.mergeService.featureReadyToMerge(featureId, commitMessage, mergeStrategy, userApprovalToken);
1036
+ }
1037
+
1038
+ async featureDelete(
1039
+ featureId: string | null,
1040
+ dryRun: boolean | null,
1041
+ confirm: boolean | null,
1042
+ removeWorktree: boolean | null,
1043
+ removeBranch: string | null
1044
+ ): Promise<FeatureDeleteResult> {
1045
+ return await this.featureDeletionService.featureDelete(featureId, {
1046
+ dryRun: dryRun ?? undefined,
1047
+ confirm: confirm ?? undefined,
1048
+ removeWorktree: removeWorktree ?? undefined,
1049
+ removeBranch: removeBranch ?? undefined
1050
+ });
1051
+ }
1052
+
1053
+ async renewLeases(featureIds) {
1054
+ return await this.lockService.renewLeases(featureIds);
1055
+ }
1056
+
1057
+ async recoverFromState() {
1058
+ return await this.lockService.recoverFromState();
1059
+ }
1060
+ }
1061
+
1062
+ function normalizeRepoPathForState(repoRoot: string, absolutePath: string) {
1063
+ const relative = path.relative(repoRoot, absolutePath).replaceAll('\\\\', '/');
1064
+ if (!relative || relative === '.') {
1065
+ return '.';
1066
+ }
1067
+ return relative;
1068
+ }
1069
+
1070
+ function normalizeFromWorktree(worktreePath: string, repoRoot: string, repoRelativeFromWorktree: string) {
1071
+ const absolute = path.resolve(repoRoot, repoRelativeFromWorktree);
1072
+ const maybeRelativeToWorktree = path.relative(worktreePath, absolute).replaceAll('\\\\', '/');
1073
+ if (!maybeRelativeToWorktree.startsWith('../')) {
1074
+ return maybeRelativeToWorktree;
1075
+ }
1076
+ return path.relative(repoRoot, absolute).replaceAll('\\\\', '/');
1077
+ }