# GitLab Issue Auto-fix

Scan **self-hosted GitLab issues**, resolve base branch from issue metadata,
create a per-issue **worktree**, run Claude on the issue (with Premium epic
context + downloaded image attachments), and open an MR — all automated.

Mirrors the existing GitHub flow (`issue-fix-and-review`) but built around
the `glab` CLI and adapted for Premium epic links + image uploads.

## Setup

### 1. Install + auth glab

```bash
brew install glab
glab auth login --hostname <your-gitlab.example.com>
```

GitLab host is auto-detected per project from the git remote, so you only
need to authenticate once per host. Self-hosted is fully supported.

### 2. Bind the pipeline to your project

Project tab → **Pipelines** → `+ Add` → `gitlab-issue-fix-and-review`.

In the binding form set:
- **Labels** (optional) — only process issues matching these labels
- **Assignee filter** — `me` (default), specific username, or empty
- **Base branch rule** — how to derive target branch from issue:
  - `milestone` (default): `release/<milestone-title-slug>`
  - `label:<prefix>`: pick first label starting with `<prefix>`, strip it
    (e.g. `label:branch:` → label `branch:hotfix/4.5` → base `hotfix/4.5`)
  - `desc`: regex match `^Base: <branch>` in issue description
  - `default`: just `main` (or repo default)
- **Base branch override** — string; bypasses the rule entirely
- **MR title template** — vars: `{issue_id}`, `{issue_title}`.
  Default: `Fix #{issue_id}: {issue_title}`
- **MR body template** — vars: `{issue_id}`, `{issue_url}`, `{summary}`.
  Default closes the issue and inlines Claude's summary.
- **Schedule** — interval in minutes (`0` = manual only)

### 3. Triggering

Three ways:

| Mode | How |
|---|---|
| Manual single-issue | `Run` button in Pipelines tab → enter `issue_id` |
| Manual scan-all | `Scan` button → fetches assigned issues, fans out, dedups |
| Scheduled | Set interval > 0 in config; runs on Forge boot + every N min |

## Pipeline nodes

```
resolve              → parse host + project path from origin
fetch-issue          → glab issue view → JSON (title, description, labels, milestone, epic.iid)
fetch-extras         → glab api → comments JSON + epic JSON (Premium)
resolve-base-branch  → apply rule from config
worktree-setup       → git worktree add .forge/worktrees/issue-<id>
download-attachments → curl GitLab /uploads/.../filename into .attachments/
fix-code             → claude in worktree, prompt = issue + comments + epic + images
push-and-mr          → git push + glab mr create
notify-issue         → glab issue note → "🤖 Forge opened MR: <url>"
```

Pipeline output: an MR URL on the issue + linked from comments.

## Storage

Per-project config + dedup tracking lives in `workflow.db`:

- `issue_autofix_gitlab_config` — projectPath → config
- `issue_autofix_gitlab_processed` — projectPath × issueIid → status + MR

Already-processed issues are skipped in scan mode. Use `reset` to re-trigger,
or `retry` with extra context to nudge Claude in a different direction.

## Image attachments

GitLab uploads (`/uploads/<32-hex>/<file>`) in issue descriptions are
auto-downloaded to `<worktree>/.attachments/`. Claude can `Read` those
files. PNG/JPG/PDF supported via Claude Code's image reader.

The download uses the same PAT that glab already has authenticated (so
no extra credential setup). Falls back to anonymous HTTPS if the upload
happens to be public.

## Premium: Epic context

If the issue links to an epic (`issue.epic.iid` present), the pipeline:
1. Fetches the epic via `glab api groups/<id>/epics/<iid>`
2. Includes epic description + acceptance criteria in Claude's prompt
3. Claude can read the full JSON dump file path from `fetch-extras`

Free-tier instances just skip this step — no error.

## Authentication notes

`glab auth login` stores a token at `~/.config/glab-cli/config.yml`. Forge
inherits the env; the spawned `glab` invocations from the pipeline use the
same credentials. **No env vars needed** unless you want to override per-
session.

For headless / CI mode use `GITLAB_TOKEN` env var — `glab` picks it up too.

## Troubleshooting

| Symptom | Cause + fix |
|---|---|
| `ERROR: glab CLI not installed` | `brew install glab` (or apt) and `glab auth login` |
| `glab CLI not authenticated` | Token expired or wrong host. `glab auth login --hostname <host>` |
| MR push fails with "main branch protected" | Configure GitLab to allow force-push to `fix/*` branches OR change pipeline to drop `--force-with-lease` |
| Base branch resolves wrong | Switch `baseBranchRule` to `desc` and add `Base: <branch>` to the issue description as a forcing override |
| `403 on attachment fetch` | Some self-hosted instances require token even for public-link uploads. Make sure glab is authenticated to the same host that issued the upload URL |
