superkick 0.1.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 (199) hide show
  1. checksums.yaml +7 -0
  2. data/CLA.md +91 -0
  3. data/CLAUDE.md +2226 -0
  4. data/CONTRIBUTING.md +104 -0
  5. data/LICENSE +108 -0
  6. data/LICENSE-COMMERCIAL.md +39 -0
  7. data/PLAN.md +161 -0
  8. data/README.md +1155 -0
  9. data/exe/superkick +6 -0
  10. data/lib/superkick/agent/runtime.rb +82 -0
  11. data/lib/superkick/agent/runtimes/local.rb +74 -0
  12. data/lib/superkick/agent/runtimes.rb +4 -0
  13. data/lib/superkick/agent.rb +209 -0
  14. data/lib/superkick/agent_store.rb +85 -0
  15. data/lib/superkick/attach/client.rb +245 -0
  16. data/lib/superkick/attach/protocol.rb +71 -0
  17. data/lib/superkick/attach/server.rb +371 -0
  18. data/lib/superkick/budget_checker.rb +120 -0
  19. data/lib/superkick/buffer/client.rb +91 -0
  20. data/lib/superkick/buffer/server.rb +127 -0
  21. data/lib/superkick/cli/agent.rb +524 -0
  22. data/lib/superkick/cli/completion.rb +591 -0
  23. data/lib/superkick/cli/goal.rb +71 -0
  24. data/lib/superkick/cli/mcp.rb +34 -0
  25. data/lib/superkick/cli/monitor.rb +47 -0
  26. data/lib/superkick/cli/notifier.rb +39 -0
  27. data/lib/superkick/cli/repository.rb +46 -0
  28. data/lib/superkick/cli/server.rb +106 -0
  29. data/lib/superkick/cli/setup.rb +166 -0
  30. data/lib/superkick/cli/spawner.rb +85 -0
  31. data/lib/superkick/cli/team.rb +407 -0
  32. data/lib/superkick/cli.rb +175 -0
  33. data/lib/superkick/client_registry.rb +30 -0
  34. data/lib/superkick/configuration.rb +178 -0
  35. data/lib/superkick/connection.rb +56 -0
  36. data/lib/superkick/control/client.rb +78 -0
  37. data/lib/superkick/control/reply.rb +43 -0
  38. data/lib/superkick/control/server.rb +1271 -0
  39. data/lib/superkick/cost_accumulator.rb +53 -0
  40. data/lib/superkick/cost_extractor.rb +65 -0
  41. data/lib/superkick/cost_poller.rb +70 -0
  42. data/lib/superkick/driver/profile_source.rb +134 -0
  43. data/lib/superkick/driver.rb +179 -0
  44. data/lib/superkick/drivers/claude_code.rb +110 -0
  45. data/lib/superkick/drivers/codex.rb +57 -0
  46. data/lib/superkick/drivers/copilot.rb +75 -0
  47. data/lib/superkick/drivers/gemini.rb +86 -0
  48. data/lib/superkick/drivers/goose.rb +74 -0
  49. data/lib/superkick/drivers.rb +16 -0
  50. data/lib/superkick/drop.rb +80 -0
  51. data/lib/superkick/drops.rb +76 -0
  52. data/lib/superkick/environment_executor.rb +90 -0
  53. data/lib/superkick/goal.rb +95 -0
  54. data/lib/superkick/goals/agent_exit.rb +41 -0
  55. data/lib/superkick/goals/agent_signal.rb +42 -0
  56. data/lib/superkick/goals/command.rb +103 -0
  57. data/lib/superkick/history_buffer.rb +38 -0
  58. data/lib/superkick/hosted/attach/bridge.rb +52 -0
  59. data/lib/superkick/hosted/attach/client.rb +208 -0
  60. data/lib/superkick/hosted/attach/relay.rb +313 -0
  61. data/lib/superkick/hosted/attach/relay_store.rb +48 -0
  62. data/lib/superkick/hosted/bridge.rb +263 -0
  63. data/lib/superkick/hosted/buffer/bridge.rb +42 -0
  64. data/lib/superkick/hosted/buffer/client.rb +63 -0
  65. data/lib/superkick/hosted/buffer/relay.rb +126 -0
  66. data/lib/superkick/hosted/buffer/relay_store.rb +42 -0
  67. data/lib/superkick/hosted/control/client.rb +84 -0
  68. data/lib/superkick/hosted/mcp_proxy.rb +144 -0
  69. data/lib/superkick/inject_handler.rb +24 -0
  70. data/lib/superkick/injection_guard.rb +26 -0
  71. data/lib/superkick/injection_queue.rb +177 -0
  72. data/lib/superkick/injector.rb +65 -0
  73. data/lib/superkick/input_buffer.rb +171 -0
  74. data/lib/superkick/integrations/bugsnag/README.md +98 -0
  75. data/lib/superkick/integrations/bugsnag/spawner.rb +307 -0
  76. data/lib/superkick/integrations/bugsnag/templates/error_opened.liquid +17 -0
  77. data/lib/superkick/integrations/bugsnag.rb +7 -0
  78. data/lib/superkick/integrations/circleci/README.md +75 -0
  79. data/lib/superkick/integrations/circleci/monitor.rb +185 -0
  80. data/lib/superkick/integrations/circleci/probe.rb +36 -0
  81. data/lib/superkick/integrations/circleci/templates/ci_failure.liquid +8 -0
  82. data/lib/superkick/integrations/circleci/templates/ci_success.liquid +1 -0
  83. data/lib/superkick/integrations/circleci.rb +8 -0
  84. data/lib/superkick/integrations/datadog/README.md +253 -0
  85. data/lib/superkick/integrations/datadog/alert_goal.rb +94 -0
  86. data/lib/superkick/integrations/datadog/alert_monitor.rb +163 -0
  87. data/lib/superkick/integrations/datadog/alert_spawner.rb +201 -0
  88. data/lib/superkick/integrations/datadog/notification_templates/default.liquid +10 -0
  89. data/lib/superkick/integrations/datadog/notifier.rb +294 -0
  90. data/lib/superkick/integrations/datadog/spawner.rb +201 -0
  91. data/lib/superkick/integrations/datadog/templates/alert_changed.liquid +8 -0
  92. data/lib/superkick/integrations/datadog/templates/alert_escalated.liquid +8 -0
  93. data/lib/superkick/integrations/datadog/templates/alert_recovered.liquid +14 -0
  94. data/lib/superkick/integrations/datadog/templates/alert_triggered.liquid +29 -0
  95. data/lib/superkick/integrations/datadog/templates/error_opened.liquid +15 -0
  96. data/lib/superkick/integrations/datadog.rb +14 -0
  97. data/lib/superkick/integrations/docker/README.md +256 -0
  98. data/lib/superkick/integrations/docker/client.rb +295 -0
  99. data/lib/superkick/integrations/docker/runtime.rb +218 -0
  100. data/lib/superkick/integrations/docker.rb +4 -0
  101. data/lib/superkick/integrations/git/repository_source.rb +66 -0
  102. data/lib/superkick/integrations/git/version_control.rb +119 -0
  103. data/lib/superkick/integrations/git.rb +8 -0
  104. data/lib/superkick/integrations/github/README.md +300 -0
  105. data/lib/superkick/integrations/github/check_failed_spawner.rb +199 -0
  106. data/lib/superkick/integrations/github/drops.rb +114 -0
  107. data/lib/superkick/integrations/github/goal.rb +135 -0
  108. data/lib/superkick/integrations/github/issue_goal.rb +104 -0
  109. data/lib/superkick/integrations/github/issue_spawner.rb +160 -0
  110. data/lib/superkick/integrations/github/monitor.rb +251 -0
  111. data/lib/superkick/integrations/github/probe.rb +30 -0
  112. data/lib/superkick/integrations/github/repository_source.rb +228 -0
  113. data/lib/superkick/integrations/github/templates/check_failed.liquid +10 -0
  114. data/lib/superkick/integrations/github/templates/ci_failure.liquid +5 -0
  115. data/lib/superkick/integrations/github/templates/ci_success.liquid +1 -0
  116. data/lib/superkick/integrations/github/templates/issue_opened.liquid +20 -0
  117. data/lib/superkick/integrations/github/templates/pr_comment.liquid +2 -0
  118. data/lib/superkick/integrations/github/templates/pr_review.liquid +4 -0
  119. data/lib/superkick/integrations/github.rb +16 -0
  120. data/lib/superkick/integrations/honeybadger/README.md +97 -0
  121. data/lib/superkick/integrations/honeybadger/notification_templates/default.liquid +8 -0
  122. data/lib/superkick/integrations/honeybadger/notifier.rb +250 -0
  123. data/lib/superkick/integrations/honeybadger/spawner.rb +214 -0
  124. data/lib/superkick/integrations/honeybadger/templates/error_opened.liquid +17 -0
  125. data/lib/superkick/integrations/honeybadger.rb +9 -0
  126. data/lib/superkick/integrations/shell/README.md +83 -0
  127. data/lib/superkick/integrations/shell/monitor.rb +87 -0
  128. data/lib/superkick/integrations/shell/templates/shell_alert.liquid +6 -0
  129. data/lib/superkick/integrations/shell/templates/shell_success.liquid +6 -0
  130. data/lib/superkick/integrations/shell.rb +7 -0
  131. data/lib/superkick/integrations/shortcut/README.md +193 -0
  132. data/lib/superkick/integrations/shortcut/drops.rb +91 -0
  133. data/lib/superkick/integrations/shortcut/monitor.rb +582 -0
  134. data/lib/superkick/integrations/shortcut/probe.rb +34 -0
  135. data/lib/superkick/integrations/shortcut/spawner.rb +264 -0
  136. data/lib/superkick/integrations/shortcut/templates/related_story_changed.liquid +6 -0
  137. data/lib/superkick/integrations/shortcut/templates/story_blocker.liquid +8 -0
  138. data/lib/superkick/integrations/shortcut/templates/story_comment.liquid +5 -0
  139. data/lib/superkick/integrations/shortcut/templates/story_description_changed.liquid +19 -0
  140. data/lib/superkick/integrations/shortcut/templates/story_owner_changed.liquid +10 -0
  141. data/lib/superkick/integrations/shortcut/templates/story_ready.liquid +41 -0
  142. data/lib/superkick/integrations/shortcut/templates/story_state_changed.liquid +9 -0
  143. data/lib/superkick/integrations/shortcut/templates/story_unblocked.liquid +5 -0
  144. data/lib/superkick/integrations/shortcut.rb +11 -0
  145. data/lib/superkick/integrations/slack/README.md +297 -0
  146. data/lib/superkick/integrations/slack/drops.rb +70 -0
  147. data/lib/superkick/integrations/slack/notifier.rb +426 -0
  148. data/lib/superkick/integrations/slack/spawner.rb +251 -0
  149. data/lib/superkick/integrations/slack/templates/default.liquid +17 -0
  150. data/lib/superkick/integrations/slack/templates/slack_reply.liquid +3 -0
  151. data/lib/superkick/integrations/slack/templates/spawn/slack_message.liquid +10 -0
  152. data/lib/superkick/integrations/slack/thread_monitor.rb +161 -0
  153. data/lib/superkick/integrations/slack.rb +12 -0
  154. data/lib/superkick/liquid.rb +129 -0
  155. data/lib/superkick/local/repository_source.rb +148 -0
  156. data/lib/superkick/mcp_server.rb +596 -0
  157. data/lib/superkick/monitor.rb +215 -0
  158. data/lib/superkick/notification_dispatcher.rb +280 -0
  159. data/lib/superkick/notifier.rb +173 -0
  160. data/lib/superkick/notifier_state_store.rb +55 -0
  161. data/lib/superkick/notifier_template.rb +121 -0
  162. data/lib/superkick/notifiers/command.rb +124 -0
  163. data/lib/superkick/notifiers/terminal_bell.rb +41 -0
  164. data/lib/superkick/output_logger.rb +54 -0
  165. data/lib/superkick/poller.rb +126 -0
  166. data/lib/superkick/process_runner.rb +87 -0
  167. data/lib/superkick/pty_proxy.rb +403 -0
  168. data/lib/superkick/registry.rb +75 -0
  169. data/lib/superkick/repository_source.rb +195 -0
  170. data/lib/superkick/server.rb +211 -0
  171. data/lib/superkick/session_recorder.rb +154 -0
  172. data/lib/superkick/setup.rb +160 -0
  173. data/lib/superkick/spawn/agent_spawner.rb +311 -0
  174. data/lib/superkick/spawn/approval_store.rb +113 -0
  175. data/lib/superkick/spawn/handler.rb +144 -0
  176. data/lib/superkick/spawn/injector.rb +119 -0
  177. data/lib/superkick/spawn/workflow_executor.rb +196 -0
  178. data/lib/superkick/spawn/workflow_validator.rb +77 -0
  179. data/lib/superkick/spawner.rb +67 -0
  180. data/lib/superkick/supervisor.rb +516 -0
  181. data/lib/superkick/team/artifact_store.rb +92 -0
  182. data/lib/superkick/team/log.rb +140 -0
  183. data/lib/superkick/team/log_entry_drop.rb +34 -0
  184. data/lib/superkick/team/log_monitor.rb +84 -0
  185. data/lib/superkick/team/log_notifier.rb +96 -0
  186. data/lib/superkick/team/log_store.rb +40 -0
  187. data/lib/superkick/template_filters.rb +24 -0
  188. data/lib/superkick/template_renderer.rb +223 -0
  189. data/lib/superkick/templates/team_log/planning_agent.liquid +38 -0
  190. data/lib/superkick/templates/team_log/team_digest.liquid +45 -0
  191. data/lib/superkick/templates/team_log/teammate_message.liquid +7 -0
  192. data/lib/superkick/templates/team_log/worker_kickoff.liquid +37 -0
  193. data/lib/superkick/templates/workflow/workflow_triggered.liquid +22 -0
  194. data/lib/superkick/version.rb +5 -0
  195. data/lib/superkick/version_control.rb +135 -0
  196. data/lib/superkick/yaml_config.rb +302 -0
  197. data/lib/superkick.rb +198 -0
  198. data/plan.md +267 -0
  199. metadata +404 -0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,104 @@
