mobile-debug-mcp 0.35.0 → 0.36.0

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.
Files changed (112) hide show
  1. package/.cursor-plugin/marketplace.json +1 -1
  2. package/.github/workflows/ci.yml +1 -1
  3. package/README.md +86 -24
  4. package/agent-plugin/.antigravity-plugin/README.md +48 -0
  5. package/agent-plugin/.antigravity-plugin/plugin.json +18 -0
  6. package/agent-plugin/.claude-plugin/plugin.json +2 -2
  7. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  8. package/agent-plugin/.cursor-plugin/plugin.json +2 -2
  9. package/agent-plugin/.mcp.json +1 -1
  10. package/agent-plugin/README.md +37 -8
  11. package/agent-plugin/cursor/.cursor-plugin/plugin.json +23 -0
  12. package/agent-plugin/cursor/LICENSE +201 -0
  13. package/agent-plugin/cursor/README.md +9 -0
  14. package/agent-plugin/cursor/skills/mobile-debug-mcp/SKILL.md +253 -0
  15. package/agent-plugin/mcp.json +1 -1
  16. package/agent-plugin/mcp_config.json +13 -0
  17. package/agent-plugin/plugin.json +1 -1
  18. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +36 -16
  19. package/dist/debugger/guard.js +4 -4
  20. package/dist/debugger/ios-worker.js +62 -0
  21. package/dist/debugger/ios.js +57 -0
  22. package/dist/debugger/sessions.js +33 -16
  23. package/dist/debugger/worker.js +6 -4
  24. package/dist/interact/index.js +12 -2
  25. package/dist/manage/ios.js +2 -0
  26. package/dist/observe/ios-trace/artifact.js +63 -0
  27. package/dist/observe/ios-trace/capabilities.js +86 -0
  28. package/dist/observe/ios-trace/native.js +93 -0
  29. package/dist/observe/ios-trace/notification.js +35 -0
  30. package/dist/observe/ios-trace/process.js +98 -0
  31. package/dist/observe/ios-trace/sessions.js +244 -0
  32. package/dist/observe/readiness-service.js +1 -1
  33. package/dist/observe/readiness.js +4 -4
  34. package/dist/observe/system-trace-error.js +7 -0
  35. package/dist/observe/system-trace-router.js +81 -0
  36. package/dist/observe/system-trace.js +4 -8
  37. package/dist/server/handlers/debugger-handlers.js +27 -8
  38. package/dist/server/handlers/diagnostic.js +12 -4
  39. package/dist/server/handlers/tool-handlers.js +19 -9
  40. package/dist/server/schemas/debugger-schema.js +10 -7
  41. package/dist/server/schemas/system-trace-schema.js +14 -9
  42. package/dist/server-core.js +1 -1
  43. package/dist/server.js +4 -4
  44. package/dist/utils/ios/utils.js +3 -0
  45. package/docs/CHANGELOG.md +23 -1
  46. package/docs/agent-plugin-installation-verification.md +39 -0
  47. package/docs/code-structure.md +3 -1
  48. package/docs/specs/031-ios-system-traces/intent.md +38 -0
  49. package/docs/specs/031-ios-system-traces/investigation.md +92 -0
  50. package/docs/specs/031-ios-system-traces/late-run-failure-evidence.json +32 -0
  51. package/docs/specs/031-ios-system-traces/plan.md +138 -0
  52. package/docs/specs/031-ios-system-traces/pr102-monitor-evidence.json +22 -0
  53. package/docs/specs/031-ios-system-traces/simulator-evidence.json +47 -0
  54. package/docs/specs/031-ios-system-traces/spec.md +135 -0
  55. package/docs/specs/032-ios-runtime-debugger/evidence.json +176 -0
  56. package/docs/specs/032-ios-runtime-debugger/intent.md +38 -0
  57. package/docs/specs/032-ios-runtime-debugger/plan.md +95 -0
  58. package/docs/specs/032-ios-runtime-debugger/report.md +205 -0
  59. package/docs/specs/032-ios-runtime-debugger/spec.md +153 -0
  60. package/docs/tools/TOOLS.md +2 -2
  61. package/docs/tools/debugger.md +45 -5
  62. package/docs/tools/system-traces.md +81 -14
  63. package/package.json +2 -1
  64. package/scripts/agent-plugin.mjs +55 -5
  65. package/src/debugger/android.ts +1 -1
  66. package/src/debugger/guard.ts +5 -5
  67. package/src/debugger/ios-worker.ts +49 -0
  68. package/src/debugger/ios.ts +45 -0
  69. package/src/debugger/lldb/breakpoints.py +51 -0
  70. package/src/debugger/lldb/inspection.py +109 -0
  71. package/src/debugger/lldb/worker.py +203 -0
  72. package/src/debugger/sessions.ts +24 -12
  73. package/src/debugger/worker.ts +7 -4
  74. package/src/interact/index.ts +13 -2
  75. package/src/manage/ios.ts +2 -0
  76. package/src/observe/ios-trace/artifact.ts +55 -0
  77. package/src/observe/ios-trace/capabilities.ts +74 -0
  78. package/src/observe/ios-trace/native.ts +72 -0
  79. package/src/observe/ios-trace/notification.ts +30 -0
  80. package/src/observe/ios-trace/process.ts +82 -0
  81. package/src/observe/ios-trace/sessions.ts +204 -0
  82. package/src/observe/readiness-service.ts +1 -1
  83. package/src/observe/readiness.ts +4 -4
  84. package/src/observe/system-trace-error.ts +3 -0
  85. package/src/observe/system-trace-router.ts +61 -0
  86. package/src/observe/system-trace.ts +4 -4
  87. package/src/server/handlers/debugger-handlers.ts +28 -9
  88. package/src/server/handlers/diagnostic.ts +11 -4
  89. package/src/server/handlers/tool-handlers.ts +20 -9
  90. package/src/server/schemas/debugger-schema.ts +10 -7
  91. package/src/server/schemas/system-trace-schema.ts +15 -9
  92. package/src/server-core.ts +1 -1
  93. package/src/server.ts +3 -3
  94. package/src/utils/ios/utils.ts +3 -0
  95. package/test/device/automated/debugger/runtime.ios.test.ts +221 -0
  96. package/test/device/automated/observe/ios-system-trace.smoke.ts +119 -0
  97. package/test/fixtures/contracts/tool-definitions.json +192 -32
  98. package/test/fixtures/ios-debugger-app/Main.swift +95 -0
  99. package/test/fixtures/ios-debugger-app/README.md +27 -0
  100. package/test/fixtures/ios-debugger-app/build.ts +21 -0
  101. package/test/helpers/ios-trace-shutdown.ts +29 -0
  102. package/test/unit/debugger/handle-guards.test.ts +88 -0
  103. package/test/unit/debugger/ios-preparation.test.ts +57 -0
  104. package/test/unit/debugger/ios.test.ts +104 -0
  105. package/test/unit/debugger/lldb-worker.py +166 -0
  106. package/test/unit/debugger/lldb-worker.test.ts +10 -0
  107. package/test/unit/observe/ios-trace-native.test.ts +90 -0
  108. package/test/unit/observe/ios-trace-sessions.test.ts +131 -0
  109. package/test/unit/observe/readiness.test.ts +1 -1
  110. package/test/unit/observe/system-trace-router.test.ts +49 -0
  111. package/test/unit/quality/agent-plugin-export.test.ts +38 -0
  112. package/test/unit/server/system-trace.test.ts +12 -3
