Title: Warden: code-review · Issue #459 · getsentry/XcodeBuildMCP · GitHub
Open Graph Title: Warden: code-review · Issue #459 · getsentry/XcodeBuildMCP
X Title: Warden: code-review · Issue #459 · getsentry/XcodeBuildMCP
Description: Warden Scheduled Scan Results Run: 2026-07-06T09:18:38.560Z Commit: db7e055 Summary Severity Count High 5 Medium 19 Low 16 Findings src/mcp/tools/project-scaffolding/scaffold_ios_project.ts W2X-8CE orientationToIOSConstant never matches ...
Open Graph Description: Warden Scheduled Scan Results Run: 2026-07-06T09:18:38.560Z Commit: db7e055 Summary Severity Count High 5 Medium 19 Low 16 Findings src/mcp/tools/project-scaffolding/scaffold_ios_project.ts W2X-8CE...
X Description: Warden Scheduled Scan Results Run: 2026-07-06T09:18:38.560Z Commit: db7e055 Summary Severity Count High 5 Medium 19 Low 16 Findings src/mcp/tools/project-scaffolding/scaffold_ios_project.ts W2X-8CE...
Opengraph URL: https://github.com/getsentry/XcodeBuildMCP/issues/459
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Warden: code-review","articleBody":"## Warden Scheduled Scan Results\n\n**Run:** 2026-07-06T09:18:38.560Z\n**Commit:** `db7e055`\n\n### Summary\n\n| Severity | Count |\n|----------|-------|\n| High | 5 |\n| Medium | 19 |\n| Low | 16 |\n\n### Findings\n\n#### [`src/mcp/tools/project-scaffolding/scaffold_ios_project.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts)\n\n- `W2X-8CE` **`orientationToIOSConstant` never matches schema enum values — raw strings written to xcconfig** ([L42-L56](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts#L42-L56)) · high\n The switch statement matches PascalCase strings (`'Portrait'`, `'LandscapeLeft'`, etc.) but the Zod schema emits lowercase kebab-case values (`'portrait'`, `'landscape-left'`, `'portrait-upside-down'`). Every call falls through to `default: return orientation`, so the xcconfig file is written with raw values like `portrait` instead of `UIInterfaceOrientationPortrait`.\n- `KAR-8DS` **Hardcoded `simulatorName: 'iPhone 17'` causes runtime failure when that simulator is not installed** ([L404](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts#L404)) · medium\n The next-step params always request `'iPhone 17'`, which will cause a build or launch error on any machine (CI or developer) that does not have an iPhone 17 simulator installed — consider using a more broadly available model like `'iPhone 16'` or reading the available simulators dynamically.\n\n#### [`src/utils/log-paths.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/log-paths.ts)\n\n- `5X2-42T` **Path traversal via `..` workspace key bypasses validation** ([L44-L49](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/log-paths.ts#L44-L49)) · high\n The `normalizeWorkspaceKey` function blocks `/` and `\\` but not `..`, so a caller passing `\"..\"` would escape the workspaces directory (e.g. resolving to the app root). Consider adding `if (normalized === '..' || normalized.startsWith('../') ...)` or checking `path.normalize(normalized) !== normalized`.\n\n#### [`src/utils/template-manager.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/template-manager.ts)\n\n- `P7M-R5N` **`process.chdir` in async context causes process-wide side effects and race conditions** ([L99-L113](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/template-manager.ts#L99-L113)) · high\n Using `process.chdir` in an async function changes the working directory for the entire Node.js process; concurrent template downloads (e.g. iOS and macOS simultaneously) will race on CWD. Replace with `unzip -q -d \u003ctempDir\u003e \u003czipPath\u003e` to extract to a specific directory without touching CWD.\n\n#### [`src/utils/video_capture.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/video_capture.ts)\n\n- `TWP-RPL` **`process.on('exit', stopAll)` fires async cleanup that Node.js will abandon** ([L137](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/video_capture.ts#L137)) · high\n The `exit` event fires synchronously; Node.js abandons any pending async work immediately after all `exit` listeners return, so the `void stopSession(...)` promises will never resolve and recording sessions won't be stopped on normal process exit.\n\n#### [`src/utils/xcodemake.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcodemake.ts)\n\n- `J34-4TT` **Downloaded executable has no integrity verification before execution** ([L61-L79](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcodemake.ts#L61-L79)) · high\n The `installXcodemake` function fetches a script from GitHub's `main` branch and immediately makes it executable without verifying a checksum or signature — a compromised repository or MITM could silently deliver malicious code to users' machines.\n\n#### [`src/benchmarks/claude-ui/harness.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/benchmarks/claude-ui/harness.ts)\n\n- `EH5-M5B` **`resolveSuitePath` silently returns a non-existent path when no suite file is found** ([L107](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/benchmarks/claude-ui/harness.ts#L107)) · medium\n When `matches.length === 0`, the function falls through to `return candidates[0]!` and returns a path that doesn't exist on disk, rather than throwing a descriptive error. Downstream callers will get a confusing file-not-found failure instead of a clear \"suite not found\" message. Consider throwing here analogously to the multi-match case.\n- `KRW-9LC` **Write streams lack error handlers, risking uncaught exceptions** ([L302-L303](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/benchmarks/claude-ui/harness.ts#L302-L303)) · medium\n The `stdout` and `stderr` `WriteStream`s created via `createWriteStream` have no `'error'` listeners; if a write fails (e.g. disk full), Node.js will throw an uncaught exception rather than routing the error through `rejectCommand`. Consider adding `stdout.on('error', rejectCommand)` and `stderr.on('error', rejectCommand)` immediately after the streams are created.\n *Suggested fix:* Attach error handlers to the write streams immediately after creation.\n\n#### [`src/cli/cli-tool-catalog.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/cli-tool-catalog.ts)\n\n- `FUF-KM2` **No test coverage for `buildCliToolCatalog` business logic** ([L157](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/cli-tool-catalog.ts#L157)) · medium\n The new `buildCliToolCatalog` function and its helpers (tool deduplication by `cliName`, merging catalog, `getBridgeDiscoveryTimeoutMs` env-var override) have no corresponding test file — consider adding unit tests covering the merge/dedup path and the daemon-not-running early-return paths.\n\n#### [`src/cli/daemon-control.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/daemon-control.ts)\n\n- `QYK-22S` **`ensureDaemonRunning` silently returns when `status()` fails with a non-version-mismatch error** ([L213](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/daemon-control.ts#L213)) · medium\n When `isRunning()` returns `true` but `status()` throws any error that is not a `DaemonVersionMismatchError` (e.g. a timeout, connection reset, or unexpected protocol error), the `else { return; }` branch causes the function to return silently as if the daemon is ready, violating the documented contract that it \"returns when the daemon is ready to accept requests.\"\n\n#### [`src/daemon/framing.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/daemon/framing.ts)\n\n- `5KJ-U9L` **O(n²) buffer growth from repeated Buffer.concat on every data chunk** ([L25](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/daemon/framing.ts#L25)) · medium\n Calling `Buffer.concat([buffer, chunk])` on every `data` event copies the entire accumulated buffer into a new allocation each time. For a large message fragmented across many small TCP chunks this becomes quadratic in the total message size; consider using a preallocated ring buffer or an array of chunks flushed only when a complete frame is ready.\n\n#### [`src/mcp/tools/debugging/debug_attach_sim.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/debugging/debug_attach_sim.ts)\n\n- `V4F-NEZ` **`waitFor: true` with `bundleId` fails at PID resolution before attach can wait** ([L143-L157](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/debugging/debug_attach_sim.ts#L143-L157)) · medium\n When `waitFor: true` is used with `bundleId` (its documented-only-valid mode), `resolveSimulatorAppPid` is called first and throws if the app isn't already running — making it impossible to use `waitFor` to attach *before* a process launches, which is the common expectation for the flag.\n\n#### [`src/mcp/tools/simulator/get_sim_app_path.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator/get_sim_app_path.ts)\n\n- `5HV-3G4` **nextStepParams uses hardcoded 'SIMULATOR_UUID' instead of actual simulator ID from params** ([L171-L177](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator/get_sim_app_path.ts#L171-L177)) · medium\n The `boot_sim`, `install_app_sim`, and `launch_app_sim` entries in `nextStepParams` use the literal string `'SIMULATOR_UUID'` instead of the actual simulator identifier from `params.simulatorId ?? params.simulatorName`, making these hints non-functional. Compare with `boot_sim.ts` which uses `resolvedParams.simulatorId` for the same fields.\n *Suggested fix:* Use actual simulator param values in nextStepParams\n\n#### [`src/mcp/tools/swift-package/swift_package_run.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/swift-package/swift_package_run.ts)\n\n- `V68-K22` **Underlying process keeps running after timeout — resource leak** ([L292-L295](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/swift-package/swift_package_run.ts#L292-L295)) · medium\n When the timeout fires and wins the race, the `commandPromise` (and its child `swift` process) continues running until natural completion because there is no reference to the `ChildProcess` before the promise resolves — consider wrapping the executor call in an `AbortController` or storing the process handle so it can be killed on timeout.\n- `2CE-RUE` **`resolveExecutablePath` spawns an extra subprocess even when background execution fails** ([L193-L199](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/swift-package/swift_package_run.ts#L193-L199)) · low\n Move the `resolveExecutablePath` call to after the `if (!result.success)` guard so `swift build --show-bin-path` is not run unnecessarily on failure.\n\n#### [`src/snapshot-tests/suites/coverage-suite.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/coverage-suite.ts)\n\n- `8GV-L7J` **`beforeAll` timeout too tight: inner `harness.invoke` alone can consume the full 120s budget** ([L42](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/coverage-suite.ts#L42)) · medium\n The `beforeAll` timeout is 120,000ms but it also runs `harness.invoke('simulator', 'test', ...)`, which has its own 120,000ms internal timeout (`SNAPSHOT_COMMAND_TIMEOUT_MS`). Any time spent on harness creation, simulator boot, or temp directory setup will eat into the shared budget, causing `beforeAll` to be killed by Vitest before a clean subprocess-level timeout or assertion can fire. Consider increasing the `beforeAll` timeout to at least 240,000ms to allow for setup overhead on top of the subprocess limit.\n\n#### [`src/snapshot-tests/suites/swift-package-suite.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/swift-package-suite.ts)\n\n- `4F7-2NG` **`harness.cleanup()` throws if `beforeAll` fails before assignment** ([L62](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/swift-package-suite.ts#L62)) · medium\n If `resetSwiftPackageState()` or `createHarnessForRuntime()` throws in `beforeAll`, `harness` remains `undefined`, and `harness.cleanup()` in `afterAll` will throw a `TypeError`, masking the original error and potentially leaving resources uncleaned.\n\n#### [`src/snapshot-tests/xcode-ide-availability.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/xcode-ide-availability.ts)\n\n- `ABH-ZGR` **Two sequential blocking `execSync` calls at module load time can stall the event loop up to 16 seconds** ([L6-L14](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/xcode-ide-availability.ts#L6-L14)) · medium\n Both `execSync` calls run synchronously at module evaluation time (invoked in `xcode-ide-suite.ts` top-level `const XCODE_IDE_BRIDGE_READY = isXcodeIdeBridgeAvailable()`), so if either command hangs until its 8 s timeout the Node.js event loop is blocked for up to 16 s before any test can start. Consider making the check lazy (call it inside `beforeAll`) or using `spawnSync` with a shorter probe timeout if the only goal is exit-code detection.\n\n#### [`src/utils/debugger/backends/lldb-cli-backend.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/debugger/backends/lldb-cli-backend.ts)\n\n- `9HW-DE5` **`resume()` response leaks into the next command's output buffer** ([L92](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/debugger/backends/lldb-cli-backend.ts#L92)) · medium\n After `resume()` writes `process continue\n`, it returns without emitting a sentinel, so LLDB's response (e.g. \"Process 1234 resuming\") accumulates in `this.buffer`. The next enqueued `runCommand` call's sentinel match will capture this leftover data as part of its output, causing `assertNoLldbError` to potentially throw spuriously if that response contains \"error:\", or silently corrupting the returned output.\n\n#### [`src/utils/log-capture/simulator-launch-oslog-registry.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/log-capture/simulator-launch-oslog-registry.ts)\n\n- `3KA-6VF` **N+1 `ps` subprocess spawning when batch `sampleProcessCommands` fails** ([L342-L346](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/log-capture/simulator-launch-oslog-registry.ts#L342-L346)) · medium\n When the batch `sampleProcessCommands` call returns `null` (i.e., `ps` itself fails), both `listSimulatorLaunchOsLogProtectedPaths` (line 342) and `listSimulatorLaunchOsLogRegistryRecords` (line 387) fall through to sequential `isRecordActive` calls that each re-invoke `sampleProcessCommands([record.helperPid])` — spawning N additional failing `ps` processes instead of reusing the known-failed result.\n\n#### [`src/utils/logger.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/logger.ts)\n\n- `KVH-D8C` **Log file writes bypass severity level filter** ([L269-L275](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/logger.ts#L269-L275)) · medium\n The file stream write at line 269 checks only `clientLogLevel !== 'none'` but doesn't compare severity, so debug-level messages are written to the log file even when `clientLogLevel` is set to a coarser level like `'error'`. Consider calling `shouldLog(level)` before writing to the file stream, or restructure so the file write follows the same severity check.\n\n#### [`src/utils/nskeyedarchiver-parser.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/nskeyedarchiver-parser.ts)\n\n- `HFL-RM7` **Run destination silently skipped when ActiveScheme is absent** ([L128-L135](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/nskeyedarchiver-parser.ts#L128-L135)) · medium\n When `activeSchemeIdx === -1` (no `ActiveScheme` key present) but `activeRunDestIdx !== -1`, `findDictWithKey(objects, -1)` can never match a UID of `-1`, so `parentDict` is `undefined` and the function returns early before ever attempting to extract the run destination. Have you considered looking up the parent dict using `activeRunDestIdx` as a fallback when `activeSchemeIdx === -1`?\n\n#### [`src/utils/renderers/cli-text-renderer.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/renderers/cli-text-renderer.ts)\n\n- `AKV-ECB` **No test coverage for `xcodebuild-line` processing path** ([L350-L409](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/renderers/cli-text-renderer.ts#L350-L409)) · medium\n The new `xcodebuild-line` case and its supporting functions (`ensureParserState`, `drainParserState`, `flushParserStates`) have no tests in `cli-text-renderer.test.ts`, leaving the multi-operation parser state lifecycle, fragment buffering, and flush-on-finalize behaviour entirely untested.\n\n#### [`src/benchmarks/claude-ui/config.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/benchmarks/claude-ui/config.ts)\n\n- `TTD-YTB` **`timeoutSeconds` accepts zero, negative, NaN, or Infinity without validation error** ([L264](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/benchmarks/claude-ui/config.ts#L264)) · low\n Consider using `readOptionalPositiveFiniteNumber` instead of `readOptionalNumber` for `timeoutSeconds`, as a value of `0`, `-1`, `NaN`, or `Infinity` silently passes config parsing but would cause the first-run prompt dismissal timeout to fire immediately or behave unpredictably at runtime.\n *Suggested fix:* Replace `readOptionalNumber` with `readOptionalPositiveFiniteNumber` for the `timeoutSeconds` field.\n\n#### [`src/cli/commands/daemon.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/commands/daemon.ts)\n\n- `FE3-QR2` **Sequential daemon status checks in `daemon list` scale linearly with number of hung daemons** ([L295-L308](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/commands/daemon.ts#L295-L308)) · low\n In the `daemon list` command, each registered daemon's status is checked serially with `await client.status()` inside a `for...of` loop. In the common failure modes (socket missing / connection refused) `DaemonClient.request()` rejects immediately, so this is only noticeable when a socket exists but the daemon process is hung — in that case each hung entry burns the full 1000 ms timeout, and total wall time grows as `N × 1000 ms`. Since this is a user-invoked CLI listing command with typically few entries, impact is limited, but running the checks concurrently via `Promise.allSettled` would cap worst-case latency at ~1 s regardless of N.\n\n#### [`src/cli/commands/setup.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/commands/setup.ts)\n\n- `T6N-TM7` **withSpinner shows start message on task failure instead of a failure indicator** ([L112-L113](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/cli/commands/setup.ts#L112-L113)) · low\n In the `catch` block of `withSpinner`, `s.stop(opts.startMessage)` is called with the start message rather than a failure message. When a task throws, the spinner halts with the \"starting\" label (e.g. \"Discovering projects…\") as its final displayed text before the error propagates, which is confusing UX. Consider passing a distinct failure string (e.g. `` `Failed: ${opts.startMessage}` ``) and/or a non-zero exit code to `s.stop` so users can distinguish success from failure.\n\n#### [`src/mcp/tools/doctor/doctor.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/doctor/doctor.ts)\n\n- `HWU-TMW` **Doctor reports Sentry enabled incorrectly when `XCODEBUILDMCP_SENTRY_DISABLED` is set** ([L713](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/doctor/doctor.ts#L713)) · low\n The Sentry status line only checks `SENTRY_DISABLED`, so users who disabled Sentry via the CLI setup (which sets `XCODEBUILDMCP_SENTRY_DISABLED=true`) will incorrectly see \"Sentry enabled: Yes\" in doctor output. Consider also checking `doctorInfo.environmentVariables.XCODEBUILDMCP_SENTRY_DISABLED !== 'true'`.\n\n#### [`src/mcp/tools/simulator-management/set_sim_location.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator-management/set_sim_location.ts)\n\n- `KBC-HWJ` **NaN coordinates bypass manual range validation and are forwarded to xcrun** ([L18](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator-management/set_sim_location.ts#L18)) · low\n Because `NaN \u003c -90` and `NaN \u003e 90` both evaluate to `false`, `NaN` latitude or longitude values silently pass the bounds checks on lines 74 and 82 and are forwarded to xcrun as `\"NaN,NaN\"`, producing a confusing tool error instead of the expected validation error. Consider using `z.number().finite().min(-90).max(90)` and `z.number().finite().min(-180).max(180)` in the schema to reject NaN/Infinity at parse time.\n\n#### [`src/mcp/tools/simulator/build_run_sim.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator/build_run_sim.ts)\n\n- `ZL8-QL4` **Two redundant `simctl list` calls when resolving and booting a named simulator** ([L338-L344](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/mcp/tools/simulator/build_run_sim.ts#L338-L344)) · low\n When `simulatorName` is provided, `determineSimulatorUuid` (line 305) issues `xcrun simctl list devices available -j` to find the UUID, then `findSimulatorById` (line 338) immediately issues an identical `xcrun simctl list devices available --json` to read the device state. The device state is already available from the first list call; the second process fork is redundant.\n\n#### [`src/smoke-tests/test-helpers.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/smoke-tests/test-helpers.ts)\n\n- `5NE-KGF` **Keyword-based error detection produces false positives and false negatives** ([L13-L18](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/smoke-tests/test-helpers.ts#L13-L18)) · low\n Scanning message text for substrings like `'error'`, `'fail'`, `'missing'`, etc. will match success messages that happen to contain those words (e.g. \"no failures found\", \"error-free build\") and miss error responses that use different phrasing, making test assertions unreliable.\n\n#### [`src/snapshot-tests/suites/session-management-suite.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/session-management-suite.ts)\n\n- `93L-BXM` **No error-path tests for session management operations** ([L48](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/snapshot-tests/suites/session-management-suite.ts#L48)) · low\n The suite only covers success paths; have you considered adding tests for error cases such as using a non-existent profile, providing both `global: true` and `profile` simultaneously, or calling `use-defaults-profile` with an empty profile name?\n\n#### [`src/utils/platform-detection.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/platform-detection.ts)\n\n- `NPN-RN3` **Missing test case for neither-path-provided error branch** ([L74-L82](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/platform-detection.ts#L74-L82)) · low\n The `else` branch at lines 74–82 (when both `projectPath` and `workspacePath` are `undefined`) has no corresponding test, leaving this error path unverified.\n\n#### [`src/utils/renderers/event-formatting.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/renderers/event-formatting.ts)\n\n- `RMV-6QW` **Module-level `resolvedDiagnosticPathCache` has no eviction policy** ([L180](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/renderers/event-formatting.ts#L180)) · low\n `resolvedDiagnosticPathCache` is a module-scoped `Map` in `event-formatting.ts` with no `clear()`, size cap, or LRU eviction. In a long-running MCP server it accumulates one entry per unique `(baseDir, bareFilename)` pair across all sessions/projects. In practice, growth is bounded by the number of distinct bare source filenames per project (the cache is only consulted when `filePath` contains no path separator), so leakage is modest — but for a daemon serving many projects over long periods it still grows monotonically. Consider capping via a small LRU or clearing at build-session boundaries.\n\n#### [`src/utils/xcode.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcode.ts)\n\n- `7J8-DDP` **Missing unit tests for new `constructDestinationString` helper** ([L6](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcode.ts#L6)) · low\n `constructDestinationString` in `src/utils/xcode.ts` has multiple branches (simulator+ID, simulator+name with/without `useLatest`, simulator missing both → throws, macOS with/without `arch`, and generic destinations for iOS/watchOS/tvOS/visionOS) and is already consumed by three call sites (`build-utils.ts`, `build_run_sim.ts`, `get_sim_app_path.ts`). No `src/utils/__tests__/xcode.test.ts` exists, while sibling utilities under `src/utils/__tests__/` follow the project's pattern of unit-testing helper logic. Adding focused unit tests for each branch (including the throw path and the fall-through log) would guard against regressions as more callers adopt it.\n\n#### [`src/utils/xcodebuild-run-state.ts`](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcodebuild-run-state.ts)\n\n- `7LQ-PSG` **Compiler diagnostic and test-failure dedup share a single Set (mixed key namespaces)** ([L152](https://github.com/getsentry/XcodeBuildMCP/blob/db7e055c39d4e2c23cbd030f14ac8bc8329f61d8/src/utils/xcodebuild-run-state.ts#L152)) · low\n `seenDiagnostics` in `createXcodebuildRunState` is used both for compiler-diagnostic keys (`normalizeDiagnosticKey` → `${location}|${message}`) and test-failure keys (`normalizeTestFailureKey` → `${test}|${location}|${message}` or `${suite}|${test}|${message}`). Practically, compiler diagnostic locations are file paths (e.g. `foo.swift:10:5`) while test failure keys begin with lowercased test identifiers, so real-world collisions are extremely unlikely. However, mixing two distinct key namespaces in one Set is a minor code smell and a latent hazard if key formats evolve (e.g. a future change that omits location or reorders fields). Consider using separate `seenCompilerDiagnosticKeys` and `seenTestFailureKeys` Sets so the two dedup domains cannot interfere.\n\n#### General\n\n- `A6K-URN` **No tests for new daemon CLI commands** · medium\n The entire `src/cli/commands/daemon.ts` module has no corresponding test file — `src/cli/commands/__tests__/` contains tests for `init`, `setup`, and `upgrade` but nothing for `daemon`.\n- `CXR-M9C` **No unit tests for `lldb-cli-backend.ts`** · medium\n The `backends/__tests__/` directory contains only `dap-backend.test.ts`; the new `LldbCliBackend` — with its buffer state machine, sentinel parsing, timeout handling, and command queue — has no test coverage at all.\n- `GAG-MX4` **Missing unit tests for `tools` command logic (`buildToolList`, workflow filtering, `--json` output)** · low\n The `tools` CLI command's core logic — `buildToolList`, `isToolCanonicalInWorkflow`, `getCanonicalWorkflow`, workflow filtering, and the `--json`/`--flat` output shapes — is not covered by unit tests. Only a smoke test in `cli-surface.test.ts` asserts that the word \"build\" appears in the default output. Consider extracting these helpers (or exporting them for tests) and adding cases that cover: canonical vs. non-canonical classification, `--workflow` filtering, and both grouped and flat `--json` outputs. This would reduce regression risk as workflows evolve.\n- `2FD-6HB` **Missing dedicated tests for `tool-catalog.ts` resolution and dedup logic** · low\n `src/runtime/tool-catalog.ts` introduces non-trivial behavior in `createToolCatalog.resolve()` (exact match, kebab alias, ambiguous alias, MCP-name fallback, notFound) plus `seenMcpNames` dedup and `buildToolCatalogFromManifest` filtering/module-cache/error-continue paths. None of these branches are covered by a dedicated test file. Consider adding a `tool-catalog.test.ts` covering: kebab-alias resolution, ambiguous alias handling, MCP-name lookup, duplicate MCP-name skip, and manifest build error recovery. This is a coverage suggestion rather than a blocker.\n- `B76-6KE` **New `device-name-resolver` module has no test coverage** · low\n No `src/utils/__tests__/device-name-resolver.test.ts` exists, leaving cache logic, failure fallback, and `formatDeviceId` output entirely untested despite comprehensive test coverage for all other utils modules.\n\n---\n*Generated by [Warden](https://github.com/getsentry/warden)*","author":{"url":"https://github.com/github-actions[bot]","@type":"Person","name":"github-actions[bot]"},"datePublished":"2026-07-06T09:18:39.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/459/XcodeBuildMCP/issues/459"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ab5a60fb-99d7-d008-e263-5e4b178dfce5 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D82E:37D99:32ABBDF:4811973:6A4D0FC5 |
| html-safe-nonce | 816125a375c05a7635f2fb0917f02651d83565018456186a148c3cda19cdc10a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEODJFOjM3RDk5OjMyQUJCREY6NDgxMTk3Mzo2QTREMEZDNSIsInZpc2l0b3JfaWQiOiIzMDQyMTQ0MDQ2NDE3Nzc2NTgxIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 9ad5761c434591a833bf32ba8427e577c2a22fb942b839d2c4adc879e55b2c1a |
| hovercard-subject-tag | issue:4817707843 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/getsentry/XcodeBuildMCP/459/issue_layout |
| twitter:image | https://opengraph.githubassets.com/c23f8e9dd82bb3bc910e1788c39a8f0256c5374d9cba454a72e6e1ce799065ae/getsentry/XcodeBuildMCP/issues/459 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/c23f8e9dd82bb3bc910e1788c39a8f0256c5374d9cba454a72e6e1ce799065ae/getsentry/XcodeBuildMCP/issues/459 |
| og:image:alt | Warden Scheduled Scan Results Run: 2026-07-06T09:18:38.560Z Commit: db7e055 Summary Severity Count High 5 Medium 19 Low 16 Findings src/mcp/tools/project-scaffolding/scaffold_ios_project.ts W2X-8CE... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | github-actions[bot] |
| hostname | github.com |
| expected-hostname | github.com |
| None | bcb4661e6fb4fe8b394c51ea02ccdb2236d40dc59afc75a3bbba50bf6517134c |
| turbo-cache-control | no-preview |
| go-import | github.com/getsentry/XcodeBuildMCP git https://github.com/getsentry/XcodeBuildMCP.git |
| octolytics-dimension-user_id | 1396951 |
| octolytics-dimension-user_login | getsentry |
| octolytics-dimension-repository_id | 945551361 |
| octolytics-dimension-repository_nwo | getsentry/XcodeBuildMCP |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 945551361 |
| octolytics-dimension-repository_network_root_nwo | getsentry/XcodeBuildMCP |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | fb70bd3c4b2bec429781b65419e912c66e2d5581 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width