Skip to content

fix(connectors): purge archived/deleted source items in KB connectors#5880

Merged
waleedlatif1 merged 4 commits into
stagingfrom
fix/confluence-kb-archived-purge
Jul 23, 2026
Merged

fix(connectors): purge archived/deleted source items in KB connectors#5880
waleedlatif1 merged 4 commits into
stagingfrom
fix/confluence-kb-archived-purge

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

A knowledge-base document is only purged when its source item is absent from a full-sync listing — the sync engine has no other removal signal. So any connector that keeps listing archived/trashed/canceled items never drops them, and they stay embedded and keep getting returned by search. This shipped as a real incident: an internal support bot cited an archived Confluence VPN article to a user.

Confluence was the reported case; auditing all 51 other connectors turned up seven more with the same bug.

Confluence — v2 GET /spaces/{id}/pages includes archived pages by default ("By default, current and archived are used"), so archived pages never left the listing. Now requests status=current, with a client-side filter for the CQL path (CQL has no status field) and in getDocument to close the archive-between-list-and-hydrate race.

Seven more connectors:

Connector Leaked into the listing
asana Tasks under archived projects — GET /projects returns archived and active when archived is omitted
google-sheets Tabs of a Drive-trashed spreadsheet, readable by id for 30 days before Sheets starts 404ing
incidentio Canceled incidents — cancelling is incident.io's documented stand-in for deletion ("we don't currently support deleting incidents")
outlook Deleted Items, which GET /me/messages includes on the all-mail path
servicenow Retired knowledge articles — the Table API applies no implicit state filter
webflow Archived CMS items — the staged items endpoint always returns them and offers no filter param
youtube Deleted/private videos, which playlistItems.list keeps returning as placeholder items

Every exclusion keys off an explicit non-current signal and fails open on a missing field, a non-boolean value, or a failed metadata read — wrongly excluding a live item would hard-delete it, which is worse than the bug being fixed. Explicit user filter selections are honoured verbatim; the new defaults apply only when nothing is configured. incident.io gains an explicit "All (including canceled)" option so there is still a way to sync everything.

Also fixed: three silent truncation bugs

asana, outlook, and servicenow each cut a listing short at their configured item cap without setting syncContext.listingCapped, so reconciliation treated the untraversed tail as deleted at the source and hard-deleted it. All three now flag the cap. This is pre-existing and independent of the archived-item bug, but it lives in the same functions and is the more dangerous of the two.

Deploy note

The first full sync after deploy removes previously-indexed archived/deleted items — that is the intended cleanup. A knowledge base where this would delete >50% of documents (and >5) is held back by the engine's existing safety threshold and needs a manual full resync to complete.

Type of Change

  • Bug fix

Testing

414 connector tests pass, including new coverage per connector for the exclusion helpers, the listDocuments/getDocument call sites, and the fail-open paths. Lint and typecheck clean. Every API-semantics claim above was verified against the service's live documentation or OpenAPI spec; not live-tested against real tenants.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 23, 2026 5:27am

Request Review

@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
First full sync after deploy can bulk-remove previously indexed retired/archived content (with existing engine safeguards for very large purges), and connector listing/cap behavior changes affect what deletion reconciliation considers “gone.”

Overview
Knowledge-base connectors only remove stored documents when an item is missing from a full-sync listing. This change stops eight connectors from continuing to list archived, trashed, retired, or otherwise hidden source content, and documents that behavior for users.

Confluence requests status=current on v2 page listings and filters CQL/getDocument with isCurrentContent. Asana lists only active projects (archived=false plus client checks) and drops tasks whose parents are all archived, with matching getDocument logic and a pinned-project exception. Google Sheets treats Drive-trashed spreadsheets as empty listings and rejects them in validateConfig. incident.io excludes canceled incidents by default (with an explicit “All including canceled” option). Outlook on all-mail sync walks Deleted Items and filters those messages in list and hydrate paths. ServiceNow drops retired KB articles when no workflow filter is set, and fixes sys_id parsing for sysparm_display_value=all. Webflow and YouTube filter archived CMS items and playlist entries with explicit private status, respectively.

Asana, Outlook, and ServiceNow also set syncContext.listingCapped when item caps or partial listings would otherwise make reconciliation treat untraversed documents as deleted. YouTube adds stricter trust checks on batched videos.list when excluding Shorts.

User-facing docs add a “Content Removed From View” section and expand the deleted-document FAQ.

Reviewed by Cursor Bugbot for commit f54b125. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes non-current source content from knowledge-base connector listings. The main changes are:

  • Filters archived, trashed, canceled, retired, deleted, and private items across eight connectors.
  • Adds matching checks to document hydration paths where needed.
  • Marks capped Asana, Outlook, and ServiceNow listings to prevent unsafe deletion reconciliation.
  • Adds connector tests and documents how restored source content is re-added.

Confidence Score: 5/5

This looks safe to merge.

  • The updated Confluence paths preserve snapshot reconciliation behavior and reject explicit non-current states.
  • The connector filters fail open on missing or malformed provider fields, avoiding accidental removal of live documents.
  • Listing-cap handling prevents partial listings from being reconciled as complete.
  • No blocking issue remains in the changed code.

Important Files Changed

