Skip to content

Commit df218a8

Browse files
committed
Make sub-skill routing survive a single-entry-point install
A single-entry-point install works: with only development-workflow discoverable, an assistant reads it, locates the sub-skills on disk, and scaffolds as well as the ten-skill control. But nothing in the skills said that was the arrangement, so it worked by inference. Two body-text changes make it explicit. The orchestrator now documents that sub-skills are siblings at `../<name>/SKILL.md` and what to do when a capability-level request lands on it directly: read the sub-skill from disk first, then skip to the Step 5 CLI command when a manifest already exists rather than re-running the whole creation flow. It also states that this never overrides the Cross-Plugin Advisory, so a standalone Fusion workflow request is still a redirect and not a capability to add. The nine sub-skills each gain one line back to the orchestrator. Only e2e-testing referenced it before, and only in a footer, so an assistant without routing hooks could select a sub-skill and never see the CLI prerequisite check, the scaffolding order, or manifest coordination. The line is conditional on the orchestrator not having run, which makes it a no-op wherever a hook already forces it. Deliberately not touching the trigger descriptions. Widening the orchestrator's DO NOT TRIGGER clause would fix the remaining Codex gap — a capability-only request on a single-entry-point install still matches nothing — but descriptions are the skill-selection surface, and adding capability-level triggers there risks pulling a 5.3k-token orchestrator into Claude Code sessions that need one sub-skill. All ten descriptions are byte-identical to main and the description budget is unchanged at 4432 chars, so selection behaviour cannot have moved. That trade needs A/B evidence first. Budget: the orchestrator and functions-falcon-api had no room, so local e2e test steps moved to development-workflow's advanced-patterns reference and the multi-API enrichment example moved to functions-falcon-api's, both linked from where they were. workflows-development's two near-duplicate HTTP Actions callouts merged into one. Every SKILL.md is under the 5500-token cap.
1 parent e9db42a commit df218a8

13 files changed

Lines changed: 91 additions & 54 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
1212
- **Agent Plugins manifest** — A root `plugin.json` conforming to the [Agent Plugins](https://agent-plugins.org) 1.0.0 spec, so any conforming client can discover the plugin alongside the existing Claude and Codex manifests. CI validates it and `release.sh` bumps its version with the others.
1313
- US-3 cloud region to the cloud-region documentation: added `us-3` to the `FOUNDRY_CLOUD_REGION` value lists (headless-operation reference, e2e-testing env var table) and the multi-cloud deployment section. Foundry CLI 2.0.2 added US-3 support; the base URL (`api.us-3.crowdstrike.com`) is in FalconPy as of v1.6.4.
1414

15+
### Fixed
16+
17+
- **Sub-skill routing survives a single-entry-point install**`development-workflow` now documents that sub-skills live beside it on disk as `../<name>/SKILL.md`, and how to handle a capability-level request that lands on the orchestrator: read the sub-skill from disk first, then skip straight to the CLI command when `manifest.yml` already exists. Assistants that register only the orchestrator no longer have to infer this. The orchestrator's trigger description is unchanged, so skill selection on a fully-registered install behaves exactly as before.
18+
- **Sub-skills point back to the orchestrator** — All nine sub-skills now link to `development-workflow` near the top of the file, so an assistant without routing hooks that selects a sub-skill directly still finds the CLI prerequisite check, scaffolding order, and manifest coordination. Previously only `e2e-testing` mentioned the orchestrator, and only in a footer.
19+
1520
## [1.4.1] - 2026-08-07
1621

1722
### Fixed

skills/api-integrations/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ metadata:
2121
>
2222
> **Note:** For `api-integrations create`, always include `--description` — the CLI still prompts for it even with `--no-prompt` if omitted.
2323
24+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
25+
2426
This skill covers exposing external APIs (third-party services or CrowdStrike Falcon APIs) to the Falcon Foundry platform via OpenAPI/Swagger specifications. These integrations make API operations available to Falcon Fusion SOAR workflows, Foundry UI extensions, Foundry Functions, and other Foundry capabilities.
2527

2628
**API integrations are how Foundry manages credentials.** There is no secrets system, no encrypted env vars, and no key vault. When you register an API integration, the platform collects credentials at install time and manages tokens automatically. This is why functions MUST call third-party REST APIs through `APIIntegrations().execute_command_proxy()` — not via raw HTTP with env vars.

skills/collections-development/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ metadata:
1919
>
2020
> You MUST design Collections with proper JSON Schemas, validation rules, and access patterns.
2121
22+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
23+
2224
Falcon Foundry Collections are NoSQL document stores with JSON Schema validation. They provide persistent storage for app data with CRUD operations, FQL queries, and schema enforcement.
2325

2426
## Collection Naming Constraints

skills/debugging-workflows/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ metadata:
1515

1616
Systematic procedures for diagnosing and resolving common CrowdStrike Falcon Foundry development issues.
1717

18+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
19+
1820
## Quick Diagnosis
1921

2022
```

skills/development-workflow/SKILL.md

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ Standalone Fusion workflow (no app — trigger + existing actions only)
6565
└── Advise the Falcon Fusion plugin — see Cross-Plugin Advisory
6666
```
6767

68+
### Routing When Sub-Skills Are Not Registered
69+
70+
Sub-skills live beside this one as `../<name>/SKILL.md`. On a single-entry-point install only this skill is discoverable, so capability-level requests land here — that is intended, not a mis-route. Read the sub-skill file from disk before writing any capability content, then:
71+
72+
- **`manifest.yml` already present** — skip Steps 1-2 and Step 4. Run the Step 3 prerequisite check, add the capability with its Step 5 CLI command, and follow Manifest Coordination.
73+
- **No app yet** — run the full App Creation Flow.
74+
75+
This never overrides the Cross-Plugin Advisory below: a standalone Fusion workflow request is still a redirect, not a capability to add.
76+
6877
## Cross-Plugin Advisory (Fusion vs. Foundry)
6978

7079
A Falcon Fusion workflow can be authored **standalone** (no app wrapper) when it
@@ -288,27 +297,6 @@ When `manifest.yml` already exists, work is primarily editing existing files. Us
288297
- `foundry apps deploy` / `foundry apps release` — deployment
289298
- `foundry api-integrations create` etc. — adding new capabilities
290299

291-
## Testing an Existing App Locally
292-
293-
When running e2e tests against a CrowdStrike/foundry-sample-* app on GitHub:
294-
295-
1. **Configure credentials** — copy `.env.sample` to `.env` in the `e2e/` directory and fill in valid Falcon credentials (username, password, TOTP secret, base URL) and app name. `APP_NAME` defaults to the repo name. `FALCON_` credentials must be for a non-SSO user because TOTP is used in e2e tests. This file is gitignored and required for local test runs.
296-
297-
2. **Align the app name** — the manifest `name` and the e2e test `APP_NAME` environment variable (in `.env`) must match for local test runs. CI pipelines typically rewrite the manifest name automatically (e.g., `${REPO}-ci-${PIPELINE_ID}`), so this only affects local development. Preferred approach: update the manifest `name` to match the repo name (e.g., `foundry-sample-logscale`) to avoid spaces and simplify artifact lookup. Remember to `git checkout manifest.yml` after deploy to revert ID changes.
298-
299-
3. **Deploy and release:**
300-
```bash
301-
foundry apps deploy --change-type Patch --change-log "e2e testing" --no-prompt
302-
# Poll until successful
303-
foundry apps list-deployments
304-
# Release
305-
foundry apps release --deployment-id <id> --change-type Patch --notes "e2e testing" --no-prompt
306-
```
307-
308-
4. **Run tests:** `cd e2e && npm test`
309-
310-
5. **Revert manifest:** `git checkout manifest.yml` (deploy writes IDs into the manifest)
311-
312300
## Manifest Coordination
313301

314302
**Dependency order:** Collections → Functions → Workflows → UI (each may depend on the previous)
@@ -327,7 +315,7 @@ When running e2e tests against a CrowdStrike/foundry-sample-* app on GitHub:
327315
| Superpowers plugin coordination | [references/superpowers-integration.md](references/superpowers-integration.md) |
328316
| Token management, performance targets | [references/performance-optimization.md](references/performance-optimization.md) |
329317
| Counter-rationalizations, red flags | [references/counter-rationalizations.md](references/counter-rationalizations.md) |
330-
| Lifecycle phases, manifest patterns, CLI state, app operations | [references/advanced-patterns.md](references/advanced-patterns.md) |
318+
| Lifecycle phases, manifest patterns, CLI state, app operations, local e2e runs | [references/advanced-patterns.md](references/advanced-patterns.md) |
331319

332320
## Improving These Skills
333321

skills/development-workflow/references/advanced-patterns.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,25 @@ For action IDs in workflow YAML, use the `api_integrations.{name}.{operationId}`
185185
## Deployment Nuance
186186

187187
`foundry apps deploy` requires `--change-type` and `--change-log`. The `--no-prompt` flag is available as a global Controller flag and skips the deployment confirmation prompt and TUI monitor, but is not required when all flags are provided. `foundry apps release` requires `--deployment-id`, `--change-type`, and `--notes` and works fully non-interactively when all three flags are provided.
188+
189+
## Testing an Existing App Locally
190+
191+
When running e2e tests against a CrowdStrike/foundry-sample-* app on GitHub:
192+
193+
1. **Configure credentials** — copy `.env.sample` to `.env` in the `e2e/` directory and fill in valid Falcon credentials (username, password, TOTP secret, base URL) and app name. `APP_NAME` defaults to the repo name. `FALCON_` credentials must be for a non-SSO user because TOTP is used in e2e tests. This file is gitignored and required for local test runs.
194+
195+
2. **Align the app name** — the manifest `name` and the e2e test `APP_NAME` environment variable (in `.env`) must match for local test runs. CI pipelines typically rewrite the manifest name automatically (e.g., `${REPO}-ci-${PIPELINE_ID}`), so this only affects local development. Preferred approach: update the manifest `name` to match the repo name (e.g., `foundry-sample-logscale`) to avoid spaces and simplify artifact lookup. Remember to `git checkout manifest.yml` after deploy to revert ID changes.
196+
197+
3. **Deploy and release:**
198+
199+
```bash
200+
foundry apps deploy --change-type Patch --change-log "e2e testing" --no-prompt
201+
# Poll until successful
202+
foundry apps list-deployments
203+
# Release
204+
foundry apps release --deployment-id <id> --change-type Patch --notes "e2e testing" --no-prompt
205+
```
206+
207+
4. **Run tests:** `cd e2e && npm test`
208+
209+
5. **Revert manifest:** `git checkout manifest.yml` (deploy writes IDs into the manifest)

skills/e2e-testing/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ End-to-end testing for Falcon Foundry apps using [Playwright](https://playwright
1717

1818
The library provides authentication, app install/uninstall, page objects, and configuration so each app only writes its app-specific tests.
1919

20+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
21+
2022
## Quick Start
2123

2224
### 1. Create the `e2e/` directory

skills/functions-development/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ metadata:
2424
> 2. Implement structured JSON responses with proper status codes
2525
> 3. Apply input validation before processing any request
2626
27+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
28+
2729
Falcon Foundry Functions are serverless handlers in Go or Python, executed inside the Foundry FaaS runtime. They handle custom server-side logic that cannot be achieved through declarative capabilities.
2830

2931
## Functions as a Last Resort

skills/functions-falcon-api/SKILL.md

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ metadata:
2121
>
2222
> The FalconPy `Detects` class is **removed**. Do not import it. Use `Alerts` for detection queries.
2323
24+
> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load [development-workflow](../development-workflow/SKILL.md) first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.
25+
2426
This skill covers calling CrowdStrike Falcon APIs from within Foundry functions (serverless Go or Python code). Authentication is completely automatic when code runs inside Foundry function handlers — the platform handles all OAuth flows, token management, and credential injection.
2527

2628
For exposing external APIs to Foundry via OpenAPI specs, see **api-integrations** instead.
@@ -35,7 +37,7 @@ For exposing external APIs to Foundry via OpenAPI specs, see **api-integrations*
3537

3638
| Topic | Reference |
3739
|-------|-----------|
38-
| Retry decorator with exponential backoff, counter-rationalizations table | [references/advanced-patterns.md](references/advanced-patterns.md) |
40+
| Retry decorator with exponential backoff, multi-API enrichment, counter-rationalizations table | [references/advanced-patterns.md](references/advanced-patterns.md) |
3941

4042
## Python: Zero-Argument Authentication
4143

@@ -192,34 +194,7 @@ def get_host_details(request: Request, config, logger) -> Response:
192194

193195
### Multi-API Enrichment
194196

195-
```python
196-
@func.handler(method='POST', path='/api/enrich')
197-
def enrich_host_context(request: Request, config, logger) -> Response:
198-
hosts_api = Hosts()
199-
alerts_api = Alerts()
200-
201-
hostname = request.body.get("hostname")
202-
if not hostname:
203-
return Response(body={"error": "Hostname required"}, code=400)
204-
205-
# Get host
206-
host_query = hosts_api.query_devices_by_filter(filter=f"hostname:'{hostname}'")
207-
host_ids = host_query.get("body", {}).get("resources", [])
208-
if not host_ids:
209-
return Response(body={"error": "Host not found"}, code=404)
210-
211-
host = hosts_api.get_device_details(ids=host_ids).get("body", {}).get("resources", [{}])[0]
212-
213-
# Get detections (via Alerts API with product filter)
214-
detection_ids = alerts_api.query_alerts_v2(filter=f"device.hostname:'{hostname}'+product:'detections'", limit=10).get("body", {}).get("resources", [])
215-
detections = alerts_api.get_alerts_v2(ids=detection_ids).get("body", {}).get("resources", []) if detection_ids else []
216-
217-
# Get all alerts (includes detections + cases)
218-
alert_ids = alerts_api.query_alerts_v2(filter=f"device.hostname:'{hostname}'", limit=10).get("body", {}).get("resources", [])
219-
alerts = alerts_api.get_alerts_v2(ids=alert_ids).get("body", {}).get("resources", []) if alert_ids else []
220-
221-
return Response(body={"host": host, "detections": detections, "alerts": alerts}, code=200)
222-
```
197+
Combining `Hosts` and `Alerts` in one handler follows the same query-then-get-details shape as above. See [references/advanced-patterns.md](references/advanced-patterns.md) for the full example.
223198

224199
## LogScale / NG-SIEM Queries from Functions
225200

skills/functions-falcon-api/references/advanced-patterns.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,36 @@ if __name__ == '__main__':
8282
| "Mocking is extra work" | Real API calls in tests are slow, flaky, and quota-consuming |
8383
| "I can skip the FDK handler pattern" | Handler pattern is required for automatic auth injection |
8484
| "I'll use the Detects class for detection queries" | The Detects API is deprecated (405 errors). Use `Alerts()` with `query_alerts_v2` — filter by `product:'detections'` to scope to detections only |
85+
86+
## Multi-API Enrichment
87+
88+
Combine `Hosts` and `Alerts` in a single handler to build host context. Each API follows the same query-then-get-details shape:
89+
90+
```python
91+
@func.handler(method='POST', path='/api/enrich')
92+
def enrich_host_context(request: Request, config, logger) -> Response:
93+
hosts_api = Hosts()
94+
alerts_api = Alerts()
95+
96+
hostname = request.body.get("hostname")
97+
if not hostname:
98+
return Response(body={"error": "Hostname required"}, code=400)
99+
100+
# Get host
101+
host_query = hosts_api.query_devices_by_filter(filter=f"hostname:'{hostname}'")
102+
host_ids = host_query.get("body", {}).get("resources", [])
103+
if not host_ids:
104+
return Response(body={"error": "Host not found"}, code=404)
105+
106+
host = hosts_api.get_device_details(ids=host_ids).get("body", {}).get("resources", [{}])[0]
107+
108+
# Get detections (via Alerts API with product filter)
109+
detection_ids = alerts_api.query_alerts_v2(filter=f"device.hostname:'{hostname}'+product:'detections'", limit=10).get("body", {}).get("resources", [])
110+
detections = alerts_api.get_alerts_v2(ids=detection_ids).get("body", {}).get("resources", []) if detection_ids else []
111+
112+
# Get all alerts (includes detections + cases)
113+
alert_ids = alerts_api.query_alerts_v2(filter=f"device.hostname:'{hostname}'", limit=10).get("body", {}).get("resources", [])
114+
alerts = alerts_api.get_alerts_v2(ids=alert_ids).get("body", {}).get("resources", []) if alert_ids else []
115+
116+
return Response(body={"host": host, "detections": detections, "alerts": alerts}, code=200)
117+
```

0 commit comments

Comments
 (0)