@aotter/mantle 0.2.1-alpha.1 → 0.2.1-alpha.3

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 (46) hide show
  1. package/README.md +16 -177
  2. package/dist/build/compiler.d.ts +4 -4
  3. package/dist/build/compiler.js +3 -3
  4. package/dist/domain/describe/{chunk-P6PP6VVV.js → chunk-CW7X5Q5T.js} +1 -1
  5. package/dist/domain/describe/index.js +1 -1
  6. package/dist/domain/describe/public.d.ts +3 -0
  7. package/dist/domain/describe/public.js +1 -0
  8. package/dist/infrastructure/help.js +1 -3
  9. package/dist/kernel/mcp-apps.d.ts +3 -2
  10. package/dist/kernel/mcp-apps.js +7 -2
  11. package/dist/kernel/mcp-handler.js +20 -8
  12. package/dist/kernel/mcp.d.ts +18 -0
  13. package/dist/kernel/mcp.js +1 -0
  14. package/dist/usecase/adminDescriptors.js +1 -1
  15. package/dist/usecase/generate.js +20 -4
  16. package/dist/usecase/rest.js +2 -4
  17. package/docs/examples/README.md +2 -2
  18. package/docs/examples/app-shell/README.md +1 -1
  19. package/docs/examples/app-shell/accept.mjs +12 -2
  20. package/docs/examples/app-shell/package.json +3 -3
  21. package/docs/handbook/cloudflare/authentication.md +1 -1
  22. package/docs/handbook/cloudflare/deploy-and-operate.md +20 -8
  23. package/docs/handbook/cloudflare/media-r2.md +31 -0
  24. package/docs/handbook/examples/hub.md +1 -1
  25. package/docs/handbook/guides/agent-setup.md +25 -67
  26. package/docs/handbook/guides/agent-workflow-acceptance.md +32 -5
  27. package/docs/handbook/guides/mcp-apps.md +43 -1
  28. package/docs/{skills/performance/SKILL.md → handbook/guides/performance.md} +53 -32
  29. package/docs/handbook/guides/size-and-startup.md +2 -2
  30. package/docs/handbook/guides/typed-queries.md +12 -0
  31. package/docs/handbook/navigation.json +4 -0
  32. package/docs/handbook/reference/describe.md +16 -6
  33. package/docs/handbook/reference/surface.md +10 -0
  34. package/docs/handbook/releases/index.md +85 -2
  35. package/docs/{upgrade-0.1-to-0.2.md → handbook/releases/upgrade-0.1-to-0.2.md} +13 -13
  36. package/docs/handbook/start/agents.md +25 -4
  37. package/docs/handbook/start/quickstart-worker.md +1 -1
  38. package/package.json +2 -3
  39. package/skills/README.md +7 -69
  40. package/skills/mantle/SKILL.md +15 -81
  41. package/dist/domain/describe/target-hash.js +0 -1
  42. package/docs/skills/develop/SKILL.md +0 -100
  43. package/docs/skills/integrate/SKILL.md +0 -51
  44. package/docs/skills/media-gc/SKILL.md +0 -109
  45. package/docs/skills/provision/SKILL.md +0 -44
  46. package/docs/skills/update/SKILL.md +0 -42
@@ -73,3 +73,34 @@ reconciliation and retry id.
73
73
  Admin's `media` option takes a `MediaStorage` value, so build it where `env` is available (for example once per
74
74
  Worker in the fetch handler). Uploads use presigned S3 PUT URLs: `wrangler dev --local` has an R2 binding but no S3
75
75
  endpoint, so test the upload flow against a real bucket; local tests can cover everything up to the signed URL.
