@aotter/mantle 0.1.4 → 0.1.5-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +16 -16
  2. package/dist/cli/generate-cloudflare.d.ts +7 -0
  3. package/dist/cli/generate-cloudflare.d.ts.map +1 -0
  4. package/dist/cli/generate-cloudflare.js +123 -0
  5. package/dist/cli/generate-cloudflare.js.map +1 -0
  6. package/dist/cli/generate-project.d.ts +25 -0
  7. package/dist/cli/generate-project.d.ts.map +1 -0
  8. package/dist/cli/generate-project.js +282 -0
  9. package/dist/cli/generate-project.js.map +1 -0
  10. package/dist/cli/generate-sites.d.ts +8 -0
  11. package/dist/cli/generate-sites.d.ts.map +1 -0
  12. package/dist/cli/generate-sites.js +273 -0
  13. package/dist/cli/generate-sites.js.map +1 -0
  14. package/dist/cli/generate.d.ts.map +1 -1
  15. package/dist/cli/generate.js +121 -10
  16. package/dist/cli/generate.js.map +1 -1
  17. package/dist/cli/main.d.ts +1 -1
  18. package/dist/cli/main.d.ts.map +1 -1
  19. package/dist/cli/main.js +10 -10
  20. package/docs/adr/0021-retire-starter-scaffolding.md +1 -1
  21. package/docs/adr/0026-progressive-generate.md +36 -0
  22. package/docs/adr/0027-scheduled-procedure-triggers.md +31 -0
  23. package/docs/adr/0028-schema-ttl-expiry.md +33 -0
  24. package/docs/adr/README.md +5 -2
  25. package/docs/agent-prompts.md +40 -78
  26. package/docs/examples/host-chatgpt-sites/README.md +1 -1
  27. package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +10 -0
  28. package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +24 -0
  29. package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +1 -1
  30. package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +60 -0
  31. package/docs/examples/host-chatgpt-sites/package.json +1 -1
  32. package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +42 -8
  33. package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +9 -6
  34. package/docs/examples/host-local-admin-otp/package.json +2 -2
  35. package/docs/examples/host-minimal-worker/package.json +2 -2
  36. package/docs/handbook/chatgpt-sites/generated-app.md +84 -0
  37. package/docs/handbook/chatgpt-sites/index.md +21 -11
  38. package/docs/handbook/cloudflare/authentication.md +19 -2
  39. package/docs/handbook/cloudflare/bindings.md +4 -11
  40. package/docs/handbook/cloudflare/conventional-worker.md +7 -3
  41. package/docs/handbook/cloudflare/deploy-and-operate.md +1 -1
  42. package/docs/handbook/concepts/runtime-and-adapters.md +7 -0
  43. package/docs/handbook/guides/agent-setup.md +3 -3
  44. package/docs/handbook/navigation.json +3 -2
  45. package/docs/handbook/reference/diagnostics.md +5 -0
  46. package/docs/handbook/reference/procedure.md +52 -1
  47. package/docs/handbook/reference/schema.md +27 -1
  48. package/docs/handbook/reference/surface.md +3 -3
  49. package/docs/handbook/reference/trigger.md +21 -1
  50. package/docs/handbook/releases/index.md +55 -0
  51. package/docs/handbook/start/overview.md +5 -2
  52. package/docs/handbook/start/project-and-cli.md +103 -119
  53. package/docs/handbook/start/quickstart-admin.md +6 -4
  54. package/docs/handbook/start/quickstart-worker.md +17 -8
  55. package/docs/skills/develop/SKILL.md +14 -6
  56. package/docs/skills/integrate/SKILL.md +60 -0
  57. package/package.json +18 -17
  58. package/skills/README.md +3 -1
  59. package/skills/develop/SKILL.md +14 -6
  60. package/skills/install/SKILL.md +100 -137
  61. package/skills/integrate/SKILL.md +60 -0
  62. package/templates/chatgpt-sites/auth.ts +66 -0
  63. package/templates/chatgpt-sites/build.mjs +14 -0
  64. package/templates/chatgpt-sites/env.ts +1 -0
  65. package/templates/chatgpt-sites/mcp.ts +48 -0
  66. package/templates/chatgpt-sites/public-mcp.ts +29 -0
  67. package/templates/chatgpt-sites/smoke-local.mjs +20 -0
