Skip to main content

Agents wander up the hill to ask the card what it can do. It tells them, in App Intents.

A little white robot leans over a phone, a tablet and a laptop on a garden bench, each showing the same green card with a switch, while Ouro smiles beside them.

Ouro AX for Apple

Siri reads your app like a manual. Write a good one.

On Apple's platforms, an agent meets your app through App Intents. Siri picks what to call from titles and descriptions, finds your things by name, and follows your skills. Ouro AX for Apple is the checklist for getting that right, and an X-ray that checks it for you.

The door

What Siri sees when it looks at your app.

  • App Intents

    The things your app can do. Siri reads their titles and descriptions like a menu.

  • App entities

    The things your app knows about, like a recipe or a document, findable by name.

  • App Shortcuts

    Phrases that work before anyone has set anything up.

  • App skills

    A short how-to, in Markdown, that teaches Siri your app's way of doing a longer job.

How it knows

It knows how it knows.

The X-ray reads a built app from outside, never importing its code, and checks it against thirty claims. It's honest about its limits: what only Siri can answer stays open until Siri is asked.

  1. App Intents metadataReads what the built app declares, right on your Mac.
  2. Skills linterChecks the skills bundle's shape, names and words.
  3. AppIntentsTestingRuns the app's queries, Spotlight index and errors in a test, without Siri.
  4. SiriEvaluationsAsks Siri itself, again and again, on a device running the Siri beta.
  5. A personSome things only a human can judge, so the report says which and leaves them open.

Real reports

Spoonjoy and Ouro MD, X‑rayed, before and after.

Spoonjoy went from 5 to 12 of 29 claims holding, and Ouro MD from 8 of 29 to 14 of 30. Plenty of what's left is waiting for Siri's beta or a person rather than failing. Flip between before and after, and open any claim for its reason and its fix.

App
Report

12 of 29 claims hold

Spoonjoy on iPhone and iPad, with the AX fixes, Siri tools and the cook-along skill (#202 to #204). 59 intents, 10 entities, 10 App Shortcuts, 1 app skill. Under the hood, the claims rest on smaller checks: 289 pass, 13 fail, 15 not run.

  • App Intents metadata: ran
  • Skills linter: ran
  • AppIntentsTesting: did not run
  • SiriEvaluations: did not run
  • A person: did not run
Show
  • Holds: Every intent has a title a person would say, not its type name.

    intent.title · should · App Intents metadata · 59 pass, 0 fail, 0 skipped

    Why: Siri tells intents apart by their titles, and Shortcuts lists them by title.

  • Holds: Every intent has a description that says what it does, beyond its title.failed before

    intent.description · should · App Intents metadata · 59 pass, 0 fail, 0 skipped

    Why: Siri decides which intent fits a request from its title and description together.

  • Holds: Every parameter has a title.

    intent.parameter-title · should · App Intents metadata · 69 pass, 0 fail, 0 skipped

    Why: Siri and Shortcuts show the title when they ask the person for a value.

  • Holds: Every parameter has a description that says what value it wants.failed before

    intent.parameter-description · should · App Intents metadata · 69 pass, 0 fail, 0 skipped

    Why: Siri fills parameters from what the person said, guided by each parameter's description.

  • Not checked: The app's main actions are tool intents (@AppIntent(.tool)) or adopt a Siri schema, so Siri AI can call them.

    intent.tool · should · App Intents metadata · 0 pass, 0 fail, 1 skipped

    Why: With the Siri AI preview, Siri calls only the custom intents an app marks as tools.

    Fix: Mark the intents people would ask Siri for with @AppIntent(.tool), building with the 27.3 SDK behind an availability check.

    metadata from Xcode tools 27B52 cannot mark tool intents, and no intent adopts a schema; build with the 27.3 SDK to check

  • Holds: No intent is both a tool and an adopter of a schema that already reaches Siri.

    intent.tool-not-on-schema · should · App Intents metadata · 1 pass, 0 fail, 0 skipped

    Why: A schema in a domain Siri supports already makes the action available; marking it a tool as well is the misuse Apple's guide names.

  • Holds: The app offers App Shortcuts for its main actions, so Siri and Spotlight find them with no setup.

    intent.shortcuts · should · App Intents metadata · 1 pass, 0 fail, 0 skipped

    Why: App Shortcuts are how an app's actions reach Siri and Spotlight before the person builds anything.

  • Not checked: When an intent is refused, its error is one sentence that says what to do next.

    intent.error-names-fix · should · AppIntentsTesting · 0 pass, 0 fail, 1 skipped

    Why: Siri reads the error to the person; an error that names the fix lets the person, or Siri, recover in one step.

    Fix: Throw an error that conforms to CustomLocalizedStringResourceConvertible and says what to do: "There's no note X. Pick one from your notes, then try again."

    needs a behaviour run: AppIntentsTesting against the app on a device or simulator whose OS matches the Xcode (ouro-ax-xray plan, then the XrayProbe template)

  • Not checked: An intent whose result other people see asks the person to confirm before it acts.

    intent.confirms-what-others-see · should · A person · 0 pass, 0 fail, 1 skipped

    Why: Siri can run an intent from a misheard request; a confirmation stops a message or a share from going out by mistake.

    Fix: Call requestConfirmation before the intent sends, shares or posts anything other people see.

    needs a person: run these through Siri and Shortcuts and check each asks first, if others see or it cannot be undone: Delete Cook Log, Delete Cookbook, Delete Recipe, Disconnect Account Connection, Remove Profile Photo, Remove Recipe from Cookbook, Remove Shopping Item, Revoke Agent Access Key and 4 more

  • Not checked: Each intent appears under the app in Shortcuts, and its parameter summary reads like a sentence.

    intent.reads-in-shortcuts · should · A person · 0 pass, 0 fail, 1 skipped

    Why: People read and edit the summary; awkward phrasing cannot be found by a code-level test.

    Fix: Give the intent a parameterSummary that reads as a sentence, such as Summary("Open \(\.$note)").

    needs a person: find the app's 59 intents in the Shortcuts action library and read each summary

  • Not checked: Siri calls the right intent with the right parameters for direct, indirect and incomplete wording, and declines unexpected requests.

    intent.siri-calls · should · SiriEvaluations · 0 pass, 0 fail, 1 skipped

    Why: This is the outcome the metadata claims are for: Siri doing what the person asked.

    Fix: Read the failing transcripts and sharpen the intent's title, description, parameter descriptions or app skill.

    needs SiriEvaluations: the app's scenario set, run five times on a 27.3 beta OS on an Apple Intelligence device

  • Not checked: After an action, what Siri says names what happened.

    intent.siri-says-what-happened · should · SiriEvaluations · 0 pass, 0 fail, 1 skipped

    Why: The person hears the outcome, not only a chime; a reply that names the result is the spoken receipt.

    Fix: Return a result with a dialog or a value that names the outcome, such as .result(dialog: "Added milk to Groceries.").

    needs SiriEvaluations: the app's scenario set, run five times on a 27.3 beta OS on an Apple Intelligence device

  • Holds: Every entity type has a display name a person would say, not its type name.

    entity.type-name · should · App Intents metadata · 10 pass, 0 fail, 0 skipped

    Why: Siri and Shortcuts name the kind of thing with it ("Recipe", "Note").

  • Fails: An entity exposes what a person would ask about it as properties.

    entity.properties · should · App Intents metadata · 0 pass, 10 fail, 0 skipped

    Why: Siri matches requests to content through an entity's properties; an entity with none offers only its display text.

    Fix: Add @Property(title:) (with description: on the 27.3 SDK) for each fact a person would ask about: name, date, amount.

    • “Access Key” has no properties
    • “Account Connection” has no properties
    • “Capture Draft” has no properties
    • “Chef Profile” has no properties
    • “Cooking Step” has no properties
    • “Cookbook” has no properties
    • …and 4 more like it.
  • Not checked: Sensitive properties require authentication, and properties only the app needs are hidden from Siri.

    entity.private-properties · should · A person · 0 pass, 0 fail, 1 skipped

    Why: By default Siri and Shortcuts can show entity information without unlocking; financial or health data should not be.

    Fix: Pass authenticationPolicy: .requiresAuthentication to sensitive properties and visibility: .internal to app-only ones.

    needs a person: no entity exposes properties yet; review each one when it does

  • Holds: Siri can find every entity: it is indexed in Spotlight, or its query can list or search it.failed before

    entity.findable · should · App Intents metadata · 10 pass, 0 fail, 0 skipped

    Why: Siri finds entities through Spotlight, an enumerable query or dynamic options; an entity it cannot find, it cannot act on.

  • Not checked: An entity that says it is indexed is in the Spotlight index once the app has content.

    entity.indexed · should · AppIntentsTesting · 0 pass, 0 fail, 1 skipped

    Why: Spotlight is the door to personal context: Siri answers from what the app indexed.

    Fix: Call CSSearchableIndex.default().indexAppEntities(_:) when content is created or changes, and on first launch.

    needs a behaviour run: AppIntentsTesting against the app on a device or simulator whose OS matches the Xcode (ouro-ax-xray plan, then the XrayProbe template)

  • Not checked: An entity's identifier resolves back to the same entity.

    entity.ids-resolve · should · AppIntentsTesting · 0 pass, 0 fail, 1 skipped

    Why: Siri holds an entity by identifier between turns; one that does not resolve breaks a multi-turn request.

    Fix: Make the query's entities(for:) return every identifier its other methods hand out.

    needs a behaviour run: AppIntentsTesting against the app on a device or simulator whose OS matches the Xcode (ouro-ax-xray plan, then the XrayProbe template)

  • Fails: Every entity is Transferable.

    entity.transferable · should · App Intents metadata · 7 pass, 3 fail, 0 skipped

    Why: Siri moves content between apps ("email this recipe"), and Apple's testing guide treats a transferable representation as a requirement for every entity; one without it cannot go anywhere.

    Fix: Conform the entity to Transferable with at least one representation other apps read, such as plain text or a file.

    • “Access Key” is not Transferable
    • “Account Connection” is not Transferable
    • “Cooking Step” is not Transferable
  • Not checked: An exported entity can be read back.

    entity.transfer-round-trip · should · AppIntentsTesting · 0 pass, 0 fail, 1 skipped

    Why: Apple names a broken Transferable as the way multi-turn Siri requests and drag and drop silently fail.

    Fix: Give the entity an importing representation, or a ProxyRepresentation, that turns the exported data back into the entity.

    needs a behaviour run: AppIntentsTesting against the app on a device or simulator whose OS matches the Xcode (ouro-ax-xray plan, then the XrayProbe template)

  • Not checked: Each entity shows a title a person recognises, with a subtitle or image where it helps.

    entity.display-representation · should · A person · 0 pass, 0 fail, 1 skipped

    Why: The display representation is what the person sees in Siri, Shortcuts and Spotlight; code-level tests cannot read it.

    Fix: Return a DisplayRepresentation(title:subtitle:image:) built from the entity's own fields.

    needs a person: find each in Spotlight and Shortcuts and check its title, subtitle and image: Access Key, Account Connection, Capture Draft, Chef Profile, Cooking Step, Cookbook, Recipe, Shopping Item and 2 more

  • Not checked: Siri finds the entity the person means from their words.

    entity.siri-finds · should · SiriEvaluations · 0 pass, 0 fail, 1 skipped

    Why: Finding the right recipe or note is most of the work in a request about it.

    Fix: Index the entity's searchable text, describe its properties, and make its display title the name people use.

    needs SiriEvaluations: the app's scenario set, run five times on a 27.3 beta OS on an Apple Intelligence device

  • Not checked: Entities shown on screen are annotated, so "this" means what the person sees.

    annotation.onscreen · should · AppIntentsTesting · 0 pass, 0 fail, 1 skipped

    Why: Onscreen context lets a person say "add this to my list"; without an annotation Siri cannot know what "this" is.

    Fix: Add .appEntityIdentifier(EntityIdentifier(for: entity)) to each row or detail view that shows an entity, or set NSUserActivity.appEntityIdentifier for a single one.

    needs a behaviour run: AppIntentsTesting against the app on a device or simulator whose OS matches the Xcode (ouro-ax-xray plan, then the XrayProbe template)

  • Not checked: Siri resolves "this" to the entity on screen.

    annotation.siri-onscreen · should · SiriEvaluations · 0 pass, 0 fail, 1 skipped

    Why: The annotation is for this moment: the person points at something and asks.

    Fix: Annotate the views that show entities, and keep the selected one marked as selected.

    needs SiriEvaluations: the app's scenario set, run five times on a 27.3 beta OS on an Apple Intelligence device

  • Holds: A skills bundle is Skills.skillsbundle holding only Skills/<name>/SKILL.md, one bundle per target.unchecked before

    skill.bundle-shape · should · Skills linter · 1 pass, 0 fail, 0 skipped

    Why: Siri reads nothing else in the bundle, so anything else is dead weight or a sign of a misplaced file.

  • Holds: Each skill's name is lowercase letters, numbers and hyphens, the same in its folder and frontmatter, and unique across the app.unchecked before

    skill.names · should · Skills linter · 1 pass, 0 fail, 0 skipped

    Why: Two skills with one name leave the system to pick between them, and nothing says which it picks.

  • Holds: A skill's frontmatter holds only name and description.unchecked before

    skill.frontmatter · should · Skills linter · 1 pass, 0 fail, 0 skipped

    Why: App skills take a subset of the Agent Skills format; other fields are not read.

  • Holds: A skill's body is at most 10,000 characters and names tools informally, not by type.unchecked before

    skill.body · should · Skills linter · 1 pass, 0 fail, 0 skipped

    Why: Siri reads at most 10,000 characters, and knows tools by their titles, not their Swift names.

  • Not checked: Siri loads the skill for the requests it is written for.

    skill.loads · should · SiriEvaluations · 0 pass, 0 fail, 1 skipped

    Why: A skill Siri never loads is guidance nobody reads.

    Fix: Make the skill's description name the requests it handles, in the words people use.

    needs SiriEvaluations: the app's scenario set, run five times on a 27.3 beta OS on an Apple Intelligence device

ouro-ax-xray 0.2.0 · claim set 0.1.0 · Xcode tools 27B52 · 2026-11-04

Siri AI

Tools and skills, ready for the new Siri.

Mark an intent as a tool and Siri AI can call it in the middle of a conversation. Give it a skill and Siri learns your app's way of doing a longer job, like cooking along with a recipe, step by step. The Siri AI parts need the Xcode 27.3 beta SDK, and until you build with it they compile to nothing.

A tool intent
@AppIntent(.tool)
struct StartCookAlong: AppIntent {
    static let title: LocalizedStringResource = "Start cooking a recipe"
    static let description = IntentDescription(
        "Opens cook mode on the recipe's first step and reads it aloud."
    )
    @Parameter(title: "Recipe") var recipe: RecipeEntity
    …
}

The skills linter checks a skills bundle at build time, with errors pinned to the line, so a typo in a skill's frontmatter is caught before Siri meets it.

swift run ouro-ax-skills lint path/to/Skills.skillsbundle

Your turn

X-ray your own app.

Build your app, point the X-ray at it, and get the same report you just browsed: Markdown for people, JSON for CI, and a page for everyone else.

swift run ouro-ax-xray --app path/to/App.app --out report

That reads the metadata and lints the skills, right on your Mac. Add the behaviour stage when you're ready, and write a Siri scenario set for the parts only Siri can answer. The full recipe.

Ways of knowing

Three layers: claims, behaviour, and how it feels.

The X-ray checks what your app tells Siri. The other layers finish the picture: Ouro Reins lets an agent drive the app itself, headless, and Ouro Feel measures how it feels in a person's hand.

  1. Claims held

    AX X-ray (this page)

    Does it say what it does?

    Reads the built app's App Intents from outside and checks that its claims hold.

  2. Behaviour

    Reins

    Does it do it?

    Drives the screens as state and actions, headless, with the network blocked.

  3. How it feels

    Feel

    Does it feel right while it does?

    Walks the real app in a simulator and measures its jank, genre by genre.