@aotter/mantle 0.1.0-alpha.9 → 0.1.2-alpha.2
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 +38 -38
- package/dist/cli/generate.d.ts +10 -1
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +49 -4
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/main.js +0 -8
- package/dist/cli/main.js.map +1 -1
- package/dist/codegen/emitMantleModule.js +8 -0
- package/dist/codegen/emitMantleModule.js.map +1 -1
- package/docs/adapter-guide.md +23 -26
- package/docs/adr/0008-structured-diagnostic-shape.md +7 -2
- package/docs/adr/0010-locale-and-translates.md +14 -16
- package/docs/adr/0011-adapter-port-spec.md +2 -1
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +100 -0
- package/docs/adr/0018-core-starters-repository-boundary.md +3 -0
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +38 -2
- package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +199 -0
- package/docs/adr/0021-retire-starter-scaffolding.md +30 -0
- package/docs/adr/0022-caller-observed-version-occ.md +212 -0
- package/docs/adr/0023-port-failure-contract.md +69 -0
- package/docs/adr/0024-manifest-native-schema-tables.md +260 -0
- package/docs/adr/README.md +19 -3
- package/docs/adr/adr-lite-803-request-diagnostics.md +38 -0
- package/docs/adr/adr-lite-808-route-readiness.md +47 -0
- package/docs/adr/adr-lite-809-bounded-public-content.md +71 -0
- package/docs/adr/adr-lite-812-native-parity.md +150 -0
- package/docs/adr/adr-lite-823-home-statistics.md +64 -0
- package/docs/adr/adr-lite-842-mcp-authoring.md +30 -0
- package/docs/adr/adr-lite-861-admin-webmcp.md +42 -0
- package/docs/adr/adr-lite-909-admin-ui-kit.md +37 -0
- package/docs/api-mcp-authorization.md +3 -664
- package/docs/auth-hosting-model.md +4 -4
- package/docs/cloudflare-low-level-composition.md +3 -104
- package/docs/deferred-lifecycle-queues.md +2 -243
- package/docs/design-atoms.md +16 -796
- package/docs/direct-authoring.md +5 -0
- package/docs/examples/minimal-worker/README.md +37 -0
- package/docs/examples/minimal-worker/manifests/site.yaml +25 -0
- package/docs/examples/minimal-worker/package.json +26 -0
- package/docs/examples/minimal-worker/smoke.mjs +23 -0
- package/docs/examples/minimal-worker/src/index.ts +4 -0
- package/docs/examples/minimal-worker/tsconfig.json +17 -0
- package/docs/examples/minimal-worker/wrangler.jsonc +22 -0
- package/docs/handbook/cloudflare/authentication.md +167 -0
- package/docs/handbook/cloudflare/bindings.md +236 -0
- package/docs/handbook/cloudflare/conventional-worker.md +162 -0
- package/docs/handbook/cloudflare/deferred-hooks-queues.md +171 -0
- package/docs/handbook/cloudflare/deploy-and-operate.md +105 -0
- package/docs/handbook/cloudflare/low-level-composition.md +169 -0
- package/docs/handbook/cloudflare/media-r2.md +216 -0
- package/docs/handbook/cloudflare/public-web.md +144 -0
- package/docs/handbook/concepts/authorization.md +116 -0
- package/docs/handbook/concepts/four-atoms.md +173 -0
- package/docs/handbook/concepts/lifecycle-and-locales.md +142 -0
- package/docs/handbook/concepts/mcp-and-agents.md +109 -0
- package/docs/handbook/concepts/procedures-and-triggers.md +221 -0
- package/docs/handbook/concepts/runtime-and-adapters.md +177 -0
- package/docs/handbook/concepts/views.md +191 -0
- package/docs/handbook/examples/commerce-transaction.md +813 -0
- package/docs/handbook/examples/guarded-api.md +427 -0
- package/docs/handbook/examples/intake-form.md +319 -0
- package/docs/handbook/examples/legal-documents.md +218 -0
- package/docs/handbook/examples/procurement-approvals.md +240 -0
- package/docs/handbook/examples/publication.md +240 -0
- package/docs/handbook/examples/reservation.md +220 -0
- package/docs/handbook/navigation.json +63 -0
- package/docs/handbook/reference/authorization.md +295 -0
- package/docs/handbook/reference/diagnostics.md +183 -0
- package/docs/handbook/reference/manifest.md +132 -0
- package/docs/handbook/reference/procedure.md +263 -0
- package/docs/handbook/reference/schema.md +250 -0
- package/docs/handbook/reference/site-config.md +233 -0
- package/docs/handbook/reference/surface.md +207 -0
- package/docs/handbook/reference/trigger.md +193 -0
- package/docs/handbook/reference/view.md +256 -0
- package/docs/handbook/start/project-and-cli.md +122 -0
- package/docs/handbook/start/quickstart-worker.md +193 -0
- package/docs/labels.md +3 -1
- package/docs/media-uploads.md +3 -184
- package/docs/migration-0.1.2.md +60 -3
- package/docs/performance-harness.md +127 -10
- package/docs/release-process.md +85 -231
- package/docs/schema-indexes.md +3 -180
- package/docs/sealed-pipeline-ownership.md +6 -5
- package/docs/spec-only-host-adoption.md +158 -0
- package/docs/transaction-patterns.md +5 -0
- package/package.json +21 -25
- package/skills/README.md +4 -4
- package/skills/develop/SKILL.md +23 -28
- package/skills/install/SKILL.md +55 -147
- package/skills/provision/SKILL.md +4 -3
- package/skills/theme/SKILL.md +6 -6
- package/skills/update/SKILL.md +32 -68
- package/dist/cli/create.d.ts +0 -2
- package/dist/cli/create.d.ts.map +0 -1
- package/dist/cli/create.js +0 -243
- package/dist/cli/create.js.map +0 -1
- package/dist/cli/update.d.ts +0 -2
- package/dist/cli/update.d.ts.map +0 -1
- package/dist/cli/update.js +0 -413
- package/dist/cli/update.js.map +0 -1
- package/dist/provision/renderProvisionBundle.d.ts +0 -70
- package/dist/provision/renderProvisionBundle.d.ts.map +0 -1
- package/dist/provision/renderProvisionBundle.js +0 -367
- package/dist/provision/renderProvisionBundle.js.map +0 -1
- package/dist/provision.d.ts +0 -2
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -2
- package/dist/provision.js.map +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ADR-0023: Storage and service failure contracts
|
|
2
|
+
|
|
3
|
+
Status: Accepted for alpha.17 under #857 (amends ADR-0008 and media deletion behavior).
|
|
4
|
+
|
|
5
|
+
This is the diagnostic grammar-revise record: add RESOURCE_EXHAUSTED (507),
|
|
6
|
+
RESOURCE_UNAVAILABLE (503), RATE_LIMITED (429), OUTCOME_UNKNOWN (503),
|
|
7
|
+
PARTIAL_FAILURE (503), PRECONDITION_FAILED (412), and optional safe `failure`
|
|
8
|
+
facts to Diagnostic. No Manifest keys, new ports, or parallel wire format.
|
|
9
|
+
Existing codes and envelopes remain compatible. DiagnosticError accepts internal
|
|
10
|
+
ErrorOptions.cause; causes never enter Diagnostic. Unexpected exceptions must not
|
|
11
|
+
be copied into public messages. Core owns no tenant, plan or provider quota policy.
|
|
12
|
+
|
|
13
|
+
## Contract
|
|
14
|
+
|
|
15
|
+
A port rejects with DiagnosticError for recognized failures. The diagnostic
|
|
16
|
+
contains stable code, safe message and optional failure facts: outcome is
|
|
17
|
+
not-applied/partial/unknown; retry is never/after-change/safe/reconcile; resource
|
|
18
|
+
is an optional non-sensitive logical resource name, not credentials or SQL.
|
|
19
|
+
A provider adapter or host policy owns classification. Unknown errors stay
|
|
20
|
+
unexpected; never guess quota from arbitrary exception prose. Existing specific
|
|
21
|
+
validation, not-found and atomic OCC diagnostics remain authoritative.
|
|
22
|
+
|
|
23
|
+
`safe` requires idempotence of the same operation against the same resource.
|
|
24
|
+
It does not request automatic retry. Timeouts after write/send default to unknown
|
|
25
|
+
outcome and reconciliation, not unconditional retry. Do not downgrade unknown
|
|
26
|
+
outcome to not-applied merely because no reply arrived. Not-found on an
|
|
27
|
+
idempotent object delete is success. Read absence may be null per the port.
|
|
28
|
+
|
|
29
|
+
## Operation matrix (public contracts)
|
|
30
|
+
|
|
31
|
+
| Contract | Success | Failure / effect / retry |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| EntryReader / ViewQueryExecutor | documented rows/null/page | denied/unavailable/rate limit; reads have no business mutation; explicit retry after transient failure |
|
|
34
|
+
| EntryRepository | semantic mutation with atomic expectedVersion | existing NOT_FOUND/CONFLICT; host capacity/denial; unknown write requires readback by stable id/version, never latest-version substitution |
|
|
35
|
+
| SiteConfigRepository | validated settings/locale | reads as above; writes same conditional and uncertain-effect rules as entries |
|
|
36
|
+
| MediaAssetRepository / PendingUploadRepository | metadata persisted/deleted | capacity/availability/unknown write; reconcile by asset/upload id; removal of missing metadata is idempotent where specified |
|
|
37
|
+
| MantleStorageAdapter.prepare / migrations | ready prepared storage | invalid plan/readiness; completed migration steps may persist; journal/readback before retry, no cross-provider rollback promise |
|
|
38
|
+
| DatabaseDriver | driver rows/batch result | constraint/precondition or recognized provider failure; transaction guarantees remain driver-specific; ambiguous writes require readback, SQL is never public |
|
|
39
|
+
| MediaStorage.createUpload | authorized capabilities, not uploaded bytes | policy/size/denied/unavailable; adapter must state any reservations created; no general single-use guarantee |
|
|
40
|
+
| MediaStorage.commitUpload | verified asset, then use case stores metadata | missing/type/size/precondition or partial/unknown provider effect; reconcile same upload group and objects before retry |
|
|
41
|
+
| MediaStorage.deleteObject | object absent | idempotent same-key retry; unavailable/unknown; use case retains metadata if any variant fails and returns PARTIAL_FAILURE |
|
|
42
|
+
| EmailSender | provider accepted, not guaranteed recipient delivery | known rejection not-applied; lost acknowledgement unknown/reconcile; do not resend automatically without provider idempotency/readback |
|
|
43
|
+
| DeferredHookDispatcher | accepted for at-least-once delivery | ambiguous enqueue may duplicate; existing fallback preserves eventId + trigger idempotency identity; consumers must deduplicate |
|
|
44
|
+
| HandlerRegistry / custom handler | application output | DiagnosticError/InvokeFailure preserved; unknown exceptions internal only; handler owns side-effect semantics |
|
|
45
|
+
| Clock / IdGenerator | timestamp / unpredictable unique id | unexpected environmental/programming errors; never silently substitute insecure ids |
|
|
46
|
+
| selected Auth contract (outside Runtime) | authentication/session/admin operation | preserve supported validation/denial; provider/email/storage failure handled at Auth adapter boundary; no assumption that Procedure catches Auth errors |
|
|
47
|
+
|
|
48
|
+
HTTP and Admin use the existing central mapping; MCP retains the diagnostic
|
|
49
|
+
code/failure facts rather than deriving application recovery from HTTP status.
|
|
50
|
+
Admin OCC recovery applies to structured CONFLICT only, not all 409 responses.
|
|
51
|
+
Unknown provider bodies, SQL and causes remain internal. Host-supplied messages
|
|
52
|
+
and resource labels are explicitly public and must not include sensitive data.
|
|
53
|
+
|
|
54
|
+
## Media partial deletion change
|
|
55
|
+
|
|
56
|
+
Previously object errors were logged and the asset row removed. Now every
|
|
57
|
+
variant deletion is attempted; any failure retains the row and returns partial
|
|
58
|
+
failure. A retry deletes the same keys (including already absent objects), then
|
|
59
|
+
removes metadata. If metadata deletion itself has an uncertain result, reconcile
|
|
60
|
+
by asset id; no new object identities are created. No cross-store transaction is
|
|
61
|
+
implied. Remaining metadata may reference already removed bytes during recovery.
|
|
62
|
+
|
|
63
|
+
## Compatibility / verification
|
|
64
|
+
|
|
65
|
+
New failure fields are additive; old consumers still have code/message. Media
|
|
66
|
+
partial-delete clients now receive failure rather than misleading success.
|
|
67
|
+
Focused tests cover diagnostic serialization, transport redaction, host rejection
|
|
68
|
+
and retry of partial deletion. Concrete adapter conformance must preserve
|
|
69
|
+
provider constraints and no automatic retry of uncertain writes/sends.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# ADR-0024: Materialize each Schema as a native storage table
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
|
|
5
|
+
**Date:** 2026-09-18
|
|
6
|
+
|
|
7
|
+
**Related:** ADR-0010, ADR-0011, ADR-0019, ADR-0020, ADR-0022, ADR-0023
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The SQLite adapter currently stores every Schema row in one `entries` table.
|
|
12
|
+
Business fields live in a JSON `data` column. Schema indexes add generated
|
|
13
|
+
columns and partial indexes to that shared table, and Schema-shaped SQL views
|
|
14
|
+
project JSON fields back into columns.
|
|
15
|
+
|
|
16
|
+
That layout made the first dynamic runtime small, but it is now the wrong
|
|
17
|
+
authority for a manifest-built system:
|
|
18
|
+
|
|
19
|
+
- a successfully built Manifest already fixes every Schema and field type;
|
|
20
|
+
- generated columns for every historical indexed field accumulate on one
|
|
21
|
+
table, while D1 allows at most 100 columns per table;
|
|
22
|
+
- native SQL, inspection, export, and migration tooling see an implementation
|
|
23
|
+
envelope instead of the authored data model;
|
|
24
|
+
- application migrations and Mantle's canonical `entries` migrations form two
|
|
25
|
+
persistence systems;
|
|
26
|
+
- the compatibility layer is now larger than direct table CRUD.
|
|
27
|
+
|
|
28
|
+
Mantle has not shipped a stable storage format. Keeping the generic table for
|
|
29
|
+
downward compatibility would create the dual path this change is intended to
|
|
30
|
+
remove.
|
|
31
|
+
|
|
32
|
+
## Decision
|
|
33
|
+
|
|
34
|
+
### One physical table per Schema
|
|
35
|
+
|
|
36
|
+
The SQLite/D1 storage adapter materializes every compiled Schema as a quoted
|
|
37
|
+
native table. The table contains:
|
|
38
|
+
|
|
39
|
+
- Mantle columns: `_mantle_id`, `_mantle_status`, `_mantle_version`,
|
|
40
|
+
`_mantle_created_at`, `_mantle_updated_at`, and `_mantle_author_id`;
|
|
41
|
+
- one column for every top-level Schema property;
|
|
42
|
+
- native indexes for every `indexes` and `uniqueIndexes` tuple.
|
|
43
|
+
|
|
44
|
+
Single scalar JSON Schema types map to SQLite affinities: string to `TEXT`,
|
|
45
|
+
integer and boolean to `INTEGER`, and number to `REAL`. Objects, arrays, unions,
|
|
46
|
+
and otherwise polymorphic values use `TEXT` containing canonical JSON so a
|
|
47
|
+
round trip cannot change their JSON type. Nullable values use SQL `NULL`.
|
|
48
|
+
|
|
49
|
+
Business columns remain nullable at the database layer. Mantle allows an
|
|
50
|
+
incomplete authoring draft; Runtime validation enforces required fields when a
|
|
51
|
+
record becomes complete or when an operational Schema is written. An optional
|
|
52
|
+
nullable property reads as `null` when its column is null. This is the v0.1
|
|
53
|
+
native-table contract; it intentionally does not preserve a distinction
|
|
54
|
+
between an absent optional nullable field and an explicit null.
|
|
55
|
+
|
|
56
|
+
Schema names and field names remain authored wire names and are always SQL
|
|
57
|
+
identifier quoted. A Schema table uses its authored Schema name. SQLite
|
|
58
|
+
preparation rejects collisions with selected infrastructure tables or views,
|
|
59
|
+
including auth tables; this check belongs to the concrete deployment because
|
|
60
|
+
the portable Manifest cannot know which optional infrastructure a host binds.
|
|
61
|
+
Internal Mantle tables and columns use the reserved `_mantle_` prefix. The
|
|
62
|
+
prefix prevents Mantle metadata from stealing ordinary authored names such as
|
|
63
|
+
`id`, `status`, or `createdAt`.
|
|
64
|
+
|
|
65
|
+
The SQLite adapter no longer creates `entries`, generated field columns, or
|
|
66
|
+
Schema projection views. There is no dual write, compatibility view, or
|
|
67
|
+
fallback JSON repository.
|
|
68
|
+
|
|
69
|
+
### The compiled storage plan owns DDL
|
|
70
|
+
|
|
71
|
+
SQLite preparation lowers the Schemas already present in the sealed
|
|
72
|
+
`RuntimePlan` into quoted DDL and prepared queries. A persistence-only
|
|
73
|
+
projection of those Schemas has a deterministic storage fingerprint; changes
|
|
74
|
+
to Procedures, Views, or Triggers do not create a storage revision. No second
|
|
75
|
+
physical-plan model mirrors `RuntimePlan`.
|
|
76
|
+
|
|
77
|
+
Other adapters map the same Schema manifests into their own native storage.
|
|
78
|
+
Runtime continues to consume semantic `EntryRepository`,
|
|
79
|
+
`EntryReader`, and `ViewQueryExecutor` ports; SQL remains infrastructure.
|
|
80
|
+
|
|
81
|
+
Repository operations that address one row carry its Schema collection.
|
|
82
|
+
`get(id)` and `readById(id)` become collection-qualified operations. Generated
|
|
83
|
+
bindings hide this argument behind Schema-specific APIs. Runtime operations
|
|
84
|
+
already know the target Schema from their compiled handler, route, or row
|
|
85
|
+
context; callers must not search every table for an unqualified id.
|
|
86
|
+
|
|
87
|
+
Cross-Schema reads are compiled explicitly from the Runtime plan. There is no
|
|
88
|
+
implicit union of all tables. A caller that needs several Schemas invokes the
|
|
89
|
+
declared Views or Schema-specific readers and combines their results at the
|
|
90
|
+
application layer.
|
|
91
|
+
|
|
92
|
+
### Migrations are build artifacts
|
|
93
|
+
|
|
94
|
+
The SQLite build compares a stored source Schema description with the target
|
|
95
|
+
RuntimePlan and emits a versioned SQL migration artifact containing:
|
|
96
|
+
|
|
97
|
+
- source and target storage fingerprints;
|
|
98
|
+
- ordered SQLite SQL;
|
|
99
|
+
- a checksum over the exact migration content;
|
|
100
|
+
- whether the change is expand-only or destructive.
|
|
101
|
+
|
|
102
|
+
Mantle deterministically emits initial tables and safe additive changes. Field
|
|
103
|
+
renames, type conversions, data transforms, and narrowing constraints are
|
|
104
|
+
destructive in the pre-beta contract. Cloud rejects them; operators rebuild the
|
|
105
|
+
database and move required data manually outside Mantle and Control. Production
|
|
106
|
+
neither generates nor accepts arbitrary migration SQL.
|
|
107
|
+
|
|
108
|
+
Preparation records applied migration ids in the existing `_migrations` ledger.
|
|
109
|
+
The enclosing immutable artifact checksum protects the ordered SQL,
|
|
110
|
+
fingerprints, and target projections. Preparation rejects an unknown source
|
|
111
|
+
fingerprint, changed artifact checksum, skipped revision, or target mismatch.
|
|
112
|
+
Generated migrations are idempotent and retryable after partial application;
|
|
113
|
+
activation and integrity verification run on every attempt instead of being
|
|
114
|
+
treated as one-time ledger entries.
|
|
115
|
+
|
|
116
|
+
Generated migrations quote all identifiers and never interpolate runtime
|
|
117
|
+
input. Automatic deployments retain removed fields, indexes, and tables as a
|
|
118
|
+
physical superset; logical reads follow the selected plan, while retained
|
|
119
|
+
storage keeps the previous Worker rollback-compatible.
|
|
120
|
+
|
|
121
|
+
### Builder support
|
|
122
|
+
|
|
123
|
+
Builder keeps its in-memory or IndexedDB semantic adapter for interactive
|
|
124
|
+
preview. It does not carry SQLite into portable Runtime.
|
|
125
|
+
|
|
126
|
+
Each successful build can show a draft-to-draft storage delta for authoring
|
|
127
|
+
feedback. It is not labeled as the deployment artifact: only Cloud knows the
|
|
128
|
+
last successfully deployed source revision and produces the exact immutable
|
|
129
|
+
artifact used by deployment. The publish result shows its source/target
|
|
130
|
+
fingerprints, risk classification, ordered SQL, and checksum.
|
|
131
|
+
The interactive preview continues to run the real Mantle Runtime over the
|
|
132
|
+
existing IndexedDB semantic adapter; it does not carry a second SQLite runtime
|
|
133
|
+
or copy preview data into one. Destructive changes remain previewable but are
|
|
134
|
+
not deployable on the pre-beta Cloud path.
|
|
135
|
+
|
|
136
|
+
CI executes generated SQL against SQLite. Mantle Cloud/D1 is authoritative at
|
|
137
|
+
deployment: it checks the immutable artifact, migration ledger, source/target
|
|
138
|
+
fingerprints, database integrity, and canary before restoring traffic. Running
|
|
139
|
+
the same DDL in every Builder browser would duplicate those gates while adding
|
|
140
|
+
WASM weight and a second Worker/browser compatibility surface.
|
|
141
|
+
|
|
142
|
+
Unsaved invalid Manifest edits produce diagnostics and no storage revision.
|
|
143
|
+
Intermediate Builder edits do not become production migrations. Deployment
|
|
144
|
+
diffs the last deployed storage revision against the selected project revision.
|
|
145
|
+
|
|
146
|
+
### Cloud runtime and Schema upgrades
|
|
147
|
+
|
|
148
|
+
Every Cloud deployment pins these immutable identities:
|
|
149
|
+
|
|
150
|
+
- Mantle runtime release;
|
|
151
|
+
- project revision and compiled plan fingerprint;
|
|
152
|
+
- source and target storage fingerprints;
|
|
153
|
+
- migration checksum;
|
|
154
|
+
- application artifact checksum.
|
|
155
|
+
|
|
156
|
+
Automatic migrations are expand-only and therefore run online. The deployment
|
|
157
|
+
sequence is:
|
|
158
|
+
|
|
159
|
+
1. verify the current tenant D1 storage fingerprint;
|
|
160
|
+
2. apply idempotent additive DDL and verify the ledger, marker, and integrity;
|
|
161
|
+
3. upload an immutable Worker script named by project revision;
|
|
162
|
+
4. run the canary directly against that script and the actual D1 marker;
|
|
163
|
+
5. select that revision for the default and custom host routes;
|
|
164
|
+
6. record the revision active.
|
|
165
|
+
|
|
166
|
+
There is no KV maintenance gate: KV propagation cannot drain in-flight writes,
|
|
167
|
+
and additive DDL does not need one. Destructive changes stop before deployment.
|
|
168
|
+
The old revision script remains addressable, so route failure or operator
|
|
169
|
+
rollback selects the previous script without rebuilding or replacing it.
|
|
170
|
+
|
|
171
|
+
Automatic deployments retain old columns and tables needed by the previous
|
|
172
|
+
active Worker. This keeps code rollback valid. Physical cleanup happens only
|
|
173
|
+
during a later reset or rebuild. Mantle runtime upgrades use the same mechanism
|
|
174
|
+
even when the authored Manifest does not change.
|
|
175
|
+
|
|
176
|
+
Release and application objects are content-addressed. A retry validates and
|
|
177
|
+
reads the same bytes selected by the original operation; mutable release names
|
|
178
|
+
are not valid deployment input.
|
|
179
|
+
|
|
180
|
+
## Consequences
|
|
181
|
+
|
|
182
|
+
### Positive
|
|
183
|
+
|
|
184
|
+
- D1, SQLite tools, generated types, and the Manifest describe the same tables.
|
|
185
|
+
- Each Schema gets independent native limits, indexes, query plans, and schema
|
|
186
|
+
evolution.
|
|
187
|
+
- The shared-table column ceiling and permanent generated-column buildup are
|
|
188
|
+
removed.
|
|
189
|
+
- Application migrations and Mantle runtime upgrades use one revision model.
|
|
190
|
+
- Removing the compatibility repository leaves less persistence code.
|
|
191
|
+
|
|
192
|
+
### Negative
|
|
193
|
+
|
|
194
|
+
- This is a storage-format break and requires coordinated changes across Spec,
|
|
195
|
+
Runtime, SQLite adapters, conformance tests, Builder, and Cloud deployment.
|
|
196
|
+
- Unqualified global entry lookups and implicit cross-Schema lists disappear.
|
|
197
|
+
- Optional nullable fields no longer distinguish absent from explicit null.
|
|
198
|
+
- Destructive schema changes require a manual rebuild and data move outside
|
|
199
|
+
Mantle and Control.
|
|
200
|
+
|
|
201
|
+
## Alternatives
|
|
202
|
+
|
|
203
|
+
### Keep `entries` and improve its generated indexes
|
|
204
|
+
|
|
205
|
+
Rejected. It preserves the shared table's column ceiling, JSON inspection
|
|
206
|
+
surface, and a second schema representation after Manifest build already knows
|
|
207
|
+
the physical model.
|
|
208
|
+
|
|
209
|
+
### Keep `entries` as a registry beside native Schema tables
|
|
210
|
+
|
|
211
|
+
Rejected. A registry adds dual-write atomicity and stale-index failure modes.
|
|
212
|
+
Schema-qualified access removes the need for it.
|
|
213
|
+
|
|
214
|
+
### Put SQLite WASM in the portable Runtime
|
|
215
|
+
|
|
216
|
+
Rejected. Runtime already has a semantic storage boundary. Builder can verify
|
|
217
|
+
the SQLite lowering as a build check without making every adapter or browser
|
|
218
|
+
preview depend on SQLite.
|
|
219
|
+
|
|
220
|
+
### Generate production migrations at deploy time
|
|
221
|
+
|
|
222
|
+
Rejected. Deployment must execute an immutable reviewed artifact and remain
|
|
223
|
+
deterministic when retried. AI generation belongs before review, not inside a
|
|
224
|
+
production mutation.
|
|
225
|
+
|
|
226
|
+
## How to apply
|
|
227
|
+
|
|
228
|
+
1. Add the sealed physical storage plan and SQLite DDL lowering.
|
|
229
|
+
2. Change semantic row operations to require collection identity and update all
|
|
230
|
+
first-party callers.
|
|
231
|
+
3. Replace the shared SQLite repository and View compiler with native-table
|
|
232
|
+
implementations; delete shared-table DDL, views, index reconciliation, and
|
|
233
|
+
compatibility helpers.
|
|
234
|
+
4. Update memory and IndexedDB adapters to the new semantic port shape.
|
|
235
|
+
5. Add build-time migration diff/artifact inspection, plus SQLite execution in
|
|
236
|
+
CI and Cloud/D1 deployment verification.
|
|
237
|
+
6. Pin content-addressed runtime, storage, migration, application, and Worker
|
|
238
|
+
revision identities in Cloud deploys; test initial deploy, additive Schema
|
|
239
|
+
change, runtime-only upgrade, partial migration retry, and route rollback.
|
|
240
|
+
7. Update all public storage, Schema, Builder, deployment, and migration
|
|
241
|
+
documentation after the conformance and consumer checks pass.
|
|
242
|
+
|
|
243
|
+
## Decision review
|
|
244
|
+
|
|
245
|
+
Reviewed after implementation against ADR-0019, the semantic storage ports,
|
|
246
|
+
Builder preview persistence, and Mantle Cloud's retryable deployment state.
|
|
247
|
+
The review found and corrected three boundary mistakes before acceptance:
|
|
248
|
+
|
|
249
|
+
- authored data may legitimately contain `id`, `status`, or `createdAt`, so
|
|
250
|
+
physical envelope columns require the `_mantle_` prefix;
|
|
251
|
+
- Builder should expose draft storage risk without pretending it knows the
|
|
252
|
+
deployed baseline; Cloud shows the exact immutable artifact, while CI and
|
|
253
|
+
Cloud/D1 own executable DDL verification and preview stays on IndexedDB;
|
|
254
|
+
- Cloud must pin the runtime release and storage artifact selected when the
|
|
255
|
+
operation starts; a mutable current release would make retry and rollback
|
|
256
|
+
nondeterministic.
|
|
257
|
+
|
|
258
|
+
The native-table path is implemented without a compatibility `entries` table.
|
|
259
|
+
Acceptance is gated by the repository consumer check and the Cloud upgrade,
|
|
260
|
+
retry, and rollback checks in the ADR's final application step.
|
package/docs/adr/README.md
CHANGED
|
@@ -18,8 +18,12 @@ Records of *why* mantle ended up shaped this way. The numbering preserves POC AD
|
|
|
18
18
|
| [0014](0014-auth-better-auth-and-multi-tenant-mcp.md) | The Cloudflare adapter owns one curated Better Auth 1.7 identity/OAuth/MCP authority with CIMD discovery. Verified callers are normalized into runtime context; mutable staff role and target authorization are re-evaluated per call. | Accepted + amended |
|
|
19
19
|
| [0016](0016-site-semantic-layer.md) | Site semantic layer: `AGENTS.md` (cross-tool entry) + `.mantle/launch-state.json` (deterministic install context). The older `mantle/site.md` letter surface is suspended from first-run scaffolds. | Accepted (slimmed) |
|
|
20
20
|
| [0017](0017-media-multi-variant-agent-side-optimization.md) | Multi-variant media assets with agent-side optimization and asset-id entry references. | Accepted |
|
|
21
|
-
| [0018](0018-core-starters-repository-boundary.md) | Core produces published SDK artifacts; the separate starters repository validates them as an external consumer. Revisit after release-contract simplification. |
|
|
21
|
+
| [0018](0018-core-starters-repository-boundary.md) | Core produces published SDK artifacts; the separate starters repository validates them as an external consumer. Revisit after release-contract simplification. | Superseded by 0021 |
|
|
22
22
|
| [0019](0019-sealed-manifest-runtime-pipeline.md) | One sealed source-to-runtime pipeline, semantic storage seam, and optional Web/Admin/platform dependency direction. | Accepted |
|
|
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
|
+
| [0021](0021-retire-starter-scaffolding.md) | Direct authoring; retire Starter scaffolding. | Accepted; supersedes 0018 |
|
|
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 |
|
|
23
27
|
|
|
24
28
|
## Reading order
|
|
25
29
|
|
|
@@ -31,7 +35,7 @@ If you're new to the codebase:
|
|
|
31
35
|
4. **0007** — what running the SDK feels like as an AI author (and as the operator agent).
|
|
32
36
|
5. **0011** — the boundary between the runtime and the adapter.
|
|
33
37
|
6. **0010** — how locale flows through the system.
|
|
34
|
-
7. **
|
|
38
|
+
7. **0021** — current direct-authoring boundary; **0013** is historical install-session context.
|
|
35
39
|
8. **0002, 0008** — the two ADRs that touch every diagnostic and every binding.
|
|
36
40
|
|
|
37
41
|
## What's NOT here (and why)
|
|
@@ -54,8 +58,20 @@ The rebuild's ADR-0011 (new) is the most load-bearing addition — the POC accum
|
|
|
54
58
|
|
|
55
59
|
## Contributing a new ADR
|
|
56
60
|
|
|
57
|
-
1. Pick the next number
|
|
61
|
+
1. Pick the next unused number after the highest existing ADR.
|
|
58
62
|
2. File: `docs/adr/<NNNN>-<kebab-title>.md`.
|
|
59
63
|
3. Sections: Status, Date, Context, Decision, Consequences, Alternatives, How to apply, Implementation status.
|
|
60
64
|
4. Link from this README's table.
|
|
61
65
|
5. Land it in a PR alongside (or before) the implementation it documents — ADR-as-design-artifact, not ADR-as-archaeology.
|
|
66
|
+
|
|
67
|
+
## ADR-lite records
|
|
68
|
+
|
|
69
|
+
These retain their issue-based filenames and original decision status.
|
|
70
|
+
|
|
71
|
+
| Record | Status |
|
|
72
|
+
|---|---|
|
|
73
|
+
| [#803 Request diagnostics](adr-lite-803-request-diagnostics.md) | Implemented |
|
|
74
|
+
| [#808 Route readiness](adr-lite-808-route-readiness.md) | Accepted |
|
|
75
|
+
| [#809 Bounded public content](adr-lite-809-bounded-public-content.md) | Implemented |
|
|
76
|
+
| [#812 Native parity](adr-lite-812-native-parity.md) | Implemented; deployment evidence linked in record |
|
|
77
|
+
| [#823 Home statistics](adr-lite-823-home-statistics.md) | Proposed; unmerged demo |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ADR-lite: request-scoped Cloudflare diagnostics
|
|
2
|
+
|
|
3
|
+
Status: implemented diagnostic record for #803; real-auth controls and measured
|
|
4
|
+
comparison acceptance belong to the #812 harness. This does not close the
|
|
5
|
+
separate starter provisioning/Smart Placement portion of #803.
|
|
6
|
+
|
|
7
|
+
Use native AsyncLocalStorage and a small versioned testing entry point instead of
|
|
8
|
+
a global current-request counter or a configurable production telemetry system.
|
|
9
|
+
Production mounts only check for an existing test context. With none, there are
|
|
10
|
+
no diagnostic clock reads, binding wrappers, records, observers or response headers.
|
|
11
|
+
Runtime, auth, role, catalog, dispatcher-build and dispatch retain their ownership
|
|
12
|
+
and authorization order. The ordinary runtime-ready promise keeps its identity.
|
|
13
|
+
|
|
14
|
+
Native D1 instrumentation sits beneath both Auth and Runtime and is idempotent.
|
|
15
|
+
It preserves receivers, bind chains, first-column behavior and native objects in
|
|
16
|
+
batches; no second driver observer is added for counting. Provider failures and
|
|
17
|
+
unknown metadata remain visible. Request context is captured when an operation
|
|
18
|
+
starts, so shared work belongs to its initiator. Catalog waiters inherit source
|
|
19
|
+
classification and wait time without inheriting the owner's binding counts.
|
|
20
|
+
|
|
21
|
+
The record freezes at response creation. Inclusive spans can overlap, and partial
|
|
22
|
+
metadata or deferred work must not be presented as a full total. A test-only cloned
|
|
23
|
+
MCP response supplies the JSON-RPC outcome without exposing its content; that
|
|
24
|
+
inspection is outside totalMs but remains instrumentation overhead for HTTP timing.
|
|
25
|
+
Observer errors never replace application results. D1/KV/R2 operations emit only
|
|
26
|
+
counts, sizes with their source/coverage, and durations; object keys, SQL, caller
|
|
27
|
+
identities and credentials never enter the record.
|
|
28
|
+
|
|
29
|
+
R2 GET bodies stay native. Successful direct stream-to-PUT completion confirms
|
|
30
|
+
payload bytes; canceled, incomplete and unconsumed reads remain unknown. This
|
|
31
|
+
avoids losing R2's known-length stream property or hiding buffering overhead.
|
|
32
|
+
|
|
33
|
+
Checks deliberately overlap MCP requests and share KV hit/failure loads, then
|
|
34
|
+
retry after failure. Native I/O is counted once per owner, and denied requests
|
|
35
|
+
leave unreached phases null. Additional checks preserve native D1 private receiver
|
|
36
|
+
and batch semantics, sync/async observer failure, immutable deferred snapshots,
|
|
37
|
+
R2 stream identity and partial-transfer uncertainty. The collector fixture labels
|
|
38
|
+
its deterministic auth explicitly; it is not evidence of native-auth parity.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Route-owned Cloudflare readiness (#808)
|
|
2
|
+
|
|
3
|
+
Status: accepted for the unreleased #806–812 implementation. Follows ADR-0019
|
|
4
|
+
(preparation owns migrations) and amends ADR-0014's unconditional HTTP boot rule.
|
|
5
|
+
|
|
6
|
+
The facade assembles immutable route projections once, then prepares only for
|
|
7
|
+
routes that consume canonical storage. It retains one retryable preparation
|
|
8
|
+
promise, the sealed plan and Better Auth's existing global AsyncLocalStorage,
|
|
9
|
+
`ready` observation, failed-assembly eviction and HTTP `waitUntil` ownership.
|
|
10
|
+
|
|
11
|
+
| Surface | Readiness, including an empty database |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| Consumer constant health, `/api/views` catalog | No content preparation. |
|
|
14
|
+
| Static Admin shell and assets | No content preparation; incomplete Auth still fails closed as before. |
|
|
15
|
+
| Manifest View/Trigger | Prepare before credential resolution, then use the same runtime invocation. |
|
|
16
|
+
| Admin API, configured Auth base path, OAuth UI/discovery | Prepare before session/provider work. Auth tables share canonical migrations. |
|
|
17
|
+
| MCP, including missing-token challenge | Prepare before the selected Auth verifier. A custom Auth implementation may require D1 even for a denied request; the facade cannot assume otherwise. |
|
|
18
|
+
| Public page/list/discovery and favicon fallback | Their existing `ref.get()` starts preparation when content is needed. |
|
|
19
|
+
| Consumer extension handlers | Call the supplied `getRuntime()` or `ref.get()` before using Mantle content or database-backed Auth. Pure handlers need neither. |
|
|
20
|
+
| Queue/scheduled `getRuntime(env)` | Await both runtime preparation and Auth initialization, unchanged. |
|
|
21
|
+
|
|
22
|
+
No deployment-time migration flag or KV readiness authority is introduced. A
|
|
23
|
+
current database's first protected route needs one fingerprint SELECT; an empty
|
|
24
|
+
or changed database performs canonical setup there. Concurrent first uses share
|
|
25
|
+
that work. A failed preparation returns the facade's redacted 500, and later
|
|
26
|
+
requests retry. Static availability is not a promise that data is ready.
|
|
27
|
+
|
|
28
|
+
The pre-change full-facade fixture recorded 106 SQL operations for its empty-DB
|
|
29
|
+
health request (its four Schemas include indexes), then 1 fingerprint operation
|
|
30
|
+
for each new-state health/catalog/shell/challenge. These are statement counts,
|
|
31
|
+
not network round trips. Its dry-run bundle was 3,921.40 KiB / 690.43 KiB gzip
|
|
32
|
+
with Wrangler 4.124.0 and compatibility date 2026-07-08. Esbuild attributed
|
|
33
|
+
1,342,428 emitted bytes to Better Auth, 76,078 to Admin, 24,624 to Web and
|
|
34
|
+
203,130 to Runtime; the rest includes transitive libraries and adapter code.
|
|
35
|
+
Byte attribution does not measure startup CPU. We retain static imports and
|
|
36
|
+
immutable mount-time projections: deferring those modules has not yet been
|
|
37
|
+
justified by a startup profile. The final #812 report owns the separate startup,
|
|
38
|
+
preparation-wall and warm-dispatch measurements.
|
|
39
|
+
|
|
40
|
+
Post-change full-facade workerd checks pass all 13 operation gates: empty-DB
|
|
41
|
+
health and fresh-state health/catalog/shell perform zero **content** queries;
|
|
42
|
+
the challenge retains its one fingerprint query. The fixture Auth is a stated
|
|
43
|
+
stub. A separate real Better Auth regression records one eager oauthResource
|
|
44
|
+
lookup while `ready` initializes; Better Auth 1.7.2 defers missing-table resource
|
|
45
|
+
seeding to first access. Subsequent static requests perform no additional Auth
|
|
46
|
+
queries. This background initialization is retained, not counted as eliminated
|
|
47
|
+
content preparation or hidden behind a zero-total-SQL claim.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# ADR-lite: bounded public content and complete discovery
|
|
2
|
+
|
|
3
|
+
Status: implemented for #809; combined deployment measurements tracked in #812.
|
|
4
|
+
Context: ADR-0010 translation joins, ADR-0019 sealed semantic storage, #792 Web
|
|
5
|
+
transport/cache ownership. No manifest keys or remote Web SDK are introduced.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
Add the semantic `EntryReader.readPublishedPage` operation instead of silently
|
|
10
|
+
capping generic `readPublished`. Forward pages sort by updatedAt and id descending,
|
|
11
|
+
with default 50 / maximum 2,000 rows and a 1-MiB data-JSON budget. A single larger
|
|
12
|
+
entry remains readable and advances the cursor. This budget covers the selected
|
|
13
|
+
canonical data, excluding envelope bytes, joined parents, media and consumer HTML.
|
|
14
|
+
SQLite uses indexed, bounded candidates, a running byte sum and lookahead before
|
|
15
|
+
transfer to Worker memory. Exact locale plus shared entries are merged in SQL.
|
|
16
|
+
Selected data fields retain JSON types and use one bound JSON field list, including
|
|
17
|
+
for projections larger than D1's ordinary bind or SQL-function argument limits.
|
|
18
|
+
|
|
19
|
+
The optional Web use cases return an explicit page object. Cloudflare mounts
|
|
20
|
+
publish `Link: rel="next"` and map cursors to URLs. Web passes `nextPageUrl` to
|
|
21
|
+
the list template; the host renders accessible navigation with its own language
|
|
22
|
+
and design. The adapter does not rewrite template HTML. Existing templates must
|
|
23
|
+
adopt this field to retain visible continuation. Web-owned llms composition
|
|
24
|
+
retains its body link. An empty intermediate/final llms page remains traversable.
|
|
25
|
+
Root llms reads each canonical page once and expands shared entries per locale
|
|
26
|
+
without repeated canonical reads. Unknown/unmapped/no-content rows consume their
|
|
27
|
+
place in the page; they do not prevent reaching later eligible rows.
|
|
28
|
+
|
|
29
|
+
Sitemap parts project path metadata and default to 2,000 entries, expanding shared
|
|
30
|
+
routes per locale. The index walks the same metadata-page boundaries as the parts,
|
|
31
|
+
so a byte-limited part cannot cause skipped URLs. Small sites keep a single urlset.
|
|
32
|
+
Additional routes appear only in the first part. A custom path resolver must declare
|
|
33
|
+
its required data fields to obtain projection; otherwise full bounded data remains
|
|
34
|
+
available. Protocol limits fail explicitly instead of silently truncating URLs.
|
|
35
|
+
|
|
36
|
+
Translation lists request only the newest published parent per join value. Storage
|
|
37
|
+
ranks matching IDs before loading their bodies, preventing duplicate historical
|
|
38
|
+
parents from multiplying transferred rows. Missing/draft parents leave the child
|
|
39
|
+
unchanged. Child data still wins and media resolution remains one batch per page.
|
|
40
|
+
|
|
41
|
+
## Limits and alternatives
|
|
42
|
+
|
|
43
|
+
Sitemap index generation is O(N) metadata work on an origin MISS. It retains one
|
|
44
|
+
page and the index's URL list rather than all entry bodies; the protocol permits
|
|
45
|
+
at most 50,000 parts. Persisted generation/part boundaries would require an explicit
|
|
46
|
+
publication/invalidation owner and are deferred until index traffic justifies it.
|
|
47
|
+
A hard `LIMIT 500` would lose discovery URLs and is rejected. Offset pages would
|
|
48
|
+
make deep traversal grow with depth and are rejected. Entry caching would leave
|
|
49
|
+
the cold materialization spike and stale-state questions unresolved.
|
|
50
|
+
|
|
51
|
+
Pages reflect live canonical state, not a cross-request snapshot: publishing or
|
|
52
|
+
changing sort keys during a crawl can move entries between pages. Browser-local
|
|
53
|
+
IndexedDB shares the semantic cursor and output budget but still scans a local
|
|
54
|
+
collection. Parent/media data and consumer template expansion have separate costs;
|
|
55
|
+
there is no promise that arbitrary consumer rendering fits 1 MiB.
|
|
56
|
+
|
|
57
|
+
## Verification
|
|
58
|
+
|
|
59
|
+
Real SQLite tests walk the 18 combinations of 100/10,000/50,000 rows, 64 B/4 KiB
|
|
60
|
+
body and 1/3/10 locales, comparing complete llms/sitemap URL sets. List transfer is
|
|
61
|
+
21 rows at limit 20 in every case; 50,000 mixed rows and 10 locales retain all
|
|
62
|
+
275,000 URLs. Tests cover tied sort keys, >1-MiB entries, JSON projection types,
|
|
63
|
+
missing projected keys, parent duplicates/missing/drafts, child overrides, media
|
|
64
|
+
batching and persistence-field privacy. HTTP tests walk visible continuations,
|
|
65
|
+
empty final pages, sitemap indexes and staff-only preview behavior. The portable
|
|
66
|
+
storage conformance suite checks new semantics across adapters.
|
|
67
|
+
|
|
68
|
+
Wrangler-local records native D1 query/row work for list, llms and sitemap alongside
|
|
69
|
+
existing readiness, View, Procedure and Admin gates. Node traversal CPU/RSS includes
|
|
70
|
+
fixture/assertion costs and is not reported as Worker CPU. Combined #812 evidence
|
|
71
|
+
will supply Worker CPU, heap, cache HIT/MISS, remote controls and placement results.
|