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.mjs — assignIssue)
- 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):
- add →
rest('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
- No
execAsync('gh …') remains in assignIssue.
- #11537 precondition-gate, audit-comment, and post-verify behavior preserved (existing spec block green).
- clear-mode clears all current assignees via fetch-then-
DELETE.
- 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).
Context
Split from #13352. That ticket's PR sliced
createIssueoff its per-callspawn('gh','issue','create')onto the newGraphqlService.rest()cached-token, retry-equipped REST path. The remaining per-call gh-spawn write surface inIssueServiceisassignIssue, whose mutations still shell out viaexecAsync('gh issue edit …').Sites (
ai/services/github-workflow/IssueService.mjs—assignIssue)gh issue edit … --remove-assignee ""gh issue edit … --remove-assignee ""gh issue edit … --add-assignee "<login>"(The reads —
#fetchCurrentAssigneesprecondition + post-verify — already route throughGraphqlService.query.)Proposed fix
Reuse
GraphqlService.rest()(landed in #13352's PR):rest('POST', '/repos/{owner}/{repo}/issues/{n}/assignees', {assignees})#fetchCurrentAssignees, thenrest('DELETE', '/repos/{owner}/{repo}/issues/{n}/assignees', {assignees: current})REST takes assignee logins directly — no user node-ID resolution. The
@meliteral resolves the same as today (RESTassigneesaccepts it via the issues endpoint's add-assignees semantics; verify in the leaf).Must-preserve (unchanged)
The #11537 precondition /
ASSIGNEE_CONFLICTgate, the strict-replacement audit-trail comment, and the post-verify re-fetch — only the 3 mutation shell-outs swap transport. The existingassignIssue precondition gate (#11537)spec block must stay green.Why a separate ticket
assignIssuecarries stateful conflict/audit/post-verify logic + its own precondition-gate tests; sliced fromcreateIssueto 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
execAsync('gh …')remains inassignIssue.DELETE.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).