@bradygaster/squad-cli 0.9.1 → 0.9.2-insider.6

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 (320) hide show
  1. package/README.md +329 -329
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +10 -10
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/config.d.ts +12 -0
  6. package/dist/cli/commands/config.d.ts.map +1 -0
  7. package/dist/cli/commands/config.js +157 -0
  8. package/dist/cli/commands/config.js.map +1 -0
  9. package/dist/cli/commands/consult.d.ts.map +1 -1
  10. package/dist/cli/commands/consult.js +9 -4
  11. package/dist/cli/commands/consult.js.map +1 -1
  12. package/dist/cli/commands/copilot.d.ts.map +1 -1
  13. package/dist/cli/commands/copilot.js +8 -7
  14. package/dist/cli/commands/copilot.js.map +1 -1
  15. package/dist/cli/commands/doctor.d.ts.map +1 -1
  16. package/dist/cli/commands/doctor.js +50 -17
  17. package/dist/cli/commands/doctor.js.map +1 -1
  18. package/dist/cli/commands/economy.d.ts.map +1 -1
  19. package/dist/cli/commands/economy.js +3 -2
  20. package/dist/cli/commands/economy.js.map +1 -1
  21. package/dist/cli/commands/export.d.ts.map +1 -1
  22. package/dist/cli/commands/export.js +22 -16
  23. package/dist/cli/commands/export.js.map +1 -1
  24. package/dist/cli/commands/extract.d.ts.map +1 -1
  25. package/dist/cli/commands/extract.js +14 -10
  26. package/dist/cli/commands/extract.js.map +1 -1
  27. package/dist/cli/commands/import.d.ts.map +1 -1
  28. package/dist/cli/commands/import.js +21 -18
  29. package/dist/cli/commands/import.js.map +1 -1
  30. package/dist/cli/commands/init-remote.d.ts.map +1 -1
  31. package/dist/cli/commands/init-remote.js +7 -6
  32. package/dist/cli/commands/init-remote.js.map +1 -1
  33. package/dist/cli/commands/link.d.ts.map +1 -1
  34. package/dist/cli/commands/link.js +11 -10
  35. package/dist/cli/commands/link.js.map +1 -1
  36. package/dist/cli/commands/migrate.d.ts.map +1 -1
  37. package/dist/cli/commands/migrate.js +19 -18
  38. package/dist/cli/commands/migrate.js.map +1 -1
  39. package/dist/cli/commands/personal.d.ts.map +1 -1
  40. package/dist/cli/commands/personal.js +57 -65
  41. package/dist/cli/commands/personal.js.map +1 -1
  42. package/dist/cli/commands/plugin.d.ts.map +1 -1
  43. package/dist/cli/commands/plugin.js +8 -7
  44. package/dist/cli/commands/plugin.js.map +1 -1
  45. package/dist/cli/commands/rc.d.ts.map +1 -1
  46. package/dist/cli/commands/rc.js +19 -12
  47. package/dist/cli/commands/rc.js.map +1 -1
  48. package/dist/cli/commands/schedule.d.ts.map +1 -1
  49. package/dist/cli/commands/schedule.js +6 -5
  50. package/dist/cli/commands/schedule.js.map +1 -1
  51. package/dist/cli/commands/start.d.ts.map +1 -1
  52. package/dist/cli/commands/start.js +18 -11
  53. package/dist/cli/commands/start.js.map +1 -1
  54. package/dist/cli/commands/streams.d.ts.map +1 -1
  55. package/dist/cli/commands/streams.js +3 -2
  56. package/dist/cli/commands/streams.js.map +1 -1
  57. package/dist/cli/commands/upstream.d.ts.map +1 -1
  58. package/dist/cli/commands/upstream.js +23 -19
  59. package/dist/cli/commands/upstream.js.map +1 -1
  60. package/dist/cli/commands/watch/capabilities/board.d.ts +22 -0
  61. package/dist/cli/commands/watch/capabilities/board.d.ts.map +1 -0
  62. package/dist/cli/commands/watch/capabilities/board.js +121 -0
  63. package/dist/cli/commands/watch/capabilities/board.js.map +1 -0
  64. package/dist/cli/commands/watch/capabilities/budget-check.d.ts +29 -0
  65. package/dist/cli/commands/watch/capabilities/budget-check.d.ts.map +1 -0
  66. package/dist/cli/commands/watch/capabilities/budget-check.js +38 -0
  67. package/dist/cli/commands/watch/capabilities/budget-check.js.map +1 -0
  68. package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts +52 -0
  69. package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts.map +1 -0
  70. package/dist/cli/commands/watch/capabilities/circuit-breaker.js +152 -0
  71. package/dist/cli/commands/watch/capabilities/circuit-breaker.js.map +1 -0
  72. package/dist/cli/commands/watch/capabilities/decision-hygiene.d.ts +14 -0
  73. package/dist/cli/commands/watch/capabilities/decision-hygiene.d.ts.map +1 -0
  74. package/dist/cli/commands/watch/capabilities/decision-hygiene.js +72 -0
  75. package/dist/cli/commands/watch/capabilities/decision-hygiene.js.map +1 -0
  76. package/dist/cli/commands/watch/capabilities/execute.d.ts +33 -0
  77. package/dist/cli/commands/watch/capabilities/execute.d.ts.map +1 -0
  78. package/dist/cli/commands/watch/capabilities/execute.js +156 -0
  79. package/dist/cli/commands/watch/capabilities/execute.js.map +1 -0
  80. package/dist/cli/commands/watch/capabilities/health-check.d.ts +29 -0
  81. package/dist/cli/commands/watch/capabilities/health-check.d.ts.map +1 -0
  82. package/dist/cli/commands/watch/capabilities/health-check.js +139 -0
  83. package/dist/cli/commands/watch/capabilities/health-check.js.map +1 -0
  84. package/dist/cli/commands/watch/capabilities/heartbeat.d.ts +48 -0
  85. package/dist/cli/commands/watch/capabilities/heartbeat.d.ts.map +1 -0
  86. package/dist/cli/commands/watch/capabilities/heartbeat.js +115 -0
  87. package/dist/cli/commands/watch/capabilities/heartbeat.js.map +1 -0
  88. package/dist/cli/commands/watch/capabilities/index.d.ts +9 -0
  89. package/dist/cli/commands/watch/capabilities/index.d.ts.map +1 -0
  90. package/dist/cli/commands/watch/capabilities/index.js +40 -0
  91. package/dist/cli/commands/watch/capabilities/index.js.map +1 -0
  92. package/dist/cli/commands/watch/capabilities/lockfile.d.ts +30 -0
  93. package/dist/cli/commands/watch/capabilities/lockfile.d.ts.map +1 -0
  94. package/dist/cli/commands/watch/capabilities/lockfile.js +100 -0
  95. package/dist/cli/commands/watch/capabilities/lockfile.js.map +1 -0
  96. package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts +30 -0
  97. package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts.map +1 -0
  98. package/dist/cli/commands/watch/capabilities/machine-capabilities.js +103 -0
  99. package/dist/cli/commands/watch/capabilities/machine-capabilities.js.map +1 -0
  100. package/dist/cli/commands/watch/capabilities/monitor-email.d.ts +14 -0
  101. package/dist/cli/commands/watch/capabilities/monitor-email.d.ts.map +1 -0
  102. package/dist/cli/commands/watch/capabilities/monitor-email.js +54 -0
  103. package/dist/cli/commands/watch/capabilities/monitor-email.js.map +1 -0
  104. package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts +14 -0
  105. package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts.map +1 -0
  106. package/dist/cli/commands/watch/capabilities/monitor-teams.js +55 -0
  107. package/dist/cli/commands/watch/capabilities/monitor-teams.js.map +1 -0
  108. package/dist/cli/commands/watch/capabilities/post-failure.d.ts +19 -0
  109. package/dist/cli/commands/watch/capabilities/post-failure.d.ts.map +1 -0
  110. package/dist/cli/commands/watch/capabilities/post-failure.js +58 -0
  111. package/dist/cli/commands/watch/capabilities/post-failure.js.map +1 -0
  112. package/dist/cli/commands/watch/capabilities/priority.d.ts +59 -0
  113. package/dist/cli/commands/watch/capabilities/priority.d.ts.map +1 -0
  114. package/dist/cli/commands/watch/capabilities/priority.js +101 -0
  115. package/dist/cli/commands/watch/capabilities/priority.js.map +1 -0
  116. package/dist/cli/commands/watch/capabilities/rate-pool.d.ts +67 -0
  117. package/dist/cli/commands/watch/capabilities/rate-pool.d.ts.map +1 -0
  118. package/dist/cli/commands/watch/capabilities/rate-pool.js +187 -0
  119. package/dist/cli/commands/watch/capabilities/rate-pool.js.map +1 -0
  120. package/dist/cli/commands/watch/capabilities/retro.d.ts +14 -0
  121. package/dist/cli/commands/watch/capabilities/retro.d.ts.map +1 -0
  122. package/dist/cli/commands/watch/capabilities/retro.js +81 -0
  123. package/dist/cli/commands/watch/capabilities/retro.js.map +1 -0
  124. package/dist/cli/commands/watch/capabilities/self-pull.d.ts +14 -0
  125. package/dist/cli/commands/watch/capabilities/self-pull.d.ts.map +1 -0
  126. package/dist/cli/commands/watch/capabilities/self-pull.js +33 -0
  127. package/dist/cli/commands/watch/capabilities/self-pull.js.map +1 -0
  128. package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts +23 -0
  129. package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts.map +1 -0
  130. package/dist/cli/commands/watch/capabilities/stale-reclaim.js +87 -0
  131. package/dist/cli/commands/watch/capabilities/stale-reclaim.js.map +1 -0
  132. package/dist/cli/commands/watch/capabilities/two-pass.d.ts +14 -0
  133. package/dist/cli/commands/watch/capabilities/two-pass.d.ts.map +1 -0
  134. package/dist/cli/commands/watch/capabilities/two-pass.js +66 -0
  135. package/dist/cli/commands/watch/capabilities/two-pass.js.map +1 -0
  136. package/dist/cli/commands/watch/capabilities/wave-dispatch.d.ts +14 -0
  137. package/dist/cli/commands/watch/capabilities/wave-dispatch.d.ts.map +1 -0
  138. package/dist/cli/commands/watch/capabilities/wave-dispatch.js +117 -0
  139. package/dist/cli/commands/watch/capabilities/wave-dispatch.js.map +1 -0
  140. package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts +29 -0
  141. package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts.map +1 -0
  142. package/dist/cli/commands/watch/capabilities/webhook-alerts.js +114 -0
  143. package/dist/cli/commands/watch/capabilities/webhook-alerts.js.map +1 -0
  144. package/dist/cli/commands/watch/config.d.ts +40 -0
  145. package/dist/cli/commands/watch/config.d.ts.map +1 -0
  146. package/dist/cli/commands/watch/config.js +129 -0
  147. package/dist/cli/commands/watch/config.js.map +1 -0
  148. package/dist/cli/commands/watch/index.d.ts +109 -0
  149. package/dist/cli/commands/watch/index.d.ts.map +1 -0
  150. package/dist/cli/commands/watch/index.js +757 -0
  151. package/dist/cli/commands/watch/index.js.map +1 -0
  152. package/dist/cli/commands/watch/registry.d.ts +19 -0
  153. package/dist/cli/commands/watch/registry.d.ts.map +1 -0
  154. package/dist/cli/commands/watch/registry.js +28 -0
  155. package/dist/cli/commands/watch/registry.js.map +1 -0
  156. package/dist/cli/commands/watch/types.d.ts +57 -0
  157. package/dist/cli/commands/watch/types.d.ts.map +1 -0
  158. package/dist/cli/commands/watch/types.js +8 -0
  159. package/dist/cli/commands/watch/types.js.map +1 -0
  160. package/dist/cli/core/cast.d.ts.map +1 -1
  161. package/dist/cli/core/cast.js +15 -19
  162. package/dist/cli/core/cast.js.map +1 -1
  163. package/dist/cli/core/detect-squad-dir.d.ts.map +1 -1
  164. package/dist/cli/core/detect-squad-dir.js +12 -10
  165. package/dist/cli/core/detect-squad-dir.js.map +1 -1
  166. package/dist/cli/core/email-scrub.d.ts.map +1 -1
  167. package/dist/cli/core/email-scrub.js +12 -11
  168. package/dist/cli/core/email-scrub.js.map +1 -1
  169. package/dist/cli/core/gh-cli.d.ts +13 -0
  170. package/dist/cli/core/gh-cli.d.ts.map +1 -1
  171. package/dist/cli/core/gh-cli.js +24 -0
  172. package/dist/cli/core/gh-cli.js.map +1 -1
  173. package/dist/cli/core/init.d.ts +2 -0
  174. package/dist/cli/core/init.d.ts.map +1 -1
  175. package/dist/cli/core/init.js +22 -5
  176. package/dist/cli/core/init.js.map +1 -1
  177. package/dist/cli/core/migrate-directory.d.ts.map +1 -1
  178. package/dist/cli/core/migrate-directory.js +14 -13
  179. package/dist/cli/core/migrate-directory.js.map +1 -1
  180. package/dist/cli/core/migrations.d.ts.map +1 -1
  181. package/dist/cli/core/migrations.js +22 -8
  182. package/dist/cli/core/migrations.js.map +1 -1
  183. package/dist/cli/core/nap.d.ts.map +1 -1
  184. package/dist/cli/core/nap.js +116 -49
  185. package/dist/cli/core/nap.js.map +1 -1
  186. package/dist/cli/core/project-type.d.ts.map +1 -1
  187. package/dist/cli/core/project-type.js +11 -10
  188. package/dist/cli/core/project-type.js.map +1 -1
  189. package/dist/cli/core/team-md.d.ts.map +1 -1
  190. package/dist/cli/core/team-md.js +43 -38
  191. package/dist/cli/core/team-md.js.map +1 -1
  192. package/dist/cli/core/templates.d.ts.map +1 -1
  193. package/dist/cli/core/templates.js +4 -3
  194. package/dist/cli/core/templates.js.map +1 -1
  195. package/dist/cli/core/upgrade.d.ts.map +1 -1
  196. package/dist/cli/core/upgrade.js +68 -55
  197. package/dist/cli/core/upgrade.js.map +1 -1
  198. package/dist/cli/core/version.d.ts.map +1 -1
  199. package/dist/cli/core/version.js +8 -7
  200. package/dist/cli/core/version.js.map +1 -1
  201. package/dist/cli/index.d.ts +1 -1
  202. package/dist/cli/index.d.ts.map +1 -1
  203. package/dist/cli/index.js +1 -1
  204. package/dist/cli/index.js.map +1 -1
  205. package/dist/cli/self-update.d.ts.map +1 -1
  206. package/dist/cli/self-update.js +7 -4
  207. package/dist/cli/self-update.js.map +1 -1
  208. package/dist/cli/shell/agent-name-parser.d.ts +16 -0
  209. package/dist/cli/shell/agent-name-parser.d.ts.map +1 -0
  210. package/dist/cli/shell/agent-name-parser.js +54 -0
  211. package/dist/cli/shell/agent-name-parser.js.map +1 -0
  212. package/dist/cli/shell/commands.d.ts.map +1 -1
  213. package/dist/cli/shell/commands.js +4 -3
  214. package/dist/cli/shell/commands.js.map +1 -1
  215. package/dist/cli/shell/coordinator.d.ts +4 -1
  216. package/dist/cli/shell/coordinator.d.ts.map +1 -1
  217. package/dist/cli/shell/coordinator.js +29 -26
  218. package/dist/cli/shell/coordinator.js.map +1 -1
  219. package/dist/cli/shell/index.d.ts.map +1 -1
  220. package/dist/cli/shell/index.js +33 -35
  221. package/dist/cli/shell/index.js.map +1 -1
  222. package/dist/cli/shell/lifecycle.d.ts +13 -2
  223. package/dist/cli/shell/lifecycle.d.ts.map +1 -1
  224. package/dist/cli/shell/lifecycle.js +26 -13
  225. package/dist/cli/shell/lifecycle.js.map +1 -1
  226. package/dist/cli/shell/session-store.d.ts.map +1 -1
  227. package/dist/cli/shell/session-store.js +16 -12
  228. package/dist/cli/shell/session-store.js.map +1 -1
  229. package/dist/cli/shell/spawn.d.ts +4 -1
  230. package/dist/cli/shell/spawn.d.ts.map +1 -1
  231. package/dist/cli/shell/spawn.js +28 -10
  232. package/dist/cli/shell/spawn.js.map +1 -1
  233. package/dist/cli-entry.js +136 -12
  234. package/dist/cli-entry.js.map +1 -1
  235. package/package.json +8 -4
  236. package/scripts/patch-esm-imports.mjs +105 -105
  237. package/scripts/patch-ink-rendering.mjs +115 -115
  238. package/templates/casting/Futurama.json +9 -9
  239. package/templates/casting-history.json +4 -4
  240. package/templates/casting-policy.json +37 -37
  241. package/templates/casting-reference.md +104 -104
  242. package/templates/casting-registry.json +3 -3
  243. package/templates/ceremonies.md +41 -41
  244. package/templates/charter.md +53 -53
  245. package/templates/constraint-tracking.md +38 -38
  246. package/templates/cooperative-rate-limiting.md +229 -229
  247. package/templates/copilot-instructions.md +46 -46
  248. package/templates/history.md +10 -10
  249. package/templates/identity/now.md +9 -9
  250. package/templates/identity/wisdom.md +15 -15
  251. package/templates/issue-lifecycle.md +412 -412
  252. package/templates/keda-scaler.md +164 -164
  253. package/templates/machine-capabilities.md +74 -74
  254. package/templates/mcp-config.md +90 -90
  255. package/templates/multi-agent-format.md +28 -28
  256. package/templates/orchestration-log.md +27 -27
  257. package/templates/plugin-marketplace.md +49 -49
  258. package/templates/ralph-circuit-breaker.md +313 -313
  259. package/templates/raw-agent-output.md +37 -37
  260. package/templates/roster.md +60 -60
  261. package/templates/routing.md +39 -39
  262. package/templates/run-output.md +50 -50
  263. package/templates/scribe-charter.md +123 -119
  264. package/templates/skill.md +24 -24
  265. package/templates/skills/agent-collaboration/SKILL.md +42 -42
  266. package/templates/skills/agent-conduct/SKILL.md +24 -24
  267. package/templates/skills/architectural-proposals/SKILL.md +151 -151
  268. package/templates/skills/ci-validation-gates/SKILL.md +84 -84
  269. package/templates/skills/cli-wiring/SKILL.md +47 -47
  270. package/templates/skills/client-compatibility/SKILL.md +89 -89
  271. package/templates/skills/cross-machine-coordination/SKILL.md +434 -0
  272. package/templates/skills/cross-squad/SKILL.md +114 -114
  273. package/templates/skills/distributed-mesh/SKILL.md +287 -287
  274. package/templates/skills/distributed-mesh/mesh.json.example +30 -30
  275. package/templates/skills/distributed-mesh/sync-mesh.ps1 +111 -111
  276. package/templates/skills/distributed-mesh/sync-mesh.sh +104 -104
  277. package/templates/skills/docs-standards/SKILL.md +71 -71
  278. package/templates/skills/economy-mode/SKILL.md +114 -114
  279. package/templates/skills/error-recovery/SKILL.md +99 -0
  280. package/templates/skills/external-comms/SKILL.md +329 -329
  281. package/templates/skills/gh-auth-isolation/SKILL.md +183 -183
  282. package/templates/skills/git-workflow/SKILL.md +204 -204
  283. package/templates/skills/github-multi-account/SKILL.md +95 -95
  284. package/templates/skills/history-hygiene/SKILL.md +36 -36
  285. package/templates/skills/humanizer/SKILL.md +105 -105
  286. package/templates/skills/init-mode/SKILL.md +102 -102
  287. package/templates/skills/iterative-retrieval/SKILL.md +165 -0
  288. package/templates/skills/model-selection/SKILL.md +117 -117
  289. package/templates/skills/nap/SKILL.md +24 -24
  290. package/templates/skills/notification-routing/SKILL.md +105 -0
  291. package/templates/skills/personal-squad/SKILL.md +57 -57
  292. package/templates/skills/pr-screenshots/SKILL.md +149 -0
  293. package/templates/skills/ralph-two-pass-scan/SKILL.md +35 -0
  294. package/templates/skills/reflect/SKILL.md +229 -0
  295. package/templates/skills/release-process/SKILL.md +131 -423
  296. package/templates/skills/reskill/SKILL.md +92 -92
  297. package/templates/skills/retro-enforcement/SKILL.md +148 -0
  298. package/templates/skills/reviewer-protocol/SKILL.md +79 -79
  299. package/templates/skills/secret-handling/SKILL.md +200 -200
  300. package/templates/skills/session-recovery/SKILL.md +155 -155
  301. package/templates/skills/squad-conventions/SKILL.md +69 -69
  302. package/templates/skills/test-discipline/SKILL.md +37 -37
  303. package/templates/skills/tiered-memory/SKILL.md +234 -0
  304. package/templates/skills/windows-compatibility/SKILL.md +98 -74
  305. package/templates/{squad.agent.md → squad.agent.md.template} +1316 -1287
  306. package/templates/workflows/squad-ci.yml +24 -24
  307. package/templates/workflows/squad-docs.yml +54 -54
  308. package/templates/workflows/squad-heartbeat.yml +0 -4
  309. package/templates/workflows/squad-insider-release.yml +61 -61
  310. package/templates/workflows/squad-issue-assign.yml +161 -161
  311. package/templates/workflows/squad-label-enforce.yml +181 -181
  312. package/templates/workflows/squad-preview.yml +55 -55
  313. package/templates/workflows/squad-promote.yml +120 -120
  314. package/templates/workflows/squad-release.yml +77 -77
  315. package/templates/workflows/squad-triage.yml +260 -260
  316. package/templates/workflows/sync-squad-labels.yml +169 -169
  317. package/dist/cli/commands/watch.d.ts +0 -18
  318. package/dist/cli/commands/watch.d.ts.map +0 -1
  319. package/dist/cli/commands/watch.js +0 -306
  320. package/dist/cli/commands/watch.js.map +0 -1
