smol-symphony 0.1.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 (627) hide show
  1. package/AGENTS.md +125 -39
  2. package/DESIGN.md +494 -273
  3. package/PRODUCT.md +2 -1
  4. package/README.md +259 -110
  5. package/SPEC.md +565 -1928
  6. package/WORKFLOW.minimal.yaml +34 -0
  7. package/WORKFLOW.template.yaml +1100 -0
  8. package/WORKFLOW.yaml +487 -0
  9. package/assets/skills/symphony-issues/SKILL.md +136 -0
  10. package/assets/symphony-mise.system.toml +68 -0
  11. package/dist/src/bin/symphony.js +30 -0
  12. package/dist/src/bin/symphony.js.map +1 -0
  13. package/dist/src/core/actions/context.js +109 -0
  14. package/dist/src/core/actions/context.js.map +1 -0
  15. package/dist/src/core/actions/parse.js +192 -0
  16. package/dist/src/core/actions/parse.js.map +1 -0
  17. package/dist/src/core/actions/plan.js +197 -0
  18. package/dist/src/core/actions/plan.js.map +1 -0
  19. package/dist/src/core/actions/predicates.js +111 -0
  20. package/dist/src/core/actions/predicates.js.map +1 -0
  21. package/dist/src/core/actions/run-fold.js +248 -0
  22. package/dist/src/core/actions/run-fold.js.map +1 -0
  23. package/dist/src/core/actions/template.js +118 -0
  24. package/dist/src/core/actions/template.js.map +1 -0
  25. package/dist/src/core/cli/args.js +116 -0
  26. package/dist/src/core/cli/args.js.map +1 -0
  27. package/dist/src/core/coerce.js +75 -0
  28. package/dist/src/core/coerce.js.map +1 -0
  29. package/dist/src/core/credential/account-id.js +20 -0
  30. package/dist/src/core/credential/account-id.js.map +1 -0
  31. package/dist/src/core/credential/adapter-config.js +136 -0
  32. package/dist/src/core/credential/adapter-config.js.map +1 -0
  33. package/dist/src/core/credential/availability.js +98 -0
  34. package/dist/src/core/credential/availability.js.map +1 -0
  35. package/dist/src/core/credential/extract.js +228 -0
  36. package/dist/src/core/credential/extract.js.map +1 -0
  37. package/dist/src/core/credential/fake-creds.js +171 -0
  38. package/dist/src/core/credential/fake-creds.js.map +1 -0
  39. package/dist/src/core/credential/identity.js +125 -0
  40. package/dist/src/core/credential/identity.js.map +1 -0
  41. package/dist/src/core/credential/shape.js +230 -0
  42. package/dist/src/core/credential/shape.js.map +1 -0
  43. package/dist/src/core/credential/strings.js +15 -0
  44. package/dist/src/core/credential/strings.js.map +1 -0
  45. package/dist/src/core/doctor/checks.js +303 -0
  46. package/dist/src/core/doctor/checks.js.map +1 -0
  47. package/dist/src/core/git/result.js +107 -0
  48. package/dist/src/core/git/result.js.map +1 -0
  49. package/dist/src/core/http/decisions.js +225 -0
  50. package/dist/src/core/http/decisions.js.map +1 -0
  51. package/dist/src/core/http/render.js +1635 -0
  52. package/dist/src/core/http/render.js.map +1 -0
  53. package/dist/src/core/http/routes.js +145 -0
  54. package/dist/src/core/http/routes.js.map +1 -0
  55. package/dist/src/core/http/views.js +181 -0
  56. package/dist/src/core/http/views.js.map +1 -0
  57. package/dist/src/core/image/managed-image.js +95 -0
  58. package/dist/src/core/image/managed-image.js.map +1 -0
  59. package/dist/src/core/issue/file.js +149 -0
  60. package/dist/src/core/issue/file.js.map +1 -0
  61. package/dist/src/core/issue/parse.js +210 -0
  62. package/dist/src/core/issue/parse.js.map +1 -0
  63. package/dist/src/core/mcp/dispatch.js +239 -0
  64. package/dist/src/core/mcp/dispatch.js.map +1 -0
  65. package/dist/src/core/mcp/post-move.js +92 -0
  66. package/dist/src/core/mcp/post-move.js.map +1 -0
  67. package/dist/src/core/mcp/protocol.js +293 -0
  68. package/dist/src/core/mcp/protocol.js.map +1 -0
  69. package/dist/src/core/mcp/url.js +162 -0
  70. package/dist/src/core/mcp/url.js.map +1 -0
  71. package/dist/src/core/path.js +63 -0
  72. package/dist/src/core/path.js.map +1 -0
  73. package/dist/src/core/reconcile/image-decide.js +48 -0
  74. package/dist/src/core/reconcile/image-decide.js.map +1 -0
  75. package/dist/src/core/reconcile/ledger.js +142 -0
  76. package/dist/src/core/reconcile/ledger.js.map +1 -0
  77. package/dist/src/core/reconcile/pr-classify.js +62 -0
  78. package/dist/src/core/reconcile/pr-classify.js.map +1 -0
  79. package/dist/src/core/reconcile/pr-decide.js +180 -0
  80. package/dist/src/core/reconcile/pr-decide.js.map +1 -0
  81. package/dist/src/core/reconcile/pr-loop.js +161 -0
  82. package/dist/src/core/reconcile/pr-loop.js.map +1 -0
  83. package/dist/src/core/reconcile/pr-notes.js +35 -0
  84. package/dist/src/core/reconcile/pr-notes.js.map +1 -0
  85. package/dist/src/core/reconcile/vm-decide.js +70 -0
  86. package/dist/src/core/reconcile/vm-decide.js.map +1 -0
  87. package/dist/src/core/reconcile/vm-reap.js +207 -0
  88. package/dist/src/core/reconcile/vm-reap.js.map +1 -0
  89. package/dist/src/core/reconcile/workspace-decide.js +162 -0
  90. package/dist/src/core/reconcile/workspace-decide.js.map +1 -0
  91. package/dist/src/core/runlog/summary.js +231 -0
  92. package/dist/src/core/runlog/summary.js.map +1 -0
  93. package/dist/src/core/runner/dispatch-config.js +95 -0
  94. package/dist/src/core/runner/dispatch-config.js.map +1 -0
  95. package/dist/src/core/runner/injection.js +61 -0
  96. package/dist/src/core/runner/injection.js.map +1 -0
  97. package/dist/src/core/runner/mise.js +210 -0
  98. package/dist/src/core/runner/mise.js.map +1 -0
  99. package/dist/src/core/runner/prompt.js +720 -0
  100. package/dist/src/core/runner/prompt.js.map +1 -0
  101. package/dist/src/core/runner/turn.js +242 -0
  102. package/dist/src/core/runner/turn.js.map +1 -0
  103. package/dist/src/core/runner/vm-plan.js +390 -0
  104. package/dist/src/core/runner/vm-plan.js.map +1 -0
  105. package/dist/src/core/schedule/admission.js +123 -0
  106. package/dist/src/core/schedule/admission.js.map +1 -0
  107. package/dist/src/core/schedule/circuit-breaker.js +111 -0
  108. package/dist/src/core/schedule/circuit-breaker.js.map +1 -0
  109. package/dist/src/core/schedule/eligibility.js +83 -0
  110. package/dist/src/core/schedule/eligibility.js.map +1 -0
  111. package/dist/src/core/schedule/reconcile-issue.js +82 -0
  112. package/dist/src/core/schedule/reconcile-issue.js.map +1 -0
  113. package/dist/src/core/schedule/retry.js +96 -0
  114. package/dist/src/core/schedule/retry.js.map +1 -0
  115. package/dist/src/core/schedule/sleep-cycle.js +133 -0
  116. package/dist/src/core/schedule/sleep-cycle.js.map +1 -0
  117. package/dist/src/core/schedule/slots.js +124 -0
  118. package/dist/src/core/schedule/slots.js.map +1 -0
  119. package/dist/src/core/schedule/tick.js +553 -0
  120. package/dist/src/core/schedule/tick.js.map +1 -0
  121. package/dist/src/core/schedule/token-fold.js +181 -0
  122. package/dist/src/core/schedule/token-fold.js.map +1 -0
  123. package/dist/src/core/state-resolve.js +86 -0
  124. package/dist/src/core/state-resolve.js.map +1 -0
  125. package/dist/src/core/vm-guards.js +278 -0
  126. package/dist/src/core/vm-guards.js.map +1 -0
  127. package/dist/src/core/workflow/derive.js +107 -0
  128. package/dist/src/core/workflow/derive.js.map +1 -0
  129. package/dist/src/core/workflow/parse.js +687 -0
  130. package/dist/src/core/workflow/parse.js.map +1 -0
  131. package/dist/src/core/workflow/prompt-probe.js +78 -0
  132. package/dist/src/core/workflow/prompt-probe.js.map +1 -0
  133. package/dist/src/core/workflow/validate.js +189 -0
  134. package/dist/src/core/workflow/validate.js.map +1 -0
  135. package/dist/src/core/workspace-key.js +19 -0
  136. package/dist/src/core/workspace-key.js.map +1 -0
  137. package/dist/src/shell/actions-runner.js +356 -0
  138. package/dist/src/shell/actions-runner.js.map +1 -0
  139. package/dist/src/shell/adapter/adapter-registry.js +45 -0
  140. package/dist/src/shell/adapter/adapter-registry.js.map +1 -0
  141. package/dist/src/shell/adapter/clock-random.js +96 -0
  142. package/dist/src/shell/adapter/clock-random.js.map +1 -0
  143. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  144. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  145. package/dist/src/shell/adapter/gondolin-dispatch.js +385 -0
  146. package/dist/src/shell/adapter/gondolin-dispatch.js.map +1 -0
  147. package/dist/src/shell/adapter/gondolin-image-converter.js +233 -0
  148. package/dist/src/shell/adapter/gondolin-image-converter.js.map +1 -0
  149. package/dist/src/shell/adapter/gondolin-image-fetch.js +180 -0
  150. package/dist/src/shell/adapter/gondolin-image-fetch.js.map +1 -0
  151. package/dist/src/shell/adapter/launcher-asset.js +57 -0
  152. package/dist/src/shell/adapter/launcher-asset.js.map +1 -0
  153. package/dist/src/shell/adapter/mise-config-asset.js +65 -0
  154. package/dist/src/shell/adapter/mise-config-asset.js.map +1 -0
  155. package/dist/src/shell/adapter/workflow-loader.js +304 -0
  156. package/dist/src/shell/adapter/workflow-loader.js.map +1 -0
  157. package/dist/src/shell/cli/doctor.js +268 -0
  158. package/dist/src/shell/cli/doctor.js.map +1 -0
  159. package/dist/src/shell/effect-interpreter-families.js +314 -0
  160. package/dist/src/shell/effect-interpreter-families.js.map +1 -0
  161. package/dist/src/shell/effect-interpreter.js +29 -0
  162. package/dist/src/shell/effect-interpreter.js.map +1 -0
  163. package/dist/src/shell/interp/acp-frame.js +137 -0
  164. package/dist/src/shell/interp/acp-frame.js.map +1 -0
  165. package/dist/src/shell/interp/acp-ws-conn.js +320 -0
  166. package/dist/src/shell/interp/acp-ws-conn.js.map +1 -0
  167. package/dist/src/shell/interp/acp-ws-frames.js +159 -0
  168. package/dist/src/shell/interp/acp-ws-frames.js.map +1 -0
  169. package/dist/src/shell/interp/acp-ws.js +197 -0
  170. package/dist/src/shell/interp/acp-ws.js.map +1 -0
  171. package/dist/src/shell/interp/acp.js +319 -0
  172. package/dist/src/shell/interp/acp.js.map +1 -0
  173. package/dist/src/shell/interp/credential-defaults.js +128 -0
  174. package/dist/src/shell/interp/credential-defaults.js.map +1 -0
  175. package/dist/src/shell/interp/credential-hooks.js +149 -0
  176. package/dist/src/shell/interp/credential-hooks.js.map +1 -0
  177. package/dist/src/shell/interp/credential-registry.js +226 -0
  178. package/dist/src/shell/interp/credential-registry.js.map +1 -0
  179. package/dist/src/shell/interp/credential.js +103 -0
  180. package/dist/src/shell/interp/credential.js.map +1 -0
  181. package/dist/src/shell/interp/gh.js +163 -0
  182. package/dist/src/shell/interp/gh.js.map +1 -0
  183. package/dist/src/shell/interp/git.js +28 -0
  184. package/dist/src/shell/interp/git.js.map +1 -0
  185. package/dist/src/shell/interp/log.js +213 -0
  186. package/dist/src/shell/interp/log.js.map +1 -0
  187. package/dist/src/shell/interp/process.js +178 -0
  188. package/dist/src/shell/interp/process.js.map +1 -0
  189. package/dist/src/shell/interp/runlog.js +193 -0
  190. package/dist/src/shell/interp/runlog.js.map +1 -0
  191. package/dist/src/shell/interp/timer.js +64 -0
  192. package/dist/src/shell/interp/timer.js.map +1 -0
  193. package/dist/src/shell/interp/tracker-disk.js +99 -0
  194. package/dist/src/shell/interp/tracker-disk.js.map +1 -0
  195. package/dist/src/shell/interp/tracker-parse.js +71 -0
  196. package/dist/src/shell/interp/tracker-parse.js.map +1 -0
  197. package/dist/src/shell/interp/tracker-scan.js +238 -0
  198. package/dist/src/shell/interp/tracker-scan.js.map +1 -0
  199. package/dist/src/shell/interp/tracker-write.js +91 -0
  200. package/dist/src/shell/interp/tracker-write.js.map +1 -0
  201. package/dist/src/shell/interp/tracker.js +41 -0
  202. package/dist/src/shell/interp/tracker.js.map +1 -0
  203. package/dist/src/shell/interp/tty.js +48 -0
  204. package/dist/src/shell/interp/tty.js.map +1 -0
  205. package/dist/src/shell/interp/vm.js +199 -0
  206. package/dist/src/shell/interp/vm.js.map +1 -0
  207. package/dist/src/shell/interp/workspace.js +310 -0
  208. package/dist/src/shell/interp/workspace.js.map +1 -0
  209. package/dist/src/shell/main-acp.js +78 -0
  210. package/dist/src/shell/main-acp.js.map +1 -0
  211. package/dist/src/shell/main-adapters.js +222 -0
  212. package/dist/src/shell/main-adapters.js.map +1 -0
  213. package/dist/src/shell/main-credential.js +122 -0
  214. package/dist/src/shell/main-credential.js.map +1 -0
  215. package/dist/src/shell/main-doctor.js +22 -0
  216. package/dist/src/shell/main-doctor.js.map +1 -0
  217. package/dist/src/shell/main-entry.js +46 -0
  218. package/dist/src/shell/main-entry.js.map +1 -0
  219. package/dist/src/shell/main-http-csrf.js +45 -0
  220. package/dist/src/shell/main-http-csrf.js.map +1 -0
  221. package/dist/src/shell/main-http-handler.js +389 -0
  222. package/dist/src/shell/main-http-handler.js.map +1 -0
  223. package/dist/src/shell/main-http-mcp.js +122 -0
  224. package/dist/src/shell/main-http-mcp.js.map +1 -0
  225. package/dist/src/shell/main-http-views.js +253 -0
  226. package/dist/src/shell/main-http-views.js.map +1 -0
  227. package/dist/src/shell/main-http.js +76 -0
  228. package/dist/src/shell/main-http.js.map +1 -0
  229. package/dist/src/shell/main-loops.js +130 -0
  230. package/dist/src/shell/main-loops.js.map +1 -0
  231. package/dist/src/shell/main-mcp.js +129 -0
  232. package/dist/src/shell/main-mcp.js.map +1 -0
  233. package/dist/src/shell/main-orchestrator.js +120 -0
  234. package/dist/src/shell/main-orchestrator.js.map +1 -0
  235. package/dist/src/shell/main-preflight.js +43 -0
  236. package/dist/src/shell/main-preflight.js.map +1 -0
  237. package/dist/src/shell/main-reconcilers-helpers.js +244 -0
  238. package/dist/src/shell/main-reconcilers-helpers.js.map +1 -0
  239. package/dist/src/shell/main-reconcilers-pr.js +148 -0
  240. package/dist/src/shell/main-reconcilers-pr.js.map +1 -0
  241. package/dist/src/shell/main-reconcilers.js +225 -0
  242. package/dist/src/shell/main-reconcilers.js.map +1 -0
  243. package/dist/src/shell/main-runner.js +355 -0
  244. package/dist/src/shell/main-runner.js.map +1 -0
  245. package/dist/src/shell/main-scaffold.js +116 -0
  246. package/dist/src/shell/main-scaffold.js.map +1 -0
  247. package/dist/src/shell/main-shutdown.js +115 -0
  248. package/dist/src/shell/main-shutdown.js.map +1 -0
  249. package/dist/src/shell/main-startup.js +48 -0
  250. package/dist/src/shell/main-startup.js.map +1 -0
  251. package/dist/src/shell/main-substrates.js +43 -0
  252. package/dist/src/shell/main-substrates.js.map +1 -0
  253. package/dist/src/shell/main.js +385 -0
  254. package/dist/src/shell/main.js.map +1 -0
  255. package/dist/src/shell/orchestrator-feedback.js +69 -0
  256. package/dist/src/shell/orchestrator-feedback.js.map +1 -0
  257. package/dist/src/shell/orchestrator-image.js +167 -0
  258. package/dist/src/shell/orchestrator-image.js.map +1 -0
  259. package/dist/src/shell/orchestrator-loop.js +468 -0
  260. package/dist/src/shell/orchestrator-loop.js.map +1 -0
  261. package/dist/src/shell/orchestrator-reconcile.js +36 -0
  262. package/dist/src/shell/orchestrator-reconcile.js.map +1 -0
  263. package/dist/src/shell/reconciler-loop.js +228 -0
  264. package/dist/src/shell/reconciler-loop.js.map +1 -0
  265. package/dist/src/shell/runner-loop-turn.js +301 -0
  266. package/dist/src/shell/runner-loop-turn.js.map +1 -0
  267. package/dist/src/shell/runner-loop.js +338 -0
  268. package/dist/src/shell/runner-loop.js.map +1 -0
  269. package/dist/src/shell/server/http.js +208 -0
  270. package/dist/src/shell/server/http.js.map +1 -0
  271. package/dist/src/shell/server/mcp-runtime-effects.js +237 -0
  272. package/dist/src/shell/server/mcp-runtime-effects.js.map +1 -0
  273. package/dist/src/shell/server/mcp-runtime.js +99 -0
  274. package/dist/src/shell/server/mcp-runtime.js.map +1 -0
  275. package/dist/src/shell/workspace-key.js +14 -0
  276. package/dist/src/shell/workspace-key.js.map +1 -0
  277. package/dist/src/types/acp.js +8 -0
  278. package/dist/src/types/acp.js.map +1 -0
  279. package/dist/src/types/actions/plan.js +6 -0
  280. package/dist/src/types/actions/plan.js.map +1 -0
  281. package/dist/src/types/actions/predicates.js +6 -0
  282. package/dist/src/types/actions/predicates.js.map +1 -0
  283. package/dist/src/types/actions/run-fold.js +8 -0
  284. package/dist/src/types/actions/run-fold.js.map +1 -0
  285. package/dist/src/types/actions.js +7 -0
  286. package/dist/src/types/actions.js.map +1 -0
  287. package/dist/src/types/adapter/clock-random.js +4 -0
  288. package/dist/src/types/adapter/clock-random.js.map +1 -0
  289. package/dist/src/types/adapter/gondolin-image-converter.js +5 -0
  290. package/dist/src/types/adapter/gondolin-image-converter.js.map +1 -0
  291. package/dist/src/types/adapter/gondolin-image-fetch.js +5 -0
  292. package/dist/src/types/adapter/gondolin-image-fetch.js.map +1 -0
  293. package/dist/src/types/adapter/workflow-loader.js +4 -0
  294. package/dist/src/types/adapter/workflow-loader.js.map +1 -0
  295. package/dist/src/types/cli/args.js +8 -0
  296. package/dist/src/types/cli/args.js.map +1 -0
  297. package/dist/src/types/config.js +8 -0
  298. package/dist/src/types/config.js.map +1 -0
  299. package/dist/src/types/credential-interp.js +6 -0
  300. package/dist/src/types/credential-interp.js.map +1 -0
  301. package/dist/src/types/credentials.js +10 -0
  302. package/dist/src/types/credentials.js.map +1 -0
  303. package/dist/src/types/doctor.js +7 -0
  304. package/dist/src/types/doctor.js.map +1 -0
  305. package/dist/src/types/domain.js +7 -0
  306. package/dist/src/types/domain.js.map +1 -0
  307. package/dist/src/types/effect.js +15 -0
  308. package/dist/src/types/effect.js.map +1 -0
  309. package/dist/src/types/errors.js +39 -0
  310. package/dist/src/types/errors.js.map +1 -0
  311. package/dist/src/types/http/decisions.js +6 -0
  312. package/dist/src/types/http/decisions.js.map +1 -0
  313. package/dist/src/types/http/render.js +10 -0
  314. package/dist/src/types/http/render.js.map +1 -0
  315. package/dist/src/types/http/views.js +6 -0
  316. package/dist/src/types/http/views.js.map +1 -0
  317. package/dist/src/types/http.js +9 -0
  318. package/dist/src/types/http.js.map +1 -0
  319. package/dist/src/types/image/managed-image.js +7 -0
  320. package/dist/src/types/image/managed-image.js.map +1 -0
  321. package/dist/src/types/interp/effect-interpreter.js +8 -0
  322. package/dist/src/types/interp/effect-interpreter.js.map +1 -0
  323. package/dist/src/types/interp/tracker.js +7 -0
  324. package/dist/src/types/interp/tracker.js.map +1 -0
  325. package/dist/src/types/issue/file.js +6 -0
  326. package/dist/src/types/issue/file.js.map +1 -0
  327. package/dist/src/types/issue/parse.js +8 -0
  328. package/dist/src/types/issue/parse.js.map +1 -0
  329. package/dist/src/types/main-acp.js +13 -0
  330. package/dist/src/types/main-acp.js.map +1 -0
  331. package/dist/src/types/main-adapters.js +5 -0
  332. package/dist/src/types/main-adapters.js.map +1 -0
  333. package/dist/src/types/main-credential.js +21 -0
  334. package/dist/src/types/main-credential.js.map +1 -0
  335. package/dist/src/types/main-doctor.js +6 -0
  336. package/dist/src/types/main-doctor.js.map +1 -0
  337. package/dist/src/types/main-http-handler.js +12 -0
  338. package/dist/src/types/main-http-handler.js.map +1 -0
  339. package/dist/src/types/main-http.js +5 -0
  340. package/dist/src/types/main-http.js.map +1 -0
  341. package/dist/src/types/main-loops.js +5 -0
  342. package/dist/src/types/main-loops.js.map +1 -0
  343. package/dist/src/types/main-mcp.js +12 -0
  344. package/dist/src/types/main-mcp.js.map +1 -0
  345. package/dist/src/types/main-orchestrator.js +5 -0
  346. package/dist/src/types/main-orchestrator.js.map +1 -0
  347. package/dist/src/types/main-reconcilers.js +11 -0
  348. package/dist/src/types/main-reconcilers.js.map +1 -0
  349. package/dist/src/types/main-runner.js +13 -0
  350. package/dist/src/types/main-runner.js.map +1 -0
  351. package/dist/src/types/main-startup.js +5 -0
  352. package/dist/src/types/main-startup.js.map +1 -0
  353. package/dist/src/types/main-substrates.js +5 -0
  354. package/dist/src/types/main-substrates.js.map +1 -0
  355. package/dist/src/types/mcp/dispatch.js +4 -0
  356. package/dist/src/types/mcp/dispatch.js.map +1 -0
  357. package/dist/src/types/mcp/post-move.js +7 -0
  358. package/dist/src/types/mcp/post-move.js.map +1 -0
  359. package/dist/src/types/mcp.js +9 -0
  360. package/dist/src/types/mcp.js.map +1 -0
  361. package/dist/src/types/ports.js +12 -0
  362. package/dist/src/types/ports.js.map +1 -0
  363. package/dist/src/types/reconcile/image-decide.js +5 -0
  364. package/dist/src/types/reconcile/image-decide.js.map +1 -0
  365. package/dist/src/types/reconcile/ledger.js +7 -0
  366. package/dist/src/types/reconcile/ledger.js.map +1 -0
  367. package/dist/src/types/reconcile/pr-loop.js +8 -0
  368. package/dist/src/types/reconcile/pr-loop.js.map +1 -0
  369. package/dist/src/types/reconcile/vm-reap.js +8 -0
  370. package/dist/src/types/reconcile/vm-reap.js.map +1 -0
  371. package/dist/src/types/reconcile/workspace-decide.js +7 -0
  372. package/dist/src/types/reconcile/workspace-decide.js.map +1 -0
  373. package/dist/src/types/reconcile.js +9 -0
  374. package/dist/src/types/reconcile.js.map +1 -0
  375. package/dist/src/types/runlog.js +7 -0
  376. package/dist/src/types/runlog.js.map +1 -0
  377. package/dist/src/types/runner/actions-runner.js +12 -0
  378. package/dist/src/types/runner/actions-runner.js.map +1 -0
  379. package/dist/src/types/runner/gondolin-dispatch.js +5 -0
  380. package/dist/src/types/runner/gondolin-dispatch.js.map +1 -0
  381. package/dist/src/types/runner/injection.js +6 -0
  382. package/dist/src/types/runner/injection.js.map +1 -0
  383. package/dist/src/types/runner/runner-loop.js +5 -0
  384. package/dist/src/types/runner/runner-loop.js.map +1 -0
  385. package/dist/src/types/runner/turn.js +4 -0
  386. package/dist/src/types/runner/turn.js.map +1 -0
  387. package/dist/src/types/runner/vm-plan.js +4 -0
  388. package/dist/src/types/runner/vm-plan.js.map +1 -0
  389. package/dist/src/types/runtime.js +9 -0
  390. package/dist/src/types/runtime.js.map +1 -0
  391. package/dist/src/types/schedule/admission.js +7 -0
  392. package/dist/src/types/schedule/admission.js.map +1 -0
  393. package/dist/src/types/schedule/circuit-breaker.js +2 -0
  394. package/dist/src/types/schedule/circuit-breaker.js.map +1 -0
  395. package/dist/src/types/schedule/eligibility.js +9 -0
  396. package/dist/src/types/schedule/eligibility.js.map +1 -0
  397. package/dist/src/types/schedule/orchestrator-loop.js +10 -0
  398. package/dist/src/types/schedule/orchestrator-loop.js.map +1 -0
  399. package/dist/src/types/schedule/sleep-cycle.js +4 -0
  400. package/dist/src/types/schedule/sleep-cycle.js.map +1 -0
  401. package/dist/src/types/schedule/slots.js +8 -0
  402. package/dist/src/types/schedule/slots.js.map +1 -0
  403. package/dist/src/types/schedule/tick.js +9 -0
  404. package/dist/src/types/schedule/tick.js.map +1 -0
  405. package/dist/src/types/server/mcp-runtime.js +8 -0
  406. package/dist/src/types/server/mcp-runtime.js.map +1 -0
  407. package/dist/src/types/workflow/parse.js +4 -0
  408. package/dist/src/types/workflow/parse.js.map +1 -0
  409. package/dist/tests/core/account-id.test.js +35 -0
  410. package/dist/tests/core/account-id.test.js.map +1 -0
  411. package/dist/tests/core/actions-parse.test.js +176 -0
  412. package/dist/tests/core/actions-parse.test.js.map +1 -0
  413. package/dist/tests/core/adapter-config.test.js +133 -0
  414. package/dist/tests/core/adapter-config.test.js.map +1 -0
  415. package/dist/tests/core/admission.test.js +215 -0
  416. package/dist/tests/core/admission.test.js.map +1 -0
  417. package/dist/tests/core/args.test.js +132 -0
  418. package/dist/tests/core/args.test.js.map +1 -0
  419. package/dist/tests/core/availability.test.js +62 -0
  420. package/dist/tests/core/availability.test.js.map +1 -0
  421. package/dist/tests/core/checks.test.js +395 -0
  422. package/dist/tests/core/checks.test.js.map +1 -0
  423. package/dist/tests/core/circuit-breaker.test.js +172 -0
  424. package/dist/tests/core/circuit-breaker.test.js.map +1 -0
  425. package/dist/tests/core/coerce.test.js +87 -0
  426. package/dist/tests/core/coerce.test.js.map +1 -0
  427. package/dist/tests/core/context.test.js +228 -0
  428. package/dist/tests/core/context.test.js.map +1 -0
  429. package/dist/tests/core/decisions.test.js +310 -0
  430. package/dist/tests/core/decisions.test.js.map +1 -0
  431. package/dist/tests/core/derive.test.js +205 -0
  432. package/dist/tests/core/derive.test.js.map +1 -0
  433. package/dist/tests/core/dispatch-config.test.js +164 -0
  434. package/dist/tests/core/dispatch-config.test.js.map +1 -0
  435. package/dist/tests/core/dispatch.test.js +302 -0
  436. package/dist/tests/core/dispatch.test.js.map +1 -0
  437. package/dist/tests/core/eligibility.test.js +163 -0
  438. package/dist/tests/core/eligibility.test.js.map +1 -0
  439. package/dist/tests/core/extract.test.js +139 -0
  440. package/dist/tests/core/extract.test.js.map +1 -0
  441. package/dist/tests/core/fake-creds.test.js +134 -0
  442. package/dist/tests/core/fake-creds.test.js.map +1 -0
  443. package/dist/tests/core/file.test.js +197 -0
  444. package/dist/tests/core/file.test.js.map +1 -0
  445. package/dist/tests/core/git-result.test.js +113 -0
  446. package/dist/tests/core/git-result.test.js.map +1 -0
  447. package/dist/tests/core/identity.test.js +180 -0
  448. package/dist/tests/core/identity.test.js.map +1 -0
  449. package/dist/tests/core/image-decide.test.js +59 -0
  450. package/dist/tests/core/image-decide.test.js.map +1 -0
  451. package/dist/tests/core/injection.test.js +163 -0
  452. package/dist/tests/core/injection.test.js.map +1 -0
  453. package/dist/tests/core/ledger.test.js +218 -0
  454. package/dist/tests/core/ledger.test.js.map +1 -0
  455. package/dist/tests/core/managed-image.test.js +68 -0
  456. package/dist/tests/core/managed-image.test.js.map +1 -0
  457. package/dist/tests/core/mise.test.js +138 -0
  458. package/dist/tests/core/mise.test.js.map +1 -0
  459. package/dist/tests/core/parse.test.js +174 -0
  460. package/dist/tests/core/parse.test.js.map +1 -0
  461. package/dist/tests/core/path.test.js +50 -0
  462. package/dist/tests/core/path.test.js.map +1 -0
  463. package/dist/tests/core/plan.test.js +218 -0
  464. package/dist/tests/core/plan.test.js.map +1 -0
  465. package/dist/tests/core/post-move.test.js +162 -0
  466. package/dist/tests/core/post-move.test.js.map +1 -0
  467. package/dist/tests/core/pr-classify.test.js +117 -0
  468. package/dist/tests/core/pr-classify.test.js.map +1 -0
  469. package/dist/tests/core/pr-decide.test.js +298 -0
  470. package/dist/tests/core/pr-decide.test.js.map +1 -0
  471. package/dist/tests/core/pr-loop.test.js +301 -0
  472. package/dist/tests/core/pr-loop.test.js.map +1 -0
  473. package/dist/tests/core/pr-notes.test.js +165 -0
  474. package/dist/tests/core/pr-notes.test.js.map +1 -0
  475. package/dist/tests/core/predicates.test.js +154 -0
  476. package/dist/tests/core/predicates.test.js.map +1 -0
  477. package/dist/tests/core/prompt.test.js +189 -0
  478. package/dist/tests/core/prompt.test.js.map +1 -0
  479. package/dist/tests/core/protocol.test.js +195 -0
  480. package/dist/tests/core/protocol.test.js.map +1 -0
  481. package/dist/tests/core/reconcile-issue.test.js +116 -0
  482. package/dist/tests/core/reconcile-issue.test.js.map +1 -0
  483. package/dist/tests/core/render.test.js +549 -0
  484. package/dist/tests/core/render.test.js.map +1 -0
  485. package/dist/tests/core/retry.test.js +186 -0
  486. package/dist/tests/core/retry.test.js.map +1 -0
  487. package/dist/tests/core/routes.test.js +247 -0
  488. package/dist/tests/core/routes.test.js.map +1 -0
  489. package/dist/tests/core/run-fold.test.js +299 -0
  490. package/dist/tests/core/run-fold.test.js.map +1 -0
  491. package/dist/tests/core/shape.test.js +185 -0
  492. package/dist/tests/core/shape.test.js.map +1 -0
  493. package/dist/tests/core/sleep-cycle.test.js +150 -0
  494. package/dist/tests/core/sleep-cycle.test.js.map +1 -0
  495. package/dist/tests/core/slots.test.js +201 -0
  496. package/dist/tests/core/slots.test.js.map +1 -0
  497. package/dist/tests/core/state-resolve.test.js +80 -0
  498. package/dist/tests/core/state-resolve.test.js.map +1 -0
  499. package/dist/tests/core/summary.test.js +200 -0
  500. package/dist/tests/core/summary.test.js.map +1 -0
  501. package/dist/tests/core/template.test.js +116 -0
  502. package/dist/tests/core/template.test.js.map +1 -0
  503. package/dist/tests/core/tick.test.js +558 -0
  504. package/dist/tests/core/tick.test.js.map +1 -0
  505. package/dist/tests/core/token-fold.test.js +176 -0
  506. package/dist/tests/core/token-fold.test.js.map +1 -0
  507. package/dist/tests/core/turn.test.js +388 -0
  508. package/dist/tests/core/turn.test.js.map +1 -0
  509. package/dist/tests/core/url.test.js +118 -0
  510. package/dist/tests/core/url.test.js.map +1 -0
  511. package/dist/tests/core/validate.test.js +247 -0
  512. package/dist/tests/core/validate.test.js.map +1 -0
  513. package/dist/tests/core/views.test.js +252 -0
  514. package/dist/tests/core/views.test.js.map +1 -0
  515. package/dist/tests/core/vm-decide.test.js +110 -0
  516. package/dist/tests/core/vm-decide.test.js.map +1 -0
  517. package/dist/tests/core/vm-guards.test.js +153 -0
  518. package/dist/tests/core/vm-guards.test.js.map +1 -0
  519. package/dist/tests/core/vm-plan.test.js +332 -0
  520. package/dist/tests/core/vm-plan.test.js.map +1 -0
  521. package/dist/tests/core/vm-reap.test.js +196 -0
  522. package/dist/tests/core/vm-reap.test.js.map +1 -0
  523. package/dist/tests/core/workflow-parse.test.js +493 -0
  524. package/dist/tests/core/workflow-parse.test.js.map +1 -0
  525. package/dist/tests/core/workspace-decide.test.js +236 -0
  526. package/dist/tests/core/workspace-decide.test.js.map +1 -0
  527. package/dist/tests/helpers/fixtures.js +167 -0
  528. package/dist/tests/helpers/fixtures.js.map +1 -0
  529. package/dist/tests/shell/acp-substrate.test.js +101 -0
  530. package/dist/tests/shell/acp-substrate.test.js.map +1 -0
  531. package/dist/tests/shell/actions-runner-push.test.js +203 -0
  532. package/dist/tests/shell/actions-runner-push.test.js.map +1 -0
  533. package/dist/tests/shell/credential-hooks.test.js +36 -0
  534. package/dist/tests/shell/credential-hooks.test.js.map +1 -0
  535. package/dist/tests/shell/credential-registry.test.js +165 -0
  536. package/dist/tests/shell/credential-registry.test.js.map +1 -0
  537. package/dist/tests/shell/credential-substrate.test.js +179 -0
  538. package/dist/tests/shell/credential-substrate.test.js.map +1 -0
  539. package/dist/tests/shell/dockerfile-mise-pin.test.js +51 -0
  540. package/dist/tests/shell/dockerfile-mise-pin.test.js.map +1 -0
  541. package/dist/tests/shell/doctor.test.js +101 -0
  542. package/dist/tests/shell/doctor.test.js.map +1 -0
  543. package/dist/tests/shell/effect-vm-create.test.js +52 -0
  544. package/dist/tests/shell/effect-vm-create.test.js.map +1 -0
  545. package/dist/tests/shell/gh-port.test.js +63 -0
  546. package/dist/tests/shell/gh-port.test.js.map +1 -0
  547. package/dist/tests/shell/gondolin-dispatch-guard.test.js +144 -0
  548. package/dist/tests/shell/gondolin-dispatch-guard.test.js.map +1 -0
  549. package/dist/tests/shell/gondolin-dispatch-shquote.test.js +168 -0
  550. package/dist/tests/shell/gondolin-dispatch-shquote.test.js.map +1 -0
  551. package/dist/tests/shell/gondolin-image-converter.test.js +208 -0
  552. package/dist/tests/shell/gondolin-image-converter.test.js.map +1 -0
  553. package/dist/tests/shell/gondolin-image-fetch.test.js +93 -0
  554. package/dist/tests/shell/gondolin-image-fetch.test.js.map +1 -0
  555. package/dist/tests/shell/http-handler.test.js +608 -0
  556. package/dist/tests/shell/http-handler.test.js.map +1 -0
  557. package/dist/tests/shell/http-server.test.js +53 -0
  558. package/dist/tests/shell/http-server.test.js.map +1 -0
  559. package/dist/tests/shell/mcp-runtime.test.js +366 -0
  560. package/dist/tests/shell/mcp-runtime.test.js.map +1 -0
  561. package/dist/tests/shell/mise-config-asset.test.js +87 -0
  562. package/dist/tests/shell/mise-config-asset.test.js.map +1 -0
  563. package/dist/tests/shell/orchestrator-loop.test.js +583 -0
  564. package/dist/tests/shell/orchestrator-loop.test.js.map +1 -0
  565. package/dist/tests/shell/reconciler-passes.test.js +314 -0
  566. package/dist/tests/shell/reconciler-passes.test.js.map +1 -0
  567. package/dist/tests/shell/runner-loop-turn.test.js +97 -0
  568. package/dist/tests/shell/runner-loop-turn.test.js.map +1 -0
  569. package/dist/tests/shell/runner-slice.test.js +536 -0
  570. package/dist/tests/shell/runner-slice.test.js.map +1 -0
  571. package/dist/tests/shell/scaffold.test.js +65 -0
  572. package/dist/tests/shell/scaffold.test.js.map +1 -0
  573. package/dist/tests/shell/tick-config.test.js +83 -0
  574. package/dist/tests/shell/tick-config.test.js.map +1 -0
  575. package/dist/tests/shell/tracker-parse-dates.test.js +44 -0
  576. package/dist/tests/shell/tracker-parse-dates.test.js.map +1 -0
  577. package/dist/tests/shell/tracker-write-issue.test.js +154 -0
  578. package/dist/tests/shell/tracker-write-issue.test.js.map +1 -0
  579. package/dist/tests/shell/workflow-prompt-split.test.js +208 -0
  580. package/dist/tests/shell/workflow-prompt-split.test.js.map +1 -0
  581. package/dist/tests/shell/workspace-live-config.test.js +140 -0
  582. package/dist/tests/shell/workspace-live-config.test.js.map +1 -0
  583. package/package.json +31 -11
  584. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  585. package/prompts/Reflect.md +91 -0
  586. package/prompts/Review.md +97 -0
  587. package/prompts/Todo.md +96 -0
  588. package/prompts/_footer.md +41 -0
  589. package/prompts/_preamble.md +42 -0
  590. package/prompts-minimal/Todo.md +26 -0
  591. package/scripts/postinstall.mjs +63 -0
  592. package/scripts/vm-agent.mjs +433 -0
  593. package/WORKFLOW.md +0 -269
  594. package/WORKFLOW.template.md +0 -307
  595. package/dist/agent/acp.js +0 -304
  596. package/dist/agent/acp.js.map +0 -1
  597. package/dist/agent/adapters.js +0 -275
  598. package/dist/agent/adapters.js.map +0 -1
  599. package/dist/agent/codex.js +0 -439
  600. package/dist/agent/codex.js.map +0 -1
  601. package/dist/agent/runner.js +0 -394
  602. package/dist/agent/runner.js.map +0 -1
  603. package/dist/agent/smolvm.js +0 -174
  604. package/dist/agent/smolvm.js.map +0 -1
  605. package/dist/bin/symphony.js +0 -205
  606. package/dist/bin/symphony.js.map +0 -1
  607. package/dist/http.js +0 -1189
  608. package/dist/http.js.map +0 -1
  609. package/dist/logging.js +0 -45
  610. package/dist/logging.js.map +0 -1
  611. package/dist/mcp.js +0 -478
  612. package/dist/mcp.js.map +0 -1
  613. package/dist/orchestrator.js +0 -683
  614. package/dist/orchestrator.js.map +0 -1
  615. package/dist/prompt.js +0 -65
  616. package/dist/prompt.js.map +0 -1
  617. package/dist/trackers/local.js +0 -344
  618. package/dist/trackers/local.js.map +0 -1
  619. package/dist/trackers/types.js +0 -10
  620. package/dist/trackers/types.js.map +0 -1
  621. package/dist/types.js +0 -3
  622. package/dist/types.js.map +0 -1
  623. package/dist/workflow.js +0 -385
  624. package/dist/workflow.js.map +0 -1
  625. package/dist/workspace.js +0 -196
  626. package/dist/workspace.js.map +0 -1
  627. package/scripts/build-vm.sh +0 -67
