@wardby/cli 0.5.2 → 0.6.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 (411) hide show
  1. package/.env.example +33 -0
  2. package/README.md +25 -14
  3. package/dist/claude-coding-worker/context.d.ts +22 -0
  4. package/dist/claude-coding-worker/context.js +92 -0
  5. package/dist/claude-coding-worker/driver.d.ts +7 -1
  6. package/dist/claude-coding-worker/driver.js +38 -5
  7. package/dist/claude-coding-worker/sdk.js +13 -4
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +5 -2
  10. package/dist/cli-native-mode.d.ts +9 -0
  11. package/dist/cli-native-mode.js +20 -0
  12. package/dist/cli.d.ts +3 -1
  13. package/dist/cli.js +141 -5
  14. package/dist/coding/claude-context.d.ts +45 -0
  15. package/dist/coding/claude-context.js +434 -0
  16. package/dist/coding/local-git.js +9 -1
  17. package/dist/coding/profile.d.ts +12 -0
  18. package/dist/coding/profile.js +6 -0
  19. package/dist/coding/protocol.d.ts +104 -0
  20. package/dist/coding/protocol.js +87 -3
  21. package/dist/coding/registry/pypi.d.ts +25 -1
  22. package/dist/coding/registry/pypi.js +140 -16
  23. package/dist/coding/registry/types.d.ts +17 -3
  24. package/dist/coding-worker/artifact.d.ts +8 -0
  25. package/dist/coding-worker/artifact.js +2 -2
  26. package/dist/coding-worker/driver.js +2 -0
  27. package/dist/coding-worker/sdk.d.ts +6 -0
  28. package/dist/coding-worker/sdk.js +1 -0
  29. package/dist/coding-worker/skills.d.ts +23 -0
  30. package/dist/coding-worker/skills.js +117 -0
  31. package/dist/coding-worker/types.d.ts +2 -0
  32. package/dist/config/github-allowed-accounts.d.ts +15 -0
  33. package/dist/config/github-allowed-accounts.js +36 -0
  34. package/dist/config/low-power.d.ts +12 -0
  35. package/dist/config/low-power.js +18 -0
  36. package/dist/config/mobile.d.ts +12 -0
  37. package/dist/config/mobile.js +18 -0
  38. package/dist/config/providers.d.ts +56 -2
  39. package/dist/config/providers.js +92 -6
  40. package/dist/core/db.d.ts +16 -6
  41. package/dist/core/db.js +12 -10
  42. package/dist/core/dispatch.d.ts +11 -0
  43. package/dist/core/dispatch.js +36 -1
  44. package/dist/core/engine-native.js +9 -5
  45. package/dist/core/host-events.d.ts +49 -3
  46. package/dist/core/host-events.js +289 -15
  47. package/dist/core/http-runtime.js +13 -6
  48. package/dist/core/issue-bridge.js +42 -2
  49. package/dist/core/issue-events.js +12 -1
  50. package/dist/core/issue-status.js +14 -3
  51. package/dist/core/merge-order-check.d.ts +129 -0
  52. package/dist/core/merge-order-check.js +336 -0
  53. package/dist/core/notification-dispatcher.d.ts +70 -0
  54. package/dist/core/notification-dispatcher.js +368 -0
  55. package/dist/core/notification-templates.d.ts +25 -0
  56. package/dist/core/notification-templates.js +105 -0
  57. package/dist/core/notifications.d.ts +10 -0
  58. package/dist/core/notifications.js +38 -0
  59. package/dist/core/pg-auth.d.ts +22 -0
  60. package/dist/core/pg-auth.js +152 -0
  61. package/dist/core/power-composition.d.ts +16 -0
  62. package/dist/core/power-composition.js +37 -0
  63. package/dist/core/power-idle.d.ts +14 -0
  64. package/dist/core/power-idle.js +30 -0
  65. package/dist/core/power.d.ts +71 -0
  66. package/dist/core/power.js +202 -0
  67. package/dist/core/reconciler.d.ts +4 -2
  68. package/dist/core/reconciler.js +29 -5
  69. package/dist/core/related-pull-requests.d.ts +48 -4
  70. package/dist/core/related-pull-requests.js +113 -11
  71. package/dist/core/repo-access.d.ts +15 -0
  72. package/dist/core/repo-access.js +66 -0
  73. package/dist/core/review-fix.d.ts +37 -0
  74. package/dist/core/review-fix.js +207 -6
  75. package/dist/core/review-host-tools.d.ts +20 -0
  76. package/dist/core/review-host-tools.js +58 -2
  77. package/dist/core/runner.d.ts +336 -3
  78. package/dist/core/runner.js +894 -317
  79. package/dist/core/secret-broker-config.d.ts +103 -0
  80. package/dist/core/secret-broker-config.js +112 -0
  81. package/dist/core/secrets.d.ts +39 -4
  82. package/dist/core/secrets.js +148 -40
  83. package/dist/core/workflow-event-recorder.d.ts +14 -0
  84. package/dist/core/workflow-event-recorder.js +120 -0
  85. package/dist/core/workflow-events.d.ts +82 -0
  86. package/dist/core/workflow-events.js +44 -0
  87. package/dist/core/workflow-run-events.d.ts +16 -0
  88. package/dist/core/workflow-run-events.js +40 -0
  89. package/dist/generated/prisma/browser.d.ts +58 -0
  90. package/dist/generated/prisma/client.d.ts +58 -0
  91. package/dist/generated/prisma/commonInputTypes.d.ts +150 -0
  92. package/dist/generated/prisma/enums.d.ts +27 -0
  93. package/dist/generated/prisma/enums.js +23 -0
  94. package/dist/generated/prisma/internal/class.d.ts +88 -0
  95. package/dist/generated/prisma/internal/class.js +4 -4
  96. package/dist/generated/prisma/internal/prismaNamespace.d.ts +772 -1
  97. package/dist/generated/prisma/internal/prismaNamespace.js +135 -4
  98. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +139 -0
  99. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -4
  100. package/dist/generated/prisma/models/Agent.d.ts +360 -1
  101. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +73 -1
  102. package/dist/generated/prisma/models/CodingRun.d.ts +205 -1
  103. package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
  104. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1675 -0
  105. package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
  106. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1517 -0
  107. package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
  108. package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
  109. package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
  110. package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
  111. package/dist/generated/prisma/models/NotificationChannel.js +1 -0
  112. package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
  113. package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
  114. package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
  115. package/dist/generated/prisma/models/NotificationThread.js +1 -0
  116. package/dist/generated/prisma/models/Run.d.ts +826 -1
  117. package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
  118. package/dist/generated/prisma/models/Secret.d.ts +37 -1
  119. package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
  120. package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
  121. package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
  122. package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
  123. package/dist/generated/prisma/models.d.ts +8 -0
  124. package/dist/help/build.js +4 -2
  125. package/dist/help/catalog.d.ts +1 -1
  126. package/dist/help/catalog.js +31 -6
  127. package/dist/help-index.json +1492 -113
  128. package/dist/import/create.js +17 -0
  129. package/dist/import/neutral-schema.d.ts +6 -2
  130. package/dist/import/neutral-schema.js +2 -0
  131. package/dist/mcp/auth/access.d.ts +1 -0
  132. package/dist/mcp/auth/ownership.d.ts +1 -1
  133. package/dist/mcp/auth/repo-authorization.js +14 -0
  134. package/dist/mcp/auth/resource-server.d.ts +2 -1
  135. package/dist/mcp/auth/resource-server.js +7 -2
  136. package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
  137. package/dist/mcp/auth/self-hosted/browser.js +19 -6
  138. package/dist/mcp/context.d.ts +6 -0
  139. package/dist/mcp/host-events/github-ingress.d.ts +23 -1
  140. package/dist/mcp/host-events/github-ingress.js +68 -0
  141. package/dist/mcp/index.d.ts +13 -0
  142. package/dist/mcp/index.js +59 -10
  143. package/dist/mcp/tools/agents.js +83 -12
  144. package/dist/mcp/tools/notification-channels.d.ts +2 -0
  145. package/dist/mcp/tools/notification-channels.js +293 -0
  146. package/dist/mcp/tools/power.d.ts +2 -0
  147. package/dist/mcp/tools/power.js +77 -0
  148. package/dist/mcp/tools/runs.d.ts +0 -6
  149. package/dist/mcp/tools/runs.js +7 -2
  150. package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
  151. package/dist/mcp/tools/secret-broker-compat.js +23 -0
  152. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
  153. package/dist/mcp/tools/secret-elicitation-form.js +200 -10
  154. package/dist/mcp/tools/secret-elicitation-server.js +8 -1
  155. package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
  156. package/dist/mcp/tools/secret-elicitation.js +110 -12
  157. package/dist/mcp/tools/secrets.js +184 -7
  158. package/dist/mcp/tools/tools.d.ts +1 -1
  159. package/dist/mcp/tools/tools.js +22 -2
  160. package/dist/native-worker/composition.d.ts +23 -0
  161. package/dist/native-worker/composition.js +77 -0
  162. package/dist/native-worker/docker-isolation.d.ts +65 -0
  163. package/dist/native-worker/docker-isolation.js +183 -0
  164. package/dist/native-worker/docker-launcher.d.ts +74 -0
  165. package/dist/native-worker/docker-launcher.js +237 -0
  166. package/dist/native-worker/gateway-server.d.ts +39 -0
  167. package/dist/native-worker/gateway-server.js +315 -0
  168. package/dist/native-worker/gateway.d.ts +44 -0
  169. package/dist/native-worker/gateway.js +197 -0
  170. package/dist/native-worker/http-server.d.ts +22 -0
  171. package/dist/native-worker/http-server.js +109 -0
  172. package/dist/native-worker/http-transport.d.ts +17 -0
  173. package/dist/native-worker/http-transport.js +88 -0
  174. package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
  175. package/dist/native-worker/kubernetes-isolation.js +218 -0
  176. package/dist/native-worker/kubernetes-launcher.d.ts +102 -0
  177. package/dist/native-worker/kubernetes-launcher.js +335 -0
  178. package/dist/native-worker/launch.d.ts +45 -0
  179. package/dist/native-worker/launch.js +44 -0
  180. package/dist/native-worker/ledger.d.ts +109 -0
  181. package/dist/native-worker/ledger.js +192 -0
  182. package/dist/native-worker/loopback.d.ts +12 -0
  183. package/dist/native-worker/loopback.js +49 -0
  184. package/dist/native-worker/main.d.ts +7 -0
  185. package/dist/native-worker/main.js +74 -0
  186. package/dist/native-worker/protocol.d.ts +744 -0
  187. package/dist/native-worker/protocol.js +200 -0
  188. package/dist/native-worker/sandbox-executor.d.ts +68 -0
  189. package/dist/native-worker/sandbox-executor.js +210 -0
  190. package/dist/native-worker/stdio.d.ts +29 -0
  191. package/dist/native-worker/stdio.js +188 -0
  192. package/dist/native-worker/warm-delivery.d.ts +16 -0
  193. package/dist/native-worker/warm-delivery.js +33 -0
  194. package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
  195. package/dist/native-worker/warm-pool-ledger.js +73 -0
  196. package/dist/native-worker/warm-pool.d.ts +107 -0
  197. package/dist/native-worker/warm-pool.js +283 -0
  198. package/dist/native-worker/worker.d.ts +17 -0
  199. package/dist/native-worker/worker.js +115 -0
  200. package/dist/providers/auth/authorization-server.d.ts +3 -0
  201. package/dist/providers/auth/index.js +2 -0
  202. package/dist/providers/auth/self-hosted.d.ts +13 -1
  203. package/dist/providers/auth/self-hosted.js +54 -4
  204. package/dist/providers/chat/fake.d.ts +46 -0
  205. package/dist/providers/chat/fake.js +39 -0
  206. package/dist/providers/chat/index.d.ts +4 -0
  207. package/dist/providers/chat/index.js +8 -0
  208. package/dist/providers/chat/slack.d.ts +37 -0
  209. package/dist/providers/chat/slack.js +105 -0
  210. package/dist/providers/chat/types.d.ts +42 -0
  211. package/dist/providers/chat/types.js +19 -0
  212. package/dist/providers/coding-proxy/mock-upstream.d.ts +33 -0
  213. package/dist/providers/coding-proxy/mock-upstream.js +116 -0
  214. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
  215. package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
  216. package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
  217. package/dist/providers/coding-proxy/registry/service.js +20 -3
  218. package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
  219. package/dist/providers/coding-proxy/registry/store.js +33 -0
  220. package/dist/providers/coding-proxy/runtime.js +12 -1
  221. package/dist/providers/coding-proxy/types.d.ts +2 -0
  222. package/dist/providers/engine/types.d.ts +19 -0
  223. package/dist/providers/engine/types.js +22 -0
  224. package/dist/providers/executor/composition.d.ts +24 -1
  225. package/dist/providers/executor/composition.js +39 -6
  226. package/dist/providers/executor/container.d.ts +24 -1
  227. package/dist/providers/executor/container.js +101 -11
  228. package/dist/providers/executor/dbos.d.ts +42 -0
  229. package/dist/providers/executor/dbos.js +118 -12
  230. package/dist/providers/executor/deferred.d.ts +56 -0
  231. package/dist/providers/executor/deferred.js +119 -0
  232. package/dist/providers/executor/routing.d.ts +37 -10
  233. package/dist/providers/executor/routing.js +73 -32
  234. package/dist/providers/executor/types.d.ts +12 -0
  235. package/dist/providers/jobs/docker.js +3 -3
  236. package/dist/providers/jobs/fake-kubernetes-api.d.ts +21 -2
  237. package/dist/providers/jobs/fake-kubernetes-api.js +42 -1
  238. package/dist/providers/jobs/kubernetes-api.d.ts +17 -1
  239. package/dist/providers/jobs/kubernetes-api.js +7 -0
  240. package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
  241. package/dist/providers/jobs/kubernetes-client.js +52 -3
  242. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
  243. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
  244. package/dist/providers/jobs/kubernetes-isolation.d.ts +16 -1
  245. package/dist/providers/jobs/kubernetes-isolation.js +58 -5
  246. package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
  247. package/dist/providers/jobs/kubernetes-platform.js +38 -1
  248. package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
  249. package/dist/providers/jobs/kubernetes-preflight.js +4 -2
  250. package/dist/providers/jobs/kubernetes.d.ts +56 -7
  251. package/dist/providers/jobs/kubernetes.js +117 -36
  252. package/dist/providers/jobs/metadata-probes.d.ts +11 -0
  253. package/dist/providers/jobs/metadata-probes.js +13 -0
  254. package/dist/providers/jobs/types.d.ts +9 -0
  255. package/dist/providers/llm/openai.js +0 -1
  256. package/dist/providers/review-host/github.d.ts +11 -1
  257. package/dist/providers/review-host/github.js +50 -0
  258. package/dist/providers/review-host/types.d.ts +23 -0
  259. package/dist/providers/vcs/github.d.ts +32 -6
  260. package/dist/providers/vcs/github.js +74 -16
  261. package/dist/quickstart/coding-db.js +14 -2
  262. package/dist/quickstart/coding-doctor.d.ts +3 -0
  263. package/dist/quickstart/coding-doctor.js +3 -0
  264. package/dist/quickstart/coding-images.d.ts +9 -0
  265. package/dist/quickstart/coding-images.js +39 -0
  266. package/dist/quickstart/coding-seed.d.ts +20 -1
  267. package/dist/quickstart/coding-seed.js +96 -7
  268. package/dist/quickstart/coding.d.ts +2 -0
  269. package/dist/quickstart/coding.js +75 -3
  270. package/dist/quickstart/config.d.ts +2 -0
  271. package/dist/quickstart/config.js +4 -0
  272. package/dist/quickstart/images.d.ts +13 -0
  273. package/dist/quickstart/images.js +80 -12
  274. package/dist/quickstart/index.d.ts +0 -2
  275. package/dist/quickstart/index.js +10 -14
  276. package/dist/quickstart/manifests.d.ts +56 -0
  277. package/dist/quickstart/manifests.js +523 -0
  278. package/dist/quickstart/python-detect.d.ts +10 -0
  279. package/dist/quickstart/python-detect.js +32 -0
  280. package/dist/quickstart/repo-packages.d.ts +10 -0
  281. package/dist/quickstart/repo-packages.js +132 -0
  282. package/dist/quickstart/reviewer-prompt.d.ts +12 -0
  283. package/dist/quickstart/reviewer-prompt.js +84 -0
  284. package/dist/quickstart/scenarios.d.ts +19 -0
  285. package/dist/quickstart/scenarios.js +28 -0
  286. package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
  287. package/dist/quickstart/sync-reviewer-prompt.js +10 -0
  288. package/dist/quickstart-images.json +1 -1
  289. package/dist/sandbox/brokered-fetch.d.ts +23 -0
  290. package/dist/sandbox/brokered-fetch.js +37 -0
  291. package/dist/sandbox/host-functions.d.ts +34 -0
  292. package/dist/sandbox/host-functions.js +144 -80
  293. package/dist/sandbox/prelude.js +7 -1
  294. package/dist/sandbox/run-in-sandbox.d.ts +19 -0
  295. package/dist/sandbox/run-in-sandbox.js +28 -10
  296. package/dist/sandbox/safe-fetch.d.ts +2 -0
  297. package/dist/sandbox/safe-fetch.js +3 -1
  298. package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
  299. package/dist/sandbox/secret-broker-sigv4.js +49 -0
  300. package/dist/sandbox/secret-broker.d.ts +34 -0
  301. package/dist/sandbox/secret-broker.js +162 -0
  302. package/dist/sandbox/user-tool.d.ts +14 -0
  303. package/dist/sandbox/user-tool.js +32 -0
  304. package/dist/serve.js +18 -0
  305. package/dist/viewer/api-schema.d.ts +214 -18
  306. package/dist/viewer/api-schema.js +22 -0
  307. package/dist/viewer/build-schemas.js +4 -1
  308. package/dist/viewer/event-bus.d.ts +6 -0
  309. package/dist/viewer/event-bus.js +16 -7
  310. package/dist/viewer/graph.d.ts +5 -2
  311. package/dist/viewer/graph.js +7 -2
  312. package/dist/viewer/http.d.ts +0 -1
  313. package/dist/viewer/http.js +2 -2
  314. package/dist/viewer/infra.js +25 -2
  315. package/dist/viewer/limits.d.ts +4 -0
  316. package/dist/viewer/limits.js +4 -0
  317. package/dist/viewer/openapi.d.ts +15 -0
  318. package/dist/viewer/openapi.js +172 -0
  319. package/docs/README.md +7 -0
  320. package/docs/agent-recipes.md +57 -8
  321. package/docs/architecture-runtime.md +16 -1
  322. package/docs/code-review-agents.md +139 -5
  323. package/docs/coding-agent-setup.md +127 -2
  324. package/docs/coding-packages.md +105 -5
  325. package/docs/coding-services.md +3 -1
  326. package/docs/coding-worker-byo-images.md +239 -2
  327. package/docs/coding-worker-isolation.md +88 -23
  328. package/docs/getting-started-gke.md +225 -9
  329. package/docs/getting-started-identity-provider.md +21 -10
  330. package/docs/getting-started.md +140 -11
  331. package/docs/jira-agents.md +5 -1
  332. package/docs/knowledge.md +11 -0
  333. package/docs/native-sandbox.md +635 -0
  334. package/docs/release-verification.md +9 -0
  335. package/docs/security-deployment.md +113 -12
  336. package/docs/slack-notifications.md +252 -0
  337. package/docs/tool-secrets.md +252 -0
  338. package/docs/viewer-api.md +46 -5
  339. package/docs/viewer-api.openapi.yml +1404 -0
  340. package/help/admin-viewer.md +6 -2
  341. package/help/agent-recipes.md +27 -0
  342. package/help/architecture-agent.md +136 -0
  343. package/help/brokered-secrets.md +79 -0
  344. package/help/build-claude-code-image.md +131 -0
  345. package/help/build-worker-image.md +195 -0
  346. package/help/code-review-agents.md +68 -6
  347. package/help/coding-packages.md +31 -2
  348. package/help/creating-agents.md +14 -0
  349. package/help/deploy-gke.md +37 -6
  350. package/help/deployment-targets.md +24 -13
  351. package/help/errors/coding-provider-not-configured.md +2 -2
  352. package/help/errors/database-auth-invalid.md +28 -0
  353. package/help/errors/github-account-not-allowed.md +25 -0
  354. package/help/errors/low-power-disabled.md +29 -0
  355. package/help/errors/low-power-scale-forbidden.md +35 -0
  356. package/help/errors/low-power-wake-timeout.md +30 -0
  357. package/help/errors/mobile-disabled.md +31 -0
  358. package/help/errors/native-sandbox-capacity.md +22 -0
  359. package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
  360. package/help/errors/native-sandbox-docker-failed.md +20 -0
  361. package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
  362. package/help/errors/native-sandbox-image-not-pinned.md +19 -0
  363. package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
  364. package/help/errors/native-sandbox-network-unenforced.md +22 -0
  365. package/help/errors/native-sandbox-requires-catalog.md +31 -0
  366. package/help/errors/native-sandbox-unavailable.md +19 -0
  367. package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
  368. package/help/errors/native-sandbox-worker-exited.md +20 -0
  369. package/help/errors/native-sandbox-worker-lost.md +20 -0
  370. package/help/errors/native-sandbox-worker-unready.md +22 -0
  371. package/help/errors/secret-broker-body-invalid.md +26 -0
  372. package/help/errors/secret-broker-changed.md +20 -0
  373. package/help/errors/secret-broker-config-invalid.md +30 -0
  374. package/help/errors/secret-broker-conflict.md +27 -0
  375. package/help/errors/secret-broker-destination-denied.md +28 -0
  376. package/help/errors/secret-broker-value-invalid.md +28 -0
  377. package/help/errors/secret-brokered.md +24 -0
  378. package/help/errors/secret-not-brokered.md +25 -0
  379. package/help/errors/slack-auth-failed.md +48 -0
  380. package/help/errors/slack-channel-unreachable.md +39 -0
  381. package/help/errors/slack-not-configured.md +33 -0
  382. package/help/getting-started.md +18 -0
  383. package/help/github.md +18 -1
  384. package/help/identity-and-access.md +5 -1
  385. package/help/local-repositories.md +35 -5
  386. package/help/low-power-mode.md +125 -0
  387. package/help/merge-order-check.md +117 -0
  388. package/help/mobile-app.md +54 -0
  389. package/help/native-sandbox.md +136 -0
  390. package/help/observability.md +8 -0
  391. package/help/related-pull-requests.md +70 -9
  392. package/help/repo-instructions-and-skills.md +91 -0
  393. package/help/review-fix-rounds.md +10 -0
  394. package/help/slack-notifications.md +62 -0
  395. package/help/troubleshooting/coding-workers.md +21 -0
  396. package/package.json +13 -5
  397. package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
  398. package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
  399. package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
  400. package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
  401. package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
  402. package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
  403. package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
  404. package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
  405. package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
  406. package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
  407. package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
  408. package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
  409. package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
  410. package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
  411. package/prisma/schema.prisma +362 -91
