@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.
Files changed (109) hide show
  1. package/README.md +38 -38
  2. package/dist/cli/generate.d.ts +10 -1
  3. package/dist/cli/generate.d.ts.map +1 -1
  4. package/dist/cli/generate.js +49 -4
  5. package/dist/cli/generate.js.map +1 -1
  6. package/dist/cli/main.js +0 -8
  7. package/dist/cli/main.js.map +1 -1
  8. package/dist/codegen/emitMantleModule.js +8 -0
  9. package/dist/codegen/emitMantleModule.js.map +1 -1
  10. package/docs/adapter-guide.md +23 -26
  11. package/docs/adr/0008-structured-diagnostic-shape.md +7 -2
  12. package/docs/adr/0010-locale-and-translates.md +14 -16
  13. package/docs/adr/0011-adapter-port-spec.md +2 -1
  14. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +100 -0
  15. package/docs/adr/0018-core-starters-repository-boundary.md +3 -0
  16. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +38 -2
  17. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +199 -0
  18. package/docs/adr/0021-retire-starter-scaffolding.md +30 -0
  19. package/docs/adr/0022-caller-observed-version-occ.md +212 -0
  20. package/docs/adr/0023-port-failure-contract.md +69 -0
  21. package/docs/adr/0024-manifest-native-schema-tables.md +260 -0
  22. package/docs/adr/README.md +19 -3
  23. package/docs/adr/adr-lite-803-request-diagnostics.md +38 -0
  24. package/docs/adr/adr-lite-808-route-readiness.md +47 -0
  25. package/docs/adr/adr-lite-809-bounded-public-content.md +71 -0
  26. package/docs/adr/adr-lite-812-native-parity.md +150 -0
  27. package/docs/adr/adr-lite-823-home-statistics.md +64 -0
  28. package/docs/adr/adr-lite-842-mcp-authoring.md +30 -0
  29. package/docs/adr/adr-lite-861-admin-webmcp.md +42 -0
  30. package/docs/adr/adr-lite-909-admin-ui-kit.md +37 -0
  31. package/docs/api-mcp-authorization.md +3 -664
  32. package/docs/auth-hosting-model.md +4 -4
  33. package/docs/cloudflare-low-level-composition.md +3 -104
  34. package/docs/deferred-lifecycle-queues.md +2 -243
  35. package/docs/design-atoms.md +16 -796
  36. package/docs/direct-authoring.md +5 -0
  37. package/docs/examples/minimal-worker/README.md +37 -0
  38. package/docs/examples/minimal-worker/manifests/site.yaml +25 -0
  39. package/docs/examples/minimal-worker/package.json +26 -0
  40. package/docs/examples/minimal-worker/smoke.mjs +23 -0
  41. package/docs/examples/minimal-worker/src/index.ts +4 -0
  42. package/docs/examples/minimal-worker/tsconfig.json +17 -0
  43. package/docs/examples/minimal-worker/wrangler.jsonc +22 -0
  44. package/docs/handbook/cloudflare/authentication.md +167 -0
  45. package/docs/handbook/cloudflare/bindings.md +236 -0
  46. package/docs/handbook/cloudflare/conventional-worker.md +162 -0
  47. package/docs/handbook/cloudflare/deferred-hooks-queues.md +171 -0
  48. package/docs/handbook/cloudflare/deploy-and-operate.md +105 -0
  49. package/docs/handbook/cloudflare/low-level-composition.md +169 -0
  50. package/docs/handbook/cloudflare/media-r2.md +216 -0
  51. package/docs/handbook/cloudflare/public-web.md +144 -0
  52. package/docs/handbook/concepts/authorization.md +116 -0
  53. package/docs/handbook/concepts/four-atoms.md +173 -0
  54. package/docs/handbook/concepts/lifecycle-and-locales.md +142 -0
  55. package/docs/handbook/concepts/mcp-and-agents.md +109 -0
  56. package/docs/handbook/concepts/procedures-and-triggers.md +221 -0
  57. package/docs/handbook/concepts/runtime-and-adapters.md +177 -0
  58. package/docs/handbook/concepts/views.md +191 -0
  59. package/docs/handbook/examples/commerce-transaction.md +813 -0
  60. package/docs/handbook/examples/guarded-api.md +427 -0
  61. package/docs/handbook/examples/intake-form.md +319 -0
  62. package/docs/handbook/examples/legal-documents.md +218 -0
  63. package/docs/handbook/examples/procurement-approvals.md +240 -0
  64. package/docs/handbook/examples/publication.md +240 -0
  65. package/docs/handbook/examples/reservation.md +220 -0
  66. package/docs/handbook/navigation.json +63 -0
  67. package/docs/handbook/reference/authorization.md +295 -0
  68. package/docs/handbook/reference/diagnostics.md +183 -0
  69. package/docs/handbook/reference/manifest.md +132 -0
  70. package/docs/handbook/reference/procedure.md +263 -0
  71. package/docs/handbook/reference/schema.md +250 -0
  72. package/docs/handbook/reference/site-config.md +233 -0
  73. package/docs/handbook/reference/surface.md +207 -0
  74. package/docs/handbook/reference/trigger.md +193 -0
  75. package/docs/handbook/reference/view.md +256 -0
  76. package/docs/handbook/start/project-and-cli.md +122 -0
  77. package/docs/handbook/start/quickstart-worker.md +193 -0
  78. package/docs/labels.md +3 -1
  79. package/docs/media-uploads.md +3 -184
  80. package/docs/migration-0.1.2.md +60 -3
  81. package/docs/performance-harness.md +127 -10
  82. package/docs/release-process.md +85 -231
  83. package/docs/schema-indexes.md +3 -180
  84. package/docs/sealed-pipeline-ownership.md +6 -5
  85. package/docs/spec-only-host-adoption.md +158 -0
  86. package/docs/transaction-patterns.md +5 -0
  87. package/package.json +21 -25
  88. package/skills/README.md +4 -4
  89. package/skills/develop/SKILL.md +23 -28
  90. package/skills/install/SKILL.md +55 -147
  91. package/skills/provision/SKILL.md +4 -3
  92. package/skills/theme/SKILL.md +6 -6
  93. package/skills/update/SKILL.md +32 -68
  94. package/dist/cli/create.d.ts +0 -2
  95. package/dist/cli/create.d.ts.map +0 -1
  96. package/dist/cli/create.js +0 -243
  97. package/dist/cli/create.js.map +0 -1
  98. package/dist/cli/update.d.ts +0 -2
  99. package/dist/cli/update.d.ts.map +0 -1
  100. package/dist/cli/update.js +0 -413
  101. package/dist/cli/update.js.map +0 -1
  102. package/dist/provision/renderProvisionBundle.d.ts +0 -70
  103. package/dist/provision/renderProvisionBundle.d.ts.map +0 -1
  104. package/dist/provision/renderProvisionBundle.js +0 -367
  105. package/dist/provision/renderProvisionBundle.js.map +0 -1
  106. package/dist/provision.d.ts +0 -2
  107. package/dist/provision.d.ts.map +0 -1
  108. package/dist/provision.js +0 -2
  109. package/dist/provision.js.map +0 -1
