@zackbart/connecta 0.33.0 → 0.34.1

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 (131) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +100 -128
  3. package/SECURITY.md +1 -1
  4. package/bin/version.mjs +1 -1
  5. package/dist/activity.d.ts +3 -0
  6. package/dist/branding.d.ts +7 -0
  7. package/dist/branding.js +76 -9
  8. package/dist/catalog-drift.d.ts +30 -26
  9. package/dist/catalog-drift.js +169 -84
  10. package/dist/catalog-service.js +16 -2
  11. package/dist/config-value-sources.js +6 -1
  12. package/dist/config.d.ts +13 -2
  13. package/dist/config.js +36 -2
  14. package/dist/connector-caller.js +2 -1
  15. package/dist/connectors/api-connector.d.ts +3 -0
  16. package/dist/connectors/api-connector.js +5 -1
  17. package/dist/connectors/option-shapes.js +1 -1
  18. package/dist/connectors/remote-mcp.d.ts +2 -0
  19. package/dist/connectors/remote-mcp.js +5 -1
  20. package/dist/describe-config.d.ts +5 -1
  21. package/dist/describe-config.js +2 -0
  22. package/dist/execute.js +1 -11
  23. package/dist/fix-prompt.js +2 -2
  24. package/dist/index.js +1 -0
  25. package/dist/invocation.js +3 -2
  26. package/dist/membership.d.ts +2 -0
  27. package/dist/meta-tools.js +3 -2
  28. package/dist/oauth-callback-outcome.js +2 -2
  29. package/dist/operator-ui/agents.d.ts +1 -1
  30. package/dist/operator-ui/agents.js +16 -16
  31. package/dist/operator-ui/config-summary.js +565 -0
  32. package/dist/operator-ui/connection-map.js +376 -0
  33. package/dist/operator-ui/endpoints.js +45 -16
  34. package/dist/operator-ui/fix-prompts.js +1 -1
  35. package/dist/operator-ui/generated.js +4 -4
  36. package/dist/operator-ui/health.js +52 -0
  37. package/dist/operator-ui/member-page.js +13 -28
  38. package/dist/operator-ui/overview.js +123 -0
  39. package/dist/operator-ui/view.d.ts +26 -22
  40. package/dist/operator-ui/view.js +130 -96
  41. package/dist/page-styles.js +5 -3
  42. package/dist/provider.d.ts +6 -1
  43. package/dist/provider.js +18 -1
  44. package/dist/providers/_shared/google/workspace.d.ts +9 -4
  45. package/dist/providers/_shared/google/workspace.js +7 -3
  46. package/dist/providers/basecamp/index.d.ts +2 -0
  47. package/dist/providers/breeze/index.d.ts +2 -0
  48. package/dist/providers/ccb/index.d.ts +2 -0
  49. package/dist/providers/ccb/index.js +2 -2
  50. package/dist/providers/cloudflare/index.d.ts +2 -0
  51. package/dist/providers/cloudflare/index.js +5 -1
  52. package/dist/providers/cloudflare/openapi.generated.js +3296 -3206
  53. package/dist/providers/cloudflare/rest.js +19 -0
  54. package/dist/providers/cloudflare/skill.generated.js +2 -2
  55. package/dist/providers/cloudflare/value-safety.js +51 -18
  56. package/dist/providers/docs/index.js +1 -1
  57. package/dist/providers/docs/skill.generated.js +2 -2
  58. package/dist/providers/drive/index.js +1 -1
  59. package/dist/providers/drive/skill.generated.js +2 -2
  60. package/dist/providers/forms/index.js +1 -1
  61. package/dist/providers/forms/skill.generated.js +2 -2
  62. package/dist/providers/gmail/index.js +1 -1
  63. package/dist/providers/gmail/skill.generated.js +2 -2
  64. package/dist/providers/intercom/index.d.ts +24 -0
  65. package/dist/providers/intercom/index.js +121 -0
  66. package/dist/providers/intercom/skill.generated.js +8 -0
  67. package/dist/providers/linear/index.js +204 -19
  68. package/dist/providers/linear/skill.generated.js +2 -2
  69. package/dist/providers/mixpanel/index.d.ts +2 -0
  70. package/dist/providers/mixpanel/index.js +21 -17
  71. package/dist/providers/mixpanel/skill.generated.js +3 -3
  72. package/dist/providers/notion/index.d.ts +2 -0
  73. package/dist/providers/notion/index.js +1 -1
  74. package/dist/providers/notion/skill.generated.js +1 -1
  75. package/dist/providers/overflow/index.d.ts +2 -0
  76. package/dist/providers/overflow/index.js +1 -1
  77. package/dist/providers/overflow/skill.generated.js +2 -2
  78. package/dist/providers/planning-center/index.d.ts +2 -0
  79. package/dist/providers/planning-center/index.js +1 -1
  80. package/dist/providers/planning-center/skill.generated.js +4 -4
  81. package/dist/providers/revenuecat/index.d.ts +5 -3
  82. package/dist/providers/revenuecat/index.js +147 -7
  83. package/dist/providers/revenuecat/skill.generated.js +3 -3
  84. package/dist/providers/sheets/index.js +1 -1
  85. package/dist/providers/sheets/skill.generated.js +2 -2
  86. package/dist/providers/slides/index.js +1 -1
  87. package/dist/providers/slides/skill.generated.js +2 -2
  88. package/dist/providers/stripe/index.d.ts +2 -0
  89. package/dist/providers/stripe/index.js +50 -30
  90. package/dist/providers/stripe/skill.generated.js +2 -1
  91. package/dist/providers/tithely/index.d.ts +2 -0
  92. package/dist/providers/tithely/index.js +1 -1
  93. package/dist/providers/vercel/index.d.ts +2 -0
  94. package/dist/providers/vercel/index.js +2 -2
  95. package/dist/providers/vercel/openapi.generated.js +391 -374
  96. package/dist/providers/vercel/skill.generated.js +2 -2
  97. package/dist/providers/vercel/value-safety.js +14 -4
  98. package/dist/registry.d.ts +10 -14
  99. package/dist/registry.js +77 -62
  100. package/dist/routes/mcp.js +20 -13
  101. package/dist/routes/oauth.js +2 -2
  102. package/dist/routes/ui-data.js +8 -2
  103. package/dist/skills.js +18 -2
  104. package/dist/storage/sql.js +5 -1
  105. package/dist/types.d.ts +36 -11
  106. package/dist/ui.js +9 -4
  107. package/dist/usage-guide.js +1 -1
  108. package/dist/version.d.ts +1 -1
  109. package/dist/version.js +1 -1
  110. package/documentation/README.md +2 -2
  111. package/documentation/architecture.md +66 -18
  112. package/documentation/auth.md +183 -67
  113. package/documentation/code-mode.md +30 -10
  114. package/documentation/deploying.md +5 -1
  115. package/documentation/integrating.md +64 -7
  116. package/documentation/meta-tools.md +38 -24
  117. package/documentation/migration-0.33.md +21 -13
  118. package/documentation/migration-0.34.md +67 -0
  119. package/documentation/operating.md +1 -1
  120. package/documentation/operator-tests.md +52 -20
  121. package/documentation/operator-ui.md +341 -104
  122. package/documentation/provider-auth-migration.md +22 -0
  123. package/examples/worker/AGENTS.md +15 -9
  124. package/examples/worker/README.md +41 -14
  125. package/examples/worker/src/connecta.config.ts +5 -4
  126. package/examples/worker/wrangler.jsonc +1 -1
  127. package/package.json +23 -19
  128. package/templates/node/AGENTS.md +13 -7
  129. package/templates/node/README.md +14 -11
  130. package/templates/node/package.json +1 -1
  131. package/templates/node/src/connecta.config.ts +2 -2
