Skip to content

fix: allow resolve_pull_request_review_thread on schedule/manual triggers when explicit thread_id is provided#26285

Merged
pelikhan merged 3 commits intomainfrom
copilot/fix-resolve-pull-request-review-thread
Apr 14, 2026
Merged

fix: allow resolve_pull_request_review_thread on schedule/manual triggers when explicit thread_id is provided#26285
pelikhan merged 3 commits intomainfrom
copilot/fix-resolve-pull-request-review-thread

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 14, 2026

In default (legacy) mode, the handler always required triggeringPRNumber — causing it to reject calls from schedule/workflow_dispatch workflows even when an explicit thread_id had already been resolved to a specific PR via the GraphQL API.

Changes

  • resolve_pr_review_thread.cjs: In the default/legacy else branch, replace the hard failure when triggeringPRNumber is absent with repo validation and an info log; skip the PR-number match check when there is no triggering PR. The check is preserved when a triggering PR is present. To maintain least-privilege, the legacy path now also validates the thread's repository against defaultTargetRepo/allowedRepos (via validateTargetRepo) and fails closed if the repo cannot be determined or is not allowed.

    // Before
    if (!triggeringPRNumber) {
      core.warning("Cannot resolve review thread: not running in a pull request context");
      return { success: false, error: "Cannot resolve review threads outside of a pull request context" };
    }
    
    // After (legacy mode now validates repo before proceeding)
    if (!threadRepo) {
      // fail closed — cannot allow resolving threads with unknown repo
    }
    const legacyRepoValidation = validateTargetRepo(threadRepo, defaultTargetRepo, allowedRepos);
    if (!legacyRepoValidation.valid) {
      // fail closed — thread repo not in allowed set
    }
    if (!triggeringPRNumber) {
      core.info(`No triggering PR context; resolving thread ${threadId} via explicit thread_id (PR #${threadPRNumber} in ${threadRepo})`);
    } else if (threadPRNumber !== triggeringPRNumber) {
      // existing mismatch guard, unchanged
    }
  • resolve_pr_review_thread.test.cjs: Updated the existing "reject when not in a pull request context" test (now expects success with a valid thread_id in the default repo); added tests covering the schedule-trigger scenario with GraphQL call assertions, as well as failure cases for null thread repo and cross-repo threads in legacy mode.

…ontext when explicit thread_id is provided

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2e5ab53b-1616-429c-9017-f04d662c5d93

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix resolve_pull_request_review_thread context check fix: allow resolve_pull_request_review_thread on schedule/manual triggers when explicit thread_id is provided Apr 14, 2026
Copilot AI requested a review from pelikhan April 14, 2026 21:07
@pelikhan pelikhan marked this pull request as ready for review April 14, 2026 21:13
Copilot AI review requested due to automatic review settings April 14, 2026 21:13
@github-actions github-actions Bot added the lgtm label Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Hey @Copilot 👋 — great work on this fix! Allowing resolve_pull_request_review_thread to succeed on schedule/workflow_dispatch triggers when an explicit thread_id has already been resolved via the GraphQL API is a clean, targeted improvement to the agentic workflows infrastructure.

The PR is well-structured: the implementation change is minimal and surgical, the existing test is correctly updated to reflect the new expected behaviour, and a dedicated new test covers the schedule-trigger scenario with GraphQL call assertions. The PR description clearly explains the problem and the solution.

This looks ready for maintainer review! 🚀


This review was generated by the contribution-checker agent.

Generated by Contribution Check · ● 1.7M ·

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the resolve_pull_request_review_thread handler to allow resolving an explicitly provided thread_id even when the workflow was triggered without a pull request context (e.g., schedule / workflow_dispatch), and adjusts tests to cover the new behavior.

Changes:

  • Relax legacy-mode behavior to no longer hard-fail when triggeringPRNumber is absent; instead logs and proceeds when thread_id is explicit.
  • Update the “non-PR context” test to expect success with an explicit thread_id.
  • Add a new test covering schedule-triggered behavior with GraphQL call assertions.
Show a summary per file
File Description
actions/setup/js/resolve_pr_review_thread.cjs Adjusts legacy-mode scoping logic to permit thread resolution without a triggering PR number when thread_id is provided.
actions/setup/js/resolve_pr_review_thread.test.cjs Updates/extends tests to validate success paths for non-PR contexts (including schedule triggers).

Copilot's findings

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment on lines +218 to +223
// Default (legacy) mode: scope to triggering PR only when a triggering PR exists
if (!triggeringPRNumber) {
core.warning("Cannot resolve review thread: not running in a pull request context");
return {
success: false,
error: "Cannot resolve review threads outside of a pull request context",
};
}

