@aotter/mantle 0.1.3-alpha.1 → 0.1.3-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 +12 -10
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +1 -1
- package/docs/adr/0024-manifest-native-schema-tables.md +9 -6
- package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +105 -0
- package/docs/adr/README.md +2 -1
- package/docs/agent-prompts.md +3 -3
- package/docs/examples/builtin-intake.md +5 -5
- package/docs/examples/builtin-publication.md +1 -1
- package/docs/examples/builtin-reservation.md +5 -5
- package/docs/examples/cf-primitives-intake-hooks.md +4 -4
- 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 +3 -3
- package/docs/handbook/concepts/views.md +2 -2
- package/docs/handbook/reference/diagnostics.md +2 -1
- package/docs/handbook/reference/manifest.md +1 -1
- package/docs/handbook/reference/schema.md +12 -4
- package/docs/handbook/reference/view.md +2 -0
- package/docs/handbook/start/project-and-cli.md +7 -8
- package/docs/release-process.md +58 -8
- package/docs/spec-only-host-adoption.md +1 -1
- package/package.json +17 -17
- package/skills/README.md +8 -9
- package/skills/develop/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -9,14 +9,14 @@ where agents write config and the runtime carries the complexity.
|
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
|
-
Cold start for a new application is the
|
|
12
|
+
Cold start for a new application is the install skill, not a bare npm add:
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
npx skills add aotter/mantle
|
|
15
|
+
npx skills add aotter/mantle --skill install
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
To depend on this package in an existing project, pin the exact version
|
|
19
|
-
from `package.json` (currently `0.1.3-alpha.
|
|
19
|
+
from `package.json` (currently `0.1.3-alpha.3`):
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm install @aotter/mantle
|
|
@@ -197,7 +197,7 @@ internals or rebuilding Mantle's adapters.
|
|
|
197
197
|
Cold start from GitHub or a marketplace host:
|
|
198
198
|
|
|
199
199
|
```sh
|
|
200
|
-
npx skills add aotter/mantle
|
|
200
|
+
npx skills add aotter/mantle --skill install
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
That skill interviews, pins this package, then uses the CLI and the
|
|
@@ -215,26 +215,28 @@ Install the Mantle Core skill bundle before authoring or maintaining a
|
|
|
215
215
|
consumer application. The canonical command is:
|
|
216
216
|
|
|
217
217
|
```sh
|
|
218
|
-
npx skills add aotter/mantle
|
|
218
|
+
npx skills add aotter/mantle --skill install
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Claude Code and Codex can install the
|
|
221
|
+
Claude Code and Codex can install the plugin, then run that skill:
|
|
222
222
|
|
|
223
223
|
```bash
|
|
224
224
|
# Claude Code
|
|
225
|
-
/plugin marketplace add aotter/mantle
|
|
225
|
+
/plugin marketplace add aotter/mantle
|
|
226
226
|
/plugin install mantle@mantle
|
|
227
227
|
|
|
228
228
|
# Codex
|
|
229
|
-
codex plugin marketplace add aotter/mantle
|
|
229
|
+
codex plugin marketplace add aotter/mantle
|
|
230
230
|
codex plugin add mantle@mantle
|
|
231
231
|
```
|
|
232
232
|
|
|
233
233
|
Cursor and VS Code Copilot can auto-discover the GitHub repo through
|
|
234
234
|
`.cursor-plugin/plugin.json` and `.copilot-plugin/plugin.json` after the repo
|
|
235
235
|
is cloned or opened. Still start from the `npx skills add` sentence (or open
|
|
236
|
-
`skills/install/SKILL.md`
|
|
237
|
-
at a
|
|
236
|
+
`skills/install/SKILL.md`). Untagged `aotter/mantle` resolves to `main`, which
|
|
237
|
+
only advances at a release, so it is always the latest published version. Add
|
|
238
|
+
`@vX.Y.Z` only to reproduce an older project; never point a consumer at
|
|
239
|
+
`develop` or another moving branch.
|
|
238
240
|
|
|
239
241
|
## Marketplace capability installs
|
|
240
242
|
|
package/dist/cli/main.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export declare const MANTLE_OVERVIEW = "mantle \u2014 compile manifests into a RuntimePlan and typed binding\n\nOverview\n Optional surfaces \u2014 take only what you need. Admin is opt-in. You can ship a\n complete service with no Dev UI, no visitor frontend, and no extra packages.\n\n Minimal \u2014 Spec + generate\n validate + generate compile manifests into a sealed plan and typed binding.\n Embed that binding in an existing host. No Admin, no visitor UI.\n\n Runtime / adapter\n Bind Runtime through an adapter (Cloudflare Worker, Bun, Vercel, or yours).\n HTTP Views, MCP, and Auth work without Admin.\n See docs/examples/host-minimal-worker or\n node_modules/@aotter/mantle/docs/examples/host-minimal-worker.\n\n Opt-in \u2014 Admin / Dev UI\n Add @aotter/mantle-admin and @aotter/mantle-admin-ui only when humans need\n a console. Then re-run generate, bind wrangler ASSETS, and open\n /admin/sign-in (local email OTP via ConsoleEmailSender).\n See docs/examples/host-local-admin-otp or\n node_modules/@aotter/mantle/docs/examples/host-local-admin-otp.\n\n Further (ask the subcommand for details)\n skills project version-matched agent instructions\n emit-openapi OpenAPI 3.1 from HTTP Triggers and Views\n mantle-harness measure indexes and live HTTP (separate binary)\n\nUsage: mantle <subcommand> [options]\n\nSubcommands:\n generate Compile manifests into a typed runtime binding\n validate Static manifest and handler-source validation\n skills Project version-matched Core skills\n emit-openapi Emit OpenAPI 3.1 from Triggers and Views\n\nDocumentation:\n Handbook: node_modules/@aotter/mantle/docs/handbook/ (or docs/handbook/)\n Online: https://mantle.tools/\n Install skill: npx skills add aotter/mantle
|
|
2
|
+
export declare const MANTLE_OVERVIEW = "mantle \u2014 compile manifests into a RuntimePlan and typed binding\n\nOverview\n Optional surfaces \u2014 take only what you need. Admin is opt-in. You can ship a\n complete service with no Dev UI, no visitor frontend, and no extra packages.\n\n Minimal \u2014 Spec + generate\n validate + generate compile manifests into a sealed plan and typed binding.\n Embed that binding in an existing host. No Admin, no visitor UI.\n\n Runtime / adapter\n Bind Runtime through an adapter (Cloudflare Worker, Bun, Vercel, or yours).\n HTTP Views, MCP, and Auth work without Admin.\n See docs/examples/host-minimal-worker or\n node_modules/@aotter/mantle/docs/examples/host-minimal-worker.\n\n Opt-in \u2014 Admin / Dev UI\n Add @aotter/mantle-admin and @aotter/mantle-admin-ui only when humans need\n a console. Then re-run generate, bind wrangler ASSETS, and open\n /admin/sign-in (local email OTP via ConsoleEmailSender).\n See docs/examples/host-local-admin-otp or\n node_modules/@aotter/mantle/docs/examples/host-local-admin-otp.\n\n Further (ask the subcommand for details)\n skills project version-matched agent instructions\n emit-openapi OpenAPI 3.1 from HTTP Triggers and Views\n mantle-harness measure indexes and live HTTP (separate binary)\n\nUsage: mantle <subcommand> [options]\n\nSubcommands:\n generate Compile manifests into a typed runtime binding\n validate Static manifest and handler-source validation\n skills Project version-matched Core skills\n emit-openapi Emit OpenAPI 3.1 from Triggers and Views\n\nDocumentation:\n Handbook: node_modules/@aotter/mantle/docs/handbook/ (or docs/handbook/)\n Online: https://mantle.tools/\n Install skill: npx skills add aotter/mantle --skill install\n";
|
|
3
3
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAgCA,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAgCA,eAAO,MAAM,eAAe,kxDAwC3B,CAAC"}
|
package/dist/cli/main.js
CHANGED
|
@@ -64,7 +64,7 @@ Subcommands:
|
|
|
64
64
|
Documentation:
|
|
65
65
|
Handbook: node_modules/@aotter/mantle/docs/handbook/ (or docs/handbook/)
|
|
66
66
|
Online: https://mantle.tools/
|
|
67
|
-
Install skill: npx skills add aotter/mantle
|
|
67
|
+
Install skill: npx skills add aotter/mantle --skill install
|
|
68
68
|
`;
|
|
69
69
|
main().then((code) => {
|
|
70
70
|
process.exitCode = code;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ADR-0024: Materialize each Schema as a native storage table
|
|
2
2
|
|
|
3
|
-
**Status:** Accepted
|
|
3
|
+
**Status:** Accepted; the clauses allowing authored data to use `id`, `status` or `createdAt` are superseded by [ADR-0025](0025-reserved-native-columns-and-published-only-public-views.md) (2026-09-22)
|
|
4
4
|
|
|
5
5
|
**Date:** 2026-09-18
|
|
6
6
|
|
|
@@ -58,9 +58,11 @@ identifier quoted. A Schema table uses its authored Schema name. SQLite
|
|
|
58
58
|
preparation rejects collisions with selected infrastructure tables or views,
|
|
59
59
|
including auth tables; this check belongs to the concrete deployment because
|
|
60
60
|
the portable Manifest cannot know which optional infrastructure a host binds.
|
|
61
|
-
Internal Mantle tables and columns use the reserved `_mantle_` prefix
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
Internal Mantle tables and columns use the reserved `_mantle_` prefix, so
|
|
62
|
+
Mantle metadata never collides with authored column names. *Superseded in
|
|
63
|
+
part by ADR-0025:* authored data may no longer use the six native column
|
|
64
|
+
names (`id`, `status`, `version`, `createdAt`, `updatedAt`, `authorId`); the
|
|
65
|
+
prefix remains for every other authored name.
|
|
64
66
|
|
|
65
67
|
The SQLite adapter no longer creates `entries`, generated field columns, or
|
|
66
68
|
Schema projection views. There is no dual write, compatibility view, or
|
|
@@ -247,8 +249,9 @@ Reviewed after implementation against ADR-0019, the semantic storage ports,
|
|
|
247
249
|
Builder preview persistence, and Mantle Cloud's retryable deployment state.
|
|
248
250
|
The review found and corrected three boundary mistakes before acceptance:
|
|
249
251
|
|
|
250
|
-
- authored data
|
|
251
|
-
physical envelope columns require the `_mantle_` prefix;
|
|
252
|
+
- authored data was allowed to contain `id`, `status`, or `createdAt`, so
|
|
253
|
+
physical envelope columns require the `_mantle_` prefix (the prefix stays;
|
|
254
|
+
ADR-0025 later reserved those names for authored data as well);
|
|
252
255
|
- Builder should expose draft storage risk without pretending it knows the
|
|
253
256
|
deployed baseline; Cloud shows the exact immutable artifact, while CI and
|
|
254
257
|
Cloud/D1 own executable DDL verification and preview stays on IndexedDB;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ADR-0025: Reserved native column names and published-only public Views
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
|
|
5
|
+
**Date:** 2026-09-22
|
|
6
|
+
|
|
7
|
+
**Related:** [#962](https://github.com/aotter/mantle/issues/962),
|
|
8
|
+
[#1007](https://github.com/aotter/mantle/issues/1007),
|
|
9
|
+
[#1008](https://github.com/aotter/mantle/issues/1008),
|
|
10
|
+
[#1009](https://github.com/aotter/mantle/issues/1009),
|
|
11
|
+
[#1010](https://github.com/aotter/mantle/issues/1010),
|
|
12
|
+
[ADR-0022](0022-caller-observed-version-occ.md),
|
|
13
|
+
[ADR-0024](0024-manifest-native-schema-tables.md) (partially superseded: its clauses allowing authored `id`, `status`, `createdAt`)
|
|
14
|
+
|
|
15
|
+
## Context
|
|
16
|
+
|
|
17
|
+
Every entry carries six native columns outside `data`: `id`, `status`,
|
|
18
|
+
`version`, `createdAt`, `updatedAt`, `authorId`. Before this decision the
|
|
19
|
+
grammar treated their names inconsistently:
|
|
20
|
+
|
|
21
|
+
- A Schema data property could reuse one of the names. The SQLite dialect then
|
|
22
|
+
resolved the name to the data column first; IndexedDB resolved it to the
|
|
23
|
+
native field first. The same View read different columns on different
|
|
24
|
+
storage.
|
|
25
|
+
- `Schema.spec.indexes` could not reference the names at all
|
|
26
|
+
(`SCHEMA_INDEX_INVALID`), while the runtime's own indexes lead with
|
|
27
|
+
`_mantle_status`.
|
|
28
|
+
- A public View over a `publishing` Schema hid drafts only when the author
|
|
29
|
+
wrote `filter: eq status published`. The handbook promised drafts are
|
|
30
|
+
unreadable until published; nothing enforced it on REST, MCP
|
|
31
|
+
`query_view_*` or WebMCP, which share one compiled plan.
|
|
32
|
+
|
|
33
|
+
The #962 soak measured the consequence of the second point: the list View
|
|
34
|
+
read 14,940 rows per request on a 10,991-row D1 table. Without planner
|
|
35
|
+
statistics, which production SQLite-family storage never has, SQLite chose
|
|
36
|
+
the runtime's `(_mantle_status, _mantle_updated_at, _mantle_id)` index for the
|
|
37
|
+
status equality and sorted every published row in a temporary B-tree; the
|
|
38
|
+
declared `[publishedAt]` index was never used, and the grammar refused the
|
|
39
|
+
`(status, publishedAt)` index that would have served the query.
|
|
40
|
+
|
|
41
|
+
Two alternatives were considered and rejected during review (#1008):
|
|
42
|
+
|
|
43
|
+
- **Core derives indexes from Views.** Rejected: the manifest is a deployment
|
|
44
|
+
spec; Mantle Core is not a query optimizer, and a derived index has no
|
|
45
|
+
owner when a View is deleted.
|
|
46
|
+
- **Explicit object references** such as `{ meta: status }` /
|
|
47
|
+
`{ field: publishedAt, direction: desc }`. Deferred: it adds a grammar shape
|
|
48
|
+
to solve an ambiguity that reserving the names removes, and no shipped View
|
|
49
|
+
needs mixed-direction ordering. It remains the upgrade path.
|
|
50
|
+
|
|
51
|
+
A data-first resolution rule with only `status` reserved was also rejected: an
|
|
52
|
+
index declared as `[[createdAt]]` would silently change meaning when a data
|
|
53
|
+
property `createdAt` is added later, while the index identity (field-name
|
|
54
|
+
hex) kept the old physical index.
|
|
55
|
+
|
|
56
|
+
## Decision
|
|
57
|
+
|
|
58
|
+
1. **The six native column names are reserved.** A Schema data property may
|
|
59
|
+
not use them; `mantle validate` fails closed with
|
|
60
|
+
`INVALID_MANIFEST_ENVELOPE` at `/spec/schema/properties/<name>`, the same
|
|
61
|
+
mechanism ADR-0022 uses for `expectedVersion`. Domain names replace them
|
|
62
|
+
(`submittedAt`, `orderStatus`, `submittedBy`).
|
|
63
|
+
2. **Every storage resolves a native name to the native column, first.** With
|
|
64
|
+
shadowing rejected this is a no-op for valid manifests; it is stated so the
|
|
65
|
+
SQLite dialect, IndexedDB and the index DDL cannot drift apart again.
|
|
66
|
+
3. **`Schema.spec.indexes` may reference native names**; the SQLite dialect
|
|
67
|
+
maps them to `_mantle_*` columns in `CREATE INDEX`. `uniqueIndexes` may not:
|
|
68
|
+
native columns carry no domain uniqueness. Tuples stay `string[][]`; no
|
|
69
|
+
direction, no object form.
|
|
70
|
+
4. **The author declares the access path; Mantle measures it.** A public View
|
|
71
|
+
over a `publishing` Schema always carries `status = published`, so its
|
|
72
|
+
index leads with `status` (`indexes: [[status, publishedAt]]`). Core does
|
|
73
|
+
not synthesize that index. The harness and the D1 inspector (#1010) report
|
|
74
|
+
whether the declared index is used.
|
|
75
|
+
5. **Public Views over `publishing` Schemas read published rows only, fail
|
|
76
|
+
closed.** The linker injects `status = published` into the compiled plan
|
|
77
|
+
whether or not the manifest writes it; an authored identical predicate is
|
|
78
|
+
redundant and kept; any other `status` comparison on such a View is
|
|
79
|
+
`VIEW_PUBLIC_STATUS_INVALID`. Staff Views, `operational` Schemas and SQL
|
|
80
|
+
Views are untouched.
|
|
81
|
+
|
|
82
|
+
## Consequences
|
|
83
|
+
|
|
84
|
+
- ADR-0024's statement that authored data may legitimately use `id`, `status`
|
|
85
|
+
or `createdAt` no longer holds; its `_mantle_` physical prefix is unchanged
|
|
86
|
+
and still guards every other authored name.
|
|
87
|
+
|
|
88
|
+
- Breaking for manifests that declared a data property with one of the six
|
|
89
|
+
names; the official examples and tests were renamed (#1009). Storage is
|
|
90
|
+
additive: the renamed property is a new column and the old one stays until
|
|
91
|
+
an explicit migration. The 0.1.3 release entry carries the upgrade note.
|
|
92
|
+
- The semantic fingerprint of any plan with a public publishing View changes
|
|
93
|
+
once (the injected predicate); `mantle generate` regenerates it.
|
|
94
|
+
- Index identities of existing declarations are unchanged; native-name tuples
|
|
95
|
+
get new identities. Stale indexes left behind by redeclaration are tracked
|
|
96
|
+
separately (#1011).
|
|
97
|
+
- Verified on real D1 without `ANALYZE`: the list View reads 50 rows per
|
|
98
|
+
request with `[status, publishedAt]` and 10,000 without it (#1014).
|
|
99
|
+
|
|
100
|
+
## Where this lives
|
|
101
|
+
|
|
102
|
+
The handbook is the single source: [`reference/schema.md`](../handbook/reference/schema.md)
|
|
103
|
+
(reserved columns, index rules), [`reference/view.md`](../handbook/reference/view.md)
|
|
104
|
+
(surfaces). Skills and CLI diagnostics point there; they do not restate the
|
|
105
|
+
rules.
|
package/docs/adr/README.md
CHANGED
|
@@ -23,7 +23,8 @@ Records of *why* mantle ended up shaped this way. The numbering preserves POC AD
|
|
|
23
23
|
| [0020](0020-builtin-handler-contracts-and-matched-upsert.md) | Static builtin handler contracts and natural-key matched upsert (`handler.match`). | Accepted + amended by 0022 |
|
|
24
24
|
| [0021](0021-retire-starter-scaffolding.md) | Direct authoring; retire Starter scaffolding. | Accepted; supersedes 0018 |
|
|
25
25
|
| [0022](0022-caller-observed-version-occ.md) | Caller-observed `expectedVersion` for Admin bind and builtin upsert OCC. | Accepted |
|
|
26
|
-
| [0024](0024-manifest-native-schema-tables.md) | Materialize each Schema as a native storage table and deploy reviewed migration artifacts. | Accepted |
|
|
26
|
+
| [0024](0024-manifest-native-schema-tables.md) | Materialize each Schema as a native storage table and deploy reviewed migration artifacts. | Accepted; authored-native-name clauses superseded by 0025 |
|
|
27
|
+
| [0025](0025-reserved-native-columns-and-published-only-public-views.md) | Reserve the six native column names, let `indexes` reference them, and compile public publishing Views published-only. | Accepted |
|
|
27
28
|
|
|
28
29
|
## Reading order
|
|
29
30
|
|
package/docs/agent-prompts.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Task-specific agent prompts
|
|
2
2
|
|
|
3
|
-
Cold start from GitHub or a marketplace host is
|
|
3
|
+
Cold start from GitHub or a marketplace host is the install skill:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npx skills add aotter/mantle
|
|
6
|
+
npx skills add aotter/mantle --skill install
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Copy one block into a coding agent after that skill is present. Paths below
|
|
@@ -20,7 +20,7 @@ manifests exist. Admin is opt-in.
|
|
|
20
20
|
```text
|
|
21
21
|
Interview me about the service: host, who uses it, whether humans need a
|
|
22
22
|
Dev UI, and whether we only embed Spec/Runtime. If the install skill is
|
|
23
|
-
missing, run npx skills add aotter/mantle
|
|
23
|
+
missing, run npx skills add aotter/mantle --skill install. Read the
|
|
24
24
|
install skill and npx --no-install mantle --help, then
|
|
25
25
|
handbook/start/project-and-cli.md.
|
|
26
26
|
Use examples/README.md as the examples index; copy builtin-* Manifests only
|
|
@@ -30,7 +30,7 @@ spec:
|
|
|
30
30
|
name: { type: string, minLength: 1, maxLength: 120 }
|
|
31
31
|
email: { type: string, format: email }
|
|
32
32
|
message: { type: string, minLength: 1, maxLength: 2000 }
|
|
33
|
-
|
|
33
|
+
submittedAt: { type: number, x-mcp-hint: timestamp-ms, x-mantle-bind: now }
|
|
34
34
|
---
|
|
35
35
|
apiVersion: cms.mantle.aotter.net/v1
|
|
36
36
|
kind: View
|
|
@@ -40,9 +40,9 @@ spec:
|
|
|
40
40
|
title: Recent requests
|
|
41
41
|
surface: staff
|
|
42
42
|
from: requests
|
|
43
|
-
fields: [id, name, email, message,
|
|
43
|
+
fields: [id, name, email, message, submittedAt]
|
|
44
44
|
orderBy:
|
|
45
|
-
- { field:
|
|
45
|
+
- { field: submittedAt, direction: desc }
|
|
46
46
|
limit: 50
|
|
47
47
|
---
|
|
48
48
|
apiVersion: cms.mantle.aotter.net/v1
|
|
@@ -80,7 +80,7 @@ spec:
|
|
|
80
80
|
target: { procedure: submit-request }
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
`
|
|
83
|
+
`submittedAt` is stamped by the server (`x-mantle-bind: now`); a caller-supplied value is ignored. It lives in `data` next to the native `createdAt` column so the timestamp travels with the record through MCP output and exports; data properties may not reuse the native column names. The builtin `create` projects `input ∩ Schema.properties`.
|
|
84
84
|
|
|
85
85
|
## Worker and handlers
|
|
86
86
|
|
|
@@ -109,7 +109,7 @@ curl -sS -X POST http://localhost:8787/api/requests \
|
|
|
109
109
|
"collection": "requests",
|
|
110
110
|
"status": "published",
|
|
111
111
|
"version": 1,
|
|
112
|
-
"data": { "name": "Ada", "email": "ada@example.test", "message": "Please call me back.", "
|
|
112
|
+
"data": { "name": "Ada", "email": "ada@example.test", "message": "Please call me back.", "submittedAt": 1788879363492 },
|
|
113
113
|
"authorId": null,
|
|
114
114
|
"createdAt": 1788879363492,
|
|
115
115
|
"updatedAt": 1788879363492
|
|
@@ -97,7 +97,7 @@ spec:
|
|
|
97
97
|
title: { type: string }
|
|
98
98
|
email: { type: string, format: email }
|
|
99
99
|
note: { type: string }
|
|
100
|
-
|
|
100
|
+
suggestedAt: { type: integer, x-mcp-hint: timestamp-ms, x-mantle-bind: now }
|
|
101
101
|
---
|
|
102
102
|
apiVersion: cms.mantle.aotter.net/v1
|
|
103
103
|
kind: Procedure
|
|
@@ -31,7 +31,7 @@ spec:
|
|
|
31
31
|
requestedFor: { type: string, description: Requested date, time, or slot. }
|
|
32
32
|
partySize: { type: integer, minimum: 1 }
|
|
33
33
|
note: { type: string, maxLength: 1000 }
|
|
34
|
-
|
|
34
|
+
submittedAt: { type: number, x-mcp-hint: timestamp-ms, x-mantle-bind: now }
|
|
35
35
|
---
|
|
36
36
|
apiVersion: cms.mantle.aotter.net/v1
|
|
37
37
|
kind: View
|
|
@@ -41,9 +41,9 @@ spec:
|
|
|
41
41
|
title: Reservation queue
|
|
42
42
|
surface: staff
|
|
43
43
|
from: reservations
|
|
44
|
-
fields: [id, name, email, requestedFor, partySize, note,
|
|
44
|
+
fields: [id, name, email, requestedFor, partySize, note, submittedAt]
|
|
45
45
|
orderBy:
|
|
46
|
-
- { field:
|
|
46
|
+
- { field: submittedAt, direction: desc }
|
|
47
47
|
limit: 50
|
|
48
48
|
---
|
|
49
49
|
apiVersion: cms.mantle.aotter.net/v1
|
|
@@ -82,7 +82,7 @@ spec:
|
|
|
82
82
|
target: { procedure: submit-reservation }
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
`
|
|
85
|
+
`submittedAt` is stamped by the server (`x-mantle-bind: now`); a caller-supplied value is ignored. `requestedFor` is a free string on purpose: this example does not impose a calendar model. The staff View orders by `submittedAt`, so the newest request is first regardless of the requested slot.
|
|
86
86
|
|
|
87
87
|
## Worker and handlers
|
|
88
88
|
|
|
@@ -113,7 +113,7 @@ curl -sS -X POST http://localhost:8787/api/reservations \
|
|
|
113
113
|
"collection": "reservations",
|
|
114
114
|
"status": "published",
|
|
115
115
|
"version": 1,
|
|
116
|
-
"data": { "name": "Ada", "email": "ada@example.test", "requestedFor": "2026-10-03T19:00:00+08:00", "partySize": 4, "
|
|
116
|
+
"data": { "name": "Ada", "email": "ada@example.test", "requestedFor": "2026-10-03T19:00:00+08:00", "partySize": 4, "submittedAt": 1788879363492 },
|
|
117
117
|
"authorId": null,
|
|
118
118
|
"createdAt": 1788879363492,
|
|
119
119
|
"updatedAt": 1788879363492
|
|
@@ -30,7 +30,7 @@ spec:
|
|
|
30
30
|
name: { type: string, minLength: 1, maxLength: 120 }
|
|
31
31
|
email: { type: string, format: email }
|
|
32
32
|
message: { type: string, minLength: 1, maxLength: 2000 }
|
|
33
|
-
|
|
33
|
+
submittedAt: { type: number, x-mcp-hint: timestamp-ms, x-mantle-bind: now }
|
|
34
34
|
---
|
|
35
35
|
apiVersion: cms.mantle.aotter.net/v1
|
|
36
36
|
kind: View
|
|
@@ -40,9 +40,9 @@ spec:
|
|
|
40
40
|
title: Recent requests
|
|
41
41
|
surface: staff
|
|
42
42
|
from: requests
|
|
43
|
-
fields: [id, name, email, message,
|
|
43
|
+
fields: [id, name, email, message, submittedAt]
|
|
44
44
|
orderBy:
|
|
45
|
-
- { field:
|
|
45
|
+
- { field: submittedAt, direction: desc }
|
|
46
46
|
limit: 50
|
|
47
47
|
---
|
|
48
48
|
apiVersion: cms.mantle.aotter.net/v1
|
|
@@ -264,7 +264,7 @@ curl -sS -X POST http://localhost:8787/api/requests \
|
|
|
264
264
|
"collection": "requests",
|
|
265
265
|
"status": "published",
|
|
266
266
|
"version": 1,
|
|
267
|
-
"data": { "name": "Ada", "email": "ada@example.test", "message": "Please call me back.", "
|
|
267
|
+
"data": { "name": "Ada", "email": "ada@example.test", "message": "Please call me back.", "submittedAt": 1788879363492 },
|
|
268
268
|
"authorId": null,
|
|
269
269
|
"createdAt": 1788879363492,
|
|
270
270
|
"updatedAt": 1788879363492
|
|
@@ -76,7 +76,7 @@ Parsing is all-or-nothing: one error in one document means no `ParsedManifestSet
|
|
|
76
76
|
|
|
77
77
|
## Schema — the entity
|
|
78
78
|
|
|
79
|
-
A Schema declares one collection: the JSON Schema for each entry's `data`, its indexes, its Admin presentation and its [lifecycle mode](./lifecycle-and-locales.md). Entries also carry
|
|
79
|
+
A Schema declares one collection: the JSON Schema for each entry's `data`, its indexes, its Admin presentation and its [lifecycle mode](./lifecycle-and-locales.md). Entries also carry six [native columns](../reference/schema.md#reserved-entry-columns) outside `data`.
|
|
80
80
|
|
|
81
81
|
```yaml
|
|
82
82
|
apiVersion: cms.mantle.aotter.net/v1
|
|
@@ -9,7 +9,7 @@ Two per-Schema decisions shape how an entry behaves for its whole life: which st
|
|
|
9
9
|
|
|
10
10
|
`spec.lifecycle` is `publishing` (the default) or `operational`. The modes mix freely inside one site.
|
|
11
11
|
|
|
12
|
-
**`publishing`** is for content a person stages and then releases: posts, pages, announcements, product copy. The point of the mode is that a draft exists and is not readable by the public until someone publishes it.
|
|
12
|
+
**`publishing`** is for content a person stages and then releases: posts, pages, announcements, product copy. The point of the mode is that a draft exists and is not readable by the public until someone publishes it. The runtime enforces this: a public declarative View over a publishing Schema is compiled with `status = published` even when the manifest omits the filter ([View reference](../reference/view.md#surfaces)).
|
|
13
13
|
|
|
14
14
|
**`operational`** is for records written as a side effect rather than drafted: submissions, inquiries, orders, inventory snapshots, grant and audit rows. The point of the mode is that the row is real the moment it is created. Declare it on any Schema whose rows a human should inspect and correct, never stage and publish.
|
|
15
15
|
|
|
@@ -91,14 +91,14 @@ pnpm exec mantle skills --check
|
|
|
91
91
|
|
|
92
92
|
This copies every skill the installed package marks `projection: project` — the develop skill among them — into matching `.agents/skills/mantle-*` and `.claude/skills/mantle-*` paths. Both layouts receive identical bytes; `--check` detects drift without writing. Skills that act destructively or target one platform stay out of that set and are opt-in. Manifest generation never rewrites agent instructions.
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
The same bundle is installable from the plugin marketplace:
|
|
95
95
|
|
|
96
96
|
```sh
|
|
97
97
|
# Canonical
|
|
98
|
-
npx skills add aotter/mantle
|
|
98
|
+
npx skills add aotter/mantle --skill install
|
|
99
99
|
|
|
100
100
|
# Claude Code — two separate prompts
|
|
101
|
-
/plugin marketplace add aotter/mantle
|
|
101
|
+
/plugin marketplace add aotter/mantle
|
|
102
102
|
/plugin install mantle@mantle
|
|
103
103
|
```
|
|
104
104
|
|
|
@@ -97,7 +97,7 @@ a native column. Declared `indexes` and `uniqueIndexes` become B-tree indexes
|
|
|
97
97
|
over those columns; Core-compiled projections, filters and ordering reference
|
|
98
98
|
the same columns directly.
|
|
99
99
|
|
|
100
|
-
Declare the **smallest ordered index justified by the measured path**, and respect SQLite's leftmost-prefix rule.
|
|
100
|
+
Declare the **smallest ordered index justified by the measured path**, and respect SQLite's leftmost-prefix rule. Equality columns go first, the ordered column last. A public View over a publishing Schema always carries `status = published`, so its index leads with `status`: `[status, locale, publishedAt]` serves `WHERE status = ? AND locale = ?`, `… AND publishedAt > ?`, and `… ORDER BY publishedAt`. It does not serve `WHERE publishedAt > ?` alone, and `[publishedAt]` alone does not serve the published list — without planner statistics SQLite prefers the status equality and sorts in a temporary B-tree. If a second hot path needs a different leading field, that is a second index — not a reason to enumerate every permutation, since each index costs storage and slows every write.
|
|
101
101
|
|
|
102
102
|
```sh
|
|
103
103
|
pnpm exec mantle-harness indexes --require-public --format text
|
|
@@ -139,7 +139,7 @@ spec:
|
|
|
139
139
|
limit: 50
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
The source Schema declares `indexes: [[locale, publishedAt]]
|
|
142
|
+
The source Schema declares `indexes: [[status, locale, publishedAt]]`; the `status` predicate is present whether or not the View writes it. The `gte publishedAt 0` clause is what keeps the ordered column inside the indexed range rather than forcing a sort. Omitting `locale` returns 400.
|
|
143
143
|
|
|
144
144
|
## Example: a staff SQL report
|
|
145
145
|
|
|
@@ -50,7 +50,7 @@ Raised by the parser, the graph validator and the code generator. `mantle valida
|
|
|
50
50
|
|
|
51
51
|
| Code | Meaning | HTTP |
|
|
52
52
|
|---|---|---|
|
|
53
|
-
| `INVALID_MANIFEST_ENVELOPE` | Wrong `apiVersion`, unknown key at a known level, wrong value shape, a YAML syntax or alias-limit failure,
|
|
53
|
+
| `INVALID_MANIFEST_ENVELOPE` | Wrong `apiVersion`, unknown key at a known level, wrong value shape, a YAML syntax or alias-limit failure, a Schema data property named `expectedVersion` (reserved Procedure OCC token; ADR-0022), or a Schema data property named after a native entry column (`id`, `status`, `version`, `createdAt`, `updatedAt`, `authorId`). | — |
|
|
54
54
|
| `DUPLICATE_NAME` | Two documents of the same kind declare the same `metadata.name`. | — |
|
|
55
55
|
| `VIEW_FROM_UNKNOWN_SCHEMA` | `spec.from` names no declared Schema. | — |
|
|
56
56
|
| `VIEW_FIELD_NOT_IN_SCHEMA` | A `fields` entry or `orderBy[i].field` is neither a Schema property nor a reserved entry column. | — |
|
|
@@ -64,6 +64,7 @@ Raised by the parser, the graph validator and the code generator. `mantle valida
|
|
|
64
64
|
| `VIEW_FILTER_CTX_USER_REF_REQUIRES_INDEX` | The bound field is not the leftmost field of a declared index. | — |
|
|
65
65
|
| `VIEW_ORDERBY_INVALID` | An `orderBy` entry has the wrong shape or an unknown `direction`. | — |
|
|
66
66
|
| `VIEW_UI_INVALID` | A View `uiSchema` key is unknown, used on a public View, or names an unknown output field. | — |
|
|
67
|
+
| `VIEW_PUBLIC_STATUS_INVALID` | A public View over a `publishing` Schema compares `status` to anything but `published`. The runtime always reads published rows only on that surface, so the filter can only contradict it. | — |
|
|
67
68
|
| `REQUIRED_FIELD_UNKNOWN` | A `required` entry of `spec.schema` is not declared under `properties`. | — |
|
|
68
69
|
| `INVALID_PATTERN` | A `pattern` does not compile as a JavaScript regular expression. | — |
|
|
69
70
|
| `JSON_SCHEMA_UNSUPPORTED` | A JSON Schema keyword outside the accepted subset. | — |
|
|
@@ -108,7 +108,7 @@ Names containing `-` must be double-quoted when used as tables in a `sql` View (
|
|
|
108
108
|
|
|
109
109
|
| Namespace | Reserved | Effect |
|
|
110
110
|
|---|---|---|
|
|
111
|
-
| Entry columns | `id`, `status`, `version`, `createdAt`, `updatedAt`, `authorId` | Native on every Schema
|
|
111
|
+
| Entry columns | `id`, `status`, `version`, `createdAt`, `updatedAt`, `authorId` | Native on every Schema; a data property may not reuse the name (`INVALID_MANIFEST_ENVELOPE`). Where they may appear in Views and indexes: [Schema reference](./schema.md#reserved-entry-columns) (ADR-0025). |
|
|
112
112
|
| Data field | `locale` | A non-localized Schema that declares `properties.locale` is rejected; use a domain name such as `orderLocale`. On a localized Schema the runtime requires `data.locale` on writes. |
|
|
113
113
|
| Data field | `expectedVersion` | Reserved Procedure OCC wire name. A Schema that declares `spec.schema.properties.expectedVersion` is `INVALID_MANIFEST_ENVELOPE` (ADR-0022). New reserved names need an ADR. |
|
|
114
114
|
| View params | `page`, `show`, `cursor` | Owned by the runtime for pagination. Declaring them under `params.properties` is `VIEW_PARAMS_RESERVED_NAME`. |
|
|
@@ -14,7 +14,7 @@ A Schema declares one collection: the JSON Schema for each entry's `data`, its i
|
|
|
14
14
|
| `schema` | JSON Schema 2020-12 | yes | — | Must be an object. Walked by the [subset validator](#json-schema-subset). |
|
|
15
15
|
| `uiSchema` | object | no | — | Accepts `fields`, `list`, and `nav`. Violations are `SCHEMA_UI_INVALID`. |
|
|
16
16
|
| `uniqueIndexes` | `string[][]` | no | `[]` | Ordered tuples of top-level scalar fields. See [Indexes](#indexes). |
|
|
17
|
-
| `indexes` | `string[][]` | no | `[]` | Ordered non-unique tuples. Must not repeat a `uniqueIndexes` tuple. |
|
|
17
|
+
| `indexes` | `string[][]` | no | `[]` | Ordered non-unique tuples of data fields and native entry columns. Must not repeat a `uniqueIndexes` tuple. |
|
|
18
18
|
| `searchableFields` | `string[]` | no | `[]` | Top-level string fields for Admin and Staff MCP substring search. |
|
|
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`. |
|
|
@@ -22,7 +22,7 @@ A Schema declares one collection: the JSON Schema for each entry's `data`, its i
|
|
|
22
22
|
|
|
23
23
|
### Reserved entry columns
|
|
24
24
|
|
|
25
|
-
Every entry carries `id`, `status`, `version`, `createdAt`, `updatedAt` and `authorId` as native columns outside `data`. They
|
|
25
|
+
Every entry carries `id`, `status`, `version`, `createdAt`, `updatedAt` and `authorId` as native columns outside `data`. They are valid in View `fields`, `filter`, `orderBy` and `uiSchema.list`, and `indexes` may include them (`uniqueIndexes` may not). A data property may not reuse one of these names: validate fails closed with `INVALID_MANIFEST_ENVELOPE` at `/spec/schema/properties/<name>`, because SQLite-family and IndexedDB storage would otherwise resolve the name differently and an index declared today could change meaning when a same-named property is added later. Use a domain name instead (`submittedAt`, `orderStatus`, `submittedBy`); the native column is still there and still readable (ADR-0025). `locale` is a reserved data field: only a localized Schema may declare it, and the runtime requires it on writes to a localized Schema. SQL Views project the native column. Do not declare `expectedVersion` under `spec.schema.properties` — that name is the reserved Procedure OCC token; validate fails closed with `INVALID_MANIFEST_ENVELOPE` (ADR-0022). New reserved Procedure input names need an ADR.
|
|
26
26
|
|
|
27
27
|
## Example
|
|
28
28
|
|
|
@@ -191,13 +191,21 @@ Keep implementation-detail children fold-only. Use `nav.standalone: true` when s
|
|
|
191
191
|
| When any index is declared, `metadata.name` matches `/^[A-Za-z][A-Za-z0-9_.-]*$/`. | `SCHEMA_INDEX_INVALID` at `/metadata/name` |
|
|
192
192
|
| Each tuple is a non-empty array of strings. | `INVALID_MANIFEST_ENVELOPE` (shape) or `SCHEMA_INDEX_INVALID` (empty) |
|
|
193
193
|
| No field repeats within a tuple; field names match the same safe pattern. | `SCHEMA_INDEX_INVALID` |
|
|
194
|
-
|
|
|
195
|
-
|
|
|
194
|
+
| `uniqueIndexes` fields are data properties. `indexes` fields may also be the native columns `id`, `status`, `version`, `createdAt`, `updatedAt`, `authorId`, which map to their `_mantle_*` columns. | `SCHEMA_INDEX_INVALID` |
|
|
195
|
+
| Data fields are exact top-level keys of `properties`. | `UNIQUE_INDEX_FIELD_UNKNOWN` or `SCHEMA_INDEX_FIELD_UNKNOWN` |
|
|
196
196
|
| Fields are indexable scalars: exactly one non-null type, optionally nullable. `string` maps to TEXT, `integer` and `boolean` to INTEGER, `number` to REAL. | `SCHEMA_INDEX_INVALID` |
|
|
197
197
|
| No tuple repeats within a list; `indexes` does not repeat a `uniqueIndexes` tuple. | `SCHEMA_INDEX_INVALID` |
|
|
198
198
|
|
|
199
199
|
On SQLite storage each Schema is a native table and every tuple becomes an
|
|
200
200
|
index over its native field columns; queries benefit from a leftmost prefix.
|
|
201
|
+
|
|
202
|
+
A public View over a `publishing` Schema is compiled with `status = published`
|
|
203
|
+
whether or not the manifest writes it ([View surfaces](./view.md#surfaces)), so
|
|
204
|
+
its hot path always starts with an equality on `status`. Lead the index with
|
|
205
|
+
it, then the ordered field: `indexes: [[status, publishedAt]]`. A bare
|
|
206
|
+
`[[publishedAt]]` does not serve that query on production SQLite (no planner
|
|
207
|
+
statistics), which sorts every published row in a temporary B-tree instead
|
|
208
|
+
(#962). Declare the index the query needs; Mantle does not derive one (ADR-0025).
|
|
201
209
|
Unique indexes are also checked before every write; a conflicting row is
|
|
202
210
|
`CONFLICT`. After the first deployment, adding, removing, reordering, or changing
|
|
203
211
|
any `uniqueIndexes` tuple is destructive and requires rebuilding the instance
|
|
@@ -206,6 +206,8 @@ Admin applies search and filters before pagination, rejecting a search term or f
|
|
|
206
206
|
| `public` | `GET /api/views/<name>`, plus a catalog at `GET /api/views` | `query_view_<segment>` on `/mcp` | Also mounted at `GET /admin/api/views/<name>` and `/export` behind the staff gate |
|
|
207
207
|
| `staff` | `GET /admin/api/views/<name>` and `/admin/api/views/<name>/export` — not mounted publicly | `query_view_<segment>` on `/mcp/staff` | Report sidebar |
|
|
208
208
|
|
|
209
|
+
A `public` declarative View over a `publishing` Schema reads **published rows only**, on every transport. The runtime adds `status = published` to the compiled query whether or not the filter spells it out; writing it is allowed and redundant, and comparing `status` to any other value is rejected at validate time (`VIEW_PUBLIC_STATUS_INVALID`). Staff Views see every status. `operational` Schemas create rows as `published`, so nothing is added. SQL Views (`spec.sql`) are the author's own statement and receive no injected predicate. Decision record: ADR-0025.
|
|
210
|
+
|
|
209
211
|
`<segment>` is `metadata.name` lower-cased with `-` replaced by `_`. Two Views that mangle to the same segment collide with `MCP_TOOL_NAME_COLLISION`. Admin also serves the manifest listing `GET /admin/api/views-manifest`. Surface choice is visibility, not authorization: `requires` still gates every call on both transports. See [Surfaces](./surface.md) and [MCP and agents](../concepts/mcp-and-agents.md).
|
|
210
212
|
|
|
211
213
|
The MCP `inputSchema` is `params.properties` plus `page` and `show` as optional numbers, carrying `params.required` through unchanged; the tool is annotated `readOnlyHint: true`.
|
|
@@ -5,10 +5,10 @@ description: The files you own in a Mantle project, every mantle and mantle-harn
|
|
|
5
5
|
|
|
6
6
|
This page describes a directly authored Mantle project: which files are yours, what the installed CLI does to them, and the loop you run before every commit. Surfaces are optional — take only what you need. [The minimal Worker](./quickstart-worker.md) is Spec + adapter without Admin. [Local Admin](./quickstart-admin.md) is the opt-in Dev UI path when humans need a console.
|
|
7
7
|
|
|
8
|
-
Cold start from GitHub or a marketplace host is the
|
|
8
|
+
Cold start from GitHub or a marketplace host is the install skill, not this page:
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npx skills add aotter/mantle
|
|
11
|
+
npx skills add aotter/mantle --skill install
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## You own the project
|
|
@@ -98,20 +98,19 @@ Run the harness after any change to a Schema index, View filter or ordering, or
|
|
|
98
98
|
|
|
99
99
|
`mantle skills` projects the skills the installed package marks `projection: project`. At this version those are `develop`, `plugin`, `theme` and `update`; `install`, `media-gc` and `provision` stay opt-in because they create projects, delete remote objects or handle production secrets. Both tool layouts receive identical bytes. Generation never rewrites these files.
|
|
100
100
|
|
|
101
|
-
Install the
|
|
102
|
-
the
|
|
103
|
-
`package.json` (currently `0.1.3-alpha.1`):
|
|
101
|
+
Install the plugin bundle in the agent host. Cold start uses
|
|
102
|
+
the install skill; an already-installed project pins packages from `package.json`:
|
|
104
103
|
|
|
105
104
|
```sh
|
|
106
105
|
# Canonical
|
|
107
|
-
npx skills add aotter/mantle
|
|
106
|
+
npx skills add aotter/mantle --skill install
|
|
108
107
|
|
|
109
108
|
# Claude Code — two separate prompts
|
|
110
|
-
/plugin marketplace add aotter/mantle
|
|
109
|
+
/plugin marketplace add aotter/mantle
|
|
111
110
|
/plugin install mantle@mantle
|
|
112
111
|
|
|
113
112
|
# Codex
|
|
114
|
-
codex plugin marketplace add aotter/mantle
|
|
113
|
+
codex plugin marketplace add aotter/mantle
|
|
115
114
|
codex plugin add mantle@mantle
|
|
116
115
|
```
|
|
117
116
|
|
package/docs/release-process.md
CHANGED
|
@@ -15,9 +15,11 @@ No task implicitly authorizes publication; no manual package/tag writer exists.
|
|
|
15
15
|
|---|---|---|
|
|
16
16
|
| Reviewed source; unused version | Core source/packed-consumer gates, then immutable Core tag | Exact canonical merged PR SHA and version required |
|
|
17
17
|
| Tag exists; registry candidates partial | Existing npm/GPR publication steps | Verify existing artifact identity; publish missing versions only |
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
18
|
+
| Tag exists and all eleven npmjs packages already exist | Metadata verify, then channel promote and the GitHub release | Skip pack and immutable tarball compare. The controller tip must not rebuild published artifact identity. npm integrity metadata still has to be `sha512`. The public-registry Worker gate stays skipped |
|
|
19
|
+
| Tag exists on an ancestor of the dispatched tip; tip package versions still match | Resolve binds release identity to the tag SHA; later steps stay the existing writers | Controller-only recovery. Do not retag. The canonical merged-PR check uses the tag SHA. Fail when the tip version differs or the tag commit is not an ancestor |
|
|
20
|
+
| Registry candidates verified | Public-registry reference consumer gate | No mutation; failure leaves public channels and `mantle-release` unchanged |
|
|
21
|
+
| Consumer passes | That registry's promote step: monotonic channel add for every package | Same version is a no-op; older runs cannot move a channel backward. Public channels are only `alpha`, `beta`, `rc`, and `latest`. `mantle-release` is never removed |
|
|
22
|
+
| Channels promoted or preserved | GitHub release step | Existing release identity or fail. Leftover `mantle-release` pointing at the last published candidate is expected |
|
|
21
23
|
|
|
22
24
|
The public-registry gate uses a disposable copy of the directly authored
|
|
23
25
|
`docs/examples/host-minimal-worker` reference, installs the exact candidate, then
|
|
@@ -40,8 +42,24 @@ decision instead of starting another local redesign loop.
|
|
|
40
42
|
|
|
41
43
|
Invariants: immutable versions/tags retain their identity; registry integrity
|
|
42
44
|
and the published-consumer gate precede public channel promotion; retries
|
|
43
|
-
cannot move channels backward. No downstream mutation, unpublish
|
|
44
|
-
is introduced. The runnable release-order check guards
|
|
45
|
+
cannot move channels backward. No downstream mutation, unpublish, dist-tag
|
|
46
|
+
removal, or rollback is introduced. The runnable release-order check guards
|
|
47
|
+
these transitions.
|
|
48
|
+
|
|
49
|
+
Mutation boundaries during a release: `Publish to npmjs` and `Mirror to
|
|
50
|
+
GitHub Packages` attach the internal candidate dist-tag `mantle-release`
|
|
51
|
+
while publishing a version. `Promote npmjs channel tags` is the only
|
|
52
|
+
release step that moves npmjs public channels. `Promote GitHub Packages
|
|
53
|
+
channel tags` is the only release step that moves GitHub Packages public
|
|
54
|
+
channels. Neither step removes `mantle-release`. Recovery of a partial
|
|
55
|
+
release reruns that same controller and version.
|
|
56
|
+
|
|
57
|
+
`mantle-release` is not a public channel. It stays forever as an internal
|
|
58
|
+
candidate tag pointing at the last published candidate. Official channels
|
|
59
|
+
are only `alpha`, `beta`, `rc`, and `latest`. Consumers must not install
|
|
60
|
+
`mantle-release`. Deleting it after promotion is not part of the release:
|
|
61
|
+
dist-tag DELETE only added failure and re-run state, and Actions
|
|
62
|
+
`NPM_TOKEN` 403s on that DELETE.
|
|
45
63
|
|
|
46
64
|
## Branches and channels
|
|
47
65
|
|
|
@@ -53,8 +71,10 @@ is introduced. The runnable release-order check guards these transitions.
|
|
|
53
71
|
| `X.Y.Z` | `main` | `latest` | release |
|
|
54
72
|
|
|
55
73
|
- The controller derives the source branch from the version: `-alpha` means
|
|
56
|
-
`develop`, anything else means `main`.
|
|
57
|
-
branch's tip
|
|
74
|
+
`develop`, anything else means `main`. An untagged dispatch must be that
|
|
75
|
+
branch's tip and the merge commit of exactly one PR into that branch.
|
|
76
|
+
When the version tag already exists on an ancestor of the tip, Resolve
|
|
77
|
+
recovers from that tag SHA; see Recovery.
|
|
58
78
|
`scripts/release-tag-order.mjs` rejects any other prerelease identifier.
|
|
59
79
|
- `develop` is where every change integrates first, so it is the base for all
|
|
60
80
|
work despite `main` being the repository's default branch on GitHub. `main`
|
|
@@ -63,6 +83,13 @@ is introduced. The runnable release-order check guards these transitions.
|
|
|
63
83
|
approval, resolved threads and a current-base `Typecheck + tests` check.
|
|
64
84
|
- Stable is the only release that moves `latest`. A prerelease channel keeps
|
|
65
85
|
its last version when a later stable publishes.
|
|
86
|
+
- Publish uses `--tag mantle-release`. `mantle-release` is an internal
|
|
87
|
+
candidate dist-tag, not a public channel. Consumers must not install it.
|
|
88
|
+
Official channels are only `alpha`, `beta`, `rc`, and `latest`.
|
|
89
|
+
Publication does not move those channels. After the public-registry
|
|
90
|
+
consumer gate, each registry's promote step moves every package's real
|
|
91
|
+
channel and leaves `mantle-release` pointing at the last published
|
|
92
|
+
candidate.
|
|
66
93
|
|
|
67
94
|
## Prepare and run
|
|
68
95
|
|
|
@@ -82,6 +109,14 @@ is introduced. The runnable release-order check guards these transitions.
|
|
|
82
109
|
git grep -n "$OLD" -- ':!pnpm-lock.yaml' ':!**/package-lock.json'
|
|
83
110
|
```
|
|
84
111
|
|
|
112
|
+
Leave the consumer cold-start entry **untagged**: `npx skills add
|
|
113
|
+
aotter/mantle --skill install`, `/plugin marketplace add aotter/mantle`,
|
|
114
|
+
`codex plugin marketplace add aotter/mantle` and the plugin-manifest
|
|
115
|
+
descriptions carry no `@v…` / `--ref`. Untagged resolves to `main`, which
|
|
116
|
+
only moves at a release, so it is always the latest published version
|
|
117
|
+
(#995, #998). Only "installed version" statements such as
|
|
118
|
+
`(currently \`X.Y.Z\`)` are bumped.
|
|
119
|
+
|
|
85
120
|
3. Review API compatibility and migration instructions for actual consumers.
|
|
86
121
|
Frozen legacy consumers stay on their pinned version; do not make them
|
|
87
122
|
follow new Core.
|
|
@@ -176,7 +211,9 @@ registries. Existing artifacts on retry must have matching integrity.
|
|
|
176
211
|
|
|
177
212
|
Completion requires the Core tag SHA, all eleven npmjs/GPR packages, exact
|
|
178
213
|
integrity, no workspace dependencies, a passing public-registry Worker gate,
|
|
179
|
-
correct channel tags and the GitHub release.
|
|
214
|
+
correct channel tags, and the GitHub release. Leftover `mantle-release`
|
|
215
|
+
pointing at the last published candidate is expected. Retain run links and
|
|
216
|
+
gate evidence.
|
|
180
217
|
This does not prove stable production soak or upgrade safety; the version's
|
|
181
218
|
release-gate issue owns those acceptance requirements. An agent acceptance run
|
|
182
219
|
uses only the version-matched authoring instructions, not an SDK checkout or
|
|
@@ -188,6 +225,19 @@ Rerun the same controller commit/version for a transient or verified partial
|
|
|
188
225
|
transition. Existing tags/artifacts must match; newer channels stay put. Fail
|
|
189
226
|
on identity disagreement instead of guessing. A wrong public artifact needs
|
|
190
227
|
a new version; never force-retag, overwrite or reuse a published version.
|
|
228
|
+
|
|
229
|
+
After a controller-only fix lands on the tip, re-dispatch the same version
|
|
230
|
+
from the source branch. Resolve recovers using the existing tag SHA when
|
|
231
|
+
that commit is an ancestor of the tip and package versions on the tip still
|
|
232
|
+
match. The tag owns the release SHA; the tip only carries controller fixes.
|
|
233
|
+
Tagged recovery skips the Core source check because that tree was already
|
|
234
|
+
released from the immutable tag. When `tag_exists` and all eleven npmjs
|
|
235
|
+
packages already exist at that version, that same existence check skips
|
|
236
|
+
packing, immutable tarball comparison, and the public-registry Worker gate.
|
|
237
|
+
The controller tip must not rebuild an artifact whose identity is already
|
|
238
|
+
published. Verify npm metadata still runs. The Worker gate stays skipped
|
|
239
|
+
because the first run already passed it before promotion.
|
|
240
|
+
|
|
191
241
|
Unpublish is reserved for actual secret/private-file exposure, never routine
|
|
192
242
|
fixes. Infrastructure renames require their explicit config diff and live
|
|
193
243
|
smoke; CI alone cannot prove provider identity.
|
|
@@ -6,7 +6,7 @@ Runtime. This Spec-only path is allowed by
|
|
|
6
6
|
[ADR-0019](adr/0019-sealed-manifest-runtime-pipeline.md), not a new adapter,
|
|
7
7
|
manifest grammar, or fork of Core.
|
|
8
8
|
|
|
9
|
-
This recipe targets `0.1.3-alpha.
|
|
9
|
+
This recipe targets `0.1.3-alpha.3`. Pin the package, record the tested version, and
|
|
10
10
|
rerun compatibility checks when upgrading.
|
|
11
11
|
|
|
12
12
|
## What stays with the host
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aotter/mantle",
|
|
3
|
-
"version": "0.1.3-alpha.
|
|
3
|
+
"version": "0.1.3-alpha.3",
|
|
4
4
|
"description": "Embeddable Mantle Core umbrella with Spec and Runtime; Web, Admin, Auth, Bun, Vercel, Cloudflare, and Admin UI are optional peer packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://mantle.tools/",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"README.md"
|
|
84
84
|
],
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@aotter/mantle-runtime": "0.1.3-alpha.
|
|
87
|
-
"@aotter/mantle-spec": "0.1.3-alpha.
|
|
86
|
+
"@aotter/mantle-runtime": "0.1.3-alpha.3",
|
|
87
|
+
"@aotter/mantle-spec": "0.1.3-alpha.3"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
90
|
"aws4fetch": "^1.0.20",
|
|
@@ -92,13 +92,13 @@
|
|
|
92
92
|
"hono": "^4.12.0",
|
|
93
93
|
"@libsql/client": "^0.17.4",
|
|
94
94
|
"zod": "^4.5.0",
|
|
95
|
-
"@aotter/mantle-admin": "0.1.3-alpha.
|
|
96
|
-
"@aotter/mantle-
|
|
97
|
-
"@aotter/mantle-
|
|
98
|
-
"@aotter/mantle-
|
|
99
|
-
"@aotter/mantle-
|
|
100
|
-
"@aotter/mantle-
|
|
101
|
-
"@aotter/mantle-
|
|
95
|
+
"@aotter/mantle-admin": "0.1.3-alpha.3",
|
|
96
|
+
"@aotter/mantle-bun": "0.1.3-alpha.3",
|
|
97
|
+
"@aotter/mantle-admin-ui": "0.1.3-alpha.3",
|
|
98
|
+
"@aotter/mantle-cloudflare": "0.1.3-alpha.3",
|
|
99
|
+
"@aotter/mantle-auth": "0.1.3-alpha.3",
|
|
100
|
+
"@aotter/mantle-web": "0.1.3-alpha.3",
|
|
101
|
+
"@aotter/mantle-vercel": "0.1.3-alpha.3"
|
|
102
102
|
},
|
|
103
103
|
"peerDependenciesMeta": {
|
|
104
104
|
"@aotter/mantle-admin": {
|
|
@@ -144,13 +144,13 @@
|
|
|
144
144
|
"typescript": "^6.0.3",
|
|
145
145
|
"vitest": "^4.1.11",
|
|
146
146
|
"zod": "^4.5.4",
|
|
147
|
-
"@aotter/mantle-admin": "0.1.3-alpha.
|
|
148
|
-
"@aotter/mantle-
|
|
149
|
-
"@aotter/mantle-
|
|
150
|
-
"@aotter/mantle-
|
|
151
|
-
"@aotter/mantle-cloudflare": "0.1.3-alpha.
|
|
152
|
-
"@aotter/mantle-vercel": "0.1.3-alpha.
|
|
153
|
-
"@aotter/mantle-web": "0.1.3-alpha.
|
|
147
|
+
"@aotter/mantle-admin": "0.1.3-alpha.3",
|
|
148
|
+
"@aotter/mantle-auth": "0.1.3-alpha.3",
|
|
149
|
+
"@aotter/mantle-admin-ui": "0.1.3-alpha.3",
|
|
150
|
+
"@aotter/mantle-bun": "0.1.3-alpha.3",
|
|
151
|
+
"@aotter/mantle-cloudflare": "0.1.3-alpha.3",
|
|
152
|
+
"@aotter/mantle-vercel": "0.1.3-alpha.3",
|
|
153
|
+
"@aotter/mantle-web": "0.1.3-alpha.3"
|
|
154
154
|
},
|
|
155
155
|
"engines": {
|
|
156
156
|
"node": ">=22"
|
package/skills/README.md
CHANGED
|
@@ -58,26 +58,25 @@ contracts.
|
|
|
58
58
|
|
|
59
59
|
## Source-repository marketplace install
|
|
60
60
|
|
|
61
|
-
Cold start is the
|
|
62
|
-
to the same
|
|
61
|
+
Cold start is the install skill. Other marketplace hosts are pointers
|
|
62
|
+
to the same entry:
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
|
-
npx skills add aotter/mantle
|
|
65
|
+
npx skills add aotter/mantle --skill install
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
# Claude Code — two separate prompts
|
|
70
|
-
/plugin marketplace add aotter/mantle
|
|
70
|
+
/plugin marketplace add aotter/mantle
|
|
71
71
|
/plugin install mantle@mantle
|
|
72
72
|
|
|
73
73
|
# Codex
|
|
74
|
-
codex plugin marketplace add aotter/mantle
|
|
74
|
+
codex plugin marketplace add aotter/mantle
|
|
75
75
|
codex plugin add mantle@mantle
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
Then follow the install skill to the CLI and handbook.
|
|
79
|
-
`
|
|
80
|
-
pinned, `mantle skills` projects the installed package's own skills into the
|
|
78
|
+
Then follow the install skill to the CLI and handbook. After packages are
|
|
79
|
+
installed, `mantle skills` projects the installed package's own skills into the
|
|
81
80
|
project, and `mantle skills --check` fails on drift.
|
|
82
81
|
|
|
83
82
|
Cursor and GitHub Copilot read their manifests from the repository directly.
|
|
@@ -97,7 +96,7 @@ package. Two audiences, two artifacts.
|
|
|
97
96
|
|
|
98
97
|
## Discoverability
|
|
99
98
|
|
|
100
|
-
The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "develop my Mantle site", "deploy"). Official cold start is `npx skills add aotter/mantle
|
|
99
|
+
The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "develop my Mantle site", "deploy"). Official cold start is `npx skills add aotter/mantle --skill install`. Point the agent at the repository or pass the version-matched markdown content directly.
|
|
101
100
|
|
|
102
101
|
## Conventions
|
|
103
102
|
|
package/skills/develop/SKILL.md
CHANGED
|
@@ -166,7 +166,9 @@ pnpm exec mantle-harness indexes --require-public --format text
|
|
|
166
166
|
The check uses crowded real SQLite and the shipped compiler. It complements
|
|
167
167
|
`pnpm validate`; it does not replace correctness validation. Declare the
|
|
168
168
|
smallest ordered index justified by the measured path and respect SQLite's
|
|
169
|
-
leftmost-prefix rule
|
|
169
|
+
leftmost-prefix rule; which columns an index may name and how a public View's
|
|
170
|
+
status predicate shapes it is in the handbook (`reference/schema.md#indexes`).
|
|
171
|
+
Do not change user-visible filter or ordering semantics
|
|
170
172
|
just to make the gate pass. Do not add every permutation or cache every read.
|
|
171
173
|
|
|
172
174
|
For relevant Cloudflare serving changes, start the project and sample the
|