@aotter/mantle 0.2.0-alpha.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +26 -3
  2. package/dist/auth/adapter.js +10 -34
  3. package/dist/auth/admin.js +2 -5
  4. package/dist/auth/caller.js +0 -27
  5. package/dist/auth/consent.js +100 -0
  6. package/dist/auth/hardening.js +0 -7
  7. package/dist/auth/identity.js +0 -11
  8. package/dist/auth/oauth.js +0 -32
  9. package/dist/auth/resource-seeds.js +0 -2
  10. package/dist/auth/route-http.js +33 -0
  11. package/dist/auth/routes.js +8 -153
  12. package/dist/build/compiler.d.ts +19 -609
  13. package/dist/build/compiler.js +3 -6896
  14. package/dist/build/spec/domain/model/SqlIr.d.ts +2 -0
  15. package/dist/build/spec/domain/model/UserFields.d.ts +1 -0
  16. package/dist/bun/assets.js +0 -2
  17. package/dist/domain/adminOrder.js +0 -2
  18. package/dist/domain/columns.js +7 -20
  19. package/dist/domain/describe/chunk-SGMRVDGE.js +1 -0
  20. package/dist/domain/describe/index.js +1 -115
  21. package/dist/domain/describe/target-hash.js +1 -12
  22. package/dist/domain/entry.js +0 -14
  23. package/dist/domain/generated-tools.js +0 -1
  24. package/dist/domain/input.js +0 -2
  25. package/dist/domain/paging.js +0 -17
  26. package/dist/domain/recipes.js +10 -7
  27. package/dist/domain/userReferences.js +0 -1
  28. package/dist/domain/wireScalars.js +0 -6
  29. package/dist/infrastructure/adminEntries.js +10 -7
  30. package/dist/infrastructure/adminPaging.js +55 -57
  31. package/dist/infrastructure/artifacts.js +0 -4
  32. package/dist/infrastructure/authSchema.js +0 -9
  33. package/dist/infrastructure/bundle.js +17 -32
  34. package/dist/infrastructure/cli.js +25 -11
  35. package/dist/infrastructure/generate.js +18 -17
  36. package/dist/infrastructure/generateAuth.js +48 -5
  37. package/dist/infrastructure/help.js +55 -0
  38. package/dist/infrastructure/init.js +50 -0
  39. package/dist/infrastructure/inputs.js +0 -9
  40. package/dist/infrastructure/mounts.js +22 -0
  41. package/dist/infrastructure/wrangler.js +18 -9
  42. package/dist/kernel/authorization.js +0 -1
  43. package/dist/kernel/binds.d.ts +16 -7
  44. package/dist/kernel/binds.js +4 -10
  45. package/dist/kernel/caller-identity.d.ts +0 -3
  46. package/dist/kernel/caller-identity.js +0 -15
  47. package/dist/kernel/caller-mode.d.ts +4 -0
  48. package/dist/kernel/caller-mode.js +14 -0
  49. package/dist/kernel/contracts.d.ts +7 -11
  50. package/dist/kernel/contracts.js +8 -44
  51. package/dist/kernel/cron.js +0 -4
  52. package/dist/kernel/dialect/common.d.ts +0 -1
  53. package/dist/kernel/dialect/common.js +0 -3
  54. package/dist/kernel/dialect/postgres.js +0 -2
  55. package/dist/kernel/dialect/sqlite.js +1 -6
  56. package/dist/kernel/drain.js +2 -8
  57. package/dist/kernel/driver/bun-sqlite.js +0 -4
  58. package/dist/kernel/driver/d1.js +0 -1
  59. package/dist/kernel/driver/postgres.js +3 -17
  60. package/dist/kernel/driver/transaction-context.js +0 -1
  61. package/dist/kernel/email.js +0 -4
  62. package/dist/kernel/host-access.js +2 -3
  63. package/dist/kernel/host-caller.d.ts +1 -1
  64. package/dist/kernel/host-caller.js +3 -6
  65. package/dist/kernel/json-body.js +0 -2
  66. package/dist/kernel/mcp-apps.js +1 -6
  67. package/dist/kernel/mcp-call.js +0 -1
  68. package/dist/kernel/mcp-handler.js +3 -27
  69. package/dist/kernel/mcp.d.ts +5 -1
  70. package/dist/kernel/mcp.js +2 -6
  71. package/dist/kernel/media.js +0 -18
  72. package/dist/kernel/paging.js +1 -9
  73. package/dist/kernel/publishing.js +3 -4
  74. package/dist/kernel/r2-media.js +0 -7
  75. package/dist/kernel/rest-dispatch.js +0 -1
  76. package/dist/kernel/rest-router.js +2 -3
  77. package/dist/kernel/rest.js +0 -3
  78. package/dist/kernel/scalars.js +4 -11
  79. package/dist/kernel/search.js +0 -1
  80. package/dist/kernel/site.js +0 -2
  81. package/dist/kernel/store.d.ts +11 -1
  82. package/dist/kernel/store.js +21 -34
  83. package/dist/kernel/surface-failure.d.ts +10 -0
  84. package/dist/kernel/surface-failure.js +29 -0
  85. package/dist/kernel/surface.js +0 -3
  86. package/dist/kernel/sweep.js +0 -1
  87. package/dist/kernel/translations.js +0 -1
  88. package/dist/kernel/user-caller.js +2 -8
  89. package/dist/kernel/user-fields.d.ts +3 -2
  90. package/dist/kernel/user-fields.js +9 -9
  91. package/dist/kernel/validation.js +0 -8
  92. package/dist/kernel/web-gateway.js +0 -2
  93. package/dist/kernel/web-head.js +0 -1
  94. package/dist/kernel/web-index.js +39 -0
  95. package/dist/kernel/web-response.js +0 -1
  96. package/dist/kernel/web-serve.js +97 -0
  97. package/dist/kernel/web.d.ts +1 -1
  98. package/dist/kernel/web.js +10 -125
  99. package/dist/kernel/where.js +0 -6
  100. package/dist/usecase/adminBindings.js +30 -21
  101. package/dist/usecase/adminClient.js +0 -21
  102. package/dist/usecase/adminDescriptors.js +2 -9
  103. package/dist/usecase/generate.js +7 -12
  104. package/dist/usecase/handlers.js +0 -3
  105. package/dist/usecase/mcp-media.js +0 -1
  106. package/dist/usecase/mcp.js +0 -4
  107. package/dist/usecase/paging.js +1 -3
  108. package/dist/usecase/publishing.js +1 -5
  109. package/dist/usecase/rest.js +0 -1
  110. package/dist/usecase/site.js +0 -2
  111. package/dist/usecase/stores.js +4 -14
  112. package/dist/usecase/tool-version.js +0 -3
  113. package/dist/usecase/translations.js +0 -2
  114. package/dist/usecase/validation.js +8 -20
  115. package/docs/examples/README.md +13 -5
  116. package/docs/examples/app-shell/README.md +43 -8
  117. package/docs/examples/app-shell/accept.mjs +91 -0
  118. package/docs/examples/app-shell/gitignore +7 -0
  119. package/docs/examples/app-shell/package.json +7 -6
  120. package/docs/examples/app-shell/smoke-kit.mjs +29 -15
  121. package/docs/examples/app-shell/src/auth.ts +3 -2
  122. package/docs/examples/app-shell/src/routes.ts +6 -3
  123. package/docs/examples/app-shell/src/service.ts +4 -3
  124. package/docs/examples/club-events/README.md +7 -2
  125. package/docs/examples/club-events/manifests/club-events.yaml +22 -0
  126. package/docs/examples/club-events/smoke.mjs +7 -1
  127. package/docs/examples/club-events/src/handlers.ts +1 -1
  128. package/docs/examples/purchasing/README.md +12 -3
  129. package/docs/examples/purchasing/manifests/purchasing.yaml +36 -3
  130. package/docs/examples/purchasing/smoke.mjs +14 -0
  131. package/docs/examples/purchasing/src/handlers.ts +1 -1
  132. package/docs/examples/recipes/README.md +5 -3
  133. package/docs/handbook/cloudflare/authentication.md +21 -4
  134. package/docs/handbook/cloudflare/chatgpt-sites.md +4 -2
  135. package/docs/handbook/cloudflare/deploy-and-operate.md +24 -1
  136. package/docs/handbook/cloudflare/media-r2.md +2 -3
  137. package/docs/handbook/cloudflare/service-entry.md +17 -4
  138. package/docs/handbook/concepts/runtime-and-adapters.md +1 -1
  139. package/docs/handbook/examples/hub.md +1 -1
  140. package/docs/handbook/guides/agent-setup.md +15 -10
  141. package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
  142. package/docs/handbook/guides/local-testing.md +40 -0
  143. package/docs/handbook/guides/mcp-apps.md +78 -1
  144. package/docs/handbook/guides/native-admin.md +29 -6
  145. package/docs/handbook/guides/native-execution.md +5 -4
  146. package/docs/handbook/guides/oauth.md +58 -0
  147. package/docs/handbook/guides/public-pages.md +44 -4
  148. package/docs/handbook/guides/sql-patterns.md +266 -0
  149. package/docs/handbook/guides/typed-queries.md +173 -0
  150. package/docs/handbook/navigation.json +16 -0
  151. package/docs/handbook/reference/admin-descriptors.md +4 -1
  152. package/docs/handbook/reference/authorization.md +1 -1
  153. package/docs/handbook/reference/diagnostics.md +6 -1
  154. package/docs/handbook/reference/procedure.md +5 -5
  155. package/docs/handbook/reference/schema.md +10 -4
  156. package/docs/handbook/reference/sql.md +64 -0
  157. package/docs/handbook/reference/surface.md +11 -4
  158. package/docs/handbook/reference/trigger.md +9 -0
  159. package/docs/handbook/reference/view.md +11 -3
  160. package/docs/handbook/releases/index.md +27 -6
  161. package/docs/handbook/start/agents.md +45 -0
  162. package/docs/handbook/start/project-and-cli.md +15 -1
  163. package/docs/handbook/start/quickstart-worker.md +1 -1
  164. package/docs/skills/develop/SKILL.md +44 -16
  165. package/docs/skills/integrate/SKILL.md +4 -9
  166. package/docs/skills/provision/SKILL.md +15 -12
  167. package/docs/skills/update/SKILL.md +21 -18
  168. package/package.json +24 -4
  169. package/skills/README.md +1 -1
  170. package/skills/mantle/SKILL.md +48 -107
  171. package/dist/domain/describe/chunk-ZVXBGP7Y.js +0 -424
  172. package/docs/skills/develop/mcp-app.md +0 -39
  173. package/skills/.keep +0 -0
  174. package/skills/mantle/references/local-testing.md +0 -15
  175. package/skills/mantle/references/web.md +0 -41
  176. package/skills/mantle/references/website.md +0 -49
