Skip to content

fix(gitlab): thread MR search query and surface MR base failures (#6263)#6591

Merged
nwparker merged 1 commit into
mainfrom
nwparker/fix-6263
Jun 29, 2026
Merged

fix(gitlab): thread MR search query and surface MR base failures (#6263)#6591
nwparker merged 1 commit into
mainfrom
nwparker/fix-6263

Conversation

@nwparker

@nwparker nwparker commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🧑‍🤝‍🧑 Impact & ELI5

1) What's broken today (ELI5). GitLab users creating a new worktree hit two real blockers (both reported in #6263, reliably reproducible). First, the MR search box doesn't work: you type the merge-request's name or number to find it, the spinner spins, but the tiny dropdown only ever shows the same unfiltered list — so you basically can't pick the MR you want. The only escape hatch was pasting the full MR web link. Second, and worse, even once you finally got an MR selected, the new worktree was built off the wrong branch — it quietly landed on the repo's default branch (origin/master) instead of the MR's actual branch, with no warning, so you'd start working on the wrong code without realizing it. This affects GitLab users specifically (GitHub already worked); for them it's a genuine blocker on a core action.

2) What this PR does (ELI5). Think of the search box as a waiter who took your order but never carried it to the kitchen. Your order (the typed query) was thrown in the trash at the door — the kitchen (the GitLab API) actually knew how to handle it, nobody ever delivered it. This PR connects the whole chain end-to-end so what you type really reaches GitLab and filters the list. For the wrong-branch problem: previously, if Orca couldn't figure out the MR's base branch it just gave up silently and used the default branch. Now it (a) tells you with an error message when it can't resolve the base, and (b) is smarter — if a merged MR's target branch was already deleted, it no longer throws away the perfectly good source branch it already found; it keeps it. So: it now actually filters your search and lands you on the right branch (or clearly tells you when it can't), instead of silently failing.

3) Tradeoffs / regressions — honest answer. No regressions — nothing is disabled, no feature is turned off, no new setup or setting is required, and no behavior anyone relied on is lost. The search-query plumbing is purely additive and GitLab-only (GitHub already had it); when no query is typed the behavior is byte-for-byte identical to before. The base-resolution change strictly improves correctness: the only "behavior" it removes is the silent wrong-branch fallback, which was never a feature anyone wanted. One honest nuance worth naming: the optional diff-compare base ref can now be dropped when its branch fetch fails — but that only affects the diff comparison baseline in an edge case (a merged MR whose target ref was already deleted), and the previous behavior there was strictly worse (it aborted entirely and dumped you on the default branch). The search input is also length-capped (2048 bytes) at the boundary to match the existing renderer limit, which no real query approaches. Net: GitLab users gain, nobody loses.


Summary

Fixes #6263.

Two distinct GitLab create-worktree defects:

