@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,213 +1,77 @@
1
- ---
2
- description: Trigger field reference — http, mcp, lifecycle and schedule sources, routing rules, and delivery semantics.
3
- ---
4
1
  # Trigger
5
2
 
6
- A Trigger binds one source to one [Procedure](./procedure.md). Every external surface for a write — an HTTP endpoint, an MCP tool, an entry-lifecycle hook — is a Trigger, and there is no other way to expose a Procedure. 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).
7
-
8
- ## Fields
9
-
10
- `spec` accepts exactly two keys.
11
-
12
- | Field | Type | Required | Rules |
13
- |---|---|---|---|
14
- | `source` | mapping | yes | Discriminated by `kind`; the accepted sibling keys depend on it. |
15
- | `target` | `{ procedure }` | yes | Only the key `procedure`, naming a declared Procedure (`TRIGGER_TARGET_PROCEDURE_UNKNOWN`). |
16
-
17
- | `source.kind` | Other keys | Binds |
18
- |---|---|---|
19
- | `http` | `method`, `path` | One REST endpoint under `/api/`. |
20
- | `mcp` | `surface` | One tool on `/mcp` or `/mcp/staff`. |
21
- | `lifecycle` | `schema`, `on`, `errorPolicy` | Entry-writer hooks on one Schema. |
22
- | `schedule` | `cron`, optional `enabled` | A Cloudflare Cron Trigger calling one Procedure. |
23
-
24
- An unknown `kind`, a missing `kind`, or a key that does not belong to the chosen kind is `INVALID_MANIFEST_ENVELOPE`. One Procedure may carry several Triggers — that is how the same handler becomes an HTTP endpoint and an MCP tool without duplicating logic.
25
-
26
- ## `schedule` source
27
-
28
3
  ```yaml
29
- apiVersion: cms.mantle.aotter.net/v1
4
+ apiVersion: cms.mantle.aotter.net/v2
5
+ kind: Schema
6
+ metadata: { name: tickets }
7
+ spec:
8
+ title: Tickets
9
+ lifecycle: operational
10
+ schema: { type: object, required: [closed], properties: { closed: { type: boolean } } }
11
+ ---
12
+ apiVersion: cms.mantle.aotter.net/v2
13
+ kind: Procedure
14
+ metadata: { name: close-ticket }
15
+ spec:
16
+ requires: { auth: { all: [ctx.admin] } }
17
+ input: { type: object, required: [id, version], properties: { id: { type: string }, version: { type: integer } }, additionalProperties: false }
18
+ output: { type: object }
19
+ handler: { sql: 'UPDATE tickets SET closed=true WHERE id=input.id AND version=input.version RETURNING id,closed' }
20
+ ---
21
+ apiVersion: cms.mantle.aotter.net/v2
30
22
  kind: Trigger
31
- metadata: { name: nightly-cleanup }
23
+ metadata: { name: close-ticket-http }
32
24
  spec:
33
- source: { kind: schedule, cron: "0 2 * * *" }
34
- target: { procedure: clean-expired-records }
35
- ```
36
-
37
- `cron` is a five-field Cloudflare UTC expression (minute, hour, day, month, weekday). Each field accepts `*`, a number, a range, comma-separated values, or `*/step` / `start-end/step`. Weekdays are **1 = Sunday through 7 = Saturday**; use `2-6` for Monday–Friday. Month and weekday names and provider-specific extensions are outside this subset. `enabled` defaults to `true`; a disabled schedule remains in the compiled plan but never invokes its Procedure. The target Procedure must accept `{}` as input (`SCHEDULE_INPUT_INVALID`) and have no user/staff authorization requirements (`SCHEDULE_AUTH_INVALID`). The schedule creates no public HTTP or MCP route.
38
-
39
- On Cloudflare, add each enabled expression to `wrangler.jsonc` under `triggers.crons` and export the `scheduled` method returned by `createMantleWorker`. Mantle does not provision the provider trigger. A disabled expression still registered in Wrangler is ignored; an expression absent from the Mantle plan fails visibly. If the Worker also owns unrelated cron tasks, route those expressions in the application's `scheduled` export. Multiple Mantle Triggers may share one expression and run in Trigger-name order; all run even if an earlier one fails, then the event reports failure.
40
-
41
- Handlers receive `ctx.schedule` with `id`, `trigger`, `cron`, and `scheduledTime` (Unix milliseconds). The stable ID is `${trigger}:${scheduledTime}`. The caller is always `user: null`, `staff: null`, with no credential metadata; normal Procedure guards still apply. Cloudflare executes cron events in UTC, but its Cron Trigger documentation does not promise automatic retries. Duplicate/manual replay is still possible, so use `ctx.schedule.id` as an idempotency key for writes. A failure rejects the scheduled event; exactly-once execution is not promised. Bun, Vercel, and ChatGPT Sites do not register schedules and reject enabled schedules at boot or generation.
42
-
43
- Cloudflare's default D1 composition records each attempt under that stable run ID. The owner-only Developer Console shows declared schedules even before the first run, separately from recent observations; Wrangler registration is **not observed** by Core. Records include scheduled/start/finish times, status, attempt, duration, a diagnostic code or generic handler-error summary, and optional sweep counts. A started attempt with no finish record says **Completion not observed**, not success. Inputs, outputs, exception text, credentials and secrets are not stored. The default history retains 30 days, hides older rows on reads, and prunes them during later runs (at most once per day per runtime instance). If observation start fails, the Procedure is skipped and the scheduled event rejects; if recording completion fails, the event also rejects. Use the stable ID to make replay safe. If the host supplies custom storage without a durable observation store, the Console says **Unavailable**; it never reports a missing record as success.
44
-
45
- ## `http` source
46
-
47
- ```yaml
48
- apiVersion: cms.mantle.aotter.net/v1
25
+ source: { kind: http, method: POST, path: '/api/tickets/{id}/close' }
26
+ target: { procedure: close-ticket }
27
+ ---
28
+ apiVersion: cms.mantle.aotter.net/v2
49
29
  kind: Trigger
50
- metadata:
51
- name: inventory-level-http
30
+ metadata: { name: close-ticket-mcp }
52
31
  spec:
53
- source:
54
- kind: http
55
- method: PUT
56
- path: /api/inventory/{sku}/level
57
- target:
58
- procedure: sync-inventory-level
32
+ source: { kind: mcp, surface: admin }
33
+ target: { procedure: close-ticket }
59
34
  ```