@@ -38,7 +38,7 @@ A string `pattern` needs a `maxLength`: generation bounds each regex's backtrack
38
38
  | object/array | JSON text | jsonb |
39
39
  | geo | lat/lng columns, R*Tree | lat/lng columns |
40
40
 
41
- System columns include id, version, created_at, updated_at and author_id; publishing adds status/published_at. Do not declare reserved fields. Manifest native column names in indexes use camelCase (`createdAt`); generated Store/SQL use storage names (`created_at`). Scope is omitted from returned rows.
41
+ System columns include id, version, created_at, updated_at and author_id; publishing adds status/published_at. Initial DDL declares a field NOT NULL when an `operational` Schema lists it in `required` and it does not allow null; `default` never does, and no column gets a DDL `DEFAULT` ([server-maintained fields](#admin-uischema)). Do not declare reserved fields. Manifest native column names in indexes use camelCase (`createdAt`); generated Store/SQL use storage names (`created_at`). Scope is omitted from returned rows.
42
42
 
43
43
  Declared date/date-time outputs are SQL-formatted strings (`YYYY-MM-DD`, UTC ISO with six fractional digits). Computed timestamps remain native. Inputs and other output scalars follow [the native contract](../guides/native-execution.md). Date conversion through JS Date loses microseconds.
44
44
 
@@ -76,9 +76,11 @@ The runtime never creates, alters or checks tables. Initial DDL is for an empty
76
76
 
77
77
  ## Admin uiSchema
78
78
 
79
- A property marked `readOnly: true` in the JSON Schema is server-maintained (a counter, a total): Admin's generic create stores its `default` whatever the input says, Admin edits ignore it, and only Procedures (or server code through the Store) change it afterwards. JSON Schema defaults are applied by Admin and the Store, not by the database: a SQL Procedure that inserts the row sets the field itself. A root `readOnly: true` makes the whole Schema read-only in Admin.
79
+ A property marked `readOnly: true` in the JSON Schema is server-maintained (a counter, a total): Admin's generic create stores its `default` whatever the input says, Admin edits ignore it, and only Procedures (or server code through the Store) change it afterwards. JSON Schema defaults are applied by Admin's generic create only, not by the Store or the database: initial DDL emits no `DEFAULT` for a field, so server code through the Store and a SQL Procedure that inserts the row set the field themselves. A root `readOnly: true` makes the whole Schema read-only in Admin: a generic Admin write is refused with 403 `AUTH_DENIED` (not a 409 version conflict).
80
80
 
81
- `fields.<name>.widget` selects a built-in control (`textarea`); `list.primaryField`, `list.columns` and enum `filterField` select list presentation; `nav.parentField` chooses the parent a child folds under (required when two or more required `id` references exist) and `nav.standalone` lists the child in the sidebar; `nav.group/order/hidden` place it in the sidebar ([sidebar](../guides/native-admin.md#sidebar)). `list.columns` must not repeat `list.primaryField` or itself (`SCHEMA_UI_INVALID`). Generation validates these against declared fields/indexes. Components that render a field, cell or record aside are not manifest references: they live in `defineAdmin` ([customising Admin](../guides/native-admin.md#customising-admin)), and a manifest that still names one (`widget: <extension>/<contribution>`, `widget` `options`, `list.cells`, `panels`) is refused. JSON Schema root `schema.readOnly: true` disables generic Admin writes, not declared Procedures; `uiSchema.readOnly` is rejected with `SCHEMA_UI_INVALID`. `x-mantle-ref` relates a field to another Schema id or single-field unique field.
81
+ `default` does not make a column NOT NULL; `required` does (in an `operational` Schema). For a server-maintained value that is never NULL, list it in both, `required: [name, total]` with `total: {type: integer, readOnly: true, default: 0}`: initial DDL declares `total` NOT NULL, Store and View row types read it as `number` rather than `number | null`, Admin's create still fills the default when the client omits it, and the Store's insert type requires server code to pass it. Without `required` the column stays nullable, and an INSERT that omits it stores NULL. Adding `required` to an existing table is a [migration](../concepts/runtime-and-adapters.md#schema-installation-and-migrations): backfill NULLs, then add NOT NULL.
82
+
83
+ `fields.<name>.widget` selects a built-in control (`textarea`); `list.primaryField`, `list.columns` and enum `filterField` select list presentation (operational Schemas only; a publishing Schema's list shows its title field and status); `nav.parentField` chooses the parent a child folds under (required when two or more required `id` references exist) and `nav.standalone` lists the child in the sidebar; `nav.group/order/hidden` place it in the sidebar ([sidebar](../guides/native-admin.md#sidebar)). `list.columns` must not repeat `list.primaryField` or itself (`SCHEMA_UI_INVALID`). Generation validates these against declared fields/indexes. Components that render a field, cell or record aside are not manifest references: they live in `defineAdmin` ([customising Admin](../guides/native-admin.md#customising-admin)), and a manifest that still names one (`widget: <extension>/<contribution>`, `widget` `options`, `list.cells`, `panels`) is refused. JSON Schema root `schema.readOnly: true` disables generic Admin writes, not declared Procedures; `uiSchema.readOnly` is rejected with `SCHEMA_UI_INVALID`. `x-mantle-ref` relates a field to another Schema id or single-field unique field. It is not a foreign key: the database does not check that the referenced row exists, and deleting that row leaves the reference dangling. Check existence in the write (a JOIN or `EXISTS` in the Procedure) and guard deletes with `NOT EXISTS` ([SQL patterns](../guides/sql-patterns.md)).
82
84
 
83
85
  See [Admin](../guides/admin-ui.md) and [diagnostics](diagnostics.md).
84
86
 
@@ -88,7 +90,11 @@ See [Admin](../guides/admin-ui.md) and [diagnostics](diagnostics.md).
88
90
 
89
91
  A caller whose value for a field is `{ all: true }` drops that field's term only; the other terms still apply. Generated SQL holds one statement per target and selects the form for each caller at run time; values are always bound parameters. Neither roles nor the admin layer widen a field scope or a translation parent's visibility. A Schema whose `x-mantle-ref` points to a field-scoped Schema must carry the same scope terms.
90
92
 
91
- For inserts, one allowed partition autofills a missing value; multiple values require an explicit allowed partition, and missing/empty values fail with `AUTH_DENIED`. Callers holding `{ all: true }` and system callers always provide an explicit partition. Procedure INSERT/upsert is trusted-only; use the scoped Store for user writes. The partition is never writable through SET, including upsert and native Admin patch. An excluded id behaves like a missing row: reads return not found; Store update/delete and row-classified Procedure UPDATE/DELETE affecting zero rows return HTTP 409 (`CONFLICT`). Native Admin's visible-row pre-read returns 404 for either id. SQLite binds a JSON array; PostgreSQL binds a quoted, escaped array. Public Views over field scope warn that anonymous callers are refused.
93
+ For inserts, one allowed partition autofills a missing value; multiple values require an explicit allowed partition, and missing/empty values fail with `AUTH_DENIED`. Callers holding `{ all: true }` and system callers always provide an explicit partition. A Procedure `INSERT … VALUES` follows the same rule for user callers, checked before any SQL runs: leave the partition column out to autofill a caller's single value, or take it from an input declared with the column's own type (`VALUES (input.region, …)`) or a matching literal, which must be inside the caller's set; any other expression there is refused. A Procedure that callers holding several values or `{ all: true }` use, such as an Admin create action, takes the partition as an optional input. `INSERT … SELECT` into a field-scoped Schema, and an insert into a Schema the target `restrict`s, run only for system callers, which this check does not cover: they name the partition themselves; use the scoped Store for user writes there. The partition is never writable through SET, including upsert and native Admin patch. An excluded id behaves like a missing row: reads return not found; Store update/delete and row-classified Procedure UPDATE/DELETE affecting zero rows return HTTP 409 (`CONFLICT`). Native Admin's visible-row pre-read returns 404 for either id. SQLite binds a JSON array; PostgreSQL binds a quoted, escaped array. Public Views over field scope warn that anonymous callers are refused.
94
+
95
+ #### References to a scoped Schema
96
+
97
+ A non-null `x-mantle-ref` value that points to a scoped Schema (field or owner scope) must name a row of that Schema the caller can read, matched on `id` or on the object form's `field`, and that row must have the same value as the child for every scope field the two share (`ADR-0034`: a reference between scoped Schemas stays inside one owner or partition). A Store insert or update, a Procedure `INSERT … VALUES`, `UPDATE … SET` or `DO UPDATE SET`, and native Admin's generic create and patch check it in the same statement; a missing, unreadable or other-partition parent behaves like a missing row: a single-row statement returns HTTP 409 (`CONFLICT`), and an `ON CONFLICT` insert or a set update writes nothing for that row. A caller holding several values or `{ all: true }` can still not point a child in one partition at a parent in another. `INSERT … SELECT` that writes such a reference runs only for system callers. A caller's `UPDATE … SET` or `DO UPDATE SET` of such a reference takes no subquery, which could read the parent's columns as its own. Trusted writes (system callers, `runtime.store`) are not checked, like the other row rules. NULL is always allowed; deleting a parent does not touch its children.
92
98
 
93
99
  ## Reserved entry columns
94
100
 
@@ -0,0 +1,64 @@
1
+ ---
2
+ description: The SQL that Views and Procedures accept on SQLite/D1 and on PostgreSQL, and what each refuses with which diagnostic.
3
+ ---
4
+ # SQL accepted by each dialect
5
+
6
+ Views and Procedures share one allowlist with two profiles. SQLite and D1 run the base profile; PostgreSQL runs the
7
+ base profile plus the additions below. Anything not listed is refused at generation with a code (`SQL_UNSUPPORTED`,
8
+ `SQL_FUNCTION`, `SQL_SHAPE`, `SQL_TYPE`) and a source position, so generation is the authority for each target. For
9
+ worked examples see [SQL patterns](../guides/sql-patterns.md).
10
+
11
+ ## Both dialects
12
+
13
+ | Area | Accepted |
14
+ | --- | --- |
15
+ | Statements | `SELECT`, `INSERT … VALUES` / `INSERT … SELECT`, `UPDATE … SET … WHERE`, `DELETE … WHERE`, each with `RETURNING`; `WITH` (non-recursive) on a `SELECT` |
16
+ | Upsert | `ON CONFLICT (<unique index columns>) DO NOTHING` / `DO UPDATE SET …`, with `excluded.<column>` |
17
+ | Joins | `INNER JOIN`, `LEFT JOIN`; a comma join only for a row source (`json_each`, or PostgreSQL's below) |
18
+ | Clauses | `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT` |
19
+ | Predicates | `=`, `<>`, `!=`, `<`, `>`, `<=`, `>=`, `AND`, `OR`, `NOT`, `IS [NOT] NULL`, `IS [NOT] DISTINCT FROM`, `[NOT] BETWEEN`, `IN (list)`, `IN (subquery)`, `LIKE`, `[NOT] EXISTS (subquery)` |
20
+ | Expressions | `+ - * / %`, `\|\|`, `->>` (JSON field), `CASE`, `COALESCE`, `NULLIF`, scalar subqueries, `CAST(… AS text \| int4 \| int8 \| float8 \| bool \| timestamptz \| date \| numeric \| interval)` |
21
+ | Aggregates | `count`, `sum`, `min`, `max`, `avg` |
22
+ | Windows | `row_number()`, `rank()`, `sum()`, `count()` with `OVER (PARTITION BY … ORDER BY …)` and the default frame |
23
+ | Scalar functions | `lower`, `upper`, `length`, `abs`, `round`, `substr`, `replace`, `trim`, `ltrim`, `rtrim`, `instr` |
24
+ | Time | `now()`, `date_trunc('hour' \| 'day' \| 'week' \| 'month' \| 'year', …)`, `extract(year \| month \| day \| dow \| hour FROM …)` |
25
+ | Mantle | `auth.uid()`, `input.<name>`, `mantle.search`, `mantle.search_rank`, `mantle.near`, `mantle.distance` ([features](features.md)) |
26
+
27
+ ## SQLite and D1 only
28
+
29
+ | Accepted | PostgreSQL instead |
30
+ | --- | --- |
31
+ | `json_each(<array>)` as a row source (`FROM json_each(input.items) j`, `j.value ->> 'field'`) | `jsonb_array_elements(…) AS j(value)` |
32
+ | `json_group_array`, `json_group_object` | `jsonb_agg`, `jsonb_object_agg` |
33
+ | `json_extract`, `json_set`, `json_insert`, `json_remove`, `json_array_length`; build an object with `json_set('{}', '$.k', v, …)` (`json_object` is refused) | `jsonb_build_object`, `jsonb_build_array` and the jsonb operators |
34
+ | `typeof`, `hex` | refused (`typeof` is SQLite's; PostgreSQL has `pg_typeof`) |
35
+
36
+ An integer `CAST` of anything but a literal is refused (`SQL_TYPE`): SQLite truncates where PostgreSQL rounds. Write
37
+ `round(x)`; `->>` already returns a JSON number as a number.
38
+
39
+ ## PostgreSQL additions
40
+
41
+ | Area | Accepted |
42
+ | --- | --- |
43
+ | Set operations | `UNION [ALL]`, `INTERSECT`, `EXCEPT`, inside a `WITH` or a subquery (the View's own SELECT is the one that pages) |
44
+ | Relations | `LATERAL` subqueries, `WITH RECURSIVE`, `MATERIALIZED` / `NOT MATERIALIZED` CTEs |
45
+ | Row sources | `jsonb_array_elements`, `jsonb_array_elements_text`, `jsonb_each`, `jsonb_each_text`, with `WITH ORDINALITY` |
46
+ | Operators | `ILIKE`, `~`, `~*`, `!~`, `!~*`, jsonb `->`, `#>`, `#>>`, `@>`, `<@`, `?`, `?\|`, `?&` |
47
+ | Aggregates | `string_agg`, `jsonb_agg`, `jsonb_object_agg`, with `FILTER (WHERE …)` and `ORDER BY` inside the call |
48
+ | Windows | also `avg`, `min`, `max`, `lag`, `lead`, `first_value`, `last_value`, `dense_rank`; frames except `GROUPS` and `EXCLUDE` |
49
+ | Functions | `GREATEST`, `LEAST`, `floor`, `ceil`, `sqrt`, `power`, `split_part`, `timezone`, `jsonb_build_object`, `jsonb_build_array`, `jsonb_strip_nulls`, `to_jsonb`, `jsonb_typeof`, `CAST(… AS jsonb)` |
50
+ | Time | `date_trunc` also `'minute'`, `'quarter'`; `extract` also `minute`, `quarter`, `week`, `isoyear`, `isodow`, `doy`, `epoch` |
51
+
52
+ `jsonb_agg` of no rows is `NULL`: write `COALESCE(jsonb_agg(…), CAST('[]' AS jsonb))` for an empty array.
53
+
54
+ ## Refused on every target
55
+
56
+ - `OFFSET`: page with the generated cursor (`limit`, `cursor`) instead.
57
+ - `RIGHT` and `FULL` joins, `UPDATE … FROM`, `DELETE … USING`, DDL and any function not listed.
58
+ - `DISTINCT` with `ORDER BY`: the paging key Mantle appends would change what is distinct; use `GROUP BY`.
59
+ - A program of more than 2000 syntax nodes.
60
+
61
+ Two write rules come from Mantle, not the dialect ([Procedure](procedure.md)). A row-classified `UPDATE`/`DELETE`
62
+ (`WHERE id = input.id …`) must change exactly one row, otherwise `CONFLICT`. A set form (`WHERE id IN (SELECT …)`,
63
+ `INSERT … SELECT`) may change none, so write a conditional write that may do nothing in that form. Scope, version,
64
+ timestamps and author columns are assigned by Mantle, never by your SQL.
@@ -13,7 +13,7 @@
13
13
  snake-cased View names on each declared surface, and snake-cased Procedure
14
14
  names per MCP Trigger. Its SDK loads only through a request-time dynamic import.
15
15
  - HTTP/MCP Triggers declare exposure only. `mantle generate` refuses schedule
16
- and lifecycle Triggers; Cloud scheduling is a separate capability.
16
+ and lifecycle Triggers; host scheduling is a separate capability.
17
17
  - Native Admin retains selected 0.2 paths and the console contracts; see
18
18
  [Native Admin](../guides/native-admin.md) for app-mounted
19
19
  `adminBindings()` and `entries(bindings.schemas, bindings.adapter)`.
@@ -25,6 +25,8 @@ challenges, JSON-RPC, web rendering, keyset paging) lives in the package's
25
25
  `rest`, `mcp`, `web`, `paging` and `validation` kernel modules, so a runtime fix
26
26
  ships as a package update instead of a regeneration.
27
27
 
28
+ Procedure surface selection follows the [HTTP/MCP pairing default and deliberate asymmetry exception](trigger.md); no surface is implied.
29
+
28
30
  The generated `validation/views/<name>.ts` and
29
31
  `validation/procedures/<name>.ts` export `validate(unknown)`. View validators
30
32
  also export `query(URLSearchParams)` returning `{ input, limit?, cursor? }`.
@@ -219,7 +221,9 @@ must also mount Better Auth's `/.well-known/oauth-protected-resource/mcp`
219
221
  metadata route. Auth challenges point to that route. Presented bearer credentials
220
222
  need the `mcp` scope; cookie sessions use the same-origin mutation checks.
221
223
 
222
- Generation emits `mcp.ts` only for a non-internal View or MCP Trigger. These
224
+ Generation emits `mcp.ts` only for a non-internal View or MCP Trigger. Omitting
225
+ MCP remains a deliberate asymmetric choice; an HTTP-only Procedure still generates
226
+ successfully with the warning described in [Trigger](trigger.md). These
223
227
  surfaces typecheck without the optional `@modelcontextprotocol/server` peer;
224
228
  install the exact version declared in the installed SDK's `peerDependencies`.
225
229
  The peer loads by dynamic import on the first matching `/mcp` request; if
@@ -229,7 +233,7 @@ surface its generated `publish_<schema>`, `unpublish_<schema>` and
229
233
  `archive_<schema>` tools use the same snake-case naming rule, are admin-only
230
234
  and take `{ id, expectedVersion }` through the shared generated validation.
231
235
 
232
- `/mcp/admin` admits only admins: `isAdmin(caller)` must hold. Better Auth’s admin plugin `adminRoles` defines this layer (default `["admin"]`); the application supplies its verified Caller. MCP bodies are limited
236
+ `/mcp/admin` admits only admins: `isAdmin(caller)` must hold. It lists only admin-surface tools; public Views and Procedures stay on `/mcp`, and generation never copies them across. An admin's AI that changes records also needs admin Views to find them and read back the committed row and its version (search, detail), declared by the app; the [acceptance design audit](../guides/agent-workflow-acceptance.md#audit-mcp-tool-design-before-implementation) checks this. Better Auth’s admin plugin `adminRoles` defines this layer (default `["admin"]`); the application supplies its verified Caller. MCP bodies are limited
233
237
  to 1 MiB (1,048,576 bytes). Resource metadata challenges use the origin of
234
238
  `request.url`; behind a proxy, the app must pass a Request with its public
235
239
  origin so the audience and metadata URL point to the client-visible service.
@@ -348,7 +352,10 @@ REST and MCP View pages expose declared JSON columns as JSON values and declared
348
352
  boolean columns as booleans, including explicitly authored SQL aliases. SQLite
349
353
  and D1 conversions are compiled into only the selected boundary pages;
350
354
  PostgreSQL already-native JSON/boolean values pass through. SQL NULL stays null and absent
351
- fields stay absent. Expressions without declared column lineage stay native.
355
+ fields stay absent. A computed output that is a JSON document by construction
356
+ (`json_group_array`, `json_set`, `jsonb_agg`, `jsonb_build_object` and the like, a scalar
357
+ subquery of one, or a `COALESCE` of those) is converted like a declared JSON column, so it
358
+ is a JSON value on every target. Other expressions without declared column lineage stay native.
352
359
  Malformed persisted JSON becomes a redacted internal failure. The conversion
353
360
  adds no database operations and does not mutate native query rows.
354
361
 
@@ -24,12 +24,21 @@ metadata: { name: close-ticket-http }
24
24
  spec:
25
25
  source: { kind: http, method: POST, path: '/api/tickets/{id}/close' }
26
26
  target: { procedure: close-ticket }
27
+ ---
28
+ apiVersion: cms.mantle.aotter.net/v2
29
+ kind: Trigger
30
+ metadata: { name: close-ticket-mcp }
31
+ spec:
32
+ source: { kind: mcp, surface: admin }
33
+ target: { procedure: close-ticket }
27
34
  ```
28
35
 
29
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.
30
37
 
31
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.
32
39
 
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.
41
+
33
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.
34
43
 
35
44
  ## Input rules
@@ -1,12 +1,14 @@
1
1
  # View
2
2
 
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.
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.
4
4
 
5
5
  ## SQL
6
6
 
7
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.
8
8
 
9
- SQLite supports the compiler's subset: scalar expressions, comparisons, AND/OR/NOT, CASE, COALESCE, supported casts, INNER/LEFT JOIN, subqueries, grouping/aggregates, supported windows, ordered LIMIT, date operations and declared Mantle search/geo functions. PostgreSQL additionally supports recursive CTEs, lateral relations, more window/aggregate forms, ILIKE and jsonb operators where the allowlist permits them. Unsupported SQL fails with a source position; generation is the authority for each target.
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.
10
+
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).
10
12
 
11
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.
12
14
 
@@ -16,6 +18,12 @@ OFFSET, arbitrary functions/relations, DDL and transaction control are not a rea
16
18
 
17
19
  ## Search and totals
18
20
 
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).
26
+
19
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:
20
28
 
21
29
  ```sql
@@ -27,7 +35,7 @@ Store keeps `find`/`first` search and `count`. `count` scans every matching row;
27
35
 
28
36
  ## Results and paging
29
37
 
30
- 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. SQL formats declared date-time/date outputs, while computed timestamps retain driver values. See [native execution](../guides/native-execution.md).
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).
31
39
 
32
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.
33
41
 
@@ -15,15 +15,36 @@ From 0.2.0-alpha.7, Mantle publishes three packages together at one version:
15
15
  and `@aotter/mantle-admin` (optional Admin server, SPA and MCP App).
16
16
  Pin every package you use to the same exact version and upgrade them together.
17
17
 
18
- ## 0.2.0 — in preparation
19
-
20
- ### Upgrading to 0.2.0-alpha.8
18
+ ## 0.2.0 (2026-10-19)
19
+
20
+ The first stable native 0.2 release compiles closed v2 manifests to SQL and
21
+ TypeScript for Bun SQLite/PostgreSQL and Cloudflare D1/PostgreSQL. It ships
22
+ three matching packages: Core, the pure UI kit and the optional application-built
23
+ Admin. Applications own routing, verified identity, database handles, migrations
24
+ and deployment; selected REST, MCP, public pages, publishing, translations and
25
+ media compose around the generated operations.
26
+
27
+ This is a breaking upgrade from 0.1.x: follow the [0.1-to-0.2 migration
28
+ guide](../../upgrade-0.1-to-0.2.md), use a new 0.2 database and explicitly move
29
+ data. From alpha.11, pin the three selected packages to 0.2.0, regenerate and
30
+ review owned output; the stable version commit changes release metadata and
31
+ documentation rather than adding another runtime API.
32
+
33
+ [GitHub release](https://github.com/aotter/mantle/releases/tag/v0.2.0) is the
34
+ complete change history. Known follow-ups remain tracked: [Admin/authoring
35
+ refinements](https://github.com/aotter/mantle/issues/1752), [production social
36
+ and magic-link recipe verification](https://github.com/aotter/mantle/issues/1273)
37
+ and [real Sites guide verification](https://github.com/aotter/mantle/issues/1272).
38
+ Those provider-specific paths are not claimed as verified by local OTP or
39
+ native-host tests.
40
+
41
+ ### Upgrading to 0.2.0
21
42
 
22
43
  Pin every selected `@aotter/mantle`, `@aotter/mantle-ui` and
23
- `@aotter/mantle-admin` dependency to `0.2.0-alpha.8`, refresh the lockfile,
44
+ `@aotter/mantle-admin` dependency to `0.2.0`, refresh the lockfile,
24
45
  then regenerate and check the application's owned artifacts. Keep existing
25
46
  production migrations application-owned; generated initial DDL is for an empty
26
- database. This alpha includes breaking Admin authoring and paging changes.
47
+ database. This release includes breaking Admin authoring and paging changes.
27
48
 
28
49
  1. **Build output and host configuration.** `mantle bundle` writes server
29
50
  modules to `dist/worker` and static files to `dist/client`, including
@@ -112,7 +133,7 @@ Existing alpha.6 applications require a source migration:
112
133
  application code. Review [authorization and Caller wiring](../concepts/runtime-and-adapters.md)
113
134
  and regenerate all owned artifacts before running `generate --check`.
114
135
 
115
- The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0-alpha.8/docs/examples/reference-service) and
136
+ The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0/docs/examples/reference-service) and
116
137
  [app shell](../../examples/app-shell/) demonstrate the current composition.
117
138
  The following earlier-alpha notes are historical; the current handbook and
118
139
  the native migration above supersede their runtime and CLI instructions.
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: The entry point `mantle help` prints first: choose a complete app or generation only, use examples as references, accept before done.
3
+ ---
4
+ # Build with an agent
5
+
6
+ `mantle help` prints this page. `mantle help <topic>` prints one handbook page and ends with the commands for its next page and for every page it links; run those to read further.
7
+ Topics are the handbook paths below (for example `guides/native-admin`) or their last segment when unique (`native-admin`).
8
+ The installed package's `docs/` is the source of every topic, so the text always matches the installed version.
9
+
10
+ ## Choose how much Mantle owns
11
+
12
+ | You want | Do |
13
+ | --- | --- |
14
+ | A complete application | `mantle init [directory]`, then follow its `README.md`. It writes the app shell once: Better Auth email OTP with `admin` and `user`, the Admin console, MCP for both roles, REST, and a member `/sign-in` page. Existing files stop it; `--dry-run` lists what it would write. |
15
+ | Only typed SQL and bindings inside an app you already have | Skip `init`. Write manifests and run `mantle generate <target> <output-directory> <manifest>...`; see `mantle help project-and-cli` and `mantle help typed-queries`. |
16
+
17
+ `init` writes the `cloudflare-d1` shell, the default target. `bun-sqlite`, `bun-postgres` and `cloudflare-postgres`
18
+ generate the same bindings; their app entry is yours (`mantle help service-entry`).
19
+
20
+ `init` writes app code you then own; it never runs again. `generate` writes only `generated/` and Wrangler build keys,
21
+ and overwrites them on every run. When `generate` sees a selected surface (REST, MCP, Admin) whose generated module
22
+ the app entry (`--entry`) never imports, it warns and names the topic that shows the wiring.
23
+
24
+ ## Build from a product brief
25
+
26
+ 1. `mantle init`, install, then read the app's `README.md` and search for `AGENT:`.
27
+ 2. Map the brief to the [four atoms](../concepts/four-atoms.md): Schemas, Views, Procedures, Triggers.
28
+ Decide what each role (`admin`, `user`) does on each surface: Admin, MCP, REST and any visitor pages.
29
+ 3. For each need, find the pattern in `mantle help examples` (the **Combining** table) and read that example's
30
+ manifest and source. Examples are references: take the pattern into your own manifests; do not copy an example
31
+ directory into the app.
32
+ - Business rules (status transitions, approval thresholds, stock, guards, several rows that commit together)
33
+ belong in SQL Procedures, not in handler code around separate writes: `mantle help sql-patterns`, and
34
+ `mantle help sql` for the SQL each dialect accepts.
35
+ - Visitor pages (anything anonymous and indexable) follow `mantle help public-pages`: server-rendered HTML,
36
+ canonical and Open Graph tags, JSON-LD and an answer block on detail pages, Markdown alternates, `/sitemap.xml`,
37
+ `/robots.txt` and `/llms.txt`. A brief that names a site, shop or catalogue for visitors includes all of them.
38
+ 4. `pnpm generate`, implement every handler it lists, `pnpm check`, then `pnpm smoke`.
39
+ 5. Before reporting the app or a feature done, run [role-only acceptance](../guides/agent-workflow-acceptance.md)
40
+ (`mantle help agent-workflow-acceptance`). Scripted smoke is not acceptance. `pnpm accept` starts the app and hands
41
+ each role to a fresh evaluator as a URL that carries that role's session.
42
+
43
+ The first verified sign-in with `ADMIN_EMAIL` becomes the admin. Further administrators are added in Admin:
44
+ **More → Administrators** invites by email, and **More → Members** grants Admin to an existing member.
45
+ [Authentication](../cloudflare/authentication.md#identity-helpers) documents the same operations as an API.
@@ -12,13 +12,27 @@ description: Generation targets, CLI options and ownership of native application
12
12
  | `wrangler.jsonc` | App configuration; generator maintains selected split-build keys |
13
13
  | `dist/` | Disposable bundle output (`dist/worker`, `dist/client`); do not commit |
14
14
 
15
+ ## Accept user-facing work
16
+
17
+ Before reporting any user-facing deliverable as done, run the installed
18
+ version's [independent acceptance
19
+ flow](../guides/agent-workflow-acceptance.md) with a fresh agent on the surface
20
+ its user uses. For MCP work, also run that guide's design audit before choosing
21
+ tool names or writing manifests/handlers.
22
+
15
23
  ## Commands
16
24
 
17
25
  ```sh
26
+ mantle help [topic] # these docs, one page at a time; start here
27
+ mantle init [directory] [--dry-run] # write the cloudflare-d1 app shell once; never overwrites
18
28
  mantle generate <target> <output-directory> <manifest>... [options]
19
29
  mantle bundle <entry> <outdir> [--assets <dir>]
20
30
  ```
21
31
 
32
+ `init` writes application code the app then owns. `generate` writes only generated output and Wrangler build keys,
33
+ on every run; after writing, it follows the `--entry` file's imports and warns for each selected REST, MCP or Admin
34
+ module the entry never imports.
35
+
22
36
  | Target | Dialect | Handle |
23
37
  | --- | --- | --- |
24
38
  | `bun-sqlite` | sqlite | bun:sqlite Database |
@@ -37,7 +51,7 @@ Other handles may adapt to the same [structural driver contracts](../guides/adap
37
51
  | `--wrangler <path>` | Existing JSONC/JSON/TOML config; default finds one or creates `wrangler.jsonc`. TOML receives manual-edit instructions. |
38
52
  | `--entry <path>` | Worker source entry; default `src/worker.ts`. |
39
53
  | `--outdir <path>` | Bundle output; default `dist`. The Worker goes to `<outdir>/worker`. Keep separate from source/generated files. |
40
- | `--assets <path>` | With `--admin`: the static assets directory `mantle bundle` builds Admin into (`<assets>/admin`); default `<outdir>/client`. Written to Wrangler's `assets.directory`, and to the build command when not the default. |
54
+ | `--assets <path>` | With `--admin`: the static assets directory `mantle bundle` builds Admin into (`<assets>/admin`); default `<outdir>/client`. Written to Wrangler's `assets.directory`, and to the build command when not the default. New assets blocks default to `ASSETS`, Worker-first routing and no HTML rewriting; existing blocks keep their binding/routing. |
41
55
 
42
56
  ## Outputs
43
57
 
@@ -3,7 +3,7 @@ description: Generate a small native D1 service and mount an application-owned W
3
3
  ---
4
4
  # Quickstart: a service on Workers
5
5
 
6
- Create an application outside the SDK checkout. Install an exact released `@aotter/mantle` version, TypeScript, Wrangler and Cloudflare Worker types; `node_modules/@aotter/mantle/docs/examples/app-shell/package.json` lists the versions this release is tested with. Read that installed version's handbook; this page describes the native 0.2.0 contract.
6
+ Create an application outside the SDK checkout. Install stable `@aotter/mantle@0.2.0` exactly, TypeScript, Wrangler and Cloudflare Worker types; `node_modules/@aotter/mantle/docs/examples/app-shell/package.json` lists the versions this release is tested with. Read that installed version's handbook; this page describes the native 0.2.0 contract.
7
7
 
8
8
  Save `manifests/tickets.yaml`:
9
9
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: develop
3
- description: Work on an existing Mantle 0.2 project — manifests, SQL Views and Procedures, ref handlers, the service entry, surfaces, identity and MCP — using the installed package's contract.
3
+ description: Work on an existing Mantle 0.2 project — manifests, SQL Views and Procedures, ref handlers, the service entry, surfaces, identity, MCP tool design and independent acceptance — using the installed package's contract.
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: docs/skills/develop/SKILL.md
@@ -12,28 +12,49 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
16
17
 
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
22
-
23
- Use the native Store/query/surface entries and app-owned fetch from the service composition guide.
18
+ Use the native Store/query/surface entries and app-owned fetch from the [service
19
+ composition guide](../../handbook/cloudflare/service-entry.md).
20
+ Pair every HTTP Trigger with an MCP Trigger by default; see [Trigger](../../handbook/reference/trigger.md)
21
+ for the generate warning and deliberate asymmetry exception.
24
22
  Keep optional modules out of unused entry imports and auth dynamically loaded.
25
23
  Run actual public/member/admin HTTP, MCP and UI scenarios; verify stored state,
26
24
  visibility, OCC and restart persistence, not just a 200 response.
25
+ Before reporting any new app or feature as done, follow the [independent
26
+ acceptance guide](../../handbook/guides/agent-workflow-acceptance.md): write a
27
+ private rubric, then give one fresh agent per role (`admin`, `user`) only its
28
+ role and entry points, across every surface it uses. This is not limited to
29
+ MCP; if it was not run, say so explicitly and report the work as UNVERIFIED
30
+ rather than done. For MCP workflows, also run the guide's design audit before implementing
31
+ tools.
27
32
 
28
- For native Admin, read the [current guide](../../handbook/guides/native-admin.md).
29
- Select `--admin` / `admin: true`; mount `entries(bindings.schemas, bindings.adapter)`
30
- from the optional Admin package. Keep one Core SDK, the Admin product and shared
31
- UI kit at matching versions. Generate → typecheck → implement listed handlers →
32
- typecheck. Ref handlers retain caller-bound `ctx.store`; generic Admin reads
33
- use shared generated View templates and writes use native Procedure fragments.
33
+ For native Admin, read the [current guide](../../handbook/guides/native-admin.md):
34
+ the app builds Admin with `mantle bundle`. To customise pages, slots, hooks or
35
+ MCP-host-safe components, follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin).
36
+ Keep matching SDK, Admin and UI versions. Ref handlers retain caller-bound
37
+ `ctx.store`; generic Admin reads use shared generated View templates and writes
38
+ use native Procedure fragments.
34
39
  Check actual member/Admin OTP, private scope/TTL/translation visibility, paging,
35
40
  statistics, safe/exact OCC and no automatic retry after unknown 503 outcomes.
36
41
 
42
+ For application-owned MCP App entries and host checks, follow the [MCP Apps
43
+ guide](../../handbook/guides/mcp-apps.md).
44
+
45
+ Build a custom frontend with `App` from `@modelcontextprotocol/ext-apps`: register
46
+ `ontoolinput`/`ontoolresult` before `connect()`, and use `callServerTool` for
47
+ host-proxied calls. Keep protocol negotiation and message routing in the official
48
+ client; importing its types while writing a second `postMessage` bridge is
49
+ insufficient.
50
+
51
+ For Admin UI upgrades, follow the installed handbook's
52
+ [retained-page recipe](../../handbook/guides/mcp-apps.md#keep-old-conversations-working-after-an-upgrade).
53
+ Keep deployed snapshots in source/artifact storage and pass `previous` to
54
+ `adminMcpApps`; verify old conversations after a changed UI build without host
55
+ tool refresh, reconnection or cache clearing. Keep tool contracts compatible
56
+ through the declared retention window.
57
+
37
58
 
38
59
  Place admin MCP Procedures in Admin deliberately: `spec.uiSchema.collectionAction:
39
60
  <schema-name>` adds a collection-header action; `spec.target: { schema, id,
@@ -51,10 +72,17 @@ The runtime never creates, alters or checks tables. Boot and requests run no DDL
51
72
 
52
73
  For the first install, apply generated `schema.sql`, including selected auth/site DDL, to an empty database. Apply selected seed SQL explicitly.
53
74
 
54
- For every later schema change, the application's coding agent diffs the regenerated `schema.sql`, writes a migration with the host's native tool (`wrangler d1 migrations` or the app's PostgreSQL migration tool), rehearses it on a copy, and applies it before deploying the new code. Cloud executes migrations at deploy time.
75
+ For every later schema change, the application's coding agent diffs the regenerated `schema.sql`, writes a migration with the host's native tool (`wrangler d1 migrations` or the app's PostgreSQL migration tool), rehearses it on a copy, and applies it before deploying the new code.
55
76
 
56
77
  Destructive changes and type changes need explicit data migration. Mantle drops nothing. Initial `schema.sql` is not an upgrade script.
57
78
 
58
79
  ## Data paths
59
80
 
60
81
  Writes use Store `insert` / `update` / `delete`, `batch`, `transaction`, or declared Procedures. Never use raw SQL writes: they bypass scope, TTL, publishing and system-column rules. For reads beyond an id lookup or simple AND filter (joins, aggregates, OR, reuse), declare an internal View so the SQL is reviewable and typed. Store `findById`, `find` and `first` remain suitable for handler lookups and embedding in an existing app. Ref handlers receive `ctx.store` bound to the same Caller and transaction.
82
+
83
+ For simple creates/partial updates, prefer Store-backed ref Procedures; keep
84
+ Procedure `requires`/generated input checks, use only allowed patch fields and
85
+ return safe business fields with the actual returned ID/version. Preserve
86
+ omitted fields and document null semantics. Retain SQL for atomic conditional
87
+ or related writes; D1 batches cannot consume an earlier result. Follow the
88
+ [Store write guidance](../../handbook/guides/typed-queries.md#prefer-store-backed-ref-procedures-for-simple-writes).
@@ -12,13 +12,8 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
16
-
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
22
17
 
23
18
  Keep the application router, database/pool, identity and frontend. Replace
24
19
  Core consumers explicitly and compose only selected generated entries. Resolve
@@ -30,8 +25,8 @@ the configured OAuth resource row (see [OAuth](../../handbook/guides/oauth.md)).
30
25
  Verify cutover with actual app tests.
31
26
 
32
27
  Preserve the app router, verified identity and native resources. For selected
33
- Admin use the [native guide](../../handbook/guides/native-admin.md): three matching
34
- packages, generated checks and `entries(bindings.schemas, bindings.adapter)`.
28
+ Admin, follow the [native guide](../../handbook/guides/native-admin.md) for its
29
+ application-built bundle and service composition.
35
30
  Do not import old `/spec`, `/core` or runtime surface APIs. Review data/role
36
31
  migrations explicitly; full cross-target int64 parity and automated transfer
37
32
  are not this release's upgrade contract.
@@ -13,13 +13,8 @@ metadata:
13
13
 
14
14
  ## Workflow
15
15
 
16
- Read the installed SDK README and version-matched handbook before editing.
17
-
18
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
19
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
20
- all generated output, run the same generation command with `--check`, and keep
21
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
22
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
16
+ Read the installed SDK README, then follow the version-matched [project and CLI
17
+ workflow](../../handbook/start/project-and-cli.md) before editing.
23
18
 
24
19
  Deploy/create resources only within the user's authorization; reuse configured
25
20
  host resources. Apply reviewed and rehearsed migrations before routing traffic;
@@ -30,12 +25,20 @@ as in [Workers assets and secrets](../../handbook/cloudflare/service-entry.md#wo
30
25
  Keep the Workers [public-fetch flags](../../handbook/cloudflare/deploy-and-operate.md#public-fetch-routing),
31
26
  including environment overrides; verify deployed MCP with a real OAuth bearer
32
27
  (`initialize` and `tools/list`), not only a browser session cookie.
28
+ For MCP Apps, also follow [deployed host acceptance](../../handbook/guides/mcp-apps.md#verify-a-deployed-app-in-chatgpt):
29
+ compare the current resource URI and visibly verify a newly invoked render tool.
30
+ Archive deployed Admin pages using the guide's `previous` recipe and carry them
31
+ through clean builds. Test the same old conversation after changing the UI build,
32
+ without reconnecting, refreshing host tools or clearing caches. A deliberate tool
33
+ contract change has its own migration/host rediscovery step. OAuth renewal, `tools/call` success and a
34
+ local official-host replay do not establish that the deployed App rendered.
33
35
  Bun uses native Database/SQL, an app server and scheduler. Verify admin sign-in
34
36
  (role admin), denied access, public reads, a persisted write, MCP and selected UI.
35
37
  Report artifact/resources/checks and keep rollback artifacts.
36
38
 
37
- Use the installed [Native Admin](../../handbook/guides/native-admin.md) asset
38
- contract and `entries(bindings.schemas, bindings.adapter)`. Admin SPA assets
39
- belong to `@aotter/mantle-admin`, while UI supplies the shared kit. Verify both
40
- member and admin OTP/UI flows. Deployment of an authorized application does
41
- not authorize SDK publication; a workspace preview remains a preview.
39
+ For ChatGPT Sites, follow [Mantle on ChatGPT Sites](../../handbook/cloudflare/chatgpt-sites.md).
40
+
41
+ The app builds Admin with `mantle bundle`; serve it as [Native
42
+ Admin](../../handbook/guides/native-admin.md) describes. Verify both member and
43
+ admin OTP/UI flows. Deployment of an authorized application does not authorize
44
+ SDK publication; a workspace preview remains a preview.
@@ -12,24 +12,27 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
16
17
 
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
18
+ For 0.1.x, follow [Upgrade 0.1 to 0.2](../../upgrade-0.1-to-0.2.md): give the
19
+ 0.2 service a new database and move data by export and import; never boot 0.2
20
+ against a 0.1.x database. Between 0.2 versions, apply reviewed migrations
21
+ rehearsed on a copy.
22
22
 
23
- Pin exact target SDK/peer versions and read its release notes and Admin asset
24
- installation instructions. Core-to-native replaces app imports/wiring; it is not
25
- a package-only update. Preserve users, sessions, content and application code.
26
- Review emitted SQL/policy diffs and rehearse explicit data-preserving schema/auth/role
27
- migrations on a copy. Initial schema.sql never authorizes rebuilding production
28
- tables. Verify auth/Admin/MCP/REST, locks and persistence before authorized cutover.
23
+ For the stable 0.2 line, pin every selected Mantle package to `0.2.0`; do not upgrade an existing application by following a moving dist-tag.
29
24
 
30
- Follow [Native Admin](../../handbook/guides/native-admin.md) for the three-package
31
- SDK/Admin/shared-kit split and asset/extension/MCP App subpath moves; old UI
32
- subpaths have no compatibility aliases. Pin only actually published versions,
33
- or explicitly use a matching workspace preview. Recheck member and Admin OTP,
34
- all affected native routes, private field visibility, OCC and unknown outcomes.
35
- Do not publish a package as part of an application dependency update.
25
+ Pin exact target SDK/peer versions and read its release notes and Admin build
26
+ instructions. Core-to-native replaces app imports/wiring; it is not
27
+ a package-only update. For 0.2-to-0.2 updates, preserve users, sessions, content
28
+ and application code; review emitted SQL/policy diffs and rehearse explicit
29
+ data-preserving schema/auth/role migrations on a copy. Initial schema.sql never
30
+ authorizes rebuilding production tables. Verify auth/Admin/MCP/REST, locks and
31
+ persistence before authorized cutover.
32
+
33
+ Follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin)
34
+ for the current application-built Admin; old UI subpaths have no compatibility
35
+ aliases. Pin only actually published versions, or explicitly use a matching
36
+ workspace preview. Recheck member and Admin OTP, all affected native routes,
37
+ private field visibility, OCC and unknown outcomes. Do not publish a package as
38
+ part of an application dependency update.