# How no-mistakes Handles PR Targeting from Bare Gate Repositories

> Learn how no-mistakes prevents accidental PR edits in bare gate repos by using explicit repo and PR identifiers, avoiding branch-based inference for secure targeting.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: how-to-guide
- Published: 2026-07-23

---

**The no-mistakes daemon avoids accidental PR edits by explicitly passing `--repo` and PR identifiers to every `gh` CLI invocation, refusing to fall back to branch-based inference when running from a detached bare gate repository.**

When the no-mistakes pipeline runs inside a *bare gate* repository—a detached worktree containing only `.git` metadata with `HEAD` pointing at the default branch—the `gh` CLI cannot infer the current repository or branch from the working directory. To prevent targeting errors, the tool implements explicit safeguards that fully qualify every GitHub operation with the correct repository slug and PR identifier.

## The Bare Gate Repository Challenge

A bare gate repository is a minimal Git environment created by the daemon (see [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)) that lacks a checked-out working tree. In this state, `gh pr` commands would normally attempt to infer the target PR from the current branch (typically `main`), leading to "no pull requests found for branch main" errors or, worse, accidental modifications to the wrong PR. The GitHub SCM implementation in [`internal/scm/github/github.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/github/github.go) eliminates this ambiguity through three coordinated mechanisms.

## Three Safeguards for Explicit PR Targeting

The `github.Host` struct ensures deterministic PR operations by combining repository-level and PR-level explicit targeting.

### Explicit Repository Flagging with `repoArgs()`

The `Host.repoArgs()` method returns `[]string{"--repo", h.repo}` whenever the repository slug (`owner/name`) is known. Every `gh` command constructed by the host appends these arguments, guaranteeing that `gh` resolves the correct remote repository regardless of the process's working directory.

```go
// Simplified from internal/scm/github/github.go
func (h *Host) repoArgs() []string {
    if h.repo == "" {
        return nil
    }
    return []string{"--repo", h.repo}
}

```

This ensures commands like `gh pr create` or `gh pr edit` always target `owner/repo` even when executed from a bare gate directory.

### Strict PR Selection via `prSelector()`

The `prSelector(pr *scm.PR)` function enforces explicit PR identification by preferring the PR number and falling back to the canonical PR URL. If neither value is available, it returns a hard error:

```go
return "", errors.New("no PR number or URL known; refusing to run gh with a cwd-inferred branch")

```

This guard prevents the daemon from invoking `gh pr <verb>` without a specific target, which would otherwise cause `gh` to infer the PR from the current branch—an operation that always fails or misfires in a bare gate context.

### Fork-Aware Head Reference Handling

The `headRef(branch string)` method generates the correct `--head` argument for `gh pr create`. When operating on a fork, it returns `<forkOwner>:<branch>`; otherwise, it returns the branch name alone. This ensures the GitHub CLI receives the properly qualified `owner:branch` pair required when the source repository differs from the target.

## PR Operations in Practice

These safeguards coordinate across all PR lifecycle methods in [`internal/scm/github/github.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/github/github.go).

### Creating a Pull Request

When `CreatePR` is invoked from a bare gate, it constructs a fully qualified command:

```go
// Assume h is a *github.Host with repo slug "owner/repo" and forkOwner ""
pr, err := h.CreatePR(ctx, "feature/foo", "main", scm.PRContent{
    Title: "Add foo feature",
    Body:  "This PR implements foo.",
})

```

The generated command includes both the repository flag and head reference:

```bash
gh pr create --head feature/foo --base main --repo owner/repo \
    --title "Add foo feature" --body-file -

```

### Updating an Existing PR

`UpdatePR` uses `prSelector` to obtain a concrete identifier before appending the repository flag:

```go
existingPR := &scm.PR{Number: "42", URL: "https://github.com/owner/repo/pull/42"}
_, err := h.UpdatePR(ctx, existingPR, scm.PRContent{
    Title: "Fix typo",
    Body:  "Corrected a typo in the README.",
})

```

This produces:

```bash
gh pr edit 42 --repo owner/repo --title "Fix typo" --body-file -

```

### Querying PR State and Checks

Read operations like `GetChecks`, `GetPRState`, and `GetMergeableState` follow the same pattern:

```go
checks, err := h.GetChecks(ctx, existingPR)

```

Invokes:

```bash
gh pr checks 42 --repo owner/repo --json name,state,bucket,completedAt

```

## Validation Through Testing

The unit tests in [`internal/scm/github/github_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/github/github_test.go) verify that bare gate scenarios cannot trigger branch-based inference. A specific test case titled *"detached bare gate repo and edit the wrong PR"* ensures that `UpdatePR` fails explicitly rather than falling back to a branch-derived selector. Additional tests confirm that `FindPR` constructs `gh pr list` with the correct `--head` and `--repo` flags even when operating outside a normal Git checkout.

## Summary

- **Explicit repository flags**: Every `gh` command includes `--repo owner/name` via `Host.repoArgs()`, eliminating reliance on the working directory's Git context.
- **Mandatory PR identifiers**: The `prSelector` function requires either a PR number or URL, refusing to execute when only branch information is available.
- **Fork-qualified references**: The `headRef` method correctly formats `owner:branch` pairs for cross-fork PR creation.
- **Test coverage**: Unit tests validate that bare gate operations remain deterministic and cannot accidentally target incorrect PRs.

## Frequently Asked Questions

### What is a bare gate repository in no-mistakes?

A bare gate repository is a detached worktree created by the daemon (defined in [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go) as `GateDir`) that contains only `.git` metadata with `HEAD` pointing to the default branch. It serves as a lightweight execution environment for pipelines without requiring a full clone of the source code.

### Why can't the gh CLI infer the PR from a bare gate repository?

The `gh` CLI typically infers the target PR by examining the current Git branch and remote configuration. In a bare gate repository, the working directory is detached and `HEAD` points to `main` (or the default branch), so `gh` would either find no PRs associated with that branch or potentially interact with the wrong PR if multiple exist.

### How does no-mistakes prevent editing the wrong PR?

The `prSelector` function in [`internal/scm/github/github.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/scm/github/github.go) enforces a strict contract: it returns an error if neither a PR number nor URL is provided. This prevents the daemon from invoking `gh pr edit` or similar commands without an explicit target identifier, eliminating the risk of branch-based inference errors.

### Does no-mistakes support PR operations on forks from a bare gate?

Yes. The `headRef` method detects when a fork is in use and formats the `--head` argument as `<forkOwner>:<branch>`. This ensures `gh pr create` correctly associates the new PR with the proper source repository when the daemon is processing contributions from forked repositories.