@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.
Files changed (111) hide show
  1. package/README.md +105 -26
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/admin.js +2 -0
  5. package/dist/admin.js.map +1 -0
  6. package/dist/bun.d.ts +2 -0
  7. package/dist/bun.d.ts.map +1 -0
  8. package/dist/bun.js +2 -0
  9. package/dist/bun.js.map +1 -0
  10. package/dist/cli/create.d.ts +2 -0
  11. package/dist/cli/create.d.ts.map +1 -0
  12. package/dist/cli/create.js +246 -0
  13. package/dist/cli/create.js.map +1 -0
  14. package/dist/cli/generate.d.ts.map +1 -0
  15. package/dist/cli/generate.js +101 -0
  16. package/dist/cli/generate.js.map +1 -0
  17. package/dist/cli/harness.d.ts +3 -0
  18. package/dist/cli/harness.d.ts.map +1 -0
  19. package/dist/{harness-cli.js → cli/harness.js} +13 -8
  20. package/dist/cli/harness.js.map +1 -0
  21. package/dist/cli/main.d.ts +3 -0
  22. package/dist/cli/main.d.ts.map +1 -0
  23. package/dist/{cli.js → cli/main.js} +6 -8
  24. package/dist/cli/main.js.map +1 -0
  25. package/dist/cli/skills.d.ts +3 -0
  26. package/dist/cli/skills.d.ts.map +1 -0
  27. package/dist/{skills.js → cli/skills.js} +51 -6
  28. package/dist/cli/skills.js.map +1 -0
  29. package/dist/cli/update.d.ts.map +1 -0
  30. package/dist/{update.js → cli/update.js} +72 -46
  31. package/dist/cli/update.js.map +1 -0
  32. package/dist/codegen/emitMantleModule.d.ts +16 -0
  33. package/dist/codegen/emitMantleModule.d.ts.map +1 -0
  34. package/dist/codegen/emitMantleModule.js +217 -0
  35. package/dist/codegen/emitMantleModule.js.map +1 -0
  36. package/dist/codegen.d.ts +2 -0
  37. package/dist/codegen.d.ts.map +1 -0
  38. package/dist/codegen.js +2 -0
  39. package/dist/codegen.js.map +1 -0
  40. package/dist/provision/renderProvisionBundle.d.ts +70 -0
  41. package/dist/provision/renderProvisionBundle.d.ts.map +1 -0
  42. package/dist/provision/renderProvisionBundle.js +367 -0
  43. package/dist/provision/renderProvisionBundle.js.map +1 -0
  44. package/dist/provision.d.ts +2 -0
  45. package/dist/provision.d.ts.map +1 -0
  46. package/dist/provision.js +2 -0
  47. package/dist/provision.js.map +1 -0
  48. package/dist/vercel-libsql.d.ts +2 -0
  49. package/dist/vercel-libsql.d.ts.map +1 -0
  50. package/dist/vercel-libsql.js +2 -0
  51. package/dist/vercel-libsql.js.map +1 -0
  52. package/dist/vercel.d.ts +2 -0
  53. package/dist/vercel.d.ts.map +1 -0
  54. package/dist/vercel.js +2 -0
  55. package/dist/vercel.js.map +1 -0
  56. package/dist/web.d.ts +2 -0
  57. package/dist/web.d.ts.map +1 -0
  58. package/dist/web.js +2 -0
  59. package/dist/web.js.map +1 -0
  60. package/docs/adapter-guide.md +94 -47
  61. package/docs/adr/0001-four-atom-manifest-model.md +48 -307
  62. package/docs/adr/0002-closed-enums-for-bindings.md +6 -15
  63. package/docs/adr/0007-ai-as-primary-author.md +6 -4
  64. package/docs/adr/0008-structured-diagnostic-shape.md +2 -2
  65. package/docs/adr/0009-consumer-supplied-manifests.md +15 -9
  66. package/docs/adr/0010-locale-and-translates.md +13 -8
  67. package/docs/adr/0011-adapter-port-spec.md +29 -21
  68. package/docs/adr/0012-views-as-public-rest.md +5 -9
  69. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +124 -43
  70. package/docs/adr/0018-core-starters-repository-boundary.md +22 -1
  71. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +204 -0
  72. package/docs/adr/README.md +16 -14
  73. package/docs/api-mcp-authorization.md +28 -42
  74. package/docs/assets/mantle-admin-operations.png +0 -0
  75. package/docs/assets/mantle-hero.jpg +0 -0
  76. package/docs/auth-hosting-model.md +15 -11
  77. package/docs/cloudflare-low-level-composition.md +30 -18
  78. package/docs/deferred-lifecycle-queues.md +20 -38
  79. package/docs/design-atoms.md +155 -401
  80. package/docs/labels.md +5 -3
  81. package/docs/media-uploads.md +9 -2
  82. package/docs/migration-0.1.2.md +45 -0
  83. package/docs/performance-harness.md +7 -6
  84. package/docs/release-process.md +86 -25
  85. package/docs/schema-indexes.md +1 -1
  86. package/docs/sealed-pipeline-ownership.md +100 -0
  87. package/package.json +84 -14
  88. package/skills/README.md +39 -7
  89. package/skills/develop/SKILL.md +31 -17
  90. package/skills/install/SKILL.md +26 -25
  91. package/skills/media-gc/SKILL.md +85 -0
  92. package/skills/plugin/SKILL.md +2 -1
  93. package/skills/provision/SKILL.md +24 -2
  94. package/skills/theme/SKILL.md +11 -1
  95. package/skills/update/SKILL.md +1 -0
  96. package/dist/cli.d.ts +0 -3
  97. package/dist/cli.d.ts.map +0 -1
  98. package/dist/cli.js.map +0 -1
  99. package/dist/generate.d.ts.map +0 -1
  100. package/dist/generate.js +0 -181
  101. package/dist/generate.js.map +0 -1
  102. package/dist/harness-cli.d.ts +0 -3
  103. package/dist/harness-cli.d.ts.map +0 -1
  104. package/dist/harness-cli.js.map +0 -1
  105. package/dist/skills.d.ts +0 -2
  106. package/dist/skills.d.ts.map +0 -1
  107. package/dist/skills.js.map +0 -1
  108. package/dist/update.d.ts.map +0 -1
  109. package/dist/update.js.map +0 -1
  110. /package/dist/{generate.d.ts → cli/generate.d.ts} +0 -0
  111. /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:** Carried over from POC v0.0.x; refreshed and folded for v0.1.0 (incorporates POC ADRs 0005 + 0006).
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), refreshed 2026-05-03 for v0.1.0 rebuild
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` + `pg_cron` + PostgREST route + `LISTEN/NOTIFY` | yes (the binding atom) | no |
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 / cron / lifecycle / queue, declare a
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 + cron + MCP,
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 only ships
154
- `http`. As MCP / cron / lifecycle / queue source kinds land
155
- (DRAFT — see § Future grammar discipline below), the same
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-CRUD)
182
- was recovered without folding — captured in the DRAFT
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
- ## Future grammar discipline (was POC ADR-0005)
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 v0.1 ship targets **minimum essential grammar only**. Rich
278
- grammar from the design experiment is preserved as DRAFT in the
279
- manifest grammar reference; it lands when a concrete use case
280
- forces it.
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 manifests. With one file per manifest, a
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. A "feature file" carries all atoms for that feature:
262
+ one file. The fixed site manifest carries all atoms:
500
263
 
501
264
  ```yaml
