Shortcuts Playground
Generate valid .shortcut files that can be signed and imported into Apple's Shortcuts app. This Codex plugin supports two workflows: building new shortcuts from scratch and remixing existing unsigned XML shortcuts with a natural-language diff.
Codex Compatibility
Codex plugins do not expose the Claude Code slash commands, specialized agents, or PATH wrapper commands from the Claude package. In Codex, use this skill directly and call the bundled scripts by path.
The Codex package also bundles a PostToolUse auto-validation hook. It runs only
when Codex plugin hooks are enabled with [features].plugin_hooks = true and
the hook has been trusted in /hooks if Codex prompts for review. The hook
validates changed .xml/.shortcut files that contain WFWorkflowActions.
Resolve the skill directory as the folder containing this SKILL.md. In examples below, SKILL_DIR means that directory.
| Task |
Codex command pattern |
| Resolve icon/color |
python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "$USER_PROMPT" |
| Validate XML |
python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml |
| Archive + sign |
"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/Shortcut.xml --name "Shortcut Name" |
sign_shortcut.sh defaults to SHORTCUTS_PLAYGROUND_OUTPUT_DIR or ~/Documents/Shortcuts Playground, and SHORTCUTS_PLAYGROUND_SIGNING_MODE or anyone. It requires macOS and Apple's shortcuts CLI. The validator only requires Python 3.10+.
Mandatory: Follow the guidelines in BEST_PRACTICES.md for every shortcut.
If guidance here conflicts with BEST_PRACTICES.md, follow BEST_PRACTICES.md.
Definition of done: a new build is not complete when XML validation passes. It is complete only after scripts/sign_shortcut.sh archives the unsigned XML, writes the signed .shortcut, and you verify the signed file exists with non-zero size.
Pipeline-first rule: write the smallest complete shortcut that implements the request, validate it, sign it, and verify the signed file before spending turns on cosmetic polish. Comments only need to be concise and repair-oriented. A valid XML draft without a signed .shortcut is not a useful stopping point.
Recommended Reading Order
- BEST_PRACTICES.md for mandatory rules and validation expectations
- PLIST_FORMAT.md for plist structure and serialization details
- ACTIONS.md, APPINTENTS.md, AUTOMATION_TRIGGERS.md, and THIRD_PARTY_ACTIONS.md for action IDs, AppIntent parameters, and OS 27 trigger metadata
- HEALTHKIT.md when building or remixing Health actions
- VARIABLES.md, CONTROL_FLOW.md, and FILTERS.md for wiring patterns
- ICONS_AND_COLORS.md, PARAMETER_TYPES.md, and EXAMPLES.md for implementation details
Quick Start
A shortcut is an XML plist that gets signed into a binary package. Generate the XML form:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WFWorkflowActions</key>
<array>
<!-- Actions go here -->
</array>
<key>WFWorkflowClientVersion</key>
<string>2700.0.4</string>
<key>WFWorkflowHasOutputFallback</key>
<false/>
<key>WFWorkflowIcon</key>
<dict>
<key>WFWorkflowIconGlyphNumber</key>
<integer>61440</integer>
<key>WFWorkflowIconStartColor</key>
<integer>431817727</integer>
</dict>
<key>WFWorkflowImportQuestions</key>
<array/>
<key>WFWorkflowInputContentItemClasses</key>
<array/>
<key>WFWorkflowMinimumClientVersion</key>
<integer>900</integer>
<key>WFWorkflowMinimumClientVersionString</key>
<string>900</string>
<key>WFWorkflowName</key>
<string>My Shortcut</string>
<key>WFWorkflowOutputContentItemClasses</key>
<array/>
<key>WFWorkflowTypes</key>
<array/>
</dict>
</plist>
Minimal Hello World
<dict>
<key>WFWorkflowActionIdentifier</key>
<string>is.workflow.actions.gettext</string>
<key>WFWorkflowActionParameters</key>
<dict>
<key>UUID</key>
<string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>
<key>WFTextActionText</key>
<string>Hello World!</string>
</dict>
</dict>
<dict>
<key>WFWorkflowActionIdentifier</key>
<string>is.workflow.actions.showresult</string>
<key>WFWorkflowActionParameters</key>
<dict>
<key>Text</key>
<dict>
<key>Value</key>
<dict>
<key>attachmentsByRange</key>
<dict>
<key>{0, 1}</key>
<dict>
<key>OutputName</key>
<string>Text</string>
<key>OutputUUID</key>
<string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>
<key>Type</key>
<string>ActionOutput</string>
</dict>
</dict>
<key>string</key>
<string></string>
</dict>
<key>WFSerializationType</key>
<string>WFTextTokenString</string>
</dict>
</dict>
</dict>
Core Concepts
1. Actions
Every action has:
- Identifier:
is.workflow.actions.<name> (e.g., is.workflow.actions.showresult)
- Parameters: Action-specific configuration in
WFWorkflowActionParameters
- UUID: Unique identifier for referencing this action's output
2. Variable References
To use output from a previous action:
- The source action needs a
UUID parameter
- Reference it using
OutputUUID in an attachmentsByRange dictionary
- Use `` (U+FFFC) as placeholder in the string where the variable goes
- Set
WFSerializationType to WFTextTokenString
3. Control Flow
Control flow actions (repeat, conditional, menu) use:
GroupingIdentifier: UUID linking start/middle/end actions
WFControlFlowMode: 0=start, 1=middle (else/case), 2=end
Common Actions Quick Reference
| Action |
Identifier |
Key Parameters |
| Text |
is.workflow.actions.gettext |
WFTextActionText |
| Show Result |
is.workflow.actions.showresult |
Text |
| Ask for Input |
is.workflow.actions.ask |
WFAskActionPrompt, WFInputType |
| Use AI Model |
is.workflow.actions.askllm |
WFLLMPrompt, WFLLMModel, WFGenerativeResultType, WFAllowWebSearch, FollowUp (OS 27+) |
| Comment |
is.workflow.actions.comment |
WFCommentActionText |
| URL |
is.workflow.actions.url |
WFURLActionURL |
| Get Contents of URL |
is.workflow.actions.downloadurl |
WFURL, WFHTTPMethod |
| Get Weather |
is.workflow.actions.weather.currentconditions |
(none required) |
| Open App |
is.workflow.actions.openapp |
WFAppIdentifier |
| Open URL |
is.workflow.actions.openurl |
WFInput |
| Alert |
is.workflow.actions.alert |
WFAlertActionTitle, WFAlertActionMessage |
| Notification |
is.workflow.actions.notification |
WFNotificationActionTitle, WFNotificationActionBody |
| Set Variable |
is.workflow.actions.setvariable |
WFVariableName, WFInput |
| Get Variable |
is.workflow.actions.getvariable |
WFVariable |
| Number |
is.workflow.actions.number |
WFNumberActionNumber |
| List |
is.workflow.actions.list |
WFItems |
| Dictionary |
is.workflow.actions.dictionary |
WFItems |
| Repeat (count) |
is.workflow.actions.repeat.count |
WFRepeatCount, GroupingIdentifier, WFControlFlowMode |
| Repeat (each) |
is.workflow.actions.repeat.each |
WFInput, GroupingIdentifier, WFControlFlowMode |
| If/Otherwise |
is.workflow.actions.conditional |
WFInput, WFCondition, GroupingIdentifier, WFControlFlowMode |
| Choose from Menu |
is.workflow.actions.choosefrommenu |
WFMenuPrompt, WFMenuItems, GroupingIdentifier, WFControlFlowMode |
| Find Photos |
is.workflow.actions.filter.photos |
WFContentItemFilter (see FILTERS.md) |
| Delete Photos |
is.workflow.actions.deletephotos |
photos (NOT WFInput!) |
Detailed Reference Files
For complete documentation, see:
- PLIST_FORMAT.md - Complete plist structure
- ICONS_AND_COLORS.md - Icon glyph + color selection (explicit and inferred)
- ACTIONS.md - WF*Action identifiers and parameters
- APPINTENTS.md - AppIntent actions (ToolKit + backups)
- AUTOMATION_TRIGGERS.md - OS 27 ToolKit automation trigger metadata for research and future automation support
- PARAMETER_TYPES.md - All parameter value types and serialization formats
- HEALTHKIT.md - iOS/iPadOS Health actions, bundled anonymized XML examples, and HealthKit value coverage
- URL_SCHEMES.md - Apple-documented Shortcuts URL schemes and x-callback-url patterns
- JAVASCRIPT_WEBPAGE.md - Run JavaScript on Webpage runtime requirements and script rules
- DATE_TIME.md - Apple-aligned date/time recipes, UNIX timestamps, ISO 8601, RFC 2822, and custom formats
- VARIABLES.md - Variable reference system
- CONTROL_FLOW.md - Repeat, Conditional, Menu patterns
- FILTERS.md - Content filters for Find/Filter actions (photos, files, etc.)
- EXAMPLES.md - Complete working examples
- BEST_PRACTICES.md - Mandatory build guidelines
- THIRD_PARTY_ACTIONS.md - Third-party actions (ToolKit + backups)
- TOOLKIT_SNAPSHOT.md - Bundled ToolKit action-ID allowlists
- CHANGELOG.md - Change history: autoresearch findings, documentation updates, and version notes
When you need to verify an unfamiliar action identifier, check the packaged data/toolkit-v*-tool-ids.json snapshots, then ACTIONS.md, APPINTENTS.md, and THIRD_PARTY_ACTIONS.md before inventing anything.
Golden Example Library (On-Demand)
A curated set of shortcut XMLs is available for on-demand reference. Use the index first; only load XML sources that match the current task.
- Index:
golden-shortcuts/index.jsonl (token-efficient metadata: title, purpose, tags, xml path)
- XMLs:
golden-shortcuts/xml/<shortcut_id>.xml
- Note: Golden XMLs are pattern references and may predate current validator/comment standards; treat them as wiring examples, not pass/fail baselines.
Workflow:
- Read
golden-shortcuts/index.jsonl to find relevant examples by tags/purpose.
- Load only the single XML file(s) needed for the current task.
- Do not bulk-load the entire library.
Icon and Color Resolver (Required)
For every generated shortcut, choose icon and color using the resolver unless the user gave explicit integer values already:
python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}"
Optional explicit overrides:
python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}" --icon "robot" --color "purple"
Then set:
WFWorkflowIconGlyphNumber = icon.glyph_number
WFWorkflowIconStartColor = color.value
The resolver supports natural-language icon requests (e.g. paper airplane icon, terminal icon, expense icon) and automatic icon selection when no icon is requested.
Preflight Validator — Craig Loop (Required)
After generating a shortcut, run the validator in a fix loop (Craig Loop). Each iteration: read the errors, make a targeted fix, re-validate. Do not re-run without changing something. If the Codex PostToolUse hook reports validator feedback after an edit, treat that as the current validator run and fix the reported errors before signing.
python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml
Craig Loop Protocol
- Run the validator. If it passes, proceed to signing.
- Read ALL error messages. The validator prints every error it finds — fix as many as possible in one pass, not just the first one.
- Make targeted fixes in the plist XML based on the error messages. Each error includes the action index and identifier so you know exactly where to edit.
- Re-run the validator. Repeat from step 1.
- Exit conditions (stop looping and report to the user):
- Max 5 iterations. If the validator still fails after 5 fix attempts, stop. Summarize the remaining errors and ask the user for guidance.
- Same errors repeating. If the same error persists across 2 consecutive iterations despite attempted fixes, stop. The fix approach is wrong — do not keep trying the same thing.
- Known validator gaps. Only waive validator failures if
BEST_PRACTICES.md lists a current, runtime-verified false positive. Otherwise fix the shortcut or stop and report the exact remaining errors.
Anti-patterns (do NOT do these)
- Chatting the validator: Running the validator repeatedly without making meaningful code changes between runs. Every re-run must follow a real edit.
- Cosmetic fixes: Rearranging comments or renaming variables to "try something" when the error is about wiring or missing parameters.
- Regenerating from scratch when only 1-2 specific actions need fixing. Targeted edits preserve working wiring.
Data sources
The validator uses bundled ToolKit snapshot IDs from packaged data/toolkit-v*-tool-ids.json files, filtered by target OS version and target platform, then augments with ACTIONS.md, APPINTENTS.md, and THIRD_PARTY_ACTIONS.md. The default OS target is auto (sw_vers on macOS, macOS 26 when the host cannot be detected). The default platform target is macos. Use --target-macos 27 or SHORTCUTS_PLAYGROUND_TARGET_MACOS=27 only when building OS 27-era shortcuts that need target-gated macOS v78 identifiers or OS 27-only parameters such as WFAllowWebSearch / FollowUp on Use Model, interpretAsMarkdown, WFAvoidTolls, and Safari Tab Group contents. Use --target-platform ios / SHORTCUTS_PLAYGROUND_TARGET_PLATFORM=ios only for iPhone/iPad authoring, and --target-platform all only for intentional cross-platform metadata audits. For OS 27 targets, the validator also uses data/toolkit-v78-first-party-parameter-keys.json to reject unknown top-level keys on first-party com.apple.* AppIntent-style actions. It does not read the user's live ToolKit SQLite database during normal validation.
For reviewed Apple-derived macOS 27 schema grounding and automation trigger metadata, use the static catalogs only:
python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier additemtolist --target-macos 27
python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --python-name when_app_opened --target-macos 27
python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier com.apple.HearingApp.MuteVolumeIntent --target-macos 27 --target-platform ios
data/macos27-shortpy-grounding.json may improve parameter/schema confidence, data/toolkit-v78-first-party-enum-cases.json may improve picker-value selection, data/toolkit-v78-trigger-parameter-keys.json may improve automation-trigger discovery, and data/macos27-workflow-trigger-samples.json may show sanitized exported WFWorkflowTriggers shapes for observed OS 27 automation headers. None of them overrides validator target gating. The lookup helper reports target-platform availability notes for iOS-only/macOS-only ToolKit rows. Exported automation-bearing shortcuts are the authority for portable automation-header authoring; do not infer that carrier from ToolKit metadata alone.
Escape-hatch comments
If a request explicitly requires vCard/VCF formatting or file-based token loading, add a Comment containing ALLOW_VCARD or ALLOW_TOKEN_FILE so the validator can allow it. Other escape hatches: ALLOW_MANUAL_UNIT_CONVERSION, ALLOW_DATETIME_FORMAT.
Wiring Regression Suite (Recommended)
When changing wiring logic or validator rules for Weather/Location actions, run the bulk regression suite:
python3 "$SKILL_DIR/scripts/test_wiring_regressions.py" --write-fixtures /tmp/shortcuts-wiring-regressions
The suite generates and validates:
- 43 Weather Detail cases (21 valid + 22 invalid)
- 40 Location parameter cases (20 valid + 20 invalid)
- 16 Set Name/Rename File cases (8 valid + 8 invalid)
It exits non-zero if any case behavior regresses.
Random Mixed-Action Stress Suite (Recommended)
For broad randomized coverage (brand-new shortcuts, 10+ distinct actions each), run:
python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10
Behavior:
- Generates brand-new random shortcuts under
--output-dir when provided, otherwise SHORTCUTS_PLAYGROUND_OUTPUT_DIR/<YYYY-MM-DD>/random-mixed-actions-<runid>/ or ~/Documents/Shortcuts Playground/<YYYY-MM-DD>/random-mixed-actions-<runid>/.
- Enforces a minimum distinct action count per shortcut (
--min-actions, default 10).
- Runs validate/retry loops per case (
--max-attempts, default 20).
- Writes
manifest.json, results.json, and summary.md for documentation.
Optional:
python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --sign
For OS 27 coverage, opt in explicitly so older users and CI jobs do not validate Golden Gate-only actions by accident:
python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --target-macos 27 --include-os27-actions --sign
The OS 27 module adds Stored Content, Add Item to List, Otherwise If, Get Selected Text, Get What's On Screen, and Get Current VPN to every generated shortcut.
Use this suite when you need randomized multi-action regression coverage beyond targeted wiring tests.
Signing Shortcuts
Shortcuts MUST be signed before they can be imported. The Codex skill ships scripts/sign_shortcut.sh, which combines archive + sign into a single command:
# Archives the unsigned XML under $output_dir/$(date +%F)/ and writes a signed .shortcut to $output_dir.
"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut"
# Override the signing mode (default is SHORTCUTS_PLAYGROUND_SIGNING_MODE, falling back to 'anyone').
"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut" --mode people-who-know-me
The underlying pipeline is still the macOS shortcuts CLI:
shortcuts sign --mode anyone --input MyShortcut.shortcut --output MyShortcut.shortcut
The signing process:
- Write your plist as XML to a
.shortcut file by default.
- Run
scripts/sign_shortcut.sh (or shortcuts sign directly) to add the cryptographic signature (~19KB added). The wrapper archives the XML, then retries a validator-clean format failure after binary plist conversion when Apple's XML signer chokes.
- Keep the signed output filename equal to the intended display name (no
_signed suffix).
- The signed file can be opened/imported into Shortcuts.app.
Signing gotchas:
- If
shortcuts sign reports Error: The file doesn't exist. but the file exists, copy the XML plist directly to a clean .shortcut path and retry (example: cp source.xml /tmp/MyShortcut.shortcut).
- If
shortcuts sign reports Error: The file couldn't be opened because it isn't in the correct format. while validate_shortcut.py and plutil -lint pass, retry after plutil -convert binary1 on the final .shortcut copy before blaming the plist. scripts/sign_shortcut.sh performs this retry automatically; if both attempts fail, suspect Codex workspace-write sandbox restrictions before treating the XML as malformed.
ERROR: Unrecognized attribute string flag '?' warnings are noisy but can be non-fatal if the output file is produced.
- The
shortcuts CLI supports run, list, view, and sign; do not assume delete, rename, or import subcommands.
Archive Raw XML (Required)
Before signing, archive the unsigned XML in a date/time folder for inspection. scripts/sign_shortcut.sh does this for you, but the rules below still apply when you invoke shortcuts sign directly.
Folder structure rule:
- The archive root is
SHORTCUTS_PLAYGROUND_OUTPUT_DIR (falls back to ~/Documents/Shortcuts Playground/ when unset).
- Inside the archive root, create a date folder for the current day (
YYYY-MM-DD) if it doesn't exist.
- Copy the unsigned XML into that date folder with a time-stamped filename.
Example (output dir = ~/Documents/Shortcuts Playground):
- Archive folder:
~/Documents/Shortcuts Playground/2026-02-03/
- Archive file:
My Shortcut-142355.xml
Command pattern (the one-liner scripts/sign_shortcut.sh wraps):
OUTPUT_DIR="${SHORTCUTS_PLAYGROUND_OUTPUT_DIR:-$HOME/Documents/Shortcuts Playground}"
ARCHIVE_ROOT="$OUTPUT_DIR/$(date +%F)"
mkdir -p "$ARCHIVE_ROOT"
cp "/path/to/My Shortcut.xml" "$ARCHIVE_ROOT/My Shortcut-$(date +%H%M%S).xml"
The archive copy must be the unsigned, raw XML (not the signed .shortcut).
Workflow for Creating Shortcuts
- Research external APIs - For complex/unfamiliar APIs, read the latest official docs before drafting request code.
- Define actions - List what the shortcut should do
- Generate UUIDs - Each action that produces output needs a unique UUID
- Build action array - Create each action dictionary with identifier and parameters
- Wire variable references - Connect outputs to inputs using
OutputUUID
- Resolve icon and color - Run
scripts/select_shortcut_icon_color.py with the full user prompt (plus any explicit icon/color hints) and use the returned values
- Wrap in plist - Add the root structure with icon, name, version
- Write to file - Save as
.shortcut (XML plist format is fine)
- Preflight validation - Run the Craig Loop (see above): validate → fix → re-validate, max 5 iterations.
- Archive + Sign (required) - Run
"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/file.xml --name "Final Name". This script archives the unsigned XML to SHORTCUTS_PLAYGROUND_OUTPUT_DIR/$(date +%F)/ or ~/Documents/Shortcuts Playground/$(date +%F)/ and writes the signed .shortcut alongside it. Never leave the signing output filename with a _signed suffix.
- Verify signed output (required) - Confirm the signed
.shortcut path reported by sign_shortcut.sh exists and has non-zero size before reporting done. Stopping at "validation passed" is a failed build.
After step 8 succeeds, go directly to step 9. Any edit after validation, including comment or wording polish, requires another validation and another signing pass.
Comment Blocks (Repair-Oriented)
Because variable wiring can require manual fixes, add a concise Comment before each major block with a bulleted list describing which variables must be connected. Keep it short, specific, and focused on wiring (e.g., “Use Repeat Item 2 inside inner loop”).
Write comments with Shortcuts UI wording (for example, Input, Date, Provided Input, Repeat Item, Text) and readable action names (Ask for Input, Text, Save File). Do not use plist key jargon like WFInput / WFDate / WFImage in Comment text. NEVER include UUIDs, OutputUUID references, or technical plist details in Comment text — comments must be descriptive natural language only.
Prefer wording like - Input uses the text output from the Text action above and • Date uses the user's answer from Ask for Input instead of WF* field names or UUID references.
Key Rules
- UUIDs must be uppercase and generated via
uuidgen, not hand-picked. Before emitting a shortcut, run a single Bash call to generate all the UUIDs you'll need:for i in $(seq 1 <N>); do uuidgen | tr '[:lower:]' '[:upper:]'; done
Where <N> is the number of action UUIDs the shortcut requires (one per action that produces output or is referenced by downstream actions). Assign each output line to a specific action in your working map, then paste them into the plist. Never use sequential placeholders like 11111111-1111-1111-1111-111111111111, AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA, or any other pattern where every hex character is the same — the validator rejects repeating-hex UUIDs as a hard error. Valid example shape: 7F3A4E91-C2D8-4B56-BE5A-0242AC120002.
- WFControlFlowMode is an integer: Use
<integer>0</integer> not <string>0</string>
- Range keys use format:
{position, length} - e.g., {0, 1} for first character
- The placeholder character: `` (U+FFFC) marks where variables are inserted
- Control flow needs matching ends: Every repeat/if/menu start needs an end action with same
GroupingIdentifier
- Match placeholder positions:
attachmentsByRange must point to the exact index of each `` in the final string
- No out-of-bounds ranges:
attachmentsByRange positions beyond string length can crash Shortcuts on import
- Avoid empty placeholders: Omit unused keys or fields; do not leave empty values where the action expects data
- WFURL serialization: For
WFURL parameters (especially downloadurl), use WFTextTokenString with `` placeholders even when the URL is entirely a variable; reserve WFTextTokenAttachment for parameters that are explicitly variable-only (e.g., WFVariable, WFRequestVariable)
- Form file fields and WFRequestVariable: For
WFHTTPBodyType = Form, file fields (WFItemType = 5) must wrap the file reference in WFTokenAttachmentParameterState with an inner WFTextTokenAttachment so the UI shows the connected file variable. Set WFRequestVariable only when body type is File (JSON Text fallback pattern)
- Array fields: For
WFDictionaryFieldValue items with WFItemType = 2, use WFArrayParameterState with a list of items (WFItemType + WFValue), not numeric keys inside another dictionary
- Format Date input: Set
WFDate (not WFInput) as a WFTextTokenString placeholder wired to a Date output; never leave WFDate empty
- Repeat loops: Inside Repeat with Each, use Repeat Item for per-item extraction; avoid Repeat Results inside the loop
- Reuse extractions: If you extract multiple fields from a dictionary/API response, reuse each value later or remove the unused extraction
- Variable placeholders: When composing strings or URLs with variables (Ask for Input, Track/Artist, etc.), insert a placeholder for every variable and align
attachmentsByRange positions
- Dictionary dot notation: Use dot notation (with 1-based indexes) in
WFDictionaryKey to access nested keys (e.g., results.tracks.items, artists.1.name), but guard optional parents first (avoid direct error.message on raw API responses)
- Conditional inputs (verified against Apple-built samples): For
is.workflow.actions.conditional with WFControlFlowMode = 0, every condition code requires an explicit WFInput set as { Type: "Variable", Variable: { Value: <ActionOutput or Variable>, WFSerializationType: "WFTextTokenAttachment" } }. There is no implicit-input mode. Per-code literal field requirements: string codes 4/5/8/9/99/999 need WFConditionalActionString; numeric codes 0/1/2/3 need WFNumberValue; numeric 1003 (is between) needs both WFNumberValue (lower) and WFAnotherNumber (upper, attachment); existence codes 100/101 need neither literal field. Code 0 is is less than (NOT equals); codes 0–3 are inequalities. Multi-condition Ifs use WFConditions with WFContentPredicateTableTemplate serialization; do not mix WFConditions with top-level WFCondition. macOS 27 Otherwise If is the same conditional identifier with WFControlFlowMode = 1 plus condition fields. Plain Otherwise is mode 1 with no condition fields; End If is mode 2. For JSON booleans, compare numerically (1/0); treat JSON null as empty. See CONTROL_FLOW.md "Condition Codes" and "Multi-condition If" for the complete reference and templates.
- Workflow icon keys are mandatory: Always set both
WFWorkflowIconGlyphNumber and WFWorkflowIconStartColor in WFWorkflowIcon (use resolver output)
- Runtime file picking: If the user needs to choose a file, use
is.workflow.actions.file.select and connect its output
- Text token validation: Run a final validation pass over every
WFTextTokenString and refuse to output if any attachmentsByRange key does not map to a placeholder position or if counts mismatch
- No unrequested services: Do not introduce third-party APIs, external CDNs, or extra import questions unless the user explicitly requests them
- Notion image uploads: Default to Notion
file_uploads + file_upload block type; avoid external image blocks unless the user supplies a URL or asks for external hosting
- Notion title filters: Use the Notion
title property name unless the user explicitly says their database uses a different title property name (e.g., Name)
- API research and endpoint accuracy: For complex or unfamiliar external APIs, verify auth, endpoints, parameters, and payload formats against the latest official docs before assembling the shortcut. Validate endpoint strings exactly (underscore vs hyphen matters)
- API string sanity checks: Before output, scan API strings for
// (beyond protocol) and empty JSON fields where variables are expected (e.g., equals:””, id:””, url:””, filename:””), and fix them
- JSON request bodies: For
WFHTTPBodyType = JSON, use WFJSONValues for flat key/value payloads so the body is preserved in Shortcuts UI
- Format Date custom style: When
WFDateFormatStyle is Custom, set WFDateFormat=Custom and put the pattern in WFDateFormatString (e.g., MMMM d, yyyy, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ssXXXXX). See DATE_TIME.md for UNIX timestamp, ISO 8601, RFC 2822, and Unicode TR35 guidance
- Complex JSON fallback: If the JSON body includes arrays of objects or deep nesting and the UI renders it as
Number 0/empty rows, use a JSON Text action and set WFRequestVariable to that text with WFHTTPBodyType = File and Content-Type: application/json
- Filename extensions: When using Get Name for file uploads, append the correct extension in the JSON payload (e.g.,
.png) and do not rely on the base name alone
- Count input visibility: For
is.workflow.actions.count, set both WFInput and Input to the same variable so the UI shows the selected list
- Get Name web titles: For file names, set Get Web Page Title to Off in Get Name so the action returns the file name, not a URL title
- String If workaround: If validator rules conflict on string conditionals, use
Match Text + Count + numeric If instead of direct string If
- Replace Text empty replacement: For delete-match patterns, prefer omitting
WFReplaceTextReplace; an explicit empty string is allowed, but omission is cleaner and more portable
- Base64 input wiring: Always set
WFInput for is.workflow.actions.base64encode; implicit input can import as an empty field and break runtime
- Replace Text input visibility: For
is.workflow.actions.text.replace, use a WFTextTokenString placeholder (or wrapped variable input), not a bare WFTextTokenAttachment
- Adjust Date reliability: Use
WFDate + non-empty WFDuration for is.workflow.actions.adjustdate (optionally mirror with WFInput); include WFAdjustOperation when explicit Add/Subtract is required. Offset-picker-only payloads can import as Add 0 seconds on iOS
- Convert Image wiring: For
is.workflow.actions.image.convert, always set WFInput explicitly; do not rely on implicit input chaining
- Weather detail wiring: For
is.workflow.actions.properties.weather.conditions, set both WFInput and WFContentItemPropertyName, keep WFContentItemPropertyName concrete (never placeholder Detail), and wire WFInput directly to an ActionOutput from is.workflow.actions.weather.currentconditions / is.workflow.actions.weather.forecast (no named-variable hop). Supported detail names are Date, Location, Temperature, Low, High, Feels Like, Condition, Visibility, Dewpoint, Humidity, Pressure, Precipitation Amount, Precipitation Chance, Wind Speed, Wind Direction, UV Index, Sunrise Time, Sunset Time, Air Quality Index, Air Quality Category, Air Pollutants, and Name. Sunrise Time and Sunset Time from Daily forecasts are lists: insert Get Item from List before Format Date, using First Item for sunrise and Last Item for sunset
- Time Between Dates input wiring: For
is.workflow.actions.gettimebetweendates, set WFInput and exactly one non-empty date operand (WFDate or WFTimeUntilCustomDate or WFTimeUntilFromDate) as WFTextTokenString placeholders. To compare with now, first add a Date action set to Current Date and reference that action output; never put a direct CurrentDate magic token in the action. WFTimeUntilUnit may be omitted only when intentionally using the default unit. Never emit empty unused date keys
- Extract from Image input wiring: For
is.workflow.actions.extracttextfromimage, set exactly one non-empty image input key. On OS 27+, prefer imageFile; older exported shortcuts may use WFImage or, when intentionally required by an existing pattern, WFInput.
- API error extraction safety: Do not read
error.message directly from a raw response; extract error, guard it with If Has Any Value, then read message
- Continuation JSON array closure: When appending to a JSON array via
Replace Text on \]$, the replacement must end with ]; missing the closing bracket corrupts JSON and causes Detect Dictionary to return empty
- No raw object/list tokens inside JSON text: Do not inject Dictionary/List outputs (for example, raw API
content arrays) directly into JSON Text templates; Shortcuts may stringify them as newline-separated blocks rather than valid JSON
- Continuation payload safe pattern: For multi-turn handoff payloads, append assistant text from a plain text variable (for example,
Response Text) and keep messages_json as valid JSON before rerunning the shortcut
- JSON string interpolation safety: Before inserting freeform text into JSON Text templates (
”content”:””), sanitize it first (at minimum handle backslashes, double quotes, and control whitespace/newlines) or the next Detect Dictionary step will fail
- Shortcuts URL schemes: Only use Apple-documented
shortcuts:// routes from URL_SCHEMES.md. URL-encode every query value; do not invent import/install routes or extra parameters
- Run JavaScript on Webpage: Use
is.workflow.actions.runjavascriptonwebpage only for Safari webpage share-sheet shortcuts. Include ActionExtension, scope input to WFSafariWebPageContentItem, call completion(...) or completion(), return JSON-compatible values, and avoid synchronous dialogs/long timers
- API response parse stability: For
downloadurl JSON APIs, keep ShowHeaders off unless explicitly needed and run Detect Dictionary on Contents of URL before any Get Dictionary Value extraction
- Action input keys matter:
Replace Text uses WFInput, while Change Case and Split Text use text; wrong keys import but show empty inputs in the editor
- Split Text custom separator: If
WFTextSeparator is Custom, always include WFTextCustomSeparator (a single space ” “ is valid)
- Find Notes filter state:
WFContentItemFilter must be WFContentPredicateTableTemplate with non-empty templates; for Folder filters use WFLinkDynamicOptionSubstitutableState wrapping a tokenized variable
- Direct variable wiring: Prefer inserting named variables directly into action input fields; avoid redundant
Get Variable → next action hops unless there is a clear transformation need
- Location parameter wiring: Never emit empty location parameters. For
WFLocation, WFWeatherCustomLocation, and WFWeatherLocation, use WFTextTokenAttachment (not token strings) and reference a Get Current Location/Location output (directly or via a variable sourced from those outputs). is.workflow.actions.location must include a non-empty WFLocation attachment; missing/blank payloads import as empty “Location” fields
- Set Name vs Rename File: Use Set Name as
is.workflow.actions.setitemname with WFInput (source file/item) and WFName (target filename, for example Test.txt) when a workflow needs a renamed file object to save or share elsewhere. Its output is Renamed Item. Do not confuse this with Rename File (is.workflow.actions.file.rename with WFFile and WFNewFilename), which renames the original file in place at its existing path. For "rename, save/share elsewhere, then delete original" workflows, store the picked file as Original File, run Set Name on Original File, save/share Renamed Item, then delete Original File only after the save/share step if requested
- ⚠️ WFMathOperation syntax (verified against Shortcuts app): For
is.workflow.actions.math: (a) Addition: OMIT the WFMathOperation key entirely — no key means addition; (b) Subtraction: - (ASCII minus, U+002D); (c) Multiplication: × (U+00D7, ord 215, Unicode MULTIPLICATION SIGN) — NEVER *; (d) Division: ÷ (U+00F7, ord 247, Unicode DIVISION SIGN) — NEVER /; (e) Scientific ops (Modulus, Power, etc.): WFMathOperation='…' (U+2026 horizontal ellipsis) as placeholder, with real op in WFScientificMathOperation and operand in WFScientificMathOperand. Literal operands (WFMathOperand) must be plain strings like "10", not wrapped dicts. Shortcuts silently renders ASCII / as + in the UI with no error. See PARAMETER_TYPES.md "Math and Counting Operations" for verified examples.
- Never inspect the user's local system for authoring discovery unless the user explicitly asks for that local evidence. If an action identifier is allowlisted in
data/toolkit-v*-tool-ids.json but its parameter schema is not documented in the bundled reference files (ACTIONS.md, APPINTENTS.md, PARAMETER_TYPES.md, FILTERS.md, EXAMPLES.md, BEST_PRACTICES.md, HEALTHKIT.md, static data/macos27-shortpy-grounding.json, or the golden-shortcuts/ library), stop and ask the user - do not try to reverse-engineer the schema by reading local databases, inspecting system binaries, querying Shortcuts.app internals, or searching cloud-backup folders without explicit permission. Escalate to the user with three options: (a) best-effort guess + iterate after they import, (b) use a simpler alternative action you propose, or (c) they paste a working example for you to mirror. When the user explicitly supplies or requests local exported XML, prefer that evidence over web references.
- Automation triggers are sample-gated.
AUTOMATION_TRIGGERS.md, data/toolkit-v78-trigger-parameter-keys.json, and data/macos27-workflow-trigger-samples.json document OS 27 ToolKit trigger IDs, Python names, parameter keys, output types, and sanitized exported WFWorkflowTriggers payloads. Use exported automation-bearing shortcuts as the source of truth for portable authoring. Do not invent trigger plists from ToolKit metadata alone
…(truncated)
1---2name: shortcuts-playground3description: Build, validate, sign, archive, and remix macOS/iOS Shortcuts in Codex by creating plist files. Use when asked to create, modify, or remix shortcuts; automate workflows; build .shortcut files; or generate Shortcuts plists. Covers WF actions, AppIntents, third-party actions, HealthKit, variable references, and control flow using bundled target-gated ToolKit snapshots.4---56# Shortcuts Playground78Generate valid `.shortcut` files that can be signed and imported into Apple's Shortcuts app. This Codex plugin supports two workflows: building new shortcuts from scratch and remixing existing unsigned XML shortcuts with a natural-language diff.910## Codex Compatibility1112Codex plugins do not expose the Claude Code slash commands, specialized agents, or PATH wrapper commands from the Claude package. In Codex, use this skill directly and call the bundled scripts by path.1314The Codex package also bundles a `PostToolUse` auto-validation hook. It runs only15when Codex plugin hooks are enabled with `[features].plugin_hooks = true` and16the hook has been trusted in `/hooks` if Codex prompts for review. The hook17validates changed `.xml`/`.shortcut` files that contain `WFWorkflowActions`.1819Resolve the skill directory as the folder containing this `SKILL.md`. In examples below, `SKILL_DIR` means that directory.2021| Task | Codex command pattern |22|------|-----------------------|23| Resolve icon/color | `python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "$USER_PROMPT"` |24| Validate XML | `python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml` |25| Archive + sign | `"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/Shortcut.xml --name "Shortcut Name"` |2627`sign_shortcut.sh` defaults to `SHORTCUTS_PLAYGROUND_OUTPUT_DIR` or `~/Documents/Shortcuts Playground`, and `SHORTCUTS_PLAYGROUND_SIGNING_MODE` or `anyone`. It requires macOS and Apple's `shortcuts` CLI. The validator only requires Python 3.10+.2829**Mandatory**: Follow the guidelines in [BEST_PRACTICES.md](BEST_PRACTICES.md) for every shortcut.30If guidance here conflicts with [BEST_PRACTICES.md](BEST_PRACTICES.md), follow `BEST_PRACTICES.md`.3132**Definition of done**: a new build is not complete when XML validation passes. It is complete only after `scripts/sign_shortcut.sh` archives the unsigned XML, writes the signed `.shortcut`, and you verify the signed file exists with non-zero size.3334**Pipeline-first rule**: write the smallest complete shortcut that implements the request, validate it, sign it, and verify the signed file before spending turns on cosmetic polish. Comments only need to be concise and repair-oriented. A valid XML draft without a signed `.shortcut` is not a useful stopping point.3536## Recommended Reading Order37381. [BEST_PRACTICES.md](BEST_PRACTICES.md) for mandatory rules and validation expectations392. [PLIST_FORMAT.md](PLIST_FORMAT.md) for plist structure and serialization details403. [ACTIONS.md](ACTIONS.md), [APPINTENTS.md](APPINTENTS.md), [AUTOMATION_TRIGGERS.md](AUTOMATION_TRIGGERS.md), and [THIRD_PARTY_ACTIONS.md](THIRD_PARTY_ACTIONS.md) for action IDs, AppIntent parameters, and OS 27 trigger metadata414. [HEALTHKIT.md](HEALTHKIT.md) when building or remixing Health actions425. [VARIABLES.md](VARIABLES.md), [CONTROL_FLOW.md](CONTROL_FLOW.md), and [FILTERS.md](FILTERS.md) for wiring patterns436. [ICONS_AND_COLORS.md](ICONS_AND_COLORS.md), [PARAMETER_TYPES.md](PARAMETER_TYPES.md), and [EXAMPLES.md](EXAMPLES.md) for implementation details4445## Quick Start4647A shortcut is an XML plist that gets signed into a binary package. Generate the XML form:4849```xml50<?xml version="1.0" encoding="UTF-8"?>51<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">52<plist version="1.0">53<dict>54 <key>WFWorkflowActions</key>55 <array>56 <!-- Actions go here -->57 </array>58 <key>WFWorkflowClientVersion</key>59 <string>2700.0.4</string>60 <key>WFWorkflowHasOutputFallback</key>61 <false/>62 <key>WFWorkflowIcon</key>63 <dict>64 <key>WFWorkflowIconGlyphNumber</key>65 <integer>61440</integer>66 <key>WFWorkflowIconStartColor</key>67 <integer>431817727</integer>68 </dict>69 <key>WFWorkflowImportQuestions</key>70 <array/>71 <key>WFWorkflowInputContentItemClasses</key>72 <array/>73 <key>WFWorkflowMinimumClientVersion</key>74 <integer>900</integer>75 <key>WFWorkflowMinimumClientVersionString</key>76 <string>900</string>77 <key>WFWorkflowName</key>78 <string>My Shortcut</string>79 <key>WFWorkflowOutputContentItemClasses</key>80 <array/>81 <key>WFWorkflowTypes</key>82 <array/>83</dict>84</plist>85```8687### Minimal Hello World8889```xml90<dict>91 <key>WFWorkflowActionIdentifier</key>92 <string>is.workflow.actions.gettext</string>93 <key>WFWorkflowActionParameters</key>94 <dict>95 <key>UUID</key>96 <string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>97 <key>WFTextActionText</key>98 <string>Hello World!</string>99 </dict>100</dict>101<dict>102 <key>WFWorkflowActionIdentifier</key>103 <string>is.workflow.actions.showresult</string>104 <key>WFWorkflowActionParameters</key>105 <dict>106 <key>Text</key>107 <dict>108 <key>Value</key>109 <dict>110 <key>attachmentsByRange</key>111 <dict>112 <key>{0, 1}</key>113 <dict>114 <key>OutputName</key>115 <string>Text</string>116 <key>OutputUUID</key>117 <string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>118 <key>Type</key>119 <string>ActionOutput</string>120 </dict>121 </dict>122 <key>string</key>123 <string></string>124 </dict>125 <key>WFSerializationType</key>126 <string>WFTextTokenString</string>127 </dict>128 </dict>129</dict>130```131132## Core Concepts133134### 1. Actions135Every action has:136- **Identifier**: `is.workflow.actions.<name>` (e.g., `is.workflow.actions.showresult`)137- **Parameters**: Action-specific configuration in `WFWorkflowActionParameters`138- **UUID**: Unique identifier for referencing this action's output139140### 2. Variable References141To use output from a previous action:1421. The source action needs a `UUID` parameter1432. Reference it using `OutputUUID` in an `attachmentsByRange` dictionary1443. Use `` (U+FFFC) as placeholder in the string where the variable goes1454. Set `WFSerializationType` to `WFTextTokenString`146147### 3. Control Flow148Control flow actions (repeat, conditional, menu) use:149- `GroupingIdentifier`: UUID linking start/middle/end actions150- `WFControlFlowMode`: 0=start, 1=middle (else/case), 2=end151152## Common Actions Quick Reference153154| Action | Identifier | Key Parameters |155|--------|------------|----------------|156| Text | `is.workflow.actions.gettext` | `WFTextActionText` |157| Show Result | `is.workflow.actions.showresult` | `Text` |158| Ask for Input | `is.workflow.actions.ask` | `WFAskActionPrompt`, `WFInputType` |159| Use AI Model | `is.workflow.actions.askllm` | `WFLLMPrompt`, `WFLLMModel`, `WFGenerativeResultType`, `WFAllowWebSearch`, `FollowUp` (OS 27+) |160| Comment | `is.workflow.actions.comment` | `WFCommentActionText` |161| URL | `is.workflow.actions.url` | `WFURLActionURL` |162| Get Contents of URL | `is.workflow.actions.downloadurl` | `WFURL`, `WFHTTPMethod` |163| Get Weather | `is.workflow.actions.weather.currentconditions` | (none required) |164| Open App | `is.workflow.actions.openapp` | `WFAppIdentifier` |165| Open URL | `is.workflow.actions.openurl` | `WFInput` |166| Alert | `is.workflow.actions.alert` | `WFAlertActionTitle`, `WFAlertActionMessage` |167| Notification | `is.workflow.actions.notification` | `WFNotificationActionTitle`, `WFNotificationActionBody` |168| Set Variable | `is.workflow.actions.setvariable` | `WFVariableName`, `WFInput` |169| Get Variable | `is.workflow.actions.getvariable` | `WFVariable` |170| Number | `is.workflow.actions.number` | `WFNumberActionNumber` |171| List | `is.workflow.actions.list` | `WFItems` |172| Dictionary | `is.workflow.actions.dictionary` | `WFItems` |173| Repeat (count) | `is.workflow.actions.repeat.count` | `WFRepeatCount`, `GroupingIdentifier`, `WFControlFlowMode` |174| Repeat (each) | `is.workflow.actions.repeat.each` | `WFInput`, `GroupingIdentifier`, `WFControlFlowMode` |175| If/Otherwise | `is.workflow.actions.conditional` | `WFInput`, `WFCondition`, `GroupingIdentifier`, `WFControlFlowMode` |176| Choose from Menu | `is.workflow.actions.choosefrommenu` | `WFMenuPrompt`, `WFMenuItems`, `GroupingIdentifier`, `WFControlFlowMode` |177| Find Photos | `is.workflow.actions.filter.photos` | `WFContentItemFilter` (see FILTERS.md) |178| Delete Photos | `is.workflow.actions.deletephotos` | `photos` (**NOT** `WFInput`!) |179180## Detailed Reference Files181182For complete documentation, see:183- [PLIST_FORMAT.md](PLIST_FORMAT.md) - Complete plist structure184- [ICONS_AND_COLORS.md](ICONS_AND_COLORS.md) - Icon glyph + color selection (explicit and inferred)185- [ACTIONS.md](ACTIONS.md) - WF*Action identifiers and parameters186- [APPINTENTS.md](APPINTENTS.md) - AppIntent actions (ToolKit + backups)187- [AUTOMATION_TRIGGERS.md](AUTOMATION_TRIGGERS.md) - OS 27 ToolKit automation trigger metadata for research and future automation support188- [PARAMETER_TYPES.md](PARAMETER_TYPES.md) - All parameter value types and serialization formats189- [HEALTHKIT.md](HEALTHKIT.md) - iOS/iPadOS Health actions, bundled anonymized XML examples, and HealthKit value coverage190- [URL_SCHEMES.md](URL_SCHEMES.md) - Apple-documented Shortcuts URL schemes and x-callback-url patterns191- [JAVASCRIPT_WEBPAGE.md](JAVASCRIPT_WEBPAGE.md) - Run JavaScript on Webpage runtime requirements and script rules192- [DATE_TIME.md](DATE_TIME.md) - Apple-aligned date/time recipes, UNIX timestamps, ISO 8601, RFC 2822, and custom formats193- [VARIABLES.md](VARIABLES.md) - Variable reference system194- [CONTROL_FLOW.md](CONTROL_FLOW.md) - Repeat, Conditional, Menu patterns195- [FILTERS.md](FILTERS.md) - Content filters for Find/Filter actions (photos, files, etc.)196- [EXAMPLES.md](EXAMPLES.md) - Complete working examples197- [BEST_PRACTICES.md](BEST_PRACTICES.md) - Mandatory build guidelines198- [THIRD_PARTY_ACTIONS.md](THIRD_PARTY_ACTIONS.md) - Third-party actions (ToolKit + backups)199- [TOOLKIT_SNAPSHOT.md](TOOLKIT_SNAPSHOT.md) - Bundled ToolKit action-ID allowlists200- [CHANGELOG.md](CHANGELOG.md) - Change history: autoresearch findings, documentation updates, and version notes201202When you need to verify an unfamiliar action identifier, check the packaged `data/toolkit-v*-tool-ids.json` snapshots, then `ACTIONS.md`, `APPINTENTS.md`, and `THIRD_PARTY_ACTIONS.md` before inventing anything.203204## Golden Example Library (On-Demand)205206A curated set of shortcut XMLs is available for on-demand reference. Use the index first; only load XML sources that match the current task.207208- Index: `golden-shortcuts/index.jsonl` (token-efficient metadata: title, purpose, tags, xml path)209- XMLs: `golden-shortcuts/xml/<shortcut_id>.xml`210- Note: Golden XMLs are pattern references and may predate current validator/comment standards; treat them as wiring examples, not pass/fail baselines.211212Workflow:2131) Read `golden-shortcuts/index.jsonl` to find relevant examples by tags/purpose.2142) Load only the single XML file(s) needed for the current task.2153) Do **not** bulk-load the entire library.216217## Icon and Color Resolver (Required)218219For every generated shortcut, choose icon and color using the resolver unless the user gave explicit integer values already:220221```bash222python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}"223```224225Optional explicit overrides:226227```bash228python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}" --icon "robot" --color "purple"229```230231Then set:232- `WFWorkflowIconGlyphNumber = icon.glyph_number`233- `WFWorkflowIconStartColor = color.value`234235The resolver supports natural-language icon requests (e.g. `paper airplane icon`, `terminal icon`, `expense icon`) and automatic icon selection when no icon is requested.236237## Preflight Validator — Craig Loop (Required)238239After generating a shortcut, run the validator in a **fix loop** (Craig Loop). Each iteration: read the errors, make a targeted fix, re-validate. Do not re-run without changing something. If the Codex `PostToolUse` hook reports validator feedback after an edit, treat that as the current validator run and fix the reported errors before signing.240241```bash242python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml243```244245### Craig Loop Protocol2462471. **Run the validator.** If it passes, proceed to signing.2482. **Read ALL error messages.** The validator prints every error it finds — fix as many as possible in one pass, not just the first one.2493. **Make targeted fixes** in the plist XML based on the error messages. Each error includes the action index and identifier so you know exactly where to edit.2504. **Re-run the validator.** Repeat from step 1.2515. **Exit conditions** (stop looping and report to the user):252 - **Max 5 iterations.** If the validator still fails after 5 fix attempts, stop. Summarize the remaining errors and ask the user for guidance.253 - **Same errors repeating.** If the same error persists across 2 consecutive iterations despite attempted fixes, stop. The fix approach is wrong — do not keep trying the same thing.254 - **Known validator gaps.** Only waive validator failures if `BEST_PRACTICES.md` lists a current, runtime-verified false positive. Otherwise fix the shortcut or stop and report the exact remaining errors.255256### Anti-patterns (do NOT do these)257- **Chatting the validator**: Running the validator repeatedly without making meaningful code changes between runs. Every re-run must follow a real edit.258- **Cosmetic fixes**: Rearranging comments or renaming variables to "try something" when the error is about wiring or missing parameters.259- **Regenerating from scratch** when only 1-2 specific actions need fixing. Targeted edits preserve working wiring.260261### Data sources262The validator uses bundled ToolKit snapshot IDs from packaged `data/toolkit-v*-tool-ids.json` files, filtered by target OS version and target platform, then augments with [`ACTIONS.md`](ACTIONS.md), [`APPINTENTS.md`](APPINTENTS.md), and [`THIRD_PARTY_ACTIONS.md`](THIRD_PARTY_ACTIONS.md). The default OS target is `auto` (`sw_vers` on macOS, macOS 26 when the host cannot be detected). The default platform target is `macos`. Use `--target-macos 27` or `SHORTCUTS_PLAYGROUND_TARGET_MACOS=27` only when building OS 27-era shortcuts that need target-gated macOS v78 identifiers or OS 27-only parameters such as `WFAllowWebSearch` / `FollowUp` on Use Model, `interpretAsMarkdown`, `WFAvoidTolls`, and Safari Tab Group `contents`. Use `--target-platform ios` / `SHORTCUTS_PLAYGROUND_TARGET_PLATFORM=ios` only for iPhone/iPad authoring, and `--target-platform all` only for intentional cross-platform metadata audits. For OS 27 targets, the validator also uses `data/toolkit-v78-first-party-parameter-keys.json` to reject unknown top-level keys on first-party `com.apple.*` AppIntent-style actions. It does not read the user's live ToolKit SQLite database during normal validation.263264For reviewed Apple-derived macOS 27 schema grounding and automation trigger metadata, use the static catalogs only:265266```bash267python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier additemtolist --target-macos 27268python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --python-name when_app_opened --target-macos 27269python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier com.apple.HearingApp.MuteVolumeIntent --target-macos 27 --target-platform ios270```271272`data/macos27-shortpy-grounding.json` may improve parameter/schema confidence, `data/toolkit-v78-first-party-enum-cases.json` may improve picker-value selection, `data/toolkit-v78-trigger-parameter-keys.json` may improve automation-trigger discovery, and `data/macos27-workflow-trigger-samples.json` may show sanitized exported `WFWorkflowTriggers` shapes for observed OS 27 automation headers. None of them overrides validator target gating. The lookup helper reports target-platform availability notes for iOS-only/macOS-only ToolKit rows. Exported automation-bearing shortcuts are the authority for portable automation-header authoring; do not infer that carrier from ToolKit metadata alone.273274### Escape-hatch comments275If a request explicitly requires vCard/VCF formatting or file-based token loading, add a Comment containing `ALLOW_VCARD` or `ALLOW_TOKEN_FILE` so the validator can allow it. Other escape hatches: `ALLOW_MANUAL_UNIT_CONVERSION`, `ALLOW_DATETIME_FORMAT`.276277### Wiring Regression Suite (Recommended)278279When changing wiring logic or validator rules for Weather/Location actions, run the bulk regression suite:280281```bash282python3 "$SKILL_DIR/scripts/test_wiring_regressions.py" --write-fixtures /tmp/shortcuts-wiring-regressions283```284285The suite generates and validates:286- 43 Weather Detail cases (21 valid + 22 invalid)287- 40 Location parameter cases (20 valid + 20 invalid)288- 16 Set Name/Rename File cases (8 valid + 8 invalid)289290It exits non-zero if any case behavior regresses.291292### Random Mixed-Action Stress Suite (Recommended)293294For broad randomized coverage (brand-new shortcuts, 10+ distinct actions each), run:295296```bash297python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10298```299300Behavior:301- Generates brand-new random shortcuts under `--output-dir` when provided, otherwise `SHORTCUTS_PLAYGROUND_OUTPUT_DIR/<YYYY-MM-DD>/random-mixed-actions-<runid>/` or `~/Documents/Shortcuts Playground/<YYYY-MM-DD>/random-mixed-actions-<runid>/`.302- Enforces a minimum distinct action count per shortcut (`--min-actions`, default `10`).303- Runs validate/retry loops per case (`--max-attempts`, default `20`).304- Writes `manifest.json`, `results.json`, and `summary.md` for documentation.305306Optional:307308```bash309python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --sign310```311312For OS 27 coverage, opt in explicitly so older users and CI jobs do not validate Golden Gate-only actions by accident:313314```bash315python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --target-macos 27 --include-os27-actions --sign316```317318The OS 27 module adds Stored Content, Add Item to List, Otherwise If, Get Selected Text, Get What's On Screen, and Get Current VPN to every generated shortcut.319320Use this suite when you need randomized multi-action regression coverage beyond targeted wiring tests.321322## Signing Shortcuts323324Shortcuts MUST be signed before they can be imported. The Codex skill ships `scripts/sign_shortcut.sh`, which combines **archive + sign** into a single command:325326```bash327# Archives the unsigned XML under $output_dir/$(date +%F)/ and writes a signed .shortcut to $output_dir.328"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut"329330# Override the signing mode (default is SHORTCUTS_PLAYGROUND_SIGNING_MODE, falling back to 'anyone').331"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut" --mode people-who-know-me332```333334The underlying pipeline is still the macOS `shortcuts` CLI:335336```bash337shortcuts sign --mode anyone --input MyShortcut.shortcut --output MyShortcut.shortcut338```339340The signing process:3411. Write your plist as XML to a `.shortcut` file by default.3422. Run `scripts/sign_shortcut.sh` (or `shortcuts sign` directly) to add the cryptographic signature (~19KB added). The wrapper archives the XML, then retries a validator-clean format failure after binary plist conversion when Apple's XML signer chokes.3433. Keep the signed output filename equal to the intended display name (no `_signed` suffix).3444. The signed file can be opened/imported into Shortcuts.app.345346Signing gotchas:347- If `shortcuts sign` reports `Error: The file doesn't exist.` but the file exists, copy the XML plist directly to a clean `.shortcut` path and retry (example: `cp source.xml /tmp/MyShortcut.shortcut`).348- If `shortcuts sign` reports `Error: The file couldn't be opened because it isn't in the correct format.` while `validate_shortcut.py` and `plutil -lint` pass, retry after `plutil -convert binary1` on the final `.shortcut` copy before blaming the plist. `scripts/sign_shortcut.sh` performs this retry automatically; if both attempts fail, suspect Codex `workspace-write` sandbox restrictions before treating the XML as malformed.349- `ERROR: Unrecognized attribute string flag '?'` warnings are noisy but can be non-fatal if the output file is produced.350- The `shortcuts` CLI supports `run`, `list`, `view`, and `sign`; do not assume `delete`, `rename`, or `import` subcommands.351352## Archive Raw XML (Required)353354Before signing, **archive the unsigned XML** in a date/time folder for inspection. `scripts/sign_shortcut.sh` does this for you, but the rules below still apply when you invoke `shortcuts sign` directly.355356Folder structure rule:357- The archive root is `SHORTCUTS_PLAYGROUND_OUTPUT_DIR` (falls back to `~/Documents/Shortcuts Playground/` when unset).358- Inside the archive root, create a **date folder** for the current day (`YYYY-MM-DD`) if it doesn't exist.359- Copy the unsigned XML into that date folder with a time-stamped filename.360361Example (output dir = `~/Documents/Shortcuts Playground`):362- Archive folder: `~/Documents/Shortcuts Playground/2026-02-03/`363- Archive file: `My Shortcut-142355.xml`364365Command pattern (the one-liner `scripts/sign_shortcut.sh` wraps):366```bash367OUTPUT_DIR="${SHORTCUTS_PLAYGROUND_OUTPUT_DIR:-$HOME/Documents/Shortcuts Playground}"368ARCHIVE_ROOT="$OUTPUT_DIR/$(date +%F)"369mkdir -p "$ARCHIVE_ROOT"370cp "/path/to/My Shortcut.xml" "$ARCHIVE_ROOT/My Shortcut-$(date +%H%M%S).xml"371```372373The archive copy must be the **unsigned, raw XML** (not the signed `.shortcut`).374375## Workflow for Creating Shortcuts3763770. **Research external APIs** - For complex/unfamiliar APIs, read the latest official docs before drafting request code.3781. **Define actions** - List what the shortcut should do3792. **Generate UUIDs** - Each action that produces output needs a unique UUID3803. **Build action array** - Create each action dictionary with identifier and parameters3814. **Wire variable references** - Connect outputs to inputs using `OutputUUID`3825. **Resolve icon and color** - Run `scripts/select_shortcut_icon_color.py` with the full user prompt (plus any explicit icon/color hints) and use the returned values3836. **Wrap in plist** - Add the root structure with icon, name, version3847. **Write to file** - Save as `.shortcut` (XML plist format is fine)3858. **Preflight validation** - Run the Craig Loop (see above): validate → fix → re-validate, max 5 iterations.3869. **Archive + Sign (required)** - Run `"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/file.xml --name "Final Name"`. This script archives the unsigned XML to `SHORTCUTS_PLAYGROUND_OUTPUT_DIR/$(date +%F)/` or `~/Documents/Shortcuts Playground/$(date +%F)/` and writes the signed `.shortcut` alongside it. Never leave the signing output filename with a `_signed` suffix.38710. **Verify signed output (required)** - Confirm the signed `.shortcut` path reported by `sign_shortcut.sh` exists and has non-zero size before reporting done. Stopping at "validation passed" is a failed build.388389After step 8 succeeds, go directly to step 9. Any edit after validation, including comment or wording polish, requires another validation and another signing pass.390391## Comment Blocks (Repair-Oriented)392393Because variable wiring can require manual fixes, add a **concise Comment before each major block** with a **bulleted list** describing which variables must be connected. Keep it short, specific, and focused on wiring (e.g., “Use Repeat Item 2 inside inner loop”).394Write comments with Shortcuts UI wording (for example, `Input`, `Date`, `Provided Input`, `Repeat Item`, `Text`) and readable action names (`Ask for Input`, `Text`, `Save File`). Do **not** use plist key jargon like `WFInput` / `WFDate` / `WFImage` in Comment text. **NEVER include UUIDs, OutputUUID references, or technical plist details in Comment text** — comments must be descriptive natural language only.395Prefer wording like `- Input uses the text output from the Text action above` and `• Date uses the user's answer from Ask for Input` instead of `WF*` field names or UUID references.396397## Key Rules3983991. **UUIDs must be uppercase and generated via `uuidgen`, not hand-picked.** Before emitting a shortcut, run a single Bash call to generate all the UUIDs you'll need:400 ```bash401 for i in $(seq 1 <N>); do uuidgen | tr '[:lower:]' '[:upper:]'; done402 ```403 Where `<N>` is the number of action UUIDs the shortcut requires (one per action that produces output or is referenced by downstream actions). Assign each output line to a specific action in your working map, then paste them into the plist. **Never use sequential placeholders** like `11111111-1111-1111-1111-111111111111`, `AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA`, or any other pattern where every hex character is the same — the validator rejects repeating-hex UUIDs as a hard error. Valid example shape: `7F3A4E91-C2D8-4B56-BE5A-0242AC120002`.4042. **WFControlFlowMode is an integer**: Use `<integer>0</integer>` not `<string>0</string>`4053. **Range keys use format**: `{position, length}` - e.g., `{0, 1}` for first character4064. **The placeholder character**: `` (U+FFFC) marks where variables are inserted4075. **Control flow needs matching ends**: Every repeat/if/menu start needs an end action with same `GroupingIdentifier`4086. **Match placeholder positions**: `attachmentsByRange` must point to the exact index of each `` in the final string4097. **No out-of-bounds ranges**: `attachmentsByRange` positions beyond string length can crash Shortcuts on import4108. **Avoid empty placeholders**: Omit unused keys or fields; do not leave empty values where the action expects data4119. **WFURL serialization**: For `WFURL` parameters (especially `downloadurl`), use `WFTextTokenString` with `` placeholders even when the URL is entirely a variable; reserve `WFTextTokenAttachment` for parameters that are explicitly variable-only (e.g., `WFVariable`, `WFRequestVariable`)41210. **Form file fields and WFRequestVariable**: For `WFHTTPBodyType = Form`, file fields (`WFItemType = 5`) must wrap the file reference in `WFTokenAttachmentParameterState` with an inner `WFTextTokenAttachment` so the UI shows the connected file variable. Set `WFRequestVariable` only when body type is `File` (JSON Text fallback pattern)41311. **Array fields**: For `WFDictionaryFieldValue` items with `WFItemType = 2`, use `WFArrayParameterState` with a list of items (`WFItemType` + `WFValue`), not numeric keys inside another dictionary41412. **Format Date input**: Set `WFDate` (not `WFInput`) as a `WFTextTokenString` placeholder wired to a Date output; never leave `WFDate` empty41513. **Repeat loops**: Inside **Repeat with Each**, use **Repeat Item** for per-item extraction; avoid **Repeat Results** inside the loop41614. **Reuse extractions**: If you extract multiple fields from a dictionary/API response, reuse each value later or remove the unused extraction41715. **Variable placeholders**: When composing strings or URLs with variables (Ask for Input, Track/Artist, etc.), insert a placeholder for every variable and align `attachmentsByRange` positions41816. **Dictionary dot notation**: Use dot notation (with 1-based indexes) in `WFDictionaryKey` to access nested keys (e.g., `results.tracks.items`, `artists.1.name`), but guard optional parents first (avoid direct `error.message` on raw API responses)41917. **Conditional inputs (verified against Apple-built samples)**: For `is.workflow.actions.conditional` with `WFControlFlowMode = 0`, **every** condition code requires an explicit `WFInput` set as `{ Type: "Variable", Variable: { Value: <ActionOutput or Variable>, WFSerializationType: "WFTextTokenAttachment" } }`. There is no implicit-input mode. Per-code literal field requirements: string codes `4`/`5`/`8`/`9`/`99`/`999` need `WFConditionalActionString`; numeric codes `0`/`1`/`2`/`3` need `WFNumberValue`; numeric `1003` (`is between`) needs both `WFNumberValue` (lower) and `WFAnotherNumber` (upper, attachment); existence codes `100`/`101` need neither literal field. Code `0` is `is less than` (NOT equals); codes `0`–`3` are inequalities. Multi-condition Ifs use `WFConditions` with `WFContentPredicateTableTemplate` serialization; do not mix `WFConditions` with top-level `WFCondition`. macOS 27 `Otherwise If` is the same conditional identifier with `WFControlFlowMode = 1` plus condition fields. Plain Otherwise is mode 1 with no condition fields; End If is mode 2. For JSON booleans, compare numerically (`1`/`0`); treat JSON `null` as empty. See `CONTROL_FLOW.md` "Condition Codes" and "Multi-condition If" for the complete reference and templates.42018. **Workflow icon keys are mandatory**: Always set both `WFWorkflowIconGlyphNumber` and `WFWorkflowIconStartColor` in `WFWorkflowIcon` (use resolver output)42119. **Runtime file picking**: If the user needs to choose a file, use `is.workflow.actions.file.select` and connect its output42220. **Text token validation**: Run a final validation pass over every `WFTextTokenString` and refuse to output if any `attachmentsByRange` key does not map to a placeholder position or if counts mismatch42321. **No unrequested services**: Do not introduce third-party APIs, external CDNs, or extra import questions unless the user explicitly requests them42422. **Notion image uploads**: Default to Notion `file_uploads` + `file_upload` block type; avoid `external` image blocks unless the user supplies a URL or asks for external hosting42523. **Notion title filters**: Use the Notion `title` property name unless the user explicitly says their database uses a different title property name (e.g., `Name`)42624. **API research and endpoint accuracy**: For complex or unfamiliar external APIs, verify auth, endpoints, parameters, and payload formats against the latest official docs before assembling the shortcut. Validate endpoint strings exactly (underscore vs hyphen matters)42725. **API string sanity checks**: Before output, scan API strings for `//` (beyond protocol) and empty JSON fields where variables are expected (e.g., `equals:””`, `id:””`, `url:””`, `filename:””`), and fix them42826. **JSON request bodies**: For `WFHTTPBodyType = JSON`, use `WFJSONValues` for flat key/value payloads so the body is preserved in Shortcuts UI42927. **Format Date custom style**: When `WFDateFormatStyle` is `Custom`, set `WFDateFormat=Custom` and put the pattern in `WFDateFormatString` (e.g., `MMMM d, yyyy`, `yyyy-MM-dd`, `yyyy-MM-dd'T'HH:mm:ssXXXXX`). See DATE_TIME.md for UNIX timestamp, ISO 8601, RFC 2822, and Unicode TR35 guidance43028. **Complex JSON fallback**: If the JSON body includes arrays of objects or deep nesting and the UI renders it as `Number 0`/empty rows, use a JSON Text action and set `WFRequestVariable` to that text with `WFHTTPBodyType = File` and `Content-Type: application/json`43129. **Filename extensions**: When using **Get Name** for file uploads, append the correct extension in the JSON payload (e.g., `.png`) and do not rely on the base name alone43230. **Count input visibility**: For `is.workflow.actions.count`, set both `WFInput` and `Input` to the same variable so the UI shows the selected list43331. **Get Name web titles**: For file names, set **Get Web Page Title** to **Off** in **Get Name** so the action returns the file name, not a URL title43432. **String If workaround**: If validator rules conflict on string conditionals, use `Match Text` + `Count` + numeric `If` instead of direct string `If`43533. **Replace Text empty replacement**: For delete-match patterns, prefer omitting `WFReplaceTextReplace`; an explicit empty string is allowed, but omission is cleaner and more portable43634. **Base64 input wiring**: Always set `WFInput` for `is.workflow.actions.base64encode`; implicit input can import as an empty field and break runtime43735. **Replace Text input visibility**: For `is.workflow.actions.text.replace`, use a `WFTextTokenString` placeholder (or wrapped variable input), not a bare `WFTextTokenAttachment`43836. **Adjust Date reliability**: Use `WFDate` + non-empty `WFDuration` for `is.workflow.actions.adjustdate` (optionally mirror with `WFInput`); include `WFAdjustOperation` when explicit Add/Subtract is required. Offset-picker-only payloads can import as `Add 0 seconds` on iOS43937. **Convert Image wiring**: For `is.workflow.actions.image.convert`, always set `WFInput` explicitly; do not rely on implicit input chaining44038. **Weather detail wiring**: For `is.workflow.actions.properties.weather.conditions`, set both `WFInput` and `WFContentItemPropertyName`, keep `WFContentItemPropertyName` concrete (never placeholder `Detail`), and wire `WFInput` directly to an ActionOutput from `is.workflow.actions.weather.currentconditions` / `is.workflow.actions.weather.forecast` (no named-variable hop). Supported detail names are `Date`, `Location`, `Temperature`, `Low`, `High`, `Feels Like`, `Condition`, `Visibility`, `Dewpoint`, `Humidity`, `Pressure`, `Precipitation Amount`, `Precipitation Chance`, `Wind Speed`, `Wind Direction`, `UV Index`, `Sunrise Time`, `Sunset Time`, `Air Quality Index`, `Air Quality Category`, `Air Pollutants`, and `Name`. `Sunrise Time` and `Sunset Time` from Daily forecasts are lists: insert `Get Item from List` before `Format Date`, using First Item for sunrise and Last Item for sunset44139. **Time Between Dates input wiring**: For `is.workflow.actions.gettimebetweendates`, set `WFInput` and exactly one non-empty date operand (`WFDate` or `WFTimeUntilCustomDate` or `WFTimeUntilFromDate`) as `WFTextTokenString` placeholders. To compare with now, first add a Date action set to Current Date and reference that action output; never put a direct `CurrentDate` magic token in the action. `WFTimeUntilUnit` may be omitted only when intentionally using the default unit. Never emit empty unused date keys44240. **Extract from Image input wiring**: For `is.workflow.actions.extracttextfromimage`, set exactly one non-empty image input key. On OS 27+, prefer `imageFile`; older exported shortcuts may use `WFImage` or, when intentionally required by an existing pattern, `WFInput`.44341. **API error extraction safety**: Do not read `error.message` directly from a raw response; extract `error`, guard it with `If Has Any Value`, then read `message`44442. **Continuation JSON array closure**: When appending to a JSON array via `Replace Text` on `\]$`, the replacement must end with `]`; missing the closing bracket corrupts JSON and causes `Detect Dictionary` to return empty44543. **No raw object/list tokens inside JSON text**: Do not inject Dictionary/List outputs (for example, raw API `content` arrays) directly into JSON Text templates; Shortcuts may stringify them as newline-separated blocks rather than valid JSON44644. **Continuation payload safe pattern**: For multi-turn handoff payloads, append assistant text from a plain text variable (for example, `Response Text`) and keep `messages_json` as valid JSON before rerunning the shortcut44745. **JSON string interpolation safety**: Before inserting freeform text into JSON Text templates (`”content”:””`), sanitize it first (at minimum handle backslashes, double quotes, and control whitespace/newlines) or the next `Detect Dictionary` step will fail44846. **Shortcuts URL schemes**: Only use Apple-documented `shortcuts://` routes from URL_SCHEMES.md. URL-encode every query value; do not invent import/install routes or extra parameters44947. **Run JavaScript on Webpage**: Use `is.workflow.actions.runjavascriptonwebpage` only for Safari webpage share-sheet shortcuts. Include `ActionExtension`, scope input to `WFSafariWebPageContentItem`, call `completion(...)` or `completion()`, return JSON-compatible values, and avoid synchronous dialogs/long timers45046. **API response parse stability**: For `downloadurl` JSON APIs, keep `ShowHeaders` off unless explicitly needed and run `Detect Dictionary` on `Contents of URL` before any `Get Dictionary Value` extraction45147. **Action input keys matter**: `Replace Text` uses `WFInput`, while `Change Case` and `Split Text` use `text`; wrong keys import but show empty inputs in the editor45248. **Split Text custom separator**: If `WFTextSeparator` is `Custom`, always include `WFTextCustomSeparator` (a single space `” “` is valid)45349. **Find Notes filter state**: `WFContentItemFilter` must be `WFContentPredicateTableTemplate` with non-empty templates; for `Folder` filters use `WFLinkDynamicOptionSubstitutableState` wrapping a tokenized variable45450. **Direct variable wiring**: Prefer inserting named variables directly into action input fields; avoid redundant `Get Variable → next action` hops unless there is a clear transformation need45551. **Location parameter wiring**: Never emit empty location parameters. For `WFLocation`, `WFWeatherCustomLocation`, and `WFWeatherLocation`, use `WFTextTokenAttachment` (not token strings) and reference a Get Current Location/Location output (directly or via a variable sourced from those outputs). `is.workflow.actions.location` must include a non-empty `WFLocation` attachment; missing/blank payloads import as empty “Location” fields45652. **Set Name vs Rename File**: Use **Set Name** as `is.workflow.actions.setitemname` with `WFInput` (source file/item) and `WFName` (target filename, for example `Test.txt`) when a workflow needs a renamed file object to save or share elsewhere. Its output is **Renamed Item**. Do not confuse this with **Rename File** (`is.workflow.actions.file.rename` with `WFFile` and `WFNewFilename`), which renames the original file in place at its existing path. For "rename, save/share elsewhere, then delete original" workflows, store the picked file as **Original File**, run Set Name on **Original File**, save/share **Renamed Item**, then delete **Original File** only after the save/share step if requested45753. **⚠️ WFMathOperation syntax (verified against Shortcuts app)**: For `is.workflow.actions.math`: (a) **Addition**: OMIT the `WFMathOperation` key entirely — no key means addition; (b) **Subtraction**: `-` (ASCII minus, U+002D); (c) **Multiplication**: `×` (U+00D7, ord 215, Unicode MULTIPLICATION SIGN) — NEVER `*`; (d) **Division**: `÷` (U+00F7, ord 247, Unicode DIVISION SIGN) — NEVER `/`; (e) **Scientific ops** (Modulus, Power, etc.): `WFMathOperation='…'` (U+2026 horizontal ellipsis) as placeholder, with real op in `WFScientificMathOperation` and operand in `WFScientificMathOperand`. **Literal operands** (`WFMathOperand`) must be plain strings like `"10"`, not wrapped dicts. Shortcuts silently renders ASCII `/` as `+` in the UI with no error. See PARAMETER_TYPES.md "Math and Counting Operations" for verified examples.45854. **Never inspect the user's local system for authoring discovery unless the user explicitly asks for that local evidence.** If an action identifier is allowlisted in `data/toolkit-v*-tool-ids.json` but its parameter schema is not documented in the bundled reference files (`ACTIONS.md`, `APPINTENTS.md`, `PARAMETER_TYPES.md`, `FILTERS.md`, `EXAMPLES.md`, `BEST_PRACTICES.md`, `HEALTHKIT.md`, static `data/macos27-shortpy-grounding.json`, or the `golden-shortcuts/` library), **stop and ask the user** - do not try to reverse-engineer the schema by reading local databases, inspecting system binaries, querying Shortcuts.app internals, or searching cloud-backup folders without explicit permission. Escalate to the user with three options: (a) best-effort guess + iterate after they import, (b) use a simpler alternative action you propose, or (c) they paste a working example for you to mirror. When the user explicitly supplies or requests local exported XML, prefer that evidence over web references.45955. **Automation triggers are sample-gated.** `AUTOMATION_TRIGGERS.md`, `data/toolkit-v78-trigger-parameter-keys.json`, and `data/macos27-workflow-trigger-samples.json` document OS 27 ToolKit trigger IDs, Python names, parameter keys, output types, and sanitized exported `WFWorkflowTriggers` payloads. Use exported automation-bearing shortcuts as the source of truth for portable authoring. Do not invent trigger plists from ToolKit metadata alone460461…(truncated)