forgeos 0.1.0-alpha.4 → 0.1.0-alpha.41

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 (416) hide show
  1. package/.npmignore +10 -0
  2. package/AGENTS.md +142 -90
  3. package/CHANGELOG.md +433 -0
  4. package/LICENSE +21 -0
  5. package/README.md +67 -8
  6. package/adapters/go/README.md +23 -0
  7. package/adapters/go/go.mod +3 -0
  8. package/adapters/go/http.go +149 -0
  9. package/adapters/go/registry.go +234 -0
  10. package/adapters/go/types.go +136 -0
  11. package/adapters/java/README.md +68 -0
  12. package/adapters/java/pom.xml +34 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  33. package/adapters/java-spring-boot-starter/README.md +32 -0
  34. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  35. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  36. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  37. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  38. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  39. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  41. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  42. package/bin/forge.mjs +18 -0
  43. package/docs/cair-protocol.md +103 -0
  44. package/docs/changelog.md +548 -0
  45. package/docs/forge-protocol.md +189 -0
  46. package/examples/go-billing/go.mod +7 -0
  47. package/examples/go-billing/main.go +120 -0
  48. package/examples/java-billing/pom.xml +52 -0
  49. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  50. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  51. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  52. package/package.json +31 -3
  53. package/schemas/forge-manifest.schema.json +57 -0
  54. package/src/forge/_generated/releaseManifest.json +1 -2
  55. package/src/forge/_generated/releaseManifest.ts +3 -3
  56. package/src/forge/agent-adapters/index.ts +1584 -125
  57. package/src/forge/agent-adapters/types.ts +224 -1
  58. package/src/forge/agent-memory/bridge.ts +1333 -0
  59. package/src/forge/agent-memory/context-pack.ts +277 -0
  60. package/src/forge/agent-memory/hook-runner.ts +312 -0
  61. package/src/forge/agent-memory/mcp.ts +224 -0
  62. package/src/forge/agent-memory/normalize.ts +498 -0
  63. package/src/forge/agent-memory/redaction.ts +103 -0
  64. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  65. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
  66. package/src/forge/agent-memory/sources/codex.ts +119 -0
  67. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  68. package/src/forge/agent-memory/types.ts +204 -0
  69. package/src/forge/bench.ts +248 -0
  70. package/src/forge/brownfield-import/index.ts +801 -0
  71. package/src/forge/brownfield-import/types.ts +127 -0
  72. package/src/forge/cair/action-journal.ts +61 -0
  73. package/src/forge/cair/action-parser.ts +314 -0
  74. package/src/forge/cair/action-validator.ts +40 -0
  75. package/src/forge/cair/actions.ts +1818 -0
  76. package/src/forge/cair/format.ts +77 -0
  77. package/src/forge/cair/index.ts +106 -0
  78. package/src/forge/cair/query.ts +478 -0
  79. package/src/forge/cair/snapshot.ts +315 -0
  80. package/src/forge/cair/types.ts +248 -0
  81. package/src/forge/cli/ai.ts +339 -21
  82. package/src/forge/cli/auth.ts +348 -10
  83. package/src/forge/cli/authmd.ts +441 -0
  84. package/src/forge/cli/baseline.ts +112 -0
  85. package/src/forge/cli/build.ts +20 -4
  86. package/src/forge/cli/changed.ts +437 -0
  87. package/src/forge/cli/codex-app-server.ts +877 -0
  88. package/src/forge/cli/commands.ts +1620 -20
  89. package/src/forge/cli/db.ts +209 -2
  90. package/src/forge/cli/deploy.ts +887 -0
  91. package/src/forge/cli/deps.ts +82 -13
  92. package/src/forge/cli/dev.ts +961 -46
  93. package/src/forge/cli/docs.ts +265 -0
  94. package/src/forge/cli/doctor.ts +297 -0
  95. package/src/forge/cli/field-test.ts +329 -0
  96. package/src/forge/cli/handoff.ts +336 -0
  97. package/src/forge/cli/index.ts +1 -0
  98. package/src/forge/cli/last-run.ts +84 -0
  99. package/src/forge/cli/main.ts +107 -3
  100. package/src/forge/cli/new.ts +152 -13
  101. package/src/forge/cli/next-actions.ts +23 -0
  102. package/src/forge/cli/output.ts +389 -6
  103. package/src/forge/cli/parse.ts +1050 -34
  104. package/src/forge/cli/progress.ts +51 -0
  105. package/src/forge/cli/query.ts +32 -0
  106. package/src/forge/cli/release.ts +35 -11
  107. package/src/forge/cli/rls.ts +43 -4
  108. package/src/forge/cli/run.ts +41 -0
  109. package/src/forge/cli/security.ts +112 -0
  110. package/src/forge/cli/self-host.ts +56 -14
  111. package/src/forge/cli/studio.ts +2218 -0
  112. package/src/forge/cli/verify.ts +1455 -32
  113. package/src/forge/cli/windows.ts +23 -0
  114. package/src/forge/cli/workos.ts +1020 -0
  115. package/src/forge/compiler/agent-contract/build.ts +670 -64
  116. package/src/forge/compiler/agent-contract/types.ts +45 -2
  117. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  118. package/src/forge/compiler/api-surface/build.ts +47 -0
  119. package/src/forge/compiler/app-graph/build.ts +112 -9
  120. package/src/forge/compiler/app-graph/extract.ts +107 -0
  121. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  122. package/src/forge/compiler/app-graph/parser.ts +24 -24
  123. package/src/forge/compiler/app-graph/profile.ts +26 -0
  124. package/src/forge/compiler/app-graph/versions.ts +1 -1
  125. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  126. package/src/forge/compiler/classifier/classify.ts +32 -8
  127. package/src/forge/compiler/classifier/secrets.ts +3 -2
  128. package/src/forge/compiler/classifier/signals.ts +91 -1
  129. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  130. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  131. package/src/forge/compiler/data-graph/parse.ts +20 -4
  132. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  133. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  134. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  135. package/src/forge/compiler/diagnostics/codes.ts +30 -0
  136. package/src/forge/compiler/diagnostics/create.ts +21 -2
  137. package/src/forge/compiler/diagnostics/index.ts +2 -0
  138. package/src/forge/compiler/emitter/barrel.ts +3 -0
  139. package/src/forge/compiler/emitter/render.ts +9 -0
  140. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  141. package/src/forge/compiler/external-manifest/types.ts +91 -0
  142. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  143. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  144. package/src/forge/compiler/integration/add.ts +618 -24
  145. package/src/forge/compiler/integration/plan.ts +43 -12
  146. package/src/forge/compiler/integration/render.ts +29 -0
  147. package/src/forge/compiler/integration/snapshot.ts +2 -0
  148. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  149. package/src/forge/compiler/integration/templates/index.ts +1 -0
  150. package/src/forge/compiler/integration/templates/render.ts +38 -0
  151. package/src/forge/compiler/integration/templates/types.ts +3 -0
  152. package/src/forge/compiler/integration/templates/workos.ts +1219 -0
  153. package/src/forge/compiler/make-registry/build.ts +8 -9
  154. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  155. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  156. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  157. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  158. package/src/forge/compiler/orchestrator/plan.ts +247 -10
  159. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  160. package/src/forge/compiler/orchestrator/run.ts +97 -31
  161. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  162. package/src/forge/compiler/orchestrator/types.ts +1 -1
  163. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  164. package/src/forge/compiler/package-graph/constants.ts +1 -1
  165. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  166. package/src/forge/compiler/package-manager/commands.ts +4 -0
  167. package/src/forge/compiler/package-manager/executor.ts +30 -1
  168. package/src/forge/compiler/package-manager/version.ts +36 -12
  169. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  170. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  171. package/src/forge/compiler/policy-registry/build.ts +47 -2
  172. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  173. package/src/forge/compiler/recipes/definitions.ts +63 -0
  174. package/src/forge/compiler/recipes/index.ts +2 -0
  175. package/src/forge/compiler/recipes/registry.ts +6 -0
  176. package/src/forge/compiler/test-graph/build.ts +11 -3
  177. package/src/forge/compiler/types/app-graph.ts +8 -2
  178. package/src/forge/compiler/types/cli.ts +75 -1
  179. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  180. package/src/forge/compiler/types/emit.ts +2 -0
  181. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  182. package/src/forge/compiler/types/integration.ts +1 -0
  183. package/src/forge/compiler/types/package-graph.ts +6 -0
  184. package/src/forge/compiler/types/policy-registry.ts +3 -1
  185. package/src/forge/delta/classifier.ts +52 -0
  186. package/src/forge/delta/explain.ts +238 -0
  187. package/src/forge/delta/git-observer.ts +43 -0
  188. package/src/forge/delta/ids.ts +44 -0
  189. package/src/forge/delta/index.ts +25 -0
  190. package/src/forge/delta/recorder.ts +540 -0
  191. package/src/forge/delta/redaction.ts +50 -0
  192. package/src/forge/delta/schema.ts +240 -0
  193. package/src/forge/delta/session.ts +142 -0
  194. package/src/forge/delta/status.ts +1202 -0
  195. package/src/forge/delta/store.ts +3284 -0
  196. package/src/forge/delta/timeline.ts +178 -0
  197. package/src/forge/dev/server.ts +808 -17
  198. package/src/forge/dev/types.ts +15 -1
  199. package/src/forge/dev/watch.ts +17 -7
  200. package/src/forge/dev-console/cycle.ts +280 -33
  201. package/src/forge/dev-console/types.ts +47 -1
  202. package/src/forge/impact/index.ts +204 -9
  203. package/src/forge/impact/types.ts +33 -1
  204. package/src/forge/intent/index.ts +35 -16
  205. package/src/forge/make/fields.ts +26 -0
  206. package/src/forge/make/index.ts +40 -6
  207. package/src/forge/make/templates.ts +155 -2
  208. package/src/forge/make/types.ts +2 -1
  209. package/src/forge/policy.ts +1 -1
  210. package/src/forge/react/index.ts +160 -2
  211. package/src/forge/refactor/index.ts +1 -0
  212. package/src/forge/repair/rules/index.ts +2 -2
  213. package/src/forge/review/index.ts +158 -12
  214. package/src/forge/review/types.ts +15 -0
  215. package/src/forge/runtime/auth/config.ts +17 -0
  216. package/src/forge/runtime/auth/evaluate.ts +15 -2
  217. package/src/forge/runtime/auth/resolve.ts +61 -6
  218. package/src/forge/runtime/auth/types.ts +1 -0
  219. package/src/forge/runtime/db/factory.ts +1 -3
  220. package/src/forge/runtime/db/generated-client.ts +13 -2
  221. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  222. package/src/forge/runtime/db/pglite-adapter.ts +263 -2
  223. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  224. package/src/forge/runtime/executor.ts +129 -16
  225. package/src/forge/runtime/external/bridge.ts +649 -0
  226. package/src/forge/runtime/runner/run-entry.ts +16 -7
  227. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  228. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  229. package/src/forge/runtime/webhooks/security.ts +12 -7
  230. package/src/forge/server.ts +17 -3
  231. package/src/forge/ui/index.ts +209 -0
  232. package/src/forge/ui/types.ts +1 -0
  233. package/src/forge/version.ts +1 -1
  234. package/src/forge/vue/index.ts +422 -0
  235. package/src/forge/workspace/baseline.ts +112 -0
  236. package/src/forge/workspace/change-summary.ts +250 -0
  237. package/src/forge/workspace/forge-cli.ts +42 -0
  238. package/src/forge/workspace/git-summary.ts +391 -0
  239. package/templates/agent-workroom/AGENTS.md +29 -0
  240. package/templates/agent-workroom/README.md +34 -0
  241. package/templates/agent-workroom/forge.config.ts +3 -0
  242. package/templates/agent-workroom/package.json +33 -0
  243. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  244. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  245. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  246. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  247. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  248. package/templates/agent-workroom/src/policies.ts +6 -0
  249. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  250. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  251. package/templates/agent-workroom/tsconfig.json +16 -0
  252. package/templates/agent-workroom/web/index.html +12 -0
  253. package/templates/agent-workroom/web/package.json +21 -0
  254. package/templates/agent-workroom/web/src/App.tsx +345 -0
  255. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  256. package/templates/agent-workroom/web/src/main.tsx +13 -0
  257. package/templates/agent-workroom/web/src/styles.css +545 -0
  258. package/templates/agent-workroom/web/tsconfig.json +27 -0
  259. package/templates/b2b-support-web/package.json +1 -0
  260. package/templates/b2b-support-web/web/package.json +1 -1
  261. package/templates/minimal-web/package.json +1 -1
  262. package/templates/minimal-web/web/package.json +2 -2
  263. package/templates/nuxt-web/.vscode/settings.json +14 -0
  264. package/templates/nuxt-web/README.md +30 -0
  265. package/templates/nuxt-web/forge.config.ts +3 -0
  266. package/templates/nuxt-web/package.json +33 -0
  267. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  268. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  269. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  270. package/templates/nuxt-web/src/policies.ts +6 -0
  271. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  272. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  273. package/templates/nuxt-web/tsconfig.json +17 -0
  274. package/templates/nuxt-web/web/app.vue +67 -0
  275. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  276. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  277. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  278. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  279. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  280. package/templates/nuxt-web/web/package.json +18 -0
  281. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  282. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  283. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  284. package/templates/nuxt-web/web/tsconfig.json +6 -0
  285. package/src/forge/_generated/actionSubscriptions.json +0 -2
  286. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  287. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  288. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  289. package/src/forge/_generated/agentContract.json +0 -2
  290. package/src/forge/_generated/agentContract.ts +0 -7829
  291. package/src/forge/_generated/agentQuickstart.md +0 -34
  292. package/src/forge/_generated/agentTools.json +0 -2
  293. package/src/forge/_generated/agentTools.md +0 -16
  294. package/src/forge/_generated/agentTools.ts +0 -12
  295. package/src/forge/_generated/aiContext.ts +0 -125
  296. package/src/forge/_generated/aiModels.json +0 -2
  297. package/src/forge/_generated/aiModels.ts +0 -51
  298. package/src/forge/_generated/aiProviders.json +0 -2
  299. package/src/forge/_generated/aiProviders.ts +0 -23
  300. package/src/forge/_generated/aiRegistry.json +0 -2
  301. package/src/forge/_generated/aiRegistry.ts +0 -31
  302. package/src/forge/_generated/api.json +0 -2
  303. package/src/forge/_generated/api.ts +0 -8
  304. package/src/forge/_generated/appGraph.json +0 -2
  305. package/src/forge/_generated/appGraph.ts +0 -14919
  306. package/src/forge/_generated/appMap.md +0 -55
  307. package/src/forge/_generated/artifactManifest.json +0 -2
  308. package/src/forge/_generated/artifactManifest.ts +0 -7
  309. package/src/forge/_generated/authClaims.json +0 -2
  310. package/src/forge/_generated/authClaims.ts +0 -13
  311. package/src/forge/_generated/authConfig.json +0 -2
  312. package/src/forge/_generated/authConfig.ts +0 -17
  313. package/src/forge/_generated/authContext.ts +0 -23
  314. package/src/forge/_generated/authRegistry.json +0 -2
  315. package/src/forge/_generated/authRegistry.ts +0 -25
  316. package/src/forge/_generated/buildInfo.json +0 -2
  317. package/src/forge/_generated/buildInfo.ts +0 -9
  318. package/src/forge/_generated/capabilityMap.json +0 -2
  319. package/src/forge/_generated/capabilityMap.md +0 -15
  320. package/src/forge/_generated/capabilityMap.ts +0 -17
  321. package/src/forge/_generated/client.ts +0 -282
  322. package/src/forge/_generated/clientApi.ts +0 -9
  323. package/src/forge/_generated/clientManifest.json +0 -2
  324. package/src/forge/_generated/clientManifest.ts +0 -39
  325. package/src/forge/_generated/clientTypes.ts +0 -78
  326. package/src/forge/_generated/configRegistry.json +0 -2
  327. package/src/forge/_generated/configRegistry.ts +0 -4
  328. package/src/forge/_generated/dataGraph.json +0 -2
  329. package/src/forge/_generated/dataGraph.ts +0 -8
  330. package/src/forge/_generated/db.json +0 -2
  331. package/src/forge/_generated/db.ts +0 -2
  332. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  333. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  334. package/src/forge/_generated/dbSessionContext.json +0 -2
  335. package/src/forge/_generated/dbSessionContext.ts +0 -39
  336. package/src/forge/_generated/deployManifest.json +0 -2
  337. package/src/forge/_generated/deployManifest.ts +0 -14
  338. package/src/forge/_generated/devManifest.json +0 -2
  339. package/src/forge/_generated/devManifest.ts +0 -62
  340. package/src/forge/_generated/envSchema.json +0 -2
  341. package/src/forge/_generated/envSchema.ts +0 -59
  342. package/src/forge/_generated/frontendGraph.json +0 -2
  343. package/src/forge/_generated/frontendGraph.ts +0 -27
  344. package/src/forge/_generated/importGuards.json +0 -2
  345. package/src/forge/_generated/importGuards.ts +0 -686
  346. package/src/forge/_generated/index.ts +0 -68
  347. package/src/forge/_generated/liveProductionManifest.json +0 -2
  348. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  349. package/src/forge/_generated/liveProtocol.json +0 -2
  350. package/src/forge/_generated/liveProtocol.ts +0 -21
  351. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  352. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  353. package/src/forge/_generated/liveTransportConfig.json +0 -2
  354. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  355. package/src/forge/_generated/makeRegistry.json +0 -2
  356. package/src/forge/_generated/makeRegistry.ts +0 -177
  357. package/src/forge/_generated/makeTemplates.json +0 -2
  358. package/src/forge/_generated/makeTemplates.ts +0 -66
  359. package/src/forge/_generated/mockMap.json +0 -2
  360. package/src/forge/_generated/mockMap.ts +0 -7
  361. package/src/forge/_generated/operationPlaybooks.md +0 -167
  362. package/src/forge/_generated/packageGraph.json +0 -2
  363. package/src/forge/_generated/packageGraph.ts +0 -249334
  364. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  365. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  366. package/src/forge/_generated/permissionMatrix.json +0 -2
  367. package/src/forge/_generated/permissionMatrix.ts +0 -7
  368. package/src/forge/_generated/policyRegistry.json +0 -2
  369. package/src/forge/_generated/policyRegistry.ts +0 -11
  370. package/src/forge/_generated/queryRegistry.json +0 -2
  371. package/src/forge/_generated/queryRegistry.ts +0 -9
  372. package/src/forge/_generated/react.d.ts +0 -22
  373. package/src/forge/_generated/react.ts +0 -29
  374. package/src/forge/_generated/reactManifest.json +0 -2
  375. package/src/forge/_generated/reactManifest.ts +0 -19
  376. package/src/forge/_generated/rlsPolicies.json +0 -2
  377. package/src/forge/_generated/rlsPolicies.sql +0 -34
  378. package/src/forge/_generated/rlsPolicies.ts +0 -6
  379. package/src/forge/_generated/runtimeGraph.json +0 -2
  380. package/src/forge/_generated/runtimeGraph.ts +0 -8
  381. package/src/forge/_generated/runtimeMatrix.json +0 -2
  382. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  383. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  384. package/src/forge/_generated/runtimeRules.md +0 -91
  385. package/src/forge/_generated/secretRegistry.json +0 -2
  386. package/src/forge/_generated/secretRegistry.ts +0 -50
  387. package/src/forge/_generated/secretsContext.ts +0 -11
  388. package/src/forge/_generated/serverApi.ts +0 -10
  389. package/src/forge/_generated/sourceMapManifest.json +0 -2
  390. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  391. package/src/forge/_generated/sqlPlan.json +0 -2
  392. package/src/forge/_generated/sqlPlan.ts +0 -88
  393. package/src/forge/_generated/subscriptionManifest.json +0 -2
  394. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  395. package/src/forge/_generated/symbolicationManifest.json +0 -2
  396. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  397. package/src/forge/_generated/telemetryRegistry.json +0 -2
  398. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  399. package/src/forge/_generated/telemetrySinks.json +0 -2
  400. package/src/forge/_generated/telemetrySinks.ts +0 -11
  401. package/src/forge/_generated/tenantScope.json +0 -2
  402. package/src/forge/_generated/tenantScope.ts +0 -8
  403. package/src/forge/_generated/testGraph.json +0 -2
  404. package/src/forge/_generated/testGraph.ts +0 -3430
  405. package/src/forge/_generated/testPlanRegistry.json +0 -2
  406. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  407. package/src/forge/_generated/uiRoutes.json +0 -2
  408. package/src/forge/_generated/uiRoutes.ts +0 -16
  409. package/src/forge/_generated/uiScenarios.json +0 -2
  410. package/src/forge/_generated/uiScenarios.ts +0 -30
  411. package/src/forge/_generated/uiTestManifest.json +0 -2
  412. package/src/forge/_generated/uiTestManifest.ts +0 -27
  413. package/src/forge/_generated/workflowRegistry.json +0 -2
  414. package/src/forge/_generated/workflowRegistry.ts +0 -9
  415. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  416. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -0,0 +1,51 @@
