Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion docs/plans/integration-readiness-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ The matrix below is the definition of "ready". A row is ready when every cell is
| GroupMe | yes | **red**: TASK-101 |
| X | yes (admin OAuth callback + feed) | unverified |
| GitHub (app) | yes | Walked 2026-09-25 and re-walked 2026-09-26 on `5561a1dd` (both below). Live on our own repository since 09-18. **red** in C1 until per-person GitHub: a team cannot connect its own repository. C2 at 390 is green since #1874, shown on `5561a1dd`. C3 (a hosted agent gets the broker, #1880) shipped and has not been walked. C8 was green on `9a32fca5`. A new grant from the connection's owner, `grant_3c4d6ad5` (2026-09-29, on Sam's approval), has the same scope as the lapsed 09-18 one: pod "C4 run 2026-09-18"; `list_issues`, `create_issue` and `close_issue`; write-with-confirm; audience Sam, `c3stranger0912` and `c4-smoke`. It runs to 2026-10-06, so C3, C4 and C8 are walkable again. None has been walked on the current build |
| Hosted MCP (generic type, TASK-172) | yes | **Built and live, with one follow-up open.** Steps 1–6 of `hosted-mcp-connection-scope.md` §10, and step 8's prerequisites (the client record, expand phase, and the pre-registered client; #2063), are live. **Open:** the reconnect withdrawal #2069 added to step 3 (withdraw the old account's token at the vendor before dropping our copy) is not built yet, and `hostedMcpConnect.ts:406–420` does not do it. The bar every entry must meet (Wren 76043): a credential that cannot write. Notion, Sentry and Stripe wait for a vendor read scope. Entries are offered one by one; the rows below walk them |
| Hosted MCP (generic type, TASK-172) | yes | **Built and live.** Steps 1–6 of `hosted-mcp-connection-scope.md` §10, and step 8's prerequisites (the client record, expand phase, and the pre-registered client; #2063), are live. The reconnect withdrawal #2069 added to step 3 is built (#2072). A reconnect that brings a different account withdraws the old token at the vendor before our copy is overwritten. When that cannot be done, or the two accounts cannot be compared, the Connectors page points the person to the vendor's revoke page instead (`hostedMcpConnect.ts:554–598`). Since 2026-10-19 an entry's credential may write: a tool that writes is pinned `write`, and each call parks for the connection's owner with its exact arguments (`hosted-mcp-connection-scope.md` §3 and §5). That lifted Wren 76043's bar, that every entry's credential be unable to write. Entries are offered one by one; the rows below walk them |
| Linear (hosted MCP) | yes | **Fresh-account walk 2026-10-15 on `cfe91c21`, green** (connector-ops 77850 and 77962; the build is Deploy Dev's last run before the walk): a new commonly.me account and a new Linear workspace, neither connected before. **C1 green**: connected through the Connectors page. Linear's consent read "Read, Identity", the authorize request asked for `read openid`, and the token response granted `read openid` (`config.grantedScope`). **C3 green in an eligible pod**: a read grant of the 36 pinned tools, made through the grant panel, which offers only Read, in the account's own My Workspace. That pod is private and invite-only, with no bridge and Scout as its only agent, so it passes §12's pod rule in `hosted-mcp-connection-scope.md` (#2211). Scout listed the workspace's one team through the grant, the trail records the call ok, and Scout quoted #2206's untrusted-content marker ahead of the data. #2211 deployed after the walk, and the same call was ok again after it, at 16:22:37Z. The earlier C3 and C4 walks ran in the C4 run pod, which fails that rule: since `ff909fb3` the deployed rule returns `pod_joinable` for both of its grants. Partly walked live 2026-10-01 (Sam's account, Chrome). Before the fresh-account walk, C1 was open: Sam's own operator-led connect succeeded on `cf9b6abf`, through CIMD with scope `read openid`, and the row records its client and the Linear subject. That didn't prove a stranger connects unaided, and no stranger had tried. **C2 green at 1440, the rest unwalked**: in the fresh-account walk the grant panel offered only Read. Earlier, on `cf9b6abf` the grant rendered as GitHub, because the catalogue projected 0 Linear tools. #2068 (`207c68d5`) fixed it, and on the deployed `e370fe9b` (its descendant) the row reads Linear, with its mark and the read eye, at 1200. 390, a failure reaching Activity, and revoke reachability are unwalked. Earlier, C3 was green in the C4 run pod: Scout listed the team and three issues through `grant_c6af31b8`; the trail showed the calls ok, with Sam as credential owner. **C4 refused by design** from #2211 (the C4 column), and c4-smoke is now offered no Linear tool. Before that it was green (2026-10-09): c4-smoke, a public local claude seat confined by Seatbelt, called `linear.list_teams` through its broker on `grant_e3a1507b` at 06:12:57Z, and the trail shows it ok. Its daemon started at 06:11:59Z, after global cli 0.1.84 (npm latest) was installed at 06:11:41Z, so it runs the current build. An earlier run on 2026-09-27 didn't count, because that daemon predated the cli it should have been running. The same spawn showed that TASK-228's headers helper works under the public Seatbelt profile. A by-name scan of the spawn's claude process found `COMMONLY_TOKEN_FILE` set and `COMMONLY_AGENT_TOKEN` absent. The rest of #2067's consumer proof is unmeasured: the child processes' environments, and a credential rotation followed by a 401 retry succeeding. **C8 open**: in a pod with no grant, Scout is offered no `linear.*` tools. That's confinement by projection. An attempted out-of-pod call, refused and recorded in the trail, hasn't been made. **C9 open**, now only for whether a revoke takes effect at Linear. Linear's revoke is page-only. The fresh-account walk reached the page: `https://linear.app/settings/account/security`, whose Authorized applications list shows the connection as "MCP · Read · Revoke". #2230 points the entry's page there. Before #2230 the entry named `https://linear.app/settings/security`, which is Linear's workspace admin page. Before that walk, every linear.app settings URL redirected to the old workspace's unfinished welcome flow, so the page had not been reached. C5, C6, C7 and C10 are unwalked |
| Google Calendar (hosted MCP) | yes | **Built, live, not offered.** #2066 (`e370fe9b`) pins the 5 read tools from Google's unauthenticated `tools/list`, with a pre-registered client and `openid` for the account subject. "Not offered" is a **config fact, not a code fact**. The client values arrived on 2026-10-09 (#2111), and the tile was offered until 2026-10-14. OAuth worked, but every tool call failed Google's Developer Preview gate, which is **per Cloud project** (Google named the project in its error on the 2026-10-09 gate test). Since 2026-10-14 `hostedMcp.googleCalendar.enabled` is `false` in values-dev (Wren's ruling), so the instance holds no client values, the catalogue reports `available: false`, and the page shows "not enabled on this instance" with no Connect. Flip the flag once the project is enrolled and an enrolled `list_calendars` call succeeds. Waiting on Sam: a Workspace seat on the domain and Developer Preview enrollment for the connectors project. The OAuth client already exists in a project of its own (Wren: a revoke withdraws every scope a person granted the project) |
| Atlassian (hosted MCP), writes in #2328 | yes (writes since #2328) | **Deployed, hidden on commonly.me since #2530.** The live build (`19de6035d`, 2026-10-24) carries every change below. `hostedMcp.hiddenEntries` in values-dev leaves the entry out of the catalogue and refuses every connect with `unknown_entry`, a reconnect included (`hostedMcpConnect.ts:251`). **C1 blocked on staging** (2026-10-23, `876870378`, #2536): the site's MCP-server-domain allowlist does not include the staging origin, and only a site admin can add it. The entry pins six Jira and Confluence reads. In a 2026-10-22 walk C3 was red: every call failed with "tools/list could not be verified", which TASK-263 attributed to our client never opening an MCP session. #2397 opens one for each list and call; the C3 re-check after its deploy belongs to TASK-263. #2328 adds five named writers: `update_confluence_content`, `create_jira_issue`, `edit_jira_issue`, `transition_jira_issue` and `add_or_edit_jira_issue_comment`, the last marked irreversible. Each is pinned `write`, so every write parks for the connection's owner with its exact arguments, and runs only under the definition it parked under (#2378). The entry now requests write scopes, so a row connected before #2328 holds a token that cannot write, and its owner must reconnect, which the hide refuses until it is lifted. **C4 refused by design** (the C4 column). The walk's checks are below |
| Sentry (hosted MCP), #2329 | yes (#2329) | **Deployed, hidden on commonly.me since #2530**, the same way as Atlassian. **C0, C1 and C2 green on staging, each with a part #2536 did not record** (2026-10-23, `876870378`). Staging has no agent runtime, so C3, C6 and C9 were not walked. #2329 pins 11 named reads and `update_issue`, an irreversible write that parks for the connection's owner with its exact arguments. `get_sentry_resource` is pinned without the vendor's `url` argument, and the vendor's dispatcher, its catalogue and Seer are left out, as ruled in the PR thread. The client is CIMD, and the entry requests five scopes, four of them write. **C4 refused by design** (the C4 column). The walk's checks are below |
| next app | no | Sam's decision; see below |

## Staging connector walk — 2026-10-23 on `876870378`
Expand Down Expand Up @@ -155,6 +157,46 @@ The test gate on "Scout (Default)" was switched back off after the walk. On Sam'

**Membership, hardened across every connector write path (2026-09-27).** A connector now writes only where its owner is a listed pod member, the same rule `createMessage` uses (`isListedPodMember`): relay, inbound, the gate PATCH, install, bind confirm and the gate reconciler (#1940, TASK-161). A pod's creator cannot leave it (#1945, TASK-166), so `createdBy` no longer stands in for membership; the Discord/Slack owner routes follow (#1946, TASK-168); `agent-admin` pods are not a gate target (#1954, TASK-171); the PG chat path reads Mongo membership rather than its own mirror (#1942, TASK-162); and a seat whose own declaration cannot confine a broker is refused at the broker call — every dispatch except the native runtime's in-process one, which is exempt because a hosted turn has no shell, web or file tool to confine (#1971, TASK-175, ruled by Wren). This bears on C9 (authority is bounded) for every channel row above.

## Walk checks: Atlassian writes and Sentry

What a walk checks for the two entries above: C1, C3, C6, C8 and C9 for Atlassian writes, and C0 to C3, C6 and C9 for Sentry. C4 is refused by design for both until TASK-213, so it is not walked. The staging walk of 2026-10-23 (above, #2536) filled the cells staging can reach. Staging has no agent runtime, so a cell that needs an agent to call a tool waits for a walk on commonly.me. #2536 keeps both entries hidden there until that walk passes; the values-dev comment that hides them (#2530) still says a staging walk. A hidden entry refuses every connect, a reconnect included, so the walk needs the hide lifted while it runs. Each result reads `unverified` until a walk replaces it with what it saw and a link to the evidence. Method rules 1 and 2 hold: a stranger account's own session, and every UI cell at 1200 and 390. #2536 does not record which widths it walked.

**A staging green is not a commonly.me green.** Ready means green on commonly.me against the deployed build, so a cell that passes here is recorded as green on staging. Like "green to the external boundary", it keeps that mark when it is copied into the rows above, and it turns green when the same check passes on commonly.me (Method rule 5).

**Record the build.** Everything the walk depends on is on `main` by `4bc63222`, so a build from that commit or later carries all of it. The staging walk's `876870378` does, and so does commonly.me's `19de6035d` (2026-10-24). A cell whose check needs a change the build lacks is `unverified` on that build, not red:

- #2328 and #2329, the entries walked;
- #2397, which opens an MCP session for each list and call; without it every Atlassian call fails (TASK-263);
- #2378 (TASK-247), so a parked call runs only under the definition it parked under. A mismatch is refused with `definition_digest_mismatch`, which the walk cannot provoke against a vendor it does not control;
- #2178 (TASK-210), so the seat learns how a parked call ended.

**C6 for a hosted write.** This type has no inbound path, so no card is answered from the vendor. Here C6 reads as the parked call's card: the connection's owner answers it in Commonly, and the seat receives the ending as `approval.resolved`: `ran`, `failed`, `refused`, `declined` or `expired`, with its code when it has one. An approved call also posts a receipt in the pod that names the approver, the agent, the tool and the ending.

**The walk's grants need an eligible pod.** Every source counts as outsider-writable today, and the mint applies §12's pod rule in `hosted-mcp-connection-scope.md` to every grant (`routes/grants.ts:389`, #2211). So each grant is a pod grant, in a pod that is private, invite-only and unbridged, with every agent in it running inside the backend. For an entry with a write tool, the grant panel offers `read` and "read and write, ask first" (`write-with-confirm`), never unattended `write` (`V2ConnectorTools.tsx:381–386`).

**Atlassian writes (#2328).**

| cell | what the walk checks | result |
|---|---|---|
| C1 | The site's admin first allows the instance's API origin under MCP server domains, as the entry's connect notice asks. A fresh connect then shows Atlassian's consent for the Jira and Confluence write scopes, and the row's `config.grantedScope` holds them. A row connected before #2328 holds no write scope; record whether the page tells its owner to reconnect, and whether a reconnect gains write | **blocked on staging** (2026-10-23, `876870378`, #2536): the allowlist does not include the staging origin. Unverified on commonly.me |
| C3 | In an eligible pod, a hosted agent calls a writer through a `write-with-confirm` pod grant, such as `create_jira_issue`. The call parks, and the card shows the connection's owner its exact arguments. Once approved it runs once, the trail records it ok, and the issue is there when read in Jira | unverified |
| C6 | The owner approves one parked write and declines another. The approved one ends `ran` and its receipt appears in the pod; the declined one ends `declined` (`approval_declined`) without running. The seat receives each ending once as `approval.resolved`, and the trail records each under its card's `approvalId`. A card nobody answers expires after 24 hours, or sooner if its grant ends first (the panel's shortest grant is 7 days), and the sweep then ends it `expired` (`approval_expired`) within five minutes; record it if the walk spans one | unverified |
| C8 | The grant's tools are offered in its pod and not in a second pod the same agent is in. A call against the grant from outside its pod or audience is refused with its code and recorded in the trail | unverified |
| C9 | Bounded: only the five writers are offered, never the vendor's `createConfluenceContent`; a `read` grant refuses a writer; the mint refuses plain `write` with `write_requires_confirm`; the comment tool is marked irreversible. Revocable: revoking a grant leaves its parked cards open; approve one afterwards, and it is refused when it runs, ending `refused` (`grant_revoked`) for the seat and in the pod's receipt. Record whether the card still offers Approve after the revoke. After a revoke on the Connectors page, record whether the connection has left Atlassian's connected apps page, the entry's revoke page. Rotation: if the walk outlasts the access token, a later call succeeds through a refresh | unverified |
| C4 | Refused by design until TASK-213 | refused by design |

**Sentry (#2329).**

| cell | what the walk checks | result |
|---|---|---|
| C0 | A stranger sees the Sentry row on the Connectors page at 1200 and 390, and the catalogue API lists `sentry` with its tools (Method rule 4) | **green on staging except the widths** (2026-10-23, `876870378`, #2536): #2536 does not record whether it walked 1200 and 390 |
| C1 | A stranger connects without an operator, through the CIMD client. At Sentry's consent, select the Triage Issues skill, as #2329 asks. The authorization then appears on Sentry's Authorized Applications page, and the row's `config.grantedScope` records what Sentry granted | **green on staging except the scope** (#2536): consent with only the Inspect and Triage groups, and the row written. Sentry lists the authorization as `mcp.sentry.dev`. Unverified: `config.grantedScope`, which #2536 did not record |
| C2 | The page shows the row connected. A failed call lands as an Activity row that names the failure. Revoke is reachable at 390 | **green on staging except the Activity row** (#2536): a read grant of the 11 reads, made in the walk pod and revoked. Unverified: the Activity row for a failed call, which needs an agent's call, and revoke at 390 |
| C3 | In an eligible pod, a hosted agent completes a read through a pod grant, such as `find_projects`. The trail records it ok, and the answer matches what Sentry's own pages show | unverified |
| C6 | Through a `write-with-confirm` grant, `update_issue` parks, and the card shows the owner its exact arguments. The owner approves one call and declines another. Only the approved one changes the issue in Sentry: it ends `ran` with a receipt in the pod, and the other ends `declined` (`approval_declined`). The seat receives each ending once as `approval.resolved` | unverified |
| C9 | Bounded: the 12 pinned tools are the only ones offered, without the dispatcher, its catalogue or Seer, and `get_sentry_resource` is offered without a `url` argument. Revocable: an `update_issue` call parked before its grant is revoked, then approved, ends `refused` (`grant_revoked`) without changing the issue. A disconnect on the Connectors page calls Sentry's revocation endpoint before deleting the row. Record what Sentry's Authorized Applications page shows afterwards: it lists the client as `mcp.sentry.dev`, and removing it there cuts every Sentry-MCP session on the account (#2536) | unverified. On staging the disconnect's revocation call returned 200 (#2536) |
| C4 | Refused by design until TASK-213 | refused by design |

## Cross-cutting reds

| red | state |
Expand Down
Loading