package/dist/types.d.ts CHANGED
@@ -252,12 +252,15 @@ export interface ReviewedTool {
252
252
  /** Why the verdict holds when the name or downstream annotations do not say. */
253
253
  readonly reason?: string;
254
254
  /**
255
- * `sha256:<hex>` digest of the input and output schemas the review read.
255
+ * `sha256-sets-v1:<hex>` digest of the reviewed input/output schemas.
256
+ * Legacy `sha256:<hex>` pins retain literal array ordering.
256
257
  * Omit until a review has actually read them; an invented digest reports
257
258
  * drift that never happened. When the live schema no longer matches it, or
258
259
  * it cannot be checked, the verdict lapses and the tool is served as a write.
259
260
  */
260
261
  readonly schemaDigest?: string;
262
+ /** Absence is expected under plan, feature, or permission gating. */
263
+ readonly optional?: boolean;
261
264
  }
262
265
  /**
263
266
  * A reviewed classification of a downstream MCP catalog, keyed by exact tool
@@ -287,7 +290,7 @@ export interface CatalogDriftCounts {
287
290
  droppedTools?: number;
288
291
  /** Live tools no release classified. Each one fails closed at call time. */
289
292
  unclassifiedTools: number;
290
- /** Classified names this catalog no longer serves — plan gating included. */
293
+ /** Required reviewed names this catalog no longer serves. */
291
294
  unservedTools: number;
292
295
  /** Explicit downstream annotations that contradict a vetted verdict. */
293
296
  annotationConflicts: number;
@@ -296,6 +299,15 @@ export interface CatalogDriftCounts {
296
299
  }
297
300
  /** One drift observation, taken while serving a catalog refresh. */
298
301
  export interface CatalogDriftReport extends CatalogDriftCounts {
302
+ /** False when this listing has no reviewed classification to compare. */
303
+ reviewedBaseline?: boolean;
304
+ /** Whether all pinned schemas could be checked on this observation. */
305
+ verification?: "complete" | "unavailable";
306
+ /** Live reviewed tools with and without recorded schema digests. */
307
+ schemaCoverage?: {
308
+ pinned: number;
309
+ unpinned: number;
310
+ };
299
311
  /** When the observation was taken; never when a probe was scheduled. */
300
312
  observedAt: string;
301
313
  }
@@ -364,6 +376,8 @@ export type ConnectorSkillResourceContents = {
364
376
  });
365
377
  /** The whole plugin contract — the one open seam. */
366
378
  export interface Connector {
379
+ /** Serve only read-classified tools on every endpoint. Defaults to false. */
380
+ readOnly?: boolean;
367
381
  id: string;
368
382
  /**
369
383
  * Who owns this connector's downstream authentication. `shared` keeps one
@@ -398,7 +412,7 @@ export interface Connector {
398
412
  * cross-operation sequencing rule).
399
413
  *
400
414
  * Served as `skill://connecta/connectors/<slug>/SKILL.md`, with generated
401
- * frontmatter followed by the unchanged guide body. `connector:<id>` is a
415
+ * frontmatter, a read-only policy notice when configured, and the guide body. `connector:<id>` is a
402
416
  * one-release lookup alias. The guide remains deployment-owned configuration;
403
417
  * no runtime registration or shared mutable copy exists.
404
418
  */
@@ -605,7 +619,7 @@ export interface ConnectorDescription {
605
619
  tools?: ConnectorToolDescription[];
606
620
  }
607
621
  export interface ConnectorUsageGuide {
608
- /** Markdown returned verbatim by `skills({ name: "connector:<id>" })`. */
622
+ /** Markdown served by `skills`, with a policy notice when the connector is read-only. */
609
623
  content: string;
610
624
  /**
611
625
  * Discovery hint describing the conventions the guide covers. Whitespace is
@@ -822,7 +836,7 @@ export interface ConnectaBranding {
822
836
  description?: string;
823
837
  /**
824
838
  * Browser tab title and page meta name. Defaults to
825
- * `"<productName> — <ownerName>"`, or just `productName` when no owner is set.
839
+ * `"<productName> · <ownerName>"`, or just `productName` when no owner is set.
826
840
  */
827
841
  pageTitle?: string;
828
842
  /**
@@ -832,14 +846,18 @@ export interface ConnectaBranding {
832
846
  * elsewhere (it replaces the `/favicon.svg` link in the page head; the
833
847
  * `/favicon.*` routes still serve whatever `svg`/`ico` provide). `href` must
834
848
  * be an absolute `http(s)` URL or a root-relative path; anything else falls
835
- * back to the default mark.
849
+ * back to the default mark. With `svg` set, the top bar shows it beside the
850
+ * name; the drawn mark shows only when no branding is set.
836
851
  */
837
852
  favicon?: {
838
853
  svg?: string;
839
854
  ico?: Uint8Array;
840
855
  href?: string;
841
856
  };
842
- /** `theme-color` meta value. Defaults to "#ffffff". */
857
+ /**
858
+ * `theme-color` meta value. Unset, pages follow the page background:
859
+ * `#f6f4ef` in light and `#12110f` in dark.
860
+ */
843
861
  themeColor?: string;
844
862
  /**
845
863
  * Operator-page appearance. Every field is optional, and a rejected value
@@ -860,18 +878,25 @@ export interface ConnectaBranding {
860
878
  export interface ConnectaTheme {
861
879
  /**
862
880
  * The brand color: links, focus rings, primary actions, the active nav item.
863
- * Hex only (`#rgb`, `#rrggbb`, or `#rrggbbaa`). Defaults to `#2f5fe0`.
881
+ * Hex only (`#rgb`, `#rrggbb`, or `#rrggbbaa`). Defaults to `#4c44e0`
882
+ * (`#8c86ff` in dark). Text on it and accent text in dark derive from it.
864
883
  */
865
884
  accent?: string;
866
885
  /**
867
886
  * Corner rounding for cards, inputs, and buttons. A CSS length (`10px`,
868
887
  * `0.5rem`) or a bare number read as pixels. `0` restores square corners.
869
- * Defaults to `10px`.
888
+ * Defaults to `14px`.
870
889
  */
871
890
  radius?: string | number;
872
- /** Body font stack. A plain CSS font-family list. Defaults to a system stack. */
891
+ /**
892
+ * Body font stack. A plain CSS font-family list. Defaults to Hanken
893
+ * Grotesk, served with the operator UI, then a system stack.
894
+ */
873
895
  fontFamily?: string;
874
- /** Monospace font stack for addresses, ids, and the endpoint URL. */
896
+ /**
897
+ * Monospace font stack for addresses, ids, and the endpoint URL. Defaults
898
+ * to JetBrains Mono, served with the operator UI, then a system stack.
899
+ */
875
900
  monoFamily?: string;
876
901
  /**
877
902
  * `"system"` (the default) follows the operator's OS setting; `"light"` and
package/dist/ui.js CHANGED
@@ -6,9 +6,10 @@ import { uiData } from "./routes/ui-data.js";
6
6
  import { runEdge } from "./runtime/run.js";
7
7
  import { OPERATOR_UI_STYLE_PATH, OPERATOR_UI_SCHEME_PATH, OPERATOR_UI_SCRIPT_PATH, OPERATOR_UI_NOTICES_PATH, } from "./operator-ui/generated.js";
8
8
  export { filterUiConnectors, } from "./operator-ui/model.js";
9
- import { escapeHtml, isSafeHttpsUrl, renderPage, resolveBranding } from "./branding.js";
9
+ import { brandMark, escapeHtml, isSafeHttpsUrl, renderPage, resolveBranding } from "./branding.js";
10
10
  export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
11
11
  const OPERATOR_PAGE_LABELS = {
12
+ overview: "Overview",
12
13
  endpoints: "Endpoints",
13
14
  connections: "Connectors",
14
15
  connector: "Connector",
@@ -18,6 +19,8 @@ const OPERATOR_PAGE_LABELS = {
18
19
  };
19
20
  export function operatorPageForPath(path) {
20
21
  if (path === "/")
22
+ return "overview";
23
+ if (path === "/endpoints")
21
24
  return "endpoints";
22
25
  if (path === "/connectors")
23
26
  return "connections";
@@ -36,7 +39,7 @@ export function operatorPageForPath(path) {
36
39
  return undefined;
37
40
  }
38
41
  export function operatorPageTitle(page, configuredTitle) {
39
- return `${OPERATOR_PAGE_LABELS[page]} — ${configuredTitle}`;
42
+ return `${OPERATOR_PAGE_LABELS[page]} · ${configuredTitle}`;
40
43
  }
41
44
  /** Report the verdict the registry stored, independent of whether an endpoint is read-only. */
42
45
  export function uiToolSafety(definition) {
@@ -146,8 +149,8 @@ _nonce, page = "endpoints") {
146
149
  <h1>${OPERATOR_PAGE_LABELS[page]}</h1>
147
150
  <div class="lead-copy">
148
151
  <p>${escapeHtml(brand.description)}</p>
149
- <noscript><p class="msg">The operator pages need JavaScript. Nothing else here
150
- does — agents reach this deployment through <span class="mono">/mcp</span>.</p></noscript>
152
+ <noscript><p class="msg">Operator pages need JavaScript. Agents connect at
153
+ <span class="mono">/mcp</span>.</p></noscript>
151
154
  </div>
152
155
  </div>
153
156
  </main>
@@ -159,6 +162,7 @@ _nonce, page = "endpoints") {
159
162
  schemePinned: brand.theme.colorScheme !== "system",
160
163
  titleSuffix: brand.pageTitle,
161
164
  productName: brand.productName,
165
+ brandMark: brandMark(branding, true),
162
166
  productDescription: brand.description,
163
167
  productOperatorLabel: brand.productName + " operator",
164
168
  })}</script><script defer src="${OPERATOR_UI_SCRIPT_PATH}"></script>`,
@@ -182,6 +186,7 @@ export function operatorUi(options = {}) {
182
186
  options = assertKnownOptions(options, "operatorUi()", OPERATOR_UI_OPTIONS);
183
187
  const reservedPaths = [
184
188
  "/",
189
+ "/endpoints",
185
190
  "/connectors",
186
191
  "/connectors/*",
187
192
  "/tools",
@@ -77,7 +77,7 @@ async () => {
77
77
 
78
78
  Host-call budget exhaustion ends the run with budget_exceeded. Catch and
79
79
  allSettled cannot recover it or return partial output. Bound batches before
80
- starting them; search, describe, call, result, and skill spend host calls.
80
+ starting them; every guest call except emit spends host calls.
81
81
  Split remaining reads. Never replay writes for their output.
82
82
 
83
83
  ## Per-connector guides
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const CONNECTA_VERSION = "0.33.0";
1
+ export declare const CONNECTA_VERSION = "0.34.1";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated from package.json by scripts/generate-version.mjs.
2
2
  // Workers-safe: no runtime filesystem read or deployment override.
3
- export const CONNECTA_VERSION = "0.33.0";
3
+ export const CONNECTA_VERSION = "0.34.1";
@@ -11,7 +11,7 @@ For a new deployment, start with [Deploying Connecta](./deploying.md).
11
11
  | Task | Self-contained procedure |
12
12
  | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
13
13
  | Add or remove a connector | [Change the connector roster](./integrating.md#add-or-remove-a-connector) |
14
- | Organize an agent's pools | [Organizing an agent's pools](./auth.md#organizing-an-agents-pools) |
14
+ | Organize an agent's pools and tiers | [Organizing an agent's pools](./auth.md#organizing-an-agents-pools) |
15
15
  | Add a person (maintainer or pool member) | [Add a person](./auth.md#add-a-person), [member page](./operator-ui.md#use-the-member-page) |
16
16
  | Connect a shared credential | [Connect a shared credential](./auth.md#connect-a-shared-credential) |
17
17
  | Issue, rotate, or revoke an agent token | [Manage an agent token](./auth.md#issue-rotate-or-revoke-an-agent-token) |
@@ -71,7 +71,7 @@ own scoped change procedures. Each [provider folder](https://github.com/zackbart
71
71
  owns its maintained `SKILL.md`, options, tests, fixtures, and review data.
72
72
  Use those details instead of copying provider catalogs into general guides.
73
73
  The provider list in the human README is generated; preserve its marker pair
74
- and run `npm run check:providers-generated` after edits.
74
+ and run `pnpm run check:providers-generated` after edits.
75
75
 
76
76
  When changing behavior, update its owning contract and task guide in the same
77
77
  change. Check examples against current source and point to the tests that
@@ -148,6 +148,7 @@ An admitted non-preflight `/mcp` request then takes five steps in
148
148
  (`src/membership.ts`, `admitsMaintainer`, `memberPool`, and `agentPool` in
149
149
  `src/routes/shared.ts`). Nothing intersects or widens either view; every
150
150
  refusal is one 404 naming no pool.
151
+ Connector `readOnly` is enforced by catalog discovery and invocation before dispatch: effective read-only is endpoint OR connector, including on plain `/mcp`. Maintained provider construction carries the policy through the shared factory.
151
152
  The endpoint's `readOnly` is the pool's own value, else the deployment's;
152
153
  plain `/mcp` uses the deployment's. It travels to the meta-tools and the
153
154
  program as one boolean that drops writes from `CatalogService` search and
@@ -158,7 +159,7 @@ An admitted non-preflight `/mcp` request then takes five steps in
158
159
  narrower slice, and the subject and principal keys. Personal connectors use
159
160
  the principal partition and result paging the subject partition with principal,
160
161
  endpoint/pool, origin, connector, and tool bindings. Paging re-admits the
161
- caller at its endpoint and rechecks the endpoint's `readOnly`; no caller parameter selects a partition (`test/identity-scope.test.ts`).
162
+ caller at its endpoint and rechecks endpoint OR connector `readOnly`; no caller parameter selects a partition (`test/identity-scope.test.ts`).
162
163
  5. **Serve.** Refuse `?toolkit=` with a 404 — the toolkits are gone
163
164
  ([#178](https://github.com/zackbart/connecta/issues/178)) but their URLs were
164
165
  handed out, and retiring a scoping boundary into fail-open is worse than any
@@ -174,7 +175,7 @@ never select an identity, grant, connector view, or pool. Direct and program
174
175
  activity records retain only the self-declared `clientName` and `clientVersion`,
175
176
  validated by one ASCII grammar at the record builder, SQL write/read boundaries,
176
177
  and authenticated activity read route. Names match
177
- `[A-Za-z0-9][A-Za-z0-9 ._@/+-]{0,63}`; versions match
178
+ `[A-Za-z0-9@][A-Za-z0-9 ._@/+-]{0,63}`; versions match
178
179
  `[A-Za-z0-9][A-Za-z0-9._+-]{0,31}`, each as a whole string. Names allow up to
179
180
  64 characters, versions up to 32. `__proto__`, `constructor`, and `prototype`
180
181
  are reserved. Invalid values are absent, never truncated or escaped. Capabilities and other client metadata never enter activity.
@@ -970,7 +971,7 @@ manual review evidence instead of claiming a machine-checkable JSON pin.
970
971
 
971
972
  The generic REST connector (`src/providers/_shared/rest/`) serves
972
973
  `<vendor>_api_search`, `_api_details`, `_api_read`, and `_api_write` over an
973
- `OperationIndex` that `npm run providers:spec` compiles offline from the
974
+ `OperationIndex` that `pnpm run providers:spec` compiles offline from the
974
975
  provider's pinned `openapi.source.json` into `openapi.generated.ts`.
975
976
  Each call is matched to an operation and checked against its parameters before
976
977
  transport; `_api_read` admits GET and the vendor's reviewed read-only POSTs in
@@ -1062,7 +1063,7 @@ every vendor ships one reviewed table through one mechanism
1062
1063
  message/error and raw string/object source payloads in both events and
1063
1064
  invocation maps. The shared review harness rejects embedded-URL operation
1064
1065
  keys absent from each vendor's pinned index and invalid path syntax.
1065
- Vercel's unavailable full pin remains a review limitation.
1066
+ The current Vercel pin was reviewed from its full response document; its unversioned URL does not retain older documents.
1066
1067
  Generic parameters in descriptions, metadata, nicknames, billing footers,
1067
1068
  notes, DNS TXT, KV values and operator HTML retain their original bytes,
1068
1069
  including whole URL strings. Ordinary code/key URLs can be false positives
@@ -1165,7 +1166,7 @@ a table.
1165
1166
 
1166
1167
  `remoteMcp({ classify })` is the public way to declare what a downstream's
1167
1168
  tools do: `{ tools: { name: "read" | "write" | "destructive" | { verdict,
1168
- reason?, schemaDigest? } }, unlisted?: "hide" }`, validated at construction. It fails closed
1169
+ reason?, schemaDigest?, optional? } }, unlisted?: "hide" }`, validated at construction. It fails closed
1169
1170
  (INV-1): a reviewed read fills silence but yields to an explicit write
1170
1171
  annotation, a reviewed write stays a write whatever the downstream claims, an
1171
1172
  unlisted tool is a read only when it says so, and a reviewed tool whose
@@ -1191,8 +1192,50 @@ receives a separate deep copy of its definition, so mutations cannot change
1191
1192
  classification, schemas, or write accounting later in the same program.
1192
1193
  A restart applies the current review to cached SDK facts. Retired registry
1193
1194
  manifest versions 2 and 3 are ignored and expire on their original TTL.
1194
- During successful listings the registry counts drift against the same record, and `scripts/drift-check.mjs` compares
1195
- its names with published inventories. The internal `withVettedCatalog()`
1195
+ During listings the registry counts drift against the same record. Optional
1196
+ reviewed tools do not count as unserved when gated out. Reports carry an explicit
1197
+ reviewed-baseline fact, counts of live reviewed tools with and without schema
1198
+ pins, and complete/unavailable schema verification. A digest failure replaces
1199
+ the previous observation rather than retaining a stale clean result (INV-1,
1200
+ INV-6). Observation and classification share the same verification result for each
1201
+ listing; classification still fails closed.
1202
+
1203
+ Reports are in-memory only, weakly keyed by connector and a registry-owned
1204
+ catalog partition token. The partition uses the same admitted principal,
1205
+ subject, caller identity, authenticated flag and pool as catalog caching, plus
1206
+ connector id and endpoint URLs. Personal registries do not share tokens with
1207
+ each other or the root. Each registry retains at most 1,024 tokens; eviction or
1208
+ restart yields no observation, never another subject's counts. No new storage
1209
+ key or durable catalog record is introduced.
1210
+
1211
+ New schema pins use `sha256-sets-v1:<hex>`. Canonicalization sorts object keys
1212
+ and schema `required`, `enum`, and `type` arrays, including schema-valued
1213
+ legacy dependencies. Tuple arrays and JSON data
1214
+ inside defaults, constants, enum members and examples retain order. Existing
1215
+ `sha256:<hex>` pins continue using the original key-sorted, literal-array
1216
+ algorithm; their reviewed digests are unchanged. Migrating a legacy pin requires
1217
+ reviewing the complete schemas and recording a new versioned digest. Legacy
1218
+ pins may still report conservative drift for array reordering until migrated.
1219
+ The iterative digest retains the whole-schema node ceiling in both formats.
1220
+
1221
+ Contributor evidence: `test/provider-schema-pins.test.ts` checks every provider
1222
+ construction case in the generated roster. Every pinned digest requires a
1223
+ complete captured schema that passes runtime verification; changed input or
1224
+ output schemas must fail it. The guard also exercises both digest prefixes.
1225
+
1226
+ The maintainer `scripts/drift-check.mjs` compares published inventories with
1227
+ provider evidence. Table inventories can require `inventory.accessColumn` and
1228
+ record `reviewedAccess` (`read` or `write`, with destructive verdicts recorded
1229
+ as write). These access snapshots must agree with the provider's runtime
1230
+ classification. A documented access change or disappearance of a reviewed explicit Access
1231
+ value is advisory drift, never an
1232
+ automatically accepted verdict. An explicit access value makes an
1233
+ `acknowledgedUnclassified` entry stale: report it and resume reporting that
1234
+ unreviewed tool as an addition. Missing tools and already reviewed names also
1235
+ invalidate such acknowledgements. Duplicate explicit access values must agree;
1236
+ a blank duplicate does not erase an explicit value elsewhere. Missing required
1237
+ columns in any tool table, unknown access values and conflicting rows are provider-local parser
1238
+ findings. The internal `withVettedCatalog()`
1196
1239
  helper has been removed. Public provider `*_VETTED_CATALOG` exports remain
1197
1240
  deprecated aliases derived from each definition
1198
1241
  (`test/classified-decorators.test.ts`, `test/hosted-presets.test.ts`).
@@ -1236,10 +1279,13 @@ connecta shows a person — the operator shell, the OAuth callback, a browser's
1236
1279
  404 — reads it. `src/operator-ui/tokens.css`
1237
1280
  resolves every color, radius, and font through a custom property and mixes the
1238
1281
  rest from those with `color-mix`; `page.css` holds the base typography and the
1239
- primitives (shell, masthead, buttons, badges, messages, the one-message status
1240
- page). The UI's `browser.css` imports both, and the build writes the same CSS,
1241
- minified, into `src/page-styles.ts`, a module of two strings that core imports
1282
+ pieces server-rendered pages share (top bar, buttons, badges, messages, the
1283
+ one-message status page). The UI's `browser.css` imports both, then one file
1284
+ per area under `src/operator-ui/styles/` (fonts, shared components, shell, and
1285
+ each page). The build writes the shared CSS, minified, into
1286
+ `src/page-styles.ts`, with the hashed font paths, a module that core imports
1242
1287
  without the UI bundle, so an OAuth callback still renders with the UI omitted.
1288
+ Those pages link the fonts only when the UI module serves them.
1243
1289
  `renderPage` in `src/branding.ts` is the one layout: head, favicon (the default
1244
1290
  is linked only when the UI serves it), `data-scheme`, the tokens with the theme