@@ -7,6 +7,9 @@ wardby's published **driver base image** and point your agent's
7
7
  `workerImageRef` at it, instead of waiting for wardby to hand-author a new
8
8
  `Dockerfile.<toolchain>`.
9
9
 
10
+ The driver image is for Codex agents. A Claude Code agent's image starts from a
11
+ different base; see [Codex and Claude Code](#codex-and-claude-code).
12
+
10
13
  ## The driver image
11
14
 
12
15
  `ghcr.io/wardby/wardby/wardby-coding-worker-driver` contains wardby's
@@ -14,7 +17,13 @@ compiled Node.js coding-worker driver, `git`, `ca-certificates`, and the
14
17
  `wardby` user (uid/gid 10001) — nothing language-specific. It's built from
15
18
  `src/coding-worker/Dockerfile.driver` and published on `driver-vN` git tags;
16
19
  each release's GitHub Release notes carry the resolved
17
- `@sha256:...` digest to pin.
20
+ `@sha256:...` digest to pin. `wardby doctor` prints the digest your installed
21
+ version's own workers are built on ("Base image for your own worker images:
22
+ …"); build on that one so your image matches the run input your Wardby sends.
23
+
24
+ An MCP assistant connected to Wardby can do the whole procedure for you: the
25
+ `build-worker-image` help article walks it through finding the toolchain,
26
+ writing and checking the Dockerfile, and setting `workerImageRef`.
18
27
 
19
28
  The image deliberately stops before setting `USER`, `WORKDIR`, or
20
29
  `ENTRYPOINT`, and before any of the hardened binary-absence checks wardby's
@@ -54,7 +63,38 @@ own READMEs tell it to — reports a failed command even when the suite is
54
63
  green. `Dockerfile.node-python` symlinks `python` to `python3` for exactly
55
64
  that reason, and asserts both work.
56
65
 
57
- Build it, push it to your own registry, and note the resulting digest —
66
+ ### Design for the run's filesystem
67
+
68
+ A run's root filesystem is read-only; `/tmp` and `/home/wardby` are empty
69
+ `noexec` tmpfs mounts of 16 to 64 MB (anything the image put there is hidden);
70
+ `/workspace` (the checkout, `CODING_DISK_MB` large, or the agent's
71
+ `codingProfile.workspaceDiskMb`) is the only place a run can write and execute;
72
+ and a run reaches no package registry except Wardby's npm and PyPI proxy. So:
73
+
74
+ - point every cache, build output and temp directory under `/workspace/.cache/`
75
+ (a `.cache` folder is never collected into the result, at any depth), and
76
+ create those directories from a small wrapper around the toolchain command,
77
+ keeping the `ENTRYPOINT` unchanged. For Go: `GOCACHE`, `GOTMPDIR` (`go test`
78
+ runs its test binary from there) and `GOMODCACHE`;
79
+ - bake dependencies into a read-only location the toolchain only reads from
80
+ (Go: a file-based module proxy filled by `go mod download`, used through
81
+ `GOPROXY=file://...`); a baked cache cannot be written to during a run;
82
+ - add build folders that stay in the checkout (Maven `target`, Gradle `build`)
83
+ to `codingProfile.collectExclude`.
84
+
85
+ Before using the image, run the project's tests against a throwaway clone with
86
+ a run's restrictions: `--read-only`, `--tmpfs /tmp:rw,noexec,nosuid,size=64m`,
87
+ `--tmpfs /home/wardby:rw,noexec,nosuid,size=64m,uid=10001,gid=10001`,
88
+ `--network none`, `--user 10001:10001`, `--cap-drop ALL` and
89
+ `--security-opt no-new-privileges`. The `build-worker-image` help article has a
90
+ complete Go Dockerfile that passes this check, recipes for Rust and Java, and
91
+ the exact command.
92
+
93
+ ### Build and pin it
94
+
95
+ On a local quickstart install with the Docker launcher, `workerImageRef` can be
96
+ the local image ID (`docker image inspect --format '{{.Id}}' <your-tag>`).
97
+ Otherwise, build it, push it to your own registry, and note the resulting digest —
58
98
  `docker inspect --format '{{index .RepoDigests 0}}' <your-tag>` after a push,
59
99
  or read it straight from `docker buildx build --push`'s output.
60
100
 
@@ -75,6 +115,189 @@ point one at an arbitrary image without the step-up scope. The scope alone
75
115
  isn't enough: the caller must also hold the admin role (see
76
116
  [roles and privileged operations](security-deployment.md#roles-and-privileged-operations)).
77
117
 
118
+ ## Codex and Claude Code
119
+
120
+ A Codex agent runs in one container, and its commands run there too. A Claude
121
+ Code agent uses two containers: one runs Claude Code and holds its API key; the
122
+ other runs the agent's commands and has your repository. `workerImageRef` always
123
+ replaces the container that runs commands.
124
+
125
+ | | Codex agent | Claude Code agent |
126
+ | ------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
127
+ | What `workerImageRef` replaces | the agent's only container | the container that runs commands |
128
+ | Build your image `FROM` | [the coding-worker driver image](#the-driver-image) (`driver-vN`) | the `wardby-claude-tool-runner` image from the same wardby release (or `wardby-claude-tool-runner-node-python`) |
129
+ | Rebuild when | the driver image changes | you upgrade wardby |
130
+ | Claude Code itself runs on | — | wardby's standard image, never yours |
131
+
132
+ The driver image sections above are for Codex. The subsections below are for
133
+ Claude Code. In them, "tool runner" means the container that runs a Claude Code
134
+ agent's commands.
135
+
136
+ ### Build a custom image for a Claude Code agent
137
+
138
+ 1. **Find your release's tool runner image.** It is the value of
139
+ `CODING_CLAUDE_TOOL_RUNNER_IMAGE` on your server. To start from Node and
140
+ Python, use `CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12` instead. A
141
+ release value looks like
142
+ `ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:<digest>`. If the
143
+ value is a local image ID (`sha256:...` only), give it a name first, because
144
+ Docker cannot build `FROM` a bare ID:
145
+ `docker tag sha256:<id> wardby-tool-runner-base:local`.
146
+ 2. **Make a build folder.** Use a small folder of your own, outside your
147
+ repository. Put in it the Dockerfile and only the files the Dockerfile
148
+ copies.
149
+ 3. **Write the Dockerfile.** Start from the image in step 1, install your tools
150
+ as `root`, then switch back to the tool runner's user:
151
+
152
+ ```dockerfile
153
+ FROM <image from step 1>
154
+ USER root
155
+ RUN apt-get update \
156
+ && apt-get install -y --no-install-recommends <your packages> \
157
+ && rm -rf /var/lib/apt/lists/*
158
+ USER 10001:10001
159
+ ```
160
+
161
+ Keep these rules:
162
+ - The last `USER` line is `USER 10001:10001`.
163
+ - Do not set `ENTRYPOINT` or `CMD`. The tool runner starts with the base
164
+ image's own entrypoint.
165
+ - Install everything at build time. A run cannot install system packages.
166
+ - Put tools in `/usr/local/bin` or `/usr/bin`. Do not put anything under
167
+ `/tmp` or `/home/wardby`: a run hides both.
168
+ - Put environment settings your commands need in a file under
169
+ `/etc/profile.d/` (see "How the tool runner runs commands" below). `ENV`
170
+ lines in the Dockerfile do not reach the agent's commands.
171
+
172
+ 4. **Build it:** `docker build --tag <name> <build folder>`. Release images are
173
+ `linux/amd64`. On an ARM machine, add `--platform linux/amd64` here and to
174
+ `docker run` in step 5.
175
+ 5. **Run the tests the way a run would.** Clone the repository to a throwaway
176
+ folder (on Linux, make it writable: `chmod -R a+rwX <clone>`). Then run the
177
+ project's test command with a run's restrictions and its fixed environment,
178
+ so `ENV` lines in the image do not apply:
179
+
180
+ ```sh
181
+ docker run --rm --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m --tmpfs /home/wardby:rw,noexec,nosuid,size=64m,uid=10001,gid=10001,mode=0700 --network none --user 10001:10001 --cap-drop ALL --security-opt no-new-privileges -v <clone>:/workspace -w /workspace --entrypoint env <name> -i HOME=/home/wardby LANG=C.UTF-8 PATH=/opt/wardby/bin:/usr/local/bin:/usr/bin:/bin sh -lc '<test command>'
182
+ ```
183
+
184
+ On an ARM machine, add `--platform linux/amd64` after `docker run`. Fix the
185
+ Dockerfile until the tests pass, then delete the clone.
186
+
187
+ 6. **Get an immutable reference.** A tag is refused.
188
+ - **Docker launcher** (wardby runs coding jobs with Docker on a single
189
+ machine): use the local image ID,
190
+ `docker image inspect --format '{{.Id}}' <name>`. Build on that machine.
191
+ Wardby never pulls these images.
192
+ - **Kubernetes:** push the image to a registry your cluster can pull from,
193
+ and use `<registry>/<name>@sha256:<digest>`.
194
+ 7. **Point the agent at it.** Call `update_agent` with the agent's id. This
195
+ needs the `agents:admin` scope and the admin role:
196
+
197
+ ```json
198
+ {
199
+ "id": "<agent id>",
200
+ "codingProfile": { "workerImageRef": "<reference from step 6>" }
201
+ }
202
+ ```
203
+
204
+ 8. **Try it.** Trigger a small task that uses your tools, such as "Run the
205
+ project's tests and report the results. Change nothing."
206
+
207
+ To undo it, set `codingProfile.workerImageRef` to `null`. The agent then uses
208
+ the standard tool runner for its `toolchain`.
209
+
210
+ ### Languages other than Node and Python
211
+
212
+ A run can download packages only from wardby's package registry, which serves
213
+ npm and PyPI. Packages for any other language must be in the image. If a
214
+ package compiles native code, compile it in an earlier build stage and copy
215
+ only the results into the final image. The final image then needs no compiler.
216
+
217
+ The tool runner is based on Debian 12 (bookworm). Build stages that compile
218
+ code must use a bookworm-based image too, so the results work with the tool
219
+ runner's system libraries.
220
+
221
+ This example adds Ruby and a project's gems. It is a starting point to adapt,
222
+ not a tested recipe. Copy the project's `Gemfile` and `Gemfile.lock` into the
223
+ build folder first.
224
+
225
+ ```dockerfile
226
+ # Stage 1: install Ruby and the project's gems, compiling any native code.
227
+ FROM ruby:3.3-slim-bookworm AS builder
228
+ RUN apt-get update \
229
+ && apt-get install -y --no-install-recommends build-essential \
230
+ && rm -rf /var/lib/apt/lists/*
231
+ ENV BUNDLE_PATH=/usr/local/bundle \
232
+ BUNDLE_DEPLOYMENT=true
233
+ WORKDIR /build
234
+ COPY Gemfile Gemfile.lock ./
235
+ RUN bundle install
236
+
237
+ # Stage 2: the tool runner from your wardby release, plus Ruby and the gems.
238
+ FROM <tool runner image from step 1>
239
+ USER root
240
+ # Shared libraries Ruby needs at run time, plus any library your native gems
241
+ # link to (for example libpq5 for pg).
242
+ RUN apt-get update \
243
+ && apt-get install -y --no-install-recommends libffi8 libgmp10 libssl3 libyaml-0-2 zlib1g \
244
+ && rm -rf /var/lib/apt/lists/*
245
+ # Ruby lives in /usr/local, and the gems in /usr/local/bundle.
246
+ COPY --from=builder /usr/local /usr/local
247
+ # Settings for the agent's commands (Dockerfile ENV lines do not reach them).
248
+ RUN ldconfig \
249
+ && printf '%s\n' \
250
+ 'export GEM_HOME=/usr/local/bundle' \
251
+ 'export BUNDLE_PATH=/usr/local/bundle' \
252
+ 'export BUNDLE_APP_CONFIG=/usr/local/bundle' \
253
+ 'export BUNDLE_DEPLOYMENT=true' \
254
+ > /etc/profile.d/ruby.sh \
255
+ && sh -lc 'ruby --version && bundle --version'
256
+ USER 10001:10001
257
+ ```
258
+
259
+ Pin the `ruby` image by digest for repeatable builds. Rebuild the image when
260
+ the project's `Gemfile.lock` changes.
261
+
262
+ ### If a run fails
263
+
264
+ | What you see | What it usually means |
265
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
266
+ | `worker_tool_runner_unreachable` | The image was not built `FROM` the tool runner of the same wardby release, or the Dockerfile changed its `USER` or `ENTRYPOINT`. |
267
+ | A command is "not found" | The tool is not in the image, or a setting it needs is in an `ENV` line instead of `/etc/profile.d/`. |
268
+ | "Permission denied" when running a file | On Docker, something writes a program to `/tmp` or `/home/wardby` and runs it. Point that tool's temporary or cache folder under `/workspace/.cache/`. |
269
+
270
+ ### How the tool runner runs commands
271
+
272
+ These details explain the rules above.
273
+
274
+ - **User and privileges.** Commands always run as user `10001`, with no Linux
275
+ capabilities and `no-new-privileges`. `root` and setuid programs do not work
276
+ at run time.
277
+ - **Filesystem.** The root filesystem is read-only. `/tmp` and `/home/wardby`
278
+ are small, empty scratch space; anything the image put there is hidden. On
279
+ Docker, files there cannot be run. `/workspace` holds the repository; put
280
+ caches, build output and temporary files under `/workspace/.cache/`.
281
+ - **Shell and environment.** Each command runs as `sh -lc "<command>"` with a
282
+ fixed environment: `HOME=/home/wardby`, `LANG=C.UTF-8`, `TMPDIR` under
283
+ `/workspace/.cache/`, wardby's package registry settings, and
284
+ `PATH=/opt/wardby/bin:/usr/local/bin:/usr/bin:/bin`. The login shell reads
285
+ `/etc/profile` and the files in `/etc/profile.d/`, so that is where your own
286
+ settings go. `/etc/profile` resets `PATH` to Debian's default, so add any
287
+ other folder to `PATH` in your `/etc/profile.d/` file.
288
+ - **Network.** The tool runner reaches only wardby's package registry (npm and
289
+ PyPI).
290
+ - **Images on the Docker launcher are never pulled.** When wardby runs coding
291
+ jobs with Docker on a single machine, the image must already be on that
292
+ machine. Kubernetes needs a registry digest
293
+ (`repo@sha256:<64 hex>`).
294
+ - **Server settings.** Claude Code itself always runs on
295
+ `CODING_CLAUDE_WORKER_IMAGE`. The server still needs both
296
+ `CODING_CLAUDE_WORKER_IMAGE` and `CODING_CLAUDE_TOOL_RUNNER_IMAGE` set, even
297
+ for agents with their own `workerImageRef`.
298
+ - **Toolchain.** While `workerImageRef` is set, `toolchain` and
299
+ `toolchainVersion` do not choose the image, for either builder.
300
+
78
301
  ## Running services with a BYO image
79
302
 
80
303
  An agent whose `codingProfile.services` allows a service ([coding
@@ -85,6 +308,20 @@ a worker built on an older driver base doesn't recognize the input's
85
308
  without them. Rebuild your image on a current `driver-vN` digest (see "The
86
309
  driver image" above) before allowing any service on an agent that uses it.
87
310
 
311
+ ## Repository skills need a current driver
312
+
313
+ On a Codex agent, a `workerImageRef` image never receives Claude Code's
314
+ repository context (`claudeContext`) or `claudeBareMode`. The one new input key
315
+ it can see is `repoSkills`, which wardby writes only when an agent sets
316
+ `repoSkills: false`. A worker built on
317
+ an older driver base validates its input with a strict schema, so it rejects
318
+ runs of agents with `repoSkills: false` (the run fails with
319
+ `worker_input_failed`), and it keeps offering Codex's built-in skills
320
+ (`imagegen`, `openai-docs`, `skill-creator`, `skill-installer`), which a
321
+ current driver always disables. Rebuild any `workerImageRef` image on a
322
+ current `driver-vN` digest (see "The driver image" above) before relying on
323
+ either behavior.
324
+
88
325
  ## What this doesn't cover
89
326
 
90
327
  wardby does not re-validate the contents of your built image beyond the
@@ -1,16 +1,24 @@
1
1
  # Coding Worker Isolation
2
2
 
3
- Wardby supports isolated Codex execution with Docker or Kubernetes and
4
- credential-separated Claude Code execution with Docker. Both launchers fail
5
- closed when the effective runtime does not match the reviewed policy.
3
+ Wardby runs Codex and Claude Code coding workers through Docker or Kubernetes
4
+ launchers. Both fail closed when the effective runtime does not match the
5
+ reviewed policy. Claude Code separates its model-facing worker from its
6
+ workspace-facing tool runner: Docker uses companion containers, while
7
+ Kubernetes uses a native sidecar in the run pod.
6
8
 
7
9
  ## Security Boundary
8
10
 
9
- Coding-agent repositories and instructions are untrusted. The Docker daemon,
10
- host kernel, Wardby control plane, immutable worker image, and dedicated coding
11
- proxy are trusted. Containers are defense in depth rather than a VM boundary;
12
- production should run the Docker host on a dedicated worker node or VM with no
13
- production credentials beyond those required by the proxy.
11
+ Coding-agent repositories and instructions are untrusted. The Wardby control
12
+ plane, immutable worker images, dedicated coding proxy, and the platform
13
+ enforcing container and network policies are trusted.
14
+
15
+ ### Docker launcher
16
+
17
+ The Docker daemon and host kernel are trusted. Docker containers are defense
18
+ in depth rather than a VM boundary. Production should run the Docker host on a
19
+ dedicated worker node or VM with no production credentials beyond those
20
+ required by the proxy. The Kubernetes launcher section below describes the
21
+ corresponding pod and network rules.
14
22
 
15
23
  The Codex worker has one network attachment: a unique per-run internal bridge using
16
24
  Docker's isolated gateway mode. It has no default external route, published
@@ -588,6 +596,7 @@ Kubernetes-specific settings (`src/config/providers.ts`,
588
596
  | `KUBERNETES_CONTEXT` | (unset → in-cluster/default kubeconfig context) | Which kubeconfig context `ClientNodeKubernetesApi` connects with. |
589
597
  | `KUBERNETES_RUNTIME_CLASS` | (unset) | e.g. `gvisor` on GKE. Unset means pods run without a sandboxing runtime class — logged once per launch as `kubernetes_runtime_class_unset` and development-only. |
590
598
  | `KUBERNETES_RESOURCE_QUOTA` | (unset) | Name of the namespace ResourceQuota run pods count against, e.g. `wardby-coding` in the GKE overlay. When set, a run whose pod would not fit the quota's free room waits in the coding queue instead of failing at pod create. Needs `get` on that one `resourcequotas` object. Unset means no check. |
599
+ | `KUBERNETES_PLATFORM` | `generic` | `generic`, `gke-autopilot` or `eks`. Selects the cluster's admission rules that run pods are conformed to. `gke-autopilot` and `eks` both require `KUBERNETES_RUNTIME_CLASS=gvisor`; see the platform profile section below. |
591
600
  | `KUBERNETES_RUN_PRIORITY_CLASS` | (unset) | PriorityClass for run pods and the preflight canary, e.g. `wardby-coding-run` in the GKE overlay. Must be an existing class and a DNS-1123 subdomain; `system-` classes are refused. Unset means no class (priority 0). |
592
601
 
593
602
  The `CODING_CPUS` / `CODING_MEMORY_MB` / `CODING_PIDS` / `CODING_DISK_MB` /
@@ -798,6 +807,16 @@ landed on — cannot appear in it. That case reached a live Autopilot launch wit
798
807
  every dry-run-derived check passing, and is now pinned by tests. Treat a
799
808
  captured fixture as a lower bound on what a platform mutates.
800
809
 
810
+ The `eks` profile is for Amazon EKS clusters with a dedicated gVisor node group.
811
+ EKS has no managed sandbox runtime, so the cluster must provide a RuntimeClass
812
+ named `gvisor` with handler `runsc` whose `scheduling` block selects nodes
813
+ labelled `wardby.dev/sandbox=gvisor` and tolerates the taint
814
+ `sandbox=gvisor:NoSchedule` on those nodes. Kubernetes' RuntimeClass admission
815
+ merges that block into each pod; Wardby sets neither the node selector nor the
816
+ toleration itself, and the `eks` profile forgives exactly that nodeSelector and
817
+ that toleration and nothing else. Pod resources follow the generic rules, plus
818
+ an explicit ephemeral-storage request and limit.
819
+
801
820
  ### The enforcement gate
802
821
 
803
822
  The Kubernetes API can create a NetworkPolicy object without that policy
@@ -808,12 +827,12 @@ not just the harness: the worker gate could otherwise
808
827
  open on a pod whose isolation isn't active yet.
809
828
 
810
829
  The fix, before seeding or opening the worker gate: the launcher execs into
811
- the keeper (which shares the pod's network namespace with the worker) one
812
- `node -e` probe that measures **both of the coding proxy's ports against the
813
- proxy Service's ClusterIP, in the same pass** — `8787` (the proxy itself, which
814
- the run policy permits) and `8788` (the deny port, which no run policy ever
815
- permits). Only the outcome **(8787 connected, 8788 blocked)** counts toward the
816
- streak.
830
+ the keeper (which shares the pod's network namespace with the worker) a
831
+ `node -e` script whose every probe measures **both of the coding proxy's ports
832
+ against the proxy Service's ClusterIP, in the same pass** — `8787` (the proxy
833
+ itself, which the run policy permits) and `8788` (the deny port, which no run
834
+ policy ever permits). Only the outcome **(8787 connected, 8788 blocked)** counts
835
+ toward the streak.
817
836
 
818
837
  Stated exactly, that outcome proves: **the SYN to 8788 was dropped somewhere on
819
838
  the path, while the same destination answered on 8787.** That the drop was the
@@ -861,10 +880,21 @@ from "unserved" is not a witness — and such a cluster needs a different one.
861
880
 
862
881
  It requires **3 consecutive proven results, 500ms apart** (anything else
863
882
  resets the streak — this guards against a single dropped SYN packet on an
864
- allowed path being misread as "policy enforced"), bounded by
865
- `enforcementTimeoutMs` (default 30,000ms — configurable via
883
+ allowed path being misread as "policy enforced"). The whole streak runs in a
884
+ single keeper exec: the script exits successfully only after three consecutive
885
+ proven probes, and stops at the first probe that is not proven, exiting with
886
+ that probe's result. A broken streak is retried from zero 500ms later, bounded
887
+ by `enforcementTimeoutMs` (default 30,000ms — configurable via
866
888
  `KubernetesJobLauncherOptions.enforcementTimeoutMs`; a drop-style CNI can
867
- need close to this whole window). The verdict at the bound comes from the
889
+ need close to this whole window). Each probe has a time budget of
890
+ `KUBERNETES_ENFORCEMENT_EXEC_TIMEOUT_MS` (default 10,000ms), so one streak exec
891
+ is allowed three budgets plus the two 500ms gaps (31,000ms at the default), and
892
+ the launcher never lets `enforcementTimeoutMs` fall below that one-streak value
893
+ — at the defaults the effective bound is therefore 31,000ms. The bound is
894
+ checked between streak execs, so a launch can run past it by up to one streak
895
+ exec. An exec that exceeds its timeout fails the launch with
896
+ `kubernetes_exec_timeout`. The
897
+ verdict at the bound comes from the
868
898
  _last_ probe — not from whether any probe was ever unavailable, so an early
869
899
  blip while the pod's networking came up does not misdirect the operator — and
870
900
  the three non-proven outcomes stay distinct, because each sends an operator
@@ -900,9 +930,9 @@ default 90,000ms):
900
930
  1. `platform` — pure configuration, checked before any cluster API call:
901
931
  `assertPlatformConfig` (`src/providers/jobs/kubernetes-platform.ts`) refuses
902
932
  a deployment that cannot work under `KUBERNETES_PLATFORM`. Under
903
- `gke-autopilot` this means `KUBERNETES_RUNTIME_CLASS` must be `gvisor`
904
- (gVisor is mandatory there — an unset or different runtime class is refused,
905
- not warned about), and the effective `CODING_MAX_DISK_MB` must leave room
933
+ `gke-autopilot` and `eks`, `KUBERNETES_RUNTIME_CLASS` must be `gvisor` (an
934
+ unset or different runtime class is refused, not warned about). Under
935
+ `gke-autopilot` the effective `CODING_MAX_DISK_MB` must also leave room
906
936
  for the worker container's 1 GiB reservation inside Autopilot's 10 GiB pod
907
937
  ephemeral-storage ceiling. The same assertion runs again at process
908
938
  start-up in `buildConfiguredExecutor`
@@ -931,20 +961,55 @@ default 90,000ms):
931
961
  5. `canary` — creates a real run pod + NetworkPolicy from the same builders
932
962
  as a live run, running a script that waits for policy enforcement (as
933
963
  above) then attempts DNS resolution, a connect to the proxy's deny port,
934
- the internet (`1.1.1.1:443`), the metadata server, and the proxy itself —
935
- requiring every one of the first four to fail and the proxy connect to
964
+ the internet (`1.1.1.1:443`), the cloud metadata endpoints (`169.254.169.254:80`,
965
+ the EKS Pod Identity agent `169.254.170.23:80` and its IPv6 address
966
+ `[fd00:ec2::23]:80`, and EC2's IPv6 metadata address `[fd00:ec2::254]:80`), and the proxy itself —
967
+ requiring every one of the first four kinds to fail and the proxy connect to
936
968
  succeed. Any other outcome, or a canary pod that itself fails to
937
969
  schedule/run, is `kubernetes_isolation_unsupported:canary`.
938
970
 
939
971
  This whole preflight is **memoized per launcher instance and its failure is
940
972
  sticky**: `KubernetesJobLauncher.runPreflight()` caches the first call's
941
- promise (`this.preflightResult ??= ...`, `kubernetes.ts:472-487`), including
973
+ promise (`this.preflightResult ??= ...`, `kubernetes.ts:790`), including
942
974
  a rejection — so once a launcher process has seen preflight fail, every
943
975
  subsequent `launch()` in that process fails immediately with the same error
944
976
  without re-probing the cluster. A fresh preflight requires a new process
945
977
  (or, from the CLI, a fresh `wardby coding preflight` invocation, which is
946
978
  not memoized).
947
979
 
980
+ **A long-running server process starts this same memoized preflight at
981
+ start-up, not on the first coding run.** `wardby serve`, `wardby mcp` (both
982
+ transports), and `wardby scheduler` each call `KubernetesJobLauncher.warmUp()`
983
+ right after the executor is built, fire-and-forget: it runs the identical
984
+ preflight `launch()` would otherwise run lazily, so the first coding run
985
+ after a restart doesn't pay the preflight's own cost (a canary pod, routinely
986
+ tens of seconds on a resource-constrained cluster such as `kind`).
987
+ `wardby scheduler` needs this just as much as the other two — it dispatches
988
+ scheduled coding runs through its own Kubernetes executor without ever
989
+ starting an MCP server, so without this it would still pay the lazy cost on
990
+ its first scheduled run. The result is logged once at start-up — an info
991
+ line on success, a warning naming the failure code (e.g.
992
+ `kubernetes_isolation_unsupported:<check>`) otherwise.
993
+
994
+ **A start-up warm-up's failure is logged and retried on the next run, not
995
+ left stuck until restart.** This is the one way a preflight failure is
996
+ _not_ memoized: a transient cluster problem at process start (the API
997
+ server briefly unreachable, a slow CNI not yet programmed, and the like)
998
+ must not fail every coding run for the rest of that process's life, so a
999
+ failed warm-up clears its own failed attempt once it has logged it, and the
1000
+ _next_ `launch()` runs the preflight fresh — succeeding if the cluster has
1001
+ since recovered. A `launch()` that was already waiting on that same
1002
+ in-flight warm-up attempt still fails with that attempt's error (it shares
1003
+ the same preflight call), exactly as it always has; it's only the attempt
1004
+ _after_ that one which retries. A preflight failure `launch()` triggers
1005
+ itself — because no warm-up ran, or because a warm-up's cleared failure was
1006
+ never retried before the next `launch()` found the cluster still broken —
1007
+ keeps the original behavior exactly: it stays memoized, failing every
1008
+ subsequent `launch()` in that process until it is restarted. This never
1009
+ runs for a one-shot CLI command (`wardby run`, `wardby coding preflight`,
1010
+ migrations, imports) — only for a process that stays up to serve or
1011
+ dispatch runs.
1012
+
948
1013
  **A hung pod create during preflight can leave a preflight pod and its
949
1014
  NetworkPolicy behind.** If `createPod` never settles (rather than failing),
950
1015
  the preflight's own timeout still fires and the caller sees `:timeout`, but