Trailblaze Decision 031: App Target Configuration¶
Context¶
Trailblaze tests are scoped to a target application. Each target app has an identity, package IDs per platform, custom tools, version requirements, and app-specific settings.
Currently implemented via TrailblazeHostAppTarget in Kotlin — this works for built-in targets but requires Kotlin knowledge, compilation, and coupling to the Trailblaze codebase for external teams.
Decision¶
App targets are configured via YAML in trailblaze.yaml, with optional user-level defaults in ~/.trailblaze/app-targets.yaml.
# trailblaze.yaml (project root)
target: rideshare_driver
# App target definitions
targets:
rideshare_driver:
displayName: "Rideshare Driver"
appIds:
android: [com.example.driver, com.example.driver.debug]
ios: [com.example.RideshareDriver]
tools:
mcpServers: [driver-tools]
namespaces: [driver_, shared_]
exclude: [tap, scroll]
minVersion:
android: "2024.01.15"
ios: "2024.01.15"
Configuration loads in order: built-in targets → user-level (~/.trailblaze/) → project-level (./trailblaze.yaml), with later sources overriding earlier ones.
Custom driver factories and other code-based customizations still require Kotlin. YAML handles the declarative parts.
Status¶
Not yet implemented. TrailblazeHostAppTarget Kotlin classes remain the active mechanism. Decision 035 later introduced dynamic app targets via setAppTarget(), allowing runtime creation without YAML or Kotlin — which may partially supersede this approach.
How App Targets Filter Tools¶
When a user selects an app target, Trailblaze filters the available tools:
┌─────────────────────────────────────────────────────────────────────┐
│ User selects: target: rideshare_driver │
│ │
│ 1. Load app target config from trailblaze.yaml │
│ │
│ 2. Connect to MCP servers listed in tools.mcpServers │
│ → driver-tools server provides: driver_login, driver_accept, .. │
│ │
│ 3. Load tool registries from each server │
│ → resources/read("trailblaze://registry") │
│ │
│ 4. Filter tools by namespace (if configured) │
│ → Keep: driver_*, shared_* │
│ → Exclude: myapp_*, otherapp_* (not in namespace list) │
│ │
│ 5. Apply explicit exclusions │
│ → Remove: tap, scroll (excluded in config) │
│ │
│ 6. Result: Tools available to LLM for this test session │
│ → driver_login, driver_accept, block_mockServer, ... │
└─────────────────────────────────────────────────────────────────────┘
Relationship to MCP Tool Registry¶
The app target config and MCP tool registry serve complementary purposes:
| Concern | Where Defined | Purpose |
|---|---|---|
| Tool implementation | MCP server code | What the tool does |
| Tool metadata | MCP registry (resources/read) |
Platforms, groups, flags |
| Which tools for this app | App target config | Filtering for this test session |
Built-in vs Custom App Targets¶
Trailblaze ships with built-in app targets for common apps:
# Built-in (ships with Trailblaze)
targets:
none:
displayName: "None"
# No app-specific tools, just primitives
myapp:
displayName: "MyApp"
appIds:
android: [com.example.myapp]
ios: [com.example.myapp]
tools:
namespaces: [myapp_, shared_]
otherapp:
displayName: "OtherApp"
appIds:
android: [com.example.otherapp]
ios: [com.example.otherapp]
tools:
namespaces: [otherapp_, shared_]
Users can override or extend these in their trailblaze.yaml:
# User's trailblaze.yaml
targets:
myapp:
# Override built-in MyApp config
minVersion:
android: "2024.06.01" # Require newer version for this project
tools:
exclude:
- myapp_legacyLogin # Don't use deprecated tool
Configuration Loading Order¶
1. Built-in app targets (ships with Trailblaze)
↓ merged with
2. User-level config (~/.trailblaze/app-targets.yaml)
↓ merged with
3. Project-level config (./trailblaze.yaml)
↓
4. Final app target configuration
Later sources override earlier ones (same pattern as LLM config).
Migration from TrailblazeHostAppTarget¶
For built-in app targets, the existing TrailblazeHostAppTarget subclasses can be:
- Kept as-is — Code-based targets still work
- Exported to YAML — Generate YAML from Kotlin for external teams
- Gradually migrated — Move config to YAML, keep Kotlin for custom logic
// Kotlin targets can reference YAML config
class MyAppHostAppTarget : TrailblazeHostAppTarget(
id = "myapp",
displayName = "MyApp",
) {
// Custom logic that can't be expressed in YAML
override fun getCustomIosDriverFactory(deviceId, originalDriver): Any {
return MyAppIosDriver(originalDriver) // Custom driver wrapper
}
}
For custom driver factories and other code-based customizations, Kotlin remains necessary. YAML config handles the declarative parts.
Desktop App Integration¶
The Trailblaze Desktop App uses app target config to:
- Show target selector — Dropdown of available app targets
- Detect installed apps — Match
appIdsagainst device’s installed apps - Show version warnings — Compare installed version against
minVersion - Filter tool palette — Show only tools for selected target
Consequences¶
Positive:
- Declarative — App targets defined in YAML, not code
- External-friendly — No Kotlin required for basic app targets
- Project-scoped — Config lives with the test project, not Trailblaze
- Consistent — Same YAML pattern as LLM config
- Extensible — Easy to add new fields without code changes
Negative:
- Limited customization — Custom driver factories still need Kotlin
- Migration effort — Existing targets need conversion
- Validation complexity — YAML schema must be validated
- Documentation — Another config file to document