@@ -1,17 +1,22 @@
1
1
  import { z } from 'zod';
2
- const target = { platform: z.enum(['android', 'ios']), deviceId: z.string().min(1).optional() };
3
- export const traceTargetSchema = z.object(target).strict();
4
- export const traceStartSchema = z.object({ ...target, durationMs: z.number().int().min(1000).max(120000).default(30000) }).strict();
2
+ const target = { platform: z.enum(['android', 'ios']), deviceId: z.string().min(1).optional(), appId: z.string().regex(/^[a-zA-Z0-9._-]+$/).optional(), template: z.enum(['System Trace', 'Time Profiler', 'Animation Hitches', 'Hangs']).optional() };
3
+ const validTarget = (value) => value.platform === 'ios' ? !!value.deviceId : value.appId === undefined && value.template === undefined;
4
+ export const traceTargetSchema = z.object(target).strict().refine(validTarget);
5
+ export const traceStartSchema = z.object({ ...target, durationMs: z.number().int().min(1000).max(120000).default(30000) }).strict().refine(validTarget).refine(value => value.platform !== 'ios' || !!value.appId);
5
6
  export const traceControlSchema = z.object({ traceId: z.string().uuid() }).strict();
6
- const properties = { platform: { type: 'string', enum: ['android', 'ios'] }, deviceId: { type: 'string', minLength: 1 } };
7
+ const properties = { platform: { type: 'string', enum: ['android', 'ios'] }, deviceId: { type: 'string', minLength: 1 }, appId: { type: 'string', minLength: 1, pattern: '^[a-zA-Z0-9._-]+$', description: 'iOS only: running development app bundle identifier; required for iOS start.' }, template: { type: 'string', enum: ['System Trace', 'Time Profiler', 'Animation Hitches', 'Hangs'], description: 'iOS only. Default Time Profiler; initial simulator support is Time Profiler only.' } };
8
+ const platformRules = (start) => ({ oneOf: [
9
+ { properties: { platform: { const: 'android' } }, not: { anyOf: [{ required: ['appId'] }, { required: ['template'] }] } },
10
+ { properties: { platform: { const: 'ios' } }, required: start ? ['deviceId', 'appId'] : ['deviceId'] }
11
+ ] });
7
12
  export const systemTraceTools = [
8
- { name: 'get_system_trace_capabilities', description: 'Probe Android Perfetto prerequisites without recording. Device-wide system tracing is optional; iOS is unsupported.',
9
- inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties } },
10
- { name: 'start_system_trace', description: 'Start bounded Android Perfetto recording, returning an owned trace_id. Run an existing journey with systemTraceId, then stop to retrieve a local artifact or cancel to discard. Device-wide traces may contain sensitive system annotations; no upload.',
11
- inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties: { ...properties, durationMs: { type: 'integer', minimum: 1000, maximum: 120000, default: 30000 } } } },
13
+ { name: 'get_system_trace_capabilities', description: 'Probe Android Perfetto or iOS Instruments prerequisites without recording. iOS requires explicit booted simulator deviceId; reports template matrix and unknown profiling permissions.',
14
+ inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties, ...platformRules(false) } },
15
+ { name: 'start_system_trace', description: 'Start bounded Android Perfetto or iOS simulator Time Profiler recording, returning an owned trace_id after readiness. iOS requires explicit deviceId and running appId. Run an existing journey with systemTraceId, then stop to retrieve a local artifact or cancel to discard. Traces can contain sensitive system metadata, logs and paths; local user-managed retention, no upload.',
16
+ inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties: { ...properties, durationMs: { type: 'integer', minimum: 1000, maximum: 120000, default: 30000 } }, ...platformRules(true) } },
12
17
  ...['get_system_trace_status', 'stop_system_trace', 'cancel_system_trace'].map(name => ({ name,
13
18
  description: name === 'get_system_trace_status' ? 'Reconcile an owned trace and retrieve its state; finalise duration-completed capture.' :
14
- name === 'stop_system_trace' ? 'Stop owned capture gracefully and retrieve a local Perfetto artifact. Retry after a transfer/cleanup failure.' : 'Stop owned capture and discard partial files. Already completed local artifacts are retained.',
19
+ name === 'stop_system_trace' ? 'Stop owned capture gracefully and retrieve a local Perfetto file or validated Instruments .trace bundle. Retry after a transfer/cleanup failure.' : 'Stop owned capture and discard partial files. Already completed local artifacts are retained.',
15
20
  inputSchema: { type: 'object', additionalProperties: false, required: ['traceId'], properties: { traceId: { type: 'string', format: 'uuid' } } }
16
21
  }))
