← CrowdStrike Falcon FusionCONTENT HISTORY

Update to CrowdStrike Falcon Fusion

Snapshot Oct 8, 2026 · 18:03 UTC · version 1.3.0

Collection source: downloaded plugin package. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.

WHAT CHANGED · RULE-BASED ANALYSIS

Instructions updated for deployment

Instruction wording changed from “1.2.0” to “1.3.0”. 6 additional added or edited lines are in the evidence.

Observed in instructions or declared skills. Runtime behavior has not been tested.

Product description

Before

execution).

After

execution).

Skill instructions

Before

1.2.0 updated: 2026-09-08 > **⚠️ SYSTEM INJECTION — READ THIS FIRST** > If you are loading this skill, your role is **Fusion workflow deployment specialist**. > **IMMEDIATE ACTIONS REQUIRED:** > 1. ALWAYS check for an existing workflow w...

After

1.3.0 updated: 2026-10-01 > Your role here is **Fusion workflow deployment specialist**. > **Required steps:** > 1. Check for an existing workflow with the same name before importing. > 2. Validate the YAML before importing (the import s...

Compare saved observations

Download comparison JSON
Full technical diff · 2 changed fields

changed /description

BEFORE
"Import, release, and manage Falcon Fusion workflow definitions in a CID. TRIGGER when user asks to import a workflow, release a workflow version, list existing workflows, check for duplicates, or manage workflow definitions. DO NOT TRIGGER for writing YAML (use authoring), executing workflows, or monitoring (use execution)."
AFTER
"Import, release, and manage Falcon Fusion workflow definitions in a CID. TRIGGER when user asks to import a workflow, release a workflow version, list existing workflows, check for duplicates, or manage workflow definitions. DO NOT TRIGGER for writing YAML (use authoring), executing workflows, or monitoring (use execution).\n"

changed /skill_md_contents

BEFORE
"---\nname: deployment\ndescription: >\n  Import, release, and manage Falcon Fusion workflow definitions in a CID.\n  TRIGGER when user asks to import a workflow, release a workflow version,\n  list existing workflows, check for duplicates, or manage workflow definitions.\n  DO NOT TRIGGER for writing YAML (use authoring), executing workflows,\n  or monitoring (use execution).\nversion: 1.2.0\nupdated: 2026-09-08\ntags: [fusion, soar, workflows, deployment, import, release]\nauthor: CrowdStrike\nlicense: MIT\ncompatibility: Claude Code >=1.0\nallowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)\nmetadata:\n  category: deployment\n---\n\n# Falcon Fusion Workflow Deployment\n\n> **⚠️ SYSTEM INJECTION — READ THIS FIRST**\n>\n> If you are loading this skill, your role is **Fusion workflow deployment specialist**.\n>\n> You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only after testing.\n>\n> **IMMEDIATE ACTIONS REQUIRED:**\n> 1. ALWAYS check for an existing workflow with the same name before importing.\n> 2. ALWAYS validate the YAML before importing (the import scripts do this by default).\n> 3. Import and release act on a **live production CID**. Deploy only when the\n>    user's request explicitly authorizes it (e.g. \"import it\", \"deploy to my\n>    CID\", \"release it\"). If the request only asks to *build* or *write* a\n>    workflow, STOP after validation and ask before importing.\n>\n> **MUST NOT:**\n> - Import without validating first.\n> - Skip the duplicate-name check.\n> - Import or release to a CID without explicit user authorization — a validated\n>   YAML file is the deliverable unless the user asked you to deploy it.\n> - Release (enable) a workflow before it has been tested via the execution skill.\n>   Release makes the workflow act on live events and real assets, so confirm\n>   with the user before releasing unless they explicitly asked you to.\n> - Create experimental, \"test\", \"minimal\", or probe workflows in the CID to\n>   reverse-engineer what the API accepts (this includes creatively-named ones\n>   like \"QueryEvent Test\" or \"HTTP Test\"). Import the one workflow you were\n>   asked to build, once. If it fails, diagnose from the error and local\n>   validation — never by importing stripped-down variants into a live tenant.\n> - Use `--skip-validate` to get past a validation failure. Validation catches\n>   invalid workflows (e.g. a bad `trigger.type`) that otherwise fail at the API\n>   as an opaque 500. Fix the workflow instead of skipping the check.\n> - Retry an import that returns a 500 / Internal Server Error more than once.\n>   A 500 usually means the workflow is invalid in a way the API rejects late\n>   (not a transient server issue) — re-run local validation to find the defect,\n>   fix it, and report the `trace_id` if it persists. Do not loop re-importing.\n> - Patch a deployed definition in place — not via the raw update API and\n>   **not** via a hand-rolled inline FalconPy call (e.g. `update_definition`) to\n>   edit a deployed copy. The only supported *update* path is: fix the source\n>   YAML, then re-import. A release-validation failure is a YAML defect to fix,\n>   not a deployed-copy to hand-edit.\n> - Call FalconPy directly for ANY workflow operation — including a\n>   `python - <<EOF ... delete_definition(...)` snippet to clean up a failed\n>   import attempt. Deleting is fine, but it MUST go through `delete_workflow.py`\n>   (or `scripts/cleanup_workflows.py`), which wrap the supported endpoints. Never\n>   `import auth; get_client()` inline to call `update_definition`/\n>   `delete_definition` yourself.\n\nThis skill moves a finished Fusion workflow definition from a local YAML/JSON file into a CrowdStrike CID. Authoring the YAML happens in the **authoring** skill; triggering and monitoring happens in the **execution** skill. Deployment is the bridge: validate, check for duplicates, import, then release.\n\nIn Falcon Fusion, an imported definition is **disabled** until it is **released** (enabled). Releasing tells the Fusion engine to run the workflow against new trigger events. Keep the workflow disabled until you have tested it.\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` (a sibling skill's script is `../<skill>/scripts/<name>.py`). For `<dir>`, Claude Code uses `\"$CLAUDE_PLUGIN_ROOT/skills/deployment\"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/deployment`). The wrapper bootstraps its own Python venv.\n\n## Prerequisites\n\n- **Python 3.13+**\n- **FalconPy** SDK installed (`pip install crowdstrike-falconpy` — leave unpinned per CrowdStrike guidance)\n- API credentials resolved by `common/scripts/auth.py` from environment\n  variables (for CI/overrides) or the TOML profile:\n  - `FALCON_CLIENT_ID`\n  - `FALCON_CLIENT_SECRET`\n  - `FALCON_BASE_URL` (optional; defaults to `https://api.crowdstrike.com`)\n\n  Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile).\n- An API client with the **Workflow** API scope (read + write)\n- Verify auth before deploying:\n  ```bash\n  ../../scripts/python.sh ../../common/scripts/auth.py\n  ```\n\n## Core Workflow\n\nDeployment is a four-step pipeline. Do not skip steps 1 and 2.\n\n### 1. Validate the YAML first\n\nValidation is owned by the **authoring** skill's `validate.py`. The import script\ncalls it automatically, but run it manually first when iterating:\n\n```bash\n../../scripts/python.sh ../authoring/scripts/validate.py workflows/my-workflow.yaml\n```\n\nFix every structural error before continuing. A definition that fails validation will be rejected by the API.\n\n### 2. Check for an existing workflow with the same name\n\nWorkflow names must be unique within the tenant. Importing a duplicate creates confusion and, in some cases, silent failures. Check first:\n\n```bash\n# Exact-name check (exit 0 if it exists, 1 if not)\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Workflow\"\n\n# Or extract the name straight from the YAML and check\n../../scripts/python.sh scripts/query_workflows.py --check-yaml workflows/my-workflow.yaml\n```\n\nIf a duplicate is found, this is almost always your own earlier attempt at the\nsame workflow. Iterate in place with `import_workflows.py --replace` (it deletes\nthe existing same-name definition, then re-imports). **Do not rename the\nworkflow to `<name> v2` to get past the check** — a renamed copy leaves the old\ndefinition orphaned in the CID, and every retry sprawls another dead workflow.\nKeep the name stable across attempts; use `--replace` (or delete the old\ndefinition explicitly) instead.\n\n### 3. Import the definition\n\n```bash\n# Single file — validates and checks duplicates by default\n../../scripts/python.sh scripts/import_workflows.py workflows/my-workflow.yaml\n\n# A whole directory of definitions (all *.yaml/*.yml)\n../../scripts/python.sh scripts/import_workflows.py workflows/\n```\n\nOn success the script prints the new **definition ID**. Capture it — you need it to release and to execute the workflow.\n\n**Post-import: configure HTTP-Action credentials in the console.** If the workflow contains a\ncredential-less HTTP Action (authored without a `definition_id`), it imports with Authentication =\n\"None\". Tell the user to attach the API key in the console before the action will succeed: open the\nCloud HTTP Request action → Authentication → **Create new** → API key → secret key → location\n**Header** → header name (e.g. `x-apikey`) → **Test** → Save (or **Use existing** if a matching\ncredential already exists). A `401`/`403` at runtime almost always means this step is pending. See\n`../authoring/references/http-actions.md`.\n\n**If the import fails, stop — do not loop.** Some import failures are *not*\nfixable by editing the YAML, and retrying wastes time and tokens. Read the error\nand route accordingly:\n\n| Error from the API / import script | What it means | What to do |\n|------------------------------------|---------------|------------|\n| `no definition ID (workflow not created)` | The API accepted the call but created nothing | Stop. Report it — this is usually a missing plugin config or a server-side issue, not a YAML defect. Do not re-edit and retry. |\n| `API returned status 500` / \"Internal Server Error\" | Server-side error (a `trace_id` is included) | Stop. Report the `trace_id` to the user; a 500 is not something YAML edits fix. Retry at most once. |\n| Missing / unknown `config_id` for a plugin action (VirusTotal, DomainTools, Charlotte AI, Slack, Zscaler) | The integration is not installed/configured in this CID | Stop. Tell the user which action needs a console-created `config_id`; do not invent one or loop editing. |\n| Structural / validation error | A real YAML defect | Fix the YAML, then re-validate and re-import (this one *is* worth iterating on). |\n\nOnly the last row justifies editing and retrying. For the others, surface the error to the user and stop — repeatedly re-importing against a 500 or a missing config will not succeed.\n\n**Never debug by importing probe workflows.** When an import fails, do not build\n\"Test QueryEvent\", \"Minimal trigger\", or other stripped-down workflows in the CID\nto isolate what the API accepts. That litters the tenant with disabled junk and\nburns time without fixing the real workflow. Diagnose from the error message and\n`validate.py` output instead, and if the blocker is a missing plugin `config_id`,\nreport it — that is a console/CID setup step the user must do, not something more\nimports will resolve.\n\n### 4. Release (enable) the workflow\n\nReleasing enables the definition so the Fusion engine runs it against trigger events. Do this only after testing (see the execution skill):\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id <definition_id>\n```\n\n**If release reports validation errors, stop — do not patch the deployed\ndefinition.** A workflow can import successfully yet fail validation at release\n(the server validates more strictly than import). When that happens, fix the\n**source YAML**, then re-validate and re-import with `import_workflows.py` — that\nre-import is the only supported way to update a definition. Do **not** try to\nrepair the deployed copy in place through *any* direct definition-mutation call.\nThat includes the raw workflow update / definition API\n(`WorkflowDefinitionsUpdate`, `.../entities/definitions/v1`), **and any\nhand-rolled FalconPy call** — an inline `python - <<'EOF' ... from falconpy`\nsnippet that reaches for `update_definition`, `delete_definition`, or similar is\nthe same forbidden path wearing a disguise. None of those are part of this skill,\nand looping edits against them returns repeated 500s without ever fixing the\nworkflow. Report the release error (include any `trace_id`) and fix the YAML at\nthe source.\n\nA concrete failure mode: the release error `exclusive gateway '<name>' outgoing\nflow ... has no condition set and is not marked as default` means a condition\nnode has a bare `next:` with neither `default: true` nor a `cel_expression`. Fix\nit in the **source YAML** (add a `cel_expression` to the gated branch, with its\nno-match fallthrough in `else:` — `validate.py` now catches this before deploy,\nincluding inside nested loops) and re-import. Never fan out with a bare\n`default: true` pass-through; list the branch targets directly in the source\nnode's `next:`. Do not hand-edit the deployed definition to add the missing flag.\n\n**The exact recovery loop (do this, not the escape hatch):**\n\n```bash\n# 1. Fix the condition in the SOURCE YAML (add cel_expression + else:).\n#    Keep the workflow `name:` IDENTICAL — do NOT bump it to `<name>-v2`.\n#    The name is the workflow's identity; --replace matches on it.\n# 2. Re-validate — this now catches the release-failing shape pre-deploy:\n../../scripts/python.sh ../authoring/scripts/validate.py my-workflow.yaml\n# 3. Re-import with --replace: deletes the broken same-name definition and\n#    imports the fixed YAML in one step (supported delete + import, not a patch).\n../../scripts/python.sh scripts/import_workflows.py --replace my-workflow.yaml\n```\n\n`--replace` keeps ONE definition per workflow name instead of leaving a renamed\ncopy per attempt — do NOT rename-and-reimport to dodge the duplicate check, which\nsprawls the CID with dead definitions. (Note: each `--replace` assigns a new\ndefinition ID; true in-place update via the PUT endpoint is not currently usable.)\n\nReaching for `python - <<EOF ... update_definition(...)` to patch the deployed\ncopy is never step 2. It does not fix the source, so the next re-import\nreintroduces the same defect, and the API returns repeated 500s. Re-import from\nfixed source is the whole recovery.\n\n## Script Reference\n\nAll scripts add `common/scripts` to `sys.path` and import `get_client` from the shared `auth` module. Run them from anywhere; paths are anchored to each script's own location.\n\n| Script | Purpose | Key flags |\n|--------|---------|-----------|\n| `query_workflows.py` | List, search, and check for existing workflows | `--list`, `--search TERM`, `--check-name NAME`, `--check-yaml FILE...`, `--json` |\n| `import_workflows.py` | Validate, dedupe, and import definitions | `FILE\\|DIR...` (positional), `--skip-validate`, `--skip-duplicate-check`, `--replace` |\n| `release_workflow.py` | Release (enable) a definition by ID | `--id DEF_ID` (required), `--json` |\n| `delete_workflow.py` | Delete a definition by ID or exact name | `--id DEF_ID`, `--name NAME` (repeatable), `--yes`, `--json` |\n\n### query_workflows.py\n\n```bash\n../../scripts/python.sh scripts/query_workflows.py --list                 # All definitions\n../../scripts/python.sh scripts/query_workflows.py --search \"contain\"     # Substring match\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Flow\" --json\n../../scripts/python.sh scripts/query_workflows.py --check-yaml *.yaml     # Batch duplicate check\n```\n\n`--check-name` and `--check-yaml` exit non-zero when a duplicate exists, so they compose cleanly in shell pipelines and CI gates.\n\n### import_workflows.py\n\n```bash\n../../scripts/python.sh scripts/import_workflows.py wf.yaml               # Default: validate + dedupe\n../../scripts/python.sh scripts/import_workflows.py --skip-validate wf.yaml   # AVOID — see pitfall 2\n../../scripts/python.sh scripts/import_workflows.py --skip-duplicate-check wf.yaml\n../../scripts/python.sh scripts/import_workflows.py ./workflows/          # Glob a directory\n```\n\nSupports YAML and JSON definitions, batch mode (multiple files), and directory expansion (`*.yaml`/`*.yml`). Prints a per-file summary and exits non-zero if any file failed or was a duplicate.\n\n### release_workflow.py\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c...        # Enable\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c... --json # Machine-readable\n```\n\nCalls the Workflows definition-action endpoint with `action_name=\"enable\"`. The definition ID comes from the import step or from `query_workflows.py`.\n\n### delete_workflow.py\n\n```bash\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c...          # Delete by ID\n../../scripts/python.sh scripts/delete_workflow.py --name \"Probe run 1\"    # Delete by exact name\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c... --yes    # Skip confirmation (scripted)\n```\n\nDeletes a whole definition via the Workflows delete endpoint (FalconPy\n`delete_definitions`). Use it to remove test, duplicate, or throwaway workflows.\nDeletion is permanent, so it prompts for confirmation unless `--yes` is passed\n(or `FUSION_SKILLS_SUPPRESS_CONFIRM=1` is set for test harnesses). This is the\nsupported way to *remove* a workflow — it is **not** a way to *edit* a deployed\none: to change a workflow, fix the source YAML and re-import (see pitfall 5).\n\n## Common Pitfalls\n\n1. **Importing a duplicate name.** Names must be unique in the tenant. Always run `query_workflows.py --check-name` (or `--check-yaml`) first. A duplicate import can fail silently or produce an \"Unknown error.\"\n\n2. **Importing without validating.** Skipping validation (`--skip-validate`) pushes broken YAML to the API. Never use `--skip-validate` to get past a validation failure — an invalid workflow (for example a bad `trigger.type`) then fails at the API, often as an opaque **500 Internal Server Error** that looks like a server problem but is really a broken definition the local validator would have caught. Fix the workflow and let validation run. `--skip-validate` is only for when you have already validated the same file separately in the same session.\n\n3. **Releasing before testing.** A freshly imported definition is disabled for a reason. Test it with the execution skill (`trigger_workflow.py`) before calling `release_workflow.py`. Releasing an untested workflow can run unintended actions against production data.\n\n4. **Losing the definition ID.** The import output contains the ID you need for release and execution. Capture it; re-finding it later means a `query_workflows.py --search` round-trip.\n\n5. **Wrong API scope.** Import and release require the Workflow scope with **write** access. A read-only client lists workflows fine but fails on import/release with a permissions error.\n\n6. **Editing the deployed copy, not the source.** Re-importing an edited YAML creates a new definition (or trips the duplicate check). Treat the local YAML as the source of truth; re-import to update, and remember the new definition is disabled again until re-released. Never try to patch a deployed definition in place through the raw workflow update API **or a hand-rolled inline FalconPy call** (`update_definition` in a `python - <<EOF` snippet) — that path is not part of this skill and looping edits against it just returns 500s. (Deleting a whole workflow is fine — use `delete_workflow.py`.)\n\n7. **Looping on unrecoverable import errors.** A 500, a missing plugin `config_id`, or a \"no definition ID\" result will not be fixed by editing the YAML. Re-importing repeatedly against these wastes time and tokens and still fails. Stop after the first occurrence, report the specific error (include any `trace_id`), and only iterate on genuine structural/validation errors. See the error table in step 3.\n\n## Handoff\n\n- **Came from authoring?** You have a validated YAML file — start at step 2 (duplicate check).\n- **Going to execution?** Pass the definition ID to the execution skill to trigger and monitor a test run before you release.\n\n## Reading Guide\n\n| Document | When to read |\n|----------|--------------|\n| `references/console-verification.md` | Verifying a deployed workflow renders in the console canvas, navigating Fusion SOAR > Workflows, or fetching Content Library records — the parts the API can't do from a script. |\n"
AFTER
"---\nname: deployment\ndescription: >\n  Import, release, and manage Falcon Fusion workflow definitions in a CID.\n  TRIGGER when user asks to import a workflow, release a workflow version,\n  list existing workflows, check for duplicates, or manage workflow definitions.\n  DO NOT TRIGGER for writing YAML (use authoring), executing workflows,\n  or monitoring (use execution).\nversion: 1.3.0\nupdated: 2026-10-01\ntags: [fusion, soar, workflows, deployment, import, release]\nauthor: CrowdStrike\nlicense: MIT\ncompatibility: Claude Code >=1.0\nallowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)\nmetadata:\n  category: deployment\n---\n\n# Falcon Fusion Workflow Deployment\n\n> Your role here is **Fusion workflow deployment specialist**.\n>\n> You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only after testing.\n>\n> **Required steps:**\n> 1. Check for an existing workflow with the same name before importing.\n> 2. Validate the YAML before importing (the import scripts do this by default).\n> 3. Import and release act on a **live production CID**. Deploy only when the\n>    user's request explicitly authorizes it (e.g. \"import it\", \"deploy to my\n>    CID\", \"release it\"). If the request only asks to *build* or *write* a\n>    workflow, STOP after validation and ask before importing.\n>\n> **Don't:**\n> - Import without validating first.\n> - Skip the duplicate-name check.\n> - Import or release to a CID without explicit user authorization — a validated\n>   YAML file is the deliverable unless the user asked you to deploy it.\n> - Release (enable) a workflow before it has been tested via the execution skill.\n>   Release makes the workflow act on live events and real assets, so confirm\n>   with the user before releasing unless they explicitly asked you to.\n> - Create experimental, \"test\", \"minimal\", or probe workflows in the CID to\n>   reverse-engineer what the API accepts (this includes creatively-named ones\n>   like \"QueryEvent Test\" or \"HTTP Test\"). Import the one workflow you were\n>   asked to build, once. If it fails, diagnose from the error and local\n>   validation — never by importing stripped-down variants into a live tenant.\n> - Use `--skip-validate` to get past a validation failure. Validation catches\n>   invalid workflows (e.g. a bad `trigger.type`) that otherwise fail at the API\n>   as an opaque 500. Fix the workflow instead of skipping the check.\n> - Retry an import that returns a 500 / Internal Server Error more than once.\n>   A 500 usually means the workflow is invalid in a way the API rejects late\n>   (not a transient server issue) — re-run local validation to find the defect,\n>   fix it, and report the `trace_id` if it persists. Do not loop re-importing.\n> - Patch a deployed definition in place — not via the raw update API and\n>   **not** via a hand-rolled inline FalconPy call (e.g. `update_definition`) to\n>   edit a deployed copy. The only supported *update* path is: fix the source\n>   YAML, then re-import. A release-validation failure is a YAML defect to fix,\n>   not a deployed-copy to hand-edit.\n> - Call FalconPy directly for ANY workflow operation — including a\n>   `python - <<EOF ... delete_definition(...)` snippet to clean up a failed\n>   import attempt. Deleting is fine, but it MUST go through `delete_workflow.py`\n>   (or `scripts/cleanup_workflows.py`), which wrap the supported endpoints. Never\n>   `import auth; get_client()` inline to call `update_definition`/\n>   `delete_definition` yourself.\n\nThis skill moves a finished Fusion workflow definition from a local YAML/JSON file into a CrowdStrike CID. Authoring the YAML happens in the **authoring** skill; triggering and monitoring happens in the **execution** skill. Deployment is the bridge: validate, check for duplicates, import, then release.\n\nIn Falcon Fusion, an imported definition is **disabled** until it is **released** (enabled). Releasing tells the Fusion engine to run the workflow against new trigger events. Keep the workflow disabled until you have tested it.\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` (a sibling skill's script is `../<skill>/scripts/<name>.py`). For `<dir>`, Claude Code uses `\"$CLAUDE_PLUGIN_ROOT/skills/deployment\"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/deployment`). The wrapper bootstraps its own Python venv.\n\n## Prerequisites\n\n- **Python 3.13+**\n- **FalconPy** SDK installed (`pip install crowdstrike-falconpy` — leave unpinned per CrowdStrike guidance)\n- API credentials resolved by `common/scripts/auth.py` from environment\n  variables (for CI/overrides) or the TOML profile:\n  - `FALCON_CLIENT_ID`\n  - `FALCON_CLIENT_SECRET`\n  - `FALCON_BASE_URL` (optional; defaults to `https://api.crowdstrike.com`)\n\n  Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile).\n- An API client with the **Workflow** API scope (read + write)\n- Verify auth before deploying:\n  ```bash\n  ../../scripts/python.sh ../../common/scripts/auth.py\n  ```\n\n## Core Workflow\n\nDeployment is a four-step pipeline. Do not skip steps 1 and 2.\n\n### 1. Validate the YAML first\n\nValidation is owned by the **authoring** skill's `validate.py`. The import script\ncalls it automatically, but run it manually first when iterating:\n\n```bash\n../../scripts/python.sh ../authoring/scripts/validate.py workflows/my-workflow.yaml\n```\n\nFix every structural error before continuing. A definition that fails validation will be rejected by the API.\n\n### 2. Check for an existing workflow with the same name\n\nWorkflow names must be unique within the tenant. Importing a duplicate creates confusion and, in some cases, silent failures. Check first:\n\n```bash\n# Exact-name check (exit 0 if it exists, 1 if not)\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Workflow\"\n\n# Or extract the name straight from the YAML and check\n../../scripts/python.sh scripts/query_workflows.py --check-yaml workflows/my-workflow.yaml\n```\n\nIf a duplicate is found, this is almost always your own earlier attempt at the\nsame workflow. Iterate in place with `import_workflows.py --replace` (it deletes\nthe existing same-name definition, then re-imports). **Do not rename the\nworkflow to `<name> v2` to get past the check** — a renamed copy leaves the old\ndefinition orphaned in the CID, and every retry sprawls another dead workflow.\nKeep the name stable across attempts; use `--replace` (or delete the old\ndefinition explicitly) instead.\n\n### 3. Import the definition\n\n```bash\n# Single file — validates and checks duplicates by default\n../../scripts/python.sh scripts/import_workflows.py workflows/my-workflow.yaml\n\n# A whole directory of definitions (all *.yaml/*.yml)\n../../scripts/python.sh scripts/import_workflows.py workflows/\n```\n\nOn success the script prints the new **definition ID**. Capture it — you need it to release and to execute the workflow.\n\n**Post-import: configure HTTP-Action credentials in the console.** If the workflow contains a\ncredential-less HTTP Action (authored without a `definition_id`), it imports with Authentication =\n\"None\". Tell the user to attach the API key in the console before the action will succeed: open the\nCloud HTTP Request action → Authentication → **Create new** → API key → secret key → location\n**Header** → header name (e.g. `x-apikey`) → **Test** → Save (or **Use existing** if a matching\ncredential already exists). A `401`/`403` at runtime almost always means this step is pending. See\n`../authoring/references/http-actions.md`.\n\n**If the import fails, stop — do not loop.** Some import failures are *not*\nfixable by editing the YAML, and retrying wastes time and tokens. Read the error\nand route accordingly:\n\n| Error from the API / import script | What it means | What to do |\n|------------------------------------|---------------|------------|\n| `no definition ID (workflow not created)` | The API accepted the call but created nothing | Stop. Report it — this is usually a missing plugin config or a server-side issue, not a YAML defect. Do not re-edit and retry. |\n| `API returned status 500` / \"Internal Server Error\" | Server-side error (a `trace_id` is included) | Stop. Report the `trace_id` to the user; a 500 is not something YAML edits fix. Retry at most once. |\n| Missing / unknown `config_id` for a plugin action (VirusTotal, DomainTools, Charlotte AI, Slack, Zscaler) | The integration is not installed/configured in this CID | Stop. Tell the user which action needs a console-created `config_id`; do not invent one or loop editing. |\n| Structural / validation error | A real YAML defect | Fix the YAML, then re-validate and re-import (this one *is* worth iterating on). |\n\nOnly the last row justifies editing and retrying. For the others, surface the error to the user and stop — repeatedly re-importing against a 500 or a missing config will not succeed.\n\n**Never debug by importing probe workflows.** When an import fails, do not build\n\"Test QueryEvent\", \"Minimal trigger\", or other stripped-down workflows in the CID\nto isolate what the API accepts. That litters the tenant with disabled junk and\nburns time without fixing the real workflow. Diagnose from the error message and\n`validate.py` output instead, and if the blocker is a missing plugin `config_id`,\nreport it — that is a console/CID setup step the user must do, not something more\nimports will resolve.\n\n### 4. Release (enable) the workflow\n\nReleasing enables the definition so the Fusion engine runs it against trigger events. Do this only after testing (see the execution skill):\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id <definition_id>\n```\n\n**If release reports validation errors, stop — do not patch the deployed\ndefinition.** A workflow can import successfully yet fail validation at release\n(the server validates more strictly than import). When that happens, fix the\n**source YAML**, then re-validate and re-import with `import_workflows.py` — that\nre-import is the only supported way to update a definition. Do **not** try to\nrepair the deployed copy in place through *any* direct definition-mutation call.\nThat includes the raw workflow update / definition API\n(`WorkflowDefinitionsUpdate`, `.../entities/definitions/v1`), **and any\nhand-rolled FalconPy call** — an inline `python - <<'EOF' ... from falconpy`\nsnippet that reaches for `update_definition`, `delete_definition`, or similar is\nthe same forbidden path wearing a disguise. None of those are part of this skill,\nand looping edits against them returns repeated 500s without ever fixing the\nworkflow. Report the release error (include any `trace_id`) and fix the YAML at\nthe source.\n\nA concrete failure mode: the release error `exclusive gateway '<name>' outgoing\nflow ... has no condition set and is not marked as default` means a condition\nnode has a bare `next:` with neither `default: true` nor a `cel_expression`. Fix\nit in the **source YAML** (add a `cel_expression` to the gated branch, with its\nno-match fallthrough in `else:` — `validate.py` now catches this before deploy,\nincluding inside nested loops) and re-import. Never fan out with a bare\n`default: true` pass-through; list the branch targets directly in the source\nnode's `next:`. Do not hand-edit the deployed definition to add the missing flag.\n\n**The exact recovery loop (do this, not the escape hatch):**\n\n```bash\n# 1. Fix the condition in the SOURCE YAML (add cel_expression + else:).\n#    Keep the workflow `name:` IDENTICAL — do NOT bump it to `<name>-v2`.\n#    The name is the workflow's identity; --replace matches on it.\n# 2. Re-validate — this now catches the release-failing shape pre-deploy:\n../../scripts/python.sh ../authoring/scripts/validate.py my-workflow.yaml\n# 3. Re-import with --replace: deletes the broken same-name definition and\n#    imports the fixed YAML in one step (supported delete + import, not a patch).\n../../scripts/python.sh scripts/import_workflows.py --replace my-workflow.yaml\n```\n\n`--replace` keeps ONE definition per workflow name instead of leaving a renamed\ncopy per attempt — do NOT rename-and-reimport to dodge the duplicate check, which\nsprawls the CID with dead definitions. (Note: each `--replace` assigns a new\ndefinition ID; true in-place update via the PUT endpoint is not currently usable.)\n\nReaching for `python - <<EOF ... update_definition(...)` to patch the deployed\ncopy is never step 2. It does not fix the source, so the next re-import\nreintroduces the same defect, and the API returns repeated 500s. Re-import from\nfixed source is the whole recovery.\n\n## Script Reference\n\nAll scripts add `common/scripts` to `sys.path` and import `get_client` from the shared `auth` module. Run them from anywhere; paths are anchored to each script's own location.\n\n| Script | Purpose | Key flags |\n|--------|---------|-----------|\n| `query_workflows.py` | List, search, and check for existing workflows | `--list`, `--search TERM`, `--check-name NAME`, `--check-yaml FILE...`, `--json` |\n| `import_workflows.py` | Validate, dedupe, and import definitions | `FILE\\|DIR...` (positional), `--skip-validate`, `--skip-duplicate-check`, `--replace` |\n| `release_workflow.py` | Release (enable) a definition by ID | `--id DEF_ID` (required), `--json` |\n| `delete_workflow.py` | Delete a definition by ID or exact name | `--id DEF_ID`, `--name NAME` (repeatable), `--yes`, `--json` |\n\n### query_workflows.py\n\n```bash\n../../scripts/python.sh scripts/query_workflows.py --list                 # All definitions\n../../scripts/python.sh scripts/query_workflows.py --search \"contain\"     # Substring match\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Flow\" --json\n../../scripts/python.sh scripts/query_workflows.py --check-yaml *.yaml     # Batch duplicate check\n```\n\n`--check-name` and `--check-yaml` exit non-zero when a duplicate exists, so they compose cleanly in shell pipelines and CI gates.\n\n### import_workflows.py\n\n```bash\n../../scripts/python.sh scripts/import_workflows.py wf.yaml               # Default: validate + dedupe\n../../scripts/python.sh scripts/import_workflows.py --skip-validate wf.yaml   # AVOID — see pitfall 2\n../../scripts/python.sh scripts/import_workflows.py --skip-duplicate-check wf.yaml\n../../scripts/python.sh scripts/import_workflows.py ./workflows/          # Glob a directory\n```\n\nSupports YAML and JSON definitions, batch mode (multiple files), and directory expansion (`*.yaml`/`*.yml`). Prints a per-file summary and exits non-zero if any file failed or was a duplicate.\n\n### release_workflow.py\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c...        # Enable\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c... --json # Machine-readable\n```\n\nCalls the Workflows definition-action endpoint with `action_name=\"enable\"`. The definition ID comes from the import step or from `query_workflows.py`.\n\n### delete_workflow.py\n\n```bash\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c...          # Delete by ID\n../../scripts/python.sh scripts/delete_workflow.py --name \"Probe run 1\"    # Delete by exact name\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c... --yes    # Skip confirmation (scripted)\n```\n\nDeletes a whole definition via the Workflows delete endpoint (FalconPy\n`delete_definitions`). Use it to remove test, duplicate, or throwaway workflows.\nDeletion is permanent, so it prompts for confirmation unless `--yes` is passed\n(or `FUSION_SKILLS_SUPPRESS_CONFIRM=1` is set for test harnesses). This is the\nsupported way to *remove* a workflow — it is **not** a way to *edit* a deployed\none: to change a workflow, fix the source YAML and re-import (see pitfall 5).\n\n## Common Pitfalls\n\n1. **Importing a duplicate name.** Names must be unique in the tenant. Always run `query_workflows.py --check-name` (or `--check-yaml`) first. A duplicate import can fail silently or produce an \"Unknown error.\"\n\n2. **Importing without validating.** Skipping validation (`--skip-validate`) pushes broken YAML to the API. Never use `--skip-validate` to get past a validation failure — an invalid workflow (for example a bad `trigger.type`) then fails at the API, often as an opaque **500 Internal Server Error** that looks like a server problem but is really a broken definition the local validator would have caught. Fix the workflow and let validation run. `--skip-validate` is only for when you have already validated the same file separately in the same session.\n\n3. **Releasing before testing.** A freshly imported definition is disabled for a reason. Test it with the execution skill (`trigger_workflow.py`) before calling `release_workflow.py`. Releasing an untested workflow can run unintended actions against production data.\n\n4. **Losing the definition ID.** The import output contains the ID you need for release and execution. Capture it; re-finding it later means a `query_workflows.py --search` round-trip.\n\n5. **Wrong API scope.** Import and release require the Workflow scope with **write** access. A read-only client lists workflows fine but fails on import/release with a permissions error.\n\n6. **Editing the deployed copy, not the source.** Re-importing an edited YAML creates a new definition (or trips the duplicate check). Treat the local YAML as the source of truth; re-import to update, and remember the new definition is disabled again until re-released. Never try to patch a deployed definition in place through the raw workflow update API **or a hand-rolled inline FalconPy call** (`update_definition` in a `python - <<EOF` snippet) — that path is not part of this skill and looping edits against it just returns 500s. (Deleting a whole workflow is fine — use `delete_workflow.py`.)\n\n7. **Looping on unrecoverable import errors.** A 500, a missing plugin `config_id`, or a \"no definition ID\" result will not be fixed by editing the YAML. Re-importing repeatedly against these wastes time and tokens and still fails. Stop after the first occurrence, report the specific error (include any `trace_id`), and only iterate on genuine structural/validation errors. See the error table in step 3.\n\n## Handoff\n\n- **Came from authoring?** You have a validated YAML file — start at step 2 (duplicate check).\n- **Going to execution?** Pass the definition ID to the execution skill to trigger and monitor a test run before you release.\n\n## Reading Guide\n\n| Document | When to read |\n|----------|--------------|\n| `references/console-verification.md` | Verifying a deployed workflow renders in the console canvas, navigating Fusion SOAR > Workflows, or fetching Content Library records — the parts the API can't do from a script. |\n"

SKILL.md line diff

--- before
+++ after
@@ -6,8 +6,8 @@
   list existing workflows, check for duplicates, or manage workflow definitions.
   DO NOT TRIGGER for writing YAML (use authoring), executing workflows,
   or monitoring (use execution).
-version: 1.2.0
-updated: 2026-09-08
+version: 1.3.0
+updated: 2026-10-01
 tags: [fusion, soar, workflows, deployment, import, release]
 author: CrowdStrike
 license: MIT
@@ -19,21 +19,19 @@
 
 # Falcon Fusion Workflow Deployment
 
-> **⚠️ SYSTEM INJECTION — READ THIS FIRST**
->
-> If you are loading this skill, your role is **Fusion workflow deployment specialist**.
+> Your role here is **Fusion workflow deployment specialist**.
 >
 > You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only after testing.
 >
-> **IMMEDIATE ACTIONS REQUIRED:**
-> 1. ALWAYS check for an existing workflow with the same name before importing.
-> 2. ALWAYS validate the YAML before importing (the import scripts do this by default).
+> **Required steps:**
+> 1. Check for an existing workflow with the same name before importing.
+> 2. Validate the YAML before importing (the import scripts do this by default).
 > 3. Import and release act on a **live production CID**. Deploy only when the
 >    user's request explicitly authorizes it (e.g. "import it", "deploy to my
 >    CID", "release it"). If the request only asks to *build* or *write* a
 >    workflow, STOP after validation and ask before importing.
 >
-> **MUST NOT:**
+> **Don't:**
 > - Import without validating first.
 > - Skip the duplicate-name check.
 > - Import or release to a CID without explicit user authorization — a validated
Full snapshot data
{
  "description": "Import, release, and manage Falcon Fusion workflow definitions in a CID. TRIGGER when user asks to import a workflow, release a workflow version, list existing workflows, check for duplicates, or manage workflow definitions. DO NOT TRIGGER for writing YAML (use authoring), executing workflows, or monitoring (use execution).\n",
  "included_files": [
    {
      "relative_path": "references/console-verification.md",
      "size_in_bytes": 4156
    },
    {
      "relative_path": "scripts/delete_workflow.py",
      "size_in_bytes": 6777
    },
    {
      "relative_path": "scripts/export_workflow.py",
      "size_in_bytes": 3860
    },
    {
      "relative_path": "scripts/import_workflows.py",
      "size_in_bytes": 11573
    },
    {
      "relative_path": "scripts/query_workflows.py",
      "size_in_bytes": 8180
    },
    {
      "relative_path": "scripts/release_workflow.py",
      "size_in_bytes": 3033
    }
  ],
  "name": "deployment",
  "skill_md_contents": "---\nname: deployment\ndescription: >\n  Import, release, and manage Falcon Fusion workflow definitions in a CID.\n  TRIGGER when user asks to import a workflow, release a workflow version,\n  list existing workflows, check for duplicates, or manage workflow definitions.\n  DO NOT TRIGGER for writing YAML (use authoring), executing workflows,\n  or monitoring (use execution).\nversion: 1.3.0\nupdated: 2026-10-01\ntags: [fusion, soar, workflows, deployment, import, release]\nauthor: CrowdStrike\nlicense: MIT\ncompatibility: Claude Code >=1.0\nallowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)\nmetadata:\n  category: deployment\n---\n\n# Falcon Fusion Workflow Deployment\n\n> Your role here is **Fusion workflow deployment specialist**.\n>\n> You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only after testing.\n>\n> **Required steps:**\n> 1. Check for an existing workflow with the same name before importing.\n> 2. Validate the YAML before importing (the import scripts do this by default).\n> 3. Import and release act on a **live production CID**. Deploy only when the\n>    user's request explicitly authorizes it (e.g. \"import it\", \"deploy to my\n>    CID\", \"release it\"). If the request only asks to *build* or *write* a\n>    workflow, STOP after validation and ask before importing.\n>\n> **Don't:**\n> - Import without validating first.\n> - Skip the duplicate-name check.\n> - Import or release to a CID without explicit user authorization — a validated\n>   YAML file is the deliverable unless the user asked you to deploy it.\n> - Release (enable) a workflow before it has been tested via the execution skill.\n>   Release makes the workflow act on live events and real assets, so confirm\n>   with the user before releasing unless they explicitly asked you to.\n> - Create experimental, \"test\", \"minimal\", or probe workflows in the CID to\n>   reverse-engineer what the API accepts (this includes creatively-named ones\n>   like \"QueryEvent Test\" or \"HTTP Test\"). Import the one workflow you were\n>   asked to build, once. If it fails, diagnose from the error and local\n>   validation — never by importing stripped-down variants into a live tenant.\n> - Use `--skip-validate` to get past a validation failure. Validation catches\n>   invalid workflows (e.g. a bad `trigger.type`) that otherwise fail at the API\n>   as an opaque 500. Fix the workflow instead of skipping the check.\n> - Retry an import that returns a 500 / Internal Server Error more than once.\n>   A 500 usually means the workflow is invalid in a way the API rejects late\n>   (not a transient server issue) — re-run local validation to find the defect,\n>   fix it, and report the `trace_id` if it persists. Do not loop re-importing.\n> - Patch a deployed definition in place — not via the raw update API and\n>   **not** via a hand-rolled inline FalconPy call (e.g. `update_definition`) to\n>   edit a deployed copy. The only supported *update* path is: fix the source\n>   YAML, then re-import. A release-validation failure is a YAML defect to fix,\n>   not a deployed-copy to hand-edit.\n> - Call FalconPy directly for ANY workflow operation — including a\n>   `python - <<EOF ... delete_definition(...)` snippet to clean up a failed\n>   import attempt. Deleting is fine, but it MUST go through `delete_workflow.py`\n>   (or `scripts/cleanup_workflows.py`), which wrap the supported endpoints. Never\n>   `import auth; get_client()` inline to call `update_definition`/\n>   `delete_definition` yourself.\n\nThis skill moves a finished Fusion workflow definition from a local YAML/JSON file into a CrowdStrike CID. Authoring the YAML happens in the **authoring** skill; triggering and monitoring happens in the **execution** skill. Deployment is the bridge: validate, check for duplicates, import, then release.\n\nIn Falcon Fusion, an imported definition is **disabled** until it is **released** (enabled). Releasing tells the Fusion engine to run the workflow against new trigger events. Keep the workflow disabled until you have tested it.\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` (a sibling skill's script is `../<skill>/scripts/<name>.py`). For `<dir>`, Claude Code uses `\"$CLAUDE_PLUGIN_ROOT/skills/deployment\"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/deployment`). The wrapper bootstraps its own Python venv.\n\n## Prerequisites\n\n- **Python 3.13+**\n- **FalconPy** SDK installed (`pip install crowdstrike-falconpy` — leave unpinned per CrowdStrike guidance)\n- API credentials resolved by `common/scripts/auth.py` from environment\n  variables (for CI/overrides) or the TOML profile:\n  - `FALCON_CLIENT_ID`\n  - `FALCON_CLIENT_SECRET`\n  - `FALCON_BASE_URL` (optional; defaults to `https://api.crowdstrike.com`)\n\n  Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile).\n- An API client with the **Workflow** API scope (read + write)\n- Verify auth before deploying:\n  ```bash\n  ../../scripts/python.sh ../../common/scripts/auth.py\n  ```\n\n## Core Workflow\n\nDeployment is a four-step pipeline. Do not skip steps 1 and 2.\n\n### 1. Validate the YAML first\n\nValidation is owned by the **authoring** skill's `validate.py`. The import script\ncalls it automatically, but run it manually first when iterating:\n\n```bash\n../../scripts/python.sh ../authoring/scripts/validate.py workflows/my-workflow.yaml\n```\n\nFix every structural error before continuing. A definition that fails validation will be rejected by the API.\n\n### 2. Check for an existing workflow with the same name\n\nWorkflow names must be unique within the tenant. Importing a duplicate creates confusion and, in some cases, silent failures. Check first:\n\n```bash\n# Exact-name check (exit 0 if it exists, 1 if not)\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Workflow\"\n\n# Or extract the name straight from the YAML and check\n../../scripts/python.sh scripts/query_workflows.py --check-yaml workflows/my-workflow.yaml\n```\n\nIf a duplicate is found, this is almost always your own earlier attempt at the\nsame workflow. Iterate in place with `import_workflows.py --replace` (it deletes\nthe existing same-name definition, then re-imports). **Do not rename the\nworkflow to `<name> v2` to get past the check** — a renamed copy leaves the old\ndefinition orphaned in the CID, and every retry sprawls another dead workflow.\nKeep the name stable across attempts; use `--replace` (or delete the old\ndefinition explicitly) instead.\n\n### 3. Import the definition\n\n```bash\n# Single file — validates and checks duplicates by default\n../../scripts/python.sh scripts/import_workflows.py workflows/my-workflow.yaml\n\n# A whole directory of definitions (all *.yaml/*.yml)\n../../scripts/python.sh scripts/import_workflows.py workflows/\n```\n\nOn success the script prints the new **definition ID**. Capture it — you need it to release and to execute the workflow.\n\n**Post-import: configure HTTP-Action credentials in the console.** If the workflow contains a\ncredential-less HTTP Action (authored without a `definition_id`), it imports with Authentication =\n\"None\". Tell the user to attach the API key in the console before the action will succeed: open the\nCloud HTTP Request action → Authentication → **Create new** → API key → secret key → location\n**Header** → header name (e.g. `x-apikey`) → **Test** → Save (or **Use existing** if a matching\ncredential already exists). A `401`/`403` at runtime almost always means this step is pending. See\n`../authoring/references/http-actions.md`.\n\n**If the import fails, stop — do not loop.** Some import failures are *not*\nfixable by editing the YAML, and retrying wastes time and tokens. Read the error\nand route accordingly:\n\n| Error from the API / import script | What it means | What to do |\n|------------------------------------|---------------|------------|\n| `no definition ID (workflow not created)` | The API accepted the call but created nothing | Stop. Report it — this is usually a missing plugin config or a server-side issue, not a YAML defect. Do not re-edit and retry. |\n| `API returned status 500` / \"Internal Server Error\" | Server-side error (a `trace_id` is included) | Stop. Report the `trace_id` to the user; a 500 is not something YAML edits fix. Retry at most once. |\n| Missing / unknown `config_id` for a plugin action (VirusTotal, DomainTools, Charlotte AI, Slack, Zscaler) | The integration is not installed/configured in this CID | Stop. Tell the user which action needs a console-created `config_id`; do not invent one or loop editing. |\n| Structural / validation error | A real YAML defect | Fix the YAML, then re-validate and re-import (this one *is* worth iterating on). |\n\nOnly the last row justifies editing and retrying. For the others, surface the error to the user and stop — repeatedly re-importing against a 500 or a missing config will not succeed.\n\n**Never debug by importing probe workflows.** When an import fails, do not build\n\"Test QueryEvent\", \"Minimal trigger\", or other stripped-down workflows in the CID\nto isolate what the API accepts. That litters the tenant with disabled junk and\nburns time without fixing the real workflow. Diagnose from the error message and\n`validate.py` output instead, and if the blocker is a missing plugin `config_id`,\nreport it — that is a console/CID setup step the user must do, not something more\nimports will resolve.\n\n### 4. Release (enable) the workflow\n\nReleasing enables the definition so the Fusion engine runs it against trigger events. Do this only after testing (see the execution skill):\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id <definition_id>\n```\n\n**If release reports validation errors, stop — do not patch the deployed\ndefinition.** A workflow can import successfully yet fail validation at release\n(the server validates more strictly than import). When that happens, fix the\n**source YAML**, then re-validate and re-import with `import_workflows.py` — that\nre-import is the only supported way to update a definition. Do **not** try to\nrepair the deployed copy in place through *any* direct definition-mutation call.\nThat includes the raw workflow update / definition API\n(`WorkflowDefinitionsUpdate`, `.../entities/definitions/v1`), **and any\nhand-rolled FalconPy call** — an inline `python - <<'EOF' ... from falconpy`\nsnippet that reaches for `update_definition`, `delete_definition`, or similar is\nthe same forbidden path wearing a disguise. None of those are part of this skill,\nand looping edits against them returns repeated 500s without ever fixing the\nworkflow. Report the release error (include any `trace_id`) and fix the YAML at\nthe source.\n\nA concrete failure mode: the release error `exclusive gateway '<name>' outgoing\nflow ... has no condition set and is not marked as default` means a condition\nnode has a bare `next:` with neither `default: true` nor a `cel_expression`. Fix\nit in the **source YAML** (add a `cel_expression` to the gated branch, with its\nno-match fallthrough in `else:` — `validate.py` now catches this before deploy,\nincluding inside nested loops) and re-import. Never fan out with a bare\n`default: true` pass-through; list the branch targets directly in the source\nnode's `next:`. Do not hand-edit the deployed definition to add the missing flag.\n\n**The exact recovery loop (do this, not the escape hatch):**\n\n```bash\n# 1. Fix the condition in the SOURCE YAML (add cel_expression + else:).\n#    Keep the workflow `name:` IDENTICAL — do NOT bump it to `<name>-v2`.\n#    The name is the workflow's identity; --replace matches on it.\n# 2. Re-validate — this now catches the release-failing shape pre-deploy:\n../../scripts/python.sh ../authoring/scripts/validate.py my-workflow.yaml\n# 3. Re-import with --replace: deletes the broken same-name definition and\n#    imports the fixed YAML in one step (supported delete + import, not a patch).\n../../scripts/python.sh scripts/import_workflows.py --replace my-workflow.yaml\n```\n\n`--replace` keeps ONE definition per workflow name instead of leaving a renamed\ncopy per attempt — do NOT rename-and-reimport to dodge the duplicate check, which\nsprawls the CID with dead definitions. (Note: each `--replace` assigns a new\ndefinition ID; true in-place update via the PUT endpoint is not currently usable.)\n\nReaching for `python - <<EOF ... update_definition(...)` to patch the deployed\ncopy is never step 2. It does not fix the source, so the next re-import\nreintroduces the same defect, and the API returns repeated 500s. Re-import from\nfixed source is the whole recovery.\n\n## Script Reference\n\nAll scripts add `common/scripts` to `sys.path` and import `get_client` from the shared `auth` module. Run them from anywhere; paths are anchored to each script's own location.\n\n| Script | Purpose | Key flags |\n|--------|---------|-----------|\n| `query_workflows.py` | List, search, and check for existing workflows | `--list`, `--search TERM`, `--check-name NAME`, `--check-yaml FILE...`, `--json` |\n| `import_workflows.py` | Validate, dedupe, and import definitions | `FILE\\|DIR...` (positional), `--skip-validate`, `--skip-duplicate-check`, `--replace` |\n| `release_workflow.py` | Release (enable) a definition by ID | `--id DEF_ID` (required), `--json` |\n| `delete_workflow.py` | Delete a definition by ID or exact name | `--id DEF_ID`, `--name NAME` (repeatable), `--yes`, `--json` |\n\n### query_workflows.py\n\n```bash\n../../scripts/python.sh scripts/query_workflows.py --list                 # All definitions\n../../scripts/python.sh scripts/query_workflows.py --search \"contain\"     # Substring match\n../../scripts/python.sh scripts/query_workflows.py --check-name \"My Flow\" --json\n../../scripts/python.sh scripts/query_workflows.py --check-yaml *.yaml     # Batch duplicate check\n```\n\n`--check-name` and `--check-yaml` exit non-zero when a duplicate exists, so they compose cleanly in shell pipelines and CI gates.\n\n### import_workflows.py\n\n```bash\n../../scripts/python.sh scripts/import_workflows.py wf.yaml               # Default: validate + dedupe\n../../scripts/python.sh scripts/import_workflows.py --skip-validate wf.yaml   # AVOID — see pitfall 2\n../../scripts/python.sh scripts/import_workflows.py --skip-duplicate-check wf.yaml\n../../scripts/python.sh scripts/import_workflows.py ./workflows/          # Glob a directory\n```\n\nSupports YAML and JSON definitions, batch mode (multiple files), and directory expansion (`*.yaml`/`*.yml`). Prints a per-file summary and exits non-zero if any file failed or was a duplicate.\n\n### release_workflow.py\n\n```bash\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c...        # Enable\n../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c... --json # Machine-readable\n```\n\nCalls the Workflows definition-action endpoint with `action_name=\"enable\"`. The definition ID comes from the import step or from `query_workflows.py`.\n\n### delete_workflow.py\n\n```bash\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c...          # Delete by ID\n../../scripts/python.sh scripts/delete_workflow.py --name \"Probe run 1\"    # Delete by exact name\n../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c... --yes    # Skip confirmation (scripted)\n```\n\nDeletes a whole definition via the Workflows delete endpoint (FalconPy\n`delete_definitions`). Use it to remove test, duplicate, or throwaway workflows.\nDeletion is permanent, so it prompts for confirmation unless `--yes` is passed\n(or `FUSION_SKILLS_SUPPRESS_CONFIRM=1` is set for test harnesses). This is the\nsupported way to *remove* a workflow — it is **not** a way to *edit* a deployed\none: to change a workflow, fix the source YAML and re-import (see pitfall 5).\n\n## Common Pitfalls\n\n1. **Importing a duplicate name.** Names must be unique in the tenant. Always run `query_workflows.py --check-name` (or `--check-yaml`) first. A duplicate import can fail silently or produce an \"Unknown error.\"\n\n2. **Importing without validating.** Skipping validation (`--skip-validate`) pushes broken YAML to the API. Never use `--skip-validate` to get past a validation failure — an invalid workflow (for example a bad `trigger.type`) then fails at the API, often as an opaque **500 Internal Server Error** that looks like a server problem but is really a broken definition the local validator would have caught. Fix the workflow and let validation run. `--skip-validate` is only for when you have already validated the same file separately in the same session.\n\n3. **Releasing before testing.** A freshly imported definition is disabled for a reason. Test it with the execution skill (`trigger_workflow.py`) before calling `release_workflow.py`. Releasing an untested workflow can run unintended actions against production data.\n\n4. **Losing the definition ID.** The import output contains the ID you need for release and execution. Capture it; re-finding it later means a `query_workflows.py --search` round-trip.\n\n5. **Wrong API scope.** Import and release require the Workflow scope with **write** access. A read-only client lists workflows fine but fails on import/release with a permissions error.\n\n6. **Editing the deployed copy, not the source.** Re-importing an edited YAML creates a new definition (or trips the duplicate check). Treat the local YAML as the source of truth; re-import to update, and remember the new definition is disabled again until re-released. Never try to patch a deployed definition in place through the raw workflow update API **or a hand-rolled inline FalconPy call** (`update_definition` in a `python - <<EOF` snippet) — that path is not part of this skill and looping edits against it just returns 500s. (Deleting a whole workflow is fine — use `delete_workflow.py`.)\n\n7. **Looping on unrecoverable import errors.** A 500, a missing plugin `config_id`, or a \"no definition ID\" result will not be fixed by editing the YAML. Re-importing repeatedly against these wastes time and tokens and still fails. Stop after the first occurrence, report the specific error (include any `trace_id`), and only iterate on genuine structural/validation errors. See the error table in step 3.\n\n## Handoff\n\n- **Came from authoring?** You have a validated YAML file — start at step 2 (duplicate check).\n- **Going to execution?** Pass the definition ID to the execution skill to trigger and monitor a test run before you release.\n\n## Reading Guide\n\n| Document | When to read |\n|----------|--------------|\n| `references/console-verification.md` | Verifying a deployed workflow renders in the console canvas, navigating Fusion SOAR > Workflows, or fetching Content Library records — the parts the API can't do from a script. |\n"
}

SHA-256 of public snapshot: 2271907f0ea8f9d9cd0044262a41116917147a46eb79eb58d094a2fb86a937ac