Filename Overview
apps/sim/connectors/confluence/confluence.ts Adds server-side and client-side current-status filtering plus a matching hydration check.
apps/sim/connectors/asana/asana.ts Excludes archived projects and records when task limits make a listing incomplete.
apps/sim/connectors/google-sheets/google-sheets.ts Checks Drive trash state during listing, hydration, and configuration validation.
apps/sim/connectors/incidentio/incidentio.ts Excludes canceled incidents by default while retaining an explicit sync-all option.
apps/sim/connectors/outlook/outlook.ts Excludes messages under Deleted Items and protects reconciliation when limits truncate results.
apps/sim/connectors/servicenow/servicenow.ts Excludes retired articles, handles display-value field objects, and improves cap detection.
apps/sim/connectors/webflow/webflow.ts Filters explicitly archived CMS items from listing and hydration paths.
apps/sim/connectors/youtube/youtube.ts Uses playlist privacy status to exclude explicitly private videos.

Reviews (4): Last reviewed commit: "fix(connectors): key removal on explicit..." | Re-trigger Greptile

Comment thread apps/sim/connectors/confluence/confluence.ts
Comment thread apps/sim/connectors/confluence/confluence.ts
@waleedlatif1 waleedlatif1 changed the title fix(confluence): exclude archived pages from KB connector listings so reconciliation purges them fix(connectors): purge archived/deleted source items in KB connectors Jul 23, 2026
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/connectors/servicenow/servicenow.ts
Comment thread apps/sim/connectors/asana/asana.ts
…e KB connectors

The sync engine only purges a knowledge-base document when its source item is
absent from a full-sync listing, so any connector that keeps listing
archived/trashed/canceled items never drops them. An audit of all 51 connectors
found seven with this bug:

- asana: list only non-archived projects (the API returns both when `archived`
  is omitted), so tasks under archived projects stop being re-listed
- google-sheets: skip a spreadsheet Drive reports as trashed, which stays
  readable by id for 30 days before the Sheets call starts 404ing
- incidentio: exclude canceled incidents by default (cancelling is incident.io's
  documented stand-in for deletion), with an explicit opt-in to sync them
- outlook: exclude Deleted Items from the all-mail listing, which Graph
  otherwise includes
- servicenow: drop retired knowledge articles, which the Table API returns with
  no implicit state filter
- webflow: drop archived CMS items, which the staged items endpoint always
  returns and offers no way to filter
- youtube: drop playlist entries whose video was deleted or made private, which
  the API keeps returning as placeholder items

Every exclusion keys off an explicit non-current signal and fails open on a
missing field or a failed metadata read, since wrongly excluding a live item
would hard-delete it. Explicit user filter selections are still honoured
verbatim; the new defaults apply only when nothing is configured.

Also flag truncated listings as capped in asana, outlook, and servicenow. All
three silently cut a listing short at their configured item cap without setting
`syncContext.listingCapped`, so reconciliation read the untraversed tail as
deleted at the source and hard-deleted it.
… path

listDocuments deliberately keeps syncing a project the user pinned via the
`project` config field even once it is archived, but getDocument ignored
sourceConfig and applied the all-parents-archived exclusion unconditionally.
For a pinned archived project the listing kept emitting its tasks while every
hydration returned null, so new tasks were dropped as empty and already-indexed
ones were frozen at their last content.

isTaskUnderActiveProject now takes the pinned project gid and keeps any task
reachable through it, matching the listing exactly. The unpinned path is
unchanged and still fails open on missing/non-boolean archived values.
@waleedlatif1
waleedlatif1 force-pushed the fix/confluence-kb-archived-purge branch from 1f4c2fb to d2ee39e Compare July 23, 2026 04:58
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/connectors/google-sheets/google-sheets.ts
…ence

Follow-up to the connector purge fixes, from an independent audit.

YouTube inferred deletion from absence: a playlist entry whose id was missing
from a `videos.list` response was dropped, so a well-formed 200 that returned 49
of 50 requested ids hard-deleted the 50th. Playlist items instead carry a
documented `status.privacyStatus`, available as a free part on a call the
connector already makes, so the extra `videos.list` request is gone along with
its quota-failure and pagination-wedge risks. An item is now excluded only on an
explicit `private`; missing, empty, or unrecognized values keep it.

ServiceNow read every record through a guard requiring a string `sys_id`, but
the listing requests `sysparm_display_value=all`, under which every field —
`sys_id` included — comes back as `{display_value, value}`. The guard rejected
every record, so the retired-article filter was unreachable and the sys_id
object would have leaked into `externalId` and `title` had it not been. Records
are now read through the existing `rawValue` normalizer, which accepts both wire
shapes, and the fixtures use the shape the API actually returns.

Also: resolve the ServiceNow cap ambiguity with `X-Total-Count` so a table that
ends exactly on a page boundary is not read as truncated; stop the Google Sheets
comment claiming a purge the engine's zero-document guard prevents; and assert
the Outlook junk-mail invariant instead of comparing a constant to itself.

Document the behavior change: content archived, retired, or trashed at the
source is now removed from the knowledge base, and restoring it re-ingests it.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit f54b125. Configure here.

@waleedlatif1
waleedlatif1 merged commit 874e742 into staging Jul 23, 2026
20 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/confluence-kb-archived-purge branch July 23, 2026 05:34
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.

1 participant