1245
1291
  after them, and the masthead. So `branding.theme` only has to append a `:root`
@@ -1253,14 +1299,16 @@ Maintained providers act as the caller downstream. `src/connector-caller.ts`
1253
1299
  attaches the admitted identity beside the request-scoped context, readable only
1254
1300
  by in-repo code and set from authorization, never arguments. Google Workspace
1255
1301
  domain-wide delegation
1256
- (`src/providers/google/`, first consumed by `./providers/gmail`) hands the
1302
+ (`src/providers/_shared/google/`, first consumed by `./providers/gmail`) hands the
1257
1303
  admitted identity to a deployment-config `subject` function and mints a
1258
1304
  service-account token as whatever Workspace address it returns. The caller
1259
1305
  carries whether an inbound provider _authenticated_ it: an open deployment
1260
1306
  admits everyone as the anonymous actor, and the function is never asked about
1261
1307
  them. It also carries the admitted pool's `owner`, which core copies from
1262
- config: an agent acts as it, before and instead of the function, and an agent
1263
- in a pool without one fails closed. A fixed `subject` address is every
1308
+ config: an agent acts as it, before and instead of the function. In a pool
1309
+ without one, the scoped registry view omits a mapping-subject connector for an
1310
+ agent (`src/registry.ts`, keyed on the tag in `src/workspace-subject.ts`), as it
1311
+ omits a personal connector, and the provider still fails closed if reached. A fixed `subject` address is every
1264
1312
  caller's. No caller, an unauthenticated one, or no address fails
