@aarwitz/tapp 0.15.0 → 0.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aarwitz/tapp",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "mcpName": "io.github.aarwitz/tapp",
5
5
  "description": "Release contracts, autonomous QA, and evidence-backed CI gates for iOS, Android, and web.",
6
6
  "license": "MIT",
@@ -24,7 +24,19 @@
24
24
  "docs/application-model.md",
25
25
  "docs/scenarios.md",
26
26
  "mcp-server/src/",
27
- "scripts/",
27
+ "scripts/ci-gate.sh",
28
+ "scripts/compile-contract.js",
29
+ "scripts/compile-flow.js",
30
+ "scripts/flow-platform.js",
31
+ "scripts/flow_ai_judge.py",
32
+ "scripts/flow_lib.py",
33
+ "scripts/platform-gate.js",
34
+ "scripts/pr-plan.js",
35
+ "scripts/quick-capture.sh",
36
+ "scripts/run-android-flow.js",
37
+ "scripts/run-flow.sh",
38
+ "scripts/run-web-flow.js",
39
+ "scripts/run-web-scenario.js",
28
40
  "Harness/OCQAHarness/",
29
41
  "Harness/OCQAHarnessUITests/",
30
42
  "Harness/OCQAHarness.xcodeproj/",
@@ -75,7 +87,7 @@
75
87
  "mobile"
76
88
  ],
77
89
  "scripts": {
78
- "test": "node --test tests/report.test.js tests/regression.test.js tests/engine.test.js tests/project-config.test.js tests/application-model.test.js tests/ui-map.test.js tests/task-runtime.test.js tests/release-contract.test.js tests/pr-selection.test.js tests/flow-runtime.test.js tests/web-explorer.test.js tests/web-session.test.js tests/web-flow.test.js tests/scenario-runtime.test.js tests/android-driver.test.js tests/android-explorer.test.js tests/android-flow.test.js tests/managed-web.test.js tests/product-operations.test.js tests/browser-product.test.js tests/browser-onboarding.test.js tests/managed-operation.test.js tests/cloud-runner.test.js tests/ci-setup.test.js tests/ci-install.test.js tests/cli.test.js tests/action.test.js tests/ci-gate.test.js tests/ci-report.test.js tests/desktop-protocol.test.js tests/ios-flow-protocol.test.js",
90
+ "test": "node --test tests/report.test.js tests/regression.test.js tests/engine.test.js tests/project-config.test.js tests/application-model.test.js tests/ui-map.test.js tests/task-runtime.test.js tests/release-contract.test.js tests/pr-selection.test.js tests/flow-runtime.test.js tests/web-explorer.test.js tests/web-session.test.js tests/web-flow.test.js tests/scenario-runtime.test.js tests/android-driver.test.js tests/android-explorer.test.js tests/android-flow.test.js tests/managed-web.test.js tests/product-operations.test.js tests/browser-product.test.js tests/browser-onboarding.test.js tests/managed-operation.test.js tests/cloud-runner.test.js tests/ci-setup.test.js tests/ci-install.test.js tests/cli.test.js tests/action.test.js tests/package-surface.test.js tests/ci-gate.test.js tests/ci-report.test.js tests/desktop-protocol.test.js tests/ios-flow-protocol.test.js",
79
91
  "test:browser-journey": "node --test tests/browser-journey.test.js",
80
92
  "test:browser-native": "TAPP_RUN_NATIVE_BROWSER=1 node --test tests/browser-native-journey.test.js"
81
93
  }
@@ -254,7 +254,7 @@ print(named[0] if named else (iphones[0] if iphones else ''))
254
254
  if [[ -z "$UDID" ]]; then
255
255
  RUNTIME="$(xcrun simctl list runtimes -j | python3 -c 'import sys,json; rts=[r for r in json.load(sys.stdin)["runtimes"] if r.get("isAvailable") and r["platform"]=="iOS"]; print(rts[-1]["identifier"] if rts else "")')"
256
256
  [[ -n "$RUNTIME" ]] || { echo "❌ No iOS simulator runtime available" >&2; exit 1; }
257
- UDID="$(xcrun simctl create "AutoTap CI" "$DEVICE" "$RUNTIME")" || { echo "❌ Could not create simulator" >&2; exit 1; }
257
+ UDID="$(xcrun simctl create "Tapp CI" "$DEVICE" "$RUNTIME")" || { echo "❌ Could not create simulator" >&2; exit 1; }
258
258
  fi
259
259
  echo "Booting $UDID …"
260
260
  xcrun simctl boot "$UDID" || true
