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–76 —
REINSTALL -> clearState = true (RESUME and FORCE_RESTART are both clearState = false)
- Line 105 —
fromString(...) 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
- Install any app and sign in (so it has real local state).
- Run:
trailblaze tool launchApp appId=com.example.app (no launchMode).
- Observe the app opens logged out / first-run onboarding — its data was cleared.
- 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:
- Line 32 — change the default:
val launchMode: LaunchMode = LaunchMode.RESUME,
- Line 105 — change the
fromString fallback so unknown input degrades to safe, not destructive:
- 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.)
Summary
launchApp's default launch mode isREINSTALL, which maps toclearState = true— so callinglaunchApp appId=<pkg>with no explicitlaunchModesilently 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 nolaunchModeargument. Every app came up in a fresh, logged-out onboarding state. Root cause: the defaultREINSTALLcleared 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.ktval launchMode: LaunchMode = LaunchMode.REINSTALLREINSTALL -> clearState = true(RESUME and FORCE_RESTART are bothclearState = false)fromString(...)also falls back toREINSTALLfor unknown/empty input, so a malformed mode string ALSO wipes state@LLMDescription: "Open an app on the device as if a user tapped on its icon in the launcher." (describes RESUME behavior, not REINSTALL)clearState = truereaches Maestro'sclearAppState(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
launchAppto 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.fromStringfallback compounds it: an unrecognizedlaunchModevalue silently degrades to the most destructive option instead of the safest.Reproduction
trailblaze tool launchApp appId=com.example.app(nolaunchMode).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:fromStringfallback so unknown input degrades to safe, not destructive:@LLMDescriptionso the default is RESUME and REINSTALL is clearly flagged as destructive, e.g.:Optional hardening: since
clearStatealso 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
v20260630...), driving a physical Android device.LaunchAppTrailblazeTool.kt), so it's platform-agnostic for the default-mode issue.(No PR — filing as a report with the exact change inline.)