@zackbart/connecta 0.33.0 → 0.34.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 (86) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +99 -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-service.js +15 -1
  9. package/dist/config-value-sources.js +6 -1
  10. package/dist/config.d.ts +13 -2
  11. package/dist/config.js +36 -2
  12. package/dist/connector-caller.js +2 -1
  13. package/dist/describe-config.d.ts +3 -1
  14. package/dist/describe-config.js +1 -0
  15. package/dist/execute.js +1 -11
  16. package/dist/fix-prompt.js +2 -2
  17. package/dist/index.js +1 -0
  18. package/dist/membership.d.ts +2 -0
  19. package/dist/oauth-callback-outcome.js +2 -2
  20. package/dist/operator-ui/agents.d.ts +1 -1
  21. package/dist/operator-ui/agents.js +16 -16
  22. package/dist/operator-ui/config-summary.js +562 -0
  23. package/dist/operator-ui/connection-map.js +375 -0
  24. package/dist/operator-ui/endpoints.js +44 -15
  25. package/dist/operator-ui/fix-prompts.js +1 -1
  26. package/dist/operator-ui/generated.js +4 -4
  27. package/dist/operator-ui/member-page.js +13 -28
  28. package/dist/operator-ui/overview.js +134 -0
  29. package/dist/operator-ui/view.d.ts +27 -22
  30. package/dist/operator-ui/view.js +104 -88
  31. package/dist/page-styles.js +5 -3
  32. package/dist/providers/_shared/google/workspace.d.ts +6 -4
  33. package/dist/providers/_shared/google/workspace.js +6 -2
  34. package/dist/providers/ccb/index.js +1 -1
  35. package/dist/providers/docs/index.js +1 -1
  36. package/dist/providers/docs/skill.generated.js +2 -2
  37. package/dist/providers/drive/index.js +1 -1
  38. package/dist/providers/drive/skill.generated.js +2 -2
  39. package/dist/providers/forms/index.js +1 -1
  40. package/dist/providers/forms/skill.generated.js +2 -2
  41. package/dist/providers/gmail/index.js +1 -1
  42. package/dist/providers/gmail/skill.generated.js +2 -2
  43. package/dist/providers/mixpanel/skill.generated.js +3 -3
  44. package/dist/providers/notion/skill.generated.js +1 -1
  45. package/dist/providers/overflow/index.js +1 -1
  46. package/dist/providers/overflow/skill.generated.js +2 -2
  47. package/dist/providers/planning-center/index.js +1 -1
  48. package/dist/providers/planning-center/skill.generated.js +4 -4
  49. package/dist/providers/revenuecat/skill.generated.js +2 -2
  50. package/dist/providers/sheets/index.js +1 -1
  51. package/dist/providers/sheets/skill.generated.js +2 -2
  52. package/dist/providers/slides/index.js +1 -1
  53. package/dist/providers/slides/skill.generated.js +2 -2
  54. package/dist/providers/tithely/index.js +1 -1
  55. package/dist/providers/vercel/index.js +1 -1
  56. package/dist/providers/vercel/skill.generated.js +1 -1
  57. package/dist/registry.js +15 -0
  58. package/dist/routes/mcp.js +20 -13
  59. package/dist/routes/oauth.js +2 -2
  60. package/dist/skills.js +10 -0
  61. package/dist/storage/sql.js +5 -1
  62. package/dist/types.d.ts +18 -7
  63. package/dist/ui.js +9 -4
  64. package/dist/usage-guide.js +1 -1
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/documentation/README.md +2 -2
  68. package/documentation/architecture.md +18 -13
  69. package/documentation/auth.md +183 -67
  70. package/documentation/code-mode.md +23 -5
  71. package/documentation/deploying.md +5 -1
  72. package/documentation/integrating.md +9 -6
  73. package/documentation/meta-tools.md +36 -22
  74. package/documentation/migration-0.33.md +21 -13
  75. package/documentation/migration-0.34.md +67 -0
  76. package/documentation/operator-tests.md +46 -20
  77. package/documentation/operator-ui.md +294 -104
  78. package/examples/worker/AGENTS.md +15 -9
  79. package/examples/worker/README.md +41 -14
  80. package/examples/worker/src/connecta.config.ts +5 -4
  81. package/examples/worker/wrangler.jsonc +1 -1
  82. package/package.json +19 -19
  83. package/templates/node/AGENTS.md +13 -7
  84. package/templates/node/README.md +14 -11
  85. package/templates/node/package.json +1 -1
  86. package/templates/node/src/connecta.config.ts +2 -2
