agentsclimarketplace

E2e route

Skill Paretofilm/superpowers-gstack/skills/e2e-route

How to use Superpowers and GStack together with Claude Code. Workflow manual, routing plugin, and CLAUDE.md generator. Use both frameworks — each in the phase where it excels.

Install
npx -y skills add Paretofilm/superpowers-gstack --skill e2e-route

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

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 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.

What its author says it does

Copied from the file, not written here

Pure dispatcher: picks the right E2E executor for a Swift test request from context (platform × intent × verification kind) and hands off. Routes to the scaffold/MCP-sim/QA/design-review skills.

SKILL.md

10.8 KB, as published. Nobody here has run it

e2e-route

A pure dispatcher for Swift E2E testing. Given a test request it reads context deterministically, chooses the right E2E executor, and dispatches — it does not own execution. It is the thin routing layer above the scaffold skills and the MCP-live tools, analogous to how pitfall-verification orchestrates lenses without reimplementing them.

Invoke with: /superpowers-gstack:e2e-route

Phase 0 — Self-check

CheckDetect viaRefuse-message
Swift project*.xcodeproj or Package.swift in cwd"Not a Swift project. /e2e-route needs a Swift app to route tests for."
Detectable targeta scheme/target is discoverable (see Platform below)If none: ask the user once which scheme to target; else refuse "No scheme/target detected — cannot route."

Routing inputs (read in order)

1. Intent — exploratory (MCP-live) vs committed (XCUITest)

  • CI forces committed. Detect CI by running Bash:
    [ -n "${CI:-}${GITHUB_ACTIONS:-}" ] && echo CI
    
    If set, force committed — a CI context cannot drive a live MCP simulator session.
  • Else infer from the user's verbs:
    • utforsk / sjekk / dogfood / trykk gjennom / explore / smoke / "press through the flow" → MCP-live
    • må aldri knekke / regresjon / regression / CI / "lock this down" → committed
  • Ambiguous (no verb signal, not in CI) → ask the user once: "Exploratory live run, or committed regression test?"

2. Platform — iOS vs macOS

Detect via mcp__XcodeBuildMCP__show_build_settings / list_schemes (read SDKROOT / SUPPORTED_PLATFORMS); if MCP unavailable, fall back to grep -E 'SDKROOT|SUPPORTED_PLATFORMS' *.xcodeproj/project.pbxproj or read .gstack/track. Ask only if undetectable.