@@ -1,186 +1,5 @@
1
- # Cloudflare R2 Media Uploads
1
+ # Media uploads
2
2
 
3
- Mantle supports staff media uploads through Staff MCP tools:
4
- `create_media_upload` and `commit_media_upload`. This is an optional
5
- post-launch feature for sites that use the Cloudflare adapter and need
6
- staff-managed images or files.
3
+ This guide moved into the handbook: [`docs/handbook/cloudflare/media-r2.md`](handbook/cloudflare/media-r2.md).
7
4
 
8
- Do not make R2 part of the Day 1 launch path. Cloudflare R2 setup may
9
- require billing or a credit card, so landing and the blank starter do not
10
- provision it by default.
11
-
12
- ## When To Enable
13
-
14
- Enable this after the site already has:
15
-
16
- - a working Cloudflare Worker deploy;
17
- - staff auth configured;
18
- - a real need for staff or an agent to upload images/files.
19
-
20
- Use Claude Code, Codex, Cursor, or another local/non-sandboxed coding
21
- agent for media maintenance. Claude Cowork often cannot complete this
22
- flow because the final upload is a direct HTTP PUT to
23
- `*.r2.cloudflarestorage.com`.
24
-
25
- ## How It Works
26
-
27
- The upload flow is deliberately split:
28
-
29
- 1. The agent calls Staff MCP `create_media_upload` with purpose, variant
30
- metadata, byte sizes, and mime types.
31
- 2. Mantle returns signed upload URLs and required headers.
32
- 3. The agent reads the local/chat attachment bytes and PUTs them directly
33
- to R2.
34
- 4. The agent calls `commit_media_upload`.
35
-
36
- Do not pass image bytes or base64 payloads through MCP tool arguments.
37
- The Worker validates policy; the agent runtime performs file processing
38
- and upload.
39
-
40
- ## Cloudflare Setup
41
-
42
- Create a bucket and public read URL:
43
-
44
- ```bash
45
- wrangler r2 bucket create <project>-media
46
- wrangler r2 bucket dev-url enable <project>-media
47
- ```
48
-
49
- In the Cloudflare dashboard, create an R2 S3 API token:
50
-
51
- 1. Open **R2**.
52
- 2. Open **Manage R2 API Tokens**.
53
- 3. Create an Object Read & Write token.
54
- 4. Copy the Access Key ID and Secret Access Key.
55
-
56
- The R2 binding alone cannot issue presigned PUT URLs; Mantle also needs
57
- these S3-compatible credentials.
58
-
59
- ## `wrangler.toml`
60
-
61
- ```toml
62
- [vars]
63
- R2_ACCOUNT_ID = "<account-id>"
64
- MEDIA_PUBLIC_URL_BASE = "https://pub-<hash>.r2.dev"
65
-
66
- [[r2_buckets]]
67
- binding = "MEDIA_BUCKET"
68
- bucket_name = "<project>-media"
69
- ```
70
-
71
- Set secrets:
72
-
73
- ```bash
74
- wrangler secret put R2_ACCESS_KEY_ID
75
- wrangler secret put R2_SECRET_ACCESS_KEY
76
- ```
77
-
78
- ## `src/mantle/config.ts`
79
-
80
- ```ts
81
- import { R2MediaStorage, type MantleCloudflareConfig } from "@aotter/mantle/cloudflare";
82
- import { AwsClient } from "aws4fetch";
83
-
84
- export interface Env {
85
- readonly MEDIA_BUCKET?: R2Bucket;
86
- readonly R2_ACCOUNT_ID?: string;
87
- readonly R2_ACCESS_KEY_ID?: string;
88
- readonly R2_SECRET_ACCESS_KEY?: string;
89
- readonly MEDIA_PUBLIC_URL_BASE?: string;
90
- }
91
-
92
- function buildMediaStorage(env: Env): MantleCloudflareConfig["bindings"]["mediaStorage"] {
93
- if (
94
- !env.MEDIA_BUCKET ||
95
- !env.R2_ACCOUNT_ID ||
96
- !env.R2_ACCESS_KEY_ID ||
97
- !env.R2_SECRET_ACCESS_KEY ||
98
- !env.MEDIA_PUBLIC_URL_BASE
99
- ) {
100
- return undefined;
101
- }
102
-
103
- const s3 = new AwsClient({
104
- accessKeyId: env.R2_ACCESS_KEY_ID,
105
- secretAccessKey: env.R2_SECRET_ACCESS_KEY,
106
- region: "auto",
107
- service: "s3",
108
- });
109
-
110
- return new R2MediaStorage(
111
- env.MEDIA_BUCKET,
112
- s3,
113
- `https://<project>-media.${env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
114
- env.MEDIA_PUBLIC_URL_BASE,
115
- );
116
- }
117
- ```
118
-
119
- Then wire it into the CMS config:
120
-
121
- ```ts
122
- siteDefaults: {
123
- media: {
124
- purposes: [
125
- {
126
- name: "page-image",
127
- required: ["image/jpeg,image/png", "image/webp", "image/avif"],
128
- maxBytes: {
129
- "image/jpeg": 5_000_000,
130
- "image/png": 5_000_000,
131
- "image/webp": 3_000_000,
132
- "image/avif": 2_000_000,
133
- },
134
- },
135
- ],
136
- },
137
- },
138
- bindings: {
139
- mediaStorage: buildMediaStorage(env),
140
- },
141
- ```
142
-
143
- `required` is slot-based. The example above declares three slots:
144
-
145
- - slot 0: choose exactly one fallback mime, preserving the source
146
- semantics (`image/jpeg` for opaque photos, `image/png` when alpha must
147
- be preserved);
148
- - slot 1: include a WebP variant;
149
- - slot 2: include an AVIF variant.
150
-
151
- This models the ADR-0017 multi-variant `<picture>` shape. If a site wants
152
- a simpler single-variant upload for a narrow purpose, it may declare one
153
- slot such as `["image/jpeg,image/png,image/webp,image/gif"]`; in that
154
- case, the agent chooses exactly one mime from the comma-separated list and
155
- must not upload one variant per listed mime.
156
-
157
- ## Tool Visibility
158
-
159
- The Staff MCP media tools are registered only when both are true:
160
-
161
- - `bindings.mediaStorage` is set;
162
- - `siteDefaults.media.purposes` contains at least one purpose.
163
-
164
- If either side is missing, `create_media_upload` and
165
- `commit_media_upload` will not appear in `tools/list`.
166
-
167
- ## Agent Guidance
168
-
169
- For image maintenance, prefer Claude Code or another coding agent that can
170
- read local files, process images, and make outbound PUT requests to R2.
171
- Avoid Claude Cowork for this workflow; use a non-sandboxed agent instead.
172
-
173
- Preserve source semantics:
174
-
175
- - photos may use JPEG/WebP variants;
176
- - transparent logos must keep alpha;
177
- - animated GIFs must stay animated;
178
- - do not silently flatten, resize, or recompress user assets without
179
- asking.
180
-
181
- ## Cleanup
182
-
183
- Run the version-matched `media-gc` skill when an upload reached R2 but was
184
- never committed. It audits first and removes only stale objects without
185
- `committedAt` metadata after explicit operator confirmation. Do not use an R2
186
- lifecycle rule: committed and uncommitted media share the same purpose prefix.
5
+ The handbook ships inside the `@aotter/mantle` package under `node_modules/@aotter/mantle/docs/handbook/` and describes the installed release.
@@ -1,4 +1,60 @@
1
- # Migrating from 0.1.0-alpha.7 to 0.1.2
1
+ # Leaving the legacy alpha.17 launch line
2
+
3
+ 0.1.0-alpha.17 remains immutable and is the final version for existing Landing
4
+ and Starters. Those repositories/tags/bundle URLs remain available. Staying on
5
+ that version requires no migration. No stable 0.1.0 release is planned.
6
+
7
+ The new line removes `mantle create`, the Starter bundle `mantle update`
8
+ command, and `@aotter/mantle/provision`. There are no aliases or replacement
9
+ scaffold commands. `generate`, `validate`, `emit-openapi` and `skills` remain.
10
+ Generation and runtime Web rendering retain their existing responsibilities.
11
+
12
+ To upgrade an existing application:
13
+
14
+ 1. Read its installed version, lockfile, entry, scripts and provider bindings;
15
+ keep unrelated work safe in the normal git workflow.
16
+ 2. Pin the selected SDK packages to the intended exact new release and update
17
+ the lockfile through the package manager. Review required peer upgrades.
18
+ 3. Remove scripts invoking the retired scaffolder/bundle updater. If application
19
+ code imports the provision renderer, remain on alpha.17 until that host's
20
+ provisioning design is migrated explicitly; do not replace it with a
21
+ handwritten remote-code loader.
22
+ 4. Retain application source and all Worker/D1/KV identity, origins, auth mode,
23
+ secrets and legacy `.mantle` metadata. Those files are not templates to
24
+ replace or evidence that new Starter tags must exist.
25
+ 5. Run the installed `mantle generate`, `generate --check`, `skills`,
26
+ `skills --check`, `validate`, and the project's TypeScript/tests. Test local
27
+ routes and configured authorization before considering deployment.
28
+
29
+ ## Native Schema-table storage reset
30
+
31
+ The 0.1.2 pre-beta line replaces the generic `entries` JSON table with one
32
+ native SQLite/D1 table per Manifest Schema. Mantle's row envelope uses
33
+ `_mantle_id`, `_mantle_status`, `_mantle_version`, `_mantle_author_id`,
34
+ `_mantle_created_at`, and `_mantle_updated_at`; authored fields keep their exact
35
+ names as native columns. The old generated columns, projection views and
36
+ compatibility repository were removed.
37
+
38
+ This is intentionally a storage-format break before beta. Reset and
39
+ re-bootstrap development or internal-alpha content databases that contain the
40
+ old `entries` layout. Move required data manually outside Mantle and Control;
41
+ there is no product migration workflow for this unreleased format. Automatic
42
+ artifacts cover initial and additive changes only. Removed columns and tables
43
+ remain physically present so the previous Worker can still run. Renames, type
44
+ changes and data transforms require the same manual rebuild. The pre-beta Cloud
45
+ path does not accept or execute destructive SQL.
46
+
47
+ Row APIs are now Schema-qualified. `EntryRepository.get` and
48
+ `EntryReader.readById` accept `{ collection, id }`; Admin entry detail and
49
+ mutation routes require `?collection=<schema>`, and generic MCP entry tools
50
+ require `collection`. Generated `entries.<schema>` bindings supply it for you.
51
+
52
+ A new project follows [direct authoring](direct-authoring.md). Templates and
53
+ provider setup are not hidden inside `generate`. Future Builder/landing-next
54
+ provisioning is a separate decision; this change does not migrate those hosts.
55
+
56
+ ## Earlier alpha.7 compatibility changes
57
+
2
58
 
3
59
  0.1.2 removes the temporary full-site compatibility stack. Mantle Core is now
4
60
  an embeddable parse → link → compile → prepare → bind pipeline; Web, Admin,
@@ -41,5 +97,6 @@ Intentional behavior changes:
41
97
 
42
98
  This alpha changes the Better Auth D1 schema, including required account
43
99
  issuer identity and OAuth resource/client tables. Reset and re-bootstrap a
44
- pre-1.7 alpha auth database; do not guess an issuer backfill. Content tables
45
- remain portable through the normal application migration/export path.
100
+ pre-1.7 alpha auth database; do not guess an issuer backfill. Reset old generic
101
+ content storage as described above and move required data manually outside
102
+ Mantle.
@@ -31,7 +31,7 @@ must not query Mantle-owned tables outside the selected storage adapter.
31
31
  the Worker. Cache keys are version-local, so a deploy starts with no stale
32
32
  response from the previous Worker version.
33
33
  - Successful publishing-content and site-setting writes purge the shared
34
- `mantle-public` Cache-Tag through Cloudflare's native cache API. Operational
34
+ deployment-scoped public Cache-Tag through Cloudflare's native cache API. Operational
35
35
  records and immutable assets stay outside that invalidation boundary.
36
36
  - Do not cache every repository read. Cross-isolate correctness for editable
37
37
  data wins unless a read has a measured hot-path contract and explicit
@@ -50,7 +50,7 @@ pnpm exec mantle-harness indexes --require account-members --format json
50
50
  ```