76
+
77
+ ## Clean up stale uploads
78
+
79
+ An upload that was created but never committed leaves objects under `uploads/`. Cleaning them up is an operator task:
80
+ audit by default, and delete only the exact objects the user approves for that exact account and bucket. Create no
81
+ Worker, Cron Trigger, lifecycle rule, table or script for it, and never create, request or store credentials.
82
+
83
+ **Layout.** `POST /admin/api/media/uploads` issues presigned PUT URLs (15 minutes) for
84
+ `uploads/<purpose>/<group>/<role>.<ext>`. Commit copies each checked object to `committed/<group>/<attempt>/<role>.<ext>`
85
+ with `committedAt` metadata and removes the upload keys after the database commit; a rejected group also removes them.
86
+ An old upload key may be leftover cleanup from a successful or uncertain commit, so it is not evidence that the media row
87
+ is absent. Objects outside `uploads/` are never candidates; attempt copies under `committed/` are reconciled with live
88
+ database references separately, under their own authorization.
89
+
90
+ **Preflight.** Resolve the bucket `r2MediaStorage` uses and the purposes in `siteDefaults.media.purposes` from
91
+ `wrangler.jsonc` and the app entry. Confirm the exact account and bucket with the user; when either is ambiguous, ask,
92
+ never match by name. Stop when the app passes no `r2MediaStorage`, declares no purpose, or the connected account lacks
93
+ List/Delete Objects access.
94
+
95
+ **Audit.** For each declared purpose, page through `uploads/<purpose>/` until the listing is not truncated. A candidate
96
+ is more than 24 hours old, has no `committedAt`, and matches
97
+ `uploads/<purpose>/<group>/(primary|alternate|fallback).(png|jpg|webp|avif|gif|bin)` with a declared purpose and a
98
+ `<group>` of letters, digits, `_` or `-`. Skip everything else (committed, unknown prefix, malformed, ambiguous) and
99
+ delete single variants, never a whole group. Report the account, bucket, prefixes, UTC cutoff, candidate object and
100
+ group counts, bytes, skipped count, and a SHA-256 digest of the sorted `key + etag` set; print no keys, group ids,
101
+ filenames, URLs or secrets. A page truncated without a cursor stops the run: never delete from a partial audit.
102
+
103
+ **Apply.** After explicit confirmation of the account, bucket, cutoff, count, bytes and digest, re-run the audit with the
104
+ same cutoff and stop if anything changed. Delete exact keys, at most 1,000 per request, and never send a `prefix`: an
105
+ empty prefix can empty the bucket. Do not retry failed keys automatically. Re-list each prefix and report whether the run
106
+ was audit-only or applied, the before/after counts and bytes, and redacted error codes.
@@ -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.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.
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.
@@ -1,97 +1,55 @@
1
1
  ---
2
- description: Install the Mantle agent plugin or its one skill, hand off to the installed package's own docs, and diagnose missing or stale instructions.
2
+ description: Install the Mantle agent skill, hand off to the installed package's `mantle help`, and diagnose missing or stale instructions.
3
3
  ---
4
4
  # Install and verify agent instructions
5
5
 
6
- Two separate things are installed: the agent's `mantle` skill and the
7
- application's `@aotter/mantle` package. Installing one does not install the
8
- other, and the installed package is always the authority for the version it
9
- carries.
6
+ Two separate things are installed: the agent's `mantle` skill and the application's `@aotter/mantle` package. The skill
7
+ holds no contract and does not depend on the SDK version: it sends the agent to `mantle help`, which prints the docs
8
+ shipped inside the installed package. So the skill can come from the latest repository while the application pins any
9
+ version.
10
10
 
11
11
  ## 1. Install the `mantle` skill
12
12
 
13
- The Mantle plugin carries one OSS authoring skill, `mantle`, and local
14
- acceptance references. It does not configure a hosted-service connection.
15
- Install from the Git tag matching the exact SDK version you selected (currently
16
- `v0.2.1-alpha.1`). Pin the tag to keep instructions aligned with the application's
17
- SDK. A skill install does not upgrade the application's SDK.
18
-
19
- For Codex, pin the marketplace checkout:
13
+ Any agent that reads skills:
20
14
 
21
15
  ```sh
22
- codex plugin marketplace add aotter/mantle --ref v0.2.1-alpha.1
23
- codex plugin add mantle@mantle
16
+ npx skills add aotter/mantle@mantle
24
17
  ```
25
18
 
26
- For Claude Code, first clone the release into a directory you will keep:
19
+ The explicit name excludes the repository's maintainer skills. As a plugin, which carries the same one skill and no
20
+ hosted-service connection:
27
21
 
28
22
  ```sh
29
- git clone --depth 1 --branch v0.2.1-alpha.1 https://github.com/aotter/mantle.git mantle-plugin
23
+ codex plugin marketplace add aotter/mantle
24
+ codex plugin add mantle@mantle
30
25
  ```
31
26
 
32
- Then, in Claude Code, use that checkout's absolute path:
33
-
34
27
  ```text
35
- /plugin marketplace add /absolute/path/to/mantle-plugin
28
+ /plugin marketplace add aotter/mantle
36
29
  /plugin install mantle@mantle
37
30
  ```
38
31
 
