Installable skillMIT

axiom-build-debugging

Use when encountering dependency conflicts, CocoaPods/SPM resolution failures, "Multiple commands produce" errors, or framework version mismatches - systematic dependency and build configuration debugging for iOS projects. Includes pressure scenario guidance for resisting quick fixes under time c...

BY CharlesWiltgenUPDATED Oct 27, 2026
INTENDED FOR◈OpenClawInstall in a listed agent.

instructions.md

---
name: axiom-build-debugging
description: "Use when encountering dependency conflicts, CocoaPods/SPM resolution failures, \"Multiple commands produce\" errors, or framework version mismatches - systematic dependency and build configuration debugging for iOS projects. Includes pressure scenario guidance for resisting quick fixes under time c..."
---


# Build Debugging

## Overview

Check dependencies BEFORE blaming code. **Core principle** 80% of persistent build failures are dependency resolution issues (CocoaPods, SPM, framework conflicts), not code bugs.

## Capture Build and Test Diagnostics

Before the next necessary build or test, resolve `tools/axbuild` under the **actual loaded package** in Claude Code (`command -v axbuild`; if that finds nothing, `tools/axbuild` two levels above the `Base directory for this skill:` path of a loaded Axiom skill) or Codex (`<plugin-root>/tools/`), or discover an executable on PATH in Pi. Check executable permission and run its absolute path with `--help`; assign that observed path to `AXBUILD`. Never infer an installation path from an example.

Cursor and MCP distributions do not bundle axbuild and expose no axbuild MCP wrapper. Use the **saved-log fallback** below when a verified shell helper is unavailable.

Before **every** Xcode invocation, run `pgrep -lx xcodebuild; echo "pgrep exit=$?"`: exit 1 means none are running, 0 lists them, and any other exit means the inventory failed. Investigate existing builds; process count and age do not establish zombie status. Never terminate unrelated processes. When interrupted or timed out, axbuild also stops build scripts and other processes descended from its build that Xcode moved into separate process groups, found by parentage when cleanup begins and on each cleanup pass. It reports `cleanup-incomplete` for any such descendant it could not stop or verify (another user's, unreadable, or being debugged). Jobs that had already left the build's process tree, such as daemons or launchd-started tools, are neither stopped nor reported. Inspect ownership before stopping anything it lists. Discover the actual scheme and destination before executing:

```bash
"$AXBUILD" xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" build
"$AXBUILD" swift test --package-path "$PACKAGE"
```

Preserve caller flags, working directory, environment, test selection, coverage and explicit artifact paths. The wrapper adds absent diagnostic defaults; it never cleans caches or automatically rebuilds. Do not pipe a build through another command.

Inspect `command` (native outcome) and `collection` (evidence completeness) separately. Native success can accompany partial collection. The default JSON is bounded to 8,000 UTF-8 bytes; inspect `omissions` and read `artifacts.report` relative to the absolute `artifacts.run` directory for complete records and evaluated values. The startup stderr JSON identifies the run directory before completion. Use diagnostic locations and values rather than rebuilding to redisplay output.

The retained log is the primary compiler source; test results and validated Swift Testing events supplement it. A null failed-test count means uncertainty, not zero. Preserve result bundles for attachment export, coverage, console logs and deeper inspection. Native informational commands pass their output through.

**Saved-log fallback:** redirect the necessary native command's stdout/stderr to a unique file, let it finish, record its exit status and inspect that saved file. An xcresult build summary is not equivalent to the compiler log. Read an existing log before considering another build. If a build's output ends in a truncation marker, or before xcodebuild's closing `** BUILD … **` or `** TEST … **` line, read the saved log or report instead of rebuilding.

<!-- end of shared section; source: skills/axiom-build/SKILL.md, copies regenerated by npm run build:shared -->

## Example Prompts

These are real questions developers ask that this skill is designed to answer:

#### 1. "I added a Swift Package but I'm getting 'No such module' errors. The package is in my Xcode project but won't compile."
→ The skill covers SPM resolution workflows, package cache clearing, and framework search path diagnostics

#### 2. "The build is failing with 'Multiple commands produce' the same output file. How do I figure out which files are duplicated?"
→ The skill shows how to identify duplicate target membership and resolve file conflicts in build settings

#### 3. "CocoaPods installed dependencies successfully but the build still fails. How do I debug CocoaPods issues?"
→ The skill covers Podfile.lock conflict resolution, linking errors, and version constraint debugging

#### 4. "My build works on my Mac but fails on the CI server. Both machines have the latest Xcode. What's different?"
→ The skill explains dependency caching differences, environment-specific paths, and reproducible build strategies

#### 5. "I'm getting framework version conflicts and I don't know which dependency is causing it. How do I resolve this?"
→ The skill demonstrates dependency graph analysis and version constraint resolution strategies for complex dependency trees

---

## Red Flags — Dependency/Build Issues

If you see ANY of these, suspect dependency problem:
- "No such module" after adding package
- "Multiple commands produce" same output file
- Build succeeds on one machine, fails on another
- CocoaPods install succeeds but build fails
- SPM resolution takes forever or times out
- Framework version conflicts in error logs

## Quick Decision Tree

```
Build failing?
├─ "No such module XYZ"?
│  ├─ After adding SPM package?
│  │  └─ Clean build folder + reset package caches
│  ├─ After pod install?
│  │  └─ Check Podfile.lock conflicts
│  └─ Framework not found?
│     └─ Check FRAMEWORK_SEARCH_PATHS
├─ "Multiple commands produce"?
│  └─ Duplicate files in target membership
├─ SPM resolution hangs?
│  └─ Clear package caches + derived data
└─ Version conflicts?
   └─ Use dependency resolution strategies below
```

## Common Build Issues

### Issue 1: SPM Package Not Found

**Symptom**: "No such module PackageName" after adding Swift Package

**❌ WRONG**:
```bash
# Rebuilding without cleaning
xcodebuild build
```

**✅ CORRECT** (after confirming a package-resolution cause):
```bash
# Remove only THIS project's DerivedData folder; other projects and running builds use the rest
DD_SETTINGS=$(mktemp "${TMPDIR:-/tmp}/build-settings.XXXXXX")
# Pass the same -workspace or -project as the build (a CocoaPods folder has both)
xcodebuild -showBuildSettings -workspace YourApp.xcworkspace -scheme YourScheme > "$DD_SETTINGS"
PROJECT_DD=$(sed -n 's/^ *BUILD_DIR = \(.*\)\/Build\/Products$/\1/p' "$DD_SETTINGS" | head -1)
case "$PROJECT_DD" in
  "$HOME/Library/Developer/Xcode/DerivedData/"?*) rm -rf "$PROJECT_DD" ;;
  *) echo "Not under the default DerivedData: '$PROJECT_DD'; inspect before deleting" ;;
esac

# Reset packages; clear the SwiftPM cache (shared by every project) only if resolution still fails
if ! xcodebuild -resolvePackageDependencies -workspace YourApp.xcworkspace -scheme YourScheme; then
  rm -rf ~/Library/Caches/org.swift.swiftpm
  xcodebuild -resolvePackageDependencies -workspace YourApp.xcworkspace -scheme YourScheme
fi

# Clean build, captured with axbuild ("Capture Build and Test Diagnostics" above)
"$AXBUILD" xcodebuild clean build -workspace YourApp.xcworkspace -scheme YourScheme \
  -destination "<ACTUAL_DESTINATION>"
```

### Issue 2: CocoaPods Conflicts

**Symptom**: Pod install succeeds but build fails with framework errors

**Check Podfile.lock**:
```bash
# See what versions were actually installed
cat Podfile.lock | grep -A 2 "PODS:"

# Compare with Podfile requirements
cat Podfile | grep "pod "
```

**Fix version conflicts**:
```ruby
# Podfile - be explicit about versions
pod 'Alamofire', '~> 5.8.0'  # Not just 'Alamofire'
pod 'SwiftyJSON', '5.0.1'     # Exact version if needed
```

**Clean reinstall**:
```bash
# Remove all pods
rm -rf Pods/
rm Podfile.lock

# Reinstall
pod install

# Open workspace (not project!)
open YourApp.xcworkspace
```

### Issue 3: Multiple Commands Produce Error

**Symptom**: "Multiple commands produce '/path/to/file'"

**Cause**: Same file added to multiple targets or build phases

**Fix**:
1. Open Xcode
2. Select file in navigator
3. File Inspector → Target Membership
4. Uncheck duplicate targets
5. Or: Build Phases → Copy Bundle Resources → remove duplicates

### Issue 4: Framework Search Paths

**Symptom**: "Framework not found" or "Linker command failed"

**Check build settings**:
```bash
# Show all build settings
xcodebuild -showBuildSettings -scheme YourScheme | grep FRAMEWORK_SEARCH_PATHS
```

**Fix in Xcode**:
1. Target → Build Settings
2. Search "Framework Search Paths"
3. Add path: `$(PROJECT_DIR)/Frameworks` (recursive)
4. Or: `$(inherited)` to inherit from project

### Issue 5: SPM Version Conflicts

**Symptom**: Package resolution fails with version conflicts

**See dependency graph**:
```bash
# In project directory
swift package show-dependencies

# Or see resolved versions
cat Package.resolved
```

**Fix conflicts**:
```swift
// Package.swift - be explicit
.package(url: "https://github.com/owner/repo", exact: "1.2.3")  // Exact version
.package(url: "https://github.com/owner/repo", from: "1.2.0")   // Minimum version
.package(url: "https://github.com/owner/repo", .upToNextMajor(from: "1.0.0"))  // SemVer
```

**Reset resolution**:
```bash
# Clear package caches
rm -rf .build
rm Package.resolved

# Re-resolve
swift package resolve
```

### Issue 6: `-ld_classic` Silently Ignored (Xcode 27)

**Symptom**: a target that passes `-ld_classic` in `OTHER_LDFLAGS` still links, but behaves as though the flag were absent. The build log carries `ld: warning: -ld_classic is no longer supported and will be ignored`.

**Cause**: Xcode 27 removed the classic linker (ld64). The toolchain ships a single `ld`, and the flag is accepted and discarded. This is a **warning, not an error** — so a project pinned to classic-linker behavior keeps building green while quietly getting the new linker, and only the downstream symptom (duplicate symbols, `-force_load` / `-all_load` ordering, dead-strip differences) shows up.

**Fix**: drop `-ld_classic` from `OTHER_LDFLAGS`, xcconfigs, and CI scripts, then re-test whatever the flag was masking — that original problem is still there and now needs a real fix. The older `-ld64` spelling is worse: it parses as `-l d64` and fails the link with `library 'd64' not found`.

Verified on Xcode 27.0 (27A266a): `clang -Wl,-ld_classic` exits 0 with that warning, and `ld -v` reports `PROJECT:ld-27037.1`.

**Same release, adjacent trap**: Apple's 27 release notes state the Swift dependency scanner now requires **every Clang module reachable from a single dependency-scan action to have a unique module name**; duplicates were previously tolerated and can now error the scan. If scanning started failing right after the upgrade, look for two modules vending the same name rather than for a code change — typically vendored third-party sources that ship a `module.modulemap` redeclaring an SDK module.

## Dependency Resolution Strategies

### Strategy 1: Lock to Specific Versions

When stability matters more than latest features:

**CocoaPods**:
```ruby
pod 'Alamofire', '5.8.0'      # Exact version
pod 'SwiftyJSON', '~> 5.0.0'  # Any 5.0.x
```

**SPM**:
```swift
.package(url: "...", exact: "1.2.3")
```

### Strategy 2: Use Version Ranges

When you want bug fixes but not breaking changes:

**CocoaPods**:
```ruby
pod 'Alamofire', '~> 5.8'     # 5.8.x but not 5.9
pod 'SwiftyJSON', '>= 5.0', '< 6.0'  # Range
```

**SPM**:
```swift
.package(url: "...", from: "1.2.0")              // 1.2.0 and higher
.package(url: "...", .upToNextMajor(from: "1.0.0"))  // 1.x.x but not 2.0.0
```

### Strategy 3: Fork and Pin

When you need custom modifications:

```bash
# Fork repo on GitHub
# Clone your fork
git clone https://github.com/yourname/package.git

# In Package.swift, use your fork
.package(url: "https://github.com/yourname/package", branch: "custom-fixes")
```

### Strategy 4: Exclude Transitive Dependencies

When a dependency's dependency conflicts:

**SPM (not directly supported, use workarounds)**:
```swift
// Instead of this:
.package(url: "https://github.com/problematic/package")

// Fork it and remove the conflicting dependency from its Package.swift
```

**CocoaPods**:
```ruby
# Exclude specific subspecs
pod 'Firebase/Core'  # Not all of Firebase
pod 'Firebase/Analytics'
```

## Build Configuration Issues

### Debug vs Release Differences

**Symptom**: Builds in Debug, fails in Release (or vice versa)

**Check optimization settings**:
```bash
# Compare Debug and Release settings
xcodebuild -showBuildSettings -configuration Debug > debug.txt
xcodebuild -showBuildSettings -configuration Release > release.txt
diff debug.txt release.txt
```

**Common culprits**:
- SWIFT_OPTIMIZATION_LEVEL (-Onone vs -O)
- ENABLE_TESTABILITY (YES in Debug, NO in Release)
- DEBUG preprocessor flag
- Code signing settings

### Workspace vs Project

**Always open workspace with CocoaPods**:
```bash
# ❌ WRONG
open YourApp.xcodeproj

# ✅ CORRECT
open YourApp.xcworkspace
```

**Check which you're building**:
```bash
# For workspace
xcodebuild -workspace YourApp.xcworkspace -scheme YourScheme build

# For project only (no CocoaPods)
xcodebuild -project YourApp.xcodeproj -scheme YourScheme build
```

## Pressure Scenarios: When to Resist "Quick Fix" Advice

### The Problem

Under deadline pressure, senior engineers and teammates provide "quick fixes" based on pattern-matching:
- "Just regenerate the lock file"
- "Increment the build number"
- "Delete DerivedData and rebuild"

These feel safe because they come from experience. **But if the diagnosis is wrong, the fix wastes time you don't have.**

**Critical insight** Time pressure makes authority bias STRONGER. You're more likely to trust advice when stressed.

### Red Flags — STOP Before Acting

If you hear ANY of these, pause 5 minutes before executing:

- ❌ **"This smells like..."** (pattern-matching, not diagnosis)
- ❌ **"Just..."** (underestimating complexity)
- ❌ **"This usually fixes it"** (worked once ≠ works always)
- ❌ **"You have plenty of time"** (overconfidence about 24-hour turnaround)
- ❌ **"This is safe"** (regenerating lock files CAN break things)

**Your brain under pressure** Trusts these phrases because they sound confident. Doesn't ask "but do they have evidence THIS is the root cause?"

### Mandatory Diagnosis Before "Quick Fix"

When someone senior suggests a fix under time pressure:

#### Step 1: Ask (Don't argue)
```
"I understand the pressure. Before we regenerate lock files,
can we spend 5 minutes comparing the broken build to our
working build? I want to know what we're fixing."
```

#### Step 2: Demand Evidence
- "What makes you think it's a lock file issue?"
- "What changed between our last successful build and this failure?"
- "Can we see the actual error from App Store build vs our build?"

#### Step 3: Document the Gamble
```
If we try "pod install":
- Time to execute: 10 minutes
- Time to learn it failed: 24 hours (next submission cycle)
- Remaining time if it fails: 6 days
- Alternative: Spend 1-2 hours diagnosing first

Cost of being wrong with quick fix: High
Cost of spending 1 hour on diagnosis: Low
```

#### Step 4: Push Back Professionally
```
"I want to move fast too. A 1-hour diagnosis now means we
won't waste another 24-hour cycle. Let's document what we're
testing before we submit."
```

#### Why this works
- You're not questioning their expertise
- You're asking for evidence (legitimate request)
- You're showing you understand the pressure
- You're making the time math visible

### Real-World Example: App Store Review Blocker

**Scenario** App rejected in App Store build, passes locally.

**Senior says** "Regenerate lock file and resubmit (7 days buffer)"

#### What you do
1. ❌ WRONG: Execute immediately, fail after 24 hours, now 6 days left
2. ✅ RIGHT: Spend 1 hour comparing builds first

#### Comparison checklist
```
Local build that works:
- Pod versions in Podfile.lock: [list them]
- Xcode version: [version]
- Derived Data: [timestamp]
- CocoaPods version: [version]

App Store build that fails:
- Pod versions used: [from error message]
- Build system: [App Store's environment]
- Differences: [explicitly document]
```

#### After comparison
- If versions match: Lock file isn't the issue. Skip the quick fix.
- If versions differ: Now you understand what to fix.

**Time saved** 24 hours of wasted iteration.

### When to Trust Quick Fixes (Rare)

Quick fixes are safe ONLY when:

- [ ] You've seen this EXACT error before (not "similar")
- [ ] You know the root cause (not "this usually works")
- [ ] You can reproduce it locally (so you know if fix worked)
- [ ] You have >48 hours buffer (so failure costs less)
- [ ] You documented the fix in case you need to explain it later

#### In production crises, NONE of these are usually true.

---

## Testing Checklist

### When Adding Dependencies
- [ ] Specify exact versions or ranges (not just latest)
- [ ] Check for known conflicts with existing deps
- [ ] Test clean build after adding
- [ ] Commit lockfile (Podfile.lock or Package.resolved)

### When Builds Fail
- [ ] Run mandatory environment checks (xcode-debugging skill)
- [ ] Check dependency lockfiles for changes
- [ ] Verify using correct workspace/project file
- [ ] Compare working vs broken build settings

### Before Shipping
- [ ] Test both Debug and Release builds
- [ ] Verify all dependencies have compatible licenses
- [ ] Check binary size impact of dependencies
- [ ] Test on clean machine or CI

## Common Mistakes

### ❌ Not Committing Lockfiles
```bash
# ❌ BAD: .gitignore includes lockfiles
Podfile.lock
Package.resolved
```

**Why**: Team members get different versions, builds differ

### ❌ Using "Latest" Version
```ruby
# ❌ BAD: No version specified
pod 'Alamofire'
```

**Why**: Breaking changes when dependency updates

### ❌ Mixing Package Managers
```
Project uses both:
- CocoaPods (Podfile)
- Carthage (Cartfile)
- SPM (Package.swift)
```

**Why**: Conflicts are inevitable, pick one primary manager

### ❌ Not Cleaning After Dependency Changes
```bash
# ❌ BAD: Just rebuild
xcodebuild build

# ✅ GOOD: Clean first
xcodebuild clean build
```

### ❌ Opening Project Instead of Workspace
When using CocoaPods, always open .xcworkspace not .xcodeproj

## Command Reference

```bash
# CocoaPods
pod install                    # Install dependencies
pod update                     # Update to latest versions
pod update PodName             # Update specific pod
pod outdated                   # Check for updates
pod deintegrate                # Remove CocoaPods from project

# Swift Package Manager
swift package resolve          # Resolve dependencies
swift package update           # Update dependencies
swift package show-dependencies # Show dependency tree
swift package reset            # Reset package cache
xcodebuild -resolvePackageDependencies  # Xcode's SPM resolve

# Carthage
carthage update                # Update dependencies
carthage bootstrap             # Download pre-built frameworks
carthage build --platform iOS  # Build for specific platform

# Xcode Build
xcodebuild clean               # Clean build folder
xcodebuild -list               # List schemes and targets
xcodebuild -showBuildSettings  # Show all build settings
```

## Real-World Impact

**Before** (trial-and-error with dependencies):
- Dependency issue: 2-4 hours debugging
- Clean builds not run consistently
- Version conflicts surprise team
- CI failures from dependency mismatches

**After** (systematic dependency management):
- Dependency issue: 15-30 minutes (check lockfile → resolve)
- Clean builds mandatory after dep changes
- Explicit version constraints prevent surprises
- CI matches local builds (committed lockfiles)

**Key insight** Lock down dependency versions early. Flexibility causes more problems than it solves.

## Resources

**Docs**: swift.org/package-manager, /xcode/build-system

**GitHub**: Carthage/Carthage

**Skills**: axiom-build (skills/xcode-debugging.md)
COMPATIBILITY & REFERENCES

Platform guidance

Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.

Resource notes updated 2026-10-27 · Original instructions by CharlesWiltgen