## Audit & Quality Tools Guide

Standalone audit commands  -  runs directly via Bash, **no MCP server dependency**. These are the same checks that multi-agent-toolkit-mcp provides as MCP tools, but embedded here as pipeline skills.

### Trigger Model

**These audits are on-demand.** They run when:

1. User explicitly requests: `/multi-agent test "accessibility"`, `/multi-agent test "store-ready"`
2. User asks during Phase 5: "run accessibility audit", "check store compliance"
3. Pipeline suggests and user confirms: "UI changes detected  -  want to run accessibility audit?"

**Pipeline never runs audits without user intent.** Phase 4 does code-level review (free, automatic). Phase 5 does device-level audit only when requested.

---

### iOS Accessibility Audit

**When**: Phase 5  -  user requests, app is running on simulator
**What it checks**: Missing labels, small tap targets (<44pt), missing identifiers

**How to run** (requires ui-tree-dumper.swift in project or ~/.claude/scripts/):

```bash
# 1. Get UI tree from running simulator
UI_TREE=$(swift "$HOME/.claude/scripts/ui-tree-dumper.swift" 10 2>/dev/null)

# 2. Parse and audit (pipeline does this in-context)
# The AI reads the JSON output and checks:
#   - Interactive elements (AXButton, AXLink, AXTextField, AXSwitch, AXSlider)
#   - Missing: title, description, AND value all null → "Missing accessibility label"
#   - Missing: identifier null → "Missing accessibility identifier"
#   - Small: frame.w < 44 OR frame.h < 44 → "Tap target too small"
```

**Scope filtering**: If only checking a specific screen, filter by identifier prefix:

```
Only audit elements where identifier starts with "{scope}_"
Skip elements outside scope  -  report scanned vs skipped count
```

**Output format** (AI generates this from parsed tree):

```
Accessibility Audit:
  Scope: {scope or "all"}
  Scanned: {N} interactive elements

  CRITICAL: {N} missing labels
    - AXButton at (120, 340)  -  no label, identifier: "btn_close"

  IMPORTANT: {N} small tap targets
    - AXButton "Submit"  -  32x32pt (min 44x44)

  WARNING: {N} missing identifiers
    - AXTextField at (20, 200)  -  has label "Email" but no identifier
```

---

### Android Accessibility Audit

**When**: Phase 5  -  user requests, app is running on emulator
**What it checks**: Missing contentDescription, small touch targets (<48dp), missing resource-id

```bash
# 1. Dump UI hierarchy
adb shell uiautomator dump /sdcard/_audit_ui.xml
adb pull /sdcard/_audit_ui.xml /tmp/_audit_ui.xml
adb shell rm /sdcard/_audit_ui.xml

# 2. Read XML
cat /tmp/_audit_ui.xml
```

**AI parses XML and checks** each `<node>` with `clickable="true"`:

- `content-desc=""` AND `text=""` → "Missing contentDescription"
- `resource-id=""` → "Missing resource-id"
- bounds `[x1,y1][x2,y2]` where `(x2-x1) < 48` or `(y2-y1) < 48` → "Touch target too small"

**Scope filtering**: Only audit nodes where `resource-id` contains `{scope}` prefix.

---

### iOS Biometric Test

**When**: Phase 5  -  auth flow testing

```bash
DEVICE_ID=$(xcrun simctl list devices booted -j | python3 -c "import sys,json; devs=json.load(sys.stdin)['devices']; print(next(d['udid'] for ds in devs.values() for d in ds if d['state']=='Booted'))")

# Enroll biometric
xcrun simctl keychain $DEVICE_ID biometric-enroll --face

# Test success
xcrun simctl keychain $DEVICE_ID biometric-match --face
# → Take screenshot, verify success screen

# Test failure
xcrun simctl keychain $DEVICE_ID biometric-match --face --no-match
# → Take screenshot, verify error handling
```

---

### Android Launch Time

**When**: Phase 5  -  performance baseline

```bash
# Force stop first (cold start)
adb shell am force-stop {package_name}

# Launch with timing
adb shell am start -W -n {package_name}/.MainActivity 2>&1
# Output includes:
#   TotalTime: 487
#   WaitTime: 501
```

**Evaluation**:

- < 500ms → excellent
- 500-1000ms → acceptable
- 1000-2000ms → warning: "Cold start > 1s"
- \> 2000ms → important: "Cold start > 2s, likely impacts retention"

---

### iOS Archive Audit (App Store Compliance)

