Skip to content

set_issue_field: remove org-level issueFields fallback from field discovery query#42517

Open
Copilot wants to merge 3 commits into
mainfrom
copilot/remove-org-level-issuefields-query
Open

set_issue_field: remove org-level issueFields fallback from field discovery query#42517
Copilot wants to merge 3 commits into
mainfrom
copilot/remove-org-level-issuefields-query

Conversation

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

set_issue_field field discovery was querying both repository and organization issue fields in one GraphQL request. With GITHUB_TOKEN, the org fragment can return “Resource not accessible by integration,” causing the entire query to fail and preventing field resolution.

  • Handler query scope reduction

    • Updated fetchIssueFields in actions/setup/js/set_issue_field.cjs to query repository-level issueFields only.
    • Removed repository.owner { ... on Organization { issueFields } } from the discovery query.
    • Removed dead fallback logic that attempted to return ownerFields.
  • Test updates for new contract

    • Updated actions/setup/js/set_issue_field.test.cjs to stop mocking org-owner issue field fallback data.
    • Added/strengthened assertions that discovery query does not include owner { or ... on Organization.
    • Updated actions/setup/js/set_issue_field_api_query.integration.test.cjs query fixture to match repository-only discovery.
  • Behavioral outcome

    • set_issue_field discovery now aligns with the permissions available to Actions GITHUB_TOKEN (issues: write) and avoids org-scope query failures.
query($owner: String!, $repo: String!) {
  repository(owner: $owner, name: $repo) {
    issueFields(first: 100) {
      nodes {
        __typename
        ... on IssueFieldText { id name }
        ... on IssueFieldNumber { id name }
        ... on IssueFieldDate { id name }
        ... on IssueFieldSingleSelect { id name options { id name } }
        ... on IssueFieldMultiSelect { id name options { id name } }
      }
    }
  }
}

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix fetchIssueFields to remove org-level issueFields query set_issue_field: remove org-level issueFields fallback from field discovery query Jun 30, 2026
Copilot AI requested a review from pelikhan June 30, 2026 16:39
@pelikhan pelikhan marked this pull request as ready for review June 30, 2026 17:24
Copilot AI review requested due to automatic review settings June 30, 2026 17:24
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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

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

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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

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

This PR updates the set_issue_field handler’s issue-field discovery to query only repository-level issueFields, avoiding org-scope GraphQL fragments that can fail under GITHUB_TOKEN with “Resource not accessible by integration”.

Changes:

  • Reduced the discovery GraphQL query in fetchIssueFields to repository scope only and removed the org-owner fallback path.
  • Updated unit tests to stop depending on org-owner field data and to assert the query does not include org/owner selections.
  • Updated the live-schema integration query fixture to match the repository-only discovery query.
Show a summary per file
File Description
actions/setup/js/set_issue_field.cjs Removes org-owner issueFields fragment from discovery and simplifies field resolution to repository-only nodes.
actions/setup/js/set_issue_field.test.cjs Updates mocks and adds assertions to enforce the repository-only discovery query contract.
actions/setup/js/set_issue_field_api_query.integration.test.cjs Updates the integration test’s query fixture to match the new repository-only discovery query.

Review details

Tip

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

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +347 to +349
expect(capturedQuery).not.toContain("... on User");
expect(capturedQuery).not.toContain("... on Organization");
expect(capturedQuery).not.toContain("owner {");
@github-actions github-actions Bot mentioned this pull request Jun 30, 2026

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

Review: set_issue_field — org-level issueFields fallback removal

The fix is correct and well-motivated: querying Organization.issueFields with GITHUB_TOKEN causes the entire GraphQL request to fail ("Resource not accessible by integration"), so scoping discovery to repository-level fields is the right default.

The code simplification and updated tests (especially the new not.toContain assertions) are clean.

One concern worth addressing (see inline comment): this silently removes the org-level fallback that was previously reachable for PAT or GitHub App callers with org-read access. Users whose issue fields live at the org level (rather than the repo level) will now get an empty discovery result and an opaque "field not found" error. A CHANGELOG entry or a brief note in the action README would be enough to address this.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 31.7 AIC · ⌖ 6.58 AIC · ⊞ 4.9K


const ownerFields = (result?.repository?.owner?.issueFields?.nodes ?? []).filter(isValidNode);
return ownerFields;
return (result?.repository?.issueFields?.nodes ?? []).filter(isValidNode);

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 simplification is clean and correct for GITHUB_TOKEN, but this removes the org-level field fallback entirely — a silent behavioral regression for callers using a PAT or GitHub App token with org-read access.

Previously, if repository.issueFields returned empty, discovery fell back to Organization.issueFields. That path is now gone: a user whose custom fields live at the org level will get an empty result and a confusing "field not found" error with no indication of why.

Consider:

  • Documenting this as a breaking change in a CHANGELOG or the action README, or
  • Offering an org_fallback input (default false for GITHUB_TOKEN safety) so PAT/GitHub App callers can opt back in.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 75/100 — Acceptable

Analyzed 4 modified test bodies across 2 JavaScript test files: 3 behavioral design tests and 1 implementation-maintenance update; 0 guideline violations.

📊 Metrics & Test Classification (4 tests analyzed)
Metric Value
New/modified tests analyzed 4
✅ Design tests (behavioral contracts) 3 (75%)
⚠️ Implementation tests (low value) 1 (25%)
Tests with error/edge cases 2 (50%)
Duplicate test clusters 0
Test inflation detected No
🚨 Coding-guideline violations 0
Test File Classification Notes
should skip gracefully with a warning when no issue fields are discovered set_issue_field.test.cjs:271 ✅ Design Mock cleaned to remove removed org-fallback path; behavioral skip-with-warning contract preserved
capturedQuery shape assertions set_issue_field.test.cjs:345 ✅ Design +2 new assertions: .not.toContain("... on Organization") and .not.toContain("owner {") — directly enforces that org-level query is excluded
fetchIssueFields filters out nodes missing id or name set_issue_field.test.cjs:363 ⚠️ Implementation Mock removal only; underlying filtering-behavior contract unchanged, change is maintenance alignment
validates against live schema and excludes the removed IssueField fragment set_issue_field_api_query.integration.test.cjs:5 ✅ Design Fixture updated to repository-only query; enforces exact GraphQL contract against live schema

Go: 0 (*_test.go); JavaScript: 2 (set_issue_field.test.cjs, set_issue_field_api_query.integration.test.cjs).

Test inflation check:

  • set_issue_field.test.cjs: +2 lines added / set_issue_field.cjs: +2 lines added → ratio 1:1 ✅
  • set_issue_field_api_query.integration.test.cjs: 0 lines added / set_issue_field.cjs: +2 lines → ratio 0:1 ✅

Verdict

Check passed. 25% implementation tests (threshold: 30%). The two new .not.toContain assertions in set_issue_field.test.cjs correctly enforce the key behavioral contract of this PR — that the discovery query no longer includes owner { or ... on Organization fragments that trigger permission failures with GITHUB_TOKEN. Mock cleanups are appropriate maintenance with no quality concerns.

References: §28463223094

🧪 Test quality analysis by Test Quality Sentinel · 42.8 AIC · ⌖ 12 AIC · ⊞ 6.7K ·
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: 75/100. Test quality is acceptable — 25% of new tests are implementation tests (threshold: 30%).

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

Non-blocking observation on test coverage

The core change is correct and achieves its stated goal: removing the org-level issueFields fragment eliminates the GITHUB_TOKEN permission failure. The simplified return logic is clean and semantically equivalent for the supported case.

Findings summary

1 non-blocking finding (see inline comment):

  • The ISSUE_FIELDS_DISCOVERY_QUERY constant in the integration test is a standalone copy of the production query, not imported from the source module. If the query changes in the future, the integration test will silently validate the stale fixture rather than the real query.

Intentional tradeoff acknowledged:

  • Removing the org-level fallback means repositories that define issue fields only at the org level (not mirrored at repo level) will now return an empty field list and fail with a generic "field not found" error. This is documented and accepted in the PR description as a trade-off for permissions compatibility. A warning log in fetchIssueFields when the returned field list is empty would improve debuggability.

Existing comment:

  • The not.toContain("owner {") whitespace fragility already flagged by another reviewer — no need to re-litigate.

🔎 Code quality review by PR Code Quality Reviewer · 57.1 AIC · ⌖ 7.41 AIC · ⊞ 1.6K
Comment /review to run again

Comments that could not be inline-anchored

actions/setup/js/set_issue_field_api_query.integration.test.cjs:4

Integration test fixture is a standalone duplicate — it cannot detect production query drift: ISSUE_FIELDS_DISCOVERY_QUERY is copy-pasted inline rather than imported from set_issue_field.cjs. If the production query changes in the future, this integration test will keep validating the old string against the live schema and silently pass — providing false confidence.

<details>
<summary>💡 Suggested fix</summary>

Export the query string from the production module and import it here:

``…

@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, /tdd, and /grill-with-docs — approving with minor suggestions.

📋 Key Themes & Highlights

Key Themes

  • Root cause correctly addressed: Removing the org-level issueFields fragment is the right fix — the fragment was structurally guaranteed to fail with GITHUB_TOKEN (issues: write is repo-scoped only). The fallback logic was effectively dead code.
  • Regression prevention is solid: New negative assertions (not.toContain("... on Organization"), not.toContain("owner {")) cleanly prevent query-shape regressions. The integration test fixture is aligned with the new contract.
  • Minor gaps: No diagnostic logging when field discovery returns empty; no JSDoc note explaining the intentional scope; null issueFields not explicitly exercised in tests (guarded by ?? [] but untested).

Positive Highlights

  • ✅ Clean simplification: the conditional fallback → single return reads clearly
  • ✅ Query-shape tests are the right regression strategy for a GQL query fix
  • ✅ Integration test fixture keeps the contract explicit and auditable
  • ✅ Well-scoped change — only touches what's necessary

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 49.8 AIC · ⌖ 7.86 AIC · ⊞ 6.5K
Comment /matt to run again


const ownerFields = (result?.repository?.owner?.issueFields?.nodes ?? []).filter(isValidNode);
return ownerFields;
return (result?.repository?.issueFields?.nodes ?? []).filter(isValidNode);

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] When fetchIssueFields returns an empty array, the caller will eventually throw a confusing "field not found" error with no indication that field discovery came back empty.

💡 Suggested improvement

Consider adding a warning log before the return:

const fields = (result?.repository?.issueFields?.nodes ?? []).filter(isValidNode);
if (fields.length === 0) {
  console.warn(`[set_issue_field] fetchIssueFields: no valid fields found for ${owner}/${repo}`);
}
return fields;

This surfaces the failure point earlier, especially during first-time setup when a misconfigured project or insufficient token scope would otherwise produce a silent empty result followed by a cryptic downstream error.

@copilot please address this.


/**
* Fetches available issue fields for the repository/owner.
* Fetches available issue fields for the repository.

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.

[/grill-with-docs] The JSDoc update from repository/owner to repository correctly reflects the scope reduction, but it doesn't explain why org-level fields are excluded.

💡 Suggested addition
/**
 * Fetches available issue fields for the repository.
 * `@note` Intentionally scoped to repository-level fields only. The org-level
 *   `issueFields` query (`repository.owner { ... on Organization }`) fails
 *   with `GITHUB_TOKEN` ("Resource not accessible by integration") since
 *   Actions tokens only carry `issues: write` at the repo level.
 * `@param` {Object} githubClient ...

Without this context, a future contributor might re-add org discovery as a "fix" and reintroduce the permission error.

@copilot please address this.

await h({ type: "set_issue_field", issue_number: 42, field_name: "Customer Impact", value: "High" }, {});

expect(capturedQuery).not.toContain("... on User");
expect(capturedQuery).not.toContain("... on Organization");

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] Good negative assertions — these prevent the problematic query patterns from quietly re-appearing. Consider also adding a case that verifies fetchIssueFields returns [] when result.repository.issueFields is null (not just { nodes: [] }), which can happen when the field doesn't exist in the GraphQL schema for a specific GitHub plan.

💡 Sketch
it('fetchIssueFields returns [] when issueFields is null', async () => {
  const mockClient = {
    graphql: () => Promise.resolve({ repository: { issueFields: null } })
  };
  const result = await fetchIssueFields(mockClient, 'owner', 'repo');
  expect(result).toEqual([]);
});

The ?? [] guard already handles this safely, but making it explicit in the test suite documents the expected behaviour and catches future regressions if the guard is accidentally removed.

@copilot please address this.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Addressed the reviewer's suggestion: replaced not.toContain("owner {") with not.toMatch(/owner\s*\{/) in commit fix: use regex for owner field query assertion in set_issue_field test. Local validation (fmt, lint, all 17 tests) passes clean.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 Low
Score 67 / 100
Action fast_track

Score breakdown: Impact 30/50 · Urgency 20/30 · Quality 17/20

Rationale: Removes broken org-level GraphQL fallback that causes full query failures. CI (github-actions) approved. Clean deletion-focused fix (+4/-43). Ready for expedited human review.

Generated by 🔧 PR Triage Agent · 83.9 AIC · ⌖ 17.1 AIC · ⊞ 1.6K ·

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.

set_issue_field: remove org-level issueFields query that fails with GITHUB_TOKEN

3 participants