Skip to content

Resolve PR review threads from review comment node IDs - #50349

Merged
pelikhan merged 5 commits into
mainfrom
copilot/deep-report-fix-resolve-pull-request-review-thread
Aug 4, 2026
Merged

Resolve PR review threads from review comment node IDs#50349
pelikhan merged 5 commits into
mainfrom
copilot/deep-report-fix-resolve-pull-request-review-thread

Conversation

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

resolve_pull_request_review_thread rejected PullRequestReviewComment node IDs (PRRC_...) even though each comment maps to exactly one review thread. This caused safe-output batches to fail when agents passed comment IDs instead of thread IDs.

  • Comment-to-thread fallback

    • Detects PullRequestReviewComment nodes during thread lookup.
    • Finds the containing PullRequestReviewThread via the PR reviewThreads connection.
    • Resolves the recovered thread ID instead of failing validation.
  • Validation behavior

    • Keeps existing rejection for unrelated node types.
    • Preserves repo/PR scoping checks after the thread ID is recovered.
  • Coverage

    • Adds focused coverage for resolving a PRRC_... ID through its parent PRRT_....
    • Keeps coverage for non-thread/non-comment node rejection.
{
  "type": "resolve_pull_request_review_thread",
  "thread_id": "PRRC_kwDOComment123"
}

Now maps to the containing review thread before calling resolveReviewThread.


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.3 AIC · ⌖ 6.27 AIC · ⊞ 8.3K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30949820215

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 11.9 AIC · ⌖ 6.9 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix resolve_pull_request_review_thread rejecting comment node-ID instead of thread node-ID Resolve PR review threads from review comment node IDs Aug 4, 2026
Copilot AI requested a review from pelikhan August 4, 2026 18:40
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Triage

Category: bug | Risk: low | Score: 50/100 (impact 20, urgency 12, quality 18)

Recommended action: batch_review

Targeted fix (191+/27-, 2 files) enabling PRRC_ node IDs for review thread resolution, includes tests. Draft PR — batch with similar safe-output/review-thread fixes.

Generated by 🔧 PR Triage Agent · auto · 43.1 AIC · ⌖ 2.46 AIC · ⊞ 8K ·

@pelikhan
pelikhan marked this pull request as ready for review August 4, 2026 19:31
Copilot AI balanced review requested due to automatic review settings August 4, 2026 19:31
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (0 additions detected).

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.

Pull request overview

Adds fallback resolution from review comment node IDs to their containing review threads.

Changes:

  • Detects PullRequestReviewComment IDs and searches the PR’s review threads.
  • Uses the recovered thread ID while preserving scope validation.
  • Adds focused success and rejection tests.
Show a summary per file
File Description
actions/setup/js/resolve_pr_review_thread.cjs Implements comment-to-thread lookup and resolution.
actions/setup/js/resolve_pr_review_thread.test.cjs Tests comment ID fallback and unrelated node rejection.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

