Skip to content

feat(knowledge): permission-aware knowledge bases behind a feature flag - #7385

Open
waleedlatif1 wants to merge 96 commits into
stagingfrom
feat/permission-aware-knowledge
Open

feat(knowledge): permission-aware knowledge bases behind a feature flag#7385
waleedlatif1 wants to merge 96 commits into
stagingfrom
feat/permission-aware-knowledge

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Permission-aware knowledge bases: a workspace admin can set a connector to sync per member, so each person's searches, document lists, and downloads return only what their own account can open. Nothing is asked of IT and nothing is asked of the admin beyond flipping the switch: a Credential Group is provisioned in the background, every workspace member is invited to it, and each person connects their own account from the knowledge base page or the Search tab. The connector then crawls the source once per member with that member's token, and a document's access list is the set of members whose crawl returned it.

Everything ships behind the knowledge-member-access feature flag (KNOWLEDGE_MEMBER_ACCESS off-AppConfig), and only where Credential Groups are available. Members mode is judged by the workspace allowlist alone, everywhere (creation, switching, the engine, the UI, the read scope); the adminEnabled clause additionally opens the hybrid retrieval default to a platform admin in any workspace. With the flag off every surface behaves exactly as today, and turning it off hides member-scoped documents on the next read without touching any data.

This PR also carries #7376 (Sim Search) merged in, with the Search tab gaining a "Shared with you" section for per-member connectors.

What changes

Access control (always on, no behaviour change until a connector enters members mode)

  • document.acl text[] with a partial GIN index; every read path (search legs, document/chunk loaders, file serving, v1/v2 routes, connector document lists, bulk select-all, tag usage) applies acl && <caller tokens> with scalar binds. Existing rows backfill to {ws} with a fast default.
  • A caller's tokens come from their verified email's active managed credentials in the workspace, restricted to live groups and options; actorless runs (schedules, webhooks, API keys, chat) see workspace-visible documents only. No admin exemption.

