@aarwitz/tapp 0.17.0 → 0.17.2

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.
@@ -163,13 +163,28 @@ run_harness_test() {
163
163
  \"OCQA_PR_TARGET\": ${OCQA_PR_TARGET_JSON}"
164
164
  fi
165
165
 
166
+ # Visual evidence must begin at the first settled target-app frame, never while Xcode is
167
+ # installing/launching the test runner or SpringBoard is selecting the app. The harness writes
168
+ # the ready file after the target is foregrounded + stable, then briefly waits for the host's
169
+ # recording-start acknowledgement so the first autonomous action cannot race ahead of video.
170
+ local visual_ready_line=""
171
+ if [[ -n "${OCQA_VISUAL_READY_PATH:-}" ]]; then
172
+ visual_ready_line=",
173
+ \"OCQA_VISUAL_READY_PATH\": \"${OCQA_VISUAL_READY_PATH}\""
174
+ fi
175
+ local recording_started_line=""
176
+ if [[ -n "${OCQA_RECORDING_STARTED_PATH:-}" ]]; then
177
+ recording_started_line=",
178
+ \"OCQA_RECORDING_STARTED_PATH\": \"${OCQA_RECORDING_STARTED_PATH}\""
179
+ fi
180
+
166
181
  cat > /tmp/ocqa-run-config.json << CONF
167
182
  {
168
183
  "OCQA_BUNDLE_ID": "$bundle_id",
169
184
  "OCQA_MAX_ACTIONS": "$max_actions",
170
185
  "OCQA_TIMEOUT_SECONDS": "$timeout_secs",
171
186
  "OCQA_TEST_EMAIL": "${OCQA_TEST_EMAIL:-qa@example.com}",
172
- "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Tapp123!}"$interactive_line$overrides_line$launch_args_line$launch_env_line$login_steps_line$pr_target_line
187
+ "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Tapp123!}"$interactive_line$overrides_line$launch_args_line$launch_env_line$login_steps_line$pr_target_line$visual_ready_line$recording_started_line
173
188
  }
174
189
  CONF
175
190
 
@@ -290,23 +305,38 @@ OCQA_COMPLETE:{\"actions\":0,\"states\":0,\"issues\":1,\"screens\":\"\",\"outcom
290
305
  echo "WARNING: Target process exited during launch preflight; recorded a crash instead of waiting for the exploration timeout." >&2
291
306
  else
292
307
 
293
- # Start video recording in background
308
+ # Prepare the foreground/settled handshake before starting the harness. Recording begins
309
+ # only after that handshake; this excludes build/install/SpringBoard footage without using a
310
+ # brittle fixed trim duration. A caller such as VS Code may provide its own ready path to
311
+ # reveal a preview at the same authoritative boundary.
294
312
  cleanup_stale_recorders "$UDID"
295
- if xcrun simctl io "$UDID" recordVideo --codec=h264 "$CAPTURE_DIR/exploration.mov" & then
296
- RECORD_PID=$!
297
- fi
298
- sleep 0.5
299
- if ! kill -0 "$RECORD_PID" 2>/dev/null; then
300
- echo "WARNING: Could not start simulator video recording. Continuing without video." >&2
301
- RECORD_PID=""
302
- fi
313
+ VISUAL_READY_PATH="${OCQA_VISUAL_READY_PATH:-$CAPTURE_DIR/visual-ready}"
314
+ RECORDING_STARTED_PATH="$CAPTURE_DIR/recording-started"
315
+ rm -f "$VISUAL_READY_PATH" "$RECORDING_STARTED_PATH"
316
+ export OCQA_VISUAL_READY_PATH="$VISUAL_READY_PATH"
317
+ export OCQA_RECORDING_STARTED_PATH="$RECORDING_STARTED_PATH"
303
318
 
304
319
  # Run exploration with watchdog timeout to avoid silent hangs.
305
320
  run_harness_test "testAutonomousExploration" "$SIM_NAME" "$APP_BUNDLE" "$MAX_ACTIONS" "$EXPLORE_TIMEOUT" > "$local_output_file" 2>&1 &
306
321
  HARNESS_PID=$!
307
322
 
308
323
  START_TS=$(date +%s)
324
+ RECORDING_ATTEMPTED=0
309
325
  while kill -0 "$HARNESS_PID" 2>/dev/null; do
326
+ if [[ "$RECORDING_ATTEMPTED" -eq 0 && -f "$VISUAL_READY_PATH" ]]; then
327
+ RECORDING_ATTEMPTED=1
328
+ if xcrun simctl io "$UDID" recordVideo --codec=h264 "$CAPTURE_DIR/exploration.mov" & then
329
+ RECORD_PID=$!
330
+ fi
331
+ sleep 0.25
332
+ if [[ -z "$RECORD_PID" ]] || ! kill -0 "$RECORD_PID" 2>/dev/null; then
333
+ echo "WARNING: Could not start simulator video recording. Continuing without video." >&2
334
+ RECORD_PID=""
335
+ fi
336
+ # Always release the bounded harness wait. The missing video remains explicit in the
337
+ # report; exploration itself must not hang merely because recording was unavailable.
338
+ : > "$RECORDING_STARTED_PATH"
339
+ fi
310
340
  NOW_TS=$(date +%s)
311
341
  ELAPSED=$((NOW_TS - START_TS))
312
342
  if [[ "$ELAPSED" -ge "$EXPLORE_TIMEOUT" ]]; then
@@ -316,7 +346,7 @@ OCQA_COMPLETE:{\"actions\":0,\"states\":0,\"issues\":1,\"screens\":\"\",\"outcom
316
346
  kill -KILL "$HARNESS_PID" 2>/dev/null || true
317
347
  break
318
348
  fi
319
- sleep 2
349
+ if [[ "$RECORDING_ATTEMPTED" -eq 0 ]]; then sleep 0.1; else sleep 1; fi
320
350
  done
321
351
 
322
352
  wait "$HARNESS_PID" 2>/dev/null || true
@@ -327,6 +357,7 @@ OCQA_COMPLETE:{\"actions\":0,\"states\":0,\"issues\":1,\"screens\":\"\",\"outcom
327
357
  kill -INT "$RECORD_PID" 2>/dev/null || true
328
358
  wait "$RECORD_PID" 2>/dev/null || true
329
359
  fi
360
+ rm -f "$RECORDING_STARTED_PATH"
330
361
  sleep 1
331
362
  fi
332
363
 
@@ -40,6 +40,14 @@ TOKEN="$(date +%s)"
40
40
  CFG="/tmp/ocqa-flow-$TOKEN.json"
41
41
  AI_RESP="/tmp/ocqa-flow-ai-$TOKEN.json"
42
42
  AI_DIR="/tmp/ocqa-flow-ai-$TOKEN"
43
+ EVIDENCE_DIR="${TAPP_FLOW_EVIDENCE_DIR:-/tmp/tapp-flow-ios-$TOKEN}"
44
+ RESULT_BUNDLE="$EVIDENCE_DIR/result.xcresult"
45
+ mkdir -p "$EVIDENCE_DIR"
46
+ case "$FLOW" in
47
+ *.json) FLOW_EVIDENCE_SOURCE="$EVIDENCE_DIR/flow-source.json" ;;
48
+ *) FLOW_EVIDENCE_SOURCE="$EVIDENCE_DIR/flow-source.yml" ;;
49
+ esac
50
+ cp "$FLOW" "$FLOW_EVIDENCE_SOURCE"
43
51
  python3 - "$CFG" "$APP" "$FLOW_JSON" "$AI_RESP" "$AI_DIR" <<'PY'
44
52
  import json, os, sys
45
53
  cfg, app, flow_json, ai_resp, ai_dir = sys.argv[1:6]
@@ -82,9 +90,12 @@ fi
82
90
 
83
91
  TEST_RUNNER_OCQA_CONFIG_PATH="$CFG" xcodebuild test-without-building \
84
92
  -xctestrun "$XCTR" -destination "platform=iOS Simulator,id=$UDID" \
85
- -only-testing:"OCQAHarnessUITests/ExplorerTests/testReplayFlow" > "$LOG" 2>&1
93
+ -only-testing:"OCQAHarnessUITests/ExplorerTests/testReplayFlow" \
94
+ -resultBundlePath "$RESULT_BUNDLE" > "$LOG" 2>&1
86
95
  [ -n "$RESPONDER_PID" ] && { kill "$RESPONDER_PID" 2>/dev/null; wait "$RESPONDER_PID" 2>/dev/null; }
87
96
 
97
+ cp "$LOG" "$EVIDENCE_DIR/flow.log"
98
+ python3 "$ROOT/scripts/flow_lib.py" report --json "$LOG" > "$EVIDENCE_DIR/flow-report.json"
88
99
  echo ""
89
100
  python3 "$ROOT/scripts/flow_lib.py" report "$LOG"
90
101
  exit $?
@@ -14,7 +14,8 @@ screen or journey works from source inspection alone.
14
14
  |---|---|
15
15
  | See or screenshot one screen | `open` / `tapp_open_app` |
16
16
  | Inspect controls on the current screen | `tree` / `tapp_ui_tree` |
17
- | Drive a specific journey | MCP session start act end |
17
+ | Reach a named screen/control | `focus` / `tapp_focus` (source + observed UI Map fast path) |
18
+ | Drive a specific journey | MCP session start → focus or act → end |
18
19
  | Find bugs autonomously | `explore` / `tapp_explore` |
19
20
  | Preserve a journey | record and save a Flow; replay it deterministically |
20
21
  | Decide whether a merge passes policy | `ci`; exploration never decides this |
@@ -37,11 +38,34 @@ When the user asks for a general first test of a repository:
37
38
  `npx -y @aarwitz/tapp@latest doctor`, apply only the stated remediation that is in scope, and
38
39
  retry once.
39
40
 
40
- For a focused request, use the requested target directly rather than forcing repository onboarding.
41
+ For a focused request in an already-grounded repository, use the requested target directly rather
42
+ than starting another broad exploration. If `.tapp/ui-map.json` does not exist yet, ground it once
43
+ with `init . --explore`; source alone can locate a surface but cannot authorize unobserved taps.
41
44
  Targets may be a repository path, Xcode container, `.app`, iOS bundle id, APK plus Android app id,
42
45
  or owned HTTP(S) URL. Never explore a third-party web property without authorization: exploration
43
46
  clicks and types.
44
47
 
48
+ ## Navigate like a source-connected expert
49
+
50
+ When the user names a screen, control, or UI condition, do not discover the app one screenshot at a
51
+ time. Start from the repository source, then use Tapp's observed navigation evidence:
52
+
53
+ 1. In a source repository, run `npx -y @aarwitz/tapp@latest focus "<exact request>" [target]`; it
54
+ selects the reviewed model target and prepares web, iOS, or Android from source. In an active MCP
55
+ session call `tapp_focus`; for managed web, `tapp_session_start` can also take `projectDir` and
56
+ `focus` without retyping the URL.
57
+ 2. Tapp searches owned source, reconciles the likely surface with `.tapp/ui-map.json`, and executes
58
+ the shortest runtime-observed route in one call. Read its final tree before visual assertions.
59
+ 3. If Tapp returns source evidence but no replayable route, inspect the cited file/line and relevant
60
+ router/navigation source. Do not wander blindly or invent a path; ground the map or drive only a
61
+ route supported by that source evidence.
62
+
63
+ A fresh repository needs one grounding exploration before `focus` can replay a route. Source can
64
+ locate an unobserved surface, but it never authorizes unobserved taps.
65
+
66
+ Source establishes intent and location; the real UI establishes behavior. A URL-only target has no
67
+ source advantage and correctly falls back to runtime observation.
68
+
45
69
  ## Observe honestly
46
70
 
47
71
  Exploration returns findings, coverage, evidence, and `inconclusive`; it does not return a score or
@@ -64,12 +88,11 @@ point the human to the report's exploration recording when available.
64
88
 
65
89
  ## Drive safely
66
90
 
67
- For an interactive MCP session, read returned `elements[]` before every action, target accessibility
91
+ After the focused fast path, read returned `elements[]` before any remaining action, target accessibility
68
92
  ids or visible labels, check `hittable`, tap a field before typing, and wait for navigation or async
69
93
  content. Use coordinates only as a last resort. End the session when finished.
70
94
 
71
95
  Do not edit the app merely because testing found a defect unless the user also asked for a fix. State
72
96
  what the evidence proves and what remains untested.
73
97
 
74
- Read [references/commands.md](references/commands.md) only when exact CLI/MCP syntax, Flow replay,
75
- credentials, or platform prerequisites are needed.
98
+ Read [references/commands.md](references/commands.md) only for exact CLI/MCP syntax, Flow replay, credentials, or platform prerequisites.
@@ -8,6 +8,7 @@ or detect one unambiguous target.
8
8
  ```bash
9
9
  npx -y @aarwitz/tapp@latest init . --explore
10
10
  npx -y @aarwitz/tapp@latest explore [target]
11
+ npx -y @aarwitz/tapp@latest focus "Save storefront settings visible above keyboard" [target]
11
12
  npx -y @aarwitz/tapp@latest open [target]
12
13
  npx -y @aarwitz/tapp@latest tree [target] --json
13
14
  npx -y @aarwitz/tapp@latest shot
@@ -26,7 +27,10 @@ npx -y @aarwitz/tapp@latest explore https://staging.example.com
26
27
  npx -y @aarwitz/tapp@latest explore MyApp.xcodeproj --platform ios
27
28
  npx -y @aarwitz/tapp@latest open com.example.MyApp --platform ios
28
29
 
29
- # Android: app id is required; APK is optional if already installed.
30
+ # Android source: select a modeled target; Tapp builds, installs, and focuses it.
31
+ npx -y @aarwitz/tapp@latest focus "About screen" . --target demoapp
32
+
33
+ # Android black-box entry: app id is required; APK is optional if already installed.
30
34
  npx -y @aarwitz/tapp@latest explore app-debug.apk --platform android --app-id com.example.app
31
35
  ```
32
36
 
@@ -41,6 +45,7 @@ npx -y @aarwitz/tapp@latest open https://example.com --tap "Not now" --wait-for
41
45
  - `tapp_init`: inspect or initialize a source repository; `operation:"explore"` prepares and explores.
42
46
  - `tapp_build`: build and install an iOS app without needing its bundle id first.
43
47
  - `tapp_open_app`: launch and return a screen summary plus inline screenshot.
48
+ - `tapp_focus`: source-locate a named screen/control and execute the shortest observed route in the active session.
44
49
  - `tapp_ui_tree` / `tapp_screenshot`: inspect the current real surface.
45
50
  - `tapp_session_start` → `tapp_session_act` → `tapp_session_end`: drive one persistent journey.
46
51
  - `tapp_explore`: autonomous iOS, Android, or web exploration; observation only.
@@ -49,13 +54,15 @@ npx -y @aarwitz/tapp@latest open https://example.com --tap "Not now" --wait-for
49
54
  - `tapp_ci_setup`: create a target-scoped baseline or reviewable CI installation.
50
55
 
51
56
  An iOS no-bundle-id MCP path is `tapp_build {projectDir:"."}` followed by `tapp_explore` with the
52
- returned `bundleId`. Android uses `androidAppId` and optional `apkPath`; web uses `url` or
53
- source-connected `tapp_init`.
57
+ returned `bundleId`. Android uses `androidAppId` and optional `apkPath`. Web can use an explicit
58
+ owned `url`, or `tapp_session_start {projectDir:".", focus:"..."}` can build/start one unambiguous
59
+ owned browser target and stop it with `tapp_session_end`.
54
60
 
55
61
  ## Interactive session loop
56
62
 
57
63
  ```text
58
- tapp_session_start {appBundleId:"com.example.app"}
64
+ tapp_session_start {appBundleId:"com.example.app", focus:"Save storefront settings visible above keyboard", projectDir:"."}
65
+ tapp_focus {query:"Save storefront settings visible above keyboard"}
59
66
  tapp_session_act {action:"tap", id:"Email"}
60
67
  tapp_session_act {action:"type", text:"qa@example.com"}
61
68
  tapp_session_act {action:"tap", id:"Password"}
@@ -95,7 +102,8 @@ or `inconclusive`; both `fail` and `inconclusive` block a merge.
95
102
  ## Platform prerequisites
96
103
 
97
104
  - iOS: macOS, Xcode, and a booted simulator. First use builds a cached harness under `~/.tapp`.
98
- - Android: `adb` and a connected authorized emulator/device.
105
+ - Android: `adb` and a connected authorized emulator/device; JDK 17 for source builds (not for a
106
+ prebuilt APK).
99
107
  - Web: Playwright and Chromium. If Tapp reports the browser missing, run
100
108
  `npx playwright install chromium` and retry.
101
109