if (threadPRNumber !== triggeringPRNumber) {
// No triggering PR (e.g. schedule/workflow_dispatch trigger) but an explicit thread_id
// was provided and resolved to a specific PR via the API — allow the resolution to proceed
core.info(`No triggering PR context; resolving thread ${threadId} via explicit thread_id (PR #${threadPRNumber})`);
} else if (threadPRNumber !== triggeringPRNumber) {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

In legacy mode (no explicit target-repo/allowed_repos), this new branch allows resolving a thread when triggeringPRNumber is missing without any repository scoping. If a per-handler github-token is configured (PAT/App) with access to multiple repos, a schedule/workflow_dispatch run could now resolve review threads in other repositories, bypassing the repo allowlist logic that only runs when hasExplicitTargetConfig is true.

To keep the behavior least-privilege while still supporting schedule/manual triggers, add a repo validation in the legacy path (e.g., require threadRepo and ensure it matches defaultTargetRepo, or call validateTargetRepo(threadRepo, defaultTargetRepo, allowedRepos) even when hasExplicitTargetConfig is false) and fail closed on mismatch/unknown repo.

See below for a potential fix:

        // Default (legacy) mode: keep least-privilege behavior by requiring the resolved thread
        // to belong to the default/allowed repository set even when there is no triggering PR.
        if (!threadRepo) {
          core.warning(`Unable to determine repository for review thread ${threadId}; refusing to resolve in legacy mode`);
          return {
            success: false,
            error: `Unable to determine repository for review thread ${threadId}`,
          };
        }

        const legacyRepoValidation = validateTargetRepo(threadRepo, defaultTargetRepo, allowedRepos);
        if (!legacyRepoValidation.valid) {
          core.warning(`Thread ${threadId} repository ${threadRepo} is not allowed in legacy mode`);
          return {
            success: false,
            error: legacyRepoValidation.error || `Repository ${threadRepo} is not allowed for this handler`,
          };
        }

        // Scope to triggering PR only when a triggering PR exists
        if (!triggeringPRNumber) {
          // No triggering PR (e.g. schedule/workflow_dispatch trigger), but the thread has been
          // resolved to a specific allowed repository via the API — allow the resolution to proceed
          core.info(`No triggering PR context; resolving thread ${threadId} via explicit thread_id (PR #${threadPRNumber} in ${threadRepo})`);

Copilot uses AI. Check for mistakes.
@github-actions github-actions Bot mentioned this pull request Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 80/100

Excellent

Metric Value
New/modified tests analyzed 2
✅ Design tests (behavioral contracts) 2 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 2 (100%)
Duplicate test clusters 0
Test inflation detected ⚠️ Yes (29 test additions / 5 production additions = 5.8:1)
🚨 Coding-guideline violations None

Test Classification Details

📋 All 2 modified/new tests
Test File Classification Issues Detected
should succeed when not in a pull request context but explicit thread_id is provided resolve_pr_review_thread.test.cjs ✅ Design None — verifies observable outputs (success, thread_id, is_resolved)
should succeed when triggered by a schedule event (no PR context) with explicit thread_id resolve_pr_review_thread.test.cjs ✅ Design Minor: toHaveBeenCalledTimes(2) checks internal API call count alongside behavioral assertions

Test Analysis

Test 1 (modified): should succeed when not in a pull request context but explicit thread_id is provided

This test was renamed and updated from the previous should reject when not in a pull request context — correctly reflecting the new behavioral contract. It verifies that when an explicit thread_id is provided and the workflow runs outside a PR context (e.g., a workflow_dispatch or push event), the resolution succeeds. Assertions are purely behavioral: result.success, result.thread_id, result.is_resolved. The test itself exercises an edge case (non-PR event context), which is exactly the scenario the fix addresses.

Test 2 (new): should succeed when triggered by a schedule event (no PR context) with explicit thread_id

Adds specific coverage for schedule-triggered workflows (empty context.payload). Behavioral assertions are solid. The additional expect(mockGraphql).toHaveBeenCalledTimes(2) and toHaveBeenCalledWith(...) assertions verify that both a thread-lookup query and a resolve mutation were issued — this is acceptable because it confirms the resolution actually reached the external API rather than short-circuiting. The mock target (global.github.graphql) is an external I/O boundary, not an internal business-logic function.


Test Inflation Note

The raw addition ratio (29 test lines / 5 production lines = 5.8:1) triggers the inflation flag, but the context is benign: the production change was primarily a simplification (9 lines deleted, 5 added), while the tests expand coverage for two new valid trigger scenarios. The added test lines are not padding — they are meaningful assertions directly tied to the behavioral change.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 2 tests (vitest)

No Go test files were changed in this PR.


Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). Both modified/new tests verify observable behavioral contracts for the edge case this PR fixes. The test inflation flag is a measurement artifact of a primarily deletion-based production change; the new test lines add genuine coverage.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 397.7K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 80/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). Both changed tests verify behavioral contracts for the schedule/non-PR trigger edge case this PR fixes.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review comments

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

@copilot review comments

Addressed in 68d2015. The legacy mode path now validates the thread's repository against defaultTargetRepo/allowedRepos (via validateTargetRepo) before allowing schedule-triggered resolution — fails closed if the repo cannot be determined or is not in the allowed set. Added two new tests covering both failure scenarios.

@pelikhan pelikhan merged commit b29f1da into main Apr 14, 2026
@pelikhan pelikhan deleted the copilot/fix-resolve-pull-request-review-thread branch April 14, 2026 21:45
Copy link
Copy Markdown

@Ejezzy9 Ejezzy9 left a comment

Choose a reason for hiding this comment

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

good work keep up

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[plan] Fix resolve_pull_request_review_thread Context Check

4 participants