smol-symphony 0.2.0 → 0.3.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 (716) hide show
  1. package/AGENTS.md +41 -22
  2. package/DESIGN.md +494 -273
  3. package/README.md +109 -57
  4. package/SPEC.md +33 -24
  5. package/WORKFLOW.minimal.yaml +34 -0
  6. package/{WORKFLOW.template.md → WORKFLOW.template.yaml} +409 -256
  7. package/WORKFLOW.yaml +487 -0
  8. package/assets/skills/symphony-issues/SKILL.md +136 -0
  9. package/assets/symphony-mise.system.toml +68 -0
  10. package/dist/src/bin/symphony.js +30 -0
  11. package/dist/src/bin/symphony.js.map +1 -0
  12. package/dist/src/core/actions/context.js +109 -0
  13. package/dist/src/core/actions/context.js.map +1 -0
  14. package/dist/{actions/parsing.js → src/core/actions/parse.js} +33 -114
  15. package/dist/src/core/actions/parse.js.map +1 -0
  16. package/dist/src/core/actions/plan.js +197 -0
  17. package/dist/src/core/actions/plan.js.map +1 -0
  18. package/dist/src/core/actions/predicates.js +111 -0
  19. package/dist/src/core/actions/predicates.js.map +1 -0
  20. package/dist/src/core/actions/run-fold.js +248 -0
  21. package/dist/src/core/actions/run-fold.js.map +1 -0
  22. package/dist/src/core/actions/template.js +118 -0
  23. package/dist/src/core/actions/template.js.map +1 -0
  24. package/dist/src/core/cli/args.js +116 -0
  25. package/dist/src/core/cli/args.js.map +1 -0
  26. package/dist/src/core/coerce.js +75 -0
  27. package/dist/src/core/coerce.js.map +1 -0
  28. package/dist/src/core/credential/account-id.js +20 -0
  29. package/dist/src/core/credential/account-id.js.map +1 -0
  30. package/dist/src/core/credential/adapter-config.js +136 -0
  31. package/dist/src/core/credential/adapter-config.js.map +1 -0
  32. package/dist/src/core/credential/availability.js +98 -0
  33. package/dist/src/core/credential/availability.js.map +1 -0
  34. package/dist/src/core/credential/extract.js +228 -0
  35. package/dist/src/core/credential/extract.js.map +1 -0
  36. package/dist/src/core/credential/fake-creds.js +171 -0
  37. package/dist/src/core/credential/fake-creds.js.map +1 -0
  38. package/dist/src/core/credential/identity.js +125 -0
  39. package/dist/src/core/credential/identity.js.map +1 -0
  40. package/dist/src/core/credential/shape.js +230 -0
  41. package/dist/src/core/credential/shape.js.map +1 -0
  42. package/dist/src/core/credential/strings.js +15 -0
  43. package/dist/src/core/credential/strings.js.map +1 -0
  44. package/dist/src/core/doctor/checks.js +303 -0
  45. package/dist/src/core/doctor/checks.js.map +1 -0
  46. package/dist/src/core/git/result.js +107 -0
  47. package/dist/src/core/git/result.js.map +1 -0
  48. package/dist/src/core/http/decisions.js +225 -0
  49. package/dist/src/core/http/decisions.js.map +1 -0
  50. package/dist/{http.js → src/core/http/render.js} +472 -738
  51. package/dist/src/core/http/render.js.map +1 -0
  52. package/dist/{http-handlers.js → src/core/http/routes.js} +52 -87
  53. package/dist/src/core/http/routes.js.map +1 -0
  54. package/dist/src/core/http/views.js +181 -0
  55. package/dist/src/core/http/views.js.map +1 -0
  56. package/dist/src/core/image/managed-image.js +95 -0
  57. package/dist/src/core/image/managed-image.js.map +1 -0
  58. package/dist/src/core/issue/file.js +149 -0
  59. package/dist/src/core/issue/file.js.map +1 -0
  60. package/dist/src/core/issue/parse.js +210 -0
  61. package/dist/src/core/issue/parse.js.map +1 -0
  62. package/dist/src/core/mcp/dispatch.js +239 -0
  63. package/dist/src/core/mcp/dispatch.js.map +1 -0
  64. package/dist/src/core/mcp/post-move.js +92 -0
  65. package/dist/src/core/mcp/post-move.js.map +1 -0
  66. package/dist/src/core/mcp/protocol.js +293 -0
  67. package/dist/src/core/mcp/protocol.js.map +1 -0
  68. package/dist/src/core/mcp/url.js +162 -0
  69. package/dist/src/core/mcp/url.js.map +1 -0
  70. package/dist/src/core/path.js +63 -0
  71. package/dist/src/core/path.js.map +1 -0
  72. package/dist/src/core/reconcile/image-decide.js +48 -0
  73. package/dist/src/core/reconcile/image-decide.js.map +1 -0
  74. package/dist/src/core/reconcile/ledger.js +142 -0
  75. package/dist/src/core/reconcile/ledger.js.map +1 -0
  76. package/dist/src/core/reconcile/pr-classify.js +62 -0
  77. package/dist/src/core/reconcile/pr-classify.js.map +1 -0
  78. package/dist/{reconciler → src/core/reconcile}/pr-decide.js +25 -12
  79. package/dist/src/core/reconcile/pr-decide.js.map +1 -0
  80. package/dist/src/core/reconcile/pr-loop.js +161 -0
  81. package/dist/src/core/reconcile/pr-loop.js.map +1 -0
  82. package/dist/src/core/reconcile/pr-notes.js +35 -0
  83. package/dist/src/core/reconcile/pr-notes.js.map +1 -0
  84. package/dist/src/core/reconcile/vm-decide.js +70 -0
  85. package/dist/src/core/reconcile/vm-decide.js.map +1 -0
  86. package/dist/src/core/reconcile/vm-reap.js +207 -0
  87. package/dist/src/core/reconcile/vm-reap.js.map +1 -0
  88. package/dist/src/core/reconcile/workspace-decide.js +162 -0
  89. package/dist/src/core/reconcile/workspace-decide.js.map +1 -0
  90. package/dist/src/core/runlog/summary.js +231 -0
  91. package/dist/src/core/runlog/summary.js.map +1 -0
  92. package/dist/src/core/runner/dispatch-config.js +95 -0
  93. package/dist/src/core/runner/dispatch-config.js.map +1 -0
  94. package/dist/src/core/runner/injection.js +61 -0
  95. package/dist/src/core/runner/injection.js.map +1 -0
  96. package/dist/src/core/runner/mise.js +210 -0
  97. package/dist/src/core/runner/mise.js.map +1 -0
  98. package/dist/src/core/runner/prompt.js +720 -0
  99. package/dist/src/core/runner/prompt.js.map +1 -0
  100. package/dist/src/core/runner/turn.js +242 -0
  101. package/dist/src/core/runner/turn.js.map +1 -0
  102. package/dist/src/core/runner/vm-plan.js +390 -0
  103. package/dist/src/core/runner/vm-plan.js.map +1 -0
  104. package/dist/src/core/schedule/admission.js +123 -0
  105. package/dist/src/core/schedule/admission.js.map +1 -0
  106. package/dist/src/core/schedule/circuit-breaker.js +111 -0
  107. package/dist/src/core/schedule/circuit-breaker.js.map +1 -0
  108. package/dist/src/core/schedule/eligibility.js +83 -0
  109. package/dist/src/core/schedule/eligibility.js.map +1 -0
  110. package/dist/src/core/schedule/reconcile-issue.js +82 -0
  111. package/dist/src/core/schedule/reconcile-issue.js.map +1 -0
  112. package/dist/src/core/schedule/retry.js +96 -0
  113. package/dist/src/core/schedule/retry.js.map +1 -0
  114. package/dist/src/core/schedule/sleep-cycle.js +133 -0
  115. package/dist/src/core/schedule/sleep-cycle.js.map +1 -0
  116. package/dist/src/core/schedule/slots.js +124 -0
  117. package/dist/src/core/schedule/slots.js.map +1 -0
  118. package/dist/src/core/schedule/tick.js +553 -0
  119. package/dist/src/core/schedule/tick.js.map +1 -0
  120. package/dist/src/core/schedule/token-fold.js +181 -0
  121. package/dist/src/core/schedule/token-fold.js.map +1 -0
  122. package/dist/src/core/state-resolve.js +86 -0
  123. package/dist/src/core/state-resolve.js.map +1 -0
  124. package/dist/src/core/vm-guards.js +278 -0
  125. package/dist/src/core/vm-guards.js.map +1 -0
  126. package/dist/src/core/workflow/derive.js +107 -0
  127. package/dist/src/core/workflow/derive.js.map +1 -0
  128. package/dist/src/core/workflow/parse.js +687 -0
  129. package/dist/src/core/workflow/parse.js.map +1 -0
  130. package/dist/src/core/workflow/prompt-probe.js +78 -0
  131. package/dist/src/core/workflow/prompt-probe.js.map +1 -0
  132. package/dist/src/core/workflow/validate.js +189 -0
  133. package/dist/src/core/workflow/validate.js.map +1 -0
  134. package/dist/src/core/workspace-key.js +19 -0
  135. package/dist/src/core/workspace-key.js.map +1 -0
  136. package/dist/src/shell/actions-runner.js +356 -0
  137. package/dist/src/shell/actions-runner.js.map +1 -0
  138. package/dist/src/shell/adapter/adapter-registry.js +45 -0
  139. package/dist/src/shell/adapter/adapter-registry.js.map +1 -0
  140. package/dist/src/shell/adapter/clock-random.js +96 -0
  141. package/dist/src/shell/adapter/clock-random.js.map +1 -0
  142. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  143. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  144. package/dist/src/shell/adapter/gondolin-dispatch.js +385 -0
  145. package/dist/src/shell/adapter/gondolin-dispatch.js.map +1 -0
  146. package/dist/src/shell/adapter/gondolin-image-converter.js +233 -0
  147. package/dist/src/shell/adapter/gondolin-image-converter.js.map +1 -0
  148. package/dist/src/shell/adapter/gondolin-image-fetch.js +180 -0
  149. package/dist/src/shell/adapter/gondolin-image-fetch.js.map +1 -0
  150. package/dist/src/shell/adapter/launcher-asset.js +57 -0
  151. package/dist/src/shell/adapter/launcher-asset.js.map +1 -0
  152. package/dist/src/shell/adapter/mise-config-asset.js +65 -0
  153. package/dist/src/shell/adapter/mise-config-asset.js.map +1 -0
  154. package/dist/src/shell/adapter/workflow-loader.js +304 -0
  155. package/dist/src/shell/adapter/workflow-loader.js.map +1 -0
  156. package/dist/src/shell/cli/doctor.js +268 -0
  157. package/dist/src/shell/cli/doctor.js.map +1 -0
  158. package/dist/src/shell/effect-interpreter-families.js +314 -0
  159. package/dist/src/shell/effect-interpreter-families.js.map +1 -0
  160. package/dist/src/shell/effect-interpreter.js +29 -0
  161. package/dist/src/shell/effect-interpreter.js.map +1 -0
  162. package/dist/src/shell/interp/acp-frame.js +137 -0
  163. package/dist/src/shell/interp/acp-frame.js.map +1 -0
  164. package/dist/src/shell/interp/acp-ws-conn.js +320 -0
  165. package/dist/src/shell/interp/acp-ws-conn.js.map +1 -0
  166. package/dist/src/shell/interp/acp-ws-frames.js +159 -0
  167. package/dist/src/shell/interp/acp-ws-frames.js.map +1 -0
  168. package/dist/src/shell/interp/acp-ws.js +197 -0
  169. package/dist/src/shell/interp/acp-ws.js.map +1 -0
  170. package/dist/src/shell/interp/acp.js +319 -0
  171. package/dist/src/shell/interp/acp.js.map +1 -0
  172. package/dist/src/shell/interp/credential-defaults.js +128 -0
  173. package/dist/src/shell/interp/credential-defaults.js.map +1 -0
  174. package/dist/src/shell/interp/credential-hooks.js +149 -0
  175. package/dist/src/shell/interp/credential-hooks.js.map +1 -0
  176. package/dist/src/shell/interp/credential-registry.js +226 -0
  177. package/dist/src/shell/interp/credential-registry.js.map +1 -0
  178. package/dist/src/shell/interp/credential.js +103 -0
  179. package/dist/src/shell/interp/credential.js.map +1 -0
  180. package/dist/src/shell/interp/gh.js +163 -0
  181. package/dist/src/shell/interp/gh.js.map +1 -0
  182. package/dist/src/shell/interp/git.js +28 -0
  183. package/dist/src/shell/interp/git.js.map +1 -0
  184. package/dist/src/shell/interp/log.js +213 -0
  185. package/dist/src/shell/interp/log.js.map +1 -0
  186. package/dist/src/shell/interp/process.js +178 -0
  187. package/dist/src/shell/interp/process.js.map +1 -0
  188. package/dist/src/shell/interp/runlog.js +193 -0
  189. package/dist/src/shell/interp/runlog.js.map +1 -0
  190. package/dist/src/shell/interp/timer.js +64 -0
  191. package/dist/src/shell/interp/timer.js.map +1 -0
  192. package/dist/src/shell/interp/tracker-disk.js +99 -0
  193. package/dist/src/shell/interp/tracker-disk.js.map +1 -0
  194. package/dist/src/shell/interp/tracker-parse.js +71 -0
  195. package/dist/src/shell/interp/tracker-parse.js.map +1 -0
  196. package/dist/src/shell/interp/tracker-scan.js +238 -0
  197. package/dist/src/shell/interp/tracker-scan.js.map +1 -0
  198. package/dist/src/shell/interp/tracker-write.js +91 -0
  199. package/dist/src/shell/interp/tracker-write.js.map +1 -0
  200. package/dist/src/shell/interp/tracker.js +41 -0
  201. package/dist/src/shell/interp/tracker.js.map +1 -0
  202. package/dist/src/shell/interp/tty.js +48 -0
  203. package/dist/src/shell/interp/tty.js.map +1 -0
  204. package/dist/src/shell/interp/vm.js +199 -0
  205. package/dist/src/shell/interp/vm.js.map +1 -0
  206. package/dist/src/shell/interp/workspace.js +310 -0
  207. package/dist/src/shell/interp/workspace.js.map +1 -0
  208. package/dist/src/shell/main-acp.js +78 -0
  209. package/dist/src/shell/main-acp.js.map +1 -0
  210. package/dist/src/shell/main-adapters.js +222 -0
  211. package/dist/src/shell/main-adapters.js.map +1 -0
  212. package/dist/src/shell/main-credential.js +122 -0
  213. package/dist/src/shell/main-credential.js.map +1 -0
  214. package/dist/src/shell/main-doctor.js +22 -0
  215. package/dist/src/shell/main-doctor.js.map +1 -0
  216. package/dist/src/shell/main-entry.js +46 -0
  217. package/dist/src/shell/main-entry.js.map +1 -0
  218. package/dist/src/shell/main-http-csrf.js +45 -0
  219. package/dist/src/shell/main-http-csrf.js.map +1 -0
  220. package/dist/src/shell/main-http-handler.js +389 -0
  221. package/dist/src/shell/main-http-handler.js.map +1 -0
  222. package/dist/src/shell/main-http-mcp.js +122 -0
  223. package/dist/src/shell/main-http-mcp.js.map +1 -0
  224. package/dist/src/shell/main-http-views.js +253 -0
  225. package/dist/src/shell/main-http-views.js.map +1 -0
  226. package/dist/src/shell/main-http.js +76 -0
  227. package/dist/src/shell/main-http.js.map +1 -0
  228. package/dist/src/shell/main-loops.js +130 -0
  229. package/dist/src/shell/main-loops.js.map +1 -0
  230. package/dist/src/shell/main-mcp.js +129 -0
  231. package/dist/src/shell/main-mcp.js.map +1 -0
  232. package/dist/src/shell/main-orchestrator.js +120 -0
  233. package/dist/src/shell/main-orchestrator.js.map +1 -0
  234. package/dist/src/shell/main-preflight.js +43 -0
  235. package/dist/src/shell/main-preflight.js.map +1 -0
  236. package/dist/src/shell/main-reconcilers-helpers.js +244 -0
  237. package/dist/src/shell/main-reconcilers-helpers.js.map +1 -0
  238. package/dist/src/shell/main-reconcilers-pr.js +148 -0
  239. package/dist/src/shell/main-reconcilers-pr.js.map +1 -0
  240. package/dist/src/shell/main-reconcilers.js +225 -0
  241. package/dist/src/shell/main-reconcilers.js.map +1 -0
  242. package/dist/src/shell/main-runner.js +355 -0
  243. package/dist/src/shell/main-runner.js.map +1 -0
  244. package/dist/src/shell/main-scaffold.js +116 -0
  245. package/dist/src/shell/main-scaffold.js.map +1 -0
  246. package/dist/src/shell/main-shutdown.js +115 -0
  247. package/dist/src/shell/main-shutdown.js.map +1 -0
  248. package/dist/src/shell/main-startup.js +48 -0
  249. package/dist/src/shell/main-startup.js.map +1 -0
  250. package/dist/src/shell/main-substrates.js +43 -0
  251. package/dist/src/shell/main-substrates.js.map +1 -0
  252. package/dist/src/shell/main.js +385 -0
  253. package/dist/src/shell/main.js.map +1 -0
  254. package/dist/src/shell/orchestrator-feedback.js +69 -0
  255. package/dist/src/shell/orchestrator-feedback.js.map +1 -0
  256. package/dist/src/shell/orchestrator-image.js +167 -0
  257. package/dist/src/shell/orchestrator-image.js.map +1 -0
  258. package/dist/src/shell/orchestrator-loop.js +468 -0
  259. package/dist/src/shell/orchestrator-loop.js.map +1 -0
  260. package/dist/src/shell/orchestrator-reconcile.js +36 -0
  261. package/dist/src/shell/orchestrator-reconcile.js.map +1 -0
  262. package/dist/src/shell/reconciler-loop.js +228 -0
  263. package/dist/src/shell/reconciler-loop.js.map +1 -0
  264. package/dist/src/shell/runner-loop-turn.js +301 -0
  265. package/dist/src/shell/runner-loop-turn.js.map +1 -0
  266. package/dist/src/shell/runner-loop.js +338 -0
  267. package/dist/src/shell/runner-loop.js.map +1 -0
  268. package/dist/src/shell/server/http.js +208 -0
  269. package/dist/src/shell/server/http.js.map +1 -0
  270. package/dist/src/shell/server/mcp-runtime-effects.js +237 -0
  271. package/dist/src/shell/server/mcp-runtime-effects.js.map +1 -0
  272. package/dist/src/shell/server/mcp-runtime.js +99 -0
  273. package/dist/src/shell/server/mcp-runtime.js.map +1 -0
  274. package/dist/src/shell/workspace-key.js +14 -0
  275. package/dist/src/shell/workspace-key.js.map +1 -0
  276. package/dist/src/types/acp.js +8 -0
  277. package/dist/src/types/acp.js.map +1 -0
  278. package/dist/src/types/actions/plan.js +6 -0
  279. package/dist/src/types/actions/plan.js.map +1 -0
  280. package/dist/src/types/actions/predicates.js +6 -0
  281. package/dist/src/types/actions/predicates.js.map +1 -0
  282. package/dist/src/types/actions/run-fold.js +8 -0
  283. package/dist/src/types/actions/run-fold.js.map +1 -0
  284. package/dist/src/types/actions.js +7 -0
  285. package/dist/src/types/actions.js.map +1 -0
  286. package/dist/src/types/adapter/clock-random.js +4 -0
  287. package/dist/src/types/adapter/clock-random.js.map +1 -0
  288. package/dist/src/types/adapter/gondolin-image-converter.js +5 -0
  289. package/dist/src/types/adapter/gondolin-image-converter.js.map +1 -0
  290. package/dist/src/types/adapter/gondolin-image-fetch.js +5 -0
  291. package/dist/src/types/adapter/gondolin-image-fetch.js.map +1 -0
  292. package/dist/src/types/adapter/workflow-loader.js +4 -0
  293. package/dist/src/types/adapter/workflow-loader.js.map +1 -0
  294. package/dist/src/types/cli/args.js +8 -0
  295. package/dist/src/types/cli/args.js.map +1 -0
  296. package/dist/src/types/config.js +8 -0
  297. package/dist/src/types/config.js.map +1 -0
  298. package/dist/src/types/credential-interp.js +6 -0
  299. package/dist/src/types/credential-interp.js.map +1 -0
  300. package/dist/src/types/credentials.js +10 -0
  301. package/dist/src/types/credentials.js.map +1 -0
  302. package/dist/src/types/doctor.js +7 -0
  303. package/dist/src/types/doctor.js.map +1 -0
  304. package/dist/src/types/domain.js +7 -0
  305. package/dist/src/types/domain.js.map +1 -0
  306. package/dist/src/types/effect.js +15 -0
  307. package/dist/src/types/effect.js.map +1 -0
  308. package/dist/src/types/errors.js +39 -0
  309. package/dist/src/types/errors.js.map +1 -0
  310. package/dist/src/types/http/decisions.js +6 -0
  311. package/dist/src/types/http/decisions.js.map +1 -0
  312. package/dist/src/types/http/render.js +10 -0
  313. package/dist/src/types/http/render.js.map +1 -0
  314. package/dist/src/types/http/views.js +6 -0
  315. package/dist/src/types/http/views.js.map +1 -0
  316. package/dist/src/types/http.js +9 -0
  317. package/dist/src/types/http.js.map +1 -0
  318. package/dist/src/types/image/managed-image.js +7 -0
  319. package/dist/src/types/image/managed-image.js.map +1 -0
  320. package/dist/src/types/interp/effect-interpreter.js +8 -0
  321. package/dist/src/types/interp/effect-interpreter.js.map +1 -0
  322. package/dist/src/types/interp/tracker.js +7 -0
  323. package/dist/src/types/interp/tracker.js.map +1 -0
  324. package/dist/src/types/issue/file.js +6 -0
  325. package/dist/src/types/issue/file.js.map +1 -0
  326. package/dist/src/types/issue/parse.js +8 -0
  327. package/dist/src/types/issue/parse.js.map +1 -0
  328. package/dist/src/types/main-acp.js +13 -0
  329. package/dist/src/types/main-acp.js.map +1 -0
  330. package/dist/src/types/main-adapters.js +5 -0
  331. package/dist/src/types/main-adapters.js.map +1 -0
  332. package/dist/src/types/main-credential.js +21 -0
  333. package/dist/src/types/main-credential.js.map +1 -0
  334. package/dist/src/types/main-doctor.js +6 -0
  335. package/dist/src/types/main-doctor.js.map +1 -0
  336. package/dist/src/types/main-http-handler.js +12 -0
  337. package/dist/src/types/main-http-handler.js.map +1 -0
  338. package/dist/src/types/main-http.js +5 -0
  339. package/dist/src/types/main-http.js.map +1 -0
  340. package/dist/src/types/main-loops.js +5 -0
  341. package/dist/src/types/main-loops.js.map +1 -0
  342. package/dist/src/types/main-mcp.js +12 -0
  343. package/dist/src/types/main-mcp.js.map +1 -0
  344. package/dist/src/types/main-orchestrator.js +5 -0
  345. package/dist/src/types/main-orchestrator.js.map +1 -0
  346. package/dist/src/types/main-reconcilers.js +11 -0
  347. package/dist/src/types/main-reconcilers.js.map +1 -0
  348. package/dist/src/types/main-runner.js +13 -0
  349. package/dist/src/types/main-runner.js.map +1 -0
  350. package/dist/src/types/main-startup.js +5 -0
  351. package/dist/src/types/main-startup.js.map +1 -0
  352. package/dist/src/types/main-substrates.js +5 -0
  353. package/dist/src/types/main-substrates.js.map +1 -0
  354. package/dist/src/types/mcp/dispatch.js +4 -0
  355. package/dist/src/types/mcp/dispatch.js.map +1 -0
  356. package/dist/src/types/mcp/post-move.js +7 -0
  357. package/dist/src/types/mcp/post-move.js.map +1 -0
  358. package/dist/src/types/mcp.js +9 -0
  359. package/dist/src/types/mcp.js.map +1 -0
  360. package/dist/src/types/ports.js +12 -0
  361. package/dist/src/types/ports.js.map +1 -0
  362. package/dist/src/types/reconcile/image-decide.js +5 -0
  363. package/dist/src/types/reconcile/image-decide.js.map +1 -0
  364. package/dist/src/types/reconcile/ledger.js +7 -0
  365. package/dist/src/types/reconcile/ledger.js.map +1 -0
  366. package/dist/src/types/reconcile/pr-loop.js +8 -0
  367. package/dist/src/types/reconcile/pr-loop.js.map +1 -0
  368. package/dist/src/types/reconcile/vm-reap.js +8 -0
  369. package/dist/src/types/reconcile/vm-reap.js.map +1 -0
  370. package/dist/src/types/reconcile/workspace-decide.js +7 -0
  371. package/dist/src/types/reconcile/workspace-decide.js.map +1 -0
  372. package/dist/src/types/reconcile.js +9 -0
  373. package/dist/src/types/reconcile.js.map +1 -0
  374. package/dist/src/types/runlog.js +7 -0
  375. package/dist/src/types/runlog.js.map +1 -0
  376. package/dist/src/types/runner/actions-runner.js +12 -0
  377. package/dist/src/types/runner/actions-runner.js.map +1 -0
  378. package/dist/src/types/runner/gondolin-dispatch.js +5 -0
  379. package/dist/src/types/runner/gondolin-dispatch.js.map +1 -0
  380. package/dist/src/types/runner/injection.js +6 -0
  381. package/dist/src/types/runner/injection.js.map +1 -0
  382. package/dist/src/types/runner/runner-loop.js +5 -0
  383. package/dist/src/types/runner/runner-loop.js.map +1 -0
  384. package/dist/src/types/runner/turn.js +4 -0
  385. package/dist/src/types/runner/turn.js.map +1 -0
  386. package/dist/src/types/runner/vm-plan.js +4 -0
  387. package/dist/src/types/runner/vm-plan.js.map +1 -0
  388. package/dist/src/types/runtime.js +9 -0
  389. package/dist/src/types/runtime.js.map +1 -0
  390. package/dist/src/types/schedule/admission.js +7 -0
  391. package/dist/src/types/schedule/admission.js.map +1 -0
  392. package/dist/src/types/schedule/circuit-breaker.js +2 -0
  393. package/dist/src/types/schedule/circuit-breaker.js.map +1 -0
  394. package/dist/src/types/schedule/eligibility.js +9 -0
  395. package/dist/src/types/schedule/eligibility.js.map +1 -0
  396. package/dist/src/types/schedule/orchestrator-loop.js +10 -0
  397. package/dist/src/types/schedule/orchestrator-loop.js.map +1 -0
  398. package/dist/src/types/schedule/sleep-cycle.js +4 -0
  399. package/dist/src/types/schedule/sleep-cycle.js.map +1 -0
  400. package/dist/src/types/schedule/slots.js +8 -0
  401. package/dist/src/types/schedule/slots.js.map +1 -0
  402. package/dist/src/types/schedule/tick.js +9 -0
  403. package/dist/src/types/schedule/tick.js.map +1 -0
  404. package/dist/src/types/server/mcp-runtime.js +8 -0
  405. package/dist/src/types/server/mcp-runtime.js.map +1 -0
  406. package/dist/src/types/workflow/parse.js +4 -0
  407. package/dist/src/types/workflow/parse.js.map +1 -0
  408. package/dist/tests/core/account-id.test.js +35 -0
  409. package/dist/tests/core/account-id.test.js.map +1 -0
  410. package/dist/tests/core/actions-parse.test.js +176 -0
  411. package/dist/tests/core/actions-parse.test.js.map +1 -0
  412. package/dist/tests/core/adapter-config.test.js +133 -0
  413. package/dist/tests/core/adapter-config.test.js.map +1 -0
  414. package/dist/tests/core/admission.test.js +215 -0
  415. package/dist/tests/core/admission.test.js.map +1 -0
  416. package/dist/tests/core/args.test.js +132 -0
  417. package/dist/tests/core/args.test.js.map +1 -0
  418. package/dist/tests/core/availability.test.js +62 -0
  419. package/dist/tests/core/availability.test.js.map +1 -0
  420. package/dist/tests/core/checks.test.js +395 -0
  421. package/dist/tests/core/checks.test.js.map +1 -0
  422. package/dist/tests/core/circuit-breaker.test.js +172 -0
  423. package/dist/tests/core/circuit-breaker.test.js.map +1 -0
  424. package/dist/tests/core/coerce.test.js +87 -0
  425. package/dist/tests/core/coerce.test.js.map +1 -0
  426. package/dist/tests/core/context.test.js +228 -0
  427. package/dist/tests/core/context.test.js.map +1 -0
  428. package/dist/tests/core/decisions.test.js +310 -0
  429. package/dist/tests/core/decisions.test.js.map +1 -0
  430. package/dist/tests/core/derive.test.js +205 -0
  431. package/dist/tests/core/derive.test.js.map +1 -0
  432. package/dist/tests/core/dispatch-config.test.js +164 -0
  433. package/dist/tests/core/dispatch-config.test.js.map +1 -0
  434. package/dist/tests/core/dispatch.test.js +302 -0
  435. package/dist/tests/core/dispatch.test.js.map +1 -0
  436. package/dist/tests/core/eligibility.test.js +163 -0
  437. package/dist/tests/core/eligibility.test.js.map +1 -0
  438. package/dist/tests/core/extract.test.js +139 -0
  439. package/dist/tests/core/extract.test.js.map +1 -0
  440. package/dist/tests/core/fake-creds.test.js +134 -0
  441. package/dist/tests/core/fake-creds.test.js.map +1 -0
  442. package/dist/tests/core/file.test.js +197 -0
  443. package/dist/tests/core/file.test.js.map +1 -0
  444. package/dist/tests/core/git-result.test.js +113 -0
  445. package/dist/tests/core/git-result.test.js.map +1 -0
  446. package/dist/tests/core/identity.test.js +180 -0
  447. package/dist/tests/core/identity.test.js.map +1 -0
  448. package/dist/tests/core/image-decide.test.js +59 -0
  449. package/dist/tests/core/image-decide.test.js.map +1 -0
  450. package/dist/tests/core/injection.test.js +163 -0
  451. package/dist/tests/core/injection.test.js.map +1 -0
  452. package/dist/tests/core/ledger.test.js +218 -0
  453. package/dist/tests/core/ledger.test.js.map +1 -0
  454. package/dist/tests/core/managed-image.test.js +68 -0
  455. package/dist/tests/core/managed-image.test.js.map +1 -0
  456. package/dist/tests/core/mise.test.js +138 -0
  457. package/dist/tests/core/mise.test.js.map +1 -0
  458. package/dist/tests/core/parse.test.js +174 -0
  459. package/dist/tests/core/parse.test.js.map +1 -0
  460. package/dist/tests/core/path.test.js +50 -0
  461. package/dist/tests/core/path.test.js.map +1 -0
  462. package/dist/tests/core/plan.test.js +218 -0
  463. package/dist/tests/core/plan.test.js.map +1 -0
  464. package/dist/tests/core/post-move.test.js +162 -0
  465. package/dist/tests/core/post-move.test.js.map +1 -0
  466. package/dist/tests/core/pr-classify.test.js +117 -0
  467. package/dist/tests/core/pr-classify.test.js.map +1 -0
  468. package/dist/tests/core/pr-decide.test.js +298 -0
  469. package/dist/tests/core/pr-decide.test.js.map +1 -0
  470. package/dist/tests/core/pr-loop.test.js +301 -0
  471. package/dist/tests/core/pr-loop.test.js.map +1 -0
  472. package/dist/tests/core/pr-notes.test.js +165 -0
  473. package/dist/tests/core/pr-notes.test.js.map +1 -0
  474. package/dist/tests/core/predicates.test.js +154 -0
  475. package/dist/tests/core/predicates.test.js.map +1 -0
  476. package/dist/tests/core/prompt.test.js +189 -0
  477. package/dist/tests/core/prompt.test.js.map +1 -0
  478. package/dist/tests/core/protocol.test.js +195 -0
  479. package/dist/tests/core/protocol.test.js.map +1 -0
  480. package/dist/tests/core/reconcile-issue.test.js +116 -0
  481. package/dist/tests/core/reconcile-issue.test.js.map +1 -0
  482. package/dist/tests/core/render.test.js +549 -0
  483. package/dist/tests/core/render.test.js.map +1 -0
  484. package/dist/tests/core/retry.test.js +186 -0
  485. package/dist/tests/core/retry.test.js.map +1 -0
  486. package/dist/tests/core/routes.test.js +247 -0
  487. package/dist/tests/core/routes.test.js.map +1 -0
  488. package/dist/tests/core/run-fold.test.js +299 -0
  489. package/dist/tests/core/run-fold.test.js.map +1 -0
  490. package/dist/tests/core/shape.test.js +185 -0
  491. package/dist/tests/core/shape.test.js.map +1 -0
  492. package/dist/tests/core/sleep-cycle.test.js +150 -0
  493. package/dist/tests/core/sleep-cycle.test.js.map +1 -0
  494. package/dist/tests/core/slots.test.js +201 -0
  495. package/dist/tests/core/slots.test.js.map +1 -0
  496. package/dist/tests/core/state-resolve.test.js +80 -0
  497. package/dist/tests/core/state-resolve.test.js.map +1 -0
  498. package/dist/tests/core/summary.test.js +200 -0
  499. package/dist/tests/core/summary.test.js.map +1 -0
  500. package/dist/tests/core/template.test.js +116 -0
  501. package/dist/tests/core/template.test.js.map +1 -0
  502. package/dist/tests/core/tick.test.js +558 -0
  503. package/dist/tests/core/tick.test.js.map +1 -0
  504. package/dist/tests/core/token-fold.test.js +176 -0
  505. package/dist/tests/core/token-fold.test.js.map +1 -0
  506. package/dist/tests/core/turn.test.js +388 -0
  507. package/dist/tests/core/turn.test.js.map +1 -0
  508. package/dist/tests/core/url.test.js +118 -0
  509. package/dist/tests/core/url.test.js.map +1 -0
  510. package/dist/tests/core/validate.test.js +247 -0
  511. package/dist/tests/core/validate.test.js.map +1 -0
  512. package/dist/tests/core/views.test.js +252 -0
  513. package/dist/tests/core/views.test.js.map +1 -0
  514. package/dist/tests/core/vm-decide.test.js +110 -0
  515. package/dist/tests/core/vm-decide.test.js.map +1 -0
  516. package/dist/tests/core/vm-guards.test.js +153 -0
  517. package/dist/tests/core/vm-guards.test.js.map +1 -0
  518. package/dist/tests/core/vm-plan.test.js +332 -0
  519. package/dist/tests/core/vm-plan.test.js.map +1 -0
  520. package/dist/tests/core/vm-reap.test.js +196 -0
  521. package/dist/tests/core/vm-reap.test.js.map +1 -0
  522. package/dist/tests/core/workflow-parse.test.js +493 -0
  523. package/dist/tests/core/workflow-parse.test.js.map +1 -0
  524. package/dist/tests/core/workspace-decide.test.js +236 -0
  525. package/dist/tests/core/workspace-decide.test.js.map +1 -0
  526. package/dist/tests/helpers/fixtures.js +167 -0
  527. package/dist/tests/helpers/fixtures.js.map +1 -0
  528. package/dist/tests/shell/acp-substrate.test.js +101 -0
  529. package/dist/tests/shell/acp-substrate.test.js.map +1 -0
  530. package/dist/tests/shell/actions-runner-push.test.js +203 -0
  531. package/dist/tests/shell/actions-runner-push.test.js.map +1 -0
  532. package/dist/tests/shell/credential-hooks.test.js +36 -0
  533. package/dist/tests/shell/credential-hooks.test.js.map +1 -0
  534. package/dist/tests/shell/credential-registry.test.js +165 -0
  535. package/dist/tests/shell/credential-registry.test.js.map +1 -0
  536. package/dist/tests/shell/credential-substrate.test.js +179 -0
  537. package/dist/tests/shell/credential-substrate.test.js.map +1 -0
  538. package/dist/tests/shell/dockerfile-mise-pin.test.js +51 -0
  539. package/dist/tests/shell/dockerfile-mise-pin.test.js.map +1 -0
  540. package/dist/tests/shell/doctor.test.js +101 -0
  541. package/dist/tests/shell/doctor.test.js.map +1 -0
  542. package/dist/tests/shell/effect-vm-create.test.js +52 -0
  543. package/dist/tests/shell/effect-vm-create.test.js.map +1 -0
  544. package/dist/tests/shell/gh-port.test.js +63 -0
  545. package/dist/tests/shell/gh-port.test.js.map +1 -0
  546. package/dist/tests/shell/gondolin-dispatch-guard.test.js +144 -0
  547. package/dist/tests/shell/gondolin-dispatch-guard.test.js.map +1 -0
  548. package/dist/tests/shell/gondolin-dispatch-shquote.test.js +168 -0
  549. package/dist/tests/shell/gondolin-dispatch-shquote.test.js.map +1 -0
  550. package/dist/tests/shell/gondolin-image-converter.test.js +208 -0
  551. package/dist/tests/shell/gondolin-image-converter.test.js.map +1 -0
  552. package/dist/tests/shell/gondolin-image-fetch.test.js +93 -0
  553. package/dist/tests/shell/gondolin-image-fetch.test.js.map +1 -0
  554. package/dist/tests/shell/http-handler.test.js +608 -0
  555. package/dist/tests/shell/http-handler.test.js.map +1 -0
  556. package/dist/tests/shell/http-server.test.js +53 -0
  557. package/dist/tests/shell/http-server.test.js.map +1 -0
  558. package/dist/tests/shell/mcp-runtime.test.js +366 -0
  559. package/dist/tests/shell/mcp-runtime.test.js.map +1 -0
  560. package/dist/tests/shell/mise-config-asset.test.js +87 -0
  561. package/dist/tests/shell/mise-config-asset.test.js.map +1 -0
  562. package/dist/tests/shell/orchestrator-loop.test.js +583 -0
  563. package/dist/tests/shell/orchestrator-loop.test.js.map +1 -0
  564. package/dist/tests/shell/reconciler-passes.test.js +314 -0
  565. package/dist/tests/shell/reconciler-passes.test.js.map +1 -0
  566. package/dist/tests/shell/runner-loop-turn.test.js +97 -0
  567. package/dist/tests/shell/runner-loop-turn.test.js.map +1 -0
  568. package/dist/tests/shell/runner-slice.test.js +536 -0
  569. package/dist/tests/shell/runner-slice.test.js.map +1 -0
  570. package/dist/tests/shell/scaffold.test.js +65 -0
  571. package/dist/tests/shell/scaffold.test.js.map +1 -0
  572. package/dist/tests/shell/tick-config.test.js +83 -0
  573. package/dist/tests/shell/tick-config.test.js.map +1 -0
  574. package/dist/tests/shell/tracker-parse-dates.test.js +44 -0
  575. package/dist/tests/shell/tracker-parse-dates.test.js.map +1 -0
  576. package/dist/tests/shell/tracker-write-issue.test.js +154 -0
  577. package/dist/tests/shell/tracker-write-issue.test.js.map +1 -0
  578. package/dist/tests/shell/workflow-prompt-split.test.js +208 -0
  579. package/dist/tests/shell/workflow-prompt-split.test.js.map +1 -0
  580. package/dist/tests/shell/workspace-live-config.test.js +140 -0
  581. package/dist/tests/shell/workspace-live-config.test.js.map +1 -0
  582. package/package.json +21 -9
  583. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  584. package/prompts/Reflect.md +91 -0
  585. package/prompts/Review.md +97 -0
  586. package/prompts/Todo.md +96 -0
  587. package/prompts/_footer.md +41 -0
  588. package/prompts/_preamble.md +42 -0
  589. package/prompts-minimal/Todo.md +26 -0
  590. package/scripts/postinstall.mjs +63 -0
  591. package/scripts/vm-agent.mjs +312 -90
  592. package/WORKFLOW.md +0 -744
  593. package/dist/acp-bridge.js +0 -324
  594. package/dist/acp-bridge.js.map +0 -1
  595. package/dist/actions/cache.js +0 -191
  596. package/dist/actions/cache.js.map +0 -1
  597. package/dist/actions/effects.js +0 -41
  598. package/dist/actions/effects.js.map +0 -1
  599. package/dist/actions/executor.js +0 -570
  600. package/dist/actions/executor.js.map +0 -1
  601. package/dist/actions/index.js +0 -13
  602. package/dist/actions/index.js.map +0 -1
  603. package/dist/actions/parsing.js.map +0 -1
  604. package/dist/actions/predicate-env.js +0 -27
  605. package/dist/actions/predicate-env.js.map +0 -1
  606. package/dist/actions/predicates.js +0 -49
  607. package/dist/actions/predicates.js.map +0 -1
  608. package/dist/actions/templating.js +0 -66
  609. package/dist/actions/templating.js.map +0 -1
  610. package/dist/actions/types.js +0 -15
  611. package/dist/actions/types.js.map +0 -1
  612. package/dist/agent/acp.js +0 -473
  613. package/dist/agent/acp.js.map +0 -1
  614. package/dist/agent/adapter-names.js +0 -159
  615. package/dist/agent/adapter-names.js.map +0 -1
  616. package/dist/agent/adapters.js +0 -511
  617. package/dist/agent/adapters.js.map +0 -1
  618. package/dist/agent/credential-extractors.js +0 -342
  619. package/dist/agent/credential-extractors.js.map +0 -1
  620. package/dist/agent/credential-secrets.js +0 -628
  621. package/dist/agent/credential-secrets.js.map +0 -1
  622. package/dist/agent/credential-ticker.js +0 -57
  623. package/dist/agent/credential-ticker.js.map +0 -1
  624. package/dist/agent/gondolin-creds-staging.js +0 -356
  625. package/dist/agent/gondolin-creds-staging.js.map +0 -1
  626. package/dist/agent/gondolin-dispatch.js +0 -375
  627. package/dist/agent/gondolin-dispatch.js.map +0 -1
  628. package/dist/agent/gondolin.js +0 -124
  629. package/dist/agent/gondolin.js.map +0 -1
  630. package/dist/agent/runner-decisions.js +0 -134
  631. package/dist/agent/runner-decisions.js.map +0 -1
  632. package/dist/agent/runner.js +0 -1456
  633. package/dist/agent/runner.js.map +0 -1
  634. package/dist/agent/tool-call-summary.js +0 -102
  635. package/dist/agent/tool-call-summary.js.map +0 -1
  636. package/dist/agent/vm-acp-mapping.js +0 -73
  637. package/dist/agent/vm-acp-mapping.js.map +0 -1
  638. package/dist/agent/vm-guards.js +0 -262
  639. package/dist/agent/vm-guards.js.map +0 -1
  640. package/dist/agent/vm-port.js +0 -22
  641. package/dist/agent/vm-port.js.map +0 -1
  642. package/dist/agent/vm-process-registry.js +0 -79
  643. package/dist/agent/vm-process-registry.js.map +0 -1
  644. package/dist/bin/cli-args.js +0 -105
  645. package/dist/bin/cli-args.js.map +0 -1
  646. package/dist/bin/symphony.js +0 -794
  647. package/dist/bin/symphony.js.map +0 -1
  648. package/dist/errors.js +0 -15
  649. package/dist/errors.js.map +0 -1
  650. package/dist/http-disk.js +0 -135
  651. package/dist/http-disk.js.map +0 -1
  652. package/dist/http-handlers.js.map +0 -1
  653. package/dist/http.js.map +0 -1
  654. package/dist/issues.js +0 -178
  655. package/dist/issues.js.map +0 -1
  656. package/dist/logging.js +0 -203
  657. package/dist/logging.js.map +0 -1
  658. package/dist/mcp.js +0 -706
  659. package/dist/mcp.js.map +0 -1
  660. package/dist/memory.js +0 -85
  661. package/dist/memory.js.map +0 -1
  662. package/dist/orchestrator-decisions.js +0 -331
  663. package/dist/orchestrator-decisions.js.map +0 -1
  664. package/dist/orchestrator.js +0 -1569
  665. package/dist/orchestrator.js.map +0 -1
  666. package/dist/prompt.js +0 -65
  667. package/dist/prompt.js.map +0 -1
  668. package/dist/reconciler/cache.js +0 -65
  669. package/dist/reconciler/cache.js.map +0 -1
  670. package/dist/reconciler/index.js +0 -448
  671. package/dist/reconciler/index.js.map +0 -1
  672. package/dist/reconciler/ledger.js +0 -131
  673. package/dist/reconciler/ledger.js.map +0 -1
  674. package/dist/reconciler/pr-adapters.js +0 -174
  675. package/dist/reconciler/pr-adapters.js.map +0 -1
  676. package/dist/reconciler/pr-decide.js.map +0 -1
  677. package/dist/reconciler/pr.js +0 -422
  678. package/dist/reconciler/pr.js.map +0 -1
  679. package/dist/reconciler/types.js +0 -12
  680. package/dist/reconciler/types.js.map +0 -1
  681. package/dist/reconciler/vm.js +0 -243
  682. package/dist/reconciler/vm.js.map +0 -1
  683. package/dist/reconciler/workspace-defaults.js +0 -83
  684. package/dist/reconciler/workspace-defaults.js.map +0 -1
  685. package/dist/reconciler/workspace.js +0 -272
  686. package/dist/reconciler/workspace.js.map +0 -1
  687. package/dist/runlog.js +0 -403
  688. package/dist/runlog.js.map +0 -1
  689. package/dist/scaffold.js +0 -165
  690. package/dist/scaffold.js.map +0 -1
  691. package/dist/trackers/local.js +0 -445
  692. package/dist/trackers/local.js.map +0 -1
  693. package/dist/trackers/types.js +0 -10
  694. package/dist/trackers/types.js.map +0 -1
  695. package/dist/types.js +0 -3
  696. package/dist/types.js.map +0 -1
  697. package/dist/util/clock.js +0 -12
  698. package/dist/util/clock.js.map +0 -1
  699. package/dist/util/crypto.js +0 -25
  700. package/dist/util/crypto.js.map +0 -1
  701. package/dist/util/frontmatter.js +0 -70
  702. package/dist/util/frontmatter.js.map +0 -1
  703. package/dist/util/fs-issues.js +0 -22
  704. package/dist/util/fs-issues.js.map +0 -1
  705. package/dist/util/process.js +0 -152
  706. package/dist/util/process.js.map +0 -1
  707. package/dist/util/workspace-key.js +0 -10
  708. package/dist/util/workspace-key.js.map +0 -1
  709. package/dist/workflow-loader.js +0 -147
  710. package/dist/workflow-loader.js.map +0 -1
  711. package/dist/workflow.js +0 -822
  712. package/dist/workflow.js.map +0 -1
  713. package/dist/workspace-types.js +0 -8
  714. package/dist/workspace-types.js.map +0 -1
  715. package/dist/workspace.js +0 -443
  716. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,48 @@