@@ -43,14 +43,14 @@ mode returns native `content`. Default-mode successes declare
43
43
  `_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
44
44
  `data` and declares `format` in its complete result envelope.
45
45
 
46
- | Tool | Arguments | Returns |
47
- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
48
- | `execute_code` | `code`, `diagnostics?` | `{ result?, error?, hostCalls }`, plus diagnostics when asked |
49
- | `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ catalogErrors, tools, total, offset, limit, hasMore }`, plus `queryAnalysis` on a partial or failed search |
50
- | `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
51
- | `call_destructive_tool` | the same, plus `reason?` | the same |
52
- | `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery) |
53
- | `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present |
46
+ | Tool | Arguments | Returns |
47
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
48
+ | `execute_code` | `code`, `diagnostics?` | `{ result?, error?, hostCalls }`, plus `logs`, `emitted`, and `emittedDiscarded` when present and diagnostics when asked |
49
+ | `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ catalogErrors, tools, total, offset, limit, hasMore }`, plus `nextOffset` when more remain, `absence` when the query names a service with no configured connector, and `queryAnalysis` on a partial, failed, unsearchable, or absent-service search |
50
+ | `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
51
+ | `call_destructive_tool` | the same, plus `reason?` | the same |
52
+ | `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery) |
53
+ | `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present |
54
54
 
55
55
  `limit` defaults to 8 and is capped at 100, as is one `connecta.describe` batch.
56
56
  `connecta.result`'s `offset` is a whole number of bytes ≥ 0 defaulting to 0 and
@@ -66,8 +66,8 @@ refuse a consequential call.
66
66
 
67
67
  `diagnostics: true` adds compact request-local timing and serialized-size
68
68
  aggregates for a caller measuring a workflow: a `diagnostics` block from
69
- `execute_code`, a `timing` block on a call response that is already structured
70
- (value mode, or a failure carrying recovery). Normal calls pay nothing for it,
69
+ `execute_code`, a `timing` block on every structured call response: value-mode successes
70
+ and every `{ ok: false }` failure. Normal calls pay nothing for it,
71
71
  and the measurements never contain program source, arguments, values, addresses,
72
72
  credentials, logs, or raw error text.
73
73
 
@@ -221,7 +221,7 @@ declared schema exactly as they do for compact.
221
221
  A connector may attach a deployment-owned guide as markdown, keeping the
222
222
  original `usageGuide: string` configuration, or as
223
223
  `{ content, summary?, required? }` — which registers no connector and creates no
224
- shared runtime template. The registry prepends Agent Skills frontmatter to
224
+ shared runtime template. The skills catalog prepends Agent Skills frontmatter to
225
225
  `content` and preserves the guide body, including its whitespace.
226
226
  `summary` is normalized and refuses construction over 120 characters; absent,
227
227
  Connecta derives the same bounded fallback the skills listing uses from the
@@ -253,14 +253,22 @@ A connector-scoped lexical miss keeps that connector's guide metadata under
253
253
  contains no endpoint vocabulary: a required guide stays discoverable instead of
254
254
  disappearing with the zero-tool page.
255
255
 
256
- Two built-in skills are byte-identical across deployments. `usage` says to read
257
- it at most once per task and owns program selection detail, examples, runtime
256
+ Two built-in skills are byte-identical across deployments. `usage` is fetched
257
+ only for missing guidance or program repair, and owns program selection detail, examples, runtime
258
258
  differences, and repair guidance; `investigate` is on-demand guidance for
259
259
  purchase verification, experiment checks, and customer or deployment
260
260
  investigations — shared guidance, not a saved workflow and not a source of
261
261
  deployment-specific ids. The always-loaded instructions and tool definitions keep
262
262
  route selection, the fail-closed boundary, and the minimum guest syntax, so a
263
- client that never fetches a skill can still write a valid first program. Connector
263
+ client that never fetches a skill can still write a valid first program.
264
+ The instructions are the same on every endpoint except one addition: at
265
+ `/mcp/<pool>` and `/agent/<pool>`, a pool that sets a
266
+ [`description`](./auth.md#pools) has it appended after `This endpoint: `, so
267
+ the model learns that endpoint's scope, for example
268
+ `This endpoint: Team tier: shared tools only. For someone's own mail or files, ask them to message you one-on-one.`
269
+ Plain `/mcp` never carries one. The description changes no tool or tool
270
+ description, and `initialize` and `server/discover` stay `cacheScope: "private"`,
271
+ so a client keeps what it learned per endpoint. Connector
264
272
  guides stay scoped to the deployment that listed them even when two deployments