Multiplatform tiebreak — REQUIRED (the user's premise is "both platforms"). If the detected target supports BOTH iOS and macOS (SUPPORTED_PLATFORMS lists both iphoneos and macosx, or .gstack/track = both, or two schemes one per platform), the platform is NOT uniquely determined. Resolve in order:

  • (a) If the test request names a platform ("test the iPhone flow", "the macOS menu bar") → use it.
  • (b) Else ask the user once: "This app targets both iOS and macOS — route this test to iOS, macOS, or both?"
  • (c) both → emit two decision blocks (one iOS, one macOS), each routing to its platform's executor. Run them SEQUENTIALLY. The scaffolds handle coexistence via their shared TARGET_DIR convention: on a multiplatform target each uses a platform-suffixed test directory — <App>iOSUITests/ for /ios-e2e-scaffold, <App>macOSUITests/ for /macos-e2e-scaffold — and each scaffold's "already scaffolded" Phase-0 check globs *UITests/ name-agnostically but EXCLUDES the sibling platform's suffixed directory (*macOSUITests/ ignored by ios, *iOSUITests/ ignored by macos). So whichever runs second is not blocked by the first's files, and the two never collide on the same directory or the same xcodegen.yml/project.pbxproj UI-test target. Each decision block's "Next action" must name its platform-specific target dir.

3. Verification kind (optional refinement)

Functional / accessibility-assertion vs visual. A request about layout, spacing, colour, dark mode, or "does it look right" → the visual-regression row.

Routing table (the oracle)

IntentPlatformExecutor
Committed regressionmacOS/macos-e2e-scaffold + its xcresult runner
Committed regressioniOS/ios-e2e-scaffold
Exploratory / livemacOSXcodeBuildMCP UI-automation (snapshot_ui → tap → screenshot)
Exploratory / liveiOSios-simulator MCP (ui_find_element / ui_tap) or /ios-qa
Visual regressioniOSscreenshot/vision diff + /ios-design-review
Visual regressionmacOSscreenshot/vision diff + /design-review (generic designer's-eye QA — no macOS-specific reviewer exists)
Visual exploration (Tier-2 escalation)iOS/iPadOS/superpowers-gstack:ios-visual-explore — Gemini computer-use drives the app visually. Route here when the accessibility tree is insufficient (visual landmarks, layout regressions XCUITest can't assert, open-ended "find visual issues" missions). Not a first resort; paid Gemini API per run.

Fallback

Degrade to the MCP-live row for the platform only when the chosen scaffold's Phase 0 actually refuses — i.e. one of the scaffold's three refuse-conditions fires:

  1. not a Swift project, or
  2. no SwiftUI app for the routed platform detected — no SwiftUI scene (e.g. a UIKit-/AppKit-only app) or no platform-discriminating signal (e.g. a pure-iOS app routed to /macos-e2e-scaffold, or vice versa), or
  3. a UI-test target already exists.

Emit an explicit note naming the unmet precondition. No false promise; always a way forward.

SPM-only is NOT a fallback trigger. The scaffold skills accept Package.swift projects and proceed — they generate files under Tests/<TARGET_DIR>/ (<App>UITests, platform-suffixed on multiplatform targets) with a "SwiftPM can't host a UI-test bundle; add an .xcodeproj" warning. So for SPM-only iOS/macOS apps the dispatcher still routes to the scaffold skill; it does NOT degrade to MCP-live.

Output — routing-decision block

Emit one block per resolved platform — normally exactly one; two when the multiplatform tiebreak resolved to both (one iOS block + one macOS block) — then stop. Do not build/tap/assert; hand control back after emitting.

## /e2e-route decision
Detected: platform=<iOS|macOS>, intent=<committed|exploratory|visual>, source=<scheme|.gstack/track|asked>
Chosen executor: <skill or MCP sequence>
Why: <one line tying context → routing cell>
Next action: <exact /skill to invoke OR exact MCP call sequence>

What this skill is NOT

  • Not an executor. It does not build, tap, assert, or run tests itself — it names the executor and the exact next action, then hands off.
  • Not a scaffolder. It does not modify app files or generate test stubs — the scaffold skills (/ios-e2e-scaffold, /macos-e2e-scaffold) do that.
  • Not a QA-report writer. Use /ios-qa / /qa for live QA reports.
  • Not auto-hooked. Manual /e2e-route + CLAUDE.md routing only — no PostToolUse/UserPromptSubmit hook (avoids hijacking existing /qa / /ios-qa routing).

Shared foundation

Both executors locate controls via the accessibility tree the same way, using the <ViewName>_<ControlType>_<Purpose> identifier convention (snake_case) owned by the scaffold skillse2e-route points at it but does not own or apply identifiers. This is why the routing layer stays thin: element-lookup is identical whether the executor is ui_find_element("PlanListView_Button_GeneratePlan") (MCP-live) or app.buttons["PlanListView_Button_GeneratePlan"].tap() (XCUITest).

Simulator readiness ladder (MCP-live only)

When the routed executor is MCP-live (ios-simulator / XcodeBuildMCP / raw idb+simctl), readiness is not a single boolean. "Booted" exists at three levels that lag each other unpredictably — sometimes by minutes on a cold iOS 26 boot:

LevelSignalReality
1xcrun simctl list devices booted shows BootedCoreSimulator state — earliest, a diagnostic not a gate
2xcrun simctl bootstatus <udid> -b returnslaunchd/system services — neither sufficient nor necessary for automation; a diagnostic not a gate
3describe-all / snapshot_ui returns a non-degenerate treeSpringBoard rendered, AX bridge live — the only readiness gate

These are not a clean monotonic ladder. In the 2026-06-27 incident bootstatus -b (level 2) was still blocking after the UI (level 3) was already up. Treat level 3 as the only readiness gate; levels 1–2 are diagnostics you read, never conditions you wait on.

Rules — these prevent stranded passive waits:

  1. Poll the precondition you actually need (level 3), not a proxy. A device that reports Booted can still show a black screen with an empty AX tree. Gate the first probe on a non-degenerate tree, not on Booted.
  2. Never block on bootstatus -b as your wakeup signal. It can block far longer than the device takes to become usable — or indefinitely, if its boot-completion condition is never met — so it always needs an external timeout. Instead run a bounded run_in_background until-loop that exits the moment the level-3 precondition is true (one notification, within seconds):
    # Ready ≠ "Booted". A cold-boot AX tree can contain elements whose frames are
    # all null/0 (degenerate) — so gate on REAL geometry, not just on nodes existing.
    # REPLACE the ready() body with a check against YOUR tool's actual output (an
    # element with a NON-ZERO frame width — a label alone does not prove geometry).
    # Confirm field names once against a real dump. Note: idb roles are bare ("Button",
    # "Application"), NOT AX-prefixed — do not grep for "AXButton". Until you replace it,
    # ready() fails loudly, so the loop can never falsely report success.
    ready() {
      echo "readiness predicate not implemented — replace ready()" >&2
      return 1
    }
    start=$SECONDS
    until ready; do
      if [ $((SECONDS - start)) -gt 120 ]; then
        echo "TIMEOUT: AX tree still degenerate" >&2
        exit 1   # fail loudly — a wrapping background task must NOT notify "ready"
      fi
      sleep 1
    done
    
  3. Always pair "I'll be notified when the task finishes" with a fallback wakeup. The harness re-invokes on task completion; a hung task strands you forever. Set a ScheduleWakeup fallback (or a bounded Monitor timeout) so a hang can't cost 10 idle minutes. If your only plan is "I'll be notified automatically," you have no plan for the notification not arriving.
  4. Never emit a bare "waiting." Either you are actively polling (a background until-loop that exits) or you hand control back. Idle-waiting on an opaque blocking command is the anti-pattern that wasted real time on a Fase-2 iPad spike (2026-06-27).

Relationship to other skills

SkillLayerAsks
e2e-routeroutingWhich executor for this test?
ios-e2e-scaffold / macos-e2e-scaffoldprojectIs this E2E-tested?
ios-qaliveDoes the running app behave?
ios-design-reviewvisualDoes it look right on device?
pitfall-verificationartifactWill this work?

e2e-route sits above the project-layer scaffold skills and the MCP-live tools. It decides the executor; the executor decides how.

Keep looking

Skills are one crate of 328,083. 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.