← Plugin catalog
Developer Tools

Tuist

Tuist GmbH v1.0.1

Publisher description

From the marketplace listing

Tuist helps teams inspect Xcode, Gradle, and Bazel build data, test results, cache activity, previews, runner jobs, and project configuration. It also supports authorized account setup and selected test-case updates.

Language: English · Automatically detected from descriptions.

Files & skills

File archives

Plugin package14 files · 19.6 KBBrowse files →
Skill instructions
analyze-selective-testing5.43 KB

View saved version →

---
name: analyze-selective-testing
description: Analyzes Xcode selective testing effectiveness for a test run, showing which test targets were skipped or ran, and diagnosing regressions in test selection. Can compare two test runs to identify what changed.
---

# Analyze Selective Testing

## Quick Start

1. Run `tuist test list --json` to find test runs.
2. Run `tuist test xcode target list <test-run-id> --json` to see per-target selective testing status.
3. If effectiveness dropped, compare targets between a known-good run and the regressed run.

## Step 1: Resolve Test Runs

### Find the test run to analyze

List recent test runs, optionally filtering by branch:

```bash
tuist test list --json --page-size 10
tuist test list --git-branch feature-x --json --page-size 5
```

Get basic info for a specific test run:

```bash
tuist test show <test-run-id> --json
```

Use this to confirm the run's branch, status, scheme, and environment (`xcode_version`, `macos_version`).

### Defaults

- If no test run is specified, use the most recent CI test run on the current branch.
- For comparison, use the most recent CI test run on the project's default branch.

## Step 2: Drill Into Selective Testing Targets

List per-target selective testing data for a test run:

```bash
tuist test xcode target list <test-run-id> --json
```

Each target includes:
- **name**: The test target name
- **hit_status**: `miss` (ran), `local` (skipped via local cache), or `remote` (skipped via remote cache)
- **hash**: The selective testing hash for this target

Filter by status to focus your analysis:

```bash
tuist test xcode target list <test-run-id> --hit-status miss --json
tuist test xcode target list <test-run-id> --hit-status local --json
tuist test xcode target list <test-run-id> --hit-status remote --json
```

### Assess effectiveness

Count the targets by status:
```
effectiveness = (local_hits + remote_hits) / total_targets * 100
```

| Effectiveness | Verdict |
|---|---|
| 60-100% | Healthy — most unchanged tests are being skipped |
| 30-60% | Moderate — some cache invalidation occurring, worth investigating |
| 0-30% | Low — likely a core dependency changed or the cache was invalidated |
| 0% | Cold cache — first run, environment change, or full invalidation |

## Step 3: Diagnose Regressions

If effectiveness dropped, investigate:

### Check for global invalidation

If nearly all targets show as `miss`, the entire selective testing cache was invalidated. Common causes:

| Cause | How to verify |
|---|---|
| Xcode version change | Compare `xcode_version` between good and bad runs via `tuist test show` |
| CI environment change | Compare `macos_version`, `model_identifier` between runs |
| Project graph or dependency change | Check git history for changes to project manifests, dependency versions, or target configurations |
| Core dependency change | A widely-depended-on target changed, invalidating all its dependents |

Note: Tuist CLI version upgrades rarely cause hash invalidation — the hash version is not updated on every release.

### Check for cascade invalidation

If only some targets show as `miss`, a dependency change likely cascaded:

1. Run `tuist test xcode target list <good-run-id> --json` and `tuist test xcode target list <bad-run-id> --json`.
2. Match targets by name — identify those that changed from `local`/`remote` to `miss`.
3. Compare hashes for changed targets — if a target's hash differs, its sources or dependencies changed.
4. Look for a common dependency among the invalidated targets.

### Compare two test runs

For each target:
- Match by `name`
- Compare `hit_status` (hit -> miss = invalidated, miss -> hit = improved)
- Compare `hash` (different hash = target or its dependencies changed)

## Step 4: Recommendations

Based on the diagnosis:

- **Environment change**: Ensure CI uses consistent Xcode/macOS versions. This is the most common cause of full cache invalidation.
- **Cascade from dependency change**: Consider if the change to the root target was necessary. If the root target is a widely-shared module, consider splitting it.
- **Cold cache on new branch**: Run tests once to warm the cache. Subsequent runs will benefit from selective testing.

## Summary Format

Produce a summary with:

1. **Overall**: effectiveness percentage, verdict (healthy/moderate/low/cold)
2. **Target breakdown**: targets grouped by hit status
3. **Comparison** (if applicable): targets that changed status between runs, with hash diffs
4. **Root cause**: what caused the regression
5. **Recommendations**: actionable next steps

Example:

```
Selective Testing Analysis: test run abc123 on feature-x

Effectiveness: 15% (3/20 targets skipped) -- LOW
  Local hits:  2 targets (CoreTests, UtilTests)
  Remote hits: 1 target (NetworkTests)
  Misses:      17 targets

Comparison with baseline (run def456, 75% effectiveness):
  14 targets changed from hit to miss
  All changed targets have different hashes

Root cause: Xcode version changed from 15.2 to 16.0 between runs.
This changed all target hashes, invalidating the entire cache.

Recommendations:
- Align CI Xcode version with the version used in the baseline run.
- If the upgrade is intentional, run tests once to re-warm the cache.
```

## Done Checklist

- Identified the test run(s) to analyze
- Drilled into per-target selective testing status via `tuist test xcode target list`
- Diagnosed root cause if effectiveness is low
- Compared targets with baseline if regression detected
- Provided actionable recommendations
compare-builds5.37 KB

View saved version →

---
name: compare-builds
description: Compares two Xcode build runs to identify duration regressions, cache changes, and new issues. Can be invoked with build IDs, dashboard URLs, or branch names (e.g. `tuist compare-builds --base main --head feature-branch`).
---

# Compare Builds

## Quick Start

You'll typically receive two build identifiers (IDs, dashboard URLs, or branch names). Follow these steps:

1. Run `tuist build list --json` to find builds on each branch.
2. Run `tuist build show <build-id> --json` for both base and head builds.
3. Fetch sub-resource details: targets (`tuist build xcode target list <id> --json`), issues (`tuist build xcode issue list <id> --json`), and cache tasks (`tuist build xcode cache-task list <id> --json`).
4. Compare duration, status, cache hit rates, and other metrics.
5. Summarize regressions, improvements, and recommendations.

If only one identifier is provided, use the project's default branch as the baseline.

## Step 1: Resolve Builds

### If base/head are build IDs or dashboard URLs

Fetch each directly:

```bash
tuist build show <base-id> --json
tuist build show <head-id> --json
```

### If base/head are branch names

List recent builds on each branch and pick the latest:

```bash
tuist build list --git-branch <base-branch> --json --page-size 1
tuist build list --git-branch <head-branch> --json --page-size 1
```

Then fetch full details with `tuist build show <id> --json`.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch with `git rev-parse --abbrev-ref HEAD`.

## Step 2: Fetch Sub-Resource Details

After fetching both builds with `tuist build show <id> --json`, drill down into sub-resources for a deeper comparison.

### Compare targets

```bash
tuist build xcode target list <base-id> --json
tuist build xcode target list <head-id> --json
```

Look for targets that changed status (e.g., success to failure) or had significant duration changes.

### Compare issues

```bash
tuist build xcode issue list <base-id> --json
tuist build xcode issue list <head-id> --json
```

Look for new warnings or errors introduced in the head build.

### Compare cache tasks

```bash
tuist build xcode cache-task list <base-id> --json
tuist build xcode cache-task list <head-id> --json
```

Identify which specific targets had cache misses or hits and whether that changed between builds.

## Step 3: Compare Top-Level Metrics

After fetching both builds, compare:

| Metric | What to check |
|---|---|
| `duration` | Flag if head is >10% slower than base |
| `status` | Flag if base succeeded but head failed |
| `cacheable_tasks_count` | Note if task count changed |
| `cacheable_task_local_hits_count` | Compare local hit counts |
| `cacheable_task_remote_hits_count` | Compare remote hit counts |
| Cache hit rate | `(local_hits + remote_hits) / cacheable_tasks_count * 100` |
| `category` | Note if one is `clean` and the other `incremental` (makes duration comparison less meaningful) |
| `scheme` / `configuration` | Ensure both builds used the same scheme and configuration for a fair comparison |

Compute the cache miss delta: `base_misses - head_misses`. Positive means head has fewer misses (improvement). Negative means regression.

## Step 4: Investigate Duration Regressions

If the head build is significantly slower:

1. Check if the `category` differs (clean vs incremental builds are not directly comparable).
2. Check if cache hit rate dropped, which would explain longer builds.
3. If both are incremental with similar cache rates, the regression is likely in compilation time.

## Step 5: Investigate Cache Changes

Compare cache statistics:

- **Hit rate dropped**: Possible causes include dependency changes, build setting changes, or Xcode version updates.
- **Hit rate improved**: Likely due to better cache warming or fewer source changes.
- **Task count changed**: New targets added or removed.

## Step 6: Check Build Context

Compare environment details:

- `xcode_version` and `macos_version`: Different versions can affect build times and cache validity.
- `is_ci`: CI vs local builds may have different performance characteristics.
- `git_branch` and `git_commit_sha`: Verify the builds are from the expected commits.

## Summary Format

Produce a summary with:

1. **Overall verdict**: Better, worse, or neutral compared to base.
2. **Duration**: Absolute and percentage change.
3. **Cache hit rate**: Change in hit rate with explanation.
4. **Status**: Any status changes (pass to fail or vice versa).
5. **Environment**: Note any environment differences that affect comparability.
6. **Recommendations**: Actionable next steps based on findings.

Example:

```
Build Comparison: base (abc123 on main) vs head (def456 on feature-x)

Duration: 45.2s -> 62.8s (+39%) -- REGRESSION
Cache hit rate: 85% -> 72% (-13%) -- 8 new cache misses
Status: success -> success

Root cause: Cache hit rate dropped due to 8 targets with invalidated caches.
The dependency hash changed for FeatureModule, cascading to 7 downstream targets.

Recommendations:
- Investigate why FeatureModule's cache was invalidated
- Consider splitting large targets to reduce cascade impact
```

## Done Checklist

- Resolved both base and head builds
- Fetched sub-resource details (targets, issues, cache tasks)
- Compared duration, cache, and status metrics
- Identified root causes for any regressions
- Provided actionable recommendations
compare-bundles4.15 KB

View saved version →

---
name: compare-bundles
description: Compares two app bundles to identify size changes, new or removed artifacts, and platform differences. Can be invoked with bundle IDs, dashboard URLs, or branch names.
---

# Compare Bundles

## Quick Start

You'll typically receive two bundle identifiers. Follow these steps:

1. Run `tuist bundle list --json` to find bundles on each branch.
2. Run `tuist bundle show <bundle-id> --json` for both base and head bundles.
3. Compare artifact trees with `tuist bundle artifact list <bundle-id> --json`.
4. Compare install size, download size, and other metadata.
5. Summarize size changes with actionable recommendations.

## Step 1: Resolve Bundles

### If base/head are bundle IDs or dashboard URLs

Fetch each directly:

```bash
tuist bundle show <base-id> --json
tuist bundle show <head-id> --json
```

### If base/head are branch names

List recent bundles on each branch and pick the latest:

```bash
tuist bundle list --git-branch <base-branch> --json
tuist bundle list --git-branch <head-branch> --json
```

Then fetch full details with `tuist bundle show <id> --json`.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch.

## Step 2: Compare Artifact Trees

After fetching both bundles with `tuist bundle show <id> --json`, compare the individual artifacts:

```bash
tuist bundle artifact list <base-id> --json
tuist bundle artifact list <head-id> --json
```

Match artifacts by name across both bundles. Look for:
- New artifacts added in the head bundle (potential size contributors)
- Removed artifacts (explain size decreases)
- Size changes in existing artifacts

## Step 3: Compare Top-Level Metrics

After fetching both bundles, compare:

| Metric | What to check |
|---|---|
| `install_size` | Flag if head is >5% larger |
| `download_size` | Flag if head is >5% larger |
| `version` | Note version changes |
| `supported_platforms` | Note platform changes |
| `app_bundle_id` | Should match between base and head |

Compute deltas:
- Install size delta: `head_install_size - base_install_size`
- Download size delta: `head_download_size - base_download_size`
- Percentage change: `delta / base_size * 100`

## Step 4: Analyze Size Changes

If size increased significantly (>5%):

1. Check if `version` changed, which might explain expected size growth.
2. Check if `supported_platforms` changed (adding a platform increases size).
3. Look at the `artifacts` field in the bundle details for individual artifact sizes.

Common causes of size increases:
- New frameworks or libraries added
- Asset catalogs grew (new images, videos)
- Unoptimized resources (large PNGs instead of compressed formats)
- Debug symbols included in release builds
- New localizations added

Common causes of size decreases:
- Removed unused frameworks
- Optimized assets
- Tree shaking or dead code elimination improvements
- Moved functionality to on-demand resources

## Summary Format

Produce a summary with:

1. **Overall verdict**: Size increased, decreased, or stable.
2. **Install size**: Absolute and percentage change.
3. **Download size**: Absolute and percentage change.
4. **Version**: Note if version changed.
5. **Platforms**: Note any platform changes.
6. **Recommendations**: Actionable next steps for size regressions.

Example:

```
Bundle Comparison: base (v2.1.0 on main) vs head (v2.2.0 on feature-x)

Install Size: 45.2 MB -> 52.8 MB (+16.8%) -- REGRESSION
Download Size: 28.1 MB -> 32.4 MB (+15.3%) -- REGRESSION
Version: 2.1.0 -> 2.2.0
Platforms: iOS, macOS (unchanged)

The install size increased by 7.6 MB. This is a significant increase
that may affect user download and storage experience.

Recommendations:
- Review new frameworks or assets added in this version
- Check for uncompressed resources or oversized image assets
- Consider using asset compression or on-demand resources
- Run `xcrun bitcode_strip` analysis to check for unnecessary bitcode
```

## Done Checklist

- Resolved both base and head bundles
- Compared artifact trees using `tuist bundle artifact list`
- Compared install and download sizes
- Analyzed root causes of size changes
- Provided actionable recommendations
compare-cache-runs5.22 KB

View saved version →

---
name: compare-cache-runs
description: Compares two `tuist cache` runs to identify cache hit rate changes and root-cause analysis of cache invalidation. Can be invoked with cache run IDs, dashboard URLs, or branch names.
---

# Compare Cache Runs

## Quick Start

You'll typically receive two cache run identifiers. Follow these steps:

1. Run `tuist cache-run list --json` to find cache runs on each branch.
2. Run `tuist cache-run show <id> --json` for both base and head cache runs.
3. Compare duration, status, and cache hit rates.
4. Summarize cache changes with root cause analysis.

## Step 1: Resolve Cache Runs

### If base/head are cache run IDs or dashboard URLs

Fetch each directly:

```bash
tuist cache-run show <base-id> --json
tuist cache-run show <head-id> --json
```

### If base/head are branch names

List recent cache runs on each branch:

```bash
tuist cache-run list --git-branch <base-branch> --json --page-size 1
tuist cache-run list --git-branch <head-branch> --json --page-size 1
```

Then fetch full details with `tuist cache-run show <id> --json`.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch.

## Step 2: Compare Top-Level Metrics

After fetching both cache runs, compare:

| Metric | What to check |
|---|---|
| `duration` | Flag if head is >10% slower |
| `status` | Flag if base succeeded but head failed |
| `cacheable_targets` | Note if target count changed |
| `local_cache_target_hits` | Compare local hit counts |
| `remote_cache_target_hits` | Compare remote hit counts |
| `is_ci` | Note if one is CI and the other local |

