Discovery
The strategy runner walks thestrategies/ directory tree recursively on every call (strategy_list, strategy_describe, strategy_run). There is no caching — drop a new strategy on disk and the next call sees it.
Discovery rules:
- The runner looks for directories containing both
contract.yamlANDstrategy.py. - Directories starting with
.or_are skipped (.venv,__pycache__, etc.). contract.yamlmust contain anamefield; otherwise the strategy is rejected.- Subdirectories are arbitrary — the runner uses
os.walk, so paths can nest as deep as you want. The convention is domain first, then category:strategies/<domain>/<category>/<slug>/, e.g.strategies/security/enrichment/splunk_field_survey/. Common categories:enrichment,hunt,detection,correlation,traversal. The directory layout is purely organizational — a strategy’s identity comes from thenamefield in itscontract.yaml, not its path.
binding.yaml is loaded if present alongside the contract. Its absence does not block discovery — the resolver falls back to semantic-tag auto-resolve. See Portability for when each path applies.
Hot reload
Because discovery is fresh on every call, deploying a new or updated strategy in a running cluster is a matter of copying the right files into the right container. No restart, no rebuild. In Kubernetes:strategy_list or strategy_run call.
In Docker Compose or local-process mode, just write the files to the strategies directory the runner is configured to scan — no container plumbing needed.
For permanent deployments, bake the strategy into the Docker image instead:
Dual-container split
In Kubernetes the gateway pod runs two containers, and which file goes where matters:| Container | Reads | Why |
|---|---|---|
| strategy-runner (Python) | contract.yaml + strategy.py | Discovery and step execution |
| fracta-gateway (Go) | binding.yaml | Per-strategy binding overrides during resolution |
strategy-runner needs the contract and the Python; fracta-gateway needs the binding. Copying contract.yaml to the gateway container is harmless but not what makes resolution work — the load-bearing split is runtime code + contract in the runner, binding overrides in the gateway.
Governance status
When the knowledge graph is connected, every strategy carries a governance status. The status reflects how much trust an operator has placed in the strategy, not whether it runs:| Status | Meaning | Entry condition |
|---|---|---|
exploratory | Newly authored or experimental. Runs fine, but not promoted as a production asset. | Default for newly created strategies |
validated | Proven reliable through repeated use. | Automatic: run_count >= 5 AND reliability >= 0.8 |
promoted | Validated and recommended for routine use. The highest tier. | Operator calls strategy_promote (or auto: run_count >= 20, reliability >= 0.95, composite_score >= 0.7) |
deprecated | Scheduled for removal. | Operator marks deprecated |
retired | No longer available for execution. | Automatic: 30 days after deprecation with no runs |
exploratory -> validated: When a strategy accumulates >= 5 runs with >= 0.8 reliability score.promoted -> validated(demotion): If reliability drops below 0.7, the strategy is demoted back to validated.deprecated -> retired: After 30 days with no executions, deprecated strategies are automatically retired.
strategy_list does not filter by status by default — it returns every discovered strategy and, when the graph is connected, enriches each with its governance status. To filter explicitly, pass the status parameter:
status="validated,promoted"— only trusted strategiesstatus="exploratory"— only experimental strategiesstatus="all"— same as the default
strategy_create start as exploratory. An operator advances them with strategy_promote once they’ve been reviewed.
Auto-generated catalogue
Every strategy understrategies/<domain>/<category>/<slug>/ shows up automatically as a page in the Strategies → Catalogue sidebar group. The catalogue page is built from:
contract.yaml— name, version, description, tags, parameters, required tables.strategy.py— parsed via Python AST (never imported or executed) to extract the@stepDAG, which renders as a Mermaid flowchart.README.md(optional) — your hand-written prose, inlined verbatim. This is where you describe what the strategy does, when to use it, what to adapt in the binding, and any caveats. Markdown including Mermaid blocks is supported.
scripts/gen-strategy-catalogue.py, which writes .mdx files into docs/strategies/catalogue/ and updates the Catalogue subgroup in docs.json. On push to main, the strategy-catalogue GitHub workflow regenerates and commits the catalogue automatically. CI also runs make docs-gen-check on every PR — a PR that changes a strategy without committing the regenerated catalogue will fail.
The example strategy at strategies/_example/security/enrichment/splunk_field_survey/ shows the file shape an author should aim for: contract + strategy + binding + README. Copy it as your starting point.
Next
- Overview — the full framework reference, including the MCP tools listed above
- Quick Start — build a minimal strategy end-to-end
- Bindings — what
fracta-gatewayis reading frombinding.yaml

