Skip to content

generate App Intents SSU training assets during bundling - #2

Draft
markvasiv wants to merge 1 commit into
masterfrom
app-intents-ssu-training
Draft

generate App Intents SSU training assets during bundling#2
markvasiv wants to merge 1 commit into
masterfrom
app-intents-ssu-training

Conversation

@markvasiv

@markvasiv markvasiv commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Generate App Intents SSU training assets during bundling

Problem

Apps built with rules_apple are invisible to Siri's App Intents natural-language matching. App Shortcut phrases ("Play something on ${applicationName}") are never recognized, and on iOS 26+ assistant schema requests (e.g. @AssistantIntent(schema: .media.playAudio)) are not routed to the app - Siri's planner reports the app as not supporting the intent and falls back to just launching it:

Play.Play@isSiriKitSupported: App <private> supports App Intent PlayAudioIntent: false, SiriKit INPlayMediaIntent: false

Root cause

Xcode runs two tools for App Intents, rules_apple only runs the first:

  1. appintentsmetadataprocessor - produces Metadata.appintents/extract.actionsdata.
  2. appintentsnltrainingprocessor - runs on the built product and produces the NL training assets: Metadata.appintents/root.ssu.yaml plus one compiled <locale>.lproj/nlu.appintents archive per localization.

Siri's utterance matching runs against the compiled nlu.appintents archives, so without them the metadata alone only feeds touch surfaces (Shortcuts app, Spotlight), not voice.

I found it by building Apple's sample project and inspecting produced archives:
https://developer.apple.com/documentation/appintents/integrating-your-music-app-with-apple-intelligence

Design

The tool must run against the assembled bundle - it reads the merged Info.plist and derives the locale set from the bundle's .lproj directories - so it can't be an ordinary analysis-time action over partial inputs. It runs in the existing post-assembly, pre-signing stage (where ipa_post_processor runs), so the generated assets are sealed into the code signature:

  • app_intents_metadata_bundle partial: accepts bundle_id and requests SSU training via a new partial output field (app_intents_ssu_training).
  • processor.bzl: collects the request (same pattern as signed_frameworks), builds the invocation with archive-relative paths, and threads it into both bundling modes:
    • Archive mode: new %ssu_training_command_lines% block in process_and_sign.sh.template, after the post-processor, before signing.
    • Tree-artifact mode: new ssu_training_commands control field executed by bundletool_experimental.py between post-processing and signing. It runs with the action's environment - the existing signing command lines run with env={}, which can't host xcrun.
  • When the bundle has no Metadata.appintents or the toolchain lacks appintentsnltrainingprocessor (older Xcode), it does nothing. The tool can print error: Could not archive SSU artifacts while exiting 0, so output is scanned for error: like the metadata processor action already does.

For now, it's opt-in via --features=apple.app_intents_ssu_training and only supported in ios_application.

Follow-ups

  • Pass localized AppShortcuts.strings via --source-file (Xcode's LM_STRINGS_FILE_PATH_LIST) for localized phrase training.
  • Pass --deployment-postprocessing for release archives.
  • Extensions and the other platform rules (macOS/tvOS/watchOS/visionOS) don't request training yet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant