LearnNewsExamplesServices
Frontmatter
id13400
titlefix(github-workflow): route assignIssue add/remove-assignee gh-edits through GraphqlService.rest()
stateClosed
labels
enhancementairefactoringarchitecturemodel-experience
assigneesneo-opus-vega
createdAtJun 16, 2026, 3:08 AM
updatedAtJun 16, 2026, 9:50 AM
githubUrlhttps://github.com/neomjs/neo/issues/13400
authorneo-opus-vega
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 16, 2026, 9:50 AM

fix(github-workflow): route assignIssue add/remove-assignee gh-edits through GraphqlService.rest()

Closed v13.1.0/archive-v13-1-0-chunk-3 enhancementairefactoringarchitecturemodel-experience
neo-opus-vega
neo-opus-vega commented on Jun 16, 2026, 3:08 AM

Context

Split from #13352. That ticket's PR sliced createIssue off its per-call spawn('gh','issue','create') onto the new GraphqlService.rest() cached-token, retry-equipped REST path. The remaining per-call gh-spawn write surface in IssueService is assignIssue, whose mutations still shell out via execAsync('gh issue edit …').

Sites (ai/services/github-workflow/IssueService.mjsassignIssue)

  • clear-mode: gh issue edit … --remove-assignee ""
  • strict-replacement override clear: gh issue edit … --remove-assignee ""
  • add-mode: gh issue edit … --add-assignee "<login>"

(The reads — #fetchCurrentAssignees precondition + post-verify — already route through GraphqlService.query.)

Proposed fix

Reuse GraphqlService.rest() (landed in #13352's PR):

  • addrest('POST', '/repos/{owner}/{repo}/issues/{n}/assignees', {assignees})
  • remove/clear → fetch current via the existing #fetchCurrentAssignees, then rest('DELETE', '/repos/{owner}/{repo}/issues/{n}/assignees', {assignees: current})

REST takes assignee logins directly — no user node-ID resolution. The @me literal resolves the same as today (REST assignees accepts it via the issues endpoint's add-assignees semantics; verify in the leaf).

Must-preserve (unchanged)

The #11537 precondition / ASSIGNEE_CONFLICT gate, the strict-replacement audit-trail comment, and the post-verify re-fetch — only the 3 mutation shell-outs swap transport. The existing assignIssue precondition gate (#11537) spec block must stay green.

Why a separate ticket

assignIssue carries stateful conflict/audit/post-verify logic + its own precondition-gate tests; sliced from createIssue to keep that PR low-blast on the shared ticket-filing infra. No observed failure on this path → lower priority than the createIssue slice.

Acceptance criteria

  1. No execAsync('gh …') remains in assignIssue.
  2. #11537 precondition-gate, audit-comment, and post-verify behavior preserved (existing spec block green).
  3. clear-mode clears all current assignees via fetch-then-DELETE.
  4. Unit coverage for the rest-routed add + remove/clear paths (stub GraphqlService.rest, mirroring #13352's createIssue spec).

Classification

Release classification: not release-blocking (boardless). Robustness / gh-spawn-surface reduction; complements the gh-auth hardening cluster.

Authored by Claude Opus 4.8 (Claude Code), @neo-opus-vega (Vega).

tobiu referenced in commit b21c486 - "fix(github-workflow): route assignIssue/unassignIssue gh-edits through GraphqlService.rest (#13400) (#13408) on Jun 16, 2026, 9:50 AM
tobiu closed this issue on Jun 16, 2026, 9:50 AM