1
+ export interface CommandHeartbeat {
2
+ setPhase: (phase: string) => void;
3
+ stop: () => void;
4
+ }
5
+
6
+ export function startCommandHeartbeat(input: {
7
+ label: string;
8
+ enabled?: boolean;
9
+ initialPhase?: string;
10
+ intervalMs?: number;
11
+ stallMs?: number;
12
+ }): CommandHeartbeat {
13
+ if (input.enabled === false || process.env.FORGE_PROGRESS === "0") {
14
+ return {
15
+ setPhase: () => {},
16
+ stop: () => {},
17
+ };
18
+ }
19
+
20
+ const startedAt = Date.now();
21
+ const intervalMs = input.intervalMs ?? 10_000;
22
+ const stallMs = input.stallMs ?? 30_000;
23
+ let phase = input.initialPhase ?? "running";
24
+ let ticks = 0;
25
+
26
+ const timer = setInterval(() => {
27
+ ticks += 1;
28
+ const elapsedMs = Date.now() - startedAt;
29
+ const elapsedSeconds = Math.round(elapsedMs / 1000);
30
+ const stalled = elapsedMs >= stallMs;
31
+ const prefix = stalled ? "still waiting" : "working";
32
+ process.stderr.write(
33
+ `[forge] ${input.label}: ${prefix}; phase=${phase}; elapsed=${elapsedSeconds}s\n`,
34
+ );
35
+ if (stalled && ticks === Math.ceil(stallMs / intervalMs)) {
36
+ process.stderr.write(
37
+ `[forge] ${input.label}: run with --json for structured status; for dev startup failures, use forge last --json.\n`,
38
+ );
39
+ }
40
+ }, intervalMs);
41
+ timer.unref?.();
42
+
43
+ return {
44
+ setPhase: (nextPhase: string) => {
45
+ phase = nextPhase;
46
+ },
47
+ stop: () => {
48
+ clearInterval(timer);
49
+ },
50
+ };
51
+ }
@@ -1,6 +1,10 @@
1
1
  import { listQueries, runQuery, type ListQueriesResult, type RunQueryResult } from "../runtime/query/run-query.ts";