1
+ // FCIS rewrite — pure image-readiness decision core (issue 206).
2
+ //
3
+ // The auto-convert-on-reconcile gate: given the shell's image-readiness
4
+ // projection (gondolin.oci_image set? a cached asset present? a build in flight?
5
+ // did the last build fail?), decide whether the poll tick must HOLD VM dispatch
6
+ // this round, and whether to EMIT an `ensure_image` effect to kick off the
7
+ // one-time conversion. The shell does all the docker/filesystem IO (resolve the
8
+ // OCI digest, look the asset up in the local gondolin store, run `buildAssets`);
9
+ // this function only folds the resulting flags into a decision so the gate is
10
+ // unit-testable without docker.
11
+ //
12
+ // HARD CONSTRAINTS (mirror the sibling reconcile deciders): 100% synchronous; no
13
+ // async/await/Promise; no IO, node: imports, process, clock, or randomness. The
14
+ // side effect is RETURNED as an Effect[]; the loop executes it.
15
+ /**
16
+ * Fold the image-readiness projection into the poll tick's hold/build decision.
17
+ *
18
+ * - `gondolin.image` escape hatch set → it WINS the runtime selector
19
+ * (`resolveImagePath`, mirrored by `checkGondolinImage`), so the `oci_image`
20
+ * auto-convert path is irrelevant: never hold dispatch on its readiness and
21
+ * never require the one-time conversion. The escape hatch takes precedence
22
+ * REGARDLESS of whether `oci_image` is set / converted / failing.
23
+ * - oci_image unset (or no ref) → auto-convert path inactive: never hold, never
24
+ * build (dispatch runs against the `gondolin.image` escape hatch).
25
+ * - cached asset present (`ready`) → proceed: never hold, never build.
26
+ * - configured but not ready → HOLD dispatch, and emit `ensure_image` UNLESS a
27
+ * build is already in flight (`building`) or the last build for this ref failed
28
+ * (`failed`, so we don't hammer a broken ref every poll — the dashboard banner
29
+ * + doctor surface the error and the operator fixes the ref / restarts).
30
+ *
31
+ * `escapeHatchConfigured` is whether `gondolin.image` is a non-empty selector,
32
+ * computed by the poll reducer the same way `resolveImagePath` tests it (so the
33
+ * gate, doctor, and the runtime selector can never disagree on which source wins).
34
+ */
35
+ export function decideImageReadiness(image, escapeHatchConfigured) {
36
+ if (escapeHatchConfigured)
37
+ return { holdDispatch: false, effects: [] };
38
+ if (!image || !image.ociConfigured || image.ref === null) {
39
+ return { holdDispatch: false, effects: [] };
40
+ }
41
+ if (image.ready)
42
+ return { holdDispatch: false, effects: [] };
43
+ const effects = image.building || image.failed
44
+ ? []
45
+ : [{ family: 'image', kind: 'ensure', ref: image.ref }];
46
+ return { holdDispatch: true, effects };
47
+ }
48
+ //# sourceMappingURL=image-decide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-decide.js","sourceRoot":"","sources":["../../../../src/core/reconcile/image-decide.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,wEAAwE;AACxE,iFAAiF;AACjF,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAChF,iFAAiF;AACjF,8EAA8E;AAC9E,gCAAgC;AAChC,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,gEAAgE;AAShE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAiC,EACjC,qBAA8B;IAE9B,IAAI,qBAAqB;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACvE,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACzD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC9C,CAAC;IACD,IAAI,KAAK,CAAC,KAAK;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC7D,MAAM,OAAO,GACX,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM;QAC5B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC"}
@@ -0,0 +1,142 @@
1
+ // FCIS rewrite — the ONE pure ResourceActionLedger reducer shared by every
2
+ // reconciler resource (vm / workspace / pr / actions). Each carries a
3
+ // most-recent-first buffer of `ActionStatus` rows ("what is this resource doing
4
+ // / what did it just try") for the dashboard. The original (REF:
5
+ // src/reconciler/ledger.ts) was a stateful CLASS that read `Date.now()` and
6
+ // mutated in place; here every transition is a PURE function returning a new
7
+ // immutable `LedgerState` with the wall clock INJECTED as an ISO string (`at`).
8
+ // The original async `run(action, fn)` helper stays in the shell, composed from
9
+ // start → (done | error). Per-function behavior is documented inline below; the
10
+ // buffer keeps a `maxHistory * 2` lookback so a late done/error still finds its
11
+ // `in_progress` row after a flood of newer actions, while `snapshot()` slices
12
+ // the freshest `maxHistory`.
13
+ /**
14
+ * Snapshot slice cap. Matches what each resource exposed as its private
15
+ * `MAX_ACTION_HISTORY` before the refactor: bake = 8, vm = 32, workspace = 32,
16
+ * pr = 64. Each call site passes its prior cap so snapshot payloads are
17
+ * unchanged.
18
+ */
19
+ export const DEFAULT_MAX_HISTORY = 32;
20
+ /** Construct an empty ledger for `resource`. `maxHistory` defaults to 32. */
21
+ export function createLedger(resource, maxHistory = DEFAULT_MAX_HISTORY) {
22
+ return { resource, maxHistory, buf: [] };
23
+ }
24
+ /**
25
+ * Prepend `status` and evict beyond the `maxHistory * 2` lookback window.
26
+ * Pure: returns a new state, leaves `state.buf` untouched.
27
+ */
28
+ function push(state, status) {
29
+ const next = [status, ...state.buf];
30
+ const cap = state.maxHistory * 2;
31
+ return { ...state, buf: next.length > cap ? next.slice(0, cap) : next };
32
+ }
33
+ /** Index of the most-recent `in_progress` row for `action`, or -1. */
34
+ function findInProgress(state, action) {
35
+ return state.buf.findIndex((a) => a.action === action && a.state === 'in_progress');
36
+ }
37
+ /** Replace the row at `idx` with `next`, returning a new state. */
38
+ function replaceAt(state, idx, next) {
39
+ const buf = state.buf.slice();
40
+ buf[idx] = next;
41
+ return { ...state, buf };
42
+ }
43
+ /** Prepend an `in_progress` row started at `at` (injected ISO clock reading). */
44
+ export function start(state, action, at) {
45
+ return push(state, {
46
+ resource: state.resource,
47
+ action,
48
+ state: 'in_progress',
49
+ started_at: at,
50
+ finished_at: null,
51
+ error: null,
52
+ });
53
+ }
54
+ /**
55
+ * Flip the most-recent matching `in_progress` row to `done` (finished at `at`).
56
+ * Idempotent: with no matching row this returns the SAME state value.
57
+ */
58
+ export function done(state, action, at) {
59
+ const idx = findInProgress(state, action);
60
+ if (idx < 0)
61
+ return state;
62
+ return replaceAt(state, idx, { ...state.buf[idx], state: 'done', finished_at: at });
63
+ }
64
+ /**
65
+ * Flip the matching `in_progress` row to `error` (finished at `at`); when no
66
+ * in-progress row exists, prepend a fresh orphan-error row with
67
+ * `started_at === finished_at`.
68
+ */
69
+ export function error(state, action, msg, at) {
70
+ const idx = findInProgress(state, action);
71
+ if (idx >= 0) {
72
+ return replaceAt(state, idx, {
73
+ ...state.buf[idx],
74
+ state: 'error',
75
+ finished_at: at,
76
+ error: msg,
77
+ });
78
+ }
79
+ return push(state, {
80
+ resource: state.resource,
81
+ action,
82
+ state: 'error',
83
+ started_at: at,
84
+ finished_at: at,
85
+ error: msg,
86
+ });
87
+ }
88
+ /**
89
+ * One-shot terminal annotation. The action is prepended already-finished;
90
+ * `started_at === finished_at`. Used for state that has no meaningful
91
+ * in-progress phase (workspace drift annotations, vm kill_session errors).
92
+ */
93
+ export function record(state, action, outcome, at, msg = null) {
94
+ return push(state, {
95
+ resource: state.resource,
96
+ action,
97
+ state: outcome,
98
+ started_at: at,
99
+ finished_at: at,
100
+ error: msg,
101
+ });
102
+ }
103
+ /** Most-recent-first slice of action statuses, capped at `maxItems` (default `maxHistory`). */
104
+ export function snapshot(state, maxItems) {
105
+ return state.buf.slice(0, maxItems ?? state.maxHistory);
106
+ }
107
+ /**
108
+ * The newest error message in the buffer, or null. Surfaced as `last_error` in
109
+ * both the ResourceSnapshot and ActionsSnapshot projections. (buf is
110
+ * most-recent-first, so the first error row is the freshest.)
111
+ */
112
+ function lastError(state) {
113
+ const row = state.buf.find((a) => a.state === 'error' && a.error !== null);
114
+ return row ? row.error : null;
115
+ }
116
+ /**
117
+ * Project a `ResourceSnapshot` for the dashboard. The ledger does not own the
118
+ * desired/actual convergence verdict, so the caller supplies `ready`,
119
+ * `desired_hash`, and (optionally) overrides `id` / `last_error`.
120
+ */
121
+ export function resourceSnapshot(state, opts) {
122
+ return {
123
+ id: opts.id ?? state.resource,
124
+ ready: opts.ready,
125
+ desired_hash: opts.desired_hash ?? null,
126
+ last_error: opts.last_error !== undefined ? opts.last_error : lastError(state),
127
+ actions: snapshot(state, opts.maxItems),
128
+ };
129
+ }
130
+ /**
131
+ * Project an `ActionsSnapshot` (the orchestrator drains this from the actions
132
+ * executor between attempts). `id` is conventionally `actions:<state>`.
133
+ */
134
+ export function actionsSnapshot(state, opts) {
135
+ return {
136
+ id: opts.id,
137
+ ready: opts.ready,
138
+ last_error: opts.last_error !== undefined ? opts.last_error : lastError(state),
139
+ actions: snapshot(state, opts.maxItems),
140
+ };
141
+ }
142
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../../../src/core/reconcile/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ // FCIS rewrite — pure PR-intent classifier.
2
+ //
3
+ // Functional-core port of the original `src/orchestrator-decisions.ts:
4
+ // classifyPrIntent`. Maps a tracker issue + the configured merge/close state
5
+ // names + the base branch into a `PrIntent` (the unit of work the PR autopilot
6
+ // reconciles) or `null` when the issue's state matches neither configured PR
7
+ // state.
8
+ //
9
+ // The original is already pure-ish (no IO of its own), but it took the
10
+ // merge-workspace path as a precomputed string — workspace pathing is an
11
+ // adapter concern (it depends on the workspace root + key scheme), so the
12
+ // SHELL still supplies `mergeWorkspacePath` here, exactly as before. Close
13
+ // intents drop the workspace path (terminal cleanup owns that directory), so
14
+ // the shell may pass anything for it on a pure-close issue.
15
+ //
16
+ // HARD CONSTRAINTS (gate-enforced):
17
+ // - 100% SYNCHRONOUS. No async/await/Promise anywhere.
18
+ // - No IO, no node: imports, no process/fetch, no wall clock, no randomness.
19
+ // - Imports ONLY from src/types/**.
20
+ //
21
+ // Behavior ported faithfully:
22
+ // - State matching is CASE-INSENSITIVE (both sides lower-cased) — the tracker
23
+ // stores the human-facing state label (e.g. "In Review") while the workflow
24
+ // config may declare it differently-cased.
25
+ // - `closeState === null` means "no close state configured" → never a close
26
+ // intent.
27
+ // - The emitted `state` field preserves the issue's ORIGINAL casing (the
28
+ // case-insensitive compare is only for matching, not for what we echo back).
29
+ // - Branch fallback: an empty / missing `branch_name` derives `agent/<id>`.
30
+ /**
31
+ * Classify a tracker issue into a PR autopilot intent or `null`.
32
+ *
33
+ * Returns `null` when the issue's state matches neither the configured merge
34
+ * state nor (if any) the configured close state. Otherwise returns a fully
35
+ * populated {@link PrIntent}:
36
+ * - `kind: 'merge'` when the state matches `mergeState` (merge takes
37
+ * precedence if — pathologically — both names coincide), with
38
+ * `workspace_path` set to the shell-supplied `mergeWorkspacePath`.
39
+ * - `kind: 'close'` otherwise, with `workspace_path: null`.
40
+ *
41
+ * Pure + synchronous: (data) -> data | null. No Effects: this is a pure
42
+ * classification, so the only "output" is the decision object itself.
43
+ */
44
+ export function classifyPrIntent(input) {
45
+ const stateLower = input.issue.state.toLowerCase();
46
+ const isMerge = stateLower === input.mergeState.toLowerCase();
47
+ const isClose = input.closeState !== null && stateLower === input.closeState.toLowerCase();
48
+ if (!isMerge && !isClose)
49
+ return null;
50
+ const branch = input.issue.branch_name && input.issue.branch_name.length > 0
51
+ ? input.issue.branch_name
52
+ : `agent/${input.issue.identifier}`;
53
+ return {
54
+ identifier: input.issue.identifier,
55
+ kind: isMerge ? 'merge' : 'close',
56
+ state: input.issue.state,
57
+ workspace_path: isMerge ? input.mergeWorkspacePath : null,
58
+ branch,
59
+ base_branch: input.baseBranch,
60
+ };
61
+ }
62
+ //# sourceMappingURL=pr-classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-classify.js","sourceRoot":"","sources":["../../../../src/core/reconcile/pr-classify.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,uEAAuE;AACvE,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,SAAS;AACT,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAC3E,6EAA6E;AAC7E,4DAA4D;AAC5D,EAAE;AACF,oCAAoC;AACpC,yDAAyD;AACzD,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,8BAA8B;AAC9B,gFAAgF;AAChF,gFAAgF;AAChF,+CAA+C;AAC/C,8EAA8E;AAC9E,cAAc;AACd,2EAA2E;AAC3E,iFAAiF;AACjF,8EAA8E;AAsB9E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAC9D,MAAM,OAAO,GACX,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAC7E,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,MAAM,GACV,KAAK,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QAC3D,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW;QACzB,CAAC,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAExC,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;QAClC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO;QACjC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;QACxB,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI;QACzD,MAAM;QACN,WAAW,EAAE,KAAK,CAAC,UAAU;KAC9B,CAAC;AACJ,CAAC"}
@@ -1,12 +1,20 @@
1
- // Pure decision logic for the PR autopilot (issue 70). `decidePr(observed) →
2
- // PrEffect[]` is the heart of the resource: every routing, arm, cleanup, and
3
- // reroute branch lives here as a deterministic function of an observation
4
- // snapshot. `pr.ts` is then the imperative shell that builds the observation,
5
- // applies effects in order through the injected ports, folds outcomes back
6
- // into the next observation, and re-decides until the decision list is empty.
1
+ // FCIS rewrite pure PR-autopilot decision core (issue 70 / issue 105).
7
2
  //
