memorydetective 1.17.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.17.1] - 2026-05-16
10
+
11
+ Docs-only patch. Syncs the npm README + USAGE with the v1.17 surface notes shipped to GitHub right after v1.17.0 was published. No code changes.
12
+
13
+ ### Changed
14
+
15
+ - README leads with the v1.17 reliability headlines; env-var table prefaced with the strtobool truthy parsing rule; macOS 26.x callout cites the new `bundleStatus` field on `recordTimeProfile`, the `savedOutsideWatchDir` field on `recordViaInstrumentsApp`, and the fault-tolerant `inspectTrace` fallback. Capture / record section renumbered from (3) to (4) with the previously-missing `recordViaInstrumentsApp` row. `verifyFix` + `countAlive` + `inspectTrace` + `recordTimeProfile` rows annotated with their v1.17 surface additions.
16
+ - USAGE.md follow-up prompts table gained a `verifyFix` example using `{ pattern, mode: "exact" }`. Troubleshooting recovery list for the macOS 26.x recording wedge documents the `savedOutsideWatchDir` AppleScript fallback.
17
+
9
18
  ## [1.17.0] - 2026-05-16
10
19
 
11
20
  Reliability pass. v1.16 closed the macOS 26.x recording gap with `recordViaInstrumentsApp`. v1.17 sweeps the audit punch list that surfaced after dogfooding: 14 bugs across three tiers, 3 known limitations documented, 3 tech-debt items deferred. The headlines are env-var truthy parsing (every `MEMORYDETECTIVE_*` boolean now accepts `1 / true / yes / on`, was `1`-only), a `verifyFix` whitelist that supports exact / substring / regex modes (was substring-only), the `recordViaInstrumentsApp` watcher catching saves outside the watch dir, and the `inspectTrace` fault-tolerant fallback so a wedged 52K bundle no longer throws. 41 MCP tools, 701 tests (+24 vs v1.16).
package/README.md CHANGED
@@ -17,17 +17,17 @@
17
17
  - **MCP-native.** Plugs into Claude Code, Claude Desktop, Cursor, Cline, and any other MCP client. The agent drives the full investigate → classify → suggest-fix loop without you opening Instruments.
18
18
  - **Honest about its limits.** No mocked outputs, no over-promises. Hangs analysis works clean from `xctrace`; sample-level Time Profile is parsed when `xctrace` symbolicates the trace and returns a structured workaround notice when it can't (the underlying `xctrace` SIGSEGV on heavy unsymbolicated traces is an Apple-side limitation we surface explicitly). Memory Graph capture works on Mac apps and iOS simulator; physical iOS devices still need Xcode.
19
19
 
20
- > **What's new in v1.16** (2026-05-15): macOS 26.x recording-unblock release. New `recordViaInstrumentsApp` MCP tool (41st) wraps the Instruments.app GUI flow: opens the app, surfaces step-by-step instructions, watches a directory for the saved `.trace`, and chains into `inspectTrace` on success. Until Apple fixes the `xcrun xctrace record` regression on macOS 26.x sims, this is the automated path. Times out after `timeoutSec` (default 10 min). 666 677 tests. 41 MCP tools.
20
+ > **What's new in v1.17** (2026-05-16): reliability pass. 14 bug fixes across three tiers swept after dogfooding. Headlines: every `MEMORYDETECTIVE_*` boolean env var now accepts the strtobool truthy set (`1 / true / yes / on`, was `1`-only, with a one-time stderr warning on unrecognized values); `verifyFix.expectedAliveClasses` supports per-entry exact / substring / regex match modes (was substring-only); `recordViaInstrumentsApp` catches traces saved outside `watchDir` via an Instruments.app AppleScript document query; `inspectTrace` fault-tolerant fallback returns `ok: true` with diagnosis text on wedged 52K bundles instead of throwing; `countAlive` framework-noise filter is now configurable with audit mode; `countByClassWithBytes` reports min / max / median for variable-size classes like `NSData`. 677 → 701 tests. Still 41 MCP tools (reliability tightening, no surface changes).
21
21
  >
22
- > **Also recent (v1.15)**: schema coverage + verify-fix UX. Three new MCP trace tools filled the remaining schema gap: `analyzeMemoryFootprint` (38th, VM resident / dirty / virtual + jetsam diagnosis), `analyzeEnergyImpact` (39th, battery drain investigation), `analyzeLeakTimeline` (40th, xctrace's leaks instrument as a time series). `summarizeTrace` now chains `analyzeNetworkActivity` so the synthesis card covers network alongside hangs / hitches / time-profile / allocations / launch. `replayScenario` captures simulator screenshots per step (DebugSwift-inspired).
22
+ > **Also recent (v1.16)**: macOS 26.x recording-unblock release. New `recordViaInstrumentsApp` MCP tool (41st) wraps the Instruments.app GUI flow: opens the app, surfaces step-by-step instructions, watches a directory for the saved `.trace`, and chains into `inspectTrace` on success. Until Apple fixes the `xcrun xctrace record` regression on macOS 26.x sims, this is the automated path. Times out after `timeoutSec` (default 10 min).
23
23
  >
24
- > **And v1.14**: trace-side reliability. Fixed two parser bugs against real Apple traces, added `analyzeNetworkActivity`, extended `analyzeHangs` for `hang-risks`, pattern-matching schema discovery refactor, unified `supportStatus[]`, three opt-in macOS 26.x UX paths, FLEX-inspired `countAlive` size view, MLeaksFinder + DebugSwift-inspired `verifyFix` whitelist.
24
+ > **And v1.15**: schema coverage + verify-fix UX. Three new MCP trace tools filled the remaining schema gap: `analyzeMemoryFootprint` (38th, VM resident / dirty / virtual + jetsam diagnosis), `analyzeEnergyImpact` (39th, battery drain investigation), `analyzeLeakTimeline` (40th, xctrace's leaks instrument as a time series). `summarizeTrace` now chains `analyzeNetworkActivity`. `replayScenario` captures simulator screenshots per step.
25
25
  >
26
- > **Earlier**: v1.13 shipped `summarizeTrace` + `/summarize-trace` MCP prompt. v1.12 completed reference-tree propagation across countAlive / findRetainers / verifyFix and added auto cross-schema correlation in analyzeHangs. v1.11 added inspectTrace, diffMemgraphs reference-tree, and CLI abandoned-memory mini-table. v1.10 closed the notelet retro feedback loop. v1.9 shipped analyzeAbandonedMemory, detectLeaksInXCTest, cleanupTraces, mainThreadViolations. Full notes in [CHANGELOG](./CHANGELOG.md).
26
+ > **Earlier**: v1.14 trace-side reliability, `analyzeNetworkActivity`, unified `supportStatus[]`, FLEX-inspired `countAlive` size view, MLeaksFinder + DebugSwift-inspired `verifyFix` whitelist. v1.13 shipped `summarizeTrace` + `/summarize-trace` MCP prompt. v1.12 completed reference-tree propagation. v1.11 added inspectTrace, diffMemgraphs reference-tree. v1.9 shipped analyzeAbandonedMemory, detectLeaksInXCTest, cleanupTraces, mainThreadViolations. Full notes in [CHANGELOG](./CHANGELOG.md).
27
27
 
28
28
  > **Heads up for macOS 26.x users:** Apple shipped a `task_for_pid` kernel regression on macOS 26.x that blocks `leaks --outputGraph`, `heap`, AND `xctrace --template Allocations` against iOS simulator processes regardless of `MallocStackLogging`. Even Xcode's "View Memory Graph Hierarchy" hits it unless `Malloc Stack Logging` is enabled in the scheme's Diagnostics tab. memorydetective surfaces this as a proactive `platformAdvisory` on the first capture-class tool call, plus a `workaroundNotice` with `issue: "macos-26-task-for-pid-broken"` if `leaks` is invoked. **The most reliable workaround today is to target an iOS 18 simulator runtime** (install via Xcode > Settings > Platforms > +iOS 18.x). Empirically validated in the [notelet investigation](https://github.com/carloshpdoc/memorydetective/blob/main/CHANGELOG.md#unreleased) 2026-05-12 where three independent CLI memory-introspection paths all failed before iOS 18 was identified as the working escape hatch. Set `MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY=1` to silence the notice once you have settled on a workaround.
29
29
 
30
- > **Also on macOS 26.x: `xctrace record` is broken for simulator targets.** Independent from the `task_for_pid` regression above, `xcrun xctrace record --time-limit Ns` against iOS simulator processes wedges past the time limit, eventually exits when killed, and the resulting `.trace` bundle is missing template metadata. `xctrace export --toc` then fails with `Document Missing Template Error`. Re-validated against Xcode 26.5 (build 17F42, xctrace 16.0) 2026-05-15: regression survives the update. This hits the entire `xctrace`-based ecosystem the same way (`memorydetective.recordTimeProfile`, XcodeTraceMCP, and naked `xcrun xctrace record` calls all fail identically). **Workarounds:** (1) **v1.16: use `recordViaInstrumentsApp`**, which opens Instruments.app for you, prompts you to record + save the `.trace`, then chains into `inspectTrace` automatically once the bundle appears in the watch directory; (2) record from an older macOS host with Xcode 26.0 if you have one; (3) record against a physical device (the regression appears to be simulator-specific). USAGE.md > Troubleshooting has a step-by-step.
30
+ > **Also on macOS 26.x: `xctrace record` is broken for simulator targets.** Independent from the `task_for_pid` regression above, `xcrun xctrace record --time-limit Ns` against iOS simulator processes wedges past the time limit, eventually exits when killed, and the resulting `.trace` bundle is missing template metadata. `xctrace export --toc` then fails with `Document Missing Template Error`. Re-validated against Xcode 26.5 (build 17F42, xctrace 16.0) 2026-05-15: regression survives the update. This hits the entire `xctrace`-based ecosystem the same way (`memorydetective.recordTimeProfile`, XcodeTraceMCP, and naked `xcrun xctrace record` calls all fail identically). **Workarounds:** (1) **use `recordViaInstrumentsApp`** (v1.16, hardened in v1.17), which opens Instruments.app for you, prompts you to record + save the `.trace`, then chains into `inspectTrace` automatically once the bundle appears. v1.17 also catches saves outside the watch directory via an Instruments.app AppleScript document query, returning `savedOutsideWatchDir: true` plus the actual path; (2) record from an older macOS host with Xcode 26.0 if you have one; (3) record against a physical device (the regression appears to be simulator-specific). v1.17 added a viability probe on `recordTimeProfile` (`bundleStatus: "wedged"` when the on-disk bundle is the 52K stub) and on `inspectTrace` (returns `ok: true` with diagnosis text instead of throwing). USAGE.md > Troubleshooting has a step-by-step.
31
31
 
32
32
  ## Quickstart
33
33
 
@@ -288,16 +288,18 @@ Copilot's MCP integration moves fast. If this snippet is stale, see the [VS Code
288
288
 
289
289
  ### Environment variables
290
290
 
291
+ Every boolean `MEMORYDETECTIVE_*` flag below accepts the **strtobool** truthy set (case-insensitive): `1 / true / t / yes / y / on` (truthy) and `0 / false / f / no / n / off` (falsy). Unrecognized values emit a one-time stderr warning per variable and fall back to the documented default. Pre-v1.17 the parser was `1`-only, which caused silent no-ops when operators exported `=true` or `=yes`. The advisory warning is gated on `MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY`.
292
+
291
293
  | Variable | Default | Effect |
292
294
  |---|---|---|
293
295
  | `MEMORYDETECTIVE_REDACTION` | `balanced` | Output scrubbing applied to every tool response. `balanced` collapses home-directory paths to `~/...` and masks token-shaped secrets (AWS keys, GitHub PATs, Stripe, Slack, Bearer auth). `strict` adds hostname, IPv4, and bundle-identifier masking. `off` disables redaction (useful for local-only debugging). Mode is logged once at server startup. |
294
- | `MEMORYDETECTIVE_ALLOW_LAUNCH` | unset | Set to `1` to allow `bootAndLaunchForLeakInvestigation`. The tool executes `xcodebuild` and `xcrun simctl launch` against caller-supplied paths and bundle ids, so opt-in is required. Without the var set, the tool returns `ok: false` with `state: launchNotAllowed` and a clear explanation. Set this only when you trust the inputs the agent is producing. |
296
+ | `MEMORYDETECTIVE_ALLOW_LAUNCH` | unset | Boolean (strtobool). Allows `bootAndLaunchForLeakInvestigation`. The tool executes `xcodebuild` and `xcrun simctl launch` against caller-supplied paths and bundle ids, so opt-in is required. Without the gate, the tool returns `ok: false` with `state: launchNotAllowed` and a clear explanation. Set this only when you trust the inputs the agent is producing. |
295
297
  | `MEMORYDETECTIVE_MAX_RECORDING_SECONDS` | `300` | Cap on `recordTimeProfile.durationSec`. Requests above the cap are rejected with a clear error. Bounded internally to a 3600s (1h) hard ceiling so a misconfigured env var cannot disable the gate. |
296
298
  | `MEMORYDETECTIVE_TRACE_ROOT` | `~/Library/Application Support/memorydetective/traces` | Directory used when `recordTimeProfile.output` is a relative path. Absolute paths bypass this default for v1.8 backwards-compat. Also the default scan path for `cleanupTraces`. The directory is auto-created on first write. |
297
- | `MEMORYDETECTIVE_ALLOW_EXTERNAL_CLEANUP` | unset | Set to `1` to allow `cleanupTraces` to scan and delete `.trace` bundles OUTSIDE `MEMORYDETECTIVE_TRACE_ROOT`. Without it, requests that resolve outside the configured root return `ok: false` with the failure reason and delete nothing. Default-deny on destructive disk operations outside the configured boundary. |
298
- | `MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY` | unset | Set to `1` to silence the macOS 26.x platform advisory that captureMemgraph, captureScenarioState, and bootAndLaunchForLeakInvestigation emit on first use. Useful once you have an iOS 18 sim runtime installed and do not need the reminder. |
299
- | `MEMORYDETECTIVE_AUTO_OPEN_INSTRUMENTS` | unset | Set to `1` to make `recordTimeProfile` invoke `open -a Instruments <tracePath>` as a fire-and-forget escape hatch when xctrace times out (the macOS 26.x regression). The partial `.trace` opens in Instruments.app, which can still symbolicate + display traces the CLI export path rejects. Off by default so unattended agent runs and CI do not spam the user's GUI. The response's `openedInInstrumentsApp` field reports whether the open was invoked. |
300
- | `MEMORYDETECTIVE_PREFLIGHT_XCTRACE` | unset (auto) | Controls the pre-flight probe in `recordTimeProfile` that detects the macOS 26.x xctrace wedge in ~3-5 seconds instead of paying the user's full `durationSec` plus 30s grace. `1` forces on regardless of platform / target. `0` forces off. When unset, the probe auto-enables on macOS 26.x simulator attach (the known-broken combo) and stays off elsewhere. Pre-flight is skipped for `--launch` mode to avoid double-launching the app. Side-effect of auto-enable: 2-second probe runs before the full recording starts. Set to `0` if you have already settled on a workaround and want maximum recording-start latency. |
299
+ | `MEMORYDETECTIVE_ALLOW_EXTERNAL_CLEANUP` | unset | Boolean (strtobool). Allows `cleanupTraces` to scan and delete `.trace` bundles OUTSIDE `MEMORYDETECTIVE_TRACE_ROOT`. Without it, requests that resolve outside the configured root return `ok: false` with the failure reason and delete nothing. Default-deny on destructive disk operations outside the configured boundary. |
300
+ | `MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY` | unset | Boolean (strtobool). Silences the macOS 26.x platform advisory that captureMemgraph, captureScenarioState, and bootAndLaunchForLeakInvestigation emit on first use. Also silences the v1.17 stderr warnings emitted on unrecognized boolean values (any `MEMORYDETECTIVE_*` flag) and on `schemaDiscovery` TOC fetch failures. Useful once you have an iOS 18 sim runtime installed and do not need the reminders. |
301
+ | `MEMORYDETECTIVE_AUTO_OPEN_INSTRUMENTS` | unset | Boolean (strtobool). Makes `recordTimeProfile` invoke `open -a Instruments <tracePath>` as a fire-and-forget escape hatch when xctrace times out (the macOS 26.x regression). v1.17 adds a `MANIFEST.plist` viability check before opening so the auto-open path skips wedged 52K stub bundles (which would otherwise present a "Document Missing Template Error" dialog in Instruments.app). The response's `openedInInstrumentsApp` field reports whether the open was invoked; `bundleStatus` (v1.17) reports whether the bundle on disk is `unknown` / `salvageable` / `wedged`. |
302
+ | `MEMORYDETECTIVE_PREFLIGHT_XCTRACE` | unset (auto) | Boolean (strtobool) + `auto`. Controls the pre-flight probe in `recordTimeProfile` that detects the macOS 26.x xctrace wedge in ~3-5 seconds instead of paying the user's full `durationSec` plus 30s grace. Truthy forces on regardless of platform / target. Falsy forces off. When unset, the probe auto-enables on macOS 26.x simulator attach (the known-broken combo) and stays off elsewhere. Pre-flight is skipped for `--launch` mode to avoid double-launching the app. Side-effect of auto-enable: 2-second probe runs before the full recording starts. |
301
303
 
302
304
  ---
303
305
 
@@ -321,11 +323,11 @@ The cycle classifier ships **36 named antipatterns** spanning SwiftUI (including
321
323
  | `analyzeMemgraph` | Run `leaks` against a `.memgraph` and return summary (totals, ROOT CYCLE blocks, plain-English diagnosis). |
322
324
  | `findCycles` | Extract just the ROOT CYCLE blocks as flattened chains, with optional `className` substring filter. |
323
325
  | `findRetainers` | "Who is keeping `<class>` alive?". Returns retain chain paths from a top-level node down to the match. |
324
- | `countAlive` | Count instances by class. Provide `className` for one number, or omit for top-N most-leaked classes. |
326
+ | `countAlive` | Count instances by class. Provide `className` for one number, or omit for top-N most-leaked classes. v1.17: configurable noise filter (`excludeFrameworkNoise`, `additionalNoisePatterns`, `unsuppressClassPatterns`, `noiseAuditMode`) so the actionable view is tunable per app. Variable-size classes report `instanceSizeBytesMin / Max / Median` (was first-observed value pre-v1.17). |
325
327
  | `reachableFromCycle` | Cycle-scoped reachability. "How many `<X>` instances are reachable from the cycle rooted at `<Y>`?". Distinguishes the actual culprit from its retained dependencies. |
326
328
  | `diffMemgraphs` | Compare two `.memgraph` snapshots: total deltas + class-count changes + cycles new/gone/persisted. |
327
329
  | `analyzeAbandonedMemory` | Diff two `.memgraph` snapshots on heap reference-tree class counts (not cycle list) and classify each grown class as `kvo-observer-orphaned`, `notificationcenter-observer-leaked`, `cache-too-aggressive`, `singleton-retains-payload`, or `unknown-growth`. Surfaces the family of bugs `leaks(1)` reports as `leakCount: 0` because no strict cycle exists. v1.10 adds `actionableGrowth[]` + `actionableShrinkage[]` (framework-noise-filtered views) and supports `outputFormat: "verify-fix-table"` which emits a focused Class \| Before \| After \| Delta markdown table directly. |
328
- | `verifyFix` | Cycle-semantic diff: per-pattern PASS/PARTIAL/FAIL verdict + bytes freed. CI-gateable. |
330
+ | `verifyFix` | Cycle-semantic diff: per-pattern PASS/PARTIAL/FAIL verdict + bytes freed. CI-gateable. `expectedAliveClasses` whitelist (v1.14) carves out singletons / caches / OS-retained windows so they do not vote FAIL; v1.17 extends each entry to per-mode matching (`{ pattern, mode: "exact" \| "substring" \| "regex" }`), with bare strings keeping the substring default. |
329
331
  | `classifyCycle` | Match each ROOT CYCLE against a built-in catalog of **36 named antipatterns** (SwiftUI / Combine / Concurrency / UIKit / Core Animation / Core Data / Coordinator / RxSwift / Realm) with confidence + textual `fixHint` + `staticAnalysisHint` (which SwiftLint rule complements this, or explicit gap) + `fixTemplate` (Swift before/after snippet). |
330
332
  | `analyzeHangs` | Parse `xctrace` `potential-hangs` schema; return Hang vs Microhang counts + top N longest. Pass `topFramesByHangStartNs` (typically from a chained `analyzeTimeProfile` correlation) to enrich each top hang with `mainThreadViolations[]` classifying the blocker as `sync-io`, `db-lock`, `network`, or `lock-contention`. |
331
333
  | `analyzeAnimationHitches` | Parse `xctrace` `animation-hitches` schema; report by-type counts and how many hitches crossed Apple's user-perceptible 100ms threshold. |
@@ -334,11 +336,12 @@ The cycle classifier ships **36 named antipatterns** spanning SwiftUI (including
334
336
  | `analyzeAppLaunch` | Parse `xctrace` `app-launch` schema; return cold/warm launch type + per-phase breakdown (process-creation, dyld-init, ObjC-init, AppDelegate, first-frame). |
335
337
  | `logShow` | One-shot query of macOS unified logging via `log show --style compact` with predicate / process / subsystem filters. Returns parsed entries (timestamp, type, process, subsystem, category, message). |
336
338
 
337
- ### Capture / record (3)
339
+ ### Capture / record (4)
338
340
 
339
341
  | Tool | What | Sim | Device |
340
342
  |---|---|---|---|
341
- | `recordTimeProfile` | Wrap `xcrun xctrace record --template "Time Profiler" --attach ... --time-limit Ns --output ...`. | ✅ | ✅ |
343
+ | `recordTimeProfile` | Wrap `xcrun xctrace record --template "Time Profiler" --attach ... --time-limit Ns --output ...`. Returns `bundleStatus: "unknown" \| "salvageable" \| "wedged"` (v1.17) so callers can branch on on-disk reality after a timeout instead of trusting the `tracePath` blindly. Auto-open path (`MEMORYDETECTIVE_AUTO_OPEN_INSTRUMENTS`) probes `MANIFEST.plist` before launching Instruments.app to skip wedged 52K stubs. | ✅ | ✅ |
344
+ | `recordViaInstrumentsApp` | macOS 26.x escape hatch (v1.16). Opens Instruments.app via `open -a Instruments`, returns an `instructions[]` array telling the user which template to pick + when to hit Record / Stop / Save, then polls `watchDir` every 5s for new `.trace` bundles (mtime-stable for 10s). v1.17: also queries running Instruments.app via AppleScript every poll for any saved document outside `watchDir`. On match, returns the path with `savedOutsideWatchDir: true` so users who hit Save and accepted the Desktop default no longer time out. Chains into `inspectTrace` on success. | ✅ | ✅ |
342
345
  | `captureMemgraph` | Wrap `leaks --outputGraph <path> <pid>`. Resolves `appName → pid` via `pgrep -x`. Returns a structured `workaroundNotice` on the macOS 26.x `Failed to get DYLD info for task` regression with stable issue ids (`minimal-corpse`, `permission-denied`, `leaks-not-found`, `transient`) and a fallback path to `recordTimeProfile` (Allocations) + `analyzeAllocations`. | ✅ | ❌. Use Xcode |
343
346
  | `logStream` | Wrap `log stream --style compact` for a bounded duration (≤ 60 s). Returns parsed entries collected during the window. | n/a | n/a |
344
347
 
@@ -358,7 +361,7 @@ These three tools combine into a single deterministic verify-fix loop: launch th
358
361
  |---|---|
359
362
  | `listTraceDevices` | Parse `xcrun xctrace list devices` (devices + simulators + UDIDs). |
360
363
  | `listTraceTemplates` | Parse `xcrun xctrace list templates` (standard + custom). |
361
- | `inspectTrace` | Orientation tool for `.trace` bundles. Returns schemas present + row counts + device/OS/template metadata + `suggestedNextCalls[]` mapping each populated known schema to its analyzer. Use this as the FIRST call on any `.trace`. New in v1.11. |
364
+ | `inspectTrace` | Orientation tool for `.trace` bundles. Returns schemas present + row counts + device/OS/template metadata + `suggestedNextCalls[]` mapping each populated known schema to its analyzer. Use this as the FIRST call on any `.trace`. New in v1.11. v1.17: fault-tolerant — returns `ok: true` with `schemas: []` and a diagnosis string when `xctrace export --toc` fails on wedged 52K bundles, instead of throwing. |
362
365
 
363
366
  ### Synthesize (1)
364
367
 
package/USAGE.md CHANGED
@@ -302,6 +302,7 @@ Once you have the diagnosis, here are useful follow-up prompts you can paste int
302
302
  | "Show the retain chain that keeps `DetailViewModel` alive." | `findRetainers(path, className: "DetailViewModel")` |
303
303
  | "Compare `~/Desktop/before.memgraph` to `~/Desktop/after.memgraph`. Did the leak go away?" | `diffMemgraphs(before, after)` |
304
304
  | "Did my fix actually resolve the `swiftui.tag-index-projection` cycle?" | `verifyFix(before, after, expectedPatternId: "swiftui.tag-index-projection")`. Returns PASS/PARTIAL/FAIL |
305
+ | "Whitelist `MySingletonCache` (exact match) so it does not vote FAIL on the verify." | `verifyFix(before, after, expectedAliveClasses: [{ pattern: "MySingletonCache", mode: "exact" }])`. v1.17 introduces per-entry modes (`"exact" \| "substring" \| "regex"`); bare strings still default to substring for backwards compat. |
305
306
  | "Render the cycle as a Mermaid graph for the PR description." | `renderCycleGraph(path, format: "mermaid")` |
306
307
  | "Profile this app on my iPhone for 90 seconds and tell me about hangs." | `listTraceDevices` → `recordTimeProfile` → `analyzeHangs` |
307
308
  | "Pull the last 5 minutes of `error`-level logs from `MyApp`." | `logShow(last: "5m", process: "MyApp", level: "default")` |
@@ -420,13 +421,13 @@ It hits every `xctrace`-based tool the same way (this MCP, XcodeTraceMCP, raw `x
420
421
 
421
422
  Recovery options, ranked by automation cost:
422
423
 
423
- 1. **`recordViaInstrumentsApp` MCP tool (v1.16+).** Opens Instruments.app for you, surfaces step-by-step instructions in the response, then watches a directory for the saved `.trace`. Once it appears, the tool returns the path plus a chained `inspectTrace` summary. The user-in-loop step is the recording itself (pick template, hit Record, hit Stop, hit Save). Why is it user-in-loop? Instruments.app's AppleScript surface is minimal: queries on the `document` class only, no verbs for start/stop/select-template (see `Xcode.app/Contents/Applications/Instruments.app/Contents/Resources/Instruments.sdef`). The watcher polls every 5 seconds and considers a bundle "saved" after its mtime is stable for 10 seconds.
424
+ 1. **`recordViaInstrumentsApp` MCP tool (v1.16+).** Opens Instruments.app for you, surfaces step-by-step instructions in the response, then watches a directory for the saved `.trace`. Once it appears, the tool returns the path plus a chained `inspectTrace` summary. The user-in-loop step is the recording itself (pick template, hit Record, hit Stop, hit Save). Why is it user-in-loop? Instruments.app's AppleScript surface is minimal: queries on the `document` class only, no verbs for start/stop/select-template (see `Xcode.app/Contents/Applications/Instruments.app/Contents/Resources/Instruments.sdef`). The watcher polls every 5 seconds and considers a bundle "saved" after its mtime is stable for 10 seconds. **v1.17 fixed the common "I hit Save and accepted the Desktop default" miss**: the watcher now also queries running Instruments.app via AppleScript each poll for any document whose file path was set after the tool started. On match, returns the path with `savedOutsideWatchDir: true` so you do not have to re-record into `watchDir`.
424
425
  2. **Record on an older macOS host with Xcode 26.0 if you have one available.** Pre-regression `xctrace record` produces clean traces. The 2026-05-15 validation used `~/Desktop/wishlist-tti-device.trace`, a Time Profiler trace captured this way: 91s recording, 35 hangs detected, 44 418 time-profile samples, fully analyzable by memorydetective's trace-side tools.
425
426
  3. **Record manually via Instruments.app GUI without the wrapper.** Instruments.app on macOS 26.x still produces valid `.trace` bundles. Open Instruments, pick a template, choose the simulator + app, hit Record, drive the scenario, Stop, Save. Then point `inspectTrace` / `summarizeTrace` / `analyzeHangs` / `analyzeTimeProfile` / `compareTracesByPattern` at the saved bundle. All trace-side analyzers work normally on Instruments-recorded `.trace` bundles.
426
427
  4. **Record against a physical device (USB or wireless).** The regression appears to be simulator-specific. `xctrace record --device <UUID> --launch <app>` against a real iPhone / iPad does NOT exhibit the same wedge. Use this when you have a physical target available.
427
428
  5. **Wait for Apple.** The Feedback assistant is the right escalation path. The regression has shipped through Xcode 26.4 + 26.5; expect a fix in 26.6 or later.
428
429
 
429
- memorydetective's `recordViaInstrumentsApp` (v1.16) + pre-flight probe (v1.14) cover the automated paths around the regression until Apple ships a fix.
430
+ memorydetective's `recordViaInstrumentsApp` (v1.16, hardened in v1.17) + pre-flight probe (v1.14) cover the automated paths around the regression until Apple ships a fix. v1.17 also added a `bundleStatus: "unknown" \| "salvageable" \| "wedged"` field on `recordTimeProfile` responses and a fault-tolerant fallback on `inspectTrace` so the entire MCP call no longer throws when xctrace refuses a wedged bundle.
430
431
 
431
432
  ### `replayScenario` returns `workaroundNotice: { issue: "axe-not-found" }`
432
433
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorydetective",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "mcpName": "io.github.carloshpdoc/memorydetective",
5
5
  "description": "MCP server for iOS leak hunting and performance investigation. 41 tools across .memgraph and .trace files. v1.17 reliability pass: strtobool env truthy parsing, verifyFix whitelist match modes (exact / substring / regex), Instruments.app AppleScript document query for traces saved outside watchDir, inspectTrace fault-tolerant fallback on wedged bundles, configurable countAlive noise filter. v1.16 added recordViaInstrumentsApp (macOS 26.x trace recording workaround). 36-pattern retain-cycle classifier with Swift fixTemplate snippets, abandoned-memory diff for KVO/NotificationCenter/cache shapes that escape leaks(1), per-test CI gate (detectLeaksInXCTest + detectLeaksInXCUITest with self-contained HTML reports), main-thread violation classifier on analyzeHangs, single-call build+boot+launch for the macOS 26.x leaks --outputGraph regression, replayScenario + captureScenarioState verify-fix loop, compareTracesByPattern CI gate on hangs/hitches/launch regressions, SourceKit-LSP source bridging. macOS only (depends on leaks(1) and xctrace).",
6
6
  "type": "module",