502
- # manifests/contact.yaml
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 walks files and parses each `---` block as a separate
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 + cron + MCP):
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 feature →
586
- one file, regardless of how many atoms it requires.
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 a
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
- #### File organization conventions (suggested, not enforced)
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 validator does not enforce naming conventions; it walks
608
- all `*.yaml` under the manifest root and parses every `---`
609
- block as a manifest.
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 tractable. A typical small site is 5–20 manifest
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
- - File-discovery patterns (`find . -name "send-contact-message.trigger.yaml"`)
634
- no longer work for atoms inside multi-doc files. Authors
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
- - **Multi-doc files grow unwieldy.** A "shop" feature might
651
- have a Schema + 3 Procedures + 5 Triggers + 2 Views. One
652
- file with 11 atoms is harder to navigate than 11 files.
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: one feature → one file with `---` separators.
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
- one HTTP + one cron + one MCP doesn't change the Procedure;
693
- it adds two more Triggers in the same file.
694
- - Tooling: SDK loader walks `*.yaml` under manifest root and
695
- parses each `---` block; static validator emits diagnostics
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
- (`feature.yaml#/2/spec/from`) so multi-doc location stays
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
- Anything beyond (`any:` disjunction; `owns:`, `withinMinutes:`,
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, including the v0.1-vs-DRAFT
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: open a
212
- grammar-revise discussion per ADR-0001 § Future grammar
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 `validate`, `generate`, `skills`, `update`,
254
- `introspect`, `emit-openapi`, and `emit-types`; direct spec installs expose
255
- the authoring subset through `mantle-spec`.
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", "schedule for Friday").
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/recent-published.view.yaml#/spec/from`.
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 `mantle-netlify`
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:** Carried over from POC v0.0.x; amended for the parser-free v0.1
4
- consumer boundary.
3
+ **Status:** Superseded by ADR-0019.
5
4
 
6
- **Date:** 2026-05-01 (POC); last amended 2026-08-03
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 YAML under `manifests/`. The installed `mantle` CLI parses and
26
- validates that YAML, then writes the machine-owned runtime module and handler
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/`, generated module, handlers, and package scripts;
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 YAML only under `manifests/`.
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.