Skip to content

launchApp defaults to REINSTALL (clearState=true) — silently wipes app data & resets permissions, contradicting its "as if a user tapped the icon" description #185

Description

@basementbot

Summary

launchApp's default launch mode is REINSTALL, which maps to clearState = true — so calling launchApp appId=<pkg> with no explicit launchMode silently wipes the app's local data (and resets its runtime permissions) before launching. This logs the user out and deletes downloads/offline content/settings.

This directly contradicts the tool's own one-line description — "Open an app on the device as if a user tapped on its icon in the launcher." Tapping a launcher icon never clears app state. The mode that actually matches that description is RESUME, not the current default.

A destructive, state-wiping operation should never be the default for a tool described as "just open the app," especially one driven by an LLM agent that will call it to simply bring an app to the foreground.

What happened (concrete)

During a benign task — opening a series of already-installed, signed-in third-party apps just to read their current screen — each was launched with launchApp appId=<pkg> and no launchMode argument. Every app came up in a fresh, logged-out onboarding state. Root cause: the default REINSTALL cleared each app's data. Several real apps were logged out and their local state (downloads, settings) destroyed before the behavior was noticed. Nothing in the call site or the tool's description hinted that "open the app" would clear it.

Root cause

trailblaze-common/src/jvmAndAndroid/kotlin/xyz/block/trailblaze/toolcalls/commands/LaunchAppTrailblazeTool.kt

  • Line 32 — the default: val launchMode: LaunchMode = LaunchMode.REINSTALL
  • Lines 71–76REINSTALL -> clearState = true (RESUME and FORCE_RESTART are both clearState = false)
  • Line 105fromString(...) also falls back to REINSTALL for unknown/empty input, so a malformed mode string ALSO wipes state
  • Lines 18–19 — class @LLMDescription: "Open an app on the device as if a user tapped on its icon in the launcher." (describes RESUME behavior, not REINSTALL)

clearState = true reaches Maestro's clearAppState(appId), whose own comment notes "Android's clear command also resets permissions" — so the blast radius is data wipe plus permission reset.

Why this is bad

  • Least-surprise violation: the default contradicts the documented behavior. "Open the app as if tapping its icon" should be non-destructive.
  • Silent + irreversible: no confirmation, no warning; cleared data/logins can't be recovered.
  • High blast radius for agents: an autonomous agent calling launchApp to inspect an app will unexpectedly nuke that app's state. On a real or shared device this means logging people out of streaming/shopping/travel/etc. apps and wiping their downloads and permissions.
  • fromString fallback compounds it: an unrecognized launchMode value silently degrades to the most destructive option instead of the safest.

Reproduction

  1. Install any app and sign in (so it has real local state).
  2. Run: trailblaze tool launchApp appId=com.example.app (no launchMode).
  3. Observe the app opens logged out / first-run onboarding — its data was cleared.
  4. Contrast with launchApp appId=com.example.app launchMode=RESUME, which opens it normally with state intact.

Proposed fix (secret recipe)

Make the non-destructive mode the default, and make the destructive one explicit + clearly labeled. In LaunchAppTrailblazeTool.kt:

  1. Line 32 — change the default:
    val launchMode: LaunchMode = LaunchMode.RESUME,
  2. Line 105 — change the fromString fallback so unknown input degrades to safe, not destructive:
    ?: RESUME
  3. Lines 24–31 — update the @LLMDescription so the default is RESUME and REINSTALL is clearly flagged as destructive, e.g.:
    - "RESUME" (Default) opens the app as if tapped from the launcher; keeps existing state/login.
    - "FORCE_RESTART" stops then relaunches the app without clearing state.
    - "REINSTALL" DESTRUCTIVE: clears all app data AND resets permissions before launching (logs the user out; removes downloads/settings). Only use when a true clean-install state is explicitly wanted.
    
    And align the class description (lines 18–19) with the fact that the default no longer clears state.

Optional hardening: since clearState also resets permissions, consider requiring an explicit opt-in flag (or a confirmation) for the wipe path rather than folding it into a "launch" verb at all.

Environment

  • Trailblaze source build (v20260630...), driving a physical Android device.
  • Behavior is in shared/common code (LaunchAppTrailblazeTool.kt), so it's platform-agnostic for the default-mode issue.

(No PR — filing as a report with the exact change inline.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions