# Welcome to Luciq's Docs

Start here for the Luciq documentation. Jump straight into SDK setup and product guides for iOS, Android, React Native, and Flutter.

{% columns %}
{% column valign="middle" %}

## What are you <mark style="color:$primary;">building</mark> today?

Ask anything about integrating Luciq's SDKs and agents. From a one-line install to deep platform tuning.
{% endcolumn %}

{% column %}

<div align="right"><figure><img src="/files/lDWbQj0id918yVd3RjxC" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}
{% endcolumns %}

<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask our docs AI</button>

***

{% hint style="warning" %}
As part of Instabug’s rebrand to Luciq, the Instabug APIs are being deprecated. For a limited time, the Instabug API documentation will remain available [here](https://instabug-docs.luciq.ai/).
{% endhint %}

### Jump right in

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>iOS</strong></td><td><a href="/files/IzfxgtTwfQmb91bbaTIu">/files/IzfxgtTwfQmb91bbaTIu</a></td><td>Setup and Product Guides</td><td><a href="https://docs.luciq.ai/ios">https://docs.luciq.ai/ios</a></td></tr><tr><td><strong>Android</strong></td><td><a href="/files/aYaC8KxPwj4rh5LsKjFA">/files/aYaC8KxPwj4rh5LsKjFA</a></td><td></td><td><a href="https://docs.luciq.ai/android">https://docs.luciq.ai/android</a></td></tr><tr><td><strong>React Native</strong></td><td><a href="/files/3ubU12rtyQYsYwWmZ0iU">/files/3ubU12rtyQYsYwWmZ0iU</a></td><td></td><td><a href="https://docs.luciq.ai/react-native">https://docs.luciq.ai/react-native</a></td></tr><tr><td><strong>Flutter</strong></td><td><a href="/files/HJlFcwrKyU1a7pOlbEHt">/files/HJlFcwrKyU1a7pOlbEHt</a></td><td></td><td><a href="https://docs.luciq.ai/flutter">https://docs.luciq.ai/flutter</a></td></tr><tr><td>API Reference</td><td></td><td></td><td><a href="https://docs.luciq.ai/references">https://docs.luciq.ai/references</a></td></tr></tbody></table>


# Introduction

An introduction to the Luciq docs, covering Bug Reporting, Crash Reporting, and App Performance Monitoring, and how to find any API method.

Welcome to the Luciq Docs, where you can find all the resources you need to start debugging faster and building better apps. Luciq includes capabilities for Bug Reporting, Crash Reporting, and App Performance Monitoring.

Navigate the docs using the sidebar on the left-hand side to jump to any functionality or specific API that you're looking for. All the API methods that can be used in the SDK can be found in these sections with explanations about what they do and how to use them.

You can also find information in these docs about the data found in [your dashboard](https://demo-dashboard.luciq.ai/applications/ios-demo-app/production/app-health?filters=%7B%22date_ms%22:%7B%22gte%22:1758240000000,%22lte%22:1758844799999,%22shortcutLabel%22:%22Last%207%20days%22%7D,%22app_version%22:%22top_releases%22%7D).

**Supported Platforms**\
Luciq supports many different platforms. To get started with any of them, just click on one of the relevant links below:

1. [**iOS**](/ios/setup-luciq-for-ios)
2. [**Android**](/android/set-up-luciq-for-android)
3. [**React-Native**](/react-native/setup-luciq-for-react-native)
4. [**Flutter**](/flutter/setup-luciq-for-flutter)


# Migrate Instabug SDK To Luciq SDK

Move your app from the Instabug SDK to the Luciq SDK. What the rebrand changes, what stays the same, and the steps for each platform.

Welcome to the next chapter of our journey together. As you may know, Instabug is evolving. We are thrilled to introduce Luciq, our new identity as the Agentic Observability Platform for Mobile.

This is more than just a name change; it's a strategic evolution of our entire platform, designed to empower you to spend less time firefighting, and more time innovating the things that matter. To deliver on this new vision, our SDK has been completely rebranded. This guide will walk you through the simple, one-time process of migrating your application from the legacy Instabug SDK to the new Luciq SDK.

### Why Migrate to the Luciq SDK?

Upgrading ensures you stay on the edge of mobile observability and continue to receive the full value of our platform. By migrating, you will:

* **Stay Up-to-Date:** Starting today, the legacy Instabug SDK will no longer receive new features or product updates. Critical updates and hotfixes will continue but only until January 2026 - after which that ends. Beyond then, it will continue to work normally as is, but migrating ensures you are on the actively maintained and evolving platform.
* **Unlock New AI-Powered Features:** All future innovation, including our new assistive AI Agents and performance improvements, will be released exclusively on the Luciq SDK.
* **Align with Our Future Vision:** The Luciq SDK is the new foundation of our platform. Upgrading ensures your application is ready for the future of agentic observability and the powerful new workflows we're building.

***

### What to Expect During the Migration?

We have invested heavily in making this a low-effort, predictable process for your development team.

This is a one-time breaking change that involves updating your application's dependency from Instabug to Luciq, and renaming all API calls. For the vast majority of projects, the process is straightforward and can be completed quickly.

**The High-Level Steps:**

* **Update Your Dependency:** Change your project's configuration (e.g., Podfile, build.gradle) to point to the new Luciq SDK package.
* **Run the Automated Migration Script:** We've built a powerful script for each platform that handles roughly 90% of the required code changes automatically.
* **Review & Test:** Follow our platform-specific guide (guides linked below) to handle any edge cases, review the automated changes, and test your application.

For a medium-complexity project, we estimate the entire process, including running the script and testing, to be around **2-3 hours.**

***

### Your Platform-Specific Migration Path

To get started, please select the detailed, step-by-step guide for your specific platform.

**Safety First:** This is a one-time migration that will modify your source code. Before you begin, it is **critical** that you commit all your work to a version control system like Git. This ensures you have a safe backup and can easily review all changes before finalizing them.

Each platform guide begins with a preflight checklist, instructions for using the automated script, and full mapping tables for all API changes - should you proceed with a manual migration or need to fallback to it for overtly complex projects.

At a glance, here are the key areas that will be updated for each platform:

| **Platform**     | **Key Files & Areas Affected**                                     |
| ---------------- | ------------------------------------------------------------------ |
| **iOS**          | `Podfile, .swift/.m files, info.plist`                             |
| **Android**      | `build.gradle, .kt/.java files, AndroidManifest.xml`               |
| **React Native** | `package.json, .js/.ts files, plus all native iOS & Android files` |
| **Flutter**      | `pubspec.yaml, .dart files, plus all native iOS & Android files`   |

Below, you can find the detailed guide for each of our supported platforms:

1. [**iOS Migration Guide**](/ios/ios-luciq-migration)**:** Migrate your CocoaPods/SPM dependency and update your code from `Instabug.*` and the `IBG*` prefix to `Luciq.*` and the new `LCQ*` prefix.
2. [**Android Migration Guide**](/android/android-luciq-migration)**:** Migrate your Maven dependency and Gradle plugin, and update your code from `Instabug.*` and the `IBG*` prefix to `Luciq.*`.
3. [**React Native Migration Guide**](/react-native/react-native-luciq-migration)**:** Migrate your npm package from `instabug-reactnative` to `@luciq/react-native` and update your JavaScript and native code.
4. [**Flutter Migration Guide**](/flutter/flutter-luciq-migration)**:** Migrate your pub.dev package from `instabug_flutter` to `luciq_flutter` and update your Dart code.

<br>


# Integrate Luciq SDK Using AI Coding Agents

Instrument the Luciq SDK with AI coding agents such as Cursor or Claude. A guided workflow installs the SDK and wires your configuration.

### Introduction

We now offer customers the ability to instrument the Luciq SDK using their favorite AI coding agents like Cursor or Claude. This is done through a guided, agent-driven workflow that fetches & integrates the latest Luciq SDK end-to-end, then wires mandatory & optional configurations to your application, all while asking for confirmation before altering your code.

### How It Works?

The workflow consists of `.md` instruction files for each platform, they can be found at:

* [Android AI Integration Guide](/android/set-up-luciq-for-android/integrate-luciq-on-android/luciq-ai-android-guide)
* [iOS AI Integration Guide](/ios/setup-luciq-for-ios/integrate-luciq-on-ios/luciq-ai-ios-guide)

These `.md` files can then be used to prompt the coding agent to explicitly follow them, for example, tell your agent something like:

integrate luciq ios sdk following the instructions at \[link to ios guide]

{% hint style="warning" %}
Although the more recommended and less hallucination-prone approach is to copy the `.md` contents of the two guides linked above into your agent conversation directly for it to follow.
{% endhint %}

Then ride the flow, answer the decision prompts, and verify your integration at the end.

### What It Does?

{% stepper %}
{% step %}
**Core workflow**

* Finds your app token
  * Reads from Luciq MCP when available, or
  * Prompts you to paste the token (with guidance on where to find it in the dashboard).
* Detects how your app is built
  * iOS: detects SPM / CocoaPods / Carthage / manual.
  * Android: detects Gradle.
  * If multiple/none, it asks you which one to use.
* Pins and installs the SDK
  * Fetches the latest released version from GitHub Releases.
  * Adds the Luciq dependency using that exact version.
* Initializes the SDK with your preferred invocation
  * Lets you choose from: shake, screenshot, floatingButton, or manual only.
  * Defaults to shake + screenshot if no selection is made.
    {% endstep %}

{% step %}
**Optional flow (user prompted)**

* Initializes the SDK with your preferred invocation
  * Lets you choose from: shake, screenshot, floatingButton, or manual only.
  * Defaults to shake + screenshot if no selection is made.
* Network logging & redaction
  * Turn automatic capture on/off.
  * Configure masking rules for headers (e.g. Authorization, Cookies) and body fields (e.g. password, token).
  * Installs the right interceptor/handler so those fields are redacted.
* Screenshot masking for ReproSteps
  * Configure which UI elements to blur in screenshots:
    * Text inputs
    * Labels/buttons
    * Images/media
    * Or a combination of the above.
* User identification
  * Helps the agent find login and logout flows.
  * Adds identifyUser(id, email, name) for these events so reports are tied back to your users.
* Wrap-up & validation
  * Runs the appropriate build command.
  * Prints a config summary and prompts you to:
    * Trigger Luciq in the app (shake/screenshot/floating button).
    * Submit a test report.
    * Verify it in the Luciq dashboard.
      {% endstep %}
      {% endstepper %}

Example on optional feature prompting:

![](/files/88ca84d3bd4ceccd50124f3e9b8c74268de931d9)

### Prerequisites

* You have a Luciq project & app token.
* You’re using a supported package manager:
  * Android: Gradle
  * iOS: SPM, CocoaPods, Carthage, or XCFramework manual integration
* (Optional but recommended): [Luciq MCP Server](broken://spaces/AM8wNfllcup3GnWJ1WtW/pages/T1gGOduIeI8YmOww0F1A) installed & configured (also helps the agent auto-discover app tokens).
* You’re running an AI coding agent that can read project files and .md instructions (e.g. Cursor, Claude Code).

### What's next

Once Luciq is integrated, the same AI coding agent can also debug crashes, hangs, and user-reported bugs end to end, pulling the production context from your Luciq workspace and proposing fixes grounded in real evidence.

That workflow is packaged as an [Agent Skill](/product-guides-and-integrations/product-guides/ai-features/agent-skills), a small set of curated instruction files that any agent supporting the [Agent Skills](https://agentskills.io) format can follow.

* [`luciq-debug`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug). Investigate any production signal (crash, hang, bug) and propose a fix.
* [`luciq-migrate`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-migrate). Move from the legacy Instabug SDK to Luciq, or upgrade between SDK versions.
* See the [Agent Skills index](/product-guides-and-integrations/product-guides/ai-features/agent-skills) for the full list.


# Product Guides

Guides for every Luciq product area, including crash reporting, bug reporting, APM, session replay, surveys, the Inbox, and AI features.


# Getting Started with Luciq

Your first steps in the Luciq dashboard, including the health metrics worth watching and how Frustration-Free Sessions summarizes quality.


# App Health Dashboard

Use the Luciq App Health Dashboard to monitor your mobile app's stability and performance. Track key metrics, spot trends, and prioritize issues at a glance.

### Overview

The App Health Dashboard provides a comprehensive overview of your application's performance, stability, and overall quality. With five key sections, this dashboard is designed to give you detailed insights into the state of your app, helping you to monitor and improve its quality and user satisfaction.

#### Benefits

* **Comprehensive Monitoring**: Get a holistic view of your app’s performance and user experience.
* **Performance Trends**: Track how your app's performance changes over time.
* **Detailed Insights**: Identify specific areas that need improvement, from crash rates to UI responsiveness.
* **User Feedback**: Understand user satisfaction through ratings and reviews.
* **Release Analysis**: Evaluate the impact of new releases on app performance and user experience.

#### Usage Tips

* Regularly monitor the **Frustration-Free Sessions** and **Crash-Free Sessions** to ensure high user satisfaction.
* Use the **App Insights** section to quickly identify and address performance issues by navigating to the worst performing metrics
* Zoom in on **Frustration-Free Sessions** drops to find the worst performing metics contributing to the drop in **App Insights**.
* Pay attention to **App Ratings** to gather qualitative feedback from users.
* Analyze the **Top Releases** section to understand the effects of recent updates and optimize future releases.

### Key Sections

#### Overall Frustration-Free Sessions and Session Breakdown

1. **Frustration-Free Sessions**: The Frustration-Free Sessions metric is a numerical measure of user satisfaction with your app's performance.
2. **Session Breakdown**: This section categorizes user sessions into four types:
   1. **Crashing Sessions**: Sessions that ended due to app crashes.
   2. **Frustrating Sessions**: Sessions that were significantly impacted by performance issues.
   3. **Tolerable Sessions**: Sessions where users experienced minor performance issues.
   4. **Satisfying Sessions**: Sessions where users had a smooth experience.

<figure><img src="https://files.readme.io/74453cb21631f1daf164a6798bbc0369cb8f1469363b51579d483dae9eb27ef6-product-guides-app-health-1.png" alt=""><figcaption></figcaption></figure>

#### Frustration-Free Sessions and Crash-Free Sessions Over Time

This graph shows the trend of your app's frustration-free sessions and crash-free sessions over a selected period. The provided graph shows fluctuations in frustration-free sessions over a 28-day period, helping in identifying patterns and periods of performance degradation or improvement. You can click and drag on the graph to zoom in and the whole page will reflect the data of the new time range.

<figure><img src="https://files.readme.io/a6d3466df427e6ed52ad9bd2bd5f250f771a60ab274629498e562a3060352cdf-product-guides-app-health-2.png" alt=""><figcaption></figcaption></figure>

#### App Insights

This section provides detailed metrics on various aspects of your app’s performance, highlighted with red, yellow, and green indicators to show their status as well as a change rate calculated by comparing the current time period to the previous time period. So for example if you’re seeing data for the past 28 days, the change is calculated by comparing to the 28 days before that.

* **Crash-Free Sessions**: Percentage of sessions without crashes.
* **Crash-Free Users**: Percentage of users who did not experience crashes.
* **OOMs**: Percentage of sessions without Out-Of-Memory errors.
* **ANRs**: Percentage of sessions without Application not Responding errors.
* **Cold App Launch**: Apdex score of the app when launched from a cold state based on its latency.
* **Network**: Network apdex score, calculated based on the performance of key network calls.
* **Screen Loading**: Apdex score of screen loading times.
* **UI Hangs**: Apdex score measuring the times your app faces UI hangs which is when the app isn't responding to the user's input for more than 250 ms.
* **App Hangs**: Percentage of sessions without App hangs which is when an app is unresponsive or more than 3 seconds.
* **Non-Fatals**: Number of non fatals reported.
* **Bugs**: Number of bugs reported.

<figure><img src="https://files.readme.io/e818184b92eb34b33cd05f39f04804c427ce8fe47f2046b5d0267353c5a11ca0-product-guides-app-health-3.png" alt=""><figcaption></figcaption></figure>

#### App Ratings

This section gives you insights on your app ratings and reviews. It is filtered by country to understand regional user satisfaction.

#### Top Releases

This section shows the latest most adopted app release and compares it with the previous versions. It includes:

* **Version Details**: Information about the latest version, such as version number and adoption rate.
* **Comparative Metrics**: Shows the top changes between the current most adopted version and the previous one.

<figure><img src="https://files.readme.io/a8c603f18bc746c8d1de55a00ac6888d5108f5eb4c85fe7a6642c638a38e79b8-product-guides-app-health-5.png" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
**Best Practices**

The App Health Dashboard is a powerful tool for developers to monitor, analyze, and improve the performance and user experience of their application. Here are the best practices for utilizing this dashboard effectively:

1. **Regular Monitoring**
   * **Daily Check-Ins**: Make it a habit to review the dashboard daily to quickly catch any emerging issues.
   * **Focus on Key Metrics**: Pay particular attention to the Overall Frustration-Free Sessions and the Crash-Free Sessions metrics to ensure your app is meeting performance standards.
2. **Analyzing Trends Over Time**
   * **Utilize the Graphs**: Use the "Frustration-Free Sessions and Crash-Free Sessions Over Time" graph to identify patterns or recurring issues. Look for dips in performance or spikes in crashes that correlate with specific timeframes or events (e.g., new releases).
   * **Historical Comparison**: Compare current performance metrics with historical data to understand how recent changes impact app stability and performance.
3. **Deep Dive into App Insights**
   * **Prioritize Red Metrics**: Focus on metrics highlighted in red in the "App Insights" section as they indicate critical issues needing immediate attention.
     * For example, if the “Cold App Launch” Apdex drops, click on it to navigate to the the “Cold App Launch” page to analyze it more.
   * **Continuous Improvement**: Aim to turn yellow metrics to green by systematically addressing the underlying issues.
     {% endhint %}


# Frustration-Free Sessions

Frustration-Free Sessions is a north star KPI scoring the share of sessions with no frustrating experiences. How it is built and read.

### What is Frustration-Free Sessions?

Frustration-Free Sessions is a **north star KPI** that reflects user frustration. It helps you measure your app’s **stability and performance**, giving you a clear signal when users experience frustrating sessions.

The score ranges from **0% to 100%**, where a higher score means your users are facing fewer frustrating experiences.

### How Is Frustration-Free Sessions Calculated?

Luciq categorizes all user sessions into four types:

* **Crashing Sessions** → Sessions that end with a **fatal crash** or OOM.
* **Frustrating Sessions** → Sessions with major issues
* **Tolerable Sessions** → Sessions with minor issues
* **Satisfying Sessions** → Sessions with smooth performance

The Frustration-Free Sessions score is calculated as:

<figure><img src="https://files.readme.io/2ca89bda2f461864c261aac073a71ce3515e527bde6db164bb485e3feafa9899-664bf013-ae28-4ab2-a263-9dd29fe7f6f6.png" alt=""><figcaption></figcaption></figure>

### What Is a Session?

A session begins when a user **launches the app** or **brings it to the foreground**. It ends when the user **moves the app to the background** or when the app **exits** (either manually or due to a crash).

#### How Are Sessions Categorized?

**Crashing Session**: If a session ends with a crash or an OOM, it is classified as crashing.

**Other Sessions**: If a session does not crash, it is categorized as **Frustrating, Tolerable, or Satisfying** based on the issues that occurred during the session.

<figure><img src="https://files.readme.io/5579da2851ec779971771f7db68d9cbd88942b092d945fe0a0556aebf57f4817-image-20250316-221816.png" alt=""><figcaption></figcaption></figure>

#### Example of Session Classification

Let's break down a session with multiple issues:

<table><thead><tr><th width="159.265625">Issue Type</th><th width="286.8515625">Impact Level</th><th>Occurrences</th></tr></thead><tbody><tr><td>App Hangs</td><td>High (an occurrence has a weight of 10 occurrences)</td><td>1 (all app hang occurrences are considered frustrating)</td></tr><tr><td>Network Requests</td><td>Low (an occurrence has a weight of 0.3 occurrences)</td><td><p>- 2 Frustrating</p><p>- 2 Tolerable</p><p>- 1 Satisfying</p></td></tr><tr><td>App Launch</td><td>Medium (an occurrence has a weight of 1 occurrence)</td><td>- 3 Frustrating</td></tr><tr><td>Flows</td><td>Medium (an occurrence has a weight of 1 occurrence)</td><td>- 2 Satisfying</td></tr></tbody></table>

**Step 1: Calculate Weighted Counts**

* Frustrating Count = (10 × 1) + (0.3 × 2) + (1 × 3) = 13.6
* Tolerable Count = (0.3 × 2) = 0.6
* Satisfying Count = (0.3 × 1) + (1 × 2) = 2.3
* Total Count = 13.6 + 0.6 + 2.3 = 16.5

**Step 2: Calculate Weighted Percentages**

* Frustrating% = 13.6 / 16.5 = 82%
* Tolerable% = 0.6 / 16.5 = 4%
* Satisfying% = 2.3 / 16.5 = 14%

**Step 3: Classify the Session**

* Frustrating Session → If Frustrating% ≥ 30%
* Satisfying Session → If Satisfying% ≥ 50%
* Tolerable Session → Otherwise

Since the Frustrating% is 82% which is over 30%, this session is classified as **Frustrating**.

#### How Is the Score Graded and Color-Coded?

| Score Range | Performance Level      |
| ----------- | ---------------------- |
| ≥ 94%       | **Excellent** (Green)  |
| 85% - 93.9% | **Good** (Light Green) |
| 70% - 84.9% | **Fair** (Yellow)      |
| 50% - 69.9% | **Poor** (Orange)      |
| < 50%       | **Unacceptable** (Red) |

### Want More Control Over Frustration-Free Sessions?

You can customize how Frustration-Free Sessions is calculated to better reflect your app’s needs.

👉 [Learn how to configure Frustration-Free Sessions](/product-guides-and-integrations/product-guides/getting-started-with-luciq/frustration-free-sessions/how-to-configure-frustration-free-sessions)

<br>


# How to Configure Frustration-free Sessions

Configure how each issue type feeds your Frustration-Free Sessions score and issue ranking, so it matches how you define user frustration.

Configure Frustration-Free Sessions your way to ensure your score reflects your unique app needs and how you define user frustration for your end users. You have full control over how different issue types contribute to frustration-free sessions and issue prioritization in the issues list.

### Define How Issue Types Affect Your Score

To adjust your configuration:

1. **Go to: Settings → Frustration-Free Sessions Config.**
2. You will see the issue types that factor into your score, based on your plan and platform.
3. (For owners only) Adjust the crash configuration of ANRs and OOMs.
   1. This dictates if a session should be considered crashing or not if an ANR or OOM occurred.
   2. If an issue type is set as crashing, its impact is set to highest and cannot be edited.
4. Adjust the **impact level** of each issue type to control how it influences session classification in frustration-free sessions and issue prioritization.

{% hint style="warning" %}
Configuring frustration-free sessions only affects future sessions; your historical score will remain unaffected.
{% endhint %}

### Impact Levels

The impact level determines how much an issue type contributes to marking a session as frustrating:

* **Highest impact**: Has a critical effect on session quality. A single occurrence of this type, even if there are other less significant issues in the session, will mark the session as frustrating.
* **High impact**: Strongly affects the session quality. Just one or two occurrences, when paired with other issues in the session, will mark the session as frustrating.
* **Medium impact**: Moderately affects session quality. A few occurrences in combination with other issues could mark the session as frustrating.
* **Low impact**: Has a small effect on session quality. Many frustrating occurrences of this type are required to mark the session as frustrating.
* **No impact**: Has no effect on session quality. Occurrences of this type do not contribute to session classification or affect frustration-free sessions.

{% hint style="info" %}
Fatal crashes are not editable; they are always set to the highest impact.\* Non-fatals are not editable; they are always set to no impact.
{% endhint %}

***

### Recommended Configuration

We recommend setting:

* **Fatals, OOMs, and ANRs** to highest impact since they completely disrupt the experience and should have the strongest influence on the score.
* **App hangs** to high impact, as they are highly noticeable and directly affect the user experience.
* **Force restarts** to high impact, as they are a strong signal of user frustration.
* **App launch** to high impact since it’s one of the most visible interactions, directly affecting user perception and app ratings.
* **Screen loading, UI hangs, and flows** to medium impact, as they moderately affect session quality.
* **Network issues** to low impact, as they occur very frequently in a session, and sometimes happen in the background. Keeping them at a low impact level ensures they don’t disproportionately affect the score.

<figure><img src="https://files.readme.io/436c8104ad7c434e0da5a05720c5e02beba5edf485d8c6de71d6a3282a74d390-Screenshot_2025-03-17_at_1.37.26_AM.png" alt=""><figcaption></figcaption></figure>

***

### Control APM Traces in Your Score

For **APM issues (App Launches, Flows, Networks, UI Hangs, Screen loading)**, which you can configure each trace as either a **Key Metric** or **Non-Key Metric**:

* **Key Metrics**: Affect your Frustration-Free Sessions score and issue prioritization.
* **Non-Key Metrics**: Do not contribute to session classification or affect the score.

By default, all traces are considered **Key Metrics**.

### Adjusting Key Metrics

You can exclude specific traces from your score in three ways:

1. From the **Issues List**

<figure><img src="https://files.readme.io/43b89150beb1ef899efecf64c4f326279ba2428caeed6f27fa6d13bdb84b6bce-Screenshot_2025-03-17_at_1.47.07_AM.png" alt=""><figcaption></figcaption></figure>

2. On the **List Page**

<figure><img src="https://files.readme.io/357910b534e44718c7d0d11c1354bf7aad1b28ee7024004c50d2441824635219-image.png" alt=""><figcaption></figcaption></figure>

3. In the **Details Page** for the issue type

<figure><img src="https://files.readme.io/712b94c06c5a4b2b4905c37ca58f16ef7f0eaa9254f6fb1cbcd003fc2f2f9e1d-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Updating your key metrics doesn't affect the already calculated app apdex and will be applied moving forward.
{% endhint %}

Fine-tuning these settings ensures that your frustration-free session score reflects only the most relevant signals for your app.


# Inbox

Inbox gathers everything worth acting on in one place, with Issues for what is broken and Opportunities for AI themes from user feedback.

Inbox is where you go to see what needs your attention: everything worth acting on, gathered in one place.

Instead of switching between products to piece together what's broken and what users want, Inbox brings that signal together under two lists:

* **Issues**: a unified view of the bugs, crashes, performance issues and other problems affecting your app.
* **Opportunities**: AI-generated themes surfaced from user feedback, highlighting feature requests, gaps, and enhancements worth prioritizing.

Both lists live side by side because they answer two sides of the same question: *what should my team work on next?* Issues tells you what's broken. Opportunities tells you what to build. Having them in one hub means you can triage and prioritize without jumping between separate tools.

Inbox is a central hub for this kind of actionable signal, a place your team can check regularly alongside the rest of your workflow. For details on how each list works, see the dedicated Issues list and Opportunities list pages below.


# Issues List

The Issues List consolidates crashes, app hangs, force restarts, bug reports, AI-detected issues, and performance metrics into one ranked view of your app.

***

### What Is the Issues List?

The Issues List is a centralized view that consolidates all your app's stability and performance issues into a single list. By combining issues like crashes, app hangs, force restarts, Bug Reporting items (both single bugs and **Bug Grouping** parents), AI-detected issues from [**Detect Agent**](/product-guides-and-integrations/product-guides/ai-features/detect-agent) (visual issues and broken functionality), and performance metrics like app launches and screen loads, it provides a comprehensive understanding of the problems affecting your app’s quality.

<figure><img src="https://files.readme.io/2788e1fc681c1b5cde861adc0c782ff5666ae3d2c015ead70793c3308b18eb6b-product-guides-issues-list-1.png" alt=""><figcaption></figcaption></figure>

### How Does It Help?

The issues list is **automatically prioritized based on each issue's impact on your App Frustration-Free Sessions**, reflecting how much the issue contributes to frustrating user sessions. By addressing the highest-impact issues first, you can significantly improve your app's quality, reduce user frustration, and boost satisfaction.

Instead of sifting through raw data or dealing with scattered issues, you can now rely on this prioritized list to efficiently manage and resolve the most critical problems first.

***

### Using the Issues List: A Workflow for Improving App Stability and Performance

#### Step 1: Monitor Your App’s Frustration-Free Sessions

The **App Frustration-Free Sessions** is your most important metric for understanding your app’s overall stability and performance. It gives you a clear signal when users experience frustrating sessions, helping you take action before issues escalate.

If you want to improve your Frustration-Free Sessions, head to the **Issues List**, a prioritized view of every performance and stability issue contributing to the score. This leads us to step 2.

#### Step 2: Identify High-Impact Issues

After monitoring your Frustration-Free Sessions, drill down into the Issues List to pinpoint the most critical problems affecting your app. The list ranks issues by their [**Apdex Impact**](/product-guides-and-integrations/product-guides/inbox/issues-list/frustration-impact), a metric that approximates how much each issue contributes to frustrating user sessions. This is calculated by approximating the percentage:

`(Frustrating Sessions caused by this issue ÷ Total Sessions) × 100`

Issues are grouped into the following categories:

* **High Impact:** Issues affecting ≥0.5% of total sessions. These are critical and require immediate attention.
* **Medium Impact:** Issues affecting between 0.01% and 0.5% of sessions. These should be addressed after high-impact issues.
* **Low Impact:** Issues affecting <0.01% of sessions. These have minimal impact but may still need resolution over time.
* **No Impact:** Issues that don’t affect your Frustration-Free Sessions, including:
  * APM groups where all occurrences are satisfying (Apdex 1).
  * Metrics marked as non-key by your team.
  * Bug Reporting items, which are always added with **No Impact** so they don’t affect your Apdex score. You’ll find them at the end of the Issues List, or by applying the **Bugs** filter. Grouped or duplicate bugs show a **count**, and clicking any bug row deep-links straight to that bug's detail page in Bug Reporting.
  * AI-detected issues from [**Detect Agent**](/product-guides-and-integrations/product-guides/ai-features/detect-agent), which are also added with **No Impact**. Each row's **Type** reads **Single**, or **Group** with the number of occurrences it holds, alongside when the issue was first and last seen. Clicking a row opens it on the AI Issues page.

For each issue, review:

* **Issue Count:** Represents the number of negative occurrences.
  * For crashes, app hangs, and force restarts: Total number of occurrences.
  * For APM metrics: Dissatisfying count, which is calculated as:\
    Frustrating Occurrences + (0.5 × Tolerable Occurrences)
* **Detailed Metrics:** Depending on the issue type, detailed metrics on the issue card provide insights into the issue’s scope and severity.

<figure><img src="https://files.readme.io/8e728143ad4373af7abf2065e65220d6b1783bc5f70287e001f30918ef361505-product-guides-issues-list-2.png" alt=""><figcaption></figcaption></figure>

Now that you've identified the issues that are impacting your users the most, the next step is to take action and address them effectively.

#### Step 3: Triage the Issue

Review the prioritized issues to determine the appropriate next steps:

**Forward to Jira**

For critical issues requiring immediate attention, forward them to Jira for tracking and resolution.

* The generated Jira ticket includes relevant context about the issue.
* Once forwarded, a shortcut to the Jira ticket will appear on the issue card for quick access.

<figure><img src="https://files.readme.io/b625cf310e912a0e677d39ad2171f4c14a2b922459f475e97607ffb18ae3c142-product-guides-issues-list-3.png" alt=""><figcaption></figcaption></figure>

**Assign to Teams**

* Assigning ensures accountability and streamlines communication.
* Assigned issues will be reflected in the team’s dashboard for full visibility.

<figure><img src="https://files.readme.io/38b5947ea034dcc91991ce73f38b04db77123d0bdd4de0fe5572e2d3f098ca71-product-guides-issues-list-4.png" alt=""><figcaption></figcaption></figure>

#### Step 4: Fix the Issue

When you’re ready to fix an issue, click on it in the Issues List or open it through the link in your Jira ticket. You’ll be redirected to the issue’s **Details Page**, where you’ll find all the debugging data you need to resolve the problem efficiently.


# Frustration Impact

Frustration Impact shows how much an issue contributes to frustrating sessions, driving both your score and where the issue ranks.

### What is Frustration Impact?

Frustration Impact helps you understand how much each issue in your app contributes to frustrating sessions, affecting both your **Frustration score** and **issue prioritization**. This calculation ensures that the issues causing the most user frustration are ranked higher in the **Issues List**, helping you focus on what matters most.

<figure><img src="https://files.readme.io/2b4eae230a41b811856f0492e32d0f7f99ee602655822f0ff0a16d99f063a59b-apdex-impact-1.png" alt=""><figcaption></figcaption></figure>

***

### Frustration Impact Levels

To better understand the significance of each issue based on its Frustration Impact, issues are classified into the following categories:

* **High Impact:** Issues affecting ≥0.5% of total sessions. These are critical and require immediate attention.
* **Medium Impact:** Issues affecting between 0.01% and 0.5% of sessions. These should be addressed after high-impact issues.
* **Low Impact:** Issues affecting <0.01% of sessions. These have minimal impact but may still need resolution over time.
* **No Impact:** Issues that don’t affect your frustration-free sessions score, including:
  * APM groups where all occurrences are satisfying (Frustration-Free Sessions 1).
  * Metrics marked as non-key by your team.

***

### How is Frustration Impact Calculated?

The Frustration Impact of an issue reflects its effect on your app's frustration-free sessions by estimating the **percentage of frustrating or tolerable sessions it causes.**

#### For performance issues:

**Example 1: Cold App Launch**

We approximate the percentage of sessions that were **frustrating** or **tolerable** due to the **cold app launch**. We calculate this by considering how bad the **cold app launch** is within all app launches, and **how much app launches contribute to frustrating and tolerable sessions** in total.

<figure><img src="https://files.readme.io/63c8179a4263bf091ea7ac67b7e812df9f0db74445e1cc8e85eb89b976f22b57-Screenshot_2025-03-25_at_4.40.26_PM.png" alt=""><figcaption></figcaption></figure>

Where:

<figure><img src="https://files.readme.io/efb66e00632dd6617410205e1ac77aa23ba24b91a810ac4aabddcdeb663c9752-image.png" alt=""><figcaption></figcaption></figure>

**Example 2: Network Requests**

We approximate the percentage of sessions that were **frustrating** or **tolerable** due to this particular **network request**. We calculate this by considering **how bad the network request is** within all networks, and **how much networks are contributing to frustrating and tolerable sessions** in total.

<figure><img src="https://files.readme.io/432fa737d1d79e8bff04ad4e247a076a2a7a002d7e420d8a74e24fc2461ae0c7-Screenshot_2025-03-25_at_4.38.48_PM.png" alt=""><figcaption></figcaption></figure>

Where:

<figure><img src="https://files.readme.io/513af81c9ca1957ab9f4272dc85af54bac0ce02531d95a1c8c8e5c8a395ad615-image.png" alt=""><figcaption></figcaption></figure>

**Example 3: App Hangs**

<figure><img src="https://files.readme.io/e9b552b3d649294f2033b56827a306f031792afae00785479f98a8b7770ab555-Screenshot_2025-04-14_at_4.00.14_PM_1.png" alt=""><figcaption></figcaption></figure>

Where:

<figure><img src="https://files.readme.io/4111cb3d88193e62c09b10e11a370b52bd2ec074fda01a30e30fe7be1a3b6edf-image.png" alt=""><figcaption></figcaption></figure>

**Notes:**

* This calculation applies to **app launch**, **networks**, **screen loading**, **flows**, **UI hangs**, **app hangs**, and **force restarts**.
* When calculating **Frustrating/Tolerable Sessions Caused by an issue, tolerable sessions** are given **half the weight** compared to **frustrating sessions**.
* **Dissat count** = (number of frustrating occurrences) + 0.5 \* (number of tolerable occurrences)

#### For Crashes:

A crash occurrence disrupts the user session completely. So we simply calculate the percentage of sessions this crash impacted.

<figure><img src="https://files.readme.io/5bb918a156bee2a5221f3ac794fef0a993367a8f0ed62137e971204837dda719-Screenshot_2025-03-25_at_4.38.59_PM.png" alt=""><figcaption></figcaption></figure>

**Notes:**

* This calculation applies to fatal crashes, ANRs for Android, and OOMs for iOS.

<br>


# Opportunities

Let AI read your users' bug reports, app store reviews, and survey responses and synthesize them into a prioritized list of what to build next.

Opportunities is an AI-powered tab inside the **Inbox** that turns your users' feedback into a prioritized, actionable list of product improvements. Every hour, the AI reads new **bug reports**, **app store reviews**, and **survey responses**, identifies the recurring themes your users keep raising, and surfaces them as *Opportunities*, each ranked by priority and backed by the exact reports that triggered it.

Until now, answering *"what should we build next?"* meant context-switching between Bug Reporting, App Ratings & Reviews, and In-App Surveys, then manually stitching the signals together. Opportunities collapses that into one place, powered by your users' own words, not a spreadsheet.

{% hint style="info" %}
**Opportunities is in beta.** It lives in the **Inbox**, alongside your Issues list.
{% endhint %}

### What are Opportunities?

An Opportunity is a recurring product improvement theme that the AI has identified across multiple pieces of user feedback. Instead of reading hundreds of individual bug reports, reviews, and survey responses, you get a short, prioritized list of what your users are collectively asking for.

<figure><img src="/files/pHJ3HrdwZl8xJnsHYqBd" alt="The Opportunities tab in the Inbox showing AI-ranked product opportunities"><figcaption></figcaption></figure>

The list view gives you an at-a-glance summary of every Opportunity:

* **Title**: a concise description of the theme, such as *"Add dark mode theme option."*
* **Priority**: a suggested priority from **Trivial** to **Blocker**.
* **Source count**: how many bug reports, app store reviews, and survey responses contributed to the Opportunity.
* **Updated** and **Created**: when the Opportunity first appeared and when new evidence last reinforced it.
* **Team**: the team currently assigned to the Opportunity.

You can filter the list by **status**, **priority**, and **team** to focus on what matters most.

### How it works

Every hour, the AI scans the new bug reports, app store reviews, and survey responses collected since its last run and looks for **recurring patterns**. When several pieces of feedback point to the same underlying need, the AI groups them into a single Opportunity, writes a clear title, and assigns a suggested priority based on how strongly and how often your users raise it.

As more feedback comes in, existing Opportunities keep evolving: new reports are attached to the theme they match, and the priority adjusts to reflect the latest signal.

### Enabling Opportunities

Opportunities is self-serve, so you can turn it on for your app in a few seconds:

1. Open your app on the Luciq dashboard.
2. Click **Inbox** in the sidebar, then select the **Opportunities** tab.
3. Hit **Enable**.

The AI picks up your app on its next hourly run and begins surfacing Opportunities from your existing bug reports, app store reviews, and survey responses.

### Working with an Opportunity

Click any Opportunity to open its detail view, where you can review the evidence behind it and manage it just like you manage a bug.

<figure><img src="/files/E5sH7jO04HijsHFF0zim" alt="An Opportunity detail view with its evidence timeline, sources, and management actions"><figcaption></figcaption></figure>

* **AI synthesized**: a badge marking that this Opportunity was generated by the AI, with the dates it was created and last updated.
* **How this evolved**: a timeline that shows how the theme built up over time as new bug reports, reviews, and survey responses reinforced it, so you can see the reasoning, not a black box.
* **Status**: move an Opportunity through **New → In Progress → Closed / Dismissed** as your team acts on it.
* **Priority**: adjust the suggested priority anywhere from **Trivial** to **Blocker**.
* **Team**: assign the Opportunity to a team for ownership and follow-up.

### Sources panel

The **Sources** panel lists every bug report, app store review, and survey response that drove the AI's reasoning for an Opportunity. Each source shows its type (**Bug**, **App review**, or **Survey**) along with its status and when it was created, and links directly to the original report or survey. This gives you full traceability: you can always trace an Opportunity back to the specific user feedback behind it.

{% hint style="info" %}
Opportunities are generated from **bug reports**, **app store reviews**, and **survey responses**. Support for **feature requests** is on the roadmap.
{% endhint %}


# Bug Reporting

Luciq Bug Reporting collects reports from testers and users with logs, screenshots, and session data. Covers grouping, categories, replies.


# Bug Grouping

Bug Grouping detects duplicate reports automatically, giving you cleaner bug lists, truer issue impact, and one master report to act on.

## Overview

Bug Grouping helps teams stay focused by:

* **Detecting duplicate reports automatically** -> Reduces manual triage and repetitive review
* **Reflecting the true magnitude of an issue** -> See how many duplicates belong to the same underlying problem
* **Letting you act once on the master report** -> Key actions propagate across duplicates

This feature works **side-by-side** with manual duplication. Manual grouping continues to work as-is.

<figure><img src="/files/Lqz4ybb5SkqHrIcRmPqG" alt="" width="563"><figcaption></figcaption></figure>

***

### How it works

When a new report is received, Luciq evaluates it against recent reports and determines whether it belongs to an existing group or should remain ungrouped.

* **Semantic matching**: Reports are compared by the meaning of their descriptions, not just exact wording, to find likely duplicates.
* **Group assignment**: If a match is found, the report joins that group as a duplicate. If not, it stays ungrouped/single.
* **Fast fallback**: The grouping logic is done on our backend, so if an automatic grouping can’t complete quickly (with in 30-secs), the report is shown normally (ungrouped) so your workflow is never blocked.

{% hint style="info" %}
**Grouping Logic**

The grouping logic focuses primarily on:

* **Report description semantic similarity**, plus
* **Report category/type match** (e.g., Bug vs Question)

Notably:

* **Subcategory is not used** in grouping.
* Additional attributes (screen name, app version, etc.) **are not** part of the current grouping logic as well.
  {% endhint %}

:new:**Additional Parameters: Grouping with logs and user steps**

In addition to the matching described above, Luciq can use two extra signals to make grouping decisions sharper: **network logs** and/or **user steps**.

* **Network logs**: the failing endpoints and HTTP errors hit during the user's session.
* **User steps**: the screens the user moved through, the screen they were on when the report was triggered, and the last action they took.

With these signals, two reports that hit the same failing endpoint or end on the same error screen can be grouped together even when their titles or descriptions don't match. Reports that read similarly but exercise different parts of the app are kept apart.

{% hint style="info" %}
**Enabling log-based grouping**

* This is an opt-in setting on top of Bug Grouping. You can turn **Network logs**, **User Steps**, or both on directly from your app settings page. See [Enabling Bug Grouping](#enabling-bug-grouping) below.
* When enabled, a new report may be held for up to **10 minutes** while its logs become available. If logs are delayed beyond that window, the report falls back to standard (text-only) grouping; if grouping still can't be applied, the report lands on the dashboard ungrouped so your workflow is never blocked.
  {% endhint %}

***

### What you’ll see in the dashboard

#### Group types

Bug reports are presented as one of the following:

* **Ungrouped report**: not part of any group AKA Single report.
* **Manual grouping**: created by your team.
* **Automatic grouping**: detected and grouped automatically.

Automatic group masters are visually distinguished so you can tell what was auto-grouped vs manually grouped.

{% hint style="info" %}
**Key concepts**

* **Master report**: the parent report representing the group’s “canonical” issue.
* **Duplicate report**: a report automatically or manually marked as duplicate of a master.
* **Ungrouped/Single report**: not part of any group.
  {% endhint %}

#### Automatic group master details

<figure><img src="/files/78oS6s5NuZ8Y9C5lWwzg" alt="" width="563"><figcaption></figcaption></figure>

Automatic group masters include an “Automatic group master” section that provides:

* **Group summary**: A short description of the shared issue across duplicates
* **Grouping confidence**: A confidence indicator for the grouping decision
* **Number of grouped reports**: Total duplicates in the group
* **Show duplicates**: View all duplicates in a dedicated list/drawer

{% hint style="info" %}
**What to expect**

* **Group summary**: A concise description of the shared issue across the group. It may take a short time to appear after grouping.
* **Confidence**: A confidence indicator for automatic grouping decisions. Group-level confidence is derived from the duplicates within the group. Manual duplicates do not contribute to the confidence calculation.
  {% endhint %}

{% hint style="info" %}
**How the group summary stays fresh**

* When your grouping logic includes **Network logs** or **User Steps**, those signals are factored into the summary alongside the report titles, so it reflects real technical detail rather than titles alone.
* The summary regenerates automatically as the group grows: it is refreshed when the group reaches **3 duplicates**, and again at **7 duplicates**. Each refresh considers every duplicate in the group, including manually-marked ones.
* Only automatic grouping triggers a re-summarization. The group **title** is set once, on the first summary, and stays fixed while the summary itself regenerates.
  {% endhint %}

#### Group Patterns

Group masters include a **Group Patterns** section that shows the common metadata patterns across the duplicates in the group. Similar to Crash Reporting, you can see patterns across:

* **App version**
* **Devices**
* **OS version**
* **Current view**
* **Feature requests**

This helps you spot the potential root causes of a grouped bug without opening every duplicate in the group. Group Patterns is enabled by default on top of Bug Grouping.

#### Automatic duplicate details

Automatic duplicates show a banner indicating they were automatically marked as duplicates, and provide a shortcut to view the master report.

<figure><img src="/files/aUUkNFnfUsFNEZkZyRae" alt="" width="563"><figcaption></figcaption></figure>

#### Actions & workflows

Automatic groups follow the same core workflow as manual duplicates:

* **Master-first actions**: Update status/assignee/priority on the master report and duplicates follow.
* **Duplicates are restricted**: You can’t apply master-level actions directly from duplicate reports.
* **Change group master / mark as duplicate**:
  * For ungrouped/single reports, you can mark them as duplicates of an existing master.
  * For master reports, you can change with another ungrouped or master reports. **Note:** When changing a master with another master, you’ll simply merge the two groups together.
  * For duplicate reports, you can unmark them as duplicates (removing them from the group).

#### Filtering & bulk actions

When Bug Grouping is enabled, the dashboard exposes a **Report Groups** filter that lets you view:

* **Ungrouped reports**
* **Automatic** -> Masters / Duplicates
* **Manual** -> Masters / Duplicate

<figure><img src="/files/QWG8S0OP3gBh6vrvRPq1" alt="" width="312"><figcaption></figcaption></figure>

Bulk actions are intentionally limited to prevent accidental changes across duplicates. If your current filter selection includes duplicates or doesn’t target masters appropriately, bulk actions will be disabled with an explanatory tooltip.

***

## Tags (master vs duplicate)

Luciq tags reports when they are created so you can distinguish master vs duplicate reports consistently.

* **Master reports**: Master\_report
* **Duplicate reports**: Duplicate\_report

**Note:** If Tags Sync is disabled in your 3rd party integration, master reports tag may not appear in certain tracking views when forwarding automatically.

<figure><img src="/files/jzikthxNrbWcwXzM3uzG" alt="" width="563"><figcaption></figcaption></figure>

***

## What to expect (and what not to expect)

**What to expect**

* Faster triage through automatic duplicate detection
* Clear distinction between manual and automatic groups
* Master-first workflow: one action on master updates the group

**What not to expect**

* New reports will not be automatically grouped into existing manual groups
* Perfect grouping in every case, yet. (you can always adjust groups manually)
* Duplicate grouping into closed/resolved issues (closed groups won’t continue collecting new duplicates)

***

## Enabling Bug Grouping

You control Bug Grouping directly from your **app settings** page on the dashboard, with no need to reach out to us. From there you can:

* **Enable or disable Bug Grouping**, and give your consent to the feature directly on the dashboard.
* **Enable or disable Network logs** in the grouping logic.
* **Enable or disable User Steps** in the grouping logic.

**Note:** After enabling the feature, **grouping will be applied only to reports that are received after the feature is enabled**; it won’t be applied retroactively to old bug reports.


# Bug Grouping | Alerts & Rules

Alert and rule recommendations for teams running Bug Grouping, including how to target master, ungrouped, and duplicate reports separately.

## Overview

If you have Bug Grouping enabled, you can choose which report types to alert on or automate with rules. You can set alerts or rules for:

* **Master & Ungrouped reports**: the parent report and any report that is not part of any group.
* **Duplicate report**: a report automatically or manually marked as a duplicate of a master.

***

### How it works

#### Main Approach

Start by choosing a trigger. You can target either **Any report type** or **Master & Ungrouped reports**.

<figure><img src="/files/1BAQtG9911r6kdjJZ5eD" alt=""><figcaption></figcaption></figure>

To trigger on **all reports** (masters, singles, and duplicates), choose **Any bug is reported**. It triggers whenever you receive **any** bug report.

If you only want **Master & Ungrouped/Single** reports (**recommended**), choose **Not a duplicate bug is reported**. This reduces noise from alerts when a **duplicate report** is received.

#### Another Approach | Utilize report tags

* By default, when a report becomes a **master report,** a new "Luciq\_Master\_report" tag is added to it.
* The same applies to **duplicate reports**. When a duplicate bug is reported, it gets a new "Luciq\_Duplicate\_report" tag.
* You can use these tags to trigger a rule when:
  * A bug report becomes a group master.
  * A bug report is marked as a duplicate of an existing report.

{% columns %}
{% column %}

<figure><img src="/files/ntknCHFkWXPYn9ioojQW" alt=""><figcaption><p>Setting a rule for master reports</p></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/MEQr2ewFRXWzO4U8cFkq" alt=""><figcaption><p>Setting a rule for duplicate reports</p></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

{% hint style="warning" %}
**Noise Alert**

Rules based on **tags** can get noisy if you rely heavily on tags in your workflow. This rule **triggers every time** a new tag is added to the report.
{% endhint %}

{% hint style="info" %}
**Why are Master & Duplicate reports tagged?**

For 3rd-party integration tools that support 2-way tag sync (for example, Jira), these tags can show up in those tools. They help you identify the report type outside the Luciq dashboard.

<img src="/files/NOr27CSTkRg65vqfhRG9" alt="" data-size="original">
{% endhint %}

***

## Good to know

* By default, our **Triage Agent** adds a comment in the Activity & Comments section when a new **duplicate report** is added to a **master report.**
* This comment is then be shown in **Jira** (or other integrations that support syncing of comments) to update you on the volume of duplicate reports received.

{% columns %}
{% column %}

<figure><img src="/files/XHHyZu7OrWHGK97ZqEy4" alt=""><figcaption><p>Triage Agent's comment on Luciq's dashboard</p></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/MlJkLtIuF9qrzmSe81Af" alt=""><figcaption><p>Triage Agent's comment inside the Jira ticket</p></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

{% hint style="info" %}
**Note on comments syncing**

Make sure you enable the Comment One-way Sync from Luciq to Jira for the comments to reflect on Jira.

<img src="/files/vn1gsxgqkYIyK1BEe2xA" alt="" data-size="original">
{% endhint %}


# Report Types & Content

This page covers the content found in the reports sent to the bugs page of your dashboard and relevant APIs for your iOS apps.

### Report Types

There are three different types of reports that, while function similarly for the end-user, appear with different types on the dashboard in order to easily separate between incoming issues. All three types will reach your dashboard with the same amount of information and can be filtered so that you can look at specific types only.

![](https://files.readme.io/22284fb638da33f2aa76fb1016bc0a270d3d3151bc25a41567187c29816caaaf-ios-bug-report-content-3.png) ![](https://files.readme.io/74bb70bbd5249226c4da54f0f64657be6ef551d2b01d64ea0682ef78482ca8a3-ios-bug-report-content-1.png)

#### Bug

When the user selects "Report a bug" from the prompt options, the sent report will appear with type Bug. This type of report is primarily meant for reporting bugs that were found in the application.

![](https://files.readme.io/77d63c1c3630513dd2235948e2d69663c62c28052b6138aec999eeb6186404cc-ios-bug-report-content-4.png)

#### Improvement

Selecting "Suggest an improvement" from the prompt options will open the improvement suggestion form that your user can use to give you feedback on how you can improve the application in certain areas. These reports are shown on the dashboard with type "Improvement".

![](https://files.readme.io/32bd146e55deab039e9b03a795a5254a59fbb6497be74025c8d7691ead88537c-ios-bug-report-content-2.png)

#### Question

The third option in the prompt options is "Ask a question". The purpose behind this option is for users that don't quite have a bug or an idea for an improvement, but rather a question they'd like to ask you. These show on your dashboard with type "Question".

![](https://files.readme.io/a473ffbf6dc6c7d369013ac65d364d77f63f8888cbebfe228d0116a4fcda9223-ios-bug-report-content-5.png)

### Report Content

The reports (bugs, improvements, and questions) that your users submit from your app (from the Prompt Options) are sent to the bugs page of your dashboard.

With each report, you receive a plethora of details that will help you fix bugs and get more context about the comments you receive. Throughout this page, you will learn about all the information that comes in these reports, as well as any relevant APIs that you can use to customize the data that you receive, including:

* User Attributes
* Bug Report Fields
* Attachments
* Auto Screen Recording
* Logs
* Repro Steps
* Session Profiler
* View Hierarchy
* Tags

#### User Attributes

Default attributes as well as any custom user attributes that you set are automatically sent to your dashboard with all reports.

Default attributes listed in each report include:

* App version
* Device
* OS version
* App view
* Device location
* Session duration

More details about how to set custom user attributes can be found [here](/ios/setup-luciq-for-ios/custom-settings/user-identification/user-attributes).

#### Bug Report Fields

The image below shows the first view that your users see when reporting a bug after [invoking the SDK](/ios/setup-luciq-for-ios/setup-bug-reporting/showing-luciq).

<figure><img src="https://files.readme.io/d098bb0-3._MB_Compose_Report_Bug.png" alt="696"><figcaption><p><em>The first step of the bug reporting flow that your app users experience.</em></p></figcaption></figure>

#### Email Address

By default, your users are required to enter a valid email address to submit a bug or feedback, You can also remove the email field from the UI completely.

### Attachments

Your users can submit two types of attachments with any report: default Luciq attachments (files that they can select from their device) and custom extra attachments (additional files that you can attach using code).

#### Luciq Attachments

When your app users invoke Luciq, the SDK automatically captures a screenshot of their current view. This is the default attachment that is sent with any report. Your users can annotate this screenshot by drawing on, magnifying, or blurring specific parts.

In addition, there are other attachment types that your users can choose to send with each report. All attachment types can be enabled or disabled.

The attachment options are:

* Extra screenshots
* Images from photo library
* Screen recording

  <figure><img src="https://files.readme.io/8d0025ed8991d209f36a38ef8470795daa2ae0f7a4adea4de5207512898a87d1-image.png" alt=""><figcaption></figcaption></figure>

All attachment options are enabled by default if they are available in your current **plan**.

#### Auto Screen Recording \[Beta]

{% hint style="info" %}
**Used for Internal Testing**

The main purpose behind this feature is to specifically use it for internal testing rather than on production.
{% endhint %}

You can also automatically capture a screen recording of your app up to the last 30 seconds before a report is sent. **By default, this is disabled**. **Your users will also be prompted once the recording starts at the beginning of the session and will have the ability to remove the video from the attachments when sending the report.** The auto screen recording attachment counts towards the limit of 4 attachments in total.

<figure><img src="https://files.readme.io/8d0025ed8991d209f36a38ef8470795daa2ae0f7a4adea4de5207512898a87d1-image.png" alt=""><figcaption></figcaption></figure>

### Logs

A whole host of logs are sent with every report. These logs include:

* **Console Logs:** Default logs that are printed to the console when the application is running.
* **Luciq Logs:** Logs with different verbosity levels that you can add manually.
* **User Steps:** Every step the user has taken in the form of log entries.
* **Repro Steps:** User steps prior to the bug report grouped by view.
* **Network Logs:** A log of each network request.
* **User Events:** A manual log of actions that a user has taken.

More details regarding logging can be found [here](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/report-logs). Different log types are enabled depending on your plan.

<figure><img src="https://files.readme.io/0ed473dbae395f24482b2ab6a5236d0253b1b694577a531a43ee6f2d25ebb52e-image.png" alt="An example of the expanded logs view in your dashboard."><figcaption></figcaption></figure>

### Repro Steps

Repro Steps help you reproduce a bug by displaying your users' actions in each view of your app. With each view, you will find a list of actions that tell you exactly what the user did in that view. More details can be found [here](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/repro-steps).

<figure><img src="https://files.readme.io/74bb70bbd5249226c4da54f0f64657be6ef551d2b01d64ea0682ef78482ca8a3-ios-bug-report-content-1.png" alt=""><figcaption></figcaption></figure>

### Session Profiler

With each report, you'll receive a detailed environment profile covering the last 60 seconds before a bug or feedback was submitted. This Session Profiler includes device data like memory load and battery state. More details about the information contained in the Session Profiler can be found [here](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/session-profiler).

<figure><img src="https://files.readme.io/173048747ef4f3084479afe370d4d120d9c7e8df9d5d0ec5becfae60f3d5197d-image.png" alt=""><figcaption></figcaption></figure>

#### View Hierarchy

With the View Hierarchy feature, you can visually inspect each layer in your app and see all the properties and constraints of each subview so you can spot errors at a glance.

This feature is critical for investigating UI bugs as it makes the process of finding the problem and fixing it faster and simpler. For example, if you receive a report that a certain UI view is missing, you can use View Hierarchy to easily discover if the missing view is hidden behind a higher layer, out of the parent view's bounds, or missing from the window.

Any editable text or text fields will automatically be replaced with asterisks.

<figure><img src="https://files.readme.io/b53ae9ed108d457d6122c178839f8c520ae242351931fa52a44536c60c953299-image.png" alt=""><figcaption></figcaption></figure>

#### Tags

You can add tags to the reports you receive to help you filter and triage bugs in your dashboard. More details regarding tags can be found [here](/ios/setup-luciq-for-ios/custom-settings/user-identification/tags).

<figure><img src="https://files.readme.io/ae430d5077ce40cad8335f91b5f5252b1df2353eca4600a567e720acf57eff64-image.png" alt=""><figcaption></figcaption></figure>

<br>


# Reply to Reporters

Send an in-app chat to the user who filed a bug, crash, or survey response, and read the icons Luciq shows in your list of reports.

{% hint style="info" %}
**Separate Conversations**

Each open conversation can only be viewed from its related issue. If you reply to a user who reported a specific bug, you can only access that conversation from that specific bug report. The same is true for crash report and survey response.
{% endhint %}

### Reply to User

Let's say you receive a bug report from a user and you want to let them know that a fix is on the way or ask for more details. You can do this by reaching out to them directly from the bug report in your dashboard using the **Reply to User** button (or **View Conversation** if one already exists) as shown below.

By default, your users will receive an in-app notification, then an email if they miss the notification.

<figure><img src="https://files.readme.io/8d0025ed8991d209f36a38ef8470795daa2ae0f7a4adea4de5207512898a87d1-image.png" alt=""><figcaption></figcaption></figure>

You can have conversations with your app users without ever having to leave the bugs page.

{% hint style="info" %}
**Note**

To ensure chat privacy and prevent conversations from being mixed between different users, it is critical to use a unique ID for each user. For more details, please see our guide on [User Identification](/ios/setup-luciq-for-ios/custom-settings/user-identification).
{% endhint %}

#### Chat Icons

You can easily see if you have are any unread messages from users by checking the icons in your list of reports.

There are three different chat icons that indicate which reports contain conversations.

* **Blue chat icon with red dot:** You have a new unread message from the user.
* **Blue chat icon only:** You read the message but didn't reply (the user sent the last message in the conversation).
* **Grey chat icon with arrow:** You have replied to this user (you sent the last message in the conversation).
* **No icon:** There is no conversation in this report.

Your reports are also sorted so that the reports with the newest messages appear first instead of when the bug was first reported. This resurfaces the reports that require your attention instead of staying buried in your list.

"Closed" reports that receive new messages are automatically changed back to "In Progress" and will appear in your list.

<figure><img src="https://files.readme.io/55d06b7-Chats_Icons_Legends.png" alt="1588"><figcaption><p><em>The chat icons are visible in your list of reports on the lefthand side of the bugs page in your dashboard</em></p></figcaption></figure>


# Extended Bug Report

The Extended Bug Report adds fields for steps to reproduce, actual results, and expected results, so triage means less free-form reading.

### What is an Extended Bug Report?

Free-form comments from reporters can be time-consuming to read through when triaging bugs. The Extended Bug Report standardizes all of your bug reports with additional fields that are commonly used by QA and technical beta testers: steps to reproduce the bug, actual results, and expected results.

<figure><img src="https://files.readme.io/be1fda00d2c01eb679298b1db96ff4606d469278569273f199861d24f1d43d87-ios-extended-bug-report-1.png" alt=""><figcaption></figcaption></figure>

If enabled, the Extended Bug Report adds a second step to the bug reporting flow that your testers experience in your app.

<figure><img src="https://files.readme.io/828b74a276d747107e40bc66b60044494876b597d7e378d61403d503ba360e47-ios-extended-bug-report-2.png" alt="6534"><figcaption><p><em>The Extended Bug Report includes additional fields for your testers to complete when sending reports.</em></p></figcaption></figure>


# Proactive Bug Reporting

Learn how Proactive Reporting works in Luciq. The SDK detects a frustrating experience and prompts that user to submit feedback about it.

### What is Proactive Reporting?

Proactive Reporting is a feature that prompts end users to submit their feedback after our SDK automatically detects a frustrating experience.<br>

<figure><img src="https://files.readme.io/aae6c1770a4d42429856d92eb93248024babff70a66c3de6d9fe92e11603bb00-Bug_Reporting.gif" alt=""><figcaption></figcaption></figure>

The frustrating experience our SDK detects to trigger the feedback modal is [**Force Restart**](/product-guides-and-integrations/product-guides/crash-reporting/force-restarts).

If you have our **Force Restart** product enabled as part of your plan, users will be prompted to submit their feedback and explain what triggered them to force restart the app.

The details of the feature are as follows:

* You should have **Force Restart** as part of your plan.
* Enable the feature (Using the APIs in the upcoming section).
* Once the SDK captures a Force Restart occurrence, a modal will be triggered to ask your end-users if they want to share their feedback and experience.
* This feedback will reflect on your Bug Reporting page in the dashboard.

If enabled, users will first see a modal asking if they want to report this experience. If their answer is yes, they will see a **Feedback** description model that allows them to enter their email and describe their experience using their own words.

|                                                                                                                             |                                                                                                                             |
| --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| ![](https://files.readme.io/ba2fb841c90a3ee873e13a59fa07c9c64fc7d3eb27379cd89e17d96b0e9422bd-ios-proactive-reporting-1.png) | ![](https://files.readme.io/96690010a0bb1031eaeba0c1a136279c9ff405974320fce53d4dbcec4134bec3-ios-proactive-reporting-2.png) |

On the dashboard, your end-user's feedback will be reflected in the Bug Reporting page as a **Frustrating Experience** report type.

Clicking the **Open Occurrence** under the **Force Restart** section will take you to the occurrence where the end-user force closed the app in the Force Restart product.

<figure><img src="https://files.readme.io/72c393502da0f2ade0af17cb5ca540a6703f720c4061e20fc2415f37f2e0615b-ios-proactive-reporting-3.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
The report's details show the data from the session after the user force-closed the app, while the details you’ll see in the Force Restart occurrence are from the session in which they force-closed the app.
{% endhint %}

### Good to know about Proactive Reporting

The feature’s default behavior is as follows:

* Feature is disabled.
* If you don’t configure the gap between pop-ups, the default gap will be 24 hours.
* If you don’t configure the gap between the launch and the first pop-up, the default gap will be 2 seconds.
* If you have [Surveys](/product-guides-and-integrations/product-guides/in-app-surveys) enabled in your plan, please contact the support team to enable this feature for you from our backend. Remember to configure the surveys triggering differently from the proactive reporting modal to avoid overwhelming your users.
* To use this feature, [Force Restart](/product-guides-and-integrations/product-guides/crash-reporting/force-restarts) (part of Crash Reporting) has to be part of your plan.
* Proactive reports have the same data retention as Force Restart (part of Crash Reporting)
* If you use our Crash Consent feature, the pop-up won’t be displayed in the session in which the crash consent will appear.


# Report Categories

Set custom report categories so users pick a type before sending a bug or suggestion. Configure them from the dashboard with no code changes.

The **Report Categories** are built specifically to optimize triaging your bug reports on Luciq, help you take faster actions and make faster decisions. It's very easy to set them up, tweak them and change them. **Everything can be controlled from your Luciq dashboard without any code changes in your application.**

You give your users a list of categories to pick from before they send you a bug report. Then, based on the category each report belongs to, the magic will start to happen.

<figure><img src="https://files.readme.io/602a29af898b45ca1546c5cdd8eec37b2dcc6a973bef37d55472e073d440d9a6-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Tip: Avoid naming categories with the same name as the main report types (Bug, Improvement, or Question) to avoid confusion.
{% endhint %}

### How Can I Benefit from the Report Categories?

#### Make Fast Decisions

With a glimpse of an eye, you can spot the categories each report belongs to. You can then start following the course of actions that you and your team agreed on following for each category.

#### Don't Let Your Users Wait

It's never guaranteed that your team will be able to reply back to all the users and their reports on the spot. Instead of making them wait, reply back to them with a content relevant to the problem or issue they reported. You can set an automatic reply and customize the content based on the category they picked before sending the report. This is easily done through the [alert & rules](/product-guides-and-integrations/product-guides/automation-and-workflows/alerts-and-rules/alerting-and-automation-for-bug-reporting).

<figure><img src="https://files.readme.io/d4da0dd3c95167f035c3fe9db230b894c463944f37a5888084a9351e81445451-image.png" alt=""><figcaption></figcaption></figure>

#### Auto-Forward to the Relevant Project

Is your team already used to a specific tracking tool (Jira, Trello, etc..)? You can auto-forward your bug reports to the relevant project depending on the selected category by the user. For example, if you're several teams and each team has an independent Jira project where they track their issues and cards, create a rule that will auto-forward all the reports relevant to each team to their Jira project.

<figure><img src="https://files.readme.io/d5e4456b03e64a4f4348dfcfc0d252f3d9d6a9c866a483de990d288a651f8018-image.png" alt=""><figcaption></figcaption></figure>

#### Auto-Assign the Reports to the Relevant Team Member

If each member of your team is responsible of handling specific types and categories of reports, auto-assign the report to them based on the selected category by the user.

<figure><img src="https://files.readme.io/cd47c1053620b4adc2ee259788187542663c4bbfc4c82a704b02752aebfdfd79-image.png" alt=""><figcaption></figcaption></figure>

### How Can I Create Categories?

It's really simple. Click on the **Settings** button, Select **Report Categories**, then, start adding the categories you have in mind.

<figure><img src="https://files.readme.io/6da05f7fd4a7cb9c42c0fbf143848fa1a81e25273a52a7c525229825acf4275a-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Avoid using commas to separate words within the same category, as our system will treat them as different categories when you filter by them on the Bug Reporting page.
{% endhint %}

#### Create Nested Categories

For more accurate and granular setup, you can add nested categories and build more detailed hierarchies. For example, the first level of categories can represent the main areas, features, products, or screens, in your app. Then, you may need to another level, to describe more details functionality within each one. Let's look at the following example:

* Report a bug
* Newsfeed
  * Writing a new post
  * Adding comments
  * Reactions
  * Unfollow a person or a page
* Friends requests
  * Approving a request
  * Rejecting a request
  * Sending a request
* Messages
* Privacy
* Suggest an improvement
  * Supporting more languages
  * Content improvements
  * Feature suggestion
    * Newsfeed
    * Friends requests
    * Messages
* Ask a question
* How can I join a group?
* How can I leave a group?
* How can I unfollow a person or a page?
* How can I delete my account?
* If your app for free?

In your rules and filters, you can combine more than one condition together to make sure your logic applies to the reports coming from a specific path of selection. For example, you may want to forward the "Newsfeed bugs" to a different Jira project than the "Newsfeed improvements".

Whatever changes or additions you're making in your Report Categories setup might take up to 24 hours to be reflected in the already installed applications and will appear immediately in freshly installed​ applications. If you want to test how the categories look like in your application on the spot, make sure you uninstall the application from the device you're testing with and install it again.

Do you have any questions? Let us know, we would love to help!


# Crash Reporting

Luciq Crash Reporting covers crashes, app hangs, ANRs, flame graphs, and occurrence detail, plus AI-ready crash context for coding agents.


# Crash Reporting Types

Understand the different types of crashes Luciq detects and reports. Learn how handled exceptions, native crashes, and ANRs are captured and categorized.

{% hint style="warning" %}
**Privacy Policy**

It is highly recommended to mention in your privacy policy that you may be collecting logging data in order to assist with troubleshooting crashes.
{% endhint %}

### Crash Types

**Fatal Crash**: Fatal crashes refer to an error or issue that causes the app to terminate unexpectedly, meaning the app completely shuts down and is no longer usable until the user restarts it. These crashes interrupt the user experience, as the app cannot recover from the issue on its own and must be relaunched.\
\
Fatal crashes are the most severe type of crashes, and they generate crash reports that help developers investigate what caused the app to crash. Typically, they occur due to unhandled errors, system conflicts, or serious resource issues. Here are the types of fatal crashes

**Non-Fatals**: Non-fatal crashes refer to an error or issue that occurs in the app but doesn’t cause the app to completely shut down or crash. Instead, the app encounters a problem, such as an exception or unexpected behavior, but is able to continue running without quitting. Non-fatal crashes are useful for developers because they provide insights into bugs or problems that need fixing before they turn into full-blown crashes.

#### **iOS Specific Issues**

1. **Signal-based crashes**: These are crashes triggered by low-level system signals, typically due to issues like segmentation faults or accessing invalid memory. Common signals include SIGSEGV, SIGBUS, and SIGABRT.
2. **Exception-based crashes**: These occur when an unhandled exception (such as NSException) is thrown, causing the app to crash. This can result from incorrect logic in the app or unhandled edge cases.
3. **Objective-C runtime errors**: Crashes that occur due to issues with the Objective-C runtime, such as unrecognized selectors (when a message is sent to a nil object or invalid object). These crashes happen when the app is trying to follow instructions, but one is missing or wrong.
4. **OOM**: Out-of-memory crashes occur when the app uses more memory than the device can provide, causing the system to terminate the app to free up resources. Common causes of OOM crashes include: **Memory leaks, Heavy resource usage, and Retaining too many objects.**

#### **Android Specific Issues**

1. **Native crashes**: Caused by issues in the native layer of an app, often written in C or C++ (e.g., NDK crashes). These involve signals like SIGSEGV, SIGBUS, or SIGABRT, similar to iOS.
2. **Java exceptions**: Unhandled exceptions in the Java layer, such as NullPointerException, ArrayIndexOutOfBoundsException, or IllegalArgumentException, that cause the app to crash.
3. **ANR (Application Not Responding)**: When the main thread is blocked for too long, leading to an ANR error. This is an Android-specific type where the system prompts the user to either wait or force close the app.

#### **React Native specific Issues**

1. **JavaScript exceptions**: Crashes caused by unhandled exceptions in JavaScript, the main programming language for React Native apps. These include syntax errors, type errors, or issues like undefined is not an object.

#### **Flutter Specific Issues**

1. **Dart exceptions**: Crashes caused by unhandled exceptions in Dart code, which is the primary language used for Flutter development. Examples include NoSuchMethodError, StateError, or FormatException.<br>

With Luciq [Crash Reporting](https://www.luciq.ai/product/crash-reporting) there are two ways to have your application report a crash; either automatically or [manually](/ios/setup-luciq-for-ios/setup-crash-reporting/reporting-crashes#manual-crash-reporting). After the crash is sent to your dashboard, you can easily sort and filter for specific crashes.

***

### Reporting Crashes

#### Automatic Crash Reporting

If you enable Crash Reporting, crashes will automatically be reported to and viewable from the crashes page of your Luciq dashboard.

You'll also see the trends covering the previous 7 days, including:

* Crash-free sessions: the percentage of sessions that ran and concluded without any fatal errors.
* Crash-free users: the percentage of users that haven't encountered any fatal errors.
* Crashing sessions: the number of sessions that ran and concluded with a fatal error.
* Affected users: the number of unique users who had one or more sessions that ended with a fatal error.
* Total number of occurrences: by hovering on it, you’ll get a breakdown of the total number of fatal sessions, the total number of OOM sessions, and the total number of non-fatal sessions.

If there is a sharp decline in the crash-free sessions rate, an email will be sent to notify you.

<figure><img src="https://files.readme.io/3a28f303923833c7c31e0e67e64b2d40321a4375300800630afd04343afcb5ce-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**C++ Crashes**

The Luciq SDK also supports and captures C++ crashes.
{% endhint %}

#### Out of Memory Crashes

By default, if Crash Reporting is enabled, Luciq captures OOM crashes, however, the *crashes will not contain a stack trace and will only be captured if they happen while the app is in the foreground.*

#### Force Restarts

Starting from SDK version 11.1.0, Luciq automatically reports Force Restarts. A Force Restart is when a user force terminates your application and re-launches it within 5 seconds, which could indicate performance issues.

{% hint style="info" %}
Please note that Force Restart reports *will not contain a stack trace*.
{% endhint %}

#### App Hangs

Starting SDK version `10.13.0`, Luciq automatically reports App Hangs. An App Hang is captured when the main thread is blocked for more than 3 seconds. App Hangs that last more than 3 seconds are considered severe and are likely to cause user frustration. They are reported along with a stack trace for debugging.

{% hint style="warning" %}
**Warning**

Crash reporting will not function correctly if the device is connected to Xcode. When it is, Xcode catches all exceptions and they will not be sent to your dashboard.
{% endhint %}

### Grouping

When an already existing crash occurs once more for any user, that crash is reported as an occurrence in the original entry. However, in order to calculate whether a crash already exists and needs to be grouped, Luciq generates a fingerprint based on attributes used in the grouping logic.

The default Luciq grouping algorithm uses a mix of the exception and stack trace information. In some cases, you might want to change how the issues are grouped together using custom grouping or fingerprints.

#### Crash-to-Screen Assignment Logic

When a crash occurs during a screen transition, Luciq assigns the crash to a specific screen based on the timing of the `viewDidAppear` and `viewDidDisappear` lifecycle events. The crash will be attributed to the screen name that was last set by the SDK before the crash occurred.

### Crashes List

This section contains a list of all the crashes that have been reported by your application. The title of each crash is usually the most significant line in the stack trace.

<figure><img src="https://files.readme.io/3a28f303923833c7c31e0e67e64b2d40321a4375300800630afd04343afcb5ce-image.png" alt=""><figcaption></figcaption></figure>

Next to each crash in the list, you can find the following details, all of which can be used to sort the crashes:

* **Occurrences**: The number of times this crash has occurred and a bar graph representing its occurrences over the past seven days.
* **Users**: The number of users affected by this crash.
* **Min ver.**: The oldest app version that was affected by this crash.
* **Max ver.**: The latest app version that was affected by this crash.
* **Last seen**: The last time an occurrence of this crash was reported.

You can then filter for crashes that match any of the following criteria:

* App version
* Date
* Device
* OS
* User attributes
* Type
* Status
* Assignee
* Team
* Tags
* Current view
* Experiments<br>


# Crash Report Content

Learn about the data included in Luciq crash reports. Understand the crash log format, metadata, user info, and attachments captured with each crash event.

When using Luciq [Crash Reporting](https://www.luciq.ai/product/crash-reporting), each crash report reaches your dashboard with all the information you need to resolve it.

### Crash Overview

The crashes page in your dashboard lists all of your crash reports. When you select one, it opens the crash overview page where you can find more details about that crash.

<figure><img src="https://files.readme.io/f20e988eae17a929cb866dcf959a5411cd417c4704d9a32f33be55de983957ed-image.png" alt="An example of a crash report."><figcaption></figcaption></figure>

The graph above depicts the number of crashing sessions caused by this particular crash against the days of the previous month. Each line color represents a different app version.

Here are what the details in the crash page cover:

* **Exception & Crash Cause**: The main application frame that caused the crash.
* **Occurrences**: The number of times this crash occurred.
* **Affected Users**: The number of unique users that experienced this crash.
* **Occurrences over time**: Shows the number of occurrences over time for each app version.
* **Max App Version**: The highest version of the app this crash occurred on.
* **App Versions**: A breakdown of the crash occurrences by app version.
* **Devices**: A breakdown of the crash occurrences by device model.
* **OS version**: A breakdown of the crash occurrences by OS version.
* **App status**: A breakdown of the crash occurrences by background or foreground state.
* **Current view**: A breakdown of the crash occurrences by the app view they occurred on.
* **Experiments**: A breakdown of the crash occurrences by different feature flags (if any are specified).
* **User attributes**: A breakdown of the crash occurrences by custom user attributes (if any are specified).

#### Stack Trace

In each crash report, you can find the stack trace of the crash with the most important frames highlighted in blue. These highlighted frames are your application's frames. In order for these crashes to be readable, symbolication is required. More information on symbolication can be found [here](https://docs.luciq.ai/ios/setup-luciq-for-ios/setup-crash-reporting/symbolication). A stack trace is captured and shown per app version within the crash itself.

<figure><img src="https://files.readme.io/f20e988eae17a929cb866dcf959a5411cd417c4704d9a32f33be55de983957ed-image.png" alt="An example of a crash report."><figcaption></figcaption></figure>

### Code Ownership

Sometimes, different stack traces are related to different teams within your organization. In an effort to allow for a more efficient method of assigning crash reports to the related teams, you can set up code ownership rules that use paths and filenames as conditions to allow for a more automated workflow.

You can use code ownership to check for specific paths and filenames, these are then compared against the first non-system frame in the stack trace. For more details on how to set up ownership rules, please check: [Team Ownership](/product-guides-and-integrations/product-guides/automation-and-workflows/team-ownership)

### Crash Variants

**Crash Variants** highlight the different stacktrace paths within a single crash group. Instead of showing only one representative stacktrace per app version, Variants let you see all the distinct execution paths that can trigger the same crash. This helps your team uncover edge cases and debug faster.

Crash Variants are automatically grouped based on **stacktrace similarity.**

#### Where to Find Variants

On the **Crash List page**, you’ll see the number of variants created for each crash.

<figure><img src="https://files.readme.io/c72efb51394fb626cfa22764b16442ef656c9d06029382565dce17e39f86bbaa-image.png" alt=""><figcaption></figcaption></figure>

Selecting a specific variant from the details page will filter the entire page to show only that variant’s data.

1. Stacktrace for the variant
2. Percentage of occurrences for that variant
3. Variant Summary
4. Variant Patterns

<figure><img src="https://files.readme.io/44d3abe185f08c675eb5ef09147bb90fc2b28531f0c88177b1a2e7a93b54dc3e-ios-crash-report-content-2.png" alt=""><figcaption></figcaption></figure>

### Occurrences

Each crash has [occurrences](https://docs.luciq.ai/product-guides-and-integrations/product-guides/crash-reporting/occurrences-content), a unique instance of that crash. With each occurrence, you can find extra details about every instance, such as [Logs](https://docs.luciq.ai/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/report-logs), [Session Profiler](https://docs.luciq.ai/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/session-profiler), and [User Attributes](https://docs.luciq.ai/ios/setup-luciq-for-ios/custom-settings/user-identification/user-attributes). You can go through these unique occurrences by clicking on the **Occurrences** button.

<figure><img src="https://files.readme.io/f20e988eae17a929cb866dcf959a5411cd417c4704d9a32f33be55de983957ed-image.png" alt="An example of a crash report."><figcaption></figcaption></figure>

### Tags

You can add tags to crash reports for filtering and analysis. More details regarding these tags can be found [here](/ios/setup-luciq-for-ios/custom-settings/user-identification/tags).

<figure><img src="https://files.readme.io/dec0d638c3cc1f81eeaca204af2c22b80aa01b36ddcf3235ee7083ec80b06a4c-image.png" alt=""><figcaption></figcaption></figure>


# Occurrences Content

What each crash occurrence carries in Luciq, including default and custom user attributes, user data, and any attachments captured on crash.

When using Luciq [Crash Reporting](https://www.luciq.ai/product/crash-reporting), each occurrence comes with user attributes, user data, and attachments if available.

#### User Attributes

Default attributes as well as any custom user attributes that you set are automatically sent to your dashboard with all crash occurrences.

Default attributes listed in each crash occurrence include:

* Date and time of crash occurrence in UTC
* App version
* Device
* OS version
* App view
* Device location
* Session duration
* Locale
* Screen resolution
* Screen density

More details about how to set custom user attributes can be found [here](/ios/setup-luciq-for-ios/custom-settings/user-identification/user-attributes).

<figure><img src="https://files.readme.io/6e6b41508f7679d50443a81b6e94b62e4c0f0aaf182e63617da83304838fe28f-ios-occurrences-content-1.png" alt="2161"><figcaption></figcaption></figure>

User attributes are listed in each occurrence page of all crash reports in your dashboard.

#### User Data

Custom data can be set and sent with each occurrence. To learn more about setting custom user data, check [here](/ios/setup-luciq-for-ios/custom-settings/user-identification).

#### Attachments

Any custom files added by you can be sent with each occurrence.

<figure><img src="https://files.readme.io/152b5d6058614e8679c03a50c918f3fe2f687431c5a2174f43cf5d8a4672c0fd-ios-occurrences-content-2.png" alt=""><figcaption></figcaption></figure>

### Repro Steps

Repro Steps help you reproduce a crash by displaying your users' actions in each view of your app. With each view, you will find a list of actions that tell you exactly what the user did in that view. More details can be found [here](/ios/setup-luciq-for-ios/custom-settings/privacy-settings/repro-steps).

This is enabled by default depending on your plan.

<figure><img src="https://files.readme.io/194e1be5f6df0bc862324148fa47998b618cde6f40b257c64afc8e4e658b8f11-ios-occurrences-content-3.png" alt=""><figcaption></figcaption></figure>

### Session Profiler

With each crash occurrence, you'll receive a detailed environment profile covering the last 60 seconds before the report was sent. This Session Profiler includes device data like memory load and battery state. This is enabled by default depending on your plan. More details about the information contained in the Session Profiler can be found [here](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/session-profiler).

<figure><img src="https://files.readme.io/faa62b52223161f5ee3eda64204c9dabbcc5923c41108e15960fa9e3455ab9df-ios-occurrences-content-4.png" alt="2161"><figcaption></figcaption></figure>

This is where the Session Profiler is located in each crash occurrence page.

### Logs

A whole host of logs are sent with every crash occurrence. These logs include:

* **Console Logs:** Default logs that are printed to the console when the application is running.
* **Luciq Logs:** Logs with different verbosity levels that you can add manually.
* **User Steps:** Every step the user has taken in the form of log entries.
* **Repro Steps:** Visual user steps prior to the crash occurrence in the form of images and GIFs.
* **Network Logs:** A log of each network request.
* **User Events:** A manual log of actions that a user has taken.

More details regarding logging can be found [here](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/report-logs). Different log types are enabled depending on your plan.

<figure><img src="https://files.readme.io/8e2a7424ee3aa2b95c319b7b5f65e4ef2fee57dbd44efd508b48d86b0c6832a8-ios-occurrences-content-5.png" alt="2161"><figcaption></figcaption></figure>

An example of the expanded logs view in the Luciq dashboard.


# App Hangs

An App Hang happens when your main thread stops responding. Luciq captures hangs of 3 seconds or more, with the tasks that caused them.

When your app hangs it causes user frustration and can lead them to abandon your app altogether.

An App Hang occurs when the main thread of your app fails to respond within a reasonable timeframe. This can happen due to tasks that consume a lot of CPU resources or when blocking I/O operations are performed on the main thread.

If your app remains unresponsive for 3 seconds or more we capture it as an “App Hang”. We detect Hangs automatically out of the box.

### Here is how to view app hangs on the dashboard:

<figure><img src="https://files.readme.io/cb18349695bceda931cb83f70dfa6fe1388a6a66c8b51b3374a736d93b495252-image.png" alt="" width="188"><figcaption></figcaption></figure>

### How are occurrences of App Hangs grouped:

App Hangs are now grouped by the **first non-system frame** in the stack, what we call the **crash cause**. This brings more accuracy to how hangs are clustered and aligns with how Luciq already groups crashes.

Previously, grouping was based on the **top-most screen**, which offered helpful context. This new method focuses more on the underlying cause.

### What to Expect

* **New App Hangs** will follow the new crash cause-based grouping.
* **Existing App Hangs** will stay grouped by screen.
* **No data will be lost**. We’re simply improving how data is clustered to improve your workflow.
* You may see both grouping types side by side in your dashboard after the change.

### FAQ

**Can I re-group old hangs using the new logic?**

* **Not at this time.** We’re only applying the new grouping logic to data going forward to avoid disrupting historical reporting and analysis.

**Will this affect other issue types?**

* No, this change only affects **App Hangs**. Fatal crashes, ANRs, and other issue types already use stacktrace-based grouping.

### What information is available inside the app hang:

* Stack trace
* Flame graphs to help debug the root cause of the app hang
* Patterns to highlight which subset of users have experienced app hangs
* Occurrences view, where you can view every occurrence of the frustrating experience and what has led to it
* Occurrences include the following debugging data:
  * Metadata of the device
  * Session Profiler to know the state of the device for the last 60 seconds before the app hang occurred
  * Repro steps which is a visual step by step reproduction of the session of the user, screen by screen and interaction by interaction on the app up until the user triggered the termination
  * Logs section including console logs and network logs for all API calls that were made during the session

An app hang is a mobile specific metric that is being picked up by Luciq to give you visibility on the frustrating experiences that the users are going through while using the app.

The data included in app hang enables developers to proactively pick up the frustrating experiences and how it happened and what led up to it. This gives them a chance to resolve those issues early on and keep the standard of quality of the app as high as possible.


# Force Restarts

Learn how Luciq detects and reports forced app restarts. Understand how force restarts differ from traditional crashes and how they appear in your dashboard.

A Force Restart occurs when a user forcefully closes an app and immediately restarts it. The underlying frustration that prompts the user to restart the app often goes unnoticed by app developers, as it is not classified as a crash.

Luciq gives you visibility over Force Restarts, which are detected automatically. You can now detect these issues early on, and address them, so you can improve the app quality and retention.

### What does a Force Restart look like?

* User opened the App
* The App was very slow
* User closed the App and opened it again

### How can I see Force Restarts on the dashboard?

Force Restarts are captured by default. You can reach the Force Restarts section as shown in the screenshot below:

<figure><img src="https://files.readme.io/e2b3e20797f42269aa470059b8d2455c93865160a78f5676cf0f26eca2a83259-image.png" alt="" width="188"><figcaption></figcaption></figure>

Similar terminations are grouped together by screen, as shown in the below screenshot.

<figure><img src="https://files.readme.io/739b48c-Screen_Shot_2023-05-18_at_3.53.43_PM.png" alt="User Terminations grouped occurrences grouped together"><figcaption></figcaption></figure>

{% hint style="info" %}
Force Restarts are grouped by events that happened on the same screen.
{% endhint %}

### What data is available in a Force Restart:

* Patterns to highlight which subset of users has triggered the highest number of Force Restarts.
* Occurrences view, where you can view every occurrence of the frustrating experience and what has led to it.
* Occurrences include the following debugging data:
  * Metadata of the device
  * [Session Profiler ](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/session-profiler)to know the state of the device for the last 60 seconds before the Force Restart was triggered.
  * [Repro steps](/ios/setup-luciq-for-ios/custom-settings/privacy-settings/repro-steps) which is a visual step by step reproduction of the session of the user, screen by screen and interaction by interaction on the app up until the user triggered the termination.
  * [Logs section ](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/report-logs)including console logs and network logs for all API calls that were made during the session.

Luciq's Force Restarts feature can be a critical tool for developers seeking to enhance their app's user experience. By notifying you of user frustrations in real-time, it provides you with the opportunity to address issues promptly, maintaining user satisfaction and improving app performance.


# Flame Graphs

Flame graphs visualize the stack traces behind ANRs and App Hangs, showing which functions appear most so you can find the bottleneck.

Flame graphs are visualization tools that help analyze and debug performance issues, such as Application Not Responding (ANR) errors and App Hangs. The display stack traces of an ANR or an App Hang, showing how many times each function appears in the occurrences of the issue. By identifying the functions that are called the most, developers can pinpoint bottlenecks and optimize performance to resolve ANR problems.To analyze a flame graph effectively, look for functions with wide boxes that consume a significant portion of the graph. The closer these boxes are to where the ANR happened, the more likely that these functions indicate potential performance bottlenecks or areas where optimization is needed. By focusing on optimizing these critical functions, you can address ANR issues and improve the overall performance of your applicationYou can [learn about Flame Graphs here.](https://www.brendangregg.com/flamegraphs.html)​

#### How can Flame Graphs help you?

Along with the available debugging data that comes with ANRs and [App Hangs](/product-guides-and-integrations/product-guides/crash-reporting/app-hangs), you can debug those frustrating experiences by seeing the visual aggregations of the stack traces. You can also find the code paths that are frequently associated with the hangs so you can debug them to improve your app’s performance and user experience.Example:Imagine you get a 100 occurrences of an app hang on a certain screen.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1754938049/https%3A%2F%2Ffiles.readme.io%2F68fede69b1cf11974ff06f9118b283151c65261dbc271dc0316085a1d16ad131-product-guides-flame-graphs-1.png" alt=""><figcaption></figcaption></figure>

* There are 100 stack traces associated with the occurrences (1 stack trace per occurrence).
* The goal of the flame graphs is to combine those 100 occurrences.
* The **width** of the bars for each frame would show the *frequency of the frame* to show up in the stack trace.
* The **depth** would show *the order* in which it appeared in the stack trace. (Refer to the below screenshot)

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=1325516117/https%3A%2F%2Ffiles.readme.io%2F5679f6aec83674fdcf0ff0ff93dd7a01d52ff5c68151cf3873672ae0800eeef9-product-guides-flame-graphs-3.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
The width of the stack trace does not show the latency of the frame, but the frequency.This is what the flame graph looks like on the Luciq Dashboard
{% endhint %}

This is what the flame graph looks like on the Luciq Dashboard

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1901080956/https%3A%2F%2Ffiles.readme.io%2F671377cc119c800c9f73f7eda8b85ebca8fc089cee54595e02860b0bf04b0363-product-guides-flame-graphs-2.gif" alt=""><figcaption></figcaption></figure>


# Crashes For Agents

Structured, AI-ready crash aggregations a coding agent can consume without blowing its context window, distilled from every occurrence.

Structured crash context that your AI coding agent can actually consume — without blowing up the context window.

Crashes have thousands of occurrences. Reviewing them one by one is impossible, and feeding raw crash data into an AI agent burns tokens and hits context limits fast. Luciq's engine processes those occurrences and distills them into structured, AI-ready aggregations — the kind agents can consume, reason over, and act on.

**The result:** full crash context available in your IDE, with no dashboard context-switching required.

### How It Works

When you open a crash in the **AI debugging assistant**, you get two modes:

<table><thead><tr><th width="141.4296875">Mode</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Debug mode</strong></td><td>Interactive AI analysis for investigating the crash with follow-up questions.</td></tr><tr><td><strong>Agent mode</strong></td><td>Structured crash aggregations formatted for your AI coding agent. Copy or export as Markdown, then paste directly into Cursor, Claude Code, Copilot, or any agent.</td></tr></tbody></table>

Agent mode is where Crashes for AI Agents lives. It gives your agent the distilled signal it needs — not the raw noise.

<figure><img src="/files/tkJrruquPewCInIiv8l9" alt=""><figcaption></figcaption></figure>

### What's in an Aggregation

Each aggregation covers the full crash picture across all occurrences, structured into sections your agent can parse and act on:

**Crash overview** A top-level summary: total occurrences, affected users, first seen, and last seen — with app version pinpointed.

**Stacktrace** The full symbolicated stacktrace with app frames highlighted. Frames are labeled by origin (app code vs. system), so your agent knows exactly where to look.

**Environment** Distribution breakdowns across device model, OS version, app version, and app status (foreground/background). Your agent sees which configurations are hit hardest — e.g. "67% of occurrences on OS Level 35."

**Device health** Hardware metrics at crash time — memory usage, battery level, storage, and session duration — with mean, p50, and p90 distributions. Surfaces low-memory or long-session patterns that raw stacktraces miss.

**Screen flows** The navigation paths users took before crashing, ranked by frequency. Helps your agent understand the user journey and reproduce the conditions.

**Crash screens** The specific screens active at crash time, with occurrence counts and percentages. Narrows down which view or fragment is in the hot path.

### Privacy & AI Processing

> **No LLM calls are required.** Crashes for AI Agents runs entirely on Luciq's own data processing engine. No crash data is sent to an external model to generate the aggregations.
>
> The one exception is **Screen flows**: if screen flow summarization is enabled for your app, screen names are passed to an LLM to generate path labels. This involves no PII — only screen names (e.g. `MainActivity → ReadingStatsFragment`). This can be disabled per request if your organization has AI or LLM restrictions.

### Availability

Crashes for AI Agents is available across **all platforms** and requires only an **app token** to enable. Contact your success manager or reach out to the team to enable it.

### Related

* [Luciq MCP Server](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) — Pull crash aggregations directly into your coding agent via MCP, no dashboard required.
* [Agent Skills](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/agent-skills) — Methodology that turns crash data into a real fix, end-to-end.
* [AI Debugging Assistant ](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/resolve-agent/ai-debugging-assistant)— Debug mode for interactive crash analysis.


# Application Performance Monitoring

Luciq APM measures app launch times, screen loading, network performance, UI hangs, and key user flows, so you can see what slows apps down.


# Instrumentation

Learn how Luciq instruments your mobile app for performance monitoring. Understand automatic tracking and how to add custom instrumentation.

### Spans

Spans allow you to better understand the root causes of the latencies that occur during your app’s launch and screen loading. This section provides a detailed breakdown of the duration of the platform life cycle stages, network calls, and more information during the app launch and screen loading.

<figure><img src="https://files.readme.io/b582643a66a8296181c6f6d61017c7c099bdb239a32c93efc0e4ddd173726700-ios-apm-instrumentation-1.png" alt=""><figcaption></figcaption></figure>

#### Spans Table Breakdown

|                  |                                                                                                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Span Name        | This shows the stage or request name to identify its source.                                                                                                                               |
| P50              | This is the 50th percentile, which is the maximum latency that 50% of all the occurrences have in the selected time period and is shown in ms.                                             |
| P95              | This is the 95th percentile, which is the maximum latency that 95% of all the occurrences have in the selected time period and is shown in ms.                                             |
| P50 & P95 Change | This shows the change rate of P50 & P95 durations in comparison to the last period based on the selected date filter.                                                                      |
| Average Calls    | This shows how many times the span happened per single occurrence to understand its redundancy better. To get the overall duration of this span, multiply the Average Call by the P50/P95. |
| Frequency        | This is how many times the span happened per all occurrences of the specified metric.                                                                                                      |

#### Supported Span Types

These are the currently supported Span Types:

* View Loading.
* Network.
* App Initialization.
* Database Queries.

#### Database Queries

You'll be able to see the Database queries that happen during your app launch or screen loading with all its details in the spans table and occurrences view.

<figure><img src="https://files.readme.io/828e441ec01ab724572ccb43c911fe593c4b6513be1bb0dc67b36f740150b008-ios-apm-instrumentation-3.png" alt=""><figcaption></figcaption></figure>

### Spans Table in Network Metric

To help you have a better understanding of what's causing the bulk delays inside your network calls, either from the Client, Server, or Network sides, you'll be able to see a detailed breakdown of the latencies caused by the stages/operations that were made to send the network request and receive its response from the server on aggregation and occurrence levels inside the network metric.

You can read more about the Spans table in Network Metric [here](https://docs.luciq.ai/product-guides-and-integrations/product-guides/application-performance-monitoring/instrumentation).

{% hint style="info" %}
If you are using the `EndAppLaunch` or `EndScreenLoading` APIs, Luciq captures the duration from the start of the app launch or screen loading up until the call of any of the APIs.
{% endhint %}


# Metrics & Dimensions

The metrics and dimensions behind Luciq APM, starting with Apdex, so you can compare transaction performance and set improvement targets.

### Apdex

Apdex is an industry-standard metric used to track and measure user satisfaction based on trace latency/response times. This metric provides a standard for you to compare transactional performances, understand which ones may require additional optimization or investigation, and set targets or goals for performance. An Apdex score ranges between 0 and 1; the higher the value, the better:

* Apdex score ≥ of 0.94 equates to \_Excellent \_performance.
* Apdex score ≥ 0.85 and < 0.94 equates to \_Good \_performance.
* Apdex score ≥ 0.7 and < 0.85 equates to \_Fair \_performance.
* Apdex score ≥ 0.5 and < 0.7 equates to \_Poor \_performance.
* Apdex score < 0.5 is considered *Unacceptable* performance.

<figure><img src="https://files.readme.io/9af846b4bc33182f327d32e5f4971ed9bff9163c97c03ad9b8d9ec7f7a81c4bf-image-20250307-141305.png" alt=""><figcaption></figcaption></figure>

For crash-free metrics like Crash-free sessions rate, Crash-free users rate, OOM-free sessions rate, App hangs-free sessions rate & Forced restarts-free sessions rate - we use the following criteria and color coding:

1. **Green**: Indicates a number greater than or equal to 99.5.
2. **Yellow**: Indicates a number between 98 (inclusive) and less than 99.5.
3. **Red**: Indicates a number less than 98.

#### How Is the App Trace Apdex Calculated?

When a trace occurrence is collected, it's flagged based on a pre-defined target (T). An app trace occurrence is considered:

* **Satisfying**: if its duration ≤ T
* **Tolerable**: if its duration > T and ≤ 4T
* **Frustrating**: if its duration > 4T

Then, based on the bucketing explained above, the Apdex score is calculated as follows:

**Total occurrences** = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences\
**Apdex score** = (Satisfying occurrences + 0.5 \* Tolerable occurrences) / Total occurrences

#### How Can You Control a Specific Trace's Target?

By default, the target is set to 2 seconds. However, you can easily change this number from your dashboard by clicking on the action highlighted in the screenshots below.

<figure><img src="https://files.readme.io/1b34a09f629f661d9466e17274670544eca472a7a0c1e4722694132f96d140a8-ios-apm-metrics-dimensions-1.png" alt=""><figcaption></figcaption></figure>

***

### P50

This is the 50th percentile, which is the maximum latency that 50% of all trace occurrences have in the selected time period.

***

### P95

This is the 95th percentile, which is the maximum latency that 95% of all trace occurrences have in the selected time period.

***

### Dissat. Count

The dissatisfied count is a frequency-weighted performance metric to assess the relative magnitude of your trace performance. While you can use Apdex to assess the performance of a trace compared to others, Dissat. Count takes into consideration how frequent/common this trace is and how frustrating it is to your users. This gives you one metric that you can sort your traces with to help you see the overall impact on your users.

#### How Is the Dissat. Count Calculated?

This Dissat. Count is calculated with the following formula:\
Dissat. Count = (1-apdex) \* total occurences = total number of frustrating occurrences + 1/2 tolerable occurrences

***

### Count

This is the total number of occurrences in the selected time period.


# SDK Debugging

Use Luciq's SDK debugging tools to troubleshoot APM issues in your mobile app. Access detailed SDK logs and diagnostic information for performance debugging.

### Debug Mode

In case you would like to view your data on the dashboard without having to wait for the SDK's default 6-hour batching period, you can simply bypass this by **running the app while attached to the debugger**.

Attaching the app to the debugger will sync the data captured by our SDK upon **closing a session and starting a new one**. This can be especially helpful if you are debugging an integration issue or simply trying out APM for the first time.

{% hint style="info" %}
Please note that rate limiting will apply if the number of sessions exceeds 50 per hour. Once this limit is reached, you will have to wait until a full hour has elapsed to be able to keep using Debug Mode. Data collected during this period will not show up on your Dashboard.
{% endhint %}

***

### Logging

APM SDK provides useful console logs in Xcode for you to be able to have visibility into significant events that might be of interest to you. Since not all events are equal in terms of importance or relevance, you can control the level of verbosity of those logs via the following API:

The available levels are:

* **`None`:** disables all APM SDK console logs.
* **`Error`:** prints errors only, we use this level to let you know if something goes wrong.
* **`Warning`:** displays warnings that will not necessarily lead to errors but should be addressed nonetheless.
* **`Info`This** is the default level, and it logs information that we think is useful without being too verbose.
* **`Debug`:** Use this in case you are debugging an issue. Not recommended for production use.
* **`Verbose`:** Use this only if `Debug` was not enough, and you need more visibility on what is going on under the hood. Similar to the `Debug` level, this is not meant to be used on production environments.

{% hint style="info" %}
Please note that each level displays the logs corresponding to its own level as well as all the levels above it. This means that `Info` also includes `Warning` and `Error` logs and so on.
{% endhint %}


# App Launch

Monitor app launch performance with Luciq APM. Track cold, warm, and hot launch times for your mobile app to optimize startup speed and experience.

{% hint style="warning" %}
Starting from **SDK 19.0.0**, we’ve refined how app launch duration is captured for improved accuracy.\
The duration is now measured **from app startup until** `UIApplication.didBecomeActiveNotification` **notification**, which marks the point when the app is fully active and ready to receive user interactions.
{% endhint %}

### Cold App Launch

Luciq automatically measures your **cold app launch** latency, which is the time between when your user launches the app from scratch and when it is responsive and accepting touch events.

It starts with the process start time and stops at the end of the first run loop. This interval accounts for any launch-blocking logic in your code, as well as the time before your app classes are loaded. It includes loading dynamic frameworks and resolving any dynamic references made in the binary.

***

### Hot App Launch

The Luciq SDK automatically measures the **hot app launch** latency, which is the time between the user launching the app from the background and it becoming responsive and accepting touch events.

Hot Launch is transitioning the app from the background to the foreground-active state. We capture the Hot Launch event by observing a `UIApplicationWillEnterForegroundNotification` notification, which is then followed by `UIApplicationDidBecomeActiveNotification`.

***

### End App Launch

In the event that you'd like to define a specific point in time where the app launch can be considered complete, such as when the app is actually interactable, you can use the end app launch API to set that point. You'll then be able to see this data alongside the automatic cold and hot app launches that were captured.

***

### App Launch Apdex

Luciq calculates an Apdex score for your app launch that reflects how satisfying your app launch time is. Your Apdex score ranges between 0 and 1; a higher value means better performance and, hence, a better user experience:

* Apdex score ≥ 0.94 equates to **Excellent** performance.
* Apdex score ≥ 0.85 and < 0.94 equates to **Good** performance.
* Apdex score ≥ 0.7 and < 0.85 equates to **Fair** performance.
* Apdex score ≥ 0.5 and < 0.7 equates to **Poor** performance.
* Apdex score < 0.5 is considered **Unacceptable**.

### How Is The App Launch Apdex Calculated?

When an app launch occurrence is collected, it is flagged based on a pre-defined target (T). An app launch occurrence is considered:

* **Satisfying:** if its duration ≤ T
* **Tolerable:** if its duration > T and ≤ 4T
* **Frustrating:** if its duration > 4T

Then, based on the bucketing explained above, the Apdex score is calculated as follows:

* Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences
* Apdex score = (Satisfying occurrences + 0.5 \* Tolerable occurrences) / Total occurrences

### How Can You Control Your App Launch Target?

By default, the target is set to **0.5 seconds**; however, you can easily change this number from your dashboard by clicking on the action highlighted in the screenshots below.

<figure><img src="https://files.readme.io/9ce338e1c64c01dd74ae950402ba91e6081478cfc3dace12b405d3ff14aa1d09-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Please note that updating your app launch target does **not** affect the already stored occurrences; only future occurrences will be flagged using the new target.
{% endhint %}


# Network

Monitor network requests with Luciq APM. See per-URL Apdex scores, latency, and both client-side and server-side failures for each pattern.

### Network Calls Apdex

Luciq calculates an Apdex score for every network request (URL pattern) in your app. Apdex score ranges between 0 and 1. The higher the value, the closer you are to satisfying a user experience:

* Apdex score ≥ 0.94 equates to **Excellent** performance.
* Apdex score ≥ 0.85 and < 0.94 equates to **Good** performance.
* Apdex score ≥ 0.7 and < 0.85 equates to **Fair** performance.
* Apdex score ≥ 0.5 and < 0.7 equates to **Poor** performance.
* Apdex score < 0.5 is considered **Unacceptable**.

#### How Is the Network Calls Apdex Calculated?

When a network call occurrence is collected, it is flagged based on a pre-defined target (T). A network call occurrence is considered:

* **Satisfying:** if its duration ≤ T
* **Tolerable:** if its duration > T and ≤ 4T
* **Frustrating:** if its duration > 4T or if it fails due to a server-side or client-side error.

Then, based on the bucketing explained above, the Apdex is calculated:

* `Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences`
* `Apdex score = (Satisfying occurrences + 0.5 * Tolerable occurrences) / Total occurrences`

#### How Can You Control a Specific Network Call's Target?

By default, it is set to **0.5 seconds**; however, you can easily change this number from your dashboard by clicking on the action highlighted in the screenshots below.

<figure><img src="https://files.readme.io/e89764232733ce5387e6c323c6ac8f8a4f09027183a6a036bc8d23bf4a4092fe-image.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://files.readme.io/907d773144802847b60c2e99f48e4523a2a0483b0809cb8f62067b050dec7626-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
📘 Please note that updating your response time target does **not** affect the already stored occurrences; only future occurrences will be flagged using the new target.
{% endhint %}

### Network Latency Breakdown

You can see the P50s, P95s, and the frequency of each stage/operation that occurred inside a network group. These are the stages/operations that were made to send the network request and receive its response from the server.

The feature works out of the box without any instrumentation, and the stages are shown inside the Spans table inside the Network metric.

The spans table contains the following stages:

* DNS Lookup
* Connection Handshake
* TLS Connection
* Uploading Request
* Downloading Response
* Server Processing

<figure><img src="https://files.readme.io/17631abeb5264f4e35785b67fabcbee2814b85a1b2e6c71594e3fada1e02b7c8-ios-apm-network-1.png" alt=""><figcaption></figcaption></figure>

You can also see the breakdown and visualize the stages' timeline on an occurrence level inside the occurrences page.

<figure><img src="https://files.readme.io/ce4899c575bf561ff4ea8ea8c2543406c194852a263aea0715b7f3fc4cfca2aa-ios-apm-network-2.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**📘 Minimum SDK Version**

The minimum required SDK version for this feature is v12.1.0.
{% endhint %}

#### URL Patterns <a href="#url-patterns" id="url-patterns"></a>

URL patterns are used to group the relevant network call occurrences and aggregate their numbers. Let's take the following examples:

* `sample.com/list/3/item/1`
* `sample.com/list/3/item/2`
* `sample.com/profile/`

It looks like 1 and 2 are the same request, but asking for different resources. While 3 is an entirely different one. Those three examples result in the following 2 URL patterns:

* `sample.com/list/*/item/*`
* `sample.com/profile/`

**What Are the URL Pattern Components?**

* Plain text: works with exact string matching
* `*`: matches with any URL part. `*` matches with only one part at a time. For example if you are mapping `sample.com/part/variable1/variable2`, your pattern should be `sample.com/part/*/*` and **not** `sample.com/part/*`

**Does Luciq Detect Patterns Automatically?**

Luciq automatically detects numbers and hexadecimal tokens in your URLs and replaces them with `*`.

**Can You Create Custom Patterns?**

If you are using more complex URLs where variable parts may contain plain text and not only numbers and hexadecimal, we recommend defining your custom patterns. Just click on the **"Create URL pattern"** button in your network list.

<figure><img src="https://docs.luciq.ai/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2F6lIBifTCHAMDxXnztiBK%2Fblobs%2FwQIzgRFX4htQG4I6SNpY%2Fd9eb1ef6e8416c2b228f0100f427b61565180ac361b4fbf8a6e0234cbe28044a%2520react%2520native%2520apm%2520network%25201.png&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=759238a4&#x26;sv=2" alt=""><figcaption></figcaption></figure>

Click on the highlighted action to create new URL patterns. URL patterns are used to group relevant network calls.

Here are a few examples:

<table><thead><tr><th width="245.703125">URL pattern example</th><th width="220.30078125">Matches with</th><th>Doesn't match with</th></tr></thead><tbody><tr><td><code>sample.com/part1/part2</code></td><td><code>sample.com/part1/part2</code></td><td><code>sample.com/part1</code></td></tr><tr><td><code>sample.com/part1/*</code></td><td><code>sample.com/part1/part2</code></td><td><code>sample.com/part1/part2/part3</code></td></tr><tr><td><code>sample.com/part1/*/part3</code></td><td><code>sample.com/part1/part2/part3</code></td><td><code>sample.com/part1/part3/part4</code> <code>sample.com/part1/part2/part3/part4</code></td></tr><tr><td><code>sample.com/part1/*/*/part4</code></td><td><code>sample.com/part1/part2/part3/part4</code></td><td><code>sample.com/part1/part2/part4</code> <code>sample.com/part1/part2/part3/part4/part5</code></td></tr><tr><td><code>sample.com/part1/*/*/*</code></td><td><code>sample.com/part1/part2/part3/part4</code></td><td><code>sample.com/part1/part2/part3/part4/part5</code> <code>sample.com/part1/part2/part3</code></td></tr><tr><td><code>sample.com/part1/**/part5</code></td><td><code>sample.com/part1/part2/part3/part4/part5</code></td><td><code>sample.com/part1/part2/part3/part4/part8</code></td></tr></tbody></table>

Some notes to consider while creating your URL patterns:

* Custom URL patterns that you define have higher precedence than the auto-generated ones. If the same call matches with a custom and an auto pattern, it gets grouped with the custom.
* At any point, you can delete a pattern to prevent grouping new calls with it.
* URL patterns shouldn't overlap. Each incoming network call gets grouped with only one pattern. In case of conflict, it gets merged with the newest pattern.

Creating or deleting patterns doesn’t affect your old data that has already been grouped. It only affects the upcoming network requests.


# UI Hangs

Luciq captures UI hangs automatically when your app stops answering input for more than 250 ms, grouped and aggregated by screen name.

Luciq automatically captures any UI hangs happening in your app. A hang is when the app isn't responding to the user's input for more than **250 ms**.

The SDK automatically groups and aggregates data based on the **screen name**, which is the name of your `UIViewController` class. A visit starts when your `viewDidAppear` is called and ends when `viewWillDisapear` is called. Screen visits are referred to as Auto UI Traces. You can create Custom UI Traces as explained [here](/ios/setup-luciq-for-ios/setup-application-performance-monitoring/setup-screen-rendering/custom-ui-traces).

For every screen visit, the SDK reports the duration percentage during which the user encountered UI hangs. Let's take an example:

* A user visits your home screen and stays there for 10000 ms.
* During this screen visit, they encountered three hang incidents. The first one lasted 250 ms, the second one lasted 300 ms, and the last one lasted 400 ms. The cumulative hang duration for this screen visit is `250+300+400=950 ms`. That means the hang % is `950 / 10000 = 9.5%`.

***

### Custom UI Traces

In case you are looking for more control over how occurrences are grouped, you can create your own groups with custom names by leveraging the relevant start and stop APIs. It is worth mentioning that:

* You can run only **one** custom UI trace at a given time; a trace must be ended before a new one can be started.
* The SDK will end any occurrence that wasn't explicitly ended via the end API.

***

### UI Hangs Apdex

Luciq calculates an apdex score for any UI trace in your app, whether it is an automatically detected screen or a custom UI trace that you defined. Apdex score ranges between 0 and 1. The higher the value, the closer you are to satisfying a user experience:

* Apdex score ≥ 0.94 equates to **Excellent** performance.
* Apdex score ≥ 0.85 and < 0.94 equates to **Good** performance.
* Apdex score ≥ 0.7 and < 0.85 equates to **Fair** performance.
* Apdex score ≥ 0.5 and < 0.7 equates to **Poor** performance.
* Apdex score < 0.5 is considered **Unacceptable**.

#### How Is the UI Trace Apdex Calculated?

When a screen visit or custom UI trace occurrence is collected, it is flagged as follows:

* **Satisfying:** if its hang% ≤ 2%
* **Tolerable:** if its hang% > 2 and ≤ 5%
* **Frustrating:** its hang% > 5%

Then, based on the bucketing explained above, the apdex is calculated:

* `Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences`
* `Apdex score = (Satisfying occurrences + 0.5 * Tolerable occurrences) / Total occurrences`


# Screen Loading

Monitor screen loading performance in your mobile app with Luciq APM. Track how fast screens load, identify slow screens, and optimize user experience.

{% hint style="info" %}
📘 The way Luciq captures the loading time of your screens depends on whether they are built using [UI Kit](https://docs.luciq.ai/product-guides-and-integrations/product-guides/application-performance-monitoring/screen-loading) or [SwiftUI](https://docs.luciq.ai/product-guides-and-integrations/product-guides/application-performance-monitoring/screen-loading).
{% endhint %}

### UI Kit

Luciq automatically captures the time it takes for any UI Kit screen to load. This covers the time for any View Controller between `viewDidLoad` and `viewDidAppear`, which includes the following lifecycle methods:

* `viewDidLoad`
* `viewWillAppear`
* `viewWillLayoutSubviews`
* `viewDidLayoutSubviews`
* `viewDidAppear`

#### Spans

Luciq will automatically show spans and operations that occurred during the View Controller loading; these include:

* Network Requests
* Database Queries
* UI Kit Lifecycle methods

#### End Screen Loading

You can also define custom points in each View Controller to manually inform the SDK that screen loading has ended.

***

### SwiftUI

{% hint style="warning" %}
**🚧 Minimum Required SDK Version**

SwiftUI Screen Loading is supported starting iOS SDK v14.0.0
{% endhint %}

To be able to measure the loading time of your SwiftUI views, you need to [instrument your views](https://docs.luciq.ai/ios/setup-luciq-for-ios/integrate-luciq-on-ios/integrate-swiftui) by wrapping them in our `LuciqTracedView` component:

#### Spans

Luciq will automatically show spans and operations that occurred during the SwiftUI view loading; these include:

* Network Requests
* Database Queries
* The `body` span, which represents how long the body object took to load

***

### Screen Loading Apdex

Luciq calculates an Apdex score for your app traces as a way of measuring their performance. An Apdex score ranges between 0 and 1; the higher the value, the better:

* Apdex score ≥ 0.94 equates to **Excellent** performance.
* Apdex score ≥ 0.85 and < 0.94 equates to **Good** performance.
* Apdex score ≥ 0.7 and < 0.85 equates to **Fair** performance.
* Apdex score ≥ 0.5 and < 0.7 equates to **Poor** performance.
* Apdex score < 0.5 is considered **Unacceptable**.

#### How Is the Screen Loading Apdex Calculated?

When an app trace occurrence is collected, it is flagged based on a pre-defined target (T). An app trace occurrence is considered:

* **Satisfying**: if its duration ≤ T
* **Tolerable**: if its duration > T and ≤ 4T
* **Frustrating**: if its duration > 4T

Then, based on the bucketing explained above, the Apdex score is calculated as follows:

* Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences
* Apdex score = (Satisfying occurrences + 0.5 \* Tolerable occurrences) / Total occurrences

#### How Can You Control a Specific Screen's Target?

By default, the target is set to **0.1 seconds**; however, you can easily change this number from your dashboard by clicking on your current threshold in the Apdex section.


# WebViews Screen Loading

Monitor WebView screen loading performance with Luciq APM. Track how fast web content loads inside your mobile app's WebViews for better performance insights.

{% hint style="warning" %}
**🚧 Min Required SDK Version**

WebViews screen loading is supported starting iOS SDK version `12.9.2`
{% endhint %}

WebViews are components that embed web content within native mobile applications. They can be an easy and cost-effective alternative to redesigning some pages from scratch for your mobile app.

Luciq automatically captures the time it takes for your WebViews to load. This includes both the time to load the native screen that hosts the WebView and the time it takes the WebView itself to load, along with its content.

<figure><img src="https://files.readme.io/b410a92bcf6590c793bafd259abfdf5e42fbead244b6f53bbab3af03de03ff3d-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Luciq only detects WebViews that fill the **majority (<75%)** of the native screens they are hosted in. Smaller WebViews, such as banners or small ads will not be captured.
{% endhint %}

{% hint style="info" %}
Luciq only supports the `WKWebView` class of the `WebKit` framework.
{% endhint %}

### Web Vitals

Web Vitals are a suite of user-centric performance metrics developed by Google that measure the loading speed, interactivity, and visual stability of web pages. Google considers **Core Web Vitals** to be the most important of these metrics and should be “tracked by every developer for every website”, including **First Input Delay (FID)**.

**First Input Delay (FID)** measures interactivity or the time between the user first trying to interact with the page and the webpage starting to process that interaction. You can learn more about Web Vitals [here](https://web.dev/articles/vitals#core-web-vitals).

{% hint style="info" %}
For iOS applications, only First Input Delay (FID) Core Web Vital is supported by Apple.
{% endhint %}

Luciq automatically captures First Input Delay (FID) and displays it for all detected WebViews in your application. FID is benchmarked according to Google’s [recommendations](https://web.dev/articles/vitals#core_web_vitals).

<figure><img src="https://files.readme.io/c6f1995-image.png" alt="Times shown on the top of the Screen Loading pages are the P75 of each Core Web Vital"><figcaption></figcaption></figure>

Time shown on the top of the Screen Loading pages is the P75 of First Input Delay (FID)

### Spans

Because total Screen Loading time only tells half the story, you can see in the Spans breakdown how each span contributes to the total screen loading time. The WebViews Loading span shows the time it took to load the WebView and its content, along with the WebView URL for quick identification or debugging. You can also check all the other spans associated with the native components of the screen, such as native loading stages or network calls, so you can find the bottleneck wherever it may be.

<figure><img src="https://files.readme.io/211ea36-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Network calls originating from **within the WebView** will not be captured as they are invisible to the SDK. Only network calls originating from the native app itself will be shown.
{% endhint %}


# Flows

Flows measure how long users take to finish key journeys, with completion and drop-off rates, root causes, and the crashes involved.

{% hint style="warning" %}
**🚧 Minimum Required SDK Version**

Flows are supported starting iOS SDK v13.0.0
{% endhint %}

Flows gives you a **consolidated view of the health and performance of your app's most important flows**. With simple instrumentation, you can measure the time it takes users to complete key user journeys, understand completion and drop-off rates as well as their root causes, learn what crashes are affecting your flows, and gain insight into the various spans and operations occurring within those flows and their performance.

### Features

#### Flow Summary

<figure><img src="https://files.readme.io/31231c03e8713d9131c4164251d60220ea9dec6d7c9a6b39d9f00b1edcab44f6-ios-app-flows-1.png" alt=""><figcaption></figcaption></figure>

Flows provides an “at a glance” summary of your flow’s performance and user behavior:

* **Count**: How many times did your users start this flow or user journey?
* **Completion Rate**: How often do your users complete this flow once they start it?
* **Drop-off Rate**: How often do your users abandon this flow mid-way without seeing it to completion? You can also see a breakdown of the drop-off cause, which could be due to:
  * **Crashes**: Your app crashed, preventing your user from completing this flow.
  * **Forced Restarts**: Your users closed the app only to reopen it within 5 seconds. This often indicates that the user faced a frustrating experience and was forced to restart the app to make it go away.
  * **Abandonments**: Users quitting this flow or your app entirely before reaching the end of the flow. An ongoing flow is considered abandoned if the app stays in the background for more than 60 seconds.
* **Time to completion**: Understand how long this flow usually takes your users to complete with P50 (median) and P95 (slowest occurrence that’s not an outlier) insights.

#### Trends

Understand how your flow’s performance changes over time.

* Track your flow’s **Apdex** and **P50** to detect performance regressions,
* Analyze **throughput** to understand user traffic on this flow, or
* View the **distribution** graph to get a wider view of this flow’s performance.

#### Spans

Learn what spans are **most impacting your flow’s** completion time or **introducing latency**.

{% hint style="info" %}
**📘 iOS supports the following span in Flows:**

* Network Requests
* Database Queries
  {% endhint %}

You can learn more about spans and how they can help you identify the root cause of performance issues [here](https://docs.luciq.ai/product-guides-and-integrations/product-guides/application-performance-monitoring/instrumentation).

#### Crashes

Identify the **top crashes affecting users on that flow** that might be causing them to drop off.

#### Patterns

**Understand how your flows are performing across different dimensions**: App versions, Devices, OS versions, etc., or custom flow attributes that you can set yourself, allowing you to **narrow down into segments of your user base** to identify and debug issues.

### Instrumentation

To create a flow, you just need to **define a start and an end for that flow** in your code. Luciq automatically captures data, health, and performance data between those two points. All instances of flows with the same name are aggregated on your dashboard.

{% hint style="info" %}
**🚧 Rules around creating Flows:**

* Flows are **uniquely identified by their name**.
* You **can** run several flows with **different names** in parallel.
* You **can’t** run different instances of the **same flow** in parallel.
* You **can start a flow** while your app is in the **background**.
* You **can’t end a flow** while your app is in the **background**.
* You can create **up to 10,000 unique Flows**.
  {% endhint %}

{% hint style="info" %}
**📘 Rules and Limitations for Custom Flow Attributes:**

* The attribute's **key** can be up to **30 characters**.
* The attribute's **value** can be up to **60 characters**.
* **Avoid** adding any of these **special characters** \[, (, ), =, {, }, <, >, /, , ] (commas not included) as they will be replaced with \_.
* You can add up to **5 unique custom attributes to each flow instance.**
* You can have up to **20 unique custom attribute keys across all instances of a flow.**
* The attribute's key **can't** be an **empty string** or **null**.
* The attribute's value **can't** be an **empty string**.
* You can call the API twice with the same key to override a previous value.
  {% endhint %}


# Flows Apdex

How Luciq scores a Flow with Apdex, how satisfying, tolerating, and frustrating occurrences are classified, and how drop-offs are counted.

Luciq calculates an Apdex score that reflects the performance of your Flows. An Apdex score ranges between 0 and 1; the higher the value, the better the performance:

* Apdex score ≥ 0.94 equates to **Excellent** performance.
* Apdex score ≥ 0.85 and < 0.94 equates to **Good** performance.
* Apdex score ≥ 0.7 and < 0.85 equates to **Fair** performance.
* Apdex score ≥ 0.5 and < 0.7 equates to **Poor** performance.
* Apdex score < 0.5 is considered **Unacceptable**.

The following color-code criteria are also applied:![](https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-715995512/https%3A%2F%2Ffiles.readme.io%2F4759f658f2055665c9eed93fbd1bc9caedfb037ed6df00e89abf08daddbc3122-flows-apdex-1.png)

#### How is the Flow Apdex calculated?

There are two factors that affect the apdex of a Flow: flow latency and drop-offs.

**Flow Latency**

When a completed occurrence of a Flow is collected from the SDK, **it is flagged based on the target duration (T) for that Flow**. A Flow occurrence is considered:

* **Satisfying**: if its duration ≤ T
* **Tolerable**: if its duration > T and ≤ 4T
* **Frustrating**: if its duration > 4T

**Drop-Offs**

If the occurrences were a drop-off (and therefore don’t have a completion time), we then consider the reason for the drop-off. A Flow occurrence is considered:

* **Frustrating**: if the drop-off reason was a Crash, User Termination, Fatal ANR, or Fatal App Hang
* **Neutral**: if the drop-off was due to user abandonment or the flow timing out in the background.

📘 Neutral occurrences **don’t affect** the Apdex of a Flow in any way.

**Apdex Calculation**

The Apdex score for the entire flow is then calculated as follows:

* **Apdex score = (Satisfying occurrences + 0.5\* Tolerable occurrences) / Total occurrences**
  * Where: Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences

For example, if a Flow had 5 satisfying occurrences, 3 tolerable occurrences, 2 frustrating occurrences, and 1 neutral occurrence, we discard the neutral occurrence and calculate the Apdex as follows:Apdex score = (5 satisfying occurrences + 0.5 \* 3 tolerable occurrences) / 10 total occurrences = 0.65

#### How to control a specific Flow's target?

By default, the target for all Flows is set to **0.5 seconds**; however, you can easily change this number from your dashboard by **clicking on the target highlighted next to each Flow or inside the flow details page**.![](https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=1602412625/https%3A%2F%2Ffiles.readme.io%2F3e3bb91659776cc5d05ac698ad6726a2069baf301a6423be794144777610f149-image.png)![](https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1825887851/https%3A%2F%2Ffiles.readme.io%2F2f9ec1ac1dbca4ab7850d5aebf65d6a74c00c5df6d78a71ec7b76bc0465452fe-image.png)


# Screen Rendering

Find the spans that correlate with slow and frozen frames on a screen, so you can pinpoint the likely root causes of rendering problems.

Identify **spans that most commonly correlate** with Slow and Frozen Frames. These spans are likely root causes of rendering issues on this screen.

![](/files/d036066aa10e96f710cc316d13f966970babd95a)

The suspect spans table shows for every span captured on this screen:

* **Frozen Frames %:** What percentage of the occurrences of this span correlated with a frozen frame.
* **Slow Frames %:** What percentage of the occurrences of this span correlated with a slow frame.
* **Change:** How each of those percentages changed in the selected date period compared to the previous period of the same length.

Learn more about spans and how they can help you identify the root cause of performance issues in the documentation: <https://docs.luciq.ai/docs/android-apm-instrumentation>

#### Patterns

Understand your rendering performance across different dimensions: App versions, Devices, OS versions, etc., allowing you to narrow down into segments of your user base to identify and debug issues.

#### Occurrence View

Navigate to the occurrences page of any screen to view individual screen visit occurrences in full detail.

* View metadata about each occurrence, including frozen and slow frames %, device and app information, and other parameters.
* View a detailed span timeline of the complete screen visit, highlighting frozen frames (in red) and slow frames (in yellow).
* Hover over any frozen or slow frame to highlight that frame’s suspect spans, a likely root cause of this rendering delay.

![](/files/929bacbae8e96996a32f21cc5655dd7ce8e7eb19)

### Apdex Calculation

Luciq calculates an Apdex score that reflects the rendering performance of every screen or custom UI trace of your application. An Apdex score ranges between 0 and 1; the higher the value, the better the performance:

* Apdex score ≥ 0.94 — Excellent
* Apdex score ≥ 0.85 and < 0.94 — Good
* Apdex score ≥ 0.7 and < 0.85 — Fair
* Apdex score ≥ 0.5 and < 0.7 — Poor
* Apdex score < 0.5 — Unacceptable

The following color-code criteria is also applied:

![](/files/31afa14e74e5e1a1db702471cbf4f26ebac0dee3)

#### How is the Screen Rendering Apdex Calculated?

Every screen visit is categorized based on the frozen frames % and slow frames % of that occurrence using the logic shown below:

![](/files/79fa36fcf1efb37ebf35a045dec334a56d161447)

Following that logic, a Screen rendering occurrence is considered:

* **Satisfying:** if it has NO frozen frames & ≤ 10% Slow frames.
* **Tolerable:** if it has NO frozen frames & ≤ 50% Slow Frames.
* **Frustrating:** if it has ANY frozen frames & > 50% Slow Frames.

#### Screen Group Apdex Calculation

The Apdex score for the entire Screen Rendering group is then calculated as follows:

Apdex score = (Satisfying occurrences + 0.5 \* Tolerable occurrences) / Total occurrences

Where Total occurrences = Satisfying occurrences + Tolerable occurrences + Frustrating occurrences

Example occurrences:

{% stepper %}
{% step %}
**Occurrence A**

5% Frozen Frames, 0% Slow Frames → Frustrating
{% endstep %}

{% step %}
**Occurrence B**

0% Frozen Frames, 30% Slow Frames → Tolerable
{% endstep %}

{% step %}
**Occurrence C**

0% Frozen Frames, 6% Slow Frames → Satisfying
{% endstep %}
{% endstepper %}

Apdex for that screen = (Satisfying occurrences + 0.5 \* Tolerable occurrences) / Total occurrences\
\= (1 + 0.5 \* 1) / 3 = 0.5


# Session Replay

Session Replay records and visualizes user sessions, capturing screen changes, interactions, and logs to show exactly what users experienced.

### **Session Replay**

Allows you see your app through your users' eyes by recording and visualizing user sessions, capturing screen changes, interactions, logs, and stability and performance issues.

It provides full context of the user experience, enabling teams to debug issues faster, reduce support back-and-forth, and proactively identify user frustration. Whether you're reproducing bugs, investigating negative feedback, or responding to support tickets, Session Replay shows you exactly what users experienced with complete visual context.

<div data-with-frame="true"><figure><img src="/files/ED5brvXRU4Zp4gx2Luy5" alt=""><figcaption></figcaption></figure></div>

### Features

### Sessions List

Sessions are sorted chronologically by default. You’ll see the following information for each session:

1. **User Data:** The end-user's email/name and ID. By default, it shows Luciq's UUID, but you can override it with your internal IDs using the [User Identification API](/ios/setup-luciq-for-ios/custom-settings/user-identification).
2. **Session Type**: Any session that ends with a fatal crash or OOM is flagged as a crashing session. Any session that is affected by an App hang or a Force Restart is flagged as a frustrating session. Otherwise, a bucket is defined based on all the other metrics and occurrences (app launch, app traces, network, and UI hangs) that happened within the session. More examples can be found [here](/product-guides-and-integrations/product-guides/application-performance-monitoring/metrics-and-dimensions#apdex).
3. **Session Issues**: The issues that happened during the session. These include crashes, app hangs, slow app launches, screen loadings, or execution traces as well as Force Restarts. This can help you identify sessions that require your attention.
4. **Duration**: The length of the replay.
5. **App Version**: The app version the user was on during the session.
6. **OS**: The OS version the device was on during the session.
7. **Date**: of when the session has occurred.

### Session Details

With each session, Luciq captures detailed information about the session itself. Below is the list of session related details that Luciq captures:

1. User ID
2. Date
3. Session duration
4. App version
5. Device
6. OS version
7. User ID (if provided)
8. Username (if provided)
9. Locale
10. Bundle ID
11. Device location
12. User attributes
13. Feature flags

#### Timeline

A timeline from the beginning to the end of the session which visually highlights performance and stability issues that happen over the course of the replay. Users can easily move to the point on the timeline where they want to start debugging. It also visually shows the duration of the session.

#### Screenshots

Screenshots taken throughout the session whenever there is a change in the UI (The SDK captures a screenshot with every screen transition).

#### Logs

A whole host of logs are sent with every session. These logs include:

1. **Luciq Logs**: Logs with various verbosity levels that you can add manually.
2. **User Steps**: Log entries detailing each step the user has taken.
3. **Network Logs**: Records of all network requests.
4. **Crashes**: If Luciq’s Crash Reporting is enabled, you'll see all crash metrics within the session, including fatal crashes, app hangs, ANRs, and non-fatal errors.
5. **Performance**: If Luciq's APM is enabled, you'll see all APM metrics for the session, including app launches, screen loading, flows, and networks.

## Session Definition

A session starts when the app is brought into the foreground and ends when it moves to the background or when it's terminated either by the OS or the user.

{% hint style="info" %}
**Foreground Only**

We currently support session replay for foreground sessions only.
{% endhint %}

## User Identification

In order to find sessions by searching for specific users, make sure you use our user identification API. More details about the user identification API can be found [here](/ios/setup-luciq-for-ios/custom-settings/user-identification).

## Constrained Devices

On Android, the SDK automatically classifies devices into performance tiers and reduces data collection on lower-tier devices — on low-end devices Session Replay is limited to Navigation mode. This is SDK-wide behavior affecting all Luciq products; see [Device Performance Classification](/product-guides-and-integrations/product-guides/device-performance-classification) for the classification criteria, emulator behavior, and what gets reduced per tier.


# Video-Like Replay

Video-Like Replay turns session recordings into smooth playback. Set quality profiles and capture frequency to balance detail and storage.

### Overview <a href="#overview" id="overview"></a>

Video-like Session Replay transforms your session recordings from simple screen-by-screen captures into smooth, video-like playback. This feature gives you complete visibility into user behavior by:

* **Capturing more frequent screenshots** -> See every interaction, not just screen transitions
* **Providing configurable quality profiles** -> Balance visual fidelity with storage efficiency
* **Supporting multiple capture modes** -> Choose the right approach for your debugging needs

{% hint style="info" %}
***Minimum SDK Version:** iOS SDK 19.2.0+*
{% endhint %}

### Capturing Modes <a href="#capturing-modes" id="capturing-modes"></a>

Control **when** screenshots are captured using the ***Capturing Mode*** API.

#### **1 - Navigation Mode (Default)**

Captures screenshots only when users navigate between screens. This is the default behavior, providing the lowest overhead.

**Best for:** Apps where screen transitions are the primary user flow

***

#### **2 - Interactions Mode**

Captures screenshots on screen navigation **and** user interactions. Includes debouncing to prevent excessive captures.

**Best for:** Debugging user interaction issues, understanding how users interact with complex screens

**Supported Interactions**

| **UIKit**   | **SwiftUI** |
| ----------- | ----------- |
| Tap         | Tap         |
| Double Tap  | Double Tap  |
| Long Press  | Long Press  |
| Force Touch | Force Touch |
| Swipe       | Swipe       |
| Pinch       | Pinch       |
| Scroll      | Scroll\*    |

***

#### **3 - Frequency Mode**

Captures screenshots at a fixed time interval for true video-like playback. Also captures on screen navigation.

**Best for:** Full video-like replay experience, debugging visual issues, understanding complete user journeys

***

### Screenshot Quality <a href="#screenshot-quality" id="screenshot-quality"></a>

Control the **visual quality** of captured screenshots. Higher quality provides better visuals but uses more storage.

#### **Quality Profiles**

| **Profile**          | **Compression**                | **Use Case**                                  |
| -------------------- | ------------------------------ | --------------------------------------------- |
| **High**             | 50% quality (WebP)             | Detailed debugging, visual regression testing |
| **Normal** (Default) | 25% quality (WebP)             | Balanced quality and storage                  |
| **Greyscale**        | 25% quality + grayscale (WebP) | Maximum storage efficiency, text-heavy apps   |

#### **Estimated Screenshots per Session**

Based on the default 1MB session screenshot limit:

| **Quality Profile** | **Approx. Screenshots per Session** |
| ------------------- | ----------------------------------- |
| High                | \~62 screenshots                    |
| Normal              | \~104 screenshots                   |
| Greyscale           | \~130 screenshots                   |

> ***Tip:** For video-like replay at 1 FPS with Normal quality, you can capture approximately 1-2 minutes of continuous session activity.*

***

### SwiftUI Considerations <a href="#swiftui-considerations" id="swiftui-considerations"></a>

For SwiftUI apps, most interactions are captured automatically. However, **scroll detection in SwiftUI requires manual gesture handling** if you want scroll events to trigger captures in Interactions mode.UIKit views embedded in SwiftUI work as expected with full interaction detection.

*→ Please refer to our SwiftUI integration docs for more details* [SwiftUI Integration for iOS](https://docs.luciq.ai/ios/setup-luciq-for-ios/integrate-luciq-on-ios/integrate-swiftui)*.*

***

### Privacy & Masking <a href="#privacy-and-masking" id="privacy-and-masking"></a>

Video-like Session Replay **respects all existing privacy** configurations:

* **Auto-masking** continues to work across all capturing modes
* **Private views** are masked in all captured screenshots

***

### Migration Guide <a href="#migration-guide" id="migration-guide"></a>

If you’re upgrading from a previous SDK version:

1. **No breaking changes** -> Default behavior remains Navigation mode with Normal quality
2. **Opt-in feature** -> Video-like replay must be explicitly configured
3. **Repro Steps unaffected** -> Bug and Crash report screenshots continue to use Navigation mode and Normal quality


# Device Performance Classification

How Luciq classifies Android devices into performance tiers and how data collection is reduced on constrained devices.

On Android, the SDK automatically classifies devices into performance tiers based on hardware capabilities such as CPU, RAM, and memory class. On lower-tier devices, the SDK reduces data collection to protect app performance and prevent issues like ANRs or out-of-memory errors.

This classification is SDK-wide — it applies to all Luciq products, not just Session Replay. See [What Gets Reduced](#what-gets-reduced) for how each data type is affected.

## Device Tiers

| Tier    | Dashboard Label | Data Collected |
| ------- | --------------- | -------------- |
| LOW     | Low-end         | \~30%          |
| AVERAGE | Mid-range       | \~60%          |
| HIGH    | High-end        | 100%           |

## How Devices Are Classified

Classification runs once per SDK start, based on the device's hardware capabilities.

A device is classified **Low-end** if **any** of the following is true:

| Signal          | Threshold                              |
| --------------- | -------------------------------------- |
| Android version | Below 5.1 (Lollipop MR1, API level 22) |
| CPU cores       | 2 or fewer                             |
| Total RAM       | 2 GB or less                           |

Otherwise, a device is classified **Mid-range** if **any** of the following is true:

| Signal               | Threshold                                                    |
| -------------------- | ------------------------------------------------------------ |
| Android version      | Below 7.0 (Nougat, API level 24)                             |
| Per-app memory class | 160 MB or less                                               |
| CPU                  | Fewer than 8 cores, or combined max CPU frequency ≤ 2055 MHz |

Everything else is classified **High-end**.

{% hint style="info" %}
These thresholds may be tuned in future SDK versions, and specific device models can be re-classified remotely by Luciq.
{% endhint %}

### Emulators

There is no emulator-specific rule — emulators are classified using the same hardware signals as physical devices:

* A default Android Virtual Device (2 GB RAM) is classified **Low-end** because its RAM is at the 2 GB threshold.
* An AVD configured with more than 2 GB of RAM and more than 2 vCPUs escapes the Low-end tier and is typically classified **Mid-range** (emulators generally don't expose readable CPU frequency information, so they don't qualify for High-end).

If you don't want your development, staging, or CI emulators classified as low-end, configure the AVD with at least 3 GB of RAM and 4 vCPUs.

## What Gets Reduced

When the SDK reduces data collection on a constrained device, the following are affected:

| Data Type                | Low-end              | Mid-range       | High-end        |
| ------------------------ | -------------------- | --------------- | --------------- |
| Session Replay           | Navigation mode only | Configured mode | Configured mode |
| Session Profiler         | \~18 seconds         | \~36 seconds    | Full duration   |
| Network and Console Logs | 30% retained         | 60% retained    | Full            |
| Repro Steps              | 30% retained         | 60% retained    | Full            |

## What You'll See on the Dashboard

When viewing a bug report, crash occurrence, or session from a constrained device:

* A **device classification badge** appears in the details panel showing the device tier and the percentage of data collected (e.g., "Low-end · 30% data collected").
* If session profiler data, repro steps, or screenshots are unavailable due to data reduction, the dashboard displays a message explaining that data collection was reduced on that device to protect app performance.

{% hint style="info" %}
Device classification is currently available on Android only. This behavior is automatic and does not require any configuration. If you need classification disabled for a specific app (for example, a staging build), contact Luciq support.
{% endhint %}


# Funnels

Funnels show where users drop off and why, built from events, screens, or network calls. They work retroactively, with no release needed.

Funnels let Mobile teams measure where users drop off and why. Customers can Build a funnel directly from the dashboard using any combination of user events, screens, or network calls. Because Luciq already captures this data continuously, funnels work retroactively, you don’t need to ship a new release or wait days to collect data, just define the funnel and see results immediately.

For every step in the funnel, Luciq surfaces the technical issues that affected users who dropped at that step (crashes, network errors, slow screens, frustration signals) alongside session replays of the dropped users themselves. The result is a clear understanding your user journeys and what to fix exactly to enhance funnel conversion rate

***

### Creating a Funnel

#### 1. Open the Funnels page

Navigate to **Funnels** from the left sidebar. You'll see a list of all existing funnels with their key metrics at a glance: user count, conversion rate, drop-off rate, and median completion time.

#### 2. Click "Create funnel"

Click the **Create funnel** button in the top-right corner to open the funnel builder.

#### 3. Name your funnel

Give your funnel a descriptive name that reflects the user journey you're analyzing, for example: `Checkout Flow` or `Onboarding – New Users`.

#### 4. Define your funnel steps

Each step represents a point in the user journey. For every step, choose a **step type** and then select the specific event:

| Step type   | What it tracks                                    |
| ----------- | ------------------------------------------------- |
| **Events**  | Custom user events you can send using an API      |
| **Screens** | Screen visits, identified by screen/activity name |
| **Network** | Network endpoints fired                           |

You can mix step types within the same funnel. For example, a checkout funnel might combine a screen visit (`HomepageActivity`) with a network call (`POST /api/checkout`) and a custom event (`purchase_confirmed`).

#### 5. Create

Click **Create** to save your funnel. Luciq will immediately process historical session data and display results.

***

### Understanding the Funnel Details Page

Clicking on any funnel opens its details page, which is organized into three sections.

#### Summary

The summary card gives you an at-a-glance health check of the funnel:

* **Count:** total number of users who entered the funnel
* **Conversion:** percentage who completed all steps
* **Drop-off:** percentage who did not complete the funnel
* **Sessions breakdown:** a segmented bar showing how drop-off sessions are classified:
  * 🔴 **Crashing sessions:** user experienced a crash
  * 🟠 **Frustrating sessions:** session was slow or encountered errors
  * 🔵 **Tolerable sessions:** some friction but within acceptable thresholds
  * 🟢 **Satisfying sessions:** smooth experience, might be voluntary abandonment
* **Median completion time:** p50 time to complete the funnel end-to-end

#### Trends

Two charts give you a time-series view of funnel health over your selected date range:

* **Conversion rate:** how your conversion rate has moved over time, useful for spotting regressions after a release
* **Funnel volume:** the number of users entering the funnel over time

#### Funnel Steps

A bar chart showing the number of users at each step. The drop-off percentage between consecutive steps is shown between each bar, making it immediately clear which step is the biggest source of abandonment.

Clicking on any step bar opens the **Step details panel**.

#### Issues

A list of all issues that happened from the first step till the last step. This includes all the issues captured by our SDK like crashes, hangs, force restarts, visual issues, bugs, networks..

#### Session Replay

A list of all session replays that happened throught the funnel, you can filter the page by frustrating sessions and look at the replays

#### Dimensions Table

The **Dimensions** table breaks down funnel performance by segment, helping you spot whether a drop-off is widespread or isolated to a specific slice of your users. Switch between three tabs (App version, Device or OS Version)

***

### Debugging Drop-offs in a specific step

When you click on a specific step in the funnel chart, a **Step details panel** slides in from the right. This panel surfaces the all the issues that occurred during that step, the performance and stability problems most likely responsible for the drop-off.

The Panel also surfaces the session replays for the users that dropped off before this step in the funnel

***

### Using Funnels with the Luciq MCP

The Luciq MCP gives your AI assistant full read and write access to Funnels, so you can analyze and manage them through natural language without opening the dashboard.

#### What you can ask

**Understanding drop-offs:**

> "Where are users dropping off in the Checkout funnel?"

> "Why are users dropping off at Step 2?"

> "What issues are impacting conversion in the Onboarding funnel?"

**Getting fix recommendations:**

> "What should I fix to improve conversion in the Checkout funnel?"

The MCP will pull the relevant issues, session data, and performance signals automatically, and surface the highest-impact problems to address.

**Managing funnels:**

> "Create a funnel called Booking Flow with these three steps: BookingScreen, POST /api/booking, booking\_confirmed event"

> "Update the Checkout funnel to add a step for the payment screen"

> "Delete the test funnel"

#### Why this matters

Traditional funnel tools require you to open the dashboard, navigate to the funnel, identify the drop-off step, and then separately search for related issues. With the Luciq MCP, your AI assistant can complete this entire investigation in a single conversation, from spotting the problem to surfacing the fix, giving your team faster time-to-resolution on conversion issues.


# In-app Surveys

Collect feedback straight from your users with Luciq In-app Surveys. Covers SDK integration, survey types, targeting, and reading answers.

{% hint style="info" %}
**Integrating Luciq**

To be able to use Luciq's In-App Surveys product, you must first [integrate the SDK](/ios/setup-luciq-for-ios/integrate-luciq-on-ios).
{% endhint %}

The best and most effective way to collect data about your application and evaluate it is by directly asking the people who are using it. Feedback from your actual users will help answer a lot of questions and guide you to make decisions. The good thing is, setting up these in-app surveys from your dashboard, controlling them in your app, and accessing their results can be done easily.

Below is the breakdown of Luciq's Surveys & Announcements:

1. [**Creating Surveys**](/product-guides-and-integrations/product-guides/in-app-surveys/creating-in-app-surveys)\
   Learn how to create a new survey or announcement as well as what the different types of templates are from this section.
2. [**Targeting Surveys**](/product-guides-and-integrations/product-guides/in-app-surveys/targeting-surveys)\
   Different users need different surveys. Learn how to show specific surveys to specific users at just the right time in this section.


# Creating In-app Surveys

Create Custom, NPS, and App Rating surveys in Luciq, choose your question types, and decide who sees each survey and when it appears.

You can create In-app surveys to collect feedback from your users. There are three different types of In app Surveys that you can create:

* [​Custom Survey](https://docs.luciq.ai/product-guides-and-integrations/product-guides/in-app-surveys/creating-in-app-surveys#custom-survey): create a set of customized questions using different question types
* [​NPS Survey](https://docs.luciq.ai/product-guides-and-integrations/product-guides/in-app-surveys/creating-in-app-surveys#nps-survey): find out whether your users would promote your application or not, then have them rate your application
* ​[App Rating](https://docs.luciq.ai/product-guides-and-integrations/product-guides/in-app-surveys/creating-in-app-surveys#app-rating): ask your users if they like your app. If they answer yes, they'll be prompted to rate your application

<figure><img src="/files/NvtpTjFbAzfBLMuCm5Cu" alt=""><figcaption></figcaption></figure>

### Surveys

You can choose between three types of surveys when creating a new one: **Custom Survey**, **NPS Survey**, and **App Rating**.

#### Custom Survey

Create your own custom surveys with any number of questions displayed to your user one by one, sequentially. You can select between three types of answers:

* **Text Field**: The user must answer the question by typing their response in a text field.
* **Multiple Choice**: The user must answer the question by choosing one of any number of answers you have defined in your dashboard.
* **Stars**: The user must answer the question with one to five stars.

This type of survey can use both manual and automatic [targeting](broken://spaces/AM8wNfllcup3GnWJ1WtW/pages/dWDflIEbr2RW8pw1AISZ).

<figure><img src="https://files.readme.io/db609f4c5a7480116b41aec7812089900e06b18a87cd09aa8f2bd789d3bb23ff-image.png" alt=""><figcaption></figcaption></figure>

#### NPS Survey

The default first question in an NPS Survey is "How likely are you to recommend `<Your App Name>` to a friend or colleague?" The user must reply on a scale of 1-10, least to most likely.

Depending on the rating given by the user, they will be shown one of three possible follow-up questions:

* **User rating is 9 or 10 (Promoter)**: The user will be asked for feedback about how to improve the app. You will also have the option to allow the users to rate the application while creating the survey. A link is then generated to your app on the App Store. The user will be asked to rate your app. If they accept, they will be redirected to the App Store link. If your app is in beta, or if your app is not available on the App Store, the user will instead be asked to submit their survey.
* **User rating is 7 or 8 (Passive)**: The user will be asked for feedback about how to improve the app. You will also have the option to allow the users to rate the application while creating the survey.
* **User rating is less than 7 (Detractor)**: The user will be asked for feedback about how to improve the app.

This type of survey can use both manual and automatic targeting.

From your dashboard, you can customize the text of any question in an NPS Survey.

<figure><img src="https://files.readme.io/e77ea6c576080d1cbfc8d44fd2b31f5faf2bf42e3aaa593d75d01ec956b54814-image.png" alt=""><figcaption></figcaption></figure>

#### App Rating

This template is used to identify happy users and ask them to rate your app.

First, your users will see an alert, native to the OS, which asks if they are satisfied with the application.

* If the user answers yes, they will be asked to rate the application on the App Store in another alert automatically. If the application is in beta mode, the user will not be asked to rate on the App Store.
* If the user answers no, they will be asked how the application can do better in a survey format similar to the one in the custom survey section.

This type of survey can use both manual and automatic targeting.

You can set the frequency for how often this message shows. By default, it is set to every 30 days.

{% hint style="info" %}
**Changing the Alert Text**

During the creation process, you can edit any of the text that will be in the alerts shown to the users.
{% endhint %}

<figure><img src="https://files.readme.io/b327d876d9c4ada50096a787014028731fcd26667d2c893e45c1ededa3cf5939-image.png" alt=""><figcaption></figcaption></figure>

### Survey Localization

You can display your surveys and announcements to your users in their language. To do this, you only need to add the locale when creating the survey, then add the questions in that language.

<figure><img src="https://files.readme.io/230fa410440ab9e8ab6519bf67bc85c7900bdd0cf547ea031f8e7458d2a07be5-image.png" alt=""><figcaption></figcaption></figure>

### Editing Survey Locales

There are some rules that should be adhered to when adding and editing locales and languages for surveys and announcements.

#### Default Language

Every survey and announcement you create must have a default language. This default language is used when no locale is set and you don't support the device's current locale, so make sure you set the right default language. By default, the default language is set to English.

For each locale you add, a new tab will be added in the dashboard from where you can modify the text that will be displayed for that locale. Other changes to the survey, like adding or removing questions, can only be done from the default locale tab.

<figure><img src="https://files.readme.io/c2d4703faae66b9a428d2eb04fb1e1aeaf913ac631494b81559d5c72e8071d6f-image.png" alt=""><figcaption></figcaption></figure>

#### Determining Shown Locale

The SDK determines which survey language is shown based on a few priorities. If you use the [set locale API](broken://pages/CME4Zg7eENYbPIVGmMAA), this locale will take precedence over the device locale so the survey will be shown in the locale you set through the API. Otherwise, surveys will be displayed in the language that matches the device locale. In case you don't support this locale, the survey will use the default language.

To summarize, in order of priority:\
1 - Locale set through API\
2 - The device locale\
3 - Default locale set through the dashboard (if device locale isn't supported)

#### Supported Locales

Currently, most locales supported by the SDK are supported in the survey localization, with more locales to be added soon. Below is the list for supported locales.

```
- English en
- Arabic ar
- Czech cs
- Danish da
- German de
- Spanish es
- French fr
- Italian it
- Japanese ja
- Korean ko
- Dutch nl
- Norwegian (no, nb) --> nb-NO
- Polish pl
- Portuguese Brazil pt-BR
- Portuguese Portugal pt-PT
- Russian ru
- Slovak sk
- Swedish sv
- Turkish tr
- Chinese Simplified (zh-hans, zh-CN) --> zh-CN
- Chinese Traditional (zh-TW,  zh-hant-TW, zh-Hant) --> zh-TW
- Hindi hi
- Greek el
- Finnish fi
- Estonian et
- Romanian ro
- Vietnamese vi
```

{% hint style="info" %}
**Backwards Compatibility**

Multiple locale surveys will appear starting from version 8.3. If you're targeting older SDKs, only the default survey locale will be shown. All surveys created before version 8.3 will continue to work with only a single locale.
{% endhint %}

#### Duplicating Surveys

If you need to create a copy of an already existing survey, you can duplicate it by opening the survey itself from the survey list, and clicking on the duplicate button in the survey results page. Please note that any duplicated survey is placed directly in a draft state, so you'll have to publish it manually once the duplication is complete.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1942622943/https%3A%2F%2Ffiles.readme.io%2Fad74d1fe3d698fb09765241dd6969d0536f43de644a75fdbbe6c6dd5c79a8b5b-ios-creating-surveys-1.png" alt=""><figcaption></figcaption></figure>


# Targeting Surveys

Configure survey targeting in Luciq to show surveys to the right users at the right time. Set audience, timing, and conditions for precise targeting.

You can define criteria that your users have to meet in order for your surveys to appear in their app. This can be done through both automatic and manual targeting. The available targeting types vary based on the survey or announcement template you're using.

### Auto Targeting

After choosing the survey type you want to create, you can target specific audiences using custom conditions.When you select **Auto Targeting**, you can define criteria for who should receive the survey. Your users matching the conditions you set will automatically see the survey. In addition to default attributes like App Version, OS, Email, Sessions Count, Last Seen, Country, etc., you can set conditions for custom user attributes or user events that you have created. Multiple different criteria of any type can be added.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1352503449/https%3A%2F%2Ffiles.readme.io%2F1e828c5a46befda6b83022b3bc27e8842214040a743bbbbe97ef40c0ac8ae882-image.png" alt=""><figcaption></figcaption></figure>

You can specify the who, when, and frequency of the survey.

* **Who**: this is used to automatically target specific users. You can target users with specific attributes or users that have done specific events. Examples can be found in the screenshot below.
* **When**: specify when the survey should show to your users. By default, this is set to 10 seconds after the application launches. This can be set so that the survey shows the moment a specific event occurs.
* **Frequency**: the amount of times the user sees the survey within a certain period. By default, this is once every 30 days. The number of days can be edited and you can set it so that the survey only appears once and never again afterward.​

  <figure><img src="https://images.gitbook.com/__img/dpr=2,width=1168,onerror=redirect,format=auto,signature=-96283333/https%3A%2F%2Ffiles.readme.io%2F1c860858da95559637bf308a0d2287c67ee491af1f91da653b8af0afea820422-image.png" alt=""><figcaption></figcaption></figure>

Depending on the template you use, different options will be available to use for automatic targeting.

* Custom/NPS/App Rating: these three templates can automatically target using default attributes, custom attributes, and user events.

{% hint style="info" %}
**Targeting using App Version**

Starting from version 8.5.0, the accepted app versions can be any of the ones with the following formats:

* x.y.z (ex: 1.4.2)
* x.y.z.w (ex: 1.4.2.7)
* \[any string] (ex: 1.4.2april2016) *this format can only be used with equals/doesn't equal to and can't be set to greater or less than*
  {% endhint %}

#### Controlling Auto Targeting

You can have auto targeting surveys shown automatically at the start of a user's session or show it manually.

**Showing Automatically**

By default, a survey will automatically be presented to users who meet your conditions in their first session after you publish the survey within 10 seconds of opening your app. If you have multiple surveys running and a user meets the conditions for more than one survey, they will be shown each survey one by one.Auto-targeting surveys are shown once only, unless specified otherwise in the targeting section of the survey.

**Targeting Through CSV**

If you'd like to target a list of specific user emails, this is now possible by uploading a CSV in the targeting step. Each email should be in a separate row with no more than 100K entries. The file should also be less than 5MB in size. Once the file is uploaded, the dashboard will take care of the rest!

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=2044603175/https%3A%2F%2Ffiles.readme.io%2Ff852076f971accde59a5a0e942f31136de9b08ce3cbaff28151aa55a7400b4c6-image.png" alt=""><figcaption></figcaption></figure>

### Manual Targeting

You can also use **Manual Targeting** to show your surveys to specific audiences, and these surveys can be re-shown any number of times.Each created survey has a unique token that you can refer to in your code, as explained in the following section. Please note that manual targeting can only be used with custom surveys, NPS surveys, and app ratings.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-713341768/https%3A%2F%2Ffiles.readme.io%2F5bebe5873fcae4cd0803c1d4e2547ee94808d156334c3aae5685cd8401d54edf-image.png" alt=""><figcaption></figcaption></figure>


# Branching Logic

Branching Logic makes a Custom Survey follow a path based on each answer, so you can skip questions, end early, or jump to the thank-you.

### Overview

Branching Logic lets you define a dynamic path through a Custom Survey, where the next question shown to the end user depends on the answer they just gave. You can also use it to skip ahead, end the survey early, or jump straight to the thank-you message based on a specific response.

This helps you:

* Show only the questions that are relevant to each user, reducing survey fatigue and increasing completion rates
* Segment feedback during collection
* Run shorter, more intentional surveys without sacrificing depth

{% hint style="info" %}

#### Availability

* Available for **Custom Surveys** only
* Supported on:
  * **iOS, React Native & Flutters SDKs on v19.6.0** and above
  * **Android SDK on v19.5.0** and above
* Branching is **disabled by default** per survey and needs to be toggled on in the Content Builder
  {% endhint %}

{% hint style="warning" %}
**Note:** Surveys with branching enabled will only be **shown to end users who are on an SDK version that supports the feature**. Users on older SDK versions will not see the survey.
{% endhint %}

***

### Supported question types

Branching can be configured for the following Custom Survey question types:

#### #1 Single-select (Multiple Choice)

Branch based on the specific answer your user picks. A branching option is added next to each choice so you can map every answer to a different target question.

**Example:**

* Q1: *Did you find what you were looking for?*
  * "Yes" → go to Q3
  * "No" → go to Q2 (default path)

#### #2 Star rating

Branch based on the user's rating, grouped into three predefined buckets:

* 1 or 2 stars
* 3 stars
* 4 or 5 stars

Each bucket can be mapped to a different target question.

**Example:**

* 1-2 stars → go to Q4 (*What went wrong?*)
* 3 stars → go to Q5 (*What could be better?*)
* 4-5 stars → go to Q6 (*What did you love?*)

#### #3 Text field

Branching for text field questions happens at the **question level, not based on the answer content.** Once the user submits their response, the survey can jump to a designated question.

***

### Configuring branching

#### Step 1 — Enable branching

In the **Content Builder** step of a Custom Survey, toggle **Branching** on.

#### Step 2 — Set the path for each answer

For every supported question, you'll see a branching option next to each choice (or at the end of the question, for text fields). Choose one of:

* **Go to a specific question** — e.g., jump from Q1 to Q4
* **Next question** *(default)* — follow the linear order
* **Show the thank-you message** — end and display the thank-you screen

You can add as many branches as you want across different questions within the same survey (e.g., branch from Q1 and again from Q4).

#### Step 3 — Review & Publish

In the **Review & Publish** step:

* An expandable branching icon appears next to each question that has branching configured, so you can verify the full flow before publishing

***

### Viewing survey responses

On the **Survey Responses** page, only the questions each user actually answered are shown. There are no placeholders for questions that were skipped by the branching logic.

For example, if a user was routed through Q1 → Q3 → Q5, their response will display:

* Q1: *Answer*
* Q3: *Answer*
* Q5: *Answer*

***

### FAQs

**Q: What happens to users on older SDK versions that don't support branching?** A: They will not see the survey. This ensures every respondent has the same branching experience.

**Q: What about surveys that were already published before my app upgraded to a branching-supported SDK?** A: Cached surveys from older SDK versions continue to behave normally. Backward compatibility is preserved.

**Q: Can I add branching to NPS, App Rating, What's New, or Version Update surveys?** A: Not in this iteration. Branching is currently supported for Custom Surveys only.

**Q: Does branching affect targeting?** A: No — targeting (Auto, Manual, Email Lists) works the same way. Branching only affects the flow of questions within the survey itself.


# App Ratings & Reviews

Track your app store ratings and reviews in Luciq. Pick a bundle ID or package name and Luciq fetches existing reviews and finds new ones.

To start using App Ratings and Reviews, navigate to the App Reviews page on your dashboard from the side navigation bar and choose your app's bundle ID or package name. Luciq will then automatically fetch your existing app store reviews and detect new reviews your app receives.

{% hint style="info" %}
**Min Required SDK Version**

App Ratings & Reviews is supported starting iOS SDK version 12.0.
{% endhint %}

Once you confirm your bundle ID, you’ll be able to track, monitor, and debug App Reviews and Ratings.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=1425383440/https%3A%2F%2Ffiles.readme.io%2F2622632fd34677d07ff628f3acbff0c3f60477366b0d2382c898a7ae33f6c82d-ios-app-reviews-2.png" alt=""><figcaption></figcaption></figure>

### Track App Reviews <a href="#track-app-reviews" id="track-app-reviews"></a>

In the App Reviews page in the side navigation bar, you’ll find a list of all reviews your app received, where you'll be able to view the following metadata:

* Rating
* Title
* Review
* Date
* App Version
* Country​<br>

  <figure><img src="https://images.gitbook.com/__img/dpr=2,width=1168,onerror=redirect,format=auto,signature=1509673054/https%3A%2F%2Ffiles.readme.io%2F6db75c0518bbd4869e2146b9c4beaae81da2f8cca5977391be308bd591b9eaa3-ios-app-reviews-5.png" alt=""><figcaption></figcaption></figure>

### Monitor App Ratings <a href="#monitor-app-ratings" id="monitor-app-ratings"></a>

From the App Overview page, you’ll be able to monitor your overall app rating per country to see how your ratings are distributed and see a chart for the Rating over time.By clicking on view all reviews button, you’ll be redirected to the App Reviews page to see a list of all your reviews.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-797350245/https%3A%2F%2Ffiles.readme.io%2F75ea3a504ff6f6503124c5e968297140a02ba1aa3cb1c0901ea8b3958bb2199c-ios-app-reviews-7.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=1934462360/https%3A%2F%2Ffiles.readme.io%2F2eaaed0ec2b3e3afeeb90add2c85b321fd3fe2b00dac826d841b7bf062632509-ios-app-reviews-4.png" alt=""><figcaption></figcaption></figure>

### Monitor App Ratings and Reviews for each Release <a href="#monitor-app-ratings-and-reviews-for-each-release" id="monitor-app-ratings-and-reviews-for-each-release"></a>

From the releases page, you'll be able to see the Average Rating for each release. This average rating is calculated based on the star rating associated with each review the user wrote on the store for this app version.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=1527531422/https%3A%2F%2Ffiles.readme.io%2F69a7f4484ad8e36d495da2b87aeb77bbf2cbb381ce400cc4bb812659330e465b-ios-app-reviews-9.png" alt=""><figcaption></figcaption></figure>

From the release details page, you'll be able to see a breakdown of your App Rating based on the number of stars. In the comparison table, you’ll be able to see the current version rating and compare it across different releases.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-2136095986/https%3A%2F%2Ffiles.readme.io%2F2b70360f2eb43a534ead2beea6b12a34f2a293e9666699c091b47fdf173adfe9-ios-app-reviews-10.png" alt=""><figcaption></figcaption></figure>

Once you navigate to the summary tab, you'll be able to see an AI-generated summary of the reviews for this release to get an idea about the end user sentiment.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-1846657529/https%3A%2F%2Ffiles.readme.io%2Ff468891c6b22a01a7fc4e503db32855a8398d3ff6338d9e1da90ef71117cc5dc-ios-app-reviews-7.png" alt=""><figcaption></figcaption></figure>

### Debug App Reviews <a href="#debug-app-reviews" id="debug-app-reviews"></a>

Tracking your app’s ratings and reviews is only the first step. Combining Ratings and Reviews with Luciq’s Session Replay allows you to view a list of the sessions related to a specific review and replay them to understand the experience that led to that review.

#### How It Works <a href="#how-it-works" id="how-it-works"></a>

**Native In-App Prompt**

If you’re using the native in-app rating API, Our SDK will automatically detect the suspected sessions that are related to the reviews you receive on the dashboard.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=669359351/https%3A%2F%2Ffiles.readme.io%2Fb8b0aa13f6f4dc02a0835514fb7489232d1ffea0ebc2070f87252808b7d402b3-ios-app-reviews-3.png" alt=""><figcaption></figcaption></figure>

Once you click on **“View Session“** CTA, you’ll be redirected to the list of suspected sessions we matched for this review.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=658194379/https%3A%2F%2Ffiles.readme.io%2F99701393b960e6c28d6136874c7f036d56c4465e974e470232b12c3a30df6951-ios-app-reviews-1.png" alt=""><figcaption></figcaption></figure>

Now when you click on session details, you will be able to replay the session associated with that review and see all the needed debugging data that would help you resolve the issue.

<figure><img src="https://images.gitbook.com/__img/dpr=2,width=760,onerror=redirect,format=auto,signature=-586801711/https%3A%2F%2Ffiles.readme.io%2F578ee15f5579fe453a62f6ca671d05e30c5aa50b4344a6013be4c9a25e7dbae1-ios-app-reviews-8.png" alt=""><figcaption></figcaption></figure>


# Rollout Management

Monitor and control staged release rollouts from Luciq. See in-progress and paused releases with progress once your app store is connected.

This feature allows you to monitor release rollout progress & manage it during the staged rollout phase. Integrating your application with the App Store is a prerequisite to rollout management with its guide [here](/product-guides-and-integrations/integrations/store-integrations).

### Overview

With the App Store integrated with the dashboard, you can view the In-Progress/Paused releases highlighted with progress percentage on your Releases page.

<figure><img src="https://files.readme.io/a4840959686931eb0be01bba1c732f281effde2f6b4defb0ec25c22dadf259c1-ios-rollout-management-1.png" alt=""><figcaption></figcaption></figure>

#### Rollout Progress

On the Releases details page, you'll be able to view the progress of the rollout in addition to the rollout start date. This phased release allows you to release the app version over seven days to users who have enabled automatic updates.

<figure><img src="https://files.readme.io/1351bdd57ad6e61f7c6d7907ef236aee4de571b238d788d3a2dda23e084e1015-image.png" alt=""><figcaption></figcaption></figure>

#### Pause Rollout

You'll be able to pause/halt the rollout process at any point in time.

<figure><img src="https://files.readme.io/b65f0c8c6ee015cebcb90ed7d4990fdb293b8a9a8400c080c518a617fdaac60e-image.png" alt=""><figcaption></figcaption></figure>

#### Complete Rollout

Once the rollout is complete, you'll be able to find the number of days it took to complete the process and the number of days paused.

<figure><img src="https://files.readme.io/69a3b024e348edff80564a06e0d52831736f9b3486b08c62af7b451c8ca3d0d8-image.png" alt=""><figcaption></figcaption></figure>

### Roles and Permissions

#### Admin and Owner Roles

Admin and Owner roles in your organization have full access to the Rollout Management feature. This means they can view and manage all aspects of your rollouts, including pausing, resuming, and releasing to all users.

<figure><img src="https://files.readme.io/7b67d3e-image.png" alt="Admin and Owner Roles"><figcaption><p>Admin and Owner Roles</p></figcaption></figure>

#### Member and Viewer Roles

Members and viewers in your organization have limited access to the Rollout Management feature. They can view the rollout progress and gain insights into the process. However, they cannot take any actions or make changes to the rollout.

<figure><img src="/files/sMQWdBOikcDRyAI3DG7u" alt=""><figcaption><p>Member and Viewer Roles</p></figcaption></figure>


# Feature Flags

Track feature flags in your mobile app with Luciq. Log active flags in bug reports and crashes to correlate issues with specific feature flag states.

There are different cases where you end up having different feature flags or experiments for different users. For example, if you are:

* Controlling your rollout by enabling features to a % of your users to monitor Performance and Stability.
* Creating and testing different mobile onboarding experiences concurrently.
* Testing different landing pages for your mobile app.
* Implementing new features with different UI.

Through the “Feature Flags” API you can keep track of your feature flags and its impact on [Bug Reports](https://www.luciq.ai/product/bug-reporting), [Crash Reports](https://www.luciq.ai/product/crash-reporting) and [App Performance](https://www.luciq.ai/product/app-performance-monitoring) for each user and even filter by them. This can help you in:

* Detecting if the the potential source of any latency or issues in the app is introduced by different variants of the experiment or new features.
* Having visibility for the latencies of your variants over different metrics.
* Filtering by your experimental variants to analyze if they impact your performance or cause crashes.
* Debugging issues faster by understanding if the experimental values contributed in a issue.

### Set-up Features & Experiments

#### Adding Feature Flags

**Notes:**

1. Feature Flag Naming: Each feature flag name should not exceed 70 characters and each variant name should not exceed 70 characters. Otherwise, they will get ignored by the SDK. Note that feature flag names are case-insensitive.
2. Feature Flag Persistence: Feature flag persist beyond individual sessions and are not automatically removed at the end of a session. Additionally, calling the logOut function does not impact the existence of the feature flag. The feature flag is only removed when you call the removing method or the clearing method.
3. The amount of feature flags sent in a session is 200 with maximum of 1 variant per a multivariate feature flag. For example, a feature flag that has 3 variants sent within 1 session will only be sent to our backend as the last variant, and not all 3.

#### Boolean Feature Flags - Example Usage

Below is an example of where in your code you would use feature flag. In this example, you are experimenting with feature logic that controls whether or not the user has a Dark Mode toggle available.

{% tabs fullWidth="true" %}
{% tab title="iOS-Swift" %}

```swift
let flag = FeatureFlag(name: "flag")
Luciq.add(featureFlag: flag)
```

{% endtab %}

{% tab title="iOS-ObjC" %}
{% code overflow="wrap" %}

```objective-c
[Luciq addFeatureFlag: [[LCQFeatureFlag alloc] initWithName:@"boolFeatureFlag"]];
```

{% endcode %}
{% endtab %}

{% tab title="Android-Java" %}
{% code overflow="wrap" %}

```java
Luciq.addFeatureFlag(new LCQFeatureFlag("boolFeatureFlag"));
```

{% endcode %}
{% endtab %}

{% tab title="Android-Kotlin" %}

```kotlin
Luciq.addFeatureFlag(LCQFeatureFlag("Flag X"))
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
Luciq.addFeatureFlag({ name: "flag_name" });
```

{% endtab %}

{% tab title="Dart" %}

```dart
Luciq.addFeatureFlags([FeatureFlag(name: "name")]);
```

{% endtab %}

{% tab title="Flutter" %}

```
Luciq.addFeatureFlags([FeatureFlag(name: 'Boolean Feature Flag')]);
```

{% endtab %}

{% tab title="React-Native" %}

```
const boolFeatureFlag: FeatureFlag = {name: 'Boolean Feature Flag'}
Luciq.addFeatureFlags([boolFeatureFlag]);
```

{% endtab %}
{% endtabs %}

#### Multivariant Feature Flags - Example Usage

Below is an example of where in your code you would use feature flag with multiple variants. In this example, you are experimenting with feature logic that controls multiple versions of a specific feature.

{% tabs fullWidth="true" %}
{% tab title="iOS-Swift" %}

```swift
let flagWithVariant = FeatureFlag(name: "flag", variant: "variant")
Luciq.add(featureFlag: flagWithVariant)
```

{% endtab %}

{% tab title="iOS-ObjC" %}
{% code overflow="wrap" %}

```objective-c
[Luciq addFeatureFlag: [[LCQFeatureFlag alloc] initWithName:@"stringFeatureFlag" variant:@"Value1"]];
```

{% endcode %}
{% endtab %}

{% tab title="Android-Java" %}
{% code overflow="wrap" %}

```java
Luciq.addFeatureFlag(new LCQFeatureFlag("StringFeatureFlag", "Value"));
```

{% endcode %}
{% endtab %}

{% tab title="Android-Kotlin" %}

```kotlin
Luciq.addFeatureFlag(LCQFeatureFlag("Flag X", "Value"))
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
Luciq.addFeatureFlag({ name: "flag_name", variant: "variant" });
```

{% endtab %}

{% tab title="Dart" %}

```dart
Luciq.addFeatureFlags([FeatureFlag(name: "name", variant: "variant")]);
```

{% endtab %}

{% tab title="Flutter" %}

```
Luciq.addFeatureFlags([FeatureFlag(name: 'Feature Flag', variant: 'Value')]);
```

{% endtab %}

{% tab title="React-Native" %}

```
const featureFlag: FeatureFlag = {name: 'Boolean Feature Flag'}
Luciq.addFeatureFlags([featureFlag]);
```

{% endtab %}
{% endtabs %}

If you have a team who is responsible for a specific feature flag or an experiment, you can automatically assign them the relevant issues and forward them to their favorite tool. You'll find more information on Team Ownership [here](broken://pages/HHjt4ehvmdpx796CKZnr).

In the screenshot below, we wanted to assign crashes relevant to the feature flag Recommendations\_enabled to the team responsible for this feature and auto-forward it to their Jira board.

#### Feature Flags & Team Ownership

If you have a team who is responsible for a specific feature flag or an experiment, you can automatically assign them the relevant issues and forward them to their favorite tool. For more details on Team Ownership, click here

In the screenshot below, we wanted to assign crashes relevant to the experiment Recommendations\_enabled to the team responsible for this feature and auto-forward it to their Jira board

<figure><img src="https://files.readme.io/d781f48-image.png" alt=""><figcaption></figcaption></figure>

#### Removing Feature Flags

If your feature flag is concluded or you would like to simply remove it, you can use this method:

{% tabs fullWidth="true" %}
{% tab title="iOS-Swift" %}

```swift
Luciq.removeFeatureFlag("stringFeatureFlag")
```

{% endtab %}

{% tab title="iOS-ObjC" %}
{% code overflow="wrap" %}

```objective-c
[Luciq removeFeatureFlag:@"boolFeatureFlag"];
[Luciq removeFeatureFlags:@[flag1]];
```

{% endcode %}
{% endtab %}

{% tab title="Android-Java" %}
{% code overflow="wrap" %}

```java
Luciq.removeFeatureFlag("Flag key");
```

{% endcode %}
{% endtab %}

{% tab title="Android-Kotlin" %}

```kotlin
Luciq.removeFeatureFlag("boolFeatureFlag")
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
Luciq.removeFeatureFlag('flag_name');
```

{% endtab %}

{% tab title="Dart" %}

```dart
Luciq.removeFeatureFlags(["name"]);
```

{% endtab %}

{% tab title="Flutter" %}

```
Luciq.removeFeatureFlags(['feature flag']);
```

{% endtab %}

{% tab title="React-Native" %}

```
Luciq.removeFeatureFLag('FeatureFlag'); // remove single key
Luciq.removeFeatureFlags(['featureFlagA', 'featureFlagB']); // remove multiple feature flags at once
```

{% endtab %}
{% endtabs %}

#### Clearing Feature Flags

You can use the below method to clear all the Feature Flags from your reports

{% tabs fullWidth="true" %}
{% tab title="iOS-Swift" %}

```swift
Luciq.removeAllFeatureFlags()
```

{% endtab %}

{% tab title="iOS-ObjC" %}
{% code overflow="wrap" %}

```objective-c
[Luciq removeFeatureFlag:@"boolFeatureFlag"];
[Luciq removeFeatureFlags:@[flag1]];
```

{% endcode %}
{% endtab %}

{% tab title="Android-Java" %}
{% code overflow="wrap" %}

```java
Luciq.removeAllFeatureFlags();
```

{% endcode %}
{% endtab %}

{% tab title="Android-Kotlin" %}

```kotlin
Luciq.removeAllFeatureFlags()
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
Luciq.removeAllFeatureFlags();
```

{% endtab %}

{% tab title="Dart" %}

```dart
Luciq.clearAllFeatureFlags();
```

{% endtab %}

{% tab title="Flutter" %}

```
Luciq.clearAllFeatureFlags();
```

{% endtab %}

{% tab title="React-Native" %}

```
Luciq.removeAllFeatureFlags();
```

{% endtab %}
{% endtabs %}

## Features Flags Dashboard

Explore the Feature Flags Dashboard for a detailed look into the performance metrics of your feature flags and all of their variants.

<img src="https://files.readme.io/7b445f95eb0b4546c99df4c10c01b3eb0f84a6c30117ec23b9cb88599ce7d4ed-image.png" alt="" data-size="original">

#### Types of Feature Flags

There are two types of feature flags:

* **Boolean**: a feature flag that has no variants. Usually used in kill switch feature flags.
* **Multivariant**: a feature flag with multiple variants.

#### Gain key insights

You are able to view different metrics within the page that allow you to gain more key insights like:

* **Apdex Score:** An overall Apdex score is calculated based on Crashes, App Hangs, and Force Restarts occurring in sessions with this feature.
* **Crash-Free Users Rate:** The percentage of users experiencing this feature without any crashes, relative to the total number of users using this feature.
* **Crash-Free Sessions Rate:** The percentage of sessions with this feature that are crash-free out of the total sessions involving this feature.
* **Total Sessions:** View the total number of sessions where this feature flag was detected, providing an overview of its usage.
* **First Seen**: Sort by and view the dates of the release of your feature flags.

  <figure><img src="https://files.readme.io/a50c5f81df42e2f79f16ff1dc64cc2d02b4ebfdee30109d2f16959e2ebdc5d0f-product-guides-feature-flags-3.png" alt=""><figcaption></figcaption></figure>

#### Details Page: Drill Down

1. Insights Table:
   * Explore a detailed breakdown of your feature othrough the Insights Table, providing a nuanced understanding of its performance. Here's a comprehensive overview of each metric:
     * **Apdex**: combining Crashes, App Hangs, and Force Restarts within sessions featuring this specific feature, gives you an overall performance indicator.
     * **Crash-Free Users Rate**: The percentage of users experiencing this feature without any crashes, relative to the total number of users using this feature.
     * **Crash-Free Sessions Rate**: The percentage of sessions with this feature that are crash-free, out of the total sessions involving this feature.
     * **Total Sessions**: View the total number of sessions where this feature flag was detected, providing an overview of its usage.
     * **App Hang-Free Sessions**: The percentage of sessions without any App hangs, out of the total sessions involving this feature.
     * **Force-Restart Free Sessions**: Understand the percentage of sessions that ended in a force restart of the app, out of the total sessions involving this feature.
   * If you click on any of the metrics, you’ll be redirected into the list issues where filtered by this feature.
   * Compare this feature flag against all users across all metrics to identify potential issues or improvements compared to your baseline.<br>

     <figure><img src="https://files.readme.io/a50c5f81df42e2f79f16ff1dc64cc2d02b4ebfdee30109d2f16959e2ebdc5d0f-product-guides-feature-flags-3.png" alt=""><figcaption></figcaption></figure>
2. Crashes Section:
   * **Crashes Tab**: This section provides details on any crash that occurred at least once with this feature.
   * **Introduced Crashes Tab**: Crashes for which the first occurrence includes this feature. This can help you understand the impact of the feature on the introduction of new crashes.
   * **Exclusive Crashes Tab**: Crashes that exclusively happened in sessions containing this feature and never occurred without it. This can help **identify the issues associated specifically with this feature**.

     <figure><img src="https://files.readme.io/60e3934-image.png" alt=""><figcaption></figcaption></figure>

By examining both the Insights Table and the Crashes Section, you gain a comprehensive understanding of your feature , enabling you to find out the best and worst performing features.

### Feature Flags in Performance Monitoring

Once you add the API to your code, you will be able to view the experiments in the patterns section of Cold App Launch, Screen Loading, and UI Hangs.

<figure><img src="https://files.readme.io/a7a75f8293cf96ec0ae381f40e43d2d1a0515804cd0c83d7d96efd59c9d6dee5-product-guides-feature-flags-5.png" alt=""><figcaption></figcaption></figure>

You can see the different latencies of your metric in correlation with the experimental variant. For example, in the previous screenshot, users who had *guest\_mode* enabled had a very different Apdex score, p50 and p95 latencies.

You can also isolate your feature flags by filtering with a specific flag value for further analysis to understand if they are impacting the latency of App launch, Screen Loading or UI hangs.

If you filter by *guest\_mode* and *No experiments* as shown on the following screenshot, the *No Experiments* presents occurrences without any experiments applied. You can also filter by one or more experimental values.

<figure><img src="https://files.readme.io/570c862-2.png" alt=""><figcaption></figcaption></figure>

The *No Experiments* selection will help you spot and compare any difference in performance in each metric.

<figure><img src="https://files.readme.io/f223108ac25c67028dba3813c54c10d831537ef83928ede43f222c7db90701d6-image.png" alt=""><figcaption></figcaption></figure>

### Feature Flags in Crash Reporting

Rolling out new features or doing modifications in your code can increase the number of errors you are seeing. By analyzing how different feature flag variants are contributing to your crashes, you can minimize the debugging efforts and team members can save time.

For example, if you just rolled out a new recommendation feature for a subset of your users, you can view all the crashes that occurred to the users who had this feature enabled by using the filters.

In the screenshot below, we filtered by feature flag *Recommendations\_enabled*, to view the relevant crashes

<figure><img src="https://files.readme.io/0c6fb07-4.png" alt=""><figcaption></figcaption></figure>

You can also view the feature flag variants attached to each crash report on your dashboard in the patterns section of a crash.

<figure><img src="https://files.readme.io/bdff9bc-5.png" alt=""><figcaption></figcaption></figure>

#### Feature Flags in Bug Reporting

Introducing new features or making changes to existing ones can sometimes lead to an increase in Bugs. By leveraging feature flags, you can isolate and analyze issues related to specific features, making it easier to identify and resolve bugs. This approach helps in reducing the debugging time and effort for your team. This should also help you collect actionable data on the next actions for any experiment you are holding

In the screenshot below, you can easily view which feature flag a bug is associated with in each bug report

<figure><img src="https://files.readme.io/88a291c0195590730bdeb31fc3d621c51f8449a3c7635d747ddaf5bdce1d1997-image.png" alt=""><figcaption></figcaption></figure>

You can also easily navigate and filter by a specific feature flag to focus on the bugs you need to prioritize and it would reflect on the reports column on the left

<figure><img src="/files/OHFJNMNrgUGfR4bZTgyi" alt=""><figcaption></figcaption></figure>


# AI Features

The AI side of Luciq, covering the MCP Server, Resolve Agent, Detect Agent, Agent Skills, and the AI-ready data your coding agent reads.


# Detect Agent

Detect Agent surfaces problems nobody reported, finding broken functionality and visual defects inside your Session Replay recordings.

Most of what breaks in a mobile app is never reported. Users abandon a flow, retry, or quietly churn, and nothing reaches your team. Detect Agent looks through your Session Replay recordings and surfaces those problems on its own, without anyone filing a report.

It detects two kinds of issue:

* [**Broken Functionality**](/product-guides-and-integrations/product-guides/ai-features/detect-agent/broken-functionality) - dead buttons, forms that fail to submit, and flows that break mid-process.
* [**Visual Issues**](/product-guides-and-integrations/product-guides/ai-features/detect-agent/visual-issues) - misaligned or overlapping elements, layout shifts, text that clips or overflows, and other rendering defects.

Both are built on Session Replay, both run server-side with no SDK changes or instrumentation, and both behave the same way once detected:

* **Grouped by default.** Issues describing the same underlying problem are collected into a single group with an occurrence count, so a defect spanning hundreds of sessions reads as one item rather than hundreds. You can still open any individual occurrence to see the exact screenshot and session behind it.
* **Open to feedback.** Every detection can be marked valid or not valid, and ungrouping asks you why. That feedback improves detection and grouping accuracy over time.
* **Surfaced alongside everything else.** Detected issues appear on the AI Issues page and in the [Issues List](/product-guides-and-integrations/product-guides/inbox/issues-list) on the Inbox page, next to your crashes, bugs, and performance issues.

Start with the page for whichever detection type you're setting up.


# Visual Issues

Detect visual UI issues in your mobile app automatically with Luciq's Detect Agent. Identify rendering problems, layout glitches, and visual regressions.

### Overview

**AI Visual Issues** is a feature built on top of Session Replay. Once enabled, it uses AI to scan screenshots captured during user sessions and flag visual defects such as:

* Misaligned elements or overlapping UI
* Color variations that don’t match design spec
* Unexpected layout shifts or gaps
* Text overflow, clipping, or truncation
* Visual artifacts, noise, or anomalies

These issues get surfaced as **Visual Issue events**, which you can filter and inspect alongside normal session data.

### How to Use

You need to have the **Session Replay** product enabled to be able to enable the AI Visual Issues feature. Once the feature is enabled, you follow these steps:

#### Step-by-Step

1. On the Session Replay list, use the new “AI Issues” filter to only show sessions flagged with AI issues including visual issues.

   <div align="left"><figure><img src="https://files.readme.io/e5631b68409cf7f0e1d02e5b35c2dbc7febec9cefffd16b551c23ed2ec1f6de4-image.png" alt="" width="563"><figcaption></figcaption></figure></div>
2. Open a session with flagged issues. Two ways to navigate:
   * Scroll through the event timeline to find the visual issue event.
   * Click the purple dot on the timeline to jump directly to the issue’s screenshot view.
   * Use the new AI issues filter, you can select “Visual Issues”
3. Viewing a Visual Issue event shows:
   * The exact screenshot where the issue was detected
   * A descriptive summary of the issue (e.g. “element misaligned by 4px”)

### Grouping

Visual issues that describe the same underlying defect are collected into a single group. A problem that shows up across many sessions appears once, with a count, instead of as a long run of near-identical entries.

#### What gets grouped

A new visual issue is compared against the representative issue of each existing group, based on what the issue actually describes: the defect itself, its likely root cause, and the flow the user was in when it was captured. A close enough match joins that group. Anything else stays on its own as a single issue.

#### What you see

* On the AI Issues page, a group appears as one row carrying its shared description and the number of occurrences it holds. A visual issue that hasn't matched anything shows as **Single**.
* Sort by occurrences to bring the most widespread issues to the top.
* Open a group's occurrences drawer to see every occurrence it holds, then open any one of them for the exact screenshot and the session it came from. This is how you check what went wrong for one specific user without losing the grouped view.
* Visual issues also appear in the [Issues List](/product-guides-and-integrations/product-guides/inbox/issues-list) on the Inbox page, where the row's **Type** reads either **Single** or **Group** with its occurrence count.

#### Ungrouping

If an occurrence doesn't belong in a group, select it in the occurrences drawer and choose **Ungroup**. You'll be asked why before the change is applied, with two options: **Issues are unrelated**, or **Duplicate of another group**.

{% hint style="info" %}
The reason you pick is used to improve grouping accuracy over time, so it's worth choosing the one that fits rather than the first one on the list.
{% endhint %}

The first occurrence in a group cannot be ungrouped.

### Feedback

Every visual issue can be marked valid or not valid, and that feedback feeds back into detection accuracy.

* **Valid.** Marking an issue as valid gives you a **Report bug** link to turn the detection into a bug report, or **View bug** if a report already exists for it.
* **Not valid.** Marking an issue as not valid opens a short form: pick a reason (**Behavior was expected**, **User aborted the flow**, or **Evidence doesn't support it**), optionally add a comment, and submit. You then get a **Hide issue** link to take it off your list.

Feedback controls are available to members with permission to manage Detect Agent.

### How It Works

* No instrumentation changes required - once enabled, existing session screenshots are routed to the visual analysis pipeline.
* Screenshots are passed through an AI model for analysis.
* When anomalies are detected, a Visual Issue event is emitted in the timeline at the same timestamp as the screenshot.
* The event payload includes descriptive text of the anomaly
* On the front end, UI surfaces let you filter and inspect these events alongside other session data.

### Compatibility & Requirements

* Supported Platforms: All mobile platforms currently supported by Luciq (iOS, Android, React Native, etc.)
* Prerequisite: Session Replay must be enabled
* No SDK version upgrade needed (feature is server-side)

### Limits & Quotas

* Per-account monthly cap: Up to **1,000 analyzed sessions**
* Maximum visual issues generated per month: 1,000

### Getting Access

* Beta / Invite-only: At present, the feature is available by invitation only.
* To request access:
  * Submit a request via Intercom / support chat
  * If you're an Enterprise customer, reach out to your customer success manager to be added to the waitlist.
* Once approved, the feature will be toggled on from the backend - no SDK changes are needed.


# Broken Functionality

AI Broken Functionality builds on Session Replay to find dead buttons, forms that fail to submit, and flows that break mid-process.

### Overview

**AI Broken Functionality** is a feature built on top of Session Replay. Once enabled, it identifies issues in your app’s functionality that affect user experience, such as:

* Buttons or links that don’t respond
* Forms that fail to submit
* Flows that break mid-process
* Unexpected crashes or error messages
* Inconsistent app behavior compared to expected flows

Detected problems are surfaced as **Broken Functionality** events, which can be reviewed alongside normal session data.

### How to Use

You need to have the **Session Replay** product enabled to be able to enable the AI Broken Functionality feature. Once the feature is enabled, you follow these steps:

#### Step-by-Step

1. On the Session Replay list, use the new “AI Issues” filter to only show sessions flagged with AI issues including broken functionality.

   <div align="left"><figure><img src="https://files.readme.io/c3f5042fc7e4f2bce5e584b78972073d9361e2dd076c15ffac309dc2be6e99ae-image.png" alt="" width="563"><figcaption></figcaption></figure></div>
2. Open a session with flagged issues. Two ways to navigate:
   * Scroll through the event timeline to find the broken functionality event.
   * Click the purple dot on the timeline to jump directly to the issue’s screenshot view.
   * Use the new AI issues filter, you can select “Broken Functionality”
3. Viewing a Broken Functionality event shows:
   * The exact screenshot where the issue was detected
   * A descriptive summary of the issue (e.g. “Subtotal is miscalculated”)

### Grouping

Broken functionality issues that describe the same underlying breakdown are collected into a single group. A problem that shows up across many sessions appears once, with a count, instead of as a long run of near-identical entries.

#### What gets grouped

A new broken functionality issue is compared against the representative issue of each existing group, based on what the issue actually describes: the breakdown itself, its likely root cause, and the flow the user was in when it was captured. A close enough match joins that group. Anything else stays on its own as a single issue.

#### What you see

* On the AI Issues page, a group appears as one row carrying its shared description and the number of occurrences it holds. An issue that hasn't matched anything shows as **Single**.
* Sort by occurrences to bring the most widespread issues to the top.
* Open a group's occurrences drawer to see every occurrence it holds, then open any one of them for the exact screenshot and the session it came from. This is how you check what went wrong for one specific user without losing the grouped view.
* Broken functionality issues also appear in the [Issues List](/product-guides-and-integrations/product-guides/inbox/issues-list) on the Inbox page, where the row's **Type** reads either **Single** or **Group** with its occurrence count.

#### Ungrouping

If an occurrence doesn't belong in a group, select it in the occurrences drawer and choose **Ungroup**. You'll be asked why before the change is applied, with two options: **Issues are unrelated**, or **Duplicate of another group**.

{% hint style="info" %}
The reason you pick is used to improve grouping accuracy over time, so it's worth choosing the one that fits rather than the first one on the list.
{% endhint %}

The first occurrence in a group cannot be ungrouped.

### Feedback

Every broken functionality issue can be marked valid or not valid, and that feedback feeds back into detection accuracy.

* **Valid.** Marking an issue as valid gives you a **Report bug** link to turn the detection into a bug report, or **View bug** if a report already exists for it.
* **Not valid.** Marking an issue as not valid opens a short form: pick a reason (**Behavior was expected**, **User aborted the flow**, or **Evidence doesn't support it**), optionally add a comment, and submit. You then get a **Hide issue** link to take it off your list.

Feedback controls are available to members with permission to manage Detect Agent.

### How It Works

* No instrumentation changes required - once enabled, existing session screenshots, user flow and app behavior are routed to broken functionality analysis pipeline.
* Screenshots, user flow and app behavior data are passed through an AI model for analysis.
* When functional breakdowns that didn’t result in crashes or performance issues are detected, a Broken functionality event is emitted in the timeline at the same timestamp as the screenshot.
* The event payload includes descriptive text of the breakdown.
* On the front end, UI surfaces let you filter and inspect these events alongside other session data.

### Compatibility & Requirements

* Supported Platforms: All mobile platforms currently supported by Luciq (iOS, Android, React Native, etc.)
* Prerequisite: Session Replay must be enabled
* No SDK version upgrade needed (feature is server-side)

### Limits & Quotas

* Per-account monthly cap: Up to **1,000 analyzed sessions**
* Maximum broken functionality issues generated per month: 1,000

### Getting Access

* Beta / Invite-only: At present, the feature is available by invitation only.
* To request access:
  * Submit a request via Intercom / support chat
  * If you're an Enterprise customer, reach out to your customer success manager to be added to the waitlist.
* Once approved, the feature will be toggled on from the backend - no SDK changes are needed.


# Resolve Agent

Learn how Resolve Agent uses AI to analyze crashes and bugs in your mobile app. Get automated root cause analysis, suggested fixes, and debugging help.

Resolve Agent is an AI agent that helps developers fix mobile app crashes directly from the Luciq dashboard. It analyzes crash data alongside your application source code, identifies the root cause, generates a code fix, and can open a pull request in your repository, all within minutes.

### Prerequisites

Resolve Agent requires a connected GitHub repository to access your source code. To get started:

{% hint style="info" %}
**Enablement:** Resolve Agent is enabled on a per-account basis. Contact your Customer Success Manager or reach out to Luciq Support to enable it.

**Source Code:** Connect your GitHub repository via [Source Code Connection - GitHub](/product-guides-and-integrations/product-guides/ai-features/resolve-agent/source-code-connection-github) before using Resolve Agent.
{% endhint %}

### Using Resolve Agent

Once enabled, the Resolve Agent widget appears at the top of each crash details page.

#### 1. Launch the agent

Click **"Launch Resolve Agent"** on any crash to start the analysis and fix generation process.

<figure><img src="/files/aeoK9uNGegW0uUH6Pqe2" alt=""><figcaption></figcaption></figure>

#### 2. Watch it work

The agent begins by checking the crash's stack trace for readability, then moves into fix generation. As it works, you'll see real-time progress messages describing what the agent is doing: reading specific files, tracing the crash origin, and building the fix. This typically takes one to two minutes depending on the complexity of the crash and the size of your codebase.

<figure><img src="/files/3MDc2CXQFBjevbXidci2" alt=""><figcaption></figcaption></figure>

#### 3. Review the fix

Once complete, you'll see a root cause analysis explaining why the crash happened and a suggested code fix displayed as a diff, directly in the dashboard.

<figure><img src="/files/kcD9pmI6tqH5D7Ykeb8a" alt=""><figcaption></figcaption></figure>

#### 4. Provide feedback (optional)

If the fix doesn't fully address the issue, you can provide feedback describing what's missing or what should change. The agent will use your input to generate an improved fix. You can iterate up to 5 times per crash.

<figure><img src="/files/Nl5eneVa7bphk48Kj4R4" alt=""><figcaption></figcaption></figure>

#### 5. Create a pull request

When you're satisfied with a fix, click **"Create pull request"** to open a PR directly in your GitHub repository. Your team can review the changes, run CI checks, and merge when ready.

<figure><img src="/files/GCdq42VNIY5IslBkenGP" alt=""><figcaption></figcaption></figure>

### Additional actions

* **View previous fixes:** All generated fixes are saved and accessible to your team.
* **Generate another fix:** Submit feedback to generate an additional fix suggestion.
* **Start over:** Delete all previous fixes and restart the analysis, useful when a new app version is available.

***

### How it works

#### Source code integration

Resolve Agent connects to your GitHub repository through the Luciq CodeLink GitHub App. This gives the agent read access to your source code so it can trace crash stack frames back to specific files and functions in your codebase.

#### Crash analysis

When you launch Resolve Agent on a crash, the agent analyzes the crash's stack trace and cross-references it with your source code to identify the root cause. It traces the execution path, examines the relevant code context, and determines what went wrong.

#### Fix generation

Based on the root cause analysis, the agent generates a code fix. You'll see the agent's progress in real time as it works through your codebase, with status messages describing each step it takes. The resulting fix is presented as a code diff with an explanation of the changes.

#### Feedback loop

If the initial fix isn't right, your feedback guides the next iteration. The agent incorporates your input and generates a new fix that addresses your specific concerns. Each crash supports up to 5 fix attempts.

#### Pull request creation

Resolve Agent uses your source code connection to create a pull request in your repository. The PR contains the fix changes and can go through your normal review and CI/CD pipeline before merging.

### Compatibility

|                           | Supported                                    |
| ------------------------- | -------------------------------------------- |
| **Platforms**             | iOS (Native), Android (Native), React Native |
| **Source code providers** | GitHub                                       |

### Enablement and consent

Resolve Agent is enabled on a per-account basis. By requesting enablement, you grant Luciq permission to process crash data and application source code through its AI models in compliance with our [privacy policy](https://www.luciq.ai/privacy). Processing only occurs when you actively use the feature, and you can request to disable it at any time.

Only account admins or owners can request enablement. Once enabled, all team members with appropriate permissions can use the feature.


# Source Code Connection - GitHub

Connect your GitHub repository to Luciq's Resolve Agent. Enable AI source code analysis to get accurate root cause analysis and fix suggestions.

Resolve Agent is an AI-powered feature designed to help developers quickly resolve app crashes by automating root cause analysis, code fix generation, and pull request creation. To enable Resolve Agent, you need to connect your source code repository to Luciq using the **Luciq CodeLink** GitHub app. This guide walks you through the steps to complete the connection and start using Resolve Agent.

### Why Connect Your Codebase to Luciq?

Connecting your codebase enables Resolve Agent to:

* Analyze crash data and identify the root cause.
* Provide actionable code fix suggestions.
* Help generate pull requests with suggested fixes directly in your repository.

By linking your codebase, you’ll save time on diagnosing and resolving crashes, improve app stability, and reduce operational costs.

#### Step 1: Start the Connection Process

1. Navigate to **Settings → Source Code Management** in the Luciq dashboard.
2. Click on the **Connect** button, within **GitHub** connect widget.

<figure><img src="/files/seyY8sokLc3F6B3rzvj6" alt=""><figcaption></figcaption></figure>

#### Step 2: Authenticate with GitHub

1. You’ll be redirected to the **Connect GitHub Source Code** setup screen.
2. Select "**Install Luciq on GitHub.**"
   * This will redirect you to GitHub, where you need to approve the installation.
   * If your organization owner has already installed the app, you can use the installation ID instead.
3. Click **Continue** once the installation is approved.<br>

   <div align="left"><figure><img src="https://files.readme.io/6258363af862d61b0d4dbb3ab722595c90d722ae23fa9d43de0f026f53312548-product-guides-connect-github-1.png" alt="" width="563"><figcaption></figcaption></figure></div>

#### Step 3: Select Repository and Branch

1. After authentication, you’ll be prompted to select the repository and branch where Luciq should analyze and generate code fixes.
2. Choose the correct repository and branch from the dropdown list.
3. Click **Continue** to proceed.<br>

   <div align="left"><figure><img src="https://files.readme.io/9a76506f763debc21b19927a80e3b732e4ce7c33979b1afa4d7f2e5ac5ae5fca-product-guides-connect-github-6.png" alt="" width="563"><figcaption></figcaption></figure></div>

#### Step 4: Connect the Codebase

1. Luciq will begin connecting to your GitHub repository.
2. This process may take a few moments.
3. Once completed, you’ll see a success message confirming the connection.

   <div align="left"><figure><img src="https://files.readme.io/1d9fbd62f6ea12a0cdb215cae52d0e99d97b0626e76f983b20c8839ba946b888-product-guides-connect-github-3.jpg" alt="" width="563"><figcaption></figcaption></figure></div>

#### Step 5: Verify Connection in Settings

1. Navigate to **Settings → Source Code Management** in the Luciq dashboard.
2. You should see a confirmation that GitHub Connect is set up successfully, showing the organization, repository, and branch.
3. You’re now ready to use Resolve Agent to fix crashes automatically!

<figure><img src="/files/vA3apS4tFrEgGumtXd3n" alt=""><figcaption></figcaption></figure>

#### Next Steps

* After connecting your codebase, you can use Resolve Agent to:
  * Analyze crash details and identify root causes.
  * Generate up to three suggested code fixes.
  * Automatically create a pull request with the suggested fix.
  * Review and merge the pull request directly in your repository.

### Troubleshooting

* **Permission Issues**: Make sure you have the necessary permissions to install the GitHub app or contact your organization owner.
* **Repository Not Showing**: Ensure the repository is under the connected GitHub organization and that you have access to it.
* **Connection Errors**: If the connection fails, try reconnecting by first deleting the existing connection and repeating the steps above or checking your GitHub permissions.


# Configure GitHub Webhooks to Track CI & Merge Status

Add a repository-level GitHub webhook so Resolve Agent can follow CI build status and pull request merges, and validate fixes in Luciq.

To track the full journey from crash to fix to validation, Resolve Agent integrates with your CI system by listening to GitHub events through a webhook. This guide walks you through setting up a repository-level GitHub webhook to track CI build status and pull request merges, enabling automated validation inside the Luciq dashboard.

{% hint style="info" %}
Before continuing, make sure you’ve [connected your GitHub repository to Luciq](/product-guides-and-integrations/product-guides/ai-features/resolve-agent/source-code-connection-github) using the CodeLink GitHub app.
{% endhint %}

### Why Connect Your CI to Luciq?

Once your repository is connected, setting up a webhook allows Resolve Agent to:

* Monitor whether AI-generated fixes pass your test suite.
* Display CI status and merge results directly in your Resolve Agent workflow.
* Block unvalidated or failing fixes from progressing to release.
* Streamline your crash resolution lifecycle with minimal manual effort.

{% hint style="warning" %}
**Security and Privacy**<br>

Luciq listens only to metadata events (`status`, `check_run`, and `pull_request`). Webhook communication is encrypted and authenticated using a shared secret provided by Luciq Support.
{% endhint %}

### What This Webhook Tracks?

The GitHub webhook enables Resolve Agent to track:

* **CI Status**
  * `status`: External CI tools (e.g. CircleCI, Jenkins, Bitrise)
  * `check_run`: GitHub Actions workflows
* **Merge Status**
  * `pull_request`: Detects when pull requests are merged into target branches

#### Prerequisites

* Your GitHub repo is connected to Luciq via CodeLink ([set it up here](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/resolve-agent/source-code-connection-github)).
* **Admin access** to the GitHub repository
* Your **Luciq Application Token**
* A **Webhook Secret**, you can get it by reaching out to Luciq Support

#### Step 1: Get Your Webhook Secret

1. Open a **GetHelp ticket** by contacting Luciq Support or reaching out to your designated customer success manager.
2. Request a **GitHub Webhook Secret** tied to your application
3. Save this token securely — it will be used to validate incoming webhook events and other integrations.

#### Step 2: Open GitHub Webhook Settings

1. On GitHub, navigate to the main page of the repository.
2. Under your repository name, click **Settings → Webhooks → Add webhook.**

#### Step 3: Configure the Webhook

| Field        | Value                                                                                                                                                                                                     |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Payload URL  | <p><code><https://api.instabug.com/api/web/public/agent_webhooks/github?application_token=YOUR_APP_TOKEN></code></p><p>Replace <code>YOUR\_APP\_TOKEN</code> with your actual Luciq Application Token</p> |
| Content type | `application/json`                                                                                                                                                                                        |
| Secret       | The Webhook Secret you obtained from Luciq Support                                                                                                                                                        |

#### Step 4: Select Events

Under "**Which events would you like to trigger this webhook?**", choose:

* Status – Tracks CI status from external CI tools.
* Check run – Tracks CI status from GitHub Actions workflows.
* Pull request – Tracks PR merge status.

{% hint style="warning" %}
**Do not select “Send me everything”**
{% endhint %}

#### Step 5: Save and Test

1. Click **Add webhook**
2. GitHub will automatically send a ping event to test delivery
3. In **Recent Deliveries**, verify a successful response (green checkmark)

### Troubleshooting

If your webhook doesn’t appear to be working as expected:

* **No events showing in Luciq dashboard**
  * Make sure the correct events (`status`, `check_run`, `pull_request`) are selected
  * Confirm that the repo is connected to Luciq
* **Webhook returns 401 (Unauthorized)**
  * Double-check that you’re using the correct webhook secret provided by Luciq
  * Ensure the secret hasn’t expired or been changed
* **CI status not updating**
  * For GitHub Actions, confirm that your workflow triggers a `check_run` event
  * For external CI tools, make sure they emit GitHub `status` events
* **Merge not detected**
  * Verify that the pull request is merged into the same branch Resolve Agent is tracking (e.g. `main` or `master`)
  * Ensure the `pull_request` event is enabled in webhook settings

If the issue persists, contact our Support team with a screenshot of your webhook settings and recent deliveries.


# AI Debugging Assistant

Use Luciq's AI Debugging Assistant to get instant analysis of crashes and bugs in your mobile app. Get AI-powered insights, root causes, and suggested fixes.

## Overview

The AI Debugging Assistant is an intelligent analysis tool, one of the Resolve Agent capabilities. It transforms mobile crash debugging from a manual process into a structured path to resolution.

Rather than requiring developers to sift through opaque stack traces and fragmented logs, the Assistant consolidates telemetry data and applies AI-driven analysis to surface the root cause, reproduction steps, and pattern insights for every mobile crash.

### Key Capabilities

**Root Cause Analysis :** Plain-English explanations of failures with evidence-based context linking to specific code and network calls.

**Common Reproduction Steps :** Sequential user-action reconstructions that eliminate the “cannot reproduce” loop.

**Pattern Insights :** Multi-factor correlation analysis that identifies whether a crash is a global issue or a localized edge case.

### Where to Find It

When viewing any crash report, the Assistant panel is available in the right sidebar. All three capabilities (Root Cause, Reproduction, Patterns) are accessible from a single interface with no context-switching required.

<figure><img src="/files/JPmK58fDYaqOTKSaAvC0" alt="" width="375"><figcaption></figcaption></figure>

## Root Cause Analysis

Root Cause Analysis goes beyond the stack trace to provide a complete understanding of why a crash occurred. It is especially useful for opaque crashes such as OOMs or ANR crashes that lack a clear trail.

**Executive Summary :** A plain-English explanation of the failure written for quick comprehension. The summary describes what happened, what component was affected, and the likely trigger.

**Evidence-Based Context :** Direct links to the specific lines of code or network calls that were active at the moment of impact.

**Contributing Factors:** Supplementary insights into conditions that may have contributed to the crash. These can include memory pressure or leaks, elevated backend latency, specific OS or device configurations, and thread contention or deadlocks.

<div align="center"><figure><img src="/files/5fRh7qX4ERvoP8ZCNTpo" alt="" width="375"><figcaption></figcaption></figure></div>

## Common Reproduction Steps

The most time-consuming part of fixing a bug is proving it exists on your own machine. The Automated Reproduction Steps feature reconstructs the sequential user actions leading to a crash, eliminating guesswork and reducing the “cannot reproduce” cycle.

#### How It Works

The Assistant analyzes session telemetry, including screen navigation events and user interactions (taps, scrolls, input) to build a chronological timeline of what the user did before the crash.

<figure><img src="/files/d14LXemKokAyL5tK2i5h" alt="" width="375"><figcaption></figcaption></figure>

{% hint style="info" %}
You need to enable the user repro steps feature to be able to get this capability.
{% endhint %}

## Pattern Insights

Pattern Insights answer the critical triage question: is this a global catastrophe or a localized edge case? The feature correlates data points across your entire crash footprint to surface the common thread behind an issue.

#### Multi-Factor Correlations

Pattern Insights can identify compound conditions where a crash requires two or more factors to occur simultaneously. For example, the Assistant might report that a crash happens specifically when the Login V2 feature flag is enabled on iOS 26, combining both a feature flag and an OS version dimension into a single insight.

\
Workflow Guide: Choose Your Own Flow

The AI Debugging Assistant is designed around flexibility. While we recommend a default sequence, every developer has a different mental model for solving problems. The interface supports starting from any capability.

<figure><img src="/files/iDn6246QFWxl0R3xhH99" alt="" width="375"><figcaption></figcaption></figure>

## Recommended Flow

| Step | Capability          | Purpose                                     |
| ---- | ------------------- | ------------------------------------------- |
| 1    | Root Cause Analysis | Understand what happened and why            |
| 2    | Reproduction Steps  | Confirm how to trigger the issue locally    |
| 3    | Pattern Insights    | Determine the scope and impact of the issue |

## Alternative Flows

**Suspect a feature flag issue?** Start with Pattern Insights to quickly confirm whether a specific flag correlates with the crash, then move to Root Cause for the technical details.

**Visual learner?** Start with Reproduction Steps to see the user journey, then review Root Cause for the underlying technical explanation.

**Triaging for severity?** Start with Pattern Insights to assess scope (global vs. localized), then use Root Cause only if the issue warrants deeper investigation.


# Release Agent

Use Luciq's Release Agent to monitor your mobile app releases with AI. Get automated analysis of post-release performance, crash rates, and stability.


# PR Review

Use Luciq's Release Agent to automatically review pull requests for mobile impact. Get AI analysis of how code changes could affect app stability.

### Overview

AI PR Review helps engineering teams ship higher-quality code by analyzing pull requests using Luciq’s agents.\
It detects bugs, flags anti-patterns, identifies performance and security risks, and summarizes the PR - before your team spends time reviewing.

The result: fewer regressions, fewer production crashes and bugs, and faster review cycles.

### How It Works

Each PR triggers Luciq’s multi-stage review pipeline:

1. GitHub event received via the Luciq GitHub App
2. Luciq agent start analyse the PR
3. The agent generates summary & explanations
4. Results posted back to GitHub as review comments + PR summary

This creates a review experience that is fast, actionable, and grounded in real production intelligence.

<figure><img src="/files/5QkaTkUgVzxNxgys2oqC" alt=""><figcaption></figcaption></figure>

### Installation

#### 1. Install the Luciq GitHub App

Navigate to:\
**Settings** → **Source Code Management** → I**nstall GitHub App**

Grant repository access to the repos you want reviewed.

<figure><img src="/files/UaloH57SDmcQ1pCB9Qwq" alt=""><figcaption></figcaption></figure>

#### 2. Enable AI PR Review

Once the GitHub App is connected, you can enable or disable AI PR Review per application from the Settings page.

#### 3. Create or update a Pull Request

Mention Luciq inside the PR by commenting:

```
@luciq review
```

Luciq will immediately react to your comment with a thumbs up :thumbsup: to confirm that the review has started.\
Once the analysis is complete, Luciq will post the PR summary and risk assessment as a new comment.

{% hint style="info" %}
The thumbs up :thumbsup: reaction helps your team know that the AI review is in progress.
{% endhint %}

<figure><img src="/files/FvfN9iRqIoT9n7OngOc6" alt=""><figcaption></figcaption></figure>

### Security & Permissions

All AI processing happens within secure, isolated environments deployed inside Luciq’s infrastructure.\
Luciq only requests the minimum GitHub permissions required to:

* Read pull requests and diffs
* Write review comments
* Access relevant repository metadata

No code is stored, and no data leaves Luciq’s secure environment.

### Best Practices

* Enable Luciq PR Review on your core repositories first
* Use AI findings as a first pass before the developer do a deeper review
* Pay attention to high-risk flags - they often correlate with production issues

### FAQ

**Does Luciq modify my code?**\
No. Luciq only comments and recommends improvements.

**Can I disable the AI reviewer on certain repos?**\
Yes - permissions and enablement are per-application, also review added only when you mention luciq reviewer.


# Luciq MCP Server

Set up and use the Luciq MCP Server to connect AI coding agents to your mobile monitoring data. Enable AI-powered debugging and issue resolution workflows.

### Your production data, in your IDE, in seconds.

The **Luciq MCP Server** gives your AI coding assistant direct access to your mobile app's crash data, performance metrics, bug reports, app hangs, surveys, and user reviews. Query your production data using natural language, without leaving your editor.

{% hint style="success" %}
**Available on all plans.** MCP is enabled for every Luciq customer - no extra setup, no feature flags, no waiting. Just connect your IDE and start querying.
{% endhint %}

**33 tools across 14 areas.** Crashes, crash diagnostics, occurrences, app hangs, performance (APM), bug reports, app store reviews, surveys, an aggregated app-health overview, per-user summaries, session replay, a unified Issues & Opportunities surface, alerts & rules, and documentation search - all accessible through natural language prompts:

> "Show me the top crashes in production this week"\
> "Help me fix crash #42 - give me the full diagnostics"\
> "List my worst app launches this release"\
> "Triage bug #51: set it to Major and close it"\
> "Show me 1-star reviews for the latest release"

**Works with your IDE.** Cursor, Claude Code, Claude Desktop, VS Code / GitHub Copilot, Windsurf, JetBrains, and any MCP-compatible client.

***

### Demo

{% embed url="<https://streamable.com/ekvoh8>" %}

***

### What is MCP?

[Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is an open standard for connecting AI tools to external data sources. It lets your IDE's AI assistant fetch real data from services like Luciq using structured, secure API calls.

Luciq's MCP Server is the **first and only MCP integration purpose-built for mobile observability**. While general-purpose observability tools retrofit MCP support for web and backend data, Luciq's server is designed from the ground up for mobile: crash frames, device state, session context, performance metrics, app hangs, surveys, and app store reviews.

### Why Luciq MCP?

* **Mobile-native.** 33 tools designed for how mobile developers actually debug
* **Standardized.** Works across all MCP-compatible IDEs with zero custom SDKs
* **Secure.** OAuth 2.0, RBAC, audit logging, no source code required. Read-only except for four permissioned write tools (`update_bug`, `apm_funnel_write`, `write_alerts`, `write_incidents`)
* **Simple.** One URL, one-click OAuth, done

***

### Tools at a Glance

| Domain                     | Tool                          | What It Does                                                                                    |
| -------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------- |
| **Applications**           | `list_applications`           | List all apps in your workspace                                                                 |
| **Crashes**                | `list_crashes`                | Query crash reports with filters (time, version, device, status, feature flag, user attributes) |
| **Crashes**                | `crash_details`               | Full crash info with stack trace and metadata                                                   |
| **Crashes**                | `crash_patterns`              | Crash distribution by device, OS, app version                                                   |
| **Crashes**                | `crash_diagnostics`           | Full debugging payload for one crash (stack, metrics, flows)                                    |
| **Occurrences**            | `list_occurrences_tokens`     | List individual crash instances                                                                 |
| **Occurrences**            | `get_occurrence_details`      | Deep-dive into a single crash session                                                           |
| **App Hangs**              | `list_app_hangs`              | UI freeze and hang reports                                                                      |
| **Performance**            | `apm_list_groups`             | Rank APM groups worst-first (network, launch, screens, flows)                                   |
| **Performance**            | `apm_group_view`              | Panels for a single APM group (charts, tables, summary)                                         |
| **Performance**            | `apm_occurrence`              | Individual APM occurrences (worst, by token, or list)                                           |
| **Performance**            | `apm_funnel_events`           | Pickable events for building or editing funnels                                                 |
| **Performance**            | `apm_funnel_write`            | Create, update, or delete a funnel (write)                                                      |
| **Bugs**                   | `list_bugs`                   | User-reported bug reports via the Luciq SDK                                                     |
| **Bugs**                   | `bug_details`                 | Full bug detail with logs and device context                                                    |
| **Bugs**                   | `update_bug`                  | Triage a bug: status, priority, tags, or mark duplicate (write)                                 |
| **Reviews**                | `list_reviews`                | App store reviews and ratings                                                                   |
| **Surveys**                | `list_surveys`                | In-app surveys, NPS, and app-store prompts                                                      |
| **Surveys**                | `survey_details`              | A survey's questions, stats, and individual responses                                           |
| **App Health**             | `app_insights`                | Aggregated cross-product health snapshot for one app                                            |
| **Users**                  | `user_summary`                | One end user's devices, sessions, APM occurrences, crashes & bugs                               |
| **Session Replay**         | `list_session_replays`        | Recorded user sessions with dashboard deep links                                                |
| **Issues & Opportunities** | `list_issues`                 | Unified Issues across crashes, APM, AI-detected issues, and bugs                                |
| **Issues & Opportunities** | `ai_issue_details`            | Details and paginated occurrences for one AI-detected issue                                     |
| **Issues & Opportunities** | `ai_issue_occurrence_details` | One AI-detected issue occurrence, with its screenshots                                          |
| **Issues & Opportunities** | `list_opportunities`          | Prioritized Opportunities for an app, ranked by priority                                        |
| **Issues & Opportunities** | `opportunity_details`         | One opportunity's status, priority, source counts, and timeline                                 |
| **Alerts & Rules**         | `read_alerts`                 | List alert rules, inspect one, or fetch the rule-building catalog                               |
| **Alerts & Rules**         | `write_alerts`                | Create, update, or delete an alert rule (write)                                                 |
| **Alerts & Rules**         | `read_incidents`              | List or inspect triggered alerts (incidents)                                                    |
| **Alerts & Rules**         | `write_incidents`             | Resolve or reopen a triggered alert (write)                                                     |
| **Documentation**          | `search_documentation`        | Search Luciq's public documentation for guides and feature explanations                         |
| **Documentation**          | `get_documentation_page`      | Fetch the full markdown content of a documentation page                                         |

***

### Get Started

**New to MCP?** Start with the [Getting Started](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/getting-started) guide - you'll be connected in under two minutes.

**Ready to connect your IDE?** Jump straight to [Setup by IDE](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide) for Cursor, Claude Code, VS Code, and more.

***

### Explore

* [**Getting Started**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/getting-started) - Connect in under 2 minutes
* [**Setup by IDE**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide) - Cursor, Claude Code, VS Code, Claude Desktop, Windsurf, JetBrains
* [**Authentication & Security**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/authentication-and-security) - OAuth, tokens, permissions, and security details
* [**Tools Reference**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/mcp-tools-reference) - All 33 tools with parameters, prompts, and response fields
* [**Use Cases & Workflows**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/use-cases-and-workflows) - Real-world debugging workflows with prompt sequences
* [**FAQ & Troubleshooting**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/faq-and-troubleshooting) - Common issues and how to resolve them


# Getting Started

Get your first Luciq MCP response in under two minutes. Find your MCP settings in the dashboard, connect your IDE, and run a first query.

Get your first MCP response in under two minutes.

***

## Prerequisites

* A Luciq account (MCP is available on **all plans**, no extra setup required)
* An MCP-compatible IDE: Cursor, Claude Code, Claude Desktop, VS Code, or any MCP client

***

## Step 1: Find Your MCP Settings

MCP server settings live in your Luciq dashboard under **Account Management**.

1. Log in to your [Luciq dashboard](https://dashboard.luciq.ai)
2. Click your **profile avatar** (top-right corner)
3. Select **Account Management**
4. In the left sidebar, click **Luciq MCP**

![Navigate to Account Management > Luciq MCP](/files/1tvuZ24C02KrId2cidZf)

From this page you can:

* View integration settings for each supported MCP client
* Generate a personal access token (for token-based auth)
* See your current token and authentication status

***

## Step 2: Connect Your IDE

The **Luciq MCP** page in your dashboard shows setup instructions tailored to each supported client. Select your IDE there to get the exact configuration for your account.

You can also use the server URL directly:

```
https://api.luciq.ai/api/mcp
```

{% tabs %}
{% tab title="Cursor" %}
Create or edit `~/.cursor/mcp.json` with the following configuration:

```json
{
  "mcpServers": {
    "luciq": {
      "url": "https://api.luciq.ai/api/mcp"
    }
  }
}
```

{% endtab %}

{% tab title="Claude Code" %}
Run the following command in your terminal:

```bash
claude mcp add --transport http luciq https://api.luciq.ai/api/mcp
```

{% endtab %}

{% tab title="VS Code" %}
Open Command Palette (**Cmd+Shift+P**) > **MCP: Add Server** > select **HTTP** > paste the server URL:

```
https://api.luciq.ai/api/mcp
```

{% endtab %}

{% tab title="Claude Desktop" %}
Copy the configuration below and paste it into your `claude_desktop_config.json` file:

```json
{
  "mcpServers": {
    "luciq": {
      "url": "https://api.luciq.ai/api/mcp"
    }
  }
}
```

{% endtab %}

{% tab title="Other clients" %}
Copy the server URL below and add it to your MCP client settings as a remote MCP server:

```
https://api.luciq.ai/api/mcp
```

Your client will initiate a connection and open an OAuth authorization in your browser. Log in via your Luciq account and grant access.
{% endtab %}
{% endtabs %}

For detailed setup instructions, visit the [Setup by IDE](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide) page or check the setup guide on your dashboard's **Luciq MCP** page.

***

## Step 3: Try Your First Query

Once connected, try one of these prompts in your IDE:

{% hint style="success" %}
"List my applications"\
"Show me the top crashes in production this week"\
"What are the most common app hangs?"
{% endhint %}

![MCP connection test in Claude Code](/files/VCkisGDV1ANJhDzLT5q2)

If you see your apps or crash data, you're connected and ready to go.

***

## What's Next?

* [**Setup by IDE**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide) - Detailed guides for every supported IDE
* [**Use Cases & Workflows**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/use-cases-and-workflows) - Real-world debugging workflows with prompt sequences
* [**Tools Reference**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/mcp-tools-reference) - All 33 tools documented with parameters and examples
* [**Authentication & Security**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/authentication-and-security) - OAuth, tokens, permissions, and security details
* [**FAQ & Troubleshooting**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/faq-and-troubleshooting) - Common issues and how to resolve them


# Setup by IDE

Connect the Luciq MCP Server to Cursor, Claude Code, Claude Desktop, VS Code, or any MCP client. OAuth handles authentication for you.

Connect Luciq MCP to your IDE. OAuth handles authentication automatically - just add the server URL and approve access in your browser.

{% hint style="info" %}
**Your dashboard has the latest setup instructions.** Go to **Account Management > Luciq MCP** in your Luciq dashboard for per-client setup guides tailored to your account, including a one-click Connect option for Cursor.
{% endhint %}

### Server URL

```
https://api.luciq.ai/api/mcp
```

***

## Cursor

Create or edit `~/.cursor/mcp.json` with the following configuration:

```json
{
  "mcpServers": {
    "luciq": {
      "url": "https://api.luciq.ai/api/mcp"
    }
  }
}
```

Or use the **Connect** button on your dashboard's Luciq MCP settings page to open Cursor and install the Luciq MCP server directly.

![Cursor Connect button on the Luciq MCP dashboard](/files/K5HknuFr0gjBeNV6hSIO)

***

## Claude Code

Run the following command in your terminal:

```bash
claude mcp add --transport http luciq https://api.luciq.ai/api/mcp
```

The OAuth flow will open your browser automatically for authorization.

***

## VS Code / GitHub Copilot

Open Command Palette (**Cmd+Shift+P**) > **MCP: Add Server** > select **HTTP** > paste the server URL:

```
https://api.luciq.ai/api/mcp
```

***

## Claude Desktop

Copy the configuration below into your `claude_desktop_config.json` file:

```json
{
  "mcpServers": {
    "luciq": {
      "url": "https://api.luciq.ai/api/mcp"
    }
  }
}
```

**Config file location:**

| Platform | Path                                                              |
| -------- | ----------------------------------------------------------------- |
| macOS    | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows  | `%APPDATA%\Claude\claude_desktop_config.json`                     |

{% hint style="info" %}
**Prefer a connector over a config file?** You can add Luciq to the Claude apps as a custom connector, including provisioning it for your whole organization in one step. See [Set Up the Claude Connector](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/claude-connector).
{% endhint %}

***

## Other MCP Clients

Any client that supports the [MCP standard](https://modelcontextprotocol.io/) can connect to Luciq. Add the server URL as a remote MCP server in your client's settings:

```
https://api.luciq.ai/api/mcp
```

Your client will initiate a connection and open an OAuth authorization in your browser. Log in with your Luciq account and grant access.

***

## Verifying Your Connection

After setup, try this prompt in your IDE:

{% hint style="success" %}
"List my applications"
{% endhint %}

If you see your Luciq apps listed, you're connected and ready to go.

If the connection isn't working, check the [FAQ & Troubleshooting](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/faq-and-troubleshooting) page for common issues and IDE-specific fixes.

***

## Using Token-Based Auth Instead of OAuth

If you're in an environment where interactive OAuth isn't practical (CI/CD pipelines, remote terminals over SSH, automated workflows), you can use token-based authentication. See [Authentication & Security](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/authentication-and-security#token-based-authentication) for setup instructions.


# Set Up the Claude Connector

Set up the Claude AI connector for the Luciq MCP Server. Connect Claude to your mobile monitoring data to enable AI-powered debugging workflows.

Add Luciq to Claude as a custom connector and query your crash reports, bug reports, app hangs, and app store reviews in plain language, right inside Claude. Once connected, the Luciq connector is available wherever you use Claude.

{% hint style="info" %}
Custom connectors are available on Claude **Pro**, **Max**, **Team**, and **Enterprise** plans. For Anthropic's reference guide, see [Get started with custom connectors](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).
{% endhint %}

## Why connect Luciq to Claude?

* **Works alongside the rest of your stack.** Adding the connector makes Luciq part of your Claude workspace, so Claude can work across your Luciq data and your other connectors, like GitHub, Linear, and Slack, in one place.
* **Not just chat, automate it.** Once connected, anything running in your Claude workspace can query Luciq, including Claude routines and Managed Agents that run unattended. It pairs naturally with the [Anthropic Routine integration](https://docs.luciq.ai/product-guides-and-integrations/integrations/anthropic-routine): a Luciq event triggers your routine, and the routine can then pull live context straight from Luciq before it acts.
* **One setup for the whole team.** On Team and Enterprise plans, an admin adds the connector once and everyone in the organization gets it automatically.
* **Secure by design.** Connections use OAuth and respect your existing Luciq roles and permissions.

### Server URL

```
https://api.luciq.ai/api/mcp
```

That's the only value you need. Luciq signs you in through a secure browser authorization, so you can leave **Advanced settings** empty.

***

## Set it up for your whole team (Team & Enterprise)

An Owner or Primary Owner adds the connector once, and it becomes available to everyone in the organization automatically, like any other company-provided tool.

**Owner or Primary Owner:**

1. Go to **Settings > Organization settings > Connectors**.
2. Click **Add**, hover over **Custom**, and choose **Web**.
3. In **Remote MCP server URL**, paste:

   ```
   https://api.luciq.ai/api/mcp
   ```
4. (Optional) Name it **Luciq**.
5. Click **Add**.

<figure><img src="/files/NQP7ozpGF9yreqAYRE6K" alt="Claude&#x27;s Add custom connector dialog, showing the Name and Remote MCP server URL fields"><figcaption><p>Adding Luciq as a custom connector in Claude</p></figcaption></figure>

**Everyone else on the team:**

1. Go to **Customize > Connectors**.
2. Find the **Luciq** connector.
3. Click **Connect** and sign in with your Luciq account to authorize.

***

## Set it up for yourself (Pro & Max)

1. In Claude, open **Customize > Connectors**.
2. Click **+**, then select **Add custom connector**.
3. Enter a **Name** (for example, Luciq) and paste the **Remote MCP server URL**:

   ```
   https://api.luciq.ai/api/mcp
   ```
4. Click **Add**, then sign in with your Luciq account to authorize.

***

## Use it in a conversation

1. In any chat, click the **+** in the message box.
2. Select **Add connectors** and toggle **Luciq** on.

Then try:

{% hint style="success" %}
"List my applications"
{% endhint %}

If you see your Luciq apps, you're connected and ready to go.

***

## Next steps

* [Use Cases & Workflows](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/use-cases-and-workflows) - real debugging workflows with prompt sequences
* [MCP Tools Reference](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/mcp-tools-reference) - every tool documented with parameters and examples
* [FAQ & Troubleshooting](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/faq-and-troubleshooting) - common issues and how to resolve them


# Authentication & Security

Learn how to authenticate and secure the Luciq MCP Server. Configure OAuth tokens and security settings for safe AI agent access to your app data.

Luciq MCP supports two authentication methods. **OAuth is recommended** for interactive use; token-based auth is available for automation and CI/CD.

***

## OAuth 2.0 (Recommended)

When you connect your IDE to Luciq MCP, OAuth handles everything automatically:

1. Your IDE discovers the authorization server (via the standard `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server` metadata)
2. Your browser opens to the Luciq authorization page
3. Log in with your Luciq account and approve access
4. Your IDE exchanges the authorization code for a token and stores it - this is a one-time step

OAuth uses the **authorization-code grant with PKCE** (`S256`) and **Dynamic Client Registration** (DCR), so no pre-configuration, client secrets, or API keys are needed. Your IDE registers itself and reports its client name automatically during the authorization flow, that's the name you'll see on the consent screen.

### Revoking OAuth Access

When you authenticate via OAuth, a token is generated automatically. To revoke access:

1. Go to **Account Management > Luciq MCP** in your dashboard
2. Switch to the **Token** tab
3. Delete the active token

Deleting the token cancels all credentials associated with your account, including those generated through the OAuth flow. The next time your IDE tries to connect, it will prompt you to re-authorize.

***

## Token-Based Authentication

For environments where interactive OAuth isn't practical - CI/CD pipelines, remote terminals, automated agent workflows, or programmatic integrations.

### Getting a Token

1. Go to **Account Management > Luciq MCP** in your Luciq dashboard
2. Switch to the **Token** tab
3. Click **Generate authentication token**
4. Copy the token immediately (it's shown once for security)

![Token generation on the Luciq MCP settings page](/files/v2ESfYtcJGnhiYj7OHkI)

{% hint style="info" %}
**One token per account, shown once.** Each developer has a single MCP token. The full value is displayed only at generation, after that the settings page shows just a short hint (the last few characters) so you can recognize it. To get a new token, **revoke the existing one first**, then generate again. The same token backs both header-based auth and the OAuth flow, so revoking it also signs out any connected IDE.
{% endhint %}

### Using the Token

Add the `Email` and `Token` headers to your MCP server configuration:

```json
"luciq": {
  "url": "https://api.luciq.ai/api/mcp",
  "headers": {
    "Email": "your-email@company.com",
    "Token": "your-authentication-token"
  }
}
```

{% hint style="warning" %}
**Keep your tokens safe.** Never commit tokens to version control, share them in Slack or email, or include them in public documentation. If a token is compromised, revoke it immediately from your MCP settings.
{% endhint %}

### Rotating Tokens

Generate a new token from the MCP settings page at any time. Previous tokens can be revoked from the same page.

***

## Permissions & RBAC

MCP access respects your existing Luciq role-based access controls. Authorization is enforced on every tool call in three layers:

1. **MCP access.** Your role must grant MCP access at all (the `account_management.mcp.view` permission). Without it, no tool will run.
2. **Per-tool permission.** Each tool maps to the same dashboard permission it would use in the UI. For example, `list_crashes` needs crash-list view access, `survey_details` needs both survey-details and survey-responses view access, and `update_bug` needs bug-modify access (plus an extra tag-modify permission when you change tags). If your role is missing the permission a tool needs, that call is rejected and the error names the missing permission.
3. **Plan entitlement.** Tools for a product your plan doesn't include (e.g. APM, Surveys, Crash Reporting) are rejected with a "not included in your plan" message.

Beyond those layers:

* **App-level access:** You can only query data for apps your Luciq account has permissions for
* **Environment-level access:** Production, beta, staging, and other environments follow your existing permission model
* **Role inheritance:** MCP tools inherit the same permissions as the Luciq dashboard - no separate MCP-specific roles
* **Admin control:** Workspace admins manage access through standard Luciq team settings

{% hint style="info" %}
**All tools are always listed, but not always callable.** Your client always sees the full set of 33 tools. Whether a given tool succeeds depends on your role and plan, checked at call time. So a tool you aren't entitled to won't disappear from the list, it will return a clear permission or plan error if you call it.
{% endhint %}

If you're seeing "App not found" or missing data, check with your workspace admin to verify your permissions.

***

## Security Overview

| Aspect             | Detail                                                                                                                 |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| **Transport**      | HTTPS (TLS 1.2+), Streamable HTTP protocol (JSON-RPC 2.0)                                                              |
| **Authentication** | OAuth 2.0 (authorization-code + PKCE, with Dynamic Client Registration) recommended, or header-based token             |
| **Authorization**  | RBAC - inherits your Luciq workspace permissions, plus plan-feature entitlement, checked on every tool call            |
| **Data access**    | **Read-only except `update_bug`,** which can modify a bug's triage state; all writes respect RBAC and are audit-logged |
| **Source code**    | No source code is accessed, transmitted, or stored                                                                     |
| **Audit logging**  | MCP write actions are logged for compliance and auditing, just like dashboard changes                                  |
| **Data residency** | Data stays within your Luciq workspace region                                                                          |

### What Data Does MCP Access?

MCP tools query the same observability data available in your Luciq dashboard:

* Crash reports, stack traces, diagnostics, and occurrence data
* App hang / freeze reports
* Performance (APM) metrics - network, launches, screen loads, flows, frame drops
* User-reported bug reports (submitted via the Luciq SDK)
* In-app surveys, NPS, and their responses
* App store reviews and ratings
* Aggregated app-health insights
* Per-user summaries (devices, sessions, APM occurrences, crashes, and bugs for one end user)
* Recorded session replays and their metadata
* Unified Issues and prioritized Opportunities
* Application metadata (names, platforms, environments)

Two tools, `search_documentation` and `get_documentation_page`, are the exception: they don't touch your workspace data at all. They query Luciq's public documentation site (docs.luciq.ai) to answer SDK integration and feature questions, which is why they're the only tools marked `openWorldHint: true`.

MCP does **not** access:

* Source code or repository contents
* User PII beyond what's already in crash/bug reports (email, device info)
* SDK configuration or integration settings
* Billing or account information

***

## Rate Limits

MCP requests are rate-limited per client (by IP). The default limit is **100 requests per 60 seconds**. If you exceed it, the server responds with a `429` and `Rate limit exceeded`, wait briefly and retry. In practice, conversational use stays well under this; only tight automated loops tend to approach it.

***

## Clustered Tenants

If your organization uses a dedicated Luciq cluster, your MCP endpoint URL may differ from the default. Contact your Luciq admin or reach out to <support@luciq.ai> for your cluster-specific endpoint.

***

## Enterprise & Compliance

For enterprise security reviews, compliance questionnaires, or custom deployment requirements, contact <support@luciq.ai>.


# MCP Tools Reference

Complete reference guide for all Luciq MCP Server tools. Explore available tools, their parameters, and how to use them for AI-powered debugging.

Luciq MCP provides **33 tools** across 14 areas. Most are read-only; four write tools (`update_bug`, `apm_funnel_write`, `write_alerts`, and `write_incidents`) can modify data in your workspace. All tools use JSON-RPC 2.0 over Streamable HTTP and respect your workspace permissions (RBAC).

| **Area**                                 | **Tools**                                                                                                     | **What they cover**                                                                                   |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **App Context**                          | `list_applications`                                                                                           | Which apps you can work with                                                                          |
| **Crash-Level Debugging**                | `list_crashes`, `crash_details`, `crash_patterns`, `crash_diagnostics`                                        | Crash groups, their details & full debugging payload                                                  |
| **Occurrences Deep-dive**                | `list_occurrences_tokens`, `get_occurrence_details`                                                           | Single crash instances (per device/session)                                                           |
| **Stability Beyond Crashes (App Hangs)** | `list_app_hangs`                                                                                              | App freezes / UI hangs                                                                                |
| **Performance (APM)**                    | `apm_list_groups`, `apm_group_view`, `apm_occurrence`, `apm_funnel_events`, `apm_funnel_write`                | Network, launches, screen loads, flows, frame drops, funnels                                          |
| **User-Reported Issues (Bugs)**          | `list_bugs`, `bug_details`, `update_bug`                                                                      | User-reported issues via Luciq SDK (read & triage)                                                    |
| **User Sentiment & Store Ratings**       | `list_reviews`                                                                                                | User reviews and ratings                                                                              |
| **Surveys**                              | `list_surveys`, `survey_details`                                                                              | In-app surveys, NPS, and their responses                                                              |
| **App Health Overview**                  | `app_insights`                                                                                                | Aggregated cross-product health snapshot                                                              |
| **Users**                                | `user_summary`                                                                                                | One end user's devices, sessions, APM occurrences, crashes & bugs in a single call                    |
| **Session Replay**                       | `list_session_replays`                                                                                        | Recorded user sessions with dashboard deep links                                                      |
| **Issues & Opportunities**               | `list_issues`, `ai_issue_details`, `ai_issue_occurrence_details`, `list_opportunities`, `opportunity_details` | Unified Issues surface (across crashes, APM, AI-detected issues & bugs) and prioritized Opportunities |
| **Alerts & Rules**                       | `read_alerts`, `write_alerts`, `read_incidents`, `write_incidents`                                            | Alert rules and the triggered alerts (incidents) they produce                                         |
| **Documentation**                        | `search_documentation`, `get_documentation_page`                                                              | Search and fetch published Luciq documentation pages                                                  |

{% hint style="info" %}
**Read vs. write.** Four tools change data: `update_bug` (a bug's status, priority, tags, or duplicate state), `apm_funnel_write` (create, update, or delete a funnel), `write_alerts` (create, update, or delete an alert rule), and `write_incidents` (resolve or reopen a triggered alert). Every other tool only reads. One read tool, `crash_diagnostics`, has a side effect: it computes and caches a crash-analysis payload on demand (so it isn't a pure read), but it never changes your crash, bug, or app data. Write actions respect your RBAC permissions and are audit-logged like any dashboard action.
{% endhint %}

Each tool exposes [MCP tool annotations](https://modelcontextprotocol.io/) so your client can reason about its behavior. All tools are `readOnlyHint: true` except the write tools (`update_bug`, `apm_funnel_write`, `write_alerts`, `write_incidents`, marked `readOnlyHint: false`) and `crash_diagnostics` (`readOnlyHint: false`, `destructiveHint: false`, because it triggers an on-demand computation). Every tool is scoped to your Luciq workspace and is `openWorldHint: false`, except `search_documentation` and `get_documentation_page`, which query Luciq's public documentation site and are `openWorldHint: true`.

The details and context for each tool are detailed below.

### **1. App Context**

***

### `1.1 list_applications`

#### What it does

Returns all applications accessible to your account.

#### Use this when

* Setting up your MCP config and not sure which `slug` / `mode` to use.
* You work across multiple apps and want a quick list in the IDE.

#### Parameters

**None required.**

Optional:

* `platform`: `ios`, `android`, `react_native`, `flutter`
* `limit`, `offset`

#### Key Fields

* **slug** — Identifier used in most tools
* **name** — Display name
* **token** — Needed for the Reviews tool
* **platform** — App platform
* **mode** — App environment
* **created\_at** — Timestamp

#### Usage Examples

* “List all my applications.”
* “Show only iOS applications.”
* “Which apps do I have access to?”

### **2. Crash-Level Debugging**

***

### `2.1 list_crashes`

#### What it does

Shows crash groups for an app: how often they happen, how many users they affect, and basic cause.

#### Use this when

* You want to know “what should we fix first?”
* You’re scanning production for new, recent, or high-impact crashes.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `date_ms` (time window, defaults to the last 7 days when omitted)
* `status_id` (`1` open, `2` closed, `3` in progress)
* `devices`, `os_versions`
* `app_versions`
* `current_views`, `teams`
* `platform` — `IOS`, `ANDROID`, `DART` (Flutter), `JAVASCRIPT` (React Native)
* `type` — `CRASH`, `ANR`, `OOM`, `NON_FATAL` (which apply depends on platform: Android supports `CRASH`, `ANR`, `NON_FATAL`; iOS supports `CRASH`, `OOM`, `NON_FATAL`; React Native and Flutter support all four)
* `subtype` — `CRITICAL`, `ERROR`, `WARNING`, `INFO` (only when `NON_FATAL` is included in `type`)
* `feature_flags` — scope to crashes seen under a given feature flag or experiment. Use the bare flag name when no variant was set (e.g. `private_mode`), or `flag_name -> variant` when a variant was set (e.g. `checkout_flow -> external_request`).
* `user_attributes` — filter by custom user attributes reported by the SDK (e.g. `Country`, `Plan`). Each attribute name maps to one or more conditions using the operators `contain`, `not_contain`, `equal`, or `not_equal`; conditions on the same attribute combine, and different attributes are AND-ed together. Attribute-name keys are case-sensitive and must match the SDK-reported name exactly; condition values are matched case-insensitively.
* `user_uuids` — keep only crashes that affected these users, identified as the SDK reported them and matched exactly (up to 100)

#### Sorting & Pagination

* `sort_by` — `last_occurred_at` (default), `occurrences_counter`, `affected_users_counter`, `max_app_version`, `min_app_version`, `severity`, `first_occurred_at`
* `direction` — `asc` or `desc` (default `desc`)
* `limit` (default 20, max 50), `offset`

#### Key Fields

* **number** — Crash ID
* **exception** — Main exception message
* **crash\_cause** — File/function of failure
* **crash\_type** — Fatal or non-fatal
* **occurrences\_counter** — Total occurrences
* **affected\_users\_counter** — Unique users affected
* **app\_version** — Version where it occurred
* **last\_occurred\_at** — Latest timestamp
* **severity / level** — Severity indicators

#### Usage Examples

* “Show production crashes for the last 7 days.”
* “List crashes for version 3.0.1.”
* “Show open crashes only.”
* “What are the top Android crashes?”
* “Show crashes seen under the `checkout_flow` feature flag.”
* “Show crashes from users in Egypt on the free plan.”

### `2.2 crash_details`

#### What it does

Shows everything we know about a single crash (stack, versions, status, severity).

#### Use this when

You need to investigate or reproduce the crash.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` crash number

#### Key Fields

* **exception** — Full exception
* **exception\_name** — Exception class/type
* **crash\_cause** — Main file/line
* **stack\_frames\[]** — Parsed stack trace
* **min\_app\_version**, **max\_app\_version** — Affected versions
* **crash\_type** — Fatal/non-fatal
* **status\_id** — Current status
* **team** — Assigned team
* **sdk\_version** — SDK version
* **package / ndk\_info / path** — Platform extra fields

#### Usage Examples

* “Show details for crash #12.”
* “Explain the stack trace for crash 45.”
* “Which file caused crash #17?”
* “What versions are affected by crash 5?”

### `2.3 crash_patterns`

#### What it does

Groups a crash’s occurrences by **device**, **app version**, **OS**, **view**, etc. to show where it clusters.

#### Use this when

* You want to understand where a crash is concentrated.
* You want to answer: “Is this crash mostly on Pixel 8? On Android 14? On version 3.0.4?”

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` crash number

#### Useful Filters

* `pattern_key` — which dimension to group by: `app_versions` (default), `devices`, `oses`, `current_views`, `app_status`, `experiments`
* `date_ms` (time window)
* `app_versions`, `devices`, `os_versions`
* `sort_by` — `occurrences_count` (default), `last_seen`, `first_seen`; `direction` — `asc` or `desc` (default `desc`)

#### Key Fields

* **value** — Group label (device, OS, version, etc.)
* **occurrences\_count** — Occurrences in that bucket
* **first\_seen**, **last\_seen** — Timestamp range

#### Usage Examples

* “Break down crash #20 by device.”
* “Show OS patterns for crash #12.”
* “Which views are tied to crash #3?”
* “Group crash #5 by app versions.”

### `2.4 crash_diagnostics`

#### What it does

Returns the **full debugging payload** for a single crash in one call: stack trace, device metrics, distributions (OS, device model, app version), and the screen flows leading up to the crash. It’s the fastest way to hand an AI assistant everything it needs to reason about a crash.

#### Use this when

* You ask your assistant to **fix, debug, or investigate** a specific crash.
* You want the stack trace plus the surrounding context (which devices, OS versions, and user journeys are affected) without chaining several calls.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` crash number

#### Notes

* This analysis is computed on demand. If the response comes back with `status: "generating"`, the payload is still being prepared, simply ask again after a moment and it will return the full result.

#### Usage Examples

* “Help me fix crash #42.”
* “Give me the full diagnostics for crash #18.”
* “What led up to crash #7 and which devices are hit hardest?”

### **3. Occurrences Deep Dive**

***

### `3.1 list_occurrences_tokens`

#### What it does

Lists individual **occurrences** of a crash as ULID tokens, so you can pick specific ones to inspect.

#### Use this when

* You want to inspect or debug specific sessions.
* You want to drill down from a crash group to specific user/device sessions.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` crash number

#### Useful Filters

* `app_status` (foreground / background)
* `devices`
* `os_versions`
* `app_versions`
* `experiments`
* `current_views`
* `date_ms` (date range, defaults to the last 7 days when omitted)
* `user_uuids` — keep only occurrences from these users, identified as the SDK reported them and matched exactly (up to 100). Useful for narrowing a crash down to the occurrences a specific user hit, so the returned tokens resolve to that user's device, OS, and logs.

#### Key Fields

* **states\_tokens\[]** — ULIDs for occurrences
* **total\_occurrences** — Count of matches

#### Usage Examples

* “List all occurrences for crash #28.”
* “Show only foreground occurrences.”
* “Which iOS 17 devices experienced crash 5?”
* “List occurrences from Pixel devices.”

### `3.2 get_occurrence_details`

#### What it does

Shows the **exact context** of one crash occurrence: device, OS, memory, storage, app status, user, and log URLs.

#### Use this when

You need to reproduce or understand a single session.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` crash number
* `ulid` state/occurrence ULID token (obtained from `list_occurrences_tokens`)

#### Key Fields

**state.fields:**

* **app\_version** — Version at crash moment
* **device**, **os** — Device info
* **current\_view** — Active screen
* **app\_status** — Foreground/background
* **memory**, **storage** — Resource usage
* **country**, **city** — Location
* **screen\_size**, **density** — Display metrics
* **reported\_at** — Timestamp
* **email**, **user\_name** — User identity

**logs:**

* Downloadable compressed logs
* Experiment logs

**user:**

* Email, UUID, name

**exception\_message:**

* Exception for this specific occurrence

#### Usage Examples

* “Show occurrence details for token X.”
* “Which device caused this occurrence?”
* “Show logs for the earliest occurrence of crash #8.”
* “What view was active during this crash?”

### **4. Stability Beyond Crashes (App Hangs)**

***

### `4.1 list_app_hangs`

#### What it does

Shows grouped **hang** events (UI freezes) for your application.

The server automatically chooses:

* `FATAL_UI_HANG` for iOS
* `ANDROID_FATAL_HANG` for Android
* Both for cross-platform apps

#### Use this when

You want to find “the app froze for me” issues, not just crashes.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `date_ms` (defaults to the last 7 days when omitted), `status_id` (`1` open, `2` closed, `3` in progress)
* `app_versions`, `devices`, `os_versions`
* `platform` — `IOS`, `ANDROID`, `DART`, `JAVASCRIPT`
* `current_views`, `teams`
* `user_uuids` — keep only hangs that affected these users, identified as the SDK reported them and matched exactly (up to 100)
* `sort_by` — `last_occurred_at` (default), `occurrences_counter`, `affected_users_counter`, `max_app_version`, `min_app_version`, `severity`, `first_occurred_at`; `direction` — `asc`/`desc` (default `desc`)

The hang type (`FATAL_UI_HANG` / `ANDROID_FATAL_HANG`) is chosen automatically by platform, you don't pass it. Unlike `list_crashes`, hangs don't take `type`, `subtype`, or `feature_flags` filters.

#### Key Fields

* **number** — Hang ID
* **crash\_type** — Hang classification
* **exception** — Hang summary
* **crash\_cause** — Where it froze
* **occurrences\_counter** — Total hangs
* **affected\_users\_counter** — Unique impacted users
* **platform**, **app\_version**
* **last\_occurred\_at** — Recent hang timestamp

#### Usage Examples

* “Show hangs in production for the last 14 days.”
* “List iOS hangs only.”
* “Which hangs are still open?”
* “What views cause most UI hangs?”

### **5. Performance (APM)**

***

The APM tools cover the full Application Performance Monitoring surface. Every APM tool takes a required `metric` parameter that selects the performance domain:

* `network` — network requests
* `launch` — app launches (cold/warm)
* `flows` — custom traces / flows
* `screen_loading` — screen load times
* `frame_drop` — UI frame drops
* `funnels` — multi-step conversion funnels (`apm_list_groups` / `apm_group_view` only)

The first three tools form a drill-down: rank groups (`apm_list_groups`) → inspect one group’s panels (`apm_group_view`) → inspect individual occurrences (`apm_occurrence`). The two funnel tools manage funnels themselves: discover pickable events (`apm_funnel_events`) → create, update, or delete a funnel (`apm_funnel_write`). Filters and sort keys are scoped per metric, the server rejects a key that doesn't apply to the chosen metric (e.g. failure-rate filters are network-only). Your AI assistant picks valid combinations from the schema, so you can describe what you want in plain language.

### `5.1 apm_list_groups`

#### What it does

Ranks APM groups worst-first for an app (e.g. slowest endpoints, slowest launches, slowest screens), paginated and filtered.

#### Use this when

* You want to know “what’s our worst-performing endpoint / screen / launch?”
* You’re scanning for performance regressions in a release.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `metric` `network`, `launch`, `flows`, `screen_loading`, `frame_drop`, `funnels`

#### Useful Filters

* `date_ms` (defaults to the last 7 days when omitted), `app_version`, `platform` (`ios`, `android`)
* `apdex`, `apdex_change`, `count`, `dissat_count` (numeric ranges; not applicable to `launch`)
* `95th_percentile_ms`, `50th_percentile_ms` (not applicable to `launch` or `frame_drop`)
* `total_failure_rate`, `client_failure_rate`, `server_failure_rate` (network only)
* `view_type` (screen\_loading only), `teams` (screen\_loading / frame\_drop), `key_metric`, `group_name`, `user_attributes`
* `user_uuids` — keep only groups with occurrences from these users, identified as the SDK reported them and matched exactly (up to 100)
* `occurrence_classification` — restrict to occurrences in these Apdex classes: `satisfying`, `tolerable`, `frustrating` (rejected for `funnels`; use `session_classification` there instead)

#### Sorting & Pagination

* `sort` by one key, `direction` `asc`/`desc`. Valid keys: `apdex`, `apdex_change`, `occurrences`, `dissat_count` (all metrics); `p95`, `p50` (all except frame\_drop); `failure_rate` (network only); `frozen_frames_percent`, `slow_frames_percent` (frame\_drop only)
* `limit` (default 5, max 100), `offset`

#### Usage Examples

* “List my worst app launches.”
* “Show the slowest network endpoints this week.”
* “Which screens have the lowest Apdex?”

### `5.2 apm_group_view`

#### What it does

Fetches the panels for a single APM group (charts, tables, and summary) in one call.

#### Use this when

You’ve identified a problem group and want the full breakdown (trends, distributions, summary metrics).

#### Required

* `slug`, `mode`, `metric`
* `views` — which panels to return. Available panels (which apply depends on the metric): `summary`, `apdex_chart`, `throughput_chart`, `spans_table`, `dimensions` (all metrics); `outliers` (all except frame\_drop); `failure_rate` (network only); `stages_breakdown` (launch, screen\_loading); `web_vitals` (screen\_loading only); `frames_distribution`, `delayed_frames` (frame\_drop only). Panels not valid for the metric are dropped and reported back in `ignored_views`.
* A group identifier: either `group_uuid` (preferred, from `apm_list_groups`) or `group_url` (with optional `method` for network endpoints)

#### Useful Filters

* `date_ms` (defaults to the last 7 days when omitted)
* `user_uuids` — match only occurrences from these users, identified as the SDK reported them and matched exactly (up to 100). Not supported by the `spans_table` / `frames_distribution` views, which return `error: filter_unsupported_for_view` instead.
* `occurrence_classification` — restrict to occurrences in these Apdex classes: `satisfying`, `tolerable`, `frustrating` (rejected for `funnels`; use `session_classification` there instead). Same `spans_table` / `frames_distribution` limit as `user_uuids`.

#### Usage Examples

* “Show me the breakdown for the slowest endpoint.”
* “Give me the summary panel for this screen-load group.”

### `5.3 apm_occurrence`

#### What it does

Inspects individual occurrences within an APM group: the single worst occurrence, a specific occurrence by token, or a paginated list.

#### Use this when

You want the concrete, individual sample behind an aggregate, e.g. the exact slowest request or launch.

#### Required

* `slug`, `mode`, `metric`
* `selector` — `worst`, `by_token`, or `list`
* A group identifier: `group_uuid` (preferred) or `group_url` (+ optional `method`)
* `token` when `selector=by_token`

#### Useful Filters

* `date_ms` (defaults to the last 7 days when omitted)
* `user_uuids` — match only occurrences from these users, identified as the SDK reported them and matched exactly (up to 100)
* `occurrence_classification` — restrict to occurrences in these Apdex classes: `satisfying`, `tolerable`, `frustrating`

#### Usage Examples

* “Show me the single worst occurrence for this endpoint.”
* “List the slowest launches in this group.”

### `5.4 apm_funnel_events`

#### What it does

Lists the pickable events (network and screen-loading groups) you can use as steps when building or editing a funnel.

#### Use this when

You’re about to create or update a funnel and need the valid step candidates and their identifiers.

#### Required

* `slug`, `mode`

Optional:

* `event_type` — restrict the picker to `network` or `screen_loading`; when omitted, returns the top results across both types
* `q` — case-insensitive substring filter on the event name
* `limit` — per-type cap on returned events (default 20, max 25)

#### Usage Examples

* “What events can I build a funnel from?”
* “Find checkout-related network events I can use as funnel steps.”

### `5.5 apm_funnel_write`

#### What it does

Creates, updates, or deletes a funnel, chosen via the `operation` parameter. Each step can be an OR group of up to 5 events — a session completes the step when **any** of them matches. This is a **write** tool.

#### Use this when

You want to define or maintain a conversion funnel without leaving your IDE — then analyze it with `apm_list_groups` / `apm_group_view` using `metric=funnels`.

#### Required

* `slug`, `mode`
* `operation` — `create`, `update`, or `delete`
* For `create`: `name` + `steps` (2–20 ordered steps)
* For `update`: `ulid` and at least one of `name` / `steps` (when `steps` is given it **replaces** the full ordered set of steps)
* For `delete`: `ulid`

`steps` is an ordered array of 2–20 steps. Each step is an array of 1–5 event objects — an OR group where the step completes as soon as any one of its events occurs. Each event has a `type` (`user_event`, `network`, or `screen_loading`); for `type=user_event`, provide `name`, and for `network` / `screen_loading`, provide the group `ulid` obtained from `apm_funnel_events`.

#### Usage Examples

* “Create a funnel from login → add to cart → checkout.”
* “Create an ‘add to cart’ step that matches whether it happened from the product page or the cart drawer.”
* “Replace the last step of this funnel with the new payment screen.”
* “Delete the old onboarding funnel.”

### **6. User-Reported Issues**

***

### `6.1 list_bugs`

#### What it does

Shows **user-reported bugs** (reported via Luciq’s SDK), with simple filtering.

#### Use this when

* You want to see user-submitted issues.
* You’re scanning for new or high-priority bugs in a release.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `status_id`: `1` New, `2` Closed, `3` In Progress
* `priority_id`: `-1` N/A, `1` Trivial, `2` Minor, `3` Major, `4` Blocker
* `app_version`, `os_versions`, `devices`
* `platform`: `ios`, `android` (cross-platform apps only)
* `device_class`: `high`, `medium`, `low` (Android apps only)
* `category` / `no_category`, `tag` / `no_tag` (include/exclude; each filter is a list of OR-groups that are AND-ed together)
* `type`, `experiments`, `email` (with an `equal` / `not_equal` / `contain` / `not_contain` operator)
* `duplicate_type`: `single`, `manual_master`, `automatic_master`, `manual_duplicate`, `automatic_duplicate` — omitted defaults to `single` + masters only (merged duplicates hidden); pass all 5 values to include them
* `forwarded_to` / `not_forwarded_to` (e.g. `jira`)
* `reported_at` (range, in Unix epoch **milliseconds**; defaults to the last 7 days when omitted), `title` (free-text search)
* `has_description`: `true` only bugs with a description, `false` only bugs without one
* `user_uuids` — keep only bugs reported by these users, identified as the SDK reported them and matched exactly (up to 100). To search by reporter email use `email` instead.

#### Sorting & Pagination

* `sort_by` (default `reported_at`), `direction` `asc`/`desc` (default `desc`)
* `limit` (default 20, max 50), `offset`

#### Key Fields

* **number** — Bug ID
* **title** — User-entered title
* **email** — Reporter
* **priority\_id**, **status\_id**
* **reported\_at**, **last\_activity**
* **categories**
* **duplicated\_bugs\_count**

#### Usage Examples

* “Show new bugs for version 3.3.”
* “List all open bugs.”
* “Show bugs reported today.”
* “Which bugs are highest priority?”

### `6.2 bug_details`

#### What it does

Returns detailed bug information including logs, user data, and device metadata.

#### Use this when

You need full context to reproduce the bug.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` bug number

#### Key Fields

**Top-level:**

* **title**, **type** — Bug title & type
* **priority\_id**, **status\_id** — Bug priority & status
* **reported\_at**, **last\_activity** — When it was reported, last update time
* **email**, **tags** — Reporter’s email, tags
* **categories**, **team** — Assigned categories, team

**state.fields (context):**

* os, device, country, city
* app\_version, sdk\_version
* current\_view
* screen\_size, density
* bundle\_id
* user\_attributes
* duration (session length)

**state.logs:**

* user\_steps, network\_log, sessions\_profiler, etc. with url and is\_empty\_array.

#### Usage Examples

* “Show details for bug #468.”
* “What steps did the user take?”
* “Which device was used?”
* “Show the network log for this bug.”

### `6.3 update_bug`

{% hint style="warning" %}
**This is a write tool.** It changes the state of a bug in your workspace, exactly as if you’d edited it in the dashboard. It respects your RBAC permissions and is audit-logged. It’s the only Luciq MCP tool that modifies data.
{% endhint %}

#### What it does

Updates a bug’s **status**, **priority**, or **tags**, or marks/unmarks it as a **duplicate** of another bug, directly from your IDE. This lets you complete bug triage end-to-end without switching to the dashboard.

#### Use this when

You’ve investigated a bug (via `bug_details`) and want to act on it: close it, reopen it, reprioritize it, retag it, fold it into another bug as a duplicate, or detach it from its master.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `number` bug number

#### Optional (provide at least one of `status_id`, `priority_id`, `tags`, or `action`)

* `status_id`: `1` New, `2` Closed, `3` In Progress
* `priority_id`: `-1` N/A, `1` Trivial, `2` Minor, `3` Major, `4` Blocker
* `tags`: the tags to apply. How they are applied is controlled by `tag_action` (default `append`). By default the listed tags are **added** while your existing tags are kept — the call no longer replaces the full set, so you don’t need to read and re-send the current tags to add or remove one. (Setting `tags` requires an additional `bugs.tags.modify` permission.)
* `tag_action`: `append` (default), `replace`, or `remove` — controls how `tags` is applied. Only meaningful together with `tags`.
  * `append` — add the listed tags, keeping existing ones.
  * `remove` — remove only the listed tags, keeping the rest.
  * `replace` — set `tags` as the full list, removing any existing tag not listed. Pass `[]` with `replace` to clear all tags.
* `action`: `mark_as_duplicate` or `unmark_as_duplicate`.
  * `mark_as_duplicate` merges this bug into a master, requires `original_bug_number`. Side effect: this bug’s occurrences move into the parent group and its status/priority are overwritten by the parent’s.
  * `unmark_as_duplicate` detaches this bug from its master, restoring it to a standalone bug.
  * `action` **cannot** be combined with `status_id` or `priority_id` in the same call.
* `original_bug_number`: the master bug’s `number`. Required when `action` is `mark_as_duplicate`; ignored otherwise.

#### Usage Examples

* “Close bug #468.”
* “Set bug #51 to Major priority and tag it `checkout`, `regression`.”
* “Add the `needs-info` tag to bug #77.” (appends, keeping existing tags)
* “Remove the `stale` tag from bug #77.”
* “Replace bug #90’s tags with just `triaged`.”
* “Reopen bug #120.”
* “Mark bug #205 as a duplicate of bug #198.”
* “Unmark bug #205 as a duplicate.”

### **7. User Sentiment & Store Ratings**

***

### `7.1 list_reviews`

#### What it does

Lists app reviews (e.g., from store/native/custom prompts) with filters for rating, version, country, etc.

#### Use this when

* You want to correlate user feedback with app stability.
* You want to see 1–2 star reviews for a release.
* You’re checking if a performance or crash issue shows up in user feedback.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `date_ms.gte` / `lte`
* `app_version`
* `rating` – array of star ratings `[1–5]`
* `country`
* `prompt_type` – `custom`, `native`, `app_store`
* `os` (for cross-platform)

#### Key Fields

* **title**, **body** — Review content
* **star\_rating** — 1–5 stars
* **username**, **country**
* **app\_version**, **device**
* **date**
* **has\_suspected\_sessions** — Linked to stability issues
* **has\_custom\_suspected\_sessions**

#### Usage Examples

* “Show 1-star reviews for version 3.0.”
* “List negative reviews from the US.”
* “Show native prompt reviews only.”
* “What are the most recent app store reviews?”

### **8. Surveys**

***

### `8.1 list_surveys`

#### What it does

Lists the surveys configured for an app (custom surveys, NPS, and app-store prompts), newest-first, with their status.

#### Use this when

* You want to see which surveys are live, paused, or in draft.
* You’re looking up a survey’s `id` before pulling its responses.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `status`: `0` Draft, `1` Published, `2` Paused
* `type`: `0` Custom, `1` NPS, `2` App Store

#### Usage Examples

* “List my published surveys.”
* “Show all NPS surveys.”
* “Which surveys are currently paused?”

### `8.2 survey_details`

#### What it does

Returns a survey’s questions, response statistics, and a page of individual responses (filterable and paginated, mirroring the dashboard).

#### Use this when

You want to read what users answered, e.g. NPS scores and verbatims for a specific survey.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `id` survey id (from `list_surveys`)

#### Useful Filters

* `nps` (filter by NPS score 0–10)
* `search_words` (free-text search across response values, name, and email)
* `response_status`: `0` Open, `1` Closed
* `app_versions`, `devices`, `os_versions`, `countries`, `platforms`, `locale`
* `date_ms`

#### Notes

* Responses are paginated at **25 responses per page** via the `page` parameter.

#### Usage Examples

* “Show me the responses for survey 312.”
* “What are the NPS detractor comments on my NPS survey?”
* “Show survey responses from the last 30 days.”

### **9. App Health Overview**

***

### `9.1 app_insights`

#### What it does

Returns an **aggregated, cross-product health snapshot** for one app in a single call. Each section is returned under its own key:

* `crashes` — non-fatal crash counts
* `bugs` — bug report totals
* `apm` — APM key metrics (network, launches, screen loads, flows)
* `monitoring` — stability rates (crash-free sessions, ANR, OOM, app hangs)

A section whose product **isn't included in your plan is omitted entirely** (its key is absent). A section that is enabled but temporarily unavailable (e.g. a data-retention limit or a failing service) returns an `error` object instead of data, while the other sections still return.

#### Use this when

You want a fast “how healthy is this app right now?” overview before drilling into a specific area.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Optional

* `filters.date_ms` (time window)
* `filters.app_version`

#### Usage Examples

* “Give me a health overview for my app.”
* “How is the app doing this week, crashes, bugs, and performance?”
* “Show app insights for version 3.4.”

### **10. Users**

***

### `10.1 user_summary`

#### What it does

Returns a single end user's summary for an app in one call: devices, OS versions, and app versions seen; sessions and session replays; per-metric APM occurrences split by satisfying/tolerable/frustrating; and the crashes and bug reports that user hit.

#### Use this when

* You're investigating one person's experience end-to-end instead of scanning aggregate data.
* Someone reports a problem and you want their devices, crashes, bugs, and performance in a single call rather than chaining several tools.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `user_uuid` the user ID as reported by the SDK, matched exactly

#### Optional

* `period_days` — length of the window ending now, in days (default 7, max 56)

#### Key Fields

* **user\_identifier**, **identifier\_type** — The ID you queried and how it's classified upstream
* **has\_data** — `false` with zeroed counts means the ID matched no user, not that the user has no activity
* **period** — `gte_ms`, `lte_ms`, `days` for the resolved window
* **overview** — `app_versions`, `devices`, `os_versions` seen, plus per-combination `profiles`
* **sessions** — `count`, `with_session_replay`
* **performance** — Per-metric occurrence counts split by Apdex class (e.g. `network.frustrating`)
* **crashes** — `occurrences`, `distinct`
* **bugs** — `occurrences`, `distinct`

#### Notes

* `user_uuid` is matched exactly against the SDK-reported ID. It only resolves an email if the app reports emails as user IDs — to look someone up by email, use `list_session_replays` with `filters.user` instead.

#### Usage Examples

* “Give me a summary for user abc-123 over the last 30 days.”
* “What crashes and bugs has this user hit?”
* “Show devices, OS versions, and sessions for user xyz-789.”

### **11. Session Replay**

***

### `11.1 list_session_replays`

#### What it does

Lists recorded sessions for an app, newest first. Every row carries a `session_replay_url` deep link into the dashboard.

#### Use this when

* You want to browse or filter recorded sessions instead of jumping straight to a specific crash or bug.
* You're looking up a user by email — this is the only tool that resolves emails directly (`user_summary` and `list_bugs` match the reported user ID instead).

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

Filters mirror the dashboard's Session Replay filters; every filter is AND-ed with the others, values inside one filter are OR-ed.

* `filters.user` — end users whose sessions to return, identified by user ID or email (the dashboard's "Search by User ID, Email" filter). Mix both freely, e.g. `["user-1234", "someone@example.com"]`; any value containing `@` is matched against the user's email instead of their ID (up to 50 values).
* `filters.date_ms` (session start time; defaults to the last 7 days when omitted)
* `filters.app_versions` — REQUIRED format `"<version> (<build>)"`, e.g. `"4.1.2 (5)"`; a bare `"4.1.2"` is rejected
* `filters.devices`, `filters.os_versions`
* `filters.session_class` — Apdex classification of the whole session: `satisfying`, `tolerable`, `frustrating`, `crashing`
* `filters.issues` — `ai_issues`, `fatal_crash`, `ndk_crash`, `anr`, `oom`, `fatal_hang`, `app_termination`, `non_fatal_crash`
* `filters.countries` (use `"N/A"` for sessions with no country)
* `filters.experiments` — feature-flag/experiment value, e.g. `private_mode` or `checkout_flow -> external_request`
* `filters.user_attributes` — custom user attributes reported by the SDK; each attribute maps to one `equal`/`not_equal` condition (not an array, unlike `list_crashes`), and attributes are AND-ed together

#### Sorting & Pagination

* Results are always newest-first; there's no `sort_by`.
* `pagination_size` (default 20, max 50), `pagination_token` — pass the previous response's `pagination_token` verbatim for the next page; an empty `pagination_token` in a response means there are no more sessions. Keep filters identical between pages.

#### Key Fields

* **session\_id** — Session identifier
* **user** — `name`, `email` as reported by the SDK
* **session\_type** — Apdex classification of the session
* **app\_version**, **device**, **os**, **platform**
* **start\_time**, **duration**
* **ui** — `anrs`, `app_hangs` counts for the session
* **session\_replay\_url** — Dashboard deep link; always surface it. Absent on rows without a `session_id`.
* **pagination\_token**, **count**

#### Usage Examples

* “Show me recent frustrating sessions in production.”
* “Find the session replay for <someone@example.com>.”
* “List sessions on iPhone 11 that hit a fatal crash.”
* “Show sessions for app version 4.1.2 (5).”

### **12. Issues & Opportunities**

***

These tools cover Luciq's product qualitative surface, mirroring the dashboard. **Issues** is the unified view of everything affecting your app across sources (crashes, APM, AI-detected visual issues and broken functionality, and bugs), ranked by Apdex impact. **Opportunities** group related signals into prioritized, actionable items you can track to closure.

### `12.1 list_issues`

#### What it does

Lists an app's **Issues**, a unified surface that spans crashes, APM, AI-detected visual issues, broken functionality, and bugs in one ranked list. Results are ranked by Apdex impact by default. Which sources appear depends on the app's plan: each source is only included when the plan enables it.

#### Use this when

* You want a single, cross-source view of “what's hurting this app the most?” rather than scanning crashes, APM, and bugs separately.
* You're triaging by user impact and want issues ranked by Apdex.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `date_ms` (time window; the range between `gte` and `lte` must be at least 24 hours)
* `search_tokens` (free-text, e.g. exception text or group name)
* `app_version`
* `teams`
* `platform` — `IOS`, `ANDROID`, `DART` (Flutter), `JAVASCRIPT` (React Native)
* `apm_types` — `networks`, `traces`, `launches`, `screen_loadings`, `frame_drops`
* `crashes_types` — `CRASH`, `ANR`, `OOM`, `NON_FATAL`
* `ai_issues_types` — `visual_issue`, `broken_functionality`
* `bugs_types` — accepted but not currently enforced by the backend
* `apdex_severity` — `high`, `medium`, `low`, `no_impact`

Sorting: `sort_by` (`apdex_impact`, `occurrences_counter`) and `sort_direction` (`asc`, `desc`). Set `top_issues: true` to return a smaller curated list of the most impactful issues.

#### Key Fields

* **source** — Which surface the issue came from (crash, APM, visual issue, broken functionality, bug)
* **apdex\_impact** — Apdex impact used for ranking
* **apdex\_severity** — `high`, `medium`, `low`, `no_impact`
* **occurrences\_counter** — Total occurrences
* **app\_version** — Version where it occurred
* **platform** — Issue platform

#### Notes

* This tool uses **per-source pagination**: the response returns a pagination token for each source (APM, crashes, visual issues, broken functionality, bugs). Pass those tokens back in the `pagination` object to fetch the next page per source.
* When a result's `source` is a `visual_issue` or `broken_functionality` AI-detected issue, drill into it with `ai_issue_details` (and then `ai_issue_occurrence_details` for a specific occurrence's screenshots).

#### Usage Examples

* “Show me the top issues for my app this week.”
* “List the highest-Apdex-impact issues in production.”
* “Show only crash and APM issues for version 3.4.”
* “What are the most impactful issues right now?”

### `12.2 ai_issue_details`

#### What it does

Gets the details of a single AI-detected `visual_issue` or `broken_functionality` issue: the group summary plus a page of the occurrences grouped under it. The issue's description comes from the group's first occurrence.

#### Use this when

* You've spotted an AI-detected issue in `list_issues` and want its full occurrence history.
* You're paging through occurrences for one AI-detected issue.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `issue_type` `visual_issue` or `broken_functionality`
* `id` AI issue id (uuid), obtained from `list_issues`

#### Sorting & Pagination

* `occurrences_limit` — maximum occurrences to return per page (default 20)
* `occurrences_pagination_token` — opaque token for the next page; omit on the first call, then pass back the value from the previous response unchanged

#### Key Fields

* **occurrences** — Returned newest first, metadata only (no screenshots); each carries a `number` to pass to `ai_issue_occurrence_details`

#### Usage Examples

* “Show me the occurrences for this AI-detected issue.”
* “Get details for the broken functionality issue with id \[uuid].”
* “Show the next page of occurrences for this visual issue.”

### `12.3 ai_issue_occurrence_details`

#### What it does

Gets a single `visual_issue` or `broken_functionality` occurrence's details together with its screenshots.

#### Use this when

You've found an occurrence via `ai_issue_details` and want to see what happened, screenshots included.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `issue_type` `visual_issue` or `broken_functionality`
* `number` occurrence number, obtained from `ai_issue_details` (only unique within an `issue_type`)

#### Key Fields

* Screenshots are returned directly in the tool result as images, in capture order, not embedded in the text payload.

#### Usage Examples

* “Show me the screenshots for occurrence 12 of this visual issue.”
* “What happened in this broken-functionality occurrence?”

### `12.4 list_opportunities`

#### What it does

Lists an app's **Opportunities** with their status, ranked by priority (highest first), then by recency. Opportunities group related signals into prioritized, actionable items.

#### Use this when

* You want to see the prioritized, actionable items for an app rather than raw issues.
* You're reviewing what's open, in progress, or already handled.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development

#### Useful Filters

* `status` — `open`, `in_progress`, `closed`, `dismissed`
* `priority` — `1`, `2`, `3`, `4`, `unset`
* `team_id` — Team ULID, or `unassigned`

Pagination via `limit` (default 20, max 50) and `offset`.

#### Key Fields

* **id** — Opportunity ID (use with `opportunity_details`)
* **status** — `open`, `in_progress`, `closed`, `dismissed`
* **priority** — Priority level
* **team** — Assigned team

#### Usage Examples

* “List my open opportunities.”
* “Show the highest-priority opportunities first.”
* “Which opportunities are assigned to my team?”
* “Show unassigned opportunities.”

### `12.5 opportunity_details`

#### What it does

Returns a single opportunity's details: status, priority, source counts, assigned team, and its timeline. Use the `id` from `list_opportunities`.

#### Use this when

You've spotted an opportunity in `list_opportunities` and want the full context: what it groups, who owns it, and how it has progressed.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `id` opportunity id (obtained from `list_opportunities`)

#### Key Fields

* **status** — `open`, `in_progress`, `closed`, `dismissed`
* **priority** — Priority level
* **source\_counts** — Counts of the signals grouped under the opportunity
* **team** — Assigned team
* **timeline** — Status/activity history for the opportunity

#### Usage Examples

* “Show details for opportunity 312.”
* “What signals are grouped under this opportunity?”
* “Who owns opportunity 45 and what's its status?”

### **13. Alerts & Rules**

***

These tools cover Luciq's alerting surface, mirroring the dashboard. An **alert (rule)** is a configured definition that watches a metric and fires when its condition is met; a **triggered alert (incident)** is a single firing that a rule produced. `read_alerts` and `read_incidents` are read-only; `write_alerts` and `write_incidents` change data and are confirmed before running. For the alerting concepts and dashboard configuration, see the [Alerts & Rules](/product-guides-and-integrations/product-guides/automation-and-workflows/alerts-and-rules) product guide.

Authoring a rule is a two-step flow: call `read_alerts` with `action: init` to get the per-app catalog of valid types, triggers, conditions, actions, and lookup IDs, then build the `write_alerts` payload strictly from what `init` exposes. `write_alerts` rejects any type, trigger, condition, action, or operator that `init` doesn't list for the app.

### `13.1 read_alerts`

#### What it does

Reads your alert **rules** (the configured definitions and thresholds). Pick the operation with `action`:

* `list` — all rules for an app, with their status and how often each has fired.
* `details` — the full payload of a single rule, by `ulid`.
* `init` — the per-app catalog of valid rule types, triggers, conditions, actions, operators, and lookup tables (developers, teams, statuses, priorities, integrations, tags). Call this before `write_alerts`.

#### Use this when

* You want to see which alerts are configured, or inspect one rule's conditions.
* You're about to create or edit a rule and need the app's valid types and IDs (call `init`).

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `action` — `list`, `details`, or `init`
* `ulid` — required for `details` (full prefixed id, e.g. `crashes_01HX…`, `apm_01HX…`)

#### Sorting (list)

* `sort_by` — `latest_creation_date` (default), `last_edit_date`, `highest_triggered_count`
* `sort_direction` — `asc` or `desc` (default `desc`)

#### Key Fields

* **ulid** — Full prefixed rule id (used by `write_alerts` and `read_alerts details`)
* **type / trigger / conditions / actions** — How the rule is defined
* **status** — Whether the rule is enabled
* **highest\_triggered\_count** — Rolling trigger count, used to sort noisiest-first

#### Usage Examples

* “Show me my alerts.”
* “Which alert rules fire the most?”
* “Show the details for the crash-free-sessions alert.”
* “What alert types can I set up for this app?”

### `13.2 write_alerts`

{% hint style="warning" %}
**This is a write tool.** It creates, updates, or deletes an alert rule in your workspace, exactly as if you'd edited it in the dashboard. It respects your RBAC permissions and is audit-logged.
{% endhint %}

#### What it does

Creates, updates, or deletes an alert **rule**. Choose the operation with `action`: `create`, `update`, or `delete`. Author the payload from `read_alerts` `init` first, following the two-step flow described in the section intro above.

#### Use this when

You've decided on an alert to add or change: a new rule, a threshold or scope edit, or removing a redundant rule.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `action` — `create`, `update`, or `delete`
* `ulid` — required for `update` and `delete` (full prefixed id from `read_alerts`)

#### Rule Fields (create / update)

* `title` — Human-readable rule title
* `type` — Rule type from `init` (e.g. `Crashes`, `Bugs`, `Overall app`, `App launches`, `Network`, `Flows`, `Screen loading`, `Screen rendering`)
* `trigger` / `trigger_options` — The trigger key and its threshold/time options, from `init`
* `conditions` — Each with a `key`, `operator`, and `value`/`values`, combined via `operation` (`0` = AND, `1` = OR)
* `actions` — What fires on trigger (email, forward, set assignee/status/priority/team, add tag, reply), with an optional notification `frequency` throttle
* `rule_owner` — Team id that owns the rule, or empty for unassigned

#### Notes

* If a required detail is missing (a threshold, a time window, which metric, or where to send the alert), the agent proposes a sensible value and waits for your confirmation rather than guessing.
* App version conditions (`app_version`, `app_version_v2`) accept an explicit version string, or the `latest_release` and `top_releases` tokens when `init` enables them for that condition — use these when you ask to alert on the latest release or the top releases instead of a pinned version.

#### Usage Examples

* “Alert me when any crash affects more than 5% of users.”
* “Raise the threshold on my launch-time alert to 4 seconds.”
* “Delete the duplicate ANR alert.”

### `13.3 read_incidents`

#### What it does

Reads **triggered alerts (incidents)**, the actual firings your rules produced. Pick the operation with `action`: `list` or `details`.

#### Use this when

* You want to know what fired, or review recent or open incidents.
* You want to investigate a specific firing, or judge whether a rule is noisy by how often it fires.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `action` — `list` or `details`
* `ulid` — required for `details`

#### Useful Filters (list)

* `filters.date_ms` (`gte` / `lte`, in milliseconds)
* `filters.status` — `open`, `manual_resolve`, `automatic_resolve`
* `filters.type` — `overall_app`, `launch`, `screen_loading`, `network`, `trace`, `frame_drop`, `crash`, `anr`, `oom`, `non_fatal`, `fatal_ui_hang`, `feature_experiment`
* `filters.title` — title tokens (case-insensitive substring; all tokens must match)

#### Sorting & Pagination

* `sort_by` — `last_triggered` (default), `first_triggered`, `count`; `sort_direction` — `asc` / `desc` (default `desc`)
* `limit` (default 50, max 100), `offset`

#### Usage Examples

* “What alerts fired this week?”
* “Show open incidents.”
* “Which alert fired the most times?”

### `13.4 write_incidents`

{% hint style="warning" %}
**This is a write tool.** It changes the status of a triggered alert in your workspace. It respects your RBAC permissions and is audit-logged.
{% endhint %}

#### What it does

Updates the status of a **triggered alert (incident)**. Choose the operation with `action`:

* `resolve` — mark an open triggered alert as manually resolved.
* `reopen` — mark a previously resolved triggered alert as open again.

#### Use this when

You want to clear a stale firing that's already handled, or reopen one that was resolved prematurely.

#### Required

* `slug` application slug
* `mode` beta, production, staging, alpha, qa, development
* `ulid` triggered-alert id (from `read_incidents` `list`)
* `action` — `resolve` or `reopen`

#### Usage Examples

* “Resolve this incident.”
* “Reopen that alert firing.”

### **14. Documentation**

***

These tools give your AI assistant access to Luciq's public documentation (docs.luciq.ai) so it can answer SDK integration and feature questions from the current published guides, instead of relying on training data that may be out of date. Unlike every other tool in this reference, they don't take `slug` or `mode` - they aren't scoped to one of your applications.

### `14.1 search_documentation`

#### What it does

Searches the Luciq public documentation for guides, API references, SDK integration steps, and feature explanations, returning matching pages with titles, direct URLs, and content previews.

#### Use this when

* You need SDK integration steps, setup instructions, or feature documentation while working in your IDE.
* You want to confirm the current, published guidance instead of relying on the assistant's training data.

#### Required

* `query` natural-language search query, e.g. `"iOS SDK setup with SPM"` or `"mask network data"`

#### Key Fields

* **title** — Page title
* **link** — Direct URL to the page
* **content** — Preview or matching excerpt from the page

#### Notes

* Results reflect live documentation and may change as the docs are updated.
* Use `get_documentation_page` to fetch a matched page's full markdown content.

#### Usage Examples

* “How do I set up Luciq on iOS with SPM?”
* “How do I mask network data in the Android SDK?”

### `14.2 get_documentation_page`

#### What it does

Fetches the full markdown content of a specific Luciq documentation page.

#### Use this when

You've found a relevant page via `search_documentation` and need its complete content rather than just the preview.

#### Required

* `url` full URL of the docs.luciq.ai page to fetch, e.g. `"https://docs.luciq.ai/ios/setup-luciq-for-ios/integrate-luciq-on-ios"`

#### Notes

* Only URLs on the `docs.luciq.ai` host are accepted; other hosts are rejected.

#### Usage Examples

* “Get the full page for 'Integrate Luciq on iOS'.”
* “Pull the complete content of the network masking guide.”

***

## Common Parameters

Most tools share these parameters:

| Parameter             | Type    | Description                                                                |
| --------------------- | ------- | -------------------------------------------------------------------------- |
| `slug`                | string  | Application slug (from `list_applications`)                                |
| `mode`                | string  | Environment: `production`, `beta`, `staging`, `alpha`, `qa`, `development` |
| `limit`               | integer | Results per page (varies by tool; commonly default 20, max 50)             |
| `offset`              | integer | Pagination offset                                                          |
| `filters.date_ms.gte` | integer | Start timestamp in milliseconds                                            |
| `filters.date_ms.lte` | integer | End timestamp in milliseconds                                              |

***

## Pagination

List tools support `limit` and `offset` parameters for pagination:

* **Default page size:** 20 results (10 for `list_applications` and `list_reviews`; 50 for `read_incidents`)
* **Maximum page size:** 50 results for most list tools (100 for `read_incidents`)
* Use `offset` to fetch subsequent pages (e.g., `offset: 20` for the second page)
* Some occurrence/APM tools use cursor-based pagination (`current_token` + `direction`) instead of `offset`

Your AI assistant handles pagination automatically when you ask for more results. For example:

> “Show me the next 20 crashes”

***

## Error Handling

The server returns standard HTTP statuses with a JSON-RPC error envelope. The main cases:

| HTTP  | JSON-RPC code | Meaning                 | Common cause                                                                                                     |
| ----- | ------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `401` | `-32600`      | Authentication required | No credentials sent (no Bearer token and no `Email`/`Token` headers)                                             |
| `401` | `-32701`      | Invalid credentials     | Token expired, revoked, or wrong; OAuth session no longer valid                                                  |
| `403` | `-32702`      | Unauthorized role       | Your role lacks MCP access, or lacks the specific permission a tool needs (message names the missing permission) |
| `403` | `-32703`      | Plan restricted         | The tool's product isn't included in your plan (e.g. APM, Surveys, Crash Reporting)                              |
| `429` | —             | Rate limited            | Too many requests, wait and retry                                                                                |

Some failures come back as a **tool result** marked as an error rather than a protocol error, for example `Application with slug '<slug>' and mode '<mode>' not found or not accessible` (wrong slug/mode or no access to that app), or `Failed to fetch data: <detail>` when an upstream request is rejected (the message describes what to fix). Transient server-side issues return a generic "please try again later" message.

***

## Rate Limits

* Requests are rate-limited per client (by IP). The default limit is **100 requests per 60 seconds**.
* If you hit the limit you get a `429` with `Rate limit exceeded`, wait briefly and retry.

For more details, see [Authentication & Security](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/authentication-and-security#rate-limits).


# Use Cases & Workflows

Explore use cases and workflows for the Luciq MCP Server. Learn how AI agents can debug crashes, analyze performance, and resolve mobile app issues.

Real-world workflows for mobile debugging with Luciq MCP. Each workflow is a sequence of natural language prompts you can use directly in your IDE.

***

## Investigate a Production Crash

When a crash spikes in production, go from alert to root cause without leaving your editor.

**Prompt sequence:**

1. "Show me the top 5 crashes in production this week"
2. "Get details for crash #\[number] - show me the stack trace"
3. "Show crash patterns by device for this crash"
4. "List the most recent occurrences"
5. "Show occurrence details for \[token] - what device and OS?"

{% embed url="<https://streamable.com/ekvoh8>" %}

**Tools used:** `list_crashes` > `crash_details` > `crash_patterns` > `list_occurrences_tokens` > `get_occurrence_details`

**What you get:** The full picture - from which crashes matter most, to the exact stack trace, to which devices and OS versions are affected, down to the specific session that triggered it.

***

## Diagnose and Fix a Crash

When you want your assistant to actually help fix a crash, hand it the full debugging payload in one shot.

**Prompt sequence:**

1. "Show me the top crashes in production this week"
2. "Help me fix crash #\[number] - give me the full diagnostics"
3. "Based on the stack trace and affected screens, what's the likely root cause?"

**Tools used:** `list_crashes` > `crash_diagnostics`

**What you get:** `crash_diagnostics` returns the stack trace, device metrics, distributions (OS, device model, app version), and the screen flows leading up to the crash in a single call, so your assistant can reason about root cause without chaining multiple queries.

***

## Investigate a Performance Regression

A screen or endpoint got slower after a release. Find the worst offenders and drill in.

**Prompt sequence:**

1. "List my worst app launches this release" (or "slowest network endpoints", "slowest screen loads")
2. "Show me the breakdown for the worst one"
3. "Show me the single worst occurrence in that group"

**Tools used:** `apm_list_groups` > `apm_group_view` > `apm_occurrence`

**What you get:** A performance drill-down across any APM metric (`network`, `launch`, `screen_loading`, `flows`, `frame_drop`) - from ranked groups, to a single group's panels, down to the individual slowest sample.

***

## Daily App Health Check

Start your morning with a quick stability check across all your apps.

**Prompt sequence:**

1. "List my applications"
2. "Give me a health overview for \[app name] this week" (one `app_insights` call returns crashes, bugs, APM, and stability rates together)
3. "Drill into the crashes from that overview, show me the worst ones"
4. "Show me recent 1-star reviews"

**Tools used:** `list_applications` > `app_insights` > `list_crashes` > `list_reviews`

**What you get:** A fast overview of crashes, hangs, performance, and user sentiment - the `app_insights` snapshot is a single call that pulls every product's key metrics at once, so you can spot what moved before drilling in.

***

## Triage by User Impact (Issues & Opportunities)

Instead of scanning crashes, APM, and bugs separately, start from the unified, impact-ranked view.

**Prompt sequence:**

1. "Show me the top issues for \[app name] this week, ranked by Apdex impact"
2. "Focus on the crash and APM issues only"
3. "Now show my open opportunities, highest priority first"
4. "Give me the full details for opportunity \[id] - what signals does it group and who owns it?"

**Tools used:** `list_issues` > `list_opportunities` > `opportunity_details`

**What you get:** A single cross-source list of what's hurting the app most (crashes, APM, AI-detected visual issues and broken functionality, and bugs), then the prioritized Opportunities that group those signals into trackable, owned items. Which sources appear depends on what your plan enables.

***

## Investigate an AI-Detected Issue

An AI-detected `visual_issue` or `broken_functionality` issue showed up in your Issues list. Drill into it to see what's happening and view the actual screenshots.

**Prompt sequence:**

1. "Show me the visual issues in my top Issues this week"
2. "Get the details and occurrences for that visual issue"
3. "Show me the screenshots for the most recent occurrence"

**Tools used:** `list_issues` > `ai_issue_details` > `ai_issue_occurrence_details`

**What you get:** The group summary and a page of occurrences for an AI-detected issue, then a specific occurrence's full context together with its screenshots, in capture order.

***

## Regression Check After a Release

Just shipped a new version? Verify nothing broke.

**Prompt sequence:**

1. "Show crashes for version \[X.Y.Z] in production"
2. "Are any of these crashes new to this version?"
3. "Show crash patterns by app version for crash #\[number]"
4. "Are there any app hangs specific to version \[X.Y.Z]?"
5. "Show 1-2 star reviews for version \[X.Y.Z]"

**Tools used:** `list_crashes` (with version filter) > `crash_patterns` > `list_app_hangs` > `list_reviews`

**What you get:** A release-specific stability report showing whether your new version introduced regressions or new crash patterns.

***

## Triage User-Reported Bugs

Review and prioritize bug reports submitted by your users through the Luciq SDK.

**Prompt sequence:**

1. "Show me open bugs for \[app name] sorted by priority"
2. "Get details for bug #\[number]"
3. "What device and OS was the user on?"
4. "Are there any crashes related to the same screen or view?"
5. "Set bug #\[number] to Major priority and tag it `checkout`, `regression`"
6. "Close bug #\[number]" or "Mark bug #\[number] as a duplicate of bug #\[other]"

**Tools used:** `list_bugs` > `bug_details` > `list_crashes` (cross-reference by view) > `update_bug`

**What you get:** Full bug context including device info, user steps, and whether the bug correlates with known crashes - then you act on it without leaving the IDE. `update_bug` is a write tool: it changes the bug's state in your workspace (status, priority, tags, or duplicate), respects your RBAC permissions, and is audit-logged.

***

## Analyze App Store Sentiment

Monitor what users are saying about your app and correlate feedback with stability data.

**Prompt sequence:**

1. "Show me recent app store reviews"
2. "Filter to 1-2 star reviews from the last month"
3. "Are there any crashes that could explain the negative reviews?"
4. "Show me reviews for version \[X.Y.Z] specifically"

**Tools used:** `list_reviews` > `list_crashes` (cross-reference)

**What you get:** User sentiment tied to app versions, with the ability to cross-reference negative reviews against crash data.

***

## Read NPS and Survey Feedback

Pull what users actually said in your in-app surveys, without opening the dashboard.

**Prompt sequence:**

1. "List my published surveys"
2. "Show me the responses for my NPS survey"
3. "What are the detractor comments (scores 0-6)?"
4. "Filter those to the last 30 days and version \[X.Y.Z]"

**Tools used:** `list_surveys` > `survey_details`

**What you get:** A survey's questions, response stats, and individual verbatims, filterable by NPS score, version, device, and date, so your assistant can summarize themes from raw responses.

***

## Device-Specific Investigation

A QA engineer reports issues on a specific device model. Investigate with MCP.

**Prompt sequence:**

1. "Show crashes on Samsung Galaxy S24 in production"
2. "Break down crash #\[number] by OS version"
3. "Show occurrence details for a Galaxy S24 occurrence"
4. "Are there app hangs on this device?"

**Tools used:** `list_crashes` (with device filter) > `crash_patterns` > `get_occurrence_details` > `list_app_hangs`

**What you get:** A device-specific stability profile showing which crashes and hangs concentrate on that hardware.

***

## Pre-Release QA Check

Before pushing to production, check the stability of your beta or staging builds.

**Prompt sequence:**

1. "Show crashes in the beta environment for the last 7 days"
2. "Are there any app hangs in staging?"
3. "Show me bug reports from beta testers this week"
4. "What are the top crashes by occurrence count in beta?"

**Tools used:** `list_crashes` (beta/staging mode) > `list_app_hangs` > `list_bugs`

**What you get:** A pre-release stability snapshot so you can catch issues before they reach production.

***

## Investigate One User's Experience

Someone reports a problem, or you're following up on a specific person's session, and you want their full picture (devices, crashes, bugs, performance) instead of chaining separate lookups.

**Prompt sequence:**

1. "Find the session replay for <someone@example.com>" (resolves an email to their SDK user ID and shows their recorded sessions)
2. "Give me a summary for that user over the last 30 days"
3. "What crashes and bugs has this user hit?"

**Tools used:** `list_session_replays` > `user_summary`

**What you get:** `list_session_replays` is the only tool that resolves an email directly; `user_summary` then returns that person's devices, OS and app versions, sessions and replays, per-metric APM occurrences by Apdex class, and the crashes and bugs they hit, all in one call.

***

## Manage Alerts and Rules

Create, edit, and delete alerts (rules) directly from your IDE, without a trip to the dashboard. Describe the alert you want in natural language and let the agent author a valid rule, groom noisy ones, or fill coverage gaps.

**Example prompts:**

* "Notify me whenever a new bug is reported by any user on Luciq"
* "Alert me when any crash gets more than 50 occurrences within an hour on Luciq"
* "I'm getting too many triggered alerts from Luciq, groom my alerts to reduce noise"
* "Analyze my app's health, and set up the missing alerts in Luciq that I need to maintain the app's health"

**Tools used:** `read_alerts` > `write_alerts` (plus `read_incidents` / `write_incidents` for triggered-alert cleanup)

**What you get:** The agent reads your app's alert catalog before writing, so it never guesses an id, threshold, or unstated value, and confirms before any state-changing operation.

{% hint style="success" %}
To get the best results managing alerts, install our latest [Agent Skills](/product-guides-and-integrations/product-guides/ai-features/agent-skills), which include [`luciq-alert-config`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-config), [`luciq-alert-gaps`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-gaps), and [`luciq-alert-noise`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-noise). They greatly improve the agent's performance on alert and rule prompts.
{% endhint %}

***

## Get Answers from the Docs

Ask your assistant SDK integration or feature questions and get answers grounded in the current published documentation instead of the assistant's training data.

**Prompt sequence:**

1. "How do I mask network data on Android?"
2. "Show me the full page on network logging"
3. "Now show me the equivalent guide for iOS"

**Tools used:** `search_documentation` > `get_documentation_page`

**What you get:** Answers grounded in the live docs.luciq.ai content - search finds the relevant pages with previews, and fetching the full page gives your assistant the complete markdown to work from (steps, code samples, and all).

***

## Tips for Better Results

{% hint style="info" %}
**Be specific about time ranges.** "Last 7 days" or "since March 1" gives more targeted results than "recent."

**Specify the environment.** "Production" vs "beta" vs "staging" helps target the right data.

**Use app names.** If you have multiple apps, mention which one by name or slug.

**Chain your queries.** Start broad (`list_crashes`) then narrow (`crash_details` > `crash_patterns` > occurrence). Each answer gives you context for the next question.

**Ask follow-up questions.** MCP tools work well in conversation. Your AI assistant retains context from previous queries in the same session.
{% endhint %}

***

## What's Next?

* [**Tools Reference**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/mcp-tools-reference) - Full documentation for all 33 tools with parameters and response fields
* [**FAQ & Troubleshooting**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/faq-and-troubleshooting) - Common issues and how to resolve them


# FAQ & Troubleshooting

Answers to frequently asked questions and troubleshooting tips for the Luciq MCP Server. Resolve common setup issues and get the most out of AI debugging.

***

## What does the MCP server do vs. what does the AI do?

The Luciq MCP server is a **data bridge** - it fetches structured data from your Luciq workspace (crashes, performance metrics, bugs, hangs, surveys, reviews) and hands it to your AI assistant. The MCP server doesn't analyze, summarize, or interpret anything.

Everything else - natural language understanding, pattern recognition, summarization, suggestions - comes from the AI model in your IDE. If a response feels inaccurate or speculative, that's the AI reasoning over the data, not the MCP returning bad data. Being more specific in your prompts (exact app names, time ranges, environments) gives the AI better data to work with.

***

## Connection Problems

**Can't connect to the MCP server**

* Verify the server URL: `https://api.luciq.ai/api/mcp`
* Restart your IDE after adding or changing the MCP configuration
* If using a VPN or corporate proxy, ensure `api.luciq.ai` is allowed through your firewall
* If your organization uses a dedicated Luciq cluster, your endpoint URL may differ - contact your Luciq admin

**OAuth isn't opening in my browser**

* Make sure your default browser allows pop-ups from your IDE
* Try the connection again - some clients need a retry on first attempt
* If using Claude Code over SSH or a remote terminal, interactive OAuth won't work. Use [token-based authentication](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/authentication-and-security#token-based-authentication) instead

**Authorized OAuth but still can't connect**

* Restart your IDE after completing authorization
* Verify your Luciq account is active and has the necessary permissions

***

## "Tool not found" or "Method not found" Errors

Luciq MCP uses **snake\_case** tool names: `list_crashes`, not `listCrashes`. If your AI is calling tools with the wrong naming convention, correct it in your prompt or verify the connection is active by asking: "List my applications."

***

## Permissions and App Visibility

MCP respects your workspace's RBAC settings. If you're seeing "App not found" or not seeing all your apps, your account may not have permissions for those apps or environments. Check with your workspace admin.

If a specific tool returns a permission error (the message names the missing permission), your role doesn't have access to that data type, ask your workspace admin to grant it in your Luciq role settings.

***

## "Not included in your plan"

If a tool returns a message that its product **isn't included in your current plan**, that tool's data type (for example APM, Surveys, or Crash Reporting) isn't part of your subscription. The tool still appears in your client's tool list (all 27 always do), but it can't run until the product is enabled on your plan. Contact your Luciq account team to add it.

The aggregated `app_insights` tool degrades gracefully here: any section for a product you don't have is simply omitted from the response, and the rest still come back.

***

## "Application not found or not accessible"

This means the `slug` + `mode` combination you (or your assistant) used doesn't match an app you can access. Common causes: a typo in the slug, the wrong environment (`production` vs `beta`/`staging`), or no permission for that app. Ask your assistant to "list my applications" to get the exact `slug` and `mode` values, then retry.

***

## Can MCP Modify or Write Data?

Almost all tools are **read-only**. The one tool that writes is **`update_bug`**, which can change a bug's triage state - its status, priority, tags, or mark/unmark it as a duplicate of another bug. This is the same action you'd take in the dashboard, exposed so agents can finish bug triage without a context switch.

Write actions:

* **Respect your RBAC permissions** - you can only update bugs in apps and environments you already have access to (changing tags needs an extra tag-modify permission)
* **Are audit-logged** - just like any change made in the dashboard

One read tool has a subtle side effect worth knowing: **`crash_diagnostics`** computes and caches a crash-analysis payload on demand, so it isn't a pure read (if it returns `status: "generating"`, just ask again in a moment). It never changes your crash, bug, or app data.

No other tool creates, updates, or deletes data. MCP cannot touch your source code, account, or billing.

***

## Slow Responses or Timeouts

If queries are taking unusually long or timing out:

* Try narrower queries - add time ranges, specify the app, filter by environment
* Avoid asking for very large result sets in a single query
* Check your network connection to `api.luciq.ai`

***

## Rate Limits

MCP requests are rate-limited per client (by IP), with a default of **100 requests per 60 seconds**. If you hit the limit you'll get a `429` with `Rate limit exceeded`, wait briefly and retry. Normal conversational use rarely gets close; only tight automated loops do.

***

## IDE-Specific Issues

**Cursor: server shows as disconnected**

* Check `~/.cursor/mcp.json` for valid JSON syntax (a missing comma or bracket will break it)
* Restart Cursor after any configuration changes
* Try removing and re-adding the Luciq server entry

**Claude Code: "Transport error"**

* Ensure you used `--transport http` when adding the server
* Try removing and re-adding: `claude mcp remove luciq` then add again

**Claude Desktop: connection hangs or fails silently**

* Double-check the JSON syntax in your config file
* Make sure the entry is inside the `mcpServers` object, not at the root level
* Restart Claude Desktop completely (quit and reopen)

**VS Code: tools not appearing in Copilot**

* Verify `.vscode/mcp.json` exists in your workspace root
* Reload the window: `Cmd/Ctrl + Shift + P` > "Developer: Reload Window"

**Any IDE: connected, but no Luciq tools show up**

* Make sure you completed the OAuth approval (or added valid `Email`/`Token` headers), an unauthenticated connection won't expose tools
* Restart the IDE so it re-runs the tool discovery (`tools/list`) after authenticating
* Confirm your role has MCP access enabled, without it the server rejects the session before any tools load
* Once authenticated, all 33 tools should appear. Whether each one returns data depends on your role and plan, but they should all be listed

***

Still need help? Contact <support@luciq.ai>.


# Agent Skills

Curated Agent Skills that teach AI coding agents to use Luciq like an experienced mobile engineer. What each skill does and how to install.

Curated skills that teach AI coding agents how to use Luciq the way an experienced mobile engineer would.

Skills are instruction sets your agent can follow when you ask it to set up, debug, or migrate Luciq in your project. They live alongside your project rules (in Cursor, Claude Code, Codex, or any agent that supports the [Agent Skills](https://agentskills.io) format) and activate automatically when the conversation matches.

## Skills vs MCP Server vs AI Coding Agent Integration

These three pieces work together but solve different problems:

| Surface                                                                                              | What it gives the agent                                                                                            | When to use                                                                                               |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| [**Luciq MCP Server**](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) | **Tools.** Read access to your crashes, hangs, bugs, occurrences, and reviews.                                     | The agent needs production data to investigate issues or answer questions.                                |
| [**AI Coding Agent Integration**](/getting-started/luciq-ai-integration)                             | **End-to-end SDK setup workflow.** Pinned dependency, init, invocation, masking, MCP wiring, smoke build.          | The user is integrating Luciq for the first time, or onboarding a new platform.                           |
| **Agent Skills** *(this page)*                                                                       | **Methodology.** The investigation patterns, decision points, and pitfalls that turn raw MCP data into a real fix. | The agent has tools and data but you want it to investigate the way an experienced mobile engineer would. |

Tools without methodology produces drive-by API calls. Methodology without tools is theory. You want all three.

## Available Skills

| Skill                                                                                                                     | Use it when                                                                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [**`luciq-setup`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup)                 | Adding Luciq to a new project for the first time. *(Wraps the existing AI Coding Agent integration flow.)*                                                                                                                                          |
| [**`luciq-onboard`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-onboard)             | Walking through which Luciq products fit your app after initial setup. The agent reads your repo, surfaces conflicts with existing mobile observability SDKs, and guides you through configuring and activating each product with cited reasoning.  |
| [**`luciq-masking-rules`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-masking-rules) | Auditing PII masking posture and preparing for a compliance framework (HIPAA, GDPR, PCI-DSS, SOC 2, CCPA, FERPA). The agent scans all three masking layers, surfaces gaps with citations, and walks you through closing them one control at a time. |
| [**`luciq-debug`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug)                 | Investigating a Luciq production signal end to end — crash, hang, user-reported bug, or APM performance issue. The agent pulls the right MCP context, maps it to your local code, and proposes a fix grounded in evidence.                          |
| [**`luciq-group-bugs`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-group-bugs)       | Consolidating your bug list by marking duplicates according to your own grouping logic. The agent compiles your rule into an explainable key, renders a dry-run plan, and only merges after you approve it.                                         |
| [**`luciq-migrate`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-migrate)             | Moving a project from the legacy Instabug SDK to Luciq, or upgrading between Luciq SDK versions. *(Wraps the existing migration hub.)*                                                                                                              |
| [**`luciq-verify`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-verify)               | Verifying a Luciq SDK upgrade actually works before you ship. The agent builds a debug-variant harness, drives a deterministic smoke, and audits the captured crash, bug, and APM data via the Luciq MCP to produce a pass/fail report.             |
| [**`luciq-readout`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-readout)             | Generating a shareable, audience-tailored readout of app health for a chosen audience — C-suite, VP, PM, EM, or QA — with every figure cited to its Luciq MCP source.                                                                               |
| [**`luciq-alert-config`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-config)   | Creating, changing, or inspecting a specific Luciq alert. The agent authors a valid payload by reading the app's alert catalog first, and never guesses an id, threshold, or unstated value.                                                        |
| [**`luciq-alert-gaps`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-gaps)       | Finding what you're not monitoring. The agent cross-references current metric health against your existing alert rules and proposes alerts only for metrics that are both unhealthy and uncovered.                                                  |
| [**`luciq-alert-noise`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-alert-noise)     | Reducing noisy or spammy Luciq alerts. The agent inspects each alert's trigger frequency and recommends targeted fixes without ever silencing a safety-critical alert.                                                                              |
| [**`luciq-cli`**](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-cli)                     | Installing and authenticating the Luciq CLI, uploading symbol files, wiring symbolication into CI, or scripting a Luciq data query as a repeatable command instead of a conversational answer.                                                      |

## Install

Add the Luciq marketplace and install all the skills in one step:

```
/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](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server), so the skills have production data from day one.

After install, the skills become available as `/luciq-skills:luciq-setup`, `/luciq-skills:luciq-onboard`, `/luciq-skills:luciq-masking-rules`, `/luciq-skills:luciq-debug`, `/luciq-skills:luciq-group-bugs`, `/luciq-skills:luciq-migrate`, `/luciq-skills:luciq-verify`, `/luciq-skills:luciq-readout`, `/luciq-skills:luciq-alert-config`, `/luciq-skills:luciq-alert-gaps`, `/luciq-skills:luciq-alert-noise`, and `/luciq-skills:luciq-cli`.

For other agents, install via npm:

```bash
npx luciq-skills install            # project-local (.claude/skills/)
npx luciq-skills install --global   # all projects (~/.claude/skills/)
```

Or copy the [`SKILL.md` files](https://github.com/luciqai/agent-skills/tree/main/plugins/luciq-skills/skills) directly from the public repo. The full SKILL.md is also reproduced on each skill page below.

## Prerequisites

* A Luciq account and at least one app token.
* An AI coding agent that supports the [Agent Skills](https://agentskills.io) format. Claude Code, Cursor, Codex, or any other compatible client.
* For `luciq-debug`, `luciq-group-bugs`, `luciq-readout`, `luciq-verify`, `luciq-alert-config`, `luciq-alert-gaps`, and `luciq-alert-noise`: the [Luciq MCP Server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) configured and authenticated. The `luciq-setup` skill wires this for you.

## How a skill activates

Skills declare *when* they apply via their description and triggering phrases. You don't `/run` them; you describe what you want, and the agent picks the right skill on its own.

For example, asking the agent *"why is this iOS crash happening, Luciq says it's only on iOS 18"* activates `luciq-debug`. Asking *"add Luciq to this project"* activates `luciq-setup`. The agent reads the skill, follows its methodology, and pulls in MCP data when the skill calls for it.

## Contributing & feedback

The skills are maintained in the public [`luciqai/agent-skills`](https://github.com/luciqai/agent-skills) repo and reviewed by our SDK leads before they ship. Spotted something inaccurate, or have a workflow you'd like a skill for? Open an issue on the repo or reach out to your Luciq contact.

We're shipping skills as we validate them. The bar is high on purpose: skills feed into agents that write code in your repo, so accuracy beats coverage.


# luciq-setup

Use the luciq-setup AI agent skill to automatically integrate the Luciq SDK into your mobile app. Let AI handle SDK setup with minimal manual steps.

Set up the Luciq SDK in any iOS, Android, React Native, Flutter, or Kotlin Multiplatform project from a single agent prompt.

## Use this skill when

* The user asks to *"add Luciq"*, *"install the Luciq SDK"*, *"set up Luciq for this project"*, or *"initialize Luciq"*.
* The user is onboarding a new platform inside an existing Luciq workspace.
* The user wants the Luciq MCP server wired into the same setup pass.

## What it does

`luciq-setup` is the agent-driven version of Luciq's published integration flow. The agent:

* Detects the platform (iOS / Android / RN / Flutter / KMP) and the user's package manager.
* Finds the app token (from Luciq MCP if available, otherwise prompts).
* Pins and installs the latest released SDK.
* Initializes Luciq with the user's preferred invocation event (shake, screenshot, floating button, or manual).
* Optionally configures network logging masking, screenshot masking, and user identification.
* Wires up the Luciq MCP server so the same project gets agent-driven debugging from day one.
* Runs the platform's build command and prints a verification checklist.

The agent confirms before applying any code change.

## 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/luciqai/luciq-docs/blob/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step.

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

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-setup/SKILL.md) from the public repo to `~/.claude/skills/luciq-setup/SKILL.md` (user-global) or `.claude/skills/luciq-setup/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-setup
description: Use when the user asks to add, install, set up, integrate, or initialize the Luciq mobile observability SDK in an iOS, Android, Flutter, React Native, or Kotlin Multiplatform project. Triggers include phrases like "add Luciq", "install Luciq SDK", "set up Luciq", "initialize Luciq", or pasting an empty mobile project and asking to wire Luciq. First-time integration only — for SDK upgrades or migration from the legacy Instabug SDK use luciq-migrate.
---

# Luciq SDK Installation

End-to-end first-time integration of the Luciq mobile observability SDK in a mobile project. Drive every API decision off the canonical platform integration guides linked below. The SDK evolved through the Instabug-to-Luciq rebrand, so any signature memorized in this skill may be stale; always verify against the live guide before applying edits.

## When NOT to use this skill

This skill is for first-time SDK integration. Hand off to a sibling skill for any of the following:

- Upgrading an already-integrated Luciq SDK between versions, or migrating from the legacy Instabug SDK, use `luciq-migrate`.
- Investigating a crash, hang, regression, user-reported bug, or rating drop, use `luciq-debug`.
- Looking up an API signature without installing anything, navigate the live integration guides directly (URLs in the workflow below).

If the user's request fits any of the above, STOP and route them to the right skill rather than running this one.

## Canonical sources of truth

YOU MUST verify SDK API signatures, package names, and MCP transport URLs against these live guides before applying edits. Hardcoded values in this file are illustrative and may be stale.

| Concern | Source |
| --- | --- |
| iOS install + init | https://docs.luciq.ai/ios/setup-luciq-for-ios/integrate-luciq-on-ios/luciq-ai-ios-guide |
| Android install + init | https://docs.luciq.ai/android/set-up-luciq-for-android/integrate-luciq-on-android/luciq-ai-android-guide |
| Flutter, React Native, KMP | the platform's setup space at https://docs.luciq.ai |
| MCP server config | https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide |
| App tokens (when authenticated) | Luciq MCP `list_applications` |

## Workflow checklist

Track every step. STOP on any failed step. Do not continue past a broken state.

```
Setup Progress:
- [ ] 1. Detect platform
- [ ] 2. Acquire app token
- [ ] 3. Run per-platform recipe (deps + init)
- [ ] 4. Configure invocation
- [ ] 5. Configure auto-masking
- [ ] 6. Wire user identification
- [ ] 7. Bootstrap Luciq MCP server
- [ ] 8. Bootstrap Luciq CLI (optional, for symbol upload)
- [ ] 9. Smoke build
- [ ] 10. Hand off summary
```

## 1. Detect platform

Run a single non-recursive Glob at workspace root: `{pubspec.yaml,package.json,*.xcodeproj,*.xcworkspace,build.gradle,build.gradle.kts,shared/build.gradle.kts}`.

Apply the rules below in this exact order. First match wins. Cross-platform projects contain native subfolders (`ios/Runner.xcodeproj`, `android/build.gradle`), so root-level markers MUST take priority over those.

1. Root has `pubspec.yaml` -> Flutter (skip iOS/Android subdirs even if present).
2. Root has `package.json` containing `"react-native"` in `dependencies` -> React Native.
3. Root has `shared/build.gradle.kts` with `kotlin("multiplatform")` -> KMP.
4. Root has `*.xcworkspace` or `*.xcodeproj` (and none of the above) -> iOS.
5. Root has `build.gradle` or `build.gradle.kts` (and none of the above) -> Android.

If two or more rules match unexpectedly (for example, both `pubspec.yaml` and a top-level `*.xcodeproj` outside `ios/`), STOP and ask the user to disambiguate. Do not guess.

If no rule matches (empty repo, unusual layout, or a project where the entry point lives in a non-standard subdirectory), STOP and ask the user which platform they're targeting and where the project root lives. Do not assume — silently picking a platform here corrupts every downstream step.

## 2. Acquire app token

Resolve the token in this order:

1. Try the Luciq MCP server: `list_applications` returns tokens for apps the authenticated user can see. This works only if Luciq MCP is already authenticated in the user's agent from a previous `luciq-setup` run on another project — for genuine first-time setups, this call will fail with a tool-not-found error and you should fall through to step 2 below. Do not attempt to bootstrap MCP here; that is step 7.
2. Read from environment (`LUCIQ_APP_TOKEN`).
3. Prompt the user.

NEVER commit the token inline. Use a build-time injection, an env var, or a gitignored secrets file. Tokens leak via git history, which is irreversible.

## 3. Per-platform recipe

YOU MUST verify the exact init signature, package name, and Gradle plugin name for the detected platform against the live integration guide above before applying. APIs evolved through the Instabug-to-Luciq rebrand. The recipes below name the files to edit, not authoritative signatures.

### iOS
1. Edit `Podfile`: add the Luciq pod to the main target.
2. Run `pod install` after user confirmation.
3. Edit `AppDelegate.swift` (or `.m`): import Luciq and call the start API in `application(_:didFinishLaunchingWithOptions:)`.

### Android
1. Edit `app/build.gradle(.kts)`: add the Luciq dependency. Apply the Luciq Gradle plugin if the live guide says so.
2. Sync Gradle.
3. Edit the `Application` subclass `onCreate` to construct and start Luciq.

### Flutter
1. Edit `pubspec.yaml`: add the `luciq_flutter` package per the live guide.
2. Run `flutter pub get`.
3. Edit `lib/main.dart`: call `Luciq.start(...)` before `runApp(...)`.

### React Native
1. Verify the exact package name on the live guide, then `npm install` or `yarn add` it.
2. iOS host app: `cd ios && pod install`.
3. Android host app: verify autolinking.
4. Edit the JS entry to call the start method early in app lifecycle.

### KMP
1. Edit `shared/build.gradle.kts` for shared deps per the live guide.
2. Run the iOS recipe for the iOS app target.
3. Run the Android recipe for the Android app target.

## 4. Configure invocation

Default to shake gesture plus screenshot. Offer alternatives: floating button, two-finger swipe, or programmatic-only. Apply the user's choice.

## 5. Configure auto-masking

Goal: identify likely-sensitive UI views and configure SDK-side masking. A naive substring grep produces false positives (validators, comments, test fixtures), so the search must be narrowly scoped and every match must be user-confirmed.

1. Grep the platform's UI source files only (`*.swift`, `*.kt`, `*.dart`, `*.tsx`, `*.jsx`) for these identifier-shaped strings: `password`, `email`, `cardNumber`, `ssn`, `cvv`, `pin`, `dob`, `iban`.
2. Filter out matches in `*test*`, `*spec*`, `*mock*`, `*fixture*` paths, validator/regex utilities, and anything under `node_modules`, `Pods/`, or `build/`.
3. Show the filtered match list with `file:line` for each. Get per-match confirmation. Do not apply masking rules in bulk.
4. Verify the masking API signature for the detected platform on the live guide. The masking API has differed across platforms and changed across SDK versions; do not hardcode it.
5. Apply masking config only for confirmed matches.

Also configure network-log redaction: sensitive headers (Authorization, Cookies) and body fields (password, token).

## 6. Wire user identification

If the app has authentication, find login and logout flows. Add `identifyUser(...)` and the corresponding sign-out call so reports tie back to your users. Verify the exact identification API on the live guide.

If the app is anonymous-first (no login surface — typical for many B2C utilities, content readers, and games with guest play), skip this step entirely. Do not synthesize a fake user identity, do not insert `identifyUser` at app launch with placeholder values, and do not block the workflow waiting for a login flow that doesn't exist. Note the skip in the hand-off summary so the user can wire identification later if they add auth.

## 7. Bootstrap Luciq MCP server

Add the Luciq MCP server to `~/.claude.json` (user-global) or `.mcp.json` (project). Confirm with the user where to write.

```json
{
  "mcpServers": {
    "luciq": {
      "type": "http",
      "url": "https://api.luciq.ai/api/mcp"
    }
  }
}
```

YOU MUST verify the MCP server URL and transport type against https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide before writing the config. Both have evolved across releases.

After writing, prompt the user to restart their agent (Claude Code, Cursor, Codex, or other supported client) and complete the OAuth flow. Once authenticated, Luciq MCP tools become available qualified as `luciq:<tool_name>` (for example, `luciq:list_crashes`).

## 8. Bootstrap the Luciq CLI (optional)

If the project will upload symbol artifacts (dSYMs, ProGuard or R8 mapping files, source maps, or split-debug-info) to Luciq for symbolication of obfuscated frames, install the Luciq CLI.

YOU MUST verify the install command, supported platforms, and exact upload subcommand on the live integration guide for the user's platform. The CLI's distribution channel and command surface have changed across releases; do not hardcode an install command here.

Store credentials via environment variables (`LUCIQ_APP_TOKEN` plus any per-platform secrets the live guide names). NEVER commit credentials inline.

## 9. Smoke build

| Platform | Command |
| --- | --- |
| iOS | `xcodebuild -workspace <Workspace>.xcworkspace -scheme <Scheme> build` |
| Android | `./gradlew :app:assembleDebug` |
| Flutter | `flutter build apk --debug` |
| React Native (Android) | `npx react-native run-android` |
| React Native (iOS) | `npx react-native run-ios` |
| KMP | run both Android and iOS builds |

Deriving `<Workspace>` and `<Scheme>` for iOS and RN-iOS:

- `<Workspace>`: the `.xcworkspace` filename (without extension) in the project's `ios/` directory (or repo root for native iOS). If only an `.xcodeproj` exists, use `-project Foo.xcodeproj` instead of `-workspace`.
- `<Scheme>`: derive by running `xcodebuild -list -workspace <Workspace>.xcworkspace` and picking the app scheme. Usually matches the workspace name. For RN, the scheme typically matches the app's display name in `app.json`.
- If multiple workspaces or schemes exist, STOP and ask the user which to build. Do not guess.

STOP on build failure. NEVER claim success on a broken build.

## 10. Hand off

Print:
- File where init was added.
- Invocation event configured.
- Masking rules applied (with file:line for each).
- User identification call sites.
- MCP / CLI wired status.
- A test command (for example, "shake the device or simulator to invoke Luciq").
- Pointers: `luciq-debug` for crash investigation, `luciq-migrate` for moving off the legacy Instabug SDK or upgrading between Luciq versions.

## Style

- ALWAYS show diffs before applying code edits.
- ALWAYS confirm before running `pod install`, gradle syncs, or build commands.
- Verify SDK API signatures from the live integration guide. Do not hardcode them in this skill.

## Red Flags - STOP and surface to the user

If you catch yourself thinking any of these, you are about to ship a broken integration. STOP, surface to the user, do not proceed:

- "The build failed but the SDK is installed, so it's probably fine." It isn't. A failing build means a broken integration. Report the failure verbatim.
- "I skipped checking the live guide because the docs probably haven't changed." That's how you ship a stale signature. Always verify.
- "I hardcoded the init signature from this file, it looked right." This file is illustrative, not authoritative. The live guide is the source of truth.
- "I committed the app token inline because it's just for local testing." Tokens leak via git history. Use env injection or a gitignored secrets file.
- "I auto-applied the masking rules without showing the user the matches." False positives are likely. Per-match confirmation is mandatory.
- "`pod install` or `gradle sync` had warnings but the build went green." Warnings about Luciq specifically are not cosmetic. Read them, surface them.
- "Two platform markers matched but I picked the obvious one." If the workspace is ambiguous, ask. Cross-platform projects break this assumption routinely.

The pattern: every shortcut here trades "looks done" for "actually works." The skill's job is to actually work.
````

</details>

## Where the methodology lives

The skill follows Luciq's published [AI Coding Agent integration flow](/getting-started/luciq-ai-integration), which is the canonical source for the setup steps and is already used by customers in production today. The skill packages that flow as a SKILL.md so any agent that supports the [Agent Skills](https://agentskills.io) format can follow it without the user pasting the guide manually.

For platform-specific details, the skill reads the corresponding integration guide:

* [iOS AI integration guide](/ios/setup-luciq-for-ios/integrate-luciq-on-ios/luciq-ai-ios-guide)
* [Android AI integration guide](/android/set-up-luciq-for-android/integrate-luciq-on-android/luciq-ai-android-guide)
* React Native, Flutter, and KMP guides ship from their respective setup spaces.

## Prerequisites

* A Luciq account and an app token.
* A supported package manager: SPM, CocoaPods, Carthage, or XCFramework (iOS); Gradle (Android); npm/yarn (RN); pub (Flutter).
* An AI coding agent that supports the [Agent Skills](https://agentskills.io) format, or that can read `.md` instruction files.

## Hand-off when setup completes

Once Luciq is installed, the agent surfaces:

* Which file the init call was added to.
* The invocation event configured.
* Whether MCP was wired and where its config lives.
* A test command to verify (e.g. *"shake the simulator to trigger Luciq, then check the dashboard"*).
* Pointers to next steps, usually [`luciq-debug`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug) for crash investigation.


# luciq-onboard

The luciq-onboard skill walks an app that already has the SDK through the Luciq product suite, recommending what fits your repo and why.

End-to-end personalized walkthrough of the Luciq product suite for an app that already has the SDK installed. The agent reads your project — code, CLAUDE.md, README, and any competing mobile observability SDKs — understands what your app is and what your team values, then walks you through the Luciq products that actually fit, with cited reasoning at every step. Products that don't fit are deferred with a specific revisit condition rather than skipped silently.

The aim is a conversation where every recommendation is justified by something the agent observed in your own repo, not a generic feature menu.

## Use this skill when

* You ask to "onboard me to Luciq", "walk me through Luciq", "tour Luciq", or "help me get started with Luciq products"
* You want a guided walkthrough of which Luciq products fit your specific app, with cited reasoning for each recommendation
* You've finished first-time setup and are ready to explore what to configure next (invoke this yourself in a fresh message — setup completing is not a trigger)

## What the agent does

* **Project analysis.** Silently reads the project across four parallel tracks: context docs (CLAUDE.md, README, AGENTS.md), app archetype with money path / auth flow / sensitive view enumeration / accessibility posture, competing mobile observability SDKs with their full config posture, and workspace precedent from the Luciq MCP if authenticated.
* **Infrastructure detection.** Gated sub-tracks detect your CI system (for dSYM / mapping upload diffs), push token registration site (for In-App Replies), network client and base URLs (for APM), distribution model (for App Ratings and Rollout Management), and locale count (for locale-aware survey copy).
* **Conflict surface.** If two SDKs are fighting — dual crash signal handlers, racing session-replay capturers, multiple ANR detectors — the agent names the conflict before any Luciq recommendation. Conflicts are surfaced independent of whether you adopt Luciq.
* **Evidence-cited recap.** Opens with up to six cited findings: a CLAUDE.md line quoted verbatim with line number, the money path `file:line`, a competitor SDK's specific config posture. Skips padding when context is sparse.
* **Three-bucket plan.** Groups Luciq products into "Recommended now", "Optional — add if you'd like", and "Can be added later", each with a timing reason. A product is never moved to "Can be added later" just because a competitor covers similar ground — that choice is yours.
* **Per-product walk.** For each product, one-line Ask (with the cited reason for fit) → diff-based Apply (derived from your profile: money path, competitor style, CLAUDE.md privacy line, auth flow `file:line`) → Summarize (done, what's left for you, move to next).
* **End-to-end activation.** After all products are configured, one consolidated round-trip that proves Luciq is working — a report or replay frame visible on your dashboard.
* **Handoff doc.** Writes `LUCIQ_ONBOARDING.md` at the repo root with two halves: a plain-English celebration half (what's working now, enabled products, dashboard URLs, three concrete next steps) and a cite-heavy audit trail half (every code change with `file:line`, conflicts detected, deferred items with revisit conditions, competitor coverage with reasons, dashboard capabilities split into "live now" and "unlock later").

## 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/luciqai/luciq-docs/blob/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step.

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

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-onboard/SKILL.md) from the public repo to `~/.claude/skills/luciq-onboard/SKILL.md` (user-global) or `.claude/skills/luciq-onboard/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-onboard
description: Use ONLY when the customer explicitly invokes onboarding on their own initiative, in a fresh message, with one of these phrases (or a close variant): "onboard me to Luciq", "walk me through Luciq", "tour Luciq", "help me get started with Luciq products". Nothing else is a trigger. Setup completing is NOT a trigger. The customer asking "what's next?" is NOT a trigger. The assistant suggesting onboarding and the customer agreeing is NOT a trigger. The customer must say an onboarding-shaped phrase themselves, unprompted. This skill reads the user's repo (code, CLAUDE.md, README, AGENTS.md), detects any existing mobile observability SDKs (Sentry, Crashlytics, Bugsnag, Datadog, Embrace, New Relic, App Center, Instabug-legacy, UXCam, Smartlook, MetricKit) along with the user's posture on each of them, and recommends Luciq products with cited rationale — never as a generic feature menu. Specifically NOT for first-time SDK install (use luciq-setup), not for upgrading SDK versions (use luciq-migrate), not for debugging a specific crash, hang, or user-reported bug (use luciq-debug), not for a deep PII / masking audit or compliance-framework prep (use luciq-masking-rules).
---

# Luciq Personalized Onboarding

End-to-end personalized walkthrough of the Luciq product suite for an app that already has the SDK installed. The skill scans the project to understand what the app is, what the team values, and what observability is already in place — then walks the user through the Luciq products that actually fit, with cited reasoning at every step. Skips products that don't fit. Frames excluded products positively as "add later" with a revisit condition.

The aim is a conversational experience the user remembers — one where every recommendation is justified by something the agent observed in their own repo, not a generic pitch.

## When NOT to use this skill

Hand off to a sibling skill (or simply don't run) for any of the following:

- **First-time SDK install** — the SDK isn't initialized yet → `luciq-setup`.
- **Upgrading a Luciq SDK version or migrating from the legacy Instabug SDK** → `luciq-migrate`.
- **Investigating a specific crash, hang, regression, or user-reported bug** → `luciq-debug`.
- **Deep PII audit, masking review, or compliance-framework prep (HIPAA / GDPR / SOC2 / PCI)** → `luciq-masking-rules`. Onboard does shallow per-view masking inline; the deep audit (auto-mask types, network mask key list, behavior matrix, consent gating, grayscale, FLAG_SECURE, SSUI `isPrivate`, compliance presets, pre-prod checklist) belongs in `luciq-masking-rules`.
- **API signature lookups** — point the user at https://docs.luciq.ai.
- **After `luciq-setup` completes.** Setup does not invite onboarding. Wait for the customer to invoke this skill on their own, in a fresh ask.

If the user's ask matches any of the above, STOP and route them. Running `luciq-onboard` on an uninstrumented project produces incorrect recommendations because every analysis step assumes the SDK init is already present.

## Canonical sources of truth

Verify product names, dashboard URLs, and config keys against the live docs before quoting them in the conversation. Hardcoded values in this skill are illustrative.

| Concern | Source |
| --- | --- |
| Product setup details (per platform) | https://docs.luciq.ai |
| iOS product setup | https://docs.luciq.ai/ios/setup-luciq-for-ios |
| Android product setup | https://docs.luciq.ai/android |
| React Native product setup | https://docs.luciq.ai/react-native |
| Flutter product setup | https://docs.luciq.ai/flutter |
| KMP product setup | https://docs.luciq.ai/kmp |
| User's apps and workspace | Luciq MCP `list_applications` (if authenticated) |
| User's prior crashes / patterns | Luciq MCP `crash_patterns`, `list_crashes`, `list_bugs` |
| The user's repo | local file system — read directly |

## Operating principles

These shape every conversational turn the skill produces. Internalize them; they're the difference between onboarding that feels like a wizard and onboarding that feels like a thoughtful peer.

1. **Cite every recommendation.** Never say "I recommend X" without naming the source: a CLAUDE.md line quoted verbatim, a file:line in the user's code, a precedent from their other Luciq apps, or a specific archetype rationale.
2. **Show your work before asking.** If a question can be answered by reading the repo, answer it — don't ask. Only ask the user things only the user knows.
3. **One conversational beat at a time.** Never present a wizard form. Present one product, one decision, one ask.
4. **Default to recommending, fall back to asking.** Every question is a tax on the user; reserve it for genuinely undecidable cases.
5. **Skip and defer are first-class.** A user who says "not now" hasn't failed — they've made a choice. Capture it with a reason and a revisit condition, then move on.
6. **Honest about competitors, never disparaging — and competitor presence is context, not a downgrade.** Position Luciq's strengths alongside what the user already has. Never criticize a competitor's product. **Never move a Luciq product out of "Recommended now" just because a competitor SDK covers similar ground.** If the Luciq product fits the customer's app on its own merits, recommend it on its own merits. Name the competitor honestly in the Ask so the customer can decide whether to adopt alongside, evaluate as a swap, or stay on what they have. The choice is theirs; the skill does not pre-decide deferral for them.
7. **Activation > configuration.** A configured product the user hasn't seen working is a half-delivery. End the walk with a single concrete verification step that produces real data on the user's dashboard.

## Workflow checklist

Track every phase. STOP on any phase that can't complete with confidence — never fake progress.

```
Onboarding Progress:
- [ ] 0. Detect mode (FAST / AUDIT / GUIDED)
- [ ] 1. Analyze the project (silent, parallel)
- [ ] 2. Recap — conflicts first, then app understanding
- [ ] 3. Present the plan (three positive buckets)
- [ ] 4. Product walk (Ask → Apply → Summarize per product)
- [ ] 5. End-of-walk activation (one consolidated Aha)
- [ ] 6. Handoff — write LUCIQ_ONBOARDING.md
```

## 0. Detect mode

Read the user's trigger phrasing and pick a mode. Different modes produce different conversation lengths.

| Trigger phrasing | Mode | Behavior |
| --- | --- | --- |
| "must-haves", "fast", "quick", "just the basics" | **FAST** | Apply the top 3 "Recommended now" products with one-line confirms each. No end-of-walk activation. ~2 minutes. |
| "what am I missing", "audit", "what should I set up" | **AUDIT** | Report-only. Print the analysis, present the plan in three buckets, write the handoff doc. Apply nothing without explicit per-product confirmation in a follow-up. |
| anything else, including "onboard me", "walk me through", "tour" | **GUIDED** | Full arc with conflict recap, per-product walk, and end-of-walk activation. ~10 minutes. This is the default. |

If unclear, default to GUIDED. Confirm the chosen mode in one line at the start so the user knows what they're about to spend time on.

## 1. Analyze the project (silent, parallel)

This phase runs without conversation. The output is a single structured **project profile** the rest of the workflow reads from. Do all four analysis tracks in parallel — they're independent and deterministic.

### Track A — Context docs

Read in priority order. Stop at the first source rich enough to anchor the recap. Augment with later sources if shallow.

1. `CLAUDE.md` (repo root + any nested)
2. `AGENTS.md`, `.cursorrules`, `.windsurfrules`
3. `README.md`, `ARCHITECTURE.md`, `docs/*.md`

Extract: app purpose (one sentence), team's stated priorities, PII / privacy posture, mention of compliance frameworks (GDPR, HIPAA, SOC2, PCI), and any quotable line (with line number) the recap can cite verbatim. The cited quote is what makes the recap feel uncanny — find at least one if context exists.

### Track B — Archetype + money path + auth + sensitive view enumeration

From the existing `luciq-setup` profile (or a fresh scan), infer:

- **Archetype**: e-commerce / fintech / social / media / productivity / gaming / B2B-tool / internal — based on deps (`Stripe`, `RevenueCat`, `Firebase Auth`, `Auth0`) and screen names (`CheckoutView`, `FeedView`, `DashboardView`).
- **Money path**: the file:line of the screen where revenue is captured (checkout, paywall, subscription). E-commerce without an identified money path means the archetype guess is shaky — ask once.
- **Auth flow**: login and logout file:line. Needed for product recommendations that depend on `identifyUser`.
- **Sensitive view enumeration**: for each sensitive screen (money path, auth, settings/profile, anywhere PII surfaces), enumerate the *individual views* that bind to PII-flavored properties. This is the difference between *"mask the checkout screen"* and *"mark these 4 specific TextFields as `luciq_privateView`."* Downstream products (Bug Reporting, Session Replay) consume this list to propose per-view privacy markers — not just screen-level masking.

#### What counts as a "sensitive view"

A view is sensitive when **two signals agree**:

1. **The view type can render or capture text/images.** SwiftUI: `TextField`, `SecureField`, `Text`, `TextEditor`, `Image`, `AsyncImage`. UIKit: `UITextField`, `UITextView`, `UILabel`, `UIImageView`. Compose: `TextField`, `OutlinedTextField`, `Text`, `Image`, `AsyncImage`. Android Views: `EditText`, `TextView`, `ImageView`. React Native: `TextInput`, `Text`, `Image`. Flutter: `TextField`, `Text`, `Image`.
2. **The bound property or surrounding identifier matches a PII pattern.** Identifier-shaped strings: `password`, `email`, `cardNumber`, `card_number`, `cvv`, `ssn`, `pin`, `iban`, `dob`, `birthDate`, `phone`, `phoneNumber`, `address`, `firstName`, `lastName`, `fullName`, `accountNumber`, `routingNumber`, `taxId`, `passport`, `driverLicense`.

Filter out matches in test/spec/mock paths, validator/regex utilities, and anything under `node_modules`, `Pods/`, `build/`. False positives are common — never apply markers in bulk.

#### Suggested marker per platform (verify against the live setup docs)

| Platform | View type | Suggested marker |
|---|---|---|
| iOS SwiftUI | `TextField`, `SecureField`, `Text`, `Image`, `TextEditor` | `.luciq_privateView()` modifier, or wrap in `LuciqPrivateView { ... }` |
| iOS UIKit | `UITextField`, `UILabel`, `UITextView`, `UIImageView` | `view.luciq_privateView = true` (UIView category property) |
| Android Compose | `TextField`, `Text`, `Image` | `Modifier.luciqPrivate()` |
| Android Views | `EditText`, `TextView`, `ImageView` | `Luciq.addPrivateViews(view)` or `LuciqPrivateView.setPrivateView(view, true)` |
| React Native | `TextInput`, `Text`, `Image` | wrap in `<LuciqPrivateView>...</LuciqPrivateView>` |
| Flutter | `TextField`, `Text`, `Image` | wrap in `LuciqPrivateView(child: ...)` |

Verify the exact import path, method signature, and any version gating against the live setup docs for the user's platform before quoting them in a diff. The markers above evolved through the Instabug → Luciq rebrand and may differ across SDK versions.

The structured output is `sensitive_views: [{screen, file, line, view_type, binding, suggested_marker}]` on the profile.

**Confirmation policy in Phase 4 Apply.** Confirm the **first 3–5 views individually** so the user sees the pattern, the platform marker syntax, and the kinds of false positives the detector can produce. After that threshold, switch to **batch confirm**: list the remaining views in one block (`file:line — view_type — binding`) and ask a single question — *"apply markers to the remaining N views? [yes / no / show details for any specific one]."* If the user picks a row to inspect, fall back to per-match confirmation for that row only.

Never bulk-apply silently — but never burn 15 conversational turns on a single screen either. The point of per-match confirmation is to catch false positives, not to ritualize every view.

If a competitor SDK in Track C has equivalent view-level masking (e.g., Sentry replay `mask` tag, UXCam's view-tagging, Smartlook's blacklisted views), translate the **same view set** to Luciq's marker — that's the privacy-posture style-match at the view level, mirroring what the user's team already considers sensitive.

#### Accessibility posture (lightweight, same pass)

In the same sweep that enumerates sensitive views, count how often the platform-appropriate accessibility identifier API appears on **interactive** views (buttons, text fields, tappable images). The identifiers themselves do no technical work for Luciq — they're a *posture signal* that lets Phase 4 pick more accessible defaults.

| Platform | Identifier APIs to grep |
|---|---|
| iOS SwiftUI / UIKit | `.accessibilityIdentifier(`, `accessibilityIdentifier =`, `accessibilityLabel =` |
| Android Views | `contentDescription`, `setContentDescription(` |
| Android Compose | `Modifier.semantics`, `Modifier.testTag(`, `contentDescription =` |
| React Native | `testID=`, `accessibilityLabel=` |
| Flutter | `Semantics(identifier:`, `Semantics(label:` |

Filter out matches in tests, mocks, generated code, and `node_modules` / `Pods/` / `build/` — same exclusions as sensitive-view enumeration.

Compute coverage as `(interactive views with any identifier) / (total interactive views)` and pair with explicit CLAUDE.md / README mentions of WCAG, VPAT, VoiceOver, TalkBack, screen reader, a11y audit.

The structured output is one field on the profile:

```
accessibility_posture: "strong"  | coverage > 60%  OR  doc mention present
                    | "partial"  | 10% ≤ coverage ≤ 60%
                    | "absent"   | coverage < 10%  AND  no doc mention
```

Also stash one cited example (`file:line` of a representative identifier site) so Phase 2's recap can quote it verbatim — same evidence-citing pattern the rest of the skill uses.

### Track C — Mobile observability SDK scan + deep config read

Detect every active mobile observability SDK on the project, then for each one read its init site and extract what's on, what's off, and what's tuned. See `references/observability-sdks.md` for the v1 detection patterns, config keys per SDK, and the coverage matrix.

Three signals per SDK:

1. **Manifest match** — package declared in Podfile / Package.swift / build.gradle(.kts) / package.json / pubspec.yaml.
2. **Init call match** — the start/configure call is present in source.
3. **Config artifact match** — config files like `GoogleService-Info.plist`, `sentry.properties`.

A package without an init call is **shelf-ware** — flag it (offer cleanup later), don't treat it as active. All three signals together = confirmed active.

For each confirmed-active SDK, extract:
- **What's on** (enabled features)
- **What's off** (explicitly disabled features — this is the most valuable column, signals team intent)
- **What's tuned** (non-default sample rates, thresholds, redaction lists, env gating)

From the extracted config, infer a **style** (one line): "privacy-conservative, low sample, strict env gating" / "permissive, full PII capture, debug+release". Luciq's defaults in Phase 4 should match this style.

### Track D — Workspace precedent (MCP)

If the Luciq MCP is authenticated, call `list_applications` to enumerate the user's other Luciq apps. For each, optionally call `apm_list_groups` and read masking/replay config patterns to infer the team's "house style" across apps. A precedent quote like *"your other 3 apps run replay at 5%"* is one of the strongest trust-building moves available.

If MCP is not authenticated, skip silently — do not nag the user to authenticate. Precedent is a nice-to-have, not a requirement.

### Track E — Infrastructure, distribution & integration sites

Five small enumerations that turn vague handoff pointers ("set up dSYM upload in CI", "register push token") into real Phase-4 Apply diffs. Each unblocks one or more product cards.

**Lazy execution.** Track C runs first in the parallel sweep (cheap manifest scan + init detection only). Once Track C identifies which Luciq products will plausibly land in *Recommended now* vs *covered by competitor*, gate the Track E sub-tracks:

| Sub-track | Run when | Skip when |
|---|---|---|
| **E1 — CI / build system** | No active competitor crash reporter; OR competitor present but flagged as a vendor-swap candidate by the user | Crashlytics / Sentry / Bugsnag / Embrace is active with confirmed symbol upload in their existing CI step (Crash Reporting will land in "covered by another SDK") |
| **E2 — Push token site** | `auth_flow` was detected in Track B, OR push SDK is in the manifest | No auth flow and no push SDK in manifest (In-App Replies is going to "Can be added later" with revisit-when-identifyUser-wired anyway) |
| **E3 — Network client + base URLs** | No active competitor APM with network tracking | Datadog RUM / New Relic Mobile / Sentry perf / Firebase Performance is active with network capture on (APM will land in "covered by another SDK") |
| **E4 — Distribution model** | Always | — |
| **E5 — Locales** | Always | — |

When a gate causes a skip, the consumer card falls back to its doc-pointer behavior (handoff "What's left for you" line instead of a Phase-4 diff). Re-running the skill after the user resolves the competing SDK will pick up the skipped detection on the next pass.

#### E1 — CI / build system

Detect the CI system in use. Determines whether symbol upload (dSYM, ProGuard / R8 mapping, native debug symbols) and env-gated SDK init can be proposed as concrete diffs to existing workflows rather than left-for-you doc pointers.

| Signal | Locations to grep |
|---|---|
| GitHub Actions | `.github/workflows/*.yml`, `*.yaml` |
| Fastlane | `Fastfile`, `fastlane/` |
| Bitrise | `bitrise.yml` |
| CircleCI | `.circleci/config.yml` |
| GitLab CI | `.gitlab-ci.yml` |
| Xcode Cloud | `ci_scripts/`, `.xcode-cloud/` |
| App Center | `appcenter-*.yml` |

Output: `ci_system: { kind, primary_workflow_file, release_lane_or_job, env_matrix }` or `none`. Consumed by **Crash Reporting** (dSYM / mapping upload step), **SDK init** (env-gated tokens when `env_matrix` has multiple build configs).

#### E2 — Push notification registration site

Detect where the app obtains its push token. Makes In-App Replies' delivery path (and Surveys' push delivery, when used) a real diff at a known file:line rather than a doc pointer.

| Platform | Grep patterns |
|---|---|
| iOS | `didRegisterForRemoteNotificationsWithDeviceToken`, `UNUserNotificationCenter`, `registerForRemoteNotifications` |
| Android | `FirebaseMessaging.getInstance()`, `onNewToken`, classes extending `FirebaseMessagingService` |
| React Native | `messaging().getToken()`, `@react-native-firebase/messaging`, `expo-notifications` `getDevicePushTokenAsync` |
| Flutter | `FirebaseMessaging.instance.getToken()`, `flutter_local_notifications` |

Output: `push_token_site: { file, line, library }` or `null`. Consumed by **In-App Replies** Apply step (`Luciq.setPushNotificationToken(token)` immediately after token acquisition).

#### E3 — Network client + base URLs

Detect the network client and the host strings it talks to. Lets **APM** propose specific tracked-hosts and per-header masking diffs at the client init site, instead of generic suggestions.

| Platform | Grep patterns |
|---|---|
| iOS | `Alamofire.AF`, `Session(`, `URLSession`, base-URL constants in `Configuration.swift` / `APIConstants.swift` |
| Android | `OkHttpClient.Builder()`, `Retrofit.Builder().baseUrl(` |
| React Native | `axios.create({ baseURL:` |
| Flutter | `Dio()`, `BaseOptions(baseUrl:` |

Output: `network_client: { type, init_file_line, base_urls: [...] }`. Consumed by **APM** (concrete `setNetworkHosts([...])` call, masking proposed at the interceptor / adapter site rather than generically).

#### E4 — Distribution model

Detect how the app reaches users. Explicit signal — current archetype + store-presence inference is too indirect for honest handling of App Ratings and Rollout Management.

| Channel | Signals |
|---|---|
| App Store | `pilot`, App Store Connect API key, `upload_to_app_store` lane, `.itmsp` in CI |
| Play Store | `upload_to_play_store`, Google Play API key, `bundle release` task |
| TestFlight only | `pilot` lane without `deliver` / `upload_to_app_store` |
| Firebase App Distribution | `firebase appdistribution:distributors`, `appDistribution` Gradle task |
| Enterprise | Distribution provisioning profile with enterprise team ID |
| Internal / MDM | Intune / JAMF / AirWatch config artifacts |

Output: `distribution_model: { primary: "appstore" | "playstore" | "testflight-only" | "firebase-appdist" | "enterprise" | "internal" | "unknown", channels: { ... booleans ... } }`. Consumed by **App Ratings** (real anti-signal when not store-bound), **Rollout Management** (unlock condition), **Surveys** (pre-launch anti-signal).

#### E5 — Locales

Count the locales the app ships. Multi-locale apps need locale-aware survey copy, App Ratings dashboards split per locale, and Bug Reporting prompt translations — and the skill should surface that as a "What's left for you" item, not silently default to English.

| Platform | Signals |
|---|---|
| iOS | `*.lproj` directories, `Localizable.strings`, `String(localized:)` |
| Android | `res/values-*/strings.xml` |
| React Native | `i18next`, `react-i18next`, `expo-localization`, `react-native-localize` |
| Flutter | `flutter_localizations`, `intl`, generated `S.of(context)` |

Output: `locales: { codes: [...], count: N }`. Consumed by **Surveys** + **App Ratings** + **Bug Reporting** ("What's left for you" notes about locale-aware copy on the dashboard when `count > 1`).

### Conflict detection (in the same pass)

Conflicts are deterministic logic over what the analysis already found — compute them in the same parallel sweep. See `references/observability-sdks.md` for the per-SDK conflict rules. Severity:

- **High** — dual crash handlers, racing ANR detectors, multiple session-replay capturers. Today's capture is non-deterministic. State this honestly.
- **Medium** — multiple network interceptors, overlapping method swizzling on lifecycle methods.
- **Low** — shelf-ware packages, deprecated SDKs still in the manifest.

Conflicts get **surfaced** in Phase 2's recap, never as a separate phase. Capture all of them in the handoff doc whether or not the user addresses them.

## 2. Recap — conflicts first, then app understanding

Open with conflicts if any exist. State them plainly, separate from any Luciq recommendation. This is the trust moment — surfacing problems the user has *independent* of whether they adopt Luciq is what earns credibility for everything that follows.

Example opener for a project with detected conflicts:

> Heads-up before we start: Sentry (AppDelegate.swift:23) and Firebase Crashlytics (AppDelegate.swift:18) both install crash signal handlers. Only one captures any given crash today — your crash capture is non-deterministic. Worth fixing regardless of what we do next.

Then the cited app understanding. Every line is sourced — a CLAUDE.md quote with line number, a file:line for the money path, an inferred style from a competitor's config:

> Quick read on your app:
> - iOS SwiftUI e-commerce app. Money path on `CheckoutView.swift:42`.
> - Your CLAUDE.md line 12 says *"never log financial data"* — I'll default masking aggressive on checkout.
> - Sentry is configured conservatively (replay at 5%, `attachScreenshot: false`, `sendDefaultPii: false`) — Luciq's defaults below will match that posture.
> - Your other 2 apps in this workspace both use Bug Reporting with shake invocation.

If no conflicts and no rich context exist, the recap is two lines instead of six — don't pad. Quality of evidence over quantity of lines.

**Cap the recap at 6 cited lines.** The recap's job is to land the *"how did you know that"* moment in a single readable block — not to inventory every signal Phase 1 collected. Across all detection tracks (A, B, C, D, E + a11y posture + sensitive views) the profile may carry 10+ citable findings; the recap picks the strongest 4–6 and saves the rest for the per-product Asks in Phase 4, where each citation fires next to the product it actually justifies.

Pick recap citations by this priority:
1. A verbatim CLAUDE.md / AGENTS.md / README line with line number (highest — these are the user's own words).
2. The money path file:line (anchors the archetype claim).
3. A competitor SDK's specific posture quote (e.g. `tracesSampleRate: 0.1` from Sentry config) — proves the skill read their actual config, not just the package list.
4. A workspace-precedent quote from MCP (*"your other 3 apps run replay at 5%"*).
5. The strongest one a11y / infrastructure citation if the team's posture is clearly differentiated (e.g. *"every TextField on CheckoutView labels accessibilityIdentifier"*).

Citations that don't make the recap cut — push token site, network base URLs, CI workflow path, locale count, individual sensitive-view enumerations — land in the corresponding Phase 4 Ask where they justify a *specific* recommendation. That's where they have the most force anyway.

## 3. Present the plan — three positive buckets

Score each Luciq product against the profile (see `references/product-cards.md` for per-product fit signals and anti-signals). Group into three buckets. **Never use negative labels like "not a fit" or "skip" in user-facing copy** — always frame as timing.

Only products with an SDK-side diff to confirm are eligible for the buckets. Capabilities that auto-derive from configured products, live only on the dashboard, or need Luciq support / admin enablement (FFS, App Health, Issues List, Business Impact, Alerts & Rules, Rollout Management, Team Ownership, One Code Apps, Detect / Resolve / Release Agents) are handled in Phase 6 via `references/post-onboarding-capabilities.md` — never bucketed here.

- **Recommended now** — strong fit, applying this session.
- **Optional — add if you'd like** — reasonable fit; user's call this session or later.
- **Can be added later** — better timed for the future, with a specific revisit condition stated.

Every item in "Can be added later" must name *when* it makes sense:

> Can be added later:
> – **App Ratings** — best once you're live on the App Store. I'll wire it then.
> – **In-App Replies** — depends on `identifyUser`. When you wire your auth flow, add this.
> – **Feature Requests** — fits better once you have an active user base submitting feedback.

Only put a product in "Can be added later" for a *timing* reason — pre-launch, missing prerequisite, awaiting a real-world event. **Never put a product in "Can be added later" just because a competitor covers similar ground.** That's the customer's call, not the skill's. See Operating Principle 6.

Close the plan presentation with one question: *"Want to adjust any bucket before I start?"* If the user moves something between buckets, accept it and proceed.

In AUDIT mode, stop here — write the handoff doc (Phase 6) and exit. Don't apply anything.

## 4. Product walk — per-product micro-flow

For each product in "Recommended now" (and any "Optional" the user picked up), run the three-step micro-flow. Per-product copy and apply targets live in `references/product-cards.md`. Read that file before starting the walk.

### A. Ask

One line: name the product, give one-line value, give the user's *specific* reason for fit (cited).

> **Bug Reporting** — your users shake the phone or screenshot to send a report with logs, network, repro steps. Strong fit for you because no competitor on your stack handles user-initiated reports, and your CLAUDE.md mentions support load. Include it?

Three responses available: **Yes / No / Tell me more**. "Tell me more" expands the why; "No" goes to defer (record the reason).

### B. Apply

Derive the *exact* changes from the profile, not from defaults. Show a diff before applying. Confirm.

- Use the **money path** to scope APM and Session Replay aggressiveness.
- Use the **competitor's style** to set sample rates, screenshot capture, env gating.
- Use the **CLAUDE.md privacy line** to set masking aggressiveness.
- Use the **auth flow file:line** for `identifyUser` injection sites.

If a required file edit conflicts with uncommitted user changes, STOP and surface — don't overwrite work.

Delegate the actual API calls to the patterns in `luciq-setup` when they exist (don't reinvent installation primitives).

### C. Summarize

After applying, three short blocks:

> Done. Here's what I did:
> - Enabled Bug Reporting in `NotDemoAppApp.swift:14`
> - Set invocation to shake (matched your Sentry's conservative posture)
> - Masked text inputs on `CheckoutView.swift:42` per your CLAUDE.md line 12
>
> What's left for you (no code needed):
> - Nothing required — reports start arriving on next launch.
>
> Moving to the next product: **Session Replay**.

The "What's left for you" block lives in `references/product-cards.md` per product. Only list items the user genuinely needs to do — dashboard config, optional symbol upload setup. Never list third-party tool integrations by name. If nothing is left for the user, say so plainly.

## 5. End-of-walk activation

After all selected products are configured, run **one consolidated activation moment** — not one per product. The aim is a single concrete round-trip that proves Luciq is working end-to-end.

Use the **primary product** as the activation vehicle (Bug Reporting if included, otherwise the highest-ranked recommended product). The verification has three concrete steps and one waiting moment:

> All set. To see Luciq working end-to-end:
>  1. Build and launch the app.
>  2. Shake the simulator (Ctrl+Cmd+Z) — or use your chosen invocation event.
>  3. Type "test from onboarding" and submit.
>  4. Open your dashboard: `<dashboard URL for this app>`
>
> Tell me when you see the report — or just come back later.

Wait for confirmation. If the user reports they don't see it after 60 seconds, diagnose (build still running? wrong app token? simulator not invoking?) — never claim success without confirmation. If the user says "later," accept and move on — record in the handoff that activation was not verified.

In FAST mode, skip this phase. The handoff doc still includes the verification steps so the user can do it themselves.

## 6. Handoff — write LUCIQ_ONBOARDING.md

Write `LUCIQ_ONBOARDING.md` at the repo root using the template in `references/handoff-template.md`. The doc is the durable artifact of the session — re-readable next week, hand-off-able to a teammate, queryable by `luciq-debug` later.

The doc is structured as **two halves separated by a hard `---` divider**. Don't merge them, don't reorder, don't add a third section. The split is what makes the doc usable by both a PM/exec skim and an engineer audit.

**🎉 Top half — the celebration.** Plain English only. Anyone — engineer, PM, exec — should grasp what shipped in under a minute. No API names, no file:line citations, no SDK jargon. Contains:

- A stat strip on one line: conflicts count, *All PII masked*, any other earned win this session genuinely delivered.
- One short paragraph (2–3 sentences) of what the app can do now that it couldn't yesterday — lead with the team's benefit.
- **Enabled now** — a two-column table per active product: emoji + name, and a one-sentence user-facing value (what the team/user gets, never what the API does).
- **Ready when you are** — a two-column table per deferred product: emoji + name, and a plain-English "turn on when…" trigger.
- **Do this next** — three concrete steps + the three primary dashboard URLs. This is the verification round-trip from Phase 5, restated for the durable doc.

**🔧 Bottom half — the audit trail.** Cite-heavy, complete, organized for engineers. Opens with an explicit *"skip if you just wanted the wins above"* italic line. Covers:

- **What was wired (and where)** — table per product: code change with file:line, key decision with citation. Mark *⚠️ Not verified yet* until the user confirms first data; replace with *✅ Verified <timestamp>* once activation lands.
- **What's left for you (no code needed)** — doc-pointer items from each active product, deduplicated.
- **"Ready when you are" — implementation notes** — per-product pointer for when the trigger fires.
- **Dashboard capabilities** — split into *Live now* (auto-derived from this session's products) and *Unlock later* (with revisit condition stated verbatim), both sourced from `references/post-onboarding-capabilities.md`.
- **Conflicts detected (independent of Luciq)** — every conflict from Phase 2, even if the user didn't address it this session. Accountability survives.
- **Competitor coverage** — products the customer chose to stay on a competitor for, with their reason verbatim if given.
- **How the agent figured this out** — context docs cited, money path, auth flow, sensitive views, accessibility posture, detected SDK style, workspace precedent.
- **When to reach for sibling skills** — `luciq-debug`, `luciq-migrate`, `luciq-verify`, `luciq-masking-rules`. Always include `luciq-masking-rules` when Bug Reporting, Session Replay, or APM were configured — per-view markers applied this session are layer 1 of 3, and the user should know how to revisit the full posture later.

**Footer.** Session metadata (mode, operator, SDK name + version, package manager, product name, import) lives in an italic one-liner at the very bottom — present for the audit trail, absent from the celebration.

**Dashboard URL sourcing.** Derive URLs from MCP `list_applications` or the live Luciq dashboard. Never construct paths by stitching slug + capability name — surface shapes change between versions and constructed URLs become broken links.

If `LUCIQ_ONBOARDING.md` already exists, *append* a new dated session block — don't overwrite. The file accumulates the team's Luciq journey across sessions and contributors.

## Style

- ALWAYS show diffs before applying any code edit. Confirm.
- Cite every recommendation with a source — CLAUDE.md line, file:line, precedent quote, or named archetype rationale.
- One conversational beat at a time. No wizard forms.
- Skip and defer are first-class. Capture reasons and revisit conditions.
- Never criticize a competitor. Position Luciq alongside what's there.
- Verify product names and dashboard surfaces against the live docs before quoting them.
- Match the team's observed style — if their Sentry is privacy-conservative, Luciq's defaults should be too.

## Red flags — STOP and surface to the user

If you catch yourself thinking any of these, you're about to ship a bad onboarding. Stop, surface, do not proceed:

- *"I'll recommend this even though there's no signal for it — it's a good Luciq feature."* Recommendation without citation breaks the trust the whole skill rests on. If you can't cite, don't recommend.
- *"I'll skip the conflict — it's not Luciq's problem."* Surfacing conflicts is the trust-building move. Skip it and the rest of the conversation feels like sales.
- *"Their CLAUDE.md says one thing but I'll override it because Luciq's default is better."* The user's stated intent wins. If you disagree, ask, don't override.
- *"I'll claim activation succeeded — the build is probably fine."* Activation needs explicit user confirmation that data is visible on the dashboard. Otherwise it's a half-delivery.
- *"I'll keep going even though MCP failed."* MCP failures are fine to skip silently in Phase 1 (precedent is optional). But if MCP fails *during apply* — e.g., looking up an app token — surface and stop. Don't fabricate a token or guess.
- *"They said 'not now' but I'll keep pitching it."* Defer means defer. Capture the reason, move on, leave it in the handoff doc with a revisit condition.
- *"I'll flag App Ratings as 'not a fit' since this is B2B."* Don't say "not a fit." Say "better added when you launch on the App Store" — same honesty, no sting.
- *"Sentry covers crashes; I'll park Luciq Crash in 'add later.'"* Don't pre-defer a Luciq product because a competitor covers similar ground. Recommend it on its own merits, name the competitor in the Ask, and let the customer choose to add alongside, swap, or stay on what they have. Pre-deferring takes the choice away from them and reads as a sales-shy posture, not honesty.
- *"Sentry covers crashes; I'll oversell Luciq Crash and pressure them to switch."* The opposite failure. Name the competitor honestly, state Luciq's specific differentiators, and let the customer decide. No pressure language, no FUD about the competitor.

Every shortcut here trades "looks done" for "actually helpful." The skill's job is to make the user *feel understood* and leave them with working Luciq products they trust — not to maximize feature adoption in one session.
````

</details>

## Prerequisites

* The Luciq SDK must be installed and initialized in your project. This skill assumes the SDK init is already present — running it on an uninstrumented project produces incorrect recommendations. Use [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup) first.
* The Luciq MCP server is not required but adds depth. If authenticated, the agent reads your other apps' configs for workspace precedent and can pull crash and bug patterns via `list_applications`, `crash_patterns`, `list_crashes`, and `list_bugs` to ground product recommendations.

{% hint style="info" %}
If the Luciq MCP server isn't connected, the skill runs without workspace precedent (Track D). Only the local project analysis is required.
{% endhint %}

## How the agent onboards

The agent tracks seven explicit phases. It stops rather than faking progress if a phase can't complete with confidence.

### Phase 0. Detect mode

Three modes based on how you phrase the request:

| Trigger phrasing                                                 | Mode       | Behavior                                                                                                                    |
| ---------------------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| "must-haves", "fast", "quick", "just the basics"                 | **FAST**   | Top 3 "Recommended now" products with one-line confirms each. No end-of-walk activation. \~2 minutes.                       |
| "what am I missing", "audit", "what should I set up"             | **AUDIT**  | Report only. Analysis + three-bucket plan + handoff doc. No apply without explicit per-product confirmation in a follow-up. |
| anything else, including "onboard me", "walk me through", "tour" | **GUIDED** | Full arc with conflict recap, per-product walk, and end-of-walk activation. \~10 minutes. Default.                          |

The chosen mode is confirmed in one line at the start.

### Phase 1. Analyze the project

Silent and parallel. The output is a single structured **project profile** the rest of the workflow reads from.

**Track A — Context docs.** Reads `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `README.md`, `ARCHITECTURE.md`, and project docs in priority order. Extracts: app purpose, team priorities, PII / privacy posture, compliance framework mentions, and a quotable line with line number.

**Track B — Archetype + money path + auth + sensitive view enumeration.** Infers the app archetype (e-commerce, fintech, social, media, productivity, gaming, B2B, internal) from dependencies and screen names. Finds the money path (`file:line` of the checkout or paywall screen), the auth flow login / logout locations, and enumerates individual views bound to PII-flavored properties on sensitive screens. Also computes an accessibility posture signal (`strong` / `partial` / `absent`) based on how consistently the team annotates interactive views with accessibility identifiers.

**Track C — Mobile observability SDK scan.** Detects every active mobile observability SDK in the project (Sentry, Crashlytics, Bugsnag, Datadog, Embrace, New Relic, UXCam, Smartlook, and others). For each, reads the init site and extracts what's on, what's off, and what's tuned — then infers a one-line style ("privacy-conservative, low sample, strict env gating") that drives Luciq's defaults in the Apply step. A package without an init call is shelf-ware and flagged separately.

**Track D — Workspace precedent (optional, MCP).** If the Luciq MCP is authenticated, reads config patterns from your other Luciq apps. A precedent quote is one of the strongest trust-building moves available. Skipped silently if MCP isn't authenticated.

**Track E — Infrastructure, distribution & integration sites.** Five gated sub-tracks:

| Sub-track                           | What it detects                                                        | Consumer product                         |
| ----------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------- |
| **E1 — CI / build system**          | `.github/workflows`, `Fastfile`, `bitrise.yml`, etc.                   | Crash Reporting (symbol upload step)     |
| **E2 — Push token registration**    | `didRegisterForRemoteNotificationsWithDeviceToken`, `onNewToken`, etc. | In-App Replies                           |
| **E3 — Network client + base URLs** | `OkHttpClient.Builder()`, `Alamofire.AF`, `axios.create`, etc.         | APM (tracked-host diffs)                 |
| **E4 — Distribution model**         | App Store, Play Store, Firebase App Distribution, Enterprise, Internal | App Ratings, Rollout Management          |
| **E5 — Locales**                    | `*.lproj`, `res/values-*`, `i18next`, etc.                             | Surveys, App Ratings, Bug Reporting copy |

Each sub-track is gated and only runs when its consumer product is plausibly in scope.

**Conflict detection** runs in the same pass. Severity: High (dual crash handlers, racing replay capturers) / Medium (multiple network interceptors) / Low (shelf-ware packages).

### Phase 2. Recap — conflicts first, then app understanding

Opens with any conflicts detected, stated plainly and independent of Luciq recommendations. Then the cited app understanding: up to six lines, each sourced to a specific file, line number, or config value. Two lines is fine when context is sparse — no padding.

### Phase 3. Present the plan — three positive buckets

Groups Luciq products into three buckets. "Can be added later" always names a timing reason — pre-launch, missing prerequisite, awaiting a real-world event. Products are never deferred just because a competitor covers similar ground.

Only products with an SDK-side code diff are bucketed here. Dashboard-only capabilities and admin-enablement features appear in the handoff doc under "Unlock later".

Closes with one question: "Want to adjust any bucket before I start?" In **AUDIT mode**, the agent stops here and writes the handoff doc.

### Phase 4. Product walk

For each "Recommended now" product (and any "Optional" you pick up), three steps:

1. **Ask** — product name, one-line value, your specific cited reason for fit. Yes / No / Tell me more.
2. **Apply** — derived from your profile, not from defaults: money path scopes APM aggressiveness, competitor style sets sample rates and screenshot behavior, CLAUDE.md privacy line sets masking aggressiveness, auth flow `file:line` is the `identifyUser` injection point. Diff shown before any edit.
3. **Summarize** — what was done, what's left for you, move to the next product.

### Phase 5. End-of-walk activation

One consolidated activation moment after all selected products are configured. Uses the primary product (Bug Reporting if included, otherwise the highest-ranked recommended product):

1. Build and launch the app.
2. Trigger the configured invocation event.
3. Submit a test report.
4. Open the dashboard and confirm the report arrives.

The agent waits for your confirmation. If you say "later," the steps appear in the handoff doc. **FAST mode** skips this phase.

### Phase 6. Handoff — write LUCIQ\_ONBOARDING.md

Writes `LUCIQ_ONBOARDING.md` at the repo root, or appends a new dated session block if the file already exists. Structured as two halves separated by a hard `---` divider:

**Top half — the celebration.** Plain English. Anyone — engineer, PM, exec — grasps what shipped in under a minute. A stat strip, a 2–3 sentence paragraph on what the app can do now, an "Enabled now" table per active product, a "Ready when you are" table per deferred product, and three concrete next steps with dashboard URLs.

**Bottom half — the audit trail.** Cite-heavy, organized for engineers. Every code change with `file:line` and key decision, "what's left for you" doc-pointer items per product, implementation notes for deferred products, dashboard capabilities split into "live now" and "unlock later", every conflict detected (even if unresolved), competitor coverage with reasons verbatim if given, how the agent analyzed the project, and pointers to sibling skills — including `luciq-masking-rules` when Bug Reporting, Session Replay, or APM were configured, since the per-view markers applied this session are layer 1 of 3.


# luciq-masking-rules

The luciq-masking-rules skill audits PII masking across all three Luciq layers, finds gaps against your compliance framework, and closes them.

End-to-end PII posture audit and masking walkthrough for an app that already has the Luciq SDK installed. The agent scans all three masking layers — automatic type-based screenshot masking, per-view markers on sensitive views, and network masking configuration — surfaces gaps against your stated compliance posture, and walks you through closing them one control at a time with cited evidence.

The audit is designed to be re-run: before launch, before a new screen ships, or before a compliance review — without redoing onboarding.

## Use this skill when

* You ask to "audit my PII", "check my masking", "review Luciq privacy / PII posture", or "what's masked in Luciq?"
* You ask to "prep Luciq for HIPAA", "GDPR", "SOC 2", "PCI-DSS", "CCPA", or "FERPA"
* You ask to "add masking to \[a screen]" and want a broader posture review alongside it

## What the agent does

* **Posture scan.** Reads all three masking layers in parallel (silent): automatic screenshot masking types at SDK init, per-view privacy markers on sensitive views (with a flag for each view showing whether it's already marked), and network masking config including `obfuscateLog`, `omitLog`, and custom mask key extensions. Also checks five defense-in-depth controls: consent gating before Session Replay, grayscale screenshots, `FLAG_SECURE` on Android, `usersPageEnabled`, and server-side UI inflate sites that require an `isPrivate` flow.
* **Compliance alignment.** If you name a framework (HIPAA, GDPR, PCI-DSS, SOC 2, CCPA, FERPA), the agent loads the matching preset and compares each profile value against it in the same scan pass.
* **Evidence-cited recap.** Opens with one line stating overall posture honestly — "Strong on view-level, weak on network" — then up to six cited findings: `file:line`, SDK version, a CLAUDE.md quote with line number.
* **Bucketed plan.** Groups gaps into three positive buckets: *close now* (clear apply step, named compliance fit, high impact), *optional — add if you'd like* (defense-in-depth, not strictly required), and *monitor* (controls already in place with revisit conditions).
* **Per-control walk.** For each "close now" item — and any "optional" you select — the agent runs a three-step micro-flow: Ask (one-line with cited gap), Apply (diff shown before any edit; server-side controls become copy-pasteable support ticket lines), and Summarize.
* **Dashboard verification.** After controls are applied, one concrete round-trip to confirm masked regions render as solid black rectangles on a session in the dashboard.
* **Handoff doc.** Writes `LUCIQ_MASKING.md` at the repo root — posture snapshot by layer, controls applied with `file:line`, deferred items with revisit conditions, server-side request lines, and a pre-production privacy checklist. Appends a new dated block if the file already exists.

## 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/luciqai/luciq-docs/blob/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step.

After install, the skill is available as `/luciq-skills:luciq-masking-rules`.

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-masking-rules/SKILL.md) from the public repo to `~/.claude/skills/luciq-masking-rules/SKILL.md` (user-global) or `.claude/skills/luciq-masking-rules/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-masking-rules
description: Use ONLY when the customer explicitly invokes a PII / masking audit on their own initiative, in a fresh message, with one of these phrases (or a close variant) — "audit my PII", "check my masking", "review Luciq privacy / PII posture", "prep Luciq for HIPAA / GDPR / SOC2 / PCI", "add masking to <screen>", "what's masked in Luciq?". Nothing else is a trigger. `luciq-onboard` finishing is NOT a trigger. The assistant suggesting a PII review and the customer agreeing is NOT a trigger. The customer must say a PII-shaped phrase themselves, unprompted. This skill scans the user's repo and Luciq SDK config for masking posture (auto-mask types, per-view markers, network auto-masking, manual obfuscate/omit, consent gating, grayscale, FLAG_SECURE, SSUI isPrivate handling), compares it against compliance frameworks the user mentions, and walks the user through adding the controls that fit — with cited rationale. Specifically NOT for first-time SDK install (use `luciq-setup`), not for the full product walk (use `luciq-onboard`), not for SDK upgrades (use `luciq-migrate`), not for debugging a specific incident (use `luciq-debug`).
---

# Luciq Masking Rules

End-to-end PII posture audit and masking apply for an app that already has the SDK installed. Scans what's masked today (screen, view-level, network), surfaces gaps against the customer's stated compliance posture, and walks the user through closing them — one control at a time, with cited reasoning.

The aim is a conversational audit the user can re-run later — before launch, before a new screen ships, before a compliance review — without re-doing onboarding.

## When NOT to use this skill

Hand off to a sibling skill (or simply don't run) for any of the following:

- **First-time SDK install** — the SDK isn't initialized yet → `luciq-setup`.
- **Full product walk** (Bug Reporting, Replay, APM, Surveys, etc.) → `luciq-onboard`.
- **Upgrading a Luciq SDK version or migrating from the legacy Instabug SDK** → `luciq-migrate`.
- **Investigating a specific crash, hang, regression, or user-reported bug** → `luciq-debug`.
- **API signature lookups** — point the user at https://docs.luciq.ai.
- **After `luciq-onboard` completes.** Onboard does not invite a PII audit. Wait for the customer to invoke this skill on their own, in a fresh ask.

If the user's ask matches any of the above, STOP and route them. Running `luciq-masking-rules` on an uninstrumented project produces incorrect results because every detection step assumes the SDK init is already present.

## Canonical sources of truth

Verify SDK class names, API signatures, default key lists, and config flags against the live docs before quoting them. Hardcoded values in this skill are illustrative.

| Concern | Source |
| --- | --- |
| Per-platform masking APIs | https://docs.luciq.ai (per-platform setup guides) |
| Auto-masking screenshot types | iOS / Android / Flutter / RN sections of the live setup guides |
| Network auto-masking key list | Server-side, configurable; the in-skill list is the SDK 14.2.0 default |
| Session Replay privacy & consent | https://docs.luciq.ai (Session Replay → Privacy & Data Masking) |
| User's repo | local file system — read directly |
| User's apps and workspace | Luciq MCP `list_applications` (if authenticated) |

## Operating principles

These shape every conversational turn the skill produces.

1. **Cite every finding and every recommendation.** Never say "masking is enabled" without `file:line`. Never say "you should mask X" without naming the signal (CLAUDE.md line, view binding, compliance framework the user named).
2. **Defense in depth, not single-layer.** The reference model has three layers: automatic type-based masking (safety net), manual per-view marking (precise), and network-layer masking. A real audit checks all three — never declare "PII is handled" off one control.
3. **Masking is client-side only.** Never imply the dashboard or backend can mask retroactively. If raw data already shipped, it shipped — the only fix is forward.
4. **One control at a time.** Per-control Ask → Apply → Summarize, same micro-flow as `luciq-onboard`. No wizard forms.
5. **Match the team's compliance posture, don't invent one.** If the user names HIPAA, default to the HIPAA preset (see `references/compliance-defaults.md`). If they name nothing, default per archetype and offer the preset rather than assume.
6. **Honest about gaps, never alarmist.** Surface what's missing with the consequence stated plainly. No FUD, no "you're exposed" language.
7. **Activation > configuration.** A masking config that's never visually verified is a half-delivery. End with a screenshot-of-a-masked-screen verification, not just "config applied."

## Workflow checklist

Track every phase. STOP on any phase that can't complete with confidence — never fake progress.

```
PII Audit Progress:
- [ ] 0. Detect mode (FAST / AUDIT / GUIDED) and compliance posture
- [ ] 1. Posture scan (silent, parallel) — all three layers + defense-in-depth controls
- [ ] 2. Recap — what's masked today, with citations
- [ ] 3. Plan — three positive buckets (close now / optional / monitor)
- [ ] 4. Per-control walk (Ask → Apply → Summarize)
- [ ] 5. Verification — visually confirm masking on one sensitive screen
- [ ] 6. Handoff — write LUCIQ_MASKING.md (pre-prod checklist + posture snapshot)
```

## 0. Detect mode + compliance posture

Two reads happen at the trigger: mode, and stated compliance framework.

**Mode** (same vocabulary as `luciq-onboard`):

| Trigger phrasing | Mode | Behavior |
| --- | --- | --- |
| "must-haves", "fast", "quick" | **FAST** | Apply the top 3 missing controls with one-line confirms. ~2 minutes. |
| "audit", "what am I missing", "report", "review" | **AUDIT** | Report-only. Posture scan + recap + bucketed plan + handoff doc. Apply nothing without follow-up confirmation. |
| anything else, including "review my PII", "prep for HIPAA" | **GUIDED** | Full arc with per-control walk and verification. ~8 minutes. Default. |

**Compliance posture** — scan the trigger message and Track A (CLAUDE.md / README) for an explicit framework mention: HIPAA, GDPR, SOC2, PCI-DSS, CCPA, FERPA. If named, load the matching preset from `references/compliance-defaults.md` and confirm it in one line at the start: *"Treating this as a HIPAA audit — aggressive masking, consent gating, no grayscale-only."* If not named, proceed with archetype defaults and offer the relevant preset in Phase 3.

Confirm mode + posture in one line so the user knows what they're about to spend time on.

## 1. Posture scan (silent, parallel)

This phase runs without conversation. The output is a structured **PII profile** the rest of the workflow reads from. Do all five tracks in parallel.

### Track A — Context docs

Read in priority order, same as `luciq-onboard`:

1. `CLAUDE.md` (repo root + any nested)
2. `AGENTS.md`, `.cursorrules`, `.windsurfrules`
3. `README.md`, `ARCHITECTURE.md`, `docs/*.md`

Extract: explicit privacy lines, compliance framework mentions, "never log X" rules, user consent obligations. Find at least one quotable line with line number — that's what makes the recap feel uncanny.

### Track B — Sensitive view enumeration

Run the same enumeration `luciq-onboard` does (see its Track B): for each sensitive screen, identify individual views bound to PII-flavored properties, with `file:line, view_type, binding, suggested_marker`. Same false-positive filters (test/spec/mock paths, validator utilities, `node_modules`, `Pods/`, `build/`). Re-enumerate from scratch every run — the audit should be evidence-driven from the current repo state, never relying on a prior session's notes which may be stale.

The structured output is `sensitive_views: [{screen, file, line, view_type, binding, suggested_marker, currently_marked: bool}]`. The `currently_marked` flag is the key new column — set true if the view already has the Luciq marker applied at its site.

### Track C — Auto-mask configuration

Detect what the SDK init does for automatic screenshot masking. See `references/auto-mask-types.md` for per-platform patterns.

| Platform | Grep / read |
|---|---|
| iOS | `SessionReplay.autoMaskScreenshotOptions`, `Luciq.setAutoMaskScreenshotsTypes`, `IBGSessionReplay.autoMaskScreenshotOptions` |
| Android | `Luciq.setAutoMaskScreenshotsTypes(`, `Instabug.setAutoMaskScreenshotsTypes(` |
| Flutter | `SessionReplay.setAutoMaskingTypes(`, `Luciq.setAutoMaskScreenshotsTypes(` |
| React Native | `Instabug.setAutoMaskScreenshotsTypes(`, `Luciq.setAutoMaskScreenshotsTypes(` |

Output: `auto_mask: { configured: bool, file_line, types: [TEXT_INPUTS | LABELS | MEDIA | WEB_VIEWS | MASK_NOTHING] }` or `{ configured: false }`. **A missing call is not the same as `MASK_NOTHING`** — record the distinction. The platform default (typically `TEXT_INPUTS`) applies when not configured; quote it from the live setup guide before recapping.

### Track D — Network masking configuration

Detect network masking state and any custom obfuscate/omit hooks.

| Signal | Locations to grep |
|---|---|
| Auto-mask explicit state | `setNetworkAutoMaskingState(`, `NetworkLogger.autoMaskingEnabled`, `IBGNetworkLogger.autoMaskingEnabled` |
| Manual obfuscate | `NetworkLogger.obfuscateLog(`, `obfuscateLog(`, custom request-mutator interceptors |
| Manual omit | `NetworkLogger.omitLog(`, `omitLog(` |
| Custom mask key extensions | support-ticket / server-side config — flag as "verify with admin" if no local signal |

Output: `network_masking: { auto_enabled: bool | "default-on", file_line, obfuscate_sites: [...], omit_sites: [...], custom_keys_requested: bool }`. Default is on starting with SDK 14.2.0 — record SDK version (read from Podfile.lock / Package.resolved / build.gradle / package.json / pubspec.yaml) so the recap can state whether auto-masking applies.

### Track E — Defense-in-depth controls

Five small reads, each one a separate control in the plan.

| Control | Signal |
|---|---|
| **Consent gating** | `SessionReplay.enabled =`, gated on a user-consent boolean (CLAUDE.md mention of consent flow) |
| **Grayscale screenshots** | `SessionReplay.screenshotQualityMode = .greyScale`, `screenshotQualityMode` |
| **FLAG_SECURE (Android)** | `ignoreFlagSecure(`, manifest review for `WindowManager.LayoutParams.FLAG_SECURE` on sensitive Activities |
| **`usersPageEnabled`** | `usersPageEnabled = false`, server-controlled — flag for verification |
| **SSUI inflate sites** | code paths that inflate views from server JSON (`inflateFromJson`, `JSONSerialization` → view tree, RemoteConfig-driven UI). If present, the audit must propose an `isPrivate` flow per `references/ssui-isprivate.md` |

Output: `defense_in_depth: { consent_gating, grayscale, flag_secure_overridden, users_page_enabled, ssui_inflate_sites: [...] }`.

Encryption posture (AES-256-GCM at rest, TLS 1.2+ with HMAC-SHA256 in transit, iOS Keychain for PII keys) is on by default and not configurable from the audit — but it belongs in the recap and handoff so the user sees masking + encryption as a layered posture, not just masking. See `references/encryption-at-rest.md` for the one-line recap form and the on-device data category table.

### Track F — Workspace precedent (MCP, optional)

If the Luciq MCP is authenticated, call `list_applications` and read masking config from peer apps. A precedent quote like *"your other 3 apps mask `TEXT_INPUTS + LABELS` at SDK init"* is the strongest trust move available for an audit. Skip silently if MCP isn't authenticated.

### Compliance-framework alignment (in the same pass)

If a framework was named at trigger or found in Track A, load its preset from `references/compliance-defaults.md` and compare each profile value to the preset. The deltas drive the Phase 3 plan.

| Framework | One-line minimum (see reference for full preset) |
|---|---|
| **HIPAA** | Aggressive auto-mask (`TEXT_INPUTS + LABELS + MEDIA`), per-view markers on every PHI view, network auto-mask on, consent gating before Session Replay. |
| **GDPR** | Consent gating before Session Replay (and ideally before SDK init in EU), no PII in network logs, right-to-erasure documentable. |
| **PCI-DSS** | Per-view markers on every cardholder data view, network auto-mask on, never log raw PAN/CVV, grayscale optional. |
| **SOC 2** | Auto-mask on as a baseline control, masking config under version control, audit trail of changes. |
| **CCPA / FERPA** | Consent gating + per-view markers on regulated data fields; verify against current statute text — defaults here are illustrative. |

## 2. Recap — what's masked today

Open with one line stating the overall posture honestly: *"Strong on view-level, weak on network"* / *"No auto-mask configured — relying on per-view markers alone"* / *"HIPAA-ready except for consent gating."* Then the cited evidence, capped at 6 lines.

Example for a partially-configured iOS SwiftUI app under HIPAA:

> Quick read on your PII posture:
> - Auto-mask: `TEXT_INPUTS + LABELS` at `LuciqInit.swift:24` — matches HIPAA minimum.
> - Per-view markers: 7 of 11 sensitive views marked; gap is 4 `Text` views on `PatientProfileView.swift:31-58` showing PHI.
> - Network auto-masking: enabled by default (SDK 14.3.1); no custom keys for your `x-patient-id` header — recommend adding via support.
> - Consent gating on Session Replay: **missing**. CLAUDE.md:8 says *"never record before consent"* — currently records on first launch.
> - Grayscale: off. Optional under HIPAA — defense-in-depth, not required.
> - SSUI: no inflate-from-JSON sites detected.

Cite the strongest 6 signals. Skip pad lines. If the posture is fully clean, say so plainly in one line and proceed to Phase 3 with the "monitor" bucket only.

## 3. Plan — three positive buckets

Never use "you're missing X" framing. Always positive bucketing.

- **Close now** — gaps with a clear apply step, named compliance fit, and high impact.
- **Optional — add if you'd like** — defense-in-depth controls; valuable, not strictly required.
- **Monitor** — controls already in place; revisit conditions for when they may need updating.

Every item in "Close now" must name its control, the cited gap, and the proposed apply target:

> Close now:
> – **Mark the 4 unmarked PHI views** — `PatientProfileView.swift:31-58` — propose `.luciqPrivate()` modifier per view, batch-confirm after first 2.
> – **Consent gating for Session Replay** — wrap `SessionReplay.enabled = true` in your existing consent check at `OnboardingFlow.swift:74` (CLAUDE.md:8).
> – **Add `x-patient-id` to the network mask list** — server-side config; I'll prep a support ticket request line for the handoff.

Close the plan with one question: *"Want to adjust any bucket before I start?"*

In AUDIT mode, stop here — write the handoff doc (Phase 6) and exit. Don't apply.

## 4. Per-control walk

For each "Close now" item (and any "Optional" the user picked up), run the three-step micro-flow.

### A. Ask

One line: name the control, the cited gap, the proposed change. Three responses: **Yes / No / Tell me more**.

> **Per-view markers on PatientProfileView** — 4 `Text` views bind to `patient.firstName`, `patient.lastName`, `patient.dob`, `patient.mrn`. Propose `.luciqPrivate()` on each. Apply?

For per-view markers, follow the confirmation policy in `luciq-onboard` SKILL.md Track B: confirm the first 3–5 individually, then batch-confirm the rest. Drop back to per-match if the user inspects a row.

### B. Apply

Derive the exact diff from the profile, not from defaults. Show before applying. Confirm.

- Use the **compliance preset** to choose auto-mask aggressiveness — see `references/compliance-defaults.md`.
- Use the **CLAUDE.md privacy line** as the override authority.
- Use the **competitor's masking style** (if any was detected in `luciq-onboard`) to keep posture consistent across SDKs.
- If a required file edit conflicts with uncommitted user changes, STOP and surface — don't overwrite work.

For server-side controls (network mask key additions, `usersPageEnabled`), prepare a one-line support-ticket request the user can copy — these can't be code-applied.

### C. Summarize

After applying, three short blocks:

> Done.
> - Marked 4 PHI views on `PatientProfileView.swift:31-58` with `.luciqPrivate()`.
> - Wrapped `SessionReplay.enabled = true` in `userHasConsented` check at `OnboardingFlow.swift:74`.
>
> What's left for you:
> - Email support to add `x-patient-id` to the network mask key list (template in `LUCIQ_MASKING.md`).
> - Verify masked regions in the dashboard after next session — see Phase 5.
>
> Moving to the next control.

## 5. Verification

After all selected controls are applied, run **one** verification round-trip — same shape as `luciq-onboard` Phase 5.

> All set. To verify masking visually:
>  1. Build and launch the app.
>  2. Navigate to one screen you just marked private (e.g., `PatientProfileView`).
>  3. Trigger a bug report or wait for a Session Replay frame.
>  4. Open the dashboard: `<dashboard URL for this app>`
>  5. Confirm masked regions render as solid black rectangles.
>
> Tell me when you see the masked screen — or just come back later.

If the user reports masking didn't work, diagnose (marker on parent vs child, auto-mask off, build cache?). Never claim success without confirmation. If the user says "later," accept and move on — record in the handoff that verification was not visually confirmed.

In FAST mode, skip the wait. The handoff doc still includes verification steps so the user can do it themselves.

## 6. Handoff — write LUCIQ_MASKING.md

Write `LUCIQ_MASKING.md` at the repo root using the template in `references/handoff-template.md`. The doc is the durable artifact — re-readable next quarter, hand-off-able to legal / compliance, queryable by `luciq-debug` later.

Contents:

- **Posture snapshot** — what's masked today, by layer (auto-mask, per-view, network, defense-in-depth).
- **Compliance posture** — framework named (or *not specified*), preset applied, deltas resolved this session.
- **Controls applied** — each with the `file:line` edited.
- **Controls deferred** — each with revisit condition.
- **Server-side requests** — copy-pasteable support ticket lines (network mask keys, `usersPageEnabled`).
- **Pre-production privacy checklist** — from `references/preprod-checklist.md`, with current state per item.
- **Visual verification** — timestamp + screen name if confirmed, or *not verified this session*.
- **When to reach for sibling skills** — `luciq-onboard` for product walk, `luciq-migrate` for SDK upgrades, `luciq-debug` for incident investigation.

If `LUCIQ_MASKING.md` already exists, *append* a new dated session block — don't overwrite. The file accumulates the team's masking journey.

## Style

- ALWAYS show diffs before applying any code edit. Confirm.
- Cite every finding with a source — `file:line`, CLAUDE.md line, framework requirement, precedent quote.
- One control at a time. No wizard forms.
- Never declare PII handled off a single layer. State which layers you verified.
- Never use FUD ("you're exposed", "you'll get fined"). State the gap and the compliance fit, let the user decide.
- Verify SDK class names and config keys against the live docs before quoting them.

## Red flags — STOP and surface to the user

If you catch yourself thinking any of these, stop and surface:

- *"Auto-mask is on, so per-view markers don't matter."* The §2.4 behavior matrix in the reference shows auto-mask alone misses anything outside its declared types; per-view markers fill the gap. Both matter.
- *"They didn't name a framework, so I'll skip compliance."* The audit should still surface relevant presets as offers in Phase 3 — the user may not know HIPAA applies until you mention it.
- *"Network auto-masking is on by default — no need to check."* Default-on is true for SDK ≥ 14.2.0; verify the version before claiming this. Older SDKs need an explicit call.
- *"Masking config was applied — I'll claim verified."* Visual verification needs explicit user confirmation that masked regions render as solid blocks on the dashboard. Otherwise it's a half-delivery.
- *"This view doesn't look sensitive, I'll skip the marker."* Track B is a positive enumeration with explicit signals. Don't filter on intuition. If a binding matches a PII pattern, surface it; let the user defer it explicitly.
- *"Grayscale alone is privacy."* Grayscale is defense-in-depth, not a substitute for masking. State that explicitly when it's the only control proposed.
- *"They said 'not now' on consent gating — fine, move on."* Capture the reason verbatim in the handoff. If the framework is HIPAA / GDPR, also surface the consequence in one neutral sentence before moving on.

Every shortcut here trades "looks done" for "actually compliant." The skill's job is honest posture — not maximum control count.
````

</details>

## Prerequisites

* The Luciq SDK must be installed and initialized in your project. This skill assumes the SDK init is already present — running it on an uninstrumented project produces incorrect results. Use [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup) to install first.
* The Luciq MCP server is optional. If authenticated, the agent reads masking configuration from your other apps to surface workspace precedent.

{% hint style="info" %}
If the Luciq MCP server isn't connected, the skill still runs. Workspace precedent (Track F) is optional — only the local repo scan is required.
{% endhint %}

## How the agent audits

The agent tracks seven explicit phases. It stops rather than faking progress if a phase can't complete with confidence.

### Phase 0. Detect mode and compliance posture

Two reads happen at the trigger: mode and stated compliance framework.

| Trigger phrasing                                           | Mode       | Behavior                                                                                                  |
| ---------------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------- |
| "must-haves", "fast", "quick"                              | **FAST**   | Applies the top 3 missing controls with one-line confirms. \~2 minutes.                                   |
| "audit", "what am I missing", "report", "review"           | **AUDIT**  | Report only. Posture scan + recap + bucketed plan + handoff doc. No apply without follow-up confirmation. |
| anything else, including "review my PII", "prep for HIPAA" | **GUIDED** | Full arc with per-control walk and verification. \~8 minutes. Default.                                    |

If you name a compliance framework (HIPAA, GDPR, SOC 2, PCI-DSS, CCPA, FERPA) in your message or in `CLAUDE.md`, the agent loads the matching preset and confirms it in one line before starting.

### Phase 1. Posture scan

Silent and parallel. Produces a structured PII profile the rest of the workflow reads from.

**Track A — Context docs.** Reads `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `README.md`, and project docs in priority order. Extracts explicit privacy lines, compliance framework mentions, and a quotable line with line number.

**Track B — Sensitive view enumeration.** For each sensitive screen, enumerates individual views bound to PII-flavored properties (`password`, `email`, `cardNumber`, `cvv`, `ssn`, `pin`, `dob`, `iban`, `firstName`, `lastName`, `phoneNumber`, `address`, and related). Records whether each view already has a Luciq privacy marker applied. Filters out false positives in test, spec, mock, and generated paths. Re-enumerated from the current repo state on every run.

**Track C — Auto-mask configuration.** Detects what the SDK init sets for automatic screenshot masking using platform-specific patterns (e.g., `SessionReplay.autoMaskScreenshotOptions` on iOS, `Luciq.setAutoMaskScreenshotsTypes(` on Android). Records whether auto-mask is not configured — which is distinct from `MASK_NOTHING`. Verifies the SDK version to determine whether network auto-masking defaults apply.

**Track D — Network masking.** Detects `setNetworkAutoMaskingState`, manual `obfuscateLog` and `omitLog` hooks, and custom mask key extensions. Default is on from SDK 14.2.0 — but the SDK version is verified before claiming this.

**Track E — Defense-in-depth controls.** Five reads: consent gating before Session Replay, grayscale screenshots, `FLAG_SECURE` override on Android, `usersPageEnabled`, and server-side UI inflate sites (which require an `isPrivate` flow if present).

**Track F — Workspace precedent (optional, MCP).** If the Luciq MCP is authenticated, reads masking config from your other apps via `list_applications`. A precedent quote is the strongest trust signal available for an audit. Skipped silently if MCP isn't authenticated.

For any named compliance framework, the agent compares each profile value against the framework's preset in the same pass.

### Phase 2. Recap — what's masked today

Opens with one line stating overall posture honestly. Then up to six cited evidence lines: `file:line`, SDK version, CLAUDE.md quote. Skips padding — if posture is fully clean, one line says so.

### Phase 3. Plan — three positive buckets

Every gap lands in a bucket. "Close now" items name the control, the cited gap, and the proposed apply target. "Monitor" items name a revisit condition.

In **AUDIT mode**, the agent stops here, writes the handoff doc, and exits. Nothing is applied without follow-up confirmation.

### Phase 4. Per-control walk

For each "close now" item (and any "optional" you select), the agent runs:

1. **Ask** — one line: the control, the cited gap, the proposed change. Three responses: Yes / No / Tell me more.
2. **Apply** — derived from the posture profile, not from defaults. The diff is shown before any edit is made. For server-side controls (network mask key additions, `usersPageEnabled`), the agent prepares a copy-pasteable support ticket line instead.
3. **Summarize** — what was done, what's left for you, move to the next control.

For per-view markers, the first 3–5 are confirmed individually so you can catch false positives; the remaining views are then batch-confirmed in one block.

### Phase 5. Verification

After all selected controls are applied, one concrete verification round-trip:

1. Build and launch the app.
2. Navigate to a screen you just marked private.
3. Trigger a bug report or wait for a Session Replay frame.
4. Open the dashboard and confirm masked regions render as solid black rectangles.

The agent waits for your confirmation. If you say "later," the verification steps appear in the handoff doc. **FAST mode** skips this phase.

### Phase 6. Handoff — write LUCIQ\_MASKING.md

Writes `LUCIQ_MASKING.md` at the repo root, or appends a new dated session block if the file already exists. Contents:

* Posture snapshot by layer (auto-mask, per-view, network, defense-in-depth)
* Compliance posture: framework named (or *not specified*), preset applied, deltas resolved this session
* Controls applied, each with `file:line`
* Controls deferred, each with a revisit condition
* Server-side requests as copy-pasteable support ticket lines
* Pre-production privacy checklist
* Visual verification: timestamp and screen name if confirmed, or *not verified this session*
* Pointers to sibling skills: `luciq-onboard` for a product walk, `luciq-migrate` for SDK upgrades, `luciq-debug` for incident investigation


# luciq-debug

The luciq-debug skill investigates a crash, hang, bug, or APM issue end to end, mapping Luciq data to your repo and proposing a cited fix.

Investigate a Luciq production signal end to end — crash, hang, user-reported bug, or APM performance issue — using the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) for context and the user's local repository for the code mapping. Form a hypothesis, propose a fix, cite the evidence.

## Use this skill when

* The user describes a crash, hang, or bug. *"Why is this happening", "investigate this", "what broke"*.
* The user pastes a stack trace, exception, or fingerprint and asks for a diagnosis.
* The user references a specific Luciq crash ID, bug ID, or hang ID.
* The user asks *"what's regressing in version X"* or *"compare crashes between versions"*.
* The user flags an APM performance issue: a slow endpoint, a latency or p95 spike, an apdex drop, a network failure-rate spike, slow app launch, UI jank (frozen or slow frames), slow screen loading, user-flow drop-off, a throughput change, or *"what got slower or flakier since version X"*.

## What the agent learns

`luciq-debug` turns the agent into an evidence-based debugger across crash, hang, bug, and APM signals:

* **Signal-typed methodology.** Different decision trees for crashes, hangs, bug reports, and APM signals, each weighted by the evidence that matters for that signal.
* **MCP tool sequencing.** Which Luciq MCP tool to call when, in the right order, with the right filters, without over-fetching.
* **APM coverage across all five metrics.** Network endpoints, app launch, user flows, screen loading, and frame drop — the agent knows how to enter each signal, which APM views to pull, and how to localize a regression to a cohort or span.
* **Metric reference material.** Before quoting an APM number, the agent reads that metric's reference files, covering units, coverage and account gating, stage boundaries, and the conditions that make a number misleading. Populated for network and app launch across iOS, Android, React Native, and Flutter.
* **Local code mapping.** Translating a top stack frame or APM group name from production into a `Read` + `Grep` against the user's repo so the fix lands in real code.
* **Evidence-cited hypotheses.** Every claim the agent makes cites the MCP tool result that produced it. No fabrication, no guessing.
* **Pattern library.** Known mobile failure modes (Swift Concurrency races, Android ANRs, OOMs, network failures, APM latency regressions, failure-rate spikes) the agent recognizes and reaches for the right Luciq context to confirm.

## 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-debug`.

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-debug/SKILL.md) from the public repo to `~/.claude/skills/luciq-debug/SKILL.md` (user-global) or `.claude/skills/luciq-debug/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-debug
description: Use when the user wants to investigate a Luciq production signal end to end, propose a code fix, or answer "why is this happening". Triggers include pasting a crash ID, fingerprint, or stack trace; mentioning a Luciq bug number, hang, or ANR; asking "what broke since version X"; flagging a rating drop or review spike; or asking why a session crashed, hung, or terminated. Also covers APM performance signals across all metrics — a slow endpoint, latency/p95 spike, apdex drop, network failure-rate spike, slow app launch, UI jank (frozen / slow frames), slow screen loading, user-flow drop-off, a throughput change, a bottleneck, or "what got slower/flakier since version X". Pulls evidence via the Luciq MCP server, maps it to local source, forms an evidence-cited hypothesis.
---

# Luciq Production Debugging

Investigate a Luciq production signal end to end. Default to evidence-based reasoning. Cite the MCP tool result that supports each claim. If a query returns nothing, surface that fact instead of filling in plausible-looking guesses.

## When NOT to use this skill

- First-time SDK install or wiring `Luciq.start(...)`, use `luciq-setup`.
- Renaming Instabug symbols to Luciq, or upgrading between Luciq SDK versions, use `luciq-migrate`.
- General mobile debugging where Luciq is not the data source. This skill is grounded in what the Luciq MCP exposes; without that, do not pretend to use it.

If the user's request fits any of the above, STOP and route them to the right skill rather than running this one.

## Prerequisites

The Luciq MCP server must be configured and authenticated. If MCP tools are not available, STOP and direct the user 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` to wire it.

The MCP exposes (verbatim names):

| Tool | Purpose |
| --- | --- |
| `list_applications` | List apps and their tokens for the authenticated user. |
| `list_crashes` | List crash groups with filters (version, OS, date range). |
| `crash_details` | Full details for a crash group: top frames, occurrence sample, distributions. |
| `crash_patterns` | Distribution by `pattern_key` (e.g. `oses`, `app_versions`, `devices`). |
| `list_occurrences_tokens` | Occurrence ULIDs for a crash group, paginated. |
| `get_occurrence_details` | Per-occurrence detail: session profiler, logs URLs, device state. |
| `list_app_hangs` | Hang and ANR groups. iOS surface as `FATAL_UI_HANG`, Android as `ANDROID_FATAL_HANG`. |
| `list_bugs` | User-reported bugs. |
| `bug_details` | Full bug detail including compressed log archive URLs. |
| `list_reviews` | App Store / Play Store reviews filtered by `rating` and `app_version`. |
| `apm_list_groups` | Rank APM groups worst-first for a `metric`. Sort `by` `apdex`/`apdex_change`/`occurrences`/`dissat_count` (all metrics), `p95`/`p50` (all but `frame_drop`), `failure_rate` (network), `frozen_frames_percent`/`slow_frames_percent` (`frame_drop`). |
| `apm_group_view` | Per-group panels for a `metric`: `summary`, `apdex_chart`, `throughput_chart`, `spans_table`, `dimensions` (all); `outliers` (all but `frame_drop`); plus metric-specific `failure_rate`, `stages_breakdown`, `web_vitals`, `frames_distribution`, `delayed_frames`. Inapplicable views return in `ignored_views`. |
| `apm_occurrence` | Per-occurrence detail by `selector: worst \| by_token \| list`. |

YOU MUST cite which of these produced any piece of evidence in your hypothesis. Do not invent capabilities the MCP does not expose. See "Out of scope" below for what the MCP deliberately does not return.

## Reference files

Detailed material is split out so the SKILL.md stays workflow-focused. Read the relevant reference when the workflow points to it:

| Reference | When to read |
| --- | --- |
| `references/metrics/preamble.md` | Before interpreting any APM number. Units, how to read p50 against p95, what `threshold_ms` means, and the aggregates-not-records model that every check has to be built around. |
| `references/metrics/<metric>/overview.md` | Next. What the metric measures, what each tool returns for it, the coverage and account gating that decide whether absence is a measurement, and the cross-platform facts. |
| `references/metrics/<metric>/<platform>.md` | Last. The platform's anchors, which stacks are instrumented, stage boundaries, optimization targets, validation checks, and the conditions under which the data misleads. |

Populated for `network` and `app-launch`, each with `ios`, `android`, `react-native`, `flutter`.

On React Native or Flutter, read the wrapper file **and** the native platform file it names. For launch the wrapper adds no timing of its own, so the native file governs the number; for network the wrapper does its own timing on the JS thread or Dart isolate, so the wrapper file governs it.

## Workflow

Run the following loop. Every step is gated on evidence.

### Step 1. Identify the entry point

Determine the kind of signal being debugged. If the user has not specified, ask. Do not pick at random.

| Entry point | Required input | First MCP tool to call |
| --- | --- | --- |
| Crash group | Crash number, fingerprint, or pasted stack trace | `crash_details` (or `list_crashes` to find it first) |
| Specific occurrence of a crash | Crash number plus ULID | `get_occurrence_details` |
| App hang or ANR | Hang number, or "recent UI hangs" | `list_app_hangs` |
| User-reported bug | Bug number | `bug_details` |
| Regression between versions | Two version numbers | `list_crashes` filtered by version, then `crash_patterns` with `pattern_key: app_versions` |
| Review or rating signal | Date range and version | `list_reviews` filtered by `rating` and `app_version` |
| APM performance regression | "what got slower/flakier since X", two versions; which signal (metric) | `apm_list_groups` for the matching `metric` sorted by `apdex_change`, then `apm_group_view` with `dimensions` |
| Worst APM group | The signal: slow endpoint/launch/screen, jank, flow drop-off (metric) | `apm_list_groups` for that `metric` sorted by its pain key (`p95`, `failure_rate`, `frozen_frames_percent`, `dissat_count`), then `apm_group_view` |
| Throughput spike / drop | Group + window | `apm_group_view` with `throughput_chart` for the group |

### Step 2. Pull MCP context

Sequence the available Luciq MCP tools deliberately for the entry point:

- Crashes: `list_crashes`, `crash_details`, `crash_patterns`, then `list_occurrences_tokens` and `get_occurrence_details` for one or more sessions.
- Hangs: `list_app_hangs` filtered to the recent window.
- Bug reports: `list_bugs` then `bug_details`. The response includes URLs to compressed logs (network, console, session profiler) when available.
- Regressions: filter `list_crashes` by the two versions, diff the result, then call `crash_patterns` with `pattern_key: app_versions` for the highest-impact new groups.
- Review signals: `list_reviews` filtered to low ratings, then correlate with crash and hang activity in the same window.
- APM regression: choose the `metric` for the signal, then `apm_list_groups` sorted by `apdex_change` (signed delta) across the two `app_version` values, take the most-degraded groups, then `apm_group_view` with `dimensions` to localize each regression to a cohort (OS, device, country, version), then `apm_occurrence` with `selector: worst` for a concrete worst case to reason over.
- APM group deep dive: `apm_list_groups` for the metric sorted by its pain key to find the group, then `apm_group_view` — `summary` for the headline metrics, then the view that matches the pain: `spans_table` (or `stages_breakdown` for launch/screen_loading) for a slow segment, `outliers` for the tail driving p95 (not on `frame_drop`), or the `failure_rate` view for a failing network group, then `apm_occurrence` (`worst`), which is the worst-failed request when the pain is failures, not the slowest.
- Before interpreting a metric's numbers, read its **Reference files**
- On a 403/501 from an APM tool, SKIP the APM step with the reason; never infer "no regression" from a tool error.

### Step 3. Symbolicate if obfuscated (crash / hang track)

If the top frame is a hex address, an obfuscated symbol, or `<unknown>`, the build is missing its symbol artifact (dSYM for iOS, R8/ProGuard mapping for Android, split-debug-info for Flutter, source map for React Native). STOP and direct the user to upload symbols before continuing. Do not reason over hex addresses.

### Step 4. Map to local source

**Crash / hang track — map the top frame:**

- `Grep` the symbol (class plus method) across the project.
- `Read` the matched file with a small window around the offending line (10 lines above and below).
- For multi-platform projects (KMP, RN, Flutter), prefer the platform-specific source set first (`iosMain/`, `androidMain/`).

**APM track — map the endpoint / span to the call site:**

The APM group name is the request signature (method + URL path template, e.g. `GET /v2/orders/{id}`). Map it to the code that issues or handles it:

- `Grep` the path template, the host, or the path segments across the project. For a client SDK, that's the request-building call site (the URL string, the route constant, or the API-client method). For a server repo, it's the route/handler registration.
- When `spans_table` localized the cost to one segment (e.g. a DB span, a downstream call, a serialization span), grep that segment's operation name — the bottleneck is usually inside that call, not in the request setup.
- Use the `dimensions` breakdown to constrain the hypothesis: a regression isolated to one OS version, one device tier, or one app version points at a different cause (client-side change, OS behavior, rollout) than one that's uniform across cohorts (backend/dependency).

If the symbol or endpoint does not exist locally, the repo isn't its source: a different commit than the build for a crash symbol, or a different service/dependency for an endpoint. Surface that fact rather than guessing at a fix.

### Step 5. Form a hypothesis

Use this structure exactly. Cite each piece of evidence to the MCP tool that produced it.

```
HYPOTHESIS: <one sentence>
CONFIDENCE: <low / medium / high>

EVIDENCE:
- Top frame: <file>:<line> - <symbol>     [from: crash_details]
- Distribution: <e.g. only iOS 18.0+>     [from: crash_patterns]
- Repro context: <e.g. backgrounded for ~5s>  [from: get_occurrence_details]
- Correlated signal: <e.g. matching review text>  [from: list_reviews]

ROOT CAUSE: <the specific defect>
```

For an APM investigation the evidence lines come from the APM channel instead:

```
HYPOTHESIS: <one sentence>
CONFIDENCE: <low / medium / high>

EVIDENCE:
- Group: GET /v2/orders/{id}  apdex 0.71 (was 0.94)   [from: apm_list_groups, sort apdex_change]
- p95: 2,140ms (was 410ms)                             [from: apm_group_view summary]
- Cohort: regression isolated to iOS 18.x             [from: apm_group_view dimensions]
- Bottleneck: DB span "orders.fetch" = 1,800ms        [from: apm_group_view spans_table]
- Call site: <file>:<line>                             [from: Grep]

ROOT CAUSE: <the specific defect>
```

Confidence is honest. Three corroborating MCP sources is high. Reasoning from the top frame or single number alone is low. A latency number with no cohort breakdown and no span decomposition is a symptom, not a root cause.

### Step 6. Propose a fix

Show a diff. Explain how the fix addresses the root cause. Flag any side effects. Optionally write a failing test that reproduces the issue before applying. Do not apply the diff without user confirmation.

## Pattern library

Carry these patterns. Reach for them when the corresponding signature appears in the MCP data.

### Swift Concurrency (iOS)

When the top frame involves `async`, `await`, an actor, or a `Sendable` violation:

- Check whether the crash is `Swift runtime: Fatal error: ...` rather than a typical exception. That is a concurrency-safety check firing.
- Confirm OS distribution via `crash_patterns` with `pattern_key: oses`. Swift 6 strict-concurrency checks behave differently across iOS versions.
- Look at the session profiler from `get_occurrence_details` for hop-to-`@MainActor` patterns near the crash time.
- Do not recommend slapping `@MainActor` on a class to silence the error. Treat that as a smell, not a fix.

### Android ANRs (`ANDROID_FATAL_HANG`)

When `list_app_hangs` returns an Android hang:

- The `crash_cause` field tells you where the main thread was blocked, but not always what blocked it. Pull a few `get_occurrence_details` to see recent main-thread activity and pending I/O.
- Check `pattern_key: app_versions` to see whether the ANR is a regression or a long-tail issue.
- Common offenders: synchronous network calls on the main thread, large `SharedPreferences.commit()` writes, blocking `Lock` acquisitions, work scheduled on the wrong dispatcher.

### iOS UI hangs (`FATAL_UI_HANG`)

- The hang `exception` summary indicates duration class.
- Pull the occurrence to confirm what the user was doing. The `current_view` and `app_status` (foreground / background) fields disambiguate.
- Common offenders: synchronous Core Data on `NSManagedObjectContext.viewContext`, file I/O on the main queue, expensive layout work in `viewDidLayoutSubviews`.

### Out-of-memory crashes

- OOMs surface as terminations. Check `crash_type` and the exception name.
- Pull the occurrence's `state.memory` and `state.storage` fields from `get_occurrence_details` for resource state at termination.
- Look at `pattern_key: devices`. OOMs concentrate on lower-RAM devices and surface a device-tier story the agent should call out.

### Network failure correlated crashes

- For crashes with a stack frame in networking code, pull the occurrence's logs URL from `get_occurrence_details` (compressed log archive).
- Cross-reference with bug reports in the same window via `bug_details`. The `state.logs.network_log` URL often shows the failed request that preceded the crash.
- Do not assume timeout vs DNS failure vs server error without log evidence. The categories matter for the fix.

### APM latency regression

When `apm_list_groups` sorted by `apdex_change` shows a group degrading between versions:

- Sanity-check `threshold_ms` against `50th_percentile_ms` before trusting the apdex — see the preamble for why. Then weigh it against the endpoint's business role: a list/read call should be near-real-time, a few hundred ms; a heavy export needn't be. A target set for the wrong role is a config fix, not a code fix.
- Confirm with the absolute numbers, not just apdex: pull `apm_group_view summary` for `50th_percentile_ms` and `95th_percentile_ms` before and after.
- Always run `dimensions` to localize. A regression confined to one OS version or device tier is a different bug than one uniform across cohorts.
- Use `spans_table` to attribute the time. The fix targets the dominant span — chasing the request-setup code when the cost is in a DB span wastes effort.
- Use `outliers` when p95/p99 moved but the median didn't — the tail requests carry the signature.

### APM app launch

When `apm_list_groups` flags a launch group (`metric: launch`):

- Segment by `type` first. Cold, warm, and hot measure different windows, so aggregating across them — or across platforms — produces a meaningless number.
- Attribute with `stages_breakdown`, not `spans_table`. Map the dominant stage to code using the platform file's optimization-targets table. On React Native and Flutter, a dominant native stage means the cause is not in JS or Dart.
- Use `dimensions` with `pattern_key: first_screen` to find which entry screen carries the cost, and `outliers` when p95 moved but p50 did not.
- Run the platform file's validation table before quoting any number. Several conditions make a launch total mean something other than "the app is slow," and they are not visible in the number itself.

### APM network

When `apm_list_groups` flags a network group (`metric: network`):

- Establish coverage first — capture is not automatic on Android or Flutter, so run the overview's coverage table against the codebase before reading absence as a measurement.
- There is **no `stages_breakdown` view for network**. Attribute with `spans_table`, and use `apm_occurrence` for one request's stage detail. Match returned span names against the overview's boundary table rather than assuming them.
- The list row gives `latency_p95_ms` and `failure_rate` only — no p50, no occurrence count. Get those from `summary` or `dimensions`.
- Segment on `radio` before comparing latency. Then check the platform file for what the measured window excludes; the app's own queueing and interceptors are not in it.

### APM failure-rate spike

When `apm_list_groups` sorted by `failure_rate` flags a group:

- Split `total_failure_rate` into `client_failure_rate` vs `server_failure_rate`. Client failures (4xx, timeouts, cancellations) point at the app; server failures (5xx) point at the backend. They lead to opposite fixes.
- Apply the platform's client-side correction before quoting the client rate. Both native platforms distort it, in opposite directions — the network platform file gives the check for each.
- Filter the group by `failure_name` / `failure_type` to see whether it's one error class or many.
- Cross-reference the window with `list_crashes` and `bug_details` — a failure-rate spike that coincides with a crash spike on the same call path is usually one root cause, not two.

### APM throughput change

When `throughput_chart` shows a spike or drop:

- A drop in throughput with flat latency often means callers stopped calling (a client-side gating change, a feature flag, a rollout) — not a performance defect. Check `dimensions` by `app_version` and correlate with a release.
- A spike with rising latency is load-driven; the fix is usually capacity/backpressure, not a code path. Say which one the evidence supports; don't default to "optimize the code."

## Out of scope

The skill is grounded in what the Luciq MCP exposes today. It deliberately does not:

- Compute crash-free session rate or any aggregate metric the MCP does not return.
- Reason about App Store rating drops as a primary investigation entry point. `list_reviews` is correlation, not causation.

When new MCP tools land (release comparison, session replay), this skill grows with them. Until then, if the user asks for one of those, say so plainly.

## Red Flags - STOP and surface to the user

If you catch yourself thinking any of these, you are about to ship a fabricated investigation. STOP, surface to the user, do not proceed:

- "MCP returned nothing, but the user clearly wants an answer, so I'll reason from the symbol name." That is a guess, not a hypothesis. Surface the empty result.
- "The top frame is a hex address but I can probably figure it out from context." Do not. Stop and ask the user to upload symbols.
- "The local symbol doesn't exist but the file looks similar enough." It isn't. The repo is at a different commit; surface that.
- "I'll quote a crash-free session rate from memory." The MCP does not expose that metric. Saying you computed it from MCP data is a fabrication.
- "Confidence is high because the top frame matches my prior." One source is not three. Lower confidence to low or medium.
- "I'll apply the fix without a diff because it's obviously right." Show the diff. Get confirmation. Always.
- "The hypothesis cites the symbol but not which MCP tool produced it." Add the citation, or weaken the hypothesis.
- "APM returned a 403/501, so there's no regression." A tool error isn't a clean result, SKIP the step, say APM was unavailable, never infer "no regression." Only a genuine server-down 5xx (500/502/503) STOPs; 403 and 501 return a body to inspect.
- "I filtered APM with `app_versions`/`experiments`/`devices` and got nothing, must be broken." Those are crash-channel names. APM uses `app_version`, `experiment`, `device: { operator, values }`, re-run before concluding.
- "p95 doubled, so the endpoint's code is slow. I'll optimize it." Not yet. run `dimensions` (one cohort?) and `spans_table` (which segment?) first. The cost may be a downstream call or one OS version; optimizing the wrong layer fixes nothing.
- "Throughput dropped, so performance regressed." A throughput drop with flat latency usually means fewer callers, not a slower path. Correlate with a release / flag before calling it a defect.
- "I'll slice by the `email` custom attribute." APM addresses custom attributes by numbered slot (1–20), not name, and the slot→name map is org config you can't infer. Ask the user which slot it is.
- "I'll quote a latency number from a group without saying which tool/view gave it." Cite `apm_list_groups` vs `apm_group_view <view>`. they're different aggregations and conflating them misstates the evidence.
- "Launch p95 is 3s, so the app takes 3s to become usable." The window closes before the first frame is drawn, so without an `endAppLaunch` stage that number is time-to-activation and the real figure is higher.
- "There's no cold launch data, so cold launches are fine." Capture is provisioned per account and defaults to off, and Android reports none under a renamed process. Absent data is an instrumentation finding until you check both.
- "There's little or no network data, so the app makes few requests." Capture is off by default on Android (a build flag) and needs a per-call-site client swap on Flutter. Check setup before reading absence as traffic.
- "The client-side failure rate is near zero, so the network is healthy." Every platform distorts that number: iOS records client failures as successes when body capture is off, Android inflates it with cancellations, and Flutter drops failed requests entirely — which also biases its latency percentiles *low*, because the slowest requests are the missing ones. A flattering p95 on Flutter is not evidence of a fast network. Correct for the platform first.
- "A slow request means slow code in the app's networking layer." The app's own interceptors and client-side queueing are outside the measured window on both native platforms. A blocking token-refresh interceptor cannot inflate the request it delayed.

The pattern: every shortcut here trades "sounds confident" for "actually true." The skill's job is to be true.
````

</details>

## Prerequisites

* The [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) is configured and authenticated. The [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup) skill wires this for first-time projects.
* The user has access to the Luciq application and environment they want to investigate.
* The agent is running inside a project repository so it can map stack frames and APM groups to local source.

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

## How the agent investigates

The skill runs the agent through a small, predictable loop. Every step is gated on evidence. If a step doesn't return data, the agent says so rather than filling in plausible-looking guesses.

### Step 1. Identify the entry point

What kind of signal are we debugging?

| Entry point                    | Required input                                                  | First MCP tool call                                                                                                                                |
| ------------------------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Crash group                    | Crash number, fingerprint, or pasted stack trace                | `crash_details` (or `list_crashes` to find it)                                                                                                     |
| Specific occurrence of a crash | Crash number + ULID                                             | `get_occurrence_details`                                                                                                                           |
| App hang / ANR                 | Hang number, or *"recent UI hangs"*                             | `list_app_hangs`                                                                                                                                   |
| User-reported bug              | Bug number                                                      | `bug_details`                                                                                                                                      |
| Regression between versions    | Two version numbers                                             | `list_crashes` filtered by version, then `crash_patterns` with `pattern_key: app_versions`                                                         |
| Review / rating signal         | Date range + version                                            | `list_reviews` filtered by `rating` + `app_version`                                                                                                |
| APM performance regression     | *"what got slower/flakier since X"*, two versions; which metric | `apm_list_groups` for the matching `metric` sorted by `apdex_change`, then `apm_group_view` with `dimensions`                                      |
| Worst APM group                | The signal: slow endpoint/launch/screen, jank, flow drop-off    | `apm_list_groups` for that `metric` sorted by its pain key (`p95`, `failure_rate`, `frozen_frames_percent`, `dissat_count`), then `apm_group_view` |
| Throughput spike / drop        | Group + window                                                  | `apm_group_view` with `throughput_chart` for the group                                                                                             |

If the user doesn't specify the signal type, the agent asks. It doesn't pick one at random.

### Step 2. Pull MCP context

Once the entry point is known, the agent calls Luciq MCP tools in a deliberate order:

* **Crashes:** `list_crashes`, `crash_details`, `crash_patterns`, then `list_occurrences_tokens` and `get_occurrence_details` for one or more sessions.
* **Hangs:** `list_app_hangs` filtered to the recent window.
* **Bug reports:** `list_bugs` then `bug_details`. The response includes URLs to compressed logs (network, console, session profiler) when available.
* **Regressions:** filter `list_crashes` by the two versions, diff the result, then call `crash_patterns` with `pattern_key: app_versions` for the highest-impact new groups.
* **Review signals:** `list_reviews` filtered to low ratings, then correlate with crash and hang activity in the same window.
* **APM regression:** choose the `metric` for the signal, then `apm_list_groups` sorted by `apdex_change` (signed delta) across the two `app_version` values, take the most-degraded groups, then `apm_group_view` with `dimensions` to localize each regression to a cohort (OS, device, country, version), then `apm_occurrence` with `selector: worst` for a concrete worst case to reason over.
* **APM group deep dive:** `apm_list_groups` for the metric sorted by its pain key to find the group, then `apm_group_view` — `summary` for the headline metrics, then the view that matches the pain: `spans_table` (or `stages_breakdown` for launch/screen loading) for a slow segment, `outliers` for the tail driving p95 (not on `frame_drop`), or the `failure_rate` view for a failing network group, then `apm_occurrence` for the worst-failed request when the pain is failures, not the slowest.
* **Reference material:** before interpreting a metric's numbers, the agent reads that metric's reference files. Coverage gating, stage boundaries, and the conditions that make a number misleading aren't visible in the raw figure.

If an APM tool returns a 403 or 501, the agent skips that step and reports the reason. It does not infer "no regression" from a tool error.

The agent does not invent metrics that the Luciq MCP doesn't expose.

### Step 3. Symbolicate if the trace is obfuscated (crash / hang track)

If the top frame is a hex address, an obfuscated symbol, or a `<unknown>` marker, the build is missing its symbol artifact:

* iOS: dSYM upload.
* Android: R8 / ProGuard mapping.
* Flutter: split-debug-info.
* React Native: source map.

The agent points the user at Luciq's symbol upload flow rather than reasoning over hex addresses.

### Step 4. Map to local source

**Crash / hang track — map the top frame:**

* `Grep` the symbol (class + method) across the project.
* `Read` the matched file with a small window around the offending line (10 lines above and below).
* For multi-platform projects (KMP, RN, Flutter), prefer the platform-specific source set first (`iosMain/`, `androidMain/`).

**APM track — map the endpoint or span to the call site:**

The APM group name is the request signature (method + URL path template, e.g. `GET /v2/orders/{id}`). The agent maps it to the code that issues or handles it:

* `Grep` the path template, the host, or the path segments across the project. For a client SDK, that's the request-building call site. For a server repo, it's the route/handler registration.
* When `spans_table` localized the cost to one segment (a DB span, a downstream call, a serialization span), grep that segment's operation name — the bottleneck is usually inside that call, not in the request setup.
* Use the `dimensions` breakdown to constrain the hypothesis: a regression confined to one OS version or device tier points at a different cause than one uniform across cohorts.

If the symbol or endpoint doesn't exist locally, the agent surfaces that fact rather than guessing at a fix.

### Step 5. Form a hypothesis

**Crash / hang track:**

```
HYPOTHESIS: <one sentence>
CONFIDENCE: <low / medium / high>

EVIDENCE:
- Top frame: <file>:<line> - <symbol>     [from: crash_details]
- Distribution: <e.g. only iOS 18.0+>     [from: crash_patterns]
- Repro context: <e.g. backgrounded for ~5s>  [from: get_occurrence_details]
- Correlated signal: <e.g. matching review text>  [from: list_reviews]

ROOT CAUSE: <the specific defect>
```

**APM track:**

```
HYPOTHESIS: <one sentence>
CONFIDENCE: <low / medium / high>

EVIDENCE:
- Group: GET /v2/orders/{id}  apdex 0.71 (was 0.94)   [from: apm_list_groups, sort apdex_change]
- p95: 2,140ms (was 410ms)                             [from: apm_group_view summary]
- Cohort: regression isolated to iOS 18.x             [from: apm_group_view dimensions]
- Bottleneck: DB span "orders.fetch" = 1,800ms        [from: apm_group_view spans_table]
- Call site: <file>:<line>                             [from: Grep]

ROOT CAUSE: <the specific defect>
```

Confidence is honest, not optimistic. Three corroborating MCP sources is high. Reasoning from the top frame or a single number alone is low. A latency figure with no cohort breakdown and no span decomposition is a symptom, not a root cause.

### Step 6. Propose a fix

The agent shows a diff, explains how the fix addresses the root cause, and flags side effects. It does not apply the diff without user confirmation.

## Pattern library

The skill carries a library of mobile failure patterns. Each pattern is a reference the agent reaches for when it sees the corresponding signature in the MCP data.

### Swift Concurrency issues (iOS)

When the top frame involves `async`, `await`, an actor, or a `Sendable` violation:

* Check whether the crash is a `Swift runtime: Fatal error: ...` rather than a typical exception. That's a concurrency-safety check firing.
* Confirm the OS distribution from `crash_patterns` with `pattern_key: oses`. Swift 6 strict-concurrency checks behave differently across iOS versions.
* Look at the session profiler from `get_occurrence_details` for hop-to-`@MainActor` patterns near the crash time.
* Don't recommend slapping `@MainActor` on a class to silence the error. Treat that as a smell, not a fix.

### Android ANRs (`ANDROID_FATAL_HANG`)

When `list_app_hangs` returns an Android hang:

* The `crash_cause` field tells you where the main thread was blocked, but not always what blocked it. Pull a few `get_occurrence_details` to see recent main-thread activity and pending I/O.
* Check `pattern_key: app_versions` to see whether the ANR is a regression or a long-tail issue.
* Common offenders: synchronous network calls on the main thread, large `SharedPreferences.commit()` writes, blocking `Lock` acquisitions, work scheduled on the wrong dispatcher.

### iOS UI hangs (`FATAL_UI_HANG`)

* The hang `exception` summary indicates duration class.
* Pull the occurrence to confirm what the user was doing. The `current_view` and `app_status` (foreground / background) fields disambiguate.
* Common offenders: synchronous Core Data on `NSManagedObjectContext.viewContext`, file I/O on the main queue, expensive layout work in `viewDidLayoutSubviews`.

### Out-of-memory crashes

* OOMs surface as terminations, not classic crashes. Check `crash_type` and the exception name.
* Pull the occurrence's `state.memory` and `state.storage` fields from `get_occurrence_details` for the resource state at termination.
* Look at `pattern_key: devices`. OOMs concentrate on lower-RAM devices and surface a device-tier story the agent should call out.

### Network failure correlated crashes

* For crashes with a stack frame in networking code, pull the occurrence's logs URL from `get_occurrence_details` (compressed log archive).
* Cross-reference with bug reports in the same window via `bug_details`. The `state.logs.network_log` URL often shows the failed request that preceded the crash.
* Don't assume timeout vs DNS failure vs server error without the log evidence. The categories matter for the fix.

### APM latency regression

When `apm_list_groups` sorted by `apdex_change` shows a group degrading between versions:

* Before trusting the apdex, the agent sanity-checks `threshold_ms` against `50th_percentile_ms`, then weighs it against the endpoint's business role: a list/read call should be near-real-time, a few hundred ms; a heavy export doesn't need to be. A target set for the wrong role is a config fix, not a code fix.
* Confirm with absolute numbers: pull `apm_group_view summary` for `50th_percentile_ms` and `95th_percentile_ms` before and after.
* Always run `dimensions` to localize. A regression confined to one OS version or device tier is a different bug than one uniform across cohorts.
* Use `spans_table` to attribute the time. The fix targets the dominant span. Chasing the request-setup code when the cost is in a DB span wastes effort.
* Use `outliers` when p95/p99 moved but the median didn't — the tail requests carry the signature.

### APM app launch

When `apm_list_groups` flags a launch group (`metric: launch`):

* Segment by `type` first. Cold, warm, and hot launches measure different windows, so aggregating across them — or across platforms — produces a meaningless number.
* Attribute with `stages_breakdown`, not `spans_table`. The dominant stage maps to code via the platform's optimization-targets table; on React Native and Flutter, a dominant native stage means the cause isn't in JS or Dart.
* Use `dimensions` with `pattern_key: first_screen` to find which entry screen carries the cost, and `outliers` when p95 moved but p50 didn't.
* The agent validates the number before quoting it — several conditions can make a launch total mean something other than "the app is slow," and they aren't visible in the number itself.

### APM network

When `apm_list_groups` flags a network group (`metric: network`):

* Coverage comes first. Capture isn't automatic on Android or Flutter, so the agent checks the codebase before reading absence as a measurement.
* There's no `stages_breakdown` view for network. The agent attributes with `spans_table` and uses `apm_occurrence` for one request's stage detail.
* The list row gives `latency_p95_ms` and `failure_rate` only — no p50, no occurrence count. Those come from `summary` or `dimensions`.
* The agent segments on `radio` before comparing latency, and accounts for what the measured window excludes — the app's own queueing and interceptors aren't in it.

### APM failure-rate spike

When `apm_list_groups` sorted by `failure_rate` flags a group:

* Split `total_failure_rate` into `client_failure_rate` vs `server_failure_rate`. Client failures (4xx, timeouts, cancellations) point at the app; server failures (5xx) point at the backend. They lead to opposite fixes.
* Both native platforms distort the client-side rate, in opposite directions, so the agent applies the platform-specific correction before quoting it.
* Filter the group by `failure_name` / `failure_type` to see whether it's one error class or many.
* Cross-reference the window with `list_crashes` and `bug_details` — a failure-rate spike that coincides with a crash spike on the same call path is usually one root cause, not two.

### APM throughput change

When `throughput_chart` shows a spike or drop:

* A drop in throughput with flat latency often means callers stopped calling — a client-side gating change, a feature flag, a rollout — not a performance defect. Check `dimensions` by `app_version` and correlate with a release.
* A spike with rising latency is load-driven; the fix is usually capacity or backpressure, not a code path. Say which the evidence supports.

## Honest about what's out of scope

The skill is grounded in what Luciq MCP exposes today. It deliberately does not:

* Compute crash-free session rate or any metric the MCP doesn't return.
* Reason about App Store rating drops as a primary investigation entry point. `list_reviews` is correlation, not causation.
* Pretend to have data it doesn't have. If a query returns nothing, the agent surfaces that fact and stops.

When Luciq MCP grows new tools (release comparison, session replay context), this skill grows with them.

## When to reach for it

* A new crash spikes in production and you want a triage and a candidate fix without leaving your IDE.
* A user-reported bug has reproduction steps but you don't know which screen they were on or what network call failed. The agent pulls that from `bug_details` and points you at the file.
* You're cutting a release and want a quick diff of new crashes between versions. The agent runs the comparison via `list_crashes` and `crash_patterns`.
* You're handed a crash by a teammate with just a number. The agent investigates from the number alone.
* You saw an apdex drop or a p95 spike on an endpoint since the last release. The agent finds the worst-affected group, decomposes the time via `spans_table`, and maps the bottleneck to local source.


# luciq-group-bugs

The luciq-group-bugs skill consolidates your bug list by marking duplicates with your own grouping logic, writing merges only on approval.

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) 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)) and not pulling a read-only report (that's [`luciq-readout`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-readout)).

## 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 top-level-combinator limitation. 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 `{"bugs": [...]}`, only a thin object per bug: `title` (can be `null`), `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), configured and authenticated. The [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup) 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) 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.


# luciq-migrate

Use the luciq-migrate AI agent skill to automatically migrate your mobile app from Instabug to Luciq. Let AI handle the migration with minimal manual effort.

Migrate a mobile codebase from the legacy Instabug SDK to Luciq, or upgrade between Luciq SDK versions, with an automated codemod and a manual review pass.

## Use this skill when

* The user asks to *"migrate from Instabug to Luciq"*, *"move us off Instabug"*, or *"rebrand the SDK"*.
* The user wants to bump a major Luciq SDK version that includes breaking changes.
* The user wants to clean up deprecated Luciq APIs in a repo.

## What it does

`luciq-migrate` runs Luciq's published migration tooling and walks the user through the review.

* Detects whether the project is on legacy Instabug SDK or already on Luciq, and on which version.
* Reads the dependency manifests (`Podfile`, `build.gradle`, `pubspec.yaml`, `package.json`) to scope the work.
* Runs the platform-specific automated migration script that ships with Luciq. Handles roughly 90% of the rename and API replacement work.
* Surfaces the remaining 10% as a manual-review checklist with file and line references.
* Runs the platform's build to verify nothing regressed.

The agent shows three sample diffs from the auto-migration before applying the bulk transform. Bulk renames without preview corrupt repos.

## 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/luciqai/luciq-docs/blob/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step.

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

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-migrate/SKILL.md) from the public repo to `~/.claude/skills/luciq-migrate/SKILL.md` (user-global) or `.claude/skills/luciq-migrate/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-migrate
description: Use when the user asks to migrate a mobile codebase from the legacy Instabug SDK to Luciq, upgrade between Luciq SDK versions, or replace deprecated Luciq APIs. Triggers include phrases like "migrate from Instabug to Luciq", "move us off Instabug", "upgrade Luciq SDK to vX", "Phoenix migration", or "replace deprecated Luciq APIs". Covers iOS, Android, Flutter, React Native, KMP. First-time SDK installs go to luciq-setup.
---

# Luciq SDK Migration

Apply code transforms to migrate or upgrade the Luciq SDK. Drive the workflow off the canonical Migration Hub, not memorized rename tables. Bulk transforms without preview corrupt repos. YOU MUST show three sample diffs before bulk-applying.

## When NOT to use this skill

- First-time integration of Luciq into a project that has never used Luciq or Instabug, use `luciq-setup`.
- Investigating a crash, hang, or production signal, use `luciq-debug`.

If the user's request fits any of the above, STOP and route them to the right skill rather than running this one.

## Canonical source of truth

YOU MUST fetch the current rename and deprecation tables from the live Migration Hub before applying any transform. Do not hardcode them in this skill. They go stale every release.

| Concern | Source |
| --- | --- |
| Instabug-to-Luciq renames, vN-to-vN+1 deprecations, v1-to-v2 ("Phoenix") API changes | https://docs.luciq.ai/getting-started/luciq-migration-hub |

## Workflow

### 1. Refuse to start on a dirty working tree

Migrations modify source in place. Source must be committed. If `git status` shows uncommitted changes, STOP and ask the user to commit, stash, or explicitly override. No exceptions for "small changes".

### 2. Detect platform and current SDK + version

Apply the rules below. First match wins.

| Platform | Source of truth | What to look for |
| --- | --- | --- |
| iOS | `Podfile.lock` | `Instabug` or `Luciq` pod |
| Android | `app/build.gradle*` plus `gradle.lockfile` | `com.instabug.*` or `ai.luciq.library.*` |
| Flutter | `pubspec.lock` | `instabug_flutter` or `luciq_flutter` |
| React Native | `package-lock.json` or `yarn.lock` | the relevant Instabug or Luciq package |
| KMP | both Android and iOS sources | as above for each side |

Report: SDK name, current version, count of call sites. The call-site count comes from `Grep` of the old symbol root (for example, `Instabug` for iOS, `com.instabug` for Android).

### 3. Pick the transform set

| Intent | Transform set |
| --- | --- |
| Instabug to Luciq | Rename `Instabug*` symbols, imports, packages, dependency entries. |
| vN to vN+1 | Apply known deprecations between those versions. |
| v1 to v2 / "Phoenix" | v1 to v2 API surface. Fetch the canonical mapping from the Migration Hub. |

Always look up the current rename and deprecation tables from the Migration Hub above. Do not invent renames.

### 4. Show three sample diffs before bulk-applying

This is a hard gate. Do not skip it.

1. Use `Grep` to find the first three call sites of the old symbol.
2. Generate the diff for each call site.
3. Show all three to the user.
4. Wait for explicit sign-off.

If the three samples reveal an ambiguity (for example, a renamed method has different parameters in different call sites), STOP, surface the ambiguity, and ask. Do not bulk-apply across an ambiguity.

### 5. Apply in waves on confirmation

Apply transforms in this order so the project remains parseable after each wave:

1. Dependency manifest: `Podfile`, `build.gradle`, `pubspec.yaml`, `package.json`.
2. Imports: every file referencing the old symbol.
3. Type names: class refs, method calls.
4. Project metadata: group names, build phases.

After each wave, sanity check by opening one sample file and confirming the transform applied cleanly.

### 6. Run the build to verify

| Platform | Command |
| --- | --- |
| iOS | `pod install && xcodebuild -workspace <Workspace>.xcworkspace -scheme <Scheme> build` |
| Android | `./gradlew :app:assembleDebug` |
| Flutter | `flutter pub get && flutter analyze && flutter build apk --debug` |
| React Native | `npm install && npx react-native run-android` (or `run-ios`) |
| KMP | both Android and iOS builds |

Derive `<Workspace>` and `<Scheme>` for iOS as in `luciq-setup`: `xcodebuild -list` to enumerate, ask the user if multiple options exist.

STOP and surface errors. NEVER claim "done" if the build is broken.

### 7. Print the manual-review checklist

For any APIs whose semantics changed beyond a rename (different parameters, callback shapes, default behavior), source the list from the Migration Hub and emit:

```
MANUAL REVIEW REQUIRED:
- [ ] <api>: <what changed>  [<file>:<line>]
```

These are not auto-applied. The user owns the semantic decision.

## Style

- ALWAYS show three sample diffs before bulk-apply.
- ALWAYS verify against the Migration Hub before applying a rename.
- Do not claim "done" if the build is broken.
- Always flag ambiguous renames for manual review.

## Red Flags - STOP and surface to the user

If you catch yourself thinking any of these, you are about to corrupt the repo. STOP, surface to the user, do not proceed:

- "I skipped the three-diff sample because the rename is obvious." It isn't. One ambiguity buried in 200 call sites is a multi-hour cleanup. Show the samples.
- "The working tree was dirty but I figured the changes were unrelated." Migrations interleave with uncommitted work and become impossible to roll back. Refuse and ask.
- "I hardcoded the rename mapping from this file because it looked right." This file is illustrative. The Migration Hub is the source of truth.
- "The build had errors but the rename succeeded, so it's mostly done." It isn't done. Surface the errors verbatim.
- "An ambiguous rename came up but I picked the more common variant." Ambiguity is a stop condition, not a tiebreaker.
- "I bulk-applied across waves without a sanity-check read." Each wave can break parsing for the next. Sanity-check.
- "The manual-review list is long, so I trimmed the low-priority items." The user owns that decision, not the agent. Print the full list.

The pattern: every shortcut here trades "looks done" for "actually correct." The skill's job is to be correct.
````

</details>

## Where the methodology lives

The skill follows Luciq's published [Migration Hub](/getting-started/luciq-migration-hub) and the platform-specific migration guides. Those pages are the canonical source for the rename tables, the script invocation, and the safety checklist (commit before you start, etc.). The skill packages those steps as a SKILL.md so the agent can drive the migration end to end.

Platform guides referenced by the skill:

* iOS migration guide
* Android migration guide
* React Native migration guide
* Flutter migration guide

(Each linked from the [Migration Hub](/getting-started/luciq-migration-hub).)

## Prerequisites

* A clean working tree. Migrations modify source files in place; the agent will refuse to start on a dirty branch unless explicitly overridden.
* Source code committed to version control.
* For ambiguous renames, the user available to review and confirm. The skill flags any API whose semantics changed (not just the name).

## After the migration

The agent prints:

* The number of files modified.
* The list of API changes flagged for manual review (semantic differences beyond renames).
* The build status.
* A pointer to test the integration end to end before merging.

For projects with non-trivial complexity, the published Migration Hub estimates the total work at around 2 to 3 hours including testing.


# luciq-verify

The luciq-verify skill checks a Luciq SDK upgrade end to end before you ship, using the dashboard as the oracle for integration regressions.

End-to-end behavioral verification of a Luciq SDK upgrade. The mechanism is the dashboard as oracle: drive a debug build through a deterministic smoke, let the new SDK ship telemetry, then pull that occurrence back through MCP and audit what landed against the customer's integration contract. The skill catches integration regressions that don't break a build — redaction callbacks that no longer fire, header-preservation hooks that silently dropped, attribute APIs whose keys got renamed, new auto-instrumentation that captured PII.

The verification harness is not a published package. The skill generates it directly inside the customer's debug variant on first run and reuses it on every subsequent upgrade. First invocation does the heavy work (harness scaffold, rule-pack bootstrap, environment confirmation); every subsequent SDK upgrade reuses the same harness.

## Use this skill when

* The user mentions verifying an SDK upgrade, auditing a Luciq version bump, or asks *"is it safe to release"* after bumping the Luciq dependency.
* The user pastes a build with a freshly bumped Luciq dependency and asks whether to release.
* The user wants to smoke-test the new SDK to confirm that URL redirection, masking, preserved headers, user attributes, PII redaction, feature flags, and experiments all behave correctly against the new version.

## What the agent does

* **Tier detection.** Classifies the project into one of four maturity tiers (full T3 harness + rule pack, harness-only T2, organic telemetry T1, or no telemetry T0) and shapes the rest of the workflow accordingly.
* **Harness scaffold.** Generates `LuciqVerifyHarness.<ext>` inside the customer's debug variant on first run; reuses and optionally regenerates it on subsequent runs. Shows diffs before applying any change.
* **Rule pack bootstrap.** Writes `luciq-verify.yaml` with the base verification rules and stubs for customer-specific checks. Infers a draft rule pack from existing telemetry when enough baseline data is available.
* **Static audit.** Inspects source and build config — SDK version, module activation, masking configuration, dSYM/mapping upload setup — without running the app. Reports findings by `S-*` code.
* **Pre-flight safety checks.** Confirms the build has the new SDK pinned, is pointed at a non-production backend, has a device or emulator running, and that the Luciq MCP server is reachable. Refuses to proceed against a production backend.
* **Smoke.** Drives the build through a canonical trigger sequence (set persona → fire network burst → exercise feature flags → file a bug report → flush → crash), then polls all three MCP channels (crash, bug, APM) for the resulting occurrence.
* **Runtime audit.** Applies the merged rule pack (base + customer-specific rules) against the captured payload. A single `FAIL` blocks release.
* **Report + drift.** Renders `luciq-verify-report.html` and `luciq-verify-report.md` with evidence citations. Produces a rule-pack drift diff the user accepts or rejects hunk by hunk.

## 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/luciqai/luciq-docs/blob/main/home/product-guides-and-integrations/product-guides/luciq-mcp-server/README.md) in one step.

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

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-verify/SKILL.md) from the public repo to `~/.claude/skills/luciq-verify/SKILL.md` (user-global) or `.claude/skills/luciq-verify/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-verify
description: Verify a Luciq SDK upgrade end to end before shipping. Confirms the customer's custom integration (URL redirection, masking / redaction callbacks, preserved headers, persona attributes, PII masking, feature flags, experiments, user steps, user attributes) still behaves correctly against the new SDK version. Use whenever the user mentions verifying an SDK upgrade, auditing a Luciq version bump, "is it safe to release", smoke-testing the new SDK, or pastes a build with a freshly bumped Luciq dependency and asks whether to release. Scaffolds a luciq-verify harness into the debug variant, drives it to produce a fresh occurrence, pulls evidence via the Luciq MCP server (APM, bug, and crash channels), applies a customer-specific rule pack against the captured payload, and renders a pass/fail HTML+Markdown report. For first-time SDK installs use luciq-setup; for the rename/upgrade transform use luciq-migrate; for production crash investigation use luciq-debug.
---

# Luciq SDK Upgrade Verification

End-to-end behavioral verification of a Luciq SDK upgrade. The mechanism is **the dashboard as oracle**: drive a debug build through a deterministic smoke, let the new SDK ship telemetry, then pull that occurrence back through MCP and audit what landed against the customer's contract. The skill catches integrations broken by SDK internal changes — redaction callbacks that no longer fire, header-preservation hooks that silently dropped, attribute APIs whose keys got renamed, new auto-instrumentation that captured PII. None of those break a build. All of them ship broken if you only test "does it compile."

The skill is **self-contained and idempotent**. The verification harness is not a published package; this skill generates it directly inside the customer's debug variant the first time it runs and reuses it forever after. First invocation does the heavy work (harness scaffold, rule-pack bootstrap, environment confirmation); every subsequent SDK upgrade is a 2-minute "press go."

## When NOT to use this skill

- First-time integration of Luciq into a project that has never used Luciq, use `luciq-setup`. Setup must succeed before verification can run.
- Performing the Instabug-to-Luciq rename or applying vN-to-vN+1 API transforms, use `luciq-migrate`. Verification runs **after** the migration transform, against the new build.
- Investigating a production crash, hang, or bug, use `luciq-debug`. Verification audits a synthetic smoke; debug audits real-user signal.
- General mobile QA 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.

If the request fits any of the above, route there and stop — running this skill on those situations produces misleading results.

## Prerequisites

### Hard dependencies (skill refuses to run without these)

| Artifact | What for | If missing |
| --- | --- | --- |
| **Luciq MCP server, authenticated** | The entire audit is grounded in what the Luciq MCP exposes — `list_applications`, `list_crashes`, `list_bugs`, `list_occurrences_tokens`, `get_occurrence_details`, `bug_details`, `crash_patterns`, and `apm_*`. Without it the skill has no oracle to verify against. | STOP at Phase 3 pre-flight. Route the user to `luciq-setup` step 7 or to https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide. Do not attempt static-analysis-only "verification" — it would silently pass real regressions. |
| **A debug-variant build with the new SDK + the luciq-verify harness** | Produces a deterministic occurrence to audit | Run Phase 1 below — the skill generates the harness (scaffold mode) or validates the customer's existing dev-tools surface (reuse mode). |
| **A device, simulator, or emulator** | Executes the build that produces the occurrence | Stop; ask the user to boot one. Do not spawn one without confirmation. |

The skill itself runs locally and pulls cloud-side telemetry — but cannot synthesize an occurrence without something running the build. This is not optional.

### Optional integrations (the skill works without these, gains capabilities with them)

| Integration | What it adds | If missing |
| --- | --- | --- |
| **[mobile-mcp](https://github.com/mobile-next/mobile-mcp)** server | Drives the smoke by reading the device's accessibility tree and tapping buttons by label / element ID. Required for reuse mode's `invoke_via: tap_by_label` path — useful when the customer's existing dev-tools menu can't be driven by intent extras or deep-link params. Also enables diagnostic screenshots (`optional_integrations.mobile_mcp.screenshot_on_smoke_end/timeout`). | `tap_by_label` triggers degrade to `manual` (the skill prints the trigger sequence and waits for the user to tap). Screenshots are simply not captured. Pre-flight passes unless the rule pack sets `optional_integrations.mobile_mcp.enabled: force`. |

## Reference files

Detailed material is split out so the SKILL.md stays workflow-focused. Read the relevant reference when the workflow points to it:

| Reference | When to read |
| --- | --- |
| `references/payload-schemas.md` | Before any runtime audit. Defines the three channels (APM / Bug / Crash), every MCP tool's response shape, identifier model, mode/platform/crash-type enums, filter naming differences. Field paths used in this SKILL.md come from here. |
| `references/check-catalog.md` | When implementing Phase 5 (runtime audit). Full E/C/S/P/A/T/U code catalog with per-channel evidence sources and the platform applicability matrix (which rules emit `N/A` on which platforms). |
| `references/static-checks-catalog.md` | When implementing Phase 2 (static audit). Full S-* code catalog: install, modules, invocation, identity, feature-flags, logging, masking, dSYM/mapping upload, build systems, privacy modifiers. |
| `references/extractors-ios.md` | When running Phase 2 on an iOS project. Per-file scan patterns + the agent-native extraction recipe (Read + Grep instructions). |
| `references/extractors-android.md` | Phase 2 on Android. Gradle Groovy + KTS coverage. |
| `references/extractors-flutter.md` | Phase 2 on Flutter. `pubspec.yaml` + Dart source patterns. |
| `references/extractors-rn.md` | Phase 2 on React Native. `package.json` + JS/TS source patterns. |
| `references/rule-pack-format.md` | When scaffolding `luciq-verify.yaml` (Phase 1c), running bootstrap inference (Phase 1d), or processing drift detection (Phase 6b). Full YAML schema, base pack, inference rules. |
| `references/harness-contract.md` | When generating the harness (Phase 1b/1c) or regenerating it on a later run. Per-platform scaffold paths, required API surface, marker convention, debug-only gating. |

## Canonical sources of truth

Verify SDK API signatures and platform packaging against the live integration guides — they evolve, and signatures memorized in this skill or its references can go stale:

| Concern | Source |
| --- | --- |
| Per-platform SDK API surface (init, hooks, callbacks, masking, identification, `reportBug`) | The platform-specific guides linked in `luciq-setup`'s "Canonical sources of truth" table |
| MCP tool surface and authentication | https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide |
| App tokens, slugs, and modes | Luciq MCP `list_applications` |

## Workflow checklist

Track every phase. Stop on any failed step rather than continuing past a broken state — a misleading "PASS" report is worse than no report.

```
Verification Progress:
- [ ] 0. Detect customer maturity tier (drives phase shape below)
- [ ] 1. Setup (idempotent; no-ops on second run)
- [ ] 2. Static audit (config inspection; skipped if --runtime)
- [ ] 3. Pre-flight safety checks (skipped if --static)
- [ ] 4. Smoke — drive the harness; produce an occurrence (skipped if --static)
- [ ] 5. Runtime audit (MCP pull + rule application; skipped if --static)
- [ ] 6. Report (rendered HTML/Markdown) + drift detection
```

## 0. Detect customer maturity tier

The skill **degrades gracefully** by tier. Detect which tier applies before doing anything else; the rest of the workflow branches on this. Detection is purely local (lockfile reads + MCP probes). Report the detected tier explicitly — the user often doesn't know which tier they're in until you tell them.

| Tier | Marker | What works |
| --- | --- | --- |
| T3 (full) | Upgrade-verify harness present **and** `luciq-verify.yaml` exists | Deterministic synthetic audit with customer-specific rule pack — the end state |
| T2 (harness only) | Harness scaffolded, no rule pack | Deterministic synthetic audit against base rule pack only — most `C*` and `P*` checks run, customer-specific checks (`A*` personas, custom redaction tokens) skipped |
| T1 (telemetry only) | Neither installed, but `list_crashes` or `apm_list_groups` returns ≥ 1 record in the last 30 days from the bumped SDK version | Audit the most recent organic occurrence; `S*` synthetic-marker checks become SKIP; `C0b` (recency) becomes WARN |
| T0 (empty) | No telemetry, no harness | Cannot audit. Run Phase 1 (Setup), stop, ask the user to produce one occurrence, re-invoke |

APM channel availability is a sub-detection: APM's `filters.platform` is `ios | android` only — Flutter (DART) and React Native (JAVASCRIPT) projects have APM permanently `N/A`. Don't probe APM on those platforms; the bug + crash channels carry the full audit there. (Detail: `references/payload-schemas.md`.)

## 1. Setup

Idempotent. Skip any sub-step whose artifact already exists. On a clean repo, this phase produces a single PR-shaped change set scoped strictly to the debug variant.

### 1a. Detect platform

Reuse `luciq-setup`'s platform-detection rules verbatim (first match wins on root markers; stop on ambiguity). Verification refuses to proceed on an ambiguous workspace — guessing the platform here corrupts every downstream step.

### 1b. Set up the harness — scaffold or reuse

Two modes, picked by the customer's `harness.mode` in `luciq-verify.yaml`. Default is `scaffold`. Read `references/harness-contract.md` for the full spec of both.

**Scaffold mode** (default — `harness.mode: scaffold`)
The skill generates `LuciqVerifyHarness.<ext>` directly inside the customer's debug variant. Per-platform file paths, the required API surface, the marker convention (`current_view == "LuciqVerifyHarness"`), and debug-only gating rules are in `references/harness-contract.md`.

Why generated, not packaged: per-customer customization (which redaction tokens to fire, which personas to test) makes a single binary library a poor fit; the generated source is small (≈ 100–200 lines per platform) and can be regenerated by the skill on subsequent runs.

Show diffs before applying. Never touch release / production source sets, manifests, Info.plists, or entry points — a release-variant harness with a public deep link is a remote-crash vector.

**Reuse mode** (`harness.mode: reuse`)
For projects that already have a debug menu with crash / hang / bug triggers (e.g. a `DevToolsFragment` or a `CrashLab` / `HangTrigger` / `ErrorTrigger` family), the skill drives the existing surface instead of generating a parallel one. The rule pack declares the marker view, an optional deep link / activity, and a trigger map (e.g. `forceCrash: "CrashTrigger.forceUnwrapNil"`).

Before the smoke runs, the skill enforces the reuse-mode invariants from `references/harness-contract.md`:
- `marker_view` is non-empty and has at least one prior occurrence in the dashboard
- The reused surface is gated to the debug variant (debug source set, `#if DEBUG`, or debug-only manifest entry)
- A `flushNow` mapping exists (strongly recommended; without it recency becomes a timer race)

Unmapped triggers become no-ops in the smoke and the rules that needed them SKIP. The audit degrades gracefully — a reuse-mode setup with only `forceCrash` and `flushNow` mapped still runs E*, C0*, S1, and (via the crash channel) most of C1–C7.

### 1c. Scaffold the rule pack

Write `luciq-verify.yaml` at the repo root with the base pack inlined plus TODO stubs for customer-specific rules. Schema, base pack defaults, and a worked example are in `references/rule-pack-format.md`. The first run can leave all customer-specific rules commented out; bootstrap inference (Phase 1d) and drift detection (Phase 6b) fill them in over time.

### 1d. Bootstrap rule inference (if any telemetry exists)

If `list_crashes` returns ≥ 10 occurrences from the **baseline** (pre-upgrade) SDK version, run the inference pass described in `references/rule-pack-format.md` ("Bootstrap inference"). The skill proposes a populated rule pack draft; commit only on user confirmation. On a brand-new integration, skip and rely on drift detection over subsequent runs.

PII regex and custom-attribute slot mappings are **never auto-inferred and committed** — the cost asymmetry of false positives vs. missing rules favors human approval. The skill may suggest; the user approves.

## 2. Static audit

Inspects the customer's source tree and build config without running the app. Catches integration bugs that surface at build/config time and never get caught by the runtime audit alone: SDK not installed (or pinned to the wrong version), modules disabled by code, masking off, dSYM / mapping upload not wired, redundant invocation listeners, suspicious patterns in custom logging.

Skipped if invoked with `--runtime`. In default and `--static` modes, this phase runs after Phase 1 (Setup) and before Phase 3 (Pre-flight). The findings feed the combined report alongside runtime-audit results.

The audit is **agent-native**: the skill instructs the agent which files to read and which patterns to look for, via per-platform extractor reference docs. No external runtimes, no scanning daemon, no installed dependencies.

### 2a. Discover platform-relevant files

Reuse the platform detection from Phase 1a. Based on the detected platform, point the agent at the corresponding extractor reference:

| Platform | Reference | Files scanned |
| --- | --- | --- |
| iOS | `references/extractors-ios.md` | `Package.resolved`, `Podfile` (+ `.lock`), `Cartfile` (+ `.resolved`), `*.swift`, `*.m`, `Info.plist`, `project.pbxproj`, dSYM upload shell scripts |
| Android | `references/extractors-android.md` | `build.gradle`(`.kts`), `settings.gradle`(`.kts`), `AndroidManifest.xml`, `*.kt`, `*.java` |
| Flutter | `references/extractors-flutter.md` | `pubspec.yaml`, `*.dart` |
| React Native | `references/extractors-rn.md` — plus `extractors-ios.md` on `ios/` and `extractors-android.md` on `android/` when those subfolders exist (RN projects are hybrid; native-side Pod / Gradle integration is part of the audit) | `package.json`, `*.{js,jsx,ts,tsx}`, and the native files when applicable |

### 2b. Run the extractors

Each reference doc enumerates a category of static checks (`S-*` codes — distinct from the `E*` / `C*` / `P*` / `A*` codes that live in `references/check-catalog.md` for the runtime audit). The agent reads the listed files (Read + Grep), applies the documented patterns, and produces findings. Field paths and check semantics live in `references/static-checks-catalog.md`.

Categories per platform (full per-platform spec in each extractor reference):

- **SDK install + version detection** — pinned version, install method, mismatched debug vs. release
- **Module activation** — Bug Reporting, Crash Reporting, APM, Session Replay, NDK, Surveys, Replies, Feature Requests, OOM monitor, ANR monitor, network auto-masking
- **Invocation events** — shake / screenshot / floating-button / two-finger swipe / programmatic
- **User identification + attribute hooks** — `setUserData`, `setCustomData`, `addUserAttribute`, `trackUserSteps`
- **Feature flag API usage** — `addFeatureFlag`, `removeFeatureFlag`, `checkFeatures`
- **Custom logging + user-event logging** — `Luciq.log*`, `LCQLog.log*`, `logUserEvent`
- **Masking / privacy config** — network auto-masking, screenshot auto-masking modes, sensitive header configuration
- **dSYM / mapping upload setup** — iOS dSYM upload script presence; Android mapping upload Gradle plugin presence
- **Build system detection** — SPM / CocoaPods / Carthage on iOS; Gradle Groovy / Gradle KTS on Android; npm on RN; pub on Flutter
- **Privacy view modifiers** — iOS only (SwiftUI `.luciqPrivate()`, UIKit equivalents)

### 2c. Privacy constraints during extraction

Hard constraints, baked into every extractor pattern:

1. **Never quote contiguous source regions in findings.** The agent reads files to grep for specific patterns and may cite matched API names (e.g. `Luciq.start`, `setBugReportingEnabled`) by name. Surrounding lines, full functions, or other source regions must not be reproduced in the report — findings cite file path and 1-indexed line range only.
2. **Mask all detected tokens in the report.** If the agent extracts an app token from source, the report shows the first 4 characters + length (e.g. `2c5f… [40 chars]`), never the full token.
3. **Never read screenshots, asset binaries, or compiled artifacts.** Static analysis is text-only.
4. **Never open `.env` files even when present.** Listed in findings as "present" / "absent"; contents not read.

The agent's outputs go into the customer's local report file; nothing is uploaded. The skill never reaches a network endpoint other than the Luciq MCP server (during runtime audit, not static).

### 2d. Findings shape

Each finding produces one row in the static-audit section of the combined report:

| Field | Example |
| --- | --- |
| Code | `S-INSTALL-001` (see `references/static-checks-catalog.md`) |
| Status | `PASS` / `FAIL` / `WARN` / `INFO` / `DISABLED` / `SKIP` |
| Evidence | File path + line range (1-indexed) — never the matched text itself unless it's a known-safe identifier |
| Remediation | Doc link or short hint |

`FAIL` blocks release the same way runtime-audit `FAIL` does. `--static` mode produces a report with only the static section populated; default mode combines static + runtime findings into one report ordered by severity.

## 3. Pre-flight safety checks

Runs on every invocation. The point of these checks is to refuse to verify against the wrong thing — a "PASS" report from a build that's still on the old SDK, or a debug build that's mistakenly pointing at production traffic, is worse than no report.

| Check | What it confirms | Stop condition |
| --- | --- | --- |
| New SDK version in lockfile | The build the user is about to verify actually has the new SDK | Lockfile pins the old version |
| Build variant is debug | Smoking against `*.debug` / `Debug` configuration | Active variant is release / production |
| Backend environment is non-prod | Build is pointed at `alpha` / `beta` / `staging` / `qa` / `development` backend | Build is pointed at production API |
| Dashboard mode matches build env | A staging build should produce occurrences in `mode: staging` | Build env and `mode` mismatch |
| Device / emulator available | `adb devices` shows ≥ 1, or `xcrun simctl list devices booted` returns ≥ 1 | Nothing booted / connected |
| **Luciq MCP reachable** | `list_applications` returns the user's app at the expected `slug` | Auth expired or MCP not configured — STOP. This is the hard dependency that grounds the entire audit; the skill cannot proceed. |
| mobile-mcp probe (only if `optional_integrations.mobile_mcp.enabled: force`) | mobile-mcp's tool surface responds to a probe call | Rule pack forced it; STOP with "mobile-mcp required by rule pack but not installed." On default `enabled: auto`, missing mobile-mcp is fine — `tap_by_label` triggers degrade to manual. |

The skill refuses to proceed against a production build variant, a build pointed at a production backend, or `mode: production` on the MCP queries. These refusals are not overridable inline — the production-canary audit is a separate mode (see "Modes" below) that the user invokes explicitly.

## 4. Smoke

The skill drives the harness end-to-end. No manual "tap the button" handoff unless the user prefers it.

### 4a. Install + launch

Default path uses platform-native commands:

| Platform | Install | Launch harness |
| --- | --- | --- |
| iOS | `xcodebuild -scheme <Debug> -destination 'platform=iOS Simulator,id=<UDID>' install` | `xcrun simctl openurl <UDID> luciq://luciq-verify-harness` |
| Android | `./gradlew :app:installDebug` | `adb shell am start -W -a android.intent.action.VIEW -d "luciq://luciq-verify-harness"` |
| Flutter | `flutter install --debug` | platform-specific `am start` / `simctl openurl` |
| React Native | `npx react-native run-<platform>` | as above |
| KMP | run both | as above |

Derive `<Scheme>`, `<UDID>`, package name from the project. Stop on ambiguity.

If mobile-mcp is available (`optional_integrations.mobile_mcp.enabled: auto` or `force`), the skill can also use it as a unified driver — its `install_app` / `launch_app` / `open_url` primitives work across iOS and Android without the per-platform command split. This is purely a convenience; both paths produce the same outcome.

### 4b. Trigger the canonical action sequence

Order matters: attributes are set before network traffic so the audit sees them associated with the right session. The bug report is created before the crash so the audit gets a clean bug-channel sample alongside the crash sample.

```
1. LuciqVerifyHarness.setTestPersona("<persona-key-from-rule-pack>")
2. LuciqVerifyHarness.fireNetworkBurst(n=<count-from-rule-pack>)
3. LuciqVerifyHarness.exerciseFeatureFlags()       # iterates declared flags / experiments
4. LuciqVerifyHarness.reportBugReport()            # produces a bug record with SPLIT log archives — cleanest C1–C7 evidence channel after APM
5. LuciqVerifyHarness.flushNow()                   # synchronously ship pending telemetry — removes the timer race
6. LuciqVerifyHarness.forceCrash()                 # produces the auditable crash with current_view=LuciqVerifyHarness
```

In **scaffold mode**, the scaffolded harness UI fires these in sequence as soon as the deep link opens it — the skill just opens the link and waits. In **reuse mode**, the skill invokes each trigger using the `invoke_via` strategy declared in the rule pack (`deep_link_param` / `intent_extra` / `tap_by_label` / `manual`). `tap_by_label` requires mobile-mcp; absent mobile-mcp, it degrades to `manual` (the skill prints the trigger sequence and waits for the user to tap). See `references/harness-contract.md` for the strategy decision table.

### 4c. Wait for the occurrence to land — three parallel channels

Poll all three channels because the audit pulls evidence from whichever returns data. The exact polling commands per channel are in `references/payload-schemas.md`. Summary:

- **Crash path** (the synthetic crash and its session payload): `list_crashes` → `list_occurrences_tokens` → `get_occurrence_details`. If C1–C7 must run on the crash-path fallback (APM unavailable), immediately fetch `state.logs.compressed_logs.url` — the URL is time-limited.
- **Bug path** (the synthetic bug from `reportBugReport()`): `list_bugs` → `bug_details`. Fetch any non-empty archive URLs (`network_log`, `user_events`, etc.) immediately.
- **APM path** (iOS / Android only): `apm_list_groups` → `apm_group_view` → `apm_occurrence`.

**Filter every poll to the smoke window.** In shared dev workspaces an unfiltered list call returns lots of irrelevant rows; the harness marker narrows but still risks latching onto an older synthetic occurrence from a previous engineer's run. Pass `date_ms.gte = now - (recency_thresholds.fail_minutes × 60_000)` on `list_crashes` and `apm_list_groups` — the window matches `C0b`'s FAIL band so anything outside it would fail recency anyway. Per-channel filter args:

- **Crash**: `list_crashes(filters: { current_views: ["<marker_view>"], type: "CRASH", date_ms: { gte: <ts> } })`. Default `sort_by: last_occurred_at`, `direction: desc` — take the first group that matches, then page its tokens with `list_occurrences_tokens(number: <group.number>)` and apply `max(states_tokens)` (Phase 4d).
- **Bug**: `list_bugs(filters: { app_version: [<build_app_version>] })`. `list_bugs` exposes no `date_ms` or `current_views` filter; rely on the default `sort_by: reported_at`, `direction: desc`, take recent results, and client-side discard any whose `state.fields.current_view` doesn't match the marker.
- **APM**: `apm_list_groups(filters: { date_ms: { gte: <ts> }, app_version: [<build_app_version>] })`. APM also has no `current_views` filter; the date + app_version pair plus the harness's deterministic traffic shape (e.g. `fireNetworkBurst` against a known host) make the synthetic groups identifiable.

Poll every 5s for up to 90s per channel. If any channel lands, proceed with that evidence and SKIP missing-channel checks with a clear reason. Stop only if all three channels timeout — diagnostics on full timeout: was `flushNow()` actually called? Is the device offline? Is the dashboard `mode` correct for the build's backend? Did `forceCrash()` actually fire (check `adb logcat` / `xcrun simctl spawn ... log stream`)?

If mobile-mcp is available and `optional_integrations.mobile_mcp.screenshot_on_smoke_timeout: true`, capture a screenshot of the device at the moment of timeout and embed it in the report — useful diagnostic for "no occurrence landed" (was the screen blank? wrong activity? crash dialog overlay?). Similarly `screenshot_on_smoke_end: true` captures a screenshot when the smoke completes successfully, as proof of "harness was reachable and the build was installed correctly."

### 4d. Pick the right occurrence — `max(tokens)`

`list_occurrences_tokens` (crash) and `apm_occurrence` with `selector: list` (APM) can each return multiple tokens. In shared development workspaces where multiple engineers smoke against the same workspace concurrently, the audit must verify *this build's* synthetic occurrence — not someone else's.

The selection rule: sort the returned tokens **lexicographically descending** and take the first (max). ULIDs are time-prefixed, so `max(tokens) ≡ newest`. Aggregate-timestamp fields like `last_occurred_at` are group-level rollups that can lag ingest order; the ULID's embedded base32 timestamp is the authoritative chronology of the occurrence itself.

```
# pseudocode
tokens   = list_occurrences_tokens(...).tokens   # ordered however the API returns
selected = max(tokens)                            # lex-max == ULID-newest
detail   = get_occurrence_details(token=selected, ...)
```

Bugs are addressed by integer `number`, not ULID, so this rule doesn't apply on the bug channel. Detail: `references/payload-schemas.md` ("ULID structure and `max(tokens)` selection").

### 4e. Verify freshness — parse the ULID timestamp

Once the freshest occurrence is selected, verify it's *actually* fresh enough to represent this build's behavior. Parse the ULID's embedded timestamp (first 10 base32 chars, Crockford's alphabet — full recipe in `references/payload-schemas.md`) and compare against mode-dependent thresholds (`C0b` in `references/check-catalog.md`):

| Mode | WARN if older than | FAIL if older than |
| --- | --- | --- |
| `synthetic` (default) | 5 min | 30 min |
| `prod-canary` | 12h | 24h |

Customers running reuse-mode against an existing dev-tools surface can override per rule pack (`recency_thresholds: { warn_minutes, fail_minutes }`) — engineers often run the in-house trigger sequence minutes-to-hours before invoking the audit, so the synthetic defaults can be too tight.

Why parse the ULID rather than read `state.fields.reported_at`: the ULID timestamp is set at occurrence creation and is what the audit identifies the record by; `reported_at` is a separate field whose precision and timezone representation can vary. Parsing the ULID is deterministic.

## 5. Audit

The audit runs every rule in the merged rule pack (base + customer) against the captured payload. Each rule produces exactly one row in the report with a status, evidence string, and (on failure) a remediation pointer.

Full rule catalog with evidence sources per channel is in `references/check-catalog.md`. The status taxonomy (PASS / FAIL / WARN / INFO / SKIP / MANUAL / DISABLED / N/A) is also there. Key principles:

- **Cite the MCP tool result that produced each piece of evidence.** No paraphrasing, no fabrication. If the evidence comes from a presigned-URL archive, cite the archive name and the parsed line range.
- **Empty evidence is never PASS.** A missing field or empty array is SKIP with reason "evidence field missing," not silent pass. Auto-passing missing data masks integration regressions — exactly the kind of failure mode this skill exists to catch.
- **`DISABLED` is not `FAIL`.** A feature turned off at the workspace level (e.g. `user_steps` disabled by dashboard policy) is intentional configuration, not a regression. Surface as `DISABLED` with the source ("workspace policy" or "rule pack"). FAILing on intentional disables produces false-positive release blocks. See `references/check-catalog.md` for detection heuristics.
- **A single FAIL blocks the release.** MANUAL items do not block automatically but appear at the top of the report.
- **Channel preference for C1–C7 / S2 / P1 / C9: APM > Bug > Crash.** APM exposes per-request structured data; the bug payload splits logs into typed archives (`network_log`, `user_events`, `instabug_log`); the crash payload bundles everything into one archive that requires disambiguating parsing.

### Input from Phase 2 (static audit)

When the default invocation runs both static + runtime, Phase 2's findings shape Phase 5's rule evaluation. Before evaluating each rule whose evidence depends on a specific SDK module, the runtime audit consults Phase 2's `S-MODULE-*` findings:

- **`S-MODULE-<x> DISABLED`** (module turned off in source) → every dependent runtime rule emits `SKIP` with reason `"module disabled in source (S-MODULE-<x> at <file>:<line>)"`. The cross-link makes the cause traceable without re-deriving it from the empty payload.
- **`S-MODULE-<x> INFO`** (default-ON, no explicit toggle) → runtime rules run normally; the static finding is the static-side affirmation and the runtime finding is the behavioural confirmation.
- **`S-MODULE-<x> FAIL`** (module expected per rule pack but pattern absent) → runtime rules still run, but the report flags the static finding at the top so the customer sees the misconfiguration before reading runtime detail.

`--runtime`-only invocations skip this coordination because there is no Phase 2 input. `--static`-only invocations stop after Phase 2; no runtime rules to coordinate with.

## 6. Report and drift detection

### 6a. Render

Two artifacts:
- `luciq-verify-report.html` — colored status pills, expandable evidence rows, network audit table, occurrences list. Format matches the customer-screenshot style.
- `luciq-verify-report.md` — same content, plain Markdown, for PR comments and CI logs.

Both include: summary bar (counts per status); test environment block (slug, mode, app version, backend host, bundle ID, SDK version); selected occurrence block (type, number, ULID, reported timestamp, current_view); APM coverage block when available; verification checks table (every rule, status, evidence, source channel); network log audit table (full table for successful redaction; failed rows excluded with stated count); occurrences list (crash + bug IDs in the smoke window); user attributes; experiments.

A single FAIL is highlighted at the top. MANUAL rows are also surfaced at top.

### 6b. Drift detection (always runs)

Compare the observed payload to the declared rule pack and produce a "Rule-pack drift" appendix proposing pack updates as a unified diff. Categories and proposal semantics are in `references/rule-pack-format.md` ("Drift detection"). The user accepts, rejects, or edits per hunk — never auto-edit `luciq-verify.yaml`.

## Modes

Two run modes are supported. Default is synthetic. Production canary requires explicit invocation.

| Mode | When | What changes |
| --- | --- | --- |
| **synthetic** (default) | Pre-release SDK upgrade verification | Smoke phase runs; harness produces a fresh occurrence; recency window is 5 min; MCP `mode` is whichever non-prod value matches the build's backend |
| **prod canary** | Day-1 of a staged rollout, audit real-user traffic from the new SDK | Smoke phase is skipped; the skill audits the most recent occurrence with the new SDK version from MCP `mode: production`; `S*` rules SKIP; recency window is 24h; PII findings are **release-blocking** even without a synthetic FAIL. Adds an explicit SDK-version regression diff via `crash_patterns` across all six `pattern_key` values: `app_versions` (primary), `oses`, `devices`, `current_views`, `app_status`, `experiments`. Same APM diff via `apm_list_groups` filtered by `app_version: [<baseline>, <new>]` and ranked by `apdex_change desc`. |

Prod canary mode is invoked explicitly: `--mode=prod-canary`. The skill surfaces a top-banner warning in the report that this is production telemetry. The "prod backend" pre-flight refusal is inverted in this mode — prod *is* the target — but every other refusal still applies.

## Invocation flags

Orthogonal to audit mode. Default invocation runs every phase; flags trim scope when only part of the audit is needed.

| Flag | Phases run | When to use |
| --- | --- | --- |
| (none — default) | 0, 1, 2, 3, 4, 5, 6 | Full audit. Static config inspection + runtime smoke + MCP-driven runtime audit, combined into one report. The intended path for SDK upgrades. |
| `--static` | 0, 1, 2, 6 | Static config inspection only. No smoke, no MCP. Useful as a precondition check before upgrade, or anytime the user wants a snapshot of "is my integration wired correctly" without driving the device. |
| `--runtime` | 0, 1, 3, 4, 5, 6 | Skip the static phase; go straight from setup to pre-flight + smoke + runtime audit. Useful when the user has already validated static config and only wants the upgrade-emission audit. |

Combine with audit mode as needed: `luciq-verify --static` runs static-only synthetic mode; `luciq-verify --runtime --mode=prod-canary` skips static and audits prod telemetry. `--static --mode=prod-canary` is an error (static doesn't read telemetry, so the mode flag has nothing to apply to) — surface the conflict and stop.

## Out of scope

Grounded in what Luciq's MCP exposes today; the skill deliberately does not:

- Audit metrics that are aggregate-only on the dashboard (crash-free session rate, MTTR, retention). The audit is per-occurrence behavioral, not statistical.
- Compute "is this string PII" via LLM judgment alone. Customer PII regex is the source of truth; the skill suggests candidates only.
- Run real UI tests (Espresso / XCUITest). The smoke is a single deep-link launch + canonical trigger sequence. Broader scenario coverage belongs in customer-owned UI tests that feed additional triggers into the harness, not in this skill.
- Modify the customer's actual integration code (redaction callbacks, URL rewriters, attribute setters). The skill edits the debug variant's harness scaffolding and the rule pack only.
- Verify on `mode: production` without explicit `prod-canary` invocation.

When new MCP tools land (APM Flows, release comparison, session replay), this skill grows with them. Until then, gaps surface as `MANUAL`.

## Style

- Show diffs before applying any code edit (harness scaffold, harness regeneration, rule-pack updates).
- Confirm before running `pod install`, gradle syncs, build commands, or smoke triggers that install / launch the app.
- Cite the MCP tool result that produced each piece of evidence in the audit. Don't paraphrase. Don't fabricate.
- Render the report in both HTML and Markdown.
- Hold the full identifier (crash: `(slug, mode, number, ulid)`; bug: `(slug, mode, number)`; APM: `(slug, mode, metric, group_uuid|group_url, token)`) end-to-end. Partial identifiers cross-contaminate.
- Verify SDK API signatures against the live guides. Verify response field shapes against an actual response when the references say "verify live."
- Refuse production-backend audits except in explicit `prod-canary` mode.

## Red Flags — patterns that mean STOP and surface to the user

These are the failure modes that produce a misleading "PASS" report. If you catch yourself reasoning in any of these directions, surface to the user and don't proceed.

**Environment and pre-flight**
- "The lockfile pins the old SDK version but I assume the user already bumped it locally." Don't assume — read the lockfile. A stale lockfile means the audit verifies the wrong build.
- "Pre-flight says the build is pointing at the prod backend, but it's a debug build so it's probably fine." It isn't. A debug build hitting prod can leak real PII into the audit payload.
- "I'll query `mode: production` because the user said 'production app'." They probably mean the production build variant for testing, not real prod telemetry. Confirm; default to a non-prod `mode`.

**Channel and identifier confusion**
- "I'll filter `list_crashes` by `platform: flutter` and got nothing — the integration must be broken." Wrong call shape. Crash filters use UPPERCASE platform values (`DART` for Flutter, `JAVASCRIPT` for RN). The lowercase form is only for `list_applications`. Re-run before concluding anything.
- "I queried `apm_group_view` with `experiments: [<x>]` and it errored." The APM filter is singular (`experiment`); the crash filter is plural (`experiments`). They are not aliases.
- "This is a Flutter project so I'll probe APM and degrade if it fails." Don't probe. APM's platform enum is `ios | android` — DART / JAVASCRIPT is permanently `N/A`. Set the channel to N/A in Phase 0.
- "I'll use the same `(slug, mode, number, ulid)` identifier for the bug." Bugs are addressed by `(slug, mode, number)` only — there is no ULID. The bug-side identifier is `state_number` (integer) inside `state.fields`.

**Channel-precedence and data shape**
- "Both crash channel and APM channel returned data — I'll just use the crash one." Reversed preference. Prefer APM > Bug > Crash for C1–C7. APM is the richer typed source; the fallback exists for accounts without APM, not as a default.
- "`get_occurrence_details` returned no `network_log` field — I'll FAIL the network checks." The crash payload does not carry network logs inline; they live at the presigned URL `state.logs.compressed_logs.url`. Fetch + decompress + parse. If `is_empty_array: true`, SKIP with reason "no log archive captured."
- "I'll look for `experiments` under `state.logs` on the bug payload." On bugs, `experiments` is at the **root** (sibling of `state`), not nested under `state.logs`. Different shape from crash.
- "I'll fetch the `compressed_logs.url` later when I render the report." Don't defer. The URL is presigned with an `Expires=` param — fetch immediately. Late fetches return 403.
- "The `os` field shows `iOS 26.1` so I'll match it against the platform filter `IOS`." Type confusion. `state.fields.os` is a combined human-readable string, not a platform enum.

**Empty evidence and false positives**
- "MCP returned empty for the network log — I'll mark the redaction checks PASS because nothing is there to leak." Empty evidence is never PASS. Mark SKIP with the reason and tell the user the smoke probably didn't generate network traffic.
- "The payload has no `user_steps` key at all — I'll FAIL the user-steps checks." Could be workspace policy (`user_steps` disabled by dashboard). Mark `DISABLED` with reason `"workspace policy: user_steps disabled"` rather than FAIL. Same logic for any feature whose entire payload key is absent rather than empty — absence-of-key often means "feature off at workspace level," empty-array means "feature on, no data captured this run."
- "The non-2xx response bodies are not redacted but C3b says response bodies should be redacted." C3b excludes failed responses by design — error bodies are intentionally captured for diagnostics. Re-read the rule.
- "`state.fields.user_attributes` is `{}` so the customer's integration is broken." Maybe — but `{}` could also mean the harness didn't call `setTestPersona()`, or the customer's app doesn't set user attributes for this code path. FAIL only when `attributes.user.required` is non-empty AND a required key is missing AND the harness was supposed to set it.
- "I see strings that look like emails in user steps — I'll auto-add an email regex to the PII rule pack." PII regexes require user approval. Auto-additions create permanent false alarms.
- "I'll auto-infer the custom-attribute slot mapping from observed traffic." Slot config is org-wide dashboard configuration, not telemetry. A wrong mapping creates permanent silent false positives. Prompt the user.

**Tool-call errors**
- "APM returned a 4xx with `{error: ...}` — I'll raise and STOP." The MCP forwards 4xx and 501 as a tool response body. Inspect the JSON, mark APM-dependent checks SKIP, continue. Stop only on 5xx (raised as `StandardError`).
- "APM tools returned 'tool not found' — I'll FAIL the network checks." Missing tools is SKIP with reason "apm tools unavailable on this account," fallback to bug / crash channel.
- "`crash_patterns` returned `MCP error -32603: Internal error` — STOP and bail." It's observably flaky. Retry once. On repeated failure, mark the prod-canary regression-diff step SKIP and continue. Don't infer "no regression" from a tool failure.

**Workflow shortcuts**
- "The harness occurrence didn't land within the poll window, but there's an older occurrence from yesterday — I'll audit that one." Recency exists so the audit verifies *this build's* behavior. Surface the timeout.
- "Drift detection found a new attribute key — I'll add it to the rule pack and commit." Never auto-commit rule-pack changes. Propose the diff; commit only on user approval.
- "The customer's rule pack disagrees with the base pack on a header name — I'll trust the base pack." Wrong. Customer overrides always win — they're authoritative for the integration.
- "Synthetic mode is failing because the user only has prod telemetry — I'll silently switch to prod canary." Mode switches are user-invoked. Recommend explicitly; wait for the flag.
- "I'll add the harness deep-link intent filter to the main `AndroidManifest.xml` because the debug merge wasn't working." The intent filter must live in `src/debug/`. A release-variant deep link is a remote-crash vector.
- "I'll generate the harness file into `src/main/` because the project has no debug source set yet." Stop, tell the user their project needs a debug source set, let them create it. The skill does not invent build-variant separation.
- "The verification report says PASS but one rule is INFO with a slightly off value — close enough." INFO is not PASS. If the rule should assert, change its status. Otherwise leave it INFO and don't claim PASS as the summary.
- "I'll only call `reportBugReport()` if the customer opts in — it might pollute their dashboard." The bug record is in staging (per pre-flight) and tagged with the harness marker — that's exactly the kind of synthetic signal the dashboard is meant to receive. Run it every smoke; document the bug number in the report for cleanup.

Every shortcut here trades "looks verified" for "actually verified." The skill's job is to actually verify.
````

</details>

## Prerequisites

Three hard dependencies. The skill refuses to run without all of them.

| Dependency                                 | What for                                                                                                                                                                                                    | If missing                                                                                                                                                                                                                                                     |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Luciq MCP server, authenticated**        | The entire audit is grounded in what the MCP exposes — `list_applications`, `list_crashes`, `list_bugs`, `list_occurrences_tokens`, `get_occurrence_details`, `bug_details`, `crash_patterns`, and `apm_*`. | Stop at pre-flight. Route to [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup) step 7 or the [MCP setup guide](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide). |
| **A debug-variant build with the new SDK** | Produces the deterministic occurrence the audit reads.                                                                                                                                                      | Phase 1 generates the harness.                                                                                                                                                                                                                                 |
| **A device, simulator, or emulator**       | Runs the build that produces the occurrence.                                                                                                                                                                | Boot one before invoking. The skill will not spawn a device without confirmation.                                                                                                                                                                              |

{% hint style="info" %}
The skill also works with the optional [mobile-mcp](https://github.com/mobile-next/mobile-mcp) server. When present, it drives the smoke using the device's accessibility tree (`tap_by_label`) and captures diagnostic screenshots on smoke end or timeout. Without it, `tap_by_label` triggers degrade to `manual` — the skill prints the trigger sequence and waits for the user to tap.
{% endhint %}

## How the agent verifies

The skill runs a linear six-phase workflow. Every phase is tracked in a progress checklist. The agent stops on any failed step — a misleading "PASS" report is worse than no report.

### Phase 0. Detect maturity tier

Before doing anything, the agent reads the project locally and probes MCP to classify which tier applies. Tiers determine how the rest of the workflow runs:

| Tier                    | Marker                                                                              | What runs                                                                     |
| ----------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **T3 (full)**           | Harness present + `luciq-verify.yaml` exists                                        | Full synthetic audit with customer-specific rule pack                         |
| **T2 (harness only)**   | Harness present, no rule pack                                                       | Synthetic audit against the base rule pack only                               |
| **T1 (telemetry only)** | No harness, but MCP returns ≥ 1 record from the new SDK version in the last 30 days | Audit the most recent organic occurrence; synthetic-marker checks become SKIP |
| **T0 (empty)**          | Neither                                                                             | Cannot audit — run Phase 1, produce one occurrence, re-invoke                 |

The agent surfaces the detected tier explicitly. Most users don't know which tier they're in until the agent tells them.

On Flutter (DART) and React Native (JAVASCRIPT) projects, APM is permanently `N/A` — the bug and crash channels carry the full audit on those platforms.

### Phase 1. Setup (idempotent)

First invocation only. On a clean repo this phase produces a single PR-shaped change set scoped to the debug variant. Sub-steps whose artifacts already exist are skipped.

**Platform detection** reuses the same root-marker rules as `luciq-setup`. Ambiguous workspaces stop here.

**Harness scaffold or reuse** — two modes controlled by `harness.mode` in `luciq-verify.yaml`:

* *Scaffold mode* (default): the skill generates `LuciqVerifyHarness.<ext>` inside the debug variant. The harness is ≈ 100–200 lines per platform, reachable by deep link, and gated to the debug variant. The skill shows the diff before applying. Release and production source sets are never touched — a release-variant harness with a public deep link is a security issue.
* *Reuse mode*: for projects that already have a debug menu with crash/hang/bug triggers. The skill drives the existing surface instead of generating one. A `marker_view`, optional deep link, and trigger map are declared in the rule pack. Unmapped triggers become no-ops; the audit degrades gracefully.

**Rule pack bootstrap** — the skill writes `luciq-verify.yaml` at the repo root with base rules inlined and TODO stubs for customer-specific checks. If MCP returns ≥ 10 occurrences from the baseline SDK version, the skill proposes an inferred draft (committed only on user approval). PII regexes and attribute slot mappings are never auto-committed.

### Phase 2. Static audit

Inspects source and build config without running the app. Catches integration issues that never surface at runtime: wrong SDK version pinned, modules disabled in code, masking off, dSYM/mapping upload not wired.

The audit is agent-native — the skill reads files and greps patterns using the agent's own tools. No external scanner is needed. Per-platform extractors cover:

* SDK install and version detection
* Module activation (Bug Reporting, Crash Reporting, APM, Session Replay, NDK, Surveys, network auto-masking)
* Invocation events configuration
* User identification and attribute hooks
* Feature flag API usage
* Masking and privacy configuration
* dSYM / mapping upload setup

Each finding produces a `S-*` coded row (status, file path + line range, remediation hint). `FAIL` findings block release the same way runtime `FAIL` findings do.

Skipped when invoked with `--runtime`.

### Phase 3. Pre-flight safety checks

Runs on every invocation. Refuses to verify the wrong thing — a "PASS" from a build still on the old SDK, or pointing at production, is worse than no report.

| Check                                    | Stop condition                         |
| ---------------------------------------- | -------------------------------------- |
| New SDK version in lockfile              | Lockfile still pins the old version    |
| Build variant is debug                   | Active variant is release / production |
| Backend is non-production                | Build points at a production API       |
| Dashboard mode matches build environment | Build env and MCP `mode` mismatch      |
| Device or emulator available             | Nothing booted or connected            |
| **Luciq MCP reachable**                  | Auth expired or MCP not configured     |

The production-backend refusal is not overridable in default mode. See Modes below for the explicit `prod-canary` path.

Skipped when invoked with `--static`.

### Phase 4. Smoke

The skill drives the harness end to end. No manual input unless the user explicitly prefers it.

Platform-native commands (Xcode, Gradle, Flutter, React Native) install and launch the debug build via deep link. If [mobile-mcp](https://github.com/mobile-next/mobile-mcp) is available, it can serve as a unified driver across iOS and Android.

The canonical trigger sequence runs in this order — attributes before network traffic, bug report before crash:

1. `LuciqVerifyHarness.setTestPersona(...)` — sets user attributes
2. `LuciqVerifyHarness.fireNetworkBurst(...)` — generates network log evidence
3. `LuciqVerifyHarness.exerciseFeatureFlags()` — iterates declared flags and experiments
4. `LuciqVerifyHarness.reportBugReport()` — produces a clean bug-channel record
5. `LuciqVerifyHarness.flushNow()` — ships pending telemetry synchronously
6. `LuciqVerifyHarness.forceCrash()` — produces the auditable crash

After triggering, the skill polls all three MCP channels simultaneously (crash, bug, APM) every 5 seconds for up to 90 seconds. Any channel that returns data is used for the audit. Full timeout triggers diagnostics; the skill does not silently continue.

When selecting among multiple crash tokens returned by `list_occurrences_tokens`, the skill takes `max(tokens)` lexicographically — ULIDs are time-prefixed, so the lex-max is the newest occurrence.

Skipped when invoked with `--static`.

### Phase 5. Runtime audit

Applies every rule in the merged rule pack (base + customer rules) against the captured payload. Channel preference: APM > Bug > Crash for behavioral checks — APM exposes per-request structured data; the bug payload splits logs into typed archives; the crash payload requires decompressing a combined archive.

**Status values:**

| Status     | Meaning                                                    |
| ---------- | ---------------------------------------------------------- |
| `PASS`     | Evidence matches the expected contract                     |
| `FAIL`     | Regression detected — blocks release                       |
| `WARN`     | Potential issue worth reviewing                            |
| `SKIP`     | Evidence unavailable; reason stated                        |
| `DISABLED` | Feature intentionally off (workspace policy or rule pack)  |
| `N/A`      | Rule doesn't apply to this platform or channel             |
| `MANUAL`   | Requires human judgment; surfaced at the top of the report |

Empty evidence is never `PASS`. A missing field or empty array is `SKIP` with a stated reason — silently passing absent data is the exact failure mode this skill exists to catch. `DISABLED` (feature intentionally off) is not `FAIL`; the skill distinguishes between a feature that's absent because of a regression and one that's absent because it was configured off.

When default mode runs both static and runtime, Phase 2's `S-MODULE-*` findings gate Phase 5 rule evaluation: a module disabled in source causes all dependent runtime rules to emit `SKIP` with a traceable cross-link.

### Phase 6. Report and drift detection

Two artifacts are always produced:

* `luciq-verify-report.html` — colored status pills, expandable evidence rows, network audit table, occurrence list
* `luciq-verify-report.md` — same content in plain Markdown, suitable for PR comments and CI logs

Both include a summary bar (counts per status), test environment block (slug, mode, app version, backend, SDK version), selected occurrence detail (type, number, ULID, `current_view`), APM coverage when available, the full checks table, network log audit, user attributes, and experiments.

Drift detection always runs afterward. The agent compares the observed payload to the declared rule pack and produces a unified diff proposing updates. The user accepts, rejects, or edits per hunk. `luciq-verify.yaml` is never auto-updated.

## Invocation flags

Flags trim scope when only part of the audit is needed. They compose with audit mode.

| Flag               | Phases run          | When to use                                                             |
| ------------------ | ------------------- | ----------------------------------------------------------------------- |
| *(none — default)* | 0, 1, 2, 3, 4, 5, 6 | Full audit: static config inspection + runtime smoke + MCP-driven audit |
| `--static`         | 0, 1, 2, 6          | Static config inspection only; no device or MCP call needed             |
| `--runtime`        | 0, 1, 3, 4, 5, 6    | Skip static phase; go straight to pre-flight and smoke                  |

`--static --mode=prod-canary` is an error — static doesn't read telemetry, so the mode flag has nothing to apply to. The skill surfaces the conflict and stops.

## Modes

| Mode                                   | When                                                                   | What changes                                                                                                                                                                                                                             |
| -------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **synthetic** (default)                | Pre-release SDK upgrade verification                                   | Smoke phase runs; harness produces a fresh occurrence; MCP `mode` is whatever non-production value matches the build's backend                                                                                                           |
| **prod-canary** (`--mode=prod-canary`) | Day-1 of a staged rollout; auditing real-user traffic from the new SDK | Smoke phase skipped; audits the most recent occurrence from `mode: production`; `S*` rules SKIP; recency window is 24h; PII findings are release-blocking. Adds SDK-version regression diffs via `crash_patterns` and `apm_list_groups`. |

Prod-canary mode is invoked explicitly. The production-backend pre-flight refusal is intentionally inverted in this mode — production is the target. All other pre-flight checks still apply.

## Out of scope

* Aggregate metrics that are dashboard-only (crash-free session rate, MTTR, retention). The audit is per-occurrence behavioral.
* Computing "is this string PII" by LLM judgment alone. Customer PII regex is the source of truth; the skill suggests candidates only.
* Real UI tests (Espresso, XCUITest). The smoke is a single deep-link launch plus the canonical trigger sequence. Broader scenario coverage belongs in customer-owned tests.
* Modifying the customer's actual integration code. The skill edits the debug variant's harness and the rule pack only.
* Verifying against `mode: production` without explicit `--mode=prod-canary`.


# luciq-readout

The luciq-readout skill builds a shareable app health readout tailored to your audience, with every figure cited to its Luciq MCP source.

Produce a shareable, audience-tailored readout of a Luciq app's health and quality. The mechanism is rendering the same data at the right altitude: pull headline aggregates from `app_insights` (stability rates and the APM performance section), slice detail across crashes, hangs, bugs, reviews, and NPS/CSAT surveys via the Luciq MCP, drill into per-occurrence stacktraces and diagnostics for the EM tier, then compose a report tuned to one persona's question.

The skill exists to prevent the failure mode of generic reporting: a dashboard dump nobody reads, a crash-free percentage quoted from a section that returned an error, a stacktrace pasted into a C-suite summary. Every number is cited to the MCP tool and the parameters that produced it. If a query returns nothing or a section comes back as an error, that fact is surfaced — never filled in.

## Use this skill when

* The user asks for an exec or leadership summary, a release readout, a stability or quality report.
* The user asks for a "how is the app doing this week vs last" rollup or a period-over-period comparison.
* The user asks to compare version X to version Y for a specific audience.
* The user needs a C-suite, VP, PM, EM, or QA-tier rendering of Luciq app health data.

## What the agent does

`luciq-readout` turns the agent into an audience-calibrated reporting tool:

* **Persona altitude.** Reads the requested persona (C-suite, VP, PM, EM, or QA) and sets the lead metric, drill-down depth, and omit-list accordingly. If unspecified, asks — the wrong altitude is the most common way a readout fails.
* **MCP tool sequencing.** Calls `app_insights` for the headline stability and APM aggregate, `list_crashes` and `crash_patterns` for issue distribution, `list_app_hangs` for hang volume, `list_bugs` for user-reported issues, `list_reviews` for store sentiment, and `list_surveys` plus `survey_details` for NPS/CSAT data.
* **EM deep-dive chain.** For EM-tier readouts, drives the per-occurrence chain: `crash_details` → `list_occurrences_tokens` → `get_occurrence_details` (one session's device context) → `crash_diagnostics` (aggregated patterns and distributions).
* **Comparison integrity.** Enforces matched time windows, rates over raw counts, and low-sample labeling before stating any delta.
* **Evidence citation.** Every number in the rendered readout carries a citation naming the Luciq MCP tool and the parameters that produced it.
* **Dual rendering.** Produces both an HTML artifact (executive-ready, shareable) and Markdown (inline preview).

## 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/luciqai/luciq-docs/blob/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-readout`.

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-readout/SKILL.md) from the public repo to `~/.claude/skills/luciq-readout/SKILL.md` (user-global) or `.claude/skills/luciq-readout/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-readout
description: Produce a shareable, audience-tailored readout of a Luciq app's health and quality, not a single fix or ship decision. Covers version-vs-version and period-over-period comparisons rendered for a chosen persona: C-suite, VP, PM, EM, or QA. Use whenever the user asks for an exec or leadership summary, a release readout, a stability or quality report, a "how is the app doing this week vs last" rollup, or a "compare version X to version Y for a PM" comparison. Pulls headline aggregates (stability, APM performance) from app_insights and slices detail across crashes, hangs, bugs, reviews, and NPS/CSAT surveys via the Luciq MCP, drilling into per-occurrence stacktraces and diagnostics for the EM tier, then renders the same data at the altitude each audience needs, citing every number to the tool and params that produced it. For root-causing one crash, hang, or bug use luciq-debug; for verifying an SDK upgrade before shipping use luciq-verify; for first-time SDK integration use luciq-setup.
---

# Luciq Stakeholder Readouts

Produce a shareable, audience-tailored readout of a Luciq app's health and quality. The mechanism is **render the same data at the right altitude**: pull headline aggregates from `app_insights` (stability rates plus the APM performance section), slice detail across crashes, hangs, bugs, reviews, and NPS/CSAT surveys, drill into per-occurrence stacktraces and diagnostics for the EM tier, then compose a report tuned to one persona's question. The skill catches the failure mode of generic reporting — a dashboard dump that nobody reads, a stacktrace pasted into a C-suite summary, a crash-free percentage quoted from a section that actually returned an error. None of those are wrong on their face. All of them produce a readout that looks authoritative and misleads.

The job of this skill is communication, not adjudication. Every number is cited to the MCP tool and the parameters that produced it. If a query returns nothing, or a section comes back as an error, that fact is surfaced — never filled in with a plausible-looking number. **A readout's only value is that the reader can trust every figure in it.** A fabricated number destroys that for the whole document.

## When NOT to use this skill

- Root-causing one specific crash, hang, or bug and proposing a code fix, use `luciq-debug`. That skill maps a single occurrence to local source and forms an evidence-cited hypothesis. This one summarizes many occurrences for an audience.
- Verifying that an SDK version upgrade did not break the integration before shipping, use `luciq-verify`. That skill audits a synthetic smoke against a contract. This one reports production health.
- First-time integration of Luciq into a project, use `luciq-setup`.
- General reporting 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.

If the request fits any of the above, route there and stop. Composing a readout when the user wanted a fix, a verdict, or an investigation wastes the audience's trust on the wrong artifact.

## Prerequisites

### Hard dependencies (skill refuses to run without these)

| Artifact | What for | If missing |
| --- | --- | --- |
| **Luciq MCP server, authenticated** | The entire readout is grounded in what the Luciq MCP exposes — `list_applications`, `app_insights` (stability + APM), `list_crashes`, `crash_patterns`, `list_app_hangs`, `list_bugs`, `list_reviews`, `list_surveys` / `survey_details` (NPS/CSAT), and the per-occurrence detail tools (`crash_details`, `list_occurrences_tokens`, `get_occurrence_details`, `crash_diagnostics`). Without it the skill has no data to render. | STOP. Direct the user to `luciq-setup`, or to https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide. Do not assemble a readout from memory or from a prior session's numbers — a readout with stale or invented figures is the exact failure this skill exists to prevent. |
| **A resolved app slug and mode** | Every tool keys off `(slug, mode)` | Run `list_applications` and confirm with the user which app and mode (default `production`). |

### Optional inputs (the readout works without these, sharpens with them)

| Input | What it adds | If missing |
| --- | --- | --- |
| **A named persona** | Sets the altitude, the lead metric, the drill-down depth, and the omit-list | If unspecified, ASK. Do not guess the audience — the wrong altitude is the most common way a readout fails. |
| **A comparison frame** | Version-vs-version or period-over-period gives the readout a "what changed" spine | Default to a single snapshot of the current window, and say so. |
| **A segment cut** (OS, device, screen, flag, team, geography) | Answers "where is the problem concentrated" instead of just "how big is it" | Skip; report at the version / period level and note that no segment was requested. |

## Reference files

Detailed material is split out so the SKILL.md stays workflow-focused. Read the relevant reference when the workflow points to it:

| Reference | When to read |
| --- | --- |
| `references/metrics-glossary.md` | Before composing any readout. Defines every MCP tool's response shape, what each metric actually means (crash-free sessions, apdex, the `rate` change field, OOM / ANR / hang), the identifier and enum model, the filter-naming differences across tools, and — critically — exactly which adoption / volume signals the MCP does and does not expose. Field paths and number meanings used in this SKILL.md come from here. |
| `references/persona-playbooks.md` | When composing for a specific persona. Per-persona spec: the lead metric, the exact tool calls that build the readout, the drill-down to include, the omit-list, the framing, and a worked outline for each of C-suite, VP, PM, EM, and QA. |

## Canonical sources of truth

Verify tool surface and number meanings against live sources — the MCP evolves, and shapes memorized here can go stale:

| Concern | Source |
| --- | --- |
| Which apps, slugs, and modes exist | Luciq MCP `list_applications` |
| MCP tool surface and authentication | https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide |
| What a given metric means on the dashboard | The product guides at https://docs.luciq.ai — confirm a metric's definition before framing it for an exec |

## Tool coverage — what the readout uses, and what it deliberately doesn't

The Luciq MCP surface is broad. A readout uses the tools that aggregate or slice many occurrences for an audience, and stays away from the ones that mutate data or root-cause a single issue. This table is the contract.

**Tools the readout USES**

| Tool | Tier(s) it serves | What it contributes |
| --- | --- | --- |
| `list_applications` | all | Resolve `(slug, mode)` at the start. Never hard-code a slug. |
| `app_insights` | all | Headline stability rates (`monitoring`) AND the APM performance section (`apm`: networks, screen loads, cold/hot launches, flows). The single most important call. |
| `list_crashes` | VP, PM, EM, QA | Top issues by users affected / volume / recency, sliced by version, OS, device, screen, flag, team, type, status. |
| `crash_patterns` | VP, EM, QA | Distribution of ONE crash group across versions / OSes / devices / screens / experiments, plus the per-bucket `adoption` signal (on `app_versions`). |
| `list_app_hangs` | PM, EM, QA | Hang / ANR volume and worst offenders, same slicing as crashes. |
| `list_bugs` | PM, VP | User-reported bug volume by priority / status / version (the `app_insights.bugs` section commonly errors; this is the real source). |
| `list_reviews` | C-suite, VP, PM | App Store / Play rating trend and verbatim review bodies — the external quality proxy. |
| `list_surveys` + `survey_details` | C-suite, VP, PM | NPS / CSAT score and verbatim survey feedback — the in-app voice-of-customer signal alongside store reviews. |
| `crash_details` | EM | Synchronous stacktrace (`stack_frames[]`); the application frames are the fix target. |
| `list_occurrences_tokens` | EM | The occurrence ULIDs of one crash group, to address a single session. |
| `get_occurrence_details` | EM | The deepest drill: one session's device metrics, screen flow, memory / battery / storage at crash time. |
| `crash_diagnostics` | EM | Aggregated diagnostics across occurrences — screen-flow patterns, device/OS/version distributions, metric histograms. Async: re-call while `status` is `generating`. |

The EM tier is the one that earns the per-occurrence chain: `list_crashes` (pick the group) → `crash_details` (the stack) → `list_occurrences_tokens` → `get_occurrence_details` (one session) → `crash_diagnostics` (the aggregate pattern). No other tier descends this far; doing so for a VP or C-suite reader violates the omit-list.

**Tools the readout deliberately does NOT use**

| Tool | Why it's out of scope |
| --- | --- |
| `update_bug` | **Write / mutating tool.** A readout is read-only reporting; it must never change a bug's status, priority, or assignment. Never call it — not via the client, not via a direct JSON-RPC call. |
| `bug_details` | Single-bug deep payload (logs, breadcrumbs, repro). The readout reports bug *volume and themes* via `list_bugs`; per-bug depth is `luciq-debug`'s job. (Pull a single "top bug" snippet only if a quick check shows it genuinely adds readout value; default exclude.) |
| `apm_list_groups` / `apm_group_view` / `apm_occurrence` | Per-span APM deep-drill: the slowest-endpoint / slowest-screen list (`apm_list_groups`), one group's full panels — apdex, p50/p95, failure-rate breakdown and trend, dimensions (`apm_group_view`), and a single occurrence's span timeline (`apm_occurrence`). The readout reports performance at the `app_insights.apm` aggregate level; these per-span tools are the deeper drill `luciq-debug` reaches for and are **not the primary performance source** for a readout. They are stripped from the MCP client by the server-side schema bug (see the note below) but are read-only and reachable by a direct JSON-RPC call — use them only as an optional EM/PM-tier drill when a persona genuinely needs span-level depth. |

**A known constraint on four tools.** `update_bug`, `apm_list_groups`, `apm_group_view`, and `apm_occurrence` carry a top-level `anyOf` / `allOf` / `oneOf` / `not` combinator in their server-side `inputSchema` (the `apm_*` tools gate per-`metric` options with `allOf` + `oneOf`; `update_bug` uses `anyOf` + `not`). The Anthropic Messages API forbids a top-level combinator on a tool's input schema, so Claude Code strips these four from the in-session tool list — they are not callable via the normal MCP client. This is a known server-side schema limitation (the combinators should be pushed below the top level); a readout does not try to fix it. For `update_bug` the strip is irrelevant — a readout would never call a write tool anyway. For the three `apm_*` tools, the readout's performance dimension comes from the `app_insights.apm` aggregate, which is fully callable. **If, and only if, a persona genuinely needs per-span APM detail**, all three read-only `apm_*` tools can be reached by a direct JSON-RPC `tools/call` against the MCP HTTP endpoint (method `tools/call`, params `{name, arguments}`), using the same auth headers the MCP client uses — this bypasses the client-side schema strip without touching the server. This path is confirmed working against live data for all three: `apm_list_groups` (the group list), `apm_group_view` (`views` like `summary` / `failure_rate` / `dimensions`), and `apm_occurrence` (`selector` `worst` / `list` / `by_token`, returning the per-stage span timeline). Treat it as an optional deeper drill, label any figure in the readout as sourced via a direct call, and never use the same path to reach a write tool. If a particular query errors server-side or you can't authenticate, fall back to the `app_insights.apm` aggregate and say so — never fabricate a per-span number.

## Workflow checklist

Track every step. Stop and ask rather than guess on persona or comparison frame — a readout at the wrong altitude, or one that compares mismatched windows, is worse than no readout.

```
Readout Progress:
- [ ] 1. Resolve the app and mode (list_applications)
- [ ] 2. Confirm the audience and the angle (persona, comparison frame, segment cut)
- [ ] 3. Pull headline aggregates incl. APM (app_insights, once per version/window in scope)
- [ ] 4. Slice the detail for the chosen angle (list_/pattern tools; surveys for voice-of-customer)
- [ ] 4b. EM tier only: drive the per-occurrence chain (crash_details -> tokens -> occurrence -> diagnostics)
- [ ] 5. Apply the comparison rules before stating any delta
- [ ] 6. Compose for the persona (lead metric, drill-down, omit-list)
- [ ] 7. Render HTML + Markdown, cite every number to its tool and params
```

## 1. Resolve the app and mode

Call `list_applications` to get the slug. Filter by `platform` if the user named a stack. Confirm the `mode` with the user; default to `production`. An app can have several modes (`production`, `beta`, `staging`, `alpha`, `qa`, `development`) — each is a separate dataset. Never assume; a readout on `beta` data presented as production is a silent error.

## 2. Confirm the audience and the angle

Resolve three things before pulling any data. Ask if any is unspecified — do not guess.

- **Persona.** C-suite, VP (Eng or Product), PM, EM, or QA. This sets the lead metric, the drill-down, and the omit-list. Spec per persona is in `references/persona-playbooks.md`.
- **Comparison frame.** Version-vs-version, period-over-period, or a single snapshot. This decides how many `app_insights` calls you make and which deltas you can honestly state.
- **Segment cut (optional).** A device tier, OS, screen (`current_views`), feature flag / experiment, owning team, or geography (reviews `country`). Pick the one cut that answers the persona's question; do not run every cut by reflex.

## 3. Pull headline aggregates

`app_insights` is the headline source. It returns four independent sections — `crashes`, `bugs`, `apm` (networks, screen loads, cold / hot launches, flows), and `monitoring` (crash-free sessions, ANR, OOM, app hangs, user termination). Call it once per version or window in scope.

- For a version comparison, call once per version via `filters.app_version: ["<version>"]`.
- For a period comparison, call once per window via `filters.date_ms: {gte, lte}`.
- **Each section is independent and may carry an `error` object instead of data.** When a section errors, record it as unavailable for that scope. Do NOT reconstruct it from another tool and present it as the same metric — a count assembled from `list_crashes` is a volume, not the `monitoring` crash-free rate. (In live testing the `bugs` section of `app_insights` frequently returns `{"error": ...}` while `list_bugs` returns data fine; the two are not interchangeable. See `references/metrics-glossary.md`.)
- A section can also be present-but-empty (`has_occurrences: false`, `data: null`). That means "no data captured," not "zero is good." Report it as no-data, not as a healthy zero.

Read the field meanings in `references/metrics-glossary.md` before quoting any of these numbers. `crash_free_sessions.value` is a percentage; its sibling `rate` is the period-over-period change, not the rate itself — misreading that pair is an easy way to invert a trend.

**The `apm` section is a real readout dimension, not just context.** It returns network apdex and failure counts, screen-load p95 latency, cold / hot launch apdex and p95, and flow drop-off counts — performance signals an EM, a VP, or a PM cares about (slow screens, failing endpoints, sluggish launches, leaky funnels). Surface it for those tiers; keep it out of a C-suite one-number summary. This `apm` section is the performance layer the readout uses, and on its own it covers the whole performance dimension. The per-span APM tools (`apm_list_groups` / `apm_group_view` / `apm_occurrence`) drill one slow screen or endpoint down to a span timeline — that's deeper than a readout usually needs and is `luciq-debug`'s territory. They are also stripped from the MCP client by the top-level-combinator limitation noted in the tool-coverage section, so they are not callable inline; when a persona genuinely needs span-level depth, all three read-only tools can be reached by a direct JSON-RPC `tools/call` to the MCP HTTP endpoint (the slowest-endpoint / slowest-screen list from `apm_list_groups`, one group's apdex / p95 / failure-rate panels from `apm_group_view`, one occurrence's per-stage spans from `apm_occurrence`) and the result labeled as sourced that way. When you don't take that path, the `app_insights.apm` aggregate still gives you the whole performance dimension; say so rather than implying span-level detail you didn't pull.

## 4. Slice the detail for the chosen angle

The headline numbers tell you how big; the list and pattern tools tell you where and what. Reach for the cut the persona and question call for.

| Tool | Pivot on | Angle it unlocks |
| --- | --- | --- |
| `list_crashes` | `app_versions[]`, `os_versions[]`, `devices[]`, `current_views[]`, `feature_flags[]`, `platform[]` (UPPERCASE: `IOS` / `ANDROID` / `DART` / `JAVASCRIPT`), `type[]` (CRASH / ANR / OOM / NON_FATAL), `subtype[]`, `status_id[]`, `teams[]`; `sort_by` (`occurrences_counter`, `affected_users_counter`, `severity`, `first_occurred_at`, `last_occurred_at`) | Top issues by version, OS, device, screen, flag, owning team, severity, status, or type. Sort by `affected_users_counter` for blast radius, `occurrences_counter` for volume, `first_occurred_at` to surface new-in-version. |
| `crash_patterns` | `number` (the crash group), `pattern_key` (`app_versions`, `devices`, `oses`, `current_views`, `app_status`, `experiments`), filters, `sort_by` | Distribution of ONE crash group across versions, devices, OSes, screens, foreground/background, or experiments. The experiment breakdown attributes a regression to a rollout or flag. This tool also returns per-bucket `adoption` and a `total_sessions_count` — see the adoption note below. |
| `list_app_hangs` | same filter surface as crashes (no `type` / `subtype`) | Hang / ANR volume and worst offenders, sliced the same ways. `crash_type` on hang rows reads `FATAL_UI_HANG`. |
| `list_bugs` | `app_version[]`, `priority_id[]` (`-1` N/A, `1` Trivial … `4` Blocker), `status_id[]` (`1` New / `2` Closed / `3` In Progress) | User-reported bug volume by priority, status, version. |
| `list_reviews` | `rating[]` (1-5), `app_version[]`, `country[]`, `os[]`, `prompt_type[]` (`custom` / `native` / `app_store`), `date_ms`; `sort_direction` | The external quality proxy execs ask about. Slice by low rating, version, country, OS, or prompt type. Review bodies are real user words — quote them verbatim for a PM, never paraphrase into a claim. |
| `list_surveys` + `survey_details` | `list_surveys` filters by `status` / `type` (`1` = nps); `survey_details` keys off the survey `id`, filters responses by rating / version / country / `nps` score / `search_words` | The in-app voice-of-customer signal. `list_surveys` finds the published NPS/CSAT survey; `survey_details` returns the headline `nps` object (`score`, promoter / passive / detractor splits) and individual verbatim responses. NPS score is a fair C-suite / VP headline; the verbatim "how can we do better" answers are PM-tier evidence — quote them, don't summarize. If no survey exists or it's empty, say so; don't imply a score. |
| `crash_details` / `list_occurrences_tokens` / `get_occurrence_details` / `crash_diagnostics` | the `(slug, mode, number)` tuple; `get_occurrence_details` also takes a `ulid` from `list_occurrences_tokens` | Per-issue depth (stacktrace, single-session device metrics, aggregated screen flows and distributions) for the EM tier only. This is the chain in step 4b. `crash_diagnostics` is async — re-call while `status` is `generating`. |

**The adoption nuance — get this right.** `app_insights` does NOT return adoption or rollout percentage; you cannot normalize its headline rates by exposure. But `crash_patterns` DOES return an `adoption` value per bucket (confirmed on the `app_versions` pattern key — a given pattern key, e.g. `oses`, may omit it) plus `total_sessions_count` and `total_occurrences_count` for the group. Use those where the tool gives them, and cite them to `crash_patterns`. Do not invent an adoption figure for a version where you only have `app_insights`, and do not present a `crash_patterns` session/adoption count as if it covered the whole app. Detail and exact field paths in `references/metrics-glossary.md`.

## 4b. EM tier only — drive the per-occurrence chain

For an EM readout, descend from the crash *group* to a single crash *occurrence* and the aggregated diagnostics. No other persona goes this deep. The chain:

1. `list_crashes(..., sort_by=affected_users_counter)` — pick the top crash group; note its `number`.
2. `crash_details(slug, mode, number)` — the synchronous stacktrace. Lead the EM with the **application** frames (`type: application`), especially the one flagged `is_grouping_frame`, and the deepest application frame before the system frames (the fix target). Drop the system frames from the readout.
3. `list_occurrences_tokens(slug, mode, number)` — the ULID tokens for individual occurrences (ULIDs are time-prefixed; the lexicographically largest is the newest). Note `total_occurrences`.
4. `get_occurrence_details(slug, mode, number, ulid)` — one session's full payload: device, OS, app version, memory / storage, foreground/background, session duration, the screen at crash time. One concrete repro context an engineer can act on.
5. `crash_diagnostics(slug, mode, number)` — the aggregate across occurrences: screen-flow `patterns` (the navigation path users were on), `distributions` (devices / OS / app versions / current views / app status), and `metrics` histograms (memory / battery / storage / duration). Async — if `status: generating`, re-call after `retry_after_seconds`.

This is the EM tier's evidence: the stack to fix, one real session to reproduce, and the distribution to scope blast radius. Keep all of it out of the C-suite, VP, PM, and QA readouts — a stack frame at the wrong altitude is noise.

## 5. Apply the comparison rules

Before stating any delta, hold to these. They are what keep a comparison honest.

- **Match the windows.** Use the same `date_ms` length for both sides. Crash-free percentages and raw volumes both scale with exposure time, so unequal windows mislead. Never compare a 7-day window to a 30-day window and call the difference a regression.
- **Prefer rates over raw where the tool gives them.** The `monitoring` rates from `app_insights` compare cleanly across versions. Raw list counts depend on traffic volume, so frame them as volume, not as a like-for-like rate. When you do have per-version session counts from `crash_patterns`, you can normalize a count — and you must cite that you did.
- **Label confidence by sample.** Mark a comparison low-confidence when a version's totals are small or its rollout is clearly early. Say "early rollout, low sample" rather than implying parity. `crash_patterns` session counts per version are the honest signal for how thin a version's exposure is.
- **Separate new from regressed from trending.** New-in-version (sort `first_occurred_at`, or absent from the baseline list), returned-after-fix, and accelerating-in-rate are three different stories that drive three different actions. Label them distinctly.
- **Segment before concluding.** A flat version-level delta can hide a device- or OS-specific regression. Pivot with `crash_patterns` or the filter surface before declaring a release healthy.
- **Benchmarks are context, not measurement.** If you cite an industry stability benchmark, present it as an external observation to validate, never as a Luciq-measured fact.

## 6. Compose for the persona

Lead with that persona's headline metric, include only their drill-down, honor their omit-list. The full per-persona spec — lead metric, tool calls, drill-down, omit-list, framing, worked outline — is in `references/persona-playbooks.md`. Summary:

| Persona | Leads on | Drill-down included | Omit / suppress | Framing |
| --- | --- | --- | --- | --- |
| C-suite (CEO / CPO / CTO) | One stability headline (crash-free sessions %), direction vs last period, App Store rating trend, NPS score, one-line top risk and its blast radius, context vs a benchmark | none inline | stacktraces, device / OS matrices, raw counts, issue IDs, APM apdex detail, tool names in the prose | Short narrative, a few trended numbers, tied to user and revenue impact |
| VP Eng / VP Product | Crash-free sessions and users across recent versions, ANR / OOM / hang rates, key APM rates (network apdex, launch / screen p95), regression flags by version, top issues by users affected with owning team, NPS trend, per-platform split | top-issue list (titles, not traces), an APM panel | full traces, device long tail, per-developer metrics | Comparative across versions and squads, summarized panels |
| Product Manager | Crash-free for the owned flow (`current_views` filter), flow drop-off and slow-screen APM for those screens, top issues by users affected on key flows, review themes, NPS verbatim feedback, new-in-version issues | issue list plus representative review and survey quotes | symbolicated traces, infra detail | Flow and user centric, tied to journeys |
| Engineering Manager | New and trending issues in their components / teams (`teams` filter), crash / ANR / OOM rates with deltas vs prior version, top crashes by frequency and by users, device / OS breakdown, per-occurrence stack and diagnostics | full stacktraces (`crash_details` / `crash_diagnostics`), occurrence detail (the step-4b chain) | exec and revenue narrative | Granular, triage-oriented; raw numbers are fine |
| QA / Release | Crash-free and ANR / OOM / hang vs the team's own ship thresholds, APM rates vs thresholds, new-in-version issues, regression count vs baseline, worst offenders | issue list plus repro context | long-term business trends | Threshold readout. This skill reports the data; it does not emit the ship / hold / rollback verdict — that decision stays with the release owner. |

The omit-list is load-bearing. A readout at the wrong altitude is a worse readout than a short one. Do not paste a stacktrace into a C-suite summary to look thorough, and do not strip the traces out of an EM readout to look clean.

## 7. Render HTML + Markdown

Render the readout in both HTML (the shareable, forwardable artifact) and Markdown (the inline preview). Structure, adapted to the persona's altitude:

```
<App> health readout — <persona>
Scope: <version X vs Y | period A vs B | snapshot>, mode=<mode>, window=<window>

HEADLINE
- <the one-or-few numbers this persona leads on>   [from: luciq:app_insights ...]

WHAT CHANGED
- <deltas, labeled new / regressed / trending, with confidence>   [from: ...]

PERFORMANCE   (VP / PM / EM / QA; omit for C-suite)
- <network apdex + failure rate, launch / screen p95, flow drop-off>   [from: luciq:app_insights apm ...]

TOP ISSUES   (depth set by persona; omit for C-suite)
- <issue, users affected, version, owning team>   [from: luciq:list_crashes sort=affected_users_counter ...]

EM DEEP DIVE   (EM tier only)
- <top crash: application stack frames, one occurrence's context, diagnostics distribution>   [from: luciq:crash_details / get_occurrence_details / crash_diagnostics number=<n>]

VOICE OF USER   (when relevant)
- <rating trend, verbatim review themes>   [from: luciq:list_reviews ...]
- <NPS score + splits, verbatim survey feedback>   [from: luciq:survey_details id=<id>]

CONTEXT
- <vs prior period, vs benchmark — observation, to validate>

CAVEATS
- <unavailable app_insights sections, low-sample comparisons, what adoption signal exists and what doesn't>
```

Every line that carries a number carries a citation naming the tool and the params that produced it, for example `[from: luciq:app_insights version=3.1.4 mode=production]` or `[from: luciq:list_reviews rating=1,2 country=US]`. The HTML should be clean and executive-ready: brand blue `#0A89FC`, a compact KPI band up top, generous whitespace, and a visible source footer per figure or per section. Anonymize any identifying app name if the artifact is meant to be shared outside the owning team.

## Out of scope

Grounded in what the Luciq MCP exposes today. This skill deliberately does not:

- **Mutate any data.** A readout is read-only. It never calls `update_bug` or any other write tool to change a bug's status, priority, or assignment — reporting must never alter the customer's data it reports on.
- Emit a ship, hold, or rollback verdict. Report the data; the decision stays with the release owner. A QA / Release readout lays the thresholds and the numbers side by side, but does not write "ship" or "hold."
- Root-cause a single issue. The per-occurrence chain (step 4b) is used to *show an EM the evidence*, not to author a fix. Deep single-issue debugging — including `bug_details` for one bug's logs and breadcrumbs, and the per-span `apm_*` tools for one slow screen or endpoint — is `luciq-debug`'s job. The readout reports bug *volume / themes* via `list_bugs` and performance at the `app_insights.apm` aggregate, not per-bug or per-span depth.
- Compute MTTR, time-to-resolve, or a whole-app adoption / rollout percentage. The MCP does not expose them at the app level. (Per-crash-group session and adoption counts from `crash_patterns` are the one real exception — use those, cite them, and don't generalize them to the whole app.)
- Reconstruct an `app_insights` section that returned an `error`, or a present-but-empty section, and present it as the real metric. Report it as unavailable for that app and window. Same for a survey: if none is published or it has no responses, report no NPS signal rather than implying a score.

When new MCP tools land (app-level adoption, release-comparison endpoints, deeper APM aggregates), this skill grows with them. Until then, if the user asks for one of those, say so plainly.

## Style

- Pick the persona before composing. If unspecified, ask. Do not guess the audience.
- Honor the persona omit-list. Wrong altitude is a worse readout than a short one.
- Cite every number to its tool and the params that produced it. Do not paraphrase a source you did not query.
- Label every comparison's confidence. Low sample and early rollout are caveats, not asterisks to bury.
- Quote review bodies verbatim. A user's words are evidence; a paraphrase is your claim.
- Render in both HTML and Markdown. No fabricated numbers, ever. A missing section is reported, not filled.

## Red Flags — patterns that mean STOP and surface to the user

If you catch yourself reasoning in any of these directions, you are about to ship a readout that looks authoritative and is not. STOP, surface to the user, do not proceed.

**Fabrication and missing data**
- "The exec just wants a number, so I'll quote a crash-free rate even though `app_insights` returned an error for `monitoring`." Report the section as unavailable. A blank is honest; an invented number is not.
- "The `apm` section is `has_occurrences: false`, so I'll report apdex as 1.0 / perfect." Empty is not zero-is-good. Report it as no data captured for that window.
- "I'll round the messy crash-free figure to a clean 99.5% for the exec." Don't fabricate precision in either direction. Cite the real value; round only the display, never the number you reason from.
- "I don't have last period's number handy, so I'll estimate the delta." If you didn't query the baseline window, you don't have a delta. Pull it or say the comparison is unavailable.

**Comparison integrity**
- "The two versions cover different date ranges, but it's close enough." It is not. Match the windows or label the comparison low-confidence.
- "Version-level crash-free barely moved, so the release is clean." Segment first. A device- or OS-specific regression hides inside a flat aggregate.
- "Version B's crash-free is higher, so it's healthier" — when B has a fraction of A's sessions. Early rollout, low sample. Cite the `crash_patterns` session counts and label confidence.
- "Adoption looks fine on the new version, so I'll call it healthy." `app_insights` gives you no app-level adoption. Don't imply it. The only adoption signal is per-crash-group from `crash_patterns`, and it doesn't cover the whole app.

**Altitude and omit-list**
- "This is for a VP, so I'll paste the stacktrace to look thorough." Wrong altitude. Honor the omit-list. A trace in a VP deck is noise that buries the signal.
- "The C-suite readout looks thin, so I'll add the device/OS matrix to fill the page." A short, true exec readout beats a padded one. Length is not credibility.
- "I'll name the tools (`list_crashes`, `app_insights`) in the C-suite prose so it reads rigorous." Keep tool names in the source footer, not the exec narrative. Cite without cluttering.

**Scope and routing**
- "I'll flip this bug to In Progress / reassign it while I'm reporting on it." STOP. A readout is read-only. `update_bug` and every write tool are off-limits — never mutate the data you're reporting on.
- "The NPS survey has no responses, so I'll estimate a score from the review ratings." Reviews and NPS are different instruments. No survey responses means no NPS signal — say so; don't manufacture one.
- "This is a C-suite summary, so I'll add the network apdex and screen p95 to look complete." APM detail is VP / PM / EM altitude. The exec gets one stability number and a direction, not an apdex table.
- "The data clearly points to ship, so I'll write 'ship it' at the top." That decision isn't this skill's to make. Lay the thresholds and numbers side by side; let the release owner decide.
- "The user pasted a single crash ID and asked why it happens — I'll readout the whole app." That's `luciq-debug`. Route it.
- "Reviews mention a checkout bug, so I'll assert the checkout crash caused the rating drop." Correlation, not causation. Report both signals; don't manufacture the link.
- "I'll cite the number to `app_insights` without noting it was a single small window." If the sample is thin, the caveat is part of the number.

Every shortcut here trades a readout that looks authoritative for one that is actually true. The skill's job is the latter.
````

</details>

## Prerequisites

* The [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) is configured and authenticated. This is a hard dependency — the entire readout is grounded in what the Luciq MCP exposes. Without it the skill will not run.
* A resolved app slug and mode. The skill calls `list_applications` first and confirms with the user which app and mode (default `production`).
* A named persona. The skill asks if unspecified — the wrong altitude produces a readout that is worse than no readout.

{% hint style="info" %}
If the MCP server isn't connected, the skill stops and points the user to [Setup by IDE](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server/setup-by-ide). Do not assemble a readout from memory or from a prior session's numbers — a readout with stale or invented figures is the exact failure this skill exists to prevent.
{% endhint %}

## How the agent builds a readout

The skill follows a seven-step checklist. The agent tracks every step and asks rather than guesses on persona or comparison frame.

### Step 1. Resolve the app and mode

The agent calls `list_applications` to get the slug, filtering by `platform` if the user named a stack. It confirms the `mode` with the user (default: `production`). An app can have several modes — `production`, `beta`, `staging`, `alpha`, `qa`, `development` — each is a separate dataset.

### Step 2. Confirm the audience and the angle

Before pulling any data, the agent resolves three things:

* **Persona.** C-suite, VP (Eng or Product), PM, EM, or QA. This sets the lead metric, the drill-down depth, and the omit-list. The per-persona spec is in the `persona-playbooks` reference file bundled with the skill.
* **Comparison frame.** Version-vs-version, period-over-period, or a single snapshot. This determines how many `app_insights` calls to make and which deltas can be honestly stated.
* **Segment cut (optional).** A device tier, OS, screen (`current_views`), feature flag, owning team, or review country. The agent picks the one cut that answers the persona's question.

If any of these is unspecified, the agent asks. It does not guess the audience.

### Step 3. Pull headline aggregates

`app_insights` is the headline source. It returns four independent sections: `monitoring` (crash-free sessions, ANR, OOM, app hangs), `crashes`, `bugs`, and `apm` (networks, screen loads, cold and hot launches, flows). The agent calls it once per version or window in scope.

Each section is independent and may carry an `error` object instead of data. When a section errors, the agent records it as unavailable — it does not reconstruct it from another tool. A count from `list_crashes` is a volume, not the `monitoring` crash-free rate. An empty section (`has_occurrences: false`) means no data captured for that window, not a healthy zero.

The `apm` section is a real readout dimension for VP, PM, and EM tiers: network apdex and failure counts, screen-load p95, cold and hot launch apdex, and flow drop-off counts. This aggregate covers the full performance dimension for a readout. Per-span APM tools (`apm_list_groups`, `apm_group_view`, `apm_occurrence`) exist for deeper drills but are out of scope for most personas.

### Step 4. Slice the detail

The headline numbers tell you how big; the list and pattern tools tell you where and what.

| Tool                              | What it adds                                                                                                                                          |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list_crashes`                    | Top issues by users affected, volume, or recency — sliced by version, OS, device, screen, feature flag, owning team, type, or status.                 |
| `crash_patterns`                  | Distribution of one crash group across versions, OSes, devices, screens, or experiments — including a per-bucket `adoption` signal on `app_versions`. |
| `list_app_hangs`                  | Hang and ANR volume and worst offenders, same filtering surface as crashes.                                                                           |
| `list_bugs`                       | User-reported bug volume by priority, status, and version. (`list_bugs` is the real source; the `bugs` section of `app_insights` frequently errors.)  |
| `list_reviews`                    | App Store and Play Store rating trend and verbatim review bodies — the external quality proxy.                                                        |
| `list_surveys` + `survey_details` | NPS or CSAT score with promoter/passive/detractor splits, and verbatim "how can we do better" responses — the in-app voice-of-customer signal.        |

**The adoption nuance.** `app_insights` does not return adoption or rollout percentage. `crash_patterns` does return an `adoption` value per bucket (confirmed on `app_versions`) plus `total_sessions_count` for the group. The agent uses those where the tool gives them, cites them to `crash_patterns`, and does not invent an adoption figure for a version where only `app_insights` data is available.

### Step 4b. EM tier only — the per-occurrence chain

For an EM readout, the agent descends from the crash group to a single occurrence and the aggregated diagnostics. No other persona goes this far.

1. `list_crashes` sorted by `affected_users_counter` — pick the top group, note its `number`.
2. `crash_details` — the synchronous stacktrace. Application frames, especially the one flagged `is_grouping_frame`, are the fix target.
3. `list_occurrences_tokens` — the ULID tokens for individual occurrences.
4. `get_occurrence_details` — one session's device, OS, app version, memory, screen at crash time, and foreground/background state.
5. `crash_diagnostics` — aggregated patterns and distributions across occurrences: screen-flow patterns, device and OS distributions, memory and battery histograms. Async — the agent re-calls while `status` is `generating`.

This chain is the EM tier's evidence. None of it belongs in a C-suite, VP, PM, or QA readout.

### Step 5. Apply the comparison rules

Before stating any delta, the agent applies these rules to keep comparisons honest:

* **Match the windows.** Use the same `date_ms` length for both sides. Unequal windows mislead on both rates and raw counts.
* **Prefer rates over raw where the tool gives them.** The `monitoring` rates compare cleanly across versions. Raw list counts depend on traffic volume.
* **Label confidence by sample.** Mark a comparison low-confidence when a version's totals are small. `crash_patterns` session counts are the honest signal for how thin a version's exposure is.
* **Separate new from regressed from trending.** New-in-version, returned-after-fix, and accelerating-in-rate are three different stories that drive three different actions.
* **Segment before concluding.** A flat version-level delta can hide a device- or OS-specific regression. Pivot with `crash_patterns` or the filter surface before declaring a release healthy.

### Step 6. Compose for the persona

The agent leads with the persona's headline metric, includes only their drill-down, and honors their omit-list.

| Persona                   | Leads on                                                                                                                                                  | Drill-down included                                     | Omit                                                                                     |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| C-suite (CEO / CPO / CTO) | One stability headline, direction vs last period, App Store rating trend, NPS score, one-line top risk                                                    | None inline                                             | Stacktraces, device / OS matrices, raw counts, APM apdex detail, tool names in the prose |
| VP Eng / VP Product       | Crash-free sessions and users across versions, ANR / OOM / hang rates, key APM rates, regression flags by version, top issues with owning team, NPS trend | Top-issue list (titles, not traces), an APM panel       | Full traces, device long tail                                                            |
| Product Manager           | Crash-free for the owned flow, flow drop-off and slow-screen APM for those screens, top issues on key flows, review themes, NPS verbatim                  | Issue list plus representative review and survey quotes | Symbolicated traces, infra detail                                                        |
| Engineering Manager       | New and trending issues in their components, crash and ANR rates with deltas, top crashes by frequency and users, device / OS breakdown                   | Full stacktraces, occurrence detail (the step 4b chain) | Exec and revenue narrative                                                               |
| QA / Release              | Crash-free and ANR / OOM / hang vs the team's ship thresholds, APM rates vs thresholds, new-in-version issues, regression count vs baseline               | Issue list plus repro context                           | Long-term business trends                                                                |

The skill reports the data. It does not emit the ship, hold, or rollback verdict — that decision stays with the release owner.

### Step 7. Render HTML + Markdown

The agent renders the readout in both HTML (executive-ready, shareable) and Markdown (inline preview). Each section that carries a number carries a citation naming the tool and the parameters that produced it, for example `[from: luciq:app_insights version=3.1.4 mode=production]`. The HTML uses a compact KPI band, generous whitespace, and a visible source footer per figure or per section.

## Honest about what's out of scope

The skill is grounded in what the Luciq MCP exposes today. It deliberately does not:

* **Mutate any data.** A readout is read-only. `update_bug` and all write tools are off-limits.
* **Emit a ship, hold, or rollback verdict.** The skill lays the thresholds and numbers side by side; the release owner decides.
* **Root-cause a single issue.** Per-occurrence depth (step 4b) shows an EM the evidence; it doesn't author a fix. Deep single-issue debugging is [`luciq-debug`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug)'s job.
* **Compute MTTR, time-to-resolve, or a whole-app adoption percentage.** The MCP doesn't expose them at the app level. Per-crash-group session counts from `crash_patterns` are the one real adoption signal — the skill uses them and cites them.
* **Reconstruct an unavailable `app_insights` section.** If a section returned an error or is present-but-empty, the skill reports it as unavailable. Same for surveys: if none is published or has no responses, there is no NPS signal.

When new MCP tools land — app-level adoption, release-comparison endpoints, deeper APM aggregates — this skill grows with them.


# luciq-alert-config

The luciq-alert-config skill creates, changes, or inspects a single Luciq alert rule from a plain-language request, never guessing values.

Create, change, or inspect a single Luciq alert (rule) from a natural-language request. The work is translating the user's intent into a valid `write_alerts` payload — and the only reliable way to do that is to read the app's `init` catalog first and build strictly from it. The skill never guesses an id, a threshold, or whether a metric is even available.

This skill acts on a specific alert the user describes: create it, change it, or manage its lifecycle.

## Use this skill when

* The user asks to create an alert. *"Alert me when ANR rate goes above 1%"*, *"notify me when..."*, *"set up an alert for..."*.
* The user wants to change an existing alert. *"Change the threshold on my crash-spike alert"*, *"also notify Slack"*.
* The user wants to enable, disable, or delete an alert. *"Turn off this alert"*, *"delete that alert"*.
* The user wants to inspect their alerts. *"Show me my alerts"*, *"what does alert X do"*.
* The user names a metric and a condition to watch: crash-free sessions, ANR, network failure rate, apdex, p95, launch time, or crash spikes.

Not for: reducing alerts that fire too often (use `luciq-alert-noise`), finding monitoring gaps (use `luciq-alert-gaps`), investigating why something crashed or hung (use `luciq-debug`), or first-time SDK install (use `luciq-setup`).

## What the agent does

* **Intent classification.** Sorts the request into create, update, enable/disable, delete, or inspect before doing anything else.
* **Careful spec gathering.** Never creates or updates an alert on an unstated value. If the metric itself is ambiguous (for example, "too many crashes" could mean occurrences, affected users, or a rate), it asks which the user means. If the metric is clear but the threshold is unstated, it either asks for a number or proposes the standard baseline and waits for confirmation before writing anything.
* **Catalog-grounded payloads.** Reads the app's `init` catalog before building any payload, so every type, trigger, condition, action, and lookup ID it sends is one the app's plan and platform actually support.
* **Correct wire-format encoding.** Handles the operator codes, time-window keys, apdex decimal ranges, and percentage formats the `write_alerts` tool expects, so a rule isn't silently malformed.
* **Honest write reporting.** Never reports an alert as created, changed, or deleted until the tool call actually returns success — and surfaces tool errors (like an out-of-range value or a hit plan limit) plainly instead of glossing over them.

## 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](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) in one step.

After install, the skill is available as `/luciq-skills:luciq-alert-config`.

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-alert-config/SKILL.md) from the public repo to `~/.claude/skills/luciq-alert-config/SKILL.md` (user-global) or `.claude/skills/luciq-alert-config/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-alert-config
description: Use when the user wants to create, set up, configure, change, enable, disable, delete, or inspect a specific Luciq alert (rule). Triggers include "alert me when…", "notify me when…", "create/set up an alert for…", "add an alert", "change the threshold on…", "turn off/disable this alert", "delete that alert", "show me my alerts", or naming a metric and a condition to watch (crash-free sessions, ANR, network failure rate, apdex, p95, launch time, crash spikes). Authors a valid alert payload by reading the per-app init catalog first, asking for any missing detail instead of guessing, and surfacing tool rejections honestly.
---

# Luciq Alert Configuration

Create and manage individual Luciq alerts (rules) correctly. The work is translating a
user's natural-language intent into a valid `write_alerts` payload — and the only way to
do that reliably is to read the app's `init` catalog first and build strictly from it.
Never guess an id, a threshold, or whether a metric is even available.

## When NOT to use this skill

- The user wants to **reduce too many / noisy** alerts → `luciq-alert-noise`.
- The user wants to find **missing coverage / what they're not monitoring** → `luciq-alert-gaps`.
- The user is investigating *why* something crashed or hung → `luciq-debug`.
- First-time SDK install / wiring `Luciq.start(...)` → `luciq-setup`.

This skill is for acting on a *specific* alert the user describes — create it, change it,
or manage its lifecycle.

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the alert tools are not
available, STOP and direct the user to set up the MCP server (or run `luciq-setup`).

Tools this skill uses:

| Tool | Action | Purpose |
| --- | --- | --- |
| `read_alerts` | `init` | The per-app catalog: valid rule types, triggers, conditions, actions, operators, time-window keys, and lookup tables (developers/teams/tracking tools/tags). The source of truth for what's possible. |
| `read_alerts` | `list` / `details` | Find or inspect an existing alert (for update / enable / disable / delete). |
| `write_alerts` | `create` / `update` / `delete` | Apply the change. State-changing — confirm intent first. |

## Workflow

### Step 1. Classify the intent

- **Create** a new alert ("alert me when…", "set up an alert for…")
- **Update** an existing alert ("change the threshold to…", "also notify Slack")
- **Enable / disable** ("pause this alert", "turn it back on")
- **Delete** ("remove that alert")
- **Inspect** ("show my alerts", "what does alert X do")

For inspect, call `read_alerts(list/details)` and answer. For the rest, continue.

### Step 2. Gather the spec — never create on an unstated value

A create/update needs: the **metric/trigger**, a **threshold** (for threshold triggers), a
**time window** (where applicable), optional **conditions** (filters), and an **action**
(who/where to notify). Two distinct cases:

- **Metric/trigger is ambiguous** ("alerts about network performance"; "crashes happen too
  much / too many crashes / app is crashing a lot"): vague intensity words don't name a
  single trigger — "too many crashes" could mean occurrences in a time window, affected
  users in a time window, or a % of users/sessions. Ask which the user means before going
  further. Do NOT pick one silently, and do NOT map volume language to a stability *rate*
  like `crash_free_session` (that's a different concept).
- **Metric is clear but the threshold/value is unstated** ("alert me when crash-free
  sessions are low", "the app launch feels slow"): the threshold is a user-specific choice
  — do NOT silently create with a guessed number. Either ask what number they want, or
  **propose the standard baseline and confirm before creating** (e.g. "I'll alert when
  launch apdex drops below the standard 0.85 — want a different threshold?"). Do not call
  `write_alerts` until the user has supplied or confirmed the threshold.

The rule: a missing threshold/channel/time-window is never silently filled. You may
*suggest* the documented baseline, but the create only fires after the user confirms it.
A fabricated-and-shipped threshold is the failure mode to avoid.

### Step 3. Read the init catalog

Call `read_alerts(action: "init", slug, mode)`. This tells you, for THIS app and plan:
which rule types exist, each type's triggers, the valid conditions/operators, the actions,
the time-window keys for each trigger, and the real lookup ids. Everything you send must
come from here.

### Step 4. Map intent → an init-exposed type + trigger; gatekeep

Pick the type and trigger that match the user's words. Disambiguate carefully:

| User says | Trigger |
| --- | --- |
| crash affecting X% of **sessions** | `configurable_velocity_alert` |
| crash affecting X% of **users** | `crash_affecting_percentage_of_users` |
| crash-free **sessions** below X% | `crash_free_session` |
| crash-free **users** below X% | `crash_free_users_overall` |
| ANR / "app not responding" | `anr_free_users_overall` / `anr_free_sessions_overall` |
| "slowest 5% / 95th percentile" latency | `p95` |
| network failures / error rate | `failure_rate` |
| crash spike / accelerating | `accelerating_crash` |
| any new crash | `crash_reported` |

If the type/trigger/condition/action the user wants is **not in init** (plan-gated, e.g.
release rollout or feature flags; platform-gated, e.g. ANR on iOS; or simply unsupported),
STOP and tell the user it isn't available for this app and why. Do not force it.

### Step 5. Build a correct payload

Encode to the wire format the tool expects — getting this wrong silently malforms the rule:

- **Operators are stringified integers**: `"1"`=equals, `"4"`=less than, `"5"`=greater than,
  `"8"`=is one of, `"3"`=contains. Send `>` as `"5"`, `<` as `"4"`.
- **`trigger_options.time` is the integer key** from init's time options for that trigger —
  read it from init. Never send `"1h"`, `24`, or a raw minute count.
- **Apdex (Overall app, Release rollout) thresholds are 0–1 decimals**: `0.8` for 80%,
  never `80`. Other percentages (crash-free, ANR, failure_rate, dropoff, velocity) are
  **literal numbers** (e.g. `99`, `10`).
- **p95 / p50 thresholds are in seconds** (`0.5` = half a second), not milliseconds.
- **Crashes use `app_version_v2`**, never the deprecated `app_version`.
- **Lookup values** (tracking-tool id for `forward`, developer ids for `send_email`, team
  id for `set_team`) come from init's lookup tables — never invented. `send_email` to
  everyone is `{ "developer_ids": ["all"] }`.
- **Operation**: `0` = AND, `1` = OR (a plain integer, not a string).

Value ranges (a value outside the range is invalid — fix it or ask, don't send it):
apdex 0–1; crash-free / ANR / velocity / failure_rate / dropoff 0–100.

Pick an **action** — an alert with no action is silent. Default to `send_email` to the
relevant developers unless the user named a channel (then `forward` to that tracking tool).

### Step 6. Write, then verify honestly

Call `write_alerts(action, …)`. Confirm before creating/deleting if the intent was at all
ambiguous. If the tool returns an error (e.g. a 422: out of range, limit reached, id not
found), **surface that failure plainly** — say what failed and why. Never report success
for a call that errored. On success, confirm the alert's effective behavior back to the user.

## Style

- Translate, don't interrogate: ask only for the piece that's genuinely missing, one question.
- Mirror the user's numbers exactly (97% stays 97%, not "about 95%").
- Confirm state-changing actions (create/delete/disable) before firing when intent is fuzzy.
- Never claim an alert was created until the tool returns success.

## Red Flags — STOP

- "No threshold given, I'll use a sensible default and create it." Stop — you may *propose*
  the standard baseline, but confirm with the user before calling write_alerts. Never ship
  a create on a threshold the user didn't state or confirm.
- "I'll call write_alerts without init." Stop — you'll send ids/triggers the app may not
  support, and the write fails or corrupts the rule.
- "The type isn't in init but the user asked for it, I'll send it anyway." Stop — it's
  unavailable. Say so.
- "It returned a 422 but I'll tell the user it's set up." Never. Report the failure.
- "Apdex 90% → send 90." Stop — apdex is 0–1; send `0.9`.
- "Crashes filter on app_version." Stop — use `app_version_v2`.
```

</details>

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the alert tools aren't available, the skill stops and directs the user to set up the MCP server, or run `luciq-setup`.

The skill uses these tools:

| Tool           | Action                         | Purpose                                                                                                                                                                                                  |
| -------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `read_alerts`  | `init`                         | The per-app catalog: valid rule types, triggers, conditions, actions, operators, time-window keys, and lookup tables (developers, teams, tracking tools, tags). The source of truth for what's possible. |
| `read_alerts`  | `list` / `details`             | Find or inspect an existing alert, ahead of an update, enable/disable, or delete.                                                                                                                        |
| `write_alerts` | `create` / `update` / `delete` | Apply the change. State-changing — the agent confirms intent first.                                                                                                                                      |

{% hint style="info" %}
If the alert tools aren't available, the skill stops and points the user to the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) setup, or to run `luciq-setup`.
{% endhint %}

## How the agent configures an alert

### Step 1. Classify the intent

The agent sorts the request into one of five buckets: create a new alert, update an existing one, enable/disable, delete, or inspect. For an inspect request, it calls `read_alerts` (`list` or `details`) and answers directly. For the rest, it continues through the workflow below.

### Step 2. Gather the spec

A create or update needs a metric/trigger, a threshold (for threshold-based triggers), a time window where applicable, optional conditions, and an action naming who or where to notify. The agent handles two distinct gaps differently:

* **The metric itself is ambiguous** — for example, "too many crashes" could mean occurrences in a time window, affected users in a time window, or a percentage of users or sessions. The agent asks which the user means rather than picking one, and it never maps vague volume language onto a stability rate like `crash_free_session`, which is a different concept.
* **The metric is clear but the threshold is unstated** — the agent either asks for a number, or proposes the standard baseline (for example, "I'll alert when launch apdex drops below the standard 0.85 — want a different threshold?") and waits for confirmation. It never calls `write_alerts` until the threshold is supplied or confirmed.

### Step 3. Read the init catalog

The agent calls `read_alerts` with `action: init` for the app and mode in question. This returns, for that specific app and plan, which rule types exist, each type's valid triggers, the valid conditions and operators, the available actions, the time-window keys for each trigger, and the real lookup ids. Every value the agent sends downstream is built from this response.

### Step 4. Map the intent to a supported type and trigger

The agent matches the user's words to a specific trigger — for example, "crash-free sessions below X%" maps to `crash_free_session`, while "crash affecting X% of users" maps to `crash_affecting_percentage_of_users`. If the type, trigger, condition, or action the user wants isn't exposed by `init` — because it's plan-gated (like release rollout or feature-flag alerts), platform-gated (like ANR on iOS), or simply unsupported — the agent stops and tells the user it isn't available for that app, rather than forcing an invalid payload.

### Step 5. Build a correct payload

The agent encodes several values in a wire format the `write_alerts` tool expects, since getting these wrong silently malforms the rule:

* Operators are stringified integers rather than symbols (for example, `"5"` for greater-than, `"4"` for less-than).
* The time-window value is the integer key `init` returned for that trigger, not a string like `"1h"` or a raw minute count.
* Apdex thresholds for Overall app and Release rollout alerts are 0–1 decimals (`0.8` for 80%), while other percentages (crash-free, ANR, failure rate, dropoff, velocity) are literal numbers.
* p95/p50 thresholds are in seconds, not milliseconds.
* Crash-related rules use `app_version_v2`, never the deprecated `app_version`.
* Lookup values — a tracking-tool id for a `forward` action, developer ids for `send_email`, a team id for `set_team` — come only from `init`'s lookup tables, never invented.

Every alert needs an action, since one with no action is silent. The agent defaults to `send_email` to the relevant developers unless the user named a channel, in which case it uses `forward` to that tracking tool.

### Step 6. Write, then verify honestly

The agent calls `write_alerts` and confirms with the user first if the intent was at all ambiguous. If the tool call errors — for example, a value out of range, a plan limit reached, or an id not found — the agent surfaces that failure plainly rather than reporting success. On a successful call, it confirms the alert's effective behavior back to the user.


# luciq-alert-gaps

The luciq-alert-gaps skill finds metrics that are unhealthy and unwatched, then proposes alerts to close the gap using live Luciq MCP data.

Find the metrics that matter, are currently unhealthy or material, and have no alert watching them — then propose alerts that close those gaps. A gap is the intersection of two facts pulled from Luciq MCP data: a metric is below a sensible bar, and no existing rule covers it. The skill won't recommend an alert for a healthy metric, or one that's already covered — that's just noise.

## Use this skill when

* The user asks whether they're missing alert coverage. *"Am I missing any alerts?"*, *"is anything unmonitored?"*
* The user asks what to alert on. *"What should I be alerting on?"*, *"recommend alerts"*.
* The user just installed Luciq and wants a starting point. *"I just installed Luciq — what alerts should I create?"*
* The user asks whether a specific degraded metric already has an alert on it.

Not for: reducing alerts that fire too often (use `luciq-alert-noise`), creating, changing, or managing one specific alert the user already has in mind (use `luciq-alert-config`), investigating why something crashed or hung (use `luciq-debug`), or first-time SDK install (use `luciq-setup`).

## What the agent does

* **Coverage mapping.** Reads every existing alert rule first, noting its type, trigger, and any entity it's scoped to, before evaluating anything else.
* **Health cross-referencing.** Pulls current app-level and per-entity health from the Luciq MCP — crash-free rate, ANR, and per-endpoint/screen/launch/flow APM metrics — to know what's actually unhealthy right now.
* **Strict gap logic.** Only calls something a gap when it's both uncovered by an existing rule and below a sensible health bar. A bad-but-already-covered metric isn't a gap; a healthy-but-uncovered metric isn't either.
* **Baseline-driven thresholds.** Recommends alert thresholds from Luciq's baseline coverage table, not from the metric's current (often worse) value, so the alert still means something once the issue improves.
* **Approval before writing.** Presents gaps to close, healthy metrics needing no alert, and already-covered metrics as three separate groups, and only creates alerts the user approves.

## 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](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) in one step.

After install, the skill is available as `/luciq-skills:luciq-alert-gaps`.

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-alert-gaps/SKILL.md) from the public repo to `~/.claude/skills/luciq-alert-gaps/SKILL.md` (user-global) or `.claude/skills/luciq-alert-gaps/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-alert-gaps
description: Use when the user wants to find what they are not monitoring and add the missing Luciq alerts. Triggers include "am I missing any alerts", "what should I be alerting on", "alerting/coverage gaps", "set up alerts for my app", "recommend alerts", "is anything unmonitored", "I just installed Luciq, what alerts should I create", or asking whether a degraded metric has an alert. Cross-references current metric health (via the Luciq MCP) against existing alert rules and proposes alerts only for metrics that are both unhealthy and uncovered — never duplicating coverage or alerting on healthy metrics.
---

# Luciq Alerting Gap Analysis

Find the metrics that matter, are currently unhealthy (or material), and have **no alert watching them** — then propose alerts that close those gaps. A gap is the intersection of two facts from MCP data: a metric is below a sensible bar, and no existing rule covers it. Recommending an alert for a healthy metric, or one that's already covered, is noise — don't.

## When NOT to use this skill

- The user's existing alerts fire too often — use `luciq-alert-noise`.
- The user wants to **create, change, or manage one specific alert** they describe — use `luciq-alert-config`.
- The user is investigating *why* something crashed/hung — use `luciq-debug`.
- First-time SDK install or wiring `Luciq.start(...)` — use `luciq-setup`.

If the request fits one of the above, STOP and route there.

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the tools are not available, STOP and direct the user to set up the MCP server (or run `luciq-setup`).

Tools this skill uses (verbatim names):

| Tool | Action | Purpose |
| --- | --- | --- |
| `read_alerts` | `list` | Existing alert rules — what is already covered, by type and trigger. |
| `read_alerts` | `init` | Per-app catalog of valid types/triggers/conditions/actions and lookup IDs. Call before creating. Also reveals what the app's plan/platform actually supports. |
| `app_insights` | — | Top-level health: crash-free sessions (+ rate), ANR, OOM, app hangs, non-fatal crash count, bug totals, and whether APM has data. |
| `apm_list_groups` | — | Per-entity APM metric values: each network endpoint, launch, flow, screen, and frame-drop group with `apdex_score`, `failure_rate`, `p95_ms`, `p50_ms`, occurrences. Sort ascending by `apdex` / descending by `failure_rate` to find the worst. |
| `write_alerts` | `create` | Create a recommended alert. State-changing — confirm first. |

You MUST ground every "this is a gap" claim on a metric value the MCP returned and the absence of a matching rule. Do not recommend from a generic checklist alone.

## What "good coverage" looks like

Luciq ships a baseline of recommended alerts that encodes sensible defaults. Use it as the reference for both *which* metrics deserve an alert and *what threshold* to set:

| Area | Trigger | Threshold | Window | Notes |
| --- | --- | --- | --- | --- |
| Network | `apdex` | < 0.7 | 1 day | scope to key metrics / high-traffic endpoints |
| Network | `failure_rate` | > 10% | 3 hours | scope to high-traffic endpoints |
| App launches | `apdex` | < 0.85 | 1 day | key launch metric |
| Flows | `dropoff_rate` | > 30% | 1 day | end_reason in crashes / force_restarts |
| Screen loading | `apdex` | < 0.7 | 1 day | key screens |
| Screen rendering | `apdex` | < 0.7 | 1 day | key screens |
| Overall app | `apdex` (frustration-free sessions) | < 0.8 | 1 day | latest / top releases |
| Stability | `crash_free_session` / `crash_free_users_overall` | a target like 99% | — | the single most important safety net |
| Stability (Android) | `anr_free_users_overall` / `anr_free_sessions_overall` | a target like 99% | — | Android only |

These thresholds are the recommended bar. Use them as the alert threshold, and as the line for deciding whether a *current* value is "unhealthy enough" to be a gap.

## Workflow

### Step 1. Read current coverage

Call `read_alerts(action: "list")`. Build a map of what's already watched: for each existing rule, note its `type` + `trigger` and, where relevant, the entity it's scoped to (endpoint, screen, etc.). This is the "covered" set.

### Step 2. Read current health

- Call `app_insights` for the app-level picture: crash-free sessions (and whether it's trending down via `rate`), ANR/OOM/hang presence, non-fatal crash volume.
- Call `apm_list_groups` per APM metric (`network`, `launch`, `flows`, `screen_loading`, `frame_drop`), sorted to surface the worst entities first. Capture `apdex_score`, `failure_rate`, p95/p50 for the key/high-traffic groups.

If the app has no data yet (brand-new integration), skip straight to Step 4's baseline recommendation — there are no live metrics to evaluate, so propose the standard baseline as proactive coverage.

### Step 3. Compute the gaps

A metric is a **gap** when **both** are true — check coverage first, every time:
1. **No existing rule covers it** (no rule of that type+trigger, scoped to that entity). If `has_alert` is true for the metric, it is COVERED — it can never be a gap, no matter how bad the current value is. Do not recommend a second alert for it.
2. It is **below the bar** (network apdex < 0.7, failure_rate > 10%, dropoff > 30%, crash-free below target). Only metrics the tools actually surface count: stability (crash-free sessions/users, ANR) from `app_insights`, and per-entity performance (apdex/failure_rate/p95 per endpoint/screen/flow/launch) from `apm_list_groups`.

Explicitly exclude:
- **Healthy metrics** — value is above the bar. No alert needed, even if uncovered. Say so; don't recommend.
- **Already-covered metrics** — a rule of that type+trigger already watches it. No duplicate, even if the metric is currently bad.

When a real gap requires a rule type the app's plan or platform doesn't support (e.g. ANR on iOS, or a plan-gated type absent from `init`), still report it as a gap and name the plan/platform requirement. Do not silently drop it.

### Step 4. Present recommendations before creating anything

Output three groups so the user sees the full reasoning:

1. **Gaps to close** — for each: the metric, its current value, why it's a gap, and the proposed alert (type, trigger, threshold from the baseline table, window, scope).
2. **Healthy / no alert needed** — uncovered metrics that are fine, so the user knows you looked and chose not to add noise.
3. **Already covered** — so the user sees existing protection.

For a brand-new app, group 1 is the baseline set proposed proactively. Do not write yet.

### Step 5. Create approved alerts

For each recommendation the user approves:

1. Call `read_alerts(action: "init", slug, mode)`. Build payloads **only** from the types, triggers, conditions, and lookup IDs that init exposes for this app. If a recommended type/trigger isn't in init, it isn't available — surface that instead of forcing it.
2. Call `write_alerts(action: "create", ...)` with the payload.
3. Confirm each created alert back to the user.

Wire encoding the tool expects (get it right or the rule is silently malformed):
- Operators are **stringified integers**: `"1"`=equals, `"4"`=less than, `"5"`=greater than, `"8"`=is one of, `"3"`=contains.
- `trigger_options.time` is the **integer key** from init's time options for that trigger — not `"1h"` or `24`.
- Apdex (Overall app) thresholds are **0–1 decimals** (`0.8`, not `80`). failure_rate / crash-free / dropoff are literal percentages.
- p95/p50 thresholds are in **seconds**, not milliseconds.
- Lookup values (developer / team / tracking-tool IDs) come from init, never invented.
- APM rules carry a `count` floor so low-traffic noise doesn't trip them.

### Step 6. Pick a delivery channel

A created alert needs an action or it's silent. Default to `send_email` to the appropriate developers (use init's lookup; `["all"]` if the user has no preference). If the user names Slack/Jira/PagerDuty/etc., use a `forward` action with the tracking-tool ID from init. Don't create an alert with no action.

## Style

- Lead with current numbers: "checkout endpoint apdex is 0.45 (bar is 0.7), no alert" — concrete and checkable.
- Use the baseline thresholds for created alerts, not the metric's current bad value. (A metric at 0.45 still gets a 0.7 alert, so it keeps firing until truly fixed.)
- Don't over-recommend. A handful of meaningful alerts beats one per endpoint.
- Never present a create as done before it returns success.

## Red Flags — STOP and surface to the user

- "This metric has no alert, so it's a gap." Only if it's also unhealthy. A healthy uncovered metric is not a gap — don't add the alert.
- "This metric is bad, so add an alert." Check coverage first — if a rule already watches it, adding another is duplication.
- "I'll set the threshold to the current value (0.45)." No — use the baseline bar (0.7). An alert pinned to today's bad number won't tell you when things get worse.
- "I'll create the alert without calling init." Don't — you'll send a type/trigger or lookup ID the app doesn't support and the write fails or corrupts the rule.
- "ANR is a gap, I'll add it" — on an iOS app. ANR is Android-only. Report the gap with the platform caveat instead of creating an invalid rule.
- "I'll recommend the full baseline even though the app's metrics are all healthy." For an app with data, recommend only real gaps. The full baseline is for brand-new apps with no data yet.
- "I'll create twelve alerts, one per endpoint." That's how you manufacture the noise `luciq-alert-noise` has to clean up. Scope to key/high-traffic metrics.
```

</details>

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the tools aren't available, the skill stops and directs the user to set up the MCP server, or run `luciq-setup`.

The skill uses these tools:

| Tool              | Action   | Purpose                                                                                                                                                                |
| ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `read_alerts`     | `list`   | Existing alert rules — what's already covered, by type and trigger.                                                                                                    |
| `read_alerts`     | `init`   | Per-app catalog of valid types, triggers, conditions, actions, and lookup IDs. Called before creating; also reveals what the app's plan and platform actually support. |
| `app_insights`    | —        | Top-level health: crash-free sessions and rate, ANR, OOM, app hangs, non-fatal crash count, bug totals, and whether APM has data.                                      |
| `apm_list_groups` | —        | Per-entity APM values — each network endpoint, launch, flow, screen, and frame-drop group with its apdex score, failure rate, and p95/p50.                             |
| `write_alerts`    | `create` | Creates a recommended alert. State-changing — the agent confirms first.                                                                                                |

Every "this is a gap" claim is grounded in a metric value the MCP actually returned and the absence of a matching rule — never recommended from a generic checklist alone.

{% hint style="info" %}
If the alert tools aren't available, the skill stops and points the user to the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) setup, or to run `luciq-setup`.
{% endhint %}

## What "good coverage" looks like

The skill carries a baseline of recommended alerts, used both as the reference for which metrics deserve an alert and as the threshold to set on any alert it creates. This baseline aligns with Luciq's [predefined alerts](/product-guides-and-integrations/product-guides/automation-and-workflows/alerts-and-rules/predefined-alerts) — see that guide for the canonical recommended defaults:

| Area                | Trigger                                                | Threshold         | Window  | Notes                                         |
| ------------------- | ------------------------------------------------------ | ----------------- | ------- | --------------------------------------------- |
| Network             | `apdex`                                                | < 0.7             | 1 day   | scope to key metrics / high-traffic endpoints |
| Network             | `failure_rate`                                         | > 10%             | 3 hours | scope to high-traffic endpoints               |
| App launches        | `apdex`                                                | < 0.85            | 1 day   | key launch metric                             |
| Flows               | `dropoff_rate`                                         | > 30%             | 1 day   | end\_reason in crashes / force\_restarts      |
| Screen loading      | `apdex`                                                | < 0.7             | 1 day   | key screens                                   |
| Screen rendering    | `apdex`                                                | < 0.7             | 1 day   | key screens                                   |
| Overall app         | `apdex` (frustration-free sessions)                    | < 0.8             | 1 day   | latest / top releases                         |
| Stability           | `crash_free_session` / `crash_free_users_overall`      | a target like 99% | —       | the single most important safety net          |
| Stability (Android) | `anr_free_users_overall` / `anr_free_sessions_overall` | a target like 99% | —       | Android only                                  |

## How the agent finds coverage gaps

### Step 1. Read current coverage

The agent calls `read_alerts` with `action: list` and builds a map of what's already watched — for each existing rule, its type, trigger, and (where relevant) the entity it's scoped to, like a specific endpoint or screen.

### Step 2. Read current health

The agent calls `app_insights` for the app-level picture — crash-free sessions and whether that's trending down, ANR/OOM/hang presence, and non-fatal crash volume — then calls `apm_list_groups` for each APM metric (network, launch, flows, screen loading, frame drop), sorted to surface the worst entities first. If the app has no data yet because it's a brand-new integration, the agent skips straight to proposing the standard baseline as proactive coverage.

### Step 3. Compute the gaps

A metric only counts as a gap when both of these hold, checked in this order:

1. **No existing rule covers it.** If a rule of that type and trigger, scoped to that entity, already exists, the metric is covered — no matter how bad its current value is, it never becomes a gap.
2. **It's below the bar** from the baseline table above — for example, network apdex under 0.7, failure rate over 10%, or crash-free below its target.

Healthy metrics (above the bar) get no alert recommendation even if uncovered, and already-covered metrics get no duplicate even if currently bad. When a real gap needs a rule type the app's plan or platform doesn't support — ANR on iOS, for instance, or a plan-gated type missing from `init` — the agent still reports the gap and names the plan or platform requirement rather than silently dropping it.

### Step 4. Present recommendations before creating anything

The agent presents three groups so the reasoning is visible: gaps to close (with the metric, its current value, why it's a gap, and the proposed alert), healthy metrics that need no alert, and metrics that are already covered. For a brand-new app, the first group is the baseline set proposed proactively. Nothing is written yet.

### Step 5. Create approved alerts

For each recommendation the user approves, the agent calls `read_alerts` with `action: init` and builds the payload only from the types, triggers, conditions, and lookup IDs that response exposes for the app — if a recommended type or trigger isn't there, it surfaces that instead of forcing it. It then calls `write_alerts` with `action: create` and confirms each created alert back to the user. The same wire-format rules `luciq-alert-config` follows apply here: stringified operator codes, integer time-window keys, 0–1 decimal apdex thresholds for Overall app alerts, and p95/p50 thresholds in seconds.

### Step 6. Pick a delivery channel

Every created alert needs an action, since one with none is silent. The agent defaults to `send_email` to the appropriate developers, or uses a `forward` action to a named tracking tool (Slack, Jira, PagerDuty, and similar) when the user specifies one.


# luciq-alert-noise

The luciq-alert-noise skill finds alert rules that fire too often to be useful and fixes them at the source, cutting fatigue not coverage.

Find the alert rules that fire too often to be useful, and fix them at the source. Noise isn't "an alert fired a lot" — it's "an alert fired a lot **and** the firings weren't individually worth a notification." The skill's job is to cut fatigue without cutting coverage, and every recommendation is grounded in Luciq MCP data rather than guessed from a rule's title.

## Use this skill when

* The user says their alerts are too noisy. *"My alerts are too noisy"*, *"I get too many alert emails/notifications"*.
* The user wants their alert setup cleaned up. *"Clean up / tune / audit my alerts"*.
* The user asks which alerts are spammy or fire constantly. *"Which alerts are spammy?"*, *"stop paging me so much"*.
* The user asks which alert rules fire too often in general.

Not for: adding missing alerts or checking monitoring coverage (use `luciq-alert-gaps`), creating, changing, or managing one specific alert the user describes (use `luciq-alert-config`), investigating why something crashed or hung (use `luciq-debug`), or first-time SDK install (use `luciq-setup`). Resolving or acknowledging a single firing (an incident) rather than changing the rule itself is a direct one-call action, not a noise audit.

## What the agent does

* **Trigger-count triage.** Pulls the full alert inventory sorted by how often each rule's conditions were met, and partitions rules into quiet (leave alone) and loud (investigate) using the product's own noise signal.
* **Root-cause diagnosis.** For each loud rule, figures out *why* it's loud — a trivially low threshold, an overly broad scope, correct detection but excessive paging, an exact duplicate of another rule, or a fully redundant rule — before picking a fix.
* **Safety-critical protection.** Never weakens detection or disables alerts that are supposed to fire loudly because each firing is a real emergency (a crash hitting real users, crash-free or ANR rate dropping below a safety floor). At most, it throttles the notification.
* **Least-destructive remediation.** Prefers raising a threshold, narrowing scope, or adding a throttle before recommending disabling a rule, and disabling before deleting.
* **Approval before writing.** Presents a per-rule remediation plan, with safety-critical rules called out separately, and only applies changes the user approves.

## 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](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) in one step.

After install, the skill is available as `/luciq-skills:luciq-alert-noise`.

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-alert-noise/SKILL.md) from the public repo to `~/.claude/skills/luciq-alert-noise/SKILL.md` (user-global) or `.claude/skills/luciq-alert-noise/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-alert-noise
description: Use when the user wants to reduce noisy, chatty, or spammy Luciq alerts, cut alert fatigue, or stop being over-notified. Triggers include "my alerts are too noisy", "I get too many alert emails/notifications", "alerts are firing constantly", "clean up / tune / audit my alerts", "which alerts are spammy", "stop paging me so much", or asking which alert rules fire too often. Inspects each alert's trigger frequency via the Luciq MCP and recommends targeted fixes (raise threshold, narrow scope, throttle, merge, or disable) — never blindly silencing a safety-critical alert.
---

# Luciq Alert Noise Reduction

Find the alert rules that fire too often to be useful, and fix them at the source. Noise is not "an alert fired a lot" — it is "an alert fired a lot **and the firings were not individually worth a notification**." The job is to cut fatigue without cutting coverage. Every recommendation is grounded in MCP data; never guess which alerts are noisy.

## When NOT to use this skill

- The user wants to **add** missing alerts or check monitoring coverage — use `luciq-alert-gaps`.
- The user wants to **create, change, or manage one specific alert** they describe — use `luciq-alert-config`.
- The user is investigating *why* something crashed/hung — use `luciq-debug`.
- First-time SDK install or wiring `Luciq.start(...)` — use `luciq-setup`.
- The user wants to resolve/acknowledge a specific firing (incident), not change the rule. That is a one-call `write_incidents` action, not a noise audit — just do it directly.

If the request fits one of the above, STOP and route there.

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the alert tools are not available, STOP and direct the user to set up the MCP server (or run `luciq-setup`).

Tools this skill uses (verbatim names):

| Tool | Action | Purpose |
| --- | --- | --- |
| `read_alerts` | `list` | All alert rules + their `conditions_met_count` (the rolling trigger count) and current `status`. Sort by `highest_triggered_count` to surface the worst first. |
| `read_alerts` | `details` | Full payload of one rule. Call before updating, to mirror the existing shape. |
| `read_alerts` | `init` | Per-app catalog of valid types/triggers/conditions/actions/operators and lookup IDs. Call before any update. |
| `read_incidents` | `list` | Actual firing history with `status` (open / resolved), `type`, `count`, and timestamps. Use to judge whether firings were actionable. |
| `write_alerts` | `update` | Apply a remediation to a rule. State-changing — confirm first. |
| `write_alerts` | `delete` | Remove a fully redundant rule. State-changing — confirm first. |
| `write_incidents` | `resolve` | Clear stale open firings as a side cleanup. |

You MUST base every "this alert is noisy" claim on a value the MCP returned. Do not infer noise from a rule's title or your priors.

## What "noisy" means

Luciq tracks how often each rule's conditions are met on a rolling 8-day window and exposes it as `conditions_met_count` on the `read_alerts` list response. That counter is the product's own noise signal: the dashboard treats a rule that crosses **4** matches in the window as a candidate for noise. Use `> 4` as the entry filter for "look closer", not as the verdict.

A rule is genuinely noisy when both hold:
1. It fires frequently (`conditions_met_count` is high relative to the others, and well above 4), AND
2. The firings are not individually worth a separate notification — because the threshold is trivially low, the scope is too broad (no conditions), it duplicates another rule, or it pages on a non-actionable signal.

A rule that fires often **and every firing matters** (a real incident stream) is not noise. The fix there is throttling the *notification*, not weakening the *detection*.

## Workflow

### Step 1. Pull the alert inventory

Call `read_alerts(action: "list", sort_by: "highest_triggered_count", sort_direction: "desc")`. This is the spine of the audit. Note for each rule: `ulid`, `type`, `trigger`, `conditions`, `actions` (and any `frequency` throttle), `status`, and `conditions_met_count`.

Drop disabled rules from consideration — a disabled rule is already silent. Say so if the user asked about one.

### Step 2. Triage by trigger count

Partition the enabled rules:

- **Quiet** (`conditions_met_count` ≤ 4): leave alone. Do not recommend changes to a rule that isn't firing much, even if it looks imperfect. Surface that they are healthy.
- **Loud** (`conditions_met_count` > 4): candidates. Continue to Step 3 for each.

If nothing is loud, report that plainly — "no noisy alerts, here's what you have" — and stop. Do not invent problems.

### Step 3. Diagnose each loud rule

For each candidate, decide *why* it is loud before deciding the fix. When the count alone is ambiguous, call `read_incidents(action: "list", filters: { ... }, sort_by: "count")` to see whether the firings were distinct real events or the same thing over and over.

Map the cause to the right remediation:

| Diagnosis | Signal in the data | Remediation |
| --- | --- | --- |
| Threshold trivially low | e.g. `occurrences_count > 1`, `crash_free_session < 99.9`, p95 just above normal | **raise_threshold** to a meaningful level |
| Scope too broad | no `conditions`, fires on every crash/request/version | **narrow_conditions** (add app_version, key_metric, endpoint, etc.) |
| Detection is right, paging is too frequent | threshold sound, firings are real, but notified on every match | **add/lengthen throttle** (set `frequency` on the action) |
| Exact duplicate of another rule | same `type` + `trigger` + `conditions` as a sibling | **merge** (keep one, delete the other) |
| Fully redundant / obsolete | superseded by a broader rule, or watches a retired metric | **disable** or **delete** |

### Step 4. Protect safety-critical alerts

Some alerts are *supposed* to fire loudly because each firing is an emergency. Never weaken detection or disable these — at most, throttle the notification. Treat as safety-critical:

- `crash_affecting_percentage_of_users`, `configurable_velocity_alert` (a crash hitting real users)
- `crash_free_session` / `crash_free_users_overall` below ~95%
- `anr_free_users_overall` / `anr_free_sessions_overall`
- `affected_users_in_time` with a large user count

If one of these is loud, the recommendation is **add_throttle** or a small **raise_threshold**, with an explicit note that you are keeping detection intact. If you find yourself about to recommend disabling one, STOP — that is removing a safety net.

### Step 5. Present the plan before touching anything

Output a per-rule table the user can approve: rule title, why it's noisy (cite `conditions_met_count` and the diagnosis), and the proposed remediation. Group safety-critical rules separately so the user sees they're handled conservatively. Do not write yet.

### Step 6. Apply approved remediations

For each rule the user approves:

1. Call `read_alerts(action: "init", slug, mode)` to get valid fields/IDs, and `read_alerts(action: "details", ulid)` to mirror the rule's current payload.
2. Build the updated payload — change only the field the remediation targets (threshold value, an added condition, a `frequency` on the action, or `status`). Keep everything else identical.
3. Call `write_alerts(action: "update", ...)` (or `delete` for a confirmed merge/redundant rule).
4. Confirm each change back to the user with the new effective behavior.

Respect the payload rules the `write_alerts` tool documents — most importantly: Overall-app/Release-rollout apdex thresholds are 0–1 decimals (send `0.9`, not `90`); all other percentages are literal; operators are stringified integers; and you may only use types/triggers/conditions that `init` exposes for this app.

### Step 7. Optional — clear stale firings

If the audit surfaced open triggered alerts that are clearly stale (resolved in reality but still `open`), offer to clear them via `write_incidents(action: "resolve", ulid)`. This is cleanup, not noise reduction — keep it separate and only on request.

## Authoring changes correctly (read before any write_alerts call)

Every remediation you apply goes through `write_alerts`. Produce a valid payload:

1. Call `read_alerts(action: "init", slug, mode)` first and `read_alerts(action: "details", ulid)` to mirror the rule's existing shape. Build only from what init exposes for this app; if a field isn't in init, don't send it. Never invent a tracking-tool / developer / team id — take it from init's lookup tables.
2. Wire encoding the tool expects:
   - Operators are **stringified integers**: `"1"`=equals, `"4"`=less than, `"5"`=greater than, `"8"`=is one of, `"3"`=contains. Send `>` as `"5"`, `<` as `"4"`.
   - `trigger_options.time` is the **integer key** from init's time options for that trigger — not hours/minutes. Read the key from init; never send `"1h"` or `24`.
   - Apdex (Overall app) thresholds are **0–1 decimals** (`0.8` for 80%, never `80`). Other percentages (crash-free, failure_rate, velocity) are literal numbers.
   - p95/p50 thresholds are in **seconds** (`0.5` = half a second), not milliseconds.
   - Crashes use `app_version_v2`, not the deprecated `app_version`.
3. Value ranges: apdex 0–1; crash-free / ANR / velocity / percentage 0–100. Never raise a threshold past these bounds.
4. Change only the field the remediation targets; keep everything else identical to the existing rule.

## Style

- Quantify every claim: "fired 47 times in 8 days" beats "fires a lot".
- Prefer the least destructive fix that works: raise/narrow/throttle before disable; disable before delete.
- One change per rule per pass. Don't stack five edits and lose the thread.
- Never present a write as done before it returns success.

## Red Flags — STOP and surface to the user

- "This alert fires constantly, so disable it" — check first whether each firing is a real incident. If so, throttle, don't silence.
- "It's a crash-affecting-users alert firing 40 times, let's turn it off." Never. That's the alert doing its job. Throttle the notification.
- "I'll guess the threshold should be ~50." Base it on the metric's normal range from the data, or ask. Don't pull numbers from nowhere.
- "I'll update the rule without calling init." Don't — you'll send IDs/triggers the app may not support and the write will fail or silently corrupt the rule.
- "The user said clean up, so I'll batch-disable everything loud." Get per-rule approval. Bulk-disabling is how coverage quietly disappears.
- "conditions_met_count is 3 but the title sounds spammy, I'll flag it." 3 is below the signal. Leave it.
```

</details>

## Prerequisites

The Luciq MCP server must be configured and authenticated. If the alert tools aren't available, the skill stops and directs the user to set up the MCP server, or run `luciq-setup`.

The skill uses these tools:

| Tool              | Action    | Purpose                                                                                                                                           |
| ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `read_alerts`     | `list`    | All alert rules with their `conditions_met_count` (rolling trigger count) and current status. Sorted by trigger count to surface the worst first. |
| `read_alerts`     | `details` | Full payload of one rule, read before updating to mirror its existing shape.                                                                      |
| `read_alerts`     | `init`    | Per-app catalog of valid types, triggers, conditions, actions, operators, and lookup IDs — read before any update.                                |
| `read_incidents`  | `list`    | Actual firing history, used to judge whether firings were distinct real events or repeats of the same thing.                                      |
| `write_alerts`    | `update`  | Applies a remediation to a rule. State-changing — the agent confirms first.                                                                       |
| `write_alerts`    | `delete`  | Removes a fully redundant rule. State-changing — the agent confirms first.                                                                        |
| `write_incidents` | `resolve` | Clears stale open firings as an optional side cleanup.                                                                                            |

Every "this alert is noisy" claim is based on a value the MCP actually returned — never inferred from a rule's title.

{% hint style="info" %}
If the alert tools aren't available, the skill stops and points the user to the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) setup, or to run `luciq-setup`.
{% endhint %}

## What "noisy" means

Luciq tracks how often each rule's conditions are met over a rolling 8-day window and exposes it as `conditions_met_count`. The dashboard treats a rule crossing 4 matches in that window as a candidate for noise — the skill uses that same threshold as an entry filter for "look closer," not as the final verdict.

A rule only counts as genuinely noisy when both are true: it fires frequently, well above the entry threshold, **and** the individual firings aren't worth a separate notification — because the threshold is trivially low, the scope is too broad, it duplicates another rule, or it pages on a non-actionable signal. A rule that fires often but where every firing is a real incident isn't noise; the fix there is throttling the notification, not weakening detection.

## How the agent reduces alert noise

### Step 1. Pull the alert inventory

The agent calls `read_alerts` with `action: list`, sorted by trigger count descending, and records each rule's id, type, trigger, conditions, actions, status, and `conditions_met_count`. Disabled rules are dropped from consideration, since a disabled rule is already silent.

### Step 2. Triage by trigger count

The agent partitions the enabled rules into quiet (at or below the 4-match threshold, left alone) and loud (candidates for further diagnosis). If nothing is loud, it reports that plainly and stops rather than inventing problems.

### Step 3. Diagnose each loud rule

For each candidate, the agent determines why it's loud before deciding on a fix, pulling firing history via `read_incidents` when the count alone doesn't make the cause obvious. It maps the cause to a remediation:

| Diagnosis                                  | Signal in the data                                                | Remediation                                               |
| ------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------- |
| Threshold trivially low                    | e.g. an occurrence count over 1, or a p95 just above normal       | Raise the threshold to a meaningful level                 |
| Scope too broad                            | no conditions set, fires on every crash/request/version           | Narrow the conditions (app version, key metric, endpoint) |
| Detection is right, paging is too frequent | threshold is sound and firings are real, but every match notifies | Add or lengthen a throttle on the action                  |
| Exact duplicate of another rule            | same type, trigger, and conditions as a sibling rule              | Merge — keep one, delete the other                        |
| Fully redundant or obsolete                | superseded by a broader rule, or watches a retired metric         | Disable or delete                                         |

### Step 4. Protect safety-critical alerts

Some alerts are supposed to fire loudly because every firing is an emergency — a crash hitting a real percentage of users, or crash-free/ANR rate dropping below a safety floor. The agent never weakens detection or disables these; at most it adds a throttle or a small threshold raise, with an explicit note that detection stays intact.

### Step 5. Present the plan before touching anything

The agent presents a per-rule table for approval — the rule, why it's noisy (citing the actual trigger count and diagnosis), and the proposed remediation — with safety-critical rules called out separately so the user can see they're being handled conservatively. Nothing is written yet.

### Step 6. Apply approved remediations

For each approved rule, the agent reads the app's `init` catalog and the rule's current `details`, changes only the field the remediation targets, and calls `write_alerts` with `update` (or `delete` for a confirmed merge or redundant rule). It confirms each change back to the user with the new effective behavior.

### Step 7. Optional — clear stale firings

If the audit surfaces open triggered alerts that are clearly stale, the agent offers to clear them via `write_incidents` with `resolve`, kept as a separate, on-request cleanup rather than part of the noise-reduction recommendation itself.


# luciq-cli

The luciq-cli skill installs and authenticates the Luciq CLI, uploads symbol files, wires CI, and scripts data queries with typed flags.

Drive Luciq from a terminal, a build pipeline, or a script — installing and authenticating the `luciq` CLI, uploading symbol files, wiring symbolication into CI, or turning a data question into a repeatable command.

Symbol uploads have no MCP equivalent, so this is the only path to fix an unsymbolicated crash. For everything else, the CLI runs the same server-side tools the [Luciq MCP server](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) exposes, under the same permissions and plan entitlements — its value over MCP is a command you can commit, schedule, diff, and pipe, not extra data.

## Use this skill when

* The user asks to install the Luciq CLI, run `luciq login`, or verify a token with `luciq whoami`.
* The user asks to upload symbol files — iOS dSYMs, Android ProGuard/R8 mappings, NDK `.so` files, React Native source maps, or Flutter Dart symbols — because crash reports aren't symbolicating.
* The user wants symbolication wired into CI: GitHub Actions, Fastlane, Gradle, Bitrise, CircleCI, an Xcode build phase, or cron.
* The user wants an exact, repeatable `luciq ...` command for a data question — one they can commit, schedule, or pipe into `jq` — rather than a conversational answer.
* The user is scripting or automating Luciq in a non-interactive environment (a CI job, cron, a container) with no MCP client available.
* The deliverable is a command, pipeline step, or script. A one-off question about production data inside an IDE conversation belongs to [`luciq-debug`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-debug) or [`luciq-readout`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-readout) instead, and first-time SDK integration belongs to [`luciq-setup`](/product-guides-and-integrations/product-guides/ai-features/agent-skills/luciq-setup).

## What the agent does

* **Instrument choice, up front.** Decides whether the request is CLI work or MCP work before doing anything. Uploads and CI wiring always go to the CLI since there's no MCP equivalent; a one-off question about data goes to MCP. The CLI is never presented as a workaround for a blocked MCP call — both hit the same backend under the same permissions.
* **Install, authenticate, verify.** Checks whether the CLI is installed, installs it if not, authenticates with `luciq login` or `LUCIQ_AUTH_TOKEN`, and confirms with `luciq whoami` before running anything that talks to the API.
* **Symbol uploads and CI wiring.** Locates the real build artifact, picks the exact upload subcommand and required flags for the platform, and requires one manual upload to print `✓` before touching any CI file.
* **Query and scripting.** Resolves the app and mode, builds the command from typed flags where they exist, and falls back to `--filters` for anything without one. Only pipes to `jq` after confirming the output is actually JSON.
* **Gated writes.** Shows the exact write command — `bugs update`, an alert or incident change, a funnel change — verbatim, and waits for explicit approval before running it.
* **`luciq help` as the final authority.** Treats the installed binary, not its own reference tables, as ground truth. Any rejected flag or unknown subcommand sends it to `luciq <group> help <subcommand>` rather than inventing one.
* **No leaked credentials.** Never echoes, logs, commits, or pastes a token, and avoids running `luciq info` in shared output since it prints the configured token in plaintext.

## 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](/product-guides-and-integrations/product-guides/ai-features/luciq-mcp-server) in one step.

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

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-cli/SKILL.md) from the public repo to `~/.claude/skills/luciq-cli/SKILL.md` (user-global) or `.claude/skills/luciq-cli/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-cli
description: Use when the deliverable is a terminal command, a build-pipeline step, or a script rather than a conversational answer — installing and authenticating the `luciq` CLI, uploading symbol files (iOS dSYMs, Android ProGuard/R8 mappings, NDK `.so` files, React Native source maps, Flutter Dart symbols), wiring those uploads into CI (GitHub Actions, Fastlane, Gradle, Bitrise, CircleCI, an Xcode build phase), or turning a data question into a repeatable `luciq ...` command that pipes into `jq`. Triggers include "install the Luciq CLI", "luciq login", "upload dSYMs / mapping / source maps to Luciq", "my crashes aren't symbolicated", "symbolicate in CI", "automate Luciq", "what's the luciq command for X", "script this Luciq query". Symbol uploads have NO MCP equivalent — the CLI is the only path, so route every symbolication-upload request here. For a one-off answer about production data inside an IDE conversation, the MCP tools are the better instrument (luciq-debug, luciq-readout); for first-time SDK integration use luciq-setup.
---

# Luciq CLI

Drive Luciq from a shell: `luciq`. Two halves, with different auth and different reasons to exist.

- **Symbol uploads** (`luciq upload …`) — iOS dSYMs, Android ProGuard/R8 mappings, NDK `.so` files, React Native source maps, Flutter Dart symbols. **There is no MCP tool for uploads.** If a crash report is unsymbolicated, the CLI is the only fix, and a build pipeline is where it belongs.
- **Data commands** (`luciq crashes|bugs|apm|reviews|surveys|apps|issues|opportunities|alerts|incidents|insights`) — each one proxies to the *same server-side tool* the Luciq MCP server exposes, under the *same* role permissions and plan entitlements. The CLI's value over MCP is not extra data; it is **determinism and composability**: an exact command you can commit, schedule, diff, and pipe.

The whole skill turns on one distinction: **is the user asking a question, or asking for a command?** A question is MCP's job. A command, a pipeline step, or a script is this skill's job.

## CLI or MCP — decide before doing anything

| Situation | Instrument |
| --- | --- |
| Upload dSYMs / mapping / NDK / source maps | **CLI only** — no MCP tool exists |
| Wire symbolication into CI, Fastlane, Gradle, a release script, or cron | **CLI** |
| "Give me a command I can re-run / commit / schedule" | **CLI** |
| Output must pipe into `jq`, a shell script, or another tool | **CLI** |
| Non-interactive environment (CI job, cron, container) with no MCP client | **CLI** |
| "Why is crash AB-1234 happening?" — root-cause with repo context | MCP → `luciq-debug` |
| "How is the app doing this week?" — a readout for humans | MCP → `luciq-readout` |
| Author or audit alert rules conversationally | MCP → `luciq-alert-config` / `luciq-alert-gaps` / `luciq-alert-noise` |
| Deduplicate the bug list by a custom rule | MCP → `luciq-group-bugs` |

Both paths hit the same backend, so **never** present the CLI as a workaround for missing MCP access to data: if a permission or plan blocks the MCP tool, it blocks the CLI command too. The one genuine capability gap runs the other way — uploads.

## When NOT to use this skill

- **First-time SDK integration** (adding the SDK, the `init` call, invocation, masking) → `luciq-setup`. This skill installs a *CLI*, not an SDK.
- **Investigating a signal and proposing a code fix** → `luciq-debug`.
- **Composing a report for an audience** → `luciq-readout`.
- **A conversational one-off question** the MCP can answer in one call. Shelling out to the CLI to answer it is slower, needs a separate token, and produces the same numbers.

If the request fits one of those, route there and stop.

## The three invariants

1. **`luciq help` outweighs this skill.** The installed binary is the ground truth; these tables are a map of it. Any rejected flag, unknown subcommand, or surprising required option means: run `luciq <group> help <subcommand>` (or `luciq upload help <subcommand>`) and follow **that**. Never invent a flag, never guess an enum value, never paper over a rejection by retrying the same line.
2. **Never leak a token.** Do not echo, log, commit, or paste a token into a transcript, and do not run `luciq info` in shared output — it prints the configured token in plaintext. In CI, tokens come from the platform's secret store into the environment; a literal token in a workflow file, `Fastfile`, or `build.gradle` is a finding to report, not a step to write.
3. **Never fabricate CLI output.** If a command wasn't run — no auth, no network, user declined — say it wasn't run and show the command. Do not present a plausible-looking JSON body, crash count, or `✓ uploaded` line as if it came from the tool.

## Pick a track

```
- [ ] A. Install / authenticate / verify         → the CLI isn't installed, isn't logged in, or 401s
- [ ] B. Symbol uploads + CI wiring              → unsymbolicated crashes, release pipeline work
- [ ] C. Query or script a data command          → "give me the command for X", jq pipelines
- [ ] D. A write command                          → bugs update, alerts/incidents, funnels  ⛔ approval gate
```

Tracks compose: B and C both require A to have succeeded. Always confirm A before running anything that talks to the API.

## Track A — install, authenticate, verify

```
- [ ] 1. Is it installed? `luciq version` (also proves the binary resolves)
- [ ] 2. Install if missing — brew, gem, or from source
- [ ] 3. Get a CLI token in place — `luciq login`, or `LUCIQ_AUTH_TOKEN` for CI
- [ ] 4. Point at the right cluster if self-hosted (LUCIQ_URL = the API host)
- [ ] 5. Verify: `luciq whoami`. Never declare success without it
```

**Install** (fastest first):

```bash
brew install luciqai/tap/luciq-cli   # macOS / Linux
gem install luciq-cli                # Ruby >= 2.7
```

From source when contributing to the CLI itself: clone, `bundle install`, `bundle exec rake install`.

**One credential for everything.** The CLI token is generated at [dashboard.luciq.ai/company/luciq-cli](https://dashboard.luciq.ai/company/luciq-cli) → *Generate authentication token* — one per user, shown in full only once — and it authenticates every command, uploads included. You never pass an application token; the server resolves the app from `--slug` + `--mode`.

Two consequences that shape every CI recipe: a CLI token carries the user's **own** dashboard role (it is not a service account, so a pipeline built on it breaks when that person rotates their token or leaves), and **uploads need `settings.mapping_files.modify`** on top of app access — a token that queries fine can still be refused for uploads.

```bash
luciq login                          # prompts, writes ~/.luciqrc
luciq login --auth-token "$TOKEN"    # non-interactive
export LUCIQ_AUTH_TOKEN="$TOKEN"     # or skip login entirely (CI-friendly)
```

**Self-hosted / single-tenant:** set `LUCIQ_URL` to the cluster's **API** host — `https://api.<cluster>.luciq.ai` — not its dashboard host. Env var beats `~/.luciqrc` beats the `https://api.luciq.ai` default. Generate the token from that cluster's own dashboard; a token from one cluster will 401 against another.

**Verify with `luciq whoami`.** It performs a real authenticated call, so it separates "token saved" from "token works". What it does *not* prove is authorization: it checks only that the token may use the CLI at all, so a green `whoami` says nothing about whether this token can read crashes or upload symbols. Those are separate per-command permissions, and the first real command is what surfaces them. `luciq info` shows version + URL + token — useful locally, never in shared output.

## Track B — symbol uploads and CI wiring

Unsymbolicated crashes are the symptom; a missing upload step is almost always the cause. Fix the immediate build by hand, then make it permanent in the pipeline.

```
- [ ] 1. Detect the platform (iOS / Android / React Native / Flutter) and the build system
- [ ] 2. Locate the real artifact for THIS build — never guess a path that isn't on disk
- [ ] 3. Pick the exact subcommand + required flags   → references/upload-matrix.md
- [ ] 4. Confirm the flag set against `luciq upload help <subcommand>`
- [ ] 5. Run one upload manually and confirm the ✓ before touching any CI file
- [ ] 6. Wire it into the pipeline, token from a secret   → references/ci-recipes.md
- [ ] 7. Show the diff, state where the secret must be configured, and stop
```

Step 5 is a **hard gate**: a committed pipeline step that has never successfully uploaded once is an untested change shipped into a release path. One manual run proves the artifact path, the credentials, the upload permission, the app/mode resolution, and the flag set in a single shot — and its failure message tells you which of them is wrong. `luciq whoami` proves none of that.

The command shape is uniform:

```bash
luciq upload <SUBCOMMAND> FILE --slug my-app --mode production [--version-name … --version-code … --arch …]
```

Requirements by file type — the part people get wrong:

| Type | Subcommands | Required beyond `--slug` / `--mode` | Extension enforced |
| --- | --- | --- | --- |
| dSYM | `*-ios-dsym` | nothing | `.zip` |
| Mapping | `*-android-mapping` | `--version-name`, `--version-code` | any |
| RN source map | `react-native-*-sourcemap` | `--version-name`, `--version-code` (+ optional `--codepush`) | `.json` / `.txt` |
| Flutter Dart symbols | `flutter-*-sourcemap` | `--version-name`, `--version-code` | `.zip` |
| NDK | `*-ndk` | `--version-name`, `--arch` — **`--arch` instead of `--version-code`** | `.zip` |

`--mode` is the environment the *build* reports to: a TestFlight/beta pipeline uploads to `beta`, not `production`. Symbols in the wrong mode symbolicate nothing in the mode where the crashes land.

`--version-name` / `--version-code` must match the build the crashes will come from, exactly. A mapping uploaded under the wrong version silently fails to deobfuscate anything — it doesn't error, so nothing tells you but the still-obfuscated stack trace weeks later. Read the version from the build system (`versionName`/`versionCode`, `MARKETING_VERSION`/`CURRENT_PROJECT_VERSION`, `pubspec.yaml`, `package.json` + native config), not from memory.

The CLI checks the file locally first — exists, readable, right extension — so a bad path or a `.zip`-vs-`.json` mixup fails instantly and for free. On success it prints `✓ … uploaded successfully!` and exits `0`; on failure, `✗ Upload failed: <message>` and non-zero, which is what makes a failed upload fail the CI job. Per-platform artifact locations, the Flutter `.zip`-vs-RN-`.json` trap, and the full subcommand matrix are in `references/upload-matrix.md`. Platform-specific pipeline snippets are in `references/ci-recipes.md`.

## Track C — query and script

```
- [ ] 1. Resolve the app: `luciq apps list` → real slug. Never guess or invent one
- [ ] 2. Confirm the mode (default `production`; each mode is a SEPARATE dataset)
- [ ] 3. Choose the command + typed flags   → references/command-reference.md
- [ ] 4. Anything with no typed flag goes through --filters '<json>'
- [ ] 5. Run it, or hand it over if the environment can't
- [ ] 6. Only pipe to jq after confirming the output is actually JSON
```

`--slug` and `--mode` are required on every data command except `apps list`. Modes are `production`, `beta`, `staging`, `alpha`, `qa`, `development`; querying the wrong mode returns real, correct, *irrelevant* data — which is worse than an error, so confirm it rather than assuming.

**Typed flags first, `--filters` as the escape hatch.** Common filters have flags (`--status`, `--platform`, `--type`, `--app-version`, `--priority`, `--rating`, …); everything else the underlying tool accepts goes in the raw JSON object, which is merged with the typed flags (**typed flags win** on key conflicts):

```bash
luciq crashes list --slug my-app --mode production --status open --limit 20 \
  --filters '{"devices":["iPhone15,2"],"os_versions":["17.4"]}'
```

**Output is mostly JSON, but not uniformly, and the JSON is enveloped.** The CLI pretty-prints whatever parses as JSON and passes anything else through verbatim. Verified against live data:

| Command | Output |
| --- | --- |
| `crashes list` / `crashes hangs` | `{"crashes": [...]}` |
| `bugs list` | `{"bugs": [...]}` |
| `reviews list` / `surveys list` | `{"reviews": [...]}` / `{"surveys": [...]}` |
| `apps list` | `{"applications": [...]}` — **includes each app's token; treat as secret** |
| `apm groups` | `{"<metric>_groups": [...], "next_offset", "total_groups_count"}` |
| `issues list` | `{"issues": [...], "issues_count", …_pagination_token}` |
| `opportunities list` | `{"opportunities": [...], "total_count", "enabled"}` |
| `alerts list`, `incidents list` | **CSV** — header row + one row per record |
| `show` / `diagnostics` / `insights` / `alerts init` / `funnel-events` | JSON object |

So a `jq` filter is `.bugs[]`, not `.[]` — and on `alerts list` / `incidents list` it fails outright, in a way that reads like an auth or empty-result problem. Check the first line of output before piping.

**Pagination and rate limits.** `--offset` / `--limit` page through results (`limit` caps at 50 on `crashes list`, `bugs list`, and `issues list`; `apm funnel-events` caps at 25). The gateway allows **100 requests per 60 seconds, keyed by source IP** — and that budget covers *every* command, uploads included, so a matrix build pushing symbols for several platforms and ABIs at once draws on the same allowance as a shared CI runner's queries. Page deliberately, don't fan out pagination in parallel, and treat `429` as back-off-and-retry, not as failure. When a scope needs more pages than you're willing to pull, say what you covered; never present a first page as the whole set.

## Track D — writes ⛔

`bugs update`, `alerts create|update|delete`, `incidents resolve|reopen`, and `apm funnel-create|funnel-update|funnel-delete` change production state. There is no `--dry-run` and no undo for most of them.

```
- [ ] 1. Confirm the target (slug, mode, number/ulid) against a read command first
- [ ] 2. For alerts: `luciq alerts init` FIRST — build --payload only from what init exposes
- [ ] 3. Show the exact command you intend to run, verbatim
- [ ] 4. ⛔ Wait for explicit approval. No approval, no write
- [ ] 5. Run one command at a time; report each result honestly, including failures
```

Never batch writes behind a single approval unless the user approved the batch and its contents. Specifics that bite:

- `bugs update` needs at least one change, and duplicate marking (`--duplicate-of` / `--action`) **cannot** be combined with `--status` / `--priority`. Marking a duplicate overwrites the duplicate's status, priority, and assignee from the master and is not fully reversible — for rule-based deduplication across many bugs, hand off to `luciq-group-bugs`, which has the plan-and-approve machinery for it.
- `alerts create|update` take a raw `--payload` JSON object. Guessing its shape wastes a write and can create a wrong alert; `luciq alerts init` returns the valid types, triggers, conditions, and actions for *that* app.
- `apm funnel-update --events` **replaces** the funnel's entire step set — it is not a merge.

## Grounding facts

| | |
| --- | --- |
| Config precedence | `LUCIQ_AUTH_TOKEN` / `LUCIQ_URL` → `~/.luciqrc` (`token=`, `url=`) → default `https://api.luciq.ai` |
| Exit codes | `0` success, non-zero failure. **The exit code is the reliable signal** — CLI errors (`✗ …`) print to stdout while Thor's argument errors go to stderr, so never infer success from an empty stderr |
| Authorization | `account_management.cli.view` gates the CLI itself; each command additionally needs its own role permission and plan entitlement, and uploads need `settings.mapping_files.modify`. Permission and plan errors are **final** — report them, don't retry |
| Rate limit | 100 requests / 60 s per source IP → `429 Rate limit exceeded` |
| `apps list` | the only data command with no `--slug` / `--mode` |

## Reference map

Load only what the current track needs:

| File | Use it for |
| --- | --- |
| `references/command-reference.md` | every group, subcommand, typed flag, sort field, and `--filters` key |
| `references/upload-matrix.md` | per-platform upload subcommands, artifact locations, required flags, file-format traps |
| `references/ci-recipes.md` | GitHub Actions, Fastlane, Gradle, Bitrise, CircleCI, Xcode build phase, cron |
| `references/troubleshooting.md` | error → cause → fix, per-command permissions, unsymbolicated-crash triage |

## Style

- Show the command before running it, and show it as it will actually be run — real slug, real path, secrets as `"$VAR"`.
- Prefer one correct command over a wall of alternatives.
- Quote the CLI's actual error text when something fails; don't paraphrase it into something friendlier and less diagnostic.
- Say "not run" plainly when you couldn't run it.
- When a flag doesn't exist, run `help` and correct the command — don't rationalize the rejection.

## Red Flags — STOP and surface to the user

If you catch yourself thinking any of these, stop:

- "There's probably a `--json` / `--since` / `--all` flag." There probably isn't. `help` is one call away, and an invented flag is a broken command handed to a user.
- "I'll put the token inline so the example is copy-pasteable." That's a leaked credential in a file or a transcript. Secret store → env var → `"$VAR"`, always.
- "I'll pass the app token to the upload." There is no `--app-token`. Uploads take `--slug` / `--mode` and use the CLI login, and they need `settings.mapping_files.modify`.
- "The upload path looks right, I'll commit the CI step." Not until one manual run printed `✓`. Untested release-path changes are how symbolication silently breaks.
- "Close enough on `--version-name` / `--version-code` / `--mode`." All three fail *silently* — the upload succeeds and nothing deobfuscates.
- "I'll pipe it to `jq` with `.[]`." The JSON is enveloped (`.bugs[]`, `.crashes[]`, `.network_groups[]`), and `alerts list` / `incidents list` aren't JSON at all.
- "I'll paste the `apps list` output so we can see the apps." It contains every app's token. Project the fields you need instead.
- "MCP is blocked, I'll shell out to the CLI instead." Same backend, same permissions, same plan gates — the block will hold. Report it.
- "I'll paginate until it's all in." Watch the 100-req/60-s IP budget, and never pass off page one as the full set.
- "I'll just run the write; it's obviously what they meant." Writes are gated on explicit approval, shown verbatim first.
- "`luciq info` will help me debug this." It prints the token in plaintext. Use `luciq whoami`.

The pattern: every shortcut here trades a verifiable command for a plausible-looking one. A command that was never run, or a pipeline step that never uploaded, is the failure this skill exists to prevent.
````

</details>

## Where the methodology lives

The skill's command surface, flags, and output shapes are cross-checked against the published [Luciq CLI](/product-guides-and-integrations/product-guides/luciq-cli) product guide — [Getting Started](/product-guides-and-integrations/product-guides/luciq-cli/getting-started) for installation and authentication, [Uploading Symbol Files](/product-guides-and-integrations/product-guides/luciq-cli/uploading-symbol-files) for the upload matrix, and [Command Reference](/product-guides-and-integrations/product-guides/luciq-cli/command-reference) for every data command. The skill packages that surface as a SKILL.md so an agent can drive it end to end instead of you looking each flag up by hand.

## Prerequisites

* A shell. The Luciq MCP server isn't involved in this skill at all — it installs and authenticates the CLI itself as its first track.
* A CLI token, generated at [dashboard.luciq.ai/company/luciq-cli](https://dashboard.luciq.ai/company/luciq-cli). It's a personal credential tied to your own dashboard role, not a service account, and it authenticates every command, uploads included.
* For symbol uploads specifically: the `settings.mapping_files.modify` permission on top of app access. A token that queries fine can still be refused for uploads.

{% hint style="info" %}
Self-hosted or single-tenant clusters need `LUCIQ_URL` pointed at the cluster's API host (`https://api.<cluster>.luciq.ai`), generated from that cluster's own dashboard — a token from one cluster 401s against another.
{% endhint %}

## How the agent drives the CLI

The skill decides CLI-or-MCP first, then works one of four tracks. Tracks B and C both assume Track A has already succeeded.

### Track A. Install, authenticate, verify

The agent checks whether the CLI is installed (`luciq version`), installs it if missing (Homebrew or the gem), gets a token in place via `luciq login` or `LUCIQ_AUTH_TOKEN`, points at the right cluster if self-hosted, and confirms with `luciq whoami` — never declaring success without that verification. A green `whoami` proves the token works, not that it's authorized for any specific command; that surfaces on the first real call.

### Track B. Upload symbols and wire CI

The agent detects the platform and build system, locates the real artifact on disk, and picks the exact upload subcommand and required flags for the file type (dSYM, ProGuard/R8 mapping, NDK, React Native source map, or Flutter Dart symbols). It runs one upload manually and confirms the `✓` before touching any CI file — a pipeline step that has never uploaded successfully once is an untested change on a release path. `--version-name` / `--version-code` / `--mode` all fail silently on a mismatch, so the agent reads the version from the build system rather than from memory.

### Track C. Query and script data

The agent resolves the app to a real slug with `luciq apps list`, confirms the mode (each mode is a separate dataset), and builds the command from typed flags where they exist, falling back to `--filters` for anything else. Output is mostly enveloped JSON (`{"bugs": [...]}`, `{"crashes": [...]}`) except `alerts list` and `incidents list`, which return CSV — the agent checks the first line of output before piping to `jq`. It paginates deliberately rather than in parallel, respecting the 100-requests-per-60-seconds-per-IP rate limit that covers uploads too.

### Track D. Gate writes

For anything that changes production state — `bugs update`, alert or incident changes, funnel changes — the agent confirms the target against a read command first, shows the exact command it intends to run verbatim, and waits for explicit approval before running it. It runs writes one at a time and reports every result honestly, including failures.

## Out of scope

The skill won't fabricate CLI output — if a command wasn't run, it says so and shows the command rather than presenting a plausible-looking result. It treats `luciq help` as outranking its own reference tables: any rejected flag or unknown subcommand sends it to the live help text instead of inventing one. It never echoes, logs, or pastes a token, and avoids `luciq info` in shared output since that command prints the token in plaintext.


# Luciq Agent

Ask, explore, and act on your app's crashes, releases, performance, and user feedback in plain English, powered by your live Luciq data.

### Your Luciq data, in plain English.

Luciq Agent is an AI assistant built into the dashboard that works with your live Luciq data. Ask about crashes, releases, performance, bugs, or user feedback in plain English, and it does the work for you — no filter menus to click through, no exports to build, no context-switching between pages.

Whether you're an engineer chasing a regression, a PM sizing up a release, or an EM pulling a weekly report, Luciq Agent gets you from question to action in seconds, with every result backed by a live query against your data.

{% hint style="info" %}
**In beta, enabled per account.** Luciq Agent is currently in beta and enabled on a per-account basis. Contact your Customer Success Manager or reach out to Luciq Support to enable it.
{% endhint %}

### Why Luciq Agent

* **One place for every product.** Crashes, APM, bug reports, hangs, surveys, and app store reviews are all just a conversation away.
* **Grounded in your data.** Every response is generated from a live query against your own account — no hallucinated numbers, no stale exports.
* **Multi-turn by design.** Follow up, refine, or pivot in the same thread. The agent keeps the context you've built up.
* **Safe by default.** Any action that modifies your data pauses for your explicit approval before it runs.
* **Zero setup.** No SDK to install, no query syntax to learn — open the panel and start typing.

### Opening Luciq Agent

Once enabled, the **Luciq agent** button appears in the dashboard header, next to your avatar. Click it to open the chat panel on the right side of the screen. You can close it and reopen it at any time — your conversations stay where you left them.

### Using Luciq Agent

#### 1. Ask a question

Type your question into the input field and press Enter. Phrasing is flexible — Luciq Agent understands natural language and picks up the app and page you're currently viewing, so you can refer to the current release, screen, or view without spelling it out.

If you're not sure where to start, Luciq Agent suggests a few example prompts you can click to try.

#### 2. Watch it work

Luciq Agent replies in a streaming response. As it works, it may call one or more of its Luciq tools to do the work — for example, listing crashes for a version, pulling APM metrics for a screen, or updating a bug's status. You can expand the **Ran tools** section under any reply to see which tools were called and what inputs were used.

#### 3. Follow up

Every conversation is multi-turn. Ask follow-up questions, refine what you want, or pivot to a related topic in the same thread — the agent keeps the full context of the conversation.

### What Luciq Agent covers

Luciq Agent covers a broad range of product surfaces, including:

| Area                      | Ask about                                                                              |
| ------------------------- | -------------------------------------------------------------------------------------- |
| **Crashes & Occurrences** | Top issues, new regressions, specific stack traces, distribution by version and device |
| **App Hangs**             | UI freezes, force restarts, and their impact on users                                  |
| **Performance (APM)**     | Slow screens, network calls, app launches, dropped frames, and user flows              |
| **Bug Reports**           | Reports by tag, status, priority, reporter, or release                                 |
| **App Store Reviews**     | Ratings, review content, and what's changing across releases                           |
| **In-app Surveys**        | NPS results, survey responses, and how a survey is performing                          |
| **Issues**                | Prioritized issues across crashes, APM, and bugs                                       |
| **Opportunities**         | AI-synthesized product improvement themes from bug reports and app store reviews       |
| **Alerts & Rules**        | Alert rules, triggered incidents, and how alerting is configured                       |
| **App Health**            | A cross-product health snapshot for any app version                                    |

### What you can ask

A few things you might ask:

**Summarize what's happening**

* *"What are the top crashes affecting users this week?"*
* *"What are users saying in recent app reviews?"*
* *"Summarize app health for the current release."*

**Investigate an issue**

* *"Why is the checkout screen slow on Android 14?"*
* *"Show me the crashes introduced in version 3.2."*
* *"Give me the full diagnostic for crash 4521."*

**Connect the dots**

* *"Which crashes are showing up in our 1-star reviews?"*
* *"Compare crash-free rate across versions 3.2, 3.3, and 3.4."*
* *"Which user flows are generating the most bug reports?"*

**Report and act**

* *"Write a weekly health update for stakeholders."*
* *"What are our biggest product opportunities right now?"*
* *"Triage bug 4521 — set it to major and add a login tag."*

### Conversation history

Luciq Agent keeps a history of every conversation you've started, grouped by day. Click the history icon in the panel header to see past chats, jump back into one to continue, or delete a conversation you no longer need. Starting a **New chat** opens a fresh thread without affecting your history.

### Actions requiring your approval

Luciq Agent can act on your data as well as answer questions about it — for example, updating a bug's status, priority, or tags, or creating and editing funnels.

Before running any action that modifies your data, Luciq Agent pauses and asks for your permission in-chat, showing you the tool it's about to call and the values it plans to use. Click **Allow** to run it once, or **Deny** to skip. Nothing is written to your dashboard without your explicit approval.

### Data access and privacy

Luciq Agent runs against your own Luciq account and only sees the data you have access to. It uses the same permissions as your dashboard user — if you can't see a certain application or record, the agent can't either.

To learn more about how Luciq handles AI feature data, see [AI Privacy & Data Retention](/organization-settings/ai-privacy-and-data-retention).


# Luciq Lens

Use Luciq Lens to get AI-powered visual analysis of your mobile app's UI. Detect layout problems and rendering glitches automatically with Detect Agent.

Luciq Lens is a natural-language search bar built into the dashboard. Instead of clicking through dropdowns to narrow down crashes by version, status, and device — or hunting for the right settings page — you describe what you want, and Lens navigates there with the filters already applied.

<figure><img src="/files/xm0gjtbk3EGQoc68Z8JJ" alt="Luciq Lens turning a plain-English request into a filtered dashboard view"><figcaption></figcaption></figure>

{% hint style="info" %}
**Available on all plans, in beta.** Luciq Lens is enabled by default for every Luciq account, with no setup required.
{% endhint %}

### Opening Lens

Lens lives at the top of every dashboard page.

<figure><img src="/files/dTlPR8KjdHbHB9Iq7INd" alt=""><figcaption></figcaption></figure>

There are two ways to open it:

* Press **⌘K** on macOS or **Ctrl+K** on Windows and Linux.
* Click the search icon in the dashboard header.

### Using Lens

#### 1. Ask a question

Type what you're looking for into the **Ask or search…** field and press Enter. Phrasing is flexible — Lens is built around natural language, not a query syntax.

* *"Open bugs assigned to me"*
* *"Fatal crashes today"*
* *"Frustrating sessions on iOS 17 last 7 days"*
* *"Take me to network URL patterns"*

If you're not sure where to start, open Lens without typing anything — it surfaces a handful of clickable example queries to show you what it can do.

<figure><img src="/files/4CP01pxT4j65LqVsOgvc" alt=""><figcaption></figcaption></figure>

#### 2. Review and confirm

Lens shows a confirmation card describing what it's about to do — for example, *"Navigating to Crashes filtered by Status and App Version."* Click **Confirm** (or press Enter again) to navigate, or **Cancel** to refine your query without leaving the modal.

<figure><img src="/files/LNZxLPatSTMeWh4usuDG" alt=""><figcaption></figcaption></figure>

If Lens isn't sure what you meant, it tells you why and suggests a few clickable queries to try instead.

<figure><img src="/files/iz3O0MZJJ7pUgSC0GA7j" alt=""><figcaption></figcaption></figure>

### What you can ask

Lens covers the main areas of the Luciq dashboard — Bugs, Crashes, Session Replay, APM, Releases, App Health, Issues, AI Issues, Surveys, and Settings. Inside each one, it understands the same filters you'd reach for by hand: app version, date range, status, assignee, OS, device, tags, and the rest. If you can filter to it from the UI, you can usually ask Lens for it in words — it's a navigation tool, not one that acts on records or answers analytical questions.

Lens also handles direct navigation to specific records — for example, *"Open bug 1234"* or *"Show crash 5678"*.

### Query examples

A few queries that show what Lens can do day to day:

**Triage your day**

* *"Open bugs assigned to me"*
* *"Fatal crashes since yesterday"*
* *"Bug reports tagged login this week"*

**Investigate a release**

* *"Fatal crashes in version 2.1"*
* *"ANRs in 3.0.4 last 24 hours"*
* *"Network failures since the 2.5 release"*

**Track customer impact**

* *"Frustrating sessions from Germany today"*
* *"Crashing sessions on iPhone 15"*
* *"Sessions with frustrating UI hangs on Android 14"*

**Spot patterns**

* *"Out of memory crashes last 7 days"*
* *"App hangs on Samsung devices"*
* *"NullPointerException crashes in foreground"*
* *"Crashes affecting more than 100 users this week"*

**Combine multiple filters**

Lens shines when you stack filters that would normally take several clicks to set up by hand.

* *"Critical open bugs assigned to me from this week"*
* *"Unresolved fatal crashes in version 2.1 from the last 24 hours"*
* *"Frustrating sessions from Germany on iPhone 15 this week"*
* *"ANRs in foreground on Android 14 since the 3.0 release"*
* *"Bugs tagged checkout, forwarded to Jira, in version 2.5"*

**Jump straight to a record or page**

* *"Open bug 4521"*
* *"Show crash 8821"*
* *"Take me to network URL patterns"*
* *"Open frustration impact settings"*


# Automation & Workflows

Automate triage and notification in Luciq with alerts, rules, and team ownership, so the right people hear about the right issues fast.


# Alerts & Rules

Set up alerts and rules for bug reports, crashes, network failures, and rollouts, then copy them across your apps and environments.


# Alerting for App Stability - Crash Free Rates

Set up Luciq alerts for app stability and crash-free rates. Get notified when crash-free user or session rates drop below your defined thresholds.

Crash-Free Rates are one of the most important metrics for any mobile app. Setting up these alerts allows you to proactively detect and address stability issues, ensuring a smoother user experience. They provide quick insights into problematic app versions, enabling faster resolution and better decision-making.

## Setting Up Alerts

1. **Go to the Alerts and Rules page**:

   <figure><img src="https://files.readme.io/a212f99777f0870d960ff23eafe29fedee0752ce692aa824b9c89544e877f44d-alerting-for-app-stability-crash-free-sessions-11.png" alt=""><figcaption></figcaption></figure>
2. **Create a new Rule**:

   <figure><img src="https://files.readme.io/ada4309aba5a80da71289be45cfe2fb33c596443514eb19bc0a3dbf70f986f1f-alerting-for-app-stability-crash-free-sessions-6.png" alt=""><figcaption></figcaption></figure>
3. **Select "Overall App" to set up the rule**:

   <figure><img src="https://files.readme.io/0034b9c3814522743440ee24927b697a1c8b7b972b1e3b22ee4db65be3a25753-alerting-for-app-stability-crash-free-sessions-7.png" alt=""><figcaption></figcaption></figure>

### Crash-Free Sessions

Set a threshold and get alerted whenever the application’s crash-free sessions rate drops below the specified threshold.

1. **Trigger**:
   * Select "Crash-free sessions in the last 24 hours".<br>

     <figure><img src="https://files.readme.io/f0835ff-image.png" alt=""><figcaption></figcaption></figure>
2. **Select the Threshold for Crash-Free Sessions**: Any drop below this point will trigger an alert.<br>

   <figure><img src="https://files.readme.io/9496a34-image.png" alt=""><figcaption></figcaption></figure>
3. **Breakout by App Version**: (Optional)
   * By toggling "Send an alert for every app version," you will get alerted for every app version that has its crash-free sessions rate drop below the threshold.
4. **Conditions**:
   * If no condition is added, the rule will be applied to the app/app version if it exceeds 100 sessions.
   * You have 2 conditions you can choose from:

     <figure><img src="https://files.readme.io/4a5bcc5-image.png" alt=""><figcaption></figcaption></figure>

     * **App Version**: Select "Top Releases" or "Latest Releases", or specify app versions.<br>

       <figure><img src="https://files.readme.io/565c942-image.png" alt=""><figcaption></figcaption></figure>

       <br>
     * **Session count**: The minimum number of sessions the app (or selected versions) has to have for the alert to trigger. Specify "Greater than" or "Less than" to set a number of sessions and reduce the noise.

       <figure><img src="https://files.readme.io/8098b92-image.png" alt=""><figcaption></figcaption></figure>
5. **Forward Alert**:
   * Set the option to forward the alert to your favorite integrated tool.<br>

     <figure><img src="https://files.readme.io/404f973-image.png" alt=""><figcaption></figcaption></figure>

### Crash-Free Users

{% hint style="info" %}
Note: Crash-free users data will only be retrieved for SDK versions newer than v11.12.0 for iOS and v11.5.2 for Android
{% endhint %}

Set a threshold and get alerted whenever the application’s crash-free user rate drops below the specified threshold.

1. **Trigger**:
   * Select "Crash-free users in the last 24 hours".<br>

     <figure><img src="https://files.readme.io/c4ee329-image.png" alt=""><figcaption></figcaption></figure>
2. **Select the Threshold for Crash-Free Users**: Any drop below this point will trigger an alert.<br>

   <figure><img src="https://files.readme.io/ae22313-image.png" alt=""><figcaption></figcaption></figure>
3. **Breakout by App Version**: (Optional)
   * By toggling "Send an alert for every app version," you will get alerted for every app version that has its crash-free user rate drop below the threshold.
4. **Conditions**:
   * If no condition is added, the rule will be applied to the app/app version if it exceeds 100 sessions.
   * You have 3 conditions you can choose from:

     <figure><img src="https://files.readme.io/dbc3e0f-image.png" alt=""><figcaption></figcaption></figure>

     * **App Version**: Select "Top Releases" or "Latest Releases", or specify app versions.

       <figure><img src="https://files.readme.io/554acfe-image.png" alt=""><figcaption></figcaption></figure>
     * **User Count**: minimum number of users the app (or selected versions) has to have for the alert to trigger. Specify "Greater than" or "Less than" to set a number of users and reduce the noise.<br>

       <figure><img src="https://files.readme.io/76472b6-image.png" alt=""><figcaption></figcaption></figure>
     * **Session Count**: The minimum number of sessions the app (or selected versions) has to have for the alert to trigger. Specify "Greater than" or "Less than" to set a number of sessions and reduce the noise.

       <figure><img src="https://files.readme.io/896c89a-image.png" alt=""><figcaption></figcaption></figure>
5. **Forward Alert**:
   * Set the option to forward the alert to your favorite integrated tool.<br>

     <figure><img src="https://files.readme.io/b6b18b5-image.png" alt=""><figcaption></figcaption></figure>
6.

```
<figure><img src="../../../../.gitbook/assets/image (18).png" alt=""><figcaption></figcaption></figure>
```


# Alerting & Automation for Bug Reporting

Automate bug report triage with Luciq rules, so logs, screenshots, and tickets reach your tracking system without any manual handling.

coThe testing process can be a lengthy manual process where the tester/user has to do several actions to submit thorough feedback including:

* Include all relevant screenshots.
* Pull all logs from the device
* Explain how to reproduce the bug
* Manually submit a ticket to the tracking system

Luciq helps you automate this entire process, as well as extra automation capabilities that you can utilize to save time and eliminate a handful of manual tasks.

### Set up your integrations

Luciq natively integrates with plenty of workflow management and messaging tools (Jira, Slack, MS Teams, PagerDuty, ServiceNow, and more), you can easily create an integration from the dashboard by navigating to the setting from the left menu

<figure><img src="https://files.readme.io/0281e99640ff02701316224b5859fe1c23d993d638d21d89695c14fa4ef297d2-product-guides-br-workflow-automation-1.png" alt="2876"><figcaption><p>Settings Menu - Bottom left of the dashboard</p></figcaption></figure>

On this menu you will see a list of all configured integrations, get started by integrating with your current workflow tools. If your tool is not listed here, you can use “Zapier” or “Webhooks” integrations if that tool is compatible with any of those integrations.\
Read more about our integrations [here](https://docs.luciq.ai/product-guides-and-integrations/integrations).

<figure><img src="https://files.readme.io/b9703d0-Bug_Reporting_Workflow_2.png" alt="862"><figcaption><p>Integrations hub</p></figcaption></figure>

After creating your integrations, you shall now start creating rules and alerts, In this section we will cover the possible workflow automation options for bug reporting.

**Workflow Automation Examples**:

* Keep your users updated with the status of their reports.
* Create alerts to get your team notified about any updates.
* Assign issues to the right team members
* Triage and manage issues from your preferred system, no need to manage tickets on multiple tools

### Create your rules

To get started with bug reporting workflow automation, hover the the left navigation menu and click on “Alerts & Rules”

<figure><img src="https://files.readme.io/c5d5cb102c737d0fda99b97bd9a0d2602a7be21b4e8aa797a9a499d37794a515-product-guides-br-workflow-automation-3.png" alt="2874"><figcaption><p>Alerts and Rules page from the Luciq menu</p></figcaption></figure>

You can view a list of all created rules, you can also use the filters to view only rules for bugs, crashes or performance metrics, click on “Create” to get started.

<figure><img src="https://files.readme.io/236c5dea069b0fca154434f9c2d563c023aadabf27b33fde4bf0aa8f12a978d9-product-guides-br-workflow-automation-4.png" alt="2874"><figcaption><p>Use this filter to view previously created rules</p></figcaption></figure>

### Bug Reporting Alert

Select “Bugs” from the dropdown menu under the “For”

<figure><img src="https://files.readme.io/63dce70ac9024ab1edefe7364c15283084c6535fc6561c9f1f4055a448869072-product-guides-br-workflow-automation-5.png" alt="2874"><figcaption><p>Choose a type - "For" - "Bugs"</p></figcaption></figure>

### Triggers

Then you need to select a trigger, below is a list of all available triggers:

* ***Bug is reported***: Get notified or apply several actions when a bug is reported, like forwarding to your ticketing tools, replying to the user and more.
* ***Bug is forwarded***: Get notified or apply other actions whenever a bug is forwarded to any integration
* ***Status changes***: Stay updated whenever the status of a bug report is changed
* ***Assignee changes***: Stay updated whenever the assignee for a bug report is changed
* ***Priority changes***: Stay updated whenever the priority of a bug report is changed
* ***Tag is added***: Apply several actions whenever a tag is added to the bug report. This can be used as a quick and easy way to automate any workflow simply by adding a tag to the bug report

<figure><img src="https://files.readme.io/9affe4b94f075aa246db12b02b1b49e205f1ac6385dc1e6451d269753d64710b-product-guides-br-workflow-automation-6.png" alt="2874"><figcaption><p>Choose a trigger</p></figcaption></figure>

### Conditions

After selecting the trigger, you can select a set of conditions that need to be met for the rule to be triggered.

{% hint style="warning" %}
**Make sure you set the condition**

If you do not select any conditions, the rule will be applied to any reported bug.
{% endhint %}

**Below is a list of all available conditions**:

* ***Title***: Specify if the bug description should include any keywords for the rule to be triggered.
* ***Reporter’s email***: Can be used if you want to focus on reports coming from internal testers.
* ***App version***
* ***Current View***: The screen used right before reporting the bug (screen affected by the bug).
* ***Categories***: These can be the main categories (Report a bug, Suggest an improvement, Ask a question) or one of the custom report categories you use.
* ***Tags***: Tags can be added manually to a bug report, or [automatically added through code](https://docs.luciq.ai/references/report-data/tags/add-tags).
* ***Device***: Device type/name used to report the bug.
* ***Status***: The status of the bug report on the dashboard.
* ***Priority***: The priority of the bug report on the dashboard.
* ***Assignee***: The team member assigned to the bug report on the dashboard.
* ***OS***: OS type/name used to report the bug.
* ***Location (City/Country)***: Specify the location of the reporter as a condition.
* ***User attributes***: Specify conditions for the user attributes (e.g login status, paying status, user ID and more). Know how to add user attributes [here](https://docs.luciq.ai/ios/setup-luciq-for-ios/custom-settings/user-identification/user-attributes).

You can add as many conditions as you see fit, you can also choose to “AND” or “OR” the selected conditions.

<figure><img src="https://files.readme.io/8b518b7b746709d6be65a483fbd1c0f2b228807f082cc74cb9518de7b1d5f465-product-guides-br-workflow-automation-7.png" alt="2874"><figcaption><p>Choose one of the conditions</p></figcaption></figure>

### Alerting channels

The last thing you need to do is specify the actions you want to automate using this rule. There are various actions available:

* ***Forward it to***: Forward the bug report to any of the setup integrations (Slack, Jira, Zendesk, Github and more). See more info about available integrations and how to set up here
* ***Reply to user***: Send an in-app message to the reporter to update them on the status of the report, ask them further questions and more. This is a 2-way conversation
* ***Assign to member***: Automatically assign bugs to the right team member
* ***Change its status to***: Change the status of the report to one of: New, In-progress & Closed. This can be used to close reports coming from very old app versions
* ***Change its priority to***: Change the priority of the report (Trivial, Minor, Major, Blocker)
* ***Tag it with***: Automatically add tags to a report
* ***Delete it***: Automatically delete a report, can be used to delete reports coming from very old app versions
* ***Send email to***: Send an email to a dashboard member(s)

You can add as many actions as you see fit, helping you eliminate several manual tasks using a single rule.

The below rule example is going to evaluate each bug report against the specified conditions, if they’re met then the bug will be forwarded to the Jira project, change the report status to in-progress and send an automated reply to the user.

<figure><img src="https://files.readme.io/4010aa0717884b9212ff38b95274d21612cbcbba645d1ec724a8627eacd1c54c-product-guides-br-workflow-automation-8.png" alt="2874"><figcaption><p>Choose one of the actions, for example: forward to the relevant Jira project</p></figcaption></figure>

Finally you need to provide a title to the rule and click “Save”, you can also assign this rule to a team for ownership and to easily find rules related to your team. [You can create teams on the dashboard here](https://dashboard.luciq.ai/company/teams).

<figure><img src="https://files.readme.io/a70dbb7ff74e659b8c83d73816bc4868a931d185d1bba11a15adf55caf551192-product-guides-br-workflow-automation-9.png" alt="2876"><figcaption><p>Choose the team that owns this rule</p></figcaption></figure>

Now let’s discover different scenarios for workflow automations, and how you can use the rules to achieve this:

***Scenario A***:\
You have an internal testing program and you’re using Luciq to streamline the feedback process, and you want to forward all bugs reported by the company members to the designated Jira project, receive Slack notifications and add the appropriate tag(s) to the report

<figure><img src="https://files.readme.io/179f7eedb8e6936024ab6e5a7192908374799c5b521ea8d247ed3ab60eef1842-image.png" alt=""><figcaption></figcaption></figure>

***Scenario B***:

You want to be connected to your users as much as possible, you want to send an auto reply to them once they report an issue, and also keep them posted with any updates that happen on the report.

The below rule will send an auto reply to the reporter

<figure><img src="https://files.readme.io/b69d3171de419bd438dfe167a55f41015dbc38eb766dd885abbac935c92ed975-image.png" alt=""><figcaption></figcaption></figure>

You can also leverage the report tags, which can be used as a quick way to apply several actions, like updating the user once any action is taken on the bug report.

Below are a couple of examples on how to utilize tags, the relevant actions will be applied once you add the appropriate tag to the bug report.\
The below two examples will send a different message to the user based on the added tag

<figure><img src="https://files.readme.io/7f32bb1b1f2413dd5c81d25014b8e691ab185320d50d84b47dfa5e9ec0df5c88-image.png" alt=""><figcaption></figcaption></figure>

If you need further assistance on setting up rules, please feel free to contact our [support team](mailto:contactus@luciq.ai).


# Alerting For Crash Reporting

Set crash alerts on your own thresholds and control which crashes notify you and where, from a first occurrence to a spike in a release.

Customize your alerts on your favorite tools.\
You can set up your alerts based on your own thresholds and control which crashes to get notified on.Luciq enables you to customize your alerts to cover your use cases of when you’d like to get alerted and where to get alerted.

### Alerts and Rules

If you’d like to get alerted as soon as you receive any crash, you will be able to do that through our “Alerts and Rules” engine.

Go to “Alerts and Rules”

<figure><img src="https://files.readme.io/4cc11d6cf4d272bf6d5300eb2f60c45c362cdd1828b315b2c14a73550db12432-product-guides-crash-reporting-alerts-1.png" alt="2874"><figcaption><p><em>Go to the Alerts &#x26; Rules page from the Luciq menu</em></p></figcaption></figure>

That will redirect you to our Alerts and Rules engine, now you can click on Create to start creating a new rule:

<figure><img src="https://files.readme.io/5ab761f026721fafe636b69905bafb8a7ecc5cddbcb457979c07f330773feea4-product-guides-crash-reporting-alerts-2.png" alt="2874"><figcaption><p><em>Click on "Create"</em></p></figcaption></figure>

That will take you to the Rules engine where you can control the conditions for which you’d like to be alerted, and the integrations where you’re alerted.

#### Alert type

Since we’re talking about Crash Reporting use cases, select “Crashes” from the first drop down list and then you have several use cases you can apply.

<figure><img src="https://files.readme.io/3797a44dcb8dcd5690e953d8872f4666820a3a34a43f4a8978208c2e77d736f0-product-guides-crash-reporting-alerts-3.png" alt="2874"><figcaption><p><em>Select "Crashes"</em></p></figcaption></figure>

#### Alert Triggers

The covered use cases to be alerted for crashes are:

* When a crash is first seen
* When a new occurrence of an existing crash is seen
* When regression is detected: when a closed crash is reactivated on a newer app version
* When a spike is detected:\
  \- Number of occurrences within time exceeds a certain threshold\
  \- Number of affected users within time exceeds a certain threshold\
  \- Number of occurrences and affected users within time exceeds a certain threshold (a combination of the\
  above 2 spikes is detected)
* When 1% of the sessions of the app crash in the last 24 hours

<figure><img src="https://files.readme.io/61092b5ec3d55792a58167256263c30c023e79a463995c1bd98d7e4d809117ba-product-guides-crash-reporting-alerts-4.png" alt="2874"><figcaption></figcaption></figure>

All Crash Reporting triggers

Now let’s take an example for each one of the use cases and walk you through how to set it up:

### Use Cases for Crash Alerts

#### A crash is first seen

You’d like to be alerted whenever a crash is first seen. An example how this can be used:\
As soon as you release a new version, you can get notified the moment a crash appears on the app.

You would be able to satisfy this use case by setting a rule as shown on the following screenshot:

<figure><img src="https://files.readme.io/c92584fef21c138ea729f887c67afd8e03e37dbc89c7596cc90ae88f6d77e83d-product-guides-crash-reporting-alerts-5.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as a crash is detected</em></p></figcaption></figure>

To create teams and specify Team Ownership, please refer to our [Team Ownership Product Guide](/product-guides-and-integrations/product-guides/automation-and-workflows/team-ownership).

#### When a new occurrence of an existing crash is seen

You can get alerted whenever a new occurrence of an existing crash is seen. If a crash is persistent and affects more than a single user, this may need your attention. You can set that up by changing the trigger to “A new occurrence of an existing crash is seen”

<figure><img src="https://files.readme.io/7775d4f0fc46c19e714c74c0c7b7cb87a130eb551982ff1a8b94043af1899a01-product-guides-crash-reporting-alerts-6.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as a new occurrence on an existing crash is seen</em></p></figcaption></figure>

#### Regression detection - when a closed crash is reactivated on a newer app version

This makes sure that you can see regression as soon as it happens. If you had resolved a crash on a previous app version and it reappears on a newer version, you can set up that alert. From your triggers list, select: "A closed crash is reactivated on a newer app version”

<figure><img src="https://files.readme.io/7130ffe8af92d6340d89f69836faaeb0f0aef1916e607907c796ff56c3c5f4cf-product-guides-crash-reporting-alerts-7.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as a regression is detected</em></p></figcaption></figure>

#### Spike detection - by number of occurrences or by number of affected users, or both

Often, if a spike in the number of occurrences happens, you'd like to get alerted. Whether that spike is in the number of occurrences or the number of users affected by those crashes, you have control over that threshold.\
\&#xNAN;*Number of occurrences within time*\
You can control this by choosing the trigger to be “Number of occurrences within {time}” so you would control the time frame for which this becomes alarming and the number of occurrences that would be worth an alert.\
The following screenshot describes what it would look like.

<figure><img src="https://files.readme.io/a54f672a4b8ca12186840428c26c86b042059fa58b724c10b9bcd722edd0e617-product-guides-crash-reporting-alerts-8.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as the number of occurrences exceeds a threshold</em></p></figcaption></figure>

#### Number of affected users within time

When the number of users concern you and you’d like to get alerted whenever a threshold is surpassed, you can choose the trigger to be “Number of users with {time}”

<figure><img src="https://files.readme.io/d06150ae5b612e14aeef5327c5df3a14920201b54b27e28938f80c18dc87db73-product-guides-crash-reporting-alerts-9.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as the number of affected users exceeds a threshold</em></p></figcaption></figure>

#### Number of occurrences and affected users within time

You can have a combination of both Number of occurrences and Number of affected users as your threshold.

<figure><img src="https://files.readme.io/4a13c7af8c56fbae69c3c6640bf554e6a9fb8bb8935da9b5cfaa58a75955b6a8-product-guides-crash-reporting-alerts-10.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as the number of occurrences and the number of effected users exceed a threshold</em></p></figcaption></figure>

#### When a crash affects 1% of your current app version’s sessions in the last 24 hours

According to our user’s research, a lot of the developers will only start worrying about a crash if it has affected more than 1% of the sessions on a specified day. Luciq also covers this use case by giving you the option to be notified if that condition is satisfied.

<figure><img src="https://files.readme.io/6bda148c14b0e574e0944e8a94415bc51e8be4352fffe99cfd25f98d961b797a-product-guides-crash-reporting-alerts-11.png" alt="2874"><figcaption><p><em>Choose this trigger if you want to be notified as soon as a crash affects at least 1% of an app version's sessions in the last 24 hours</em></p></figcaption></figure>

### Alert conditions

Along with the use cases mentioned above, you can control the way your team is being alerted by targeting specific parts of the app, specific teams, versions, tags, priorities, types of crashes or even experiments running on the app.\
Below is a list of the attributes that you can use within your rule to satisfy that, you can also use more than a single attribute on the same rule, (for example, if you’d like to be alerted regarding Out Of Memory crashes on a specific app version).

Attributes:

* App version
* Exception Message
* Team
* Path
* Filename
* Crash type
* Tags
* Status
* Assignee
* Priority
* Total affected users
* Total occurrences count
* Experiments
* App status

<figure><img src="/files/u8rSgYUR07WhHAaADSui" alt=""><figcaption><p><em>A list of conditions that you can choose from to have finer control over your alerts</em></p></figcaption></figure>


# Automatic Detection for Accelerating Crashes

Let Luciq automatically detect accelerating crash patterns in your mobile app. Get alerted when a previously rare crash suddenly spikes in occurrence.

### Overview

Luciq's new automatic detection of accelerating crashes alert type allows you to safely ignore non-critical crashes by notifying you only when a crash starts accelerating. This feature ensures that you can focus on significant crashes that require immediate attention without being overwhelmed by sporadic, non-critical crashes.

#### Use Case

Consider the scenario where a crash occurs intermittently. Initially, this crash might not warrant immediate action. However, if the frequency of this crash begins to increase rapidly, it could indicate a growing problem that needs addressing. With automatic detection of accelerating crashes, Luciq monitors the rate of a crash’s occurrences and alerts you when there is a notable acceleration.

**Examples:**

1. **Sudden Spike in Crash Occurrences**:
   * In this graph, you can see a crash occurrence that suddenly spikes. Luciq detects this rapid increase and triggers the accelerating crash alert.<br>

     <figure><img src="https://files.readme.io/6c19ec6-image.png" alt=""><figcaption></figcaption></figure>
2. **Stable Crash starting to accelerate**:
   * This graph shows a crash that was stable for a while, started to accelerate, stabilized again, and then accelerated once more. We identify both acceleration patterns and notify you accordingly.<br>

     <figure><img src="https://files.readme.io/b7d0e17-image.png" alt=""><figcaption></figcaption></figure>

#### Technical Details

We run an algorithm on incoming crashes to analyze crash patterns and identify accelerations. We do so by comparing the occurrence of each particular crash against its historical average, determining whether there is a significant increase. The decision-making process is outlined in the flow chart below:

<figure><img src="https://files.readme.io/6fdfd8b-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
The default values used in this flow chart are configurable to fit your company's specific needs. Please contact our customer support team (<support@luciq.ai>) for any customization requests, and we will adjust these settings accordingly.
{% endhint %}

### Alert Creation

You can create a new alert for detecting accelerating crashes by following these steps:

1. Go to the Alerts & Rules section in your Luciq dashboard.
2. Select **“For → Crashes”**.
3. Choose **“Trigger → A crash is accelerating”**.

<figure><img src="https://files.readme.io/ba6302f1520d5821fc28462967b30ab8b41d27e765854ccffeedbe21e6c75195-image.png" alt=""><figcaption></figcaption></figure>

#### Available Alert Conditions

To provide you with precise control over which accelerating crashes trigger alerts, we've included several customizable conditions:

* **Exception Message**: Specify the exception message text of crashes you want to be alerted on.
* **Path (Package)**: Get alerted on crashes occurring within specific packages or paths.
* **Filename**: Focus on crashes originating from particular files.
* **Crash Type**: Differentiate between various crash types (e.g., OOMs, ANRs).
* **Tags**: Filter crashes based on assigned tags.
* **Priority**: Restrict alerts to crashes with a specific priority.
* **Team**: Restrict alerts to crashes owned by specific teams.
* **Assignee**: Restrict alerts to crashes owned by specific team members.
* **Total Occurrences Count Greater Than X**: Only evaluate crashes with a total occurrence count above a set threshold.
* **Total Affected Users Greater Than X**: Only evaluate crashes impacting more than a specified number of users.

#### Available Actions

Once an accelerating crash is detected, you can choose from a variety of actions to ensure prompt resolution:

* **Forwarding**:
  * **Jira**: Automatically create and assign Jira issues.
  * **Slack**: Send notifications to specific Slack channels.
  * **PagerDuty**: Trigger incidents in PagerDuty.
  * **Webhook**: Integrate with custom webhooks.
  * **MS Teams**: Notify teams via Microsoft Teams.
* **Email**: Send detailed crash reports via email to designated recipients.
* **Assigning to Team/Member**: Automatically assign the crash to the relevant team or team member.
* **Changing Status/Priority**: Update the status or priority of the crash for better tracking.
* **Adding a Tag**: Tag the crash for easier identification and filtering.

By leveraging these alert conditions and actions, you can efficiently manage and resolve accelerating crashes.

### Tracking the Accelerating Crash

Once an accelerating crash is detected and the alert is triggered, it will be reflected on the **Triggered Alerts** page in your Luciq dashboard. You can track and manage all triggered alerts from this page, ensuring that you stay informed about any accelerating crashes and take appropriate action in a timely manner.


# Alerting and Automation for Rollout Management

Automate a staged rollout with Luciq alerts and rules. Keep the team posted on release progress and triage regressions without checking.

Luciq helps you automate the entire rollout process, as well as extra automation capabilities that you can utilize to save time and eliminate a handful of manual tasks.

**Workflow Automation Examples**:

* Keep your team updated with the status of the rollout.
* Create alerts to get your team notified about any updates.
* Triage and manage your rollout without the need to use multiple tools.

### Create your rules

To get started with rollout management workflow automation, hover the the left navigation menu and click on “Alerts & Rules”

<figure><img src="https://files.readme.io/4cc11d6cf4d272bf6d5300eb2f60c45c362cdd1828b315b2c14a73550db12432-product-guides-crash-reporting-alerts-1.png" alt="2874"><figcaption><p><em>Go to the Alerts &#x26; Rules page from the Luciq menu</em></p></figcaption></figure>

You can view a list of all created rules or performance metrics, use the filters to view only rules for bugs, crashes, performance metrics or release rollout, and click on “Create” to get started.

<figure><img src="https://files.readme.io/5ab761f026721fafe636b69905bafb8a7ecc5cddbcb457979c07f330773feea4-product-guides-crash-reporting-alerts-2.png" alt="2874"><figcaption><p><em>Click on "Create"</em></p></figcaption></figure>

### Rollout Management Alert

Select “Release Rollout” from the dropdown menu under the “For”.

<figure><img src="https://files.readme.io/3797a44dcb8dcd5690e953d8872f4666820a3a34a43f4a8978208c2e77d736f0-product-guides-crash-reporting-alerts-3.png" alt="2874"><figcaption><p><em>Select "Crashes"</em></p></figcaption></figure>

#### Triggers

Then you need to select a trigger; below is a list of all available triggers:

* **Health Metrics**:
  * ***Apdex***: Halt/Pause or Release the Rollout process based on your application's apdex score within a period of time for a specific or all app versions.
  * ***Crash-Free Sessions***: Halt/Pause or Release the Rollout process based on the Crash-Free percentage within a period of time for a specific or all app versions.
* **Rollout Progress**:
  * ***Rollout Status Change***: Get notified whenever the rollout status is changed for a specific or all app versions.
  * ***Rollout Percentage Change***: Get notified whenever the rollout percentage is changed for a specific or all app versions.
  * ***Daily Rollout Summary***: Get notified if the daily rollout summary is changed for a specific or all app versions.

<figure><img src="https://files.readme.io/c2da648ac7e464b8b81957bd69d7b742eb01dbb07d3b6e98246f5524ad3df3f2-product-guides-alerting-and-automation-for-rollout-management-4.png" alt="Choose a Trigger"><figcaption><p><em>Choose a Trigger</em></p></figcaption></figure>

#### Conditions

After selecting the trigger, you can select a set of conditions that need to be met for the rule to be triggered.

**Below is a list of all available conditions**:

* ***Version Adoption Percentage***: Check if the version adoption percentage is greater or less than a specified percentage.
* ***Version Rollout Percentage***: Check if the version rollout percentage is greater or less than a specified percentage.
* ***Rollout Status***: The status of the rollout is one of the following: Started, Halted/Paused, Resumed, or Completed.

You can add as many conditions as you see fit. You can also choose to “AND” or “OR” the selected conditions.

<figure><img src="https://files.readme.io/d387df3f520adb726c2ab7959e89879327e9eab7d5bdfde3c35792ab078bd283-product-guides-alerting-and-automation-for-rollout-management-5.png" alt="Choose one of the conditions"><figcaption><p><em>Choose one of the conditions</em></p></figcaption></figure>

#### Alerting channels

The last thing you need to do is specify the actions you want to automate using this rule. There are various actions available:

* ***Change status to***: Change the status of the rollout to one of Halt/Pause & Release to All.
* ***Send email to***: Send an email to a dashboard member(s).
* ***Forward it to***: Get notified on Slack if you have an integration set up on your dashboard.

<figure><img src="https://files.readme.io/5813222806c6e5c276456fdf0cd4490df1ce1309a40b4791682277d77afae04f-product-guides-alerting-and-automation-for-rollout-management-6.png" alt="Choose an action to be performed"><figcaption><p><em>Choose an action to be performed</em></p></figcaption></figure>

### Use Cases

Now let's discover different scenarios for workflow automation that help track your app's health, like Apdex and Crash-Free Sessions, if they reach a specific threshold to get notified and how you can use the rules to achieve this:

***Scenario A***:

You have a version being rolled out to your users, and if you're worried it might cause crashes, you can set up an automation that would automatically pause the rollout if the crash-free sessions rate falls below 97% in a 24-hour window to stop the issue from affecting any other customers while you work on a fix.

<figure><img src="https://files.readme.io/dffe46f18968a8774ed84209c59ec8ac897a3c7eefe3c5b1e4bf9659075f5463-product-guides-alerting-and-automation-for-rollout-management-1.png" alt=""><figcaption></figcaption></figure>

***Scenario B***:\
You can set an automation that keeps track of the crash-free sessions for your application, and if a release is performing as well as you expect (e.g., not facing any crashes), you can automatically have it released to all users to quickly increase its adoption.

<figure><img src="https://files.readme.io/4e9295cd41719642ee2caef1cf684f691585874b256946e904a3d1a8b45a78c0-product-guides-alerting-and-automation-for-rollout-management-4.png" alt=""><figcaption></figcaption></figure>

***Scenario C***:

You can stay up to date whenever the rollout status changes to either Started, Halted / Paused, Resumed, or Completed. This can be done by setting up a Slack integration and getting notified on your preferred channel.

<figure><img src="https://files.readme.io/1ad18c065c7305658dd2ffb938274581d1266be9c83f3291fded70083b9df945-product-guides-alerting-and-automation-for-rollout-management-2.png" alt=""><figcaption></figcaption></figure>

***Scenario D***:

You can get a daily update on your release rollout health and progress by sending an e-mail to all team members who are on the dashboard or a selection of them upon your preference.

<figure><img src="https://files.readme.io/12ab02a3b96335cea5a066bb1ad4c970908be6c781b517458680989db1a08f01-product-guides-alerting-and-automation-for-rollout-management-3.png" alt=""><figcaption></figcaption></figure>

If you need further assistance in setting up rules, please feel free to contact our support team.


# Alerting for Performance Metrics

Configure Luciq alerts for mobile app performance metrics. Get notified when app launch times, screen loading, or network performance degrades.

Luciq [App Performance Monitoring](https://www.luciq.ai/product/app-performance-monitoring) is continuously monitoring application performance on the client-side, giving you insights on the following metrics:

* App launch times
* Client-side network health & latency
* Screen loading time
* UI hangs
* Flows

These metrics directly impact the user experience, helping you identify performance issues before they affect more users. Instead of paying regular visits to the dashboard to check for any performance issues, you can set up alerts to immediately get notified on Slack or email about any performance drops.

### Alerts and Rules

To get started with setting up performance alerts, hover over the left navigation pane and click on “Alerts & Rules”

<figure><img src="https://files.readme.io/9a598e833b2bf96afabb890c1f5a16f7a4c90beaebb68f293649631c7b138050-product-guides-alerting-for-performance-metrics-1.png" alt="2874"><figcaption><p><em>Alerts and rules from the Luciq menu</em></p></figcaption></figure>

Now you can see a full list of all alerts & rules that you previously set up (if any), get started and create a new rule/alert by clicking on the “Create” button

<figure><img src="https://files.readme.io/06ed9ac05b0042f9171a62ee4ef83b9ff17819fb50009c8a2eba730b4473e8e7-product-guides-alerting-for-performance-metrics-2.png" alt="2874"><figcaption><p><em>Click on "Create"</em></p></figcaption></figure>

#### Alert types

**Select the performance metric**:\
You can set up alerts for each of the performance metrics individually, under the “For” , select the performance metric of interest from the dropdown list

<figure><img src="https://files.readme.io/0c955e69c66e51eb00c11e008afb8a447eb925b53332abec40ab962f0dd8af49-product-guides-alerting-for-performance-metrics-3.png" alt="Choose the relevant performance metric for which you want to be alerted"><figcaption><p><em>Choose the relevant performance metric for which you want to be alerted</em></p></figcaption></figure>

Let’s choose “Network” as an example, then you need to select the trigger that you want to be notified for (triggers vary based on the selected performance metric\*)

#### Alert triggers

The alert trigger can be one of the following:

* **P95 (95th percentile)**: The maximum latency encountered by 95% of the users for the selected metric
* Insert the desired threshold that you want to get notified about if exceeded (3 sec for example)
* Select the time range that you want to be taken into account for this alert

<figure><img src="https://files.readme.io/60b684759dc868461435add0ae72fc560f57af6bef066d65e2b3060543742c1a-product-guides-alerting-for-performance-metrics-4.png" alt="2874"><figcaption><p><em>Choose the trigger and time range - Example P95</em></p></figcaption></figure>

**Apdex change rate**: Get notified whenever your Apdex score changes by a certain percentage over a specific period of time.

* Insert the desired threshold for the Apdex change rate, the below example will notify you when the Apdex score drops by 10%
* Select the time range that you want to be taken into account for this alert

<figure><img src="https://files.readme.io/d9aa56aa8bba0848fe83616cafc71622fc25d7a361062435d19f7325bda35fc3-product-guides-alerting-for-performance-metrics-5.png" alt="2874"><figcaption><p><em>Choose the trigger and time range - Example Apdex change rate and time range</em></p></figcaption></figure>

**Failure rate** (network only): Get notified whenever failure rate for network requests exceeds a certain threshold

* Insert the desired threshold for network failure rate, the below example will notify you when network failures exceeds 10%
* Select the time range that you want to be taken into account for this alert

<figure><img src="https://files.readme.io/06dfed92b3be4b6b7db66a6c4de546c2e59596c7348cd7345c2155d0921ee575-product-guides-alerting-for-performance-metrics-6.png" alt="2874"><figcaption><p><em>Choose the trigger and time range - Example Failure rate and time range</em></p></figcaption></figure>

**Apdex**: Get notified whenever the Apdex score for the selected performance metric drops below a certain threshold

* Insert the desired threshold for the Apdex score, the below example will notify you if Apdex dropped below 0.7
* Select the time range that you want to be taken into account for this alert

For more information on Apdex definition and calculation, please check the docs [here](https://docs.luciq.ai/product-guides-and-integrations/product-guides/getting-started-with-luciq/frustration-free-sessions).

<figure><img src="https://files.readme.io/ce386d1cb4f92423275332f854c16a5bfb0e61cac67f337acb044ac414247dbd-product-guides-alerting-for-performance-metrics-7.png" alt="2874"><figcaption><p><em>Choose the trigger and time range - Example Apdex and time range</em></p></figcaption></figure>

#### Alerts conditions

After setting the triggers for alerting, you can also add some conditions, these conditions need to be met in order to fire this alert (conditions vary based on the selected performance metric\*).

You can specify the desired conditions under the “If” section, click “Add Conditions” to select from the available conditions. You can add as many conditions to fulfill your use case.

{% hint style="warning" %}
Note: If you didn’t add any conditions, the rule will apply on the selected metric when the number of occurrences exceeds 100
{% endhint %}

Below is a list of available conditions:

* App version: If you are interested in monitoring a specific app version (e.g your latest release or top releases)
* Trace name: This can be a specific screen name, network URL or a flow that you want to monitor (e.g home screen, payment API or checkout flow)
* Key metric: Choose to get notified only on metrics that you define as “Key metrics”, or the ones that are not. See more info about key metrics [here](https://docs.luciq.ai/product-guides-and-integrations/product-guides/getting-started-with-luciq/frustration-free-sessions/how-to-configure-frustration-free-sessions#control-apm-traces-in-your-score)
* Count: Define a threshold for the occurrences count of the selected metric that needs to be met
* Method: This applies on network alerting only, you can select the network request method as a condition for the alert (GET, PUT, POST, PATCH, DELETE)
* Launch Type: This applies on app launch only, you can select between cold or hot app launches

Example: The below rule will fire an alert when the P95 exceeds 3 seconds within 1 day, the alert will be fired only when all the conditions are met

<figure><img src="https://files.readme.io/9bd9b889f939b104a1ca3f070acfd323822c60051fe0c10cc3dbbdb99e2f9610-product-guides-alerting-for-performance-metrics-8.png" alt="2876"><figcaption><p><em>Choose the condition that should be checked to get alerted</em></p></figcaption></figure>

#### Alerting channels

Alerting for performance events is currently supported through Slack, MS Teams and Emails. You can select multiple channels for a single rule

<figure><img src="https://files.readme.io/ac329461e1a8be4f36a5ab2c4dbc9f5fb75613be844e4ae9acb886a410b49942-product-guides-alerting-for-performance-metrics-9.png" alt="2876"><figcaption><p><em>Forward your alert to slack</em></p></figcaption></figure>

Finally, all you need to do is assign this rule to your team (optional for ownership), provide a title for the rule and click “Save”

<figure><img src="https://files.readme.io/6497f5341af1c1fc4886f48defe73464be0d87c40fe16ac2839dbf606ab86dcd-product-guides-alerting-for-performance-metrics-10.png" alt="2876"><figcaption><p><em>Optional - Choose the team that is responsible for this alert. And click "Save"</em></p></figcaption></figure>

The below matrix shows the different triggers & conditions for each metric

<figure><img src="/files/6ZKQIluRkg2h8D9dlHBf" alt=""><figcaption><p><em>Triggers, conditions and Actions</em></p></figcaption></figure>


# Network Failure Alerts

Alert on failed network requests by failure type, separating client-side errors from server-side ones so your team knows where to look.

Luciq now gives you more control and precision when setting up alerts for failed network requests. With our enhanced alerting capabilities, you can receive alerts tailored to specific types of failures, allowing your team to act faster and more efficiently.

### 🔍 Filter Alerts by Failure Type

You can now specify whether the alert should trigger for:

* **Client-Side Failures:** Failures occurring on the user's device (e.g., timeout, no internet).
* **Server-Side Failures:** Failures returned from your backend (e.g., 500 Internal Server Error).

This gives you the ability to monitor the type of failures that matter most to your team.

<figure><img src="https://files.readme.io/b97abb5a6918a1ba0f2deff95ab68af06ea9b89415eb0fce53e2f775a9f347c2-image.png" alt=""><figcaption></figcaption></figure>

### 🎯 Add Extra Filters for Granularity

In addition to filtering by failure type, you can further narrow down alerts using the following criteria:

1. **HTTP Status Code**

Include/Exclude specific HTTP response codes in your alerts, such as:

* `500` for internal server errors
* `404` for not found
* `401` for unauthorized access<br>

  <figure><img src="https://files.readme.io/6319ea0f31ee4ade9d6b7818a997ee69bb3dee2b3ac257337c160c1dfd858326-image.png" alt=""><figcaption></figcaption></figure>

2. **Failure Name**

Use the failure name to detect specific issues, especially useful when failure codes are not available (as is often the case with client-side issues).

> **What is a Failure Name**?
>
> A failure name is the descriptive message sent alongside a failure event. You can view failure names by:
>
> * Navigating to the **Network tab** on your dashboard
> * Selecting the network request of interest
> * Scrolling to the **Failures** section
> * Checking the value under the **Name** column

This enables you to create highly targeted alerts—like catching `NoConnectionError` or `TimeoutException` events.

<figure><img src="https://files.readme.io/a7fc972a59271fc122cb837fd0ceaedd0fca610cd8277e93b29b0447cdb7e01c-image.png" alt=""><figcaption></figcaption></figure>


# Predefined Alerts

Use Luciq's predefined alert templates to set up monitoring for common mobile issues. Get notified of crashes, performance degradation, and stability drops.

Luciq creates predefined alerts for a few of the metrics that the SDK measures. These alerts are automatically enabled upon creating a new app environment and aim to give you a head start to creating your own. By default, notifications for these alerts are sent via email but this can be customizable to your preferred alerting channel like Slack or Microsoft Teams.

### Crash Free Rates

Predefined alerts are set for both the crash-free sessions and crash-free users metrics. These alerts notify you when either the crash-free sessions or crash-free users percentages drop below 99. Notifications for those alerts will be sent via email to all users on the dashboard. The alerts are refined to only the latest and top releases of your app to avoid spamming.

<figure><img src="https://files.readme.io/e8393f6-CFS2.png" alt="Crash-free sessions predefined alert"><figcaption><p><em>Crash-free sessions predefined alert</em></p></figcaption></figure>

### Crash Reporting

A default crash reporting alert is created that checks if a crash is affecting 1% of total sessions in the last 24 hours for any of the latest or top releases. When triggered, an email is sent to all users on the dashboard.

<figure><img src="https://files.readme.io/f73dafa-1percent2.png" alt="Predefined alert for a crash affecting 1% of sessions"><figcaption><p><em>Predefined alert for a crash affecting 1% of sessions</em></p></figcaption></figure>

Setting a dynamic threshold using a percentage rather than an absolute session count accounts for variations in different app sizes and user traffic fluctuations.

### Rollout Management

Luciq creates an alert to keep your dashboard admins and owners up to date on any changes that happen to the rollout status of active releases.

<figure><img src="https://files.readme.io/4cd2a65-rollout2.png" alt="Rollout status change predefined alert"><figcaption><p><em>Rollout status change predefined alert</em></p></figcaption></figure>

### Performance Metrics

Recommended alerts for performance metrics are available out of the box, so you can get started immediately after you finish integrating the Luciq SDK.

**Network Apdex**: Get notified when the network Apdex is **less than 0.7** within 1 day.

* The alert is refined to only key metrics and for APIs with a count greater than 1000.

**Network Failure Rate**: Get notified when the failure rate for a network API is **more than 10%** within 3 hours.

* The alert is refined to only key metrics and for APIs with a count greater than 1000.

**Screen Loading Apdex**: Get notified when the screen loading Apdex is **less than 0.7** within 1 day.

* The alert is refined to only key metrics and a default occurrences count greater than 100.

**UI Hangs Apdex**: Get notified when the UI hangs Apdex is **less than 0.7** within 1 day.

* The alert is refined to only key metrics and a default occurrences count greater than 100.

**App Launch Apdex**: Get notified when the app launch Apdex is **less than 0.85** within 1 day.

* The alert is refined to only key metrics and a default occurrences count greater than 100.

These alerts act as built-in examples which then can be disabled, deleted or customized to match your application performance requirements.


# Triggered Alerts

Set up triggered alerts in Luciq to get notified about specific events in your mobile app. Configure conditions, thresholds, and notification channels.

Luciq [Crash Reporting](https://www.luciq.ai/product/crash-reporting) and [App Performance Monitoring](https://www.luciq.ai/product/app-performance-monitoring) automatically create a triggered alert report whenever any of your alerts generated from Luciq for overall app, crashes, app launch, screen loading, network, execution traces, or UI hangs are triggered.

### Triggered Alerts List

The alert list displays all performance alerts that have been triggered, along with their type and other relevant details.

Open alerts are highlighted in red, while resolved ones are highlighted in green.

<figure><img src="https://files.readme.io/1cdca5518c243d7a1722427a5deb6a3fba632b2ec00488002397bcdde8dbb0d4-product-guides-incidents-1.png" alt=""><figcaption></figcaption></figure>

### Alert details

Selecting any alert from the list will take you to its details page, where you can view more details about the event, its occurrence over time, and the conditions that triggered it.

Triggered alerts are automatically resolved when the triggering conditions are no longer being met and automatically reopened when they are met again.

You can also manually resolve alerts. Alerts that were manually resolved can be manually reopened but will not be automatically reopened when the triggering conditions are met again.

<figure><img src="https://files.readme.io/4f8ff0c41f93302cdf2435cfc6c7934d089dcca32271656e60cbb7a5db970d0a-product-guides-incidents-2.png" alt=""><figcaption></figcaption></figure>


# Copying Alerts & Rules

Reuse existing Luciq alerts and rules across apps and environments. Import and adapt conditions, triggers, and actions instead of rebuilding.

Luciq enables you to seamlessly reuse existing alerts and rules across your applications and environments on Luciq. This feature helps you:

* Reduce setup time for new apps or environments
* Preserve consistent alerts & rules across your applications

Instead of manually recreating conditions, triggers, and actions, you can import and adapt rules with just a few clicks.

### When to Use This Feature?

Use import when:

* Launching a new app or environment (e.g., staging → production)
* Scaling your mobile stack across multiple platforms (iOS ↔ Android)
* Standardizing alerting across development squads

### How to Import Alerts & Rules

#### 1) Open Alerts & Rules

* Navigate to your app → **Alerts & rules** page
* Select **Import alerts & rules**

  <figure><img src="https://files.readme.io/0bc8a1bde63dc0187906bddc2b12ddc54601e0aa6e286ae68d4802d78a81b3b5-image.png" alt=""><figcaption></figcaption></figure>

#### 2) Select Source App and Environment

{% hint style="info" %}
You can only import user defined alerts created by your team members, not the predefined alerts created by Luciq
{% endhint %}

You will see your available apps and environments, each showing the number of alerts available to import.

* Search by app or environment name
* Select the application environment you want to import alerts from
* Environments with **0 user-defined alerts** appear disabled

  <figure><img src="https://files.readme.io/a3226330af97a6f1c67094e6883a7639aff7b407dec190c76aa0d58add0a45e2-image.png" alt=""><figcaption></figcaption></figure>

#### 3) Choose Alerts & Rules to Import

{% hint style="info" %}
Some alerts and rules could have some application or environment specific configuration, These alerts would have a tag “Needs update” to highlight that you need to update the configuration of the alert post importing to match it with your current application or environment attributes.
{% endhint %}

On the selection screen:

* Select specific alerts, or choose **All alerts & rules**
* Alerts with missing or app-specific fields display a **Needs update** label
* Click **Import alerts & rules** to complete the import.

  <figure><img src="https://files.readme.io/e8eae752b35994450d227887018617fcb16715e9dbf956cf0941a752eb621b5b-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Some rules may include app-specific logic. These are still imported but require updates to activate.
{% endhint %}

#### Unsupported Import Cases

Rules marked **Not supported** cannot be imported due to incompatible configuration types. These remain visible during import with an explanation tooltip.

Example: You cannot import an OOM Alert to an Android app because OOMs are not supported on Android.

<figure><img src="https://files.readme.io/36bb7f627d4eaeffcad6c7bcee3bb4f63348d8f9962837ed7795eee87e88b560-image.png" alt=""><figcaption></figcaption></figure>

#### After Import: Activation & Updating Settings

Imported alerts that need updates will remain inactive until configured.

You'll see:

* A banner: *“You have inactive alerts & rules that require configuration updates…”*
* A **Review alerts & rules** button
* Each alert marked as `Needs update`

<figure><img src="https://files.readme.io/9bf0901b655bcc4d768344bd059f2d31c62b03d75748ebe1ff8a17570d4b15c7-image.png" alt=""><figcaption></figcaption></figure>

#### Application specific configurations

If any of the following conditions exist in the alert you are importing, the alert would always require an update of these fields after import to activate the alert.

* Integrations
* Teams
* Members
* App version
* Path/Package
* Filename
* Trace name
* Current view
* Assignee
* User attributes
* Feature flags
* Flow name

#### Updating Alerts

Click **Review alerts & rules** → a right-side drawer opens showing each alert needing attention.

For each alert, select **Update** to open the editing screen. Fix highlighted fields, then click **Save**. Once the configuration is valid, the alert becomes active.

Common updates include:

* Adding an integration or selecting one that exists in the new environment
* Selecting an app version or adjusting version logic
* Selecting recipients or teams
* Replacing invalid fields or actions that no longer apply

{% hint style="info" %}
Alerts cannot be saved until all required fields are complete. This prevents accidental activation with incomplete settings.
{% endhint %}

![](https://files.readme.io/65150b7b2dcc1244aef13b02dbbf91f92e14c103859a75c3e5f8e74d396b1529-image.png)![](https://files.readme.io/2814e2300a76c2fe0867a52d2a295a9d5f930e9011833a9539d56cd5fc163479-image.png)<br>


# Team ownership

Configure team ownership in Luciq to automatically assign bug reports and crashes to the right team. Define rules based on app area, version, or tags.

To set up team ownership in the Luciq dashboard, you will need to go through a couple of steps. Team ownership will help ensure every crash and bug gets assigned to the corresponding team. Assignment can be done either manually or automatically. These steps will ensure you create the best possible team definitions. It will enable you to then prioritize issues related to your team and get alerted about them. These are the quick few steps needed to complete team ownership on the dashboard

1. Team Creation
2. Team Assignment
   * Automatic Approach which is done through Team Definition
   * Manual Approach
3. Prioritization
4. Alerting
5. Team Performance Dashboard

### Team Creation

The first step of team ownership is creating the team using the following steps.

To create a team, you will go through “Account management” (on the organizational level) at the top right corner of the dashboard

<figure><img src="https://files.readme.io/65962bdeb9b45d0869e2c4515251227cc2e221e31aeb6910f85258de7ac2ff31-image.png" alt=""><figcaption></figcaption></figure>

Then, from the left list choose “Teams”, then “Create a team”

<figure><img src="https://files.readme.io/00c6b4eccb5e9a92734804a0b18b65d014a1926e496fa1935a78e4a4d8978384-image.png" alt=""><figcaption></figcaption></figure>

Choose the most suitable Team name, example "Payment"

<figure><img src="https://files.readme.io/9127128f62c57540755e4ae8f4ad5047af6756ac4d46dca9392373dfdcae346c-image.png" alt=""><figcaption></figcaption></figure>

### Team Assignment <a href="#team-assignment" id="team-assignment"></a>

#### Automatic Assignment to Corresponding Teams

* **Assign Ownership Type:**
  * **Bugs**: Define teams based on categories, user attributes, and current view (screen name).
  * **Crashes**: Use path/package or filename to assign ownership.
  * **Screen loading, UI hangs, App Launch**: Assign teams based on screen names.
  * **Networks**: Assign teams based on the URL.

To define the team, you will need to go to “Settings” (on the app level) from the bottom of the sidebar (on the left)

Then, choose “Team ownership”, then "Create Definition"

<figure><img src="https://files.readme.io/9047758885c456eaf691736f09c2c68c4d973a452e8f0f9f78dc07d0545a4106-image.png" alt=""><figcaption></figcaption></figure>

Choose the **Type** that you want to define team ownership for.

<figure><img src="https://files.readme.io/7f9c3bbb77124ddf759405382d8b296a1fd3e965178e41bce08d83dba6b5979b-image.png" alt=""><figcaption></figcaption></figure>

#### Automatically Assigning Bugs to the Corresponding Team

Choose the team that you created. For example, “Payment” Team

<figure><img src="https://files.readme.io/58afa7575d95dcc5674eecb898dc5669f407adcbdbcf8a798d2efd103657d736-image.png" alt=""><figcaption></figcaption></figure>

Start adding conditions to define the team by choosing from the following:

* Categories
* User Attributes
* Current View

<figure><img src="https://files.readme.io/b9f36e25f58be04b618bb061bc8170e98ef141ae943fe9075a60abb9ed5e04b7-image.png" alt=""><figcaption></figcaption></figure>

#### Automatically Assigning Crashes to the Corresponding Team

And now defining the **Payment** team that is responsible for Crashes

<figure><img src="https://files.readme.io/6d92fadc473de9403f2a0efae4a2df4e45f9a5b947e1cf9fcd8b835851ba6838-image.png" alt=""><figcaption></figcaption></figure>

Start adding conditions to define the team by choosing from the following:

* Path (iOS)/Package (Android)
* Filename

<figure><img src="https://files.readme.io/cd711115ad791248e68f9b83f40fbcb64090d1e5e35b3ecfd610bd8e1e20c21c-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Path/Package or Filename**

NOTE: Whenever you type the path/package you need to press Enter in order to lock it in
{% endhint %}

#### **Matching Paths/Packages**

**iOS**

When setting up the definition, Luciq supports partial matching of paths using the **match** condition (not case sensitive), let's take a look at some examples:

**Sample Crash**

Actual Crash Path: luciq/crashes/list/singleCrash

| Successful Match                                                                                                                                                | Unsuccessful Match                                                                                                                                                                             |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <ul><li>luciq</li><li>luciq/crashes</li><li>luciq/crashes/list/singleCrash</li><li>luciq/crashes/list</li><li>/crashes/list</li><li>/list/singleCrash</li></ul> | <ul><li>luciq//crashes</li><li>/luciq/crashes</li><li>/luciq/crashes/list/singleCrash/</li><li>luciq/crashes/list/singleCrash/Occurrence</li><li>luciq/cr/list</li><li>crashes/luciq</li></ul> |

**Unsuccessful Matches:**

* luciq//crashes - *contains an extra slash in the middle*
* /luciq/crashes - *contains an extra slash before luciq*
* com/luciq/crashes/list/singleCrash/ - *contains an extra slash after singleCrash*
* luciq/crashes/list/singleCrash/Occurrence - *contains an extra sub-path*
* luciq/cr/list - *Luciq does not match partial words (cr and crashes in this case)*
* crashes/luciq - *path is written in a wrong order*

**Android**

When setting up the definition, Luciq supports partial matching of package names using the **match** condition (not case sensitive), let's take a look at some examples:

**Sample Crash**

Sample Package: com.luciq.crashes.list.singleCrash

| Successful Match                                                                                                                                           | Unsuccessful Match                                                                                                                                                                                  |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <ul><li>luciq</li><li>com.luciq.crashes</li><li>com.luciq.crashes.list</li><li>luciq.crashes.list</li><li>.crashes.list</li><li>list.singleCrash</li></ul> | <ul><li>com..luciq.crashes</li><li>.com.luciq.crashes</li><li>com.luciq.crashes.list.singleCrash.</li><li>crashes.list.singleCrash.Occurrence</li><li>luciq.cr.list</li><li>crashes.luciq</li></ul> |

**Unsuccessful Matches:**

* com..luciq.crashes - *contains an extra dot in the middle*
* .com.luciq.crashes - *contains an extra dot before com*
* com.luciq.crashes.list.singleCrash. - *contains an extra dot after singleCrash*
* crashes.list.singleCrash.Occurrence - *actual path does not contain 'Occurrence'*
* luciq.cr.list - *Luciq does not match partial words (cr and crashes in this case)*

#### Automatically Assigning Performance Metrics to the Corresponding Team

And now defining the **Screen loading, UI hangs, and App Launches** that the payments team is responsible for:

Choose the **“Screen loading, UI hangs, and App Launches”** type, then choose the payments team.

<figure><img src="https://files.readme.io/a6258c8f5a60a971ea1cec8316188153e3d52180a91cc60fa7be3b8146dac806-image.png" alt=""><figcaption></figcaption></figure>

Start adding conditions by choosing the **Screen Names** your team is responsible for

<figure><img src="https://files.readme.io/b12a15d2857cad767599a79687576c852d167158f21c9f34ad178733d427a25a-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Screen Name Assignment**

For **Screen loading, UI hangs, and App Launch**, the same team can be assigned to multiple screen names, but the same screen cannot be assigned to multiple teams.
{% endhint %}

### Manual Assignment to Corresponding Teams

#### Manually Assigning Bugs to the Corresponding Team

You can also assign the team manually from within the bug itself without defining it.

Go to bugs from the left sidebar of the dashboard and then click on the bug that you want to assign to a certain team

Navigate to the right sidebar “Actions” and change the Team to the corresponding team. For example, choosing the “Payment”

<figure><img src="https://files.readme.io/4fbc7441352fa9bd244c41d0366828213b0e714716134976a7780feff14c30f0-image.png" alt=""><figcaption></figcaption></figure>

#### Manually Assigning Crashes to the Corresponding Team

You can also assign the team manually from within the crash itself without defining it.

Go to crashes from the left sidebar of the dashboard and then click on the crash that does not have a team assigned to it

<figure><img src="https://files.readme.io/990830d38a82c672bc6dff92a182e573c415cb317d6876d2d050b8140d92db9f-image.png" alt=""><figcaption></figcaption></figure>

Navigate to the right sidebar “Actions” and change the Team to the corresponding team. For example, choosing the “Payment”

<figure><img src="https://files.readme.io/45395cfde156d667fe6f198e99d83aa8924843cfb326807c385860471557f939-image.png" alt=""><figcaption></figcaption></figure>

### Prioritizing

#### Bugs Prioritization

Prioritizing bugs related to your team can be done through the Team filter at the top bar of the "Bugs" main page\
Select your team from the dropdown list, you can even search by the team’s name. For example, “Payment” Team

<figure><img src="https://files.readme.io/250e776e003c784bbd7575f2de96a61424abea01f9624e59ba0e0376f17df746-image.png" alt=""><figcaption></figcaption></figure>

As you can see below, you can prioritize the bugs related to your team only and get more granular with the options to filter. For example, Payment team wants to focus on *bugs status' new or in-progress*. Also, you will be able to save those filters and you can access them from the right side of the filter bar.

<figure><img src="https://files.readme.io/b7155213f66caee1d302e5b14e6460eb2219f06bfc39bbfc36e55ee20fc7304a-image.png" alt=""><figcaption></figcaption></figure>

### Crash Prioritization

Prioritizing crashes related to your team can be done through the Team filter at the top bar of the "Crashes" main page.

Select your team from the dropdown list, you can even search by the team’s name.

For example, “Payment” Team

<figure><img src="https://files.readme.io/b6a8d985c5167edca38397d25a4acfe58cebe5765aaddb4d4f4f2e1f1d93b6a1-image.png" alt=""><figcaption></figcaption></figure>

As you can see below, you can prioritize the crashes related to your team only and get more granular with the options to filter. For example, Payment Team wants to focus on *crashes seen in a certain app version*. Also, you will be able to save those filters and you can access them from the right side of the filter bar.

<figure><img src="https://files.readme.io/c1eca471c1bc86298c4004b44e8a8536a2f00426cea9751f9503f1f302ac6516-image.png" alt=""><figcaption></figcaption></figure>

### Alerting

If you’d like to get alerted as soon as you receive any crash related to your Team, you will be able to do that through our “Alerts and Rules” engine.

Go to “Alerts and Rules” and "Create" a new Alert

**Use Case**:

* Regressing crash assigned to team “Payment”
* Choose the condition (Regression) you want to be alerted to whenever it is assigned to “Payment” team

<figure><img src="https://files.readme.io/aabe857ec8bfabcff2a30d605e14e0d449a43e12c91cb44aa0fdb6102a0444c8-image.png" alt=""><figcaption></figcaption></figure>

**For more details on the different use cases of Alerting, please refer to** [**Alerts & Rules**](/product-guides-and-integrations/product-guides/automation-and-workflows/alerts-and-rules) **product guides.**

Then, Forward it to your favorite tool (Ex: Slack, MS Teams, PagerDuty, … etc). Thus, Payment Team will be notified about this regression through that tool.

<figure><img src="https://files.readme.io/93ebb2ddbc9cca6c6da2d60403e45fa9a61d62976695a6a86d55986d7e393be0-image.png" alt=""><figcaption></figcaption></figure>

**For more details on the different integrations that is supported, please refer to** [**Luciq's Integrations Docs**](/product-guides-and-integrations/integrations)

You can assign this Alert/Rule to a certain team through "Owned by" section at the bottom of the rule. This enables you to figure out who to contact if there was a problem with any of the Alerts/Rules

<figure><img src="https://files.readme.io/b22edd50bed7165186cb1ec14e093364f8e6e1dc0172bd0f38e6de978ce43f65-image.png" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Unassigned Rules**

If the Alert/Rule does not have a team assigned to it, then by default the value will be **Unassigned**
{% endhint %}


# GitHub Integration For Team Ownership

The GitHub CODEOWNERS integration keeps Luciq crash ownership rules in sync with your repo, so team assignment stays accurate at scale.

The GitHub CODEOWNERS integration allows Luciq to automatically generate and maintain Crashes team ownership rules based on the `CODEOWNERS` file in your GitHub repository.

Instead of manually configuring file paths and assigning them to teams, Luciq fetches and syncs your CODEOWNERS file to ensure accurate, up-to-date ownership rules at scale.

### How It Works?

{% stepper %}
{% step %}
Once connected, Luciq fetches your `CODEOWNERS` file from your GitHub repository.
{% endstep %}

{% step %}
Parses all ownership patterns (paths, filenames).
{% endstep %}

{% step %}
Extracts teams (GitHub teams).
{% endstep %}

{% step %}
Lets you map teams to Luciq teams in a simple UI.
{% endstep %}

{% step %}
Generates crash ownership rules automatically based on this mapping.
{% endstep %}

{% step %}
Keeps rules in sync whenever your `CODEOWNERS` file changes on GitHub.
{% endstep %}
{% endstepper %}

This automation ensures that crash reports are always routed to the correct team without manual updates.

### Prerequisites

Before you begin:

* You must be an **Owner** or **Admin** or have permissions to manage Team Ownership.
* Your GitHub organization must allow installing GitHub apps.
* Your repository must contain a valid `CODEOWNERS` file.

***

#### How to Set it up?

{% stepper %}
{% step %}
**Connect Luciq to GitHub**

1. Go to **Settings → Source Code Management** in the Luciq dashboard.<br>

   <figure><img src="/files/incR7I0zirpLWMXpmXki" alt=""><figcaption></figcaption></figure>
2. Click **Connect with GitHub**.
3. Install Luciq App on GitHub or use the Installation ID if you already installed the App<br>

   <figure><img src="/files/ig7GLjg90l7ChHPWRucX" alt=""><figcaption></figcaption></figure>
4. On GitHub; choose the repository you want Luciq to access.
5. Confirm permissions and return to Luciq.
   {% endstep %}

{% step %}
**Select Repository, Branch, and Features**

After connecting:

1. Choose the **repository** that contains your `CODEOWNERS` file.
2. Select the **branch** (e.g., `main`, `master`) where `CODEOWNERS` lives.
3. Select which features should access the integration:
   * `CODEOWNERS` file: allows you to define crashes team ownership by fetching and processing GitHub `CODEONWERS` file.
   * Resolve Agent: An AI-powered feature designed to automate the process of resolving mobile app crashes, enabling developers to resolve issues within minutes. [Learn more](https://docs.luciq.ai/product-guides-and-integrations/product-guides/ai-features/resolve-agent)
4. Click Continue<br>

   <figure><img src="/files/4LqlXmJHepVqOe54W2ap" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Important Note**

CODEOWNERS file feature requires only read-access to the `CODEOWNERS` file. It doesn't access your code or make any changes to your Repository.
{% endhint %}
{% endstep %}

{% step %}
**Review and confirm configuration**

Luciq will let you review all the configuration settings including:

* Organization
* Repository
* Branch
* Enabled features

Then you can select whether you want to enable auto-sync for `CODEOWNERS` file or not. The auto-sync will automatically update team ownership rules once the `CODEOWNERS` file is updated on GitHub.

Finally once you confirm all settings and click **Connect**, Luciq will fetch and process the `CODEOWNERS` file

<figure><img src="/files/odebcagKXmrqgEl4ZwAN" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Map `CODEOWNERS` to Luciq Teams**

Luciq will extract all teams from your `CODEOWNERS` file (GitHub teams), then try to match the team names to Luciq teams. If matching is not successful then you will see a list of teams that need to be mapped to Luciq teams.

For each team:

1. Select a **Luciq team** that represents their ownership.
2. Save the mapping.
3. You can revisit and update this mapping anytime.

This mapping allows Luciq to convert `CODEOWNERS` rules into crash ownership rules.

<figure><img src="/files/i97PRQeiQsviXtozdVqI" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Ownership rules are defined for mapped teams only. For Unmapped teams, their rules are defined once the mapping is complete.
{% endhint %}
{% endstep %}

{% step %}
**Automatic Rule Generation**

Once mapping is complete:

* Luciq automatically generates **Team Ownership Rules** based on the parsed `CODEOWNERS` patterns.
* These rules appear on the **Team Ownership** page in your dashboard.
* Each rule references the associated Luciq team.

<figure><img src="/files/bbE2Rq1vcDlLGT6mDdkn" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

### Automatic Syncing For `CODEOWNERS` File

Luciq keeps your rules in sync by:

* Monitoring your GitHub repository for any updates to `CODEOWNERS`.
* Re-processing the file when changes are detected.
* Updating ownership rules automatically.
* Sending email notifications to admins when:
  * Code ownership changes
  * New owners appear that need mapping
  * Rules are updated

<figure><img src="/files/xXHVnumoTetRGcVvXAQo" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Important Notes**

* **The `CODEOWNERS` file sync process runs once per day at 00:00 UTC.**
* When automatic syncing is enabled, team ownership rules created by `CODEOWNERS` file cannot be edited. The only way to edit these rules is by updating the `CODEOWNERS` file on GitHub.
  {% endhint %}

{% hint style="success" %}
If Automatic syncing is disabled, you will see a banner in the team ownership page to notify you that the `CODEOWNERS` file has changed on GitHub.

You can manually update ownership rules directly from the banner.
{% endhint %}

***

### Processing `CODEOWNERS` File

#### Sample of CODEOWNERS file

`CODEOWNERS` file is a text file that contains paths and/or filenames assigned to certain teams in the following format:

{% code title="CODEOWNERS" %}

```
## App folders
/apps/web/ @frontend-team
/apps/mobile/ @mobile-team @qa-team

## Library and shared folders
/libs/api/ @backend-team
/libs/shared/ @platform-team

## Specific files
/README.md @documentation-team
/scripts/deploy.sh @devops-team

## Wildcard (valid pattern)
*/src/payment @payments-team
/src/checkout/* @checkout-team

## Specific file inside a path
/src/payments/invoice_service.py @payments-team
```

{% endcode %}

#### iOS

Refer to [Matching iOS paths](/product-guides-and-integrations/product-guides/automation-and-workflows/team-ownership#matching-paths-packages) for more information.

{% hint style="info" %}
Flutter and React-Native uses the same matching logic for iOS.
{% endhint %}

#### Android

Unlike other platforms Android relies on packages (not paths) to assign crashes to teams. Hence, the paths in the `CODEOWNERS` file should include the package.

For example:

Assume the package is named: com.luciq.crashes; Then the path in the `CODEOWNERS` file should be: `/src/main/git/com/luciq/crashes`

#### Supported Wildcard Patterns

On GitHub, you can use wildcards to assign certain patterns to teams but Luciq supports only the following wildcard patterns:

{% hint style="success" %}
**Example**

**\*/src/payments**

**/src/payments/\***
{% endhint %}

{% hint style="danger" %}
**Examples of unsupported wildcard patterns**

* ***/src/\******/payment/**
* *\***.rb***
* **src/**\*\***/test/**
* **src/action**\*\* /test
  {% endhint %}

#### Team Selection Logic

If multiple teams are assigned to the same path, only the first team on the line will be used.

Example:

```
/src/payment/file1.txt @teamA @teamB
```

@teamA will be assigned to this path


# CODEOWNERS-Based Team Assignment

Assign crashes to the right team automatically from your repository CODEOWNERS file. How name matching and specificity-based paths work.

## How It Works

When you upload a CODEOWNERS file, Luciq maps your crash reports to the right team automatically based on where the crash happened in your code.

Here's what happens under the hood:

1. **Team Mapping**: Team names from your CODEOWNERS file are matched to your existing teams on the Luciq dashboard. This matching is flexible since it's case-insensitive and treats hyphens, underscores, and spaces as the same thing. So `@org/my-team` will match a team called "My Team" without any extra configuration.
2. **Frame Selection**: When a crash comes in, Luciq looks at the crashed thread and picks the **top application frame** (the first frame that belongs to your code, not system libraries). The file path from that frame is what gets used for team matching.
3. **Path Matching**: That file path is compared against the paths in your CODEOWNERS rules. The matching works on path segments (split by `/`), so a rule for `LibraryA` will match any crash in `LibraryA/SomeFile.swift`, `LibraryA/SubDir/AnotherFile.swift`, etc.
4. **Specificity-Based Resolution**: When multiple rules match the same crash, Luciq picks the **most specific** one. Specificity is determined by the number of path segments in the matching rule — more segments means a more specific match. See [Specificity-Based Matching](#specificity-based-matching) below for details.

## CODEOWNERS File Example

A CODEOWNERS file lives in your repository and defines which team owns which part of the codebase. Here's what a typical one looks like:

```
# Each line maps a path pattern to a team
# The format is: <path>    <team>

# Networking layer owned by the platform team
/Sources/Networking/                    @org/platform-team

# Authentication module owned by the identity team
/Sources/Auth/                          @org/identity-team

# Checkout and payments owned by the commerce team
/Sources/Checkout/                      @org/commerce-team
/Sources/Payments/                      @org/commerce-team

# Everything under the analytics module
/Sources/Analytics/                     @org/data-team
```

With this file uploaded, if a crash happens in `Sources/Checkout/CartViewController.swift`, Luciq will automatically assign it to **Commerce Team**. A crash in `Sources/Auth/LoginManager.swift` goes to **Identity Team**, and so on.

A team can own multiple paths (like the commerce team above owning both Checkout and Payments), and paths can be as broad as a top-level directory or as specific as a single file.

{% hint style="warning" %}
**Don't Mix Files and Directories for the Same Team**

When defining ownership rules for the same team, avoid mixing file paths and directory paths. If a team has both file-level and directory-level rules, only the file-level rules will be used for assignment.

**Workaround**: Use only directories or only files for the same team — don't mix both.
{% endhint %}

## Specificity-Based Matching

When multiple ownership rules match the same crash, Luciq uses **specificity** to determine which team gets assigned. The rule with the most path segments wins.

### How Specificity Is Calculated

Specificity is based on the number of segments in the matching path token, where segments are separated by `/` (for file paths) or `.` (for Android packages).

| Rule token                     | Segments | Specificity |
| ------------------------------ | -------- | ----------- |
| `Sources`                      | 1        | 1           |
| `Sources/Networking`           | 2        | 2           |
| `Sources/Networking/WebSocket` | 3        | 3           |

### Example

Consider the following ownership definitions:

| Team           | Rule                                        |
| -------------- | ------------------------------------------- |
| Platform Team  | Path matches `Sources/Networking`           |
| Real-Time Team | Path matches `Sources/Networking/WebSocket` |

If a crash occurs in `Sources/Networking/WebSocket/ConnectionManager.swift`:

* **Platform Team** matches with specificity **2** (`Sources/Networking` has 2 segments)
* **Real-Time Team** matches with specificity **3** (`Sources/Networking/WebSocket` has 3 segments)

**Result**: The crash is assigned to **Real-Time Team** because it has the more specific rule.

### Tie-Breaking

When two rules have the **same specificity** (same number of path segments), the rule that was **most recently updated** wins. This ensures that if you update a definition, it takes priority over older definitions with the same level of specificity.

### How It Works Across Platforms

Specificity-based matching applies across all platforms:

| Platform         | Field             | Separator | Example                             |
| ---------------- | ----------------- | --------- | ----------------------------------- |
| **iOS**          | Path              | `/`       | `Controllers/Auth` → 2 segments     |
| **Android**      | Package           | `.`       | `com.example.app.auth` → 4 segments |
| **Flutter**      | Flutter Path      | `/`       | `lib/features/auth` → 3 segments    |
| **React Native** | React Native Path | `/`       | `src/screens/Auth` → 3 segments     |

For **filename-based** rules (as opposed to path/package), the specificity is always **1** regardless of the filename. This means path-based rules will generally take precedence over filename-based rules when both match.

## How to Test

To verify that team assignment is working correctly for your CODEOWNERS setup, keep in mind that Luciq needs **server-side symbolication** to extract file paths from crash frames, and that only happens when dSYMs are available.

Here's what you need to do:

1. **Build in Release mode**: Debug builds don't generate dSYM files. You need to archive or build your app in Release configuration for Xcode to produce them.
2. **Upload your dSYMs**: Once you have the dSYM files, upload them to Luciq (either manually through the dashboard or via your CI/CD pipeline). Without dSYMs, Luciq can't resolve the file paths needed for team matching.
3. **Trigger a crash**: Run the Release build, trigger a crash, and relaunch the app so the crash report gets sent.

{% hint style="warning" %}
**SDK-Side Symbolication**

If the SDK sends crashes that are already symbolicated (i.e., the stack frames already contain file paths and line numbers from the SDK side), server-side symbolication is skipped, and team assignment will not happen. This is why testing with a proper Release build and uploaded dSYMs is the correct flow.
{% endhint %}

Once the crash appears on your dashboard, you should see it assigned to the expected team based on your CODEOWNERS rules.




---

[Next Page](/llms-full.txt/1)