8
- // Effects-as-data lets the branch table be tested as a plain function over
9
- // input objects — and the shell shrinks to one effect-application switch.
3
+ // Verbatim port of the original `src/reconciler/pr-decide.ts`:
4
+ // decidePr, buildConflictNotes, and the two private deciders decideClose /
5
+ // decideConflict. The original module was already a pure, synchronous
6
+ // (data) -> data function — it declared `No IO imports. No clock. No
7
+ // randomness. Domain-only.` at the top. The only change in this rewrite is
8
+ // structural: the type vocabulary (PrEffect, PrObservation, PrIntent, PrView,
9
+ // …) now lives in `src/types/reconcile.ts` and is imported here rather than
10
+ // declared inline. The branch logic is unchanged.
11
+ //
12
+ // `decidePr(observation) → PrEffect[]` is the heart of the resource: every
13
+ // routing, arm, cleanup, and reroute branch lives here as a deterministic
14
+ // function of an observation snapshot. The imperative shell builds the
15
+ // observation, applies the returned effects in order through injected ports,
16
+ // folds outcomes back into the next observation, and re-decides until the
17
+ // decision list is empty.
10
18
  //
11
19
  // Multi-pass semantics: outcomes of IO effects (summary lookup, view fetch)
12
20
  // drive subsequent decisions. The shell records each outcome into the
