agentsclimarketplace

Headspin explore bugs

Skill krzemienski/headspin-control/skills/headspin-explore-bugs

Systematically explore an app on a connected HeadSpin device to discover bugs and crashes. Use for "explore app for bugs", "crawl the app on headspin", "find crashes", "automated exploratory QA on a device", "monkey-test this app", or "walk every screen and flag anomalies". Runs a bounded BFS crawl over the live control session — capture page source + screenshot, inventory interactive elements, act, detect anomalies (crash/session death, error text, stuck screen, app-not-foreground), and write a timestamped evidence bundle per anomaly. Wraps the run in a HeadSpin capture session so a video exists. Feeds headspin-bug-report.From its SKILL.md

Install
npx -y skills add krzemienski/headspin-control --skill headspin-explore-bugs

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.0 KB, as published. Nobody here has run it

headspin-explore-bugs

When to use

  • User runs /headspin:explore or asks to "crawl the app for bugs / crashes".
  • A device is already connected and you want automated exploratory QA with evidence.
  • A test needs to walk every reachable screen and flag anomalies for triage.

Scope

  • In scope: the BFS crawl loop, anomaly detection, evidence-bundle capture, wrapping the run in a capture session so a video exists, bounded exploration (max steps, max depth, visited-screen dedup).
  • Out of scope: logging in (headspin-login), locking the device (headspin-session-manager), opening the control transport (headspin-connection-manager), the tap/press/screenshot primitives (headspin-control-ios for the iOS CONTROL_TOUCH_PATHS channel; the Android socket.io input.* primitives), and writing the final report (headspin-bug-report).
  • Roku is doc-only / not HAR-verified in this environment — no Roku device appears in any captured devicelist, so the crawl targets Android and iOS.

Prerequisites

  1. headspin-login has run; $CLAUDE_PLUGIN_OPTION_API_HOST and $CLAUDE_PLUGIN_OPTION_API_TOKEN are set (token in the OS keychain).
  2. headspin-session-manager holds a live device lock (capture sessions 403 with "Device must be locked before capture." otherwise).
  3. headspin-connection-manager has an open control socket; the device's device_address (<device_id>@<hostname>) is in /tmp/headspin-control/selected-device.txt and device_id is known.

Security policy

  • Token from the plugin config ONLY — read inline as $CLAUDE_PLUGIN_OPTION_API_TOKEN (keychain-backed via sensitive: true). Never hardcode, print, or write it into any evidence file. REST calls send it as a Authorization: Bearer header only.
  • Evidence bundles carry screenshots and page source of the app under test. They must NOT contain the API token or any token-in-path driver_url (/v0/{TOKEN}/wd/hub) — redact before writing.

Exploration loop (BFS)

headspin-login → headspin-session-manager (lock) → headspin-connection-manager (socket)
start capture session (POST /v0/sessions {session_type:"capture", device_address}) ─┐
                                                                                     │ video of the whole run
  queue = [ current screen ]                                                         ▼
  while queue and steps < max_steps and depth < max_depth:
     page_source = control dump (iOS: headspin-control-ios `dump`; Android: Appium/uiautomator2 page source)
     screen_hash = sha1(normalized page_source)     # dedup key — makes the crawl terminate
     if screen_hash in visited: continue
     visited.add(screen_hash)
     screenshot  = control screenshot
     if detect_anomaly(...): capture_evidence_bundle(); continue
     act on next unvisited interactive element (headspin-control-*), enqueue new screen
stop capture session (PATCH /v0/sessions/{id} {active:false}) → video at /v0/sessions/{id}.mp4

Anomaly detection (five signals)

SignalHow detectedMeaning
Crash / session deathcontrol command raises / session invalidapp or session died
Error text on screenerror keywords in page source OR POST /v0/video/{device_id}/ocrerror dialog / exception surfaced
App-UI / a11y defecta11y_defects.py predicates over the live page_source (see below)real accessibility / usability bug in the app under test
Stuck screenN consecutive identical page-source hashes after an actionfrozen / dead-end UI
App not foregroundcurrent package/bundle ≠ app under testapp was backgrounded / kicked out

App-UI / accessibility defect analyzer (scripts/a11y_defects.py)

_detect runs a package-scoped, subtree-aware analyzer over the live Appium page_source (the uiautomator2 accessibility tree) at every screen. It surfaces defects in the application under test, not the harness:

PredicateDefectSeverity
A1Crash / ANR dialog on top of the app (isn't responding / has stopped)critical
A3Unlabeled clickable control — no text, no content-desc anywhere in its subtreemedium
A4Unlabeled clickable ImageView/ImageButton leaf (icon-only, no a11y name)high
A5Undersized touch target — an on-screen control below 48 dp (Material / WCAG 2.5.8)low

False-positive guards: every predicate skips nodes whose package ≠ the target app (OS chrome is never flagged); A3 is subtree-aware (a labelled child clears the parent); A3 and A5 require a real control (a resource-id, a content-desc, or a widget class) so anonymous layout slivers are ignored; A5 requires the node fully on-screen with non-zero area. Proven live on com.android.settings (Pixel 6): found a genuine 24.5 dp Dismiss button on the Security banner, with 2 edge-sliver false positives correctly suppressed.

Error keywords (case-insensitive): error, exception, crash, not responding, something went wrong, unfortunately, force close, anr, fatal.

The OCR endpoint (POST /v0/video/{device_id}/ocr) reads the LIVE screen text left-to-right, top-to-bottom — useful when error text is rendered as an image or a native alert that never appears in the control page source.

Evidence bundle (per anomaly)

Written to ./headspin-exploration/{run-id}/anomaly-{n}/:

screenshot.png    # control screenshot at the moment of the anomaly
page_source.xml   # UI hierarchy dump
ocr.txt           # POST /v0/video/{device_id}/ocr  (live-screen text)
log_tail.txt      # syslog (iOS) / logcat (Android) tail
action_log.json   # verbatim ordered steps that led here (repro trail)
meta.json         # timestamp, device_address, device_id, session_id, signal, screen_hash

meta.json.session_id links to the HeadSpin session video: https://ui.headspin.io/sessions/{session_id} and /v0/sessions/{session_id}.mp4.

Bounds (non-negotiable)

  • max_steps (default 200) — total actions before the crawl stops.
  • max_depth (default 25) — BFS depth cap so the crawl does not wander forever.
  • Visited-screen dedup by normalized page-source SHA1 — never re-explore a screen already seen. This is what makes the crawl terminate.
  • One capture session for the whole run; stop it in a finally block.

Quick start

AppExplorer drives an already-open control session (login + lock + socket established by the skills above); it does not open one itself. Drive it programmatically with the live driver:

import sys
sys.path.insert(0, f"{CLAUDE_PLUGIN_ROOT}/skills/headspin-explore-bugs/scripts")
from explore import AppExplorer

explorer = AppExplorer(driver, device_address, device_id,
                       out_dir="./headspin-exploration")
run = explorer.run(max_steps=150, max_depth=20)   # starts+stops the capture session
print(run["run_dir"])   # hand this to headspin-bug-report

device_address is <device_id>@<hostname> from /tmp/headspin-control/selected-device.txt. Running explore.py directly prints this usage and exits — it has no standalone auto-connect harness.

Evidence

  • OCR endpoint (live-screen text): plans/260702-headspin-skills/SYNTHESIS.md §8 (POST /v0/video/{device_id}/ocr) + headspin-docs/api-reference/.
  • Capture-session lifecycle (lock → POST /v0/sessions → timestamps → PATCH active:false → .mp4): plans/260702-headspin-skills/SYNTHESIS.md §9.
  • Websocket control surface the crawl drives: headspin-docs/ui-dev.headspin.io.har:177.

Related skills

  • headspin-login — token validation if OCR/session calls return 401/403 (call FIRST).
  • headspin-session-manager — locks the device; a capture session requires the lock.
  • headspin-connection-manager — opens the platform-correct control transport this skill drives (Android socket.io input.*, iOS :5002 CONTROL_TOUCH_PATHS).
  • headspin-control-ios — the iOS tap/press/dump/screenshot primitives. (Roku control is doc-only, not exercised in this environment.)
  • headspin-bug-report — turns this skill's evidence bundles into filed bugs.

Resources

  • scripts/explore.py — bounded BFS loop, anomaly detection, capture-session wrapping, and evidence-bundle writing. Stdlib REST; token read from $CLAUDE_PLUGIN_OPTION_API_TOKEN (env-only, never on disk).

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.