@aotter/mantle 0.2.1-alpha.3 → 0.2.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.
@@ -39,5 +39,5 @@ shell's allow-all `src/signup.ts`, unless sign-up should be invite-only. Purchas
39
39
 
40
40
  Every example uses two roles: `admin` (the admin layer) and `user`.
41
41
 
42
- [Reference service](https://github.com/aotter/mantle/tree/v0.2.1-alpha.3/docs/examples/reference-service) is the release smoke test for packed packages, not a starting point. Its source lives in the tagged repository; it is not included in the npm docs.
42
+ [Reference service](https://github.com/aotter/mantle/tree/v0.2.1/docs/examples/reference-service) is the release smoke test for packed packages, not a starting point. Its source lives in the tagged repository; it is not included in the npm docs.
43
43
  Projects on 0.1.x move with [the upgrade guide](../handbook/releases/upgrade-0.1-to-0.2.md).
@@ -16,8 +16,8 @@ pnpm types && pnpm check # each builds once (see Which steps build)
16
16
  cp .dev.vars.example .dev.vars
17
17
  pnpm db:init # optional seed.sql for static reference data, then pnpm db:seed (see Seed data)
18
18
  pnpm dev # builds, rebuilds on change; one-time codes print to this log
19
- pnpm smoke # builds; your smoke.mjs on smoke-kit.mjs (see Files you edit)
20
- pnpm accept # builds; hands each role to a fresh evaluator (see Independent acceptance)
19
+ pnpm smoke # builds; runs smoke.mjs, which you write on smoke-kit.mjs (init does not create it)
20
+ pnpm accept # builds; prints a signed-in URL per role for fresh evaluators (see Independent acceptance)
21
21
  pnpm test # Admin MCP App check of the last build: needs Chrome (npx playwright-core install chromium, or MANTLE_TEST_CHROMIUM=<executable>)
22
22
  ```
23
23
 
@@ -15,8 +15,8 @@
15
15
  "accept": "node accept.mjs"
16
16
  },
17
17
  "dependencies": {
18
- "@aotter/mantle": "0.2.1-alpha.3",
19
- "@aotter/mantle-ui": "0.2.1-alpha.3",
18
+ "@aotter/mantle": "0.2.1",
19
+ "@aotter/mantle-ui": "0.2.1",
20
20
  "@better-auth/cimd": "1.7.7",
21
21
  "@better-auth/core": "1.7.7",
22
22
  "@better-auth/mcp": "1.7.7",
@@ -25,7 +25,7 @@
25
25
  "@modelcontextprotocol/ext-apps": "2.0.0",
26
26
  "@modelcontextprotocol/server": "2.3.1",
27
27
  "better-auth": "1.7.7",
28
- "@aotter/mantle-admin": "0.2.1-alpha.3",
28
+ "@aotter/mantle-admin": "0.2.1",
29
29
  "react": "19.3.0",
30
30
  "react-dom": "19.3.0"
31
31
  },
@@ -21,7 +21,7 @@ The driver layer is open: handles are structural contracts, not a registration A
21
21
 
22
22
  Users may supply anything that executes the emitted SQL, including WASM SQLite persisted to IndexedDB, at their own risk. Mantle promises behavior only on supported drivers: bun:sqlite, D1, Bun.SQL and postgres.js. A structural match alone does not prove transaction, result or error semantics.
23
23
 
24
- The dialect layer is closed in 0.2.0: exactly `postgres` and `sqlite`, selected by the generation target. SQL is rendered at generation time. A third engine requires forking the compiler and Store runtime; it is not a dialect plugin.
24
+ The dialect layer is closed in 0.2: exactly `postgres` and `sqlite`, selected by the generation target. SQL is rendered at generation time. A third engine requires forking the compiler and Store runtime; it is not a dialect plugin.
25
25
 
26
26
  ## Store and queries
27
27
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  Use the [Worker quickstart](../start/quickstart-worker.md) for a complete native manifest and entry, [typed queries](../guides/typed-queries.md) for embedding, and [service composition](../cloudflare/service-entry.md) for optional surfaces.
4
4
 
5
- The runnable [reference service](https://github.com/aotter/mantle/tree/v0.2.1-alpha.3/docs/examples/reference-service) exercises the native package integration. See the [generated public website](../../examples/generated-web/README.md) for a native Worker example.
5
+ The runnable [reference service](https://github.com/aotter/mantle/tree/v0.2.1/docs/examples/reference-service) exercises the native package integration. See the [generated public website](../../examples/generated-web/README.md) for a native Worker example.
@@ -1,6 +1,6 @@
1
1
  # Another host or database handle
2
2
 
3
- Mantle has an open driver boundary and exactly two closed dialects in 0.2.0, postgres and sqlite. Choose the generation target whose emitted SQL and handle calls match your host. The app owns its router, resources, lifetime and scheduler.
3
+ Mantle has an open driver boundary and exactly two closed dialects in 0.2, postgres and sqlite. Choose the generation target whose emitted SQL and handle calls match your host. The app owns its router, resources, lifetime and scheduler.
4
4
 
5
5
  PostgreSQL's `Postgres` in `kernel/contracts.ts` needs `unsafe(text, values)` and `begin(options, fn)`. Bun.SQL and postgres.js fit without wrappers. [The node-postgres example](postgres-adapters.md) shows the transaction and result-count contract. SQLite handle contracts are exported by `@aotter/mantle/driver/d1` and `/driver/bun-sqlite`: D1 prepare/bind/all/batch, or SQLite query/exec/transaction. Reads may need only the narrower reader shape.
6
6
 
@@ -85,15 +85,17 @@ rounds below before claiming the user-facing work is done. Keep the author's
85
85
  capability map and worked call sequence out of the fresh agent's prompt: an MCP
86
86
  evaluator must discover the workflow from public tool descriptions and results.
87
87
 
88
- ## Propose the run first
88
+ ## Plan the run
89
89
 
90
- Before the first round, stop and give the user the plan, then wait for a go-ahead:
90
+ The plan the user approves before the build ([Build from a product brief](../start/agents.md#build-from-a-product-brief))
91
+ says in plain words who tries the app and what they try to get done. Behind it, the run
92
+ fixes:
91
93
 
92
94
  - each evaluator you will start: its role (`admin`, `user`, a visitor), the surfaces it uses (Admin, member pages,
93
95
  REST, MCP, visitor pages) and the goals it is asked to finish, in the user's words;
94
96
  - the round limit (at most three) and what triggers a re-run;
95
- - anything the environment needs, such as a browser for a visitor or member page, so a missing one is settled now
96
- rather than reported as BLOCKED later.
97
+ - what the environment needs, such as a browser for a visitor or member page. Provide it yourself (install a headless
98
+ browser if none is there); only what you cannot provide goes to the user now, rather than reported as BLOCKED later.
97
99
 
98
100
  Keep the rubric itself private: the plan names goals, never the checks an evaluator is graded on.
99
101
 
@@ -614,7 +614,7 @@ key's path. The `UI_EXTENSION_*` diagnostics no longer exist.
614
614
 
615
615
  The compiler derives display/filter metadata from Schema `uiSchema`, searchable fields from `searchableFields`, and sortable fields from indexes plus required/non-null fields and selected system columns. It emits per-collection quoted fragments, row policies and bind recipes; Admin uses one shared `nativeEntryPage` implementation, not Store `find`, `search` or `count` for these lists.
616
616
 
617
- Requests select only generated fields and append only supplied predicates. Absent filters are omitted, rather than expressed as `(? IS NULL OR …)`. Sorts and scalar values are checked before the route acquires its DB handle. In 0.2.0 there is no generated Cartesian set of filter variants or variant cap: runtime SQL shapes depend on selected filters, bounded IN lists, search, cursor and order. Only sortable fields are checked for indexes; there is no per-filter-combination index proof. Inspect EXPLAIN for the actual workload.
617
+ Requests select only generated fields and append only supplied predicates. Absent filters are omitted, rather than expressed as `(? IS NULL OR …)`. Sorts and scalar values are checked before the route acquires its DB handle. In 0.2 there is no generated Cartesian set of filter variants or variant cap: runtime SQL shapes depend on selected filters, bounded IN lists, search, cursor and order. Only sortable fields are checked for indexes; there is no per-filter-combination index proof. Inspect EXPLAIN for the actual workload.
618
618
 
619
619
  Store keeps `find({ search })`, `first({ search })` and `count(where?)`. See [Store search and count costs](typed-queries.md#store-reads-and-writes): Admin shares the dialect search, and a View does not make PostgreSQL ILIKE faster.
620
620
 
@@ -35,9 +35,11 @@ skipped part with its reason.
35
35
  A change in one part can move another: an added index slows writes, a shared
36
36
  stylesheet or entry import affects every page. Re-measure what a repair can reach.
37
37
 
38
- ## Propose the pass first
38
+ ## Plan the pass
39
39
 
40
- Before measuring, stop and give the user the plan, then wait for a go-ahead:
40
+ The plan the user approves before the build ([Build from a product brief](../start/agents.md#build-from-a-product-brief))
41
+ says in plain words how much sample data there is and what "fast enough" means for each page.
42
+ Behind it, the pass fixes:
41
43
 
42
44
  - the parts that run and the parts skipped, each with its reason (the table above);
43
45
  - the test data for backend parts: rows per table (for example 1,000,000 RSVPs across 5,000 events and 20,000
@@ -23,7 +23,7 @@ report it.
23
23
  |---|---|---|
24
24
  | First-load JavaScript | Every script a page's HTML loads: `<script src>` files and inline `<script>` text, classic or `type="module"`, plus every file they import statically, recursively. A dynamic `import()` is not followed. CSS, fonts, images and scripts from another origin do not count. | gzip per file |
25
25
  | MCP App HTML document | The whole built App document as served: its inline JavaScript, CSS and markup in one file. | raw bytes and gzip of the file |
26
- | Worker entry static graph | The Worker entry (Wrangler's `main`, such as `dist/worker/worker.js`) plus every chunk it imports statically, recursively. A dynamic `import()` is not followed. | gzip per file; raw bytes beside it |
26
+ | Worker entry static graph | The Worker entry (Wrangler's `main`, `dist/worker/index.js` in an app from `mantle init`) plus every chunk it imports statically, recursively. A dynamic `import()` is not followed. | gzip per file; raw bytes beside it |
27
27
 
28
28
  The 100 KB budget in the [performance pass](performance.md) is first-load JavaScript. It applies to
29
29
  an MCP App too: `mantle bundle` builds the App as one HTML file with its
@@ -91,7 +91,7 @@ node first-load.mjs dist/client/admin/mcp-app.html
91
91
  Worker entry static graph: pass Wrangler's `main`.
92
92
 
93
93
  ```sh
94
- node first-load.mjs dist/worker/worker.js
94
+ node first-load.mjs dist/worker/index.js
95
95
  ```
96
96
 
97
97
  MCP App HTML document:
@@ -128,7 +128,7 @@ dependencies, so they hold only for the version and commit named.
128
128
 
129
129
  | SDK | Source | Admin page first-load JavaScript | MCP App first-load JavaScript | MCP App HTML document |
130
130
  |---|---|---|---|---|
131
- | 0.2.1 | `develop` at `6dafb812`, before the release | 212,102 B gzip in 5 files (211,338 B without `theme.js`) | 193,535 B gzip, inline | 694,170 B raw / 200,457 B gzip |
131
+ | 0.2.1 | `develop` at `1294c672` | 195,827 B gzip in 13 files (195,069 B without `theme.js`) | 167,516 B gzip, inline | 612,324 B raw / 174,501 B gzip |
132
132
 
133
133
  Neither the built-in Admin page nor its MCP App meets the 100 KB first-load
134
134
  budget. Both misses are SDK cost, not your application's: report them as
@@ -4,7 +4,7 @@ description: Eight SQL patterns most apps need, each a complete manifest that co
4
4
  # SQL patterns
5
5
 
6
6
  Each section is a business need, the manifest that meets it, and what the database refuses. Every block here is
7
- generated by the docs check, and `packages/mantle/tests/sql-patterns.test.ts` runs the same manifest on bun:sqlite,
7
+ generated by the docs check, and the SDK's tests run the same manifest on bun:sqlite,
8
8
  local D1 and PostgreSQL through the generated REST boundary. The SQL is the SQLite and D1 spelling; where PostgreSQL
9
9
  differs, the section says so. What each dialect accepts is listed in [the SQL reference](../reference/sql.md).
10
10
 
@@ -1,6 +1,6 @@
1
1
  # Supported declarations
2
2
 
3
- The native 0.2.0 SDK compiles the closed v2 grammar for four targets. Generation diagnostics, rather than historical APIs or release plans, define the checked-out implementation. Existing integrations require explicit app wiring and data migration; initial schema.sql is for an empty database.
3
+ The native 0.2 SDK compiles the closed v2 grammar for four targets. Generation diagnostics, rather than historical APIs or release plans, define the checked-out implementation. Existing integrations require explicit app wiring and data migration; initial schema.sql is for an empty database.
4
4
 
5
5
  ## Supported
6
6
 
@@ -19,7 +19,7 @@ worked examples see [SQL patterns](../guides/sql-patterns.md).
19
19
  | Predicates | `=`, `<>`, `!=`, `<`, `>`, `<=`, `>=`, `AND`, `OR`, `NOT`, `IS [NOT] NULL`, `IS [NOT] DISTINCT FROM`, `[NOT] BETWEEN`, `IN (list)`, `IN (subquery)`, `LIKE`, `[NOT] EXISTS (subquery)` |
20
20
  | Expressions | `+ - * / %`, `\|\|`, `->>` (JSON field), `CASE`, `COALESCE`, `NULLIF`, scalar subqueries, `CAST(… AS text \| int4 \| int8 \| float8 \| bool \| timestamptz \| date \| numeric \| interval)` |
21
21
  | Aggregates | `count`, `sum`, `min`, `max`, `avg` |
22
- | Windows | `row_number()`, `rank()`, `sum()`, `count()` with `OVER (PARTITION BY … ORDER BY …)` and the default frame |
22
+ | Windows (Views only) | `row_number()`, `rank()`, `sum()`, `count()` with `OVER (PARTITION BY … ORDER BY …)` and the default frame. A Procedure refuses them; rank there with a correlated `count(*)` subquery |
23
23
  | Scalar functions | `lower`, `upper`, `length`, `abs`, `round`, `substr`, `replace`, `trim`, `ltrim`, `rtrim`, `instr` |
24
24
  | Time | `now()`, `date_trunc('hour' \| 'day' \| 'week' \| 'month' \| 'year', …)`, `extract(year \| month \| day \| dow \| hour FROM …)` |
25
25
  | Mantle | `auth.uid()`, `input.<name>`, `mantle.search`, `mantle.search_rank`, `mantle.near`, `mantle.distance` ([features](features.md)) |
@@ -21,8 +21,7 @@ OFFSET, arbitrary functions/relations, DDL and transaction control are not a rea
21
21
  `mantle.search(<alias>, <text>)` matches the Schema's declared `searchableFields`, and `mantle.search_rank(<alias>)`
22
22
  orders by relevance; qualify both with `mantle.`. An optional search reads
23
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).
24
+ `WHERE mantle.search(p, input.q) ORDER BY mantle.search_rank(p), p.id`; page it with a cursor over the rank and the id.
26
25
 
27
26
  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:
28
27
 
@@ -32,114 +32,116 @@ Pin every package you use to the same exact version and upgrade them together.
32
32
  From 0.1.x, follow [Upgrade 0.1 to 0.2](upgrade-0.1-to-0.2.md) instead: a new database, a reviewed and rehearsed data
33
33
  move, and never booting 0.2 against a 0.1.x database.
34
34
 
35
- ## 0.2.1 — in preparation
36
-
37
- 0.2.1 adds Schema locks: a manifest rule that refuses a row's update or delete
38
- for every caller path (REST, MCP, Admin and Procedures), with Admin showing the
39
- lock before an edit. `spec.publishing.liveEdits` lets an application edit a
40
- published row in place, and the [revisions guide](../guides/revisions.md) now
41
- freezes the live revision with a lock and switches it atomically. It also brings
42
- Admin form and refresh fixes, the media picker for custom inputs and pages
43
- (`@aotter/mantle-admin/media`), `matchTarget` in `@aotter/mantle/describe`, and
44
- dependency updates for current advisories.
45
-
46
- Procedures gain `handler.steps`: a program written one statement per step, where
47
- `onNoRows: conflict` makes a set-op write that writes nothing a `CONFLICT` and
48
- rolls the whole program back ("create only if the parent exists"). Ref handlers
49
- and interactive transactions can use `store.batch` for atomic multi-writes, and
50
- `mantle bundle` under `wrangler dev` reports stale generated output in one line
51
- instead of a failed build.
52
-
53
- ### Upgrading to 0.2.1-alpha.3
54
-
55
- - **Agent guidance has one home, the handbook.** The package no longer ships `docs/skills/`, so the `mantle help
56
- skills/<name>` topics are gone. Their guidance is now in handbook pages:
57
- [performance pass](../guides/performance.md),
58
- [deploy and operate](../cloudflare/deploy-and-operate.md),
59
- [cleaning up stale uploads](../cloudflare/media-r2.md#clean-up-stale-uploads),
60
- [upgrading an application](#upgrade-an-application) and
61
- [adoption in an existing app](../guides/typed-queries.md#adopt-mantle-in-an-existing-application).
62
- `mantle help` reaches all of them from the entry page.
63
- - **The `mantle` skill no longer depends on the SDK version.** It routes to `mantle help`, so
64
- `npx skills add aotter/mantle@mantle` no longer needs a release tag. A skill installed from an earlier tag still
65
- names `docs/skills/` paths; reinstall it.
66
- - **Docs moves.** The 0.1 → 0.2 upgrade guide is now the topic `mantle help upgrade-0.1-to-0.2`. The package README is
67
- an index of topics.
68
- - **The agent workflow checks in with the user.** Once the manifests generate, `pnpm accept --open` opens the Developer
69
- UI (`/admin/dev`) so the user can see the wiring. The agent then stops twice with a plan: before acceptance and
70
- before the performance pass. It finishes with an acceptance summary and a before → after performance summary
71
- ([agents](../start/agents.md)).
72
-
73
- Regenerate to pick up the rest of this release. No manifest or API changes:
74
-
75
- - **Developer UI diagrams.**
76
- - The data model is a real ER diagram: keys, types and crow's-foot cardinality, with three display modes.
77
- - System wiring is laid out in columns, with a minimap.
35
+ ## 0.2.1 (2026-10-30)
36
+
37
+ 0.2.1 adds **Schema locks**: a manifest rule that refuses a row's update or delete on every caller path (REST, MCP,
38
+ Admin and Procedures), and Admin shows the lock before an edit. `spec.publishing.liveEdits` lets an application edit a
39
+ published row in place. The [revisions guide](../guides/revisions.md) now freezes the live revision with a lock and
40
+ switches it atomically.
41
+
42
+ Procedures gain `handler.steps`, a program written one statement per step. With `onNoRows: conflict`, a set-op write
43
+ that writes nothing becomes a `CONFLICT` and rolls the whole program back ("create only if the parent exists"). Ref
44
+ handlers and interactive transactions can use `store.batch` for atomic multi-writes. Hand-built MCP tools can attach
45
+ listing and result metadata and page `tools/list`.
46
+
47
+ For agents and for the people working with them:
48
+
49
+ - `mantle help` is the single source of guidance, and the `mantle` skill no longer depends on the SDK version.
50
+ - An agent building from a brief first replies with one plan, written in business terms, and asks what the brief
51
+ leaves open. After one go-ahead, it builds, shows the Developer UI, runs acceptance and a performance pass, and
52
+ reports what each found and changed.
53
+ - The Developer UI draws a real ER diagram, a columned system wiring graph and one Procedure logic graph.
54
+
55
+ Generated Workers and the Admin start smaller.
56
+
57
+ [GitHub release](https://github.com/aotter/mantle/releases/tag/v0.2.1) is the complete change history. Known
58
+ follow-ups:
59
+
60
+ - mounting an adopter MCP server behind Mantle's caller gate ([#1843](https://github.com/aotter/mantle/issues/1843));
61
+ - tool-catalogue change notifications ([#1810](https://github.com/aotter/mantle/issues/1810));
62
+ - the Admin MCP App, which is still above its 480 KB size reference. About 40% of it is the official MCP SDK and zod,
63
+ which ADR-0029 requires.
64
+
65
+ ### Upgrading from 0.2.0
66
+
67
+ Pin every selected `@aotter/mantle`, `@aotter/mantle-ui` and `@aotter/mantle-admin` dependency to `0.2.1`, refresh the
68
+ lockfile, regenerate, and review the owned output. No DDL changes are required.
69
+
70
+ Breaking changes:
71
+
72
+ - **`@aotter/mantle/describe`** keeps only `matchTarget` and the `AppDescription`, `DescribeTarget` and
73
+ `TargetRequest` types. `describeApp`, `diffApp` and the `@aotter/mantle/target-hash` subpath are removed.
74
+ - Get a description outside the CLI with `mantle describe <target> manifests/*.yaml > describe.json`, run in Node.js
75
+ or Bun (for example in a deploy step), and give the JSON to `matchTarget`. Do not import compiler files from
76
+ `dist`: a Worker cannot compile manifests that contain SQL ([describe](../reference/describe.md)).
77
+ - Use `mantle describe --diff` instead of `diffApp`, and `_meta.targetHash` instead of `targetHash()`.
78
+ - **PostgreSQL `version`:** Store results return `version` as a number, as on SQLite and D1. Procedure results stay
79
+ driver-native.
80
+ - **PostgreSQL serialization failures:** SQLSTATE `40001` and `40P01` from a definitive server refusal are now
81
+ `CONFLICT` with `kind: "serialization"` (409), instead of an unclassified driver error. Nothing is retried
82
+ automatically; read again before deciding to retry.
83
+ - **Admin record form:** clearing an optional field sends `null`, and generated form types for optional fields are
84
+ `T | null`.
85
+ - **REST route precedence** is fixed and most-specific-first. Only overlapping same-method http Triggers with the same
86
+ parameter count can change which one wins. `TRIGGER_PATH_INVALID` and `TRIGGER_PATH_COLLISION` refuse ambiguous
87
+ paths at generation; see [route precedence](../reference/trigger.md#route-precedence).
88
+ - **Agent guidance moved into the handbook.** The package no longer ships `docs/skills/`, so the `mantle help
89
+ skills/<name>` topics are gone. Their guidance now lives in these pages, and `mantle help` reaches all of them from
90
+ its entry page:
91
+ - [performance pass](../guides/performance.md);
92
+ - [deploy and operate](../cloudflare/deploy-and-operate.md);
93
+ - [cleaning up stale uploads](../cloudflare/media-r2.md#clean-up-stale-uploads);
94
+ - [upgrading an application](#upgrade-an-application);
95
+ - [adoption in an existing app](../guides/typed-queries.md#adopt-mantle-in-an-existing-application).
96
+
97
+ The 0.1 → 0.2 upgrade guide is the topic `mantle help upgrade-0.1-to-0.2`. A `mantle` skill installed from an
98
+ earlier tag still names `docs/skills/` paths; reinstall it with `npx skills add aotter/mantle@mantle`.
99
+ - **Revisions recipe:** an application that copied the guide's `switch-revision` handler should adopt the new version.
100
+ It requires `expectedRevisionVersion` from the preview, and seals and switches in one `ctx.store.batch`.
101
+ - **Tooling:** the templates move to Wrangler 4.153.0 and `@cloudflare/workers-types` 5.20261027.1.
102
+
103
+ New, opt-in:
104
+
105
+ - **Schema locks and live edits:** see [revisions](../guides/revisions.md).
106
+ - **`handler.steps` and `onNoRows: conflict`.** Ref handlers and interactive transactions can also use
107
+ `store.batch` for atomic multi-writes.
108
+ - **Hand-built MCP tools** ([MCP Apps](../guides/mcp-apps.md), [surface](../reference/surface.md)):
109
+ - `Tool.meta` merges into the tool's listed `_meta`. `ui.visibility: ["app"]` can hide a Procedure from clients
110
+ without MCP Apps; it is a listing hint, and calls are authorized as before.
111
+ - `withResultMeta(data, meta)` returns `meta` only in the result `_meta`, so a value such as a signed upload URL stays
112
+ out of `content`. `callTool` returns `{ data, _meta }`; drop `_meta` before forwarding a result to a model.
113
+ - `toolsPageSize` pages `tools/list` with an opaque `nextCursor`.
114
+ - **Admin media picker** for custom inputs and pages: `@aotter/mantle-admin/media`.
115
+
116
+ Picked up by regenerating, with no manifest or API change:
117
+
118
+ - **Agent workflow** ([Build from a product brief](../start/agents.md#build-from-a-product-brief)). The agent works
119
+ in four phases:
120
+ 1. Before anything else, it replies with one plain-language plan: what each kind of user can do, the rules the app
121
+ enforces, who will try it, how much sample data, and what counts as fast enough. The plan ends with the open
122
+ questions, each with a default answer.
123
+ 2. After one go-ahead, it builds and opens the Developer UI (`pnpm accept --open`) in the user's browser.
124
+ 3. It runs role-only acceptance and the performance pass without stopping.
125
+ 4. It ends with an acceptance summary and a before → after performance summary.
126
+ - **Developer UI diagrams:**
127
+ - The data model is an ER diagram with keys, types and crow's-foot cardinality, in three display modes, with a
128
+ minimap.
129
+ - System wiring is laid out in columns, also with a minimap.
78
130
  - A Procedure's logic is one graph, the same in the wiring side panel and on the Procedure page: a transaction box,
79
131
  CASE as ordered branch rows, and COMMIT and ROLLBACK ends. The SQL syntax trees stay available, folded, on the
80
132
  Procedure page.
81
- - Their wording is translated in every Admin language that has translations.
82
- - **Smaller startup.**
83
- - Generated Workers emit a repeated SQL statement once. REST loads a Procedure on its first request, the way views and
84
- MCP already did. The reference D1 Worker's startup is about 20% smaller.
85
- - Admin loads sign-in, OAuth consent and the collection list on demand.
86
- - The Admin MCP App uses native select and popover controls.
87
-
88
- ### Upgrading to 0.2.1-alpha.2
89
-
90
- Pin the selected packages to `0.2.1-alpha.2` and regenerate. Breaking change:
91
-
92
- - **`@aotter/mantle/describe`** keeps only `matchTarget` and the
93
- `AppDescription`, `DescribeTarget` and `TargetRequest` types. `describeApp`,
94
- `diffApp` and the `@aotter/mantle/target-hash` subpath are removed.
95
- **`mantle describe` output is the supported way to get a description outside
96
- the CLI:** run `mantle describe <target> manifests/*.yaml > describe.json` in
97
- Node.js or Bun (for example in a deploy step) and give the JSON to
98
- `matchTarget`. Do not import compiler files from `dist`; a Worker cannot
99
- compile manifests that contain SQL ([describe](../reference/describe.md)).
100
- Use `mantle describe --diff` instead of `diffApp`, and `_meta.targetHash`
101
- instead of `targetHash()`.
102
-
103
- Hand-built MCP tools gain three opt-in capabilities; without them the output
104
- is unchanged ([MCP Apps](../guides/mcp-apps.md), [surface](../reference/surface.md)):
105
-
106
- - `Tool.meta` merges into the tool's listed `_meta`. `ui.visibility: ["app"]`
107
- can now hide a Procedure from clients without MCP Apps; it is a listing hint,
108
- and calls are authorized as before.
109
- - `withResultMeta(data, meta)` returns `meta` only in the result `_meta`, so a
110
- value such as a signed upload URL stays out of `content`. `callTool` returns
111
- `{ data, _meta }`; drop `_meta` before forwarding a result to a model.
112
- - `toolsPageSize` pages `tools/list` with an opaque `nextCursor`.
113
-
114
- Also: generation reports a SQL parser that cannot load in the current runtime
115
- as such, instead of `SQL_SYNTAX`, and the handbook renders under Vue-based
116
- site generators (bare `<placeholder>` text is now quoted, and `pnpm check`
117
- fails on any that return).
118
-
119
- ### Upgrading to 0.2.1-alpha.1
120
-
121
- Pin `@aotter/mantle`, and any selected `@aotter/mantle-ui` and
122
- `@aotter/mantle-admin`, to `0.2.1-alpha.1`, refresh the lockfile, regenerate and
123
- review owned output. No DDL changes are required. Breaking changes:
124
-
125
- - **PostgreSQL `version`:** Store results return `version` as a number, as on
126
- SQLite and D1. Procedure results stay driver-native.
127
- - **Admin record form:** clearing an optional field sends `null`; generated
128
- form types for optional fields are `T | null`.
129
- - **REST route precedence** is fixed and most-specific-first. Only overlapping
130
- same-method http Triggers with the same parameter count can change winner.
131
- `TRIGGER_PATH_INVALID` and `TRIGGER_PATH_COLLISION` refuse ambiguous paths
132
- at generation; see [route precedence](../reference/trigger.md#route-precedence).
133
- - **Tooling:** the templates move to Wrangler 4.153.0 and
134
- `@cloudflare/workers-types` 5.20261027.1.
135
- - **PostgreSQL serialization failures:** SQLSTATE `40001` and `40P01` from a
136
- definitive server refusal are now `CONFLICT` with `kind: "serialization"`
137
- (409), instead of an unclassified driver error. Nothing is retried
138
- automatically; read again before deciding to retry.
139
- - **Revisions recipe:** an application that copied the guide's
140
- `switch-revision` handler should adopt the new version. It requires
141
- `expectedRevisionVersion` from the preview and seals and switches in one
142
- `ctx.store.batch`.
133
+ - All of their wording is translated in every Admin language that has translations.
134
+ - **Smaller startup:**
135
+ - Generated Workers emit a repeated SQL statement once, and REST loads a Procedure on its first request. A generated
136
+ D1 Worker's startup is about 20% smaller.
137
+ - Admin loads sign-in, OAuth consent and the collection list on demand, and the Admin MCP App uses native select and
138
+ popover controls.
139
+ - **Fixes:**
140
+ - Admin forms and refresh;
141
+ - `mantle bundle` under `wrangler dev` reports stale generated output in one line, instead of a failed build;
142
+ - generation reports a SQL parser that cannot load in the current runtime as such, instead of `SQL_SYNTAX`;
143
+ - the handbook renders under Vue-based site generators;
144
+ - dependency updates for current advisories.
143
145
 
144
146
  ## 0.2.0 (2026-10-19)
145
147
 
@@ -25,6 +25,24 @@ the app entry (`--entry`) never imports, it warns and names the topic that shows
25
25
 
26
26
  ## Build from a product brief
27
27
 
28
+ **Plan once, then run to the end.** Before writing anything, reply with one plan and wait for the user's go-ahead.
29
+ Write it in the user's language and in business terms: no Schema, SQL, route, tool or package names.
30
+
31
+ - **What the app does:** what each kind of user can do, and the business rules it enforces. For example: "a full event
32
+ puts new sign-ups on a waiting list, and a cancellation moves the first person on it in".
33
+ - **How you will check it works:** who will try the app and what each of them tries to get done
34
+ ([acceptance](../guides/agent-workflow-acceptance.md#plan-the-run)).
35
+ - **How you will check it is fast:** how much sample data, and what "fast enough" means for each page
36
+ ([performance](../guides/performance.md#plan-the-pass)).
37
+ - **What you will do on the user's machine,** such as starting the app and opening the browser for them and for the
38
+ testers. Say it; do not ask. Ask only for what you cannot do yourself, such as a deployment or a credential.
39
+ - **Questions:** every rule the brief leaves open, such as limits, deadlines, who may see or change what, and what
40
+ happens on a cancellation. Give each one the answer you will assume if the user does not reply.
41
+
42
+ After the go-ahead, do every step below without stopping, and end with the report in the last step. Stop again only
43
+ for what the plan did not cover and needs the user: a credential, a deployment, a destructive action or a change of
44
+ scope.
45
+
28
46
  1. `mantle init`, install, then read the app's `README.md` and search for `AGENT:`.
29
47
  2. Map the brief to the [four atoms](../concepts/four-atoms.md): Schemas, Views, Procedures, Triggers.
30
48
  Decide what each role (`admin`, `user`) does on each surface: Admin, MCP, REST and any visitor pages.
@@ -43,16 +61,14 @@ the app entry (`--entry`) never imports, it warns and names the topic that shows
43
61
  the Schemas, Views, Procedures and Triggers and how they connect, with each surface's routes and tools. Tell the user
44
62
  what they are looking at. Keep building while they look; the app rebuilds on every change. Without a browser on the
45
63
  machine, give the user the admin URL it prints.
46
- 6. **Stop and ask before acceptance.** Propose the [acceptance plan](../guides/agent-workflow-acceptance.md#propose-the-run-first)
47
- and wait for the user's go-ahead. Then run [role-only acceptance](../guides/agent-workflow-acceptance.md)
48
- (`mantle help agent-workflow-acceptance`). Scripted smoke is not acceptance. `pnpm accept` starts the app and hands
49
- each role to a fresh evaluator as a URL that carries that role's session. After a repair, the guide's
64
+ 6. **Run [role-only acceptance](../guides/agent-workflow-acceptance.md)** (`mantle help agent-workflow-acceptance`) as
65
+ planned. Scripted smoke is not acceptance. `pnpm accept` starts the app and prints, for each role, a URL that carries
66
+ that role's session; give each URL to a fresh evaluator. After a repair, the guide's
50
67
  [re-verification rule](../guides/agent-workflow-acceptance.md#re-verify-what-a-repair-touches) decides which roles
51
68
  and surfaces run again.
52
- 7. **Stop and ask before the performance pass.** For every role and surface acceptance reports CONVERGED, propose the
53
- [performance plan](../guides/performance.md#propose-the-pass-first) (which parts run, how much test data, which
54
- targets) and wait for the go-ahead; then run the [performance pass](../guides/performance.md). BLOCKED or UNVERIFIED
55
- parts are reported as they are, never optimized. A declined step is reported as not run, never as done.
69
+ 7. **Run the [performance pass](../guides/performance.md)** as planned, for every role and surface acceptance reports
70
+ CONVERGED. BLOCKED or UNVERIFIED parts are reported as they are, never optimized. A step the user declined in the
71
+ plan is reported as not run, never as done.
56
72
  8. **Report what changed, not only that it passed:** the [acceptance summary](../guides/agent-workflow-acceptance.md#summarise-for-the-user)
57
73
  and the [performance summary](../guides/performance.md#summarise-for-the-user), then the layered status.
58
74
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The native 0.2.0 handbook and its three package boundaries.
2
+ description: The native 0.2 handbook and its three package boundaries.
3
3
  ---
4
4
  # Mantle handbook
5
5
 
@@ -40,7 +40,7 @@ module the entry never imports.
40
40
  | `cloudflare-d1` | sqlite | D1 binding/session |
41
41
  | `cloudflare-postgres` | postgres | postgres.js, usually with Hyperdrive |
42
42
 
43
- Other handles may adapt to the same [structural driver contracts](../guides/adapter-guide.md). Dialects are exactly postgres and sqlite in 0.2.0.
43
+ Other handles may adapt to the same [structural driver contracts](../guides/adapter-guide.md). Dialects are exactly postgres and sqlite in 0.2.
44
44
 
45
45
  | Option | Meaning |
46
46
  | --- | --- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.2.1-alpha.3",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "engines": {
@@ -18,7 +18,8 @@ its docs one topic at a time.
18
18
  2. **Work in the user's application.** Read its `package.json` and lockfile for the exact Mantle version and keep using
19
19
  that version's docs until the user asks to upgrade (`mantle help releases/index`). Preserve existing code, identity,
20
20
  versions and resources.
21
- 3. **Stop for the user at the entry page's checkpoints:** show the running Admin and its Developer UI once the manifests
22
- generate, and ask before acceptance and before the performance pass. Never report done without them; end with what
23
- each one found and changed, and each layer as CONVERGED, BLOCKED or UNVERIFIED.
21
+ 3. **Plan once, then run to the end.** For a new app, first reply with the entry page's plan, in plain business terms
22
+ and with the questions the brief leaves open, and wait for one go-ahead. Then build, show the Developer UI, accept
23
+ and measure without stopping. Never report done without acceptance and performance; end with what each found and
24
+ changed, and each layer as CONVERGED, BLOCKED or UNVERIFIED.
24
25
  4. **Deploy, delete remote data or publish nothing unless the user asks for that exact action.**