Skip to content

Consolidate actor-mode signals into ApiActorsSupported#13025

Merged
BagToad merged 6 commits intotrunkfrom
kw/refactor/reviewer-assignee-actor-symmetry
Mar 25, 2026
Merged

Consolidate actor-mode signals into ApiActorsSupported#13025
BagToad merged 6 commits intotrunkfrom
kw/refactor/reviewer-assignee-actor-symmetry

Conversation

@BagToad
Copy link
Member

@BagToad BagToad commented Mar 25, 2026

Description

So the diff here is gnarly but bear with me 😅. In #13009 I was frustrated seeing the divergence between how actor types were handled for assignees vs reviewers (this is my fault I'm sure).

Now, with all the context, I recognize that these two paths for handling actor types are not actually different. They're keying on the same feature detector field (ActorIsAssignable) anyway, but they ended up diverging and checking different things across the stack:

The divergence

There are two flows: create (pr create, issue create) uses IssueMetadataState, and edit (pr edit, issue edit) uses Editable. The actor signal was carried differently in each, and assignees vs reviewers were handled asymmetrically:

Create flows (via IssueMetadataState):

Layer Assignees Reviewers
State struct ActorAssignees field ActorReviewers field (separate, always same value)
MetadataSurvey Explicit state.ActorAssignees checks Only checked reviewerSearchFunc != nil
params.go mutation tb.ActorAssignees for login vs ID tb.ActorReviewers for login vs ID

Edit flows (via Editable):

Layer Assignees Reviewers
Editable struct EditableAssignees.ActorAssignees Nothing, no equivalent
RepoMetadataInput ActorAssignees field Nothing
RepoMetadata fetch Branched on ActorAssignees for actor-aware fetch No reviewer-specific branch
FetchOptions Skipped fetch when ActorAssignees true No equivalent optimization
Reviewer mutations Piggybacked on editable.Assignees.ActorAssignees1 No own field to check

Shared (used by both flows):

Layer Assignees Reviewers
Feature detection issueFeatures.ActorIsAssignable Same (single source of truth)

So the source was always one signal, but it fanned out into different field names on different structs, with reviewers missing coverage in the edit path entirely.

After this PR, it's just one field at every level:

Layer Assignees & Reviewers
Feature detection issueFeatures.ApiActorsSupported
IssueMetadataState (create) state.ApiActorsSupported
Editable (edit) editable.ApiActorsSupported
RepoMetadataInput input.ApiActorsSupported
MetadataSurvey state.ApiActorsSupported
params.go mutation tb.ApiActorsSupported
pr edit reviewer mutations editable.ApiActorsSupported

What this PR does

This PR calls it like it is: these are all the same capability signal.

  1. It consolidates ActorAssignees and ActorReviewers into a single ApiActorsSupported field that lives at the shared level on each struct (Editable, IssueMetadataState, RepoMetadataInput). Both assignees and reviewers key off the same signal.
  2. The feature detector field is also renamed from ActorIsAssignable to ApiActorsSupported so the name is consistent from detection through to consumption.
  3. BONUS: I also expanded the documentation on the feature detector to describe exactly when this codepath can be consolidated and what GraphQL schema additions to look for to confirm GHES support is ready. Previously the feature detector was multi-purposed and the documentation didn't reflect the breadth of what it affects.

Every branch site in the code is now tagged with // TODO ApiActorsSupported so a future cleanup can be done with a single grep.

Why one field and not separate feature detectors?

You may be wondering: these seem like somewhat different features (actor assignees vs actor reviewers vs search-based selection), so why not have separate feature detector fields?

In short, I believe it is simpler to group them. The harm is that, sure, maybe Copilot coding agent could hypothetically be allowed on GHES sooner than Copilot code reviewer or the other login-based mutations. But the complexity would matrix really quickly IMO, and the juice isn't worth the squeeze.

So: group all the actor stuff together, control it with one field. The state is easier to reason about and we can ship GHES support with more confidence because we know these things are compatible.