Defect 1 — GitLab search never filtered. The typed search query was dropped before it ever reached the GitLab API. The downstream layers already supported search (listMergeRequests builds &search=, and the runtime RPC forwards params.query), but no caller supplied it: the renderer GitLab list-mode effect called listGitLabMRsForSource without the query (and debouncedQuery wasn't even in its dependency array), listGitLabMRsForSource had no query param on either branch, and the desktop IPC handlers gitlab:listMRs / gitlab:listWorkItems passed a hardcoded undefined in the query slot. So typing an MR name/number/!N just re-fetched the unfiltered opened list (only pasting a full MR URL worked, via the separate paste-URL path).

The fix threads query?: string end-to-end (renderer effect → listGitLabMRsForSource → preload/RPC args + PreloadApi type → IPC handlers), normalizing it on the main side (normalizeGitLabSearchQuery: trim, cap at the same 2048-byte budget the renderer enforces). It is honored on both the glab REST path (&search=<encoded>) and the cwd-inferred glab mr list fallback (--search), so self-hosted / unresolved-projectRef repos filter too.

Defect 2 — worktree created off the default branch when MR base resolution failed. The renderer silently early-returned on a {error} result from resolveMrBase and had no .catch, unlike the GitHub path which toasts. When resolution errored, baseBranch stayed undefined and the worktree was created off the repo default branch (origin/master) with no feedback. The fix surfaces the failure via toast.error and clears stale base state, mirroring the GitHub PR handler. A contributing robustness bug in resolveManagedMrBase aborted the entire resolution if the optional target/compare-branch fetch failed — even after the MR source branch was successfully fetched and verified — discarding a valid base (common for merged MRs whose target ref was deleted). It now degrades gracefully: log a warning, drop compareBaseRef, and still return the valid source-branch base.

All changes are platform-agnostic (no metaKey/path-sep), consider SSH/remote (resolution already routes via sshGitProvider; the RPC query path already worked), and the query plumbing is GitLab-side only (GitHub already had it).

Screenshots

This is a behavioral fix to the GitLab MR list/search and base-resolution plumbing. Verified end-to-end in a running Orca dev build over CDP rather than by a static screenshot — see the evidence comment below: a live window.api.gl.listMRs({ query: 'login' }) against a real gitlab.com project with three open MRs returned only the matching MR (Add login validation flow), proving the query now reaches the API and filters. Unit/integration tests cover all changed layers.

Testing

  • pnpm lint (oxlint, scoped to changed files — clean)
  • pnpm typecheck (tsgo node + web — clean)
  • pnpm test (affected suites — 611 passing; see evidence comment)
  • pnpm build
  • Added/updated tests that catch this regression (IPC query forwarding, glab REST + CLI-fallback --search, source-lookup query threading, resolveManagedMrBase compare-base degradation, and the renderer error-surfacing path)

AI Review Report

Reviewed the diff adversarially for correctness, edge cases, and cross-platform safety. Risks checked:

  • Cross-platform (macOS/Linux/Windows): no hardcoded metaKey, no path-separator assumptions; the touched code routes git through gitExec/sshGitProvider and glab through glabExecFileAsync, identical across local/WSL/SSH. The WSL-routing IPC tests still pass with the new query arg (passed as the 6th positional, undefined when absent).
  • Empty/whitespace query: normalized to undefined so no stray &search=/--search is emitted; covered by tests.
  • SSH/remote: resolveManagedMrBase keeps SSH/WSL/local paths identical; the compare-base degradation applies uniformly.
  • GitLab vs other providers: the query plumbing is GitLab-namespaced only; GitHub already threads its query.
  • React effect deps: added debouncedQuery to the GitLab list-mode effect deps (it was missing); shouldQueryGitlab already gates oversized queries via sourceQueryWithinLimit.

Security Audit

  • Input handling: the search string is bounded (trim + 2048-byte cap at the IPC boundary, matching the renderer) before reaching glab.
  • Command execution / injection: glab is invoked via execFile with array args (no shell); the REST path encodeURIComponents the query into the URL; the CLI fallback passes it as a discrete --search <value> arg. No injection vector.
  • Path handling / secrets / auth: none touched. No new dependencies, no eval, no exfiltration. IPC handlers retain the existing assertRegisteredRepo filesystem-auth boundary.

Notes

  • Added one translate() key (Failed to resolve MR base.) and ran pnpm run sync:localization-catalog; non-English catalogs carry the English fallback pending translation, consistent with the existing workflow.
  • Future suggestion (out of scope): the GitLab cwd-fallback glab mr list uses the deprecated --opened flag and approximates totals from the page length; both are pre-existing and unrelated to this bug.

Made with Orca 🐋

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 79723680-2aeb-4886-acda-21cacff0e12e

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6f88b and dba412a.

📒 Files selected for processing (19)
  • src/main/gitlab/client-mr.test.ts
  • src/main/gitlab/client.ts
  • src/main/gitlab/gitlab-preload-args.ts
  • src/main/ipc/gitlab.test.ts
  • src/main/ipc/gitlab.ts
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/preload/api-types.ts
  • src/preload/gitlab.ts
  • src/renderer/src/components/new-workspace/SmartWorkspaceNameField.tsx
  • src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts
  • src/renderer/src/hooks/useComposerState.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/gitlab-work-item-source-lookup.test.ts
  • src/renderer/src/lib/gitlab-work-item-source-lookup.ts

📝 Walkthrough

Walkthrough

This PR makes two independent improvements. First, it adds optional free-text search query filtering to GitLab MR and work-item listings: a new normalizeGitLabSearchQuery helper is introduced, the preload API types and IPC handlers are updated to accept and normalize a query field, the GitLab client's cwd-fallback path gains a --search flag, and the renderer's SmartWorkspaceNameField threads debouncedQuery into the listing request. Second, it makes compare-base ref fetching optional during MR base resolution: a new fetchCompareBaseRef helper returns false on failure instead of aborting resolution, and useComposerState is updated to clear relevant state and show a toast error when resolution fails or rejects.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main GitLab MR query and base-failure fixes.
Description check ✅ Passed The description follows the template with Summary, Screenshots, Testing, AI Review Report, Security Audit, and Notes filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker

Copy link
Copy Markdown
Contributor Author

Evidence

Unit / integration tests (611 passing across the affected suites)

$ ./node_modules/.bin/vitest run --config config/vitest.config.ts \
    src/main/ipc/gitlab.test.ts src/main/gitlab/client-mr.test.ts \
    src/main/runtime/orca-runtime.test.ts \
    src/renderer/src/lib/gitlab-work-item-source-lookup.test.ts \
    src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts \
    src/main/runtime/rpc/methods/gitlab.test.ts \
    src/renderer/src/web/web-preload-api.test.ts

 Test Files  7 passed (7)
      Tests  611 passed (611)

New/extended regression tests:

  • gitlab.test.tsgitlab:listMRs / gitlab:listWorkItems now forward the trimmed query into the 6th positional arg (previously hardcoded undefined); blank/whitespace → undefined.
  • client-mr.test.ts — REST path appends &search=fix%20login (and omits it when blank); the cwd glab mr list fallback now threads --search <value> (and omits it when blank).
  • gitlab-work-item-source-lookup.test.ts — query reaches both the runtime RPC params and the local Electron IPC args.
  • orca-runtime.test.tsresolveManagedMrBase keeps baseBranch = origin/<source> (with compareBaseRef dropped, no {error}) when the optional compare-base fetch fails (merged-MR / deleted-target-ref case).
  • useComposerState-host-context-boundaries.test.ts — the GitLab MR-select path now toast.errors and has a .catch, instead of silently returning.

Lint / typecheck (clean)

$ ./node_modules/.bin/oxlint <14 changed .ts/.tsx files>     # exit 0
$ ./node_modules/.bin/tsgo --noEmit -p config/tsconfig.node.json    # exit 0
$ ./node_modules/.bin/tsgo --noEmit -p config/tsconfig.tc.web.json  # exit 0
$ node config/scripts/verify-localization-catalog.mjs               # exit 0 (catalogs in sync)

Live end-to-end verification (running Orca dev build over CDP)

Set up a real gitlab.com project (neil148/test) registered in Orca with three open MRs:
!1 Add login validation flow, !2 Refactor payment gateway timeout, !3 Implement dark mode toggle.

Direct GitLab API/CLI confirm search filters server-side:

$ glab api "projects/neil148%2Ftest/merge_requests?...&state=opened&search=login"  -> ["Add login validation flow"]   (1 of 3)
$ glab api "...&search=dark"   -> ["Implement dark mode toggle"]   (1 of 3)

Through the running fixed app (CDP eval on window.api.gl.listMRs), the typed query now reaches the API and filters:

window.api.gl.listMRs({ repoPath, repoId, state:'opened', perPage:12, query:'login' })
  -> items: ["Add login validation flow"]      // 1 of 3, was previously all 3

I confirmed the running main bundle contains the fix (normalizeGitLabSearchQuery(args.query) in the gitlab:listMRs handler; &search= in the REST path; --search in the CLI fallback).

Note on the visual repro: I drove this over CDP at the IPC layer rather than capturing a dropdown screenshot. The synthetic test repo (a thin local repo pointing at the real GitLab remote) occasionally fell through glab's local project-ref inference between rapid calls, making the dropdown-list screenshot flaky; the IPC-level result above is the deterministic proof that the query is now threaded end-to-end and filters. A normally-cloned GitLab repo takes the REST path consistently. Test MRs were closed after verification.

Defect 1: the typed GitLab MR search query was dropped before reaching
the API. Thread query?: string end-to-end through the renderer effect,
the source-lookup, the preload/RPC args, and the desktop IPC handlers
(which previously passed a hardcoded undefined), and honor it on both the
glab REST path (&search=) and the cwd-inferred 'glab mr list' fallback.

Defect 2: when MR base resolution failed the renderer silently returned,
leaving baseBranch undefined so the worktree was created off the repo
default branch (origin/master) with no feedback. Surface the failure via
toast and clear stale base state, mirroring the GitHub PR path. Also make
resolveManagedMrBase resilient to an optional compare-base (target branch)
fetch failure: degrade gracefully by dropping compareBaseRef instead of
aborting, so a merged MR with a deleted target ref still resolves to its
valid source-branch base.

Fixes #6263

Co-authored-by: Orca <help@stably.ai>
@nwparker
nwparker force-pushed the nwparker/fix-6263 branch from 1278b5c to dba412a Compare June 29, 2026 00:15
@nwparker

Copy link
Copy Markdown
Contributor Author

📸 Screenshot proof (rebased build — live Orca dev, CDP-driven)

After rebasing onto main (conflicts resolved, verify ✅, MERGEABLE/CLEAN), I exercised the fix end-to-end in a running Orca dev build attached over CDP. To make the visual repro deterministic I cloned a real gitlab.com project (neil148/test) via a normal HTTPS clone (so it takes the consistent REST path), registered it in Orca, and reopened three MRs:

  • !1 Add login validation flow
  • !2 Refactor payment gateway timeout
  • !3 Implement dark mode toggle

These shots are from the Create worktree → GitLab tab — the exact surface #6263 reported as broken.

Defect 1 — search now filters the MR list

Unfiltered (no query): all 3 open MRs are listed.

GitLab MR list, unfiltered — 3 MRs

Typing login → the list filters to the one matching MR (!1 Add login validation flow).

GitLab MR search 'login' — 1 result

Typing payment → filters to !2 Refactor payment gateway timeout.

GitLab MR search 'payment' — 1 result

Before this PR the typed query was dropped before reaching the API, so all three rows stayed visible no matter what you typed. Now the query is threaded end-to-end and the list narrows to the match.

Defect 2 — worktree lands on the MR's branch, not the default

Selecting !1 resolves cleanly and the MR is pinned as the create-from source.

MR !1 selected as create-from source

Advanced panel — name 'login', note 'MR !1 — Add login validation flow'

Driving the real IPC the PR fixes (window.api.worktrees.resolveMrBase) confirms it resolves to the MR's actual source branch, not origin/main:

window.api.worktrees.resolveMrBase({ repoId, mrIid: 1,
  sourceBranch: 'stably-session-cmmb6alvc0002ky04i797mzsb', targetBranch: 'main' })
// =>
{
  "baseBranch": "origin/stably-session-cmmb6alvc0002ky04i797mzsb",  // the MR branch, not origin/main
  "compareBaseRef": "refs/remotes/origin/main",
  "pushTarget": { "remoteName": "origin", "branchName": "stably-session-cmmb6alvc0002ky04i797mzsb" }
}

And the search query reaches the API through the same IPC the renderer calls (window.api.gl.listMRs):

listMRs({ repoPath, state:'opened', perPage:20 })                 // -> 3 MRs (unfiltered)
listMRs({ repoPath, state:'opened', perPage:20, query:'login' })  // -> ["Add login validation flow"]   (1 of 3)
listMRs({ repoPath, state:'opened', perPage:20, query:'payment' })// -> ["Refactor payment gateway timeout"] (1 of 3)

Rebase verification

  • Resolved 5 i18n locale conflicts (kept main's setupAgentStartupPolicySaveFailed + this PR's 5f3d2c8a1b); dropped an accidental node_modules symlink that had been committed.
  • pnpm typecheck (node + cli + web) — clean
  • pnpm lint (oxlint + localization catalog/coverage) — clean
  • Affected suites — 565 passing across client-mr, ipc/gitlab, orca-runtime, useComposerState-host-context-boundaries, gitlab-work-item-source-lookup
  • GitHub verify check — ✅ pass

Test MRs were re-closed after capture. Images hosted on a public scratch repo and referenced by raw URL (no gh-attach).

@nwparker
nwparker merged commit 31bfeff into main Jun 29, 2026
1 check passed
@nwparker
nwparker deleted the nwparker/fix-6263 branch June 29, 2026 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gitlab search doesn't work, and creates wrong workspace when an MR or branch is selected

1 participant