smol-symphony 0.2.0 → 0.3.1

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 (540) 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/bin/symphony.js +22 -786
  11. package/dist/bin/symphony.js.map +1 -1
  12. package/dist/core/actions/context.js +109 -0
  13. package/dist/core/actions/context.js.map +1 -0
  14. package/dist/{actions/parsing.js → core/actions/parse.js} +33 -114
  15. package/dist/core/actions/parse.js.map +1 -0
  16. package/dist/core/actions/plan.js +197 -0
  17. package/dist/core/actions/plan.js.map +1 -0
  18. package/dist/core/actions/predicates.js +111 -0
  19. package/dist/core/actions/predicates.js.map +1 -0
  20. package/dist/core/actions/run-fold.js +248 -0
  21. package/dist/core/actions/run-fold.js.map +1 -0
  22. package/dist/core/actions/template.js +118 -0
  23. package/dist/core/actions/template.js.map +1 -0
  24. package/dist/core/cli/args.js +116 -0
  25. package/dist/core/cli/args.js.map +1 -0
  26. package/dist/core/coerce.js +75 -0
  27. package/dist/core/coerce.js.map +1 -0
  28. package/dist/core/credential/account-id.js +20 -0
  29. package/dist/core/credential/account-id.js.map +1 -0
  30. package/dist/core/credential/adapter-config.js +136 -0
  31. package/dist/core/credential/adapter-config.js.map +1 -0
  32. package/dist/core/credential/availability.js +98 -0
  33. package/dist/core/credential/availability.js.map +1 -0
  34. package/dist/core/credential/extract.js +228 -0
  35. package/dist/core/credential/extract.js.map +1 -0
  36. package/dist/core/credential/fake-creds.js +171 -0
  37. package/dist/core/credential/fake-creds.js.map +1 -0
  38. package/dist/core/credential/identity.js +125 -0
  39. package/dist/core/credential/identity.js.map +1 -0
  40. package/dist/core/credential/shape.js +230 -0
  41. package/dist/core/credential/shape.js.map +1 -0
  42. package/dist/core/credential/strings.js +15 -0
  43. package/dist/core/credential/strings.js.map +1 -0
  44. package/dist/core/doctor/checks.js +303 -0
  45. package/dist/core/doctor/checks.js.map +1 -0
  46. package/dist/core/git/result.js +107 -0
  47. package/dist/core/git/result.js.map +1 -0
  48. package/dist/core/http/decisions.js +225 -0
  49. package/dist/core/http/decisions.js.map +1 -0
  50. package/dist/{http.js → core/http/render.js} +472 -738
  51. package/dist/core/http/render.js.map +1 -0
  52. package/dist/{http-handlers.js → core/http/routes.js} +52 -87
  53. package/dist/core/http/routes.js.map +1 -0
  54. package/dist/core/http/views.js +181 -0
  55. package/dist/core/http/views.js.map +1 -0
  56. package/dist/core/image/managed-image.js +95 -0
  57. package/dist/core/image/managed-image.js.map +1 -0
  58. package/dist/core/issue/file.js +149 -0
  59. package/dist/core/issue/file.js.map +1 -0
  60. package/dist/core/issue/parse.js +210 -0
  61. package/dist/core/issue/parse.js.map +1 -0
  62. package/dist/core/mcp/dispatch.js +239 -0
  63. package/dist/core/mcp/dispatch.js.map +1 -0
  64. package/dist/core/mcp/post-move.js +92 -0
  65. package/dist/core/mcp/post-move.js.map +1 -0
  66. package/dist/core/mcp/protocol.js +293 -0
  67. package/dist/core/mcp/protocol.js.map +1 -0
  68. package/dist/core/mcp/url.js +162 -0
  69. package/dist/core/mcp/url.js.map +1 -0
  70. package/dist/core/path.js +63 -0
  71. package/dist/core/path.js.map +1 -0
  72. package/dist/core/reconcile/image-decide.js +48 -0
  73. package/dist/core/reconcile/image-decide.js.map +1 -0
  74. package/dist/core/reconcile/ledger.js +142 -0
  75. package/dist/core/reconcile/ledger.js.map +1 -0
  76. package/dist/core/reconcile/pr-classify.js +62 -0
  77. package/dist/core/reconcile/pr-classify.js.map +1 -0
  78. package/dist/{reconciler → core/reconcile}/pr-decide.js +25 -12
  79. package/dist/core/reconcile/pr-decide.js.map +1 -0
  80. package/dist/core/reconcile/pr-loop.js +161 -0
  81. package/dist/core/reconcile/pr-loop.js.map +1 -0
  82. package/dist/core/reconcile/pr-notes.js +35 -0
  83. package/dist/core/reconcile/pr-notes.js.map +1 -0
  84. package/dist/core/reconcile/vm-decide.js +70 -0
  85. package/dist/core/reconcile/vm-decide.js.map +1 -0
  86. package/dist/core/reconcile/vm-reap.js +207 -0
  87. package/dist/core/reconcile/vm-reap.js.map +1 -0
  88. package/dist/core/reconcile/workspace-decide.js +162 -0
  89. package/dist/core/reconcile/workspace-decide.js.map +1 -0
  90. package/dist/core/runlog/summary.js +231 -0
  91. package/dist/core/runlog/summary.js.map +1 -0
  92. package/dist/core/runner/dispatch-config.js +95 -0
  93. package/dist/core/runner/dispatch-config.js.map +1 -0
  94. package/dist/core/runner/injection.js +61 -0
  95. package/dist/core/runner/injection.js.map +1 -0
  96. package/dist/core/runner/mise.js +210 -0
  97. package/dist/core/runner/mise.js.map +1 -0
  98. package/dist/core/runner/prompt.js +720 -0
  99. package/dist/core/runner/prompt.js.map +1 -0
  100. package/dist/core/runner/turn.js +242 -0
  101. package/dist/core/runner/turn.js.map +1 -0
  102. package/dist/core/runner/vm-plan.js +390 -0
  103. package/dist/core/runner/vm-plan.js.map +1 -0
  104. package/dist/core/schedule/admission.js +123 -0
  105. package/dist/core/schedule/admission.js.map +1 -0
  106. package/dist/core/schedule/circuit-breaker.js +111 -0
  107. package/dist/core/schedule/circuit-breaker.js.map +1 -0
  108. package/dist/core/schedule/eligibility.js +83 -0
  109. package/dist/core/schedule/eligibility.js.map +1 -0
  110. package/dist/core/schedule/reconcile-issue.js +82 -0
  111. package/dist/core/schedule/reconcile-issue.js.map +1 -0
  112. package/dist/core/schedule/retry.js +96 -0
  113. package/dist/core/schedule/retry.js.map +1 -0
  114. package/dist/core/schedule/sleep-cycle.js +133 -0
  115. package/dist/core/schedule/sleep-cycle.js.map +1 -0
  116. package/dist/core/schedule/slots.js +124 -0
  117. package/dist/core/schedule/slots.js.map +1 -0
  118. package/dist/core/schedule/tick.js +553 -0
  119. package/dist/core/schedule/tick.js.map +1 -0
  120. package/dist/core/schedule/token-fold.js +181 -0
  121. package/dist/core/schedule/token-fold.js.map +1 -0
  122. package/dist/core/state-resolve.js +86 -0
  123. package/dist/core/state-resolve.js.map +1 -0
  124. package/dist/core/vm-guards.js +278 -0
  125. package/dist/core/vm-guards.js.map +1 -0
  126. package/dist/core/workflow/derive.js +107 -0
  127. package/dist/core/workflow/derive.js.map +1 -0
  128. package/dist/core/workflow/parse.js +687 -0
  129. package/dist/core/workflow/parse.js.map +1 -0
  130. package/dist/core/workflow/prompt-probe.js +78 -0
  131. package/dist/core/workflow/prompt-probe.js.map +1 -0
  132. package/dist/core/workflow/validate.js +189 -0
  133. package/dist/core/workflow/validate.js.map +1 -0
  134. package/dist/core/workspace-key.js +19 -0
  135. package/dist/core/workspace-key.js.map +1 -0
  136. package/dist/shell/actions-runner.js +356 -0
  137. package/dist/shell/actions-runner.js.map +1 -0
  138. package/dist/shell/adapter/adapter-registry.js +45 -0
  139. package/dist/shell/adapter/adapter-registry.js.map +1 -0
  140. package/dist/shell/adapter/clock-random.js +96 -0
  141. package/dist/shell/adapter/clock-random.js.map +1 -0
  142. package/dist/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  143. package/dist/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  144. package/dist/shell/adapter/gondolin-dispatch.js +385 -0
  145. package/dist/shell/adapter/gondolin-dispatch.js.map +1 -0
  146. package/dist/shell/adapter/gondolin-image-converter.js +233 -0
  147. package/dist/shell/adapter/gondolin-image-converter.js.map +1 -0
  148. package/dist/shell/adapter/gondolin-image-fetch.js +180 -0
  149. package/dist/shell/adapter/gondolin-image-fetch.js.map +1 -0
  150. package/dist/shell/adapter/launcher-asset.js +57 -0
  151. package/dist/shell/adapter/launcher-asset.js.map +1 -0
  152. package/dist/shell/adapter/mise-config-asset.js +65 -0
  153. package/dist/shell/adapter/mise-config-asset.js.map +1 -0
  154. package/dist/shell/adapter/workflow-loader.js +304 -0
  155. package/dist/shell/adapter/workflow-loader.js.map +1 -0
  156. package/dist/shell/cli/doctor.js +268 -0
  157. package/dist/shell/cli/doctor.js.map +1 -0
  158. package/dist/shell/effect-interpreter-families.js +314 -0
  159. package/dist/shell/effect-interpreter-families.js.map +1 -0
  160. package/dist/shell/effect-interpreter.js +29 -0
  161. package/dist/shell/effect-interpreter.js.map +1 -0
  162. package/dist/shell/interp/acp-frame.js +137 -0
  163. package/dist/shell/interp/acp-frame.js.map +1 -0
  164. package/dist/shell/interp/acp-ws-conn.js +320 -0
  165. package/dist/shell/interp/acp-ws-conn.js.map +1 -0
  166. package/dist/shell/interp/acp-ws-frames.js +159 -0
  167. package/dist/shell/interp/acp-ws-frames.js.map +1 -0
  168. package/dist/shell/interp/acp-ws.js +197 -0
  169. package/dist/shell/interp/acp-ws.js.map +1 -0
  170. package/dist/shell/interp/acp.js +319 -0
  171. package/dist/shell/interp/acp.js.map +1 -0
  172. package/dist/shell/interp/credential-defaults.js +128 -0
  173. package/dist/shell/interp/credential-defaults.js.map +1 -0
  174. package/dist/shell/interp/credential-hooks.js +149 -0
  175. package/dist/shell/interp/credential-hooks.js.map +1 -0
  176. package/dist/shell/interp/credential-registry.js +226 -0
  177. package/dist/shell/interp/credential-registry.js.map +1 -0
  178. package/dist/shell/interp/credential.js +103 -0
  179. package/dist/shell/interp/credential.js.map +1 -0
  180. package/dist/shell/interp/gh.js +163 -0
  181. package/dist/shell/interp/gh.js.map +1 -0
  182. package/dist/shell/interp/git.js +28 -0
  183. package/dist/shell/interp/git.js.map +1 -0
  184. package/dist/shell/interp/log.js +213 -0
  185. package/dist/shell/interp/log.js.map +1 -0
  186. package/dist/shell/interp/process.js +178 -0
  187. package/dist/shell/interp/process.js.map +1 -0
  188. package/dist/shell/interp/runlog.js +193 -0
  189. package/dist/shell/interp/runlog.js.map +1 -0
  190. package/dist/shell/interp/timer.js +64 -0
  191. package/dist/shell/interp/timer.js.map +1 -0
  192. package/dist/shell/interp/tracker-disk.js +99 -0
  193. package/dist/shell/interp/tracker-disk.js.map +1 -0
  194. package/dist/shell/interp/tracker-parse.js +71 -0
  195. package/dist/shell/interp/tracker-parse.js.map +1 -0
  196. package/dist/shell/interp/tracker-scan.js +238 -0
  197. package/dist/shell/interp/tracker-scan.js.map +1 -0
  198. package/dist/shell/interp/tracker-write.js +91 -0
  199. package/dist/shell/interp/tracker-write.js.map +1 -0
  200. package/dist/shell/interp/tracker.js +41 -0
  201. package/dist/shell/interp/tracker.js.map +1 -0
  202. package/dist/shell/interp/tty.js +48 -0
  203. package/dist/shell/interp/tty.js.map +1 -0
  204. package/dist/shell/interp/vm.js +199 -0
  205. package/dist/shell/interp/vm.js.map +1 -0
  206. package/dist/shell/interp/workspace.js +310 -0
  207. package/dist/shell/interp/workspace.js.map +1 -0
  208. package/dist/shell/main-acp.js +78 -0
  209. package/dist/shell/main-acp.js.map +1 -0
  210. package/dist/shell/main-adapters.js +222 -0
  211. package/dist/shell/main-adapters.js.map +1 -0
  212. package/dist/shell/main-credential.js +122 -0
  213. package/dist/shell/main-credential.js.map +1 -0
  214. package/dist/shell/main-doctor.js +22 -0
  215. package/dist/shell/main-doctor.js.map +1 -0
  216. package/dist/shell/main-entry.js +46 -0
  217. package/dist/shell/main-entry.js.map +1 -0
  218. package/dist/shell/main-http-csrf.js +45 -0
  219. package/dist/shell/main-http-csrf.js.map +1 -0
  220. package/dist/shell/main-http-handler.js +389 -0
  221. package/dist/shell/main-http-handler.js.map +1 -0
  222. package/dist/shell/main-http-mcp.js +122 -0
  223. package/dist/shell/main-http-mcp.js.map +1 -0
  224. package/dist/shell/main-http-views.js +253 -0
  225. package/dist/shell/main-http-views.js.map +1 -0
  226. package/dist/shell/main-http.js +76 -0
  227. package/dist/shell/main-http.js.map +1 -0
  228. package/dist/shell/main-loops.js +130 -0
  229. package/dist/shell/main-loops.js.map +1 -0
  230. package/dist/shell/main-mcp.js +129 -0
  231. package/dist/shell/main-mcp.js.map +1 -0
  232. package/dist/shell/main-orchestrator.js +120 -0
  233. package/dist/shell/main-orchestrator.js.map +1 -0
  234. package/dist/shell/main-preflight.js +43 -0
  235. package/dist/shell/main-preflight.js.map +1 -0
  236. package/dist/shell/main-reconcilers-helpers.js +244 -0
  237. package/dist/shell/main-reconcilers-helpers.js.map +1 -0
  238. package/dist/shell/main-reconcilers-pr.js +148 -0
  239. package/dist/shell/main-reconcilers-pr.js.map +1 -0
  240. package/dist/shell/main-reconcilers.js +225 -0
  241. package/dist/shell/main-reconcilers.js.map +1 -0
  242. package/dist/shell/main-runner.js +355 -0
  243. package/dist/shell/main-runner.js.map +1 -0
  244. package/dist/shell/main-scaffold.js +116 -0
  245. package/dist/shell/main-scaffold.js.map +1 -0
  246. package/dist/shell/main-shutdown.js +115 -0
  247. package/dist/shell/main-shutdown.js.map +1 -0
  248. package/dist/shell/main-startup.js +48 -0
  249. package/dist/shell/main-startup.js.map +1 -0
  250. package/dist/shell/main-substrates.js +43 -0
  251. package/dist/shell/main-substrates.js.map +1 -0
  252. package/dist/shell/main.js +385 -0
  253. package/dist/shell/main.js.map +1 -0
  254. package/dist/shell/orchestrator-feedback.js +69 -0
  255. package/dist/shell/orchestrator-feedback.js.map +1 -0
  256. package/dist/shell/orchestrator-image.js +167 -0
  257. package/dist/shell/orchestrator-image.js.map +1 -0
  258. package/dist/shell/orchestrator-loop.js +468 -0
  259. package/dist/shell/orchestrator-loop.js.map +1 -0
  260. package/dist/shell/orchestrator-reconcile.js +36 -0
  261. package/dist/shell/orchestrator-reconcile.js.map +1 -0
  262. package/dist/shell/reconciler-loop.js +228 -0
  263. package/dist/shell/reconciler-loop.js.map +1 -0
  264. package/dist/shell/runner-loop-turn.js +301 -0
  265. package/dist/shell/runner-loop-turn.js.map +1 -0
  266. package/dist/shell/runner-loop.js +338 -0
  267. package/dist/shell/runner-loop.js.map +1 -0
  268. package/dist/shell/server/http.js +208 -0
  269. package/dist/shell/server/http.js.map +1 -0
  270. package/dist/shell/server/mcp-runtime-effects.js +237 -0
  271. package/dist/shell/server/mcp-runtime-effects.js.map +1 -0
  272. package/dist/shell/server/mcp-runtime.js +99 -0
  273. package/dist/shell/server/mcp-runtime.js.map +1 -0
  274. package/dist/shell/workspace-key.js +14 -0
  275. package/dist/shell/workspace-key.js.map +1 -0
  276. package/dist/types/acp.js +8 -0
  277. package/dist/types/acp.js.map +1 -0
  278. package/dist/types/actions/plan.js +6 -0
  279. package/dist/types/actions/plan.js.map +1 -0
  280. package/dist/types/actions/predicates.js +6 -0
  281. package/dist/types/actions/predicates.js.map +1 -0
  282. package/dist/types/actions/run-fold.js +8 -0
  283. package/dist/types/actions/run-fold.js.map +1 -0
  284. package/dist/types/actions.js +7 -0
  285. package/dist/types/actions.js.map +1 -0
  286. package/dist/types/adapter/clock-random.js +4 -0
  287. package/dist/types/adapter/clock-random.js.map +1 -0
  288. package/dist/types/adapter/gondolin-image-converter.js +5 -0
  289. package/dist/types/adapter/gondolin-image-converter.js.map +1 -0
  290. package/dist/types/adapter/gondolin-image-fetch.js +5 -0
  291. package/dist/types/adapter/gondolin-image-fetch.js.map +1 -0
  292. package/dist/types/adapter/workflow-loader.js +4 -0
  293. package/dist/types/adapter/workflow-loader.js.map +1 -0
  294. package/dist/types/cli/args.js +8 -0
  295. package/dist/types/cli/args.js.map +1 -0
  296. package/dist/types/config.js +8 -0
  297. package/dist/types/config.js.map +1 -0
  298. package/dist/types/credential-interp.js +6 -0
  299. package/dist/types/credential-interp.js.map +1 -0
  300. package/dist/types/credentials.js +10 -0
  301. package/dist/types/credentials.js.map +1 -0
  302. package/dist/types/doctor.js +7 -0
  303. package/dist/types/doctor.js.map +1 -0
  304. package/dist/types/domain.js +7 -0
  305. package/dist/types/domain.js.map +1 -0
  306. package/dist/types/effect.js +15 -0
  307. package/dist/types/effect.js.map +1 -0
  308. package/dist/types/errors.js +39 -0
  309. package/dist/types/errors.js.map +1 -0
  310. package/dist/types/http/decisions.js +6 -0
  311. package/dist/types/http/decisions.js.map +1 -0
  312. package/dist/types/http/render.js +10 -0
  313. package/dist/types/http/render.js.map +1 -0
  314. package/dist/types/http/views.js +6 -0
  315. package/dist/types/http/views.js.map +1 -0
  316. package/dist/types/http.js +9 -0
  317. package/dist/types/http.js.map +1 -0
  318. package/dist/types/image/managed-image.js +7 -0
  319. package/dist/types/image/managed-image.js.map +1 -0
  320. package/dist/types/interp/effect-interpreter.js +8 -0
  321. package/dist/types/interp/effect-interpreter.js.map +1 -0
  322. package/dist/types/interp/tracker.js +7 -0
  323. package/dist/types/interp/tracker.js.map +1 -0
  324. package/dist/types/issue/file.js +6 -0
  325. package/dist/types/issue/file.js.map +1 -0
  326. package/dist/types/issue/parse.js +8 -0
  327. package/dist/types/issue/parse.js.map +1 -0
  328. package/dist/types/main-acp.js +13 -0
  329. package/dist/types/main-acp.js.map +1 -0
  330. package/dist/types/main-adapters.js +5 -0
  331. package/dist/types/main-adapters.js.map +1 -0
  332. package/dist/types/main-credential.js +21 -0
  333. package/dist/types/main-credential.js.map +1 -0
  334. package/dist/types/main-doctor.js +6 -0
  335. package/dist/types/main-doctor.js.map +1 -0
  336. package/dist/types/main-http-handler.js +12 -0
  337. package/dist/types/main-http-handler.js.map +1 -0
  338. package/dist/types/main-http.js +5 -0
  339. package/dist/types/main-http.js.map +1 -0
  340. package/dist/types/main-loops.js +5 -0
  341. package/dist/types/main-loops.js.map +1 -0
  342. package/dist/types/main-mcp.js +12 -0
  343. package/dist/types/main-mcp.js.map +1 -0
  344. package/dist/types/main-orchestrator.js +5 -0
  345. package/dist/types/main-orchestrator.js.map +1 -0
  346. package/dist/types/main-reconcilers.js +11 -0
  347. package/dist/types/main-reconcilers.js.map +1 -0
  348. package/dist/types/main-runner.js +13 -0
  349. package/dist/types/main-runner.js.map +1 -0
  350. package/dist/types/main-startup.js +5 -0
  351. package/dist/types/main-startup.js.map +1 -0
  352. package/dist/types/main-substrates.js +5 -0
  353. package/dist/types/main-substrates.js.map +1 -0
  354. package/dist/types/mcp/dispatch.js +4 -0
  355. package/dist/types/mcp/dispatch.js.map +1 -0
  356. package/dist/types/mcp/post-move.js +7 -0
  357. package/dist/types/mcp/post-move.js.map +1 -0
  358. package/dist/types/mcp.js +9 -0
  359. package/dist/types/mcp.js.map +1 -0
  360. package/dist/types/ports.js +12 -0
  361. package/dist/types/ports.js.map +1 -0
  362. package/dist/types/reconcile/image-decide.js +5 -0
  363. package/dist/types/reconcile/image-decide.js.map +1 -0
  364. package/dist/types/reconcile/ledger.js +7 -0
  365. package/dist/types/reconcile/ledger.js.map +1 -0
  366. package/dist/types/reconcile/pr-loop.js +8 -0
  367. package/dist/types/reconcile/pr-loop.js.map +1 -0
  368. package/dist/types/reconcile/vm-reap.js +8 -0
  369. package/dist/types/reconcile/vm-reap.js.map +1 -0
  370. package/dist/types/reconcile/workspace-decide.js +7 -0
  371. package/dist/types/reconcile/workspace-decide.js.map +1 -0
  372. package/dist/types/reconcile.js +9 -0
  373. package/dist/types/reconcile.js.map +1 -0
  374. package/dist/types/runlog.js +7 -0
  375. package/dist/types/runlog.js.map +1 -0
  376. package/dist/types/runner/actions-runner.js +12 -0
  377. package/dist/types/runner/actions-runner.js.map +1 -0
  378. package/dist/types/runner/gondolin-dispatch.js +5 -0
  379. package/dist/types/runner/gondolin-dispatch.js.map +1 -0
  380. package/dist/types/runner/injection.js +6 -0
  381. package/dist/types/runner/injection.js.map +1 -0
  382. package/dist/types/runner/runner-loop.js +5 -0
  383. package/dist/types/runner/runner-loop.js.map +1 -0
  384. package/dist/types/runner/turn.js +4 -0
  385. package/dist/types/runner/turn.js.map +1 -0
  386. package/dist/types/runner/vm-plan.js +4 -0
  387. package/dist/types/runner/vm-plan.js.map +1 -0
  388. package/dist/types/runtime.js +9 -0
  389. package/dist/types/runtime.js.map +1 -0
  390. package/dist/types/schedule/admission.js +7 -0
  391. package/dist/types/schedule/admission.js.map +1 -0
  392. package/dist/types/schedule/circuit-breaker.js +2 -0
  393. package/dist/types/schedule/circuit-breaker.js.map +1 -0
  394. package/dist/types/schedule/eligibility.js +9 -0
  395. package/dist/types/schedule/eligibility.js.map +1 -0
  396. package/dist/types/schedule/orchestrator-loop.js +10 -0
  397. package/dist/types/schedule/orchestrator-loop.js.map +1 -0
  398. package/dist/types/schedule/sleep-cycle.js +4 -0
  399. package/dist/types/schedule/sleep-cycle.js.map +1 -0
  400. package/dist/types/schedule/slots.js +8 -0
  401. package/dist/types/schedule/slots.js.map +1 -0
  402. package/dist/types/schedule/tick.js +9 -0
  403. package/dist/types/schedule/tick.js.map +1 -0
  404. package/dist/types/server/mcp-runtime.js +8 -0
  405. package/dist/types/server/mcp-runtime.js.map +1 -0
  406. package/dist/types/workflow/parse.js +4 -0
  407. package/dist/types/workflow/parse.js.map +1 -0
  408. package/package.json +22 -10
  409. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  410. package/prompts/Reflect.md +91 -0
  411. package/prompts/Review.md +97 -0
  412. package/prompts/Todo.md +96 -0
  413. package/prompts/_footer.md +41 -0
  414. package/prompts/_preamble.md +42 -0
  415. package/prompts-minimal/Todo.md +26 -0
  416. package/scripts/postinstall.mjs +63 -0
  417. package/scripts/vm-agent.mjs +312 -90
  418. package/WORKFLOW.md +0 -744
  419. package/dist/acp-bridge.js +0 -324
  420. package/dist/acp-bridge.js.map +0 -1
  421. package/dist/actions/cache.js +0 -191
  422. package/dist/actions/cache.js.map +0 -1
  423. package/dist/actions/effects.js +0 -41
  424. package/dist/actions/effects.js.map +0 -1
  425. package/dist/actions/executor.js +0 -570
  426. package/dist/actions/executor.js.map +0 -1
  427. package/dist/actions/index.js +0 -13
  428. package/dist/actions/index.js.map +0 -1
  429. package/dist/actions/parsing.js.map +0 -1
  430. package/dist/actions/predicate-env.js +0 -27
  431. package/dist/actions/predicate-env.js.map +0 -1
  432. package/dist/actions/predicates.js +0 -49
  433. package/dist/actions/predicates.js.map +0 -1
  434. package/dist/actions/templating.js +0 -66
  435. package/dist/actions/templating.js.map +0 -1
  436. package/dist/actions/types.js +0 -15
  437. package/dist/actions/types.js.map +0 -1
  438. package/dist/agent/acp.js +0 -473
  439. package/dist/agent/acp.js.map +0 -1
  440. package/dist/agent/adapter-names.js +0 -159
  441. package/dist/agent/adapter-names.js.map +0 -1
  442. package/dist/agent/adapters.js +0 -511
  443. package/dist/agent/adapters.js.map +0 -1
  444. package/dist/agent/credential-extractors.js +0 -342
  445. package/dist/agent/credential-extractors.js.map +0 -1
  446. package/dist/agent/credential-secrets.js +0 -628
  447. package/dist/agent/credential-secrets.js.map +0 -1
  448. package/dist/agent/credential-ticker.js +0 -57
  449. package/dist/agent/credential-ticker.js.map +0 -1
  450. package/dist/agent/gondolin-creds-staging.js +0 -356
  451. package/dist/agent/gondolin-creds-staging.js.map +0 -1
  452. package/dist/agent/gondolin-dispatch.js +0 -375
  453. package/dist/agent/gondolin-dispatch.js.map +0 -1
  454. package/dist/agent/gondolin.js +0 -124
  455. package/dist/agent/gondolin.js.map +0 -1
  456. package/dist/agent/runner-decisions.js +0 -134
  457. package/dist/agent/runner-decisions.js.map +0 -1
  458. package/dist/agent/runner.js +0 -1456
  459. package/dist/agent/runner.js.map +0 -1
  460. package/dist/agent/tool-call-summary.js +0 -102
  461. package/dist/agent/tool-call-summary.js.map +0 -1
  462. package/dist/agent/vm-acp-mapping.js +0 -73
  463. package/dist/agent/vm-acp-mapping.js.map +0 -1
  464. package/dist/agent/vm-guards.js +0 -262
  465. package/dist/agent/vm-guards.js.map +0 -1
  466. package/dist/agent/vm-port.js +0 -22
  467. package/dist/agent/vm-port.js.map +0 -1
  468. package/dist/agent/vm-process-registry.js +0 -79
  469. package/dist/agent/vm-process-registry.js.map +0 -1
  470. package/dist/bin/cli-args.js +0 -105
  471. package/dist/bin/cli-args.js.map +0 -1
  472. package/dist/errors.js +0 -15
  473. package/dist/errors.js.map +0 -1
  474. package/dist/http-disk.js +0 -135
  475. package/dist/http-disk.js.map +0 -1
  476. package/dist/http-handlers.js.map +0 -1
  477. package/dist/http.js.map +0 -1
  478. package/dist/issues.js +0 -178
  479. package/dist/issues.js.map +0 -1
  480. package/dist/logging.js +0 -203
  481. package/dist/logging.js.map +0 -1
  482. package/dist/mcp.js +0 -706
  483. package/dist/mcp.js.map +0 -1
  484. package/dist/memory.js +0 -85
  485. package/dist/memory.js.map +0 -1
  486. package/dist/orchestrator-decisions.js +0 -331
  487. package/dist/orchestrator-decisions.js.map +0 -1
  488. package/dist/orchestrator.js +0 -1569
  489. package/dist/orchestrator.js.map +0 -1
  490. package/dist/prompt.js +0 -65
  491. package/dist/prompt.js.map +0 -1
  492. package/dist/reconciler/cache.js +0 -65
  493. package/dist/reconciler/cache.js.map +0 -1
  494. package/dist/reconciler/index.js +0 -448
  495. package/dist/reconciler/index.js.map +0 -1
  496. package/dist/reconciler/ledger.js +0 -131
  497. package/dist/reconciler/ledger.js.map +0 -1
  498. package/dist/reconciler/pr-adapters.js +0 -174
  499. package/dist/reconciler/pr-adapters.js.map +0 -1
  500. package/dist/reconciler/pr-decide.js.map +0 -1
  501. package/dist/reconciler/pr.js +0 -422
  502. package/dist/reconciler/pr.js.map +0 -1
  503. package/dist/reconciler/types.js +0 -12
  504. package/dist/reconciler/types.js.map +0 -1
  505. package/dist/reconciler/vm.js +0 -243
  506. package/dist/reconciler/vm.js.map +0 -1
  507. package/dist/reconciler/workspace-defaults.js +0 -83
  508. package/dist/reconciler/workspace-defaults.js.map +0 -1
  509. package/dist/reconciler/workspace.js +0 -272
  510. package/dist/reconciler/workspace.js.map +0 -1
  511. package/dist/runlog.js +0 -403
  512. package/dist/runlog.js.map +0 -1
  513. package/dist/scaffold.js +0 -165
  514. package/dist/scaffold.js.map +0 -1
  515. package/dist/trackers/local.js +0 -445
  516. package/dist/trackers/local.js.map +0 -1
  517. package/dist/trackers/types.js +0 -10
  518. package/dist/trackers/types.js.map +0 -1
  519. package/dist/types.js +0 -3
  520. package/dist/types.js.map +0 -1
  521. package/dist/util/clock.js +0 -12
  522. package/dist/util/clock.js.map +0 -1
  523. package/dist/util/crypto.js +0 -25
  524. package/dist/util/crypto.js.map +0 -1
  525. package/dist/util/frontmatter.js +0 -70
  526. package/dist/util/frontmatter.js.map +0 -1
  527. package/dist/util/fs-issues.js +0 -22
  528. package/dist/util/fs-issues.js.map +0 -1
  529. package/dist/util/process.js +0 -152
  530. package/dist/util/process.js.map +0 -1
  531. package/dist/util/workspace-key.js +0 -10
  532. package/dist/util/workspace-key.js.map +0 -1
  533. package/dist/workflow-loader.js +0 -147
  534. package/dist/workflow-loader.js.map +0 -1
  535. package/dist/workflow.js +0 -822
  536. package/dist/workflow.js.map +0 -1
  537. package/dist/workspace-types.js +0 -8
  538. package/dist/workspace-types.js.map +0 -1
  539. package/dist/workspace.js +0 -443
  540. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ // FCIS rewrite — the dependency contract the orchestrator poll/dispatch loop
