@desplega.ai/agent-swarm 1.159.0 → 1.161.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 (415) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-qhch0n2v.js → acp-adapter-xx489vvb.js} +286 -75
  3. package/dist/{actions-dtsh1mfb.js → actions-ykjkn0m9.js} +32 -30
  4. package/dist/{anthropic-messages-f2h06k64.js → anthropic-messages-5qx80j9k.js} +99 -14
  5. package/dist/{app-y1b9srva.js → app-0hdqa4k1.js} +13 -11
  6. package/dist/{assistant-31hwy5xj.js → assistant-am2g5g8a.js} +40 -32
  7. package/dist/{azure-openai-responses-0k1mfax5.js → azure-openai-responses-fszbkp0e.js} +3 -3
  8. package/dist/{boot-reembed-48d2qpkt.js → boot-reembed-gt5gjkt4.js} +14 -12
  9. package/dist/{boot-reembed-w0bryhkr.js → boot-reembed-xy9t7avg.js} +15 -13
  10. package/dist/{boot-scrub-logs-t65wbrn7.js → boot-scrub-logs-9x0985y5.js} +12 -10
  11. package/dist/boot-seeds-trv0d4vg.js +29 -0
  12. package/dist/{claude-adapter-1t6611ne.js → claude-adapter-jb6e1e54.js} +32 -26
  13. package/dist/{claude-managed-adapter-werp659j.js → claude-managed-adapter-yxkfxg8j.js} +7 -7
  14. package/dist/{claude-managed-setup-y6rdgbc1.js → claude-managed-setup-d1q9hv2b.js} +26 -27
  15. package/dist/{claude-sdk-session-zhkyk8yw.js → claude-sdk-session-zga700s2.js} +11737 -11256
  16. package/dist/{cli-79aq9cgb.js → cli-0383bp7f.js} +1222 -406
  17. package/dist/{cli-xgpf00sk.js → cli-0c7ata9h.js} +24 -23
  18. package/dist/{cli-qjxy52w7.js → cli-121ph74b.js} +2 -2
  19. package/dist/{cli-20035trh.js → cli-17xpf7cm.js} +14 -7
  20. package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
  21. package/dist/{cli-g9df2m37.js → cli-1cz0c18q.js} +249 -200
  22. package/dist/{cli-fjtp3463.js → cli-1er0rhz8.js} +13 -12
  23. package/dist/{cli-ca2w7zv7.js → cli-2hhfhfq5.js} +11 -6
  24. package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
  25. package/dist/{cli-rykfmgwg.js → cli-2x0e561b.js} +1 -1
  26. package/dist/{cli-503ya656.js → cli-35pj2yva.js} +20 -150
  27. package/dist/cli-3qhyh31j.js +166 -0
  28. package/dist/{cli-cygyqj3b.js → cli-3zc55ctx.js} +8 -6
  29. package/dist/{cli-96qd5psa.js → cli-58e8yr0d.js} +1 -1
  30. package/dist/{cli-4aj25szq.js → cli-5nbmpy8w.js} +2 -2
  31. package/dist/{cli-mg24hkt4.js → cli-5p3x4zna.js} +2 -2
  32. package/dist/{cli-vwbbaecp.js → cli-7119ja3w.js} +3 -3
  33. package/dist/{cli-mzkqpxee.js → cli-798vkbks.js} +2 -2
  34. package/dist/{cli-3gvrr8vj.js → cli-82c1pacn.js} +111 -54
  35. package/dist/{cli-hgqfttfc.js → cli-89s4ngw3.js} +1 -1
  36. package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
  37. package/dist/{cli-b6thtzac.js → cli-8g6mc14k.js} +7 -7
  38. package/dist/{cli-eg7cdg68.js → cli-9m03pzga.js} +5 -5
  39. package/dist/cli-9s68w752.js +303 -0
  40. package/dist/{cli-48527fpw.js → cli-a8c82p2t.js} +16 -15
  41. package/dist/{cli-5f2hq128.js → cli-ad4qxm6j.js} +12 -11
  42. package/dist/{cli-e4xnc0qp.js → cli-akfj32vj.js} +33 -7
  43. package/dist/{cli-ejpygnqz.js → cli-bh7jed3k.js} +212 -22
  44. package/dist/{cli-0z2v4nhw.js → cli-cat6h73s.js} +1 -1
  45. package/dist/{cli-e0ndxhtk.js → cli-cfkrztza.js} +190 -13
  46. package/dist/{cli-kc3dp84d.js → cli-d56tgd45.js} +216 -59
  47. package/dist/cli-dbmwby6a.js +9 -0
  48. package/dist/{cli-0n2e4x49.js → cli-ecfxwnyb.js} +15 -2
  49. package/dist/{cli-gzyh0j0g.js → cli-ee8bc46j.js} +66 -166
  50. package/dist/{cli-kdnehe0t.js → cli-et8m9xsv.js} +1 -1
  51. package/dist/cli-f9w1semm.js +413 -0
  52. package/dist/cli-gt4hxb8p.js +99 -0
  53. package/dist/{cli-qv4jv8bx.js → cli-hgmnf38m.js} +2 -2
  54. package/dist/{cli-10ym38qc.js → cli-hn04t9db.js} +3 -3
  55. package/dist/{cli-qvnnx4ct.js → cli-jpf7e1zq.js} +105 -243
  56. package/dist/cli-jyy6pm2f.js +333872 -0
  57. package/dist/{cli-dvjpkkjd.js → cli-kgd3p0bm.js} +7 -7
  58. package/dist/cli-ma3zvh39.js +10404 -0
  59. package/dist/{cli-r2ap2czm.js → cli-nexqx4ve.js} +6 -0
  60. package/dist/{cli-j9z8mfx3.js → cli-nsdxtcbg.js} +37 -20
  61. package/dist/{cli-wgjx0q33.js → cli-phfg8vy8.js} +392 -53
  62. package/dist/{cli-rgjprct3.js → cli-ptr8h7kp.js} +19 -1
  63. package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
  64. package/dist/{cli-bv0a38v9.js → cli-qwsnmjdd.js} +515 -229
  65. package/dist/{cli-68bmme5p.js → cli-r6fsxc76.js} +40018 -52654
  66. package/dist/{cli-rrqmqr1s.js → cli-rtx9dygp.js} +1 -1
  67. package/dist/{cli-rfezr056.js → cli-sqm3r5ps.js} +83 -15
  68. package/dist/{cli-zw65mab6.js → cli-t6v2ah3r.js} +67 -13
  69. package/dist/{cli-vn0qv9cj.js → cli-t845n9xk.js} +28 -14
  70. package/dist/cli-trc24wk2.js +143 -0
  71. package/dist/{cli-wd9hz40d.js → cli-tre4bdgq.js} +5 -5
  72. package/dist/{cli-9yg65c1j.js → cli-tsk3nz8a.js} +29 -1
  73. package/dist/{cli-phsaj4xq.js → cli-v0pvvhfq.js} +1 -1
  74. package/dist/{cli-hj65wsts.js → cli-vnnz5ncg.js} +4695 -4915
  75. package/dist/{cli-cpvsaqsw.js → cli-w0bwte75.js} +8 -5
  76. package/dist/{cli-z9x0sh1t.js → cli-we0q7t0e.js} +3 -3
  77. package/dist/{cli-r94z9p1z.js → cli-wgbezhw2.js} +3 -0
  78. package/dist/{cli-19jenss3.js → cli-xk79303e.js} +51 -17
  79. package/dist/{cli-31nkw3g3.js → cli-ybgv4kw2.js} +15 -8
  80. package/dist/{cli-ardpzhjm.js → cli-yrg7c73k.js} +13 -6
  81. package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
  82. package/dist/{cli-vhjwh6cz.js → cli-yy6dew39.js} +2 -2
  83. package/dist/{cli-bgv70kym.js → cli-zb6xbk61.js} +3 -2
  84. package/dist/{cli-4tvgz99m.js → cli-ze1d3wzv.js} +79 -27
  85. package/dist/cli.js +33 -32
  86. package/dist/{cloudflare-workers-ai-system-one-1sg586qv.js → cloudflare-workers-ai-system-one-k6fbxw9d.js} +2 -2
  87. package/dist/{codex-adapter-jqfqqa21.js → codex-adapter-04v97vep.js} +23 -20
  88. package/dist/{codex-hook-5eys3dya.js → codex-hook-20me6byw.js} +2 -2
  89. package/dist/{codex-login-he9vrn90.js → codex-login-cf7mzsgg.js} +48 -3
  90. package/dist/{codex-session-runner-zd341dsm.js → codex-session-runner-ax69mqq3.js} +21 -20
  91. package/dist/{commands-vfsysz4s.js → commands-18gy08n1.js} +12 -10
  92. package/dist/{db-s3znhvbn.js → db-d2pvjpyt.js} +22 -10
  93. package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
  94. package/dist/{dispatcher-5xn0c7rf.js → dispatcher-8wbwyzjg.js} +31 -29
  95. package/dist/{dsh-adapter-894mgfvs.js → dsh-adapter-ysqk9snv.js} +9 -4
  96. package/dist/{e2b-ez07znam.js → e2b-esmt1r16.js} +3 -1
  97. package/dist/{execute-71ysgv35.js → execute-cmagrbrz.js} +235 -52
  98. package/dist/{google-generative-ai-1nfcdphp.js → google-generative-ai-k6n5nbpp.js} +3 -3
  99. package/dist/{google-vertex-1tqcd7b1.js → google-vertex-44xxtcvp.js} +3 -3
  100. package/dist/{handlers-5w4jwycr.js → handlers-6b69m5kp.js} +34 -32
  101. package/dist/{hook-z0hbxj85.js → hook-k3mydf2n.js} +16 -14
  102. package/dist/{hook-853bxrr6.js → hook-srnkt0hb.js} +16 -14
  103. package/dist/{http-akc3wy3m.js → http-hrf80ca1.js} +836 -189
  104. package/dist/{index-ttpbhnjg.js → index-1asbaj62.js} +32 -30
  105. package/dist/{index-cd151qkq.js → index-45evq4ee.js} +31 -29
  106. package/dist/{index-k661dzzr.js → index-9rxdvj6w.js} +32 -30
  107. package/dist/index-k4nj18qs.js +31 -0
  108. package/dist/{index-5sz16ksh.js → index-xmevk2b9.js} +89 -61
  109. package/dist/{keepalive-8p9rpvz5.js → keepalive-sfdz6ksp.js} +14 -12
  110. package/dist/{lead-jp69z5h3.js → lead-ej91e9hw.js} +52 -50
  111. package/dist/{llama-cpp-classify-t2tccams.js → llama-cpp-classify-tgj35k8d.js} +1 -1
  112. package/dist/{maintenance-fwx4ev0k.js → maintenance-80dnd7ke.js} +15 -13
  113. package/dist/{mistral-conversations-bhvwqcnk.js → mistral-conversations-pc3ntnxt.js} +1 -1
  114. package/dist/node-f0ykebbv.js +2107 -0
  115. package/dist/node-wyamey5r.js +19 -0
  116. package/dist/{oauth-refresh-sweep-cwcq1ctx.js → oauth-refresh-sweep-m1etk2mx.js} +13 -11
  117. package/dist/{onboard-2b4q0t84.js → onboard-0q8sdfqc.js} +2 -2
  118. package/dist/{openai-codex-responses-nekthgks.js → openai-codex-responses-n7n683p2.js} +2 -2
  119. package/dist/{openai-completions-hxtzwj0s.js → openai-completions-jpaj5g2b.js} +2 -2
  120. package/dist/{openai-responses-w4r118w7.js → openai-responses-9a3nme9j.js} +3 -3
  121. package/dist/{opencode-adapter-zz7xvjj6.js → opencode-adapter-4wcgmvgf.js} +13 -11
  122. package/dist/{openrouter-images-b6sqqjgn.js → openrouter-images-8yx3487g.js} +1 -1
  123. package/dist/{otel-impl-g8rs4vwr.js → otel-impl-hnqm9mxq.js} +2 -2
  124. package/dist/pi-logo-animation-e9a0t68z.js +1003 -0
  125. package/dist/{pi-mono-adapter-n0me28wt.js → pi-mono-adapter-d6sqm9wr.js} +301 -78
  126. package/dist/{pricing-refresh-0v77385v.js → pricing-refresh-y60z64pm.js} +14 -13
  127. package/dist/quickjs-r1pq2107.wasm +0 -0
  128. package/dist/{rbac-roles-krjyazbj.js → rbac-roles-8pa0754b.js} +13 -11
  129. package/dist/{render-v2-3wjsp9aw.js → render-v2-418s1vj7.js} +13 -11
  130. package/dist/{runtime-ktjysacx.js → runtime-3pknc168.js} +5 -4
  131. package/dist/{setup-yeqryke6.js → setup-yghdkj4x.js} +2 -2
  132. package/dist/{typesafe-system-one-96960jxp.js → typesafe-system-one-2ef30pec.js} +2 -2
  133. package/dist/{virtual-modules-dzdeq1sf.js → virtual-modules-jcceevt5.js} +8 -6
  134. package/dist/{work-objects-3pakpkw0.js → work-objects-j1sz20v9.js} +34 -32
  135. package/dist/{worker-85testgj.js → worker-d4gcvkjt.js} +52 -50
  136. package/dist/{x-n3e0v3xa.js → x-f1gt1pz3.js} +2 -2
  137. package/openapi.json +587 -19
  138. package/package.json +16 -15
  139. package/src/azure-devops/api.ts +121 -0
  140. package/src/azure-devops/auth.ts +78 -0
  141. package/src/azure-devops/handlers.ts +413 -0
  142. package/src/azure-devops/index.ts +27 -0
  143. package/src/azure-devops/templates.ts +108 -0
  144. package/src/azure-devops/types.ts +90 -0
  145. package/src/be/db/api-keys.ts +70 -0
  146. package/src/be/db/tasks/read.ts +1 -1
  147. package/src/be/db/tasks/write.ts +16 -0
  148. package/src/be/db-client.ts +11 -2
  149. package/src/be/db-retention.ts +95 -32
  150. package/src/be/db.ts +187 -24
  151. package/src/be/memory/constants.ts +16 -0
  152. package/src/be/memory/graph-expansion.ts +8 -1
  153. package/src/be/memory/index-content.ts +20 -1
  154. package/src/be/memory/key-guard.ts +93 -0
  155. package/src/be/memory/key-paths.ts +103 -0
  156. package/src/be/memory/providers/sqlite-store.ts +203 -60
  157. package/src/be/memory/raters/llm-summarizer.ts +10 -4
  158. package/src/be/memory/raters/llm.ts +15 -5
  159. package/src/be/memory/raters/store.ts +6 -3
  160. package/src/be/memory/raters/types.ts +11 -0
  161. package/src/be/memory/reranker.ts +38 -7
  162. package/src/be/memory/types.ts +12 -1
  163. package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
  164. package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
  165. package/src/be/migrations/190_memory_rating_model.sql +7 -0
  166. package/src/be/migrations/191_context_versions_previous_index.sql +8 -0
  167. package/src/be/model-tier-keys.ts +16 -1
  168. package/src/be/model-tier-resolution.ts +63 -18
  169. package/src/be/model-validation.ts +86 -10
  170. package/src/be/pricing-normalize.ts +4 -2
  171. package/src/be/scripts/typecheck.ts +4 -3
  172. package/src/be/seed/runner.ts +18 -12
  173. package/src/be/seed/types.ts +1 -0
  174. package/src/be/seed-pricing.ts +16 -0
  175. package/src/be/seed-scripts/catalog/task-failure-audit.ts +26 -26
  176. package/src/be/swarm-config-guard.ts +13 -1
  177. package/src/commands/claude-managed-setup.ts +33 -31
  178. package/src/commands/codex-login.ts +67 -0
  179. package/src/commands/credential-outcome-report.ts +191 -0
  180. package/src/commands/provider-credentials.ts +28 -15
  181. package/src/commands/rate-limit-outcome.ts +34 -4
  182. package/src/commands/runner.ts +479 -228
  183. package/src/commands/seat-mismatch-report.ts +46 -0
  184. package/src/e2b/env.ts +2 -0
  185. package/src/extensions/apply-task-create.ts +27 -1
  186. package/src/extensions/catalog.generated.json +17 -0
  187. package/src/extensions/contract-types.generated.ts +1 -1
  188. package/src/github/handlers.ts +10 -74
  189. package/src/github/reactions.ts +119 -138
  190. package/src/github/task-reactions.ts +34 -11
  191. package/src/heartbeat/heartbeat.ts +15 -2
  192. package/src/hooks/hook.ts +1 -1
  193. package/src/http/agents.ts +15 -2
  194. package/src/http/api-keys.ts +140 -2
  195. package/src/http/codex-oauth-keep-warm.ts +3 -5
  196. package/src/http/core.ts +12 -0
  197. package/src/http/extensions.ts +51 -27
  198. package/src/http/index.ts +4 -0
  199. package/src/http/mcp-user.ts +8 -13
  200. package/src/http/mcp.ts +19 -12
  201. package/src/http/memory.ts +91 -18
  202. package/src/http/poll.ts +9 -1
  203. package/src/http/schedules.ts +17 -2
  204. package/src/http/script-runs.ts +12 -0
  205. package/src/http/session-data.ts +6 -4
  206. package/src/http/stats.ts +1 -0
  207. package/src/http/status.ts +15 -1
  208. package/src/http/tasks.ts +80 -8
  209. package/src/http/users.ts +1 -1
  210. package/src/http/webhooks.ts +120 -2
  211. package/src/http/workflows.ts +11 -0
  212. package/src/mcp-client/http-client.ts +5 -0
  213. package/src/memory/recall-query.ts +74 -0
  214. package/src/prompts/base-prompt.ts +15 -4
  215. package/src/prompts/session-templates.ts +22 -1
  216. package/src/providers/acp-adapter.ts +1 -0
  217. package/src/providers/claude-adapter.ts +98 -21
  218. package/src/providers/claude-managed-adapter.ts +30 -2
  219. package/src/providers/claude-sdk-session.ts +3 -1
  220. package/src/providers/codex-adapter.ts +11 -11
  221. package/src/providers/codex-skill-resolver.ts +6 -0
  222. package/src/providers/devin-adapter.ts +1 -0
  223. package/src/providers/dsh-adapter.ts +205 -7
  224. package/src/providers/harness-version.ts +83 -7
  225. package/src/providers/opencode-adapter.ts +77 -4
  226. package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
  227. package/src/providers/pi-codemode-runtime.ts +17 -0
  228. package/src/providers/pi-mono-adapter.ts +387 -83
  229. package/src/providers/pi-mono-extension.ts +17 -2
  230. package/src/providers/pricing-sources.md +5 -1
  231. package/src/providers/reasoning-effort.ts +10 -2
  232. package/src/providers/types.ts +9 -1
  233. package/src/rbac/legacy-policy.ts +20 -0
  234. package/src/rbac/permissions.ts +15 -0
  235. package/src/scripts-runtime/types/stdlib.d.ts +13 -5
  236. package/src/scripts-runtime/types/swarm-extension.d.ts +4 -2
  237. package/src/scripts-runtime/types/swarm-sdk.d.ts +13 -5
  238. package/src/slack/assistant.ts +13 -0
  239. package/src/slack/render-v2.ts +87 -37
  240. package/src/slack/session-status.ts +541 -0
  241. package/src/tasks/context-key.ts +38 -1
  242. package/src/tasks/sibling-awareness.ts +3 -2
  243. package/src/telemetry.ts +5 -4
  244. package/src/templates.d.ts +6 -0
  245. package/src/tests/api-key-auth-failure-route.test.ts +142 -0
  246. package/src/tests/api-key-tracking.test.ts +368 -1
  247. package/src/tests/azure-devops-auth.test.ts +100 -0
  248. package/src/tests/azure-devops-handlers.test.ts +324 -0
  249. package/src/tests/azure-devops-webhook-malformed.test.ts +96 -0
  250. package/src/tests/base-prompt.test.ts +40 -2
  251. package/src/tests/claude-adapter.test.ts +58 -0
  252. package/src/tests/claude-default-route.test.ts +260 -0
  253. package/src/tests/claude-managed-adapter.test.ts +47 -0
  254. package/src/tests/claude-managed-setup.test.ts +48 -0
  255. package/src/tests/claude-prompt-body-once.test.ts +117 -0
  256. package/src/tests/claude-routes-e2e.test.ts +316 -0
  257. package/src/tests/codex-auth-failure.test.ts +52 -0
  258. package/src/tests/codex-login.test.ts +116 -0
  259. package/src/tests/context-versions-retention.test.ts +314 -0
  260. package/src/tests/core-auth.test.ts +7 -0
  261. package/src/tests/credential-exhaustion-policy.test.ts +118 -3
  262. package/src/tests/credential-outcome-report.test.ts +171 -0
  263. package/src/tests/credential-status-api.test.ts +103 -2
  264. package/src/tests/db-retention.test.ts +14 -14
  265. package/src/tests/db-task-read-contract.test.ts +6 -0
  266. package/src/tests/db-task-write-contract.test.ts +533 -1
  267. package/src/tests/docs-index-footprint.test.js +3 -3
  268. package/src/tests/dokploy-deploy.test.ts +315 -0
  269. package/src/tests/dsh-adapter.test.ts +134 -2
  270. package/src/tests/dsh-usage.test.ts +153 -0
  271. package/src/tests/explicit-model-validation.test.ts +253 -2
  272. package/src/tests/extensions-catalog.test.ts +1 -0
  273. package/src/tests/extensions-example-deploy-awareness.test.ts +301 -0
  274. package/src/tests/extensions-http.test.ts +137 -10
  275. package/src/tests/extensions-mcp-tools.test.ts +28 -0
  276. package/src/tests/extensions-pre-task.test.ts +73 -0
  277. package/src/tests/fetch-resolved-env.test.ts +70 -0
  278. package/src/tests/fixtures/azure-devops/pull-request-commented.json +129 -0
  279. package/src/tests/fixtures/azure-devops/pull-request-created.json +88 -0
  280. package/src/tests/fixtures/fake-claude-routes.ts +229 -0
  281. package/src/tests/github-handlers-reactions.test.ts +486 -0
  282. package/src/tests/github-reactions.test.ts +280 -0
  283. package/src/tests/hang-watchdog-worker.ts +47 -0
  284. package/src/tests/hang-watchdog.test.ts +45 -0
  285. package/src/tests/hang-watchdog.ts +30 -0
  286. package/src/tests/harness-cli-version.test.ts +62 -0
  287. package/src/tests/harness-model-guard.test.ts +123 -0
  288. package/src/tests/harness-version.test.ts +70 -3
  289. package/src/tests/http-api-integration.test.ts +21 -0
  290. package/src/tests/http-task-write-ownership.test.ts +195 -0
  291. package/src/tests/internal-ai/complete-structured.test.ts +94 -0
  292. package/src/tests/internal-ai/credentials.test.ts +22 -0
  293. package/src/tests/internal-ai/models.test.ts +36 -2
  294. package/src/tests/internal-ai/schema-parity.test.ts +25 -1
  295. package/src/tests/internal-ai/summarize-session.test.ts +23 -7
  296. package/src/tests/mcp-user-route.test.ts +133 -0
  297. package/src/tests/memory-fts-populate.test.ts +139 -0
  298. package/src/tests/memory-http-recall-gating.test.ts +125 -1
  299. package/src/tests/memory-key-paths.test.ts +1055 -0
  300. package/src/tests/memory-rate-endpoint.test.ts +66 -0
  301. package/src/tests/memory-rater-llm-summarizer.test.ts +2 -1
  302. package/src/tests/memory-rater-llm.test.ts +73 -0
  303. package/src/tests/memory-rater-store.test.ts +31 -0
  304. package/src/tests/memory-reranker.test.ts +40 -0
  305. package/src/tests/model-rate-limit-windows.test.ts +19 -0
  306. package/src/tests/model-tier-resolution.test.ts +92 -0
  307. package/src/tests/opencode-adapter.test.ts +214 -2
  308. package/src/tests/opencode-plugin.test.ts +4 -0
  309. package/src/tests/pi-codemode.test.ts +324 -0
  310. package/src/tests/pi-mono-adapter.test.ts +3 -0
  311. package/src/tests/pi-native-mcp.test.ts +193 -0
  312. package/src/tests/pi-tool-exposure.test.ts +374 -0
  313. package/src/tests/preload.ts +9 -0
  314. package/src/tests/prompt-template-session.test.ts +5 -3
  315. package/src/tests/provider-registration.test.ts +0 -2
  316. package/src/tests/providers/pi-cost.test.ts +190 -4
  317. package/src/tests/rate-limit-event.test.ts +65 -0
  318. package/src/tests/rate-limit-outcome.test.ts +90 -0
  319. package/src/tests/rbac-engine.test.ts +41 -2
  320. package/src/tests/runner-key-completion-outcome.test.ts +112 -0
  321. package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
  322. package/src/tests/runner-registration-retry.test.ts +149 -0
  323. package/src/tests/runner-slot-reconcile.test.ts +199 -0
  324. package/src/tests/script-connections.test.ts +8 -0
  325. package/src/tests/seat-mismatch-report.test.ts +85 -0
  326. package/src/tests/seed-scripts.test.ts +49 -0
  327. package/src/tests/seed.test.ts +25 -0
  328. package/src/tests/session-costs-recompute-all-providers.test.ts +23 -0
  329. package/src/tests/slack-render-v2.test.ts +96 -0
  330. package/src/tests/slack-session-status.test.ts +626 -0
  331. package/src/tests/status.test.ts +29 -0
  332. package/src/tests/steering-promotion.test.ts +51 -0
  333. package/src/tests/store-progress-ownership.test.ts +136 -0
  334. package/src/tests/subscription-plans.test.ts +28 -0
  335. package/src/tests/task-citations.test.ts +3 -2
  336. package/src/tests/task-reactions.test.ts +29 -6
  337. package/src/tests/vcs-azure-devops.test.ts +73 -0
  338. package/src/tests/webhooks-task-blocked.test.ts +23 -0
  339. package/src/tests/workflow-agent-task.test.ts +30 -0
  340. package/src/tests/workflow-llm.test.ts +14 -0
  341. package/src/tests/workflow-triggers-v2.test.ts +320 -0
  342. package/src/tools/context-diff.ts +20 -0
  343. package/src/tools/extension-install.ts +27 -16
  344. package/src/tools/memory-edit.ts +47 -3
  345. package/src/tools/memory-search.ts +13 -1
  346. package/src/tools/memory-store.ts +55 -3
  347. package/src/tools/schedules/create-schedule.ts +1 -1
  348. package/src/tools/schedules/patch-schedule.ts +16 -3
  349. package/src/tools/schedules/update-schedule.ts +16 -3
  350. package/src/tools/send-task.ts +10 -7
  351. package/src/tools/store-progress.ts +49 -4
  352. package/src/tools/task-action.ts +2 -1
  353. package/src/tools/workflows/create-workflow.ts +8 -2
  354. package/src/tools/workflows/update-workflow.ts +8 -2
  355. package/src/types.ts +54 -7
  356. package/src/utils/codex-auth-failure.ts +34 -0
  357. package/src/utils/credentials.test.ts +31 -1
  358. package/src/utils/credentials.ts +92 -39
  359. package/src/utils/dsh-usage.ts +53 -0
  360. package/src/utils/error-tracker.ts +80 -0
  361. package/src/utils/harness-cli-version.ts +6 -4
  362. package/src/utils/internal-ai/complete-structured.ts +26 -3
  363. package/src/utils/internal-ai/credentials.ts +23 -9
  364. package/src/utils/internal-ai/index.ts +8 -1
  365. package/src/utils/internal-ai/models.ts +34 -8
  366. package/src/utils/internal-ai/summarize-session.ts +22 -8
  367. package/src/utils/model-rate-limit-windows.ts +12 -0
  368. package/src/utils/runtime-model-catalog.ts +15 -0
  369. package/src/utils/secret-scrubber.ts +8 -0
  370. package/src/utils/spawn-model-guard.ts +43 -0
  371. package/src/utils/subscription-plans.ts +22 -0
  372. package/src/vcs/azure-devops.ts +80 -0
  373. package/src/vcs/index.ts +5 -1
  374. package/src/vcs/types.ts +2 -2
  375. package/src/workflows/executors/agent-task.ts +7 -6
  376. package/src/workflows/model-validation.ts +45 -0
  377. package/src/workflows/patch-definition.ts +5 -0
  378. package/src/workflows/triggers.ts +85 -0
  379. package/templates/community/open-prompt-showreel/PLAYBOOK.md +336 -0
  380. package/templates/community/open-prompt-showreel/README.md +33 -0
  381. package/templates/community/open-prompt-showreel/example/tla-races/facts.ts +145 -0
  382. package/templates/community/open-prompt-showreel/example/tla-races/facts.v3.json +140 -0
  383. package/templates/community/open-prompt-showreel/example/tla-races/music.mist-city.json +8 -0
  384. package/templates/community/open-prompt-showreel/example/tla-races/reel.html +805 -0
  385. package/templates/community/open-prompt-showreel/example/tla-races/smoke.sh +48 -0
  386. package/templates/community/open-prompt-showreel/lead-prompt.md +89 -0
  387. package/templates/community/open-prompt-showreel/setup.sh +167 -0
  388. package/templates/community/open-prompt-showreel/skills/motion-design-replication/SKILL.md +85 -0
  389. package/templates/community/open-prompt-showreel/skills/motion-design-video-analysis/SKILL.md +115 -0
  390. package/templates/community/open-prompt-showreel/skills/open-prompt-showreel/SKILL.md +147 -0
  391. package/templates/community/open-prompt-showreel/skills/video-generation/SKILL.md +195 -0
  392. package/templates/community/open-prompt-showreel/tools/fetch-music.sh +65 -0
  393. package/templates/community/open-prompt-showreel/tools/music-analyze.py +69 -0
  394. package/templates/community/open-prompt-showreel/tools/music-current.sh +34 -0
  395. package/templates/community/open-prompt-showreel/tools/music-cut.sh +36 -0
  396. package/templates/community/open-prompt-showreel/tools/music-refine.py +39 -0
  397. package/templates/community/open-prompt-showreel/tools/render.mjs +157 -0
  398. package/templates/community/open-prompt-showreel/tools/sheet.sh +44 -0
  399. package/templates/community/open-prompt-showreel/tools/web-compress.sh +17 -0
  400. package/templates/community/open-prompt-showreel/tools/x-reencode.sh +21 -0
  401. package/templates/extensions/README.md +1 -0
  402. package/templates/extensions/deploy-awareness/README.md +28 -0
  403. package/templates/extensions/deploy-awareness/hooks.ts +152 -0
  404. package/templates/extensions/deploy-awareness/manifest.json +8 -0
  405. package/templates/skills/memory/config.json +2 -2
  406. package/templates/skills/memory/content.md +59 -5
  407. package/templates/skills/swarm-extensions/config.json +1 -1
  408. package/templates/skills/swarm-extensions/content.md +28 -5
  409. package/templates/skills/workflow-iterate/content.md +1 -0
  410. package/dist/boot-seeds-h7c32hvm.js +0 -27
  411. package/dist/cli-2djtr51b.js +0 -48
  412. package/dist/cli-7zttzetj.js +0 -324
  413. package/dist/cli-j490egb5.js +0 -5632
  414. package/dist/cli-m4excttz.js +0 -333481
  415. package/dist/index-bjmjh9h4.js +0 -29
