# Gitlab Mr Review

> Manually triggered skill that harvests merge request review comments from a GitLab project using MCP tools, classifies them for playbook relevance, and auto-invokes packmind-update-playbook with structured findings. Use when wanting to mine merged MR discussions for recurring patterns, missing conventions, or stale rules that should be captured in the Packmind playbook. Supports non-interactive mode via `CI=true` or `--non-interactive` for scheduled runs.

- Skill: `packmindhub/gitlab-mr-review` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add packmindhub/gitlab-mr-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/packmindhub/gitlab-mr-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: PackmindHub (https://skillmd.com/u/packmindhub)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/packmindhub/gitlab-mr-review

---


# GitLab MR Review Mining

Harvest discussion notes from merged GitLab merge requests, classify them for playbook relevance, and feed actionable findings into the `packmind-update-playbook` workflow.

**Workflow: Phase 0 → 1 → 2 → 3 → 4 → 5. Follow every phase in order.**

> **Prerequisite**: The GitLab MCP server must be available. Load `references/gitlab-mcp-tools.md` for tool signatures and parameters.

## Phase 0: Collect Parameters

Gather the required inputs before fetching any data.

### Execution Mode Detection

Determine the execution mode **before** collecting any parameters:

1. **Check `CI` env var**: run `echo $CI`. If the value is `true`, set `non_interactive = true`.
2. **Check skill arguments**: if the skill was invoked with `--non-interactive`, set `non_interactive = true`.
3. **Parse optional arguments**:
   - `--days N` — override the default time period (default: 7 days)
   - `--project namespace/project` — override git remote inference

If neither condition is met, `non_interactive = false` (interactive mode — original behavior).

### Collect Parameters

1. **Infer `project_path`** from the git remote:
   ```bash
   git remote get-url origin
   ```
   Parse the GitLab project path (`namespace/project` or `group/subgroup/project`) from the URL. Handle both SSH (`git@gitlab.com:namespace/project.git`) and HTTPS (`https://gitlab.com/namespace/project.git`) formats. Strip `.git` suffix if present.
   - **Interactive**: If the remote is unavailable or ambiguous, ask the user.
   - **Non-interactive**: If `--project` was provided, use it. Otherwise abort with error: "Could not infer project path from git remote. Use `--project namespace/project`."

2. **Gather `time_period`**:
   - **Interactive**: Ask the user how far back to look (default: 7 days, max: 90 days).
   - **Non-interactive**: Use `--days` value if provided, otherwise default to 7 days silently.

   Compute the cutoff date as `today - time_period` in `YYYY-MM-DD` format.

3. **Confirm** before proceeding:
   - **Interactive**: Display and **BLOCK** until the user confirms:
     > "Mining MR review comments for **project_path** merged in the last **N days** (since YYYY-MM-DD). Proceed?"
   - **Non-interactive**: Log the parameters and proceed automatically:
     > "Non-interactive mode: mining MR review comments for **project_path** merged in the last **N days** (since YYYY-MM-DD)."

## Phase 1: Fetch MRs

Use `mcp__gitlab__search` with:
- `scope: "merge_requests"`
- `state: "merged"` (run a second search with `state: "opened"` to also include open MRs)
- `project_id: project_path`
- `search: "*"` (**must use `"*"`, not `""` — an empty string causes a 500 error**)

Paginate using `page` and `per_page` (max 100 per page). Collect MR IIDs, titles, and web URLs.

For merged MRs, call `mcp__gitlab__get_merge_request` to get full details including `merged_at`. Filter out MRs where `merged_at` is before the cutoff date. For open MRs, use `created_at` or `updated_at` from the search results for the time filter.

- If no MRs remain within the time window, inform the user and stop.
- Display progress: "Found **N** MRs to analyze (M merged, K open)."

## Phase 2: Fetch Review Comments & Filter

Use `mcp__gitlab__search` with `scope: "notes"` and `project_id: project_path` to fetch all discussion notes. Then correlate notes to MRs using `noteable_type: "MergeRequest"` and `noteable_iid`.

> **Important**: Do NOT use `mcp__gitlab__get_workitem_notes` for MR notes — it only supports work item URLs (`/-/work_items/<iid>`), not merge request URLs. MR IIDs are not work item IIDs.

### Filtering

Apply these filters to discard noise:

**System note filtering** — discard notes where the `system` flag is `true`. These are auto-generated by GitLab for:
- Label changes
- Assignee changes
- Milestone changes
- Pipeline status updates
- Branch operations (merge, rebase)
- Reviewer assignments

**Low-value filtering** — discard comments that:
- Are shorter than 15 characters
- Match low-value patterns: consists only of "LGTM", "+1", thumbs-up emoji variants, "nit", "thanks", ":thumbsup:", ":+1:"

**Thread deduplication** — when a note has a `discussion_id`, group notes by discussion. Keep only the root note unless a reply adds substantive new feedback (>30 chars and not a simple acknowledgment).

Display progress: "Collected **N** review comments from **M** MRs (after filtering)."

If no comments remain after filtering, inform the user and stop.

## Phase 3: Classify Comments

Categorize each remaining comment by playbook relevance:

### High relevance (keep)
- **convention** — Enforces or suggests a naming, structure, or organizational pattern
- **best-practice** — Recommends a better approach for correctness, performance, or maintainability
- **recurring-feedback** — Same feedback appears across 2+ different MRs
- **bug-prevention** — Points out a pattern that could lead to bugs

### Low relevance (discard)
- **style** — Pure formatting preference
- **clarification** — Asks a question without suggesting a change
- **nitpick** — Minor, one-off preference

Retain only High-relevance comments.

### Recurring Theme Detection

Group retained comments by semantic similarity. A **recurring theme** is 2+ comments across different MRs that address the same underlying pattern. For each theme:
- Assign a short descriptive label
- List all contributing comments
- Note the occurrence count

### Codebase Relevance Gate

After classification, apply a strict codebase relevance filter to all high-relevance findings. GitLab MR notes can include CI/CD pipeline chatter, process comments, and DevOps discussions that aren't relevant to code.

**Litmus test**: "Would an AI coding agent need to know this when writing, reviewing, or shipping code in this repository?" — if no, discard the signal.

| Signal | Verdict | Why |
|--------|---------|-----|
| "All API endpoints must validate input" | KEEP | Coding convention |
| "Use factory pattern for test data" | KEEP | Best practice |
| "Always add DB migration for schema changes" | KEEP | Dev workflow |
| "Run linter before pushing" | KEEP | Affects how agent ships code |
| "Pipeline passed on retry" | DISCARD | CI/CD operational noise |
| "Assigned to @user for review" | DISCARD | Process management |
| "Moved to staging environment" | DISCARD | Deployment operations |
| "Updated the .gitlab-ci.yml timeout" | DISCARD | CI/CD configuration chatter |

**Edge cases**: When a signal straddles both worlds (e.g., "update CI to run new test suite"), apply the litmus test strictly — does it change how an AI agent writes or ships code? If not, discard.

Display progress: "Retained **N** codebase-relevant findings (discarded **M** non-relevant signals)."

## Phase 4: Build Findings Report

Choose the output path based on execution mode:
- **Interactive**: `.claude/tmp/gitlab-mr-review-findings.md`
- **Non-interactive**: `.claude/reports/gitlab-mr-review-findings-YYYY-MM-DD.md` (using today's date, timestamped for CI artifact upload)

Create the target directory if needed.

### Report Structure

```markdown
# GitLab MR Review Findings Report

**Project**: project_path
**Period**: YYYY-MM-DD to YYYY-MM-DD
**MRs analyzed**: N
**Comments collected**: N (after filtering)
**Actionable findings**: N
**Codebase-relevant findings**: N (of M high-relevance signals)

---

## Recurring Themes

### Theme: <label>
**Occurrences**: N across N MRs

| MR | File | Author | Comment |
|----|------|--------|---------|
| [!123](url) | path/to/file.ts | @author | Summary of comment |
| [!456](url) | path/to/other.ts | @author | Summary of comment |

**Suggested playbook action**: <Create standard | Update standard X | Create skill | ...>
**Rationale**: <Why this theme warrants a playbook change>

---

## Individual Findings

### Finding: <short description>
- **MR**: [!123](url)
- **File**: path/to/file.ts (if available from diff context)
- **Author**: @author
- **Comment**: Full comment text
- **Category**: convention | best-practice | bug-prevention
- **Suggested playbook action**: <action>

---

## Discarded Signals (non-relevant)

> Optional section for transparency. Lists signals that passed high-relevance classification but were filtered by the Codebase Relevance Gate.

| Signal summary | Reason discarded |
|---------------|-----------------|
| <one-line summary> | <CI/CD noise / process management / deployment ops / ...> |
```

### URL Requirements

**Every MR reference in the report MUST include the full, navigable GitLab URL** (e.g., `https://gitlab.com/namespace/project/-/merge_requests/123`). This is critical because `packmind-update-playbook` extracts source URLs from the findings report for its `packmind-cli diff --submit -m` source attribution. The expected attribution format is:

```
<topic>: <summary> (source: MR !N https://gitlab.com/namespace/project/-/merge_requests/N, MR !M https://gitlab.com/namespace/project/-/merge_requests/M)
```

Use the `web_url` field returned by `mcp__gitlab__get_merge_request` for each MR. Never use placeholder `(url)` — always substitute the real URL.

## Phase 5: Present and Hand Off

### Interactive mode

1. Display a summary to the user:
   - Number of recurring themes found
   - Number of individual findings
   - List of suggested playbook actions

2. Ask the user:
   > "Found **N recurring themes** and **M individual findings**. Review the full report at `.claude/tmp/gitlab-mr-review-findings.md`. Proceed to update the playbook with these findings?"

3. **On confirm**: Invoke the `packmind-update-playbook` skill with the full report content as the intent. This maps to **Case B (explicit intent)** of that skill's Phase 0. Frame the intent as:
   > "Update the Packmind playbook based on the following GitLab MR review findings report: <full report content>"

4. **On decline**: Inform the user the report is available at `.claude/tmp/gitlab-mr-review-findings.md` for manual review.

### Non-interactive mode

1. Display a summary (same as interactive: themes count, findings count, suggested actions).
2. Log the report output path:
   > "Report written to `.claude/reports/gitlab-mr-review-findings-YYYY-MM-DD.md`."
3. **Invoke `packmind-update-playbook`** with the full report content as the intent, passing along non-interactive mode. Frame the intent as:
   > "Update the Packmind playbook based on the following GitLab MR review findings report: <full report content>"

   The `packmind-update-playbook` skill will auto-approve all changes and delegate creation of new artifacts to the corresponding creation skills (`packmind-create-standard`, `packmind-create-command`, `packmind-create-skill`), all running in non-interactive mode.

## Resources

### references/
- `gitlab-mcp-tools.md` — GitLab MCP tool signatures and parameters used by this skill

