@aotter/mantle 0.1.0-alpha.1 → 0.1.0-alpha.11
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 +105 -26
- package/dist/admin.d.ts +2 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/admin.js +2 -0
- package/dist/admin.js.map +1 -0
- package/dist/bun.d.ts +2 -0
- package/dist/bun.d.ts.map +1 -0
- package/dist/bun.js +2 -0
- package/dist/bun.js.map +1 -0
- package/dist/cli/create.d.ts +2 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +246 -0
- package/dist/cli/create.js.map +1 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +101 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/harness.d.ts +3 -0
- package/dist/cli/harness.d.ts.map +1 -0
- package/dist/{harness-cli.js → cli/harness.js} +13 -8
- package/dist/cli/harness.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/{cli.js → cli/main.js} +6 -8
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/{skills.js → cli/skills.js} +51 -6
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/{update.js → cli/update.js} +72 -46
- package/dist/cli/update.js.map +1 -0
- package/dist/codegen/emitMantleModule.d.ts +16 -0
- package/dist/codegen/emitMantleModule.d.ts.map +1 -0
- package/dist/codegen/emitMantleModule.js +217 -0
- package/dist/codegen/emitMantleModule.js.map +1 -0
- package/dist/codegen.d.ts +2 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +2 -0
- package/dist/codegen.js.map +1 -0
- package/dist/provision/renderProvisionBundle.d.ts +70 -0
- package/dist/provision/renderProvisionBundle.d.ts.map +1 -0
- package/dist/provision/renderProvisionBundle.js +367 -0
- package/dist/provision/renderProvisionBundle.js.map +1 -0
- package/dist/provision.d.ts +2 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +2 -0
- package/dist/provision.js.map +1 -0
- package/dist/vercel-libsql.d.ts +2 -0
- package/dist/vercel-libsql.d.ts.map +1 -0
- package/dist/vercel-libsql.js +2 -0
- package/dist/vercel-libsql.js.map +1 -0
- package/dist/vercel.d.ts +2 -0
- package/dist/vercel.d.ts.map +1 -0
- package/dist/vercel.js +2 -0
- package/dist/vercel.js.map +1 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2 -0
- package/dist/web.js.map +1 -0
- package/docs/adapter-guide.md +94 -47
- package/docs/adr/0001-four-atom-manifest-model.md +48 -307
- package/docs/adr/0002-closed-enums-for-bindings.md +6 -15
- package/docs/adr/0007-ai-as-primary-author.md +6 -4
- package/docs/adr/0008-structured-diagnostic-shape.md +2 -2
- package/docs/adr/0009-consumer-supplied-manifests.md +15 -9
- package/docs/adr/0010-locale-and-translates.md +13 -8
- package/docs/adr/0011-adapter-port-spec.md +29 -21
- package/docs/adr/0012-views-as-public-rest.md +5 -9
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +124 -43
- package/docs/adr/0018-core-starters-repository-boundary.md +22 -1
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +204 -0
- package/docs/adr/README.md +16 -14
- package/docs/api-mcp-authorization.md +28 -42
- package/docs/assets/mantle-admin-operations.png +0 -0
- package/docs/assets/mantle-hero.jpg +0 -0
- package/docs/auth-hosting-model.md +15 -11
- package/docs/cloudflare-low-level-composition.md +30 -18
- package/docs/deferred-lifecycle-queues.md +20 -38
- package/docs/design-atoms.md +155 -401
- package/docs/labels.md +5 -3
- package/docs/media-uploads.md +9 -2
- package/docs/migration-0.1.2.md +45 -0
- package/docs/performance-harness.md +7 -6
- package/docs/release-process.md +86 -25
- package/docs/schema-indexes.md +1 -1
- package/docs/sealed-pipeline-ownership.md +100 -0
- package/package.json +84 -14
- package/skills/README.md +39 -7
- package/skills/develop/SKILL.md +31 -17
- package/skills/install/SKILL.md +26 -25
- package/skills/media-gc/SKILL.md +85 -0
- package/skills/plugin/SKILL.md +2 -1
- package/skills/provision/SKILL.md +24 -2
- package/skills/theme/SKILL.md +11 -1
- package/skills/update/SKILL.md +1 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js +0 -181
- package/dist/generate.js.map +0 -1
- package/dist/harness-cli.d.ts +0 -3
- package/dist/harness-cli.d.ts.map +0 -1
- package/dist/harness-cli.js.map +0 -1
- package/dist/skills.d.ts +0 -2
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- /package/dist/{generate.d.ts → cli/generate.d.ts} +0 -0
- /package/dist/{update.d.ts → cli/update.d.ts} +0 -0
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# ADR-0001: 4-atom YAML manifest model under `cms.mantle.aotter.net/v1`
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Accepted for the four-atom grammar and multi-document YAML;
|
|
4
|
+
the fixed `site.yaml` file contract is superseded by ADR-0019.
|
|
4
5
|
|
|
5
|
-
**Date**: 2026-04-30 (POC origin)
|
|
6
|
+
**Date**: 2026-04-30 (POC origin); last amended 2026-08-16
|
|
6
7
|
|
|
7
8
|
**Deciders**: phsu
|
|
8
9
|
|
|
9
|
-
**Related**: ADR-0002 (closed enums for bindings)
|
|
10
|
+
**Related**: ADR-0002 (closed enums for bindings),
|
|
11
|
+
[ADR-0019](0019-sealed-manifest-runtime-pipeline.md) (caller-owned source boundary)
|
|
12
|
+
|
|
13
|
+
> **2026-08-16 amendment:** the four atoms and multi-document YAML remain
|
|
14
|
+
> normative. Core no longer assigns source file names: its parser accepts
|
|
15
|
+
> caller-owned source IDs, and its CLI reads immediate `.yaml` / `.yml` files
|
|
16
|
+
> in lexicographic order. `manifests/site.yaml` remains only a starter
|
|
17
|
+
> convention. Fixed-file statements below are retained as decision history.
|
|
10
18
|
|
|
11
19
|
---
|
|
12
20
|
|
|
@@ -78,14 +86,14 @@ the `cms.mantle.aotter.net/v1` API group:
|
|
|
78
86
|
| `Schema` | `CREATE TABLE` | no | no |
|
|
79
87
|
| `View` | `CREATE VIEW` | yes (auto-mounted at `GET /api/views/<name>`; see ADR-0012) | no |
|
|
80
88
|
| `Procedure` | `CREATE FUNCTION ... LANGUAGE plpgsql` | **no** (transport-agnostic; needs a Trigger to bind) | **yes — handler ref to consumer's TS** |
|
|
81
|
-
| `Trigger` | `CREATE TRIGGER` +
|
|
89
|
+
| `Trigger` | `CREATE TRIGGER` + route/tool binding | yes (the binding atom) | no |
|
|
82
90
|
|
|
83
91
|
### Path A: Trigger does all binding
|
|
84
92
|
|
|
85
93
|
A Procedure is **not externally exposed by itself**. To make it
|
|
86
|
-
callable over HTTP / MCP /
|
|
94
|
+
callable over HTTP / MCP / lifecycle, declare a
|
|
87
95
|
`Trigger` whose `target.procedure` points at it. The same
|
|
88
|
-
Procedure can be bound by multiple Triggers (HTTP +
|
|
96
|
+
Procedure can be bound by multiple Triggers (HTTP + MCP,
|
|
89
97
|
all sharing one handler).
|
|
90
98
|
|
|
91
99
|
This is "Path A" relative to an alternative considered (and
|
|
@@ -135,9 +143,6 @@ existing atoms cannot express it.
|
|
|
135
143
|
by multi-doc YAML grouping (see § Authoring shape below).
|
|
136
144
|
- "Procedure is not directly exposed" is a teaching point; new
|
|
137
145
|
authors initially try to call a Procedure URL and get 404.
|
|
138
|
-
Mitigated by the dispatcher emitting a hint in the 404 if a
|
|
139
|
-
Procedure exists with no Trigger binding (DRAFT — not yet
|
|
140
|
-
spec'd as a code).
|
|
141
146
|
- The PG-1:1 pitch breaks down once authors look at the actual
|
|
142
147
|
storage layer (D1 today). The mapping is conceptual; the
|
|
143
148
|
runtime is SQLite + JSON.
|
|
@@ -150,12 +155,9 @@ existing atoms cannot express it.
|
|
|
150
155
|
multi-doc YAML keeping atoms separate while the file count
|
|
151
156
|
stays low; reviewers should push back when a single Procedure's
|
|
152
157
|
handler becomes a workflow engine.
|
|
153
|
-
- **Trigger.source.kind expansion pressure.** v0.1
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
atom has to accommodate radically different invocation shapes.
|
|
157
|
-
The contract holds (one binding atom, many source kinds) but
|
|
158
|
-
each new kind is a design pass.
|
|
158
|
+
- **Trigger.source.kind expansion pressure.** v0.1 ships `http`, `mcp`,
|
|
159
|
+
and `lifecycle`. Each new source kind requires a complete grammar and
|
|
160
|
+
runtime design pass.
|
|
159
161
|
- **The PG-1:1 pitch may become a constraint.** If a future
|
|
160
162
|
capability has no PG analogue, framing pressure will push to
|
|
161
163
|
shoehorn it into the table or to break the pitch. Either is
|
|
@@ -178,8 +180,8 @@ Rejected: under R7's reframing, the right axis is contract /
|
|
|
178
180
|
implementation × state / event, not "PG attachment." Schema
|
|
179
181
|
holds state; Procedure holds operations against state; folding
|
|
180
182
|
them conflates "what data exists" with "what can happen to data."
|
|
181
|
-
Also the LOC win that motivated the fold (92→58 via default
|
|
182
|
-
was recovered without folding
|
|
183
|
+
Also the LOC win that motivated the fold (92→58 via default CRUD)
|
|
184
|
+
was recovered without folding through the shipped
|
|
183
185
|
`handler.kind: builtin` shortcut.
|
|
184
186
|
|
|
185
187
|
**(d) 4 kinds with `Procedure` retaining inline
|
|
@@ -217,251 +219,12 @@ executor, and Procedure dispatcher live in this repository.
|
|
|
217
219
|
|
|
218
220
|
---
|
|
219
221
|
|
|
220
|
-
##
|
|
221
|
-
|
|
222
|
-
> Folded in 2026-05-03 from POC ADR-0005 ("v0.1 minimum essential
|
|
223
|
-
> grammar; rich grammar reserved as DRAFT"). The discipline below
|
|
224
|
-
> applies to every key inside every atom defined above.
|
|
225
|
-
|
|
226
|
-
### Context
|
|
227
|
-
|
|
228
|
-
The 4-atom design experiment (same 6 rounds + R6.5 + R7 cited
|
|
229
|
-
above) produced a rich inner grammar:
|
|
230
|
-
|
|
231
|
-
- 18 grammar keys across the 4 atoms
|
|
232
|
-
- 7 meta-rules (DRY across `requires` siblings; one-site
|
|
233
|
-
placeholder evaluation; SDK helper doctrine; closed
|
|
234
|
-
`x-mantle-bind` enum; list-shape for cross-resource refs;
|
|
235
|
-
singleton-fallback exception; ctx.system origin invariant)
|
|
236
|
-
- 2 closed enums (`x-mantle-bind`, `ctx.*` predicate identity)
|
|
237
|
-
- 2 placeholder namespaces (`:ctx.*` identity bindings, `$.*`
|
|
238
|
-
data-flow bindings)
|
|
239
|
-
- ~36 cognitive surface units (consumer-cc count)
|
|
240
|
-
|
|
241
|
-
The pressure-tested grammar covered features as ambitious as
|
|
242
|
-
`Trigger.target.project` (declarative cross-Schema aggregate
|
|
243
|
-
projection in same-tx), `Schema.spec.policies.{visible,
|
|
244
|
-
readable, writable}` (PG-RLS-style row/field policy),
|
|
245
|
-
`View.recursive` (declarative recursive CTEs),
|
|
246
|
-
`requires.window/quota/owns/contains` (temporal predicates,
|
|
247
|
-
rate caps, row ownership, array containment), and
|
|
248
|
-
`handler.kind: builtin` (default-CRUD shortcut).
|
|
249
|
-
|
|
250
|
-
Shipping all of that in v0.1 would mean roughly 5–10× the
|
|
251
|
-
dispatcher / validator / type-system surface v0.1 actually
|
|
252
|
-
needs, plus an OpenAPI emission story for keys (recursive CTEs,
|
|
253
|
-
RLS) that have no clean OpenAPI mapping.
|
|
254
|
-
|
|
255
|
-
The user principle, articulated 2026-04-30 EOD:
|
|
256
|
-
|
|
257
|
-
> 本次 iteration 只匡出四個 yml 最 minimum essential 的 grammar,
|
|
258
|
-
> 四個都蓋出來且能動,之後當應用場景擴充真的有需要再往上蓋。
|
|
259
|
-
|
|
260
|
-
(Translation: this iteration only ships the absolute minimum
|
|
261
|
-
grammar to make the 4 atoms work end-to-end; each extension
|
|
262
|
-
arrives when a real application use case demands it.)
|
|
263
|
-
|
|
264
|
-
This is YAGNI applied to grammar surface, motivated by:
|
|
265
|
-
|
|
266
|
-
- The runtime cost above.
|
|
267
|
-
- The risk that speculative grammar locks the wrong shape —
|
|
268
|
-
features designed in the abstract often look different once a
|
|
269
|
-
real use case applies pressure (R3-R5 of the design experiment
|
|
270
|
-
showed multiple grammar shapes proposed and revised as
|
|
271
|
-
features unfolded).
|
|
272
|
-
- The maintenance cost of keys nobody uses but everybody must
|
|
273
|
-
understand.
|
|
274
|
-
|
|
275
|
-
### Decision
|
|
222
|
+
## Grammar changes
|
|
276
223
|
|
|
277
|
-
The
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#### v0.1 grammar lock per atom
|
|
283
|
-
|
|
284
|
-
**Schema (v0.1)**:
|
|
285
|
-
- `spec.schema:` (JSON Schema 2020-12 body)
|
|
286
|
-
- `spec.uniqueIndexes:` (composite uniques)
|
|
287
|
-
- `spec.indexes:` (ordered composite, non-unique access paths)
|
|
288
|
-
- Property extensions: `x-mantle-bind`, `x-mantle-ref`, `x-mcp-hint`
|
|
289
|
-
|
|
290
|
-
**View (v0.1)**:
|
|
291
|
-
- `spec.from:` (source Schema)
|
|
292
|
-
- `spec.fields:` (projected fields)
|
|
293
|
-
- `spec.filter:` AST — `eq`, `gt`, `gte`, `lt`, `lte`, `and`, `or`
|
|
294
|
-
- `spec.orderBy:`
|
|
295
|
-
- `spec.limit:`
|
|
296
|
-
- `spec.params:` (required query params referenced by filter values)
|
|
297
|
-
- `spec.requires.{auth, guard}:` (same authorization contract as Procedure)
|
|
298
|
-
|
|
299
|
-
**Procedure (v0.1)**:
|
|
300
|
-
- `spec.requires.auth.all:` (closed predicate vocabulary)
|
|
301
|
-
- `spec.requires.guard.procedure:` (one consumer-owned dynamic guard)
|
|
302
|
-
- `spec.input:` (JSON Schema 2020-12)
|
|
303
|
-
- `spec.output:` (JSON Schema 2020-12)
|
|
304
|
-
- `spec.handler.{kind: ref, ref: <opaque-key>}` — author-supplied
|
|
305
|
-
handler function
|
|
306
|
-
- `spec.handler.{kind: builtin, op: <create | update | upsert |
|
|
307
|
-
delete>, schema: <Schema name>}` — SDK-supplied CRUD shortcut
|
|
308
|
-
(promoted to v0.1.0; runtime implemented by `InvokeBuiltinUseCase`)
|
|
309
|
-
|
|
310
|
-
**Trigger (v0.1)**:
|
|
311
|
-
- `spec.source.kind: http` — public HTTP endpoint
|
|
312
|
-
- `spec.source.{method, path}` (when `kind: http`)
|
|
313
|
-
- `spec.source.kind: lifecycle` — entry-writer hook (promoted to
|
|
314
|
-
v0.1.0; runtime implemented by `LifecycleHookingEntryRepository`)
|
|
315
|
-
- `spec.source.{schema, on, errorPolicy}` (when `kind: lifecycle`)
|
|
316
|
-
- `spec.source.kind: mcp` plus `surface: public | staff` — MCP tool
|
|
317
|
-
exposure for a declared Procedure
|
|
318
|
-
- `spec.target.procedure:`
|
|
319
|
-
|
|
320
|
-
#### v0.1 closed enums
|
|
321
|
-
|
|
322
|
-
- `x-mantle-bind: {ctx.user, ctx.staff, now}`
|
|
323
|
-
- `ctx.*` predicate vocabulary: `{user, staff, auth, auth.scope}` (no
|
|
324
|
-
`system` until a use case forces it)
|
|
325
|
-
- `Trigger.source.kind: {http, lifecycle, mcp}`
|
|
326
|
-
- `Procedure.handler.kind: {ref, builtin}`
|
|
327
|
-
- `BuiltinOp: {create, update, upsert, delete}`
|
|
328
|
-
- `LifecycleHook: {before_create, after_create, before_update,
|
|
329
|
-
after_update, before_delete, after_delete, before_publish,
|
|
330
|
-
after_publish}`
|
|
331
|
-
|
|
332
|
-
#### What's DRAFT (do not implement, do not type)
|
|
333
|
-
|
|
334
|
-
- Schema: `policies.{visible, readable, writable, owner}`,
|
|
335
|
-
`x-mantle-ref` auto-lift to virtual column,
|
|
336
|
-
computed columns via projection Trigger
|
|
337
|
-
- View: `recursive`, `gatedBy`, `join`, `policies.skip`,
|
|
338
|
-
filter AST extensions (`contains`, `not`, `in`, `like`)
|
|
339
|
-
- Procedure: `requires.auth.{any | all}` with disjunction;
|
|
340
|
-
`owns:`, `contains:`, `withinMinutes:`, `requires.window`,
|
|
341
|
-
`requires.quota`, `errors`, `retry`
|
|
342
|
-
- Trigger: `source.kind: {cron, queue}`,
|
|
343
|
-
`target.project`, `atomicity`
|
|
344
|
-
(`source.kind: mcp` was promoted in alpha.16 — #281)
|
|
345
|
-
- Cross-cutting: `ctx.system`, `$.*` placeholder namespace,
|
|
346
|
-
`staffBypass:`
|
|
347
|
-
|
|
348
|
-
#### Discipline
|
|
349
|
-
|
|
350
|
-
- The v0.1 validator **rejects DRAFT keys at parse time** with a
|
|
351
|
-
`DRAFT_KEY_USED` warning (not error — see the
|
|
352
|
-
AI-as-primary-author contract on permissive bias). The
|
|
353
|
-
diagnostic explicitly references the future-grammar reference
|
|
354
|
-
so authors know the key is reserved, not
|
|
355
|
-
unsupported-and-forgotten.
|
|
356
|
-
- Each DRAFT key has a documented **landing condition** (the
|
|
357
|
-
use case that surfaces it). Promoting a key to v0.1+ requires
|
|
358
|
-
showing that condition has been met — not just "it would be
|
|
359
|
-
nice to have."
|
|
360
|
-
- Each promotion goes through a 3-agent design review
|
|
361
|
-
(yml-editor proposes / code-impler tests buildability /
|
|
362
|
-
fresh-dev verifies clarity) before locking.
|
|
363
|
-
- The v0.1 floor is the floor, not the ceiling. The atom set
|
|
364
|
-
(4) is locked; the grammar inside each atom is permitted to
|
|
365
|
-
grow.
|
|
366
|
-
|
|
367
|
-
### Consequences
|
|
368
|
-
|
|
369
|
-
#### Pros
|
|
370
|
-
|
|
371
|
-
- v0.1 dispatcher / validator / types are tractable in a small
|
|
372
|
-
number of weeks, not many months.
|
|
373
|
-
- Author cognitive surface is small: 4 atoms × ~5 keys each ≈
|
|
374
|
-
20 things to learn, instead of 18 keys with ~36 cognitive
|
|
375
|
-
surface units.
|
|
376
|
-
- DRAFT keys document where the spec is **going**, so authors
|
|
377
|
-
with future requirements can tell whether the project is on
|
|
378
|
-
trajectory or not — without those features being prematurely
|
|
379
|
-
committed.
|
|
380
|
-
- Each DRAFT promotion gets a real use case attached, so the
|
|
381
|
-
grammar evolves under empirical pressure rather than
|
|
382
|
-
speculation. This was the design experiment's strongest
|
|
383
|
-
signal: features like `View.recursive` and
|
|
384
|
-
`Trigger.target.project` looked very different once a real
|
|
385
|
-
feature (threaded comments, cross-Schema invariants) applied
|
|
386
|
-
pressure.
|
|
387
|
-
|
|
388
|
-
#### Costs
|
|
389
|
-
|
|
390
|
-
- Authors with ambitions beyond v0.1 (private posts, role-gated
|
|
391
|
-
reads, rate limits, recursive views) must wait or write
|
|
392
|
-
handler-side TS. For some projects this is enough to shop
|
|
393
|
-
elsewhere; that's acceptable for an OSS v0.1.
|
|
394
|
-
- DRAFT documentation is itself a maintenance burden — the
|
|
395
|
-
reference has to stay coherent as the spec evolves; stale
|
|
396
|
-
DRAFT entries (features no longer planned) need pruning.
|
|
397
|
-
- The "warning, not error, on DRAFT keys" rule means an author
|
|
398
|
-
who copies a DRAFT example into a manifest doesn't fail —
|
|
399
|
-
they get a warning. This requires the validator to know the
|
|
400
|
-
full DRAFT vocabulary, not just the v0.1 vocabulary.
|
|
401
|
-
|
|
402
|
-
#### Risks
|
|
403
|
-
|
|
404
|
-
- **Grammar surface grows ad-hoc.** Each DRAFT promotion looks
|
|
405
|
-
reasonable; together they reproduce the over-grammar v0.1
|
|
406
|
-
was avoiding. Mitigation: 3-agent review per promotion;
|
|
407
|
-
promotions land in batches with documented use cases, not
|
|
408
|
-
one-by-one.
|
|
409
|
-
- **DRAFT becomes vaporware.** Some DRAFT features are listed
|
|
410
|
-
but never land because their use case never surfaces.
|
|
411
|
-
Mitigation: this is fine — the appendix is documenting
|
|
412
|
-
shapes, not commitments. If a DRAFT entry stays cold for >12
|
|
413
|
-
months, prune it from the appendix and let the future
|
|
414
|
-
proposer re-derive it.
|
|
415
|
-
- **Authors hit walls and route around the SDK.** "I need
|
|
416
|
-
`View.recursive` so I'll just write raw SQL in a Procedure
|
|
417
|
-
handler" — over time this drifts the actual extension shape
|
|
418
|
-
away from what the SDK eventually builds. Mitigation: when
|
|
419
|
-
ad-hoc handler patterns repeat, that's the use case that
|
|
420
|
-
promotes the DRAFT key. Watch handler implementations for
|
|
421
|
-
recurring patterns.
|
|
422
|
-
|
|
423
|
-
### Alternatives considered
|
|
424
|
-
|
|
425
|
-
**(a) Ship the full experiment grammar in v0.1**.
|
|
426
|
-
Rejected: 5–10× the dispatcher work; locks shapes in the
|
|
427
|
-
abstract before real use cases apply pressure; author surface
|
|
428
|
-
explodes.
|
|
429
|
-
|
|
430
|
-
**(b) Don't document DRAFT at all; ship v0.1 spec only**.
|
|
431
|
-
Rejected: authors with future requirements have no way to
|
|
432
|
-
evaluate trajectory; the design experiment's results would be
|
|
433
|
-
lost institutionally; future proposals would re-litigate solved
|
|
434
|
-
questions. The DRAFT reference is a forward-looking invariant.
|
|
435
|
-
|
|
436
|
-
**(c) Ship a subset of DRAFT in v0.1 (e.g. `policies.visible`
|
|
437
|
-
and `requires.auth.any`)**.
|
|
438
|
-
Rejected: any line we draw between v0.1 and DRAFT has the same
|
|
439
|
-
litigation problem. The principled cut is "minimum essential to
|
|
440
|
-
make 4 atoms work end-to-end"; anything richer needs a specific
|
|
441
|
-
motivation. None of the DRAFT features had that motivation in
|
|
442
|
-
the v0.1 ship list.
|
|
443
|
-
|
|
444
|
-
**(d) Promote DRAFT keys lazily — implement at runtime when
|
|
445
|
-
authors ask, no spec lock**.
|
|
446
|
-
Rejected: spec coherence demands keys are spec'd before they
|
|
447
|
-
ship. Authors writing against runtime-only features get
|
|
448
|
-
silently broken on SDK upgrades.
|
|
449
|
-
|
|
450
|
-
### How to apply
|
|
451
|
-
|
|
452
|
-
- Authoring v0.1 manifests: stick to the v0.1 vocabulary above.
|
|
453
|
-
If something seems missing, check the DRAFT reference; if it's
|
|
454
|
-
there and you have a concrete use case, file an issue
|
|
455
|
-
describing the use case.
|
|
456
|
-
- SDK code: implement only v0.1 keys in the dispatcher /
|
|
457
|
-
validator / types. DRAFT keys are documented but not handled.
|
|
458
|
-
- DRAFT promotion: needs a documented use case + 3-agent design
|
|
459
|
-
review + ADR landing the promotion (with cross-link to the
|
|
460
|
-
use case).
|
|
461
|
-
- Pruning DRAFT: if an entry has stayed cold for 12+ months,
|
|
462
|
-
propose removal in a small ADR. Removal is reversible (the
|
|
463
|
-
shape is in the experiment transcripts and prior ADR
|
|
464
|
-
history).
|
|
224
|
+
The parser accepts only grammar implemented by the current SDK. Unknown keys and
|
|
225
|
+
closed-enum values fail validation; Mantle does not reserve speculative syntax.
|
|
226
|
+
Add new grammar only alongside a concrete use case, runtime behavior, validation,
|
|
227
|
+
and an ADR update. Earlier experiments remain available in git history.
|
|
465
228
|
|
|
466
229
|
---
|
|
467
230
|
|
|
@@ -482,7 +245,7 @@ bundles multiple atoms. A contact form is at minimum:
|
|
|
482
245
|
inserts)
|
|
483
246
|
- A `Trigger` binding the Procedure to `POST /api/contact`
|
|
484
247
|
|
|
485
|
-
Three atoms, three
|
|
248
|
+
Three atoms, three documents. With one file per atom, a
|
|
486
249
|
small site with 10 features has 30+ manifest files. An early
|
|
487
250
|
design-experiment vibe-user called this out:
|
|
488
251
|
|
|
@@ -496,10 +259,10 @@ Two paths surfaced to address the file count:
|
|
|
496
259
|
#### Path A — Multi-doc YAML
|
|
497
260
|
|
|
498
261
|
YAML's standard `---` separator allows multiple documents in
|
|
499
|
-
one file.
|
|
262
|
+
one file. The fixed site manifest carries all atoms:
|
|
500
263
|
|
|
501
264
|
```yaml
|
|
502
|
-
# manifests/
|
|
265
|
+
# manifests/site.yaml
|
|
503
266
|
apiVersion: cms.mantle.aotter.net/v1
|
|
504
267
|
kind: Schema
|
|
505
268
|
metadata: { name: contact-messages }
|
|
@@ -525,7 +288,7 @@ spec:
|
|
|
525
288
|
```
|
|
526
289
|
|
|
527
290
|
One file, three atoms, conceptual separation preserved. The
|
|
528
|
-
loader
|
|
291
|
+
loader reads `site.yaml` and parses each `---` block as a separate
|
|
529
292
|
manifest.
|
|
530
293
|
|
|
531
294
|
#### Path B — Inline shortcut on Procedure (`expose:`)
|
|
@@ -560,7 +323,7 @@ Path B created **two ways to bind a Procedure to HTTP**:
|
|
|
560
323
|
|
|
561
324
|
- For a Procedure with one HTTP transport: inline `expose:` on
|
|
562
325
|
Procedure
|
|
563
|
-
- For a Procedure with multiple transports (HTTP +
|
|
326
|
+
- For a Procedure with multiple transports (HTTP + MCP):
|
|
564
327
|
separate `Trigger` per transport
|
|
565
328
|
|
|
566
329
|
This is the exact "two ways to do the same thing" failure mode.
|
|
@@ -582,31 +345,20 @@ multi") but creates permanent tax:
|
|
|
582
345
|
|
|
583
346
|
#### What multi-doc YAML accomplishes
|
|
584
347
|
|
|
585
|
-
- File count drops without inventing redundancy. A
|
|
586
|
-
|
|
348
|
+
- File count drops without inventing redundancy. A site has one
|
|
349
|
+
authored manifest file regardless of how many atoms it requires.
|
|
587
350
|
- Atom separation stays honest. Every atom has its own
|
|
588
351
|
envelope (`apiVersion`, `kind`, `metadata`, `spec`); there
|
|
589
352
|
is no "shortcut form."
|
|
590
|
-
- Related atoms sit next to each other for readers reviewing
|
|
591
|
-
feature.
|
|
353
|
+
- Related atoms sit next to each other for readers reviewing the site.
|
|
592
354
|
- The PG-1:1 framing stays clean: Procedure is
|
|
593
355
|
internal-callable, Trigger is the binding atom.
|
|
594
356
|
|
|
595
|
-
####
|
|
596
|
-
|
|
597
|
-
- One feature → one file. Name the file by feature
|
|
598
|
-
(`contact.yaml`, `posts.yaml`, `comments.yaml`), not by atom
|
|
599
|
-
type.
|
|
600
|
-
- The shared file holds the Procedure + its Trigger + (when
|
|
601
|
-
feature-specific) the Schema and View.
|
|
602
|
-
- Schemas that are referenced by many features can live in
|
|
603
|
-
their own `<schema-name>.schema.yaml` file.
|
|
604
|
-
- Views with no Procedure / Trigger neighbors live in
|
|
605
|
-
`<view-name>.view.yaml`.
|
|
357
|
+
#### Fixed file contract
|
|
606
358
|
|
|
607
|
-
The
|
|
608
|
-
|
|
609
|
-
|
|
359
|
+
The authored file is always `manifests/site.yaml`. The common loader rejects
|
|
360
|
+
a missing or differently named file, and every `---` document becomes one
|
|
361
|
+
atom. This is enforced so humans and agents never need a file-discovery rule.
|
|
610
362
|
|
|
611
363
|
### Consequences
|
|
612
364
|
|
|
@@ -616,8 +368,7 @@ block as a manifest.
|
|
|
616
368
|
OpenAPI emitter, runtime dispatcher all walk one shape.
|
|
617
369
|
- Conceptual atom separation preserved. The 4-atom story holds
|
|
618
370
|
for every feature.
|
|
619
|
-
- File count
|
|
620
|
-
files, not 30+.
|
|
371
|
+
- File count is fixed at one authored manifest.
|
|
621
372
|
- Cardinality changes (1 HTTP → HTTP + cron) are additive (add
|
|
622
373
|
a Trigger); no refactor needed.
|
|
623
374
|
- AI authors learn one mental model, not two.
|
|
@@ -630,9 +381,8 @@ block as a manifest.
|
|
|
630
381
|
- Some loaders (older / non-compliant) don't handle multi-doc.
|
|
631
382
|
Standard `js-yaml` / `yaml` libraries do; the SDK uses
|
|
632
383
|
`yaml` (already a dependency).
|
|
633
|
-
-
|
|
634
|
-
|
|
635
|
-
must `grep` instead.
|
|
384
|
+
- Authors use `grep` by atom name inside `manifests/site.yaml` instead of
|
|
385
|
+
discovering feature-specific filenames.
|
|
636
386
|
- Editor support: YAML language servers handle multi-doc, but
|
|
637
387
|
some IDE features (e.g. JSON Schema validation per document
|
|
638
388
|
in a multi-doc file) work less smoothly than in single-doc
|
|
@@ -647,18 +397,9 @@ block as a manifest.
|
|
|
647
397
|
ways is permanent tax that grows with the codebase.
|
|
648
398
|
Mitigation: this section is the answer to that argument; flag
|
|
649
399
|
attempts and link here.
|
|
650
|
-
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
Mitigation: file-organization convention is suggestion, not
|
|
654
|
-
rule; authors are free to split when it stops helping.
|
|
655
|
-
- **Co-location breaks discoverability for shared atoms.**
|
|
656
|
-
A Schema referenced by 4 features can live in any of those
|
|
657
|
-
4 files (or its own). Without grep / IDE go-to-def, finding
|
|
658
|
-
the Schema definition requires knowing where the author
|
|
659
|
-
put it. Mitigation: Loop 1 (`mantle validate`) emits the
|
|
660
|
-
filesystem path in `VIEW_FROM_UNKNOWN_SCHEMA` diagnostics;
|
|
661
|
-
IDE-shaped tooling (LSP — DRAFT) closes the rest.
|
|
400
|
+
- **`site.yaml` can grow.** Search by `kind` plus `metadata.name`; reconsider
|
|
401
|
+
splitting only when real projects show that one deterministic location is
|
|
402
|
+
worse than file discovery.
|
|
662
403
|
|
|
663
404
|
### Alternatives considered
|
|
664
405
|
|
|
@@ -685,14 +426,14 @@ explicit `Trigger` + explicit `expose:`).
|
|
|
685
426
|
|
|
686
427
|
### How to apply
|
|
687
428
|
|
|
688
|
-
- Authoring:
|
|
429
|
+
- Authoring: put every atom in `manifests/site.yaml`, separated by `---`.
|
|
689
430
|
- Reviewing: when an author writes a Procedure with an inline
|
|
690
431
|
HTTP binding, push back with this section.
|
|
691
432
|
- Refactoring: a feature that grew from one HTTP binding to
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
- Tooling: SDK loader
|
|
695
|
-
|
|
433
|
+
HTTP + MCP doesn't change the Procedure; it adds one Trigger
|
|
434
|
+
in the same file.
|
|
435
|
+
- Tooling: SDK loader reads `<manifest-root>/site.yaml` and parses each `---`
|
|
436
|
+
block; static validator emits diagnostics
|
|
696
437
|
with file path + manifest pointer
|
|
697
|
-
(`
|
|
438
|
+
(`site.yaml#/2/spec/from`) so multi-doc location stays
|
|
698
439
|
precise.
|
|
@@ -96,26 +96,19 @@ The vocabulary is exactly:
|
|
|
96
96
|
|
|
97
97
|
- `ctx.user` — caller is signed in as an end-user
|
|
98
98
|
- `ctx.staff: [<role>, ...]` — caller is signed in as staff in one of these roles
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`contains:`, quota predicates) is DRAFT — see
|
|
102
|
-
[ADR-0001](0001-four-atom-manifest-model.md) § Future grammar
|
|
103
|
-
discipline.
|
|
99
|
+
- `ctx.auth` — caller supplied an adapter-verified credential
|
|
100
|
+
- `ctx.auth.scope: <scope>` — credential carries the exact scope
|
|
104
101
|
|
|
105
102
|
### Adding a new entry is an explicit grammar-revise round
|
|
106
103
|
|
|
107
|
-
New `x-mantle-bind` values or `ctx.*` predicates do not get added
|
|
108
|
-
ad-hoc. They go through the discipline gate documented in
|
|
109
|
-
[ADR-0001](0001-four-atom-manifest-model.md) § Future grammar
|
|
110
|
-
discipline:
|
|
104
|
+
New `x-mantle-bind` values or `ctx.*` predicates do not get added ad hoc:
|
|
111
105
|
|
|
112
106
|
1. A documented use case showing the existing closed set cannot
|
|
113
107
|
express the requirement.
|
|
114
108
|
2. A design pass on what the new value's runtime semantics are
|
|
115
109
|
(where does it come from? when is it null? what happens at
|
|
116
110
|
the storage layer?).
|
|
117
|
-
3. A spec doc revision
|
|
118
|
-
classification.
|
|
111
|
+
3. A spec doc revision.
|
|
119
112
|
4. Code that updates the validator to accept the new value.
|
|
120
113
|
|
|
121
114
|
This treats the closed set as load-bearing infrastructure, not
|
|
@@ -208,10 +201,8 @@ attributes.
|
|
|
208
201
|
- New manifest using a value not in the enum: parse error,
|
|
209
202
|
exact diagnostic shape with `candidates` populated. AI authors
|
|
210
203
|
fix in one turn.
|
|
211
|
-
- New use case wanting an entry not in the enum:
|
|
212
|
-
|
|
213
|
-
discipline. Document the use case in the spec PR; do not
|
|
214
|
-
fast-track.
|
|
204
|
+
- New use case wanting an entry not in the enum: document the concrete
|
|
205
|
+
semantics and add validation, runtime behavior, and docs together.
|
|
215
206
|
- Lookups (e.g. "stamp the team_id"): handler-side TS, not
|
|
216
207
|
binding metadata. The Procedure handler has the lookup
|
|
217
208
|
context anyway.
|
|
@@ -100,6 +100,8 @@ What it catches:
|
|
|
100
100
|
`View.filter` refer only to declared properties of that Schema
|
|
101
101
|
- `Schema.uniqueIndexes` and `Schema.indexes` contain valid ordered
|
|
102
102
|
tuples of scalar properties declared in `spec.schema.properties`
|
|
103
|
+
- `Schema.searchableFields` contains unique top-level string properties
|
|
104
|
+
- `Schema.uiSchema.list.filterField` names an indexed operational string enum
|
|
103
105
|
- `x-mantle-bind: <value>` is in the closed enum
|
|
104
106
|
- `requires.auth.all` predicates are in the v0.1 vocabulary
|
|
105
107
|
- `Trigger.source.path` does not collide with another Trigger
|
|
@@ -250,9 +252,9 @@ Concrete artifacts today:
|
|
|
250
252
|
- Version-matched Core skills ship inside `@aotter/mantle`, through the Mantle
|
|
251
253
|
agent plugin, and through exact-byte repo-local projections written by
|
|
252
254
|
`mantle skills`.
|
|
253
|
-
- The umbrella `mantle` CLI exposes `
|
|
254
|
-
`
|
|
255
|
-
|
|
255
|
+
- The umbrella `mantle` CLI exposes `create`, `generate`, `validate`,
|
|
256
|
+
`emit-openapi`, `update`, and `skills`; direct spec installs expose advanced
|
|
257
|
+
`introspect` and `emit-types` primitives through `mantle-spec`.
|
|
256
258
|
- `@aotter/mantle/runtime/testing` and `mantle-harness` expose the measured
|
|
257
259
|
SQLite/index and live-HTTP checks described in Loop 2.
|
|
258
260
|
|
|
@@ -264,7 +266,7 @@ Concrete artifacts today:
|
|
|
264
266
|
admin: write a post, update a draft, request publish, list
|
|
265
267
|
submissions.
|
|
266
268
|
- Future ops-role verbs that don't exist yet but have an obvious
|
|
267
|
-
operator use case ("translate this draft", "
|
|
269
|
+
operator use case ("translate this draft", "publish this entry").
|
|
268
270
|
When added, they land as **new SDK builtin tools** — declared in
|
|
269
271
|
the MCP catalog source under the closed Day-1 set, not
|
|
270
272
|
auto-generated from consumer Procedures.
|
|
@@ -126,7 +126,7 @@ code string. That was retired because:
|
|
|
126
126
|
### `path` format
|
|
127
127
|
|
|
128
128
|
- For static validation: filesystem path + JSON Pointer fragment,
|
|
129
|
-
e.g. `manifests/
|
|
129
|
+
e.g. `manifests/site.yaml#/3/spec/from`.
|
|
130
130
|
- For a consumer test diagnostic: test file path + assertion location when
|
|
131
131
|
available, e.g. `tests/handlers/contact.test.ts:42`.
|
|
132
132
|
- For boot-time: manifest pointer (no on-disk path because boot
|
|
@@ -250,7 +250,7 @@ Diagnostic candidate (alternative (d) below) applies to zod —
|
|
|
250
250
|
and AI authors see the same structure.
|
|
251
251
|
- Single source of truth. The interface, code constants, and
|
|
252
252
|
formatter all live in `@aotter/mantle-spec`; the runtime
|
|
253
|
-
package and adapters import them. A future
|
|
253
|
+
package and adapters import them. A future adapter
|
|
254
254
|
inherits the shape for free.
|
|
255
255
|
|
|
256
256
|
### Costs
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# ADR-0009: Consumer-supplied manifests at SDK boot
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
4
|
-
consumer boundary.
|
|
3
|
+
**Status:** Superseded by ADR-0019.
|
|
5
4
|
|
|
6
|
-
**Date:** 2026-05-01 (POC);
|
|
5
|
+
**Date:** 2026-05-01 (POC); superseded 2026-08-16
|
|
7
6
|
|
|
8
7
|
**Related:** [ADR-0001](0001-four-atom-manifest-model.md),
|
|
9
8
|
[ADR-0007](0007-ai-as-primary-author.md),
|
|
10
|
-
[ADR-0018](0018-core-starters-repository-boundary.md)
|
|
9
|
+
[ADR-0018](0018-core-starters-repository-boundary.md),
|
|
10
|
+
[ADR-0019](0019-sealed-manifest-runtime-pipeline.md)
|
|
11
|
+
|
|
12
|
+
> **Supersession note:** consumer ownership remains durable, but the exact
|
|
13
|
+
> `site.yaml`, generated `site.ts`, and parser-free array workflow below is
|
|
14
|
+
> historical. The sealed source → parse → link → compile pipeline in ADR-0019
|
|
15
|
+
> is current; source names and generated code are optional caller concerns.
|
|
11
16
|
|
|
12
17
|
## Context
|
|
13
18
|
|
|
@@ -22,9 +27,10 @@ YAML machinery into the runtime bundle and coupled consumers to Wrangler
|
|
|
22
27
|
|
|
23
28
|
## Decision
|
|
24
29
|
|
|
25
|
-
Consumers own
|
|
26
|
-
validates
|
|
27
|
-
types
|
|
30
|
+
Consumers own exactly `manifests/site.yaml`. The installed `mantle` CLI parses
|
|
31
|
+
and validates its multi-document YAML, then writes the machine-owned runtime
|
|
32
|
+
module and handler types. A missing or differently named file fails before
|
|
33
|
+
generation:
|
|
28
34
|
|
|
29
35
|
```bash
|
|
30
36
|
pnpm exec mantle generate
|
|
@@ -72,7 +78,7 @@ runtime boot validation covers cross-manifest and registered-handler facts.
|
|
|
72
78
|
|
|
73
79
|
The external [`aotter/mantle-starters`](https://github.com/aotter/mantle-starters)
|
|
74
80
|
repository owns the blank project and typed overlays. Materialized projects
|
|
75
|
-
carry their own `manifests
|
|
81
|
+
carry their own `manifests/site.yaml`, generated module, handlers, and package scripts;
|
|
76
82
|
they consume the exact packed Core artifact rather than a workspace link.
|
|
77
83
|
|
|
78
84
|
## Consequences
|
|
@@ -128,7 +134,7 @@ runtime.
|
|
|
128
134
|
|
|
129
135
|
When authoring or reviewing a generated project:
|
|
130
136
|
|
|
131
|
-
1. Edit
|
|
137
|
+
1. Edit only `manifests/site.yaml`; do not create feature-named manifest files.
|
|
132
138
|
2. Run the installed `pnpm exec mantle generate`.
|
|
133
139
|
3. Import `manifest` from `.mantle/generated/site.js` into the conventional
|
|
134
140
|
Worker, or pass it as `CmsConfig.manifests` in low-level composition.
|