@@ -1,313 +1,313 @@
1
- # Ralph Circuit Breaker — Model Rate Limit Fallback
2
-
3
- > Classic circuit breaker pattern (Hystrix / Polly / Resilience4j) applied to Copilot model selection.
4
- > When the preferred model hits rate limits, Ralph automatically degrades to free-tier models, then self-heals.
5
-
6
- ## Problem
7
-
8
- When running multiple Ralph instances across repos, Copilot model rate limits cause cascading failures.
9
- All Ralphs fail simultaneously when the preferred model (e.g., `claude-sonnet-4.6`) hits quota.
10
-
11
- Premium models burn quota fast:
12
- | Model | Multiplier | Risk |
13
- |-------|-----------|------|
14
- | `claude-sonnet-4.6` | 1x | Moderate with many Ralphs |
15
- | `claude-opus-4.6` | 10x | High |
16
- | `gpt-5.4` | 50x | Very high |
17
- | `gpt-5.4-mini` | **0x** | **Free — unlimited** |
18
- | `gpt-5-mini` | **0x** | **Free — unlimited** |
19
- | `gpt-4.1` | **0x** | **Free — unlimited** |
20
-
21
- ## Circuit Breaker States
22
-
23
- ```
24
- ┌─────────┐ rate limit error ┌────────┐
25
- │ CLOSED │ ───────────────────► │ OPEN │
26
- │ (normal)│ │(fallback)│
27
- └────┬────┘ ◄──────────────── └────┬────┘
28
- │ 2 consecutive │
29
- │ successes │ cooldown expires
30
- │ ▼
31
- │ ┌──────────┐
32
- └───── success ◄──────── │HALF-OPEN │
33
- (close) │ (testing) │
34
- └──────────┘
35
- ```
36
-
37
- ### CLOSED (normal operation)
38
- - Use preferred model from config
39
- - Every successful response confirms circuit stays closed
40
- - On rate limit error → transition to OPEN
41
-
42
- ### OPEN (rate limited — fallback active)
43
- - Fall back through the free-tier model chain:
44
- 1. `gpt-5.4-mini`
45
- 2. `gpt-5-mini`
46
- 3. `gpt-4.1`
47
- - Start cooldown timer (default: 10 minutes)
48
- - When cooldown expires → transition to HALF-OPEN
49
-
50
- ### HALF-OPEN (testing recovery)
51
- - Try preferred model again
52
- - If 2 consecutive successes → transition to CLOSED
53
- - If rate limit error → back to OPEN, reset cooldown
54
-
55
- ## State File: `.squad/ralph-circuit-breaker.json`
56
-
57
- ```json
58
- {
59
- "state": "closed",
60
- "preferredModel": "claude-sonnet-4.6",
61
- "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"],
62
- "currentFallbackIndex": 0,
63
- "cooldownMinutes": 10,
64
- "openedAt": null,
65
- "halfOpenSuccesses": 0,
66
- "consecutiveFailures": 0,
67
- "metrics": {
68
- "totalFallbacks": 0,
69
- "totalRecoveries": 0,
70
- "lastFallbackAt": null,
71
- "lastRecoveryAt": null
72
- }
73
- }
74
- ```
75
-
76
- ## PowerShell Functions
77
-
78
- Paste these into your `ralph-watch.ps1` or source them from a shared module.
79
-
80
- ### `Get-CircuitBreakerState`
81
-
82
- ```powershell
83
- function Get-CircuitBreakerState {
84
- param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
85
-
86
- if (-not (Test-Path $StateFile)) {
87
- $default = @{
88
- state = "closed"
89
- preferredModel = "claude-sonnet-4.6"
90
- fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1")
91
- currentFallbackIndex = 0
92
- cooldownMinutes = 10
93
- openedAt = $null
94
- halfOpenSuccesses = 0
95
- consecutiveFailures = 0
96
- metrics = @{
97
- totalFallbacks = 0
98
- totalRecoveries = 0
99
- lastFallbackAt = $null
100
- lastRecoveryAt = $null
101
- }
102
- }
103
- $default | ConvertTo-Json -Depth 3 | Set-Content $StateFile
104
- return $default
105
- }
106
-
107
- return (Get-Content $StateFile -Raw | ConvertFrom-Json)
108
- }
109
- ```
110
-
111
- ### `Save-CircuitBreakerState`
112
-
113
- ```powershell
114
- function Save-CircuitBreakerState {
115
- param(
116
- [object]$State,
117
- [string]$StateFile = ".squad/ralph-circuit-breaker.json"
118
- )
119
-
120
- $State | ConvertTo-Json -Depth 3 | Set-Content $StateFile
121
- }
122
- ```
123
-
124
- ### `Get-CurrentModel`
125
-
126
- Returns the model Ralph should use right now, based on circuit state.
127
-
128
- ```powershell
129
- function Get-CurrentModel {
130
- param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
131
-
132
- $cb = Get-CircuitBreakerState -StateFile $StateFile
133
-
134
- switch ($cb.state) {
135
- "closed" {
136
- return $cb.preferredModel
137
- }
138
- "open" {
139
- # Check if cooldown has expired
140
- if ($cb.openedAt) {
141
- $opened = [DateTime]::Parse($cb.openedAt)
142
- $elapsed = (Get-Date) - $opened
143
- if ($elapsed.TotalMinutes -ge $cb.cooldownMinutes) {
144
- # Transition to half-open
145
- $cb.state = "half-open"
146
- $cb.halfOpenSuccesses = 0
147
- Save-CircuitBreakerState -State $cb -StateFile $StateFile
148
- Write-Host " [circuit-breaker] Cooldown expired. Testing preferred model..." -ForegroundColor Yellow
149
- return $cb.preferredModel
150
- }
151
- }
152
- # Still in cooldown — use fallback
153
- $idx = [Math]::Min($cb.currentFallbackIndex, $cb.fallbackChain.Count - 1)
154
- return $cb.fallbackChain[$idx]
155
- }
156
- "half-open" {
157
- return $cb.preferredModel
158
- }
159
- default {
160
- return $cb.preferredModel
161
- }
162
- }
163
- }
164
- ```
165
-
166
- ### `Update-CircuitBreakerOnSuccess`
167
-
168
- Call after every successful model response.
169
-
170
- ```powershell
171
- function Update-CircuitBreakerOnSuccess {
172
- param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
173
-
174
- $cb = Get-CircuitBreakerState -StateFile $StateFile
175
- $cb.consecutiveFailures = 0
176
-
177
- if ($cb.state -eq "half-open") {
178
- $cb.halfOpenSuccesses++
179
- if ($cb.halfOpenSuccesses -ge 2) {
180
- # Recovery! Close the circuit
181
- $cb.state = "closed"
182
- $cb.openedAt = $null
183
- $cb.halfOpenSuccesses = 0
184
- $cb.currentFallbackIndex = 0
185
- $cb.metrics.totalRecoveries++
186
- $cb.metrics.lastRecoveryAt = (Get-Date).ToString("o")
187
- Save-CircuitBreakerState -State $cb -StateFile $StateFile
188
- Write-Host " [circuit-breaker] RECOVERED — back to preferred model ($($cb.preferredModel))" -ForegroundColor Green
189
- return
190
- }
191
- Save-CircuitBreakerState -State $cb -StateFile $StateFile
192
- Write-Host " [circuit-breaker] Half-open success $($cb.halfOpenSuccesses)/2" -ForegroundColor Yellow
193
- return
194
- }
195
-
196
- # closed state — nothing to do
197
- }
198
- ```
199
-
200
- ### `Update-CircuitBreakerOnRateLimit`
201
-
202
- Call when a model response indicates rate limiting (HTTP 429 or error message containing "rate limit").
203
-
204
- ```powershell
205
- function Update-CircuitBreakerOnRateLimit {
206
- param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
207
-
208
- $cb = Get-CircuitBreakerState -StateFile $StateFile
209
- $cb.consecutiveFailures++
210
-
211
- if ($cb.state -eq "closed" -or $cb.state -eq "half-open") {
212
- # Open the circuit
213
- $cb.state = "open"
214
- $cb.openedAt = (Get-Date).ToString("o")
215
- $cb.halfOpenSuccesses = 0
216
- $cb.currentFallbackIndex = 0
217
- $cb.metrics.totalFallbacks++
218
- $cb.metrics.lastFallbackAt = (Get-Date).ToString("o")
219
- Save-CircuitBreakerState -State $cb -StateFile $StateFile
220
-
221
- $fallbackModel = $cb.fallbackChain[0]
222
- Write-Host " [circuit-breaker] RATE LIMITED — falling back to $fallbackModel (cooldown: $($cb.cooldownMinutes)m)" -ForegroundColor Red
223
- return
224
- }
225
-
226
- if ($cb.state -eq "open") {
227
- # Already open — try next fallback in chain if current one also fails
228
- if ($cb.currentFallbackIndex -lt ($cb.fallbackChain.Count - 1)) {
229
- $cb.currentFallbackIndex++
230
- $nextModel = $cb.fallbackChain[$cb.currentFallbackIndex]
231
- Write-Host " [circuit-breaker] Fallback also limited — trying $nextModel" -ForegroundColor Red
232
- }
233
- # Reset cooldown timer
234
- $cb.openedAt = (Get-Date).ToString("o")
235
- Save-CircuitBreakerState -State $cb -StateFile $StateFile
236
- }
237
- }
238
- ```
239
-
240
- ## Integration with ralph-watch.ps1
241
-
242
- In your Ralph polling loop, wrap the model selection:
243
-
244
- ```powershell
245
- # At the top of your polling loop
246
- $model = Get-CurrentModel
247
-
248
- # When invoking copilot CLI
249
- $result = copilot-cli --model $model ...
250
-
251
- # After the call
252
- if ($result -match "rate.?limit" -or $LASTEXITCODE -eq 429) {
253
- Update-CircuitBreakerOnRateLimit
254
- } else {
255
- Update-CircuitBreakerOnSuccess
256
- }
257
- ```
258
-
259
- ### Full integration example
260
-
261
- ```powershell
262
- # Source the circuit breaker functions
263
- . .squad-templates/ralph-circuit-breaker-functions.ps1
264
-
265
- while ($true) {
266
- $model = Get-CurrentModel
267
- Write-Host "Polling with model: $model"
268
-
269
- try {
270
- # Your existing Ralph logic here, but pass $model
271
- $response = Invoke-RalphCycle -Model $model
272
-
273
- # Success path
274
- Update-CircuitBreakerOnSuccess
275
- }
276
- catch {
277
- if ($_.Exception.Message -match "rate.?limit|429|quota|Too Many Requests") {
278
- Update-CircuitBreakerOnRateLimit
279
- # Retry immediately with fallback model
280
- continue
281
- }
282
- # Other errors — handle normally
283
- throw
284
- }
285
-
286
- Start-Sleep -Seconds $pollInterval
287
- }
288
- ```
289
-
290
- ## Configuration
291
-
292
- Override defaults by editing `.squad/ralph-circuit-breaker.json`:
293
-
294
- | Field | Default | Description |
295
- |-------|---------|-------------|
296
- | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed |
297
- | `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) |
298
- | `cooldownMinutes` | `10` | How long to wait before testing recovery |
299
-
300
- ## Metrics
301
-
302
- The state file tracks operational metrics:
303
-
304
- - **totalFallbacks** — How many times the circuit opened
305
- - **totalRecoveries** — How many times it recovered to preferred model
306
- - **lastFallbackAt** — ISO timestamp of last rate limit event
307
- - **lastRecoveryAt** — ISO timestamp of last successful recovery
308
-
309
- Query metrics with:
310
- ```powershell
311
- $cb = Get-Content .squad/ralph-circuit-breaker.json | ConvertFrom-Json
312
- Write-Host "Fallbacks: $($cb.metrics.totalFallbacks) | Recoveries: $($cb.metrics.totalRecoveries)"
313
- ```
1
+ # Ralph Circuit Breaker — Model Rate Limit Fallback
2
+
3
+ > Classic circuit breaker pattern (Hystrix / Polly / Resilience4j) applied to Copilot model selection.
4
+ > When the preferred model hits rate limits, Ralph automatically degrades to free-tier models, then self-heals.
5
+
6
+ ## Problem
7
+
8
+ When running multiple Ralph instances across repos, Copilot model rate limits cause cascading failures.
9
+ All Ralphs fail simultaneously when the preferred model (e.g., `claude-sonnet-4.6`) hits quota.
10
+
11
+ Premium models burn quota fast:
12
+ | Model | Multiplier | Risk |
13
+ |-------|-----------|------|
14
+ | `claude-sonnet-4.6` | 1x | Moderate with many Ralphs |
15
+ | `claude-opus-4.6` | 10x | High |
16
+ | `gpt-5.4` | 50x | Very high |
17
+ | `gpt-5.4-mini` | **0x** | **Free — unlimited** |
18
+ | `gpt-5-mini` | **0x** | **Free — unlimited** |
19
+ | `gpt-4.1` | **0x** | **Free — unlimited** |
20
+
21
+ ## Circuit Breaker States
22
+
23
+ ```
24
+ ┌─────────┐ rate limit error ┌────────┐
25
+ │ CLOSED │ ───────────────────► │ OPEN │
26
+ │ (normal)│ │(fallback)│
27
+ └────┬────┘ ◄──────────────── └────┬────┘
28
+ │ 2 consecutive │
29
+ │ successes │ cooldown expires
30
+ │ ▼
31
+ │ ┌──────────┐
32
+ └───── success ◄──────── │HALF-OPEN │
33
+ (close) │ (testing) │
34
+ └──────────┘
35
+ ```
36
+
37
+ ### CLOSED (normal operation)
38
+ - Use preferred model from config
39
+ - Every successful response confirms circuit stays closed
40
+ - On rate limit error → transition to OPEN
41
+
42
+ ### OPEN (rate limited — fallback active)
43
+ - Fall back through the free-tier model chain:
44
+ 1. `gpt-5.4-mini`
45
+ 2. `gpt-5-mini`
46
+ 3. `gpt-4.1`
47
+ - Start cooldown timer (default: 10 minutes)
48
+ - When cooldown expires → transition to HALF-OPEN
49
+
50
+ ### HALF-OPEN (testing recovery)
51
+ - Try preferred model again
52
+ - If 2 consecutive successes → transition to CLOSED
53
+ - If rate limit error → back to OPEN, reset cooldown
54
+
55
+ ## State File: `.squad/ralph-circuit-breaker.json`
56
+
57
+ ```json
58
+ {
59
+ "state": "closed",
60
+ "preferredModel": "claude-sonnet-4.6",
61
+ "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"],
62
+ "currentFallbackIndex": 0,
63
+ "cooldownMinutes": 10,
64
+ "openedAt": null,
65
+ "halfOpenSuccesses": 0,
66
+ "consecutiveFailures": 0,
67
+ "metrics": {
68
+ "totalFallbacks": 0,
69
+ "totalRecoveries": 0,
70
+ "lastFallbackAt": null,
71
+ "lastRecoveryAt": null
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## PowerShell Functions
77
+
78
+ Paste these into your `ralph-watch.ps1` or source them from a shared module.
79
+
80
+ ### `Get-CircuitBreakerState`
81
+
82
+ ```powershell
83
+ function Get-CircuitBreakerState {
84
+ param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
85
+
86
+ if (-not (Test-Path $StateFile)) {
87
+ $default = @{
88
+ state = "closed"
89
+ preferredModel = "claude-sonnet-4.6"
90
+ fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1")
91
+ currentFallbackIndex = 0
92
+ cooldownMinutes = 10
93
+ openedAt = $null
94
+ halfOpenSuccesses = 0
95
+ consecutiveFailures = 0
96
+ metrics = @{
97
+ totalFallbacks = 0
98
+ totalRecoveries = 0
99
+ lastFallbackAt = $null
100
+ lastRecoveryAt = $null
101
+ }
102
+ }
103
+ $default | ConvertTo-Json -Depth 3 | Set-Content $StateFile
104
+ return $default
105
+ }
106
+
107
+ return (Get-Content $StateFile -Raw | ConvertFrom-Json)
108
+ }
109
+ ```
110
+
111
+ ### `Save-CircuitBreakerState`
112
+
113
+ ```powershell
114
+ function Save-CircuitBreakerState {
115
+ param(
116
+ [object]$State,
117
+ [string]$StateFile = ".squad/ralph-circuit-breaker.json"
118
+ )
119
+
120
+ $State | ConvertTo-Json -Depth 3 | Set-Content $StateFile
121
+ }
122
+ ```
123
+
124
+ ### `Get-CurrentModel`
125
+
126
+ Returns the model Ralph should use right now, based on circuit state.
127
+
128
+ ```powershell
129
+ function Get-CurrentModel {
130
+ param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
131
+
132
+ $cb = Get-CircuitBreakerState -StateFile $StateFile
133
+
134
+ switch ($cb.state) {
135
+ "closed" {
136
+ return $cb.preferredModel
137
+ }
138
+ "open" {
139
+ # Check if cooldown has expired
140
+ if ($cb.openedAt) {
141
+ $opened = [DateTime]::Parse($cb.openedAt)
142
+ $elapsed = (Get-Date) - $opened
143
+ if ($elapsed.TotalMinutes -ge $cb.cooldownMinutes) {
144
+ # Transition to half-open
145
+ $cb.state = "half-open"
146
+ $cb.halfOpenSuccesses = 0
147
+ Save-CircuitBreakerState -State $cb -StateFile $StateFile
148
+ Write-Host " [circuit-breaker] Cooldown expired. Testing preferred model..." -ForegroundColor Yellow
149
+ return $cb.preferredModel
150
+ }
151
+ }
152
+ # Still in cooldown — use fallback
153
+ $idx = [Math]::Min($cb.currentFallbackIndex, $cb.fallbackChain.Count - 1)
154
+ return $cb.fallbackChain[$idx]
155
+ }
156
+ "half-open" {
157
+ return $cb.preferredModel
158
+ }
159
+ default {
160
+ return $cb.preferredModel
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### `Update-CircuitBreakerOnSuccess`
167
+
168
+ Call after every successful model response.
169
+
170
+ ```powershell
171
+ function Update-CircuitBreakerOnSuccess {
172
+ param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
173
+
174
+ $cb = Get-CircuitBreakerState -StateFile $StateFile
175
+ $cb.consecutiveFailures = 0
176
+
177
+ if ($cb.state -eq "half-open") {
178
+ $cb.halfOpenSuccesses++
179
+ if ($cb.halfOpenSuccesses -ge 2) {
180
+ # Recovery! Close the circuit
181
+ $cb.state = "closed"
182
+ $cb.openedAt = $null
183
+ $cb.halfOpenSuccesses = 0
184
+ $cb.currentFallbackIndex = 0
185
+ $cb.metrics.totalRecoveries++
186
+ $cb.metrics.lastRecoveryAt = (Get-Date).ToString("o")
187
+ Save-CircuitBreakerState -State $cb -StateFile $StateFile
188
+ Write-Host " [circuit-breaker] RECOVERED — back to preferred model ($($cb.preferredModel))" -ForegroundColor Green
189
+ return
190
+ }
191
+ Save-CircuitBreakerState -State $cb -StateFile $StateFile
192
+ Write-Host " [circuit-breaker] Half-open success $($cb.halfOpenSuccesses)/2" -ForegroundColor Yellow
193
+ return
194
+ }
195
+
196
+ # closed state — nothing to do
197
+ }
198
+ ```
199
+
200
+ ### `Update-CircuitBreakerOnRateLimit`
201
+
202
+ Call when a model response indicates rate limiting (HTTP 429 or error message containing "rate limit").
203
+
204
+ ```powershell
205
+ function Update-CircuitBreakerOnRateLimit {
206
+ param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
207
+
208
+ $cb = Get-CircuitBreakerState -StateFile $StateFile
209
+ $cb.consecutiveFailures++
210
+
211
+ if ($cb.state -eq "closed" -or $cb.state -eq "half-open") {
212
+ # Open the circuit
213
+ $cb.state = "open"
214
+ $cb.openedAt = (Get-Date).ToString("o")
215
+ $cb.halfOpenSuccesses = 0
216
+ $cb.currentFallbackIndex = 0
217
+ $cb.metrics.totalFallbacks++
218
+ $cb.metrics.lastFallbackAt = (Get-Date).ToString("o")
219
+ Save-CircuitBreakerState -State $cb -StateFile $StateFile
220
+
221
+ $fallbackModel = $cb.fallbackChain[0]
222
+ Write-Host " [circuit-breaker] RATE LIMITED — falling back to $fallbackModel (cooldown: $($cb.cooldownMinutes)m)" -ForegroundColor Red
223
+ return
224
+ }
225
+
226
+ if ($cb.state -eq "open") {
227
+ # Already open — try next fallback in chain if current one also fails
228
+ if ($cb.currentFallbackIndex -lt ($cb.fallbackChain.Count - 1)) {
229
+ $cb.currentFallbackIndex++
230
+ $nextModel = $cb.fallbackChain[$cb.currentFallbackIndex]
231
+ Write-Host " [circuit-breaker] Fallback also limited — trying $nextModel" -ForegroundColor Red
232
+ }
233
+ # Reset cooldown timer
234
+ $cb.openedAt = (Get-Date).ToString("o")
235
+ Save-CircuitBreakerState -State $cb -StateFile $StateFile
236
+ }
237
+ }
238
+ ```
239
+
240
+ ## Integration with ralph-watch.ps1
241
+
242
+ In your Ralph polling loop, wrap the model selection:
243
+
244
+ ```powershell
245
+ # At the top of your polling loop
246
+ $model = Get-CurrentModel
247
+
248
+ # When invoking copilot CLI
249
+ $result = copilot-cli --model $model ...
250
+
251
+ # After the call
252
+ if ($result -match "rate.?limit" -or $LASTEXITCODE -eq 429) {
253
+ Update-CircuitBreakerOnRateLimit
254
+ } else {
255
+ Update-CircuitBreakerOnSuccess
256
+ }
257
+ ```
258
+
259
+ ### Full integration example
260
+
261
+ ```powershell
262
+ # Source the circuit breaker functions
263
+ . .squad-templates/ralph-circuit-breaker-functions.ps1
264
+
265
+ while ($true) {
266
+ $model = Get-CurrentModel
267
+ Write-Host "Polling with model: $model"
268
+
269
+ try {
270
+ # Your existing Ralph logic here, but pass $model
271
+ $response = Invoke-RalphCycle -Model $model
272
+
273
+ # Success path
274
+ Update-CircuitBreakerOnSuccess
275
+ }
276
+ catch {
277
+ if ($_.Exception.Message -match "rate.?limit|429|quota|Too Many Requests") {
278
+ Update-CircuitBreakerOnRateLimit
279
+ # Retry immediately with fallback model
280
+ continue
281
+ }
282
+ # Other errors — handle normally
283
+ throw
284
+ }
285
+
286
+ Start-Sleep -Seconds $pollInterval
287
+ }
288
+ ```
289
+
290
+ ## Configuration
291
+
292
+ Override defaults by editing `.squad/ralph-circuit-breaker.json`:
293
+
294
+ | Field | Default | Description |
295
+ |-------|---------|-------------|
296
+ | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed |
297
+ | `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) |
298
+ | `cooldownMinutes` | `10` | How long to wait before testing recovery |
299
+
300
+ ## Metrics
301
+
302
+ The state file tracks operational metrics:
303
+
304
+ - **totalFallbacks** — How many times the circuit opened
305
+ - **totalRecoveries** — How many times it recovered to preferred model
306
+ - **lastFallbackAt** — ISO timestamp of last rate limit event
307
+ - **lastRecoveryAt** — ISO timestamp of last successful recovery
308
+
309
+ Query metrics with:
310
+ ```powershell
311
+ $cb = Get-Content .squad/ralph-circuit-breaker.json | ConvertFrom-Json
312
+ Write-Host "Fallbacks: $($cb.metrics.totalFallbacks) | Recoveries: $($cb.metrics.totalRecoveries)"
313
+ ```