Skip to content

fix: skip github-deleted records in shadow diff (CM-1473) - #4778

Merged
mbani01 merged 4 commits into
mainfrom
fix/skip-deleted-records-shadow-diff
Sep 25, 2026
Merged

mbani01 merged 4 commits into
mainfrom
fix/skip-deleted-records-shadow-diff

Conversation

@mbani01

@mbani01 mbani01 commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Problem

The daily shadow-diff Temporal workflow compares Nango records against our shadow GitHub connector records and persists missing_in_shadow mismatches. Nango never retracts deletes, so records that were deleted on GitHub after Nango captured them (issue/PR comments, pull requests + their timeline events, review comments, forks) surface as steady false-positive missing_in_shadow noise — around 200-700/day on busy repos (e.g. openclaw/openclaw bot comments, NousResearch/hermes-agent deleted-account PRs and children), all confirmed via GraphQL node(id:) returning null/NOT_FOUND.

An analogous confirm-before-flag filter already exists for force-pushed commits (forcePushedCommits.ts), but commit sourceIds are SHAs, not GraphQL node ids, so it can't cover these record kinds.

Fix

Adds services/apps/connectors_worker/src/deletedRecords.ts, mirroring forcePushedCommits.ts's structure:

  • hasDeletedRecordCandidates / dropConfirmedDeletedRecords apply to any sync other than pull-request-commits, since every other record kind's sourceId is a GitHub GraphQL node id (issue/PR comments, pull requests, reviews, review comments, forks, discussions). Synthetic composite ids generated for PR/issue timeline events (gen-..., not real node ids) are excluded from lookup.
  • Candidates are looked up in batches of up to 100 via a single nodes(ids: [...]) GraphQL query, instead of per-record calls.
  • A response with top-level data and per-id null entries (with NOT_FOUND in errors) is treated as a valid confirmation that the node is deleted — dropped from the mismatch list. A response with no top-level data (request error, timeout, forbidden, etc.) is treated as unconfirmed and the record is kept as missing_in_shadow (fail open).
  • A 60s time budget (matching the force-push filter) bounds total confirmation work per unit; once exhausted, all remaining unchecked candidates are kept.
  • Wired into runShadowDiffForChannel right after the existing force-push filter, reusing the same GitHub confirmation HTTP client/token pool. Logs how many records were confirmed deleted vs kept unconfirmed.

Validation

  • New Vitest unit tests in services/apps/connectors_worker/src/deletedRecords.test.ts (written and run locally, not committed per repo convention): confirmed-deleted dropped, alive kept, request-error kept, no-data-with-NOT_FOUND-errors treated as confirmation, commit sync untouched, synthetic gen- ids skipped, budget-exhaustion keeps the remainder unconfirmed. All 10 pass, plus the existing shadowDiffActivities.test.ts suite (11 tests) still passes.
  • pnpm lint:fix + pnpm format run; npx oxfmt --check and npx oxlint --deny-warnings pass clean on the changed files.
  • npx tsc -b services/apps/connectors_worker/tsconfig.json (forced full rebuild) passes with no errors. Root pnpm tsc-check still fails on the 2 pre-existing unrelated errors (resolveCdpEmails.test.ts TS2550, backend/src/database/models/index.ts TS2339).

🤖 Generated with Claude Code

Signed-off-by: Mouad BANI <mouad-mb@outlook.com>
Copilot AI balanced review requested due to automatic review settings September 25, 2026 16:18
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@cursor

cursor Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes which shadow-diff mismatches are persisted and adds GitHub GraphQL calls in the daily workflow; incorrect confirmation logic could hide real drift, though unconfirmed cases are kept as mismatches.

Overview
Adds a GitHub deletion confirmation step to the shadow-diff pipeline so missing_in_shadow mismatches caused by records deleted on GitHub (but still present in Nango) are dropped instead of persisted as noise.

New deletedRecords helpers mirror the existing force-push filter: eligible missing_in_shadow rows (real GraphQL node sourceIds, not gen- synthetics or pull-request-commits) are checked in batches via nodes(ids: [...]). Only null nodes with per-id NOT_FOUND are removed; API failures, ambiguous nulls, and budget/timeouts fail open and keep the mismatch.

runShadowDiffForChannel now shares the same GitHub confirmation HTTP client for force-push and deleted-record checks, with logging for confirmed-deleted vs kept-unconfirmed counts. Vitest coverage exercises batching, error handling, sync/id exclusions, and the 60s budget.

Reviewed by Cursor Bugbot for commit 18a850c. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Partial GraphQL errors can suppress valid mismatches, synthetic timeline records remain unresolved, and tests are uncommitted.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
What changed in this PR

Adds GitHub deletion confirmation to reduce false-positive shadow-diff mismatches.

Changes:

  • Batch-checks missing records through GitHub GraphQL.
  • Integrates deletion filtering with existing force-push confirmation.
File Description
deletedRecords.ts Implements deletion confirmation and filtering.
shadowDiffActivities.ts Applies filtering during shadow diff processing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread services/apps/connectors_worker/src/deletedRecords.ts Outdated
Comment thread services/apps/connectors_worker/src/deletedRecords.ts
Comment on lines +79 to +84
export async function dropConfirmedDeletedRecords(
syncName: string,
mismatches: IShadowDiffMismatch[],
http: ConnectorHttp,
log: Logger,
): Promise<DeletedRecordFilterResult> {

@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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3b493a7. Configure here.

Comment thread services/apps/connectors_worker/src/deletedRecords.ts Outdated
Signed-off-by: Mouad BANI <mouad-mb@outlook.com>
Copilot AI review requested due to automatic review settings September 25, 2026 16:29
Signed-off-by: Mouad BANI <mouad-mb@outlook.com>

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

The described deletion-filter regression tests must be committed before approval.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (2)

Copilot AI review requested due to automatic review settings September 25, 2026 16:32
@mbani01
mbani01 merged commit 0af4b30 into main Sep 25, 2026
11 checks passed
@mbani01
mbani01 deleted the fix/skip-deleted-records-shadow-diff branch September 25, 2026 16:36

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The implementation is scoped, fail-open, and adequately tested, with previous substantive feedback addressed.

Review effort: Balanced
Findings: 1 Low severity

Open (1)

Copilot AI review requested due to automatic review settings September 25, 2026 16:37

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Per-unit 60-second budgets can collectively exceed the activity’s five-minute timeout before results are persisted.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)

Comment on lines +200 to +202
if (hasDeletedRecordCandidates(unit.syncName, result.mismatches)) {
const { mismatches, confirmedDeletedCount, keptUnconfirmedCount } =
await dropConfirmedDeletedRecords(unit.syncName, result.mismatches, http, svc.log)
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.

3 participants