@aotter/mantle 0.2.0 → 0.2.1-alpha.1

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 (82) hide show
  1. package/README.md +6 -2
  2. package/dist/build/compiler.d.ts +4 -4
  3. package/dist/build/compiler.js +6 -3
  4. package/dist/build/spec/domain/model/ManifestGrammar.d.ts +39 -1
  5. package/dist/build/spec/domain/model/RuntimePlan.d.ts +16 -3
  6. package/dist/build/spec/domain/model/SqlIr.d.ts +30 -0
  7. package/dist/build/spec/kernel/diagnostic.d.ts +1 -1
  8. package/dist/domain/describe/chunk-P6PP6VVV.js +1 -0
  9. package/dist/domain/describe/index.d.ts +1 -0
  10. package/dist/domain/describe/index.js +1 -1
  11. package/dist/domain/describe/match.d.ts +19 -0
  12. package/dist/domain/describe/target-hash.js +1 -1
  13. package/dist/infrastructure/adminEntries.js +19 -3
  14. package/dist/infrastructure/bundle.js +43 -8
  15. package/dist/infrastructure/cli.js +1 -1
  16. package/dist/infrastructure/generate.js +17 -3
  17. package/dist/kernel/binds.d.ts +18 -3
  18. package/dist/kernel/binds.js +1 -1
  19. package/dist/kernel/contracts.d.ts +7 -0
  20. package/dist/kernel/contracts.js +9 -0
  21. package/dist/kernel/driver/bun-sqlite.js +3 -3
  22. package/dist/kernel/driver/d1.d.ts +1 -1
  23. package/dist/kernel/driver/d1.js +6 -3
  24. package/dist/kernel/driver/postgres.d.ts +2 -1
  25. package/dist/kernel/driver/postgres.js +11 -6
  26. package/dist/kernel/mcp-handler.js +2 -1
  27. package/dist/kernel/rest-router.js +3 -5
  28. package/dist/kernel/rest.d.ts +2 -1
  29. package/dist/kernel/rest.js +1 -0
  30. package/dist/kernel/route-pattern.d.ts +26 -0
  31. package/dist/kernel/route-pattern.js +34 -0
  32. package/dist/kernel/store.d.ts +15 -1
  33. package/dist/kernel/store.js +51 -18
  34. package/dist/usecase/adminBindings.js +2 -2
  35. package/dist/usecase/adminClient.js +4 -1
  36. package/dist/usecase/generate.js +7 -5
  37. package/dist/usecase/handlers.js +1 -1
  38. package/dist/usecase/publishing.js +20 -7
  39. package/dist/usecase/rest.js +5 -5
  40. package/dist/usecase/stores.js +8 -3
  41. package/docs/examples/README.md +1 -1
  42. package/docs/examples/app-shell/README.md +58 -10
  43. package/docs/examples/app-shell/package.json +6 -6
  44. package/docs/examples/club-events/manifests/club-events.yaml +2 -0
  45. package/docs/examples/generated-web/seed.sql +5 -4
  46. package/docs/examples/purchasing/manifests/purchasing.yaml +3 -0
  47. package/docs/handbook/cloudflare/media-r2.md +6 -3
  48. package/docs/handbook/cloudflare/service-entry.md +2 -0
  49. package/docs/handbook/concepts/lifecycle-and-locales.md +21 -1
  50. package/docs/handbook/examples/hub.md +1 -1
  51. package/docs/handbook/guides/admin-ui.md +1 -1
  52. package/docs/handbook/guides/agent-setup.md +13 -8
  53. package/docs/handbook/guides/agent-workflow-acceptance.md +129 -19
  54. package/docs/handbook/guides/local-testing.md +50 -5
  55. package/docs/handbook/guides/native-admin.md +218 -11
  56. package/docs/handbook/guides/native-execution.md +5 -4
  57. package/docs/handbook/guides/revisions.md +584 -0
  58. package/docs/handbook/guides/size-and-startup.md +140 -0
  59. package/docs/handbook/guides/sql-patterns.md +47 -1
  60. package/docs/handbook/guides/typed-queries.md +8 -3
  61. package/docs/handbook/navigation.json +4 -0
  62. package/docs/handbook/reference/authorization.md +2 -0
  63. package/docs/handbook/reference/describe.md +42 -0
  64. package/docs/handbook/reference/diagnostics.md +12 -3
  65. package/docs/handbook/reference/procedure.md +4 -4
  66. package/docs/handbook/reference/schema.md +71 -7
  67. package/docs/handbook/reference/sql.md +2 -1
  68. package/docs/handbook/reference/surface.md +3 -2
  69. package/docs/handbook/reference/trigger.md +12 -0
  70. package/docs/handbook/releases/index.md +44 -1
  71. package/docs/handbook/start/agents.md +6 -2
  72. package/docs/handbook/start/project-and-cli.md +4 -0
  73. package/docs/handbook/start/quickstart-worker.md +1 -1
  74. package/docs/skills/develop/SKILL.md +15 -3
  75. package/docs/skills/integrate/SKILL.md +3 -1
  76. package/docs/skills/performance/SKILL.md +169 -0
  77. package/docs/skills/update/SKILL.md +9 -5
  78. package/docs/upgrade-0.1-to-0.2.md +151 -39
  79. package/package.json +9 -9
  80. package/skills/README.md +6 -2
  81. package/skills/mantle/SKILL.md +19 -5
  82. package/dist/domain/describe/chunk-SGMRVDGE.js +0 -1
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The entry point `mantle help` prints first: choose a complete app or generation only, use examples as references, accept before done.
2
+ description: "The entry point `mantle help` prints first: choose a complete app or generation only, use examples as references, accept before done."
3
3
  ---
