@zackbart/connecta 0.37.0 → 0.38.0

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 (183) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +2 -2
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/auth/clerk-sdk/generated.js +2 -1
  8. package/dist/auth/cloudflare-access.d.ts +3 -1
  9. package/dist/auth/cloudflare-access.js +22 -11
  10. package/dist/auth/downstream-oauth.js +195 -270
  11. package/dist/auth/downstream-revocation.js +1 -1
  12. package/dist/auth/oauth-grant.js +11 -0
  13. package/dist/auth/oauth-token-response.js +1 -1
  14. package/dist/auth/vercel-agent.js +2 -1
  15. package/dist/auth-elicitation.js +2 -2
  16. package/dist/branding.d.ts +1 -15
  17. package/dist/branding.js +2 -2
  18. package/dist/bytes.js +42 -0
  19. package/dist/call-admission-context.js +2 -5
  20. package/dist/call-admission.js +5 -0
  21. package/dist/call-recovery.js +0 -3
  22. package/dist/catalog-cache.js +26 -7
  23. package/dist/catalog-fingerprint.js +2 -18
  24. package/dist/catalog-revision.js +9 -0
  25. package/dist/catalog-service.js +51 -20
  26. package/dist/config-defaults.d.ts +1 -0
  27. package/dist/config-defaults.js +1 -0
  28. package/dist/config-schema.d.ts +1 -1
  29. package/dist/config-schema.js +6 -2
  30. package/dist/config.d.ts +41 -14
  31. package/dist/config.js +21 -71
  32. package/dist/connector-caller.js +1 -1
  33. package/dist/connectors/api-connector.js +4 -3
  34. package/dist/connectors/guarded-fetch.d.ts +0 -6
  35. package/dist/connectors/guarded-fetch.js +1 -11
  36. package/dist/connectors/remote-mcp.js +93 -69
  37. package/dist/connectors/resource-uri.js +0 -4
  38. package/dist/credentials.js +3 -0
  39. package/dist/d1.d.ts +0 -89
  40. package/dist/d1.js +0 -34
  41. package/dist/downstream-input.js +2 -2
  42. package/dist/execute.js +161 -116
  43. package/dist/executor-result.js +2 -1
  44. package/dist/executors/quickjs-runtime.js +2 -25
  45. package/dist/executors/quickjs.js +1 -1
  46. package/dist/guest-runtime.js +5 -3
  47. package/dist/guest-types.d.ts +5 -5
  48. package/dist/http/client.js +1 -1
  49. package/dist/http/errors.js +2 -2
  50. package/dist/http/json-schema.js +1 -1
  51. package/dist/http/node-transport.js +0 -1
  52. package/dist/http/oauth.js +1 -1
  53. package/dist/http/paging.js +6 -9
  54. package/dist/http/provider.js +1 -1
  55. package/dist/http/safety.js +1 -1
  56. package/dist/http/services.js +1 -8
  57. package/dist/http/tools.js +1 -7
  58. package/dist/http/vendor.js +7 -7
  59. package/dist/identity.js +2 -6
  60. package/dist/index.js +1 -2
  61. package/dist/invocation-auth.js +2 -1
  62. package/dist/invocation.js +8 -2
  63. package/dist/meta-tools.js +27 -27
  64. package/dist/native-tools.js +2 -1
  65. package/dist/oauth-handoff.js +2 -2
  66. package/dist/operation-vocabulary.js +1 -1
  67. package/dist/operator-record.d.ts +8 -2
  68. package/dist/operator-record.js +18 -50
  69. package/dist/operator-ui/contract.d.ts +4 -2
  70. package/dist/operator-ui/generated.js +3 -3
  71. package/dist/operator-ui/model.d.ts +7 -2
  72. package/dist/program-writes.js +3 -8
  73. package/dist/provider-call.js +0 -7
  74. package/dist/provider.d.ts +1 -1
  75. package/dist/providers/_shared/google/kit.js +1 -6
  76. package/dist/providers/_shared/google/service-account.js +1 -3
  77. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  78. package/dist/providers/_shared/guide.js +3 -0
  79. package/dist/providers/_shared/json.js +12 -0
  80. package/dist/providers/_shared/rest/compact-index.js +1 -1
  81. package/dist/providers/_shared/rest/kit.js +1 -3
  82. package/dist/providers/_shared/rest/operation-index.js +1 -3
  83. package/dist/providers/_shared/rest/value-safety.js +26 -28
  84. package/dist/providers/breeze/api.js +2 -7
  85. package/dist/providers/breeze/index.js +4 -2
  86. package/dist/providers/breeze/tools.js +3 -4
  87. package/dist/providers/ccb/api.js +1 -6
  88. package/dist/providers/ccb/index.js +2 -1
  89. package/dist/providers/ccb/tools.js +1 -6
  90. package/dist/providers/cloudflare/api.js +2 -2
  91. package/dist/providers/cloudflare/index.js +2 -1
  92. package/dist/providers/cloudflare/safety.js +3 -3
  93. package/dist/providers/docs/tools.js +2 -10
  94. package/dist/providers/drive/tools.js +12 -25
  95. package/dist/providers/forms/tools.js +1 -9
  96. package/dist/providers/gmail/tools.js +5 -21
  97. package/dist/providers/overflow/api.js +1 -3
  98. package/dist/providers/overflow/index.js +2 -1
  99. package/dist/providers/overflow/tools.js +3 -7
  100. package/dist/providers/planning-center/api.js +2 -1
  101. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  102. package/dist/providers/planning-center/lib/json-api.js +1 -9
  103. package/dist/providers/planning-center/tools.js +2 -1
  104. package/dist/providers/sheets/tools.js +1 -9
  105. package/dist/providers/slides/tools.js +4 -13
  106. package/dist/providers/stripe/index.js +2 -1
  107. package/dist/providers/stripe/safety.js +2 -2
  108. package/dist/providers/tithely/api.js +2 -7
  109. package/dist/providers/tithely/index.js +3 -2
  110. package/dist/providers/tithely/tools.js +7 -5
  111. package/dist/providers/vercel/api.js +1 -6
  112. package/dist/providers/vercel/lib/pin.js +1 -1
  113. package/dist/providers/vercel/safety.js +1 -1
  114. package/dist/providers/vercel/tools.js +2 -1
  115. package/dist/redaction-marker.js +2 -2
  116. package/dist/registry.d.ts +4 -1
  117. package/dist/registry.js +35 -22
  118. package/dist/request-principal.js +15 -2
  119. package/dist/request-state.js +2 -2
  120. package/dist/routes/access-tokens.js +1 -1
  121. package/dist/routes/activity-disclosure.js +1 -1
  122. package/dist/routes/connect.js +2 -2
  123. package/dist/routes/credentials.js +1 -1
  124. package/dist/routes/dispatch.js +10 -0
  125. package/dist/routes/mcp.js +23 -21
  126. package/dist/routes/oauth-management.js +1 -1
  127. package/dist/routes/operator.js +53 -7
  128. package/dist/routes/shared.d.ts +8 -8
  129. package/dist/routes/shared.js +22 -15
  130. package/dist/routes/ui-config.js +6 -6
  131. package/dist/routes/ui-data.js +2 -2
  132. package/dist/routes/ui.js +9 -4
  133. package/dist/runtime/call-admission.js +1 -1
  134. package/dist/runtime/call.js +1 -1
  135. package/dist/runtime/settlement.js +1 -1
  136. package/dist/runtime/shared-read.js +2 -15
  137. package/dist/sent-secrets.d.ts +14 -6
  138. package/dist/sent-secrets.js +231 -85
  139. package/dist/server.js +4 -4
  140. package/dist/skills.js +8 -35
  141. package/dist/sqlite.d.ts +0 -19
  142. package/dist/sqlite.js +2 -83
  143. package/dist/storage/keys.js +29 -124
  144. package/dist/storage/sql-native.js +14 -111
  145. package/dist/storage/sql.d.ts +1 -24
  146. package/dist/storage/sql.js +1 -12
  147. package/dist/tool-error.js +4 -0
  148. package/dist/types.d.ts +4 -3
  149. package/dist/ui.js +3 -0
  150. package/dist/url-safety.js +0 -4
  151. package/dist/usage-guide.js +2 -2
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/dist/worker.js +1 -1
  155. package/documentation/README.md +6 -42
  156. package/documentation/architecture.md +190 -507
  157. package/documentation/auth.md +172 -58
  158. package/documentation/code-mode.md +98 -66
  159. package/documentation/deploying.md +19 -40
  160. package/documentation/integrating.md +8 -19
  161. package/documentation/meta-tools.md +23 -6
  162. package/documentation/migration-0.33.md +5 -5
  163. package/documentation/migration-0.34.md +2 -4
  164. package/documentation/migration-retired-layouts.md +74 -0
  165. package/documentation/operator-tests.md +19 -119
  166. package/documentation/operator-ui.md +46 -8
  167. package/documentation/provider-auth-migration.md +4 -10
  168. package/documentation/provider-migration-0.29.md +4 -15
  169. package/examples/worker/AGENTS.md +4 -6
  170. package/examples/worker/README.md +20 -115
  171. package/guides/connecting-agents.md +7 -30
  172. package/guides/how-it-works.md +2 -2
  173. package/guides/safety-and-trust.md +4 -1
  174. package/package.json +19 -19
  175. package/templates/node/README.md +9 -26
  176. package/templates/node/package.json +1 -1
  177. package/dist/auth/oauth-v2-migration.js +0 -135
  178. package/dist/provider-call-facts.d.ts +0 -115
  179. package/dist/provider-call.d.ts +0 -112
  180. package/dist/storage/d1-copy.js +0 -255
  181. package/dist/storage/sql-schema.d.ts +0 -6
  182. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  183. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -31,9 +31,8 @@ implementation boundary. Use these sections for the affected subsystem.