51
51
 
52
52
  Without `--require-public` or `--require`, findings are advisory. A required
53
- path fails on an `entries` table scan, a temporary ORDER BY B-tree, or a
53
+ path fails on a required Schema-table scan, a temporary ORDER BY B-tree, or a
54
54
  data-field predicate/order that does not use a declared Schema index.
55
55
  Projection alone does not require an index. `mantle validate` remains a pure
56
56
  correctness check; no performance grammar or manifest atom was added.
@@ -75,12 +75,14 @@ Timing always reports p50/p95/max. A test-only Worker wrapper may also return
75
75
  `x-mantle-query-count` and `x-mantle-rows-read`; those become distributions in
76
76
  the same report. Do not expose these diagnostic headers in production.
77
77
 
78
- Core CI runs `pnpm bench:wrangler` against real Wrangler-local D1, Worker HTTP
79
- routing, View execution, and origin page rendering. It compares 100 and 10,000
80
- row fixtures and gates row-read scaling plus endpoint query budgets, not
81
- absolute milliseconds. Wrangler-local does not emulate the new entrypoint
82
- Workers Cache, so cache hits are a deployment-level smoke check rather than a
83
- fabricated local metric.
78
+ The path-scoped Cloudflare benchmark workflow runs `pnpm bench:wrangler`
79
+ against real Wrangler-local D1, Worker HTTP routing, View execution, and origin
80
+ page rendering. It compares 100 and 10,000 row fixtures and gates row-read
81
+ scaling plus endpoint query budgets, not absolute milliseconds. It is separate
82
+ from the required repository checks so an unrelated dependency or docs PR does
83
+ not fail on the platform harness. Wrangler-local does not emulate the new
84
+ entrypoint Workers Cache, so cache hits are a deployment-level smoke check
85
+ rather than a fabricated local metric.
84
86
 