**When**: Phase 6  -  release branches only, user confirms
**Input**: Path to .xcarchive

```bash
ARCHIVE="/path/to/MyApp.xcarchive"
APP_DIR=$(find "$ARCHIVE/Products/Applications" -name "*.app" -maxdepth 1 | head -1)
APP_NAME=$(basename "$APP_DIR")
BINARY="$APP_DIR/${APP_NAME%.app}"

# 1. Binary size
stat -f "%z" "$BINARY" 2>/dev/null
# → > 500MB = warning

# 2. Debug tool leak
nm "$BINARY" 2>/dev/null | grep -iE "FLEX|Reveal|Stetho|Flipper|CocoaDebug|Pulse" | head -10
# → Any match = CRITICAL

# 3. Code signing
codesign -dvv "$APP_DIR" 2>&1
# → "not signed" = CRITICAL

# 4. Debug entitlements
codesign -d --entitlements - "$APP_DIR" 2>/dev/null | grep "get-task-allow"
# → get-task-allow = true → CRITICAL (debug build)

# 5. Info.plist
plutil -convert json -o - "$APP_DIR/Info.plist" 2>/dev/null
# Check: CFBundleShortVersionString exists, NSAllowsArbitraryLoads not true

# 6. Privacy manifest
find "$APP_DIR" -name "PrivacyInfo.xcprivacy" | head -1
# → Not found = WARNING

# 7. Privacy permission strings
plutil -convert json -o - "$APP_DIR/Info.plist" 2>/dev/null | python3 -c "
import sys,json; p=json.load(sys.stdin)
keys=['NSCameraUsageDescription','NSPhotoLibraryUsageDescription','NSLocationWhenInUseUsageDescription','NSMicrophoneUsageDescription','NSFaceIDUsageDescription']
for k in keys:
    if k in p: print(f'  {k}: {p[k]}')
"

# 8. Embedded frameworks
ls "$APP_DIR/Frameworks/" 2>/dev/null
```

**Verdict**:

- Any CRITICAL → "FAIL  -  must fix before submission"
- Only WARNING → "WARN  -  review before submission"
- All pass → "PASS  -  ready for App Store"

---

### Android APK Audit (Play Store Compliance)

**When**: Phase 6  -  release branches only, user confirms
**Input**: Path to .apk

```bash
APK="/path/to/app-release.apk"

# 1. Basic info (requires aapt2 or aapt from Android SDK Build-Tools)
aapt2 dump badging "$APK" 2>/dev/null
# Extract: package name, versionName, versionCode, targetSdkVersion, sdkVersion
# → targetSdk < 34 = WARNING

# 2. Debuggable check
aapt2 dump badging "$APK" 2>/dev/null | grep "application-debuggable"
# → Found = CRITICAL

# 3. Permissions
aapt2 dump badging "$APK" 2>/dev/null | grep "uses-permission"
# → Count dangerous permissions (CAMERA, CONTACTS, LOCATION, etc.)
# → > 5 dangerous = WARNING

# 4. Signing
apksigner verify --print-certs "$APK" 2>&1
# → "DOES NOT VERIFY" = CRITICAL
# → Only v1 signature = WARNING

# 5. File size
stat -f "%z" "$APK" 2>/dev/null
# → > 150MB = WARNING (suggest App Bundle .aab)

# 6. DEX count (R8/ProGuard check)
unzip -l "$APK" 2>/dev/null | grep "classes.*\.dex" | wc -l
# → > 3 DEX files = WARNING (ensure minification enabled)
```

---

### Integration with Pipeline Phases

| Phase            | What Happens                                        | Method                          |
| ---------------- | --------------------------------------------------- | ------------------------------- |
| Phase 4 (Review) | Accessibility check  -  **code-level only**           | AI reads source code, no device |
| Phase 5 (Test)   | Accessibility audit  -  **device-level, on-demand**   | Bash commands above             |
| Phase 5 (Test)   | Biometric test  -  **on-demand**                      | `xcrun simctl keychain`         |
| Phase 5 (Test)   | Launch time  -  **on-demand**                         | `adb shell am start -W`         |
| Phase 6 (Commit) | Archive/APK audit  -  **release branches, on-demand** | Bash commands above             |

### Graceful Degradation

- No Xcode installed → skip iOS audits, log "Xcode not available"
- No Android SDK → skip Android audits, log "Android SDK not available"
- No booted simulator → skip accessibility audit, log "No running simulator"
- Audits are **enhancements**, never blockers for the pipeline flow