265
273
  use identical content, and a deployment with no connector guides receives none of
266
274
  the short conditional guide pointers in its tool definitions.
@@ -386,7 +394,11 @@ including dependencies inherited from pages read during the run. Downstream skil
386
394
  resource content requires a whole-connector grant on every page. Local guides and catalog
387
395
  metadata require the contributing connector or tool to remain visible, including
388
396
  caught catalog-load errors, unknown-tool failures, and recovery messages that
389
- list visible connectors or local guides. Returns with no downstream calls still
397
+ list visible connectors or local guides. No-match search guidance also retains
398
+ the connectors it names or summarizes by count, including matches to connector
399
+ titles; scoped guides and summaries, absence rosters, and catalog errors retain
400
+ their contributors. Search counts, ranking, and term analysis also retain every
401
+ searched connector, including matches outside the returned page. Returns with no downstream calls still
390
402
  require matching identity, origin and endpoint, current auth, and pool membership. A page must
391
403
  match those bindings and pass current auth, connector/tool grants, pool membership,
392
404
  and read-only checks. Old entries lacking bindings fail closed. A random UUID is a
@@ -394,7 +406,7 @@ handle, never an access grant.
394
406
 
395
407
  New entries store UTF-8 bytes in a base64 envelope split across storage keys,
396
408
  48 KiB of result text per chunk — widening past roughly 1.5 MB so no result
397
- occupies more than 33 keys, because every chunk is also a write. `connecta.result`
409
+ occupies more than 32 keys, because every chunk is also a write. `connecta.result`
398
410
  reads and decodes only the chunks a page covers plus a few boundary bytes, so
399
411
  paging a 1.2 MB result costs the same per page as paging a 300 KB one; it
400
412
  neither encodes nor reads the whole result per page. Entries without the required identity and invocation bindings fail closed,
@@ -426,7 +438,7 @@ JSON; everything after the first newline is the preview. `structuredContent`
426
438
  is absent, so the notice cannot hide the preview in clients that prefer it:
427
439
 
428
440
  ```text
429
- {"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with connecta.result using nextAction.","nextOffset":24000,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => await connecta.result(\"…\", { offset: 24000 })"}}}
441
+ {"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with connecta.result using nextAction.","nextOffset":24000,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => await connecta.result(\"…\", { offset: 24000, maxBytes: 3829 })"}}}
430
442
  {"ts":"2026-09-16T21:31:11.759Z","actor":"sam.ortiz@example.com",…
431
443
  ```
432
444
 
@@ -458,7 +470,7 @@ characters. One that rejects outright — Claude Code with
458
470
  `MAX_MCP_OUTPUT_TOKENS` set below 12,500 — needs a cap under twice its limit.
459
471
 
460
472
  The preview is the head of what `connecta.result` pages, cut on a character
461
- boundary, so `nextAction.arguments.offset` continues exactly where it stops. A
473
+ boundary, so the `offset` in `nextAction.arguments.code` continues exactly where it stops. A
462
474
  lone text block — including the one synthesized from `structuredContent` — is
463
475
  measured, stashed, previewed, and paged as its text: wrapping it in a content
464
476
  envelope added nothing but a second layer of JSON escaping, so a JSON payload
@@ -682,8 +694,8 @@ replay. Downstream payloads and input responses are capped at 64 KiB, with at
682
694
  most 16 inputs and 256 characters per downstream key. The combined private
683
695
  state history is also capped at 64 KiB. Sealed wire state is
684
696
  capped at 128 KiB. `input_required_invalid`, `input_required_limit`,