1
+ # Contributing to Superkick
2
+
3
+ Thank you for your interest in contributing to Superkick.
4
+
5
+ ## Getting started
6
+
7
+ Clone the repo and install dependencies:
8
+
9
+ ```bash
10
+ git clone https://github.com/your-org/superkick.git
11
+ cd superkick
12
+ bundle install
13
+ ```
14
+
15
+ If you are working in the Claude Code sandbox, see the bootstrap steps in
16
+ [CLAUDE.md](CLAUDE.md#claude-code-sandbox-bootstrap) first.
17
+
18
+ ## Running tests
19
+
20
+ ```bash
21
+ # Full test suite + lint (the default)
22
+ bundle exec rake
23
+
24
+ # Tests only
25
+ bundle exec rake test
26
+
27
+ # Single test file
28
+ bundle exec rake test TEST=test/injector_test.rb
29
+
30
+ # Specific test by name
31
+ bundle exec rake test TEST=test/injector_test.rb TESTOPTS="-n test_returns_skipped_when_gate_refuses"
32
+
33
+ # Lint only
34
+ bundle exec rake standard
35
+
36
+ # Auto-fix lint violations
37
+ bundle exec rake standard:fix
38
+ ```
39
+
40
+ All tests must pass and the linter must report no violations before a pull
41
+ request can be merged.
42
+
43
+ ## Making changes
44
+
45
+ - Keep pull requests focused on a single concern.
46
+ - Follow the existing code style (StandardRB, zero config).
47
+ - Add or update tests for any behaviour you change.
48
+ - All lib files use `# frozen_string_literal: true` — keep it that way.
49
+
50
+ ### Adding a new driver
51
+
52
+ See [skills/superkick-new-driver/SKILL.md](skills/superkick-new-driver/SKILL.md) for
53
+ the step-by-step guide.
54
+
55
+ ### Adding a new monitor
56
+
57
+ See [skills/superkick-new-monitor/SKILL.md](skills/superkick-new-monitor/SKILL.md)
58
+ for the step-by-step guide.
59
+
60
+ ### Adding a new spawner
61
+
62
+ See [skills/superkick-new-spawner/SKILL.md](skills/superkick-new-spawner/SKILL.md)
63
+ for the step-by-step guide. Spawners support workflow hooks (`on_complete:` /
64
+ `on_fail:`) for multi-stage pipelines — the skill covers designing spawners
65
+ that work well in chains.
66
+
67
+ ### Adding a new goal
68
+
69
+ See [skills/superkick-new-goal/SKILL.md](skills/superkick-new-goal/SKILL.md) for the
70
+ step-by-step guide.
71
+
72
+ ### Adding a new version control adapter
73
+
74
+ See [skills/superkick-new-version-control/SKILL.md](skills/superkick-new-version-control/SKILL.md)
75
+ for the step-by-step guide.
76
+
77
+ ### Adding a new repository source
78
+
79
+ See [skills/superkick-new-repository-source/SKILL.md](skills/superkick-new-repository-source/SKILL.md)
80
+ for the step-by-step guide.
81
+
82
+ ### Adding a new notifier
83
+
84
+ See [skills/superkick-new-notifier/SKILL.md](skills/superkick-new-notifier/SKILL.md)
85
+ for the step-by-step guide.
86
+
87
+ ### Adding a new agent runtime
88
+
89
+ See [skills/superkick-new-runtime/SKILL.md](skills/superkick-new-runtime/SKILL.md)
90
+ for the step-by-step guide.
91
+
92
+ ## Submitting a pull request
93
+
94
+ 1. Fork the repository and create a branch from `main`.
95
+ 2. Make your changes, ensuring tests pass and the linter is clean.
96
+ 3. Open a pull request with a clear description of what changes you made and
97
+ why.
98
+
99
+ ## License
100
+
101
+ By submitting a contribution, you agree to the terms of the
102
+ [Contributor License Agreement](CLA.md). In short: you retain ownership of your
103
+ work, and you grant the Maintainer the right to distribute it under both the
104
+ [Business Source License 1.1](LICENSE) and a [commercial license](LICENSE-COMMERCIAL.md).
data/LICENSE ADDED
@@ -0,0 +1,108 @@
1
+ Business Source License 1.1
2
+
3
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
4
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
5
+
6
+ -----------------------------------------------------------------------------
7
+
8
+ Parameters
9
+
10
+ Licensor: Superkick Systems, Inc.
11
+
12
+ Licensed Work: Superkick
13
+ The Licensed Work is (c) 2026 Superkick Systems, Inc.
14
+
15
+ Additional Use Grant: You may make production use of the Licensed Work,
16
+ provided your use does not include offering the Licensed
17
+ Work to third parties on a hosted, managed, or embedded
18
+ basis in a manner that competes with the Licensor's paid
19
+ or commercial versions of the Licensed Work. For purposes
20
+ of this license, "compete" means offering a product or
21
+ service whose primary value derives substantially from
22
+ the functionality of the Licensed Work.
23
+
24
+ Change Date: Four years from the date the Licensed Work is published.
25
+
26
+ Change License: Apache License, Version 2.0
27
+
28
+ -----------------------------------------------------------------------------
29
+
30
+ Terms
31
+
32
+ The Licensor hereby grants you the right to copy, modify, create derivative
33
+ works, redistribute, and make non-production use of the Licensed Work. The
34
+ Licensor may make an Additional Use Grant, above, permitting limited
35
+ production use.
36
+
37
+ Effective on the Change Date, or the fourth anniversary of the first publicly
38
+ available distribution of a specific version of the Licensed Work under this
39
+ License, whichever comes first, the Licensor hereby grants you rights under
40
+ the terms of the Change License, and the rights granted in the paragraph
41
+ above terminate.
42
+
43
+ If your use of the Licensed Work does not comply with the requirements
44
+ currently in effect as described in this License, you must purchase a
45
+ commercial license from the Licensor, its affiliated entities, or authorized
46
+ resellers, or you must refrain from using the Licensed Work.
47
+
48
+ All copies of the original and modified Licensed Work, and derivative works
49
+ of the Licensed Work, are subject to this License. This License applies
50
+ separately for each version of the Licensed Work and the Change Date may vary
51
+ for each version of the Licensed Work released by Licensor.
52
+
53
+ You must conspicuously display this License on each original or modified copy
54
+ of the Licensed Work. If you receive the Licensed Work in original or
55
+ modified form from a third party, the terms and conditions set forth in this
56
+ License apply to your use of that work.
57
+
58
+ Any use of the Licensed Work in violation of this License will automatically
59
+ terminate your rights under this License for the current and all other
60
+ versions of the Licensed Work.
61
+
62
+ This License does not grant you any right in any trademark or logo of
63
+ Licensor or its affiliates (provided that you may use a trademark or logo of
64
+ Licensor as expressly required by this License).
65
+
66
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
67
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
68
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
69
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
70
+ TITLE.
71
+
72
+ MariaDB hereby grants you permission to use this License's text to license
73
+ your works, and to refer to it using the trademark "Business Source License",
74
+ as long as you comply with the Covenants of Licensor below.
75
+
76
+ -----------------------------------------------------------------------------
77
+
78
+ Covenants of Licensor
79
+
80
+ In consideration of the right to use this License's text and the "Business
81
+ Source License" name and trademark, Licensor covenants to MariaDB, and to all
82
+ other recipients of the licensed work to be provided by Licensor:
83
+
84
+ 1. To specify as the Change License the GPL Version 2.0 or any later version,
85
+ or a license that is compatible with GPL Version 2.0 or a later version,
86
+ where "compatible" means that software provided under the Change License can
87
+ be included in a program with software provided under GPL Version 2.0 or a
88
+ later version. Licensor may specify additional Change Licenses without
89
+ limitation.
90
+
91
+ 2. To either: (a) specify an additional grant of rights to use that does not
92
+ impose any additional restriction on the right granted in this License, as
93
+ the Additional Use Grant; or (b) insert the text "None".
94
+
95
+ 3. To specify a Change Date.
96
+
97
+ 4. Not to modify this License in any other way.
98
+
99
+ -----------------------------------------------------------------------------
100
+
101
+ Notice
102
+
103
+ The Business Source License (this document, or the "License") is not an Open
104
+ Source license. However, the Licensed Work will eventually be made available
105
+ under an Open Source License, as stated in this License.
106
+
107
+ For more information on the use of the Business Source License, please visit:
108
+ https://mariadb.com/bsl11/
@@ -0,0 +1,39 @@
1
+ # Superkick Commercial License
2
+
3
+ The Superkick source code is made available under the Business Source License 1.1
4
+ (see [LICENSE](LICENSE)). The BSL permits non-production use and production use
5
+ that does not compete with the Licensor's offerings.
6
+
7
+ ## When you need a commercial license
8
+
9
+ You need a separate commercial license if your use of Superkick falls outside the
10
+ BSL's Additional Use Grant — for example, if you are offering Superkick (or a
11
+ product built substantially on Superkick) to third parties as a hosted, managed, or
12
+ SaaS product.
13
+
14
+ ## What the commercial license provides
15
+
16
+ A commercial license grants you:
17
+
18
+ - **Production use** without the BSL's non-compete restriction
19
+ - **Redistribution rights** for embedding Superkick in your own product
20
+ - **Priority support** and direct access to the maintainer
21
+ - **Custom terms** tailored to your use case
22
+
23
+ ## Pricing
24
+
25
+ Commercial licenses are priced on a case-by-case basis depending on the nature
26
+ and scale of your use. Founder-friendly terms are available for startups and
27
+ small teams.
28
+
29
+ ## Contact
30
+
31
+ To discuss commercial licensing, please reach out:
32
+
33
+ - **Email:** licensing@superkick.ai
34
+ - **GitHub:** Open a [licensing inquiry](https://github.com/admtnnr/superkick/issues/new?title=Commercial+License+Inquiry)
35
+
36
+ ---
37
+
38
+ *This document is informational and does not constitute a license. The binding
39
+ license terms are in [LICENSE](LICENSE).*
data/PLAN.md ADDED
@@ -0,0 +1,161 @@
1
+ # Docker Runtime Implementation Plan
2
+
3
+ ## Overview
4
+
5
+ Add `Agent::Runtime::Docker` — a Docker container runtime that provisions spawned agents in Docker containers via the Docker Engine API. Uses Faraday for HTTP (Unix socket and TCP+TLS), following established codebase patterns.
6
+
7
+ ## Existing plumbing
8
+
9
+ `Configuration#build_agent_runtime` (line 107) already has a `when :docker` branch that passes the `docker:` config hash to the constructor. We just need to build the class it instantiates.
10
+
11
+ ## Implementation steps
12
+
13
+ ### Step 1: Docker Engine API client (`lib/superkick/docker/client.rb`)
14
+
15
+ A thin Faraday-based wrapper around the Docker Engine API endpoints we need. Accepts `connection:` for test injection.
16
+
17
+ **Constructor:** `initialize(host:, tls: nil, connection: nil)`
18
+ - `host` — `unix:///var/run/docker.sock` or `tcp://host:port`
19
+ - `tls` — optional Hash `{ ca:, cert:, key: }` for TCP+TLS
20
+ - `connection` — injected Faraday connection (tests)
21
+
22
+ **Methods:**
23
+ - `create_container(name:, config:)` → Hash (container ID, warnings)
24
+ - `start_container(id:)` → nil
25
+ - `stop_container(id:, timeout: 30)` → nil
26
+ - `remove_container(id:, force: false)` → nil
27
+ - `inspect_container(id:)` → Hash (full container state)
28
+ - `pull_image(image:, auth: nil)` → nil (blocks until complete)
29
+ - `image_exists?(image:)` → Boolean
30
+ - `ping` → Boolean (health check)
31
+
32
+ **Faraday Unix socket:** Faraday supports Unix sockets via the URL scheme `http+unix:///path/to/socket`. The base URL becomes `http+unix:///var/run/docker.sock/` and paths are appended normally (`/v1.47/containers/create`).
33
+
34
+ **API version:** Pin to `v1.47` (Docker 27.x). All endpoints are prefixed with this version.
35
+
36
+ **Error handling:** Define `Docker::Client::Error` (base), `Docker::Client::NotFound` (404), `Docker::Client::Conflict` (409). Map HTTP status codes to these.
37
+
38
+ ### Step 2: Docker runtime (`lib/superkick/agent/runtimes/docker.rb`)
39
+
40
+ **Constructor:** `initialize(image:, host: nil, tls: nil, registry: nil, pull_policy: :missing, memory: nil, cpu: nil, network: nil, stop_timeout: 30, auto_remove: true, env: nil, volumes: nil, labels: nil, connection: nil)`
41
+
42
+ All config keys from the YAML `docker:` section map directly to constructor kwargs. The `connection:` param is for test injection into the underlying `Docker::Client`.
43
+
44
+ **Handle:** `Data.define(:container_id, :container_name)`
45
+
46
+ **`provision(agent_id:, config:)`:**
47
+ 1. Pull image (respecting `pull_policy`)
48
+ 2. Build container config hash:
49
+ - `Image` — from constructor
50
+ - `Cmd` — from `config[:command]`
51
+ - `Env` — merge runtime-level `env` with `config[:env]` (config wins)
52
+ - `WorkingDir` — from `config[:working_dir]`
53
+ - `HostConfig.Memory` — parse memory string (e.g. `"4G"` → bytes)
54
+ - `HostConfig.NanoCpus` — parse cpu (e.g. `2` → 2_000_000_000)
55
+ - `HostConfig.Binds` — from `volumes`
56
+ - `HostConfig.NetworkMode` — from `network`
57
+ - `Labels` — merge runtime labels with `{ "superkick.agent_id" => agent_id }`
58
+ - `StopTimeout` — from `stop_timeout`
59
+ 3. Create container with name `superkick-#{agent_id}`
60
+ 4. Start container
61
+ 5. Return `Handle.new(container_id:, container_name:)`
62
+
63
+ **`terminate(handle:)`:**
64
+ 1. Stop container (with `stop_timeout`)
65
+ 2. Remove container if `auto_remove` is true
66
+ 3. Rescue `Docker::Client::NotFound` silently (already gone)
67
+
68
+ **`alive?(handle:)`:**
69
+ 1. Inspect container
70
+ 2. Return `state["Running"]`
71
+ 3. Rescue `Docker::Client::NotFound` → false
72
+
73
+ **`metadata(handle:)`:**
74
+ ```ruby
75
+ { container_id: handle.container_id, container_name: handle.container_name }
76
+ ```
77
+
78
+ ### Step 3: Memory size parser (private helper)
79
+
80
+ Parse Docker memory strings: `"4G"` → `4_294_967_296`, `"512M"` → `536_870_912`, `"1024K"` → `1_048_576`. If already an Integer, pass through. Lives as a private method on the Docker runtime class.
81
+
82
+ ### Step 4: Update `Configuration#build_agent_runtime`
83
+
84
+ The existing `when :docker` branch needs updating. Current code:
85
+
86
+ ```ruby
87
+ when :docker
88
+ docker_config = @runtime[:docker] || {}
89
+ klass.new(**docker_config.except(:socket).merge(
90
+ socket: docker_config[:socket] || "/var/run/docker.sock"
91
+ ))
92
+ ```
93
+
94
+ Update to pass `host:` instead of `socket:` (since the config key is `host:`), and pass the full docker config hash through:
95
+
96
+ ```ruby
97
+ when :docker
98
+ docker_config = @runtime[:docker] || {}
99
+ klass.new(**docker_config)
100
+ ```
101
+
102
+ The Docker runtime constructor handles all defaults internally.
103
+
104
+ ### Step 5: Register and require
105
+
106
+ 1. Self-register at bottom of `docker.rb`: `Superkick::Agent::Runtime.register(Superkick::Agent::Runtime::Docker)`
107
+ 2. Add `require_relative "runtimes/docker"` to `lib/superkick/agent/runtimes.rb`
108
+
109
+ ### Step 6: Tests
110
+
111
+ **`test/docker/client_test.rb`** — test the Faraday-based Docker client:
112
+ - `ping` — success and failure
113
+ - `create_container` — sends correct JSON, returns container ID
114
+ - `start_container` / `stop_container` / `remove_container` — correct HTTP methods and paths
115
+ - `inspect_container` — parses response
116
+ - `pull_image` — correct endpoint, sends auth header when registry config provided
117
+ - `image_exists?` — true on 200, false on 404
118
+ - Error mapping (404 → NotFound, 409 → Conflict, 500 → Error)
119
+
120
+ All tests use `Faraday::Adapter::Test::Stubs` with `connection:` injection.
121
+
122
+ **`test/agent/runtimes/docker_test.rb`** — test the runtime:
123
+ - `type` returns `:docker`
124
+ - `provision` — pulls image (when policy requires it), creates container with correct config, starts it, returns Handle
125
+ - `provision` — merges env correctly (config env overrides runtime env)
126
+ - `provision` — parses memory/cpu into Docker API format
127
+ - `provision` — skips pull when `pull_policy: :never`
128
+ - `terminate` — stops and removes container
129
+ - `terminate` — handles already-removed container
130
+ - `alive?` — returns true/false based on container state
131
+ - `alive?` — returns false when container not found
132
+ - `metadata` — returns container_id and container_name
133
+ - Memory parser — various units and passthrough
134
+
135
+ Tests stub the `Docker::Client` methods rather than HTTP, keeping them focused on runtime logic.
136
+
137
+ ### Step 7: Documentation
138
+
139
+ 1. **CLAUDE.md** — update directory structure (add `docker/` dir), add Docker runtime to Key Classes section, update `build_agent_runtime` description, update runtime config example
140
+ 2. **README.md** — add Docker runtime section under configuration/deployment
141
+ 3. **`docs/tutorials/first-spawner-pipeline.md`** — mention Docker runtime as an option
142
+ 4. **`docs/explanation/spawner-workflows.md`** — note runtime choice affects agent isolation
143
+
144
+ ## File list
145
+
146
+ New files:
147
+ - `lib/superkick/docker/client.rb`
148
+ - `test/docker/client_test.rb`
149
+ - `lib/superkick/agent/runtimes/docker.rb`
150
+ - `test/agent/runtimes/docker_test.rb`
151
+
152
+ Modified files:
153
+ - `lib/superkick/agent/runtimes.rb` (add require)
154
+ - `lib/superkick/configuration.rb` (update `build_agent_runtime`)
155
+ - `lib/superkick.rb` (add require for `docker/client`)
156
+ - `CLAUDE.md` (docs)
157
+ - `README.md` (docs)
158
+
159
+ ## Open questions
160
+
161
+ None — config surface is agreed, patterns are clear from existing code.