@aotter/mantle 0.1.3-alpha.6 → 0.1.4
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 +5 -5
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +4 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/skills.js +2 -2
- package/dist/cli/skills.js.map +1 -1
- package/docs/adr/0001-four-atom-manifest-model.md +3 -2
- package/docs/adr/0013-agent-provisioned-consumer-projects.md +4 -4
- package/docs/adr/0016-site-semantic-layer.md +5 -4
- package/docs/adr/0018-core-starters-repository-boundary.md +1 -1
- package/docs/adr/README.md +2 -2
- package/docs/agent-prompts.md +10 -8
- package/docs/assets/mantle-admin-developer.png +0 -0
- package/docs/consumer-onboarding-audit.md +65 -0
- package/docs/examples/README.md +1 -1
- package/docs/examples/host-chatgpt-sites/README.md +29 -12
- package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +1 -0
- package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +1 -1
- package/docs/examples/host-chatgpt-sites/public/site.css +1 -1
- package/docs/examples/host-chatgpt-sites/scripts/build.mjs +2 -1
- package/docs/examples/host-chatgpt-sites/scripts/check.mjs +2 -0
- package/docs/examples/host-chatgpt-sites/src/index.ts +1 -1
- package/docs/examples/host-chatgpt-sites/src/web.ts +1 -1
- package/docs/examples/host-minimal-worker/README.md +5 -5
- package/docs/handbook/{sites → chatgpt-sites}/equipment-checkout.md +1 -1
- package/docs/handbook/{sites → chatgpt-sites}/index.md +15 -3
- package/docs/handbook/cloudflare/authentication.md +16 -0
- package/docs/handbook/cloudflare/chatgpt-sites.md +6 -6
- package/docs/handbook/cloudflare/deploy-and-operate.md +3 -2
- package/docs/handbook/cloudflare/media-r2.md +2 -2
- package/docs/handbook/concepts/four-atoms.md +1 -1
- package/docs/handbook/concepts/lifecycle-and-locales.md +1 -1
- package/docs/handbook/concepts/mcp-and-agents.md +25 -5
- package/docs/handbook/concepts/procedures-and-triggers.md +1 -1
- package/docs/handbook/concepts/runtime-and-adapters.md +2 -2
- package/docs/handbook/concepts/views.md +9 -8
- package/docs/handbook/guides/admin-ui.md +135 -0
- package/docs/handbook/guides/agent-setup.md +97 -0
- package/docs/handbook/guides/typed-queries.md +161 -0
- package/docs/handbook/navigation.json +28 -18
- package/docs/handbook/reference/features.md +55 -0
- package/docs/handbook/reference/manifest.md +3 -1
- package/docs/handbook/reference/schema.md +1 -1
- package/docs/handbook/reference/surface.md +1 -1
- package/docs/handbook/reference/view.md +4 -2
- package/docs/handbook/releases/index.md +71 -2
- package/docs/handbook/start/overview.md +52 -0
- package/docs/handbook/start/project-and-cli.md +7 -7
- package/docs/handbook/start/quickstart-worker.md +8 -6
- package/docs/release-process.md +3 -2
- package/docs/sealed-pipeline-ownership.md +1 -1
- package/docs/skills/develop/SKILL.md +250 -0
- package/docs/skills/media-gc/SKILL.md +85 -0
- package/docs/skills/plugin/SKILL.md +132 -0
- package/docs/skills/provision/SKILL.md +194 -0
- package/docs/skills/theme/SKILL.md +77 -0
- package/docs/skills/update/SKILL.md +51 -0
- package/docs/spec-only-host-adoption.md +1 -1
- package/package.json +18 -18
- package/skills/README.md +30 -21
- package/skills/develop/SKILL.md +21 -4
- package/skills/install/SKILL.md +40 -8
- package/skills/media-gc/SKILL.md +2 -2
- package/skills/plugin/SKILL.md +19 -7
- package/skills/provision/SKILL.md +3 -3
- package/skills/theme/SKILL.md +10 -3
- package/skills/update/SKILL.md +9 -4
- package/skills/.keep +0 -0
- /package/docs/handbook/{sites → chatgpt-sites}/host-reference.md +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: develop
|
|
3
|
+
description: Work on any Mantle project using the Core SDK contract. Use for manifest, runtime, content model, handler, adapter, validation, and MCP work after a project already exists.
|
|
4
|
+
metadata:
|
|
5
|
+
source: "@aotter/mantle"
|
|
6
|
+
sourcePath: docs/skills/develop/SKILL.md
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
|
+
projection: project
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Develop
|
|
12
|
+
|
|
13
|
+
This is the Core workflow skill for an existing Mantle project. Repo-local
|
|
14
|
+
copies are byte-for-byte projections from the installed package; its embedded
|
|
15
|
+
docs govern runtime/API behavior.
|
|
16
|
+
|
|
17
|
+
## First Read
|
|
18
|
+
|
|
19
|
+
1. `package.json` for the installed `@aotter/mantle*` versions.
|
|
20
|
+
2. The manifest directory selected by the project scripts, the actual host
|
|
21
|
+
entry and adapter config (for example `wrangler.jsonc` on Cloudflare).
|
|
22
|
+
Read custom Auth construction there when present.
|
|
23
|
+
3. Optional local context: `.mantle/plugins.json`, `.mantle/plugins.lock.json`,
|
|
24
|
+
and `.mantle/recipes/`. Legacy launch/handoff files are context only.
|
|
25
|
+
4. Installed Core docs in `node_modules/@aotter/mantle/docs/`.
|
|
26
|
+
|
|
27
|
+
If `node_modules/` is missing, run `pnpm install --frozen-lockfile` before
|
|
28
|
+
falling back to remote docs. Remote docs must use a tag matching the installed
|
|
29
|
+
version; never use `develop` branch docs for a versioned consumer project.
|
|
30
|
+
|
|
31
|
+
## Existing Examples
|
|
32
|
+
|
|
33
|
+
Read installed `docs/handbook/start/project-and-cli.md` and
|
|
34
|
+
`docs/examples/README.md`. Use `docs/examples/host-minimal-worker/` for Spec +
|
|
35
|
+
adapter without Admin. Read `docs/examples/host-local-admin-otp/` only when the
|
|
36
|
+
project already has Admin or the human asked for Dev UI — that path is opt-in.
|
|
37
|
+
Ingest only `docs/examples/builtin-*.md` Manifests as grammar for new domains.
|
|
38
|
+
Read `docs/examples/cf-primitives-*.md` before inventing Durable Object, Queue,
|
|
39
|
+
cron, or `ref` handler patterns.
|
|
40
|
+
References are test/documentation, not a Starter or a fixed application shape.
|
|
41
|
+
|
|
42
|
+
Public rendering is opt-in consumer wiring: `mountPublicRoutes`, a
|
|
43
|
+
`TemplateRegistry`, and a matching `publicPathResolver` must agree on the
|
|
44
|
+
exposed collections. Do not auto-publish every Schema. Applications document their mounted URL surface in their own README.
|
|
45
|
+
Import the registry and resolver from `@aotter/mantle/web`; Core runtime does
|
|
46
|
+
not install public rendering by itself.
|
|
47
|
+
|
|
48
|
+
## Authoring CLI
|
|
49
|
+
|
|
50
|
+
Use the project's scripts first; applications use the shipping
|
|
51
|
+
`mantle` authoring CLI from `@aotter/mantle`. Ask the installed CLI for its
|
|
52
|
+
command list instead of trusting one copied into prose — the surface is
|
|
53
|
+
version-matched and changes between releases:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm exec mantle --help
|
|
57
|
+
pnpm validate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This CLI validates and derives artifacts from application-authored manifests.
|
|
61
|
+
It does not create projects, business schemas or a visitor homepage. There is
|
|
62
|
+
no `mantle create` / `mantle update` happy path.
|
|
63
|
+
|
|
64
|
+
## Core Model
|
|
65
|
+
|
|
66
|
+
Mantle exposes exactly four declarative atoms:
|
|
67
|
+
|
|
68
|
+
| Atom | Purpose |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `Schema` | Stored entity/table shape. |
|
|
71
|
+
| `View` | Read/query surface. |
|
|
72
|
+
| `Procedure` | Typed mutation or operation. |
|
|
73
|
+
| `Trigger` | HTTP/lifecycle/MCP invocation binding. |
|
|
74
|
+
|
|
75
|
+
Do not invent manifest kinds such as `Form`, `Feature`, `Workflow`, or
|
|
76
|
+
`Membership`. Compose those from the four atoms plus TypeScript only where
|
|
77
|
+
the atoms cannot express the behavior.
|
|
78
|
+
|
|
79
|
+
## Choose the manifest feature first
|
|
80
|
+
|
|
81
|
+
Read installed `docs/handbook/reference/features.md` to map the requested
|
|
82
|
+
behavior to fields before adding handlers or a custom UI. For host-only reads,
|
|
83
|
+
`surface: internal` keeps a View out of REST/MCP/Admin while preserving its
|
|
84
|
+
`requires` checks. Read `docs/handbook/guides/typed-queries.md` for generated
|
|
85
|
+
View params/results, indexed entry reads, and their authorization boundary.
|
|
86
|
+
Use `from` for portable typed projections; SQL is for queries needing native
|
|
87
|
+
SQLite and produces `unknown` row types.
|
|
88
|
+
|
|
89
|
+
For Admin labels, inputs, collection columns/tabs, reports or action buttons,
|
|
90
|
+
read `docs/handbook/guides/admin-ui.md`. Prefer supported Schema/Procedure/View
|
|
91
|
+
metadata and `uiSchema` before custom frontend code. These control the Admin
|
|
92
|
+
console, not the visitor frontend. Regenerate and verify the actual console;
|
|
93
|
+
never edit generated `public/_mantle/admin/` assets.
|
|
94
|
+
|
|
95
|
+
## Content Edits
|
|
96
|
+
|
|
97
|
+
- Follow the actual frontend content source. Use Admin or Staff MCP for
|
|
98
|
+
runtime-backed content. Do not invent an overlay/seed homepage.
|
|
99
|
+
- For a new submitted field, update the stored `Schema` and the public
|
|
100
|
+
`Procedure.spec.input` before any form UI. Keep public mutation inputs
|
|
101
|
+
`additionalProperties: false`; otherwise JSON Schema's default may strip an
|
|
102
|
+
undeclared field while returning success.
|
|
103
|
+
- Use `lifecycle: operational` for submissions, inquiries, orders, and other
|
|
104
|
+
Procedure-created operational records that staff inspect or correct. Reserve
|
|
105
|
+
`publishing` for content a person stages and publishes.
|
|
106
|
+
- Lifecycle `before_update` / `after_update` hooks also fire for unpublish,
|
|
107
|
+
archive, and every other status transition whose target is not `published`;
|
|
108
|
+
do not use them for edit-only work.
|
|
109
|
+
- When a form's fixed option values change, update the stored Schema and public
|
|
110
|
+
Procedure input `enum` together. Keep translated labels in the frontend content source;
|
|
111
|
+
Admin and Staff MCP derive their typed controls from the manifest values.
|
|
112
|
+
- If the application has a `page` Schema with sections, update its declared
|
|
113
|
+
section properties when adding display fields; an undeclared property has no
|
|
114
|
+
runtime-backed Admin or Staff MCP path.
|
|
115
|
+
- Update notification handlers when they need the new field. Test the stored
|
|
116
|
+
entry, not only the HTTP `{ "ok": true }` response.
|
|
117
|
+
|
|
118
|
+
## Locales
|
|
119
|
+
|
|
120
|
+
- `data.locale` is reserved for `localized: true` Schemas. A non-localized
|
|
121
|
+
Schema must use a domain field such as `replyLocale`.
|
|
122
|
+
- Use a standalone localized Schema only for independent locale rows. For
|
|
123
|
+
versions of one entity, use a non-localized parent plus a localized child
|
|
124
|
+
with `translates: { parent, on }`. The child must own at least one field
|
|
125
|
+
besides `locale` and the join field.
|
|
126
|
+
- Parallel locale blocks must keep field names, option values, step IDs, and
|
|
127
|
+
result keys identical; translate display strings only.
|
|
128
|
+
- `siteDefaults.origin`, `siteDefaults.locales`, and `siteDefaults.icons` are
|
|
129
|
+
code-owned and boot-synced. The icon list is shared by browser favicons,
|
|
130
|
+
Admin chrome, and MCP `serverInfo.icons`; keep its static files under
|
|
131
|
+
`public/`. Brand, title, and description seed once, then change through site
|
|
132
|
+
settings.
|
|
133
|
+
- When changing an existing collection from `[slug]` to `[slug, locale]`,
|
|
134
|
+
boot with a Mantle version that reconciles obsolete unique indexes and test
|
|
135
|
+
the same slug in two locales. Do not patch D1 manually.
|
|
136
|
+
|
|
137
|
+
## Adapter Boundary
|
|
138
|
+
|
|
139
|
+
The runtime is adapter-neutral. A `MantleStorageAdapter` prepares the compiled
|
|
140
|
+
plan into semantic storage ports; `createMantleRuntime` binds that prepared
|
|
141
|
+
storage and selected capabilities. Database drivers and asset serving belong
|
|
142
|
+
to the host/optional composition. Follow the installed adapter guide.
|
|
143
|
+
|
|
144
|
+
Do not assume Cloudflare unless the project imports `@aotter/mantle/cloudflare`
|
|
145
|
+
or its adapter config is visible. A future Netlify adapter should satisfy the
|
|
146
|
+
same Core workflow through its own ports and provider setup.
|
|
147
|
+
|
|
148
|
+
Site code is a consumer of this abstraction. Use Manifests, runtime use cases,
|
|
149
|
+
`runtime.entries`, and optional `runtime.siteConfig`; do not query Mantle-owned `entries` or
|
|
150
|
+
`site_config`, reach through deprecated `runtime.db`, copy generated-column
|
|
151
|
+
names, or construct SDK KV keys. Cloudflare bindings belong only at the
|
|
152
|
+
composition root. If a normal feature cannot be expressed through a
|
|
153
|
+
purpose-shaped surface, treat that as a Core abstraction gap instead of
|
|
154
|
+
teaching the project Mantle internals.
|
|
155
|
+
|
|
156
|
+
## Auth Composition
|
|
157
|
+
|
|
158
|
+
Admin is opt-in. A project without `@aotter/mantle-admin-ui` is complete.
|
|
159
|
+
When Admin is installed, `createMantleWorker({ auth })` with `email-otp`
|
|
160
|
+
and `ConsoleEmailSender` is the local human path (OTP in wrangler logs).
|
|
161
|
+
That override owns Auth construction; Core still owns `/admin` and
|
|
162
|
+
`/api/auth/*`. Admin also requires wrangler `assets.directory=./public`
|
|
163
|
+
and an `ASSETS` binding. A white screen at `/admin` with HTML 200 and
|
|
164
|
+
`/_mantle/admin/assets/*` 404 is a missing assets binding, not a missing
|
|
165
|
+
frontend build.
|
|
166
|
+
|
|
167
|
+
Conventional Cloudflare projects that do not replace Auth declare
|
|
168
|
+
`MANTLE_AUTH_MODE=hosted` or `self-managed`; Core owns that standard
|
|
169
|
+
composition and rejects partial or mixed bindings. Preserve the explicit
|
|
170
|
+
mode recorded in Worker config, keep provider secrets out of source, and
|
|
171
|
+
do not infer a mode from whichever credentials happen to be present.
|
|
172
|
+
|
|
173
|
+
## Performance Loop
|
|
174
|
+
|
|
175
|
+
After changing a Schema index, View filter/order, public API, or rendered page,
|
|
176
|
+
run the project's index check when present. Otherwise run the installed
|
|
177
|
+
harness directly:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pnpm exec mantle-harness indexes --require-public --format text
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The check uses crowded real SQLite and the shipped compiler. It complements
|
|
184
|
+
`pnpm validate`; it does not replace correctness validation. Declare the
|
|
185
|
+
smallest ordered index justified by the measured path and respect SQLite's
|
|
186
|
+
leftmost-prefix rule; which columns an index may name and how a public View's
|
|
187
|
+
status predicate shapes it is in the handbook (`reference/schema.md#indexes`).
|
|
188
|
+
Do not change user-visible filter or ordering semantics
|
|
189
|
+
just to make the gate pass. Do not add every permutation or cache every read.
|
|
190
|
+
|
|
191
|
+
For relevant Cloudflare serving changes, start the project and sample the
|
|
192
|
+
actual routes:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pnpm exec mantle-harness http \
|
|
196
|
+
--base-url http://127.0.0.1:8787 \
|
|
197
|
+
--route page=/en/example \
|
|
198
|
+
--rounds 20 --warmup 2 --format text
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Prefer query plan, query count, `rows_read` scaling, and cache MISS/HIT
|
|
202
|
+
evidence. Do not create CI gates from absolute local milliseconds.
|
|
203
|
+
|
|
204
|
+
## Loop
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
pnpm install --frozen-lockfile
|
|
208
|
+
pnpm validate
|
|
209
|
+
pnpm check:indexes # when the project provides it
|
|
210
|
+
pnpm typecheck
|
|
211
|
+
pnpm check
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Use `pnpm dev` for local preview when the project provides it.
|
|
215
|
+
|
|
216
|
+
## Connect a Local MCP Client
|
|
217
|
+
|
|
218
|
+
Start the project with `pnpm dev`, then use the exact local origin it prints.
|
|
219
|
+
The conventional Cloudflare adapter exposes:
|
|
220
|
+
|
|
221
|
+
- `http://localhost:8787/mcp` for public tools;
|
|
222
|
+
- `http://localhost:8787/mcp/staff` for authenticated authoring tools.
|
|
223
|
+
|
|
224
|
+
Prefer the client's native remote HTTP + OAuth support. Use a standard
|
|
225
|
+
HTTP-to-stdio bridge such as `npx -y mcp-remote <url>` only when the client
|
|
226
|
+
accepts stdio MCP servers but cannot connect to remote HTTP directly. Mantle
|
|
227
|
+
does not own a separate local proxy or an auth-bypass mode.
|
|
228
|
+
|
|
229
|
+
Before changing client config, confirm the Worker is reachable:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
curl -i http://localhost:8787/mcp
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
An OAuth-protected endpoint should respond with `401` and a
|
|
236
|
+
`WWW-Authenticate` resource-metadata challenge before sign-in. After
|
|
237
|
+
connecting, inspect `tools/list`; make one read-only `query_view_*` call when
|
|
238
|
+
available before invoking any mutation. Use project-scoped client config when
|
|
239
|
+
the client offers it, and never commit OAuth tokens or the bridge's token
|
|
240
|
+
cache.
|
|
241
|
+
|
|
242
|
+
## Rules
|
|
243
|
+
|
|
244
|
+
- Keep content models in the configured manifest directory; its immediate
|
|
245
|
+
`.yaml` and `.yml` files are loaded together.
|
|
246
|
+
- Add TypeScript only for handlers, rendering, adapter wiring, or real behavior.
|
|
247
|
+
- Do not write directly to D1, KV, Postgres, or object storage for content
|
|
248
|
+
authoring. Use runtime use cases, admin APIs, or Staff MCP.
|
|
249
|
+
- Do not commit provider secrets.
|
|
250
|
+
- If the work is an installable capability, switch to `mantle:plugin`.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: media-gc
|
|
3
|
+
description: Audit and safely remove stale, uncommitted public media uploads from a Mantle Cloudflare R2 bucket. Use when a Mantle operator asks to inspect or clean orphan media objects left after create_media_upload without commit_media_upload.
|
|
4
|
+
metadata:
|
|
5
|
+
source: "@aotter/mantle"
|
|
6
|
+
sourcePath: docs/skills/media-gc/SKILL.md
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
|
+
projection: package
|
|
9
|
+
projectionReason: Destructive remote object deletion and Cloudflare-specific; opt-in only.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Mantle Media GC
|
|
13
|
+
|
|
14
|
+
Use the connected Cloudflare API. Audit by default; delete only the exact
|
|
15
|
+
objects approved by the user.
|
|
16
|
+
|
|
17
|
+
## Preflight
|
|
18
|
+
|
|
19
|
+
1. Read the project's Wrangler config and Mantle config. Resolve the R2 bucket
|
|
20
|
+
binding, bucket name, and declared public media purpose names.
|
|
21
|
+
2. Confirm the exact Cloudflare account and bucket. If either is ambiguous,
|
|
22
|
+
ask; never choose by name similarity.
|
|
23
|
+
3. Use the Cloudflare OpenAPI search before execution to resolve the current
|
|
24
|
+
R2 List Objects and Delete Objects endpoints. If the connected account lacks
|
|
25
|
+
access, stop. Do not create, request, or store credentials.
|
|
26
|
+
4. Stop when the project has no public R2 media binding or no declared purpose.
|
|
27
|
+
|
|
28
|
+
## Audit
|
|
29
|
+
|
|
30
|
+
For each declared purpose, list up to 1,000 objects per page under the exact
|
|
31
|
+
`<purpose>/` prefix. Use the API cursor until `is_truncated` is false.
|
|
32
|
+
|
|
33
|
+
An object is a deletion candidate only when all conditions hold:
|
|
34
|
+
|
|
35
|
+
- `last_modified` is more than 24 hours old;
|
|
36
|
+
- `custom_metadata.committedAt` is absent or empty;
|
|
37
|
+
- the key matches the exact Mantle layout
|
|
38
|
+
`<purpose>/<group>/(primary|alternate|fallback).(png|jpg|webp|avif|gif|svg)`,
|
|
39
|
+
where `<purpose>` is declared by this project and `<group>` contains only
|
|
40
|
+
letters, digits, `_`, or `-`.
|
|
41
|
+
|
|
42
|
+
Skip committed, unknown-prefix, unprefixed, malformed, or ambiguous objects.
|
|
43
|
+
Delete only an uncommitted variant, never its whole group.
|
|
44
|
+
|
|
45
|
+
Report the account, bucket, purpose prefixes, UTC cutoff, candidate object and
|
|
46
|
+
group counts, total bytes, skipped count, and a SHA-256 digest of the sorted
|
|
47
|
+
`key + etag` candidate set. Do not print object keys, upload group IDs,
|
|
48
|
+
filenames, public URLs, signed URLs, or secrets.
|
|
49
|
+
|
|
50
|
+
## Apply
|
|
51
|
+
|
|
52
|
+
1. Show the audit summary and get explicit confirmation for that exact account,
|
|
53
|
+
bucket, cutoff, count, byte total, and candidate-set digest.
|
|
54
|
+
2. Re-run the complete audit with the same UTC cutoff. If the candidate count,
|
|
55
|
+
bytes, or digest changed, stop and present the new audit for confirmation.
|
|
56
|
+
3. Call Delete Objects with JSON arrays of exact keys, at most 1,000 keys per
|
|
57
|
+
request. Never send the `prefix` query parameter: an empty prefix can empty
|
|
58
|
+
the bucket.
|
|
59
|
+
4. Do not automatically retry failed keys. Report safe API error codes and
|
|
60
|
+
counts; a later invocation can audit and retry what remains.
|
|
61
|
+
5. Re-list every inspected purpose prefix and report the remaining candidate
|
|
62
|
+
count and bytes.
|
|
63
|
+
|
|
64
|
+
## Don't
|
|
65
|
+
|
|
66
|
+
- Don't create a Worker, Cron Trigger, lifecycle rule, D1 table, or local script.
|
|
67
|
+
- Don't use prefix deletion or empty-bucket operations.
|
|
68
|
+
- Don't delete private-media buckets or objects outside declared purpose
|
|
69
|
+
prefixes.
|
|
70
|
+
- Don't treat missing pagination pages, metadata, or permissions as an empty
|
|
71
|
+
result.
|
|
72
|
+
|
|
73
|
+
## Diagnostics
|
|
74
|
+
|
|
75
|
+
| Symptom | Action |
|
|
76
|
+
|---|---|
|
|
77
|
+
| Multiple matching accounts or buckets | Stop and ask the user to select the exact target. |
|
|
78
|
+
| A page is truncated without a cursor | Stop; do not delete from a partial audit. |
|
|
79
|
+
| A candidate has an unexpected key or metadata shape | Skip it and include it only in the aggregate skipped count. |
|
|
80
|
+
| A delete request partially fails | Report safe error codes and leave the remaining objects for a later audit. |
|
|
81
|
+
|
|
82
|
+
## When You're Done
|
|
83
|
+
|
|
84
|
+
Return whether the run was audit-only or applied, the aggregate before/after
|
|
85
|
+
counts and bytes, and any safely redacted failures.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plugin
|
|
3
|
+
description: Discover, plan, apply, and verify Mantle marketplace plugins through the Core SDK contract. Use when the user wants to add an installable capability without hand-planning provisioning steps.
|
|
4
|
+
metadata:
|
|
5
|
+
source: "@aotter/mantle"
|
|
6
|
+
sourcePath: docs/skills/plugin/SKILL.md
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
|
+
projection: project
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Plugin
|
|
12
|
+
|
|
13
|
+
Mantle plugins are Core SDK capability packages. They are not application
|
|
14
|
+
scaffolds (retired in ADR-0021) and they are not provider provisioning scripts.
|
|
15
|
+
|
|
16
|
+
A plugin may contribute:
|
|
17
|
+
|
|
18
|
+
- manifests: `Schema`, `View`, `Procedure`, `Trigger`;
|
|
19
|
+
- handler source or handler registration notes;
|
|
20
|
+
- site defaults or media policy additions;
|
|
21
|
+
- expected HTTP, admin, and MCP surfaces;
|
|
22
|
+
- adapter capability requirements and provider setup notes.
|
|
23
|
+
|
|
24
|
+
## User Install Entry
|
|
25
|
+
|
|
26
|
+
The user-facing install path is:
|
|
27
|
+
|
|
28
|
+
```txt
|
|
29
|
+
Use repo-local mantle:plugin to install <plugin slug or recipe URL> in this repo.
|
|
30
|
+
Use repo-local mantle:plugin to update <plugin id> in this repo.
|
|
31
|
+
Use repo-local mantle:plugin to remove <plugin id> from this repo.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
There is no `mantle plugin add` CLI yet. Do not invent one. Install from a
|
|
35
|
+
marketplace entry, plugin package, or recipe URL that declares enough data for
|
|
36
|
+
an agent to apply the capability deterministically.
|
|
37
|
+
|
|
38
|
+
A valid marketplace entry must include:
|
|
39
|
+
|
|
40
|
+
- plugin id, title, source, and version;
|
|
41
|
+
- supported Mantle version range;
|
|
42
|
+
- files, manifests, handlers, routes, MCP tools, and admin surfaces it adds;
|
|
43
|
+
- adapter capabilities and provider resources it requires;
|
|
44
|
+
- required env vars and secrets, without secret values;
|
|
45
|
+
- verification commands and expected surfaces.
|
|
46
|
+
|
|
47
|
+
If the marketplace page is only marketing copy or lacks an install recipe,
|
|
48
|
+
stop and ask for the recipe instead of guessing.
|
|
49
|
+
|
|
50
|
+
## First Read
|
|
51
|
+
|
|
52
|
+
1. `package.json` for Mantle version and adapter package.
|
|
53
|
+
2. The manifest directory selected by project scripts for current atom names
|
|
54
|
+
and route/tool collisions.
|
|
55
|
+
3. The actual host entry and its handler, template and port registrations;
|
|
56
|
+
`src/mantle/config.ts` and `src/mantle/handlers/` are conventions, not required paths.
|
|
57
|
+
4. `.mantle/plugins.json` and `.mantle/plugins.lock.json` if present.
|
|
58
|
+
5. `.mantle/launch-state.json` only as context, not as plugin authority.
|
|
59
|
+
|
|
60
|
+
Read version-matched contracts under `node_modules/@aotter/mantle/docs/`,
|
|
61
|
+
starting with `handbook/reference/features.md`. Plugin recipes cannot override
|
|
62
|
+
the installed grammar.
|
|
63
|
+
|
|
64
|
+
## Plan First
|
|
65
|
+
|
|
66
|
+
Before applying any plugin, produce a plan:
|
|
67
|
+
|
|
68
|
+
- files to add or change;
|
|
69
|
+
- atoms to add and their names;
|
|
70
|
+
- HTTP routes and MCP tools that will appear;
|
|
71
|
+
- required runtime ports;
|
|
72
|
+
- adapter-specific resources, env vars, and secrets;
|
|
73
|
+
- checks to run.
|
|
74
|
+
|
|
75
|
+
If the plugin needs a capability the current adapter does not expose, stop
|
|
76
|
+
with the missing capability instead of inventing provider steps.
|
|
77
|
+
|
|
78
|
+
## Apply
|
|
79
|
+
|
|
80
|
+
Apply the smallest deterministic diff. Do not run arbitrary install scripts
|
|
81
|
+
from a plugin package. Copy declared files, wire declared handlers, update the
|
|
82
|
+
plugin ledger, then validate.
|
|
83
|
+
|
|
84
|
+
Suggested ledger paths:
|
|
85
|
+
|
|
86
|
+
```txt
|
|
87
|
+
.mantle/plugins.json
|
|
88
|
+
.mantle/plugins.lock.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Keep optional legacy launch files such as `.mantle/features.json` separate from
|
|
92
|
+
plugin state. They are not the Core plugin ledger.
|
|
93
|
+
|
|
94
|
+
## Update
|
|
95
|
+
|
|
96
|
+
Compare the installed lock entry against the marketplace entry or recipe URL.
|
|
97
|
+
Apply only the declared version diff, update `.mantle/plugins.lock.json`, then
|
|
98
|
+
run the same verification checks.
|
|
99
|
+
|
|
100
|
+
## Remove
|
|
101
|
+
|
|
102
|
+
Use the lock entry as the removal manifest. Delete only files and atoms owned
|
|
103
|
+
by that plugin, unwind handler registrations it added, remove its ledger entry,
|
|
104
|
+
then validate. If another plugin or local code depends on a removed atom, stop
|
|
105
|
+
and report the dependency instead of deleting through it.
|
|
106
|
+
|
|
107
|
+
## Verify
|
|
108
|
+
|
|
109
|
+
Use the project's validation and typecheck scripts when present. Regenerate
|
|
110
|
+
the plan after manifest changes before probing the running host:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pnpm exec mantle validate
|
|
114
|
+
pnpm exec mantle generate
|
|
115
|
+
pnpm exec mantle generate --check
|
|
116
|
+
# Run the project's TypeScript check and restart its local server.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then verify the plugin's declared surfaces:
|
|
120
|
+
|
|
121
|
+
- public Views via `GET /api/views/<name>`, staff Views via authenticated
|
|
122
|
+
Admin/staff MCP, and internal Views through the host binding;
|
|
123
|
+
- HTTP Trigger path for public writes;
|
|
124
|
+
- Staff/Public MCP `tools/list` for MCP Trigger or Schema-derived tools;
|
|
125
|
+
- adapter resource presence when the plugin requires optional ports.
|
|
126
|
+
|
|
127
|
+
## Don't
|
|
128
|
+
|
|
129
|
+
- Don't treat an application template as a plugin.
|
|
130
|
+
- Don't assume Cloudflare; inspect the active adapter and capability ports.
|
|
131
|
+
- Don't create a second skill namespace for host-specific plugins.
|
|
132
|
+
- Don't commit secrets. Provider secrets stay in the platform secret store.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: provision
|
|
3
|
+
description: Ship a Mantle project through its selected host, routing ChatGPT Sites to its integration guide and conventional Cloudflare Workers to production auth and provisioning.
|
|
4
|
+
metadata:
|
|
5
|
+
source: "@aotter/mantle"
|
|
6
|
+
sourcePath: docs/skills/provision/SKILL.md
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
|
+
projection: package
|
|
9
|
+
projectionReason: Platform-specific deploy that handles production secrets; opt-in only.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Provision a Mantle Project
|
|
13
|
+
|
|
14
|
+
Local cold start deliberately stops before this skill. Provision only after the
|
|
15
|
+
user asks to create remote resources or ship production. This flow is for
|
|
16
|
+
consumer-owned Cloudflare Workers. New direct-authored apps do not need Landing
|
|
17
|
+
artifacts (`.mantle/launch-state.json`, `.mantle/handoff.md`, or a hosted-auth
|
|
18
|
+
allocation). Treat any Landing handoff as **legacy/optional**. For a ChatGPT
|
|
19
|
+
Site, use the installed `docs/handbook/chatgpt-sites/index.md` integration guide and
|
|
20
|
+
the "Publish with Sites" steps in `docs/examples/host-chatgpt-sites/README.md`:
|
|
21
|
+
request D1 and R2 on the Site, set `PUBLIC_ORIGIN` and `OWNER_EMAIL` in Sites
|
|
22
|
+
settings, review the migration, then save and deploy a Sites version. Do not
|
|
23
|
+
run `wrangler deploy` or require R2 S3 credentials merely because Sites exposes
|
|
24
|
+
an R2 binding.
|
|
25
|
+
|
|
26
|
+
## Source of Truth
|
|
27
|
+
|
|
28
|
+
1. Read the actual provider config (`wrangler.jsonc` or `wrangler.toml`),
|
|
29
|
+
application entry and git remotes. Read legacy `.mantle/launch-state.json`
|
|
30
|
+
and `.mantle/handoff.md` only when present; they are optional leftovers from
|
|
31
|
+
Landing and must not be created as prerequisites for a new app.
|
|
32
|
+
2. Read installed `@aotter/mantle*` versions from `package.json`.
|
|
33
|
+
3. Use matching embedded docs under `node_modules/@aotter/mantle/docs/`.
|
|
34
|
+
4. Never infer provider authority from launch state. Confirm the active GitHub
|
|
35
|
+
and Cloudflare accounts before changing them.
|
|
36
|
+
|
|
37
|
+
Run the local gate first:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pnpm install --frozen-lockfile
|
|
41
|
+
pnpm validate
|
|
42
|
+
pnpm typecheck
|
|
43
|
+
git status --short
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Resume From Observed State
|
|
47
|
+
|
|
48
|
+
Do not branch on how the project was created. Verify these facts and skip
|
|
49
|
+
completed work:
|
|
50
|
+
|
|
51
|
+
1. `git remote get-url origin` confirms the GitHub repo.
|
|
52
|
+
2. An HTTPS `PUBLIC_ORIGIN` that responds confirms the Cloudflare deploy.
|
|
53
|
+
3. `/admin/sign-in` returning `503 setup_incomplete` means auth is not bound.
|
|
54
|
+
Use the recorded auth intent only to choose hosted or self-hosted setup;
|
|
55
|
+
live behavior is authoritative.
|
|
56
|
+
|
|
57
|
+
If there is no remote, confirm the target account, create a private repo,
|
|
58
|
+
commit, and push `main`. If there is no live Worker, confirm the Cloudflare
|
|
59
|
+
account, prefer an available connector, or use `pnpm exec wrangler login` with
|
|
60
|
+
the user's agreement, then run `pnpm deploy`.
|
|
61
|
+
|
|
62
|
+
Capture the live URL in `PUBLIC_ORIGIN` and `Public site:` in `AGENTS.md`, then
|
|
63
|
+
commit and push non-secret changes. Reuse any repo or Worker already created.
|
|
64
|
+
Do not recreate Landing artifacts for a new direct-authored app. Workers Builds
|
|
65
|
+
is optional after a direct deploy.
|
|
66
|
+
|
|
67
|
+
When the owner later adopts a custom domain, update `PUBLIC_ORIGIN` and the
|
|
68
|
+
provider's OAuth callback together, then redeploy. Do not patch `site_config`
|
|
69
|
+
directly; boot syncs its canonical origin from `PUBLIC_ORIGIN`.
|
|
70
|
+
|
|
71
|
+
## Choose Auth
|
|
72
|
+
|
|
73
|
+
- **Self-hosted email OTP:** use the application's production transactional-email sender. Replace `ConsoleEmailSender`; never deploy it.
|
|
74
|
+
- **Self-hosted GitHub OAuth — free fallback:** use when the application has no email provider. Configure the owner's per-site GitHub OAuth App and Worker secrets using the steps below.
|
|
75
|
+
- **Mantle hosted auth — paid, legacy/optional:** use only when a **legacy
|
|
76
|
+
Landing handoff** already records a hosted allocation and client
|
|
77
|
+
configuration. New direct-authored apps do not get this from Core. Mantle
|
|
78
|
+
Platform operates the identity provider; do not ask the user for a per-site
|
|
79
|
+
GitHub OAuth App.
|
|
80
|
+
|
|
81
|
+
Configure only the selected mode. Core deliberately rejects partial or mixed
|
|
82
|
+
hosted/self-managed bindings with `503 setup_incomplete`.
|
|
83
|
+
|
|
84
|
+
Do not claim that hosted auth can attach to an arbitrary local repo unless a
|
|
85
|
+
legacy Landing handoff already supplies that configuration.
|
|
86
|
+
|
|
87
|
+
For the exact boundary, read
|
|
88
|
+
`node_modules/@aotter/mantle/docs/auth-hosting-model.md`.
|
|
89
|
+
|
|
90
|
+
## Self-hosted email OTP
|
|
91
|
+
|
|
92
|
+
Keep the application's custom `createAuth()` factory, replace
|
|
93
|
+
`ConsoleEmailSender` with its production `EmailSender`, and retain
|
|
94
|
+
`bootstrapOwner: { match: "email", value: <owner email> }`. Store sender
|
|
95
|
+
credentials and `BETTER_AUTH_SECRET` as Worker secrets, put `PUBLIC_ORIGIN` in
|
|
96
|
+
non-secret vars, deploy, then verify that the owner receives an OTP at
|
|
97
|
+
`/admin/sign-in`. If there is no production email provider, use GitHub OAuth
|
|
98
|
+
below instead of deploying console delivery.
|
|
99
|
+
|
|
100
|
+
## Self-hosted GitHub OAuth
|
|
101
|
+
|
|
102
|
+
1. Ask the user to create a GitHub OAuth App:
|
|
103
|
+
|
|
104
|
+
- Homepage URL: `<worker-url>`
|
|
105
|
+
- Authorization callback URL: `<worker-url>/api/auth/callback/github`
|
|
106
|
+
- Device Flow: unchecked
|
|
107
|
+
|
|
108
|
+
2. Put non-secret values in `wrangler.toml`:
|
|
109
|
+
|
|
110
|
+
- `MANTLE_AUTH_MODE = "self-managed"`
|
|
111
|
+
- `PUBLIC_ORIGIN`
|
|
112
|
+
- `GITHUB_CLIENT_ID`
|
|
113
|
+
- `ADMIN_GITHUB_LOGIN`
|
|
114
|
+
- correct Worker `name`
|
|
115
|
+
|
|
116
|
+
Remove `MANTLE_HOSTED_AUTH_ISSUER` and `MANTLE_HOSTED_AUTH_CLIENT_ID` if they
|
|
117
|
+
were present for a hosted allocation.
|
|
118
|
+
|
|
119
|
+
3. Keep the Client Secret out of chat. Prefer a Cloudflare connector for
|
|
120
|
+
secrets; otherwise use hidden shell input:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
read -rsp "GitHub OAuth client secret: " MANTLE_GITHUB_CLIENT_SECRET && printf "\n"
|
|
124
|
+
printf '%s' "$MANTLE_GITHUB_CLIENT_SECRET" | pnpm exec wrangler secret put GITHUB_CLIENT_SECRET
|
|
125
|
+
openssl rand -hex 32 | pnpm exec wrangler secret put BETTER_AUTH_SECRET
|
|
126
|
+
unset MANTLE_GITHUB_CLIENT_SECRET
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Set `BETTER_AUTH_SECRET` once and preserve it. Rotating it invalidates existing
|
|
130
|
+
sessions.
|
|
131
|
+
|
|
132
|
+
4. Commit and push only non-secret config, then redeploy:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git add wrangler.toml AGENTS.md
|
|
136
|
+
git commit -m "mantle: wire production auth"
|
|
137
|
+
git push
|
|
138
|
+
pnpm deploy
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Hosted Auth (legacy Landing)
|
|
142
|
+
|
|
143
|
+
Skip this section unless a legacy Landing handoff is already present. New
|
|
144
|
+
direct-authored apps use self-hosted email OTP or GitHub OAuth above.
|
|
145
|
+
|
|
146
|
+
Follow that handoff and its client configuration. Hosted configuration remains
|
|
147
|
+
in landing-managed Cloudflare Worker bindings. Verify:
|
|
148
|
+
|
|
149
|
+
- `MANTLE_AUTH_MODE = "hosted"`;
|
|
150
|
+
- `MANTLE_HOSTED_AUTH_ISSUER` is the HTTPS root issuer;
|
|
151
|
+
- `MANTLE_HOSTED_AUTH_CLIENT_ID` is the same-origin `/clients/<id>` URL;
|
|
152
|
+
- `PUBLIC_ORIGIN` and `ADMIN_GITHUB_LOGIN` are set;
|
|
153
|
+
- `BETTER_AUTH_SECRET` exists as a Worker secret;
|
|
154
|
+
- `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` are absent.
|
|
155
|
+
|
|
156
|
+
Hosted clients use PKCE and have no client secret. Do not write secrets into
|
|
157
|
+
`wrangler.toml`.
|
|
158
|
+
|
|
159
|
+
Verify that admin sign-in redirects to Mantle Hosted Auth and Staff MCP
|
|
160
|
+
authenticates, then skip the self-hosted flow.
|
|
161
|
+
|
|
162
|
+
## Smoke Test
|
|
163
|
+
|
|
164
|
+
- public home route;
|
|
165
|
+
- `/admin/sign-in`;
|
|
166
|
+
- selected admin sign-in path;
|
|
167
|
+
- `/mcp/staff` with an agent client when available;
|
|
168
|
+
- one type-specific core workflow.
|
|
169
|
+
|
|
170
|
+
Media uploads are optional. Configure R2 only when the owner asks for
|
|
171
|
+
staff-managed files; then read
|
|
172
|
+
`node_modules/@aotter/mantle/docs/handbook/cloudflare/media-r2.md`.
|
|
173
|
+
|
|
174
|
+
## Handoff
|
|
175
|
+
|
|
176
|
+
Return:
|
|
177
|
+
|
|
178
|
+
- public URL;
|
|
179
|
+
- admin sign-in URL;
|
|
180
|
+
- Staff MCP URL;
|
|
181
|
+
- operator setup URL:
|
|
182
|
+
`https://mantle.tools/connect?site=<url-encoded-worker-url>`;
|
|
183
|
+
- remote resources created or reused;
|
|
184
|
+
- auth mode and any intentionally deferred setup.
|
|
185
|
+
|
|
186
|
+
## Don't
|
|
187
|
+
|
|
188
|
+
- Don't create remote resources before the user asks to ship.
|
|
189
|
+
- Don't ask for a Cloudflare API token in the base flow.
|
|
190
|
+
- Don't commit provider secrets.
|
|
191
|
+
- Don't require R2 for first production.
|
|
192
|
+
- Don't invent a second provision orchestrator.
|
|
193
|
+
- Don't use `/admin/auth/github/callback`; the callback is
|
|
194
|
+
`/api/auth/callback/github`.
|