685
- `input_required_unsupported`, and `input_required_round_limit` are non-retryable
686
- typed failures.
697
+ and `input_required_unsupported` are non-retryable typed failures;
698
+ `input_required_round_limit` is an untyped failure with `retryable: false`.
687
699
 
688
700
  Programs and in-process meta-tools return `input_required_unsupported`, with a
689
701
  `nextAction` naming the equivalent direct call and bounded original arguments.
@@ -965,12 +977,14 @@ Handlers and schemas live in [meta-tools.ts](https://github.com/zackbart/connect
965
977
  [Catalog service](https://github.com/zackbart/connecta/blob/main/src/catalog-service.ts)
966
978
  and [invocation](https://github.com/zackbart/connecta/blob/main/src/invocation.ts)
967
979
  serve both direct tools and programs. [Skills](https://github.com/zackbart/connecta/blob/main/src/skills.ts)
968
- owns the supplied usage instructions and connector guides.
980
+ owns the supplied usage instructions, each endpoint's instructions
981
+ (`endpointInstructions`), and connector guides.
969
982
 
970
983
  [Schema tests](https://github.com/zackbart/connecta/blob/main/test/meta-tool-schemas.test.ts),
971
984
  [search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
972
985
  [call tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-call.test.ts),
973
986
  [skills tests](https://github.com/zackbart/connecta/blob/main/test/skills-extension.test.ts),
974
- and [surface tests](https://github.com/zackbart/connecta/blob/main/test/code-first-surface.test.ts)
987
+ [surface tests](https://github.com/zackbart/connecta/blob/main/test/code-first-surface.test.ts),
988
+ and [pool description tests](https://github.com/zackbart/connecta/blob/main/test/pool-description.test.ts)
975
989
  check the published contract. Guest parity and clause evidence are in
976
990
  [code-mode verification](./code-mode.md#verification).
@@ -127,7 +127,12 @@ maintainer issues, rotates, and revokes; nothing outside code adds, renames,
127
127
  or rebinds one, and removing a name stops its token on the next request.
128
128
 
129
129
  1. Add `agents: ["<name>", …]` and keep `accessTokens(storage)` on the same
130
- storage and key namespace. Drop `maxActive`.
130
+ storage and key namespace. Drop `maxActive`. A Node template deployment
131
+ from before 0.33 also copies `src/rotate-token.ts` and `src/adopt-token.ts`
132
+ from `node_modules/@zackbart/connecta/templates/node/src/`, deletes
133
+ `src/provision-token.ts`, and replaces the `provision-token` and `doctor`
134
+ scripts with the installed template's `rotate-token`, `adopt-token`, and
135
+ `doctor` (`connecta doctor --pool <pool>`) scripts.
131
136
  2. Keep each machine's existing secret by adopting its token once:
132
137
  `npm run adopt-token` lists the tokens that can be adopted, and
133
138
  `npm run adopt-token -- <token-id> <agent>` binds one (Node template), or
@@ -205,7 +210,8 @@ membership contract.
205
210
  In 0.32 a `cta_` token reached a Workspace connector's `subject` mapping,
206
211
  which usually answered nothing for a caller without a principal. In 0.33 an
207
212
  agent never reaches the mapping. It acts as the `owner` of the pool it came
208
- through, and fails `auth_required` in a pool without one. People always act
213
+ through; in a pool without one the connector is absent for it, as a personal
214
+ connector is. People always act
209
215
  as themselves through the mapping, in any pool. A fixed `subject` address
210
216
  still applies to every caller.
211
217
 
@@ -303,24 +309,26 @@ pools: {
303
309
  // 0.33
304
310
  agents: ["assistant"],
305
311
  pools: {
306
- assistant,
307
- "assistant-alice": { ...assistant, tools: [...assistant.tools, "notes_alice", "gmail"], owner: "alice@example.com" },
308
- "assistant-bob": { ...assistant, tools: [...assistant.tools, "notes_bob", "gmail"], owner: "bob@example.com" },
312
+ assistant: { tools: base, members },
313
+ "assistant-alice": { tools: [...base, "notes_alice", "gmail"], owner: "alice@example.com", members },
314
+ "assistant-bob": { tools: [...base, "notes_bob", "gmail"], owner: "bob@example.com", members },
309
315
  },
310
- // with const assistant = { tools: ["crm", "tickets"], members: ["agent:assistant"] };
316
+ // with const base = ["crm", "tickets"], members = ["agent:assistant"];
311
317
  ```
312
318
 
313
319
  1. Declare the agent in `agents` and adopt its token (see
314
320
  [Agents and their tokens](#agents-and-their-tokens)).
315
- 2. Define the base pool `<agent>` once with the shared connectors and
316
- `members: ["agent:<agent>"]`, and spread it into one `<agent>-<user>` pool
317
- per person, adding that person's connectors and `owner`.
321
+ 2. Define the base pool's tool list once with the shared connectors, and
322
+ spread it into the base pool `<agent>` and one `<agent>-<user>` pool per
323
+ person, adding that person's connectors and `owner`. Each pool lists
324
+ `members: ["agent:<agent>"]`. If group chats should get different tools
325
+ depending on who is present, add tiers as the layout describes.
318
326
  3. Collapse per-person Workspace connectors into one connector with a mapping
319
327
  `subject`. Keep other per-person accounts as their own shared-auth
320
328
  connector ids (`notes_alice`) and list each only in its person's pool.
321
329
  4. Change the agent's client to one connection whose URL it sets per turn:
322
- `/agent/<agent>-<user>` in a one-on-one with that user, `/agent/<agent>`
323
- otherwise. Remove any second Connecta connection.
330
+ `/agent/<agent>-<user>` in a one-on-one with that user, otherwise the
331
+ layout's tier for who is present (`/agent/<agent>` with a single tier). Remove any second Connecta connection.
324
332
  5. Add this line to your deployment's own AGENTS.md (or its CLAUDE.md
325
333
  equivalent), so agents find all operator procedures after the upgrade:
326
334
 
@@ -339,6 +347,6 @@ pools: {
339
347
  5. For Workspace, call a read in an agent's personal pool and confirm it acts
340
348
  as the pool's owner. In the base pool, confirm Workspace connectors are
341
349
  absent: search finds none of their tools and a call fails
342
- `unknown_address`. `auth_required` is what an agent gets only in a pool
343
- without an owner that does list a Workspace connector.
350
+ `unknown_address`. The same holds in any pool without an owner, even one
351
+ that lists a Workspace connector with a subject mapping.
344
352
  6. Confirm each `<agent>-<user>` pool lists no other person in `members`.
@@ -0,0 +1,67 @@
1
+ # Migration to 0.34
2
+
3
+ Use this guide when upgrading an existing deployment from 0.33 to 0.34.
4
+ [Deployment upgrades](./deploying.md#upgrade-an-existing-deployment) cover the
5
+ general procedure; [the agent index](./README.md) routes other work. Upgrades
6
+ from earlier versions must also apply the intervening migrations.
7
+
8
+ No storage migration, token rotation or required configuration field is added.
9
+ Review the following changes before updating the package pin.
10
+
11
+ ## Operator links and browser titles
12
+
13
+ Maintainers now land on Overview at `/`. Endpoints is at `/endpoints`, and
14
+ `/access` redirects to `/endpoints`. Update saved links and browser automation
15
+ that expected Endpoints at `/`, including row links: `/#mcp/support` becomes
16
+ `/endpoints#mcp/support`. Members still get their member page on every operator
17
+ page path. MCP client URLs (`/mcp`, `/mcp/<pool>` and `/agent/<pool>`) do not change.
18
+
19
+ The default `branding.pageTitle` joins `productName` and `ownerName` with a
20
+ middle dot, for example `Assistant · Example team`. A title set explicitly in
21
+ `branding.pageTitle` is unchanged. If automation requires the previous title,
22
+ set that exact title explicitly; otherwise update its expectation. With no
23
+ owner name, the default remains the product name alone.
24
+
25
+ Activity links can use `failed` or `succeeded` for the outcome filter. Older
26
+ exact outcome values in saved links still work. See the
27
+ [operator UI guide](./operator-ui.md) for the new pages and controls.
28
+
29
+ ## Workspace connectors in agent pools
30
+
31
+ An agent in a pool without an `owner` no longer sees a Google Workspace
32
+ connector whose `subject` is a mapping function. Previously the connector
33
+ appeared but its calls failed `auth_required`; now discovery, search, describe,
34
+ skills and code mode omit it, and a call by address fails `unknown_address`.
35
+ Paging a saved program return also rechecks visibility, including contributions
36
+ to search counts and guidance.
37
+
38
+ Keep a shared or group pool owner-less when it must not act as one person.
39
+ If an agent needs to act as Alice, put that access in her personal pool and set
40
+ `owner: "alice@example.com"` there, using the Workspace account she is intended
41
+ to use. Confirm the pool's tools and members before adding an owner. Follow
42
+ [the agent pool layout](./auth.md#organizing-an-agents-pools) for the full
43
+ configuration. People in the same pool, agents in owned pools and connectors
44
+ with a fixed `subject` address keep their behavior.
45
+
46
+ ## Optional settings and development tooling
47
+
48
+ Pool `description` is optional; omit it to preserve the existing MCP
49
+ instructions. A custom activity store can optionally return
50
+ `describe().optionSources.retentionDays` as `"config"` or `"default"` to mark
51
+ whether code explicitly set retention. Existing implementations need no
52
+ change; Settings marks retention as explicitly set only when the store
53
+ reports `"config"`.
54
+
55
+ The repository now uses pnpm for development and CI. Installed deployments and
56
+ the Node template continue to use npm; keep their npm commands and lockfiles.
57
+ Deployments without the operator UI or mapping-subject Workspace connectors in
58
+ owner-less agent pools can skip the UI and discovery steps above.
59
+
60
+ ## Verify
61
+
62
+ After upgrading, open `/` as a maintainer and confirm Overview loads, then open
63
+ `/endpoints` and a saved endpoint row link. Check any automation that reads the
64
+ browser title. With an agent's existing credentials, discover tools in its base
65
+ and personal pools and perform a bounded Workspace read in the owned pool.
66
+ Owner-less pools must omit mapping-subject Workspace connectors. Run
67
+ `connecta doctor` as described in [deployment upgrades](./deploying.md#upgrade-an-existing-deployment).
@@ -13,15 +13,19 @@ or browser evidence. Read the [operator UI contract](./operator-ui.md) first.
13
13
  `test/fixtures/operator-visual.ts` owns the visual data. It builds the
14
14
  maintainer's contract v2, live overlay, and legacy auth details through the
15
15
  current server routes, using the types in `src/operator-ui/contract.ts` and
16
- `model.ts`; its people carry labels from a fake directory. Its Tokens agents
16
+ `model.ts`; its people carry labels from a fake directory. It sets a few
17
+ limits, an activity retention, one classification override and a GitHub call
18
+ admission rule in code, so Settings and the connector Configuration card show
19
+ set values beside defaults; Slack describes OAuth facts (scope, endpoints, an
20
+ API origin, client secret, PKCE) for the card's auth row. Its Tokens agents
17
21
  (`claude-desktop` issued, `triage` none, `nightly` revoked) are real slots
18
22
  committed through the deployment's own store, so the contract's `agents` and
19
23
  `GET /ui/access-tokens` agree, revisions included; only their timestamps and
20
24
  last seen are fixed. The restricted state is the member fixture from
21
25
  `test/fixtures/operator-contract-v2.ts`, and the endpoint and matrix variants
22
26
  use its maintainer fixture, which covers every runs-as kind and agent pools,
23
- with `test/fixtures/operator-console.ts` crowding the team pool so its chips
24
- collapse to "+3 more".
27
+ with `test/fixtures/operator-console.ts` crowding the team pool so its avatar
28
+ stack collapses to "+3".
25
29
  Collections use their corresponding browser types.
26
30
  `test/operator-visual-fixtures.test.ts` validates contract versions, connector
27
31
  IDs, granted tools, matrix cells, classification, provenance, and secret
@@ -31,7 +35,10 @@ server serializer.
31
35
 
32
36
  `test/browser/operator-visual.spec.ts` intercepts API reads and serves the real
33
37
  hashed assets and HTML with the production CSP. Loading responses wait on an
34
- explicit gate. Error responses use fixed HTTP failures. Screenshots use a fixed
38
+ explicit gate. Error responses use fixed HTTP failures. The map trace focuses its
39
+ node instead of hovering it: the capture scrolls to the top first, which would
40
+ move the node out from under a pointer and drop the highlight, so the test waits
41
+ for `.map-svg.tracing` before capturing. Screenshots use a fixed
35
42
  clock, UTC, en-US, locally hosted fonts, disabled animations, hidden carets,
36
43
  and a 1280 × 900 viewport. Narrow fixtures use 390 × 844. Every test waits for
37
44
  its state and fonts before capturing.
@@ -39,11 +46,12 @@ its state and fonts before capturing.
39
46
  ## Page and state matrix
40
47
 
41
48
  Each row runs empty, loading, error, populated, and restricted-viewer fixtures
42
- in both light and dark themes, for 80 screenshots. The restricted viewer is a
49
+ in both light and dark themes, for 90 screenshots. The restricted viewer is a
43
50
  pool member, who gets the member page, with no tabs, on every path.
44
51
 
45
52
  | Page or tab | Empty state | Loading/error request | Restricted viewer (member) |
46
53
  | -------------------- | ------------------------------------ | --------------------- | ----------------------------------- |
54
+ | Overview | No connectors: nothing to map | Config | Member page at `/` |
47
55
  | Endpoints | Root endpoint without tools | Config | Member page: endpoints, accounts |
48
56
  | Connectors | No connectors | Config | Member page, no connector request |
49
57
  | Connector Tools | Empty catalog | Config | Member page, no connector request |
@@ -53,24 +61,26 @@ pool member, who gets the member page, with no tabs, on every path.
53
61
  | Activity | No calls | Activity | Member page, no activity request |
54
62
  | Settings | Snapshot without connectors or pools | Config | Member page, no configuration facts |
55
63
 
56
- Populated Endpoints shows who can use each endpoint; populated Tokens has one
64
+ Populated Overview has a failing connector, drift, auth and credential problems
65
+ and a tokenless agent in its attention list. Populated Endpoints shows who can use each endpoint; populated Tokens has one
57
66
  agent per slot state, with pools and last seen.
58
67
 
59
- Another 54 screenshots cover configured, mismatched, unreadable and multifield
68
+ Another 60 screenshots cover the Overview map on the frozen v2 maintainer, one
69
+ person's trace, and the narrow map list; configured, mismatched, unreadable and multifield
60
70
  credentials; all three OAuth client mechanisms; clean and unobserved drift;
61
- historical Activity; client setup; schema dialog; read/write filters; expanded
62
- config provenance; narrow sign-in and Connection; deployment branding; `/mcp`
71
+ historical Activity; client setup; schema dialog; read/write filters; narrow
72
+ Settings; narrow sign-in and Connection; deployment branding; `/mcp`
63
73
  and an agent pool expanded with every runs-as kind; command search; narrow
64
74
  endpoints; the connector × endpoint matrix, one cell's open popover, and the
65
75
  narrow matrix; a new token's one-time secret dialog; and the member page, wide
66
76
  and narrow. `endpoints-expanded` and `narrow-endpoints` include the team row
67
- collapsed to "+3 more". The base
77
+ collapsed to "+3". The base
68
78
  populated Activity fixture includes grouped calls, an ungrouped legacy row,
69
79
  classification/result size/client facts, and a catalog-change event.
70
80
 
71
81
  `test/browser/operator-matrix.spec.ts` serves the frozen v2 fixtures to the
72
- real shell. It covers the matrix's table semantics, cell names and runs-as
73
- labels, popover keyboard behavior, placement at a short viewport, the in-flow
82
+ real shell. It covers the matrix's table semantics, cell names, status dots,
83
+ runs-as labels in each kind's popover, popover keyboard behavior, placement at a short viewport, the in-flow
74
84
  fallback without CSS anchors, and the absence of `style` attributes, agent URLs on Endpoints, the member page (its
75
85
  received body checked with `expectMemberViewWithholds`, `/ui/data` answering 404
76
86
  and never requested, no maintainer fact rendered, no maintainer request sent),
@@ -80,9 +90,25 @@ refresh that stalls past its deadline (one focusable Retry, no maintainer
80
90
  request until Retry succeeds), an open client setup drawer closed by a failed
81
91
  refresh with focus on Retry, Tokens under recovery (an open rotate question
82
92
  closes with focus on Retry and nothing sent; an unseen secret dialog stays open
83
- over the banner until dismissed), payload sentinels, CSP, and the refusal of a v1 body. `test/operator-endpoints.test.ts` covers the view model.
93
+ over the banner until dismissed), payload sentinels, CSP, and the refusal of a v1 body. Matrix coverage excludes retired grants from
94
+ its live counts and pie, keeps their names inspectable in the popover, and
95
+ renders an unavailable catalog as a dashed empty ring with "?" and an unknown
96
+ accessible name. Cell names distinguish equal grants against different live
97
+ catalog sizes. `test/operator-endpoints.test.ts` covers those view-model cases,
98
+ including a known empty catalog versus an unavailable one.
99
+ `test/operator-connection-map.test.ts` also checks that a failed or unobserved
100
+ catalog counts no tools, which the map draws as a muted dash, while a known
101
+ empty catalog counts zero.
102
+ `test/browser/operator-overview.spec.ts` covers the Overview on the frozen v2
103
+ fixtures: the status pill, stat labels and numbers, map columns, dashed read-only grants and dotted
104
+ tokenless access; attention rows with their fix links, a copied fix prompt equal
105
+ to the catalogue's text and a failing connector sorted first; the hover
106
+ sentence replacing the legend, and dimming; keyboard focus in column order with each node's sentence
107
+ as its accessible name, Enter opening an endpoint's row and a connector; the
108
+ narrow list, whose caller chips link where their map nodes do; an empty deployment; and a member at `/` getting the member page
109
+ with no maintainer read, plus CSP and no `style` attributes.
84
110
  `test/browser/operator-console.spec.ts` covers UI-3: who-can-use chips by
85
- label, raw id and agent mark and "+N more" from the keyboard; Tokens rows,
111
+ label, raw id and agent mark and "+N" from the keyboard; Tokens rows,
86
112
  issue with the once-only secret dialog, in-row rotate and revoke questions,
87
113
  503, 499 and 409 failures that send exactly one request, slots missing from
88
114
  the list or left unresolved shown with disabled controls until Refresh; and the
@@ -121,15 +147,15 @@ cannot prove these rules.
121
147
 
122
148
  The source of truth is `*-linux.png`, produced on `ubuntu-latest` with the
123
149
  lockfile's Playwright Chromium. Both CI workflows use the same runner, Node 22,
124
- and `npm run test:browser:install -- --with-deps`. macOS runs fixture routing and
150
+ and `pnpm run test:browser:install --with-deps`. macOS runs fixture routing and
125
151
  readiness checks but omits screenshot comparison. The update script refuses
126
152
  non-Linux hosts. Do not commit Darwin baselines.
127
153
 
128
154
  When Docker is unavailable, use the **Operator snapshots** workflow:
129
155
 
130
156
  ```sh
131
- git push --force-with-lease origin ui/834-shell
132
- gh workflow run operator-snapshots.yml --ref ui/834-shell
157
+ git push --force-with-lease origin <branch>
158
+ gh workflow run operator-snapshots.yml --ref <branch>
133
159
  # Read the completed run ID from the Actions page or gh run list.
134
160
  gh run download RUN_ID --name operator-linux-snapshots \
135
161
  --dir test/browser/operator-visual.spec.ts-snapshots
@@ -140,9 +166,9 @@ images and changed files, commit the intentional changes, and push again. The
140
166
  normal CI browser job must compare and pass against that commit. Snapshot
141
167
  updates run only through `workflow_dispatch`.
142
168
 
143
- On a matching Linux runner, `npm ci`, install Chromium with the command above,
144
- then run `npm run test:browser:snapshots`. The normal command is
145
- `npm run test:browser`. Missing baselines fail CI; retries cannot bless them.
169
+ On a matching Linux runner, `pnpm install --frozen-lockfile`, install Chromium with the command above,
170
+ then run `pnpm run test:browser:snapshots`. The normal command is
171
+ `pnpm run test:browser`. Missing baselines fail CI; retries cannot bless them.
146
172
  Set `OPERATOR_VISUAL_INSPECT=1` to retain local screenshots under ignored
147
173
  `test-results/` for inspection, without updating committed baselines.
148
174