Members mode (flag)

  • One migration, 0319_permission_aware_knowledge (expand-only, after staging's 0318 managed-MCP migration): knowledge_connector_member, knowledge_document_observation, knowledge_connector_member_sync_log, connector columns for mode, binding, member-sync state, and pending rewrites. The knowledge_connector.credential_id FK is deferred (contract-pending marker) because legacy rows still hold account.id values; script migration 0011 remaps them first.
  • Member sync engine (background/knowledge-connector-member-sync, cron */5): lock CAS, membership reconciliation from the option's credentials, invitations for anyone who joined the workspace since the last run, a time-boxed drain loop claiming members with FOR UPDATE SKIP LOCKED, first-writer-wins union, hydration through observers, observations, idempotent ACL materialisation, tombstone/resurrect/purge lifecycle, stuck sweep, immediate re-dispatch while members are due.
  • Background provisioning: a members-mode connector with no named group gets one created after the connector (or reuses the option this provider's other members-mode connectors already sync through, never a group curated for something else). Connecting an account through the group's OAuth flow dispatches a member run for every connector on that option, so the person's documents appear without an admin doing anything.
  • Credential Groups: Google Docs and Google Forms join Google Drive as providers; knowledge_connector policy principal + credential_group:OptionId condition; per-option KnowledgeConnectorCredentialAccess statements, which the db-package canonicaliser now carries through; minting audited as CREDENTIAL_ACCESSED.
  • Mode switch under the connector's content lease: grant → rewrite ACLs → flip (members); flip with the rewrite pending → rewrite → release → revoke (workspace); over-budget or interrupted rewrites finish in the next run.
  • Per-member change feeds: Drive changes.list (a lost share arrives as a removal, so access is withdrawn without a full listing); Confluence CQL lastModified for incremental content refreshes. Feed members relist fully weekly as a check.

Connectors that can sync per member (their listing is scoped by the caller's own permissions, and a scope the member cannot reach is treated as a complete empty listing so their access is withdrawn): Airtable, Asana, Bitbucket, Box, ClickUp, Confluence, DocuSign, Dropbox, Gmail, Google Calendar, Google Chat, Google Docs, Google Drive, Google Forms, Google Meet, Google Sheets, Google Slides, Jira, Jira Service Management, Linear, Microsoft Excel, Microsoft Teams, monday.com, OneDrive, Outlook, Salesforce, SharePoint, Zoom. Their listing caps (maxDocs, maxFiles, ...) are hidden and cleared in members mode, since a cap per member would suppress removals. Credential Groups gain managed OAuth policies for the Google services above, for Microsoft (subject oid, tenant tid, id token verified against the common discovery keys, email accepted only when the token asserts it verified), and for Bitbucket (subject account_id, primary confirmed email). Deployment notes: the Bitbucket OAuth consumer must have the Email permission or enrollment fails fast; Microsoft work accounts enroll only when the app registration emits xms_edov, and personal accounts are refused.

Retrieval (flag)

  • Hybrid (full-text + semantic, reciprocal-rank fusion) becomes the default where the flag is on; connector documents record source_modified_at and a bounded rank-based recency boost lets a fresh document edge past a stale one. The knowledge block gains an "Automatic" retrieval mode so the server's default applies.

UI

  • Access section in the add/edit connector modals (Workspace / Per member); the edit modal applies an access switch on its own control, with re-enable for a disabled binding. Connector cards show the per-member badge, member sync status, "Updating access" while a rewrite is pending, and a member run history.

  • Chat, Search mode (Sim Search): under the composer a Sources strip lists every source a person can connect themselves, as chips. One click opens OAuth; a source that needs a site or space (Confluence, Jira, Jira Service Management, Asana, ClickUp, Salesforce) asks for it once in a small modal on the connect that creates it, and everyone after clicks straight through. The first connect of a source in a workspace provisions the "Sim Search" knowledge base and a per-member connector for it and requires a workspace admin (an admin turns a source on once; a member who is first is told to ask an admin); connecting queues the person's member run, so indexing starts on its own, and the chip shows Indexing, then how many documents that person can read. Sources whose listing is not permission-scoped do not appear. Searching lists what the person may read as result rows (source icon, title linking back to the document, source app, author, updated date, and the passage around the first query term with the terms in bold, skipping an email's header block), one per document, with a header that says how many and that the search ran as them, an indexing note while a source is still indexing, source and date filters once the list is long and mixed, hover actions (copy link, Summarize), arrow-key navigation, and an Answer with Sim button that hands the query to the Assistant. The query stays in the box after a search and lives in the URL as q, so a search is a shareable link; emptying the box returns to the sources. Existing chats never open in Search. The agent's knowledge tool returns each result's title, link, connector, and modified time and is told to cite with <source> tags carrying a snippet, so a prose answer ends with the same cards.

  • Search tab: the full catalog with each source's state, the same inline setup, and a "Shared with you" section for per-member connectors in other knowledge bases. Connecting opens the enrollment link in a tab opened inside the click (popup-safe) and polls the membership for up to ten minutes.

  • Chat, Assistant mode: the composer's third mode beside Build and Search, the same vocabulary as Glean. The mode lives in the URL as mode (Build is the clean URL), so a refresh, back, forward, or shared link lands in the same mode as Glean's separate routes do; the new-chat handoff carries the query string across the path swap. The previous mode store is gone. The Home route seeds the knowledge-base list, so a first Assistant question typed before the list arrives is still grounded; a question handed to the Assistant from a result empties the composer as a send does; a chat's composer follows the live search query; and a queued message re-enters the composer in the mode it was written in. Search lists the matching documents; Assistant makes the query a normal turn of the agent in the same chat UI, with the searched knowledge bases attached the way an @ mention attaches one, so the answer is grounded in exactly the documents Search shows and ends with the same <source> cards. Summarize and Answer with Sim on a result switch to Assistant, and a follow-up in the chat stays an Assistant turn. An Assistant turn sends mode: ask, which the chat handler forwards instead of hard-coding agent, and the turn carries a built-in skill: search the attached bases with the knowledge tool first, cite each claim, say so when nothing is found, reach for a connected integration only when the indexed sources cannot answer (live data, or an action the person asked for), and keep suggested follow-ups to questions. The reply's cited documents sit behind a counted button in the action row, beside copy and fork, opening a popover of one row per document; the prose keeps its inline citation chips.

  • Chat, Credential Group credentials: the agent can now act as the signed-in person through a credential they collected under their own Credential Group enrollment. Those credentials appear in the agent's credentials file typed managed_oauth, and a Chat tool call proves itself with a copilot delegation naming the user and scoped to the one credential. Authorization evaluates the group policy's existing actor statement (the enrollee may use the credential under their own enrollment) with no workflow, so nothing the model passes can reach another enrollee's credential; the tool-scope checks and audit are unchanged, and the Go agent needs no change.

Deliberately not in this PR

  • Connectors left in workspace mode, and why: Notion (page access is granted to the integration's shared bot, so a member's listing is not their own permissions); HubSpot (an OAuth token reflects the app's scopes, not the user's CRM permissions, so every member would read every record); Google Vault (admin privilege, eDiscovery records); Slack (Sim's Slack app issues bot tokens, per-person tokens exist only through a workspace's own Slack app, and the identity email cannot be matched); Trello (OAuth 1.0a, no managed enrollment path); the crawler, SFTP, and API-key connectors.
  • A single member listing that outgrows one run's budget is retried from the start next run; a persisted per-member listing cursor is a follow-up with a migration. The content sync log does not record whether a run listed fully or incrementally, so an incremental run can corroborate a suspect listing after a switch to full mode; pre-existing on staging, follow-up with a listing_mode column.
  • Admin mirror mode (service account + mirrored ACLs) — design retained in the plan, not started.
  • Webhook ingress for change feeds; ts_headline snippets; copilot query expansion; title-prefixed embeddings (needs a re-embed).
  • Members-mode creation cannot validate the source config against a credential (the admin may not be enrolled yet), so a mistyped folder or space id yields an empty connector rather than a save error; the member run history shows every member listing nothing.
  • A provisioned group is created before the connector row; if the connector save then fails the group stays in Settings, and because a group nobody syncs through is never chosen automatically, the next attempt creates another ("Google Drive 2"). Delete the orphan first.
  • Choosing an existing, curated Credential Group in the Access picker invites the whole workspace to it on the first run; the field says so, and a group nobody syncs through is never chosen automatically.
  • The edit modal's "Browse with" picker lists only accounts already connected in Integrations; an admin with none connects one there first.
  • Members are asked for the connector's full Drive scope, not drive.readonly; a read-only member scope is a follow-up.
  • Clicking Connect in the app mints a fresh enrollment link, which retires the one in the invitation email; use one or the other.
  • After OAuth the enrollment page still offers Submit; it is not needed (an in-progress enrollment already counts), and the knowledge base page updates on its own.
  • Search quality follow-ups from the Onyx comparison, not in this PR: title and path in the embedded text (needs a re-embed), a reranker on by default, smaller chunks with heading propagation, language-aware full-text config, query rewriting. Done here: hybrid legs over-fetch before fusion, the vector leg's iterative scan fills limits past the default candidate pool, the recency weight is bounded to a few places, and results carry the document's link, connector, and modified time everywhere.
  • v1/v2 search: a caller that omits searchMode gets hybrid + recency where the flag is on (was always vector). Intended, and called out here because the versioned API's default becomes workspace-dependent.

Demo setup

  1. Add knowledge-member-access: { workspaceIds: ["<demo workspace id>"] } to the hosted feature-flags AppConfig document and start a fast deployment; the demo workspace also needs credential-groups on and an Enterprise plan.
  2. Run migration 0319 (expand-only) and script migration 0011, and deploy the Trigger.dev tasks from this branch (the member run is the new knowledge-connector-member-sync task; without it every dispatch fails and the cron retries).
  3. Demo members must be Sim users with a verified email that is also their Google Workspace address: the OAuth callback refuses an account whose email differs from the invitation's.
  4. Chat → switch the composer to Search (as a workspace admin the first time) → click Gmail, Google Drive, or Google Calendar on the Sources strip → finish OAuth in the tab. The chip reads Indexing, then the document count; other members click the same chip to connect their own account. Confluence or Jira ask for the site (and space or project) once.
  5. Type a query: the documents the signed-in person may read, one row per document, with the source, updated date, and the matching passage. Summarize on a row or Answer with Sim switches the composer to Assistant and hands it to the agent, whose answer ends with the same source cards; pick Assistant in the mode switcher to type questions directly. The URL carries q, so the search can be shared.
  6. Unshare a document in Drive → "Sync members now" (a manual run now makes every member due) → the member loses it on their next search; nobody else ever sees it.

Verification

  • bun run type-check (apps/sim, packages/db, packages/testing), bun run check:audits, bun run check:migrations, biome — clean. The tool-registry boundary baseline was re-recorded for the knowledge module's new imports.
  • Six independent review passes over the stack (schema/enforcement, sync extraction, credential-group plumbing, member engine/freshness, mode switch/contracts/UI, retrieval) with findings fixed; then a comprehensive re-audit (provisioning, every enabled connector's listing semantics, the Search wiring, the collapsed migration) and a dead-code/indirection sweep, with their findings fixed too.
  • Final swarm: the eight /cleanup passes (effects, memo, callback, state, React Query, emcn design, url-state, comments) plus seven line-by-line audits (schema/enforcement, member engine, credential-group plumbing, contracts/routes/hooks, UI/UX, and two full LOC sweeps). Defects found and fixed there: mutation responses failed contract validation (viewerMembership); bulk enable/disable wrote to documents outside the caller's scope; the staleness sweep tombstoned deferred connectors; Gmail and Calendar kept a 500-item cap after the switch; Jira/ClickUp/Asana/Linear/Dropbox classified "scope unavailable" on the wrong status; a Credential Group serving a members-mode connector could be deleted under it; the Search tab showed "No connectors found" beside member rows; a connector could not be rebound when exactly one other group existed; the iterative vector scan ran for every workspace; the member-scoped hint ignored the flag.
  • Full apps/sim suite green after the swarm (2900 files / 39,900 tests), plus type-check on apps/sim, packages/db, packages/testing, check:audits, check:migrations, drizzle-kit check, and biome.
  • Review round on the Sim Search push: 54 findings triaged by area (UI and contracts, member engine, orchestration and Credential Groups, connectors) with 47 fixed and 7 answered as not applicable; notable fixes: connector-owned documents are processed as the system (members-mode documents are hidden until observed, so the processor denied its own read), Gmail's unlimited cap stopped after one page, Box/Monday/JSM/Salesforce/Calendar/Confluence scope classification, lease proofs before every document write, a locked stale-member sweep, member schedule writes on interval and resume, the Credential Group option lock around binding changes, an advisory lock around a workspace's first Sim Search connect, and the execution principal threaded into knowledge file hydration.
  • Then the eight /cleanup passes and the four /simplify angles over the Sim Search surface, applied in pass order: the connect flow lives in the enrollment hook and is shared by the strip and the Search page, one availability gate serves every members-mode refusal, setup fields are precomputed on the catalog, the chip gained a trailing adornment slot, a search is a shareable link (q) that is dropped when the box empties or the mode leaves Search, and the processor takes its source access as one object.
  • A second /cleanup over the results and sources: result rows use the chat surface's row rhythm with hairlines, OverflowText titles and meta lines, an icon button for Copy link, a ghost Summarize, keyboard-revealed actions, and a linkless document rendered in the same row; the source and date filters live in the URL beside q; the member-connector query is gated with enabled and cancelled before the optimistic queue write.
  • Second review round (21 findings, 18 fixed, 3 answered): per-member Teams, SharePoint, and OneDrive listings skip a channel or descendant folder the member cannot reach instead of failing the whole member; Confluence pins its relative time clause across pages; blank Gmail and Outlook caps keep their defaults; the processing queue proves the run lease inside the queue transaction before marking or dispatching (the reclaimed-run race Greptile flagged); the copilot composer no longer offers Search mode; a first connect shows Connecting until its membership row exists.

emir-karabeg and others added 16 commits September 1, 2026 17:25
- Build/Search mode switcher in the chat composer; Search mode swaps the
  suggested actions for Connect Sim Search rows that open the OAuth modal
- Search tab beside Integrations and Skills: a personally scoped catalog
  of knowledge-base connectors with connect-in-place rows and a
  connected-credential page without sharing or members
- <source> special tag contract: inline citation chips at the citation
  point plus a scrolling, right-faded sources strip under the reply
- chip `shape` variant so a fully round chip is chip chrome, not a class
  override
- keep the connector catalog out of the workflow editor's module graph
  and record the new routes in the boundary baseline
- validate <source> urls by parsing them and requiring a host, so a
  malformed value never renders a dead citation link
- gate Search-mode suggestions on deployment OAuth availability, sharing
  the predicate with the Search catalog
- match connected credentials across a service's additional provider ids
  (Salesforce sandbox) and count them in connector telemetry
- collect footer sources from the rendered text segments, covering a
  block-less message's fallback text and excluding subagent lanes
- use a distinctive citation-link sentinel and an absolute import for
  SuggestedActions
- teach the email tokens transcription test the composed chip geometry
Adds document.acl (sorted access tokens, fast default '{ws}') with a partial
GIN index and a token-shape CHECK, source_modified_at, the members-mode
columns and lease on knowledge_connector, a real FK on credential_id (left
NOT VALID until script migration 0011 has remapped legacy account ids), and
three tables: knowledge_connector_member, knowledge_document_observation,
knowledge_connector_member_sync_log.

The document contract stops passing unknown keys through so the new column
and storageKey never reach clients.
Pure building blocks for document-level access: the token vocabulary and
its single sorting/validation site, the subject token derived from a managed
credential row, the read-side predicate (scalar binds, literal ARRAY), the
per-principal scope resolver with a lazy memoising provider, the branded
system scope, and the members-mode sync limits. Nothing consumes them yet.
Every document loader, chunk list, tag usage, search leg, metadata lookup,
and knowledge-base file serve now takes a required access scope and applies
the shared ACL predicate. Context resolvers attach a lazy, memoised scope
provider derived from the acting principal and load documents through it,
so a document the caller may not read is absent from the first read. Raw v1
routes and legacy helpers resolve the same scope; the outbox handler is the
one branded system-scope caller. Vector legs run inside a transaction that
enables pgvector's relaxed iterative scan (with a 42704 fallback) so a
narrow ACL still fills topK. Forked workspaces copy documents as '{ws}'.

Behaviour is identical today: every row carries the default '{ws}' token.
…engine

Splits the content sync engine into four modules without changing behaviour:
sync-lock (lease predicates, heartbeat, and a SyncRunLease the stages run
under), sync-persistence (storage and document-row writes), sync-primitives
(the pure decision functions plus the six orchestration stages: listing
pass, owned-corpus load, classification, hydrate/persist/dispatch, deletion
reconciliation, and the stuck-document sweep), and sync-engine (lock
acquisition, terminal state, and executeSync composed from the stages with
its original closures). The stages take the token, hydration, and lease as
inputs so a second engine can run the same guards under a different lease.

Tests keep every assertion; only import paths moved with the symbols.
Registers Google Drive as a Credential Group provider (provider list,
adapter registry, and the Google managed-OAuth policy), marks the Drive and
Confluence connectors as permission-scoped listings with the config fields
that would cap them, and extends the Credential Group policy with a
knowledge_connector principal and a credential_group:OptionId condition so a
group can grant one connector the credentials collected under one option.

The policy codec gains one KnowledgeConnectorCredentialAccess statement per
option, decoded by SID; workflow-access edits now carry connector grants
through untouched. Knowledge-side helpers grant and revoke the binding under
the policy's revision CAS, mint a member token only when the policy names
the connector for the credential's option (audited per access), enumerate an
option's credentials in every state for membership reconciliation, and
validate a members-mode binding before anything is touched. Nothing calls
them yet.
Adds the engine that crawls a knowledge connector once per Credential Group
member with that member's own token and derives each document's ACL from who
observed it. A run takes the member lease (mutually exclusive with the
content engine's), finishes any pending access rewrite, mirrors the option's
credentials onto member rows (active or suspended, tokens rewritten, departed
members dropped), then claims due members one at a time until its budget
ends: each member's listing is admitted into a first-writer-wins union, every
listed document is hydrated once through its observers' tokens, observations
are recorded, removals follow only a full, complete, non-suspect listing, the
affected ACLs are rematerialised from the observation graph, and the document
lifecycle tombstones the unobserved, resurrects the re-observed, and purges
the long-unobserved under the lease. The run re-dispatches itself while
members remain due.

Around the engine: a member queue mirroring the content dispatch (token,
lease, lost-hand-off release), a Trigger.dev task on its own queue, a cron
scheduler that reclaims stale leases and lost dispatches, closes orphaned run
logs, sweeps members whose crawls stopped, and dispatches due connectors, and
the Docker and Helm cron entries. The content engine and its dispatch refuse
members-mode connectors, workspace-mode syncs restore the workspace ACL on
completion, document writes carry the ACL each mode needs, the hard-delete
guard can verify the member lease, and the Drive and Confluence connectors
report a scope the member cannot reach as an empty listing rather than an
error. Nothing enters members mode yet.
Wires the members-mode engine to the connector surface. A connector can be
created in members mode (admin only) bound to a Credential Group option, or
switched between modes through a new admin-only access operation and route:
the switch takes the connector's content lease, rewrites the documents' ACLs
to the new mode's default in bounded batches (finishing under the member run
when the request budget runs out), grants or revokes the option's credentials
in the group's policy, flips the mode, and queues the first run of the new
engine. Members-mode source edits refuse listing caps and queue a member run,
a manual sync routes to the member queue, keep-documents deletion is refused,
and deletion revokes the grant. Contracts carry the access mode, the binding,
the member-sync state, the member summary, and member run logs; the member
scheduler and dispatch honour a paused connector.
Add an Access section to the add and edit connector modals so a workspace
admin can sync a permission-scoped connector once per credential-group
member. The connector card shows the per-member badge, member sync status,
and a member run history in place of the content sync history.
Knowledge bases report whether a live connector syncs per member, and the
knowledge block's selector explains that such documents depend on who
triggers the run.
Connectors may open a change feed over a member's view of the source.
Google Drive reads changes.list, where a lost share arrives as a removal,
so a member's access is withdrawn without waiting for a full listing;
Confluence filters its CQL listing by lastModified for incremental
content refreshes. Each member stores where their feed resumes, and a
member with an open feed is relisted fully only weekly as a check.
Search fuses the full-text and semantic legs unless a caller asks for
vector-only, at every surface: internal, v1, v2, the knowledge block,
and Sim's own knowledge tools. Connector documents now record when the
source last changed them, and a recently modified document edges past
a stale one of similar relevance.
…rden the stack

Add the knowledge-member-access feature flag (workspace allowlist and
platform admins via AppConfig; KNOWLEDGE_MEMBER_ACCESS off-AppConfig). It
gates members mode at creation and switching, the member engine, and the
hybrid-by-default retrieval with its recency boost; the connector modals
read it from the workspace host context. The knowledge block's retrieval
mode gains an Automatic option so the server's default applies.

An admin with no credential group for the connector's provider can create
one from the Access field; the group is named after the connector and
members are invited from Settings.

Review fixes: manual-only connectors no longer re-dispatch member runs
forever; ACL materialisation is idempotent so a run that died mid-way is
repaired; incomplete listings retry at once only when they can resume;
suspended members keep their observations; a scope the member cannot reach
closes their change feed; the workspace ACL restore runs inside the
completion lock and only for workspace-mode connectors; the mode switch
grants before rewriting and flips in one transaction, clearing listing
caps; minting checks group, option, and enrollment status; connector
document lists, bulk select-all operations, and tag usage stats apply the
access scope; the connector card reads the member engine's status; the
edit modal applies an access switch on its own instead of racing the
settings save.
…osed when the flag is off

Members mode is gated by the workspace at creation, switching, in the
member engine, and in the workspace host context, so no gate can open a
mode another will refuse; the flag's admin clause now affects only the
retrieval default, and search passes the signed-in person rather than a
billing owner or key creator. Member tokens count only where the feature
is on, so turning it off hides member-scoped documents on the next read.
New and reactivated members are due at once so a drain re-dispatches
until everyone has been listed. Leaving members mode flips first under
the lease with the rewrite marked pending, then rewrites; a failed move
between options of one group restores the previous option. The edit modal
closes after a switch and cannot re-choose per-member where the flag is
off; connector document exclusion honours the caller's scope.
@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Sep 2, 2026 8:45pm UTC

Request Review

Choosing per-member access no longer asks the admin for anything: the
server reuses the workspace's one credential group for the connector's
provider or creates one, invites every workspace member to connect, and
each member run invites people who joined since. A member's only step is
to connect: the knowledge base page shows a banner for every per-member
connector they have not connected, and a new enroll route mints their
enrollment link on demand so the invitation email is never needed. The
connector list reports the viewer's membership for that banner.

The permission-scoped listing CI test now compares OAuth provider ids,
which is what the runtime checks. The knowledge page module baseline is
re-recorded for the provisioning graph.
Choosing per-member access provisions a group named after the connector
and the first member run invites everyone in the workspace, under the
run's lease; the request itself sends nothing, so nobody gets two links.
A member connecting queues a member run for every connector on that
option, and the knowledge base page shows what each viewer must do —
connect, reconnect, verify their email, or nothing — opening enrollment
in a new tab and refreshing until they are connected.

Every gate reads one availability check (flag and Credential Groups).
When it is off, readers get no member token, the engine waits instead of
suspending anyone, and the field cannot re-choose per-member access.
Member tokens go only to current workspace members with a live group and
option. A connector that just entered members mode never tombstones its
documents before a member has listed, the stale sweep leaves paused and
manual connectors alone, a disabled member sync is re-enabled by
re-applying its binding, leaving members mode forces a full content
sync, and by-id bulk operations honour the caller's scope. Deleting a
per-member connector always takes its documents.
One read returns every per-member connector in the workspace with where
the viewer stands on each, so a surface outside the knowledge base can
ask them to connect. The member sync status enum now has one home in
lib/knowledge/types.
Collapses the stack's two migrations into one, 0318_permission_aware_knowledge,
generated from the schema on top of staging's 0317 and written in the
expand/contract form the migration gate requires. The knowledge_connector
credential_id foreign key is deferred until script migration 0011 has
remapped legacy rows in production. Also makes the on-connect member sync
dispatch reachable: the grant persistence returned before it ran.
The Search tab lists the knowledge bases whose connectors sync per member
under "Shared with you", with where the viewer stands on each and the same
one-click connect the knowledge base page offers. One hook opens
enrollment in a new tab and refreshes the surface until the viewer is
connected; the knowledge base banner uses it too.
…connector

Google Slides, Docs, Forms, Calendar, Gmail, Notion, Jira, Jira Service
Management, Box, Dropbox, monday, ClickUp, Asana, Salesforce, Linear,
DocuSign, and Zoom list only what the caller's own account can read, so
each now declares its listing caps and can sync per member. A shared
listing-scope error lets a connector report a folder, space, board, or
calendar the member cannot reach as a complete listing of nothing; Notion
and Asana report it from their typed errors, Dropbox from its 409, and
the Atlassian connectors also from an unreachable site. Google Docs and
Forms join the credential-group providers so their accounts can be
collected per person.
- The credential-group policy canonicaliser carries knowledge-connector
  statements through instead of rejecting a third statement
- Google Docs and Box report an unreachable scope so a member's access is
  withdrawn rather than retried forever; Notion leaves members mode because
  its page access is granted to the shared integration bot
- Provisioning reuses a Credential Group only when other members-mode
  connectors already sync through it, never a group curated for something
  else; the fast dispatch on connect accepts the same statuses as the sweep
- Enrollment opens its tab inside the click so popup blockers cannot swallow
  it, and the membership poll stops after ten minutes
- Session-only routes use session auth; members mode stores the cap-stripped
  config; one liveness rule for members and managed bindings
- Drop dead code: the access barrel, system.ts and rank.ts folded into their
  siblings, unused ACL helpers, the unread change_cursor_at column, stale
  documentation, and the engine/provisioning/queue import cycle
- Bump the chart version for the member-sync cron and secret
- Pass an access scope to getDocuments in the list-convention test and
  resolve a knowledge scope only for knowledge-base file reads
- Drop memoisation nothing observes, read the enrollment error from the
  mutation, hoist the empty connector list, use the default Cancel variant,
  let Badge own its gap, and keep the sidebar lit on the Search tab
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

Comment thread apps/sim/lib/knowledge/connectors/member-sync-engine.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 350 files

Not reviewed (too large): apps/sim/lib/knowledge/connectors/sync-engine.ts (~2,879 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/lib/credentials/environment.ts
Comment thread apps/sim/lib/copilot/chat/ask-mode.ts
…ace and carry only chat params on the handoff
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

…s disabled on every mint, workflow runs included

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 351 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Not reviewed (too large): apps/sim/lib/knowledge/connectors/sync-engine.ts (~2,879 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Re-trigger cubic

…t, follow the search query in a chat, restore a queued message's mode, and seed the base list
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 352 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Not reviewed (too large): apps/sim/lib/knowledge/connectors/sync-engine.ts (~2,879 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants