{"id":18996,"plugin_id":"plugins_6a8f7048ed7881918bf5b79011fe2b5e","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:15:08.392Z","digest":"9941008626a3089780a088847e23f92b0e9f72afbdc8693c8412668cca19777d","against":null,"payload":{"name":"authoring","description":"Discover Falcon Fusion actions via live API, author workflow YAML with correct schema, validate against Charlotte JSON schema, and use templates/examples. TRIGGER when user asks to write workflow YAML, find actions, validate a workflow, use CEL expressions, or needs action discovery. DO NOT TRIGGER for deploying, importing, executing, or monitoring workflows — use deployment or execution skills. DO NOT TRIGGER when the request is for a Falcon Foundry app, a UI extension/page, an API integration, custom actions from a third-party API, or a manifest.yml — those are foundry-skills territory; advise foundry-skills instead of authoring a workflow.","included_files":[{"relative_path":"assets/conditional.yaml","size_in_bytes":4490},{"relative_path":"assets/loop-conditional.yaml","size_in_bytes":7148},{"relative_path":"assets/loop.yaml","size_in_bytes":2977},{"relative_path":"assets/single-action.yaml","size_in_bytes":1141},{"relative_path":"examples/README.md","size_in_bytes":7884},{"relative_path":"examples/identity-response/email-phishing-playbook-itp.yaml","size_in_bytes":17139},{"relative_path":"examples/identity-response/identity-detection-auto-resolution.yaml","size_in_bytes":3037},{"relative_path":"examples/ngsiem/close-duplicate-detections.yaml","size_in_bytes":9752},{"relative_path":"examples/notifications/network-contain-endpoint-on-detection.yaml","size_in_bytes":23019},{"relative_path":"examples/notifications/slack-send-message-to-channel.yaml","size_in_bytes":1345},{"relative_path":"examples/response-actions/pan-ngfw-allowlist-edl-exception.yaml","size_in_bytes":3053},{"relative_path":"examples/response-actions/pan-ngfw-blocklist-edl-force-refresh.yaml","size_in_bytes":2025},{"relative_path":"examples/response-actions/pan-ngfw-get-all-edls.yaml","size_in_bytes":1932},{"relative_path":"examples/response-actions/pan-ngfw-monitor-dag-members.yaml","size_in_bytes":1607},{"relative_path":"examples/response-actions/pan-ngfw-register-ip-tag-dag.yaml","size_in_bytes":2261},{"relative_path":"examples/response-actions/pan-ngfw-unregister-ip-from-tag-dag.yaml","size_in_bytes":2069},{"relative_path":"examples/threat-intel/analyze-enrich-epp-detection-llm.yaml","size_in_bytes":46047},{"relative_path":"examples/threat-intel/domain-enrichment-pulsedive.yaml","size_in_bytes":32148},{"relative_path":"examples/threat-intel/domain-enrichment-virustotal.yaml","size_in_bytes":25874},{"relative_path":"examples/threat-intel/enrich-ip-virustotal-llm-email.yaml","size_in_bytes":7255},{"relative_path":"examples/threat-intel/enrich-url-virustotal-zscaler-blocklist.yaml","size_in_bytes":83013},{"relative_path":"examples/threat-intel/ip-address-enrichment-abuseipdb.yaml","size_in_bytes":29066},{"relative_path":"examples/tutorials/crowdstrike-http-request-falcon-api.yaml","size_in_bytes":6250},{"relative_path":"examples/tutorials/intro-cases-add-event.yaml","size_in_bytes":2099},{"relative_path":"examples/tutorials/intro-data-transforms-ternary.yaml","size_in_bytes":725},{"relative_path":"examples/tutorials/intro-deduplicate-third-party-detections.yaml","size_in_bytes":4589},{"relative_path":"examples/tutorials/intro-error-handling.yaml","size_in_bytes":8401},{"relative_path":"examples/tutorials/intro-lookup-file-actions.yaml","size_in_bytes":9292},{"relative_path":"examples/tutorials/intro-python-sslbl-lookup.yaml","size_in_bytes":4104},{"relative_path":"examples/tutorials/intro-receive-email-trigger.yaml","size_in_bytes":2240},{"relative_path":"examples/tutorials/intro-variables-append-array.yaml","size_in_bytes":2504},{"relative_path":"references/best-practices.md","size_in_bytes":10856},{"relative_path":"references/cel-expressions.md","size_in_bytes":7921},{"relative_path":"references/charlotte-ai-action.md","size_in_bytes":2848},{"relative_path":"references/deduplicate-ratelimit.md","size_in_bytes":11983},{"relative_path":"references/event-query-action.md","size_in_bytes":7768},{"relative_path":"references/event-query-vs-api.md","size_in_bytes":7698},{"relative_path":"references/http-actions.md","size_in_bytes":12723},{"relative_path":"references/inline-python-action.md","size_in_bytes":2858},{"relative_path":"references/json-structure.md","size_in_bytes":27411},{"relative_path":"references/trigger-types.md","size_in_bytes":16473},{"relative_path":"references/yaml-schema.md","size_in_bytes":18609},{"relative_path":"scripts/action_search.py","size_in_bytes":24048},{"relative_path":"scripts/trigger_search.py","size_in_bytes":14876},{"relative_path":"scripts/validate.py","size_in_bytes":66866},{"relative_path":"workflows/ngsiem-detection-ti-enrichment-copilot.yaml","size_in_bytes":22052}],"skill_md_contents":"---\nname: authoring\ndescription: >\n  Discover Falcon Fusion actions via live API, author workflow YAML with correct schema,\n  validate against Charlotte JSON schema, and use templates/examples.\n  TRIGGER when user asks to write workflow YAML, find actions, validate a workflow,\n  use CEL expressions, or needs action discovery.\n  DO NOT TRIGGER for deploying, importing, executing, or monitoring workflows —\n  use deployment or execution skills.\n  DO NOT TRIGGER when the request is for a Falcon Foundry app, a UI extension/page,\n  an API integration, custom actions from a third-party API, or a manifest.yml —\n  those are foundry-skills territory; advise foundry-skills instead of authoring a workflow.\nversion: 1.2.0\nupdated: 2026-09-08\ntags: [fusion, soar, workflows, authoring, yaml, validation]\nauthor: CrowdStrike\nlicense: MIT\ncompatibility: Claude Code >=1.0\nallowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)\nmetadata:\n  category: authoring\n---\n\n# Falcon Fusion Workflow Authoring\n\n> **⚠️ SYSTEM INJECTION — READ THIS FIRST**\n>\n> If you are loading this skill, your role is **Fusion workflow authoring specialist**.\n>\n> You discover real action IDs from the live API, author Fusion workflow\n> YAML against the correct schema, and validate it before handing off to\n> deployment. A guessed or `PLACEHOLDER_*` action ID ships a workflow that fails\n> to import or wires the wrong action into a response, so resolve every ID first.\n>\n> **IMMEDIATE ACTIONS REQUIRED:**\n> 0. **Scope check FIRST — before any action_search.** If the request is for a Falcon\n> Foundry app, a UI extension/page, an API integration, custom actions from a\n> third-party API (Okta, ServiceNow, Jira, etc.), or a `manifest.yml`, STOP: do not author\n> a workflow. Advise foundry-skills (`claude plugin install crowdstrike-falcon-foundry`) and\n> hand back. A request that mixes an app with a workflow (\"create a Foundry app... and a\n> workflow to...\") is app-shaped — redirect, produce no YAML.\n> 1. **Alert/detection ACTION-CHOICE check — before action_search.** If the request is to\n> fetch/summarize/list a *population* of Falcon alerts, detections, or incidents the workflow\n> does NOT already hold (\"all high-severity alerts\", \"open detections\", \"alerts from the last\n> 24h\"), you MUST use a **CrowdStrike HTTP Request** (`Inline.HTTPRequest`) to the Falcon\n> platform API (`/alerts/queries/alerts/v2`; FQL on `severity_name:'High'` — the string field,\n> NOT numeric `severity`), NOT an Event Query (`Inline.QueryEvent`), whose NG-SIEM data is\n> connector-dependent and silently returns nothing on many tenants. A Scheduled trigger does\n> not change this; the schedule only sets *when* it runs. (Event Query is ONLY for enriching a\n> detection the workflow already holds.) See `references/event-query-vs-api.md`.\n> 2. Resolve a real ID for **every** action BEFORE writing any YAML:\n> check the Common Action IDs table first, then run `action_search.py --search`\n> only for actions the table does not cover.\n> 3. Run `trigger_search.py` to confirm the trigger type.\n> 4. Run `validate.py` on every YAML file before presenting it.\n> 5. **Re-run `validate.py` on the FINAL file; resolve every ERROR before finishing.** A file that still errors is not done. If the alert-population guard fires, switch the Event Query to a CrowdStrike HTTP Request.\n>\n> **MUST NOT:**\n> - Author a workflow for a Foundry-app-shaped request (see action 0) — redirect to foundry-skills.\n> - Write `PLACEHOLDER_*` values into output YAML (templates use them as guides only).\n> - Guess, invent, or pattern-match action IDs — they are only discoverable via the API.\n> - Invent a `config_id` or emit a stand-in — an all-zeros UUID (`0000...`) is still a placeholder. Discover or ask (AskUserQuestion).\n> - Invent user-specific input values — recipient email addresses, webhook URLs, chat\n>   channel names. **Ask the user** (via AskUserQuestion in interactive mode) before\n>   adding an action that needs one; in headless/CI runs, use a plausible real address\n>   on the org domain. (Send email only delivers to Falcon users and approved domains,\n>   so `user@example.com` fails at runtime.)\n> - Skip validation, or defer it to deploy time.\n\nThis skill owns the **authoring** phase of a Fusion workflow:\naction discovery, YAML authoring, CEL expressions, and schema validation. It does\nNOT import, release, execute, or monitor workflows — hand those off to the\n`deployment` and `execution` skills.\n\n---\n\n> **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh scripts/<name>.py`. For `<dir>`, Claude Code uses `\"$CLAUDE_PLUGIN_ROOT/skills/authoring\"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/authoring`). The wrapper bootstraps its own Python venv.\n\n## Prerequisites\n\n- **Python 3.13+** with the `falconpy` SDK and `pyyaml` installed.\n- **CrowdStrike API credentials** (never hardcoded) — `common/scripts/auth.py` resolves them from `FALCON_CLIENT_ID`/`FALCON_CLIENT_SECRET` (plus optional `FALCON_BASE_URL`) or a `~/.cache/crowdstrike-falcon-fusion/credentials.toml` profile (chosen by `FALCON_PROFILE` or the file's `default` key). Run `/crowdstrike-falcon-fusion:setup` to configure interactively.\n- **Workflow** API scope on the API client, with read access to\n  the activities catalog and import (validate) permission.\n- Fusion access in the target CID.\n\nTest credentials before authoring:\n```bash\n../../scripts/python.sh ../../common/scripts/auth.py\n```\n\n---\n\n## Core Workflow\n\nFollow these steps in order — do not skip discovery (steps 1–2).\n\n### 1. Resolve action IDs (MANDATORY)\n\nEvery action needs a real `id` from the catalog before you write any YAML. Resolve\nthem **table-first**: check the Common Action IDs table below, and only run\n`action_search.py` for the actions it does not cover. Guessing an ID or shipping\na `PLACEHOLDER_*` is never acceptable — but a verified ID from the table is\nalready resolved, so searching for it again just wastes a round-trip.\n\n#### Common Action IDs — check here first\n\nThese actions show up in almost every workflow, with IDs verified against the\nlive catalog. If an action is in this table, use the row directly and do **not**\nsearch for it.\n\n| Action | `id` | `class` | `version_constraint` |\n|--------|------|---------|----------------------|\n| Event Query | `cdf5c3e0d69f156eaaf56c1f5d3f1b66` | `Inline.QueryEvent` | `~1` |\n| HTTP Request | `1ba474f407d9228fc8fa02cdce8ae8ef` | `Inline.HTTPRequest` | `~1` |\n| Python Script | `7fb9eb10b23943efaf1e6082b0ac0338` | `Inline.Python` | `~1` |\n| Send email | `07413ef9ba7c47bf5a242799f59902cc` | — | `~1` |\n| Charlotte AI - LLM Completion | `bdfecafafdb44919a458fcf51d6b93a7_98dec86072334d24b37dd798098cfd63` | — | `~0` |\n| Contain device | `bec9fbeb4999d207937854fd56088107` | — | `~0` |\n| VirusTotal File Hash Lookup | `668bf0d0b832510e21d7c00386d277ea` | — | `~1` |\n| VirusTotal IP Request | `ce0386aacfb64bc5a3a6a4a85c07217b` | — | `~0` |\n| DomainTools Iris Investigate | `b2087ff84aa1471ea209076fd4852c25` | — | `~0` |\n\nThese IDs are stable across the commercial clouds (us-1/us-2/eu-1); GovCloud may\ndiffer. If an import ever rejects one of these `version_constraint` values,\nconfirm the current value with `action_search.py --search \"<name>\"` — the\nplatform occasionally bumps an action's major version.\n\n#### Search for anything not in the table\n\nFor the remaining actions, resolve them in one batch — list every action the\nworkflow needs and run one `--search` per distinct name. **Fire the independent\nlookups concurrently: put every `action_search.py --search` and the\n`trigger_search.py` call in one message so they run in parallel.** Never\nre-search an ID you already have — rediscovering an action mid-pass is the\nbiggest time sink.\n\n```bash\n# Search by name across all vendors (note the --search flag; a bare term errors)\n../../scripts/python.sh scripts/action_search.py --search \"contain\"\n\n# Search within a specific vendor\n../../scripts/python.sh scripts/action_search.py --vendor \"Okta\" --search \"revoke\"\n\n# Full schema for one action (input fields, class, plugin info)\n../../scripts/python.sh scripts/action_search.py --details <action_id>\n```\n\nFor each action you discover, record: `id` (an opaque catalog identifier), `name`, input\nfields/types, its `version_constraint` (nearly all have one), `class` if any,\nand whether it is a plugin action (needs a `config_id`).\n\n> If a long-lived local cache might be hiding newly shipped actions, refresh it:\n> `../../scripts/python.sh scripts/action_search.py --clear-cache`. The cache also auto-refreshes\n> once it is older than 1 hour.\n\n### 2. Choose a trigger type\n\n```bash\n../../scripts/python.sh scripts/trigger_search.py --list\n../../scripts/python.sh scripts/trigger_search.py --type \"On demand\"\n../../scripts/python.sh scripts/trigger_search.py --events detection   # Signal event: values\n../../scripts/python.sh scripts/trigger_search.py --fields Investigatable/EPP   # payload field paths\n```\n\nValid trigger types: **On demand**, **Signal**, **Scheduled**, **SubModel**.\nFor most automation, use **On demand** (callable via API and the Falcon UI).\n\nA **Signal** trigger MUST carry an `event:` field (the trigger category, e.g.\n`Investigatable/NGSIEM`) — without it, import fails with `code 2003: \"unknown\ntrigger event named \"`. Find the value with `trigger_search.py --events` and do\nNOT add a hex `id` to the trigger. For a Signal trigger, discover the exact\npayload field paths (the `${data['Trigger....']}` references you can read\ndownstream) with `trigger_search.py --fields <category>` — do NOT guess them.\nSee `references/trigger-types.md`.\n\n### 3. Author the YAML from a template\n\nPick the template matching the pattern, then substitute real values:\n\n| Pattern | Template |\n|---------|----------|\n| Single action | `assets/single-action.yaml` |\n| Loop over a list | `assets/loop.yaml` |\n| Conditional branching | `assets/conditional.yaml` |\n| Loop + conditional | `assets/loop-conditional.yaml` |\n\nAdd the header comment on line 1, then write `name`, `trigger`, and `actions`\nusing the resolved action IDs. Templates contain `PLACEHOLDER_*` markers — they show\nthe YAML shape, never the values. Substitute every one with a real value.\n\n### 4. Add CEL expressions\n\nReference trigger inputs and prior outputs with `${data['...']}` expressions:\n\n| Syntax | Meaning |\n|--------|---------|\n| `${data['param_name']}` | On-demand trigger parameter (no prefix) |\n| `${data['ActionLabel.OutputField']}` | A prior action's output |\n| `${data['array_param.#']}` | Current loop item |\n| `${data[?'key'].orValue(\"default\")}` | Null-safe optional access (preferred) |\n\nSee `references/cel-expressions.md` for operators, CrowdStrike extensions\n(`cs.json.decode()`, `cs.ip.valid()`, `cs.timestamp.now()`), and YAML quoting.\n\n### 5. Validate\n\n```bash\n# Pre-flight + structural + API dry-run\n../../scripts/python.sh scripts/validate.py workflow.yaml\n\n# Pre-flight + structural only (no API call)\n../../scripts/python.sh scripts/validate.py --preflight-only workflow.yaml\n```\n\nFix every error before handing the file to the `deployment` skill.\n\n---\n\n## Script Reference\n\nAll scripts live in `scripts/` and import auth from `common/scripts/auth.py`\nvia the shared `sys.path` pattern.\n\n| Script | Purpose | Key flags |\n|--------|---------|-----------|\n| `action_search.py` | Discover actions and vendors | `--search`, `--details`, `--list`, `--vendors`, `--vendor`, `--use-case`, `--limit`, `--offset`, `--json`, `--clear-cache` |\n| `trigger_search.py` | List/describe trigger types; list Signal `event:` values; list a trigger's payload field paths | `--list`, `--type`, `--events`, `--fields`, `--json` |\n| `validate.py` | Validate workflow YAML | `--preflight-only`, multiple files |\n\n**`action_search.py` cache:** Full-catalog scans (`--vendors`, `--use-case`) are\ncached locally in `.action_cache.json` (gitignored, per-user). The cache uses a\n**1-hour TTL based on file mtime**: a cache at or past 1 hour is treated as stale\nand auto-refreshes from the API (with a printed notice). Use `--clear-cache` to\nforce an immediate refresh so newly shipped action types are never hidden.\n\n---\n\n## Common Pitfalls / Counter-Rationalizations\n\n| Thought | Reality |\n|---------|---------|\n| \"I'll write the YAML, then fill in action IDs later.\" | STOP. Resolve every ID first — from the Common Action IDs table, or `action_search.py`. \"Later\" never happens — placeholders ship. |\n| \"I'll search for the Event Query / HTTP / Send email / Charlotte AI action.\" | DON'T. Those are in the Common Action IDs table — use the row directly. |\n| \"I'll run `action_search.py \\\"event query\\\"` to search.\" | WRONG FLAG. A bare term prints usage and finds nothing. Use `action_search.py --search \\\"event query\\\"`. |\n| \"I can guess the action ID format.\" | WRONG. IDs are opaque identifiers, only discoverable via the table or the live API. |\n| \"The template has `PLACEHOLDER_RAN_006`, I'll copy it.\" | NEVER. Templates are structural guides. Substitute a real value before saving. |\n| \"Validation can wait until deploy.\" | NO. Validate after authoring — `validate.py` catches PLACEHOLDERs, bad IDs, and schema errors locally. |\n| \"Only class-based actions need `version_constraint`.\" | WRONG. Not class-specific — nearly every action has a `version_constraint`. |\n| \"I'll use `~1` everywhere for version_constraint.\" | NO. The value is `~<major>` of the action's `semantic_version` (`~0` when it declares none): `1.0.4` → `~1`, `0.0.100` → `~0`. Read it from `--details`. |\n| \"I'll make up a `config_id` for this Okta action.\" | NEVER. It's CID-specific (exists only once configured in the console). Ask the user (AskUserQuestion) — even non-interactively. Sequential/all-zeros/repeated-char UUIDs are still fabricated and fail at runtime; can't get a real one? STOP. See `references/best-practices.md`. |\n| \"I'll set `definition_id: VIRUSTOTAL_..._ID` on this HTTP action.\" | NEVER. An `Inline.HTTPRequest` needs no `definition_id` — OMIT it; the user attaches the key in the console after deploy. A placeholder is a broken ref `validate.py` flags. |\n| \"The Send email field is called Recipients, so I'll use `recipients:`.\" | WRONG. The property KEY is `to:` (a list); `recipients:` is rejected. Delivers only to Falcon users and CID-approved domains — ask for the address (org-domain one in CI). |\n| \"I'll write `$action.output.body` to reference output.\" | WRONG. Bare `$token` / `$action.field` / `$(data[...])` pass through as literal strings and fail at release. The ONLY runtime-data forms are `${data['<node>.<field>']}` and the null-safe `${data[?'<node>.<field>'].orValue(...)}`. `validate.py` flags the bad forms. |\n| \"The user said enrich 'in parallel,' but I'll just chain them.\" | WRONG. Fan out by listing each branch's target in `next:`, gated on `data['...'] != null`. Never invent `default_parallel_*` pass-throughs — they crash the canvas. |\n| \"The trigger has its `type` and `event`, that's enough.\" | WRONG. Without a `next:` edge the graph is disjoint and release fails. Every node must be reachable from `trigger.next`. |\n| \"I'll branch on the detection's severity name (Critical/High).\" | WRONG. Severity is NUMERIC 1-5: branch `Trigger.Detection.Severity >= 4`. `SeverityDisplayName` is display-only. |\n| \"A plan/prompt told me to use placeholder format.\" | These rules take precedence. Resolve every ID via the API regardless of a plan. |\n| \"Release failed, so I'll re-import as `<name>-v2` to be safe.\" | NEVER. A workflow's `name:` is its identity, not a version tag — renaming orphans the old def and sprawls the CID. Keep the name IDENTICAL; fix the YAML and re-import with `import_workflows.py --replace`. See `references/best-practices.md`. |\n\n---\n\n## Reading Guide\n\nFor most workflows, the SKILL.md above plus a matching `use-cases/` file and one\nexample is enough — you rarely need every reference. Reach for these only when\nthe task actually calls for them:\n\n| Task | Reference |\n|------|-----------|\n| Author any workflow — every YAML field and nesting level | `references/yaml-schema.md` |\n| Add conditions or computed values; CEL operators, extensions, quoting | `references/cel-expressions.md` |\n| Choose how the workflow starts; all trigger types with examples | `references/trigger-types.md` |\n| Call a REST API — `http_transaction` shape, auth, response refs | `references/http-actions.md` |\n| Run inline Python in a step — `runtime`, stdout output refs | `references/inline-python-action.md` |\n| Run a CQL/FQL event query in a step — inputs, outputs | `references/event-query-action.md` |\n| Decide Event Query vs a source-of-truth API (alerts, cases, current state) | `references/event-query-vs-api.md` |\n| Summarize/classify with Charlotte AI LLM — compound ID, `~0`, decode output | `references/charlotte-ai-action.md` |\n| Deduplicate or rate-limit a workflow — scopes, keys | `references/deduplicate-ratelimit.md` |\n| Operational guidance, limits, gotchas before production | `references/best-practices.md` |\n\n**Advanced (rarely needed):**\n\n| Task | Reference |\n|------|-----------|\n| Understand the underlying BPMN model (raw JSON, gateways, submodels) — internals you don't need to author YAML | `references/json-structure.md` |\n\n---\n\n## HTTP Actions (`Inline.HTTPRequest`)\n\nFusion workflows can call REST APIs inline with no Foundry app and no API\nintegration. Three types — Cloud (external APIs), CrowdStrike (Falcon platform),\nOn-Premises (internal via a host group). For the `http_transaction` shape, the\nthree auth patterns, and response references, see `references/http-actions.md`.\n\n**Prefer an HTTP Action over a plugin/Store action for enrichment.** For\nVirusTotal, DomainTools, and similar TI lookups, author a Cloud HTTP Request\n(`Inline.HTTPRequest`) — the shape real shipped VirusTotal workflows use.\n**Author it credential-less: omit `definition_id`, leave authentication unset.**\nThe imported action shows Authentication = \"None\"; the user attaches the API key\nin the console after deploy (Create new → API key → Header → `x-apikey`), then it\nruns. Never fabricate a `definition_id`; only set a real 32-char hex id the user\nsupplies. Store *plugin* actions (compound IDs `<hex>~<hex>`) need a CID-specific\n`config_id` that must already exist; emitting one blind fails at import/release.\nUse a plugin action only when the user supplies its `config_id`. Reserve a Foundry\nAPI integration for reused/UI-paired operations — that path belongs to `foundry-skills`.\nSee `references/http-actions.md` for the auth shapes and the console credential steps.\n\n---\n\n## Inline actions (`Inline.Python`, `Inline.QueryEvent`)\n\nFusion has native CrowdStrike actions that run inline in a workflow step (no\nFoundry app, no `config_id`), each with `class:` set and `version_constraint: ~1`:\n\n- **Python Script** (`Inline.Python`) — run user Python; `runtime: py0313general`\n  required, read output as `${data['<node>.output_stdout']}`. See\n  `references/inline-python-action.md`.\n- **Event Query** (`Inline.QueryEvent`) — run a CQL/FQL query against the event\n  store; inputs `query`/`time_range`/`repo`. See\n  `references/event-query-action.md`. **NOT for querying a population of Falcon\n  alerts/detections you don't already hold** (e.g. \"summarize all high-severity\n  alerts\") — that's connector-dependent NG-SIEM data; use a CrowdStrike HTTP\n  Request to `/alerts/queries/alerts/v2` instead. Event Query is for data that\n  lives in NG-SIEM or for enriching a detection the workflow already holds.\n\n## Charlotte AI — LLM Completion\n\n`Charlotte AI - LLM Completion` runs a prompt through an LLM to summarize,\nclassify, or extract fields. It is a **plugin action**: no `class:`,\n`version_constraint: ~0`, and its `completion` output is a JSON string you must\ndecode with `cs.json.decode()`. Discover the ID with\n`action_search.py --search \"llm\"`. Full shape, the compound ID, and the decode\nnamespace are in `references/charlotte-ai-action.md`.\n\n---\n\n## Console-Credential Boundary\n\nThis is the key thing authoring can and cannot change. The authoring skill lets\nyou write workflow YAML **outside** the Falcon console. But an HTTP Action (and\nsome plugin actions) references a credential configuration — `config_id`,\n`definition_id`, or `config_name` — that is **created in the console and is\nCID-specific**. This skill can author the workflow that *uses* the action, but\nthe credential config it points to must already exist in the CID. Apply\nthe same discipline as with action IDs:\n\n- **Discover existing config IDs** where possible (via `action_search.py\n  --details` on the plugin action, or ask the user where to find it: Falcon\n  console → CrowdStrike Store → [App] → Integration settings).\n- **Never invent a `config_id`, or substitute a placeholder for one.** A\n  fabricated ID fails at runtime — a fake UUID, `YOUR_*`, `TODO`/`FIXME`, or an\n  all-zeros UUID is NOT a valid stand-in. When you can't discover it, **ask the\n  user** via AskUserQuestion; asking is required even in non-interactive/CI runs\n  (the caller supplies the value there).\n- If the config does not yet exist, **document the dependency** and pause — the\n  user must create it in the console before the workflow will run.\n\n**HTTP Actions are the exception — do not block on a credential.** An\n`Inline.HTTPRequest` can be authored credential-less (no `definition_id`) and\ndeployed; it imports with Authentication = \"None\" and the user attaches the API\nkey in the console afterward (proven end-to-end). So for HTTP actions, prefer\ncredential-less authoring over pausing — see `references/http-actions.md`. The\n\"must already exist\" rule applies to *plugin* actions gated on a `config_id`.\n\nNothing this skill produces runs until `deployment` imports it and `execution`\ntriggers it.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}