LearnNewsExamplesServices
Frontmatter
id10092
title[bug] IssueSyncer delta-sync is blind to comment deletions — updatedAt does not bump
stateClosed
labels
bugairegressionarchitecture
assigneestobiu
createdAtApr 19, 2026, 1:44 PM
updatedAtMay 15, 2026, 2:42 PM
githubUrlhttps://github.com/neomjs/neo/issues/10092
authortobiu
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtApr 19, 2026, 2:03 PM

[bug] IssueSyncer delta-sync is blind to comment deletions — updatedAt does not bump

Closed v13.0.0/archive-v13-0-0-chunk-4 bugairegressionarchitecture
tobiu
tobiu commented on Apr 19, 2026, 1:44 PM

The Problem

IssueSyncer's delta-sync relies on issue.updatedAt as the invalidation signal, via filterBy: {since: $since} in FETCH_ISSUES_FOR_SYNC. This gate is blind to comment deletions: GitHub does not bump issue.updatedAt when a user deletes a comment. The sync engine never revisits the affected issue, and the local markdown retains:

  • Stale frontmattercommentsCount frozen at the pre-delete value
  • Stale timeline — if the deleted comment was one we had rendered, its body sits in the markdown indefinitely

Concrete reproducer, found during the detector sweep for #10090: issue #9535. Local frontmatter at time of detection: commentsCount: 11. Live GraphQL issue.comments.totalCount: 10. Local updatedAt: 2026-04-01T02:28:35Z matched GitHub exactly — but a comment had been deleted post-sync and the local file stayed frozen for weeks. Silent disagreement with upstream.

This is a distinct bug class from #10090. That ticket ensured full event retrieval when we DO fetch an issue (timelineItems pagination). This ticket is about deciding when to fetch in the first place. The recovery primitive from #10090GH_SyncService.refetchIssuesByNumber — is the right healing endpoint here; what's missing is the detection trigger.

Architectural Reality

  • FETCH_ISSUES_FOR_SYNC uses filterBy: {since: $since} — GitHub filters on updatedAt >= since. Deleted-comment drift never crosses that gate.
  • runFullSync already has the timeline-scan workaround from #7948 for relationship events that don't bump updatedAt (sub-issues, blocking). But GraphQL exposes no ISSUE_COMMENT_DELETED_EVENT — comment deletions can't be detected the same way; the timeline only shows the surviving nodes.
  • We already fetch comments { totalCount } on every sync, but when delta-sync skips an issue, that totalCount check never runs.
  • REST /issues/N/timeline DOES expose DeletedCommentEvent, but adding a parallel REST surface for a single edge case is the wrong trade.

Options Considered

A) Full-scan sweep periodically — drop the since: filter every Nth sync. Simple, but O(all issues) cost on a timer that's hard to tune.

B) Totals-only sentinel pass — add a lightweight query FETCH_ISSUE_TOTALS_BATCH that returns {number, comments{totalCount}} for all tracked issues with no since: filter, no body, no timelineItems. Compare against stored metadata; mismatches feed into refetchIssuesByNumber. ~1 rate-limit unit per 100 issues. Recommended.

C) Per-repo events API polling — listen for IssueCommentEvent.action=deleted. New infra surface; not justified for a deletion-detection corner case.

Fix Approach (option B)

  1. Add FETCH_ISSUE_TOTALS_BATCH(numbers[]) to issueQueries.mjs: returns only {number, comments{totalCount}} per issue. No body, no timelineItems, no since: filter.
  2. Extend MetadataManager metadata shape to persist commentsTotal per issue alongside existing updatedAt, contentHash.
  3. Add IssueSyncer.detectStaleCommentsCounts(metadata) that runs inside runFullSync after the delta pull:
    • Partition metadata.issues into batches (100/query).
    • Fetch current comments.totalCount per batch.
    • Any mismatch → accumulate into a Set, feed to refetchIssuesByNumber.
  4. Playwright spec: happy path (no mismatches is a no-op) + deletion path (injected 11→10 drift recovers the affected file).
  5. Observability: surface a staleCommentsCount count in runFullSync logs and return-stats, mirroring the #10090 pagination-warn pattern.

Avoided Traps

  • Reusing the timeline-scan pattern from #7948. Won't work — no deletion event type in GraphQL timelineItems.
  • Raising sync frequency. Tightens the drift window; does not close it.
  • Adding a webhook or polling a second API surface. New infra dependency for an edge case already addressable via existing GraphQL primitives.

Acceptance Criteria

  • FETCH_ISSUE_TOTALS_BATCH query fetches {number, comments{totalCount}} with no since: filter
  • MetadataManager persists commentsTotal per issue in the sync-metadata file
  • IssueSyncer.detectStaleCommentsCounts(metadata) runs inside runFullSync after the delta pull; mismatches are fed into refetchIssuesByNumber
  • Playwright spec covers happy path (no mismatches = no writes) AND the deletion-detection path (injected 11→10 drift, verify recovery)
  • runFullSync logs + return-stats expose a staleCommentsCount count

Related

  • #10090timelineItems pagination fix (same session discovered both bugs)
  • #7948 — prior "Timeline-Based Relationship Discovery" workaround for a related updatedAt blind spot

Origin Session ID

d9eb5e76-5430-45f7-b3ea-8600664d28f9

tobiu added the bug label on Apr 19, 2026, 1:44 PM
tobiu added the ai label on Apr 19, 2026, 1:44 PM
tobiu added the regression label on Apr 19, 2026, 1:44 PM
tobiu added the architecture label on Apr 19, 2026, 1:44 PM
tobiu assigned to @tobiu on Apr 19, 2026, 1:49 PM
tobiu referenced in commit 386073d - "fix(github-workflow): sentinel sweep for comment-deletion drift (#10092) on Apr 19, 2026, 1:56 PM
tobiu cross-referenced by PR #10093 on Apr 19, 2026, 2:00 PM
tobiu referenced in commit 88307bf - "fix(github-workflow): sentinel sweep for comment-deletion drift (#10092) (#10093) on Apr 19, 2026, 2:03 PM
tobiu closed this issue on Apr 19, 2026, 2:03 PM
tobiu cross-referenced by #10094 on Apr 19, 2026, 2:20 PM
tobiu cross-referenced by #10095 on Apr 19, 2026, 2:20 PM
tobiu cross-referenced by #10096 on Apr 19, 2026, 2:20 PM
tobiu cross-referenced by #10097 on Apr 19, 2026, 2:25 PM
tobiu cross-referenced by PR #10098 on Apr 19, 2026, 2:29 PM
tobiu cross-referenced by PR #10099 on Apr 19, 2026, 3:10 PM
tobiu cross-referenced by #10110 on Apr 19, 2026, 11:40 PM
tobiu cross-referenced by PR #10111 on Apr 19, 2026, 11:53 PM
tobiu cross-referenced by PR #10114 on Apr 20, 2026, 1:11 AM