@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,315 +1,23 @@
1
- ---
2
- description: Procedure field reference — runtime order of operations, ref and builtin handlers, the builtin op contract, EntryRow responses and Admin operations.
3
- ---
4
1
  # Procedure
5
2
 
6
- A Procedure is a typed callable: input schema, output schema, authorization requirement, and one handler binding. It is the only atom with a code seam, and it is never exposed on its own — a [Trigger](./trigger.md) is what makes it reachable. This page is the field-level contract; the concepts are in [Procedures and Triggers](../concepts/procedures-and-triggers.md). Envelope rules are in [Manifest envelope and conventions](./manifest.md), and diagnostic codes are catalogued in [Diagnostics](./diagnostics.md).
3
+ A Procedure requires `input`, `output` JSON Schemas and a `handler` containing SQL or a ref name. Optional fields include title, description, requires, target, MCP annotations and Admin uiSchema. See [manifest envelope](manifest.md).
7
4
 
8
- ## Fields
5
+ SQL uses declared Schemas, `input.<name>`, `auth.uid()` and the expressions [the SQL reference](sql.md) lists for the selected dialect; [SQL patterns](../guides/sql-patterns.md) shows conditional status, approval, partial updates, upserts and guards. Compiler policy supplies scope and system fields. Do not assign scope, version, status, published_at, timestamps or author columns yourself. A SQL Procedure cannot write a `publishing` Schema: create and edit its rows from a ref handler through the Store, and change status with the generated publish/unpublish/archive transitions. `restrict` narrows the rows the statements read and write, as on a [View](view.md#restrict); it is refused on ref handlers.
9
6
 
10
- | Field | Type | Required | Rules |
11
- |---|---|---|---|
12
- | `title` | LocalizedText | no | Admin label for the staff-operations surface. Absent falls back to a Title-Cased `metadata.name`. |
13
- | `description` | LocalizedText | no | The MCP tool description and the `description` field of `GET /admin/api/operations`. |
14
- | `requires` | AuthorizationRequirements | no | `auth.all` predicates plus one optional `guard.procedure`. See [Authorization](./authorization.md). |
15
- | `input` | JSON Schema | yes | Must be an object. Becomes the MCP tool `inputSchema` and the OpenAPI request body. |
16
- | `uiSchema` | object | no | Admin-only. Accepts `collectionAction` and `fields`. Violations are `SCHEMA_UI_INVALID`. |
17
- | `output` | JSON Schema | yes | Checked after the handler returns. Failure is `OUTPUT_VALIDATION_FAILED` (500). |
18
- | `handler` | `ref` \| `builtin` | yes | Exactly one binding shape; see below. |
19
- | `mcp` | object | no | MCP tool annotations the author asserts: `readOnlyHint`, `destructiveHint`, `openWorldHint` (booleans). Core infers what it can prove — every builtin handler writes, `op: delete` destroys, an `x-mcp-hint: idempotency-key` input makes the tool idempotent — and emits nothing else, so absent hints keep the MCP spec's conservative defaults. `readOnlyHint: true` on a builtin handler is `BUILTIN_HANDLER_CONTRACT_INVALID`. |
7
+ A row-classified INSERT/UPDATE/DELETE (one that targets a single row, e.g. `WHERE id = input.id`) must affect one row. Use `WHERE id = input.id AND version = input.expectedVersion` for explicit OCC. Set operations, including UPDATE with a subquery and `INSERT ... SELECT`, may affect zero rows without an error: check the returned rows when zero means failure. A unique clash, a failed Schema `checks` constraint or a failed row assertion is `CONFLICT` (409) with `kind` `"unique"`, `"check"` or `"row"` ([diagnostics](diagnostics.md)); statements run atomically. A check failure keeps that code, and its message is `A check of the Schema failed: <schema>: <check>` on every target, with the driver's own error only in `cause`; `ContractError.fields` is `{ kind: "check", schema, check }`. `check` is the expression on SQLite/D1 and the generated constraint name on PostgreSQL. Results are raw `{ results }`: one array of RETURNING rows per statement, in order (`results[0][0].id` is the first statement's first row), not runtime-validated authored outputs, with driver-native values: a SQLite/D1 boolean is `1`/`0` here, while a View returns a declared boolean column as `true`/`false`; read a row back through a View when a client needs the typed form. An array input is a row source: `json_each(input.items)` on SQLite/D1 and `jsonb_array_elements(input.items) AS j(value)` on PostgreSQL write any number of rows in the same atomic program. Callers pass the array itself (from a handler, `ctx.store.procedures["place-order"]({ orderRef, items })`); the generated input type is the authored one and the generated query serializes a JSON input for the driver, so no `JSON.stringify` is needed (a string is bound as it is). See [several rows that commit together](../guides/typed-queries.md#several-rows-that-commit-together). RETURNING uses SQL output names; quote an explicit alias to preserve declared case (`RETURNING reqStatus AS "reqStatus"`). Generation warns about unaliased direct fields whose declared name differs from storage.
20
8
 
21
- Both `input` and `output` are walked by the [JSON Schema subset](./schema.md#json-schema-subset) validator, so the same recognized and rejected keywords apply.
9
+ A row-classified UPDATE/DELETE on an excluded or missing id affects zero rows and returns HTTP 409 (`CONFLICT`), just like a stale version; reads return not found. Native Admin's visible-row pre-read returns 404 for excluded/missing ids.
22
10
 
23
- ## Order of operations
11
+ A ref handler is application code called as `(ctx, input)`. Generation lists the `Handlers` keys the app must provide (every run, whether or not they exist yet); `tsc --noEmit` checks the implementation against the generated `Handlers` type. Wire them at `store(db, caller, { handlers })` and selected surfaces. The context has the Caller, the native db and a Store for that caller. A handler is not one transaction by itself: when it runs inside a caller's `store.transaction`, its db and Store are that transaction's; otherwise each write commits on its own. On PostgreSQL and bun:sqlite, `ctx.transaction(async ({ store, db }) => …)` makes writes that must commit together atomic; it joins the transaction the handler already runs in. A joined transaction has no savepoint, so let an error inside `ctx.transaction` propagate: a handler that catches it and carries on keeps the writes made before the error. Keep external calls (payments, supplier APIs) outside it, so a callback never waits for an uncommitted row. D1 has no interactive transactions, so its `HandlerContext` has no `transaction`: put writes that must commit together in one SQL Procedure, whose statements commit together, and call it through `ctx.store.procedures`. It is not a sandbox; the app owns external side effects and input/output handling. The context has no `env`: on Workers read bindings and secrets with `import { env } from "cloudflare:workers"`. The Caller carries no name or email; `userOf(caller)` from `@aotter/mantle/auth` returns the Better Auth user record it was resolved from, for session, OAuth and service credentials alike (undefined for system callers).
24
12
 
25
- Every invocation — HTTP Trigger, MCP tool call, lifecycle hook, Admin operation — runs the same pipeline.
13
+ `target: { schema, id, version? }` binds an Admin operation to a row. SQL targets can be inferred for a single locked row operation; declare a target for a ref operation when needed. Input fields must have the required shapes.
26
14
 
27
- | Step | Behavior | Failure |
28
- |---|---|---|
29
- | 1. Authorize | Evaluate every `requires.auth.all` predicate against the caller context. | `UNAUTHENTICATED` (401) when the caller carried no credential, `AUTH_DENIED` (403) when an authenticated caller falls short. |
30
- | 2. Validate input | Compile `input` to zod and parse the request. | `INPUT_VALIDATION_FAILED` (400), pointing at the first failing property. |
31
- | 3. Guard | Invoke `requires.guard.procedure` with the validated input and the same context. | Any guard failure denies the target. Guards fail closed. |
32
- | 4. Dispatch | `ref`: look up the registration key and call the function. `builtin`: run the op. | See the two handler sections. |
33
- | 5. Validate output | Parse the handler result against `output`. | `OUTPUT_VALIDATION_FAILED` (500) — this is a handler bug, not a caller error. |
15
+ ## MCP tool hints
34
16
 
35
- The value returned to the caller is the handler's own result. Output validation checks it; it does not strip unexpected fields.
17
+ Every generated AI-facing tool (MCP, WebMCP, publishing and media tools) carries a `title`, `readOnlyHint` and `destructiveHint`; clients such as Claude use them to decide between auto-approval and confirmation. The title is the declared `title`, else the name made readable (`add-item` reads `Add item`). A View is `readOnlyHint: true, destructiveHint: false`. A Procedure is `readOnlyHint: false`, and `destructiveHint` is `false` only when its SQL inserts without overwriting: any DELETE or UPDATE, an `ON CONFLICT DO UPDATE` or a ref handler makes it `true`. Of the generated publishing transitions only `archive` is destructive. The media tools always carry `openWorldHint: true`; the upload policy, list and get tools are read-only and only `delete_media_asset` is destructive. Admin's WebMCP navigation tools (`admin_get_context`, `admin_navigate`) are read-only. `spec.mcp` accepts `readOnlyHint`, `destructiveHint` and `openWorldHint`, and a declaration always wins; a declared `readOnlyHint: true` defaults `destructiveHint` to `false`. Outside the media tools `openWorldHint` appears only when declared. `idempotentHint` is never generated, and `spec.mcp` refuses it. Hints never change authorization.
36
18
 
37
- ## `handler.kind: ref`
19
+ Generated requires checks gate calls. Boundary input validators are generated separately; direct queries accept driver-ready, app-validated input. Dynamic guards and lifecycle hooks are rejected. See [native execution](../guides/native-execution.md) and [surface contracts](surface.md).
38
20
 
39
- ```yaml
40
- handler:
41
- kind: ref
42
- ref: approve-purchase-order
43
- ```
21
+ SQL `INSERT … VALUES`/upsert into a user-field scoped Schema follows the Store's insert rule ([field scope](schema.md#field-scope)); `INSERT … SELECT` into it is system-only, so a ref handler inserts those rows through `ctx.store`. `ctx.store` speaks storage names, not the manifest's: the handler's `input` uses authored names (`input.parentId`), while Store writes, filters and rows use the folded column (`insert({ parentid: input.parentId })`, `where: { parentid }`, `row.parentid`); generated Store types show the exact names ([typed queries](../guides/typed-queries.md)). SET cannot change a field-scope column, including ON CONFLICT DO UPDATE. Scoped unique conflict targets gain the owner/field prefix automatically.
44
22
 
45
- `ref` is an **opaque registration key, not a path**. It never names a file, module or export. The consumer passes a matching key in the `handlers` map given to the runtime or Worker, and the key is the whole contract between manifest and code.
46
-
47
- | Rule | Effect |
48
- |---|---|
49
- | `ref` is a non-empty string; only `kind` and `ref` are accepted under `handler`. | `INVALID_MANIFEST_ENVELOPE` |
50
- | Every declared `ref` resolves to a registered handler. | `HANDLER_NOT_REGISTERED` at boot, listing the registered keys as candidates. |
51
- | An unregistered key reached at request time. | The same `HANDLER_NOT_REGISTERED` code, mapped to 500 — defense in depth for embeddings that skipped boot validation. |
52
- | A handler throws. | Anything other than a structured error becomes `INTERNAL_ERROR` (500) with a safe generic message; exception details remain in internal logs. |
53
-
54
- To return a structured error instead, throw `InvokeFailure` carrying a diagnostic; the runtime unwraps it and returns that diagnostic with its own status. This is how a handler reports `CONFLICT`, `ENTITLEMENT_REQUIRED` or a domain-specific `INPUT_VALIDATION_FAILED` rather than a generic 500.
55
-
56
- ### `ref` example
57
-
58
- ```yaml
59
- apiVersion: cms.mantle.aotter.net/v1
60
- kind: Procedure
61
- metadata:
62
- name: approve-purchase-order
63
- spec:
64
- title: { en: Approve purchase order, "zh-TW": 核准採購單 }
65
- description: Approve a submitted order and record the approver.
66
- requires:
67
- auth:
68
- all:
69
- - { "ctx.staff": [owner, editor] }
70
- input:
71
- type: object
72
- additionalProperties: false
73
- required: [orderId, decision]
74
- properties:
75
- orderId: { type: string, x-mantle-ref: purchase-orders }
76
- decision: { type: string, enum: [approve, reject] }
77
- note: { type: string, maxLength: 2000 }
78
- requestId: { type: string, x-mcp-hint: idempotency-key }
79
- uiSchema:
80
- fields:
81
- note: { widget: textarea }
82
- output:
83
- type: object
84
- required: [orderId, status]
85
- properties:
86
- orderId: { type: string }
87
- status: { type: string, enum: [approved, rejected] }
88
- handler:
89
- kind: ref
90
- ref: approve-purchase-order
91
- ```
92
-
93
- ```ts
94
- // src/mantle/config.ts
95
- import { approvePurchaseOrder } from "./handlers/approve-purchase-order";
96
-
97
- export const handlers = {
98
- "approve-purchase-order": approvePurchaseOrder,
99
- };
100
- ```
101
-
102
- ## `handler.kind: builtin`
103
-
104
- A shortcut over the entry-writer chokepoint for Procedures whose body is "write a row". Reach for `ref` as soon as there is real business logic.
105
-
106
- ```yaml
107
- handler:
108
- kind: builtin
109
- op: create | update | upsert | delete | archive
110
- schema: <Schema metadata.name>
111
- match: [<field>, ...] # only with op: upsert
112
- ```
113
-
114
- | Rule | Diagnostic |
115
- |---|---|
116
- | Only `kind`, `op`, `schema` and `match` are accepted; `ref` alongside `builtin` is rejected. | `INVALID_MANIFEST_ENVELOPE` |
117
- | `op` is one of the five; `schema` is a non-empty string. | `INVALID_MANIFEST_ENVELOPE` |
118
- | `match` appears only with `op: upsert`, and is a non-empty array of unique non-empty strings. | `INVALID_MANIFEST_ENVELOPE` |
119
- | `schema` names a declared Schema. | `BUILTIN_HANDLER_SCHEMA_UNKNOWN` |
120
- | `input` is an object schema. | `BUILTIN_HANDLER_CONTRACT_INVALID` |
121
- | The runtime was built without the builtin dispatcher. | `HANDLER_BUILTIN_NOT_IN_V010` at request time. |
122
-
123
- `request_publish` and `publish` are deliberately absent: they are lifecycle operations, not CRUD primitives.
124
-
125
- ### Ops
126
-
127
- | `op` | Runtime behavior | Input contract |
128
- |---|---|---|
129
- | `create` | Projects `input ∩ Schema.properties` into `data`, stamps every `x-mantle-bind` property, generates an id and writes. `status` is `draft`, or `published` on a `lifecycle: operational` Schema. `authorId` is `ctx.user?.id ?? null`. Returns the created row. | `input` is an object schema. No other required properties. |
130
- | `update` | Loads the row (`NOT_FOUND` if absent), merges the patch over the stored `data` so omitted fields and existing stamps survive, writes under optimistic concurrency against the caller's `expectedVersion` (observed native `entry.version` at read time, not `version+1`), bumps `version`. | `id` (strict `type: string`) and `expectedVersion` (strict `type: number`) declared under `properties` **and** listed in `required`. |
131
- | `upsert` with `match` | Reads the matched fields off the validated input and looks the row up by those data values. Found: the update path, using the **caller's** `expectedVersion` (never the preloaded row's version). Not found: the create path only when `expectedVersion` is omitted; a versioned write for a missing row is `NOT_FOUND` and does not recreate. | `match` equals one declared `uniqueIndexes` tuple exactly, in order. Every matched field is a Schema property, is declared in `input.properties`, and appears in `input.required`. `input` must **not** declare `id`. `expectedVersion` **must** be declared as strict `number`; it is not globally required so create can omit it. |
132
- | `upsert` without `match` | Id-based upsert. Create when the caller omits `expectedVersion` (and either omits `id` or the id is unknown). Update when a resolved `id` is present — the caller token is required and is the OCC check. A versioned write for a missing id is `NOT_FOUND`. | `expectedVersion` must be declared as strict `number`. If `id` is declared it must be strict `string`. Neither is in `required`. |
133
- | `delete` | Loads the row (`NOT_FOUND` if absent), runs the delete guard, then hard-deletes pinned to the row's status and version. Returns `{ removed }`. | `id` (strict `type: string`) declared and in `required`. |
134
- | `archive` | Loads the row, checks the lifecycle state machine (`CONFLICT` on an illegal transition), then transitions to `archived` pinned to the version just read. | `id` (strict `type: string`) declared and in `required`. The target Schema must be `lifecycle: publishing`; an operational target is rejected. |
135
-
136
- Every contract violation in the right-hand column is `BUILTIN_HANDLER_CONTRACT_INVALID`, reported at the offending pointer. *Strict* means a single scalar type — an array-valued `type` or `nullable: true` does not satisfy it.
137
-
138
- All five ops write through the same chokepoint, which validates the projected `data` against the Schema, runs the [write-time locale gate](./schema.md#write-time-locale-gate) and performs a unique-index preflight before the write.
139
-
140
- ### Side-channel input fields
141
-
142
- `input` is the contract with the *caller*, not with the Schema. It may declare fields the collection has no column for — a CAPTCHA token, an idempotency key, a routing hint. JSON Schema's default `additionalProperties: true` lets them validate, and the builtin op projects `input ∩ Schema.properties`, so they never reach `data`.
143
-
144
- They are not lost. The pre-projection input travels to the chokepoint as `originalInput`, and synchronous `before_*` lifecycle hooks receive it as their handler input. A `before_create` hook can therefore verify a token the row never stores. See [Trigger](./trigger.md#lifecycle-source).
145
-
146
- ### Builtin `upsert` example
147
-
148
- ```yaml
149
- apiVersion: cms.mantle.aotter.net/v1
150
- kind: Procedure
151
- metadata:
152
- name: sync-inventory-level
153
- spec:
154
- title: Sync inventory level
155
- description: Insert or update the stock level for one SKU in one warehouse.
156
- requires:
157
- auth:
158
- all:
159
- - ctx.auth
160
- - { "ctx.auth.scope": "inventory:write" }
161
- input:
162
- type: object
163
- required: [sku, warehouse, onHand]
164
- properties:
165
- sku: { type: string, minLength: 1 }
166
- warehouse: { type: string, minLength: 1 }
167
- onHand: { type: integer, minimum: 0 }
168
- countedAt: { type: integer, x-mcp-hint: timestamp-ms }
169
- requestId: { type: string, x-mcp-hint: idempotency-key }
170
- expectedVersion: { type: number }
171
- output:
172
- type: object
173
- required: [id, version]
174
- properties:
175
- id: { type: string }
176
- version: { type: number }
177
- handler:
178
- kind: builtin
179
- op: upsert
180
- schema: inventory-levels
181
- match: [sku, warehouse]
182
- ```
183
-
184
- This requires `inventory-levels` to declare `uniqueIndexes: [[sku, warehouse]]` — the same fields, in the same order. `requestId` is a side-channel field: it validates, reaches `before_*` hooks, and is never written to `data`. `expectedVersion` is the observed native `entry.version` at read time (not `version+1`). Omit it to create; send it to update. First-party Admin binds and hides it; HTTP and MCP callers supply it themselves.
185
-
186
- ## The response shape
187
-
188
- Every builtin op except `delete` returns the persisted `EntryRow`.
189
-
190
- | Field | Type | Notes |
191
- |---|---|---|
192
- | `id` | string | Generated on create. |
193
- | `collection` | string | The Schema's `metadata.name`. |
194
- | `status` | `draft` \| `published` \| `archived` | |
195
- | `version` | number | Optimistic-concurrency counter; bumps on every persisted update. |
196
- | `data` | object | The projected, stamped Schema fields. |
197
- | `authorId` | string \| null | |
198
- | `createdAt`, `updatedAt` | number | Unix epoch milliseconds. |
199
- | `locale` | string | Present only when the row carries `data.locale`. |
200
-
201
- An HTTP Trigger wraps a success as `{ "ok": true, "data": <EntryRow> }` with status 200.
202
-
203
- > **Warning**
204
- > Declare `output` against the **row**, not the envelope. `output: { type: object, required: [id], properties: { id: { type: string } } }` checks that an id came back. Output validation does not strip the other fields, so a caller still receives the whole row; use a `ref` handler with an explicit projection when the response must be smaller.
205
-
206
- `delete` returns `{ removed: boolean }` instead.
207
-
208
- ## Conflicts and idempotency
209
-
210
- A unique-index preflight runs before individual writes, and the database's own constraints catch the races the preflight misses. Atomic groups rely on the transaction's database constraints so an earlier operation may free a unique value for a later one. Both paths surface collisions as `CONFLICT` (409), as do a stale `expectedVersion` and an illegal lifecycle transition. **There is no automatic retry** — the caller decides whether to re-read and try again.
211
-
212
- Idempotency has no grammar key. The convention is an `input` property marked `x-mcp-hint: idempotency-key`: Admin generates and hides one UUID per form submission, and other callers generate one and reuse it across retries of the same logical request. The handler is responsible for acting on it.
213
-
214
- Optimistic concurrency uses the reserved input name `expectedVersion` — the version the caller **read**, not the next version. First-party Admin and SDK bind-and-hide that property from the OCC target row; other callers send it themselves. There is no `x-mcp-hint` for OCC. On `CONFLICT` (409) Admin keeps the operator's business fields and requires an explicit re-read; it does not retry with the latest version. New reserved Procedure input names need an ADR.
215
-
216
- Deferred lifecycle hooks have a stronger guarantee to work with: delivery is at-least-once, and handlers key on `${ctx.event.id}:${ctx.event.trigger}` — stable across enqueue fallback, queue retries and replay. See [Deferred hooks on Queues](../cloudflare/deferred-hooks-queues.md).
217
-
218
- ## Atomic entry writes in a ref handler
219
-
220
- When one request must change several Schemas together, a `ref` handler can call
221
- `ctx.writeAtomically(operations)`. Cloudflare D1 and Bun SQLite support it; an
222
- adapter without the optional `atomicEntries` capability returns
223
- `RESOURCE_UNAVAILABLE` (503)
224
- instead of committing part of the group. Operations use the `createDraft`,
225
- `updateDraft`, or `deleteEntry` request shape; atomic deletes additionally
226
- require `expectedVersion`. Operational Schemas become live on create;
227
- publishing Schemas create drafts. Updates and deletes require the version the
228
- caller read.
229
-
230
- ```ts
231
- const sessionId = crypto.randomUUID();
232
- const rows = await ctx.writeAtomically!([
233
- { kind: "create", id: sessionId, request: {
234
- collection: "sessions", data: { name: input.name }, authorId: ctx.user?.id ?? null, ctx,
235
- } },
236
- { kind: "create", request: {
237
- collection: "exercise-blocks", data: { sessionId, exercise: input.exercise },
238
- authorId: ctx.user?.id ?? null, ctx,
239
- } },
240
- { kind: "create", request: {
241
- collection: "receipts", data: { token: input.requestId }, authorId: ctx.user?.id ?? null, ctx,
242
- } },
243
- ]);
244
- return { sessionId: rows[0]!.id };
245
- ```
246
-
247
- Declare `receipts.token` as a Schema `uniqueIndexes: [[token]]`. A duplicate
248
- receipt rejects the whole group, including the session and block. The handler
249
- can then read the existing receipt **after** the failed transaction to answer
250
- an idempotent retry. A stale update or delete rejects the whole group even
251
- when it is the final operation. The group may touch each entry only once;
252
- read and authorization decisions happen before the batch, and the expected
253
- version/status is checked again by the conditional database write.
254
-
255
- All Schema projection, stamping, validation, uniqueness, and lifecycle rules
256
- still apply. `before_*` hooks run in operation order before the batch and can
257
- veto it; their external effects cannot be rolled back. `after_*` hooks run
258
- only after commit, in operation order. Publishing-content invalidation runs
259
- once for the group. Deferred Queue delivery is separate from the database
260
- transaction. Application-owned tables can use their host's transaction
261
- facility inside a ref handler, but that does not give those tables Mantle
262
- entry semantics. Direct SQL writes to Mantle Schema tables are unsupported.
263
- Authorization guard Procedures do not receive `ctx.writeAtomically`.
264
-
265
- ## TTL sweep in a ref handler
266
-
267
- `ctx.sweepExpired({ collection, limit })` previews a bounded page of expired rows; `delete: true` explicitly removes it. The result contains `scanned`, `removed` and an optional `nextCursor`. Continue with that cursor until absent. D1 and Bun SQLite implement this semantic capability; unsupported storage returns `RESOURCE_UNAVAILABLE`. Authorization guard Procedures do not receive the sweep function. For a scheduled cleanup, declare a [schedule Trigger](./trigger.md#schedule-source) targeting a no-input ref Procedure. No sweep is scheduled automatically. See [Schema TTL](./schema.md#ttl).
268
-
269
- ## `uiSchema`
270
-
271
- Admin presentation only. It never affects input validation, the MCP tool schema or the OpenAPI document. Roots are closed: `collectionAction` and `fields`.
272
-
273
- | Key | Rule |
274
- |---|---|
275
- | `collectionAction` | A declared Schema name. Admin offers the Procedure as an action on that collection's list page. A non-empty string that names no Schema is `SCHEMA_UI_INVALID`; a Schema that declares `collectionAction` is rejected outright. |
276
- | `fields.<field>.widget` | Only `textarea`. `<field>` must be a top-level property of `input` with a string type. Anything else is `SCHEMA_UI_INVALID`. |
277
-
278
- ## Staff operations in Admin
279
-
280
- Admin derives its operations surface from the manifest graph — there is no extra grammar. A Procedure is staff-operable when **either** condition holds:
281
-
282
- 1. Some Trigger targets it with `source.kind: mcp` and `source.surface: staff` — the same predicate that builds the `/mcp/staff` tool catalog.
283
- 2. Some Trigger targets it with `source.kind: http` **and** the Procedure's `requires.auth.all` includes a `ctx.staff` predicate.
284
-
285
- | Endpoint | Behavior |
286
- |---|---|
287
- | `GET /admin/api/operations` | Lists the staff-operable Procedures the calling staff member may actually run. |
288
- | `POST /admin/api/operations/:name` | Invokes one, through the same pipeline as any other caller. |
289
-
290
- Each listed operation carries `name`, `title`, `description`, `input`, `uiSchema`, `triggers` (the distinct kinds that qualified it, so a Procedure can be both), `rowBindings`, and `targetCollection` (the builtin handler schema, or `null`).
291
-
292
- `rowBindings` come from `x-mantle-ref` on the Procedure's input properties. An input property referencing a declared, non-`translates` Schema produces `{ collection, inputField, rowField }`, and Admin offers the operation from that collection's row menu with the field pre-filled and read-only. `rowField` is the target Schema's same-named property when it has one, otherwise the lone field of a single single-field unique index, otherwise the reserved `id` column. Refs to unknown collections or to translation children produce no binding and no error.
293
-
294
- When `input` declares `expectedVersion`, Admin treats that reserved name as magic: it captures the OCC target's current `version` at read time, submits it, and does not render an editable version field. Changing the selected target rebinds version (an organization row must not supply a membership mutation's version). A resolvable OCC target or a row-bound dialog must have that captured version before Run is enabled, including matched upsert where the field is declared but not globally required. If `expectedVersion` is in `input.required` and no target can be resolved, submit stays disabled. Collection create / no-row dialogs may omit it when it is not required. Builtin operations also expose `targetCollection` (the handler schema) so Admin can pick the mutated collection over a contextual parent.
295
-
296
- Worked end-to-end examples live in [Commerce inventory](../../examples/cf-primitives-commerce-inventory.md), [Commerce catalog](../../examples/builtin-commerce.md), and [Procurement approvals](../../examples/builtin-procurement.md).
297
-
298
- ## Source
299
-
300
- - [`packages/mantle-spec/src/domain/model/ManifestGrammar.ts`](../../../packages/mantle-spec/src/domain/model/ManifestGrammar.ts)
301
- - [`packages/mantle-spec/src/domain/service/ManifestParser.ts`](../../../packages/mantle-spec/src/domain/service/ManifestParser.ts)
302
- - [`packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts`](../../../packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts)
303
- - [`packages/mantle-spec/src/domain/service/SchemaAdminUiChecker.ts`](../../../packages/mantle-spec/src/domain/service/SchemaAdminUiChecker.ts)
304
- - [`packages/mantle-runtime/src/usecase/procedure/InvokeProcedureUseCase.ts`](../../../packages/mantle-runtime/src/usecase/procedure/InvokeProcedureUseCase.ts)
305
- - [`packages/mantle-runtime/src/usecase/procedure/InvokeBuiltinUseCase.ts`](../../../packages/mantle-runtime/src/usecase/procedure/InvokeBuiltinUseCase.ts)
306
- - [`docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md`](../../adr/0020-builtin-handler-contracts-and-matched-upsert.md)
307
- - [`docs/adr/0022-caller-observed-version-occ.md`](../../adr/0022-caller-observed-version-occ.md)
308
- - [`packages/mantle-runtime/src/domain/service/BuiltinProjector.ts`](../../../packages/mantle-runtime/src/domain/service/BuiltinProjector.ts)
309
- - [`packages/mantle-runtime/src/domain/model/EntryRow.ts`](../../../packages/mantle-runtime/src/domain/model/EntryRow.ts)
310
- - [`packages/mantle-runtime/src/domain/service/io/EntryWriteGuard.ts`](../../../packages/mantle-runtime/src/domain/service/io/EntryWriteGuard.ts)
311
- - [`packages/mantle-runtime/src/domain/service/io/EntryDeleteGuard.ts`](../../../packages/mantle-runtime/src/domain/service/io/EntryDeleteGuard.ts)
312
- - [`packages/mantle-runtime/src/domain/service/CallableCapabilityProjector.ts`](../../../packages/mantle-runtime/src/domain/service/CallableCapabilityProjector.ts)
313
- - [`packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts`](../../../packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts)
314
- - [`packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts`](../../../packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts)
315
- - [`packages/mantle-admin/src/mountMantleAdmin.ts`](../../../packages/mantle-admin/src/mountMantleAdmin.ts)
23
+ Triggers are the only way request data reaches a Procedure: REST and MCP validate the input against the Procedure's schema, then call it. App code may also call a generated Procedure directly (`import run from "../generated/procedures/<name>.js"`, `await run(db, caller, input)`) or through the Store (`ctx.store.procedures[name](input)` in a handler); `requires`, row policy and checks still apply, but input validation does not, so pass only data the app produced or has verified (a cron job, a callback after its signature check). A page that takes user input submits it to the Procedure's REST Trigger; see `node_modules/@aotter/mantle/docs/examples/purchasing/src/requests-page.ts`.