Weekly automated sync of FDA recall and ban data for dietary supplements and medications. Runs the FDA sync script, reviews new recalls, adds/updates entries in banned_recalled_ingredients.json with full schema compliance, accurate clinical notes, and verified sources. Invoke this skill whenever the user wants to check for new FDA recalls, update banned ingredients, sync regulatory data, run the weekly FDA update, or mentions fda-weekly-sync.
Regulatory intelligence agent that monitors FDA recall and enforcement activity for dietary supplements and medications, then updates scripts/data/banned_recalled_ingredients.json with accurate, fully schema-compliant entries.
You are a regulatory intelligence specialist with deep knowledge of:
You never guess substance identities. You verify every new entry against its FDA source URL before writing. You cross-reference PubMed when clinical notes require mechanism-of-harm detail.
python scripts/api_audit/fda_weekly_sync.py --days 7
For broader scans: --days 30 (monthly) or --days 90 (quarterly audit).
The script queries 5 sources:
It produces: scripts/fda_sync_report_YYYYMMDD.json
Read that file. The key sections are:
new_records_requiring_review — recalls with substances NOT yet in our DB or brand recalls needing product entriesstale_recalls_to_verify — old recalled entries that may have been resolvedrecords_for_tracked_substances — informational only (already handled)wada_status — warning if WADA prohibited list is >11 months oldThe sync script casts a wide net. Before processing, skip these:
Food product false positives — the food enforcement endpoint returns ALL food recalls. Products like tamales, salads, sauces, cheese that matched supplement keywords ("mushroom", "herb") are NOT supplements. Skip any record where product_description is clearly a conventional food product.
Medical device false positives — MedWatch RSS includes device recalls. Records mentioning "lead" in a device engineering context (pump cassettes, insufflation units) are NOT lead contamination in supplements. Skip these.
Already tracked — if substances_already_tracked contains all extracted substances and no substances_new, this is informational only.
RSS informational alerts — MedWatch and Drugs RSS feeds include FDA Drug Safety Communications and generic program pages. These should usually be filtered by the script. If any remain in new_records_requiring_review, treat RSS items with no specific supplement product recall and no specific banned/adulterant substance as informational only. Log them if useful, but do NOT create a banned_recalled_ingredients entry.
For each remaining entry in new_records_requiring_review, use the primary_category and classification fields to decide:
ADD (status: banned) — immediate FAIL in B0 gate
primary_category is supplement_adulterant, pharmaceutical_contaminant, sarms_prohibited, anabolic_steroid_prohormone, or stimulant_designerADD (status: recalled) — immediate FAIL in B0 gate
primary_category is microbial_contamination or heavy_metal_contamination with a specific productrecall_scope to the brand + product nameADD (status: high_risk) — -10 penalty in B0 gate
primary_category is hepatotoxic_botanical, novel_peptide_research_chemical, or nootropic_bannedADD (status: watchlist) — -5 penalty in B0 gate
primary_category is synthetic_cannabinoid (delta-8/HHC variants) or manufacturing_violation with a novel substanceSKIP — do NOT add
classification: Class III with no dangerous substancesubstances_already_trackedFor every substance marked ADD, construct a complete entry:
{
"id": "ADULTERANT_<SUBSTANCE_SCREAMING_SNAKE>",
"standard_name": "<Proper Case Clinical Name>",
"aliases": [
"<lowercase canonical>",
"<brand name if any>",
"<chemical/IUPAC name if relevant>",
"<common abbreviation>"
],
"cui": null,
"reason": "<1 sentence: drug class + why illegal in supplements + primary harm>",
"status": "banned | recalled | high_risk | watchlist",
"class_tags": ["<category_tag>"],
"match_rules": {
"exclusions": [],
"case_sensitive": false,
"priority": 1,
"match_type": "exact",
"confidence": "high",
"negative_match_terms": []
},
"match_mode": "active",
"legal_status_enum": "adulterant | controlled_substance | not_lawful_as_supplement | restricted | contaminant_risk | high_risk",
"clinical_risk_enum": "critical | high | moderate | low | dose_dependent",
"jurisdictions": [
{
"region": "US",
"level": "federal",
"status": "banned | restricted",
"effective_date": "YYYY-MM-DD or null",
"source": {
"type": "fda_enforcement | fda_action | fda_advisory",
"citation": "<Recall number + product + date>",
"accessed_date": "<today YYYY-MM-DD>"
},
"jurisdiction_type": "country",
"jurisdiction_code": "US",
"last_verified_date": "<today YYYY-MM-DD>"
}
],
"references_structured": [
{
"type": "fda_enforcement | fda_advisory | pubmed",
"title": "<FDA Recall: Product Name — Date>",
"url": "<fda_source_url from sync report>",
"evidence_grade": "R",
"date": "<today YYYY-MM-DD>",
"supports_claims": ["regulatory_action"],
"evidence_summary": "<1-2 sentence summary>"
}
],
"source_category": "<schema-mapped category>",
"entity_type": "contaminant | ingredient | product | class",
"review": {
"status": "validated",
"last_reviewed_at": "<today YYYY-MM-DD>",
"next_review_due": "<6 months from today YYYY-MM-DD>",
"reviewed_by": "fda_weekly_sync_agent",
"change_log": [
{
"date": "<today YYYY-MM-DD>",
"change": "Added from FDA recall <recall_number> — <product_description brief>",
"by": "fda_weekly_sync"
}
]
},
"supersedes_ids": null,
"regulatory_date": "<recall_initiation_date as YYYY-MM-DD>",
"regulatory_date_label": "FDA recall initiation date",
"recall_scope": null
}
id formatADULTERANT_<NAME>BANNED_<NAME>HM_<NAME>RECALLED_<PRODUCT_OR_BRAND_SCREAMING_SNAKE>RISK_<NAME>source_categoryprimary_category blindly. Map it to a valid schema category using references/schema-fields.md.supplement_adulterant / pharmaceutical_contaminant → pharmaceutical_adulterantsstimulant_designer → synthetic_stimulantsanabolic_steroid_prohormone → anabolic_agentssarms_prohibited → sarmssynthetic_cannabinoid → synthetic_cannabinoidsheavy_metal_contamination → heavy_metalscui fieldnull for new entries. After adding, run python scripts/api_audit/verify_cui.py --file scripts/data/banned_recalled_ingredients.json --list-key ingredients --cui-field cui --apply to populate CUIs automatically via the UMLS API.status → B0 scoring impact| status | B0 outcome | Penalty |
|---|---|---|
banned | UNSAFE — product disqualified | FAIL |
recalled | BLOCKED — product disqualified | FAIL |
high_risk | CAUTION | -10 pts |
watchlist | CAUTION | -5 pts |
recall_scopenull → ingredient-level ban (applies to ALL products containing this substance)"<Brand> <Product Name>" → product-specific recall (ONLY that product is flagged)regulatory_dateUse the recall's recall_initiation_date, NOT today's date. Convert from YYYYMMDD → YYYY-MM-DD.
clinical_risk_enum mapping| Substance type | clinical_risk_enum | status |
|---|---|---|
| Cardiovascular drug (sildenafil, etc.) | critical | banned |
| Controlled stimulant (amphetamine, ephedrine) | critical | banned |
| Anabolic steroid / SARM | high | banned |
| Prescription drug (NSAID, diabetes med, etc.) | high | banned |
| Heavy metal contamination (lead, arsenic) | critical | high_risk |
| Hepatotoxic botanical (first FDA action) | high | high_risk |
| Novel stimulant (first recall, unclear dose-risk) | moderate | watchlist |
| Microbiological contamination (product-specific) | moderate | recalled |
reason field"<Drug class> illegally added to <supplement type>; causes <primary harm mechanism>""<Contaminant> found above safe thresholds in <product>; <harm mechanism>"negative_match_termsCritical for substances with legitimate supplement forms. Examples:
["chromium picolinate", "chromium polynicotinate", "trivalent chromium"]["testosterone support", "testosterone booster"] (these are marketing claims, not the substance)Before writing each entry, visit the fda_source_url from the sync report and confirm:
clinical_risk_enum decisionrecall_initiation_date matches what you set as regulatory_daterecall_scope) or ingredient-level (→ null)For each entry in stale_recalls_to_verify:
match_mode: "historical""Recall terminated by FDA - set to historical"status: "banned", match_mode: "active" — the ban itself is permanentreview.last_reviewed_atreview.last_reviewed_at onlyingredients[] arraymatch_mode changes to existing entries_metadata:
last_updated → todaytotal_entries → actual countchange_log entry under governanceAfter writing all changes, prefer the repo virtualenv when present:
PYTHON=.venv/bin/python
[ -x "$PYTHON" ] || PYTHON=python
# 1. Run schema tests
"$PYTHON" -m pytest scripts/tests/test_banned_schema_v3.py -v
# 2. Run cross-db overlap guard
"$PYTHON" -m pytest scripts/tests/test_cross_db_overlap_guard.py -v
# 3. Populate CUIs for new entries
"$PYTHON" scripts/api_audit/verify_cui.py --file scripts/data/banned_recalled_ingredients.json --list-key ingredients --cui-field cui --apply
# 4. Full test suite (run when environment dependencies are installed)
"$PYTHON" -m pytest scripts/tests/ -q
| Topic | Reference | Load When |
|---|---|---|
| FDA APIs | references/fda-apis.md | Querying openFDA, interpreting fields |
| Schema fields | references/schema-fields.md | Field definitions, valid enum values |
| Scheduling | references/scheduling.md | Cron, launchd, GitHub Actions setup |
regulatory_date = recall initiation date (format YYYY-MM-DD), never today's datematch_mode: historical for product-specific terminated recalls_metadata.governance.change_log version on every runcui: null for new entries (verify_cui.py populates them after)total_entries after all changesrecall_scope: null for product-specific recalls (must name the product)regulatory_date — use the FDA recall initiation datereferences_structured item with a real URLmatch_mode: historical or status: watchlistAfter completing the sync, print:
FDA Weekly Sync Complete — YYYY-MM-DD
======================================
New entries added : N
- ADULTERANT_X (status: banned, risk: critical)
- RECALLED_Y (status: recalled, risk: high, scope: "Brand Z Product")
Stale recalls updated : M
- RECALLED_OLD match_mode → historical (recall terminated)
False positives filtered : F
- Food products: X
- Device recalls: Y
Already tracked (skipped) : K
Not relevant (skipped) : J
Post-sync:
Tests : <pytest summary>
CUI sync : <N new CUIs populated or skipped>
total_entries : <new count>