@@ -0,0 +1,84 @@
1
+ ---
2
+ description: Generate a blank Mantle app for ChatGPT Sites, apply D1 migrations, and verify local Admin and MCP.
3
+ ---
4
+ # Generate a ChatGPT Sites app
5
+
6
+ `mantle generate --host chatgpt-sites` creates a blank home, a D1-backed Runtime,
7
+ public API and MCP, and Mantle Admin. It adds no article Schema, media bucket, or
8
+ demo content. Edit `src/home.ts` and add your own manifests and handlers as needed.
9
+
10
+ ```sh
11
+ mkdir my-site && cd my-site
12
+ npm init -y
13
+ npm pkg set type=module
14
+ npm install --save-exact @aotter/mantle
15
+ npx mantle generate --host chatgpt-sites
16
+ npm install
17
+ npx mantle generate
18
+ npm run build
19
+ npx wrangler d1 migrations apply my-site --local
20
+ ```
21
+
22
+ The first generate declares the exact Mantle package versions it needs; the
23
+ second runs after those packages are installed. Review `drizzle/0000_mantle.sql`
24
+ before applying it. The D1 name comes from `wrangler.jsonc`. Generation does not
25
+ apply a migration, provision a Site, or publish one. `npm run build` removes the
26
+ old `dist` before copying the current migrations and Admin assets.
27
+
28
+ For local Admin, copy `.dev.vars.example` to `.dev.vars`, set `OWNER_EMAIL` to
29
+ the email of the ChatGPT account that will own this Site, and keep
30
+ `PUBLIC_ORIGIN` equal to the local URL. Run `npm run dev`.
31
+ The plain local browser has no ChatGPT Sites identity, so it will redirect to
32
+ Sites sign-in and cannot prove owner access. To exercise the identity contract
33
+ locally, set `MANTLE_TEST_OWNER_EMAIL` to the same email and run
34
+ `npm run smoke:local`. That script refuses non-loopback
35
+ targets. Its identity headers are a local simulation; the production Worker
36
+ contains no identity-injection path and must run behind Sites' trusted
37
+ identity-stripping dispatcher. The generated Wrangler config disables the
38
+ independent `workers.dev` URL; do not add a direct Worker route. A passing
39
+ local smoke does not verify a deployed ChatGPT sign-in.
40
+
41
+ Without `OWNER_EMAIL`, sign-in grants no Mantle staff role. If the intended
42
+ owner signed in before configuration, their existing `sites_users` row remains
43
+ unprivileged. Configure the owner email and deliberately assign `role='owner'`
44
+ to that account's exact email and `chatgpt:` ID in D1, or reset a disposable
45
+ local database and sign in again. Mantle never gives ownership to the first
46
+ visitor.
47
+
48
+ The anonymous public MCP endpoint is `/api/mcp`; it discovers only public
49
+ manifest capabilities. `/api/mcp/staff` uses the current Sites browser session
50
+ and a Mantle staff role. Admin advertises these endpoints, and Admin WebMCP
51
+ works in supporting browsers. The staff endpoint is not a remote OAuth MCP
52
+ connector. A remote agent needs a separately implemented verified OAuth flow.
53
+
54
+ When a Schema changes, run `npx mantle generate --check` to detect drift, then
55
+ `npx mantle generate`. Review the newly appended SQL and apply pending D1
56
+ migrations before deploying code that expects the new Schema. Previous
57
+ migration files remain untouched; an unapplied migration keeps the old database
58
+ fingerprint, and managed boot refuses to use an older database. Sites owns
59
+ `.openai/hosting.json` project metadata and production D1 migration delivery.
60
+
61
+ Replacing a Schema's `uniqueIndexes` tuple requires an explicit reviewed
62
+ migration. With the old local D1 migration applied, edit the Manifest and run
63
+ `npx mantle generate --review-unique-indexes --check` to inspect the proposed
64
+ source/target fingerprints, affected tuples and checksum without writing. Then
65
+ run `npx mantle generate --review-unique-indexes`, review the new
66
+ `drizzle/meta/000N_mantle.review.json` and matching SQL, and apply that SQL
67
+ through the normal Sites D1 migration path. The CLI checks **local D1 only**
68
+ for duplicate target tuples and reports a count and sample before writing;
69
+ inspect production data separately before applying there. SQLite also refuses
70
+ to create the new unique index if production has conflicts; do
71
+ not deploy the target Worker until the migration has succeeded. Old Workers
72
+ cannot safely reactivate after the old uniqueness guarantee is removed.
73
+ This option is limited to managed Sites SQLite and unique tuple changes;
74
+ field conversions and runtime-managed CF boot still reject destructive
75
+ changes.
76
+
77
+ For a complete article, media, and publishing example, use the
78
+ [Sites reference](../../examples/host-chatgpt-sites/README.md).
79
+
80
+ ## Source
81
+
82
+ - [Sites generator](../../../packages/mantle/src/cli/generate-sites.ts)
83
+ - [Generated Sites templates](../../../packages/mantle/templates/chatgpt-sites)
84
+ - [Runnable Sites reference](../../examples/host-chatgpt-sites/README.md)
@@ -16,6 +16,10 @@ repository. Sites owns hosting, visitor access, and ChatGPT sign-in; Mantle owns
16
16
  the content model, staff roles, and publishing workflow. For platform access and