2
2
  import { resolveAuthFromCli } from "../runtime/auth/resolve.ts";
3
3
  import { createDiagnostic } from "../compiler/diagnostics/create.ts";
4
+ import {
5
+ resolveExternalQualifiedName,
6
+ runExternalEntry,
7
+ } from "../runtime/external/bridge.ts";
4
8
 
5
9
  export type QuerySubcommand = "list" | "run";
6
10
 
@@ -52,6 +56,34 @@ export async function runQueryCommand(
52
56
  role: options.role,
53
57
  });
54
58
 
59
+ const external = resolveExternalQualifiedName(options.workspaceRoot, options.name, "query");
60
+ if (external) {
61
+ const run = await runExternalEntry(options.workspaceRoot, {
62
+ kind: "query",
63
+ serviceName: external.serviceName,
64
+ entryName: external.entryName,
65
+ args: options.args,
66
+ auth,
67
+ });
68
+ return {
69
+ run: {
70
+ ok: run.ok,
71
+ result: run.result,
72
+ query: {
73
+ name: `${external.serviceName}.${external.entryName}`,
74
+ qualifiedName: `${external.serviceName}.${external.entryName}`,
75
+ file: `external:${external.serviceName}`,
76
+ symbolId: `external:${external.serviceName}:query:${external.entryName}`,
77
+ moduleId: `external:${external.serviceName}`,
78
+ },
79
+ diagnostics: run.diagnostics,
80
+ exitCode: run.exitCode,
81
+ traceId: run.traceId,
82
+ },
83
+ exitCode: run.exitCode,
84
+ };
85
+ }
86
+
55
87
  const run = await runQuery(options.workspaceRoot, options.name, {
56
88
  args: options.args,
57
89
  auth,
@@ -18,12 +18,14 @@ import {
18
18
  providerConfigDiagnostics,
19
19
  } from "../runtime/release/runtime.ts";
20
20
  import { symbolicateStacktrace } from "../runtime/release/symbolicate.ts";
21
+ import { releasePrepareNextActions, releaseReadyNextActions } from "./next-actions.ts";
21
22
 
22
23
  export type ReleaseArea = "release" | "artifacts" | "sourcemaps";
23
24
  export type ReleaseAction =
24
25
  | "prepare"
25
26
  | "inspect"
26
27
  | "check"
28
+ | "doctor"
27
29
  | "finalize"
28
30
  | "collect"
29
31
  | "list"
@@ -44,12 +46,15 @@ export interface ReleaseCommandOptions {
44
46
  target?: ReleaseExportProvider;
45
47
  allowDirty: boolean;
46
48
  allowPublicSourcemaps: boolean;
49
+ allowMissingLocalRelease?: boolean;
47
50
  }
48
51
 
49
52
  export interface ReleaseCommandResult {
50
53
  ok: boolean;
51
54
  data?: unknown;
52
55
  diagnostics: Diagnostic[];
56
+ nextActions?: string[];
57
+ failureKind?: string;
53
58
  exitCode: 0 | 1;
54
59
  }
55
60
 
@@ -125,19 +130,34 @@ export async function runReleaseCommand(
125
130
  const dir = latestReleaseDir(options.workspaceRoot);
126
131
  const diagnostics: Diagnostic[] = [];
127
132
  if (!dir || !nodeFileSystem.exists(join(dir, "release.json"))) {
128
- diagnostics.push(
129
- createDiagnostic({
130
- severity: "error",
133
+ if (options.allowMissingLocalRelease) {
134
+ diagnostics.push(createDiagnostic({
135
+ severity: "warning",
131
136
  code: FORGE_RELEASE_ID_MISSING,
132
- message: "no prepared local release found",
133
- }),
134
- );
137
+ message: "no prepared local release found; treating this as not-prepared because --allow-missing-local-release was provided",
138
+ fixHint: "Run forge release prepare when you want to validate a concrete release bundle.",
139
+ suggestedCommands: releasePrepareNextActions(),
140
+ }));
141
+ } else {
142
+ diagnostics.push(
143
+ createDiagnostic({
144
+ severity: "error",
145
+ code: FORGE_RELEASE_ID_MISSING,
146
+ message: "no prepared local release found; run forge release prepare before release check/finalize",
147
+ fixHint: "Prepare a local release first, then re-run the release gate.",
148
+ suggestedCommands: releasePrepareNextActions(),
149
+ }),
150
+ );
151
+ }
135
152
  }
153
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
136
154
  return {
137
- ok: diagnostics.length === 0,
138
- data: { releaseDir: dir },
155
+ ok: errors.length === 0,
156
+ data: { releaseDir: dir, state: diagnostics.length === 0 ? "prepared" : "missing-prepared-release" },
139
157
  diagnostics,
140
- exitCode: diagnostics.length === 0 ? 0 : 1,
158
+ nextActions: diagnostics.length === 0 ? releaseReadyNextActions() : releasePrepareNextActions(),
159
+ failureKind: errors.length === 0 ? undefined : "missing-prepared-release",
160
+ exitCode: errors.length === 0 ? 0 : 1,
141
161
  };
142
162
  }
143
163
 
@@ -256,7 +276,7 @@ export async function runReleaseCommand(
256
276
  };
257
277
  }
258
278
 
259
- return { ok: false, diagnostics: [], exitCode: 1 };
279
+ return { ok: false, diagnostics: [], failureKind: "unsupported-release-command", exitCode: 1 };
260
280
  }
261
281
 
262
282
  function missingRelease(): ReleaseCommandResult {
@@ -266,9 +286,13 @@ function missingRelease(): ReleaseCommandResult {
266
286
  createDiagnostic({
267
287
  severity: "error",
268
288
  code: FORGE_RELEASE_ID_MISSING,
269
- message: "release not found",
289
+ message: "release not found; pass --release <id> or run forge release prepare first",
290
+ fixHint: "Inspect the prepared releases directory or create a new local release.",
291
+ suggestedCommands: ["forge release prepare --env production", "forge release inspect --json"],
270
292
  }),
271
293
  ],
294
+ nextActions: ["forge release prepare --env production", "forge release inspect --json"],
295
+ failureKind: "missing-prepared-release",
272
296
  exitCode: 1,
273
297
  };
274
298
  }
@@ -19,7 +19,7 @@ import type { DbAdapter, DbAdapterKind, DbTransaction } from "../runtime/db/adap
19
19
  import { createDbAdapter } from "../runtime/db/factory.ts";
20
20
  import { databaseUrlUsesPostgresSuperuser } from "../runtime/db/session-context.ts";
21
21
  import type { RlsTableSecurity } from "../compiler/data-graph/rls/types.ts";
22
- import { createHash } from "node:crypto";
22
+ import { createHash, randomUUID } from "node:crypto";
23
23
 
