LearnNewsExamplesServices
Frontmatter
id10090
title[bug] IssueSyncer silently drops content past timelineItems cap — handoff comment on #10030 lost
stateClosed
labels
bugairegressionarchitecture
assigneestobiu
createdAtApr 19, 2026, 1:09 PM
updatedAtMay 15, 2026, 2:42 PM
githubUrlhttps://github.com/neomjs/neo/issues/10090
authortobiu
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtApr 19, 2026, 1:41 PM

[bug] IssueSyncer silently drops content past timelineItems cap — handoff comment on #10030 lost

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

The Problem

IssueSyncer silently truncates issue content once the timelineItems GraphQL connection grows past maxTimelineItemsPerIssue (50). Concrete reproducer: comment 4275716860 on Epic #10030 — a session handoff — was authored 2026-04-19T10:33:35Z. The local file resources/content/issues/issue-10030.md received correct updatedAt and commentsCount: 2 metadata, but the comment BODY never landed. The sync engine reported success.

Root cause: since the unified-timeline refactor (#8527 / #8528, Jan 2026), IssueSyncer.#formatTimelineEvent renders IssueComment events inline through the issue.timelineItems.nodes channel. The GraphQL query asks for timelineItems(first: $maxTimelineItems); GitHub orders this connection oldest-first (no orderBy available). Once total events exceed 50, the tail — including newly-authored comments — is dropped. No guard checks pageInfo.hasNextPage or compares fetched comments to issue.comments.totalCount.

Architectural Reality

  • Failure is silent because the frontmatter channel (issue.updatedAt, issue.comments.totalCount) is a separate GraphQL scalar path, unaffected by the timeline cap. Metadata tracking and content rendering have diverged.
  • PR-based workflow is accelerating the trigger: each PR cross-references the Epic, consuming timeline slots. Epics will hit 50 faster now than when the unified-timeline design landed.
  • REST /issues/10030/timeline reports 55 total events; the local file renders exactly 50, confirming the cap is the cutoff.
  • Files touched by the fix live inside the MCP server tree (ai/mcp/server/github-workflow/services/sync/IssueSyncer.mjs, ai/mcp/server/github-workflow/services/queries/issueQueries.mjs). Detector + recovery glue live OUTSIDE that tree at ai/scripts/detectTruncatedTimelines.mjs and consume the ai/services.mjs SDK facade (GH_SyncService, GH_IssueService). This respects the ongoing migration of services out of ai/mcp/server/; when IssueSyncer eventually moves to ai/services/github/, only the SDK re-exports shift — consumer scripts stay stable.

Fix Approach (option B — pagination)

  1. Add pageInfo { hasNextPage endCursor } on timelineItems in FETCH_ISSUES_FOR_SYNC and FETCH_SINGLE_ISSUE; introduce a $timelineCursor variable.
  2. In IssueSyncer.pullFromGitHub + the related-issue force-refetch loop, continue-paginate the connection when hasNextPage is true; concat nodes before handing to #formatIssueMarkdown.
  3. Keep maxTimelineItemsPerIssue: 50 as the page size.
  4. Emit logger.warn when pagination continues (issue number + cumulative count). Silent failure is the root issue; observability is non-negotiable.
  5. Extract the existing single-issue force-fetch loop (IssueSyncer.mjs:456-506) into refetchIssuesByNumber(numbers[]); expose via GH_SyncService so the recovery script consumes it as a first-class SDK method, not a re-implementation.
  6. Playwright spec under test/playwright/unit/ai/mcp/server/github-workflow/ mocks an issue with 75 timeline events and asserts every comment body renders.

Avoided Traps

  • Bump cap to 250 and move on. GraphQL max is 250 for this connection; PR-accelerated growth will reach it too. Silent truncation stays possible.
  • Separate comments pagination + merge at render. Cleaner in isolation but duplicates render logic and adds a merge pass. Paginating the single unified timelineItems connection hits the same correctness with one code path.
  • Make the detector an MCP tool. Admin-initiated + rare; MCP tool budget is 77/100. A standalone script under ai/scripts/ matching the analyzeNlTelemetry.mjs precedent is right-sized.
  • Full sync_all for recovery. Delta sync gates on per-issue updatedAt, which did NOT change when truncation occurred. Blanket sync won't heal affected files. Recovery script calls refetchIssuesByNumber() with just the detector's output list — surgical, no churn.

Acceptance Criteria

  • ai/scripts/detectTruncatedTimelines.mjs scans resources/content/issues/*.md only (archives skipped); primary signal commentsCount > rendered-comment-block count; secondary heuristic rendered-timeline-entries === 50; emits JSON affected-set
  • Baseline detector run attached in PR narrative
  • issueQueries.mjs queries expose pageInfo on timelineItems + accept $timelineCursor
  • IssueSyncer paginates until hasNextPage === false, warn-logs on continuation
  • refetchIssuesByNumber(numbers[]) extracted + exposed via GH_SyncService
  • Playwright spec covers 75-event mocked issue
  • Post-fix detector run reports zero affected issues after force-refetch
  • issue-10030.md body contains the 2026-04-19 handoff comment — regression proof

Origin Session ID

d9eb5e76-5430-45f7-b3ea-8600664d28f9

tobiu added the bug label on Apr 19, 2026, 1:09 PM
tobiu added the ai label on Apr 19, 2026, 1:09 PM
tobiu added the regression label on Apr 19, 2026, 1:09 PM
tobiu added the architecture label on Apr 19, 2026, 1:09 PM
tobiu assigned to @tobiu on Apr 19, 2026, 1:10 PM
tobiu referenced in commit a32c0af - "fix(github-workflow): paginate timelineItems to prevent silent content drop (#10090) on Apr 19, 2026, 1:34 PM
tobiu cross-referenced by PR #10091 on Apr 19, 2026, 1:35 PM
tobiu closed this issue on Apr 19, 2026, 1:41 PM
tobiu referenced in commit 3ec8167 - "fix(github-workflow): paginate timelineItems to prevent silent content drop (#10090) (#10091) on Apr 19, 2026, 1:41 PM
tobiu cross-referenced by #10092 on Apr 19, 2026, 1:44 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 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 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