39
- Cursor and GitHub Copilot use their plugin manifests from the same tagged
40
- checkout. Without a plugin host, install only the consumer skill by name:
41
-
42
- ```sh
43
- npx skills add aotter/mantle@mantle
44
- ```
45
-
46
- Without a version it follows `main`, the latest stable release. To match the
47
- application's SDK exactly, add its release tag: `npx skills add aotter/mantle#v0.2.1-alpha.1@mantle`.
48
-
49
- When upgrading the SDK, update the marketplace ref or local tagged checkout and
50
- refresh/reinstall the plugin using the host's native workflow. Verify the loaded
51
- manifest version and skill source. Do not reset a checkout containing local edits.
52
-
53
- Read the path the installer prints (for project-local Codex,
54
- `.agents/skills/mantle/SKILL.md`). The skill installs no SDK, creates no
32
+ Cursor and GitHub Copilot read their plugin manifests from the same repository. The skill installs no SDK, creates no
55
33
  project and copies no handbook.
56
34
 
57
- ## 2. Install the SDK, then read its own docs
35
+ ## 2. Install the SDK and read its docs
58
36
 
59
- Use the version the project already pins. For a new project, install this
60
- handbook's version exactly. 0.2.1-alpha.1 is a prerelease; stable 0.2.0 remains the
61
- production line, and its own tagged handbook describes it.
37
+ Use the version the project already pins. For a new project, pick one exact version from
38
+ [Releases](../releases/index.md) (only plain `X.Y.Z` versions are for production) and pin every selected
39
+ `@aotter/mantle*` package to it:
62
40
 
63
41
  ```sh
64
- pnpm add --save-exact @aotter/mantle@0.2.1-alpha.1
42
+ pnpm add --save-exact @aotter/mantle@<version>
43
+ npx mantle help
65
44
  ```
66
45
 
67
- Add optional `@aotter/mantle-ui@0.2.1-alpha.1` and `@aotter/mantle-admin@0.2.1-alpha.1` only
68
- when the selected surfaces need them. Every selected `@aotter/mantle*` package
69
- stays at the same exact version. Read, from the application:
70
-
71
- ```text
72
- node_modules/@aotter/mantle/README.md
73
- node_modules/@aotter/mantle/docs/handbook/start/overview.md
74
- node_modules/@aotter/mantle/docs/handbook/reference/features.md
75
- node_modules/@aotter/mantle/docs/skills/<workflow>/SKILL.md
76
- ```
77
-
78
- These ship in the npm tarball and match the installed code. Prefer them over
79
- the skill's GitHub copy and over any online handbook.
80
-
81
- | Package skill | Read it when |
82
- |---|---|
83
- | `docs/skills/develop/SKILL.md` | changing manifests, handlers, the service or its surfaces |
84
- | `docs/skills/integrate/SKILL.md` | adding Mantle to an existing application |
85
- | `docs/skills/update/SKILL.md` | upgrading `@aotter/mantle`, including from 0.1.x |
86
- | `docs/skills/provision/SKILL.md` | deploying, with production sign-in |
87
- | `docs/skills/media-gc/SKILL.md` | auditing or removing stale R2 uploads |
88
- | `docs/skills/performance/SKILL.md` | after acceptance converges, or when a page, first request or query is slow |
89
-
90
- 0.2.0 has no `mantle skills` command: nothing is copied into the project.
91
- An agent reads these files from `node_modules` when the task calls for them.
46
+ `mantle help` prints the installed version's entry page; prefer it over any online copy of this handbook.
92
47
 
93
48
  ## 3. Verify the application
94
49
 
95
- Run the app's exact generation command with `--check`, typecheck, implement every printed handler and typecheck again. Test actual routes and persisted state. With selected Admin, mount `entries(bindings.schemas, bindings.adapter)` and serve assets separately. Verify member/admin OTP, row visibility and OCC. Installing a skill proves none of these flows.
50
+ Run the app's exact generation command with `--check`, typecheck, implement every printed handler and typecheck again.
51
+ Test actual routes and persisted state, then run acceptance as the entry page describes. Installing a skill proves none
52
+ of these flows.
96
53
 
