@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.
- package/README.md +16 -177
- package/dist/build/compiler.d.ts +4 -4
- package/dist/build/compiler.js +3 -3
- package/dist/domain/describe/{chunk-P6PP6VVV.js → chunk-CW7X5Q5T.js} +1 -1
- package/dist/domain/describe/index.js +1 -1
- package/dist/domain/describe/public.d.ts +3 -0
- package/dist/domain/describe/public.js +1 -0
- package/dist/infrastructure/help.js +1 -3
- package/dist/kernel/mcp-apps.d.ts +3 -2
- package/dist/kernel/mcp-apps.js +7 -2
- package/dist/kernel/mcp-handler.js +20 -8
- package/dist/kernel/mcp.d.ts +18 -0
- package/dist/kernel/mcp.js +1 -0
- package/dist/usecase/adminDescriptors.js +1 -1
- package/dist/usecase/generate.js +20 -4
- package/dist/usecase/rest.js +2 -4
- package/docs/examples/README.md +2 -2
- package/docs/examples/app-shell/README.md +1 -1
- package/docs/examples/app-shell/accept.mjs +12 -2
- package/docs/examples/app-shell/package.json +3 -3
- package/docs/handbook/cloudflare/authentication.md +1 -1
- package/docs/handbook/cloudflare/deploy-and-operate.md +20 -8
- package/docs/handbook/cloudflare/media-r2.md +31 -0
- package/docs/handbook/examples/hub.md +1 -1
- package/docs/handbook/guides/agent-setup.md +25 -67
- package/docs/handbook/guides/agent-workflow-acceptance.md +32 -5
- package/docs/handbook/guides/mcp-apps.md +43 -1
- package/docs/{skills/performance/SKILL.md → handbook/guides/performance.md} +53 -32
- package/docs/handbook/guides/size-and-startup.md +2 -2
- package/docs/handbook/guides/typed-queries.md +12 -0
- package/docs/handbook/navigation.json +4 -0
- package/docs/handbook/reference/describe.md +16 -6
- package/docs/handbook/reference/surface.md +10 -0
- package/docs/handbook/releases/index.md +85 -2
- package/docs/{upgrade-0.1-to-0.2.md → handbook/releases/upgrade-0.1-to-0.2.md} +13 -13
- package/docs/handbook/start/agents.md +25 -4
- package/docs/handbook/start/quickstart-worker.md +1 -1
- package/package.json +2 -3
- package/skills/README.md +7 -69
- package/skills/mantle/SKILL.md +15 -81
- package/dist/domain/describe/target-hash.js +0 -1
- package/docs/skills/develop/SKILL.md +0 -100
- package/docs/skills/integrate/SKILL.md +0 -51
- package/docs/skills/media-gc/SKILL.md +0 -109
- package/docs/skills/provision/SKILL.md +0 -44
- 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.
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
codex plugin add mantle@mantle
|
|
16
|
+
npx skills add aotter/mantle@mantle
|
|
24
17
|
```
|
|
25
18
|
|
|
26
|
-
|
|
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
|
-
|
|
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 /
|
|
28
|
+
/plugin marketplace add aotter/mantle
|
|
36
29
|
/plugin install mantle@mantle
|
|
37
30
|
```
|
|
38
31
|
|
|
39
|
-
Cursor and GitHub Copilot
|
|
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
|
|
35
|
+
## 2. Install the SDK and read its docs
|
|
58
36
|
|
|
59
|
-
Use the version the project already pins. For a new project,
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
42
|
+
pnpm add --save-exact @aotter/mantle@<version>
|
|
43
|
+
npx mantle help
|
|
65
44
|
```
|
|
66
45
|
|
|
67
|
-
|
|
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.
|
|
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
|
|
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.
|
|
158
|
-
[performance
|
|
159
|
-
checks apply and holds them to their targets
|
|
160
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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](
|
|
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](
|
|
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](
|
|
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](
|
|
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](
|
|
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](
|
|
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](
|
|
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](
|
|
123
|
-
together with a native migration ([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](
|
|
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](
|
|
129
|
-
page with keysets ([results and paging](
|
|
130
|
-
and replace exact totals on hot paths ([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
|
-
[
|
|
143
|
-
- After any repair, rerun the [local HTTP checks](
|
|
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](
|
|
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](
|
|
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.
|
|
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
|
|
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
|
|
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`:
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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`
|