85
87
  ## Seven findings: measured disposition
86
88
 
@@ -90,8 +92,8 @@ diagnostic, while query/row counts are the stable assertions.
90
92
  | Finding | Disposition |
91
93
  |---|---|
92
94
  | Public cache hits read D1 first | Removed from Worker code. Cloudflare's entrypoint Workers Cache runs before the Worker; Core has no inner render cache. |
93
- | Slug/locale reads bypass generated indexes | Fixed by the shared schema-aware entry-read boundary. A 10,000-row page MISS measured 2 queries / 5 rows read. |
94
- | OFFSET pagination | Accepted for the v0.1 bounded-result surfaces: every response is capped at 500 rows and public hot paths must stay shallow. Deep/export workloads require a purpose-shaped cursor API before they are declared hot. |
95
+ | Slug/locale reads bypass Schema indexes | Fixed by the shared schema-aware entry-read boundary over native columns. A 10,000-row page MISS measured 2 queries / 5 rows read. |
96
+ | OFFSET pagination | Retained only where the View/Admin contract explicitly uses it, with a 500-row response cap. Public content lists and discovery now use forward keyset pages; they are not covered by the old 500-row claim. |
95
97
  | Admin substring search scans | Accepted only for the authenticated Admin collection browser, with a 500-row response cap. Large/search-heavy sites should add a purpose-shaped indexed View or dedicated search service; do not expose this scan publicly. |
