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.mdfor 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:
- Check
CIenv var: runecho $CI. If the value istrue, setnon_interactive = true. - Check skill arguments: if the skill was invoked with
--non-interactive, setnon_interactive = true. - 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
Infer
project_pathfrom the git remote:git remote get-url originParse the GitLab project path (
namespace/projectorgroup/subgroup/project) from the URL. Handle both SSH (git@gitlab.com:namespace/project.git) and HTTPS (https://gitlab.com/namespace/project.git) formats. Strip.gitsuffix if present.- Interactive: If the remote is unavailable or ambiguous, ask the user.
- Non-interactive: If
--projectwas provided, use it. Otherwise abort with error: "Could not infer project path from git remote. Use--project namespace/project."
Gather
time_period:- Interactive: Ask the user how far back to look (default: 7 days, max: 90 days).
- Non-interactive: Use
--daysvalue if provided, otherwise default to 7 days silently.
Compute the cutoff date as
today - time_periodinYYYY-MM-DDformat.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)."
- Interactive: Display and BLOCK until the user confirms:
Phase 1: Fetch MRs
Use mcp__gitlab__search with:
scope: "merge_requests"state: "merged"(run a second search withstate: "opened"to also include open MRs)project_id: project_pathsearch: "*"(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_notesfor 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
# 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
Display a summary to the user:
- Number of recurring themes found
- Number of individual findings
- List of suggested playbook actions
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?"On confirm: Invoke the
packmind-update-playbookskill 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: "
On decline: Inform the user the report is available at
.claude/tmp/gitlab-mr-review-findings.mdfor manual review.
Non-interactive mode
Display a summary (same as interactive: themes count, findings count, suggested actions).
Log the report output path:
"Report written to
.claude/reports/gitlab-mr-review-findings-YYYY-MM-DD.md."Invoke
packmind-update-playbookwith 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: "
The
packmind-update-playbookskill 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