1265
1313
  `auth_required` before any request leaves. The provider never chooses the account and a
1266
1314
  deployment's own connector still cannot read the caller (PRINCIPLES.md, INV-3; `test/google-workspace-delegation.test.ts`). Its tokens live in a
@@ -1321,7 +1369,7 @@ Effect callers skip the Promise and take the program: `admit` and
1321
1369
  and `acquireCallScoped` for call admission, `closeScope` and `closeScopeOnExit`
1322
1370
  for a connector scope, `withDeadlineEffect` where Promise code has
1323
1371
  `withDeadline`. The build prunes every declaration no `exports` target reaches,
1324
- and `npm run check:declarations` fails if what remains names an Effect type.
1372
+ and `pnpm run check:declarations` fails if what remains names an Effect type.
1325
1373
 
1326
1374
  ### One runner
1327
1375
 
@@ -1456,7 +1504,7 @@ resolve a single copy. Adopting an unstable subpath is its own pull request.
1456
1504
 
1457
1505
  The stable `4.0.0` release was re-evaluated on 2026-10-01 with
1458
1506
  [`scripts/probes/effect-v4-evaluation.mjs`](https://github.com/zackbart/connecta/blob/main/scripts/probes/effect-v4-evaluation.mjs).
1459
- Run `npm run build`, then `node scripts/probes/effect-v4-evaluation.mjs output.json`.
1507
+ Run `pnpm run build`, then `node scripts/probes/effect-v4-evaluation.mjs output.json`.
1460
1508
  The [raw observations](https://github.com/zackbart/connecta/blob/main/scripts/probes/effect-v4-evaluation-2026-10-01.json)
1461
1509
  are synthetic Node requests and neutral esbuild bundles, not live-client or
1462
1510
  workerd lifetime verification. Bundle figures add representative prototypes
@@ -1494,7 +1542,7 @@ replacement's benefit or compatibility.
1494
1542
  The core's cost is recorded rather than guessed. Against 0.24.4 the root entry
1495
1543
  grew from 235,346 to 279,526 bytes gzip, the Worker example from 263,949 to
1496
1544
  314,336, and each provider by about 36 KB, since each reaches Effect through
1497
- `remoteMcp()`; `npm run check:bundle` caps every entry against
1545
+ `remoteMcp()`; `pnpm run check:bundle` caps every entry against
1498
1546
  `scripts/bundle-budget.json`. An install carries about 53 MB of `effect`, with
1499
1547
  no dependencies of its own.
1500
1548
 
@@ -1510,7 +1558,7 @@ src/
1510
1558
  catalog-drift.ts reviewed classifications and the drift counts a refresh produces
1511
1559
  provider.ts defineProvider(): the one maintained-provider shape
1512
1560
  activity.ts optional history factory and best-effort recorder
1513
- auth/ bearer, Cloudflare Access, clerk (optional peer), downstream OAuth (remote and static)
1561
+ auth/ Cloudflare Access, clerk (optional peer), downstream OAuth (remote and static)
1514
1562
  executors/ the QuickJS pool and child (Node only)
1515
1563
  node.ts listen() (Node only)
1516
1564
  d1.ts, sqlite.ts the two storage drivers; storage/ holds the SQL core and key families
@@ -1518,7 +1566,7 @@ src/
1518
1566
 
1519
1567
  There are exactly two deployment shapes — `templates/node/`, which
1520
1568
  `connecta init` copies with its container files, and `examples/worker/` — and
1521
- `test/deployment-shapes.node.test.ts` with `npm run check:examples` keeps both
1569
+ `test/deployment-shapes.node.test.ts` with `pnpm run check:examples` keeps both
1522
1570
  compiling and configuring the real thing.
1523
1571
 
1524
1572
  ## Sharp edges