LearnNewsExamplesServices
Frontmatter
id12631
titleReal MergeRequestService merge-request operations + tests
stateClosed
labels
enhancementaiarchitecturebuild
assigneesneo-opus-vega
createdAtJun 6, 2026, 2:07 PM
updatedAtJun 6, 2026, 10:50 PM
githubUrlhttps://github.com/neomjs/neo/issues/12631
authorneo-opus-vega
commentsCount0
parentIssue11404
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[x] 12624 GitLabClient + real IssueService issue operations + tests
blocking[]
closedAtJun 6, 2026, 10:50 PM

Real MergeRequestService merge-request operations + tests

Closed v13.0.0/archive-v13-0-0-chunk-16 enhancementaiarchitecturebuild
neo-opus-vega
neo-opus-vega commented on Jun 6, 2026, 2:07 PM

Context

#12624 shipped the GitLabClient (REST + GraphQL + PAT auth + pagination) and the real IssueService issue operations (PR #12625, APPROVED, in the merge queue). This ticket is the merge-request twin — the next #11404 sub per @neo-gpt's [epic-resolution] pointer ("#11404 KEEP_OPEN after #12625 — next GitLab sub should be non-micro MR/sync batch"). It brings GitLab's PR-equivalent (Merge Requests) to parity with the issue surface that just landed.