Acceptance & Regression Testing

38 scenarios tested across github.com and GHES 3.20, all passing: Acceptance test results

Notes For Reviewers

Commits are intentionally grouped. Some are chunky, but thematic, and IMO it's easier to review this way.

Commits

  1. 3c00ffdCore change: Consolidate actor signals into ApiActorsSupported on Editable, IssueMetadataState, RepoMetadataInput
  2. ae5e857Rename feature detector from ActorIsAssignable to ApiActorsSupported
  3. 92f205eDocument GHES removal criteria on the feature detector struct
  4. 6a68ebcNit: simplify redundant expression in survey.go where RepoMetadataInput.ApiActorsSupported was rechecking conditions already gated upstream
  5. bff468bFix: wire up @copilot assignee replacement in pr create and add [bot] suffix for replaceActorsForAssignable mutation (bugs introduced in Use login-based assignee mutation on github.com #13009)
  6. 391e661Fix: use useReviewerSearch consistently in the reviewer prompt path (matched existing assignee pattern)

Footnotes

  1. Particularly confusing: the reviewer mutation decision in pr edit was keyed off the assignee actor flag because reviewers didn't carry their own. See the write-up on #13009 for the full investigation.

BagToad and others added 4 commits March 24, 2026 21:04
…to ApiActorsSupported

The CLI had two per-entity flags (ActorAssignees on EditableAssignees and
IssueMetadataState, ActorReviewers on IssueMetadataState) threaded through
different layers of the stack to distinguish github.com from GHES. Both
flags were always set from the same source (issueFeatures.ActorIsAssignable)
and never had different values, but they were carried independently on
different structs. This led to a confusing asymmetry where:

- EditableAssignees had ActorAssignees but EditableReviewers had nothing
- The PR edit flow piggybacked on editable.Assignees.ActorAssignees to
  make reviewer mutation decisions, which was misleading
- RepoMetadataInput only had ActorAssignees with no reviewer equivalent

This commit replaces all per-entity flags with a single ApiActorsSupported
bool hoisted to the shared level on Editable, IssueMetadataState, and
RepoMetadataInput. Both assignees and reviewers now key off the same signal.

Every branch site is marked with // TODO ApiActorsSupported so we can grep
for cleanup sites when GHES eventually supports the actor-based mutations
(replaceActorsForAssignable, requestReviewsByLogin).

Co-authored-by: Copilot <[email protected]>
…orted

Aligns the feature detector field name with the downstream
ApiActorsSupported flag introduced in the previous commit, so the
signal has one consistent name from detection through to consumption.

Also consolidates leftover TODO tags (actorIsAssignableCleanup,
requestReviewsByLoginCleanup) under the single // TODO ApiActorsSupported
tag so there's exactly one thing to grep for.

Pure rename with no logic changes.

Co-authored-by: Copilot <[email protected]>
…upported

Expand the ApiActorsSupported doc comment to explain the two API
generations (legacy AssignableUser vs actor-based AssignableActor),
what each returns, and the specific GraphQL schema additions to
check for when evaluating GHES support.

Co-authored-by: Copilot <[email protected]>
The expression was redundantly re-checking whether assignees or
reviewers were chosen. RepoMetadata already gates on
input.Assignees || input.Reviewers before consulting
ApiActorsSupported, so passing state.ApiActorsSupported directly
is sufficient.

Co-authored-by: Copilot <[email protected]>
@BagToad BagToad requested a review from a team as a code owner March 25, 2026 04:03
@BagToad BagToad requested a review from babakks March 25, 2026 04:03
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

This PR consolidates the previously divergent “actor-mode” capability signals for assignees/reviewers across PR/issue create and edit flows into a single ApiActorsSupported flag, aligning feature detection, state/editable structs, metadata fetching, and mutation parameter selection.

Changes:

  • Replaces ActorAssignees / ActorReviewers (and ActorIsAssignable) with a single ApiActorsSupported capability flag across create/edit state and API metadata inputs.
  • Updates branching logic to consistently use login-based mutations (github.com/ghe.com) vs ID-based fallbacks (GHES) under the unified flag.
  • Expands and clarifies feature detection documentation for what “actor-based APIs supported” entails and what schema elements to check for GHES parity.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/cmd/pr/shared/survey.go Switches survey prefetch/search gating to the unified ApiActorsSupported flag.
pkg/cmd/pr/shared/state.go Consolidates create-flow state flags into ApiActorsSupported.
pkg/cmd/pr/shared/params.go Uses ApiActorsSupported to select login vs ID reviewer/assignee mutation params.
pkg/cmd/pr/shared/editable_http.go Uses ApiActorsSupported to decide when to do separate actor-assignment mutation.
pkg/cmd/pr/shared/editable.go Moves capability flag to Editable and threads it through fetch/clone and assignee handling.
pkg/cmd/pr/edit/edit.go Updates PR edit flow to key assignedActors fields, search wiring, and reviewer mutation choice off ApiActorsSupported.
pkg/cmd/pr/edit/edit_test.go Renames mocks/expectations to reflect ApiActorsSupported and updated behavior.
pkg/cmd/pr/create/create.go Wires search-based selection and state capability via ApiActorsSupported.
pkg/cmd/pr/create/create_test.go Updates expectation text for the renamed capability flag.
pkg/cmd/issue/edit/edit.go Updates issue edit flow to set/use ApiActorsSupported when assignees are edited.
pkg/cmd/issue/edit/edit_test.go Renames mocks/tests to align with ApiActorsSupported.
pkg/cmd/issue/create/create.go Switches create logic to pass ApiActorsSupported through assignee replacer and state.
internal/featuredetection/feature_detection.go Renames and documents feature detection as ApiActorsSupported.
internal/featuredetection/feature_detection_test.go Updates expected feature flag name/value in tests.
api/queries_repo.go Replaces ActorAssignees metadata fetch input with ApiActorsSupported and keeps actor/user branching.
api/queries_pr.go Updates comments/TODO markers to ApiActorsSupported terminology.
api/queries_issue.go Updates comments to ApiActorsSupported terminology.

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

Two bugs introduced in #13009 found during acceptance testing:

1. `pr create --assignee @copilot` sent the literal `@copilot` to the
   API because NewIssueState only ran MeReplacer on assignees, not
   CopilotReplacer. Fixed by switching to SpecialAssigneeReplacer (which
   handles both @me and @copilot) like issue create already does.

2. The replaceActorsForAssignable mutation requires the [bot] suffix on
   bot actor logins (e.g. `copilot-swe-agent[bot]`), unlike
   requestReviewsByLogin which has a separate botLogins field. Added the
   suffix in ReplaceActorsForAssignableByLogin for CopilotAssigneeLogin.

Co-authored-by: Copilot <[email protected]>
@BagToad BagToad force-pushed the kw/refactor/reviewer-assignee-actor-symmetry branch from 5265c35 to bff468b Compare March 25, 2026 05:26
The reviewer prompt branch checked reviewerSearchFunc != nil directly
instead of useReviewerSearch, making the fetch and prompt decisions
inconsistent. This mirrors how the assignee path already uses
useAssigneeSearch at both the fetch and prompt gates.

Co-authored-by: Copilot <[email protected]>
Copy link
Member

@williammartin williammartin left a comment

Choose a reason for hiding this comment

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

Seems like a fairly mechanical change. There does seem to be some missing test coverage around the api package so test coverage was a bit tricky to verify e.g. [bot] addition, but removing it does cause things to fail so 👍


type IssueFeatures struct {
ActorIsAssignable bool
// TODO ApiActorsSupported
Copy link
Member

Choose a reason for hiding this comment

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

Great comment.

@BagToad BagToad merged commit 8f7d208 into trunk Mar 25, 2026
53 checks passed
@BagToad BagToad deleted the kw/refactor/reviewer-assignee-actor-symmetry branch March 25, 2026 17:43
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