> For the complete documentation index, see [llms.txt](https://docs.luciq.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-group-bugs.md).

# luciq-group-bugs

Consolidate your Luciq bug list by marking duplicates according to **your own** grouping logic — using the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server.md) to fetch bugs, compile your rule into an explainable key, and write the merges only after you approve a plan.

This is the plugin's first **write** skill. Marking a bug as a duplicate is destructive — the duplicate's occurrences move into the master's group, and its status, priority, and assignee are overwritten by the master's, with no bulk undo on the server. So the skill never writes from a rule alone: it always computes a dry-run plan, shows the exact key that united every group, and calls the write only after you approve.

## Use this skill when

* You ask to consolidate, group, or deduplicate your bugs by your own logic — *"group my bugs by X"*, *"mark these bugs as duplicates"*, *"dedupe / consolidate the bug list"*.
* You want to merge bugs that share the same screen, failed request, tag, title, category, app version, or user attribute, described in plain language.
* You're reorganizing many bugs at once, not root-causing a single one (that's [`luciq-debug`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug.md)) and not pulling a read-only report (that's [`luciq-readout`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-readout.md)).

## What the agent does

* **Deterministic key compilation.** Translates your grouping logic into a `list_bugs` filter set and a per-bug key built only from concrete fields it can name — title, screen, tag, category, app version, user attribute, failed request, or user steps. If the logic can't reduce to concrete fields, it stops and asks you to restate it rather than guessing at a clustering.
* **Scoped, capped fetching.** Pulls a bounded candidate pool with `list_bugs`, paginating up to a cap, and warns rather than silently truncating if your scope is bigger than the cap.
* **Conditional enrichment.** Calls `bug_details` — and, for failed-request or user-step keys, fetches the signed log archive — only when the key you chose actually needs those fields.
* **Auditable dry-run plan.** Groups bugs that share an identical key, drops singleton groups, defaults the master to the oldest bug per group (you can reassign it), and renders every group with the verbatim key that united it.
* **Approval-gated writes.** Marks duplicates via `update_bug` only after you approve the plan — never from the rule alone.
* **Two-mode undo.** Detach only, or detach and restore each bug's pre-merge status and priority. Assignee can't be restored either way, and the skill says so up front.

## Install

The fastest path is the plugin install. Add the marketplace and install:

```
/plugin marketplace add luciqai/agent-skills
/plugin install luciq-skills@luciq.ai
```

Works in **Claude Code** and **Cursor**. The plugin install also wires up the [Luciq MCP server](https://github.com/Instabug/luciq-docs/tree/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step (this skill needs it).

After install, the skill is available as `/luciq-skills:luciq-group-bugs`.

For other agents, install via npm:

```bash
npx luciq-skills install            # project-local
npx luciq-skills install --global   # all projects
```

Or copy [`SKILL.md`](https://github.com/luciqai/agent-skills/blob/main/plugins/luciq-skills/skills/luciq-group-bugs/SKILL.md) from the public repo to `~/.claude/skills/luciq-group-bugs/SKILL.md` (user-global) or `.claude/skills/luciq-group-bugs/SKILL.md` (project-local). The full SKILL.md is reproduced in the expandable below.

<details>

<summary>📋 Click to expand the full SKILL.md</summary>

````markdown
---
name: luciq-group-bugs
description: Use when the customer wants to consolidate, group, or deduplicate their Luciq bugs by marking duplicates according to their own grouping logic. Triggers include "group my bugs by X", "mark these bugs as duplicates", "dedupe / consolidate the bug list", "merge bugs that share the same screen / failed request / tag / title / version". Fetches a scoped candidate set via list_bugs, derives an explainable grouping key per bug (from list_bugs metadata and, only when the key needs it, bug_details logs and steps), renders a dry-run plan, and marks duplicates via update_bug ONLY after the customer approves. This is a WRITE skill — it mutates how bugs are grouped. For root-causing one bug or proposing a code fix use luciq-debug; for a read-only report of bug volume and themes use luciq-readout.
---

# Luciq Bug Grouping

Consolidate a customer's bug list by marking duplicates according to **their own** grouping logic. The mechanism is **filter, then cluster by an explainable key**: compile the customer's logic into (1) a `list_bugs` filter set that bounds the candidate pool and (2) a per-bug **grouping key** built from the exact fields their logic names, then group bugs that share a key and merge each group into one master.

This is the plugin's first **write** skill. `update_bug`'s `mark_as_duplicate` action is destructive (`destructive_hint: true`): the duplicate's occurrences move into the master's group and the duplicate's status, priority, and assignee are **overwritten** by the master's — and are not rolled back when the bug is later unmarked. There is no bulk undo on the server. So the entire spine of this skill is **propose → prove → confirm → write**: the skill computes a dry-run plan, shows the exact key that unites every group, and calls `update_bug` only after the customer approves the plan. It never writes from logic alone.

Every merge in the plan is auditable: the customer can see *why* two bugs grouped (the verbatim key) before a single write happens. A merge the customer didn't approve never happens.

## When NOT to use this skill

- **Root-causing one specific bug** and proposing a code fix → use `luciq-debug`. That skill maps a single occurrence to source and forms a hypothesis. This one reorganizes many bugs.
- **A read-only report** of bug volume, priorities, or themes → use `luciq-readout`. That skill never mutates; it reports. This one mutates grouping and must never be used just to "look at" bugs.
- **General bug triage where Luciq is not the data source.** This skill is grounded in what the Luciq MCP exposes; without it, do not pretend to use it.
- **Logic that cannot reduce to concrete fields** ("group bugs about the same user journey", "cluster by vibe"). This skill is deterministic by design — it does not semantically guess. If the logic is fuzzy, STOP and ask the customer to restate it in terms of concrete fields (title, screen, tag, category, failed request, app version, user attribute). Do not invent a clustering it can't explain.

If the request fits the first three, route there and stop.

## Prerequisites

The Luciq MCP server must be configured and authenticated. If MCP tools are not available, STOP and direct the customer to https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide for setup, or run `luciq-setup`.

The MCP exposes (verbatim names) — this skill uses exactly four:

| Tool | Role in this skill | Read/Write |
| --- | --- | --- |
| `list_applications` | Resolve `(slug, mode)`. Never hard-code a slug. | read |
| `list_bugs` | Fetch the scoped, capped candidate pool using the compiled filter set. | read |
| `bug_details` | Conditional enrichment — called **only** when the grouping key depends on per-bug logs, user steps, or attributes not returned by `list_bugs`. | read |
| `update_bug` | The write. `action: mark_as_duplicate` (+ `original_bug_number`) merges a bug into a master; `action: unmark_as_duplicate` powers undo-last. Post-approval only. | **write** |

**Permission.** `update_bug` requires `bugs.list.modify`. If the authenticated token lacks it, the skill can still fetch candidates and render the dry-run plan, but it CANNOT apply — say so at the plan stage and stop before the write.

**Web-fetch for log-based keys.** Grouping by network failures or user steps additionally needs (a) the `bugs.network_logs.view` / `bugs.user_steps.view` permissions, so `bug_details` includes the archive URLs, and (b) a web-fetch capability to retrieve those signed URLs — the MCP tools return only the URL, not the log contents. Keys built from inline fields (title, category, screen, tag, version, user attribute) need neither. If either is unavailable, the log-based recipes can't run; say so rather than substituting a weaker key.

**`update_bug` is callable inline.** Its input schema is a flat object (no top-level `anyOf` / `oneOf` / `not`), so unlike the `apm_*` tools it is **not** stripped by the AI-94 top-level-combinator issue. Call it through the normal MCP client; no direct-JSON-RPC workaround is needed.

## The cardinal rule

**No `update_bug` write happens until the customer has approved the rendered plan.** Not from the client, not from a direct call, not "the rule is obviously right so I'll just apply it". The plan is the contract: only groups and merges shown in the approved plan get written. This is the inverse of `luciq-readout`'s absolute "never write" — here writes happen, but *only* post-approval.

## Workflow

Track every step. Stop and ask rather than guess — a wrong rule silently merges unrelated bugs and overwrites their status and priority.

```
Grouping Progress:
- [ ] 1. Resolve app + mode (list_applications) — never hard-code a slug
- [ ] 2. Elicit the grouping logic (free-form + suggested starters)
- [ ] 3. Compile logic -> (a) list_bugs filter set, (b) grouping-key recipe
       └─ if the logic is fuzzy / can't reduce to concrete fields: STOP, ask the customer to restate
- [ ] 4. Fetch candidates (list_bugs, scoped + capped; warn/paginate if larger — never silently truncate)
- [ ] 5. Enrich ONLY if the key needs it (bug_details for network-log / user-step / attribute signals)
- [ ] 6. Compute the grouping key per bug -> form groups -> DROP singletons (a group of one is not a duplicate)
- [ ] 7. Pick the master per group = oldest bug (overridable by the customer)
- [ ] 8. Render the dry-run PLAN (groups, master, members, the verbatim key, the skipped list)
- [ ] 9. ⛔ HARD GATE — wait for explicit customer approval
- [ ] 10. Snapshot each member's status/priority, then apply: update_bug action=mark_as_duplicate per member, recording each write
- [ ] 11. Report results; offer undo-last (detach only, OR detach + restore status/priority)
```

### Step 1 — Resolve app and mode

Call `list_applications`. Confirm `mode` with the customer; default to `production`. Each mode (`production`, `beta`, `staging`, `alpha`, `qa`, `development`) is a separate dataset. All candidates and the master in any merge must share one `(slug, mode)` — the skill never merges across apps or modes.

### Step 2 — Elicit the grouping logic

Ask the customer, in plain language, how they want bugs grouped. Offer a few starters so they don't start from a blank page:

- **By title** — bugs whose titles describe the same problem.
- **By screen** — bugs reported from the same `current_view`.
- **By failed request** — bugs whose network logs share the same failed endpoint(s) and status.
- **By tag or category** — bugs sharing a tag set or category.
- **By app version** — same issue scoped to a version.
- **By user attribute** — bugs from users with the same attribute value (e.g. `plan = pro`).

**Dimensions combine — treat the starters as multi-select, not pick-one.** The customer can choose more than one at once (e.g. "same screen **and** same failed request", or "same title **and** same app version"). Each chosen dimension becomes one component of the composite key (see `references/grouping-keys.md`). The customer can also describe their own; the starters are examples, not a closed menu.

### Step 3 — Compile the logic (the deterministic gate)

Translate the logic into two artifacts (recipes are in `references/grouping-keys.md`):

- **Filter set** — `list_bugs` filters that bound the candidate pool (e.g. `app_version`, `tag`, `status_id`, date range).
- **Key recipe** — the per-bug composite key the logic implies (e.g. `current_view` + sorted failed requests).

**If the logic cannot be reduced to concrete fields, STOP here.** Tell the customer what's missing and ask them to restate it concretely. Do not approximate fuzzy logic with a semantic guess — a merge the skill can't explain is a merge it shouldn't make.

### Step 4 — Fetch candidates

Call `list_bugs` with the compiled filter set, scoped to `(slug, mode)`. `list_bugs` returns **at most 50 bugs per call** (`limit` max 50, default 20), so to assemble a candidate pool larger than one page, **paginate with `offset`** (0, 50, 100, …) until you hit the cap or the results run out. Cap the total pulled (default ~300, i.e. ~6 pages). If the scope exceeds the cap, warn the customer, show what is covered, and offer to narrow the filters or raise the cap. **Never silently truncate** — a partial set presented as complete is a silent error.

### Step 5 — Enrich (conditional)

`list_bugs` returns only a thin CSV row per bug: `title`, `categories`, `type`, `duplicate_type`, `email`, `status_id`, `priority_id`, `number`, `reported_at`, `last_activity`, `duplicated_bugs_count`. Any key field beyond those comes from `bug_details`. Call `bug_details` per candidate only when the key recipe needs it, and only for the candidates in scope. Two tiers of enrichment:

- **Inline fields** — `current_view`, `tags`, `app_version`, and `user_attributes` are returned directly in the `bug_details` response (`state.fields.*` / top-level `tags`). Read them straight off the payload.
- **Log-archive signals** — the network-log and user-step signals are **not** inline. `bug_details` returns only a **signed archive URL** under `state.logs.network_log.url` / `state.logs.user_steps.url` (and only if the token has `bugs.network_logs.view` / `bugs.user_steps.view`). To build `failed_requests_sig` or `user_steps_sig` you must, per candidate: (1) read the URL from `bug_details`, (2) **fetch** it (plain HTTPS GET — the URL is pre-signed, no auth header), (3) **decompress + parse** the archive (typically base64 → zlib → JSON), then (4) derive the signature. This needs a web-fetch capability alongside the MCP tools; the Claude Code / Cursor host provides one. If the permission is missing, the URL is absent, or the log is empty, the bug is **skipped** (see "missing fields"), never guessed.

(See the field-source map and the fetch procedure in `references/grouping-keys.md`.)

### Step 6 — Compute keys and form groups

Build the key for each candidate per `references/grouping-keys.md` (normalize, sort sets, strip URL query strings). Group bugs by identical key. **Drop singleton keys** — only keys shared by ≥2 bugs become proposed merges. A bug missing a field the key requires gets **no key** and goes on the skipped list with the reason ("skipped: no network log") — it is never bucketed into a catch-all group.

### Step 7 — Pick the master

For each group, default the master to the **oldest** bug (earliest reported / lowest number), preserving the original report and its history. Show that choice in the plan. The customer can reassign the master for any group before approving.

### Step 8 — Render the dry-run plan

Render the plan per `references/plan-format.md`. For each group show: the master (number + report date), each member that will merge, and the **verbatim key** that united them. Separately list every skipped / not-grouped bug with its reason. Nothing merges that isn't on this plan.

### Step 9 — Hard gate

Present the plan and **wait for explicit approval.** If the token lacks `bugs.list.modify`, say the plan can be shown but not applied, and stop here.

### Step 10 — Apply

Marking a bug as a duplicate **overwrites its status, priority, and assignee** with the master's (`inherit_parent_values` on the server), and unmarking does **not** roll those back. So **before** marking each member, snapshot its current `status_id` and `priority_id` (already present on the candidate's `list_bugs` row) into the session ledger — that snapshot is what makes "restore status/priority" possible in undo. Then, for each member in each approved group, call:

```
update_bug(slug, mode, number: <member>, action: "mark_as_duplicate", original_bug_number: <master>)
```

Apply sequentially and record each result (ok / failure, the bug number, and the pre-merge status/priority snapshot) in the session ledger. Guards:
- **Self-merge guard** — never mark the master as a duplicate of itself.
- **Already-grouped** — if a candidate is already a duplicate/master, surface it in the plan and exclude it from re-merge by default.
- **Continue on failure** — if one `update_bug` fails, record it, keep going, and report all failures at the end. Never silently drop a member.

### Step 11 — Report and offer undo-last

Summarize what merged (groups, masters, member counts) and list any failures. Then offer **undo-last** in two modes, using the session ledger:

- **Detach only** — `update_bug(action: "unmark_as_duplicate")` per member. Restores each bug to standalone but leaves the parent's status/priority on it (unmark does not roll those back).
- **Detach + restore status/priority** — after unmarking each member, re-apply its snapshotted values via `update_bug(status_id:, priority_id:)`.

Tell the customer up front that **assignee cannot be restored** in either mode — `update_bug` has no assignee parameter, so a merge's assignee change is irreversible through this skill. Only offer undo for merges this skill made this session; never unmark pre-existing groups.

## Out of scope

This skill is bugs-only and deliberately does not touch `crash_*`, `apm_*`, `list_app_hangs`, `app_insights`, surveys, or reviews. It does not change a bug's status, priority, tags, or assignee except as the unavoidable side effect of `mark_as_duplicate` (which the customer is told about up front, including that assignee can't be restored). It does not regroup across apps/modes, and it does not move bugs between two existing masters (that's beyond v1 — unmark then re-mark instead).

## Style

- Do not fabricate bug numbers, counts, or keys. Every number in the plan comes from a tool result.
- Do not merge anything not on the approved plan.
- Do not approximate fuzzy logic with a semantic guess — restate-or-stop.
- If `list_bugs` returns nothing for the scope, surface that; do not invent candidates.
- Always show the verbatim key for every group so each merge is auditable.

## Red Flags — STOP and surface to the customer

If you catch yourself thinking any of these, you are about to ship a wrong or unaudited merge. STOP:

- "The logic is a bit vague but I can probably cluster these by meaning." No. Restate-or-stop — this skill is deterministic.
- "I'll apply the merges without showing the plan because the rule is obviously right." Never. The plan + approval gate is the contract.
- "The scope is bigger than the cap but I'll just group what I pulled." That's a silent truncation. Warn and offer to narrow or proceed explicitly.
- "This bug has no network log but it probably belongs with the others." No — missing field means skipped, never catch-all.
- "I'll quietly skip the member that failed to merge." Surface every failure in the end report.
- "I'll mark the master as a duplicate too." Self-merge guard — the master is never a duplicate of itself.
- "The token can't write, but I'll try the merges anyway." Stop at the plan stage and tell the customer the permission is missing.
- "Undo will put everything back the way it was." It won't — unmark restores neither status/priority (only the snapshot + re-apply does) nor assignee (not at all). Say what undo can and can't restore.
- "I'll group by screen / tag / version straight from the list_bugs rows." Those fields aren't in the `list_bugs` response — pull them from `bug_details` first.

The pattern: every shortcut trades "looks done" for "actually correct and reversible". A destructive write the customer didn't approve is the one failure this skill exists to prevent.
````

</details>

## Prerequisites

* The [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server.md), configured and authenticated. The [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup.md) skill wires this for first-time projects.
* The `bugs.list.modify` permission on the authenticated token, to apply merges. Without it, the skill can still fetch candidates and show the dry-run plan — it just can't write.
* For grouping by failed network requests or user steps: the `bugs.network_logs.view` / `bugs.user_steps.view` permissions (so `bug_details` returns the log archive URLs) plus a web-fetch capability in your agent host to retrieve those signed URLs. Grouping by title, category, screen, tag, version, or user attribute needs neither.

{% hint style="info" %}
If the MCP server isn't connected, the skill stops and points you to [Setup by IDE](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide.md) before continuing.
{% endhint %}

{% hint style="warning" %}
Marking a bug as a duplicate overwrites its status, priority, and assignee with the master's, and there's no bulk undo on the server. The skill never applies a merge until you've approved the rendered plan.
{% endhint %}

## How the agent groups bugs

The skill runs a fixed loop: compile your logic into a deterministic key, fetch and key every candidate, render a plan, and write only after you approve it.

### Step 1. Resolve app and mode

The agent calls `list_applications` and confirms the mode with you (defaulting to `production`). Every candidate and the eventual master must share one app and mode — the skill never merges across apps or modes.

### Step 2. Elicit your grouping logic

The agent asks how you want bugs grouped, offering starters: by title, screen, failed request, tag or category, app version, or user attribute. You can combine dimensions — "same screen **and** same failed request" — or describe your own logic in your own words.

### Step 3. Compile the logic

The agent translates your logic into a `list_bugs` filter set that bounds the candidate pool, and a per-bug key recipe built from the fields you named. If your logic can't reduce to concrete fields — "group by vibe", "same user journey" — the agent stops and asks you to restate it. It won't semantically guess its way into an irreversible merge.

### Step 4. Fetch candidates

The agent pulls a scoped, capped set of bugs with `list_bugs`, paginating as needed. If your scope is larger than the cap (around 300 bugs), it warns you and offers to narrow the filters or raise the cap rather than silently working with a partial set.

### Step 5. Enrich only when needed

`list_bugs` returns a thin row per bug. If your key needs a field beyond that — screen, tags, app version, user attributes, or a signature built from network logs or user steps — the agent calls `bug_details` for the candidates in scope. Log-based signals (failed requests, user steps) require fetching and parsing a signed archive URL, which needs a web-fetch capability alongside the MCP tools.

### Step 6. Compute keys and form groups

The agent normalizes and builds a key for each candidate — lowercasing titles, sorting tag sets, stripping query strings from URLs — then groups bugs that share an identical key. Groups of one are dropped; a bug missing a field its key needs is skipped and listed with a reason, never folded into a catch-all group.

### Step 7. Pick the master

For each group, the agent defaults the master to the oldest bug, preserving the original report and its history. You can reassign the master for any group before approving.

### Step 8. Render the dry-run plan

The agent shows every proposed group: the master, each member that would merge into it, and the verbatim key that united them, plus a list of skipped bugs and why. Nothing has been written yet.

### Step 9. Wait for your approval

This is a hard gate. If your token lacks the `bugs.list.modify` permission, the agent says the plan can only be shown, not applied, and stops here.

### Step 10. Apply

Once you approve, the agent snapshots each member's current status and priority (so undo can restore them later), then marks each member as a duplicate of its group's master, one at a time. It never merges the master into itself, skips bugs already grouped, and if one merge fails it keeps going and reports every failure at the end.

### Step 11. Report and offer undo

The agent summarizes what merged and any failures, then offers to undo the merges made this session — either detaching the bugs only, or detaching and restoring each bug's pre-merge status and priority. Assignee can't be restored either way, and the agent says so up front.

## Out of scope

This skill only touches bugs. It doesn't group or act on crashes, hangs, APM data, surveys, or reviews, and it doesn't change a bug's status, priority, tags, or assignee except as the unavoidable side effect of marking it a duplicate. It doesn't regroup bugs across apps or modes, and it doesn't move a bug between two existing masters — unmark it and re-mark it under the new master instead.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-group-bugs.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