package/PRODUCT.md CHANGED
@@ -23,7 +23,8 @@ process is logging.
23
23
 
24
24
  smol-symphony is a small TypeScript orchestrator that takes Markdown issues off a
25
25
  local tracker, prepares per-issue workspaces, and runs coding agents
26
- (Claude Code, Codex, OpenCode) inside isolated smolvm microVMs over ACP. The
26
+ (Claude Code, Codex, OpenCode) inside isolated per-issue Gondolin microVMs over
27
+ ACP. The
27
28
  HTTP dashboard exists to do two things well:
28
29
 
29
30
  1. **Dispatch** — create issues into the tracker without dropping back to the
package/README.md CHANGED
@@ -1,77 +1,180 @@
1
1
  # smol-symphony
2
2
 
3
+ > **Disclaimer:** this project is written using AI — the orchestrator
4
+ > dispatches AI coding agents at itself, and the bulk of the code in this
5
+ > repository was authored by those agents under human review.
6
+
3
7
  A small TypeScript orchestrator that reads issues off a local Markdown tracker,
4
8
  prepares per-issue workspaces, and runs coding agents (Claude Code, Codex,
5
- OpenCode) inside isolated [smolvm](https://smolmachines.com/) microVMs over the
6
- [Agent Client Protocol](https://agentclientprotocol.com).
9
+ OpenCode) inside isolated per-issue [Gondolin](https://github.com/earendil-works/gondolin)
10
+ microVMs over the [Agent Client Protocol](https://agentclientprotocol.com).
7
11
 
8
- The agent signals completion through an injected MCP server (`mark_done`,
9
- `request_human_steering`); the orchestrator handles state, retry, concurrency,
10
- and produces either a pull request or a `git format-patch` bundle per issue.
12
+ The agent signals progress through an injected MCP server (`transition`,
13
+ `request_human_steering`, `propose_issue`); the orchestrator handles state,
14
+ retry, concurrency, and opens a pull request per issue when configured for
15
+ a GitHub remote. In local-only mode the per-issue branch is left in the
16
+ workspace for review.
11
17
 
12
18
  ```
13
19
  ┌──────────────────────────────────────────────────────────────────────────┐
14
20
  │ symphony (node host) │
15
21
  │ │
16
- issues/<state>/*.md ─┐
17
- WORKFLOW.md ─┼──▶ orchestrator ──▶ agent runner
18
- WORKFLOW.template.md │ (poll reconcile dispatch)
19
-
20
- ▼ ACP (JSON-RPC)
21
- ┌──────────────────────────┐ │
22
- smolvm (per-issue VM)
23
- adapter binary │
24
- workspace mount
25
- mcp client ←───────────┼─┼─▶ symphony MCP
26
- │ └──────────────────────────┘ │ mark_done
27
- ▼ │ request_human_steering
28
- HTTP dashboard (HTMX): / │
29
- attention · sessions · on disk · new issue · totals
22
+ ./issues/<state>/*.md ──┐
23
+ ./WORKFLOW.yaml ├──▶ orchestrator ──▶ agent runner
24
+ ./WORKFLOW.template.yaml ──┘ poll · reconcile · dispatch
25
+
26
+ ▼ ACP/RPC
27
+ ┌───────────────────────────────┐
28
+ Gondolin (per-issue VM)
29
+ │ adapter (claude / codex)
30
+ │ workspace mount
31
+ │ mcp client ────────────────┼─┐│
32
+ └───────────────────────────────┘ ││
33
+ ││
34
+ symphony MCP server ◀─────────────────────────────────────┘│
35
+ ( transition · request_human_steering · propose_issue )
36
+ │ │
37
+ │ HTTP dashboard (HTMX): / │
38
+ │ attention · sessions · on disk · new issue · totals │
30
39
  └──────────────────────────────────────────────────────────────────────────┘
31
40
  ```
32
41
 
42
+ `SPEC.md` documents the contracts this repo's code references; for the
43
+ original architectural narrative, see
44
+ [openai/symphony/SPEC.md](https://github.com/openai/symphony/blob/main/SPEC.md).
45
+
33
46
  ## Quick start
34
47
 
35
- Prerequisites:
48
+ The golden path from zero to your first dispatched issue. The details behind
49
+ each step live in [Prerequisites in detail](#prerequisites-in-detail) below — you
50
+ should not need them to get here.
36
51
 
37
- - Node.js ≥ 20.
38
- - A `smolvm` binary on `$PATH` with the server reachable on the configured
39
- endpoint (e.g. `smolvm serve start --listen unix:///run/user/$UID/smolvm.sock`).
40
- - A packed VM image (one-time): `bash scripts/build-vm.sh` produces
41
- `.vm/symphony.smolmachine.smolmachine` (~1.1 GB; ships `claude-agent-acp`,
42
- `codex-acp`, and `opencode` on the guest `$PATH`).
43
- - For the default `acp.adapter: claude`: a credentials file at
44
- `~/.claude/.credentials.json` on the host (symphony reads and stages it; the
45
- host directory is **not** bind-mounted into the VM).
52
+ ```bash
53
+ # 1. Build the agent rootfs image once. This ships only `mise` (jdx/mise) — node +
54
+ # the coding-agent CLIs are mise-installed at dispatch, the launcher is injected —
55
+ # and tags the result `symphony-agents:latest` the tag the scaffolded
56
+ # WORKFLOW.yaml pins, so a first run needs no edit.
57
+ git clone https://github.com/dizk/smol-symphony.git
58
+ cd smol-symphony && npm install && npm run build:image
59
+
60
+ # 2. In your project, scaffold the minimal WORKFLOW.yaml. With no workflow file
61
+ # present, symphony offers to write a ~30-line starter (every key required,
62
+ # no power features), then exits so you can glance at it.
63
+ cd /path/to/your/project
64
+ npx smol-symphony WORKFLOW.yaml
65
+
66
+ # 3. Verify the whole setup in one pass before the first dispatch. `symphony
67
+ # doctor` runs every adoption-prerequisite probe — Node version, the
68
+ # `gondolin.image` pin, each configured adapter's host credential, a
69
+ # writable `tracker.root`, and a bindable dashboard port — and prints a
70
+ # PASS/FAIL line per check with a one-line fix for each failure, exiting 0
71
+ # iff every check passes. Unlike startup (which bails on the first failure),
72
+ # it runs all probes so you see the whole picture at once.
73
+ npx smol-symphony doctor WORKFLOW.yaml
74
+
75
+ # 4. Drop one issue into the tracker.
76
+ mkdir -p issues/Todo
77
+ printf -- '---\ntitle: "Say hi"\n---\nAdd a hi() that returns "hi".\n' > issues/Todo/1.md
78
+
79
+ # 5. Run symphony and watch it dispatch.
80
+ npx smol-symphony WORKFLOW.yaml --port 8787
81
+ # Open http://127.0.0.1:8787/ — issue 1 moves from "on disk" into a running
82
+ # session as symphony boots a per-issue microVM and dispatches the agent.
83
+ ```
46
84
 
47
- Run:
85
+ The same first-run scaffold also installs an issue-filing skill at
86
+ `.claude/skills/symphony-issues/SKILL.md` (best-effort — it never blocks the
87
+ workflow scaffold), so a coding agent in the freshly-onboarded repo can file
88
+ well-formed tracker issues and decompose a large task into a `blocked_by` DAG
89
+ from day one.
90
+
91
+ The scaffolded file ([WORKFLOW.minimal.yaml](./WORKFLOW.minimal.yaml) is the shipped
92
+ copy) runs as-is once `symphony-agents:latest` exists; the only edits a different
93
+ setup needs are `gondolin.image` (if you pinned a build id) and the adapter
94
+ credential for your chosen agent. When you want a power feature it omits — typed
95
+ action DAGs, PR autopilot, per-state adapters, egress allowlists — graduate to
96
+ the annotated [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).
97
+
98
+ (Or `npm i -g smol-symphony` and then `symphony doctor` / `symphony WORKFLOW.yaml`
99
+ to skip the `npx` fetch — both invoke the `symphony` bin shipped in this
100
+ package.)
101
+
102
+ ### Prerequisites in detail
103
+
104
+ - **Node.js ≥ 20.**
105
+ - **The agent rootfs image** (step 1). Gondolin is the in-process microVM
106
+ substrate (`@earendil-works/gondolin`), so there is no separate VM daemon to
107
+ run. The image is built **once** from `images/agents/` via
108
+ `npm run build:image`: it starts from `node:24-bookworm-slim`, installs base
109
+ CLI tooling, and ships only the `mise` (jdx/mise) binary. node + every
110
+ ACP-capable coding agent (`claude-agent-acp`, `codex-acp`, …) are
111
+ NO LONGER baked — they come from a mise SYSTEM config symphony stages into the
112
+ guest at dispatch (`assets/symphony-mise.system.toml`) and installs via
113
+ `mise install` into a warm-cached data dir; a consuming repo's own `mise.toml`
114
+ adds project toolchains (rust, go, kotlin+gradle, …) on top. The in-VM stdio
115
+ launcher is likewise injected at dispatch, not baked. So bumping an agent CLI is
116
+ a one-line edit to the staged mise config + a restart — no image rebuild + repin.
117
+ The build prints a content-addressed build id; pin it (or the
118
+ `symphony-agents:latest` tag) in `WORKFLOW.yaml`'s `gondolin.image`. See
119
+ [images/agents/README.md](./images/agents/README.md) for the build steps and
120
+ requirements, and `gondolin.mise` in
121
+ [WORKFLOW.template.yaml](./WORKFLOW.template.yaml) to tune the data dir or opt out.
122
+ - **An adapter credential.** For the default `acp.adapter: claude`: a
123
+ credentials file at `~/.claude/.credentials.json` on the host. Symphony reads
124
+ it only on the host side: the guest holds only a token-shaped placeholder, and
125
+ the host substitutes the real OAuth access token into the outbound request at
126
+ Gondolin egress (TLS-MITM). The credential file itself is never staged into
127
+ the VM. (For `acp.adapter: codex`, see [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).)
128
+ - **Skipping the `npx` fetch.** `npm i -g smol-symphony` installs the `symphony`
129
+ bin globally so `symphony WORKFLOW.yaml` works without re-fetching; the image
130
+ build still needs a checkout (it lives in `images/agents/`).
131
+ - **Console output.** With the default log file configured, symphony prints a
132
+ one-line-per-field startup banner (workflow, tracker root, dashboard URL,
133
+ log-file path) and routes the structured `key=value` stream to
134
+ `.symphony/logs/symphony.log` only. `tail -f` that file to follow the detail.
135
+ Pass `--verbose` (alias `--foreground`) to mirror the structured stream back
136
+ to the console for interactive debugging. With no log file configured (the
137
+ `SYMPHONY_LOG_FILE=""` override), the structured stream stays on stderr.
138
+
139
+ ### From a checkout
140
+
141
+ If you're hacking on symphony itself:
48
142
 
49
143
  ```bash
144
+ git clone https://github.com/dizk/smol-symphony.git
145
+ cd smol-symphony
50
146
  npm install
51
147
  npm run build
52
- npx symphony WORKFLOW.md
148
+ npx symphony WORKFLOW.yaml # the local bin
53
149
  ```
54
150
 
55
- Open the dashboard at `http://127.0.0.1:8787/`. Drop issues into
56
- `issues/Todo/` from the filesystem or the dashboard's `new issue` form;
57
- symphony dispatches them on the next poll.
151
+ `npm run dev` (via `tsx watch`) reruns on source edits.
58
152
 
59
153
  ## Local Markdown tracker
60
154
 
61
155
  Issues live as `.md` files under `tracker.root`. The parent directory is the
62
- issue state.
156
+ issue state; the set of valid state directories comes from the `states:` block
157
+ in `WORKFLOW.yaml` (see below) and is auto-mkdir'd on startup.
63
158
 
64
159
  ```
65
160
  issues/
66
161
  ├── Todo/
67
- │ ├── ABC-1.md
68
- │ └── ABC-2.md
69
- ├── In Progress/
70
- │ └── ABC-3.md
71
- └── Done/
72
- └── ABC-4.md
162
+ │ ├── 1.md
163
+ │ └── 2.md
164
+ ├── Review/
165
+ │ └── 3.md
166
+ ├── Done/
167
+ └── 4.md
168
+ └── Triage/
169
+ └── 5.md
73
170
  ```
74
171
 
172
+ The basename is the issue identifier. When a caller (dashboard form, MCP
173
+ `propose_issue`) omits an explicit identifier, the tracker picks the next free
174
+ positive integer by scanning every state directory under `tracker.root`.
175
+ Operator-supplied identifiers (e.g. `CACHE-7.md`) pass through unchanged and
176
+ coexist with the numeric ones.
177
+
75
178
  Each file has YAML front matter and an optional body:
76
179
 
77
180
  ```markdown
@@ -79,28 +182,40 @@ Each file has YAML front matter and an optional body:
79
182
  title: "Fix the login bug"
80
183
  priority: 2
81
184
  labels: [bug, auth]
82
- blocked_by: [ABC-5]
185
+ blocked_by: [5]
83
186
  ---
84
187
  Long-form description in the body.
85
188
  ```
86
189
 
87
190
  State comparison is case-insensitive. Moving the file between state
88
191
  directories is the canonical state transition; the orchestrator does this
89
- itself in response to `mark_done`. The agent inside the VM does **not** have
90
- filesystem access to the tracker root: it signals completion through the
91
- MCP server and the orchestrator does the file move.
92
-
93
- ## WORKFLOW.md
94
-
95
- `WORKFLOW.md` is a YAML front matter block (orchestrator config) plus a
96
- [Liquid](https://liquidjs.com/)-templated prompt body. The shipped file in
97
- this repo is the canonical project workflow; see
98
- [WORKFLOW.template.md](./WORKFLOW.template.md) for the annotated reference
192
+ itself in response to `symphony.transition`. The agent inside the VM does
193
+ **not** have filesystem access to the tracker root: it signals progress
194
+ through the MCP server and the orchestrator does the file move.
195
+
196
+ ## WORKFLOW.yaml
197
+
198
+ `WORKFLOW.yaml` is a pure YAML config file (the orchestrator config). Each
199
+ active state's prompt lives in its own `prompt_file` — a
200
+ [Liquid](https://liquidjs.com/)-templated file under `prompts/`, wrapped in the
201
+ shared `prompt.preamble_file`/`footer_file`. The shipped file in this repo is
202
+ the canonical project workflow; see
203
+ [WORKFLOW.template.yaml](./WORKFLOW.template.yaml) for the annotated reference
99
204
  covering every supported option, its type, default, and example.
100
205
 
101
- Symphony watches the file and re-applies poll interval, concurrency, hooks,
102
- prompt body, smolvm settings, etc. on change without restart. In-flight runs
103
- keep the settings they started with.
206
+ The workflow is a **state machine**. A required top-level `states:` block
207
+ declares every state an issue can occupy, its `role` (`active` — dispatched;
208
+ `terminal` triggers cleanup and handoff; `holding` — sits outside the
209
+ dispatch loop, e.g. `Triage`), and optional per-state `adapter`, `model`,
210
+ `max_turns`, and `allowed_transitions` overrides. A single issue can travel
211
+ through any number of states with distinct adapters and instructions; each
212
+ active state names its own `prompt_file`, so the agent in each state sees only
213
+ that state's instructions plus the shared preamble/footer. The shipped workflow
214
+ uses a two-stage `Todo → Review → Done` flow (Claude implements, Codex reviews).
215
+
216
+ Symphony watches the file and its prompt files, and re-applies poll interval,
217
+ concurrency, typed actions, prompts, gondolin settings, etc. on change without
218
+ restart. In-flight runs keep the settings they started with.
104
219
 
105
220
  ## Dashboard
106
221
 
@@ -130,37 +245,47 @@ API clients. CSRF-relevant content types (`text/plain`,
130
245
 
131
246
  Symphony injects an MCP server into each ACP session at
132
247
  `http://<host>:<bound-port>/api/v1/issues/<id>/mcp`, gated by a per-dispatch
133
- bearer token. Two tools:
134
-
135
- - **`symphony.mark_done({ title, summary })`** — call once at end of a
136
- successful run. `title` is a single-line imperative summary (≤72 chars);
137
- `summary` is a one- to three-paragraph narrative. The orchestrator
138
- atomically moves the issue file to the terminal state and stops
139
- dispatching. The pair lands in
140
- `<workspace>/.git/symphony-runtime/mark_done.md` (or
141
- `<workspace>/.symphony-runtime/mark_done.md` when the workspace doesn't
142
- have its own `.git/`) for the `after_run` hook to consume.
248
+ bearer token. Three tools:
249
+
250
+ - **`symphony.transition({ to_state, notes? })`** — canonical (and only)
251
+ exit verb. Moves the issue into another declared state, optionally
252
+ appending markdown `notes` to the issue body before the move so the next
253
+ agent (in `to_state`) reads them as part of `issue.description`. A
254
+ transition into a `role: terminal` state ends the run and triggers
255
+ workspace cleanup; transitions between active/holding states preserve the
256
+ workspace so the same `agent/<id>` git branch survives the handoff.
257
+ Rejected transitions (unknown target, disallowed by
258
+ `allowed_transitions`) return MCP tool-result errors the agent can read
259
+ and retry.
143
260
  - **`symphony.request_human_steering({ question, context? })`** — call
144
261
  when blocked on something only a human can answer. The turn ends
145
262
  immediately; the human's reply arrives as the prompt for the next turn.
146
263
  Steering-reply turns don't count against `agent.max_turns`.
147
-
148
- In smolvm, the VM's `127.0.0.1` transparently reaches the host's
264
+ - **`symphony.propose_issue({ title, description?, labels?, priority? })`** —
265
+ call when the agent notices work that is out of scope for its current
266
+ task. The proposal lands in the `Triage/` state directory, which the
267
+ orchestrator does **not** dispatch; the operator approves (→ first active
268
+ state) or discards (→ first terminal state, prefers `Cancelled`) from the
269
+ dashboard. The calling issue's identifier and a timestamp are stamped into
270
+ the proposal's front-matter as `proposed_by` / `proposed_at` so provenance
271
+ is visible.
272
+
273
+ In Gondolin, the VM's `127.0.0.1` transparently reaches the host's
149
274
  `127.0.0.1` (verified empirically), so the agent reaches the orchestrator
150
275
  without any mount or special host alias.
151
276
 
152
277
  ## ACP — adapter registry
153
278
 
154
279
  One ACP client (symphony's `agent/acp.ts`), two shipped adapter profiles.
155
- Each profile encodes the binary symphony launches and the host credential
156
- file it stages into the workspace before exec:
280
+ Each profile encodes the binary symphony launches and the credential path
281
+ the adapter reaches for inside the VM:
157
282
 
158
- | Adapter | Binary | Host credential file |
283
+ | Adapter | Binary | Credential surface |
159
284
  | --------- | ------------------ | --------------------------------- |
160
- | `claude` | `claude-agent-acp` | `~/.claude/.credentials.json` |
161
- | `codex` | `codex-acp` | `~/.codex/auth.json` |
285
+ | `claude` | `claude-agent-acp` | placeholder + host egress swap |
286
+ | `codex` | `codex-acp` | placeholder + host egress swap |
162
287
 
163
- `WORKFLOW.md`:
288
+ `WORKFLOW.yaml`:
164
289
 
165
290
  ```yaml
166
291
  acp:
@@ -171,44 +296,64 @@ acp:
171
296
  stall_timeout_ms: 300000
172
297
  ```
173
298
 
174
- Selecting an adapter is enough — symphony auto-derives the launch command
175
- that stages the credential into the workspace's runtime dir and copies it
176
- into the adapter's expected guest path before exec. Set `command:` only to
177
- override (testing a forked adapter, a non-standard binary path); doing so
178
- opts out of automatic credential staging.
179
-
180
- Credentials are **never bind-mounted from the host**. Symphony copies the
181
- single credential file into a per-workspace location (under `.git/` when
182
- the workspace has its own clone, else `.symphony-runtime/`) and refuses to
183
- operate on workspaces inside the credential file's ancestor repo.
184
-
185
- ## After-run handoff: PR or patch
186
-
187
- `WORKFLOW.md`'s `after_run` hook ships in two modes:
188
-
189
- - **Pull request mode.** Triggered when `SYMPHONY_REPO=<owner>/<repo>` is
190
- exported. The hook pushes the per-issue branch to GitHub and runs
191
- `gh pr create --base $SYMPHONY_BASE_BRANCH ...`. Requires `gh auth status`
192
- to be clean on the host. The token never enters the VM.
193
- - **Patch bundle mode** (default). Writes
194
- `.symphony/patches/<branch>.patch` via `git format-patch` so you can
195
- review and apply with `git am`. No remote required.
196
-
197
- The agent's `mark_done.md` provides the PR title/body or commit message; the
198
- hook reads it from the workspace's runtime dir.
199
-
200
- See [AGENTS.md](./AGENTS.md) for the env-var contract and switch-over
201
- commands.
299
+ Selecting an adapter is enough — symphony auto-derives the launch command.
300
+ For `claude`, a per-VM identity file (organization + account UUIDs only,
301
+ no tokens) is staged into the workspace runtime dir and copied to
302
+ `~/.claude.json` inside the VM; the guest holds only a token-shaped
303
+ placeholder, and the host substitutes the real access token into the
304
+ outbound request at Gondolin egress. For `codex`, no identity file is
305
+ staged (OpenAI ships no third-party fingerprint check); the VM is launched
306
+ with `OPENAI_API_KEY=<placeholder>`, and the host substitutes the real
307
+ OpenAI credential at egress. Set `command:` only to override (testing a
308
+ forked adapter, a non-standard binary path).
309
+
310
+ Credentials **never enter the VM** for either adapter. For `claude`, the
311
+ host's `~/.claude/.credentials.json` is read only on the host side; the VM
312
+ sees `~/.claude.json` (identity-only) plus the placeholder value in its
313
+ `Authorization` header, which the host swaps for the real token at egress.
314
+ For `codex`, the host's `~/.codex/auth.json` (access token /
315
+ `OPENAI_API_KEY`, **never** the refresh token) is read only host-side; the
316
+ real `OPENAI_API_KEY` is stripped from the forwarded VM boot env, so the VM
317
+ holds only the placeholder.
318
+
319
+ ## After-run handoff: pull request
320
+
321
+ On transition into the Done state the orchestrator runs that state's typed
322
+ `actions:` block two records, `push_branch` then `create_pr_if_missing`
323
+ which push the per-issue branch and open a PR when a GitHub repo is configured:
324
+ set `workspace.github_repo: <owner>/<repo>` in `WORKFLOW.yaml` (or export
325
+ `SYMPHONY_REPO=<owner>/<repo>`, which overrides the file). Each action's
326
+ `if: $repo` predicate short-circuits to a no-op in local-only mode (neither
327
+ set), so the branch is simply left in the workspace until cleanup; pick the
328
+ commits up with `git log agent/<id>` against your local clone. The PR is opened with `gh pr create --base
329
+ $base_branch ...`, which requires `gh auth status` to be clean on the host; the
330
+ token never enters the VM.
331
+
332
+ The orchestrator stages the action context — `$branch`, `$base_branch`,
333
+ `$pr_title` (already id-prefixed), and `$pr_body_file` (a temp file holding the
334
+ current issue body) — before the block fires, so the PR description carries
335
+ every `symphony.transition` notes block accumulated across the run, the full
336
+ handoff thread from implementer through reviewer to approval. Per-action retry
337
+ and snapshot plumbing replaces the old opaque shell-exit surface: on a
338
+ rate-limit the `create_pr_if_missing` action surfaces "retrying in 60s" on the
339
+ dashboard rather than failing silently.
340
+
341
+ See `states.Done.actions` in [WORKFLOW.yaml](./WORKFLOW.yaml) for the canonical
342
+ record pair and [WORKFLOW.template.yaml](./WORKFLOW.template.yaml) for the
343
+ typed-action reference.
202
344
 
203
345
  ## Trust posture
204
346
 
205
- Sandbox isolation comes from running each agent inside a smolvm microVM.
206
- The VM has no network credentials (only the agent's API key is forwarded
207
- via `smolvm.forward_env`), no tracker filesystem access (the tracker is
208
- reached only through the MCP server), and stripped git remotes (set by
209
- `after_create`).
347
+ Sandbox isolation comes from running each agent inside a Gondolin microVM.
348
+ The VM has no OAuth refresh tokens or long-lived access tokens: for both
349
+ `claude` and `codex`, the guest holds only a token-shaped placeholder and
350
+ the host substitutes the real credential into the outbound request at
351
+ Gondolin egress, so no real Anthropic or OpenAI credential is present in
352
+ the VM. The VM has no tracker filesystem access (the tracker is reached
353
+ only through the MCP server) and stripped git remotes (set by the
354
+ orchestrator's `setupWorkspaceDir`).
210
355
 
211
- Within the ACP session, the orchestrator follows SPEC §10.5's "high-trust"
356
+ Within the ACP session, the orchestrator follows SPEC §6.1's "high-trust"
212
357
  posture:
213
358
 
214
359
  - Command execution and file change approvals: auto-approve.
@@ -220,12 +365,16 @@ posture:
220
365
 
221
366
  ```bash
222
367
  npm run typecheck # tsc --noEmit
223
- npm test # 67 tests across workflow, tracker, prompt, workspace,
224
- # adapters, http, and mcp surfaces
368
+ npm test # 170 tests across workflow, tracker, prompt, workspace,
369
+ # adapters, http, mcp, acp-bridge, orchestrator, run log,
370
+ # runner state resolution, and tool-call summary surfaces
225
371
  npm run build # tsc emit to dist/
226
372
  ```
227
373
 
228
- An end-to-end smoke run needs a real smolvm + VM image.
374
+ An end-to-end smoke run needs the built agent image (see `images/agents/`).
375
+
376
+ See [CHANGELOG.md](./CHANGELOG.md) for operator-visible changes between
377
+ releases.
229
378
 
230
379
  ## License
231
380