@@ -14,10 +22,10 @@
14
22
  // `summaryResolved` / `viewResolved` to decide whether the next observation
15
23
  // step has run yet. The loop terminates when `decidePr` returns `[]`.
16
24
  //
17
- // Action ledger / log lines are handled inside the effect handlers in
18
- // `pr.ts` this module only emits the data describing what should happen.
19
- //
20
- // No IO imports. No clock. No randomness. Domain-only.
25
+ // HARD CONSTRAINTS (gate-enforced):
26
+ // - 100% SYNCHRONOUS. No async/await/Promise anywhere.
27
+ // - No IO, no node: imports, no process/fetch, no wall clock, no randomness.
28
+ // - Imports ONLY from src/types/**.
21
29
  /**
22
30
  * Pure decision: given an observation, return the next batch of effects the
23
31
  * shell should apply. After applying, the shell rebuilds the observation
@@ -136,6 +144,11 @@ function decideClose(obs) {
136
144
  */
137
145
  function decideConflict(obs) {
138
146
  const id = obs.intent.identifier;
147
+ // `decideConflict` is only reached from the MERGEABLE-branch guard in
148
+ // `decidePr` after `obs.view === null` has been ruled out, so the view is
149
+ // always present here. The original asserted this with a non-null assertion
150
+ // (`obs.view!`); we narrow explicitly instead to keep the module free of
151
+ // `!` and still hand a defined `PrView` to `buildConflictNotes`.
139
152
  const view = obs.view;
140
153
  return [
141
154
  {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-decide.js","sourceRoot":"","sources":["../../../../src/core/reconcile/pr-decide.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ // FCIS rewrite — pure multi-pass driver of `decidePr` (the PR autopilot's
2
+ // per-intent decide → apply → re-decide loop).
3
+ //
4
+ // Ported faithfully from the reference `src/reconciler/pr.ts`
5
+ // (`processIntent` + `applyEffect`). The reference was an async driver that:
6
+ //
7
+ // • built a fresh `PrObservation` from live `PerIssueState`,
8
+ // • looped up to 12 times calling the pure `decidePr(obs)`,
9
+ // • AWAITED each emitted `PrEffect` against injected `gh` / tracker / cleanup
10
+ // ports (IO), and
11
+ // • folded each effect's OUTCOME back into the next observation
12
+ // (`summaryResolved`/`summary`, `viewResolved`/`view`, `closeAttempted`/
13
+ // `closeOutcome`, `halt`, and the sticky per-issue cache view: `armed` /
14
+ // `lastObservedHeadSha` / `completed`).
15
+ //
16
+ // In the functional core all the IO disappears. This module is JUST the fold:
17
+ //
18
+ // • The shell APPLIES each `PrEffect` (running the matching gh/tracker/fs
19
+ // port) and hands the result back as a `PrEffectOutcome` — the exact data
20
+ // the reference's `applyEffect` derived from its awaited IO call.
21
+ // • `foldPrEffectOutcome(obs, effect, outcome)` reproduces the reference's
22
+ // `applyEffect` switch as a pure `(obs, effect, outcome) -> obs` reducer:
23
+ // it threads the next observation (including the sticky cache view) without
24
+ // touching any port.
25
+ // • `decidePrPass(obs, decide)` runs ONE `decidePr` call and returns the
26
+ // emitted effects (or `[]` to stop) — `decide` is the pure `decidePr`
27
+ // injected as a plain parameter so this driver stays import-clean and
28
+ // independently testable.
29
+ // • `MAX_PR_DECIDE_ITERATIONS` is the reference's defensive 12-pass cap; the
30
+ // shell drives the outer loop and emits an `iteration_cap_reached` log when
31
+ // `driveStartState`/the loop signals it.
32
+ //
33
+ // The reference's sticky-cache mutations (`PerIssueState.armed` /
34
+ // `lastObservedHeadSha` / `completed`, and the `resetTransient` wipe) lived in
35
+ // the live `PerIssueState` map; here they ride in the `PrCacheView` carried by
36
+ // the observation. The shell folds the returned `obs.cache` back into its
37
+ // per-issue state after each pass.
38
+ //
39
+ // 100% SYNCHRONOUS. No async / await / Promise. No IO, no node: imports, no
40
+ // wall clock or randomness. Imports ONLY from src/types. (NodeNext ESM → .js
41
+ // extensions on relative imports.)
42
+ /**
43
+ * Defensive iteration cap, mirroring the reference's `for (let i = 0; i < 12;`
44
+ * loop in `processIntent`. `decidePr` strictly reduces work on every iteration
45
+ * (a `*_resolved` flag flips, a cache latch lands, or `halt` is set), so the
46
+ * loop terminates well inside this bound; the cap only guards against a future
47
+ * decide-table regression looping forever.
48
+ */
49
+ export const MAX_PR_DECIDE_ITERATIONS = 12;
50
+ /**
51
+ * Run one `decidePr` pass over the current observation. Returns the effects the
52
+ * shell should apply (in order), or `[]` to terminate the per-intent loop.
53
+ *
54
+ * Mirrors the reference's `const effects = decidePr(obs); if (effects.length
55
+ * === 0) return;`. Kept as a one-liner seam so the shell never imports
56
+ * `decidePr` directly and the cap loop below has a single decide entry point.
57
+ */
58
+ export function decidePrPass(obs, decide) {
59
+ return decide(obs);
60
+ }
61
+ /**
62
+ * Fold one applied `PrEffect`'s outcome back into the next observation. Pure
63
+ * reproduction of the reference `applyEffect` switch (`src/reconciler/pr.ts`):
64
+ * the shell runs the IO for `effect`, hands back the matching `PrEffectOutcome`,
65
+ * and this returns the observation the next `decidePr` call should see.
66
+ *
67
+ * Every branch returns a NEW observation object (never mutates the input),
68
+ * matching the reference's `{ ...obs, ... }` spreads.
69
+ */
70
+ export function foldPrEffectOutcome(obs, effect, outcome) {
71
+ switch (effect.kind) {
72
+ case 'observe_summary': {
73
+ // Reference: `{ ...obs, summaryResolved: true, summary, cache }`.
74
+ const summary = outcome.kind === 'summary' ? outcome.summary : null;
75
+ const cache = outcome.kind === 'summary' ? outcome.cache : obs.cache;
76
+ return { ...obs, summaryResolved: true, summary, cache };
77
+ }
78
+ case 'observe_view': {
79
+ // Reference: `{ ...obs, viewResolved: true, view, cache }` — the shell's
80
+ // `fetchPrView` also folds `armed` / `lastObservedHeadSha` from the view
81
+ // into the cache, which arrives here in `outcome.cache`.
82
+ const view = outcome.kind === 'view' ? outcome.view : null;
83
+ const cache = outcome.kind === 'view' ? outcome.cache : obs.cache;
84
+ return { ...obs, viewResolved: true, view, cache };
85
+ }
86
+ case 'arm_auto_merge': {
87
+ // Reference: sets `st.armed = true`, returns `{ ...obs, cache }`.
88
+ const cache = outcome.kind === 'cache' ? outcome.cache : obs.cache;
89
+ return { ...obs, cache };
90
+ }
91
+ case 'update_branch':
92
+ // Reference: `{ ...obs, halt: true }`. The cached view still reads BEHIND;
93
+ // halting prevents re-emitting `update_branch` every iteration.
94
+ return { ...obs, halt: true };
95
+ case 'close_pr': {
96
+ // Reference: `{ ...obs, closeAttempted: true, closeOutcome: { ok } }`.
97
+ const ok = outcome.kind === 'close' ? outcome.ok : false;
98
+ return { ...obs, closeAttempted: true, closeOutcome: { ok } };
99
+ }
100
+ case 'delete_remote_branch':
101
+ case 'cleanup_workspace':
102
+ // Reference: returns `obs` unchanged (fire-and-forget, best-effort).
103
+ return obs;
104
+ case 'route_conflict':
105
+ // Reference: `{ ...obs, halt: true }`. Terminal for this pass; the paired
106
+ // `reset_transient` effect carries the cache wipe.
107
+ return { ...obs, halt: true };
108
+ case 'update_observed_head': {
109
+ // Reference: sets `st.lastObservedHeadSha = eff.sha`, returns `{ ...obs, cache }`.
110
+ const cache = outcome.kind === 'cache' ? outcome.cache : obs.cache;
111
+ return { ...obs, cache };
112
+ }
113
+ case 'mark_completed': {
114
+ // Reference: sets `st.completed = true`, returns `{ ...obs, cache }`.
115
+ const cache = outcome.kind === 'cache' ? outcome.cache : obs.cache;
116
+ return { ...obs, cache };
117
+ }
118
+ case 'reset_transient': {
119
+ // Reference: `resetTransient(st)` then `{ ...obs, cache, halt: true }`.
120
+ // Always paired with `route_conflict`; terminal for this pass. The shell
121
+ // hands back the wiped cache view (or the EMPTY cache if the per-issue
122
+ // state had already been dropped).
123
+ const cache = outcome.kind === 'cache' ? outcome.cache : obs.cache;
124
+ return { ...obs, cache, halt: true };
125
+ }
126
+ }
127
+ }
128
+ /**
129
+ * Pure multi-pass driver: the functional-core form of the reference
130
+ * `processIntent`. Repeatedly runs `decide(obs)`; if it emits no effects the
131
+ * loop ends; otherwise it folds each effect's applied outcome (via `apply`)
132
+ * back into the observation and re-decides — capped at
133
+ * {@link MAX_PR_DECIDE_ITERATIONS} passes.
134
+ *
135
+ * `apply` is synchronous on purpose: in production the shell drives the loop
136
+ * directly (awaiting IO between folds); this helper exists for pure tests and
137
+ * for shells that can resolve every effect synchronously. The fold reducer
138
+ * ({@link foldPrEffectOutcome}) and the pass seam ({@link decidePrPass}) are the
139
+ * load-bearing pure pieces — `drivePr` just sequences them, exactly like the
140
+ * reference loop, with no IO of its own.
141
+ */
142
+ export function drivePr(initial, decide, apply) {
143
+ let obs = initial;
144
+ const steps = [];
145
+ for (let i = 0; i < MAX_PR_DECIDE_ITERATIONS; i += 1) {
146
+ const effects = decidePrPass(obs, decide);
147
+ if (effects.length === 0) {
148
+ return { observation: obs, steps, stop: 'idle' };
149
+ }
150
+ for (const effect of effects) {
151
+ const outcome = apply(effect, obs);
152
+ obs = foldPrEffectOutcome(obs, effect, outcome);
153
+ }
154
+ steps.push({ effects, observation: obs });
155
+ // A halt set by this pass means decidePr will return [] next iteration; we
156
+ // let the loop spin once more so the terminating `[]` is observed (matching
157
+ // the reference, where the next `decidePr` short-circuits on `obs.halt`).
158
+ }
159
+ return { observation: obs, steps, stop: 'iteration_cap' };
160
+ }
161
+ //# sourceMappingURL=pr-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-loop.js","sourceRoot":"","sources":["../../../../src/core/reconcile/pr-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ // FCIS rewrite — pure per-PR cache TTL freshness predicates.
2
+ //
3
+ // Extracted from the original `src/reconciler/pr.ts` (`PrResource.lookupPrSummary`
4
+ // + `PrResource.fetchPrView`), which tangled these freshness checks into the same
5
+ // async methods that shelled out to `gh pr list`/`gh pr view` and mutated state.
6
+ // Here it is ONLY the decision: given an injected `now` plus the prior
7
+ // `lastLookupAt`/`lastViewAt` and the cached payloads, reuse or re-poll/re-fetch.
8
+ //
9
+ // Two INDEPENDENT predicates (issue 70 "sticky cache" mustFix):
10
+ // 1. Summary (sticky): once a PR is FOUND it is reused forever — PR numbers
11
+ // don't change and a found PR moving OPEN→MERGED/CLOSED must keep its cached
12
+ // number so the autopilot can observe the terminal state. `lastLookupAt`
13
+ // governs ONLY the cache-MISS (null) re-poll.
14
+ // 2. View (TTL): plain time-to-live on the detailed view, keyed solely on its
15
+ // own `lastViewAt` — never coupled to the summary re-poll.
16
+ //
17
+ // `pollIntervalMs === 0` disables caching on both paths: the strict `<` makes any
18
+ // elapsed (>= 0) fall outside the window. Pure + synchronous: `now` is injected.
19
+ /** Mirrors the head of `PrResource.lookupPrSummary`: sticky short-circuit, then null-result TTL guard. */
20
+ export function decidePrSummaryFreshness(input) {
21
+ if (input.summary)
22
+ return { kind: 'reuse', summary: input.summary };
23
+ if (input.summary === null && input.now - input.lastLookupAt < input.pollIntervalMs) {
24
+ return { kind: 'defer' };
25
+ }
26
+ return { kind: 'repoll' };
27
+ }
28
+ /** Mirrors the head of `PrResource.fetchPrView`: reuse while inside the TTL, else re-fetch (strict `<`). */
29
+ export function decidePrViewFreshness(input) {
30
+ if (input.view !== null && input.now - input.lastViewAt < input.pollIntervalMs) {
31
+ return { kind: 'reuse', view: input.view };
32
+ }
33
+ return { kind: 'refetch' };
34
+ }
35
+ //# sourceMappingURL=pr-notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-notes.js","sourceRoot":"","sources":["../../../../src/core/reconcile/pr-notes.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,mFAAmF;AACnF,kFAAkF;AAClF,iFAAiF;AACjF,uEAAuE;AACvE,kFAAkF;AAClF,EAAE;AACF,gEAAgE;AAChE,8EAA8E;AAC9E,kFAAkF;AAClF,8EAA8E;AAC9E,mDAAmD;AACnD,gFAAgF;AAChF,gEAAgE;AAChE,EAAE;AACF,kFAAkF;AAClF,iFAAiF;AAsBjF,0GAA0G;AAC1G,MAAM,UAAU,wBAAwB,CAAC,KAA8B;IACrE,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACpE,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QACpF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAmBD,4GAA4G;AAC5G,MAAM,UAAU,qBAAqB,CAAC,KAA2B;IAC/D,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QAC/E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,70 @@
1
+ // FCIS rewrite — pure VM-reaper decision core (issue 33 / reconciler stage 2;
2
+ // Gondolin migration Phase 4).
3
+ //
4
+ // Verbatim port of the original `src/reconciler/vm.ts:decideVm`. The original
5
+ // module mixed the pure decision (`decideVm`) with the imperative reaper loop
6
+ // (`VmResource`, gc / listSessions / process.kill / setTimeout). This rewrite
7
+ // extracts ONLY the pure decision; the SIGTERM→grace→SIGKILL escalation, the
8
+ // Gondolin gc, the session enumeration, and the ledger all live in the shell.
9
+ //
10
+ // The decision: given the observed live Gondolin session set, the orchestrator's
11
+ // intended label set, and the reaper's own pid, return one `kill_session`
12
+ // effect per session that is a LIVE orphan — a `symphony-`-prefixed session that
13
+ // the orchestrator does NOT intend to keep alive AND whose host pid is safe to
14
+ // signal (a sane positive integer that is not the reaper itself).
15
+ //
16
+ // Pid safety (verbatim from the original): a host signal is only ever emitted
17
+ // for a `pid` that is a safe positive integer and is NOT the reaper's own pid.
18
+ // The shell delivers the signal with `process.kill(pid, sig)`, which signals the
19
+ // whole PROCESS GROUP for `pid <= 0` and would target the orchestrator itself
20
+ // for `pid === selfPid`. A session carrying a `0`/`NaN`/negative/self pid is a
21
+ // registry anomaly, never a legitimate orphan, so it is left untouched here.
22
+ //
23
+ // Namespace safety (verbatim): anything outside the symphony-prefixed namespace
24
+ // — a session with no label, or a label that doesn't start with the prefix — is
25
+ // left untouched (operator VMs, sibling tools' sessions). STALE/dead-pid
26
+ // sessions and orphan sockets are NOT this function's concern; Gondolin's
27
+ // `gc()` collects them in the shell before the live session set reaches here.
28
+ //
29
+ // HARD CONSTRAINTS (gate-enforced): 100% synchronous; no async/await/Promise; no
30
+ // IO, node: imports, process, clock, or randomness. Imports ONLY from
31
+ // src/types. Every side effect is RETURNED as a `KillSessionAction[]`; the
32
+ // shell executes it.
33
+ /**
34
+ * Domain constant. Every VM the orchestrator creates is labelled with this
35
+ * prefix (the runner mints `sessionLabel = symphony-<identifier>`); the reaper
36
+ * only acts on labels that match. The vm-port adapter in the shell mirrors the
37
+ * same value — keep them in sync.
38
+ */
39
+ export const SYMPHONY_VM_PREFIX = 'symphony-';
40
+ /**
41
+ * Pure decision: given the observed Gondolin session state, return the
42
+ * `kill_session` effects the shell should apply. No IO, no clock reads, no
43
+ * logging.
44
+ *
45
+ * Effect rule:
46
+ * • Session has a `label` that starts with SYMPHONY_VM_PREFIX and isn't
47
+ * intended, AND a sane, non-self host pid → `kill_session` (the shell
48
+ * SIGTERM→SIGKILLs its host pid).
49
+ */
50
+ export function decideVm(state) {
51
+ const out = [];
52
+ for (const s of state.sessions) {
53
+ const label = s.label;
54
+ if (label === undefined)
55
+ continue;
56
+ if (!label.startsWith(SYMPHONY_VM_PREFIX))
57
+ continue;
58
+ if (state.intendedLabels.has(label))
59
+ continue;
60
+ // Pid safety: skip anomalous pids (0/NaN/negative — process-group / invalid)
61
+ // and never signal the reaper's own process.
62
+ if (!Number.isSafeInteger(s.pid) || s.pid <= 0)
63
+ continue;
64
+ if (s.pid === state.selfPid)
65
+ continue;
66
+ out.push({ kind: 'kill_session', pid: s.pid, label });
67
+ }
68
+ return out;
69
+ }
70
+ //# sourceMappingURL=vm-decide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vm-decide.js","sourceRoot":"","sources":["../../../../src/core/reconcile/vm-decide.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+BAA+B;AAC/B,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,EAAE;AACF,iFAAiF;AACjF,0EAA0E;AAC1E,iFAAiF;AACjF,+EAA+E;AAC/E,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,8EAA8E;AAC9E,+EAA+E;AAC/E,6EAA6E;AAC7E,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,yEAAyE;AACzE,0EAA0E;AAC1E,8EAA8E;AAC9E,EAAE;AACF,iFAAiF;AACjF,sEAAsE;AACtE,2EAA2E;AAC3E,qBAAqB;AAIrB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAsB;IAC7C,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QACtB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC;YAAE,SAAS;QACpD,IAAI,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QAC9C,6EAA6E;QAC7E,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAAE,SAAS;QACzD,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO;YAAE,SAAS;QACtC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}