17
22
  ];
@@ -6,7 +6,7 @@ import { handleToolCall } from './server/handlers/tool-handlers.js';
6
6
  export { wrapResponse, toolDefinitions, handleToolCall };
7
7
  export const serverInfo = {
8
8
  name: 'mobile-debug-mcp',
9
- version: '0.35.0'
9
+ version: '0.36.0'
10
10
  };
11
11
  export function createServer() {
12
12
  const server = new Server(serverInfo, {
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { debuggerSessions } from './debugger/sessions.js';
2
+ import { debuggerSessions, iosDebuggerSessions } from './debugger/sessions.js';
3
3
  import { systemTraces } from './observe/system-trace.js';
4
4
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
5
  import { createServer } from './server-core.js';
@@ -19,10 +19,10 @@ async function shutdown() {
19
19
  const deadline = setTimeout(() => { console.error('System trace shutdown cleanup timed out; owned device captures remain duration-bounded.'); process.exit(1); }, 10000);
20
20
  let exitCode = 0;
21
21
  try {
22
- const [remaining, debuggers] = await Promise.all([systemTraces.shutdown(), debuggerSessions.shutdown()]);
23
- if (debuggers.length) {
22
+ const [remaining, debuggers, iosDebuggers] = await Promise.all([systemTraces.shutdown(), debuggerSessions.shutdown(), iosDebuggerSessions.shutdown()]);
23
+ if ((debuggers.length + iosDebuggers.length)) {
24
24
  exitCode = 1;
25
- console.error('Debugger shutdown cleanup incomplete', debuggers.map(s => s.cleanup));
25
+ console.error('Debugger shutdown cleanup incomplete', [...debuggers, ...iosDebuggers].map(s => s.cleanup));
26
26
  }
27
27
  if (remaining.length) {
28
28
  exitCode = 1;
@@ -1,3 +1,4 @@
1
+ import { checkDebuggerGuard } from '../../debugger/guard.js';
1
2
  import { remainingBudget } from '../operation-budget.js';
2
3
  import { execFile, spawn, execSync, spawnSync } from "child_process";
3
4
  import { promises as fsPromises } from 'fs';
@@ -94,6 +95,7 @@ export function execCommand(args, deviceId = "booted") {
94
95
  return new Promise((resolve, reject) => {
95
96
  // Use spawn for better stream control and consistency with Android implementation
96
97
  const budgetMs = remainingBudget();
98
+ checkDebuggerGuard();
97
99
  const child = spawn(getXcrunCmd(), args);
98
100
  let stdout = '';
99
101
  let stderr = '';
@@ -135,6 +137,7 @@ export function execCommandWithDiagnostics(args, deviceId = "booted") {
135
137
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000;
136
138
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000;
137
139
  const timeoutMs = budgetMs || (args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT);
140
+ checkDebuggerGuard();
138
141
  const res = spawnSync(getXcrunCmd(), args, { encoding: 'utf8', timeout: timeoutMs });
139
142
  const runResult = {
140
143
  exitCode: typeof res.status === 'number' ? res.status : null,
package/docs/CHANGELOG.md CHANGED
@@ -8,7 +8,29 @@ and [tool reference](tools/TOOLS.md) for the current checkout.
8
8
 
9
9
  All notable changes to the **Mobile Debug MCP** project will be documented in this file.
10
10
 
11
- ## Unreleased
11
+ ## [0.36.0]
12
+
13
+ - Updated the agent plugin to `0.0.6`, pinned to MCP `0.36.0`.
14
+
15
+ - Add an optional iOS simulator LLDB backend to the existing debugger tools,
16
+ including exact source-file breakpoints, bounded static inspection, stepping,
17
+ platform-scoped pause guards and owned cleanup. A separate Swift concurrency
18
+ fixture checks inspection around await, cancellation and recovery; verification
19
+ coverage is recorded in specification 032. Physical iOS devices remain unsupported.
20
+
21
+ - Preserve iOS trace capture through isolated monitoring command failures; require
22
+ three consecutive non-definitive failures before discarding, while identity and
23
+ storage failures remain immediate and capture expiry still reconciles.
24
+
25
+ - Add optional iOS simulator Time Profiler capture through Instruments, with explicit
26
+ device/app targeting, recording-start notification, local `.trace` validation,
27
+ journey evidence linkage and owned cleanup. Physical devices and other templates
28
+ remain unsupported in the initial matrix. Android tracing retains its preset.
29
+
30
+
31
+ - Added plugin `0.0.5` Antigravity compatibility and a standalone distribution
32
+ export. Generated a Cursor-native package for its supported path variables,
33
+ and checked/refined installation instructions for all four clients.
12
34
 
13
35
  ## [0.35.0]
14
36
 
@@ -0,0 +1,39 @@
1
+ # Agent plugin installation verification
2
+
3
+ Checked 2026-11-02 for plugin 0.0.5 and published MCP 0.35.0. This record separates
4
+ command/schema checks from installation and live device evidence.
5
+
6
+ | Client | Evidence | Limit |
7
+ | --- | --- | --- |
8
+ | Codex CLI 0.166.0-alpha.5.1 | Marketplace-add and plugin-add syntax confirmed with installed CLI help and official docs; catalog paths checked by repository verification | No new Codex installation or agent journey performed |
9
+ | Claude Code 2.1.285 | Native strict validation passes for the marketplace, compatibility manifest and complete plugin folder; shell command syntax confirmed | No new Claude Code installation or agent journey performed |
10
+ | Cursor | Native package and catalog generated from the canonical skill; uses documented CURSOR_PLUGIN_ROOT; local-copy instructions preserve hidden files | Host loading in Cursor is unrun |
11
+ | Antigravity CLI 1.4.12 | Standalone export passes strict validation with zero warnings; local install discovers one skill and one MCP server | An agent print-mode attempt was denied by plan-mode MCP policy; a narrowly permitted read-only attempt timed out, so no agent tool-call success is claimed |
12
+
13
+ The Antigravity export contains native `plugin.json`, `mcp_config.json`, README,
14
+ license, logo and the shared skill. It excludes foreign client manifests and
15
+ Finder metadata. Export destinations must be new directories. Regression coverage
16
+ checks inventory, canonical skill/license bytes and refusal to overwrite existing
17
+ files. Cursor's generated skill/license are checked against their canonical
18
+ sources by `npm run check:agent-plugin`.
19
+
20
+ A separate SDK test launched the exact exported npx command from an expanded
21
+ PLUGIN_DATA working directory. It connected to published MCP 0.35.0, discovered
22
+ 52 tools, selected emulator-5554 (Android 16/API 36), launched Settings and asserted
23
+ a fresh UI tree contained a visible Settings-owned resource. **PASS**; this is an
24
+ exported-config/device check, not an Antigravity agent interaction. Log:
25
+ `/tmp/antigravity-published-mcp-check.log`. The emulator remains running.
26
+
27
+ `npm run verify` **PASS**, all 91 unit files, including the exporter regression,
28
+ lint/quality/architecture, build and plugin consistency. Native Antigravity strict
29
+ validation and Claude strict validation also **PASS**. Markdown links and diff
30
+ whitespace were checked. No production server code or dependencies changed.
31
+ GitHub/local distribution does not require the optional Marketplace intake form;
32
+ no application was submitted.
33
+
34
+ ## Primary documentation
35
+
36
+ - [OpenAI plugin packaging](https://developers.openai.com/plugins/build/plugins)
37
+ - [Claude Code installation](https://code.claude.com/docs/en/discover-plugins)
38
+ - [Cursor plugins and local testing](https://cursor.com/docs/plugins)
39
+ - [Antigravity plugin building and validation](https://antigravity.google/docs/build-plugins)
@@ -111,7 +111,9 @@ requires reviewing its exception rather than silently transferring it.
111
111
  it is not packet capture or a proxy. Results depend on the app logging requests,
112
112
  and the current implementation does not measure request duration.
113
113
  - `src/debugger/protocol.ts` validates worker messages. The session manager owns
114
- serialized transitions and cleanup; the Java worker owns JDI operations.
114
+ serialized transitions and cleanup; the Java worker owns Android JDI operations.
115
+ The optional `lldb/` Python worker owns iOS simulator LLDB operations and static
116
+ inspection; `ios.ts` verifies simulator/app/process membership before attach.
115
117
 
116
118
  The [refactoring plan](specs/026-code-structure-and-quality-gates/plan.md) records
117
119
  progress and verification. The [follow-on health cleanup](specs/026-code-structure-and-quality-gates/health-plan.md)
@@ -0,0 +1,38 @@
1
+ # iOS system traces — intent
2
+
3
+ - Source: [GitHub issue #78](https://github.com/clivejefferies/mobile-debug-tools/issues/78), authored by clivejefferies.
4
+ - Captured: 2026-11-04.
5
+ - Status: intent approved by the user's “ok” on 2026-11-04, responding to the
6
+ complete scope and approval request in this chat. Specification approval pending.
7
+
8
+ ## Problem and outcome
9
+
10
+ MCP clients need optional iOS performance evidence around an existing journey
11
+ to investigate slow interactions, hangs and rendering hitches. Add bounded
12
+ Apple Instruments capture through `xcrun xctrace` on macOS with Xcode, returning
13
+ a readable local `.trace` artifact with explicit iOS target/app metadata and
14
+ journey/evidence correlation identifiers.
15
+
16
+ ## Scope and constraints
17
+
18
+ Investigate System Trace, Time Profiler, Hangs and Animation Hitches availability
19
+ and distinct coverage, then propose an initial supported target/template matrix.
20
+ Assess simulators and physical devices independently. Require explicit target
21
+ UDIDs, recording readiness before journey input, artifact validation, bounded
22
+ resource use, and cleanup of session-owned processes. Keep trace contents local,
23
+ explain sensitive-data and retention implications, and report setup/permission
24
+ failures without bypassing them. Reuse Android tracing conventions where suitable
25
+ and preserve existing Android and untraced-journey behaviour.
26
+
27
+ Success requires at least one development fixture completing start → asserted
28
+ journey → stop with a readable artifact and matching correlation metadata.
29
+ Report unsupported, blocked and unrun coverage separately.
30
+
31
+ Automatic diagnosis, performance summaries, arbitrary queries, general export
32
+ APIs and runtime debugging are excluded.
33
+
34
+ ## Questions for investigation
35
+
36
+ Which target/template combinations are usable, how is recording readiness
37
+ established, and which fixture and export tables can prove useful capture?
38
+ These choices require specification approval after intent acceptance.
@@ -0,0 +1,92 @@
1
+ # iOS tracing investigation
2
+
3
+ 2026-11-04. Native CLI feasibility only; no new MCP behaviour implemented.
4
+ Xcode 27.2 (27B52), Instruments 27.2; iPhone 17 Pro simulator, iOS 26.4.1,
5
+ UDID `F9243BAF-62A8-4990-8A99-17F527F56FDA`.
6
+
7
+ `xcrun xctrace help record`, `help export`, `list templates`, `list devices`,
8
+ and `record --template 'Time Profiler' --device <UDID>
9
+ --show-recording-options` were inspected. Listing required execution outside
10
+ the workspace sandbox because Instruments writes its normal cache. No physical
11
+ iOS device was listed. No permission bypass or `--no-prompt` was used.
12
+
13
+ The existing `test/fixtures/readiness-app` was built, installed and launched on
14
+ the explicitly selected simulator. It was previously shut down. Three-second
15
+ recordings used `record --template <name> --device <UDID> --attach Readiness
16
+ --time-limit 3s --output <unique local path>`. Initial PID attachment failed
17
+ with “Cannot find process”; later name attachment resolved fixture PID 22313.
18
+ This argues for exact app identity resolution and fail-closed readiness, not
19
+ blind PID/name retries. No journey or UI assertion ran during these probes.
20
+
21
+ | Template | Result | Artifact validation |
22
+ | --- | --- | --- |
23
+ | Time Profiler | exit 0 | TOC confirms simulator UDID, fixture PID and template; `time-sample` export contains 5 rows |
24
+ | System Trace | exit 0 | TOC confirms target/template, but `context-switch` export has 0 rows; scheduling coverage unproven |
25
+ | Animation Hitches | exit 2 | explicit “Hitches is not supported on this platform”; saved failed artifact is not success |
26
+ | Hangs | no standalone template | Time Profiler recording options and TOC include Hangs; hang detection itself unverified |
27
+
28
+ Successful recordings warned that one table lacked a known input source.
29
+ Preserve such warnings; readable output does not establish every instrument works.
30
+ `--notify-tracing-started` is documented by the installed CLI; notification-based
31
+ readiness and graceful interruption remain unverified implementation checks.
32
+
33
+ Artifacts are local temporary investigation data under `/private/tmp/`:
34
+ `mcp-ios-time-profiler-name-01a1a646.trace`,
35
+ `mcp-ios-system-trace-01a1a646.trace`, `mcp-ios-animation-01a1a646.trace`.
36
+ Exports: `mcp-ios-time-samples-01a1a646.xml`,
37
+ `mcp-ios-system-toc-01a1a646.xml`, `mcp-ios-context-switch-01a1a646.xml`.
38
+ These were temporary investigation artifacts, subsequently removed during task cleanup; they are not tracked repository artifacts.
39
+ Fixture uninstall and simulator shutdown both passed; no recording remained active.
40
+
41
+ Owning code inspected: `src/observe/system-trace.ts`,
42
+ `src/server/handlers/diagnostic.ts`, `src/server/handlers/journey.ts`,
43
+ `src/server/schemas/system-trace-schema.ts`, and `src/utils/resolve-device.ts`.
44
+ Current session metadata and journey validation hardcode Android; existing APIs
45
+ must be extended consistently rather than routing iOS into the Perfetto adapter.
46
+
47
+ ## Updated-server verification and input tooling
48
+
49
+ The default Homebrew `idb_companion` 1.1.8 (built 2022-08-12) can read this
50
+ simulator hierarchy but its HID input fails because it loads SimulatorKit from
51
+ `Contents/Developer/Library/PrivateFrameworks/SimulatorKit.framework`. Xcode 27.2
52
+ ships it under `Contents/SharedFrameworks/SimulatorKit.framework` instead.
53
+ A direct tap on the offline fixture reproduced that failure without tracing.
54
+ The early full smoke runs therefore failed; their assertions were not weakened.
55
+
56
+ For the successful input verification, a **test-owned** companion used a private
57
+ layout alias under `/private/tmp/mcp-idb-xcode-o6ks8j9h/Contents/Developer`:
58
+ other Xcode contents were symlinks to the installed Xcode, with an additional
59
+ `Developer/Library/PrivateFrameworks/SimulatorKit.framework` link to the actual
60
+ SharedFrameworks location. `DEVELOPER_DIR` was set only for that companion.
61
+ The installed Xcode and existing companion were not modified or replaced.
62
+
63
+ The isolated companion command was:
64
+
65
+ ```sh
66
+ DEVELOPER_DIR=<private-alias>/Contents/Developer idb_companion \
67
+ --udid F9243BAF-62A8-4990-8A99-17F527F56FDA \
68
+ --grpc-domain-sock /private/tmp/mcp-ios-trace-idb.sock --log-level info
69
+ ```
70
+
71
+ The smoke passes `IDB_COMPANION=/private/tmp/mcp-ios-trace-idb.sock` explicitly
72
+ into the built server, using the installed idb client's supported companion
73
+ option. Instruments uses the real selected Xcode. This is verification-specific
74
+ tooling setup, not a new shipped input backend or proof that default idb HID works
75
+ with Xcode 27.2. Environments need a compatible idb companion for input journeys.
76
+ Capture-only use does not require idb input. See [plan](plan.md) and the final
77
+ [simulator evidence](simulator-evidence.json) for exactly what passed and cleanup.
78
+
79
+ A later long smoke run passed tap → Done (66 exported sample rows), cancellation,
80
+ expiry and failed-journey handling, then returned `TRACE_UNSUPPORTED` starting an
81
+ independent shutdown probe. Its exact prerequisite cause was not established;
82
+ a subsequent standalone capability read found all capture prerequisites available.
83
+ The original smoke then incorrectly reported expired completed metadata as failed
84
+ cleanup after ten minutes. [That failed report](late-run-failure-evidence.json)
85
+ is retained. The harness now tracks only sessions that still require cleanup and
86
+ queries explicit capabilities before the separate shutdown probe. The prerequisite
87
+ failure is not attributed to CPU load or a particular permission without evidence.
88
+
89
+ Final cleanup verified the simulator returned to Shutdown and no Readiness.app
90
+ fixture remained. The test-owned companion, layout alias, socket and private log
91
+ were removed. The final standalone combined smoke passed; its exact build and
92
+ outcomes are recorded in plan.md and simulator-evidence.json.
@@ -0,0 +1,32 @@
1
+ {
2
+ "linked_baseline": "PASS; Ready asserted via built-server journey",
3
+ "journey": {
4
+ "status": "PASS",
5
+ "trace_id": "ec906d78-08bf-495b-a824-2cdaf84536ba",
6
+ "correlation_id": "78d5c21f-435e-4b9c-822a-1cdfb23a1d63",
7
+ "ready_at_ms": 1793794165955,
8
+ "journey_submitted_at_ms": 1793794170369,
9
+ "outcome": "Done visible via journey UI-tree assertion",
10
+ "sample_rows": 66,
11
+ "artifact_bytes": 12304469,
12
+ "warnings": [
13
+ "INSTRUMENTS_RUN_WARNING: one or more Instruments warnings; coverage may be incomplete."
14
+ ]
15
+ },
16
+ "stale_id": "PASS; rejected tap left prior fixture label visible",
17
+ "cancel": "PASS",
18
+ "expiry": "PASS",
19
+ "failed_journey": "PASS; failure retained capture for explicit stop",
20
+ "status": "FAIL",
21
+ "error": "AssertionError [ERR_ASSERTION]: {\"error\":{\"tool\":\"start_system_trace\",\"code\":\"TRACE_UNSUPPORTED\",\"message\":\"TRACE_UNSUPPORTED\"}}\n+ actual - expected\n\n+ undefined\n- 'recording'\n",
22
+ "cleanup": {
23
+ "trace_errors": [
24
+ "ec906d78-08bf-495b-a824-2cdaf84536ba",
25
+ "7f89df16-af5c-4c1f-a21d-bb1f5b702dc5",
26
+ "379e63fc-d780-4a80-96d3-1eca3cda1c28"
27
+ ],
28
+ "fixture_removed": true,
29
+ "simulator_shutdown": false,
30
+ "host_artifacts_removed": true
31
+ }
32
+ }
@@ -0,0 +1,138 @@
1
+ # iOS system traces implementation plan
2
+
3
+ [Intent](intent.md) and [specification](spec.md) approved in this chat on
4
+ 2026-11-04. Implementation is local on `feature/ios-system-traces`; no publication
5
+ or push requested. Preserve Android contracts and unrelated work.
6
+
7
+ ## Implementation
8
+
9
+ - [x] Add bounded owned subprocess and Darwin-notification helper support.
10
+ - [x] Add simulator/app capability discovery and validated Instruments artifacts.
11
+ - [x] Add iOS sessions and shared platform routing, limits and journey linkage.
12
+ - [x] Extend schemas, public definitions, fixtures and documentation/usage skill.
13
+ - [x] Add deterministic lifecycle/native/contract tests and explicit simulator smoke.
14
+ - [x] Review resource ownership, failure/retry paths, schemas and Android callers.
15
+
16
+ ## Verification
17
+
18
+ Node >=22 (use installed Node 22 where needed), Xcode 27.2 and iOS 26.4.1
19
+ simulator `F9243BAF-62A8-4990-8A99-17F527F56FDA`. Boot only that simulator,
20
+ install the existing offline readiness fixture, launch, start Time Profiler,
21
+ run a journey tapping Show loader and asserting Done, stop and validate target,
22
+ IDs, readiness ordering, TOC and sample rows. Also exercise expiry, cancel and
23
+ failed journey. Remove fixture and restore simulator shutdown afterward.
24
+
25
+ | Criteria | Checks |
26
+ | --- | --- |
27
+ | 1 capability/input safety | injected native probes, handler/schema and definition goldens |
28
+ | 2 validated linked capture | updated-server simulator smoke with outcome assertion and artifact exports |
29
+ | 3 lifecycle and ownership | deterministic native/session failure, concurrency, stale/mismatch, retry tests |
30
+ | 4 real lifecycle evidence | simulator explicit stop, expiry, cancellation and journey failure |
31
+ | 5 compatibility | Android session tests, existing device suite, full maintainer verification |
32
+
33
+ Commands: focused `node --import tsx test/unit/...` checks, `npm run verify`,
34
+ `npm run test:device`, opt-in iOS trace smoke with an explicit simulator UDID.
35
+ CI workflows will be checked for coverage differences. Physical-device capture
36
+ is outside the initial matrix and unrun; none is connected. Notification helper
37
+ compilation/readiness, PID identity resolution and finalisation deadlines require
38
+ early native checks. No test result is implied by the prior feasibility probes.
39
+
40
+ ## Results
41
+
42
+ Implementation and focused review complete locally. No commit, push or release.
43
+
44
+ | Check | Result and scope |
45
+ | --- | --- |
46
+ | Focused native/session/router/tool contracts | PASS: identity/executable matching, notification-before-readiness, stale and mismatched links, shared pending reservations, expiry, cancellation, export retry without restart, storage and subprocess deadlines |
47
+ | `npm run verify` | PASS on Node 23.11.0: lint, structural gates, architecture, TypeScript, 94 unit files, agent-plugin consistency. Final post-fix run also passed, including the corrected smoke cleanup harness. |
48
+ | `npm run test:device` | PASS on the local emulator/simulator: Android Perfetto capture/readability, explicit stop/cancel/expiry/shutdown plus Android+iOS observation/evidence smoke. 13 files visited; opt-in debugger/coroutine/Kotlin/interaction and iOS trace files report UNRUN without their explicit environment. |
49
+ | Focused built-server iOS trace smoke | Full fixture tap → Done UI-tree assertion and sample export passed using the isolated input companion documented in investigation.md. Final standalone combined lifecycle/shutdown run PASS; see simulator-evidence.json. |
50
+ | Real iOS shutdown recheck | PASS after allowing recorder finalisation up to eight seconds inside the ten-second server budget. Earlier one-second forced-stop run failed cleanup; that failure was investigated and fixed. |
51
+ | Physical device / real native failure injection | UNRUN. Physical capture is outside the accepted initial matrix; disconnect/permission/start/export/storage failure branches have deterministic coverage, not real-device proof. |
52
+
53
+ Logs: `/private/tmp/mobile-debug-ios-trace-verify-final.log` and
54
+ `/private/tmp/mobile-debug-ios-trace-device.log`. CI uses Ubuntu/Node 22/JDK 17;
55
+ local validation used macOS/Node 23.11.0 and installed toolchains. Hosted CI is
56
+ unrun. No hosted-CI or installed/published-package compatibility claim is made.
57
+
58
+ Review: schemas, registration goldens, dispatch, response serialization and callers
59
+ were checked together. Android adapter/session logic remains intact behind routing.
60
+ Recorder handles are owned, process identity is revalidated, artifacts validate
61
+ UUID/PID/executable/template/table data, and temporary exports/helpers are removed.
62
+ Forced-stop deadlines, incomplete shutdown reporting and oversized-output disposal
63
+ were reviewed. No remaining actionable code review finding is recorded.
64
+
65
+ Local limitations: default idb 1.1.8 HID is incompatible with the Xcode 27.2
66
+ framework layout. Successful input evidence uses a test-owned companion with a
67
+ private path alias; capture itself uses the actual selected Xcode. Instruments
68
+ reported a bounded run warning; successful CPU sample export does not verify all
69
+ bundled instruments, scheduling data, symbolication or hang detection.
70
+
71
+
72
+ ## Final device result and cleanup
73
+
74
+ The final standalone built-server smoke passed with build SHA-256
75
+ `48eee7b1f71403645ab301def8d83dd14585eec099b221f8ac4f6aaba0941282`.
76
+ The UI-tree assertion established Done after tapping Show loader, with readiness
77
+ before journey submission, matching correlation identifiers and 70 exported CPU
78
+ sample rows in an 11,961,327-byte readable Instruments bundle. Explicit stop,
79
+ stale rejection, cancel, duration expiry, failed-journey retention, real server
80
+ shutdown and target-app survival passed. The generated report is
81
+ [simulator-evidence.json](simulator-evidence.json).
82
+
83
+ The earlier long-run failure is retained as
84
+ [late-run-failure-evidence.json](late-run-failure-evidence.json); it is not called
85
+ a pass. The final run used corrected active-session cleanup tracking and explicit
86
+ shutdown capability checks, after maintainer verification had finished.
87
+
88
+ Final `npm run verify`: PASS, 94 unit files, on Node 23.11.0. The native forced-stop
89
+ nonclosure case has a bounded deterministic test. The existing device suite's
90
+ passed checks and opt-in UNRUN entries are scoped above. Hosted CI remains unrun.
91
+
92
+ All task-owned fixture installations and trace/probe artifacts were removed. The
93
+ test-owned companion was stopped only after verifying its exact command/PID;
94
+ its path alias, socket and private diagnostic log were removed. The simulator's
95
+ original Shutdown state and fixture absence were verified with simctl and the
96
+ filesystem. The original companion and installed Xcode were unchanged. No active
97
+ task recording remains. Changes are uncommitted and unpushed.
98
+
99
+ ## PR #102 review repair — 2026-11-04
100
+
101
+ The three review findings were checked against the current branch. Clarified
102
+ artifact retention wording in the canonical and generated Cursor skills, and
103
+ separated Android/iOS capability and session fields in the tool guide.
104
+
105
+ Monitoring now tolerates two consecutive non-definitive failures, resets the
106
+ counter on success, and preserves COMMAND_FAILED/ACTION_TIMEOUT classifications.
107
+ Unknown failures use bounded TRACE_MONITOR_FAILED rather than claiming a target
108
+ disconnect. Storage, changed identity and missing-app failures remain immediate.
109
+ Tolerated failures still reconcile expiry and recorder exit; retries retain the
110
+ same device/app/recording and never restart capture.
111
+
112
+ Deterministic tests cover reset, third-failure cleanup, immediate failures,
113
+ reason privacy, pinned recording identity and deadline/recorder fall-through.
114
+ Fresh maintainer/device results and focused simulator evidence are recorded
115
+ below after validation; previous feature evidence remains scoped to its build.
116
+
117
+ PR repair validation:
118
+
119
+ - `npm run verify`: PASS on Node 23.11.0, including 94 unit files and plugin sync.
120
+ - `npm run test:device`: Android+iOS observation/evidence checks passed; the
121
+ Android system-trace shutdown assertion failed in the full run. Its uniquely
122
+ owned leftover trace was removed. The unchanged Android trace smoke passed
123
+ independently afterward, including active shutdown. The initial suite failure
124
+ remains recorded; its exact cause was not established.
125
+ - Focused iPhone 17 Pro / iOS 26.4 simulator check: PASS using built iOS adapters,
126
+ real Time Profiler recording, and explicitly injected monitor failures. Two
127
+ COMMAND_FAILED polls preserved capture and yielded seven exported sample rows;
128
+ three ACTION_TIMEOUT polls failed with that reason and complete cleanup.
129
+ See [pr102-monitor-evidence.json](pr102-monitor-evidence.json) for its build hash.
130
+ This checks monitoring and native capture, not a new UI-input journey or real
131
+ device disconnection. Deterministic tests establish expiry/reset/identity cases.
132
+ - Fixture and test host artifacts removed; simulator returned to Shutdown.
133
+ Physical devices and hosted CI are not established by these local checks.
134
+
135
+ Logs: `/private/tmp/mobile-debug-pr102-verify.log`,
136
+ `/private/tmp/mobile-debug-pr102-device.log`,
137
+ `/private/tmp/mobile-debug-pr102-android-rerun.log` and
138
+ `/private/tmp/mobile-debug-pr102-monitor.log`.
@@ -0,0 +1,22 @@
1
+ {
2
+ "source": "built dist iOS sessions/native adapter; injected monitor failures, real Instruments capture",
3
+ "device": "F9243BAF-62A8-4990-8A99-17F527F56FDA",
4
+ "runtime": "v23.11.0",
5
+ "transient_recovery": {
6
+ "status": "PASS",
7
+ "injected_command_failures": 2,
8
+ "trace_id": "a979b090-3ebb-4a95-92d0-9060c8105d45",
9
+ "pid": 90191,
10
+ "sample_rows": 7,
11
+ "artifact_bytes": 10898587
12
+ },
13
+ "repeated_timeout": {
14
+ "status": "PASS",
15
+ "reason": "ACTION_TIMEOUT",
16
+ "cleanup": "complete"
17
+ },
18
+ "status": "PASS",
19
+ "build_sha256": "e735ac25c9b0065797b748934efa5f3fc977a538fffef50e464b31ca4603c64a",
20
+ "cleanup": "Owned captures stopped; fixture and host artifacts removed; simulator shutdown restored separately",
21
+ "simulator_restored_to_shutdown": true
22
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "linked_baseline": "PASS; Ready asserted via built-server journey",
3
+ "journey": {
4
+ "status": "PASS",
5
+ "trace_id": "742664b0-c676-4fe8-9231-26cca3c41009",
6
+ "correlation_id": "5affcc93-6944-4b58-a4a9-0d6cd0a55954",
7
+ "ready_at_ms": 1793796042115,
8
+ "journey_submitted_at_ms": 1793796046237,
9
+ "outcome": "Done visible via journey UI-tree assertion",
10
+ "sample_rows": 70,
11
+ "artifact_bytes": 11961327,
12
+ "warnings": [
13
+ "INSTRUMENTS_RUN_WARNING: one or more Instruments warnings; coverage may be incomplete."
14
+ ]
15
+ },
16
+ "stale_id": "PASS; rejected tap left prior fixture label visible",
17
+ "cancel": "PASS",
18
+ "expiry": "PASS",
19
+ "failed_journey": "PASS; failure retained capture for explicit stop",
20
+ "shutdown": "PASS; owned recording and partial directory removed",
21
+ "app_survived_cleanup": "PASS",
22
+ "status": "PASS",
23
+ "build_sha256": "48eee7b1f71403645ab301def8d83dd14585eec099b221f8ac4f6aaba0941282",
24
+ "runtime": "v23.11.0",
25
+ "device": "F9243BAF-62A8-4990-8A99-17F527F56FDA",
26
+ "server": {
27
+ "name": "mobile-debug-mcp",
28
+ "version": "0.35.0"
29
+ },
30
+ "physical_device": "UNRUN; unsupported initial matrix",
31
+ "native_failure_injection": "UNRUN on real target; deterministic unit tests separate",
32
+ "input_toolchain": "Explicit test companion via IDB_COMPANION; see investigation.md",
33
+ "cleanup": {
34
+ "trace_errors": [],
35
+ "fixture_removed": true,
36
+ "simulator_shutdown": false,
37
+ "host_artifacts_removed": true
38
+ },
39
+ "task_cleanup": {
40
+ "original_simulator_state": "Shutdown",
41
+ "restored_simulator_state": "Shutdown",
42
+ "fixture_absent": true,
43
+ "temporary_companion_stopped": true,
44
+ "temporary_framework_alias_removed": true,
45
+ "named_probe_artifacts_removed": true
46
+ }
47
+ }