@@ -0,0 +1,336 @@
1
+ # Open-Prompt Motion-Graphics Videos, Made by Your Swarm
2
+
3
+ An Agent-Swarm playbook template for making design-led motion-graphics videos (showreels, eval comparisons, "we found N bugs" stories, launch clips) from one open prompt. One Claude Code worker on Opus at max effort designs, codes, renders and critiques the whole video in a scratch directory. The Lead briefs it, reviews the frames, and relays the result.
4
+
5
+ The worked example is a 24-second video about a model checker finding race conditions in Agent Swarm itself. It is on the docs page for this playbook, with its contact sheet.
6
+
7
+ ## What You Get
8
+
9
+ - A 15-30 second video, 1920x1080 (and 1080x1350 for feeds), h264 yuv420p, with music you are allowed to use, a credit line on the end card, and the full attribution in the post.
10
+ - Every number on screen read live from a named source into `facts.json` right before render.
11
+ - The real Agent Swarm logo on the end card, never a redrawn one.
12
+ - A contact sheet per critique round, so a reviewer sees what the maker saw.
13
+ - Delivery to agent-fs, with a share link per mp4.
14
+
15
+ ## Template Files
16
+
17
+ `templates/community/open-prompt-showreel/`:
18
+
19
+ - `PLAYBOOK.md`: this playbook.
20
+ - `README.md`: quick start.
21
+ - `lead-prompt.md`: the one-time setup prompt, the per-video brief, and the feedback-round brief.
22
+ - `setup.sh`: idempotent toolchain setup for a worker container.
23
+ - `tools/`: `render.mjs`, `sheet.sh`, `music-analyze.py`, `music-refine.py`, `music-cut.sh`, `music-current.sh`, `fetch-music.sh`, `x-reencode.sh`, `web-compress.sh`.
24
+ - `skills/`: sanitized copies of `open-prompt-showreel`, `video-generation`, `motion-design-video-analysis`, `motion-design-replication`.
25
+ - `example/tla-races/`: the finished reel (`reel.html`), the facts script (`facts.ts`), the facts it produced (`facts.v3.json`), and `smoke.sh`.
26
+
27
+ ## Roles
28
+
29
+ | Role | Does |
30
+ |---|---|
31
+ | Lead | Writes the brief from the requester's words, sets model and effort, reviews frames before relaying, relays the links. |
32
+ | Maker | A Claude Code harness worker (provider `claude`) on the full worker image. Runs on `claude-opus-5-5` at `max` effort. Writes the facts script, the reel, the music cut; renders; critiques; delivers. |
33
+
34
+ Codex, pi and other harnesses were not tried for this workflow. Do not route it to them without a test run.
35
+
36
+ ## Setup, Start to Finish
37
+
38
+ Everything below was run in a worker container (x86_64, node 22.23.3, bun 1.4.0, python 3.12.3) unless it is marked **not run**.
39
+
40
+ ### 1. What the worker image already has
41
+
42
+ | Tool | In the stock full worker image | Notes |
43
+ |---|---|---|
44
+ | node, npm, bun, git, gh, curl, tar | yes | |
45
+ | python3 + venv + pip | yes | system `pip install` is blocked by PEP 668; use a venv |
46
+ | numpy | **no** | `setup.sh` installs it in a venv |
47
+ | ffmpeg 7.0.2 (static) | yes | has libx264, aac, `xstack`, `atempo`, `loudnorm`, `ass`; **no `drawtext`, no `ffprobe`** |
48
+ | playwright 1.58.0 | yes, `/opt/global-deps-full/node_modules` | |
49
+ | Chromium 145 | yes, `/opt/playwright` | `PLAYWRIGHT_BROWSERS_PATH` is already set |
50
+ | agent-fs CLI 0.15.0 | yes | |
51
+
52
+ ### 2. Install the template on the maker
53
+
54
+ ```bash
55
+ git clone --depth 1 --filter=blob:none --sparse https://github.com/desplega-ai/agent-swarm.git /tmp/agent-swarm-tpl
56
+ git -C /tmp/agent-swarm-tpl sparse-checkout set templates/community/open-prompt-showreel
57
+ mkdir -p /workspace/personal/showreel-toolkit
58
+ cp -r /tmp/agent-swarm-tpl/templates/community/open-prompt-showreel/. /workspace/personal/showreel-toolkit/
59
+ bash /workspace/personal/showreel-toolkit/setup.sh
60
+ ```
61
+
62
+ `/workspace/personal` is the path that survives a container restart. Do not install into `$HOME`.
63
+
64
+ `setup.sh` does, in order, and skips whatever is already there:
65
+
66
+ 1. Checks node, npm, bun, python3, curl, tar.
67
+ 2. Checks ffmpeg has libx264; otherwise installs the pinned static build (version and sha256 match `Dockerfile.worker`).
68
+ 3. Installs `ffprobe` from the same tarball (`INSTALL_FFPROBE=0` to skip).
69
+ 4. Checks Playwright and launches Chromium once; otherwise `npm install playwright@1.58.0` and `playwright install chromium`.
70
+ 5. Creates `.venv` and installs numpy.
71
+ 6. Downloads Space Grotesk, JetBrains Mono and Instrument Serif Italic from `google/fonts` (SIL OFL) and checks each against a pinned sha256.
72
+ 7. Downloads the Agent Swarm logo from agent-swarm.dev and from `apps/ui/public/logo.png` on main, and warns if they differ.
73
+ 8. Copies `tools/`, `example/` and itself, and writes `env.sh` (PATH, fonts, logo, Playwright paths).
74
+
75
+ Measured on a clean directory: 17.6 s with ffprobe, numpy and fonts. A second run took 0.9 s and installed nothing.
76
+
77
+ ### 3. Re-run setup at every container start
78
+
79
+ Append to the maker's `setupScript` (the `update-profile` tool, field `setupScript`). Keep what is already there.
80
+
81
+ ```bash
82
+ # Showreel toolchain: idempotent, no-ops when already installed.
83
+ [ -f /workspace/personal/showreel-toolkit/setup.sh ] && bash /workspace/personal/showreel-toolkit/setup.sh >> /workspace/personal/showreel-toolkit/setup.log 2>&1 || true
84
+ ```
85
+
86
+ **Not run** for this playbook: changing a live profile. The `setup.sh` line it calls was run from a clean directory.
87
+
88
+ ### 4. Prove the toolchain with the smoke test
89
+
90
+ ```bash
91
+ . /workspace/personal/showreel-toolkit/env.sh
92
+ cd /workspace/personal/showreel-toolkit/example/tla-races && bash smoke.sh
93
+ ```
94
+
95
+ It renders the example reel to a 16-frame contact sheet and a 2-second mp4 at 960x540 with a generated tone as the audio track. It checks the pixel format and the AAC stream, then renders once more with a broken audio file and checks that `render.mjs` exits non-zero and leaves no mp4. Expected lines: `OK: yuv420p 960x540`, `OK: aac audio`, `OK: failed encode exits non-zero`. It needs no network, no `gh`, no agent-fs. It took 8.8 s.
96
+
97
+ ### 5. Install the skills
98
+
99
+ For each `skills/<name>/SKILL.md`, a Lead calls `skill-create` with the file content and `scope: "swarm"`, then `skill-install` on the maker (and `open-prompt-showreel` on itself).
100
+
101
+ **Not run** for this playbook: `skill-create` writes to a live swarm. The four files do parse with the repo's `parseSkillContent`, which is what `skill-create` calls, and each name matches its directory.
102
+
103
+ `swarm-studio` and `studio-api`, the fixed-template skills that the original `open-prompt-showreel` skill points at, are not shipped. They sit on a private repo and a hosted renderer. If your swarm has a template toolkit, route fixed-template asks there.
104
+
105
+ ### 6. Model and effort
106
+
107
+ Set both on every maker task. They are fields of `send-task`:
108
+
109
+ ```text
110
+ model: claude-opus-5-5
111
+ effort: max
112
+ agentId: <maker>
113
+ ```
114
+
115
+ Every brief ends with a gate: "If you are not on claude-opus-5-5, stop and say so instead of rendering."
116
+
117
+ ### 7. agent-fs delivery
118
+
119
+ The maker uploads to `thoughts/<maker-agent-id>/videos/<slug>/` (versions as `<slug>-v2`, `<slug>-v3`; never overwrite). After each non-trivial write, check the size: `agent-fs stat <path> --json | jq '.size'`.
120
+
121
+ ```bash
122
+ agent-fs --org <ORG_ID> --drive <DRIVE_ID> write thoughts/<maker-agent-id>/videos/<slug>/<file>.mp4 --file ./out/<file>.mp4
123
+ agent-fs --org <ORG_ID> --drive <DRIVE_ID> share-create thoughts/<maker-agent-id>/videos/<slug>/<file>.mp4 --expires-in 604800 --json
124
+ ```
125
+
126
+ A share link opens a player with no login and expires in 7 days at most. Report the url, the share id and the expiry. A raw signed URL downloads instead of playing, and agent-fs serves a raw `.mp4` as `application/octet-stream`. For a byte-exact copy back down: `agent-fs download <path> -o <file>` (`cat` is paginated and truncates).
127
+
128
+ **Not run** for this playbook: `write` to a real drive and `share-create` (it publishes a public link). `write --file`, `share-create` and `signed-url --inline` flags were checked against the CLI's `--help`. `agent-fs download` was run on the example master.
129
+
130
+ ## How One Video Gets Made
131
+
132
+ ```text
133
+ Lead brief -> facts.ts -> reel.html -> render.mjs stills -> contact sheet -> fix -> (x3) -> music cut -> render -> agent-fs -> Lead review -> relay
134
+ ```
135
+
136
+ ### Brief
137
+
138
+ One task, one open prompt. The brief template is in `lead-prompt.md`. Do not write a storyboard, shot list, palette or template for the maker; earlier templated cuts read as generic. Hard rules go in, design rules stay out:
139
+
140
+ - **Content rule:** every number or claim on screen comes from a named source and matches it exactly. Anything not in the source stays off screen. Fewer numbers is fine.
141
+ - **Real brand:** the logo is `apps/ui/public/logo.png`, never a redrawn mark.
142
+ - **Music:** CC0 or CC BY only. Short credit on the end card; source link, license link and the changes made in the output.
143
+ - **Process:** a contact sheet looked at least 3 times.
144
+
145
+ ### Facts before design
146
+
147
+ The maker writes `facts.ts` and runs `bun facts.ts > facts.json`. The reel reads `facts.json`; nothing is hard-coded. The example script `example/tla-races/facts.ts` reads:
148
+
149
+ | On screen | Source | How it is read |
150
+ |---|---|---|
151
+ | Whether each of 7 workflow races is red or green | `src/tests/workflow-tla-races.test.ts` on `origin/main` | `git show origin/main:<file>`; a regex over `test.failing("CXn: ...")` (red) vs `test("CXn: ...")` (green) |
152
+ | Which PR fixes which race | open and merged PRs | a PR fixes `CXn` when its diff replaces `test.failing("CXn:` with `test("CXn:`; merged PRs come from `gh api commits/<sha>/pulls` over the file's history |
153
+ | 3 heartbeat bugs: fix PR, violated invariant, 5-step trace, regression test | merged PRs whose body cites a TLC trace in `specs/tla/heartbeat` | `gh pr list --search`, a regex over the PR body, the added test from `gh api pulls/N/files`, then `git cat-file -e origin/main:<test>` |
154
+ | State counts and status views | a model-checking write-up | table rows read from the markdown; carried over and labelled `carried` when the write-up is not configured |
155
+ | Short SHA in the footer | `git rev-parse --short origin/main` | |
156
+
157
+ Rules the maker follows:
158
+
159
+ - A fact turns green only if main says so. A PR that is merged in the brief but not on main stays red.
160
+ - If the source disagrees with the brief, the source wins and the output says so.
161
+ - Draft numbers never ship. If the source is not ready, build the design on draft facts, `defer-task` with `wakeOn:{event:"settled",taskIds:[...]}`, and re-read on wake.
162
+
163
+ ### The reel
164
+
165
+ One `reel.html` draws each frame on a `<canvas>` as a pure function of the frame number. It exposes two functions, which `tools/render.mjs` drives with Playwright:
166
+
167
+ ```js
168
+ window.boot = async (facts, fonts, logo) => { /* load fonts + logo, stash facts */ };
169
+ window.frame = (f) => { render(f); return cv.toDataURL("image/png"); };
170
+ ```
171
+
172
+ Stills for contact sheets:
173
+
174
+ ```bash
175
+ . /workspace/personal/showreel-toolkit/env.sh
176
+ TK=/workspace/personal/showreel-toolkit
177
+ cd /workspace/personal/<slug>
178
+ export REEL=reel.html FACTS=facts.json MUSIC=music/<track>.json # the credit file from fetch-music.sh is enough for stills
179
+ bash $TK/tools/sheet.sh 1920 1080 r1 # 16 frames, 4x4 -> r1.png
180
+ COLS=5 bash $TK/tools/sheet.sh 1920 1080 t1 "180,183,185,187,189,190,191,192,193,194,196,198,200,203,206,209,212,215,220,230"
181
+ ```
182
+
183
+ The full render, with audio. It needs `music/current.json` from the Music section below (it carries the `wav` field; a `--music` file without `wav` renders a silent video and `render.mjs` warns). `render.mjs` creates the output directory and exits non-zero if ffmpeg fails:
184
+
185
+ ```bash
186
+ node $TK/tools/render.mjs --reel reel.html --w 1920 --h 1080 --facts facts.json \
187
+ --logo $LOGO --fonts "$FONTS" --music music/current.json --out out/<slug>-1920x1080.mp4
188
+ ```
189
+
190
+ `render.mjs` pipes PNG frames to ffmpeg with `-pix_fmt yuv420p`, h264 High, `-crf 16`, AAC 192k and `+faststart`. The 720-frame 1080p render of the example took about 3.5 minutes (frame 600 at 167 s).
191
+
192
+ Layout is responsive to `?w=&h=`, so the 4:5 cut is a second render at 1080x1350, not a crop.
193
+
194
+ ### The critique loop
195
+
196
+ At least three rounds. Each round:
197
+
198
+ 1. Render a 16-frame contact sheet and **look at it** (Read the PNG).
199
+ 2. Render a 20-frame transition strip around each cut to catch pops and empty frames.
200
+ 3. Write down what is weak: dead space, text too small for a phone, a cramped 4:5, jumpy easing, a hit off the beat. Fix it.
201
+
202
+ The last round checks every on-screen number against `facts.json` and the source, and opens the end-card frame alone at full size to confirm the logo and the credit line. Keep every round's sheet (`r1-16x9.png`, `r2-16x9.png`, ...).
203
+
204
+ ### Music
205
+
206
+ CC0 or CC BY only. No NC (it is a company post), no ND (the track is cut and stretched) and no SA (it would carry over to the video). Pick a fresh track for each video.
207
+
208
+ ```bash
209
+ bash $TK/tools/fetch-music.sh incompetech Voltaic music # Kevin MacLeod, CC BY 4.0
210
+ bash $TK/tools/fetch-music.sh url https://opengameart.org/sites/default/files/mist_city.ogg music \
211
+ "Mist City" Section7 "CC BY 4.0" https://opengameart.org/content/mist-city
212
+ bash $TK/tools/fetch-music.sh url https://example.com/x.mp3 music "Nope" Someone "CC BY-NC 4.0" https://example.com # refused
213
+ ```
214
+
215
+ Both downloads were byte-identical to the files used for the example video. The script accepts only CC0 1.0 and CC BY 1.0 to 4.0, matched case-insensitively (`cc-by 4.0` works), and refuses NC, ND, SA, "All rights reserved" and any string it does not know. It cannot check the source page, so read the license there yourself. It writes `music/<slug>.json` with the license link and a short `credit` for the end card.
216
+
217
+ To time the cut to the music, find tempo, beat phase and the drop, then stretch the track so the beats sit on the frame grid and the drop lands on the key reveal. Then write `music/current.json`, the file the render reads:
218
+
219
+ ```bash
220
+ python3 $TK/tools/music-analyze.py music/voltaic.mp3 # bpm, beat phase, top drop candidates (json)
221
+ python3 $TK/tools/music-refine.py music/voltaic.mp3 <bpm> <drop_sec>
222
+ bash $TK/tools/music-cut.sh music/voltaic.mp3 <bpm> <drop_sec> music/cut.wav # TARGET_BPM=120 DROP_AT=14 DUR=24 by default
223
+ bash $TK/tools/music-current.sh music/voltaic.json music/cut.wav # writes music/current.json
224
+ ```
225
+
226
+ At 30 fps and 120 BPM a beat is 15 frames. On the example track the analyzer returned 120.2 BPM and a strongest drop candidate at 143.3 s. `music-cut.sh` fades in 0.3 s and out 1.7 s, and `DIP=1` ducks the track just before the drop.
227
+
228
+ `music-current.sh` copies the track json to `music/current.json` and adds `"wav": "music/cut.wav"` (which `render.mjs` muxes in as AAC) and `attribution`. CC BY needs a link to the source, a link to the license and a notice that the track was changed, so the post carries the whole `attribution` string, for example:
229
+
230
+ ```text
231
+ "Mist City" by Section7, https://opengameart.org/content/mist-city, licensed under CC BY 4.0, https://creativecommons.org/licenses/by/4.0/. Changes: trimmed, time-stretched to the video's beat grid, faded in and out, and loudness-normalized.
232
+ ```
233
+
234
+ Set `MODIFIED="..."` to describe the edits more exactly. The short `credit` stays on the end card.
235
+
236
+ ### Re-encode for X when the source is full range
237
+
238
+ The reel pipeline already writes limited-range `yuv420p`. A clip from a screen recording, a JPEG pipeline or `remotion render` may be tagged `yuvj420p` (full range, "pc"). Re-encode it:
239
+
240
+ ```bash
241
+ bash $TK/tools/x-reencode.sh in.mp4 out.mp4
242
+ # before: h264 (High) ... yuvj420p(pc, progressive) ...
243
+ # after: h264 (High) ... yuv420p(tv, bt709, progressive) ...
244
+ ```
245
+
246
+ The ffmpeg side is reproduced: `remotion render` 4.0.532 wrote `yuvj420p(pc, bt470bg)` by default and `yuv420p(tv, bt709)` with `--color-space=bt709`. That X mishandles full-range files is a field report from our own posting runs; it was not reproduced here.
247
+
248
+ ### Web copy for docs and PRs
249
+
250
+ ```bash
251
+ bash $TK/tools/web-compress.sh out/<slug>-1920x1080.mp4 docs-site/public/<dir> <name> 16.5
252
+ ```
253
+
254
+ Writes a 1280-wide mp4 and a poster jpg. The 17.8 MB master of the example became 2.6 MB. Commit the small copy, not the master. A signed agent-fs URL expires; a file in the repo does not.
255
+
256
+ ### Lead review before relay
257
+
258
+ The maker's output is not the finish line. Before relaying, the Lead:
259
+
260
+ 1. Opens the contact sheet and looks at the frames, not just the file list.
261
+ 2. Opens the end-card frame: real logo, credit line present and readable.
262
+ 3. Checks every on-screen number against the maker's `facts.json`, and `facts.json` against the source named in the brief.
263
+ 4. Checks the ffprobe line: `h264`, `yuv420p`, exactly 1920x1080 (and 1080x1350), duration as briefed, and an `aac` audio stream when the video has music. (`ffprobe` after `setup.sh`, or `ffmpeg -i` stderr in the stock image.)
264
+ 5. Plays the share link once.
265
+ 6. Relays the share link, the share id and the expiry, plus the `attribution` string from `music/current.json` (source link, license link, changes made). The maker never posts.
266
+
267
+ ## Known Gotchas
268
+
269
+ | Gotcha | What happens | What to do |
270
+ |---|---|---|
271
+ | No `ffprobe` in the worker image | `ffprobe: command not found` | `ffmpeg -hide_banner -i f.mp4 2>&1 \| grep Stream`, or run `setup.sh` (installs 7.0.2 ffprobe). Remotion's compositor package also bundles one. |
272
+ | No `drawtext` filter | `ffmpeg -version` lists libfreetype, but `drawtext` is not registered | Draw text on the canvas, or burn captions with the `ass` filter |
273
+ | numpy not installed, system pip refused | `externally-managed-environment` (PEP 668) | `python3 -m venv` under `/workspace/personal`; `setup.sh` does it |
274
+ | PNG frames encoded by libx264 without `-pix_fmt` | The output is `yuv444p` (High 4:4:4 Predictive), which many players and uploaders reject | Always `-pix_fmt yuv420p`. `render.mjs` does. |
275
+ | Full-range `yuvj420p` | Rendered by `remotion render` by default | `--color-space=bt709`, or `x-reencode.sh` |
276
+ | Remotion's bundled ffmpeg is stripped | no `fps`, `tile`, `hstack` filters | Use the image's `/usr/local/bin/ffmpeg` for analysis and sheets |
277
+ | agent-fs serves `.mp4` as `application/octet-stream` | A content-type check on a raw link fails | Use a `share-create` link. Do not retry the content-type check. |
278
+ | Signed URLs expire and default to attachment | A `<video src>` pointing at one downloads instead of playing, and stops working at the expiry | Never use one as a public URL. For a page, commit a compressed copy. `signed-url --inline` renders in a browser tab but still expires. |
279
+ | `agent-fs cat` truncates | Pagination hides the tail of a big file | `agent-fs download <path> -o <file>` for exact bytes |
280
+ | Container restart mid-render | The scratch dir is the only thing that survives | Keep it in `/workspace/personal/<slug>`, name it in the first progress note, upload an interim cut before the long render |
281
+ | Brief and source disagree | The brief says four PRs merged; main has five | The source wins, and the output says so |
282
+ | Invented logo | The first example cut drew its own mark | Use `apps/ui/public/logo.png`; `setup.sh` downloads it and warns if the live site differs |
283
+ | `--music` json without `wav` | The mp4 has no audio; `render.mjs` warns on stderr | Run `music-current.sh` after `music-cut.sh`, render with `music/current.json`, check for an `aac` stream |
284
+ | Music the requester already heard | Same track on every video | Pick a fresh track; offer two alternates when music is in question |
285
+
286
+ ## Example: TLA+ Races Showreel
287
+
288
+ ### The prompt (abridged)
289
+
290
+ > Make a dynamic 15 to 25 second motion graphics video about how a model checker (TLA+) found real race conditions in Agent Swarm, each one pinned as a failing test and then turned green by a fix PR. Make it your showreel: show what an incredible motion designer you are. Go all out.
291
+ >
292
+ > The only content rule: every number, PR and race on screen is read live right before the final render. Anything you cannot confirm stays off screen. Fewer facts is fine: design-led, not a ledger.
293
+
294
+ Plus the standard how-to: ad hoc scratch dir, a contact sheet at least three times, CC-licensed music with a credit, and the gate that stops a maker who is not on `claude-opus-5-5`.
295
+
296
+ ### What is in the video
297
+
298
+ Title "TLA+", then a particle field counting the model checker's reachable states up to 1,349,396. A counterexample trace draws across a timeline (heartbeat bug 1, the invariant `NoLiveKill` violated). Ten tiles appear red under "Each race, pinned as a failing test". A scan sweep turns them green under "Then a fix PR turns it green." The counter, labelled "today" at 1,349,396, collapses to 4,243 under "a simpler design, proposed." A hexagon morphs into the real logo and the end card reads "Races found by a model checker", with the music credit.
299
+
300
+ ### How the facts got on screen
301
+
302
+ `example/tla-races/facts.ts` produced `facts.v3.json`: the 7 workflow races (`CX1` to `CX7`) and 3 heartbeat bugs, their fix PRs (#1666, #1668, #1669, #1670, #1673, #1675, #1678), the invariants and traces, and `origin/main` at `e3c90a88`. The reel draws only a handful of those facts.
303
+
304
+ ### v1 to v3
305
+
306
+ | Cut | Why it was made | What it showed |
307
+ |---|---|---|
308
+ | v1 | Fixed-template cuts of the same subject read as generic. The brief became "Opus, max effort, one open prompt, your own design". | 24 s, 16:9 and 4:5. Music: "Voltaic", Kevin MacLeod, CC BY 4.0. 7 workflow races red (their fix PRs were still open); 2 heartbeat bugs green. The end card used a hexagon mark the maker had drawn. |
309
+ | v2 | Feedback: use the correct logo at the end, and use different music. | Real logo asset on the end card; "Mist City", Section7, CC BY 4.0, with two alternate tracks (CC0 and CC BY 3.0) rendered for choice. A first attempt died in the final render when the worker session was lost; the retry found the scratch dir and finished in 8 minutes. Facts re-read: only the first race fix had merged. |
310
+ | v3 | Once the remaining fix PRs merged, the cut for the post. "Only the facts change." | Same design, music and logo. `facts.ts` re-run against `origin/main`: all 10 tiles green. The brief listed four merged PRs, main also had the heartbeat bug 2 fix, so the reel says "ALL 10 FIXED ON MAIN". Source over brief. |
311
+
312
+ ### Final video
313
+
314
+ `tla-races-showreel-v3`: 24.00 s, h264 High, yuv420p, 1920x1080, 30 fps, AAC. The docs page embeds a 1280-wide, 2.6 MB copy and a poster. The master stays in agent-fs.
315
+
316
+ ## Verification Log
317
+
318
+ | Step | Result |
319
+ |---|---|
320
+ | Sparse clone of the template, `cp`, `setup.sh`, `smoke.sh` | Run against the PR branch (`--branch`); the commands in this playbook target `main`, which has the template once this merges. 14.6 s for `setup.sh`, then `OK: yuv420p 960x540`. |
321
+ | `setup.sh` on a clean directory, stock image | Run. Installs ffprobe, numpy, fonts, logo. Re-run installs nothing. |
322
+ | `FORCE_LOCAL_FFMPEG=1 FORCE_LOCAL_PLAYWRIGHT=1 bash setup.sh` | Run. Pinned ffmpeg tarball passed its sha256; `npm install playwright@1.58.0`; `playwright install chromium` fetched 622 MB; smoke test passed on that toolchain. |
323
+ | `python3 -m venv` + `pip install numpy pillow` | Run. numpy 2.5.3, pillow 12.3.0. |
324
+ | System `pip install numpy` | Run. Refused (PEP 668). |
325
+ | Fonts from `google/fonts` | Run. Byte-identical to the files used in the example. |
326
+ | `fetch-music.sh` for Voltaic and Mist City | Run. Byte-identical to the files used. NC, ND, SA, "All rights reserved", a bare `CC BY` and unknown licenses refused; `cc-by 4.0`, `CC0` and `cc by 2.5` normalize and get the right license link. |
327
+ | `music-analyze.py`, `music-cut.sh` | Run on Voltaic (120.2 BPM; cut is 24.00 s). Run on Mist City (129.2 BPM, `atempo=0.929`). |
328
+ | `music-current.sh`, then a 720-frame render at 960x540 with `music/current.json` | Run on the Mist City cut. ffprobe: h264 `yuv420p` 960x540 and an `aac` stream, both 24.00 s. |
329
+ | `render.mjs` with a broken wav, with ffmpeg killed mid-render, with no ffmpeg on `PATH` | Run. Each exits 1 with the ffmpeg exit status in the message and leaves no partial mp4. |
330
+ | `x-reencode.sh` | Run on a `yuvj420p` clip and on `remotion render` output. Both came out `yuv420p(tv, bt709)`. |
331
+ | `web-compress.sh` | Run on the example master (17.8 MB to 2.6 MB). |
332
+ | Remotion `npm install` and a 30-frame render with `--browser-executable=/opt/playwright/chromium` | Run. Remotion 4.0.532, 14 s. |
333
+ | `smoke.sh` | Run. `OK: yuv420p 960x540`, `OK: aac audio`, `OK: failed encode exits non-zero`. |
334
+ | `apt-get install` of Chromium system libraries | **Not run.** The worker has no root. |
335
+ | `npx remotion studio` | **Not run.** |
336
+ | `skill-create`, `skill-install`, `update-profile`, `agent-fs share-create` | **Not run.** They change a live swarm or publish a link. |
@@ -0,0 +1,33 @@
1
+ # Open-Prompt Motion-Graphics Videos, Made by Your Swarm
2
+
3
+ Community template for making design-led motion-graphics videos with an agent swarm: one Claude Code worker on `claude-opus-5-5` at `max` effort designs, codes, renders and critiques the video from one open prompt. The worked example is a 24-second TLA+ races showreel.
4
+
5
+ Files:
6
+
7
+ - `PLAYBOOK.md`: end-to-end setup, the per-video procedure, gotchas, the TLA+ example and the verification log.
8
+ - `lead-prompt.md`: the one-time setup prompt, the per-video brief, and the feedback-round brief.
9
+ - `setup.sh`: idempotent toolchain setup for a worker container (ffmpeg/ffprobe, Playwright + Chromium, numpy venv, fonts, logo).
10
+ - `tools/`: `render.mjs` (canvas reel to stills or mp4), `sheet.sh` (contact sheet and transition strip), `music-analyze.py`, `music-refine.py`, `music-cut.sh`, `music-current.sh`, `fetch-music.sh`, `x-reencode.sh`, `web-compress.sh`.
11
+ - `skills/`: sanitized copies of `open-prompt-showreel`, `video-generation`, `motion-design-video-analysis`, `motion-design-replication`. Install them with `skill-create` and `skill-install`.
12
+ - `example/tla-races/`: `reel.html` (the finished reel), `facts.ts` and `facts.v3.json` (the live facts), `smoke.sh` (toolchain test).
13
+
14
+ Quick start, on the maker worker:
15
+
16
+ ```bash
17
+ git clone --depth 1 --filter=blob:none --sparse https://github.com/desplega-ai/agent-swarm.git /tmp/agent-swarm-tpl
18
+ git -C /tmp/agent-swarm-tpl sparse-checkout set templates/community/open-prompt-showreel
19
+ mkdir -p /workspace/personal/showreel-toolkit
20
+ cp -r /tmp/agent-swarm-tpl/templates/community/open-prompt-showreel/. /workspace/personal/showreel-toolkit/
21
+ bash /workspace/personal/showreel-toolkit/setup.sh
22
+
23
+ . /workspace/personal/showreel-toolkit/env.sh
24
+ cd /workspace/personal/showreel-toolkit/example/tla-races && bash smoke.sh # expect: OK: yuv420p 960x540
25
+ ```
26
+
27
+ Then send the maker a task built from the brief in `lead-prompt.md`, with `model: claude-opus-5-5` and `effort: max`.
28
+
29
+ The docs page is [Open-Prompt Showreel Videos](https://docs.agent-swarm.dev/docs/playbooks/open-prompt-showreel).
30
+
31
+ Not shipped: `swarm-studio` and `studio-api`. The original skill points at them for fixed-template creatives; they depend on a private repository and a hosted renderer.
32
+
33
+ Music: use CC0 or CC BY tracks only. Put a short credit on the end card and the full attribution in the output: source link, license link and the changes made (`tools/music-current.sh` writes it to `music/current.json`, next to the `wav` the render needs). The TLA+ example credits ["Mist City"](https://opengameart.org/content/mist-city) by Section7 ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)), trimmed and time-stretched.
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Every fact the reel puts on screen, read live from its source right before render.
3
+ *
4
+ * - workflow races: `test.failing` vs `test` in src/tests/workflow-tla-races.test.ts on origin/main
5
+ * - workflow fix PRs: open + merged PRs whose diff flips a CX `test.failing` to `test` (gh pr diff)
6
+ * - heartbeat fixes: merged PRs whose body cites a TLC trace in specs/tla/heartbeat, and the test
7
+ * file each one added (gh api)
8
+ * - heartbeat numbers: a model-checking write-up (markdown table + "### Bug N" sections). Set
9
+ * HEARTBEAT_DOC (agent-fs path) and AGENT_FS_ORG to read it live. Without them the `heartbeat`
10
+ * block is carried over from FALLBACK_FACTS and marked `carried`, so the run is still honest.
11
+ *
12
+ * Usage (from the repo clone): bun facts.ts > facts.json
13
+ * Needs: bun, git, an authenticated gh, and agent-fs only when HEARTBEAT_DOC is set.
14
+ */
15
+ import { readFileSync } from "node:fs";
16
+ import { $ } from "bun";
17
+
18
+ const R = process.env.REPO ?? "desplega-ai/agent-swarm";
19
+ const repo = process.env.REPO_DIR ?? process.cwd();
20
+ const FILE = "src/tests/workflow-tla-races.test.ts";
21
+ const DOC = process.env.HEARTBEAT_DOC;
22
+ const ORG = process.env.AGENT_FS_ORG;
23
+ const FALLBACK_FACTS = process.env.FALLBACK_FACTS ?? `${import.meta.dir}/facts.v3.json`;
24
+
25
+ await $`git -C ${repo} fetch -q origin main`;
26
+ const sha = (await $`git -C ${repo} rev-parse --short origin/main`.text()).trim();
27
+ const src = await $`git -C ${repo} show origin/main:${FILE}`.text();
28
+ const cx = [...src.matchAll(/^\s*test(\.failing)?\("(CX\d+): ([^"]+)"/gm)].map((m) => ({
29
+ id: m[2],
30
+ title: m[3],
31
+ failing: !!m[1],
32
+ }));
33
+
34
+ type Pr = { number: number; state: string; files: { path: string }[] };
35
+ const open = JSON.parse(
36
+ await $`gh pr list -R ${R} --state open --limit 10000 --json number,state,files`.text(),
37
+ ) as Pr[];
38
+ const shas = (
39
+ await $`gh api --paginate ${`repos/${R}/commits?path=${FILE}&per_page=100`} --jq ${".[].sha"}`.text()
40
+ )
41
+ .split("\n")
42
+ .filter(Boolean);
43
+ const merged = new Map<number, Pr>();
44
+ for (const c of shas) {
45
+ const ps = JSON.parse(await $`gh api repos/${R}/commits/${c}/pulls`.text()) as {
46
+ number: number;
47
+ merged_at: string | null;
48
+ }[];
49
+ for (const p of ps) {
50
+ if (p.merged_at)
51
+ merged.set(p.number, { number: p.number, state: "MERGED", files: [{ path: FILE }] });
52
+ }
53
+ }
54
+ const flips: { id: string; pr: number; state: string }[] = [];
55
+ for (const p of [...open, ...merged.values()].filter((p) => p.files.some((f) => f.path === FILE))) {
56
+ const diff = await $`gh pr diff ${p.number} -R ${R}`.text();
57
+ for (const m of diff.matchAll(/^\+\s*test\("(CX\d+):/gm)) {
58
+ const id = m[1];
59
+ if (!id) continue;
60
+ const flipped = new RegExp(`^-\\s*test\\.failing\\("${id}:`, "m").test(diff);
61
+ if (flipped && !flips.some((x) => x.id === id && x.pr === p.number)) {
62
+ flips.push({ id, pr: p.number, state: p.state });
63
+ }
64
+ }
65
+ }
66
+
67
+ // Heartbeat numbers: live from the write-up when configured, otherwise carried and labelled.
68
+ let heartbeat: Record<string, unknown>;
69
+ let bugs: { n: number; title: string; violates: string | null }[] = [];
70
+ if (DOC && ORG) {
71
+ const doc = await $`agent-fs --org ${ORG} cat ${DOC}`.text();
72
+ const row = (label: string) => {
73
+ const line = doc.split("\n").find((l) => l.startsWith(`| ${label}`));
74
+ if (!line) throw new Error(`row not found: ${label}`);
75
+ return line
76
+ .split("|")
77
+ .slice(2, 5)
78
+ .map((c) => c.replace(/\*\*/g, "").trim());
79
+ };
80
+ bugs = [...doc.matchAll(/^### Bug (\d+): (.+)\n\n([^\n]+)/gm)].map((m) => ({
81
+ n: Number(m[1]),
82
+ title: m[2] ?? "",
83
+ violates: m[3]?.match(/Violates `(\w+)`/)?.[1] ?? null,
84
+ }));
85
+ heartbeat = {
86
+ bugs,
87
+ states: row("Reachable states"),
88
+ statusViews: row("Task-status views in the graph"),
89
+ rows: row("Rows per unit of work").map((s) => s.replace("up to ", "")),
90
+ };
91
+ } else {
92
+ const carried = JSON.parse(readFileSync(FALLBACK_FACTS, "utf8")).heartbeat;
93
+ bugs = carried.bugs;
94
+ heartbeat = { ...carried, carried: true };
95
+ }
96
+
97
+ // Heartbeat fix PRs: merged, body cites a TLC trace in specs/tla/heartbeat. Matched to a bug by title keyword.
98
+ const hbPrs = JSON.parse(
99
+ await $`gh pr list -R ${R} --state merged --search ${"TLC trace heartbeat in:body"} --limit 50 --json number,title,state,mergedAt,body`.text(),
100
+ ) as { number: number; title: string; state: string; mergedAt: string; body: string }[];
101
+ const KEY: Record<number, RegExp> = { 1: /stall/i, 2: /resume/i, 3: /reboot/i };
102
+ const hbFixes = [];
103
+ for (const p of hbPrs.filter((p) => p.body.includes("TLC trace (`specs/tla/heartbeat`"))) {
104
+ const bug = bugs.find((b) => KEY[b.n]?.test(p.title));
105
+ if (!bug) continue;
106
+ const trace = p.body.match(
107
+ /TLC trace \(`specs\/tla\/heartbeat`, `(\w+)`[^)]*\): (?:… )?([^\n]+?)\.?\n/,
108
+ );
109
+ const added = JSON.parse(await $`gh api ${`repos/${R}/pulls/${p.number}/files`}`.text()) as {
110
+ filename: string;
111
+ status: string;
112
+ }[];
113
+ const test =
114
+ added.find((f) => f.status === "added" && f.filename.startsWith("src/tests/"))?.filename ??
115
+ null;
116
+ const onMain = test
117
+ ? (await $`git -C ${repo} cat-file -e origin/main:${test}`.nothrow()).exitCode === 0
118
+ : false;
119
+ hbFixes.push({
120
+ bug: bug.n,
121
+ bugTitle: bug.title,
122
+ pr: p.number,
123
+ state: p.state,
124
+ mergedAt: p.mergedAt,
125
+ invariant: trace?.[1] ?? null,
126
+ trace: trace?.[2]?.split(" → ") ?? null,
127
+ test,
128
+ testOnMain: onMain,
129
+ });
130
+ }
131
+
132
+ console.log(
133
+ JSON.stringify(
134
+ {
135
+ readAt: new Date().toISOString(),
136
+ mainSha: sha,
137
+ cx,
138
+ flips: flips.sort((a, b) => a.id.localeCompare(b.id)),
139
+ hbFixes: hbFixes.sort((a, b) => a.bug - b.bug),
140
+ heartbeat,
141
+ },
142
+ null,
143
+ 2,
144
+ ),
145
+ );
@@ -0,0 +1,140 @@
1
+ {
2
+ "readAt": "2026-09-28T21:27:02.622Z",
3
+ "mainSha": "e3c90a88",
4
+ "cx": [
5
+ {
6
+ "id": "CX1",
7
+ "title": "the retry poller does not re-execute a failed step of a cancelled run",
8
+ "failing": false
9
+ },
10
+ {
11
+ "id": "CX2",
12
+ "title": "a user retry does not re-execute a branch the live walk is still running",
13
+ "failing": false
14
+ },
15
+ {
16
+ "id": "CX2",
17
+ "title": "a user retry does not re-dispatch a branch whose waiting checkpoint lost to the run failing",
18
+ "failing": false
19
+ },
20
+ {
21
+ "id": "CX3",
22
+ "title": "an async branch completing does not fire the join while a sibling branch is still executing",
23
+ "failing": false
24
+ },
25
+ {
26
+ "id": "CX4",
27
+ "title": "two branch completions racing to the join execute it once",
28
+ "failing": false
29
+ },
30
+ {
31
+ "id": "CX5",
32
+ "title": "heartbeat recovery leaves a step that is pending retry to the retry poller",
33
+ "failing": false
34
+ },
35
+ {
36
+ "id": "CX6",
37
+ "title": "heartbeat recovery does not walk a run whose trigger is still resolving inputs",
38
+ "failing": false
39
+ },
40
+ {
41
+ "id": "CX7",
42
+ "title": "the walk finalizer does not complete a run while the retry poller is executing a step",
43
+ "failing": false
44
+ }
45
+ ],
46
+ "flips": [
47
+ {
48
+ "id": "CX1",
49
+ "pr": 1666,
50
+ "state": "MERGED"
51
+ },
52
+ {
53
+ "id": "CX2",
54
+ "pr": 1675,
55
+ "state": "MERGED"
56
+ },
57
+ {
58
+ "id": "CX3",
59
+ "pr": 1673,
60
+ "state": "MERGED"
61
+ },
62
+ {
63
+ "id": "CX4",
64
+ "pr": 1675,
65
+ "state": "MERGED"
66
+ },
67
+ {
68
+ "id": "CX5",
69
+ "pr": 1678,
70
+ "state": "MERGED"
71
+ },
72
+ {
73
+ "id": "CX6",
74
+ "pr": 1675,
75
+ "state": "MERGED"
76
+ },
77
+ {
78
+ "id": "CX7",
79
+ "pr": 1673,
80
+ "state": "MERGED"
81
+ }
82
+ ],
83
+ "hbFixes": [
84
+ {
85
+ "bug": 1,
86
+ "bugTitle": "stall classifier supersedes a live task",
87
+ "pr": 1668,
88
+ "state": "MERGED",
89
+ "mergedAt": "2026-09-28T16:50:26Z",
90
+ "invariant": "NoLiveKill",
91
+ "trace": ["PollStart", "Age", "HbRead", "Progress", "HbWrite"],
92
+ "test": "src/tests/heartbeat-stall-cas.test.ts",
93
+ "testOnMain": true
94
+ },
95
+ {
96
+ "bug": 2,
97
+ "bugTitle": "API crash between supersede and resume",
98
+ "pr": 1670,
99
+ "state": "MERGED",
100
+ "mergedAt": "2026-09-28T21:11:02Z",
101
+ "invariant": "SupersededGetsResume",
102
+ "trace": ["HbRead", "HbWrite", "ApiCrash", "ApiBoot", "stutter"],
103
+ "test": "src/tests/heartbeat-orphan-resume.test.ts",
104
+ "testOnMain": true
105
+ },
106
+ {
107
+ "bug": 3,
108
+ "bugTitle": "reboot sweep fails a live worker",
109
+ "pr": 1669,
110
+ "state": "MERGED",
111
+ "mergedAt": "2026-09-28T16:50:26Z",
112
+ "invariant": "NoLiveKill",
113
+ "trace": ["PollStart", "RegisterSession", "ApiCrash", "ApiBoot", "RebootFail"],
114
+ "test": "src/tests/heartbeat-reboot-live.test.ts",
115
+ "testOnMain": true
116
+ }
117
+ ],
118
+ "heartbeat": {
119
+ "bugs": [
120
+ {
121
+ "n": 1,
122
+ "title": "stall classifier supersedes a live task",
123
+ "violates": "NoLiveKill"
124
+ },
125
+ {
126
+ "n": 2,
127
+ "title": "API crash between supersede and resume",
128
+ "violates": "SupersededHasResume"
129
+ },
130
+ {
131
+ "n": 3,
132
+ "title": "reboot sweep fails a live worker",
133
+ "violates": "NoLiveKill"
134
+ }
135
+ ],
136
+ "states": ["1,349,396", "544,912", "4,243"],
137
+ "statusViews": ["64", "n/a", "6"],
138
+ "rows": ["3", "3", "1"]
139
+ }
140
+ }