96
98
  | Published list/sitemap/llms paths lack system indexes | Fixed with measured partial indexes for published global, locale, collection, and collection+locale ordering. The 100-row and 10,000-row API runs both measured 1 query / 20 rows read. |
97
99
  | Page MISS waits for cache write-back | Removed. Origin rendering returns directly; Workers Cache owns response storage outside the Worker. |
@@ -102,3 +104,118 @@ not patterns for new public APIs. Re-measure before widening either scope.
102
104
 
103
105
  See also [Schema indexes](./schema-indexes.md) and the official Cloudflare
104
106
  [D1 index guidance](https://developers.cloudflare.com/d1/best-practices/use-indexes/).
107
+
108
+ ### Prepared database, new Worker state
109
+
110
+ The Wrangler fixture resets its in-isolate runtime after seeding while retaining
111
+ D1. The first public page has a four-statement budget (fingerprint, lazy locale,
112
+ site settings, entry); subsequent origin pages have a two-statement budget.
113
+ This is a new application state in the same workerd isolate, not a measurement
114
+ of module startup CPU or an entrypoint cache HIT. Locale caching is enabled by
115
+ a successful preparation fingerprint; editable settings and media policy still
116
+ read the canonical database on every call.
117
+
118
+ ### HTTP Trigger routing
119
+
120
+ The portable request handler indexes Trigger paths by method and segments once.
121
+ Literal and wildcard branches retain sealed-plan order, including encoded literal
122
+ collisions that an outer router may select differently. Each request decodes its
123
+ segments once and invokes the original Trigger identity through the same runtime.
124
+ The 1/10/100/1,000-route regression uses four segment lookups at every size;
125
+ overlapping wildcard shapes can visit multiple branches, pruned by route rank.
126
+ The HTTP microbench and workerd harness include the same route-count axis.
127
+ Workerd wall times include I/O and are not CPU measurements or a fixed-ms CI gate.
128
+
129
+ ### Public content pages and discovery (#809)
130
+
131
+ | Surface | Canonical read / continuation |
132
+ |---|---|
133
+ | Collection HTML and collection Markdown | 50 entries by default, forward `cursor`; visible Next link plus HTTP `Link: rel="next"`. |
134
+ | Locale and root llms.txt | One canonical page, default 50 entries; root expands that page across configured locales in memory. Shared entries are not reread once per locale. Follow the body/HTTP continuation link. |
135
+ | Sitemap part | Up to 2,000 entries, with only declared path fields (built-in resolver: `slug`). A small site returns a urlset directly; a larger site returns a sitemap index linking every part. |
136
+ | Sitemap index | Walks metadata pages to derive exact part cursors; O(N) metadata work on an index MISS, with one page resident at a time. It is not a constant-work list endpoint. |
137
+
138
+ `EntryReader.readPublishedPage` caps returned data JSON at 1 MiB and 2,000 rows.
139
+ One oversized entry is returned alone to make progress. SQLite applies the byte
140
+ budget before transferring/parsing JSON in the Worker; one extra candidate
141
+ identifies continuation. A localized + shared page merges two indexed ranges
142
+ inside the same statement. The original `readPublished` remains an explicit
143
+ unbounded read unless its caller supplies a limit.
144
+
145
+ Translation lists resolve at most one newest published parent per join value,
146
+ then resolve media once for the bounded list. Parent payloads and media metadata
147
+ are additional input; the 1 MiB budget describes the canonical child page, not
148
+ arbitrary template output or total Worker heap. Custom renderers own their output
149
+ size. IndexedDB keeps identical page semantics but currently scans its local
150
+ collection; this is not a claim of bounded IndexedDB storage I/O.
151
+
152
+ The real SQLite fixture matrix covers 100/10,000/50,000 published rows, 64 B/4 KiB
153
+ bodies, and 1/3/10 locales. At limit 20, every case transfers 21 candidate rows;
154
+ 4 KiB body data occupies 87,003–87,129 bytes, independent of collection size.
155
+ Complete llms traversal uses 2/200/1,000 statements at the default 50-row page
156
+ size, independent of locale count. Sitemap and llms URL sets match, including
157
+ 275,000 URLs for 50,000 mixed localized/shared rows across 10 locales.
158
+
159
+ Run `pnpm --filter @aotter/mantle-cloudflare exec vitest run
160
+ test/public-content-scaling.test.ts` to emit JSON transfer sizes and traversal
161
+ CPU/wall/RSS diagnostics. These are Node + SQLite + assertions, including the
162
+ fixture database and URL-validation set; RSS is a process high-water mark, not
163
+ per-request Worker peak memory. Worker CPU, true cache HIT/MISS and placement
164
+ measurements belong to the matched native/full-stack harness (#812).
165
+
166
+ The workerd smoke also measures public list, llms and sitemap. The first two
167
+ use two warm statements (settings + page) and bounded D1 work. Sitemap index
168
+ queries and rows-read scale with the number of metadata parts; this explicit
169
+ cost preserves complete discovery instead of silently dropping URLs.
170
+
171
+ ### Request diagnostics (version 1, test/performance only)
172
+
173
+ Import `runWithRequestDiagnostics`, `instrumentD1`, `instrumentKv` and
174
+ `instrumentR2` from `@aotter/mantle-cloudflare/testing`. Wrap native bindings once
175
+ before handing the same D1 object to Auth and Runtime. Open the request context
176
+ outside the complete facade fetch, and pass binding-presence flags matching the
177
+ instrumented fixture. The observer receives one response-time record; it never
178
+ receives request headers, tokens, proofs, user IDs, SQL, parameters, tool arguments,
179
+ object keys or response content. Sync/async observer failure cannot change the
180
+ original response or exception. The library emits no diagnostic headers or logs.
181
+
182
+ Records distinguish HTTP outcome from JSON-RPC result/error/tool-error. They
183
+ include actual in-isolate arrivals, inclusive OAuth/DPoP, role, runtime, catalog,
184
+ dispatcher construction and dispatch wall spans. Unreached phases are null.
185
+ Shared KV loads charge native I/O once to the initiating request; waiters record
186
+ wait duration and the same hit/miss/repair/error source. Boot publication has a
187
+ separate counter. The original rejected shared load remains retryable.
188
+
189
+ D1 statements and binding calls are separate: a batch is one call and N attempted
190
+ statements. Failed attempts count. `exec` uses the provider's count; unavailable
191
+ counts stay in `unknownStatementCalls`, never a semicolon parser. `first(column)`
192
+ and `raw` preserve native behavior and do not silently execute `all` to manufacture
193
+ metadata. Their absent metadata is null. Rows/duration are sums of available
194
+ metadata, and `metadataStatements` identifies coverage; incomplete coverage is
195
+ not a full-workload total. Serialized binding results are measured bytes, not a
196
+ claim about bytes on the provider's wire. Existing D1DatabaseDriver observers can
197
+ request metadata for first-row reads when a fixture explicitly chooses that mode.
198
+
199
+ KV bytes identify UTF-8, buffer or reserialized JSON sources. R2 payload bytes
200
+ remain unknown for an unconsumed/partly consumed GET. A successful PUT of that exact
201
+ native GET stream confirms the transferred body size on both operations. Streams
202
+ are never wrapped or buffered for diagnostics, preserving R2's native known-length
203
+ contract. `byteSamples` distinguishes known payload samples from the operation
204
+ count. Metadata/list response serialization is labeled separately from object
205
+ payload. R2 coverage is head/get/put/delete/list, not multipart-upload instrumentation.
206
+
207
+ Snapshots freeze at response creation; outstanding/deferred operations remain
208
+ visible through `inFlight` and metadata coverage and cannot rewrite a published
209
+ record. `totalMs` excludes the subsequent test-only JSON-RPC response inspection,
210
+ delivery and deferred work. CPU, TTFB, full-body duration and heap must be measured
211
+ separately. Worker wall clocks advance on I/O and are not a CPU timer; use the
212
+ [official CPU profiler](https://developers.cloudflare.com/workers/observability/dev-tools/cpu-usage/).
213
+ Measure diagnostics off/on overhead with the same workload before interpreting
214
+ small latency differences.
215
+
216
+ ### Matched native facade controls (#812)
217
+
218
+ `pnpm bench:wrangler` now also runs the native parity smoke. The full
219
+ `pnpm bench:parity` matrix adds real Auth/MCP, cold workerd processes, R2,
220
+ TTFB/full-body timing and CPU/heap evidence. See
221
+ [the controls, gates and reproducible commands](./adr/adr-lite-812-native-parity.md).