Compute cache hit rates:
- Base: `(local_hits + remote_hits) / cacheable_targets * 100`
- Head: same formula
- Delta: `head_rate - base_rate`

## Step 3: Analyze Cache Invalidation

If cache hit rate dropped, the key question is: **which target(s) caused the invalidation cascade?**

Cache invalidation in `tuist cache` works the same way as in `tuist generate`:
1. A "root cause" target has a direct change (source file modified, build setting changed, etc.)
2. All targets that depend on the root cause target also get invalidated because their `dependencies` hash changes.
3. This cascade can invalidate many targets from a single root change.

### Identifying root cause targets

Without the module cache target detail endpoint (available via MCP), use these heuristics:

1. Check `git diff` between the base and head commits to see which files changed.
2. Map changed files to their Tuist targets/modules.
3. Targets with direct source changes are likely root causes.
4. Targets that only changed due to dependency hash cascading are secondary invalidations.

### Common root causes of cache invalidation

| Cause | Description |
|---|---|
| Source changes | Files in the target's source directory were modified |
| Resource changes | Assets, XIBs, storyboards, or other resources changed |
| Build settings | Target or project build settings were modified |
| Dependency changes | An external dependency version changed |
| Info.plist changes | The target's Info.plist was modified |
| Entitlements changes | The entitlements file was modified |
| Deployment target | The minimum deployment target changed |
| Headers | Public or project headers changed |
| Project settings | Shared project-level settings changed |

## Step 4: Assess Impact

Categorize the cache invalidation:

- **Expected**: Source files were intentionally changed, causing expected cache misses.
- **Unexpected**: No source changes but cache was invalidated (build settings, Xcode version, etc.).
- **Cascade**: A small change invalidated many downstream targets.

For `tuist cache` specifically, also consider:
- Cache warming strategy: Was the cache run warming from scratch or incrementally?
- The `command_arguments` field can reveal if different flags were used.

## Summary Format

Produce a summary with:

1. **Overall verdict**: Cache hit rate improved, regressed, or stable.
2. **Cache hit rate**: Base rate vs head rate with delta.
3. **Duration**: Absolute and percentage change.
4. **Root cause targets**: Which targets had direct changes.
5. **Cascade impact**: How many targets were invalidated due to dependency cascading.
6. **Recommendations**: How to minimize cache invalidation.

Example:

```
Cache Run Comparison: base (run-123 on main) vs head (run-456 on feature-x)

Duration: 95.2s -> 142.8s (+50%) -- REGRESSION
Cache hit rate: 88% (44/50) -> 60% (30/50) (-28%) -- REGRESSION
Status: success -> success

Root cause: CoreModule had source changes (5 files modified).
This cascaded to 14 downstream targets that depend on CoreModule.

Cache invalidation breakdown:
- Direct changes: CoreModule (sources changed)
- Cascade: UIKit, Networking, Analytics, + 11 others (dependency hash changed)

Recommendations:
- Consider splitting CoreModule into smaller, more focused modules
- Use interface/implementation module pattern for frequently-changed modules
- Run `tuist cache` on the feature branch after rebasing to warm caches
```

## Done Checklist

- Resolved both base and head cache runs
- Compared duration and cache hit rates
- Identified root cause targets for cache invalidation
- Analyzed cascade impact
- Provided actionable recommendations for reducing cache misses
compare-generations5.06 KB

View saved version →

---
name: compare-generations
description: Compares two `tuist generate` runs to identify cache hit rate changes and root-cause analysis of cache invalidation. Can be invoked with generation IDs, dashboard URLs, or branch names.
---

# Compare Generations

## Quick Start

You'll typically receive two generation identifiers. Follow these steps:

1. Run `tuist generate list --json` to find generations on each branch.
2. Run `tuist generate show <id> --json` for both base and head generations.
3. Compare duration, status, and cache hit rates.
4. Summarize cache changes with root cause analysis.

## Step 1: Resolve Generations

### If base/head are generation IDs or dashboard URLs

Fetch each directly:

```bash
tuist generate show <base-id> --json
tuist generate show <head-id> --json
```

### If base/head are branch names

List recent generations on each branch:

```bash
tuist generate list --git-branch <base-branch> --json --page-size 1
tuist generate list --git-branch <head-branch> --json --page-size 1
```

Then fetch full details with `tuist generate show <id> --json`.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch.

## Step 2: Compare Top-Level Metrics

After fetching both generations, compare:

| Metric | What to check |
|---|---|
| `duration` | Flag if head is >10% slower |
| `status` | Flag if base succeeded but head failed |
| `cacheable_targets` | Note if target count changed |
| `local_cache_target_hits` | Compare local hit counts |
| `remote_cache_target_hits` | Compare remote hit counts |
| `is_ci` | Note if one is CI and the other local |

Compute cache hit rates:
- Base: `(local_hits + remote_hits) / cacheable_targets * 100`
- Head: same formula
- Delta: `head_rate - base_rate`

## Step 3: Analyze Cache Invalidation

If cache hit rate dropped, the key question is: **which target(s) caused the invalidation cascade?**

Cache invalidation typically works like this:
1. A "root cause" target has a direct change (source file modified, build setting changed, etc.)
2. All targets that depend on the root cause target also get invalidated because their `dependencies` hash changes.
3. This cascade can invalidate many targets from a single root change.

### Identifying root cause targets

Without the module cache target detail endpoint (available via MCP), use these heuristics:

1. Check `git diff` between the base and head commits to see which files changed.
2. Map changed files to their Tuist targets/modules.
3. Targets with direct source changes are likely root causes.
4. Targets that only changed due to dependency hash cascading are secondary invalidations.

### Common root causes of cache invalidation

| Cause | Description |
|---|---|
| Source changes | Files in the target's source directory were modified |
| Resource changes | Assets, XIBs, storyboards, or other resources changed |
| Build settings | Target or project build settings were modified |
| Dependency changes | An external dependency version changed |
| Info.plist changes | The target's Info.plist was modified |
| Entitlements changes | The entitlements file was modified |
| Deployment target | The minimum deployment target changed |
| Headers | Public or project headers changed |
| Project settings | Shared project-level settings changed |

## Step 4: Assess Impact

Categorize the cache invalidation:

- **Expected**: Source files were intentionally changed, causing expected cache misses.
- **Unexpected**: No source changes but cache was invalidated (build settings, Xcode version, etc.).
- **Cascade**: A small change invalidated many downstream targets.

## Summary Format

Produce a summary with:

1. **Overall verdict**: Cache hit rate improved, regressed, or stable.
2. **Cache hit rate**: Base rate vs head rate with delta.
3. **Duration**: Absolute and percentage change.
4. **Root cause targets**: Which targets had direct changes.
5. **Cascade impact**: How many targets were invalidated due to dependency cascading.
6. **Recommendations**: How to minimize cache invalidation.

Example:

```
Generation Comparison: base (gen-123 on main) vs head (gen-456 on feature-x)

Duration: 12.5s -> 28.3s (+126%) -- REGRESSION
Cache hit rate: 92% (46/50) -> 64% (32/50) (-28%) -- REGRESSION
Status: success -> success

Root cause: FoundationModule had source changes (3 files modified).
This cascaded to 14 downstream targets that depend on FoundationModule.

Cache invalidation breakdown:
- Direct changes: FoundationModule (sources changed)
- Cascade: NetworkModule, AuthModule, UIModule, + 11 others (dependency hash changed)

Recommendations:
- Consider splitting FoundationModule into smaller modules to reduce cascade impact
- The 14 cascaded targets could benefit from more granular dependency declarations
- If FoundationModule changes frequently, consider interface/implementation module splitting
```

## Done Checklist

- Resolved both base and head generations
- Compared duration and cache hit rates
- Identified root cause targets for cache invalidation
- Analyzed cascade impact
- Provided actionable recommendations for reducing cache misses
compare-gradle-builds5.8 KB

View saved version →

---
name: compare-gradle-builds
description: Compares two Gradle build runs to identify duration regressions, cache changes, and task outcome differences. Can be invoked with build IDs, dashboard URLs, or branch names.
---