4
4
  # Build with an agent
5
5
 
@@ -38,7 +38,11 @@ the app entry (`--entry`) never imports, it warns and names the topic that shows
38
38
  4. `pnpm generate`, implement every handler it lists, `pnpm check`, then `pnpm smoke`.
39
39
  5. Before reporting the app or a feature done, run [role-only acceptance](../guides/agent-workflow-acceptance.md)
40
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.
41
+ each role to a fresh evaluator as a URL that carries that role's session. After a repair, the guide's
42
+ [re-verification rule](../guides/agent-workflow-acceptance.md#re-verify-what-a-repair-touches) decides which roles
43
+ and surfaces run again.
44
+ 6. Once acceptance converges, follow the [performance skill](../../skills/performance/SKILL.md): it decides which of
45
+ front end (Lighthouse), cold start and backend load apply, and holds each to a target before you report done.
42
46
 
43
47
  The first verified sign-in with `ADMIN_EMAIL` becomes the admin. Further administrators are added in Admin:
44
48
  **More → Administrators** invites by email, and **More → Members** grants Admin to an existing member.
@@ -59,6 +59,10 @@ Other handles may adapt to the same [structural driver contracts](../guides/adap
59
59
  `--auth` and `--site` modules). `mantle bundle`, which Wrangler runs for `wrangler dev` and every
60
60
  deploy, refuses to build when an input changed since, so stale generated code cannot run; regenerate and rebuild.
61
61
  Commit it with the other generated files.
62
+ Under `wrangler dev` (Wrangler sets `WRANGLER_COMMAND=dev`) the refusal is one line per change instead of a failed
63
+ build: `mantle bundle` removes `<outdir>/worker` and exits 0, so Wrangler reports the entry missing and holds requests.
64
+ Writing `generated/` rebuilds. Other commands, other errors and a build by hand still fail with a nonzero exit.
65
+ To see that form while `wrangler dev` runs, run `pnpm build` (or `mantle bundle` by hand).
62
66
  The bundle also prints notes, never errors, for SQL in the app's own code against Better Auth's tables (read users
63
67
  through `userOf(caller)` or `readIdentity(auth)` instead) and for Better Auth roles beyond `admin` and `user`.
64
68
 
@@ -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 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.
6
+ Create an application outside the SDK checkout. Install `@aotter/mantle@0.2.1-alpha.1` exactly (a prerelease; stable 0.2.0 has its own tagged handbook), 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 contract.
7
7
 
8
8
  Save `manifests/tickets.yaml`:
9
9
 
@@ -25,14 +25,23 @@ visibility, OCC and restart persistence, not just a 200 response.
25
25
  Before reporting any new app or feature as done, follow the [independent
26
26
  acceptance guide](../../handbook/guides/agent-workflow-acceptance.md): write a
27
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
28
+ role and entry points, across every surface it uses. After a repair, re-run
29
+ the scope its [re-verification
30
+ rule](../../handbook/guides/agent-workflow-acceptance.md#re-verify-what-a-repair-touches)
31
+ names and state which evidence you reused. This is not limited to
29
32
  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.
33
+ rather than done. Hand off with the guide's [layered
34
+ report](../../handbook/guides/agent-workflow-acceptance.md#report-done-ness-in-layers):
35
+ an environment block is BLOCKED on its layer, never a product failure. For MCP workflows, also run the guide's design audit before implementing
36
+ tools. Once acceptance converges, follow the [performance skill](../performance/SKILL.md) before reporting done.
32
37
 
33
38
  For native Admin, read the [current guide](../../handbook/guides/native-admin.md):
34
39
  the app builds Admin with `mantle bundle`. To customise pages, slots, hooks or
35
40
  MCP-host-safe components, follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin).
41
+ Custom structured inputs and browser pages can use the optional
42
+ `@aotter/mantle-admin/media` controlled `MediaPicker`: pass `readOnly` and an exact
43
+ `uploadPurpose` (omit for selection only), apply `onSelect` to the local draft,
44
+ and keep its import out of the MCP App entry. See the native Admin media example.
36
45
  Keep matching SDK, Admin and UI versions. Ref handlers retain caller-bound
37
46
  `ctx.store`; generic Admin reads use shared generated View templates and writes
38
47
  use native Procedure fragments.
@@ -80,6 +89,9 @@ Destructive changes and type changes need explicit data migration. Mantle drops
80
89
 
81
90
  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
91
 
92
+ For published-content replacement with preview and rollback, follow `mantle help revisions`: Core supplies OCC, Schema locks and atomic `store.batch`; the recipe binds the previewed revision ID/version and atomically seals it while switching the parent. Applications own retention and review policy; Core does not mandate a preparation step. Keep the previewed version on submit and require another preview after a revision conflict.
93
+ Use its custom Native Admin preview/switch page: freeze the reviewed content and both row versions, then submit them through the authorized operation. Do not make editors copy version numbers or silently replace the reviewed snapshot after a refresh; conflicts require another explicit preview.
94
+
83
95
  For simple creates/partial updates, prefer Store-backed ref Procedures; keep
84
96
  Procedure `requires`/generated input checks, use only allowed patch fields and
85
97
  return safe business fields with the actual returned ID/version. Preserve
@@ -22,7 +22,9 @@ data-preserving schema/auth/role migrations on a copy and retain rollback artifa
22
22
  Initial schema.sql is for an empty database, not an in-place upgrade. Requests run
23
23
  no DDL or storage checks; the only request-time write outside app operations is
24
24
  the configured OAuth resource row (see [OAuth](../../handbook/guides/oauth.md)).
25
- Verify cutover with actual app tests.
25
+ Verify cutover with actual app tests. Before reporting user-facing work done, run
26
+ [independent acceptance](../../handbook/guides/agent-workflow-acceptance.md), then the
27
+ [performance skill](../performance/SKILL.md).
26
28
 
27
29
  Preserve the app router, verified identity and native resources. For selected
28
30
  Admin, follow the [native guide](../../handbook/guides/native-admin.md) for its
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: performance
3
+ description: Make an accepted Mantle 0.2 application fast — Lighthouse for app-authored pages, Worker cold start and first-load budgets, and backend reads and writes under realistic data volume with indexes and internal Views. Use right after independent acceptance converges, or when a user reports a slow page, first request or query.
4
+ metadata:
5
+ source: "@aotter/mantle"
6
+ sourcePath: docs/skills/performance/SKILL.md
7
+ applies_to: mantle 0.2
8
+ projection: package
9
+ ---
10
+
11
+ # Mantle Performance
12
+
13
+ This skill sets direction: when to run, what to aim for, in what order and when
14
+ to stop. It names no tools or commands. Choose the measurement and repair
15
+ methods that fit the application, its host and the tools available to you; the
16
+ targets and rules below are what stay fixed.
17
+
18
+ ## When to run
19
+
20
+ - **After acceptance.** Once [independent acceptance](../../handbook/guides/agent-workflow-acceptance.md)
21
+ reports CONVERGED for a new application or a feature, continue here before
22
+ reporting done. Never optimize behavior that has not been accepted: after
23
+ BLOCKED or UNVERIFIED, report that and stop.
24
+ - **On request.** When a user reports a slow page, slow first request or slow
25
+ query, run only the part that covers it, then check the others it could affect.
26
+
27
+ ## Choose what to run
28
+
29
+ Decide from the manifests, the service entry and the changed files, not from
30
+ guesses about future use. Run every part whose condition holds; record each
31
+ skipped part with its reason.
32
+
33
+ | Run | When the application or change has | Skip when |
34
+ |---|---|---|
35
+ | Front end | Pages a browser opens directly that the application authors: public pages, member pages, a custom frontend, Admin customisations | The only browser UI is SDK-built Admin, unchanged; MCP and REST alone have no page to measure |
36
+ | Cold start | A deployed Worker or service entry: always for a new application; for a change, any added or moved import in the entry, its startup graph, a page's or an MCP App entry's first-load code | The change touched no import, entry, page or MCP App bundle |
37
+ | Backend reads | Views, or Store reads in handlers, whose rows grow with data: lists, search, paging, joins, aggregates, totals, reports | Only id lookups and small, bounded configuration tables |
38
+ | Backend writes | Procedures or Store writes that run often or concurrently: stock, counters, approvals, checkout, imports | Rare single-row administrative writes |
39
+
40
+ A change in one part can move another: an added index slows writes, a shared
41
+ stylesheet or entry import affects every page. Re-measure what a repair can reach.
42
+
43
+ ## Front end
44
+
45
+ **Target:** the highest Lighthouse performance score the page allows, with a
46
+ floor of 90 on mobile and 95 on desktop for every distinct page template, and
47
+ no regression in accessibility, SEO or best practices.
48
+
49
+ - Measure a production build, several runs on mobile and desktop, and report the
50
+ range. Separate what is real from what only the local environment causes
51
+ (no compression or HTTP/2, a cold cache) and say which is which.
52
+ - Measure pages behind sign-in (member pages, Admin) through the role's session
53
+ URL that `pnpm accept` prints
54
+ ([hand each role](../../handbook/guides/agent-workflow-acceptance.md#hand-each-role-to-its-evaluator)),
55
+ never by weakening sign-in.
56
+ - Find the actual bottleneck before changing anything; a trace beats a guess.
57
+ - Repair in this order: what blocks the first paint (render-blocking styles and
58
+ font stylesheets, request chains, unused bytes), then main-thread work (heavy
59
+ scripts, animations, layout of off-screen content), then repeat visits
60
+ (content-hashed assets cached long, HTML revalidated).
61
+ - When a bottleneck appears only because the measuring environment lacks
62
+ something (a GPU, a fast CPU), fix it only in a way that also helps real users
63
+ without it. Never detect the measuring tool to serve it a different page.
64
+ - After every change, prove nothing else moved: the page looks the same, links,
65
+ forms and in-page anchors still work, and the
66
+ [public-page checks](../../handbook/guides/public-pages.md#check-before-you-report-done)
67
+ still pass without JavaScript.
68
+
69
+ ## Cold start
70
+
71
+ **Target:** budgets that force code to load when it is needed, not before.
72
+
73
+ - **Worker:** its startup graph imports only what every request needs; auth, MCP,
74
+ Admin and rarely used routes load on the request that uses them
75
+ ([size and startup](../../handbook/guides/size-and-startup.md)). On Workers the
76
+ startup graph is the entry chunk `mantle bundle` writes under `<outdir>/worker`
77
+ plus the chunks it imports statically ([outputs](../../handbook/start/project-and-cli.md#outputs));
78
+ anything behind a dynamic import is outside it. Report its size locally as the
79
+ *Worker entry static graph*; measure the host's startup time only on
80
+ a deployment the user has authorized, and keep it well under the host's limit
81
+ (aim below 50 ms on Cloudflare).
82
+ - **Pages:** each page's *first-load JavaScript* is at most 100 KB, and only
83
+ what the first paint and first interaction need. Move the rest behind dynamic
84
+ imports, on use or when the browser is idle. An MCP App the application
85
+ authors holds its first-load JavaScript, the inline script text of its one
86
+ HTML file, to the same budget; it renders inside a host, so Lighthouse does
87
+ not measure it. Report the whole *MCP App HTML document* as its own number: it
88
+ also holds CSS and markup, so never compare it with this budget.
89
+ - Use the one definition of each metric in
90
+ [size metrics](../../handbook/guides/size-and-startup.md#size-metrics), with
91
+ its measurement. Where the SDK's own checks measure differently, that page says how.
92
+ - An overrun is a finding to repair by splitting, not a budget to raise. Raise a
93
+ budget only with the user's agreement and record why.
94
+
95
+ ## Backend
96
+
97
+ **Target:** every read and write the application's surfaces actually make stays
98
+ fast at a realistic volume, and no hot path scans a table that grows.
99
+
100
+ - **Data:** seed an isolated local database, never a shared or production one,
101
+ with data shaped like real use: several owners and roles, every state, long
102
+ text where fields allow it, and more than one page of results everywhere. Size
103
+ the largest tables at 100,000 rows or 10 times the expected first-year volume,
104
+ whichever is larger. Seed with SQL straight into that database, setting the
105
+ native columns as [reserved entry columns](../../handbook/reference/schema.md#reserved-entry-columns)
106
+ describes: it is test data, not an application write.
107
+ - **Load:** call every View and Procedure through the same callers and surfaces
108
+ the application uses, as each role. The app's local test harness already
109
+ starts it on a fresh database, signs each role in and calls routes and MCP
110
+ tools as that role ([local HTTP checks](../../handbook/guides/local-testing.md#what-smoke-covers)).
111
+ Record typical and slow-tail latency and
112
+ rows read, then run concurrent writers wherever contention matters. A business
113
+ rule that breaks under concurrency (oversell, double approval, lost update) is
114
+ a correctness defect, not a performance finding: fix it first.
115
+ - **Aim:** hot reads stay in the tens of milliseconds locally at seeded volume,
116
+ and their cost does not grow with table size.
117
+ - **Repair in this order:**
118
+ 1. Read the native query plan of each hot path, using the SQL in its generated
119
+ query module under `generated/` against the seeded database; a full scan of a
120
+ growing table is a finding.
121
+ 2. Add the index that serves it in the Schema's `indexes`
122
+ ([indexes](../../handbook/reference/schema.md#indexes-checks-and-search))
123
+ together with a native migration ([database changes](../develop/SKILL.md#database-changes)).
124
+ Index only what a measured query uses; every index costs writes. Paged Views
125
+ and [Schema locks](../../handbook/reference/schema.md#locks) are index-proven
126
+ already: generation refuses one no index serves.
127
+ 3. Move complex reads (joins, aggregates, OR, reuse) into an
128
+ [internal View](../../handbook/guides/typed-queries.md#declare-an-internal-view),
129
+ page with keysets ([results and paging](../../handbook/reference/view.md#results-and-paging)),
130
+ and replace exact totals on hot paths ([search and totals](../../handbook/reference/view.md#search-and-totals)).
131
+ 4. Re-measure reads and writes after each repair.
132
+
133
+ ## Rules
134
+
135
+ - Speed never buys a lower standard: scope, authorization, OCC, atomicity,
136
+ accessibility, the public-page requirements and every explicit user
137
+ requirement stay intact. The application's writes still go through Store and
138
+ Procedures; only seed data for the isolated test database is inserted directly.
139
+ - Do not lower a target, shrink the seeded data or drop a slow case to pass.
140
+ Report what is left instead.
141
+ - Work locally. Deploying or measuring a deployment follows the
142
+ [provision skill](../provision/SKILL.md) and needs the user's authorization.
143
+ - After any repair, rerun the [local HTTP checks](../../handbook/guides/local-testing.md).
144
+ A repair that changes what a user sees or can do is no longer only a
145
+ performance change: re-run acceptance for the scope the
146
+ [re-verification rule](../../handbook/guides/agent-workflow-acceptance.md#re-verify-what-a-repair-touches)
147
+ names. A changed View or Procedure widens it to every surface that reads or
148
+ writes that data, and so does a `uniqueIndexes` entry. A non-unique
149
+ `indexes` entry and its migration keep the business rules: run `pnpm check`,
150
+ the local HTTP checks and your focused regressions, not acceptance.
151
+ - Report app-added cost and SDK-existing cost separately. Built-in Admin has a
152
+ recorded [baseline](../../handbook/guides/size-and-startup.md#native-admin-baselines)
153
+ per SDK version; while the installed version and Admin entry are unchanged,
154
+ reuse it instead of repeating that experiment each round. Built-in Admin's
155
+ page and MCP App both miss the first-load budget; that is SDK-existing cost,
156
+ not the application's. A target the SDK's cost misses is still PARTIAL, with
157
+ its owner named. Never relabel it PASS.
158
+ - Do not patch vendor or SDK code, remove native Admin features or build a
159
+ general benchmark framework to meet a target.
160
+ - Stop when every target is met, after three rounds, or when two rounds in a row
161
+ bring no measurable gain.
162
+
163
+ ## Report
164
+
165
+ For each part: run or skipped (with the reason), the environment, before and
166
+ after numbers as ranges, what changed and what is left. Close with one verdict
167
+ beside the acceptance verdict: **PASS** (every target that applies is met),
168
+ **PARTIAL** (improved, with the targets still missed and why) or **SKIPPED**
169
+ (nothing applied).
@@ -15,12 +15,16 @@ metadata:
15
15
  Read the installed SDK README, then follow the version-matched [project and CLI
16
16
  workflow](../../handbook/start/project-and-cli.md) before editing.
17
17
 
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.
18
+ For 0.1.x, follow [Upgrade 0.1 to 0.2](../../upgrade-0.1-to-0.2.md). Mantle
19
+ ships no migration tool: plan the data move from
20
+ [Moving data from 0.1.x](../../upgrade-0.1-to-0.2.md#moving-data-from-01x),
21
+ verify each fact against the generated `schema.sql` and the 0.1.4 export, and
22
+ have the plan reviewed. Give the 0.2 service a new database, rehearse the load
23
+ on a copy, never boot 0.2 against a 0.1.x database, and keep exports out of the
24
+ repository. The guide's appendix is historical; do not follow it. Between 0.2
25
+ versions, apply reviewed migrations rehearsed on a copy.
22
26
 
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.
27
+ For the stable 0.2 line, pin every selected Mantle package to `0.2.0`; to try the 0.2.1 prerelease, pin all of them to `0.2.1-alpha.1` and read its release notes. Do not upgrade an existing application by following a moving dist-tag.
24
28
 
25
29
  Pin exact target SDK/peer versions and read its release notes and Admin build
26
30
  instructions. Core-to-native replaces app imports/wiring; it is not
@@ -1,26 +1,141 @@
1
1
  # Upgrading from 0.1.x to 0.2.0 (for AI agents)
2
2
 
3
- For **0.2.0-alpha.7 and later**, use the
4
- [native SDK migration](handbook/releases/index.md#native-sdk-migration-alpha7),
5
- [current CLI](handbook/start/project-and-cli.md) and
6
- [app shell](examples/app-shell/). The application owns routing,
7
- Auth, native handles and database migrations; the former `createMantle` runtime,
8
- host presets, boot-time convergence and CLI flags below have been replaced.
9
- Install the same exact version of `@aotter/mantle`, `@aotter/mantle-ui` and,
10
- when selecting Admin, `@aotter/mantle-admin`. Start with a new database for a
11
- 0.1.x migration and preserve every authorization, scope and OCC requirement.
12
- The remainder describes the earlier alpha contract for historical reference.
13
-
14
- No codemod, and no in-place upgrade of a 0.1.x database. A coding agent moves
15
- one project by hand with this guide. When it and
16
- [ADR-0032](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0032-store-centric-core-and-service-contract.md),
17
- [ADR-0033](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0033-storage-converges-to-the-plan.md),
18
- [ADR-0034](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0034-store-is-authored-as-sql.md),
19
- [ADR-0035](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0035-sql-dialects.md),
20
- [ADR-0036](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0036-host-and-dialect.md) or
21
- [ADR-0038](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0038-bun-native-host.md) disagree, the ADR wins.
22
-
23
- ## Rules
3
+ 0.2.0 replaces 0.1.x's grammar, runtime and storage. There is no codemod, no
4
+ migration tool and no in-place upgrade of a 0.1.x database: a coding agent
5
+ rebuilds the project on 0.2.0 and plans a reviewed data move for it.
6
+
7
+ 1. **Rebuild the project on 0.2.0** with the current handbook: the
8
+ [0.2.0 release notes](handbook/releases/index.md#upgrading-to-020) and the
9
+ [native SDK migration](handbook/releases/index.md#native-sdk-migration-alpha7),
10
+ the [CLI and generated ownership](handbook/start/project-and-cli.md), the
11
+ [app shell](examples/app-shell/) and the [Schema](handbook/reference/schema.md),
12
+ [View](handbook/reference/view.md) and [Procedure](handbook/reference/procedure.md)
13
+ references. The application owns routing, Auth, native handles and database
14
+ migrations. Install the same exact version of `@aotter/mantle`,
15
+ `@aotter/mantle-ui` and, when selecting Admin, `@aotter/mantle-admin`.
16
+ Preserve every authorization, scope and OCC requirement.
17
+ 2. **Move the data** into a new 0.2 database, as
18
+ [Moving data from 0.1.x](#moving-data-from-01x) describes.
19
+
20
+ The [appendix](#appendix-earlier-02-alpha-contract-historical) maps 0.1.x
21
+ manifests to an earlier 0.2 alpha. Its runtime, CLI, handler and data
22
+ instructions are out of date; use it only to recognise what a 0.1.x construct
23
+ meant, never as instructions.
24
+
25
+ ## Moving data from 0.1.x
26
+
27
+ These facts come from the 0.1.4 and 0.2.0 sources. Before relying on one,
28
+ check it against the application's own generated `schema.sql` and its 0.1.4
29
+ export; where they disagree, trust them and stop to ask. Write the plan down
30
+ for human review before it touches production data.
31
+
32
+ - **Rehearse on a copy.** Load a copy of the export into a scratch 0.2
33
+ database, check the result through the app (Views, Admin, sign-in), and only
34
+ then run the reviewed load once into the fresh production database, before
35
+ it takes traffic.
36
+ - **Never boot 0.2 against a 0.1.x database.** Apply the generated `schema.sql`
37
+ (it includes the site tables when the site module is selected) to an empty
38
+ database, then load rows into it.
39
+ - **Keep data files out of the repository.** Exports, dumps and copies hold
40
+ user emails and content: keep them outside the working tree or ignored, and
41
+ never commit them.
42
+
43
+ ### Writing entries: the Store or SQL
44
+
45
+ - **Through the SDK.** The generated `store(db, caller)`, or `ctx.store` in a
46
+ `ref` handler, with a trusted caller such as `{ kind: "system", reason }`
47
+ ([authorization](handbook/concepts/authorization.md)). It accepts no value
48
+ for `id`, `version`, `created_at`, `updated_at`, `author_id`, `status`,
49
+ `published_at` or the scope column, for any caller: it sets them itself, so new IDs, versions,
50
+ timestamps and authors replace the old ones. Publishing goes through the
51
+ generated `<schema>.publish` Procedure, which sets `published_at` to now
52
+ ([lifecycle](handbook/concepts/lifecycle-and-locales.md)). With new IDs,
53
+ remap every `x-mantle-ref`, every `translates.on` key and any ID embedded
54
+ in JSON; `x-mantle-ref` is not a foreign key, so a missed one dangles
55
+ quietly.
56
+ - **Through SQL**, to keep IDs, history and authors: `INSERT` the rows with
57
+ every native column set, as
58
+ [reserved entry columns](handbook/reference/schema.md#reserved-entry-columns)
59
+ lists. This bypasses row policy, so review it like a migration.
60
+
61
+ ### Entry columns on D1
62
+
63
+ | 0.1.4 | 0.2.0 |
64
+ |---|---|
65
+ | `_mantle_id` (UUID text) | `id` (text, unique) |
66
+ | `_mantle_version` (an OCC counter) | `version` |
67
+ | `_mantle_created_at`, `_mantle_updated_at` (epoch **milliseconds**) | `created_at`, `updated_at` (epoch **microseconds**: multiply by 1000). Set `updated_at`: it defaults to `0`, which Admin shows as 1970 |
68
+ | `_mantle_author_id` | `author_id`, the Better Auth `user.id` |
69
+ | `_mantle_status` (on every table) | `status` (`draft`, `published`, `archived`) on publishing Schemas only |
70
+ | none | `published_at` (publishing Schemas): 0.1.4 has no source; choose a value for each published row, `NULL` otherwise |
71
+ | the `scope` field | the same column, text, not null |
72
+ | `date-time` field, ISO text | integer microseconds since the epoch |
73
+ | `date` field, text | integer days since 1970-01-01 |
74
+ | object field (JSON text) | 0.1.4 had no geo type; if you remodel a position as a `format: geo` field, it is two columns, `<field>_lat` and `<field>_lng` |
75
+
76
+ - 0.2 tables on D1 are `STRICT`: a value of the wrong type fails the insert
77
+ instead of being stored.
78
+ - A `numeric(p,s)` field is an integer scaled by 10^s on D1: rescale a 0.1.4
79
+ REAL value before loading it.
80
+ - Schema `checks` run as `BEFORE INSERT` and `BEFORE UPDATE` triggers, so
81
+ loaded rows are checked.
82
+ - Full-text search and geo index tables are maintained by triggers: insert
83
+ only into the Schema tables.
84
+
85
+ ### Better Auth
86
+
87
+ 0.1.4 used Better Auth 1.7.2 with hand-written DDL; 0.2.0 uses 1.7.7, with the
88
+ tables `mantle generate --auth src/auth.ts` adds to `schema.sql`
89
+ ([auth](handbook/cloudflare/authentication.md)).
90
+
91
+ - The 0.2 caller subject is the Better Auth `user.id`. Keep user IDs, so that
92
+ `author_id` and owner scopes still name the right people.
93
+ - Copy `user` and `account` with explicit column lists on both sides; both
94
+ versions store dates as ISO text and booleans as 0/1 on D1. 0.1.4's
95
+ `account` has an `issuer` column and `UNIQUE(issuer, accountId)` that the
96
+ 0.2 table may not have; compare the two DDLs before copying.
97
+ - 0.1.4 roles `owner`, `editor` and `contributor` have no 0.2 meaning. The
98
+ admin layer is Better Auth's admin plugin `adminRoles` (default
99
+ `["admin"]`) and `adminUserIds`
100
+ ([authorization](handbook/reference/authorization.md)); decide each mapping
101
+ deliberately.
102
+ - A 0.1.4 `githubLogin` column must be declared in `user.additionalFields`
103
+ to keep it.
104
+ - Leave sessions, verification rows and OAuth tokens behind; users sign in
105
+ again. Mantle re-creates its OAuth resource rows from configuration
106
+ ([OAuth](handbook/guides/oauth.md)). Check `jwks`, OAuth clients and
107
+ consents against Better Auth 1.7.7 and your secrets and URLs before copying
108
+ any.
109
+ - The admin MCP endpoint moved from `/mcp/staff` to `/mcp/admin`; update MCP
110
+ client URLs.
111
+
112
+ ### Media
113
+
114
+ - `media_assets` has the same columns in 0.2, and its `created_at` stays in
115
+ epoch **milliseconds**. Copy the rows as they are.
116
+ - Each variant keeps its stored `storageKey` and `publicUrl`, so 0.1.x objects
117
+ in the same bucket keep working while their prefixes stay served
118
+ ([media](handbook/cloudflare/media-r2.md)).
119
+ - Do not carry over `pending_media_uploads`; an upload expires after 15
120
+ minutes.
121
+ - `site_config` keeps its `key`/`value` columns; compare its keys with the
122
+ generated `site-seed.sql` before copying.
123
+
124
+ ## Appendix: earlier 0.2 alpha contract (historical)
125
+
126
+ > **Historical, do not follow.** This appendix describes an earlier 0.2
127
+ > alpha: `createMantle`, `runtime.store`, host presets, boot-time convergence
128
+ > and CLI flags that 0.2.0 does not have. The current handbook and
129
+ > [Moving data from 0.1.x](#moving-data-from-01x) replace it. Where it and
130
+ > [ADR-0032](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0032-store-centric-core-and-service-contract.md),
131
+ > [ADR-0033](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0033-storage-converges-to-the-plan.md),
132
+ > [ADR-0034](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0034-store-is-authored-as-sql.md),
133
+ > [ADR-0035](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0035-sql-dialects.md),
134
+ > [ADR-0036](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0036-host-and-dialect.md) or
135
+ > [ADR-0038](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0038-bun-native-host.md)
136
+ > disagreed, the ADR won.
137
+
138
+ ### Rules
24
139
 
25
140
  - Work on a branch. Keep the 0.1.x project running until the 0.2.0 one passes
26
141
  its checks.
@@ -32,10 +147,10 @@ one project by hand with this guide. When it and
32
147
  refused in 0.2.0 and stamps nothing, so each one becomes `scope` or an
33
148
  explicit value in the write (section 3).
34
149
  - Give the 0.2.0 service a new database; never boot it against the 0.1.x
35
- one. Move data by export and import (section 5).
150
+ one. Move data as [Moving data from 0.1.x](#moving-data-from-01x) says.
36
151
  - `mantle generate --check` is the gate. Never hand-edit `.mantle/generated/`.
37
152
 
38
- ## 1. Packages
153
+ ### 1. Packages
39
154
 
40
155
  | 0.1.x | 0.2.0 |
41
156
  |---|---|
@@ -46,7 +161,7 @@ Remove every old package and install `@aotter/mantle` at the exact target
46
161
  version. `mantle generate` names any other package it needs, with the install
47
162
  command; it never installs anything.
48
163
 
49
- ## 2. `mantle.config.json`
164
+ ### 2. `mantle.config.json`
50
165
 
51
166
  0.1.4 had none; 0.1.5-alpha.1 wrote a `version: 1` one. Write version 2:
52
167
 
@@ -72,7 +187,7 @@ command; it never installs anything.
72
187
  - `host` and `dialect` are chosen on the first `mantle generate` (`--host`,
73
188
  `--dialect`); a rerun that asks for another is refused.
74
189
 
75
- ## 3. Manifests
190
+ ### 3. Manifests
76
191
 
77
192
  Set `apiVersion: cms.mantle.aotter.net/v2` on every document; a v1 document
78
193
  is refused.
@@ -81,7 +196,7 @@ is refused.
81
196
  `created_at`, `updated_at`, `author_id`. (`ctx.store` JSON keeps the
82
197
  camelCase names.)
83
198
 
84
- ### Schema
199
+ #### Schema
85
200
 
86
201
  | 0.1.x | 0.2.0 |
87
202
  |---|---|
@@ -113,7 +228,7 @@ which also accepts leading, trailing and doubled hyphens. On a Schema field
113
228
  that must not have them, add a `checks` entry:
114
229
  `"slug NOT LIKE '-%' AND slug NOT LIKE '%-' AND slug NOT LIKE '%--%'"`.
115
230
 
116
- ### View
231
+ #### View
117
232
 
118
233
  A View is one SQL `SELECT` in `spec.sql`, with `input` for its parameters.
119
234
  The SQL is PostgreSQL syntax: it names declared Schemas (never
@@ -134,14 +249,14 @@ Schema reference; do not repeat them.
134
249
  | free-text search over `searchableFields` | `WHERE mantle.search(p, input.q) ORDER BY mantle.search_rank(p)`; an unqualified `search` is refused |
135
250
  | `page` / `show` on the wire | `limit` / `cursor`, `nextCursor` in the response |
136
251
 
137
- ### Procedure
252
+ #### Procedure
138
253
 
139
254
  0.1.x builtin ops are `create`, `update`, `upsert`, `delete` and `archive`.
140
255
 
141
256
  | 0.1.x | 0.2.0 |
142
257
  |---|---|
143
258
  | builtin `create` | `handler: { sql: "INSERT INTO s (a, b) VALUES (input.a, input.b) RETURNING id" }` (on a `publishing` Schema the row starts as a draft, as before) |
144
- | builtin `update` (always version-checked, merges over the stored row) | `UPDATE s SET a = COALESCE(input.a, a), … WHERE id = input.id AND version = input.expectedVersion`. Keep both: without the version the lock is lost; without `COALESCE` an omitted optional field is cleared |
259
+ | builtin `update` (always version-checked, merges over the stored row) | `UPDATE s SET a = COALESCE(input.a, a), … WHERE id = input.id AND version = input.expectedVersion`. Keep both: without the version the lock is lost; without `COALESCE` an omitted optional field is cleared. `COALESCE` also means `null` cannot clear a field; give clearing its own flag input ([clear flag](handbook/guides/sql-patterns.md#change-only-the-fields-given-coalesce), [leave out, replace or clear](handbook/guides/native-admin.md#leave-out-replace-or-clear)) |
145
260
  | builtin `upsert` with `match` | `INSERT … ON CONFLICT (<match columns>) DO UPDATE SET x = EXCLUDED.x`; the conflict columns are a unique index; on a scoped Schema the scope field may be named or left out, Store adds it |
146
261
  | builtin `delete` | `DELETE FROM s WHERE id = input.id AND version = input.expectedVersion` |
147
262
  | builtin `archive` | a `ref` handler: `ctx.store.write([{ update: "s", set: { status: "archived" }, where: { id }, lock: expectedVersion }])` (SQL cannot set `status`) |
@@ -153,7 +268,7 @@ timestamps. On a scoped Schema an `INSERT` names no `id`. Several statements
153
268
  in one `sql` apply together or not at all. `requires.auth` and
154
269
  `requires.guard` are unchanged.
155
270
 
156
- ### Trigger
271
+ #### Trigger
157
272
 
158
273
  - `schedule` cron is POSIX: weekday 0 = Sunday. 0.1.x used Cloudflare's
159
274
  numbering (1 = Sunday), so subtract 1 from every weekday number, in ranges
@@ -164,7 +279,7 @@ in one `sql` apply together or not at all. `requires.auth` and
164
279
  `continue` now blocks the write), an after hook is best effort.
165
280
  - `http`, `mcp` and other `lifecycle` fields keep their shape.
166
281
 
167
- ## 4. Handler code
282
+ ### 4. Handler code
168
283
 
169
284
  A `ref` handler is `(input, ctx) => output` in the service's `handlers` map.
170
285
  The generated types list exactly the plan's refs.
@@ -184,7 +299,7 @@ The generated types list exactly the plan's refs.
184
299
  `ctx.store` is scoped to the caller. `runtime.store` is not scoped; use it
185
300
  only in trusted host code.
186
301
 
187
- ## 5. The Worker and the data
302
+ ### 5. The Worker and the data
188
303
 
189
304
  1. Move the old `src/`, `wrangler.jsonc` (or `.toml`) and `tsconfig.json`
190
305
  aside. `mantle generate` writes the preset (`src/*.ts`, `tsconfig.json`,
@@ -197,13 +312,10 @@ only in trusted host code.
197
312
  4. Admin MCP moves from `/mcp/staff` to `/mcp/admin`. Update MCP client URLs.
198
313
  It admits only the admin layer classified by Better Auth’s admin plugin
199
314
  `adminRoles` (default `["admin"]`), and requires the `mcp` scope for tokens.
200
- 5. Import data into the new database through `runtime.store`. A scoped row
201
- goes through `runtime.store.as(<its owner's caller>)`, so the scope field is
202
- the owner. `status`, `created_at` and `author_id` cannot be imported:
203
- publish with a second update that sets `status`, and accept new
204
- timestamps and author.
315
+ 5. Data: this step described an alpha API that 0.2.0 does not have. Follow
316
+ [Moving data from 0.1.x](#moving-data-from-01x) instead.
205
317
 
206
- ## 6. CLI
318
+ ### 6. CLI
207
319
 
208
320
  | 0.1.x | 0.2.0 |
209
321
  |---|---|
@@ -212,7 +324,7 @@ only in trusted host code.
212
324
  | `mantle generate --output`, `--namespace` | removed: output is always `.mantle/generated/` |
213
325
  | `mantle skills`, the `mantle-harness` bin | removed |
214
326
 
215
- ## 7. Verify
327
+ ### 7. Verify
216
328
 
217
329
  1. `mantle generate` and `mantle generate --check` exit 0.
218
330
  2. The project typechecks against the generated `mantle.ts`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-alpha.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "engines": {
@@ -20,9 +20,9 @@
20
20
  "jsonc-parser": "3.3.1",
21
21
  "@pgsql/types": "18.0.0",
22
22
  "libpg-query": "18.1.5",
23
- "pgsql-deparser": "18.3.8",
23
+ "pgsql-deparser": "18.3.10",
24
24
  "yaml": "^2.9.1",
25
- "zod": "^4.6.5"
25
+ "zod": "^4.6.7"
26
26
  },
27
27
  "exports": {
28
28
  ".": "./dist/infrastructure/generate.js",
@@ -72,7 +72,7 @@
72
72
  "./target-hash": "./dist/domain/describe/target-hash.js"
73
73
  },
74
74
  "peerDependencies": {
75
- "@modelcontextprotocol/server": "2.2.0",
75
+ "@modelcontextprotocol/server": "2.3.1",
76
76
  "@better-auth/core": "1.7.7",
77
77
  "@better-auth/oauth-provider": "1.7.7",
78
78
  "better-auth": "1.7.7"
@@ -92,19 +92,19 @@
92
92
  }
93
93
  },
94
94
  "devDependencies": {
95
- "@modelcontextprotocol/client": "2.2.0",
96
- "@modelcontextprotocol/server": "2.2.0",
95
+ "@modelcontextprotocol/client": "2.3.1",
96
+ "@modelcontextprotocol/server": "2.3.1",
97
97
  "@better-auth/core": "1.7.7",
98
98
  "@better-auth/oauth-provider": "1.7.7",
99
99
  "@better-auth/test-utils": "1.7.7",
100
- "@cloudflare/workers-types": "^5.20261008.1",
100
+ "@cloudflare/workers-types": "^5.20261027.1",
101
101
  "@types/node": "^26",
102
102
  "better-auth": "1.7.7",
103
103
  "bun-types": "1.4.2",
104
104
  "postgres": "3.4.9",
105
105
  "typescript": "^6.0.3",
106
- "wrangler": "4.145.0",
107
- "vitest": "^5.0.1"
106
+ "wrangler": "4.153.0",
107
+ "vitest": "^5.0.4"
108
108
  },
109
109
  "description": "Agent-native app SDK and backend compiler: YAML manifests to native SQL (SQLite, Cloudflare D1, PostgreSQL) and typed TypeScript; optional REST and MCP.",
110
110
  "keywords": [