17
17
  sharing options, see [OpenAI's Sites documentation](https://learn.chatgpt.com/docs/sites).
18
18
 
19
+ For a new project, start with the [blank generated app](./generated-app.md).
20
+ The article reference below demonstrates a complete editorial Site with R2
21
+ media after you choose that content model.
22
+
19
23
  ## What you get
20
24
 
21
25
  | Capability | In the Mantle Sites reference |
@@ -34,12 +38,17 @@ boundaries.
34
38
 
35
39
  ## Start with the supported SDK
36
40
 
37
- You need Node.js 22+, pnpm 9+, and Sites access with D1 and R2 available for your
38
- project. Follow the reference's local build and smoke test before deployment.
39
- Keep your application outside the SDK checkout. The included article schema is
40
- a working example; adapt its fields and lifecycle to your team's content.
41
+ You need Node.js 22+ and Sites access with D1. Start with the
42
+ [blank generated app](./generated-app.md) to choose your own content model;
43
+ it requires no R2 bucket. The article reference also needs R2 for cover
44
+ images. Build and smoke-test locally before deployment, and keep your
45
+ application outside the SDK checkout.
46
+
47
+ ## Follow the article reference when it fits
41
48
 
42
- ## Publish your first article
49
+ The steps below apply to the optional prebuilt article example. A new blank
50
+ generated Site has no article Schema, media bucket or content; add those only
51
+ when your requirements call for them.
43
52
 
44
53
  1. **Prepare the application.** Follow the
45
54
  [runnable reference](../../examples/host-chatgpt-sites/README.md#install-and-run).
@@ -81,15 +90,15 @@ staff connector needs its own verified OAuth flow; see the [host reference](./ho
81
90
 
82
91
  ## Ask your agent to set it up
83
92
 
84
- Install the Mantle agent plugin, or `@aotter/mantle` itself, then describe your
85
- audience and content requirements:
93
+ Install the Mantle skill and exact SDK version, then describe your audience
94
+ and content requirements:
86
95
 
87
96
  ```text
88
97
  Build a content site with ChatGPT Sites and Mantle. Read
89
- handbook/chatgpt-sites/index.md and examples/host-chatgpt-sites/README.md from the
90
- installed Mantle docs, and follow that reference's install and run steps.
91
- Adapt the article example to my content requirements. Include ChatGPT
92
- sign-in, Mantle staff roles, cover uploads, and published article pages.
98
+ handbook/chatgpt-sites/generated-app.md from the installed Mantle docs, and
99
+ start from the blank generated Site when that CLI supports it. Use the article
100
+ reference only if its content model matches my requirements. Include ChatGPT
101
+ sign-in and Mantle staff roles; add cover uploads only if I request media.
93
102
  Run the local checks and show me a draft-to-publish walkthrough before
94
103
  deploying through Sites. Keep remote staff OAuth MCP outside this scope.
95
104
  ```
@@ -112,6 +121,7 @@ evidence from the equipment and Slack work still required in your application.
112
121
  ## Integration details
113
122
 
114
123
  - [Host reference](./host-reference.md): identity, migrations, R2 routes, MCP, and deployment checks.
124
+ - [Generated blank app](./generated-app.md): local composition, D1 migration, Admin, and MCP.
115
125
  - [Equipment checkout](./equipment-checkout.md): custom business rules and external API delivery without payments.
116
126
  - [Runnable application](../../examples/host-chatgpt-sites/README.md): installation, build artifacts, and smoke tests.
117
127
  - [Conventional Cloudflare Worker](../cloudflare/conventional-worker.md): the separate path for a Worker you deploy directly.
@@ -7,11 +7,28 @@ Conventional Auth is chosen by one variable, `MANTLE_AUTH_MODE`, and fails close
7
7
 
8
8
  ## Local Admin: email OTP
9
9
 
10
- The local human path does not use `MANTLE_AUTH_MODE` or GitHub. Pass `auth` to `createMantleWorker` with `email-otp`, `ConsoleEmailSender`, and `bootstrapOwner.match: "email"`. The one-time code is printed on the wrangler log. See [Quickstart: local Admin](../start/quickstart-admin.md). `ConsoleEmailSender` is for `wrangler dev` only; production needs a real sender. The official example binds `127.0.0.1:8787` and sets `PUBLIC_ORIGIN` to that origin; Better Auth rejects OTP with `INVALID_ORIGIN` when they diverge from the origin wrangler prints.
10
+ The generated CF app writes `.dev.vars.example`. Copy it to `.dev.vars` for
11
+ local use, then set `MANTLE_AUTH_MODE=local-otp`, the real `ADMIN_EMAIL`, a
12
+ random `BETTER_AUTH_SECRET`, and the exact loopback `PUBLIC_ORIGIN`. It configures
13
+ its email-OTP auth factory only for a
14
+ loopback origin. If you author the Worker yourself, pass `auth` to
15
+ `createMantleWorker` with `email-otp`, `ConsoleEmailSender`, and
16
+ `bootstrapOwner.match: "email"`; that factory does not read the conventional
17
+ mode matrix. The one-time code is printed in wrangler logs. See
18
+ [Quickstart: local Admin](../start/quickstart-admin.md). `ConsoleEmailSender`
19
+ is for `wrangler dev` only; production needs a real sender. Match
20
+ `PUBLIC_ORIGIN` to wrangler's actual `127.0.0.1:8787` origin or Better Auth
21
+ will reject OTP with `INVALID_ORIGIN`.
22
+
23
+ For production, the generated Worker's local OTP branch is disabled outside
24
+ loopback. Configure the conventional `self-managed` or `hosted` Auth mode,
25
+ including its required provider credentials and `ADMIN_GITHUB_LOGIN`, instead
26
+ of editing the CLI-owned Worker. An application that needs production email
27
+ delivery should author its own Worker Auth factory and real `EmailSender`.
11
28
 
12
29
  With `auth` set, the mode matrix below is not read. Core still owns `/admin` and `/api/auth/*`.
13
30
 
14
- For production email OTP, keep the custom `createAuth()` factory, replace
31
+ For production email OTP in a manually authored Worker, keep the custom `createAuth()` factory, replace
15
32
  `ConsoleEmailSender` with the application's production `EmailSender`, and keep
16
33
  `bootstrapOwner: { match: "email", value: <owner email> }`. Store the sender
17
34
  credentials and `BETTER_AUTH_SECRET` as Worker secrets. If the application has
@@ -186,26 +186,19 @@ the interface is one `record(event)` method.
186
186
  "triggers": { "crons": ["*/5 * * * *"] }
187
187
  ```
188
188
 
189
- A `scheduled()` handler reuses the fetch path's runtime and invokes a Procedure that has no Trigger of its own:
189
+ A `schedule` Trigger declares the Procedure, and the Worker export reuses the same runtime as `fetch`:
190
190
 
191
191
  ```ts
192
- import { bindMantle } from "../.mantle/generated/mantle.js";
193
-
194
192
  const worker = createMantleWorker<Env>({ plan, handlers });
195
193
 
196
194
  export default {
197
195
  fetch: worker.fetch,
198
- async scheduled(_controller, env, ctx) {
199
- const mantle = bindMantle(await worker.getRuntime(env));
200
- const result = await mantle.procedures.sweepExpiredOrders(
201
- { now: Date.now() },
202
- { user: null, staff: null, env, waitUntil: (p) => ctx.waitUntil(p) },
203
- );
204
- if (!result.ok) throw new Error(`sweep failed: ${result.diagnostic.code}`);
205
- },
196
+ scheduled: worker.scheduled,
206
197
  } satisfies ExportedHandler<Env>;
207
198
  ```
208
199
 
200
+ Register the exact enabled `cron` expression in `wrangler.jsonc`; Mantle does not create provider triggers. The Procedure takes `{}` input and sees `ctx.schedule.id` for retry-safe idempotency. See [Trigger](../reference/trigger.md#schedule-source).
201
+
209
202
  ## Durable Objects (application-owned)
210
203
 
211
204
  Durable Objects are not a Core primitive. Use one as an application-owned coordinator when a decision must be serialized, for example reserving inventory exactly once. Export the class from the Worker entry, then bind and migrate it:
@@ -3,7 +3,10 @@ description: Assemble a Mantle Worker with createMantleWorker, add handlers and
3
3
  ---
4
4
  # The conventional Worker
5
5
 
6
- `createMantleWorker` is the normal Cloudflare entry for a Mantle application. This page covers its options, what the facade owns, how to add application handlers and routes, and the readiness and reserved-path rules that keep those extensions safe.
6
+ New applications can start with `mantle generate --host cf`, which writes a
7
+ blank home, Worker entry and selected Admin/API/MCP wiring. This page covers
8
+ the lower-level `createMantleWorker` facade for applications that author their
9
+ own Worker entry: its options, extension seam, readiness and reserved paths.
7
10
 
8
11
  ## Minimal entry
9
12
 
@@ -14,7 +17,7 @@ import { plan } from "../.mantle/generated/mantle.js";
14
17
  export default createMantleWorker({ plan, cacheScope: "my-site-production" });
15
18
  ```
16
19
 
17
- `plan` is the sealed plan that `mantle generate` writes to `.mantle/generated/mantle.ts`. With only `plan`, the Worker serves public View REST, HTTP Triggers, Admin, Auth, OAuth and MCP. It renders no public pages and `/` is a 404; see [Public web](./public-web.md). Local Admin with email OTP replaces Auth construction; see [Quickstart: local Admin](../start/quickstart-admin.md). Admin still requires the `ASSETS` binding.
20
+ `plan` is the sealed plan that `mantle generate` writes to `.mantle/generated/mantle.ts`. With only `plan`, this manually assembled Worker serves public View REST, HTTP Triggers, Admin, Auth, OAuth and MCP. It renders no public pages and `/` is a 404; see [Public web](./public-web.md). A newly generated full CF project wires a separate editable blank home. Local Admin with email OTP replaces Auth construction; see [Quickstart: local Admin](../start/quickstart-admin.md). Admin still requires the `ASSETS` binding.
18
21
 
19
22
  ## Options
20
23
 
@@ -64,9 +67,10 @@ If `auth.ready` rejects, the memoized assembly is evicted so the next request re
64
67
  const worker = createMantleWorker<Env>({ plan });
65
68
  worker.fetch(request, env, ctx); // HTTP entry
66
69
  worker.getRuntime(env); // the runtime fetch uses, resolved after auth.ready
70
+ worker.scheduled(controller, env, ctx); // declared scheduled Procedure Triggers
67
71
  ```
68
72
 
69
- Queue and scheduled handlers call `worker.getRuntime(env)` and then generated `bindMantle(runtime)` so they reuse the assembled runtime and never write Mantle tables directly. See [Bindings](./bindings.md#cron-triggers) and [Deferred hooks](./deferred-hooks-queues.md).
73
+ Queue handlers call `worker.getRuntime(env)` and then generated `bindMantle(runtime)` so they reuse the assembled runtime. Scheduled Procedure Triggers use `worker.scheduled` with expressions registered in Wrangler. See [Bindings](./bindings.md#cron-triggers), [Trigger](../reference/trigger.md#schedule-source), and [Deferred hooks](./deferred-hooks-queues.md).
70
74
 
71
75
  ## Readiness rule
72
76
 
@@ -10,7 +10,7 @@ This page is the production checklist for a Mantle Worker on Cloudflare: what to
10
10
  - Pin every `@aotter/mantle*` package to one exact version and commit the lockfile. Install with `pnpm install --frozen-lockfile` (or `npm ci`) from then on. See [Project and CLI](../start/project-and-cli.md).
11
11
  - Set `PUBLIC_ORIGIN` to the real HTTPS origin, without a trailing slash. It drives canonical URLs, `.md` mirrors, `llms.txt`, the MCP resource and the OAuth callback. If a static documentation build also emits absolute URLs, give it the same value.
12
12
  - Set the production D1 `database_id` (and `account_id` if your deployment needs it) in `wrangler.jsonc`. A local `database_name` is not a production identifier, and local D1 is not production data.
13
- - Choose production Auth: replace local console email delivery with a real `EmailSender`, or configure `MANTLE_AUTH_MODE` for GitHub OAuth or hosted Auth. Store secrets with `wrangler secret put`. See [Authentication](./authentication.md).
13
+ - Choose production Auth: a generated CF app uses `MANTLE_AUTH_MODE=self-managed` or `hosted` with `ADMIN_GITHUB_LOGIN` and the matching provider credentials. Local `local-otp` works only on loopback. If you author your own Worker Auth factory, replace console email delivery with a real `EmailSender`. Store secrets with `wrangler secret put`. See [Authentication](./authentication.md).
14
14
  - Keep `compatibility_flags: ["nodejs_compat", "global_fetch_strictly_public"]`.
15
15
  - Enable observability:
16
16
 
@@ -95,6 +95,13 @@ A storage adapter prepares one `RuntimePlan` into semantic ports. Three are requ
95
95
 
96
96
  Two are optional and only when a feature needs them: `MediaStorage` for upload flows, and `DeferredHookDispatcher` for at-least-once `after_*` delivery. `DatabaseDriver` is not a portability contract — it is the reusable SQLite/D1 seam. A PostgreSQL, MongoDB or application-owned-table adapter implements the semantic ports directly rather than emulating D1.
97
97
 
98
+ `PreparedMantleStorage.atomicEntries` is an optional semantic write capability.
99
+ An adapter exposes it only when it can commit an ordered group of entry
100
+ mutations all-or-nothing, including zero-row version/status failures. The
101
+ SQLite implementation covers Cloudflare D1 and Bun; IndexedDB and custom
102
+ adapters without that guarantee leave it absent. Ref Procedures use
103
+ `ctx.writeAtomically` as described in [Procedure](../reference/procedure.md#atomic-entry-writes-in-a-ref-handler).
104
+
98
105
  Declarative Views compile to logical plans once, and preparation lowers those plans to native queries. `View.spec.sql` is the exception: it is explicitly SQLite-only in v0.1. Storage that does not declare that dialect rejects such a View at preparation with `VIEW_DIALECT_UNSUPPORTED`, before mutating any state. Mantle does not guess a translation and ships no universal query driver. A View that must run everywhere uses `from` with a filter AST; see [View](../reference/view.md).
99
106
 
100
107
  ## Embedding each adapter
@@ -62,7 +62,7 @@ pnpm exec mantle skills --check
62
62
  |---|---|
63
63
  | `.agents/skills/mantle-{develop,plugin,theme,update}/SKILL.md` | The installed package's four project-scoped workflows. |
64
64
  | `.claude/skills/mantle-{develop,plugin,theme,update}/SKILL.md` | Identical bytes for Claude compatibility. |
65
- | `node_modules/@aotter/mantle/skills/` | All seven shipped skills, including opt-in `mantle`, `provision` and `media-gc`. |
65
+ | `node_modules/@aotter/mantle/skills/` | All eight shipped skills, including opt-in `mantle`, `integrate`, `provision` and `media-gc`. |
66
66
  | `node_modules/@aotter/mantle/docs/` | Handbook and examples matched to the package. |
67
67
 
68
68
  `mantle skills` overwrites these four generated projections. Keep project-specific
@@ -84,9 +84,9 @@ It does not prove a server, login, Admin assets or MCP connection works.
84
84
  | `skills/install/SKILL.md` is missing | That is source provenance. Read the installer's destination instead. |
85
85
  | `docs/...` is missing after `skills add` | The skill does not carry the handbook. Install the pinned SDK and read its embedded docs. |
86
86
  | `mantle` is missing | Install local `@aotter/mantle`; run its binary through the project package manager. |
87
- | `MANIFEST_ROOT_NOT_FOUND` | Author manifests in the configured directory; `generate` is not a scaffold command. |
87
+ | `MANIFEST_ROOT_NOT_FOUND` | Check the installed CLI and saved project selection. New generated projects may start with an empty manifest set; legacy compile mode still needs the configured source. |
88
88
  | `mantle skills --check` exits 1 | Run `mantle skills`, then read the refreshed project skill. |
89
- | No projected provision/media-gc skill | These are intentionally opt-in. Read their installed package files when the task calls for them. |
89
+ | No projected integrate/provision/media-gc skill | These are intentionally opt-in. Read their installed package files when the task calls for them. |
90
90
  | A documented feature fails on an older release | Use that release's docs or explicitly upgrade; changing a skill does not change Runtime. |
91
91
 
92
92
  ## Source
@@ -5,9 +5,9 @@
5
5
  "text": "Start here",
6
6
  "items": [
7
7
  { "text": "Handbook overview", "link": "/start/overview" },
8
- { "text": "Project layout and the CLI loop", "link": "/start/project-and-cli" },
8
+ { "text": "Generate a project and use the CLI", "link": "/start/project-and-cli" },
9
9
  { "text": "Quickstart: a minimal Worker", "link": "/start/quickstart-worker" },
10
- { "text": "Quickstart: local Admin (opt-in)", "link": "/start/quickstart-admin" },
10
+ { "text": "Quickstart: authored Worker Admin", "link": "/start/quickstart-admin" },
11
11
  { "text": "When the host is ChatGPT Sites", "link": "/chatgpt-sites/index" }
12
12
  ]
13
13
  },
@@ -50,6 +50,7 @@
50
50
  "text": "ChatGPT Sites",
51
51
  "items": [
52
52
  { "text": "Get started", "link": "/chatgpt-sites/index" },
53
+ { "text": "Generate a blank app", "link": "/chatgpt-sites/generated-app" },
53
54
  { "text": "Host reference", "link": "/chatgpt-sites/host-reference" },
54
55
  { "text": "Equipment checkout and external APIs", "link": "/chatgpt-sites/equipment-checkout" },
55
56
  { "text": "Earlier guide URL", "link": "/cloudflare/chatgpt-sites" }
@@ -81,11 +81,16 @@ Raised by the parser, the graph validator and the code generator. `mantle valida
81
81
  | `UNIQUE_INDEX_FIELD_UNKNOWN` | A `uniqueIndexes` field is not a top-level Schema property. | — |
82
82
  | `SCHEMA_SEARCH_INVALID` | `searchableFields` repeats an entry or names a non-string property. | — |
83
83
  | `SCHEMA_SEARCH_FIELD_UNKNOWN` | A `searchableFields` entry is not a Schema property. | — |
84
+ | `SCHEMA_TTL_INVALID` | TTL must name a top-level date-time field and a finite nonnegative duration. | — |
85
+ | `SCHEMA_TTL_TRANSLATION_UNSUPPORTED` | TTL on either side of a translation join cannot guarantee the joined content's expiry. | — |
86
+ | `VIEW_TTL_NATIVE_UNSAFE` | A native SQL View cannot guarantee logical expiry while a Schema has TTL. | — |
84
87
  | `SCHEMA_UI_INVALID` | A Schema or Procedure `uiSchema` rule is broken, including unknown roots, invalid `nav.standalone`, or a Schema declaring `uiSchema.collectionAction`. | — |
85
88
  | `HANDLER_BUILTIN_NOT_IN_V010` | A builtin Procedure was invoked on a runtime assembled without the builtin collaborator. | — |
86
89
  | `MANIFEST_ROOT_NOT_FOUND` | The manifests directory is missing, unreadable, or contains no `.yaml` or `.yml` file. | — |
87
90
  | `MANIFEST_READ_FAILED` | A manifest source could not be read. | — |
88
91
  | `CODEGEN_IDENTIFIER_COLLISION` | Two names in one group collapse to the same generated lower-camel identifier. | — |
92
+ | `SCHEDULE_INPUT_INVALID` | A scheduled Procedure cannot accept the empty object supplied by its system Trigger. | — |
93
+ | `SCHEDULE_AUTH_INVALID` | A scheduled Procedure requires user or staff authority that the system caller never has. | — |
89
94
  | `FIXTURE_SCHEMA_VIOLATION` | Reserved for consumer-authored test diagnostics on the `test` phase. Core emits purpose-shaped harness reports instead. | — |
90
95
 
91
96
  ## Cross-phase
@@ -207,7 +207,7 @@ An HTTP Trigger wraps a success as `{ "ok": true, "data": <EntryRow> }` with sta
207
207
 
208
208
  ## Conflicts and idempotency
209
209
 
210
- A unique-index preflight runs before every write, and the database's own constraints catch the races the preflight misses. Both surface as `CONFLICT` (409), as do a stale `expectedVersion` and an illegal lifecycle transition. **There is no automatic retry** — the caller decides whether to re-read and try again.
210
+ A unique-index preflight runs before individual writes, and the database's own constraints catch the races the preflight misses. Atomic groups rely on the transaction's database constraints so an earlier operation may free a unique value for a later one. Both paths surface collisions as `CONFLICT` (409), as do a stale `expectedVersion` and an illegal lifecycle transition. **There is no automatic retry** — the caller decides whether to re-read and try again.
211
211
 
212
212
  Idempotency has no grammar key. The convention is an `input` property marked `x-mcp-hint: idempotency-key`: Admin generates and hides one UUID per form submission, and other callers generate one and reuse it across retries of the same logical request. The handler is responsible for acting on it.
213
213
 
@@ -215,6 +215,57 @@ Optimistic concurrency uses the reserved input name `expectedVersion` — the ve
215
215
 
216
216
  Deferred lifecycle hooks have a stronger guarantee to work with: delivery is at-least-once, and handlers key on `${ctx.event.id}:${ctx.event.trigger}` — stable across enqueue fallback, queue retries and replay. See [Deferred hooks on Queues](../cloudflare/deferred-hooks-queues.md).
217
217
 
218
+ ## Atomic entry writes in a ref handler
219
+
220
+ When one request must change several Schemas together, a `ref` handler can call
221
+ `ctx.writeAtomically(operations)`. Cloudflare D1 and Bun SQLite support it; an
222
+ adapter without the optional `atomicEntries` capability returns
223
+ `RESOURCE_UNAVAILABLE` (503)
224
+ instead of committing part of the group. Operations use the `createDraft`,
225
+ `updateDraft`, or `deleteEntry` request shape; atomic deletes additionally
226
+ require `expectedVersion`. Operational Schemas become live on create;
227
+ publishing Schemas create drafts. Updates and deletes require the version the
228
+ caller read.
229
+
230
+ ```ts
231
+ const sessionId = crypto.randomUUID();
232
+ const rows = await ctx.writeAtomically!([
233
+ { kind: "create", id: sessionId, request: {
234
+ collection: "sessions", data: { name: input.name }, authorId: ctx.user?.id ?? null, ctx,
235
+ } },
236
+ { kind: "create", request: {
237
+ collection: "exercise-blocks", data: { sessionId, exercise: input.exercise },
238
+ authorId: ctx.user?.id ?? null, ctx,
239
+ } },
240
+ { kind: "create", request: {
241
+ collection: "receipts", data: { token: input.requestId }, authorId: ctx.user?.id ?? null, ctx,
242
+ } },
243
+ ]);
244
+ return { sessionId: rows[0]!.id };
245
+ ```
246
+
247
+ Declare `receipts.token` as a Schema `uniqueIndexes: [[token]]`. A duplicate
248
+ receipt rejects the whole group, including the session and block. The handler
249
+ can then read the existing receipt **after** the failed transaction to answer
250
+ an idempotent retry. A stale update or delete rejects the whole group even
251
+ when it is the final operation. The group may touch each entry only once;
252
+ read and authorization decisions happen before the batch, and the expected
253
+ version/status is checked again by the conditional database write.
254
+
255
+ All Schema projection, stamping, validation, uniqueness, and lifecycle rules
256
+ still apply. `before_*` hooks run in operation order before the batch and can
257
+ veto it; their external effects cannot be rolled back. `after_*` hooks run
258
+ only after commit, in operation order. Publishing-content invalidation runs
259
+ once for the group. Deferred Queue delivery is separate from the database
260
+ transaction. Application-owned tables can use their host's transaction
261
+ facility inside a ref handler, but that does not give those tables Mantle
262
+ entry semantics. Direct SQL writes to Mantle Schema tables are unsupported.
263
+ Authorization guard Procedures do not receive `ctx.writeAtomically`.
264
+
265
+ ## TTL sweep in a ref handler
266
+
267
+ `ctx.sweepExpired({ collection, limit })` previews a bounded page of expired rows; `delete: true` explicitly removes it. The result contains `scanned`, `removed` and an optional `nextCursor`. Continue with that cursor until absent. D1 and Bun SQLite implement this semantic capability; unsupported storage returns `RESOURCE_UNAVAILABLE`. Authorization guard Procedures do not receive the sweep function. For a scheduled cleanup, declare a [schedule Trigger](./trigger.md#schedule-source) targeting a no-input ref Procedure. No sweep is scheduled automatically. See [Schema TTL](./schema.md#ttl).
268
+
218
269
  ## `uiSchema`
219
270
 
220
271
  Admin presentation only. It never affects input validation, the MCP tool schema or the OpenAPI document. Roots are closed: `collectionAction` and `fields`.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Schema field reference — JSON Schema subset, extension keywords, uiSchema, indexes, searchable fields, translates and lifecycle rules.
2
+ description: Schema field reference — JSON Schema subset, uiSchema, indexes, translates, lifecycle and TTL rules.
3
3
  ---
4
4
  # Schema
5
5
 
@@ -19,6 +19,7 @@ A Schema declares one collection: the JSON Schema for each entry's `data`, its i
19
19
  | `localized` | boolean | no | `false` | When `false`, a `locale` property is rejected. Must be a boolean. |
20
20
  | `translates` | `{ parent, on }` | no | — | Marks a translation child. Requires `localized: true`. |
21
21
  | `lifecycle` | `publishing` \| `operational` | no | `publishing` | Selects the [state machine](#lifecycle). |
22
+ | `ttl` | `{ field, expireAfterSeconds }` | no | — | Logical expiry over one top-level date-time property. See [TTL](#ttl). |
22
23
 
23
24
  ### Reserved entry columns
24
25
 
@@ -242,6 +243,31 @@ Every authoring path (Admin, Staff MCP, builtin Procedures) runs the same guard
242
243
 
243
244
  Site locales are configured in [Site config](./site-config.md).
244
245
 
246
+ ## TTL
247
+
248
+ ```yaml
249
+ spec:
250
+ ttl: { field: expiresAt, expireAfterSeconds: 0 }
251
+ schema:
252
+ type: object
253
+ properties:
254
+ expiresAt: { type: string, format: date-time, nullable: true }
255
+ ```
256
+
257
+ `field` must name one top-level `date-time` string property; `expireAfterSeconds` is finite and nonnegative. An entry expires when its timestamp plus the duration is **at or before** the current instant. Missing or null dates never expire. `SCHEMA_TTL_INVALID` rejects invalid policies.
258
+
259
+ Legacy values that SQLite cannot parse as dates also remain visible and are not swept; correct those rows before relying on TTL. New writes must pass the Schema's date-time validation.
260
+
261
+ Expiry is logical first: Entry, declarative View, Admin, MCP and Web reads stop returning the row at the boundary, even before a sweep deletes it. A native SQL View cannot guarantee that filter and is rejected (`VIEW_TTL_NATIVE_UNSAFE`) while any Schema has TTL. A View over a TTL Schema cannot use shared caching (`VIEW_CACHE_INVALID`). Bun and D1 use the same SQLite predicate; other storage adapters must supply equivalent read filtering or reject the plan.
262
+
263
+ TTL is currently rejected on either side of a `translates` relationship (`SCHEMA_TTL_TRANSLATION_UNSUPPORTED`), because a translation child could otherwise remain visible after its parent expires.
264
+
265
+ Physical cleanup is **explicit**. `runtime.sweepExpired({ collection: "events", limit: 50 })` previews one page; add `delete: true` to remove it. Follow `nextCursor` until absent. The limit is 1–100, `scanned` counts expired candidates and `removed` counts successful deletes. A failure throws and the previous cursor is safe to retry. Ref Procedures can call `ctx.sweepExpired`; a Cloudflare schedule may invoke such a Procedure. The sweep does not fire entry lifecycle hooks: expiration already changed logical visibility, and the sweep only reclaims storage (ADR-0028).
266
+
267
+ The Developer Console shows declared TTL policies. It does not infer sweep history from a policy; a scheduled Procedure can return `scanned` and `removed` counts to include them in its run observation.
268
+
269
+ Adding or shortening TTL on an existing Schema immediately changes **read visibility**, but never starts a deletion job. Preview a sweep and review its counts before requesting `delete: true`. Keep a backup when changing the policy on populated data. Physical unique constraints still see expired rows until cleanup, so a reused unique value may conflict before the sweep.
270
+
245
271
  ## Source
246
272
 
247
273
  - [`packages/mantle-spec/src/domain/model/ManifestGrammar.ts`](../../../packages/mantle-spec/src/domain/model/ManifestGrammar.ts)
@@ -116,14 +116,14 @@ The `@aotter/mantle` package installs two binaries, `mantle` and `mantle-harness
116
116
 
117
117
  | Command | Flags |
118
118
  |---|---|
119
- | `mantle generate` | `--manifests <dir>` (default `./manifests`), `-o, --output <dir>` (default `.mantle/generated`), `--namespace <name>` (default `Mantle`), `--check`, `-h, --help` |
119
+ | `mantle generate` | `--manifests <dir>` (default `./manifests`), `-o, --output <dir>` (default `.mantle/generated`), `--namespace <name>` (default `Mantle`), `--host cf\|chatgpt-sites`, `--features <list>`, `--review-unique-indexes` (managed Sites only), `--check`, `-h, --help` |
120
120
  | `mantle skills` | `--check`, `-h, --help` |
121
121
  | `mantle validate` | `--manifests <dir>` (default `./manifests`), `--source <dir>` (default `./src`), `--no-source`, `--phase preview\|deploy` (default `preview`), `--format json\|text` (default by TTY), `--json`, `-h, --help` |
122
122
  | `mantle emit-openapi` | `--manifests <dir>`, `--title <str>` (default `mantle`), `--version <str>` (default `0.1.0`), `--session-cookie-name <str>`, `-o, --output <file>`, `-h, --help` |
123
123
  | `mantle-harness indexes` | `--manifests <dir>`, `--rows <n>`, `--require <view>` (repeatable), `--require-public`, `--format json\|text`, `-h, --help` |
124
124
  | `mantle-harness http` | `--route <name=url>` (repeatable, required), `--base-url <url>`, `--rounds <n>`, `--warmup <n>`, `--format json\|text`, `-h, --help` |
125
125
 
126
- `generate` validates and compiles the manifest directory, writes the typed module, and — when `@aotter/mantle-admin-ui` is installed — syncs the Admin SPA into `public/_mantle/admin/`, excluding `server.*` files. `--check` fails without writing when either output is stale. `skills` copies every skill the installed package marks `projection: project` into `.agents/skills/mantle-*` and `.claude/skills/mantle-*`; both layouts receive identical bytes. `validate --phase deploy` adds the pre-deploy-only gates on top of the grammar and cross-Schema checks. `emit-openapi` covers HTTP Triggers and View REST routes; MCP is out of scope. `mantle-harness indexes` executes compiled Views against crowded SQLite and inspects query plans; `http` samples a running Worker for p50 and p95. Day-to-day use is in [Project and CLI](../start/project-and-cli.md).
126
+ `generate` assembles new hosted projects with full Spec/Runtime/API/MCP/Admin/Web by default, or a smaller positive `--features` selection, then validates and compiles manifests into a typed module. Saved selections rerun safely; legacy authored projects keep compile mode. Selected Admin syncs its prebuilt SPA into `public/_mantle/admin/`. `--check` fails without writing on drift. `skills` copies project-scoped skills into `.agents/skills/mantle-*` and `.claude/skills/mantle-*`. `validate --phase deploy` adds pre-deploy checks; `emit-openapi` covers HTTP Triggers and View REST routes. `mantle-harness indexes` checks SQLite plans, while `http` samples a running Worker. [Project and CLI](../start/project-and-cli.md) has the install and ownership sequence.
127
127
 
128
128
  ## The generated module
129
129
 
@@ -182,7 +182,7 @@ This handbook describes the snapshot in this source tree. Pin every `@aotter/man
182
182
 
183
183
  The documentation site pins its handbook commit in `docs/handbook.json` and records its vendored SDK source in `vendor/mantle/SOURCE.txt`. Those commits may differ when the intervening changes are behavior-neutral; the site verifies that condition during the build.
184
184
 
185
- Projects are authored directly: write the manifests, run `generate`, `skills`, `validate` and `emit-openapi`, and wire the Worker. There is no project generator. Prerelease packages take their exact version from their own `package.json`; APIs may change between prereleases until a stable line ships.
185
+ New projects can use `generate` to assemble a blank hosted app. Existing authored Workers can keep their own entry and use the same CLI for typed compilation. Pin all selected packages to one exact SDK version; prerelease APIs can change until stable publication.
186
186
 
187
187
  ## Source
188
188
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Trigger field reference — http, mcp and lifecycle sources, path and tool-name rules, the eight lifecycle hooks and their timing and error policy.
2
+ description: Trigger field reference — http, mcp, lifecycle and schedule sources, routing rules, and delivery semantics.
3
3
  ---
4
4
  # Trigger
5
5
 
@@ -19,9 +19,29 @@ A Trigger binds one source to one [Procedure](./procedure.md). Every external su
19
19
  | `http` | `method`, `path` | One REST endpoint under `/api/`. |
20
20
  | `mcp` | `surface` | One tool on `/mcp` or `/mcp/staff`. |
21
21
  | `lifecycle` | `schema`, `on`, `errorPolicy` | Entry-writer hooks on one Schema. |
22
+ | `schedule` | `cron`, optional `enabled` | A Cloudflare Cron Trigger calling one Procedure. |
22
23
 
23
24
  An unknown `kind`, a missing `kind`, or a key that does not belong to the chosen kind is `INVALID_MANIFEST_ENVELOPE`. One Procedure may carry several Triggers — that is how the same handler becomes an HTTP endpoint and an MCP tool without duplicating logic.
24
25
 
26
+ ## `schedule` source
27
+
28
+ ```yaml
29
+ apiVersion: cms.mantle.aotter.net/v1
30
+ kind: Trigger
31
+ metadata: { name: nightly-cleanup }
32
+ spec:
33
+ source: { kind: schedule, cron: "0 2 * * *" }
34
+ target: { procedure: clean-expired-records }
35
+ ```
36
+
37
+ `cron` is a five-field Cloudflare UTC expression (minute, hour, day, month, weekday). Each field accepts `*`, a number, a range, comma-separated values, or `*/step` / `start-end/step`. Weekdays are **1 = Sunday through 7 = Saturday**; use `2-6` for Monday–Friday. Month and weekday names and provider-specific extensions are outside this subset. `enabled` defaults to `true`; a disabled schedule remains in the compiled plan but never invokes its Procedure. The target Procedure must accept `{}` as input (`SCHEDULE_INPUT_INVALID`) and have no user/staff authorization requirements (`SCHEDULE_AUTH_INVALID`). The schedule creates no public HTTP or MCP route.
38
+
39
+ On Cloudflare, add each enabled expression to `wrangler.jsonc` under `triggers.crons` and export the `scheduled` method returned by `createMantleWorker`. Mantle does not provision the provider trigger. A disabled expression still registered in Wrangler is ignored; an expression absent from the Mantle plan fails visibly. If the Worker also owns unrelated cron tasks, route those expressions in the application's `scheduled` export. Multiple Mantle Triggers may share one expression and run in Trigger-name order; all run even if an earlier one fails, then the event reports failure.
40
+
41
+ Handlers receive `ctx.schedule` with `id`, `trigger`, `cron`, and `scheduledTime` (Unix milliseconds). The stable ID is `${trigger}:${scheduledTime}`. The caller is always `user: null`, `staff: null`, with no credential metadata; normal Procedure guards still apply. Cloudflare executes cron events in UTC, but its Cron Trigger documentation does not promise automatic retries. Duplicate/manual replay is still possible, so use `ctx.schedule.id` as an idempotency key for writes. A failure rejects the scheduled event; exactly-once execution is not promised. Bun, Vercel, and ChatGPT Sites do not register schedules and reject enabled schedules at boot or generation.
42
+
43
+ Cloudflare's default D1 composition records each attempt under that stable run ID. The owner-only Developer Console shows declared schedules even before the first run, separately from recent observations; Wrangler registration is **not observed** by Core. Records include scheduled/start/finish times, status, attempt, duration, a diagnostic code or generic handler-error summary, and optional sweep counts. A started attempt with no finish record says **Completion not observed**, not success. Inputs, outputs, exception text, credentials and secrets are not stored. The default history retains 30 days, hides older rows on reads, and prunes them during later runs (at most once per day per runtime instance). If observation start fails, the Procedure is skipped and the scheduled event rejects; if recording completion fails, the event also rejects. Use the stable ID to make replay safe. If the host supplies custom storage without a durable observation store, the Console says **Unavailable**; it never reports a missing record as success.
44
+
25
45
  ## `http` source
26
46
 
27
47
  ```yaml
@@ -21,6 +21,61 @@ All eleven packages share a single version and are published together, so mixed
21
21
  versions across `@aotter/mantle*` are never a supported combination. Pin the
22
22
  version you install and upgrade the whole set at once.
23
23
 
24
+ ## 0.1.5 — in preparation
25
+
26
+ `mantle generate` now assembles a blank, editable full site by default for
27
+ Cloudflare Workers or ChatGPT Sites. `--features` positively selects a smaller
28
+ composition; Spec-only needs no host. The first run declares exact matching
29
+ dependencies, and a second run after installation completes the project.
30
+ Existing directly authored applications retain their compile path. Saved
31
+ choices and user-owned files survive reruns, while `--check` reports drift
32
+ without writing. On Sites, review and apply append-only D1 migrations before
33
+ running code that expects the new Schema. The new Sites worker trusts identity
34
+ only behind the Sites dispatcher and does not expose a separate `workers.dev`
35
+ URL.
36
+
37
+ Reviewed Sites unique-index tuple replacement can now generate an immutable
38
+ SQL migration with source/target fingerprints, checksum and an explicit report.
39
+ Local D1 duplicate preflight fails before writing files; production D1 still
40
+ enforces the new constraint when the reviewed migration is applied. Runtime-
41
+ managed CF boot continues to reject uniqueness changes without a reviewed
42
+ artifact path.
43
+
44
+ The owner-only Developer Console can inspect live Schema entries and run
45
+ declared Views on demand through guarded Admin routes. Its HTTP and MCP lists
46
+ link to the owning declarations. UI action placement is no longer shown as a
47
+ Procedure-to-Schema execution path; custom handlers and native SQL Views state
48
+ when their data relationships cannot be inferred from the Manifest.
49
+
50
+ Ref Procedures can now group create, update and delete operations across
51
+ Schemas with `ctx.writeAtomically`. D1 and Bun commit the group or roll it
52
+ back, including when the last conditional write finds a stale version. Other
53
+ adapters must explicitly implement the optional capability before using it.
54
+
55
+ Cloudflare Cron Triggers now target ordinary Procedures through `source.kind:
56
+ schedule`. The generated runtime plan records each schedule, while Wrangler
57
+ registration remains application-owned. Scheduled calls have no user or staff
58
+ authority, carry a stable retry key, and pass through Procedure validation and
59
+ authorization. D1-backed Workers retain owner-only run observations
60
+ for 30 days; the Console distinguishes a declaration from registration and
61
+ observed executions. Managed SQLite hosts must apply the append-only
62
+ `0007-schedule-run-observations` migration before boot. Other hosts do not
63
+ register schedules.
64
+
65
+ Schemas may declare a `ttl` date-time policy. D1 and Bun hide expired entries
66
+ from semantic reads and declarative Views before any deletion. Physical cleanup
67
+ is an explicit bounded sweep, previewed by default and resumable by cursor;
68
+ there is no automatic bulk deletion when a policy is introduced or shortened.
69
+ Native SQL Views and shared View caches are rejected for TTL Schemas because
70
+ they cannot guarantee the expiry boundary. Cloudflare public routes that can
71
+ include TTL content use `no-store` so a cached page cannot outlive its entries.
72
+
73
+ Agents installed through `npx skills add aotter/mantle` must still inspect the
74
+ target project's actual SDK version. Published 0.1.4 packages do **not**
75
+ support the new project flags; 0.1.5-alpha.1 does. Use the installed docs and
76
+ upgrade all selected packages together. The bootstrap skill remains a small directory,
77
+ not a repository clone.
78
+
24
79
  ## 0.1.4 — 2026-09-24
25
80
 
26
81
  0.1.4 repairs the published consumer path without changing the Manifest
@@ -21,6 +21,7 @@ current npm `latest` contains it.
21
21
  |---|---|---|
22
22
  | Understand what manifests can express | [Manifest feature reference](../reference/features.md) | A capability-to-field map across the four atoms. |
23
23
  | Validate manifests in an existing tool | [Spec-only adoption](../../spec-only-host-adoption.md) | Parse and link without Runtime, storage or a UI. |
24
+ | Bring Mantle into an existing application | [Integration skill](../../skills/integrate/SKILL.md) | Inspect the app and choose embedding, incremental replacement or rebuilding with data migration. |
24
25
  | Embed Runtime in an existing host | [Runtime and adapters](../concepts/runtime-and-adapters.md), then [typed queries](../guides/typed-queries.md) | Bind your storage and call the generated API. |
25
26
  | Build a local Cloudflare API | [Minimal Worker tutorial](./quickstart-worker.md) | A running public View and a verified HTTP response. |
26
27
  | Add a staff console | [Local Admin tutorial](./quickstart-admin.md) | Email OTP, Admin assets and a local human workflow. |
@@ -28,8 +29,10 @@ current npm `latest` contains it.
28
29
  | Work through a coding agent | [Skill installation and handoff](../guides/agent-setup.md) | Bootstrap skill, pinned package, then project-local instructions. |
29
30
 
30
31
  Human authors can follow these pages directly; installing an agent skill is
31
- optional. Do not start with `mantle generate` in an empty directory: author the
32
- manifests and host first, then generate and validate.
32
+ optional. For a new app, install an exact SDK version and run
33
+ `mantle generate --host cf` or `--host chatgpt-sites`. The CLI assembles a blank
34
+ site by default; add your own Manifests after generation. Use explicit
35
+ `--features` for a smaller composition, including host-free Spec-only.
33
36
 
34
37
  ## Find the right kind of documentation
35
38