# Compare Gradle Builds

## Quick Start

You'll typically receive two build identifiers (IDs, dashboard URLs, or branch names). Follow these steps using the Tuist MCP tools:

1. Use `list_gradle_builds` to find builds on each branch.
2. Use `get_gradle_build` for both base and head builds.
3. Use `list_gradle_build_tasks` to fetch task-level details for both builds.
4. Compare duration, status, cache hit rates, and task outcomes.
5. Summarize regressions, improvements, and recommendations.

If only one identifier is provided, use the project's default branch as the baseline.

## Step 1: Resolve Builds

### If base/head are build IDs or dashboard URLs

Fetch each directly with `get_gradle_build(build_run_id: "<id>")`.

### If base/head are branch names

List recent builds on each branch and pick the latest:

```
list_gradle_builds(account_handle: "...", project_handle: "...", git_branch: "<branch>", page_size: 1)
```

Then fetch full details with `get_gradle_build(build_run_id: "<id>")`.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch with `git rev-parse --abbrev-ref HEAD`.

## Step 2: Compare Top-Level Metrics

After fetching both builds, compare:

| Metric | What to check |
|---|---|
| `duration_ms` | Flag if head is >10% slower than base |
| `status` | Flag if base succeeded but head failed |
| `tasks_local_hit_count` | Compare local cache hit counts |
| `tasks_remote_hit_count` | Compare remote cache hit counts |
| `tasks_executed_count` | Compare how many tasks ran (higher means more cache misses) |
| `cacheable_tasks_count` | Note if the cacheable task count changed |
| `cache_hit_rate` | `(local_hits + remote_hits) / cacheable_tasks_count * 100` |
| `requested_tasks` | Ensure both builds ran the same tasks for a fair comparison |
| `gradle_version` / `java_version` | Note environment differences that affect comparability |

Compute the cache miss delta: `base_executed - head_executed`. Positive means head has fewer executions (improvement). Negative means regression.

## Step 3: Drill Into Tasks

Use `list_gradle_build_tasks` for both builds. Compare `duration_ms` and `outcome` per task path.

Look for:
- Tasks that changed from `local_hit` or `remote_hit` to `executed` (cache invalidation).
- Tasks that changed from `executed` to `local_hit` or `remote_hit` (cache improvement).
- Tasks that changed to `failed` (new failures).
- New tasks that appeared in the head build.
- Tasks with significant duration increases.

Sort by absolute time difference to find the biggest regressions.

### Filtering tasks

Use the `outcome` filter to focus on specific task states:
- `list_gradle_build_tasks(build_run_id: "<id>", outcome: "executed")` to see only executed tasks.
- `list_gradle_build_tasks(build_run_id: "<id>", outcome: "failed")` to see failures.
- `list_gradle_build_tasks(build_run_id: "<id>", cacheable: true)` to see only cacheable tasks.

## Step 4: Investigate Duration Regressions

If the head build is significantly slower:

1. Check if `requested_tasks` differ (different task sets are not directly comparable).
2. Check if cache hit rate dropped, which would explain longer builds.
3. Look for tasks that changed from cache hits to `executed`.
4. Check if `gradle_version` or `java_version` changed, which can affect performance.
5. Compare individual task durations to find the biggest contributors.

## Step 5: Investigate Cache Changes

Compare task-level cache behavior:

- **Hit rate dropped**: Possible causes include dependency changes, build configuration changes, or Gradle version updates that alter cache keys.
- **Hit rate improved**: Likely due to better cache warming or fewer source changes.
- **Task count changed**: New modules or tasks added/removed.
- **Outcome changes**: Tasks moving between `up_to_date`, `local_hit`, `remote_hit`, and `executed` reveal cache effectiveness.

## Step 6: Check Build Context

Compare environment details:

- `gradle_version` and `java_version`: Different versions can affect build times and cache validity.
- `is_ci`: CI vs local builds may have different performance characteristics.
- `git_branch` and `git_commit_sha`: Verify the builds are from the expected commits.
- `root_project_name`: Ensure both builds are from the same project structure.

## Summary Format

Produce a summary with:

1. **Overall verdict**: Better, worse, or neutral compared to base.
2. **Duration**: Absolute and percentage change in `duration_ms`.
3. **Cache hit rate**: Change in hit rate with explanation.
4. **Task outcomes**: Notable outcome changes (hit to executed, new failures).
5. **Status**: Any status changes (success to failure or vice versa).
6. **Environment**: Note any environment differences that affect comparability.
7. **Recommendations**: Actionable next steps based on findings.

Example:

```
Build Comparison: base (abc123 on main) vs head (def456 on feature-x)

Duration: 45200ms -> 62800ms (+39%) -- REGRESSION
Cache hit rate: 85% -> 72% (-13%) -- 8 tasks went from cache hit to executed
Status: success -> success

Root cause: Cache hit rate dropped because 8 tasks had invalidated caches.
The :app:compileKotlin task changed from remote_hit to executed,
cascading to 7 downstream tasks.

Recommendations:
- Investigate which source changes invalidated :app:compileKotlin cache
- Consider splitting large modules to reduce cache invalidation cascading
```

## Done Checklist

- Resolved both base and head builds
- Compared duration, cache, and status metrics
- Drilled into task-level outcomes for both builds
- Identified root causes for any regressions
- Provided actionable recommendations
compare-test-case5.12 KB

View saved version →

---
name: compare-test-case
description: Compares a single test case's behavior across two branches, analyzing pass/fail status, duration, flakiness, and failure details. Useful for investigating test regressions introduced by a feature branch.
---

# Compare Test Case

## Quick Start

You'll typically receive a test case identifier and two branches. Follow these steps:

1. Run `tuist test case show <id-or-identifier> --json` to get the test case metrics.
2. Run `tuist test case run list <identifier> --json` to see runs across branches.
3. Compare behavior between base and head branches.
4. Inspect failures with `tuist test case run show <run-id> --json`.
5. Summarize findings with root cause analysis.

## Step 1: Resolve the Test Case

### By ID or dashboard URL

```bash
tuist test case show <test-case-id> --json
```

### By identifier (Module/Suite/TestCase)

```bash
tuist test case show Module/Suite/TestCase --json
```

### If no test case is provided

Discover flaky or failing tests to investigate:

```bash
tuist test case list --flaky --json --page-size 10
```

Key fields from the response:
- `id` -- unique identifier for subsequent commands
- `name`, `module_name`, `suite_name` -- the test identity
- `reliability_rate` -- percentage of successful runs
- `flakiness_rate` -- percentage of flaky runs in the last 30 days
- `total_runs` / `failed_runs` -- volume context
- `is_flaky` / `is_quarantined` -- current flags

## Step 2: Get Runs on Each Branch

List test case runs filtered by the test case, and look at the `git_branch` field:

```bash
tuist test case run list <identifier> --json --page-size 20
```

Separate runs by branch. For each branch, compute:
- Pass rate: `passed_runs / total_runs * 100`
- Average duration
- Flaky run count
- Most recent status

### Defaults

- If no base branch is provided, use the project's default branch (usually `main`).
- If no head branch is provided, detect the current git branch.

## Step 3: Compare Branch Behavior

| Metric | Base branch | Head branch | Verdict |
|---|---|---|---|
| Pass rate | e.g. 100% | e.g. 60% | REGRESSION |
| Avg duration | e.g. 0.5s | e.g. 2.1s | REGRESSION |
| Flaky runs | 0 | 3 | NEW FLAKINESS |
| Last status | success | failure | REGRESSION |

Classify the change:
- **Newly failing**: 100% pass rate on base, <100% on head
- **Newly flaky**: No flaky runs on base, flaky runs on head
- **Duration regression**: >50% increase in average duration
- **Fixed**: Failing on base, passing on head
- **Stable**: Same behavior on both branches

## Step 4: Inspect Failures

For each failing run on the head branch:

```bash
tuist test case run show <test-case-run-id> --json
```

