Skip to content

Remove unnecessary StateReason and StateReasonDuplicate feature detection#12838

Merged
williammartin merged 2 commits intotrunkfrom
remove-state-reason-duplicate-detection
Mar 5, 2026
Merged

Remove unnecessary StateReason and StateReasonDuplicate feature detection#12838
williammartin merged 2 commits intotrunkfrom
remove-state-reason-duplicate-detection

Conversation

@BagToad
Copy link
Member

@BagToad BagToad commented Mar 4, 2026

Description

Remove StateReason and StateReasonDuplicate from IssueFeatures feature detection. The stateReason field was added in GHES ~3.4 and the DUPLICATE enum variant was added in GHES 3.16 — both available on all currently supported GHES versions, making the introspection queries and conditional logic unnecessary.

Follows up on #12811 (comment).

Impact

  • IssueFeatures struct reduced to only ActorIsAssignable
  • GHES introspection query in IssueFeatures() removed entirely (was only checking stateReason)
  • stateReason field now always included in issue list queries
  • --reason flag on gh issue close no longer conditionally stripped on GHES
  • --duplicate-of flag on gh issue close no longer conditionally erroring on GHES
  • Removes // TODO stateReasonCleanup markers across the codebase

Notes for reviewers

Two commits:

  1. Removes StateReasonDuplicate (the specific ask from the PR comment)
  2. Removes StateReason entirely (same rationale — available on all supported GHES)

Manual tests

Tested on github.com and GHES 3.20.0:

Test github.com GHES 3.20.0
gh issue close --reason completed
gh issue close --reason "not planned"
gh issue close --reason duplicate
gh issue close --duplicate-of <num>
gh issue list --state closed --json stateReason ✅ correct reasons ✅ correct reasons
github.com raw output
=== close with --reason completed ===
✓ Closed issue bagtoad/garbage#88 (test issue 4)

=== close with --reason 'not planned' ===
✓ Closed issue bagtoad/garbage#87 (test issue 3)

=== close with --reason duplicate ===
✓ Closed issue bagtoad/garbage#89 (test issue 5)

=== close with --duplicate-of ===
✓ Closed issue bagtoad/garbage#85 (test issue 1)

=== issue list (verify stateReason) ===
[
  {"number":89,"stateReason":"DUPLICATE","title":"test issue 5"},
  {"number":88,"stateReason":"COMPLETED","title":"test issue 4"},
  {"number":87,"stateReason":"NOT_PLANNED","title":"test issue 3"},
  {"number":85,"stateReason":"DUPLICATE","title":"test issue 1"}
]
GHES 3.20.0 raw output
=== close with --reason completed ===
✓ Closed issue bagtoad/garbage#4 (test issue 4)

=== close with --reason 'not planned' ===
✓ Closed issue bagtoad/garbage#3 (test issue 3)

=== close with --reason duplicate ===
✓ Closed issue bagtoad/garbage#5 (test issue 5)

=== close with --duplicate-of ===
✓ Closed issue bagtoad/garbage#1 (test issue 1)

=== issue list (verify stateReason) ===
[
  {"number":5,"stateReason":"DUPLICATE","title":"test issue 5"},
  {"number":4,"stateReason":"COMPLETED","title":"test issue 4"},
  {"number":3,"stateReason":"NOT_PLANNED","title":"test issue 3"},
  {"number":1,"stateReason":"DUPLICATE","title":"test issue 1"}
]

BagToad and others added 2 commits March 4, 2026 13:24
The DUPLICATE enum variant for IssueClosedStateReason was added in
GHES 3.16, which is older than the earliest supported GHES version.
The feature detection check is therefore unnecessary.

Addresses: #12811 (comment)

Co-authored-by: Copilot <[email protected]>
The stateReason field was added in GHES ~3.4, which is far older than
the earliest supported GHES version (3.14). The feature detection and
conditional inclusion of stateReason is therefore unnecessary.

This removes:
- StateReason field from IssueFeatures struct
- GHES introspection query in IssueFeatures() (only ActorIsAssignable
  remains, which is always false on GHES)
- Conditional stateReason field inclusion in issue list
- Feature detection guard in issue close
- Feature detection guard in FindIssueOrPR

Co-authored-by: Copilot <[email protected]>
@BagToad BagToad requested a review from a team as a code owner March 4, 2026 21:27
@BagToad BagToad requested review from babakks and Copilot March 4, 2026 21:27
@BagToad BagToad requested a review from williammartin March 4, 2026 21:28
Copy link
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