nodes {
id
isResolved
comments(first: 100) {
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

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

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 25.7 AIC · ⌖ 13.1 AIC · ⊞ 5.4K

pullRequest(number: $number) {
reviewThreads(first: 100, after: $cursor) {
pageInfo {
hasNextPage

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.

The inner comments(first: 100) query fetches at most 100 comments per thread. If a thread has >100 comments, the matching comment will be missed and the fallback will silently return comment_without_thread instead of the correct thread.

Consider using the pullRequestThread field directly on PullRequestReviewComment (available in GitHub's GraphQL schema) in the initial lookup query instead of scanning all threads. That removes the O(threads × comments) fan-out and the 100-comment cap entirely:

... on PullRequestReviewComment {
  pullRequestThread {
    id
    isResolved
  }
  pullRequest { ... }
}

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. The PullRequestReviewComment fragment now fetches reviewThread { id isResolved } directly in the initial node lookup query, eliminating the O(threads × comments) fan-out and the per-page cap entirely. findThreadInfoForReviewComment has been removed.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 85/100 — Excellent

Analyzed 2 test(s): 2 design, 0 implementation, 0 violation(s).

📊 Metrics (2 tests)
Metric Value
Analyzed 2 (JS: 2)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 1 (50%)
Duplicate clusters 0
Inflation 0.53:1 ✅
🚨 Violations 0
Test File Classification Status
"should resolve a review comment node ID by finding its parent thread" resolve_pr_review_thread.test.cjs behavioral_contract ✅ design_test, 5 assertions
"should still reject non-thread and non-comment node IDs" resolve_pr_review_thread.test.cjs behavioral_contract ✅ design_test, 3 assertions, error path

Quality Summary

Strengths:

  • 100% design test coverage — all assertions verify behavioral contracts, not implementation details
  • Comprehensive mock strategy — proper branching for complex GraphQL scenarios (mutation, query, and lookup paths)
  • Error path validation — modified test correctly updated to verify type rejection after feature change
  • No mock library violations — uses vitest vi.fn() only (no gomock, testify/mock)
  • Well-proportioned coverage — test additions (66 lines) match code complexity (125 lines); 0.53:1 ratio is excellent

Opportunities for enhancement:

  1. Pagination edge case: New feature includes pagination loop (while (cursor)) but only happy-path test exists; multi-page scenarios would strengthen confidence
  2. Error scenarios for new feature: Consider adding a test for "comment not found in any thread" (already covered by existing error tests, but not specific to new feature)

Verdict

PASSED. 85/100 with 0% implementation tests (threshold: 30%). No violations.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 24.4 AIC · ⌖ 6.01 AIC · ⊞ 8.6K ·
Comment /review to run again

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

✅ Test Quality Sentinel: 85/100 Excellent. 100% design tests, 0% implementation tests (threshold: 30%). No violations.

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on two correctness issues.

📋 Key Themes & Highlights

Issues

  • Hidden data loss (comments(first: 100)): The inner loop over thread comments is capped at 100 with no pagination cursor. A thread with >100 comments will never match the target commentId and the function will silently return comment_without_thread — the root cause of the original bug may resurface for high-activity threads. The fix is to use PullRequestReviewComment.reviewThread directly in the initial lookup fragment, which is a simpler, O(1) alternative that avoids the pagination problem entirely.

  • Silent fallback (threadNode.id || threadId): Now that id is explicitly selected in the fragment, falling back to the input threadId when id is absent is misleading and masks schema regressions.

  • Missing test for comment_without_thread: The error path where a comment exists but belongs to no discoverable thread is untested. This is the most important negative-path assertion for the new feature.

Positive Highlights

  • ✅ Clean separation via the new findThreadInfoForReviewComment helper
  • ✅ Existing scoping/validation logic is correctly reused after thread ID recovery
  • ✅ Good use of core.info to log the ID remapping for observability
  • ✅ Updated test correctly changes the rejection case to use Issue instead of PullRequestReviewComment

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 35.1 AIC · ⌖ 8.22 AIC · ⊞ 7.1K
Comment /matt to run again

nodes {
id
isResolved
comments(first: 100) {

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.

[/diagnosing-bugs] comments(first: 100) has no pagination — a review thread with more than 100 comments will never match and the function silently returns comment_without_thread.

💡 Simpler fix: query the parent thread directly

The PullRequestReviewComment GraphQL type exposes a reviewThread field, so you can get the parent thread ID in one query — no iteration needed:

... on PullRequestReviewComment {
  reviewThread {
    id
    isResolved
  }
  pullRequest { number repository { nameWithOwner name owner { login } } }
}

This eliminates findThreadInfoForReviewComment entirely and removes the O(threads × comments) pagination loop.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The initial query now uses reviewThread { id isResolved } on the PullRequestReviewComment fragment, resolving the parent thread in a single round-trip. The findThreadInfoForReviewComment pagination loop has been removed entirely.

@@ -165,12 +227,12 @@ describe("resolve_pr_review_thread", () => {

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.

[/tdd] No test for the comment_without_thread path — when a PRRC_ comment exists but no review thread contains it, the handler should return a descriptive error. Without a test, this code path is invisible to CI.

💡 Suggested test sketch
it('should return error when comment is not found in any review thread', async () => {
  mockGraphql.mockImplementation(query => {
    if (query.includes('reviewThreads')) {
      return Promise.resolve({
        repository: {
          pullRequest: {
            reviewThreads: {
              pageInfo: { hasNextPage: false, endCursor: null },
              nodes: [{ id: 'PRRT_other', isResolved: false, comments: { nodes: [] } }],
            },
          },
        },
      });
    }
    return Promise.resolve({
      node: {
        __typename: 'PullRequestReviewComment',
        pullRequest: { number: 42, repository: { name: 'r', nameWithOwner: 'o/r', owner: { login: 'o' } } },
      },
    });
  });
  const { main } = require('./resolve_pr_review_thread.cjs');
  const handler = await main({ max: 10 });
  const result = await handler({ type: 'resolve_pull_request_review_thread', thread_id: 'PRRC_kwDOOrphan' });
  expect(result.success).toBe(false);
  expect(result.error).toContain('PRRC_kwDOOrphan');
});

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A test for the comment_without_thread path is present: "should return comment_without_thread when the PRRC_ node has no reviewThread" mocks reviewThread: null and asserts success: false with an error containing the comment ID.

@@ -62,12 +79,87 @@ async function getThreadPullRequestInfo(github, threadId) {

return {

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.

[/diagnosing-bugs] threadNode.id || threadId — the id field is now explicitly selected in the GraphQL fragment, so the || threadId fallback silently masks any future schema regression where id is unexpectedly absent. Prefer asserting its presence explicitly.

💡 Suggestion
if (!threadNode.id) {
  core.warning(`PullRequestReviewThread node returned without an id for input ${threadId}`);
  return { status: 'missing' };
}
return {
  status: 'thread',
  threadId: threadNode.id,
  ...
};

This makes the failure observable rather than silently using the input ID as the resolved ID.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The fallback threadNode.id || threadId was removed. The code now checks if (!threadNode.id) and returns { status: "missing" } with a core.warning, making the absence observable rather than silently substituting the input ID.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the remaining blocking review feedback on this PR, then run the pr-finisher skill before handing it back.

Open review feedback to close out:

  • github-actions review (newest first): notes about paginating thread-comment lookup beyond 100 comments, removing the threadNode.id || threadId fallback, and adding a negative test for comment_without_thread.

Failed checks:

Please refresh the branch as needed after fixes and resolve addressed review threads once the reviewer feedback has been handled.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.3 AIC · ⌖ 6.27 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the remaining review feedback on this PR, refresh the branch if GitHub allows it, and then run the pr-finisher skill before handing it back.

Open review feedback to close out:

  • github-actions review: avoid the comments(first: 100) pagination hole by resolving the review thread directly from the review comment lookup, remove the threadNode.id || threadId fallback, and add a negative test for the comment_without_thread path.

Failed checks from compact context: none currently listed.

Please resolve addressed review threads once the reviewer feedback has been handled.

Generated by 👨🍳 PR Sous Chef

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 11.9 AIC · ⌖ 6.9 AIC · ⊞ 8.3K ·
Comment /souschef to run again

@pelikhan
pelikhan merged commit 6122702 into main Aug 4, 2026
8 checks passed
@pelikhan
pelikhan deleted the copilot/deep-report-fix-resolve-pull-request-review-thread branch August 4, 2026 21:21
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.85.0

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[deep-report] Fix resolve_pull_request_review_thread rejecting comment node-ID instead of thread node-ID

4 participants