Examine:
- `failures[].message` -- the assertion or error message
- `failures[].path` -- source file path
- `failures[].line_number` -- exact line of failure
- `failures[].issue_type` -- type of issue
- `repetitions` -- shows retry behavior (e.g., pass-fail-pass means flaky)
- `crash_report` -- crash data if the test runner crashed

## Step 5: Identify Root Cause

Based on the comparison:

### Newly failing
- Check commits between base and head branches for changes to the test file or the code under test.
- Look at the failure message for clues about what changed.

### Newly flaky
- Common patterns: timing/async issues, shared state, environment dependencies.
- Check if `repetitions` show intermittent pass/fail patterns.
- See the fix-flaky-tests skill for detailed flaky test analysis patterns.

### Duration regression
- Check if setup/teardown time increased.
- Check if the test is doing more work (new assertions, larger data sets).
- Check if a dependency became slower.

## Summary Format

Produce a summary with:

1. **Test case info**: Name, module, suite, overall reliability.
2. **Base branch behavior**: Pass rate, avg duration, flaky count.
3. **Head branch behavior**: Pass rate, avg duration, flaky count.
4. **Verdict**: What changed and classification.
5. **Root cause**: Hypothesis based on failure analysis.
6. **Recommendations**: Specific file paths, line numbers, and fix suggestions.

Example:

```
Test Case Comparison: AuthModuleTests/LoginTests/test_login_with_expired_token

Overall reliability: 85% (was 100% before head branch)

Base (main):
  Pass rate: 100% (15/15 runs)
  Avg duration: 0.3s
  Flaky: No

Head (feature/auth-refactor):
  Pass rate: 60% (3/5 runs)
  Avg duration: 0.5s
  Flaky: Yes (2 flaky runs)

Verdict: NEWLY FLAKY -- test was stable on main but intermittently fails on feature branch

Root cause: The auth refactor introduced an async token refresh that races with the
test's synchronous assertion. Failures show "Expected status 401, got nil" at
Tests/AuthModuleTests/LoginTests.swift:42, suggesting the response arrives before
the token refresh completes.

Recommendations:
- Add an await/expectation before the assertion at LoginTests.swift:42
- Consider mocking the token refresh to make the test deterministic
```

## Done Checklist

- Resolved the test case identity
- Gathered runs on both branches
- Compared pass rates, durations, and flakiness
- Inspected failure details for failing runs
- Identified root cause with file paths and line numbers
- Provided actionable fix recommendations
compare-test-runs5.4 KB

View saved version →

---
name: compare-test-runs
description: Compares two test runs to identify new failures, newly flaky tests, fixed tests, and duration regressions. Can be invoked with test run IDs, dashboard URLs, or branch names.
---

# Compare Test Runs

## Quick Start

You'll typically receive two test run identifiers. Follow these steps:

1. Run `tuist test show <id> --json` for both base and head test runs.
2. Run `tuist test module list <test-run-id> --json` and `tuist test suite list <test-run-id> --json` to get module and suite breakdowns.
3. Run `tuist test case run list <identifier> --json` to get individual test case results.
4. Compare failures, flaky tests, durations, and overall status.
5. Inspect failing test cases with `tuist test case run show <id> --json`.
6. Summarize findings with actionable recommendations.

## Step 1: Resolve Test Runs

### If base/head are test run IDs or dashboard URLs

Fetch each directly:

```bash
tuist test show <base-id> --json
tuist test show <head-id> --json
```

### If base/head are branch names

List recent test runs on each branch to identify test run IDs:

```bash
tuist test list --git-branch <base-branch> --json --page-size 5
tuist test list --git-branch <head-branch> --json --page-size 5
```

Pick the latest test run ID from each branch's results.

### Defaults

- If no base is provided, use the project's default branch (usually `main`).
- If no head is provided, detect the current git branch.

## Step 2: Compare Top-Level Metrics

After fetching both test runs, compare:

| Metric | What to check |
|---|---|
| `status` | Flag if base passed but head failed |
| `duration` | Flag if head is >10% slower |
| `total_test_count` | Note if test count changed (new or removed tests) |
| `failed_test_count` | Compare failure counts |
| `flaky_test_count` | Compare flaky counts |
| `avg_test_duration` | Flag significant changes |

## Step 3: Get Module and Suite Breakdowns

Fetch module and suite-level results for both test runs to understand which areas regressed:

```bash
tuist test module list <base-test-run-id> --json
tuist test module list <head-test-run-id> --json

tuist test suite list <base-test-run-id> --json
tuist test suite list <head-test-run-id> --json
```

Match modules and suites by name across both runs to identify areas with new failures or duration regressions.

## Step 4: Get Individual Test Case Results

Fetch test case runs for both test runs:

```bash
tuist test case run list <identifier> --json --page-size 100
```

Match test cases by their `name` + `module_name` + `suite_name` across both runs.

## Step 5: Classify Changes

Group test cases into categories:

1. **New failures**: Tests that passed in base but failed in head.
2. **Fixed tests**: Tests that failed in base but passed in head.
3. **Newly flaky**: Tests not flaky in base but flaky in head.
4. **No longer flaky**: Tests that were flaky in base but stable in head.
5. **New tests**: Tests present in head but not in base.
6. **Removed tests**: Tests present in base but not in head.
7. **Duration regressions**: Tests with >50% duration increase.

## Step 6: Inspect Failures

For each new failure, get detailed information:

```bash
tuist test case run show <test-case-run-id> --json
```

Key fields to examine:
- `failures[].message` -- the assertion or error message
- `failures[].path` -- source file path
- `failures[].line_number` -- exact line of failure
- `failures[].issue_type` -- type of issue
- `repetitions` -- if present, shows retry behavior (flaky detection)
- `crash_report` -- crash data if test runner crashed

## Step 7: Inspect Attachments

The `tuist test case run show` output includes attachment and crash report information. Review:
- Screenshots or UI test artifacts
- Log files or crash reports
- Any diagnostic data attached to failing runs

## Summary Format

Produce a summary with:

1. **Overall verdict**: Better, worse, or neutral compared to base.
2. **New failures**: List each with failure message, file path, and line number.
3. **New flaky tests**: List with flakiness context.
4. **Fixed tests**: List tests that are now passing.
5. **Duration**: Overall and notable per-test regressions.
6. **Recommendations**: Actionable next steps for each issue.

Example:

```
Test Run Comparison: base (run-123 on main) vs head (run-456 on feature-x)

Status: success -> failure -- REGRESSION
Duration: 120.5s -> 145.2s (+21%)
Tests: 342 -> 345 (3 new tests)
Failures: 0 -> 2 (2 new failures)
Flaky: 1 -> 3 (2 newly flaky)

New Failures:
1. AuthModuleTests/LoginTests/test_login_with_expired_token
   Message: "Expected status 401, got 500"
   File: Tests/AuthModuleTests/LoginTests.swift:42
   Likely cause: Server error handling changed for expired tokens

2. NetworkTests/RetryTests/test_retry_on_timeout
   Message: "Timed out waiting for retry"
   File: Tests/NetworkTests/RetryTests.swift:87
   Likely cause: Timeout threshold too low after network layer refactor

Newly Flaky:
1. CacheTests/WriteCacheTests/test_concurrent_writes (flaky in 3/5 runs)

Recommendations:
- Fix expired token handling in AuthModule
- Increase timeout in RetryTests or mock the network layer
- Investigate concurrent write synchronization in CacheTests
```

## Done Checklist

- Resolved both base and head test runs
- Compared top-level metrics
- Fetched module and suite breakdowns for both runs
- Identified new failures, fixed tests, and flaky changes
- Inspected failure details for new failures
- Provided actionable recommendations with file paths
debug-generated-project8.1 KB

View saved version →

---
name: debug-generated-project
description: Debugs issues users encounter with Tuist-generated projects by reproducing the scenario locally, building Tuist from source when needed, and triaging whether it is a bug, misconfiguration, or something that needs team input. Use when users report generation failures, build errors after generation, or unexpected project behavior.
---

# Debug Tuist Project Issue

## Quick Start

1. Ask the user to describe the issue and the project setup (targets, dependencies, configurations, platform).
2. Confirm the issue exists with the latest release by running `mise exec tuist@latest -- tuist generate` against a reproduction project.
3. If confirmed, clone the Tuist repository and build from source to test against main.
4. Triage: fix the bug and open a PR, advise on misconfiguration, or recommend the user files an issue with a reproduction.

## Step 1: Gather Context

Ask the user for:

- What command they ran (e.g. `tuist generate`)
- The error message or unexpected behavior
- **When the issue happens**: generation time, compile time, or runtime (app launch or later)
- Their project structure: targets, platforms, dependencies (SwiftPM, XCFrameworks, local packages)
- Their `Project.swift` and `Tuist.swift` content (or relevant excerpts)
- Their Tuist version (`tuist version`)

The answer to "when" determines the verification strategy:

- **Generation time**: the issue might be a Tuist bug or a project misconfiguration. Reproduce with `tuist generate`.
- **Compile time**: the generated project has incorrect build settings, missing sources, or wrong dependency wiring. Reproduce with `xcodebuild build` after generation.
- **Runtime**: the app builds but crashes or misbehaves on launch or during use. Reproduce by installing and launching on a simulator.

## Step 2: Reproduce with the latest release

Before investigating the source code, confirm the issue is not already fixed in the latest release.

### Set up a temporary reproduction project

```bash
REPRO_DIR=$(mktemp -d)
cd "$REPRO_DIR"
```

Create minimal `Tuist.swift`, `Project.swift`, and source files that reproduce the user's scenario. Keep it as small as possible while still triggering the issue.

### Run generation with the latest Tuist release

```bash
mise exec tuist@latest -- tuist generate --no-open --path "$REPRO_DIR"
```

If the issue involves dependencies, install them first:

```bash
mise exec tuist@latest -- tuist install --path "$REPRO_DIR"
```

### Check the result

- If generation succeeds and the issue is gone, tell the user to update to the latest version.
- If the issue persists, continue to Step 3.

## Step 3: Build Tuist from Source

Clone the repository and build the `tuist` executable and `ProjectDescription` library from source to test against the latest code on `main`.

```bash
TUIST_SRC=$(mktemp -d)
git clone --depth 1 https://github.com/tuist/tuist.git "$TUIST_SRC"
cd "$TUIST_SRC"
swift build --product tuist --product ProjectDescription --replace-scm-with-registry
```

The built binary will be at `.build/debug/tuist`. Use it to test the reproduction project:

```bash
"$TUIST_SRC/.build/debug/tuist" generate --no-open --path "$REPRO_DIR"
```

### If the issue is fixed on main

Tell the user the fix is already on `main`, and it hasn't been released, tell them it'll be in the nest release and point them to the relevant commit if you can identify it.

### If the issue persists on main

Continue to Step 4.

## Step 4: Triage the Issue

Investigate the Tuist source code to understand why the issue occurs.

### Outcome A: It is a bug

1. Identify the root cause in the source code.
2. Apply the fix.
3. Verify by rebuilding and running against the reproduction project:
   ```bash
   cd "$TUIST_SRC"
   swift build --product tuist --product ProjectDescription --replace-scm-with-registry
   "$TUIST_SRC/.build/debug/tuist" generate --no-open --path "$REPRO_DIR"
   ```
4. Zip the reproduction project and include it in the PR:
   ```bash
   cd "$REPRO_DIR" && cd ..
   zip -r reproduction.zip "$(basename "$REPRO_DIR")" -x '*.xcodeproj/*' -x '*.xcworkspace/*' -x 'Derived/*' -x '.build/*'
   ```
5. Open a PR on the Tuist repository with:
   - The fix
   - The zipped reproduction project attached or committed as a fixture
   - A clear description of the root cause and how to verify the fix

### Outcome B: It is a misconfiguration

Tell the user what is wrong and how to fix it. Common misconfigurations:

- Missing `tuist install` before `tuist generate` when using external dependencies
- Incorrect source or resource globs that exclude or double-include files
- Mismatched build configurations between the project and external dependencies
- Wrong product types for dependencies (static vs dynamic)
- Missing `-ObjC` linker flag for Objective-C dependencies
- Using `sources` and `resources` globs together with `buildableFolders`

Provide the corrected manifest snippet so the user can apply the fix directly.

### Outcome C: Unclear or needs team input

If you cannot determine whether it is a bug or misconfiguration, recommend the user:

1. Open a GitHub issue at https://github.com/tuist/tuist/issues with:
   - The reproduction project (zipped)
   - The error output
   - Their Tuist version and environment details

Provide a summary of what you investigated and what you ruled out, so the user does not have to repeat the triage.

## Build Verification

When testing a fix, always verify the full cycle:

```bash
# Build the patched tuist
cd "$TUIST_SRC"
swift build --product tuist --product ProjectDescription --replace-scm-with-registry

# Install dependencies if needed
"$TUIST_SRC/.build/debug/tuist" install --path "$REPRO_DIR"

# Generate the project
"$TUIST_SRC/.build/debug/tuist" generate --no-open --path "$REPRO_DIR"

# Build the generated project
xcodebuild build \
  -workspace "$REPRO_DIR"/*.xcworkspace \
  -scheme <scheme> \
  -destination "platform=iOS Simulator,name=iPhone 16 Pro"
```

## Runtime Verification

When the user reports a runtime issue (crash on launch, missing resources at runtime, wrong bundle structure, or unexpected behavior), you must go beyond building and actually launch the app on a simulator.

### Launch and monitor for crashes

```bash
# Boot a simulator
xcrun simctl boot "iPhone 16 Pro" 2>/dev/null || true

# Build for the simulator
xcodebuild build \
  -workspace "$REPRO_DIR"/*.xcworkspace \
  -scheme <scheme> \
  -destination "platform=iOS Simulator,name=iPhone 16 Pro" \
  -derivedDataPath "$REPRO_DIR/DerivedData"

# Install the app
xcrun simctl install booted "$REPRO_DIR/DerivedData/Build/Products/Debug-iphonesimulator/<AppName>.app"

# Launch and monitor — this will print crash info if the app terminates abnormally
xcrun simctl launch --console-pty booted <bundle-identifier>
```

The `--console-pty` flag streams the app's stdout/stderr so you can observe logs and crash output directly. Watch for:

- **Immediate crash on launch**: usually a missing framework, wrong bundle ID, missing entitlements, or stripped ObjC categories (`-ObjC` linker flag missing)
- **Crash after a few seconds**: often missing resources (images, storyboards, XIBs, asset catalogs) or a bundle structure mismatch
- **Runtime misbehavior without crash**: wrong resource paths, missing localization files, or incorrect Info.plist values

### Check crash logs

If the app crashes without useful console output, pull the crash log:

```bash
# List recent crash logs for the app
find ~/Library/Logs/DiagnosticReports -name "<AppName>*" -newer "$REPRO_DIR" -print
```

Read the crash log to identify the crashing thread and the faulting symbol.

## Done Checklist

- Gathered enough context from the user to reproduce the issue
- Determined whether the issue is at generation time, compile time, or runtime
- Confirmed whether the issue exists in the latest release
- Tested against Tuist built from source (main branch)
- If runtime issue: launched the app on a simulator and verified the crash or misbehavior
- Triaged the issue as a bug, misconfiguration, or unclear
- If bug: applied fix, verified it, and opened a PR with reproduction project
- If misconfiguration: provided corrected manifest to the user
- If unclear: gave the user a summary and recommended next steps
fix-flaky-tests8.44 KB

View saved version →

---
name: fix-flaky-tests
description: Fixes flaky tests by analyzing failure patterns from Tuist test insights, identifying root causes, and applying targeted corrections. Can be invoked with a specific test case URL (e.g. `https://tuist.dev/{account}/{project}/tests/test-cases/{id}`) or without arguments to discover and fix all flaky tests in the project.
---

# Fix Flaky Tests

## Quick Start

You'll typically receive a Tuist test case URL or identifier. Follow these steps to investigate and fix it:

1. Run `tuist test case show <id-or-identifier> --json` to get reliability metrics for the test.
2. Run `tuist test case run list Module/Suite/TestCase --flaky --json` to see flaky run patterns.
3. Run `tuist test case run show <test-case-run-id> --json` on failing flaky runs to get failure messages and file paths.
4. Read the test source at the reported path and line, identify the flaky pattern, and fix it.
5. Verify by running the test multiple times to confirm it passes consistently.

If no specific test is provided, start with the Discovery section below.

## Discovery

When no specific test case is provided, find all flaky tests in the project:

```bash
tuist test case list --flaky --json --page-size 50
```

This returns all test cases currently flagged as flaky. Key fields:
- `module.name` / `suite.name` / `name` — the test identifier
- `avg_duration` — helps prioritize (fix fast unit tests first)
- `is_quarantined` — whether the test is already quarantined

**Triage strategy:**
1. Group tests by suite — multiple flaky tests in the same suite often share a root cause.
2. Check if failures share a `test_run_id` — tests that all failed in the same run may have been killed by a process crash, not individual test bugs.
3. Look at failure messages to categorize: test logic bugs vs infrastructure issues (network errors, server 502s, conflicts on retry).

## Investigation

### 1. Get test case metrics

You can pass either the UUID or the `Module/Suite/TestCase` identifier:

```bash
tuist test case show <id> --json
tuist test case show Module/Suite/TestCase --json
```

Key fields:
- `reliability_rate` — percentage of successful runs (higher is better)
- `flakiness_rate` — percentage of runs marked flaky in the last 30 days
- `total_runs` / `failed_runs` — volume context
- `last_status` — current state

### 2. View flaky run history

```bash
tuist test case run list Module/Suite/TestCase --flaky --json
```

The identifier uses the format `ModuleName/SuiteName/TestCaseName` or `ModuleName/TestCaseName` when there is no suite. This returns only runs that were detected as flaky.

### 3. View full run history

```bash
tuist test case run list Module/Suite/TestCase --json --page-size 20
```

Look for patterns:
- Does it fail on specific branches?
- Does it fail only on CI (`is_ci: true`) or also locally?
- Are failures clustered around specific commits?

### 4. Get failure details

```bash
tuist test case run show <test-case-run-id> --json
```

Key fields:
- `failures[].message` — the assertion or error message
- `failures[].path` — source file path
- `failures[].line_number` — exact line of failure
- `failures[].issue_type` — type of issue (assertion_failure, etc.)
- `repetitions` — if present, shows retry behavior (pass/fail sequence)
- `test_run_id` — the broader test run this execution belongs to
- `crash_report` — crash report data (present when the test runner crashed); contains `exception_type`, `signal`, `exception_subtype`, and `triggered_thread_frames`

## Code Analysis

1. Open the file at `failures[0].path` and go to `failures[0].line_number`.
2. Read the full test function and its setup/teardown.
3. Identify which of the common flaky patterns below applies.
4. Check if the test shares state with other tests in the same suite.

## Common Flaky Patterns

### Timing and async issues
- **Missing waits**: Test checks a result before an async operation completes. Fix: use `await`, expectations with timeouts, or polling.
- **Race conditions**: Multiple concurrent operations access shared state. Fix: synchronize access or use serial queues.
- **Hardcoded timeouts**: `sleep(1)` or fixed delays that are too short on CI. Fix: use condition-based waits instead of fixed delays.

### Shared state
- **Test pollution**: One test modifies global/static state that another test depends on. Fix: reset state in setUp/tearDown or use unique instances per test.
- **Singleton contamination**: Shared singletons carry state between tests. Fix: inject dependencies or reset singletons.
- **File system leftovers**: Tests leave files that affect subsequent runs. Fix: use temporary directories and clean up.

### Environment dependencies
- **Network calls**: Tests hit real services that may be slow or unavailable. Fix: mock network calls.
- **Date/time sensitivity**: Tests depend on current time or timezone. Fix: inject a clock or freeze time.
- **File system paths**: Hardcoded paths that differ between environments. Fix: use relative paths or temp directories.

### Order dependence
- **Implicit ordering**: Test passes only when run after another test that sets up required state. Fix: make each test self-contained.
- **Parallel execution conflicts**: Tests that work in isolation but fail when run concurrently. Fix: use unique resources per test.

### Crashes (identified via `crash_report`)
- `EXC_BREAKPOINT` / `SIGTRAP` — force-unwrap of nil, Swift precondition failure
- `EXC_BAD_ACCESS` / `SIGSEGV` — use-after-free or dangling pointer
- `EXC_CRASH` / `SIGABRT` — uncaught Objective-C exception

## Fix Implementation

After identifying the pattern:

1. Apply the smallest fix that addresses the root cause.
2. Do not refactor unrelated code.
3. If the fix requires a test utility (like a mock or helper), check if one already exists before creating a new one.

## Verification

### Running tests repeatedly

Run the specific test repeatedly until failure using `xcodebuild`'s built-in repetition support:

```bash
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module>/<suite>/<test> -test-iterations <count> -run-tests-until-failure
```

This runs the test up to `<count>` times and stops at the first failure. Choose the iteration count based on how long the test takes — for fast unit tests use 50–100, for slower integration or acceptance tests use 2–5.

### Reproducing before fixing

Before applying a fix, try to reproduce the flaky failure locally. A successful reproduction confirms your root cause analysis and lets you verify the fix directly. Use the "Running tests repeatedly" approach above, or the race condition strategies below if concurrency is suspected.

Some flaky scenarios — especially race conditions, CI-specific timing issues, or environment-dependent failures — may be difficult or impossible to reproduce locally. If you cannot reproduce after reasonable effort, proceed with fixing based on code analysis and failure logs. A fix backed by clear evidence of a bug (e.g. unsynchronized shared state, TOCTOU pattern) is valid even without local reproduction.

### Reproducing race conditions

Race conditions and concurrency bugs often only manifest under CI-level parallelism and are hard to reproduce locally. Try these strategies in order:

1. **Increase parallelism**: Add `-parallel-testing-enabled YES` to run test suites concurrently.
2. **Run broader test suites**: Instead of running a single test, run the entire module (e.g. `-only-testing ModuleTests`) to increase contention on shared resources.
3. **Thread Sanitizer**: Run with TSan enabled to detect data races deterministically. Note: TSan adds overhead which can change timing, so some races may not trigger under TSan.

```bash
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module> -enableThreadSanitizer YES
```

4. **High iteration count with broad scope**: Combine all the above — run the full module with parallelism and many iterations.

If a race condition cannot be reproduced locally but the code is provably thread-unsafe (e.g. unsynchronized mutation of shared state), the fix is still valid. Verify the fix by confirming the tests pass with the same reproduction strategies above. Document in the commit message that the fix addresses a CI-only race condition identified through code analysis and failure logs.

## Done Checklist

- Identified the root cause of flakiness
- Applied a targeted fix
- Verified the test passes consistently (multiple runs)
- Did not introduce new test dependencies or shared state
- Committed the fix with a descriptive message
generated-projects3.91 KB

View saved version →

---
name: generated-projects
description: Guides day-to-day work in Tuist-generated Xcode workspaces, including generation, build and test commands, and buildable folders. Use when working in a Tuist-generated project or when users mention `tuist generate`, `xcodebuild test`, or generated workspaces.
---

# Using Tuist Generated Projects

## Quick Start

```bash
# Generate workspace without opening Xcode
tuist generate --no-open

# Build a scheme with xcodebuild
xcodebuild build -workspace App.xcworkspace -scheme App

# Run tests with xcodebuild
xcodebuild test -workspace App.xcworkspace -scheme AppTests -only-testing AppTests/MyTestCase
```

## Project definition

### Prefer buildable folders

