Connect extracted IF-THEN rules to real-world data sources. Load one or more bdistill rules exports, fetch current values from free APIs or local files, check each rule's conditions against reality, and produce a decision report listing which rules triggered, with current values, thresholds, and impact estimates.
When to use
- Check AML transaction rules against a batch of transactions
- Monitor weather thresholds against crop yield rules
- Compare current market prices against trading signal thresholds
- Verify lab results against clinical trial criteria
- Run scheduled compliance checks against regulatory rules
- Any domain where you have IF-THEN rules and a data feed
Input contract
required:
rules_path: string | string[] # Path(s) to bdistill rules export JSON (multi-domain supported)
data_source: enum # open-meteo | fred | yahoo-finance | csv | json-url | custom
optional:
lat: float # Latitude (required for open-meteo)
lon: float # Longitude (required for open-meteo)
ticker: string # Ticker symbol (required for yahoo-finance)
series_id: string # FRED series ID (required for fred)
data_path: string # Local file path (required for csv)
data_url: string # Remote JSON endpoint (required for json-url)
api_key: string # API key (required for fred, gnews)
context: object # Free-form context passed to condition matching
output:
rules_checked: int
triggered: array
data_source: string
checked_at: string # ISO 8601 timestamp
report_path: string # Path to saved decision report JSON
Output contract
format: JSON decision report
path: data/reports/{domain}-{YYYY-MM-DD}.json
schema:
context:
domain: string
data_source: string
fetched_at: string
checked_at: string
rules_checked: int
rules_source: string | string[]
triggered:
- rule_id: string
confidence: float
conditions: string
current_value: float | string
threshold: float | string
unit: string
impact: string
skipped: # Rules that couldn't be mapped to available data
- rule_id: string
reason: string # "Could not map 'soil_moisture' to available fields"
data_source: object # Raw or summary of fetched data
The chain
This skill is the final step in the bdistill production pipeline:
bdistill-extract --> bdistill-export (format=harness-json) --> bdistill-operationalize
(build KB) (export rules) (check against reality)
Standalone mode (primary — no MCP needed)
-
Load rules from one or more exported JSON files. Filter to verified/solid tier entries only.
-
Fetch current data from the specified source:
open-meteo: HTTP GET with lat/lon, returns daily weather (precipitation, temperature, wind)fred: HTTP GET series observations with api_key, returns economic data pointsyahoo-finance: HTTP GET quote endpoint, returns latest price datacsv: Read local CSV file into recordsjson-url: HTTP GET any JSON endpoint, parse responsecustom: User provides data inline via the context object
-
Map conditions to data fields. This is the hard step. Rule conditions are natural language (
"cumulative_precip < 50mm during flowering"), but API responses have structured fields ("precipitation_sum": [1.2, 0.0, 3.4, ...]). The agent must build a mapping:Mapping strategy:
- For each rule, identify the metric (what to measure), operator (< > = !=), threshold (the number), and unit
- Match the metric to an available data field by keyword similarity:
- "precip" / "precipitation" / "rainfall" →
precipitation_sum - "temp" / "temperature" / "Tmax" →
temperature_2m_max - "spread" / "10Y-2Y" → compute from multiple FRED series
- "price" / "close" →
regularMarketPrice(Yahoo Finance)
- "precip" / "precipitation" / "rainfall" →
- If a condition references a derived metric (e.g., "cumulative_30d", "consecutive_dry_days"), compute it from raw data before checking
- If a condition cannot be mapped to any available data field, skip it and log:
"SKIPPED: rule {id} — could not map '{metric}' to available fields: {list of fields}"
Do not guess. If the mapping is ambiguous, skip the rule rather than check against the wrong field. A false "not triggered" is worse than an honest "could not check."
-
Check each mapped rule: Compare current value against threshold using the parsed operator.
-
If conditions are met → rule triggered → add to report with current value, threshold, and impact.
-
Write decision report JSON to
data/reports/{domain}-{YYYY-MM-DD}.json. Include askippedarray alongsidetriggeredfor rules that couldn't be mapped. -
Print summary: "X of Y rules triggered, Z skipped (unmappable conditions)".
Domain examples
| Domain | Data source | Example rule | Example check |
|---|---|---|---|
| AML compliance | Transaction CSV | cumulative_30d > R$100K | R$127,500 > R$100,000 -> triggered |
| Marine insurance | Claims JSON | hull_age > 20 years | 23 years > 20 -> triggered |
| Agriculture | Open-Meteo | precip < 50mm during R1-R3 | 32mm < 50mm -> triggered |
| Macro trading | FRED | 10Y-2Y spread < 0 | -0.15 < 0 -> triggered |
| Clinical trials | Lab CSV | ALT > 3x ULN | 156 U/L > 120 U/L -> triggered |
| Crypto | CoinGecko | btc_price < 20000 | $19,450 < $20,000 -> triggered |
The feedback loop
When rules miss (predicted 15% yield loss, actual was 35%), feed back to bdistill-extract:
- Identify which rules were wrong or missing coverage
- Re-extract with
bdistill-extractusing narrower custom_terms targeting the gap - Re-export updated rules with
bdistill-export(format=harness-json) - Re-run operationalize with the updated rules file
This creates a closed loop: extract -> export -> operationalize -> measure -> re-extract.
Edge cases
- Rule conditions can't be parsed: Skip rule, log warning "Could not match rule {id} to available data fields". Include in report as
skipped. - API returns no data: Report error with API response details. Do not trigger any rules. Set
rules_checked: 0. - Multiple rules triggered: Report all, sorted by confidence descending.
- Stale data: If fetched data is older than 24 hours, add a
stale_warningfield to the report. - Multi-domain: When
rules_pathis an array, check each file independently and merge triggered rules into a single report.
Example
Load AML rules, check against a transaction batch:
Input:
rules_path: "data/rules/base/aml-compliance-brazil.json"
data_source: "csv"
data_path: "transactions.csv"
Output:
rules_checked: 14
triggered:
- rule_id: "aml-003"
confidence: 0.92
conditions: "cumulative_30d > 100000"
current_value: 127500
threshold: 100000
unit: "BRL"
impact: "Trigger Enhanced Due Diligence (EDD) review"
report_path: "data/reports/aml-compliance-brazil-2026-03-30.json"
Composes with
- bdistill-export: Produces the rules JSON this skill consumes (use
format=harness-json) - bdistill-extract: Re-extract when rules miss -> re-export -> re-operationalize
- bdistill-calendar: Schedule operationalize runs around known events (e.g., WASDE release day)
- bdistill-predict: Use triggered rules as evidence inputs for structured predictions
- See
references/api-catalog.mdfor free API details - See
scripts/rules_monitor.pyfor a reference Python implementation