feat(gitlab): issue and merge request writeback parity - #282
Conversation
Add GitLab REST v4 creation, branch refs, merge, and lifecycle writeback routes with discovery contracts and catalog entries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Session-Id: 01a0b7a6-3196-7081-b3db-70fc609b7d28
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reachedNext included review available in 2 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (35)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Devin Review found 3 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| }; | ||
| if (response.status >= 400) return { success: false, error: `${request.method} ${request.endpoint} failed with ${response.status}` }; | ||
| const result = response.data; | ||
| return { success: true, externalId: result?.id ? String(result.id) : result?.iid ? String(result.iid) : undefined }; |
There was a problem hiding this comment.
🔴 Created records use global IDs
When GitLab returns both identifiers, writeBack records id instead of project-scoped iid. Create receipts then direct later writes to a nonexistent issue or merge-request number.
Learn more
GitLab issues and merge requests expose a global id and a project-scoped iid. Relayfile mounts these records and addresses REST updates by iid, as shown by canonicalTarget. The generic file-native router uses externalId to rewrite a create draft into its resulting record path. Preferring the global ID therefore creates a valid-looking filename that later targets the wrong /:iid endpoint.
Example: GitLab creates issue { id: 700, iid: 7 }. The receipt currently identifies 700, so an edit targets /issues/700; the created issue is available at /issues/7.
Recommended fix: Prefer result.iid over result.id. Responses without an iid, such as notes and discussions, can continue using id.
| return { success: true, externalId: result?.id ? String(result.id) : result?.iid ? String(result.iid) : undefined }; | |
| return { success: true, externalId: result?.iid ? String(result.iid) : result?.id ? String(result.id) : undefined }; |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in 70de4fa9: issue/merge-request receipts prefer iid (fallback id), notes/discussions keep id, branch refs return the branch name. Live re-run against GitLab: issues create receipt is the iid — externalId=7 iid=7 id=203602926, merge-requests … externalId=8 iid=8 id=535560958, refs receipt = branch name. Unit tests added for each case.
| { | ||
| name: "merge", | ||
| path: "/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/merge.json", | ||
| pathPattern: /^\/gitlab\/projects\/.+?\/merge_requests\/[1-9]\d*(?:__[^\/]+)?\/merge\.json$/, | ||
| idPattern: /^[1-9]\d*(?:__.*)?$/, | ||
| schema: "discovery/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/merge.json/.schema.json", | ||
| createExample: "discovery/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/merge.json/.create.example.json", | ||
| operations: ["update"], | ||
| }, | ||
| { | ||
| name: "close-merge-request", | ||
| path: "/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/close.json", | ||
| pathPattern: /^\/gitlab\/projects\/.+?\/merge_requests\/[1-9]\d*(?:__[^\/]+)?\/close\.json$/, | ||
| idPattern: /^[1-9]\d*(?:__.*)?$/, | ||
| schema: "discovery/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/close.json/.schema.json", | ||
| createExample: "discovery/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/close.json/.create.example.json", | ||
| operations: ["update"], | ||
| }, | ||
| { | ||
| name: "refs", | ||
| path: "/gitlab/projects/{projectPath}/refs", | ||
| pathPattern: /^\/gitlab\/projects\/.+?\/refs(?:\/[^\/]+(?:\.json)?)?$/, | ||
| idPattern: /^$/, | ||
| schema: "discovery/gitlab/projects/{projectPath}/refs/.schema.json", | ||
| createExample: "discovery/gitlab/projects/{projectPath}/refs/.create.example.json", | ||
| operations: ["create"], |
There was a problem hiding this comment.
🟡 New writebacks lack path helpers
The new merge.json, close.json, refs, and create paths have no typed path-mapper helpers. Consumers cannot construct these writebacks through the adapter's supported path API.
Learn more
The adapter contract requires a typed path-mapper.ts helper for every canonical path the adapter emits. The new resource declarations publish several paths that the existing path mapper cannot construct directly. Generic metadata helpers cover issue and merge-request records, but not create collections, merge/close sidecars, or branch refs.
Example: A consumer can use computeMetadataPath for a merge-request meta.json, but must concatenate strings to produce its sibling merge.json. That string construction is explicitly unsupported and can drift from the catalog.
Recommended fix: Add typed helpers for the issue and merge-request create directories, branch-ref drafts, and merge-request merge/close sidecars. Add compose-and-parse round-trip tests for each helper.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Added in 70de4fa9: computeIssueCreateDraftPath, computeMergeRequestCreateDraftPath, computeRefCreateDraftPath, computeMergeRequestMergePath, computeMergeRequestClosePath, exported from the package, with compose→parse round-trip tests including a nested group/subgroup/project path.
| { | ||
| "name": "@relayfile/adapter-gitlab", | ||
| "version": "0.4.2", | ||
| "version": "0.5.0", |
There was a problem hiding this comment.
🟡 Package declares an unpublished version
The feature PR changes version to 0.5.0 before the release workflow runs. Consumers can pin a version that the workflow never publishes.
Learn more
Repository releases bump package versions during publishing. A feature PR must retain the currently published version because the workflow computes and commits the release bump later. Starting at 0.5.0 can make the workflow publish a later version while repository metadata temporarily advertises an unavailable one.
Example: A consumer reads this feature branch and pins @relayfile/adapter-gitlab@0.5.0. The release workflow applies its own bump and publishes another version, so installation of 0.5.0 fails.
Recommended fix: Restore 0.4.2 here and in package-lock.json; let the publish workflow select and commit the release version.
| "version": "0.5.0", | |
| "version": "0.4.2", |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Restored to 0.4.2 in 70de4fa9 (package-lock.json diff vs main is clean). The release will be cut with the publish workflow's minor bump.
|
Not ready to merge yet: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 52f7d19. Configure here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Session-Id: 01a0b7be-e7b0-7631-857e-3fa62f47bbc9
Live end-to-end verification (head
|
…th helpers, no pre-bump Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Session-Id: 01a0b7cd-aef0-75e1-9e1b-17ef1c310831

GitLab writeback parity for issues and merge requests
Problem
GitLab is advertised as a first-class Relayfile provider, but its writeback catalog previously exposed only merge-request discussions and issue notes. A GitLab-backed software factory could not create issues or merge requests through Relayfile.
Catalog
comments,discussionsissues(create + update),merge-requests(create + update),refs,merge,close-merge-request, plus the existingcommentsanddiscussionsNew canonical writeback templates:
/gitlab/projects/{projectPath}/issues/gitlab/projects/{projectPath}/issues/{issueIid}__{slug}/meta.json/gitlab/projects/{projectPath}/merge-requests/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/meta.json/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/merge.json/gitlab/projects/{projectPath}/merge_requests/{mergeRequestIid}__{slug}/close.json/gitlab/projects/{projectPath}/refsGitLab REST v4 endpoints
POST /projects/:id/issuesPUT /projects/:id/issues/:iidPOST /projects/:id/merge_requestsPUT /projects/:id/merge_requests/:iidPUT /projects/:id/merge_requests/:iid/mergePUT /projects/:id/merge_requests/:iidwithstate_event: close | reopenPOST /projects/:id/repository/branchesPayload validation rejects missing required fields, invalid lifecycle states, read-only fields, unknown fields, and unsupported paths. Label arrays are normalized to GitLab's comma-separated API representation.
Discovery and layout
Every new resource has non-empty JSON Schema and create-example contracts, is represented in the GitLab adapter discovery guide, layout manifest, LAYOUT prompt, mappings, and generated writeback catalog. The shared file-native router now supports nested GitLab
projectPathsegments when recognizing exact writeback sidecars.Validation
npm test -w @relayfile/adapter-gitlab— 93 passingnpm run typecheck -w @relayfile/adapter-gitlabnpm run build -w @relayfile/adapter-gitlabnpm test(workspace gate)npm run typecheck(workspace gate)npm run test:writeback-discovery— 119 endpoints verifiednpm run test:digest-contractswriteback.ts)Scope
Nothing was skipped.
refsis included because it is the GitLab equivalent of GitHub's branch-creation route and lets a flow create a branch before opening a merge request.f.gitlabwas not changed.Release note
@relayfile/adapter-gitlabis bumped from0.4.2to0.5.0for the new capability. Consumers that mount GitLab paths should resync to materialize the new discovery and catalog surface.Reviewer notes (orchestrator)
mainafter relayfile-adapters#281 / the 0.2.1 publish; re-rannpm test -w packages/gitlab(93/93), rootnpm run typecheck(55/55 tasks) and rootnpm test(105/105 tasks) post-rebase.packages/core/src/runtime/file-native-router.tsreadExactFileResourceIdno longer requires equal segment counts, so a{projectPath}placeholder can span a nested GitLab namespace (group/subgroup/project). Fixed suffix segments are still matched from the right; only placeholder templates are affected.packages/coregained mapping + generated catalog entries but its version is not bumped here — the publish workflow's changed-package mode should pick it up; shout if you want an explicit core bump in this PR.../flows): make thef.gitlabhelper's read/list gap visible or close it, per the "second-class provider" note that prompted this.🤖 Generated with Claude Code
Note
Medium Risk
Expands provider write surface (create branches, MRs, merges) and changes shared exact-file ID matching for nested GitLab project paths; mis-routing could affect non-GitLab adapters using similar templates.
Overview
Adds file-native GitLab writeback for issues, merge requests, branches, and merge-request lifecycle actions—beyond the prior discussions/notes-only surface.
Agents can draft creates under
issues/,merge-requests/, andrefs/, patch issue/MRmeta.json, and drive merge and close/reopen viamerge.json/close.jsonsidecars.GitLabWritebackHandlerroutes these to GitLab REST v4 with stricter payload validation (allowed fields, read-only rejection, label normalization,state_eventchecks).Discovery and contracts are wired through mappings, generated writeback catalog, JSON schemas/examples, layout manifest, and
LAYOUT.md. Path helpers andparseGitLabPathunderstandmerge-requestsvsmerge_requestsand new draft/sidecar paths.In adapter-core,
readExactFileResourceIdno longer requires equal segment counts so nested{projectPath}namespaces match exact sidecar templates (e.g.group/subgroup/project).Reviewed by Cursor Bugbot for commit 70de4fa. Bugbot is set up for automated code reviews on this repo. Configure here.