Live latest-open sweep: checked the latest 20 open issues at 2026-06-06; no equivalent MR-ops sub found (#12624 is issues-only; #12456 is the AiConfig-SSOT cleanup; no MergeRequestService / merge_request ticket exists). KB unavailable for the semantic pass — not treated as evidence of absence; the live tracker sweep is the authoritative duplicate gate here.

The Problem

Epic #11404 scopes the GitLab Workflow MCP server to issues + merge requests parity. Issues are done (#12624). The MR surface today is scaffold-only: ai/mcp/server/gitlab-workflow/openapi.yaml declares list_merge_requests / get_merge_request as scaffold entries with no backing service. Without real MR operations, a client-project deployment cannot coordinate the MR lifecycle (review routing, comments, labels, assignees/reviewers) through Neo's Agent-OS substrate — the exact gap the epic exists to close, now half-filled.

The Architectural Reality

Mirror the proven IssueService shape under ai/services/gitlab-workflow/:

  • MergeRequestService.mjs (new) — extends Neo.core.Base, @singleton; the MR twin of IssueService.mjs. Real ops via the existing GitLabClient (no new client). Private helpers mirror IssueService: #apiErrorResponse / #mutationErrorResponse / #userErrors / GID + label/assignee resolvers.
  • queries/mrQueries.mjs + queries/mrMutations.mjs (new) — GraphQL for project(fullPath){ mergeRequest(iid){…} mergeRequests{…} }, createNote, mergeRequestSetLabels, mergeRequestSetAssignees, mergeRequestSetReviewers. Sibling to the issue queries shipped in #12624.
  • ai/services.mjs — add import _GL_MergeRequestService, wrap via makeSafe(_GL_MergeRequestService, gitlabSpec) against the existing gitlabSpec, export. ADR 0019: read resolved AiConfig leaves at the use site; never re-implement / alias / mutate the shared singleton.
  • openapi.yaml — replace the 2 scaffold MR entries with real op descriptions + response schemas (MergeRequestResponse / MergeRequestCommentResponse / MergeRequestLabelResponse / MergeRequestAssigneeResponse), mirroring the 4 issue response schemas added in #12624.

Reuse, do not rebuild: GitLabClient (ai/services/gitlab-workflow/api/GitLabClient.mjs) is provider-agnostic transport — the MR service consumes it directly (AC6). shared/contentPath.mjs is out of scope here (that's the Syncer sub-task).

The Fix

  • New: ai/services/gitlab-workflow/MergeRequestService.mjs, queries/mrQueries.mjs, queries/mrMutations.mjs
  • Edit: ai/services.mjs (+import / makeSafe / export), ai/mcp/server/gitlab-workflow/openapi.yaml (real MR op descriptions + response schemas)
  • Tests: test/playwright/unit/ai/services/gitlab-workflow/MergeRequestService.spec.mjs (mock-HTTP pattern; auth, GraphQL shape, userErrors, label/assignee resolution) — parity with the IssueService spec

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
get_merge_request / list_merge_requests MCP tools #11404 epic; IssueService precedent (#12624); GitLab GraphQL mergeRequest/mergeRequests Real reads via GitLabClient GraphQL; structured {items,count} for list API error → structured #apiErrorResponse (no throw across the MCP boundary) openapi.yaml real schemas spec covers a mocked read round-trip
manage_mr_comment (createNote) GitLab createNote(noteableId: MR-GID); IssueService manageIssueComment precedent Create MR note; resolve MR GID from iid first userErrors surfaced via #mutationErrorResponse openapi.yaml spec asserts userErrors path
manage_mr_labels / manage_mr_assignees (+ reviewers) GitLab mergeRequestSetLabels / …SetAssignees / …SetReviewers; IssueService precedent Add/remove via set-mutations; resolve label/user IDs partial-resolution → structured error, no silent drop openapi.yaml spec covers add + remove

Row-level Surface-Anchor V-B-A: each GraphQL field above is verified against the GitLab GraphQL schema + the IssueService twin already merged-pending in #12625; create_merge_request is deferred (MR creation needs source/target branch + diff semantics beyond this batch — see Out of Scope) unless review says otherwise.

Decision Record impact

  • aligned-with ADR 0019 (AiConfig reactive Provider SSOT — makeSafe wiring reads resolved leaves; no singleton mutation).
  • aligned-with ADR 0004 (§1.3 provider-agnostic regeneratable-cache substrate — GitLab-as-source-of-truth generalizes verbatim).

Acceptance Criteria

  • AC1: MergeRequestService.mjs implements real read + comment + label + assignee/reviewer operations via GitLabClient (no scaffold responses).
  • AC2: queries/mrQueries.mjs + queries/mrMutations.mjs carry the GraphQL with GID resolution mirroring the issue queries.
  • AC3: ai/services.mjs wraps the service via makeSafe(..., gitlabSpec) + exports it; reads resolved AiConfig leaves at use site (ADR 0019), no singleton mutation.
  • AC4: openapi.yaml MR ops carry real descriptions + response schemas (no scaffold labels on the implemented ops).
  • AC5: Test coverage parity with IssueService.spec.mjs (auth, GraphQL shape, userErrors, label/assignee resolution) — mock-HTTP, no live instance.
  • AC6: Consumes the existing GitLabClient directly — no duplicate client implementation.
  • AC7: JSDoc is best-knowledge, explicitly NOT integration-validated against a live GitLab instance (the #12625 overclaim lesson — truthful provenance, no "validated against live GitLab").

Out of Scope

  • MergeRequestSyncer + LocalFileService — epic Sub-task 3, a separate sub (the file-IO + resources/content/gitlab/merge-requests/ substrate). This ticket is the service/tool surface only.
  • create_merge_request — MR creation (source/target branch + diff semantics) is heavier than the read/comment/label/assignee batch; deferred to a follow-up unless cross-family review pulls it in.
  • GitLab Discussions / Pipelines / OIDC — per epic Out of Scope.

Avoided Traps

  • Re-implementing GitLabClient — rejected; the #12624 client is provider-agnostic transport, consumed directly (AC6). A second client is migration debt.
  • Claiming live-integration validation — rejected; #12625's reviewer (@neo-gpt) caught exactly this overclaim. JSDoc states best-knowledge-not-yet-integration-validated (AC7).
  • Mutating the shared AiConfig singleton — rejected (ADR 0019 / the #12335 live-DB-bleed class); read resolved leaves at use site only.

Related

  • Epic parent: #11404 (GitLab Workflow MCP Server parity).
  • Issue twin + dependency: #12624 / PR #12625 (the GitLabClient this reuses; APPROVED, unmerged). Implementation note: branch stacks on the #12624 branch to consume GitLabClient, OR lands after #12625 merges. This is stackable, not a hold — work proceeds on the stacked branch.
  • ADRs: 0019 (AiConfig SSOT), 0004 (provider-agnostic substrate).

Authored by Claude Opus 4.8 (Claude Code). Origin Session ID: a54e89a3-4259-4b41-9e26-561f665de744

Retrieval Hint: "GitLab MergeRequestService merge-request operations GraphQL mergeRequestSetLabels makeSafe gitlabSpec #11404 twin of #12624"

tobiu referenced in commit 01bc4fb - "feat(ai): real GitLab MergeRequestService — MR ops + tests (#12631) (#12653) on Jun 6, 2026, 10:50 PM
tobiu closed this issue on Jun 6, 2026, 10:50 PM