31
31
  | Find code and deployment evidence | [Source map](#where-else-to-look), [sharp edges](#sharp-edges), [repository policy](https://github.com/zackbart/connecta/blob/main/AGENTS.md) |
32
32
 
33
33
  Source paths and test names in each section identify its implementation and
34
- regression evidence. Recorded production probes and bundle measurements are
35
- historical observations tied to their stated date or version, not current
36
- benchmarks or proof that planned replacements have shipped.
34
+ regression evidence. Decision records preserve historical measurements; current
35
+ contracts and executable evidence describe the implementation.
37
36
 
38
37
  ## The two lifetimes
39
38
 
@@ -226,7 +225,7 @@ An admitted non-preflight `/mcp` request then takes five steps in
226
225
  ([#178](https://github.com/zackbart/connecta/issues/178)) but their URLs were
227
226
  handed out, and retiring a scoping boundary into fail-open is worse than any
228
227
  404 — then register the six meta-tools on a fresh `McpServer`
229
- (`test/server.test.ts`, `test/code-first-surface.test.ts`).
228
+ (`test/server.test.ts`).
230
229
 
231
230
  ### Modern request metadata and transport refusals
232
231
 
@@ -315,6 +314,28 @@ surface, and the endpoint's `readOnly`.
315
314
  `test/execute.test.ts` asserts the parity directly, because a sandbox path that
316
315
  quietly diverges is how generated code would mint a capability.
317
316
 
317
+ Catalog search retains private, stable arrays for each request's registry and
318
+ safety view, so repeated searches reuse the lexical index. Endpoint and connector
319
+ read-only policy, hidden tools, classification and caller/pool grants still
320
+ bound the visible corpus. Views are keyed by their source catalog array, so a
321
+ replacement read cannot inherit an older index; failed reads remain evicted.
322
+ Before reusing a dynamic catalog, the request checks an in-process revision fence,
323
+ without storage reads, decryption or hashing. Catalog invalidation (including
324
+ operator refresh and OAuth connect/disconnect/restart) advances a counter for the
325
+ registry's cache store; built-in vault set/clear advances a counter for that vault.
326
+ These weakly held counters contain no catalogs, credentials or caller identity;
327
+ catalogs and derived views remain request-local. A moved fence replaces the
328
+ retained read and its search views, and prevents joining an older registry flight.
329
+ Changes made through another process/isolate or directly in storage need not be
330
+ observed mid-request; the next request uses the durable generation and auth checks.
331
+ Custom vault mutations must also invalidate the registry's catalog.
332
+ Authored static catalogs remain independent of credential reads.
333
+ No snapshot or index is shared across requests. Search and describe clone only
334
+ selected definitions before returning schemas or annotations; `loadConnector()`
335
+ and native catalog listings still return independent copies. Observed output
336
+ schemas are resolved when rendering each page rather than retained in the index.
337
+ `test/catalog-service.test.ts` guards reuse and mutation isolation.
338
+
318
339
  Program result paging shares `meta-tools.ts`'s direct-call stash envelope and
319
340
  reader. `program-result.ts` owns executor-independent byte limits;
320
341
  `execute.ts` issues bounded transfer cursors, and `guest-runtime.ts` reconstructs
@@ -336,8 +357,12 @@ capacity boundary, not tenant fairness, and one deployment serves one tenant.
336
357
  `admission.requests.maxDurationMs` defaults to 300,000 ms and may be set to a
337
358
  positive whole number up to 2,147,483,647 ms. Its clock starts when admission
338
359
  grants a permit and covers authorization, tool execution, and response delivery.
339
- The owning request aborts its derived signal at that deadline, including signals
340
- passed to connector calls, and closes a live response. If workerd ends the
360
+ A header-named `subscriptions/listen` candidate starts this clock before auth;
361
+ if classification requires a fallback permit, that queue wait shares the same
362
+ deadline. The owning request aborts its derived signal at that deadline, including
363
+ signals passed to admission waiters and connector calls, and closes a live
364
+ response. A timed-out candidate withdraws its fallback waiter before returning
365
+ 504, so a later slot release cannot admit the ended request. If workerd ends the
341
366
  request without running that timer or cancellation, the controller reclaims
342
367
  only the expired permit's scalar record on the next request (or a queued
343
368
  request's own timer). It never reads the old request's signal or touches its
@@ -356,6 +381,10 @@ endpoint admission, core counts streamed bytes before any SDK or native tool
356
381
  body reader. A body exactly at the cap proceeds through normal protocol
357
382
  validation. Past the cap, core stops reading, cancels both stream branches and
358
383
  returns HTTP 413 with JSON-RPC code `-33006`, `"MCP request body too large."`.
384
+ The inspection reads through the first chunk crossing the cap; the clone's tee
385
+ may pull one further chunk. The request-local signal wrapper reuses the input
386
+ body explicitly, without an intermediate pipe that could pull additional chunks
387
+ ahead. Bytes already buffered by the caller or host are outside this read bound.
359
388
  The response retains the request's existing admission lifetime and releases
360
389
  the permit when delivery ends. Caller cancellation and the existing request
361
390
  deadline also cancel stalled reads. `describeConfig().limits.requests.maxBodyBytes`
@@ -413,139 +442,24 @@ Only an omitted or `undefined` `callAdmission` selects a connector's default.
413
442
  The shared schema rejects malformed defined policies at factory construction,
414
443
  and the registry validates custom connector policies before registration.
415
444
  Falsy values such as `false`, `null`, `0`, and `""` are construction errors
416
- (INV-11), never a way to disable admission. The generated provider fixtures
417
- exercise these refusals and unchanged defaults in
418
- `test/call-admission-options.test.ts`.
445
+ (INV-11), never a way to disable admission.
419
446
 
420
447
  ### Production Worker disconnects
421
448
 
422
- The original production probe for [#573](https://github.com/zackbart/connecta/issues/573)
423
- ran on 2026-09-25 with compatibility date `2025-01-01`, first with no flags and
424
- then with `enable_request_signal`. It used Wrangler 4.114.0 and a Node 26.9.0
425
- client. A raw Worker stream established the runtime behavior; a custom auth
426
- provider returning a synthetic streaming 401 exercised Connecta's real admission
427
- wrapper. Each case had its own Connecta instance with one request permit and no
428
- queue. Health checks and subsequent MCP requests confirmed the same isolate ID.
429
- The [recorded observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-2026-09-25.json)
430
- contain all twelve cases, including served flags and client-end timing.
431
-
432
- | Response and flag | Raw stream abort / cancel | Connecta source abort / cancel | Admission after response; next request |
433
- | ----------------------------------------------------- | ------------------------- | ------------------------------ | -------------------------------------- |
434
- | Heartbeats, no flag | neither | neither | active 1; 503 |
435
- | Idle with a pending timer, no flag | neither | neither | active 1; 503 |
436
- | Heartbeats, request signal enabled | abort only | both | active 0; 200 |
437
- | Idle with a pending timer, request signal enabled | abort only | both | active 0; 200 |
438
- | One chunk, then no timer or I/O, either configuration | neither | neither | active 1; 503 |
439
-
440
- The last row is a different failure. The client observed the response end
441
- _before_ its planned disconnect, although the source never closed itself.
442
- The runtime ended the response without running JavaScript cleanup. This does
443
- not show a missing abort on a still-live request. The other cases remained open
444
- until the client aborted. Admission was sampled 1.5 seconds later, followed by
445
- an actual MCP request; this measures retained capacity, not an infinite leak.
446
-
447
- Enable `enable_request_signal` when deploying on Workers: it let Connecta's
448
- abort listener cancel the source and release admission for live responses in
449
- this experiment. It does not solve runtime-ended streams. The admitted-request
450
- bound above recovers capacity on the first later request at or after its
449
+ Enable `enable_request_signal` when deploying on Workers so Connecta can cancel
450
+ the source and release admission for live responses. It does not solve
451
+ runtime-ended streams. The admitted-request bound above recovers capacity on the first later request at or after its
451
452
  deadline, even if the old request's timer never ran. This is capacity recovery,
452
453
  not proof that workerd cancelled an old stream. A still-live request reaches
453
454
  the same hard deadline and is aborted in its own context.
454
455
  Cloudflare documents the flag in its
455
456
  [Request API](https://developers.cloudflare.com/workers/runtime-apis/request/).
456
457
 
457
- ### Authentication release by collection on workerd
458
-
459
- Measured on 2026-10-20 with workerd 1.20260722.1 (Miniflare 4.20260722.0),
460
- compatibility date `2025-01-01`:
461
-
462
- - `FinalizationRegistry` and `WeakRef` are undefined without
463
- `enable_weak_ref`; the flag's default-on date is 2025-05-05.
464
- - With `enable_weak_ref` and `enable_request_signal`, an object reachable only
465
- through a fetch continuation is finalized once its invocation ends and
466
- workerd cancels the fetch; while the invocation runs it is not. Pending
467
- timers and listeners on the request's signal do not keep it.
468
- - Strong values in a WeakMap keyed by a `Request` (even one the handler
469
- created), properties set on a `Request`, and WeakMap values keyed by its
470
- `Headers`, `ctx` or `env` stay reachable after the invocation ends. The
471
- lifetime registry in `src/auth-admission.ts` therefore stores `WeakRef`s.
472
- - No `gc()` is exposed. Allocating large arrays (32 × 1 MiB per request)
473
- triggers collections, but the most recent unreachable object often survives
474
- them; V8 in this build scans the native stack conservatively. The inspector's
475
- precise `HeapProfiler.collectGarbage` freed it.
476
- - In the vitest-pool-workers harness, an isolate that had already collected
477
- one scenario's cancelled work sometimes kept a later scenario's cancelled
478
- work reachable through 400 rounds of pressure; standalone Miniflare never
479
- did. `test/auth-admission-binding.test.ts` gives each collection scenario a
480
- fresh isolate and requires two of four sentinels collected before asserting
481
- that live charges stayed counted.
482
-
483
- A second production run on the same date tested the request bound at 2,000 ms.
484
- All twelve raw-stream and MCP cases passed with both flag configurations. Live
485
- MCP requests still rejected a competing request with 503 before the deadline;
486
- the first MCP request after the deadline returned all eight tools with 200 in
487
- the original isolate. Admission then reported zero active requests. Without the
488
- flag, the source still recorded no abort or cancellation, so recovery did not
489
- rely on runtime cleanup. The [recovery observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-recovered-2026-09-25.json)
490
- also retain two inconclusive queue-handoff cases: concurrent client connections
491
- reached different isolates and could not establish an abandoned queued waiter.
492
- A [second trial using HTTP/2](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-h2-2026-09-25.json)
493
- repeated the twelve passing core cases and verified queued cancellation with
494
- request signals enabled. Even on one HTTP/2 connection, the no-signal queue case
495
- crossed isolates and remained inconclusive. The Node and workerd regression
496
- suites cover expired waiters, orphaned handoffs, and identity-safe late release
497
- deterministically.
498
-
499
- The [historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect.mjs)
500
- and observations are retained in Git history, outside the current checkout and
501
- npm package. The probe deployed a disposable Worker containing only synthetic
502
- data, verified the served configuration, saved observations, and deleted the
503
- Worker in `finally`. It used no `waitUntil`, which would change the lifetime
504
- being measured. The production runtime build number was not exposed by the
505
- deployment; these findings are tied to the date and flags above.
506
-
507
458
  ### Production Access bypass for agent endpoints
508
459
 
509
- P0 of [#837](https://github.com/zackbart/connecta/issues/837) asked whether
510
- `/agent/*` can skip a Worker-level Cloudflare Access application, so an agent
511
- sends only its `cta_` token, while every other path keeps Access and
512
- `ctx.access`. It can. The probe ran on 2026-10-15 against a disposable Worker on
513
- its `workers.dev` hostname, with compatibility date `2026-09-01`, `cf`
514
- 1.0.0-beta.14, Wrangler 4.114.0, and curl 8.7.1 with `--path-as-is`, so raw
515
- paths reached the edge unnormalized. The Worker-level application allowed only
516
- a probe service token. Its unauthenticated refusal was therefore Access's 403
517
- page rather than a login redirect. The [observations](https://github.com/zackbart/connecta/blob/main/scripts/probes/access-agent-bypass-2026-10-15.json)
518
- record three identical rounds after each configuration change had produced
519
- three consecutive matching responses.
520
-
521
- - **Precedence.** A self-hosted application whose destination is
522
- `{ "type": "public", "uri": "<host>/agent/*" }`, with one `bypass` policy
523
- including `everyone`, overrides the Worker-level application on matching
524
- paths only. Unauthenticated requests reach the Worker there. `/mcp`, `/`,
525
- and every unmatched path still get the Access refusal. A service token is
526
- still admitted on them with `ctx.access` from the Worker-level application.
527
- This confirms Cloudflare's documented hierarchy, and that Worker-level
528
- protection still covers the `workers.dev` hostname.
529
- - **`ctx.access` on a bypassed request is present, not absent.** Its `aud` is
530
- nonempty and is the _path_ application's AUD, not the Worker-level one.
531
- `getIdentity()` resolved to `undefined` for every credential tested: none, a
532
- valid service token, a `cta_`-style bearer token, and a forged
533
- `Cf-Access-Jwt-Assertion`. A service token on a protected path also gave
534
- `undefined` identity, carrying the Worker-level AUD. A human
535
- `CF_Authorization` cookie on a bypassed path was not tested, so what
536
- `getIdentity()` returns there is unknown. That is one more reason the agent
537
- route ignores Access context rather than relying on its contents.
538
- - **API shape.** A Worker destination is exclusive to one application. A
539
- second application with `{ "type": "worker", "worker_id" }` plus `uri` or
540
- `path` was refused with 409 `destination belongs to another application`. The
541
- bypass must be hostname-and-path, stored with domain `<host>/agent/*`, and it
542
- covers only that exact hostname.
543
- - **Propagation.** Creating or deleting each application took 15–19 seconds to
544
- settle across runs. While a deployment or application change was propagating,
545
- the edge returned transient 500 (`error code: 1104`) and 404 (`error code:
546
- 1042` or `1043`) responses. The recorded run has one such 404, on the
547
- Worker-level-only service-token control for `/agent/x`. Deleting the path
548
- application restored the 403 on `/agent/x`.
460
+ Access uses a hostname-and-path bypass application for `/agent/*`; the
461
+ Worker-level application protects other paths. A bypassed request can still
462
+ carry `ctx.access`, so agent routes ignore that context.
549
463
 
550
464
  | Raw request path | Access outcome | Worker pathname (+ search) |
551
465
  | -------------------------- | -------------- | -------------------------- |
@@ -577,139 +491,32 @@ leading `//`, and treats `%2f` as data, not a separator. The effective Worker
577
491
  pathname, `new URL(request.url).pathname`, is the raw request path with case,
578
492
  escapes, and slashes untouched, except that dot segments arrive resolved:
579
493
  `/mcp/../agent/x` arrives as `/agent/x`. Every path that
580
- resolves outside `/agent/*`, including every `/mcp` form, stayed protected. The
494
+ resolves outside `/agent/*`, including every `/mcp` form, remains protected. The
581
495
  inverse is the risk: Access bypasses paths
582
496
  that connecta must not treat as an agent pool. Connecta's router compares raw
583
497
  pathnames without decoding, case folding, or slash collapsing, so an `/agent/`
584
- route must keep that property. It should accept exactly
585
- `/agent/<pool>`, with `<pool>` byte-equal to a declared pool name. It should
586
- refuse an empty segment, a trailing slash, any extra segment, `%`, uppercase
587
- variants, and a leading `//`, all with the same 404. It must ignore
498
+ route keeps that property. It accepts exactly
499
+ `/agent/<pool>`, with `<pool>` byte-equal to a declared pool name. It refuses
500
+ an empty segment, a trailing slash, any extra segment, `%`, uppercase
501
+ variants, and a leading `//`, all with the same 404. It ignores
588
502
  `ctx.access` entirely, since a bypassed request still carries an AUD.
589
503
  [Agent endpoints](./auth.md#agent-endpoints) implement exactly this in
590
504
  `src/routes/agent-path.ts`, and `test/agent-endpoints.test.ts` replays every
591
505
  row of the table above. The hyphenated row stands for any two-part pool name
592
- such as `<agent>-<user>`. The pool names in the recorded paths were replaced
593
- with placeholders after the run; the edge behavior for that path shape was
594
- unaffected.
595
-
596
- To configure the bypass, keep the Worker-level application described in
597
- [Cloudflare Access on Workers](./auth.md#cloudflare-access-on-workers). Then,
598
- for each hostname agents use (the `workers.dev` hostname, each Custom Domain,
599
- or route host), add a self-hosted Access application. In the dashboard, set
600
- domain to that hostname and path to `agent/*`, with one policy whose action is
601
- **Bypass** and whose include rule is **Everyone**. Through the API, that is:
602
-
603
- ```json
604
- {
605
- "type": "self_hosted",
606
- "name": "connecta agents",
607
- "destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
608
- "policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
609
- }
610
- ```
506
+ such as `<agent>-<user>`.
611
507
 
612
- Only the `workers.dev` hostname was probed. A Custom Domain application is the
613
- same documented hostname-and-path form, but it was not observed here. Do not
614
- rely on a fixed wait after a change. Changes settled in 15–19 seconds, yet one
615
- earlier round still saw a 403 on a bypassed path more than 30 seconds after
616
- the change. Test only after the positive and negative controls agree together
617
- for several consecutive rounds. The positive controls are bypassed paths
618
- reaching the Worker with the path AUD and a service token reaching a protected
619
- path with the Worker-level AUD; the negative controls are `/mcp` and `/`
620
- returning the Access refusal. The current script requires five such rounds
621
- after creating the bypass and three after other changes. The recorded run
622
- predates this: after the bypass it converged only the positive `/agent/x`
623
- control, then waited a fixed 90 seconds, and its negative controls were checked
624
- in the recorded rounds rather than before them.
625
-
626
- To repeat from a repository checkout, run
627
- `node scripts/probes/access-agent-bypass.mjs --profile <name> [output.json]`.
628
- `--profile` is required and has no default; every `cf` call passes it
629
- explicitly. Name the profile for the account that should host the probe; it
630
- needs
631
- Access application, policy, and service-token writes, plus Workers script
632
- writes. The script deploys and deletes the Worker with `cf`. `cf deploy` builds
633
- through Wrangler 4.136.0 or newer, so the probe installs Wrangler 4.145.0 into
634
- its temporary project. Wrangler needs no login. The script records only the
635
- count of existing Access applications, then names everything it creates
636
- `connecta-probe-<run>`. It force-deletes and verifies each resource in
637
- `finally`, then checks that the non-probe application ids are unchanged.
638
- Observations record statuses, outcome classes, pathnames, and which
639
- application's AUD matched, compared by digest inside the Worker. They contain no
640
- hostnames, account ids, tokens, cookies, or JWTs. `cf` delete commands prompt
641
- for confirmation and exit 0 without deleting when stdin is not a terminal, so
642
- the script passes `--force`. `cf deploy` printed `Deploy complete` and then
643
- kept running, so the script stops it after that line. The recorded run deployed
644
- with Wrangler 4.114.0 and used `cf` for Access. A standalone deploy and delete
645
- verified the `cf` deploy and delete path. The full probe has not been rerun on
646
- that path.
647
-
648
- Every subprocess (`cf`, `curl`, `npm`) runs through one runner. It enforces a
649
- deadline by killing the process group, keeps stdout and stderr in memory, and
650
- returns only facts: success, exit code, timeout, HTTP status, numeric API
651
- code, and an error slug from a fixed set (anything else is `unknown`). Commands
652
- that return data are parsed inside the runner against schemas that copy only
653
- whitelisted fields: ids, names, counts, booleans, and the Worker's `ctx.access`
654
- summary. Credentials stay in a holder that prints as `[secret]`. An unexpected
655
- shape, including `null` where a list belongs, is a failure, never an empty
656
- result. Errors are fixed diagnostics plus those facts. Teardown deletes every
657
- tracked id on its own. Then Access applications, service tokens, and Workers
658
- each get their own bounded discovery-and-delete pass by name, for resources
659
- whose create response was lost, so a failure in one family never skips
660
- another. Each discovered resource is deleted independently. Access policies are
661
- inline on their applications and go with them. A resource counts as gone only
662
- when a successful lookup omits it, and the final inventory verifies each family
663
- separately. Run
664
- `node scripts/probes/access-agent-bypass.mjs --self-test` to check the runner
665
- offline. It feeds canary secrets, suffixed slugs, malformed JSON, and a stalled
666
- process tree through it. It also runs teardown against a fake `cf` whose
667
- application listing always fails, after a lost token-create response, and
668
- checks that the token is still found and deleted. It also checks that a Worker
669
- that refuses deletion does not block another Worker's deletion. It runs no real
670
- `cf`, `curl`, or network command. This
671
- hardening and the joint convergence are verified offline only and have not run
672
- against Cloudflare.
508
+ For the operator procedure and Access application JSON, see
509
+ [Agent endpoints behind Cloudflare Access](./deploying.md#agent-endpoints-behind-cloudflare-access).
673
510
 
674
511
  ### Alchemy Worker deployment
675
512
 
676
- Two throwaway deployments on 2026-10-20 verified the Worker example's
677
- Alchemy path with alchemy 2.0.0-beta.83, effect 4.0.5, and Alchemy local
678
- state, then deleted every resource and confirmed by re-listing. Alchemy
679
- uploads the full Worker with `PUT /workers/scripts/<name>`, the endpoint an
680
- earlier full deploy had been suspected of losing `ctx.access` through.
681
-
682
- The first deployment ran a near copy of the stack with a temporary diagnostic
683
- route. With an Access service token in a non-identity policy, the Worker saw
684
- `ctx.access` carrying the Worker-level application's AUD and no identity, as
685
- expected for a service token. D1, the Worker Loader, R2, and secret bindings
686
- worked; `/agent/*` reached connecta through the bypass; `/ui/api/config`
687
- returned 403 `Cloudflare Access human identity required`; and
688
- `connecta doctor` passed with a `cta_` token issued inside the Worker. A code
689
- update through `alchemy deploy` kept the script tag, `ctx.access`, and D1 rows.
690
- Three consecutive probe rounds agreed.
691
-
692
- The second deployment ran the shipped `alchemy.run.ts` from the packed package
693
- in a separate project on Node 26, changing only resource names and the Access policy
694
- (a service token). The edge checks in the example's "Verify after deploying"
695
- held for three or more rounds after each deploy. `alchemy destroy` deleted the
696
- Worker and both Access applications and orphaned the retained D1 database; the
697
- next deploy adopted it by name with its rows. After deleting local state, a
698
- deploy reused the Worker with the same script tag, and `applicationId` took
699
- both Access applications over with unchanged AUDs and no duplicates. Removing
700
- `applicationId` afterwards planned an in-place update, and a further deploy
701
- planned no changes. The account API token needed only Workers Scripts Write,
702
- D1 Write, Access: Apps and Policies Write, and Access: Organizations, Identity
703
- Providers, and Groups Read.
704
-
705
513
  Four findings shape the operator rules. Alchemy local state under
706
514
  `.alchemy/state/` records secret binding values in plaintext. Alchemy sends no
707
515
  `keep_bindings`, so each deploy deletes secrets set outside the stack.
708
516
  `alchemy dev` does not provide `ctx.access` to a plain fetch Worker, so local
709
517
  development stays on `wrangler dev` and its `access.dev` identity. D1 needs
710
518
  `RemovalPolicy.retain()` so `alchemy destroy` cannot delete a deployment's
711
- state. Not verified: `Cloudflare.state()` remote state, custom domains, and
712
- taking over a Worker first deployed with Wrangler.
519
+ state.
713
520
 
714
521
  npm strips `.gitignore` from the package, so the copied Worker example's
715
522
  ignore rules ship as `examples/worker/gitignore`, renamed by the README's copy
@@ -730,15 +537,8 @@ activity table beside it. `src/storage/sql.ts` owns the Promise edges.
730
537
  Each store creates its tables on first use. `memoryStorage()` is the default
731
538
  and the test double. Workers KV and the 0.28 JSON file store are gone: KV is
732
539
  eventually consistent and cannot compare-and-set, and the file store rewrote
733
- its whole file on every write. Each has a one-shot copy into its replacement,
734
- `copyKvToD1` on `/d1` and `connecta migrate-state` on Node. Both copy live
735
- entries verbatim, because physical keys did not change, and both keep any key
736
- the target already holds. `copyKvToD1` reports counts per key family
737
- (`familyOfKey` in `src/storage/keys.ts`), never a key. It resumes through a
738
- source-bound, atomically claimed token, because Workers KV's own list cursor
739
- can spell a key. Copy under maintenance after KV stabilizes, verify hashes
740
- and expiries, and mark cutover before reopening traffic; the permanent D1
741
- marker refuses stale copies afterward.
540
+ its whole file on every write. Their one-shot copies were removed; deployments with old state must finish
541
+ conversion on 0.37.1 before upgrading. See [Retired layout migrations](./migration-retired-layouts.md).
742
542
 
743
543
  `KVStorage` is `get`/`set`/`delete`/`list(prefix)`/`compareAndSet`, all
744
544
  required, and `createConnecta` refuses storage missing one (INV-11).
@@ -762,6 +562,11 @@ read-then-write fallback: agent-token slot swaps and
762
562
  adoption, OAuth handoff ownership, single-use connect links, every downstream
763
563
  OAuth grant write and consent claim, the result stash, and initialization of the cursor signing key.
764
564
 
565
+ `set()` and mutating `compareAndSet()` statements first reclaim at most 16
566
+ expired SQL rows per call. The CAS sweep runs as a separate statement; its
567
+ affected-row count cannot change the comparison's boolean result. A
568
+ `compareAndSet(key, null, null)` only checks absence and does not sweep.
569
+
765
570
  Every key is built in `src/storage/keys.ts`, which lists each family with its
766
571
  scope, version, codec, and TTL policy. Deployed-consumer persistence journeys
767
572
  check actual owner isolation in the physical database; key probes retain NUL
@@ -773,12 +578,10 @@ Keys and list prefixes must not contain U+0000 (NUL). D1, SQLite, and memory
773
578
  storage reject them with `TypeError` before accessing storage: Node 22's
774
579
  `node:sqlite` truncates TEXT results at NUL. Builders reject NUL in unencoded
775
580
  components.
776
- State-file import validates all keys before writing. The `connecta_kv` table
777
- keeps its existing TEXT keys, including compatibility with the 0.28 schema.
778
- Storage writes no log lines. A refused import names the file and an entry's
779
- position, never a key, a value, or the JSON parser's account, which quotes
780
- the file (INV-6). Database failures report only a fixed import step and an
781
- entry's position, including errors raised by target-database triggers.
581
+ The `connecta_kv` table keeps its existing TEXT keys, including compatibility
582
+ with the 0.28 schema. Storage writes no log lines. JSON state-file import is
583
+ retired; deployments needing it must use the 0.37.1 bridge release before
584
+ upgrading. See [Retired layout migrations](./migration-retired-layouts.md).
782
585
 
783
586
  Remote MCP catalogs use the SDK `ResponseCacheStore` over this same SQL KV.
784
587
  The `response-cache:v1:` family binds connector id, a hash of its configured
@@ -868,6 +671,16 @@ that charge until its deadline. Booking retries recognize their own reservation
868
671
  ID and reuse its stored deadline, including a final read after an ambiguous
869
672
  last CAS. A lost swap or transient storage error backs off and re-reads, up to
870
673
  32 attempts. A full ledger or unconfirmed exhausted booking returns no result id.
674
+ The entire stash operation, including reservation reads, has a 45-second
675
+ outer budget. Invocation result processing shares the call's remaining deadline
676
+ and request cancellation, so shorter call or request limits stop stash waits
677
+ sooner. Program paging runs in that same scope; a stalled storage promise cannot
678
+ retain its executor permit after cancellation. Late storage responses start no
679
+ further stash work.
680
+ Final program-return stashing composes through its registered Effect program
681
+ after the executor scope closes, under the caller's cancellation rather than
682
+ the closed run's signal. Cancellation settles the handler while preserving
683
+ completed-write accounting; it cannot resume stashing after a held read returns.
871
684
 
872
685
  Caller time only nominates charges for pruning. Before discarding one, the
873
686
  ledger writes a probe with that charge's absolute deadline and reads it back.
@@ -949,14 +762,37 @@ Basic form components so a raw echo is covered too. Public client IDs remain
949
762
  visible. Redaction replaces these values and their auth prefixes, mixed JSON
950
763
  escapes, URL encodings, and base64/base64url forms with `[redacted]`.
951
764
 
952
- Every redaction is one pass over a text projection of the whole value: string
765
+ Every redaction builds one text projection of the whole value: string
953
766
  and number leaves in serialization order, nested JSON strings parsed into their
954
767
  own leaves, and errors' own fields (non-enumerable diagnostics and causes
955
- included). The joined projection is matched once, raw and through each JSON
768
+ included). The joined projection is matched raw and through each JSON
956
769
  decoding layer, so a form split across adjacent leaves is found; each match is
957
770
  replaced only in the leaves it touches, so public runs, links, annotations and
958
- container shapes stay. The walk reads every field once, from its descriptor, and
959
- returns what it checked. A caller's container stands only as verified plain
771
+ container shapes stay. Touched numbers are first checked together with non-number
772
+ pieces already replaced, so a number stays numeric when replacing a neighbouring
773
+ string breaks the match. Every replacement is then checked in the lane as it
774
+ will be returned: partial string replacements, whole numbers, typed binary,
775
+ decoded binary, whole-field private matches and unchecked runs included. A
776
+ withheld error number contributes empty text, matching its `undefined` field on
777
+ the error copy. An unregistered error name is one whole-withheld group, even
778
+ when its text was parsed into several JSON leaves: any changed leaf collapses
779
+ that group to one placeholder before settling. JSON strings withheld for a
780
+ match spanning tokens, and scalars withheld while walking, contribute their
781
+ actual placeholders instead of their discarded contents.
782
+ A newly formed match reaching a kept piece withholds that entire part, and the
783
+ lane is checked again until nothing changes. This includes matches formed from
784
+ `[redacted]` text and a kept neighbour, within one string or across leaves;
785
+ a span wholly inside a placeholder remains the placeholder. Each changing
786
+ round withholds at least one more part, so there are at most as many changing
787
+ rounds as parts. Every round, including its bookkeeping, shares the call's work
788
+ budget. An unaffordable leaf or seam check withholds every participating part,
789
+ including neighbours untouched by the original scan and any originally touched
790
+ numbers awaiting retention. A marker that could not itself be scanned still
791
+ participates in its seam windows. Unchanged lanes pay for no recheck.
792
+ Joined and leaf-by-leaf matching follow the same rule; large lanes recheck
793
+ changed parts and the seams touching them without rereading unchanged leaf
794
+ interiors. Bounded lanes and deep escapes withhold changed numbers whole.
795
+ The walk reads every field once, from its descriptor, and returns what it checked. A caller's container stands only as verified plain
960
796
  data: prototype `Object.prototype`, `Array.prototype` or `null`, no `toJSON` on
961
797
  its chain, no symbol-keyed field, every field a data property it read (an
962
798
  array's holes read through its prototypes), and nothing in it changed. Anything
@@ -969,8 +805,12 @@ as plain data; an array that is not plain as a plain array of its indices; a
969
805
  `String`, `Number` or `Boolean` wrapper as its primitive; a `toJSON` result,
970
806
  called once and checked; a function or symbol as the placeholder; a typed array,
971
807
  `DataView` or `ArrayBuffer` as a fresh `Uint8Array` of its bytes, which are
972
- checked as bytes and, with the elements it held, as the numbers JSON shows for
973
- them. The type table is in [auth.md](./auth.md). An own accessor is
808
+ checked as bytes on their own. Its returned byte numbers join neighbouring
809
+ leaves directly in one projection; non-byte typed arrays' original element
810
+ numbers substitute for those bytes in a separate projection with the same
811
+ neighbours. Neither numeric view interrupts the other. Each binary is one
812
+ whole-withheld part, so a match immediately exposes its placeholder to the lane
813
+ check. The type table is in [auth.md](./auth.md). An own accessor is
974
814
  never invoked: an object's copy leaves it out, an array index holds the
975
815
  placeholder. A change copies the containers on its path on first write, so a
976
816
  caller's value is never mutated. An array's non-index fields are never read:
@@ -997,10 +837,18 @@ Connecta mints and authenticates are kept whole: they hold only sealed state or
997
837
  Connecta's own facts. Serialized JSON whose keys, syntax and leaves together
998
838
  hold a run of a form that no single leaf holds is withheld whole.
999
839
 
1000
- The guarantee: **no caller-visible text holds a registered form whole, nor any
840
+ Settling covers this joined projection, including whole replacement markers;
841
+ object keys, JSON punctuation and fixed framing do not join that lane. Keys
842
+ are checked alone, and serialized token-spanning matches follow the separate
843
+ whole-JSON rule above.
844
+
845
+ The guarantee for these checked text views and projections: **no caller-visible
846
+ text holds a registered form whole, nor any
1001
847
  run of K = 8 consecutive UTF-16 units of a registered form of a value at least K
1002
848
  long, in the raw text or any JSON-decoded layer; text cut where the cut can end
1003
- inside a form loses that unfinished tail, however short.** Registered forms are
849
+ inside a form loses that unfinished tail, however short.** Runs formed from
850
+ placeholder text and kept text or numbers are covered too; the placeholder
851
+ itself is reserved replacement text. Registered forms are
1004
852
  the raw value, its URL/form encodings (either percent hex case), base64 and
1005
853
  base64url, and the `Bearer `/`token ` framings. The framing words are public, so
1006
854
  no indexed run starts inside them; a run reaching K units of the value is
@@ -1022,7 +870,12 @@ result and errors, the invocation's raw reply, executor outcomes and logs,
1022
870
  emits, and resource and skill reads. The agent boundary (`redactAgentOutput`,
1023
871
  every meta-tool exit and the MCP route) repeats every check except short
1024
872
  private values, which would rewrite Connecta's own ids, counts and addresses;
1025
- payload passes already removed them. Identifiers sent beside a credential — a
873
+ payload passes already removed them. Each `execute_code` response crosses one
874
+ operation-boundary envelope pass after write accounting, including success,
875
+ failure, budget refusal, cancellation and oversized-code intake refusal. The
876
+ pass includes emits and diagnostics. Connecta attaches its own integer
877
+ `hostCalls` counters afterward. The MCP route still checks the serialized HTTP
878
+ response separately. Identifiers sent beside a credential — a
1026
879
  confidential client's ID, a Basic username beside a password of at least eight
1027
880
  characters, and credential-slot fields named as identifiers (`…Id`, `user`,
1028
881
  `username`, `login`, `email`, `public…`) — are matched whole only: consent URLs
@@ -1148,6 +1001,14 @@ The matcher is cached until its set changes; the empty set has a fast path.
1148
1001
  including errors, and every host-to-guest value or rejection passes through
1149
1002
  it. Both MCP transports also pass their serialized response through it, covering
1150
1003
  JSON-RPC errors, HTTP diagnostics, structured content, and paging responses.
1004
+ After fully reading an SDK response body, an idle request set (no registered
1005
+ forms or unregistered values) returns the original text without parsing or
1006
+ reserializing it, so clients receive exactly the bytes the SDK serialized. Only
1007
+ an active set reparses the body, which also canonicalizes numbers a handler
1008
+ emitted through `JSON.rawJSON` (an out-of-range value becomes `null`, digits past
1009
+ double precision are rounded); clients must not rely on either behaviour. A set
1010
+ activated before the read completes still applies structured redaction, with
1011
+ the text fallback for non-JSON diagnostics.
1151
1012
  Per-call redaction remains before diagnostic truncation, paging, and emits. Paging stores only request-redacted text before encoding
1152
1013
  chunks, so a later request needs no original credentials. Programs retain the
1153
1014
  request set only for the run so later outputs cannot reconstruct an echo.
@@ -1190,9 +1051,8 @@ parse as text, so connecta reads downstream bodies as bytes
1190
1051
  (`src/byte-read-response.ts`), `ctx.oauth.fetch()` answers included. The
1191
1052
  operator page may show catalog metadata the operator loaded (descriptions,
1192
1053
  schemas), but withholds a tool name outside the grammar as it does in records.
1193
- `test/operator-record-sources.node.test.ts` is a secondary lint over every other
1194
- log call in `src/`. Fix the sink, not the source: a filter at each source missed
1195
- the next one. Recorded HTTP status facts survive the bare write-401 fallback,
1054
+ Fix the sink, not the source: a filter at each source missed the next one.
1055
+ Recorded HTTP status facts survive the bare write-401 fallback,
1196
1056
  Google's uncertain-write wording, and oversized-response mapping; see
1197
1057
  [Read failure records](./operating.md#read-failure-records) for their operator
1198
1058
  meaning. These facts do not change agent error fields or write outcomes.
@@ -1363,9 +1223,7 @@ acknowledgement, then reconciles vendor state with exactly one physical send.
1363
1223
  Focused provider suites retain MIME/draft restrictions, revision conflicts,
1364
1224
  CCB routing, Planning Center product exclusions and deployment pins. Native
1365
1225
  HTTP framing, cursor and request-lifetime fault probes remain in the HTTP suites.
1366
- The catalog-wide status sweep and its argument roster, catalog/ranking copies
1367
- and unused paging strategy tests are retired under #1072. Provider workflows
1368
- belong in the existing shared outcomes; a new tool requires no failure-matrix row.
1226
+ Provider workflows belong in the existing shared outcomes.
1369
1227
  No legacy REST tool adapter remains.
1370
1228
  Large documents opt into generator options that drop restated operation ids
1371
1229
  and plain path parameters and cap one operation's details (`opBudget`).
@@ -1639,8 +1497,7 @@ path, or fields a schema leaves undeclared; such paths still apply at
1639
1497
  runtime).
1640
1498
 
1641
1499
  `test/fixtures/value-safety.ts` is the shared harness each vendor's
1642
- `provider.safety.node.test.ts` runs; `reviewProblems` is pure, and
1643
- `test/value-safety-harness.node.test.ts` proves wrong paths fail it. It fails
1500
+ `provider.safety.node.test.ts` runs; `reviewProblems` is pure. It fails
1644
1501
  on candidates from another pin, format, or option set; an unreviewed
1645
1502
  candidate; a verdict for an operation the index lacks; a table path the stamp
1646
1503
  lacks (unresolved, or edited since `providers:spec`); a flagged field no path,
@@ -1668,12 +1525,6 @@ and reports only added, removed and changed counts in the caller's partition.
1668
1525
 
1669
1526
  #### Behavioral evidence (contributor)
1670
1527
 
1671
- The characterization snapshot engine, provider JSON snapshots, source scheduler
1672
- and its self-tests are retired under
1673
- [#1072](https://github.com/zackbart/connecta/issues/1072). Provider changes no
1674
- longer require golden enrollment, snapshot updates, byte equivalence or nightly
1675
- capture determinism. Production invariants remain unchanged.
1676
-
1677
1528
  Use the existing stateful fixture vendors and published-edge deployment adapters:
1678
1529
 
1679
1530
  - `pnpm run eval:outcomes` drives representative joins, retained paging, endpoint
@@ -1701,14 +1552,6 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1701
1552
  returned private values and credential echoes; positive record facts must
1702
1553
  still arrive. An explicitly writable pool also exercises reopening a
1703
1554
  read-only deployment without bypassing connector policy.
1704
- - `test/cancellation-journey.node.test.ts` observes a queued client's departure
1705
- without another vendor send, and an active client's departure after the
1706
- vendor commits. The vendor must see its held response socket close before
1707
- releasing it; a later MCP read recovers the committed intent exactly once.
1708
- `test/cancellation-faults.test.ts` keeps signal-less token-resolution,
1709
- warmed-credential digest, stalled body-cleanup and abort/grant races on Node
1710
- and workerd. These replace the universal Promise-method/seam/owner matrix
1711
- and its enrollment self-test, plus migration-only runtime wrapper checks.
1712
1555
  - `test/browser/operator-flows.spec.ts` serves the actual operator shell and
1713
1556
  routes over TCP with file-backed SQLite. Sign-in, Settings provenance, OAuth
1714
1557
  connect/disconnect and credential save/test/replace/remove survive closing and
@@ -1742,17 +1585,10 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1742
1585
  after HTML, malformed JSON and oversized acknowledgements with one physical
1743
1586
  send. It distinguishes Stripe's reviewed pre-execution 401 refusal, and checks
1744
1587
  read failure facts, secrecy and recovery through direct and QuickJS calls.
1745
- `test/provider-failure-sweep.test.ts` retains only concrete auxiliary, composite,
1746
- in-flight and untracked-call fault probes; no provider/tool argument roster or
1747
- catalog/finalizer agreement sweep remains. Existing
1748
- `test/sent-secrets.test.ts`, `test/sent-secret-output-boundary.test.ts`,
1749
- `test/request-secret-redaction.test.ts` and `test/catalog-secret-redaction.test.ts`
1750
- cover secrecy before projection, truncation, caching, paging and diagnostics.
1751
- - `test/write-evidence.node.test.ts`, `test/remote-mcp-attempts.node.test.ts` and
1752
- `test/oauth-credential-attempts.node.test.ts` inspect physical socket sends.
1753
- Native transport parity, OAuth and write-provenance suites retain their
1754
- workerd coverage. The shared throwaway RSA key in `test/fixtures/rsa.ts` is
1755
- used by write-provenance and unknown-write-advice fixtures.
1588
+ Existing `test/sent-secrets.test.ts` covers secrecy before projection,
1589
+ truncation, caching, paging and diagnostics.
1590
+ - The shared throwaway RSA key in `test/fixtures/rsa.ts` is used by
1591
+ write-provenance and unknown-write-advice fixtures.
1756
1592
  - `test/executor-requests.test.ts` runs overlapping `execute_code` programs
1757
1593
  as separate workerd requests through the real Worker Loader executor, with
1758
1594
  real TCP host calls that read their own request's signal. Queued programs
@@ -1767,23 +1603,21 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1767
1603
  `test/request-context.node.test.ts` pins the same wake order and async context
1768
1604
  on Node for executor and call admission and QuickJS host calls, and gives back
1769
1605
  grants that shutdown overtook.
1770
- - The request, catalog, auxiliary-call and renewal lifetime
1771
- suites, plus `test/auth-admission-binding.test.ts`, exercise cancellation,
1772
- held native work and garbage collection. These test actual request ownership;
1773
- the retired capture scheduler's source guards were only harness constraints.
1606
+ - `test/request-context.node.test.ts` and `test/auth-admission-binding.test.ts`
1607
+ exercise cancellation, held native work and garbage collection. These test
1608
+ actual request ownership.
1774
1609
 
1775
1610
  The remaining probes defend distinct reachable failures: auth admission and
1776
1611
  native scheduler ownership, grant/callback fencing and refresh coordination,
1777
1612
  SDK replay/redirect/SSE attempts, nested write provenance and refusal evidence,
1778
1613
  unawaited program drain and failed-program accounting, catalog drift and paging
1779
1614
  revocation, and hostile serializers, structural echoes, truncation and persisted
1780
- pages. Broad scripted-executor policy/privacy cross-products and repeated
1781
- post-401 result/error-shape tables are retired. Protocol claims link executable
1615
+ pages. Protocol claims link executable
1782
1616
  evidence files in `spec/coverage.json`; the invariant contracts above are defended
1783
1617
  by the actual behavior assertions in these journeys and focused probes.
1784
1618
  The required CI gate includes build/declarations, Node and Workers probes,
1785
1619
  outcomes, browser flows, installed consumers and Docker, audits and real Eve
1786
- compatibility. Further contributor cleanup is tracked in #1072.
1620
+ compatibility.
1787
1621
 
1788
1622
  Add a compact fault journey only for a meaningful outcome those suites cannot
1789
1623
  observe. Reuse their fixtures and adapters; do not reintroduce a snapshot
@@ -1811,8 +1645,7 @@ behind the same redirect wrapper and a same-origin guard.
1811
1645
  client replacement, transport confinement and secrecy on Node, and a real
1812
1646
  service binding on workerd.
1813
1647
  `test/google-service-account-token.test.ts` exercises signing/cache behavior on
1814
- Node and workerd; `test/google-service-account-remote.node.test.ts` checks
1815
- real-socket token/MCP requests, invalidation and one physical attempt per write.
1648
+ Node.
1816
1649
  Operator setup lives in [Google hosted MCP](./auth.md#google-service-account-tokens-and-hosted-mcp).
1817
1650
 
1818
1651
  `createConnecta` takes closed typed `ui`, `vault`, and `activity`
@@ -2093,10 +1926,8 @@ request fixture and native-output suites supply runtime parity evidence.
2093
1926
 
2094
1927
  ### Native SQL storage (contributor)
2095
1928
 
2096
- `src/storage/sql-native.ts` composes KV, activity schema upgrades, retention,
2097
- and bounded SQL copies as Effects. `src/storage/d1-copy.ts` composes the
2098
- Workers KV copy, source-bound resume claims, verification, cutover guard, and
2099
- recovery. Public factories and copy functions retain their Promise contracts;
1929
+ `src/storage/sql-native.ts` composes KV, activity schema upgrades, and retention
1930
+ as Effects. Public factories retain their Promise contracts;
2100
1931
  custom Promise SQL drivers retain the fallback lift.
2101
1932
 
2102
1933
  `src/storage/sql-driver.ts` constructs a public Effect 4.0.5 `SqlClient`
@@ -2115,17 +1946,8 @@ is the existing exact `effect` pin. Connecta does not adopt `@effect/sql-d1`:
2115
1946
  its 4.0.5 batch API drops metadata and its binding contract is wider than
2116
1947
  Connecta's published structural interface.
2117
1948
 
2118
- The D.2 architecture allowance was measured against `0e314247` using cached
2119
- official `node:22-slim` (Node 22.23.4, zlib 1.3.1-e00f703), no network, and
2120
- read-only bundle artifacts: `/d1` grew from 62,516 to 88,305 bytes gzip
2121
- (+25,789); the Worker example grew from 729,847 to 756,375 (+26,528).
2122
- Final typed-driver failure normalization measures 88,319 and 756,377 bytes
2123
- on that same official runtime. Executing real services and statement operations requires those runtime
2124
- semantics; the earlier identity-compiler facade did not provide them. The
2125
- explicit allowance changes only `/d1`'s cap from 62,542 to 90,000 bytes and
2126
- the Worker example's cap from 743,000 to 760,000. Historical baselines and
2127
- all other caps stay fixed. Final integration must remeasure the combined
2128
- implementation and satisfy the same compatibility and privacy contracts.
1949
+ Bundle caps and baselines are authoritative in
1950
+ [`scripts/bundle-budget.json`](https://github.com/zackbart/connecta/blob/main/scripts/bundle-budget.json).
2129
1951
 
2130
1952
  The platform connection capability contains no request state. D1 prepares in
2131
1953
  the current request and retains no prepared handles or promises. SQLite keeps
@@ -2152,14 +1974,11 @@ value, matching that fallback. Interruption remains interruption. The lookup
2152
1974
  module imports no SQL or platform implementation and does not change the
2153
1975
  published storage interfaces.
2154
1976
 
2155
- The synchronous state-file parser, validation, file read, and one atomic
2156
- SQLite import remain native synchronous leaves because `importStateFile` is a
2157
- published synchronous API. `openSqlite` retains file modes, WAL, busy timeout,
1977
+ `openSqlite` retains file modes, WAL, busy timeout,
2158
1978
  and database ownership. `test/sql-effect-contract.ts` retains affected-row,
2159
1979
  50-claim, typed-failure, cancellation, and tracing probes on SQLite and native
2160
- D1. `test/kv-to-d1.test.ts` also reuses stores across separate workerd requests;
2161
- the existing SQL contracts prove rollback, database-clock TTL, encodings,
2162
- activity upgrades, and copy recovery.
1980
+ D1. The existing SQL contracts prove rollback, database-clock TTL, encodings,
1981
+ and activity upgrades.
2163
1982
 
2164
1983
  ### Internal provider HTTP failures (contributor)
2165
1984
 
@@ -2200,7 +2019,7 @@ Stripe declares HTTP 409 as a conflict, alongside its fact-driven 429
2200
2019
  classification to an oversized 424 reply, preserving the dependency-failure
2201
2020
  meaning when the bounded body cannot be read. The original ceiling diagnostic
2202
2021
  and answered status remain; the core still decides write outcomes. It does not
2203
- apply to 401 or alter Q8. A fixed read-phase fact distinguishes failed body
2022
+ apply to 401 or change write-outcome settlement. A fixed read-phase fact distinguishes failed body
2204
2023
  reads from malformed JSON only for vendors declaring that wording; raw
2205
2024
  exceptions never enter diagnostics. Other providers retain their mapping and
2206
2025
  wording.
@@ -2217,14 +2036,13 @@ The mapping records each request's own served provenance and raw 401 facts.
2217
2036
  reservation's answer even for a read-classified tool issuing a physical write.
2218
2037
  An accepted write's blocked retry retains its original HTTP status; a slot
2219
2038
  occupied by an auxiliary read instead names the separate write step needed.
2220
- Structured resend advice reaches output only when the core permits it. Generic
2221
- unknown writes retain the core's diagnostic wait; Q8 removes it. The mapping
2222
- adds no Promise entry or lifetime owner. `provider-failure-sweep.test.ts`,
2223
- `guarded-attempts.node.test.ts`, `call-services.test.ts` and the native
2039
+ Structured resend advice reaches output only when the core permits it. Unknown
2040
+ write outcomes are never retryable; a generic one keeps the core's diagnostic
2041
+ `retryAfterMs`, and one settled by a 401 drops it. The mapping
2042
+ adds no Promise entry or lifetime owner. `call-services.test.ts` and the native
2224
2043
  transport suites exercise these contracts through invocation, physical sockets
2225
2044
  and request closure. `http-kit.test.ts` retains focused numeric/text fact,
2226
- request-equivalence and bulk-capacity fault probes. The migration-era
2227
- kit/core verdict comparison is retired under #1072. Maintained API providers use the kit through their
2045
+ request-equivalence and bulk-capacity fault probes. Maintained API providers use the kit through their
2228
2046
  explicit provider subpaths; it stays off the root import graph and published
2229
2047
  declarations expose Promises.
2230
2048
 
@@ -2235,8 +2053,7 @@ declarations expose Promises.
2235
2053
  are Promises. A connector is called with `Effect.tryPromise`, never asked to
2236
2054
  return an Effect.
2237
2055
  - **Inbound authorization.** `authorize` in `src/routes/shared.ts` ships in
2238
- the declarations and in the `./activity` bundle; converting it cost 32 KB
2239
- gzip there for nothing a caller could see.
2056
+ the declarations and in the `./activity` bundle.
2240
2057
  - **Pure synchronous code.** Ranking, schema rendering, classification,
2241
2058
  `validateToolInput`, the host-call budget, the emit collector, failure
2242
2059
  framing, and result shaping. The microtask scheduler never yields to I/O, so
@@ -2996,17 +2813,9 @@ including structural lookalikes and genuine router errors, retain their identity
2996
2813
 
2997
2814
  Four public `effect/http/*` modules are allowed only in the private dispatcher;
2998
2815
  all other root-purity and optional-implementation boundaries remain enforced.
2999
- Direct public module imports avoid the area's barrel cost. The local D3
3000
- measurement over the reviewed D1/D2 stack is root 373,435 to 393,470 bytes gzip
3001
- and Worker 758,274 to 774,384 (official Node 22.23.4/zlib 1.3.1-e00f703, identical
3002
- repository bundle options). The barrel alternative measures 423,348/787,614.
3003
- The parent accepted this measured native-routing cost with a Worker cap of
3004
- 785,000 bytes, including room for separately measured native-auth and reviewed
3005
- host/transport additions. Root and all other caps and historical baselines are
3006
- unchanged by D3. Separate-input costs are not a combined gzip measurement;
3007
- the integrated deployment must be measured again.
3008
-
3009
- `test/server.test.ts` asserts production routing directly on Node and workerd:
2816
+ Direct public module imports avoid the area's barrel cost.
2817
+
2818
+ `test/server.test.ts` asserts production routing directly on Node:
3010
2819
  raw prefixes and malformed suffixes, extension methods, CIMD before auth/UI/body
3011
2820
  reads, Response/error identity, unmatched fallthrough, concurrent request
3012
2821
  isolation, and body-held permits through end/failure/cancel/abort/abandonment.
@@ -3018,85 +2827,20 @@ Root purity, declaration and bundle checks own import boundaries and size caps.
3018
2827
  The frozen legacy dispatcher, source transforms, diagnostic latency loops and
3019
2828
  rejected Effect MCP adapters were migration evidence; their historical source
3020
2829
  is linked from decision 0016 rather than rebuilt by ordinary tests.
3021
- The upward MCP protocol leaves remain SDK implementations, with D7's separate
3022
- native-orchestration work and compatibility evidence unchanged.
3023
-
3024
- ### Why not Schema or HttpApi for core routes
3025
-
3026
- Both were measured and both lost. `effect` is imported through its barrel,
3027
- which defeats tree-shaking: the first conversion step measured about +20 ms
3028
- of Worker cold start for it, and that cost was accepted over deep imports at
3029
- every call site. The catch is that a module the barrel reaches is paid for in
3030
- full, however little of it is used. Effect Schema for config validation and the
3031
- meta-tool inputs came to about +62 KB gzip and +13 ms of cold start in the
3032
- root, for a validator no clearer than the hand-written schema in `src/config.ts`; the
3033
- meta-tool inputs stay zod, which the MCP SDK bundles regardless. `HttpApi` for
3034
- the operator and activity routes cost about +100 KB gzip on `./ui` and +130 KB
3035
- on `./activity`, and matching the wire format meant opting out of most of what
3036
- it does: unowned paths fall through rather than 404, a wrong method is a JSON
3037
- 405, and the content-type and size checks run before a body is read. The root
3038
- entry imports `effect` and the four public HTTP modules confined to the private
3039
- dispatcher (`test/purity.node.test.ts`).
3040
- Effect 4.x is a long-term-support line, but its area barrels `effect/http`,
3041
- `effect/http-api`, `effect/schema`, and `effect/sql` are still tagged
3042
- `@stability unstable` and can change within it; only core modules such as
3043
- `effect/Schema` are stable. That is why Effect is pinned to one exact version
3044
- and every Effect upgrade is its own deliberate pull request. A deployment
3045
- that also depends on Effect, such as the Worker example's Alchemy stack, must pin the
3046
- same version to resolve a single copy. Connecta's own lockfile and packed
3047
- install are checked for one copy (`test/package-surface.node.test.ts` and
3048
- `scripts/check-package.mjs`).
3049
-
3050
- The stable `4.0.0` release was re-evaluated on 2026-10-01 with this
3051
- [historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation.mjs),
3052
- retained in Git history rather than the current checkout.
3053
- The [raw observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation-2026-10-01.json)
3054
- are synthetic Node requests and neutral esbuild bundles, not live-client or
3055
- workerd lifetime verification. Bundle figures add representative prototypes
3056
- alongside the current code; they do not claim savings from removing old code.
3057
-
3058
- Two native input schemas with validation and JSON Schema rendering add 62,148
3059
- bytes gzip to the root, taking it from 303,083 to 365,231 against a 339,526
3060
- cap. Effect can reject excess properties when both decoding and rendering use
3061
- `onExcessProperty: "error"`; its default strips them. The generated schemas
3062
- still differ from the published meta-tool schemas, including record rendering and
3063
- composed numeric bounds. A Schema replacement would need to preserve that
3064
- contract and memoized rendering. Both MCP SDK packages still depend on Zod,
3065
- so changing our inputs alone does not remove Zod from an installation.
3066
-
3067
- A one-endpoint `HttpApi` prototype adds 106,634 bytes gzip to `./ui` and 106,453
3068
- to `./activity`. Even plain `HttpRouter` adds 45,324 to `./ui`, taking it to
3069
- 130,023 against a 128,983 cap. Both default handlers return an empty 404 for a
3070
- wrong method and an unowned path; our routes need a JSON 405 for the former
3071
- and module fallthrough for the latter. Their `toWebHandler` also builds its
3072
- layer immediately and owns a runner, so adopting it would require proving
3073
- global-scope safety and preserving our scheduler and response-lifetime rules.
3074
- Those historical defaults are not the production adapter: the narrow public
3075
- HttpRouter dispatch above preserves explicit method refusals, unowned fallthrough
3076
- and the existing edge runner without `toWebHandler`.
3077
-
3078
- Effect's MCP server has a separate compatibility gate. Its `2026-07-28`
3079
- adapter accepted a stateless `tools/list`, but its `2025-06-18` adapter refused
3080
- a fresh request with 400. Initialization issued a session, and that session's
3081
- next request returned 404 on a fresh handler; Connecta served the same fresh
3082
- legacy list with 200 JSON and no session. The stable package exports no MCP
3083
- client, so it also cannot replace our downstream SDK and OAuth implementation.
3084
- [Issue #622](https://github.com/zackbart/connecta/issues/622) records the server
3085
- parity requirements. These historical measurements supported retaining MCP protocol and validation
3086
- implementations. Production HTTP dispatch now uses the separately proven narrow
3087
- public adapter above; stable v4 alone did not establish its compatibility.
3088
-
3089
- The core's cost is recorded rather than guessed. Against 0.24.4 the root entry
3090
- grew from 235,346 to 279,526 bytes gzip, the Worker example from 263,949 to
3091
- 314,336, and each provider by about 36 KB, since each reaches Effect through
3092
- `remoteMcp()`. `pnpm run check:bundle` caps the deployed shapes (the root,
3093
- `./worker`, `./ui`, the operator UI assets, `./d1`, `./auth/access-tokens`,
3094
- the Worker example, and the other non-provider entries) against
3095
- `scripts/bundle-budget.json`. Per-provider entries are reported with their
3096
- size and change from baseline but not capped: every deployment already imports
3097
- the root, which carries the Effect core, so a provider's standalone bundle
3098
- counts that core a second time. An install carries about 53 MB of `effect`, with
3099
- no dependencies of its own.
2830
+ The upward MCP protocol leaves remain SDK implementations with native
2831
+ orchestration and compatibility evidence.
2832
+
2833
+ ### Validation and HTTP boundaries
2834
+
2835
+ Config validation uses `src/config.ts`; meta-tool inputs use Zod, which the MCP
2836
+ SDK also depends on. Core routing uses the narrow public HTTP adapter above.
2837
+ Effect is pinned exactly; deployments using Effect must resolve the same version.
2838
+ `test/package-surface.node.test.ts` and `scripts/check-package.mjs` check one copy.
2839
+ Per-provider bundles are reported, not capped, because their standalone graphs
2840
+ include the shared core again. Deployed non-provider shapes are capped by
2841
+ `scripts/bundle-budget.json`.
2842
+
2843
+ [Decision 0019](https://github.com/zackbart/connecta/blob/main/decisions/0019-core-validation-http-boundaries.md) records the rejected Schema/HttpApi measurements.
3100
2844
 
3101
2845
  ## Where else to look
3102
2846
 
@@ -3150,9 +2894,7 @@ compiling and configuring the real thing.
3150
2894
  accounting and claim ownership keep their existing ordering. Late
3151
2895
  rejections are observed; dispatch and lease guards still contain detached
3152
2896
  work. Per-await I/O cancellation remains useful for releasing resources,
3153
- while prompt refusal is enforced at the boundary
3154
- (`test/cancellation-faults.test.ts`, `test/operation-cancellation.test.ts`
3155
- and `test/cancellation-journey.node.test.ts`). Invocation owns
2897
+ while prompt refusal is enforced at the boundary. Invocation owns
3156
2898
  the recovery and logical-call records it reads after teardown. Closing an
3157
2899
  outer call also revokes nested calls paused in preparation. Request-owned
3158
2900
  MCP sessions and token flights outlive individual calls and close with the
@@ -3169,11 +2911,9 @@ compiling and configuring the real thing.
3169
2911
  transfers preparation, token exchange and fenced rotation commit to the
3170
2912
  storage coordinator under its own deadlines and deferred-work hook; only
3171
2913
  the caller's wait still closes with the call. That completion
3172
- cannot resume a closed call or resend its resource request
3173
- (`test/call-renewal-lifetime.test.ts`,
3174
- `test/remote-mcp-renewal-lifetime.test.ts`). Explicit request revocation also
3175
- aborts pending authorization-code exchange and refuses late token writes
3176
- (`test/operation-cancellation.test.ts`). Request closure
2914
+ cannot resume a closed call or resend its resource request. Explicit request
2915
+ revocation also aborts pending authorization-code exchange and refuses late
2916
+ token writes. Request closure
3177
2917
  revokes views and calls first, then runs every finalizer in reverse order,
3178
2918
  collecting failures. The caller's original cancellation signal is retained
3179
2919
  separately from the lease signal, so normal teardown never looks like caller
@@ -3247,13 +2987,13 @@ compiling and configuring the real thing.
3247
2987
  as a `manual` check in its `drift.json`): an API v1 answer carrying Stripe's
3248
2988
  `Request-Id`. The real HTTP hostile-response journey checks this refusal
3249
2989
  against an ambiguous committed write; there is no copied declaration list.
3250
- Q8 is decided in one place, `settleWrite` in
2990
+ Write outcomes are decided in one place, `settleWrite` in
3251
2991
  `src/write-outcome.ts`, once per logical call and over every request it
3252
2992
  recorded. The invocation calls it for every failure of a served write: a
3253
2993
  thrown or mapped error, a custom connector's, a program's call, and its
3254
2994
  own deadline or the caller's cancellation, when no connector failure
3255
2995
  arrives at all. It reads the logical call's requests
3256
- (`logicalCallRequests`), which hold every resource request dispatched
2996
+ (`LogicalLedger`'s `sent`), which hold every resource request dispatched
3257
2997
  under the call from any ledger: nested `api()` calls, remote MCP, guarded
3258
2998
  transports, `ctx.fetch` and the kit. Without an invocation (a direct call,
3259
2999
  or a context no invocation opened), only the outermost boundary calls it,
@@ -3322,9 +3062,8 @@ compiling and configuring the real thing.
3322
3062
  Remote MCP captures
3323
3063
  each physical read's entry before awaiting its response; a later send
3324
3064
  cannot overwrite it. The kit's response identity and Google's init identity
3325
- identify the request whose credential was renewed. The legacy guarded-fetch
3326
- `answeredRequest` helper has no production caller; tests retain it to exercise
3327
- renewal provenance outside the kit.
3065
+ identify the request whose credential was renewed. Guarded mappers that throw
3066
+ an authorization refusal identify its request through `refusalRequest(error)`.
3328
3067
  The decider normalizes every failure it settles,
3329
3068
  typed or not (a raw network error, an `AbortError`, a thrown string). Its
3330
3069
  verdict is final (`settledWrite`, which follows copies and redaction): the
@@ -3498,23 +3237,8 @@ compiling and configuring the real thing.
3498
3237
  unchanged for the invocation to report.
3499
3238
  Shared provider outcomes and the real HTTP hostile-response journey check
3500
3239
  returned data, failure/recovery, privacy and physical writes without a
3501
- catalog-wide status matrix. `test/provider-failure-sweep.test.ts` retains
3502
- fault probes for Docs creation with initial text, composite success followed
3503
- by refusal, GitHub preflight failures, typed handler errors after successful
3504
- sends, Forms cursor conflicts after 200, generic-code vendor refusals,
3505
- auxiliary failures after resource dispatch and concurrent direct-call isolation.
3506
- It also holds siblings awaiting credentials or already in flight when another
3507
- write fails, refuses requests after call closure, and probes network errors,
3508
- refusal-shaped abort reasons and untracked sends. Native transport and
3509
- request-lifetime suites retain their platform-specific evidence.
3510
- `test/write-evidence.test.ts` uses real refused, reset, and held sockets on
3511
- both runtimes: a refused write is unknown and a refused read retryable,
3512
- including a write aborted with another fetch's real refusal as its reason
3513
- after the server read its body; `test/write-evidence.node.test.ts` covers a
3514
- Planning Center POST that undici resends after a 421 to a closed listener, a
3515
- DELETE that a `RetryAgent` resends after a reset, and a streamed body and a
3516
- `Blob` with an overridden `stream()` that undici partly sends before its
3517
- producer fails. Each has the server holding the write and reports unknown.
3240
+ catalog-wide status matrix. Native transport and request-lifetime suites
3241
+ retain their platform-specific evidence.
3518
3242
  Additional tests:
3519
3243
  `test/remote-mcp-write-once.test.ts` (real SDK transport),
3520
3244
  `test/api-oauth.test.ts`, `test/google-workspace-delegation.test.ts`, and each
@@ -3601,10 +3325,10 @@ OAuth retry, grant, response-decoding or ownership orchestration.
3601
3325
  there before claiming a code/refresh grant, so an unsupported Node host consumes
3602
3326
  neither. The base-fetch leaves in static OAuth and remote MCP call
3603
3327
  `sendCredentialPost`; remote registration and disconnect revocation do so too.
3604
- D4 must preserve this preflight and the distinction between request-owned code
3328
+ The adapters preserve this preflight and the distinction between request-owned code
3605
3329
  exchange and coordinator-owned refresh after claim submission. The existing
3606
3330
  spent-token algorithm intentionally remains conservative after a submitted claim,
3607
- even if a later CONNECT fails before destination dispatch. D6 must preserve the
3331
+ even if a later CONNECT fails before destination dispatch. Remote MCP preserves the
3608
3332
  SDK JSON-RPC envelope distinction at the leaf: protocol POSTs are not credential
3609
3333
  POSTs; forms and registration JSON are. Discovery GETs remain native fetch.
3610
3334
 
@@ -3692,22 +3416,6 @@ unchanged. A narrow machine check restores only those two exact adjacent
3692
3416
  pairs and requires byte equality, with no other type, export or reference
3693
3417
  change. All 88 declarations still exclude Effect and have complete references.
3694
3418
 
3695
- The superseding 2026-10-25 D3/D4 cost ruling authorizes only Worker
3696
- `maxGzip: 785000` (previously 760000) and Clerk `maxGzip: 85000` (previously
3697
- 63087). Official cached Node 22.23.4 / zlib 1.3.1-e00f703 measured Clerk at
3698
- 47,688 -> 80,961 bytes gzip (+33,273): the generated SDK is unchanged, while
3699
- native Effect adds 91.8 KB raw plus the native helper and small auth-source
3700
- growth. The isolated D4 candidate measured Worker at 762,666, root at 377,177
3701
- and D1 at 88,321 bytes gzip. Optional-graph optimization brought UI to 342,955,
3702
- so the earlier unused UI allowance is revoked and its original 345000 cap
3703
- remains. Historical baselines, root/D1 and all other caps, dependency pins and
3704
- security/API gates remain unchanged. These independent measurements do not
3705
- predict a combined gzip by addition; final artifacts must be rebuilt. Native
3706
- registration, decorated fallback and request/native-storage semantics also
3707
- require a smoke through an actual minified production bundle. Final post-hook
3708
- official artifacts measure root 377,290, D1 88,321, Clerk 80,959, UI 342,959
3709
- and Worker 762,774 bytes gzip, all within those caps.
3710
-
3711
3419
  ### Native remote MCP programs (contributor)
3712
3420
 
3713
3421
  Remote MCP connection joins, credential and epoch checks, catalog intake,
@@ -3731,7 +3439,7 @@ restore that same owner. Every operation captures its own generation, credential
3731
3439
  identity and listing binding before waiting; a later listing cannot adopt an
3732
3440
  abandoned publication. Request closure revokes the connector and cache scopes.
3733
3441
  No client, transport, response, signal, promise or live request Context is shared
3734
- across inbound requests. D4's bounded refresh coordinator retains its separate
3442
+ across inbound requests. The bounded refresh coordinator retains its separate
3735
3443
  ownership, synchronous submission gate and waiting-request handoff.
3736
3444
 
3737
3445
  Two deliberate Promise waits remain: a connect waiter receives the Deferred's
@@ -3770,16 +3478,11 @@ select the underlying current methods at dispatch, including namespaces created
3770
3478
  while a source method is decorated. Original SQL shells call their own native
3771
3479
  program directly. Tests cover current and previously selected programs, own and
3772
3480
  inherited saved-original delegates, get/set/CAS and activity record/list,
3773
- restoration, typed failures and interruption. The D4 native proof fixtures install
3481
+ restoration, typed failures and interruption. The native auth proof fixtures install
3774
3482
  their intentional throwing Promise shells before registration, so they establish
3775
3483
  native composition without authorizing bypass of later replacements.
3776
3484
 
3777
- `remote-mcp-native.test.ts` runs native SDK/cache/request-boundary cases on Node
3778
- and workerd. `remote-mcp-native-bundle.node.test.ts` additionally loads an actual
3779
- minified production bundle to verify call/cache registration, request Context,
3780
- native storage, saved-original delegates, custom replacements and closed-owner
3781
- refusal. Existing wire, physical-attempt, cache and OAuth lifetime suites remain
3782
- the compatibility evidence.
3485
+ Existing wire, cache and OAuth suites remain the compatibility evidence.
3783
3486
 
3784
3487
  ### Native upward MCP orchestration (contributor)
3785
3488
 
@@ -3833,19 +3536,9 @@ identity, credential, storage, executor and tool callbacks. Connecta's OAuth-lin
3833
3536
  binding, TTL, progress, closing and one-use claims are native orchestration around
3834
3537
  those leaves. The supported protocol and unsupported-feature refusals remain
3835
3538
  unchanged. Native consumer, failure, decorator, sharing, close and minified-bundle
3836
- regressions live in `upward-native.test.ts` and
3837
- `upward-native-bundle.node.test.ts`; existing wire, continuation, redaction,
3539
+ regressions live in `upward-native.test.ts`; existing wire, continuation, redaction,
3838
3540
  physical-attempt and body/admission suites remain compatibility evidence.
3839
3541
 
3840
- The combined native/operator graph has a measured contributor bundle allowance
3841
- in PR #1060: the UI gzip cap is 346,000 bytes, with its historical baseline
3842
- unchanged. The official full checker measured 345,228 bytes after regenerating
3843
- the merged UI assets. Removing unused Invocation/Catalog registration retention
3844
- returned the public Worker entry from 198,744 to 77,426 bytes; live minified
3845
- Context, inherited-method, decorator and restoration proofs retain native
3846
- composition. Root, example Worker, public Worker, D1 and Clerk caps remain
3847
- unchanged. This cost decision grants no later request-body-limit allowance.
3848
-
3849
3542
  ### Catalog operation vocabulary (contributor)
3850
3543
 
3851
3544
  The standard hatch privately registers bounded vendor operation text against its
@@ -3860,13 +3553,3 @@ cache. Exact authored names and complete authored matches precede API pointers;
3860
3553
  ordinary result limits, offsets, catalog failure disclosure and byte bounds apply.
3861
3554
  The published discovery contract states compilation, query and cache bounds.
3862
3555
  No request identity, signal, credential or promise enters a vocabulary cache.
3863
-
3864
- ### Authenticated body-bound cost (contributor)
3865
-
3866
- The separate #860 change has a measured allowance only for the example Worker
3867
- gzip cap, from 785,000 to 786,000 bytes, with its historical baseline unchanged.
3868
- The official full checker measures 785,036 bytes against D7's exact 784,948-byte
3869
- lower graph, an actual 88-byte increase. It reuses the bounded platform body
3870
- inspector, preserves native orchestration and avoids a new reader lifecycle.
3871
- Root, UI, public Worker, D1, Clerk and asset caps remain fixed. The cost ruling
3872
- does not approve protocol, cancellation or authority semantics.