97
- If packaged docs are missing, reinstall the SDK version pinned by the app. If a feature differs from an online page, read the installed docs rather than assuming compatibility.
54
+ If `mantle help` reports missing docs, reinstall the SDK version the app pins. If a feature differs from an online page,
55
+ the installed docs win.
@@ -85,6 +85,18 @@ 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
89
+
90
+ Before the first round, stop and give the user the plan, then wait for a go-ahead:
91
+
92
+ - each evaluator you will start: its role (`admin`, `user`, a visitor), the surfaces it uses (Admin, member pages,
93
+ REST, MCP, visitor pages) and the goals it is asked to finish, in the user's words;
94
+ - 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
+
98
+ Keep the rubric itself private: the plan names goals, never the checks an evaluator is graded on.
99
+
88
100
  ## Run role-only acceptance rounds until they converge
89
101
 
90
102
  The evaluator gets a role, not a task list. It works out what that role needs
@@ -154,10 +166,11 @@ acceptance as BLOCKED and report the scripted checks on their own
154
166
  [layers](#report-done-ness-in-layers). Do not invent
155
167
  an independent result. Do not deploy or publish merely to obtain acceptance.
156
168
 
157
- Acceptance proves the behavior, not its speed. Once a round converges, the
158
- [performance skill](../../skills/performance/SKILL.md) decides which performance
159
- checks apply and holds them to their targets; a performance repair that changes
160
- what a role sees or can do returns here, scoped by the
169
+ Acceptance proves the behavior, not its speed. For every role and surface a
170
+ round reports CONVERGED, the [performance pass](performance.md)
171
+ decides which performance checks apply and holds them to their targets. BLOCKED
172
+ or UNVERIFIED parts are reported as they are, never optimized. A performance
173
+ repair that changes what a role sees or can do returns here, scoped by the
161
174
  [re-verification rule](#re-verify-what-a-repair-touches).
162
175
 
163
176
  ## Re-verify what a repair touches
@@ -300,6 +313,20 @@ reported, layer by layer; it is not done:
300
313
  | 4. External platform | not applicable: no deployment or external host requested | |
301
314
  ```
302
315
 
316
+ ## Summarise for the user
317
+
318
+ Lead the handoff with what acceptance found and fixed, so the user sees the value of the rounds:
319
+
320
+ ```markdown
321
+ | Evaluator (role, surfaces) | Rounds | Found | Fixed in | Final |
322
+ | --- | --- | --- | --- | --- |
323
+ | admin: Admin, admin MCP | 2 | P1: approving a stale event overwrote a newer edit | `approve-event` checks the version | CONVERGED |
324
+ | user: member pages, MCP | 1 | none | | CONVERGED |
325
+ | visitor: public pages | 1 | P2: no link back from an event to the list | `src/routes.ts` | CONVERGED |
326
+ ```
327
+
328
+ Name each finding by its effect on the user, not the code, then give the layered status below.
329
+
303
330
  ## Record evidence in the existing issue or handoff
304
331
 
305
332
  Use ordinary Markdown; no new receipt schema or framework is required:
@@ -317,7 +344,7 @@ Use ordinary Markdown; no new receipt schema or framework is required:
317
344
  - Findings ledger with P0–P3, owner, disposition, minimal repair and
318
345
  regression commands/results.
319
346
  - Coverage of each rubric item (evaluator-observed in this round,
320
- evaluator-observed and reused from round <n> at <revision> with the reason,
347
+ evaluator-observed and reused from round `<n>` at `<revision>` with the reason,
321
348
  author regression only, or UNVERIFIED); the final CONVERGED / BLOCKED / UNVERIFIED verdict and rounds
322
349
  used, which is layer 3 of the [layered report](#report-done-ness-in-layers).
323
350
  Retain every failed report.
@@ -33,6 +33,46 @@ passed through as the resource's `_meta.ui`. `domain` is the dedicated sandbox
33
33
  origin for hosts that serve each App from its own origin; use the form that
34
34
  host documents.
35
35
 
36
+ ### Tool metadata and result `_meta`
37
+
38
+ A hand-built kernel `Tool` may set `meta`, merged into its `_meta` in
39
+ `tools/list` with what App links produce: the top level shallowly, `ui` one key
40
+ deep. Use it for keys such as `ui.visibility`, `ui.resourceUri` with
41
+ `openai/outputTemplate`, or `openai/fileParams`. A value that contradicts an App
42
+ link (another `ui.resourceUri` than the `renders` resource, or a visibility other
43
+ than `["app"]` on an `appOnly` tool) fails when `mcp()` is constructed.
44
+ `meta: { ui: { visibility: ["app"] } }` hides any tool, a Procedure included,
45
+ from clients that declare no MCP Apps support, as `appOnly` does for Views. It
46
+ is a listing hint only: `tools/call` still runs every identity, scope, `check`
47
+ and `authorize` decision, so an App-only write is authorized like any other.
48
+
49
+ A `run` may return `withResultMeta(data, meta)` from `@aotter/mantle/mcp`. The
50
+ result's text and `structuredContent` are built from `data` exactly as before;
51
+ `meta` goes only into the result `_meta`, beside `net.aotter.mantle/tool`, which
52
+ it cannot replace. Hosts give result `_meta` to the App, not the model, so a
53
+ signed upload URL belongs there:
54
+
55
+ ```ts
56
+ import { withResultMeta, type Tool } from '@aotter/mantle/mcp';
57
+
58
+ declare function signUpload(caller: unknown): Promise<{ id: string; url: string; headers: Record<string, string> }>;
59
+
60
+ const uploadTarget: Tool<unknown> = {
61
+ name: 'upload_target', surface: 'public', description: 'Prepare an upload.', closed: true,
62
+ meta: { ui: { visibility: ['app'] } },
63
+ inputSchema: { type: 'object', additionalProperties: false }, validate: raw => raw as Record<string, unknown>,
64
+ run: async (_db, caller) => {
65
+ const upload = await signUpload(caller);
66
+ return withResultMeta({ id: upload.id }, { uploadUrl: upload.url, requiredHeaders: upload.headers });
67
+ },
68
+ };
69
+ ```
70
+
71
+ `callTool` and `callToolLazy` return the wrapper itself, `{ data, _meta }`.
72
+ A caller that forwards a result to a model, such as an application's own agent,
73
+ must forward only `data` and drop `_meta`, or the signed URL leaks through that
74
+ path. A thrown error still produces the standard diagnostics result.
75
+
36
76
  ## Package and application ownership
37
77
 
38
78
  Core (`@aotter/mantle`) owns tools, resources, endpoint links and server authorization,
@@ -46,7 +86,9 @@ source/origin guard is not a protocol implementation.
46
86
 
47
87
  Applications own domain components and build entries. A frontend MCP App uses its
48
88
  own React entry, official ext-apps and optionally mantle-ui, registered through
49
- Core's generic resources. It needs no Admin package or Admin session. See the
89
+ Core's generic resources. It needs no Admin package or Admin session. Build it
90
+ with `App` from `@modelcontextprotocol/ext-apps`: register `ontoolinput`/`ontoolresult` before `connect()` and use
91
+ `callServerTool` for host-proxied calls; never write a second `postMessage` bridge beside the official client. See the
50
92
  [standalone frontend example](../../examples/frontend-mcp-app/README.md) for a
51
93
  small owner-scoped example. `public` names the frontend endpoint, not anonymous
52
94
  access: use the application's verified Caller and generated authorization rules.
@@ -1,26 +1,21 @@
1
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
2
+ description: "After acceptance converges, or when something is slow: front end, cold start and backend load, each held to a target."
9
3
  ---
4
+ # Performance pass
10
5
 
11
- # Mantle Performance
12
-
13
- This skill sets direction: when to run, what to aim for, in what order and when
6
+ This page sets direction: when to run, what to aim for, in what order and when
14
7
  to stop. It names no tools or commands. Choose the measurement and repair
15
8
  methods that fit the application, its host and the tools available to you; the
16
9
  targets and rules below are what stay fixed.
17
10
 
18
11
  ## When to run
19
12
 
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.
13
+ - **After acceptance.** Run for every role and surface that
14
+ [independent acceptance](agent-workflow-acceptance.md)
15
+ reports CONVERGED for a new application or a feature, before reporting done.
16
+ Never optimize behavior that has not been accepted: report each BLOCKED or
17
+ UNVERIFIED role or surface as it is, with what it needs, and leave it out of
18
+ the pass. Stop without running only when no part is CONVERGED.
24
19
  - **On request.** When a user reports a slow page, slow first request or slow
25
20
  query, run only the part that covers it, then check the others it could affect.
26
21
 
@@ -40,6 +35,16 @@ skipped part with its reason.
40
35
  A change in one part can move another: an added index slows writes, a shared
41
36
  stylesheet or entry import affects every page. Re-measure what a repair can reach.
42
37
 
38
+ ## Propose the pass first
39
+
40
+ Before measuring, stop and give the user the plan, then wait for a go-ahead:
41
+
42
+ - the parts that run and the parts skipped, each with its reason (the table above);
43
+ - the test data for backend parts: rows per table (for example 1,000,000 RSVPs across 5,000 events and 20,000
44
+ members), its shape (owners, states, text length) and that it goes only into an isolated local database;
45
+ - the target for each part and the stop rule below;
46
+ - anything that needs the user, such as a deployment to measure real cold start.
47
+
43
48
  ## Front end
44
49
 
45
50
  **Target:** the highest Lighthouse performance score the page allows, with a
@@ -51,7 +56,7 @@ no regression in accessibility, SEO or best practices.
51
56
  (no compression or HTTP/2, a cold cache) and say which is which.
52
57
  - Measure pages behind sign-in (member pages, Admin) through the role's session
53
58
  URL that `pnpm accept` prints
54
- ([hand each role](../../handbook/guides/agent-workflow-acceptance.md#hand-each-role-to-its-evaluator)),
59
+ ([hand each role](agent-workflow-acceptance.md#hand-each-role-to-its-evaluator)),
55
60
  never by weakening sign-in.
56
61
  - Find the actual bottleneck before changing anything; a trace beats a guess.
57
62
  - Repair in this order: what blocks the first paint (render-blocking styles and
@@ -63,7 +68,7 @@ no regression in accessibility, SEO or best practices.
63
68
  without it. Never detect the measuring tool to serve it a different page.
64
69
  - After every change, prove nothing else moved: the page looks the same, links,
65
70
  forms and in-page anchors still work, and the
66
- [public-page checks](../../handbook/guides/public-pages.md#check-before-you-report-done)
71
+ [public-page checks](public-pages.md#check-before-you-report-done)
67
72
  still pass without JavaScript.
68
73
 
69
74
  ## Cold start
@@ -72,9 +77,9 @@ no regression in accessibility, SEO or best practices.
72
77
 
73
78
  - **Worker:** its startup graph imports only what every request needs; auth, MCP,
74
79
  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
80
+ ([size and startup](size-and-startup.md)). On Workers the
76
81
  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));
82
+ plus the chunks it imports statically ([outputs](../start/project-and-cli.md#outputs));
78
83
  anything behind a dynamic import is outside it. Report its size locally as the
79
84
  *Worker entry static graph*; measure the host's startup time only on
80
85
  a deployment the user has authorized, and keep it well under the host's limit
@@ -87,7 +92,7 @@ no regression in accessibility, SEO or best practices.
87
92
  not measure it. Report the whole *MCP App HTML document* as its own number: it
88
93
  also holds CSS and markup, so never compare it with this budget.
89
94
  - Use the one definition of each metric in
90
- [size metrics](../../handbook/guides/size-and-startup.md#size-metrics), with
95
+ [size metrics](size-and-startup.md#size-metrics), with
91
96
  its measurement. Where the SDK's own checks measure differently, that page says how.
92
97
  - An overrun is a finding to repair by splitting, not a budget to raise. Raise a
93
98
  budget only with the user's agreement and record why.
@@ -102,12 +107,12 @@ fast at a realistic volume, and no hot path scans a table that grows.
102
107
  text where fields allow it, and more than one page of results everywhere. Size
103
108
  the largest tables at 100,000 rows or 10 times the expected first-year volume,
104
109
  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)
110
+ native columns as [reserved entry columns](../reference/schema.md#reserved-entry-columns)
106
111
  describes: it is test data, not an application write.
107
112
  - **Load:** call every View and Procedure through the same callers and surfaces
108
113
  the application uses, as each role. The app's local test harness already
109
114
  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)).
115
+ tools as that role ([local HTTP checks](local-testing.md#what-smoke-covers)).
111
116
  Record typical and slow-tail latency and
112
117
  rows read, then run concurrent writers wherever contention matters. A business
113
118
  rule that breaks under concurrency (oversell, double approval, lost update) is
@@ -119,15 +124,15 @@ fast at a realistic volume, and no hot path scans a table that grows.
119
124
  query module under `generated/` against the seeded database; a full scan of a
120
125
  growing table is a finding.
121
126
  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)).
127
+ ([indexes](../reference/schema.md#indexes-checks-and-search))
128
+ together with a native migration ([database changes](../cloudflare/deploy-and-operate.md#install-and-change-schemas)).
124
129
  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
130
+ and [Schema locks](../reference/schema.md#locks) are index-proven
126
131
  already: generation refuses one no index serves.
127
132
  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)).
133
+ [internal View](typed-queries.md#declare-an-internal-view),
134
+ page with keysets ([results and paging](../reference/view.md#results-and-paging)),
135
+ and replace exact totals on hot paths ([search and totals](../reference/view.md#search-and-totals)).
131
136
  4. Re-measure reads and writes after each repair.
132
137
 
133
138
  ## Rules
@@ -139,17 +144,17 @@ fast at a realistic volume, and no hot path scans a table that grows.
139
144
  - Do not lower a target, shrink the seeded data or drop a slow case to pass.
140
145
  Report what is left instead.
141
146
  - 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).
147
+ [deploy guide](../cloudflare/deploy-and-operate.md) and needs the user's authorization.
148
+ - After any repair, rerun the [local HTTP checks](local-testing.md).
144
149
  A repair that changes what a user sees or can do is no longer only a
145
150
  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)
151
+ [re-verification rule](agent-workflow-acceptance.md#re-verify-what-a-repair-touches)
147
152
  names. A changed View or Procedure widens it to every surface that reads or
148
153
  writes that data, and so does a `uniqueIndexes` entry. A non-unique
149
154
  `indexes` entry and its migration keep the business rules: run `pnpm check`,
150
155
  the local HTTP checks and your focused regressions, not acceptance.
151
156
  - 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)
157
+ recorded [baseline](size-and-startup.md#native-admin-baselines)
153
158
  per SDK version; while the installed version and Admin entry are unchanged,
154
159
  reuse it instead of repeating that experiment each round. Built-in Admin's
155
160
  page and MCP App both miss the first-load budget; that is SDK-existing cost,
@@ -160,10 +165,26 @@ fast at a realistic volume, and no hot path scans a table that grows.
160
165
  - Stop when every target is met, after three rounds, or when two rounds in a row
161
166
  bring no measurable gain.
162
167
 
168
+ ## Summarise for the user
169
+
170
+ Report each change with the number it moved, so the gain is visible:
171
+
172
+ ```markdown
173
+ | Part | Change | Before | After |
174
+ | --- | --- | --- | --- |
175
+ | Backend read: `event-roster`, 1,000,000 RSVPs | index `[eventId, created_at]` + keyset paging | p95 1,840 ms | p95 38 ms |
176
+ | Front end: `/events/{slug}` (mobile) | inline critical CSS, sized images | Lighthouse 71 | 94 |
177
+ | Cold start: Worker entry | auth and Admin behind dynamic imports | 212 KB gzip startup graph | 61 KB |
178
+ ```
179
+
180
+ Give the data volume beside every backend number, the device and throttling beside every Lighthouse score, and say
181
+ where a target is still missed and why.
182
+
163
183
  ## Report
164
184
 
165
185
  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
186
+ after numbers as ranges, what changed and what is left. Name the roles and
187
+ surfaces left out because acceptance did not converge on them. Close with one verdict
167
188
  beside the acceptance verdict: **PASS** (every target that applies is met),
168
189
  **PARTIAL** (improved, with the targets still missed and why) or **SKIPPED**
169
190
  (nothing applied).
@@ -10,7 +10,7 @@ Cloudflare startup_time_ms needs an actual upload/deployment; offline dry runs d
10
10
 
11
11
  ## Size metrics
12
12
 
13
- Three metrics, one definition each. The [performance skill](../../skills/performance/SKILL.md)
13
+ Three metrics, one definition each. The [performance pass](performance.md)
14
14
  and the SDK's own checks use these names. Measure the production build that
15
15
  `mantle bundle` writes, never a dev server.
16
16
 
@@ -25,7 +25,7 @@ report it.
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
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 |
27
27
 
28
- The 100 KB budget in the performance skill is first-load JavaScript. It applies to
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
30
30
  JavaScript inline, so its first-load JavaScript is its inline script text. The
31
31
  MCP App HTML document also holds CSS and markup, so do not compare the document
@@ -11,6 +11,18 @@ description: Choose Store for writes and simple lookups; declare internal Views
11
11
 
12
12
  Never write raw SQL to Mantle collections: it bypasses scope, TTL, publishing and system-column rules. The Store applies row rules but does not check a View's or Procedure's `requires`; authorize your handler before using it.
13
13
 
14
+ ## Adopt Mantle in an existing application
15
+
16
+ Keep the application's router, database handle, identity and frontend; declare only the Schemas and operations you
17
+ adopt and import their `views/<encoded-name>.ts` or `procedures/<encoded-name>.ts`. Bun.SQL and postgres.js fit
18
+ directly; node-postgres needs an [adapter](postgres-adapters.md). Resolve one verified Caller per request, with the
19
+ shared cookie-session origin check on writes ([authentication](../cloudflare/authentication.md)). No Admin, auth or
20
+ surface module is required for direct queries.
21
+
22
+ Existing tables need an explicit migration into the declared storage shape, including system columns and constraints;
23
+ never apply the initial `schema.sql` over an occupied database. Rehearse the migration on a copy, keep a rollback, and
24
+ test scope, TTL, publication, OCC and persisted state with the application's own identity and tests.
25
+
14
26
  ## Declare an internal View
15
27
 
16
28
  Save this as `manifests/tickets.yaml`:
@@ -84,6 +84,10 @@
84
84
  {
85
85
  "text": "Size and startup",
86
86
  "link": "/guides/size-and-startup"
87
+ },
88
+ {
89
+ "text": "Performance pass",
90
+ "link": "/guides/performance"
87
91
  }
88
92
  ]
89
93
  },
@@ -5,10 +5,19 @@ An explicit target is required; use the generation target because hashes
5
5
  include the dialect. The command writes no project files. `--diff <old.json>` prints a JSON
6
6
  array of changes against a saved report.
7
7
 
8
- `describeApp(plan)` and `diffApp(a, b)` are exported by
9
- `@aotter/mantle/describe`. The `AppDescription` TypeScript type is the contract.
10
- `targetHash(plan, id)` is also exported by `@aotter/mantle/target-hash`.
11
- These exports use no Node APIs and load no SQL parser.
8
+ This command's output is the supported way to get a description outside the
9
+ CLI. Run it in Node.js or Bun, for example in a deploy step, from the
10
+ committed manifests, and give the saved JSON to whatever needs it. A Worker
11
+ cannot compile manifests that contain SQL, because the SQL parser's WASM needs
12
+ the whole isolate memory (ADR-0034); do not import compiler files from `dist`.
13
+
14
+ ```sh
15
+ mantle describe cloudflare-d1 manifests/*.yaml > describe.json
16
+ ```
17
+
18
+ `@aotter/mantle/describe` exports `matchTarget` and the `AppDescription`,
19
+ `DescribeTarget` and `TargetRequest` types; `AppDescription` is the contract.
20
+ It loads no compiler, SQL parser or Node API.
12
21
 
13
22
  The schemaVersion 1 report contains:
14
23
 
@@ -55,8 +64,9 @@ imports no application, opens no database and makes no network call, so a
55
64
  gateway can compute `props.mantle.target` and `targetHash` before forwarding.
56
65
 
57
66
  ```ts
58
- import { matchTarget } from "@aotter/mantle/describe";
59
- const url = new URL(request.url);
67
+ import { matchTarget, type AppDescription } from "@aotter/mantle/describe";
68
+ import saved from "./describe.json";
69
+ const describe = saved as AppDescription, url = new URL(request.url);
60
70
  matchTarget(describe, { surface: "rest", method: request.method, path: url.pathname }); // rest:GET /api/views/orders
61
71
  matchTarget(describe, { surface: "admin", method: request.method, path: url.pathname }); // admin:GET /entries/:id
62
72
  matchTarget(describe, { surface: "mcp", tool: params.name, path: url.pathname }); // view:orders, admin:tool:<name>
@@ -215,6 +215,16 @@ by declared identity, scope and `check` requirements. Without `authorize`, the
215
215
  complete endpoint catalogue is not a permission-filtered catalogue. Operation
216
216
  validation, guards and credential checks still apply to calls.
217
217
 
218
+ `toolsPageSize` pages `tools/list`: each page holds at most that many tools and
219
+ carries an opaque `nextCursor` while more remain; pass it back as `cursor`. A
220
+ cursor is valid only for the catalogue version that issued it; an unknown or
221
+ stale cursor is answered with JSON-RPC error `-32602`, and the client lists
222
+ again from the first page. Without the option, one page lists every tool and
223
+ carries no `nextCursor`.
224
+
225
+ A hand-built tool's `meta` and a `run` returning `withResultMeta(data, meta)`
226
+ add listed and result `_meta`; see [MCP Apps](../guides/mcp-apps.md#tool-metadata-and-result-_meta).
227
+
218
228
  The app resolves bearer tokens with `resolveCaller(request, auth, { audience:
219
229
  new URL('/mcp', request.url).href })` (both surfaces use `<origin>/mcp`). The app
220
230
  must also mount Better Auth's `/.well-known/oauth-protected-resource/mcp`