Frontmatter
| title | feat(ai): real GitLab MergeRequestService — MR ops + tests (#12631) |
| author | neo-opus-vega |
| state | Merged |
| createdAt | Jun 6, 2026, 10:29 PM |
| updatedAt | Jun 6, 2026, 10:50 PM |
| closedAt | Jun 6, 2026, 10:50 PM |
| mergedAt | Jun 6, 2026, 10:50 PM |
| branches | dev ← agent/12631-gitlab-mr-service |
| url | https://github.com/neomjs/neo/pull/12653 |

PR Review Summary
Status: Approve+Follow-Up
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve+Follow-Up
- Rationale: The PR delivers the full #12631 service/tool surface on the ticket's declared L2 mocked-contract scope, with the only remaining risk explicitly constrained to post-merge live GitLab validation and the already-declared
gitlab-workflowinclusion in the shared OpenAPI compliance server list. That shape is better tracked as follow-up than another review cycle.
Thanks for the clean MR twin. The reuse of the existing GitLabClient, note mutations, and project-label resolver is the right boundary for the multi-tenant deployment concern: no second transport, no local-only graph behavior, no config singleton mutation.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12631 body and Contract Ledger; #12653 changed-file list; current sibling precedent in
IssueService.mjs,issueQueries.mjs,mutations.mjs,toolService.mjs, andOpenApiValidatorCompliance.spec.mjs; KB query for GitLab workflow/MR service precedent; official GitLab GraphQL reference forproject.mergeRequests,MergeRequestState,mergeRequestSetLabels,mergeRequestSetAssignees,mergeRequestSetReviewers, andMutationOperationMode; exact PR head939167c872873f0cfaca85bd90263e5a39722ccbchecked out intmp/pr-12653-review. - Expected Solution Shape: A correct implementation should mirror the merged issue-service surface through the existing provider transport and
makeSafe(..., gitlabSpec)boundary, not hardcode a second GitLab client or any local/cloud-specific runtime path. Test isolation should mockGitLabClient.query, prove variable forwarding / id resolution / user-error surfacing, and keep OpenAPI schema generation strict-client-safe. - Patch Verdict: Matches and slightly improves the expected shape. The diff replaces the list scaffold with list/get/comment/label/assignee/reviewer methods, reuses shared note and label queries, wires
toolService.mjs+ai/services.mjs, adds focused MR tests, and keeps live GitLab validation truthfully out of the claimed evidence.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12631
- Related Graph Nodes: Related: #11404, #12624, #12625; ADR 0019; ADR 0004; GitLab Workflow MCP parity
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge: The remaining unverified assumption is the live GitLab instance behavior of the new GraphQL strings. I did check GitLab's current reference and it confirms the core names/types used here (
authorUsername,MergeRequestState.all,mergeRequestSet*,APPEND/REMOVE), but this is still not a substitute for a live integration run. That is acceptable for this PR because #12631 AC7 explicitly requires truthful best-knowledge provenance, not live validation.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing matches what the diff substantiates; it claims real GraphQL behavior through
GitLabClient, not live-validated behavior. - Anchor & Echo summaries: the new JSDoc distinguishes mocked service logic from unvalidated live GitLab GraphQL strings.
-
[RETROSPECTIVE]tag: below is scoped to the reusable service-shape takeaway, not inflated beyond the diff. - Linked anchors: #12631 and related issue-service precedent support the MR twin framing.
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: None for this PR. The KB had issue-service precedent but no real MR-service implementation yet, which matches the ticket's stated gap.[TOOLING_GAP]:gitlab-workflowis still absent fromOpenApiValidatorCompliance.spec.mjs'sserverslist. This PR compensated with a direct strict-client schema probe for all 13 operations and documented the follow-up after #12649 to avoid collision.[RETROSPECTIVE]: The MR surface is strongest when it reuses the issue-service transport/query primitives and only adds MR-specific query/mutation constants; that keeps GitLab parity extensible without multiplying provider clients.
🎯 Close-Target Audit
For every issue named as close-target, verify it does NOT carry the epic label:
- Close-targets identified: #12631 from PR body
Resolves #12631. - #12631 labels checked live:
enhancement,ai,architecture,build; notepic. - Branch commit messages checked via
git log origin/dev..HEAD; noCloses/Fixes/Resolvesmagic keyword in commit body.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket contains a Contract Ledger matrix.
- Implemented PR diff matches the ledger:
list_merge_requests/get_merge_request,manage_mr_comment,manage_mr_labels,manage_mr_assignees, andmanage_mr_reviewersare present in OpenAPI, mapped intoolService.mjs, implemented inMergeRequestService.mjs, and covered by focused tests.
Findings: Pass.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - Achieved evidence is L2 mocked-
GitLabClientunit coverage, matching the ticket scope. - AC7 is handled as truthfulness/provenance: the PR explicitly says the GraphQL strings are not live-integration-validated and lists live validation under Post-Merge Validation.
- Review language does not promote L2 evidence to live GitLab validation.
Findings: Pass.
📡 MCP-Tool-Description Budget Audit
For every modified or added OpenAPI tool description:
- Single-line descriptions only; no block-literal descriptions.
- No internal ticket/session/phase refs in runtime tool descriptions.
- Descriptions stay call-site focused.
- 1024-char cap respected; measured longest description length is 115 chars.
Findings: Pass.
🔗 Cross-Skill Integration Audit
- New MCP tool surface is wired through the existing GitLab workflow OpenAPI +
toolService.mjsroute, matching the local server pattern. -
ai/services.mjsexports the singleton throughmakeSafe(..., gitlabSpec), preserving the existing service-boundary pattern. - No skill/startup workflow conventions are introduced by this PR.
- Follow-up integration gap is documented: add
gitlab-workflowto the shared OpenAPI compliance server list after #12649 lands.
Findings: All checks pass — no merge-blocking integration gaps.
🧪 Test-Execution & Location Audit
- Branch checked out locally at exact head
939167c872873f0cfaca85bd90263e5a39722ccb. - Canonical location: new unit test lives under
test/playwright/unit/ai/services/gitlab-workflow/, matching the service precedent. - Related tests run locally:
npm run test-unit -- test/playwright/unit/ai/services/gitlab-workflow/→ 37 passed. - MCP test-location lint run locally:
npm run ai:lint-mcp-test-locations→ OK. - OpenAPI validator compliance run locally:
npm run test-unit -- test/playwright/unit/ai/mcp/validation/OpenApiValidatorCompliance.spec.mjs→ 26 passed. - Direct gitlab-workflow strict-client probe run locally: 13 operations, 0 offenders.
-
git diff --check origin/dev...HEADpassed. - GitHub CI checked live: lint-pr-body, CodeQL, retired-primitives check, unit, and integration-unified are all green;
mergeStateStatusisCLEAN.
Findings: Tests pass.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 - 5 points deducted because live GitLab integration remains post-merge validation; otherwise the service correctly reusesGitLabClient,makeSafe, shared note mutations, shared label resolution, and does not introduce a second cloud/runtime transport path.[CONTENT_COMPLETENESS]: 95 - 5 points deducted because the sharedOpenApiValidatorCompliance.spec.mjsserver-list follow-up remains outside this PR; JSDoc and PR body are otherwise explicit about scope, evidence, and unvalidated live assumptions.[EXECUTION_QUALITY]: 95 - 5 points deducted because the GraphQL strings are docs/source-checked and unit-mocked but not live-instance checked; local focused tests, schema probes, diff check, and GitHub CI are green.[PRODUCTIVITY]: 100 - I actively considered stale ticket scope, duplicate-client risk, and partial MR-surface delivery; the PR completes #12631's service/tool-surface goals without expanding into syncer or MR creation scope.[IMPACT]: 80 - Major subsystem value: it turns GitLab Workflow's MR surface from scaffold/list-only into real read/write lifecycle operations needed for v13 client-project GitLab deployments.[COMPLEXITY]: 75 - High: seven changed files spanning service implementation, GraphQL constants, MCP OpenAPI schemas, tool routing, service exports, and focused tests, but it deliberately follows an existing issue-service template rather than introducing a new architecture.[EFFORT_PROFILE]: Heavy Lift - High-impact cross-surface implementation with meaningful test coverage and contract/schema validation, bounded by existing GitLab workflow patterns.
Approved for human merge gate. Follow-up remains live GitLab validation plus adding gitlab-workflow to the shared OpenAPI compliance server list once #12649 is settled.
Resolves #12631
Related: #11404
Brings GitLab's merge-request surface to parity with the issue surface: the list-only
MergeRequestServicescaffold is replaced with real GitLab GraphQL behavior — list / get / comment / labels / assignees / reviewers — driven through the sharedGitLabClient(no second client). Mirrors the provenIssueServiceshape (private#apiErrorResponse/#mutationErrorResponse/#userErrors/ id-resolvers), so a client-project GitLab deployment can now coordinate the full MR lifecycle through Neo's Agent-OS substrate.Evidence: L2 (mocked-
GitLabClientunit suite — 18 MR specs covering node→item mapping, filter forwarding, MR-gid + label-id resolution,userErrorssurfacing, assignee/reviewer set-mutations, structured read errors) → L2 required (every AC is unit/contract-scoped; AC7 explicitly defers live-GitLab integration to a follow-up). No residuals.Deltas from ticket
CREATE_NOTE/UPDATE_NOTE(mutations.mjs) and label resolution reusesGET_PROJECT_LABEL_IDS(issueQueries.mjs, project-scoped) rather than duplicating identical GraphQL. New files are MR-specific only:mrQueries.mjs(list / get / gid) +mrMutations.mjs(mergeRequestSetLabels/…SetAssignees/…SetReviewers).list_merge_requests, soget_merge_requestis added fresh.list/get) wrap transport failures in a structured{error, code}(this ticket's Contract Ledger) — a deliberate, JSDoc-documented divergence from the issue twin whose reads currently propagate. Aligning the issue reads is a noted trivial follow-up (out of scope to avoid touching freshly-merged code).mergeRequestSetLabels(labelIds+operationMode), cleaner than the issueupdateIssueadd/remove split — used accordingly; all three MR set-mutations share theAPPEND/REMOVEoperationMode shape.Test Evidence
UNIT_TEST_MODE=true playwright -c …unit.mjs test/playwright/unit/ai/services/gitlab-workflow/→ 37 passed (18 new MR specs + 19IssueServicespecs, confirming the shared-query reuse did not regress issues).npm run ai:lint-mcp-test-locations→ OK.OpenApiValidatorCompliance.spec.mjs→ 26 passed. Note:gitlab-workflowis not in that spec'sserverslist, so I ran its 4 strict-client guards (#10064 array-items, #9837 lenient-output, #10070 open-bag, #9837-overreach root-strict) directly against the gitlab spec — 13/13 ops build, 0 violations.openapi.yaml: valid YAML, all$refs resolve, no orphaned schemas; 13operationIds ↔ 13serviceMappingkeys exact match.Post-Merge Validation
gitlab-workflowtoOpenApiValidatorCompliance.spec.mjsserverslist so the issue/MR specs are CI-guarded like the other servers. Deferred here to avoid colliding with #12649, which is actively editing that spec file; best done as a rider after #12649 merges.Out of scope (per ticket)
create_merge_request(source/target branch + diff semantics) and theMergeRequestSyncer/LocalFileServicefile-IO subtask remain deferred.Coordination note
ai/services.mjsis also touched by #12649 (a single validation-import-path rename on a different line); this PR's change there is an additive import/makeSafe/export. Whichever lands first, the other rebases trivially.Authored by Claude Opus 4.8 (Claude Code). Session d55abb62-72e6-42e5-813a-21c0d4c8d00e.