@@ -269,7 +269,7 @@ xcrun simctl install "$UDID" "$APP_PATH" || { echo "❌ simctl install failed
269
269
  # ── Autonomous exploration (quick-capture builds the harness itself if needed).
270
270
  step "Explore ($ACTIONS actions, ${TIMEOUT}s watchdog)"
271
271
  set +e
272
- CAPTURE_ROOT="${AUTOTAP_HOME:-$ROOT}/captures"
272
+ CAPTURE_ROOT="${TAPP_HOME:-${AUTOTAP_HOME:-$ROOT}}/captures"
273
273
  mkdir -p "$CAPTURE_ROOT"
274
274
  CAPTURE_DIR="$(mktemp -d "$CAPTURE_ROOT/ci.XXXXXX")"
275
275
  TAPP_CAPTURE_DIR="$CAPTURE_DIR" OCQA_PR_TARGET_JSON="$IOS_PR_TARGET_JSON" "$ROOT/scripts/quick-capture.sh" explore "$BUNDLE_ID" --actions "$ACTIONS" --timeout "$TIMEOUT"
@@ -282,7 +282,7 @@ echo "Markers: $MARKERS"
282
282
  FLOW_LOG_ARGS=()
283
283
  if [[ "${#FLOW_FILES[@]}" -gt 0 ]]; then
284
284
  step "Flows"
285
- FLOW_LOG_DIR="$(mktemp -d /tmp/autotap-ci-flows.XXXXXX)"
285
+ FLOW_LOG_DIR="$(mktemp -d /tmp/tapp-ci-flows.XXXXXX)"
286
286
  for flow in "${FLOW_FILES[@]}"; do
287
287
  name="$(basename "$flow" .yml)"
288
288
  log="$FLOW_LOG_DIR/$name.log"
@@ -26,7 +26,7 @@ def _ctx():
26
26
 
27
27
 
28
28
  SSL_CTX = _ctx()
29
- MODEL = os.environ.get("AUTOTAP_VISION_MODEL", "claude-haiku-4-5-20251001")
29
+ MODEL = os.environ.get("TAPP_VISION_MODEL") or os.environ.get("AUTOTAP_VISION_MODEL", "claude-haiku-4-5-20251001")
30
30
  SYSTEM = (
31
31
  "You are a QA test oracle. You are shown one iOS app screenshot and a CLAIM the test asserts about "
32
32
  "it. Decide if the claim is TRUE of what is actually visible. Be strict and literal — only pass if "
@@ -12,27 +12,28 @@ set -euo pipefail
12
12
  # [--timeout <secs>] # Hard timeout for explore command
13
13
  # quick-capture.sh tree <bundleId> # Dump accessibility tree
14
14
  #
15
- # All output goes to ~/repos/AutoTap/captures/<timestamp>/
15
+ # All output goes to the configured Tapp home or the repository capture directory.
16
16
 
17
17
  PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
18
18
  # TAPP_CAPTURE_DIR selects one exact run directory (used by CI so a failed run can never
19
- # accidentally reuse stale markers). AUTOTAP_HOME redirects all writable output otherwise —
20
- # set by the `autotap` CLI when running as an installed npm package, where the package dir
19
+ # accidentally reuse stale markers). TAPP_HOME redirects all writable output otherwise —
20
+ # set by the `tapp` CLI when running as an installed npm package, where the package dir
21
21
  # must stay read-only. Unset (repo dev flow), everything lands in the repo as before.
22
+ TAPP_RUNTIME_HOME="${TAPP_HOME:-${AUTOTAP_HOME:-}}"
22
23
  if [[ -n "${TAPP_CAPTURE_DIR:-}" ]]; then
23
24
  CAPTURE_DIR="$TAPP_CAPTURE_DIR"
24
- elif [[ -n "${AUTOTAP_HOME:-}" ]]; then
25
- CAPTURE_DIR="$AUTOTAP_HOME/captures/$(date +%Y%m%d-%H%M%S)"
25
+ elif [[ -n "$TAPP_RUNTIME_HOME" ]]; then
26
+ CAPTURE_DIR="$TAPP_RUNTIME_HOME/captures/$(date +%Y%m%d-%H%M%S)"
26
27
  else
27
28
  CAPTURE_DIR="$PROJECT_ROOT/captures/$(date +%Y%m%d-%H%M%S)"
28
29
  fi
29
- if [[ -n "${AUTOTAP_HOME:-}" ]]; then
30
- HARNESS_DERIVED="$AUTOTAP_HOME/harness-derived"
30
+ if [[ -n "$TAPP_RUNTIME_HOME" ]]; then
31
+ HARNESS_DERIVED="$TAPP_RUNTIME_HOME/harness-derived"
31
32
  else
32
- HARNESS_DERIVED="/tmp/autotap-harness-derived"
33
+ HARNESS_DERIVED="/tmp/tapp-harness-derived"
33
34
  fi
34
35
  HARNESS_PROJECT="$PROJECT_ROOT/Harness/OCQAHarness.xcodeproj"
35
- DEFAULT_BUNDLE="com.autotap.demoapp"
36
+ DEFAULT_BUNDLE="io.github.aarwitz.tapp.demoapp"
36
37
 
37
38
  get_booted_sim() {
38
39
  xcrun simctl list devices booted -j 2>/dev/null \
@@ -168,13 +169,13 @@ run_harness_test() {
168
169
  "OCQA_MAX_ACTIONS": "$max_actions",
169
170
  "OCQA_TIMEOUT_SECONDS": "$timeout_secs",
170
171
  "OCQA_TEST_EMAIL": "${OCQA_TEST_EMAIL:-qa@example.com}",
171
- "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Autotap123!}"$interactive_line$overrides_line$launch_args_line$launch_env_line$login_steps_line$pr_target_line
172
+ "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Tapp123!}"$interactive_line$overrides_line$launch_args_line$launch_env_line$login_steps_line$pr_target_line
172
173
  }
173
174
  CONF
174
175
 
175
176
  local xctestrun=$(find "$HARNESS_DERIVED/Build/Products" -name "*.xctestrun" 2>/dev/null | head -1)
176
177
  if [[ -z "$xctestrun" ]]; then
177
- echo "ERROR: No xctestrun found. Run: ./scripts/deploy-and-build.sh --harness" >&2
178
+ echo "ERROR: No xctestrun found. Run: tapp install" >&2
178
179
  return 1
179
180
  fi
180
181
 
@@ -382,12 +383,12 @@ case "$MODE" in
382
383
  "OCQA_SESSION_RESULT_PATH": "$SESS_RES",
383
384
  "OCQA_SESSION_TIMEOUT": "$SESS_TIMEOUT",
384
385
  "OCQA_TEST_EMAIL": "${OCQA_TEST_EMAIL:-qa@example.com}",
385
- "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Autotap123!}"$sess_args_line$sess_env_line
386
+ "OCQA_TEST_PASSWORD": "${OCQA_TEST_PASSWORD:-Tapp123!}"$sess_args_line$sess_env_line
386
387
  }
387
388
  CONF
388
389
  xctestrun=$(find "$HARNESS_DERIVED/Build/Products" -name "*.xctestrun" 2>/dev/null | head -1)
389
390
  if [[ -z "$xctestrun" ]]; then
390
- echo "ERROR: No xctestrun. Run: ./scripts/deploy-and-build.sh --harness" >&2
391
+ echo "ERROR: No xctestrun. Run: tapp install" >&2
391
392
  exit 1
392
393
  fi
393
394
  export TEST_RUNNER_OCQA_CONFIG_PATH=/tmp/ocqa-run-config.json
@@ -399,7 +400,7 @@ CONF
399
400
  ;;
400
401
 
401
402
  build-harness)
402
- # Just (re)build the harness cache for the booted sim — used by `autotap install`
403
+ # Just (re)build the harness cache for the booted sim — used by `tapp install`
403
404
  # for a fast first tool call later. No capture output.
404
405
  ensure_harness_built "$SIM_NAME"
405
406
  rmdir "$CAPTURE_DIR" 2>/dev/null || true
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Run an AutoTap Flow — a deterministic, authored/recorded E2E test (see docs/flows-architecture.md).
2
+ # Run a Tapp Flow — a deterministic, authored/recorded E2E test.
3
3
  #
4
4
  # Replays the flow's steps against an app on the BOOTED simulator with wait-for-condition timing and
5
5
  # poll-with-timeout assertions (no sleeps), then prints a scannable pass/fail report. Deterministic by
@@ -24,14 +24,14 @@ APP="${2:-}"
24
24
 
25
25
  UDID="$(xcrun simctl list devices booted -j 2>/dev/null | python3 -c 'import sys,json; d=json.load(sys.stdin); print(next((x["udid"] for v in d["devices"].values() for x in v if x.get("state")=="Booted"), ""))')"
26
26
  [ -z "$UDID" ] && { echo "❌ No booted simulator."; exit 2; }
27
- # When run through the `autotap` CLI (installed npm package), the harness cache lives under
28
- # AUTOTAP_HOME — check there first, then the repo-dev locations.
27
+ # When run through the installed `tapp` CLI, the harness cache lives under TAPP_HOME.
29
28
  XCTR=""
30
- [ -n "${AUTOTAP_HOME:-}" ] && XCTR="$(find "$AUTOTAP_HOME/harness-derived/Build/Products" -name '*.xctestrun' 2>/dev/null | head -1)"
29
+ TAPP_RUNTIME_HOME="${TAPP_HOME:-${AUTOTAP_HOME:-}}"
30
+ [ -n "$TAPP_RUNTIME_HOME" ] && XCTR="$(find "$TAPP_RUNTIME_HOME/harness-derived/Build/Products" -name '*.xctestrun' 2>/dev/null | head -1)"
31
31
  [ -z "$XCTR" ] && XCTR="$(find "$HOME/Library/Developer/Xcode/DerivedData/OCQAHarness-"*/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
32
- [ -z "$XCTR" ] && XCTR="$(find /tmp/autotap-harness-derived/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
32
+ [ -z "$XCTR" ] && XCTR="$(find /tmp/tapp-harness-derived/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
33
33
  [ -z "$XCTR" ] && XCTR="$(find /tmp/harness-build/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
34
- [ -z "$XCTR" ] && { echo "❌ Harness not built. Run: scripts/deploy-and-build.sh --harness"; exit 2; }
34
+ [ -z "$XCTR" ] && { echo "❌ Harness not built. Run: tapp install"; exit 2; }
35
35
 
36
36
  NAME="$(python3 -c "import sys,json;print(json.loads(sys.argv[1]).get('name','flow'))" "$FLOW_JSON")"
37
37
  echo "▶️ Running flow \"$NAME\" against $APP …"
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Build and exercise every native Android fixture through the same release gate
3
- # customers use. A connected, booted emulator/device is required.
4
- set -euo pipefail
5
-
6
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
7
- CORPUS="$ROOT/AndroidCorpus"
8
- SERIAL="${1:-${ANDROID_SERIAL:-}}"
9
- SERIAL_ARGS=()
10
- [[ -n "$SERIAL" ]] && SERIAL_ARGS=(--serial "$SERIAL")
11
-
12
- "$CORPUS/gradlew" -p "$CORPUS" \
13
- :demoapp:assembleDebug :logindemo:assembleDebug :shopdemo:assembleDebug --no-daemon --quiet
14
-
15
- run_fixture() {
16
- local module="$1" app_id="$2" flow="$3"
17
- local apk="$CORPUS/$module/build/outputs/apk/debug/$module-debug.apk"
18
- local output_dir="${RUNNER_TEMP:-/tmp}/tapp-android-corpus-$module"
19
- mkdir -p "$output_dir"
20
- echo "Testing Android corpus fixture: $module ($app_id)"
21
- "$ROOT/scripts/ci-gate.sh" --platform android --apk "$apk" --app-id "$app_id" \
22
- "${SERIAL_ARGS[@]}" --actions 12 --timeout 180 --flows "$CORPUS/$module/.autotap/flows/$flow" \
23
- --project-dir "$CORPUS/$module" --json-out "$output_dir/report.json" --md-out "$output_dir/report.md"
24
- }
25
-
26
- run_fixture demoapp io.tapp.corpus.demo smoke.yml
27
- run_fixture logindemo io.tapp.corpus.login sign-in.yml
28
- run_fixture shopdemo io.tapp.corpus.shop checkout.yml
29
-
30
- echo "All Android corpus gates passed."
@@ -1,157 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Reclaim disk space consumed by Xcode build artifacts.
3
- #
4
- # Default run (no flags): deletes unavailable simulators and stale DerivedData
5
- # from unrelated projects. Flags unlock more aggressive passes.
6
- #
7
- # Usage:
8
- # scripts/cleanup-xcode.sh # safe defaults
9
- # scripts/cleanup-xcode.sh --dry-run # preview only, nothing deleted
10
- # scripts/cleanup-xcode.sh --device-support # also wipe iOS DeviceSupport caches
11
- # scripts/cleanup-xcode.sh --all # everything above + Xcode caches
12
- #
13
- # AutoTap-related DerivedData (OCQAHarness, DemoApp, LoginDemo, etc.) is always
14
- # preserved. The local build/ directory in this repo is never touched.
15
- set -uo pipefail
16
-
17
- DRY_RUN=0
18
- CLEAN_DEVICE_SUPPORT=0
19
- CLEAN_ALL=0
20
-
21
- for arg in "$@"; do
22
- case "$arg" in
23
- --dry-run) DRY_RUN=1 ;;
24
- --device-support) CLEAN_DEVICE_SUPPORT=1 ;;
25
- --all) CLEAN_DEVICE_SUPPORT=1; CLEAN_ALL=1 ;;
26
- -h|--help)
27
- sed -n '/^# /s/^# //p' "$0" | head -15
28
- exit 0 ;;
29
- *) echo "Unknown flag: $arg (try --help)"; exit 1 ;;
30
- esac
31
- done
32
-
33
- DERIVED="$HOME/Library/Developer/Xcode/DerivedData"
34
- SIMS="$HOME/Library/Developer/CoreSimulator/Devices"
35
- DEVICE_SUPPORT="$HOME/Library/Developer/Xcode/iOS DeviceSupport"
36
- XCODE_CACHES="$HOME/Library/Caches/com.apple.dt.Xcode"
37
-
38
- # Projects to preserve in DerivedData (all AutoTap-related build products).
39
- KEEP_PREFIXES=("AutoTap" "OCQAHarness" "DemoApp" "LoginDemo" "WizardDemo"
40
- "ShopDemo" "RestaurantDemo" "SymbolCache")
41
-
42
- hr() { printf '%0.s─' {1..60}; echo; }
43
- human() { du -sh "$1" 2>/dev/null | cut -f1; }
44
-
45
- run() {
46
- if [[ $DRY_RUN -eq 1 ]]; then
47
- echo " [dry-run] $*"
48
- else
49
- eval "$@"
50
- fi
51
- }
52
-
53
- echo ""
54
- hr
55
- echo " Xcode Cleanup $(date '+%Y-%m-%d %H:%M')"
56
- [[ $DRY_RUN -eq 1 ]] && echo " DRY RUN — nothing will be deleted"
57
- hr
58
-
59
- # ── Before snapshot ──────────────────────────────────────────────────────────
60
- echo ""
61
- echo "Before:"
62
- printf " %-38s %s\n" "CoreSimulator/Devices" "$(human "$SIMS")"
63
- printf " %-38s %s\n" "Xcode/DerivedData" "$(human "$DERIVED")"
64
- printf " %-38s %s\n" "Xcode/iOS DeviceSupport" "$(human "$DEVICE_SUPPORT")"
65
- printf " %-38s %s\n" "Xcode caches" "$(human "$XCODE_CACHES")"
66
- echo ""
67
-
68
- # ── 1. Unavailable simulators ─────────────────────────────────────────────────
69
- echo "1/3 Removing unavailable simulators …"
70
- UNAVAIL=$(xcrun simctl list devices --json 2>/dev/null | \
71
- python3 -c '
72
- import sys, json
73
- d = json.load(sys.stdin)
74
- n = sum(1 for v in d["devices"].values() for x in v if not x.get("isAvailable", True))
75
- print(n)
76
- ')
77
- echo " Found $UNAVAIL unavailable device(s)."
78
- if [[ "$UNAVAIL" -gt 0 ]]; then
79
- run "xcrun simctl delete unavailable"
80
- fi
81
-
82
- # ── 2. Unrelated DerivedData ──────────────────────────────────────────────────
83
- echo ""
84
- echo "2/3 Scanning DerivedData for unrelated projects …"
85
- STALE=()
86
- if [[ -d "$DERIVED" ]]; then
87
- while IFS= read -r -d '' entry; do
88
- base="$(basename "$entry")"
89
- keep=0
90
- for prefix in "${KEEP_PREFIXES[@]}"; do
91
- if [[ "$base" == "$prefix"* ]]; then
92
- keep=1; break
93
- fi
94
- done
95
- [[ $keep -eq 0 ]] && STALE+=("$entry")
96
- done < <(find "$DERIVED" -maxdepth 1 -mindepth 1 -type d -print0)
97
- fi
98
-
99
- if [[ ${#STALE[@]} -eq 0 ]]; then
100
- echo " Nothing to remove."
101
- else
102
- echo " Will remove ${#STALE[@]} unrelated build folder(s):"
103
- for entry in "${STALE[@]}"; do
104
- printf " %-8s %s\n" "$(human "$entry")" "$(basename "$entry")"
105
- done
106
- for entry in "${STALE[@]}"; do
107
- run "rm -rf \"$entry\""
108
- done
109
- fi
110
-
111
- # ── 3. iOS DeviceSupport (optional) ──────────────────────────────────────────
112
- echo ""
113
- echo "3/3 iOS DeviceSupport …"
114
- if [[ $CLEAN_DEVICE_SUPPORT -eq 1 ]]; then
115
- if [[ -d "$DEVICE_SUPPORT" ]]; then
116
- echo " Removing $(human "$DEVICE_SUPPORT") of device symbol caches …"
117
- echo " (Xcode re-downloads these automatically when a device is connected.)"
118
- run "rm -rf \"$DEVICE_SUPPORT\""
119
- else
120
- echo " Directory not found — nothing to do."
121
- fi
122
- else
123
- SIZE=$(human "$DEVICE_SUPPORT")
124
- echo " Skipped ($SIZE on disk). Pass --device-support to remove."
125
- fi
126
-
127
- # ── 4. Xcode caches (--all only) ─────────────────────────────────────────────
128
- if [[ $CLEAN_ALL -eq 1 ]]; then
129
- echo ""
130
- echo "4/4 Xcode caches …"
131
- if [[ -d "$XCODE_CACHES" ]]; then
132
- echo " Removing $(human "$XCODE_CACHES") …"
133
- run "rm -rf \"$XCODE_CACHES\""
134
- fi
135
- fi
136
-
137
- # ── Simulator data cache (always safe) ───────────────────────────────────────
138
- SIM_CACHES="$HOME/Library/Developer/CoreSimulator/Caches"
139
- if [[ -d "$SIM_CACHES" ]] && [[ "$(du -sk "$SIM_CACHES" 2>/dev/null | cut -f1)" -gt 0 ]]; then
140
- echo ""
141
- echo " Removing simulator caches ($(human "$SIM_CACHES")) …"
142
- run "rm -rf \"$SIM_CACHES\""
143
- fi
144
-
145
- # ── After snapshot ─────────────────────────────────────────────────────────────
146
- echo ""
147
- if [[ $DRY_RUN -eq 0 ]]; then
148
- hr
149
- echo "After:"
150
- printf " %-38s %s\n" "CoreSimulator/Devices" "$(human "$SIMS")"
151
- printf " %-38s %s\n" "Xcode/DerivedData" "$(human "$DERIVED")"
152
- printf " %-38s %s\n" "Xcode/iOS DeviceSupport" "$(human "$DEVICE_SUPPORT")"
153
- printf " %-38s %s\n" "Xcode caches" "$(human "$XCODE_CACHES")"
154
- echo ""
155
- fi
156
-
157
- echo "Done."
@@ -1,9 +0,0 @@
1
- # Corpus sweep list — curated from dkhamsing/open-source-ios-apps across backend classes.
2
- # Class coverage: self-hostable-service client (login wall), public API (no key),
3
- # public API (keyed), no-backend/local-first, heavyweight real-world.
4
- https://github.com/Dimillian/IceCubesApp
5
- https://github.com/weiran/Hackers
6
- https://github.com/Ranchero-Software/NetNewsWire
7
- https://github.com/yattee/yattee
8
- https://github.com/wikimedia/wikipedia-ios
9
- https://github.com/mastodon/mastodon-ios
@@ -1,121 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Corpus sweep — run a list of real open-source iOS apps through the tapp pipeline and
3
- # produce a friction scoreboard. Failures ARE the data: every app gets a row recording
4
- # how far it got (clone → detect → build → install → explore) and why it stopped.
5
- #
6
- # Usage: scripts/corpus-sweep.sh <urls-file> [--actions N] [--out <dir>]
7
- # urls-file: one git URL per line (# comments ok)
8
- #
9
- # Needs a booted simulator. Rows land in <out>/scoreboard.tsv; per-app logs in <out>/<app>/.
10
- set -uo pipefail
11
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
12
-
13
- URLS_FILE="${1:?usage: corpus-sweep.sh <urls-file> [--actions N] [--out dir]}"; shift
14
- ACTIONS=25
15
- OUT="/tmp/tapp-corpus"
16
- while [[ $# -gt 0 ]]; do
17
- case "$1" in
18
- --actions) ACTIONS="$2"; shift 2 ;;
19
- --out) OUT="$2"; shift 2 ;;
20
- *) echo "unknown arg $1" >&2; exit 2 ;;
21
- esac
22
- done
23
- mkdir -p "$OUT"
24
- BOARD="$OUT/scoreboard.tsv"
25
- [[ -f "$BOARD" ]] || echo -e "app\tstage\tbackend_signals\tbundle\tscreens\tactions\tfindings\tverdict\tnote" > "$BOARD"
26
-
27
- UDID="$(xcrun simctl list devices booted -j | python3 -c 'import sys,json; d=json.load(sys.stdin); print(next((x["udid"] for v in d["devices"].values() for x in v if x.get("state")=="Booted"), ""))')"
28
- [[ -n "$UDID" ]] || { echo "❌ boot a simulator first" >&2; exit 1; }
29
-
30
- row() { echo -e "$1\t$2\t$3\t$4\t$5\t$6\t$7\t$8\t$9" >> "$BOARD"; }
31
-
32
- # Cheap backend-class signals from the repo tree (feeds the classifier's rules).
33
- backend_signals() {
34
- local dir="$1" sig=()
35
- [[ -n "$(find "$dir" -maxdepth 4 -name 'GoogleService-Info.plist' -print -quit 2>/dev/null)" ]] && sig+=("firebase-plist")
36
- grep -rqli "firebase" "$dir"/Podfile "$dir"/*/Package.resolved "$dir"/Package.resolved 2>/dev/null && sig+=("firebase-dep")
37
- grep -rqli "supabase" "$dir"/Podfile "$dir"/*/Package.resolved "$dir"/Package.resolved 2>/dev/null && sig+=("supabase-dep")
38
- [[ -n "$(find "$dir" -maxdepth 2 -name 'docker-compose.y*ml' -print -quit 2>/dev/null)" ]] && sig+=("in-repo-backend")
39
- grep -rqli "mastodon" "$dir"/Package.resolved 2>/dev/null && sig+=("mastodon-client")
40
- grep -rql "YOUR_API_KEY\|INSERT_API_KEY\|<API_KEY>" --include="*.xcconfig" --include="*.plist" --include="*.swift" "$dir" 2>/dev/null | head -1 | grep -q . && sig+=("key-placeholder")
41
- local IFS=","; echo "${sig[*]:-none}"
42
- }
43
-
44
- # fd 3 for the URL list — xcodebuild/git inside the loop consume stdin and would
45
- # silently swallow the remaining lines (this truncated the first sweep run).
46
- while IFS= read -r url <&3; do
47
- [[ -z "$url" || "$url" == \#* ]] && continue
48
- name="$(basename "$url" .git)"
49
- dir="$OUT/$name"
50
- log="$OUT/$name.log"
51
- echo "━━━ $name"
52
-
53
- # 1. clone
54
- if [[ ! -d "$dir" ]]; then
55
- git clone --depth 1 --recurse-submodules --shallow-submodules "$url" "$dir" >"$log" 2>&1 \
56
- || { row "$name" clone none - - - - - "git clone failed"; continue; }
57
- fi
58
- signals="$(backend_signals "$dir")"
59
-
60
- # 2. detect workspace/project + scheme (workspace wins; ignore Pods)
61
- target="$(find "$dir" -maxdepth 3 -name '*.xcworkspace' -not -path '*/Pods/*' -not -path '*/.*' | head -1)"
62
- flag="-workspace"
63
- if [[ -z "$target" ]]; then
64
- target="$(find "$dir" -maxdepth 3 -name '*.xcodeproj' -not -path '*/Pods/*' | head -1)"
65
- flag="-project"
66
- fi
67
- [[ -n "$target" ]] || { row "$name" detect "$signals" - - - - - "no xcworkspace/xcodeproj found"; continue; }
68
- schemes="$(xcodebuild -list -json $flag "$target" 2>>"$log" | python3 -c 'import sys,json
69
- try:
70
- d=json.load(sys.stdin); o=d.get("workspace") or d.get("project") or {}
71
- print("\n".join(o.get("schemes") or []))
72
- except Exception: pass')"
73
- # Prefer an explicitly-iOS scheme (multi-platform repos like NetNewsWire name-match the
74
- # macOS scheme first, which can't build for an iOS Simulator destination).
75
- scheme="$(echo "$schemes" | grep -i "ios" | grep -iv "test\|widget\|extension\|notification\|clip" | head -1)"
76
- [[ -z "$scheme" ]] && scheme="$(echo "$schemes" | grep -ix "$name" | head -1)"
77
- [[ -z "$scheme" ]] && scheme="$(echo "$schemes" | grep -iv "test\|uitests\|widget\|extension\|notification\|clip\|watch\|mac" | head -1)"
78
- [[ -z "$scheme" ]] && scheme="$(echo "$schemes" | head -1)"
79
- [[ -n "$scheme" ]] || { row "$name" detect "$signals" - - - - - "no schemes listed"; continue; }
80
- echo " scheme: $scheme"
81
-
82
- # 3. build for simulator (20 min cap; perl alarm — stock macOS has no `timeout`)
83
- dd="$OUT/dd-$name"
84
- if ! perl -e 'alarm shift; exec @ARGV' 1200 xcodebuild $flag "$target" -scheme "$scheme" \
85
- -destination 'generic/platform=iOS Simulator' -derivedDataPath "$dd" \
86
- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
87
- build >>"$log" 2>&1; then
88
- reason="$(grep -m1 -E "error: |xcodebuild: error" "$log" | tail -c 120 || true)"
89
- row "$name" build "$signals" - - - - - "build failed: ${reason:-see log}"
90
- continue
91
- fi
92
- app="$(find "$dd/Build/Products" -maxdepth 2 -name '*.app' -path '*iphonesimulator*' | head -1)"
93
- [[ -n "$app" ]] || { row "$name" build "$signals" - - - - - "built but no simulator .app (macOS-only scheme?)"; continue; }
94
- bundle="$(/usr/libexec/PlistBuddy -c 'Print CFBundleIdentifier' "$app/Info.plist" 2>/dev/null || echo "?")"
95
-
96
- # 4. install
97
- xcrun simctl install "$UDID" "$app" >>"$log" 2>&1 \
98
- || { row "$name" install "$signals" "$bundle" - - - - "simctl install failed"; continue; }
99
-
100
- # 5. explore + report
101
- "$ROOT/scripts/quick-capture.sh" explore "$bundle" --actions "$ACTIONS" --timeout 420 >>"$log" 2>&1
102
- cap="$(ls -td "$ROOT"/captures/*/ 2>/dev/null | head -1)"
103
- if [[ ! -f "$cap/ocqa-markers.txt" ]]; then
104
- row "$name" explore "$signals" "$bundle" - - - - "exploration produced no markers"
105
- continue
106
- fi
107
- json="$OUT/$name-report.json"
108
- node "$ROOT/mcp-server/src/ci-report.js" --markers "$cap/ocqa-markers.txt" --json-out "$json" >>"$log" 2>&1 || true
109
- read -r screens acted findings verdict <<< "$(python3 -c "
110
- import json,sys
111
- try:
112
- r=json.load(open('$json'))
113
- print(r.get('screensExplored','?'), r.get('actionsPerformed','?'), r.get('findingCounts',{}).get('total','?'), r.get('verdict','?'))
114
- except Exception: print('? ? ? ?')")"
115
- row "$name" explored "$signals" "$bundle" "$screens" "$acted" "$findings" "$verdict" "capture: $(basename "$cap")"
116
- echo " ✅ $verdict — $screens screens"
117
- done 3< "$URLS_FILE"
118
-
119
- echo ""
120
- echo "═══ scoreboard: $BOARD ═══"
121
- column -t -s $'\t' "$BOARD"
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env bash
2
- # AutoTap penetration / coverage eval — the "real-app scoreboard".
3
- #
4
- # Unlike validation-matrix.sh (which asserts *expected findings* against DemoApp, i.e. accuracy on a
5
- # known fixture), this measures how far AutoTap actually PENETRATES an arbitrary app: did it get past
6
- # launch, did it hit a login wall, how many distinct screens it reached, how much of its budget went
7
- # to productive exploration vs. stuck/recovery flailing, and what it found. There is no ground truth —
8
- # this is a diagnostic you point at real apps to see where they stall, so coverage work is measurable.
9
- #
10
- # Usage:
11
- # scripts/coverage-eval.sh [bundleId ...] # defaults to the local corpus apps
12
- # scripts/coverage-eval.sh com.acme.app # a real app (must be installed on the booted sim)
13
- # ACTIONS=120 scripts/coverage-eval.sh com.acme.app
14
- # VISION_ESCALATION=1 ANTHROPIC_API_KEY=... scripts/coverage-eval.sh com.acme.app
15
- # # opt-in: when the a11y tree goes blank or exploration gets stuck, a sidecar
16
- # # (vision_escalation_responder.py) answers the harness's OCQA_VISION_QUERY with a
17
- # # model-chosen next move — same channel the desktop app serves.
18
- #
19
- # Prereqs: a booted simulator, the harness built (scripts/deploy-and-build.sh --harness), and each
20
- # app installed on the sim. Writes a JSON report to captures/coverage-eval-<timestamp>.json.
21
- #
22
- # NOTE: auth sessions stored in the keychain (Firebase et al) SURVIVE app reinstall — a "fresh"
23
- # run may silently resume the previous account. For a genuinely fresh login:
24
- # xcrun simctl keychain <udid> reset
25
- set -uo pipefail
26
-
27
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
28
- ACTIONS="${ACTIONS:-70}"
29
- TIMEOUT="${TIMEOUT:-600}"
30
- DEFAULT_APPS=(com.autotap.demoapp com.autotap.logindemo com.autotap.wizarddemo com.autotap.shopdemo com.autotap.restaurantdemo)
31
- APPS=("$@"); [ ${#APPS[@]} -eq 0 ] && APPS=("${DEFAULT_APPS[@]}")
32
-
33
- UDID="$(xcrun simctl list devices booted -j 2>/dev/null | python3 -c 'import sys,json; d=json.load(sys.stdin); print(next((x["udid"] for v in d["devices"].values() for x in v if x.get("state")=="Booted"), ""))')"
34
- [ -z "$UDID" ] && { echo "❌ No booted simulator. Boot one first."; exit 2; }
35
-
36
- XCTR="$(find "$HOME/Library/Developer/Xcode/DerivedData/OCQAHarness-"*/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
37
- [ -z "$XCTR" ] && XCTR="$(find /tmp/autotap-harness-derived/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
38
- [ -z "$XCTR" ] && XCTR="$(find /tmp/harness-build/Build/Products -name '*.xctestrun' 2>/dev/null | head -1)"
39
- [ -z "$XCTR" ] && { echo "❌ Harness not built. Run: scripts/deploy-and-build.sh --harness"; exit 2; }
40
-
41
- TS="$(date +%Y%m%d-%H%M%S)"
42
- REPORT="$ROOT/captures/coverage-eval-$TS.json"
43
- mkdir -p "$ROOT/captures"
44
- echo "AutoTap coverage eval — $ACTIONS actions/app, sim $UDID"
45
- echo "Apps: ${APPS[*]}"
46
- echo ""
47
-
48
- RESULTS_JSON="["
49
- FIRST=1
50
- for APP in "${APPS[@]}"; do
51
- if ! xcrun simctl get_app_container "$UDID" "$APP" >/dev/null 2>&1; then
52
- printf " %-32s ⏭ not installed — skipping\n" "$APP"
53
- continue
54
- fi
55
- CFG="/tmp/ocqa-coverage-$APP.json"
56
- LOG="/tmp/ocqa-coverage-$APP.log"
57
- RESPONDER_PID=""
58
- # Real test credentials (OCQA_TEST_EMAIL / OCQA_TEST_PASSWORD env) let the heuristic login
59
- # preamble get PAST a real auth wall — the single biggest coverage unlock on gated apps.
60
- CREDS_LINE=""
61
- [ -n "${OCQA_TEST_EMAIL:-}" ] && CREDS_LINE=",
62
- \"OCQA_TEST_EMAIL\": \"$OCQA_TEST_EMAIL\", \"OCQA_TEST_PASSWORD\": \"${OCQA_TEST_PASSWORD:-}\""
63
- if [ "${VISION_ESCALATION:-0}" = "1" ] && [ -n "${ANTHROPIC_API_KEY:-}" ]; then
64
- VRESP="/tmp/ocqa-vision-response-eval-$APP.json"
65
- VDIR="/tmp/ocqa-vision-eval-$APP"; mkdir -p "$VDIR"
66
- cat > "$CFG" <<JSON
67
- { "OCQA_BUNDLE_ID": "$APP", "OCQA_MAX_ACTIONS": "$ACTIONS", "OCQA_TIMEOUT_SECONDS": "$TIMEOUT",
68
- "OCQA_VISION_ESCALATION": "1", "OCQA_VISION_RESPONSE_PATH": "$VRESP",
69
- "OCQA_VISION_IMAGE_DIR": "$VDIR", "OCQA_VISION_BUDGET": "4", "OCQA_VISION_WAIT_TIMEOUT": "60"$CREDS_LINE }
70
- JSON
71
- : > "$LOG"
72
- python3 "$ROOT/scripts/vision_escalation_responder.py" "$LOG" "$VRESP" > "/tmp/ocqa-vision-responder-$APP.log" 2>&1 &
73
- RESPONDER_PID=$!
74
- else
75
- cat > "$CFG" <<JSON
76
- { "OCQA_BUNDLE_ID": "$APP", "OCQA_MAX_ACTIONS": "$ACTIONS", "OCQA_TIMEOUT_SECONDS": "$TIMEOUT"$CREDS_LINE }
77
- JSON
78
- fi
79
- TEST_RUNNER_OCQA_CONFIG_PATH="$CFG" xcodebuild test-without-building \
80
- -xctestrun "$XCTR" -destination "platform=iOS Simulator,id=$UDID" \
81
- -only-testing:"OCQAHarnessUITests/ExplorerTests/testAutonomousExploration" > "$LOG" 2>&1
82
- if [ -n "$RESPONDER_PID" ]; then kill "$RESPONDER_PID" 2>/dev/null; wait "$RESPONDER_PID" 2>/dev/null; fi
83
-
84
- ROW="$(python3 "$ROOT/scripts/coverage_eval_parse.py" "$LOG" "$APP")"
85
- echo "$ROW" | python3 -c 'import sys,json; d=json.load(sys.stdin); ca=d.get("crash_action") or {}; print(" %-32s screens=%-3s launch=%-3s login_wall=%-3s stuck=%-4s findings=%s%s" % (d["app"], d["screens"], "ok" if d["launch_ok"] else "NO", "YES" if d["login_wall"] else "no", d["stuck_ratio"], d["findings_total"], (" ⚠️ CRASHED on %s %s" % (ca.get("type",""), ca.get("screen","")) if d.get("crashed") else "")))'
86
- [ $FIRST -eq 0 ] && RESULTS_JSON+=","
87
- RESULTS_JSON+="$ROW"; FIRST=0
88
- done
89
- RESULTS_JSON+="]"
90
- echo "$RESULTS_JSON" | python3 -m json.tool > "$REPORT" 2>/dev/null || echo "$RESULTS_JSON" > "$REPORT"
91
- echo ""
92
- echo "Report: $REPORT"