2
+ // driver (src/shell/orchestrator-loop.ts) receives from the composition root.
3
+ //
4
+ // The loop itself is an imperative shell value (the OrchestratorLoop class lives
5
+ // in src/shell); only the *shapes* it is wired with live here (the
6
+ // exported-types-live-in-src/types invariant). The shell re-exports
7
+ // `OrchestratorLoopDeps` so importers (the composition root) keep their original
8
+ // specifier.
9
+ export {};
10
+ //# sourceMappingURL=orchestrator-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-loop.js","sourceRoot":"","sources":["../../../src/types/schedule/orchestrator-loop.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,EAAE;AACF,iFAAiF;AACjF,mEAAmE;AACnE,oEAAoE;AACpE,iFAAiF;AACjF,aAAa"}
@@ -0,0 +1,4 @@
1
+ // Sleep-cycle (recurring reflection spawn) decision shapes (declarations only).
2
+ // The pure decisions over these live in src/core/schedule/sleep-cycle.ts.
3
+ export {};
4
+ //# sourceMappingURL=sleep-cycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep-cycle.js","sourceRoot":"","sources":["../../../src/types/schedule/sleep-cycle.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0EAA0E"}
@@ -0,0 +1,8 @@
1
+ // FCIS rewrite — types for src/core/schedule/slots.ts (pure slot accounting).
2
+ //
3
+ // Pure declarations only (the exported-types-live-in-src/types invariant). The
4
+ // slot-accounting DECISIONS live in src/core/schedule/slots.ts, which re-exports
5
+ // these types so existing importers (tick.ts, the unit test) keep the original
6
+ // specifier.
7
+ export {};
8
+ //# sourceMappingURL=slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../../src/types/schedule/slots.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,+EAA+E;AAC/E,aAAa"}
@@ -0,0 +1,9 @@
1
+ // FCIS rewrite — data types for the orchestrator scheduler reducer
2
+ // (src/core/schedule/tick.ts).
3
+ //
4
+ // The reducer in core is pure runtime (it produces these shapes); the shapes
5
+ // themselves live here so core/shell only declare values, not exported types
6
+ // (the exported-types-live-in-src/types invariant). The core module re-exports
7
+ // these so importers keep their original specifier.
8
+ export {};
9
+ //# sourceMappingURL=tick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.js","sourceRoot":"","sources":["../../../src/types/schedule/tick.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,+BAA+B;AAC/B,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,oDAAoD"}
@@ -0,0 +1,8 @@
1
+ // FCIS rewrite — types mirror for the per-issue MCP runtime (shell slice
2
+ // src/shell/server/mcp-runtime.ts). Declarations only; the runtime + effect
3
+ // interpreter live in the shell. Kept here so the "exported types belong in
4
+ // src/types" gate holds and the composition root + interpreter share one
5
+ // vocabulary. The shell modules re-export these so importers keep their
6
+ // `server/mcp-runtime` specifier.
7
+ export {};
8
+ //# sourceMappingURL=mcp-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-runtime.js","sourceRoot":"","sources":["../../../src/types/server/mcp-runtime.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;AACxE,kCAAkC"}
@@ -0,0 +1,4 @@
1
+ // Type surface for the pure WORKFLOW.yaml parser (src/core/workflow/parse.ts).
2
+ // Declarations only — the core re-exports these so importers keep their specifier.
3
+ export {};
4
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/types/workflow/parse.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mFAAmF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smol-symphony",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "TypeScript orchestrator that runs coding agents (Claude, Codex, OpenCode) in per-issue Gondolin microVMs over ACP.",
5
5
  "keywords": [
6
6
  "orchestrator",
@@ -29,26 +29,37 @@
29
29
  },