60
35
 
61
- | Field | Rules |
62
- |---|---|
63
- | `method` | `POST`, `PUT`, `PATCH` or `DELETE`. |
64
- | `path` | Non-empty string starting with `/`. OpenAPI `{param}` syntax for path params. No optional segments. |
65
-
66
- `GET` is deliberately absent. Reads are [Views](./view.md), which mount themselves from `surface` and need no Trigger at all; a Procedure is a write.
67
-
68
- ### Path rules
36
+ The target names a declared Procedure. `http` sources use POST/PUT/PATCH/DELETE and an `/api/` path. Path parameters bind to corresponding declared input fields and override JSON-body values. The generated REST module retains the Procedure's authorization and validates boundary input.
69
37
 
70
- | Phase | Rule | Diagnostic |
71
- |---|---|---|
72
- | parse | `path` starts with `/`. | `INVALID_MANIFEST_ENVELOPE` |
73
- | validate | `path` starts with `/api/`, so adapters can route public pages and Procedure endpoints without ambiguity. | `TRIGGER_PATH_INVALID` |
74
- | validate | `(method, path)` is unique across every `http` Trigger. | `TRIGGER_PATH_COLLISION`, naming the Trigger that claimed it first. |
75
- | boot | `path` falls outside the adapter's reserved prefixes. | `TRIGGER_PATH_INVALID` |
38
+ An MCP source is `{ kind: mcp, surface: public }` or admin. The selected MCP module exposes the Procedure as a snake-case tool with its description, [tool hints](procedure.md#mcp-tool-hints) and requires checks.
76
39
 
77
- Only well-prefixed paths are tracked for collisions, so a path missing `/api/` produces one diagnostic rather than two. The Cloudflare Worker reserves `/admin`, `/_mantle`, `/api/auth`, `/api/views`, `/oauth`, `/mcp`, anything starting `/.well-known/oauth`, and the exact registrations `*` and `/*`; a prefix matches the path itself or a `/` or `{` boundary after it. See [Conventional Worker](../cloudflare/conventional-worker.md).
40
+ By default, pair every HTTP Trigger with an MCP Trigger so the same operation is reachable by the user's AI. `mantle generate` warns when a Procedure has HTTP exposure but no MCP exposure, suggesting `surface: admin` for an admin-only Procedure and `public` otherwise. Triggers still decide every Procedure surface: intentional asymmetric exposure is allowed and generation succeeds.
78
41
 
79
- ### Routing and binding
42
+ Lifecycle and schedule sources are rejected at generation with `GENERATE_UNSUPPORTED`, including disabled schedules. Use an app-owned handler or scheduler. For signed webhooks, verify the original raw Request in the app and call a generated Procedure with validated input; do not route it through a JSON boundary before signature verification.
80
43
 
81
- | Behavior | Detail |
82
- |---|---|
83
- | Path params | Each `{param}` binds to the identically named field on the target Procedure's `input`, which must declare it. |
84
- | Precedence | The invocation merges the body first and the path params second, so **the path wins** over a same-named body field. |
85
- | Trailing slash | `/api/posts` and `/api/posts/` are the same route; the root `/` is preserved. |
86
- | Percent-encoding | Each request segment is decoded once, so `/api/by%2Dtag` matches the literal `/api/by-tag`. Malformed encoding such as `%GG` is a routing miss (404), not a 500. |
87
- | Body | Must be a JSON object. Anything else is `INPUT_VALIDATION_FAILED` (400) with *HTTP Trigger request body must be a JSON object*. A body over 1 MiB is the same code at 413. |
88
- | Empty body | A `DELETE`, or any request without a JSON content type, is treated as `{}` — bind those inputs through path params. |
44
+ ## Input rules
89
45
 
90
- A success is `{ "ok": true, "data": <handler result> }`; a failure is `{ "ok": false, "diagnostic": ... }` at the diagnostic's mapped status. `http` Triggers are also what the OpenAPI emitter projects into operations.
91
-
92
- ## `mcp` source
46
+ The admin MCP tool below lists `reason` and `closed` without the rules, and its
47
+ description ends "'reason' is required when 'closed' is false."; a call with
48
+ `closed: false` and no `reason` fails with `INPUT_VALIDATION_FAILED` at
49
+ `procedures/set-ticket/input/reason` (the error path is
50
+ `procedures/<name>/input/<field>`). See [surface contracts](surface.md) for the
51
+ supported rules.
93
52
 
94
53
  ```yaml
95
- apiVersion: cms.mantle.aotter.net/v1
96
- kind: Trigger
97
- metadata:
98
- name: approve-purchase-order-mcp
54
+ apiVersion: cms.mantle.aotter.net/v2
55
+ kind: Procedure
56
+ metadata: { name: set-ticket }
99
57
  spec:
100
- source:
101
- kind: mcp
102
- surface: staff
103
- target:
104
- procedure: approve-purchase-order
105
- ```
106
-
107
- | `surface` | Endpoint | Gate |
108
- |---|---|---|
109
- | `public` | `/mcp` | Bearer token. |
110
- | `staff` | `/mcp/staff` | Bearer token plus a staff role read from storage on every invocation. |
111
-
112
- `surface` is **discovery only**. It decides which tools appear in `tools/list` on which endpoint; it authorizes nothing. The target Procedure's `requires.auth.all` predicates and its optional guard are re-evaluated on every `tools/call` against the authenticated caller, exactly as they are over HTTP. A `public`-surface Procedure that requires `ctx.staff` is discoverable on `/mcp` and will still be denied there.
113
-
114
- The tool name is derived from the **Procedure's** `metadata.name`, not the Trigger's: lower-cased, with `-` replaced by `_`. Only one Trigger may claim a given `(surface, tool name)` pair; a second is `MCP_TOOL_NAME_COLLISION`. The same code also fires when the mangled name hits a reserved generic tool name or prefix, or a Schema's or another Procedure's segment — see [Reserved names](./manifest.md#reserved-names). A Procedure exposed this way should carry `spec.description`; a missing one is the `MCP_TOOL_DESCRIPTION_MISSING` warning, because the catalog's generated fallback tells an agent nothing about when to call the tool. A write tool that requires `expectedVersion` also needs a View on the same surface that exposes that collection's `version`; otherwise the tool is listed but uncallable, and validation warns with `MCP_TOOL_INPUT_UNREACHABLE`.
115
-
116
- The tool carries the Procedure's `title` and `description`, with a short authorization summary appended to the description. `output` is not surfaced; MCP clients infer the response shape from the `tools/call` result. See [MCP and agents](../concepts/mcp-and-agents.md).
117
-
118
- ## `lifecycle` source
119
-
120
- ```yaml
121
- apiVersion: cms.mantle.aotter.net/v1
58
+ description: Open or close a ticket.
59
+ input:
60
+ type: object
61
+ required: [id, closed]
62
+ properties: { id: { type: string }, closed: { type: boolean }, reason: { type: string, maxLength: 200 } }
63
+ additionalProperties: false
64
+ if: { properties: { closed: { const: false } }, required: [closed] }
65
+ then: { required: [reason] }
66
+ output: { type: object }
67
+ handler: { sql: 'UPDATE tickets SET closed=input.closed WHERE id=input.id RETURNING id,closed' }
68
+ ---
69
+ apiVersion: cms.mantle.aotter.net/v2
122
70
  kind: Trigger
123
- metadata:
124
- name: 010-verify-purchase-token
71
+ metadata: { name: set-ticket-mcp }
125
72
  spec:
126
- source:
127
- kind: lifecycle
128
- schema: purchase-orders
129
- on: [before_create]
130
- errorPolicy: abort
131
- target:
132
- procedure: verify-purchase-token
73
+ source: { kind: mcp, surface: admin }
74
+ target: { procedure: set-ticket }
133
75
  ```
134
76
 
135
- | Field | Type | Required | Default | Rules |
136
- |---|---|---|---|---|
137
- | `schema` | string | yes | — | A declared Schema (`LIFECYCLE_SCHEMA_UNKNOWN`). |
138
- | `on` | `LifecycleHook[]` | yes | — | Non-empty; every entry from the closed list below. |
139
- | `errorPolicy` | `abort` \| `continue` | no | `abort` for `before_*`, `continue` for `after_*` | See below. |
140
-
141
- `errorPolicy: abort` is rejected at parse time when **any** `after_*` hook appears in `on`: an `after_*` hook runs once the response has already been sent, so an abort could never reach the caller. Split the `after_*` hooks into their own Trigger, or declare `continue`.
142
-
143
- > **Info**
144
- > `Schema.spec.lifecycle` (`publishing` / `operational`) is a different domain that shares the word. That setting governs which states an entry may be in; a lifecycle Trigger governs what fires around a mutation. See [Schema](./schema.md#lifecycle).
145
-
146
- ### Hooks
147
-
148
- | Hook | Fires |
149
- |---|---|
150
- | `before_create` | Before the insert. |
151
- | `after_create` | After the insert. |
152
- | `before_update` | Before an update **or** any status transition whose target is not `published` — this includes unpublish and archive. |
153
- | `after_update` | After an update or such a transition. |
154
- | `before_delete` | Before the delete. |
155
- | `after_delete` | After the delete, only when a row was actually removed. |
156
- | `before_publish` | Before a transition to `published`. |
157
- | `after_publish` | After a transition to `published`. |
158
-
159
- There are no unpublish-specific or archive-specific hooks. Do not read `before_update` / `after_update` as edit-only.
160
-
161
- ### Error policy
162
-
163
- | Phase | Default | Behavior |
164
- |---|---|---|
165
- | `before_*` | `abort` | A throwing hook cancels the surrounding mutation and the caller receives the hook's own diagnostic. A hook that rejects a write on purpose raises `LIFECYCLE_HOOK_REJECTED` (409). Under `continue` the failure is logged and the mutation proceeds. |
166
- | `after_*` | `continue` | The committed mutation stands. On the inline or `waitUntil` path a failure is logged and swallowed. With a deferred dispatcher wired in, the failure reaches the delivery adapter so its at-least-once retry and dead-letter policy can run. Neither path ever rolls back. |
167
-
168
- ### Handler input and `ctx.event`
169
-
170
- Hook input is phase-specific.
171
-
172
- | Phase | Handler input |
173
- |---|---|
174
- | `before_*` | The original **pre-projection** Procedure input, so a hook can read side-channel fields the row never stores — a CAPTCHA token, a client nonce. It falls back to the row's `data` when there is no caller input. |
175
- | `after_*` | The persisted `entry.data` only. Deferred envelopes deliberately never carry arbitrary request input. |
176
-
177
- Every hook handler also receives `ctx.event`:
178
-
179
- | Field | Value |
180
- |---|---|
181
- | `id` | Stable event id, unchanged across enqueue fallback and deferred retries. |
182
- | `trigger` | The firing Trigger's `metadata.name`. |
183
- | `hook` | The hook name. |
184
- | `schema` | The watched Schema name. |
185
- | `entry` | `null` only on `before_create`; the pre-mutation row for the other `before_*` hooks; the persisted post-mutation row for every `after_*`. |
186
-
187
- Deferred handlers key on `${ctx.event.id}:${ctx.event.trigger}`. See [Procedure](./procedure.md#conflicts-and-idempotency).
188
-
189
- ### Ordering and coverage
190
-
191
- When several lifecycle Triggers bind the same `(schema, hook)`, they fire **alphabetically by `Trigger.metadata.name`**. Choose names that sort the way you want them to run — the `010-`, `020-` convention exists for exactly this, and the code generator handles the leading digits.
192
-
193
- Hooks are wired through a repository decorator that wraps the single entry-writer chokepoint, so **Staff MCP, Admin and builtin Procedure writes all fire the same hooks**. There is no write path that bypasses them.
194
-
195
- For deferred `after_*` delivery, the ordered Trigger-name list is captured into one versioned envelope carrying the persisted row and a small identity snapshot. Every captured Trigger runs before a failure is reported back, so a retry may replay Triggers that already succeeded — hence the idempotency key. Queue acceptance is not transactional with the entry write, the `waitUntil` fallback is best-effort, and exactly-once is not promised. See [Deferred hooks on Queues](../cloudflare/deferred-hooks-queues.md).
196
-
197
- ## Source
198
-
199
- - [`packages/mantle-spec/src/domain/model/ManifestGrammar.ts`](../../../packages/mantle-spec/src/domain/model/ManifestGrammar.ts)
200
- - [`packages/mantle-spec/src/domain/service/ManifestParser.ts`](../../../packages/mantle-spec/src/domain/service/ManifestParser.ts)
201
- - [`packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts`](../../../packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts)
202
- - [`packages/mantle-spec/src/domain/service/McpToolNaming.ts`](../../../packages/mantle-spec/src/domain/service/McpToolNaming.ts)
203
- - [`packages/mantle-runtime/src/domain/service/PathMatcher.ts`](../../../packages/mantle-runtime/src/domain/service/PathMatcher.ts)
204
- - [`packages/mantle-runtime/src/domain/service/TriggerIndex.ts`](../../../packages/mantle-runtime/src/domain/service/TriggerIndex.ts)
205
- - [`packages/mantle-runtime/src/usecase/lifecycle/RunLifecycleHooksUseCase.ts`](../../../packages/mantle-runtime/src/usecase/lifecycle/RunLifecycleHooksUseCase.ts)
206
- - [`packages/mantle-runtime/src/infrastructure/persistence/LifecycleHookingEntryRepository.ts`](../../../packages/mantle-runtime/src/infrastructure/persistence/LifecycleHookingEntryRepository.ts)
207
- - [`packages/mantle-runtime/src/domain/port/DeferredHookDispatcher.ts`](../../../packages/mantle-runtime/src/domain/port/DeferredHookDispatcher.ts)
208
- - [`packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts`](../../../packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts)
209
- - [`packages/mantle-runtime/src/infrastructure/http/readJsonBody.ts`](../../../packages/mantle-runtime/src/infrastructure/http/readJsonBody.ts)
210
- - [`packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts`](../../../packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts)
211
- - [`packages/mantle/src/codegen/emitMantleModule.ts`](../../../packages/mantle/src/codegen/emitMantleModule.ts)
212
- - [`packages/adapters/cloudflare/src/worker/createMantleWorker.ts`](../../../packages/adapters/cloudflare/src/worker/createMantleWorker.ts)
213
- - [`packages/adapters/cloudflare/src/mount/mountMcp.ts`](../../../packages/adapters/cloudflare/src/mount/mountMcp.ts)
77
+ See [surfaces](surface.md) for mounted paths, error mapping and origin checks.
@@ -1,263 +1,44 @@
1
- ---
2
- description: View field reference — declarative and SQL forms, filter AST, params, pagination, REST and MCP surfaces, and every diagnostic they raise.
3
- ---
4
1
  # View
5
2
 
6
- A View is a named read-only query over Schemas. It is the only atom that needs no [Trigger](./trigger.md): `surface: public` or `staff` exposes it on supported transports; `internal` keeps it host-only. This page is the field-level contract; the concepts are in [Views](../concepts/views.md) and [The four atoms](../concepts/four-atoms.md). Envelope rules are in [Manifest envelope and conventions](./manifest.md), and every diagnostic code named here is catalogued in [Diagnostics](./diagnostics.md).
3
+ A View requires `surface: public | admin | internal` and one `sql` SELECT. Optional keys include title, description, input, requires, restrict, uiSchema (admin only), and cache. The app imports its generated typed function and supplies a native handle, verified Caller and driver-ready input; a JSON input (an array read by `json_each(input.ids)`) takes its authored array or object, which the generated query serializes.
7
4
 
8
- ## Fields
5
+ ## SQL
9
6
 
10
- | Field | Type | Required | Default | Rules |
11
- |---|---|---|---|---|
12
- | `title` | LocalizedText | no | Title-Cased `metadata.name` | Admin report label. Non-empty string or locale map. |
13
- | `uiSchema` | object | no | — | Only on `surface: staff`; only the key `list`. Violations are `VIEW_UI_INVALID`. |
14
- | `from` | string | exactly one of `from` / `sql` | — | Name of a declared Schema (`VIEW_FROM_UNKNOWN_SCHEMA`). The declarative form. |
15
- | `sql` | string | exactly one of `from` / `sql` | — | One SQLite `SELECT`. See [`sql`](#sql). |
16
- | `surface` | `public` \| `staff` \| `internal` | yes | — | Decides where the View mounts. See [Surfaces](#surfaces). |
17
- | `cache` | `{ sharedMaxAge }` | no | — | Anonymous REST shared-cache hint. `sharedMaxAge` is an integer from 1 to 86400. Only an unguarded, declarative public View over a publishing Schema may declare it. |
18
- | `requires` | AuthorizationRequirements | no | — | `auth.all` predicates plus one optional `guard.procedure`. See [Authorization](./authorization.md). |
19
- | `filter` | FilterAst | no | — | `from` form only. See [Filter AST](#filter-ast). |
20
- | `fields` | `string[]` | no | every column | `from` form only. Projection. Not shape-validated by the parser. |
21
- | `orderBy` | `{ field, direction? }[]` | no | `[]` | `from` form only. `direction` defaults to `asc`. |
22
- | `limit` | number | no | 50 at runtime | `from` and `sql`. Not shape-validated by the parser; clamped at request time. |
23
- | `params` | JSON Schema | no | — | `type: object` with `properties`. Reserved: `page`, `show`, `cursor`. |
7
+ PostgreSQL syntax is parsed and checked at generation. Reads may reference declared Schemas, CTEs and reusable internal Views; no auth/system tables or arbitrary external relations. The compiler adds scope, TTL, publication and translation-parent visibility at every relation. SQL policy is not injected at request time.
24
8
 
25
- `from` counts as present when it is a non-empty string; `sql` when it is non-empty after trimming. Declaring both, or neither, is `INVALID_MANIFEST_ENVELOPE` at `/spec` with the message *View.spec requires exactly one of from or sql*. Combining `sql` with `filter`, `fields` or `orderBy` is rejected the same way at `/spec/<key>`.
9
+ The SQL each dialect accepts, for Views and Procedures alike, is listed in [the SQL reference](sql.md); [SQL patterns](../guides/sql-patterns.md) shows the common ones. Unsupported SQL fails with a source position; generation is the authority for each target.
26
10
 
27
- ## Declarative example
11
+ `*` and `t.*` expand to the Schema's declared fields only. Select `id`, `version`, `created_at` and `updated_at` by name when a client needs them (a write's `expectedVersion`, a detail link, keyset paging).
28
12
 
29
- ```yaml
30
- apiVersion: cms.mantle.aotter.net/v1
31
- kind: View
32
- metadata:
33
- name: my-support-requests
34
- spec:
35
- title: { en: My support requests, "zh-TW": 我的客服請求 }
36
- surface: public
37
- from: support-requests
38
- requires:
39
- auth:
40
- all: [ctx.user]
41
- fields: [id, ticketNumber, subject, requestStatus, submittedAt]
42
- filter:
43
- and:
44
- - eq: { field: submittedBy, value: { "$ctx.user": "id" } }
45
- - eq: { field: requestStatus, value: { $param: requestStatus } }
46
- orderBy:
47
- - { field: submittedAt, direction: desc }
48
- limit: 100
49
- params:
50
- type: object
51
- required: [requestStatus]
52
- properties:
53
- requestStatus: { type: string, enum: [open, waiting, closed] }
54
- ```
13
+ OFFSET, arbitrary functions/relations, DDL and transaction control are not a read escape hatch. Use keyset paging. Select latitude/longitude explicitly instead of native geo projections.
55
14
 
56
- The Schema this reads must index `submittedBy` as the leftmost field of some tuple, otherwise the identity filter is rejected. See [Schema indexes](./schema.md#indexes).
15
+ ## Restrict
57
16
 
58
- For caller-independent published data, a View may opt its anonymous REST response into the deployment cache:
17
+ `restrict` narrows one target by Schema: `restrict: { orders: "region IN auth.user('region')" }`. Each key is a Schema the SQL reads; each value uses the [scope](schema.md#field-scope) grammar, and a `= auth.uid()` term only on `author_id` or that Schema's own owner column. The effective rule is the Schema's scope AND the restrict. `{ all: true }` never lifts a restrict term: such a caller is refused with `AUTH_DENIED`. A View or Procedure that reads a restricted View carries that View's restrict too, so composing never widens it. A View with `restrict` cannot use a shared cache.
59
18
 
60
- ```yaml
61
- spec:
62
- surface: public
63
- from: published-notes
64
- cache: { sharedMaxAge: 3600 }
65
- ```
19
+ ## Search and totals
66
20
 
67
- This emits `Cache-Control: public, max-age=0, s-maxage=3600` only when the request has no cookie or authorization header and the Cloudflare Worker has a valid `cacheScope`. MCP, WebMCP, staff, guarded, SQL and operational-schema reads remain uncached. Invalid combinations fail with `VIEW_CACHE_INVALID`.
21
+ `mantle.search(<alias>, <text>)` matches the Schema's declared `searchableFields`, and `mantle.search_rank(<alias>)`
22
+ orders by relevance; qualify both with `mantle.`. An optional search reads
23
+ `WHERE p.active AND (input.q IS NULL OR mantle.search(p, input.q))`, and a ranked one
24
+ `WHERE mantle.search(p, input.q) ORDER BY mantle.search_rank(p), p.id` (see `examples/reference-service` `catalog.yaml`
25
+ for cursor paging over the rank).
68
26
 
69
- ## SQL example
27
+ Views using `mantle.search`, Store search and Admin list search share the dialect search: SQLite/D1 use FTS for queries of 3 or more characters (shorter queries use LIKE); PostgreSQL uses ILIKE over the declared fields and scans by default. A View does not make PostgreSQL search faster. For large PostgreSQL tables, the application's agent adds trigram indexes in a migration, which ILIKE then uses with no code change:
70
28
 
71
- ```yaml
72
- apiVersion: cms.mantle.aotter.net/v1
73
- kind: View
74
- metadata:
75
- name: order-lines-by-status
76
- spec:
77
- title: Order lines by status
78
- surface: staff
79
- sql: |
80
- SELECT o._mantle_id AS orderId,
81
- o.orderNumber AS orderNumber,
82
- json_extract(line.value, '$.sku') AS sku,
83
- json_extract(line.value, '$.quantity') AS quantity
84
- FROM orders AS o
85
- JOIN json_each(o.lines) AS line
86
- WHERE o.orderStatus = :orderStatus
87
- ORDER BY o.orderNumber ASC
88
- params:
89
- type: object
90
- required: [orderStatus]
91
- properties:
92
- orderStatus: { type: string, enum: [paid, shipped, cancelled] }
93
- limit: 200
94
- uiSchema:
95
- list:
96
- columns: [orderNumber, sku, quantity]
97
- searchFields: [orderNumber, sku]
98
- filterFields: [sku]
29
+ ```sql
30
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
31
+ CREATE INDEX articles_title_trgm ON articles USING gin (title gin_trgm_ops);
99
32
  ```
100
33
 
101
- ## Filter AST
102
-
103
- `filter` is a tree. Every node is an object with **exactly one** key: a comparison operator, or `and` / `or`.
104
-
105
- | Key | Shape | Rules |
106
- |---|---|---|
107
- | `eq`, `gt`, `gte`, `lt`, `lte` | `{ field, value }` | Only those two keys. `field` is a non-empty string. `value` must be present; `null` is a legal value. |
108
- | `and`, `or` | array of nodes | Non-empty. Nests to any depth. |
109
-
110
- Any other key, a node with zero or several keys, an array node, or an empty `and` / `or` is `INVALID_MANIFEST_ENVELOPE` at the node's pointer.
111
-
112
- ### Value forms
113
-
114
- | Form | Written as | Rules |
115
- |---|---|---|
116
- | Literal | `value: published` | Any JSON scalar, including `null`. Compared as written. |
117
- | Param reference | `value: { $param: locale }` | The name must be declared under `params.properties` (`VIEW_FILTER_PARAM_REF_UNKNOWN`, also raised when no `params` is declared at all or the name is empty) and listed in `params.required` (`VIEW_FILTER_PARAM_REF_NOT_REQUIRED`). |
118
- | Caller identity | `value: { "$ctx.user": "id" }` | The sentinel is closed: exactly one key, the literal string `id`, and only under `eq`. Anything else is `VIEW_FILTER_CTX_USER_REF_INVALID`. |
119
-
120
- The identity sentinel carries two further graph-level obligations, checked once `from` resolves:
121
-
122
- | Rule | Diagnostic |
123
- |---|---|
124
- | The View declares `ctx.user` in `requires.auth.all`. | `VIEW_FILTER_CTX_USER_REF_REQUIRES_AUTH` at `/spec/requires/auth/all` |
125
- | The compared field is the leftmost field of some `uniqueIndexes` or `indexes` tuple on the source Schema. | `VIEW_FILTER_CTX_USER_REF_REQUIRES_INDEX` |
126
-
127
- Provider claims and platform identities are deliberately out of reach; `id` is the only bindable identity value.
128
-
129
- ### Graph-level field checks
130
-
131
- For a `from` View, the valid field names are the top-level keys of the Schema's `properties` plus the [reserved entry columns](./schema.md#reserved-entry-columns). Unknown names are rejected per site:
132
-
133
- | Location | Diagnostic |
134
- |---|---|
135
- | `filter.<op>.field` | `VIEW_FILTER_FIELD_NOT_IN_SCHEMA` |
136
- | `fields[i]`, `orderBy[i].field` | `VIEW_FIELD_NOT_IN_SCHEMA` |
137
- | `uiSchema.list.<key>[i]` | `VIEW_UI_INVALID` |
138
- | `from` itself | `VIEW_FROM_UNKNOWN_SCHEMA` (no further field checks run) |
139
-
140
- None of these run for a `sql` View — its output columns are whatever the `SELECT` produces.
141
-
142
- ## `sql`
143
-
144
- One statement, read-only, compiled and bound by the runtime.
145
-
146
- | Rule | Effect |
147
- |---|---|
148
- | The trimmed text matches `/^select\b/i` and contains no `;`. | Otherwise `INVALID_MANIFEST_ENVELOPE` at `/spec/sql`: `View.spec.sql must be one SELECT statement without a semicolon`. |
149
- | Every `:name` occurrence is declared in `params.properties`. | `VIEW_FILTER_PARAM_REF_UNKNOWN` |
150
- | Every `:name` occurrence is listed in `params.required`. | `VIEW_FILTER_PARAM_REF_NOT_REQUIRED` |
151
- | `filter`, `fields` and `orderBy` are absent. | `INVALID_MANIFEST_ENVELOPE` at `/spec/<key>` |
152
- | Tables are Schema names. | Each Schema is exposed as a logical table reconciled at boot. Names containing `-` must be double-quoted: `FROM "post-translations"`. |
153
-
154
- Bound params are passed as positional values; caller input is never interpolated into the statement. SQLite JSON functions are available, so `json_each` and `json_extract` can unnest and project array or object members of `data` — the SQL example above does both.
155
-
156
- > **Warning**
157
- > `sql` Views are native SQLite. `mantle validate` prepares each statement against an empty SQLite database containing only the declared Schema tables; an undeclared table, syntax error or unknown column is `INVALID_MANIFEST_ENVELOPE` at `/spec/sql`. Programmatic validation runs the same check when the host supplies `sqlViewSandbox`; without that port it is deferred to the host's admission gate. On a storage adapter that does not support the native dialect the View fails at prepare time with `VIEW_DIALECT_UNSUPPORTED`, naming the dialects that adapter does support.
158
-
159
- ## `params`
160
-
161
- `params` declares the caller-supplied query shape and is walked by the [JSON Schema subset](./schema.md#json-schema-subset) validator.
162
-
163
- | Rule | Diagnostic |
164
- |---|---|
165
- | A non-array object. | `VIEW_PARAMS_INVALID_SHAPE` at `/spec/params` |
166
- | `type: "object"`. | `VIEW_PARAMS_INVALID_SHAPE` at `/spec/params/type` |
167
- | `properties` is declared and is an object. | `VIEW_PARAMS_INVALID_SHAPE` at `/spec/params/properties` |
168
- | No property named `page`, `show` or `cursor`. | `VIEW_PARAMS_RESERVED_NAME` |
169
-
170
- The runtime owns those three names for pagination, which is why they cannot be redeclared. Rename the domain param (`pageSize`, `showArchived`).
171
-
172
- ## `orderBy`, `fields` and `limit`
173
-
174
- `orderBy` is an array of objects accepting only `field` and `direction`. A non-array value, a non-object entry, a missing or empty `field`, or a `direction` other than `asc` / `desc` is `VIEW_ORDERBY_INVALID` at the offending pointer; an unrecognized key inside an entry is `INVALID_MANIFEST_ENVELOPE`.
175
-
176
- `fields` and `limit` receive **no shape validation in the parser**. A `fields` value that is not an array of strings, or a `limit` that is not a number, is not reported as a diagnostic — it fails later, at graph validation or at request time. Declare them as documented.
177
-
178
- `limit` is a per-View cap, and the runtime clamps around it on every call:
179
-
180
- | Input | Result |
181
- |---|---|
182
- | `limit` missing, non-numeric, non-finite or `<= 0` | Cap is 50. |
183
- | `limit` valid | Cap is `min(floor(limit), 500)`. 500 is the hard ceiling for any single round-trip. |
184
- | `?show=` missing or not a positive finite number | Page size is the cap. |
185
- | `?show=` valid | Page size is `min(floor(show), cap)`. |
186
- | `?page=` missing or below 1 | Page 1. |
187
-
188
- ## `uiSchema.list`
189
-
190
- Admin presentation for `surface: staff` Views. Declaring `uiSchema` on a public View is `VIEW_UI_INVALID`, as is any root key other than `list` or any key inside `list` other than the three below.
191
-
192
- | Key | Meaning |
193
- |---|---|
194
- | `columns` | Ordered columns for the Admin report table and the default CSV column set. |
195
- | `searchFields` | Output fields the Admin substring search box covers. |
196
- | `filterFields` | Output fields offered as exact-match filters (`?filter.<field>=`). |
197
-
198
- Each is an array of non-empty strings with no duplicates within the key. The characters `"`, `\` and NUL are rejected in a field name. The names are **View output field names** — SQL aliases for a `sql` View; for a `from` View they are additionally checked against the Schema's properties plus reserved columns.
199
-
200
- Admin applies search and filters before pagination, rejecting a search term or filter value longer than 200 characters and any `filter.<field>` key that is not a declared `filterFields` entry. `GET /admin/api/views/<name>/export` streams the same query as CSV covering every matching row, not only the visible page; its columns come from `uiSchema.list.columns`, falling back to `spec.fields` and then to the union of keys in the returned rows.
201
-
202
- ## Surfaces
203
-
204
- | `surface` | REST | MCP tool | Admin |
205
- |---|---|---|---|
206
- | `public` | `GET /api/views/<name>`, plus a catalog at `GET /api/views` | `query_view_<segment>` on `/mcp` | Also mounted at `GET /admin/api/views/<name>` and `/export` behind the staff gate |
207
- | `staff` | `GET /admin/api/views/<name>` and `/admin/api/views/<name>/export` — not mounted publicly | `query_view_<segment>` on `/mcp/staff` | Report sidebar |
208
- | `internal` | Not mounted | Not mounted | Not listed or mounted |
209
-
210
- For a complete generated-binding example, see [Typed queries](../guides/typed-queries.md).
211
-
212
- An `internal` View remains in the compiled plan for host code to call through `MantleRuntime.executeView`. It is an exposure policy, not an authorization bypass: `requires` and guards still evaluate against the `ctx` supplied by the host. Shared HTTP caching is invalid because no adapter owns an HTTP response for the View.
213
-
214
- A `public` declarative View over a `publishing` Schema reads **published rows only**, on every transport. The runtime adds `status = published` to the compiled query whether or not the filter spells it out; writing it is allowed and redundant, and comparing `status` to any other value is rejected at validate time (`VIEW_PUBLIC_STATUS_INVALID`). Staff Views see every status. `operational` Schemas create rows as `published`, so nothing is added. SQL Views (`spec.sql`) are the author's own statement and receive no injected predicate. Decision record: ADR-0025.
215
-
216
- `<segment>` is `metadata.name` lower-cased with `-` replaced by `_`. Two Views that mangle to the same segment collide with `MCP_TOOL_NAME_COLLISION`. Admin also serves the manifest listing `GET /admin/api/views-manifest`. Surface choice is visibility, not authorization: `requires` still gates every call on both transports. See [Surfaces](./surface.md) and [MCP and agents](../concepts/mcp-and-agents.md).
217
-
218
- The MCP `inputSchema` is `params.properties` plus `page` and `show` as optional numbers, carrying `params.required` through unchanged; the tool is annotated `readOnlyHint: true`.
219
-
220
- ## REST contract
221
-
222
- Pagination uses the two reserved knobs, `?page=` (1-indexed) and `?show=`. The response envelope is:
223
-
224
- ```json
225
- { "ok": true, "data": { "rows": [], "page": 1, "show": 20, "hasMore": true } }
226
- ```
227
-
228
- `hasMore` is the lazy form — `rows.length === show`. There is no COUNT query and no `LIMIT n+1` probe, so a final page that exactly fills `show` reports `hasMore: true` and the next page comes back empty.
229
-
230
- A failure returns `{ ok: false, diagnostic }` with the diagnostic's mapped status. Static `requires.auth` runs before parameter validation, so an unauthorized caller never learns the parameter shape; a `guard` Procedure runs after validation and authorizes the whole query rather than filtering rows.
231
-
232
- ### Param coercion
34
+ Store keeps `find`/`first` search and `count`. `count` scans every matching row; for large tables or frequently shown totals, use an internal View with indexed predicates or avoid exact totals. Aggregates still scan matching rows. See [Store reads](../guides/typed-queries.md#store-reads-and-writes).
233
35
 
234
- Query strings arrive as text. The runtime coerces each declared param by its `type` before validating against `params`; MCP callers send typed JSON and skip this step.
36
+ ## Results and paging
235
37
 
236
- | Declared `type` | Coercion | Rejected when |
237
- |---|---|---|
238
- | `string`, or `type` omitted | Used as-is. | — |
239
- | `integer` | `parseInt(raw, 10)` | The round-trip does not equal the trimmed input, so `"1.5"` and `"1abc"` fail. |
240
- | `number` | `Number(raw)` | The result is not finite. |
241
- | `boolean` | `"true"` / `"false"` | Any other text. |
242
- | `enum` (with any of the above) | Coerced by `type` first, then checked for membership. | The coerced value is not in `enum`. |
243
- | anything else | — | Unsupported on the REST surface. |
38
+ Each SELECT output needs a unique static name. Unquoted SQL identifiers fold to lower case; use an explicit quoted alias to preserve case (`r.reqStatus AS "reqStatus"`). Generation warns when an unaliased direct declared field would use its lower-case storage name. Declared-column lineage supplies generated types; computed outputs remain native/unknown, except a top-level output that is a JSON document by construction (a JSON aggregate or builder, a scalar subquery of one, or a `COALESCE` of those; see [SQL patterns](../guides/sql-patterns.md#a-row-with-its-children-in-one-read)), which is typed JSON. An output is non-null (`string`, not `string | null`) only when it reads unchanged a column its table declares NOT NULL (`id`, `version`, `created_at`, `updated_at`, a publishing `status`, or a [required](schema.md#fields-and-storage) field of an operational Schema) directly or through FROM subqueries; the right side of a LEFT JOIN, an aggregate, a column beside an aggregate without GROUP BY, a CTE and an expression stay nullable. SQL formats declared date-time/date outputs, while computed timestamps retain driver values. See [native execution](../guides/native-execution.md).
244
39
 
245
- A missing required param or a failed coercion is `INPUT_VALIDATION_FAILED` (400). Unknown query keys are ignored.
40
+ Bare `views/<encoded-name>.ts` returns row arrays. Proved pageable exposed/internal Views also receive native `paged/views/*`, returning `{ rows, nextCursor? }`. Selected REST/MCP boundaries use separate `paged/wire/views/*` SQL when declared integers require exact transport projections; bare modules also export `WireRow`. Indexed ordering after fixed equality prefixes ([index prefixes](schema.md#index-prefixes-for-paged-views)), non-null typed keys, complete groups and singleton joins/lookups are checked at generation. Unsupported internal paging retains its bare View; exposure requires a supported page contract. Aggregates still scan/group their sources: inspect EXPLAIN.
246
41
 
247
- ## Source
42
+ Publishing Schema fields may be NULL in drafts even when required, so public View paging cannot order by them. `published_at` is accepted by Store ordering but rejected as a View paging key. Use `ORDER BY id`, or keep the business date in an operational Schema as an indexed, required non-null field.
248
43
 
249
- - [`packages/mantle-spec/src/domain/model/ManifestGrammar.ts`](../../../packages/mantle-spec/src/domain/model/ManifestGrammar.ts)
250
- - [`packages/mantle-spec/src/domain/service/ManifestParser.ts`](../../../packages/mantle-spec/src/domain/service/ManifestParser.ts)
251
- - [`packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts`](../../../packages/mantle-spec/src/domain/service/ManifestGraphValidator.ts)
252
- - [`packages/mantle-spec/src/domain/service/SchemaAdminUiChecker.ts`](../../../packages/mantle-spec/src/domain/service/SchemaAdminUiChecker.ts)
253
- - [`packages/mantle-spec/src/domain/service/McpToolNaming.ts`](../../../packages/mantle-spec/src/domain/service/McpToolNaming.ts)
254
- - [`packages/mantle-runtime/src/usecase/view/ExecuteViewUseCase.ts`](../../../packages/mantle-runtime/src/usecase/view/ExecuteViewUseCase.ts)
255
- - [`packages/mantle-runtime/src/domain/service/ViewParamCoercer.ts`](../../../packages/mantle-runtime/src/domain/service/ViewParamCoercer.ts)
256
- - [`packages/mantle-runtime/src/domain/service/Pagination.ts`](../../../packages/mantle-runtime/src/domain/service/Pagination.ts)
257
- - [`packages/mantle-runtime/src/domain/service/CallableCapabilityProjector.ts`](../../../packages/mantle-runtime/src/domain/service/CallableCapabilityProjector.ts)
258
- - [`packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts`](../../../packages/mantle-runtime/src/infrastructure/http/createMantleRequestHandler.ts)
259
- - [`packages/mantle-runtime/src/infrastructure/storage/SqliteViewCompiler.ts`](../../../packages/mantle-runtime/src/infrastructure/storage/SqliteViewCompiler.ts)
260
- - [`packages/mantle-runtime/src/infrastructure/storage/SqliteMantleStorageAdapter.ts`](../../../packages/mantle-runtime/src/infrastructure/storage/SqliteMantleStorageAdapter.ts)
261
- - [`packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts`](../../../packages/mantle-runtime/src/usecase/boot/ValidateBootUseCase.ts)
262
- - [`packages/mantle-admin/src/mountMantleAdmin.ts`](../../../packages/mantle-admin/src/mountMantleAdmin.ts)
263
- - [`packages/adapters/cloudflare/src/mount/mountRuntimeEndpoints.ts`](../../../packages/adapters/cloudflare/src/mount/mountRuntimeEndpoints.ts)
44
+ Internal Views have no public route/tool. Public and admin Views use selected REST/MCP/Admin bindings. `uiSchema.list` selects admin search/filter/display metadata; `uiSchema.nav.group/order/hidden` places an admin View in the sidebar ([sidebar](../guides/native-admin.md#sidebar)). See [surfaces](surface.md) for validation, paging and wire conversions and [typed queries](../guides/typed-queries.md) for a complete example.