Use `buildableFolders` instead of `sources` and `resources` globs. Buildable folders stay synchronized with the file system, so adding or removing files does not require regeneration.

```swift
let target = Target(
  name: "App",
  buildableFolders: [
    "App/Sources",
    "App/Resources",
  ]
)
```

### Tag targets for focus

Use target tags to group areas of the project, for example:

- `tag:team:*`
- `tag:feature:*`
- `tag:layer:*`

These tags make it easier to scope generation and testing later.

Example target metadata with tags:

```swift
let target = Target(
  name: "PaymentsUI",
  metadata: .metadata(tags: [
    "tag:team:commerce",
    "tag:feature:payments",
    "tag:layer:ui",
  ])
)
```

When working on a focused area, generate only what you need:

```bash
tuist generate tag:feature:payments
tuist generate PaymentsUI PaymentsTests
```

### Align build configurations

Keep build configurations aligned between the project and external dependencies. Use `PackageSettings(settings: .settings(configurations: []))` to mirror project configurations; mismatches emit warnings.

## Workflows

### Generate intentionally

- Use `tuist generate --no-open` in automation and scripts to avoid launching Xcode.
- Regenerate when any manifest changes (or the dependency graph changes).
- If generation fails due to missing products, run `tuist install` to resolve dependencies and retry.

### Build with xcodebuild

Use `xcodebuild build` against the generated workspace and scheme.

```bash
xcodebuild build \
  -workspace App.xcworkspace \
  -scheme App \
  -destination "generic/platform=iOS Simulator"
```

### Test with xcodebuild

Use `xcodebuild test` for running tests locally. Prefer it over `tuist test` because `tuist test` regenerates the project on each invocation, which slows down iteration.

To optimize test run time:

- **Use `--only-testing`** to run only the specific test suite or test case you are working on, instead of the full target.
- **Pick the scheme with the fewest compilation targets** that still includes the test target you need. This minimizes build time before tests run.

```bash
# Run a specific test suite
xcodebuild test \
  -workspace App.xcworkspace \
  -scheme AppTests \
  -only-testing AppTests/MyTestSuite

# Run a single test case
xcodebuild test \
  -workspace App.xcworkspace \
  -scheme AppTests \
  -only-testing AppTests/MyTestSuite/testMyFunction
```

## Guidelines

- Keep `buildableFolders` paths aligned to the target's real file system layout.
- Avoid overlapping `buildableFolders` with `sources` or `resources` globs in the same target.
- Open Xcode manually when needed after running `tuist generate --no-open`.

## Troubleshooting

**Static side effects warnings:** adjust product types deliberately. Use `Target.product` for local targets and `PackageSettings(productTypes:)` for external products. Making everything dynamic with `.framework` can compile and run, but it may hurt launch time. Prefer static products (static frameworks or libraries) when possible and when they do not introduce side effects.

**Objective-C dependency crashes:** add `-ObjC` or `-force_load` via `OTHER_LDFLAGS` on consuming targets as needed. Reference: `https://tuist.dev/en/docs/guides/features/projects/dependencies#objectivec-dependencies`.
migrate5 KB

View saved version →

---
name: migrate
description: Migrates existing Xcode projects to Tuist generated workspaces with build and run validation, external dependency mapping, and migration checklists. Use when adopting Tuist for an existing app or converting a hand-edited Xcode project to generated projects.
---

# Migrating to Tuist Generated Projects

## Quick Start

1. Baseline build and run the app with xcodebuild.
2. Inventory targets, build settings, and external dependencies.
3. Create `Tuist.swift`, `Project.swift`, and `Tuist/Package.swift`.
4. Extract settings into `.xcconfig` files and wire them in `Project.swift`.
5. Generate and build: `tuist generate --no-open` then `xcodebuild build`.
6. Fix build issues, regenerate, and validate runtime on a simulator.

## Preflight Checklist

- Primary app scheme and any extension/test schemes
- Targets list (app, extensions, tests, helper tools)
- Deployment targets and bundle identifiers
- Info.plist locations and entitlements
- Custom build settings (per target and per configuration)
- External dependencies (SPM, XCFrameworks, local packages)
- Build scripts (SwiftGen, Sourcery, codegen)
- Runtime validation plan (simulator destination and launch command)

## Outputs

- `Project.swift` and `Tuist.swift`
- `Tuist/Package.swift` for external dependencies
- `.xcconfig` files (optional but recommended)
- Build and runtime validation notes
- A short migration log of decisions and fixes

## Migration Workflow

### 1. Baseline the project

Start by proving the current project builds and runs. Capture the command you use so the generated workspace can be validated the same way.

```bash
xcodebuild build \
  -project App.xcodeproj \
  -scheme App \
  -configuration Debug \
  -destination "generic/platform=iOS Simulator" \
  -derivedDataPath DerivedDataBaseline
```

### 2. Map targets and settings

List every target and its role. Extract build settings into `.xcconfig` files when they are large or shared across targets. Keep deployment targets and bundle identifiers identical to the original project to avoid runtime surprises.

### 3. Add Tuist manifests

Create the manifests and keep them minimal and close to the existing project.

- `Tuist.swift`: enable generation options you need and keep them explicit.
- `Project.swift`: define targets, sources, resources, scripts, and dependencies.
- `Tuist/Package.swift`: list external dependencies and map product types.

Use `.external` for third-party dependencies to keep the graph consistent.

### 4. Handle sources and resources carefully

Be precise here. Small mistakes often cause large failures later.

- `.intentdefinition` files belong in `sources`, not `resources`.
- `.xcstrings` should remain the primary localization source. Avoid double-including `.strings` or `.stringsdict` from overlapping globs.
- Use `.folderReference` for bundles like `Settings.bundle`.
- If a resource bundle is missing, ensure the package target declares `.process("Resources")`.

### 5. Generate and build

```bash
tuist install
tuist generate --no-open
xcodebuild build \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Debug \
  -destination "generic/platform=iOS Simulator" \
  -derivedDataPath DerivedDataTuist
```

### 6. Resolve build issues iteratively

Common fixes you will likely need:

- **Missing SDK frameworks**: add `.sdk(name: ..., type: .framework)`.
- **SPM resource bundles**: verify `.process("Resources")` and `Bundle.module` usage.
- **File-system-synchronized groups**: avoid over-excluding directories; compare with the pbx if a type vanishes.
- **Invalid bundle identifiers**: override with `PackageSettings` or vendor a local package.
- **Generated sources**: ensure codegen outputs (SwiftGen/Sourcery) are part of the build.

### 7. Validate runtime

A build is not enough; launch the app on a simulator.

```bash
xcrun simctl boot "iPhone 17 Pro"
xcrun simctl install booted DerivedDataTuist/Build/Products/Debug-iphonesimulator/App.app
xcrun simctl launch booted com.example.app
```

## Common Failure Patterns

- **Type not found**: a source file or entire directory was excluded accidentally.
- **Copy Bundle Resources errors**: Swift files are being treated as resources; fix the resource globs.
- **Localization conflicts**: `.xcstrings` colliding with `.strings` globs.
- **Undefined symbols**: missing SDK frameworks or dependency products.
- **Unrecognized selector at launch**: ObjC categories in static frameworks were stripped. Add `-ObjC` to `OTHER_LDFLAGS` or `-force_load` for the library that defines the category.
- **Runtime crash on launch**: mismatched bundle id, missing entitlements, or miswired resources.

## Migration Notes to Capture

- What changed in `Project.swift` and why
- Any exclusions or overrides (and the reason)
- Dependency patches or local vendoring
- The exact build and run commands used for validation

## Done Checklist

- Generated workspace builds cleanly
- App launches on simulator without immediate crash
- All targets and extensions build
- Dependencies are wired through `.external`
- Settings match the original Xcode project
Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package author
Tuist GmbH

Package observed Oct 2, 2026.

Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Oct 2, 2026 · 12:00 UTC
Collection status
Collected

plugin_asdk_app_69fa07fe5ba48191a4ea47c4d7d55a69

Download plugin data (JSON)