30
30
  "files": [
31
31
  "dist/",
32
- "WORKFLOW.md",
33
- "WORKFLOW.template.md",
32
+ "patches/",
33
+ "WORKFLOW.yaml",
34
+ "WORKFLOW.minimal.yaml",
35
+ "WORKFLOW.template.yaml",
36
+ "assets/",
37
+ "prompts/",
38
+ "prompts-minimal/",
34
39
  "AGENTS.md",
35
40
  "SPEC.md",
36
41
  "PRODUCT.md",
37
42
  "DESIGN.md",
43
+ "scripts/postinstall.mjs",
38
44
  "scripts/vm-agent.mjs",
39
45
  "README.md",
40
46
  "LICENSE"
41
47
  ],
42
48
  "scripts": {
43
- "build": "tsc",
44
- "start": "tsx src/bin/symphony.ts",
45
- "symphony": "tsx src/bin/symphony.ts WORKFLOW.md",
46
- "dev": "tsx watch src/bin/symphony.ts",
49
+ "postinstall": "node scripts/postinstall.mjs",
50
+ "build": "tsc -p tsconfig.build.json",
51
+ "start": "tsx src/shell/main.ts",
52
+ "symphony": "tsx src/shell/main.ts WORKFLOW.yaml",
53
+ "dev": "tsx watch src/shell/main.ts",
47
54
  "typecheck": "tsc --noEmit",
48
- "test": "node --test --import tsx tests/*.test.ts",
49
- "prepublishOnly": "npm run typecheck && npm test && npm run build",
50
55
  "lint": "eslint src --max-warnings 0",
51
- "lint:arch": "depcruise src --ts-config tsconfig.json --ignore-known",
56
+ "lint:arch": "depcruise src --ts-config tsconfig.json",
57
+ "check:ratio": "node scripts/check-core-ratio.mjs",
58
+ "check:pokes": "node scripts/check-state-pokes.mjs",
59
+ "check:io": "node scripts/check-shell-io.mjs",
60
+ "test": "node --test --import tsx $(find tests -name '*.test.ts')",
61
+ "verify": "npm run typecheck && npm run lint && npm run lint:arch && npm run check:ratio && npm run check:pokes && npm run check:io && npm test",
62
+ "prepublishOnly": "npm run typecheck && npm test && npm run build && node -e \"require('node:fs').accessSync('dist/bin/symphony.js')\"",
52
63
  "build:image": "bash images/agents/build-image-oci.sh"
53
64
  },
