Context
Observed live 2026-08-09T15:37Z during a post-review-pickup §6 lane-discovery sweep: list_issues({assignee: '@me', state: 'open'}) → GraphQL API error: Could not find an assignee with the login '@me'. The write path on the same service accepts the alias: manage_issue_assignees documents "Use @me to assign/unassign the authenticated user" and resolves it via #resolveAssigneeAliases (ai/services/github-workflow/IssueService.mjs:540-553 — cached-token GET /user, one round-trip, only when the alias is present). The read path passes the string straight into the GraphQL variables (IssueService.mjs:1353), where GitHub rejects it.
Filed as a friction → gold item on operator direction: the failed call burned a tool call mid-sweep and forced a retry with the concrete login.
Live latest-open sweep: checked latest 20 open issues at 2026-08-09T15:46Z — no equivalent. A2A in-flight sweep: 30 most recent messages at ~15:47Z — no [lane-claim]/[lane-intent] on this scope. KB semantic sweep: instrument timed out (15s, #16677-class wedge window) — substituted with an exact/semantic grep over resources/content/**, which surfaced the two sibling tickets below and no equivalent.
The Problem
"My open lanes" is the canonical ownership-truth query an agent runs at every intake, lane-claim collision check, and post-review pickup. The alias that works on the identity-taking write surfaces of the same server fails on exactly that query, with a raw GraphQL error that names no remediation. The per-occurrence cost is small; the contract inconsistency is the defect — agents generalize @me from the write path (and from memory-core's query_recent_turns, where agentIdentity: '@me' is the resolving default) and the read path punishes the generalization.
The Architectural Reality
listIssues filters assignee SERVER-side — the #15220 fix moved ownership truth to live GraphQL (ai/services/github-workflow/IssueService.mjs:1313-1356); the alias reaches GitHub verbatim inside filterBy: {assignee}.
- The resolution mechanism already exists in the same file:
#resolveAssigneeAliases (:540-553) maps @me → the authenticated viewer login via GraphqlService.rest('GET', '/user'), short-circuiting when no alias is present.
- Refusal-by-design precedent exists and is NOT this surface:
#16398 refuses @me at the query_recent_turns boundary because recall targeting must be explicit; an ownership filter has the opposite contract — the caller's own lanes are the dominant query.
- Read-side resolution precedent: memory-core's
query_recent_turns({agentIdentity: '@me'}) resolves the request-bound caller as its default.
The Fix
- In
listIssues, when assignee === '@me', resolve through the existing #resolveAssigneeAliases (or a shared extraction of it) before the hasAssigneeFilter/variables construction — same round-trip semantics as the write path (only when the alias is present).
- A resolution failure (no login from
/user) returns a named tool error — never a raw GraphQL rejection.
- Update the
list_issues tool description (ai/mcp/server/github-workflow tool surface + any doc mirror) to advertise the alias, matching the write tools' wording.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
IssueService.listIssues assignee param (:1291) |
this ticket |
@me resolves to the authenticated viewer login pre-query |
concrete logins pass through unchanged |
method docblock |
spec: alias-resolution + pass-through + failure-path tests |
#resolveAssigneeAliases (:540-553) |
pre-existing |
reused (or extracted shared) by the read path |
— |
existing docblock |
write-path spec stays green |
list_issues tool description |
this ticket |
advertises @me like manage_issue_assignees |
— |
tool description |
surface parity check |
Acceptance Criteria
Out of Scope
- Generalizing alias resolution to other read surfaces — none currently take identity filters; named here so the next one inherits the pattern.
list_pull_requests (no assignee filter exists there).
- Any change to the
#16398 explicit-target refusal contract.
Avoided Traps
- Do NOT fix only the error text (remediation-message-only): the friction is the failed call, not the wording — the alias works everywhere else on the server, so support is the consistent fix.
- Do NOT resolve client-side via a second tool call: the server owns the token and the cached
/user path; pushing resolution to every caller multiplies the round-trip per session.
Related
#15220 (read-path assignee filter went live-GraphQL — the surface this alias lands on; also names the 2026-07-12 write-side @me false-success bug) · #16398 (refusal-by-design counter-precedent) · #16677 (the MCP-wedge class that degraded the KB sweep instrument during filing)
Decision Record impact: none.
Origin Session ID: 5d15190b-c9da-4dfe-b448-2e67e2fdb9f6
Context
Observed live 2026-08-09T15:37Z during a
post-review-pickup§6 lane-discovery sweep:list_issues({assignee: '@me', state: 'open'})→GraphQL API error: Could not find an assignee with the login '@me'.The write path on the same service accepts the alias:manage_issue_assigneesdocuments "Use@meto assign/unassign the authenticated user" and resolves it via#resolveAssigneeAliases(ai/services/github-workflow/IssueService.mjs:540-553— cached-tokenGET /user, one round-trip, only when the alias is present). The read path passes the string straight into the GraphQL variables (IssueService.mjs:1353), where GitHub rejects it.Filed as a friction → gold item on operator direction: the failed call burned a tool call mid-sweep and forced a retry with the concrete login.
Live latest-open sweep: checked latest 20 open issues at 2026-08-09T15:46Z — no equivalent. A2A in-flight sweep: 30 most recent messages at ~15:47Z — no
[lane-claim]/[lane-intent]on this scope. KB semantic sweep: instrument timed out (15s,#16677-class wedge window) — substituted with an exact/semantic grep overresources/content/**, which surfaced the two sibling tickets below and no equivalent.The Problem
"My open lanes" is the canonical ownership-truth query an agent runs at every intake, lane-claim collision check, and post-review pickup. The alias that works on the identity-taking write surfaces of the same server fails on exactly that query, with a raw GraphQL error that names no remediation. The per-occurrence cost is small; the contract inconsistency is the defect — agents generalize
@mefrom the write path (and from memory-core'squery_recent_turns, whereagentIdentity: '@me'is the resolving default) and the read path punishes the generalization.The Architectural Reality
listIssuesfiltersassigneeSERVER-side — the#15220fix moved ownership truth to live GraphQL (ai/services/github-workflow/IssueService.mjs:1313-1356); the alias reaches GitHub verbatim insidefilterBy: {assignee}.#resolveAssigneeAliases(:540-553) maps@me→ the authenticated viewer login viaGraphqlService.rest('GET', '/user'), short-circuiting when no alias is present.#16398refuses@meat thequery_recent_turnsboundary because recall targeting must be explicit; an ownership filter has the opposite contract — the caller's own lanes are the dominant query.query_recent_turns({agentIdentity: '@me'})resolves the request-bound caller as its default.The Fix
listIssues, whenassignee === '@me', resolve through the existing#resolveAssigneeAliases(or a shared extraction of it) before thehasAssigneeFilter/variables construction — same round-trip semantics as the write path (only when the alias is present)./user) returns a named tool error — never a raw GraphQL rejection.list_issuestool description (ai/mcp/server/github-workflowtool surface + any doc mirror) to advertise the alias, matching the write tools' wording.Contract Ledger
IssueService.listIssuesassigneeparam (:1291)@meresolves to the authenticated viewer login pre-query#resolveAssigneeAliases(:540-553)list_issuestool description@melikemanage_issue_assigneesAcceptance Criteria
list_issues({assignee: '@me'})returns the same set aslist_issues({assignee: '<resolved login>'})for the authenticated token.Out of Scope
list_pull_requests(no assignee filter exists there).#16398explicit-target refusal contract.Avoided Traps
/userpath; pushing resolution to every caller multiplies the round-trip per session.Related
#15220 (read-path assignee filter went live-GraphQL — the surface this alias lands on; also names the 2026-07-12 write-side
@mefalse-success bug) · #16398 (refusal-by-design counter-precedent) · #16677 (the MCP-wedge class that degraded the KB sweep instrument during filing)Decision Record impact: none.
Origin Session ID: 5d15190b-c9da-4dfe-b448-2e67e2fdb9f6