@aotter/mantle 0.1.5-alpha.1 → 0.2.0-alpha.10

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 (495) hide show
  1. package/README.md +166 -298
  2. package/dist/auth/adapter.d.ts +32 -0
  3. package/dist/auth/adapter.js +288 -0
  4. package/dist/auth/admin.d.ts +3 -0
  5. package/dist/auth/admin.js +21 -0
  6. package/dist/auth/caller.d.ts +86 -0
  7. package/dist/auth/caller.js +155 -0
  8. package/dist/auth/hardening.d.ts +24 -0
  9. package/dist/auth/hardening.js +40 -0
  10. package/dist/auth/identity.d.ts +121 -0
  11. package/dist/auth/identity.js +203 -0
  12. package/dist/auth/oauth.d.ts +63 -0
  13. package/dist/auth/oauth.js +217 -0
  14. package/dist/auth/resource-seeds.js +10 -0
  15. package/dist/auth/routes.d.ts +32 -0
  16. package/dist/auth/routes.js +226 -0
  17. package/dist/build/compiler.d.ts +721 -0
  18. package/dist/build/compiler.js +7029 -0
  19. package/dist/build/spec/domain/model/ManifestGrammar.d.ts +439 -0
  20. package/dist/build/spec/domain/model/RuntimePlan.d.ts +168 -0
  21. package/dist/build/spec/domain/model/SqlIr.d.ts +129 -0
  22. package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
  23. package/dist/build/spec/domain/service/PlanFingerprint.d.ts +7 -0
  24. package/dist/build/spec/kernel/diagnostic.d.ts +108 -0
  25. package/dist/bun/assets.d.ts +2 -0
  26. package/dist/bun/assets.js +24 -0
  27. package/dist/domain/adminOrder.js +24 -0
  28. package/dist/domain/columns.js +57 -0
  29. package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
  30. package/dist/domain/describe/index.d.ts +58 -0
  31. package/dist/domain/describe/index.js +115 -0
  32. package/dist/domain/describe/target-hash.d.ts +8 -0
  33. package/dist/domain/describe/target-hash.js +12 -0
  34. package/dist/domain/describe/targets.d.ts +40 -0
  35. package/dist/domain/entry.js +33 -0
  36. package/dist/domain/generated-tools.js +19 -0
  37. package/dist/domain/generation.d.ts +16 -0
  38. package/dist/domain/generation.js +1 -0
  39. package/dist/domain/input.js +17 -0
  40. package/dist/domain/model.js +1 -0
  41. package/dist/domain/paging.js +230 -0
  42. package/dist/domain/recipes.js +24 -0
  43. package/dist/domain/userReferences.js +5 -0
  44. package/dist/domain/wireScalars.js +47 -0
  45. package/dist/infrastructure/adminEntries.js +153 -0
  46. package/dist/infrastructure/adminPaging.js +72 -0
  47. package/dist/infrastructure/artifacts.js +71 -0
  48. package/dist/infrastructure/authSchema.js +83 -0
  49. package/dist/infrastructure/bundle.js +188 -0
  50. package/dist/infrastructure/cli.js +118 -0
  51. package/dist/infrastructure/generate.d.ts +12 -0
  52. package/dist/infrastructure/generate.js +189 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +74 -0
  55. package/dist/infrastructure/help.js +60 -0
  56. package/dist/infrastructure/init.js +54 -0
  57. package/dist/infrastructure/inputs.js +41 -0
  58. package/dist/infrastructure/mounts.js +25 -0
  59. package/dist/infrastructure/wrangler.js +109 -0
  60. package/dist/kernel/authorization.d.ts +4 -0
  61. package/dist/kernel/authorization.js +16 -0
  62. package/dist/kernel/binds.d.ts +60 -0
  63. package/dist/kernel/binds.js +49 -0
  64. package/dist/kernel/caller-identity.d.ts +24 -0
  65. package/dist/kernel/caller-identity.js +22 -0
  66. package/dist/kernel/contract-error.d.ts +12 -0
  67. package/dist/kernel/contract-error.js +14 -0
  68. package/dist/kernel/contracts.d.ts +143 -0
  69. package/dist/kernel/contracts.js +97 -0
  70. package/dist/kernel/cron.d.ts +1 -0
  71. package/dist/kernel/cron.js +58 -0
  72. package/dist/kernel/dialect/common.d.ts +38 -0
  73. package/dist/kernel/dialect/common.js +6 -0
  74. package/dist/kernel/dialect/postgres.d.ts +16 -0
  75. package/dist/kernel/dialect/postgres.js +18 -0
  76. package/dist/kernel/dialect/sqlite.d.ts +20 -0
  77. package/dist/kernel/dialect/sqlite.js +34 -0
  78. package/dist/kernel/drain.d.ts +5 -0
  79. package/dist/kernel/drain.js +35 -0
  80. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  81. package/dist/kernel/driver/bun-sqlite.js +77 -0
  82. package/dist/kernel/driver/d1.d.ts +24 -0
  83. package/dist/kernel/driver/d1.js +22 -0
  84. package/dist/kernel/driver/postgres.d.ts +25 -0
  85. package/dist/kernel/driver/postgres.js +99 -0
  86. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  87. package/dist/kernel/driver/transaction-context.js +29 -0
  88. package/dist/kernel/email.d.ts +28 -0
  89. package/dist/kernel/email.js +38 -0
  90. package/dist/kernel/host-access.d.ts +10 -0
  91. package/dist/kernel/host-access.js +65 -0
  92. package/dist/kernel/host-caller.d.ts +39 -0
  93. package/dist/kernel/host-caller.js +56 -0
  94. package/dist/kernel/json-body.d.ts +4 -0
  95. package/dist/kernel/json-body.js +44 -0
  96. package/dist/kernel/mcp-apps.d.ts +69 -0
  97. package/dist/kernel/mcp-apps.js +40 -0
  98. package/dist/kernel/mcp-call.d.ts +5 -0
  99. package/dist/kernel/mcp-call.js +25 -0
  100. package/dist/kernel/mcp-handler.js +371 -0
  101. package/dist/kernel/mcp.d.ts +110 -0
  102. package/dist/kernel/mcp.js +50 -0
  103. package/dist/kernel/media.d.ts +27 -0
  104. package/dist/kernel/media.js +216 -0
  105. package/dist/kernel/paging.d.ts +55 -0
  106. package/dist/kernel/paging.js +95 -0
  107. package/dist/kernel/publishing.d.ts +4 -0
  108. package/dist/kernel/publishing.js +16 -0
  109. package/dist/kernel/r2-media.d.ts +42 -0
  110. package/dist/kernel/r2-media.js +78 -0
  111. package/dist/kernel/rest-dispatch.js +38 -0
  112. package/dist/kernel/rest-router.js +64 -0
  113. package/dist/kernel/rest.d.ts +58 -0
  114. package/dist/kernel/rest.js +41 -0
  115. package/dist/kernel/scalars.d.ts +6 -0
  116. package/dist/kernel/scalars.js +44 -0
  117. package/dist/kernel/search.js +5 -0
  118. package/dist/kernel/site-types.d.ts +146 -0
  119. package/dist/kernel/site-types.js +1 -0
  120. package/dist/kernel/site.d.ts +39 -0
  121. package/dist/kernel/site.js +32 -0
  122. package/dist/kernel/store.d.ts +175 -0
  123. package/dist/kernel/store.js +269 -0
  124. package/dist/kernel/surface.d.ts +10 -0
  125. package/dist/kernel/surface.js +19 -0
  126. package/dist/kernel/sweep.d.ts +17 -0
  127. package/dist/kernel/sweep.js +27 -0
  128. package/dist/kernel/translations.d.ts +1 -0
  129. package/dist/kernel/translations.js +13 -0
  130. package/dist/kernel/user-caller.d.ts +31 -0
  131. package/dist/kernel/user-caller.js +67 -0
  132. package/dist/kernel/user-field-name.js +3 -0
  133. package/dist/kernel/user-fields.d.ts +35 -0
  134. package/dist/kernel/user-fields.js +81 -0
  135. package/dist/kernel/validation.d.ts +15 -0
  136. package/dist/kernel/validation.js +54 -0
  137. package/dist/kernel/web-gateway.js +68 -0
  138. package/dist/kernel/web-head.js +15 -0
  139. package/dist/kernel/web-index.js +40 -0
  140. package/dist/kernel/web-response.js +8 -0
  141. package/dist/kernel/web.d.ts +77 -0
  142. package/dist/kernel/web.js +124 -0
  143. package/dist/kernel/where.js +27 -0
  144. package/dist/testing/index.d.ts +1 -0
  145. package/dist/testing/index.js +1 -0
  146. package/dist/usecase/adminBindings.js +143 -0
  147. package/dist/usecase/adminClient.js +87 -0
  148. package/dist/usecase/adminDescriptors.js +107 -0
  149. package/dist/usecase/generate.js +166 -0
  150. package/dist/usecase/handlers.js +21 -0
  151. package/dist/usecase/mcp-media.js +17 -0
  152. package/dist/usecase/mcp.js +100 -0
  153. package/dist/usecase/paging.js +71 -0
  154. package/dist/usecase/publishing.js +63 -0
  155. package/dist/usecase/refusals.js +27 -0
  156. package/dist/usecase/rest.js +54 -0
  157. package/dist/usecase/search.js +7 -0
  158. package/dist/usecase/site.js +39 -0
  159. package/dist/usecase/stores.js +64 -0
  160. package/dist/usecase/tool-version.js +9 -0
  161. package/dist/usecase/translations.js +78 -0
  162. package/dist/usecase/ttl.js +20 -0
  163. package/dist/usecase/validation.js +241 -0
  164. package/dist/usecase/web.js +23 -0
  165. package/docs/examples/README.md +39 -18
  166. package/docs/examples/app-shell/.dev.vars.example +4 -0
  167. package/docs/examples/app-shell/README.md +121 -0
  168. package/docs/examples/app-shell/accept.mjs +91 -0
  169. package/docs/examples/app-shell/gitignore +7 -0
  170. package/docs/examples/app-shell/package.json +43 -0
  171. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  172. package/docs/examples/app-shell/smoke-kit.mjs +168 -0
  173. package/docs/examples/app-shell/src/auth.ts +58 -0
  174. package/docs/examples/app-shell/src/handlers.ts +5 -0
  175. package/docs/examples/app-shell/src/index.ts +4 -0
  176. package/docs/examples/app-shell/src/routes.ts +14 -0
  177. package/docs/examples/app-shell/src/schedule.ts +8 -0
  178. package/docs/examples/app-shell/src/service.ts +70 -0
  179. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  180. package/docs/examples/app-shell/src/signup.ts +8 -0
  181. package/docs/examples/app-shell/tsconfig.json +26 -0
  182. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  183. package/docs/examples/app-shell/wrangler.jsonc +62 -0
  184. package/docs/examples/club-events/README.md +65 -0
  185. package/docs/examples/club-events/manifests/club-events.yaml +247 -0
  186. package/docs/examples/club-events/smoke.mjs +127 -0
  187. package/docs/examples/club-events/src/handlers.ts +25 -0
  188. package/docs/examples/club-events/src/signup.ts +9 -0
  189. package/docs/examples/generated-web/README.md +78 -0
  190. package/docs/examples/generated-web/posts.yaml +38 -0
  191. package/docs/examples/generated-web/seed.sql +6 -0
  192. package/docs/examples/generated-web/src/worker.ts +43 -0
  193. package/docs/examples/generated-web/tsconfig.json +8 -0
  194. package/docs/examples/generated-web/wrangler.jsonc +8 -0
  195. package/docs/examples/purchasing/.dev.vars.example +6 -0
  196. package/docs/examples/purchasing/README.md +99 -0
  197. package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
  198. package/docs/examples/purchasing/seed.sql +4 -0
  199. package/docs/examples/purchasing/smoke.mjs +191 -0
  200. package/docs/examples/purchasing/src/admin.tsx +13 -0
  201. package/docs/examples/purchasing/src/handlers.ts +33 -0
  202. package/docs/examples/purchasing/src/notify.ts +29 -0
  203. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  204. package/docs/examples/purchasing/src/routes.ts +53 -0
  205. package/docs/examples/purchasing/src/schedule.ts +31 -0
  206. package/docs/examples/purchasing/src/signup.ts +5 -0
  207. package/docs/examples/purchasing/src/supplier.ts +32 -0
  208. package/docs/examples/purchasing/wrangler.jsonc +69 -0
  209. package/docs/examples/recipes/README.md +60 -0
  210. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  211. package/docs/examples/recipes/smoke.mjs +102 -0
  212. package/docs/examples/recipes/src/media.ts +13 -0
  213. package/docs/examples/recipes/src/pages.ts +99 -0
  214. package/docs/examples/recipes/src/routes.ts +71 -0
  215. package/docs/examples/recipes/src/site-config.ts +12 -0
  216. package/docs/examples/recipes/wrangler.jsonc +74 -0
  217. package/docs/handbook/cloudflare/authentication.md +180 -322
  218. package/docs/handbook/cloudflare/chatgpt-sites.md +5 -23
  219. package/docs/handbook/cloudflare/deploy-and-operate.md +55 -77
  220. package/docs/handbook/cloudflare/media-r2.md +61 -205
  221. package/docs/handbook/cloudflare/service-entry.md +239 -0
  222. package/docs/handbook/concepts/authorization.md +10 -107
  223. package/docs/handbook/concepts/four-atoms.md +9 -168
  224. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -132
  225. package/docs/handbook/concepts/mcp-and-agents.md +24 -121
  226. package/docs/handbook/concepts/procedures-and-triggers.md +5 -217
  227. package/docs/handbook/concepts/runtime-and-adapters.md +15 -169
  228. package/docs/handbook/concepts/views.md +10 -185
  229. package/docs/handbook/examples/hub.md +3 -8
  230. package/docs/handbook/guides/adapter-guide.md +11 -0
  231. package/docs/handbook/guides/admin-ui.md +115 -133
  232. package/docs/handbook/guides/agent-setup.md +64 -74
  233. package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
  234. package/docs/handbook/guides/email.md +86 -0
  235. package/docs/handbook/guides/local-testing.md +40 -0
  236. package/docs/handbook/guides/mcp-apps.md +560 -0
  237. package/docs/handbook/guides/native-admin.md +566 -0
  238. package/docs/handbook/guides/native-execution.md +133 -0
  239. package/docs/handbook/guides/oauth.md +219 -0
  240. package/docs/handbook/guides/postgres-adapters.md +56 -0
  241. package/docs/handbook/guides/public-pages.md +87 -0
  242. package/docs/handbook/guides/size-and-startup.md +9 -0
  243. package/docs/handbook/guides/sql-patterns.md +266 -0
  244. package/docs/handbook/guides/typed-queries.md +211 -107
  245. package/docs/handbook/navigation.json +176 -55
  246. package/docs/handbook/reference/admin-descriptors.md +53 -0
  247. package/docs/handbook/reference/authorization.md +39 -266
  248. package/docs/handbook/reference/describe.md +56 -0
  249. package/docs/handbook/reference/diagnostics.md +19 -186
  250. package/docs/handbook/reference/features.md +45 -55
  251. package/docs/handbook/reference/manifest.md +6 -129
  252. package/docs/handbook/reference/procedure.md +11 -303
  253. package/docs/handbook/reference/schema.md +64 -240
  254. package/docs/handbook/reference/site-config.md +5 -218
  255. package/docs/handbook/reference/sql.md +64 -0
  256. package/docs/handbook/reference/surface.md +392 -201
  257. package/docs/handbook/reference/trigger.md +56 -192
  258. package/docs/handbook/reference/view.md +24 -243
  259. package/docs/handbook/releases/index.md +244 -77
  260. package/docs/handbook/start/agents.md +45 -0
  261. package/docs/handbook/start/overview.md +9 -48
  262. package/docs/handbook/start/project-and-cli.md +51 -104
  263. package/docs/handbook/start/quickstart-worker.md +43 -178
  264. package/docs/skills/develop/SKILL.md +79 -249
  265. package/docs/skills/integrate/SKILL.md +39 -50
  266. package/docs/skills/media-gc/SKILL.md +34 -10
  267. package/docs/skills/provision/SKILL.md +36 -186
  268. package/docs/skills/update/SKILL.md +23 -38
  269. package/docs/source/scripts/postgres-adapter.ts +101 -0
  270. package/docs/upgrade-0.1-to-0.2.md +227 -0
  271. package/package.json +120 -131
  272. package/skills/README.md +48 -108
  273. package/skills/mantle/SKILL.md +76 -0
  274. package/dist/admin-ui.d.ts +0 -2
  275. package/dist/admin-ui.d.ts.map +0 -1
  276. package/dist/admin-ui.js +0 -2
  277. package/dist/admin-ui.js.map +0 -1
  278. package/dist/admin.d.ts +0 -2
  279. package/dist/admin.d.ts.map +0 -1
  280. package/dist/admin.js +0 -2
  281. package/dist/admin.js.map +0 -1
  282. package/dist/auth.d.ts +0 -2
  283. package/dist/auth.d.ts.map +0 -1
  284. package/dist/auth.js +0 -2
  285. package/dist/auth.js.map +0 -1
  286. package/dist/bun.d.ts +0 -2
  287. package/dist/bun.d.ts.map +0 -1
  288. package/dist/bun.js +0 -2
  289. package/dist/bun.js.map +0 -1
  290. package/dist/cli/generate-cloudflare.d.ts +0 -7
  291. package/dist/cli/generate-cloudflare.d.ts.map +0 -1
  292. package/dist/cli/generate-cloudflare.js +0 -123
  293. package/dist/cli/generate-cloudflare.js.map +0 -1
  294. package/dist/cli/generate-project.d.ts +0 -25
  295. package/dist/cli/generate-project.d.ts.map +0 -1
  296. package/dist/cli/generate-project.js +0 -282
  297. package/dist/cli/generate-project.js.map +0 -1
  298. package/dist/cli/generate-sites.d.ts +0 -8
  299. package/dist/cli/generate-sites.d.ts.map +0 -1
  300. package/dist/cli/generate-sites.js +0 -273
  301. package/dist/cli/generate-sites.js.map +0 -1
  302. package/dist/cli/generate.d.ts +0 -20
  303. package/dist/cli/generate.d.ts.map +0 -1
  304. package/dist/cli/generate.js +0 -299
  305. package/dist/cli/generate.js.map +0 -1
  306. package/dist/cli/harness.d.ts +0 -3
  307. package/dist/cli/harness.d.ts.map +0 -1
  308. package/dist/cli/harness.js +0 -214
  309. package/dist/cli/harness.js.map +0 -1
  310. package/dist/cli/main.d.ts +0 -3
  311. package/dist/cli/main.d.ts.map +0 -1
  312. package/dist/cli/main.js +0 -77
  313. package/dist/cli/main.js.map +0 -1
  314. package/dist/cli/skills.d.ts +0 -3
  315. package/dist/cli/skills.d.ts.map +0 -1
  316. package/dist/cli/skills.js +0 -128
  317. package/dist/cli/skills.js.map +0 -1
  318. package/dist/cloudflare.d.ts +0 -2
  319. package/dist/cloudflare.d.ts.map +0 -1
  320. package/dist/cloudflare.js +0 -2
  321. package/dist/cloudflare.js.map +0 -1
  322. package/dist/codegen/emitMantleModule.d.ts +0 -22
  323. package/dist/codegen/emitMantleModule.d.ts.map +0 -1
  324. package/dist/codegen/emitMantleModule.js +0 -291
  325. package/dist/codegen/emitMantleModule.js.map +0 -1
  326. package/dist/codegen.d.ts +0 -2
  327. package/dist/codegen.d.ts.map +0 -1
  328. package/dist/codegen.js +0 -2
  329. package/dist/codegen.js.map +0 -1
  330. package/dist/runtime-testing.d.ts +0 -2
  331. package/dist/runtime-testing.d.ts.map +0 -1
  332. package/dist/runtime-testing.js +0 -2
  333. package/dist/runtime-testing.js.map +0 -1
  334. package/dist/runtime.d.ts +0 -2
  335. package/dist/runtime.d.ts.map +0 -1
  336. package/dist/runtime.js +0 -2
  337. package/dist/runtime.js.map +0 -1
  338. package/dist/spec.d.ts +0 -2
  339. package/dist/spec.d.ts.map +0 -1
  340. package/dist/spec.js +0 -2
  341. package/dist/spec.js.map +0 -1
  342. package/dist/vercel-libsql.d.ts +0 -2
  343. package/dist/vercel-libsql.d.ts.map +0 -1
  344. package/dist/vercel-libsql.js +0 -2
  345. package/dist/vercel-libsql.js.map +0 -1
  346. package/dist/vercel.d.ts +0 -2
  347. package/dist/vercel.d.ts.map +0 -1
  348. package/dist/vercel.js +0 -2
  349. package/dist/vercel.js.map +0 -1
  350. package/dist/web.d.ts +0 -2
  351. package/dist/web.d.ts.map +0 -1
  352. package/dist/web.js +0 -2
  353. package/dist/web.js.map +0 -1
  354. package/docs/adapter-guide.md +0 -222
  355. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  356. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  357. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  358. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  359. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  360. package/docs/adr/0010-locale-and-translates.md +0 -453
  361. package/docs/adr/0011-adapter-port-spec.md +0 -281
  362. package/docs/adr/0012-views-as-public-rest.md +0 -161
  363. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  364. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -900
  365. package/docs/adr/0016-site-semantic-layer.md +0 -70
  366. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  367. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  368. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -240
  369. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  370. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  371. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  372. package/docs/adr/0023-port-failure-contract.md +0 -69
  373. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  374. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  375. package/docs/adr/0026-progressive-generate.md +0 -36
  376. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  377. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  378. package/docs/adr/README.md +0 -81
  379. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  380. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  381. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  382. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  383. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  384. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  385. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  386. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -42
  387. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  388. package/docs/agent-prompts.md +0 -64
  389. package/docs/api-mcp-authorization.md +0 -5
  390. package/docs/assets/mantle-admin-developer.png +0 -0
  391. package/docs/assets/mantle-admin-operations.png +0 -0
  392. package/docs/assets/mantle-hero.jpg +0 -0
  393. package/docs/auth-hosting-model.md +0 -166
  394. package/docs/cloudflare-low-level-composition.md +0 -5
  395. package/docs/consumer-onboarding-audit.md +0 -65
  396. package/docs/deferred-lifecycle-queues.md +0 -5
  397. package/docs/design-atoms.md +0 -16
  398. package/docs/design-references/editorial-blog-2026-05-05.md +0 -218
  399. package/docs/direct-authoring.md +0 -5
  400. package/docs/examples/builtin-commerce.md +0 -269
  401. package/docs/examples/builtin-intake.md +0 -143
  402. package/docs/examples/builtin-legal-documents.md +0 -189
  403. package/docs/examples/builtin-procurement.md +0 -241
  404. package/docs/examples/builtin-publication.md +0 -241
  405. package/docs/examples/builtin-reservation.md +0 -149
  406. package/docs/examples/cf-primitives-commerce-inventory.md +0 -809
  407. package/docs/examples/cf-primitives-guarded-api.md +0 -429
  408. package/docs/examples/cf-primitives-intake-hooks.md +0 -319
  409. package/docs/examples/host-chatgpt-sites/.openai/hosting.json +0 -1
  410. package/docs/examples/host-chatgpt-sites/README.md +0 -70
  411. package/docs/examples/host-chatgpt-sites/drizzle/0000_sites_users.sql +0 -8
  412. package/docs/examples/host-chatgpt-sites/drizzle/0001_mantle.sql +0 -294
  413. package/docs/examples/host-chatgpt-sites/drizzle/0002_article_cover.sql +0 -5
  414. package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +0 -1
  415. package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +0 -10
  416. package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +0 -24
  417. package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +0 -1
  418. package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +0 -60
  419. package/docs/examples/host-chatgpt-sites/manifests/site.yaml +0 -27
  420. package/docs/examples/host-chatgpt-sites/package.json +0 -1
  421. package/docs/examples/host-chatgpt-sites/public/site.css +0 -1
  422. package/docs/examples/host-chatgpt-sites/scripts/build.mjs +0 -13
  423. package/docs/examples/host-chatgpt-sites/scripts/check.mjs +0 -101
  424. package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +0 -43
  425. package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +0 -66
  426. package/docs/examples/host-chatgpt-sites/src/index.ts +0 -46
  427. package/docs/examples/host-chatgpt-sites/src/mcp.ts +0 -49
  428. package/docs/examples/host-chatgpt-sites/src/media.ts +0 -109
  429. package/docs/examples/host-chatgpt-sites/src/r2-lab.ts +0 -38
  430. package/docs/examples/host-chatgpt-sites/src/storage-fingerprint.json +0 -1
  431. package/docs/examples/host-chatgpt-sites/src/web.ts +0 -35
  432. package/docs/examples/host-chatgpt-sites/tsconfig.json +0 -1
  433. package/docs/examples/host-chatgpt-sites/wrangler.jsonc +0 -10
  434. package/docs/examples/host-local-admin-otp/.dev.vars.example +0 -3
  435. package/docs/examples/host-local-admin-otp/README.md +0 -80
  436. package/docs/examples/host-local-admin-otp/ensure-dev-vars.mjs +0 -5
  437. package/docs/examples/host-local-admin-otp/manifests/site.yaml +0 -25
  438. package/docs/examples/host-local-admin-otp/package.json +0 -29
  439. package/docs/examples/host-local-admin-otp/public/.gitkeep +0 -1
  440. package/docs/examples/host-local-admin-otp/smoke.mjs +0 -141
  441. package/docs/examples/host-local-admin-otp/src/index.ts +0 -54
  442. package/docs/examples/host-local-admin-otp/tsconfig.json +0 -17
  443. package/docs/examples/host-local-admin-otp/wrangler.jsonc +0 -23
  444. package/docs/examples/host-minimal-worker/README.md +0 -41
  445. package/docs/examples/host-minimal-worker/manifests/site.yaml +0 -25
  446. package/docs/examples/host-minimal-worker/package.json +0 -26
  447. package/docs/examples/host-minimal-worker/smoke.mjs +0 -23
  448. package/docs/examples/host-minimal-worker/src/index.ts +0 -4
  449. package/docs/examples/host-minimal-worker/tsconfig.json +0 -17
  450. package/docs/examples/host-minimal-worker/wrangler.jsonc +0 -22
  451. package/docs/handbook/chatgpt-sites/equipment-checkout.md +0 -231
  452. package/docs/handbook/chatgpt-sites/generated-app.md +0 -84
  453. package/docs/handbook/chatgpt-sites/host-reference.md +0 -113
  454. package/docs/handbook/chatgpt-sites/index.md +0 -133
  455. package/docs/handbook/cloudflare/bindings.md +0 -291
  456. package/docs/handbook/cloudflare/conventional-worker.md +0 -166
  457. package/docs/handbook/cloudflare/deferred-hooks-queues.md +0 -170
  458. package/docs/handbook/cloudflare/low-level-composition.md +0 -169
  459. package/docs/handbook/cloudflare/public-web.md +0 -144
  460. package/docs/handbook/cloudflare/site-chrome.md +0 -75
  461. package/docs/handbook/examples/commerce-transaction.md +0 -11
  462. package/docs/handbook/examples/commerce.md +0 -11
  463. package/docs/handbook/examples/guarded-api.md +0 -10
  464. package/docs/handbook/examples/intake-form.md +0 -12
  465. package/docs/handbook/examples/intake-hooks.md +0 -11
  466. package/docs/handbook/examples/legal-documents.md +0 -10
  467. package/docs/handbook/examples/procurement-approvals.md +0 -10
  468. package/docs/handbook/examples/publication.md +0 -10
  469. package/docs/handbook/examples/reservation.md +0 -10
  470. package/docs/handbook/start/quickstart-admin.md +0 -52
  471. package/docs/labels.md +0 -71
  472. package/docs/media-uploads.md +0 -5
  473. package/docs/migration-0.1.2.md +0 -13
  474. package/docs/performance-harness.md +0 -228
  475. package/docs/release-process.md +0 -244
  476. package/docs/schema-indexes.md +0 -5
  477. package/docs/sealed-pipeline-ownership.md +0 -101
  478. package/docs/skills/plugin/SKILL.md +0 -132
  479. package/docs/skills/theme/SKILL.md +0 -77
  480. package/docs/spec-only-host-adoption.md +0 -157
  481. package/docs/transaction-patterns.md +0 -5
  482. package/skills/develop/SKILL.md +0 -258
  483. package/skills/install/SKILL.md +0 -112
  484. package/skills/integrate/SKILL.md +0 -60
  485. package/skills/media-gc/SKILL.md +0 -85
  486. package/skills/plugin/SKILL.md +0 -132
  487. package/skills/provision/SKILL.md +0 -194
  488. package/skills/theme/SKILL.md +0 -77
  489. package/skills/update/SKILL.md +0 -51
  490. package/templates/chatgpt-sites/auth.ts +0 -66
  491. package/templates/chatgpt-sites/build.mjs +0 -14
  492. package/templates/chatgpt-sites/env.ts +0 -1
  493. package/templates/chatgpt-sites/mcp.ts +0 -48
  494. package/templates/chatgpt-sites/public-mcp.ts +0 -29
  495. package/templates/chatgpt-sites/smoke-local.mjs +0 -20
@@ -1,194 +1,44 @@
1
1
  ---
2
2
  name: provision
3
- description: Ship a Mantle project through its selected host, routing ChatGPT Sites to its integration guide and conventional Cloudflare Workers to production auth and provisioning.
3
+ description: Ship a Mantle 0.2 project to production — Cloudflare Workers with D1, production sign-in and secrets, or ChatGPT Sites through its guide — and verify the deployed service.
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: docs/skills/provision/SKILL.md
7
- applies_to: mantle grammar v0.1
7
+ applies_to: mantle 0.2
8
8
  projection: package
9
- projectionReason: Platform-specific deploy that handles production secrets; opt-in only.
9
+ projectionReason: Creates remote resources and handles production secrets; opt-in only.
10
10
  ---
11
11
 
12
- # Provision a Mantle Project
13
-
14
- Local cold start deliberately stops before this skill. Provision only after the
15
- user asks to create remote resources or ship production. This flow is for
16
- consumer-owned Cloudflare Workers. New direct-authored apps do not need Landing
17
- artifacts (`.mantle/launch-state.json`, `.mantle/handoff.md`, or a hosted-auth
18
- allocation). Treat any Landing handoff as **legacy/optional**. For a ChatGPT
19
- Site, use the installed `docs/handbook/chatgpt-sites/index.md` integration guide and
20
- the "Publish with Sites" steps in `docs/examples/host-chatgpt-sites/README.md`:
21
- request D1 and R2 on the Site, set `PUBLIC_ORIGIN` and `OWNER_EMAIL` in Sites
22
- settings, review the migration, then save and deploy a Sites version. Do not
23
- run `wrangler deploy` or require R2 S3 credentials merely because Sites exposes
24
- an R2 binding.
25
-
26
- ## Source of Truth
27
-
28
- 1. Read the actual provider config (`wrangler.jsonc` or `wrangler.toml`),
29
- application entry and git remotes. Read legacy `.mantle/launch-state.json`
30
- and `.mantle/handoff.md` only when present; they are optional leftovers from
31
- Landing and must not be created as prerequisites for a new app.
32
- 2. Read installed `@aotter/mantle*` versions from `package.json`.
33
- 3. Use matching embedded docs under `node_modules/@aotter/mantle/docs/`.
34
- 4. Never infer provider authority from launch state. Confirm the active GitHub
35
- and Cloudflare accounts before changing them.
36
-
37
- Run the local gate first:
38
-
39
- ```bash
40
- pnpm install --frozen-lockfile
41
- pnpm validate
42
- pnpm typecheck
43
- git status --short
44
- ```
45
-
46
- ## Resume From Observed State
47
-
48
- Do not branch on how the project was created. Verify these facts and skip
49
- completed work:
50
-
51
- 1. `git remote get-url origin` confirms the GitHub repo.
52
- 2. An HTTPS `PUBLIC_ORIGIN` that responds confirms the Cloudflare deploy.
53
- 3. `/admin/sign-in` returning `503 setup_incomplete` means auth is not bound.
54
- Use the recorded auth intent only to choose hosted or self-hosted setup;
55
- live behavior is authoritative.
56
-
57
- If there is no remote, confirm the target account, create a private repo,
58
- commit, and push `main`. If there is no live Worker, confirm the Cloudflare
59
- account, prefer an available connector, or use `pnpm exec wrangler login` with
60
- the user's agreement, then run `pnpm deploy`.
61
-
62
- Capture the live URL in `PUBLIC_ORIGIN` and `Public site:` in `AGENTS.md`, then
63
- commit and push non-secret changes. Reuse any repo or Worker already created.
64
- Do not recreate Landing artifacts for a new direct-authored app. Workers Builds
65
- is optional after a direct deploy.
66
-
67
- When the owner later adopts a custom domain, update `PUBLIC_ORIGIN` and the
68
- provider's OAuth callback together, then redeploy. Do not patch `site_config`
69
- directly; boot syncs its canonical origin from `PUBLIC_ORIGIN`.
70
-
71
- ## Choose Auth
72
-
73
- - **Self-hosted email OTP:** use the application's production transactional-email sender. Replace `ConsoleEmailSender`; never deploy it.
74
- - **Self-hosted GitHub OAuth — free fallback:** use when the application has no email provider. Configure the owner's per-site GitHub OAuth App and Worker secrets using the steps below.
75
- - **Mantle hosted auth — paid, legacy/optional:** use only when a **legacy
76
- Landing handoff** already records a hosted allocation and client
77
- configuration. New direct-authored apps do not get this from Core. Mantle
78
- Platform operates the identity provider; do not ask the user for a per-site
79
- GitHub OAuth App.
80
-
81
- Configure only the selected mode. Core deliberately rejects partial or mixed
82
- hosted/self-managed bindings with `503 setup_incomplete`.
83
-
84
- Do not claim that hosted auth can attach to an arbitrary local repo unless a
85
- legacy Landing handoff already supplies that configuration.
86
-
87
- For the exact boundary, read
88
- `node_modules/@aotter/mantle/docs/auth-hosting-model.md`.
89
-
90
- ## Self-hosted email OTP
91
-
92
- Keep the application's custom `createAuth()` factory, replace
93
- `ConsoleEmailSender` with its production `EmailSender`, and retain
94
- `bootstrapOwner: { match: "email", value: <owner email> }`. Store sender
95
- credentials and `BETTER_AUTH_SECRET` as Worker secrets, put `PUBLIC_ORIGIN` in
96
- non-secret vars, deploy, then verify that the owner receives an OTP at
97
- `/admin/sign-in`. If there is no production email provider, use GitHub OAuth
98
- below instead of deploying console delivery.
99
-
100
- ## Self-hosted GitHub OAuth
101
-
102
- 1. Ask the user to create a GitHub OAuth App:
103
-
104
- - Homepage URL: `<worker-url>`
105
- - Authorization callback URL: `<worker-url>/api/auth/callback/github`
106
- - Device Flow: unchecked
107
-
108
- 2. Put non-secret values in `wrangler.toml`:
109
-
110
- - `MANTLE_AUTH_MODE = "self-managed"`
111
- - `PUBLIC_ORIGIN`
112
- - `GITHUB_CLIENT_ID`
113
- - `ADMIN_GITHUB_LOGIN`
114
- - correct Worker `name`
115
-
116
- Remove `MANTLE_HOSTED_AUTH_ISSUER` and `MANTLE_HOSTED_AUTH_CLIENT_ID` if they
117
- were present for a hosted allocation.
118
-
119
- 3. Keep the Client Secret out of chat. Prefer a Cloudflare connector for
120
- secrets; otherwise use hidden shell input:
121
-
122
- ```bash
123
- read -rsp "GitHub OAuth client secret: " MANTLE_GITHUB_CLIENT_SECRET && printf "\n"
124
- printf '%s' "$MANTLE_GITHUB_CLIENT_SECRET" | pnpm exec wrangler secret put GITHUB_CLIENT_SECRET
125
- openssl rand -hex 32 | pnpm exec wrangler secret put BETTER_AUTH_SECRET
126
- unset MANTLE_GITHUB_CLIENT_SECRET
127
- ```
128
-
129
- Set `BETTER_AUTH_SECRET` once and preserve it. Rotating it invalidates existing
130
- sessions.
131
-
132
- 4. Commit and push only non-secret config, then redeploy:
133
-
134
- ```bash
135
- git add wrangler.toml AGENTS.md
136
- git commit -m "mantle: wire production auth"
137
- git push
138
- pnpm deploy
139
- ```
140
-
141
- ## Hosted Auth (legacy Landing)
142
-
143
- Skip this section unless a legacy Landing handoff is already present. New
144
- direct-authored apps use self-hosted email OTP or GitHub OAuth above.
145
-
146
- Follow that handoff and its client configuration. Hosted configuration remains
147
- in landing-managed Cloudflare Worker bindings. Verify:
148
-
149
- - `MANTLE_AUTH_MODE = "hosted"`;
150
- - `MANTLE_HOSTED_AUTH_ISSUER` is the HTTPS root issuer;
151
- - `MANTLE_HOSTED_AUTH_CLIENT_ID` is the same-origin `/clients/<id>` URL;
152
- - `PUBLIC_ORIGIN` and `ADMIN_GITHUB_LOGIN` are set;
153
- - `BETTER_AUTH_SECRET` exists as a Worker secret;
154
- - `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` are absent.
155
-
156
- Hosted clients use PKCE and have no client secret. Do not write secrets into
157
- `wrangler.toml`.
158
-
159
- Verify that admin sign-in redirects to Mantle Hosted Auth and Staff MCP
160
- authenticates, then skip the self-hosted flow.
161
-
162
- ## Smoke Test
163
-
164
- - public home route;
165
- - `/admin/sign-in`;
166
- - selected admin sign-in path;
167
- - `/mcp/staff` with an agent client when available;
168
- - one type-specific core workflow.
169
-
170
- Media uploads are optional. Configure R2 only when the owner asks for
171
- staff-managed files; then read
172
- `node_modules/@aotter/mantle/docs/handbook/cloudflare/media-r2.md`.
173
-
174
- ## Handoff
175
-
176
- Return:
177
-
178
- - public URL;
179
- - admin sign-in URL;
180
- - Staff MCP URL;
181
- - operator setup URL:
182
- `https://mantle.tools/connect?site=<url-encoded-worker-url>`;
183
- - remote resources created or reused;
184
- - auth mode and any intentionally deferred setup.
185
-
186
- ## Don't
187
-
188
- - Don't create remote resources before the user asks to ship.
189
- - Don't ask for a Cloudflare API token in the base flow.
190
- - Don't commit provider secrets.
191
- - Don't require R2 for first production.
192
- - Don't invent a second provision orchestrator.
193
- - Don't use `/admin/auth/github/callback`; the callback is
194
- `/api/auth/callback/github`.
12
+ # Provision a Mantle project
13
+
14
+ ## Workflow
15
+
16
+ Read the installed SDK README, then follow the version-matched [project and CLI
17
+ workflow](../../handbook/start/project-and-cli.md) before editing.
18
+
19
+ Deploy/create resources only within the user's authorization; reuse configured
20
+ host resources. Apply reviewed and rehearsed migrations before routing traffic;
21
+ imports/first requests perform no DDL or schema checks. Keep auth dynamic, production
22
+ secrets in the host store, trusted HTTPS origin and verified bootstrap rule,
23
+ production email and selected media wiring. Configure Admin ASSETS worker-first
24
+ as in [Workers assets and secrets](../../handbook/cloudflare/service-entry.md#workers-assets-and-secrets). Hyperdrive caching stays off; PG settings are deployment-owned.
25
+ Keep the Workers [public-fetch flags](../../handbook/cloudflare/deploy-and-operate.md#public-fetch-routing),
26
+ including environment overrides; verify deployed MCP with a real OAuth bearer
27
+ (`initialize` and `tools/list`), not only a browser session cookie.
28
+ For MCP Apps, also follow [deployed host acceptance](../../handbook/guides/mcp-apps.md#verify-a-deployed-app-in-chatgpt):
29
+ compare the current resource URI and visibly verify a newly invoked render tool.
30
+ Archive deployed Admin pages using the guide's `previous` recipe and carry them
31
+ through clean builds. Test the same old conversation after changing the UI build,
32
+ without reconnecting, refreshing host tools or clearing caches. A deliberate tool
33
+ contract change has its own migration/host rediscovery step. OAuth renewal, `tools/call` success and a
34
+ local official-host replay do not establish that the deployed App rendered.
35
+ Bun uses native Database/SQL, an app server and scheduler. Verify admin sign-in
36
+ (role admin), denied access, public reads, a persisted write, MCP and selected UI.
37
+ Report artifact/resources/checks and keep rollback artifacts.
38
+
39
+ For ChatGPT Sites, follow [Mantle on ChatGPT Sites](../../handbook/cloudflare/chatgpt-sites.md).
40
+
41
+ The app builds Admin with `mantle bundle`; serve it as [Native
42
+ Admin](../../handbook/guides/native-admin.md) describes. Verify both member and
43
+ admin OTP/UI flows. Deployment of an authorized application does not authorize
44
+ SDK publication; a workspace preview remains a preview.
@@ -1,51 +1,36 @@
1
1
  ---
2
2
  name: update
3
- description: Review and upgrade Mantle SDK dependencies and project-local skills while preserving application source, provider identities and plugin lockfiles.
3
+ description: Upgrade a Mantle project's @aotter/mantle packages to a new exact version, including the move from 0.1.x to 0.2, without overwriting application-owned code.
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: docs/skills/update/SKILL.md
7
- applies_to: mantle grammar v0.1
8
- projection: project
7
+ applies_to: mantle 0.2
8
+ projection: package
9
9
  ---
10
10
 
11
11
  # Mantle Update
12
12
 
13
- Upgrade SDK dependencies deliberately; never overwrite application-owned code.
14
- The old Starter bundle comparison command `mantle update` is removed. This
15
- skill remains the version-matched upgrade workflow, not a replacement CLI.
13
+ ## Workflow
16
14
 
17
- 1. Inspect git status, package.json, lockfile, actual project scripts and
18
- installed versions. Preserve unrelated local changes. Read plugin locks
19
- and legacy `.mantle` metadata if present; they are context, not required.
20
- 2. Select an explicit target release and read that version's GitHub release
21
- notes. Installed docs live under `node_modules/@aotter/mantle/docs/`;
22
- `handbook/releases/index.md` is a stable-release index, so a prerelease
23
- need not appear there. Do not use the old package's docs as the new API contract.
24
- Do not resolve new Starter refs or compare the project to a baseline
25
- template.
26
- 3. Update only selected `@aotter/mantle*` dependencies to the same exact target
27
- version, preserving dependency sections. Use the package manager to update
28
- the lockfile; inspect the dependency diff and required peer changes.
29
- 4. Remove scripts that invoke retired create/bundle-update commands. Keep all
30
- application manifests, handlers, routes, theme, Worker/D1/KV names, origins,
31
- provider bindings, secrets, and legacy metadata. Apply API migration edits
32
- individually; do not copy the reference consumer over a real application.
33
- 5. Use the upgraded package to regenerate machine-owned bindings and project
34
- its skills, then run the application's validation, types and tests:
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
35
17
 
36
- ```sh
37
- pnpm exec mantle generate
38
- pnpm exec mantle generate --check
39
- pnpm exec mantle skills
40
- pnpm exec mantle skills --check
41
- pnpm exec mantle validate
42
- ```
18
+ For 0.1.x, follow [Upgrade 0.1 to 0.2](../../upgrade-0.1-to-0.2.md): give the
19
+ 0.2 service a new database and move data by export and import; never boot 0.2
20
+ against a 0.1.x database. Between 0.2 versions, apply reviewed migrations
21
+ rehearsed on a copy.
43
22
 
44
- Read the refreshed `mantle-develop` skill and the upgraded package's embedded
45
- docs before continuing application edits.
23
+ Pin exact target SDK/peer versions and read its release notes and Admin build
24
+ instructions. Core-to-native replaces app imports/wiring; it is not
25
+ a package-only update. For 0.2-to-0.2 updates, preserve users, sessions, content
26
+ and application code; review emitted SQL/policy diffs and rehearse explicit
27
+ data-preserving schema/auth/role migrations on a copy. Initial schema.sql never
28
+ authorizes rebuilding production tables. Verify auth/Admin/MCP/REST, locks and
29
+ persistence before authorized cutover.
46
30
 
47
- Start the local application and test its actual routes, including configured
48
- auth behavior. Review the final source/lockfile/generated diff; unexpected
49
- provider or user-source changes block completion. Provider credentials must
50
- never enter git or logs. Report exact old/new versions, checks, and remaining
51
- migration work. A dependency update does not authorize a production deploy.
31
+ Follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin)
32
+ for the current application-built Admin; old UI subpaths have no compatibility
33
+ aliases. Pin only actually published versions, or explicitly use a matching
34
+ workspace preview. Recheck member and Admin OTP, all affected native routes,
35
+ private field visibility, OCC and unknown outcomes. Do not publish a package as
36
+ part of an application dependency update.
@@ -0,0 +1,101 @@
1
+ /** Reusable application-owned adapter check. The factory must open an isolated, disposable database/schema. */
2
+ import assert from "node:assert/strict";
3
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
4
+ import { resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { unstable_splitSqlQuery } from "wrangler";
7
+ import type { Postgres } from "../src/kernel/contracts.js";
8
+ import { procedure, transaction } from "../src/kernel/driver/postgres.js";
9
+ import { generateOrThrow, writeGenerated } from "../tests/apps/link.js";
10
+
11
+ export type PostgresAdapterFactory = () => Promise<{ db: Postgres; close(): Promise<void>; interactive?: boolean }>;
12
+
13
+ const atom = (kind: string, name: string, spec: object) => JSON.stringify({ apiVersion: "cms.mantle.aotter.net/v2", kind, metadata: { name }, spec });
14
+ const properties = { key: { type: "string" }, at: { type: "string", format: "date-time" }, day: { type: "string", format: "date" }, payload: { type: "object" }, units: { type: "integer" }, enabled: { type: "boolean" }, amount: { type: "number" } };
15
+ const source = [
16
+ atom("Schema", "adapter_items", { title: "Adapter conformance", schema: { type: "object", properties, required: Object.keys(properties) }, uniqueIndexes: [["key"]] }),
17
+ atom("View", "adapter-items", { surface: "internal", sql: "SELECT id, key, at, day, payload, units, enabled, amount, created_at, CAST(at AS timestamptz) AS computed_at FROM adapter_items ORDER BY id" }),
18
+ atom("View", "adapter-wire", { surface: "public", sql: `SELECT units AS "quantityAlias", enabled, amount, version AS "versionAlias" FROM adapter_items ORDER BY id` }),
19
+ atom("Procedure", "adapter-create", { input: { type: "object", properties, required: Object.keys(properties) }, output: { type: "object" }, handler: { sql: "INSERT INTO adapter_items(key, at, day, payload, units, enabled, amount) VALUES(input.key, input.at, input.day, input.payload, input.units, input.enabled, input.amount) RETURNING id, at, day, payload, units, enabled, amount" } }),
20
+ ].join("\n---\n");
21
+
22
+ /** Returns only a check count; assertions retain the exact failing contract. No latency or benchmark data. */
23
+ export async function verifyPostgresAdapter(factory: PostgresAdapterFactory): Promise<{ checks: number }> {
24
+ const files = await generateOrThrow("bun-postgres", [{ sourceId: "adapter-conformance.yaml", text: source }]);
25
+ await mkdir(resolve(import.meta.dir, "../tests/apps/.generated"), { recursive: true });
26
+ const directory = await mkdtemp(resolve(import.meta.dir, "../tests/apps/.generated/adapter-"));
27
+ let client: Awaited<ReturnType<PostgresAdapterFactory>> | undefined;
28
+ let checks = 0;
29
+ try {
30
+ await writeGenerated(directory, files);
31
+ const app = await import(pathToFileURL(resolve(directory, "generated.ts")).href);
32
+ client = await factory();
33
+ const { db, interactive = true } = client;
34
+ for (const sql of unstable_splitSqlQuery(files["schema.sql"]!)) await db.unsafe(sql, []);
35
+ const caller = { kind: "system", reason: "adapter conformance" } as const;
36
+ const input = { key: "first", at: "2026-10-12T08:00:00.123456+08:00", day: "2024-02-29", payload: JSON.stringify({ text: "'\\", nested: [false, null] }), units: Number.MAX_SAFE_INTEGER, enabled: true, amount: 0.1 };
37
+ const output = await app.procedures["adapter-create"](db, caller, input);
38
+ const created = output.results[0][0];
39
+ assert.equal(created.at, "2026-10-12T00:00:00.123456Z", "declared RETURNING date-time retains microseconds");
40
+ assert.equal(created.day, input.day, "declared RETURNING date retains calendar day");
41
+ assert.deepEqual(created.payload, JSON.parse(input.payload), "JSON text is bound verbatim, not JSON-encoded twice");
42
+ assert.equal(String(created.units), String(input.units), "int8 retains a safe integer exactly");
43
+ assert.equal(created.enabled, true, "PostgreSQL boolean native output"); assert.equal(created.amount, input.amount, "declared number native output"); checks += 6;
44
+ const rows = await app.views["adapter-items"](db, caller, {});
45
+ const count = rows.count;
46
+ assert.ok(Array.isArray(rows), "unsafe returns a row array");
47
+ assert.equal(count, 1, "unsafe array carries affected count");
48
+ assert.equal(rows[0].at, created.at, "declared SELECT date-time retains microseconds");
49
+ assert.equal(rows[0].day, input.day);
50
+ assert.match(rows[0].created_at, /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}Z$/, "system timestamps have the same wire form");
51
+ // Computed outputs are native: the driver may return Date (milliseconds) or text (microseconds).
52
+ assert.ok(rows[0].computed_at instanceof Date || typeof rows[0].computed_at === "string"); checks += 6;
53
+ await assert.rejects(() => app.procedures["adapter-create"](db, caller, input), { code: "CONFLICT" }); checks++;
54
+ const step = { sql: "UPDATE adapter_items SET key = $1 WHERE id = $2 RETURNING id", binds: ["changed", "missing"], row: true };
55
+ await assert.rejects(() => procedure(db, [step], "adapter/zero"), { code: "CONFLICT" }); checks++;
56
+ const integer = await db.unsafe("SELECT CAST($1 AS bigint) AS positive, CAST($2 AS bigint) AS negative, CAST($3 AS numeric(15,2)) AS decimal", ["9007199254740993", "-9007199254740993", "1234567890123.45"]);
57
+ assert.equal(String(integer[0].positive), "9007199254740993", "unsafe int8 must not be rounded by an adapter");
58
+ assert.equal(String(integer[0].negative), "-9007199254740993");
59
+ assert.equal(integer[0].decimal, "1234567890123.45", "numeric text retains declared scale and precision"); checks += 3;
60
+ const { default: page } = await import(pathToFileURL(resolve(directory, "paged/wire/views/adapter-wire.ts")).href);
61
+ for (const value of ["9007199254740991", "9007199254740993", "-9007199254740993", "9223372036854775807", "-9223372036854775808"]) {
62
+ await db.unsafe("UPDATE adapter_items SET units=CAST($1 AS bigint), version=CAST($1 AS bigint)", [value]);
63
+ const wire = (await page(db, caller, {})).rows[0];
64
+ // The selected-boundary module returns exact integer text; REST/MCP turn safe values into numbers.
65
+ assert.equal(String(wire.quantityAlias), value); assert.equal(String(wire.versionAlias), value);
66
+ assert.equal(wire.enabled, true); assert.equal(wire.amount, input.amount); checks += 4;
67
+ }
68
+ await db.unsafe("UPDATE adapter_items SET version=1", []);
69
+ const store = app.store(db, caller), collection = store.collection("adapter_items");
70
+ const changed = { sql: "UPDATE adapter_items SET key = $1 WHERE id = $2 RETURNING id", binds: ["transaction", created.id], row: true };
71
+ if (!interactive) {
72
+ let calls = 0;
73
+ const tapped = { unsafe: (sql: string, values: any[]) => { calls++; return db.unsafe(sql, values); }, begin: db.begin.bind(db) };
74
+ const limitedStore = app.store(tapped, caller);
75
+ for (const run of [() => procedure(tapped, [changed, changed], "adapter/multi"), () => limitedStore.transaction(async () => { throw Error("callback must not run"); }), () => limitedStore.batch([limitedStore.collection("adapter_items").update(created.id, { key: "no-write" }, { lock: 1 }), limitedStore.collection("adapter_items").update(created.id, { key: "also-no-write" }, { lock: 1 })])]) {
76
+ await assert.rejects(run, (error: unknown) => {
77
+ const e = error as Error & { code?: string; status?: number };
78
+ return e instanceof Error && e.name === "ContractError" && e.code === "TRANSACTION_UNSUPPORTED" && e.status === 400 && /transaction/i.test(e.message);
79
+ });
80
+ assert.equal(calls, 0, "unsupported interactive transaction refuses before any statement"); checks += 2;
81
+ }
82
+ } else {
83
+ await transaction(db, async tx => {
84
+ await procedure(tx, [changed, changed], "adapter/nested-handle");
85
+ assert.equal((await tx.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction");
86
+ await assert.rejects(() => transaction(tx, async () => {}), /Nested transactions/);
87
+ }); checks += 2;
88
+ assert.equal((await db.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction", "begin commits after callback"); checks++;
89
+ const rollback = new Error("rollback sentinel");
90
+ await assert.rejects(() => transaction(db, async tx => { await tx.unsafe("UPDATE adapter_items SET key = $1 WHERE id = $2", ["rolled-back", created.id]); throw rollback; }), error => error === rollback);
91
+ assert.equal((await db.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction", "begin rolls back callback failure"); checks += 2;
92
+ await store.batch([collection.update(created.id, { key: "batch" }, { lock: 1 })]); checks++;
93
+ }
94
+ const uncertain = { unsafe: db.unsafe.bind(db), begin: async <T>(_options: string, callback: (tx: Postgres) => Promise<T>) => { await callback(db); throw new Error("socket lost at COMMIT"); } };
95
+ await assert.rejects(() => transaction(uncertain, async () => {}), { code: "OUTCOME_UNKNOWN" }); checks++;
96
+ return { checks };
97
+ } finally {
98
+ try { if (client) await client.close(); }
99
+ finally { await rm(directory, { recursive: true, force: true }); }
100
+ }
101
+ }
@@ -0,0 +1,227 @@
1
+ # Upgrading from 0.1.x to 0.2.0 (for AI agents)
2
+
3
+ For **0.2.0-alpha.7 and later**, use the
4
+ [native SDK migration](handbook/releases/index.md#native-sdk-migration-alpha7),
5
+ [current CLI](handbook/start/project-and-cli.md) and
6
+ [app shell](examples/app-shell/). The application owns routing,
7
+ Auth, native handles and database migrations; the former `createMantle` runtime,
8
+ host presets, boot-time convergence and CLI flags below have been replaced.
9
+ Install the same exact version of `@aotter/mantle`, `@aotter/mantle-ui` and,
10
+ when selecting Admin, `@aotter/mantle-admin`. Start with a new database for a
11
+ 0.1.x migration and preserve every authorization, scope and OCC requirement.
12
+ The remainder describes the earlier alpha contract for historical reference.
13
+
14
+ No codemod, and no in-place upgrade of a 0.1.x database. A coding agent moves
15
+ one project by hand with this guide. When it and
16
+ [ADR-0032](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0032-store-centric-core-and-service-contract.md),
17
+ [ADR-0033](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0033-storage-converges-to-the-plan.md),
18
+ [ADR-0034](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0034-store-is-authored-as-sql.md),
19
+ [ADR-0035](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0035-sql-dialects.md),
20
+ [ADR-0036](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0036-host-and-dialect.md) or
21
+ [ADR-0038](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0038-bun-native-host.md) disagree, the ADR wins.
22
+
23
+ ## Rules
24
+
25
+ - Work on a branch. Keep the 0.1.x project running until the 0.2.0 one passes
26
+ its checks.
27
+ - Move meaning, not text. If a manifest does something no row below covers,
28
+ stop and ask. Vercel projects have no 0.2.0 preset: stop and ask. A Bun
29
+ project moves to `host: bun` (section 2).
30
+ - Never loosen access. Every 0.1.x `requires`, guard, `scope` and
31
+ `x-mantle-bind` must come out at least as strict. `x-mantle-bind` is
32
+ refused in 0.2.0 and stamps nothing, so each one becomes `scope` or an
33
+ explicit value in the write (section 3).
34
+ - Give the 0.2.0 service a new database; never boot it against the 0.1.x
35
+ one. Move data by export and import (section 5).
36
+ - `mantle generate --check` is the gate. Never hand-edit `.mantle/generated/`.
37
+
38
+ ## 1. Packages
39
+
40
+ | 0.1.x | 0.2.0 |
41
+ |---|---|
42
+ | `@aotter/mantle` and `@aotter/mantle-spec`, `-runtime`, `-cloudflare`, `-auth`, `-admin`, `-web`, `-bun`, `-vercel`, `-indexeddb` | `@aotter/mantle` (subpaths `/spec`, `/d1`, `/postgres`, `/cloudflare`, `/bun`, `/auth`, `/admin`, `/mcp`, `/web`, `/testing`) |
43
+ | `@aotter/mantle-admin-ui` | `@aotter/mantle-ui` at the same version as `@aotter/mantle`: the preset serves the console at `/admin` from its `dist/admin` files; nothing to import. Its `/kit` re-export is gone: import `@aotter/mantle-ui/kit` |
44
+
45
+ Remove every old package and install `@aotter/mantle` at the exact target
46
+ version. `mantle generate` names any other package it needs, with the install
47
+ command; it never installs anything.
48
+
49
+ ## 2. `mantle.config.json`
50
+
51
+ 0.1.4 had none; 0.1.5-alpha.1 wrote a `version: 1` one. Write version 2:
52
+
53
+ ```json
54
+ { "version": 2, "identity": "mantle", "features": ["mcp", "admin", "web"] }
55
+ ```
56
+
57
+ - `version: 1` is refused.
58
+ - `host` is `cloudflare` (the default, left out), `bun` or `none`; `none`
59
+ writes the plan and types only, for a platform with no preset. `host: bun`
60
+ refuses an enabled schedule Trigger: Bun has no cron dispatcher in the
61
+ preset.
62
+ - `identity`: `mantle` if the project used Mantle auth (Better Auth sign-in,
63
+ Admin users, OAuth for MCP), `custom` if it resolved callers itself, `none`
64
+ if it had no signed-in callers. `admin` needs an identity.
65
+ - `features` is a subset of `mcp`, `admin`, `web`, in that order. The 0.1.x
66
+ features `spec`, `runtime` and `api` are dropped: REST is always mounted.
67
+ - `dialect` is `sqlite` (alias `d1`, the default, left out) or `postgres`
68
+ (PostgreSQL through Hyperdrive on `cloudflare`; node-postgres on `bun`, where it
69
+ is the default). A 0.1.x project was SQLite: keep `sqlite` (with
70
+ `--host bun`, pass `--dialect sqlite`) unless the data also moves to
71
+ PostgreSQL.
72
+ - `host` and `dialect` are chosen on the first `mantle generate` (`--host`,
73
+ `--dialect`); a rerun that asks for another is refused.
74
+
75
+ ## 3. Manifests
76
+
77
+ Set `apiVersion: cms.mantle.aotter.net/v2` on every document; a v1 document
78
+ is refused.
79
+
80
+ **Native columns are snake_case in SQL**: `id`, `status`, `version`,
81
+ `created_at`, `updated_at`, `author_id`. (`ctx.store` JSON keeps the
82
+ camelCase names.)
83
+
84
+ ### Schema
85
+
86
+ | 0.1.x | 0.2.0 |
87
+ |---|---|
88
+ | `scope: { owner: $ctx.user.id }` | `scope: "owner = auth.uid()"`. The field is a required string, the leftmost column of an index (`indexes: [[owner]]`), and the first column of every `uniqueIndexes` entry. Store fills it; a write never sets it |
89
+ | `x-mantle-bind: ctx.user` on a field only its owner may see | remove the bind; add `scope` as above |
90
+ | any other `x-mantle-bind: ctx.user` | remove the bind; every write that sets the field sets it to `auth.uid()`, never to an input |
91
+ | `x-mantle-bind: ctx.staff` | remove the bind; set it to `auth.uid()` in writes whose Procedure requires `ctx.admin` |
92
+ | `x-mantle-bind: now` | remove the bind; set it to `now()` in every write that sets it |
93
+ | `searchableFields` | unchanged, now trigram full-text search; date and date-time fields are refused |
94
+
95
+ `checks` (boolean SQL over the row's own columns) is new and optional.
96
+
97
+ A JSON Schema `pattern` (in a Schema, a View input or a Procedure input/output)
98
+ is now refused with `INVALID_PATTERN` when it could backtrack without bound on
99
+ a caller's string:
100
+
101
+ - a repeated group whose body repeats or alternates, such as the 0.1.x slug
102
+ `^[a-z0-9]+(-[a-z0-9]+)*$` or `(a|b)+`, and any backreference;
103
+ - a pattern whose backtracking, branches × maxLength^e, could exceed
104
+ 10,000,000 — e is its variable quantifiers (`*`, `+`, `?`, `{m,}`,
105
+ `{m,n}`) plus 1 unless it starts with `^`, branches the product of its
106
+ alternations' alternatives — or one with e > 0 whose string has no
107
+ `maxLength`: `^[a-z0-9-]+$` (e = 1) allows any maxLength up to
108
+ 10,000,000, an email `^[^@]+@[^@]+$` (e = 2) up to 3162, the unanchored
109
+ `[^@]+@` (e = 2) also 3162, `^a*a*a*…$` almost none.
110
+
111
+ Rewrite a slug as `{ type: string, maxLength: 120, pattern: "^[a-z0-9-]+$" }`,
112
+ which also accepts leading, trailing and doubled hyphens. On a Schema field
113
+ that must not have them, add a `checks` entry:
114
+ `"slug NOT LIKE '-%' AND slug NOT LIKE '%-' AND slug NOT LIKE '%--%'"`.
115
+
116
+ ### View
117
+
118
+ A View is one SQL `SELECT` in `spec.sql`, with `input` for its parameters.
119
+ The SQL is PostgreSQL syntax: it names declared Schemas (never
120
+ schema-qualified), `input.<name>`, `auth.uid()`, `auth.role()`, `now()` and
121
+ `mantle.*` functions. The runtime adds scope, TTL and published-only to every
122
+ Schema reference; do not repeat them.
123
+
124
+ | 0.1.x | 0.2.0 |
125
+ |---|---|
126
+ | `from: posts`, `fields: [id, title]` | `SELECT id, title FROM posts` |
127
+ | `filter: { eq: { field: status, value: published } }`, with `and` / `or` | `WHERE status = 'published'`, with `AND` / `OR` |
128
+ | `value: { "$ctx.user": id }` | `auth.uid()`, or `scope` on the Schema |
129
+ | `params` and `{ "$param": q }` | `input: { type: object, properties: { q: … } }` and `input.q` |
130
+ | `orderBy: [{ field: createdAt, direction: desc }]` | `ORDER BY created_at DESC` |
131
+ | `limit: 20` | `LIMIT 20` (a `LIMIT` needs an `ORDER BY`) |
132
+ | `select:` (`columns`, `where`, `$input.x`, `$ctx.user.id`, `$now`) | the same query as SQL: `input.x`, `auth.uid()`, `now()` |
133
+ | `sql:` (SQLite, `:name` parameters) | the same `sql:`, `:name` → `input.name`, rewritten to PostgreSQL syntax where it differs |
134
+ | free-text search over `searchableFields` | `WHERE mantle.search(p, input.q) ORDER BY mantle.search_rank(p)`; an unqualified `search` is refused |
135
+ | `page` / `show` on the wire | `limit` / `cursor`, `nextCursor` in the response |
136
+
137
+ ### Procedure
138
+
139
+ 0.1.x builtin ops are `create`, `update`, `upsert`, `delete` and `archive`.
140
+
141
+ | 0.1.x | 0.2.0 |
142
+ |---|---|
143
+ | builtin `create` | `handler: { sql: "INSERT INTO s (a, b) VALUES (input.a, input.b) RETURNING id" }` (on a `publishing` Schema the row starts as a draft, as before) |
144
+ | builtin `update` (always version-checked, merges over the stored row) | `UPDATE s SET a = COALESCE(input.a, a), … WHERE id = input.id AND version = input.expectedVersion`. Keep both: without the version the lock is lost; without `COALESCE` an omitted optional field is cleared |
145
+ | builtin `upsert` with `match` | `INSERT … ON CONFLICT (<match columns>) DO UPDATE SET x = EXCLUDED.x`; the conflict columns are a unique index; on a scoped Schema the scope field may be named or left out, Store adds it |
146
+ | builtin `delete` | `DELETE FROM s WHERE id = input.id AND version = input.expectedVersion` |
147
+ | builtin `archive` | a `ref` handler: `ctx.store.write([{ update: "s", set: { status: "archived" }, where: { id }, lock: expectedVersion }])` (SQL cannot set `status`) |
148
+ | `handler: { kind: ref, ref: fn }` | `handler: { ref: fn }`; the function's signature changes (section 4) |
149
+ | `target: { schema, id, version }` | optional: inferred from `WHERE id = input.… AND version = input.…` |
150
+
151
+ A write never sets the scope field, `version`, `status`, `author_id` or the
152
+ timestamps. On a scoped Schema an `INSERT` names no `id`. Several statements
153
+ in one `sql` apply together or not at all. `requires.auth` and
154
+ `requires.guard` are unchanged.
155
+
156
+ ### Trigger
157
+
158
+ - `schedule` cron is POSIX: weekday 0 = Sunday. 0.1.x used Cloudflare's
159
+ numbering (1 = Sunday), so subtract 1 from every weekday number, in ranges
160
+ and lists too (`1` → `0`, `2-6` → `1-5`, `7` → `6`); `*` and `*/n` stay.
161
+ On host `cloudflare`, a cron that sets both a day of month and a weekday
162
+ is refused.
163
+ - `lifecycle` `errorPolicy` is removed: a before hook fails closed (a 0.1.x
164
+ `continue` now blocks the write), an after hook is best effort.
165
+ - `http`, `mcp` and other `lifecycle` fields keep their shape.
166
+
167
+ ## 4. Handler code
168
+
169
+ A `ref` handler is `(input, ctx) => output` in the service's `handlers` map.
170
+ The generated types list exactly the plan's refs.
171
+
172
+ | 0.1.x | 0.2.0 |
173
+ |---|---|
174
+ | `ctx.user` present | `ctx.caller.kind === "user"`. Never test `!== "anonymous"`: a schedule runs as `system` |
175
+ | `ctx.user.id` | `ctx.caller.subject` (after that check) |
176
+ | `ctx.staff` | `isAdmin(ctx.caller)` from `@aotter/mantle/contracts` |
177
+ | `ctx.event`, `ctx.schedule` | `ctx.cause` (`kind`: `http`, `mcp`, `internal`, `schedule`, `lifecycle`); `ctx.cause.cron` is POSIX, so shift any cron string the code compares |
178
+ | `ctx.event.entry` | **a loop** over `ctx.cause.rows` (one statement can touch many rows; never `rows[0]`). Rows are flat: `entry.data.x` → `row.x` |
179
+ | `ctx.store.view(name, { params, page, show, search, filters })` | `ctx.store.view(name, { input, limit, cursor, search, filters })`; `search` and `filters` match only the View's `uiSchema.list.searchFields` and `filterFields` |
180
+ | `runtime.entries`, `runtime.executeView`, `ctx.writeAtomically` | `ctx.db.<schema>.get / first / find`, `ctx.store.view`, `ctx.store.write([...ops])` (all or nothing), `ctx.store.id()` |
181
+ | `runtime.store.as(ctx)` | `runtime.store.as(caller)` |
182
+ | calling another Procedure through a `getRuntime` closure | `await ctx.invoke("procedure-name", input)` (keeps the caller, re-checks its auth) |
183
+
184
+ `ctx.store` is scoped to the caller. `runtime.store` is not scoped; use it
185
+ only in trusted host code.
186
+
187
+ ## 5. The Worker and the data
188
+
189
+ 1. Move the old `src/`, `wrangler.jsonc` (or `.toml`) and `tsconfig.json`
190
+ aside. `mantle generate` writes the preset (`src/*.ts`, `tsconfig.json`,
191
+ and `wrangler.jsonc` on `cloudflare` or `.env.example` on `bun`) only
192
+ where no such file exists, and only while `src/service.ts` is missing.
193
+ 2. Run `mantle generate`, then move custom routes, bindings and env from the
194
+ old Worker into the new files by hand.
195
+ 3. With `identity: mantle`, sign-in uses a console email sender locally;
196
+ replace it with a real `EmailSender` before deploying.
197
+ 4. Admin MCP moves from `/mcp/staff` to `/mcp/admin`. Update MCP client URLs.
198
+ It admits only the admin layer classified by Better Auth’s admin plugin
199
+ `adminRoles` (default `["admin"]`), and requires the `mcp` scope for tokens.
200
+ 5. Import data into the new database through `runtime.store`. A scoped row
201
+ goes through `runtime.store.as(<its owner's caller>)`, so the scope field is
202
+ the owner. `status`, `created_at` and `author_id` cannot be imported:
203
+ publish with a second update that sets `status`, and accept new
204
+ timestamps and author.
205
+
206
+ ## 6. CLI
207
+
208
+ | 0.1.x | 0.2.0 |
209
+ |---|---|
210
+ | `mantle validate`, `mantle emit-openapi` | `mantle generate --check` (fails if anything is stale; `--database <file>` also prints the storage SQL) |
211
+ | `mantle generate --host …` (0.1.5-alpha.1) | `mantle generate --host cloudflare\|bun\|none` on the first run only |
212
+ | `mantle generate --output`, `--namespace` | removed: output is always `.mantle/generated/` |
213
+ | `mantle skills`, the `mantle-harness` bin | removed |
214
+
215
+ ## 7. Verify
216
+
217
+ 1. `mantle generate` and `mantle generate --check` exit 0.
218
+ 2. The project typechecks against the generated `mantle.ts`.
219
+ 3. Each 0.1.x View and Procedure returns the same rows for the same caller,
220
+ including a caller who must see nothing (scope) and an anonymous request.
221
+ 4. A write cannot set an owner, author or timestamp field from its input.
222
+ 5. Every schedule fires on the intended weekday.
223
+ 6. `wrangler dev` (or `bun src/index.ts`) starts, sign-in works if
224
+ `identity` is `mantle`, and Admin, MCP and REST answer on their paths.
225
+
226
+ Report what changed per manifest, every place where access got stricter, and
227
+ anything you could not map.