54
65
  "dependencies": {
@@ -56,6 +67,7 @@
56
67
  "@earendil-works/gondolin": "^0.12.0",
57
68
  "chokidar": "^4.0.3",
58
69
  "liquidjs": "^10.21.1",
70
+ "patch-package": "^8.0.1",
59
71
  "yaml": "^2.6.1"
60
72
  },
61
73
  "devDependencies": {
@@ -0,0 +1,173 @@
1
+ diff --git a/node_modules/@earendil-works/gondolin/dist/src/qemu/network-stack.js b/node_modules/@earendil-works/gondolin/dist/src/qemu/network-stack.js
2
+ index f08a11f..b8603bd 100644
3
+ --- a/node_modules/@earendil-works/gondolin/dist/src/qemu/network-stack.js
4
+ +++ b/node_modules/@earendil-works/gondolin/dist/src/qemu/network-stack.js
5
+ @@ -10,6 +10,35 @@ const IP_PROTO_ICMP = 1;
6
+ function makeTcpNatKey(srcIP, srcPort, dstIP, dstPort) {
7
+ return `TCP:${srcIP}:${srcPort}:${dstIP}:${dstPort}`;
8
+ }
9
+ +// TCP sequence and acknowledgement numbers are 32 bits wide and wrap at 2^32.
10
+ +// We keep every stored counter (mySeq/myAck/vmSeq/vmAck) masked into [0, 2^32)
11
+ +// and compare them with RFC 1982 serial-number arithmetic instead of raw </>,
12
+ +// which break across the wrap boundary. Skipping either half re-introduces the
13
+ +// `writeUInt32BE` overflow crash or silent reassembly desync near 0xFFFFFFFF.
14
+ +/** Fold a value into the 32-bit TCP sequence space [0, 2^32). */
15
+ +function wrapSeq(value) {
16
+ + return value >>> 0;
17
+ +}
18
+ +/**
19
+ + * Signed RFC 1982 distance between two sequence numbers: > 0 when `a` is "after"
20
+ + * `b`, < 0 when "before", 0 when equal. ToInt32 (`| 0`) folds the difference into
21
+ + * the [-2^31, 2^31) serial window so comparisons stay correct across wrap.
22
+ + */
23
+ +function seqDistance(a, b) {
24
+ + return (a - b) | 0;
25
+ +}
26
+ +/** `a` is strictly after `b` in serial-number order. */
27
+ +function seqGt(a, b) {
28
+ + return seqDistance(a, b) > 0;
29
+ +}
30
+ +/** `a` is strictly before `b` in serial-number order. */
31
+ +function seqLt(a, b) {
32
+ + return seqDistance(a, b) < 0;
33
+ +}
34
+ +/** `a` is at or before `b` in serial-number order. */
35
+ +function seqLe(a, b) {
36
+ + return seqDistance(a, b) <= 0;
37
+ +}
38
+ const HTTP_METHODS = [
39
+ "GET",
40
+ "POST",
41
+ @@ -503,7 +532,7 @@ export class NetworkStack extends EventEmitter {
42
+ vmSeq: seq,
43
+ vmAck: ack,
44
+ mySeq: Math.floor(Math.random() * 0x0fffffff),
45
+ - myAck: seq + 1,
46
+ + myAck: wrapSeq(seq + 1),
47
+ peerWindow: window,
48
+ pendingOutbound: Buffer.alloc(0),
49
+ endPending: false,
50
+ @@ -527,14 +556,14 @@ export class NetworkStack extends EventEmitter {
51
+ }
52
+ if (!session) {
53
+ if (!SYN) {
54
+ - this.sendTCP(srcIP, srcPort, dstIP, dstPort, 0, seq + (payload.length || 1), 0x04);
55
+ + this.sendTCP(srcIP, srcPort, dstIP, dstPort, 0, wrapSeq(seq + (payload.length || 1)), 0x04);
56
+ }
57
+ return;
58
+ }
59
+ const prevPeerWindow = session.peerWindow;
60
+ session.peerWindow = window;
61
+ let shouldDrainOutbound = false;
62
+ - if (ack > session.vmAck && ack <= session.mySeq) {
63
+ + if (seqGt(ack, session.vmAck) && seqLe(ack, session.mySeq)) {
64
+ session.vmAck = ack;
65
+ shouldDrainOutbound = true;
66
+ }
67
+ @@ -553,12 +582,12 @@ export class NetworkStack extends EventEmitter {
68
+ // This matters for SSH in particular: duplicating bytes can desync the
69
+ // protocol and trigger errors like "Bad packet length".
70
+ const expectedSeq = session.myAck;
71
+ - if (seq > expectedSeq) {
72
+ + if (seqGt(seq, expectedSeq)) {
73
+ // Out-of-order: re-ACK what we've already seen.
74
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x10);
75
+ return;
76
+ }
77
+ - const skip = Math.max(0, expectedSeq - seq);
78
+ + const skip = Math.max(0, seqDistance(expectedSeq, seq));
79
+ if (skip >= payload.length) {
80
+ // Pure retransmit (or segment contains only already-acked bytes)
81
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x10);
82
+ @@ -568,7 +597,7 @@ export class NetworkStack extends EventEmitter {
83
+ }
84
+ const newPayload = payload.subarray(skip);
85
+ let sendBuffer = null;
86
+ - const nextAck = expectedSeq + newPayload.length;
87
+ + const nextAck = wrapSeq(expectedSeq + newPayload.length);
88
+ if (!session.flowProtocol) {
89
+ session.pendingData = Buffer.concat([session.pendingData, newPayload]);
90
+ if (session.allowRawTcp) {
91
+ @@ -635,20 +664,20 @@ export class NetworkStack extends EventEmitter {
92
+ if (FIN) {
93
+ // FIN consumes one sequence number, so only accept it once the sequence
94
+ // space up to the FIN is fully received.
95
+ - const finSeq = seq + payload.length;
96
+ - if (finSeq > session.myAck) {
97
+ + const finSeq = wrapSeq(seq + payload.length);
98
+ + if (seqGt(finSeq, session.myAck)) {
99
+ // Out-of-order FIN: keep ACKing the last in-order byte.
100
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x10);
101
+ return;
102
+ }
103
+ - if (finSeq < session.myAck) {
104
+ + if (seqLt(finSeq, session.myAck)) {
105
+ // Duplicate FIN (already acked)
106
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x10);
107
+ return;
108
+ }
109
+ // finSeq === session.myAck
110
+ this.callbacks.onTcpClose({ key, destroy: false });
111
+ - session.myAck++;
112
+ + session.myAck = wrapSeq(session.myAck + 1);
113
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x10);
114
+ if (session.state === "CLOSED_BY_REMOTE" ||
115
+ session.state === "FIN_WAIT") {
116
+ @@ -664,8 +693,10 @@ export class NetworkStack extends EventEmitter {
117
+ const header = Buffer.alloc(20);
118
+ header.writeUInt16BE(srcPort, 0);
119
+ header.writeUInt16BE(dstPort, 2);
120
+ - header.writeUInt32BE(seq, 4);
121
+ - header.writeUInt32BE(ack, 8);
122
+ + // Counters are kept wrapped at their mutation sites; mask again here so the
123
+ + // serialization boundary can never emit an out-of-range uint32 (the crash).
124
+ + header.writeUInt32BE(wrapSeq(seq), 4);
125
+ + header.writeUInt32BE(wrapSeq(ack), 8);
126
+ header[12] = 0x50;
127
+ header[13] = flags;
128
+ header.writeUInt16BE(65535, 14);
129
+ @@ -925,7 +956,7 @@ export class NetworkStack extends EventEmitter {
130
+ }
131
+ return;
132
+ }
133
+ - const inFlight = Math.max(0, session.mySeq - session.vmAck);
134
+ + const inFlight = Math.max(0, seqDistance(session.mySeq, session.vmAck));
135
+ const maxInFlight = Math.max(0, Math.min(session.peerWindow, this.TCP_MAX_IN_FLIGHT_BYTES));
136
+ if (inFlight < maxInFlight) {
137
+ this.txFlowPaused.delete(key);
138
+ @@ -945,7 +976,7 @@ export class NetworkStack extends EventEmitter {
139
+ }
140
+ const MSS = 1460;
141
+ let bytesBurstThisTick = 0;
142
+ - let inFlight = Math.max(0, session.mySeq - session.vmAck);
143
+ + let inFlight = Math.max(0, seqDistance(session.mySeq, session.vmAck));
144
+ const maxInFlight = Math.max(0, Math.min(session.peerWindow, this.TCP_MAX_IN_FLIGHT_BYTES));
145
+ while (session.pendingOutbound.length > 0 &&
146
+ inFlight < maxInFlight &&
147
+ @@ -963,7 +994,7 @@ export class NetworkStack extends EventEmitter {
148
+ if (!isLiveSession()) {
149
+ return;
150
+ }
151
+ - session.mySeq += chunk.length;
152
+ + session.mySeq = wrapSeq(session.mySeq + chunk.length);
153
+ inFlight += chunk.length;
154
+ bytesBurstThisTick += chunk.length;
155
+ }
156
+ @@ -990,7 +1021,7 @@ export class NetworkStack extends EventEmitter {
157
+ if (!isLiveSession()) {
158
+ return;
159
+ }
160
+ - session.mySeq++;
161
+ + session.mySeq = wrapSeq(session.mySeq + 1);
162
+ session.state = "FIN_WAIT";
163
+ session.endPending = false;
164
+ inFlight += 1;
165
+ @@ -1012,7 +1043,7 @@ export class NetworkStack extends EventEmitter {
166
+ return;
167
+ session.state = "ESTABLISHED";
168
+ this.sendTCP(session.srcIP, session.srcPort, session.dstIP, session.dstPort, session.mySeq, session.myAck, 0x12);
169
+ - session.mySeq++;
170
+ + session.mySeq = wrapSeq(session.mySeq + 1);
171
+ }
172
+ handleTcpData(message) {
173
+ const session = this.natTable.get(message.key);
@@ -0,0 +1,91 @@
1
+ You are the **reflector** running symphony's *sleep cycle*. You are not
2
+ implementing or reviewing a product change. Your job: mine symphony's own
3
+ finished work for *recurring* friction in **how it runs work**, distil concrete
4
+ lessons, and file one harness-improvement proposal per lesson into Triage —
5
+ where a human operator decides whether to adopt it. Then close out this cycle.
6
+
7
+ You are a single-run, ephemeral reflection issue: you were spawned for this one
8
+ cycle, you run once, and you terminate in `Reflected`. There is no parked ticket
9
+ to re-arm — the orchestrator mints a fresh reflection issue for the next cycle.
10
+
11
+ This is a self-modifying loop: you read agent-authored transcripts and then
12
+ propose changes to *your own* operating instructions. The guardrails below are
13
+ load-bearing — follow them exactly.
14
+
15
+ **What you can read** (read-only mounts present only in this state):
16
+
17
+ - `/symphony/issues/` — every issue file across every state directory. The
18
+ richest signal is `/symphony/issues/Done/*.md`: each file is the full handoff
19
+ thread (every `symphony.transition` notes block from implementer → reviewer →
20
+ approval is appended to the body), so a Done file shows how the work actually
21
+ went, not just the final result. `Triage/`, `Cancelled/`, and the active
22
+ state dirs are visible too.
23
+ - `/symphony/logs/<id>.jsonl` — the per-issue run log: every ACP frame, adapter
24
+ stderr line, typed-action output, and orchestrator lifecycle event for that issue.
25
+ This is where stalls, turn-budget exhaustion, retries, and timeouts show up
26
+ in detail.
27
+ - Your workspace is a clone of the symphony repo, so you can read `WORKFLOW.yaml`,
28
+ `WORKFLOW.template.yaml`, `src/`, `images/agents/`, etc. to ground each proposal
29
+ in the concrete knob it would change.
30
+
31
+ If structured per-issue run summaries exist (companion issue #123), start from
32
+ those as an index; otherwise skim `Done/*.md` and open the
33
+ `/symphony/logs/<id>.jsonl` for the issues that look anomalous.
34
+
35
+ **What to look for — *recurring* patterns, not one-offs.** One bad run is
36
+ noise; the same failure shape across several issues is signal. For example:
37
+
38
+ - repeated `Review → Todo` rejects with the same root cause (the reviewer keeps
39
+ catching the same class of mistake the implementer prompt doesn't prevent);
40
+ - turn-budget exhaustion (a state hits `max_turns` before it can transition);
41
+ - stalls / timeouts (`stall_timeout_ms` / `prompt_timeout_ms` trips);
42
+ - rebase / merge-conflict churn on re-dispatch;
43
+ - credential re-login loops;
44
+ - acceptance-criteria misses a sharper prompt or checklist would have caught;
45
+ - prompt ambiguity that forced `request_human_steering`.
46
+
47
+ **For each distilled lesson, file exactly one `symphony.propose_issue` call**
48
+ (one per fix — never batch multiple fixes into one proposal; Triage is
49
+ per-item). Each proposal must:
50
+
51
+ - name a single concrete change to the **harness / operating config**: a
52
+ per-state `prompt_file`, a per-state `model` / `max_turns` /
53
+ `allowed_transitions` / `effort` / `actions`, the `gondolin` image config, an
54
+ acceptance criterion, or a timeout;
55
+ - include a **before → after** (what the config/prompt says now, what you'd
56
+ change it to);
57
+ - cite the **evidence** — the issue ids (and, where useful, the log lines or
58
+ Done-file quotes) that motivated it — so the operator can check the lesson
59
+ against the trajectories rather than trusting your summary.
60
+
61
+ **Hard guardrails — a proposal that violates any of these must NOT be filed:**
62
+
63
+ - Propose changes to the **harness only** — `WORKFLOW.yaml` /
64
+ `WORKFLOW.template.yaml`, per-state config (including `actions:`), the `gondolin`
65
+ image config, acceptance criteria, timeouts. Do **not** propose edits to product/source code under
66
+ `src/` as a "fix" for a trajectory; if a genuine product bug is the root
67
+ cause, that is an ordinary `propose_issue` for an implementer, not a harness
68
+ change, and you should frame it that way.
69
+ - Never propose anything that **weakens a quality gate.** Do not weaken or
70
+ remove the Review state, the `npm run typecheck` / `npm test` /
71
+ `npm run lint:arch` / `npm run lint` gates, or the Triage human-approval gate
72
+ itself. The whole point of this loop is that a human stays in it; proposals
73
+ that would let the loop dispatch its own changes without review are
74
+ forbidden, even if the trajectories seem to "justify" them.
75
+ - One fix per proposal, each with cited evidence. No proposal without issue ids.
76
+
77
+ When you have filed your proposals — or concluded there is no recurring pattern
78
+ worth acting on this cycle — hand off by terminating in `Reflected`:
79
+
80
+ ```
81
+ symphony.transition({
82
+ to_state: "Reflected",
83
+ notes: "<one-paragraph log of this cycle: how many issues you reviewed, the
84
+ patterns you found, and the proposal titles you filed (or 'no
85
+ actionable pattern this cycle')>"
86
+ })
87
+ ```
88
+
89
+ Then end your turn; do not call any further tools. This issue is archived in the
90
+ `Reflected` column as a per-cycle audit trail; the orchestrator mints a fresh
91
+ reflection issue for the next cycle.
@@ -0,0 +1,97 @@
1
+ You are the **reviewer**. The implementer has committed work to the per-issue
2
+ branch (`agent/{{ issue.identifier }}`) and handed off. Their summary is in
3
+ the issue description above. Your job: decide whether the work is correct and
4
+ either approve (→ Done) or send it back (→ Todo) with specific findings.
5
+
6
+ 1. Read the implementer's notes in the issue description carefully — title,
7
+ summary, files claimed touched. (Follow-ups belong in `propose_issue`,
8
+ not the notes; if you see a "Follow-ups not done" section in the notes,
9
+ reject and ask the implementer to file each item as a separate
10
+ `propose_issue` call instead.)
11
+ 2. Inspect the diff against the freshly-fetched base. The host fetched
12
+ `origin/main` (or whatever base branch `SYMPHONY_BASE_BRANCH` names — `main`
13
+ is the default here) into this workspace before the dispatch, and the
14
+ implementer rebased onto it, so `origin/main` is the true base:
15
+
16
+ ```
17
+ git log --oneline origin/main..HEAD
18
+ git diff origin/main..HEAD
19
+ ```
20
+
21
+ Diff against `origin/main`, **not** the local `main` ref: your workspace's
22
+ local `main` is frozen at clone time and goes **stale** as sibling PRs merge
23
+ — `origin/main` advances but local `main` does not. Diffing `main..HEAD` then
24
+ surfaces commits already on base as if they were the implementer's change,
25
+ and can make a correctly-based branch look like it *reverts* recently-landed
26
+ work. (Local-only mode — no `origin` remote — has no such drift, so `main`
27
+ stays the correct base there.) If `HEAD`'s merge-base is behind `origin/main`
28
+ (e.g. `git merge-tree --write-tree HEAD origin/main` reports conflicts),
29
+ reject to `Todo` asking the implementer to rebase onto `origin/main` — don't
30
+ agonize over an apparent revert.
31
+
32
+ Look at each file the implementer claimed to touch. Spot-check tests and
33
+ typecheck pass:
34
+
35
+ ```
36
+ npm run typecheck
37
+ npm test
38
+ ```
39
+
40
+ 3. Placement check — is the change on the right side of the seam?
41
+
42
+ The orchestrator (`src/agent/runner.ts`, `src/mcp.ts`, `src/orchestrator.ts`)
43
+ owns the state machine and tracker. Repo-local glue (`git push`, `gh pr
44
+ create`, rescuing artifacts) lives in a state's typed `actions:` block
45
+ (or a `run_in_vm` action for arbitrary guest commands). Reject when the
46
+ diff crosses that line:
47
+
48
+ - An action implements a new state transition, or mutates the tracker
49
+ filesystem the orchestrator owns (e.g. `mv issues/<state>/<id>.md
50
+ issues/<other-state>/<id>.md`).
51
+ - An action mutates runtime state the orchestrator committed earlier (e.g.
52
+ undoing a cleanup flag) and the runner now has to re-detect what the
53
+ action did via a post-action scan.
54
+ - A new `SYMPHONY_*` env var is added so an action can reach into
55
+ orchestrator-owned state. The contract is growing because the logic is
56
+ on the wrong side; surface it as a typed call in the runner/MCP layer
57
+ instead.
58
+
59
+ If any of the above fires, reject with a pointer to the right home
60
+ (runner, MCP tool, or orchestrator). Action-only diffs that stay within
61
+ repo-local glue (push/PR/format-patch) are fine — this check is
62
+ about state-machine logic leaking into a state's `actions:`.
63
+
64
+ 4. Decide:
65
+
66
+ - **Approve**: the change is correct, tests pass, no blocking issues. Call
67
+
68
+ ```
69
+ symphony.transition({
70
+ to_state: "Done",
71
+ notes: "<approval rationale; this becomes the PR body>"
72
+ })
73
+ ```
74
+
75
+ Your notes are appended to the issue body and feed straight into the PR
76
+ description the host opens against the base branch. Be specific about
77
+ what you verified.
78
+
79
+ - **Reject**: the change is wrong, incomplete, or has issues that need
80
+ rework. Call
81
+
82
+ ```
83
+ symphony.transition({
84
+ to_state: "Todo",
85
+ notes: "<specific findings: file paths, line numbers, what's wrong,
86
+ what needs to change. Be concrete — the implementer will see
87
+ this as their next prompt.>"
88
+ })
89
+ ```
90
+
91
+ The issue goes back to Todo with your findings appended. The same
92
+ workspace and `agent/{{ issue.identifier }}` branch survive the round
93
+ trip, so the next implementer dispatch sees both your notes and their
94
+ prior commits.
95
+
96
+ Either way, end your turn after the transition call. Do not call any
97
+ further tools.
@@ -0,0 +1,96 @@
1
+ You are the **implementer**. Your job: turn the issue into a working change on
2
+ the per-issue branch, then hand off to the reviewer.
3
+
4
+ 1. **Rebase onto a fresh base first.** Symphony has just fetched
5
+ `origin/main` into your workspace (or whatever base branch
6
+ `SYMPHONY_BASE_BRANCH` names — `main` is the default and matches this
7
+ project). The very first thing to do is rebase your branch onto it:
8
+
9
+ ```
10
+ git rebase origin/main
11
+ ```
12
+
13
+ - On a fresh issue this is a no-op (you're already on top of base).
14
+ - On a re-dispatch where base has advanced this picks up the new commits.
15
+ - If `git rebase` reports conflicts, **resolve them in-tree as part of
16
+ this turn** (reconcile your change with what landed on base), `git add`
17
+ the resolved files, and `git rebase --continue` (repeat per replayed
18
+ commit). Then proceed to step 2. There is no separate conflict state
19
+ to route to — handling the conflict is part of normal implementation
20
+ work, just like any other rebase you'd do on your own machine.
21
+ 2. Read enough of the codebase to understand the change you need to make.
22
+ 3. Decide where the change belongs before writing it. The orchestrator
23
+ (`src/agent/runner.ts`, `src/mcp.ts`, `src/orchestrator.ts`) owns the
24
+ state machine and the tracker. Repo-local glue (`git push`, `gh pr
25
+ create`, rescuing artifacts) lives in a state's typed `actions:` block
26
+ (or, for arbitrary guest commands, a `run_in_vm` action); workspace
27
+ setup is the orchestrator's `setupWorkspaceDir`, and per-VM tooling is
28
+ baked into the agent image. State-machine behavior (new transitions,
29
+ anything that mutates tracker files or runtime entry state) belongs in
30
+ the orchestrator with typed APIs and tests — not in an action. If you
31
+ find yourself adding a `SYMPHONY_*` env var so an action can reach into
32
+ orchestrator state, or writing an action that the runner then has to
33
+ re-detect via a post-action scan, that is the signal you are on the wrong
34
+ side of the seam: stop and put the logic in the runner/MCP layer
35
+ instead. The issue body may sketch a shell-shaped solution; treat that
36
+ as one option, not a directive.
37
+ 4. Make the smallest correct change for the issue's stated scope, and keep it
38
+ focused. If you notice work beyond what the issue states, call
39
+ `symphony.propose_issue` for it rather than expanding this change to swallow
40
+ follow-up work. Add or update tests where the change is testable. Before
41
+ handing off, run `npm run typecheck`, `npm test`, `npm run lint:arch`, and
42
+ `npm run lint` — all must pass. `node_modules` may be absent on dispatch;
43
+ if you `npm install` to run those gates, the image's npm can rewrite
44
+ `package-lock.json` (e.g. bumping the root package `engines.node` from
45
+ `>=20` to `>=23.6.0`). That drift is unrelated to your change — before
46
+ handing off, run `git status` and `git checkout -- package-lock.json` to
47
+ drop any incidental lockfile churn you did not intend. The handoff tree
48
+ must be clean.
49
+ **Do NOT edit the `--max-warnings` ratchet in `package.json`** — leave that line
50
+ exactly as-is. It is tightened in one pass at the end of the burn-down. Your change
51
+ only needs to keep `npm run lint` green at the *current* ratchet (it will, as long
52
+ as you reduce or hold the warning count). Lowering it yourself just collides with
53
+ every other in-flight issue's `package.json` and forces manual conflict resolution.
54
+ 5. Commit your work to the per-issue branch with a short message.
55
+ 6. **Re-read the issue's Acceptance criteria and confirm your diff satisfies
56
+ every bullet before handing off.** An acceptance bullet you've left unmet —
57
+ because closing it would touch a path outside the stated `allowed_paths`, or
58
+ because you'd rather defer it to a `symphony.propose_issue` follow-up — means
59
+ the issue is **not done**. `allowed_paths` describes where the change
60
+ *normally* lands; it is not license to hand off an unmet acceptance bullet.
61
+ Satisfy the criterion (touch the extra path and say why in your notes — the
62
+ way you would for a required `CHANGELOG.md` edit), and do **not**
63
+ `symphony.propose_issue` work the acceptance criteria require of *this*
64
+ issue. If a criterion genuinely cannot be met within scope (it conflicts
65
+ with another constraint, or needs a decision only a human can make), call
66
+ `symphony.request_human_steering` rather than handing off a partial change
67
+ with the gap described in the notes. (If satisfying a bullet here means new
68
+ edits, re-run the gates from step 4 and amend your commit before handing
69
+ off.)
70
+ 7. Hand off to the reviewer by calling:
71
+
72
+ ```
73
+ symphony.transition({
74
+ to_state: "Review",
75
+ notes: "# <imperative-voice title, ≤72 chars>\n\n<one- to three-paragraph
76
+ summary of what you changed, why, files touched, and tests added>"
77
+ })
78
+ ```
79
+
80
+ The notes describe **this change**. Out-of-scope items you noticed —
81
+ unrelated bugs, refactors, follow-ups, a future ticket someone should
82
+ size — go through `symphony.propose_issue` (see the shared section below).
83
+ Do not park them in a "Follow-ups not done" section in the notes; that
84
+ surface dies in `Done/<id>.md` and no agent ever sees it again.
85
+
86
+ Don't include a verification section restating that
87
+ `npm run typecheck` / `npm test` / `npm run build` passed — that's an
88
+ AGENTS.md requirement and the reviewer re-runs them. Mention test count
89
+ or extra commands only when something is atypical (test count dropped,
90
+ you ran a smoke against a live service, etc.).
91
+
92
+ The notes block is appended to the issue body **before** the file moves to
93
+ `Review/`, so the reviewer sees it as part of `issue.description` on the
94
+ next dispatch. Write it as if it were the PR body — because if the reviewer
95
+ approves, the entire issue body (including this block) becomes the PR
96
+ description. Then end your turn; do not call any further tools.
@@ -0,0 +1,41 @@
1
+ If you genuinely cannot proceed — ambiguous requirements, missing context that
2
+ only a human can supply, a design decision that needs human input — call
3
+ `symphony.request_human_steering({ question, context })` instead of guessing.
4
+ Your turn will end immediately and the human's response will arrive as your
5
+ next prompt.
6
+
7
+ Steering tips:
8
+
9
+ - The operator's dashboard already shows the original issue title and body
10
+ alongside your question. Do **not** restate or paraphrase the issue body in
11
+ `question`; ask the specific thing you need answered.
12
+ - Use `context` for facts the operator wouldn't otherwise see: what you've
13
+ inspected, what you've already tried, what constraint is forcing the
14
+ question.
15
+
16
+ If during your work you notice something worth fixing that is **out of scope**
17
+ for your current task — an unrelated bug, a follow-up the operator should
18
+ size, a refactor a future agent could pick up — call
19
+ `symphony.propose_issue({ title, description?, labels?, priority? })`. The
20
+ proposal lands in a Triage state directory that the orchestrator does **not**
21
+ dispatch; the operator approves or discards it from the dashboard. Your
22
+ current issue is automatically recorded as the proposal's parent — do not
23
+ paste it into the body. Use this instead of grafting unrelated changes onto
24
+ your current task; keep your branch focused.
25
+
26
+ File **one `propose_issue` call per follow-up**, not a batched call with
27
+ multiple items in one body. Triage is per-item triage; the operator's
28
+ approve/discard verb is per-issue, so a batched proposal forces an
29
+ all-or-nothing decision and loses the individual sizing/priority each
30
+ follow-up deserves. The "Follow-ups not done" pattern of writing a bulleted
31
+ list in the handoff notes is the wrong surface — those notes ride into the
32
+ PR and then die in `Done/<id>.md`; only `propose_issue` puts the items in
33
+ front of the operator on the dashboard.
34
+
35
+ {% if attempt -%}
36
+ This is continuation/retry attempt {{ attempt }}. Inspect the workspace before
37
+ making new edits; your previous run may have left commits on the branch. Check
38
+ `git log agent/{{ issue.identifier }}` to see what's there. If the work is
39
+ already complete in the current state, call `symphony.transition` with the
40
+ appropriate next state and notes summarising what was already done, then stop.
41
+ {%- endif %}