24
24
  export type RlsSubcommand = "generate" | "check" | "apply" | "test" | "mutate-test";
25
25
 
@@ -201,34 +201,61 @@ async function setTenant(tx: DbTransaction, tenantId: string): Promise<void> {
201
201
  async function setProbeRole(adapter: DbAdapter, tableNames: string[]): Promise<string | null> {
202
202
  const roleName = "forge_rls_probe";
203
203
  try {
204
+ const schema = await adapter.query("SELECT current_schema() AS schema");
205
+ const currentSchema = String(schema.rows[0]?.schema ?? "public");
204
206
  await adapter.query(
205
207
  `DO $$ BEGIN CREATE ROLE ${quoteIdent(roleName)} NOLOGIN NOBYPASSRLS; EXCEPTION WHEN duplicate_object THEN NULL; END $$`,
206
208
  );
207
209
  await adapter.query(`GRANT USAGE ON SCHEMA public TO ${quoteIdent(roleName)}`);
208
210
  await adapter.query(`GRANT USAGE ON SCHEMA forge TO ${quoteIdent(roleName)}`);
211
+ if (currentSchema !== "public" && currentSchema !== "forge") {
212
+ await adapter.query(`GRANT USAGE ON SCHEMA ${quoteIdent(currentSchema)} TO ${quoteIdent(roleName)}`);
213
+ }
209
214
  for (const tableName of tableNames) {
210
215
  await adapter.query(
211
216
  `GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE ${quoteIdent(tableName)} TO ${quoteIdent(roleName)}`,
212
217
  );
213
218
  }
214
219
  await adapter.query(`GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${quoteIdent(roleName)}`);
220
+ if (currentSchema !== "public" && currentSchema !== "forge") {
221
+ await adapter.query(
222
+ `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ${quoteIdent(currentSchema)} TO ${quoteIdent(roleName)}`,
223
+ );
224
+ }
215
225
  return roleName;
216
226
  } catch {
217
227
  return null;
218
228
  }
219
229
  }
220
230
 
231
+ async function isolateRlsProbeSchema(adapter: DbAdapter): Promise<string | null> {
232
+ const schemaName = `forge_rls_probe_${randomUUID().replaceAll("-", "").slice(0, 16)}`;
233
+ try {
234
+ await adapter.query(`CREATE SCHEMA ${quoteIdent(schemaName)}`);
235
+ await adapter.query(`SET search_path TO ${quoteIdent(schemaName)}, forge, public`);
236
+ return schemaName;
237
+ } catch {
238
+ return null;
239
+ }
240
+ }
241
+
221
242
  async function seedReferencedRows(
222
243
  tx: DbTransaction,
223
244
  plan: SqlPlan,
224
245
  columns: ColumnDef[],
225
246
  row: Record<string, unknown>,
226
247
  seed: string,
248
+ seen: Set<string>,
227
249
  ): Promise<void> {
228
250
  for (const column of columns) {
229
251
  if (!column.references) {
230
252
  continue;
231
253
  }
254
+ const key = `${column.references.table}.${column.references.column}:${String(row[column.name])}`;
255
+ if (seen.has(key)) {
256
+ continue;
257
+ }
258
+ seen.add(key);
232
259
  const referenced = tablePlan(plan, column.references.table);
233
260
  if (!referenced?.columns) {
234
261
  continue;
@@ -297,9 +324,10 @@ async function runTableProbe(
297
324
 
298
325
  const tx = await adapter.begin();
299
326
  try {
300
- await seedReferencedRows(tx, plan, columns, rowA, `${table.table}:a`);
301
- await seedReferencedRows(tx, plan, columns, rowB, `${table.table}:b`);
302
- await seedReferencedRows(tx, plan, columns, mismatchRow, `${table.table}:mismatch`);
327
+ const seededReferences = new Set<string>();
328
+ await seedReferencedRows(tx, plan, columns, rowA, `${table.table}:a`, seededReferences);
329
+ await seedReferencedRows(tx, plan, columns, rowB, `${table.table}:b`, seededReferences);
330
+ await seedReferencedRows(tx, plan, columns, mismatchRow, `${table.table}:mismatch`, seededReferences);
303
331
  if (role) {
304
332
  await tx.query(`SET ROLE ${quoteIdent(role)}`);
305
333
  }
@@ -419,7 +447,13 @@ async function runRlsIsolationTests(options: RlsCommandOptions): Promise<RlsComm
419
447
  };
420
448
  }
421
449
 
450
+ let probeSchema: string | null = null;
451
+
422
452
  try {
453
+ if (options.db === "postgres") {
454
+ probeSchema = await isolateRlsProbeSchema(adapter);
455
+ }
456
+
423
457
  const migrationDiagnostics = await applyMigrations(adapter, plan);
424
458
  const migrationErrors = migrationDiagnostics.filter((diagnostic) => diagnostic.severity === "error");
425
459
  if (migrationErrors.length > 0) {
@@ -490,6 +524,11 @@ async function runRlsIsolationTests(options: RlsCommandOptions): Promise<RlsComm
490
524
  exitCode: 1,
491
525
  };
492
526
  } finally {
527
+ if (probeSchema) {
528
+ await adapter.query("RESET ROLE").catch(() => undefined);
529
+ await adapter.query("RESET search_path").catch(() => undefined);
530
+ await adapter.query(`DROP SCHEMA IF EXISTS ${quoteIdent(probeSchema)} CASCADE`).catch(() => undefined);
531
+ }
493
532
  await adapter.close();
494
533
  }
495
534
  }
@@ -1,6 +1,11 @@
1
1
  import { listEntries, runEntry } from "../runtime/executor.ts";
2
2
  import { createDiagnostic } from "../compiler/diagnostics/create.ts";
3
3
  import type { RunEntryResult, ListEntriesResult } from "../runtime/executor.ts";
4
+ import {
5
+ resolveExternalQualifiedName,
6
+ runExternalEntry,
7
+ } from "../runtime/external/bridge.ts";
8
+ import { resolveAuthFromCli } from "../runtime/auth/resolve.ts";
4
9
 
5
10
  export interface RunCommandOptions {
6
11
  name?: string;
@@ -10,6 +15,7 @@ export interface RunCommandOptions {
10
15
  userId?: string;
11
16
  tenantId?: string;
12
17
  role?: string;
18
+ args?: unknown;
13
19
  workspaceRoot: string;
14
20
  }
15
21
 
@@ -29,6 +35,41 @@ export async function runRunCommand(
29
35
  return { list, exitCode: list.exitCode };
30
36
  }
31
37
 
38
+ const external = resolveExternalQualifiedName(options.workspaceRoot, options.name!, "command");
39
+ if (external) {
40
+ const run = await runExternalEntry(options.workspaceRoot, {
41
+ kind: "command",
42
+ serviceName: external.serviceName,
43
+ entryName: external.entryName,
44
+ args: options.args,
45
+ auth: resolveAuthFromCli({
46
+ userId: options.userId,
47
+ tenantId: options.tenantId,
48
+ role: options.role,
49
+ }),
50
+ });
51
+ return {
52
+ run: {
53
+ ok: run.ok,
54
+ result: run.result,
55
+ diagnostics: run.diagnostics,
56
+ exitCode: run.exitCode,
57
+ traceId: run.traceId,
58
+ entry: {
59
+ id: `external:${external.serviceName}:command:${external.entryName}`,
60
+ kind: "command",
61
+ name: `${external.serviceName}.${external.entryName}`,
62
+ qualifiedName: `${external.serviceName}.${external.entryName}`,
63
+ file: `external:${external.serviceName}`,
64
+ moduleId: `external:${external.serviceName}`,
65
+ runtimeContext: "command",
66
+ dependencies: [],
67
+ },
68
+ },
69
+ exitCode: run.exitCode,
70
+ };
71
+ }
72
+
32
73
  const run = await runEntry(options.workspaceRoot, options.name!, {
33
74
  json: options.json,
34
75
  mock: options.mock,
@@ -9,6 +9,9 @@ import type { AiCommandResult } from "./ai.ts";
9
9
  import { runAiCommand } from "./ai.ts";
10
10
  import { runCheckCommand } from "./commands.ts";
11
11
  import type { GenerateResult } from "../compiler/types/cli.ts";
12
+ import { spawnSync } from "node:child_process";
13
+ import { existsSync } from "node:fs";
14
+ import { join } from "node:path";
12
15
 
13
16
  export type SecuritySubcommand = "prove";
14
17
 
@@ -27,6 +30,17 @@ export interface SecurityCommandOptions {
27
30
  json: boolean;
28
31
  db: DbAdapterKind;
29
32
  databaseUrl?: string;
33
+ runTests: boolean;
34
+ }
35
+
36
+ export interface SecurityTestRunResult {
37
+ enabled: boolean;
38
+ ok: boolean;
39
+ command: string[];
40
+ tests: string[];
41
+ exitCode: number | null;
42
+ stdout: string;
43
+ stderr: string;
30
44
  }
31
45
 
32
46
  export interface SecurityProofResult {
@@ -41,6 +55,7 @@ export interface SecurityProofResult {
41
55
  rls: RlsCommandResult;
42
56
  rlsMutation: RlsCommandResult;
43
57
  agentRedteam: AiCommandResult;
58
+ securityTests: SecurityTestRunResult;
44
59
  };
45
60
  evidence: {
46
61
  invariants: SecurityInvariantEvidence[];
@@ -155,6 +170,88 @@ function passed(name: string, ok: boolean, summary: SecurityProofResult["summary
155
170
  }
156
171
  }
157
172
 
173
+ function securityTestFiles(options: SecurityCommandOptions): string[] {
174
+ const tests = new Set<string>();
175
+ for (const invariant of invariantEvidence()) {
176
+ for (const test of invariant.tests) {
177
+ if (!test.startsWith("tests/security/")) {
178
+ continue;
179
+ }
180
+ if (options.db !== "postgres" && test.includes("rls-postgres-adversarial.test.ts")) {
181
+ continue;
182
+ }
183
+ tests.add(test);
184
+ }
185
+ }
186
+ return [...tests].sort();
187
+ }
188
+
189
+ function runSecurityTests(options: SecurityCommandOptions): SecurityTestRunResult {
190
+ const tests = securityTestFiles(options);
191
+ const command = [
192
+ "./bin/forge-bun.mjs",
193
+ "test",
194
+ ...tests,
195
+ "--timeout",
196
+ "120000",
197
+ ];
198
+
199
+ if (!options.runTests) {
200
+ return {
201
+ enabled: false,
202
+ ok: true,
203
+ command: ["node", ...command],
204
+ tests,
205
+ exitCode: null,
206
+ stdout: "",
207
+ stderr: "",
208
+ };
209
+ }
210
+
211
+ const missingTests = tests.filter((test) => !existsSync(join(options.workspaceRoot, test)));
212
+ const runnerPath = join(options.workspaceRoot, "bin", "forge-bun.mjs");
213
+ if (missingTests.length > 0 || !existsSync(runnerPath)) {
214
+ return {
215
+ enabled: false,
216
+ ok: true,
217
+ command: ["node", ...command],
218
+ tests,
219
+ exitCode: null,
220
+ stdout: "",
221
+ stderr:
222
+ "security invariant test fixtures are not available in this workspace; structural proofs still ran. Run this command from the ForgeOS source checkout to execute the full framework test fixtures.",
223
+ };
224
+ }
225
+
226
+ const result = spawnSync(process.execPath, command, {
227
+ cwd: options.workspaceRoot,
228
+ encoding: "utf8",
229
+ env: {
230
+ ...process.env,
231
+ ...(options.databaseUrl ? { DATABASE_URL: options.databaseUrl } : {}),
232
+ },
233
+ windowsHide: true,
234
+ });
235
+
236
+ return {
237
+ enabled: true,
238
+ ok: result.status === 0,
239
+ command: ["node", ...command],
240
+ tests,
241
+ exitCode: result.status,
242
+ stdout: limitOutput(result.stdout ?? ""),
243
+ stderr: limitOutput(result.stderr ?? ""),
244
+ };
245
+ }
246
+
247
+ function limitOutput(output: string): string {
248
+ const maxLength = 20_000;
249
+ if (output.length <= maxLength) {
250
+ return output;
251
+ }
252
+ return `${output.slice(0, 4_000)}\n\n[forge output truncated]\n\n${output.slice(-16_000)}`;
253
+ }
254
+
158
255
  export async function runSecurityCommand(
159
256
  options: SecurityCommandOptions,
160
257
  ): Promise<SecurityProofResult> {
@@ -189,6 +286,7 @@ export async function runSecurityCommand(
189
286
  workspaceRoot: options.workspaceRoot,
190
287
  json: true,
191
288
  });
289
+ const securityTests = runSecurityTests(options);
192
290
 
193
291
  const summary: SecurityProofResult["summary"] = {
194
292
  passed: [],
@@ -201,6 +299,9 @@ export async function runSecurityCommand(
201
299
  passed("rls-proof", rls.exitCode === 0, summary);
202
300
  passed("rls-mutation-proof", rlsMutation.exitCode === 0, summary);
203
301
  passed("agent-redteam", agentRedteam.exitCode === 0, summary);
302
+ if (securityTests.enabled) {
303
+ passed("security-tests", securityTests.ok, summary);
304
+ }
204
305
 
205
306
  if (auth.mode === "dev-headers") {
206
307
  summary.warnings.push("auth-proof uses local-only dev-headers mode");
@@ -220,6 +321,15 @@ export async function runSecurityCommand(
220
321
  summary.warnings.push(`${diagnostic.code}: ${diagnostic.message}`);
221
322
  }
222
323
  }
324
+ if (!securityTests.enabled && !options.runTests) {
325
+ summary.warnings.push("security-tests not executed; pass --full or --run-tests to run invariant security tests");
326
+ }
327
+ if (!securityTests.enabled && options.runTests) {
328
+ summary.warnings.push(securityTests.stderr);
329
+ }
330
+ if (options.runTests && options.db !== "postgres") {
331
+ summary.warnings.push("postgres RLS adversarial test skipped because --db postgres was not selected");
332
+ }
223
333
 
224
334
  const ok = summary.failed.length === 0;
225
335
  const assurance =
@@ -240,6 +350,7 @@ export async function runSecurityCommand(
240
350
  rls,
241
351
  rlsMutation,
242
352
  agentRedteam,
353
+ securityTests,
243
354
  },
244
355
  evidence: {
245
356
  invariants: invariantEvidence(),
@@ -261,6 +372,7 @@ export function formatSecurityHuman(result: SecurityProofResult): string {
261
372
  `Assurance: ${result.assurance}`,
262
373
  `Passed: ${result.summary.passed.join(", ") || "none"}`,
263
374
  `Failed: ${result.summary.failed.join(", ") || "none"}`,
375
+ `Security tests: ${result.proofs.securityTests.enabled ? (result.proofs.securityTests.ok ? "passed" : "failed") : "not run"}`,
264
376
  ];
265
377
  if (result.summary.warnings.length > 0) {
266
378
  lines.push("", "Warnings:", ...result.summary.warnings.map((warning) => `- ${warning}`));
@@ -4,6 +4,7 @@ import { GENERATED_DIR } from "../compiler/emitter/constants.ts";
4
4
  import { stripDeterministicHeader } from "../compiler/primitives/header.ts";
5
5
  import { runGenerateCommand } from "./commands.ts";
6
6
  import { runVerifyCommand } from "./verify.ts";
7
+ import { selfHostPrepareNextActions, selfHostReadyNextActions } from "./next-actions.ts";
7
8
 
8
9
  export type SelfHostSubcommand = "compose" | "env" | "check" | "clean";
9
10
 
@@ -15,6 +16,7 @@ export interface SelfHostCommandOptions {
15
16
  postgresVersion: string;
16
17
  runtimePort: number;
17
18
  webPort: number;
19
+ preparedOnly?: boolean;
18
20
  }
19
21
 
20
22
  export interface SelfHostCheck {
@@ -26,8 +28,10 @@ export interface SelfHostCheck {
26
28
  export interface SelfHostCommandResult {
27
29
  ok: boolean;
28
30
  exitCode: 0 | 1;
31
+ state?: "ready" | "not-prepared" | "failed";
29
32
  files?: string[];
30
33
  checks?: SelfHostCheck[];
34
+ nextActions?: string[];
31
35
  }
32
36
 
33
37
  function deployDir(workspaceRoot: string): string {
@@ -392,6 +396,28 @@ export async function runSelfHostCommand(
392
396
  }
393
397
 
394
398
  const checks: SelfHostCheck[] = [];
399
+ const requiredDeployFiles = [
400
+ "docker-compose.yml",
401
+ "Dockerfile.runtime",
402
+ "Dockerfile.web",
403
+ ".dockerignore",
404
+ ".env.example",
405
+ "README.md",
406
+ ];
407
+ const missingDeployFiles = requiredDeployFiles.filter((file) => !nodeFileSystem.exists(join(dir, file)));
408
+ if (options.preparedOnly && missingDeployFiles.length > 0) {
409
+ return {
410
+ ok: true,
411
+ state: "not-prepared",
412
+ exitCode: 0,
413
+ checks: missingDeployFiles.map((file) => ({
414
+ name: `deploy/${file}`,
415
+ ok: true,
416
+ details: { state: "not-prepared", missing: true, command: "forge self-host compose" },
417
+ })),
418
+ nextActions: selfHostPrepareNextActions(),
419
+ };
420
+ }
395
421
  const generated = await runGenerateCommand({
396
422
  workspaceRoot: options.workspaceRoot,
397
423
  check: true,
@@ -399,7 +425,11 @@ export async function runSelfHostCommand(
399
425
  json: false,
400
426
  concurrency: 4,
401
427
  });
402
- checks.push({ name: "generated", ok: generated.exitCode === 0 });
428
+ checks.push({
429
+ name: "generated",
430
+ ok: generated.exitCode === 0,
431
+ details: generated.exitCode === 0 ? undefined : { command: "forge generate" },
432
+ });
403
433
 
404
434
  const verify = await runVerifyCommand({
405
435
  workspaceRoot: options.workspaceRoot,
@@ -409,17 +439,19 @@ export async function runSelfHostCommand(
409
439
  skipEslint: true,
410
440
  strict: true,
411
441
  });
412
- checks.push({ name: "verify-strict", ok: verify.exitCode === 0 });
442
+ checks.push({
443
+ name: "verify-strict",
444
+ ok: verify.exitCode === 0,
445
+ details: verify.exitCode === 0 ? undefined : { command: "forge verify --strict" },
446
+ });
413
447
 
414
- for (const file of [
415
- "docker-compose.yml",
416
- "Dockerfile.runtime",
417
- "Dockerfile.web",
418
- ".dockerignore",
419
- ".env.example",
420
- "README.md",
421
- ]) {
422
- checks.push({ name: `deploy/${file}`, ok: nodeFileSystem.exists(join(dir, file)) });
448
+ for (const file of requiredDeployFiles) {
449
+ const exists = nodeFileSystem.exists(join(dir, file));
450
+ checks.push({
451
+ name: `deploy/${file}`,
452
+ ok: exists,
453
+ details: exists ? undefined : { missing: true, command: "forge self-host compose" },
454
+ });
423
455
  }
424
456
 
425
457
  const envExample = nodeFileSystem.exists(join(dir, ".env.example"))
@@ -452,7 +484,13 @@ export async function runSelfHostCommand(
452
484
  });
453
485
 
454
486
  const ok = checks.every((check) => check.ok);
455
- return { ok, exitCode: ok ? 0 : 1, checks };
487
+ return {
488
+ ok,
489
+ state: ok ? "ready" : "failed",
490
+ exitCode: ok ? 0 : 1,
491
+ checks,
492
+ nextActions: ok ? selfHostReadyNextActions() : selfHostPrepareNextActions(),
493
+ };
456
494
  }
457
495
 
458
496
  export function formatSelfHostHuman(result: SelfHostCommandResult): string {
@@ -460,9 +498,13 @@ export function formatSelfHostHuman(result: SelfHostCommandResult): string {
460
498
  return `wrote self-host files:\n${result.files.map((file) => ` ${file}`).join("\n")}\n`;
461
499
  }
462
500
  if (result.checks) {
463
- return `${result.checks
501
+ const lines = result.checks
464
502
  .map((check) => `${check.ok ? "ok" : "fail"} ${check.name}`)
465
- .join("\n")}\n`;
503
+ .join("\n");
504
+ const next = result.nextActions && result.nextActions.length > 0
505
+ ? `\nNext:\n${result.nextActions.map((action) => ` ${action}`).join("\n")}\n`
506
+ : "\n";
507
+ return `${lines}${next}`;
466
508
  }
467
509
  return result.ok ? "self-host clean complete\n" : "self-host command failed\n";
468
510
  }