Removes IssueFeatures detection and conditional logic around Issue.stateReason / duplicate close reasons, based on the assumption these are now present on all supported GitHub hosts (including GHES), simplifying issue list/lookup queries and gh issue close behavior.

Changes:

  • Simplifies internal/featuredetection.IssueFeatures to only ActorIsAssignable and removes the GHES introspection query.
  • Always requests stateReason in issue list queries and stops stripping it in issue lookups.
  • Removes conditional fallback behavior (and related tests) for --reason / --duplicate-of on GHES.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/cmd/issue/shared/lookup.go Removes conditional stripping of stateReason from issue/PR lookup GraphQL fields.
pkg/cmd/issue/list/list.go Always includes stateReason in the default issue list field set.
pkg/cmd/issue/close/close.go Removes feature-detection gating for close reasons and duplicate-of behavior.
pkg/cmd/issue/close/close_test.go Deletes tests/mocks that asserted fallback behavior when close reasons/duplicate weren’t supported.
internal/featuredetection/feature_detection.go Drops StateReason and StateReasonDuplicate from IssueFeatures and removes GHES schema introspection.
internal/featuredetection/feature_detection_test.go Updates feature detection tests to match the simplified IssueFeatures.
Comments suppressed due to low confidence (1)

pkg/cmd/issue/close/close.go:203

  • CloseIssueInput now always includes stateReason/duplicateIssueId when flags are used, without any compatibility fallback. If the CLI still supports GHES versions that predate the IssueClosedStateReason values (and duplicateIssueId support) or even the stateReason field (README says GHES 2.20+), this mutation will error on those instances. Consider either explicitly bumping the minimum supported GHES version in docs/release notes, or retaining a gated fallback that omits these inputs when unsupported.
	switch reason {
	case "":
		// If no reason is specified do not set it.
	case "not planned":
		reason = "NOT_PLANNED"
	case "duplicate":
		reason = "DUPLICATE"
	default:
		reason = "COMPLETED"
	}

	var mutation struct {
		CloseIssue struct {
			Issue struct {
				ID githubv4.ID
			}
		} `graphql:"closeIssue(input: $input)"`
	}

	variables := map[string]interface{}{
		"input": CloseIssueInput{
			IssueID:          issue.ID,
			StateReason:      reason,
			DuplicateIssueID: duplicateIssueID,
		},

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

Comment on lines 136 to 139
func FindIssueOrPR(httpClient *http.Client, repo ghrepo.Interface, number int, fields []string) (*api.Issue, error) {
fieldSet := set.NewStringSet()
fieldSet.AddValues(fields)
if fieldSet.Contains("stateReason") {
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
detector := fd.NewDetector(cachedClient, repo.RepoHost())
features, err := detector.IssueFeatures()
if err != nil {
return nil, err
}
// TODO stateReasonCleanup
if !features.StateReason {
fieldSet.Remove("stateReason")
}
}

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Feature detection for stateReason was removed here, so callers that include stateReason in fields will now always request it. If older GHES versions without Issue.stateReason are still within the supported range (README currently states GHES 2.20+), FindIssueOrPR will start failing with GraphQL schema errors. Either align/support-policy docs to guarantee stateReason availability, or reintroduce a compatibility gate before adding this field to the query.

Copilot uses AI. Check for mistakes.
Comment on lines 146 to 151
if opts.Detector == nil {
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
}
features, err := opts.Detector.IssueFeatures()
if err != nil {
return err
}
fields := defaultFields
// TODO stateReasonCleanup
if features.StateReason {
fields = append(defaultFields, "stateReason")
}
fields := append(defaultFields, "stateReason")

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This change makes stateReason unconditional in the issue list GraphQL fragment. If GHES versions prior to when Issue.stateReason existed are still considered supported (the repo README currently claims GHES 2.20+), this will cause gh issue list to fail with a GraphQL schema error on those instances. Either update the documented minimum supported GHES version to a release where stateReason is guaranteed to exist, or keep a compatibility fallback (feature detection or version gating) before requesting this field.

Copilot uses AI. Check for mistakes.
@williammartin williammartin merged commit e17d203 into trunk Mar 5, 2026
22 checks passed
@williammartin williammartin deleted the remove-state-reason-duplicate-detection branch March 5, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants