# How GitLab Integration in no-mistakes Handles glab Version Drift and Detached-HEAD Worktrees

> Discover how no-mistakes GitLab integration prevents version drift and detached-HEAD worktrees using defensive flag handling and REST API fallbacks for seamless compatibility.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: deep-dive
- Published: 2026-07-18

---

**The GitLab integration in no-mistakes uses defensive flag handling and REST API fallbacks to remain compatible across glab CLI versions and function correctly in detached-HEAD worktrees.**

The no-mistakes repository implements a resilient GitLab integration that anticipates CLI instability and unconventional repository states. By wrapping the glab CLI with intelligent fallbacks in [`internal/scm/gitlab/gitlab.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/gitlab/gitlab.go), the code ensures continuous functionality despite version drift and detached-HEAD worktrees commonly encountered in daemonized workflows.

## Defensive Coding Against glab CLI Version Drift

The integration explicitly handles breaking changes between glab releases through defensive command construction and error detection. This prevents hard failures when flags are added, removed, or renamed across glab versions.

### Handling Removed Flags in Merge Request Listings

The `FindPR` method avoids the deprecated `--state opened` flag that was removed in glab v1.5x. Instead, it relies on glab's default behavior of returning only open merge requests.

```go
// Lines 63-71 in internal/scm/gitlab/gitlab.go
cmd := h.cmd(ctx, "glab", "mr", "list",
    "--search", pr.Number,
    "--output", "json")

```

The inline comment at Lines 63-71 explains the rationale: "glab v1.5x removed `--state opened` ... rely on the open-by-default behavior." This ensures the integration continues working across glab releases without requiring users to upgrade their CLI.

### Graceful Degradation for CI Status Checks

The `GetChecks` method implements a try-then-fallback pattern for the `--mr` flag. It first attempts `glab ci status --mr`, and if the command fails with an unsupported flag error, it falls back to an alternative pipeline listing path via `mr view`.

```go
// Lines 94-115 in internal/scm/gitlab/gitlab.go
func (h *Host) GetChecks(ctx context.Context, pr *scm.PR) ([]scm.Check, error) {
    cmd := h.cmd(ctx, "glab", "ci", "status", "--mr", pr.Number, "--output", "json")
    out, err := cmd.CombinedOutput()
    if err != nil && isUnsupportedMRFlagError(out) {
        // Older glab – use the MR-view → pipeline-jobs path.
        return h.getChecksFallback(ctx, pr)
    }
    return parseGitlabJobs(out)
}

```

The helper `isUnsupportedMRFlagError` recognizes various error messages produced by older glab versions to trigger the fallback correctly, making the integration resilient to version drift.

## Branch-Independent Operations in Detached-HEAD Worktrees

When the no-mistakes daemon runs in a temporary worktree without a checked-out branch, standard glab commands that depend on the current branch context would fail. The integration solves this by using branch-independent API calls when necessary.

### REST API Fallback for Pipeline Jobs

The `pipelineJobsArgs` function constructs a `glab api` request when the `projectPath` is known (derived from the repository's remote URL). This REST endpoint is branch-independent and functions correctly in detached-HEAD environments.

```go
// Lines 91-102 in internal/scm/gitlab/gitlab.go
func (h *Host) pipelineJobsArgs(pipelineID int) []string {
    if h.projectPath != "" {
        // Encode each path segment, then call the REST endpoint.
        // Works even when there is no checked-out branch.
        enc := url.PathEscape(h.projectPath)
        return []string{
            "api", "--paginate",
            fmt.Sprintf("projects/%s/pipelines/%d/jobs", enc, pipelineID),
        }
    }
    // Legacy fallback when we cannot determine the project path.
    return []string{
        "ci", "get", "--pipeline-id", fmt.Sprintf("%d", pipelineID),
        "--output", "json", "--with-job-details",
    }
}

```

### Legacy CLI Path for Unknown Project Paths

If the project path cannot be determined from the remote URL, the code gracefully degrades to `glab ci get`, which requires a checked-out branch but supports legacy setups. This ensures backward compatibility while modern deployments benefit from detached-HEAD safety.

## Scoped Authentication to Prevent Cross-Host Failures

The `Available` method prevents authentication errors from unrelated GitLab hosts by scoping the `glab auth status` check with `--hostname` when the repository host is known.

```go
// Lines 31-38 in internal/scm/gitlab/gitlab.go
func (h *Host) Available(ctx context.Context) error {
    authArgs := []string{"auth", "status"}
    if h.host != "" {
        authArgs = append(authArgs, "--hostname", h.host)
    }
    if err := h.cmd(ctx, "glab", authArgs...).Run(); err != nil {
        return errors.New("glab CLI is not authenticated")
    }
    return nil
}

```

This prevents stale credentials for other hosts from causing false-negative availability checks that would otherwise block the integration.

## Summary

- The `FindPR` method in [`internal/scm/gitlab/gitlab.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/gitlab/gitlab.go) omits the deprecated `--state opened` flag to maintain compatibility with glab v1.5x and newer.
- `GetChecks` implements a dual-path strategy: it attempts modern `--mr` syntax first, falling back to `mr view` pipeline jobs when `isUnsupportedMRFlagError` detects older glab versions.
- Detached-HEAD worktrees are supported through `glab api` REST calls in `pipelineJobsArgs`, which operate independently of the current branch state.
- Authentication checks use scoped `--hostname` flags to avoid interference from unrelated GitLab host credentials.
- Legacy fallbacks exist for both CI status checks (`glab ci get`) and project path resolution, ensuring backward compatibility.

## Frequently Asked Questions

### How does no-mistakes handle glab versions that don't support the --mr flag?

When `GetChecks` encounters an unsupported flag error from older glab versions, it triggers `getChecksFallback` to retrieve pipeline status through an alternative path using `mr view` and pipeline job listings. The `isUnsupportedMRFlagError` helper recognizes specific error strings from legacy glab releases to initiate this fallback automatically.

### Why does the integration use glab api instead of glab ci get in detached-HEAD scenarios?

The `glab ci get` command requires a checked-out branch to determine the pipeline context, which fails in detached-HEAD worktrees. The `pipelineJobsArgs` function instead constructs a `glab api` request to the GitLab REST endpoint `projects/{path}/pipelines/{id}/jobs`, which is branch-independent and functions correctly even when HEAD points to a specific commit rather than a branch reference.

### What prevents authentication errors from unrelated GitLab hosts?

The `Available` method scopes authentication checks using the `--hostname` flag when the repository host is known. This ensures that `glab auth status` only validates credentials for the specific GitLab instance, preventing expired or invalid tokens for other hosts from incorrectly reporting the CLI as unauthenticated.

### Which file contains the core GitLab integration logic?

The primary implementation resides in [`internal/scm/gitlab/gitlab.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/gitlab/gitlab.go). This file contains the `Host` struct and its methods including `FindPR`, `GetChecks`, `pipelineJobsArgs`, and `Available`, which collectively handle version drift, detached-HEAD worktrees, and scoped authentication.